@arbor-education/design-system.components 0.0.3 → 0.0.5

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 (274) hide show
  1. package/README.md +1 -1
  2. package/dist/components/button/Button.d.ts +5 -2
  3. package/dist/components/button/Button.d.ts.map +1 -1
  4. package/dist/components/button/Button.js +3 -1
  5. package/dist/components/button/Button.js.map +1 -1
  6. package/dist/components/card/Card.d.ts +1 -2
  7. package/dist/components/card/Card.d.ts.map +1 -1
  8. package/dist/components/card/Card.js +3 -3
  9. package/dist/components/card/Card.js.map +1 -1
  10. package/dist/components/card/Card.test.js +0 -5
  11. package/dist/components/card/Card.test.js.map +1 -1
  12. package/dist/components/formField/FormField.d.ts +4 -0
  13. package/dist/components/formField/FormField.d.ts.map +1 -1
  14. package/dist/components/formField/FormField.js +2 -1
  15. package/dist/components/formField/FormField.js.map +1 -1
  16. package/dist/components/formField/FormField.stories.d.ts.map +1 -1
  17. package/dist/components/formField/FormField.stories.js +3 -1
  18. package/dist/components/formField/FormField.stories.js.map +1 -1
  19. package/dist/components/formField/FormField.test.js +5 -0
  20. package/dist/components/formField/FormField.test.js.map +1 -1
  21. package/dist/components/formField/inputs/checkbox/CheckboxInput.d.ts +7 -0
  22. package/dist/components/formField/inputs/checkbox/CheckboxInput.d.ts.map +1 -0
  23. package/dist/components/formField/inputs/checkbox/CheckboxInput.js +31 -0
  24. package/dist/components/formField/inputs/checkbox/CheckboxInput.js.map +1 -0
  25. package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts +17 -0
  26. package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts.map +1 -0
  27. package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js +19 -0
  28. package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js.map +1 -0
  29. package/dist/components/formField/inputs/checkbox/CheckboxInput.test.d.ts +2 -0
  30. package/dist/components/formField/inputs/checkbox/CheckboxInput.test.d.ts.map +1 -0
  31. package/dist/components/formField/inputs/checkbox/CheckboxInput.test.js +30 -0
  32. package/dist/components/formField/inputs/checkbox/CheckboxInput.test.js.map +1 -0
  33. package/dist/components/formField/inputs/dropdown/Dropdown.d.ts +11 -0
  34. package/dist/components/formField/inputs/dropdown/Dropdown.d.ts.map +1 -0
  35. package/dist/components/formField/inputs/dropdown/Dropdown.js +43 -0
  36. package/dist/components/formField/inputs/dropdown/Dropdown.js.map +1 -0
  37. package/dist/components/formField/inputs/dropdown/Dropdown.stories.d.ts +161 -0
  38. package/dist/components/formField/inputs/dropdown/Dropdown.stories.d.ts.map +1 -0
  39. package/dist/components/formField/inputs/dropdown/Dropdown.stories.js +172 -0
  40. package/dist/components/formField/inputs/dropdown/Dropdown.stories.js.map +1 -0
  41. package/dist/components/formField/inputs/dropdown/Dropdown.test.d.ts +2 -0
  42. package/dist/components/formField/inputs/dropdown/Dropdown.test.d.ts.map +1 -0
  43. package/dist/components/formField/inputs/dropdown/Dropdown.test.js +93 -0
  44. package/dist/components/formField/inputs/dropdown/Dropdown.test.js.map +1 -0
  45. package/dist/components/formField/inputs/dropdown/buttons/dropdownButton/DropdownButton.d.ts +11 -0
  46. package/dist/components/formField/inputs/dropdown/buttons/dropdownButton/DropdownButton.d.ts.map +1 -0
  47. package/dist/components/formField/inputs/dropdown/buttons/dropdownButton/DropdownButton.js +15 -0
  48. package/dist/components/formField/inputs/dropdown/buttons/dropdownButton/DropdownButton.js.map +1 -0
  49. package/dist/components/formField/inputs/dropdown/items/DropdownItemRenderer.d.ts +10 -0
  50. package/dist/components/formField/inputs/dropdown/items/DropdownItemRenderer.d.ts.map +1 -0
  51. package/dist/components/formField/inputs/dropdown/items/DropdownItemRenderer.js +12 -0
  52. package/dist/components/formField/inputs/dropdown/items/DropdownItemRenderer.js.map +1 -0
  53. package/dist/components/formField/inputs/dropdown/items/dropdownItem/DropdownItem.d.ts +9 -0
  54. package/dist/components/formField/inputs/dropdown/items/dropdownItem/DropdownItem.d.ts.map +1 -0
  55. package/dist/components/formField/inputs/dropdown/items/dropdownItem/DropdownItem.js +17 -0
  56. package/dist/components/formField/inputs/dropdown/items/dropdownItem/DropdownItem.js.map +1 -0
  57. package/dist/components/formField/inputs/dropdown/items/dropdownMultiLineItem/DropdownMultiLineItem.d.ts +7 -0
  58. package/dist/components/formField/inputs/dropdown/items/dropdownMultiLineItem/DropdownMultiLineItem.d.ts.map +1 -0
  59. package/dist/components/formField/inputs/dropdown/items/dropdownMultiLineItem/DropdownMultiLineItem.js +16 -0
  60. package/dist/components/formField/inputs/dropdown/items/dropdownMultiLineItem/DropdownMultiLineItem.js.map +1 -0
  61. package/dist/components/formField/inputs/dropdown/wrapper/DropdownWrapper.d.ts +16 -0
  62. package/dist/components/formField/inputs/dropdown/wrapper/DropdownWrapper.d.ts.map +1 -0
  63. package/dist/components/formField/inputs/dropdown/wrapper/DropdownWrapper.js +73 -0
  64. package/dist/components/formField/inputs/dropdown/wrapper/DropdownWrapper.js.map +1 -0
  65. package/dist/components/formField/inputs/number/NumberInput.d.ts +6 -0
  66. package/dist/components/formField/inputs/number/NumberInput.d.ts.map +1 -0
  67. package/dist/components/formField/inputs/number/NumberInput.js +39 -0
  68. package/dist/components/formField/inputs/number/NumberInput.js.map +1 -0
  69. package/dist/components/formField/inputs/number/NumberInput.stories.d.ts +20 -0
  70. package/dist/components/formField/inputs/number/NumberInput.stories.d.ts.map +1 -0
  71. package/dist/components/formField/inputs/number/NumberInput.stories.js +22 -0
  72. package/dist/components/formField/inputs/number/NumberInput.stories.js.map +1 -0
  73. package/dist/components/formField/inputs/number/NumberInput.test.d.ts +2 -0
  74. package/dist/components/formField/inputs/number/NumberInput.test.d.ts.map +1 -0
  75. package/dist/components/formField/inputs/number/NumberInput.test.js +30 -0
  76. package/dist/components/formField/inputs/number/NumberInput.test.js.map +1 -0
  77. package/dist/components/formField/inputs/radio/RadioButtonInput.d.ts +7 -0
  78. package/dist/components/formField/inputs/radio/RadioButtonInput.d.ts.map +1 -0
  79. package/dist/components/formField/inputs/radio/RadioButtonInput.js +9 -0
  80. package/dist/components/formField/inputs/radio/RadioButtonInput.js.map +1 -0
  81. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts +46 -0
  82. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts.map +1 -0
  83. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js +83 -0
  84. package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js.map +1 -0
  85. package/dist/components/formField/inputs/radio/RadioButtonInput.test.d.ts +2 -0
  86. package/dist/components/formField/inputs/radio/RadioButtonInput.test.d.ts.map +1 -0
  87. package/dist/components/formField/inputs/radio/RadioButtonInput.test.js +34 -0
  88. package/dist/components/formField/inputs/radio/RadioButtonInput.test.js.map +1 -0
  89. package/dist/components/heading/Heading.d.ts +392 -388
  90. package/dist/components/heading/Heading.d.ts.map +1 -1
  91. package/dist/components/heading/Heading.js +8 -1
  92. package/dist/components/heading/Heading.js.map +1 -1
  93. package/dist/components/heading/Heading.stories.d.ts.map +1 -1
  94. package/dist/components/heading/Heading.stories.js +7 -8
  95. package/dist/components/heading/Heading.stories.js.map +1 -1
  96. package/dist/components/heading/HeadingInnerContainer.d.ts +2 -2
  97. package/dist/components/heading/HeadingInnerContainer.js +4 -4
  98. package/dist/components/icon/Icon.d.ts +2 -2
  99. package/dist/components/icon/Icon.d.ts.map +1 -1
  100. package/dist/components/icon/Icon.js.map +1 -1
  101. package/dist/components/icon/Icon.stories.d.ts +7 -0
  102. package/dist/components/icon/Icon.stories.d.ts.map +1 -1
  103. package/dist/components/icon/Icon.stories.js +8 -0
  104. package/dist/components/icon/Icon.stories.js.map +1 -1
  105. package/dist/components/icon/allowedIcons.d.ts +1 -0
  106. package/dist/components/icon/allowedIcons.d.ts.map +1 -1
  107. package/dist/components/section/Section.d.ts +18 -0
  108. package/dist/components/section/Section.d.ts.map +1 -0
  109. package/dist/components/section/Section.js +36 -0
  110. package/dist/components/section/Section.js.map +1 -0
  111. package/dist/components/section/Section.stories.d.ts +18 -0
  112. package/dist/components/section/Section.stories.d.ts.map +1 -0
  113. package/dist/components/section/Section.stories.js +27 -0
  114. package/dist/components/section/Section.stories.js.map +1 -0
  115. package/dist/components/section/Section.test.d.ts +2 -0
  116. package/dist/components/section/Section.test.d.ts.map +1 -0
  117. package/dist/components/section/Section.test.js +157 -0
  118. package/dist/components/section/Section.test.js.map +1 -0
  119. package/dist/components/slideover/Slideover.d.ts +11 -0
  120. package/dist/components/slideover/Slideover.d.ts.map +1 -0
  121. package/dist/components/slideover/Slideover.js +11 -0
  122. package/dist/components/slideover/Slideover.js.map +1 -0
  123. package/dist/components/slideover/Slideover.test.d.ts +2 -0
  124. package/dist/components/slideover/Slideover.test.d.ts.map +1 -0
  125. package/dist/components/slideover/Slideover.test.js +33 -0
  126. package/dist/components/slideover/Slideover.test.js.map +1 -0
  127. package/dist/components/slideoverManager/SlideoverManager.d.ts +7 -0
  128. package/dist/components/slideoverManager/SlideoverManager.d.ts.map +1 -0
  129. package/dist/components/slideoverManager/SlideoverManager.js +29 -0
  130. package/dist/components/slideoverManager/SlideoverManager.js.map +1 -0
  131. package/dist/components/slideoverManager/SlideoverManager.stories.d.ts +15 -0
  132. package/dist/components/slideoverManager/SlideoverManager.stories.d.ts.map +1 -0
  133. package/dist/components/slideoverManager/SlideoverManager.stories.js +102 -0
  134. package/dist/components/slideoverManager/SlideoverManager.stories.js.map +1 -0
  135. package/dist/components/slideoverManager/SlideoverManager.test.d.ts +2 -0
  136. package/dist/components/slideoverManager/SlideoverManager.test.d.ts.map +1 -0
  137. package/dist/components/slideoverManager/SlideoverManager.test.js +53 -0
  138. package/dist/components/slideoverManager/SlideoverManager.test.js.map +1 -0
  139. package/dist/components/tabs/Tabs.d.ts +14 -18
  140. package/dist/components/tabs/Tabs.d.ts.map +1 -1
  141. package/dist/components/tabs/Tabs.js +6 -39
  142. package/dist/components/tabs/Tabs.js.map +1 -1
  143. package/dist/components/tabs/Tabs.stories.d.ts +35 -6
  144. package/dist/components/tabs/Tabs.stories.d.ts.map +1 -1
  145. package/dist/components/tabs/Tabs.stories.js +17 -45
  146. package/dist/components/tabs/Tabs.stories.js.map +1 -1
  147. package/dist/components/tabs/Tabs.test.d.ts.map +1 -1
  148. package/dist/components/tabs/Tabs.test.js +90 -97
  149. package/dist/components/tabs/Tabs.test.js.map +1 -1
  150. package/dist/components/tabs/TabsItem.d.ts +15 -0
  151. package/dist/components/tabs/TabsItem.d.ts.map +1 -0
  152. package/dist/components/tabs/TabsItem.js +18 -0
  153. package/dist/components/tabs/TabsItem.js.map +1 -0
  154. package/dist/components/tabs/TabsItem.stories.d.ts +618 -0
  155. package/dist/components/tabs/TabsItem.stories.d.ts.map +1 -0
  156. package/dist/components/tabs/TabsItem.stories.js +48 -0
  157. package/dist/components/tabs/TabsItem.stories.js.map +1 -0
  158. package/dist/index.css +1996 -1326
  159. package/dist/index.css.map +1 -1
  160. package/dist/index.d.ts +8 -1
  161. package/dist/index.d.ts.map +1 -1
  162. package/dist/index.js +8 -1
  163. package/dist/index.js.map +1 -1
  164. package/dist/utils/Constants.d.ts +6 -0
  165. package/dist/utils/Constants.d.ts.map +1 -0
  166. package/dist/utils/Constants.js +6 -0
  167. package/dist/utils/Constants.js.map +1 -0
  168. package/dist/utils/PopupParentContext.d.ts +3 -0
  169. package/dist/utils/PopupParentContext.d.ts.map +1 -0
  170. package/dist/utils/PopupParentContext.js +6 -0
  171. package/dist/utils/PopupParentContext.js.map +1 -0
  172. package/dist/utils/PubSub.d.ts +11 -0
  173. package/dist/utils/PubSub.d.ts.map +1 -0
  174. package/dist/utils/PubSub.js +27 -0
  175. package/dist/utils/PubSub.js.map +1 -0
  176. package/dist/utils/PubSub.test.d.ts +2 -0
  177. package/dist/utils/PubSub.test.d.ts.map +1 -0
  178. package/dist/utils/PubSub.test.js +229 -0
  179. package/dist/utils/PubSub.test.js.map +1 -0
  180. package/dist/utils/SlideoverUtils.d.ts +7 -0
  181. package/dist/utils/SlideoverUtils.d.ts.map +1 -0
  182. package/dist/utils/SlideoverUtils.js +8 -0
  183. package/dist/utils/SlideoverUtils.js.map +1 -0
  184. package/dist/utils/getDefaultPopupParent.d.ts +2 -0
  185. package/dist/utils/getDefaultPopupParent.d.ts.map +1 -0
  186. package/dist/utils/getDefaultPopupParent.js +13 -0
  187. package/dist/utils/getDefaultPopupParent.js.map +1 -0
  188. package/dist/utils/hooks/useComponentDidMount.d.ts +3 -0
  189. package/dist/utils/hooks/useComponentDidMount.d.ts.map +1 -0
  190. package/dist/utils/hooks/useComponentDidMount.js +5 -0
  191. package/dist/utils/hooks/useComponentDidMount.js.map +1 -0
  192. package/dist/utils/hooks/usePubSub.d.ts +2 -0
  193. package/dist/utils/hooks/usePubSub.d.ts.map +1 -0
  194. package/dist/utils/hooks/usePubSub.js +12 -0
  195. package/dist/utils/hooks/usePubSub.js.map +1 -0
  196. package/package.json +3 -3
  197. package/src/components/button/Button.story.tsx +9 -0
  198. package/src/components/button/Button.tsx +10 -2
  199. package/src/components/button/button.scss +75 -33
  200. package/src/components/card/Card.test.tsx +0 -6
  201. package/src/components/card/Card.tsx +12 -7
  202. package/src/components/card/card.scss +32 -18
  203. package/src/components/formField/FormField.stories.tsx +9 -1
  204. package/src/components/formField/FormField.test.tsx +6 -0
  205. package/src/components/formField/FormField.tsx +5 -0
  206. package/src/components/formField/formField.scss +20 -8
  207. package/src/components/formField/inputs/checkbox/CheckboxInput.stories.tsx +22 -0
  208. package/src/components/formField/inputs/checkbox/CheckboxInput.test.tsx +35 -0
  209. package/src/components/formField/inputs/checkbox/CheckboxInput.tsx +79 -0
  210. package/src/components/formField/inputs/checkbox/checkboxInput.scss +96 -0
  211. package/src/components/formField/inputs/dropdown/Dropdown.stories.tsx +185 -0
  212. package/src/components/formField/inputs/dropdown/Dropdown.test.tsx +185 -0
  213. package/src/components/formField/inputs/dropdown/Dropdown.tsx +82 -0
  214. package/src/components/formField/inputs/dropdown/buttons/dropdownButton/DropdownButton.tsx +41 -0
  215. package/src/components/formField/inputs/dropdown/buttons/dropdownButton/dropdownButton.scss +12 -0
  216. package/src/components/formField/inputs/dropdown/dropdown.scss +24 -0
  217. package/src/components/formField/inputs/dropdown/items/DropdownItemRenderer.tsx +38 -0
  218. package/src/components/formField/inputs/dropdown/items/dropdownItem/DropdownItem.tsx +49 -0
  219. package/src/components/formField/inputs/dropdown/items/dropdownItem/dropdownItem.scss +62 -0
  220. package/src/components/formField/inputs/dropdown/items/dropdownMultiLineItem/DropdownMultiLineItem.tsx +48 -0
  221. package/src/components/formField/inputs/dropdown/items/dropdownMultiLineItem/dropdownMultiLineItem.scss +52 -0
  222. package/src/components/formField/inputs/dropdown/wrapper/DropdownWrapper.tsx +138 -0
  223. package/src/components/formField/inputs/dropdown/wrapper/dropdownWrapper.scss +32 -0
  224. package/src/components/formField/inputs/input.scss +25 -26
  225. package/src/components/formField/inputs/number/NumberInput.stories.tsx +25 -0
  226. package/src/components/formField/inputs/number/NumberInput.test.tsx +33 -0
  227. package/src/components/formField/inputs/number/NumberInput.tsx +107 -0
  228. package/src/components/formField/inputs/number/numberInput.scss +68 -0
  229. package/src/components/formField/inputs/radio/RadioButtonInput.stories.tsx +97 -0
  230. package/src/components/formField/inputs/radio/RadioButtonInput.test.tsx +37 -0
  231. package/src/components/formField/inputs/radio/RadioButtonInput.tsx +46 -0
  232. package/src/components/formField/inputs/radio/radioButtonInput.scss +100 -0
  233. package/src/components/formField/label/label.scss +5 -1
  234. package/src/components/heading/Heading.stories.tsx +11 -12
  235. package/src/components/heading/Heading.tsx +21 -2
  236. package/src/components/heading/heading.scss +4 -0
  237. package/src/components/icon/Icon.stories.tsx +8 -0
  238. package/src/components/icon/Icon.tsx +2 -2
  239. package/src/components/icon/allowedIcons.tsx +2 -0
  240. package/src/components/pill/pill.scss +7 -7
  241. package/src/components/section/Section.stories.tsx +34 -0
  242. package/src/components/section/Section.test.tsx +308 -0
  243. package/src/components/section/Section.tsx +131 -0
  244. package/src/components/section/section.scss +42 -0
  245. package/src/components/slideover/Slideover.test.tsx +36 -0
  246. package/src/components/slideover/Slideover.tsx +38 -0
  247. package/src/components/slideover/slideover.scss +50 -0
  248. package/src/components/slideoverManager/SlideoverManager.stories.tsx +374 -0
  249. package/src/components/slideoverManager/SlideoverManager.test.tsx +64 -0
  250. package/src/components/slideoverManager/SlideoverManager.tsx +51 -0
  251. package/src/components/slideoverManager/slideoverManager.scss +13 -0
  252. package/src/components/tabs/Tabs.stories.tsx +92 -0
  253. package/src/components/tabs/Tabs.test.tsx +220 -0
  254. package/src/components/tabs/Tabs.tsx +14 -0
  255. package/src/components/tabs/TabsItem.stories.tsx +55 -0
  256. package/src/components/tabs/TabsItem.tsx +42 -0
  257. package/src/components/tabs/tabs.scss +62 -0
  258. package/src/global.scss +10 -1
  259. package/src/index.scss +15 -3
  260. package/src/index.ts +10 -3
  261. package/src/tokens.scss +1321 -1238
  262. package/src/utils/Constants.ts +5 -0
  263. package/src/utils/PopupParentContext.ts +6 -0
  264. package/src/utils/PubSub.test.ts +303 -0
  265. package/src/utils/PubSub.ts +34 -0
  266. package/src/utils/SlideoverUtils.ts +9 -0
  267. package/src/utils/getDefaultPopupParent.ts +14 -0
  268. package/src/utils/hooks/useComponentDidMount.ts +5 -0
  269. package/src/utils/hooks/usePubSub.ts +12 -0
  270. package/tokens/export-config.json +32 -0
  271. package/tokens/json/$metadata.json +5 -0
  272. package/tokens/json/$themes.json +1333 -0
  273. package/tokens/json/Arbor.json +6329 -0
  274. package/src/components/heading/HeadingInnerContainer.tsx +0 -18
