From 730affb58fcb8a1b52dedfb45d5693c6f1b6138e Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 29 Nov 2024 16:37:12 +0100 Subject: [PATCH 01/59] Setup initial component --- .../css/src/components/header/header.scss | 151 +++++++++------ packages/react/src/Header/Header.tsx | 102 ++++++++-- .../src/components/ams/header.tokens.json | 8 +- .../src/components/Header/Header.stories.tsx | 178 ++++++++++++------ 4 files changed, 303 insertions(+), 136 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 1014dbb3a3..9d74489713 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -6,81 +6,126 @@ @import "../../common/breakpoint"; .ams-header { + column-gap: var(--ams-header-column-gap); + display: grid; + row-gap: var(--ams-header-row-gap); +} + +.ams-header__brand-section { align-items: center; + align-self: start; + column-gap: var(--ams-header-section-column-gap); // TODO display: flex; - flex-wrap: wrap; - padding-block: var(--ams-header-padding-block); - row-gap: 1.5rem; - - @media screen and (min-width: $ams-breakpoint-wide) { - column-gap: var(--ams-header-column-gap); - flex-wrap: nowrap; - } + grid-column: 1; + grid-row: 1; + padding-inline-start: var(--ams-grid-padding-inline); } .ams-header__logo-link { - flex: none; outline-offset: var(--ams-header-logo-link-outline-offset); } -.ams-header__links { - display: none; - - @media screen and (min-width: $ams-breakpoint-medium) { - display: block; - flex: 10 0 auto; - } +.ams-header__navigation { + column-gap: var(--ams-header-column-gap); + display: flex; + flex-wrap: wrap; + grid-column: 1; + grid-row: 1; + inset-block-start: 0; + pointer-events: none; + row-gap: var(--ams-header-row-gap); +} - @media screen and (min-width: $ams-breakpoint-wide) { - order: 3; - } +@mixin reset-list { + margin-block: 0; + padding-inline-start: 0; } .ams-header__menu { - flex: 1; - padding-inline-start: var(--ams-page-menu-column-gap); // TODO Don’t use tokens of another component - text-align: end; + align-items: center; + box-sizing: border-box; + column-gap: var(--ams-page-menu-column-gap); + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-end; + list-style: none; + margin-inline-start: auto; + padding-inline-end: var(--ams-grid-padding-inline); + pointer-events: auto; + row-gap: var(--ams-page-menu-row-gap); - @media screen and (min-width: $ams-breakpoint-wide) { - order: 4; - padding-inline-start: 0; - } + @include reset-list; } -.ams-header__app-name { - flex: 1 1 100%; - - @media screen and (min-width: $ams-breakpoint-wide) { - min-inline-size: 0; - order: 2; - - .ams-header__app-name-heading { - display: block; - inline-size: 100%; - line-height: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } +.ams-header__menu-item--secondary { + @media screen and (not (min-width: $ams-breakpoint-wide)) { + display: none; } } -// Temporary – will move to Mega Menu and/or Icon Button -.ams-header__menu-button { - background-color: transparent; - background-image: url("data:image/svg+xml;utf8,"); - background-position: center right; - background-repeat: no-repeat; - background-size: 1.1875rem 1.1875rem; - border: 0; +// TODO: tokens kopieren +@mixin shared-styles { color: var(--ams-page-menu-item-color); font-family: var(--ams-page-menu-item-font-family); font-size: var(--ams-page-menu-item-font-size); font-weight: var(--ams-page-menu-item-font-weight); line-height: var(--ams-page-menu-item-line-height); - margin-block: 0; - margin-inline: 0; - padding-inline: 0 1.875rem; - text-align: center; + outline-offset: var(--ams-page-menu-item-outline-offset); touch-action: manipulation; + white-space: nowrap; + + @include text-rendering; + + &:hover { + color: var(--ams-page-menu-item-hover-color); + } +} + +// TODO: tokens kopieren +.ams-header__menu-link { + text-decoration-line: var(--ams-page-menu-item-text-decoration-line); + text-decoration-thickness: var(--ams-page-menu-item-text-decoration-thickness); + text-underline-offset: var(--ams-page-menu-item-text-underline-offset); + + @include shared-styles; + + &:hover { + text-decoration-line: var(--ams-page-menu-item-hover-text-decoration-line); + } +} + +.ams-header__mega-menu-button-item:not(.ams-header__mega-menu-button-item--always-show) { + @media screen and (min-width: $ams-breakpoint-wide) { + display: none; + } +} + +@mixin reset-button { + background: none; + border: 0; + margin-block: 0; // [1] + margin-inline: 0; // [1] + padding-block: 0; + padding-inline: 0; + + // [1] Remove the margin in older Safari. +} + +.ams-header__mega-menu-button { + column-gap: var(--ams-page-menu-item-column-gap); + cursor: pointer; + display: inline-flex; + + @include shared-styles; + @include reset-button; +} + +.ams-header__mega-menu { + inline-size: 100%; + pointer-events: auto; +} + +.ams-header__mega-menu--closed.ams-header__mega-menu--closed { + display: none; } diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 907c46b57b..4704dd1143 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -3,14 +3,30 @@ * Copyright Gemeente Amsterdam */ +import { MenuIcon } from '@amsterdam/design-system-react-icons' import clsx from 'clsx' -import { forwardRef } from 'react' -import type { ForwardedRef, HTMLAttributes, ReactNode } from 'react' +import { forwardRef, useState } from 'react' +import type { AnchorHTMLAttributes, ForwardedRef, HTMLAttributes, PropsWithChildren, ReactNode } from 'react' +import { Grid } from '../Grid' import { Heading } from '../Heading' +import { Icon } from '../Icon' import { Logo } from '../Logo' import type { LogoBrand } from '../Logo' +type HeaderMenuLinkProps = { + secondary?: boolean +} & PropsWithChildren> + +const HeaderMenuLink = ({ children, secondary, ...restProps }: HeaderMenuLinkProps) => ( +
  • + + {children} + +
  • +) + export type HeaderProps = { + alwaysShowMenuButton?: boolean /** The name of the application. */ appName?: string /** The list of menu links. Use a Page Menu here. */ @@ -23,42 +39,94 @@ export type HeaderProps = { logoLinkTitle?: string /** A button to toggle the visibility of a Mega Menu. */ menu?: ReactNode + menuButtonText?: string + navigationLabel?: string } & HTMLAttributes -export const Header = forwardRef( +// TODO: change state als je over grens media query gaat +// TODO: gov.uk leest elke keer als je menu opent en sluit 'Show / close navigation menu' voor. +// Da's wel handig, alleen als je dat met een vis hidden span doet leest 'ie 'm niet elke keer voor. + +export const HeaderRoot = forwardRef( ( { + alwaysShowMenuButton, appName, className, - links, + children, logoBrand = 'amsterdam', logoLink = '/', logoLinkTitle = 'Ga naar de homepage', menu, + menuButtonText = 'Menu', + navigationLabel = 'Hoofdnavigatie', ...restProps }: HeaderProps, ref: ForwardedRef, ) => { + const [open, setOpen] = useState(false) + return ( - <> -
    - +
    +
    + {logoLinkTitle} - {links &&
    {links}
    } - {menu &&
    {menu}
    } {appName && ( -
    - - {appName} - -
    + + {appName} + )} -
    - + + +
    ) }, ) -Header.displayName = 'Header' +HeaderRoot.displayName = 'Header' + +export const Header = Object.assign(HeaderRoot, { MenuLink: HeaderMenuLink }) diff --git a/proprietary/tokens/src/components/ams/header.tokens.json b/proprietary/tokens/src/components/ams/header.tokens.json index fe4f70d283..40c59a90d0 100644 --- a/proprietary/tokens/src/components/ams/header.tokens.json +++ b/proprietary/tokens/src/components/ams/header.tokens.json @@ -1,11 +1,11 @@ { "ams": { "header": { - "column-gap": { - "value": "{ams.space.grid.md}", - "comment": "Must have the same value as `ams.grid.column-gap`." + "column-gap": { "value": "{ams.space.grid.md}" }, + "row-gap": { "value": "{ams.space.grid.md}" }, + "section": { + "column-gap": { "value": "{ams.space.md}" } }, - "padding-block": { "value": "{ams.space.md}" }, "logo-link": { "outline-offset": { "value": "{ams.focus.outline-offset}" } } diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 8c08a33001..252136b1c6 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -3,9 +3,10 @@ * Copyright Gemeente Amsterdam */ -import { PageMenu } from '@amsterdam/design-system-react' +// import { PageMenu } from '@amsterdam/design-system-react' +import { Grid, Heading, LinkList } from '@amsterdam/design-system-react' import { Header } from '@amsterdam/design-system-react/src' -import { SearchIcon } from '@amsterdam/design-system-react-icons' +// import { SearchIcon } from '@amsterdam/design-system-react-icons' import { Meta, StoryObj } from '@storybook/react' const meta = { @@ -17,76 +18,129 @@ export default meta type Story = StoryObj -export const Default: Story = {} - -export const ForSubBrand: Story = { - args: { - logoBrand: 'ggd-amsterdam', - logoLink: 'https://www.ggd.amsterdam.nl/', - logoLinkTitle: 'Naar de homepage van de GGD Amsterdam', - }, -} - -export const WithAppName: Story = { +export const Default: Story = { args: { + alwaysShowMenuButton: true, appName: 'Aan de Amsterdamse grachten', + children: ( + + Alle onderwerpen + {/* */} + + Afval + Bestuur en organisatie + Bouw- en verkeersprojecten + + {/* */} + + ), + menu: ( + <> + + English + + + Contact + + + ), }, } -export const WithLinks: Story = { +export const Default2: Story = { args: { - links: ( - - Contact - Mijn Amsterdam - - Zoeken - - + appName: 'Aan de Amsterdamse grachten', + children: ( + + {/* */} + + English + Contact + + {/* */} + + ), + menu: ( + <> + + English + + + Contact + + ), }, } -export const WithMenuButton: Story = { - args: { - menu: , - }, -} +// export const ForSubBrand: Story = { +// args: { +// logoBrand: 'ggd-amsterdam', +// logoLink: 'https://www.ggd.amsterdam.nl/', +// logoLinkTitle: 'Naar de homepage van de GGD Amsterdam', +// }, +// } -export const WithLinksAndMenuButton: Story = { - args: { - links: ( - - Contact - Mijn Amsterdam - - Zoeken - - - ), - menu: , - }, -} +// export const WithAppName: Story = { +// args: { +// appName: 'Aan de Amsterdamse grachten', +// }, +// } -export const WithAppNameAndMenuButton: Story = { - args: { - appName: 'Aan de Amsterdamse grachten', - menu: , - }, -} +// export const WithLinks: Story = { +// args: { +// links: ( +// +// Contact +// Mijn Amsterdam +// +// Zoeken +// +// +// ), +// }, +// } -export const WithAppNameLinksAndMenuButton: Story = { - args: { - appName: 'Aan de Amsterdamse grachten', - links: ( - - Contact - Mijn Amsterdam - - Zoeken - - - ), - menu: , - }, -} +// export const WithMenuButton: Story = { +// args: { +// menu: , +// }, +// } + +// export const WithLinksAndMenuButton: Story = { +// args: { +// links: ( +// +// Contact +// Mijn Amsterdam +// +// Zoeken +// +// +// ), +// menu: , +// }, +// } + +// export const WithAppNameAndMenuButton: Story = { +// args: { +// appName: 'Aan de Amsterdamse grachten', +// menu: , +// }, +// } + +// export const WithAppNameLinksAndMenuButton: Story = { +// args: { +// appName: 'Aan de Amsterdamse grachten', +// links: ( +// +// Contact +// Mijn Amsterdam +// +// Zoeken +// +// +// ), +// menu: , +// }, +// } From 1e63ba3352de0a03583eecc84b48ce966f0d790a Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 29 Nov 2024 16:56:16 +0100 Subject: [PATCH 02/59] Add tokens --- .../css/src/components/header/header.scss | 38 +++++++++---------- .../src/components/ams/header.tokens.json | 34 +++++++++++++++-- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 9d74489713..c64e9b7176 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -6,15 +6,13 @@ @import "../../common/breakpoint"; .ams-header { - column-gap: var(--ams-header-column-gap); display: grid; - row-gap: var(--ams-header-row-gap); } .ams-header__brand-section { align-items: center; align-self: start; - column-gap: var(--ams-header-section-column-gap); // TODO + column-gap: var(--ams-header-brand-section-column-gap); display: flex; grid-column: 1; grid-row: 1; @@ -26,14 +24,14 @@ } .ams-header__navigation { - column-gap: var(--ams-header-column-gap); + column-gap: var(--ams-header-navigation-column-gap); display: flex; flex-wrap: wrap; grid-column: 1; grid-row: 1; inset-block-start: 0; pointer-events: none; - row-gap: var(--ams-header-row-gap); + row-gap: var(--ams-header-navigation-row-gap); } @mixin reset-list { @@ -44,7 +42,7 @@ .ams-header__menu { align-items: center; box-sizing: border-box; - column-gap: var(--ams-page-menu-column-gap); + column-gap: var(--ams-header-menu-column-gap); display: flex; flex-direction: row; flex-wrap: wrap; @@ -53,7 +51,7 @@ margin-inline-start: auto; padding-inline-end: var(--ams-grid-padding-inline); pointer-events: auto; - row-gap: var(--ams-page-menu-row-gap); + row-gap: var(--ams-header-menu-row-gap); @include reset-list; } @@ -64,34 +62,32 @@ } } -// TODO: tokens kopieren @mixin shared-styles { - color: var(--ams-page-menu-item-color); - font-family: var(--ams-page-menu-item-font-family); - font-size: var(--ams-page-menu-item-font-size); - font-weight: var(--ams-page-menu-item-font-weight); - line-height: var(--ams-page-menu-item-line-height); - outline-offset: var(--ams-page-menu-item-outline-offset); + color: var(--ams-header-menu-item-color); + font-family: var(--ams-header-menu-item-font-family); + font-size: var(--ams-header-menu-item-font-size); + font-weight: var(--ams-header-menu-item-font-weight); + line-height: var(--ams-header-menu-item-line-height); + outline-offset: var(--ams-header-menu-item-outline-offset); touch-action: manipulation; white-space: nowrap; @include text-rendering; &:hover { - color: var(--ams-page-menu-item-hover-color); + color: var(--ams-header-menu-item-hover-color); } } -// TODO: tokens kopieren .ams-header__menu-link { - text-decoration-line: var(--ams-page-menu-item-text-decoration-line); - text-decoration-thickness: var(--ams-page-menu-item-text-decoration-thickness); - text-underline-offset: var(--ams-page-menu-item-text-underline-offset); + text-decoration-line: var(--ams-header-menu-link-text-decoration-line); + text-decoration-thickness: var(--ams-header-menu-link-text-decoration-thickness); + text-underline-offset: var(--ams-header-menu-link-text-underline-offset); @include shared-styles; &:hover { - text-decoration-line: var(--ams-page-menu-item-hover-text-decoration-line); + text-decoration-line: var(--ams-header-menu-link-hover-text-decoration-line); } } @@ -113,7 +109,7 @@ } .ams-header__mega-menu-button { - column-gap: var(--ams-page-menu-item-column-gap); + column-gap: var(--ams-header-menu-item-column-gap); cursor: pointer; display: inline-flex; diff --git a/proprietary/tokens/src/components/ams/header.tokens.json b/proprietary/tokens/src/components/ams/header.tokens.json index 40c59a90d0..ee283e83d4 100644 --- a/proprietary/tokens/src/components/ams/header.tokens.json +++ b/proprietary/tokens/src/components/ams/header.tokens.json @@ -1,13 +1,39 @@ { "ams": { "header": { - "column-gap": { "value": "{ams.space.grid.md}" }, - "row-gap": { "value": "{ams.space.grid.md}" }, - "section": { - "column-gap": { "value": "{ams.space.md}" } + "brand-section": { + "column-gap": { "value": "{ams.space.md}" }, + "row-gap": { "value": "{ams.space.grid.xs}" } }, "logo-link": { "outline-offset": { "value": "{ams.focus.outline-offset}" } + }, + "menu": { + "column-gap": { "value": "{ams.space.grid.md}" }, + "item": { + "color": { "value": "{ams.link-appearance.color}" }, + "column-gap": { "value": "{ams.space.xs}" }, + "font-family": { "value": "{ams.text.font-family}" }, + "font-size": { "value": "{ams.text.level.6.font-size}" }, + "font-weight": { "value": "{ams.text.font-weight.normal}" }, + "line-height": { "value": "{ams.text.level.6.line-height}" }, + "outline-offset": { "value": "{ams.focus.outline-offset}" }, + "hover": { + "color": { "value": "{ams.link-appearance.hover.color}" } + } + }, + "link": { + "text-decoration-line": { "value": "{ams.link-appearance.subtle.text-decoration-line}" }, + "text-decoration-thickness": { "value": "{ams.link-appearance.text-decoration-thickness}" }, + "text-underline-offset": { "value": "{ams.link-appearance.text-underline-offset}" }, + "hover": { + "text-decoration-line": { "value": "{ams.link-appearance.subtle.hover.text-decoration-line}" } + } + } + }, + "navigation": { + "column-gap": { "value": "{ams.space.grid.md}" }, + "row-gap": { "value": "{ams.space.grid.md}" } } } } From 17a747942dedd8ae599b3aea142c918af3edd427 Mon Sep 17 00:00:00 2001 From: alimpens Date: Sat, 30 Nov 2024 15:09:00 +0100 Subject: [PATCH 03/59] Use grid-area shorthand --- packages/css/src/components/header/header.scss | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index c64e9b7176..c68d08fb61 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -14,8 +14,7 @@ align-self: start; column-gap: var(--ams-header-brand-section-column-gap); display: flex; - grid-column: 1; - grid-row: 1; + grid-area: 1 / 1; padding-inline-start: var(--ams-grid-padding-inline); } @@ -27,8 +26,7 @@ column-gap: var(--ams-header-navigation-column-gap); display: flex; flex-wrap: wrap; - grid-column: 1; - grid-row: 1; + grid-area: 1 / 1; inset-block-start: 0; pointer-events: none; row-gap: var(--ams-header-navigation-row-gap); From 98832e0982a70654683dc2ee1d71ff5be5beadbc Mon Sep 17 00:00:00 2001 From: alimpens Date: Sun, 1 Dec 2024 13:16:28 +0100 Subject: [PATCH 04/59] Move padding to header instead of grid --- packages/css/src/components/header/header.scss | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index c68d08fb61..9cbc72c397 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -7,6 +7,7 @@ .ams-header { display: grid; + padding-inline: var(--ams-grid-padding-inline); } .ams-header__brand-section { @@ -15,7 +16,6 @@ column-gap: var(--ams-header-brand-section-column-gap); display: flex; grid-area: 1 / 1; - padding-inline-start: var(--ams-grid-padding-inline); } .ams-header__logo-link { @@ -47,7 +47,6 @@ justify-content: flex-end; list-style: none; margin-inline-start: auto; - padding-inline-end: var(--ams-grid-padding-inline); pointer-events: auto; row-gap: var(--ams-header-menu-row-gap); @@ -120,6 +119,11 @@ pointer-events: auto; } +// Reset the inline padding of the grid here, and set on the root header element instead. +.ams-header__mega-menu.ams-header__mega-menu { + padding-inline: 0; +} + .ams-header__mega-menu--closed.ams-header__mega-menu--closed { display: none; } From 7fdceff07ef85a34209961a14ee9c24b3609d490 Mon Sep 17 00:00:00 2001 From: alimpens Date: Sun, 1 Dec 2024 18:02:56 +0100 Subject: [PATCH 05/59] Make button bold on open without layout shift --- .../css/src/components/header/header.scss | 21 ++++++++++++++++++- packages/react/src/Header/Header.tsx | 12 ++++++++++- .../src/components/ams/header.tokens.json | 9 ++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 9cbc72c397..649ed7ab88 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -108,12 +108,31 @@ .ams-header__mega-menu-button { column-gap: var(--ams-header-menu-item-column-gap); cursor: pointer; - display: inline-flex; + display: grid; + grid-auto-flow: column; @include shared-styles; @include reset-button; } +.ams-header__mega-menu-button-label { + grid-area: 1 / 1; +} + +.ams-header__mega-menu-button-label--open { + font-weight: var(--ams-header-mega-menu-button-label-open-font-weight); +} + +// This hidden label is used to prevent a layout shift when the mega menu is opened +// and the button text becomes bold. +.ams-header__mega-menu-button-hidden-label { + font-weight: var(--ams-header-mega-menu-button-label-open-font-weight); + grid-area: 1 / 1; + pointer-events: none; + user-select: none; + visibility: hidden; +} + .ams-header__mega-menu { inline-size: 100%; pointer-events: auto; diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 4704dd1143..fee41684fa 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -108,7 +108,17 @@ export const HeaderRoot = forwardRef( onClick={() => setOpen(!open)} type="button" > - {menuButtonText} + + {menuButtonText} + + diff --git a/proprietary/tokens/src/components/ams/header.tokens.json b/proprietary/tokens/src/components/ams/header.tokens.json index ee283e83d4..3b2047f9e0 100644 --- a/proprietary/tokens/src/components/ams/header.tokens.json +++ b/proprietary/tokens/src/components/ams/header.tokens.json @@ -8,6 +8,15 @@ "logo-link": { "outline-offset": { "value": "{ams.focus.outline-offset}" } }, + "mega-menu": { + "button": { + "label": { + "open": { + "font-weight": { "value": "{ams.text.font-weight.bold}" } + } + } + } + }, "menu": { "column-gap": { "value": "{ams.space.grid.md}" }, "item": { From d3f325931fa438b74e80ebeec7069166cdd7029a Mon Sep 17 00:00:00 2001 From: alimpens Date: Sun, 1 Dec 2024 21:17:50 +0100 Subject: [PATCH 06/59] Add animated menu icon --- .../css/src/components/header/header.scss | 38 +++++++++++++++++++ packages/react/src/Header/Header.tsx | 9 ++++- packages/react/src/Header/HeaderMenuIcon.tsx | 9 +++++ packages/react/src/Icon/Icon.tsx | 6 +-- 4 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 packages/react/src/Header/HeaderMenuIcon.tsx diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 649ed7ab88..b2e4e58d4d 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -133,6 +133,44 @@ visibility: hidden; } +.ams-header__menu-icon { + line { + stroke: currentColor; + stroke-width: 3px; + transform-origin: center; + transition: + translate 0.1s ease-in-out, + rotate 0.2s ease-in-out, + opacity 0.1s ease-in-out; + + @media (prefers-reduced-motion) { + transition: none; + } + } + + .ams-header__menu-icon-top { + translate: 0 -7px; + } + + .ams-header__menu-icon-bottom { + translate: 0 7px; + } +} + +.ams-header__menu-icon--open { + .ams-header__menu-icon-top { + rotate: 45deg; + translate: 0; + } + .ams-header__menu-icon-middle { + opacity: 0%; + } + .ams-header__menu-icon-bottom { + rotate: -45deg; + translate: 0; + } +} + .ams-header__mega-menu { inline-size: 100%; pointer-events: auto; diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index fee41684fa..5313485490 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -3,7 +3,6 @@ * Copyright Gemeente Amsterdam */ -import { MenuIcon } from '@amsterdam/design-system-react-icons' import clsx from 'clsx' import { forwardRef, useState } from 'react' import type { AnchorHTMLAttributes, ForwardedRef, HTMLAttributes, PropsWithChildren, ReactNode } from 'react' @@ -12,6 +11,7 @@ import { Heading } from '../Heading' import { Icon } from '../Icon' import { Logo } from '../Logo' import type { LogoBrand } from '../Logo' +import { HeaderMenuIcon } from './HeaderMenuIcon' type HeaderMenuLinkProps = { secondary?: boolean @@ -119,7 +119,12 @@ export const HeaderRoot = forwardRef( - + + } + size="level-6" + /> diff --git a/packages/react/src/Header/HeaderMenuIcon.tsx b/packages/react/src/Header/HeaderMenuIcon.tsx new file mode 100644 index 0000000000..3de326c087 --- /dev/null +++ b/packages/react/src/Header/HeaderMenuIcon.tsx @@ -0,0 +1,9 @@ +import type { SVGProps } from 'react' + +export const HeaderMenuIcon = (props: SVGProps) => ( + +) diff --git a/packages/react/src/Icon/Icon.tsx b/packages/react/src/Icon/Icon.tsx index e741e31c9a..158ea0c295 100644 --- a/packages/react/src/Icon/Icon.tsx +++ b/packages/react/src/Icon/Icon.tsx @@ -7,7 +7,7 @@ import clsx from 'clsx' import { forwardRef } from 'react' -import type { ForwardedRef, HTMLAttributes } from 'react' +import type { ForwardedRef, HTMLAttributes, ReactNode } from 'react' export type IconProps = { /** The size of the icon. Corresponds with the text levels. */ @@ -15,7 +15,7 @@ export type IconProps = { /** Whether the icon container should be made square. */ square?: boolean /** The component rendering the icon’s markup. */ - svg: Function + svg: Function | ReactNode } & HTMLAttributes export const Icon = forwardRef( @@ -33,7 +33,7 @@ export const Icon = forwardRef( )} {...restProps} > - {svg()} + {typeof svg === 'function' ? svg() : svg} ), ) From 46b5702cf34048d42b9e6768b86270dc82024127 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 4 Dec 2024 09:58:18 +0100 Subject: [PATCH 07/59] Move subcomponent to separate file --- packages/react/src/Header/Header.tsx | 17 +++-------------- packages/react/src/Header/HeaderMenuLink.tsx | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 packages/react/src/Header/HeaderMenuLink.tsx diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 5313485490..513113cd9e 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -5,25 +5,14 @@ import clsx from 'clsx' import { forwardRef, useState } from 'react' -import type { AnchorHTMLAttributes, ForwardedRef, HTMLAttributes, PropsWithChildren, ReactNode } from 'react' +import type { ForwardedRef, HTMLAttributes, ReactNode } from 'react' import { Grid } from '../Grid' import { Heading } from '../Heading' import { Icon } from '../Icon' import { Logo } from '../Logo' import type { LogoBrand } from '../Logo' import { HeaderMenuIcon } from './HeaderMenuIcon' - -type HeaderMenuLinkProps = { - secondary?: boolean -} & PropsWithChildren> - -const HeaderMenuLink = ({ children, secondary, ...restProps }: HeaderMenuLinkProps) => ( -
  • - - {children} - -
  • -) +import { HeaderMenuLink } from './HeaderMenuLink' export type HeaderProps = { alwaysShowMenuButton?: boolean @@ -47,7 +36,7 @@ export type HeaderProps = { // TODO: gov.uk leest elke keer als je menu opent en sluit 'Show / close navigation menu' voor. // Da's wel handig, alleen als je dat met een vis hidden span doet leest 'ie 'm niet elke keer voor. -export const HeaderRoot = forwardRef( +const HeaderRoot = forwardRef( ( { alwaysShowMenuButton, diff --git a/packages/react/src/Header/HeaderMenuLink.tsx b/packages/react/src/Header/HeaderMenuLink.tsx new file mode 100644 index 0000000000..e6e50220b1 --- /dev/null +++ b/packages/react/src/Header/HeaderMenuLink.tsx @@ -0,0 +1,16 @@ +import clsx from 'clsx' +import { AnchorHTMLAttributes, forwardRef, PropsWithChildren } from 'react' + +type HeaderMenuLinkProps = { + secondary?: boolean +} & PropsWithChildren> + +export const HeaderMenuLink = forwardRef(({ children, secondary, ...restProps }: HeaderMenuLinkProps) => ( +
  • + + {children} + +
  • +)) + +HeaderMenuLink.displayName = 'Header.MenuLink' From 600f87aa466b2599ac21532ad2c007574e60b3c0 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 4 Dec 2024 10:23:28 +0100 Subject: [PATCH 08/59] Update and document header props --- packages/react/src/Header/Header.tsx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 513113cd9e..750dafe8ac 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -15,27 +15,24 @@ import { HeaderMenuIcon } from './HeaderMenuIcon' import { HeaderMenuLink } from './HeaderMenuLink' export type HeaderProps = { + /** Whether the menu button is always shown. By default, it only shows on narrow screens. */ alwaysShowMenuButton?: boolean /** The name of the application. */ appName?: string - /** The list of menu links. Use a Page Menu here. */ - links?: ReactNode /** The name of the brand for which to display the logo. */ logoBrand?: LogoBrand /** The url for the link on the logo. */ logoLink?: string /** The accessible text for the link on the logo. */ logoLinkTitle?: string - /** A button to toggle the visibility of a Mega Menu. */ + /** A slot for the menu. Use Header.MenuLink here. */ menu?: ReactNode + /** The text for the menu button. */ menuButtonText?: string + /** The accessible label for the navigation section. */ navigationLabel?: string } & HTMLAttributes -// TODO: change state als je over grens media query gaat -// TODO: gov.uk leest elke keer als je menu opent en sluit 'Show / close navigation menu' voor. -// Da's wel handig, alleen als je dat met een vis hidden span doet leest 'ie 'm niet elke keer voor. - const HeaderRoot = forwardRef( ( { From 51cddb155b6146b6b94c953b47fee8fcd4e1d850 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 4 Dec 2024 10:28:07 +0100 Subject: [PATCH 09/59] Fix typo --- packages/css/src/components/header/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/css/src/components/header/README.md b/packages/css/src/components/header/README.md index 78e1e0f28d..380a585d2a 100644 --- a/packages/css/src/components/header/README.md +++ b/packages/css/src/components/header/README.md @@ -19,5 +19,5 @@ Includes the name of the application if it is not the general website. ## References -- A Header is a [landmark](https://www.w3.org/TR/wai-aria-practices-1.1/#aria_landmark_roles) and can be use to group navigation elements. +- A Header is a [landmark](https://www.w3.org/TR/wai-aria-practices-1.1/#aria_landmark_roles) and can be used to group navigation elements. - [WCAG 3.2.3](https://wcag.com/designers/3-2-3-consistent-navigation/) Consistent Navigation: Navigation menus that appear on multiple pages are consistent. From bfc6d515195492814f46e7a02543608609a32ee3 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 4 Dec 2024 14:04:09 +0100 Subject: [PATCH 10/59] Add stories --- .../src/components/Header/Header.docs.mdx | 31 +-- .../src/components/Header/Header.stories.tsx | 194 +++++++++--------- 2 files changed, 101 insertions(+), 124 deletions(-) diff --git a/storybook/src/components/Header/Header.docs.mdx b/storybook/src/components/Header/Header.docs.mdx index a6aa627823..41d18c4ba4 100644 --- a/storybook/src/components/Header/Header.docs.mdx +++ b/storybook/src/components/Header/Header.docs.mdx @@ -17,33 +17,14 @@ import { StatusBadge } from "../../docs/components/StatusBadge"; ## Examples -### For a sub-brand +### With always visible menu button - + -### With app name +### With custom logo link - + -### With menu button +### With custom texts - - -### With links - -Use a [Page Menu](/docs/components-navigation-page-menu--docs) to add links. -A Page Menu in the Header should not wrap. - - - -### With links and menu button - - - -### With app name and menu button - - - -### With app name, links and menu button - - + diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 252136b1c6..2e5bb536ad 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -3,10 +3,8 @@ * Copyright Gemeente Amsterdam */ -// import { PageMenu } from '@amsterdam/design-system-react' import { Grid, Heading, LinkList } from '@amsterdam/design-system-react' import { Header } from '@amsterdam/design-system-react/src' -// import { SearchIcon } from '@amsterdam/design-system-react-icons' import { Meta, StoryObj } from '@storybook/react' const meta = { @@ -18,129 +16,127 @@ export default meta type Story = StoryObj +const defaultStoryLinks = [ + { label: 'Stad', href: '#' }, + { label: 'Techniek', href: '#' }, + { label: 'Historie', href: '#' }, + { label: 'Duurzaamheid', href: '#' }, + { label: 'Zoeken', href: '#' }, +] + export const Default: Story = { args: { - alwaysShowMenuButton: true, appName: 'Aan de Amsterdamse grachten', children: ( - Alle onderwerpen - {/* */} + + Alle onderwerpen + - Afval - Bestuur en organisatie - Bouw- en verkeersprojecten + {defaultStoryLinks.map(({ label, href }) => ( + + {label} + + ))} - {/* */} ), menu: ( <> - - English - - - Contact - + {defaultStoryLinks.map(({ label, href }) => ( + + {label} + + ))} ), }, } -export const Default2: Story = { +const alwaysShowMenuButtonStoryLinks = [ + [ + { label: 'Kaart', href: '#' }, + { label: 'Panoramabeelden', href: '#' }, + { label: 'Tabellen', href: '#' }, + { label: 'Catalogus (Beta)', href: '#' }, + { label: 'Kaarten', href: '#' }, + { label: 'Datacatalogus', href: '#' }, + ], + [ + { label: 'Over de organisatie', href: '#' }, + { label: 'Over het dataplatform', href: '#' }, + ], + [ + { label: 'Help', href: '#' }, + { label: 'Contact', href: '#' }, + ], +] + +export const WithAlwaysVisibleMenuButton: Story = { args: { - appName: 'Aan de Amsterdamse grachten', - children: ( - - {/* */} + alwaysShowMenuButton: true, + appName: 'Data Amsterdam', + children: [ + + {/* TODO: link die alleen verschijnt op kleine schermen */} + + Onderdelen + - English - Contact + {alwaysShowMenuButtonStoryLinks[0].map(({ label, href }) => ( + + {label} + + ))} - {/* */} - - ), + , + + + Over ons + + + {alwaysShowMenuButtonStoryLinks[1].map(({ label, href }) => ( + + {label} + + ))} + + , + + + Help + + + {alwaysShowMenuButtonStoryLinks[2].map(({ label, href }) => ( + + {label} + + ))} + + , + ], menu: ( <> - - English - - Contact + Inloggen ), }, } -// export const ForSubBrand: Story = { -// args: { -// logoBrand: 'ggd-amsterdam', -// logoLink: 'https://www.ggd.amsterdam.nl/', -// logoLinkTitle: 'Naar de homepage van de GGD Amsterdam', -// }, -// } - -// export const WithAppName: Story = { -// args: { -// appName: 'Aan de Amsterdamse grachten', -// }, -// } - -// export const WithLinks: Story = { -// args: { -// links: ( -// -// Contact -// Mijn Amsterdam -// -// Zoeken -// -// -// ), -// }, -// } - -// export const WithMenuButton: Story = { -// args: { -// menu: , -// }, -// } - -// export const WithLinksAndMenuButton: Story = { -// args: { -// links: ( -// -// Contact -// Mijn Amsterdam -// -// Zoeken -// -// -// ), -// menu: , -// }, -// } - -// export const WithAppNameAndMenuButton: Story = { -// args: { -// appName: 'Aan de Amsterdamse grachten', -// menu: , -// }, -// } +export const WithCustomLogoLink: Story = { + args: { + logoBrand: 'ggd-amsterdam', + logoLink: 'https://www.ggd.amsterdam.nl/', + logoLinkTitle: 'Naar de homepage van de GGD Amsterdam', + }, +} -// export const WithAppNameLinksAndMenuButton: Story = { -// args: { -// appName: 'Aan de Amsterdamse grachten', -// links: ( -// -// Contact -// Mijn Amsterdam -// -// Zoeken -// -// -// ), -// menu: , -// }, -// } +export const WithCustomTexts: Story = { + args: { + alwaysShowMenuButton: true, + menuButtonText: 'Hoofdmenu', + navigationLabel: 'Navigatie', + }, +} From 2bbee6283078b08d24e7ca7afc1b6c63e0d39b50 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 4 Dec 2024 14:10:02 +0100 Subject: [PATCH 11/59] Add ref, temp disable tests --- packages/react/src/Header/Header.test.tsx | 94 ++++++++++---------- packages/react/src/Header/HeaderMenuLink.tsx | 18 ++-- 2 files changed, 57 insertions(+), 55 deletions(-) diff --git a/packages/react/src/Header/Header.test.tsx b/packages/react/src/Header/Header.test.tsx index 663342a1ec..ddeaa64bec 100644 --- a/packages/react/src/Header/Header.test.tsx +++ b/packages/react/src/Header/Header.test.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import { createRef } from 'react' +// import { createRef } from 'react' import { Header } from './Header' import '@testing-library/jest-dom' @@ -13,74 +13,74 @@ describe('Header', () => { expect(component).toBeVisible() }) - it('renders a design system BEM class name', () => { - render(
    ) + // it('renders a design system BEM class name', () => { + // render(
    ) - const component = screen.getByRole('banner') + // const component = screen.getByRole('banner') - expect(component).toHaveClass('ams-header') - }) + // expect(component).toHaveClass('ams-header') + // }) - it('renders an additional class name', () => { - render(
    ) + // it('renders an additional class name', () => { + // render(
    ) - const component = screen.getByRole('banner') + // const component = screen.getByRole('banner') - expect(component).toHaveClass('extra') - expect(component).toHaveClass('ams-header') - }) + // expect(component).toHaveClass('extra') + // expect(component).toHaveClass('ams-header') + // }) - it('supports ForwardRef in React', () => { - const ref = createRef() + // it('supports ForwardRef in React', () => { + // const ref = createRef() - render(
    ) + // render(
    ) - const component = screen.getByRole('banner') + // const component = screen.getByRole('banner') - expect(ref.current).toBe(component) - }) + // expect(ref.current).toBe(component) + // }) - it('renders with a logo link', () => { - render(
    ) + // it('renders with a logo link', () => { + // render(
    ) - const logoLink = screen.getByRole('link') + // const logoLink = screen.getByRole('link') - expect(logoLink).toHaveAttribute('href', '/home') - }) + // expect(logoLink).toHaveAttribute('href', '/home') + // }) - it('renders with a logo link title', () => { - render(
    ) + // it('renders with a logo link title', () => { + // render(
    ) - const logoLinkTitle = screen.getByRole('link', { name: 'Go to homepage' }) + // const logoLinkTitle = screen.getByRole('link', { name: 'Go to homepage' }) - expect(logoLinkTitle).toHaveTextContent('Go to homepage') - }) + // expect(logoLinkTitle).toHaveTextContent('Go to homepage') + // }) - it('renders an application name', () => { - render(
    ) + // it('renders an application name', () => { + // render(
    ) - const heading = screen.getByRole('heading', { - name: 'Application name', - level: 1, - }) + // const heading = screen.getByRole('heading', { + // name: 'Application name', + // level: 1, + // }) - expect(heading).toBeInTheDocument() - }) + // expect(heading).toBeInTheDocument() + // }) - it('renders with links', () => { - const { container } = render(
    Test content} />) + // it('renders with links', () => { + // const { container } = render(
    Test content} />) - const menu = container.querySelector('.ams-header__links') + // const menu = container.querySelector('.ams-header__links') - expect(menu).toBeInTheDocument() - expect(menu).toHaveTextContent('Test content') - }) + // expect(menu).toBeInTheDocument() + // expect(menu).toHaveTextContent('Test content') + // }) - it('renders with menu button', () => { - render(
    Menu Button} />) + // it('renders with menu button', () => { + // render(
    Menu Button} />) - const menu = screen.getByRole('button') + // const menu = screen.getByRole('button') - expect(menu).toBeInTheDocument() - }) + // expect(menu).toBeInTheDocument() + // }) }) diff --git a/packages/react/src/Header/HeaderMenuLink.tsx b/packages/react/src/Header/HeaderMenuLink.tsx index e6e50220b1..bec221f5aa 100644 --- a/packages/react/src/Header/HeaderMenuLink.tsx +++ b/packages/react/src/Header/HeaderMenuLink.tsx @@ -1,16 +1,18 @@ import clsx from 'clsx' -import { AnchorHTMLAttributes, forwardRef, PropsWithChildren } from 'react' +import { AnchorHTMLAttributes, ForwardedRef, forwardRef, PropsWithChildren } from 'react' type HeaderMenuLinkProps = { secondary?: boolean } & PropsWithChildren> -export const HeaderMenuLink = forwardRef(({ children, secondary, ...restProps }: HeaderMenuLinkProps) => ( -
  • - - {children} - -
  • -)) +export const HeaderMenuLink = forwardRef( + ({ children, secondary, ...restProps }: HeaderMenuLinkProps, ref: ForwardedRef) => ( +
  • + + {children} + +
  • + ), +) HeaderMenuLink.displayName = 'Header.MenuLink' From fe0581612bae96b092d7742807a543a3669f1c36 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 4 Dec 2024 14:50:02 +0100 Subject: [PATCH 12/59] Close the menu when the menu button disappears --- packages/react/src/Header/Header.tsx | 12 ++++++- packages/react/src/common/useMediaQuery.tsx | 36 +++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 packages/react/src/common/useMediaQuery.tsx diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 750dafe8ac..7fb92c5da1 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -4,7 +4,7 @@ */ import clsx from 'clsx' -import { forwardRef, useState } from 'react' +import { forwardRef, useEffect, useState } from 'react' import type { ForwardedRef, HTMLAttributes, ReactNode } from 'react' import { Grid } from '../Grid' import { Heading } from '../Heading' @@ -13,6 +13,7 @@ import { Logo } from '../Logo' import type { LogoBrand } from '../Logo' import { HeaderMenuIcon } from './HeaderMenuIcon' import { HeaderMenuLink } from './HeaderMenuLink' +import useMediaQuery from '../common/useMediaQuery' export type HeaderProps = { /** Whether the menu button is always shown. By default, it only shows on narrow screens. */ @@ -52,6 +53,15 @@ const HeaderRoot = forwardRef( ) => { const [open, setOpen] = useState(false) + const isWideScreen = useMediaQuery('wide') + + useEffect(() => { + // Close the menu when the menu button disappears + if (!alwaysShowMenuButton && isWideScreen) { + setOpen(false) + } + }, [isWideScreen]) + return (
    diff --git a/packages/react/src/common/useMediaQuery.tsx b/packages/react/src/common/useMediaQuery.tsx new file mode 100644 index 0000000000..d6cfbca783 --- /dev/null +++ b/packages/react/src/common/useMediaQuery.tsx @@ -0,0 +1,36 @@ +import { useEffect, useState } from 'react' + +// TODO: we should set the breakpoint in JS somewhere and render this and the sass variables from that +const breakpoints = { + medium: '36rem', + wide: '68rem', +} + +type useMediaQueryProps = 'medium' | 'wide' + +const useMediaQuery = (breakpoint: useMediaQueryProps) => { + const [matches, setMatches] = useState(false) + + useEffect(() => { + // Check for window object to avoid SSR issues + if (breakpoint && typeof window !== 'undefined') { + const media = window.matchMedia(`(min-width: ${breakpoints[breakpoint]})`) + + if (media.matches !== matches) { + setMatches(media.matches) + } + + const listener = () => setMatches(media.matches) + + window.addEventListener('resize', listener) + + return () => window.removeEventListener('resize', listener) + } + + return undefined + }, [matches, breakpoint]) + + return matches +} + +export default useMediaQuery From e26ae5e4dbaeef99ee2f1edf2917d03601b1a844 Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 6 Dec 2024 10:32:52 +0100 Subject: [PATCH 13/59] Add showMenuButton enum, only render necessary markup --- .../css/src/components/header/header.scss | 2 +- packages/react/src/Header/Header.tsx | 118 +++++++++--------- .../src/components/Header/Header.stories.tsx | 5 +- 3 files changed, 66 insertions(+), 59 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index b2e4e58d4d..801021523e 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -88,7 +88,7 @@ } } -.ams-header__mega-menu-button-item:not(.ams-header__mega-menu-button-item--always-show) { +.ams-header__mega-menu-button-item:not(.ams-header__mega-menu-button-item--show-always) { @media screen and (min-width: $ams-breakpoint-wide) { display: none; } diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 7fb92c5da1..14f9bb4dc2 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -16,8 +16,6 @@ import { HeaderMenuLink } from './HeaderMenuLink' import useMediaQuery from '../common/useMediaQuery' export type HeaderProps = { - /** Whether the menu button is always shown. By default, it only shows on narrow screens. */ - alwaysShowMenuButton?: boolean /** The name of the application. */ appName?: string /** The name of the brand for which to display the logo. */ @@ -32,12 +30,14 @@ export type HeaderProps = { menuButtonText?: string /** The accessible label for the navigation section. */ navigationLabel?: string + /** When to show the menu button. The default is show only when the screen is narrow. */ + showMenuButton?: 'always' | 'never' } & HTMLAttributes const HeaderRoot = forwardRef( ( { - alwaysShowMenuButton, + showMenuButton, appName, className, children, @@ -57,7 +57,7 @@ const HeaderRoot = forwardRef( useEffect(() => { // Close the menu when the menu button disappears - if (!alwaysShowMenuButton && isWideScreen) { + if (!showMenuButton && isWideScreen) { setOpen(false) } }, [isWideScreen]) @@ -75,64 +75,70 @@ const HeaderRoot = forwardRef( )}
    - + )}
    ) }, diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 2e5bb536ad..42fd4c4404 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -74,7 +74,6 @@ const alwaysShowMenuButtonStoryLinks = [ export const WithAlwaysVisibleMenuButton: Story = { args: { - alwaysShowMenuButton: true, appName: 'Data Amsterdam', children: [ @@ -122,6 +121,7 @@ export const WithAlwaysVisibleMenuButton: Story = { ), + showMenuButton: 'always', }, } @@ -130,13 +130,14 @@ export const WithCustomLogoLink: Story = { logoBrand: 'ggd-amsterdam', logoLink: 'https://www.ggd.amsterdam.nl/', logoLinkTitle: 'Naar de homepage van de GGD Amsterdam', + showMenuButton: 'never', }, } export const WithCustomTexts: Story = { args: { - alwaysShowMenuButton: true, menuButtonText: 'Hoofdmenu', navigationLabel: 'Navigatie', + showMenuButton: 'always', }, } From 812aff7adb276e1da033267739102e0408d543c9 Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 6 Dec 2024 10:47:04 +0100 Subject: [PATCH 14/59] Add story for header without menu --- storybook/src/components/Header/Header.docs.mdx | 8 ++++++-- storybook/src/components/Header/Header.stories.tsx | 9 ++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/storybook/src/components/Header/Header.docs.mdx b/storybook/src/components/Header/Header.docs.mdx index 41d18c4ba4..a8eb44f92c 100644 --- a/storybook/src/components/Header/Header.docs.mdx +++ b/storybook/src/components/Header/Header.docs.mdx @@ -17,9 +17,13 @@ import { StatusBadge } from "../../docs/components/StatusBadge"; ## Examples -### With always visible menu button +### Always show menu button - + + +### Never show menu button + + ### With custom logo link diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 42fd4c4404..bf16771cb9 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -72,7 +72,7 @@ const alwaysShowMenuButtonStoryLinks = [ ], ] -export const WithAlwaysVisibleMenuButton: Story = { +export const AlwaysShowMenuButton: Story = { args: { appName: 'Data Amsterdam', children: [ @@ -125,6 +125,13 @@ export const WithAlwaysVisibleMenuButton: Story = { }, } +export const NeverShowMenuButton: Story = { + args: { + appName: 'Mijn Amsterdam', + showMenuButton: 'never', + }, +} + export const WithCustomLogoLink: Story = { args: { logoBrand: 'ggd-amsterdam', From b5d1be96cad8e34f599982d705cfa9b0d0e13232 Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 6 Dec 2024 11:36:41 +0100 Subject: [PATCH 15/59] Add secondary link list --- .../css/src/components/header/header.scss | 6 +++ packages/react/src/Header/Header.tsx | 8 +++- packages/react/src/Header/HeaderMenuLink.tsx | 2 +- .../src/Header/HeaderSecondaryLinkList.tsx | 13 ++++++ .../Header/HeaderSecondaryLinkListLink.tsx | 12 ++++++ packages/react/src/Header/index.ts | 1 + .../src/components/Header/Header.stories.tsx | 40 ++++++++++--------- 7 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 packages/react/src/Header/HeaderSecondaryLinkList.tsx create mode 100644 packages/react/src/Header/HeaderSecondaryLinkListLink.tsx diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 801021523e..670620f897 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -184,3 +184,9 @@ .ams-header__mega-menu--closed.ams-header__mega-menu--closed { display: none; } + +.ams-header__secondary-link-list { + @media screen and (min-width: $ams-breakpoint-wide) { + display: none; + } +} diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 14f9bb4dc2..a9469d8a6d 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -13,6 +13,8 @@ import { Logo } from '../Logo' import type { LogoBrand } from '../Logo' import { HeaderMenuIcon } from './HeaderMenuIcon' import { HeaderMenuLink } from './HeaderMenuLink' +import { HeaderSecondaryLinkList } from './HeaderSecondaryLinkList' +import { HeaderSecondaryLinkListLink } from './HeaderSecondaryLinkListLink' import useMediaQuery from '../common/useMediaQuery' export type HeaderProps = { @@ -146,4 +148,8 @@ const HeaderRoot = forwardRef( HeaderRoot.displayName = 'Header' -export const Header = Object.assign(HeaderRoot, { MenuLink: HeaderMenuLink }) +export const Header = Object.assign(HeaderRoot, { + MenuLink: HeaderMenuLink, + SecondaryLinkList: HeaderSecondaryLinkList, + SecondaryLinkListLink: HeaderSecondaryLinkListLink, +}) diff --git a/packages/react/src/Header/HeaderMenuLink.tsx b/packages/react/src/Header/HeaderMenuLink.tsx index bec221f5aa..23bb20aa7e 100644 --- a/packages/react/src/Header/HeaderMenuLink.tsx +++ b/packages/react/src/Header/HeaderMenuLink.tsx @@ -1,7 +1,7 @@ import clsx from 'clsx' import { AnchorHTMLAttributes, ForwardedRef, forwardRef, PropsWithChildren } from 'react' -type HeaderMenuLinkProps = { +export type HeaderMenuLinkProps = { secondary?: boolean } & PropsWithChildren> diff --git a/packages/react/src/Header/HeaderSecondaryLinkList.tsx b/packages/react/src/Header/HeaderSecondaryLinkList.tsx new file mode 100644 index 0000000000..f6021ce272 --- /dev/null +++ b/packages/react/src/Header/HeaderSecondaryLinkList.tsx @@ -0,0 +1,13 @@ +import clsx from 'clsx' +import { ForwardedRef, forwardRef } from 'react' +import { LinkList, LinkListProps } from '../LinkList' + +export const HeaderSecondaryLinkList = forwardRef( + ({ children, className, ...restProps }: LinkListProps, ref: ForwardedRef) => ( + + {children} + + ), +) + +HeaderSecondaryLinkList.displayName = 'Header.SecondaryLinkList' diff --git a/packages/react/src/Header/HeaderSecondaryLinkListLink.tsx b/packages/react/src/Header/HeaderSecondaryLinkListLink.tsx new file mode 100644 index 0000000000..ca4beb7468 --- /dev/null +++ b/packages/react/src/Header/HeaderSecondaryLinkListLink.tsx @@ -0,0 +1,12 @@ +import { ForwardedRef, forwardRef } from 'react' +import { LinkList, LinkListLinkProps } from '../LinkList' + +export const HeaderSecondaryLinkListLink = forwardRef( + ({ children, ...restProps }: LinkListLinkProps, ref: ForwardedRef) => ( + + {children} + + ), +) + +HeaderSecondaryLinkListLink.displayName = 'Header.SecondaryLinkListLink' diff --git a/packages/react/src/Header/index.ts b/packages/react/src/Header/index.ts index acc1f0dbc9..9d50745641 100644 --- a/packages/react/src/Header/index.ts +++ b/packages/react/src/Header/index.ts @@ -1,2 +1,3 @@ export { Header } from './Header' export type { HeaderProps } from './Header' +export type { HeaderMenuLinkProps } from './HeaderMenuLink' diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index bf16771cb9..6ada0e64f4 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -27,6 +27,15 @@ const defaultStoryLinks = [ export const Default: Story = { args: { appName: 'Aan de Amsterdamse grachten', + menu: ( + <> + {defaultStoryLinks.map(({ label, href }) => ( + + {label} + + ))} + + ), children: ( @@ -41,15 +50,6 @@ export const Default: Story = { ), - menu: ( - <> - {defaultStoryLinks.map(({ label, href }) => ( - - {label} - - ))} - - ), }, } @@ -75,9 +75,21 @@ const alwaysShowMenuButtonStoryLinks = [ export const AlwaysShowMenuButton: Story = { args: { appName: 'Data Amsterdam', + menu: ( + <> + + Inloggen + + + ), + showMenuButton: 'always', children: [ + + + Inloggen + + , - {/* TODO: link die alleen verschijnt op kleine schermen */} Onderdelen @@ -114,14 +126,6 @@ export const AlwaysShowMenuButton: Story = { , ], - menu: ( - <> - - Inloggen - - - ), - showMenuButton: 'always', }, } From 32890bc36ee483ca1bcab698ada81c6d5f6e7cd3 Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 6 Dec 2024 12:00:53 +0100 Subject: [PATCH 16/59] Hide cell instead of list, to prevent unwanted row gap --- packages/react/src/Header/HeaderSecondaryLinkList.tsx | 9 ++++++--- storybook/src/components/Header/Header.stories.tsx | 11 +++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/react/src/Header/HeaderSecondaryLinkList.tsx b/packages/react/src/Header/HeaderSecondaryLinkList.tsx index f6021ce272..2e46012f7e 100644 --- a/packages/react/src/Header/HeaderSecondaryLinkList.tsx +++ b/packages/react/src/Header/HeaderSecondaryLinkList.tsx @@ -1,12 +1,15 @@ import clsx from 'clsx' import { ForwardedRef, forwardRef } from 'react' +import { Grid } from '../Grid' import { LinkList, LinkListProps } from '../LinkList' export const HeaderSecondaryLinkList = forwardRef( ({ children, className, ...restProps }: LinkListProps, ref: ForwardedRef) => ( - - {children} - + + + {children} + + ), ) diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 6ada0e64f4..1070d8fa95 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -38,9 +38,6 @@ export const Default: Story = { ), children: ( - - Alle onderwerpen - {defaultStoryLinks.map(({ label, href }) => ( @@ -84,11 +81,9 @@ export const AlwaysShowMenuButton: Story = { ), showMenuButton: 'always', children: [ - - - Inloggen - - , + + Inloggen + , Onderdelen From 10742cbf0e8df1e2619fc5c7ebd48c183b602dc3 Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 6 Dec 2024 17:18:51 +0100 Subject: [PATCH 17/59] Make story with always a menu button the default --- .../src/components/Header/Header.docs.mdx | 8 +- .../src/components/Header/Header.stories.tsx | 78 +++++++++---------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/storybook/src/components/Header/Header.docs.mdx b/storybook/src/components/Header/Header.docs.mdx index a8eb44f92c..59e434476e 100644 --- a/storybook/src/components/Header/Header.docs.mdx +++ b/storybook/src/components/Header/Header.docs.mdx @@ -17,13 +17,13 @@ import { StatusBadge } from "../../docs/components/StatusBadge"; ## Examples -### Always show menu button +### Without menu button on wide screens - + -### Never show menu button +### Without menu button - + ### With custom logo link diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 1070d8fa95..84e90ab6da 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -17,40 +17,6 @@ export default meta type Story = StoryObj const defaultStoryLinks = [ - { label: 'Stad', href: '#' }, - { label: 'Techniek', href: '#' }, - { label: 'Historie', href: '#' }, - { label: 'Duurzaamheid', href: '#' }, - { label: 'Zoeken', href: '#' }, -] - -export const Default: Story = { - args: { - appName: 'Aan de Amsterdamse grachten', - menu: ( - <> - {defaultStoryLinks.map(({ label, href }) => ( - - {label} - - ))} - - ), - children: ( - - - {defaultStoryLinks.map(({ label, href }) => ( - - {label} - - ))} - - - ), - }, -} - -const alwaysShowMenuButtonStoryLinks = [ [ { label: 'Kaart', href: '#' }, { label: 'Panoramabeelden', href: '#' }, @@ -69,7 +35,7 @@ const alwaysShowMenuButtonStoryLinks = [ ], ] -export const AlwaysShowMenuButton: Story = { +export const Default: Story = { args: { appName: 'Data Amsterdam', menu: ( @@ -89,7 +55,7 @@ export const AlwaysShowMenuButton: Story = { Onderdelen - {alwaysShowMenuButtonStoryLinks[0].map(({ label, href }) => ( + {defaultStoryLinks[0].map(({ label, href }) => ( {label} @@ -101,7 +67,7 @@ export const AlwaysShowMenuButton: Story = { Over ons - {alwaysShowMenuButtonStoryLinks[1].map(({ label, href }) => ( + {defaultStoryLinks[1].map(({ label, href }) => ( {label} @@ -113,7 +79,7 @@ export const AlwaysShowMenuButton: Story = { Help - {alwaysShowMenuButtonStoryLinks[2].map(({ label, href }) => ( + {defaultStoryLinks[2].map(({ label, href }) => ( {label} @@ -124,7 +90,41 @@ export const AlwaysShowMenuButton: Story = { }, } -export const NeverShowMenuButton: Story = { +const WithoutMenuButtonOnWideScreenStoryLinks = [ + { label: 'Stad', href: '#' }, + { label: 'Techniek', href: '#' }, + { label: 'Historie', href: '#' }, + { label: 'Duurzaamheid', href: '#' }, + { label: 'Zoeken', href: '#' }, +] + +export const WithoutMenuButtonOnWideScreen: Story = { + args: { + appName: 'Aan de Amsterdamse grachten', + menu: ( + <> + {WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( + + {label} + + ))} + + ), + children: ( + + + {WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( + + {label} + + ))} + + + ), + }, +} + +export const WithoutMenuButton: Story = { args: { appName: 'Mijn Amsterdam', showMenuButton: 'never', From 7f77424712408ecdb7092d9f1866612e784eae55 Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 6 Dec 2024 17:26:20 +0100 Subject: [PATCH 18/59] Rename menu prop to menuItems --- packages/react/src/Header/Header.tsx | 8 +++--- .../src/components/Header/Header.stories.tsx | 26 +++++++------------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index a9469d8a6d..6f981c958e 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -26,8 +26,8 @@ export type HeaderProps = { logoLink?: string /** The accessible text for the link on the logo. */ logoLinkTitle?: string - /** A slot for the menu. Use Header.MenuLink here. */ - menu?: ReactNode + /** A slot for the menu items. Use Header.MenuLink here. */ + menuItems?: ReactNode /** The text for the menu button. */ menuButtonText?: string /** The accessible label for the navigation section. */ @@ -46,7 +46,7 @@ const HeaderRoot = forwardRef( logoBrand = 'amsterdam', logoLink = '/', logoLinkTitle = 'Ga naar de homepage', - menu, + menuItems, menuButtonText = 'Menu', navigationLabel = 'Hoofdnavigatie', ...restProps @@ -92,7 +92,7 @@ const HeaderRoot = forwardRef(
      - {menu} + {menuItems}
    • - - Inloggen - - - ), + menuItems: [ + + Inloggen + , + ], showMenuButton: 'always', children: [ @@ -101,15 +99,11 @@ const WithoutMenuButtonOnWideScreenStoryLinks = [ export const WithoutMenuButtonOnWideScreen: Story = { args: { appName: 'Aan de Amsterdamse grachten', - menu: ( - <> - {WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( - - {label} - - ))} - - ), + menuItems: WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( + + {label} + + )), children: ( From ea2198bfd0192c4c760c3fd9fb775dd87163b3d9 Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 6 Dec 2024 17:38:09 +0100 Subject: [PATCH 19/59] Don't render Grid in Header itself, allow users to render what they want in mega menu --- .../css/src/components/header/header.scss | 5 - packages/react/src/Header/Header.tsx | 6 +- .../src/components/Header/Header.stories.tsx | 101 +++++++++--------- 3 files changed, 53 insertions(+), 59 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 670620f897..fc3f364366 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -176,11 +176,6 @@ pointer-events: auto; } -// Reset the inline padding of the grid here, and set on the root header element instead. -.ams-header__mega-menu.ams-header__mega-menu { - padding-inline: 0; -} - .ams-header__mega-menu--closed.ams-header__mega-menu--closed { display: none; } diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 6f981c958e..abb1dbcf18 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -6,7 +6,6 @@ import clsx from 'clsx' import { forwardRef, useEffect, useState } from 'react' import type { ForwardedRef, HTMLAttributes, ReactNode } from 'react' -import { Grid } from '../Grid' import { Heading } from '../Heading' import { Icon } from '../Icon' import { Logo } from '../Logo' @@ -131,13 +130,12 @@ const HeaderRoot = forwardRef(
    {children && ( - {children} - + )} )} diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 5cacfe9500..ec58ea3589 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -44,47 +44,50 @@ export const Default: Story = { , ], showMenuButton: 'always', - children: [ - - Inloggen - , - - - Onderdelen - - - {defaultStoryLinks[0].map(({ label, href }) => ( - - {label} - - ))} - - , - - - Over ons - - - {defaultStoryLinks[1].map(({ label, href }) => ( - - {label} - - ))} - - , - - - Help - - - {defaultStoryLinks[2].map(({ label, href }) => ( - - {label} - - ))} - - , - ], + children: ( + // TODO: Add noInlinePadding to Grid? + + + Inloggen + + + + Onderdelen + + + {defaultStoryLinks[0].map(({ label, href }) => ( + + {label} + + ))} + + + + + Over ons + + + {defaultStoryLinks[1].map(({ label, href }) => ( + + {label} + + ))} + + + + + Help + + + {defaultStoryLinks[2].map(({ label, href }) => ( + + {label} + + ))} + + + + ), }, } @@ -105,15 +108,13 @@ export const WithoutMenuButtonOnWideScreen: Story = { )), children: ( - - - {WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( - - {label} - - ))} - - + + {WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( + + {label} + + ))} + ), }, } From f41083b629aa659d66c5ec326115c4d24f6f363e Mon Sep 17 00:00:00 2001 From: alimpens Date: Mon, 9 Dec 2024 09:37:39 +0100 Subject: [PATCH 20/59] Add NarrowScreenOnlyGridCell, use regular LinkList for content --- packages/css/src/components/header/header.scss | 2 +- packages/react/src/Header/Header.tsx | 6 ++---- .../Header/HeaderNarrowScreenOnlyGridCell.tsx | 13 +++++++++++++ .../react/src/Header/HeaderSecondaryLinkList.tsx | 16 ---------------- .../src/Header/HeaderSecondaryLinkListLink.tsx | 12 ------------ .../src/components/Header/Header.stories.tsx | 8 +++++--- 6 files changed, 21 insertions(+), 36 deletions(-) create mode 100644 packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.tsx delete mode 100644 packages/react/src/Header/HeaderSecondaryLinkList.tsx delete mode 100644 packages/react/src/Header/HeaderSecondaryLinkListLink.tsx diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index fc3f364366..144c277241 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -180,7 +180,7 @@ display: none; } -.ams-header__secondary-link-list { +.ams-header__narrow-screen-only-grid-cell { @media screen and (min-width: $ams-breakpoint-wide) { display: none; } diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index abb1dbcf18..aa69a2a199 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -12,8 +12,7 @@ import { Logo } from '../Logo' import type { LogoBrand } from '../Logo' import { HeaderMenuIcon } from './HeaderMenuIcon' import { HeaderMenuLink } from './HeaderMenuLink' -import { HeaderSecondaryLinkList } from './HeaderSecondaryLinkList' -import { HeaderSecondaryLinkListLink } from './HeaderSecondaryLinkListLink' +import { HeaderNarrowScreenOnlyGridCell } from './HeaderNarrowScreenOnlyGridCell' import useMediaQuery from '../common/useMediaQuery' export type HeaderProps = { @@ -148,6 +147,5 @@ HeaderRoot.displayName = 'Header' export const Header = Object.assign(HeaderRoot, { MenuLink: HeaderMenuLink, - SecondaryLinkList: HeaderSecondaryLinkList, - SecondaryLinkListLink: HeaderSecondaryLinkListLink, + NarrowScreenOnlyGridCell: HeaderNarrowScreenOnlyGridCell, }) diff --git a/packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.tsx b/packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.tsx new file mode 100644 index 0000000000..9c6686f3ca --- /dev/null +++ b/packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.tsx @@ -0,0 +1,13 @@ +import clsx from 'clsx' +import { ForwardedRef, forwardRef } from 'react' +import { Grid, GridCellProps } from '../Grid' + +export const HeaderNarrowScreenOnlyGridCell = forwardRef( + ({ children, className, ...restProps }: GridCellProps, ref: ForwardedRef) => ( + + {children} + + ), +) + +HeaderNarrowScreenOnlyGridCell.displayName = 'Header.NarrowScreenOnlyGridCell' diff --git a/packages/react/src/Header/HeaderSecondaryLinkList.tsx b/packages/react/src/Header/HeaderSecondaryLinkList.tsx deleted file mode 100644 index 2e46012f7e..0000000000 --- a/packages/react/src/Header/HeaderSecondaryLinkList.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import clsx from 'clsx' -import { ForwardedRef, forwardRef } from 'react' -import { Grid } from '../Grid' -import { LinkList, LinkListProps } from '../LinkList' - -export const HeaderSecondaryLinkList = forwardRef( - ({ children, className, ...restProps }: LinkListProps, ref: ForwardedRef) => ( - - - {children} - - - ), -) - -HeaderSecondaryLinkList.displayName = 'Header.SecondaryLinkList' diff --git a/packages/react/src/Header/HeaderSecondaryLinkListLink.tsx b/packages/react/src/Header/HeaderSecondaryLinkListLink.tsx deleted file mode 100644 index ca4beb7468..0000000000 --- a/packages/react/src/Header/HeaderSecondaryLinkListLink.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { ForwardedRef, forwardRef } from 'react' -import { LinkList, LinkListLinkProps } from '../LinkList' - -export const HeaderSecondaryLinkListLink = forwardRef( - ({ children, ...restProps }: LinkListLinkProps, ref: ForwardedRef) => ( - - {children} - - ), -) - -HeaderSecondaryLinkListLink.displayName = 'Header.SecondaryLinkListLink' diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index ec58ea3589..58a2ded22f 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -47,9 +47,11 @@ export const Default: Story = { children: ( // TODO: Add noInlinePadding to Grid? - - Inloggen - + + + Inloggen + + Onderdelen From da61baa7f9648222fe6bed22734ff9faf1f995fe Mon Sep 17 00:00:00 2001 From: alimpens Date: Mon, 9 Dec 2024 09:48:55 +0100 Subject: [PATCH 21/59] Use fixed instead of secondary as prop on MenuLink --- .../css/src/components/header/header.scss | 6 +++++- packages/react/src/Header/HeaderMenuLink.tsx | 6 +++--- .../src/components/Header/Header.stories.tsx | 21 ++++++++++++------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 144c277241..6c9a4f86ca 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -53,12 +53,16 @@ @include reset-list; } -.ams-header__menu-item--secondary { +.ams-header__menu-item { @media screen and (not (min-width: $ams-breakpoint-wide)) { display: none; } } +.ams-header__menu-item--fixed { + display: revert; +} + @mixin shared-styles { color: var(--ams-header-menu-item-color); font-family: var(--ams-header-menu-item-font-family); diff --git a/packages/react/src/Header/HeaderMenuLink.tsx b/packages/react/src/Header/HeaderMenuLink.tsx index 23bb20aa7e..dfa5de8444 100644 --- a/packages/react/src/Header/HeaderMenuLink.tsx +++ b/packages/react/src/Header/HeaderMenuLink.tsx @@ -2,12 +2,12 @@ import clsx from 'clsx' import { AnchorHTMLAttributes, ForwardedRef, forwardRef, PropsWithChildren } from 'react' export type HeaderMenuLinkProps = { - secondary?: boolean + fixed?: boolean } & PropsWithChildren> export const HeaderMenuLink = forwardRef( - ({ children, secondary, ...restProps }: HeaderMenuLinkProps, ref: ForwardedRef) => ( -
  • + ({ children, fixed, ...restProps }: HeaderMenuLinkProps, ref: ForwardedRef) => ( +
  • {children} diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 58a2ded22f..4a443758de 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -39,9 +39,12 @@ export const Default: Story = { args: { appName: 'Data Amsterdam', menuItems: [ - + Inloggen , + + Zoeken + , ], showMenuButton: 'always', children: ( @@ -98,17 +101,21 @@ const WithoutMenuButtonOnWideScreenStoryLinks = [ { label: 'Techniek', href: '#' }, { label: 'Historie', href: '#' }, { label: 'Duurzaamheid', href: '#' }, - { label: 'Zoeken', href: '#' }, ] export const WithoutMenuButtonOnWideScreen: Story = { args: { appName: 'Aan de Amsterdamse grachten', - menuItems: WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( - - {label} - - )), + menuItems: [ + WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( + + {label} + + )), + + Zoeken + , + ], children: ( {WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( From 8bf7f782bf7b7289938cae11aaeaffbbe12d8bdc Mon Sep 17 00:00:00 2001 From: alimpens Date: Mon, 9 Dec 2024 15:47:56 +0100 Subject: [PATCH 22/59] Change app name line height --- packages/css/src/components/header/header.scss | 5 +++++ packages/react/src/Header/Header.tsx | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 6c9a4f86ca..8409453d4e 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -22,6 +22,11 @@ outline-offset: var(--ams-header-logo-link-outline-offset); } +/* TODO Remove after updating Header line heights in DES-973. */ +.ams-header__app-name { + line-height: 1.335; +} + .ams-header__navigation { column-gap: var(--ams-header-navigation-column-gap); display: flex; diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index aa69a2a199..a124fa5c4d 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -70,7 +70,7 @@ const HeaderRoot = forwardRef( {appName && ( - + {appName} )} @@ -86,7 +86,7 @@ const HeaderRoot = forwardRef(
    - {appName && {appName}} + {appName && {appName}}
      From d6b8b49a716ed5d0671e474a1a12085194b88b6f Mon Sep 17 00:00:00 2001 From: alimpens Date: Mon, 9 Dec 2024 15:52:41 +0100 Subject: [PATCH 23/59] Disallow user-select for hidden duplicate brand section --- packages/css/src/components/header/header.scss | 5 +++++ packages/react/src/Header/Header.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 8409453d4e..06de177e2a 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -18,6 +18,11 @@ grid-area: 1 / 1; } +.ams-header__brand-section--hidden { + opacity: 0%; + user-select: none; +} + .ams-header__logo-link { outline-offset: var(--ams-header-logo-link-outline-offset); } diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index a124fa5c4d..6bf0987206 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -82,7 +82,7 @@ const HeaderRoot = forwardRef( {/* The logo and app name section is recreated here, to make sure the page menu breaks at the right spot */} -
      +
      From e7e831234a242bf5b6b21394940c9bd6f491e863 Mon Sep 17 00:00:00 2001 From: alimpens Date: Mon, 9 Dec 2024 15:56:34 +0100 Subject: [PATCH 24/59] Add missing token --- proprietary/tokens/src/components/ams/header.tokens.json | 1 + 1 file changed, 1 insertion(+) diff --git a/proprietary/tokens/src/components/ams/header.tokens.json b/proprietary/tokens/src/components/ams/header.tokens.json index 3b2047f9e0..55d8d069a0 100644 --- a/proprietary/tokens/src/components/ams/header.tokens.json +++ b/proprietary/tokens/src/components/ams/header.tokens.json @@ -19,6 +19,7 @@ }, "menu": { "column-gap": { "value": "{ams.space.grid.md}" }, + "row-gap": { "value": "{ams.space.grid.xs}" }, "item": { "color": { "value": "{ams.link-appearance.color}" }, "column-gap": { "value": "{ams.space.xs}" }, From 746a0cfdcfd3e32ccb7f67fc900b4eafabb5d987 Mon Sep 17 00:00:00 2001 From: alimpens Date: Mon, 9 Dec 2024 16:07:53 +0100 Subject: [PATCH 25/59] Allow logo to grow to 56px --- packages/css/src/components/logo/README.md | 2 +- packages/css/src/components/logo/logo.scss | 1 + proprietary/tokens/src/components/ams/logo.tokens.json | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/css/src/components/logo/README.md b/packages/css/src/components/logo/README.md index 0db557e7f9..421fa8b41e 100644 --- a/packages/css/src/components/logo/README.md +++ b/packages/css/src/components/logo/README.md @@ -34,7 +34,7 @@ The sub-brands are: - The logo links to the homepage of the website or application. - If the application is a form, application, or tool without a homepage, the logo links to the page where the form, application, or tool is referred to. -The height of the logo is always 40 pixels. +The logo is 40 pixels tall at its minimum, growing to 56 pixels in wider windows. This also applies to sub-brand logos. ## Download diff --git a/packages/css/src/components/logo/logo.scss b/packages/css/src/components/logo/logo.scss index 43247d6eaf..a4ef372767 100644 --- a/packages/css/src/components/logo/logo.scss +++ b/packages/css/src/components/logo/logo.scss @@ -6,6 +6,7 @@ .ams-logo { block-size: var(--ams-logo-block-size); display: block; + min-block-size: var(--ams-logo-min-block-size); } .ams-logo__emblem { diff --git a/proprietary/tokens/src/components/ams/logo.tokens.json b/proprietary/tokens/src/components/ams/logo.tokens.json index c4f9ac41e2..8188991c62 100644 --- a/proprietary/tokens/src/components/ams/logo.tokens.json +++ b/proprietary/tokens/src/components/ams/logo.tokens.json @@ -1,8 +1,9 @@ { "ams": { "logo": { - "block-size": { "value": "2.5rem" }, + "block-size": { "value": "{ams.space.grid.md}" }, "emblem": { "color": { "value": "{ams.brand.color.red.60}" } }, + "min-block-size": { "value": "2.5rem" }, "title": { "color": { "value": "{ams.brand.color.red.60}" } }, "subsite": { "color": { "value": "{ams.brand.color.neutral.100}" } } } From abe295cb2244698611a468d0c9d12128caeae55f Mon Sep 17 00:00:00 2001 From: alimpens Date: Mon, 9 Dec 2024 16:09:35 +0100 Subject: [PATCH 26/59] Make sure app name line height override beats ams-heading --- packages/css/src/components/header/header.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 06de177e2a..17e6b0b34c 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -28,7 +28,7 @@ } /* TODO Remove after updating Header line heights in DES-973. */ -.ams-header__app-name { +.ams-heading.ams-header__app-name { line-height: 1.335; } From 5852adb7bc3934acd4b5cd51006842b26b25f702 Mon Sep 17 00:00:00 2001 From: alimpens Date: Mon, 9 Dec 2024 16:17:09 +0100 Subject: [PATCH 27/59] Add extra padding block to header --- packages/css/src/components/header/header.scss | 1 + proprietary/tokens/src/components/ams/header.tokens.json | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 17e6b0b34c..4d8e27c4c1 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -7,6 +7,7 @@ .ams-header { display: grid; + padding-block: var(--ams-header-padding-block); padding-inline: var(--ams-grid-padding-inline); } diff --git a/proprietary/tokens/src/components/ams/header.tokens.json b/proprietary/tokens/src/components/ams/header.tokens.json index 55d8d069a0..b1cf75e65e 100644 --- a/proprietary/tokens/src/components/ams/header.tokens.json +++ b/proprietary/tokens/src/components/ams/header.tokens.json @@ -1,6 +1,7 @@ { "ams": { "header": { + "padding-block": { "value": "{ams.space.grid.sm}" }, "brand-section": { "column-gap": { "value": "{ams.space.md}" }, "row-gap": { "value": "{ams.space.grid.xs}" } From 6a8eb2a3e5ddba985705eac1229e509c3d595b5f Mon Sep 17 00:00:00 2001 From: alimpens Date: Mon, 9 Dec 2024 16:35:59 +0100 Subject: [PATCH 28/59] Use noMenuButtonOnWideScreen boolean instead of showMenuButton enum --- packages/css/src/components/header/header.scss | 2 +- packages/react/src/Header/Header.tsx | 15 ++++++--------- .../src/components/Header/Header.stories.tsx | 14 ++++++++++---- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 4d8e27c4c1..28126127dd 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -103,7 +103,7 @@ } } -.ams-header__mega-menu-button-item:not(.ams-header__mega-menu-button-item--show-always) { +.ams-header__mega-menu-button-item--hide-on-wide-screen { @media screen and (min-width: $ams-breakpoint-wide) { display: none; } diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 6bf0987206..6f94ba48f2 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -30,14 +30,13 @@ export type HeaderProps = { menuButtonText?: string /** The accessible label for the navigation section. */ navigationLabel?: string - /** When to show the menu button. The default is show only when the screen is narrow. */ - showMenuButton?: 'always' | 'never' + /** Whether the menu button is visible on wide screens. */ + noMenuButtonOnWideScreen?: boolean } & HTMLAttributes const HeaderRoot = forwardRef( ( { - showMenuButton, appName, className, children, @@ -47,6 +46,7 @@ const HeaderRoot = forwardRef( menuItems, menuButtonText = 'Menu', navigationLabel = 'Hoofdnavigatie', + noMenuButtonOnWideScreen, ...restProps }: HeaderProps, ref: ForwardedRef, @@ -57,7 +57,7 @@ const HeaderRoot = forwardRef( useEffect(() => { // Close the menu when the menu button disappears - if (!showMenuButton && isWideScreen) { + if (noMenuButtonOnWideScreen && isWideScreen) { setOpen(false) } }, [isWideScreen]) @@ -75,7 +75,7 @@ const HeaderRoot = forwardRef( )}
      - {showMenuButton !== 'never' && ( + {(children || menuItems) && (
      } />) + it('renders a nav section with a custom label', () => { + render(
      Test
      ) - // const menu = container.querySelector('.ams-header__links') + const component = screen.getByRole('navigation', { name: 'Custom Navigation' }) - // expect(menu).toBeInTheDocument() - // expect(menu).toHaveTextContent('Test content') - // }) + expect(component).toBeInTheDocument() + }) - // it('renders with menu button', () => { - // render(
      Menu Button} />) + it('renders a menu', () => { + render(
      Menu Item} />) - // const menu = screen.getByRole('button') + const component = screen.getByRole('list') - // expect(menu).toBeInTheDocument() - // }) + expect(component).toHaveClass('ams-header__menu') + }) + + it('renders menu items', () => { + render( +
      + Menu Item 1 + , + + Menu Item 2 + , + ]} + />, + ) + + const item1 = screen.getByRole('link', { name: 'Menu Item 1' }) + const item2 = screen.getByRole('link', { name: 'Menu Item 2' }) + + expect(item1).toBeInTheDocument() + expect(item2).toBeInTheDocument() + }) + + it('renders a menu button', () => { + render(
      Test
      ) + + const component = screen.getByRole('button', { name: 'Menu' }) + + expect(component).toHaveClass('ams-header__mega-menu-button') + }) + + it('renders a menu button icon', () => { + const { container } = render(
      Test
      ) + + const component = container.querySelector('.ams-header__menu-icon') + + expect(component).toBeInTheDocument() + }) + + it('renders a custom menu button text', () => { + render(
      Test
      ) + + const component = screen.getByRole('button', { name: 'Custom button text' }) + + expect(component).toBeInTheDocument() + }) + + it('renders the correct class when noMenuButtonOnWideScreen is true', () => { + render(
      Test
      ) + + const component = screen.getByRole('listitem') + + expect(component).toHaveClass('ams-header__mega-menu-button-item--hide-on-wide-screen') + }) + + it('opens and closes the mega menu', async () => { + const user = userEvent.setup() + + const { container } = render(
      Test
      ) + + const closedMegaMenu = container.querySelector('.ams-header__mega-menu--closed') + + expect(closedMegaMenu).toBeInTheDocument() + + const menuButton = screen.getByRole('button', { name: 'Menu' }) + + await user.click(menuButton) + + const openMegaMenu = container.querySelector('.ams-header__mega-menu') + + expect(openMegaMenu).toBeInTheDocument() + expect(openMegaMenu).not.toHaveClass('ams-header__mega-menu--closed') + }) + + it.skip('it closes the mega menu when it is open and the screen width passes the breakpoint', () => { + // TODO: Add this test + }) }) From b4bd94beac0c81c6402902d734eb9f9ec13e51c4 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 11 Dec 2024 13:34:32 +0100 Subject: [PATCH 41/59] Override grid inline padding from mega menu --- packages/css/src/components/header/header.scss | 6 ++++++ storybook/src/components/Header/Header.stories.tsx | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index efb5d59307..5ee9955c13 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -191,6 +191,12 @@ .ams-header__mega-menu { inline-size: 100%; pointer-events: auto; + + // Remove inline padding from Grids that are used in the mega menu. + // The grid inline padding is set on the header element. + & .ams-grid { + padding-inline: 0; + } } .ams-header__mega-menu--closed.ams-header__mega-menu--closed { diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index be91fbea29..71994644f5 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -47,8 +47,7 @@ export const Default: Story = { , ], children: ( - // TODO: Add noInlinePadding to Grid? - + Inloggen @@ -147,7 +146,7 @@ export const WithCustomTexts: Story = { menuButtonText: 'Hoofdmenu', navigationLabel: 'Navigatie', children: ( - + Inloggen From 03537864826abae0baa306a5dd2ab0de2bde0460 Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 13 Dec 2024 13:39:26 +0100 Subject: [PATCH 42/59] Rename useMediaQuery to useIsAfterBreakpoint --- packages/react/src/Header/Header.tsx | 8 ++++---- .../{useMediaQuery.tsx => useIsAfterBreakpoint.tsx} | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) rename packages/react/src/common/{useMediaQuery.tsx => useIsAfterBreakpoint.tsx} (83%) diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 569483a538..cef88bdcd2 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -13,7 +13,7 @@ import type { LogoBrand } from '../Logo' import { HeaderMenuIcon } from './HeaderMenuIcon' import { HeaderMenuLink } from './HeaderMenuLink' import { HeaderNarrowScreenOnlyGridCell } from './HeaderNarrowScreenOnlyGridCell' -import useMediaQuery from '../common/useMediaQuery' +import useIsAfterBreakpoint from '../common/useIsAfterBreakpoint' export type HeaderProps = { /** The name of the application. */ @@ -53,14 +53,14 @@ const HeaderRoot = forwardRef( ) => { const [open, setOpen] = useState(false) - const isWideScreen = useMediaQuery('wide') + const isWideWindow = useIsAfterBreakpoint('wide') useEffect(() => { // Close the menu when the menu button disappears - if (noMenuButtonOnWideScreen && isWideScreen) { + if (noMenuButtonOnWideScreen && isWideWindow) { setOpen(false) } - }, [isWideScreen]) + }, [isWideWindow]) return (
      diff --git a/packages/react/src/common/useMediaQuery.tsx b/packages/react/src/common/useIsAfterBreakpoint.tsx similarity index 83% rename from packages/react/src/common/useMediaQuery.tsx rename to packages/react/src/common/useIsAfterBreakpoint.tsx index d6cfbca783..488274de90 100644 --- a/packages/react/src/common/useMediaQuery.tsx +++ b/packages/react/src/common/useIsAfterBreakpoint.tsx @@ -6,9 +6,9 @@ const breakpoints = { wide: '68rem', } -type useMediaQueryProps = 'medium' | 'wide' +type useIsAfterBreakpointProps = 'medium' | 'wide' -const useMediaQuery = (breakpoint: useMediaQueryProps) => { +const useIsAfterBreakpoint = (breakpoint: useIsAfterBreakpointProps) => { const [matches, setMatches] = useState(false) useEffect(() => { @@ -33,4 +33,4 @@ const useMediaQuery = (breakpoint: useMediaQueryProps) => { return matches } -export default useMediaQuery +export default useIsAfterBreakpoint From 7ea54efa09c21d8261945a70c6477bc77e015ab3 Mon Sep 17 00:00:00 2001 From: alimpens Date: Mon, 16 Dec 2024 10:20:40 +0100 Subject: [PATCH 43/59] Use layout effect instead of regular effect --- packages/react/src/common/useIsAfterBreakpoint.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/src/common/useIsAfterBreakpoint.tsx b/packages/react/src/common/useIsAfterBreakpoint.tsx index 488274de90..5cb31fc0d2 100644 --- a/packages/react/src/common/useIsAfterBreakpoint.tsx +++ b/packages/react/src/common/useIsAfterBreakpoint.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useLayoutEffect, useState } from 'react' // TODO: we should set the breakpoint in JS somewhere and render this and the sass variables from that const breakpoints = { @@ -11,7 +11,7 @@ type useIsAfterBreakpointProps = 'medium' | 'wide' const useIsAfterBreakpoint = (breakpoint: useIsAfterBreakpointProps) => { const [matches, setMatches] = useState(false) - useEffect(() => { + useLayoutEffect(() => { // Check for window object to avoid SSR issues if (breakpoint && typeof window !== 'undefined') { const media = window.matchMedia(`(min-width: ${breakpoints[breakpoint]})`) From ac66c861acf3acb4592dbd0406d17ae77ce40a57 Mon Sep 17 00:00:00 2001 From: alimpens Date: Tue, 17 Dec 2024 16:42:17 +0100 Subject: [PATCH 44/59] Rename brand section to branding, and app name to brand name --- .../css/src/components/header/header.scss | 8 ++++---- packages/react/src/Header/Header.test.tsx | 4 ++-- packages/react/src/Header/Header.tsx | 20 ++++++++++--------- .../src/components/ams/header.tokens.json | 2 +- .../src/components/Header/Header.stories.tsx | 6 +++--- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 5ee9955c13..1f8e4bbd20 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -12,15 +12,15 @@ padding-inline: var(--ams-grid-padding-inline); } -.ams-header__brand-section { +.ams-header__branding { align-items: center; align-self: start; - column-gap: var(--ams-header-brand-section-column-gap); + column-gap: var(--ams-header-branding-column-gap); display: flex; grid-area: 1 / 1; } -.ams-header__brand-section--hidden { +.ams-header__branding--hidden { opacity: 0%; user-select: none; } @@ -30,7 +30,7 @@ } /* TODO Remove after updating Heading line heights in DES-973. */ -.ams-heading.ams-header__app-name { +.ams-heading.ams-header__brand-name { line-height: 1.35; } diff --git a/packages/react/src/Header/Header.test.tsx b/packages/react/src/Header/Header.test.tsx index ed220cb561..c596383552 100644 --- a/packages/react/src/Header/Header.test.tsx +++ b/packages/react/src/Header/Header.test.tsx @@ -45,7 +45,7 @@ describe('Header', () => { it('renders a brand section', () => { const { container } = render(
      ) - const component = container.querySelector('.ams-header__brand-section') + const component = container.querySelector('.ams-header__branding') expect(component).toBeInTheDocument() }) @@ -83,7 +83,7 @@ describe('Header', () => { }) it('renders an application name', () => { - render(
      ) + render(
      ) const heading = screen.getByRole('heading', { name: 'Application name', diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index cef88bdcd2..71e858dc88 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -17,7 +17,7 @@ import useIsAfterBreakpoint from '../common/useIsAfterBreakpoint' export type HeaderProps = { /** The name of the application. */ - appName?: string + brandName?: string /** The name of the brand for which to display the logo. */ logoBrand?: LogoBrand /** The url for the link on the logo. */ @@ -37,7 +37,7 @@ export type HeaderProps = { const HeaderRoot = forwardRef( ( { - appName, + brandName, className, children, logoBrand = 'amsterdam', @@ -64,14 +64,14 @@ const HeaderRoot = forwardRef( return (
      -
      +
      {logoLinkTitle} - {appName && ( - - {appName} + {brandName && ( + + {brandName} )}
      @@ -81,12 +81,14 @@ const HeaderRoot = forwardRef( {navigationLabel} - {/* The logo and app name section is recreated here, to make sure the page menu breaks at the right spot */} -
      + {/* The branding section is recreated here, to make sure the page menu breaks at the right spot */} +
      - {appName && {appName}} + {brandName && ( + {brandName} + )}
        diff --git a/proprietary/tokens/src/components/ams/header.tokens.json b/proprietary/tokens/src/components/ams/header.tokens.json index fdf7eba2b1..7f7e004ee1 100644 --- a/proprietary/tokens/src/components/ams/header.tokens.json +++ b/proprietary/tokens/src/components/ams/header.tokens.json @@ -2,7 +2,7 @@ "ams": { "header": { "padding-block": { "value": "{ams.space.grid.sm}" }, - "brand-section": { + "branding": { "column-gap": { "value": "{ams.space.md}" }, "row-gap": { "value": "{ams.space.grid.xs}" } }, diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 71994644f5..88216d02b1 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -37,7 +37,7 @@ const defaultStoryLinks = [ export const Default: Story = { args: { - appName: 'Data Amsterdam', + brandName: 'Data Amsterdam', menuItems: [ Inloggen @@ -103,7 +103,7 @@ const WithoutMenuButtonOnWideScreenStoryLinks = [ export const WithoutMenuButtonOnWideScreen: Story = { args: { - appName: 'Aan de Amsterdamse grachten', + brandName: 'Aan de Amsterdamse grachten', menuItems: [ ...WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( @@ -129,7 +129,7 @@ export const WithoutMenuButtonOnWideScreen: Story = { export const WithoutMenuButton: Story = { args: { - appName: 'Mijn Amsterdam', + brandName: 'Mijn Amsterdam', }, } From 3eef970336b9b3d55ed778605bda43f9ce603177 Mon Sep 17 00:00:00 2001 From: alimpens Date: Tue, 17 Dec 2024 17:16:11 +0100 Subject: [PATCH 45/59] Add comments --- .../css/src/components/header/header.scss | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 1f8e4bbd20..6b321af09d 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -7,6 +7,11 @@ @import "../../common/text-rendering"; .ams-header { + /* + * The branding section is created twice: once outside the navigation and once hidden inside it. + * This keeps all navigation in one nav element and lets the menu wrap around the branding section. + * Display grid is used to let both branding sections overlap. + */ display: grid; padding-block: var(--ams-header-padding-block); padding-inline: var(--ams-grid-padding-inline); @@ -14,15 +19,15 @@ .ams-header__branding { align-items: center; - align-self: start; + align-self: start; // To align the branding section to the top of the header when it wraps column-gap: var(--ams-header-branding-column-gap); display: flex; - grid-area: 1 / 1; + grid-area: 1 / 1; // To allow this section to overlap with the second branding section } .ams-header__branding--hidden { opacity: 0%; - user-select: none; + user-select: none; // The hidden branding section should not be selectable } .ams-header__logo-link { @@ -38,39 +43,39 @@ column-gap: var(--ams-header-navigation-column-gap); display: flex; flex-wrap: wrap; - grid-area: 1 / 1; - inset-block-start: 0; + grid-area: 1 / 1; // To allow this section to overlap with the branding section + // This section blocks pointer events initially, so the hidden branding section can't be activated. + // The menu and collapsible menu set it back to auto, to make sure they can be activated. pointer-events: none; row-gap: var(--ams-header-navigation-row-gap); } @mixin reset-list { + list-style: none; margin-block: 0; padding-inline-start: 0; } .ams-header__menu { align-items: center; - box-sizing: border-box; column-gap: var(--ams-header-menu-column-gap); display: flex; - flex-direction: row; flex-wrap: wrap; justify-content: flex-end; - list-style: none; - margin-inline-start: auto; - pointer-events: auto; + pointer-events: auto; // Set pointer events back to auto to allow the menu to be activated row-gap: var(--ams-header-menu-row-gap); @include reset-list; } +// Do not show menu items below the wide breakpoint... .ams-header__menu-item { @media screen and (not (min-width: $ams-breakpoint-wide)) { display: none; } } +// ...unless they're fixed. .ams-header__menu-item--fixed { display: revert; } @@ -138,7 +143,7 @@ .ams-header__mega-menu-button-label, .ams-header__mega-menu-button-hidden-label { - grid-area: 1 / 1; + grid-area: 1 / 1; // To allow the label and the hidden label to overlap } // This hidden label is used to prevent a layout shift when the mega menu is opened @@ -190,7 +195,7 @@ .ams-header__mega-menu { inline-size: 100%; - pointer-events: auto; + pointer-events: auto; // Set pointer events back to auto to allow the mega menu to be activated // Remove inline padding from Grids that are used in the mega menu. // The grid inline padding is set on the header element. From c5acb8c32475f7b2f9411e0e3451d4a6e62d76e6 Mon Sep 17 00:00:00 2001 From: alimpens Date: Tue, 17 Dec 2024 17:18:08 +0100 Subject: [PATCH 46/59] Rename mixin --- packages/css/src/components/header/header.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 6b321af09d..1bda29c42c 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -80,7 +80,7 @@ display: revert; } -@mixin shared-styles { +@mixin header-menu-action { color: var(--ams-header-menu-item-color); font-family: var(--ams-header-menu-item-font-family); font-size: var(--ams-header-menu-item-font-size); @@ -104,7 +104,7 @@ text-decoration-thickness: var(--ams-header-menu-link-text-decoration-thickness); text-underline-offset: var(--ams-header-menu-link-text-underline-offset); - @include shared-styles; + @include header-menu-action; &:hover { text-decoration-line: var(--ams-header-menu-link-hover-text-decoration-line); @@ -133,7 +133,7 @@ display: grid; grid-auto-flow: column; - @include shared-styles; + @include header-menu-action; @include reset-button; } From d5cf33fd43acb8af9d4da081323aa17df5d21624 Mon Sep 17 00:00:00 2001 From: alimpens Date: Tue, 17 Dec 2024 17:19:46 +0100 Subject: [PATCH 47/59] Fix copy paste error --- packages/react/src/Header/HeaderMenuLink.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/Header/HeaderMenuLink.test.tsx b/packages/react/src/Header/HeaderMenuLink.test.tsx index 5106daf152..5bcdceb0d1 100644 --- a/packages/react/src/Header/HeaderMenuLink.test.tsx +++ b/packages/react/src/Header/HeaderMenuLink.test.tsx @@ -3,7 +3,7 @@ import { createRef } from 'react' import { HeaderMenuLink } from './HeaderMenuLink' import '@testing-library/jest-dom' -describe('Breadcrumb link', () => { +describe('Header menu link', () => { it('renders', () => { render() From 3d6c8b011354eb2be9137a6c56c05ebc70f311a7 Mon Sep 17 00:00:00 2001 From: alimpens Date: Tue, 17 Dec 2024 17:21:00 +0100 Subject: [PATCH 48/59] Fix typo --- .../react/src/Header/HeaderNarrowScreenOnlyGridCell.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.test.tsx b/packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.test.tsx index ed4331feb2..97182df468 100644 --- a/packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.test.tsx +++ b/packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.test.tsx @@ -3,7 +3,7 @@ import { createRef } from 'react' import { HeaderNarrowScreenOnlyGridCell } from './HeaderNarrowScreenOnlyGridCell' import '@testing-library/jest-dom' -describe('HeaderNarrowScreenOnlyGridCell', () => { +describe('Header narrow screen only grid cell', () => { it('renders', () => { const { container } = render() From bdcc68625437eeeb394d6434e4256bedd165eb14 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 18 Dec 2024 09:40:39 +0100 Subject: [PATCH 49/59] Use token for inline padding --- packages/css/src/components/header/header.scss | 2 +- proprietary/tokens/src/components/ams/header.tokens.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 1bda29c42c..77cfa74efb 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -14,7 +14,7 @@ */ display: grid; padding-block: var(--ams-header-padding-block); - padding-inline: var(--ams-grid-padding-inline); + padding-inline: var(--ams-header-padding-inline); } .ams-header__branding { diff --git a/proprietary/tokens/src/components/ams/header.tokens.json b/proprietary/tokens/src/components/ams/header.tokens.json index 7f7e004ee1..bf72af5876 100644 --- a/proprietary/tokens/src/components/ams/header.tokens.json +++ b/proprietary/tokens/src/components/ams/header.tokens.json @@ -2,6 +2,10 @@ "ams": { "header": { "padding-block": { "value": "{ams.space.grid.sm}" }, + "padding-inline": { + "value": "{ams.grid.padding-inline}", + "comment": "Must be the Grid inline padding, to make sure Header and Grid line up" + }, "branding": { "column-gap": { "value": "{ams.space.md}" }, "row-gap": { "value": "{ams.space.grid.xs}" } From 0eab0c58b36a6eb63e2352d54cb7a207fb520946 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 18 Dec 2024 09:46:59 +0100 Subject: [PATCH 50/59] Rename GridCellNarrowWindowOnly --- packages/css/src/components/header/header.scss | 2 +- packages/react/src/Header/Header.tsx | 4 ++-- ...x => HeaderGridCellNarrowWindowOnly.test.tsx} | 16 ++++++++-------- ...ll.tsx => HeaderGridCellNarrowWindowOnly.tsx} | 6 +++--- .../src/components/Header/Header.stories.tsx | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) rename packages/react/src/Header/{HeaderNarrowScreenOnlyGridCell.test.tsx => HeaderGridCellNarrowWindowOnly.test.tsx} (63%) rename packages/react/src/Header/{HeaderNarrowScreenOnlyGridCell.tsx => HeaderGridCellNarrowWindowOnly.tsx} (50%) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 77cfa74efb..8c2fe3ae65 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -208,7 +208,7 @@ display: none; } -.ams-header__narrow-screen-only-grid-cell { +.ams-header__grid-cell-narrow-window-only { @media screen and (min-width: $ams-breakpoint-wide) { display: none; } diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 71e858dc88..7558ab8d25 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -10,9 +10,9 @@ import { Heading } from '../Heading' import { Icon } from '../Icon' import { Logo } from '../Logo' import type { LogoBrand } from '../Logo' +import { HeaderGridCellNarrowWindowOnly } from './HeaderGridCellNarrowWindowOnly' import { HeaderMenuIcon } from './HeaderMenuIcon' import { HeaderMenuLink } from './HeaderMenuLink' -import { HeaderNarrowScreenOnlyGridCell } from './HeaderNarrowScreenOnlyGridCell' import useIsAfterBreakpoint from '../common/useIsAfterBreakpoint' export type HeaderProps = { @@ -138,6 +138,6 @@ const HeaderRoot = forwardRef( HeaderRoot.displayName = 'Header' export const Header = Object.assign(HeaderRoot, { + GridCellNarrowWindowOnly: HeaderGridCellNarrowWindowOnly, MenuLink: HeaderMenuLink, - NarrowScreenOnlyGridCell: HeaderNarrowScreenOnlyGridCell, }) diff --git a/packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.test.tsx b/packages/react/src/Header/HeaderGridCellNarrowWindowOnly.test.tsx similarity index 63% rename from packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.test.tsx rename to packages/react/src/Header/HeaderGridCellNarrowWindowOnly.test.tsx index 97182df468..08e552a530 100644 --- a/packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.test.tsx +++ b/packages/react/src/Header/HeaderGridCellNarrowWindowOnly.test.tsx @@ -1,11 +1,11 @@ import { render } from '@testing-library/react' import { createRef } from 'react' -import { HeaderNarrowScreenOnlyGridCell } from './HeaderNarrowScreenOnlyGridCell' +import { HeaderGridCellNarrowWindowOnly } from './HeaderGridCellNarrowWindowOnly' import '@testing-library/jest-dom' describe('Header narrow screen only grid cell', () => { it('renders', () => { - const { container } = render() + const { container } = render() const component = container.querySelector(':only-child') @@ -14,7 +14,7 @@ describe('Header narrow screen only grid cell', () => { }) it('renders a Grid.Cell', () => { - const { container } = render() + const { container } = render() const component = container.querySelector(':only-child') @@ -22,25 +22,25 @@ describe('Header narrow screen only grid cell', () => { }) it('renders a design system BEM class name', () => { - const { container } = render() + const { container } = render() const component = container.querySelector(':only-child') - expect(component).toHaveClass('ams-header__narrow-screen-only-grid-cell') + expect(component).toHaveClass('ams-header__grid-cell-narrow-window-only') }) it('renders an additional class name', () => { - const { container } = render() + const { container } = render() const component = container.querySelector(':only-child') - expect(component).toHaveClass('ams-header__narrow-screen-only-grid-cell extra') + expect(component).toHaveClass('ams-header__grid-cell-narrow-window-only extra') }) it('supports ForwardRef in React', () => { const ref = createRef() - const { container } = render() + const { container } = render() const component = container.querySelector(':only-child') diff --git a/packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.tsx b/packages/react/src/Header/HeaderGridCellNarrowWindowOnly.tsx similarity index 50% rename from packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.tsx rename to packages/react/src/Header/HeaderGridCellNarrowWindowOnly.tsx index 9c6686f3ca..148f1f6b11 100644 --- a/packages/react/src/Header/HeaderNarrowScreenOnlyGridCell.tsx +++ b/packages/react/src/Header/HeaderGridCellNarrowWindowOnly.tsx @@ -2,12 +2,12 @@ import clsx from 'clsx' import { ForwardedRef, forwardRef } from 'react' import { Grid, GridCellProps } from '../Grid' -export const HeaderNarrowScreenOnlyGridCell = forwardRef( +export const HeaderGridCellNarrowWindowOnly = forwardRef( ({ children, className, ...restProps }: GridCellProps, ref: ForwardedRef) => ( - + {children} ), ) -HeaderNarrowScreenOnlyGridCell.displayName = 'Header.NarrowScreenOnlyGridCell' +HeaderGridCellNarrowWindowOnly.displayName = 'Header.GridCellNarrowWindowOnly' diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 88216d02b1..650a2b6823 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -48,11 +48,11 @@ export const Default: Story = { ], children: ( - + Inloggen - + Onderdelen From 5ee9552f9c8ec7e5156c7e8377030c002e9aff22 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 18 Dec 2024 10:03:34 +0100 Subject: [PATCH 51/59] Revert deleting margin inline start --- packages/css/src/components/header/header.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index 8c2fe3ae65..a716b2bae4 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -62,6 +62,7 @@ display: flex; flex-wrap: wrap; justify-content: flex-end; + margin-inline-start: auto; pointer-events: auto; // Set pointer events back to auto to allow the menu to be activated row-gap: var(--ams-header-menu-row-gap); From e324cd496259665eddae90d42b2fed056c713db2 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 18 Dec 2024 10:08:12 +0100 Subject: [PATCH 52/59] Update main README --- packages/css/src/components/header/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/css/src/components/header/README.md b/packages/css/src/components/header/README.md index 380a585d2a..b2543cfbd2 100644 --- a/packages/css/src/components/header/README.md +++ b/packages/css/src/components/header/README.md @@ -12,10 +12,10 @@ Includes the name of the application if it is not the general website. - The Header is important because it conveys our corporate identity and is the first thing people see. Using it consistently helps users recognize and trust the website. - It is the same on every page of the application. -- The page menu can contain a maximum of 5 items. +- The inline menu can contain a maximum of 5 items. The last two will often be ‘Search’ and ‘Menu’. -- Labels should be short to ensure the menu fits on one line, even on medium-wide screens. -- An icon can be added to the end of a link to make its function easier to find. +- Labels should be short to allow the menu to fit on one line as much as possible. +- An icon can be added to the end of a link to make its destination easier to guess. ## References From fec77f79407d8862778d0815ce01358535dd7376 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 18 Dec 2024 11:28:33 +0100 Subject: [PATCH 53/59] Add docs --- packages/css/src/components/header/README.md | 4 +- packages/react/src/Header/Header.tsx | 50 ++++++++++--------- .../src/components/Header/Header.docs.mdx | 30 ++++++++++- .../src/components/Header/Header.stories.tsx | 40 +++++++++++++-- 4 files changed, 93 insertions(+), 31 deletions(-) diff --git a/packages/css/src/components/header/README.md b/packages/css/src/components/header/README.md index b2543cfbd2..fddfe61435 100644 --- a/packages/css/src/components/header/README.md +++ b/packages/css/src/components/header/README.md @@ -14,7 +14,9 @@ Includes the name of the application if it is not the general website. - It is the same on every page of the application. - The inline menu can contain a maximum of 5 items. The last two will often be ‘Search’ and ‘Menu’. -- Labels should be short to allow the menu to fit on one line as much as possible. +- The 'Menu' button opens a collapsible menu, which has room for more links. +- On narrow windows, links can move from the inline menu to the collapsible one. +- Labels should be short to help the inline menu fit on a single line whenever possible. - An icon can be added to the end of a link to make its destination easier to guess. ## References diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index 7558ab8d25..ccd53b1ff2 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -93,31 +93,33 @@ const HeaderRoot = forwardRef(
          {menuItems} -
        • - -
        • + + + )}
        {children && ( diff --git a/storybook/src/components/Header/Header.docs.mdx b/storybook/src/components/Header/Header.docs.mdx index 59e434476e..8309defb84 100644 --- a/storybook/src/components/Header/Header.docs.mdx +++ b/storybook/src/components/Header/Header.docs.mdx @@ -17,18 +17,44 @@ import { StatusBadge } from "../../docs/components/StatusBadge"; ## Examples -### Without menu button on wide screens +### With moving links - +Links can move from the inline menu to the collapsible one on narrow windows. +A `MenuLink` is hidden by default on narrow windows. +Use `GridCellNarrowWindowOnly` to show that same link in the collapsible menu on narrow windows. + +If you do not want the `MenuLink` to be hidden, use the `fixed` prop. + +In this example, ‘Inloggen’ moves to the collapsible menu, while ‘Zoeken’ does not. + + + +### Without menu button on wide windows + +If you only have a few links, you may not always need a collapsible menu. +Use `noMenuButtonOnWideScreen` to hide the menu button on wide windows. +On narrow windows, the menu button will still be visible, +so do not forget to add the links to the collapsible menu. + + ### Without menu button +In some cases, a collapsible menu might not be necessary. +If the Header has no `children`, the menu button will not appear. +Remember to use the `fixed` prop if you want the inline menu links to stay in place. + ### With custom logo link +The destination and accessible text of the logo link can be customized, +as well as the logo itself. + ### With custom texts +The text of the menu button and the accessible navigation description can be customized. + diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 650a2b6823..e82c3a732e 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -94,18 +94,45 @@ export const Default: Story = { }, } -const WithoutMenuButtonOnWideScreenStoryLinks = [ +export const WithMovingLinks: Story = { + args: { + menuItems: [ + + Inloggen + , + + Zoeken + , + ], + children: ( + + + + Inloggen + + + + + Regular collapsible menu link + + + + ), + }, +} + +const WithoutMenuButtonOnWideWindowStoryLinks = [ { label: 'Stad', href: '#' }, { label: 'Techniek', href: '#' }, { label: 'Historie', href: '#' }, { label: 'Duurzaamheid', href: '#' }, ] -export const WithoutMenuButtonOnWideScreen: Story = { +export const WithoutMenuButtonOnWideWindow: Story = { args: { brandName: 'Aan de Amsterdamse grachten', menuItems: [ - ...WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( + ...WithoutMenuButtonOnWideWindowStoryLinks.map(({ label, href }) => ( {label} @@ -117,7 +144,7 @@ export const WithoutMenuButtonOnWideScreen: Story = { noMenuButtonOnWideScreen: true, children: ( - {WithoutMenuButtonOnWideScreenStoryLinks.map(({ label, href }) => ( + {WithoutMenuButtonOnWideWindowStoryLinks.map(({ label, href }) => ( {label} @@ -130,6 +157,11 @@ export const WithoutMenuButtonOnWideScreen: Story = { export const WithoutMenuButton: Story = { args: { brandName: 'Mijn Amsterdam', + menuItems: ( + + Inloggen + + ), }, } From e0c637debfa8cf4986a8cab580c3e36c94c0bad4 Mon Sep 17 00:00:00 2001 From: alimpens Date: Wed, 18 Dec 2024 11:33:12 +0100 Subject: [PATCH 54/59] Rename noMenuButtonOnWideWindow --- packages/css/src/components/header/header.scss | 2 +- packages/react/src/Header/Header.test.tsx | 6 +++--- packages/react/src/Header/Header.tsx | 8 ++++---- storybook/src/components/Header/Header.docs.mdx | 2 +- storybook/src/components/Header/Header.stories.tsx | 2 +- storybook/src/pages/amsterdam/common/AppHeader.tsx | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/css/src/components/header/header.scss b/packages/css/src/components/header/header.scss index a716b2bae4..e542ba6686 100644 --- a/packages/css/src/components/header/header.scss +++ b/packages/css/src/components/header/header.scss @@ -112,7 +112,7 @@ } } -.ams-header__mega-menu-button-item--hide-on-wide-screen { +.ams-header__mega-menu-button-item--hide-on-wide-window { @media screen and (min-width: $ams-breakpoint-wide) { display: none; } diff --git a/packages/react/src/Header/Header.test.tsx b/packages/react/src/Header/Header.test.tsx index c596383552..891f8554ed 100644 --- a/packages/react/src/Header/Header.test.tsx +++ b/packages/react/src/Header/Header.test.tsx @@ -162,12 +162,12 @@ describe('Header', () => { expect(component).toBeInTheDocument() }) - it('renders the correct class when noMenuButtonOnWideScreen is true', () => { - render(
        Test
        ) + it('renders the correct class when noMenuButtonOnWideWindow is true', () => { + render(
        Test
        ) const component = screen.getByRole('listitem') - expect(component).toHaveClass('ams-header__mega-menu-button-item--hide-on-wide-screen') + expect(component).toHaveClass('ams-header__mega-menu-button-item--hide-on-wide-window') }) it('opens and closes the mega menu', async () => { diff --git a/packages/react/src/Header/Header.tsx b/packages/react/src/Header/Header.tsx index ccd53b1ff2..544bf689c8 100644 --- a/packages/react/src/Header/Header.tsx +++ b/packages/react/src/Header/Header.tsx @@ -31,7 +31,7 @@ export type HeaderProps = { /** The accessible label for the navigation section. */ navigationLabel?: string /** Whether the menu button is visible on wide screens. */ - noMenuButtonOnWideScreen?: boolean + noMenuButtonOnWideWindow?: boolean } & HTMLAttributes const HeaderRoot = forwardRef( @@ -46,7 +46,7 @@ const HeaderRoot = forwardRef( menuItems, menuButtonText = 'Menu', navigationLabel = 'Hoofdnavigatie', - noMenuButtonOnWideScreen, + noMenuButtonOnWideWindow, ...restProps }: HeaderProps, ref: ForwardedRef, @@ -57,7 +57,7 @@ const HeaderRoot = forwardRef( useEffect(() => { // Close the menu when the menu button disappears - if (noMenuButtonOnWideScreen && isWideWindow) { + if (noMenuButtonOnWideWindow && isWideWindow) { setOpen(false) } }, [isWideWindow]) @@ -95,7 +95,7 @@ const HeaderRoot = forwardRef( {menuItems} {children && (
      • diff --git a/proprietary/tokens/src/components/ams/header.tokens.json b/proprietary/tokens/src/components/ams/header.tokens.json index df59ebfeda..d95b0d98b0 100644 --- a/proprietary/tokens/src/components/ams/header.tokens.json +++ b/proprietary/tokens/src/components/ams/header.tokens.json @@ -29,9 +29,9 @@ "color": { "value": "{ams.link-appearance.color}" }, "column-gap": { "value": "{ams.space.xs}" }, "font-family": { "value": "{ams.text.font-family}" }, - "font-size": { "value": "{ams.text.level.6.font-size}" }, + "font-size": { "value": "{ams.text.level.5.font-size}" }, "font-weight": { "value": "{ams.text.font-weight.normal}" }, - "line-height": { "value": "{ams.text.level.6.line-height}" }, + "line-height": { "value": "{ams.text.level.5.line-height}" }, "outline-offset": { "value": "{ams.focus.outline-offset}" }, "padding-block": { "value": "{ams.space.xs}" }, "hover": { From 7795bd3138c10321d14194ecec6d99cef42d5651 Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 20 Dec 2024 16:44:54 +0100 Subject: [PATCH 57/59] Do not use Inloggen for disappearing links --- .../src/components/Header/Header.docs.mdx | 2 +- .../src/components/Header/Header.stories.tsx | 20 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/storybook/src/components/Header/Header.docs.mdx b/storybook/src/components/Header/Header.docs.mdx index 5857b56fc2..5bafc981cd 100644 --- a/storybook/src/components/Header/Header.docs.mdx +++ b/storybook/src/components/Header/Header.docs.mdx @@ -25,7 +25,7 @@ Use `GridCellNarrowWindowOnly` to show that same link in the collapsible menu on If you do not want the `MenuLink` to be hidden, use the `fixed` prop. -In this example, ‘Inloggen’ moves to the collapsible menu, while ‘Zoeken’ does not. +In this example, ‘English’ moves to the collapsible menu, while ‘Zoeken’ does not. diff --git a/storybook/src/components/Header/Header.stories.tsx b/storybook/src/components/Header/Header.stories.tsx index 346f6abd90..ac3fc5be6f 100644 --- a/storybook/src/components/Header/Header.stories.tsx +++ b/storybook/src/components/Header/Header.stories.tsx @@ -39,8 +39,8 @@ export const Default: Story = { args: { brandName: 'Data Amsterdam', menuItems: [ - - Inloggen + + English , Zoeken @@ -50,7 +50,9 @@ export const Default: Story = { - Inloggen + + English + @@ -97,8 +99,8 @@ export const Default: Story = { export const WithMovingLinks: Story = { args: { menuItems: [ - - Inloggen + + English , Zoeken @@ -108,7 +110,9 @@ export const WithMovingLinks: Story = { - Inloggen + + English + @@ -181,7 +185,9 @@ export const WithCustomTexts: Story = { - Inloggen + + English + From 0181ec1ca1933372715f115c7ccfee0b581dfcb9 Mon Sep 17 00:00:00 2001 From: alimpens Date: Fri, 20 Dec 2024 16:57:29 +0100 Subject: [PATCH 58/59] Do not use grid space token for logo size --- proprietary/tokens/src/components/ams/logo.tokens.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proprietary/tokens/src/components/ams/logo.tokens.json b/proprietary/tokens/src/components/ams/logo.tokens.json index 8188991c62..5dc3df14cf 100644 --- a/proprietary/tokens/src/components/ams/logo.tokens.json +++ b/proprietary/tokens/src/components/ams/logo.tokens.json @@ -1,7 +1,10 @@ { "ams": { "logo": { - "block-size": { "value": "{ams.space.grid.md}" }, + "block-size": { + "value": "clamp(1rem, calc(0.375rem + 3.125vw), 3.5rem)", + "comment": "This is the same size as Grid space medium" + }, "emblem": { "color": { "value": "{ams.brand.color.red.60}" } }, "min-block-size": { "value": "2.5rem" }, "title": { "color": { "value": "{ams.brand.color.red.60}" } }, From cf8448769cf73b9d026213a852e08404b258e787 Mon Sep 17 00:00:00 2001 From: Vincent Smedinga Date: Fri, 20 Dec 2024 17:17:51 +0100 Subject: [PATCH 59/59] Allow custom headers for some kinds of pages --- packages/css/src/components/header/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/css/src/components/header/README.md b/packages/css/src/components/header/README.md index fddfe61435..c0c9a345c1 100644 --- a/packages/css/src/components/header/README.md +++ b/packages/css/src/components/header/README.md @@ -11,7 +11,7 @@ Includes the name of the application if it is not the general website. - It includes the logo of the City or the organization, the site title (except for the general website), and a menu with links to commonly used pages. - The Header is important because it conveys our corporate identity and is the first thing people see. Using it consistently helps users recognize and trust the website. -- It is the same on every page of the application. +- The Header is the same on every page of the application, although full-screen pages may hide it, e.g. a video or a map. - The inline menu can contain a maximum of 5 items. The last two will often be ‘Search’ and ‘Menu’. - The 'Menu' button opens a collapsible menu, which has room for more links.