@onewelcome/react-lib-components 0.1.0-alpha → 0.1.3-alpha

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 (240) hide show
  1. package/README.md +16 -1
  2. package/dist/Breadcrumbs/Breadcrumbs.d.ts +3 -3
  3. package/dist/Button/BaseButton.d.ts +3 -4
  4. package/dist/Button/Button.d.ts +3 -4
  5. package/dist/Button/IconButton.d.ts +4 -5
  6. package/dist/ContextMenu/ContextMenu.d.ts +3 -3
  7. package/dist/Form/Checkbox/Checkbox.d.ts +5 -5
  8. package/dist/Form/Fieldset/Fieldset.d.ts +9 -7
  9. package/dist/Form/FormControl/FormControl.d.ts +6 -5
  10. package/dist/Form/FormGroup/FormGroup.d.ts +4 -4
  11. package/dist/Form/FormHelperText/FormHelperText.d.ts +4 -5
  12. package/dist/Form/FormSelectorWrapper/FormSelectorWrapper.d.ts +8 -12
  13. package/dist/Form/Input/Input.d.ts +7 -6
  14. package/dist/Form/Label/Label.d.ts +4 -5
  15. package/dist/Form/Radio/Radio.d.ts +5 -5
  16. package/dist/Form/Select/Option.d.ts +3 -4
  17. package/dist/Form/Select/Select.d.ts +4 -4
  18. package/dist/Form/Textarea/Textarea.d.ts +9 -5
  19. package/dist/Form/Toggle/Toggle.d.ts +3 -3
  20. package/dist/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.d.ts +4 -3
  21. package/dist/Form/Wrapper/InputWrapper/InputWrapper.d.ts +5 -5
  22. package/dist/Form/Wrapper/RadioWrapper/RadioWrapper.d.ts +4 -4
  23. package/dist/Form/Wrapper/SelectWrapper/SelectWrapper.d.ts +8 -4
  24. package/dist/Form/Wrapper/TextareaWrapper/TextareaWrapper.d.ts +3 -3
  25. package/dist/Form/Wrapper/Wrapper/Wrapper.d.ts +6 -6
  26. package/dist/Form/form.interfaces.d.ts +4 -3
  27. package/dist/Icon/Icon.d.ts +4 -4
  28. package/dist/Link/Link.d.ts +3 -5
  29. package/dist/Notifications/BaseModal/BaseModal.d.ts +17 -0
  30. package/dist/Notifications/BaseModal/BaseModalActions/BaseModalActions.d.ts +5 -0
  31. package/dist/Notifications/BaseModal/BaseModalContent/BaseModalContent.d.ts +8 -0
  32. package/dist/{BaseModal → Notifications/BaseModal}/BaseModalContext.d.ts +0 -0
  33. package/dist/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.d.ts +8 -0
  34. package/dist/Notifications/Dialog/Dialog.d.ts +19 -0
  35. package/dist/Notifications/Dialog/DialogActions/DialogActions.d.ts +6 -0
  36. package/dist/Notifications/Dialog/DialogTitle/DialogTitle.d.ts +6 -0
  37. package/dist/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.d.ts +13 -0
  38. package/dist/Notifications/DiscardChangesModal/DiscardChangesModal.d.ts +13 -0
  39. package/dist/{Modal → Notifications/Modal}/Modal.d.ts +0 -0
  40. package/dist/{Modal → Notifications/Modal}/ModalActions/ModalActions.d.ts +0 -0
  41. package/dist/{Modal → Notifications/Modal}/ModalContent/ModalContent.d.ts +0 -0
  42. package/dist/{Modal → Notifications/Modal}/ModalHeader/ModalHeader.d.ts +0 -0
  43. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.d.ts +0 -0
  44. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.d.ts +0 -0
  45. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarProvider.d.ts +1 -1
  46. package/dist/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarStateProvider.d.ts +0 -0
  47. package/dist/{Snackbar → Notifications/Snackbar}/interfaces.d.ts +0 -0
  48. package/dist/{Snackbar → Notifications/Snackbar}/useSnackbar.d.ts +0 -0
  49. package/dist/Pagination/Pagination.d.ts +19 -0
  50. package/dist/Popover/Popover.d.ts +3 -3
  51. package/dist/Tabs/Tab.d.ts +11 -0
  52. package/dist/Tabs/TabButton.d.ts +10 -0
  53. package/dist/Tabs/TabPanel.d.ts +8 -0
  54. package/dist/Tabs/Tabs.d.ts +9 -0
  55. package/dist/TextEllipsis/TextEllipsis.d.ts +6 -0
  56. package/dist/Tiles/Tile.d.ts +6 -7
  57. package/dist/Tiles/Tiles.d.ts +3 -3
  58. package/dist/Tooltip/Tooltip.d.ts +3 -3
  59. package/dist/Typography/Typography.d.ts +6 -4
  60. package/dist/Wizard/BaseWizardSteps/BaseWizardSteps.d.ts +3 -3
  61. package/dist/Wizard/WizardSteps/WizardSteps.d.ts +3 -3
  62. package/dist/_BaseStyling_/BaseStyling.d.ts +9 -0
  63. package/dist/hooks/useRepeater.d.ts +10 -0
  64. package/dist/hooks/useSpacing.d.ts +2 -2
  65. package/dist/hooks/useWrapper.d.ts +1 -1
  66. package/dist/index.d.ts +12 -7
  67. package/dist/interfaces.d.ts +2 -11
  68. package/dist/react-lib-components.cjs.development.js +1861 -1287
  69. package/dist/react-lib-components.cjs.development.js.map +1 -1
  70. package/dist/react-lib-components.cjs.production.min.js +1 -1
  71. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  72. package/dist/react-lib-components.esm.js +1858 -1289
  73. package/dist/react-lib-components.esm.js.map +1 -1
  74. package/dist/util/helper.d.ts +6 -1
  75. package/package.json +30 -24
  76. package/src/Breadcrumbs/Breadcrumbs.tsx +39 -37
  77. package/src/Button/BaseButton.test.tsx +65 -19
  78. package/src/Button/BaseButton.tsx +2 -3
  79. package/src/Button/Button.test.tsx +63 -17
  80. package/src/Button/Button.tsx +15 -4
  81. package/src/Button/IconButton.test.tsx +57 -22
  82. package/src/Button/IconButton.tsx +21 -12
  83. package/src/ContextMenu/ContextMenu.test.tsx +27 -1
  84. package/src/ContextMenu/ContextMenu.tsx +70 -65
  85. package/src/Form/Checkbox/Checkbox.module.scss +4 -0
  86. package/src/Form/Checkbox/Checkbox.test.tsx +28 -2
  87. package/src/Form/Checkbox/Checkbox.tsx +132 -117
  88. package/src/Form/Fieldset/Fieldset.module.scss +11 -1
  89. package/src/Form/Fieldset/Fieldset.test.tsx +30 -4
  90. package/src/Form/Fieldset/Fieldset.tsx +101 -43
  91. package/src/Form/FormControl/FormControl.test.tsx +27 -1
  92. package/src/Form/FormControl/FormControl.tsx +37 -37
  93. package/src/Form/FormGroup/FormGroup.test.tsx +27 -1
  94. package/src/Form/FormGroup/FormGroup.tsx +64 -58
  95. package/src/Form/FormHelperText/FormHelperText.test.tsx +27 -1
  96. package/src/Form/FormHelperText/FormHelperText.tsx +20 -16
  97. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.test.tsx +78 -0
  98. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.tsx +61 -55
  99. package/src/Form/Input/Input.module.scss +34 -15
  100. package/src/Form/Input/Input.test.tsx +27 -1
  101. package/src/Form/Input/Input.tsx +88 -47
  102. package/src/Form/Label/Label.test.tsx +27 -1
  103. package/src/Form/Label/Label.tsx +18 -14
  104. package/src/Form/Radio/Radio.module.scss +4 -0
  105. package/src/Form/Radio/Radio.test.tsx +28 -2
  106. package/src/Form/Radio/Radio.tsx +98 -80
  107. package/src/Form/Select/Option.test.tsx +27 -1
  108. package/src/Form/Select/Option.tsx +49 -42
  109. package/src/Form/Select/Select.module.scss +5 -1
  110. package/src/Form/Select/Select.test.tsx +224 -30
  111. package/src/Form/Select/Select.tsx +248 -182
  112. package/src/Form/Textarea/Textarea.module.scss +2 -1
  113. package/src/Form/Textarea/Textarea.test.tsx +28 -2
  114. package/src/Form/Textarea/Textarea.tsx +44 -29
  115. package/src/Form/Toggle/Toggle.module.scss +9 -0
  116. package/src/Form/Toggle/Toggle.test.tsx +27 -1
  117. package/src/Form/Toggle/Toggle.tsx +25 -12
  118. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.test.tsx +28 -2
  119. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.tsx +45 -48
  120. package/src/Form/Wrapper/InputWrapper/InputWrapper.module.scss +17 -1
  121. package/src/Form/Wrapper/InputWrapper/InputWrapper.test.tsx +89 -1
  122. package/src/Form/Wrapper/InputWrapper/InputWrapper.tsx +134 -74
  123. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.test.tsx +1 -1
  124. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.tsx +64 -59
  125. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.module.scss +1 -1
  126. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.test.tsx +43 -1
  127. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +55 -44
  128. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.module.scss +5 -7
  129. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.test.tsx +43 -1
  130. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.tsx +100 -85
  131. package/src/Form/Wrapper/Wrapper/Wrapper.module.scss +1 -1
  132. package/src/Form/Wrapper/Wrapper/Wrapper.test.tsx +27 -1
  133. package/src/Form/Wrapper/Wrapper/Wrapper.tsx +76 -71
  134. package/src/Form/form.interfaces.ts +4 -3
  135. package/src/Icon/Icon.module.scss +4 -0
  136. package/src/Icon/Icon.test.tsx +30 -2
  137. package/src/Icon/Icon.tsx +5 -5
  138. package/src/Link/Link.test.tsx +27 -1
  139. package/src/Link/Link.tsx +4 -6
  140. package/src/{BaseModal → Notifications/BaseModal}/BaseModal.module.scss +0 -0
  141. package/src/{BaseModal → Notifications/BaseModal}/BaseModal.test.tsx +35 -16
  142. package/src/Notifications/BaseModal/BaseModal.tsx +105 -0
  143. package/src/{BaseModal → Notifications/BaseModal}/BaseModalActions/BaseModalActions.module.scss +0 -0
  144. package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.test.tsx +42 -0
  145. package/src/Notifications/BaseModal/BaseModalActions/BaseModalActions.tsx +16 -0
  146. package/src/{BaseModal → Notifications/BaseModal}/BaseModalContent/BaseModalContent.module.scss +0 -0
  147. package/src/{BaseModal → Notifications/BaseModal}/BaseModalContent/BaseModalContent.test.tsx +27 -1
  148. package/src/Notifications/BaseModal/BaseModalContent/BaseModalContent.tsx +36 -0
  149. package/src/{BaseModal → Notifications/BaseModal}/BaseModalContext.ts +0 -0
  150. package/src/{BaseModal → Notifications/BaseModal}/BaseModalHeader/BaseModalHeader.module.scss +0 -0
  151. package/src/{BaseModal → Notifications/BaseModal}/BaseModalHeader/BaseModalHeader.test.tsx +29 -1
  152. package/src/Notifications/BaseModal/BaseModalHeader/BaseModalHeader.tsx +30 -0
  153. package/src/{Dialog → Notifications/Dialog}/Dialog.module.scss +0 -0
  154. package/src/{Dialog → Notifications/Dialog}/Dialog.test.tsx +52 -17
  155. package/src/Notifications/Dialog/Dialog.tsx +113 -0
  156. package/src/{Dialog → Notifications/Dialog}/DialogActions/DialogActions.module.scss +0 -0
  157. package/src/Notifications/Dialog/DialogActions/DialogActions.test.tsx +51 -0
  158. package/src/Notifications/Dialog/DialogActions/DialogActions.tsx +24 -0
  159. package/src/{Dialog → Notifications/Dialog}/DialogTitle/DialogTitle.module.scss +0 -0
  160. package/src/Notifications/Dialog/DialogTitle/DialogTitle.test.tsx +44 -0
  161. package/src/Notifications/Dialog/DialogTitle/DialogTitle.tsx +20 -0
  162. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.test.tsx +95 -0
  163. package/src/Notifications/DiscardChangesModal/DiscardChangesDialog/DiscardChangesDialog.tsx +55 -0
  164. package/src/Notifications/DiscardChangesModal/DiscardChangesModal.test.tsx +162 -0
  165. package/src/Notifications/DiscardChangesModal/DiscardChangesModal.tsx +61 -0
  166. package/src/{Modal → Notifications/Modal}/Modal.test.tsx +0 -0
  167. package/src/{Modal → Notifications/Modal}/Modal.tsx +0 -0
  168. package/src/{Modal → Notifications/Modal}/ModalActions/ModalActions.tsx +0 -0
  169. package/src/{Modal → Notifications/Modal}/ModalContent/ModalContent.tsx +0 -0
  170. package/src/{Modal → Notifications/Modal}/ModalHeader/ModalHeader.tsx +0 -0
  171. package/src/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.module.scss +0 -0
  172. package/src/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.test.tsx +0 -0
  173. package/src/{Snackbar → Notifications/Snackbar}/SnackbarContainer/SnackbarContainer.tsx +0 -0
  174. package/src/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.module.scss +1 -1
  175. package/src/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.test.tsx +0 -0
  176. package/src/{Snackbar → Notifications/Snackbar}/SnackbarItem/SnackbarItem.tsx +6 -7
  177. package/src/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarProvider.test.tsx +0 -0
  178. package/src/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarProvider.tsx +2 -2
  179. package/src/{Snackbar → Notifications/Snackbar}/SnackbarProvider/SnackbarStateProvider.tsx +0 -0
  180. package/src/{Snackbar → Notifications/Snackbar}/interfaces.ts +0 -0
  181. package/src/{Snackbar → Notifications/Snackbar}/useSnackbar.ts +0 -0
  182. package/src/Pagination/Pagination.module.scss +120 -0
  183. package/src/Pagination/Pagination.test.tsx +176 -0
  184. package/src/Pagination/Pagination.tsx +205 -0
  185. package/src/Popover/Popover.test.tsx +3 -3
  186. package/src/Popover/Popover.tsx +3 -3
  187. package/src/Tabs/Tab.test.tsx +71 -0
  188. package/src/Tabs/Tab.tsx +17 -0
  189. package/src/Tabs/TabButton.module.scss +36 -0
  190. package/src/Tabs/TabButton.test.tsx +77 -0
  191. package/src/Tabs/TabButton.tsx +58 -0
  192. package/src/Tabs/TabPanel.module.scss +7 -0
  193. package/src/Tabs/TabPanel.test.tsx +76 -0
  194. package/src/Tabs/TabPanel.tsx +27 -0
  195. package/src/Tabs/Tabs.module.scss +41 -0
  196. package/src/Tabs/Tabs.test.tsx +268 -0
  197. package/src/Tabs/Tabs.tsx +149 -0
  198. package/src/TextEllipsis/TextEllipsis.module.scss +18 -0
  199. package/src/TextEllipsis/TextEllipsis.test.tsx +80 -0
  200. package/src/TextEllipsis/TextEllipsis.tsx +55 -0
  201. package/src/Tiles/Tile.module.scss +1 -1
  202. package/src/Tiles/Tile.test.tsx +48 -12
  203. package/src/Tiles/Tile.tsx +68 -34
  204. package/src/Tiles/Tiles.test.tsx +38 -10
  205. package/src/Tiles/Tiles.tsx +42 -39
  206. package/src/Tooltip/Tooltip.test.tsx +27 -1
  207. package/src/Tooltip/Tooltip.tsx +104 -92
  208. package/src/Typography/Typography.test.tsx +27 -1
  209. package/src/Typography/Typography.tsx +66 -68
  210. package/src/Wizard/BaseWizardSteps/BaseWizardSteps.tsx +67 -62
  211. package/src/Wizard/Wizard.tsx +2 -2
  212. package/src/Wizard/WizardActions/WizardActions.tsx +3 -3
  213. package/src/Wizard/WizardSteps/WizardSteps.tsx +24 -21
  214. package/src/_BaseStyling_/BaseStyling.tsx +19 -1
  215. package/src/hooks/usePosition.test.tsx +3 -3
  216. package/src/hooks/useRepeater.test.tsx +139 -0
  217. package/src/hooks/useRepeater.ts +34 -0
  218. package/src/hooks/useSpacing.ts +1 -1
  219. package/src/hooks/useWrapper.ts +7 -2
  220. package/src/index.ts +20 -8
  221. package/src/interfaces.ts +2 -12
  222. package/src/util/helper.test.tsx +38 -1
  223. package/src/util/helper.tsx +21 -0
  224. package/dist/BaseModal/BaseModal.d.ts +0 -16
  225. package/dist/BaseModal/BaseModalActions/BaseModalActions.d.ts +0 -5
  226. package/dist/BaseModal/BaseModalContent/BaseModalContent.d.ts +0 -8
  227. package/dist/BaseModal/BaseModalHeader/BaseModalHeader.d.ts +0 -8
  228. package/dist/Dialog/Dialog.d.ts +0 -18
  229. package/dist/Dialog/DialogActions/DialogActions.d.ts +0 -6
  230. package/dist/Dialog/DialogTitle/DialogTitle.d.ts +0 -6
  231. package/src/BaseModal/BaseModal.tsx +0 -113
  232. package/src/BaseModal/BaseModalActions/BaseModalActions.test.tsx +0 -17
  233. package/src/BaseModal/BaseModalActions/BaseModalActions.tsx +0 -14
  234. package/src/BaseModal/BaseModalContent/BaseModalContent.tsx +0 -35
  235. package/src/BaseModal/BaseModalHeader/BaseModalHeader.tsx +0 -28
  236. package/src/Dialog/Dialog.tsx +0 -96
  237. package/src/Dialog/DialogActions/DialogActions.test.tsx +0 -25
  238. package/src/Dialog/DialogActions/DialogActions.tsx +0 -21
  239. package/src/Dialog/DialogTitle/DialogTitle.test.tsx +0 -18
  240. package/src/Dialog/DialogTitle/DialogTitle.tsx +0 -18