@@ -0,0 +1,49 @@
1
+ import { allowedIcons } from 'Components/icon/allowedIcons';
2
+ import { Icon } from 'Components/icon/Icon';
3
+ import classNames from 'classnames';
4
+ import type { DropdownItemBaseProps } from 'Components/formField/inputs/dropdown/wrapper/DropdownWrapper';
5
+
6
+ export type DropdownItemProps = {
7
+ value: string;
8
+ label?: string;
9
+ icon?: keyof typeof allowedIcons;
10
+ } & DropdownItemBaseProps;
11
+
12
+ export const DropdownItem = (props: DropdownItemProps) => {
13
+ const { label, value, icon, selected = false, onSelection, id } = props;
14
+
15
+ const itemClassNames = classNames('ds-dropdown-item', {
16
+ 'ds-dropdown-item--pressed': selected,
17
+ });
18
+
19
+ return (
20
+ <div
21
+ role="option"
22
+ id={id}
23
+ className={itemClassNames}
24
+ onClick={() => onSelection?.(value)}
25
+ tabIndex={0} // Ensure the item is focusable
26
+ aria-selected={selected}
27
+ onKeyDown={(e) => {
28
+ if (e.key === 'Enter' || e.key === ' ') {
29
+ e.preventDefault();
30
+ onSelection?.(value);
31
+ }
32
+ }}
33
+ >
34
+ <div className="ds-dropdown-item--content">
35
+ {icon && (
36
+ <span className="ds-dropdown-item--icon">
37
+ <Icon name={icon} size={16} />
38
+ </span>
39
+ )}
40
+ <span className="ds-dropdown-item--label">{label ? label : value}</span>
41
+ {selected && (
42
+ <span className="ds-dropdown-item--check-icon">
43
+ <Icon name="check" size={16} />
44
+ </span>
45
+ )}
46
+ </div>
47
+ </div>
48
+ );
49
+ };
@@ -0,0 +1,62 @@
1
+
2
+ .ds-dropdown-item {
3
+ display: flex;
4
+ padding: var(--form-dropdown-form-drop-item-spacing-vertical)
5
+ var(--form-dropdown-form-drop-item-spacing-horizontal);
6
+ align-items: center;
7
+ border-radius: var(--form-dropdown-form-drop-item-radius);
8
+ background: var(--form-dropdown-form-drop-item-default-color-background);
9
+ cursor: pointer;
10
+ color: var(--form-dropdown-form-drop-item-default-color-text);
11
+
12
+ /* typography/body/p1-regular */
13
+ font-family: var(--type-body-p-family);
14
+ font-size: var(--type-body-p-size);
15
+ font-style: normal;
16
+ font-weight: var(--type-body-p-weight);
17
+ line-height: 150%;
18
+
19
+ &:focus-visible {
20
+ outline: none;
21
+ box-shadow: 0 0 0 3px var(--color-brand-500);
22
+ background: var(--button-medium-secondary-default-color-background);
23
+ }
24
+
25
+ &:hover {
26
+ background: var(--form-dropdown-form-drop-item-hover-color-background);
27
+ }
28
+
29
+ &--pressed {
30
+ background: var(--form-dropdown-form-drop-item-active-color-background);
31
+ color: var(--form-dropdown-form-drop-item-active-color-text);
32
+ }
33
+
34
+ &--content {
35
+ display: flex;
36
+ justify-content: space-between;
37
+ align-items: center;
38
+ width: 100%;
39
+ }
40
+
41
+ &--icon {
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ margin-right: 8px;
46
+ }
47
+
48
+ &--check-icon {
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: center;
52
+ margin-right: 8px;
53
+ }
54
+
55
+ &--label {
56
+ flex: 1;
57
+ }
58
+
59
+ &--check {
60
+ margin-left: auto;
61
+ }
62
+ }
@@ -0,0 +1,48 @@
1
+ import classNames from 'classnames'; // Import the classNames utility
2
+ import { Icon } from 'Components/icon/Icon'; // Import the Icon component
3
+ import type { DropdownItemBaseProps } from 'Components/formField/inputs/dropdown/wrapper/DropdownWrapper'; // Import the base props type
4
+
5
+ // Define or import the DropdownMultiLineItemProps type
6
+ export type DropdownMultiLineItemProps = {
7
+ header: string;
8
+ label: string;
9
+ } & DropdownItemBaseProps;
10
+
11
+ export const DropdownMultiLineItem = (props: DropdownMultiLineItemProps) => {
12
+ const { value, label, header, selected, onSelection, id } = props;
13
+
14
+ const itemClassNames = classNames('ds-dropdown-item', {
15
+ 'ds-dropdown-item--pressed': selected,
16
+ });
17
+
18
+ return (
19
+ <div
20
+ role="option"
21
+ id={id}
22
+ className={itemClassNames}
23
+ onClick={() => onSelection?.(value)}
24
+ tabIndex={0} // Ensure focusability
25
+ aria-selected={selected}
26
+ onKeyDown={(e) => {
27
+ if (e.key === 'Enter' || e.key === ' ') {
28
+ e.preventDefault();
29
+ onSelection?.(value);
30
+ }
31
+ }}
32
+ >
33
+ <div className="ds-dropdown-item-multiline ds-dropdown-item--content">
34
+ <h4 className="ds-dropdown-item-multiline--header">
35
+ {header}
36
+ {selected && (
37
+ <span className="ds-dropdown-item-multiline--check-icon">
38
+ <Icon name="check" size={16} />
39
+ </span>
40
+ )}
41
+ </h4>
42
+ <div className="ds-dropdown-item-multiline--content">
43
+ {label ? label : value}
44
+ </div>
45
+ </div>
46
+ </div>
47
+ );
48
+ };
@@ -0,0 +1,52 @@
1
+
2
+ .ds-dropdown-item-multiline {
3
+ display: inline-flex;
4
+ padding: var(--form-dropdown-multi-line-form-drop-item-multi-base-spacing-padding);
5
+ flex-direction: column;
6
+ justify-content: center;
7
+ align-items: flex-start;
8
+ gap: var(--form-dropdown-multi-line-spacing-gap-vertical);
9
+
10
+ &.ds-dropdown-item {
11
+ &:focus-visible {
12
+ outline: none;
13
+ box-shadow: 0 0 0 3px var(--color-brand-500);
14
+ background: var(--button-medium-secondary-default-color-background);
15
+ }
16
+ }
17
+
18
+ h4 {
19
+ margin: 0;
20
+ }
21
+
22
+ &--header {
23
+ align-self: stretch;
24
+ font-family: var(--type-body-bold-family);
25
+ font-size: var(--type-body-bold-size);
26
+ font-style: normal;
27
+ font-weight: var(--type-body-bold-weight);
28
+ line-height: 150%;
29
+ position: relative;
30
+ display: flex;
31
+ justify-content: space-between;
32
+ align-items: center;
33
+ }
34
+
35
+ &--content {
36
+ align-self: stretch;
37
+ font-family: var(--type-body-p-family);
38
+ font-size: var(--type-body-p-size);
39
+ font-style: normal;
40
+ font-weight: var(--type-body-p-weight);
41
+ line-height: 150%;
42
+ }
43
+
44
+ &--check-icon {
45
+ position: absolute;
46
+ right: 0;
47
+ top: 50%;
48
+ transform: translateY(-50%);
49
+ width: 12px;
50
+ height: 12px;
51
+ }
52
+ }
@@ -0,0 +1,138 @@
1
+ import React, {
2
+ type SelectHTMLAttributes,
3
+ useState,
4
+ type ReactNode,
5
+ cloneElement,
6
+ useEffect,
7
+ useRef,
8
+ } from 'react';
9
+ import { DropdownButton } from 'Components/formField/inputs/dropdown/buttons/dropdownButton/DropdownButton';
10
+
11
+ export type DropdownItemBaseProps = {
12
+ value: string;
13
+ selected?: boolean;
14
+ onSelection?: (value: string) => void;
15
+ } & React.HTMLAttributes<HTMLDivElement>;
16
+
17
+ export type DropdownWrapperProps = {
18
+ placeholder?: string;
19
+ multiple?: boolean;
20
+ disabled?: boolean;
21
+ errorText?: string;
22
+ children: ReactNode;
23
+ onSelectionChange: (value: string[]) => void;
24
+ } & SelectHTMLAttributes<HTMLSelectElement>;
25
+
26
+ export const DropdownWrapper = (props: DropdownWrapperProps) => {
27
+ const { placeholder, disabled, errorText, multiple, children, onSelectionChange } = props;
28
+
29
+ const [dropdownOpen, setDropdownOpen] = useState(false);
30
+ const [selected, setSelected] = useState<string[]>([]);
31
+ const [focusedIndex, setFocusedIndex] = useState<number>(-1);
32
+ const dropdownRef = useRef<HTMLDivElement>(null);
33
+
34
+ const toggleDropdown = (e: React.MouseEvent) => {
35
+ e.preventDefault();
36
+ if (!disabled) {
37
+ setDropdownOpen(!dropdownOpen);
38
+ }
39
+ };
40
+
41
+ const handleItemClick = (value: string) => {
42
+ if (!multiple) {
43
+ setSelected([value]);
44
+ setDropdownOpen(false);
45
+ }
46
+ else {
47
+ setSelected(prev =>
48
+ prev.includes(value) ? prev.filter(sel => sel !== value) : [...prev, value],
49
+ );
50
+ }
51
+ };
52
+
53
+ const handleKeyDown = (e: React.KeyboardEvent) => {
54
+ if (!dropdownOpen) {
55
+ if (e.key === 'ArrowDown' || e.key === 'Enter') {
56
+ e.preventDefault();
57
+ setDropdownOpen(true);
58
+ setFocusedIndex(0);
59
+ }
60
+ return;
61
+ }
62
+
63
+ const items = React.Children.toArray(children).filter(child =>
64
+ React.isValidElement<DropdownItemBaseProps>(child),
65
+ );
66
+
67
+ switch (e.key) {
68
+ case 'ArrowDown':
69
+ e.preventDefault();
70
+ setFocusedIndex(prev => (prev + 1) % items.length);
71
+ break;
72
+ case 'ArrowUp':
73
+ e.preventDefault();
74
+ setFocusedIndex(prev => (prev - 1 + items.length) % items.length);
75
+ break;
76
+ case 'Enter':
77
+ e.preventDefault();
78
+ if (focusedIndex >= 0 && focusedIndex < items.length) {
79
+ const item = items[focusedIndex] as React.ReactElement<DropdownItemBaseProps>;
80
+ handleItemClick(item.props.value);
81
+ }
82
+ break;
83
+ case 'Escape':
84
+ e.preventDefault();
85
+ setDropdownOpen(false);
86
+ break;
87
+ default:
88
+ break;
89
+ }
90
+ };
91
+
92
+ useEffect(() => {
93
+ onSelectionChange(selected);
94
+ }, [selected]);
95
+
96
+ return (
97
+ <div
98
+ className="ds-dropdown-wrapper"
99
+ ref={dropdownRef}
100
+ onKeyDown={handleKeyDown}
101
+ role="combobox"
102
+ aria-expanded={dropdownOpen}
103
+ aria-haspopup="listbox"
104
+ aria-disabled={disabled}
105
+ >
106
+
107
+ <DropdownButton
108
+ label={placeholder ?? ''}
109
+ disabled={disabled}
110
+ error={!!errorText}
111
+ pressed={dropdownOpen}
112
+ onClick={toggleDropdown}
113
+ />
114
+
115
+ {dropdownOpen && (
116
+ <div
117
+ role="listbox"
118
+ id="dropdownList"
119
+ className="ds-dropdown-wrapper--items"
120
+ aria-activedescendant={
121
+ focusedIndex >= 0 ? `dropdown-item-${focusedIndex}` : undefined
122
+ }
123
+ >
124
+ {React.Children.map(children, (child, index) => {
125
+ if (React.isValidElement<DropdownItemBaseProps>(child)) {
126
+ return cloneElement(child, {
127
+ selected: selected.includes(child.props.value),
128
+ onSelection: handleItemClick,
129
+ id: `dropdown-item-${index}`,
130
+ });
131
+ }
132
+ return child;
133
+ })}
134
+ </div>
135
+ )}
136
+ </div>
137
+ );
138
+ };
@@ -0,0 +1,32 @@
1
+
2
+ .ds-dropdown-wrapper {
3
+ color: var(--color-grey-600, #595959);
4
+
5
+ /* typography/body/p1-regular */
6
+ font-family: var(--type-body-p-family, Inter);
7
+ font-size: var(--type-body-p-size, 13px);
8
+ font-style: normal;
9
+ font-weight: var(--type-body-p-weight, 400);
10
+ line-height: 150%;
11
+
12
+
13
+ &--items {
14
+ h3 {
15
+ margin: 0;
16
+ }
17
+
18
+ &--header {
19
+ color: var(--form-dropdown-form-drop-item-title-color-text);
20
+ font-size: var(--type-body-bold-size);
21
+ font-style: normal;
22
+ font-weight: var(--type-body-bold-weight);
23
+ line-height: 150%;
24
+ display: flex;
25
+ padding: var(--spacing-xsmall) var(--spacing-small) var(--spacing-medium) var(--spacing-small);
26
+ align-items: center;
27
+ gap: 8px;
28
+ align-self: stretch;
29
+ border-bottom: 1px solid var(--page-base-color-border);
30
+ }
31
+ }
32
+ }
@@ -4,72 +4,71 @@
4
4
  width: 100%;
5
5
  font-family: var(--font-family-standard);
6
6
  font-weight: var(--font-weight-regular);
7
- border: 1px solid var(--form-field-placeholder-color-border);
7
+ border: var(--border-weight) solid var(--form-field-text-default-color-border);
8
8
  border-radius: var(--form-field-radius);
9
- color: var(--colour-grey-900);
9
+ color: var(--form-field-text-default-color-text);
10
10
  transition: border-color 0.2s, box-shadow 0.2s, background-color 0.2s;
11
- line-height: 150%;
12
- background-color: var(--form-field-placeholder-color-background);
13
- padding: 0 var(--form-field-spacing-x);
11
+ line-height: var(--line-height-default);
12
+ background-color: var(--form-field-text-default-color-background);
13
+ padding: 0 var(--form-field-spacing-horizontal);
14
14
  box-sizing: border-box;
15
15
 
16
16
  &::placeholder {
17
- color: var(--form-field-pill-placeholder-color-text);
17
+ color: var(--form-field-text-placeholder-color-text);
18
18
  }
19
19
 
20
20
  &:focus {
21
- border-color: var(--form-field-focus-color-border);
22
- background-color: var(--form-field-focus-color-background);
23
- outline: 2px solid var(--button-medium-primary-focus-color-focus);
21
+ color: var(--form-field-text-focus-color-text);
22
+ border-color: var(--form-field-text-focus-color-border);
23
+ background-color: var(--form-field-text-focus-color-background);
24
+ outline: var(--focus-border) solid var(--button-medium-primary-focus-color-focus);
24
25
  }
25
26
 
26
27
  // Disabled state
27
28
  &:disabled {
28
- background-color: var(--colour-grey-100);
29
- border-color: var(--colour-grey-200);
30
- color: var(--colour-grey-500);
29
+ background-color: var(--form-field-text-disabled-color-background);
30
+ border-color: var(--form-field-text-disabled-color-border);
31
+ color: var(--form-field-text-disabled-color-text);
31
32
  cursor: not-allowed;
32
33
 
33
- &::placeholder {
34
- color: var(--colour-grey-400);
35
- }
36
-
37
34
  &:hover {
38
- border-color: var(--colour-grey-200);
35
+ border-color: var(--form-field-text-disabled-color-border);
39
36
  }
40
37
  }
41
38
 
42
39
  &:hover:not(:disabled, :focus) {
43
- border-color: var(--form-field-hover-color-border);
44
- background-color: var(--form-field-hover-color-background);
40
+ color: var(--form-field-text-hover-color-text);
41
+ border-color: var(--form-field-text-hover-color-border);
42
+ background-color: var(--form-field-text-hover-color-background);
45
43
  }
46
44
 
47
45
  // Input sizes
48
46
  &--M {
49
47
  font-size: var(--font-size-3-14);
50
- height: var(--text-field-medium-height);
48
+ height: var(--form-field-text-medium-height);
51
49
  }
52
50
 
53
51
  &--S {
54
52
  font-size: var(--font-size-2-13);
55
- height: var(--text-field-small-height);
53
+ height: var(--form-field-text-small-height);
56
54
  }
57
55
 
58
56
  // Error state
59
57
  &--error {
60
- border-color: var(--colour-semantic-destructive-500);
58
+ border-color: var(--form-field-text-error-color-border);
59
+ color: var(--form-field-text-error-color-text);
61
60
 
62
61
  &:focus {
63
- border-color: var(--colour-semantic-destructive-500);
64
- box-shadow: 0 0 0 2px var(--colour-semantic-destructive-100-hover);
62
+ border-color: var(--form-field-text-error-color-border);
63
+ box-shadow: 0 0 0 var(--focus-border) var(--form-field-text-error-color-border);
65
64
  }
66
65
 
67
66
  &:hover:not(:disabled, :focus) {
68
- border-color: var(--colour-semantic-destructive-300);
67
+ border-color: var(--form-field-text-hover-color-border);
69
68
  }
70
69
  }
71
70
  }
