Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"ams": {
"progress-list": {
"step": {
"padding-block-end": { "value": "{ams.space.l}" }
},
"progress": {
"padding-inline-start": { "value": "{ams.space.l}" },
"padding-inline-end": { "value": "{ams.space.l}" }
},
"marker": {
"background-color": { "value": "{ams.color.feedback.success}" },
"margin-inline-start": { "value": "1.25rem" },
"inline-size": { "value": "1.25rem" },
"block-size": { "value": "1.25rem" },
"heading-2": {
"font-size": { "value": "{ams.typography.heading.2.font-size}" },
"line-height": { "value": "{ams.typography.heading.2.line-height}" }
},
"heading-3": {
"font-size": { "value": "{ams.typography.heading.3.font-size}" },
"line-height": { "value": "{ams.typography.heading.3.line-height}" }
},
"heading-4": {
"font-size": { "value": "{ams.typography.heading.4.font-size}" },
"line-height": { "value": "{ams.typography.heading.4.line-height}" }
}
},
"connector": {
"background-color": { "value": "{ams.color.feedback.success}" },
"inline-size": { "value": "{ams.border.width.m}" }
},
"heading": {
"padding-block-end": { "value": "{ams.space.s}" }
}
}
}
}
1 change: 1 addition & 0 deletions packages/css/src/components/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

/* Append here */
@use "progress-list/progress-list";
@use "accordion/accordion";
@use "action-group/action-group";
@use "alert/alert";
Expand Down
3 changes: 3 additions & 0 deletions packages/css/src/components/progress-list/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!-- @license CC0-1.0 -->

# Progress List
100 changes: 100 additions & 0 deletions packages/css/src/components/progress-list/progress-list.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/

@use "../../common/hyphenation" as *;
@use "../../common/text-rendering" as *;

@mixin reset-ol {
list-style-type: none;
margin-block: 0;
padding-inline-start: 0;
}

.ams-progress-list__connector {
background-color: var(--ams-progress-list-connector-background-color);
block-size: 100%;
grid-column: 1; // This is used to position the connector in the middle of the marker.
grid-row: 1; // This is used to position the connector in the middle of the marker.
inline-size: var(--ams-progress-list-connector-inline-size);

&-heading-2 {
margin-block-start: calc(
(var(--ams-progress-list-marker-heading-2-line-height) * var(--ams-progress-list-marker-heading-2-font-size)) / 2
);
}

&-heading-3 {
margin-block-start: calc(
(var(--ams-progress-list-marker-heading-3-line-height) * var(--ams-progress-list-marker-heading-3-font-size)) / 2
);
}

&-heading-4 {
margin-block-start: calc(
(var(--ams-progress-list-marker-heading-4-line-height) * var(--ams-progress-list-marker-heading-4-font-size)) / 2
);
}
}

.ams-progress-list {
@include hyphenation;
@include text-rendering;
@include reset-ol;

list-style: none;
}

.ams-progress-list__step {
display: flex;

&:last-child .ams-progress-list__connector {
display: none;
}

&:not(:last-child) > div:last-child {
padding-block-end: var(--ams-progress-list-step-padding-block-end);
}
}

.ams-progress-list__progress {
align-items: start;
display: grid;
justify-items: center;
padding-inline-end: var(--ams-progress-list-progress-padding-inline-end);
padding-inline-start: var(--ams-progress-list-progress-padding-inline-start);
}

.ams-progress-list__marker {
align-items: center;
display: flex;
grid-column: 1; // This is used to position the marker in the middle of the connector.
grid-row: 1; // This is used to position the marker in the middle of the connector.

&-heading-2 {
font-size: var(--ams-progress-list-marker-heading-2-font-size);
line-height: var(--ams-progress-list-marker-heading-2-line-height);
}

&-heading-3 {
font-size: var(--ams-progress-list-marker-heading-3-font-size);
line-height: var(--ams-progress-list-marker-heading-3-line-height);
}

&-heading-4 {
font-size: var(--ams-progress-list-marker-heading-4-font-size);
line-height: var(--ams-progress-list-marker-heading-4-line-height);
}

&::after {
content: "\00200B"; // This zero width space enables baseline alignment.
}
}

