diff --git a/src/App.css b/src/App.css index 7913902..868e790 100644 --- a/src/App.css +++ b/src/App.css @@ -434,8 +434,6 @@ font-size: 14px; } - - .pokemon-info { width: 100%; margin-top: 16px; diff --git a/src/App.tsx b/src/App.tsx index ad25df3..3b15a09 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,35 +1,20 @@ import { useState } from 'react'; -import type { FormEvent } from 'react'; import { useQuery } from '@tanstack/react-query'; import { useFavorites } from './useFavorites'; import FavoritesSidebar from './FavoritesSidebar'; +import SearchBar, { SEARCH_STORAGE_KEY } from './SearchBar'; import { listAllPokemon } from './Api'; import PokemonDetails from './PokemonDetails'; import './App.css'; -const SEARCH_STORAGE_KEY = 'pokemon-search'; -const RECENT_SEARCHES_STORAGE_KEY = 'pokemon-recent-searches'; -const MAX_RECENT_SEARCHES = 5; - -function loadRecentSearches(): string[] { - try { - const raw = sessionStorage.getItem(RECENT_SEARCHES_STORAGE_KEY); - const parsed = raw ? JSON.parse(raw) : []; - return Array.isArray(parsed) ? parsed : []; - } catch { - return []; - } -} +const HOMEPAGE_POKEMON_COUNT = 8; function capitalize(text: string): string { return text.charAt(0).toUpperCase() + text.slice(1); } function App() { - const [query, setQuery] = useState( - () => sessionStorage.getItem(SEARCH_STORAGE_KEY) ?? '' - ); const [selectedPokemonId, setSelectedPokemonId] = useState(1); // TanStack Query håndterer henting og caching av Pokémon-lista. @@ -48,8 +33,6 @@ function App() { ); const [error, setError] = useState(''); - const [recentSearches, setRecentSearches] = useState(loadRecentSearches); - const [showSuggestions, setShowSuggestions] = useState(false); const { favorites, isFavorite, toggleFavorite } = useFavorites(); @@ -57,22 +40,7 @@ function App() { ? allPokemon .filter((entry) => entry.name.includes(submittedQuery.toLowerCase())) .slice(0, 24) - : []; - - function addToRecentSearches(trimmedQuery: string) { - const withoutDuplicate = recentSearches.filter( - (entry) => entry.toLowerCase() !== trimmedQuery.toLowerCase() - ); - const updated = [trimmedQuery, ...withoutDuplicate].slice( - 0, - MAX_RECENT_SEARCHES - ); - setRecentSearches(updated); - sessionStorage.setItem( - RECENT_SEARCHES_STORAGE_KEY, - JSON.stringify(updated) - ); - } + : allPokemon.slice(0, HOMEPAGE_POKEMON_COUNT); function runSearch(rawQuery: string) { const trimmedQuery = rawQuery.trim(); @@ -86,18 +54,6 @@ function App() { sessionStorage.setItem(SEARCH_STORAGE_KEY, trimmedQuery); setSubmittedQuery(trimmedQuery); setError(''); - addToRecentSearches(trimmedQuery); - } - - function handleSearch(event: FormEvent) { - event.preventDefault(); - runSearch(query); - } - - function handleSelectRecentSearch(recentQuery: string) { - setQuery(recentQuery); - setShowSuggestions(false); - runSearch(recentQuery); } return ( @@ -117,38 +73,8 @@ function App() { setSelectedPokemonId={setSelectedPokemonId} /> -
-
-
- setQuery(event.target.value)} - onFocus={() => setShowSuggestions(true)} - onBlur={() => setShowSuggestions(false)} - placeholder="For eksempel: bulba" - aria-label="Søk etter Pokémon" - /> - {showSuggestions && recentSearches.length > 0 && ( -
    - {recentSearches.map((recentQuery) => ( -
  • - -
  • - ))} -
- )} -
- -
+
+
diff --git a/src/PokemonDetails.tsx b/src/PokemonDetails.tsx index 7cd9d6b..af49afb 100644 --- a/src/PokemonDetails.tsx +++ b/src/PokemonDetails.tsx @@ -87,7 +87,9 @@ function PokemonDetails({ - {selectedPokemon.types.map(capitalize).join(', ')} + + {selectedPokemon.types.map(capitalize).join(', ')} + diff --git a/src/SearchBar.tsx b/src/SearchBar.tsx new file mode 100644 index 0000000..7a0898f --- /dev/null +++ b/src/SearchBar.tsx @@ -0,0 +1,98 @@ +import { useState, type FormEvent } from 'react'; + +export const SEARCH_STORAGE_KEY = 'pokemon-search'; +const RECENT_SEARCHES_STORAGE_KEY = 'pokemon-recent-searches'; +const MAX_RECENT_SEARCHES = 5; + +type SearchBarProps = { + onSearch: (query: string) => void; + isLoading: boolean; +}; + +function loadRecentSearches(): string[] { + try { + const raw = sessionStorage.getItem(RECENT_SEARCHES_STORAGE_KEY); + const parsed = raw ? JSON.parse(raw) : []; + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } +} + +function SearchBar({ onSearch, isLoading }: SearchBarProps) { + const [query, setQuery] = useState( + () => sessionStorage.getItem(SEARCH_STORAGE_KEY) ?? '' + ); + const [recentSearches, setRecentSearches] = useState(loadRecentSearches); + const [showSuggestions, setShowSuggestions] = useState(false); + + function addToRecentSearches(trimmedQuery: string) { + const withoutDuplicate = recentSearches.filter( + (entry) => entry.toLowerCase() !== trimmedQuery.toLowerCase() + ); + const updated = [trimmedQuery, ...withoutDuplicate].slice( + 0, + MAX_RECENT_SEARCHES + ); + setRecentSearches(updated); + sessionStorage.setItem( + RECENT_SEARCHES_STORAGE_KEY, + JSON.stringify(updated) + ); + } + + function submitSearch(rawQuery: string) { + const trimmedQuery = rawQuery.trim(); + onSearch(trimmedQuery); + if (trimmedQuery) { + addToRecentSearches(trimmedQuery); + } + } + + function handleSearch(event: FormEvent) { + event.preventDefault(); + submitSearch(query); + } + + function handleSelectRecentSearch(recentQuery: string) { + setQuery(recentQuery); + setShowSuggestions(false); + submitSearch(recentQuery); + } + + return ( +
+
+ setQuery(event.target.value)} + onFocus={() => setShowSuggestions(true)} + onBlur={() => setShowSuggestions(false)} + placeholder="For eksempel: bulba" + aria-label="Søk etter Pokémon" + /> + {showSuggestions && recentSearches.length > 0 && ( +
    + {recentSearches.map((recentQuery) => ( +
  • + +
  • + ))} +
+ )} +
+ +
+ ); +} + +export default SearchBar;