@onewelcome/react-lib-components 5.2.0 → 6.1.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 (255) hide show
  1. package/dist/cjs/Button/BaseButton.cjs.js.map +1 -1
  2. package/dist/cjs/Button/BaseButton.module.cjs.js +1 -1
  3. package/dist/cjs/Button/Button.module.cjs.js +1 -1
  4. package/dist/cjs/Button/IconButton.module.cjs.js +1 -1
  5. package/dist/cjs/ContextMenu/ContextMenu.cjs.js +1 -1
  6. package/dist/cjs/ContextMenu/ContextMenu.cjs.js.map +1 -1
  7. package/dist/cjs/ContextMenu/ContextMenuItem.module.cjs.js +1 -1
  8. package/dist/cjs/ContextMenu/ContextMenuService.cjs.js +1 -1
  9. package/dist/cjs/ContextMenu/ContextMenuService.cjs.js.map +1 -1
  10. package/dist/cjs/DataGrid/DataGridActions/DataGridColumnsToggle.module.cjs.js +1 -1
  11. package/dist/cjs/Form/Input/Input.module.cjs.js +1 -1
  12. package/dist/cjs/Form/Select/Option.cjs.js +1 -1
  13. package/dist/cjs/Form/Select/Option.cjs.js.map +1 -1
  14. package/dist/cjs/Form/Select/Select.cjs.js +1 -1
  15. package/dist/cjs/Form/Select/Select.cjs.js.map +1 -1
  16. package/dist/cjs/Form/Select/Select.module.cjs.js +1 -1
  17. package/dist/cjs/Form/Select/SelectService.cjs.js +1 -1
  18. package/dist/cjs/Form/Select/SelectService.cjs.js.map +1 -1
  19. package/dist/cjs/Form/Textarea/Textarea.module.cjs.js +1 -1
  20. package/dist/cjs/Icon/Icon.cjs.js +1 -1
  21. package/dist/cjs/Icon/Icon.cjs.js.map +1 -1
  22. package/dist/cjs/Icon/Icon.module.cjs.js +1 -1
  23. package/dist/cjs/Link/Link.cjs.js.map +1 -1
  24. package/dist/cjs/Link/Link.module.cjs.js +1 -1
  25. package/dist/cjs/Notifications/BaseModal/BaseModal.module.cjs.js +1 -1
  26. package/dist/cjs/Notifications/SlideInModal/SlideInModal.module.cjs.js +1 -1
  27. package/dist/cjs/Notifications/Snackbar/SnackbarItem/SnackbarItem.cjs.js +1 -1
  28. package/dist/cjs/Notifications/Snackbar/SnackbarItem/SnackbarItem.cjs.js.map +1 -1
  29. package/dist/cjs/Notifications/Snackbar/SnackbarItem/SnackbarItem.module.cjs.js +1 -1
  30. package/dist/cjs/Notifications/Snackbar/SnackbarProvider/SnackbarProvider.cjs.js +1 -1
  31. package/dist/cjs/Notifications/Snackbar/SnackbarProvider/SnackbarProvider.cjs.js.map +1 -1
  32. package/dist/cjs/Notifications/Snackbar/SnackbarProvider/SnackbarStateProvider.cjs.js.map +1 -1
  33. package/dist/cjs/Notifications/Snackbar/interfaces.cjs.js +2 -0
  34. package/dist/cjs/Notifications/Snackbar/interfaces.cjs.js.map +1 -0
  35. package/dist/cjs/Popover/Popover.cjs.js +1 -1
  36. package/dist/cjs/Popover/Popover.cjs.js.map +1 -1
  37. package/dist/cjs/Popover/Popover.module.cjs.js +1 -1
  38. package/dist/cjs/Stepper/Step.cjs.js +1 -1
  39. package/dist/cjs/Stepper/Step.cjs.js.map +1 -1
  40. package/dist/cjs/Stepper/Step.module.cjs.js +1 -1
  41. package/dist/cjs/Stepper/Stepper.cjs.js +1 -1
  42. package/dist/cjs/Stepper/Stepper.cjs.js.map +1 -1
  43. package/dist/cjs/Tabs/TabButton.module.cjs.js +1 -1
  44. package/dist/cjs/TextEllipsis/TextEllipsis.module.cjs.js +1 -1
  45. package/dist/cjs/Tiles/Tile.module.cjs.js +1 -1
  46. package/dist/cjs/Tooltip/Tooltip.module.cjs.js +1 -1
  47. package/dist/cjs/Wizard/BaseWizardSteps/BaseWizardSteps.module.cjs.js +1 -1
  48. package/dist/cjs/_BaseStyling_/BaseStyling.cjs.js +1 -1
  49. package/dist/cjs/_BaseStyling_/BaseStyling.cjs.js.map +1 -1
  50. package/dist/cjs/src/.scope.d.ts +2 -0
  51. package/dist/cjs/src/components/Button/BaseButton.d.ts +1 -1
  52. package/dist/cjs/src/components/ContextMenu/ContextMenu.d.ts +1 -1
  53. package/dist/cjs/src/components/ContextMenu/ContextMenuService.d.ts +4 -1
  54. package/dist/cjs/src/components/Form/Select/Option.d.ts +1 -0
  55. package/dist/cjs/src/components/Form/Select/Select.d.ts +20 -0
  56. package/dist/cjs/src/components/Form/Select/Select.interfaces.d.ts +3 -1
  57. package/dist/cjs/src/components/Form/Select/Select.test.d.ts +63 -1
  58. package/dist/cjs/src/components/Form/Select/SelectKeyboardNavigation.test.d.ts +1 -0
  59. package/dist/cjs/src/components/Form/Select/SelectService.d.ts +3 -2
  60. package/dist/cjs/src/components/Icon/Icon.d.ts +4 -1
  61. package/dist/cjs/src/components/Link/Link.d.ts +1 -1
  62. package/dist/cjs/src/components/Notifications/Snackbar/SnackbarProvider/SnackbarStateProvider.d.ts +5 -5
  63. package/dist/cjs/src/components/Notifications/Snackbar/interfaces.d.ts +6 -0
  64. package/dist/cjs/src/components/Notifications/Snackbar/useSnackbar.d.ts +4 -4
  65. package/dist/cjs/src/components/Notifications/Snackbar/useSnackbar.test.d.ts +1 -0
  66. package/dist/cjs/src/components/Stepper/Stepper.d.ts +0 -1
  67. package/dist/cjs/src/components/_BaseStyling_/BaseStyling.d.ts +11 -10
  68. package/dist/cjs/src/hooks/usePosition.cjs.js.map +1 -1
  69. package/dist/cjs/src/hooks/usePosition.d.ts +1 -1
  70. package/dist/esm/Button/BaseButton.esm.js.map +1 -1
  71. package/dist/esm/Button/BaseButton.module.esm.js +1 -1
  72. package/dist/esm/Button/Button.module.esm.js +1 -1
  73. package/dist/esm/Button/IconButton.module.esm.js +1 -1
  74. package/dist/esm/ContextMenu/ContextMenu.esm.js +1 -1
  75. package/dist/esm/ContextMenu/ContextMenu.esm.js.map +1 -1
  76. package/dist/esm/ContextMenu/ContextMenuItem.module.esm.js +1 -1
  77. package/dist/esm/ContextMenu/ContextMenuService.esm.js +1 -1
  78. package/dist/esm/ContextMenu/ContextMenuService.esm.js.map +1 -1
  79. package/dist/esm/DataGrid/DataGridActions/DataGridColumnsToggle.module.esm.js +1 -1
  80. package/dist/esm/Form/Input/Input.module.esm.js +1 -1
  81. package/dist/esm/Form/Select/Option.esm.js +1 -1
  82. package/dist/esm/Form/Select/Option.esm.js.map +1 -1
  83. package/dist/esm/Form/Select/Select.esm.js +1 -1
  84. package/dist/esm/Form/Select/Select.esm.js.map +1 -1
  85. package/dist/esm/Form/Select/Select.module.esm.js +1 -1
  86. package/dist/esm/Form/Select/SelectService.esm.js +1 -1
  87. package/dist/esm/Form/Select/SelectService.esm.js.map +1 -1
  88. package/dist/esm/Form/Textarea/Textarea.module.esm.js +1 -1
  89. package/dist/esm/Icon/Icon.esm.js +1 -1
  90. package/dist/esm/Icon/Icon.esm.js.map +1 -1
  91. package/dist/esm/Icon/Icon.module.esm.js +1 -1
  92. package/dist/esm/Link/Link.esm.js.map +1 -1
  93. package/dist/esm/Link/Link.module.esm.js +1 -1
  94. package/dist/esm/Notifications/BaseModal/BaseModal.module.esm.js +1 -1
  95. package/dist/esm/Notifications/SlideInModal/SlideInModal.module.esm.js +1 -1
  96. package/dist/esm/Notifications/Snackbar/SnackbarItem/SnackbarItem.esm.js +1 -1
  97. package/dist/esm/Notifications/Snackbar/SnackbarItem/SnackbarItem.esm.js.map +1 -1
  98. package/dist/esm/Notifications/Snackbar/SnackbarItem/SnackbarItem.module.esm.js +1 -1
  99. package/dist/esm/Notifications/Snackbar/SnackbarProvider/SnackbarProvider.esm.js +1 -1
  100. package/dist/esm/Notifications/Snackbar/SnackbarProvider/SnackbarProvider.esm.js.map +1 -1
  101. package/dist/esm/Notifications/Snackbar/SnackbarProvider/SnackbarStateProvider.esm.js.map +1 -1
  102. package/dist/esm/Notifications/Snackbar/interfaces.esm.js +2 -0
  103. package/dist/esm/Notifications/Snackbar/interfaces.esm.js.map +1 -0
  104. package/dist/esm/Popover/Popover.esm.js +1 -1
  105. package/dist/esm/Popover/Popover.esm.js.map +1 -1
  106. package/dist/esm/Popover/Popover.module.esm.js +1 -1
  107. package/dist/esm/Stepper/Step.esm.js +1 -1
  108. package/dist/esm/Stepper/Step.esm.js.map +1 -1
  109. package/dist/esm/Stepper/Step.module.esm.js +1 -1
  110. package/dist/esm/Stepper/Stepper.esm.js +1 -1
  111. package/dist/esm/Stepper/Stepper.esm.js.map +1 -1
  112. package/dist/esm/Tabs/TabButton.module.esm.js +1 -1
  113. package/dist/esm/TextEllipsis/TextEllipsis.module.esm.js +1 -1
  114. package/dist/esm/Tiles/Tile.module.esm.js +1 -1
  115. package/dist/esm/Tooltip/Tooltip.module.esm.js +1 -1
  116. package/dist/esm/Wizard/BaseWizardSteps/BaseWizardSteps.module.esm.js +1 -1
  117. package/dist/esm/_BaseStyling_/BaseStyling.esm.js +1 -1
  118. package/dist/esm/_BaseStyling_/BaseStyling.esm.js.map +1 -1
  119. package/dist/esm/src/.scope.d.ts +2 -0
  120. package/dist/esm/src/components/Button/BaseButton.d.ts +1 -1
  121. package/dist/esm/src/components/ContextMenu/ContextMenu.d.ts +1 -1
  122. package/dist/esm/src/components/ContextMenu/ContextMenuService.d.ts +4 -1
  123. package/dist/esm/src/components/Form/Select/Option.d.ts +1 -0
  124. package/dist/esm/src/components/Form/Select/Select.d.ts +20 -0
  125. package/dist/esm/src/components/Form/Select/Select.interfaces.d.ts +3 -1
  126. package/dist/esm/src/components/Form/Select/Select.test.d.ts +63 -1
  127. package/dist/esm/src/components/Form/Select/SelectKeyboardNavigation.test.d.ts +1 -0
  128. package/dist/esm/src/components/Form/Select/SelectService.d.ts +3 -2
  129. package/dist/esm/src/components/Icon/Icon.d.ts +4 -1
  130. package/dist/esm/src/components/Link/Link.d.ts +1 -1
  131. package/dist/esm/src/components/Notifications/Snackbar/SnackbarProvider/SnackbarStateProvider.d.ts +5 -5
  132. package/dist/esm/src/components/Notifications/Snackbar/interfaces.d.ts +6 -0
  133. package/dist/esm/src/components/Notifications/Snackbar/useSnackbar.d.ts +4 -4
  134. package/dist/esm/src/components/Notifications/Snackbar/useSnackbar.test.d.ts +1 -0
  135. package/dist/esm/src/components/Stepper/Stepper.d.ts +0 -1
  136. package/dist/esm/src/components/_BaseStyling_/BaseStyling.d.ts +11 -10
  137. package/dist/esm/src/hooks/usePosition.d.ts +1 -1
  138. package/dist/esm/src/hooks/usePosition.esm.js.map +1 -1
  139. package/package.json +48 -45
  140. package/src/{hooks/useWrapper.test.ts → .scope.ts} +1 -12
  141. package/src/components/Button/BaseButton.tsx +1 -1
  142. package/src/components/Button/Button.module.scss +2 -10
  143. package/src/components/ContextMenu/ContextMenu.tsx +13 -23
  144. package/src/components/ContextMenu/ContextMenuService.ts +47 -1
  145. package/src/components/Form/Select/Option.tsx +3 -1
  146. package/src/components/Form/Select/Select.interfaces.ts +3 -1
  147. package/src/components/Form/Select/Select.module.scss +55 -34
  148. package/src/components/Form/Select/Select.tsx +74 -23
  149. package/src/components/Form/Select/SelectService.ts +26 -10
  150. package/src/components/Icon/Icon.module.scss +12 -0
  151. package/src/components/Icon/Icon.tsx +4 -1
  152. package/src/components/Link/Link.module.scss +28 -4
  153. package/src/components/Link/Link.tsx +1 -1
  154. package/src/components/Notifications/Snackbar/SnackbarItem/SnackbarItem.module.scss +14 -21
  155. package/src/components/Notifications/Snackbar/SnackbarItem/SnackbarItem.tsx +17 -13
  156. package/src/components/Notifications/Snackbar/SnackbarProvider/SnackbarProvider.tsx +43 -17
  157. package/src/components/Notifications/Snackbar/SnackbarProvider/SnackbarStateProvider.tsx +5 -13
  158. package/src/components/Notifications/Snackbar/interfaces.ts +15 -0
  159. package/src/components/Popover/Popover.tsx +19 -2
  160. package/src/components/Stepper/Step.tsx +2 -1
  161. package/src/components/Stepper/Stepper.tsx +0 -2
  162. package/src/components/Tooltip/Tooltip.module.scss +1 -0
  163. package/src/components/_BaseStyling_/BaseStyling.tsx +24 -22
  164. package/src/font/icomoon.eot +0 -0
  165. package/src/font/icomoon.svg +5 -2
  166. package/src/font/icomoon.ttf +0 -0
  167. package/src/font/icomoon.woff +0 -0
  168. package/src/font/selection.json +1 -1
  169. package/src/hooks/usePosition.ts +1 -1
  170. package/src/interfaces.ts +2 -2
  171. package/src/mixins.module.scss +60 -61
  172. package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +0 -64
  173. package/src/components/Button/BaseButton.test.tsx +0 -133
  174. package/src/components/Button/Button.test.tsx +0 -138
  175. package/src/components/Button/IconButton.test.tsx +0 -122
  176. package/src/components/ContextMenu/ContextMenu.test.tsx +0 -358
  177. package/src/components/DataGrid/DataGrid.test.tsx +0 -437
  178. package/src/components/DataGrid/DataGridActions/DataGridActions.test.tsx +0 -204
  179. package/src/components/DataGrid/DataGridActions/DataGridColumnsToggle.test.tsx +0 -99
  180. package/src/components/DataGrid/DataGridBody/DataGridBody.test.tsx +0 -140
  181. package/src/components/DataGrid/DataGridBody/DataGridCell.test.tsx +0 -90
  182. package/src/components/DataGrid/DataGridBody/DataGridRow.test.tsx +0 -117
  183. package/src/components/DataGrid/DataGridHeader/DataGridHeader.test.tsx +0 -276
  184. package/src/components/DataGrid/DataGridHeader/DataGridHeaderCell.test.tsx +0 -144
  185. package/src/components/Form/Checkbox/Checkbox.test.tsx +0 -308
  186. package/src/components/Form/Fieldset/Fieldset.test.tsx +0 -127
  187. package/src/components/Form/FileUpload/FileItem/FileItem.test.tsx +0 -103
  188. package/src/components/Form/FileUpload/FileUpload.test.tsx +0 -374
  189. package/src/components/Form/Form.test.tsx +0 -63
  190. package/src/components/Form/FormControl/FormControl.test.tsx +0 -98
  191. package/src/components/Form/FormGroup/FormGroup.test.tsx +0 -127
  192. package/src/components/Form/FormHelperText/FormHelperText.test.tsx +0 -84
  193. package/src/components/Form/FormSelectorWrapper/FormSelectorWrapper.test.tsx +0 -94
  194. package/src/components/Form/Input/Input.test.tsx +0 -267
  195. package/src/components/Form/Label/Label.test.tsx +0 -68
  196. package/src/components/Form/Radio/Radio.test.tsx +0 -130
  197. package/src/components/Form/Select/Option.test.tsx +0 -57
  198. package/src/components/Form/Select/Select.test.tsx +0 -564
  199. package/src/components/Form/Textarea/Textarea.test.tsx +0 -124
  200. package/src/components/Form/Toggle/Toggle.test.tsx +0 -200
  201. package/src/components/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.test.tsx +0 -141
  202. package/src/components/Form/Wrapper/InputWrapper/InputWrapper.test.tsx +0 -211
  203. package/src/components/Form/Wrapper/RadioWrapper/RadioWrapper.test.tsx +0 -117
  204. package/src/components/Form/Wrapper/SelectWrapper/SelectWrapper.test.tsx +0 -186
  205. package/src/components/Form/Wrapper/TextareaWrapper/TextareaWrapper.test.tsx +0 -173
  206. package/src/components/Form/Wrapper/Wrapper/Wrapper.test.tsx +0 -59
  207. package/src/components/Icon/Icon.test.tsx +0 -83
  208. package/src/components/Link/Link.test.tsx +0 -197
  209. package/src/components/Notifications/Banner/Banner.test.tsx +0 -84
  210. package/src/components/Notifications/BaseModal/BaseModal.test.tsx +0 -194
  211. package/src/components/Notifications/BaseModal/BaseModalActions/BaseModalActions.test.tsx +0 -74
  212. package/src/components/Notifications/BaseModal/BaseModalContent/BaseModalContent.test.tsx +0 -71
  213. package/src/components/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.test.tsx +0 -74
  214. package/src/components/Notifications/Dialog/Dialog.test.tsx +0 -118
  215. package/src/components/Notifications/Dialog/DialogActions/DialogActions.test.tsx +0 -61
  216. package/src/components/Notifications/Dialog/DialogTitle/DialogTitle.test.tsx +0 -87
  217. package/src/components/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.test.tsx +0 -111
  218. package/src/components/Notifications/DiscardChangesModal/DiscardChangesModal.test.tsx +0 -175
  219. package/src/components/Notifications/Modal/Modal.test.tsx +0 -18
  220. package/src/components/Notifications/NotificationProvider/NotificationContext.test.tsx +0 -449
  221. package/src/components/Notifications/SlideInModal/SlideInModal.test.tsx +0 -90
  222. package/src/components/Notifications/Snackbar/SnackbarContainer/SnackbarContainer.test.tsx +0 -53
  223. package/src/components/Notifications/Snackbar/SnackbarItem/SnackbarItem.test.tsx +0 -63
  224. package/src/components/Notifications/Snackbar/SnackbarProvider/SnackbarProvider.test.tsx +0 -215
  225. package/src/components/Pagination/Pagination.test.tsx +0 -183
  226. package/src/components/Popover/Popover.test.tsx +0 -103
  227. package/src/components/ProgressBar/ProgressBar.test.tsx +0 -91
  228. package/src/components/Skeleton/Skeleton.test.tsx +0 -112
  229. package/src/components/StatusIndicator/StatusIndicator.test.tsx +0 -143
  230. package/src/components/Stepper/Stepper.test.tsx +0 -83
  231. package/src/components/Tabs/Tab.test.tsx +0 -49
  232. package/src/components/Tabs/TabButton.test.tsx +0 -65
  233. package/src/components/Tabs/Tabs.test.tsx +0 -291
  234. package/src/components/Tag/Tag.test.tsx +0 -89
  235. package/src/components/TextEllipsis/TextEllipsis.test.tsx +0 -96
  236. package/src/components/Tiles/Tile.test.tsx +0 -183
  237. package/src/components/Tiles/Tiles.test.tsx +0 -162
  238. package/src/components/Tooltip/Tooltip.test.tsx +0 -390
  239. package/src/components/Typography/Typography.test.tsx +0 -177
  240. package/src/components/Wizard/BaseWizardSteps/BaseWizardSteps.test.tsx +0 -90
  241. package/src/components/Wizard/Wizard.test.tsx +0 -218
  242. package/src/components/Wizard/WizardActions/WizardActions.test.tsx +0 -187
  243. package/src/components/Wizard/WizardSteps/WizardSteps.test.tsx +0 -125
  244. package/src/components/_BaseStyling_/BaseStyling.test.tsx +0 -55
  245. package/src/hooks/useAnimation.test.tsx +0 -65
  246. package/src/hooks/useBodyClick.test.tsx +0 -55
  247. package/src/hooks/useDebouncedCallback.test.ts +0 -150
  248. package/src/hooks/useDetermineStatusIcon.test.ts +0 -28
  249. package/src/hooks/useFormSelector.test.ts +0 -56
  250. package/src/hooks/usePosition.test.tsx +0 -510
  251. package/src/hooks/useRepeater.test.tsx +0 -156
  252. package/src/hooks/useScroll.test.tsx +0 -36
  253. package/src/hooks/useSpacing.test.ts +0 -86
  254. package/src/hooks/useUploadFile.test.ts +0 -211
  255. package/src/util/helper.test.tsx +0 -403