.ams-progress-list__marker-shape {
aspect-ratio: 1 / 1;
background-color: var(--ams-progress-list-marker-background-color);
border-radius: 100%;
inline-size: 1em;
}
47 changes: 47 additions & 0 deletions packages/react/src/ProgressList/ProgressList.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/

import { render } from '@testing-library/react'
import { createRef } from 'react'

import { ProgressList } from './ProgressList'
import '@testing-library/jest-dom'

describe('ProgressList', () => {
it('renders', () => {
const { container } = render(<ProgressList headingLevel={3} />)

const component = container.querySelector(':only-child')

expect(component).toBeInTheDocument()
expect(component).toBeVisible()
})

it('renders a design system BEM class name', () => {
const { container } = render(<ProgressList headingLevel={3} />)

const component = container.querySelector(':only-child')

expect(component).toHaveClass('ams-progress-list')
})

it('renders an extra class name', () => {
const { container } = render(<ProgressList className="extra" headingLevel={3} />)

const component = container.querySelector(':only-child')

expect(component).toHaveClass('ams-progress-list extra')
})

it('supports ForwardRef in React', () => {
const ref = createRef<HTMLOListElement>()

const { container } = render(<ProgressList headingLevel={3} ref={ref} />)

const component = container.querySelector(':only-child')

expect(ref.current).toBe(component)
})
})
34 changes: 34 additions & 0 deletions packages/react/src/ProgressList/ProgressList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/

import type { ForwardedRef, HTMLAttributes, PropsWithChildren } from 'react'

import { clsx } from 'clsx'
import { forwardRef } from 'react'

import type { HeadingProps } from '../Heading'

import ProgressListContext from './ProgressListContext'
import { ProgressListStep } from './ProgressListStep'

export type ProgressListProps = {
headingLevel: HeadingProps['level']
} & PropsWithChildren<HTMLAttributes<HTMLOListElement>>

/**
* @see {@link https://designsystem.amsterdam/?path=/docs/components-TODO-ADD-GROUP-progress-list--docs ProgressList docs at Amsterdam Design System}
*/
const ProgressListRoot = forwardRef(
({ children, className, headingLevel, ...restProps }: ProgressListProps, ref: ForwardedRef<HTMLOListElement>) => (
<ProgressListContext.Provider value={{ headingLevel: headingLevel }}>
<ol {...restProps} className={clsx('ams-progress-list', className)} ref={ref}>
{children}
</ol>
</ProgressListContext.Provider>
),
)

export const ProgressList = Object.assign(ProgressListRoot, { Step: ProgressListStep })
ProgressListRoot.displayName = 'ProgressList'
22 changes: 22 additions & 0 deletions packages/react/src/ProgressList/ProgressListContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/

import { createContext } from 'react'

import type { ProgressListProps } from './ProgressList'

export type ProgressListContextValue = {
headingLevel: ProgressListProps['headingLevel']
}

const defaultValues: ProgressListContextValue = {
// Level 2 is set here, but it is never used.
// headingLevel is a required prop in ProgressList, which always overwrites it.
headingLevel: 2,
}

const ProgressListContext = createContext(defaultValues)

export default ProgressListContext
42 changes: 42 additions & 0 deletions packages/react/src/ProgressList/ProgressListStep.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/
import type { ForwardedRef, HTMLAttributes, PropsWithChildren } from 'react'

import { clsx } from 'clsx'
import { forwardRef, useContext } from 'react'

import { Heading } from '../Heading'
import ProgressListContext from './ProgressListContext'

export type ProgressListStepProps = {
heading: string
} & PropsWithChildren<HTMLAttributes<HTMLElement>>

