@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.
Files changed (344) hide show
  1. package/.storybook/decorators.ts +15 -0
  2. package/.storybook/{preview.ts → preview.tsx} +8 -1
  3. package/.storybook/src/docs.tsx +29 -0
  4. package/.storybook/themes/campus-vibrant.css +181 -0
  5. package/.storybook/themes/civic-formal.css +246 -0
  6. package/.storybook/{theme-demo.css → themes/demo.css} +14 -7
  7. package/.storybook/themes/health-services.css +255 -0
  8. package/.storybook/themes/research-faculty.css +169 -0
  9. package/dist/build/accordion.css +1 -1
  10. package/dist/build/accordion.entry.js +1 -1
  11. package/dist/build/autocomplete.css +1 -1
  12. package/dist/build/autocomplete.entry.js +1 -1
  13. package/dist/build/base.css +1 -1
  14. package/dist/build/breadcrumb.css +1 -1
  15. package/dist/build/button.css +1 -1
  16. package/dist/build/card.css +1 -1
  17. package/dist/build/chunks/{Accordion-z-dH6nFH.js → Accordion-GN0sME4W.js} +1 -1
  18. package/dist/build/chunks/{AccordionMobile-BYPPNyf5.js → AccordionMobile-DG5jSe8Q.js} +1 -1
  19. package/dist/build/chunks/{Autocomplete-2L6tLLuh.js → Autocomplete-B4RtkAyh.js} +1 -1
  20. package/dist/build/chunks/{ClosableAlert-DdXbF1J2.js → ClosableAlert-DEhj8Nxm.js} +1 -1
  21. package/dist/build/chunks/{Dialog-Ds8gzhri.js → Dialog-Haahn-Yn.js} +1 -1
  22. package/dist/build/chunks/DropMenu-1baQc8Yc.js +1 -0
  23. package/dist/build/chunks/Gallery-D8NGXy2Q.js +1 -0
  24. package/dist/build/chunks/GlobalToggle-B52fdBQu.js +1 -0
  25. package/dist/build/chunks/InPageNavigation-Dyn2TR-N.js +1 -0
  26. package/dist/build/chunks/Navigation-hOrfLq7h.js +1 -0
  27. package/dist/build/chunks/Popover-Cxg-s5Jn.js +1 -0
  28. package/dist/build/chunks/Sticky-BGCuuEJK.js +1 -0
  29. package/dist/build/chunks/Tabs-DYy1YTdU.js +15 -0
  30. package/dist/build/chunks/enums-DLV_1NAf.js +1 -0
  31. package/dist/build/chunks/polyfills-C7pRt54M.js +1 -0
  32. package/dist/build/chunks/utilities-_Bq3TxAQ.js +1 -0
  33. package/dist/build/constants.css +1 -1
  34. package/dist/build/cookie-consent.css +1 -1
  35. package/dist/build/dialog.entry.js +1 -1
  36. package/dist/build/drop-menu.entry.js +1 -1
  37. package/dist/build/elements.js +1 -1
  38. package/dist/build/filters.css +1 -1
  39. package/dist/build/filters.entry.js +1 -1
  40. package/dist/build/form.css +1 -1
  41. package/dist/build/gallery.entry.js +1 -1
  42. package/dist/build/global-alert.entry.js +1 -1
  43. package/dist/build/grid.css +1 -1
  44. package/dist/build/header.entry.js +1 -1
  45. package/dist/build/icon.css +1 -1
  46. package/dist/build/in-page-alert.css +1 -1
  47. package/dist/build/in-page-navigation.entry.js +1 -1
  48. package/dist/build/list-item.css +1 -1
  49. package/dist/build/navigation.css +1 -1
  50. package/dist/build/navigation.entry.js +1 -1
  51. package/dist/build/one-time-code.css +1 -0
  52. package/dist/build/popover.css +1 -1
  53. package/dist/build/popover.entry.js +1 -1
  54. package/dist/build/print.css +1 -1
  55. package/dist/build/progress-indicator.css +1 -0
  56. package/dist/build/react.js +1 -1
  57. package/dist/build/sidebar.css +1 -1
  58. package/dist/build/steps.css +1 -1
  59. package/dist/build/sticky.entry.js +1 -1
  60. package/dist/build/tabs.css +1 -1
  61. package/dist/build/tabs.entry.js +1 -1
  62. package/dist/build/tag.css +1 -1
  63. package/dist/build/utilities.css +1 -1
  64. package/dist/types/Atom/DefinitionList/DefinitionList.d.ts +3 -2
  65. package/dist/types/Atom/DefinitionList/DefinitionList.d.ts.map +1 -1
  66. package/dist/types/Atom/Heading/Heading.d.ts.map +1 -1
  67. package/dist/types/Atom/Link/Link.d.ts.map +1 -1
  68. package/dist/types/Atom/List/List.d.ts +10 -0
  69. package/dist/types/Atom/List/List.d.ts.map +1 -0
  70. package/dist/types/Component/Accordion/Accordion.d.ts.map +1 -1
  71. package/dist/types/Component/Dialog/Dialog.d.ts.map +1 -1
  72. package/dist/types/Component/DropMenu/Elements/DropMenu.d.ts.map +1 -1
  73. package/dist/types/Component/Filters/Elements/Filters.d.ts +20 -1
  74. package/dist/types/Component/Filters/Elements/Filters.d.ts.map +1 -1
  75. package/dist/types/Component/Filters/Filters.d.ts +7 -2
  76. package/dist/types/Component/Filters/Filters.d.ts.map +1 -1
  77. package/dist/types/Component/Gallery/Gallery.d.ts.map +1 -1
  78. package/dist/types/Component/InPageNavigation/Elements/InPageNavigation.d.ts +2 -2
  79. package/dist/types/Component/InPageNavigation/Elements/InPageNavigation.d.ts.map +1 -1
  80. package/dist/types/Component/InPageNavigation/Hooks/useInPageNavigation.d.ts.map +1 -1
  81. package/dist/types/Component/Pagination/Components/PaginationContext.d.ts.map +1 -1
  82. package/dist/types/Component/ProgressIndicator/ProgressIndicator.d.ts +9 -0
  83. package/dist/types/Component/ProgressIndicator/ProgressIndicator.d.ts.map +1 -0
  84. package/dist/types/Component/Tabs/Elements/Tabs.d.ts.map +1 -1
  85. package/dist/types/Component/Tabs/Tabs.d.ts.map +1 -1
  86. package/dist/types/Component/Tag/Tag.d.ts +4 -2
  87. package/dist/types/Component/Tag/Tag.d.ts.map +1 -1
  88. package/dist/types/Form/Autocomplete/Autocomplete.d.ts +2 -2
  89. package/dist/types/Form/Autocomplete/Autocomplete.d.ts.map +1 -1
  90. package/dist/types/Form/Autocomplete/Elements/Autocomplete.d.ts +10 -0
  91. package/dist/types/Form/Autocomplete/Elements/Autocomplete.d.ts.map +1 -1
  92. package/dist/types/Form/Checkbox/FormCheckbox.d.ts.map +1 -1
  93. package/dist/types/Form/CheckboxCard/CheckboxCard.d.ts +23 -0
  94. package/dist/types/Form/CheckboxCard/CheckboxCard.d.ts.map +1 -0
  95. package/dist/types/Form/OneTimeCode/OneTimeCode.d.ts +12 -0
  96. package/dist/types/Form/OneTimeCode/OneTimeCode.d.ts.map +1 -0
  97. package/dist/types/Form/Radio/FormRadio.d.ts.map +1 -1
  98. package/dist/types/Form/RadioCard/RadioCard.d.ts +16 -0
  99. package/dist/types/Form/RadioCard/RadioCard.d.ts.map +1 -0
  100. package/dist/types/Form/Select/FormSelect.d.ts.map +1 -1
  101. package/dist/types/Form/TextInput/FormText.d.ts.map +1 -1
  102. package/dist/types/Form/Textarea/FormTextarea.d.ts.map +1 -1
  103. package/dist/types/Utility/Hooks/useIsomorphicLayoutEffect.d.ts +4 -0
  104. package/dist/types/Utility/Hooks/useIsomorphicLayoutEffect.d.ts.map +1 -0
  105. package/dist/types/enums.d.ts +19 -0
  106. package/dist/types/enums.d.ts.map +1 -1
  107. package/dist/types/react.d.ts +5 -0
  108. package/dist/types/react.d.ts.map +1 -1
  109. package/package.json +8 -8
  110. package/src/Atom/Background/_background.css +2 -2
  111. package/src/Atom/Blockquote/_blockquote.css +9 -2
  112. package/src/Atom/Button/README.md +1 -0
  113. package/src/Atom/Button/__snapshots__/Button.stories.ts.snap +2 -3
  114. package/src/Atom/Button/_buttons-styles.css +6 -6
  115. package/src/Atom/Button/_buttons.css +21 -3
  116. package/src/Atom/Button/button.twig +4 -1
  117. package/src/Atom/DefinitionList/DefinitionList.stories.ts +42 -2
  118. package/src/Atom/DefinitionList/DefinitionList.stories.tsx +36 -0
  119. package/src/Atom/DefinitionList/DefinitionList.tsx +4 -1
  120. package/src/Atom/DefinitionList/DefinitionListItem.stories.ts +1 -1
  121. package/src/Atom/DefinitionList/README.md +43 -0
  122. package/src/Atom/DefinitionList/__snapshots__/DefinitionList.stories.ts.snap +37 -3
  123. package/src/Atom/DefinitionList/__snapshots__/DefinitionList.stories.tsx.snap +2 -0
  124. package/src/Atom/DefinitionList/__snapshots__/DefinitionListItem.stories.ts.snap +3 -3
  125. package/src/Atom/DefinitionList/definition-list-item.twig +1 -0
  126. package/src/Atom/DefinitionList/definition-list.twig +6 -1
  127. package/src/Atom/Heading/Heading.tsx +6 -0
  128. package/src/Atom/Heading/_headings.css +2 -1
  129. package/src/Atom/Icon/_extended-set.css +30 -30
  130. package/src/Atom/Icon/_icon.css +39 -36
  131. package/src/Atom/Image/README.md +2 -2
  132. package/src/Atom/Image/_image.css +12 -6
  133. package/src/Atom/Link/Link.tsx +16 -3
  134. package/src/Atom/Link/__snapshots__/Link.stories.ts.snap +7 -12
  135. package/src/Atom/Link/__snapshots__/Link.stories.tsx.snap +1 -1
  136. package/src/Atom/Link/_links.css +9 -0
  137. package/src/Atom/Link/link.twig +8 -2
  138. package/src/Atom/LinkedImage/linked-image.twig +1 -2
  139. package/src/Atom/List/List.stories.ts +102 -0
  140. package/src/Atom/List/List.stories.tsx +103 -0
  141. package/src/Atom/List/List.tsx +22 -0
  142. package/src/Atom/List/README.md +150 -0
  143. package/src/Atom/List/__snapshots__/List.stories.ts.snap +43 -0
  144. package/src/Atom/List/__snapshots__/List.stories.tsx.snap +13 -0
  145. package/src/Atom/List/_list.css +123 -0
  146. package/src/Atom/List/list.twig +5 -0
  147. package/src/Atom/Media/Media.stories.ts +1 -1
  148. package/src/Atom/Media/__snapshots__/Media.stories.ts.snap +2 -2
  149. package/src/Atom/Spacing/_spacing.css +24 -24
  150. package/src/Atom/Table/__snapshots__/Table.stories.ts.snap +24 -4
  151. package/src/Atom/Table/_table.css +15 -3
  152. package/src/Atom/Table/table.twig +6 -1
  153. package/src/Atom/Text/__snapshots__/Text.stories.ts.snap +3 -2
  154. package/src/Atom/Text/_text-styles.css +21 -1
  155. package/src/Atom/Text/twig/text-styles-example.twig +3 -2
  156. package/src/Atom/_base.css +6 -0
  157. package/src/Atom/_flow.css +1 -1
  158. package/src/Atom/_generic.css +18 -11
  159. package/src/Component/Accordion/Accordion.stories.ts +31 -0
  160. package/src/Component/Accordion/Accordion.tsx +3 -2
  161. package/src/Component/Accordion/AccordionItem.stories.ts +27 -0
  162. package/src/Component/Accordion/__snapshots__/Accordion.stories.ts.snap +36 -0
  163. package/src/Component/Accordion/__snapshots__/AccordionItem.stories.ts.snap +16 -0
  164. package/src/Component/Accordion/accordion.css +2 -0
  165. package/src/Component/Breadcrumb/__snapshots__/Breadcrumb.stories.ts.snap +3 -6
  166. package/src/Component/Breadcrumb/breadcrumb.css +4 -0
  167. package/src/Component/Card/__snapshots__/Card.stories.ts.snap +13 -24
  168. package/src/Component/Card/card.css +2 -2
  169. package/src/Component/Card/card.twig +1 -1
  170. package/src/Component/Carousel/Carousel.stories.ts +101 -23
  171. package/src/Component/Carousel/Carousel.stories.tsx +74 -0
  172. package/src/Component/Carousel/__snapshots__/Carousel.stories.ts.snap +273 -88
  173. package/src/Component/Carousel/__snapshots__/Carousel.stories.tsx.snap +5 -3
  174. package/src/Component/ContentBlock/__snapshots__/ContentBlock.stories.ts.snap +12 -24
  175. package/src/Component/CookieConsent/cookie-consent.css +3 -1
  176. package/src/Component/Dialog/Dialog.stories.ts +33 -0
  177. package/src/Component/Dialog/Dialog.stories.tsx +25 -0
  178. package/src/Component/Dialog/Dialog.tsx +2 -2
  179. package/src/Component/Dialog/__snapshots__/Dialog.stories.ts.snap +33 -2
  180. package/src/Component/Dialog/__snapshots__/Dialog.stories.tsx.snap +2 -0
  181. package/src/Component/DropMenu/DropMenu.stories.ts +39 -1
  182. package/src/Component/DropMenu/DropMenu.stories.tsx +29 -0
  183. package/src/Component/DropMenu/DropMenu.tsx +1 -1
  184. package/src/Component/DropMenu/Elements/DropMenu.ts +10 -1
  185. package/src/Component/DropMenu/__snapshots__/DropMenu.stories.ts.snap +14 -0
  186. package/src/Component/DropMenu/__snapshots__/DropMenu.stories.tsx.snap +3 -1
  187. package/src/Component/Filters/Elements/Filters.ts +71 -1
  188. package/src/Component/Filters/FilterItem.stories.ts +2 -1
  189. package/src/Component/Filters/FilterItem.tsx +1 -1
  190. package/src/Component/Filters/Filters.figma.ts +1 -1
  191. package/src/Component/Filters/Filters.stories.ts +95 -3
  192. package/src/Component/Filters/Filters.stories.tsx +246 -17
  193. package/src/Component/Filters/Filters.tsx +145 -29
  194. package/src/Component/Filters/README.md +102 -37
  195. package/src/Component/Filters/__snapshots__/FilterItem.stories.tsx.snap +3 -3
  196. package/src/Component/Filters/__snapshots__/Filters.stories.ts.snap +82 -4
  197. package/src/Component/Filters/__snapshots__/Filters.stories.tsx.snap +9 -3
  198. package/src/Component/Filters/filters.css +11 -0
  199. package/src/Component/Gallery/Gallery.tsx +2 -2
  200. package/src/Component/HeroBanner/HeroBanner.stories.ts +1 -1
  201. package/src/Component/HeroBanner/__snapshots__/HeroBanner.stories.ts.snap +4 -8
  202. package/src/Component/HeroSearch/__snapshots__/HeroSearch.stories.ts.snap +5 -10
  203. package/src/Component/InPageAlert/__snapshots__/InPageAlert.stories.ts.snap +6 -12
  204. package/src/Component/InPageAlert/in-page-alert.css +2 -1
  205. package/src/Component/InPageNavigation/Elements/InPageNavigation.ts +10 -5
  206. package/src/Component/InPageNavigation/Hooks/useInPageNavigation.ts +10 -5
  207. package/src/Component/InPageNavigation/InPageNavigation.stories.ts +83 -0
  208. package/src/Component/InPageNavigation/InPageNavigation.stories.tsx +130 -0
  209. package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.ts.snap +61 -1
  210. package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.tsx.snap +4 -0
  211. package/src/Component/InPageNavigation/twig/content-example-duplicate-headings.twig +20 -0
  212. package/src/Component/InPageNavigation/twig/in-page-navigation-story-duplicate-headings.twig +2 -0
  213. package/src/Component/LinkList/__snapshots__/LinkList.stories.ts.snap +6 -12
  214. package/src/Component/ListItem/__snapshots__/ListItem.stories.ts.snap +11 -18
  215. package/src/Component/ListItem/list-item.css +1 -1
  216. package/src/Component/ListItem/list-item.twig +2 -2
  217. package/src/Component/Navigation/Dropdown.stories.tsx +46 -0
  218. package/src/Component/Navigation/Navigation.stories.ts +56 -0
  219. package/src/Component/Navigation/__snapshots__/Dropdown.stories.tsx.snap +2 -0
  220. package/src/Component/Navigation/__snapshots__/Navigation.stories.ts.snap +57 -0
  221. package/src/Component/Navigation/_navigation.css +1 -1
  222. package/src/Component/Pagination/Components/PaginationContext.tsx +2 -1
  223. package/src/Component/Pagination/Components/PaginationItem.tsx +1 -1
  224. package/src/Component/Pagination/Pagination.stories.ts +44 -4
  225. package/src/Component/Pagination/Pagination.stories.tsx +31 -0
  226. package/src/Component/Pagination/__snapshots__/Pagination.stories.ts.snap +69 -12
  227. package/src/Component/Pagination/__snapshots__/Pagination.stories.tsx.snap +3 -1
  228. package/src/Component/Pagination/pagination-item.twig +1 -1
  229. package/src/Component/Pagination/pagination.css +2 -2
  230. package/src/Component/Popover/__snapshots__/Popover.stories.ts.snap +13 -26
  231. package/src/Component/Popover/popover.css +1 -1
  232. package/src/Component/ProgressIndicator/ProgressIndicator.stories.ts +36 -0
  233. package/src/Component/ProgressIndicator/ProgressIndicator.stories.tsx +30 -0
  234. package/src/Component/ProgressIndicator/ProgressIndicator.tsx +60 -0
  235. package/src/Component/ProgressIndicator/README.md +111 -0
  236. package/src/Component/ProgressIndicator/__snapshots__/ProgressIndicator.stories.ts.snap +67 -0
  237. package/src/Component/ProgressIndicator/__snapshots__/ProgressIndicator.stories.tsx.snap +7 -0
  238. package/src/Component/ProgressIndicator/progress-indicator.css +59 -0
  239. package/src/Component/ProgressIndicator/progress-indicator.twig +22 -0
  240. package/src/Component/SideNavigation/__snapshots__/SideNavigation.stories.ts.snap +1 -2
  241. package/src/Component/SocialLinks/__snapshots__/SocialLinks.stories.ts.snap +6 -9
  242. package/src/Component/Steps/Steps.stories.ts +1 -1
  243. package/src/Component/Steps/steps.css +13 -0
  244. package/src/Component/Sticky/Elements/Sticky.ts +1 -1
  245. package/src/Component/Sticky/sticky.css +3 -3
  246. package/src/Component/Tabs/Elements/Tabs.ts +3 -1
  247. package/src/Component/Tabs/Tabs.stories.ts +77 -21
  248. package/src/Component/Tabs/Tabs.stories.tsx +24 -0
  249. package/src/Component/Tabs/Tabs.tsx +2 -2
  250. package/src/Component/Tabs/__snapshots__/Tabs.stories.ts.snap +20 -2
  251. package/src/Component/Tabs/__snapshots__/Tabs.stories.tsx.snap +3 -1
  252. package/src/Component/Tabs/tabs.css +2 -0
  253. package/src/Component/Tag/README.md +52 -26
  254. package/src/Component/Tag/Tag.stories.tsx +24 -0
  255. package/src/Component/Tag/Tag.tsx +12 -2
  256. package/src/Component/Tag/TagItem.stories.ts +25 -0
  257. package/src/Component/Tag/__snapshots__/Tag.stories.ts.snap +2 -2
  258. package/src/Component/Tag/__snapshots__/Tag.stories.tsx.snap +2 -0
  259. package/src/Component/Tag/tag-item.twig +2 -2
  260. package/src/Component/Tag/tag.css +76 -2
  261. package/src/Form/Autocomplete/Autocomplete.stories.ts +89 -7
  262. package/src/Form/Autocomplete/Autocomplete.stories.tsx +85 -1
  263. package/src/Form/Autocomplete/Autocomplete.tsx +20 -10
  264. package/src/Form/Autocomplete/Elements/Autocomplete.ts +24 -1
  265. package/src/Form/Autocomplete/README.md +26 -26
  266. package/src/Form/Autocomplete/__snapshots__/Autocomplete.stories.ts.snap +39 -5
  267. package/src/Form/Autocomplete/__snapshots__/Autocomplete.stories.tsx.snap +8 -4
  268. package/src/Form/Autocomplete/autocomplete.css +12 -1
  269. package/src/Form/Autocomplete/autocomplete.twig +1 -3
  270. package/src/Form/Checkbox/FormCheckbox.tsx +2 -8
  271. package/src/Form/Checkbox/README.md +23 -18
  272. package/src/Form/Checkbox/input-checkbox.twig +0 -2
  273. package/src/Form/CheckboxCard/CheckboxCard.stories.ts +90 -0
  274. package/src/Form/CheckboxCard/CheckboxCard.stories.tsx +43 -0
  275. package/src/Form/CheckboxCard/CheckboxCard.tsx +102 -0
  276. package/src/Form/CheckboxCard/README.md +163 -0
  277. package/src/Form/CheckboxCard/__snapshots__/CheckboxCard.stories.ts.snap +63 -0
  278. package/src/Form/CheckboxCard/__snapshots__/CheckboxCard.stories.tsx.snap +7 -0
  279. package/src/Form/CheckboxCard/checkbox-card.twig +29 -0
  280. package/src/Form/FormItem/__snapshots__/FormItem.stories.ts.snap +2 -2
  281. package/src/Form/OneTimeCode/OneTimeCode.stories.ts +20 -0
  282. package/src/Form/OneTimeCode/OneTimeCode.stories.tsx +18 -0
  283. package/src/Form/OneTimeCode/OneTimeCode.tsx +56 -0
  284. package/src/Form/OneTimeCode/README.md +83 -0
  285. package/src/Form/OneTimeCode/__snapshots__/OneTimeCode.stories.ts.snap +7 -0
  286. package/src/Form/OneTimeCode/__snapshots__/OneTimeCode.stories.tsx.snap +3 -0
  287. package/src/Form/OneTimeCode/one-time-code.css +13 -0
  288. package/src/Form/OneTimeCode/one-time-code.twig +31 -0
  289. package/src/Form/README.md +65 -13
  290. package/src/Form/Radio/FormRadio.tsx +1 -4
  291. package/src/Form/Radio/input-radio.twig +0 -2
  292. package/src/Form/RadioCard/README.md +146 -0
  293. package/src/Form/RadioCard/RadioCard.stories.ts +95 -0
  294. package/src/Form/RadioCard/RadioCard.stories.tsx +26 -0
  295. package/src/Form/RadioCard/RadioCard.tsx +59 -0
  296. package/src/Form/RadioCard/__snapshots__/RadioCard.stories.ts.snap +69 -0
  297. package/src/Form/RadioCard/__snapshots__/RadioCard.stories.tsx.snap +3 -0
  298. package/src/Form/RadioCard/radio-card.twig +20 -0
  299. package/src/Form/Select/FormSelect.tsx +1 -4
  300. package/src/Form/Select/input-select.twig +0 -2
  301. package/src/Form/TextInput/FormText.stories.tsx +14 -0
  302. package/src/Form/TextInput/FormText.tsx +1 -4
  303. package/src/Form/TextInput/__snapshots__/FormText.stories.tsx.snap +2 -0
  304. package/src/Form/TextInput/input-text.twig +0 -2
  305. package/src/Form/Textarea/FormTextarea.tsx +1 -4
  306. package/src/Form/Textarea/input-textarea.twig +0 -2
  307. package/src/Form/Validation.stories.ts +153 -0
  308. package/src/Form/__snapshots__/Validation.stories.ts.snap +130 -0
  309. package/src/Form/form.css +240 -55
  310. package/src/Layout/Footer/__snapshots__/Footer.stories.ts.snap +18 -33
  311. package/src/Layout/Grid/grid.css +4 -4
  312. package/src/Layout/Header/Header.stories.ts +33 -0
  313. package/src/Layout/Header/Header.stories.tsx +31 -0
  314. package/src/Layout/Header/__snapshots__/Header.stories.ts.snap +50 -0
  315. package/src/Layout/Header/__snapshots__/Header.stories.tsx.snap +2 -0
  316. package/src/Layout/Masthead/__snapshots__/Masthead.stories.ts.snap +8 -16
  317. package/src/Layout/Section/__snapshots__/Background.stories.ts.snap +2 -4
  318. package/src/Layout/Section/__snapshots__/Section.stories.ts.snap +4 -8
  319. package/src/Layout/Sidebar/__snapshots__/Sidebar.stories.ts.snap +5 -5
  320. package/src/Layout/Sidebar/sidebar.css +1 -0
  321. package/src/Layout/Sidebar/sidebar.twig +1 -1
  322. package/src/Utility/Hooks/useIsomorphicLayoutEffect.ts +8 -0
  323. package/src/Utility/_layout-utils.css +8 -0
  324. package/src/Utility/_mixins.css +24 -0
  325. package/src/Utility/print.css +51 -29
  326. package/src/Utility/utilities.css +22 -2
  327. package/src/_custom-media.css +1 -0
  328. package/src/constants.css +12 -2
  329. package/src/enums.ts +22 -0
  330. package/src/react.ts +5 -0
  331. package/tools/lib/extract-classnames.mjs +0 -0
  332. package/tools/lint-classnames.mjs +1 -1
  333. package/tools/test-utils/screen-reader.ts +33 -0
  334. package/dist/build/chunks/DropMenu-Co9jZZ4S.js +0 -1
  335. package/dist/build/chunks/Gallery-BT3cxS5m.js +0 -1
  336. package/dist/build/chunks/GlobalToggle-DQHho97V.js +0 -1
  337. package/dist/build/chunks/InPageNavigation-CzR_KZaz.js +0 -1
  338. package/dist/build/chunks/Navigation-B9Wug-ok.js +0 -1
  339. package/dist/build/chunks/Popover-De_CQqcd.js +0 -1
  340. package/dist/build/chunks/Sticky-Kyjykb36.js +0 -1
  341. package/dist/build/chunks/Tabs-Dm8g301e.js +0 -15
  342. package/dist/build/chunks/disclosure-widget-BETzOHLO.js +0 -1
  343. package/dist/build/chunks/polyfills-Dokbkxxk.js +0 -1
  344. package/dist/build/chunks/utilities-C5dAO2YA.js +0 -1
