@pnx-mixtape/mxds 0.1.0-rc.0 → 0.1.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/decorators.ts +15 -0
- package/.storybook/preview.ts +4 -1
- package/.storybook/themes/campus-vibrant.css +181 -0
- package/.storybook/themes/civic-formal.css +246 -0
- package/.storybook/{theme-demo.css → themes/demo.css} +14 -7
- package/.storybook/themes/health-services.css +255 -0
- package/.storybook/themes/research-faculty.css +169 -0
- package/dist/build/accordion.css +1 -1
- package/dist/build/base.css +1 -1
- package/dist/build/button.css +1 -1
- package/dist/build/chunks/DropMenu-1baQc8Yc.js +1 -0
- package/dist/build/chunks/InPageNavigation-CVieEl9u.js +1 -0
- package/dist/build/chunks/Popover-Cxg-s5Jn.js +1 -0
- package/dist/build/chunks/Sticky-cWUVSOXN.js +1 -0
- package/dist/build/chunks/{Tabs-Dm8g301e.js → Tabs-DA_Ez8pv.js} +2 -2
- package/dist/build/chunks/polyfills-C7pRt54M.js +1 -0
- package/dist/build/constants.css +1 -1
- package/dist/build/drop-menu.css +1 -1
- package/dist/build/drop-menu.entry.js +1 -1
- package/dist/build/elements.js +1 -1
- package/dist/build/form.css +1 -1
- package/dist/build/grid.css +1 -1
- package/dist/build/header.css +1 -1
- package/dist/build/icon.css +1 -1
- package/dist/build/in-page-alert.css +1 -1
- package/dist/build/in-page-navigation.entry.js +1 -1
- package/dist/build/navigation.css +1 -1
- package/dist/build/pagination.css +1 -1
- package/dist/build/popover.entry.js +1 -1
- package/dist/build/print.css +1 -1
- package/dist/build/progress-indicator.css +1 -0
- package/dist/build/react.js +1 -1
- package/dist/build/side-navigation.css +1 -1
- package/dist/build/sidebar.css +1 -1
- package/dist/build/steps.css +1 -1
- package/dist/build/sticky.entry.js +1 -1
- package/dist/build/tabs.entry.js +1 -1
- package/dist/build/tag.css +1 -1
- package/dist/build/utilities.css +1 -1
- package/dist/types/Atom/DefinitionList/DefinitionList.d.ts +3 -2
- package/dist/types/Atom/DefinitionList/DefinitionList.d.ts.map +1 -1
- package/dist/types/Atom/Heading/Heading.d.ts.map +1 -1
- package/dist/types/Atom/List/List.d.ts +10 -0
- package/dist/types/Atom/List/List.d.ts.map +1 -0
- package/dist/types/Component/DropMenu/Elements/DropMenu.d.ts.map +1 -1
- package/dist/types/Component/InPageNavigation/Elements/InPageNavigation.d.ts +2 -2
- package/dist/types/Component/InPageNavigation/Elements/InPageNavigation.d.ts.map +1 -1
- package/dist/types/Component/InPageNavigation/Hooks/useInPageNavigation.d.ts.map +1 -1
- package/dist/types/Component/ProgressIndicator/ProgressIndicator.d.ts +9 -0
- package/dist/types/Component/ProgressIndicator/ProgressIndicator.d.ts.map +1 -0
- package/dist/types/Component/Tabs/Elements/Tabs.d.ts.map +1 -1
- package/dist/types/Component/Tag/Tag.d.ts +4 -2
- package/dist/types/Component/Tag/Tag.d.ts.map +1 -1
- package/dist/types/Form/Autocomplete/Autocomplete.d.ts.map +1 -1
- package/dist/types/Form/Checkbox/FormCheckbox.d.ts.map +1 -1
- package/dist/types/Form/CheckboxCard/CheckboxCard.d.ts +23 -0
- package/dist/types/Form/CheckboxCard/CheckboxCard.d.ts.map +1 -0
- package/dist/types/Form/Radio/FormRadio.d.ts.map +1 -1
- package/dist/types/Form/RadioCard/RadioCard.d.ts +16 -0
- package/dist/types/Form/RadioCard/RadioCard.d.ts.map +1 -0
- package/dist/types/Form/Select/FormSelect.d.ts.map +1 -1
- package/dist/types/Form/TextInput/FormText.d.ts.map +1 -1
- package/dist/types/Form/Textarea/FormTextarea.d.ts.map +1 -1
- package/dist/types/enums.d.ts +15 -0
- package/dist/types/enums.d.ts.map +1 -1
- package/dist/types/react.d.ts +4 -0
- package/dist/types/react.d.ts.map +1 -1
- package/package.json +7 -8
- package/src/Atom/Background/_background.css +2 -2
- package/src/Atom/Blockquote/_blockquote.css +9 -2
- package/src/Atom/Button/README.md +1 -0
- package/src/Atom/Button/_buttons-styles.css +6 -6
- package/src/Atom/Button/_buttons.css +6 -3
- package/src/Atom/DefinitionList/DefinitionList.stories.ts +39 -2
- package/src/Atom/DefinitionList/DefinitionList.stories.tsx +36 -0
- package/src/Atom/DefinitionList/DefinitionList.tsx +4 -1
- package/src/Atom/DefinitionList/DefinitionListItem.stories.ts +1 -1
- package/src/Atom/DefinitionList/README.md +43 -0
- package/src/Atom/DefinitionList/__snapshots__/DefinitionList.stories.ts.snap +37 -3
- package/src/Atom/DefinitionList/__snapshots__/DefinitionList.stories.tsx.snap +2 -0
- package/src/Atom/DefinitionList/__snapshots__/DefinitionListItem.stories.ts.snap +3 -3
- package/src/Atom/DefinitionList/definition-list-item.twig +1 -0
- package/src/Atom/DefinitionList/definition-list.twig +6 -1
- package/src/Atom/Heading/Heading.tsx +6 -0
- package/src/Atom/Heading/_headings.css +2 -1
- package/src/Atom/Icon/_extended-set.css +30 -30
- package/src/Atom/Icon/_icon.css +37 -36
- package/src/Atom/Image/README.md +2 -2
- package/src/Atom/Image/_image.css +12 -6
- package/src/Atom/List/List.stories.ts +102 -0
- package/src/Atom/List/List.stories.tsx +103 -0
- package/src/Atom/List/List.tsx +22 -0
- package/src/Atom/List/README.md +150 -0
- package/src/Atom/List/__snapshots__/List.stories.ts.snap +43 -0
- package/src/Atom/List/__snapshots__/List.stories.tsx.snap +13 -0
- package/src/Atom/List/_list.css +123 -0
- package/src/Atom/List/list.twig +5 -0
- package/src/Atom/Media/Media.stories.ts +1 -1
- package/src/Atom/Media/__snapshots__/Media.stories.ts.snap +2 -2
- package/src/Atom/Spacing/_spacing.css +24 -24
- package/src/Atom/Table/__snapshots__/Table.stories.ts.snap +24 -4
- package/src/Atom/Table/_table.css +15 -3
- package/src/Atom/Table/table.twig +6 -1
- package/src/Atom/Text/__snapshots__/Text.stories.ts.snap +3 -2
- package/src/Atom/Text/_text-styles.css +21 -1
- package/src/Atom/Text/twig/text-styles-example.twig +3 -2
- package/src/Atom/_base.css +6 -0
- package/src/Atom/_flow.css +1 -1
- package/src/Atom/_generic.css +18 -11
- package/src/Component/Accordion/accordion.css +2 -0
- package/src/Component/Card/__snapshots__/Card.stories.ts.snap +2 -2
- package/src/Component/Card/card.twig +1 -1
- package/src/Component/Carousel/Carousel.stories.ts +46 -23
- package/src/Component/DropMenu/Elements/DropMenu.ts +10 -1
- package/src/Component/InPageAlert/in-page-alert.css +2 -1
- package/src/Component/InPageNavigation/Elements/InPageNavigation.ts +10 -5
- package/src/Component/InPageNavigation/Hooks/useInPageNavigation.ts +10 -5
- package/src/Component/InPageNavigation/InPageNavigation.stories.ts +33 -0
- package/src/Component/InPageNavigation/InPageNavigation.stories.tsx +52 -0
- package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.ts.snap +26 -0
- package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.tsx.snap +2 -0
- package/src/Component/InPageNavigation/twig/content-example-duplicate-headings.twig +20 -0
- package/src/Component/InPageNavigation/twig/in-page-navigation-story-duplicate-headings.twig +2 -0
- package/src/Component/ListItem/__snapshots__/ListItem.stories.ts.snap +4 -4
- package/src/Component/ListItem/list-item.twig +2 -2
- package/src/Component/Navigation/_navigation.css +1 -1
- package/src/Component/Pagination/Pagination.stories.ts +8 -4
- package/src/Component/Pagination/pagination.css +3 -3
- package/src/Component/ProgressIndicator/ProgressIndicator.stories.ts +36 -0
- package/src/Component/ProgressIndicator/ProgressIndicator.stories.tsx +30 -0
- package/src/Component/ProgressIndicator/ProgressIndicator.tsx +60 -0
- package/src/Component/ProgressIndicator/README.md +111 -0
- package/src/Component/ProgressIndicator/__snapshots__/ProgressIndicator.stories.ts.snap +67 -0
- package/src/Component/ProgressIndicator/__snapshots__/ProgressIndicator.stories.tsx.snap +7 -0
- package/src/Component/ProgressIndicator/progress-indicator.css +59 -0
- package/src/Component/ProgressIndicator/progress-indicator.twig +22 -0
- package/src/Component/Sticky/Elements/Sticky.ts +1 -1
- package/src/Component/Sticky/sticky.css +3 -3
- package/src/Component/Tabs/Elements/Tabs.ts +3 -1
- package/src/Component/Tabs/Tabs.stories.ts +55 -21
- package/src/Component/Tabs/__snapshots__/Tabs.stories.ts.snap +2 -2
- package/src/Component/Tag/README.md +52 -26
- package/src/Component/Tag/Tag.stories.tsx +24 -0
- package/src/Component/Tag/Tag.tsx +12 -2
- package/src/Component/Tag/TagItem.stories.ts +25 -0
- package/src/Component/Tag/__snapshots__/Tag.stories.ts.snap +2 -2
- package/src/Component/Tag/__snapshots__/Tag.stories.tsx.snap +2 -0
- package/src/Component/Tag/tag-item.twig +2 -2
- package/src/Component/Tag/tag.css +76 -2
- package/src/Form/Autocomplete/Autocomplete.stories.ts +8 -3
- package/src/Form/Autocomplete/Autocomplete.tsx +1 -4
- package/src/Form/Autocomplete/README.md +26 -26
- package/src/Form/Autocomplete/__snapshots__/Autocomplete.stories.ts.snap +1 -1
- package/src/Form/Autocomplete/__snapshots__/Autocomplete.stories.tsx.snap +1 -1
- package/src/Form/Autocomplete/autocomplete.twig +0 -2
- package/src/Form/Checkbox/FormCheckbox.tsx +2 -8
- package/src/Form/Checkbox/README.md +23 -18
- package/src/Form/Checkbox/input-checkbox.twig +0 -2
- package/src/Form/CheckboxCard/CheckboxCard.stories.ts +90 -0
- package/src/Form/CheckboxCard/CheckboxCard.stories.tsx +43 -0
- package/src/Form/CheckboxCard/CheckboxCard.tsx +102 -0
- package/src/Form/CheckboxCard/README.md +163 -0
- package/src/Form/CheckboxCard/__snapshots__/CheckboxCard.stories.ts.snap +63 -0
- package/src/Form/CheckboxCard/__snapshots__/CheckboxCard.stories.tsx.snap +7 -0
- package/src/Form/CheckboxCard/checkbox-card.twig +29 -0
- package/src/Form/FormItem/__snapshots__/FormItem.stories.ts.snap +2 -2
- package/src/Form/README.md +65 -13
- package/src/Form/Radio/FormRadio.tsx +1 -4
- package/src/Form/Radio/input-radio.twig +0 -2
- package/src/Form/RadioCard/README.md +146 -0
- package/src/Form/RadioCard/RadioCard.stories.ts +95 -0
- package/src/Form/RadioCard/RadioCard.stories.tsx +26 -0
- package/src/Form/RadioCard/RadioCard.tsx +59 -0
- package/src/Form/RadioCard/__snapshots__/RadioCard.stories.ts.snap +69 -0
- package/src/Form/RadioCard/__snapshots__/RadioCard.stories.tsx.snap +3 -0
- package/src/Form/RadioCard/radio-card.twig +20 -0
- package/src/Form/Select/FormSelect.tsx +1 -4
- package/src/Form/Select/input-select.twig +0 -2
- package/src/Form/TextInput/FormText.stories.tsx +14 -0
- package/src/Form/TextInput/FormText.tsx +1 -4
- package/src/Form/TextInput/__snapshots__/FormText.stories.tsx.snap +2 -0
- package/src/Form/TextInput/input-text.twig +0 -2
- package/src/Form/Textarea/FormTextarea.tsx +1 -4
- package/src/Form/Textarea/input-textarea.twig +0 -2
- package/src/Form/Validation.stories.ts +153 -0
- package/src/Form/__snapshots__/Validation.stories.ts.snap +130 -0
- package/src/Form/form.css +155 -28
- package/src/Layout/Grid/grid.css +4 -4
- package/src/Layout/Sidebar/__snapshots__/Sidebar.stories.ts.snap +5 -5
- package/src/Layout/Sidebar/sidebar.css +1 -0
- package/src/Layout/Sidebar/sidebar.twig +1 -1
- package/src/Utility/_layout-utils.css +8 -0
- package/src/Utility/print.css +50 -28
- package/src/Utility/utilities.css +2 -2
- package/src/constants.css +12 -2
- package/src/enums.ts +17 -0
- package/src/react.ts +4 -0
- package/tools/lib/extract-classnames.mjs +0 -0
- package/tools/lint-classnames.mjs +1 -1
- package/dist/build/chunks/DropMenu-Co9jZZ4S.js +0 -1
- package/dist/build/chunks/InPageNavigation-CzR_KZaz.js +0 -1
- package/dist/build/chunks/Popover-De_CQqcd.js +0 -1
- package/dist/build/chunks/Sticky-Kyjykb36.js +0 -1
- package/dist/build/chunks/polyfills-Dokbkxxk.js +0 -1
|
@@ -32,9 +32,10 @@ export default class InPageNavigation extends HTMLElement {
|
|
|
32
32
|
) as HTMLHeadingElement
|
|
33
33
|
this.nav.insertBefore(headingElement, this.menu)
|
|
34
34
|
// Build links if not provided.
|
|
35
|
+
const counts: Record<string, number> = {}
|
|
35
36
|
this.headings.forEach((heading: HTMLHeadingElement) => {
|
|
36
37
|
const listItem: HTMLLIElement = this.generateListItem(heading)
|
|
37
|
-
const link: HTMLAnchorElement = this.generateAnchor(heading)
|
|
38
|
+
const link: HTMLAnchorElement = this.generateAnchor(heading, counts)
|
|
38
39
|
this.items.push({ link, heading })
|
|
39
40
|
listItem.appendChild(link)
|
|
40
41
|
this.menu?.appendChild(listItem)
|
|
@@ -119,16 +120,20 @@ export default class InPageNavigation extends HTMLElement {
|
|
|
119
120
|
return this.menu?.querySelectorAll("a")
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
generatedId(heading: HTMLHeadingElement): string {
|
|
123
|
+
generatedId(heading: HTMLHeadingElement, counts: Record<string, number>): string {
|
|
123
124
|
const string: string | undefined = heading?.textContent?.trim()
|
|
124
|
-
|
|
125
|
+
if (!string) return ""
|
|
126
|
+
const base: string = makeAnchor(string)
|
|
127
|
+
const index: number = counts[base] ?? 0
|
|
128
|
+
counts[base] = index + 1
|
|
129
|
+
return index > 0 ? `${base}-${index}` : base
|
|
125
130
|
}
|
|
126
131
|
|
|
127
|
-
generateAnchor(heading: HTMLHeadingElement): HTMLAnchorElement {
|
|
132
|
+
generateAnchor(heading: HTMLHeadingElement, counts: Record<string, number>): HTMLAnchorElement {
|
|
128
133
|
const { textContent, id } = heading
|
|
129
134
|
const linkText: string | null =
|
|
130
135
|
heading.getAttribute("data-in-page-navigation-title") || textContent
|
|
131
|
-
const targetID: string = id || this.generatedId(heading)
|
|
136
|
+
const targetID: string = id || this.generatedId(heading, counts)
|
|
132
137
|
if (!id) heading.id = targetID
|
|
133
138
|
const item: Element = createElement(
|
|
134
139
|
`<a href="#${targetID}"><span class="mx-icon mx-icon--chevron-right"></span><span>${linkText}</span></a>`,
|
|
@@ -23,17 +23,22 @@ const useInPageNavigation = ({
|
|
|
23
23
|
const context = ref?.current || document.querySelector("section")
|
|
24
24
|
const headingElements: Element[] = [...context.querySelectorAll(levels)]
|
|
25
25
|
|
|
26
|
+
const counts: Record<string, number> = {}
|
|
26
27
|
const values: HeadingType[] = headingElements.map(
|
|
27
|
-
({ textContent: text, id, localName }: Element) =>
|
|
28
|
-
(
|
|
29
|
-
|
|
28
|
+
({ textContent: text, id, localName }: Element) => {
|
|
29
|
+
const base: string = makeAnchor(text)
|
|
30
|
+
const index: number = counts[base] ?? 0
|
|
31
|
+
counts[base] = index + 1
|
|
32
|
+
return {
|
|
33
|
+
id: id || (index > 0 ? `${base}-${index}` : base),
|
|
30
34
|
text: text,
|
|
31
35
|
level: localName,
|
|
32
|
-
}
|
|
36
|
+
} as HeadingType
|
|
37
|
+
},
|
|
33
38
|
)
|
|
34
39
|
setHeadings([...new Set(values)])
|
|
35
40
|
headingElements.forEach((heading: HTMLHeadingElement, index: number): void => {
|
|
36
|
-
|
|
41
|
+
heading.id = values[index].id
|
|
37
42
|
})
|
|
38
43
|
}, [ref, levels])
|
|
39
44
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Meta, StoryObj } from "@storybook/html-vite"
|
|
2
|
+
import { expect } from "storybook/test"
|
|
2
3
|
import Component from "./twig/in-page-navigation-story.twig"
|
|
4
|
+
import Default from "./twig/in-page-navigation-story-duplicate-headings.twig"
|
|
3
5
|
import Other from "./twig/in-page-navigation-selector.twig"
|
|
4
6
|
import "./in-page-navigation.css"
|
|
5
7
|
import "./Elements/InPageNavigation"
|
|
@@ -119,6 +121,37 @@ export const NoHeadings: Story = {
|
|
|
119
121
|
},
|
|
120
122
|
}
|
|
121
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Duplicate heading text: the javascript ensures generated ids stay unique.
|
|
126
|
+
*/
|
|
127
|
+
export const IdenticalHeadings: Story = {
|
|
128
|
+
args: {
|
|
129
|
+
items: "",
|
|
130
|
+
levels: [HeadingTypes.TWO, HeadingTypes.THREE],
|
|
131
|
+
},
|
|
132
|
+
render: args => Default(args),
|
|
133
|
+
play: async ({ canvasElement, step }) => {
|
|
134
|
+
const links: HTMLAnchorElement[] = [
|
|
135
|
+
...canvasElement.querySelectorAll<HTMLAnchorElement>(".mx-in-page-navigation a"),
|
|
136
|
+
]
|
|
137
|
+
const ids: string[] = links.map(({ hash }: HTMLAnchorElement) => hash.slice(1))
|
|
138
|
+
|
|
139
|
+
await step("Repeated heading text is suffixed, the first stays unsuffixed", async () => {
|
|
140
|
+
await expect(ids.filter((id: string) => id.startsWith("identical-heading"))).toEqual([
|
|
141
|
+
"identical-heading",
|
|
142
|
+
"identical-heading-1",
|
|
143
|
+
"identical-heading-2",
|
|
144
|
+
])
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
await step("Every link targets a heading that exists", async () => {
|
|
148
|
+
const missing: string[] = ids.filter((id: string) => !canvasElement.querySelector(`#${id}`))
|
|
149
|
+
await expect(missing).toEqual([])
|
|
150
|
+
await expect(new Set(ids).size).toBe(ids.length)
|
|
151
|
+
})
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
|
|
122
155
|
/**
|
|
123
156
|
* Generated items from anywhere in the page: page content contains headings, but not within the container element.
|
|
124
157
|
*/
|
|
@@ -5,6 +5,7 @@ import Text from "../../Atom/Text/Text"
|
|
|
5
5
|
import Component from "./InPageNavigation"
|
|
6
6
|
import "./in-page-navigation.css"
|
|
7
7
|
import { Page } from "../../../.storybook-react/decorators"
|
|
8
|
+
import { expect } from "storybook/test"
|
|
8
9
|
|
|
9
10
|
const meta: Meta<typeof Component> = {
|
|
10
11
|
tags: ["autodocs"],
|
|
@@ -76,3 +77,54 @@ const meta: Meta<typeof Component> = {
|
|
|
76
77
|
}
|
|
77
78
|
export default meta
|
|
78
79
|
export const InPageNavigation: StoryObj<typeof Component> = {}
|
|
80
|
+
|
|
81
|
+
export const IdenticalHeadings: StoryObj<typeof Component> = {
|
|
82
|
+
render: () => (
|
|
83
|
+
<div className="mx-vertical-flow">
|
|
84
|
+
<Component />
|
|
85
|
+
<section className="mx-vertical-flow mx-rich-text">
|
|
86
|
+
<Heading>Identical heading</Heading>
|
|
87
|
+
<Text>
|
|
88
|
+
In publishing and graphic design, lorem ipsum is a placeholder text commonly used to
|
|
89
|
+
demonstrate the visual form of a document without relying on meaningful content. Replacing
|
|
90
|
+
the actual content with placeholder text allows designers to design the form of the
|
|
91
|
+
content before the content itself has been produced.
|
|
92
|
+
</Text>
|
|
93
|
+
<Heading>Identical heading</Heading>
|
|
94
|
+
<Text>
|
|
95
|
+
In publishing and graphic design, lorem ipsum is a placeholder text commonly used to
|
|
96
|
+
demonstrate the visual form of a document without relying on meaningful content. Replacing
|
|
97
|
+
the actual content with placeholder text allows designers to design the form of the
|
|
98
|
+
content before the content itself has been produced.
|
|
99
|
+
</Text>
|
|
100
|
+
<Heading>Identical heading</Heading>
|
|
101
|
+
<Text>
|
|
102
|
+
In publishing and graphic design, lorem ipsum is a placeholder text commonly used to
|
|
103
|
+
demonstrate the visual form of a document without relying on meaningful content. Replacing
|
|
104
|
+
the actual content with placeholder text allows designers to design the form of the
|
|
105
|
+
content before the content itself has been produced.
|
|
106
|
+
</Text>
|
|
107
|
+
</section>
|
|
108
|
+
</div>
|
|
109
|
+
),
|
|
110
|
+
play: async ({ canvasElement, step }) => {
|
|
111
|
+
const links: HTMLAnchorElement[] = [
|
|
112
|
+
...canvasElement.querySelectorAll<HTMLAnchorElement>(".mx-in-page-navigation a"),
|
|
113
|
+
]
|
|
114
|
+
const ids: string[] = links.map(({ hash }: HTMLAnchorElement) => hash.slice(1))
|
|
115
|
+
|
|
116
|
+
await step("Repeated heading text is suffixed, the first stays unsuffixed", async () => {
|
|
117
|
+
await expect(ids.filter((id: string) => id.startsWith("identical-heading"))).toEqual([
|
|
118
|
+
"identical-heading",
|
|
119
|
+
"identical-heading-1",
|
|
120
|
+
"identical-heading-2",
|
|
121
|
+
])
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
await step("Every link targets a heading that exists", async () => {
|
|
125
|
+
const missing: string[] = ids.filter((id: string) => !canvasElement.querySelector(`#${id}`))
|
|
126
|
+
await expect(missing).toEqual([])
|
|
127
|
+
await expect(new Set(ids).size).toBe(ids.length)
|
|
128
|
+
})
|
|
129
|
+
},
|
|
130
|
+
}
|
|
@@ -16,6 +16,32 @@ exports[`Generated Items 1`] = `
|
|
|
16
16
|
"
|
|
17
17
|
`;
|
|
18
18
|
|
|
19
|
+
exports[`Identical Headings 1`] = `
|
|
20
|
+
"<mx-in-page-navigation data-content=".js-content" data-levels="h2,h3" data-title="On this page">
|
|
21
|
+
<nav class="mx-nav mx-nav--list mx-in-page-navigation mx-vertical-flow"><h2 class="is-excluded mx-heading--m">On this page</h2><ul><li class="mx-in-page-navigation__level--h2"><a href="#section-1"><span class="mx-icon mx-icon--chevron-right"></span><span>Section 1</span></a></li><li class="mx-in-page-navigation__level--h2"><a href="#section-2"><span class="mx-icon mx-icon--chevron-right"></span><span>Section 2</span></a></li><li class="mx-in-page-navigation__level--h3"><a href="#test-level-3"><span class="mx-icon mx-icon--chevron-right"></span><span>Test level 3</span></a></li><li class="mx-in-page-navigation__level--h2"><a href="#section-3"><span class="mx-icon mx-icon--chevron-right"></span><span>Section 3</span></a></li><li class="mx-in-page-navigation__level--h2"><a href="#identical-heading"><span class="mx-icon mx-icon--chevron-right"></span><span>Identical heading</span></a></li><li class="mx-in-page-navigation__level--h3"><a href="#identical-subheading"><span class="mx-icon mx-icon--chevron-right"></span><span>Identical subheading</span></a></li><li class="mx-in-page-navigation__level--h3"><a href="#identical-heading-1"><span class="mx-icon mx-icon--chevron-right"></span><span>Identical heading</span></a></li><li class="mx-in-page-navigation__level--h3"><a href="#identical-subheading-1"><span class="mx-icon mx-icon--chevron-right"></span><span>Identical subheading</span></a></li><li class="mx-in-page-navigation__level--h2"><a href="#identical-heading-2"><span class="mx-icon mx-icon--chevron-right"></span><span>Identical heading</span></a></li></ul></nav></mx-in-page-navigation>
|
|
22
|
+
<div class="js-content mx-rich-text mx-vertical-flow">
|
|
23
|
+
<h2 id="section-1">Section 1</h2>
|
|
24
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
25
|
+
<h2 id="section-2">Section 2</h2>
|
|
26
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
27
|
+
<h3 id="test-level-3">Test level 3</h3>
|
|
28
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
29
|
+
<h2 id="section-3">Section 3</h2>
|
|
30
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
31
|
+
<h2 id="identical-heading">Identical heading</h2>
|
|
32
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
33
|
+
<h3 id="identical-subheading">Identical subheading</h3>
|
|
34
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
35
|
+
<h3 id="identical-heading-1">Identical heading</h3>
|
|
36
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
37
|
+
<h3 id="identical-subheading-1">Identical subheading</h3>
|
|
38
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
39
|
+
<h2 id="identical-heading-2">Identical heading</h2>
|
|
40
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
41
|
+
</div>
|
|
42
|
+
"
|
|
43
|
+
`;
|
|
44
|
+
|
|
19
45
|
exports[`In Page Navigation 1`] = `
|
|
20
46
|
"<mx-in-page-navigation data-content=".js-content" data-levels="" data-title="On this page">
|
|
21
47
|
<nav class="mx-nav mx-nav--list mx-in-page-navigation mx-vertical-flow"><h2 class="is-excluded mx-heading--m">On this page</h2><ul><li><a href="#section-1"><span class="mx-icon mx-icon--chevron-right"></span><span>Section 1</span></a></li><li><a href="#section-2"><span class="mx-icon mx-icon--chevron-right"></span><span>Section 2</span></a></li><li><a href="#section-3"><span class="mx-icon mx-icon--chevron-right"></span><span>Section 3</span></a></li></ul></nav></mx-in-page-navigation>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
+
exports[`Identical Headings 1`] = `"<div class="mx-vertical-flow"><nav class="mx-in-page-navigation mx-nav mx-nav--list mx-vertical-flow"><h2 class="mx-heading--m" id="on-this-page">On this page</h2><ul><li class="mx-in-page-navigation__level--h2"><a href="#identical-heading" class="mx-icon mx-icon--chevron-right">Identical heading</a></li><li class="mx-in-page-navigation__level--h2"><a href="#identical-heading-1" class="mx-icon mx-icon--chevron-right">Identical heading</a></li><li class="mx-in-page-navigation__level--h2"><a href="#identical-heading-2" class="mx-icon mx-icon--chevron-right">Identical heading</a></li></ul></nav><section class="mx-vertical-flow mx-rich-text"><h2 class="" id="identical-heading">Identical heading</h2><p class="">In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p><h2 class="" id="identical-heading-1">Identical heading</h2><p class="">In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p><h2 class="" id="identical-heading-2">Identical heading</h2><p class="">In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p></section></div>"`;
|
|
4
|
+
|
|
3
5
|
exports[`In Page Navigation 1`] = `"<div class="mx-vertical-flow"><nav class="mx-in-page-navigation mx-nav mx-nav--list mx-vertical-flow"><h2 class="mx-heading--m" id="on-this-page">On this page</h2><ul><li class="mx-in-page-navigation__level--h2"><a href="#section-1" class="mx-icon mx-icon--chevron-right">Section 1</a></li><li class="mx-in-page-navigation__level--h2"><a href="#anchor-test" class="mx-icon mx-icon--chevron-right">Section 2</a></li><li class="mx-in-page-navigation__level--h3"><a href="#test-level-3" class="mx-icon mx-icon--chevron-right">Test level 3</a></li><li class="mx-in-page-navigation__level--h2"><a href="#section-3" class="mx-icon mx-icon--chevron-right">Section 3</a></li><li class="mx-in-page-navigation__level--h2"><a href="#section-4" class="mx-icon mx-icon--chevron-right">Section 4</a></li><li class="mx-in-page-navigation__level--h2"><a href="#section-5" class="mx-icon mx-icon--chevron-right">Section 5</a></li><li class="mx-in-page-navigation__level--h3"><a href="#test-level-6" class="mx-icon mx-icon--chevron-right">Test level 6</a></li><li class="mx-in-page-navigation__level--h2"><a href="#section-7" class="mx-icon mx-icon--chevron-right">Section 7</a></li></ul></nav><section class="mx-vertical-flow mx-rich-text"><h2 class="" id="section-1">Section 1</h2><p class="">In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p><h2 class="" id="anchor-test">Section 2</h2><p class="">In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p><h3 class="" id="test-level-3">Test level 3</h3><p class="">In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p><h2 class="" id="section-3">Section 3</h2><p class="">In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p><h2 class="" id="section-4">Section 4</h2><p class="">In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p><h2 class="" id="section-5">Section 5</h2><p class="">In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p><h3 class="" id="test-level-6">Test level 6</h3><p class="">In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p><h2 class="" id="section-7">Section 7</h2><p class="">In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p></section></div>"`;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<div class="js-content mx-rich-text mx-vertical-flow">
|
|
2
|
+
<h2 id="section-1">Section 1</h2>
|
|
3
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
4
|
+
<h2 id="section-2">Section 2</h2>
|
|
5
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
6
|
+
<h3 id="test-level-3">Test level 3</h3>
|
|
7
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
8
|
+
<h2 id="section-3">Section 3</h2>
|
|
9
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
10
|
+
<h2>Identical heading</h2>
|
|
11
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
12
|
+
<h3>Identical subheading</h3>
|
|
13
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
14
|
+
<h3>Identical heading</h3>
|
|
15
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
16
|
+
<h3>Identical subheading</h3>
|
|
17
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
18
|
+
<h2>Identical heading</h2>
|
|
19
|
+
<p>In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.</p>
|
|
20
|
+
</div>
|
|
@@ -25,7 +25,7 @@ exports[`Image Reverse 1`] = `
|
|
|
25
25
|
<article class="mx-card mx-list-item mx-card--collapse mx-card--reversed">
|
|
26
26
|
<div class="mx-card__content mx-vertical-flow">
|
|
27
27
|
<strong class="mx-text--s">Publication</strong>
|
|
28
|
-
<div class="mx-text--
|
|
28
|
+
<div class="mx-text--meta">25 May 2025</div>
|
|
29
29
|
<div class="mx-heading--l">
|
|
30
30
|
|
|
31
31
|
<a href="#">
|
|
@@ -73,7 +73,7 @@ exports[`With Image 1`] = `
|
|
|
73
73
|
<article class="mx-card mx-list-item mx-card--collapse">
|
|
74
74
|
<div class="mx-card__content mx-vertical-flow">
|
|
75
75
|
<strong class="mx-text--s">Publication</strong>
|
|
76
|
-
<div class="mx-text--
|
|
76
|
+
<div class="mx-text--meta">25 May 2025</div>
|
|
77
77
|
<div class="mx-heading--l">
|
|
78
78
|
|
|
79
79
|
<a href="#">
|
|
@@ -101,7 +101,7 @@ exports[`With Info 1`] = `
|
|
|
101
101
|
<article class="mx-card mx-list-item">
|
|
102
102
|
<div class="mx-card__content mx-vertical-flow">
|
|
103
103
|
<strong class="mx-text--s">Publication</strong>
|
|
104
|
-
<div class="mx-text--
|
|
104
|
+
<div class="mx-text--meta">25 May 2025</div>
|
|
105
105
|
<div class="mx-heading--l">
|
|
106
106
|
|
|
107
107
|
<a href="#">
|
|
@@ -166,7 +166,7 @@ exports[`With Url 1`] = `
|
|
|
166
166
|
</a>
|
|
167
167
|
|
|
168
168
|
</div>
|
|
169
|
-
<div class="mx-text--
|
|
169
|
+
<div class="mx-text--meta">https://example.com/some-thing</div>
|
|
170
170
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
|
171
171
|
|
|
172
172
|
</div>
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
<strong class="mx-text--s">{{ label }}</strong>
|
|
16
16
|
{% endif %}
|
|
17
17
|
{% if info is not empty and infoBefore %}
|
|
18
|
-
<div class="mx-text--
|
|
18
|
+
<div class="mx-text--meta">{{ info }}</div>
|
|
19
19
|
{% endif %}
|
|
20
20
|
<div class="mx-heading--l">
|
|
21
21
|
{{ link }}
|
|
22
22
|
</div>
|
|
23
23
|
{% if info is not empty and not infoBefore %}
|
|
24
|
-
<div class="mx-text--
|
|
24
|
+
<div class="mx-text--meta">{{ info }}</div>
|
|
25
25
|
{% endif %}
|
|
26
26
|
{{ content }}
|
|
27
27
|
{{ tags }}
|
|
@@ -117,14 +117,18 @@ type Story = StoryObj<PaginationType>
|
|
|
117
117
|
// click/keyboard interaction to test here, just the ARIA structure.
|
|
118
118
|
export const Pagination: Story = {
|
|
119
119
|
play: async ({ canvas, step }) => {
|
|
120
|
+
const prev = canvas.getByRole("link", { name: /Previous/ })
|
|
121
|
+
const next = canvas.getByRole("link", { name: /Next/ })
|
|
122
|
+
const link1 = canvas.getByRole("link", { name: "1", hidden: true })
|
|
123
|
+
const link2 = canvas.getByRole("link", { name: "2", hidden: true })
|
|
120
124
|
await step("Current page is marked with aria-current", async () => {
|
|
121
|
-
await expect(
|
|
122
|
-
await expect(
|
|
125
|
+
await expect(link2).toHaveAttribute("aria-current", "page")
|
|
126
|
+
await expect(link1).not.toHaveAttribute("aria-current")
|
|
123
127
|
})
|
|
124
128
|
|
|
125
129
|
await step("Previous/next links are present", async () => {
|
|
126
|
-
await expect(
|
|
127
|
-
await expect(
|
|
130
|
+
await expect(prev).toBeInTheDocument()
|
|
131
|
+
await expect(next).toBeInTheDocument()
|
|
128
132
|
})
|
|
129
133
|
},
|
|
130
134
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Pagination
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
@import "
|
|
5
|
+
@import "../../Utility/_mixins.css";
|
|
6
6
|
|
|
7
7
|
@layer design-system.components {
|
|
8
8
|
.mx-pagination {
|
|
@@ -103,10 +103,10 @@
|
|
|
103
103
|
text-transform: capitalize;
|
|
104
104
|
|
|
105
105
|
& span:where(:not(.mx-icon)) {
|
|
106
|
-
@mixin sr-only;
|
|
106
|
+
@add-mixin sr-only;
|
|
107
107
|
|
|
108
108
|
@container pagination (min-width: 25ch) {
|
|
109
|
-
@mixin sr-undo;
|
|
109
|
+
@add-mixin sr-undo;
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Meta, StoryObj } from "@storybook/html-vite"
|
|
2
|
+
import Component from "./progress-indicator.twig"
|
|
3
|
+
import "./progress-indicator.css"
|
|
4
|
+
|
|
5
|
+
type ProgressIndicatorProps = {
|
|
6
|
+
steps: string[]
|
|
7
|
+
current: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const steps = ["Your card", "Your details", "Your income", "Evidence", "Review and submit"]
|
|
11
|
+
|
|
12
|
+
const meta: Meta<ProgressIndicatorProps> = {
|
|
13
|
+
tags: ["autodocs"],
|
|
14
|
+
component: Component,
|
|
15
|
+
args: {
|
|
16
|
+
steps,
|
|
17
|
+
current: 1,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default meta
|
|
22
|
+
type Story = StoryObj<ProgressIndicatorProps>
|
|
23
|
+
|
|
24
|
+
export const ProgressIndicator: Story = {}
|
|
25
|
+
|
|
26
|
+
export const MiddleStep: Story = {
|
|
27
|
+
args: {
|
|
28
|
+
current: 3,
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const LastStep: Story = {
|
|
33
|
+
args: {
|
|
34
|
+
current: 5,
|
|
35
|
+
},
|
|
36
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Meta, StoryObj } from "@storybook/react-vite"
|
|
2
|
+
import Component from "./ProgressIndicator"
|
|
3
|
+
import "./progress-indicator.css"
|
|
4
|
+
import { Page } from "../../../.storybook-react/decorators"
|
|
5
|
+
|
|
6
|
+
const steps = ["Your card", "Your details", "Your income", "Evidence", "Review and submit"]
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Component> = {
|
|
9
|
+
component: Component,
|
|
10
|
+
decorators: [Page],
|
|
11
|
+
args: {
|
|
12
|
+
steps,
|
|
13
|
+
current: 1,
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default meta
|
|
18
|
+
export const ProgressIndicator: StoryObj<typeof meta> = {}
|
|
19
|
+
|
|
20
|
+
export const MiddleStep: StoryObj<typeof meta> = {
|
|
21
|
+
args: {
|
|
22
|
+
current: 3,
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const LastStep: StoryObj<typeof meta> = {
|
|
27
|
+
args: {
|
|
28
|
+
current: 5,
|
|
29
|
+
},
|
|
30
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { JSX } from "react"
|
|
2
|
+
import classNames from "classnames"
|
|
3
|
+
import { validators } from "../../Utility/utilities"
|
|
4
|
+
|
|
5
|
+
type ProgressIndicatorProps = {
|
|
6
|
+
steps: string[]
|
|
7
|
+
current: number
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const ProgressIndicator = ({
|
|
12
|
+
steps,
|
|
13
|
+
current,
|
|
14
|
+
className = null,
|
|
15
|
+
}: ProgressIndicatorProps): JSX.Element => {
|
|
16
|
+
const total = steps.length
|
|
17
|
+
const currentStep = validators.integer(current, 1, total) ? current : 1
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className={classNames("mx-progress-indicator", className)}>
|
|
21
|
+
<p className="mx-progress-indicator__count">
|
|
22
|
+
Step {currentStep} of {total} — {steps[currentStep - 1]}
|
|
23
|
+
</p>
|
|
24
|
+
<div className="mx-progress-indicator__track" aria-hidden="true">
|
|
25
|
+
{steps.map((step, index) => {
|
|
26
|
+
const position = index + 1
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<i
|
|
30
|
+
key={`${step}-${position}`}
|
|
31
|
+
className={classNames("mx-progress-indicator__marker", {
|
|
32
|
+
"mx-progress-indicator__marker--done": position < currentStep,
|
|
33
|
+
"mx-progress-indicator__marker--now": position === currentStep,
|
|
34
|
+
})}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
})}
|
|
38
|
+
</div>
|
|
39
|
+
<ol className="mx-progress-indicator__steps">
|
|
40
|
+
{steps.map((step, index) => {
|
|
41
|
+
const position = index + 1
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<li
|
|
45
|
+
key={`${step}-${position}`}
|
|
46
|
+
className={classNames("mx-progress-indicator__step", {
|
|
47
|
+
"mx-progress-indicator__step--done": position < currentStep,
|
|
48
|
+
})}
|
|
49
|
+
aria-current={position === currentStep ? "step" : undefined}
|
|
50
|
+
>
|
|
51
|
+
{step}
|
|
52
|
+
</li>
|
|
53
|
+
)
|
|
54
|
+
})}
|
|
55
|
+
</ol>
|
|
56
|
+
</div>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default ProgressIndicator
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# ProgressIndicator
|
|
2
|
+
|
|
3
|
+
A horizontal step-progress tracker for multi-step forms and wizards — a status sentence, a segmented bar, and a list of step names, with the current step highlighted. Presentational only: the parent application owns the step-switching logic and re-renders with an updated `current` prop/value.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## CSS Import
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
@import "@pnx-mixtape/mxds/src/Component/ProgressIndicator/progress-indicator.css";
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Twig
|
|
16
|
+
|
|
17
|
+
```twig
|
|
18
|
+
{% include '@mixtape/Component/ProgressIndicator/progress-indicator.twig' with {
|
|
19
|
+
steps: ['Your card', 'Your details', 'Your income', 'Evidence', 'Review and submit'],
|
|
20
|
+
current: 2,
|
|
21
|
+
} %}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**progress-indicator.twig props:**
|
|
25
|
+
|
|
26
|
+
| Prop | Type | Required | Description |
|
|
27
|
+
| ------------ | ----------- | -------- | --------------------------------------------------- |
|
|
28
|
+
| `steps` | `string[]` | Yes | Ordered list of step labels |
|
|
29
|
+
| `current` | `int` | Yes | 1-indexed position of the active step |
|
|
30
|
+
| `attributes` | `Attribute` | No | Optional Drupal-style attributes object on the root |
|
|
31
|
+
|
|
32
|
+
`steps` is treated as plain data (fixed step names for one form), not editorial content — each label renders as a bare list item, so it's looped directly rather than passed as a pre-joined string.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## HTML
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<div class="mx-progress-indicator">
|
|
40
|
+
<p class="mx-progress-indicator__count">Step 2 of 5 — Your details</p>
|
|
41
|
+
<div class="mx-progress-indicator__track" aria-hidden="true">
|
|
42
|
+
<i class="mx-progress-indicator__marker mx-progress-indicator__marker--done"></i>
|
|
43
|
+
<i class="mx-progress-indicator__marker mx-progress-indicator__marker--now"></i>
|
|
44
|
+
<i class="mx-progress-indicator__marker"></i>
|
|
45
|
+
<i class="mx-progress-indicator__marker"></i>
|
|
46
|
+
<i class="mx-progress-indicator__marker"></i>
|
|
47
|
+
</div>
|
|
48
|
+
<ol class="mx-progress-indicator__steps">
|
|
49
|
+
<li class="mx-progress-indicator__step mx-progress-indicator__step--done">Your card</li>
|
|
50
|
+
<li class="mx-progress-indicator__step" aria-current="step">Your details</li>
|
|
51
|
+
<li class="mx-progress-indicator__step">Your income</li>
|
|
52
|
+
<li class="mx-progress-indicator__step">Evidence</li>
|
|
53
|
+
<li class="mx-progress-indicator__step">Review and submit</li>
|
|
54
|
+
</ol>
|
|
55
|
+
</div>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## React
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { ProgressIndicator } from "@pnx-mixtape/mxds/react"
|
|
64
|
+
|
|
65
|
+
;<ProgressIndicator
|
|
66
|
+
steps={["Your card", "Your details", "Your income", "Evidence", "Review and submit"]}
|
|
67
|
+
current={2}
|
|
68
|
+
/>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**`ProgressIndicator` props:**
|
|
72
|
+
|
|
73
|
+
| Prop | Type | Default | Description |
|
|
74
|
+
| ----------- | ---------- | ------- | ------------------------------------- |
|
|
75
|
+
| `steps` | `string[]` | — | Ordered list of step labels |
|
|
76
|
+
| `current` | `number` | — | 1-indexed position of the active step |
|
|
77
|
+
| `className` | `string` | — | Extra class on the root |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Modifiers
|
|
82
|
+
|
|
83
|
+
There are no BEM modifier classes to apply by hand — the per-step `--done`/`--now` states on the track markers and step list are derived automatically from comparing each step's position against `current`.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Accessibility
|
|
88
|
+
|
|
89
|
+
- The segmented track (`mx-progress-indicator__track`) is `aria-hidden` — it's a decorative echo of progress, not the source of truth.
|
|
90
|
+
- The real status is the `mx-progress-indicator__count` sentence ("Step 2 of 5 — Your details") and the `<ol>` of step names, with `aria-current="step"` on the active one — both are read by screen readers as ordinary text/list content.
|
|
91
|
+
- The `::before` checkmark on completed steps (`mx-progress-indicator__step--done`) is a visual-only enhancement; completion is already conveyed by list order relative to `aria-current`, so its absence from some assistive tech's generated-content support doesn't lose information.
|
|
92
|
+
- On navigation, the consuming application is responsible for moving focus to the new step's heading (or an error summary, if validation fails) — this component doesn't manage focus itself, same as it doesn't manage step-switching.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Customisation
|
|
97
|
+
|
|
98
|
+
| Custom property | Applies to | Default |
|
|
99
|
+
| -------------------------- | ------------------------------------------------------------------------ | ----------------------- |
|
|
100
|
+
| `--gap` | `.mx-progress-indicator` | `var(--spacing-xs)` |
|
|
101
|
+
| `--track-gap` | `.mx-progress-indicator__track` | `var(--spacing-xxxs)` |
|
|
102
|
+
| `--marker-size` | `.mx-progress-indicator__track` | `var(--spacing-xxxs)` |
|
|
103
|
+
| `--marker-colour` | `.mx-progress-indicator__track` | `var(--colour-border)` |
|
|
104
|
+
| `--marker-colour-complete` | `.mx-progress-indicator__track` | `var(--colour-primary)` |
|
|
105
|
+
| `--marker-colour-active` | `.mx-progress-indicator__track` and active `mx-progress-indicator__step` | `var(--colour-primary)` |
|
|
106
|
+
|
|
107
|
+
```css
|
|
108
|
+
.theme--health-services .mx-progress-indicator {
|
|
109
|
+
font-family: var(--font-family-display);
|
|
110
|
+
}
|
|
111
|
+
```
|