diff --git a/pwa/package-lock.json b/pwa/package-lock.json index 8dbe3670..66a84a4c 100644 --- a/pwa/package-lock.json +++ b/pwa/package-lock.json @@ -8,8 +8,8 @@ "name": "product-website-template", "version": "1.0.0", "dependencies": { - "@conduction/components": "2.2.17", - "@conduction/theme": "1.0.49", + "@conduction/components": "2.2.18", + "@conduction/theme": "1.0.50", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.1.18", @@ -43,7 +43,7 @@ "@tabler/icons-react": "2.21.0", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "@utrecht/component-library-react": "^1.0.0-alpha.355", + "@utrecht/component-library-react": "^1.0.0-alpha.394", "@utrecht/design-tokens": "^1.0.0-alpha.524", "axios": "^0.25.0", "clsx": "^1.1.1", @@ -2055,9 +2055,9 @@ } }, "node_modules/@conduction/components": { - "version": "2.2.17", - "resolved": "https://registry.npmjs.org/@conduction/components/-/components-2.2.17.tgz", - "integrity": "sha512-xSrAuuWbaXQsdFiUQa/ldjovX7FbPseudz1Hc7GsjD5iFf8Nly+rNTXxkQp9QxOhTn5c8UdZlOrJ/V3qshafUA==", + "version": "2.2.18", + "resolved": "https://registry.npmjs.org/@conduction/components/-/components-2.2.18.tgz", + "integrity": "sha512-dXuDeA4BVGtNzQ0w2t2J3tDoqEHEOehwd0YYm68NWCDYMy/EtlX8tJpTrQSbXpkKt/pQV6dGt5WuBknWHOPHkA==", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.2.0", "@fortawesome/free-solid-svg-icons": "^6.2.0", @@ -2110,9 +2110,9 @@ } }, "node_modules/@conduction/theme": { - "version": "1.0.49", - "resolved": "https://registry.npmjs.org/@conduction/theme/-/theme-1.0.49.tgz", - "integrity": "sha512-uyEwfKH+5x1QsKlOlfG32HGrfAelkDh0g0C9ddWvaiii245nZXDntIkdtFv57j7LEF3rI0v4veoU6IkMX24q6g==", + "version": "1.0.50", + "resolved": "https://registry.npmjs.org/@conduction/theme/-/theme-1.0.50.tgz", + "integrity": "sha512-VdkN12+r12WORjVtl2W1kSLMaeZLtqRdrwtHJKuuUFGZmUnqDoxrdjPnX9c5in+MROLgIbBN1Xp/8/Ucy/Y6Lg==", "dependencies": { "@nl-design-system-unstable/rotterdam-design-tokens": "^1.0.0-alpha.100" } @@ -4351,16 +4351,30 @@ } }, "node_modules/@utrecht/component-library-react": { - "version": "1.0.0-alpha.356", - "license": "EUPL-1.2", + "version": "1.0.0-alpha.406", + "resolved": "https://registry.npmjs.org/@utrecht/component-library-react/-/component-library-react-1.0.0-alpha.406.tgz", + "integrity": "sha512-lZoV/O4EFDIYghVF8okdXBNlmal7bcvZ1jqNk+b1gac54TwpvTmmbd4dlwB2+kulN1vhwiFN+oUII3zOMuYCuQ==", "dependencies": { "clsx": "1.2.1", - "date-fns": "2.30.0", "lodash.chunk": "4.2.0" }, "peerDependencies": { + "date-fns": "^2.30.0", "react": "18", - "react-dom": "18" + "react-dom": "18", + "react-vega": "^7.6.0", + "vega": "^5.25.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "react-vega": { + "optional": true + }, + "vega": { + "optional": true + } } }, "node_modules/@utrecht/design-tokens": { diff --git a/pwa/package.json b/pwa/package.json index 6753018e..ea587d63 100644 --- a/pwa/package.json +++ b/pwa/package.json @@ -22,8 +22,8 @@ "prepare": "cd .. && husky install" }, "dependencies": { - "@conduction/components": "2.2.17", - "@conduction/theme": "1.0.49", + "@conduction/components": "2.2.18", + "@conduction/theme": "1.0.50", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.1.18", @@ -57,7 +57,7 @@ "@tabler/icons-react": "2.21.0", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", - "@utrecht/component-library-react": "^1.0.0-alpha.355", + "@utrecht/component-library-react": "^1.0.0-alpha.394", "@utrecht/design-tokens": "^1.0.0-alpha.524", "axios": "^0.25.0", "clsx": "^1.1.1", diff --git a/pwa/src/apiService/resources/openWoo.ts b/pwa/src/apiService/resources/openWoo.ts index 91174196..4a2f178f 100644 --- a/pwa/src/apiService/resources/openWoo.ts +++ b/pwa/src/apiService/resources/openWoo.ts @@ -14,8 +14,10 @@ export default class OpenWoo { this._send = send; } - public getAll = async (filters: IFiltersContext, currentPage: number): Promise => { - let endpoint = `/openWOO?extend[]=all${filtersToQueryParams(filters)}&_order[Publicatiedatum]=desc&_limit=${OPEN_WOO_LIMIT}&_page=${currentPage}`; + public getAll = async (filters: IFiltersContext, currentPage: number, limit: number): Promise => { + let endpoint = `/openWOO?extend[]=all${filtersToQueryParams( + filters, + )}&_order[Publicatiedatum]=desc&_limit=${limit}&_page=${currentPage}`; if (process.env.GATSBY_OIDN_NUMBER) { endpoint += `&oidn=${process.env.GATSBY_OIDN_NUMBER}`; diff --git a/pwa/src/components/paginationLimitSelect/PaginationLimitSelect.module.css b/pwa/src/components/paginationLimitSelect/PaginationLimitSelect.module.css new file mode 100644 index 00000000..4357e4cd --- /dev/null +++ b/pwa/src/components/paginationLimitSelect/PaginationLimitSelect.module.css @@ -0,0 +1,12 @@ +.container { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; + list-style-type: none; + + padding: 0; + margin: 0; + + user-select: none; +} diff --git a/pwa/src/components/paginationLimitSelect/PaginationLimitSelect.tsx b/pwa/src/components/paginationLimitSelect/PaginationLimitSelect.tsx new file mode 100644 index 00000000..0a516e0d --- /dev/null +++ b/pwa/src/components/paginationLimitSelect/PaginationLimitSelect.tsx @@ -0,0 +1,72 @@ +import * as React from "react"; +import * as styles from "./PaginationLimitSelect.module.css"; +import clsx from "clsx"; +import { useForm } from "react-hook-form"; +import { SelectSingle } from "@conduction/components"; +import { IQueryLimitContext, queryLimitDefault, useQueryLimitContext } from "../../context/queryLimit"; +import { useTranslation } from "react-i18next"; + +interface PaginationLimitSelectProps { + queryLimitName: string; + layoutClassName?: string; +} + +export const PaginationLimitSelectComponent: React.FC = ({ + queryLimitName, + layoutClassName, +}) => { + const { + watch, + register, + control, + setValue, + formState: { errors }, + } = useForm(); + const { queryLimit, setQueryLimit } = useQueryLimitContext(); + const { t } = useTranslation(); + + const watchLimit = watch("limit"); + + const value = queryLimit[queryLimitName as keyof IQueryLimitContext]; + + React.useEffect(() => { + if (!watchLimit) return; + if (parseInt(watchLimit.value) === value) return; + + const selectedLimit = limitSelectOptions.find((LimitOption) => LimitOption.value === watchLimit.value); + + selectedLimit !== undefined && setQueryLimit({ ...queryLimit, [queryLimitName]: parseInt(selectedLimit.value) }); + }, [watchLimit]); + + React.useEffect(() => { + setValue( + "limit", + limitSelectOptions.find((LimitOption) => LimitOption.value === (value !== undefined && value.toString())), + ); + }, []); + + return ( +
+ {`${t("Results per page")}:`} + +
+ ); +}; + +const limitSelectOptions = [ + { label: "6", value: "6" }, + { label: "9", value: "9" }, + { label: "12", value: "12" }, + { label: "21", value: "21" }, + { label: "30", value: "30" }, + { label: "60", value: "60" }, + { label: "120", value: "120" }, +]; diff --git a/pwa/src/context/global.ts b/pwa/src/context/global.ts index c9c3f4b8..189ac629 100644 --- a/pwa/src/context/global.ts +++ b/pwa/src/context/global.ts @@ -3,6 +3,7 @@ import { defaultGatsbyContext, IGatsbyContext } from "./gatsby"; import { defaultFiltersContext, IFiltersContext } from "./filters"; import { defaultDisplayContext, IDisplayContext } from "./displays"; import { defaultPaginationContext, IPaginationContext } from "./pagination"; +import { defaultQueryLimitContext, IQueryLimitContext } from "./queryLimit"; export interface IGlobalContext { initiated: boolean; @@ -10,6 +11,7 @@ export interface IGlobalContext { filters: IFiltersContext; displays: IDisplayContext; pagination: IPaginationContext; + queryLimit: IQueryLimitContext; } export const defaultGlobalContext: IGlobalContext = { @@ -18,6 +20,7 @@ export const defaultGlobalContext: IGlobalContext = { filters: defaultFiltersContext, displays: defaultDisplayContext, pagination: defaultPaginationContext, + queryLimit: defaultQueryLimitContext, }; export const GlobalContext = React.createContext< diff --git a/pwa/src/context/queryLimit.ts b/pwa/src/context/queryLimit.ts new file mode 100644 index 00000000..2a7c021c --- /dev/null +++ b/pwa/src/context/queryLimit.ts @@ -0,0 +1,24 @@ +import * as React from "react"; +import { GlobalContext } from "./global"; + +export const queryLimitDefault = 12; + +export interface IQueryLimitContext { + objectsQueryLimit: number; +} + +export const defaultQueryLimitContext: IQueryLimitContext = { + objectsQueryLimit: queryLimitDefault, +}; + +export const useQueryLimitContext = () => { + const [globalContext, setGlobalContext] = React.useContext(GlobalContext); + + const queryLimit: IQueryLimitContext = globalContext.queryLimit; + + const setQueryLimit = (query: IQueryLimitContext) => { + setGlobalContext((context) => ({ ...context, queryLimit: { ...globalContext.queryLimit, ...query } })); + }; + + return { setQueryLimit, queryLimit }; +}; diff --git a/pwa/src/hooks/openWoo.ts b/pwa/src/hooks/openWoo.ts index e3f7d642..11d6bb03 100644 --- a/pwa/src/hooks/openWoo.ts +++ b/pwa/src/hooks/openWoo.ts @@ -7,8 +7,8 @@ import { IFiltersContext } from "../context/filters"; export const useOpenWoo = (queryClient: QueryClient) => { const API: APIService | null = React.useContext(APIContext); - const getAll = (filters: IFiltersContext, currentPage: number) => - useQuery(["OpenWoo", filters, currentPage], () => API?.OpenWoo.getAll(filters, currentPage), { + const getAll = (filters: IFiltersContext, currentPage: number, limit: number) => + useQuery(["OpenWoo", filters, currentPage, limit], () => API?.OpenWoo.getAll(filters, currentPage, limit), { onError: (error) => { console.warn(error.message); }, diff --git a/pwa/src/templates/landing/LandingTemplate.module.css b/pwa/src/templates/landing/LandingTemplate.module.css index 48669c37..ddb02a7a 100644 --- a/pwa/src/templates/landing/LandingTemplate.module.css +++ b/pwa/src/templates/landing/LandingTemplate.module.css @@ -6,7 +6,7 @@ margin-block-end: var(--utrecht-space-block-lg); } -.header1 { - margin-block-start: 0px; - margin-block-end: 0px; +.pagination { + display: flex; + justify-content: space-between; } diff --git a/pwa/src/templates/landing/LandingTemplate.tsx b/pwa/src/templates/landing/LandingTemplate.tsx index fd8e27be..f7b1ac3c 100644 --- a/pwa/src/templates/landing/LandingTemplate.tsx +++ b/pwa/src/templates/landing/LandingTemplate.tsx @@ -11,18 +11,20 @@ import { QueryClient } from "react-query"; import { Pagination } from "@conduction/components"; import { usePaginationContext } from "../../context/pagination"; import { useTranslation } from "react-i18next"; +import { useQueryLimitContext } from "../../context/queryLimit"; +import { PaginationLimitSelectComponent } from "../../components/paginationLimitSelect/PaginationLimitSelect"; export const LandingTemplate: React.FC = () => { const { currentPage, setCurrentPage } = usePaginationContext(); const { filters } = useFiltersContext(); const { t } = useTranslation(); + const { queryLimit } = useQueryLimitContext(); const queryClient = new QueryClient(); - const getItems = useOpenWoo(queryClient).getAll(filters, currentPage); + const getItems = useOpenWoo(queryClient).getAll(filters, currentPage, queryLimit.objectsQueryLimit); return ( <> -

@@ -33,12 +35,14 @@ export const LandingTemplate: React.FC = () => { {getItems.data?.results && getItems.data?.results?.length > 0 && (
- - +
+ + +
)} {getItems.isLoading && } diff --git a/pwa/src/templates/templateParts/tableResultsTemplate/TableResultsTemplate.tsx b/pwa/src/templates/templateParts/tableResultsTemplate/TableResultsTemplate.tsx index 990c0164..13e2a5e9 100644 --- a/pwa/src/templates/templateParts/tableResultsTemplate/TableResultsTemplate.tsx +++ b/pwa/src/templates/templateParts/tableResultsTemplate/TableResultsTemplate.tsx @@ -11,6 +11,7 @@ import { import { navigate } from "gatsby"; import { translateDate } from "../../../services/dateFormat"; import { useTranslation } from "react-i18next"; +import { HorizontalOverflowWrapper } from "@conduction/components"; interface TableResultsTemplateProps { requests: any[]; @@ -20,35 +21,42 @@ export const TableResultsTemplate: React.FC = ({ requ const { t, i18n } = useTranslation(); return ( - - - - {t("Subject")} - {t("Publication date")} - {t("Summary")} - - - - {requests.map((request) => ( - navigate(request.id)} - tabIndex={0} - aria-label={`${request.Titel}, ${ - request.Publicatiedatum ? translateDate(i18n.language, request.Publicatiedatum) : t("N/A") - }, ${request.Samenvatting}`} - > - {request.Titel ?? t("No subject available")} - - {request.Publicatiedatum - ? translateDate(i18n.language, request.Publicatiedatum) - : t("No publication date available")} - - {request.Samenvatting ?? t("No summary available")} + +
+ + + {t("Subject")} + {t("Publication date")} + {t("Summary")} - ))} - -
+ + + {requests.map((request) => ( + navigate(request.id)} + tabIndex={0} + aria-label={`${request.Titel}, ${ + request.Publicatiedatum ? translateDate(i18n.language, request.Publicatiedatum) : t("N/A") + }, ${request.Samenvatting}`} + > + {request.Titel ?? t("No subject available")} + + {request.Publicatiedatum + ? translateDate(i18n.language, request.Publicatiedatum) + : t("No publication date available")} + + {request.Samenvatting ?? t("No summary available")} + + ))} + + + ); }; diff --git a/pwa/src/translations/en.ts b/pwa/src/translations/en.ts index 1570bfbd..9f4620f1 100644 --- a/pwa/src/translations/en.ts +++ b/pwa/src/translations/en.ts @@ -14,6 +14,7 @@ export const en = { Address: "Address", Page: "Page", Jumbotron: "Jumbotron", + Limit: "Limit", "Jumbotron card": "Jumbotron card", "N/A": "N/A", "Details page": "Details page", @@ -51,4 +52,8 @@ export const en = { "Can open a new window": "Can open a new window", "No results found": "No results found", "Enter search query": "Enter search query", + "Results per page": "Results per page", + "Select result limit": "Select result limit", + "Scroll table to the left": "Scroll table to the left", + "Scroll table to the right": "Scroll table to the right", }; diff --git a/pwa/src/translations/nl.ts b/pwa/src/translations/nl.ts index 49eae986..1df1e1cb 100644 --- a/pwa/src/translations/nl.ts +++ b/pwa/src/translations/nl.ts @@ -14,6 +14,7 @@ export const nl = { Address: "Adres", Page: "Pagina", Jumbotron: "Jumbotron", + Limit: "Limiet", "Jumbotron card": "Jumbotron tegel", "N/A": "N.v.t", "Details page": "Detailpagina", @@ -51,4 +52,8 @@ export const nl = { "Can open a new window": "Kan een nieuw venster openen", "No results found": "Geen resultaten gevonden", "Enter search query": "Voer zoekopdracht in", + "Results per page": "Resultaten per pagina", + "Select result limit": "Selecteer resultaten limiet", + "Scroll table to the left": "Scroll tabel naar links", + "Scroll table to the right": "Scroll tabel naar rechts", };