@@ -1,7 +1,13 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
- exports[`Collapsible 1`] = `"<div class="mx-filters mx-filters--down"><details class="mx-accordion" open=""><summary class="mx-accordion__toggle mx-filters__title">Filters<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></summary><form class="mx-filters__form mx-vertical-flow" action="#"><div class="mx-accordion mx-filter-item" data-open="true"><button type="button" class="mx-accordion__toggle" id="filter-keywords-container" aria-expanded="true">Keywords<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></button><section aria-labelledby="filter-keywords-container" class="mx-accordion__content mx-rich-text mx-vertical-flow"><div class="mx-form-item mx-accordion__content" id="filter-keywords"><div id="help-filter-keywords" class="mx-description">Search by keywords</div><input class="mx-input__text" id="filter-keywords" aria-describedby="help-filter-keywords" aria-invalid="false" aria-label="Keywords" type="text" value="" name="keywords"></div></section></div><div class="mx-accordion mx-filter-item" data-open="true"><button type="button" class="mx-accordion__toggle" id="filter-categories-container" aria-expanded="true">Categories<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></button><section aria-labelledby="filter-categories-container" class="mx-accordion__content mx-rich-text mx-vertical-flow"><fieldset class="mx-form-item mx-fieldset mx-accordion__content" id="filter-categories"><div class="mx-checkbox"><input id="_r_b_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="news" name="categories"><label for="_r_b_">News</label></div><div class="mx-checkbox"><input id="_r_e_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="media-releases" name="categories"><label for="_r_e_">Media Releases</label></div><button class="mx-button mx-button--light" type="button">Show all (3)</button></fieldset></section></div><div class="mx-form-actions"><button type="submit" class="mx-button mx-button--dark">Apply filters</button><button type="reset" class="mx-button mx-button--light">Clear all filters</button></div></form></details></div>"`;
3
+ exports[`Collapsible 1`] = `"<div class="mx-filters mx-filters--down"><details class="mx-accordion" open=""><summary class="mx-accordion__toggle mx-filters__title">Filters<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></summary><form class="mx-filters__form mx-vertical-flow" action="#" aria-busy="false"><div class="mx-accordion mx-filter-item" data-open="true"><button type="button" class="mx-accordion__toggle" id="filter-keywords-container" aria-expanded="true">Keywords<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></button><section aria-labelledby="filter-keywords-container" class="mx-accordion__content mx-rich-text mx-vertical-flow"><div class="mx-form-item mx-accordion__content" id="filter-keywords-item"><div id="help-filter-keywords" class="mx-description">Search by keywords</div><input class="mx-input__text" id="filter-keywords" aria-describedby="help-filter-keywords" aria-invalid="false" aria-label="Keywords" type="text" value="" name="keywords"></div></section></div><div class="mx-accordion mx-filter-item" data-open="true"><button type="button" class="mx-accordion__toggle" id="filter-categories-container" aria-expanded="true">Categories<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></button><section aria-labelledby="filter-categories-container" class="mx-accordion__content mx-rich-text mx-vertical-flow"><fieldset class="mx-form-item mx-fieldset mx-accordion__content" id="filter-categories-item"><div class="mx-checkbox"><input id="_r_b_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="news" name="categories"><label for="_r_b_">News</label></div><div class="mx-checkbox"><input id="_r_e_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="media-releases" name="categories"><label for="_r_e_">Media Releases</label></div><button class="mx-button mx-button--light" type="button">Show all (3)</button></fieldset></section></div><div class="mx-form-actions"><button type="submit" class="mx-button mx-button--dark" disabled="">Apply filters</button><button type="reset" class="mx-button mx-button--light" disabled="">Clear all</button></div></form></details></div>"`;
4
4
 
