@arbor-education/design-system.components 0.6.0 → 0.8.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 (311) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/bin/createComponent.sh +2 -2
  3. package/dist/components/avatar/Avatar.d.ts +1 -1
  4. package/dist/components/avatar/Avatar.d.ts.map +1 -1
  5. package/dist/components/avatar/Avatar.js +1 -1
  6. package/dist/components/avatar/Avatar.js.map +1 -1
  7. package/dist/components/avatar/Avatar.stories.d.ts.map +1 -1
  8. package/dist/components/avatar/Avatar.stories.js +7 -0
  9. package/dist/components/avatar/Avatar.stories.js.map +1 -1
  10. package/dist/components/badge/Badge.d.ts +12 -0
  11. package/dist/components/badge/Badge.d.ts.map +1 -0
  12. package/dist/components/badge/Badge.js +6 -0
  13. package/dist/components/badge/Badge.js.map +1 -0
  14. package/dist/components/badge/Badge.stories.d.ts +10 -0
  15. package/dist/components/badge/Badge.stories.d.ts.map +1 -0
  16. package/dist/components/badge/Badge.stories.js +51 -0
  17. package/dist/components/badge/Badge.stories.js.map +1 -0
  18. package/dist/components/badge/Badge.test.d.ts +2 -0
  19. package/dist/components/badge/Badge.test.d.ts.map +1 -0
  20. package/dist/components/badge/Badge.test.js +23 -0
  21. package/dist/components/badge/Badge.test.js.map +1 -0
  22. package/dist/components/card/Card.js +1 -1
  23. package/dist/components/card/Card.js.map +1 -1
  24. package/dist/components/combobox/Combobox.d.ts +16 -0
  25. package/dist/components/combobox/Combobox.d.ts.map +1 -0
  26. package/dist/components/combobox/Combobox.js +195 -0
  27. package/dist/components/combobox/Combobox.js.map +1 -0
  28. package/dist/components/combobox/Combobox.stories.d.ts +24 -0
  29. package/dist/components/combobox/Combobox.stories.d.ts.map +1 -0
  30. package/dist/components/combobox/Combobox.stories.js +246 -0
  31. package/dist/components/combobox/Combobox.stories.js.map +1 -0
  32. package/dist/components/combobox/Combobox.test.d.ts +2 -0
  33. package/dist/components/combobox/Combobox.test.d.ts.map +1 -0
  34. package/dist/components/combobox/Combobox.test.js +798 -0
  35. package/dist/components/combobox/Combobox.test.js.map +1 -0
  36. package/dist/components/combobox/ComboboxButtonTrigger.d.ts +28 -0
  37. package/dist/components/combobox/ComboboxButtonTrigger.d.ts.map +1 -0
  38. package/dist/components/combobox/ComboboxButtonTrigger.js +64 -0
  39. package/dist/components/combobox/ComboboxButtonTrigger.js.map +1 -0
  40. package/dist/components/combobox/ComboboxListbox.d.ts +44 -0
  41. package/dist/components/combobox/ComboboxListbox.d.ts.map +1 -0
  42. package/dist/components/combobox/ComboboxListbox.js +37 -0
  43. package/dist/components/combobox/ComboboxListbox.js.map +1 -0
  44. package/dist/components/combobox/ComboboxOptionRow.d.ts +23 -0
  45. package/dist/components/combobox/ComboboxOptionRow.d.ts.map +1 -0
  46. package/dist/components/combobox/ComboboxOptionRow.js +27 -0
  47. package/dist/components/combobox/ComboboxOptionRow.js.map +1 -0
  48. package/dist/components/combobox/ComboboxTrigger.d.ts +35 -0
  49. package/dist/components/combobox/ComboboxTrigger.d.ts.map +1 -0
  50. package/dist/components/combobox/ComboboxTrigger.js +15 -0
  51. package/dist/components/combobox/ComboboxTrigger.js.map +1 -0
  52. package/dist/components/combobox/buildListboxDisplayOptions.d.ts +3 -0
  53. package/dist/components/combobox/buildListboxDisplayOptions.d.ts.map +1 -0
  54. package/dist/components/combobox/buildListboxDisplayOptions.js +13 -0
  55. package/dist/components/combobox/buildListboxDisplayOptions.js.map +1 -0
  56. package/dist/components/combobox/buildListboxDisplayOptions.test.d.ts +2 -0
  57. package/dist/components/combobox/buildListboxDisplayOptions.test.d.ts.map +1 -0
  58. package/dist/components/combobox/buildListboxDisplayOptions.test.js +22 -0
  59. package/dist/components/combobox/buildListboxDisplayOptions.test.js.map +1 -0
  60. package/dist/components/combobox/comboboxKeyboardTypes.d.ts +41 -0
  61. package/dist/components/combobox/comboboxKeyboardTypes.d.ts.map +1 -0
  62. package/dist/components/combobox/comboboxKeyboardTypes.js +2 -0
  63. package/dist/components/combobox/comboboxKeyboardTypes.js.map +1 -0
  64. package/dist/components/combobox/highlightLabel.d.ts +10 -0
  65. package/dist/components/combobox/highlightLabel.d.ts.map +1 -0
  66. package/dist/components/combobox/highlightLabel.js +18 -0
  67. package/dist/components/combobox/highlightLabel.js.map +1 -0
  68. package/dist/components/combobox/normaliseComboboxQuery.d.ts +2 -0
  69. package/dist/components/combobox/normaliseComboboxQuery.d.ts.map +1 -0
  70. package/dist/components/combobox/normaliseComboboxQuery.js +2 -0
  71. package/dist/components/combobox/normaliseComboboxQuery.js.map +1 -0
  72. package/dist/components/combobox/types.d.ts +46 -0
  73. package/dist/components/combobox/types.d.ts.map +1 -0
  74. package/dist/components/combobox/types.js +2 -0
  75. package/dist/components/combobox/types.js.map +1 -0
  76. package/dist/components/combobox/useChipSelection.d.ts +11 -0
  77. package/dist/components/combobox/useChipSelection.d.ts.map +1 -0
  78. package/dist/components/combobox/useChipSelection.js +35 -0
  79. package/dist/components/combobox/useChipSelection.js.map +1 -0
  80. package/dist/components/combobox/useComboboxChipKeyboard.d.ts +3 -0
  81. package/dist/components/combobox/useComboboxChipKeyboard.d.ts.map +1 -0
  82. package/dist/components/combobox/useComboboxChipKeyboard.js +103 -0
  83. package/dist/components/combobox/useComboboxChipKeyboard.js.map +1 -0
  84. package/dist/components/combobox/useComboboxChipKeyboard.test.d.ts +2 -0
  85. package/dist/components/combobox/useComboboxChipKeyboard.test.d.ts.map +1 -0
  86. package/dist/components/combobox/useComboboxChipKeyboard.test.js +116 -0
  87. package/dist/components/combobox/useComboboxChipKeyboard.test.js.map +1 -0
  88. package/dist/components/combobox/useComboboxKeyboard.d.ts +4 -0
  89. package/dist/components/combobox/useComboboxKeyboard.d.ts.map +1 -0
  90. package/dist/components/combobox/useComboboxKeyboard.js +68 -0
  91. package/dist/components/combobox/useComboboxKeyboard.js.map +1 -0
  92. package/dist/components/combobox/useComboboxListboxDom.d.ts +11 -0
  93. package/dist/components/combobox/useComboboxListboxDom.d.ts.map +1 -0
  94. package/dist/components/combobox/useComboboxListboxDom.js +15 -0
  95. package/dist/components/combobox/useComboboxListboxDom.js.map +1 -0
  96. package/dist/components/combobox/useComboboxListboxKeyboard.d.ts +3 -0
  97. package/dist/components/combobox/useComboboxListboxKeyboard.d.ts.map +1 -0
  98. package/dist/components/combobox/useComboboxListboxKeyboard.js +143 -0
  99. package/dist/components/combobox/useComboboxListboxKeyboard.js.map +1 -0
  100. package/dist/components/combobox/useComboboxListboxKeyboard.test.d.ts +2 -0
  101. package/dist/components/combobox/useComboboxListboxKeyboard.test.d.ts.map +1 -0
  102. package/dist/components/combobox/useComboboxListboxKeyboard.test.js +152 -0
  103. package/dist/components/combobox/useComboboxListboxKeyboard.test.js.map +1 -0
  104. package/dist/components/combobox/useComboboxPopoverBehavior.d.ts +38 -0
  105. package/dist/components/combobox/useComboboxPopoverBehavior.d.ts.map +1 -0
  106. package/dist/components/combobox/useComboboxPopoverBehavior.js +104 -0
  107. package/dist/components/combobox/useComboboxPopoverBehavior.js.map +1 -0
  108. package/dist/components/combobox/useComboboxState.d.ts +27 -0
  109. package/dist/components/combobox/useComboboxState.d.ts.map +1 -0
  110. package/dist/components/combobox/useComboboxState.js +122 -0
  111. package/dist/components/combobox/useComboboxState.js.map +1 -0
  112. package/dist/components/combobox/useElementWidth.d.ts +2 -0
  113. package/dist/components/combobox/useElementWidth.d.ts.map +1 -0
  114. package/dist/components/combobox/useElementWidth.js +31 -0
  115. package/dist/components/combobox/useElementWidth.js.map +1 -0
  116. package/dist/components/combobox/useVisibleChips.d.ts +21 -0
  117. package/dist/components/combobox/useVisibleChips.d.ts.map +1 -0
  118. package/dist/components/combobox/useVisibleChips.js +59 -0
  119. package/dist/components/combobox/useVisibleChips.js.map +1 -0
  120. package/dist/components/combobox/useVisibleChips.test.d.ts +2 -0
  121. package/dist/components/combobox/useVisibleChips.test.d.ts.map +1 -0
  122. package/dist/components/combobox/useVisibleChips.test.js +81 -0
  123. package/dist/components/combobox/useVisibleChips.test.js.map +1 -0
  124. package/dist/components/dot/Dot.d.ts +8 -0
  125. package/dist/components/dot/Dot.d.ts.map +1 -0
  126. package/dist/components/dot/Dot.js +6 -0
  127. package/dist/components/dot/Dot.js.map +1 -0
  128. package/dist/components/dot/Dot.stories.d.ts +15 -0
  129. package/dist/components/dot/Dot.stories.d.ts.map +1 -0
  130. package/dist/components/dot/Dot.stories.js +25 -0
  131. package/dist/components/dot/Dot.stories.js.map +1 -0
  132. package/dist/components/dot/Dot.test.d.ts +2 -0
  133. package/dist/components/dot/Dot.test.d.ts.map +1 -0
  134. package/dist/components/dot/Dot.test.js +19 -0
  135. package/dist/components/dot/Dot.test.js.map +1 -0
  136. package/dist/components/formField/FormField.d.ts +8 -4
  137. package/dist/components/formField/FormField.d.ts.map +1 -1
  138. package/dist/components/formField/FormField.js +7 -6
  139. package/dist/components/formField/FormField.js.map +1 -1
  140. package/dist/components/formField/FormField.stories.d.ts +1 -0
  141. package/dist/components/formField/FormField.stories.d.ts.map +1 -1
  142. package/dist/components/formField/FormField.stories.js +13 -1
  143. package/dist/components/formField/FormField.stories.js.map +1 -1
  144. package/dist/components/formField/FormField.test.js +10 -0
  145. package/dist/components/formField/FormField.test.js.map +1 -1
  146. package/dist/components/icon/allowedIcons.d.ts +1 -0
  147. package/dist/components/icon/allowedIcons.d.ts.map +1 -1
  148. package/dist/components/icon/allowedIcons.js +2 -1
  149. package/dist/components/icon/allowedIcons.js.map +1 -1
  150. package/dist/components/progress/Progress.stories.d.ts +49 -49
  151. package/dist/components/row/Row.d.ts +10 -0
  152. package/dist/components/row/Row.d.ts.map +1 -0
  153. package/dist/components/row/Row.js +17 -0
  154. package/dist/components/row/Row.js.map +1 -0
  155. package/dist/components/row/Row.stories.d.ts +15 -0
  156. package/dist/components/row/Row.stories.d.ts.map +1 -0
  157. package/dist/components/row/Row.stories.js +65 -0
  158. package/dist/components/row/Row.stories.js.map +1 -0
  159. package/dist/components/row/Row.test.d.ts +2 -0
  160. package/dist/components/row/Row.test.d.ts.map +1 -0
  161. package/dist/components/row/Row.test.js +62 -0
  162. package/dist/components/row/Row.test.js.map +1 -0
  163. package/dist/components/section/Section.stories.d.ts +27 -0
  164. package/dist/components/section/Section.stories.d.ts.map +1 -1
  165. package/dist/components/section/Section.stories.js +45 -1
  166. package/dist/components/section/Section.stories.js.map +1 -1
  167. package/dist/components/singleUser/SingleUser.d.ts +15 -0
  168. package/dist/components/singleUser/SingleUser.d.ts.map +1 -0
  169. package/dist/components/singleUser/SingleUser.js +9 -0
  170. package/dist/components/singleUser/SingleUser.js.map +1 -0
  171. package/dist/components/singleUser/SingleUser.stories.d.ts +11 -0
  172. package/dist/components/singleUser/SingleUser.stories.d.ts.map +1 -0
  173. package/dist/components/singleUser/SingleUser.stories.js +52 -0
  174. package/dist/components/singleUser/SingleUser.stories.js.map +1 -0
  175. package/dist/components/singleUser/SingleUser.test.d.ts +2 -0
  176. package/dist/components/singleUser/SingleUser.test.d.ts.map +1 -0
  177. package/dist/components/singleUser/SingleUser.test.js +30 -0
  178. package/dist/components/singleUser/SingleUser.test.js.map +1 -0
  179. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.d.ts.map +1 -1
  180. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.js +9 -3
  181. package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.js.map +1 -1
  182. package/dist/components/tabs/TabsItem.stories.d.ts +2 -2
  183. package/dist/components/tag/Tag.d.ts +9 -6
  184. package/dist/components/tag/Tag.d.ts.map +1 -1
  185. package/dist/components/tag/Tag.js +8 -2
  186. package/dist/components/tag/Tag.js.map +1 -1
  187. package/dist/components/tag/Tag.stories.d.ts +11 -6
  188. package/dist/components/tag/Tag.stories.d.ts.map +1 -1
  189. package/dist/components/tag/Tag.stories.js +68 -4
  190. package/dist/components/tag/Tag.stories.js.map +1 -1
  191. package/dist/components/tag/Tag.test.js +86 -50
  192. package/dist/components/tag/Tag.test.js.map +1 -1
  193. package/dist/components/toggle/Toggle.d.ts +3 -0
  194. package/dist/components/toggle/Toggle.d.ts.map +1 -0
  195. package/dist/components/toggle/Toggle.js +8 -0
  196. package/dist/components/toggle/Toggle.js.map +1 -0
  197. package/dist/components/toggle/Toggle.stories.d.ts +97 -0
  198. package/dist/components/toggle/Toggle.stories.d.ts.map +1 -0
  199. package/dist/components/toggle/Toggle.stories.js +186 -0
  200. package/dist/components/toggle/Toggle.stories.js.map +1 -0
  201. package/dist/components/toggle/Toggle.test.d.ts +2 -0
  202. package/dist/components/toggle/Toggle.test.d.ts.map +1 -0
  203. package/dist/components/toggle/Toggle.test.js +58 -0
  204. package/dist/components/toggle/Toggle.test.js.map +1 -0
  205. package/dist/index.css +703 -25
  206. package/dist/index.css.map +1 -1
  207. package/dist/index.d.ts +35 -25
  208. package/dist/index.d.ts.map +1 -1
  209. package/dist/index.js +31 -25
  210. package/dist/index.js.map +1 -1
  211. package/dist/mocks/comboboxStoryOptions.d.ts +5 -0
  212. package/dist/mocks/comboboxStoryOptions.d.ts.map +1 -0
  213. package/dist/mocks/comboboxStoryOptions.js +22 -0
  214. package/dist/mocks/comboboxStoryOptions.js.map +1 -0
  215. package/dist/utils/isSelectAllChord.d.ts +5 -0
  216. package/dist/utils/isSelectAllChord.d.ts.map +1 -0
  217. package/dist/utils/isSelectAllChord.js +7 -0
  218. package/dist/utils/isSelectAllChord.js.map +1 -0
  219. package/dist/utils/isSelectAllChord.test.d.ts +2 -0
  220. package/dist/utils/isSelectAllChord.test.d.ts.map +1 -0
  221. package/dist/utils/isSelectAllChord.test.js +19 -0
  222. package/dist/utils/isSelectAllChord.test.js.map +1 -0
  223. package/dist/utils/nextCircularIndex.d.ts +3 -0
  224. package/dist/utils/nextCircularIndex.d.ts.map +1 -0
  225. package/dist/utils/nextCircularIndex.js +10 -0
  226. package/dist/utils/nextCircularIndex.js.map +1 -0
  227. package/dist/utils/nextCircularIndex.test.d.ts +2 -0
  228. package/dist/utils/nextCircularIndex.test.d.ts.map +1 -0
  229. package/dist/utils/nextCircularIndex.test.js +23 -0
  230. package/dist/utils/nextCircularIndex.test.js.map +1 -0
  231. package/dist/utils/scrollElementIntoViewById.d.ts +2 -0
  232. package/dist/utils/scrollElementIntoViewById.d.ts.map +1 -0
  233. package/dist/utils/scrollElementIntoViewById.js +16 -0
  234. package/dist/utils/scrollElementIntoViewById.js.map +1 -0
  235. package/dist/utils/scrollElementIntoViewById.test.d.ts +2 -0
  236. package/dist/utils/scrollElementIntoViewById.test.d.ts.map +1 -0
  237. package/dist/utils/scrollElementIntoViewById.test.js +31 -0
  238. package/dist/utils/scrollElementIntoViewById.test.js.map +1 -0
  239. package/package.json +1 -1
  240. package/src/components/avatar/Avatar.stories.tsx +8 -0
  241. package/src/components/avatar/Avatar.tsx +3 -3
  242. package/src/components/badge/Badge.stories.tsx +74 -0
  243. package/src/components/badge/Badge.test.tsx +28 -0
  244. package/src/components/badge/Badge.tsx +35 -0
  245. package/src/components/badge/badge.scss +86 -0
  246. package/src/components/card/Card.tsx +1 -1
  247. package/src/components/combobox/Combobox.stories.tsx +340 -0
  248. package/src/components/combobox/Combobox.test.tsx +1160 -0
  249. package/src/components/combobox/Combobox.tsx +434 -0
  250. package/src/components/combobox/ComboboxButtonTrigger.tsx +195 -0
  251. package/src/components/combobox/ComboboxListbox.tsx +224 -0
  252. package/src/components/combobox/ComboboxOptionRow.tsx +128 -0
  253. package/src/components/combobox/ComboboxTrigger.tsx +134 -0
  254. package/src/components/combobox/buildListboxDisplayOptions.test.ts +24 -0
  255. package/src/components/combobox/buildListboxDisplayOptions.ts +12 -0
  256. package/src/components/combobox/combobox.scss +390 -0
  257. package/src/components/combobox/comboboxKeyboardTypes.ts +45 -0
  258. package/src/components/combobox/highlightLabel.tsx +42 -0
  259. package/src/components/combobox/normaliseComboboxQuery.ts +1 -0
  260. package/src/components/combobox/types.ts +53 -0
  261. package/src/components/combobox/useChipSelection.ts +53 -0
  262. package/src/components/combobox/useComboboxChipKeyboard.test.tsx +141 -0
  263. package/src/components/combobox/useComboboxChipKeyboard.ts +121 -0
  264. package/src/components/combobox/useComboboxKeyboard.ts +108 -0
  265. package/src/components/combobox/useComboboxListboxDom.ts +36 -0
  266. package/src/components/combobox/useComboboxListboxKeyboard.test.tsx +186 -0
  267. package/src/components/combobox/useComboboxListboxKeyboard.ts +172 -0
  268. package/src/components/combobox/useComboboxPopoverBehavior.ts +179 -0
  269. package/src/components/combobox/useComboboxState.ts +232 -0
  270. package/src/components/combobox/useElementWidth.ts +40 -0
  271. package/src/components/combobox/useVisibleChips.test.tsx +91 -0
  272. package/src/components/combobox/useVisibleChips.ts +100 -0
  273. package/src/components/dot/Dot.stories.tsx +41 -0
  274. package/src/components/dot/Dot.test.tsx +21 -0
  275. package/src/components/dot/Dot.tsx +18 -0
  276. package/src/components/dot/dot.scss +35 -0
  277. package/src/components/formField/FormField.stories.tsx +30 -1
  278. package/src/components/formField/FormField.test.tsx +20 -0
  279. package/src/components/formField/FormField.tsx +11 -5
  280. package/src/components/formField/inputs/number/numberInput.scss +12 -4
  281. package/src/components/icon/allowedIcons.tsx +2 -0
  282. package/src/components/pill/pill.scss +4 -6
  283. package/src/components/row/Row.stories.tsx +85 -0
  284. package/src/components/row/Row.test.tsx +82 -0
  285. package/src/components/row/Row.tsx +54 -0
  286. package/src/components/row/row.scss +61 -0
  287. package/src/components/section/Section.stories.tsx +56 -0
  288. package/src/components/singleUser/SingleUser.stories.tsx +63 -0
  289. package/src/components/singleUser/SingleUser.test.tsx +61 -0
  290. package/src/components/singleUser/SingleUser.tsx +45 -0
  291. package/src/components/singleUser/singleUser.scss +14 -0
  292. package/src/components/table/cellRenderers/SelectDropdownCellRenderer.tsx +19 -3
  293. package/src/components/tag/Tag.stories.tsx +88 -6
  294. package/src/components/tag/Tag.test.tsx +110 -44
  295. package/src/components/tag/Tag.tsx +38 -14
  296. package/src/components/tag/tag.scss +45 -30
  297. package/src/components/toggle/Toggle.stories.tsx +239 -0
  298. package/src/components/toggle/Toggle.test.tsx +66 -0
  299. package/src/components/toggle/Toggle.tsx +12 -0
  300. package/src/components/toggle/toggle.scss +126 -0
  301. package/src/index.scss +6 -0
  302. package/src/index.ts +48 -31
  303. package/src/mocks/comboboxStoryOptions.ts +25 -0
  304. package/src/tokens.scss +33 -4
  305. package/src/utils/isSelectAllChord.test.ts +24 -0
  306. package/src/utils/isSelectAllChord.ts +8 -0
  307. package/src/utils/nextCircularIndex.test.ts +26 -0
  308. package/src/utils/nextCircularIndex.ts +15 -0
  309. package/src/utils/scrollElementIntoViewById.test.ts +38 -0
  310. package/src/utils/scrollElementIntoViewById.ts +20 -0
  311. package/tokens/json/Arbor.json +3828 -3704
