@onewelcome/react-lib-components 0.1.4-alpha → 0.1.7-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 (47) 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/Form/Checkbox/Checkbox.d.ts +2 -2
  5. package/dist/Form/Toggle/Toggle.d.ts +1 -1
  6. package/dist/Link/Link.d.ts +4 -3
  7. package/dist/Notifications/BaseModal/BaseModal.d.ts +4 -2
  8. package/dist/Notifications/SlideInModal/SlideInModal.d.ts +4 -0
  9. package/dist/StatusIndicator/StatusIndicator.d.ts +9 -0
  10. package/dist/index.d.ts +43 -42
  11. package/dist/react-lib-components.cjs.development.js +2208 -2033
  12. package/dist/react-lib-components.cjs.development.js.map +1 -1
  13. package/dist/react-lib-components.cjs.production.min.js +1 -1
  14. package/dist/react-lib-components.cjs.production.min.js.map +1 -1
  15. package/dist/react-lib-components.esm.js +2207 -2034
  16. package/dist/react-lib-components.esm.js.map +1 -1
  17. package/package.json +1 -1
  18. package/src/Button/BaseButton.module.scss +3 -18
  19. package/src/Button/Button.module.scss +4 -311
  20. package/src/Button/IconButton.module.scss +21 -128
  21. package/src/Button/IconButton.test.tsx +24 -0
  22. package/src/Button/IconButton.tsx +6 -1
  23. package/src/ContextMenu/ContextMenu.test.tsx +121 -6
  24. package/src/ContextMenu/ContextMenu.tsx +84 -6
  25. package/src/ContextMenu/ContextMenuItem.tsx +57 -9
  26. package/src/Form/Checkbox/Checkbox.test.tsx +144 -8
  27. package/src/Form/Checkbox/Checkbox.tsx +8 -8
  28. package/src/Form/Toggle/Toggle.test.tsx +45 -19
  29. package/src/Form/Toggle/Toggle.tsx +3 -3
  30. package/src/Form/Wrapper/CheckboxWrapper/CheckboxWrapper.test.tsx +1 -1
  31. package/src/Link/Link.module.scss +20 -0
  32. package/src/Link/Link.test.tsx +33 -0
  33. package/src/Link/Link.tsx +8 -2
  34. package/src/Notifications/BaseModal/BaseModal.test.tsx +75 -11
  35. package/src/Notifications/BaseModal/BaseModal.tsx +27 -6
  36. package/src/Notifications/Dialog/Dialog.tsx +1 -1
  37. package/src/Notifications/SlideInModal/SlideInModal.module.scss +36 -0
  38. package/src/Notifications/SlideInModal/SlideInModal.test.tsx +69 -0
  39. package/src/Notifications/SlideInModal/SlideInModal.tsx +31 -0
  40. package/src/StatusIndicator/StatusIndicator.module.scss +27 -0
  41. package/src/StatusIndicator/StatusIndicator.test.tsx +127 -0
  42. package/src/StatusIndicator/StatusIndicator.tsx +25 -0
  43. package/src/Tiles/Tile.module.scss +1 -1
  44. package/src/Tiles/Tile.test.tsx +4 -4
  45. package/src/index.ts +79 -47
  46. package/src/mixins.module.scss +171 -0
  47. package/src/readyclasses.module.scss +0 -30
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
2
  import { ContextMenu, Props } from './ContextMenu';
3
- import { render, getByRole } from '@testing-library/react';
3
+ import { render } from '@testing-library/react';
4
4
  import { Button } from '../Button/Button';
5
5
  import { ContextMenuItem } from './ContextMenuItem';
6
6
  import userEvent from '@testing-library/user-event';
@@ -15,8 +15,12 @@ const defaultParams: Props = {
15
15
  <ContextMenuItem onClick={onClick} data-testid="contextmenuitem" key="1">
16
16
  Example item 1
17
17
  </ContextMenuItem>,
18
- <ContextMenuItem key="2">Example item 2</ContextMenuItem>,
19
- <ContextMenuItem key="3">Example item 3</ContextMenuItem>,
18
+ <ContextMenuItem onClick={onClick} data-testid="contextmenuitem2" key="2">
19
+ Example item 2
20
+ </ContextMenuItem>,
21
+ <ContextMenuItem onClick={onClick} data-testid="contextmenuitem3" key="3">
22
+ Example item 3
23
+ </ContextMenuItem>,
20
24
  ],
21
25
  show: false,
22
26
  onShow: onShow,
@@ -61,12 +65,10 @@ describe('ContextMenu should render', () => {
61
65
  show: true,
62
66
  }));
63
67
 
