@onewelcome/react-lib-components 0.1.5-alpha → 0.1.8-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 (105) hide show
  1. package/dist/Button/IconButton.d.ts +2 -1
  2. package/dist/ContextMenu/ContextMenu.d.ts +2 -3
  3. package/dist/ContextMenu/ContextMenuItem.d.ts +10 -3
  4. package/dist/DataGrid/DataGrid.d.ts +30 -0
  5. package/dist/DataGrid/DataGridActions/DataGridActions.d.ts +14 -0
  6. package/dist/DataGrid/DataGridActions/DataGridColumnsToggle.d.ts +13 -0
  7. package/dist/DataGrid/DataGridBody/DataGridBody.d.ts +16 -0
  8. package/dist/DataGrid/DataGridBody/DataGridCell.d.ts +6 -0
  9. package/dist/DataGrid/DataGridBody/DataGridRow.d.ts +7 -0
  10. package/dist/DataGrid/DataGridHeader/DataGridHeader.d.ts +10 -0
  11. package/dist/DataGrid/DataGridHeader/DataGridHeaderCell.d.ts +10 -0
  12. package/dist/DataGrid/datagrid.interfaces.d.ts +13 -0
  13. package/dist/Form/Checkbox/Checkbox.d.ts +2 -2
  14. package/dist/Form/Select/Option.d.ts +9 -4
  15. package/dist/Form/Select/Select.d.ts +8 -2
  16. package/dist/Form/Toggle/Toggle.d.ts +1 -1
  17. package/dist/Form/Wrapper/SelectWrapper/SelectWrapper.d.ts +1 -1
  18. package/dist/Icon/Icon.d.ts +1 -0
  19. package/dist/Link/Link.d.ts +4 -3
  20. package/dist/Notifications/BaseModal/BaseModal.d.ts +4 -2
  21. package/dist/Notifications/SlideInModal/SlideInModal.d.ts +4 -0
  22. package/dist/StatusIndicator/StatusIndicator.d.ts +9 -0
  23. package/dist/_BaseStyling_/BaseStyling.d.ts +4 -0
  24. package/dist/hooks/useSpacing.d.ts +1 -1
  25. package/dist/hooks/useWrapper.d.ts +1 -1
  26. package/dist/index.d.ts +48 -43
  27. package/dist/react-lib-components.cjs.development.js +2900 -2049
  28. package/dist/react-lib-components.cjs.development.js.map +1 -1
  29. package/dist/react-lib-components.cjs.production.min.js +1 -1
  30. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  31. package/dist/react-lib-components.esm.js +2897 -2050
  32. package/dist/react-lib-components.esm.js.map +1 -1
  33. package/dist/util/helper.d.ts +1 -1
  34. package/package.json +11 -11
  35. package/src/Button/BaseButton.module.scss +3 -18
  36. package/src/Button/Button.module.scss +4 -311
  37. package/src/Button/IconButton.module.scss +21 -128
  38. package/src/Button/IconButton.test.tsx +24 -0
  39. package/src/Button/IconButton.tsx +6 -1
  40. package/src/ContextMenu/ContextMenu.test.tsx +121 -6
  41. package/src/ContextMenu/ContextMenu.tsx +99 -6
  42. package/src/ContextMenu/ContextMenuItem.tsx +57 -9
  43. package/src/DataGrid/DataGrid.module.scss +21 -0
  44. package/src/DataGrid/DataGrid.test.tsx +276 -0
  45. package/src/DataGrid/DataGrid.tsx +101 -0
  46. package/src/DataGrid/DataGridActions/DataGridActions.module.scss +35 -0
  47. package/src/DataGrid/DataGridActions/DataGridActions.test.tsx +184 -0
  48. package/src/DataGrid/DataGridActions/DataGridActions.tsx +109 -0
  49. package/src/DataGrid/DataGridActions/DataGridColumnsToggle.module.scss +41 -0
  50. package/src/DataGrid/DataGridActions/DataGridColumnsToggle.test.tsx +83 -0
  51. package/src/DataGrid/DataGridActions/DataGridColumnsToggle.tsx +88 -0
  52. package/src/DataGrid/DataGridBody/DataGridBody.module.scss +10 -0
  53. package/src/DataGrid/DataGridBody/DataGridBody.test.tsx +123 -0
  54. package/src/DataGrid/DataGridBody/DataGridBody.tsx +64 -0
  55. package/src/DataGrid/DataGridBody/DataGridCell.module.scss +33 -0
  56. package/src/DataGrid/DataGridBody/DataGridCell.test.tsx +74 -0
  57. package/src/DataGrid/DataGridBody/DataGridCell.tsx +25 -0
  58. package/src/DataGrid/DataGridBody/DataGridRow.module.scss +7 -0
  59. package/src/DataGrid/DataGridBody/DataGridRow.test.tsx +101 -0
  60. package/src/DataGrid/DataGridBody/DataGridRow.tsx +27 -0
  61. package/src/DataGrid/DataGridBody/__snapshots__/DataGridBody.test.tsx.snap +258 -0
  62. package/src/DataGrid/DataGridHeader/DataGridHeader.module.scss +26 -0
  63. package/src/DataGrid/DataGridHeader/DataGridHeader.test.tsx +255 -0
  64. package/src/DataGrid/DataGridHeader/DataGridHeader.tsx +80 -0
  65. package/src/DataGrid/DataGridHeader/DataGridHeaderCell.module.scss +68 -0
  66. package/src/DataGrid/DataGridHeader/DataGridHeaderCell.test.tsx +128 -0
  67. package/src/DataGrid/DataGridHeader/DataGridHeaderCell.tsx +72 -0
  68. package/src/DataGrid/datagrid.interfaces.ts +14 -0
  69. package/src/Form/Checkbox/Checkbox.test.tsx +144 -8
  70. package/src/Form/Checkbox/Checkbox.tsx +8 -8
  71. package/src/Form/Select/Option.tsx +39 -21
  72. package/src/Form/Select/Select.module.scss +1 -1
  73. package/src/Form/Select/Select.test.tsx +235 -56
  74. package/src/Form/Select/Select.tsx +194 -34
  75. package/src/Form/Toggle/Toggle.module.scss +1 -0
  76. package/src/Form/Toggle/Toggle.test.tsx +45 -19
  77. package/src/Form/Toggle/Toggle.tsx +3 -3
  78. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.test.tsx +1 -1
  79. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.test.tsx +44 -0
  80. package/src/Form/Wrapper/SelectWrapper/SelectWrapper.tsx +4 -2
  81. package/src/Icon/Icon.module.scss +4 -0
  82. package/src/Icon/Icon.tsx +1 -0
  83. package/src/Link/Link.module.scss +20 -0
  84. package/src/Link/Link.test.tsx +33 -0
  85. package/src/Link/Link.tsx +8 -2
  86. package/src/Notifications/BaseModal/BaseModal.module.scss +1 -1
  87. package/src/Notifications/BaseModal/BaseModal.test.tsx +77 -12
  88. package/src/Notifications/BaseModal/BaseModal.tsx +27 -6
  89. package/src/Notifications/Dialog/Dialog.module.scss +1 -1
  90. package/src/Notifications/Dialog/Dialog.tsx +1 -1
  91. package/src/Notifications/SlideInModal/SlideInModal.module.scss +36 -0
  92. package/src/Notifications/SlideInModal/SlideInModal.test.tsx +69 -0
  93. package/src/Notifications/SlideInModal/SlideInModal.tsx +31 -0
  94. package/src/Notifications/Snackbar/SnackbarContainer/SnackbarContainer.module.scss +1 -1
  95. package/src/Notifications/Snackbar/SnackbarItem/SnackbarItem.module.scss +1 -1
  96. package/src/Pagination/Pagination.module.scss +74 -74
  97. package/src/StatusIndicator/StatusIndicator.module.scss +27 -0
  98. package/src/StatusIndicator/StatusIndicator.test.tsx +127 -0
  99. package/src/StatusIndicator/StatusIndicator.tsx +25 -0
  100. package/src/Tiles/Tile.module.scss +1 -1
  101. package/src/Tiles/Tile.test.tsx +4 -4
  102. package/src/_BaseStyling_/BaseStyling.tsx +14 -6
  103. package/src/index.ts +85 -48
  104. package/src/mixins.module.scss +171 -0
  105. package/src/readyclasses.module.scss +0 -30