@@ -0,0 +1,61 @@
1
+ .ds-row {
2
+ display: flex;
3
+ flex-direction: row;
4
+ justify-content: space-between;
5
+ align-items: center;
6
+ background-color: var(--section-list-row-default-color-background);
7
+ padding: var(--section-list-row-spacing-vertical) var(--section-list-row-spacing-horizontal);
8
+ border-radius: var(--section-list-row-radius);
9
+
10
+ .ds-row__icon-click {
11
+ flex-shrink: 0;
12
+ margin-left: var(--spacing-small);
13
+ stroke: var(--section-list-row-default-color-icon-arrow);
14
+
15
+
16
+ &.ds-icon-arrow-right {
17
+ display: none;
18
+ }
19
+ }
20
+
21
+ &--clickable {
22
+ cursor: pointer;
23
+
24
+ &:hover {
25
+ background-color: var(--section-list-row-hover-hover-bg);
26
+ }
27
+ }
28
+
29
+ &:focus {
30
+ box-shadow: 0 0 0 3px var(--color-brand-300);
31
+ outline: none;
32
+ }
33
+
34
+ &:hover {
35
+ .ds-row__icon-click {
36
+ stroke: var(--section-list-row-hover-color-icon-arrow);
37
+
38
+
39
+ &.ds-icon-chevron-right {
40
+ display: none;
41
+ }
42
+
43
+ &.ds-icon-arrow-right {
44
+ display: block;
45
+ }
46
+ }
47
+ }
48
+
49
+ &__label {
50
+ min-width: 190px;
51
+ font-weight: var(--font-weight-semi-bold);
52
+ }
53
+
54
+ &__value {
55
+ flex-grow: 1;
56
+ }
57
+
58
+ &__note {
59
+ font-style: italic;
60
+ }
61
+ }
@@ -1,5 +1,6 @@
1
1
  import type { Meta } from '@storybook/react-vite';
