Skip to content

Commit

Permalink
Merge branch 'main' into DDFBRA-217-opdater-unilogin-integration-til-…
Browse files Browse the repository at this point in the history
…at-bruge-seneste-stil-anvisninger
  • Loading branch information
spaceo committed Nov 15, 2024
2 parents d6fdc10 + 55d32f0 commit bf658e0
Show file tree
Hide file tree
Showing 29 changed files with 810 additions and 243 deletions.
22 changes: 22 additions & 0 deletions __tests__/url.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { expect, test, vi } from "vitest"

import { resolveUrl } from "../lib/helpers/helper.routes"

test("That resolveUrl can return a work url", async () => {
const workUrl = resolveUrl({ type: "work", routeParams: { id: 123 } })
expect(workUrl).toBe("/work/123")
})

test("That resolveUrl can return a work url with a manifestation type", async () => {
const workUrl = resolveUrl({
type: "work",
routeParams: { id: 123 },
queryParams: { audio: "true" },
})
expect(workUrl).toBe("/work/123?audio=true")
})

test("That resolveUrl can return a search url", async () => {
const workUrl = resolveUrl({ type: "search", queryParams: { q: "test" } })
expect(workUrl).toBe("/search?q=test")
})
Binary file removed app/favicon.ico
Binary file not shown.
10 changes: 8 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ import ReactQueryProvider from "@/lib/providers/ReactQueryProvider"
import "@/styles/globals.css"

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "Børnebiblioteket",
description: "GO er en digital platform, der giver børn adgang til bøger, lydbøger og e-bøger.",
icons: [
{ rel: "icon", type: "image/png", url: "/favicon-96x96.png", sizes: "96x96" },
{ rel: "shortcut icon", url: "/favicon.ico" },
{ rel: "apple-touch-icon", sizes: "180x180", url: "/apple-touch-icon.png" },
],
manifest: "/site.webmanifest",
}

// When adding or changing fonts, remember to update the imports in .storybook/preview.tsx
Expand Down
2 changes: 1 addition & 1 deletion app/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const Page = async ({ searchParams: { q } }: { searchParams: { q: string } }) =>
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<Suspense fallback={<p>Loading...</p>}>
<SearchPageLayout searchQuery={q} />
<SearchPageLayout />
</Suspense>
</HydrationBoundary>
)
Expand Down
168 changes: 45 additions & 123 deletions components/pages/searchPageLayout/SearchPageLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,137 +1,53 @@
"use client"

import { useInfiniteQuery } from "@tanstack/react-query"
import { useInView } from "framer-motion"
import { motion } from "framer-motion"
import { useSearchParams } from "next/navigation"
import { useEffect, useRef, useState } from "react"
import { motion, useInView } from "framer-motion"
import { useEffect, useRef } from "react"

import SearchFiltersMobile from "@/components/shared/searchFilters/SearchFiltersMobile"
import { getFacetMachineNames } from "@/components/shared/searchFilters/helper"
import goConfig from "@/lib/config/config"
import {
FacetValue,
SearchFiltersInput,
useSearchFacetsQuery,
useSearchWithPaginationQuery,
} from "@/lib/graphql/generated/fbi/graphql"
import useSearchMachineActor from "@/lib/machines/search/useSearchMachineActor"

import SearchFiltersDesktop, {
SearchFiltersDesktopGhost,
} from "../../shared/searchFilters/SearchFiltersDesktop"
import SearchResults, { SearchResultsGhost } from "./SearchResults"
import { getFacetsForSearchRequest, getNextPageParamsFunc, getSearchQueryArguments } from "./helper"
import { useSearchDataAndLoadingStates } from "./helper"

const SEARCH_RESULTS_LIMIT = goConfig<number>("search.item.limit")

export type FilterItemTerm = Omit<FacetValue, "__typename">

