@pnx-mixtape/mxds 0.1.0-rc.1 → 0.1.0-rc.3
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 → preview.tsx} +8 -1
- package/.storybook/src/docs.tsx +29 -0
- 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/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/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/DropMenu-1baQc8Yc.js +1 -0
- package/dist/build/chunks/Gallery-D8NGXy2Q.js +1 -0
- package/dist/build/chunks/GlobalToggle-B52fdBQu.js +1 -0
- package/dist/build/chunks/InPageNavigation-Dyn2TR-N.js +1 -0
- package/dist/build/chunks/Navigation-hOrfLq7h.js +1 -0
- package/dist/build/chunks/Popover-Cxg-s5Jn.js +1 -0
- package/dist/build/chunks/Sticky-BGCuuEJK.js +1 -0
- package/dist/build/chunks/Tabs-DYy1YTdU.js +15 -0
- package/dist/build/chunks/enums-DLV_1NAf.js +1 -0
- package/dist/build/chunks/polyfills-C7pRt54M.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.entry.js +1 -1
- package/dist/build/drop-menu.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.entry.js +1 -1
- package/dist/build/grid.css +1 -1
- package/dist/build/header.entry.js +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/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/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/sidebar.css +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/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/Link/Link.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/Accordion/Accordion.d.ts.map +1 -1
- package/dist/types/Component/Dialog/Dialog.d.ts.map +1 -1
- package/dist/types/Component/DropMenu/Elements/DropMenu.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/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/Pagination/Components/PaginationContext.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/Tabs/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 +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/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/OneTimeCode/OneTimeCode.d.ts +12 -0
- package/dist/types/Form/OneTimeCode/OneTimeCode.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/Utility/Hooks/useIsomorphicLayoutEffect.d.ts +4 -0
- package/dist/types/Utility/Hooks/useIsomorphicLayoutEffect.d.ts.map +1 -0
- package/dist/types/enums.d.ts +19 -0
- package/dist/types/enums.d.ts.map +1 -1
- package/dist/types/react.d.ts +5 -0
- package/dist/types/react.d.ts.map +1 -1
- package/package.json +8 -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/__snapshots__/Button.stories.ts.snap +2 -3
- package/src/Atom/Button/_buttons-styles.css +6 -6
- package/src/Atom/Button/_buttons.css +21 -3
- package/src/Atom/Button/button.twig +4 -1
- package/src/Atom/DefinitionList/DefinitionList.stories.ts +42 -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 +39 -36
- package/src/Atom/Image/README.md +2 -2
- package/src/Atom/Image/_image.css +12 -6
- 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/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.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 +2 -0
- 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 +13 -24
- package/src/Component/Card/card.css +2 -2
- package/src/Component/Card/card.twig +1 -1
- package/src/Component/Carousel/Carousel.stories.ts +101 -23
- 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/ContentBlock/__snapshots__/ContentBlock.stories.ts.snap +12 -24
- package/src/Component/CookieConsent/cookie-consent.css +3 -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/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/Elements/DropMenu.ts +10 -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/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/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 +83 -0
- package/src/Component/InPageNavigation/InPageNavigation.stories.tsx +130 -0
- package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.ts.snap +61 -1
- package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.tsx.snap +4 -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/LinkList/__snapshots__/LinkList.stories.ts.snap +6 -12
- package/src/Component/ListItem/__snapshots__/ListItem.stories.ts.snap +11 -18
- package/src/Component/ListItem/list-item.css +1 -1
- package/src/Component/ListItem/list-item.twig +2 -2
- 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.css +1 -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 +44 -4
- 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/Pagination/pagination.css +2 -2
- package/src/Component/Popover/__snapshots__/Popover.stories.ts.snap +13 -26
- package/src/Component/Popover/popover.css +1 -1
- 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/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/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 +77 -21
- 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 +20 -2
- package/src/Component/Tabs/__snapshots__/Tabs.stories.tsx.snap +3 -1
- package/src/Component/Tabs/tabs.css +2 -0
- 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 +89 -7
- package/src/Form/Autocomplete/Autocomplete.stories.tsx +85 -1
- package/src/Form/Autocomplete/Autocomplete.tsx +20 -10
- package/src/Form/Autocomplete/Elements/Autocomplete.ts +24 -1
- package/src/Form/Autocomplete/README.md +26 -26
- package/src/Form/Autocomplete/__snapshots__/Autocomplete.stories.ts.snap +39 -5
- 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 -3
- 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/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/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 +240 -55
- package/src/Layout/Footer/__snapshots__/Footer.stories.ts.snap +18 -33
- package/src/Layout/Grid/grid.css +4 -4
- 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/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/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/Hooks/useIsomorphicLayoutEffect.ts +8 -0
- package/src/Utility/_layout-utils.css +8 -0
- package/src/Utility/_mixins.css +24 -0
- package/src/Utility/print.css +51 -29
- package/src/Utility/utilities.css +22 -2
- package/src/_custom-media.css +1 -0
- package/src/constants.css +12 -2
- package/src/enums.ts +22 -0
- package/src/react.ts +5 -0
- package/tools/lib/extract-classnames.mjs +0 -0
- package/tools/lint-classnames.mjs +1 -1
- package/tools/test-utils/screen-reader.ts +33 -0
- package/dist/build/chunks/DropMenu-Co9jZZ4S.js +0 -1
- package/dist/build/chunks/Gallery-BT3cxS5m.js +0 -1
- package/dist/build/chunks/GlobalToggle-DQHho97V.js +0 -1
- package/dist/build/chunks/InPageNavigation-CzR_KZaz.js +0 -1
- package/dist/build/chunks/Navigation-B9Wug-ok.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/Tabs-Dm8g301e.js +0 -15
- package/dist/build/chunks/disclosure-widget-BETzOHLO.js +0 -1
- package/dist/build/chunks/polyfills-Dokbkxxk.js +0 -1
- package/dist/build/chunks/utilities-C5dAO2YA.js +0 -1
|
@@ -39,14 +39,15 @@ A small inline label used to categorise content. Renders as plain text metadata,
|
|
|
39
39
|
|
|
40
40
|
**tags.twig props** (`Tags` type from `ids-shape`):
|
|
41
41
|
|
|
42
|
-
| Prop | Type | Description
|
|
43
|
-
| --------------- | ------------------------------------- |
|
|
44
|
-
| `type` | `TagTypes` | Controls which markup is rendered per item. One of `text`, `link`, `checkbox`. Changing the type also changes the required shape of `items`.
|
|
45
|
-
| `items` | `string[] \| Link[] \| FormControl[]` | List of tag items. Shape depends on `type` — see below.
|
|
46
|
-
| `attributes` | `Attribute` | Extra attributes on the outer `<ul class="mx-tags">`
|
|
47
|
-
| `tagAttributes` | `Attribute` | Extra attributes on each `.mx-tag` element
|
|
48
|
-
| `dismissible` | `boolean` | When true, plain (`text`) tags render a remove button (`mx-tag--dismissible`). Wire up removal behaviour in the consuming app.
|
|
49
|
-
| `removeLabel` | `string` | Accessible label for the remove button. Defaults to `"Remove"`.
|
|
42
|
+
| Prop | Type | Description |
|
|
43
|
+
| --------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
44
|
+
| `type` | `TagTypes` | Controls which markup is rendered per item. One of `text`, `link`, `checkbox`. Changing the type also changes the required shape of `items`. |
|
|
45
|
+
| `items` | `string[] \| Link[] \| FormControl[]` | List of tag items. Shape depends on `type` — see below. |
|
|
46
|
+
| `attributes` | `Attribute` | Extra attributes on the outer `<ul class="mx-tags">` |
|
|
47
|
+
| `tagAttributes` | `Attribute` | Extra attributes on each `.mx-tag` element |
|
|
48
|
+
| `dismissible` | `boolean` | When true, plain (`text`) tags render a remove button (`mx-tag--dismissible`). Wire up removal behaviour in the consuming app. |
|
|
49
|
+
| `removeLabel` | `string` | Accessible label for the remove button. Defaults to `"Remove"`. |
|
|
50
|
+
| `variant` | `TagVariants` | Colour variant: `info`, `success`, `warning`, `error`, `featured`. Applied per-item via `tag-item.twig`. Defaults to the neutral, unstyled tag. |
|
|
50
51
|
|
|
51
52
|
**Item shapes per `type`:**
|
|
52
53
|
|
|
@@ -78,20 +79,20 @@ Link tags:
|
|
|
78
79
|
</ul>
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
Checkbox tags (the `<input>` is visually hidden off-screen; the `<label>` is the click target):
|
|
82
|
+
Checkbox tags (the `<input>` is visually hidden off-screen; the `<label>` is the click target). The `mx-icon mx-icon--tick` classes on the label render the shared checkmark icon (from `@pnx-mixtape/mxds/src/Atom/Icon/icon.css`) — its space is always reserved, but it's only visible once the input is checked, so checking a tag doesn't change its size:
|
|
82
83
|
|
|
83
84
|
```html
|
|
84
85
|
<ul class="mx-tags">
|
|
85
86
|
<li>
|
|
86
87
|
<div class="mx-tag">
|
|
87
88
|
<input type="checkbox" id="music" name="tag" value="music" />
|
|
88
|
-
<label for="music">Music</label>
|
|
89
|
+
<label class="mx-icon mx-icon--tick" for="music">Music</label>
|
|
89
90
|
</div>
|
|
90
91
|
</li>
|
|
91
92
|
<li>
|
|
92
93
|
<div class="mx-tag">
|
|
93
94
|
<input type="checkbox" id="news" name="tag" value="news" checked />
|
|
94
|
-
<label for="news">News</label>
|
|
95
|
+
<label class="mx-icon mx-icon--tick" for="news">News</label>
|
|
95
96
|
</div>
|
|
96
97
|
</li>
|
|
97
98
|
</ul>
|
|
@@ -131,7 +132,7 @@ import { AsTags } from "@pnx-mixtape/mxds/src/enums"
|
|
|
131
132
|
<li>
|
|
132
133
|
<div className="mx-tag">
|
|
133
134
|
<input type="checkbox" id="music" name="tag" value="music" />
|
|
134
|
-
<label htmlFor="music">Music</label>
|
|
135
|
+
<label className="mx-icon mx-icon--tick" htmlFor="music">Music</label>
|
|
135
136
|
</div>
|
|
136
137
|
</li>
|
|
137
138
|
</ul>
|
|
@@ -139,13 +140,14 @@ import { AsTags } from "@pnx-mixtape/mxds/src/enums"
|
|
|
139
140
|
|
|
140
141
|
**`Tag` props:**
|
|
141
142
|
|
|
142
|
-
| Prop | Type
|
|
143
|
-
| ------------- |
|
|
144
|
-
| `as` | `AsTags`
|
|
145
|
-
| `className` | `string`
|
|
146
|
-
| `children` | `ReactNode`
|
|
147
|
-
| `onRemove` | `() => void`
|
|
148
|
-
| `removeLabel` | `string`
|
|
143
|
+
| Prop | Type | Default | Description |
|
|
144
|
+
| ------------- | ------------- | ---------- | --------------------------------------------------------------------------------------------------------- |
|
|
145
|
+
| `as` | `AsTags` | `div` | Semantic element for the tag: `div`, `li`, `article`, `aside`, `section` |
|
|
146
|
+
| `className` | `string` | — | Extra class on the tag element |
|
|
147
|
+
| `children` | `ReactNode` | — | Tag label content |
|
|
148
|
+
| `onRemove` | `() => void` | — | When set, renders a remove button (`mx-tag--dismissible`). Consumer removes the tag from state. |
|
|
149
|
+
| `removeLabel` | `string` | `"Remove"` | Accessible label for the remove button |
|
|
150
|
+
| `variant` | `TagVariants` | — | Colour variant: `info`, `success`, `warning`, `error`, `featured`. Defaults to the neutral, unstyled tag. |
|
|
149
151
|
|
|
150
152
|
> For filter UIs, tag removal can also be handled upstream (e.g. via the checkbox type toggling its checked state, or via the Filters component). Use `onRemove` when you need a self-contained dismissible chip.
|
|
151
153
|
|
|
@@ -155,11 +157,11 @@ import { AsTags } from "@pnx-mixtape/mxds/src/enums"
|
|
|
155
157
|
|
|
156
158
|
The `type` prop on the Twig template maps to `TagTypes` values from `@pnx-mixtape/ids-shape`. The same base `mx-tag` class is used for all variants — the variant changes the inner element, not the class.
|
|
157
159
|
|
|
158
|
-
| `TagTypes` value | Element rendered | Visual intent
|
|
159
|
-
| ---------------- | --------------------------------------------------------------------- |
|
|
160
|
-
| `text` | `<span class="mx-tag">` | Read-only metadata label (e.g. category shown on a Card)
|
|
161
|
-
| `link` | `<a class="mx-tag" href>` | Navigational tag — clicking takes the user to a filtered / category view
|
|
162
|
-
| `checkbox` | `<div class="mx-tag">` wrapping `<input type="checkbox">` + `<label>` | Toggleable filter tag inside a form; checked state
|
|
160
|
+
| `TagTypes` value | Element rendered | Visual intent |
|
|
161
|
+
| ---------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
162
|
+
| `text` | `<span class="mx-tag">` | Read-only metadata label (e.g. category shown on a Card) |
|
|
163
|
+
| `link` | `<a class="mx-tag" href>` | Navigational tag — clicking takes the user to a filtered / category view |
|
|
164
|
+
| `checkbox` | `<div class="mx-tag">` wrapping `<input type="checkbox">` + `<label>` | Toggleable filter tag inside a form; checked state fills the tag with the primary link colour (or the variant's own colour) and prefixes the label with a checkmark icon |
|
|
163
165
|
|
|
164
166
|
---
|
|
165
167
|
|
|
@@ -168,7 +170,7 @@ The `type` prop on the Twig template maps to `TagTypes` values from `@pnx-mixtap
|
|
|
168
170
|
- **Text tags** are plain `<span>` elements — they are metadata, not interactive. Do not add click handlers or `role` attributes; if a tag needs to be clickable, use the `link` or `checkbox` variant.
|
|
169
171
|
- **Link tags** use a real `<a href>`. Ensure the destination makes sense out of context — the visible label is the accessible name, so labels like "Music" should lead to an unambiguous page (e.g. `/category/music`).
|
|
170
172
|
- **Checkbox tags** rely on a native `<input type="checkbox">` paired with a `<label for="…">`. The `<input>` is positioned off-screen (not `display: none`) so it remains focusable and announceable. The whole `.mx-tag` gets a focus outline via `:has(input:focus)` — do not remove this selector.
|
|
171
|
-
- The checked state is communicated by colour
|
|
173
|
+
- The checked state is communicated by colour (a solid fill) **and** a checkmark icon prefixed to the label — not colour alone. When rendering checkbox tags in a form, ensure surrounding context (a fieldset legend, heading, or `aria-label`) describes what group of filters the tags belong to.
|
|
172
174
|
- If you need to render a list of tags for assistive-tech as a single labelled group, add `aria-label` (or `aria-labelledby`) to the `<ul class="mx-tags">`:
|
|
173
175
|
|
|
174
176
|
```html
|
|
@@ -201,7 +203,31 @@ The `type` prop on the Twig template maps to `TagTypes` values from `@pnx-mixtap
|
|
|
201
203
|
}
|
|
202
204
|
```
|
|
203
205
|
|
|
204
|
-
Hover (on `a.mx-tag`)
|
|
206
|
+
Hover (on `a.mx-tag`) sets `--tag-foreground` to `--link-colour` (falls back to `--colour-primary`). Checked (`:has(input:checked)`) fills `--tag-background`/`--line-colour` with that same colour and switches `--tag-foreground` to `--colour-foreground-reverse`, plus prefixes the label with a checkmark icon — except on a colour variant (see below), where checked keeps the variant's own colours and hover darkens them instead.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Colour variants — `TagVariants` enum
|
|
211
|
+
|
|
212
|
+
Apply `mx-tag--{variant}` for `info`, `success`, `warning`, `error`, or `featured`. Each variant fills `--tag-background` and sets the border/outline to match; foreground uses `--colour-foreground-reverse` (`featured` excepted, which stays dark-on-light).
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
<Tag variant={TagVariants.INFO}>New</Tag>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`tags.twig` itself only prints pre-rendered `items` markup — `variant`, like `dismissible`, is applied per item when building that markup with `tag-item.twig`:
|
|
219
|
+
|
|
220
|
+
```twig
|
|
221
|
+
{{ include('@mixtape/Component/Tag/tag-item.twig', {
|
|
222
|
+
type: 'text',
|
|
223
|
+
item: 'New',
|
|
224
|
+
variant: 'info',
|
|
225
|
+
}) }}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
- A checked variant tag keeps its own colours (the solid fill and "checked" link colour swap only apply to non-variant tags) — it's already a solid fill. The checkmark icon on checked still applies.
|
|
229
|
+
- The focus outline (`:has(input:focus)`) on a variant tag uses the variant's own colour instead of the standard blue.
|
|
230
|
+
- Hovering a variant **link** tag darkens its background/border by 20% (via `color-mix`) rather than switching to the link hover colour; the text colour is unchanged. `featured` only darkens by 10% — it keeps dark text on its light background, so a smaller step protects contrast.
|
|
205
231
|
|
|
206
232
|
---
|
|
207
233
|
|
|
@@ -2,6 +2,7 @@ import { Meta, StoryObj } from "@storybook/react-vite"
|
|
|
2
2
|
import Component from "./Tag"
|
|
3
3
|
import "./tag.css"
|
|
4
4
|
import { Page } from "../../../.storybook-react/decorators"
|
|
5
|
+
import { TagVariants } from "../../enums"
|
|
5
6
|
|
|
6
7
|
const meta: Meta<typeof Component> = {
|
|
7
8
|
component: Component,
|
|
@@ -9,7 +10,30 @@ const meta: Meta<typeof Component> = {
|
|
|
9
10
|
args: {
|
|
10
11
|
children: "Tag name",
|
|
11
12
|
},
|
|
13
|
+
argTypes: {
|
|
14
|
+
variant: {
|
|
15
|
+
control: {
|
|
16
|
+
type: "select",
|
|
17
|
+
labels: { undefined: "default" },
|
|
18
|
+
},
|
|
19
|
+
options: [undefined, ...Object.values(TagVariants)],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
12
22
|
}
|
|
13
23
|
|
|
14
24
|
export default meta
|
|
15
25
|
export const Tag: StoryObj<typeof meta> = {}
|
|
26
|
+
|
|
27
|
+
const variantsListStyle = { display: "flex", gap: "1rem" }
|
|
28
|
+
|
|
29
|
+
export const Variants: StoryObj<typeof meta> = {
|
|
30
|
+
render: args => (
|
|
31
|
+
<div style={variantsListStyle}>
|
|
32
|
+
{Object.values(TagVariants).map(variant => (
|
|
33
|
+
<Component key={variant} {...args} variant={variant}>
|
|
34
|
+
{variant}
|
|
35
|
+
</Component>
|
|
36
|
+
))}
|
|
37
|
+
</div>
|
|
38
|
+
),
|
|
39
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { JSX, PropsWithChildren } from "react"
|
|
2
2
|
import classNames from "classnames"
|
|
3
|
-
import { AsTags, Icons } from "../../enums"
|
|
3
|
+
import { AsTags, Icons, TagVariants } from "../../enums"
|
|
4
4
|
|
|
5
5
|
type TagProps = PropsWithChildren & {
|
|
6
6
|
className?: string
|
|
7
7
|
as?: AsTags
|
|
8
|
+
/** Colour variant. Defaults to the neutral, unstyled tag. */
|
|
9
|
+
variant?: TagVariants
|
|
8
10
|
/**
|
|
9
11
|
* When provided, renders a remove button. Called when the button is
|
|
10
12
|
* activated — the consumer is responsible for removing the tag from state.
|
|
@@ -18,12 +20,20 @@ const Tag = ({
|
|
|
18
20
|
children,
|
|
19
21
|
className,
|
|
20
22
|
as = AsTags.DIV,
|
|
23
|
+
variant,
|
|
21
24
|
onRemove,
|
|
22
25
|
removeLabel = "Remove",
|
|
23
26
|
}: TagProps): JSX.Element => {
|
|
24
27
|
const TagEl: JSX.ElementType = `${as}` as unknown as JSX.ElementType
|
|
25
28
|
return (
|
|
26
|
-
<TagEl
|
|
29
|
+
<TagEl
|
|
30
|
+
className={classNames(
|
|
31
|
+
"mx-tag",
|
|
32
|
+
{ "mx-tag--dismissible": onRemove },
|
|
33
|
+
variant && `mx-tag--${variant}`,
|
|
34
|
+
className,
|
|
35
|
+
)}
|
|
36
|
+
>
|
|
27
37
|
{children}
|
|
28
38
|
{onRemove && (
|
|
29
39
|
<button
|
|
@@ -2,6 +2,7 @@ import { Meta, StoryObj } from "@storybook/html-vite"
|
|
|
2
2
|
import Component from "./tag-item.twig"
|
|
3
3
|
import "./tag.css"
|
|
4
4
|
import { TagTypes } from "@pnx-mixtape/ids-shape"
|
|
5
|
+
import { TagVariants } from "../../enums"
|
|
5
6
|
|
|
6
7
|
type TagItemType = {
|
|
7
8
|
type: TagTypes
|
|
@@ -11,6 +12,7 @@ type TagItemType = {
|
|
|
11
12
|
| { label: string; id: string; name: string; value: string; checked?: boolean }
|
|
12
13
|
dismissible?: boolean
|
|
13
14
|
removeLabel?: string
|
|
15
|
+
variant?: TagVariants
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
/**
|
|
@@ -49,6 +51,17 @@ const meta: Meta<TagItemType> = {
|
|
|
49
51
|
"When true, plain (text) tags render a remove button. Wire up removal behaviour in the consuming app.",
|
|
50
52
|
control: "boolean",
|
|
51
53
|
},
|
|
54
|
+
variant: {
|
|
55
|
+
description: "Colour variant.",
|
|
56
|
+
control: {
|
|
57
|
+
type: "select",
|
|
58
|
+
labels: { undefined: "default" },
|
|
59
|
+
},
|
|
60
|
+
options: [undefined, ...Object.values(TagVariants)],
|
|
61
|
+
table: {
|
|
62
|
+
type: { summary: "enum" },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
52
65
|
},
|
|
53
66
|
render: args => {
|
|
54
67
|
return `<ul class="mx-tags">${Component({ ...args })}</ul>`
|
|
@@ -99,3 +112,15 @@ export const Checkbox: Story = {
|
|
|
99
112
|
},
|
|
100
113
|
},
|
|
101
114
|
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Colour variants for conveying status or emphasis.
|
|
118
|
+
*/
|
|
119
|
+
export const Variants: Story = {
|
|
120
|
+
render: () => {
|
|
121
|
+
const items = Object.values(TagVariants)
|
|
122
|
+
.map(variant => Component({ type: TagTypes.TEXT, item: variant, variant }))
|
|
123
|
+
.join("")
|
|
124
|
+
return `<ul class="mx-tags">${items}</ul>`
|
|
125
|
+
},
|
|
126
|
+
}
|
|
@@ -6,13 +6,13 @@ exports[`Checkbox 1`] = `
|
|
|
6
6
|
<li>
|
|
7
7
|
<div class="mx-tag">
|
|
8
8
|
<input type="checkbox" id="music" name="tag" value="music">
|
|
9
|
-
<label for="music">Music</label>
|
|
9
|
+
<label class="mx-icon mx-icon--tick" for="music">Music</label>
|
|
10
10
|
</div>
|
|
11
11
|
</li>
|
|
12
12
|
<li>
|
|
13
13
|
<div class="mx-tag">
|
|
14
14
|
<input type="checkbox" id="news" name="tag" value="news">
|
|
15
|
-
<label for="news">News</label>
|
|
15
|
+
<label class="mx-icon mx-icon--tick" for="news">News</label>
|
|
16
16
|
</div>
|
|
17
17
|
</li>
|
|
18
18
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
3
|
exports[`Tag 1`] = `"<div class="mx-tag">Tag name</div>"`;
|
|
4
|
+
|
|
5
|
+
exports[`Variants 1`] = `"<div style="display: flex; gap: 1rem;"><div class="mx-tag mx-tag--info">info</div><div class="mx-tag mx-tag--success">success</div><div class="mx-tag mx-tag--warning">warning</div><div class="mx-tag mx-tag--error">error</div><div class="mx-tag mx-tag--featured">featured</div></div>"`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{% set tagClasses = ['mx-tag', dismissible ? 'mx-tag--dismissible' : null] %}
|
|
1
|
+
{% set tagClasses = ['mx-tag', dismissible ? 'mx-tag--dismissible' : null, variant ? 'mx-tag--' ~ variant : null] %}
|
|
2
2
|
{% set tagAttributes = (tagAttributes ?? create_attribute()).addClass(tagClasses) %}
|
|
3
3
|
<li>
|
|
4
4
|
{% if type == "link" %}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
name="{{ item.name }}"
|
|
11
11
|
value="{{ item.value }}"
|
|
12
12
|
{% if item.checked %} checked{% endif %}>
|
|
13
|
-
<label for="{{ item.id }}">{{ item.label }}</label>
|
|
13
|
+
<label class="mx-icon mx-icon--tick" for="{{ item.id }}">{{ item.label }}</label>
|
|
14
14
|
</div>
|
|
15
15
|
{% else %}
|
|
16
16
|
<span{{ tagAttributes }}>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
@layer design-system.components {
|
|
6
|
-
/* @public --tag-background --tag-foreground --tag-font-size --tags-gap */
|
|
6
|
+
/* @public --tag-background --tag-foreground --tag-font-size --tags-gap --tag-letter-spacing */
|
|
7
7
|
.mx-tag {
|
|
8
8
|
display: inline-flex;
|
|
9
9
|
inline-size: fit-content;
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
line-height: var(--line-height, 2);
|
|
17
17
|
border-radius: var(--border-radius);
|
|
18
18
|
border: var(--line-width, 1px) solid var(--line-colour, currentcolor);
|
|
19
|
+
letter-spacing: var(--tag-letter-spacing, 0);
|
|
19
20
|
text-decoration: none;
|
|
20
21
|
overflow: clip;
|
|
21
22
|
overflow-clip-margin: var(--spacing-xxxs);
|
|
@@ -36,12 +37,35 @@
|
|
|
36
37
|
font-size: inherit;
|
|
37
38
|
color: inherit;
|
|
38
39
|
margin: 0;
|
|
40
|
+
gap: var(--spacing-xxxs);
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
/* the checkmark icon (from the shared mx-icon set) is always reserved so
|
|
44
|
+
checking the tag doesn't change its size — only its visibility toggles */
|
|
45
|
+
& label.mx-icon--tick::before {
|
|
46
|
+
--icon-size: 1em;
|
|
47
|
+
|
|
48
|
+
opacity: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&:is(a:hover):not(
|
|
52
|
+
:is(.mx-tag--info, .mx-tag--success, .mx-tag--warning, .mx-tag--error, .mx-tag--featured)
|
|
53
|
+
) {
|
|
42
54
|
--tag-foreground: var(--link-colour, var(--colour-primary));
|
|
43
55
|
}
|
|
44
56
|
|
|
57
|
+
&:has(input:checked):not(
|
|
58
|
+
:is(.mx-tag--info, .mx-tag--success, .mx-tag--warning, .mx-tag--error, .mx-tag--featured)
|
|
59
|
+
) {
|
|
60
|
+
--tag-background: var(--link-colour, var(--colour-primary));
|
|
61
|
+
--tag-foreground: var(--colour-foreground-reverse);
|
|
62
|
+
--line-colour: var(--tag-background);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&:has(input:checked) label.mx-icon--tick::before {
|
|
66
|
+
opacity: 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
45
69
|
&:empty {
|
|
46
70
|
display: none;
|
|
47
71
|
}
|
|
@@ -76,6 +100,56 @@
|
|
|
76
100
|
}
|
|
77
101
|
}
|
|
78
102
|
|
|
103
|
+
.mx-tag--info,
|
|
104
|
+
.mx-tag--success,
|
|
105
|
+
.mx-tag--warning,
|
|
106
|
+
.mx-tag--error,
|
|
107
|
+
.mx-tag--featured {
|
|
108
|
+
--line-colour: var(--tag-background);
|
|
109
|
+
--outline-color: var(--tag-background);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.mx-tag--info {
|
|
113
|
+
--tag-accent: var(--colour-info);
|
|
114
|
+
--tag-background: var(--tag-accent);
|
|
115
|
+
--tag-foreground: var(--colour-foreground-reverse);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.mx-tag--success {
|
|
119
|
+
--tag-accent: var(--colour-success);
|
|
120
|
+
--tag-background: var(--tag-accent);
|
|
121
|
+
--tag-foreground: var(--colour-foreground-reverse);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.mx-tag--warning {
|
|
125
|
+
--tag-accent: var(--colour-warning);
|
|
126
|
+
--tag-background: var(--tag-accent);
|
|
127
|
+
--tag-foreground: var(--colour-foreground-reverse);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.mx-tag--error {
|
|
131
|
+
--tag-accent: var(--colour-error);
|
|
132
|
+
--tag-background: var(--tag-accent);
|
|
133
|
+
--tag-foreground: var(--colour-foreground-reverse);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.mx-tag--featured {
|
|
137
|
+
--tag-accent: var(--colour-featured, var(--colour-accent));
|
|
138
|
+
--tag-background: var(--tag-accent);
|
|
139
|
+
--tag-foreground: var(--colour-featured-foreground, var(--colour-foreground));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.mx-tag:is(.mx-tag--info, .mx-tag--success, .mx-tag--warning, .mx-tag--error):is(a:hover) {
|
|
143
|
+
--tag-background: color-mix(in oklch, var(--tag-accent), black 20%);
|
|
144
|
+
--line-colour: var(--tag-background);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.mx-tag--featured:is(a:hover) {
|
|
148
|
+
/* featured keeps dark text, so a smaller darken step protects contrast */
|
|
149
|
+
--tag-background: color-mix(in oklch, var(--tag-accent), black 10%);
|
|
150
|
+
--line-colour: var(--tag-background);
|
|
151
|
+
}
|
|
152
|
+
|
|
79
153
|
.mx-tags {
|
|
80
154
|
padding-inline: 0;
|
|
81
155
|
list-style: none;
|
|
@@ -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
|
}
|
|
@@ -185,9 +193,14 @@ export const Autocomplete: Story = {
|
|
|
185
193
|
await userEvent.clear(input)
|
|
186
194
|
await expect(input).toHaveValue("")
|
|
187
195
|
await expect(input).toHaveAttribute("aria-expanded", "false")
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
196
|
+
// `hidden: true` includes the listbox even while it's closed (`hidden`
|
|
197
|
+
// attribute set) - otherwise this resolves immediately regardless of
|
|
198
|
+
// whether the stale options were actually removed, since closed
|
|
199
|
+
// elements are excluded from accessibility queries by default.
|
|
200
|
+
await waitFor(
|
|
201
|
+
() => expect(canvas.queryByRole("option", { hidden: true })).not.toBeInTheDocument(),
|
|
202
|
+
{ timeout: 2000 },
|
|
203
|
+
)
|
|
191
204
|
// Clear focus.
|
|
192
205
|
await userEvent.click(canvasElement)
|
|
193
206
|
})
|
|
@@ -211,3 +224,72 @@ export const Search: Story = {
|
|
|
211
224
|
type: "search",
|
|
212
225
|
},
|
|
213
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
|
+
}
|