@@ -14,7 +14,8 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { Placement, Offset, vertical, horizontal } from "../../hooks/usePosition";
17
+ import { SetStateAction, useEffect } from "react";
18
+ import { horizontal, Offset, Placement, vertical } from "../../hooks/usePosition";
18
19
  import { remToPx } from "../../util/helper";
19
20
 
20
21
  interface UseArrowNavigation {
@@ -28,6 +29,7 @@ interface UseArrowNavigation {
28
29
  setShouldClick: React.Dispatch<React.SetStateAction<boolean>>;
29
30
  }
30
31
 
32
+ /** @scope . */
31
33
  export const useArrowNavigation = ({
32
34
  selectedContextMenuItem,
33
35
  setSelectedContextMenuItem,
@@ -123,3 +125,47 @@ export const useDefaultOffset = () => {
123
125
 
124
126
  return { calculateDefaultOffset };
125
127
  };
128
+
129
+ export const useFocusAnchorElement = (
130
+ anchorEl: React.RefObject<HTMLElement>,
131
+ id: string,
132
+ showContextMenu: boolean,
133
+ setShowContextMenu: React.Dispatch<SetStateAction<boolean>>,
134
+ setFocusedContextMenuItem: React.Dispatch<SetStateAction<number>>,
135
+ onShow?: () => void,
136
+ onClose?: () => void
137
+ ) => {
138
+ const handleEscapeKeyPress = (e: KeyboardEvent) => {
139
+ if (e.key === "Escape") {
140
+ setShowContextMenu(false);
141
+ anchorEl.current?.focus();
142
+ }
143
+ };
144
+
145
+ const escapeKeyEventHandlerManager = () => {
146
+ if (showContextMenu) {
147
+ document.addEventListener("keydown", handleEscapeKeyPress);
148
+ } else {
149
+ document.removeEventListener("keydown", handleEscapeKeyPress);
150
+ }
151
+ };
152
+
153
+ const emitContextMenuEvent = () => {
154
+ if (showContextMenu) {
155
+ onShow?.();
156
+ } else {
157
+ onClose?.();
158
+
159
+ if (document.activeElement?.closest(`#${id}-menu`)) {
160
+ anchorEl.current?.focus();
161
+ }
162
+
163
+ setFocusedContextMenuItem(-1);
164
+ }
165
+ };
166
+
167
+ useEffect(() => {
168
+ escapeKeyEventHandlerManager();
169
+ emitContextMenuEvent();
170
+ }, [showContextMenu]);
171
+ };
@@ -37,6 +37,7 @@ export interface Props extends ComponentPropsWithRef<"li"> {
37
37
  childIndex?: number;
38
38
  onOptionSelect?: (ref: React.RefObject<HTMLLIElement>) => void;
39
39
  onFocusChange?: (childIndex: number) => void;
40
+ isAddBtnFocused?: boolean;
40
41
  }
41
42
 
42
43
  const OptionComponent: ForwardRefRenderFunction<HTMLLIElement, Props> = (
@@ -53,6 +54,7 @@ const OptionComponent: ForwardRefRenderFunction<HTMLLIElement, Props> = (
53
54
  onFocusChange,
54
55
  disabled,
55
56
  value,
57
+ isAddBtnFocused,
56
58
  ...rest
57
59
  }: Props,
58
60
  ref
@@ -66,7 +68,7 @@ const OptionComponent: ForwardRefRenderFunction<HTMLLIElement, Props> = (
66
68
  }, [isSelected, shouldClick]);
67
69
 
68
70
  useEffect(() => {
69
- if (innerOptionRef.current && hasFocus && selectOpened && !isSearching) {
71
+ if (innerOptionRef.current && hasFocus && selectOpened && !isSearching && !isAddBtnFocused) {
70
72
  onFocusChange && childIndex && onFocusChange(childIndex);
71
73
  innerOptionRef.current.focus();
72
74
  }
@@ -29,11 +29,13 @@ export interface UseArrowNavigationParams {
29
29
  setShouldClick: React.Dispatch<React.SetStateAction<boolean>>;
30
30
  onOptionChangeHandler: (optionElement: HTMLElement | null) => void;
31
31
  searchInputRef: React.RefObject<HTMLInputElement>;
32
- renderSearchCondition: number;
32
+ addBtnRef?: React.RefObject<HTMLButtonElement>;
33
+ renderThreshold: number;
33
34
  }
34
35
 
35
36
  export interface UseSelectPositionListParams {
36
37
  expanded: boolean;
37
38
  optionListReference: React.RefObject<HTMLDivElement>;
38
39
  containerReference: React.RefObject<HTMLDivElement>;
40
+ addBtnRef: React.RefObject<HTMLButtonElement>;
39
41
  }
@@ -17,7 +17,7 @@
17
17
  @use "src/mixins.module";
18
18
  @use "src/variables";
19
19
 
20
- $listItemHeight: 2.125rem;
20
+ $listItemHeight: 2.5rem;
21
21
 
22
22
  .select {
23
23
  position: relative;
@@ -92,9 +92,7 @@ $listItemHeight: 2.125rem;
92
92
  border-style: var(--input-border-style);
93
93
  border-width: var(--input-border-width);
94
94
  border-radius: var(--input-border-radius);
95
- box-shadow: 0px 4px 5px 0px #01053233;
96
- box-shadow: 0px 3px 14px 0px #0105321f;
97
- box-shadow: 0px 8px 10px 0px #01053224;
95
+ box-shadow: 0 8px 10px 0 #01053224;
98
96
  position: absolute;
99
97
  z-index: variables.$select-z-index;
100
98
  top: 44px;
@@ -113,39 +111,28 @@ $listItemHeight: 2.125rem;
113
111
  border-radius: var(--input-border-radius);
114
112
  color: var(--default);
115
113
  text-align: left;
116
- max-height: calc($listItemHeight * 10);
117
114
 
118
115
  li {
119
- padding: 0.5rem 0.75rem;
116
+ padding: 0.62rem 0.75rem;
120
117
  font-size: var(--form-control-font-size);
121
118
  margin: 0;
122
119
  position: relative;
123
- line-height: var(--default-line-height);
120
+ line-height: 1.25rem;
121
+ box-sizing: border-box;
122
+ min-height: $listItemHeight;
124
123
  cursor: pointer;
125
124
 
126
- &:after {
127
- content: "";
128
- position: absolute;
129
- top: 0;
130
- left: 0;
131
- background-color: transparent;
132
- width: 100%;
133
- height: 100%;
134
- opacity: 0.05;
135
- }
136
-
137
125
  &:focus {
138
126
  outline: var(--input-border-width-focus) solid var(--color-primary300);
139
- outline-offset: -var(--input-border-width-focus);
127
+ outline-offset: var(--input-border-width-focus);
140
128
  }
141
129
 
142
- &:hover:after,
143
- &:active:after {
144
- background-color: var(--color-primary);
130
+ &:hover {
131
+ background-color: var(--color-blue-grey50);
145
132
  }
146
133
 
147
- &:active:after {
148
- opacity: 0.1;
134
+ &:active {
135
+ background-color: var(--color-blue-grey100);
149
136
  }
150
137
 
151
138
  &.disabled {
@@ -157,17 +144,19 @@ $listItemHeight: 2.125rem;
157
144
  }
158
145
  }
159
146
 
147
+ ul.has-sibling {
148
+ padding-bottom: 2px;
149
+ }
150
+
160
151
  .selected-option {
161
- &:before {
162
- content: "";
163
- position: absolute;
164
- top: 0;
165
- left: 0;
166
- height: 100%;
167
- border-top-right-radius: 0.125rem;
168
- border-bottom-right-radius: 0.125rem;
169
- border-left: 0.25rem solid var(--color-primary);
170
- }
152
+ content: "";
153
+ position: absolute;
154
+ top: 0;
155
+ left: 0;
156
+ height: 100%;
157
+ border-top-right-radius: 0.125rem;
158
+ border-bottom-right-radius: 0.125rem;
159
+ border-left: 0.25rem solid var(--color-primary);
171
160
  color: var(--color-primary);
172
161
  }
173
162
 
@@ -178,6 +167,38 @@ $listItemHeight: 2.125rem;
178
167
  pointer-events: none;
179
168
  }
180
169
 
170
+ .action-button {
171
+ border: none;
172
+ border-top: 1px solid var(--color-blue-grey50);
173
+ width: 100%;
174
+ height: 2.5rem;
175
+ padding: 0.625rem 0.75rem;
176
+ margin: 0 0 0.25rem;
177
+ background-color: var(--light);
178
+ border-radius: var(--input-border-radius);
179
+ color: var(--default);
180
+ text-align: left;
181
+ box-sizing: border-box;
182
+ cursor: pointer;
183
+
184
+ font-family: var(--font-family);
185
+ font-size: var(--form-control-font-size);
186
+
187
+ &:focus {
188
+ outline: 1px solid;
189
+ outline-offset: 0;
190
+ border-radius: 0;
191
+ }
192
+
193
+ &:hover {
194
+ background-color: var(--color-blue-grey50);
195
+ }
196
+
197
+ &:active {
198
+ background-color: var(--color-blue-grey100);
199
+ }
200
+ }
201
+
181
202
  .status {
182
203
  position: absolute;
183
204
  top: 50%;
@@ -37,25 +37,41 @@ import { useDetermineStatusIcon } from "../../../hooks/useDetermineStatusIcon";
37
37
 
38
38
  type PartialInputProps = Partial<InputProps>;
39
39
 
40
+ interface SearchProps {
41
+ enabled?: boolean;
42
+ renderThreshold?: number;
43
+ searchPlaceholder?: string;
44
+ searchInputProps?: PartialInputProps & { reset?: boolean };
45
+ }
46
+
40
47
  export interface Props extends ComponentPropsWithRef<"select">, FormElement {
41
48
  children: ReactElement[];
42
49
  name?: string;
43
50
  labeledBy?: string;
44
51
  describedBy?: string;
45
52
  placeholder?: string;
53
+ /**
54
+ * @deprecated
55
+ */
46
56
  searchPlaceholder?: string;
57
+ /**
58
+ * @deprecated
59
+ */
47
60
  searchInputProps?: PartialInputProps & { reset?: boolean };
48
61
  selectButtonProps?: ComponentPropsWithRef<"button">;
62
+ search?: SearchProps;
49
63
  className?: string;
50
64
  value: string;
51
65
  clearLabel?: string;
52
66
  noResultsLabel?: string;
53
67
  onChange?: (event: React.ChangeEvent<HTMLSelectElement>, child?: ReactElement) => void;
68
+ addNew?: {
69
+ label: string;
70
+ onAddNew: (value: string) => void;
71
+ btnProps?: React.ButtonHTMLAttributes<HTMLButtonElement>;
72
+ };
54
73
  }
55
74
 
56
- /** Amount of items to be rendered before a search input is rendered */
57
- const renderSearchCondition = 10;
58
-
59
75
  const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
60
76
  {
61
77
  children,
@@ -74,6 +90,8 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
74
90
  clearLabel = "Clear selection",
75
91
  noResultsLabel = "No results found",
76
92
  onChange,
93
+ addNew,
94
+ search,
77
95
  ...rest
78
96
  }: Props,
79
97
  ref
@@ -85,14 +103,32 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
85
103
  const optionListReference = useRef<HTMLDivElement>(null);
86
104
  const [isSearching, setIsSearching] = useState(false);
87
105
  const [focusedSelectItem, setFocusedSelectItem] = useState(-1);
88
- const [shouldClick, setShouldClick] =
89
- useState(
90
- false
91
- ); /** We need this, because whenever we use the arrow keys to select the select item, and we focus the currently selected item it fires the "click" listener in Option component. Instead, we only want this to fire if we press "enter" or "spacebar" so we set this to true whenever that is the case, and back to false when it has been executed. */
106
+ const [shouldClick, setShouldClick] = useState(false);
107
+ /** We need this, because whenever we use the arrow keys to select the select item, and we focus the currently selected item it fires the "click" listener in Option component. Instead, we only want this to fire if we press "enter" or "spacebar" so we set this to true whenever that is the case, and back to false when it has been executed. */
92
108
  const [shouldFocusButtonAfterClose, setShouldFocusButtonAfterClose] = useState(false);
93
109
 
110
+ const DEFAULT_RENDER_THRESHOLD = 10;
111
+
94
112
  const nativeSelect = (ref as React.RefObject<HTMLSelectElement>) || createRef();
95
113
  const searchInputRef = useRef<HTMLInputElement>(null);
114
+ const addBtnRef = useRef<HTMLButtonElement>(null);
115
+
116
+ const addNewLabel = addNew?.label ?? "Create new";
117
+
118
+ const getRenderThreshold = search?.renderThreshold ?? DEFAULT_RENDER_THRESHOLD;
119
+ const hasEnoughChildren = Array.isArray(children) && children.length > getRenderThreshold;
120
+
121
+ const shouldRenderSearch = () => {
122
+ if (search?.enabled) {
123
+ return hasEnoughChildren;
124
+ }
125
+
126
+ if (search) {
127
+ return search.enabled as boolean;
128
+ }
129
+
130
+ return children.length > DEFAULT_RENDER_THRESHOLD;
131
+ };
96
132
 
97
133
  const onOptionChangeHandler = (optionElement: HTMLElement | null) => {
98
134
  if (nativeSelect.current && optionElement) {
@@ -114,11 +150,12 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
114
150
  childrenCount: React.Children.count(children),
115
151
  setShouldClick,
116
152
  searchInputRef,
117
- renderSearchCondition
153
+ addBtnRef,
154
+ renderThreshold: getRenderThreshold
118
155
  });
119
156
 
120
157
  const { listPosition, opacity, optionsListMaxHeight, setListPosition, setOpacity } =
121
- useSelectPositionList({ expanded, optionListReference, containerReference });
158
+ useSelectPositionList({ expanded, optionListReference, containerReference, addBtnRef });
122
159
 
123
160
  const syncDisplayValue = (val: string) => {
124
161
  React.Children.forEach(children, child => {
@@ -129,7 +166,7 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
129
166
  };
130
167
 
131
168
  /**
132
- * @description We have to modify the children (Option component) to have a additional props that allows us to keep track of which one is selected and focused at all times and if a filter is active.
169
+ * @description We have to modify the children (Option component) to have an additional props that allows us to keep track of which one is selected and focused at all times and if a filter is active.
133
170
  * The `children` prop can be either a single object (1 child) or an array of multiple children.
134
171
  */
135
172
  const renderOptions = () => {
@@ -165,32 +202,32 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
165
202
  selectOpened: expanded,
166
203
  childIndex: index,
167
204
  hasFocus: focusedSelectItem === index,
168
- shouldClick: shouldClick
205
+ shouldClick: shouldClick,
206
+ isAddBtnFocused: addBtnRef.current === document.activeElement
169
207
  });
170
208
  });
171
209
  }
172
210
  };
173
211
 
174
- const shouldRenderSearch =
175
- expanded && Array.isArray(children) && children.length > renderSearchCondition;
176
-
177
212
  const renderSearch = () => (
178
213
  <Input
179
- {...searchInputProps}
214
+ {...(search?.searchInputProps ?? searchInputProps)}
180
215
  ref={searchInputRef}
181
216
  onFocus={() => setIsSearching(true)}
182
217
  onBlur={() => setIsSearching(false)}
183
218
  onChange={filterResults}
184
219
  className={classes["select-search"]}
185
220
  wrapperProps={{
186
- className: searchInputProps?.wrapperProps?.className
221
+ className:
222
+ search?.searchInputProps?.wrapperProps?.className ??
223
+ searchInputProps?.wrapperProps?.className
187
224
  }}
188
225
  style={{
189
226
  display: expanded ? "block" : "none"
190
227
  }}
191
228
  type="text"
192
229
  name="search-option"
193
- placeholder={searchPlaceholder}
230
+ placeholder={search?.searchPlaceholder ?? searchPlaceholder}
194
231
  />
195
232
  );
196
233
 
@@ -245,8 +282,8 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
245
282
  };
246
283
 
247
284
  useEffect(() => {
248
- searchInputProps?.reset && resetSearchState();
249
- }, [searchInputProps?.reset]);
285
+ (search?.searchInputProps?.reset || searchInputProps?.reset) && resetSearchState();
286
+ }, [searchInputProps?.reset, search?.searchInputProps?.reset]);
250
287
 
251
288
  const additionalClasses = [];
252
289
  expanded && additionalClasses.push(classes.expanded);
@@ -280,7 +317,7 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
280
317
  className ?? ""
281
318
  }`}
282
319
  >
283
- {Array.isArray(children) && children.length > renderSearchCondition && renderSearch()}
320
+ {shouldRenderSearch() && renderSearch()}
284
321
  <button
285
322
  {...selectButtonProps}
286
323
  onClick={() => {
@@ -290,7 +327,7 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
290
327
  type="button"
291
328
  name={name}
292
329
  className={`${classes["custom-select"]} ${additionalClasses.join(" ")} `}
293
- style={{ display: shouldRenderSearch ? "none" : "initial" }}
330
+ style={{ display: expanded && shouldRenderSearch() ? "none" : "initial" }}
294
331
  disabled={disabled}
295
332
  aria-disabled={disabled}
296
333
  aria-invalid={error}
@@ -317,11 +354,25 @@ const SelectComponent: ForwardRefRenderFunction<HTMLSelectElement, Props> = (
317
354
  ...listPosition
318
355
  }}
319
356
  >
320
- <ul role="listbox">{renderOptions()}</ul>
357
+ <ul className={addNew && classes["has-sibling"]} role="listbox">
358
+ {renderOptions()}
359
+ </ul>
360
+ {addNew && (
361
+ <button
362
+ data-testid={"select-action-button"}
363
+ className={classes["action-button"]}
364
+ onClick={() => addNew.onAddNew(filter)}
365
+ ref={addBtnRef}
366
+ {...addNew.btnProps}
367
+ >
368
+ {!filter && addNewLabel}
369
+ {filter && <span style={{ fontWeight: "700" }}>{`"${filter}"`}</span>}
370
+ {filter && ` (${addNewLabel.toLowerCase()})`}
371
+ </button>
372
+ )}
321
373
  </div>
322
374
  </div>
323
375
  </Fragment>
324
376
  );
325
377
  };
326
-
327
378
  export const Select = React.forwardRef(SelectComponent);
@@ -21,6 +21,7 @@ import {
21
21
  UseSelectPositionListParams
22
22
  } from "./Select.interfaces";
23
23
 
24
+ /** @scope . */
24
25
  export const useArrowNavigation = ({
25
26
  expanded,
26
27
  setExpanded,
@@ -31,7 +32,8 @@ export const useArrowNavigation = ({
31
32
  childrenCount,
32
33
  setShouldClick,
33
34
  searchInputRef,
34
- renderSearchCondition
35
+ addBtnRef,
36
+ renderThreshold
35
37
  }: UseArrowNavigationParams) => {
36
38
  const onArrowNavigation = (event: React.KeyboardEvent) => {
37
39
  const codesToPreventDefault = [
@@ -54,12 +56,16 @@ export const useArrowNavigation = ({
54
56
  "MetaRight"
55
57
  ];
56
58
 
57
- /** If the select is expanded, we also want to control the Tab key */
59
+ const isAddBtnFocused = addBtnRef?.current === document.activeElement;
60
+
58
61
  if (expanded) {
59
62
  codesToPreventDefault.push("Tab");
60
63
  }
61
64
 
62
- /** We will handle the way certain key strokes affect the Select, unless we're searching */
65
+ if (addBtnRef) {
66
+ codesToPreventDefaultWhenSearching.push("Tab");
67
+ }
68
+
63
69
  if (codesToPreventDefault.includes(event.code) && !event.metaKey && !isSearching) {
64
70
  event.preventDefault();
65
71
  }
@@ -80,7 +86,14 @@ export const useArrowNavigation = ({
80
86
  setFocusedSelectItem(childrenCount - 1);
81
87
  return;
82
88
  case "Escape":
89
+ setIsSearching(false);
90
+ setExpanded(false);
91
+ return;
83
92
  case "Tab":
93
+ if (addBtnRef?.current) {
94
+ addBtnRef.current.focus();
95
+ return;
96
+ }
84
97
  setIsSearching(false);
85
98
  setExpanded(false);
86
99
  }
@@ -125,10 +138,13 @@ export const useArrowNavigation = ({
125
138
 
126
139
  return;
127
140
  case "Tab":
128
- if (childrenCount >= renderSearchCondition && expanded) {
141
+ if (childrenCount >= renderThreshold && expanded && !isAddBtnFocused) {
129
142
  setIsSearching(true);
130
143
  searchInputRef.current?.focus();
131
144
  return;
145
+ } else if (addBtnRef?.current && expanded && !isAddBtnFocused) {
146
+ addBtnRef.current.focus();
147
+ return;
132
148
  }
133
149
  setExpanded(false);
134
150
 
@@ -163,10 +179,11 @@ export const useArrowNavigation = ({
163
179
  export const useSelectPositionList = ({
164
180
  expanded,
165
181
  optionListReference,
182
+ addBtnRef,
166
183
  containerReference
167
184
  }: UseSelectPositionListParams) => {
168
185
  const [optionsListMaxHeight, setOptionsListMaxHeight] = useState("none");
169
- const [opacity, setOpacity] = useState(0); // We set opacity because other wise if we calculate the max height you see the list full height for a split second and then it shortens.
186
+ const [opacity, setOpacity] = useState(0); // We set opacity because otherwise if we calculate the max height you see the list full height for a split second and then it shortens.
170
187
  const [listPosition, setListPosition] = useState<Partial<Position>>({});
171
188
 
172
189
  useEffect(() => {
@@ -203,6 +220,7 @@ export const useSelectPositionList = ({
203
220
  const calculateOptionListMaxHeight = (position: Position) => {
204
221
  // Calculate max height if there's more space below the select
205
222
  const listHeight = optionListReference.current?.getBoundingClientRect().height;
223
+ const addNewButtonHeight = addBtnRef.current?.getBoundingClientRect().height ?? 0;
206
224
  const transformOrigin = position.top !== "initial" ? "top" : "bottom";
207
225
 
208
226
  if (!containerReference.current) {
@@ -214,12 +232,10 @@ export const useSelectPositionList = ({
214
232
 
215
233
  const availableSpace =
216
234
  transformOrigin === "top"
217
- ? window.innerHeight -
218
- containerReference.current.getBoundingClientRect()[transformOrigin] -
219
- 16
220
- : containerReference.current.getBoundingClientRect()[transformOrigin] - 16;
235
+ ? window.innerHeight - containerReference.current.getBoundingClientRect().bottom - 16
236
+ : containerReference.current.getBoundingClientRect().top - 16;
221
237
 
222
- if (listHeight && availableSpace < listHeight) {
238
+ if (listHeight && availableSpace < listHeight + addNewButtonHeight) {
223
239
  setOptionsListMaxHeight(`${availableSpace}px`);
224
240
  setOpacity(100);
225
241
  return;
@@ -45,6 +45,18 @@
45
45
  font-size: 0.875rem;
46
46
  }
47
47
 
48
+ .icon-save-outline:before {
49
+ content: "\e947";
50
+ @include fontProperties();
51
+ }
52
+ .icon-reply-outline:before {
53
+ content: "\e946";
54
+ @include fontProperties();
55
+ }
56
+ .icon-upload-outline:before {
57
+ content: "\e945";
58
+ @include fontProperties();
59
+ }
48
60
  .icon-home-filled:before {
49
61
  content: "\e940";
50
62
  @include fontProperties();
@@ -87,7 +87,10 @@ export enum Icons {
87
87
  Warning = "warning",
88
88
  FileOutline = "file-outline",
89
89
  FileUpload = "file-upload-outline",
90
- FileDownload = "file-download-outline"
90
+ FileDownload = "file-download-outline",
91
+ UploadOutline = "upload-outline",
92
+ ReplyOutline = "reply-outline",
93
+ SaveOutline = "save-outline"
91
94
  }
92
95
 
93
96
  type Tag = "span" | "div" | "i";
@@ -39,22 +39,46 @@
39
39
  &:focus-visible {
40
40
  outline: 0.5px dashed var(--color-primary);
41
41
  }
42
+
43
+ &:visited {
44
+ color: var(--button-primary-pressed-color);
45
+ }
46
+ }
47
+
48
+ &.success {
49
+ color: var(--color-success);
50
+
51
+ &:focus-visible {
52
+ outline: 0.5px dashed var(--color-primary);
53
+ }
54
+
55
+ &:visited {
56
+ color: var(--button-success-pressed-color);
57
+ }
42
58
  }
43
59
 
44
- &.secondary {
45
- color: var(--color-secondary);
60
+ &.danger {
61
+ color: var(--color-danger);
46
62
 
47
63
  &:focus-visible {
48
64
  outline: 0.5px dashed var(--color-primary);
49
65
  }
66
+
67
+ &:visited {
68
+ color: var(--button-danger-pressed-color);
69
+ }
50
70
  }
51
71
 
52
- &.tertiary {
53
- color: var(--color-tertiary);
72
+ &.warning {
73
+ color: var(--color-warning);
54
74
 
55
75
  &:focus-visible {
56
76
  outline: 0.5px dashed var(--color-primary);
57
77
  }
78
+
79
+ &:visited {
80
+ color: var(--button-warning-pressed-color);
81
+ }
58
82
  }
59
83
 
60
84
  &.disabled {
@@ -27,7 +27,7 @@ export type AnchorType = "external" | "internal" | "download";
27
27
 
28
28
  export interface Props extends ComponentPropsWithRef<"a"> {
29
29
  prefixIcon?: ReactElement<IconProps, typeof Icon>;
30
- color?: "primary" | "secondary" | "tertiary";
30
+ color?: "primary" | "success" | "danger" | "warning";
31
31
  display?: "link" | "button";
32
32
  buttonVariant?: "outline" | "text" | "fill";
33
33
  type?: AnchorType;