@@ -1,98 +1,116 @@
1
- import React from 'react';
1
+ import React, { ComponentPropsWithRef } from 'react';
2
2
  import { Icon, Icons } from '../../Icon/Icon';
3
3
  import { Props as HelperProps } from '../FormHelperText/FormHelperText';
4
4
  import classes from './Radio.module.scss';
5
5
  import { useFormSelector } from '../../hooks/useFormSelector';
6
6
  import { FormSelector } from '../form.interfaces';
7
- import { HTMLProps } from '../../interfaces';
8
- import { FormSelectorWrapper } from '../FormSelectorWrapper/FormSelectorWrapper';
7
+ import {
8
+ FormSelectorWrapper,
9
+ Props as FormSelectorWrapperProps,
10
+ } from '../FormSelectorWrapper/FormSelectorWrapper';
9
11
 
10
- export interface Props extends FormSelector<HTMLInputElement> {
12
+ export interface Props extends ComponentPropsWithRef<'input'>, FormSelector {
11
13
  children: string;
12
14
  value: string;
13
- wrapperProps?: HTMLProps<HTMLDivElement>;
15
+ formSelectorWrapperProps?: FormSelectorWrapperProps;
14
16
  helperProps?: HelperProps;
15
17
  }
16
18
 
17
- export const Radio = ({
18
- children,
19
- disabled,
20
- className,
21
- value,
22
- name,
23
- helperText,
24
- parentErrorId,
25
- parentHelperId,
26
- error,
27
- errorMessage,
28
- checked = false,
29
- wrapperProps,
30
- helperProps,
31
- onChange,
32
- ...rest
33
- }: Props) => {
34
- const { errorId, identifier, describedBy } = useFormSelector({
35
- name,
36
- helperText,
37
- parentErrorId,
38
- errorMessage,
39
- error,
40
- parentHelperId,
41
- });
19
+ export const Radio = React.forwardRef<HTMLInputElement, Props>(
20
+ (
21
+ {
22
+ children,
23
+ disabled,
24
+ className,
25
+ value,
26
+ name,
27
+ helperText,
28
+ parentErrorId,
29
+ parentHelperId,
30
+ error,
31
+ errorMessage,
32
+ checked = false,
33
+ formSelectorWrapperProps,
34
+ helperProps,
35
+ onChange,
36
+ ...rest
37
+ }: Props,
38
+ ref
39
+ ) => {
40
+ const { errorId, identifier, describedBy } = useFormSelector({
41
+ name,
42
+ helperText,
43
+ parentErrorId,
44
+ errorMessage,
45
+ error,
46
+ parentHelperId,
47
+ });
42
48
 
43
- const onChangeHandler = (event: React.ChangeEvent<HTMLInputElement> | React.MouseEvent) => {
44
- if (disabled) {
45
- return;
46
- }
47
- /** We have to clone the native event we got here and change the "target" property to the value. Otherwise, this regular event has "on" as it's event.target.value, which is useless. */
48
- const nativeEvent: any = event.nativeEvent || event;
49
- const clonedEvent = new nativeEvent.constructor(nativeEvent.type, nativeEvent);
49
+ const onChangeHandler = (event: React.ChangeEvent<HTMLInputElement> | React.MouseEvent) => {
50
+ if (disabled) {
51
+ return;
52
+ }
53
+ /** We have to clone the native event we got here and change the "target" property to the value. Otherwise, this regular event has "on" as it's event.target.value, which is useless. */
54
+ const nativeEvent: any = event.nativeEvent || event;
55
+ const clonedEvent = new nativeEvent.constructor(nativeEvent.type, nativeEvent);
50
56
 
51
- Object.defineProperty(clonedEvent, 'target', {
52
- writable: true,
53
- value: { value: value },
54
- });
57
+ Object.defineProperty(clonedEvent, 'target', {
58
+ writable: true,
59
+ value: { value: value },
60
+ });
55
61
 
56
- onChange && onChange(clonedEvent);
57
- };
62
+ onChange && onChange(clonedEvent);
63
+ };
58
64
 
59
- /** Default return value is the default radio */
60
- return (
61
- <FormSelectorWrapper
62
- {...wrapperProps}
63
- className={`${classes['radio-wrapper']} ${className ?? ''}`}
64
- containerProps={{ className: classes['radio-container'] }}
65
- helperText={helperText}
66
- helperProps={helperProps}
67
- parentErrorId={parentErrorId}
68
- errorId={errorId}
69
- errorMessage={errorMessage}
70
- error={error}
71
- disabled={disabled}
72
- identifier={identifier}
73
- >
74
- <input
75
- {...rest}
65
+ /** Default return value is the default radio */
66
+ return (
67
+ <FormSelectorWrapper
68
+ {...formSelectorWrapperProps}
69
+ className={`${classes['radio-wrapper']} ${className ?? ''}`}
70
+ containerProps={{ className: classes['radio-container'] }}
71
+ helperText={helperText}
72
+ helperProps={helperProps}
73
+ parentErrorId={parentErrorId}
74
+ errorId={errorId}
75
+ errorMessage={errorMessage}
76
+ error={error}
76
77
  disabled={disabled}
77
- tabIndex={0}
78
- className={`${classes['native-input']} ${error ? classes['error'] : ''}`}
79
- onChange={onChangeHandler}
80
- checked={checked}
81
- aria-invalid={error ? true : false}
82
- aria-checked={checked}
83
- aria-describedby={describedBy}
84
- name={name}
85
- value={value}
86
- id={`${identifier}-radio`}
87
- type="radio"
88
- />
78
+ identifier={identifier}
79
+ >
80
+ <input
81
+ {...rest}
82
+ ref={ref}
83
+ disabled={disabled}
84
+ tabIndex={0}
85
+ className={`${classes['native-input']} ${error ? classes['error'] : ''}`}
86
+ onChange={onChangeHandler}
87
+ checked={checked}
88
+ aria-invalid={error ? true : false}
89
+ aria-checked={checked}
90
+ aria-describedby={describedBy}
91
+ name={name}
92
+ value={value}
93
+ id={`${identifier}-radio`}
94
+ type="radio"
95
+ />
89
96
 
90
- {checked && <Icon className={classes.input} icon={Icons.Radio} />}
91
- {!checked && <Icon className={classes.input} icon={Icons.Circle} />}
97
+ {checked && (
98
+ <Icon
99
+ className={`${classes['input']} ${disabled ? classes['disabled'] : ''}`}
100
+ icon={Icons.Radio}
101
+ />
102
+ )}
103
+ {!checked && (
104
+ <Icon
105
+ className={`${classes['input']} ${disabled ? classes['disabled'] : ''}`}
106
+ icon={Icons.Circle}
107
+ />
108
+ )}
92
109
 
93
- <label onClick={onChangeHandler} htmlFor={`${identifier}-radio`}>
94
- {children}
95
- </label>
96
- </FormSelectorWrapper>
97
- );
98
- };
110
+ <label onClick={onChangeHandler} htmlFor={`${identifier}-radio`}>
111
+ {children}
112
+ </label>
113
+ </FormSelectorWrapper>
114
+ );
115
+ }
116
+ );
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect, useRef } from 'react';
2
2
  import { Option } from './Option';
