Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion country-explorer/src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import SortSelector from './SortSelector';
import Loading from './Loading';
import ErrorMessage from './ErrorMessage';
import NavigationControls from './NavigationControls';
import FavoritesList from './FavoritesList';

// sessionStorage keys for the display choices this component persists.
const REGION_STORAGE_KEY = 'selectedRegion';
Expand Down Expand Up @@ -43,6 +44,13 @@ function App() {
// A fresh, sorted copy — filteredCountries (and the cached query data it
// derives from) is left untouched.
const sortedCountries = sortCountries(filteredCountries, sortOption);
// Favorites are drawn from the full, unfiltered country list (not
// filteredCountries/sortedCountries) so a favorite from another region
// still shows up here even while a region filter is active.
const favoriteCountries = sortCountries(
countries.filter((country) => isFavorite(country.code)),
sortOption,
);
const [selectedCountryCode, setSelectedCountryCode] = useState<string | null>(null);
const selectedCountry =
sortedCountries.find((country) => country.code === selectedCountryCode) ?? sortedCountries[0];
Expand Down Expand Up @@ -78,6 +86,15 @@ function App() {
}
}

// Selecting a favorite clears the region filter first: the favorite may
// belong to a region other than the one currently filtered to, and
// selectedCountry/CountrySelector/NavigationControls are all derived from
// sortedCountries, so the code has to be present there to actually show up.
function selectFavorite(code: string) {
setSelectedRegion('');
setSelectedCountryCode(code);
}

// Keep the selection valid when data arrives or the available countries change.
// Store the fallback so a removed country is not reselected if it returns later.
if (selectedCountryCode !== activeCountryCode) {
Expand Down Expand Up @@ -147,7 +164,7 @@ function App() {

<section aria-labelledby="favorites-heading">
<h2 id="favorites-heading">Favorites</h2>
{/* FavoritesList goes here */}
<FavoritesList countries={favoriteCountries} onSelectFavorite={selectFavorite} />
</section>
</main>

Expand Down
30 changes: 30 additions & 0 deletions country-explorer/src/components/FavoritesList.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.favorites-list {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.5rem;
margin: 1rem;
padding: 0;
list-style: none;
}

.favorites-list button {
min-height: 44px;
padding: 0.5rem 1rem;
font: inherit;
color: var(--text-h);
background: var(--bg);
border: 1px solid var(--border);
border-radius: 4px;
cursor: pointer;
}

.favorites-list button:hover {
background: var(--accent-bg);
border-color: var(--accent);
}

.favorites-list button:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 3px;
}
27 changes: 27 additions & 0 deletions country-explorer/src/components/FavoritesList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Country } from '../types/country';
import './FavoritesList.css';

interface FavoritesListProps {
countries: Country[];
onSelectFavorite: (code: string) => void;
}

function FavoritesList({ countries, onSelectFavorite }: FavoritesListProps) {
if (countries.length === 0) {
return <p role="status">You haven't added any favorites yet.</p>;
}

return (
<ul className="favorites-list">
{countries.map((country) => (
<li key={country.code}>
<button type="button" onClick={() => onSelectFavorite(country.code)}>
{country.name || country.code}
</button>
</li>
))}
</ul>
);
}

export default FavoritesList;