5
- exports[`Default 1`] = `"<div class="mx-filters"><h2 class="mx-filters__title">Filters</h2><form class="mx-filters__form mx-vertical-flow" action="#"><div class="mx-form-item mx-filter-item" id="filter-keywords"><label class="mx-label" for="filter-keywords">Keywords</label><div id="help-filter-keywords" class="mx-description">Search by keywords</div><input class="mx-input__text" id="filter-keywords" aria-describedby="help-filter-keywords" aria-invalid="false" aria-label="Keywords" type="text" value="" name="keywords"></div><fieldset class="mx-form-item mx-fieldset mx-filter-item" id="filter-categories"><legend class="mx-legend">Categories</legend><div class="mx-checkbox"><input id="_r_0_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="news" name="categories"><label for="_r_0_">News</label></div><div class="mx-checkbox"><input id="_r_3_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="media-releases" name="categories"><label for="_r_3_">Media Releases</label></div><div class="mx-checkbox"><input id="_r_6_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="publications" name="categories"><label for="_r_6_">Publications</label></div></fieldset><div class="mx-form-actions"><button type="submit" class="mx-button mx-button--dark">Apply filters</button><button type="reset" class="mx-button mx-button--light">Clear all filters</button></div></form></div>"`;
5
+ exports[`Collapsible Right 1`] = `"<div class="mx-filters mx-filters--right"><details class="mx-accordion" open=""><summary class="mx-accordion__toggle mx-filters__title">Filters<span class="mx-icon mx-icon--chevron-right" aria-hidden="true"></span></summary><form class="mx-filters__form mx-vertical-flow" action="#" aria-busy="false"><span data-focus-scope-start="true" hidden=""></span><div class="mx-filters__title">Filters<button class="mx-button mx-button--icon-only mx-icon mx-icon--close" type="button">Close filters</button></div><div class="mx-accordion mx-filter-item" data-open="true"><button type="button" class="mx-accordion__toggle" id="filter-keywords-container" aria-expanded="true">Keywords<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></button><section aria-labelledby="filter-keywords-container" class="mx-accordion__content mx-rich-text mx-vertical-flow"><div class="mx-form-item mx-accordion__content" id="filter-keywords-item"><div id="help-filter-keywords" class="mx-description">Search by keywords</div><input class="mx-input__text" id="filter-keywords" aria-describedby="help-filter-keywords" aria-invalid="false" aria-label="Keywords" type="text" value="" name="keywords"></div></section></div><div class="mx-accordion mx-filter-item" data-open="true"><button type="button" class="mx-accordion__toggle" id="filter-categories-container" aria-expanded="true">Categories<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></button><section aria-labelledby="filter-categories-container" class="mx-accordion__content mx-rich-text mx-vertical-flow"><fieldset class="mx-form-item mx-fieldset mx-accordion__content" id="filter-categories-item"><div class="mx-checkbox"><input id="_r_j_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="news" name="categories"><label for="_r_j_">News</label></div><div class="mx-checkbox"><input id="_r_m_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="media-releases" name="categories"><label for="_r_m_">Media Releases</label></div><button class="mx-button mx-button--light" type="button">Show all (3)</button></fieldset></section></div><div class="mx-form-actions"><button type="submit" class="mx-button mx-button--dark" disabled="">Apply filters</button><button type="reset" class="mx-button mx-button--light" disabled="">Clear all</button></div><span data-focus-scope-end="true" hidden=""></span></form></details></div>"`;
6
6
 
