From 717db4fb3ee6073e6494ff4dc4ca997a328faa45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Str=C3=B8m-Andersen?= Date: Wed, 16 Sep 2026 22:15:45 +0200 Subject: [PATCH 01/10] feat: add SearchBar component --- src/components/SearchBar/SearchBar.tsx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/components/SearchBar/SearchBar.tsx diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx new file mode 100644 index 0000000..02cf3cb --- /dev/null +++ b/src/components/SearchBar/SearchBar.tsx @@ -0,0 +1,20 @@ +import styles from "./SearchBar.module.css"; + +interface SearchBarProps { + query: string; + onQueryChange: (newQuery: string) => void; +} + +function SearchBar({ query, onQueryChange }: SearchBarProps) { + return ( + onQueryChange(e.target.value)} + /> + ); +} + +export { SearchBar }; From 227552a502d95ac490ed93a44bcc03e60a928716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Str=C3=B8m-Andersen?= Date: Wed, 16 Sep 2026 22:15:59 +0200 Subject: [PATCH 02/10] feat: style SearchBar --- src/components/SearchBar/SearchBar.module.css | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/components/SearchBar/SearchBar.module.css diff --git a/src/components/SearchBar/SearchBar.module.css b/src/components/SearchBar/SearchBar.module.css new file mode 100644 index 0000000..12d573e --- /dev/null +++ b/src/components/SearchBar/SearchBar.module.css @@ -0,0 +1,16 @@ +.searchBar { + display: block; + width: 100%; + max-width: 500px; + padding: 10px 16px; + margin: 80px auto 0 auto; + + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + font-size: 1rem; + font-family: inherit; + + &:hover { + border-color: var(--theme-color); + } +} From 9fa61d02ef2b50a6c6433fca2d2cb5c50fde42bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Str=C3=B8m-Andersen?= Date: Wed, 16 Sep 2026 22:16:27 +0200 Subject: [PATCH 03/10] feat: add SearchBar to BookGrid --- src/components/BookGrid/BookGrid.tsx | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/components/BookGrid/BookGrid.tsx b/src/components/BookGrid/BookGrid.tsx index d41832e..22150ea 100644 --- a/src/components/BookGrid/BookGrid.tsx +++ b/src/components/BookGrid/BookGrid.tsx @@ -1,4 +1,6 @@ +import { useState } from "react"; import { BookCard } from "../BookCard/BookCard"; +import { SearchBar } from "../SearchBar/SearchBar"; import styles from "./BookGrid.module.css"; import { useBooks } from "../../api/useBooks"; import type { Book } from "../../api/types"; @@ -8,6 +10,8 @@ interface BookGridProps { } function BookGrid({ onBookSelect }: BookGridProps) { + const [searchQuery, setSearchQuery] = useState(""); + const { data, isPending, isError, error } = useBooks({ query: "subject:fantasy", }); @@ -37,13 +41,17 @@ function BookGrid({ onBookSelect }: BookGridProps) { } return ( -
    - {data.map((book) => ( -
  • - -
  • - ))} -
+ <> + + +
    + {data.map((book) => ( +
  • + +
  • + ))} +
+ ); } From 7a91e7e4be26b8bfc5360fc7ec7b9e25baa23a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Str=C3=B8m-Andersen?= Date: Wed, 16 Sep 2026 23:11:37 +0200 Subject: [PATCH 04/10] feat: add name and aria-label to search input --- src/components/SearchBar/SearchBar.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx index 02cf3cb..549df0b 100644 --- a/src/components/SearchBar/SearchBar.tsx +++ b/src/components/SearchBar/SearchBar.tsx @@ -9,6 +9,8 @@ function SearchBar({ query, onQueryChange }: SearchBarProps) { return ( Date: Wed, 16 Sep 2026 23:12:10 +0200 Subject: [PATCH 05/10] feat: add responsive styling to SearchBar --- src/components/SearchBar/SearchBar.module.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/SearchBar/SearchBar.module.css b/src/components/SearchBar/SearchBar.module.css index 12d573e..d955f3f 100644 --- a/src/components/SearchBar/SearchBar.module.css +++ b/src/components/SearchBar/SearchBar.module.css @@ -13,4 +13,10 @@ &:hover { border-color: var(--theme-color); } + + @media only screen and (max-width: 600px) { + width: calc(100% - 80px); /* Adjust width to account for the margins*/ + margin: 40px 40px 0 40px; + max-width: none; + } } From f245c205965e1ef12e9617296fb8419946f7f08c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Str=C3=B8m-Andersen?= Date: Wed, 16 Sep 2026 23:14:58 +0200 Subject: [PATCH 06/10] feat: add useDebounce hook Created a custom hook to debounce a value. Will be used in the search bar to avoid triggering an API request on every keystroke --- src/components/BookGrid/useDebounce.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/components/BookGrid/useDebounce.ts diff --git a/src/components/BookGrid/useDebounce.ts b/src/components/BookGrid/useDebounce.ts new file mode 100644 index 0000000..7449557 --- /dev/null +++ b/src/components/BookGrid/useDebounce.ts @@ -0,0 +1,19 @@ +import { useEffect, useState } from "react"; + +function useDebounce(value: T, delay: number): T { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect(() => { + const timeoutId = window.setTimeout(() => { + setDebouncedValue(value); + }, delay); + + return () => { + window.clearTimeout(timeoutId); + }; + }, [value, delay]); + + return debouncedValue; +} + +export { useDebounce }; From c9adfe377300fb2aa9285ee54575b8bcd4a04901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Str=C3=B8m-Andersen?= Date: Wed, 16 Sep 2026 23:16:22 +0200 Subject: [PATCH 07/10] feat: use debounced search query in BookGrid --- src/components/BookGrid/BookGrid.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/BookGrid/BookGrid.tsx b/src/components/BookGrid/BookGrid.tsx index 22150ea..abb70d6 100644 --- a/src/components/BookGrid/BookGrid.tsx +++ b/src/components/BookGrid/BookGrid.tsx @@ -3,6 +3,7 @@ import { BookCard } from "../BookCard/BookCard"; import { SearchBar } from "../SearchBar/SearchBar"; import styles from "./BookGrid.module.css"; import { useBooks } from "../../api/useBooks"; +import { useDebounce } from "./useDebounce"; import type { Book } from "../../api/types"; interface BookGridProps { @@ -12,8 +13,11 @@ interface BookGridProps { function BookGrid({ onBookSelect }: BookGridProps) { const [searchQuery, setSearchQuery] = useState(""); + const debouncedSearch = useDebounce(searchQuery, 500); + const query = debouncedSearch.trim() || "subject:fantasy"; + const { data, isPending, isError, error } = useBooks({ - query: "subject:fantasy", + query, }); if (isPending) { From 463ad94e938311472380ee988d9fa100d276f551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Str=C3=B8m-Andersen?= Date: Wed, 16 Sep 2026 23:17:38 +0200 Subject: [PATCH 08/10] fix: keep SearchBar visible in all BookGrid states --- src/components/BookGrid/BookGrid.tsx | 33 +++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/components/BookGrid/BookGrid.tsx b/src/components/BookGrid/BookGrid.tsx index abb70d6..673d818 100644 --- a/src/components/BookGrid/BookGrid.tsx +++ b/src/components/BookGrid/BookGrid.tsx @@ -20,33 +20,46 @@ function BookGrid({ onBookSelect }: BookGridProps) { query, }); + const searchBar = ( + + ); + if (isPending) { return ( -

- Loading books... -

+ <> + {searchBar} +

+ Loading books... +

+ ); } if (isError) { return ( -

- Error: {error.message} -

+ <> + {searchBar} +

+ Error: {error.message} +

+ ); } if (data.length === 0) { return ( -

- No books found for this search -

+ <> + {searchBar} +

+ No books found for this search +

+ ); } return ( <> - + {searchBar}
    {data.map((book) => ( From 9f5f1f2a15d15cc43bf8f8b09fd1c4a5d1b2a40e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Str=C3=B8m-Andersen?= Date: Wed, 16 Sep 2026 23:32:50 +0200 Subject: [PATCH 09/10] style: rename camelCase class name to kebab-case --- src/components/SearchBar/SearchBar.module.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SearchBar/SearchBar.module.css b/src/components/SearchBar/SearchBar.module.css index d955f3f..ea27572 100644 --- a/src/components/SearchBar/SearchBar.module.css +++ b/src/components/SearchBar/SearchBar.module.css @@ -1,4 +1,4 @@ -.searchBar { +.search-bar { display: block; width: 100%; max-width: 500px; From acedad2ecc1b72e01fe7aabd28aa7c53b2d96c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Str=C3=B8m-Andersen?= Date: Wed, 16 Sep 2026 23:53:08 +0200 Subject: [PATCH 10/10] docs: credit Stack Overflow source in useDebounce --- src/components/BookGrid/useDebounce.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/BookGrid/useDebounce.ts b/src/components/BookGrid/useDebounce.ts index 7449557..0d94079 100644 --- a/src/components/BookGrid/useDebounce.ts +++ b/src/components/BookGrid/useDebounce.ts @@ -1,5 +1,7 @@ import { useEffect, useState } from "react"; +// Debounce pattern largely adapted from this Stack Overflow post: +// https://stackoverflow.com/questions/75556418/how-to-use-debounce-hooks-in-react function useDebounce(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState(value);