@pnx-mixtape/mxds 0.1.0-rc.4 → 0.1.0-rc.6

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 (236) hide show
  1. package/.claude/skills/create-mxds-component/SKILL.md +77 -0
  2. package/DESIGN.md +595 -0
  3. package/README.md +2 -3
  4. package/dist/build/accordion.entry.js +2 -1
  5. package/dist/build/autocomplete.entry.js +1 -1
  6. package/dist/build/back-to-top.css +1 -0
  7. package/dist/build/back-to-top.entry.js +1 -0
  8. package/dist/build/base.css +1 -1
  9. package/dist/build/button.css +1 -1
  10. package/dist/build/card.css +1 -1
  11. package/dist/build/chunks/Accordion-Bqe8vTNR.js +44 -0
  12. package/dist/build/chunks/AccordionGroup-BYtoAFxg.js +47 -0
  13. package/dist/build/chunks/AccordionMobile-D517oXYZ.js +26 -0
  14. package/dist/build/chunks/Autocomplete-kRo8RNbC.js +135 -0
  15. package/dist/build/chunks/BackToTop-4M-Wl7z3.js +64 -0
  16. package/dist/build/chunks/ClosableAlert-CZ114JsL.js +55 -0
  17. package/dist/build/chunks/CookieConsent-wd8gQ2Oe.js +18 -0
  18. package/dist/build/chunks/Dialog-DoYcGqhW.js +48 -0
  19. package/dist/build/chunks/DropMenu-CSTZq0Zt.js +68 -0
  20. package/dist/build/chunks/FeedbackForm-BsL7fv8s.js +64 -0
  21. package/dist/build/chunks/Gallery-DqJPujX5.js +165 -0
  22. package/dist/build/chunks/GlobalToggle-7H3F1Wvc.js +82 -0
  23. package/dist/build/chunks/InPageNavigation-D7m3IN89.js +86 -0
  24. package/dist/build/chunks/Navigation-CRModT40.js +142 -0
  25. package/dist/build/chunks/Popover-CiTAftNU.js +30 -0
  26. package/dist/build/chunks/Sticky-C1H8MNqK.js +51 -0
  27. package/dist/build/chunks/Tabs-BJD3kABL.js +160 -0
  28. package/dist/build/chunks/Translate-yAgySwet.js +46 -0
  29. package/dist/build/chunks/cookie-compliance-CG6x-TcV.js +119 -0
  30. package/dist/build/chunks/enums-BrHxPZZ_.js +36 -0
  31. package/dist/build/chunks/polyfills-D-qh1a_2.js +33 -0
  32. package/dist/build/chunks/utilities-B9mNRs7V.js +255 -0
  33. package/dist/build/content-block.css +1 -1
  34. package/dist/build/cookie-consent.entry.js +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 +147 -1
  38. package/dist/build/feedback-form.css +1 -0
  39. package/dist/build/feedback-form.entry.js +1 -0
  40. package/dist/build/filters.entry.js +145 -1
  41. package/dist/build/form.css +1 -1
  42. package/dist/build/gallery.entry.js +1 -1
  43. package/dist/build/global-alert.entry.js +1 -1
  44. package/dist/build/header.css +1 -1
  45. package/dist/build/header.entry.js +1 -1
  46. package/dist/build/icon.css +1 -1
  47. package/dist/build/in-page-navigation.entry.js +1 -1
  48. package/dist/build/navigation.css +1 -1
  49. package/dist/build/navigation.entry.js +1 -1
  50. package/dist/build/pagination.css +1 -1
  51. package/dist/build/popover.entry.js +1 -1
  52. package/dist/build/react.js +2291 -1
  53. package/dist/build/side-navigation.css +1 -1
  54. package/dist/build/sidebar.css +1 -1
  55. package/dist/build/sticky.entry.js +1 -1
  56. package/dist/build/tabs.entry.js +1 -1
  57. package/dist/build/translate.css +1 -0
  58. package/dist/build/translate.entry.js +1 -0
  59. package/dist/build/utility-list.entry.js +53 -1
  60. package/dist/types/Component/BackToTop/BackToTop.d.ts +20 -0
  61. package/dist/types/Component/BackToTop/BackToTop.d.ts.map +1 -0
  62. package/dist/types/Component/BackToTop/Elements/BackToTop.d.ts +27 -0
  63. package/dist/types/Component/BackToTop/Elements/BackToTop.d.ts.map +1 -0
  64. package/dist/types/Component/BackToTop/back-to-top.entry.d.ts +2 -0
  65. package/dist/types/Component/BackToTop/back-to-top.entry.d.ts.map +1 -0
  66. package/dist/types/Component/DropMenu/DropMenu.d.ts +2 -1
  67. package/dist/types/Component/DropMenu/DropMenu.d.ts.map +1 -1
  68. package/dist/types/Component/Navigation/Elements/Navigation.d.ts +2 -3
  69. package/dist/types/Component/Navigation/Elements/Navigation.d.ts.map +1 -1
  70. package/dist/types/Component/SideNavigation/Components/SideNavigationTree.d.ts +3 -3
  71. package/dist/types/Component/SideNavigation/Components/SideNavigationTree.d.ts.map +1 -1
  72. package/dist/types/Component/SideNavigation/SideNavigation.d.ts +3 -1
  73. package/dist/types/Component/SideNavigation/SideNavigation.d.ts.map +1 -1
  74. package/dist/types/Component/Tabs/Components/TabDropMenu.d.ts.map +1 -1
  75. package/dist/types/Component/Tabs/Elements/Tabs.d.ts.map +1 -1
  76. package/dist/types/Component/Translate/Elements/Translate.d.ts +23 -0
  77. package/dist/types/Component/Translate/Elements/Translate.d.ts.map +1 -0
  78. package/dist/types/Component/Translate/Translate.d.ts +37 -0
  79. package/dist/types/Component/Translate/Translate.d.ts.map +1 -0
  80. package/dist/types/Component/Translate/languages.d.ts +11 -0
  81. package/dist/types/Component/Translate/languages.d.ts.map +1 -0
  82. package/dist/types/Component/Translate/translate.entry.d.ts +2 -0
  83. package/dist/types/Component/Translate/translate.entry.d.ts.map +1 -0
  84. package/dist/types/Form/FeedbackForm/Elements/FeedbackForm.d.ts +37 -0
  85. package/dist/types/Form/FeedbackForm/Elements/FeedbackForm.d.ts.map +1 -0
  86. package/dist/types/Form/FeedbackForm/FeedbackForm.d.ts +36 -0
  87. package/dist/types/Form/FeedbackForm/FeedbackForm.d.ts.map +1 -0
  88. package/dist/types/Form/FeedbackForm/feedback-form.entry.d.ts +2 -0
  89. package/dist/types/Form/FeedbackForm/feedback-form.entry.d.ts.map +1 -0
  90. package/dist/types/Utility/Elements/google-translate-widget.d.ts +91 -0
  91. package/dist/types/Utility/Elements/google-translate-widget.d.ts.map +1 -0
  92. package/dist/types/Utility/utilities.d.ts +2 -0
  93. package/dist/types/Utility/utilities.d.ts.map +1 -1
  94. package/dist/types/elements.d.ts +3 -0
  95. package/dist/types/elements.d.ts.map +1 -1
  96. package/dist/types/enums.d.ts +3 -1
  97. package/dist/types/enums.d.ts.map +1 -1
  98. package/dist/types/react.d.ts +3 -0
  99. package/dist/types/react.d.ts.map +1 -1
  100. package/dist/types/tokens.d.ts +1 -0
  101. package/dist/types/tokens.d.ts.map +1 -1
  102. package/docs/API.md +1215 -0
  103. package/docs/CSSGuidelines.md +893 -0
  104. package/docs/CodeQuality.md +940 -0
  105. package/docs/ComponentArchitecture.md +714 -0
  106. package/docs/Contributing.md +626 -0
  107. package/docs/MemoryManagement.md +598 -0
  108. package/docs/Migrations.md +6 -0
  109. package/docs/README.md +100 -0
  110. package/docs/Security.md +444 -0
  111. package/docs/Testing.md +739 -0
  112. package/docs/Troubleshooting.md +674 -0
  113. package/package.json +9 -6
  114. package/src/Atom/Button/Button.tsx +1 -1
  115. package/src/Atom/Button/__snapshots__/Button.stories.tsx.snap +2 -2
  116. package/src/Atom/Button/_buttons.css +1 -0
  117. package/src/Atom/Icon/README.md +1 -1
  118. package/src/Atom/Icon/_icon.css +1 -0
  119. package/src/Atom/Icon/images/thumb.svg +1 -0
  120. package/src/Atom/Link/_links.css +1 -0
  121. package/src/Atom/Media/__snapshots__/Media.stories.ts.snap +4 -9
  122. package/src/Atom/Media/media.twig +4 -5
  123. package/src/Component/BackToTop/BackToTop.stories.ts +127 -0
  124. package/src/Component/BackToTop/BackToTop.stories.tsx +53 -0
  125. package/src/Component/BackToTop/BackToTop.tsx +120 -0
  126. package/src/Component/BackToTop/Elements/BackToTop.ts +119 -0
  127. package/src/Component/BackToTop/README.md +164 -0
  128. package/src/Component/BackToTop/__snapshots__/BackToTop.stories.ts.snap +24 -0
  129. package/src/Component/BackToTop/__snapshots__/BackToTop.stories.tsx.snap +3 -0
  130. package/src/Component/BackToTop/back-to-top.css +70 -0
  131. package/src/Component/BackToTop/back-to-top.entry.js +1 -0
  132. package/src/Component/BackToTop/back-to-top.twig +26 -0
  133. package/src/Component/Card/Components/CardContent.tsx +1 -1
  134. package/src/Component/Card/Components/CardMedia.tsx +1 -1
  135. package/src/Component/Card/__snapshots__/Card.stories.tsx.snap +3 -3
  136. package/src/Component/Card/card.css +1 -1
  137. package/src/Component/Carousel/Carousel.stories.ts +66 -36
  138. package/src/Component/Carousel/__snapshots__/Carousel.stories.tsx.snap +5 -5
  139. package/src/Component/ContentBlock/Components/ContentBlockMedia.tsx +1 -1
  140. package/src/Component/ContentBlock/content-block.css +1 -1
  141. package/src/Component/DropMenu/DropMenu.stories.ts +21 -2
  142. package/src/Component/DropMenu/DropMenu.tsx +14 -4
  143. package/src/Component/DropMenu/__snapshots__/DropMenu.stories.ts.snap +3 -3
  144. package/src/Component/DropMenu/__snapshots__/DropMenu.stories.tsx.snap +2 -2
  145. package/src/Component/DropMenu/drop-menu.twig +7 -1
  146. package/src/Component/HeroBanner/__snapshots__/HeroBanner.stories.tsx.snap +1 -1
  147. package/src/Component/InPageNavigation/__snapshots__/InPageNavigation.stories.ts.snap +48 -13
  148. package/src/Component/InPageNavigation/in-page-navigation.twig +4 -6
  149. package/src/Component/ListItem/Components/ListItemMedia.tsx +1 -1
  150. package/src/Component/Navigation/Elements/Navigation.ts +25 -29
  151. package/src/Component/Navigation/README.md +11 -10
  152. package/src/Component/Navigation/__snapshots__/Navigation.stories.ts.snap +8 -8
  153. package/src/Component/Navigation/_navigation-dropdown.css +9 -11
  154. package/src/Component/Navigation/_navigation-mega.css +5 -11
  155. package/src/Component/Navigation/_navigation.css +5 -3
  156. package/src/Component/Navigation/menu.twig +1 -1
  157. package/src/Component/Navigation/navigation.twig +0 -1
  158. package/src/Component/Pagination/Pagination.stories.ts +57 -27
  159. package/src/Component/Pagination/pagination.css +16 -7
  160. package/src/Component/Popover/__snapshots__/Popover.stories.tsx.snap +3 -3
  161. package/src/Component/SideNavigation/Components/SideNavigationTree.tsx +68 -24
  162. package/src/Component/SideNavigation/README.md +78 -32
  163. package/src/Component/SideNavigation/SideNavigation.stories.ts +41 -2
  164. package/src/Component/SideNavigation/SideNavigation.stories.tsx +31 -0
  165. package/src/Component/SideNavigation/SideNavigation.tsx +13 -3
  166. package/src/Component/SideNavigation/__snapshots__/SideNavigation.stories.ts.snap +87 -3
  167. package/src/Component/SideNavigation/__snapshots__/SideNavigation.stories.tsx.snap +2 -0
  168. package/src/Component/SideNavigation/side-navigation.css +3 -3
  169. package/src/Component/SideNavigation/side-navigation.twig +10 -3
  170. package/src/Component/Tabs/Components/TabDropMenu.tsx +7 -1
  171. package/src/Component/Tabs/Elements/Tabs.ts +13 -3
  172. package/src/Component/Tabs/Tabs.stories.ts +33 -12
  173. package/src/Component/Tabs/Tabs.stories.tsx +28 -14
  174. package/src/Component/Tabs/__snapshots__/Tabs.stories.ts.snap +3 -3
  175. package/src/Component/Tabs/__snapshots__/Tabs.stories.tsx.snap +2 -2
  176. package/src/Component/Translate/Elements/Translate.ts +76 -0
  177. package/src/Component/Translate/README.md +285 -0
  178. package/src/Component/Translate/Translate.stories.ts +87 -0
  179. package/src/Component/Translate/Translate.stories.tsx +77 -0
  180. package/src/Component/Translate/Translate.tsx +213 -0
  181. package/src/Component/Translate/__snapshots__/Translate.stories.ts.snap +18 -0
  182. package/src/Component/Translate/__snapshots__/Translate.stories.tsx.snap +3 -0
  183. package/src/Component/Translate/languages.ts +39 -0
  184. package/src/Component/Translate/translate.css +62 -0
  185. package/src/Component/Translate/translate.entry.js +1 -0
  186. package/src/Component/Translate/translate.twig +44 -0
  187. package/src/Form/Autocomplete/Autocomplete.stories.tsx +2 -1
  188. package/src/Form/FeedbackForm/Elements/FeedbackForm.ts +122 -0
  189. package/src/Form/FeedbackForm/FeedbackForm.stories.ts +99 -0
  190. package/src/Form/FeedbackForm/FeedbackForm.stories.tsx +73 -0
  191. package/src/Form/FeedbackForm/FeedbackForm.tsx +196 -0
  192. package/src/Form/FeedbackForm/README.md +233 -0
  193. package/src/Form/FeedbackForm/__snapshots__/FeedbackForm.stories.ts.snap +125 -0
  194. package/src/Form/FeedbackForm/__snapshots__/FeedbackForm.stories.tsx.snap +5 -0
  195. package/src/Form/FeedbackForm/feedback-form.css +74 -0
  196. package/src/Form/FeedbackForm/feedback-form.entry.js +1 -0
  197. package/src/Form/FeedbackForm/feedback-form.twig +80 -0
  198. package/src/Form/form.css +17 -19
  199. package/src/Introduction.mdx +1 -1
  200. package/src/Layout/Footer/__snapshots__/Footer.stories.ts.snap +3 -3
  201. package/src/Layout/Header/Components/GlobalToggle.tsx +1 -1
  202. package/src/Layout/Header/Header.stories.ts +53 -48
  203. package/src/Layout/Header/Header.stories.tsx +35 -22
  204. package/src/Layout/Header/Header.tsx +2 -2
  205. package/src/Layout/Header/__snapshots__/Header.stories.ts.snap +18 -14
  206. package/src/Layout/Header/__snapshots__/Header.stories.tsx.snap +2 -2
  207. package/src/Layout/Header/_toggles.css +7 -7
  208. package/src/Layout/Page/__snapshots__/Page.stories.tsx.snap +1 -1
  209. package/src/Layout/Sidebar/sidebar.css +10 -3
  210. package/src/Utility/Elements/google-translate-widget.ts +293 -0
  211. package/src/Utility/global.d.ts +4 -0
  212. package/src/Utility/utilities.ts +2 -0
  213. package/src/_custom-media.css +2 -0
  214. package/src/elements.ts +3 -0
  215. package/src/enums.ts +2 -0
  216. package/src/react.ts +3 -0
  217. package/src/tokens.js +1 -0
  218. package/dist/build/chunks/Accordion-GN0sME4W.js +0 -1
  219. package/dist/build/chunks/AccordionGroup-Bzqp5__q.js +0 -1
  220. package/dist/build/chunks/AccordionMobile-DG5jSe8Q.js +0 -1
  221. package/dist/build/chunks/Autocomplete-B4RtkAyh.js +0 -1
  222. package/dist/build/chunks/ClosableAlert-DEhj8Nxm.js +0 -1
  223. package/dist/build/chunks/CookieConsent-BkCMRVST.js +0 -1
  224. package/dist/build/chunks/Dialog-Haahn-Yn.js +0 -1
  225. package/dist/build/chunks/DropMenu-1baQc8Yc.js +0 -1
  226. package/dist/build/chunks/Gallery-D8NGXy2Q.js +0 -1
  227. package/dist/build/chunks/GlobalToggle-B52fdBQu.js +0 -1
  228. package/dist/build/chunks/InPageNavigation-Dyn2TR-N.js +0 -1
  229. package/dist/build/chunks/Navigation-hOrfLq7h.js +0 -1
  230. package/dist/build/chunks/Popover-Cxg-s5Jn.js +0 -1
  231. package/dist/build/chunks/Sticky-BGCuuEJK.js +0 -1
  232. package/dist/build/chunks/Tabs-DYy1YTdU.js +0 -15
  233. package/dist/build/chunks/cookie-compliance-Bbm-0vpj.js +0 -1
  234. package/dist/build/chunks/enums-DLV_1NAf.js +0 -1
  235. package/dist/build/chunks/polyfills-C7pRt54M.js +0 -1
  236. package/dist/build/chunks/utilities-_Bq3TxAQ.js +0 -1