@@ -0,0 +1,128 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { DataGridHeaderCell, Props } from './DataGridHeaderCell';
3
+ import { render } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
5
+
6
+ const defaultParams: Props = { name: 'name-123', headline: 'Test' };
7
+
8
+ const createDataGridHeaderCell = (params?: (defaultParams: Props) => Props) => {
9
+ let parameters: Props = defaultParams;
10
+ if (params) {
11
+ parameters = params(defaultParams);
12
+ }
13
+ const container = document.createElement('tr');
14
+ const queries = render(<DataGridHeaderCell {...parameters} data-testid="dataGridHeaderCell" />, {
15
+ container,
16
+ });
17
+ const dataGridHeaderCell = queries.getByTestId('dataGridHeaderCell');
18
+
19
+ return {
20
+ ...queries,
21
+ dataGridHeaderCell,
22
+ };
23
+ };
24
+
25
+ describe('DataGridHeaderCell should render', () => {
26
+ it('renders without crashing', () => {
27
+ const { dataGridHeaderCell, queryByRole } = createDataGridHeaderCell();
28
+
29
+ expect(dataGridHeaderCell).toBeDefined();
30
+ expect(dataGridHeaderCell).toHaveTextContent(defaultParams.headline);
31
+ expect(queryByRole('button')).toBeDefined();
32
+ expect(dataGridHeaderCell.querySelectorAll('[data-icon]')).toHaveLength(2);
33
+ });
34
+
35
+ it('renders without sorting indicators (icons)', () => {
36
+ const { dataGridHeaderCell } = createDataGridHeaderCell((params) => ({
37
+ ...params,
38
+ disableSorting: true,
39
+ }));
40
+
41
+ expect(dataGridHeaderCell.querySelector('[data-icon]')).toBeNull();
42
+ });
43
+
44
+ it('renders DESC and ASC sorting indicators (icons) when sorting is enabled and current sorting is not defined', () => {
45
+ const { dataGridHeaderCell } = createDataGridHeaderCell();
46
+
47
+ expect(dataGridHeaderCell).toBeDefined();
48
+ const sortingIcons = dataGridHeaderCell.querySelectorAll('[data-icon]');
49
+ expect(sortingIcons).toHaveLength(2);
50
+ sortingIcons.forEach((icon) => {
51
+ expect(icon).toHaveClass('indicator');
52
+ expect(icon).not.toHaveClass('active');
53
+ expect(icon).not.toHaveClass('hidden');
54
+ });
55
+ });
56
+
57
+ it('renders ASC sorting indicator (icon) when sorting is enabled and current sorting is ASC', () => {
58
+ const { dataGridHeaderCell } = createDataGridHeaderCell((params) => ({
59
+ ...params,
60
+ activeSortDirection: 'ASC',
61
+ }));
62
+
63
+ expect(dataGridHeaderCell).toBeDefined();
64
+ const sortingIcons = dataGridHeaderCell.querySelectorAll('[data-icon]');
65
+ expect(sortingIcons).toHaveLength(2);
66
+ sortingIcons.forEach((icon) => expect(icon).toHaveClass('indicator'));
67
+ expect(sortingIcons[0]).toHaveClass('indicator', 'active', 'icon-triangle-up');
68
+ expect(sortingIcons[0]).not.toHaveClass('hidden');
69
+ expect(sortingIcons[1]).toHaveClass('indicator', 'hidden', 'icon-triangle-down');
70
+ expect(sortingIcons[1]).not.toHaveClass('active');
71
+ });
72
+ });
73
+
74
+ it('renders DESC sorting indicator (icon) when sorting is enabled and current sorting is DESC', () => {
75
+ const { dataGridHeaderCell } = createDataGridHeaderCell((params) => ({
76
+ ...params,
77
+ activeSortDirection: 'DESC',
78
+ }));
79
+
80
+ expect(dataGridHeaderCell).toBeDefined();
81
+ const sortingIcons = dataGridHeaderCell.querySelectorAll('[data-icon]');
82
+ expect(sortingIcons).toHaveLength(2);
83
+ sortingIcons.forEach((icon) => expect(icon).toHaveClass('indicator'));
84
+ expect(sortingIcons[0]).toHaveClass('indicator', 'hidden', 'icon-triangle-up');
85
+ expect(sortingIcons[0]).not.toHaveClass('active');
86
+ expect(sortingIcons[1]).toHaveClass('indicator', 'active', 'icon-triangle-down');
87
+ expect(sortingIcons[1]).not.toHaveClass('hidden');
88
+ });
89
+
90
+ describe('DataGridHeaderCell should be interactive', () => {
91
+ it('clicking on cell calls onSort callback', () => {
92
+ const onSortHandler = jest.fn();
93
+ const { getByRole } = createDataGridHeaderCell((params) => ({
94
+ ...params,
95
+ disableSorting: false,
96
+ onSort: onSortHandler,
97
+ }));
98
+
99
+ userEvent.click(getByRole('button'));
100
+
101
+ expect(onSortHandler).toBeCalledWith(defaultParams.name);
102
+ });
103
+ });
104
+
105
+ describe('ref should work', () => {
106
+ it('should give back the proper data prop, this also checks if the component propagates ...rest properly', () => {
107
+ const ExampleComponent = ({
108
+ propagateRef,
109
+ }: {
110
+ propagateRef: (ref: React.RefObject<HTMLElement>) => void;
111
+ }) => {
112
+ const ref = useRef(null);
113
+
114
+ useEffect(() => {
115
+ propagateRef(ref);
116
+ }, [ref]);
117
+
118
+ return <DataGridHeaderCell {...defaultParams} data-ref="testing" ref={ref} />;
119
+ };
120
+
121
+ const refCheck = (ref: React.RefObject<HTMLElement>) => {
122
+ expect(ref.current).toHaveAttribute('data-ref', 'testing');
123
+ };
124
+
125
+ const container = document.createElement('tr');
126
+ render(<ExampleComponent propagateRef={refCheck} />, { container });
127
+ });
128
+ });
@@ -0,0 +1,72 @@
1
+ import React, { ComponentPropsWithRef, Fragment } from 'react';
2
+ import { Icon, Icons } from '../../Icon/Icon';
3
+ import { ColumnName, Direction } from '../datagrid.interfaces';
4
+ import classes from './DataGridHeaderCell.module.scss';
5
+
6
+ export interface Props extends ComponentPropsWithRef<'th'> {
7
+ headline: string;
8
+ name: ColumnName;
9
+ disableSorting?: boolean;
10
+ activeSortDirection?: Direction;
11
+ onSort?: (name: ColumnName) => void;
12
+ }
13
+
14
+ const ariaSortMapping = {
15
+ ASC: 'ascending',
16
+ DESC: 'descending',
17
+ } as const;
18
+
19
+ export const DataGridHeaderCell = React.forwardRef<HTMLTableCellElement, Props>(
20
+ ({ headline, name, disableSorting, activeSortDirection, onSort, ...rest }: Props, ref) => {
21
+ const onCellClick = () => {
22
+ onSort && onSort(name);
23
+ };
24
+
25
+ const sortingIndicator = () => {
26
+ const getSortingIndicatorClasses = (direction: Direction) => {
27
+ const sortingIndicatorClasses = [classes['indicator']];
28
+ activeSortDirection &&
29
+ sortingIndicatorClasses.push(
30
+ activeSortDirection === direction ? classes['active'] : classes['hidden']
31
+ );
32
+ return sortingIndicatorClasses;
33
+ };
34
+
35
+ return (
36
+ <Fragment>
37
+ <Icon className={getSortingIndicatorClasses('ASC').join(' ')} icon={Icons.TriangleUp} />
38
+ <Icon
39
+ className={getSortingIndicatorClasses('DESC').join(' ')}
40
+ icon={Icons.TriangleDown}
41
+ />
42
+ </Fragment>
43
+ );
44
+ };
45
+
46
+ const innerContent = (
47
+ <Fragment>
48
+ <span className={classes['headline']}>{headline}</span>
49
+ {!disableSorting && (
50
+ <div className={classes['sorting-indicator-container']}>{sortingIndicator()}</div>
51
+ )}
52
+ </Fragment>
53
+ );
54
+
55
+ return (
56
+ <th
57
+ {...rest}
58
+ ref={ref}
59
+ className={classes['header-cell']}
60
+ aria-sort={activeSortDirection && ariaSortMapping[activeSortDirection]}
61
+ >
62
+ {disableSorting ? (
63
+ <div key={name}>{innerContent}</div>
64
+ ) : (
65
+ <button key={name} onClick={onCellClick}>
66
+ {innerContent}
67
+ </button>
68
+ )}
69
+ </th>
70
+ );
71
+ }
72
+ );
@@ -0,0 +1,14 @@
1
+ export type ColumnName = string;
2
+ export type Direction = 'ASC' | 'DESC';
3
+ export type Sort = {
4
+ name: ColumnName;
5
+ direction: Direction;
6
+ }[];
7
+ export type OnSortFunction = (sort: Sort) => void;
8
+
9
+ export interface HeaderCell {
10
+ name: ColumnName;
11
+ headline: string;
12
+ disableSorting?: boolean;
13
+ hidden?: boolean;
14
+ }
@@ -1,15 +1,16 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
- import { Checkbox, CheckboxProps as Props } from './Checkbox';
2
+ import { Checkbox, Props } from './Checkbox';
3
3
  import { render } from '@testing-library/react';