7
- exports[`With Counter 1`] = `"<div class="mx-filters mx-filters--down"><details class="mx-accordion" open=""><summary class="mx-accordion__toggle mx-filters__title">Filters<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></summary><form class="mx-filters__form mx-vertical-flow" action="#"><div class="mx-accordion mx-filter-item" data-open="true"><button type="button" class="mx-accordion__toggle" id="filter-keywords-container" aria-expanded="true">Keywords<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></button><section aria-labelledby="filter-keywords-container" class="mx-accordion__content mx-rich-text mx-vertical-flow"><div class="mx-form-item mx-accordion__content" id="filter-keywords"><div id="help-filter-keywords" class="mx-description">Search by keywords</div><input class="mx-input__text" id="filter-keywords" aria-describedby="help-filter-keywords" aria-invalid="false" aria-label="Keywords" type="text" value="" name="keywords"></div></section></div><div class="mx-accordion mx-filter-item" data-open="true"><button type="button" class="mx-accordion__toggle" id="filter-categories-container" aria-expanded="true">Categories<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></button><section aria-labelledby="filter-categories-container" class="mx-accordion__content mx-rich-text mx-vertical-flow"><fieldset class="mx-form-item mx-fieldset mx-accordion__content" id="filter-categories"><div class="mx-checkbox"><input id="_r_j_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="news" name="categories"><label for="_r_j_">News</label></div><div class="mx-checkbox"><input id="_r_m_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="media-releases" name="categories"><label for="_r_m_">Media Releases</label></div><button class="mx-button mx-button--light" type="button">Show all (3)</button></fieldset></section></div><div class="mx-form-actions"><button type="submit" class="mx-button mx-button--dark">Apply filters</button><button type="reset" class="mx-button mx-button--light">Clear all filters</button></div></form></details></div>"`;
7
+ exports[`Default 1`] = `"<div class="mx-filters mx-filters--down"><h2 class="mx-filters__title">Filters</h2><form class="mx-filters__form mx-vertical-flow" action="#" aria-busy="false"><div class="mx-form-item mx-filter-item" id="filter-keywords-item"><label class="mx-label" for="filter-keywords">Keywords</label><div id="help-filter-keywords" class="mx-description">Search by keywords</div><input class="mx-input__text" id="filter-keywords" aria-describedby="help-filter-keywords" aria-invalid="false" aria-label="Keywords" type="text" value="" name="keywords"></div><fieldset class="mx-form-item mx-fieldset mx-filter-item" id="filter-categories-item"><legend class="mx-legend">Categories</legend><div class="mx-checkbox"><input id="_r_0_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="news" name="categories"><label for="_r_0_">News</label></div><div class="mx-checkbox"><input id="_r_3_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="media-releases" name="categories"><label for="_r_3_">Media Releases</label></div><div class="mx-checkbox"><input id="_r_6_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="publications" name="categories"><label for="_r_6_">Publications</label></div></fieldset><div class="mx-form-actions"><button type="submit" class="mx-button mx-button--dark" disabled="">Apply filters</button><button type="reset" class="mx-button mx-button--light" disabled="">Clear all</button></div></form></div>"`;
8
+
9
+ exports[`Instant 1`] = `"<div class="mx-filters mx-filters--down mx-filters--auto"><h2 class="mx-filters__title">Filters</h2><form class="mx-filters__form mx-vertical-flow" action="#" aria-busy="false"><div class="mx-form-item mx-filter-item" id="filter-keywords-item"><label class="mx-label" for="filter-keywords">Keywords</label><div id="help-filter-keywords" class="mx-description">Search by keywords</div><input class="mx-input__text" id="filter-keywords" aria-describedby="help-filter-keywords" aria-invalid="false" aria-label="Keywords" type="text" value="" name="keywords"></div><fieldset class="mx-form-item mx-fieldset mx-filter-item" id="filter-categories-item"><legend class="mx-legend">Categories</legend><div class="mx-checkbox"><input id="_r_11_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="news" name="categories"><label for="_r_11_">News</label></div><div class="mx-checkbox"><input id="_r_14_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="media-releases" name="categories"><label for="_r_14_">Media Releases</label></div><div class="mx-checkbox"><input id="_r_17_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="publications" name="categories"><label for="_r_17_">Publications</label></div></fieldset><div class="mx-form-actions"><button type="reset" class="mx-button mx-button--light" disabled="">Clear all</button></div></form></div>"`;
10
+
11
+ exports[`Pending 1`] = `"<div class="mx-filters mx-filters--down"><h2 class="mx-filters__title">Filters</h2><form class="mx-filters__form mx-vertical-flow" action="#" aria-busy="false"><div class="mx-form-item mx-filter-item" id="filter-keywords-item"><label class="mx-label" for="filter-keywords">Keywords</label><div id="help-filter-keywords" class="mx-description">Search by keywords</div><input class="mx-input__text" id="filter-keywords" aria-describedby="help-filter-keywords" aria-invalid="false" aria-label="Keywords" type="text" value="" name="keywords"></div><fieldset class="mx-form-item mx-fieldset mx-filter-item" id="filter-categories-item"><legend class="mx-legend">Categories</legend><div class="mx-checkbox"><input id="_r_1a_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="news" name="categories"><label for="_r_1a_">News</label></div><div class="mx-checkbox"><input id="_r_1d_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="media-releases" name="categories"><label for="_r_1d_">Media Releases</label></div><div class="mx-checkbox"><input id="_r_1g_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="publications" name="categories"><label for="_r_1g_">Publications</label></div></fieldset><div class="mx-form-actions"><button type="submit" class="mx-button mx-button--dark">Apply filters</button><button type="reset" class="mx-button mx-button--light">Clear all</button></div></form></div>"`;
12
+
13
+ exports[`With Counter 1`] = `"<div class="mx-filters mx-filters--down"><h2 class="mx-filters__title">Filters</h2><form class="mx-filters__form mx-vertical-flow" action="#" aria-busy="false"><div class="mx-accordion mx-filter-item" data-open="true"><button type="button" class="mx-accordion__toggle" id="filter-keywords-container" aria-expanded="true">Keywords<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></button><section aria-labelledby="filter-keywords-container" class="mx-accordion__content mx-rich-text mx-vertical-flow"><div class="mx-form-item mx-accordion__content" id="filter-keywords-item"><div id="help-filter-keywords" class="mx-description">Search by keywords</div><input class="mx-input__text" id="filter-keywords" aria-describedby="help-filter-keywords" aria-invalid="false" aria-label="Keywords" type="text" value="" name="keywords"></div></section></div><div class="mx-accordion mx-filter-item" data-open="true"><button type="button" class="mx-accordion__toggle" id="filter-categories-container" aria-expanded="true">Categories<span class="mx-icon mx-icon--chevron-down" aria-hidden="true"></span></button><section aria-labelledby="filter-categories-container" class="mx-accordion__content mx-rich-text mx-vertical-flow"><fieldset class="mx-form-item mx-fieldset mx-accordion__content" id="filter-categories-item"><div class="mx-checkbox"><input id="_r_r_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="news" name="categories"><label for="_r_r_">News</label></div><div class="mx-checkbox"><input id="_r_u_" data-react-aria-pressable="true" tabindex="0" type="checkbox" value="media-releases" name="categories"><label for="_r_u_">Media Releases</label></div><button class="mx-button mx-button--light" type="button">Show all (3)</button></fieldset></section></div><div class="mx-form-actions"><button type="submit" class="mx-button mx-button--dark" disabled="">Apply filters</button><button type="reset" class="mx-button mx-button--light" disabled="">Clear all</button></div></form></div>"`;
@@ -73,6 +73,7 @@
73
73
  display: flex;
