@discourser/design-system 0.15.1 → 0.18.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 (281) hide show
  1. package/dist/{chunk-UNWXE6UB.cjs → chunk-2P7Z5PVP.cjs} +817 -16
  2. package/dist/chunk-2P7Z5PVP.cjs.map +1 -0
  3. package/dist/{chunk-ABC7N32K.cjs → chunk-PFWU7QSM.cjs} +464 -8
  4. package/dist/chunk-PFWU7QSM.cjs.map +1 -0
  5. package/dist/{chunk-GD6Q2FUE.js → chunk-QC7LGFM3.js} +808 -18
  6. package/dist/chunk-QC7LGFM3.js.map +1 -0
  7. package/dist/{chunk-SBKRSXSZ.js → chunk-SNUJBT5R.js} +464 -8
  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 +2 -0
  12. package/dist/components/Breadcrumb.d.ts.map +1 -1
  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/ContentCard/ContentCard.d.ts +13 -0
  16. package/dist/components/ContentCard/ContentCard.d.ts.map +1 -0
  17. package/dist/components/ContentCard/ContentCard.figma.d.ts +2 -0
  18. package/dist/components/ContentCard/ContentCard.figma.d.ts.map +1 -0
  19. package/dist/components/ContentCard/index.d.ts +2 -0
  20. package/dist/components/ContentCard/index.d.ts.map +1 -0
  21. package/dist/components/{Heading.d.ts → Header.d.ts} +3 -3
  22. package/dist/components/Header.d.ts.map +1 -0
  23. package/dist/components/Header.figma.d.ts +2 -0
  24. package/dist/components/Header.figma.d.ts.map +1 -0
  25. package/dist/components/Icons/AccountIcon.d.ts +6 -0
  26. package/dist/components/Icons/AccountIcon.d.ts.map +1 -0
  27. package/dist/components/Icons/AudienceIcon.d.ts +6 -0
  28. package/dist/components/Icons/AudienceIcon.d.ts.map +1 -0
  29. package/dist/components/Icons/AudienceIcon.figma.d.ts +2 -0
  30. package/dist/components/Icons/AudienceIcon.figma.d.ts.map +1 -0
  31. package/dist/components/Icons/AudioSpeakerIcon.d.ts +6 -0
  32. package/dist/components/Icons/AudioSpeakerIcon.d.ts.map +1 -0
  33. package/dist/components/Icons/AudioSpeakerIcon.figma.d.ts +2 -0
  34. package/dist/components/Icons/AudioSpeakerIcon.figma.d.ts.map +1 -0
  35. package/dist/components/Icons/BookmarkPlusIcon.d.ts +6 -0
  36. package/dist/components/Icons/BookmarkPlusIcon.d.ts.map +1 -0
  37. package/dist/components/Icons/BookmarkPlusIcon.figma.d.ts +2 -0
  38. package/dist/components/Icons/BookmarkPlusIcon.figma.d.ts.map +1 -0
  39. package/dist/components/Icons/ChevronUpIcon.d.ts +6 -0
  40. package/dist/components/Icons/ChevronUpIcon.d.ts.map +1 -0
  41. package/dist/components/Icons/ClipBoardIcon.d.ts +6 -0
  42. package/dist/components/Icons/ClipBoardIcon.d.ts.map +1 -0
  43. package/dist/components/Icons/ClipBoardIcon.figma.d.ts +2 -0
  44. package/dist/components/Icons/ClipBoardIcon.figma.d.ts.map +1 -0
  45. package/dist/components/Icons/ClockIcon.d.ts.map +1 -1
  46. package/dist/components/Icons/DashboardIcon.d.ts +6 -0
  47. package/dist/components/Icons/DashboardIcon.d.ts.map +1 -0
  48. package/dist/components/Icons/DiscourserLogo.d.ts +6 -0
  49. package/dist/components/Icons/DiscourserLogo.d.ts.map +1 -0
  50. package/dist/components/Icons/DiscourserLogo.figma.d.ts +2 -0
  51. package/dist/components/Icons/DiscourserLogo.figma.d.ts.map +1 -0
  52. package/dist/components/Icons/ExitStudioIcon.d.ts +6 -0
  53. package/dist/components/Icons/ExitStudioIcon.d.ts.map +1 -0
  54. package/dist/components/Icons/ExitStudioIcon.figma.d.ts +2 -0
  55. package/dist/components/Icons/ExitStudioIcon.figma.d.ts.map +1 -0
  56. package/dist/components/Icons/GripDotsVerticalIcon.d.ts.map +1 -1
  57. package/dist/components/Icons/HelpIcon.d.ts +6 -0
  58. package/dist/components/Icons/HelpIcon.d.ts.map +1 -0
  59. package/dist/components/Icons/MicrophoneIcon.d.ts +6 -0
  60. package/dist/components/Icons/MicrophoneIcon.d.ts.map +1 -0
  61. package/dist/components/Icons/MicrophoneIcon.figma.d.ts +2 -0
  62. package/dist/components/Icons/MicrophoneIcon.figma.d.ts.map +1 -0
  63. package/dist/components/Icons/NotebookIcon.d.ts +6 -0
  64. package/dist/components/Icons/NotebookIcon.d.ts.map +1 -0
  65. package/dist/components/Icons/NotebookPenIcon.d.ts +6 -0
  66. package/dist/components/Icons/NotebookPenIcon.d.ts.map +1 -0
  67. package/dist/components/Icons/NotebookPenIcon.figma.d.ts +2 -0
  68. package/dist/components/Icons/NotebookPenIcon.figma.d.ts.map +1 -0
  69. package/dist/components/Icons/PausePlayIcon.d.ts +6 -0
  70. package/dist/components/Icons/PausePlayIcon.d.ts.map +1 -0
  71. package/dist/components/Icons/PausePlayIcon.figma.d.ts +2 -0
  72. package/dist/components/Icons/PausePlayIcon.figma.d.ts.map +1 -0
  73. package/dist/components/Icons/PlayIcon.d.ts +6 -0
  74. package/dist/components/Icons/PlayIcon.d.ts.map +1 -0
  75. package/dist/components/Icons/PlayIcon.figma.d.ts +2 -0
  76. package/dist/components/Icons/PlayIcon.figma.d.ts.map +1 -0
  77. package/dist/components/Icons/RecordIcon.d.ts +6 -0
  78. package/dist/components/Icons/RecordIcon.d.ts.map +1 -0
  79. package/dist/components/Icons/RecordIcon.figma.d.ts +2 -0
  80. package/dist/components/Icons/RecordIcon.figma.d.ts.map +1 -0
  81. package/dist/components/Icons/RepeatQuestionIcon.d.ts +6 -0
  82. package/dist/components/Icons/RepeatQuestionIcon.d.ts.map +1 -0
  83. package/dist/components/Icons/RepeatQuestionIcon.figma.d.ts +2 -0
  84. package/dist/components/Icons/RepeatQuestionIcon.figma.d.ts.map +1 -0
  85. package/dist/components/Icons/RightArrowIcon.d.ts +6 -0
  86. package/dist/components/Icons/RightArrowIcon.d.ts.map +1 -0
  87. package/dist/components/Icons/ScenarioIcon.d.ts +6 -0
  88. package/dist/components/Icons/ScenarioIcon.d.ts.map +1 -0
  89. package/dist/components/Icons/ScrollTextIcon.d.ts +6 -0
  90. package/dist/components/Icons/ScrollTextIcon.d.ts.map +1 -0
  91. package/dist/components/Icons/ScrollTextIcon.figma.d.ts +2 -0
  92. package/dist/components/Icons/ScrollTextIcon.figma.d.ts.map +1 -0
  93. package/dist/components/Icons/SparklesIcon.d.ts +6 -0
  94. package/dist/components/Icons/SparklesIcon.d.ts.map +1 -0
  95. package/dist/components/Icons/SparklesIcon.figma.d.ts +2 -0
  96. package/dist/components/Icons/SparklesIcon.figma.d.ts.map +1 -0
  97. package/dist/components/Icons/SpeechIcon.d.ts +6 -0
  98. package/dist/components/Icons/SpeechIcon.d.ts.map +1 -0
  99. package/dist/components/Icons/SpeechIcon.figma.d.ts +2 -0
  100. package/dist/components/Icons/SpeechIcon.figma.d.ts.map +1 -0
  101. package/dist/components/Icons/StopPlayIcon.d.ts +6 -0
  102. package/dist/components/Icons/StopPlayIcon.d.ts.map +1 -0
  103. package/dist/components/Icons/StopPlayIcon.figma.d.ts +2 -0
  104. package/dist/components/Icons/StopPlayIcon.figma.d.ts.map +1 -0
  105. package/dist/components/Icons/TimerIcon.d.ts +6 -0
  106. package/dist/components/Icons/TimerIcon.d.ts.map +1 -0
  107. package/dist/components/Icons/TimerIcon.figma.d.ts +2 -0
  108. package/dist/components/Icons/TimerIcon.figma.d.ts.map +1 -0
  109. package/dist/components/Icons/UserProfileIcon.d.ts +6 -0
  110. package/dist/components/Icons/UserProfileIcon.d.ts.map +1 -0
  111. package/dist/components/Icons/UserProfileIcon.figma.d.ts +2 -0
  112. package/dist/components/Icons/UserProfileIcon.figma.d.ts.map +1 -0
  113. package/dist/components/Icons/index.d.ts +26 -1
  114. package/dist/components/Icons/index.d.ts.map +1 -1
  115. package/dist/components/NavigationMenu/NavigationMenu.d.ts +3 -0
  116. package/dist/components/NavigationMenu/NavigationMenu.d.ts.map +1 -0
  117. package/dist/components/NavigationMenu/NavigationMenu.figma.d.ts +2 -0
  118. package/dist/components/NavigationMenu/NavigationMenu.figma.d.ts.map +1 -0
  119. package/dist/components/NavigationMenu/index.d.ts +3 -0
  120. package/dist/components/NavigationMenu/index.d.ts.map +1 -0
  121. package/dist/components/NavigationMenu/types.d.ts +25 -0
  122. package/dist/components/NavigationMenu/types.d.ts.map +1 -0
  123. package/dist/components/QuickStartPage/QuickStartPage.d.ts +21 -0
  124. package/dist/components/QuickStartPage/QuickStartPage.d.ts.map +1 -0
  125. package/dist/components/QuickStartPage/index.d.ts +3 -0
  126. package/dist/components/QuickStartPage/index.d.ts.map +1 -0
  127. package/dist/components/ScenarioQueue/ScenarioQueue.figma.d.ts +2 -0
  128. package/dist/components/ScenarioQueue/ScenarioQueue.figma.d.ts.map +1 -0
  129. package/dist/components/ScenarioSettings/ScenarioSettings.d.ts +3 -0
  130. package/dist/components/ScenarioSettings/ScenarioSettings.d.ts.map +1 -0
  131. package/dist/components/ScenarioSettings/ScenarioSettings.figma.d.ts +2 -0
  132. package/dist/components/ScenarioSettings/ScenarioSettings.figma.d.ts.map +1 -0
  133. package/dist/components/ScenarioSettings/index.d.ts +3 -0
  134. package/dist/components/ScenarioSettings/index.d.ts.map +1 -0
  135. package/dist/components/ScenarioSettings/types.d.ts +54 -0
  136. package/dist/components/ScenarioSettings/types.d.ts.map +1 -0
  137. package/dist/components/index.cjs +86 -42
  138. package/dist/components/index.d.ts +14 -3
  139. package/dist/components/index.d.ts.map +1 -1
  140. package/dist/components/index.js +1 -1
  141. package/dist/figma-codex/config.d.ts +8 -0
  142. package/dist/figma-codex/config.d.ts.map +1 -0
  143. package/dist/figma-codex/fixtures/CompoundComponent/CompoundComponent.d.ts +6 -0
  144. package/dist/figma-codex/fixtures/CompoundComponent/CompoundComponent.d.ts.map +1 -0
  145. package/dist/figma-codex/fixtures/CompoundComponent/index.d.ts +2 -0
  146. package/dist/figma-codex/fixtures/CompoundComponent/index.d.ts.map +1 -0
  147. package/dist/figma-codex/fixtures/CompoundComponent.figma.d.ts +2 -0
  148. package/dist/figma-codex/fixtures/CompoundComponent.figma.d.ts.map +1 -0
  149. package/dist/figma-codex/fixtures/SimpleComponent.d.ts +8 -0
  150. package/dist/figma-codex/fixtures/SimpleComponent.d.ts.map +1 -0
  151. package/dist/figma-codex/fixtures/SimpleComponent.figma.d.ts +2 -0
  152. package/dist/figma-codex/fixtures/SimpleComponent.figma.d.ts.map +1 -0
  153. package/dist/figma-codex/generate.d.ts +6 -0
  154. package/dist/figma-codex/generate.d.ts.map +1 -0
  155. package/dist/figma-codex/parser.d.ts +18 -0
  156. package/dist/figma-codex/parser.d.ts.map +1 -0
  157. package/dist/figma-codex/resolver.d.ts +5 -0
  158. package/dist/figma-codex/resolver.d.ts.map +1 -0
  159. package/dist/figma-codex/schema.d.ts +60 -0
  160. package/dist/figma-codex/schema.d.ts.map +1 -0
  161. package/dist/figma-codex/writer.d.ts +8 -0
  162. package/dist/figma-codex/writer.d.ts.map +1 -0
  163. package/dist/figma-codex.json +679 -0
  164. package/dist/index.cjs +90 -46
  165. package/dist/index.js +2 -2
  166. package/dist/preset/index.cjs +2 -2
  167. package/dist/preset/index.d.ts.map +1 -1
  168. package/dist/preset/index.js +1 -1
  169. package/dist/preset/recipes/accordion.d.ts.map +1 -1
  170. package/dist/preset/recipes/breadcrumb.d.ts.map +1 -1
  171. package/dist/preset/recipes/content-card.d.ts +2 -0
  172. package/dist/preset/recipes/content-card.d.ts.map +1 -0
  173. package/dist/preset/recipes/index.d.ts +4 -0
  174. package/dist/preset/recipes/index.d.ts.map +1 -1
  175. package/dist/preset/recipes/navigation-menu.d.ts +2 -0
  176. package/dist/preset/recipes/navigation-menu.d.ts.map +1 -0
  177. package/dist/preset/recipes/scenario-settings.d.ts +2 -0
  178. package/dist/preset/recipes/scenario-settings.d.ts.map +1 -0
  179. package/package.json +26 -2
  180. package/src/components/Accordion.figma.tsx +20 -0
  181. package/src/components/Breadcrumb.figma.tsx +18 -0
  182. package/src/components/Breadcrumb.tsx +33 -15
  183. package/src/components/ContentCard/ContentCard.figma.tsx +21 -0
  184. package/src/components/ContentCard/ContentCard.test.tsx +197 -0
  185. package/src/components/ContentCard/ContentCard.tsx +19 -0
  186. package/src/components/ContentCard/index.ts +13 -0
  187. package/src/components/Header.figma.tsx +25 -0
  188. package/src/components/{Heading.tsx → Header.tsx} +2 -2
  189. package/src/components/Icons/AccountIcon.tsx +26 -0
  190. package/src/components/Icons/AudienceIcon.figma.tsx +10 -0
  191. package/src/components/Icons/AudienceIcon.tsx +20 -0
  192. package/src/components/Icons/AudioSpeakerIcon.figma.tsx +10 -0
  193. package/src/components/Icons/AudioSpeakerIcon.tsx +26 -0
  194. package/src/components/Icons/BookmarkPlusIcon.figma.tsx +10 -0
  195. package/src/components/Icons/BookmarkPlusIcon.tsx +26 -0
  196. package/src/components/Icons/ChevronUpIcon.tsx +24 -0
  197. package/src/components/Icons/ClipBoardIcon.figma.tsx +10 -0
  198. package/src/components/Icons/ClipBoardIcon.tsx +61 -0
  199. package/src/components/Icons/ClockIcon.tsx +6 -6
  200. package/src/components/Icons/DashboardIcon.tsx +47 -0
  201. package/src/components/Icons/Discourser-Logo.svg +14 -0
  202. package/src/components/Icons/DiscourserLogo.figma.tsx +10 -0
  203. package/src/components/Icons/DiscourserLogo.tsx +72 -0
  204. package/src/components/Icons/ExitStudioIcon.figma.tsx +10 -0
  205. package/src/components/Icons/ExitStudioIcon.tsx +34 -0
  206. package/src/components/Icons/GripDotsVerticalIcon.tsx +6 -6
  207. package/src/components/Icons/HelpIcon.tsx +26 -0
  208. package/src/components/Icons/MicrophoneIcon.figma.tsx +10 -0
  209. package/src/components/Icons/MicrophoneIcon.tsx +40 -0
  210. package/src/components/Icons/NotebookIcon.tsx +26 -0
  211. package/src/components/Icons/NotebookPenIcon.figma.tsx +10 -0
  212. package/src/components/Icons/NotebookPenIcon.tsx +26 -0
  213. package/src/components/Icons/PausePlayIcon.figma.tsx +10 -0
  214. package/src/components/Icons/PausePlayIcon.tsx +41 -0
  215. package/src/components/Icons/PlayIcon.figma.tsx +10 -0
  216. package/src/components/Icons/PlayIcon.tsx +33 -0
  217. package/src/components/Icons/RecordIcon.figma.tsx +10 -0
  218. package/src/components/Icons/RecordIcon.tsx +41 -0
  219. package/src/components/Icons/RepeatQuestionIcon.figma.tsx +10 -0
  220. package/src/components/Icons/RepeatQuestionIcon.tsx +26 -0
  221. package/src/components/Icons/RightArrowIcon.tsx +23 -0
  222. package/src/components/Icons/ScenarioIcon.tsx +26 -0
  223. package/src/components/Icons/ScrollTextIcon.figma.tsx +10 -0
  224. package/src/components/Icons/ScrollTextIcon.tsx +26 -0
  225. package/src/components/Icons/SparklesIcon.figma.tsx +10 -0
  226. package/src/components/Icons/SparklesIcon.tsx +26 -0
  227. package/src/components/Icons/SpeechIcon.figma.tsx +10 -0
  228. package/src/components/Icons/SpeechIcon.tsx +26 -0
  229. package/src/components/Icons/StopPlayIcon.figma.tsx +10 -0
  230. package/src/components/Icons/StopPlayIcon.tsx +35 -0
  231. package/src/components/Icons/TimerIcon.figma.tsx +10 -0
  232. package/src/components/Icons/TimerIcon.tsx +26 -0
  233. package/src/components/Icons/UserProfileIcon.figma.tsx +10 -0
  234. package/src/components/Icons/UserProfileIcon.tsx +26 -0
  235. package/src/components/Icons/index.ts +39 -2
  236. package/src/components/NavigationMenu/NavigationMenu.figma.tsx +26 -0
  237. package/src/components/NavigationMenu/NavigationMenu.test.tsx +524 -0
  238. package/src/components/NavigationMenu/NavigationMenu.tsx +102 -0
  239. package/src/components/NavigationMenu/index.ts +2 -0
  240. package/src/components/NavigationMenu/types.ts +27 -0
  241. package/src/components/QuickStartPage/QuickStartPage.tsx +627 -0
  242. package/src/components/QuickStartPage/index.ts +2 -0
  243. package/src/components/ScenarioQueue/ScenarioQueue.figma.tsx +37 -0
  244. package/src/components/ScenarioSettings/ScenarioSettings.figma.tsx +12 -0
  245. package/src/components/ScenarioSettings/ScenarioSettings.test.tsx +406 -0
  246. package/src/components/ScenarioSettings/ScenarioSettings.tsx +386 -0
  247. package/src/components/ScenarioSettings/index.ts +11 -0
  248. package/src/components/ScenarioSettings/types.ts +70 -0
  249. package/src/components/__tests__/Breadcrumb.test.tsx +94 -0
  250. package/src/components/index.ts +38 -4
  251. package/src/figma-codex/README.md +186 -0
  252. package/src/figma-codex/__tests__/config.test.ts +63 -0
  253. package/src/figma-codex/__tests__/generate.test.ts +78 -0
  254. package/src/figma-codex/__tests__/parser.test.ts +138 -0
  255. package/src/figma-codex/__tests__/resolver.test.ts +196 -0
  256. package/src/figma-codex/__tests__/writer.test.ts +111 -0
  257. package/src/figma-codex/config.ts +42 -0
  258. package/src/figma-codex/fixtures/CompoundComponent/CompoundComponent.tsx +17 -0
  259. package/src/figma-codex/fixtures/CompoundComponent/index.ts +1 -0
  260. package/src/figma-codex/fixtures/CompoundComponent.figma.tsx +14 -0
  261. package/src/figma-codex/fixtures/SimpleComponent.figma.tsx +10 -0
  262. package/src/figma-codex/fixtures/SimpleComponent.tsx +10 -0
  263. package/src/figma-codex/fixtures/expected-output.json +78 -0
  264. package/src/figma-codex/generate.ts +106 -0
  265. package/src/figma-codex/parser.ts +138 -0
  266. package/src/figma-codex/resolver.ts +280 -0
  267. package/src/figma-codex/schema.ts +79 -0
  268. package/src/figma-codex/writer.ts +54 -0
  269. package/src/preset/index.ts +6 -0
  270. package/src/preset/recipes/accordion.ts +8 -5
  271. package/src/preset/recipes/breadcrumb.ts +34 -2
  272. package/src/preset/recipes/content-card.ts +124 -0
  273. package/src/preset/recipes/index.ts +4 -0
  274. package/src/preset/recipes/navigation-menu.ts +97 -0
  275. package/src/preset/recipes/scenario-settings.ts +182 -0
  276. package/src/test/setup.ts +12 -9
  277. package/dist/chunk-ABC7N32K.cjs.map +0 -1
  278. package/dist/chunk-GD6Q2FUE.js.map +0 -1
  279. package/dist/chunk-SBKRSXSZ.js.map +0 -1
  280. package/dist/chunk-UNWXE6UB.cjs.map +0 -1
  281. package/dist/components/Heading.d.ts.map +0 -1