@@ -1,4 +1,5 @@
1
1
  import { Meta, StoryObj } from "@storybook/react-vite"
2
+ import { expect } from "storybook/test"
2
3
  import Component from "./SideNavigation"
3
4
  import SideNavigationTree from "./Components/SideNavigationTree"
4
5
  import "../Navigation/navigation.css"
@@ -66,3 +67,33 @@ const meta: Meta<typeof Component> = {
66
67
  export default meta
67
68
 
68
69
  export const SideNavigation: StoryObj<typeof meta> = {}
70
+
71
+ export const Collapsible: StoryObj<typeof meta> = {
72
+ args: {
73
+ collapsible: true,
74
+ },
75
+ play: async ({ canvas, userEvent, step }) => {
76
+ const item = canvas.getByRole("link", { name: "What we do" }).closest("li")
77
+ const trigger = item.querySelector(":scope > .mx-nav__toggle")
78
+ const list = item.querySelector(":scope > ul")
79
+
80
+ await step("Opens the active trail", async () => {
81
+ await expect(trigger).toHaveAttribute("aria-expanded", "true")
82
+ await expect(trigger).toHaveAttribute("aria-controls", list.id)
83
+ await expect(list).not.toHaveAttribute("inert")
84
+ })
85
+
86
+ await step("Closes on click", async () => {
87
+ await userEvent.click(trigger)
88
+ await expect(trigger).toHaveAttribute("aria-expanded", "false")
89
+ await expect(list).toHaveAttribute("inert")
90
+ })
91
+
92
+ await step("Reopens on click", async () => {
93
+ await userEvent.click(trigger)
94
+ await expect(trigger).toHaveAttribute("aria-expanded", "true")
95
+ await expect(list).not.toHaveAttribute("inert")
96
+ ;(trigger as HTMLElement).blur()
97
+ })
98
+ },
99
+ }
@@ -1,18 +1,23 @@
1
- import { JSX, ReactNode, PropsWithChildren, useId } from "react"
1
+ import { JSX, ReactNode, PropsWithChildren, createContext, useId } from "react"
2
2
  import classNames from "classnames"
3
3
 
4
4
  type SideNavigationProps = PropsWithChildren & {
5
5
  parent?: ReactNode
6
6
  title?: string
7
7
  id?: string
8
+ collapsible?: boolean
8
9
  className?: string
9
10
  }
10
11
 
12
+ // Whether sub-lists in a nested SideNavigationTree can be collapsed.
13
+ export const SideNavigationContext = createContext<boolean>(false)
14
+
11
15
  const SideNavigation = ({
12
16
  children,
13
17
  parent,
14
18
  title,
15
19
  id,
20
+ collapsible = false,
16
21
  className,
17
22
  }: SideNavigationProps): JSX.Element => {
18
23
  const autoId = useId()
@@ -20,13 +25,18 @@ const SideNavigation = ({
20
25
 
21
26
  return (
22
27
  <nav
23
- className={classNames("mx-nav", "mx-side-nav", className)}
28
+ className={classNames(
29
+ "mx-nav",
30
+ "mx-side-nav",
31
+ { "mx-nav--collapsible": collapsible },
32
+ className,
33
+ )}
24
34
  {...(title ? { "aria-label": title } : { "aria-labelledby": navId })}
25
35
  >
26
36
  <div className="mx-side-nav__parent" id={navId}>
27
37
  {parent}
28
38
  </div>
29
- {children}
39
+ <SideNavigationContext value={collapsible}>{children}</SideNavigationContext>
30
40
  </nav>
31
41
  )
32
42
  }
@@ -1,9 +1,93 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
+ exports[`Collapsible 1`] = `
4
+ "
5
+
6
+ <mx-nav openactive="">
7
+ <nav class="mx-nav mx-side-nav mx-nav--collapsible" aria-label="Sidebar nav">
8
+ <div class="mx-side-nav__parent" id="sidenav-story">
9
+
10
+ <a href="#">
11
+
12
+ About
13
+ </a>
14
+
15
+ </div>
16
+ <ul class="mx-nav__level-1">
17
+ <li>
18
+ <a href="#">
19
+ News
20
+ </a>
21
+ </li>
22
+ <li class="mx-nav__has-subnav">
23
+ <a href="#" id="unique-2">
24
+ What we do
25
+ </a>
26
+ <button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only" type="button" aria-expanded="true" aria-controls="unique-0">Sub-navigation</button>
27
+ <ul class="mx-nav__level-2" id="unique-0" aria-labelledby="unique-1">
28
+ <li>
29
+ <a href="#">
30
+ Events
31
+ </a>
32
+ </li>
33
+ <li class="mx-nav__has-subnav">
34
+ <a href="#" id="unique-3">
35
+ Who we are
36
+ </a>
37
+ <button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only" type="button" aria-expanded="true" aria-controls="unique-2">Sub-navigation</button>
38
+ <ul class="mx-nav__level-3" id="unique-1" aria-labelledby="unique-3">
39
+ <li>
40
+ <a href="#">
41
+ Our team
42
+ </a>
43
+ </li>
44
+ <li>
45
+ <a href="#" aria-current="page">
46
+ Our philosophy
47
+ </a>
48
+ </li>
49
+ <li>
50
+ <a href="#">
51
+ Why music?
52
+ </a>
53
+ </li>
54
+ </ul>
55
+
56
+ </li>
57
+ <li>
58
+ <a href="#">
59
+ Join us
60
+ </a>
61
+ </li>
62
+ <li>
63
+ <a href="#">
64
+ Careers
65
+ </a>
66
+ </li>
67
+ </ul>
68
+
69
+ </li>
70
+ <li>
71
+ <a href="#">
72
+ Contact
73
+ </a>
74
+ </li>
75
+ <li>
76
+ <a href="#">
77
+ Resources
78
+ </a>
79
+ </li>
80
+ </ul>
81
+
82
+ </nav>
83
+ </mx-nav>
84
+ "
85
+ `;
86
+
3
87
  exports[`Side Navigation 1`] = `
4
88
  "
5
89
 
6
- <nav class="mx-nav mx-side-nav" aria-label="Sidebar nav" "="">
90
+ <nav class="mx-nav mx-side-nav" aria-label="Sidebar nav">
7
91
  <div class="mx-side-nav__parent" id="sidenav-story">
8
92
 
9
93
  <a href="#">
@@ -22,7 +106,7 @@ exports[`Side Navigation 1`] = `
22
106
  <a href="#">
23
107
  What we do
24
108
  </a>
25
- <button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only">Sub-navigation</button>
109
+ <button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only" type="button">Sub-navigation</button>
26
110
  <ul class="mx-nav__level-2">
27
111
  <li>
28
112
  <a href="#">
@@ -33,7 +117,7 @@ exports[`Side Navigation 1`] = `
33
117
  <a href="#">
34
118
  Who we are
35
119
  </a>
36
- <button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only">Sub-navigation</button>
120
+ <button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only" type="button">Sub-navigation</button>
37
121
  <ul class="mx-nav__level-3">
38
122
  <li>
39
123
  <a href="#">
@@ -1,3 +1,5 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
+ exports[`Collapsible 1`] = `"<nav class="mx-nav mx-side-nav mx-nav--collapsible" aria-label="Sidebar nav"><div class="mx-side-nav__parent" id="sidenav-story"><a class="mx-link" href="#">About</a></div><ul class="mx-nav__level-1"><li><a href="#" class="mx-link">News</a></li><li class="mx-nav__has-subnav"><a href="#" class="mx-link" id="_r_17_">What we do</a><button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only" type="button" aria-expanded="true" aria-controls="_r_16_">Sub-navigation</button><ul class="mx-nav__level-2" id="_r_16_" aria-labelledby="_r_17_"><li><a href="#" class="mx-link">Events</a></li><li class="mx-nav__has-subnav"><a href="#" class="mx-link" id="_r_1d_">Who we are</a><button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only" type="button" aria-expanded="true" aria-controls="_r_1c_">Sub-navigation</button><ul class="mx-nav__level-3" id="_r_1c_" aria-labelledby="_r_1d_"><li><a href="#" class="mx-link">Our team</a></li><li><a href="#" class="mx-link" aria-current="page">Our philosophy</a></li><li><a href="#" class="mx-link">Why music?</a></li></ul></li><li><a href="#" class="mx-link">Join us</a></li><li><a href="#" class="mx-link">Careers</a></li></ul></li><li><a href="#" class="mx-link">Contact</a></li><li><a href="#" class="mx-link">Resources</a></li></ul></nav>"`;
4
+
3
5
  exports[`Side Navigation 1`] = `"<nav class="mx-nav mx-side-nav" aria-label="Sidebar nav"><div class="mx-side-nav__parent" id="sidenav-story"><a class="mx-link" href="#">About</a></div><ul class="mx-nav__level-1"><li><a href="#" class="mx-link">News</a></li><li class="mx-nav__has-subnav"><a href="#" class="mx-link">What we do</a><button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only" type="button">Sub-navigation</button><ul class="mx-nav__level-2"><li><a href="#" class="mx-link">Events</a></li><li class="mx-nav__has-subnav"><a href="#" class="mx-link">Who we are</a><button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only" type="button">Sub-navigation</button><ul class="mx-nav__level-3"><li><a href="#" class="mx-link">Our team</a></li><li><a href="#" class="mx-link" aria-current="page">Our philosophy</a></li><li><a href="#" class="mx-link">Why music?</a></li></ul></li><li><a href="#" class="mx-link">Join us</a></li><li><a href="#" class="mx-link">Careers</a></li></ul></li><li><a href="#" class="mx-link">Contact</a></li><li><a href="#" class="mx-link">Resources</a></li></ul></nav>"`;
@@ -41,7 +41,7 @@
41
41
  & .mx-nav__level-2 > li > a {
42
42
  --indent: var(--spacing-s);
43
43
 
44
- @media (--global-nav-up) {
44
+ @media (--sidebar-up) {
45
45
  --indent: var(--spacing-m);
46
46
  }
47
47
  }
@@ -49,7 +49,7 @@
49
49
  & .mx-nav__level-3 > li > a {
50
50
  --indent: var(--spacing-m);
51
51
 
52
- @media (--global-nav-up) {
52
+ @media (--sidebar-up) {
53
53
  --indent: var(--spacing-l);
54
54
  }
55
55
  }
@@ -62,7 +62,7 @@
62
62
  color: var(--nav-active-foreground, inherit);
63
63
  }
64
64
 
65
- & .mx-nav__toggle {
65
+ &:not(.mx-nav--collapsible) .mx-nav__toggle {
66
66
  display: none;
67
67
  }
68
68
  }
@@ -2,7 +2,8 @@
2
2
  {% set baseClass = 'mx-nav' %}
3
3
  {% set classes = [
4
4
  baseClass,
5
- 'mx-side-nav'
5
+ 'mx-side-nav',
6
+ collapsible ? 'mx-nav--collapsible' : null,
6
7
  ] %}
7
8
  {% set attributes = (attributes ?? create_attribute()).addClass(classes) %}
8
9
 
@@ -19,7 +20,7 @@
19
20
  {{ item.link.title }}
20
21
  {% endif %}
21
22
  {% if item.items is not empty %}
22
- <button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only">Sub-navigation</button>
23
+ <button class="mx-nav__toggle mx-icon mx-icon--chevron-down mx-icon--only" type="button">Sub-navigation</button>
23
24
  {{ nav.menutree(item.items, level + 1, setInert, baseClass) }}
24
25
  {% endif %}
25
26
  </li>
@@ -27,9 +28,15 @@
27
28
  </ul>
28
29
  {% endmacro %}
29
30
 
30
- <nav{{ attributes }}{% if title is not empty %} aria-label="{{ title }}"{% else %} aria-labelledby="{{ id }}{% endif %}">
31
+ {% if collapsible %}
32
+ <mx-nav openActive>
33
+ {% endif %}
34
+ <nav{{ attributes }}{% if title is not empty %} aria-label="{{ title }}"{% else %} aria-labelledby="{{ id }}"{% endif %}>
31
35
  <div class="mx-side-nav__parent" id="{{ id }}">
32
36
  {{ parent }}
33
37
  </div>
34
38
  {{ nav.menutree(items, 0, false, baseClass) }}
35
39
  </nav>
40
+ {% if collapsible %}
41
+ </mx-nav>
42
+ {% endif %}
@@ -5,7 +5,13 @@ import { useTabContext } from "../Tabs"
5
5
  const TabDropMenu = ({ children }: PropsWithChildren): JSX.Element => {
6
6
  const { activeTabLabel } = useTabContext()
7
7
  return (
8
- <DropMenu label={activeTabLabel} className="mobile-only" role="tablist">
8
+ <DropMenu
9
+ label={activeTabLabel}
10
+ className="mobile-only"
11
+ role="tablist"
12
+ description="Selecting a tab updates the content below. Press escape to close."
13
+ triggerDescription="Select a tab"
14
+ >
9
15
  {children}
10
16
  </DropMenu>
11
17
  )
@@ -150,8 +150,10 @@ export default class Tabs extends HTMLElement {
150
150
  this.dropTriggerClasses =
151
151
  this.getAttribute("dropTriggerClasses") ||
152
152
  "mx-button mx-icon mx-icon--chevron-down mx-icon--end"
153
- this.dropMenuId = this.id || makeAnchor("tab-drop-menu")
153
+ this.dropMenuId = this.id ? `${this.id}-drop-menu` : makeAnchor("tab-drop-menu")
154
154
  const name = `${this.dropMenuId}-target`
155
+ const triggerDescriptionId = `${name}-description`
156
+ const menuDescriptionId = `${this.dropMenuId}-description`
155
157
  const tabDropMenu = new DropMenu()
156
158
  tabDropMenu.setAttribute("closeOnClick", "true")
157
159
  const trigger = createElement(`<button
@@ -159,20 +161,28 @@ export default class Tabs extends HTMLElement {
159
161
  class="mx-popover__trigger ${this.dropTriggerClasses}"
160
162
  popovertarget="${this.dropMenuId}"
161
163
  style="anchor-name: --${this.dropMenuId}"
164
+ aria-describedby="${triggerDescriptionId}"
162
165
  hidden
163
166
  ></button>`)
167
+ const triggerDescription = createElement(
168
+ `<span id="${triggerDescriptionId}" class="sr-only" aria-hidden="true">Select a tab</span>`,
169
+ )
170
+ const menuDescription = createElement(
171
+ `<span id="${menuDescriptionId}" class="sr-only" aria-hidden="true">Selecting a tab updates the content below. Press escape to close.</span>`,
172
+ )
164
173
  const menu = createElement(
165
174
  `<div
166
175
  class="mx-popover mx-drop-menu"
167
176
  id="${this.dropMenuId}"
168
- aria-describedby="${name}"
177
+ aria-labelledby="${name}"
178
+ aria-describedby="${menuDescriptionId}"
169
179
  style="position-anchor: --${this.dropMenuId}"
170
180
  data-placement="${PopoverPlacementModifier.BOTTOM_START}"
171
181
  popover
172
182
  role="tablist"
173
183
  ></div>`,
174
184
  )
175
- tabDropMenu.append(trigger, menu)
185
+ tabDropMenu.append(trigger, triggerDescription, menuDescription, menu)
176
186
  this.prepend(tabDropMenu)
177
187
  return tabDropMenu
178
188
  }
@@ -180,22 +180,43 @@ export const Tabs: Story = {
180
180
 
181
181
  /**
182
182
  * Steps a virtual screen reader through the tab list and asserts on what it announces.
183
+ *
184
+ * Below `Elements/Tabs.ts`'s breakpoint (`mq`, default `(max-width: 720px)`)
185
+ * the tab list collapses into a `DropMenu` popover trigger - see the component
186
+ * doc comment above. This branches on the real `matchMedia(...).matches`,
187
+ * same as the `Tabs` interaction story above, and is paired with
188
+ * `chromatic.modes` on `meta` so both shapes get exercised in CI. See
189
+ * "Testing Mobile / matchMedia-Driven Behavior in Play Functions" in
190
+ * docs/Testing.md.
183
191
  */
184
192
  export const ScreenReaderAnnouncements: Story = {
185
193
  play: async () => {
194
+ const isMobile = matchMedia("(max-width: 767px)").matches
186
195
  const log = await runScreenReaderWalkthrough()
187
- await expect(log).toEqual([
188
- "document",
189
- "tablist, orientated horizontally",
190
- "tab, Funk, selected, 1 control, position 1, set size 3",
191
- "tab, Soul, not selected, 1 control, position 2, set size 3",
192
- "tab, Motown, not selected, 1 control, position 3, set size 3",
193
- "end of tablist, orientated horizontally",
194
- "tabpanel, Funk",
195
- "Funk is the ipsum dolor sit amet, consectetur adipiscing elit. Proin at finibus velit. Curabitur consequat, dolor venenatis ullamcorper commodo, quam mi iaculis sem, non varius massa risus et enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus non mi lorem. Morbi semper sagittis euismod. Vestibulum non ornare orci.",
196
- "end of tabpanel, Funk",
197
- "end of document",
198
- ])
196
+
197
+ if (isMobile) {
198
+ await expect(log).toEqual([
199
+ "document",
200
+ "button, Funk, Select a tab",
201
+ "tabpanel, Funk",
202
+ "Funk is the ipsum dolor sit amet, consectetur adipiscing elit. Proin at finibus velit. Curabitur consequat, dolor venenatis ullamcorper commodo, quam mi iaculis sem, non varius massa risus et enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus non mi lorem. Morbi semper sagittis euismod. Vestibulum non ornare orci.",
203
+ "end of tabpanel, Funk",
204
+ "end of document",
205
+ ])
206
+ } else {
207
+ await expect(log).toEqual([
208
+ "document",
209
+ "tablist, orientated horizontally",
210
+ "tab, Funk, selected, 1 control, position 1, set size 3",
211
+ "tab, Soul, not selected, 1 control, position 2, set size 3",
212
+ "tab, Motown, not selected, 1 control, position 3, set size 3",
213
+ "end of tablist, orientated horizontally",
214
+ "tabpanel, Funk",
215
+ "Funk is the ipsum dolor sit amet, consectetur adipiscing elit. Proin at finibus velit. Curabitur consequat, dolor venenatis ullamcorper commodo, quam mi iaculis sem, non varius massa risus et enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus non mi lorem. Morbi semper sagittis euismod. Vestibulum non ornare orci.",
216
+ "end of tabpanel, Funk",
217
+ "end of document",
218
+ ])
219
+ }
199
220
  },
200
221
  }
201
222
 
@@ -121,20 +121,34 @@ export const Default: StoryObj<typeof meta> = {
121
121
  */
122
122
  export const ScreenReaderAnnouncements: StoryObj<typeof meta> = {
123
123
  play: async () => {
124
+ const isMobile = matchMedia("(max-width: 767px)").matches
124
125
  const log = await runScreenReaderWalkthrough()
125
- await expect(log).toEqual([
126
- "document",
127
- "tablist, orientated horizontally",
128
- "tab, Tab 1, selected, 1 control, position 1, set size 3",
129
- "tab, Tab 2, not selected, 1 control, position 2, set size 3",
130
- "tab, Tab 3, not selected, 1 control, position 3, set size 3",
131
- "end of tablist, orientated horizontally",
132
- "tabpanel, Tab 1",
133
- "paragraph",
134
- "This is tab 1",
135
- "end of paragraph",
136
- "end of tabpanel, Tab 1",
137
- "end of document",
138
- ])
126
+ if (isMobile) {
127
+ await expect(log).toEqual([
128
+ "document",
129
+ "button, Tab 1, Select a tab",
130
+ "tabpanel, Tab 1",
131
+ "paragraph",
132
+ "This is tab 1",
133
+ "end of paragraph",
134
+ "end of tabpanel, Tab 1",
135
+ "end of document",
136
+ ])
137
+ } else {
138
+ await expect(log).toEqual([
139
+ "document",
140
+ "tablist, orientated horizontally",
141
+ "tab, Tab 1, selected, 1 control, position 1, set size 3",
142
+ "tab, Tab 2, not selected, 1 control, position 2, set size 3",
143
+ "tab, Tab 3, not selected, 1 control, position 3, set size 3",
144
+ "end of tablist, orientated horizontally",
145
+ "tabpanel, Tab 1",
146
+ "paragraph",
147
+ "This is tab 1",
148
+ "end of paragraph",
149
+ "end of tabpanel, Tab 1",
150
+ "end of document",
151
+ ])
152
+ }
139
153
  },
140
154
  }
@@ -3,7 +3,7 @@
3
3
  exports[`Screen Reader Announcements 1`] = `
4
4
  "
5
5
 
6
- <mx-tabs class="mx-tabs mx-tabs--lined" id="tab-example"><mx-dropmenu closeonclick="true"><button id="unique-3" class="mx-popover__trigger mx-button mx-icon mx-icon--chevron-down mx-icon--end" popovertarget="tab-example" style="anchor-name: --tab-example" hidden="">Funk</button><div class="mx-popover mx-drop-menu" id="tab-example" aria-describedby="unique-0" style="position-anchor: --tab-example" data-placement="bottom-start" popover="" role="tablist"><button type="button" role="tab" aria-controls="unique-1" aria-selected="true">Funk</button><button type="button" role="tab" aria-controls="unique-2" aria-selected="false">Soul</button><button type="button" role="tab" aria-controls="unique-3" aria-selected="false">Motown</button></div></mx-dropmenu><div class="mx-tabs__list" role="tablist"><button type="button" role="tab" aria-controls="unique-4" aria-selected="true" tabindex="0">Funk</button><button type="button" role="tab" aria-controls="unique-5" aria-selected="false" tabindex="-1">Soul</button><button type="button" role="tab" aria-controls="unique-6" aria-selected="false" tabindex="-1">Motown</button></div>
6
+ <mx-tabs class="mx-tabs mx-tabs--lined" id="tab-example"><mx-dropmenu closeonclick="true"><button id="unique-3" class="mx-popover__trigger mx-button mx-icon mx-icon--chevron-down mx-icon--end" popovertarget="tab-example-drop-menu" style="anchor-name: --tab-example-drop-menu" aria-describedby="unique-0" hidden="">Funk</button><span id="unique-4" class="sr-only" aria-hidden="true">Select a tab</span><span id="unique-5" class="sr-only" aria-hidden="true">Selecting a tab updates the content below. Press escape to close.</span><div class="mx-popover mx-drop-menu" id="tab-example-drop-menu" aria-labelledby="unique-1" aria-describedby="unique-1" style="position-anchor: --tab-example-drop-menu" data-placement="bottom-start" popover="" role="tablist"><button type="button" role="tab" aria-controls="unique-2" aria-selected="true">Funk</button><button type="button" role="tab" aria-controls="unique-3" aria-selected="false">Soul</button><button type="button" role="tab" aria-controls="unique-4" aria-selected="false">Motown</button></div></mx-dropmenu><div class="mx-tabs__list" role="tablist"><button type="button" role="tab" aria-controls="unique-5" aria-selected="true" tabindex="0">Funk</button><button type="button" role="tab" aria-controls="unique-6" aria-selected="false" tabindex="-1">Soul</button><button type="button" role="tab" aria-controls="unique-7" aria-selected="false" tabindex="-1">Motown</button></div>
7
7
  <div class="mx-tabs__panel mx-vertical-flow mx-container" id="unique-0" data-tab="Funk" role="tabpanel" tabindex="-1" aria-label="Funk">
8
8
  Funk is the ipsum dolor sit amet, consectetur adipiscing elit. Proin at finibus velit. Curabitur consequat, dolor venenatis ullamcorper commodo, quam mi iaculis sem, non varius massa risus et enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus non mi lorem. Morbi semper sagittis euismod. Vestibulum non ornare orci.
9
9
  </div>
@@ -21,7 +21,7 @@ exports[`Screen Reader Announcements 1`] = `
21
21
  exports[`Tabs 1`] = `
22
22
  "
23
23
 
24
- <mx-tabs class="mx-tabs mx-tabs--lined" id="tab-example"><mx-dropmenu closeonclick="true"><button id="unique-3" class="mx-popover__trigger mx-button mx-icon mx-icon--chevron-down mx-icon--end" popovertarget="tab-example" style="anchor-name: --tab-example" hidden="">Funk</button><div class="mx-popover mx-drop-menu" id="tab-example" aria-describedby="unique-0" style="position-anchor: --tab-example" data-placement="bottom-start" popover="" role="tablist"><button type="button" role="tab" aria-controls="unique-1" aria-selected="true">Funk</button><button type="button" role="tab" aria-controls="unique-2" aria-selected="false">Soul</button><button type="button" role="tab" aria-controls="unique-3" aria-selected="false">Motown</button></div></mx-dropmenu><div class="mx-tabs__list" role="tablist"><button type="button" role="tab" aria-controls="unique-4" aria-selected="true" tabindex="0">Funk</button><button type="button" role="tab" aria-controls="unique-5" aria-selected="false" tabindex="-1">Soul</button><button type="button" role="tab" aria-controls="unique-6" aria-selected="false" tabindex="-1">Motown</button></div>
24
+ <mx-tabs class="mx-tabs mx-tabs--lined" id="tab-example"><mx-dropmenu closeonclick="true"><button id="unique-3" class="mx-popover__trigger mx-button mx-icon mx-icon--chevron-down mx-icon--end" popovertarget="tab-example-drop-menu" style="anchor-name: --tab-example-drop-menu" aria-describedby="unique-0" hidden="">Funk</button><span id="unique-4" class="sr-only" aria-hidden="true">Select a tab</span><span id="unique-5" class="sr-only" aria-hidden="true">Selecting a tab updates the content below. Press escape to close.</span><div class="mx-popover mx-drop-menu" id="tab-example-drop-menu" aria-labelledby="unique-1" aria-describedby="unique-1" style="position-anchor: --tab-example-drop-menu" data-placement="bottom-start" popover="" role="tablist"><button type="button" role="tab" aria-controls="unique-2" aria-selected="true">Funk</button><button type="button" role="tab" aria-controls="unique-3" aria-selected="false">Soul</button><button type="button" role="tab" aria-controls="unique-4" aria-selected="false">Motown</button></div></mx-dropmenu><div class="mx-tabs__list" role="tablist"><button type="button" role="tab" aria-controls="unique-5" aria-selected="true" tabindex="0">Funk</button><button type="button" role="tab" aria-controls="unique-6" aria-selected="false" tabindex="-1">Soul</button><button type="button" role="tab" aria-controls="unique-7" aria-selected="false" tabindex="-1">Motown</button></div>
25
25
  <div class="mx-tabs__panel mx-vertical-flow mx-container" id="unique-0" data-tab="Funk" role="tabpanel" tabindex="-1" aria-label="Funk">
26
26
  Funk is the ipsum dolor sit amet, consectetur adipiscing elit. Proin at finibus velit. Curabitur consequat, dolor venenatis ullamcorper commodo, quam mi iaculis sem, non varius massa risus et enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus non mi lorem. Morbi semper sagittis euismod. Vestibulum non ornare orci.
27
27
  </div>
@@ -41,7 +41,7 @@ exports[`With Heading 1`] = `
41
41
 
42
42
  <h2>Tabs heading</h2>
43
43
 
44
- <mx-tabs class="mx-tabs mx-tabs--lined" id="tab-example"><mx-dropmenu closeonclick="true"><button id="unique-3" class="mx-popover__trigger mx-button mx-icon mx-icon--chevron-down mx-icon--end" popovertarget="tab-example" style="anchor-name: --tab-example" hidden="">Funk</button><div class="mx-popover mx-drop-menu" id="tab-example" aria-describedby="unique-0" style="position-anchor: --tab-example" data-placement="bottom-start" popover="" role="tablist"><button type="button" role="tab" aria-controls="unique-1" aria-selected="true">Funk</button><button type="button" role="tab" aria-controls="unique-2" aria-selected="false">Soul</button><button type="button" role="tab" aria-controls="unique-3" aria-selected="false">Motown</button></div></mx-dropmenu><div class="mx-tabs__list" role="tablist"><button type="button" role="tab" aria-controls="unique-4" aria-selected="true" tabindex="0">Funk</button><button type="button" role="tab" aria-controls="unique-5" aria-selected="false" tabindex="-1">Soul</button><button type="button" role="tab" aria-controls="unique-6" aria-selected="false" tabindex="-1">Motown</button></div>
44
+ <mx-tabs class="mx-tabs mx-tabs--lined" id="tab-example"><mx-dropmenu closeonclick="true"><button id="unique-3" class="mx-popover__trigger mx-button mx-icon mx-icon--chevron-down mx-icon--end" popovertarget="tab-example-drop-menu" style="anchor-name: --tab-example-drop-menu" aria-describedby="unique-0" hidden="">Funk</button><span id="unique-4" class="sr-only" aria-hidden="true">Select a tab</span><span id="unique-5" class="sr-only" aria-hidden="true">Selecting a tab updates the content below. Press escape to close.</span><div class="mx-popover mx-drop-menu" id="tab-example-drop-menu" aria-labelledby="unique-1" aria-describedby="unique-1" style="position-anchor: --tab-example-drop-menu" data-placement="bottom-start" popover="" role="tablist"><button type="button" role="tab" aria-controls="unique-2" aria-selected="true">Funk</button><button type="button" role="tab" aria-controls="unique-3" aria-selected="false">Soul</button><button type="button" role="tab" aria-controls="unique-4" aria-selected="false">Motown</button></div></mx-dropmenu><div class="mx-tabs__list" role="tablist"><button type="button" role="tab" aria-controls="unique-5" aria-selected="true" tabindex="0">Funk</button><button type="button" role="tab" aria-controls="unique-6" aria-selected="false" tabindex="-1">Soul</button><button type="button" role="tab" aria-controls="unique-7" aria-selected="false" tabindex="-1">Motown</button></div>
45
45
  <div class="mx-tabs__panel mx-vertical-flow mx-container" id="unique-0" data-tab="Funk" role="tabpanel" tabindex="-1" aria-label="Funk">
46
46
  Funk is the ipsum dolor sit amet, consectetur adipiscing elit. Proin at finibus velit. Curabitur consequat, dolor venenatis ullamcorper commodo, quam mi iaculis sem, non varius massa risus et enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus non mi lorem. Morbi semper sagittis euismod. Vestibulum non ornare orci.
47
47
  </div>
@@ -1,5 +1,5 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
- exports[`Default 1`] = `"<div class="mx-tabs"><div class="mobile-only"><button id="tabs-storybook-target" type="button" class="mx-button mx-icon mx-icon--chevron-down mx-icon--end mx-popover__trigger" popovertarget="tabs-storybook" style="anchor-name: --drop-menu-tabs-storybook;">Tab 1</button><div id="tabs-storybook" class="mx-popover mx-drop-menu" popover="" aria-labelledby="tabs-storybook-target" aria-describedby="tabs-storybook-description" role="tablist" style="position-anchor: --drop-menu-tabs-storybook;"><div id="tabs-storybook-description" class="sr-only" aria-hidden="true">Results will update when you make a selection. Press escape to close.</div><span data-focus-scope-start="true" hidden=""></span><button type="button" data-id="tab-1" aria-checked="true" role="tab" aria-controls="tab-1" aria-selected="true">Tab 1</button><button type="button" data-id="tab-2" aria-checked="false" role="tab" aria-controls="tab-2" aria-selected="false">Tab 2</button><button type="button" data-id="tab-3" aria-checked="false" role="tab" aria-controls="tab-3" aria-selected="false">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div></div><div class="mx-tabs__list desktop-only" role="tablist"><span data-focus-scope-start="true" hidden=""></span><button id="tab-1-button" aria-controls="tab-1" role="tab" aria-selected="true" tabindex="0" type="button">Tab 1</button><button id="tab-2-button" aria-controls="tab-2" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 2</button><button id="tab-3-button" aria-controls="tab-3" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div><div id="tab-1" class="mx-tabs__panel mx-vertical-flow mx-section--l" aria-labelledby="tab-1-button" role="tabpanel" tabindex="0"><p>This is tab 1</p></div><div id="tab-2" class="mx-tabs__panel mx-vertical-flow mx-section--l" aria-labelledby="tab-2-button" role="tabpanel" tabindex="0" inert=""><p>This is tab 2</p></div><div id="tab-3" class="mx-tabs__panel mx-vertical-flow mx-section--l" inert="" aria-labelledby="tab-3-button" role="tabpanel" tabindex="0"><p>This is tab 3</p></div></div>"`;
3
+ exports[`Default 1`] = `"<div class="mx-tabs"><div class="mobile-only"><button id="tabs-storybook-target" type="button" class="mx-button mx-icon mx-icon--chevron-down mx-icon--end mx-popover__trigger" popovertarget="tabs-storybook" aria-describedby="tabs-storybook-target-desc" style="anchor-name: --drop-menu-tabs-storybook;">Tab 1</button><span id="tabs-storybook-target-desc" class="sr-only" aria-hidden="true">Select a tab</span><span id="tabs-storybook-description" class="sr-only" aria-hidden="true">Selecting a tab updates the content below. Press escape to close.</span><div id="tabs-storybook" class="mx-popover mx-drop-menu" popover="" aria-labelledby="tabs-storybook-target" aria-describedby="tabs-storybook-description" role="tablist" style="position-anchor: --drop-menu-tabs-storybook;"><span data-focus-scope-start="true" hidden=""></span><button type="button" data-id="tab-1" aria-checked="true" role="tab" aria-controls="tab-1" aria-selected="true">Tab 1</button><button type="button" data-id="tab-2" aria-checked="false" role="tab" aria-controls="tab-2" aria-selected="false">Tab 2</button><button type="button" data-id="tab-3" aria-checked="false" role="tab" aria-controls="tab-3" aria-selected="false">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div></div><div class="mx-tabs__list desktop-only" role="tablist"><span data-focus-scope-start="true" hidden=""></span><button id="tab-1-button" aria-controls="tab-1" role="tab" aria-selected="true" tabindex="0" type="button">Tab 1</button><button id="tab-2-button" aria-controls="tab-2" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 2</button><button id="tab-3-button" aria-controls="tab-3" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div><div id="tab-1" class="mx-tabs__panel mx-vertical-flow mx-section--l" aria-labelledby="tab-1-button" role="tabpanel" tabindex="0"><p>This is tab 1</p></div><div id="tab-2" class="mx-tabs__panel mx-vertical-flow mx-section--l" aria-labelledby="tab-2-button" role="tabpanel" tabindex="0" inert=""><p>This is tab 2</p></div><div id="tab-3" class="mx-tabs__panel mx-vertical-flow mx-section--l" inert="" aria-labelledby="tab-3-button" role="tabpanel" tabindex="0"><p>This is tab 3</p></div></div>"`;
4
4
 
5
- exports[`Screen Reader Announcements 1`] = `"<div class="mx-tabs"><div class="mobile-only"><button id="tabs-storybook-target" type="button" class="mx-button mx-icon mx-icon--chevron-down mx-icon--end mx-popover__trigger" popovertarget="tabs-storybook" style="anchor-name: --drop-menu-tabs-storybook;">Tab 1</button><div id="tabs-storybook" class="mx-popover mx-drop-menu" popover="" aria-labelledby="tabs-storybook-target" aria-describedby="tabs-storybook-description" role="tablist" style="position-anchor: --drop-menu-tabs-storybook;"><div id="tabs-storybook-description" class="sr-only" aria-hidden="true">Results will update when you make a selection. Press escape to close.</div><span data-focus-scope-start="true" hidden=""></span><button type="button" data-id="tab-1" aria-checked="true" role="tab" aria-controls="tab-1" aria-selected="true">Tab 1</button><button type="button" data-id="tab-2" aria-checked="false" role="tab" aria-controls="tab-2" aria-selected="false">Tab 2</button><button type="button" data-id="tab-3" aria-checked="false" role="tab" aria-controls="tab-3" aria-selected="false">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div></div><div class="mx-tabs__list desktop-only" role="tablist"><span data-focus-scope-start="true" hidden=""></span><button id="tab-1-button" aria-controls="tab-1" role="tab" aria-selected="true" tabindex="0" type="button">Tab 1</button><button id="tab-2-button" aria-controls="tab-2" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 2</button><button id="tab-3-button" aria-controls="tab-3" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div><div id="tab-1" class="mx-tabs__panel mx-vertical-flow mx-section--l" aria-labelledby="tab-1-button" role="tabpanel" tabindex="0"><p>This is tab 1</p></div><div id="tab-2" class="mx-tabs__panel mx-vertical-flow mx-section--l" inert="" aria-labelledby="tab-2-button" role="tabpanel" tabindex="0"><p>This is tab 2</p></div><div id="tab-3" class="mx-tabs__panel mx-vertical-flow mx-section--l" inert="" aria-labelledby="tab-3-button" role="tabpanel" tabindex="0"><p>This is tab 3</p></div></div>"`;
5
+ exports[`Screen Reader Announcements 1`] = `"<div class="mx-tabs"><div class="mobile-only"><button id="tabs-storybook-target" type="button" class="mx-button mx-icon mx-icon--chevron-down mx-icon--end mx-popover__trigger" popovertarget="tabs-storybook" aria-describedby="tabs-storybook-target-desc" style="anchor-name: --drop-menu-tabs-storybook;">Tab 1</button><span id="tabs-storybook-target-desc" class="sr-only" aria-hidden="true">Select a tab</span><span id="tabs-storybook-description" class="sr-only" aria-hidden="true">Selecting a tab updates the content below. Press escape to close.</span><div id="tabs-storybook" class="mx-popover mx-drop-menu" popover="" aria-labelledby="tabs-storybook-target" aria-describedby="tabs-storybook-description" role="tablist" style="position-anchor: --drop-menu-tabs-storybook;"><span data-focus-scope-start="true" hidden=""></span><button type="button" data-id="tab-1" aria-checked="true" role="tab" aria-controls="tab-1" aria-selected="true">Tab 1</button><button type="button" data-id="tab-2" aria-checked="false" role="tab" aria-controls="tab-2" aria-selected="false">Tab 2</button><button type="button" data-id="tab-3" aria-checked="false" role="tab" aria-controls="tab-3" aria-selected="false">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div></div><div class="mx-tabs__list desktop-only" role="tablist"><span data-focus-scope-start="true" hidden=""></span><button id="tab-1-button" aria-controls="tab-1" role="tab" aria-selected="true" tabindex="0" type="button">Tab 1</button><button id="tab-2-button" aria-controls="tab-2" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 2</button><button id="tab-3-button" aria-controls="tab-3" role="tab" aria-selected="false" tabindex="-1" type="button">Tab 3</button><span data-focus-scope-end="true" hidden=""></span></div><div id="tab-1" class="mx-tabs__panel mx-vertical-flow mx-section--l" aria-labelledby="tab-1-button" role="tabpanel" tabindex="0"><p>This is tab 1</p></div><div id="tab-2" class="mx-tabs__panel mx-vertical-flow mx-section--l" inert="" aria-labelledby="tab-2-button" role="tabpanel" tabindex="0"><p>This is tab 2</p></div><div id="tab-3" class="mx-tabs__panel mx-vertical-flow mx-section--l" inert="" aria-labelledby="tab-3-button" role="tabpanel" tabindex="0"><p>This is tab 3</p></div></div>"`;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Translate
3
+ * @file A language switcher built on `DropMenu` (roving-focus keyboard nav,
4
+ * click-to-select, and the `drop-menu-select` event all come from there for
5
+ * free — see DropMenu's own docs). This layer drives Google's Website
6
+ * Translator widget in response to that selection via the shared
7
+ * `GoogleTranslateWidget` class.
8
+ */
9
+
10
+ import DropMenu, { DropMenuEvent } from "../../DropMenu/Elements/DropMenu"
11
+ import GoogleTranslateWidget from "../../../Utility/Elements/google-translate-widget"
12
+
13
+ export default class Translate extends DropMenu {
14
+ widget?: GoogleTranslateWidget
15
+
16
+ async connectedCallback(): Promise<void> {
17
+ await super.connectedCallback()
18
+ if (!this.container) return
19
+
20
+ this.widget = new GoogleTranslateWidget(this, {
21
+ defaultLanguage: this.dataset.defaultLanguage || "en",
22
+ blockedPaths: (this.dataset.blockedPaths || "")
23
+ .split(",")
24
+ .map(path => path.trim())
25
+ .filter(Boolean),
26
+ scriptUrl: this.dataset.scriptUrl,
27
+ storageKey: this.dataset.storageKey,
28
+ updateTriggerLabel: this.dataset.updateTriggerLabel !== "false",
29
+ notice: this.dataset.noticeContent
30
+ ? {
31
+ content: this.dataset.noticeContent,
32
+ blockedContent: this.dataset.noticeBlockedContent,
33
+ anchorSelector: this.dataset.noticeAnchor,
34
+ position: this.dataset.noticePosition as InsertPosition | undefined,
35
+ }
36
+ : undefined,
37
+ onLanguageChange: code => this.dispatchLifecycleEvent("translate-language-change", code),
38
+ })
39
+ this.widget.init()
40
+
41
+ const { signal } = this.controller
42
+ this.container.addEventListener("drop-menu-select", this.handleLanguageSelect, { signal })
43
+ this.container.addEventListener(
44
+ "toggle",
45
+ (event: Event) => {
46
+ const { newState } = event as ToggleEvent
47
+ this.dispatchLifecycleEvent(newState === "open" ? "translate-open" : "translate-close")
48
+ },
49
+ { signal },
50
+ )
51
+ }
52
+
53
+ disconnectedCallback(): void {
54
+ super.disconnectedCallback()
55
+ this.widget?.destroy()
56
+ }
57
+
58
+ handleLanguageSelect = (event: Event): void => {
59
+ const { target } = (event as DropMenuEvent).detail
60
+ if (!(target instanceof HTMLAnchorElement)) return
61
+ const code = target.dataset.language
62
+ if (code) this.widget?.setLanguage(code)
63
+ }
64
+
65
+ dispatchLifecycleEvent(name: string, code?: string): void {
66
+ this.dispatchEvent(new CustomEvent(name, { bubbles: true, detail: code ? { code } : {} }))
67
+ }
68
+ }
69
+
70
+ if (!customElements.get("mx-translate")) customElements.define("mx-translate", Translate)
71
+
72
+ declare global {
73
+ interface HTMLElementTagNameMap {
74
+ "mx-translate": Translate
75
+ }
76
+ }