64
- const contextmenuitem = getByTestId('contextmenuitem');
65
- const button = getByRole(contextmenuitem, 'button');
68
+ const button = getByTestId('contextmenuitem');
66
69
 
67
70
  userEvent.click(button);
68
71
 
69
- expect(contextmenuitem).toBeTruthy();
70
72
  expect(onClick).toHaveBeenCalled();
71
73
  });
72
74
 
@@ -117,3 +119,116 @@ describe('ref should work', () => {
117
119
  render(<ExampleComponent propagateRef={refCheck} />);
118
120
  });
119
121
  });
122
+
123
+ describe('accessibility controls', () => {
124
+ it('opening works correctly with arrow key down, then navigation should work with arrow keys', () => {
125
+ const { getByTestId, trigger } = createContextMenu();
126
+ const firstContextMenuItem = getByTestId('contextmenuitem');
127
+ const secondContextMenuItem = getByTestId('contextmenuitem2');
128
+ const thirdContextMenuItem = getByTestId('contextmenuitem3');
129
+
130
+ userEvent.keyboard('{arrowdown}');
131
+ userEvent.keyboard('{arrowdown}');
132
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
133
+ expect(firstContextMenuItem).toHaveFocus();
134
+
135
+ userEvent.keyboard('{arrowdown}');
136
+ expect(secondContextMenuItem).toHaveFocus();
137
+
138
+ userEvent.keyboard('{arrowdown}');
139
+ expect(thirdContextMenuItem).toHaveFocus();
140
+
141
+ userEvent.keyboard('{arrowdown}');
142
+ expect(firstContextMenuItem).toHaveFocus();
143
+
144
+ userEvent.keyboard('{arrowup}');
145
+ expect(thirdContextMenuItem).toHaveFocus();
146
+
147
+ userEvent.keyboard('{arrowup}');
148
+ expect(secondContextMenuItem).toHaveFocus();
149
+ });
150
+
151
+ it('opens correctly with enter key, closing works with escape key.', async () => {
152
+ const { trigger } = createContextMenu();
153
+
154
+ userEvent.keyboard('{enter}');
155
+
156
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
157
+
158
+ userEvent.keyboard('{escape}');
159
+
160
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
161
+ });
162
+
163
+ it('opens correctly with space, home and end buttons work', () => {
164
+ const { trigger, getByTestId } = createContextMenu();
165
+ const firstContextMenuItem = getByTestId('contextmenuitem');
166
+ const thirdContextMenuItem = getByTestId('contextmenuitem3');
167
+
168
+ userEvent.keyboard('{space}');
169
+
170
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
171
+
172
+ userEvent.keyboard('{end}');
173
+
174
+ expect(thirdContextMenuItem).toHaveFocus();
175
+
176
+ userEvent.keyboard('{home}');
177
+
178
+ expect(firstContextMenuItem).toHaveFocus();
179
+ });
180
+
181
+ it('opens correctly with space, navigate with arrow keys, select with enter', () => {
182
+ onClick.mockImplementation((e) => {
183
+ expect(e.target.getAttribute('data-testid')).toBe('contextmenuitem3');
184
+ });
185
+
186
+ const { trigger, getByTestId } = createContextMenu((defaultParams) => ({
187
+ ...defaultParams,
188
+ }));
189
+ const thirdContextMenuItem = getByTestId('contextmenuitem3');
190
+
191
+ userEvent.keyboard('{space}');
192
+
193
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
194
+
195
+ userEvent.keyboard('{arrowdown}');
196
+ userEvent.keyboard('{arrowdown}');
197
+ userEvent.keyboard('{arrowdown}');
198
+
199
+ expect(thirdContextMenuItem).toHaveFocus();
200
+
201
+ userEvent.keyboard('{enter}');
202
+
203
+ expect(onClick).toHaveBeenCalled();
204
+ });
205
+
206
+ it('opens correctly with enter, navigate with arrow keys, select with space', () => {
207
+ onClick.mockImplementation((e) => {
208
+ expect(e.target.getAttribute('data-testid')).toBe('contextmenuitem3');
209
+ });
210
+
211
+ const { trigger, getByTestId } = createContextMenu((defaultParams) => ({
212
+ ...defaultParams,
213
+ }));
214
+ const thirdContextMenuItem = getByTestId('contextmenuitem3');
215
+
216
+ userEvent.keyboard('{enter}');
217
+
218
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
219
+
220
+ userEvent.keyboard('{arrowdown}');
221
+ userEvent.keyboard('{arrowdown}');
222
+ userEvent.keyboard('{arrowdown}');
223
+
224
+ expect(thirdContextMenuItem).toHaveFocus();
225
+
226
+ userEvent.keyboard('{space}');
227
+
228
+ expect(onClick).toHaveBeenCalled();
229
+
230
+ userEvent.keyboard('{space}');
231
+
232
+ expect(thirdContextMenuItem).toHaveFocus();
233
+ });
234
+ });
@@ -1,16 +1,22 @@
1
- import React, { ComponentPropsWithRef, ReactElement, useEffect, useRef, useState } from 'react';
1
+ import React, {
2
+ ComponentPropsWithRef,
3
+ ReactElement,
4
+ ReactNode,
5
+ useEffect,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
2
9
  import { Props as ButtonProps } from '../Button/Button';
3
10
  import { Props as IconButtonProps } from '../Button/IconButton';
4
11
  import { Popover } from '../Popover/Popover';
5
12
  import { Placement, Offset } from '../hooks/usePosition';
6
- import { Props as ContextMenuItemProps } from './ContextMenuItem';
7
13
  import classes from './ContextMenu.module.scss';
8
14
  import { useBodyClick } from '../hooks/useBodyClick';
9
15
  import { createPortal } from 'react-dom';
10
16
 
11
17
  export interface Props extends ComponentPropsWithRef<'div'> {
12
18
  trigger: ReactElement<ButtonProps> | ReactElement<IconButtonProps>;
13
- children: ReactElement<ContextMenuItemProps> | ReactElement<ContextMenuItemProps>[];
19
+ children: ReactNode;
14
20
  placement?: Placement;
15
21
  transformOrigin?: Placement;
16
22
  offset?: Offset;
@@ -40,11 +46,63 @@ export const ContextMenu = React.forwardRef<HTMLDivElement, Props>(
40
46
  ) => {
41
47
  const anchorEl = useRef<HTMLButtonElement>(null);
42
48
  const [showContextMenu, setShowContextMenu] = useState(show);
49
+ const [selectedContextMenuItem, setSelectedContextMenuItem] = useState(-1);
50
+ const [focusedContextMenuItem, setFocusedContextMenuItem] = useState(-1);
51
+ const [shouldClick, setShouldClick] =
52
+ useState(
53
+ false
54
+ ); /** We need this, because whenever we use the arrow keys to select the contextmenu item, and we focus the currently selected item it fires the "click" listener in ContextMenuItem component. Instead, we only want this to fire if we press "enter" or "spacebar" so we set this to true whenever that is the case, and back to false when it has been executed. */
55
+ const [childrenCount] = useState(React.Children.count(children));
43
56
 
44
57
  if (!id) {
45
58
  throw new Error('You need to provide an ID to the context menu');
46
59
  }
47
60
 
61
+ const onArrowNavigation = (event: React.KeyboardEvent) => {
62
+ if (focusedContextMenuItem === -1 && selectedContextMenuItem !== -1) {
63
+ setFocusedContextMenuItem(selectedContextMenuItem);
64
+ }
65
+
66
+ event.preventDefault();
67
+ switch (event.code) {
68
+ case 'ArrowDown':
69
+ if (!showContextMenu) {
70
+ setShowContextMenu(true);
71
+ return;
72
+ }
73
+ setFocusedContextMenuItem((prevState) => {
74
+ return prevState + 1 > childrenCount - 1 ? 0 : prevState + 1;
75
+ });
76
+ return;
77
+ case 'ArrowUp':
78
+ setFocusedContextMenuItem((prevState) => {
79
+ return prevState - 1 < 0 ? childrenCount - 1 : prevState - 1;
80
+ });
81
+ return;
82
+ case 'Enter':
83
+ case 'Space':
84
+ if (!showContextMenu) {
85
+ setShowContextMenu(true);
86
+ return;
87
+ }
88
+
89
+ setShouldClick(true);
90
+ setSelectedContextMenuItem(focusedContextMenuItem);
91
+ setShowContextMenu(false);
92
+ return;
93
+ case 'Tab':
94
+ case 'Escape':
95
+ setShowContextMenu(false);
96
+ return;
97
+ case 'End':
98
+ setFocusedContextMenuItem(childrenCount - 1);
99
+ return;
100
+ case 'Home':
101
+ setFocusedContextMenuItem(0);
102
+ return;
103
+ }
104
+ };
105
+
48
106
  useBodyClick(
49
107
  (event) => {
50
108
  return showContextMenu && anchorEl.current !== event.target;
@@ -60,6 +118,8 @@ export const ContextMenu = React.forwardRef<HTMLDivElement, Props>(
60
118
  onShow && onShow();
61
119
  } else {
62
120
  onClose && onClose();
121
+ setFocusedContextMenuItem(-1);
122
+ anchorEl.current && anchorEl.current.focus();
63
123
  }
64
124
  }, [showContextMenu]);
65
125
 
@@ -68,13 +128,31 @@ export const ContextMenu = React.forwardRef<HTMLDivElement, Props>(
68
128
  id: id,
69
129
  'aria-haspopup': 'true',
70
130
  'aria-controls': `${id}-menu`,
71
- 'aria-expanded': show,
131
+ 'aria-expanded': showContextMenu,
72
132
  onClick: () => setShowContextMenu(!showContextMenu),
133
+ tabIndex: 0,
73
134
  ref: anchorEl,
74
135
  });
75
136
 
137
+ const renderChildren = () => {
138
+ return React.Children.map(children, (child, index) => {
139
+ return React.cloneElement(child as ReactElement, {
140
+ onFocusChange: (childIndex: number) => setFocusedContextMenuItem(childIndex),
141
+ onSelectedChange: (childIndex: number) => {
142
+ setSelectedContextMenuItem(childIndex);
143
+ setShouldClick(false);
144
+ },
145
+ childIndex: index,
146
+ hasFocus: focusedContextMenuItem === index,
147
+ isSelected: selectedContextMenuItem === index,
148
+ contextMenuOpened: showContextMenu,
149
+ shouldClick: shouldClick,
150
+ });
151
+ });
152
+ };
153
+
76
154
  return (
77
- <div {...rest} ref={ref} className={classes['context-menu']}>
155
+ <div {...rest} ref={ref} onKeyDown={onArrowNavigation} className={classes['context-menu']}>
78
156
  {renderTrigger()}
79
157
  {createPortal(
80
158
  <Popover
@@ -85,7 +163,7 @@ export const ContextMenu = React.forwardRef<HTMLDivElement, Props>(
85
163
  show={showContextMenu}
86
164
  >
87
165
  <ul className={classes.menu} id={`${id}-menu`} aria-describedby={id} role="menu">
88
- {children}
166
+ {renderChildren()}
89
167
  </ul>
90
168
  </Popover>,
91
169
  domRoot
@@ -1,15 +1,63 @@
1
- import React, { HTMLProps } from 'react';
1
+ import React, { ComponentPropsWithRef, createRef, RefObject, useEffect } from 'react';
2
2
  import classes from './ContextMenuItem.module.scss';
3
3
 
4
- export interface Props extends Omit<HTMLProps<HTMLLIElement>, 'onClick'> {
4
+ export interface Props extends Omit<ComponentPropsWithRef<'button'>, 'onClick'> {
5
5
  children?: string;
6
+ hasFocus?: boolean;
7
+ isSelected?: boolean;
8
+ childIndex?: number;
9
+ shouldClick?: boolean;
10
+ contextMenuOpened?: boolean;
6
11
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
12
+ onFocusChange?: (childIndex: number) => void;
13
+ onSelectedChange?: (childIndex: number) => void;
7
14
  }
8
15
 
9
- export const ContextMenuItem = ({ children, onClick, ...rest }: Props) => {
10
- return (
11
- <li {...rest} role="menuitem" className={classes['context-menu-item']}>
12
- <button onClick={(event) => onClick && onClick(event)}>{children}</button>
13
- </li>
14
- );
15
- };
16
+ export const ContextMenuItem = React.forwardRef<HTMLButtonElement, Props>(
17
+ (
18
+ {
19
+ children,
20
+ onClick,
21
+ onFocusChange,
22
+ onSelectedChange,
23
+ hasFocus,
24
+ isSelected,
25
+ childIndex,
26
+ contextMenuOpened,
27
+ shouldClick,
28
+ ...rest
29
+ }: Props,
30
+ ref
31
+ ) => {
32
+ let innerButtonRef = (ref as RefObject<HTMLButtonElement>) || createRef<HTMLButtonElement>();
33
+
34
+ useEffect(() => {
35
+ if (isSelected && innerButtonRef.current && shouldClick) {
36
+ innerButtonRef.current.click();
37
+ }
38
+ }, [isSelected, shouldClick]);
39
+
40
+ useEffect(() => {
41
+ if (innerButtonRef.current && hasFocus && contextMenuOpened) {
42
+ onFocusChange && childIndex && onFocusChange(childIndex);
43
+ innerButtonRef.current.focus();
44
+ }
45
+ }, [hasFocus, innerButtonRef, contextMenuOpened]);
46
+
47
+ return (
48
+ <li role="menuitem" className={classes['context-menu-item']}>
49
+ <button
50
+ {...rest}
51
+ ref={innerButtonRef}
52
+ data-focus={hasFocus}
53
+ onClick={(event) => {
54
+ onClick && onClick(event);
55
+ onSelectedChange && childIndex && onSelectedChange(childIndex);
56
+ }}
57
+ >
58
+ {children}
59
+ </button>
60
+ </li>
61
+ );
62
+ }
63
+ );
@@ -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
  );