@@ -0,0 +1,406 @@
1
+ /* global describe, it, expect, vi */
2
+ import React from 'react';
3
+ import { render, screen, waitFor } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+ import { axe } from 'jest-axe';
6
+ import { ScenarioSettings } from './ScenarioSettings';
7
+
8
+ // ── Helpers ────────────────────────────────────────────────────────────────────
9
+
10
+ function renderSettings(props: Record<string, unknown> = {}) {
11
+ return render(<ScenarioSettings {...props} />);
12
+ }
13
+
14
+ // ── Tests ─────────────────────────────────────────────────────────────────────
15
+
16
+ describe('ScenarioSettings', () => {
17
+ // ── Rendering ─────────────────────────────────────────────────────────────
18
+
19
+ describe('Rendering', () => {
20
+ it('renders all 6 section triggers', () => {
21
+ renderSettings();
22
+ // Use exact name matching to avoid matching Dialog.Trigger aria-labels
23
+ // (e.g. "Open Conversation Flow adjustments" also contains "Conversation Flow")
24
+ expect(
25
+ screen.getByRole('button', { name: 'Conversation Flow' }),
26
+ ).toBeInTheDocument();
27
+ expect(
28
+ screen.getByRole('button', { name: 'Question Complexity' }),
29
+ ).toBeInTheDocument();
30
+ expect(
31
+ screen.getByRole('button', { name: 'Discourse Tone' }),
32
+ ).toBeInTheDocument();
33
+ expect(
34
+ screen.getByRole('button', { name: 'Response Pacing' }),
35
+ ).toBeInTheDocument();
36
+ expect(
37
+ screen.getByRole('button', { name: 'Duration' }),
38
+ ).toBeInTheDocument();
39
+ expect(
40
+ screen.getByRole('button', { name: 'Number of Questions' }),
41
+ ).toBeInTheDocument();
42
+ });
43
+
44
+ it('all 6 sections are expanded by default', () => {
45
+ renderSettings();
46
+ expect(screen.getAllByText('What this means:')).toHaveLength(4);
47
+ expect(screen.getByText('Expected time to complete')).toBeVisible();
48
+ expect(
49
+ screen.getByText('Follow up Questions in this session'),
50
+ ).toBeVisible();
51
+ });
52
+
53
+ it('renders the correct explanation text for Conversation Flow', () => {
54
+ renderSettings();
55
+ expect(
56
+ screen.getByText('Topic transitions are explicit and signposted'),
57
+ ).toBeInTheDocument();
58
+ expect(
59
+ screen.getByText(
60
+ 'Conversation flows organically with realistic tangents',
61
+ ),
62
+ ).toBeInTheDocument();
63
+ expect(
64
+ screen.getByText('Topic shifts are sudden and unpredictable'),
65
+ ).toBeInTheDocument();
66
+ });
67
+
68
+ it('renders Beginner / Intermediate / Advanced level labels for all info sections', () => {
69
+ renderSettings();
70
+ expect(screen.getAllByText('Beginner:')).toHaveLength(4);
71
+ expect(screen.getAllByText('Intermediate:')).toHaveLength(4);
72
+ expect(screen.getAllByText('Advanced:')).toHaveLength(4);
73
+ });
74
+
75
+ it('renders Adjustments buttons for the 4 info sections', () => {
76
+ renderSettings();
77
+ expect(screen.getAllByText('Adjustments')).toHaveLength(4);
78
+ });
79
+
80
+ it('renders Duration radio options', () => {
81
+ renderSettings();
82
+ expect(screen.getByText('Quick (10 - 15 min)')).toBeInTheDocument();
83
+ expect(screen.getByText('Standard (15 - 25 min)')).toBeInTheDocument();
84
+ expect(screen.getByText('Extended (25 - 35 min)')).toBeInTheDocument();
85
+ });
86
+
87
+ it('renders Number of Questions radio options', () => {
88
+ renderSettings();
89
+ expect(screen.getByText('Brief (2-4)')).toBeInTheDocument();
90
+ expect(screen.getByText('Standard (5-8)')).toBeInTheDocument();
91
+ expect(screen.getByText('Extended (9-12)')).toBeInTheDocument();
92
+ });
93
+ });
94
+
95
+ // ── Accordion Expand / Collapse ────────────────────────────────────────────
96
+
97
+ describe('Accordion', () => {
98
+ it('starts with all sections expanded by default', () => {
99
+ renderSettings();
100
+ expect(screen.getAllByText('What this means:')).toHaveLength(4);
101
+ });
102
+
103
+ it('collapses a section when its trigger is clicked', async () => {
104
+ const user = userEvent.setup();
105
+ renderSettings();
106
+
107
+ // Use exact name to avoid matching Dialog.Trigger aria-label
108
+ const trigger = screen.getByRole('button', { name: 'Conversation Flow' });
109
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
110
+
111
+ await user.click(trigger);
112
+
113
+ // In JSDOM, Ark UI updates aria-expanded without relying on CSS
114
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
115
+ });
116
+
117
+ it('expands a collapsed section when clicked', async () => {
118
+ const user = userEvent.setup();
119
+ renderSettings({ defaultValue: [] });
120
+
121
+ const trigger = screen.getByRole('button', { name: 'Discourse Tone' });
122
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
123
+
124
+ await user.click(trigger);
125
+
126
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
127
+ });
128
+
129
+ it('respects defaultValue to start specific sections open', () => {
130
+ renderSettings({ defaultValue: ['duration'] });
131
+ // Duration section should be open, info sections should be closed
132
+ expect(screen.getByRole('button', { name: 'Duration' })).toHaveAttribute(
133
+ 'aria-expanded',
134
+ 'true',
135
+ );
136
+ expect(
137
+ screen.getByRole('button', { name: 'Conversation Flow' }),
138
+ ).toHaveAttribute('aria-expanded', 'false');
139
+ expect(
140
+ screen.getByRole('button', { name: 'Question Complexity' }),
141
+ ).toHaveAttribute('aria-expanded', 'false');
142
+ });
143
+
144
+ it('opening a second section closes the first (always single-open)', async () => {
145
+ const user = userEvent.setup();
146
+ renderSettings({ defaultValue: [] });
147
+
148
+ const cfTrigger = screen.getByRole('button', {
149
+ name: 'Conversation Flow',
150
+ });
151
+ const qcTrigger = screen.getByRole('button', {
152
+ name: 'Question Complexity',
153
+ });
154
+
155
+ await user.click(cfTrigger);
156
+ expect(cfTrigger).toHaveAttribute('aria-expanded', 'true');
157
+ expect(qcTrigger).toHaveAttribute('aria-expanded', 'false');
158
+
159
+ await user.click(qcTrigger);
160
+ expect(cfTrigger).toHaveAttribute('aria-expanded', 'false');
161
+ expect(qcTrigger).toHaveAttribute('aria-expanded', 'true');
162
+ });
163
+ });
164
+
165
+ // ── Single Open Mode ───────────────────────────────────────────────────────
166
+
167
+ describe('Single Open Mode', () => {
168
+ it('only one section can be open at a time when singleOpen is true', async () => {
169
+ const user = userEvent.setup();
170
+ renderSettings({ singleOpen: true, defaultValue: ['conversation-flow'] });
171
+
172
+ const cfTrigger = screen.getByRole('button', {
173
+ name: 'Conversation Flow',
174
+ });
175
+ const qcTrigger = screen.getByRole('button', {
176
+ name: 'Question Complexity',
177
+ });
178
+
179
+ expect(cfTrigger).toHaveAttribute('aria-expanded', 'true');
180
+ expect(qcTrigger).toHaveAttribute('aria-expanded', 'false');
181
+
182
+ // Open Question Complexity — should close Conversation Flow
183
+ await user.click(qcTrigger);
184
+
185
+ expect(qcTrigger).toHaveAttribute('aria-expanded', 'true');
186
+ expect(cfTrigger).toHaveAttribute('aria-expanded', 'false');
187
+ });
188
+
189
+ it('allows collapsing all sections when singleOpen is true (collapsible)', async () => {
190
+ const user = userEvent.setup();
191
+ renderSettings({ singleOpen: true, defaultValue: ['conversation-flow'] });
192
+
193
+ const cfTrigger = screen.getByRole('button', {
194
+ name: 'Conversation Flow',
195
+ });
196
+ expect(cfTrigger).toHaveAttribute('aria-expanded', 'true');
197
+
198
+ // Click again to collapse
199
+ await user.click(cfTrigger);
200
+ expect(cfTrigger).toHaveAttribute('aria-expanded', 'false');
201
+ });
202
+
203
+ it('defaults to first section when singleOpen is true and no defaultValue provided', () => {
204
+ renderSettings({ singleOpen: true });
205
+ expect(
206
+ screen.getByRole('button', { name: 'Conversation Flow' }),
207
+ ).toHaveAttribute('aria-expanded', 'true');
208
+ });
209
+ });
210
+
211
+ // ── Radio Group ────────────────────────────────────────────────────────────
212
+
213
+ describe('Radio Group', () => {
214
+ it('Duration defaults to quick and shows the correct badge', () => {
215
+ renderSettings({ defaultValue: ['duration'] });
216
+ expect(screen.getByText('~ (10-15 min)')).toBeInTheDocument();
217
+ });
218
+
219
+ it('Number of Questions defaults to brief and shows the correct badge', () => {
220
+ renderSettings({ defaultValue: ['number-of-questions'] });
221
+ expect(screen.getByText('2-4 questions')).toBeInTheDocument();
222
+ });
223
+
224
+ it('selecting a Duration radio updates the Currently badge', async () => {
225
+ const user = userEvent.setup();
226
+ renderSettings({ defaultValue: ['duration'] });
227
+
228
+ await user.click(screen.getByText('Standard (15 - 25 min)'));
229
+
230
+ expect(screen.getByText('~ (15-25 min)')).toBeInTheDocument();
231
+ });
232
+
233
+ it('selecting a Question Count radio updates the Currently badge', async () => {
234
+ const user = userEvent.setup();
235
+ renderSettings({ defaultValue: ['number-of-questions'] });
236
+
237
+ await user.click(screen.getByText('Extended (9-12)'));
238
+
239
+ expect(screen.getByText('9-12 questions')).toBeInTheDocument();
240
+ });
241
+
242
+ it('calls onSelectionChange with correct sectionId and value on Duration change', async () => {
243
+ const user = userEvent.setup();
244
+ const onSelectionChange = vi.fn();
245
+ renderSettings({ defaultValue: ['duration'], onSelectionChange });
246
+
247
+ await user.click(screen.getByText('Extended (25 - 35 min)'));
248
+
249
+ expect(onSelectionChange).toHaveBeenCalledWith('duration', 'extended');
250
+ });
251
+
252
+ it('calls onSelectionChange with correct sectionId and value on Questions change', async () => {
253
+ const user = userEvent.setup();
254
+ const onSelectionChange = vi.fn();
255
+ renderSettings({
256
+ defaultValue: ['number-of-questions'],
257
+ onSelectionChange,
258
+ });
259
+
260
+ await user.click(screen.getByText('Standard (5-8)'));
261
+
262
+ expect(onSelectionChange).toHaveBeenCalledWith(
263
+ 'number-of-questions',
264
+ 'standard',
265
+ );
266
+ });
267
+
268
+ it('accepts custom defaultDuration', () => {
269
+ renderSettings({
270
+ defaultValue: ['duration'],
271
+ defaultDuration: 'extended',
272
+ });
273
+ expect(screen.getByText('~ (25-35 min)')).toBeInTheDocument();
274
+ });
275
+
276
+ it('accepts custom defaultQuestionCount', () => {
277
+ renderSettings({
278
+ defaultValue: ['number-of-questions'],
279
+ defaultQuestionCount: 'extended',
280
+ });
281
+ expect(screen.getByText('9-12 questions')).toBeInTheDocument();
282
+ });
283
+ });
284
+
285
+ // ── Adjustments Dialog ─────────────────────────────────────────────────────
286
+
287
+ describe('Adjustments Dialog', () => {
288
+ it('dialog is not visible initially', () => {
289
+ renderSettings({ defaultValue: ['conversation-flow'] });
290
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
291
+ });
292
+
293
+ it('clicking Adjustments opens the dialog', async () => {
294
+ const user = userEvent.setup();
295
+ renderSettings({ defaultValue: ['conversation-flow'] });
296
+
297
+ await user.click(screen.getAllByText('Adjustments')[0]);
298
+
299
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
300
+ });
301
+
302
+ it('dialog title shows the correct section name', async () => {
303
+ const user = userEvent.setup();
304
+ renderSettings({ defaultValue: ['conversation-flow'] });
305
+
306
+ await user.click(screen.getAllByText('Adjustments')[0]);
307
+
308
+ expect(
309
+ screen.getByRole('heading', { name: /Adjust Conversation Flow/i }),
310
+ ).toBeInTheDocument();
311
+ });
312
+
313
+ it('shows placeholder content when no custom content provided', async () => {
314
+ const user = userEvent.setup();
315
+ renderSettings({ defaultValue: ['conversation-flow'] });
316
+
317
+ await user.click(screen.getAllByText('Adjustments')[0]);
318
+
319
+ expect(
320
+ screen.getByText(
321
+ /Adjustment controls for Conversation Flow will appear here/i,
322
+ ),
323
+ ).toBeInTheDocument();
324
+ });
325
+
326
+ it('renders custom adjustmentDialogContent when provided', async () => {
327
+ const user = userEvent.setup();
328
+ renderSettings({
329
+ defaultValue: ['conversation-flow'],
330
+ adjustmentDialogContent: {
331
+ 'conversation-flow': <div>Custom Conversation Flow Content</div>,
332
+ },
333
+ });
334
+
335
+ await user.click(screen.getAllByText('Adjustments')[0]);
336
+
337
+ expect(
338
+ screen.getByText('Custom Conversation Flow Content'),
339
+ ).toBeInTheDocument();
340
+ });
341
+
342
+ it('renders content from renderAdjustmentDialog when provided', async () => {
343
+ const user = userEvent.setup();
344
+ const renderFn = vi.fn((sectionId: string) => (
345
+ <div>Rendered for {sectionId}</div>
346
+ ));
347
+ renderSettings({
348
+ defaultValue: ['question-complexity'],
349
+ renderAdjustmentDialog: renderFn,
350
+ });
351
+
352
+ await user.click(screen.getAllByText('Adjustments')[1]);
353
+
354
+ expect(
355
+ screen.getByText('Rendered for question-complexity'),
356
+ ).toBeInTheDocument();
357
+ });
358
+
359
+ it('renderAdjustmentDialog takes precedence over adjustmentDialogContent', async () => {
360
+ const user = userEvent.setup();
361
+ renderSettings({
362
+ defaultValue: ['conversation-flow'],
363
+ adjustmentDialogContent: {
364
+ 'conversation-flow': <div>Record content</div>,
365
+ },
366
+ renderAdjustmentDialog: () => <div>Render prop content</div>,
367
+ });
368
+
369
+ await user.click(screen.getAllByText('Adjustments')[0]);
370
+
371
+ expect(screen.getByText('Render prop content')).toBeInTheDocument();
372
+ expect(screen.queryByText('Record content')).not.toBeInTheDocument();
373
+ });
374
+
375
+ it('dialog can be closed via the close button', async () => {
376
+ const user = userEvent.setup();
377
+ renderSettings({ defaultValue: ['conversation-flow'] });
378
+
379
+ await user.click(screen.getAllByText('Adjustments')[0]);
380
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
381
+
382
+ await user.click(screen.getByLabelText('Close dialog'));
383
+
384
+ // unmountOnExit unmounts asynchronously; waitFor polls until the dialog leaves the DOM
385
+ await waitFor(() =>
386
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument(),
387
+ );
388
+ });
389
+ });
390
+
391
+ // ── Accessibility ──────────────────────────────────────────────────────────
392
+
393
+ describe('Accessibility', () => {
394
+ it('has no axe violations with default settings', async () => {
395
+ const { container } = renderSettings();
396
+ const results = await axe(container);
397
+ expect(results).toHaveNoViolations();
398
+ });
399
+
400
+ it('has no axe violations with all sections collapsed', async () => {
401
+ const { container } = renderSettings({ defaultValue: [] });
402
+ const results = await axe(container);
403
+ expect(results).toHaveNoViolations();
404
+ });
405
+ });
406
+ });