4
4
  import userEvent from '@testing-library/user-event';
5
+ import { Toggle } from '../Toggle/Toggle';
5
6
 
6
- const onChangeHandeler = jest.fn();
7
+ const onChangeHandler = jest.fn();
7
8
 
8
9
  const defaultParams: Props = {
9
10
  name: 'Testing',
10
11
  children: 'checkbox content',
11
12
  helperText: 'example helper',
12
- onChange: onChangeHandeler,
13
+ onChange: onChangeHandler,
13
14
  };
14
15
 
15
16
  const createCheckbox = (params?: (defaultParams: Props) => Props) => {
@@ -83,9 +84,35 @@ describe('Checkbox should have proper attributes', () => {
83
84
  });
84
85
 
85
86
  it('should be disabled', () => {
86
- const { checkbox } = createCheckbox((defaultParams) => ({ ...defaultParams, disabled: true }));
87
+ const onChangeHandler = jest.fn();
88
+ const { checkbox } = createCheckbox((defaultParams) => ({
89
+ ...defaultParams,
90
+ onChange: onChangeHandler,
91
+ disabled: true,
92
+ }));
93
+
94
+ expect(checkbox).toBeDisabled();
95
+
96
+ userEvent.click(checkbox);
97
+
98
+ expect(onChangeHandler).not.toHaveBeenCalled();
99
+ });
100
+
101
+ it('nested checkbox should be disabled', () => {
102
+ const { getByTestId } = createCheckbox((defaultParams) => ({
103
+ ...defaultParams,
104
+ indeterminate: false,
105
+ label: 'test',
106
+ children: (
107
+ <Checkbox data-testid="nested-checkbox" name="test" disabled={true}>
108
+ test
109
+ </Checkbox>
110
+ ),
111
+ }));
112
+
113
+ const nestedCheckbox = getByTestId('nested-checkbox');
87
114
 
88
- expect(checkbox).toHaveAttribute('disabled');
115
+ expect(nestedCheckbox).toBeDisabled();
89
116
  });
90
117
 
91
118
  it('should have helpertext rendered', () => {
@@ -131,15 +158,124 @@ describe('Checkbox should be interactive', () => {
131
158
  it('should call onChange when clicked', () => {
132
159
  const { checkbox } = createCheckbox();
133
160
 
134
- expect(onChangeHandeler).not.toBeCalled();
161
+ expect(onChangeHandler).not.toBeCalled();
135
162
  userEvent.click(checkbox);
136
- expect(onChangeHandeler).toBeCalledTimes(1);
163
+ expect(onChangeHandler).toBeCalledTimes(1);
137
164
  });
138
165
 
139
166
  it('should not call onChange when disabled', () => {
140
167
  const { checkbox } = createCheckbox((defaultParams) => ({ ...defaultParams, disabled: true }));
141
168
 
142
169
  userEvent.click(checkbox);
143
- expect(onChangeHandeler).not.toBeCalled();
170
+ expect(onChangeHandler).not.toBeCalled();
171
+ });
172
+ });
173
+
174
+ describe('toggle version', () => {
175
+ it('should turn into a toggle', () => {
176
+ const { container } = render(<Toggle name="toggle">Test</Toggle>);
177
+
178
+ expect(container.querySelector('[data-toggle]')).toBeInTheDocument();
179
+ });
180
+ });
181
+
182
+ describe('missing attributes gets us errors', () => {
183
+ it('throws an error for missing label prop', () => {
184
+ // Prevent throwing an error in the console when this test is executed. We fix this and the end of this test.
185
+ const err = console.error;
186
+ console.error = jest.fn();
187
+
188
+ let actual;
189
+
190
+ try {
191
+ // @ts-ignore: mandatory props (test for non-typescript react projects)
192
+ createCheckbox((defaultParams) => ({
193
+ ...defaultParams,
194
+ name: 'testing',
195
+ children: <Checkbox name="test">Test</Checkbox>,
196
+ }));
197
+ } catch (e: any) {
198
+ actual = e.message;
199
+ }
200
+
201
+ const expected =
202
+ 'If you pass Checkboxes as a child component (to create nested checkbox tree) you need to pass a label to the parent checkbox.';
203
+
204
+ expect(actual).toEqual(expected);
205
+
206
+ console.error = err;
207
+ });
208
+
209
+ it('throws an error for indeterminate prop', () => {
210
+ // Prevent throwing an error in the console when this test is executed. We fix this and the end of this test.
211
+ const err = console.error;
212
+ console.error = jest.fn();
213
+
214
+ let actual;
215
+
216
+ try {
217
+ // @ts-ignore: mandatory props (test for non-typescript react projects)
218
+ createCheckbox((defaultParams) => ({
219
+ ...defaultParams,
220
+ label: 'testing',
221
+ children: <Checkbox name="test">Test</Checkbox>,
222
+ }));
223
+ } catch (e: any) {
224
+ actual = e.message;
225
+ }
226
+
227
+ const expected =
228
+ 'If you have nested checkboxes you have to manage the indeterminate state by passing a boolean to the `indeterminate` prop.';
229
+
230
+ expect(actual).toEqual(expected);
231
+
232
+ console.error = err;
233
+ });
234
+
235
+ it('throws an error for incorrect children prop', () => {
236
+ // Prevent throwing an error in the console when this test is executed. We fix this and the end of this test.
237
+ const err = console.error;
238
+ console.error = jest.fn();
239
+
240
+ let actual;
241
+
242
+ try {
243
+ // @ts-ignore: mandatory props (test for non-typescript react projects)
244
+ createCheckbox((defaultParams) => ({
245
+ name: undefined,
246
+ }));
247
+ } catch (e: any) {
248
+ actual = e.message;
249
+ }
250
+
251
+ const expected =
252
+ 'Please make sure to pass either a string or more Checkbox components as a child of your Checkbox component.';
253
+
254
+ expect(actual).toEqual(expected);
255
+
256
+ console.error = err;
257
+ });
258
+
259
+ it('throws an error for missing name prop', () => {
260
+ // Prevent throwing an error in the console when this test is executed. We fix this and the end of this test.
261
+ const err = console.error;
262
+ console.error = jest.fn();
263
+
264
+ let actual;
265
+
266
+ try {
267
+ // @ts-ignore: mandatory props (test for non-typescript react projects)
268
+ createCheckbox((defaultParams) => ({
269
+ children: 'test',
270
+ }));
271
+ } catch (e: any) {
272
+ actual = e.message;
273
+ }
274
+
275
+ const expected = "Please pass a 'name' prop to your <Checkbox> component.";
276
+
277
+ expect(actual).toEqual(expected);
278
+
279
+ console.error = err;
144
280
  });
145
281
  });
@@ -11,7 +11,7 @@ import { FormSelector } from '../form.interfaces';
11
11
 
12
12
  const isToggle = (children: ReactNode) => (children as ReactElement)?.props?.['data-toggle'];
13
13
 
14
- export interface CheckboxProps extends ComponentPropsWithRef<'input'>, FormSelector {
14
+ export interface Props extends ComponentPropsWithRef<'input'>, FormSelector {
15
15
  children: ReactNode;
16
16
  label?: string;
17
17
  indeterminate?: boolean;
@@ -20,7 +20,7 @@ export interface CheckboxProps extends ComponentPropsWithRef<'input'>, FormSelec
20
20
  onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
21
21
  }
22
22
 
23
- export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
23
+ export const Checkbox = React.forwardRef<HTMLInputElement, Props>(
24
24
  (
25
25
  {
26
26
  children,
@@ -39,7 +39,7 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
39
39
  formSelectorWrapperProps,
40
40
  onChange,
41
41
  ...rest
42
- }: CheckboxProps,
42
+ }: Props,
43
43
  ref
44
44
  ) => {
45
45
  const { errorId, identifier, describedBy } = useFormSelector({
@@ -53,7 +53,7 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
53
53
 
54
54
  useEffect(() => {
55
55
  if (!name) {
56
- console.error("Please pass a 'name' prop to your <Checkbox> component.");
56
+ throw new Error("Please pass a 'name' prop to your <Checkbox> component.");
57
57
  }
58
58
 
59
59
  if (typeof children === 'object' && !isToggle(children) && indeterminate === undefined) {
@@ -83,17 +83,17 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
83
83
 
84
84
  const renderNestedCheckboxes = () => (
85
85
  <ul className={classes['checkbox-list']}>
86
- {React.Children.map(children as ReactNode[], (child) => {
86
+ {React.Children.map(children as ReactElement[], (child) => {
87
87
  return (
88
88
  <li>
89
89
  <Checkbox
90
- {...(child as ReactElement).props}
90
+ {...child.props}
91
91
  parentHelperId={parentHelperId}
92
92
  parentErrorId={parentErrorId}
93
93
  error={error}
94
- disabled={disabled ? disabled : (child as CheckboxProps).disabled}
94
+ disabled={child.props.disabled ? child.props.disabled : disabled}
95
95
  >
96
- {(child as ReactElement).props.children}
96
+ {child.props.children}
97
97
  </Checkbox>
98
98
  </li>
99
99
  );
@@ -1,14 +1,19 @@
1
- import React, { ComponentPropsWithRef, useEffect, useState } from 'react';
1
+ import React, { ComponentPropsWithRef, createRef, RefObject, useEffect } from 'react';
2
2
  import classes from './Select.module.scss';
3
3
 
4
4
  export interface Props extends ComponentPropsWithRef<'li'> {
5
5
  children: string;
6
6
  value: string;
7
7
  disabled?: boolean;
8
- selected?: boolean;
8
+ isSelected?: boolean;
9
+ selectOpened?: boolean;
10
+ hasFocus?: boolean;
11
+ shouldClick?: boolean;
12
+ isSearching?: boolean;
9
13
  label?: string;
10
- filter?: string;
11
- onOptionSelect?: (event: React.SyntheticEvent<HTMLLIElement>) => void;
14
+ childIndex?: number;
15
+ onOptionSelect?: (ref: React.RefObject<HTMLLIElement>) => void;
16
+ onFocusChange?: (childIndex: number) => void;
12
17
  }
13
18
 
14
19
  export const Option = React.forwardRef<HTMLLIElement, Props>(
@@ -16,44 +21,57 @@ export const Option = React.forwardRef<HTMLLIElement, Props>(
16
21
  {
17
22
  children,
18
23
  className,
19
- selected = false,
24
+ isSelected = false,
25
+ shouldClick,
26
+ hasFocus,
27
+ selectOpened,
28
+ isSearching,
29
+ childIndex,
20
30
  onOptionSelect,
31
+ onFocusChange,
21
32
  disabled,
22
- filter,
23
33
  value,
24
34
  ...rest
25
35
  }: Props,
26
36
  ref
27
37
  ) => {
28
- const [showOption, setShowOption] = useState(true);
38
+ let innerOptionRef = (ref as RefObject<HTMLLIElement>) || createRef<HTMLLIElement>();
29
39
 
30
- const onSelectHandler = (event: React.SyntheticEvent<HTMLLIElement>) => {
31
- if (onOptionSelect) onOptionSelect(event);
32
- };
40
+ useEffect(() => {
41
+ if (isSelected && innerOptionRef.current && shouldClick) {
42
+ innerOptionRef.current.click();
43
+ }
44
+ }, [isSelected, shouldClick]);
33
45
 
34
46
  useEffect(() => {
35
- if (filter) {
36
- setShowOption(children.toLowerCase().match(filter.toLowerCase()) !== null);
37
- } else {
38
- setShowOption(true);
47
+ if (innerOptionRef.current && hasFocus && selectOpened && !isSearching) {
48
+ onFocusChange && childIndex && onFocusChange(childIndex);
49
+ innerOptionRef.current.focus();
39
50
  }
40
- }, [filter]);
51
+ }, [hasFocus, innerOptionRef, selectOpened, isSearching]);
41
52
 
42
- if (!showOption) return null;
53
+ const onSelectHandler = () => {
54
+ if (onOptionSelect) onOptionSelect(innerOptionRef);
55
+ };
43
56
 
44
57
  return (
45
58
  <li
46
59
  {...rest}
47
- ref={ref}
60
+ ref={innerOptionRef}
48
61
  data-value={value}
49
- className={`${selected ? classes['selected-option'] : ''} ${
62
+ className={`${isSelected ? classes['selected-option'] : ''} ${
50
63
  disabled ? classes.disabled : ''
51
64
  } ${className ?? ''}`}
52
65
  onClick={onSelectHandler}
53
- onKeyPress={(e) => {
54
- e.key === 'Enter' && onSelectHandler(e);
66
+ onKeyDownCapture={(event) => {
67
+ if (event.code === 'Enter') {
68
+ event.stopPropagation();
69
+ event.preventDefault();
70
+
71
+ onSelectHandler();
72
+ }
55
73
  }}
56
- aria-selected={selected}
74
+ aria-selected={isSelected}
57
75
  role="option"
58
76
  tabIndex={disabled ? -1 : 0}
59
77
  >
@@ -37,7 +37,7 @@ $listItemHeight: 2.125rem;
37
37
  border-color: var(--default);
38
38
  }
39
39
 
40
- button {
40
+ .custom-select {
41
41
  width: 100%;
42
42
  min-height: 4rem;
43
43
  background-color: transparent;