3
3
  import { render } from '@testing-library/react';
4
4
 
@@ -13,3 +13,29 @@ describe('Option should render', () => {
13
13
  expect(component).toBeDefined();
14
14
  });
15
15
  });
16
+
17
+ describe('ref should work', () => {
18
+ it('should give back the proper data prop, this also checks if the component propagates ...rest properly', () => {
19
+ const ExampleComponent = ({
20
+ propagateRef,
21
+ }: {
22
+ propagateRef?: (ref: React.RefObject<HTMLElement>) => void;
23
+ }) => {
24
+ const ref = useRef(null);
25
+
26
+ useEffect(() => {
27
+ if (ref.current) {
28
+ propagateRef && propagateRef(ref);
29
+ }
30
+ }, [ref]);
31
+
32
+ return <Option value="test" children="Test" data-ref="testing" ref={ref} />;
33
+ };
34
+
35
+ const refCheck = (ref: React.RefObject<HTMLElement>) => {
36
+ expect(ref.current).toHaveAttribute('data-ref', 'testing');
37
+ };
38
+
39
+ render(<ExampleComponent propagateRef={refCheck} />);
40
+ });
41
+ });
@@ -1,8 +1,7 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { ComponentPropsWithRef, useEffect, useState } from 'react';
2
2
  import classes from './Select.module.scss';