const SearchPageLayout = ({ searchQuery }: { searchQuery?: string }) => {
const searchParams = useSearchParams()
const q = searchQuery || searchParams.get("q") || ""
const [currentQueryString, setCurrentQueryString] = useState("")
const [currentPage, setCurrentPage] = useState(0)
const [facetFilters, setFacetFilters] = useState<SearchFiltersInput>({})
const SearchPageLayout = () => {
const loadMoreRef = useRef(null)
const isInView = useInView(loadMoreRef)
const facets = getFacetMachineNames()

const facetsForSearchRequest = getFacetsForSearchRequest(searchParams)
const searchQueryArguments = getSearchQueryArguments({
q: currentQueryString,
currentPage,
facetFilters,
})

const {
data,
fetchNextPage,
isLoading: isLoadingResults,
isFetchingNextPage: isFetchingMoreResults,
isFetching: isFetchingResults,
isPending: isPendingResults,
} = useInfiniteQuery({
queryKey: useSearchWithPaginationQuery.getKey({
...searchQueryArguments,
offset: goConfig("search.offset.initial"),
}),
queryFn: useSearchWithPaginationQuery.fetcher(searchQueryArguments),
getNextPageParam: getNextPageParamsFunc(currentPage),
initialPageParam: goConfig<number>("search.param.initial"),
refetchOnWindowFocus: false,
enabled: currentQueryString?.length > 0, // Disable search result & search filter queries if q doesn't exist
})

const { data: dataFacets, isLoading: isLoadingFacets } = useSearchFacetsQuery(
{
q: searchQueryArguments.q,
facetLimit: goConfig("search.facet.limit"),
facets,
filters: searchQueryArguments.filters,
},
{
refetchOnWindowFocus: false,
enabled: currentQueryString?.length > 0,
}
)

const handleLoadMore = () => {
const totalPages = Math.ceil((data?.pages?.[0]?.search.hitcount ?? 0) / SEARCH_RESULTS_LIMIT)

if (currentPage < totalPages) {
fetchNextPage()
setCurrentPage(currentPage + 1)
}
}
const actor = useSearchMachineActor()
const { data, isLoadingFacets, isLoadingResults, machineIsReady, searchQuery } =
useSearchDataAndLoadingStates()

useEffect(() => {
if (isInView) {
handleLoadMore()
actor.send({ type: "LOAD_MORE" })
}
// We choose to ignore the eslint warning below
// because we do not want to add the handleMore callback which changes on every render.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isInView])

// TODO: consider finding a better way to control fetching of data without using the useEffects below
useEffect(() => {
const page = data?.pages.length || 0
setCurrentPage(page)
}, [data?.pages])

useEffect(() => {
setCurrentPage(0)
setCurrentQueryString(q)
}, [q])

useEffect(() => {
// Check if the filters in URL have changed
const isFilterMatching = JSON.stringify(facetFilters) === JSON.stringify(facetsForSearchRequest)
if (!isFilterMatching) {
setFacetFilters(facetsForSearchRequest)
setCurrentPage(0)
}
}, [facetFilters, facetsForSearchRequest])

const facetData = dataFacets?.search?.facets
const hitcount = data?.pages?.[0]?.search.hitcount ?? 0
const isLoading =
isLoadingResults || isFetchingMoreResults || isFetchingResults || isPendingResults
const isNoSearchResult = !isLoadingResults && (!data.search || !data.search.pages[0].length)
const hitCountText = data.search?.hitcount ? `(${data.search.hitcount})` : ""
const searchQueryText = searchQuery ? `"${searchQuery}"` : ""

return (
<div className="content-container my-grid-gap-2 space-y-grid-gap-2">
<h1 className="text-typo-heading-3 lg:text-typo-heading-2">
{`Viser resultater for "${q}" ${hitcount ? "(" + hitcount + ")" : ""}`}
</h1>
{q ? (
{searchQuery && (
<h1 className="text-typo-heading-3 lg:text-typo-heading-2">
{`Viser resultater for ${searchQueryText} ${hitCountText}`}
</h1>
)}
{searchQuery ? (
<>
{!isLoadingFacets && facetData && facetData?.length > 0 ? (
{!isLoadingFacets && data.facets && data.facets.length > 0 ? (
<div className="relative">
<div className="xl:hidden">
<SearchFiltersMobile facets={dataFacets.search.facets} />
<SearchFiltersMobile facets={data.facets} />
</div>
<div className="hidden xl:block">
<SearchFiltersDesktop facets={dataFacets.search.facets} />
<SearchFiltersDesktop facets={data.facets} />
</div>
</div>
) : (
Expand All @@ -144,28 +60,34 @@ const SearchPageLayout = ({ searchQuery }: { searchQuery?: string }) => {
)}
<hr className="-mx-grid-edge w-screen border-foreground opacity-10 md:mx-auto md:w-full" />
<div className="mb-space-y flex flex-col gap-y-[calc(var(--grid-gap-x)*2)]">
{data?.pages.map(
(page, i) =>
page.search.works && (
<motion.div
key={i}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
exit={{ opacity: 0 }}>
<SearchResults works={page.search.works} />
</motion.div>
)
)}
{isLoading && <SearchResultsGhost />}
{isNoSearchResult && <p>Ingen søgeresultat</p>}
{data.search &&
data.search.pages.map(
(works, i) =>
works && (
<motion.div
key={i}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
exit={{ opacity: 0 }}>
<SearchResults works={works} />
</motion.div>
)
)}
{isLoadingResults && <SearchResultsGhost />}
</div>
<div ref={loadMoreRef} className="h-0 opacity-0"></div>
</>
) : (
<div className="text-typo-body-1">
<p className="text-foreground opacity-80">Ingen søgeord fundet</p>
</div>
<>
{machineIsReady && (
<div className="text-typo-body-1">
<p className="text-foreground opacity-80">Ingen søgeord fundet</p>
</div>
)}
</>
)}
<div ref={loadMoreRef} className="h-0 opacity-0"></div>
</div>
)
}
Expand Down
4 changes: 2 additions & 2 deletions components/pages/searchPageLayout/SearchResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import React from "react"

import WorkCard, { WorkCardGhost } from "@/components/shared/workCard/WorkCard"
import { SearchWithPaginationQuery } from "@/lib/graphql/generated/fbi/graphql"
import { WorkTeaserFragment } from "@/lib/graphql/generated/fbi/graphql"

type SearchResultProps = {
works: SearchWithPaginationQuery["search"]["works"]
works: WorkTeaserFragment[]
}

const SearchResults = ({ works }: SearchResultProps) => {
Expand Down
34 changes: 32 additions & 2 deletions components/pages/searchPageLayout/helper.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { GetNextPageParamFunction } from "@tanstack/react-query"
import { useSelector } from "@xstate/react"
import { ReadonlyURLSearchParams } from "next/navigation"

import { getFacetMachineNames } from "@/components/shared/searchFilters/helper"
import goConfig from "@/lib/config/config"
import { TConfigSearchFacets } from "@/lib/config/resolvers/search"
import { SearchFiltersInput, SearchWithPaginationQuery } from "@/lib/graphql/generated/fbi/graphql"
import { TFilters } from "@/lib/machines/search/types"
import useSearchMachineActor from "@/lib/machines/search/useSearchMachineActor"

export const getSearchQueryArguments = ({
q,
Expand Down Expand Up @@ -32,7 +35,7 @@ export const getFacetsForSearchRequest = (searchParams: ReadonlyURLSearchParams)
const facetsMachineNames = getFacetMachineNames()

return facetsMachineNames.reduce(
(acc: SearchFiltersInput, machineName) => {
(acc: TFilters, machineName) => {
const values = searchParams.getAll(facets[machineName].filter)
if (values.length > 0) {
return {
Expand All @@ -42,7 +45,7 @@ export const getFacetsForSearchRequest = (searchParams: ReadonlyURLSearchParams)
}
return acc
},
{} as { [key: string]: keyof SearchFiltersInput[] }
{} as { [key: string]: keyof TFilters[] }
)
}

Expand All @@ -57,3 +60,30 @@ export const getNextPageParamsFunc = (
return currentPage < totalPages ? nextPage : undefined // By returning undefined if there are no more pages, hasNextPage boolean will be set to false
}
}

export const useSearchDataAndLoadingStates = () => {
const actor = useSearchMachineActor()
const searchQuery = useSelector(actor, snapshot => {
return snapshot.context.submittedQuery
})
const data = useSelector(actor, snapshot => {
const { facetData: facets, searchData: search } = snapshot.context
return { facets, search }
})
const isLoadingFacets =
!data.facets || actor.getSnapshot().matches({ filteringAndSearching: "filter" })
const isLoadingResults =
!data.search || actor.getSnapshot().matches({ filteringAndSearching: "search" })
const machineIsReady = !actor.getSnapshot().matches("bootstrap")

const selectedFilters = useSelector(actor, snapshot => snapshot.context.selectedFilters)

return {
searchQuery,
data,
selectedFilters,
isLoadingFacets,
isLoadingResults,
machineIsReady,
}
}
Loading

0 comments on commit bf658e0

Please sign in to comment.