72
71
 
73
72
  .ds-textarea {
74
- padding: var(--form-field-spacing-y) var(--form-field-spacing-x);
73
+ padding: var(--form-field-spacing-vertical) var(--form-field-spacing-padding-x);
75
74
  }
@@ -0,0 +1,25 @@
1
+ import type { Meta } from '@storybook/react-vite';
2
+ import { NumberInput } from './NumberInput';
3
+
4
+ const meta: Meta<typeof NumberInput> = {
5
+ title: 'Components/FormField/Inputs/Numeric',
6
+ component: NumberInput,
7
+ };
8
+
9
+ export const Default = {
10
+ parameters: {
11
+ layout: 'centered',
12
+ },
13
+ tags: ['autodocs'],
14
+ args: {
15
+ defaultValue: '',
16
+ step: 1,
17
+ disabled: false,
18
+ hasError: false,
19
+ placeholder: 'Enter a number',
20
+ min: 0,
21
+ max: 10,
22
+ },
23
+ };
24
+
25
+ export default meta;
@@ -0,0 +1,33 @@
1
+ import { describe, expect, test, vi } from 'vitest';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import { NumberInput } from './NumberInput';
4
+ import '@testing-library/jest-dom/vitest';
5
+
6
+ describe('NumberInput component', () => {
7
+ test('NumberInput renders', () => {
8
+ render(<NumberInput placeholder="Enter a number" />);
9
+ expect(screen.getByPlaceholderText('Enter a number')).toBeInTheDocument();
10
+ });
11
+
12
+ test('NumberInput calls onChange when input changes', () => {
13
+ const onChange = vi.fn();
14
+ render(<NumberInput placeholder="Enter a number" onChange={onChange} />);
15
+ const inputElement = screen.getByPlaceholderText('Enter a number') as HTMLInputElement;
16
+
17
+ fireEvent.change(inputElement, { target: { value: '2' } });
18
+ expect(onChange).toHaveBeenCalled();
19
+ expect(inputElement.value).toBe('2');
20
+ });
21
+
22
+ test('Spinner buttons change the value of the input', () => {
23
+ render(<NumberInput placeholder="Enter a number" defaultValue="10" step={2} />);
24
+ const inputElement = screen.getByPlaceholderText('Enter a number') as HTMLInputElement;
25
+
26
+ fireEvent.click(screen.getByTestId('minus-button'));
27
+ expect(inputElement.value).toBe('8');
28
+ fireEvent.click(screen.getByTestId('plus-button'));
29
+ expect(inputElement.value).toBe('10');
30
+ fireEvent.click(screen.getByTestId('plus-button'));
31
+ expect(inputElement.value).toBe('12');
32
+ });
33
+ });
@@ -0,0 +1,107 @@
1
+ import classNames from 'classnames';
2
+ import { Icon } from 'Components/icon/Icon';
3
+ import { useEffect, useRef, useState, type ChangeEvent, type InputHTMLAttributes } from 'react';
4
+
5
+ export type NumberInputProps = {
6
+ hasError?: boolean;
7
+ } & InputHTMLAttributes<HTMLInputElement>;
8
+
9
+ export const NumberInput = (props: NumberInputProps) => {
10
+ const {
11
+ hasError,
12
+ className = '',
13
+ disabled,
14
+ onChange,
15
+ defaultValue = '',
16
+ step = 1,
17
+ min,
18
+ max,
19
+ ...rest
20
+ } = props;
21
+
22
+ const [value, setValue] = useState<string>(defaultValue.toString());
23
+ const inputRef = useRef<HTMLInputElement>(null);
24
+
25
+ useEffect(() => {
26
+ if (inputRef.current) {
27
+ inputRef.current.value = value;
28
+ onChange?.({ currentTarget: { value } } as ChangeEvent<HTMLInputElement>);
29
+ }
30
+ }, [value]);
31
+
32
+ const inputClasses = classNames(
33
+ 'ds-input',
34
+ 'ds-number-input',
35
+ className,
36
+ );
37
+
38
+ const handleSpinner = (step: string | number) => {
39
+ const currentValue = isNaN(Number(value)) ? 0 : Number(value);
40
+ const newValue = (currentValue + Number(step)).toString();
41
+ handleOnChange(newValue);
42
+ };
43
+
44
+ const handleOnChange = (newValue: string) => {
45
+ if (newValue !== value && (newValue === '' || !isNaN(Number(newValue)))
46
+ ) {
47
+ setValue(newValue);
48
+ }
49
+ };
50
+
51
+ const handleOnBlur = () => {
52
+ if (min !== undefined && Number(value) < Number(min)) {
53
+ setValue(min.toString());
54
+ }
55
+ if (max !== undefined && Number(value) > Number(max)) {
56
+ setValue(max.toString());
57
+ }
58
+ };
59
+
60
+ return (
61
+ <div className={classNames('ds-number-input__container', {
62
+ 'ds-number-input__container--error': hasError,
63
+ 'ds-number-input__container--disabled': disabled,
64
+ })}
65
+ >
66
+ <button
67
+ className="ds-number-input__spinner-button"
68
+ type="button"
69
+ disabled={disabled || (min !== undefined && Number(value) <= Number(min))}
70
+ onClick={() => handleSpinner(-step)}
71
+ aria-label="Minus button"
72
+ data-testid="minus-button"
73
+ >
74
+ <Icon name="minus" size={12} />
75
+ </button>
76
+
77
+ <input
78
+ ref={inputRef}
79
+ type="text"
80
+ inputMode="numeric"
81
+ autoComplete="off"
82
+ pattern="^[+\-]?\d+(,\d{2})?"
83
+ className={inputClasses}
84
+ disabled={disabled}
85
+ defaultValue={defaultValue}
86
+ onChange={(e: ChangeEvent<HTMLInputElement>) => handleOnChange(e.currentTarget.value)}
87
+ onBlur={handleOnBlur}
88
+ step={step}
89
+ min={min}
90
+ max={max}
91
+ {...rest}
92
+ />
93
+
94
+ <button
95
+ className="ds-number-input__spinner-button"
96
+ type="button"
97
+ disabled={disabled || (max !== undefined && Number(value) >= Number(max))}
98
+ onClick={() => handleSpinner(step)}
99
+ aria-label="Plus button"
100
+ data-testid="plus-button"
101
+ >
102
+ <Icon name="plus" size={12} />
103
+ </button>
104
+ </div>
105
+
106
+ );
107
+ };