3
- import { HTMLProps } from '../../interfaces';
4
3
 
5
- export interface Props extends HTMLProps<HTMLLIElement> {
4
+ export interface Props extends ComponentPropsWithRef<'li'> {
6
5
  children: string;
7
6
  value: string;
8
7
  disabled?: boolean;
@@ -12,46 +11,54 @@ export interface Props extends HTMLProps<HTMLLIElement> {
12
11
  onOptionSelect?: (event: React.SyntheticEvent<HTMLLIElement>) => void;
13
12
  }
14
13
 
15
- export const Option = ({
16
- children,
17
- className,
18
- selected = false,
19
- onOptionSelect,
20
- disabled,
21
- filter,
22
- ...rest
23
- }: Props) => {
24
- const [showOption, setShowOption] = useState(true);
14
+ export const Option = React.forwardRef<HTMLLIElement, Props>(
15
+ (
16
+ {
17
+ children,
18
+ className,
19
+ selected = false,
20
+ onOptionSelect,
21
+ disabled,
22
+ filter,
23
+ value,
24
+ ...rest
25
+ }: Props,
26
+ ref
27
+ ) => {
28
+ const [showOption, setShowOption] = useState(true);
25
29
 
26
- const onSelectHandler = (event: React.SyntheticEvent<HTMLLIElement>) => {
27
- if (onOptionSelect) onOptionSelect(event);
28
- };
30
+ const onSelectHandler = (event: React.SyntheticEvent<HTMLLIElement>) => {
31
+ if (onOptionSelect) onOptionSelect(event);
32
+ };
29
33
 
30
- useEffect(() => {
31
- if (filter) {
32
- setShowOption(children.toLowerCase().match(filter.toLowerCase()) !== null);
33
- } else {
34
- setShowOption(true);
35
- }
36
- }, [filter]);
34
+ useEffect(() => {
35
+ if (filter) {
36
+ setShowOption(children.toLowerCase().match(filter.toLowerCase()) !== null);
37
+ } else {
38
+ setShowOption(true);
39
+ }
40
+ }, [filter]);
37
41
 
38
- if (!showOption) return null;
42
+ if (!showOption) return null;
39
43
 
40
- return (
41
- <li
42
- {...rest}
43
- className={`${selected ? classes['selected-option'] : ''} ${
44
- disabled ? classes.disabled : ''
45
- } ${className ?? ''}`}
46
- onClick={onSelectHandler}
47
- onKeyPress={(e) => {
48
- e.key === 'Enter' && onSelectHandler(e);
49
- }}
50
- aria-selected={selected}
51
- role="option"
52
- tabIndex={disabled ? -1 : 0}
53
- >
54
- {children}
55
- </li>
56
- );
57
- };
44
+ return (
45
+ <li
46
+ {...rest}
47
+ ref={ref}
48
+ data-value={value}
49
+ className={`${selected ? classes['selected-option'] : ''} ${
50
+ disabled ? classes.disabled : ''
51
+ } ${className ?? ''}`}
52
+ onClick={onSelectHandler}
53
+ onKeyPress={(e) => {
54
+ e.key === 'Enter' && onSelectHandler(e);
55
+ }}
56
+ aria-selected={selected}
57
+ role="option"
58
+ tabIndex={disabled ? -1 : 0}
59
+ >
60
+ {children}
61
+ </li>
62
+ );
63
+ }
64
+ );
@@ -13,6 +13,10 @@ $listItemHeight: 2.125rem;
13
13
  background-color: #fff;
14
14
  font-size: var(--font-size);
15
15
 
16
+ [data-display] {
17
+ color: var(--greyed-out);
18
+ }
19
+
16
20
  &.expanded {
17
21
  border: var(--input-border-width) solid transparent;
18
22
 
@@ -62,6 +66,7 @@ $listItemHeight: 2.125rem;
62
66
  width: 100%;
63
67
  opacity: 0;
64
68
  pointer-events: none;
69
+ overflow: auto;
65
70
  }
66
71
 
67
72
  ul {
@@ -74,7 +79,6 @@ $listItemHeight: 2.125rem;
74
79
  border-radius: var(--input-border-radius);
75
80
  color: var(--default);
76
81
  text-align: left;
77
- overflow: auto;
78
82
  max-height: calc($listItemHeight * 10);
79
83
 
80
84
  li {
@@ -1,32 +1,41 @@
1
- import React, { ReactElement } from 'react';
2
- import { Select, Props } from './Select';
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { Select as SelectComponent, Props } from './Select';
3
3
  import { render } from '@testing-library/react';
4
4
  import { Option } from './Option';
5
5
  import userEvent from '@testing-library/user-event';
6
6
 
7
- const createSelect = (amountOfOptions = 5, params?: Props) => {
8
- const renderOptions = (amount: number): ReactElement[] => {
9
- const returnArr: ReactElement[] = [];
10
-
11
- for (let i = 0; i < amount; i++) {
12
- returnArr.push(<Option key={i} value={`option${i}`}>{`Option${i}`}</Option>);
13
- }
14
-
15
- return returnArr;
16
- };
17
-
18
- const queries = render(
19
- <Select onChange={jest.fn()} {...params} data-testid="custom-select">
20
- {renderOptions(amountOfOptions)}
21
- </Select>
22
- );
23
- const select = queries.getByTestId('custom-select')!;
24
- const button = select.querySelector('button');
7
+ const defaultParams: Props = {
8
+ name: 'Example select',
9
+ children: [
10
+ <Option value="option1">Test</Option>,
11
+ <Option value="option2">Test2</Option>,
12
+ <Option value="option3">Test3</Option>,
13
+ <Option value="option4">Test4</Option>,
14
+ <Option value="option5">Test5</Option>,
15
+ <Option value="option6">Test6</Option>,
16
+ <Option value="option7">Test7</Option>,
17
+ <Option value="option8">Test8</Option>,
18
+ <Option value="option9">Test9</Option>,
19
+ <Option value="option10">Test10</Option>,
20
+ <Option value="option11">Test11</Option>,
21
+ <Option value="option12">Test12</Option>,
22
+ <Option value="option13">Test13</Option>,
23
+ <Option value="option14">Test14</Option>,
24
+ <Option value="option15">Test15</Option>,
25
+ <Option value="option16">Test16</Option>,
26
+ <Option value="option17">Test17</Option>,
27
+ ],
28
+ value: '',
29
+ };
25
30
 
26
- if (button) {
27
- userEvent.click(button);
31
+ const createSelect = (params?: (defaultParams: Props) => Props) => {
32
+ let parameters: Props = defaultParams;
33
+ if (params) {
34
+ parameters = params(defaultParams);
28
35
  }
29
-
36
+ const queries = render(<SelectComponent {...parameters} data-testid="select" />);
37
+ const select = queries.getByTestId('select');
38
+ const button = select.querySelector('button');
30
39
  const list = select.querySelector('ul[role="listbox"]');
31
40
  const dropdownWrapper = select.querySelector('.list-wrapper');
32
41
 
@@ -41,39 +50,112 @@ const createSelect = (amountOfOptions = 5, params?: Props) => {
41
50
 
42
51
  describe('Select should render', () => {
43
52
  it('renders with 5 options and proper attributes', () => {
44
- const { select, button, list } = createSelect(5);
53
+ const { select, button, list } = createSelect((defaultParams) => ({
54
+ ...defaultParams,
55
+ children: [
56
+ <Option value="option1">Test</Option>,
57
+ <Option value="option2">Test2</Option>,
58
+ <Option value="option3">Test3</Option>,
59
+ <Option value="option4">Test4</Option>,
60
+ <Option value="option5">Test5</Option>,
61
+ ],
62
+ placeholder: 'Placeholder',
63
+ }));
64
+
65
+ if (button) {
66
+ userEvent.click(button);
67
+ }
45
68
 
46
69
  expect(select).toBeDefined();
70
+ expect(select.querySelector('.placeholder')).toBeInTheDocument();
47
71
  expect(button?.getAttribute('aria-expanded')).toBe('true');
48
72
  expect(button?.getAttribute('aria-disabled')).toBe('false');
49
73
  expect(list).toBeDefined();
50
74
  expect(list?.querySelectorAll("li[role='option']").length).toBe(5);
51
75
  });
76
+
77
+ it('should be disabled', () => {
78
+ const { select, button } = createSelect((defaultParams) => ({
79
+ ...defaultParams,
80
+ disabled: true,
81
+ }));
82
+
83
+ expect(select).toHaveClass('disabled');
84
+ expect(button).toHaveAttribute('aria-disabled', 'true');
85
+ expect(button).toHaveAttribute('disabled');
86
+ });
87
+
88
+ it('should have an error', () => {
89
+ const { select, button } = createSelect((defaultParams) => ({
90
+ ...defaultParams,
91
+ error: true,
92
+ value: 'option4',
93
+ }));
94
+
95
+ expect(select).toHaveClass('error');
96
+ expect(button).toHaveAttribute('aria-invalid', 'true');
97
+ expect(select.querySelector('[data-clear]')).not.toBeInTheDocument();
98
+ });
99
+ });
100
+
101
+ describe('ref should work', () => {
102
+ it('should give back the proper data prop, this also checks if the component propagates ...rest properly', () => {
103
+ const ExampleComponent = ({
104
+ propagateRef,
105
+ }: {
106
+ propagateRef?: (ref: React.RefObject<HTMLElement>) => void;
107
+ }) => {
108
+ const ref = useRef(null);
109
+
110
+ useEffect(() => {
111
+ if (ref.current) {
112
+ propagateRef && propagateRef(ref);
113
+ }
114
+ }, [ref]);
115
+
116
+ return <SelectComponent {...defaultParams} data-ref="testing" ref={ref} />;
117
+ };
118
+
119
+ const refCheck = (ref: React.RefObject<HTMLElement>) => {
120
+ expect(ref.current!.nodeName).toBe('SELECT');
121
+ };
122
+
123
+ render(<ExampleComponent propagateRef={refCheck} />);
124
+ });
52
125
  });
53
126
 
54
127
  describe('Select should render with search', () => {
55
128
  it('shows the search and filtering works', () => {
56
- const { select, list, dropdownWrapper } = createSelect(20);
129
+ const { select, list, button, dropdownWrapper } = createSelect();
130
+
131
+ if (button) {
132
+ userEvent.click(button);
133
+ }
57
134
 
58
135
  const search = dropdownWrapper?.querySelector('input');
59
136
 
60
137
  expect(select).toBeTruthy();
61
138
  expect(search).toBeTruthy();
62
- expect(list?.querySelectorAll("li[role='option']").length).toBe(20);
139
+ expect(list?.querySelectorAll("li[role='option']").length).toBe(17);
63
140
 
64
141
  if (search) {
65
- userEvent.type(search, '19');
142
+ userEvent.type(search, '17');
66
143
  }
67
144
 
68
145
  expect(list?.querySelectorAll("li[role='option']").length).toBe(1);
69
- expect(list?.querySelector("li[role='option']")?.innerHTML).toBe('Option19');
146
+ expect(list?.querySelector("li[role='option']")?.innerHTML).toBe('Test17');
70
147
  });
71
148
  });
72
149
 
73
150
  describe('Selecting options using keyboard', () => {
74
151
  it('should focus through list items and select on enterpress', () => {
75
- const { select, list } = createSelect(7);
152
+ const { select, button, list } = createSelect();
76
153
 
154
+ if (button) {
155
+ userEvent.click(button);
156
+ }
157
+
158
+ userEvent.tab();
77
159
  userEvent.tab();
78
160
  expect(list?.querySelectorAll('li')[0]).toHaveFocus();
79
161
  userEvent.tab();
@@ -90,7 +172,119 @@ describe('Selecting options using keyboard', () => {
90
172
  userEvent.keyboard('{enter}');
91
173
 
92
174
  setTimeout(() => {
93
- expect(select.querySelector('button > span > span')?.innerHTML).toBe('Option5');
175
+ expect(select.querySelector('button > span > span')?.innerHTML).toBe('Test5');
94
176
  }, 50);
95
177
  });
96
178
  });
179
+
180
+ describe('Expanded should be false whenever we click the body', () => {
181
+ it('closes select on body click', () => {
182
+ const { button } = createSelect();
183
+
184
+ if (button) {
185
+ userEvent.click(button);
186
+ }
187
+
188
+ expect(button).toHaveAttribute('aria-expanded', 'true');
189
+ userEvent.click(document.body);
190
+ expect(button).toHaveAttribute('aria-expanded', 'false');
191
+ });
192
+ });
193
+
194
+ describe('List expansion', () => {
195
+ it('should expand upwards', () => {
196
+ const { select, button } = createSelect();
197
+
198
+ Object.defineProperty(window, 'innerHeight', { value: 500, writable: true });
199
+
200
+ select.getBoundingClientRect = () => ({
201
+ x: 50,
202
+ y: 50,
203
+ width: 500,
204
+ height: 50,
205
+ top: 250,
206
+ left: 250,
207
+ right: 750,
208
+ bottom: 750,
209
+ toJSON: () => jest.fn(),
210
+ });
211
+
212
+ if (button) {
213
+ userEvent.click(button);
214
+ }
215
+
216
+ const listWrapper = select.querySelector('.list-wrapper');
217
+
218
+ expect(listWrapper).toHaveStyle({ bottom: '0px' });
219
+ });
220
+
221
+ it('should expand downwards with a max height set', () => {
222
+ const { select, getByRole } = createSelect();
223
+ const listWrapper = select.querySelector('.list-wrapper');
224
+
225
+ listWrapper!.getBoundingClientRect = () => ({
226
+ x: 50,
227
+ y: 50,
228
+ width: 500,
229
+ height: 600,
230
+ top: 10,
231
+ left: 250,
232
+ right: 750,
233
+ bottom: 50,
234
+ toJSON: () => jest.fn(),
235
+ });
236
+
237
+ Object.defineProperty(window, 'innerHeight', { value: 500, writable: true });
238
+
239
+ select.getBoundingClientRect = () => ({
240
+ x: 50,
241
+ y: 50,
242
+ width: 500,
243
+ height: 40,
244
+ top: 10,
245
+ left: 250,
246
+ right: 750,
247
+ bottom: 50,
248
+ toJSON: () => jest.fn(),
249
+ });
250
+
251
+ userEvent.click(document.body);
252
+ const button = getByRole('button');
253
+ userEvent.click(button);
254
+
255
+ expect(listWrapper).toHaveStyle({ maxHeight: '474px' });
256
+ expect(listWrapper).toHaveStyle({ top: '0px' });
257
+ });
258
+ });
259
+
260
+ describe('onClear method', () => {
261
+ it('should show a cross and fire the passed onClear function', async () => {
262
+ const onClearHandler = jest.fn();
263
+ const onChangeHandler = jest.fn();
264
+
265
+ const { button, container } = createSelect((defaultParams) => ({
266
+ ...defaultParams,
267
+ onClear: onClearHandler,
268
+ onChange: onChangeHandler,
269
+ value: 'option4',
270
+ }));
271
+
272
+ if (button) {
273
+ userEvent.click(button);
274
+ }
275
+
276
+ const optionToClick = container.querySelector('li[data-value="option5"]')!;
277
+ const onClearButton = container.querySelector('[data-clear]')!;
278
+
279
+ userEvent.click(optionToClick);
280
+ userEvent.click(onClearButton);
281
+
282
+ expect(onClearHandler).toHaveBeenCalled();
283
+ expect(onClearButton).toBeInTheDocument();
284
+ expect(container.querySelector('li[aria-selected="true"]')).toHaveTextContent('Test4');
285
+ expect(container.querySelector('.selected-option')).toHaveTextContent('Test4');
286
+ expect(onChangeHandler).toBeCalledWith(
287
+ expect.objectContaining({ target: expect.objectContaining({ value: 'option5' }) })
288
+ );
289
+ });
290
+ });