2
2
  import { Section } from './Section';
3
+ import { Row } from 'Components/row/Row';
3
4
 
4
5
  const meta: Meta<typeof Section> = {
5
6
  title: 'Components/Section',
@@ -31,4 +32,59 @@ export const NestedSections = {
31
32
  },
32
33
  };
33
34
 
35
+ export const WithRows = {
36
+ args: {
37
+ title: 'Student Details',
38
+ children: [
39
+ <Row key={1} label="First Name" value="Jacob" />,
40
+ <Row key={2} label="Last Name" value="Black" />,
41
+ <Row key={3} label="Year Group" value="Year 10" note="2024/2025" />,
42
+ <Row key={4} label="Form Group" value="10A" />,
43
+ ],
44
+ },
45
+ };
46
+
47
+ export const WithClickableRows = {
48
+ args: {
49
+ title: 'Quick Links',
50
+ buttonText: 'Edit',
51
+ children: [
52
+ <Row key={1} label="Attendance" value="96%" note="Above target" onClick={() => { console.log('click!'); }} />,
53
+ <Row key={2} label="Behaviour" value="3 positive points" onClick={() => { console.log('click!'); }} />,
54
+ <Row key={3} label="Assessment" value="On track" note="Last updated 2 days ago" onClick={() => { console.log('click!'); }} />,
55
+ ],
56
+ },
57
+ };
58
+
59
+ export const CollapsibleWithRows = {
60
+ args: {
61
+ title: 'Contact Information',
62
+ collapsible: true,
63
+ children: [
64
+ <Row key={1} label="Email" value="jacob.black@forks.edu" />,
65
+ <Row key={2} label="Phone" value="01onal 555 0123" />,
66
+ <Row key={3} label="Address" value="La Push, Washington" note="Primary" />,
67
+ ],
68
+ },
69
+ };
70
+
71
+ export const NestedSectionsWithRows = {
72
+ args: {
73
+ title: 'Student Profile',
74
+ collapsible: true,
75
+ children: [
76
+ <Section key={1} title="Personal Details" collapsible>
77
+ <Row label="First Name" value="Jacob" />
78
+ <Row label="Last Name" value="Black" />
79
+ <Row label="Date of Birth" value="14/01/1990" note="Age 36" />
80
+ </Section>,
81
+ <Section key={2} title="Academic" collapsible>
82
+ <Row label="Year Group" value="Year 10" note="2024/2025" />
83
+ <Row label="Form Group" value="10A" />
84
+ <Row label="Attendance" value="96%" note="Above target" onClick={() => { console.log('click!'); }} />
85
+ </Section>,
86
+ ],
87
+ },
88
+ };
89
+
34
90
  export default meta;