export const ProgressListStep = forwardRef(
({ children, className, heading, ...restProps }: ProgressListStepProps, ref: ForwardedRef<HTMLLIElement>) => {
const { headingLevel } = useContext(ProgressListContext)

return (
<li className={clsx('ams-progress-list__step', className)} {...restProps} ref={ref}>
<div className="ams-progress-list__progress">
<div className={clsx('ams-progress-list__marker', `ams-progress-list__marker-heading-${headingLevel}`)}>
<span className="ams-progress-list__marker-shape" />
</div>
<span
className={clsx('ams-progress-list__connector', `ams-progress-list__connector-heading-${headingLevel}`)}
/>
</div>
<div>
<Heading className="ams-progress-list__heading" level={headingLevel}>
{heading}
</Heading>
<div className="ams-progress-list__content">{children}</div>
</div>
</li>
)
},
)

ProgressListStep.displayName = 'ProgressList.Step'
7 changes: 7 additions & 0 deletions packages/react/src/ProgressList/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!-- @license CC0-1.0 -->

# React Progress List component

[Progress List documentation](../../../css/src/components/progress-list/README.md)

# TODO: Write documentation
7 changes: 7 additions & 0 deletions packages/react/src/ProgressList/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/

export { ProgressList } from './ProgressList'
export type { ProgressListProps } from './ProgressList'
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export * from './PageHeading'
export * from './Pagination'
export * from './Paragraph'
export * from './PasswordInput'
export * from './ProgressList'
export * from './Radio'
export * from './Row'
export * from './SearchField'
Expand Down
13 changes: 13 additions & 0 deletions storybook/src/components/ProgressList/ProgressList.docs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{/* @license CC0-1.0 */}

import { Controls, Markdown, Meta, Primary } from "@storybook/addon-docs/blocks";
import * as ProgressListStories from "./ProgressList.stories.tsx";
import README from "../../../../packages/css/src/components/progress-list/README.md?raw";

<Meta of={ProgressListStories} />

<Markdown>{README}</Markdown>

<Primary />

<Controls />
49 changes: 49 additions & 0 deletions storybook/src/components/ProgressList/ProgressList.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/

import type { Meta, StoryObj } from '@storybook/react-vite'

import { ProgressList, UnorderedList } from '@amsterdam/design-system-react/src'

const meta = {
title: 'Progress List',
component: ProgressList,
args: {
children: [
<ProgressList.Step heading="2025" key={0}>
<UnorderedList>
<UnorderedList.Item>Start aanleg nieuwe openbare ruimte rondom HEROS (LEBO blok C).</UnorderedList.Item>
<UnorderedList.Item>Oplevering lammetjesfontein.</UnorderedList.Item>
</UnorderedList>
</ProgressList.Step>,
<ProgressList.Step heading="2026" key={1}>
<UnorderedList>
<UnorderedList.Item>Start aanleg nieuwe openbare ruimte rondom HEROS (LEBO blok C).</UnorderedList.Item>
<UnorderedList.Item>Oplevering lammetjesfontein.</UnorderedList.Item>
</UnorderedList>
</ProgressList.Step>,
<ProgressList.Step heading="2027" key={2}>
<UnorderedList>
<UnorderedList.Item>Start aanleg nieuwe openbare ruimte rondom HEROS (LEBO blok C).</UnorderedList.Item>
<UnorderedList.Item>Oplevering lammetjesfontein.</UnorderedList.Item>
</UnorderedList>
</ProgressList.Step>,
<ProgressList.Step heading="2028 - Afronding" key={3} />,
],
headingLevel: 3,
},
argTypes: {
headingLevel: {
control: 'radio',
options: [2, 3, 4], // Level 1 is deprecated, the argTypes object can be removed when Level 1 is removed
},
},
} satisfies Meta<typeof ProgressList>

export default meta

type Story = StoryObj<typeof meta>

export const Default: Story = {}
Loading
Loading