74
74
  gap: var(--spacing-s);
75
75
  align-items: center;
76
+ min-block-size: var(--spacing-m);
76
77
 
77
78
  & .mx-icon {
78
79
  margin-inline-start: auto;
@@ -82,6 +83,16 @@
82
83
  & :is(.mx-input__text, .mx-input__select, .mx-input__textarea) {
83
84
  inline-size: 100%;
84
85
  }
86
+
87
+ &[aria-busy="true"] {
88
+ position: relative;
89
+
90
+ &::after {
91
+ position: absolute;
92
+ inset-block-end: var(--spinner-size, var(--spacing-xs));
93
+ inset-inline-end: 0;
94
+ }
95
+ }
85
96
  }
86
97
 
87
98
  .mx-filter-item .mx-accordion__content {
@@ -6,13 +6,13 @@ import {
6
6
  createContext,
7
7
  use,
8
8
  useCallback,
9
- useEffect,
10
9
  useId,
11
10
  useMemo,
12
11
  useRef,
13
12
  useState,
14
13
  } from "react"
15
14
  import classNames from "classnames"
15
+ import useIsomorphicLayoutEffect from "../../Utility/Hooks/useIsomorphicLayoutEffect"
16
16
  import Dialog from "../Dialog/Dialog"
17
17
  import DialogContent from "../Dialog/Components/DialogContent"
18
18
  import DialogCloseButton from "../Dialog/Components/DialogCloseButton"
@@ -55,7 +55,7 @@ const Gallery = ({
55
55
  // Runs after Dialog's own effect has already called showModal() (child
56
56
  // effects flush before the parent's), so the carousel is laid out and
57
57
  // scrollIntoView measures correctly.
58
- useEffect(() => {
58
+ useIsomorphicLayoutEffect(() => {
59
59
  if (!open) return
60
60
  const slide = findActiveSlide(contentRef.current, activeIndex)
61
61
  if (!slide) return
@@ -39,7 +39,7 @@ const meta: Meta<HeroBannerType> = {
39
39
  },
40
40
  argTypes: {
41
41
  modifiers: {
42
- description: "Controls the background colour",
42
+ description: "Controls the background colour (only one applies)",
43
43
  options: Object.values(HeroBannerModifier),
44
44
  control: "check",
45
45
  table: {
@@ -36,8 +36,7 @@ exports[`Highlight 1`] = `
36
36
 
37
37
  <a href="#" class="mx-link--more">
38
38
 
39
- <span>Find out more</span>
40
-
39
+ Find out more
41
40
  </a>
42
41
 
43
42
  </div>
@@ -73,24 +72,21 @@ exports[`With Link List 1`] = `
73
72
 
74
73
  <li><a href="#">
75
74
 
76
- <span>Link one</span>
77
-
75
+ Link one
78
76
  <span class="mx-icon mx-icon--chevron-right"></span>
79
77
 
80
78
  </a>
81
79
  </li>
82
80
  <li><a href="#">
83
81
 
84
- <span>Link two</span>
85
-
82
+ Link two
86
83
  <span class="mx-icon mx-icon--chevron-right"></span>
87
84
 
88
85
  </a>
89
86
  </li>
90
87
  <li><a href="#">
91
88
 
92
- <span>Link three</span>
93
-
89
+ Link three
94
90
  <span class="mx-icon mx-icon--chevron-right"></span>
95
91
 
96
92
  </a>
@@ -82,40 +82,35 @@ exports[`With Link List 1`] = `
82
82
 
83
83
  <li><a href="#">
84
84
 
85
- <span>Link one</span>
86
-
85
+ Link one
87
86
  <span class="mx-icon mx-icon--chevron-right"></span>
88
87
 
89
88
  </a>
90
89
  </li>
91
90
  <li><a href="#">
92
91
 
93
- <span>Link two</span>
94
-
92
+ Link two
95
93
  <span class="mx-icon mx-icon--chevron-right"></span>
96
94
 
97
95
  </a>
98
96
  </li>
99
97
  <li><a href="#">
100
98
 
101
- <span>Link three</span>
102
-
99
+ Link three
103
100
  <span class="mx-icon mx-icon--chevron-right"></span>
104
101
 
105
102
  </a>
106
103
  </li>
107
104
  <li><a href="#">
108
105
 
109
- <span>Link four</span>
110
-
106
+ Link four
111
107
  <span class="mx-icon mx-icon--chevron-right"></span>
112
108
 
113
109
  </a>
114
110
  </li>
115
111
  <li><a href="#">
116
112
 
117
- <span>Link five</span>
118
-
113
+ Link five
119
114
  <span class="mx-icon mx-icon--chevron-right"></span>
120
115
 
121
116
  </a>
@@ -9,8 +9,7 @@ exports[`Error 1`] = `
9
9
 
10
10
  <a href="#">
11
11
 
12
- <span>Click here</span>
13
-
12
+ Click here
14
13
  </a>
15
14
 
16
15
  </div>
@@ -27,8 +26,7 @@ exports[`In Page Alert 1`] = `
27
26
 
28
27
  <a href="#">
29
28
 
30
- <span>Click here</span>
31
-
29
+ Click here
32
30
  </a>
33
31
 
34
32
  </div>
@@ -45,8 +43,7 @@ exports[`Info 1`] = `
45
43
 
46
44
  <a href="#">
47
45
 
48
- <span>Click here</span>
49
-
46
+ Click here
50
47
  </a>
51
48
 
52
49
  </div>
@@ -63,8 +60,7 @@ exports[`Success 1`] = `
63
60
 
64
61
  <a href="#">
65
62
 
66
- <span>Click here</span>
67
-
63
+ Click here
68
64
  </a>
69
65
 
70
66
  </div>
@@ -81,8 +77,7 @@ exports[`Warning 1`] = `
81
77
 
82
78
  <a href="#">
83
79
 
84
- <span>Click here</span>
85
-
80
+ Click here
86
81
  </a>
87
82
 
88
83
  </div>
@@ -101,8 +96,7 @@ exports[`With Heading 1`] = `
101
96
 
102
97
  <a href="#">
103
98
 
104
- <span>Click here</span>
105
-
99
+ Click here
106
100
  </a>
107
101
 
108
102
  </div>
@@ -14,11 +14,12 @@
14
14
  gap: var(--gap-s);
15
15
  padding-block: var(--spacing-xxs);
16
16
  padding-inline: var(--spacing-s);
17
- border: 1px solid;
17
+ border: var(--line-width, 1px) solid;
18
18
  background-color: var(--background, var(--colour-background-alt));
19
19
  color: var(--foreground, var(--colour-foreground));
20
20
  color-scheme: var(--alert-scheme, light);
21
21
  border-radius: var(--border-radius);
22
+ align-items: start;
22
23
 
23
24
  &:has(.mx-icon--close) {
24
25
  grid-template-columns: auto min-content;
@@ -32,9 +32,10 @@ export default class InPageNavigation extends HTMLElement {
32
32
  ) as HTMLHeadingElement
33
33
  this.nav.insertBefore(headingElement, this.menu)
34
34
  // Build links if not provided.
35
+ const counts: Record<string, number> = {}
35
36
  this.headings.forEach((heading: HTMLHeadingElement) => {
36
37
  const listItem: HTMLLIElement = this.generateListItem(heading)
37
- const link: HTMLAnchorElement = this.generateAnchor(heading)
38
+ const link: HTMLAnchorElement = this.generateAnchor(heading, counts)
38
39
  this.items.push({ link, heading })
39
40
  listItem.appendChild(link)
40
41
  this.menu?.appendChild(listItem)
@@ -119,16 +120,20 @@ export default class InPageNavigation extends HTMLElement {
119
120
  return this.menu?.querySelectorAll("a")
120
121
  }
121
122
 
122
- generatedId(heading: HTMLHeadingElement): string {
123
+ generatedId(heading: HTMLHeadingElement, counts: Record<string, number>): string {
123
124
  const string: string | undefined = heading?.textContent?.trim()
124
- return !string ? "" : makeAnchor(string)
125
+ if (!string) return ""
126
+ const base: string = makeAnchor(string)
127
+ const index: number = counts[base] ?? 0
128
+ counts[base] = index + 1
129
+ return index > 0 ? `${base}-${index}` : base
125
130
  }
126
131
 
127
- generateAnchor(heading: HTMLHeadingElement): HTMLAnchorElement {
132
+ generateAnchor(heading: HTMLHeadingElement, counts: Record<string, number>): HTMLAnchorElement {
128
133
  const { textContent, id } = heading
129
134
  const linkText: string | null =
130
135
  heading.getAttribute("data-in-page-navigation-title") || textContent
131
- const targetID: string = id || this.generatedId(heading)
136
+ const targetID: string = id || this.generatedId(heading, counts)
132
137
  if (!id) heading.id = targetID
133
138
  const item: Element = createElement(
134
139
  `<a href="#${targetID}"><span class="mx-icon mx-icon--chevron-right"></span><span>${linkText}</span></a>`,
@@ -23,17 +23,22 @@ const useInPageNavigation = ({
23
23
  const context = ref?.current || document.querySelector("section")
24
24
  const headingElements: Element[] = [...context.querySelectorAll(levels)]
25
25
 
26
+ const counts: Record<string, number> = {}
26
27
  const values: HeadingType[] = headingElements.map(
27
- ({ textContent: text, id, localName }: Element) =>
28
- ({
29
- id: id || makeAnchor(text),
28
+ ({ textContent: text, id, localName }: Element) => {
29
+ const base: string = makeAnchor(text)
30
+ const index: number = counts[base] ?? 0
31
+ counts[base] = index + 1
32
+ return {
33
+ id: id || (index > 0 ? `${base}-${index}` : base),
30
34
  text: text,
31
35
  level: localName,
32
- }) as HeadingType,
36
+ } as HeadingType
37
+ },
33
38
  )
34
39
  setHeadings([...new Set(values)])
35
40
  headingElements.forEach((heading: HTMLHeadingElement, index: number): void => {
36
- if (!heading.id) heading.id = values[index].id
41
+ heading.id = values[index].id
37
42
  })
38
43
  }, [ref, levels])
39
44
 
@@ -1,5 +1,7 @@
1
1
  import { Meta, StoryObj } from "@storybook/html-vite"
2
+ import { expect } from "storybook/test"
2
3
  import Component from "./twig/in-page-navigation-story.twig"
4
+ import Default from "./twig/in-page-navigation-story-duplicate-headings.twig"
3
5
  import Other from "./twig/in-page-navigation-selector.twig"
4
6
  import "./in-page-navigation.css"
5
7
  import "./Elements/InPageNavigation"
@@ -10,6 +12,7 @@ import Heading from "../../Atom/Heading/heading.twig"
10
12
  import Link from "../../Atom/Link/link.twig"
11
13
  import Icon from "../../Atom/Icon/icon.twig"
12
14
  import { Icons } from "../../enums"
15
+ import { runScreenReaderWalkthrough } from "../../../tools/test-utils/screen-reader"
13
16
 
14
17
  /**
15
18
  * A table of contents with anchor links to the headings.
@@ -90,6 +93,55 @@ type Story = StoryObj<InPageNavigationType>
90
93
 
91
94
  export const InPageNavigation: Story = {}
92
95
 
96
+ /**
97
+ * Steps a virtual screen reader through the page and asserts on what it announces.
98
+ */
99
+ export const ScreenReaderAnnouncements: Story = {
100
+ play: async () => {
101
+ const log = await runScreenReaderWalkthrough()
102
+ await expect(log).toEqual([
103
+ "document",
104
+ "navigation",
105
+ "heading, On this page, level 2",
106
+ "list",
107
+ "listitem, level 1, position 1, set size 3",
108
+ "link, Section 1",
109
+ "Section 1",
110
+ "end of link, Section 1",
111
+ "end of listitem, level 1, position 1, set size 3",
112
+ "listitem, level 1, position 2, set size 3",
113
+ "link, Section 2",
114
+ "Section 2",
115
+ "end of link, Section 2",
116
+ "end of listitem, level 1, position 2, set size 3",
117
+ "listitem, level 1, position 3, set size 3",
118
+ "link, Section 3",
119
+ "Section 3",
120
+ "end of link, Section 3",
121
+ "end of listitem, level 1, position 3, set size 3",
122
+ "end of list",
123
+ "end of navigation",
124
+ "heading, Section 1, level 2",
125
+ "paragraph",
126
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
127
+ "end of paragraph",
128
+ "heading, Section 2, level 2",
129
+ "paragraph",
130
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
131
+ "end of paragraph",
132
+ "heading, Test level 3, level 3",
133
+ "paragraph",
134
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
135
+ "end of paragraph",
136
+ "heading, Section 3, level 2",
137
+ "paragraph",
138
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
139
+ "end of paragraph",
140
+ "end of document",
141
+ ])
142
+ },
143
+ }
144
+
93
145
  /**
94
146
  * When the `items` are empty, the Javascript builds the list and heading.
95
147
  */
@@ -119,6 +171,37 @@ export const NoHeadings: Story = {
119
171
  },
120
172
  }
121
173
 
174
+ /**
175
+ * Duplicate heading text: the javascript ensures generated ids stay unique.
176
+ */
177
+ export const IdenticalHeadings: Story = {
178
+ args: {
179
+ items: "",
180
+ levels: [HeadingTypes.TWO, HeadingTypes.THREE],
181
+ },
182
+ render: args => Default(args),
183
+ play: async ({ canvasElement, step }) => {
184
+ const links: HTMLAnchorElement[] = [
185
+ ...canvasElement.querySelectorAll<HTMLAnchorElement>(".mx-in-page-navigation a"),
186
+ ]
187
+ const ids: string[] = links.map(({ hash }: HTMLAnchorElement) => hash.slice(1))
188
+
189
+ await step("Repeated heading text is suffixed, the first stays unsuffixed", async () => {
190
+ await expect(ids.filter((id: string) => id.startsWith("identical-heading"))).toEqual([
191
+ "identical-heading",
192
+ "identical-heading-1",
193
+ "identical-heading-2",
194
+ ])
195
+ })
196
+
197
+ await step("Every link targets a heading that exists", async () => {
198
+ const missing: string[] = ids.filter((id: string) => !canvasElement.querySelector(`#${id}`))
199
+ await expect(missing).toEqual([])
200
+ await expect(new Set(ids).size).toBe(ids.length)
201
+ })
202
+ },
203
+ }
204
+
122
205
  /**
123
206
  * Generated items from anywhere in the page: page content contains headings, but not within the container element.
124
207
  */
@@ -5,6 +5,8 @@ import Text from "../../Atom/Text/Text"
5
5
  import Component from "./InPageNavigation"
6
6
  import "./in-page-navigation.css"
7
7
  import { Page } from "../../../.storybook-react/decorators"
8
+ import { expect } from "storybook/test"
9
+ import { runScreenReaderWalkthrough } from "../../../tools/test-utils/screen-reader"
8
10
 
9
11
  const meta: Meta<typeof Component> = {
10
12
  tags: ["autodocs"],
@@ -76,3 +78,131 @@ const meta: Meta<typeof Component> = {
76
78
  }
77
79
  export default meta
78
80
  export const InPageNavigation: StoryObj<typeof Component> = {}
81
+
82
+ export const IdenticalHeadings: StoryObj<typeof Component> = {
83
+ render: () => (
84
+ <div className="mx-vertical-flow">
85
+ <Component />
86
+ <section className="mx-vertical-flow mx-rich-text">
87
+ <Heading>Identical heading</Heading>
88
+ <Text>
89
+ In publishing and graphic design, lorem ipsum is a placeholder text commonly used to
90
+ demonstrate the visual form of a document without relying on meaningful content. Replacing
91
+ the actual content with placeholder text allows designers to design the form of the
92
+ content before the content itself has been produced.
93
+ </Text>
94
+ <Heading>Identical heading</Heading>
95
+ <Text>
96
+ In publishing and graphic design, lorem ipsum is a placeholder text commonly used to
97
+ demonstrate the visual form of a document without relying on meaningful content. Replacing
98
+ the actual content with placeholder text allows designers to design the form of the
99
+ content before the content itself has been produced.
100
+ </Text>
101
+ <Heading>Identical heading</Heading>
102
+ <Text>
103
+ In publishing and graphic design, lorem ipsum is a placeholder text commonly used to
104
+ demonstrate the visual form of a document without relying on meaningful content. Replacing
105
+ the actual content with placeholder text allows designers to design the form of the
106
+ content before the content itself has been produced.
107
+ </Text>
108
+ </section>
109
+ </div>
110
+ ),
111
+ play: async ({ canvasElement, step }) => {
112
+ const links: HTMLAnchorElement[] = [
113
+ ...canvasElement.querySelectorAll<HTMLAnchorElement>(".mx-in-page-navigation a"),
114
+ ]
115
+ const ids: string[] = links.map(({ hash }: HTMLAnchorElement) => hash.slice(1))
116
+
117
+ await step("Repeated heading text is suffixed, the first stays unsuffixed", async () => {
118
+ await expect(ids.filter((id: string) => id.startsWith("identical-heading"))).toEqual([
119
+ "identical-heading",
120
+ "identical-heading-1",
121
+ "identical-heading-2",
122
+ ])
123
+ })
124
+
125
+ await step("Every link targets a heading that exists", async () => {
126
+ const missing: string[] = ids.filter((id: string) => !canvasElement.querySelector(`#${id}`))
127
+ await expect(missing).toEqual([])
128
+ await expect(new Set(ids).size).toBe(ids.length)
129
+ })
130
+ },
131
+ }
132
+
133
+ /**
134
+ * Steps a virtual screen reader through the page and asserts on what it announces.
135
+ *
136
+ * Kept as the last story in this file - see the note on ordering in
137
+ * `docs/Testing.md`'s Automated Screen Reader Testing section.
138
+ */
139
+ export const ScreenReaderAnnouncements: StoryObj<typeof Component> = {
140
+ play: async () => {
141
+ const log = await runScreenReaderWalkthrough()
142
+ await expect(log).toEqual([
143
+ "document",
144
+ "navigation",
145
+ "heading, On this page, level 2",
146
+ "list",
147
+ "listitem, level 1, position 1, set size 8",
148
+ "link, Section 1",
149
+ "end of listitem, level 1, position 1, set size 8",
150
+ "listitem, level 1, position 2, set size 8",
151
+ "link, Section 2",
152
+ "end of listitem, level 1, position 2, set size 8",
153
+ "listitem, level 1, position 3, set size 8",
154
+ "link, Test level 3",
155
+ "end of listitem, level 1, position 3, set size 8",
156
+ "listitem, level 1, position 4, set size 8",
157
+ "link, Section 3",
158
+ "end of listitem, level 1, position 4, set size 8",
159
+ "listitem, level 1, position 5, set size 8",
160
+ "link, Section 4",
161
+ "end of listitem, level 1, position 5, set size 8",
162
+ "listitem, level 1, position 6, set size 8",
163
+ "link, Section 5",
164
+ "end of listitem, level 1, position 6, set size 8",
165
+ "listitem, level 1, position 7, set size 8",
166
+ "link, Test level 6",
167
+ "end of listitem, level 1, position 7, set size 8",
168
+ "listitem, level 1, position 8, set size 8",
169
+ "link, Section 7",
170
+ "end of listitem, level 1, position 8, set size 8",
171
+ "end of list",
172
+ "end of navigation",
173
+ "heading, Section 1, level 2",
174
+ "paragraph",
175
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
176
+ "end of paragraph",
177
+ "heading, Section 2, level 2",
178
+ "paragraph",
179
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
180
+ "end of paragraph",
181
+ "heading, Test level 3, level 3",
182
+ "paragraph",
183
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
184
+ "end of paragraph",
185
+ "heading, Section 3, level 2",
186
+ "paragraph",
187
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
188
+ "end of paragraph",
189
+ "heading, Section 4, level 2",
190
+ "paragraph",
191
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
192
+ "end of paragraph",
193
+ "heading, Section 5, level 2",
194
+ "paragraph",
195
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
196
+ "end of paragraph",
197
+ "heading, Test level 6, level 3",
198
+ "paragraph",
199
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
200
+ "end of paragraph",
201
+ "heading, Section 7, level 2",
202
+ "paragraph",
203
+ "In publishing and graphic design, lorem ipsum is a placeholder text commonly used to demonstrate the visual form of a document without relying on meaningful content. Replacing the actual content with placeholder text allows designers to design the form of the content before the content itself has been produced.",
204
+ "end of paragraph",
205
+ "end of document",
206
+ ])
207
+ },
208
+ }