@discourser/design-system 0.15.0 → 0.17.0

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 (229) hide show
  1. package/dist/chunk-2P7Z5PVP.cjs +2151 -0
  2. package/dist/chunk-2P7Z5PVP.cjs.map +1 -0
  3. package/dist/{chunk-QC44JPCA.cjs → chunk-PFWU7QSM.cjs} +777 -15
  4. package/dist/chunk-PFWU7QSM.cjs.map +1 -0
  5. package/dist/chunk-QC7LGFM3.js +2099 -0
  6. package/dist/chunk-QC7LGFM3.js.map +1 -0
  7. package/dist/{chunk-M7J7WKJY.js → chunk-SNUJBT5R.js} +778 -16
  8. package/dist/chunk-SNUJBT5R.js.map +1 -0
  9. package/dist/components/Accordion.figma.d.ts +2 -0
  10. package/dist/components/Accordion.figma.d.ts.map +1 -0
  11. package/dist/components/Breadcrumb.d.ts +11 -0
  12. package/dist/components/Breadcrumb.d.ts.map +1 -0
  13. package/dist/components/Breadcrumb.figma.d.ts +2 -0
  14. package/dist/components/Breadcrumb.figma.d.ts.map +1 -0
  15. package/dist/components/Checkbox.d.ts +6 -6
  16. package/dist/components/ContentCard/ContentCard.d.ts +13 -0
  17. package/dist/components/ContentCard/ContentCard.d.ts.map +1 -0
  18. package/dist/components/ContentCard/ContentCard.figma.d.ts +2 -0
  19. package/dist/components/ContentCard/ContentCard.figma.d.ts.map +1 -0
  20. package/dist/components/ContentCard/index.d.ts +2 -0
  21. package/dist/components/ContentCard/index.d.ts.map +1 -0
  22. package/dist/components/{Heading.d.ts → Header.d.ts} +3 -3
  23. package/dist/components/Header.d.ts.map +1 -0
  24. package/dist/components/Header.figma.d.ts +2 -0
  25. package/dist/components/Header.figma.d.ts.map +1 -0
  26. package/dist/components/Icons/AccountIcon.d.ts +6 -0
  27. package/dist/components/Icons/AccountIcon.d.ts.map +1 -0
  28. package/dist/components/Icons/ChevronUpIcon.d.ts +6 -0
  29. package/dist/components/Icons/ChevronUpIcon.d.ts.map +1 -0
  30. package/dist/components/Icons/ClockIcon.d.ts +6 -0
  31. package/dist/components/Icons/ClockIcon.d.ts.map +1 -0
  32. package/dist/components/Icons/DashboardIcon.d.ts +6 -0
  33. package/dist/components/Icons/DashboardIcon.d.ts.map +1 -0
  34. package/dist/components/Icons/DiscourserLogo.d.ts +6 -0
  35. package/dist/components/Icons/DiscourserLogo.d.ts.map +1 -0
  36. package/dist/components/Icons/DiscourserLogo.figma.d.ts +2 -0
  37. package/dist/components/Icons/DiscourserLogo.figma.d.ts.map +1 -0
  38. package/dist/components/Icons/GripDotsVerticalIcon.d.ts +6 -0
  39. package/dist/components/Icons/GripDotsVerticalIcon.d.ts.map +1 -0
  40. package/dist/components/Icons/HelpIcon.d.ts +6 -0
  41. package/dist/components/Icons/HelpIcon.d.ts.map +1 -0
  42. package/dist/components/Icons/NotebookIcon.d.ts +6 -0
  43. package/dist/components/Icons/NotebookIcon.d.ts.map +1 -0
  44. package/dist/components/Icons/RightArrowIcon.d.ts +6 -0
  45. package/dist/components/Icons/RightArrowIcon.d.ts.map +1 -0
  46. package/dist/components/Icons/ScenarioIcon.d.ts +6 -0
  47. package/dist/components/Icons/ScenarioIcon.d.ts.map +1 -0
  48. package/dist/components/Icons/index.d.ts +11 -0
  49. package/dist/components/Icons/index.d.ts.map +1 -0
  50. package/dist/components/NavigationMenu/NavigationMenu.d.ts +3 -0
  51. package/dist/components/NavigationMenu/NavigationMenu.d.ts.map +1 -0
  52. package/dist/components/NavigationMenu/NavigationMenu.figma.d.ts +2 -0
  53. package/dist/components/NavigationMenu/NavigationMenu.figma.d.ts.map +1 -0
  54. package/dist/components/NavigationMenu/index.d.ts +3 -0
  55. package/dist/components/NavigationMenu/index.d.ts.map +1 -0
  56. package/dist/components/NavigationMenu/types.d.ts +25 -0
  57. package/dist/components/NavigationMenu/types.d.ts.map +1 -0
  58. package/dist/components/QuickStartPage/QuickStartPage.d.ts +21 -0
  59. package/dist/components/QuickStartPage/QuickStartPage.d.ts.map +1 -0
  60. package/dist/components/QuickStartPage/index.d.ts +3 -0
  61. package/dist/components/QuickStartPage/index.d.ts.map +1 -0
  62. package/dist/components/ScenarioQueue/AddScenarioDialog.d.ts +16 -0
  63. package/dist/components/ScenarioQueue/AddScenarioDialog.d.ts.map +1 -0
  64. package/dist/components/ScenarioQueue/ScenarioCard.d.ts +10 -0
  65. package/dist/components/ScenarioQueue/ScenarioCard.d.ts.map +1 -0
  66. package/dist/components/ScenarioQueue/ScenarioCardDraggable.d.ts +15 -0
  67. package/dist/components/ScenarioQueue/ScenarioCardDraggable.d.ts.map +1 -0
  68. package/dist/components/ScenarioQueue/ScenarioQueue.d.ts +3 -0
  69. package/dist/components/ScenarioQueue/ScenarioQueue.d.ts.map +1 -0
  70. package/dist/components/ScenarioQueue/ScenarioQueue.figma.d.ts +2 -0
  71. package/dist/components/ScenarioQueue/ScenarioQueue.figma.d.ts.map +1 -0
  72. package/dist/components/ScenarioQueue/index.d.ts +6 -0
  73. package/dist/components/ScenarioQueue/index.d.ts.map +1 -0
  74. package/dist/components/ScenarioQueue/types.d.ts +56 -0
  75. package/dist/components/ScenarioQueue/types.d.ts.map +1 -0
  76. package/dist/components/ScenarioSettings/ScenarioSettings.d.ts +3 -0
  77. package/dist/components/ScenarioSettings/ScenarioSettings.d.ts.map +1 -0
  78. package/dist/components/ScenarioSettings/ScenarioSettings.figma.d.ts +2 -0
  79. package/dist/components/ScenarioSettings/ScenarioSettings.figma.d.ts.map +1 -0
  80. package/dist/components/ScenarioSettings/index.d.ts +3 -0
  81. package/dist/components/ScenarioSettings/index.d.ts.map +1 -0
  82. package/dist/components/ScenarioSettings/types.d.ts +54 -0
  83. package/dist/components/ScenarioSettings/types.d.ts.map +1 -0
  84. package/dist/components/index.cjs +110 -34
  85. package/dist/components/index.d.ts +16 -1
  86. package/dist/components/index.d.ts.map +1 -1
  87. package/dist/components/index.js +1 -1
  88. package/dist/figma-codex/config.d.ts +8 -0
  89. package/dist/figma-codex/config.d.ts.map +1 -0
  90. package/dist/figma-codex/fixtures/CompoundComponent/CompoundComponent.d.ts +6 -0
  91. package/dist/figma-codex/fixtures/CompoundComponent/CompoundComponent.d.ts.map +1 -0
  92. package/dist/figma-codex/fixtures/CompoundComponent/index.d.ts +2 -0
  93. package/dist/figma-codex/fixtures/CompoundComponent/index.d.ts.map +1 -0
  94. package/dist/figma-codex/fixtures/CompoundComponent.figma.d.ts +2 -0
  95. package/dist/figma-codex/fixtures/CompoundComponent.figma.d.ts.map +1 -0
  96. package/dist/figma-codex/fixtures/SimpleComponent.d.ts +8 -0
  97. package/dist/figma-codex/fixtures/SimpleComponent.d.ts.map +1 -0
  98. package/dist/figma-codex/fixtures/SimpleComponent.figma.d.ts +2 -0
  99. package/dist/figma-codex/fixtures/SimpleComponent.figma.d.ts.map +1 -0
  100. package/dist/figma-codex/generate.d.ts +6 -0
  101. package/dist/figma-codex/generate.d.ts.map +1 -0
  102. package/dist/figma-codex/parser.d.ts +18 -0
  103. package/dist/figma-codex/parser.d.ts.map +1 -0
  104. package/dist/figma-codex/resolver.d.ts +5 -0
  105. package/dist/figma-codex/resolver.d.ts.map +1 -0
  106. package/dist/figma-codex/schema.d.ts +60 -0
  107. package/dist/figma-codex/schema.d.ts.map +1 -0
  108. package/dist/figma-codex/writer.d.ts +8 -0
  109. package/dist/figma-codex/writer.d.ts.map +1 -0
  110. package/dist/figma-codex.json +373 -0
  111. package/dist/index.cjs +114 -38
  112. package/dist/index.js +2 -2
  113. package/dist/preset/index.cjs +2 -2
  114. package/dist/preset/index.d.ts.map +1 -1
  115. package/dist/preset/index.js +1 -1
  116. package/dist/preset/recipes/accordion.d.ts.map +1 -1
  117. package/dist/preset/recipes/avatar.d.ts.map +1 -1
  118. package/dist/preset/recipes/breadcrumb.d.ts +2 -0
  119. package/dist/preset/recipes/breadcrumb.d.ts.map +1 -0
  120. package/dist/preset/recipes/checkbox.d.ts.map +1 -1
  121. package/dist/preset/recipes/content-card.d.ts +2 -0
  122. package/dist/preset/recipes/content-card.d.ts.map +1 -0
  123. package/dist/preset/recipes/field.d.ts.map +1 -1
  124. package/dist/preset/recipes/index.d.ts +7 -0
  125. package/dist/preset/recipes/index.d.ts.map +1 -1
  126. package/dist/preset/recipes/navigation-menu.d.ts +2 -0
  127. package/dist/preset/recipes/navigation-menu.d.ts.map +1 -0
  128. package/dist/preset/recipes/progress.d.ts.map +1 -1
  129. package/dist/preset/recipes/radio-group.d.ts.map +1 -1
  130. package/dist/preset/recipes/scenario-card.d.ts +2 -0
  131. package/dist/preset/recipes/scenario-card.d.ts.map +1 -0
  132. package/dist/preset/recipes/scenario-queue.d.ts +2 -0
  133. package/dist/preset/recipes/scenario-queue.d.ts.map +1 -0
  134. package/dist/preset/recipes/scenario-settings.d.ts +2 -0
  135. package/dist/preset/recipes/scenario-settings.d.ts.map +1 -0
  136. package/dist/preset/recipes/steps.d.ts.map +1 -1
  137. package/dist/preset/recipes/toast.d.ts.map +1 -1
  138. package/dist/preset/recipes/tooltip.d.ts.map +1 -1
  139. package/dist/preset/semantic-tokens.d.ts +12 -0
  140. package/dist/preset/semantic-tokens.d.ts.map +1 -1
  141. package/package.json +35 -2
  142. package/src/components/Accordion.figma.tsx +20 -0
  143. package/src/components/Breadcrumb.figma.tsx +18 -0
  144. package/src/components/Breadcrumb.tsx +52 -0
  145. package/src/components/ContentCard/ContentCard.figma.tsx +21 -0
  146. package/src/components/ContentCard/ContentCard.test.tsx +197 -0
  147. package/src/components/ContentCard/ContentCard.tsx +19 -0
  148. package/src/components/ContentCard/index.ts +13 -0
  149. package/src/components/Header.figma.tsx +25 -0
  150. package/src/components/{Heading.tsx → Header.tsx} +2 -2
  151. package/src/components/Icons/AccountIcon.tsx +26 -0
  152. package/src/components/Icons/ChevronUpIcon.tsx +24 -0
  153. package/src/components/Icons/ClockIcon.tsx +40 -0
  154. package/src/components/Icons/DashboardIcon.tsx +47 -0
  155. package/src/components/Icons/Discourser-Logo.svg +14 -0
  156. package/src/components/Icons/DiscourserLogo.figma.tsx +10 -0
  157. package/src/components/Icons/DiscourserLogo.tsx +72 -0
  158. package/src/components/Icons/GripDotsVerticalIcon.tsx +26 -0
  159. package/src/components/Icons/HelpIcon.tsx +26 -0
  160. package/src/components/Icons/NotebookIcon.tsx +26 -0
  161. package/src/components/Icons/RightArrowIcon.tsx +23 -0
  162. package/src/components/Icons/ScenarioIcon.tsx +26 -0
  163. package/src/components/Icons/index.ts +13 -0
  164. package/src/components/NavigationMenu/NavigationMenu.figma.tsx +26 -0
  165. package/src/components/NavigationMenu/NavigationMenu.test.tsx +524 -0
  166. package/src/components/NavigationMenu/NavigationMenu.tsx +102 -0
  167. package/src/components/NavigationMenu/index.ts +2 -0
  168. package/src/components/NavigationMenu/types.ts +27 -0
  169. package/src/components/QuickStartPage/QuickStartPage.tsx +627 -0
  170. package/src/components/QuickStartPage/index.ts +2 -0
  171. package/src/components/ScenarioQueue/AddScenarioDialog.tsx +137 -0
  172. package/src/components/ScenarioQueue/ScenarioCard.tsx +120 -0
  173. package/src/components/ScenarioQueue/ScenarioCardDraggable.tsx +41 -0
  174. package/src/components/ScenarioQueue/ScenarioQueue.figma.tsx +37 -0
  175. package/src/components/ScenarioQueue/ScenarioQueue.test.tsx +398 -0
  176. package/src/components/ScenarioQueue/ScenarioQueue.tsx +162 -0
  177. package/src/components/ScenarioQueue/index.ts +11 -0
  178. package/src/components/ScenarioQueue/types.ts +86 -0
  179. package/src/components/ScenarioSettings/ScenarioSettings.figma.tsx +12 -0
  180. package/src/components/ScenarioSettings/ScenarioSettings.test.tsx +406 -0
  181. package/src/components/ScenarioSettings/ScenarioSettings.tsx +386 -0
  182. package/src/components/ScenarioSettings/index.ts +11 -0
  183. package/src/components/ScenarioSettings/types.ts +70 -0
  184. package/src/components/__tests__/Breadcrumb.test.tsx +94 -0
  185. package/src/components/index.ts +54 -1
  186. package/src/figma-codex/README.md +186 -0
  187. package/src/figma-codex/__tests__/config.test.ts +63 -0
  188. package/src/figma-codex/__tests__/generate.test.ts +78 -0
  189. package/src/figma-codex/__tests__/parser.test.ts +138 -0
  190. package/src/figma-codex/__tests__/resolver.test.ts +196 -0
  191. package/src/figma-codex/__tests__/writer.test.ts +111 -0
  192. package/src/figma-codex/config.ts +42 -0
  193. package/src/figma-codex/fixtures/CompoundComponent/CompoundComponent.tsx +17 -0
  194. package/src/figma-codex/fixtures/CompoundComponent/index.ts +1 -0
  195. package/src/figma-codex/fixtures/CompoundComponent.figma.tsx +14 -0
  196. package/src/figma-codex/fixtures/SimpleComponent.figma.tsx +10 -0
  197. package/src/figma-codex/fixtures/SimpleComponent.tsx +10 -0
  198. package/src/figma-codex/fixtures/expected-output.json +78 -0
  199. package/src/figma-codex/generate.ts +106 -0
  200. package/src/figma-codex/parser.ts +138 -0
  201. package/src/figma-codex/resolver.ts +280 -0
  202. package/src/figma-codex/schema.ts +79 -0
  203. package/src/figma-codex/writer.ts +54 -0
  204. package/src/preset/index.ts +15 -0
  205. package/src/preset/recipes/accordion.ts +8 -5
  206. package/src/preset/recipes/avatar.ts +1 -2
  207. package/src/preset/recipes/breadcrumb.ts +109 -0
  208. package/src/preset/recipes/checkbox.ts +1 -2
  209. package/src/preset/recipes/content-card.ts +124 -0
  210. package/src/preset/recipes/field.ts +1 -2
  211. package/src/preset/recipes/index.ts +11 -0
  212. package/src/preset/recipes/navigation-menu.ts +97 -0
  213. package/src/preset/recipes/progress.ts +1 -2
  214. package/src/preset/recipes/radio-group.ts +1 -2
  215. package/src/preset/recipes/scenario-card.ts +151 -0
  216. package/src/preset/recipes/scenario-queue.ts +99 -0
  217. package/src/preset/recipes/scenario-settings.ts +182 -0
  218. package/src/preset/recipes/steps.ts +1 -2
  219. package/src/preset/recipes/toast.ts +1 -2
  220. package/src/preset/recipes/tooltip.ts +1 -2
  221. package/src/preset/semantic-tokens.ts +4 -0
  222. package/src/test/setup.ts +15 -0
  223. package/dist/chunk-F7LHARS4.js +0 -869
  224. package/dist/chunk-F7LHARS4.js.map +0 -1
  225. package/dist/chunk-M7J7WKJY.js.map +0 -1
  226. package/dist/chunk-QC44JPCA.cjs.map +0 -1
  227. package/dist/chunk-QP4EJI3G.cjs +0 -902
  228. package/dist/chunk-QP4EJI3G.cjs.map +0 -1
  229. package/dist/components/Heading.d.ts.map +0 -1
