From cd9a98e3bdcbfefd61ecd0380627413b1ad3388a Mon Sep 17 00:00:00 2001 From: Johannes Horn Date: Fri, 18 Sep 2026 13:26:55 +0200 Subject: [PATCH 1/2] feat: browse to previous and next book in BookDetails BookDetails now recieves the list it was opened from, and finds previous and next book. Buttons are disabled at both ends. --- src/App.tsx | 15 +++++++ .../BookDetails/BookDetails.module.css | 26 ++++++++++++ src/components/BookDetails/BookDetails.tsx | 42 ++++++++++++++++++- src/components/BookGrid/BookGrid.tsx | 29 ++++++------- 4 files changed, 97 insertions(+), 15 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index d4a7658..f8f8c3b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,11 +3,21 @@ import { Header } from "./components/Header/Header.tsx"; import { Footer } from "./components/Footer/Footer.tsx"; import { BookGrid } from "./components/BookGrid/BookGrid.tsx"; import { BookDetails } from "./components/BookDetails/BookDetails.tsx"; +import { useBooks } from "./api/useBooks.ts"; +import { useDebounce } from "./components/BookGrid/useDebounce.ts"; import type { Book } from "./api/types.ts"; function App() { const [selectedBook, setSelectedBook] = useState(null); const [page, setPage] = useState(1); + // Search state and the fetched books live here instead of in BookGrid, so + // they survive switching to BookDetails and back. + const [searchQuery, setSearchQuery] = useState(""); + + const debouncedSearch = useDebounce(searchQuery, 500); + const query = debouncedSearch.trim() || "subject:fantasy"; + + const booksQuery = useBooks({ query, page }); return ( <> @@ -16,10 +26,15 @@ function App() { {selectedBook ? ( setSelectedBook(null)} /> ) : ( void; onGoBack: () => void; } -function BookDetails({ book, onGoBack }: BookDetailsProps) { +function BookDetails({ + book, + books, + onBookSelect, + onGoBack, +}: BookDetailsProps) { const { data: description, isPending, isError } = useBookDescription(book.id); const descriptionText = isPending ? "Loading description..." : isError ? "Error loading description" : (description ?? "No description available"); + + // index is -1 if the list has changed since the book was opened; then both + // browse buttons are disabled. + const index = books.findIndex((b) => b.id === book.id); + const previousBook: Book | undefined = + index > 0 ? books[index - 1] : undefined; + const nextBook: Book | undefined = index >= 0 ? books[index + 1] : undefined; + return (
@@ -26,6 +42,30 @@ function BookDetails({ book, onGoBack }: BookDetailsProps) { > Go back +
diff --git a/src/components/BookGrid/BookGrid.tsx b/src/components/BookGrid/BookGrid.tsx index 26c8444..81c3b71 100644 --- a/src/components/BookGrid/BookGrid.tsx +++ b/src/components/BookGrid/BookGrid.tsx @@ -1,32 +1,33 @@ -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 { useDebounce } from "./useDebounce"; +import type { useBooks } from "../../api/useBooks"; import type { Book } from "../../api/types"; import { Pagination } from "../Pagination/Pagination"; import { SEARCH_LIMIT } from "../../api/openLibrary"; interface BookGridProps { + // The result of useBooks, owned by App so it outlives this component. + booksQuery: ReturnType; + searchQuery: string; + onSearchChange: (query: string) => void; page: number; onBookSelect: (book: Book) => void; onPageChange: (page: number) => void; } -function BookGrid({ page, onBookSelect, onPageChange }: BookGridProps) { - const [searchQuery, setSearchQuery] = useState(""); - - const debouncedSearch = useDebounce(searchQuery, 500); - const query = debouncedSearch.trim() || "subject:fantasy"; - - const { data, isPending, isError, error } = useBooks({ - query, - page, - }); +function BookGrid({ + booksQuery, + searchQuery, + onSearchChange, + page, + onBookSelect, + onPageChange, +}: BookGridProps) { + const { data, isPending, isError, error } = booksQuery; const searchBar = ( - + ); if (isPending) { From 365e44b505363decc34ab6deb793ccae41d55a33 Mon Sep 17 00:00:00 2001 From: Johannes Horn Date: Fri, 18 Sep 2026 17:25:02 +0200 Subject: [PATCH 2/2] Update src/components/BookDetails/BookDetails.tsx Co-authored-by: Martin Alexander Kaminski --- src/components/BookDetails/BookDetails.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/BookDetails/BookDetails.tsx b/src/components/BookDetails/BookDetails.tsx index d1f71fd..38c9f5d 100644 --- a/src/components/BookDetails/BookDetails.tsx +++ b/src/components/BookDetails/BookDetails.tsx @@ -28,9 +28,8 @@ function BookDetails({ // index is -1 if the list has changed since the book was opened; then both // browse buttons are disabled. const index = books.findIndex((b) => b.id === book.id); - const previousBook: Book | undefined = - index > 0 ? books[index - 1] : undefined; - const nextBook: Book | undefined = index >= 0 ? books[index + 1] : undefined; + const previousBook = index > 0 ? books[index - 1] : undefined; + const nextBook = index >= 0 ? books[index + 1] : undefined; return (