@@ -0,0 +1,63 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { SingleUser } from './SingleUser';
3
+
4
+ const meta: Meta<typeof SingleUser> = {
5
+ title: 'Components/SingleUser',
6
+ component: SingleUser,
7
+ tags: ['autodocs'],
8
+ };
9
+
10
+ export default meta;
11
+
12
+ type Story = StoryObj<typeof SingleUser>;
13
+
14
+ const withDescription = (story: Story, description: string): Story => ({
15
+ ...story,
16
+ parameters: {
17
+ ...story.parameters,
18
+ docs: {
19
+ ...story.parameters?.docs,
20
+ description: {
21
+ story: description,
22
+ },
23
+ },
24
+ },
25
+ });
26
+
27
+ const sampleSrc = 'https://i.pravatar.cc/150?img=12';
28
+
29
+ export const WithImage: Story = withDescription({
30
+ args: {
31
+ label: 'Jordan Matthews',
32
+ src: sampleSrc,
33
+ },
34
+ }, 'Displays `SingleUser` with an avatar image and the default small layout.');
35
+
36
+ export const WithInitials: Story = withDescription({
37
+ args: {
38
+ label: 'Riley Chen',
39
+ initials: 'RC',
40
+ },
41
+ }, 'Uses initials when no image source is provided.');
42
+
43
+ export const Placeholder: Story = withDescription({
44
+ args: {
45
+ label: 'Invited user',
46
+ },
47
+ }, 'Shows the placeholder avatar treatment when neither image nor initials are supplied.');
48
+
49
+ export const MediumAvatar: Story = withDescription({
50
+ name: 'Size override (medium)',
51
+ args: {
52
+ label: 'Taylor Brooks',
53
+ initials: 'TB',
54
+ size: 'medium',
55
+ },
56
+ }, 'Demonstrates overriding the default avatar size within the SingleUser pill.');
57
+
58
+ export const LongLabel: Story = withDescription({
59
+ args: {
60
+ label: 'Dr. Alexandrina Constantinopolous-Worthington',
61
+ initials: 'AC',
62
+ },
63
+ }, 'Exercises the component with a longer label so text wrapping and spacing can be reviewed.');
@@ -0,0 +1,61 @@
1
+ import '@testing-library/jest-dom/vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { describe, expect, test } from 'vitest';
4
+ import { SingleUser } from './SingleUser';
5
+
6
+ describe('SingleUser', () => {
7
+ test('renders label text', () => {
8
+ render(
9
+ <SingleUser
10
+ label="Jamie Smith"
11
+ initials="JS"
12
+ />,
13
+ );
14
+ expect(screen.getByText('Jamie Smith')).toBeInTheDocument();
15
+ });
16
+
17
+ test('hides Avatar from screen readers', () => {
18
+ const { container } = render(
19
+ <SingleUser
20
+ label="Pat Lee"
21
+ initials="PL"
22
+ />,
23
+ );
24
+ const avatar = container.querySelector('.ds-avatar');
25
+ expect(avatar).toHaveAttribute('aria-hidden', 'true');
26
+ });
27
+
28
+ test('defaults Avatar to small size', () => {
29
+ const { container } = render(
30
+ <SingleUser
31
+ label="Sam"
32
+ initials="S"
33
+ />,
34
+ );
35
+ expect(container.querySelector('.ds-avatar--small')).toBeInTheDocument();
36
+ });
37
+
38
+ test('allows size override', () => {
39
+ const { container } = render(
40
+ <SingleUser
41
+ label="Sam"
42
+ initials="S"
43
+ size="medium"
44
+ />,
45
+ );
46
+ expect(container.querySelector('.ds-avatar--medium')).toBeInTheDocument();
47
+ });
48
+
49
+ test('applies className to root and avatarClassName to Avatar', () => {
50
+ const { container } = render(
51
+ <SingleUser
52
+ label="User"
53
+ initials="U"
54
+ className="my-chip"
55
+ avatarClassName="my-avatar"
56
+ />,
57
+ );
58
+ expect(container.querySelector('.ds-single-user.my-chip')).toBeInTheDocument();
59
+ expect(container.querySelector('.ds-avatar.my-avatar')).toBeInTheDocument();
60
+ });
61
+ });
@@ -0,0 +1,45 @@
1
+ import classNames from 'classnames';
2
+ import { Avatar, type AvatarProps, type AvatarSize } from 'Components/avatar/Avatar';
3
+ import React from 'react';
4
+
5
+ export type SingleUserProps = {
6
+ label: string;
7
+ className?: string;
8
+ avatarClassName?: string;
9
+ size?: AvatarSize;
10
+ } & Omit<AvatarProps, 'className' | 'alt' | 'size'>;
11
+
12
+ export const SingleUser = React.forwardRef<HTMLSpanElement, SingleUserProps>(
13
+ (
14
+ {
15
+ label,
16
+ className,
17
+ avatarClassName,
18
+ size = 'small',
19
+ ...avatarProps
20
+ },
21
+ ref,
22
+ ) => {
23
+ return (
24
+ <span
25
+ ref={ref}
26
+ className={classNames('ds-single-user', className)}
27
+ >
28
+
29
+ <Avatar
30
+ {...avatarProps}
31
+ size={size}
32
+ aria-hidden="true"
33
+ alt=""
34
+ className={avatarClassName}
35
+ />
36
+
37
+ <span className="ds-single-user__label">
38
+ {label}
39
+ </span>
40
+ </span>
41
+ );
42
+ },
43
+ );
44
+
45
+ SingleUser.displayName = 'SingleUser';
@@ -0,0 +1,14 @@
1
+ .ds-single-user {
2
+ box-sizing: border-box;
3
+ display: inline-flex;
4
+ align-items: center;
5
+ gap: var(--single-user-spacing-gap);
6
+ padding-block: 0;
7
+ padding-inline: 0 var(--single-user-spacing-padding-inline-end);
8
+ border-radius: var(--single-user-radius);
9
+ border: var(--single-user-border-width) solid var(--single-user-color-border);
10
+ background-color: var(--single-user-color-background);
11
+ color: var(--single-user-color-text);
12
+ font-size: var(--single-user-font-size);
13
+ line-height: var(--line-height-tight);
14
+ }
@@ -13,6 +13,13 @@ import { useComponentDidMount } from 'Utils/hooks/useComponentDidMount';
13
13
  type SelectDropdownCellRendererProps = CustomCellRendererProps
