@pnx-mixtape/mxds 0.1.0-rc.2 → 0.1.0-rc.4
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/{preview.ts → preview.tsx} +4 -0
- package/.storybook/src/docs.tsx +29 -0
- package/dist/build/accordion.css +1 -1
- package/dist/build/accordion.entry.js +1 -1
- package/dist/build/autocomplete.css +1 -1
- package/dist/build/autocomplete.entry.js +1 -1
- package/dist/build/base.css +1 -1
- package/dist/build/breadcrumb.css +1 -1
- package/dist/build/button.css +1 -1
- package/dist/build/card.css +1 -1
- package/dist/build/carousel.css +1 -1
- package/dist/build/chunks/{Accordion-z-dH6nFH.js → Accordion-GN0sME4W.js} +1 -1
- package/dist/build/chunks/{AccordionMobile-BYPPNyf5.js → AccordionMobile-DG5jSe8Q.js} +1 -1
- package/dist/build/chunks/{Autocomplete-2L6tLLuh.js → Autocomplete-B4RtkAyh.js} +1 -1
- package/dist/build/chunks/{ClosableAlert-DdXbF1J2.js → ClosableAlert-DEhj8Nxm.js} +1 -1
- package/dist/build/chunks/{Dialog-Ds8gzhri.js → Dialog-Haahn-Yn.js} +1 -1
- package/dist/build/chunks/Gallery-D8NGXy2Q.js +1 -0
- package/dist/build/chunks/GlobalToggle-B52fdBQu.js +1 -0
- package/dist/build/chunks/{InPageNavigation-CVieEl9u.js → InPageNavigation-Dyn2TR-N.js} +1 -1
- package/dist/build/chunks/Navigation-hOrfLq7h.js +1 -0
- package/dist/build/chunks/{Sticky-cWUVSOXN.js → Sticky-BGCuuEJK.js} +1 -1
- package/dist/build/chunks/{Tabs-DA_Ez8pv.js → Tabs-DYy1YTdU.js} +1 -1
- package/dist/build/chunks/enums-DLV_1NAf.js +1 -0
- package/dist/build/chunks/utilities-_Bq3TxAQ.js +1 -0
- package/dist/build/constants.css +1 -1
- package/dist/build/cookie-consent.css +1 -1
- package/dist/build/dialog.css +1 -1
- package/dist/build/dialog.entry.js +1 -1
- package/dist/build/elements.js +1 -1
- package/dist/build/filters.css +1 -1
- package/dist/build/filters.entry.js +1 -1
- package/dist/build/form.css +1 -1
- package/dist/build/gallery.entry.js +1 -1
- package/dist/build/global-alert.css +1 -1
- package/dist/build/global-alert.entry.js +1 -1
- package/dist/build/grid.css +1 -1
- package/dist/build/header.css +1 -1
- package/dist/build/header.entry.js +1 -1
- package/dist/build/icon.css +1 -1
- package/dist/build/in-page-navigation.entry.js +1 -1
- package/dist/build/list-item.css +1 -1
- package/dist/build/navigation.css +1 -1
- package/dist/build/navigation.entry.js +1 -1
- package/dist/build/one-time-code.css +1 -0
- package/dist/build/popover.css +1 -1
- package/dist/build/print.css +1 -1
- package/dist/build/react.js +1 -1
- package/dist/build/steps.css +1 -1
- package/dist/build/sticky.entry.js +1 -1
- package/dist/build/tabs.css +1 -1
- package/dist/build/tabs.entry.js +1 -1
- package/dist/build/utilities.css +1 -1
- package/dist/types/Atom/Link/Link.d.ts.map +1 -1
- package/dist/types/Component/Accordion/Accordion.d.ts.map +1 -1
- package/dist/types/Component/Dialog/Dialog.d.ts.map +1 -1
- package/dist/types/Component/Filters/Elements/Filters.d.ts +20 -1
- package/dist/types/Component/Filters/Elements/Filters.d.ts.map +1 -1
- package/dist/types/Component/Filters/Filters.d.ts +7 -2
- package/dist/types/Component/Filters/Filters.d.ts.map +1 -1
- package/dist/types/Component/Gallery/Gallery.d.ts.map +1 -1
- package/dist/types/Component/Pagination/Components/PaginationContext.d.ts.map +1 -1
- package/dist/types/Component/Tabs/Tabs.d.ts.map +1 -1
- package/dist/types/Form/Autocomplete/Autocomplete.d.ts +2 -2
- package/dist/types/Form/Autocomplete/Autocomplete.d.ts.map +1 -1
- package/dist/types/Form/Autocomplete/Elements/Autocomplete.d.ts +10 -0
- package/dist/types/Form/Autocomplete/Elements/Autocomplete.d.ts.map +1 -1
- package/dist/types/Form/OneTimeCode/OneTimeCode.d.ts +12 -0
- package/dist/types/Form/OneTimeCode/OneTimeCode.d.ts.map +1 -0
- package/dist/types/Utility/Hooks/useIsomorphicLayoutEffect.d.ts +4 -0
- package/dist/types/Utility/Hooks/useIsomorphicLayoutEffect.d.ts.map +1 -0
- package/dist/types/enums.d.ts +4 -0
- package/dist/types/enums.d.ts.map +1 -1
- package/dist/types/react.d.ts +1 -0
- package/dist/types/react.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/Atom/Button/__snapshots__/Button.stories.ts.snap +2 -3
- package/src/Atom/Button/_buttons.css +16 -0
- package/src/Atom/Button/button.twig +4 -1
- package/src/Atom/DefinitionList/DefinitionList.stories.ts +3 -0
- package/src/Atom/Icon/_icon.css +3 -0
- package/src/Atom/Link/Link.tsx +16 -3
- package/src/Atom/Link/__snapshots__/Link.stories.ts.snap +7 -12
- package/src/Atom/Link/__snapshots__/Link.stories.tsx.snap +1 -1
- package/src/Atom/Link/_links.css +9 -0
- package/src/Atom/Link/link.twig +8 -2
- package/src/Atom/LinkedImage/linked-image.twig +1 -2
- package/src/Atom/_generic.css +0 -10
- package/src/Component/Accordion/Accordion.stories.ts +31 -0
- package/src/Component/Accordion/Accordion.tsx +3 -2
- package/src/Component/Accordion/AccordionItem.stories.ts +27 -0
- package/src/Component/Accordion/__snapshots__/Accordion.stories.ts.snap +36 -0
- package/src/Component/Accordion/__snapshots__/AccordionItem.stories.ts.snap +16 -0
- package/src/Component/Accordion/accordion.css +3 -1
- package/src/Component/Breadcrumb/__snapshots__/Breadcrumb.stories.ts.snap +3 -6
- package/src/Component/Breadcrumb/breadcrumb.css +4 -0
- package/src/Component/Card/__snapshots__/Card.stories.ts.snap +11 -22
- package/src/Component/Card/card.css +2 -2
- package/src/Component/Carousel/Carousel.stories.ts +55 -0
- package/src/Component/Carousel/Carousel.stories.tsx +74 -0
- package/src/Component/Carousel/__snapshots__/Carousel.stories.ts.snap +273 -88
- package/src/Component/Carousel/__snapshots__/Carousel.stories.tsx.snap +5 -3
- package/src/Component/Carousel/carousel.css +1 -1
- package/src/Component/ContentBlock/__snapshots__/ContentBlock.stories.ts.snap +12 -24
- package/src/Component/CookieConsent/cookie-consent.css +6 -1
- package/src/Component/Dialog/Dialog.stories.ts +33 -0
- package/src/Component/Dialog/Dialog.stories.tsx +25 -0
- package/src/Component/Dialog/Dialog.tsx +2 -2
- package/src/Component/Dialog/__snapshots__/Dialog.stories.ts.snap +33 -2
- package/src/Component/Dialog/__snapshots__/Dialog.stories.tsx.snap +2 -0
- package/src/Component/Dialog/dialog.css +5 -2
- package/src/Component/DropMenu/DropMenu.stories.ts +39 -1
- package/src/Component/DropMenu/DropMenu.stories.tsx +29 -0
- package/src/Component/DropMenu/DropMenu.tsx +1 -1
- package/src/Component/DropMenu/__snapshots__/DropMenu.stories.ts.snap +14 -0
- package/src/Component/DropMenu/__snapshots__/DropMenu.stories.tsx.snap +3 -1
- package/src/Component/Filters/Elements/Filters.ts +71 -1
- package/src/Component/Filters/FilterItem.stories.ts +2 -1
- package/src/Component/Filters/FilterItem.tsx +1 -1
- package/src/Component/Filters/Filters.figma.ts +1 -1
- package/src/Component/Filters/Filters.stories.ts +95 -3
- package/src/Component/Filters/Filters.stories.tsx +246 -17
- package/src/Component/Filters/Filters.tsx +145 -29
- package/src/Component/Filters/README.md +102 -37
- package/src/Component/Filters/__snapshots__/FilterItem.stories.tsx.snap +3 -3
- package/src/Component/Filters/__snapshots__/Filters.stories.ts.snap +82 -4
- package/src/Component/Filters/__snapshots__/Filters.stories.tsx.snap +9 -3
- package/src/Component/Filters/filters.css +11 -0
- package/src/Component/Gallery/Gallery.tsx +2 -2
- package/src/Component/GlobalAlert/global-alert.css +3 -0
- package/src/Component/HeroBanner/HeroBanner.stories.ts +1 -1
- package/src/Component/HeroBanner/__snapshots__/HeroBanner.stories.ts.snap +4 -8
- package/src/Component/HeroSearch/__snapshots__/HeroSearch.stories.ts.snap +5 -10
- package/src/Component/InPageAlert/__snapshots__/InPageAlert.stories.ts.snap +6 -12
- package/src/Component/InPageNavigation/InPageNavigation.stories.ts +50 -0
- package/src/Component/InPageNavigation/InPageNavigation.stories.tsx +78 -0
- package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.ts.snap +35 -1
- package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.tsx.snap +2 -0
- package/src/Component/LinkList/__snapshots__/LinkList.stories.ts.snap +6 -12
- package/src/Component/ListItem/__snapshots__/ListItem.stories.ts.snap +7 -14
- package/src/Component/ListItem/list-item.css +1 -1
- package/src/Component/Navigation/Dropdown.stories.tsx +46 -0
- package/src/Component/Navigation/Navigation.stories.ts +56 -0
- package/src/Component/Navigation/__snapshots__/Dropdown.stories.tsx.snap +2 -0
- package/src/Component/Navigation/__snapshots__/Navigation.stories.ts.snap +57 -0
- package/src/Component/Navigation/_navigation-collapsible.css +3 -1
- package/src/Component/Navigation/_navigation-dropdown.css +3 -1
- package/src/Component/Pagination/Components/PaginationContext.tsx +2 -1
- package/src/Component/Pagination/Components/PaginationItem.tsx +1 -1
- package/src/Component/Pagination/Pagination.stories.ts +36 -0
- package/src/Component/Pagination/Pagination.stories.tsx +31 -0
- package/src/Component/Pagination/__snapshots__/Pagination.stories.ts.snap +69 -12
- package/src/Component/Pagination/__snapshots__/Pagination.stories.tsx.snap +3 -1
- package/src/Component/Pagination/pagination-item.twig +1 -1
- package/src/Component/Popover/__snapshots__/Popover.stories.ts.snap +13 -26
- package/src/Component/Popover/popover.css +4 -2
- package/src/Component/SideNavigation/__snapshots__/SideNavigation.stories.ts.snap +1 -2
- package/src/Component/SocialLinks/__snapshots__/SocialLinks.stories.ts.snap +6 -9
- package/src/Component/Steps/Steps.stories.ts +1 -1
- package/src/Component/Steps/steps.css +13 -0
- package/src/Component/Tabs/Tabs.stories.ts +22 -0
- package/src/Component/Tabs/Tabs.stories.tsx +24 -0
- package/src/Component/Tabs/Tabs.tsx +2 -2
- package/src/Component/Tabs/__snapshots__/Tabs.stories.ts.snap +18 -0
- package/src/Component/Tabs/__snapshots__/Tabs.stories.tsx.snap +3 -1
- package/src/Component/Tabs/tabs.css +6 -5
- package/src/Form/Autocomplete/Autocomplete.stories.ts +81 -4
- package/src/Form/Autocomplete/Autocomplete.stories.tsx +85 -1
- package/src/Form/Autocomplete/Autocomplete.tsx +19 -6
- package/src/Form/Autocomplete/Elements/Autocomplete.ts +24 -1
- package/src/Form/Autocomplete/__snapshots__/Autocomplete.stories.ts.snap +38 -4
- package/src/Form/Autocomplete/__snapshots__/Autocomplete.stories.tsx.snap +8 -4
- package/src/Form/Autocomplete/autocomplete.css +12 -1
- package/src/Form/Autocomplete/autocomplete.twig +1 -1
- package/src/Form/OneTimeCode/OneTimeCode.stories.ts +20 -0
- package/src/Form/OneTimeCode/OneTimeCode.stories.tsx +18 -0
- package/src/Form/OneTimeCode/OneTimeCode.tsx +56 -0
- package/src/Form/OneTimeCode/README.md +83 -0
- package/src/Form/OneTimeCode/__snapshots__/OneTimeCode.stories.ts.snap +7 -0
- package/src/Form/OneTimeCode/__snapshots__/OneTimeCode.stories.tsx.snap +3 -0
- package/src/Form/OneTimeCode/one-time-code.css +13 -0
- package/src/Form/OneTimeCode/one-time-code.twig +31 -0
- package/src/Form/__snapshots__/Validation.stories.ts.snap +1 -1
- package/src/Form/form.css +95 -35
- package/src/Layout/Footer/__snapshots__/Footer.stories.ts.snap +18 -33
- package/src/Layout/Grid/Grid.stories.ts +1 -0
- package/src/Layout/Grid/Grid.stories.tsx +26 -0
- package/src/Layout/Grid/__snapshots__/Grid.stories.tsx.snap +2 -0
- package/src/Layout/Grid/grid.css +12 -0
- package/src/Layout/Header/Header.stories.ts +33 -0
- package/src/Layout/Header/Header.stories.tsx +31 -0
- package/src/Layout/Header/__snapshots__/Header.stories.ts.snap +50 -0
- package/src/Layout/Header/__snapshots__/Header.stories.tsx.snap +2 -0
- package/src/Layout/Header/_header.css +4 -2
- package/src/Layout/Header/_toggles.css +4 -1
- package/src/Layout/Masthead/__snapshots__/Masthead.stories.ts.snap +8 -16
- package/src/Layout/Section/__snapshots__/Background.stories.ts.snap +2 -4
- package/src/Layout/Section/__snapshots__/Section.stories.ts.snap +4 -8
- package/src/Utility/Hooks/useIsomorphicLayoutEffect.ts +8 -0
- package/src/Utility/_mixins.css +46 -0
- package/src/Utility/print.css +1 -1
- package/src/Utility/utilities.css +20 -0
- package/src/_custom-media.css +3 -2
- package/src/constants.css +3 -13
- package/src/enums.ts +5 -0
- package/src/react.ts +1 -0
- package/tools/test-utils/screen-reader.ts +33 -0
- package/dist/build/chunks/Gallery-BT3cxS5m.js +0 -1
- package/dist/build/chunks/GlobalToggle-DQHho97V.js +0 -1
- package/dist/build/chunks/Navigation-B9Wug-ok.js +0 -1
- package/dist/build/chunks/disclosure-widget-BETzOHLO.js +0 -1
- package/dist/build/chunks/utilities-C5dAO2YA.js +0 -1
|
@@ -21,8 +21,7 @@ exports[`Bottom 1`] = `
|
|
|
21
21
|
|
|
22
22
|
<a href="#" class="mx-link--more">
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
Learn more
|
|
26
25
|
</a>
|
|
27
26
|
|
|
28
27
|
</div>
|
|
@@ -52,8 +51,7 @@ exports[`Bottom End 1`] = `
|
|
|
52
51
|
|
|
53
52
|
<a href="#" class="mx-link--more">
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
Learn more
|
|
57
55
|
</a>
|
|
58
56
|
|
|
59
57
|
</div>
|
|
@@ -83,8 +81,7 @@ exports[`Bottom Start 1`] = `
|
|
|
83
81
|
|
|
84
82
|
<a href="#" class="mx-link--more">
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
Learn more
|
|
88
85
|
</a>
|
|
89
86
|
|
|
90
87
|
</div>
|
|
@@ -114,8 +111,7 @@ exports[`Left 1`] = `
|
|
|
114
111
|
|
|
115
112
|
<a href="#" class="mx-link--more">
|
|
116
113
|
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
Learn more
|
|
119
115
|
</a>
|
|
120
116
|
|
|
121
117
|
</div>
|
|
@@ -145,8 +141,7 @@ exports[`Left End 1`] = `
|
|
|
145
141
|
|
|
146
142
|
<a href="#" class="mx-link--more">
|
|
147
143
|
|
|
148
|
-
|
|
149
|
-
|
|
144
|
+
Learn more
|
|
150
145
|
</a>
|
|
151
146
|
|
|
152
147
|
</div>
|
|
@@ -176,8 +171,7 @@ exports[`Left Start 1`] = `
|
|
|
176
171
|
|
|
177
172
|
<a href="#" class="mx-link--more">
|
|
178
173
|
|
|
179
|
-
|
|
180
|
-
|
|
174
|
+
Learn more
|
|
181
175
|
</a>
|
|
182
176
|
|
|
183
177
|
</div>
|
|
@@ -207,8 +201,7 @@ exports[`Popover 1`] = `
|
|
|
207
201
|
|
|
208
202
|
<a href="#" class="mx-link--more">
|
|
209
203
|
|
|
210
|
-
|
|
211
|
-
|
|
204
|
+
Learn more
|
|
212
205
|
</a>
|
|
213
206
|
|
|
214
207
|
</div>
|
|
@@ -238,8 +231,7 @@ exports[`Right 1`] = `
|
|
|
238
231
|
|
|
239
232
|
<a href="#" class="mx-link--more">
|
|
240
233
|
|
|
241
|
-
|
|
242
|
-
|
|
234
|
+
Learn more
|
|
243
235
|
</a>
|
|
244
236
|
|
|
245
237
|
</div>
|
|
@@ -269,8 +261,7 @@ exports[`Right End 1`] = `
|
|
|
269
261
|
|
|
270
262
|
<a href="#" class="mx-link--more">
|
|
271
263
|
|
|
272
|
-
|
|
273
|
-
|
|
264
|
+
Learn more
|
|
274
265
|
</a>
|
|
275
266
|
|
|
276
267
|
</div>
|
|
@@ -300,8 +291,7 @@ exports[`Right Start 1`] = `
|
|
|
300
291
|
|
|
301
292
|
<a href="#" class="mx-link--more">
|
|
302
293
|
|
|
303
|
-
|
|
304
|
-
|
|
294
|
+
Learn more
|
|
305
295
|
</a>
|
|
306
296
|
|
|
307
297
|
</div>
|
|
@@ -331,8 +321,7 @@ exports[`Top 1`] = `
|
|
|
331
321
|
|
|
332
322
|
<a href="#" class="mx-link--more">
|
|
333
323
|
|
|
334
|
-
|
|
335
|
-
|
|
324
|
+
Learn more
|
|
336
325
|
</a>
|
|
337
326
|
|
|
338
327
|
</div>
|
|
@@ -362,8 +351,7 @@ exports[`Top End 1`] = `
|
|
|
362
351
|
|
|
363
352
|
<a href="#" class="mx-link--more">
|
|
364
353
|
|
|
365
|
-
|
|
366
|
-
|
|
354
|
+
Learn more
|
|
367
355
|
</a>
|
|
368
356
|
|
|
369
357
|
</div>
|
|
@@ -393,8 +381,7 @@ exports[`Top Start 1`] = `
|
|
|
393
381
|
|
|
394
382
|
<a href="#" class="mx-link--more">
|
|
395
383
|
|
|
396
|
-
|
|
397
|
-
|
|
384
|
+
Learn more
|
|
398
385
|
</a>
|
|
399
386
|
|
|
400
387
|
</div>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* **Experimental** Using new CSS Anchoring and Popover API's, requires JS polyfill.
|
|
5
5
|
*/
|
|
6
|
+
@import "../../Utility/_mixins.css";
|
|
6
7
|
|
|
7
8
|
@layer design-system.defaults {
|
|
8
9
|
mx-popover {
|
|
@@ -20,13 +21,14 @@
|
|
|
20
21
|
|
|
21
22
|
/* @public --popover-padding --popover-background --popover-foreground --popover-radius --popover-width */
|
|
22
23
|
:where(.mx-popover) {
|
|
24
|
+
@add-mixin transition;
|
|
25
|
+
|
|
23
26
|
display: none;
|
|
24
|
-
opacity: 0;
|
|
25
27
|
position: absolute;
|
|
26
28
|
padding: var(--popover-padding, var(--spacing-xs));
|
|
27
29
|
background-color: var(--popover-background, var(--colour-background));
|
|
28
30
|
color: var(--popover-foreground, var(--colour-foreground));
|
|
29
|
-
border: var(--popover-line-width,
|
|
31
|
+
border: var(--popover-line-width, 1px) solid var(--popover-line-colour, transparent);
|
|
30
32
|
border-radius: var(--popover-radius, var(--border-radius));
|
|
31
33
|
box-shadow: var(--popover-shadow, var(--box-shadow));
|
|
32
34
|
max-inline-size: var(--popover-width, 30ch);
|
|
@@ -5,28 +5,25 @@ exports[`Social Links 1`] = `
|
|
|
5
5
|
|
|
6
6
|
<h2 class="mx-heading--m">Follow us</h2>
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
<a href="#">
|
|
8
|
+
|
|
9
|
+
<a href="#" aria-label="Facebook">
|
|
10
10
|
|
|
11
11
|
<span class="mx-icon mx-icon--facebook mx-icon--size-md"></span>
|
|
12
12
|
|
|
13
|
-
<span class="sr-only">Facebook</span>
|
|
14
13
|
|
|
15
14
|
</a>
|
|
16
|
-
|
|
17
|
-
<a href="#">
|
|
15
|
+
|
|
16
|
+
<a href="#" aria-label="Instagram">
|
|
18
17
|
|
|
19
18
|
<span class="mx-icon mx-icon--instagram mx-icon--size-md"></span>
|
|
20
19
|
|
|
21
|
-
<span class="sr-only">Instagram</span>
|
|
22
20
|
|
|
23
21
|
</a>
|
|
24
|
-
|
|
25
|
-
<a href="#">
|
|
22
|
+
|
|
23
|
+
<a href="#" aria-label="LinkedIn">
|
|
26
24
|
|
|
27
25
|
<span class="mx-icon mx-icon--linkedin mx-icon--size-md"></span>
|
|
28
26
|
|
|
29
|
-
<span class="sr-only">LinkedIn</span>
|
|
30
27
|
|
|
31
28
|
</a>
|
|
32
29
|
|
|
@@ -31,7 +31,7 @@ const meta: Meta<StepsType> = {
|
|
|
31
31
|
},
|
|
32
32
|
argTypes: {
|
|
33
33
|
modifiers: {
|
|
34
|
-
description: "Change the colour of the line and marker.",
|
|
34
|
+
description: "Change the colour of the line and marker (only one applies).",
|
|
35
35
|
options: Object.values(StepsModifier),
|
|
36
36
|
control: "check",
|
|
37
37
|
type: "string",
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Steps
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
@import "../../Utility/_mixins.css";
|
|
6
|
+
|
|
5
7
|
@layer design-system.components {
|
|
6
8
|
.mx-steps {
|
|
7
9
|
counter-reset: step;
|
|
@@ -73,6 +75,8 @@
|
|
|
73
75
|
inset-inline-start: calc((var(--counter-size, 2rem) / 2) + (var(--line-width, 2px)));
|
|
74
76
|
inset-block: 0;
|
|
75
77
|
position: absolute;
|
|
78
|
+
|
|
79
|
+
@add-mixin forced-colors-paint;
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
|
|
@@ -89,6 +93,15 @@
|
|
|
89
93
|
.mx-steps--fill:where(:not(:has(.mx-steps--fill))) {
|
|
90
94
|
--background: var(--line-colour, var(--colour-border));
|
|
91
95
|
--foreground: var(--counter-foreground, var(--colour-foreground));
|
|
96
|
+
|
|
97
|
+
@media (--forced-colors) {
|
|
98
|
+
&.mx-steps__panel::before,
|
|
99
|
+
& .mx-steps__panel::before {
|
|
100
|
+
forced-color-adjust: none;
|
|
101
|
+
background-color: CanvasText;
|
|
102
|
+
border-color: CanvasText;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
92
105
|
}
|
|
93
106
|
|
|
94
107
|
.mx-steps--counters {
|
|
@@ -12,6 +12,7 @@ import "../Popover/popover.css"
|
|
|
12
12
|
import "../DropMenu/drop-menu.css"
|
|
13
13
|
import Heading from "../../Atom/Heading/heading.twig"
|
|
14
14
|
import { modes } from "../../../.storybook/modes"
|
|
15
|
+
import { runScreenReaderWalkthrough } from "../../../tools/test-utils/screen-reader"
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* A Tabs component built from a list of TabItems.
|
|
@@ -177,6 +178,27 @@ export const Tabs: Story = {
|
|
|
177
178
|
},
|
|
178
179
|
}
|
|
179
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Steps a virtual screen reader through the tab list and asserts on what it announces.
|
|
183
|
+
*/
|
|
184
|
+
export const ScreenReaderAnnouncements: Story = {
|
|
185
|
+
play: async () => {
|
|
186
|
+
const log = await runScreenReaderWalkthrough()
|
|
187
|
+
await expect(log).toEqual([
|
|
188
|
+
"document",
|
|
189
|
+
"tablist, orientated horizontally",
|
|
190
|
+
"tab, Funk, selected, 1 control, position 1, set size 3",
|
|
191
|
+
"tab, Soul, not selected, 1 control, position 2, set size 3",
|
|
192
|
+
"tab, Motown, not selected, 1 control, position 3, set size 3",
|
|
193
|
+
"end of tablist, orientated horizontally",
|
|
194
|
+
"tabpanel, Funk",
|
|
195
|
+
"Funk is the ipsum dolor sit amet, consectetur adipiscing elit. Proin at finibus velit. Curabitur consequat, dolor venenatis ullamcorper commodo, quam mi iaculis sem, non varius massa risus et enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus non mi lorem. Morbi semper sagittis euismod. Vestibulum non ornare orci.",
|
|
196
|
+
"end of tabpanel, Funk",
|
|
197
|
+
"end of document",
|
|
198
|
+
])
|
|
199
|
+
},
|
|
200
|
+
}
|
|
201
|
+
|
|
180
202
|
/**
|
|
181
203
|
* Can have a heading above the tabs for extra context.
|
|
182
204
|
*/
|
|
@@ -11,6 +11,7 @@ import "./tabs.css"
|
|
|
11
11
|
import "../DropMenu/drop-menu.css"
|
|
12
12
|
import { TabStyles } from "../../enums"
|
|
13
13
|
import { Page } from "../../../.storybook-react/decorators"
|
|
14
|
+
import { runScreenReaderWalkthrough } from "../../../tools/test-utils/screen-reader"
|
|
14
15
|
|
|
15
16
|
const defaultChildren = (
|
|
16
17
|
<Fragment>
|
|
@@ -114,3 +115,26 @@ export const Default: StoryObj<typeof meta> = {
|
|
|
114
115
|
})
|
|
115
116
|
},
|
|
116
117
|
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Steps a virtual screen reader through the tab list and asserts on what it announces.
|
|
121
|
+
*/
|
|
122
|
+
export const ScreenReaderAnnouncements: StoryObj<typeof meta> = {
|
|
123
|
+
play: async () => {
|
|
124
|
+
const log = await runScreenReaderWalkthrough()
|
|
125
|
+
await expect(log).toEqual([
|
|
126
|
+
"document",
|
|
127
|
+
"tablist, orientated horizontally",
|
|
128
|
+
"tab, Tab 1, selected, 1 control, position 1, set size 3",
|
|
129
|
+
"tab, Tab 2, not selected, 1 control, position 2, set size 3",
|
|
130
|
+
"tab, Tab 3, not selected, 1 control, position 3, set size 3",
|
|
131
|
+
"end of tablist, orientated horizontally",
|
|
132
|
+
"tabpanel, Tab 1",
|
|
133
|
+
"paragraph",
|
|
134
|
+
"This is tab 1",
|
|
135
|
+
"end of paragraph",
|
|
136
|
+
"end of tabpanel, Tab 1",
|
|
137
|
+
"end of document",
|
|
138
|
+
])
|
|
139
|
+
},
|
|
140
|
+
}
|
|
@@ -8,7 +8,6 @@ import React, {
|
|
|
8
8
|
createContext,
|
|
9
9
|
use,
|
|
10
10
|
useState,
|
|
11
|
-
useEffect,
|
|
12
11
|
useMemo,
|
|
13
12
|
useId,
|
|
14
13
|
useRef,
|
|
@@ -17,6 +16,7 @@ import React, {
|
|
|
17
16
|
} from "react"
|
|
18
17
|
import classNames from "classnames"
|
|
19
18
|
import DropMenuProvider from "../DropMenu/Components/DropMenuContext"
|
|
19
|
+
import useIsomorphicLayoutEffect from "../../Utility/Hooks/useIsomorphicLayoutEffect"
|
|
20
20
|
import { TabStyles } from "../../enums"
|
|
21
21
|
|
|
22
22
|
type TabContextValues = {
|
|
@@ -54,7 +54,7 @@ const Tabs = ({ children, active, modifier, id }: TabsProps): JSX.Element => {
|
|
|
54
54
|
window.history.replaceState({}, "", `${window.location.pathname}?${urlParams.toString()}`)
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
useIsomorphicLayoutEffect(() => {
|
|
58
58
|
if (typeof window === "undefined") return
|
|
59
59
|
const tabParam = new URLSearchParams(window.location.search).get("tab")
|
|
60
60
|
if (tabParam && tabParam !== activeTab) {
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
+
exports[`Screen Reader Announcements 1`] = `
|
|
4
|
+
"
|
|
5
|
+
|
|
6
|
+
<mx-tabs class="mx-tabs mx-tabs--lined" id="tab-example"><mx-dropmenu closeonclick="true"><button id="unique-3" class="mx-popover__trigger mx-button mx-icon mx-icon--chevron-down mx-icon--end" popovertarget="tab-example" style="anchor-name: --tab-example" hidden="">Funk</button><div class="mx-popover mx-drop-menu" id="tab-example" aria-describedby="unique-0" style="position-anchor: --tab-example" data-placement="bottom-start" popover="" role="tablist"><button type="button" role="tab" aria-controls="unique-1" aria-selected="true">Funk</button><button type="button" role="tab" aria-controls="unique-2" aria-selected="false">Soul</button><button type="button" role="tab" aria-controls="unique-3" aria-selected="false">Motown</button></div></mx-dropmenu><div class="mx-tabs__list" role="tablist"><button type="button" role="tab" aria-controls="unique-4" aria-selected="true" tabindex="0">Funk</button><button type="button" role="tab" aria-controls="unique-5" aria-selected="false" tabindex="-1">Soul</button><button type="button" role="tab" aria-controls="unique-6" aria-selected="false" tabindex="-1">Motown</button></div>
|
|
7
|
+
<div class="mx-tabs__panel mx-vertical-flow mx-container" id="unique-0" data-tab="Funk" role="tabpanel" tabindex="-1" aria-label="Funk">
|
|
8
|
+
Funk is the ipsum dolor sit amet, consectetur adipiscing elit. Proin at finibus velit. Curabitur consequat, dolor venenatis ullamcorper commodo, quam mi iaculis sem, non varius massa risus et enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus non mi lorem. Morbi semper sagittis euismod. Vestibulum non ornare orci.
|
|
9
|
+
</div>
|
|
10
|
+
<div class="mx-tabs__panel mx-vertical-flow mx-container" id="unique-1" data-tab="Soul" role="tabpanel" tabindex="-1" aria-label="Soul" inert="">
|
|
11
|
+
Soul music ipsum dolor sit amet, consectetur adipiscing elit. Proin at finibus velit. Curabitur consequat, dolor venenatis ullamcorper commodo, quam mi iaculis sem, non varius massa risus et enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus non mi lorem.
|
|
12
|
+
</div>
|
|
13
|
+
<div class="mx-tabs__panel mx-vertical-flow mx-container" id="unique-2" data-tab="Motown" role="tabpanel" tabindex="-1" aria-label="Motown" inert="">
|
|
14
|
+
Motown lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin at finibus velit. Curabitur consequat, dolor venenatis ullamcorper commodo, quam mi iaculis sem, non varius massa risus et enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus non mi lorem. Morbi semper sagittis euismod. Vestibulum non ornare orci.
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
</mx-tabs>
|
|
18
|
+
"
|
|
19
|
+
`;
|
|
20
|
+
|
|
3
21
|
exports[`Tabs 1`] = `
|
|
4
22
|
"
|
|
5
23
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`Default 1`] = `"<div class="mx-tabs"><div class="mobile-only"><button id="tabs-storybook-target" type="button" class="mx-button mx-icon mx-icon--chevron-down mx-icon--end mx-popover__trigger" popovertarget="tabs-storybook" style="anchor-name: --drop-menu-tabs-storybook;">Tab 1</button><div id="tabs-storybook" class="mx-popover mx-drop-menu" popover="" aria-labelledby="tabs-storybook-target" aria-describedby="tabs-storybook-description" role="tablist" style="position-anchor: --drop-menu-tabs-storybook;"><div id="tabs-storybook-description" class="sr-only">Results will update when you make a selection. Press escape to close.</div><span data-focus-scope-start="true" hidden=""></span><button type="button" data-id="tab-1" aria-checked="true" role="tab" aria-controls="tab-1" aria-selected="true">Tab 1</button><button type="button" data-id="tab-2" aria-checked="false" role="tab" aria-controls="tab-2" aria-selected="false">Tab 2</button><button type="button" data-id="tab-3" aria-checked="false" role="tab" aria-controls="tab-3" aria-selected="false">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div></div><div class="mx-tabs__list desktop-only" role="tablist"><span data-focus-scope-start="true" hidden=""></span><button id="tab-1-button" aria-controls="tab-1" role="tab" aria-selected="true" tabindex="0" type="button">Tab 1</button><button id="tab-2-button" aria-controls="tab-2" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 2</button><button id="tab-3-button" aria-controls="tab-3" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div><div id="tab-1" class="mx-tabs__panel mx-vertical-flow mx-section--l" aria-labelledby="tab-1-button" role="tabpanel" tabindex="0"><p>This is tab 1</p></div><div id="tab-2" class="mx-tabs__panel mx-vertical-flow mx-section--l" aria-labelledby="tab-2-button" role="tabpanel" tabindex="0" inert=""><p>This is tab 2</p></div><div id="tab-3" class="mx-tabs__panel mx-vertical-flow mx-section--l" inert="" aria-labelledby="tab-3-button" role="tabpanel" tabindex="0"><p>This is tab 3</p></div></div>"`;
|
|
3
|
+
exports[`Default 1`] = `"<div class="mx-tabs"><div class="mobile-only"><button id="tabs-storybook-target" type="button" class="mx-button mx-icon mx-icon--chevron-down mx-icon--end mx-popover__trigger" popovertarget="tabs-storybook" style="anchor-name: --drop-menu-tabs-storybook;">Tab 1</button><div id="tabs-storybook" class="mx-popover mx-drop-menu" popover="" aria-labelledby="tabs-storybook-target" aria-describedby="tabs-storybook-description" role="tablist" style="position-anchor: --drop-menu-tabs-storybook;"><div id="tabs-storybook-description" class="sr-only" aria-hidden="true">Results will update when you make a selection. Press escape to close.</div><span data-focus-scope-start="true" hidden=""></span><button type="button" data-id="tab-1" aria-checked="true" role="tab" aria-controls="tab-1" aria-selected="true">Tab 1</button><button type="button" data-id="tab-2" aria-checked="false" role="tab" aria-controls="tab-2" aria-selected="false">Tab 2</button><button type="button" data-id="tab-3" aria-checked="false" role="tab" aria-controls="tab-3" aria-selected="false">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div></div><div class="mx-tabs__list desktop-only" role="tablist"><span data-focus-scope-start="true" hidden=""></span><button id="tab-1-button" aria-controls="tab-1" role="tab" aria-selected="true" tabindex="0" type="button">Tab 1</button><button id="tab-2-button" aria-controls="tab-2" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 2</button><button id="tab-3-button" aria-controls="tab-3" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div><div id="tab-1" class="mx-tabs__panel mx-vertical-flow mx-section--l" aria-labelledby="tab-1-button" role="tabpanel" tabindex="0"><p>This is tab 1</p></div><div id="tab-2" class="mx-tabs__panel mx-vertical-flow mx-section--l" aria-labelledby="tab-2-button" role="tabpanel" tabindex="0" inert=""><p>This is tab 2</p></div><div id="tab-3" class="mx-tabs__panel mx-vertical-flow mx-section--l" inert="" aria-labelledby="tab-3-button" role="tabpanel" tabindex="0"><p>This is tab 3</p></div></div>"`;
|
|
4
|
+
|
|
5
|
+
exports[`Screen Reader Announcements 1`] = `"<div class="mx-tabs"><div class="mobile-only"><button id="tabs-storybook-target" type="button" class="mx-button mx-icon mx-icon--chevron-down mx-icon--end mx-popover__trigger" popovertarget="tabs-storybook" style="anchor-name: --drop-menu-tabs-storybook;">Tab 1</button><div id="tabs-storybook" class="mx-popover mx-drop-menu" popover="" aria-labelledby="tabs-storybook-target" aria-describedby="tabs-storybook-description" role="tablist" style="position-anchor: --drop-menu-tabs-storybook;"><div id="tabs-storybook-description" class="sr-only" aria-hidden="true">Results will update when you make a selection. Press escape to close.</div><span data-focus-scope-start="true" hidden=""></span><button type="button" data-id="tab-1" aria-checked="true" role="tab" aria-controls="tab-1" aria-selected="true">Tab 1</button><button type="button" data-id="tab-2" aria-checked="false" role="tab" aria-controls="tab-2" aria-selected="false">Tab 2</button><button type="button" data-id="tab-3" aria-checked="false" role="tab" aria-controls="tab-3" aria-selected="false">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div></div><div class="mx-tabs__list desktop-only" role="tablist"><span data-focus-scope-start="true" hidden=""></span><button id="tab-1-button" aria-controls="tab-1" role="tab" aria-selected="true" tabindex="0" type="button">Tab 1</button><button id="tab-2-button" aria-controls="tab-2" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 2</button><button id="tab-3-button" aria-controls="tab-3" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div><div id="tab-1" class="mx-tabs__panel mx-vertical-flow mx-section--l" aria-labelledby="tab-1-button" role="tabpanel" tabindex="0"><p>This is tab 1</p></div><div id="tab-2" class="mx-tabs__panel mx-vertical-flow mx-section--l" inert="" aria-labelledby="tab-2-button" role="tabpanel" tabindex="0"><p>This is tab 2</p></div><div id="tab-3" class="mx-tabs__panel mx-vertical-flow mx-section--l" inert="" aria-labelledby="tab-3-button" role="tabpanel" tabindex="0"><p>This is tab 3</p></div></div>"`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tabs
|
|
3
3
|
*/
|
|
4
|
+
@import "../../Utility/_mixins.css";
|
|
4
5
|
|
|
5
6
|
@layer design-system.defaults {
|
|
6
7
|
mx-tabs {
|
|
@@ -46,21 +47,21 @@
|
|
|
46
47
|
background-color: var(--tab-background, var(--background, var(--colour-background)));
|
|
47
48
|
color: var(--tab-foreground, var(--foreground, var(--colour-foreground)));
|
|
48
49
|
font-size: var(--tab-font-size, var(--font-size-xs));
|
|
50
|
+
cursor: pointer;
|
|
49
51
|
|
|
50
52
|
&[aria-selected="true"] {
|
|
51
53
|
background-color: var(--tab-active-background, var(--colour-background-alt));
|
|
52
54
|
color: var(--tab-active-foreground, var(--colour-foreground));
|
|
55
|
+
font-weight: var(--font-weight-bold);
|
|
56
|
+
text-decoration-line: underline;
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
.mx-tabs__panel {
|
|
58
|
-
|
|
59
|
-
opacity: 1;
|
|
62
|
+
@add-mixin transition;
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
opacity: 0;
|
|
63
|
-
}
|
|
64
|
+
inline-size: 100%;
|
|
64
65
|
|
|
65
66
|
&[inert] {
|
|
66
67
|
opacity: 0;
|
|
@@ -4,6 +4,7 @@ import Component from "./autocomplete.twig"
|
|
|
4
4
|
import "./autocomplete.css"
|
|
5
5
|
import "./Elements/Autocomplete"
|
|
6
6
|
import type { AutocompleteSearchEvent } from "./Elements/Autocomplete"
|
|
7
|
+
import { runScreenReaderWalkthrough } from "../../../tools/test-utils/screen-reader"
|
|
7
8
|
|
|
8
9
|
const fruits = [
|
|
9
10
|
"Apple",
|
|
@@ -59,7 +60,7 @@ type AutocompleteTwigProps = {
|
|
|
59
60
|
// covers the part that can be shared - each play function still calls
|
|
60
61
|
// `mount()` itself before wiring this up.
|
|
61
62
|
const wireSearchDemo = (canvasElement: HTMLElement, id: string): void => {
|
|
62
|
-
const root = canvasElement.querySelector
|
|
63
|
+
const root = canvasElement.querySelector("mx-autocomplete")
|
|
63
64
|
const listbox = root?.querySelector<HTMLUListElement>('[role="listbox"]')
|
|
64
65
|
if (!root || !listbox) return
|
|
65
66
|
|
|
@@ -67,17 +68,23 @@ const wireSearchDemo = (canvasElement: HTMLElement, id: string): void => {
|
|
|
67
68
|
// pending response so a stale, since-superseded query can't land after a
|
|
68
69
|
// newer one, which would yank the currently-tracked active option out from
|
|
69
70
|
// under the widget mid-interaction.
|
|
70
|
-
let
|
|
71
|
+
let pendingTimeout: ReturnType<typeof setTimeout> | null = null
|
|
71
72
|
|
|
72
73
|
root.addEventListener("mx-autocomplete-search", event => {
|
|
73
|
-
if (
|
|
74
|
+
if (pendingTimeout) clearTimeout(pendingTimeout)
|
|
74
75
|
const { query } = (event as AutocompleteSearchEvent).detail
|
|
75
76
|
const matches = query
|
|
76
77
|
? fruits.filter(fruit => fruit.label.toLowerCase().includes(query.toLowerCase()))
|
|
77
78
|
: []
|
|
78
79
|
|
|
80
|
+
// `pending` is fully consumer-controlled (see Elements/Autocomplete.ts) -
|
|
81
|
+
// set it before the simulated request starts, clear it once it settles.
|
|
82
|
+
// Only the most recent timeout ever fires (earlier ones were cleared
|
|
83
|
+
// above), so this can't be cleared out of order by a stale response.
|
|
84
|
+
root.pending = true
|
|
85
|
+
|
|
79
86
|
// Simulate the latency of a real search API.
|
|
80
|
-
|
|
87
|
+
pendingTimeout = setTimeout(() => {
|
|
81
88
|
listbox.replaceChildren(
|
|
82
89
|
...matches.map((option, index) => {
|
|
83
90
|
const li = document.createElement("li")
|
|
@@ -90,6 +97,7 @@ const wireSearchDemo = (canvasElement: HTMLElement, id: string): void => {
|
|
|
90
97
|
return li
|
|
91
98
|
}),
|
|
92
99
|
)
|
|
100
|
+
root.pending = false
|
|
93
101
|
}, 150)
|
|
94
102
|
})
|
|
95
103
|
}
|
|
@@ -216,3 +224,72 @@ export const Search: Story = {
|
|
|
216
224
|
type: "search",
|
|
217
225
|
},
|
|
218
226
|
}
|
|
227
|
+
|
|
228
|
+
// Demonstrates the built-in pending state: `wireSearchDemo` above toggles the
|
|
229
|
+
// consumer-controlled `pending` property around its simulated search delay,
|
|
230
|
+
// which the Web Component reflects as `aria-busy` on the control - the same
|
|
231
|
+
// attribute React's implementation sets. A single generic CSS rule
|
|
232
|
+
// (`[aria-busy="true"]` in Utility/utilities.css) drives the spinner for
|
|
233
|
+
// both, and for any other component that reflects `aria-busy`.
|
|
234
|
+
export const Pending: Story = {
|
|
235
|
+
args: {
|
|
236
|
+
type: "search",
|
|
237
|
+
},
|
|
238
|
+
play: async ({ canvasElement, args, mount, canvas, userEvent, step }) => {
|
|
239
|
+
await mount()
|
|
240
|
+
wireSearchDemo(canvasElement, args.id)
|
|
241
|
+
const input = canvas.getByRole("combobox", { name: "Choose a fruit" })
|
|
242
|
+
const control = canvasElement.querySelector(".mx-autocomplete__control")
|
|
243
|
+
if (!control) throw new Error("mx-autocomplete__control not found")
|
|
244
|
+
|
|
245
|
+
await step("Shows a pending state while the simulated search resolves", async () => {
|
|
246
|
+
await userEvent.type(input, "lu")
|
|
247
|
+
await expect(control).toHaveAttribute("aria-busy", "true")
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
await step("Clears pending once the search settles", async () => {
|
|
251
|
+
await waitFor(
|
|
252
|
+
() => {
|
|
253
|
+
expect(control).toHaveAttribute("aria-busy", "false")
|
|
254
|
+
expect(canvas.getByRole("option", { name: "Blueberry" })).toBeInTheDocument()
|
|
255
|
+
},
|
|
256
|
+
{ timeout: 2000 },
|
|
257
|
+
)
|
|
258
|
+
})
|
|
259
|
+
},
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Steps a virtual screen reader through the page with matching results open, and asserts on what it announces.
|
|
264
|
+
*/
|
|
265
|
+
export const ScreenReaderAnnouncements: Story = {
|
|
266
|
+
play: async ({ canvasElement, args, mount, canvas, userEvent }) => {
|
|
267
|
+
await mount()
|
|
268
|
+
wireSearchDemo(canvasElement, args.id)
|
|
269
|
+
const input = canvas.getByRole("combobox", { name: "Choose a fruit" })
|
|
270
|
+
|
|
271
|
+
// "lu" matches exactly two fruits: Blueberry, then Plum.
|
|
272
|
+
await userEvent.type(input, "lu")
|
|
273
|
+
await waitFor(() => expect(input).toHaveAttribute("aria-expanded", "true"), { timeout: 2000 })
|
|
274
|
+
|
|
275
|
+
const log = await runScreenReaderWalkthrough()
|
|
276
|
+
await expect(log).toEqual([
|
|
277
|
+
"document",
|
|
278
|
+
"not busy",
|
|
279
|
+
"combobox, Choose a fruit, lu, has popup listbox, expanded, autocomplete in list, 1 control",
|
|
280
|
+
"end, not busy",
|
|
281
|
+
"listbox, orientated vertically",
|
|
282
|
+
"option, Blueberry, not selected, position 1, set size 2",
|
|
283
|
+
"option, Plum, not selected, position 2, set size 2",
|
|
284
|
+
"end of listbox, orientated vertically",
|
|
285
|
+
"status",
|
|
286
|
+
"2 results available",
|
|
287
|
+
"end of status",
|
|
288
|
+
"end of document",
|
|
289
|
+
])
|
|
290
|
+
|
|
291
|
+
await userEvent.clear(input)
|
|
292
|
+
await expect(input).toHaveAttribute("aria-expanded", "false")
|
|
293
|
+
await userEvent.click(canvasElement)
|
|
294
|
+
},
|
|
295
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { useState } from "react"
|
|
2
2
|
import { Meta, StoryObj } from "@storybook/react-vite"
|
|
3
|
-
import { expect } from "storybook/test"
|
|
3
|
+
import { expect, waitFor } from "storybook/test"
|
|
4
4
|
import Component, { AutocompleteOption } from "./Autocomplete"
|
|
5
5
|
import "./autocomplete.css"
|
|
6
6
|
import { Page } from "../../../.storybook-react/decorators"
|
|
7
7
|
import { FormStatus } from "../../enums"
|
|
8
|
+
import { runScreenReaderWalkthrough } from "../../../tools/test-utils/screen-reader"
|
|
8
9
|
|
|
9
10
|
const fruits = [
|
|
10
11
|
"Apple",
|
|
@@ -147,3 +148,86 @@ export const Search: StoryObj<typeof meta> = {
|
|
|
147
148
|
type: "search",
|
|
148
149
|
},
|
|
149
150
|
}
|
|
151
|
+
|
|
152
|
+
// Demonstrates the built-in pending state: `onSearch` returns a promise
|
|
153
|
+
// (standing in for a real debounced REST/Elasticsearch call), so the
|
|
154
|
+
// component sets `aria-busy` on the control until it resolves. A generic CSS
|
|
155
|
+
// rule (`[aria-busy="true"]` in Utility/utilities.css) shows the spinner -
|
|
156
|
+
// no component-specific markup or class needed.
|
|
157
|
+
export const Pending: StoryObj<typeof meta> = {
|
|
158
|
+
render: args => {
|
|
159
|
+
const [options, setOptions] = useState<AutocompleteOption[]>([])
|
|
160
|
+
|
|
161
|
+
const handleSearch = (query: string): Promise<void> =>
|
|
162
|
+
new Promise(resolve => {
|
|
163
|
+
setTimeout(() => {
|
|
164
|
+
setOptions(
|
|
165
|
+
query
|
|
166
|
+
? fruits.filter(fruit => fruit.label.toLowerCase().includes(query.toLowerCase()))
|
|
167
|
+
: [],
|
|
168
|
+
)
|
|
169
|
+
resolve()
|
|
170
|
+
}, 800)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<Component {...args} options={options} onSearch={handleSearch}>
|
|
175
|
+
{null}
|
|
176
|
+
</Component>
|
|
177
|
+
)
|
|
178
|
+
},
|
|
179
|
+
play: async ({ canvas, userEvent, step }) => {
|
|
180
|
+
const input = canvas.getByRole("combobox", { name: "Choose a fruit" })
|
|
181
|
+
const control = input.closest(".mx-autocomplete__control")
|
|
182
|
+
|
|
183
|
+
await step("Shows a pending spinner while the pending resolves", async () => {
|
|
184
|
+
await userEvent.type(input, "lu")
|
|
185
|
+
await expect(control).toHaveAttribute("aria-busy", "true")
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
await step("Clears pending once the search settles", async () => {
|
|
189
|
+
await waitFor(
|
|
190
|
+
() => {
|
|
191
|
+
expect(control).toHaveAttribute("aria-busy", "false")
|
|
192
|
+
expect(canvas.getByRole("option", { name: "Blueberry" })).toBeInTheDocument()
|
|
193
|
+
},
|
|
194
|
+
{ timeout: 2000 },
|
|
195
|
+
)
|
|
196
|
+
})
|
|
197
|
+
},
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Steps a virtual screen reader through the page with matching results open, and asserts on what it announces.
|
|
202
|
+
*
|
|
203
|
+
* Kept as the last story in this file - see the note on ordering in
|
|
204
|
+
* `docs/Testing.md`'s Automated Screen Reader Testing section.
|
|
205
|
+
*/
|
|
206
|
+
export const ScreenReaderAnnouncements: StoryObj<typeof meta> = {
|
|
207
|
+
play: async ({ canvas, userEvent }) => {
|
|
208
|
+
const input = canvas.getByRole("combobox", { name: "Choose a fruit" })
|
|
209
|
+
|
|
210
|
+
// "lu" matches exactly two fruits: Blueberry, then Plum.
|
|
211
|
+
await userEvent.type(input, "lu")
|
|
212
|
+
await expect(input).toHaveAttribute("aria-expanded", "true")
|
|
213
|
+
|
|
214
|
+
const log = await runScreenReaderWalkthrough()
|
|
215
|
+
await expect(log).toEqual([
|
|
216
|
+
"document",
|
|
217
|
+
"not busy",
|
|
218
|
+
"combobox, Choose a fruit, lu, has popup listbox, expanded, autocomplete in list, 1 control, not invalid",
|
|
219
|
+
"end, not busy",
|
|
220
|
+
"listbox, orientated vertically",
|
|
221
|
+
"option, Blueberry, not selected, position 1, set size 2",
|
|
222
|
+
"option, Plum, not selected, position 2, set size 2",
|
|
223
|
+
"end of listbox, orientated vertically",
|
|
224
|
+
"status",
|
|
225
|
+
"2 results available",
|
|
226
|
+
"end of status",
|
|
227
|
+
"end of document",
|
|
228
|
+
])
|
|
229
|
+
|
|
230
|
+
await userEvent.clear(input)
|
|
231
|
+
await expect(input).toHaveAttribute("aria-expanded", "false")
|
|
232
|
+
},
|
|
233
|
+
}
|