From 9013e0f7394552d2f770f56662e298d972f8eafa Mon Sep 17 00:00:00 2001 From: Michelle Date: Tue, 20 Jan 2026 15:33:28 +0100 Subject: [PATCH 01/33] wip --- .../src/pages/search/ResultsPage.tsx | 29 +++++++++++++++++++ .../src/pages/search/results.astro | 16 ++++++++++ 2 files changed, 45 insertions(+) create mode 100644 packages/theme-wizard-templates/src/pages/search/ResultsPage.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/results.astro diff --git a/packages/theme-wizard-templates/src/pages/search/ResultsPage.tsx b/packages/theme-wizard-templates/src/pages/search/ResultsPage.tsx new file mode 100644 index 000000000..0972fd9b1 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/ResultsPage.tsx @@ -0,0 +1,29 @@ +import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; +import React, { type FC } from 'react'; +import MainIntroSection from './Sections/MainIntro'; +import Navigation from './Sections/Navigation'; +import PageFooterSection from './Sections/PageFooter'; +import PageHeaderSection from './Sections/PageHeader'; +import './styles.css'; + +export interface ResultsPageProps { + currentPath?: string; +} + +const GemeenteVoorbeeldHome: FC = ({ currentPath }) => ( + <> + Skip to main content + + + + + +
+ +
+ + + +); + +export default GemeenteVoorbeeldHome; diff --git a/packages/theme-wizard-templates/src/pages/search/results.astro b/packages/theme-wizard-templates/src/pages/search/results.astro new file mode 100644 index 000000000..88cff51e5 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/results.astro @@ -0,0 +1,16 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import ResultsPage from './ResultsPage'; + +export const detail = { + name: 'Homepagina', + value: 'page', + module: 'Gemeentevoorbeeld', +}; + +const currentPath = Astro.url.pathname; +--- + + + + From 7515bd34f3dfe86b6d5de96ef9c39afc06ce21fd Mon Sep 17 00:00:00 2001 From: Michelle Date: Thu, 22 Jan 2026 09:28:21 +0100 Subject: [PATCH 02/33] feat: shared components --- .../src/templates/SearchResults.tsx | 28 +++++++++++++++++++ .../components/AccordionSection/index.tsx | 0 .../components/AccordionSection/types.ts | 0 .../components/Layout/Column.tsx | 0 .../components/Layout/Row.tsx | 0 .../components/Layout/index.ts | 0 .../components/Layout/styles.css | 0 .../components/NewsCards/index.tsx | 0 .../components/NewsCards/types.ts | 0 .../components/OpeningHoursCard/index.tsx | 0 .../OpeningHoursCard/openingHours.json | 0 .../components/OpeningHoursCard/types.ts | 0 .../components/QuickTasks/index.tsx | 0 .../components/QuickTasks/types.ts | 0 .../GemeenteVoorbeeldHome.tsx | 12 ++++---- .../{ResultsPage.tsx => SearchResults.tsx} | 8 +++--- .../src/pages/search/results.astro | 2 +- .../Sections => sections}/MainIntro/index.tsx | 0 .../Navigation/index.tsx | 2 +- .../Sections => sections}/Navigation/types.ts | 0 .../Sections => sections}/News/index.tsx | 0 .../PageFooter/index.tsx | 0 .../PageHeader/index.tsx | 0 .../SelfService/index.tsx | 0 24 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 packages/clippy-storybook/src/templates/SearchResults.tsx rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/AccordionSection/index.tsx (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/AccordionSection/types.ts (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/Layout/Column.tsx (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/Layout/Row.tsx (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/Layout/index.ts (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/Layout/styles.css (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/NewsCards/index.tsx (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/NewsCards/types.ts (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/OpeningHoursCard/index.tsx (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/OpeningHoursCard/openingHours.json (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/OpeningHoursCard/types.ts (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/QuickTasks/index.tsx (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld => }/components/QuickTasks/types.ts (100%) rename packages/theme-wizard-templates/src/pages/search/{ResultsPage.tsx => SearchResults.tsx} (73%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld/Sections => sections}/MainIntro/index.tsx (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld/Sections => sections}/Navigation/index.tsx (93%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld/Sections => sections}/Navigation/types.ts (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld/Sections => sections}/News/index.tsx (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld/Sections => sections}/PageFooter/index.tsx (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld/Sections => sections}/PageHeader/index.tsx (100%) rename packages/theme-wizard-templates/src/{pages/gemeentevoorbeeld/Sections => sections}/SelfService/index.tsx (100%) diff --git a/packages/clippy-storybook/src/templates/SearchResults.tsx b/packages/clippy-storybook/src/templates/SearchResults.tsx new file mode 100644 index 000000000..49b195ed8 --- /dev/null +++ b/packages/clippy-storybook/src/templates/SearchResults.tsx @@ -0,0 +1,28 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; +import SearchResults, { + type SearchResultsProps, +} from '@nl-design-system-community/theme-wizard-templates/src/pages/search/SearchResults'; +import * as React from 'react'; +import '@utrecht/component-library-css'; +import documentation from '../docs/templates/gemeente-voorbeeld-documentatie.md?raw'; + +const meta = { + component: SearchResults as React.ComponentType, + parameters: { + docs: { + description: { + component: documentation, + }, + }, + layout: 'fullscreen', + }, + title: 'Templates/Zoekresultaten', +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + name: 'Zoekresultaten', +}; diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/AccordionSection/index.tsx b/packages/theme-wizard-templates/src/components/AccordionSection/index.tsx similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/AccordionSection/index.tsx rename to packages/theme-wizard-templates/src/components/AccordionSection/index.tsx diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/AccordionSection/types.ts b/packages/theme-wizard-templates/src/components/AccordionSection/types.ts similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/AccordionSection/types.ts rename to packages/theme-wizard-templates/src/components/AccordionSection/types.ts diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/Layout/Column.tsx b/packages/theme-wizard-templates/src/components/Layout/Column.tsx similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/Layout/Column.tsx rename to packages/theme-wizard-templates/src/components/Layout/Column.tsx diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/Layout/Row.tsx b/packages/theme-wizard-templates/src/components/Layout/Row.tsx similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/Layout/Row.tsx rename to packages/theme-wizard-templates/src/components/Layout/Row.tsx diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/Layout/index.ts b/packages/theme-wizard-templates/src/components/Layout/index.ts similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/Layout/index.ts rename to packages/theme-wizard-templates/src/components/Layout/index.ts diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/Layout/styles.css b/packages/theme-wizard-templates/src/components/Layout/styles.css similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/Layout/styles.css rename to packages/theme-wizard-templates/src/components/Layout/styles.css diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/NewsCards/index.tsx b/packages/theme-wizard-templates/src/components/NewsCards/index.tsx similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/NewsCards/index.tsx rename to packages/theme-wizard-templates/src/components/NewsCards/index.tsx diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/NewsCards/types.ts b/packages/theme-wizard-templates/src/components/NewsCards/types.ts similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/NewsCards/types.ts rename to packages/theme-wizard-templates/src/components/NewsCards/types.ts diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/OpeningHoursCard/index.tsx b/packages/theme-wizard-templates/src/components/OpeningHoursCard/index.tsx similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/OpeningHoursCard/index.tsx rename to packages/theme-wizard-templates/src/components/OpeningHoursCard/index.tsx diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/OpeningHoursCard/openingHours.json b/packages/theme-wizard-templates/src/components/OpeningHoursCard/openingHours.json similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/OpeningHoursCard/openingHours.json rename to packages/theme-wizard-templates/src/components/OpeningHoursCard/openingHours.json diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/OpeningHoursCard/types.ts b/packages/theme-wizard-templates/src/components/OpeningHoursCard/types.ts similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/OpeningHoursCard/types.ts rename to packages/theme-wizard-templates/src/components/OpeningHoursCard/types.ts diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/QuickTasks/index.tsx b/packages/theme-wizard-templates/src/components/QuickTasks/index.tsx similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/QuickTasks/index.tsx rename to packages/theme-wizard-templates/src/components/QuickTasks/index.tsx diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/QuickTasks/types.ts b/packages/theme-wizard-templates/src/components/QuickTasks/types.ts similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/components/QuickTasks/types.ts rename to packages/theme-wizard-templates/src/components/QuickTasks/types.ts diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx index e9d21de8e..30938c46f 100644 --- a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx +++ b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx @@ -2,12 +2,12 @@ import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; import { PageBody } from '@utrecht/page-body-react'; import React from 'react'; import type { SummaryItem } from './components/OpeningHoursCard/types'; -import MainIntroSection from './Sections/MainIntro'; -import Navigation from './Sections/Navigation'; -import NewsSection from './Sections/News'; -import PageFooterSection from './Sections/PageFooter'; -import PageHeaderSection from './Sections/PageHeader'; -import SelfServiceSection from './Sections/SelfService'; +import MainIntroSection from './sections/MainIntro'; +import Navigation from './sections/Navigation'; +import NewsSection from './sections/News'; +import PageFooterSection from './sections/PageFooter'; +import PageHeaderSection from './sections/PageHeader'; +import SelfServiceSection from './sections/SelfService'; import './styles.css'; export interface GemeenteVoorbeeldHomeProps { diff --git a/packages/theme-wizard-templates/src/pages/search/ResultsPage.tsx b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx similarity index 73% rename from packages/theme-wizard-templates/src/pages/search/ResultsPage.tsx rename to packages/theme-wizard-templates/src/pages/search/SearchResults.tsx index 0972fd9b1..aebf80b41 100644 --- a/packages/theme-wizard-templates/src/pages/search/ResultsPage.tsx +++ b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx @@ -1,16 +1,16 @@ import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; import React, { type FC } from 'react'; -import MainIntroSection from './Sections/MainIntro'; +import MainIntroSection from '../Sections/MainIntro'; import Navigation from './Sections/Navigation'; import PageFooterSection from './Sections/PageFooter'; import PageHeaderSection from './Sections/PageHeader'; import './styles.css'; -export interface ResultsPageProps { +export interface SearchResultsProps { currentPath?: string; } -const GemeenteVoorbeeldHome: FC = ({ currentPath }) => ( +const SearchResults: FC = ({ currentPath }) => ( <> Skip to main content @@ -26,4 +26,4 @@ const GemeenteVoorbeeldHome: FC = ({ currentPath }) => ( ); -export default GemeenteVoorbeeldHome; +export default SearchResults; diff --git a/packages/theme-wizard-templates/src/pages/search/results.astro b/packages/theme-wizard-templates/src/pages/search/results.astro index 88cff51e5..624776b8d 100644 --- a/packages/theme-wizard-templates/src/pages/search/results.astro +++ b/packages/theme-wizard-templates/src/pages/search/results.astro @@ -1,6 +1,6 @@ --- import BaseLayout from '../../layouts/BaseLayout.astro'; -import ResultsPage from './ResultsPage'; +import ResultsPage from './SearchResults'; export const detail = { name: 'Homepagina', diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/MainIntro/index.tsx b/packages/theme-wizard-templates/src/sections/MainIntro/index.tsx similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/MainIntro/index.tsx rename to packages/theme-wizard-templates/src/sections/MainIntro/index.tsx diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/Navigation/index.tsx b/packages/theme-wizard-templates/src/sections/Navigation/index.tsx similarity index 93% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/Navigation/index.tsx rename to packages/theme-wizard-templates/src/sections/Navigation/index.tsx index d9ec3c575..58d1f2b5f 100644 --- a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/Navigation/index.tsx +++ b/packages/theme-wizard-templates/src/sections/Navigation/index.tsx @@ -2,7 +2,7 @@ import { Heading } from '@nl-design-system-candidate/heading-react/css'; import '@amsterdam/design-system-css/dist/visually-hidden/visually-hidden.css'; import { NavBar, NavList, NavListLink } from '@utrecht/component-library-react'; import React from 'react'; -import { NAVIGATION_ITEMS } from '../..//constants'; +import { NAVIGATION_ITEMS } from '../../constants'; export interface NavigationProps { currentPath?: string; diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/Navigation/types.ts b/packages/theme-wizard-templates/src/sections/Navigation/types.ts similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/Navigation/types.ts rename to packages/theme-wizard-templates/src/sections/Navigation/types.ts diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/News/index.tsx b/packages/theme-wizard-templates/src/sections/News/index.tsx similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/News/index.tsx rename to packages/theme-wizard-templates/src/sections/News/index.tsx diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/PageFooter/index.tsx b/packages/theme-wizard-templates/src/sections/PageFooter/index.tsx similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/PageFooter/index.tsx rename to packages/theme-wizard-templates/src/sections/PageFooter/index.tsx diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/PageHeader/index.tsx b/packages/theme-wizard-templates/src/sections/PageHeader/index.tsx similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/PageHeader/index.tsx rename to packages/theme-wizard-templates/src/sections/PageHeader/index.tsx diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/SelfService/index.tsx b/packages/theme-wizard-templates/src/sections/SelfService/index.tsx similarity index 100% rename from packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/Sections/SelfService/index.tsx rename to packages/theme-wizard-templates/src/sections/SelfService/index.tsx From 700aa509e23e4fb9d6160103297a28f16d21a016 Mon Sep 17 00:00:00 2001 From: Michelle Date: Thu, 22 Jan 2026 15:16:55 +0100 Subject: [PATCH 03/33] chore: case insensitive stylesheet selector --- packages/clippy-storybook/config/decorators.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/clippy-storybook/config/decorators.ts b/packages/clippy-storybook/config/decorators.ts index 616449b3c..9e66148ca 100644 --- a/packages/clippy-storybook/config/decorators.ts +++ b/packages/clippy-storybook/config/decorators.ts @@ -1,7 +1,7 @@ import { THEMES } from './themes/theme-data'; const stylesheetExists = (href: string): HTMLLinkElement | undefined => - Array.from(document.querySelectorAll('link[rel="stylesheet"]')).find((link) => link.href === href); + Array.from(document.querySelectorAll('link[rel~="stylesheet"]')).find((link) => link.href === href); const ensureThemeStylesheet = (root: HTMLElement, themeClassName: string) => { const head = document.querySelector('head'); From a73fd9b613e6352d9176b345fa9ec76202c331d2 Mon Sep 17 00:00:00 2001 From: Michelle Date: Thu, 22 Jan 2026 15:22:06 +0100 Subject: [PATCH 04/33] fix: relative file refs --- .../gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx | 16 ++++++++-------- .../src/pages/gemeentevoorbeeld/page.astro | 2 +- .../src/pages/search/SearchResults.tsx | 9 ++++----- .../src/sections/MainIntro/index.tsx | 2 +- .../src/sections/Navigation/index.tsx | 2 +- .../src/sections/News/index.tsx | 2 +- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx index 30938c46f..24c0d7565 100644 --- a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx +++ b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx @@ -1,13 +1,13 @@ import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; -import { PageBody } from '@utrecht/page-body-react'; import React from 'react'; -import type { SummaryItem } from './components/OpeningHoursCard/types'; -import MainIntroSection from './sections/MainIntro'; -import Navigation from './sections/Navigation'; -import NewsSection from './sections/News'; -import PageFooterSection from './sections/PageFooter'; -import PageHeaderSection from './sections/PageHeader'; -import SelfServiceSection from './sections/SelfService'; +import { PageBody } from '@utrecht/page-body-react'; +import type { SummaryItem } from '../../components/OpeningHoursCard/types'; +import MainIntroSection from '../../sections/MainIntro'; +import Navigation from '../../sections/Navigation'; +import NewsSection from '../../sections/News'; +import PageFooterSection from '../../sections/PageFooter'; +import PageHeaderSection from '../../sections/PageHeader'; +import SelfServiceSection from '../../sections/SelfService'; import './styles.css'; export interface GemeenteVoorbeeldHomeProps { diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/page.astro b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/page.astro index 501576197..9e64ec1e0 100644 --- a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/page.astro +++ b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/page.astro @@ -1,7 +1,7 @@ --- import BaseLayout from '../../layouts/BaseLayout.astro'; import GemeenteVoorbeeldHome from './GemeenteVoorbeeldHome'; -import openingHoursData from './components/OpeningHoursCard/openingHours.json'; +import openingHoursData from '../../components/OpeningHoursCard/openingHours.json'; export const detail = { name: 'Homepagina', diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx index aebf80b41..e0d5beed4 100644 --- a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx +++ b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx @@ -1,10 +1,9 @@ import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; import React, { type FC } from 'react'; -import MainIntroSection from '../Sections/MainIntro'; -import Navigation from './Sections/Navigation'; -import PageFooterSection from './Sections/PageFooter'; -import PageHeaderSection from './Sections/PageHeader'; -import './styles.css'; +import MainIntroSection from '../../sections/MainIntro'; +import Navigation from '../../sections/Navigation'; +import PageFooterSection from '../../sections/PageFooter'; +import PageHeaderSection from '../../sections/PageHeader'; export interface SearchResultsProps { currentPath?: string; diff --git a/packages/theme-wizard-templates/src/sections/MainIntro/index.tsx b/packages/theme-wizard-templates/src/sections/MainIntro/index.tsx index 89b9128bf..afaaec967 100644 --- a/packages/theme-wizard-templates/src/sections/MainIntro/index.tsx +++ b/packages/theme-wizard-templates/src/sections/MainIntro/index.tsx @@ -6,7 +6,7 @@ import type { OpeningHoursCardProps } from '../../components/OpeningHoursCard/ty import { Column, Row } from '../../components/Layout'; import OpeningHoursCard from '../../components/OpeningHoursCard'; import QuickTasks from '../../components/QuickTasks'; -import { QUICK_TASKS } from '../../constants'; +import { QUICK_TASKS } from '../../pages/gemeentevoorbeeld/constants'; const MainIntroSection = ({ openingHoursSummary }: OpeningHoursCardProps) => ( diff --git a/packages/theme-wizard-templates/src/sections/Navigation/index.tsx b/packages/theme-wizard-templates/src/sections/Navigation/index.tsx index 58d1f2b5f..f8b3ee3ee 100644 --- a/packages/theme-wizard-templates/src/sections/Navigation/index.tsx +++ b/packages/theme-wizard-templates/src/sections/Navigation/index.tsx @@ -2,7 +2,7 @@ import { Heading } from '@nl-design-system-candidate/heading-react/css'; import '@amsterdam/design-system-css/dist/visually-hidden/visually-hidden.css'; import { NavBar, NavList, NavListLink } from '@utrecht/component-library-react'; import React from 'react'; -import { NAVIGATION_ITEMS } from '../../constants'; +import { NAVIGATION_ITEMS } from '../../pages/gemeentevoorbeeld/constants'; export interface NavigationProps { currentPath?: string; diff --git a/packages/theme-wizard-templates/src/sections/News/index.tsx b/packages/theme-wizard-templates/src/sections/News/index.tsx index e932d6e6c..68ed50d46 100644 --- a/packages/theme-wizard-templates/src/sections/News/index.tsx +++ b/packages/theme-wizard-templates/src/sections/News/index.tsx @@ -2,7 +2,7 @@ import { Heading } from '@nl-design-system-candidate/heading-react/css'; import { PageContent } from '@utrecht/component-library-react/dist/css-module'; import React from 'react'; import NewsCards from '../../components/NewsCards'; -import { NEWS_ITEMS } from '../../constants'; +import { NEWS_ITEMS } from '../../pages/gemeentevoorbeeld/constants'; export interface NewsSectionProps { heading?: string; From 9afe27c77f91c78d2926101a88755aad2710e2cc Mon Sep 17 00:00:00 2001 From: Michelle Date: Thu, 22 Jan 2026 15:22:43 +0100 Subject: [PATCH 05/33] fix: import clippy-code --- packages/theme-wizard-templates/src/components/ClientInit.astro | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/theme-wizard-templates/src/components/ClientInit.astro b/packages/theme-wizard-templates/src/components/ClientInit.astro index 36cc04d73..eaa644f79 100644 --- a/packages/theme-wizard-templates/src/components/ClientInit.astro +++ b/packages/theme-wizard-templates/src/components/ClientInit.astro @@ -4,5 +4,6 @@ }); import '@nl-design-system-community/clippy-components/clippy-heading'; + import '@nl-design-system-community/clippy-components/clippy-code'; import '../components'; From f4cb22766ad6eea56b85876b067e04688de3d4d4 Mon Sep 17 00:00:00 2001 From: Michelle Date: Thu, 22 Jan 2026 15:24:04 +0100 Subject: [PATCH 06/33] chore: rename file --- .../src/pages/search/{results.astro => page.astro} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename packages/theme-wizard-templates/src/pages/search/{results.astro => page.astro} (83%) diff --git a/packages/theme-wizard-templates/src/pages/search/results.astro b/packages/theme-wizard-templates/src/pages/search/page.astro similarity index 83% rename from packages/theme-wizard-templates/src/pages/search/results.astro rename to packages/theme-wizard-templates/src/pages/search/page.astro index 624776b8d..42a9c5d53 100644 --- a/packages/theme-wizard-templates/src/pages/search/results.astro +++ b/packages/theme-wizard-templates/src/pages/search/page.astro @@ -3,9 +3,9 @@ import BaseLayout from '../../layouts/BaseLayout.astro'; import ResultsPage from './SearchResults'; export const detail = { - name: 'Homepagina', + name: 'Resultaten', value: 'page', - module: 'Gemeentevoorbeeld', + module: 'Zoeken', }; const currentPath = Astro.url.pathname; From 6a94c70417ddec4b10cd053be6b6fa7090a6f435 Mon Sep 17 00:00:00 2001 From: Michelle Date: Thu, 22 Jan 2026 15:30:23 +0100 Subject: [PATCH 07/33] feat: foldering and structuring --- .../{ => Cookies/Drawer}/Cookies.astro | 0 .../src/components/index.ts | 18 +++++++++--------- .../src/layouts/PageLayout.astro | 2 +- .../theme-wizard-templates/src/pages/index.ts | 11 ----------- .../src/pages/search/SearchResults.tsx | 3 +-- .../action}/index.ts | 0 .../case-card}/index.ts | 0 .../case-card}/styles.ts | 0 .../color-swatch}/index.ts | 0 .../color-swatch}/styles.ts | 0 .../link-list}/index.ts | 0 .../link}/index.ts | 0 .../page-header}/index.ts | 0 .../paragraph}/index.ts | 0 .../side-nav}/index.ts | 0 .../skip-link}/index.ts | 0 16 files changed, 11 insertions(+), 23 deletions(-) rename packages/theme-wizard-templates/src/components/{ => Cookies/Drawer}/Cookies.astro (100%) delete mode 100644 packages/theme-wizard-templates/src/pages/index.ts rename packages/theme-wizard-templates/src/{components/template-action => webcomponents/action}/index.ts (100%) rename packages/theme-wizard-templates/src/{components/template-case-card => webcomponents/case-card}/index.ts (100%) rename packages/theme-wizard-templates/src/{components/template-case-card => webcomponents/case-card}/styles.ts (100%) rename packages/theme-wizard-templates/src/{components/template-color-swatch => webcomponents/color-swatch}/index.ts (100%) rename packages/theme-wizard-templates/src/{components/template-color-swatch => webcomponents/color-swatch}/styles.ts (100%) rename packages/theme-wizard-templates/src/{components/template-link-list => webcomponents/link-list}/index.ts (100%) rename packages/theme-wizard-templates/src/{components/template-link => webcomponents/link}/index.ts (100%) rename packages/theme-wizard-templates/src/{components/template-page-header => webcomponents/page-header}/index.ts (100%) rename packages/theme-wizard-templates/src/{components/template-paragraph => webcomponents/paragraph}/index.ts (100%) rename packages/theme-wizard-templates/src/{components/template-side-nav => webcomponents/side-nav}/index.ts (100%) rename packages/theme-wizard-templates/src/{components/template-skip-link => webcomponents/skip-link}/index.ts (100%) diff --git a/packages/theme-wizard-templates/src/components/Cookies.astro b/packages/theme-wizard-templates/src/components/Cookies/Drawer/Cookies.astro similarity index 100% rename from packages/theme-wizard-templates/src/components/Cookies.astro rename to packages/theme-wizard-templates/src/components/Cookies/Drawer/Cookies.astro diff --git a/packages/theme-wizard-templates/src/components/index.ts b/packages/theme-wizard-templates/src/components/index.ts index b95b05ad1..38f9ca8f0 100644 --- a/packages/theme-wizard-templates/src/components/index.ts +++ b/packages/theme-wizard-templates/src/components/index.ts @@ -1,9 +1,9 @@ -export * from './template-action'; -export * from './template-case-card'; -export * from './template-color-swatch'; -export * from './template-link'; -export * from './template-link-list'; -export * from './template-page-header'; -export * from './template-paragraph'; -export * from './template-side-nav'; -export * from './template-skip-link'; \ No newline at end of file +export * from '../webcomponents/action'; +export * from '../webcomponents/case-card'; +export * from '../webcomponents/color-swatch'; +export * from '../webcomponents/link'; +export * from '../webcomponents/link-list'; +export * from '../webcomponents/page-header'; +export * from '../webcomponents/paragraph'; +export * from '../webcomponents/side-nav'; +export * from '../webcomponents/skip-link'; \ No newline at end of file diff --git a/packages/theme-wizard-templates/src/layouts/PageLayout.astro b/packages/theme-wizard-templates/src/layouts/PageLayout.astro index 857a8c6c7..34ee5c726 100644 --- a/packages/theme-wizard-templates/src/layouts/PageLayout.astro +++ b/packages/theme-wizard-templates/src/layouts/PageLayout.astro @@ -1,7 +1,7 @@ --- import logo from '../assets/logo.svg'; import BaseLayout from './BaseLayout.astro'; -import Cookies from '../components/Cookies.astro'; +import Cookies from '../components/Cookies/Drawer/Cookies.astro'; export interface Props { sideNav?: boolean; diff --git a/packages/theme-wizard-templates/src/pages/index.ts b/packages/theme-wizard-templates/src/pages/index.ts deleted file mode 100644 index e898fb005..000000000 --- a/packages/theme-wizard-templates/src/pages/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export * from '../exports/react-pages'; - -// Astro pages (default exports) -export { default as IndexPage } from './index.astro'; -export { default as MyEnvironmentOverviewPage } from './my-environment/overview.astro'; -export { default as GemeenteVoorbeeldPage } from './gemeentevoorbeeld/page.astro'; -export { default as MultiStepFormStep1Page } from './multi-step-form/step-1.astro'; -export { default as MultiStepFormStep2Page } from './multi-step-form/step-2.astro'; -export { default as PreviewComponentsCollage1Page } from './preview-components/collage-1.astro'; -export { default as PreviewComponentsColorSwatchPage } from './preview-components/color-swatch.astro'; -export { default as PreviewComponentsFontPropertiesPage } from './preview-components/font-properties.astro'; diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx index e0d5beed4..00cc2ba39 100644 --- a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx +++ b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx @@ -1,6 +1,5 @@ import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; import React, { type FC } from 'react'; -import MainIntroSection from '../../sections/MainIntro'; import Navigation from '../../sections/Navigation'; import PageFooterSection from '../../sections/PageFooter'; import PageHeaderSection from '../../sections/PageHeader'; @@ -18,7 +17,7 @@ const SearchResults: FC = ({ currentPath }) => (
- +
diff --git a/packages/theme-wizard-templates/src/components/template-action/index.ts b/packages/theme-wizard-templates/src/webcomponents/action/index.ts similarity index 100% rename from packages/theme-wizard-templates/src/components/template-action/index.ts rename to packages/theme-wizard-templates/src/webcomponents/action/index.ts diff --git a/packages/theme-wizard-templates/src/components/template-case-card/index.ts b/packages/theme-wizard-templates/src/webcomponents/case-card/index.ts similarity index 100% rename from packages/theme-wizard-templates/src/components/template-case-card/index.ts rename to packages/theme-wizard-templates/src/webcomponents/case-card/index.ts diff --git a/packages/theme-wizard-templates/src/components/template-case-card/styles.ts b/packages/theme-wizard-templates/src/webcomponents/case-card/styles.ts similarity index 100% rename from packages/theme-wizard-templates/src/components/template-case-card/styles.ts rename to packages/theme-wizard-templates/src/webcomponents/case-card/styles.ts diff --git a/packages/theme-wizard-templates/src/components/template-color-swatch/index.ts b/packages/theme-wizard-templates/src/webcomponents/color-swatch/index.ts similarity index 100% rename from packages/theme-wizard-templates/src/components/template-color-swatch/index.ts rename to packages/theme-wizard-templates/src/webcomponents/color-swatch/index.ts diff --git a/packages/theme-wizard-templates/src/components/template-color-swatch/styles.ts b/packages/theme-wizard-templates/src/webcomponents/color-swatch/styles.ts similarity index 100% rename from packages/theme-wizard-templates/src/components/template-color-swatch/styles.ts rename to packages/theme-wizard-templates/src/webcomponents/color-swatch/styles.ts diff --git a/packages/theme-wizard-templates/src/components/template-link-list/index.ts b/packages/theme-wizard-templates/src/webcomponents/link-list/index.ts similarity index 100% rename from packages/theme-wizard-templates/src/components/template-link-list/index.ts rename to packages/theme-wizard-templates/src/webcomponents/link-list/index.ts diff --git a/packages/theme-wizard-templates/src/components/template-link/index.ts b/packages/theme-wizard-templates/src/webcomponents/link/index.ts similarity index 100% rename from packages/theme-wizard-templates/src/components/template-link/index.ts rename to packages/theme-wizard-templates/src/webcomponents/link/index.ts diff --git a/packages/theme-wizard-templates/src/components/template-page-header/index.ts b/packages/theme-wizard-templates/src/webcomponents/page-header/index.ts similarity index 100% rename from packages/theme-wizard-templates/src/components/template-page-header/index.ts rename to packages/theme-wizard-templates/src/webcomponents/page-header/index.ts diff --git a/packages/theme-wizard-templates/src/components/template-paragraph/index.ts b/packages/theme-wizard-templates/src/webcomponents/paragraph/index.ts similarity index 100% rename from packages/theme-wizard-templates/src/components/template-paragraph/index.ts rename to packages/theme-wizard-templates/src/webcomponents/paragraph/index.ts diff --git a/packages/theme-wizard-templates/src/components/template-side-nav/index.ts b/packages/theme-wizard-templates/src/webcomponents/side-nav/index.ts similarity index 100% rename from packages/theme-wizard-templates/src/components/template-side-nav/index.ts rename to packages/theme-wizard-templates/src/webcomponents/side-nav/index.ts diff --git a/packages/theme-wizard-templates/src/components/template-skip-link/index.ts b/packages/theme-wizard-templates/src/webcomponents/skip-link/index.ts similarity index 100% rename from packages/theme-wizard-templates/src/components/template-skip-link/index.ts rename to packages/theme-wizard-templates/src/webcomponents/skip-link/index.ts From d201cb613f35e0dcbbce83fe5eaa585a8d36d0b9 Mon Sep 17 00:00:00 2001 From: Michelle Date: Thu, 22 Jan 2026 16:57:39 +0100 Subject: [PATCH 08/33] chore: separate files for styles --- .../src/components/NewsCards/index.tsx | 2 + .../src/components/NewsCards/styles.css | 6 ++ .../src/components/OpeningHoursCard/index.tsx | 1 + .../src/components/QuickTasks/index.tsx | 2 + .../src/components/QuickTasks/styles.css | 4 ++ .../src/components/shared/card-styles.css | 13 ++++ .../src/layouts/BaseLayout.astro | 1 + .../GemeenteVoorbeeldHome.tsx | 1 - .../src/pages/gemeentevoorbeeld/styles.css | 66 ------------------- .../src/pages/search/SearchResults.tsx | 17 +++-- .../src/pages/search/page.astro | 6 +- .../src/sections/PageHeader/index.tsx | 1 + .../src/sections/PageHeader/styles.css | 11 ++++ .../src/sections/SelfService/index.tsx | 1 + .../src/sections/SelfService/styles.css | 15 +++++ .../src/styles/base.css | 5 ++ 16 files changed, 77 insertions(+), 75 deletions(-) create mode 100644 packages/theme-wizard-templates/src/components/NewsCards/styles.css create mode 100644 packages/theme-wizard-templates/src/components/QuickTasks/styles.css create mode 100644 packages/theme-wizard-templates/src/components/shared/card-styles.css delete mode 100644 packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/styles.css create mode 100644 packages/theme-wizard-templates/src/sections/PageHeader/styles.css create mode 100644 packages/theme-wizard-templates/src/sections/SelfService/styles.css create mode 100644 packages/theme-wizard-templates/src/styles/base.css diff --git a/packages/theme-wizard-templates/src/components/NewsCards/index.tsx b/packages/theme-wizard-templates/src/components/NewsCards/index.tsx index 94f31748d..b1ac74e03 100644 --- a/packages/theme-wizard-templates/src/components/NewsCards/index.tsx +++ b/packages/theme-wizard-templates/src/components/NewsCards/index.tsx @@ -7,6 +7,8 @@ import { Icon } from '@utrecht/component-library-react'; import React from 'react'; import type { NewsItem } from './types'; import { Column, Row } from '../Layout'; +import '../shared/card-styles.css'; +import './styles.css'; export interface NewsCardsProps { cards: NewsItem[]; diff --git a/packages/theme-wizard-templates/src/components/NewsCards/styles.css b/packages/theme-wizard-templates/src/components/NewsCards/styles.css new file mode 100644 index 000000000..68db6ed66 --- /dev/null +++ b/packages/theme-wizard-templates/src/components/NewsCards/styles.css @@ -0,0 +1,6 @@ +.clippy-voorbeeld__link header { + display: flex; + flex-direction: column; + justify-content: space-between; + row-gap: var(--basis-space-row-xl); +} diff --git a/packages/theme-wizard-templates/src/components/OpeningHoursCard/index.tsx b/packages/theme-wizard-templates/src/components/OpeningHoursCard/index.tsx index 78d3fe878..ee75e745c 100644 --- a/packages/theme-wizard-templates/src/components/OpeningHoursCard/index.tsx +++ b/packages/theme-wizard-templates/src/components/OpeningHoursCard/index.tsx @@ -4,6 +4,7 @@ import { Icon } from '@utrecht/component-library-react'; import { ButtonLink } from '@utrecht/component-library-react/dist/css-module'; import React, { Fragment, useMemo } from 'react'; import type { OpeningHoursCardProps } from './types'; +import '../shared/card-styles.css'; const formatTime = (time: string): string => time.substring(0, 5); diff --git a/packages/theme-wizard-templates/src/components/QuickTasks/index.tsx b/packages/theme-wizard-templates/src/components/QuickTasks/index.tsx index 3b71241b5..02e08c6b5 100644 --- a/packages/theme-wizard-templates/src/components/QuickTasks/index.tsx +++ b/packages/theme-wizard-templates/src/components/QuickTasks/index.tsx @@ -12,6 +12,8 @@ import { import React, { type ReactElement } from 'react'; import type { QuickTask } from './types'; import { Column, Row } from '../Layout'; +import '../shared/card-styles.css'; +import './styles.css'; export interface QuickTasksProps { tasks: QuickTask[]; diff --git a/packages/theme-wizard-templates/src/components/QuickTasks/styles.css b/packages/theme-wizard-templates/src/components/QuickTasks/styles.css new file mode 100644 index 000000000..fa6dedbb5 --- /dev/null +++ b/packages/theme-wizard-templates/src/components/QuickTasks/styles.css @@ -0,0 +1,4 @@ +nav { + block-size: 100%; + display: block; +} diff --git a/packages/theme-wizard-templates/src/components/shared/card-styles.css b/packages/theme-wizard-templates/src/components/shared/card-styles.css new file mode 100644 index 000000000..e595590ab --- /dev/null +++ b/packages/theme-wizard-templates/src/components/shared/card-styles.css @@ -0,0 +1,13 @@ +/* Shared card and link styles */ +.clippy-voorbeeld__link, +.clippy-voorbeeld__card { + align-items: start !important; + background-color: var(--clippy-voorbeeld-card-bg); + block-size: 100% !important; + display: flex; + flex-direction: column; + justify-content: space-between; + padding-block: var(--clippy-voorbeeld-card-padding-block) !important; + padding-inline: var(--clippy-voorbeeld-card-padding-inline) !important; + row-gap: var(--clippy-voorbeeld-card-row-gap); +} diff --git a/packages/theme-wizard-templates/src/layouts/BaseLayout.astro b/packages/theme-wizard-templates/src/layouts/BaseLayout.astro index 8663124e8..95a887186 100644 --- a/packages/theme-wizard-templates/src/layouts/BaseLayout.astro +++ b/packages/theme-wizard-templates/src/layouts/BaseLayout.astro @@ -7,6 +7,7 @@ import '@nl-design-system-candidate/link-css/link.css'; import '@nl-design-system-candidate/heading-css/heading.css'; import '@nl-design-system-candidate/paragraph-css/paragraph.css'; import '@nl-design-system-candidate/skip-link-css/skip-link.css'; +import '../styles/base.css'; import ClientInit from '../components/ClientInit.astro'; import DevTools from '../components/DevTools.astro'; diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx index 24c0d7565..1c43ee272 100644 --- a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx +++ b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx @@ -8,7 +8,6 @@ import NewsSection from '../../sections/News'; import PageFooterSection from '../../sections/PageFooter'; import PageHeaderSection from '../../sections/PageHeader'; import SelfServiceSection from '../../sections/SelfService'; -import './styles.css'; export interface GemeenteVoorbeeldHomeProps { currentPath?: string; diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/styles.css b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/styles.css deleted file mode 100644 index a4d05c2c3..000000000 --- a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/styles.css +++ /dev/null @@ -1,66 +0,0 @@ -/* Gemeentevoorbeeld specific styles */ -* { - box-sizing: border-box; - margin-block: 0; - margin-inline: 0; -} - -.clippy-section--secondary { - background-color: var(--clippy-page-secondary-background-color); -} - -nav.clippy-top-tasks { - block-size: 100%; - display: block; -} - -.clippy-voorbeeld__link, -.clippy-voorbeeld__card { - align-items: start !important; - background-color: var(--clippy-voorbeeld-card-background-color); - block-size: 100% !important; - display: flex; - flex-direction: column; - justify-content: space-between; - row-gap: var(--clippy-voorbeeld-card-row-gap); -} - -.clippy-voorbeeld__content { - padding-block: var(--clippy-voorbeeld-card-padding-block) !important; - padding-inline: var(--clippy-voorbeeld-card-padding-inline) !important; -} - -.clippy-voorbeeld__link header { - display: flex; - flex-direction: column; - justify-content: space-between; - row-gap: var(--basis-space-row-xl); -} - -.clippy-page-header__actions { - display: flex; - gap: var(--basis-space-row-4xl); - justify-content: flex-end; -} - -.clippy-page-header__action-link { - align-items: center; - column-gap: var(--basis-space-inline-xs); - display: inline-flex; -} - -.clippy-voorbeeld-accordion__section .utrecht-accordion__button[aria-expanded="true"] { - border-end-end-radius: 0; - border-end-start-radius: 0; - border-start-end-radius: var(--utrecht-button-border-radius); - border-start-start-radius: var(--utrecht-button-border-radius); -} - -.clippy-voorbeeld-accordion__section .utrecht-accordion__panel--expanded { - background-color: var(--utrecht-accordion-button-background-color); - border-end-end-radius: var(--utrecht-button-border-radius); - border-end-start-radius: var(--utrecht-button-border-radius); - border-start-end-radius: 0; - border-start-start-radius: 0; - margin-block-end: -1px; -} diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx index 00cc2ba39..4333a0570 100644 --- a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx +++ b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx @@ -1,27 +1,34 @@ import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; +import { PageContent } from '@utrecht/component-library-react/dist/css-module'; import React, { type FC } from 'react'; import Navigation from '../../sections/Navigation'; import PageFooterSection from '../../sections/PageFooter'; import PageHeaderSection from '../../sections/PageHeader'; + export interface SearchResultsProps { currentPath?: string; } const SearchResults: FC = ({ currentPath }) => ( - <> + Skip to main content +
+
+ + + Zoekresultaten -
- -
+
+
+
- + ); export default SearchResults; diff --git a/packages/theme-wizard-templates/src/pages/search/page.astro b/packages/theme-wizard-templates/src/pages/search/page.astro index 42a9c5d53..01eb9cb9e 100644 --- a/packages/theme-wizard-templates/src/pages/search/page.astro +++ b/packages/theme-wizard-templates/src/pages/search/page.astro @@ -1,6 +1,6 @@ --- import BaseLayout from '../../layouts/BaseLayout.astro'; -import ResultsPage from './SearchResults'; +import SearchResults from './SearchResults'; export const detail = { name: 'Resultaten', @@ -11,6 +11,6 @@ export const detail = { const currentPath = Astro.url.pathname; --- - - + + diff --git a/packages/theme-wizard-templates/src/sections/PageHeader/index.tsx b/packages/theme-wizard-templates/src/sections/PageHeader/index.tsx index 10a44248b..13d898e7f 100644 --- a/packages/theme-wizard-templates/src/sections/PageHeader/index.tsx +++ b/packages/theme-wizard-templates/src/sections/PageHeader/index.tsx @@ -5,6 +5,7 @@ import { PageHeader } from '@utrecht/page-header-react'; import React, { type ReactNode, type PropsWithChildren } from 'react'; import logo from '../../../../assets/logo.svg'; import { Column, Row } from '../../components/Layout'; +import './styles.css'; export interface PageHeaderProps { actions?: Array<{ href: string; label: string; icon?: string }>; diff --git a/packages/theme-wizard-templates/src/sections/PageHeader/styles.css b/packages/theme-wizard-templates/src/sections/PageHeader/styles.css new file mode 100644 index 000000000..68f1a290e --- /dev/null +++ b/packages/theme-wizard-templates/src/sections/PageHeader/styles.css @@ -0,0 +1,11 @@ +.clippy-page-header__actions { + display: flex; + gap: var(--basis-space-row-4xl); + justify-content: flex-end; +} + +.clippy-page-header__action-link { + align-items: center; + column-gap: var(--basis-space-inline-xs); + display: inline-flex; +} diff --git a/packages/theme-wizard-templates/src/sections/SelfService/index.tsx b/packages/theme-wizard-templates/src/sections/SelfService/index.tsx index 814cb4e24..e54e80013 100644 --- a/packages/theme-wizard-templates/src/sections/SelfService/index.tsx +++ b/packages/theme-wizard-templates/src/sections/SelfService/index.tsx @@ -3,6 +3,7 @@ import { AccordionProvider, ButtonLink, PageContent } from '@utrecht/component-l import React from 'react'; import { ACCORDION_SECTIONS } from '../../components/AccordionSection'; import { Row } from '../../components/Layout'; +import './styles.css'; export interface SelfServiceSectionProps { heading?: string; diff --git a/packages/theme-wizard-templates/src/sections/SelfService/styles.css b/packages/theme-wizard-templates/src/sections/SelfService/styles.css new file mode 100644 index 000000000..429f6846c --- /dev/null +++ b/packages/theme-wizard-templates/src/sections/SelfService/styles.css @@ -0,0 +1,15 @@ +.clippy-voorbeeld-accordion__section .utrecht-accordion__button[aria-expanded="true"] { + border-end-end-radius: 0; + border-end-start-radius: 0; + border-start-end-radius: var(--utrecht-button-border-radius); + border-start-start-radius: var(--utrecht-button-border-radius); +} + +.clippy-voorbeeld-accordion__section .utrecht-accordion__panel--expanded { + background-color: var(--utrecht-accordion-button-background-color); + border-end-end-radius: var(--utrecht-button-border-radius); + border-end-start-radius: var(--utrecht-button-border-radius); + border-start-end-radius: 0; + border-start-start-radius: 0; + margin-block-end: -1px; +} diff --git a/packages/theme-wizard-templates/src/styles/base.css b/packages/theme-wizard-templates/src/styles/base.css new file mode 100644 index 000000000..732906095 --- /dev/null +++ b/packages/theme-wizard-templates/src/styles/base.css @@ -0,0 +1,5 @@ +* { + box-sizing: border-box; + margin-block: 0; + margin-inline: 0; +} From 87d9239eef4dbe6f3807eed0af7d2081a32eda54 Mon Sep 17 00:00:00 2001 From: Michelle Date: Fri, 23 Jan 2026 11:07:34 +0100 Subject: [PATCH 09/33] feat: initial version of search results page --- .../src/components/ClientInit.astro | 3 +- .../src/pages/search/SearchResults.tsx | 90 ++++++-- .../pages/search/components/SearchFilters.tsx | 129 ++++++++++++ .../pages/search/components/SearchForm.tsx | 73 +++++++ .../search/components/SearchResultItem.tsx | 29 +++ .../search/components/SearchResultsHeader.tsx | 39 ++++ .../search/components/SearchResultsList.tsx | 30 +++ .../src/pages/search/components/index.ts | 11 + .../src/pages/search/constants.ts | 117 +++++++++++ .../pages/search/hooks/useSearchFilters.ts | 23 ++ .../src/pages/search/page.astro | 2 +- .../src/pages/search/styles.css | 197 ++++++++++++++++++ .../src/pages/search/types.ts | 23 ++ 13 files changed, 746 insertions(+), 20 deletions(-) create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchFilters.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchResultItem.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchResultsList.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/components/index.ts create mode 100644 packages/theme-wizard-templates/src/pages/search/constants.ts create mode 100644 packages/theme-wizard-templates/src/pages/search/hooks/useSearchFilters.ts create mode 100644 packages/theme-wizard-templates/src/pages/search/styles.css create mode 100644 packages/theme-wizard-templates/src/pages/search/types.ts diff --git a/packages/theme-wizard-templates/src/components/ClientInit.astro b/packages/theme-wizard-templates/src/components/ClientInit.astro index eaa644f79..a9068a571 100644 --- a/packages/theme-wizard-templates/src/components/ClientInit.astro +++ b/packages/theme-wizard-templates/src/components/ClientInit.astro @@ -3,7 +3,8 @@ defineCustomElements(); }); - import '@nl-design-system-community/clippy-components/clippy-heading'; + import '@nl-design-system-community/clippy-components/clippy-button'; import '@nl-design-system-community/clippy-components/clippy-code'; + import '@nl-design-system-community/clippy-components/clippy-heading'; import '../components'; diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx index 4333a0570..7db6b9569 100644 --- a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx +++ b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx @@ -1,34 +1,88 @@ -import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; import { PageContent } from '@utrecht/component-library-react/dist/css-module'; -import React, { type FC } from 'react'; +import React, { type FC, useState, useCallback } from 'react'; +import type { SortOption } from './components/SearchResultsHeader'; +import type { SearchResultsData } from './types'; import Navigation from '../../sections/Navigation'; import PageFooterSection from '../../sections/PageFooter'; import PageHeaderSection from '../../sections/PageHeader'; - +import { SearchFiltersComponent } from './components/SearchFilters'; +import { SearchForm } from './components/SearchForm'; +import { SearchResultsHeader } from './components/SearchResultsHeader'; +import { SearchResultsList } from './components/SearchResultsList'; +import { MOCK_SEARCH_RESULTS } from './constants'; +import { useSearchFilters } from './hooks/useSearchFilters'; +import './styles.css'; export interface SearchResultsProps { currentPath?: string; + initialData?: SearchResultsData; + onSearch?: (query: string, filters: SearchResultsData['filters'], sortBy: SortOption) => void; } -const SearchResults: FC = ({ currentPath }) => ( - - Skip to main content +export const SearchResults: FC = ({ currentPath, initialData = MOCK_SEARCH_RESULTS, onSearch }) => { + const [searchQuery, setSearchQuery] = useState(initialData.query); + const [sortBy, setSortBy] = useState(initialData.sortBy); + const { filters, updateFilter } = useSearchFilters(initialData.filters); + + const handleSearch = useCallback( + (query: string) => { + setSearchQuery(query); + onSearch?.(query, filters, sortBy); + }, + [filters, sortBy, onSearch], + ); + + const handleSortChange = useCallback( + (newSort: SortOption) => { + setSortBy(newSort); + onSearch?.(searchQuery, filters, newSort); + }, + [searchQuery, filters, onSearch], + ); + + const handleFilterChange = useCallback( + (key: keyof typeof filters, value: string) => { + updateFilter(key, value); + onSearch?.(searchQuery, { ...filters, [key]: value }, sortBy); + }, + [searchQuery, filters, sortBy, updateFilter, onSearch], + ); + + return ( + + Skip to main content + + + + + +
+
+ + Zoeken - + - -
-
- +
+ - Zoekresultaten +
+ - -
-
+ +
+ +
+ + - - -); + + + ); +}; export default SearchResults; diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters.tsx new file mode 100644 index 000000000..ff546f47f --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters.tsx @@ -0,0 +1,129 @@ +import { Heading } from '@nl-design-system-candidate/heading-react/css'; +import { + AccordionProvider, + Fieldset, + FieldsetLegend, + FormField, + FormFieldCheckbox, + FormLabel, + Paragraph, + RadioButton, +} from '@utrecht/component-library-react/dist/css-module'; +import React, { type FC, useMemo } from 'react'; +import type { AccordionSection } from '../../../components/AccordionSection/types'; +import type { SearchFilters as SearchFiltersState } from '../types'; +import { SEARCH_FILTER_PERIODS, SEARCH_FILTER_MINISTRIES, SEARCH_FILTER_TYPES } from '../constants'; + +export interface SearchFiltersProps { + filters: SearchFiltersState; + onFilterChange: (key: keyof SearchFiltersState, value: string) => void; +} + +export const SearchFiltersComponent: FC = ({ filters, onFilterChange }) => { + const filterSections: AccordionSection[] = useMemo( + () => [ + { + body: ( +
+ + Selecteer periode + + +
+ {SEARCH_FILTER_PERIODS.map((period) => { + const id = `search-filter-period-${period.value}`; + const checked = filters.period === period.value; + + return ( + + + + onFilterChange('period', period.value)} + /> + {period.label} + + + + ); + })} +
+
+ ), + label: 'Periode', + }, + { + body: ( +
+ + Selecteer ministerie + + +
+ {SEARCH_FILTER_MINISTRIES.map((ministry) => ( + onFilterChange('ministry', ministry.value)} + /> + ))} +
+
+ ), + label: 'Ministerie', + }, + { + body: ( +
+ + Selecteer documenttype + + +
+ {SEARCH_FILTER_TYPES.map((type) => ( + onFilterChange('documentType', type.value)} + /> + ))} +
+
+ ), + label: 'Type', + }, + ], + [filters, onFilterChange], + ); + + return ( + + ); +}; diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx new file mode 100644 index 000000000..1030723bc --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx @@ -0,0 +1,73 @@ +import { FormLabel } from '@utrecht/component-library-react/dist/css-module'; +import React, { type FC, type FormEvent, useState } from 'react'; + +export interface SearchFormProps { + query: string; + onQueryChange: (query: string) => void; + onSubmit: (query: string) => void; + placeholder?: string; + label?: string; +} + +export const SearchForm: FC = ({ + label = 'Zoek binnen Gemeente Voorbeeld', + onQueryChange, + onSubmit, + placeholder = 'Bijvoorbeeld: afval, paspoort, verhuizing', + query, +}) => { + const [error, setError] = useState(null); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + const trimmedQuery = query.trim(); + + if (!trimmedQuery) { + setError('U moet een zoekterm invoeren voordat u zoekt.'); + return; + } + + setError(null); + onSubmit(trimmedQuery); + }; + + return ( +
+
+
+ + {label} + (verplicht veld) + + + + {error && ( + + )} + +
+ onQueryChange(e.target.value)} + placeholder={placeholder} + aria-required="true" + required + autoComplete="off" + aria-invalid={Boolean(error)} + /> + + + Zoek + +
+
+
+
+ ); +}; diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem.tsx new file mode 100644 index 000000000..1b4a26274 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem.tsx @@ -0,0 +1,29 @@ +import React, { type FC, memo } from 'react'; +import type { SearchResult } from '../types'; + +export interface SearchResultItemProps { + result: SearchResult; +} + +export const SearchResultItem: FC = memo(({ result }) => { + return ( +
+ + {result.title} + + +
+ {result.type} + {result.date && ( + + )} +
+ + {result.description} +
+ ); +}); + +SearchResultItem.displayName = 'SearchResultItem'; diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx new file mode 100644 index 000000000..931075761 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx @@ -0,0 +1,39 @@ +import React, { type FC } from 'react'; +import { SEARCH_SORT_OPTIONS } from '../constants'; + +export type SortOption = 'relevance' | 'date'; + +export interface SearchResultsHeaderProps { + onSortChange: (sort: SortOption) => void; + sortBy: SortOption; + totalResults: number; +} + +export const SearchResultsHeader: FC = ({ onSortChange, sortBy, totalResults }) => { + return ( +
+
+ {totalResults.toLocaleString('nl-NL')} zoekresultaten +
+ +
+ + + +
+
+ ); +}; diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList.tsx new file mode 100644 index 000000000..d4d1f5f39 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList.tsx @@ -0,0 +1,30 @@ +import { Paragraph } from '@utrecht/component-library-react/dist/css-module'; +import React, { type FC } from 'react'; +import type { SearchResult } from '../types'; +import { SearchResultItem } from './SearchResultItem'; + +export interface SearchResultsListProps { + results: SearchResult[]; +} + +export const SearchResultsList: FC = ({ results }) => { + if (results.length === 0) { + return ( +
+ Geen resultaten gevonden. Probeer andere zoektermen. +
+ ); + } + + return ( +
+
    + {results.map((result) => ( +
  1. + +
  2. + ))} +
+
+ ); +}; diff --git a/packages/theme-wizard-templates/src/pages/search/components/index.ts b/packages/theme-wizard-templates/src/pages/search/components/index.ts new file mode 100644 index 000000000..100662d50 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/index.ts @@ -0,0 +1,11 @@ +export { SearchForm } from './SearchForm'; +export { SearchFiltersComponent } from './SearchFilters'; +export { SearchResultsHeader } from './SearchResultsHeader'; +export { SearchResultsList } from './SearchResultsList'; +export { SearchResultItem } from './SearchResultItem'; + +export type { SearchFormProps } from './SearchForm'; +export type { SearchFiltersProps } from './SearchFilters'; +export type { SearchResultsHeaderProps, SortOption } from './SearchResultsHeader'; +export type { SearchResultsListProps } from './SearchResultsList'; +export type { SearchResultItemProps } from './SearchResultItem'; diff --git a/packages/theme-wizard-templates/src/pages/search/constants.ts b/packages/theme-wizard-templates/src/pages/search/constants.ts new file mode 100644 index 000000000..8b8813ff3 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/constants.ts @@ -0,0 +1,117 @@ +import type { SearchResultsData } from './types'; + +export const SEARCH_FILTER_PERIODS = [ + { label: 'Afgelopen 7 dagen', value: '7' }, + { label: 'Afgelopen 30 dagen', value: '30' }, + { label: 'Afgelopen 365 dagen', value: '365' }, + { label: 'Specifieke periode', value: 'custom' }, +] as const; + +export const SEARCH_FILTER_MINISTRIES = [ + { label: 'Alle ministeries', value: 'all' }, + { label: 'Ministerie van Binnenlandse Zaken en Koninkrijksrelaties', value: 'bzk' }, + { label: 'Ministerie van Volksgezondheid, Welzijn en Sport', value: 'vws' }, + { label: 'Ministerie van Economische Zaken', value: 'ez' }, + { label: 'Ministerie van Onderwijs, Cultuur en Wetenschap', value: 'ocw' }, +] as const; + +export const SEARCH_FILTER_TYPES = [ + { label: 'Alle documenten', value: 'all' }, + { label: 'Nieuwsbericht', value: 'news' }, + { label: 'Vraag en antwoord', value: 'qa' }, + { label: 'Publicatie', value: 'publication' }, + { label: 'Rapport', value: 'report' }, +] as const; + +export const SEARCH_SORT_OPTIONS = [ + { label: 'Relevantie', value: 'relevance' }, + { label: 'Datum', value: 'date' }, +] as const; + +// Mock data - replace with actual data fetching +export const MOCK_SEARCH_RESULTS: SearchResultsData = { + filters: {}, + query: 'afval', + results: [ + { + id: '1', + description: 'Wat doet de overheid om de totale hoeveelheid afval terug te dringen?', + title: 'Afval', + type: 'Onderwerp', + url: '/onderwerpen/afval', + }, + { + id: '2', + description: + 'Klein chemisch afval (kca) kunt u inleveren bij een gemeentedepot, chemokar of kca-depot. Kijk voor meer informatie over afval op ...', + title: 'Waar kan ik klein chemisch afval (kca) inleveren?', + type: 'Vraag en antwoord', + url: '/onderwerpen/afval/vraag-en-antwoord/waar-kan-ik-klein-chemisch-afval-kca-inleveren', + }, + { + id: '3', + description: + 'Alle EU-lidstaten zijn verplicht iedere 10 jaar een nationaal programma te maken voor het beheer van radioactief afval en ...', + title: 'Nationaal programma radioactief afval', + type: 'Onderwerp', + url: '/onderwerpen/afval/radioactief-afval', + }, + { + id: '4', + description: 'Welke regels gelden voor gevaarlijk afval?', + title: 'Gevaarlijk afval veilig inzamelen', + type: 'Onderwerp', + url: '/onderwerpen/afval/gevaarlijk-afval', + }, + { + id: '5', + description: 'Op weetwatjedoorspoelt.nl leest u wat er wel en niet in het riool terecht mag komen.', + title: 'Welk afval mag ik door de wc of gootsteen spoelen?', + type: 'Vraag en antwoord', + url: '/onderwerpen/afval/vraag-en-antwoord/welk-afval-mag-ik-door-de-wc-of-gootsteen-spoelen', + }, + { + id: '6', + description: + 'Goed gescheiden afval is makkelijker te recyclen dan afval dat niet wordt gescheiden. Daarom wil de overheid dat afval wordt ...', + title: 'Huishoudelijk afval scheiden en recyclen', + type: 'Onderwerp', + url: '/onderwerpen/afval/huishoudelijk-afval', + }, + { + id: '7', + description: 'Wat moet u doen met uw bedrijfsafval? Lees meer over inzameling en verwerking van bedrijfsafval.', + title: 'Afval buitenshuis scheiden en recyclen', + type: 'Onderwerp', + url: '/onderwerpen/afval/bedrijfsafval', + }, + { + id: '8', + description: 'Contactgegevens Centrale Organisatie voor Radioactief Afval (COVRA)', + title: 'Centrale Organisatie voor Radioactief Afval (COVRA)', + type: 'Contactgegevens', + url: '/contact/covra', + }, + { + id: '9', + date: '01-06-2025', + dateTime: '2025-06-01', + description: + 'Deze scenariostudie verkent mogelijkheden voor internationale samenwerking in het beheer van radioactief afval. De resultaten ...', + title: 'Scenariostudie multinationale strategie eindberging radioactief afval', + type: 'Rapport', + url: '/publicaties/scenariostudie-radioactief-afval', + }, + { + id: '10', + date: '06-11-2025', + dateTime: '2025-11-06', + description: 'Ontwikkelpad Afval en recycling - november 2025', + title: 'Ontwikkelpad Afval en recycling - november 2025', + type: 'Publicatie', + url: '/publicaties/ontwikkelpad-afval-recycling', + }, + ], + sortBy: 'relevance', + totalResults: 471, +}; diff --git a/packages/theme-wizard-templates/src/pages/search/hooks/useSearchFilters.ts b/packages/theme-wizard-templates/src/pages/search/hooks/useSearchFilters.ts new file mode 100644 index 000000000..0479e43d0 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/hooks/useSearchFilters.ts @@ -0,0 +1,23 @@ +import { useState, useCallback } from 'react'; +import type { SearchFilters } from '../types'; + +export const useSearchFilters = (initialFilters: SearchFilters = {}) => { + const [filters, setFilters] = useState(initialFilters); + + const updateFilter = useCallback((key: keyof SearchFilters, value: string) => { + setFilters((prev) => ({ + ...prev, + [key]: value, + })); + }, []); + + const resetFilters = useCallback(() => { + setFilters({}); + }, []); + + return { + filters, + resetFilters, + updateFilter, + }; +}; diff --git a/packages/theme-wizard-templates/src/pages/search/page.astro b/packages/theme-wizard-templates/src/pages/search/page.astro index 01eb9cb9e..8a46d014e 100644 --- a/packages/theme-wizard-templates/src/pages/search/page.astro +++ b/packages/theme-wizard-templates/src/pages/search/page.astro @@ -12,5 +12,5 @@ const currentPath = Astro.url.pathname; --- - + diff --git a/packages/theme-wizard-templates/src/pages/search/styles.css b/packages/theme-wizard-templates/src/pages/search/styles.css new file mode 100644 index 000000000..e4f59a720 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/styles.css @@ -0,0 +1,197 @@ +.search-form-section { + margin-block-end: var(--basis-space-row-2xl); +} + +.search-form__field { + display: flex; + flex-direction: column; +} + +.search-form__input-group { + display: flex; + gap: var(--basis-space-inline-md); + align-items: flex-start; +} + +.search-form__input-group .utrecht-textbox { + flex: 1; + min-inline-size: 0; +} + +.search-form__input:focus-visible { + outline: 3px solid var(--utrecht-color-blue-90, #003d82); + outline-offset: 2px; +} + +.search-form__error { + margin: var(--basis-space-row-xs) 0 0; + color: var(--utrecht-color-red-90, #b00020); + font-size: 0.875rem; +} + +.search-form__submit { + flex-shrink: 0; +} + +/* Search Results Layout */ +.search-results-layout { + display: grid; + grid-template-columns: 1fr 3fr; + gap: var(--basis-space-column-2xl); + margin-block-start: var(--basis-space-row-2xl); +} + +@media (max-width: 768px) { + .search-results-layout { + grid-template-columns: 1fr; + } +} + +.search-filter-options { + display: flex; + flex-direction: column; + gap: var(--basis-space-row-md); +} + +/* Align Utrecht form-field radios and checkboxes neatly to the right */ +.search-filter-options .utrecht-form-field__label--radio, +.search-filter-options .utrecht-form-field__label--checkbox { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--basis-space-inline-sm); +} + +.search-filter-options .utrecht-form-field__input { + flex-shrink: 0; +} + +/* Search Results Content */ +.search-results-content { + display: flex; + flex-direction: column; + gap: var(--basis-space-row-xl); +} + +.search-results-header { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: var(--basis-space-row-md); + inline-size: 100%; +} + +.search-results-summary h2 { + margin: 0; +} + +.search-results-sort { + display: flex; + align-items: center; + gap: var(--basis-space-inline-sm); +} + +.search-results-sort label { + margin: 0; + white-space: nowrap; +} + +/* Search Results List */ +.search-results-list { + margin-block-start: var(--basis-space-row-lg); +} + +.search-results-list__items { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: var(--basis-space-row-xl); +} + +.search-result-item { + padding-block-end: var(--basis-space-row-lg); + border-block-end: 1px solid var(--utrecht-color-grey-30, #e0e0e0); +} + +.search-result-item:last-child { + border-block-end: none; +} + +.search-result-item h3 { + margin-block-start: 0; + margin-block-end: var(--basis-space-row-sm); +} + +.search-result-meta { + display: flex; + gap: var(--basis-space-inline-md); + margin-block-end: var(--basis-space-row-sm); + font-size: 0.875rem; + color: var(--utrecht-color-grey-60, #666); +} + +.search-result-type { + font-weight: 600; +} + +.search-result-date { + color: var(--utrecht-color-grey-60, #666); +} + +/* Pagination */ +.search-pagination { + margin-block-start: var(--basis-space-row-2xl); +} + +.search-pagination__list { + display: flex; + flex-wrap: wrap; + gap: var(--basis-space-inline-sm); + list-style: none; + padding: 0; + margin: 0; + align-items: center; +} + +.search-pagination__list li { + margin: 0; +} + +.search-pagination__current { + display: inline-block; + padding: var(--basis-space-inline-sm) var(--basis-space-inline-md); + background-color: var(--utrecht-color-blue-5, #e6f2ff); + color: var(--utrecht-color-blue-90, #003d82); + font-weight: 600; + border-radius: var(--utrecht-button-border-radius, 4px); + text-decoration: none; +} + +.search-pagination__list a { + display: inline-block; + padding: var(--basis-space-inline-sm) var(--basis-space-inline-md); + text-decoration: none; + border-radius: var(--utrecht-button-border-radius, 4px); +} + +.search-pagination__list a:hover, +.search-pagination__list a:focus { + background-color: var(--utrecht-color-grey-20, #f0f0f0); + text-decoration: underline; +} + +/* Accessibility: Visually hidden but accessible to screen readers */ +.utrecht-visually-hidden { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} diff --git a/packages/theme-wizard-templates/src/pages/search/types.ts b/packages/theme-wizard-templates/src/pages/search/types.ts new file mode 100644 index 000000000..d730b1f09 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/types.ts @@ -0,0 +1,23 @@ +export interface SearchResult { + id: string; + title: string; + url: string; + description: string; + type: string; + date?: string; + dateTime?: string; +} + +export interface SearchFilters { + period?: string; + ministry?: string; + documentType?: string; +} + +export interface SearchResultsData { + query: string; + totalResults: number; + results: SearchResult[]; + sortBy: 'relevance' | 'date'; + filters: SearchFilters; +} From 5e9b7086a621e0efe1f9cb3bd92ae2138ae3382b Mon Sep 17 00:00:00 2001 From: Michelle Date: Tue, 27 Jan 2026 08:02:48 +0100 Subject: [PATCH 10/33] chore: classname --- .../theme-wizard-templates/src/components/QuickTasks/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/theme-wizard-templates/src/components/QuickTasks/styles.css b/packages/theme-wizard-templates/src/components/QuickTasks/styles.css index fa6dedbb5..988bca790 100644 --- a/packages/theme-wizard-templates/src/components/QuickTasks/styles.css +++ b/packages/theme-wizard-templates/src/components/QuickTasks/styles.css @@ -1,4 +1,4 @@ -nav { +nav.clippy-top-tasks { block-size: 100%; display: block; } From 50548af70f3ef62cb26c5dbd50c5573e6cebe328 Mon Sep 17 00:00:00 2001 From: Michelle Date: Tue, 3 Feb 2026 09:00:10 +0100 Subject: [PATCH 11/33] WIP --- .../clippy-components/custom-elements.json | 1040 +++++++++++++++++ packages/clippy-components/package.json | 2 + packages/design-tokens-schema/package.json | 2 +- packages/theme-wizard-app/package.json | 2 +- packages/theme-wizard-templates/package.json | 3 +- .../src/custom-elements.d.ts | 160 +++ packages/theme-wizard-templates/src/env.d.ts | 2 + .../src/pages/gemeentevoorbeeld/constants.ts | 6 +- .../src/pages/search/SearchResults.md | 233 ++++ .../src/pages/search/SearchResults.tsx | 43 +- .../pages/search/components/SearchFilters.tsx | 191 ++- .../pages/search/components/SearchForm.tsx | 20 +- .../search/components/SearchResultItem.tsx | 81 +- .../search/components/SearchResultsHeader.tsx | 61 +- .../search/components/SearchResultsList.tsx | 31 +- .../src/pages/search/styles.css | 238 ++-- .../src/sections/PageFooter/index.tsx | 2 +- .../src/sections/PageHeader/index.tsx | 2 +- pnpm-lock.yaml | 92 +- 19 files changed, 1916 insertions(+), 295 deletions(-) create mode 100644 packages/clippy-components/custom-elements.json create mode 100644 packages/theme-wizard-templates/src/custom-elements.d.ts create mode 100644 packages/theme-wizard-templates/src/pages/search/SearchResults.md diff --git a/packages/clippy-components/custom-elements.json b/packages/clippy-components/custom-elements.json new file mode 100644 index 000000000..42912cf16 --- /dev/null +++ b/packages/clippy-components/custom-elements.json @@ -0,0 +1,1040 @@ +{ + "schemaVersion": "2.1.0", + "modules": [ + { + "kind": "javascript-module", + "path": "src/clippy-button/index.js", + "declarations": [ + { + "name": "ClippyButton", + "superclass": { + "name": "LitElement", + "package": "lit" + }, + "members": [ + { + "name": "toggle", + "default": "undefined", + "kind": "field", + "attribute": "toggle" + }, + { + "name": "pressed", + "type": { + "text": "boolean" + }, + "default": false, + "kind": "field", + "attribute": "pressed" + }, + { + "name": "busy", + "type": { + "text": "boolean" + }, + "default": false, + "kind": "field", + "attribute": "busy" + }, + { + "name": "hint", + "type": { + "text": "'positive' | 'negative' | undefined", + "references": [ + { + "name": "Hint", + "module": "src/clippy-button/index.js" + } + ] + }, + "kind": "field", + "attribute": "hint" + }, + { + "name": "disabled", + "type": { + "text": "boolean" + }, + "default": false, + "kind": "field", + "attribute": "disabled" + }, + { + "name": "size", + "type": { + "text": "'small' | 'medium'", + "references": [ + { + "name": "Size", + "module": "src/clippy-button/index.js" + } + ] + }, + "default": "defaultSize", + "kind": "field", + "attribute": "size" + }, + { + "name": "purpose", + "type": { + "text": "'primary' | 'secondary' | 'subtle' | undefined", + "references": [ + { + "name": "Purpose", + "module": "src/clippy-button/index.js" + } + ] + }, + "kind": "field", + "attribute": "purpose" + } + ], + "kind": "class" + } + ], + "exports": [ + { + "kind": "js", + "name": "ClippyButton", + "declaration": { + "name": "ClippyButton", + "module": "src/clippy-button/index.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/clippy-button/styles.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-code/index.js", + "declarations": [ + { + "name": "ClippyCode", + "superclass": { + "name": "LitElement", + "package": "lit" + }, + "kind": "class" + } + ], + "exports": [ + { + "kind": "js", + "name": "ClippyCode", + "declaration": { + "name": "ClippyCode", + "module": "src/clippy-code/index.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/clippy-color-combobox/index.js", + "declarations": [ + { + "name": "ClippyColorCombobox", + "superclass": { + "name": "ClippyCombobox" + }, + "members": [ + { + "name": "styles", + "default": "[...ClippyCombobox.styles, colorComboboxStyles, unsafeCSS(colorSampleStyles)]", + "readonly": true, + "kind": "field", + "static": true + }, + { + "name": "translations", + "default": "messages", + "kind": "field" + }, + { + "name": "lang", + "kind": "field" + }, + { + "parameters": [ + { + "name": "lang", + "type": { + "text": "string" + } + } + ], + "name": "loadLocalizations", + "kind": "method" + }, + { + "parameters": [ + { + "name": "option", + "type": { + "text": "{\n color: Color;\n label: string;\n names: ColorName[];\n value: string;\n}", + "references": [ + { + "name": "Option", + "module": "src/clippy-color-combobox/index.js" + } + ] + } + } + ], + "name": "filter", + "kind": "method" + }, + { + "name": "options", + "type": { + "text": "Option[]" + }, + "kind": "field" + }, + { + "parameters": [ + { + "name": "value", + "type": { + "text": "Option['value']" + } + } + ], + "return": { + "type": { + "text": "string" + } + }, + "name": "valueToQuery", + "kind": "method" + }, + { + "return": { + "type": { + "text": "void" + } + }, + "name": "connectedCallback", + "kind": "method" + }, + { + "parameters": [ + { + "name": "option", + "type": { + "text": "{\n color: Color;\n label: string;\n names: ColorName[];\n value: string;\n}", + "references": [ + { + "name": "Option", + "module": "src/clippy-color-combobox/index.js" + } + ] + } + } + ], + "name": "renderEntry", + "kind": "method" + } + ], + "kind": "class" + } + ], + "exports": [ + { + "kind": "js", + "name": "ClippyColorCombobox", + "declaration": { + "name": "ClippyColorCombobox", + "module": "src/clippy-color-combobox/index.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/clippy-color-combobox/index.test.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-color-combobox/lib.js", + "declarations": [ + { + "name": "namedColors", + "type": { + "text": ": ColorLookup[]" + }, + "kind": "variable" + } + ], + "exports": [ + { + "kind": "js", + "name": "COLOR_NAMES", + "declaration": { + "name": "COLOR_NAMES", + "module": "src/clippy-color-combobox/lib.js" + } + }, + { + "kind": "js", + "name": "namedColors", + "declaration": { + "name": "namedColors", + "module": "src/clippy-color-combobox/lib.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/clippy-color-combobox/messages/en.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-color-combobox/messages/nl.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-color-combobox/styles.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-combobox/index.js", + "declarations": [ + { + "name": "ClippyCombobox", + "superclass": { + "name": "FormField" + }, + "members": [ + { + "name": "open", + "type": { + "text": "boolean" + }, + "default": false, + "kind": "field" + }, + { + "name": "position", + "type": { + "text": "'block-start' | 'block-end'", + "references": [ + { + "name": "Position", + "module": "src/clippy-combobox/index.js" + } + ] + }, + "default": "'block-end'", + "readonly": true, + "kind": "field" + }, + { + "name": "styles", + "default": "[\\n srOnly,\\n unsafeCSS(comboboxStyles),\\n unsafeCSS(listboxStyles),\\n unsafeCSS(textboxStyles),\\n ]", + "readonly": true, + "kind": "field", + "static": true + }, + { + "name": "filteredOptions", + "type": { + "text": "T[]" + }, + "readonly": true, + "kind": "field" + }, + { + "name": "options", + "type": { + "text": "T[]" + }, + "kind": "field" + }, + { + "name": "value", + "type": { + "text": "T['value'] | null" + }, + "kind": "field" + }, + { + "parameters": [ + { + "name": "type", + "type": { + "text": "'blur' | 'change' | 'focus' | 'input'" + } + } + ], + "name": "emit", + "kind": "method" + }, + { + "name": "filter", + "description": "Override this function to customize how options are filtered when typing", + "kind": "method" + }, + { + "parameters": [ + { + "name": "_query", + "type": { + "text": "string" + } + } + ], + "name": "fetchAdditionalOptions", + "description": "Override this function to customize an external data source", + "kind": "method" + }, + { + "parameters": [ + { + "name": "query", + "type": { + "text": "string" + } + } + ], + "return": { + "type": { + "text": "T['value']" + } + }, + "name": "queryToValue", + "description": "Override this function to customize how the user input is resolved to a value.\nThis runs on input.", + "kind": "method" + }, + { + "parameters": [ + { + "name": "value", + "type": { + "text": "T['value'] | null" + } + } + ], + "return": { + "type": { + "text": "string" + } + }, + "name": "valueToQuery", + "description": "Override this function to customize how a value is converted to a query.\nThis runs on setting the value.", + "kind": "method" + }, + { + "parameters": [ + { + "name": "_index", + "type": { + "text": "number" + } + } + ], + "name": "renderEntry", + "description": "Override this function to customize the rendering of combobox options and selected value.", + "kind": "method" + }, + { + "name": "connectedCallback", + "kind": "method" + }, + { + "name": "disconnectedCallback", + "kind": "method" + }, + { + "name": "render", + "kind": "method" + } + ], + "kind": "class" + } + ], + "exports": [ + { + "kind": "js", + "name": "ClippyCombobox", + "declaration": { + "name": "ClippyCombobox", + "module": "src/clippy-combobox/index.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/clippy-combobox/index.test.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-font-combobox/external.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-font-combobox/index.js", + "declarations": [ + { + "name": "ClippyFontCombobox", + "superclass": { + "name": "ClippyCombobox" + }, + "members": [ + { + "parameters": [ + { + "name": "query", + "type": { + "text": "string" + } + } + ], + "return": { + "type": { + "text": "Promise\u003cOption[]\u003e" + } + }, + "name": "fetchAdditionalOptions", + "kind": "method" + }, + { + "parameters": [ + { + "name": "value", + "type": { + "text": "Option['value']" + } + } + ], + "return": { + "type": { + "text": "string" + } + }, + "name": "valueToQuery", + "kind": "method" + }, + { + "parameters": [ + { + "name": "option", + "type": { + "text": "{\n label: string;\n value: string | Array\u003cstring\u003e;\n cssUrl?: string;\n}", + "references": [ + { + "name": "Option", + "module": "src/clippy-font-combobox/index.js" + } + ] + } + }, + { + "name": "_index", + "type": { + "text": "number" + } + } + ], + "name": "renderEntry", + "kind": "method" + } + ], + "kind": "class" + } + ], + "exports": [ + { + "kind": "js", + "name": "ClippyFontCombobox", + "declaration": { + "name": "ClippyFontCombobox", + "module": "src/clippy-font-combobox/index.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/clippy-font-combobox/index.test.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-heading/index.js", + "declarations": [ + { + "name": "ClippyHeading", + "superclass": { + "name": "LitElement", + "package": "lit" + }, + "members": [ + { + "name": "level", + "type": { + "text": "number" + }, + "default": 1, + "kind": "field", + "attribute": "level" + } + ], + "kind": "class" + } + ], + "exports": [ + { + "kind": "js", + "name": "ClippyHeading", + "declaration": { + "name": "ClippyHeading", + "module": "src/clippy-heading/index.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/clippy-heading/index.test.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-html-image/index.js", + "declarations": [ + { + "name": "ClippyHtmlImage", + "superclass": { + "name": "LitElement", + "package": "lit" + }, + "members": [ + { + "name": "labelId", + "default": "`clippy-html-image-label-${++labelCounter}`", + "readonly": true, + "kind": "field", + "privacy": "private" + }, + { + "name": "observer", + "type": { + "text": "MutationObserver" + }, + "kind": "field", + "privacy": "private" + }, + { + "name": "onLabelSlotChange", + "kind": "method", + "privacy": "private" + }, + { + "name": "updateLabelledBy", + "kind": "method", + "privacy": "private" + } + ], + "kind": "class" + } + ], + "exports": [ + { + "kind": "js", + "name": "ClippyHtmlImage", + "declaration": { + "name": "ClippyHtmlImage", + "module": "src/clippy-html-image/index.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/clippy-html-image/index.test.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-html-image/styles.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-icon/index.js", + "declarations": [ + { + "name": "ClippyIcon", + "description": "Clippy Icon Component\n\nA decorative icon wrapper component following NL Design System patterns.\nImplements WCAG 2.2 compliance by marking decorative icons as aria-hidden.", + "superclass": { + "name": "LitElement", + "package": "lit" + }, + "kind": "class" + } + ], + "exports": [ + { + "kind": "js", + "name": "ClippyIcon", + "declaration": { + "name": "ClippyIcon", + "module": "src/clippy-icon/index.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/clippy-icon/styles.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-modal/index.js", + "declarations": [ + { + "name": "DIALOG_BUTTON_VALUES", + "kind": "variable" + }, + { + "name": "ClippyModal", + "superclass": { + "name": "LitElement", + "package": "lit" + }, + "members": [ + { + "name": "ariaDescribedby", + "description": "Id of an element that describes the dialog, used for aria-describedby.\nConsumers are responsible for rendering that element in the light DOM.", + "type": { + "text": "string" + }, + "default": "''", + "kind": "field", + "attribute": "aria-describedby" + }, + { + "name": "standardOpen", + "description": "When loading the page, the dialog is not open by default.", + "type": { + "text": "boolean" + }, + "default": false, + "kind": "field", + "attribute": "standardopen" + }, + { + "name": "title", + "type": { + "text": "string" + }, + "default": "''", + "kind": "field", + "attribute": "title" + }, + { + "name": "closedBy", + "type": { + "text": "string" + }, + "default": "'any'", + "kind": "field", + "attribute": "closedby" + }, + { + "name": "actions", + "description": "Control which footer actions are rendered.\n- \"none\": no footer actions\n- \"cancel\": only cancel button\n- \"confirm\": only confirm button\n- \"both\": both confirm and cancel buttons", + "type": { + "text": "'none' | 'cancel' | 'confirm' | 'both'" + }, + "default": "'cancel'", + "kind": "field", + "attribute": "actions" + }, + { + "name": "confirmLabel", + "description": "Button labels for confirm/cancel actions. Consumers can override these,\ne.g. with localized strings.", + "type": { + "text": "string" + }, + "default": "'OK'", + "kind": "field", + "attribute": "confirmlabel" + }, + { + "name": "cancelLabel", + "type": { + "text": "string" + }, + "default": "'Cancel'", + "kind": "field", + "attribute": "cancellabel" + }, + { + "name": "titleId", + "default": "`clippy-modal-title-${++dialogInstanceCounter}`", + "readonly": true, + "kind": "field", + "privacy": "private" + }, + { + "name": "previouslyFocusedElement", + "type": { + "text": "HTMLElement | null" + }, + "default": "null", + "kind": "field", + "privacy": "private" + }, + { + "name": "open", + "kind": "method" + }, + { + "parameters": [ + { + "name": "value", + "type": { + "text": "string" + } + } + ], + "name": "close", + "kind": "method" + }, + { + "name": "onDialogClose", + "kind": "method", + "privacy": "private" + }, + { + "parameters": [ + { + "name": "event", + "type": { + "text": "Event" + } + } + ], + "name": "onDialogCancel", + "kind": "method", + "privacy": "private" + }, + { + "name": "onCloseClick", + "kind": "method", + "privacy": "private" + }, + { + "name": "returnValue", + "type": { + "text": "string" + }, + "readonly": true, + "kind": "field" + } + ], + "kind": "class" + } + ], + "exports": [ + { + "kind": "js", + "name": "DIALOG_BUTTON_VALUES", + "declaration": { + "name": "DIALOG_BUTTON_VALUES", + "module": "src/clippy-modal/index.js" + } + }, + { + "kind": "js", + "name": "ClippyModal", + "declaration": { + "name": "ClippyModal", + "module": "src/clippy-modal/index.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/clippy-modal/index.test.js" + }, + { + "kind": "javascript-module", + "path": "src/clippy-modal/styles.js", + "declarations": [ + { + "name": "dialogStyles", + "kind": "variable" + } + ], + "exports": [ + { + "kind": "js", + "name": "dialogStyles", + "declaration": { + "name": "dialogStyles", + "module": "src/clippy-modal/styles.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/lib/FormField/index.js", + "declarations": [ + { + "name": "FormField", + "superclass": { + "name": "LitElement", + "package": "lit" + }, + "members": [ + { + "name": "name", + "type": { + "text": "string" + }, + "default": "''", + "kind": "field", + "attribute": "name" + }, + { + "name": "hiddenLabel", + "type": { + "text": "string" + }, + "default": "''", + "kind": "field", + "attribute": "hidden-label" + }, + { + "name": "disabled", + "type": { + "text": "boolean" + }, + "default": false, + "kind": "field", + "attribute": "disabled" + }, + { + "name": "readonly", + "type": { + "text": "boolean" + }, + "default": false, + "kind": "field", + "attribute": "readonly" + }, + { + "name": "internals_", + "default": "this.attachInternals()", + "kind": "field" + }, + { + "name": "value", + "type": { + "text": "V | null" + }, + "kind": "field", + "attribute": "value" + }, + { + "parameters": [ + { + "name": "value", + "type": { + "text": "V | null" + } + } + ], + "return": { + "type": { + "text": "string | File | null" + } + }, + "name": "valueToFormValue", + "description": "Override this function to customize how the value is converted to a form value;", + "kind": "method" + } + ], + "kind": "class" + } + ], + "exports": [ + { + "kind": "js", + "name": "FormField", + "declaration": { + "name": "FormField", + "module": "src/lib/FormField/index.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/lib/FormField/index.test.js" + }, + { + "kind": "javascript-module", + "path": "src/lib/converters/index.js", + "declarations": [ + { + "name": "arrayFromTokenList", + "kind": "function" + } + ], + "exports": [ + { + "kind": "js", + "name": "arrayFromTokenList", + "declaration": { + "name": "arrayFromTokenList", + "module": "src/lib/converters/index.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/lib/decorators.js", + "declarations": [ + { + "parameters": [ + { + "name": "tagName", + "description": "- The custom element tag name", + "type": { + "text": "string" + } + } + ], + "return": { + "description": "A class decorator function" + }, + "name": "safeCustomElement", + "description": "Custom element decorator with safety check that prevents duplicate registrations.\n\nThis decorator provides the same functionality as Lit's @customElement but adds\na safety check to prevent \"already defined\" errors when the same element is\nregistered multiple times (e.g., in hot module reloading scenarios).", + "kind": "function" + } + ], + "exports": [ + { + "kind": "js", + "name": "safeCustomElement", + "declaration": { + "name": "safeCustomElement", + "module": "src/lib/decorators.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "src/lib/sr-only/styles.js" + } + ] +} diff --git a/packages/clippy-components/package.json b/packages/clippy-components/package.json index b074e8c5b..01fafc0ad 100644 --- a/packages/clippy-components/package.json +++ b/packages/clippy-components/package.json @@ -14,6 +14,7 @@ "files": [ "dist/" ], + "custom-elements": "custom-elements.json", "publishConfig": { "access": "public", "provenance": true @@ -58,6 +59,7 @@ "memoize": "10.2.0" }, "devDependencies": { + "@pwrs/cem": "0.9.4", "@types/dlv": "1.1.5", "@types/react": "18.3.23", "@vitest/browser-playwright": "4.0.16", diff --git a/packages/design-tokens-schema/package.json b/packages/design-tokens-schema/package.json index 350c55490..fd49102cd 100644 --- a/packages/design-tokens-schema/package.json +++ b/packages/design-tokens-schema/package.json @@ -38,7 +38,7 @@ }, "devDependencies": { "@nl-design-system-community/ma-design-tokens": "2.5.0", - "@nl-design-system-unstable/start-design-tokens": "2.4.1", + "@nl-design-system-unstable/start-design-tokens": "3.0.0", "@nl-design-system-unstable/voorbeeld-design-tokens": "7.4.0", "@types/dlv": "1.1.5", "@vitest/coverage-v8": "4.0.16", diff --git a/packages/theme-wizard-app/package.json b/packages/theme-wizard-app/package.json index 12d2c734f..a879410c6 100644 --- a/packages/theme-wizard-app/package.json +++ b/packages/theme-wizard-app/package.json @@ -57,7 +57,7 @@ "@nl-design-system-community/css-scraper": "workspace:*", "@nl-design-system-community/design-tokens-schema": "workspace:*", "@nl-design-system-community/ma-design-tokens": "3.0.0", - "@nl-design-system-unstable/start-design-tokens": "2.4.1", + "@nl-design-system-unstable/start-design-tokens": "3.0.0", "@utrecht/button-css": "3.0.1", "@utrecht/form-field-css": "2.0.1", "@utrecht/form-field-error-message-css": "2.0.1", diff --git a/packages/theme-wizard-templates/package.json b/packages/theme-wizard-templates/package.json index 476de8aaa..24a5bc307 100644 --- a/packages/theme-wizard-templates/package.json +++ b/packages/theme-wizard-templates/package.json @@ -54,7 +54,7 @@ "@nl-design-system-community/design-tokens-schema": "workspace:*", "@nl-design-system-community/ma-design-tokens": "3.0.0", "@nl-design-system-community/theme-wizard-app": "workspace:*", - "@nl-design-system-unstable/start-design-tokens": "2.4.1", + "@nl-design-system-unstable/start-design-tokens": "3.0.0", "@tabler/icons-react": "3.36.1", "@utrecht/component-library-css": "8.2.1", "@utrecht/component-library-react": "12.0.0", @@ -71,6 +71,7 @@ }, "devDependencies": { "@astrojs/check": "0.9.6", + "@pwrs/cem": "0.9.4", "@types/react": "19.2.7", "@types/react-dom": "19.2.3", "astro": "5.16.6", diff --git a/packages/theme-wizard-templates/src/custom-elements.d.ts b/packages/theme-wizard-templates/src/custom-elements.d.ts new file mode 100644 index 000000000..8dd939930 --- /dev/null +++ b/packages/theme-wizard-templates/src/custom-elements.d.ts @@ -0,0 +1,160 @@ +/** + * Custom Elements Type Definitions + * + * This file provides TypeScript definitions for custom web components used in the application. + * It extends JSX.IntrinsicElements to include both clippy-* and template-* custom elements. + */ + +declare namespace JSX { + interface IntrinsicElements { + // Clippy Components (from @nl-design-system-community/clippy-components) + 'clippy-button': React.DetailedHTMLProps< + React.HTMLAttributes & { + type?: 'button' | 'submit' | 'reset'; + disabled?: boolean; + class?: string; + 'aria-label'?: string; + }, + HTMLElement + >; + + 'clippy-heading': React.DetailedHTMLProps< + React.HTMLAttributes & { + level?: 1 | 2 | 3 | 4 | 5 | 6; + appearance?: 'level-1' | 'level-2' | 'level-3' | 'level-4' | 'level-5' | 'level-6'; + id?: string; + class?: string; + }, + HTMLElement + >; + + 'clippy-code': React.DetailedHTMLProps, HTMLElement>; + + 'clippy-color-combobox': React.DetailedHTMLProps< + React.HTMLAttributes & { + value?: string; + name?: string; + label?: string; + }, + HTMLElement + >; + + 'clippy-combobox': React.DetailedHTMLProps< + React.HTMLAttributes & { + value?: string; + name?: string; + label?: string; + }, + HTMLElement + >; + + 'clippy-font-combobox': React.DetailedHTMLProps< + React.HTMLAttributes & { + value?: string; + name?: string; + label?: string; + }, + HTMLElement + >; + + 'clippy-html-image': React.DetailedHTMLProps< + React.HTMLAttributes & { + src?: string; + alt?: string; + }, + HTMLElement + >; + + 'clippy-icon': React.DetailedHTMLProps< + React.HTMLAttributes & { + name?: string; + }, + HTMLElement + >; + + 'clippy-modal': React.DetailedHTMLProps< + React.HTMLAttributes & { + open?: boolean; + }, + HTMLElement + >; + + 'clippy-link': React.DetailedHTMLProps< + React.HTMLAttributes & { + href?: string; + target?: '_blank' | '_self' | '_parent' | '_top'; + rel?: string; + }, + HTMLElement + >; + + // Template Components (local web components) + 'template-action': React.DetailedHTMLProps< + React.HTMLAttributes & { + href?: string; + label?: string; + }, + HTMLElement + >; + + 'template-case-card': React.DetailedHTMLProps< + React.HTMLAttributes & { + title?: string; + description?: string; + href?: string; + }, + HTMLElement + >; + + 'template-color-swatch': React.DetailedHTMLProps< + React.HTMLAttributes & { + color?: string; + label?: string; + }, + HTMLElement + >; + + 'template-heading': React.DetailedHTMLProps< + React.HTMLAttributes & { + level?: 1 | 2 | 3 | 4 | 5 | 6; + }, + HTMLElement + >; + + 'template-link': React.DetailedHTMLProps< + React.HTMLAttributes & { + href?: string; + target?: '_blank' | '_self' | '_parent' | '_top'; + rel?: string; + }, + HTMLElement + >; + + 'template-link-list': React.DetailedHTMLProps< + React.HTMLAttributes & { + heading?: string; + }, + HTMLElement + >; + + 'template-page-header': React.DetailedHTMLProps, HTMLElement>; + + 'template-paragraph': React.DetailedHTMLProps, HTMLElement>; + + 'template-side-nav': React.DetailedHTMLProps< + React.HTMLAttributes & { + items?: string; + }, + HTMLElement + >; + + 'template-skip-link': React.DetailedHTMLProps< + React.HTMLAttributes & { + href?: string; + }, + HTMLElement + >; + } +} + +export {}; diff --git a/packages/theme-wizard-templates/src/env.d.ts b/packages/theme-wizard-templates/src/env.d.ts index e86330098..409fcb108 100644 --- a/packages/theme-wizard-templates/src/env.d.ts +++ b/packages/theme-wizard-templates/src/env.d.ts @@ -1,3 +1,5 @@ +/// + declare const __STANDALONE_PACKAGE__: boolean; declare module '@nl-design-system-community/theme-wizard-app'; diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/constants.ts b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/constants.ts index f8169379c..0619d5fa3 100644 --- a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/constants.ts +++ b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/constants.ts @@ -1,6 +1,6 @@ -import type { NewsItem } from './components/NewsCards/types'; -import type { QuickTask } from './components/QuickTasks/types'; -import type { NavigationItem } from './Sections/Navigation/types'; +import type { NewsItem } from '../../components/NewsCards/types'; +import type { QuickTask } from '../../components/QuickTasks/types'; +import type { NavigationItem } from '../../sections/Navigation/types'; import sewerImage from '../../assets/images/140571-klein.jpg'; import cyclistImage from '../../assets/images/56370-klein.jpg'; import parrotImage from '../../assets/images/NL-HaNA_2.24.01.03_0_905-2093-klein.jpg'; diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResults.md b/packages/theme-wizard-templates/src/pages/search/SearchResults.md new file mode 100644 index 000000000..fdbd0c39f --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/SearchResults.md @@ -0,0 +1,233 @@ +# Zoekresultaten template + +Bied bezoekers een krachtige zoekervaring met filters, sortering en duidelijke resultaatweergave. Deze zoekresultaten template helpt bezoekers snel te vinden wat ze zoeken binnen de content van je organisatie. + +Gebruik deze template als basis voor je eigen zoekfunctionaliteit, in combinatie met de huisstijl die je maakt met de Theme Wizard. De template werkt met het **Start-thema** en past zich automatisch aan jouw huisstijl aan via design tokens. + +> Meer over hoe templates en thema's samenwerken? Zie de [algemene template-documentatie](../../../docs/templates.md). + +## Opbouw van de pagina + +De zoekresultaten pagina is opgebouwd uit herbruikbare NL Design System componenten, zoals: + +- **Zoekformulier**: voor het invoeren en aanpassen van zoekopdrachten +- **Filters**: voor het verfijnen van resultaten op periode +- **Resultatenlijst**: voor het tonen van zoekresultaten met metadata +- **Sortering**: voor het ordenen van resultaten op relevantie of datum + +Zie `@nl-design-system-community/theme-wizard-templates` voor de concrete implementatie van `SearchResults` en de exacte prop-definities. + +## Gebruikte componenten + +Deze template combineert componenten uit meerdere NL Design System libraries: + +- **NL Design System (candidate)**: + - Navigatie: + - [Skip Link](https://nldesignsystem.nl/skip-link) + - Content: + - [Heading](https://nldesignsystem.nl/heading) + - [Paragraph](https://nldesignsystem.nl/paragraph) +- **Utrecht Design System**: + - Paginastructuur: + - Page Body + - Page Content + - Formulieren: + - [Fieldset](https://nldesignsystem.nl/fieldset), Fieldset Legend + - Form Field, Form Label + - [Radio Button](https://nldesignsystem.nl/radio-button) + - [Textbox](https://nldesignsystem.nl/textbox) + - Interactie: + - [Button](https://nldesignsystem.nl/button), Button Group +- **Clippy Components** (web components): + - `clippy-button`: voor zoekknop +- **Tabler Icons** (functionele iconen): + - `IconTarget`, `IconCalendar` (sorteerindicatoren) + - `IconSearch` (zoekformulier) + +## Hoe werken deze componenten samen? + +- **Layout & structuur**: + - De pagina gebruikt een twee-kolommen grid: filters in de sidebar, resultaten in de hoofdkolom + - Het zoekformulier staat bovenaan voor directe toegang + - Elk zoekresultaat is een clickable `` block met semantisch `
` wrapper + - Resultaattitels gebruiken native `

` elementen + +- **Zoeken & filteren**: + - Het zoekformulier staat bovenaan en blijft altijd zichtbaar + - Filters zijn gegroepeerd in een ` ); }; diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx index 1030723bc..ea1e7445d 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx @@ -32,17 +32,20 @@ export const SearchForm: FC = ({ }; return ( -
-
-
- +
+

+ Zoekformulier +

+ + +
+ {label} - (verplicht veld) - + (verplicht veld) {error && ( - )} @@ -57,12 +60,13 @@ export const SearchForm: FC = ({ onChange={(e) => onQueryChange(e.target.value)} placeholder={placeholder} aria-required="true" + aria-describedby={error ? 'search-error' : undefined} required autoComplete="off" aria-invalid={Boolean(error)} /> - + Zoek
diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem.tsx index 1b4a26274..2424f864e 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem.tsx +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem.tsx @@ -1,27 +1,78 @@ import React, { type FC, memo } from 'react'; +import { Mark } from '@utrecht/component-library-react/dist/css-module'; import type { SearchResult } from '../types'; export interface SearchResultItemProps { result: SearchResult; + position?: number; + totalResults?: number; + query?: string; } -export const SearchResultItem: FC = memo(({ result }) => { +/** + * Highlight search terms in text using Utrecht Mark component + */ +const highlightText = (text: string, query?: string) => { + if (!query || !query.trim()) { + return text; + } + + const terms = query.trim().split(/\s+/); + const regex = new RegExp(`(${terms.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})`, 'gi'); + const parts = text.split(regex); + + return ( + <> + {parts.map((part, index) => { + const isMatch = regex.test(part); + regex.lastIndex = 0; // Reset regex state + + return isMatch ? ( + {part} + ) : ( + {part} + ); + })} + + ); +}; + +export const SearchResultItem: FC = memo(({ position, result, totalResults, query }) => { return (
); }); diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx index 931075761..58f426d5e 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx @@ -1,3 +1,6 @@ +import { Heading } from '@nl-design-system-candidate/heading-react/css'; +import { Select, SelectOption } from '@utrecht/component-library-react/dist/css-module'; +import { IconTarget, IconCalendar } from '@tabler/icons-react'; import React, { type FC } from 'react'; import { SEARCH_SORT_OPTIONS } from '../constants'; @@ -7,33 +10,55 @@ export interface SearchResultsHeaderProps { onSortChange: (sort: SortOption) => void; sortBy: SortOption; totalResults: number; + query?: string; } -export const SearchResultsHeader: FC = ({ onSortChange, sortBy, totalResults }) => { +export const SearchResultsHeader: FC = ({ onSortChange, query, sortBy, totalResults }) => { + const resultText = totalResults === 1 ? 'zoekresultaat' : 'zoekresultaten'; + const queryText = query ? ` voor "${query}"` : ''; + return (
-
- {totalResults.toLocaleString('nl-NL')} zoekresultaten +
+ + {totalResults.toLocaleString('nl-NL')} {resultText} + {queryText} +
- + - +
+ + + {sortBy === 'relevance' ? ( + <> + +
); }; + diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList.tsx index d4d1f5f39..92c07a22c 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList.tsx +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList.tsx @@ -5,23 +5,40 @@ import { SearchResultItem } from './SearchResultItem'; export interface SearchResultsListProps { results: SearchResult[]; + query?: string; } -export const SearchResultsList: FC = ({ results }) => { +export const SearchResultsList: FC = ({ query, results }) => { + const queryText = query ? ` voor "${query}"` : ''; + if (results.length === 0) { return ( -
- Geen resultaten gevonden. Probeer andere zoektermen. +
+

+ Geen zoekresultaten{queryText} +

+ + Geen resultaten gevonden{queryText}. Probeer andere zoektermen. +
); } return ( -
-
    - {results.map((result) => ( +
    +

    + Lijst met zoekresultaten{queryText} +

    + +
      + {results.map((result, index) => (
    1. - +
    2. ))}
    diff --git a/packages/theme-wizard-templates/src/pages/search/styles.css b/packages/theme-wizard-templates/src/pages/search/styles.css index e4f59a720..774d4789a 100644 --- a/packages/theme-wizard-templates/src/pages/search/styles.css +++ b/packages/theme-wizard-templates/src/pages/search/styles.css @@ -1,12 +1,54 @@ -.search-form-section { - margin-block-end: var(--basis-space-row-2xl); +/* Mark Component Override */ +.search-result-item__link .utrecht-mark { + --utrecht-mark-background-color: #ffe44d; + --utrecht-mark-color: #000; } -.search-form__field { +/* Page Container */ +.search-page-container { display: flex; flex-direction: column; + gap: var(--basis-space-row-2xl); } +/* Grid Layout: Sidebar + Main */ +.search-results-layout { + display: grid; + grid-template-columns: minmax(250px, 1fr) minmax(0, 4fr); + gap: var(--basis-space-column-2xl); + align-items: start; +} + +@media (max-width: 768px) { + .search-results-layout { + grid-template-columns: 1fr; + } + + .search-results-sidebar { + order: 2; + } + + .search-results-main { + order: 1; + } +} + +/* Sidebar: Filters */ +.search-results-sidebar { + display: flex; + flex-direction: column; + gap: var(--basis-space-row-xl); +} + +/* Main Content Area */ +.search-results-main { + display: flex; + flex-direction: column; + gap: var(--basis-space-row-xl); + min-width: 0; +} + +/* Search Form Input Group */ .search-form__input-group { display: flex; gap: var(--basis-space-inline-md); @@ -18,33 +60,16 @@ min-inline-size: 0; } -.search-form__input:focus-visible { - outline: 3px solid var(--utrecht-color-blue-90, #003d82); - outline-offset: 2px; -} - -.search-form__error { - margin: var(--basis-space-row-xs) 0 0; - color: var(--utrecht-color-red-90, #b00020); - font-size: 0.875rem; -} - .search-form__submit { flex-shrink: 0; } -/* Search Results Layout */ -.search-results-layout { - display: grid; - grid-template-columns: 1fr 3fr; - gap: var(--basis-space-column-2xl); - margin-block-start: var(--basis-space-row-2xl); -} - -@media (max-width: 768px) { - .search-results-layout { - grid-template-columns: 1fr; - } +/* Filter Container */ +.search-filters { + background-color: var(--basis-color-default-bg-subtle); + padding: var(--basis-space-row-lg); + border-radius: var(--basis-border-radius-sm); + border: 1px solid var(--basis-color-default-border-subtle); } .search-filter-options { @@ -53,33 +78,35 @@ gap: var(--basis-space-row-md); } -/* Align Utrecht form-field radios and checkboxes neatly to the right */ -.search-filter-options .utrecht-form-field__label--radio, -.search-filter-options .utrecht-form-field__label--checkbox { +/* Date Range Picker */ +.search-filter-date-range { + margin-block-start: var(--basis-space-row-md); + padding: var(--basis-space-row-md); + background-color: var(--basis-color-accent-1-bg-default); + border-radius: var(--basis-border-radius-sm); + border: 1px solid var(--basis-color-accent-1-border-subtle); +} + +.search-filter-date-range__fields { display: flex; - align-items: center; - justify-content: space-between; - gap: var(--basis-space-inline-sm); + flex-direction: column; + gap: var(--basis-space-row-md); } -.search-filter-options .utrecht-form-field__input { - flex-shrink: 0; +.search-filter-date-range__fields .utrecht-form-field { + margin: 0; } -/* Search Results Content */ -.search-results-content { - display: flex; - flex-direction: column; - gap: var(--basis-space-row-xl); +.search-filter-date-range__fields .utrecht-textbox { + width: 100%; + max-width: 200px; } +/* Results Header */ .search-results-header { display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; + flex-direction: column; gap: var(--basis-space-row-md); - inline-size: 100%; } .search-results-summary h2 { @@ -90,18 +117,41 @@ display: flex; align-items: center; gap: var(--basis-space-inline-sm); + flex-wrap: wrap; + align-self: flex-end; } -.search-results-sort label { +.search-results-sort__label { margin: 0; white-space: nowrap; + font-weight: var(--basis-text-font-weight-bold); } -/* Search Results List */ -.search-results-list { - margin-block-start: var(--basis-space-row-lg); +.search-results-sort__control { + display: flex; + align-items: center; + gap: var(--basis-space-inline-md); + flex-wrap: wrap; +} + +.search-results-sort__indicator { + font-size: var(--basis-text-font-size-sm); + color: var(--basis-color-default-color-subtle); + font-weight: 500; + padding: var(--basis-space-inline-xs) var(--basis-space-inline-sm); + background-color: var(--basis-color-default-bg-default); + border-radius: var(--basis-border-radius-sm); + white-space: nowrap; + display: inline-flex; + align-items: center; + gap: var(--basis-space-inline-xs); +} + +.search-results-sort__indicator svg { + flex-shrink: 0; } +/* Results List */ .search-results-list__items { list-style: none; padding: 0; @@ -113,85 +163,55 @@ .search-result-item { padding-block-end: var(--basis-space-row-lg); - border-block-end: 1px solid var(--utrecht-color-grey-30, #e0e0e0); + border-block-end: 1px solid var(--basis-color-default-border-default); } .search-result-item:last-child { border-block-end: none; } -.search-result-item h3 { - margin-block-start: 0; - margin-block-end: var(--basis-space-row-sm); -} - -.search-result-meta { - display: flex; - gap: var(--basis-space-inline-md); - margin-block-end: var(--basis-space-row-sm); - font-size: 0.875rem; - color: var(--utrecht-color-grey-60, #666); -} - -.search-result-type { - font-weight: 600; +/* Clickable Result Block */ +.search-result-item__link { + display: block; + text-decoration: none; + color: inherit; + padding: var(--basis-space-row-md); + margin: calc(-1 * var(--basis-space-row-md)); + border-radius: var(--basis-border-radius-sm); + transition: background-color 0.2s ease; } -.search-result-date { - color: var(--utrecht-color-grey-60, #666); +.search-result-item__link:hover, +.search-result-item__link:focus { + background-color: var(--basis-color-default-bg-subtle); + outline: 2px solid var(--basis-color-accent-1-color-default); + outline-offset: 2px; } -/* Pagination */ -.search-pagination { - margin-block-start: var(--basis-space-row-2xl); +.search-result-item__title { + margin-block-start: 0; + margin-block-end: var(--basis-space-row-sm); + color: var(--basis-color-accent-1-color-default); + text-decoration: underline; } -.search-pagination__list { - display: flex; - flex-wrap: wrap; - gap: var(--basis-space-inline-sm); - list-style: none; - padding: 0; - margin: 0; - align-items: center; +.search-result-item__link:visited .search-result-item__title { + color: var(--basis-color-accent-1-color-active); } -.search-pagination__list li { +.search-result-item__description { margin: 0; + margin-block-end: var(--basis-space-row-sm); + color: var(--basis-color-default-color-document); } -.search-pagination__current { - display: inline-block; - padding: var(--basis-space-inline-sm) var(--basis-space-inline-md); - background-color: var(--utrecht-color-blue-5, #e6f2ff); - color: var(--utrecht-color-blue-90, #003d82); - font-weight: 600; - border-radius: var(--utrecht-button-border-radius, 4px); - text-decoration: none; -} - -.search-pagination__list a { - display: inline-block; - padding: var(--basis-space-inline-sm) var(--basis-space-inline-md); - text-decoration: none; - border-radius: var(--utrecht-button-border-radius, 4px); -} - -.search-pagination__list a:hover, -.search-pagination__list a:focus { - background-color: var(--utrecht-color-grey-20, #f0f0f0); - text-decoration: underline; +.search-result-meta { + display: flex; + gap: var(--basis-space-inline-md); + font-size: var(--basis-text-font-size-sm); + color: var(--basis-color-default-color-subtle); } -/* Accessibility: Visually hidden but accessible to screen readers */ -.utrecht-visually-hidden { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; +.search-result-type { + font-weight: var(--basis-text-font-weight-bold); } diff --git a/packages/theme-wizard-templates/src/sections/PageFooter/index.tsx b/packages/theme-wizard-templates/src/sections/PageFooter/index.tsx index 92e51a0a6..877108250 100644 --- a/packages/theme-wizard-templates/src/sections/PageFooter/index.tsx +++ b/packages/theme-wizard-templates/src/sections/PageFooter/index.tsx @@ -2,7 +2,7 @@ import { Link } from '@nl-design-system-candidate/link-react/css'; import { LinkList, Image, PageContent } from '@utrecht/component-library-react/dist/css-module'; import { PageFooter } from '@utrecht/page-footer-react'; import React, { type ReactNode, type PropsWithChildren } from 'react'; -import logo from '../../../../assets/logo.svg'; +import logo from '../../assets/logo.svg'; import { Column, Row } from '../../components/Layout'; export interface PageFooterProps { diff --git a/packages/theme-wizard-templates/src/sections/PageHeader/index.tsx b/packages/theme-wizard-templates/src/sections/PageHeader/index.tsx index 13d898e7f..ad09239fb 100644 --- a/packages/theme-wizard-templates/src/sections/PageHeader/index.tsx +++ b/packages/theme-wizard-templates/src/sections/PageHeader/index.tsx @@ -3,7 +3,7 @@ import { IconUser } from '@tabler/icons-react'; import { Icon, PageContent, Image } from '@utrecht/component-library-react/dist/css-module'; import { PageHeader } from '@utrecht/page-header-react'; import React, { type ReactNode, type PropsWithChildren } from 'react'; -import logo from '../../../../assets/logo.svg'; +import logo from '../../assets/logo.svg'; import { Column, Row } from '../../components/Layout'; import './styles.css'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b4713ee71..edefbb67d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,6 +124,9 @@ importers: specifier: 10.2.0 version: 10.2.0 devDependencies: + '@pwrs/cem': + specifier: 0.9.4 + version: 0.9.4 '@types/dlv': specifier: 1.1.5 version: 1.1.5 @@ -332,8 +335,8 @@ importers: specifier: 2.5.0 version: 2.5.0 '@nl-design-system-unstable/start-design-tokens': - specifier: 2.4.1 - version: 2.4.1 + specifier: 3.0.0 + version: 3.0.0 '@nl-design-system-unstable/voorbeeld-design-tokens': specifier: 7.4.0 version: 7.4.0 @@ -413,8 +416,8 @@ importers: specifier: 3.0.0 version: 3.0.0 '@nl-design-system-unstable/start-design-tokens': - specifier: 2.4.1 - version: 2.4.1 + specifier: 3.0.0 + version: 3.0.0 '@utrecht/button-css': specifier: 3.0.1 version: 3.0.1 @@ -610,8 +613,8 @@ importers: specifier: workspace:* version: link:../theme-wizard-app '@nl-design-system-unstable/start-design-tokens': - specifier: 2.4.1 - version: 2.4.1 + specifier: 3.0.0 + version: 3.0.0 '@tabler/icons-react': specifier: 3.36.1 version: 3.36.1(react@19.2.3) @@ -655,6 +658,9 @@ importers: '@astrojs/check': specifier: 0.9.6 version: 0.9.6(prettier@3.7.4)(typescript@5.9.3) + '@pwrs/cem': + specifier: 0.9.4 + version: 0.9.4 '@types/react': specifier: 19.2.7 version: 19.2.7 @@ -2259,8 +2265,8 @@ packages: '@nl-design-system-community/ma-design-tokens@3.0.0': resolution: {integrity: sha512-95uF6BODj/RldOGIdB0i4L8ISoJ4CauQVzZb6sgD8GBA4btEdX3Cdc6VOKncKbknads5WePeNFap7lo7YrlRGg==} - '@nl-design-system-unstable/start-design-tokens@2.4.1': - resolution: {integrity: sha512-ZNj5bRH8WHA55xxUcl/Ih77BISVES2r2616qUTMlgHx4iOGi6bEbVI2D/uJysbIPaXzK1OfvI5aXowDahpG8aQ==} + '@nl-design-system-unstable/start-design-tokens@3.0.0': + resolution: {integrity: sha512-L42dSHhdlc70PElvHpTM8RfqXaI3wXbi8tcyr+F152A9nwrtR/fAvnetOTwwrvGfrtuw5iplBjGg0x3d8rKnHQ==} '@nl-design-system-unstable/voorbeeld-design-tokens@7.4.0': resolution: {integrity: sha512-WiI8AbCM7pWiM50WRYoU9fqBx2sf0qvc8H51yi/vGyakfAnQwbkKlq1RdDLfM6UKki2BbqypY37j23GMs5Vb/w==} @@ -2326,6 +2332,47 @@ packages: '@projectwallace/css-parser@0.11.4': resolution: {integrity: sha512-NWQFups5PWGT7b9yjvKi7YLbFS49zWtFnn9DM0CS2hzyQR/9puCaDf4qtntQoMOYCveWiLpPh0aBGW2Okcfvnw==} + '@pwrs/cem-darwin-arm64@0.9.0': + resolution: {integrity: sha512-6ppWZXaft9/K3SB2CNntbIlczf0YNOr1JstbNA5vy2W4Y9wObY/aacuC5CjVWEF2eoJECs9NBYySHy3cIDtpVQ==} + engines: {node: '>=22.0.0'} + cpu: [arm64] + os: [darwin] + + '@pwrs/cem-darwin-x64@0.9.0': + resolution: {integrity: sha512-FbSF4+yS0+5VjAVTbtHpxI0Oiuy8f3AvDgl3VkqR3pWqSyd4CdcoQzrvWOYjrGtVFavt89uXM4xIPnJ/pFCxRw==} + engines: {node: '>=22.0.0'} + cpu: [x64] + os: [darwin] + + '@pwrs/cem-linux-arm64@0.9.0': + resolution: {integrity: sha512-m8ncTwBsGxkiTxrYZAYWrcY/YFeqkiT2Nmuwrv3g9Jby3XNT2TQrxJHxjJnKz17wZ2mnKHFhr7j7txAte9x9/A==} + engines: {node: '>=22.0.0'} + cpu: [arm64] + os: [linux] + + '@pwrs/cem-linux-x64@0.9.0': + resolution: {integrity: sha512-OlO8lV/FFqzOBuQn41Wenc2nbBIrSsXsXGJbUBVX63jGEGi9Vi9d+iEc+mnIrif0pKiLJWPx50MNa97/gpxXcw==} + engines: {node: '>=22.0.0'} + cpu: [x64] + os: [linux] + + '@pwrs/cem-win32-arm64@0.9.0': + resolution: {integrity: sha512-PCzPUTpC5Qp25IkaWuGd4BrfF+GTc6CA5RSvfvIOUdWDdDvQ0AfA7CcuIOE7dh4BLaqcHC4AKXFS1+VCkWqkxw==} + engines: {node: '>=22.0.0'} + cpu: [arm64] + os: [win32] + + '@pwrs/cem-win32-x64@0.9.0': + resolution: {integrity: sha512-ZcmHeD1rPMk7sLGC0MDSl+fxP9uCalRYd0+ydM9GNXoeqqZWblCd9Kpg58hAc8zbORqEEKbCAjWeUXfckZd1aQ==} + engines: {node: '>=22.0.0'} + cpu: [x64] + os: [win32] + + '@pwrs/cem@0.9.4': + resolution: {integrity: sha512-ku4QhRx8tZ5oM0kMT6spf4IQ8JItR8l5ifB3RfcxzrXoz6ElSsew9YqPHczrs1ZVFd+UViyvLSPWkDfelCDkNg==} + engines: {node: '>=22.0.0'} + hasBin: true + '@rijkshuisstijl-community/storybook-tooling@1.1.1': resolution: {integrity: sha512-6XCape5xGLrZtXTMy5J5xMUFXx1NqxlRo2TM2s+htbfROVuUcA4mQJpWN98Ttr0FiGO5k6MJp/+SapxNnw0iyg==} @@ -9752,7 +9799,7 @@ snapshots: '@nl-design-system-community/ma-design-tokens@3.0.0': {} - '@nl-design-system-unstable/start-design-tokens@2.4.1': {} + '@nl-design-system-unstable/start-design-tokens@3.0.0': {} '@nl-design-system-unstable/voorbeeld-design-tokens@7.4.0': {} @@ -9820,6 +9867,33 @@ snapshots: '@projectwallace/css-parser@0.11.4': {} + '@pwrs/cem-darwin-arm64@0.9.0': + optional: true + + '@pwrs/cem-darwin-x64@0.9.0': + optional: true + + '@pwrs/cem-linux-arm64@0.9.0': + optional: true + + '@pwrs/cem-linux-x64@0.9.0': + optional: true + + '@pwrs/cem-win32-arm64@0.9.0': + optional: true + + '@pwrs/cem-win32-x64@0.9.0': + optional: true + + '@pwrs/cem@0.9.4': + optionalDependencies: + '@pwrs/cem-darwin-arm64': 0.9.0 + '@pwrs/cem-darwin-x64': 0.9.0 + '@pwrs/cem-linux-arm64': 0.9.0 + '@pwrs/cem-linux-x64': 0.9.0 + '@pwrs/cem-win32-arm64': 0.9.0 + '@pwrs/cem-win32-x64': 0.9.0 + '@rijkshuisstijl-community/storybook-tooling@1.1.1': dependencies: remark: 15.0.1 From 1291b872ed2a01b77a99f621148b6f08810ca46d Mon Sep 17 00:00:00 2001 From: Michelle Date: Tue, 3 Feb 2026 11:14:45 +0100 Subject: [PATCH 12/33] wip --- .../src/pages/search/Search.tsx | 51 +++++++++++++++++++ .../src/pages/search/SearchResults.tsx | 6 ++- .../pages/search/components/SearchForm.tsx | 9 ++-- .../src/pages/search/search-results.astro | 16 ++++++ .../pages/search/{page.astro => search.astro} | 4 +- 5 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 packages/theme-wizard-templates/src/pages/search/Search.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/search-results.astro rename packages/theme-wizard-templates/src/pages/search/{page.astro => search.astro} (70%) diff --git a/packages/theme-wizard-templates/src/pages/search/Search.tsx b/packages/theme-wizard-templates/src/pages/search/Search.tsx new file mode 100644 index 000000000..47a36d1f6 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/Search.tsx @@ -0,0 +1,51 @@ +import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; +import { PageContent } from '@utrecht/component-library-react/dist/css-module'; +import { PageBody } from '@utrecht/page-body-react'; +import React, { useState, useCallback } from 'react'; +import Navigation from '../../sections/Navigation'; +import PageFooterSection from '../../sections/PageFooter'; +import PageHeaderSection from '../../sections/PageHeader'; +import { SearchForm } from './components/SearchForm'; +import './styles.css'; +import '@amsterdam/design-system-css/dist/visually-hidden/visually-hidden.css'; + +export interface SearchProps { + currentPath?: string; + onSearch?: (query: string) => void; +} + +export const Search = ({ currentPath, onSearch }: SearchProps): JSX.Element => { + const [searchQuery, setSearchQuery] = useState(""); + + const handleSearch = useCallback( + (query: string) => { + setSearchQuery(query); + onSearch?.(query); + }, + [onSearch], + ); + + return ( + <> + Skip to main content + + + + + + +
    + +
    + +
    +
    +
    +
    + + + + ); +}; + +export default Search; diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx index e01299f4e..98a4d37ea 100644 --- a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx +++ b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx @@ -23,7 +23,11 @@ export interface SearchResultsProps { onSearch?: (query: string, filters: SearchResultsData['filters'], sortBy: SortOption) => void; } -export const SearchResults: FC = ({ currentPath, initialData = MOCK_SEARCH_RESULTS, onSearch }) => { +export const SearchResults = ({ + currentPath, + initialData = MOCK_SEARCH_RESULTS, + onSearch, +}: SearchResultsProps): JSX.Element => { const [searchQuery, setSearchQuery] = useState(initialData.query); const [sortBy, setSortBy] = useState(initialData.sortBy); const { filters, updateFilter } = useSearchFilters(initialData.filters); diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx index ea1e7445d..a1581099e 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx @@ -13,7 +13,7 @@ export const SearchForm: FC = ({ label = 'Zoek binnen Gemeente Voorbeeld', onQueryChange, onSubmit, - placeholder = 'Bijvoorbeeld: afval, paspoort, verhuizing', + placeholder = 'Zoeken', query, }) => { const [error, setError] = useState(null); @@ -37,9 +37,9 @@ export const SearchForm: FC = ({ Zoekformulier

- +
- + {label} (verplicht veld) @@ -59,6 +59,7 @@ export const SearchForm: FC = ({ value={query} onChange={(e) => onQueryChange(e.target.value)} placeholder={placeholder} + aria-description='Search results will appear below' aria-required="true" aria-describedby={error ? 'search-error' : undefined} required @@ -66,7 +67,7 @@ export const SearchForm: FC = ({ aria-invalid={Boolean(error)} /> - + Zoek
diff --git a/packages/theme-wizard-templates/src/pages/search/search-results.astro b/packages/theme-wizard-templates/src/pages/search/search-results.astro new file mode 100644 index 000000000..0ca37e18e --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/search-results.astro @@ -0,0 +1,16 @@ +--- +import BaseLayout from '../../layouts/BaseLayout.astro'; +import SearchResultsPage from './SearchResults'; + +export const detail = { + name: 'Resultaten', + value: 'page', + module: 'Zoeken', +}; + +const currentPath = Astro.url.pathname; +--- + + + + diff --git a/packages/theme-wizard-templates/src/pages/search/page.astro b/packages/theme-wizard-templates/src/pages/search/search.astro similarity index 70% rename from packages/theme-wizard-templates/src/pages/search/page.astro rename to packages/theme-wizard-templates/src/pages/search/search.astro index 8a46d014e..c022933c1 100644 --- a/packages/theme-wizard-templates/src/pages/search/page.astro +++ b/packages/theme-wizard-templates/src/pages/search/search.astro @@ -1,6 +1,6 @@ --- import BaseLayout from '../../layouts/BaseLayout.astro'; -import SearchResults from './SearchResults'; +import SearchPage from './Search'; export const detail = { name: 'Resultaten', @@ -12,5 +12,5 @@ const currentPath = Astro.url.pathname; --- - + From b8b1b1b14ca3289079ab28e2489a23da9b5d851f Mon Sep 17 00:00:00 2001 From: Michelle Date: Tue, 3 Feb 2026 12:18:13 +0100 Subject: [PATCH 13/33] wip --- .../src/pages/search/Search.tsx | 14 +++++++- .../pages/search/components/SearchForm.tsx | 33 ++++++------------- .../src/pages/search/styles.css | 12 +++++++ 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/packages/theme-wizard-templates/src/pages/search/Search.tsx b/packages/theme-wizard-templates/src/pages/search/Search.tsx index 47a36d1f6..b4c356e45 100644 --- a/packages/theme-wizard-templates/src/pages/search/Search.tsx +++ b/packages/theme-wizard-templates/src/pages/search/Search.tsx @@ -1,5 +1,6 @@ import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; import { PageContent } from '@utrecht/component-library-react/dist/css-module'; +import { Heading } from '@nl-design-system-candidate/heading-react'; import { PageBody } from '@utrecht/page-body-react'; import React, { useState, useCallback } from 'react'; import Navigation from '../../sections/Navigation'; @@ -20,7 +21,14 @@ export const Search = ({ currentPath, onSearch }: SearchProps): JSX.Element => { const handleSearch = useCallback( (query: string) => { setSearchQuery(query); - onSearch?.(query); + if (onSearch) { + onSearch(query); + } else { + // Default behavior: navigate to results page + const searchParams = new URLSearchParams(); + searchParams.set("query", query); + window.location.href = `/search-results?${searchParams.toString()}`; + } }, [onSearch], ); @@ -37,6 +45,10 @@ export const Search = ({ currentPath, onSearch }: SearchProps): JSX.Element => {
+ + Waar bent u naar op zoek? + +
diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx index a1581099e..f40b534b9 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx @@ -1,4 +1,6 @@ import { FormLabel } from '@utrecht/component-library-react/dist/css-module'; +import { IconSearch } from '@tabler/icons-react'; +import { Icon, FormFieldTextbox } from '@utrecht/component-library-react'; import React, { type FC, type FormEvent, useState } from 'react'; export interface SearchFormProps { @@ -37,13 +39,9 @@ export const SearchForm: FC = ({ Zoekformulier - +
- - {label} - (verplicht veld) - - + {error && (
- onQueryChange(e.target.value)} - placeholder={placeholder} - aria-description='Search results will appear below' - aria-required="true" - aria-describedby={error ? 'search-error' : undefined} - required - autoComplete="off" - aria-invalid={Boolean(error)} - /> + onQueryChange(e.target.value)} aria-required id="search-query" type="search" aria-description='Search results will appear below' autoComplete='off' required aria-invalid={Boolean(error)} name="trefwoord" placeholder="Zoeken" label={label} className='clippy--visually-hidden'/> - - Zoek + + + +
+
diff --git a/packages/theme-wizard-templates/src/pages/search/styles.css b/packages/theme-wizard-templates/src/pages/search/styles.css index 774d4789a..9fdb6f221 100644 --- a/packages/theme-wizard-templates/src/pages/search/styles.css +++ b/packages/theme-wizard-templates/src/pages/search/styles.css @@ -215,3 +215,15 @@ .search-result-type { font-weight: var(--basis-text-font-weight-bold); } + + +.clippy--visually-hidden:not(:active, :focus) .utrecht-form-label { + block-size:1px; + clip-path:inset(50%); + inline-size:1px; + overflow:hidden; + position:absolute; + -webkit-user-select:none; + user-select:none; + white-space:nowrap +} \ No newline at end of file From 33ea001dca00c46a85fbc161301993d1ef17a266 Mon Sep 17 00:00:00 2001 From: Michelle Date: Tue, 3 Feb 2026 16:42:44 +0100 Subject: [PATCH 14/33] wip --- .../src/pages/search/SearchResults.tsx | 71 ++++++++++++++----- .../search/components/SearchResultsHeader.tsx | 51 ++----------- .../src/pages/search/styles.css | 59 ++++++++++----- 3 files changed, 99 insertions(+), 82 deletions(-) diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx index 98a4d37ea..64fe78fe0 100644 --- a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx +++ b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx @@ -1,4 +1,4 @@ -import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; +import { SkipLink } from '@nl-design-system-candidate/skip-link-react'; import { Heading } from '@nl-design-system-candidate/heading-react/css'; import { PageContent } from '@utrecht/component-library-react/dist/css-module'; import { PageBody } from '@utrecht/page-body-react'; @@ -14,6 +14,9 @@ import { SearchResultsHeader } from './components/SearchResultsHeader'; import { SearchResultsList } from './components/SearchResultsList'; import { MOCK_SEARCH_RESULTS } from './constants'; import { useSearchFilters } from './hooks/useSearchFilters'; +import { Select, SelectOption } from '@utrecht/component-library-react/dist/css-module'; +import { IconTarget, IconCalendar } from '@tabler/icons-react'; +import { SEARCH_SORT_OPTIONS } from './constants'; import './styles.css'; import '@amsterdam/design-system-css/dist/visually-hidden/visually-hidden.css'; @@ -23,6 +26,15 @@ export interface SearchResultsProps { onSearch?: (query: string, filters: SearchResultsData['filters'], sortBy: SortOption) => void; } +export type SortOption = 'relevance' | 'date'; + +export interface SearchResultsHeaderProps { + onSortChange: (sort: SortOption) => void; + sortBy: SortOption; + totalResults: number; + query?: string; +} + export const SearchResults = ({ currentPath, initialData = MOCK_SEARCH_RESULTS, @@ -56,6 +68,10 @@ export const SearchResults = ({ [searchQuery, filters, sortBy, updateFilter, onSearch], ); + const resultText = initialData.totalResults === 1 ? 'zoekresultaat' : 'zoekresultaten'; + const queryText = searchQuery ? ` voor "${searchQuery}"` : ''; + + return ( <> Skip to main content @@ -65,30 +81,53 @@ export const SearchResults = ({ -
+
+
+
+ + {initialData.totalResults.toLocaleString('nl-NL')} {resultText} + {queryText} + +
+ +
+ -
- +
+ +
+
+
-
- +
+ - +
+ +
-
+
-
diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx index 58f426d5e..0d5d23311 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx @@ -1,63 +1,20 @@ -import { Heading } from '@nl-design-system-candidate/heading-react/css'; -import { Select, SelectOption } from '@utrecht/component-library-react/dist/css-module'; -import { IconTarget, IconCalendar } from '@tabler/icons-react'; + import React, { type FC } from 'react'; -import { SEARCH_SORT_OPTIONS } from '../constants'; -export type SortOption = 'relevance' | 'date'; + + export interface SearchResultsHeaderProps { onSortChange: (sort: SortOption) => void; sortBy: SortOption; - totalResults: number; query?: string; } -export const SearchResultsHeader: FC = ({ onSortChange, query, sortBy, totalResults }) => { - const resultText = totalResults === 1 ? 'zoekresultaat' : 'zoekresultaten'; - const queryText = query ? ` voor "${query}"` : ''; +export const SearchResultsHeader: FC = ({ onSortChange, query, sortBy }) => { return (
-
- - {totalResults.toLocaleString('nl-NL')} {resultText} - {queryText} - -
- -
- -
- - - {sortBy === 'relevance' ? ( - <> - -
-
); }; diff --git a/packages/theme-wizard-templates/src/pages/search/styles.css b/packages/theme-wizard-templates/src/pages/search/styles.css index 9fdb6f221..8a52db5f7 100644 --- a/packages/theme-wizard-templates/src/pages/search/styles.css +++ b/packages/theme-wizard-templates/src/pages/search/styles.css @@ -11,14 +11,6 @@ gap: var(--basis-space-row-2xl); } -/* Grid Layout: Sidebar + Main */ -.search-results-layout { - display: grid; - grid-template-columns: minmax(250px, 1fr) minmax(0, 4fr); - gap: var(--basis-space-column-2xl); - align-items: start; -} - @media (max-width: 768px) { .search-results-layout { grid-template-columns: 1fr; @@ -33,19 +25,11 @@ } } -/* Sidebar: Filters */ -.search-results-sidebar { - display: flex; - flex-direction: column; - gap: var(--basis-space-row-xl); -} - /* Main Content Area */ .search-results-main { display: flex; flex-direction: column; gap: var(--basis-space-row-xl); - min-width: 0; } /* Search Form Input Group */ @@ -102,6 +86,34 @@ max-width: 200px; } +.search-results-content { + display: flex; + flex-direction: column; + gap: var(--basis-space-row-xl, 2rem); + min-width: 0; +} + +/* Grid Layout: Sidebar + Main */ +.search-results-layout { + display: flex; + flex-direction: column; + gap: var(--basis-space-column-2xl, 3rem); + align-items: start; +} + +/* Sidebar: Filters */ +.search-results-sidebar { + display: flex; + flex-direction: column; + gap: var(--basis-space-row-xl); +} + +.search-results-wrapper { + display: flex; + inline-size: 100%; + gap: var(--basis-space-row-4xl); +} + /* Results Header */ .search-results-header { display: flex; @@ -109,16 +121,25 @@ gap: var(--basis-space-row-md); } -.search-results-summary h2 { +.search-results-summary { + display: flex; + align-items: baseline; + justify-content: space-between; margin: 0; + inline-size: 100%; + gap: var(--basis-space-inline-lg, 1.5rem); } .search-results-sort { display: flex; align-items: center; gap: var(--basis-space-inline-sm); - flex-wrap: wrap; - align-self: flex-end; + flex: 0 0 auto; +} + +.search-results-heading-wrapper { + flex: 1; + min-width: 0; } .search-results-sort__label { From 2a9552b8f55db0f13a8bc62cfbc7fd5468dee1e4 Mon Sep 17 00:00:00 2001 From: Michelle Date: Tue, 3 Feb 2026 16:47:05 +0100 Subject: [PATCH 15/33] feat: Implement client-side search filtering and sorting directly within SearchResults, removing the dedicated SearchResultsHeader component. --- .../src/pages/search/SearchResults.tsx | 70 +++++++++++++++++-- .../search/components/SearchResultsHeader.tsx | 21 ------ .../src/pages/search/constants.ts | 14 ++-- 3 files changed, 72 insertions(+), 33 deletions(-) delete mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx index 64fe78fe0..8238623a1 100644 --- a/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx +++ b/packages/theme-wizard-templates/src/pages/search/SearchResults.tsx @@ -2,20 +2,17 @@ import { SkipLink } from '@nl-design-system-candidate/skip-link-react'; import { Heading } from '@nl-design-system-candidate/heading-react/css'; import { PageContent } from '@utrecht/component-library-react/dist/css-module'; import { PageBody } from '@utrecht/page-body-react'; -import React, { type FC, useState, useCallback } from 'react'; -import type { SortOption } from './components/SearchResultsHeader'; +import React, { useState, useCallback, useMemo, useEffect } from 'react'; import type { SearchResultsData } from './types'; import Navigation from '../../sections/Navigation'; import PageFooterSection from '../../sections/PageFooter'; import PageHeaderSection from '../../sections/PageHeader'; import { SearchFiltersComponent } from './components/SearchFilters'; import { SearchForm } from './components/SearchForm'; -import { SearchResultsHeader } from './components/SearchResultsHeader'; import { SearchResultsList } from './components/SearchResultsList'; import { MOCK_SEARCH_RESULTS } from './constants'; import { useSearchFilters } from './hooks/useSearchFilters'; import { Select, SelectOption } from '@utrecht/component-library-react/dist/css-module'; -import { IconTarget, IconCalendar } from '@tabler/icons-react'; import { SEARCH_SORT_OPTIONS } from './constants'; import './styles.css'; import '@amsterdam/design-system-css/dist/visually-hidden/visually-hidden.css'; @@ -44,6 +41,14 @@ export const SearchResults = ({ const [sortBy, setSortBy] = useState(initialData.sortBy); const { filters, updateFilter } = useSearchFilters(initialData.filters); + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const urlQuery = params.get('query'); + if (urlQuery) { + setSearchQuery(urlQuery); + } + }, []); + const handleSearch = useCallback( (query: string) => { setSearchQuery(query); @@ -68,7 +73,57 @@ export const SearchResults = ({ [searchQuery, filters, sortBy, updateFilter, onSearch], ); - const resultText = initialData.totalResults === 1 ? 'zoekresultaat' : 'zoekresultaten'; + const filteredResults = useMemo(() => { + let results = [...initialData.results]; + + // Filter by query + if (searchQuery) { + const q = searchQuery.toLowerCase(); + results = results.filter( + (r) => r.title.toLowerCase().includes(q) || r.description.toLowerCase().includes(q), + ); + } + + // Filter by documentType + if (filters.documentType && filters.documentType !== 'all') { + const typeMap: Record = { + qa: 'Vraag en antwoord', + publication: 'Publicatie', + report: 'Rapport', + news: 'Nieuwsbericht', + }; + const typeLabel = typeMap[filters.documentType]; + if (typeLabel) { + results = results.filter((r) => r.type === typeLabel); + } + } + + // Filter by period (dummy logic) + if (filters.period && filters.period !== 'all' && filters.period !== 'custom') { + const days = parseInt(filters.period, 10); + if (!isNaN(days)) { + const now = new Date(); + const cutoff = new Date(now.setDate(now.getDate() - days)); + results = results.filter((r) => { + if (!r.dateTime) return false; + return new Date(r.dateTime) >= cutoff; + }); + } + } + + // Sort + if (sortBy === 'date') { + results.sort((a, b) => { + if (!a.dateTime) return 1; + if (!b.dateTime) return -1; + return new Date(b.dateTime).getTime() - new Date(a.dateTime).getTime(); + }); + } + + return results; + }, [initialData.results, searchQuery, filters, sortBy]); + + const resultText = filteredResults.length === 1 ? 'zoekresultaat' : 'zoekresultaten'; const queryText = searchQuery ? ` voor "${searchQuery}"` : ''; @@ -84,11 +139,12 @@ export const SearchResults = ({
+
- {initialData.totalResults.toLocaleString('nl-NL')} {resultText} + {filteredResults.length.toLocaleString('nl-NL')} {resultText} {queryText}
@@ -122,7 +178,7 @@ export const SearchResults = ({
- +
diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx deleted file mode 100644 index 0d5d23311..000000000 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsHeader.tsx +++ /dev/null @@ -1,21 +0,0 @@ - -import React, { type FC } from 'react'; - - - - -export interface SearchResultsHeaderProps { - onSortChange: (sort: SortOption) => void; - sortBy: SortOption; - query?: string; -} - -export const SearchResultsHeader: FC = ({ onSortChange, query, sortBy }) => { - - return ( -
- -
- ); -}; - diff --git a/packages/theme-wizard-templates/src/pages/search/constants.ts b/packages/theme-wizard-templates/src/pages/search/constants.ts index 8b8813ff3..6d5773e76 100644 --- a/packages/theme-wizard-templates/src/pages/search/constants.ts +++ b/packages/theme-wizard-templates/src/pages/search/constants.ts @@ -42,6 +42,7 @@ export const MOCK_SEARCH_RESULTS: SearchResultsData = { }, { id: '2', + dateTime: '2026-01-15', description: 'Klein chemisch afval (kca) kunt u inleveren bij een gemeentedepot, chemokar of kca-depot. Kijk voor meer informatie over afval op ...', title: 'Waar kan ik klein chemisch afval (kca) inleveren?', @@ -50,10 +51,11 @@ export const MOCK_SEARCH_RESULTS: SearchResultsData = { }, { id: '3', + dateTime: '2025-10-20', description: 'Alle EU-lidstaten zijn verplicht iedere 10 jaar een nationaal programma te maken voor het beheer van radioactief afval en ...', title: 'Nationaal programma radioactief afval', - type: 'Onderwerp', + type: 'Publicatie', url: '/onderwerpen/afval/radioactief-afval', }, { @@ -65,6 +67,7 @@ export const MOCK_SEARCH_RESULTS: SearchResultsData = { }, { id: '5', + dateTime: '2026-02-01', description: 'Op weetwatjedoorspoelt.nl leest u wat er wel en niet in het riool terecht mag komen.', title: 'Welk afval mag ik door de wc of gootsteen spoelen?', type: 'Vraag en antwoord', @@ -80,10 +83,11 @@ export const MOCK_SEARCH_RESULTS: SearchResultsData = { }, { id: '7', - description: 'Wat moet u doen met uw bedrijfsafval? Lees meer over inzameling en verwerking van bedrijfsafval.', - title: 'Afval buitenshuis scheiden en recyclen', - type: 'Onderwerp', - url: '/onderwerpen/afval/bedrijfsafval', + dateTime: '2026-01-28', + description: 'Nieuwe maatregelen voor het scheiden van bedrijfsafval per 2026.', + title: 'Nieuws: Bedrijfsafval scheiden wordt verplicht', + type: 'Nieuwsbericht', + url: '/nieuws/bedrijfsafval-scheiden', }, { id: '8', From 572092cba61b05915199a2895ac9f6be72fef21c Mon Sep 17 00:00:00 2001 From: Michelle Date: Wed, 4 Feb 2026 11:39:39 +0100 Subject: [PATCH 16/33] chore: devtools --- .../src/components/DevTools.astro | 166 +++++++++++------- .../src/layouts/BaseLayout.astro | 2 +- 2 files changed, 103 insertions(+), 65 deletions(-) diff --git a/packages/theme-wizard-templates/src/components/DevTools.astro b/packages/theme-wizard-templates/src/components/DevTools.astro index b47312cb9..70d93ee0c 100644 --- a/packages/theme-wizard-templates/src/components/DevTools.astro +++ b/packages/theme-wizard-templates/src/components/DevTools.astro @@ -3,16 +3,20 @@ const isStandalone = typeof __STANDALONE_PACKAGE__ !== 'undefined' && __STANDALONE_PACKAGE__ === true; --- -{isStandalone && ( -
- Back to home - Clear localStorage - + -{isStandalone && ( - -)} + } + + // Update display on page load + updateLocalStorageDisplay(); + + // Update display when details is opened + const detailsElement = document.querySelector('.clippy-dev-tools__details') as HTMLDetailsElement | null; + if (detailsElement) { + detailsElement.addEventListener('toggle', () => { + if (detailsElement?.open) { + updateLocalStorageDisplay(); + } + }); + } + + // Clear localStorage button + const clearButton = document.getElementById('clear-localstorage'); + if (clearButton) { + clearButton.addEventListener('click', () => { + try { + localStorage.removeItem(STORAGE_KEY); + localStorage.removeItem('clippy-search-use-reload'); + window.location.reload(); + } catch (error) { + console.error('Fout bij verwijderen van localStorage:', error); + } + }); + } + + // Search reload toggle + const RELOAD_KEY = 'clippy-search-use-reload'; + const reloadToggle = document.getElementById('search-reload-toggle') as HTMLInputElement | null; + + if (reloadToggle) { + // Initialize from localStorage + const useReload = localStorage.getItem(RELOAD_KEY) === 'true'; + reloadToggle.checked = useReload; + + reloadToggle.addEventListener('change', () => { + localStorage.setItem(RELOAD_KEY, String(reloadToggle.checked)); + }); + } + diff --git a/packages/theme-wizard-templates/src/layouts/BaseLayout.astro b/packages/theme-wizard-templates/src/layouts/BaseLayout.astro index 95a887186..6f8c2989e 100644 --- a/packages/theme-wizard-templates/src/layouts/BaseLayout.astro +++ b/packages/theme-wizard-templates/src/layouts/BaseLayout.astro @@ -41,7 +41,7 @@ const { - + From f71bd3d203d8da5a536965d8dfd846dd2cced147 Mon Sep 17 00:00:00 2001 From: Michelle Date: Wed, 4 Feb 2026 16:35:21 +0100 Subject: [PATCH 17/33] refactor(search): relocate search components into feature directories and clean up old files --- .../src/components/SearchForm/index.tsx | 76 +++++ .../src/components/SearchForm/styles.css | 79 +++++ .../src/exports/react-pages.ts | 9 + .../src/pages/search/Search.tsx | 63 ---- .../src/pages/search/SearchPage/index.tsx | 54 ++++ .../src/pages/search/SearchPage/styles.css | 9 + .../src/pages/search/SearchResults.md | 233 -------------- .../src/pages/search/SearchResults.tsx | 194 ------------ .../pages/search/SearchResultsPage/index.tsx | 296 ++++++++++++++++++ .../pages/search/SearchResultsPage/styles.css | 259 +++++++++++++++ .../pages/search/SearchResultsPage/types.ts | 10 + .../pages/search/components/SearchFilters.tsx | 114 ------- .../SearchFiltersGroup/index.tsx | 73 +++++ .../SearchFiltersGroup/styles.css | 50 +++ .../SearchFilters/SearchFiltersGroup/types.ts | 17 + .../search/components/SearchFilters/index.tsx | 137 ++++++++ .../components/SearchFilters/styles.css | 60 ++++ .../search/components/SearchFilters/types.ts | 15 + .../pages/search/components/SearchForm.tsx | 65 ---- .../index.tsx} | 23 +- .../components/SearchResultItem/styles.css | 70 +++++ .../components/SearchResultItem/types.ts | 9 + .../index.tsx} | 11 +- .../components/SearchResultsList/styles.css | 11 + .../src/pages/search/components/index.ts | 6 +- .../src/pages/search/search-results.astro | 22 +- .../src/pages/search/search.astro | 8 +- .../src/pages/search/styles.css | 250 --------------- .../src/pages/search/types.ts | 23 -- .../theme-wizard-website/astro.config.mjs | 3 + packages/theme-wizard-website/package.json | 3 +- 31 files changed, 1282 insertions(+), 970 deletions(-) create mode 100644 packages/theme-wizard-templates/src/components/SearchForm/index.tsx create mode 100644 packages/theme-wizard-templates/src/components/SearchForm/styles.css delete mode 100644 packages/theme-wizard-templates/src/pages/search/Search.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/SearchPage/index.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/SearchPage/styles.css delete mode 100644 packages/theme-wizard-templates/src/pages/search/SearchResults.md delete mode 100644 packages/theme-wizard-templates/src/pages/search/SearchResults.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/SearchResultsPage/index.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/SearchResultsPage/styles.css create mode 100644 packages/theme-wizard-templates/src/pages/search/SearchResultsPage/types.ts delete mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchFilters.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/index.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/styles.css create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/types.ts create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchFilters/index.tsx create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchFilters/styles.css create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchFilters/types.ts delete mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx rename packages/theme-wizard-templates/src/pages/search/components/{SearchResultItem.tsx => SearchResultItem/index.tsx} (69%) create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/styles.css create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/types.ts rename packages/theme-wizard-templates/src/pages/search/components/{SearchResultsList.tsx => SearchResultsList/index.tsx} (79%) create mode 100644 packages/theme-wizard-templates/src/pages/search/components/SearchResultsList/styles.css delete mode 100644 packages/theme-wizard-templates/src/pages/search/styles.css delete mode 100644 packages/theme-wizard-templates/src/pages/search/types.ts diff --git a/packages/theme-wizard-templates/src/components/SearchForm/index.tsx b/packages/theme-wizard-templates/src/components/SearchForm/index.tsx new file mode 100644 index 000000000..8542aeba1 --- /dev/null +++ b/packages/theme-wizard-templates/src/components/SearchForm/index.tsx @@ -0,0 +1,76 @@ +import { IconSearch } from '@tabler/icons-react'; +import { FormFieldTextbox } from '@utrecht/component-library-react'; +import React, { type FC, type FormEvent, useState } from 'react'; +import './styles.css'; + +export interface SearchFormProps { + query: string; + onQueryChange: (query: string) => void; + onSubmit: (query: string) => void; + placeholder?: string; + label?: string; +} + +export const SearchForm: FC = ({ + label = 'Zoek binnen Gemeente Voorbeeld', + onQueryChange, + onSubmit, + query, +}) => { + const [error, setError] = useState(null); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + const trimmedQuery = query.trim(); + + if (!trimmedQuery) { + setError('U moet een zoekterm invoeren voordat u zoekt.'); + return; + } + + setError(null); + onSubmit(trimmedQuery); + }; + + return ( +
+

+ Zoekformulier +

+ +
+
+ + {error && ( + + )} + +
+ onQueryChange((e.target as HTMLInputElement).value)} + aria-required + id="search-query" + type="search" + aria-description="Search results will appear below" + autoComplete="off" + required + aria-invalid={Boolean(error)} + name="search" + placeholder="Zoeken" + label={label} + className="clippy--visually-hidden" + /> + + +
+
+
+
+
+ ); +}; diff --git a/packages/theme-wizard-templates/src/components/SearchForm/styles.css b/packages/theme-wizard-templates/src/components/SearchForm/styles.css new file mode 100644 index 000000000..ff5ab56ff --- /dev/null +++ b/packages/theme-wizard-templates/src/components/SearchForm/styles.css @@ -0,0 +1,79 @@ +/* ========================================================================== + Search Form (General) + ========================================================================== */ +.clippy--search-form__input-group { + display: flex; + min-block-size: var(--basis-pointer-target-min-block-size); +} + +.clippy--search-form__input-group .utrecht-textbox { + flex: 1; + min-inline-size: 0; + border-start-start-radius: var(--basis-border-radius-sm); + border-end-start-radius: var(--basis-border-radius-sm); + border-start-end-radius: 0; + border-end-end-radius: 0; + border-inline-end: none; + block-size: auto; +} + +.clippy--search-form__input-group .nl-button { + border-start-start-radius: 0; + border-end-start-radius: 0; + border-start-end-radius: var(--basis-border-radius-sm); + border-end-end-radius: var(--basis-border-radius-sm); + margin: 0; + border-inline-start: 1px solid var(--utrecht-textbox-border-color); + block-size: auto; +} + +.clippy--search-form__submit { + flex-shrink: 0; +} + +.clippy--search-form .nl-button--icon-only { + display: flex; + align-items: center; + justify-content: center; + padding: 0; + min-inline-size: 3rem; + border-start-start-radius: 0; + border-end-start-radius: 0; + border-start-end-radius: var(--basis-border-radius-sm); + border-end-end-radius: var(--basis-border-radius-sm); +} + +.clippy--search-form .nl-button--icon-only svg { + flex-shrink: 0; +} + +/* Responsive Design (Mobile) - Contextual styles when inside navigation */ +@media (max-width: 768px) { + /* Search Wrapper (Header) */ + .clippy--navigation-search-wrapper .clippy--search-form-section { + margin-block: var(--basis-space-inline-xs); + inline-size: 100%; + } + + .clippy--navigation-search-wrapper .clippy--search-form { + inline-size: 100%; + } + + .clippy--navigation-search-wrapper .clippy--search-form__field { + inline-size: 100%; + } + + .clippy--navigation-search-wrapper .clippy--search-form__field search { + display: block; + inline-size: 100%; + } + + .clippy--navigation-search-wrapper .clippy--search-form__input-group { + inline-size: 100%; + } + + .clippy--navigation-search-wrapper .clippy--search-form__input-group .utrecht-form-field { + min-inline-size: 0; + flex: 1; + } +} diff --git a/packages/theme-wizard-templates/src/exports/react-pages.ts b/packages/theme-wizard-templates/src/exports/react-pages.ts index 132dd5ef2..c4ba9d9cd 100644 --- a/packages/theme-wizard-templates/src/exports/react-pages.ts +++ b/packages/theme-wizard-templates/src/exports/react-pages.ts @@ -1,2 +1,11 @@ export { default as GemeenteVoorbeeldHome } from '../pages/gemeentevoorbeeld/GemeenteVoorbeeldHome'; export type { GemeenteVoorbeeldHomeProps } from '../pages/gemeentevoorbeeld/GemeenteVoorbeeldHome'; + +export { default as Search } from '../pages/search/SearchPage'; +export type { SearchProps } from '../pages/search/SearchPage'; + +export { default as SearchResults } from '../pages/search/SearchResultsPage'; +export type { SearchResultsProps } from '../pages/search/SearchResultsPage'; + +export { default as TemplateLayout } from '../layouts/TemplateLayout'; +export type { TemplateLayoutProps } from '../layouts/TemplateLayout'; diff --git a/packages/theme-wizard-templates/src/pages/search/Search.tsx b/packages/theme-wizard-templates/src/pages/search/Search.tsx deleted file mode 100644 index b4c356e45..000000000 --- a/packages/theme-wizard-templates/src/pages/search/Search.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; -import { PageContent } from '@utrecht/component-library-react/dist/css-module'; -import { Heading } from '@nl-design-system-candidate/heading-react'; -import { PageBody } from '@utrecht/page-body-react'; -import React, { useState, useCallback } from 'react'; -import Navigation from '../../sections/Navigation'; -import PageFooterSection from '../../sections/PageFooter'; -import PageHeaderSection from '../../sections/PageHeader'; -import { SearchForm } from './components/SearchForm'; -import './styles.css'; -import '@amsterdam/design-system-css/dist/visually-hidden/visually-hidden.css'; - -export interface SearchProps { - currentPath?: string; - onSearch?: (query: string) => void; -} - -export const Search = ({ currentPath, onSearch }: SearchProps): JSX.Element => { - const [searchQuery, setSearchQuery] = useState(""); - - const handleSearch = useCallback( - (query: string) => { - setSearchQuery(query); - if (onSearch) { - onSearch(query); - } else { - // Default behavior: navigate to results page - const searchParams = new URLSearchParams(); - searchParams.set("query", query); - window.location.href = `/search-results?${searchParams.toString()}`; - } - }, - [onSearch], - ); - - return ( - <> - Skip to main content - - - - - - -
- -
- - Waar bent u naar op zoek? - - - -
-
-
-
- - - - ); -}; - -export default Search; diff --git a/packages/theme-wizard-templates/src/pages/search/SearchPage/index.tsx b/packages/theme-wizard-templates/src/pages/search/SearchPage/index.tsx new file mode 100644 index 000000000..21172e6ea --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/SearchPage/index.tsx @@ -0,0 +1,54 @@ +import { PageContent } from '@utrecht/component-library-react/dist/css-module'; +import { Heading } from '@nl-design-system-candidate/heading-react'; +import React, { useState, useCallback } from 'react'; +import TemplateLayout from '../../../layouts/TemplateLayout'; +import { SearchForm } from '../../../components/SearchForm'; +import './styles.css'; +import '@amsterdam/design-system-css/dist/visually-hidden/visually-hidden.css'; + +export interface SearchProps { + currentPath?: string; + onSearch?: (query: string) => void; +} + +export const Search = ({ currentPath, onSearch }: SearchProps): React.ReactElement => { + const [searchQuery, setSearchQuery] = useState(""); + + const handleSearch = useCallback( + (query: string) => { + setSearchQuery(query); + if (onSearch) { + onSearch(query); + } else { + // Default behavior: navigate to results page + const searchParams = new URLSearchParams(); + searchParams.set("search", query); + window.location.href = `search-results?${searchParams.toString()}`; + } + }, + [onSearch], + ); + + return ( + +
+ +
+ + Waar bent u naar op zoek? + + + +
+
+
+
+ ); +}; + +export default Search; diff --git a/packages/theme-wizard-templates/src/pages/search/SearchPage/styles.css b/packages/theme-wizard-templates/src/pages/search/SearchPage/styles.css new file mode 100644 index 000000000..1a1c71de3 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/SearchPage/styles.css @@ -0,0 +1,9 @@ +/* ========================================================================== + Search Page Layout + ========================================================================== */ +.clippy--search-page-container { + display: flex; + flex-direction: column; + gap: var(--basis-space-row-2xl); + margin-block-start: var(--basis-space-row-xl); +} diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResults.md b/packages/theme-wizard-templates/src/pages/search/SearchResults.md deleted file mode 100644 index fdbd0c39f..000000000 --- a/packages/theme-wizard-templates/src/pages/search/SearchResults.md +++ /dev/null @@ -1,233 +0,0 @@ -# Zoekresultaten template - -Bied bezoekers een krachtige zoekervaring met filters, sortering en duidelijke resultaatweergave. Deze zoekresultaten template helpt bezoekers snel te vinden wat ze zoeken binnen de content van je organisatie. - -Gebruik deze template als basis voor je eigen zoekfunctionaliteit, in combinatie met de huisstijl die je maakt met de Theme Wizard. De template werkt met het **Start-thema** en past zich automatisch aan jouw huisstijl aan via design tokens. - -> Meer over hoe templates en thema's samenwerken? Zie de [algemene template-documentatie](../../../docs/templates.md). - -## Opbouw van de pagina - -De zoekresultaten pagina is opgebouwd uit herbruikbare NL Design System componenten, zoals: - -- **Zoekformulier**: voor het invoeren en aanpassen van zoekopdrachten -- **Filters**: voor het verfijnen van resultaten op periode -- **Resultatenlijst**: voor het tonen van zoekresultaten met metadata -- **Sortering**: voor het ordenen van resultaten op relevantie of datum - -Zie `@nl-design-system-community/theme-wizard-templates` voor de concrete implementatie van `SearchResults` en de exacte prop-definities. - -## Gebruikte componenten - -Deze template combineert componenten uit meerdere NL Design System libraries: - -- **NL Design System (candidate)**: - - Navigatie: - - [Skip Link](https://nldesignsystem.nl/skip-link) - - Content: - - [Heading](https://nldesignsystem.nl/heading) - - [Paragraph](https://nldesignsystem.nl/paragraph) -- **Utrecht Design System**: - - Paginastructuur: - - Page Body - - Page Content - - Formulieren: - - [Fieldset](https://nldesignsystem.nl/fieldset), Fieldset Legend - - Form Field, Form Label - - [Radio Button](https://nldesignsystem.nl/radio-button) - - [Textbox](https://nldesignsystem.nl/textbox) - - Interactie: - - [Button](https://nldesignsystem.nl/button), Button Group -- **Clippy Components** (web components): - - `clippy-button`: voor zoekknop -- **Tabler Icons** (functionele iconen): - - `IconTarget`, `IconCalendar` (sorteerindicatoren) - - `IconSearch` (zoekformulier) - -## Hoe werken deze componenten samen? - -- **Layout & structuur**: - - De pagina gebruikt een twee-kolommen grid: filters in de sidebar, resultaten in de hoofdkolom - - Het zoekformulier staat bovenaan voor directe toegang - - Elk zoekresultaat is een clickable `` block met semantisch `
` wrapper - - Resultaattitels gebruiken native `

` elementen - -- **Zoeken & filteren**: - - Het zoekformulier staat bovenaan en blijft altijd zichtbaar - - Filters zijn gegroepeerd in een `

+ } + searchQuery={searchQuery} + onSearchQueryChange={setSearchQuery} + onSearchSubmit={handleSearch} + > +
+ +
+ {breadcrumbs} +
+ +
+
+
+ + {summaryHeading} + +
+ +
+ {Object.values(filters).some(v => v && v !== 'all') && ( + + )} + + + +
+ +
+
+
+ +
+ + +
+ +
+
+
+
+
+ + ); +}; + +export default SearchResults; diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResultsPage/styles.css b/packages/theme-wizard-templates/src/pages/search/SearchResultsPage/styles.css new file mode 100644 index 000000000..f02f0d648 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/SearchResultsPage/styles.css @@ -0,0 +1,259 @@ +/* ========================================================================== + Utilities & Component Overrides + ========================================================================== */ +.clippy--visually-hidden .utrecht-form-label { + block-size: 1px; + clip-path: inset(50%); + inline-size: 1px; + overflow: hidden; + position: absolute; + user-select: none; + white-space: nowrap; +} + +/* ========================================================================== + Page Layout + ========================================================================== */ +.clippy--search-page-container { + display: flex; + flex-direction: column; + gap: var(--basis-space-row-2xl); + margin-block-start: var(--basis-space-row-xl); +} + +.clippy--search-results-layout { + display: flex; + flex-direction: column; + gap: var(--basis-space-column-2xl); + align-items: stretch; +} + +.clippy--search-results-wrapper { + display: flex; + inline-size: 100%; + gap: 6rem; + align-items: flex-start; +} + +.clippy--search-results-content { + padding-block-start: var(--basis-space-block-lg); + display: flex; + flex-direction: column; + gap: var(--basis-space-row-xl); + flex: 1; + min-inline-size: 0; +} + +/* ========================================================================== + Navigation & Breadcrumbs + ========================================================================== */ +.clippy--navigation-content-wrapper { + display: flex; + align-items: center; + justify-content: space-between; + inline-size: 100%; +} + +.clippy--navigation-search-wrapper { + margin-inline-start: var(--basis-space-inline-xl); +} + +.clippy--navigation-search-wrapper .clippy--search-form-section { + margin-block: var(--basis-space-inline-xl); +} + +.clippy--navigation-search-wrapper .clippy--search-form__field { + margin: 0; +} + +.clippy--navigation-search-wrapper .clippy--search-form__input-group .utrecht-textbox { + min-inline-size: 25ch; +} + +.clippy--navigation-hamburger-button { + display: none; +} + +/* Breadcrumbs */ +.clippy--navigation-breadcrumb-wrapper--desktop { + display: block; + margin-block-end: var(--basis-space-row-sm); + margin-block-start: var(--basis-space-row-md); +} + +.clippy--navigation-breadcrumb-wrapper { + display: none; +} + +.utrecht-breadcrumb-nav__list { + display: flex; + flex-wrap: wrap; + list-style: none; + padding: 0; + margin: 0; + font-size: var(--basis-font-size-sm); + align-items: center; +} + +.utrecht-breadcrumb-nav__link { + color: var(--basis-color-action-link-color-default) !important; + text-decoration: none; +} + +.utrecht-breadcrumb-nav__link:hover { + text-decoration: underline; +} + +.clippy--breadcrumb-separator { + color: var(--basis-color-default-color-subtle); + margin-inline: var(--basis-space-inline-xs); + display: flex !important; + align-items: center; + justify-content: center; +} + +.utrecht-breadcrumb-nav__link[aria-current="page"] { + color: var(--basis-color-default-color-document) !important; + font-weight: var(--basis-font-weight-bold); + text-decoration: none; +} + +/* ========================================================================== + Search Results Area (Header & Layout) + ========================================================================== */ +/* Header & Controls */ +.clippy--search-results-header { + display: flex; + flex-direction: column; + gap: var(--basis-space-row-md); +} + +.clippy--search-results-summary { + display: flex; + align-items: baseline; + justify-content: space-between; + margin: 0; + inline-size: 100%; + gap: var(--basis-space-inline-lg); + margin-block-end: var(--basis-space-block-2xl); +} + +.clippy--search-results-heading-wrapper { + flex: 1; + min-inline-size: 0; +} + +.clippy--search-results-sort { + display: flex; + align-items: center; + gap: var(--basis-space-inline-md); + flex: 0 0 auto; +} + +.clippy--search-results-reset-button { + margin-inline-end: var(--basis-space-inline-md); +} + +.clippy--search-results-sort__label { + margin: 0; + white-space: nowrap; + font-weight: var(--basis-text-font-weight-bold); +} + +.clippy--search-results-sort__control { + display: flex; + align-items: center; + gap: var(--basis-space-inline-md); + flex-wrap: wrap; +} + +/* ========================================================================== + Responsive Design (Mobile) + ========================================================================== */ +@media (max-width: 768px) { + /* Navigation & Header */ + .clippy--navigation-content-wrapper { + flex-wrap: wrap; + gap: var(--basis-space-row-sm); + padding: var(--basis-space-row-sm); + } + + .clippy--navigation-hamburger-button { + display: flex !important; + align-items: center; + justify-content: center; + background-color: var(--basis-color-default-bg-subtle); + border: 1px solid var(--basis-color-default-border-subtle); + border-radius: var(--basis-border-radius-sm); + padding-block: var(--basis-space-inline-xs); + padding-inline: var(--basis-space-inline-sm); + cursor: pointer; + color: var(--basis-color-default-color-document); + z-index: 10; + order: 2; + flex: initial; + gap: var(--basis-space-inline-xs); + font-weight: var(--basis-font-weight-bold); + min-block-size: var(--basis-pointer-target-min-block-size); + } + + .clippy--navigation-hamburger-icon { + display: flex; + align-items: center; + justify-content: center; + line-height: 0; + } + + .clippy--navigation-hamburger-label { + font-size: var(--basis-font-size-sm); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + /* Breadcrumbs */ + .clippy--navigation-breadcrumb-wrapper--desktop { + display: none; + } + + .clippy--navigation-breadcrumb-wrapper { + display: block; + flex: 1; + overflow: hidden; + order: 1; + } + + .clippy--navigation-breadcrumb-wrapper .utrecht-breadcrumb-nav { + margin: 0; + } + + /* Navigation Menu (Dropdown) */ + .clippy--navigation-menu { + display: none; + inline-size: 100%; + background-color: var(--basis-color-default-bg-default); + border-block-start: 1px solid var(--basis-color-default-border-subtle); + order: 4; + } + + .clippy--navigation-menu.is-open { + display: block; + } + + .clippy--navigation-menu .utrecht-nav-list { + flex-direction: column; + padding: var(--basis-space-row-md); + margin: 0; + } + + /* Search Wrapper (Header) - Navigation specific layout only */ + .clippy--navigation-search-wrapper { + inline-size: 100%; + margin: 0; + order: 3; + } + + /* Layout Adjustments */ + .clippy--search-results-wrapper { + flex-direction: column; + } +} diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResultsPage/types.ts b/packages/theme-wizard-templates/src/pages/search/SearchResultsPage/types.ts new file mode 100644 index 000000000..9071e8472 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/SearchResultsPage/types.ts @@ -0,0 +1,10 @@ +import type { SearchFilters } from '../components/SearchFilters/types'; +import type { SearchResult } from '../components/SearchResultItem/types'; + +export interface SearchResultsData { + query: string; + totalResults: number; + results: SearchResult[]; + sortBy: 'relevance' | 'date'; + filters: SearchFilters; +} diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters.tsx deleted file mode 100644 index a53722c75..000000000 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { Heading } from '@nl-design-system-candidate/heading-react/css'; -import { - Fieldset, - FieldsetLegend, - FormField, - FormLabel, - Paragraph, - RadioButton, - Textbox, -} from '@utrecht/component-library-react/dist/css-module'; -import React, { type FC, useState } from 'react'; -import type { SearchFilters as SearchFiltersState } from '../types'; -import { SEARCH_FILTER_PERIODS } from '../constants'; - -export interface SearchFiltersProps { - filters: SearchFiltersState; - onFilterChange: (key: keyof SearchFiltersState, value: string) => void; -} - -export const SearchFiltersComponent: FC = ({ filters, onFilterChange }) => { - const [dateFrom, setDateFrom] = useState(''); - const [dateTo, setDateTo] = useState(''); - const isCustomPeriod = filters.period === 'custom'; - - const handleDateChange = () => { - if (dateFrom && dateTo) { - // Format: "YYYY-MM-DD to YYYY-MM-DD" - onFilterChange('period', `${dateFrom} to ${dateTo}`); - } - }; - - return ( - - ); -}; diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/index.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/index.tsx new file mode 100644 index 000000000..531dcc2ae --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/index.tsx @@ -0,0 +1,73 @@ +import { Heading } from '@nl-design-system-candidate/heading-react/css'; +import { + Fieldset, + FieldsetLegend, + FormField, + FormLabel, + Paragraph, + RadioButton, +} from '@utrecht/component-library-react/dist/css-module'; +import { NumberBadge } from '@nl-design-system-candidate/number-badge-react/css'; +import type { FC } from 'react'; +import type { SearchFilterGroupProps } from './types'; +import './styles.css'; + +export const SearchFilterGroup: FC = ({ + title, + id, + name, + options, + selectedValue, + onValueChange, + children, +}) => ( +
+
+ + {title} + +
+ +
+ + Selecteer {title.toLowerCase()} + + +
    + {options.map((option) => { + const optionId = `${id}-${option.value}`; + const checked = selectedValue === option.value; + + return ( + +
  • + onValueChange(option.value)} + /> + + + {option.label} + {option.count !== undefined && ( + <> + + , bevat {option.count} resultaten + + )} + + +
  • +
    + ); + })} +
+ {children} +
+
+); diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/styles.css b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/styles.css new file mode 100644 index 000000000..eb9effe7c --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/styles.css @@ -0,0 +1,50 @@ +/* ========================================================================== + Search Filter Group Component + ========================================================================== */ + +.clippy--search-filter-heading { + margin-block-end: var(--basis-space-block-xl); + display: flex; + align-items: baseline; + justify-content: space-between; + gap: var(--basis-space-inline-md); + border-block-end: 1px solid var(--basis-color-accent-1-border-subtle) +} + +.clippy--search-filter-heading h3 { + padding-block-start: var(--basis-space-block-lg); +} + +.clippy--search-filter-options { + padding: 0; + list-style-type: none; + display: flex; + flex-direction: column; + gap: var(--basis-space-row-md); +} + +.clippy--search-filter-list-item { + display: flex; + align-items: center; +} + +.clippy--search-filter-label { + flex-grow: 1; + display: flex; + min-inline-size: 0; + justify-content: space-between; +} + +.clippy--radio-button { + display: flex; + flex-shrink: 0; + order: 0 !important; +} + +.clippy--search-filter-item-text { + flex-shrink: 1; +} + +.clippy--search-filter-item-count { + flex-shrink: 0; +} \ No newline at end of file diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/types.ts b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/types.ts new file mode 100644 index 000000000..8d2829490 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/types.ts @@ -0,0 +1,17 @@ +import type { ReactNode } from 'react'; + +export interface FilterOption { + label: string; + value: string; + count?: number; +} + +export interface SearchFilterGroupProps { + title: string; + id: string; + name: string; + options: readonly FilterOption[]; + selectedValue: string; + onValueChange: (value: string) => void; + children?: ReactNode; +} diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/index.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/index.tsx new file mode 100644 index 000000000..6dc628700 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/index.tsx @@ -0,0 +1,137 @@ +import { + FormField, + FormLabel, + Paragraph, + Textbox, +} from '@utrecht/component-library-react/dist/css-module'; +import React, { type FC, useState } from 'react'; +import type { + SearchFilters as SearchFiltersState, + SearchFiltersProps, +} from './types'; +import { SearchFilterGroup } from './SearchFiltersGroup'; +import { SEARCH_FILTER_PERIODS, SEARCH_FILTER_TYPES, SEARCH_FILTER_ORGANIZATIONS } from '../../constants'; +import './styles.css'; + +export const SearchFiltersComponent: FC = ({ + filters, + onFilterChange, + currentQuery = '', + currentSort = 'relevance', +}) => { + const [dateFrom, setDateFrom] = useState(''); + const [dateTo, setDateTo] = useState(''); + const isCustomPeriod = filters.period === 'custom'; + + // Clear local date states when filters are reset or period changes + React.useEffect(() => { + if (filters.period !== 'custom' && !filters.period?.includes('to')) { + setDateFrom(''); + setDateTo(''); + } + }, [filters.period]); + + const handleDateChange = () => { + if (dateFrom && dateTo) { + // Format: "YYYY-MM-DD to YYYY-MM-DD" + onFilterChange('period', `${dateFrom} to ${dateTo}`); + } + }; + + return ( + + ); +}; diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/styles.css b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/styles.css new file mode 100644 index 000000000..473b603d3 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/styles.css @@ -0,0 +1,60 @@ +/* ========================================================================== + Filter Sidebar + ========================================================================== */ +.clippy--search-filters { + inline-size: 25%; + padding-block-end: var(--basis-space-block-xl); + border-radius: var(--basis-border-radius-sm); +} + +.clippy--search-filters-form { + display: flex; + flex-direction: column; + row-gap: var(--basis-space-row-4xl); +} + +.clippy--search-filter-date-range { + margin-block-start: var(--basis-space-row-md); + padding: var(--basis-space-row-md); + background-color: var(--basis-color-accent-1-bg-default); + border-radius: var(--basis-border-radius-sm); + border: 1px solid var(--basis-color-accent-1-border-subtle); +} + +.clippy--search-filter-date-range__fields { + display: flex; + flex-direction: column; + gap: var(--basis-space-row-md); +} + +.clippy--search-filter-date-range__fields .utrecht-form-field { + margin: 0; +} + +.clippy--search-filter-date-range__fields .utrecht-textbox { + inline-size: 100%; + max-inline-size: 12.5rem; +} + +/* Progressive Enhancement */ +.clippy--filter-submit-wrapper { + margin-block-start: var(--basis-space-row-lg); + padding-block-start: var(--basis-space-row-md); +} + +.clippy--pe-only { + display: block; +} + +/* Hide PE-only elements when JavaScript is enabled */ +.js-enabled .clippy--pe-only { + display: none !important; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .clippy--search-filters { + flex: initial; + inline-size: 100%; + } +} diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/types.ts b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/types.ts new file mode 100644 index 000000000..c58df30ed --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/types.ts @@ -0,0 +1,15 @@ +import type { ReactNode } from 'react'; + +export interface SearchFilters { + period?: string; + organization?: string; + documentType?: string; +} + +export interface SearchFiltersProps { + filters: SearchFilters; + onFilterChange: (key: keyof SearchFilters, value: string) => void; + // --- PROGRESSIVE ENHANCEMENT PROPS --- + currentQuery?: string; + currentSort?: string; +} diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx deleted file mode 100644 index f40b534b9..000000000 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchForm.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { FormLabel } from '@utrecht/component-library-react/dist/css-module'; -import { IconSearch } from '@tabler/icons-react'; -import { Icon, FormFieldTextbox } from '@utrecht/component-library-react'; -import React, { type FC, type FormEvent, useState } from 'react'; - -export interface SearchFormProps { - query: string; - onQueryChange: (query: string) => void; - onSubmit: (query: string) => void; - placeholder?: string; - label?: string; -} - -export const SearchForm: FC = ({ - label = 'Zoek binnen Gemeente Voorbeeld', - onQueryChange, - onSubmit, - placeholder = 'Zoeken', - query, -}) => { - const [error, setError] = useState(null); - - const handleSubmit = (e: FormEvent) => { - e.preventDefault(); - const trimmedQuery = query.trim(); - - if (!trimmedQuery) { - setError('U moet een zoekterm invoeren voordat u zoekt.'); - return; - } - - setError(null); - onSubmit(trimmedQuery); - }; - - return ( -
-

- Zoekformulier -

- -
-
- - {error && ( - - )} - -
- onQueryChange(e.target.value)} aria-required id="search-query" type="search" aria-description='Search results will appear below' autoComplete='off' required aria-invalid={Boolean(error)} name="trefwoord" placeholder="Zoeken" label={label} className='clippy--visually-hidden'/> - - - - - - -
-
-
-
-
- ); -}; diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/index.tsx similarity index 69% rename from packages/theme-wizard-templates/src/pages/search/components/SearchResultItem.tsx rename to packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/index.tsx index 2424f864e..40ea3e865 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem.tsx +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/index.tsx @@ -1,6 +1,7 @@ import React, { type FC, memo } from 'react'; import { Mark } from '@utrecht/component-library-react/dist/css-module'; -import type { SearchResult } from '../types'; +import type { SearchResult } from './types'; +import './styles.css'; export interface SearchResultItemProps { result: SearchResult; @@ -17,18 +18,18 @@ const highlightText = (text: string, query?: string) => { return text; } - const terms = query.trim().split(/\s+/); - const regex = new RegExp(`(${terms.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})`, 'gi'); + const escapedQuery = query.trim().replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const regex = new RegExp(`(${escapedQuery})`, 'gi'); const parts = text.split(regex); return ( <> {parts.map((part, index) => { const isMatch = regex.test(part); - regex.lastIndex = 0; // Reset regex state + regex.lastIndex = 0; return isMatch ? ( - {part} + {part} ) : ( {part} ); @@ -39,18 +40,18 @@ const highlightText = (text: string, query?: string) => { export const SearchResultItem: FC = memo(({ position, result, totalResults, query }) => { return ( -
- -

+
+ +

{highlightText(result.title, query)}

-

+

{highlightText(result.description, query)}

-
- +
+ {result.type} {result.date && ( diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/styles.css b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/styles.css new file mode 100644 index 000000000..43d6b8c91 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/styles.css @@ -0,0 +1,70 @@ +/* ========================================================================== + Search Highlight + ========================================================================== */ +.clippy--search-highlight { + background-color: var(--basis-color-highlight-inverse-bg-default); + color: var(--basis-color-highlight-inverse-color-default); + padding-block: 0; + padding-inline: 2px; + border-radius: 2px; +} + +/* ========================================================================== + Result Items + ========================================================================== */ +.clippy--search-result-item__link { + display: flex; + flex-direction: column; + gap: var(--basis-space-row-lg); + text-decoration: none; + color: inherit; + padding: var(--basis-space-row-md); + margin-block: calc(-1 * var(--basis-space-row-md)); + margin-inline: calc(-1 * var(--basis-space-row-md)); + border-radius: var(--basis-border-radius-sm); + transition: background-color 0.2s ease; +} + +.clippy--search-result-item__link:hover, +.clippy--search-result-item__link:focus { + background-color: var(--basis-color-default-bg-subtle); + outline: 2px solid var(--basis-color-accent-1-color-default); + outline-offset: 2px; +} + +.clippy--search-result-item__title { + margin-block-start: 0; + margin-block-end: var(--basis-space-row-sm); + color: var(--basis-color-accent-1-color-default); + text-decoration: underline; +} + +.clippy--search-result-item__link:visited .clippy--search-result-item__title { + color: var(--basis-color-accent-1-color-active); +} + +.clippy--search-result-item__description { + margin: 0; + margin-block-end: var(--basis-space-row-sm); + color: var(--basis-color-default-color-document); +} + +.clippy--search-result-meta { + display: flex; + gap: var(--basis-space-inline-md); + font-size: var(--basis-text-font-size-sm); + color: var(--basis-color-default-color-subtle); +} + +.clippy--search-result-type { + font-weight: var(--basis-text-font-weight-bold); +} + +.clippy--search-result-item { + padding-block-end: var(--basis-space-row-lg); + border-block-end: 1px solid var(--basis-color-default-border-default); +} + +.clippy--search-result-item:last-child { + border-block-end: none; +} diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/types.ts b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/types.ts new file mode 100644 index 000000000..f0f6ee663 --- /dev/null +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/types.ts @@ -0,0 +1,9 @@ +export interface SearchResult { + id: string; + title: string; + url: string; + description: string; + type: string; + date?: string; + dateTime?: string; +} diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList/index.tsx similarity index 79% rename from packages/theme-wizard-templates/src/pages/search/components/SearchResultsList.tsx rename to packages/theme-wizard-templates/src/pages/search/components/SearchResultsList/index.tsx index 92c07a22c..fde602a53 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList.tsx +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList/index.tsx @@ -1,7 +1,8 @@ import { Paragraph } from '@utrecht/component-library-react/dist/css-module'; import React, { type FC } from 'react'; -import type { SearchResult } from '../types'; -import { SearchResultItem } from './SearchResultItem'; +import type { SearchResult } from '../SearchResultItem/types'; +import { SearchResultItem } from '../SearchResultItem'; +import './styles.css'; export interface SearchResultsListProps { results: SearchResult[]; @@ -26,11 +27,11 @@ export const SearchResultsList: FC = ({ query, results } return (
-

+

Lijst met zoekresultaten{queryText} -

+

-
    +
      {results.map((result, index) => (
    1. - + diff --git a/packages/theme-wizard-templates/src/pages/search/search.astro b/packages/theme-wizard-templates/src/pages/search/search.astro index c022933c1..5f3c4d284 100644 --- a/packages/theme-wizard-templates/src/pages/search/search.astro +++ b/packages/theme-wizard-templates/src/pages/search/search.astro @@ -1,16 +1,16 @@ --- import BaseLayout from '../../layouts/BaseLayout.astro'; -import SearchPage from './Search'; +import SearchPage from './SearchPage'; export const detail = { - name: 'Resultaten', - value: 'page', + name: 'Zoeken', + value: 'search', module: 'Zoeken', }; const currentPath = Astro.url.pathname; --- - + diff --git a/packages/theme-wizard-templates/src/pages/search/styles.css b/packages/theme-wizard-templates/src/pages/search/styles.css deleted file mode 100644 index 8a52db5f7..000000000 --- a/packages/theme-wizard-templates/src/pages/search/styles.css +++ /dev/null @@ -1,250 +0,0 @@ -/* Mark Component Override */ -.search-result-item__link .utrecht-mark { - --utrecht-mark-background-color: #ffe44d; - --utrecht-mark-color: #000; -} - -/* Page Container */ -.search-page-container { - display: flex; - flex-direction: column; - gap: var(--basis-space-row-2xl); -} - -@media (max-width: 768px) { - .search-results-layout { - grid-template-columns: 1fr; - } - - .search-results-sidebar { - order: 2; - } - - .search-results-main { - order: 1; - } -} - -/* Main Content Area */ -.search-results-main { - display: flex; - flex-direction: column; - gap: var(--basis-space-row-xl); -} - -/* Search Form Input Group */ -.search-form__input-group { - display: flex; - gap: var(--basis-space-inline-md); - align-items: flex-start; -} - -.search-form__input-group .utrecht-textbox { - flex: 1; - min-inline-size: 0; -} - -.search-form__submit { - flex-shrink: 0; -} - -/* Filter Container */ -.search-filters { - background-color: var(--basis-color-default-bg-subtle); - padding: var(--basis-space-row-lg); - border-radius: var(--basis-border-radius-sm); - border: 1px solid var(--basis-color-default-border-subtle); -} - -.search-filter-options { - display: flex; - flex-direction: column; - gap: var(--basis-space-row-md); -} - -/* Date Range Picker */ -.search-filter-date-range { - margin-block-start: var(--basis-space-row-md); - padding: var(--basis-space-row-md); - background-color: var(--basis-color-accent-1-bg-default); - border-radius: var(--basis-border-radius-sm); - border: 1px solid var(--basis-color-accent-1-border-subtle); -} - -.search-filter-date-range__fields { - display: flex; - flex-direction: column; - gap: var(--basis-space-row-md); -} - -.search-filter-date-range__fields .utrecht-form-field { - margin: 0; -} - -.search-filter-date-range__fields .utrecht-textbox { - width: 100%; - max-width: 200px; -} - -.search-results-content { - display: flex; - flex-direction: column; - gap: var(--basis-space-row-xl, 2rem); - min-width: 0; -} - -/* Grid Layout: Sidebar + Main */ -.search-results-layout { - display: flex; - flex-direction: column; - gap: var(--basis-space-column-2xl, 3rem); - align-items: start; -} - -/* Sidebar: Filters */ -.search-results-sidebar { - display: flex; - flex-direction: column; - gap: var(--basis-space-row-xl); -} - -.search-results-wrapper { - display: flex; - inline-size: 100%; - gap: var(--basis-space-row-4xl); -} - -/* Results Header */ -.search-results-header { - display: flex; - flex-direction: column; - gap: var(--basis-space-row-md); -} - -.search-results-summary { - display: flex; - align-items: baseline; - justify-content: space-between; - margin: 0; - inline-size: 100%; - gap: var(--basis-space-inline-lg, 1.5rem); -} - -.search-results-sort { - display: flex; - align-items: center; - gap: var(--basis-space-inline-sm); - flex: 0 0 auto; -} - -.search-results-heading-wrapper { - flex: 1; - min-width: 0; -} - -.search-results-sort__label { - margin: 0; - white-space: nowrap; - font-weight: var(--basis-text-font-weight-bold); -} - -.search-results-sort__control { - display: flex; - align-items: center; - gap: var(--basis-space-inline-md); - flex-wrap: wrap; -} - -.search-results-sort__indicator { - font-size: var(--basis-text-font-size-sm); - color: var(--basis-color-default-color-subtle); - font-weight: 500; - padding: var(--basis-space-inline-xs) var(--basis-space-inline-sm); - background-color: var(--basis-color-default-bg-default); - border-radius: var(--basis-border-radius-sm); - white-space: nowrap; - display: inline-flex; - align-items: center; - gap: var(--basis-space-inline-xs); -} - -.search-results-sort__indicator svg { - flex-shrink: 0; -} - -/* Results List */ -.search-results-list__items { - list-style: none; - padding: 0; - margin: 0; - display: flex; - flex-direction: column; - gap: var(--basis-space-row-xl); -} - -.search-result-item { - padding-block-end: var(--basis-space-row-lg); - border-block-end: 1px solid var(--basis-color-default-border-default); -} - -.search-result-item:last-child { - border-block-end: none; -} - -/* Clickable Result Block */ -.search-result-item__link { - display: block; - text-decoration: none; - color: inherit; - padding: var(--basis-space-row-md); - margin: calc(-1 * var(--basis-space-row-md)); - border-radius: var(--basis-border-radius-sm); - transition: background-color 0.2s ease; -} - -.search-result-item__link:hover, -.search-result-item__link:focus { - background-color: var(--basis-color-default-bg-subtle); - outline: 2px solid var(--basis-color-accent-1-color-default); - outline-offset: 2px; -} - -.search-result-item__title { - margin-block-start: 0; - margin-block-end: var(--basis-space-row-sm); - color: var(--basis-color-accent-1-color-default); - text-decoration: underline; -} - -.search-result-item__link:visited .search-result-item__title { - color: var(--basis-color-accent-1-color-active); -} - -.search-result-item__description { - margin: 0; - margin-block-end: var(--basis-space-row-sm); - color: var(--basis-color-default-color-document); -} - -.search-result-meta { - display: flex; - gap: var(--basis-space-inline-md); - font-size: var(--basis-text-font-size-sm); - color: var(--basis-color-default-color-subtle); -} - -.search-result-type { - font-weight: var(--basis-text-font-weight-bold); -} - - -.clippy--visually-hidden:not(:active, :focus) .utrecht-form-label { - block-size:1px; - clip-path:inset(50%); - inline-size:1px; - overflow:hidden; - position:absolute; - -webkit-user-select:none; - user-select:none; - white-space:nowrap -} \ No newline at end of file diff --git a/packages/theme-wizard-templates/src/pages/search/types.ts b/packages/theme-wizard-templates/src/pages/search/types.ts deleted file mode 100644 index d730b1f09..000000000 --- a/packages/theme-wizard-templates/src/pages/search/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface SearchResult { - id: string; - title: string; - url: string; - description: string; - type: string; - date?: string; - dateTime?: string; -} - -export interface SearchFilters { - period?: string; - ministry?: string; - documentType?: string; -} - -export interface SearchResultsData { - query: string; - totalResults: number; - results: SearchResult[]; - sortBy: 'relevance' | 'date'; - filters: SearchFilters; -} diff --git a/packages/theme-wizard-website/astro.config.mjs b/packages/theme-wizard-website/astro.config.mjs index a590f76c7..3e93939c2 100644 --- a/packages/theme-wizard-website/astro.config.mjs +++ b/packages/theme-wizard-website/astro.config.mjs @@ -1,5 +1,7 @@ +// import react from '@astrojs/react'; // This line seems to be outside the target content in the user's file based on previous read, checking context. import react from '@astrojs/react'; import vercel from '@astrojs/vercel'; +// import node from '@astrojs/node'; import { defineConfig } from 'astro/config'; import { readdirSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; @@ -19,6 +21,7 @@ const fontDirs = readdirSync(pnpmDir) // https://astro.build/config export default defineConfig({ + output: 'server', adapter: vercel(), devToolbar: { enabled: false, diff --git a/packages/theme-wizard-website/package.json b/packages/theme-wizard-website/package.json index 132c1643f..8ccd45473 100644 --- a/packages/theme-wizard-website/package.json +++ b/packages/theme-wizard-website/package.json @@ -25,8 +25,9 @@ "test-e2e": "playwright test" }, "dependencies": { - "@astrojs/vercel": "9.0.2", + "@astrojs/node": "9.5.2", "@astrojs/react": "^4.4.2", + "@astrojs/vercel": "9.0.2", "@nl-design-system-community/ma-design-tokens": "2.5.0", "@nl-design-system-community/theme-wizard-app": "workspace:*", "@nl-design-system-community/theme-wizard-templates": "workspace:*", From 3c7cbad034058ae10ee6ade87b7be85efc23ee17 Mon Sep 17 00:00:00 2001 From: Michelle Date: Wed, 4 Feb 2026 16:35:50 +0100 Subject: [PATCH 18/33] feat(layout): introduce TemplateLayout and integrate navigation with search pattern --- .../src/components/QuickTasks/index.tsx | 6 +- .../src/layouts/BaseLayout.astro | 5 +- .../src/layouts/TemplateLayout.tsx | 49 +++++++++++ .../GemeenteVoorbeeldHome.tsx | 38 ++++---- .../src/sections/Navigation/index.tsx | 87 +++++++++++++++---- 5 files changed, 146 insertions(+), 39 deletions(-) create mode 100644 packages/theme-wizard-templates/src/layouts/TemplateLayout.tsx diff --git a/packages/theme-wizard-templates/src/components/QuickTasks/index.tsx b/packages/theme-wizard-templates/src/components/QuickTasks/index.tsx index 02e08c6b5..867f212c7 100644 --- a/packages/theme-wizard-templates/src/components/QuickTasks/index.tsx +++ b/packages/theme-wizard-templates/src/components/QuickTasks/index.tsx @@ -9,7 +9,7 @@ import { UtrechtIconNummerbord, UtrechtIconAfvalScheiden, } from '@utrecht/web-component-library-react'; -import React, { type ReactElement } from 'react'; +import React, { type ReactNode } from 'react'; import type { QuickTask } from './types'; import { Column, Row } from '../Layout'; import '../shared/card-styles.css'; @@ -21,7 +21,7 @@ export interface QuickTasksProps { type IconName = 'afval-scheiden' | 'melding-klacht' | 'nummerbord' | 'paspoort' | 'verhuizen' | 'werken'; -const ICON_COMPONENTS: Record = { +const ICON_COMPONENTS: Record = { 'afval-scheiden': , 'melding-klacht': , nummerbord: , @@ -30,7 +30,7 @@ const ICON_COMPONENTS: Record = { werken: , }; -const renderIcon = (iconName: string): JSX.Element | null => { +const renderIcon = (iconName: string): ReactNode => { const icon = ICON_COMPONENTS[iconName as IconName]; return icon ?? null; }; diff --git a/packages/theme-wizard-templates/src/layouts/BaseLayout.astro b/packages/theme-wizard-templates/src/layouts/BaseLayout.astro index 6f8c2989e..9e536f9a3 100644 --- a/packages/theme-wizard-templates/src/layouts/BaseLayout.astro +++ b/packages/theme-wizard-templates/src/layouts/BaseLayout.astro @@ -30,6 +30,9 @@ const { + @@ -41,7 +44,7 @@ const { - + diff --git a/packages/theme-wizard-templates/src/layouts/TemplateLayout.tsx b/packages/theme-wizard-templates/src/layouts/TemplateLayout.tsx new file mode 100644 index 000000000..a8bb70d83 --- /dev/null +++ b/packages/theme-wizard-templates/src/layouts/TemplateLayout.tsx @@ -0,0 +1,49 @@ +import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; +import { PageBody } from '@utrecht/page-body-react'; +import React from 'react'; +import Navigation, { type NavigationProps } from '../sections/Navigation'; +import PageFooterSection from '../sections/PageFooter'; +import PageHeaderSection from '../sections/PageHeader'; + +export interface TemplateLayoutProps extends NavigationProps { + children: React.ReactNode; + mainId?: string; +} + +/** + * TemplateLayout provides the standard structure for all template pages. + * It includes the SkipLink, Header, Navigation (with Search pattern), Footer, and PageBody wrapper. + */ +export const TemplateLayout = ({ + children, + currentPath, + breadcrumb, + searchQuery, + onSearchQueryChange, + onSearchSubmit, + mainId = 'main', +}: TemplateLayoutProps) => { + return ( + <> + Skip to main content + + + + + + + {children} + + + + + ); +}; + +export default TemplateLayout; diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx index 1c43ee272..1f3030e18 100644 --- a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx +++ b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/GemeenteVoorbeeldHome.tsx @@ -1,12 +1,8 @@ -import { SkipLink } from '@nl-design-system-candidate/skip-link-react/css'; import React from 'react'; -import { PageBody } from '@utrecht/page-body-react'; import type { SummaryItem } from '../../components/OpeningHoursCard/types'; +import TemplateLayout from '../../layouts/TemplateLayout'; import MainIntroSection from '../../sections/MainIntro'; -import Navigation from '../../sections/Navigation'; import NewsSection from '../../sections/News'; -import PageFooterSection from '../../sections/PageFooter'; -import PageHeaderSection from '../../sections/PageHeader'; import SelfServiceSection from '../../sections/SelfService'; export interface GemeenteVoorbeeldHomeProps { @@ -14,15 +10,22 @@ export interface GemeenteVoorbeeldHomeProps { openingHoursSummary?: SummaryItem[]; } -const GemeenteVoorbeeldHome = ({ currentPath, openingHoursSummary }: GemeenteVoorbeeldHomeProps) => ( - <> - Skip to main content - - - - - - +const GemeenteVoorbeeldHome = ({ currentPath, openingHoursSummary }: GemeenteVoorbeeldHomeProps) => { + const [searchQuery, setSearchQuery] = React.useState(''); + + const handleSearch = (query: string) => { + const params = new URLSearchParams(); + params.set('search', query); + window.location.href = `/search/search-results?${params.toString()}`; + }; + + return ( +
      @@ -30,9 +33,8 @@ const GemeenteVoorbeeldHome = ({ currentPath, openingHoursSummary }: GemeenteVoo
      -
      - - -); + + ); +}; export default GemeenteVoorbeeldHome; diff --git a/packages/theme-wizard-templates/src/sections/Navigation/index.tsx b/packages/theme-wizard-templates/src/sections/Navigation/index.tsx index f8b3ee3ee..69a897c26 100644 --- a/packages/theme-wizard-templates/src/sections/Navigation/index.tsx +++ b/packages/theme-wizard-templates/src/sections/Navigation/index.tsx @@ -1,27 +1,80 @@ import { Heading } from '@nl-design-system-candidate/heading-react/css'; import '@amsterdam/design-system-css/dist/visually-hidden/visually-hidden.css'; -import { NavBar, NavList, NavListLink } from '@utrecht/component-library-react'; -import React from 'react'; +import { Icon, NavBar, NavList, NavListLink } from '@utrecht/component-library-react'; +import React, { useState } from 'react'; import { NAVIGATION_ITEMS } from '../../pages/gemeentevoorbeeld/constants'; +import { IconMenu2, IconX } from '@tabler/icons-react'; +import { SearchForm } from '../../components/SearchForm'; export interface NavigationProps { currentPath?: string; + children?: React.ReactNode; + breadcrumb?: React.ReactNode; + // Search pattern props + showSearch?: boolean; + searchQuery?: string; + onSearchQueryChange?: (query: string) => void; + onSearchSubmit?: (query: string) => void; } -const Navigation = ({ currentPath }: NavigationProps) => ( - - - Hoofdnavigatie - - - - {NAVIGATION_ITEMS.map(({ href, label }) => ( - - {label} - - ))} - - -); +const Navigation = ({ + currentPath, + children, + breadcrumb, + showSearch, + searchQuery = '', + onSearchQueryChange, + onSearchSubmit +}: NavigationProps) => { + const [isOpen, setIsOpen] = useState(false); + + return ( + +
      + + Hoofdnavigatie + + + {breadcrumb &&
      {breadcrumb}
      } + + + + + + {(showSearch || (onSearchQueryChange && onSearchSubmit)) && ( +
      + {})} + onSubmit={onSearchSubmit || (() => {})} + /> +
      + )} + + {children &&
      {children}
      } +
      +
      + ); +}; export default Navigation; From 061fba7390431becbc1ae1055ba90828d87be401 Mon Sep 17 00:00:00 2001 From: Michelle Date: Wed, 4 Feb 2026 16:36:40 +0100 Subject: [PATCH 19/33] feat(search): update constants to be organization-agnostic and improve filter logic --- .../src/pages/search/constants.ts | 33 ++++++++++--------- .../pages/search/hooks/useSearchFilters.ts | 17 ++++++++-- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/packages/theme-wizard-templates/src/pages/search/constants.ts b/packages/theme-wizard-templates/src/pages/search/constants.ts index 6d5773e76..c6e81d25a 100644 --- a/packages/theme-wizard-templates/src/pages/search/constants.ts +++ b/packages/theme-wizard-templates/src/pages/search/constants.ts @@ -1,26 +1,27 @@ -import type { SearchResultsData } from './types'; +import type { SearchResultsData } from './SearchResultsPage/types'; export const SEARCH_FILTER_PERIODS = [ - { label: 'Afgelopen 7 dagen', value: '7' }, - { label: 'Afgelopen 30 dagen', value: '30' }, - { label: 'Afgelopen 365 dagen', value: '365' }, + { label: 'Altijd', value: 'all', count: 471 }, + { label: 'Afgelopen 7 dagen', value: '7', count: 12 }, + { label: 'Afgelopen 30 dagen', value: '30', count: 45 }, + { label: 'Afgelopen 365 dagen', value: '365', count: 120 }, { label: 'Specifieke periode', value: 'custom' }, ] as const; -export const SEARCH_FILTER_MINISTRIES = [ - { label: 'Alle ministeries', value: 'all' }, - { label: 'Ministerie van Binnenlandse Zaken en Koninkrijksrelaties', value: 'bzk' }, - { label: 'Ministerie van Volksgezondheid, Welzijn en Sport', value: 'vws' }, - { label: 'Ministerie van Economische Zaken', value: 'ez' }, - { label: 'Ministerie van Onderwijs, Cultuur en Wetenschap', value: 'ocw' }, +export const SEARCH_FILTER_ORGANIZATIONS = [ + { label: 'Alle organisaties', value: 'all', count: 471 }, + { label: 'Organisatie A', value: 'org-a', count: 156 }, + { label: 'Organisatie B', value: 'org-b', count: 89 }, + { label: 'Organisatie C', value: 'org-c', count: 124 }, + { label: 'Organisatie D', value: 'org-d', count: 102 }, ] as const; export const SEARCH_FILTER_TYPES = [ - { label: 'Alle documenten', value: 'all' }, - { label: 'Nieuwsbericht', value: 'news' }, - { label: 'Vraag en antwoord', value: 'qa' }, - { label: 'Publicatie', value: 'publication' }, - { label: 'Rapport', value: 'report' }, + { label: 'Alle documenten', value: 'all', count: 245 }, + { label: 'Nieuwsbericht', value: 'news', count: 86 }, + { label: 'Vraag en antwoord', value: 'qa', count: 54 }, + { label: 'Publicatie', value: 'publication', count: 92 }, + { label: 'Rapport', value: 'report', count: 13 }, ] as const; export const SEARCH_SORT_OPTIONS = [ @@ -31,7 +32,7 @@ export const SEARCH_SORT_OPTIONS = [ // Mock data - replace with actual data fetching export const MOCK_SEARCH_RESULTS: SearchResultsData = { filters: {}, - query: 'afval', + query: '', results: [ { id: '1', diff --git a/packages/theme-wizard-templates/src/pages/search/hooks/useSearchFilters.ts b/packages/theme-wizard-templates/src/pages/search/hooks/useSearchFilters.ts index 0479e43d0..d50ca4afb 100644 --- a/packages/theme-wizard-templates/src/pages/search/hooks/useSearchFilters.ts +++ b/packages/theme-wizard-templates/src/pages/search/hooks/useSearchFilters.ts @@ -1,8 +1,19 @@ import { useState, useCallback } from 'react'; -import type { SearchFilters } from '../types'; +import type { SearchFilters } from '../components/SearchFilters/types'; + +const DEFAULT_FILTERS: SearchFilters = { + period: 'all', + documentType: 'all', + organization: 'all', +}; export const useSearchFilters = (initialFilters: SearchFilters = {}) => { - const [filters, setFilters] = useState(initialFilters); + // Filter out undefined values to ensure DEFAULT_FILTERS are used when keys are missing in URL + const cleanInitialFilters = Object.fromEntries( + Object.entries(initialFilters).filter(([_, value]) => value !== undefined && value !== null) + ); + + const [filters, setFilters] = useState({ ...DEFAULT_FILTERS, ...cleanInitialFilters }); const updateFilter = useCallback((key: keyof SearchFilters, value: string) => { setFilters((prev) => ({ @@ -12,7 +23,7 @@ export const useSearchFilters = (initialFilters: SearchFilters = {}) => { }, []); const resetFilters = useCallback(() => { - setFilters({}); + setFilters(DEFAULT_FILTERS); }, []); return { From e95eea4ea23a9c97ae1c928f8df2a1df42700c8c Mon Sep 17 00:00:00 2001 From: Michelle Date: Wed, 4 Feb 2026 16:39:44 +0100 Subject: [PATCH 20/33] chore(storybook): update search templates and preview configuration --- .../clippy-storybook/config/preview-head.html | 3 + .../src/templates/SearchResults.tsx | 28 --- .../src/templates/Zoeken/Results.stories.tsx | 41 +++ .../src/templates/Zoeken/Search.stories.tsx | 32 +++ .../src/templates/Zoeken/docs/Results.md | 233 ++++++++++++++++++ .../src/templates/Zoeken/docs/Search.md | 13 + 6 files changed, 322 insertions(+), 28 deletions(-) delete mode 100644 packages/clippy-storybook/src/templates/SearchResults.tsx create mode 100644 packages/clippy-storybook/src/templates/Zoeken/Results.stories.tsx create mode 100644 packages/clippy-storybook/src/templates/Zoeken/Search.stories.tsx create mode 100644 packages/clippy-storybook/src/templates/Zoeken/docs/Results.md create mode 100644 packages/clippy-storybook/src/templates/Zoeken/docs/Search.md diff --git a/packages/clippy-storybook/config/preview-head.html b/packages/clippy-storybook/config/preview-head.html index 7f8a041fd..1fd9eec45 100644 --- a/packages/clippy-storybook/config/preview-head.html +++ b/packages/clippy-storybook/config/preview-head.html @@ -1,3 +1,6 @@ + diff --git a/packages/theme-wizard-templates/src/components/SearchForm/styles.css b/packages/theme-wizard-templates/src/components/SearchForm/styles.css index ff5ab56ff..de63d45c5 100644 --- a/packages/theme-wizard-templates/src/components/SearchForm/styles.css +++ b/packages/theme-wizard-templates/src/components/SearchForm/styles.css @@ -7,24 +7,24 @@ } .clippy--search-form__input-group .utrecht-textbox { - flex: 1; - min-inline-size: 0; - border-start-start-radius: var(--basis-border-radius-sm); - border-end-start-radius: var(--basis-border-radius-sm); - border-start-end-radius: 0; + block-size: auto; border-end-end-radius: 0; + border-end-start-radius: var(--basis-border-radius-sm); border-inline-end: none; - block-size: auto; + border-start-end-radius: 0; + border-start-start-radius: var(--basis-border-radius-sm); + flex: 1; + min-inline-size: 0; } .clippy--search-form__input-group .nl-button { - border-start-start-radius: 0; + block-size: auto; + border-end-end-radius: var(--basis-border-radius-sm); border-end-start-radius: 0; + border-inline-start: 1px solid var(--utrecht-textbox-border-color); border-start-end-radius: var(--basis-border-radius-sm); - border-end-end-radius: var(--basis-border-radius-sm); + border-start-start-radius: 0; margin: 0; - border-inline-start: 1px solid var(--utrecht-textbox-border-color); - block-size: auto; } .clippy--search-form__submit { @@ -32,15 +32,15 @@ } .clippy--search-form .nl-button--icon-only { - display: flex; align-items: center; - justify-content: center; - padding: 0; - min-inline-size: 3rem; - border-start-start-radius: 0; + border-end-end-radius: var(--basis-border-radius-sm); border-end-start-radius: 0; border-start-end-radius: var(--basis-border-radius-sm); - border-end-end-radius: var(--basis-border-radius-sm); + border-start-start-radius: 0; + display: flex; + justify-content: center; + min-inline-size: 3rem; + padding: 0; } .clippy--search-form .nl-button--icon-only svg { @@ -48,11 +48,11 @@ } /* Responsive Design (Mobile) - Contextual styles when inside navigation */ -@media (max-width: 768px) { +@media (width <= 768px) { /* Search Wrapper (Header) */ .clippy--navigation-search-wrapper .clippy--search-form-section { - margin-block: var(--basis-space-inline-xs); inline-size: 100%; + margin-block: var(--basis-space-inline-xs); } .clippy--navigation-search-wrapper .clippy--search-form { @@ -73,7 +73,7 @@ } .clippy--navigation-search-wrapper .clippy--search-form__input-group .utrecht-form-field { - min-inline-size: 0; flex: 1; + min-inline-size: 0; } } diff --git a/packages/theme-wizard-templates/src/pages/search/SearchResultsPage/styles.css b/packages/theme-wizard-templates/src/pages/search/SearchResultsPage/styles.css index f02f0d648..805b0441d 100644 --- a/packages/theme-wizard-templates/src/pages/search/SearchResultsPage/styles.css +++ b/packages/theme-wizard-templates/src/pages/search/SearchResultsPage/styles.css @@ -22,36 +22,36 @@ } .clippy--search-results-layout { + align-items: stretch; display: flex; flex-direction: column; gap: var(--basis-space-column-2xl); - align-items: stretch; } .clippy--search-results-wrapper { + align-items: flex-start; display: flex; - inline-size: 100%; gap: 6rem; - align-items: flex-start; + inline-size: 100%; } .clippy--search-results-content { - padding-block-start: var(--basis-space-block-lg); display: flex; + flex: 1; flex-direction: column; gap: var(--basis-space-row-xl); - flex: 1; min-inline-size: 0; + padding-block-start: var(--basis-space-block-lg); } /* ========================================================================== Navigation & Breadcrumbs ========================================================================== */ .clippy--navigation-content-wrapper { - display: flex; align-items: center; - justify-content: space-between; + display: flex; inline-size: 100%; + justify-content: space-between; } .clippy--navigation-search-wrapper { @@ -86,13 +86,13 @@ } .utrecht-breadcrumb-nav__list { + align-items: center; display: flex; flex-wrap: wrap; + font-size: var(--basis-font-size-sm); list-style: none; - padding: 0; margin: 0; - font-size: var(--basis-font-size-sm); - align-items: center; + padding: 0; } .utrecht-breadcrumb-nav__link { @@ -105,11 +105,11 @@ } .clippy--breadcrumb-separator { + align-items: center; color: var(--basis-color-default-color-subtle); - margin-inline: var(--basis-space-inline-xs); display: flex !important; - align-items: center; justify-content: center; + margin-inline: var(--basis-space-inline-xs); } .utrecht-breadcrumb-nav__link[aria-current="page"] { @@ -121,6 +121,7 @@ /* ========================================================================== Search Results Area (Header & Layout) ========================================================================== */ + /* Header & Controls */ .clippy--search-results-header { display: flex; @@ -129,12 +130,12 @@ } .clippy--search-results-summary { - display: flex; align-items: baseline; + display: flex; + gap: var(--basis-space-inline-lg); + inline-size: 100%; justify-content: space-between; margin: 0; - inline-size: 100%; - gap: var(--basis-space-inline-lg); margin-block-end: var(--basis-space-block-2xl); } @@ -144,10 +145,10 @@ } .clippy--search-results-sort { - display: flex; align-items: center; - gap: var(--basis-space-inline-md); + display: flex; flex: 0 0 auto; + gap: var(--basis-space-inline-md); } .clippy--search-results-reset-button { @@ -155,22 +156,22 @@ } .clippy--search-results-sort__label { + font-weight: var(--basis-text-font-weight-bold); margin: 0; white-space: nowrap; - font-weight: var(--basis-text-font-weight-bold); } .clippy--search-results-sort__control { - display: flex; align-items: center; - gap: var(--basis-space-inline-md); + display: flex; flex-wrap: wrap; + gap: var(--basis-space-inline-md); } /* ========================================================================== Responsive Design (Mobile) ========================================================================== */ -@media (max-width: 768px) { +@media (width <= 768px) { /* Navigation & Header */ .clippy--navigation-content-wrapper { flex-wrap: wrap; @@ -179,35 +180,35 @@ } .clippy--navigation-hamburger-button { - display: flex !important; align-items: center; - justify-content: center; background-color: var(--basis-color-default-bg-subtle); border: 1px solid var(--basis-color-default-border-subtle); border-radius: var(--basis-border-radius-sm); - padding-block: var(--basis-space-inline-xs); - padding-inline: var(--basis-space-inline-sm); - cursor: pointer; color: var(--basis-color-default-color-document); - z-index: 10; - order: 2; + cursor: pointer; + display: flex !important; flex: initial; - gap: var(--basis-space-inline-xs); font-weight: var(--basis-font-weight-bold); + gap: var(--basis-space-inline-xs); + justify-content: center; min-block-size: var(--basis-pointer-target-min-block-size); + order: 2; + padding-block: var(--basis-space-inline-xs); + padding-inline: var(--basis-space-inline-sm); + z-index: 10; } .clippy--navigation-hamburger-icon { - display: flex; align-items: center; + display: flex; justify-content: center; line-height: 0; } .clippy--navigation-hamburger-label { font-size: var(--basis-font-size-sm); - text-transform: uppercase; letter-spacing: 0.05em; + text-transform: uppercase; } /* Breadcrumbs */ @@ -218,8 +219,8 @@ .clippy--navigation-breadcrumb-wrapper { display: block; flex: 1; - overflow: hidden; order: 1; + overflow: hidden; } .clippy--navigation-breadcrumb-wrapper .utrecht-breadcrumb-nav { @@ -228,10 +229,10 @@ /* Navigation Menu (Dropdown) */ .clippy--navigation-menu { - display: none; - inline-size: 100%; background-color: var(--basis-color-default-bg-default); border-block-start: 1px solid var(--basis-color-default-border-subtle); + display: none; + inline-size: 100%; order: 4; } @@ -241,8 +242,8 @@ .clippy--navigation-menu .utrecht-nav-list { flex-direction: column; - padding: var(--basis-space-row-md); margin: 0; + padding: var(--basis-space-row-md); } /* Search Wrapper (Header) - Navigation specific layout only */ diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/index.tsx b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/index.tsx index 5ca5d6256..d81d3ba6d 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/index.tsx +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/index.tsx @@ -4,7 +4,6 @@ import { FieldsetLegend, FormField, FormLabel, - Paragraph, RadioButton, } from '@utrecht/component-library-react/dist/css-module'; import { NumberBadge } from '@nl-design-system-candidate/number-badge-react/css'; diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/styles.css b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/styles.css index eb9effe7c..420674db2 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/styles.css +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/SearchFiltersGroup/styles.css @@ -3,12 +3,12 @@ ========================================================================== */ .clippy--search-filter-heading { - margin-block-end: var(--basis-space-block-xl); - display: flex; align-items: baseline; - justify-content: space-between; + border-block-end: 1px solid var(--basis-color-accent-1-border-subtle); + display: flex; gap: var(--basis-space-inline-md); - border-block-end: 1px solid var(--basis-color-accent-1-border-subtle) + justify-content: space-between; + margin-block-end: var(--basis-space-block-xl) } .clippy--search-filter-heading h3 { @@ -16,23 +16,23 @@ } .clippy--search-filter-options { - padding: 0; - list-style-type: none; display: flex; flex-direction: column; gap: var(--basis-space-row-md); + list-style-type: none; + padding: 0; } .clippy--search-filter-list-item { - display: flex; align-items: center; + display: flex; } .clippy--search-filter-label { - flex-grow: 1; display: flex; - min-inline-size: 0; + flex-grow: 1; justify-content: space-between; + min-inline-size: 0; } .clippy--radio-button { diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/styles.css b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/styles.css index 849ab8d41..bf6352730 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/styles.css +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchFilters/styles.css @@ -2,9 +2,9 @@ Filter Sidebar ========================================================================== */ .clippy--search-filters { + border-radius: var(--basis-border-radius-sm); inline-size: 25%; padding-block-end: var(--basis-space-block-xl); - border-radius: var(--basis-border-radius-sm); } .clippy--search-filters-form { @@ -14,17 +14,17 @@ } .clippy--search-filters-header { - margin-block-end: var(--basis-space-block-lg); display: flex; justify-content: space-between; + margin-block-end: var(--basis-space-block-lg); } .clippy--search-filter-date-range { - margin-block-start: var(--basis-space-row-md); - padding: var(--basis-space-row-md); background-color: var(--basis-color-accent-1-bg-default); - border-radius: var(--basis-border-radius-sm); border: 1px solid var(--basis-color-accent-1-border-subtle); + border-radius: var(--basis-border-radius-sm); + margin-block-start: var(--basis-space-row-md); + padding: var(--basis-space-row-md); } .clippy--search-filter-date-range__fields { @@ -58,7 +58,7 @@ } /* Responsive Design */ -@media (max-width: 768px) { +@media (width <= 768px) { .clippy--search-filters { flex: initial; inline-size: 100%; diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/styles.css b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/styles.css index 43d6b8c91..253461304 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/styles.css +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultItem/styles.css @@ -3,25 +3,25 @@ ========================================================================== */ .clippy--search-highlight { background-color: var(--basis-color-highlight-inverse-bg-default); + border-radius: 2px; color: var(--basis-color-highlight-inverse-color-default); padding-block: 0; padding-inline: 2px; - border-radius: 2px; } /* ========================================================================== Result Items ========================================================================== */ .clippy--search-result-item__link { + border-radius: var(--basis-border-radius-sm); + color: inherit; display: flex; flex-direction: column; gap: var(--basis-space-row-lg); - text-decoration: none; - color: inherit; - padding: var(--basis-space-row-md); margin-block: calc(-1 * var(--basis-space-row-md)); margin-inline: calc(-1 * var(--basis-space-row-md)); - border-radius: var(--basis-border-radius-sm); + padding: var(--basis-space-row-md); + text-decoration: none; transition: background-color 0.2s ease; } @@ -33,9 +33,9 @@ } .clippy--search-result-item__title { - margin-block-start: 0; - margin-block-end: var(--basis-space-row-sm); color: var(--basis-color-accent-1-color-default); + margin-block-end: var(--basis-space-row-sm); + margin-block-start: 0; text-decoration: underline; } @@ -44,16 +44,16 @@ } .clippy--search-result-item__description { + color: var(--basis-color-default-color-document); margin: 0; margin-block-end: var(--basis-space-row-sm); - color: var(--basis-color-default-color-document); } .clippy--search-result-meta { + color: var(--basis-color-default-color-subtle); display: flex; - gap: var(--basis-space-inline-md); font-size: var(--basis-text-font-size-sm); - color: var(--basis-color-default-color-subtle); + gap: var(--basis-space-inline-md); } .clippy--search-result-type { @@ -61,8 +61,8 @@ } .clippy--search-result-item { - padding-block-end: var(--basis-space-row-lg); border-block-end: 1px solid var(--basis-color-default-border-default); + padding-block-end: var(--basis-space-row-lg); } .clippy--search-result-item:last-child { diff --git a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList/styles.css b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList/styles.css index 5be46e6cf..2cb024f72 100644 --- a/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList/styles.css +++ b/packages/theme-wizard-templates/src/pages/search/components/SearchResultsList/styles.css @@ -2,10 +2,10 @@ Results List ========================================================================== */ .clippy--search-results-list__items { - list-style: none; - padding: 0; - margin: 0; display: flex; flex-direction: column; gap: var(--basis-space-row-xl); + list-style: none; + margin: 0; + padding: 0; } From 3561d53224632faa89fad148bfdbf0274f3b8ec5 Mon Sep 17 00:00:00 2001 From: Michelle Date: Wed, 4 Feb 2026 17:11:07 +0100 Subject: [PATCH 25/33] chore: apply code formatting, update Stylelint patterns --- .stylelintrc.json | 6 +- .../src/components/DevTools.astro | 5 - .../src/components/SearchForm/index.tsx | 2 +- .../src/components/SearchForm/styles.css | 14 +- .../src/custom-elements.d.ts | 160 ------------------ .../src/layouts/BaseLayout.astro | 2 +- .../src/layouts/TemplateLayout.tsx | 6 +- .../src/pages/gemeentevoorbeeld/page.astro | 2 +- .../src/pages/search/SearchPage/index.tsx | 4 +- .../pages/search/SearchResultsPage/index.tsx | 22 +-- .../pages/search/SearchResultsPage/styles.css | 33 ++-- .../SearchFiltersGroup/index.tsx | 8 +- .../SearchFiltersGroup/styles.css | 3 +- .../search/components/SearchFilters/index.tsx | 11 +- .../components/SearchFilters/styles.css | 8 +- .../search/components/SearchFilters/types.ts | 2 - .../components/SearchResultItem/index.tsx | 4 +- .../components/SearchResultItem/styles.css | 9 +- .../components/SearchResultsList/styles.css | 6 +- .../src/pages/search/constants.ts | 28 +-- .../pages/search/hooks/useSearchFilters.ts | 4 +- .../src/sections/Navigation/index.tsx | 18 +- .../theme-wizard-website/astro.config.mjs | 2 +- 23 files changed, 105 insertions(+), 254 deletions(-) delete mode 100644 packages/theme-wizard-templates/src/custom-elements.d.ts diff --git a/.stylelintrc.json b/.stylelintrc.json index 11f7521b3..e9897e60a 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -12,9 +12,9 @@ "scss/percent-placeholder-pattern": "^(theme|utrecht)-[a-z0-9-]+$", "scss/operator-no-newline-after": null, "scss/at-extend-no-missing-placeholder": null, - "custom-property-pattern": "^_?(ams|basis|clippy|theme|denhaag|utrecht)-[a-z0-9-]+$", - "selector-class-pattern": "^(ams|basis|clippy|theme|denhaag|utrecht)-[a-z0-9_-]+$", - "keyframes-name-pattern": "^(ams|basis|clippy|theme|utrecht)-[a-z0-9-]+$", + "custom-property-pattern": "^_?(nl|ams|basis|clippy|theme|denhaag|utrecht)-[a-z0-9-]+$", + "selector-class-pattern": "^(nl|ams|basis|clippy|theme|denhaag|utrecht)-[a-z0-9_-]+$", + "keyframes-name-pattern": "^(nl|ams|basis|clippy|theme|utrecht)-[a-z0-9-]+$", "at-rule-no-unknown": null, "block-no-empty": [true], "color-no-invalid-hex": [true], diff --git a/packages/theme-wizard-templates/src/components/DevTools.astro b/packages/theme-wizard-templates/src/components/DevTools.astro index 0c0a437fe..570969b1b 100644 --- a/packages/theme-wizard-templates/src/components/DevTools.astro +++ b/packages/theme-wizard-templates/src/components/DevTools.astro @@ -1,8 +1,3 @@ ---- -// Check if we're in standalone mode (defined in astro.config.mjs) -const isStandalone = typeof __STANDALONE_PACKAGE__ !== 'undefined' && __STANDALONE_PACKAGE__ === true; ---- -
      diff --git a/packages/theme-wizard-templates/src/components/SearchForm/styles.css b/packages/theme-wizard-templates/src/components/SearchForm/styles.css index de63d45c5..dafd0d7b1 100644 --- a/packages/theme-wizard-templates/src/components/SearchForm/styles.css +++ b/packages/theme-wizard-templates/src/components/SearchForm/styles.css @@ -17,33 +17,37 @@ min-inline-size: 0; } -.clippy--search-form__input-group .nl-button { +.clippy--search-form__submit-button { block-size: auto; border-end-end-radius: var(--basis-border-radius-sm); border-end-start-radius: 0; border-inline-start: 1px solid var(--utrecht-textbox-border-color); border-start-end-radius: var(--basis-border-radius-sm); border-start-start-radius: 0; - margin: 0; + margin-block: 0; + margin-inline: 0; } .clippy--search-form__submit { flex-shrink: 0; } -.clippy--search-form .nl-button--icon-only { +.clippy--search-form__submit-button.nl-button--icon-only { align-items: center; + border-block: none; border-end-end-radius: var(--basis-border-radius-sm); border-end-start-radius: 0; + border-inline: none; border-start-end-radius: var(--basis-border-radius-sm); border-start-start-radius: 0; display: flex; justify-content: center; min-inline-size: 3rem; - padding: 0; + padding-block: 0; + padding-inline: 0; } -.clippy--search-form .nl-button--icon-only svg { +.clippy--search-form__submit-button.nl-button--icon-only svg { flex-shrink: 0; } diff --git a/packages/theme-wizard-templates/src/custom-elements.d.ts b/packages/theme-wizard-templates/src/custom-elements.d.ts deleted file mode 100644 index 8dd939930..000000000 --- a/packages/theme-wizard-templates/src/custom-elements.d.ts +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Custom Elements Type Definitions - * - * This file provides TypeScript definitions for custom web components used in the application. - * It extends JSX.IntrinsicElements to include both clippy-* and template-* custom elements. - */ - -declare namespace JSX { - interface IntrinsicElements { - // Clippy Components (from @nl-design-system-community/clippy-components) - 'clippy-button': React.DetailedHTMLProps< - React.HTMLAttributes & { - type?: 'button' | 'submit' | 'reset'; - disabled?: boolean; - class?: string; - 'aria-label'?: string; - }, - HTMLElement - >; - - 'clippy-heading': React.DetailedHTMLProps< - React.HTMLAttributes & { - level?: 1 | 2 | 3 | 4 | 5 | 6; - appearance?: 'level-1' | 'level-2' | 'level-3' | 'level-4' | 'level-5' | 'level-6'; - id?: string; - class?: string; - }, - HTMLElement - >; - - 'clippy-code': React.DetailedHTMLProps, HTMLElement>; - - 'clippy-color-combobox': React.DetailedHTMLProps< - React.HTMLAttributes & { - value?: string; - name?: string; - label?: string; - }, - HTMLElement - >; - - 'clippy-combobox': React.DetailedHTMLProps< - React.HTMLAttributes & { - value?: string; - name?: string; - label?: string; - }, - HTMLElement - >; - - 'clippy-font-combobox': React.DetailedHTMLProps< - React.HTMLAttributes & { - value?: string; - name?: string; - label?: string; - }, - HTMLElement - >; - - 'clippy-html-image': React.DetailedHTMLProps< - React.HTMLAttributes & { - src?: string; - alt?: string; - }, - HTMLElement - >; - - 'clippy-icon': React.DetailedHTMLProps< - React.HTMLAttributes & { - name?: string; - }, - HTMLElement - >; - - 'clippy-modal': React.DetailedHTMLProps< - React.HTMLAttributes & { - open?: boolean; - }, - HTMLElement - >; - - 'clippy-link': React.DetailedHTMLProps< - React.HTMLAttributes & { - href?: string; - target?: '_blank' | '_self' | '_parent' | '_top'; - rel?: string; - }, - HTMLElement - >; - - // Template Components (local web components) - 'template-action': React.DetailedHTMLProps< - React.HTMLAttributes & { - href?: string; - label?: string; - }, - HTMLElement - >; - - 'template-case-card': React.DetailedHTMLProps< - React.HTMLAttributes & { - title?: string; - description?: string; - href?: string; - }, - HTMLElement - >; - - 'template-color-swatch': React.DetailedHTMLProps< - React.HTMLAttributes & { - color?: string; - label?: string; - }, - HTMLElement - >; - - 'template-heading': React.DetailedHTMLProps< - React.HTMLAttributes & { - level?: 1 | 2 | 3 | 4 | 5 | 6; - }, - HTMLElement - >; - - 'template-link': React.DetailedHTMLProps< - React.HTMLAttributes & { - href?: string; - target?: '_blank' | '_self' | '_parent' | '_top'; - rel?: string; - }, - HTMLElement - >; - - 'template-link-list': React.DetailedHTMLProps< - React.HTMLAttributes & { - heading?: string; - }, - HTMLElement - >; - - 'template-page-header': React.DetailedHTMLProps, HTMLElement>; - - 'template-paragraph': React.DetailedHTMLProps, HTMLElement>; - - 'template-side-nav': React.DetailedHTMLProps< - React.HTMLAttributes & { - items?: string; - }, - HTMLElement - >; - - 'template-skip-link': React.DetailedHTMLProps< - React.HTMLAttributes & { - href?: string; - }, - HTMLElement - >; - } -} - -export {}; diff --git a/packages/theme-wizard-templates/src/layouts/BaseLayout.astro b/packages/theme-wizard-templates/src/layouts/BaseLayout.astro index 9e536f9a3..08442e78e 100644 --- a/packages/theme-wizard-templates/src/layouts/BaseLayout.astro +++ b/packages/theme-wizard-templates/src/layouts/BaseLayout.astro @@ -31,7 +31,7 @@ const { diff --git a/packages/theme-wizard-templates/src/layouts/TemplateLayout.tsx b/packages/theme-wizard-templates/src/layouts/TemplateLayout.tsx index a8bb70d83..0b10000ea 100644 --- a/packages/theme-wizard-templates/src/layouts/TemplateLayout.tsx +++ b/packages/theme-wizard-templates/src/layouts/TemplateLayout.tsx @@ -15,13 +15,13 @@ export interface TemplateLayoutProps extends NavigationProps { * It includes the SkipLink, Header, Navigation (with Search pattern), Footer, and PageBody wrapper. */ export const TemplateLayout = ({ + breadcrumb, children, currentPath, - breadcrumb, - searchQuery, + mainId = 'main', onSearchQueryChange, onSearchSubmit, - mainId = 'main', + searchQuery, }: TemplateLayoutProps) => { return ( <> diff --git a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/page.astro b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/page.astro index 9e64ec1e0..e5189ac3a 100644 --- a/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/page.astro +++ b/packages/theme-wizard-templates/src/pages/gemeentevoorbeeld/page.astro @@ -17,5 +17,5 @@ const openingHoursSummary = openingHoursData.summary; -