@onewelcome/react-lib-components 0.1.0-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 (248) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/dist/BaseModal/BaseModal.d.ts +16 -0
  4. package/dist/BaseModal/BaseModalActions/BaseModalActions.d.ts +5 -0
  5. package/dist/BaseModal/BaseModalContent/BaseModalContent.d.ts +8 -0
  6. package/dist/BaseModal/BaseModalContext.d.ts +2 -0
  7. package/dist/BaseModal/BaseModalHeader/BaseModalHeader.d.ts +8 -0
  8. package/dist/Breadcrumbs/Breadcrumbs.d.ts +9 -0
  9. package/dist/Button/BaseButton.d.ts +8 -0
  10. package/dist/Button/Button.d.ts +10 -0
  11. package/dist/Button/IconButton.d.ts +10 -0
  12. package/dist/ContextMenu/ContextMenu.d.ts +18 -0
  13. package/dist/ContextMenu/ContextMenuItem.d.ts +6 -0
  14. package/dist/Dialog/Dialog.d.ts +18 -0
  15. package/dist/Dialog/DialogActions/DialogActions.d.ts +6 -0
  16. package/dist/Dialog/DialogTitle/DialogTitle.d.ts +6 -0
  17. package/dist/Form/Checkbox/Checkbox.d.ts +13 -0
  18. package/dist/Form/Fieldset/Fieldset.d.ts +13 -0
  19. package/dist/Form/Form.d.ts +5 -0
  20. package/dist/Form/FormControl/FormControl.d.ts +8 -0
  21. package/dist/Form/FormGroup/FormGroup.d.ts +18 -0
  22. package/dist/Form/FormHelperText/FormHelperText.d.ts +7 -0
  23. package/dist/Form/FormSelectorWrapper/FormSelectorWrapper.d.ts +18 -0
  24. package/dist/Form/Input/Input.d.ts +12 -0
  25. package/dist/Form/Label/Label.d.ts +6 -0
  26. package/dist/Form/Radio/Radio.d.ts +11 -0
  27. package/dist/Form/Select/Option.d.ts +12 -0
  28. package/dist/Form/Select/Select.d.ts +15 -0
  29. package/dist/Form/Textarea/Textarea.d.ts +7 -0
  30. package/dist/Form/Toggle/Toggle.d.ts +6 -0
  31. package/dist/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.d.ts +8 -0
  32. package/dist/Form/Wrapper/InputWrapper/InputWrapper.d.ts +17 -0
  33. package/dist/Form/Wrapper/RadioWrapper/RadioWrapper.d.ts +10 -0
  34. package/dist/Form/Wrapper/SelectWrapper/SelectWrapper.d.ts +12 -0
  35. package/dist/Form/Wrapper/TextareaWrapper/TextareaWrapper.d.ts +14 -0
  36. package/dist/Form/Wrapper/Wrapper/Wrapper.d.ts +28 -0
  37. package/dist/Form/form.interfaces.d.ts +12 -0
  38. package/dist/Icon/Icon.d.ts +75 -0
  39. package/dist/Link/Link.d.ts +15 -0
  40. package/dist/Modal/Modal.d.ts +1 -0
  41. package/dist/Modal/ModalActions/ModalActions.d.ts +1 -0
  42. package/dist/Modal/ModalContent/ModalContent.d.ts +1 -0
  43. package/dist/Modal/ModalHeader/ModalHeader.d.ts +1 -0
  44. package/dist/Popover/Popover.d.ts +11 -0
  45. package/dist/Snackbar/SnackbarContainer/SnackbarContainer.d.ts +12 -0
  46. package/dist/Snackbar/SnackbarItem/SnackbarItem.d.ts +13 -0
  47. package/dist/Snackbar/SnackbarProvider/SnackbarProvider.d.ts +18 -0
  48. package/dist/Snackbar/SnackbarProvider/SnackbarStateProvider.d.ts +14 -0
  49. package/dist/Snackbar/interfaces.d.ts +10 -0
  50. package/dist/Snackbar/useSnackbar.d.ts +1 -0
  51. package/dist/Tiles/Tile.d.ts +16 -0
  52. package/dist/Tiles/Tiles.d.ts +6 -0
  53. package/dist/Tooltip/Tooltip.d.ts +11 -0
  54. package/dist/Typography/Typography.d.ts +12 -0
  55. package/dist/Wizard/BaseWizardSteps/BaseWizardSteps.d.ts +13 -0
  56. package/dist/Wizard/Wizard.d.ts +12 -0
  57. package/dist/Wizard/WizardActions/WizardActions.d.ts +12 -0
  58. package/dist/Wizard/WizardStateProvider.d.ts +12 -0
  59. package/dist/Wizard/WizardSteps/WizardSteps.d.ts +5 -0
  60. package/dist/Wizard/wizardStateReducer.d.ts +26 -0
  61. package/dist/_BaseStyling_/BaseStyling.d.ts +47 -0
  62. package/dist/hooks/useAnimation.d.ts +6 -0
  63. package/dist/hooks/useBodyClick.d.ts +1 -0
  64. package/dist/hooks/useFormSelector.d.ts +13 -0
  65. package/dist/hooks/usePosition.d.ts +36 -0
  66. package/dist/hooks/useScroll.d.ts +2 -0
  67. package/dist/hooks/useSpacing.d.ts +18 -0
  68. package/dist/hooks/useWrapper.d.ts +11 -0
  69. package/dist/index.d.ts +43 -0
  70. package/dist/index.js +8 -0
  71. package/dist/interfaces.d.ts +13 -0
  72. package/dist/react-lib-components.cjs.development.js +3282 -0
  73. package/dist/react-lib-components.cjs.development.js.map +1 -0
  74. package/dist/react-lib-components.cjs.production.min.js +2 -0
  75. package/dist/react-lib-components.cjs.production.min.js.map +1 -0
  76. package/dist/react-lib-components.esm.js +3235 -0
  77. package/dist/react-lib-components.esm.js.map +1 -0
  78. package/dist/util/helper.d.ts +1 -0
  79. package/package.json +88 -0
  80. package/src/BaseModal/BaseModal.module.scss +58 -0
  81. package/src/BaseModal/BaseModal.test.tsx +59 -0
  82. package/src/BaseModal/BaseModal.tsx +113 -0
  83. package/src/BaseModal/BaseModalActions/BaseModalActions.module.scss +9 -0
  84. package/src/BaseModal/BaseModalActions/BaseModalActions.test.tsx +17 -0
  85. package/src/BaseModal/BaseModalActions/BaseModalActions.tsx +14 -0
  86. package/src/BaseModal/BaseModalContent/BaseModalContent.module.scss +6 -0
  87. package/src/BaseModal/BaseModalContent/BaseModalContent.test.tsx +29 -0
  88. package/src/BaseModal/BaseModalContent/BaseModalContent.tsx +35 -0
  89. package/src/BaseModal/BaseModalContext.ts +2 -0
  90. package/src/BaseModal/BaseModalHeader/BaseModalHeader.module.scss +17 -0
  91. package/src/BaseModal/BaseModalHeader/BaseModalHeader.test.tsx +30 -0
  92. package/src/BaseModal/BaseModalHeader/BaseModalHeader.tsx +28 -0
  93. package/src/Breadcrumbs/Breadcrumbs.module.scss +14 -0
  94. package/src/Breadcrumbs/Breadcrumbs.test.tsx +42 -0
  95. package/src/Breadcrumbs/Breadcrumbs.tsx +48 -0
  96. package/src/Button/BaseButton.module.scss +20 -0
  97. package/src/Button/BaseButton.test.tsx +59 -0
  98. package/src/Button/BaseButton.tsx +31 -0
  99. package/src/Button/Button.module.scss +336 -0
  100. package/src/Button/Button.test.tsx +76 -0
  101. package/src/Button/Button.tsx +44 -0
  102. package/src/Button/IconButton.module.scss +161 -0
  103. package/src/Button/IconButton.test.tsx +47 -0
  104. package/src/Button/IconButton.tsx +29 -0
  105. package/src/ContextMenu/ContextMenu.module.scss +20 -0
  106. package/src/ContextMenu/ContextMenu.test.tsx +93 -0
  107. package/src/ContextMenu/ContextMenu.tsx +91 -0
  108. package/src/ContextMenu/ContextMenuItem.module.scss +31 -0
  109. package/src/ContextMenu/ContextMenuItem.tsx +15 -0
  110. package/src/Dialog/Dialog.module.scss +16 -0
  111. package/src/Dialog/Dialog.test.tsx +79 -0
  112. package/src/Dialog/Dialog.tsx +96 -0
  113. package/src/Dialog/DialogActions/DialogActions.module.scss +11 -0
  114. package/src/Dialog/DialogActions/DialogActions.test.tsx +25 -0
  115. package/src/Dialog/DialogActions/DialogActions.tsx +21 -0
  116. package/src/Dialog/DialogTitle/DialogTitle.module.scss +7 -0
  117. package/src/Dialog/DialogTitle/DialogTitle.test.tsx +18 -0
  118. package/src/Dialog/DialogTitle/DialogTitle.tsx +18 -0
  119. package/src/Form/Checkbox/Checkbox.module.scss +65 -0
  120. package/src/Form/Checkbox/Checkbox.test.tsx +119 -0
  121. package/src/Form/Checkbox/Checkbox.tsx +145 -0
  122. package/src/Form/Fieldset/Fieldset.module.scss +19 -0
  123. package/src/Form/Fieldset/Fieldset.test.tsx +85 -0
  124. package/src/Form/Fieldset/Fieldset.tsx +55 -0
  125. package/src/Form/Form.module.scss +3 -0
  126. package/src/Form/Form.test.tsx +47 -0
  127. package/src/Form/Form.tsx +14 -0
  128. package/src/Form/FormControl/FormControl.module.scss +67 -0
  129. package/src/Form/FormControl/FormControl.test.tsx +56 -0
  130. package/src/Form/FormControl/FormControl.tsx +47 -0
  131. package/src/Form/FormGroup/FormGroup.module.scss +29 -0
  132. package/src/Form/FormGroup/FormGroup.test.tsx +61 -0
  133. package/src/Form/FormGroup/FormGroup.tsx +78 -0
  134. package/src/Form/FormHelperText/FormHelperText.module.scss +8 -0
  135. package/src/Form/FormHelperText/FormHelperText.test.tsx +42 -0
  136. package/src/Form/FormHelperText/FormHelperText.tsx +22 -0
  137. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.module.scss +33 -0
  138. package/src/Form/FormSelectorWrapper/FormSelectorWrapper.tsx +65 -0
  139. package/src/Form/Input/Input.module.scss +65 -0
  140. package/src/Form/Input/Input.test.tsx +135 -0
  141. package/src/Form/Input/Input.tsx +72 -0
  142. package/src/Form/Label/Label.module.scss +5 -0
  143. package/src/Form/Label/Label.test.tsx +26 -0
  144. package/src/Form/Label/Label.tsx +19 -0
  145. package/src/Form/Radio/Radio.module.scss +100 -0
  146. package/src/Form/Radio/Radio.test.tsx +88 -0
  147. package/src/Form/Radio/Radio.tsx +98 -0
  148. package/src/Form/Select/Option.test.tsx +15 -0
  149. package/src/Form/Select/Option.tsx +57 -0
  150. package/src/Form/Select/Select.module.scss +189 -0
  151. package/src/Form/Select/Select.test.tsx +96 -0
  152. package/src/Form/Select/Select.tsx +217 -0
  153. package/src/Form/Textarea/Textarea.module.scss +53 -0
  154. package/src/Form/Textarea/Textarea.test.tsx +76 -0
  155. package/src/Form/Textarea/Textarea.tsx +33 -0
  156. package/src/Form/Toggle/Toggle.module.scss +58 -0
  157. package/src/Form/Toggle/Toggle.test.tsx +29 -0
  158. package/src/Form/Toggle/Toggle.tsx +20 -0
  159. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.module.scss +12 -0
  160. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.test.tsx +99 -0
  161. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.tsx +62 -0
  162. package/src/Form/Wrapper/InputWrapper/InputWrapper.module.scss +24 -0
  163. package/src/Form/Wrapper/InputWrapper/InputWrapper.test.tsx +93 -0
  164. package/src/Form/Wrapper/InputWrapper/InputWrapper.tsx +92 -0
  165. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.module.scss +12 -0
  166. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.test.tsx +101 -0
  167. package/src/Form/Wrapper/RadioWrapper/RadioWrapper.tsx +74 -0
  168. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.module.scss +14 -0
  169. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.test.tsx +101 -0
  170. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +59 -0
  171. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.module.scss +65 -0
  172. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.test.tsx +125 -0
  173. package/src/Form/Wrapper/TextareaWrapper/TextareaWrapper.tsx +105 -0
  174. package/src/Form/Wrapper/Wrapper/Wrapper.module.scss +35 -0
  175. package/src/Form/Wrapper/Wrapper/Wrapper.test.tsx +17 -0
  176. package/src/Form/Wrapper/Wrapper/Wrapper.tsx +101 -0
  177. package/src/Form/form.interfaces.ts +14 -0
  178. package/src/Icon/Icon.module.scss +278 -0
  179. package/src/Icon/Icon.test.tsx +39 -0
  180. package/src/Icon/Icon.tsx +94 -0
  181. package/src/Link/Link.module.scss +46 -0
  182. package/src/Link/Link.test.tsx +122 -0
  183. package/src/Link/Link.tsx +80 -0
  184. package/src/Link/types.d.ts +9 -0
  185. package/src/Modal/Modal.test.tsx +16 -0
  186. package/src/Modal/Modal.tsx +1 -0
  187. package/src/Modal/ModalActions/ModalActions.tsx +4 -0
  188. package/src/Modal/ModalContent/ModalContent.tsx +4 -0
  189. package/src/Modal/ModalHeader/ModalHeader.tsx +4 -0
  190. package/src/Popover/Popover.module.scss +18 -0
  191. package/src/Popover/Popover.test.tsx +84 -0
  192. package/src/Popover/Popover.tsx +46 -0
  193. package/src/Snackbar/SnackbarContainer/SnackbarContainer.module.scss +35 -0
  194. package/src/Snackbar/SnackbarContainer/SnackbarContainer.test.tsx +37 -0
  195. package/src/Snackbar/SnackbarContainer/SnackbarContainer.tsx +28 -0
  196. package/src/Snackbar/SnackbarItem/SnackbarItem.module.scss +135 -0
  197. package/src/Snackbar/SnackbarItem/SnackbarItem.test.tsx +47 -0
  198. package/src/Snackbar/SnackbarItem/SnackbarItem.tsx +105 -0
  199. package/src/Snackbar/SnackbarProvider/SnackbarProvider.test.tsx +179 -0
  200. package/src/Snackbar/SnackbarProvider/SnackbarProvider.tsx +127 -0
  201. package/src/Snackbar/SnackbarProvider/SnackbarStateProvider.tsx +25 -0
  202. package/src/Snackbar/interfaces.ts +11 -0
  203. package/src/Snackbar/useSnackbar.ts +4 -0
  204. package/src/Tiles/Tile.module.scss +72 -0
  205. package/src/Tiles/Tile.test.tsx +129 -0
  206. package/src/Tiles/Tile.tsx +48 -0
  207. package/src/Tiles/Tiles.module.scss +11 -0
  208. package/src/Tiles/Tiles.test.tsx +118 -0
  209. package/src/Tiles/Tiles.tsx +48 -0
  210. package/src/Tooltip/Tooltip.module.scss +42 -0
  211. package/src/Tooltip/Tooltip.test.tsx +72 -0
  212. package/src/Tooltip/Tooltip.tsx +130 -0
  213. package/src/Typography/Typography.module.scss +46 -0
  214. package/src/Typography/Typography.test.tsx +114 -0
  215. package/src/Typography/Typography.tsx +84 -0
  216. package/src/Wizard/BaseWizardSteps/BaseWizardSteps.module.scss +192 -0
  217. package/src/Wizard/BaseWizardSteps/BaseWizardSteps.test.tsx +75 -0
  218. package/src/Wizard/BaseWizardSteps/BaseWizardSteps.tsx +86 -0
  219. package/src/Wizard/Wizard.test.tsx +198 -0
  220. package/src/Wizard/Wizard.tsx +49 -0
  221. package/src/Wizard/WizardActions/WizardActions.test.tsx +168 -0
  222. package/src/Wizard/WizardActions/WizardActions.tsx +100 -0
  223. package/src/Wizard/WizardStateProvider.tsx +26 -0
  224. package/src/Wizard/WizardSteps/WizardSteps.test.tsx +110 -0
  225. package/src/Wizard/WizardSteps/WizardSteps.tsx +30 -0
  226. package/src/Wizard/wizardStateReducer.ts +51 -0
  227. package/src/_BaseStyling_/BaseStyling.test.tsx +39 -0
  228. package/src/_BaseStyling_/BaseStyling.tsx +115 -0
  229. package/src/hooks/useAnimation.test.tsx +45 -0
  230. package/src/hooks/useAnimation.ts +20 -0
  231. package/src/hooks/useBodyClick.test.tsx +39 -0
  232. package/src/hooks/useBodyClick.ts +20 -0
  233. package/src/hooks/useFormSelector.test.ts +40 -0
  234. package/src/hooks/useFormSelector.ts +47 -0
  235. package/src/hooks/usePosition.test.tsx +494 -0
  236. package/src/hooks/usePosition.ts +347 -0
  237. package/src/hooks/useScroll.test.tsx +20 -0
  238. package/src/hooks/useScroll.ts +16 -0
  239. package/src/hooks/useSpacing.test.ts +70 -0
  240. package/src/hooks/useSpacing.ts +42 -0
  241. package/src/hooks/useWrapper.test.ts +49 -0
  242. package/src/hooks/useWrapper.ts +35 -0
  243. package/src/index.ts +52 -0
  244. package/src/interfaces.ts +15 -0
  245. package/src/readyclasses.module.scss +77 -0
  246. package/src/types.d.ts +4 -0
  247. package/src/util/helper.test.tsx +15 -0
  248. package/src/util/helper.tsx +80 -0
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import classes from './Label.module.scss';
3
+ import readyclasses from '../../readyclasses.module.scss';
4
+ import { HTMLProps } from '../../interfaces';
5
+
6
+ export interface Props extends HTMLProps<HTMLLabelElement> {
7
+ children?: string;
8
+ }
9
+
10
+ export const Label = ({ children, className, hidden = false, ...rest }: Props) => {
11
+ return (
12
+ <label
13
+ {...rest}
14
+ className={`${hidden ? readyclasses['sr-only'] : ''} ${classes['label']} ${className ?? ''}`}
15
+ >
16
+ {children}
17
+ </label>
18
+ );
19
+ };
@@ -0,0 +1,100 @@
1
+ .radio-wrapper {
2
+ position: relative;
3
+
4
+ + .radio-wrapper {
5
+ margin-top: 1.25rem;
6
+ }
7
+ }
8
+
9
+ .error {
10
+ color: var(--error);
11
+ }
12
+
13
+ .helper-text {
14
+ margin-top: 0.25rem;
15
+ margin-left: 1.75rem;
16
+ }
17
+
18
+ .error-message {
19
+ margin-top: 1.25rem;
20
+ display: flex;
21
+ align-items: bottom;
22
+ }
23
+
24
+ .error-icon {
25
+ margin-right: 0.5rem;
26
+ font-size: 1.25rem;
27
+ }
28
+
29
+ .radio-container {
30
+ display: flex;
31
+ align-items: center;
32
+
33
+ label {
34
+ display: inline-block;
35
+ margin-left: 0.5rem;
36
+ user-select: none;
37
+ font-size: var(--font-size);
38
+
39
+ .disabled & {
40
+ cursor: not-allowed;
41
+ }
42
+ }
43
+ }
44
+
45
+ .radio-list {
46
+ padding: 0;
47
+ margin: 1.25rem 0 0 1.75rem;
48
+ list-style: none;
49
+
50
+ li + li {
51
+ margin-top: 1.75rem;
52
+ }
53
+ }
54
+
55
+ .native-input {
56
+ position: absolute;
57
+ top: 0;
58
+ left: 0;
59
+ opacity: 0;
60
+ margin: 0;
61
+ width: 1.75rem;
62
+ height: 1.75rem;
63
+
64
+ &:not(.error):not(:disabled):focus,
65
+ &:not(.error):not(:disabled):active {
66
+ + * {
67
+ color: var(--color-primary);
68
+ }
69
+ }
70
+
71
+ &:disabled {
72
+ cursor: not-allowed;
73
+ }
74
+
75
+ &:focus-visible {
76
+ + * {
77
+ border-radius: 2px;
78
+ outline: 1px solid var(--color-primary);
79
+ outline-offset: 1px;
80
+ }
81
+ }
82
+
83
+ &.error:focus-visible {
84
+ + * {
85
+ outline-color: var(--color-error);
86
+ }
87
+ }
88
+ }
89
+
90
+ .input {
91
+ font-size: 1.25rem;
92
+ pointer-events: none;
93
+ width: 1.25rem;
94
+ height: 1.25rem;
95
+
96
+ /** The & behind disabled means parent selector (so in reality this means .disabled .input) */
97
+ .disabled & {
98
+ color: var(--disabled);
99
+ }
100
+ }
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import { Radio, Props } from './Radio';
3
+ import { render } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+
6
+ const onChangeHandeler = jest.fn();
7
+
8
+ const defaultParams: Props = {
9
+ value: 'test',
10
+ children: 'Label',
11
+ checked: false,
12
+ error: false,
13
+ errorMessage: 'errormessage',
14
+ disabled: false,
15
+ wrapperProps: { 'data-testid': 'radiowrapper' },
16
+ helperText: 'helpertext',
17
+ onChange: onChangeHandeler,
18
+ };
19
+
20
+ const createRadio = (params?: (defaultParams: Props) => Props) => {
21
+ let parameters: Props = defaultParams;
22
+ if (params) {
23
+ parameters = params(defaultParams);
24
+ }
25
+ const queries = render(<Radio {...parameters} data-testid="radio" />);
26
+ const radio = queries.getByTestId('radio');
27
+ const radiowrapper = queries.getByTestId('radiowrapper');
28
+
29
+ return {
30
+ ...queries,
31
+ radio,
32
+ radiowrapper,
33
+ };
34
+ };
35
+
36
+ describe('Radio should render', () => {
37
+ it('renders without crashing and has proper attributes/values', () => {
38
+ const { radio } = createRadio();
39
+
40
+ expect((radio as HTMLInputElement).value).toBe('test');
41
+ expect((radio as HTMLInputElement).checked).toBe(false);
42
+ expect(radio).toBeTruthy();
43
+ });
44
+
45
+ it('it is checked', () => {
46
+ const { radio } = createRadio((defaultParams) => ({ ...defaultParams, checked: true }));
47
+
48
+ expect((radio as HTMLInputElement).checked).toBe(true);
49
+ });
50
+
51
+ it('it is disabled', () => {
52
+ const { radio, radiowrapper } = createRadio((defaultParams) => ({
53
+ ...defaultParams,
54
+ disabled: true,
55
+ }));
56
+
57
+ expect((radio as HTMLInputElement).disabled).toBe(true);
58
+ expect(radiowrapper).toHaveClass('disabled');
59
+ });
60
+
61
+ it('has the correct describedby value when theres no error', async () => {
62
+ const { radio, findByText } = createRadio((defaultParams) => ({
63
+ ...defaultParams,
64
+ error: true,
65
+ }));
66
+
67
+ const error = await findByText('errormessage');
68
+
69
+ expect(radio).toHaveAttribute('aria-describedby', error.id);
70
+ });
71
+ });
72
+
73
+ describe('Radio should be interactive', () => {
74
+ it('should call onChange when clicked', () => {
75
+ const { radio } = createRadio();
76
+
77
+ expect(onChangeHandeler).not.toBeCalled();
78
+ userEvent.click(radio);
79
+ expect(onChangeHandeler).toBeCalledTimes(1);
80
+ });
81
+
82
+ it('should not call onChange when disabled', () => {
83
+ const { radio } = createRadio((defaultParams) => ({ ...defaultParams, disabled: true }));
84
+
85
+ userEvent.click(radio);
86
+ expect(onChangeHandeler).not.toBeCalled();
87
+ });
88
+ });
@@ -0,0 +1,98 @@
1
+ import React from 'react';
2
+ import { Icon, Icons } from '../../Icon/Icon';
3
+ import { Props as HelperProps } from '../FormHelperText/FormHelperText';
4
+ import classes from './Radio.module.scss';
5
+ import { useFormSelector } from '../../hooks/useFormSelector';
6
+ import { FormSelector } from '../form.interfaces';
7
+ import { HTMLProps } from '../../interfaces';
8
+ import { FormSelectorWrapper } from '../FormSelectorWrapper/FormSelectorWrapper';
9
+
10
+ export interface Props extends FormSelector<HTMLInputElement> {
11
+ children: string;
12
+ value: string;
13
+ wrapperProps?: HTMLProps<HTMLDivElement>;
14
+ helperProps?: HelperProps;
15
+ }
16
+
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
+ });
42
+
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);
50
+
51
+ Object.defineProperty(clonedEvent, 'target', {
52
+ writable: true,
53
+ value: { value: value },
54
+ });
55
+
56
+ onChange && onChange(clonedEvent);
57
+ };
58
+
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}
76
+ 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
+ />
89
+
90
+ {checked && <Icon className={classes.input} icon={Icons.Radio} />}
91
+ {!checked && <Icon className={classes.input} icon={Icons.Circle} />}
92
+
93
+ <label onClick={onChangeHandler} htmlFor={`${identifier}-radio`}>
94
+ {children}
95
+ </label>
96
+ </FormSelectorWrapper>
97
+ );
98
+ };
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import { Option } from './Option';
3
+ import { render } from '@testing-library/react';
4
+
5
+ describe('Option should render', () => {
6
+ it('renders without crashing', () => {
7
+ const { getByTestId } = render(
8
+ <Option value="option" data-testid="component">
9
+ Option
10
+ </Option>
11
+ );
12
+ const component = getByTestId('component');
13
+ expect(component).toBeDefined();
14
+ });
15
+ });
@@ -0,0 +1,57 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import classes from './Select.module.scss';
3
+ import { HTMLProps } from '../../interfaces';
4
+
5
+ export interface Props extends HTMLProps<HTMLLIElement> {
6
+ children: string;
7
+ value: string;
8
+ disabled?: boolean;
9
+ selected?: boolean;
10
+ label?: string;
11
+ filter?: string;
12
+ onOptionSelect?: (event: React.SyntheticEvent<HTMLLIElement>) => void;
13
+ }
14
+
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);
25
+
26
+ const onSelectHandler = (event: React.SyntheticEvent<HTMLLIElement>) => {
27
+ if (onOptionSelect) onOptionSelect(event);
28
+ };
29
+
30
+ useEffect(() => {
31
+ if (filter) {
32
+ setShowOption(children.toLowerCase().match(filter.toLowerCase()) !== null);
33
+ } else {
34
+ setShowOption(true);
35
+ }
36
+ }, [filter]);
37
+
38
+ if (!showOption) return null;
39
+
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
+ };
@@ -0,0 +1,189 @@
1
+ @import '../../readyclasses.module.scss';
2
+
3
+ $listItemHeight: 2.125rem;
4
+
5
+ .select {
6
+ border-color: var(--input-border-color);
7
+ border-style: var(--input-border-style);
8
+ border-width: var(--input-border-width);
9
+ border-radius: var(--input-border-radius);
10
+ position: relative;
11
+ box-sizing: border-box;
12
+ transition: all 0.2s ease-in-out;
13
+ background-color: #fff;
14
+ font-size: var(--font-size);
15
+
16
+ &.expanded {
17
+ border: var(--input-border-width) solid transparent;
18
+
19
+ .list-wrapper {
20
+ background: #fff;
21
+ opacity: 1;
22
+ pointer-events: auto;
23
+ }
24
+ }
25
+
26
+ &:not(.expanded) {
27
+ button:focus-visible {
28
+ outline: 1px solid var(--color-primary);
29
+ }
30
+ }
31
+
32
+ &:hover:not(.disabled):not(.expanded) {
33
+ border-color: var(--default);
34
+ }
35
+
36
+ button {
37
+ width: 100%;
38
+ min-height: 4rem;
39
+ background-color: transparent;
40
+ border: 0;
41
+ cursor: pointer;
42
+ padding: 0 1.25rem;
43
+ position: relative;
44
+ font-size: var(--font-size);
45
+ border-radius: var(--input-border-radius);
46
+
47
+ &:focus-visible {
48
+ outline: 0;
49
+ }
50
+ }
51
+
52
+ .list-wrapper {
53
+ border-color: var(--input-border-color);
54
+ border-style: var(--input-border-style);
55
+ border-width: var(--input-border-width);
56
+ border-radius: var(--input-border-radius);
57
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.29);
58
+ position: absolute;
59
+ z-index: 2;
60
+ top: 0;
61
+ left: 0;
62
+ width: 100%;
63
+ opacity: 0;
64
+ pointer-events: none;
65
+ }
66
+
67
+ ul {
68
+ box-sizing: border-box;
69
+ padding: 0.25rem 0;
70
+ width: 100%;
71
+ margin: 0;
72
+ list-style: none;
73
+ background-color: #fff;
74
+ border-radius: var(--input-border-radius);
75
+ color: var(--default);
76
+ text-align: left;
77
+ overflow: auto;
78
+ max-height: calc($listItemHeight * 10);
79
+
80
+ li {
81
+ padding: 0.5rem 1rem;
82
+ font-size: var(--font-size);
83
+ margin: 0;
84
+ position: relative;
85
+ line-height: 1;
86
+ cursor: pointer;
87
+
88
+ &:after {
89
+ content: '';
90
+ position: absolute;
91
+ top: 0;
92
+ left: 0;
93
+ background-color: transparent;
94
+ width: 100%;
95
+ height: 100%;
96
+ opacity: 0.05;
97
+ }
98
+
99
+ &:focus-visible {
100
+ outline: 1px solid var(--color-primary);
101
+ outline-offset: -1px;
102
+ }
103
+
104
+ &:hover:after {
105
+ background-color: var(--color-primary);
106
+ }
107
+
108
+ &.disabled {
109
+ color: var(--greyed-out);
110
+ background-color: var(--disabled);
111
+ pointer-events: none;
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ .selected-option {
118
+ &:before {
119
+ content: '';
120
+ position: absolute;
121
+ top: 0;
122
+ left: 0;
123
+ background-color: var(--color-primary);
124
+ width: 100%;
125
+ height: 100%;
126
+ opacity: 0.1;
127
+ }
128
+ }
129
+
130
+ .selected {
131
+ position: absolute;
132
+ top: 50%;
133
+ transform: translateY(-50%);
134
+ left: 1.25rem;
135
+ pointer-events: none;
136
+ }
137
+
138
+ .status {
139
+ position: absolute;
140
+ top: 50%;
141
+ right: 1.625rem;
142
+ transform: translateY(-50%);
143
+ display: flex;
144
+ align-items: center;
145
+
146
+ .warning {
147
+ color: var(--error);
148
+ font-size: 1.25rem;
149
+ }
150
+
151
+ .triangle-down {
152
+ color: var(--default);
153
+ font-size: 0.625rem;
154
+ }
155
+
156
+ * + * {
157
+ margin-left: 1.25rem;
158
+ }
159
+ }
160
+
161
+ .placeholder {
162
+ color: var(--greyed-out);
163
+ }
164
+
165
+ .error {
166
+ border-color: var(--error);
167
+ color: var(--error);
168
+ }
169
+
170
+ .select-search-wrapper + ul {
171
+ border-radius: 0 0 var(--input-border-radius) var(--input-border-radius);
172
+ }
173
+
174
+ .select-search {
175
+ width: 100%;
176
+ box-sizing: border-box;
177
+ border-bottom-left-radius: 0;
178
+ border-bottom-right-radius: 0;
179
+ }
180
+
181
+ .disabled {
182
+ cursor: not-allowed;
183
+ color: var(--greyed-out);
184
+ background-color: var(--disabled);
185
+
186
+ > * {
187
+ pointer-events: none;
188
+ }
189
+ }
@@ -0,0 +1,96 @@
1
+ import React, { ReactElement } from 'react';
2
+ import { Select, Props } from './Select';
3
+ import { render } from '@testing-library/react';
4
+ import { Option } from './Option';
5
+ import userEvent from '@testing-library/user-event';
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');
25
+
26
+ if (button) {
27
+ userEvent.click(button);
28
+ }
29
+
30
+ const list = select.querySelector('ul[role="listbox"]');
31
+ const dropdownWrapper = select.querySelector('.list-wrapper');
32
+
33
+ return {
34
+ ...queries,
35
+ select,
36
+ button,
37
+ list,
38
+ dropdownWrapper,
39
+ };
40
+ };
41
+
42
+ describe('Select should render', () => {
43
+ it('renders with 5 options and proper attributes', () => {
44
+ const { select, button, list } = createSelect(5);
45
+
46
+ expect(select).toBeDefined();
47
+ expect(button?.getAttribute('aria-expanded')).toBe('true');
48
+ expect(button?.getAttribute('aria-disabled')).toBe('false');
49
+ expect(list).toBeDefined();
50
+ expect(list?.querySelectorAll("li[role='option']").length).toBe(5);
51
+ });
52
+ });
53
+
54
+ describe('Select should render with search', () => {
55
+ it('shows the search and filtering works', () => {
56
+ const { select, list, dropdownWrapper } = createSelect(20);
57
+
58
+ const search = dropdownWrapper?.querySelector('input');
59
+
60
+ expect(select).toBeTruthy();
61
+ expect(search).toBeTruthy();
62
+ expect(list?.querySelectorAll("li[role='option']").length).toBe(20);
63
+
64
+ if (search) {
65
+ userEvent.type(search, '19');
66
+ }
67
+
68
+ expect(list?.querySelectorAll("li[role='option']").length).toBe(1);
69
+ expect(list?.querySelector("li[role='option']")?.innerHTML).toBe('Option19');
70
+ });
71
+ });
72
+
73
+ describe('Selecting options using keyboard', () => {
74
+ it('should focus through list items and select on enterpress', () => {
75
+ const { select, list } = createSelect(7);
76
+
77
+ userEvent.tab();
78
+ expect(list?.querySelectorAll('li')[0]).toHaveFocus();
79
+ userEvent.tab();
80
+ expect(list?.querySelectorAll('li')[1]).toHaveFocus();
81
+ userEvent.tab();
82
+ expect(list?.querySelectorAll('li')[2]).toHaveFocus();
83
+ userEvent.tab();
84
+ expect(list?.querySelectorAll('li')[3]).toHaveFocus();
85
+ userEvent.tab();
86
+ expect(list?.querySelectorAll('li')[4]).toHaveFocus();
87
+ userEvent.tab();
88
+ expect(list?.querySelectorAll('li')[5]).toHaveFocus();
89
+
90
+ userEvent.keyboard('{enter}');
91
+
92
+ setTimeout(() => {
93
+ expect(select.querySelector('button > span > span')?.innerHTML).toBe('Option5');
94
+ }, 50);
95
+ });
96
+ });