@@ -0,0 +1,26 @@
1
+ import { ark } from '@ark-ui/react/factory';
2
+ import type { ComponentProps } from 'react';
3
+ import { styled } from 'styled-system/jsx';
4
+
5
+ const StyledSvg = styled(ark.svg);
6
+
7
+ export type HelpIconProps = ComponentProps<typeof StyledSvg>;
8
+
9
+ export const HelpIcon = (props: HelpIconProps) => (
10
+ <StyledSvg
11
+ viewBox="0 0 20 20"
12
+ fill="none"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ width="1em"
15
+ height="1em"
16
+ {...props}
17
+ >
18
+ <path
19
+ d="M9.16663 10H10.8333C11.2753 10 11.6992 9.82443 12.0118 9.51186C12.3244 9.1993 12.5 8.77538 12.5 8.33335C12.5 7.89133 12.3244 7.4674 12.0118 7.15484C11.6992 6.84228 11.2753 6.66669 10.8333 6.66669H8.33329C7.83329 6.66669 7.41663 6.83335 7.16663 7.16669L2.49996 11.6667M5.83329 15L7.16663 13.8334C7.41663 13.5 7.83329 13.3334 8.33329 13.3334H11.6666C12.5833 13.3334 13.4166 13 14 12.3334L17.8333 8.66671C18.1549 8.36282 18.3426 7.94362 18.3551 7.50135C18.3676 7.05908 18.2039 6.62995 17.9 6.30838C17.5961 5.9868 17.1769 5.79912 16.7346 5.78662C16.2923 5.77411 15.8632 5.93782 15.5416 6.24171L12.0416 9.49171M1.66663 10.8334L6.66663 15.8334"
20
+ stroke="currentColor"
21
+ strokeWidth="2"
22
+ strokeLinecap="round"
23
+ strokeLinejoin="round"
24
+ />
25
+ </StyledSvg>
26
+ );
@@ -0,0 +1,26 @@
1
+ import { ark } from '@ark-ui/react/factory';
2
+ import type { ComponentProps } from 'react';
3
+ import { styled } from 'styled-system/jsx';
4
+
5
+ const StyledSvg = styled(ark.svg);
6
+
7
+ export type NotebookIconProps = ComponentProps<typeof StyledSvg>;
8
+
9
+ export const NotebookIcon = (props: NotebookIconProps) => (
10
+ <StyledSvg
11
+ viewBox="0 0 24 24"
12
+ fill="none"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ width="1em"
15
+ height="1em"
16
+ {...props}
17
+ >
18
+ <path
19
+ d="M12 7.83333C12 6.94928 11.6488 6.10143 11.0236 5.47631C10.3985 4.85119 9.55068 4.5 8.66663 4.5H3.66663V17H9.49996C10.163 17 10.7989 17.2634 11.2677 17.7322C11.7366 18.2011 12 18.837 12 19.5M12 7.83333V19.5M12 7.83333C12 6.94928 12.3511 6.10143 12.9763 5.47631C13.6014 4.85119 14.4492 4.5 15.3333 4.5H20.3333V17H14.5C13.8369 17 13.201 17.2634 12.7322 17.7322C12.2634 18.2011 12 18.837 12 19.5M6.99996 8.66667H8.66663M6.99996 12H8.66663M15.3333 8.66667H17M15.3333 12H17"
20
+ stroke="currentColor"
21
+ strokeWidth="2"
22
+ strokeLinecap="round"
23
+ strokeLinejoin="round"
24
+ />
25
+ </StyledSvg>
26
+ );
@@ -0,0 +1,23 @@
1
+ import { ark } from '@ark-ui/react/factory';
2
+ import type { ComponentProps } from 'react';
3
+ import { styled } from 'styled-system/jsx';
4
+
5
+ const StyledSvg = styled(ark.svg);
6
+
7
+ export type RightArrowIconProps = ComponentProps<typeof StyledSvg>;
8
+
9
+ export const RightArrowIcon = (props: RightArrowIconProps) => (
10
+ <StyledSvg
11
+ viewBox="0 0 19 19"
12
+ fill="none"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ width="1em"
15
+ height="1em"
16
+ {...props}
17
+ >
18
+ <path
19
+ d="M15.9172 13.2266H3.74219V11.2266H15.9172L10.3172 5.62656L11.7422 4.22656L19.7422 12.2266L11.7422 20.2266L10.3172 18.8266L15.9172 13.2266Z"
20
+ fill="currentColor"
21
+ />
22
+ </StyledSvg>
23
+ );
@@ -0,0 +1,26 @@
1
+ import { ark } from '@ark-ui/react/factory';
2
+ import type { ComponentProps } from 'react';
3
+ import { styled } from 'styled-system/jsx';
4
+
5
+ const StyledSvg = styled(ark.svg);
6
+
7
+ export type ScenarioIconProps = ComponentProps<typeof StyledSvg>;
8
+
9
+ export const ScenarioIcon = (props: ScenarioIconProps) => (
10
+ <StyledSvg
11
+ viewBox="0 0 24 24"
12
+ fill="none"
13
+ xmlns="http://www.w3.org/2000/svg"
14
+ width="1em"
15
+ height="1em"
16
+ {...props}
17
+ >
18
+ <path
19
+ d="M19.5 14.5C19.5 14.942 19.3244 15.366 19.0118 15.6785C18.6993 15.9911 18.2754 16.1667 17.8333 16.1667H7.83333L4.5 19.5V6.16667C4.5 5.72464 4.67559 5.30072 4.98816 4.98816C5.30072 4.67559 5.72464 4.5 6.16667 4.5H17.8333C18.2754 4.5 18.6993 4.67559 19.0118 4.98816C19.3244 5.30072 19.5 5.72464 19.5 6.16667V14.5Z"
20
+ stroke="currentColor"
21
+ strokeWidth="2"
22
+ strokeLinecap="round"
23
+ strokeLinejoin="round"
24
+ />
25
+ </StyledSvg>
26
+ );
@@ -0,0 +1,13 @@
1
+ export {
2
+ GripDotsVerticalIcon,
3
+ type GripDotsVerticalIconProps,
4
+ } from './GripDotsVerticalIcon';
5
+ export { ClockIcon, type ClockIconProps } from './ClockIcon';
6
+ export { DashboardIcon, type DashboardIconProps } from './DashboardIcon';
7
+ export { NotebookIcon, type NotebookIconProps } from './NotebookIcon';
8
+ export { ScenarioIcon, type ScenarioIconProps } from './ScenarioIcon';
9
+ export { HelpIcon, type HelpIconProps } from './HelpIcon';
10
+ export { AccountIcon, type AccountIconProps } from './AccountIcon';
11
+ export { RightArrowIcon, type RightArrowIconProps } from './RightArrowIcon';
12
+ export { ChevronUpIcon, type ChevronUpIconProps } from './ChevronUpIcon';
13
+ export { DiscourserLogo, type DiscourserLogoProps } from './DiscourserLogo';
@@ -0,0 +1,26 @@
1
+ import figma from '@figma/code-connect';
2
+ import { NavigationMenu } from './NavigationMenu';
3
+
4
+ figma.connect(
5
+ NavigationMenu,
6
+ 'https://www.figma.com/design/GaHmFfmvO4loUzuZS4TgEz/Discourser.AI--V1?node-id=38-8485',
7
+ {
8
+ example: () => (
9
+ <NavigationMenu
10
+ sections={[
11
+ {
12
+ value: 'dashboard',
13
+ title: 'Dashboard',
14
+ icon: null,
15
+ items: [
16
+ { label: 'Quick Start', href: '/dashboard/quick-start' },
17
+ { label: 'Progress', href: '/dashboard/progress' },
18
+ ],
19
+ },
20
+ ]}
21
+ defaultOpenSections={['dashboard']}
22
+ activeHref="/dashboard/quick-start"
23
+ />
24
+ ),
25
+ },
26
+ );
@@ -0,0 +1,524 @@
1
+ /* global describe, it, expect, vi */
2
+ import React from 'react';
3
+ import { render, screen } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+ import { axe } from 'jest-axe';
6
+ import { NavigationMenu } from './NavigationMenu';
7
+ import type { NavSection } from './types';
8
+
9
+ // ── Mock icon ─────────────────────────────────────────────────────────────────
10
+ // Avoids importing real DS icon SVGs — keeps tests free of styled-system deps.
11
+
12
+ const MockIcon = () =>
13
+ React.createElement('svg', { 'data-testid': 'section-icon' });
14
+
15
+ // ── Fixtures ──────────────────────────────────────────────────────────────────
16
+ // Mirrors the mock data in NavigationMenu.stories.tsx
17
+
18
+ const MOCK_SECTIONS: NavSection[] = [
19
+ {
20
+ value: 'dashboard',
21
+ title: 'Dashboard',
22
+ icon: React.createElement(MockIcon),
23
+ items: [
24
+ { label: 'Quick Start', href: '/dashboard/quick-start' },
25
+ { label: 'Resume Session', href: '/dashboard/resume-session' },
26
+ { label: 'Progress', href: '/dashboard/progress' },
27
+ ],
28
+ },
29
+ {
30
+ value: 'scenarios',
31
+ title: 'Scenarios',
32
+ icon: React.createElement(MockIcon),
33
+ items: [
34
+ { label: 'MyQueue', href: '/scenarios/my-queue' },
35
+ { label: 'Conversation Studio', href: '/scenarios/conversation-studio' },
36
+ ],
37
+ },
38
+ {
39
+ value: 'help',
40
+ title: 'Help',
41
+ icon: React.createElement(MockIcon),
42
+ items: [
43
+ { label: 'How it Works', href: '/help/how-it-works' },
44
+ { label: 'Contact Support', href: '/help/contact-support' },
45
+ ],
46
+ },
47
+ ];
48
+
49
+ // ── Tests ─────────────────────────────────────────────────────────────────────
50
+
51
+ describe('NavigationMenu', () => {
52
+ // ── Rendering ───────────────────────────────────────────────────────────────
53
+
54
+ describe('Rendering', () => {
55
+ it('renders a <nav> with the provided aria-label', () => {
56
+ render(
57
+ <NavigationMenu
58
+ sections={MOCK_SECTIONS}
59
+ ariaLabel="Dashboard navigation"
60
+ />,
61
+ );
62
+
63
+ expect(
64
+ screen.getByRole('navigation', { name: 'Dashboard navigation' }),
65
+ ).toBeInTheDocument();
66
+ });
67
+
68
+ it('defaults ariaLabel to "Navigation" when not provided', () => {
69
+ render(<NavigationMenu sections={MOCK_SECTIONS} />);
70
+
71
+ expect(
72
+ screen.getByRole('navigation', { name: 'Navigation' }),
73
+ ).toBeInTheDocument();
74
+ });
75
+
76
+ it('renders one trigger button per section', () => {
77
+ render(<NavigationMenu sections={MOCK_SECTIONS} />);
78
+
79
+ expect(
80
+ screen.getByRole('button', { name: /Dashboard/i }),
81
+ ).toBeInTheDocument();
82
+ expect(
83
+ screen.getByRole('button', { name: /Scenarios/i }),
84
+ ).toBeInTheDocument();
85
+ expect(screen.getByRole('button', { name: /Help/i })).toBeInTheDocument();
86
+ });
87
+
88
+ it('renders icons for each section', () => {
89
+ render(<NavigationMenu sections={MOCK_SECTIONS} />);
90
+
91
+ expect(screen.getAllByTestId('section-icon')).toHaveLength(
92
+ MOCK_SECTIONS.length,
93
+ );
94
+ });
95
+
96
+ it('renders nav items for sections that are open by default', () => {
97
+ render(
98
+ <NavigationMenu
99
+ sections={MOCK_SECTIONS}
100
+ defaultOpenSections={['dashboard']}
101
+ />,
102
+ );
103
+
104
+ expect(
105
+ screen.getByRole('link', { name: 'Quick Start' }),
106
+ ).toBeInTheDocument();
107
+ expect(
108
+ screen.getByRole('link', { name: 'Resume Session' }),
109
+ ).toBeInTheDocument();
110
+ expect(
111
+ screen.getByRole('link', { name: 'Progress' }),
112
+ ).toBeInTheDocument();
113
+ });
114
+
115
+ it('does not render items for sections that are closed by default', () => {
116
+ render(
117
+ <NavigationMenu
118
+ sections={MOCK_SECTIONS}
119
+ defaultOpenSections={['dashboard']}
120
+ />,
121
+ );
122
+
123
+ // Scenarios section is closed — its items are not in the DOM
124
+ expect(
125
+ screen.queryByRole('link', { name: 'MyQueue' }),
126
+ ).not.toBeInTheDocument();
127
+ });
128
+ });
129
+
130
+ // ── Expand / Collapse ───────────────────────────────────────────────────────
131
+
132
+ describe('Expand / Collapse', () => {
133
+ it('closed section trigger has aria-expanded="false"', () => {
134
+ render(
135
+ <NavigationMenu sections={MOCK_SECTIONS} defaultOpenSections={[]} />,
136
+ );
137
+
138
+ const trigger = screen.getByRole('button', { name: /Dashboard/i });
139
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
140
+ });
141
+
142
+ it('open section trigger has aria-expanded="true"', () => {
143
+ render(
144
+ <NavigationMenu
145
+ sections={MOCK_SECTIONS}
146
+ defaultOpenSections={['dashboard']}
147
+ />,
148
+ );
149
+
150
+ const trigger = screen.getByRole('button', { name: /Dashboard/i });
151
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
152
+ });
153
+
154
+ it('clicking a closed section opens it and shows its items', async () => {
155
+ const user = userEvent.setup();
156
+ render(
157
+ <NavigationMenu sections={MOCK_SECTIONS} defaultOpenSections={[]} />,
158
+ );
159
+
160
+ const dashboardTrigger = screen.getByRole('button', {
161
+ name: /Dashboard/i,
162
+ });
163
+ await user.click(dashboardTrigger);
164
+
165
+ expect(dashboardTrigger).toHaveAttribute('aria-expanded', 'true');
166
+ expect(
167
+ screen.getByRole('link', { name: 'Quick Start' }),
168
+ ).toBeInTheDocument();
169
+ });
170
+
171
+ it('clicking an open section closes it', async () => {
172
+ const user = userEvent.setup();
173
+ render(
174
+ <NavigationMenu
175
+ sections={MOCK_SECTIONS}
176
+ defaultOpenSections={['dashboard']}
177
+ />,
178
+ );
179
+
180
+ const dashboardTrigger = screen.getByRole('button', {
181
+ name: /Dashboard/i,
182
+ });
183
+ await user.click(dashboardTrigger);
184
+
185
+ expect(dashboardTrigger).toHaveAttribute('aria-expanded', 'false');
186
+ });
187
+
188
+ it('multiple sections can be open simultaneously', async () => {
189
+ const user = userEvent.setup();
190
+ render(
191
+ <NavigationMenu
192
+ sections={MOCK_SECTIONS}
193
+ defaultOpenSections={['dashboard']}
194
+ />,
195
+ );
196
+
197
+ // Open Scenarios section too
198
+ await user.click(screen.getByRole('button', { name: /Scenarios/i }));
199
+
200
+ expect(
201
+ screen.getByRole('button', { name: /Dashboard/i }),
202
+ ).toHaveAttribute('aria-expanded', 'true');
203
+ expect(
204
+ screen.getByRole('button', { name: /Scenarios/i }),
205
+ ).toHaveAttribute('aria-expanded', 'true');
206
+ expect(
207
+ screen.getByRole('link', { name: 'Quick Start' }),
208
+ ).toBeInTheDocument();
209
+ expect(screen.getByRole('link', { name: 'MyQueue' })).toBeInTheDocument();
210
+ });
211
+
212
+ it('all sections expand correctly when defaultOpenSections covers all values', () => {
213
+ render(
214
+ <NavigationMenu
215
+ sections={MOCK_SECTIONS}
216
+ defaultOpenSections={['dashboard', 'scenarios', 'help']}
217
+ />,
218
+ );
219
+
220
+ expect(
221
+ screen.getByRole('link', { name: 'Quick Start' }),
222
+ ).toBeInTheDocument();
223
+ expect(screen.getByRole('link', { name: 'MyQueue' })).toBeInTheDocument();
224
+ expect(
225
+ screen.getByRole('link', { name: 'How it Works' }),
226
+ ).toBeInTheDocument();
227
+ });
228
+ });
229
+
230
+ // ── Active Item ─────────────────────────────────────────────────────────────
231
+
232
+ describe('Active Item', () => {
233
+ it('active link has aria-current="page"', () => {
234
+ render(
235
+ <NavigationMenu
236
+ sections={MOCK_SECTIONS}
237
+ defaultOpenSections={['dashboard']}
238
+ activeHref="/dashboard/quick-start"
239
+ />,
240
+ );
241
+
242
+ const activeLink = screen.getByRole('link', { name: 'Quick Start' });
243
+ expect(activeLink).toHaveAttribute('aria-current', 'page');
244
+ });
245
+
246
+ it('active link has data-active="true"', () => {
247
+ render(
248
+ <NavigationMenu
249
+ sections={MOCK_SECTIONS}
250
+ defaultOpenSections={['dashboard']}
251
+ activeHref="/dashboard/quick-start"
252
+ />,
253
+ );
254
+
255
+ const activeLink = screen.getByRole('link', { name: 'Quick Start' });
256
+ expect(activeLink).toHaveAttribute('data-active', 'true');
257
+ });
258
+
259
+ it('non-active links do not have aria-current', () => {
260
+ render(
261
+ <NavigationMenu
262
+ sections={MOCK_SECTIONS}
263
+ defaultOpenSections={['dashboard']}
264
+ activeHref="/dashboard/quick-start"
265
+ />,
266
+ );
267
+
268
+ const inactiveLink = screen.getByRole('link', { name: 'Resume Session' });
269
+ expect(inactiveLink).not.toHaveAttribute('aria-current');
270
+ });
271
+
272
+ it('non-active links do not have data-active', () => {
273
+ render(
274
+ <NavigationMenu
275
+ sections={MOCK_SECTIONS}
276
+ defaultOpenSections={['dashboard']}
277
+ activeHref="/dashboard/quick-start"
278
+ />,
279
+ );
280
+
281
+ const inactiveLink = screen.getByRole('link', { name: 'Resume Session' });
282
+ expect(inactiveLink).not.toHaveAttribute('data-active');
283
+ });
284
+
285
+ it('only the matching href is marked active', () => {
286
+ render(
287
+ <NavigationMenu
288
+ sections={MOCK_SECTIONS}
289
+ defaultOpenSections={['dashboard', 'scenarios']}
290
+ activeHref="/dashboard/progress"
291
+ />,
292
+ );
293
+
294
+ const links = screen.getAllByRole('link');
295
+ const activeLinks = links.filter((l) => l.hasAttribute('aria-current'));
296
+ expect(activeLinks).toHaveLength(1);
297
+ expect(activeLinks[0]).toHaveAccessibleName('Progress');
298
+ });
299
+
300
+ it('no link is marked active when activeHref does not match any item', () => {
301
+ render(
302
+ <NavigationMenu
303
+ sections={MOCK_SECTIONS}
304
+ defaultOpenSections={['dashboard']}
305
+ activeHref="/unmatched/path"
306
+ />,
307
+ );
308
+
309
+ const links = screen.getAllByRole('link');
310
+ for (const link of links) {
311
+ expect(link).not.toHaveAttribute('aria-current');
312
+ }
313
+ });
314
+ });
315
+
316
+ // ── Navigation (onNavigate) ─────────────────────────────────────────────────
317
+
318
+ describe('Navigation', () => {
319
+ it('clicking a nav item calls onNavigate with the correct href', async () => {
320
+ const user = userEvent.setup();
321
+ const onNavigate = vi.fn();
322
+
323
+ render(
324
+ <NavigationMenu
325
+ sections={MOCK_SECTIONS}
326
+ defaultOpenSections={['dashboard']}
327
+ onNavigate={onNavigate}
328
+ />,
329
+ );
330
+
331
+ await user.click(screen.getByRole('link', { name: 'Quick Start' }));
332
+
333
+ expect(onNavigate).toHaveBeenCalledWith('/dashboard/quick-start');
334
+ expect(onNavigate).toHaveBeenCalledTimes(1);
335
+ });
336
+
337
+ it("clicking different items calls onNavigate with each item's href", async () => {
338
+ const user = userEvent.setup();
339
+ const onNavigate = vi.fn();
340
+
341
+ render(
342
+ <NavigationMenu
343
+ sections={MOCK_SECTIONS}
344
+ defaultOpenSections={['dashboard']}
345
+ onNavigate={onNavigate}
346
+ />,
347
+ );
348
+
349
+ await user.click(screen.getByRole('link', { name: 'Quick Start' }));
350
+ await user.click(screen.getByRole('link', { name: 'Resume Session' }));
351
+
352
+ expect(onNavigate).toHaveBeenNthCalledWith(1, '/dashboard/quick-start');
353
+ expect(onNavigate).toHaveBeenNthCalledWith(
354
+ 2,
355
+ '/dashboard/resume-session',
356
+ );
357
+ });
358
+
359
+ it('default link href attribute matches the nav item href', () => {
360
+ render(
361
+ <NavigationMenu
362
+ sections={MOCK_SECTIONS}
363
+ defaultOpenSections={['dashboard']}
364
+ />,
365
+ );
366
+
367
+ const link = screen.getByRole('link', { name: 'Quick Start' });
368
+ expect(link).toHaveAttribute('href', '/dashboard/quick-start');
369
+ });
370
+ });
371
+
372
+ // ── Custom Link Renderer ────────────────────────────────────────────────────
373
+
374
+ describe('Custom Link Renderer', () => {
375
+ it('renders the element returned by renderLink', () => {
376
+ render(
377
+ <NavigationMenu
378
+ sections={MOCK_SECTIONS}
379
+ defaultOpenSections={['dashboard']}
380
+ renderLink={({ href, children, className }) =>
381
+ React.createElement(
382
+ 'a',
383
+ { href, className, 'data-custom': 'true' },
384
+ children,
385
+ )
386
+ }
387
+ />,
388
+ );
389
+
390
+ const link = screen.getByRole('link', { name: 'Quick Start' });
391
+ expect(link).toHaveAttribute('data-custom', 'true');
392
+ });
393
+
394
+ it('passes isActive=true to renderLink for the matching href', () => {
395
+ const renderLink = vi.fn(
396
+ ({
397
+ href,
398
+ children,
399
+ }: {
400
+ href: string;
401
+ children: React.ReactNode;
402
+ isActive: boolean;
403
+ className: string;
404
+ }) => React.createElement('a', { href }, children),
405
+ );
406
+
407
+ render(
408
+ <NavigationMenu
409
+ sections={MOCK_SECTIONS}
410
+ defaultOpenSections={['dashboard']}
411
+ activeHref="/dashboard/quick-start"
412
+ renderLink={renderLink}
413
+ />,
414
+ );
415
+
416
+ const quickStartCall = renderLink.mock.calls.find(
417
+ ([props]) => props.href === '/dashboard/quick-start',
418
+ );
419
+ expect(quickStartCall?.[0].isActive).toBe(true);
420
+ });
421
+
422
+ it('passes isActive=false to renderLink for non-matching hrefs', () => {
423
+ const renderLink = vi.fn(
424
+ ({
425
+ href,
426
+ children,
427
+ }: {
428
+ href: string;
429
+ children: React.ReactNode;
430
+ isActive: boolean;
431
+ className: string;
432
+ }) => React.createElement('a', { href }, children),
433
+ );
434
+
435
+ render(
436
+ <NavigationMenu
437
+ sections={MOCK_SECTIONS}
438
+ defaultOpenSections={['dashboard']}
439
+ activeHref="/dashboard/quick-start"
440
+ renderLink={renderLink}
441
+ />,
442
+ );
443
+
444
+ const resumeCall = renderLink.mock.calls.find(
445
+ ([props]) => props.href === '/dashboard/resume-session',
446
+ );
447
+ expect(resumeCall?.[0].isActive).toBe(false);
448
+ });
449
+
450
+ it('passes the recipe className string to renderLink', () => {
451
+ let receivedClassName = '';
452
+
453
+ render(
454
+ <NavigationMenu
455
+ sections={MOCK_SECTIONS}
456
+ defaultOpenSections={['dashboard']}
457
+ renderLink={({ href, children, className }) => {
458
+ receivedClassName = className;
459
+ return React.createElement('a', { href }, children);
460
+ }}
461
+ />,
462
+ );
463
+
464
+ expect(typeof receivedClassName).toBe('string');
465
+ expect(receivedClassName.length).toBeGreaterThan(0);
466
+ });
467
+ });
468
+
469
+ // ── Accessibility ───────────────────────────────────────────────────────────
470
+
471
+ describe('Accessibility', () => {
472
+ it('passes axe audit with all sections open', async () => {
473
+ const { container } = render(
474
+ <NavigationMenu
475
+ sections={MOCK_SECTIONS}
476
+ defaultOpenSections={['dashboard', 'scenarios', 'help']}
477
+ activeHref="/dashboard/quick-start"
478
+ ariaLabel="Dashboard navigation"
479
+ />,
480
+ );
481
+
482
+ const results = await axe(container);
483
+ expect(results).toHaveNoViolations();
484
+ });
485
+
486
+ it('passes axe audit with all sections closed', async () => {
487
+ const { container } = render(
488
+ <NavigationMenu
489
+ sections={MOCK_SECTIONS}
490
+ defaultOpenSections={[]}
491
+ ariaLabel="Dashboard navigation"
492
+ />,
493
+ );
494
+
495
+ const results = await axe(container);
496
+ expect(results).toHaveNoViolations();
497
+ });
498
+
499
+ it('section triggers are keyboard-focusable buttons', () => {
500
+ render(<NavigationMenu sections={MOCK_SECTIONS} />);
501
+
502
+ const buttons = screen.getAllByRole('button');
503
+ expect(buttons).toHaveLength(MOCK_SECTIONS.length);
504
+ for (const btn of buttons) {
505
+ expect(btn.tagName).toBe('BUTTON');
506
+ }
507
+ });
508
+
509
+ it('nav items are rendered as links', () => {
510
+ render(
511
+ <NavigationMenu
512
+ sections={MOCK_SECTIONS}
513
+ defaultOpenSections={['dashboard']}
514
+ />,
515
+ );
516
+
517
+ const links = screen.getAllByRole('link');
518
+ expect(links.length).toBeGreaterThan(0);
519
+ for (const link of links) {
520
+ expect(link.tagName).toBe('A');
521
+ }
522
+ });
523
+ });
524
+ });