diff --git a/packages/css/src/components/description-list/description-list.scss b/packages/css/src/components/description-list/description-list.scss index b21b7cb7a4..ce8a678d3d 100644 --- a/packages/css/src/components/description-list/description-list.scss +++ b/packages/css/src/components/description-list/description-list.scss @@ -10,6 +10,10 @@ margin-block: 0; } +@mixin reset-dd { + margin-inline: 0; +} + .ams-description-list { box-sizing: border-box; color: var(--ams-description-list-color); @@ -25,19 +29,23 @@ } @media screen and (min-width: $ams-breakpoint-medium) { - .ams-description-list { + .ams-description-list, + .ams-description-list__section { grid-template-columns: auto 1fr; } - .ams-description-list--terms-width-sm { + .ams-description-list--terms-width-sm, + .ams-description-list--terms-width-sm .ams-description-list__section { grid-template-columns: 1fr 4fr; } - .ams-description-list--terms-width-md { + .ams-description-list--terms-width-md, + .ams-description-list--terms-width-md .ams-description-list__section { grid-template-columns: 1fr 2fr; } - .ams-description-list--terms-width-lg { + .ams-description-list--terms-width-lg, + .ams-description-list--terms-width-lg .ams-description-list__section { grid-template-columns: 1fr 1fr; } } @@ -54,8 +62,22 @@ } } -@mixin reset-dd { - margin-inline: 0; +.ams-description-list__section { + @media screen and (min-width: $ams-breakpoint-medium) { + column-gap: var(--ams-description-list-column-gap); + display: grid; + grid-column: span 2; + + > :only-of-type { + grid-row: 1 / 9; + } + } +} + +// Aligns terms and descriptions in a section to the grid of the list. +// The extra class selector increases specificity to match earlier declarations. +.ams-description-list .ams-description-list__section { + grid-template-columns: subgrid; } .ams-description-list__description { diff --git a/packages/react/src/DescriptionList/DescriptionList.tsx b/packages/react/src/DescriptionList/DescriptionList.tsx index 7f63876075..ef7069c7b5 100644 --- a/packages/react/src/DescriptionList/DescriptionList.tsx +++ b/packages/react/src/DescriptionList/DescriptionList.tsx @@ -7,6 +7,7 @@ import clsx from 'clsx' import { forwardRef } from 'react' import type { ForwardedRef, HTMLAttributes, PropsWithChildren } from 'react' import { DescriptionListDescription } from './DescriptionListDescription' +import { DescriptionListSection } from './DescriptionListSection' import { DescriptionListTerm } from './DescriptionListTerm' export const descriptionListTermsWidths = ['sm', 'md', 'lg'] as const @@ -42,6 +43,7 @@ const DescriptionListRoot = forwardRef( DescriptionListRoot.displayName = 'DescriptionList' export const DescriptionList = Object.assign(DescriptionListRoot, { - Term: DescriptionListTerm, Description: DescriptionListDescription, + Section: DescriptionListSection, + Term: DescriptionListTerm, }) diff --git a/packages/react/src/DescriptionList/DescriptionListSection.test.tsx b/packages/react/src/DescriptionList/DescriptionListSection.test.tsx new file mode 100644 index 0000000000..bd12f1c927 --- /dev/null +++ b/packages/react/src/DescriptionList/DescriptionListSection.test.tsx @@ -0,0 +1,41 @@ +import { render } from '@testing-library/react' +import { createRef } from 'react' +import { DescriptionList } from './DescriptionList' +import '@testing-library/jest-dom' + +describe('Description List Section', () => { + it('renders', () => { + const { container } = render(Test) + + const component = container.querySelector(':only-child') + + expect(component).toBeInTheDocument() + expect(component).toBeVisible() + }) + + it('renders a design system BEM class name', () => { + const { container } = render(Test) + + const component = container.querySelector(':only-child') + + expect(component).toHaveClass('ams-description-list__section') + }) + + it('renders an additional class name', () => { + const { container } = render(Test) + + const component = container.querySelector(':only-child') + + expect(component).toHaveClass('ams-description-list__section extra') + }) + + it('supports ForwardRef in React', () => { + const ref = createRef() + + const { container } = render(Test) + + const component = container.querySelector(':only-child') + + expect(ref.current).toBe(component) + }) +}) diff --git a/packages/react/src/DescriptionList/DescriptionListSection.tsx b/packages/react/src/DescriptionList/DescriptionListSection.tsx new file mode 100644 index 0000000000..81b2771ded --- /dev/null +++ b/packages/react/src/DescriptionList/DescriptionListSection.tsx @@ -0,0 +1,20 @@ +/** + * @license EUPL-1.2+ + * Copyright Gemeente Amsterdam + */ + +import clsx from 'clsx' +import { forwardRef } from 'react' +import type { ForwardedRef, HTMLAttributes, PropsWithChildren } from 'react' + +export type DescriptionListSectionProps = PropsWithChildren> + +export const DescriptionListSection = forwardRef( + ({ children, className, ...restProps }: DescriptionListSectionProps, ref: ForwardedRef) => ( +
+ {children} +
+ ), +) + +DescriptionListSection.displayName = 'DescriptionList.Section' diff --git a/storybook/src/components/DescriptionList/DescriptionList.docs.mdx b/storybook/src/components/DescriptionList/DescriptionList.docs.mdx index 88728215f8..4e34bfd49d 100644 --- a/storybook/src/components/DescriptionList/DescriptionList.docs.mdx +++ b/storybook/src/components/DescriptionList/DescriptionList.docs.mdx @@ -20,6 +20,12 @@ A term may have multiple descriptions. +### Multiple terms + +If multiple terms share a single description, group them in a `DescriptionList.Section` component to ensure proper grid layout. + + + ### Rich description A description can include rich content such as inline formatting, links, paragraphs, and even lists. diff --git a/storybook/src/components/DescriptionList/DescriptionList.stories.tsx b/storybook/src/components/DescriptionList/DescriptionList.stories.tsx index 5430965a8c..81598822ec 100644 --- a/storybook/src/components/DescriptionList/DescriptionList.stories.tsx +++ b/storybook/src/components/DescriptionList/DescriptionList.stories.tsx @@ -54,6 +54,26 @@ export const MultipleDescriptions: Story = { }, } +export const MultipleTerms: Story = { + args: { + termsWidth: 'md', + children: [ + Achternaam, + + De naam die een persoon van zijn of haar ouders krijgt + , + + Voornaam + Roepnaam + Bijnaam + De naam waarmee een persoon wordt aangesproken + , + Geboortedatum, + De datum waarop een persoon is geboren, + ], + }, +} + export const RichDescription: Story = { render: (args) => (