14
14
  & SelectDropdownInputProps;
15
15
 
16
+ type SelectDropdownCellRendererOption = Omit<
17
+ SelectDropdownInputProps['options'][number],
18
+ 'value'
19
+ > & {
20
+ value: string | number;
21
+ };
22
+
16
23
  export const SelectDropdownCellRenderer = (
17
24
  props: SelectDropdownCellRendererProps,
18
25
  ) => {
@@ -31,6 +38,12 @@ export const SelectDropdownCellRenderer = (
31
38
  alwaysShowPlaceholder = false,
32
39
  } = props;
33
40
 
41
+ const rawOptions: SelectDropdownCellRendererOption[] = options;
42
+ const normalisedOptions: SelectDropdownInputProps['options'] = rawOptions.map(option => ({
43
+ ...option,
44
+ value: String(option.value),
45
+ }));
46
+
34
47
  const valueStr = value != null && value !== '' ? String(value) : '';
35
48
  const initialSelectedValues = valueStr ? [valueStr] : [];
36
49
 
@@ -65,15 +78,18 @@ export const SelectDropdownCellRenderer = (
65
78
  aria-describedBy={ariaDescribedBy}
66
79
  aria-invalid={ariaInvalid}
67
80
  alwaysShowPlaceholder={alwaysShowPlaceholder}
68
- options={options}
81
+ options={normalisedOptions}
69
82
  placeholder={placeholder}
70
83
  initialSelectedValues={initialSelectedValues}
71
84
  open={isOpen}
72
85
  onOpenChange={setIsOpen}
73
86
  multiple={false}
74
87
  onSelectionChange={(newValue) => {
75
- if (column) {
76
- node.setDataValue(column, newValue[0]);
88
+ if (column && newValue[0] != null) {
89
+ const selectedOption = rawOptions.find(
90
+ option => String(option.value) === newValue[0],
91
+ );
92
+ node.setDataValue(column, selectedOption?.value ?? newValue[0]);
77
93
  }
78
94
  }}
79
95
  />
@@ -1,16 +1,98 @@
1
- import type { Meta } from '@storybook/react-vite';
2
- import { Tag } from './Tag';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { fn } from '@storybook/test';
3
+ import { Dot } from 'Components/dot/Dot';
4
+ import { Icon } from 'Components/icon/Icon';
5
+ import { Tag, type TagColor } from './Tag';
3
6
 
4
7
  const meta: Meta<typeof Tag> = {
5
8
  tags: ['autodocs'],
6
9
  title: 'Components/Tag',
7
10
  component: Tag,
11
+ parameters: {
12
+ docs: {
13
+ description: {
14
+ component:
15
+ 'Displays compact tag content with optional start/end slots and removable actions. Removable tags keep their remove button in the tab order by default; composite parents such as Combobox can pass `removeButtonTabIndex={-1}` for roving tab focus.',
16
+ },
17
+ },
18
+ },
8
19
  };
9
20
 
10
- export const Default = {
21
+ export default meta;
22
+
23
+ type Story = StoryObj<typeof Tag>;
24
+
25
+ const withDescription = (story: Story, description: string): Story => ({
26
+ ...story,
27
+ parameters: {
28
+ ...story.parameters,
29
+ docs: {
30
+ ...story.parameters?.docs,
31
+ description: {
32
+ story: description,
33
+ },
34
+ },
35
+ },
36
+ });
37
+
38
+ export const Default: Story = withDescription({
39
+ args: { children: 'Tag content' },
40
+ }, 'Shows the default neutral Tag with plain text content.');
41
+
42
+ export const WithColour: Story = withDescription({
43
+ args: { children: 'Blue tag', color: 'blue' },
44
+ }, 'Applies one of the semantic colour variants to the Tag.');
45
+
46
+ export const Selected: Story = withDescription({
47
+ args: { children: 'Selected tag', color: 'blue', selected: true },
48
+ }, 'Shows the selected treatment, which intentionally prioritises selection styling over the base colour variant.');
49
+
50
+ export const WithDot: Story = withDescription({
11
51
  args: {
12
- text: 'Tag content',
52
+ children: 'With dot',
53
+ slotStart: <Dot colour="purple" />,
13
54
  },
14
- };
55
+ }, 'Uses `slotStart` to render a decorative dot before the label.');
15
56
 
16
- export default meta;
57
+ export const WithIcon: Story = withDescription({
58
+ args: {
59
+ children: 'Alice Johnson',
60
+ slotStart: <Icon name="user" size={12} />,
61
+ },
62
+ }, 'Uses `slotStart` to show an icon before the Tag label.');
63
+
64
+ export const Removable: Story = withDescription({
65
+ args: {
66
+ children: 'Removable tag',
67
+ onRemove: fn(),
68
+ removeLabel: 'Remove removable tag',
69
+ },
70
+ }, 'Demonstrates a removable Tag with the default close affordance and a custom accessible remove label. The remove button is tabbable by default.');
71
+
72
+ export const RemovableWithIcon: Story = withDescription({
73
+ args: {
74
+ children: 'Alice Johnson',
75
+ slotStart: <Icon name="user" size={12} />,
76
+ onRemove: fn(),
77
+ removeLabel: 'Remove Alice Johnson',
78
+ },
79
+ }, 'Combines a leading icon with the removable Tag interaction.');
80
+
81
+ export const WithSlotEnd: Story = withDescription({
82
+ args: {
83
+ children: 'Custom end',
84
+ slotEnd: <Icon name="chevron-down" size={12} />,
85
+ },
86
+ }, 'Uses `slotEnd` to render trailing custom content.');
87
+
88
+ const allColours: TagColor[] = ['neutral', 'orange', 'blue', 'green', 'purple', 'teal', 'salmon', 'yellow'];
89
+
90
+ export const AllColours: Story = withDescription({
91
+ render: () => (
92
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
93
+ {allColours.map(color => (
94
+ <Tag key={color} color={color}>{color}</Tag>
95
+ ))}
96
+ </div>
97
+ ),
98
+ }, 'Displays every available Tag colour variant side by side.');
@@ -1,65 +1,131 @@
1
- import { describe, expect, test } from 'vitest';
1
+ import { describe, expect, test, vi } from 'vitest';
2
2
  import { render, screen } from '@testing-library/react';
3
- import { Tag } from './Tag';
3
+ import userEvent from '@testing-library/user-event';
4
4
  import '@testing-library/jest-dom/vitest';
5
+ import { Tag } from './Tag';
6
+ import { Dot } from 'Components/dot/Dot';
5
7
 
6
8
  describe('Tag', () => {
7
- test('Tag says hello', () => {
8
- render(<Tag text="Hello I'm a Pill!" />);
9
- expect(screen.getByText("Hello I'm a Pill!")).toBeInTheDocument();
9
+ test('renders children as text content', () => {
10
+ render(<Tag>Hello</Tag>);
11
+ expect(screen.getByText('Hello')).toBeInTheDocument();
12
+ });
13
+
14
+ test('has default color neutral', () => {
15
+ const { container } = render(<Tag>Neutral</Tag>);
16
+ expect(container.querySelector('.ds-tag')).toHaveClass('ds-tag--neutral');
17
+ });
18
+
19
+ test('applies custom color class', () => {
20
+ const { container } = render(<Tag color="blue">Blue</Tag>);
21
+ expect(container.querySelector('.ds-tag')).toHaveClass('ds-tag--blue');
10
22
  });
11
23
 
12
- test('Tag has default color neutral', () => {
13
- render(<Tag text="Hello I'm a Pill!" />);
14
- expect(screen.getByText("Hello I'm a Pill!")).toHaveClass('ds-tag--neutral');
24
+ test('applies selected class when selected is true', () => {
25
+ const { container } = render(<Tag selected>Selected</Tag>);
26
+ expect(container.querySelector('.ds-tag')).toHaveClass('ds-tag--selected');
15
27
  });
16
28
 
17
- test('Tag has custom color', () => {
18
- render(<Tag text="Hello I'm a Pill!" color="blue" />);
19
- expect(screen.getByText("Hello I'm a Pill!")).toHaveClass('ds-tag--blue');
29
+ describe('slotStart', () => {
30
+ test('renders slotStart content', () => {
31
+ const { container } = render(<Tag slotStart={<Dot colour="purple" />}>With dot</Tag>);
32
+ expect(container.querySelector('.ds-tag__slot-start .ds-dot--purple')).toBeInTheDocument();
33
+ expect(container.querySelector('.ds-tag__slot-start .ds-dot')).toHaveAttribute('aria-hidden', 'true');
34
+ });
35
+
36
+ test('does not render slot-start wrapper when slotStart is not provided', () => {
37
+ const { container } = render(<Tag>No slot</Tag>);
38
+ expect(container.querySelector('.ds-tag__slot-start')).not.toBeInTheDocument();
39
+ });
40
+ });
41
+
42
+ describe('slotEnd', () => {
43
+ test('renders slotEnd content', () => {
44
+ const { container } = render(<Tag slotEnd={<span>End</span>}>With end</Tag>);
45
+ expect(container.querySelector('.ds-tag__slot-end')).toHaveTextContent('End');
46
+ });
47
+
48
+ test('does not render slot-end wrapper when slotEnd is not provided', () => {
49
+ const { container } = render(<Tag>No slot</Tag>);
50
+ expect(container.querySelector('.ds-tag__slot-end')).not.toBeInTheDocument();
51
+ });
20
52
  });
21
53
 
22
- describe('dot functionality', () => {
23
- test('Tag does not render dot when dotColour is not provided', () => {
24
- render(<Tag text="No dot here" />);
25
- expect(screen.queryByLabelText(/dot/i)).not.toBeInTheDocument();
54
+ describe('onRemove', () => {
55
+ test('renders remove button when onRemove is provided', () => {
56
+ render(<Tag onRemove={() => {}}>Removable</Tag>);
57
+ expect(screen.getByRole('button', { name: 'Remove' })).toBeInTheDocument();
58
+ });
59
+
60
+ test('uses a custom remove label when provided', () => {
61
+ render(
62
+ <Tag onRemove={() => {}} removeLabel="Remove Alice Johnson">
63
+ Removable
64
+ </Tag>,
65
+ );
66
+ expect(screen.getByRole('button', { name: 'Remove Alice Johnson' })).toBeInTheDocument();
67
+ });
68
+
69
+ test('keeps the remove button in the tab order by default', () => {
70
+ render(<Tag onRemove={() => {}}>Removable</Tag>);
71
+ expect(screen.getByRole('button', { name: 'Remove' })).toHaveAttribute('tabindex', '0');
26
72
  });
27
73
 
28
- test('Tag renders dot when dotColour is provided', () => {
29
- render(<Tag text="Has dot" dotColour="purple" />);
30
- expect(screen.getByLabelText('purple dot')).toBeInTheDocument();
74
+ test('allows composite parents to remove the button from the tab order', () => {
75
+ render(
76
+ <Tag onRemove={() => {}} removeButtonTabIndex={-1}>
77
+ Removable
78
+ </Tag>,
79
+ );
80
+ expect(screen.getByRole('button', { name: 'Remove' })).toHaveAttribute('tabindex', '-1');
31
81
  });
32
82
 
33
- test('Tag dot has correct color class', () => {
34
- render(<Tag text="Purple dot" dotColour="purple" />);
35
- const dot = screen.getByLabelText('purple dot');
36
- expect(dot).toHaveClass('ds-tag__dot');
37
- expect(dot).toHaveClass('ds-tag__dot--purple');
83
+ test('does not render remove button when onRemove is not provided', () => {
84
+ render(<Tag>Static</Tag>);
85
+ expect(screen.queryByRole('button', { name: 'Remove' })).not.toBeInTheDocument();
38
86
  });
39
87
 
40
- test('Tag dot renders with each available colour', () => {
41
- const colours: Array<'purple' | 'salmon' | 'teal' | 'yellow' | 'green' | 'orange' | 'blue'> = [
42
- 'purple',
43
- 'salmon',
44
- 'teal',
45
- 'yellow',
46
- 'green',
47
- 'orange',
48
- 'blue',
49
- ];
50
-
51
- colours.forEach((colour) => {
52
- const { unmount } = render(<Tag text={`${colour} dot`} dotColour={colour} />);
53
- const dot = screen.getByLabelText(`${colour} dot`);
54
- expect(dot).toHaveClass(`ds-tag__dot--${colour}`);
55
- unmount();
56
- });
88
+ test('fires onRemove callback when close button is clicked', async () => {
89
+ const onRemove = vi.fn();
90
+ render(<Tag onRemove={onRemove}>Removable</Tag>);
91
+
92
+ await userEvent.click(screen.getByRole('button', { name: 'Remove' }));
93
+ expect(onRemove).toHaveBeenCalledOnce();
94
+ });
95
+
96
+ test('stops event propagation on remove click', async () => {
97
+ const onRemove = vi.fn();
98
+ const outerClick = vi.fn();
99
+ render(
100
+ <div onClick={outerClick}>
101
+ <Tag onRemove={onRemove}>Removable</Tag>
102
+ </div>,
103
+ );
104
+
105
+ await userEvent.click(screen.getByRole('button', { name: 'Remove' }));
106
+ expect(onRemove).toHaveBeenCalledOnce();
107
+ expect(outerClick).not.toHaveBeenCalled();
57
108
  });
109
+ });
110
+
111
+ describe('combined slots', () => {
112
+ test('renders slotStart, children, slotEnd, and remove button in correct order', () => {
113
+ const { container } = render(
114
+ <Tag
115
+ slotStart={<span>S</span>}
116
+ slotEnd={<span>E</span>}
117
+ onRemove={() => {}}
118
+ >
119
+ Label
120
+ </Tag>,
121
+ );
58
122
 
59
- test('Tag renders both dot and text correctly', () => {
60
- render(<Tag text="Text with dot" dotColour="teal" />);
61
- expect(screen.getByText('Text with dot')).toBeInTheDocument();
62
- expect(screen.getByLabelText('teal dot')).toBeInTheDocument();
123
+ const tag = container.querySelector('.ds-tag')!;
124
+ const children = Array.from(tag.children);
125
+ expect(children[0]).toHaveClass('ds-tag__slot-start');
126
+ expect(children[1]).toHaveClass('ds-tag__label');
127
+ expect(children[2]).toHaveClass('ds-tag__slot-end');
128
+ expect(children[3]).toHaveClass('ds-tag__remove');
63
129
  });
64
130
  });
65
131
  });