@lumx/react 3.3.1-alpha.0 → 3.3.1

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 (117) hide show
  1. package/_internal/ClickAwayProvider.js.map +1 -1
  2. package/_internal/types.d.ts +0 -2
  3. package/index.d.ts +2 -0
  4. package/index.js +347 -75
  5. package/index.js.map +1 -1
  6. package/package.json +23 -19
  7. package/src/components/autocomplete/Autocomplete.test.tsx +55 -142
  8. package/src/components/autocomplete/AutocompleteMultiple.test.tsx +37 -75
  9. package/src/components/autocomplete/__mockData__/index.ts +6 -1
  10. package/src/components/badge/Badge.test.tsx +20 -64
  11. package/src/components/button/Button.test.tsx +44 -121
  12. package/src/components/button/ButtonGroup.test.tsx +16 -39
  13. package/src/components/button/IconButton.stories.tsx +7 -0
  14. package/src/components/button/IconButton.test.tsx +37 -78
  15. package/src/components/button/IconButton.tsx +8 -1
  16. package/src/components/checkbox/Checkbox.test.tsx +62 -67
  17. package/src/components/chip/Chip.test.tsx +89 -139
  18. package/src/components/chip/ChipGroup.test.tsx +27 -10
  19. package/src/components/date-picker/DatePicker.test.tsx +15 -23
  20. package/src/components/date-picker/DatePickerControlled.test.tsx +24 -20
  21. package/src/components/date-picker/DatePickerField.test.tsx +43 -27
  22. package/src/components/dialog/Dialog.test.tsx +36 -35
  23. package/src/components/divider/Divider.test.tsx +23 -69
  24. package/src/components/dropdown/Dropdown.test.tsx +30 -61
  25. package/src/components/expansion-panel/ExpansionPanel.test.tsx +12 -8
  26. package/src/components/flag/Flag.test.tsx +28 -53
  27. package/src/components/generic-block/GenericBlock.test.tsx +93 -89
  28. package/src/components/grid-column/GridColumn.stories.tsx +3 -3
  29. package/src/components/icon/Icon.test.tsx +80 -64
  30. package/src/components/index.ts +0 -2
  31. package/src/components/inline-list/InlineList.test.tsx +30 -17
  32. package/src/components/input-helper/InputHelper.test.tsx +21 -81
  33. package/src/components/input-label/InputLabel.test.tsx +19 -61
  34. package/src/components/lightbox/Lightbox.test.tsx +3 -2
  35. package/src/components/link/Link.test.tsx +47 -31
  36. package/src/components/link-preview/LinkPreview.test.tsx +51 -51
  37. package/src/components/message/Message.test.tsx +31 -52
  38. package/src/components/mosaic/Mosaic.test.tsx +56 -72
  39. package/src/components/notification/Notification.test.tsx +51 -82
  40. package/src/components/popover/Popover.tsx +7 -9
  41. package/src/components/progress-tracker/ProgressTracker.test.tsx +20 -33
  42. package/src/components/progress-tracker/ProgressTrackerProvider.test.tsx +61 -36
  43. package/src/components/progress-tracker/ProgressTrackerStep.test.tsx +19 -109
  44. package/src/components/progress-tracker/ProgressTrackerStepPanel.test.tsx +21 -58
  45. package/src/components/progress-tracker/ProgressTrackerStepPanel.tsx +1 -1
  46. package/src/components/radio-button/RadioButton.test.tsx +78 -92
  47. package/src/components/radio-button/RadioGroup.test.tsx +13 -59
  48. package/src/components/select/Select.test.tsx +115 -284
  49. package/src/components/select/SelectMultiple.stories.tsx +105 -2
  50. package/src/components/select/SelectMultiple.test.tsx +126 -322
  51. package/src/components/select/WithSelectContext.tsx +10 -4
  52. package/src/components/side-navigation/SideNavigation.test.tsx +22 -35
  53. package/src/components/side-navigation/SideNavigationItem.test.tsx +72 -139
  54. package/src/components/switch/Switch.test.tsx +70 -149
  55. package/src/components/table/Table.test.tsx +2 -0
  56. package/src/components/table/TableBody.test.tsx +18 -8
  57. package/src/components/table/TableCell.test.tsx +34 -9
  58. package/src/components/table/TableHeader.test.tsx +18 -8
  59. package/src/components/table/TableRow.test.tsx +28 -8
  60. package/src/components/tabs/Tab.test.tsx +27 -96
  61. package/src/components/tabs/TabList.test.tsx +21 -56
  62. package/src/components/tabs/TabPanel.test.tsx +20 -55
  63. package/src/components/tabs/TabPanel.tsx +1 -1
  64. package/src/components/tabs/TabProvider.test.tsx +158 -37
  65. package/src/components/tabs/test-utils.ts +39 -0
  66. package/src/components/text-field/TextField.stories.tsx +14 -5
  67. package/src/components/text-field/TextField.test.tsx +54 -8
  68. package/src/components/text-field/TextField.tsx +49 -5
  69. package/src/components/tooltip/Tooltip.test.tsx +134 -75
  70. package/src/components/tooltip/useInjectTooltipRef.tsx +9 -2
  71. package/src/components/uploader/Uploader.test.tsx +60 -48
  72. package/src/components/user-block/UserBlock.test.tsx +69 -13
  73. package/src/hooks/useFocusTrap.ts +2 -2
  74. package/src/testing/utils/commonTestsSuiteRTL.ts +18 -8
  75. package/src/testing/utils/index.ts +0 -1
  76. package/src/utils/flattenChildren.ts +5 -0
  77. package/src/components/autocomplete/__snapshots__/Autocomplete.test.tsx.snap +0 -213
  78. package/src/components/autocomplete/__snapshots__/AutocompleteMultiple.test.tsx.snap +0 -88
  79. package/src/components/badge/__snapshots__/Badge.test.tsx.snap +0 -11
  80. package/src/components/button/ButtonRoot.test.tsx +0 -203
  81. package/src/components/button/__snapshots__/Button.test.tsx.snap +0 -96
  82. package/src/components/button/__snapshots__/ButtonGroup.test.tsx.snap +0 -22
  83. package/src/components/button/__snapshots__/ButtonRoot.test.tsx.snap +0 -160
  84. package/src/components/button/__snapshots__/IconButton.test.tsx.snap +0 -83
  85. package/src/components/checkbox/__snapshots__/Checkbox.test.tsx.snap +0 -141
  86. package/src/components/chip/__snapshots__/Chip.test.tsx.snap +0 -12
  87. package/src/components/chip/__snapshots__/ChipGroup.test.tsx.snap +0 -29
  88. package/src/components/date-picker/__snapshots__/DatePicker.test.tsx.snap +0 -22
  89. package/src/components/date-picker/__snapshots__/DatePickerControlled.test.tsx.snap +0 -597
  90. package/src/components/date-picker/__snapshots__/DatePickerField.test.tsx.snap +0 -43
  91. package/src/components/divider/__snapshots__/Divider.test.tsx.snap +0 -9
  92. package/src/components/dropdown/__snapshots__/Dropdown.test.tsx.snap +0 -35
  93. package/src/components/icon/__snapshots__/Icon.test.tsx.snap +0 -49
  94. package/src/components/input-helper/__snapshots__/InputHelper.test.tsx.snap +0 -9
  95. package/src/components/input-label/__snapshots__/InputLabel.test.tsx.snap +0 -10
  96. package/src/components/link/__snapshots__/Link.test.tsx.snap +0 -29
  97. package/src/components/message/__snapshots__/Message.test.tsx.snap +0 -15
  98. package/src/components/notification/__snapshots__/Notification.test.tsx.snap +0 -34
  99. package/src/components/progress-tracker/__snapshots__/ProgressTracker.test.tsx.snap +0 -41
  100. package/src/components/progress-tracker/__snapshots__/ProgressTrackerStep.test.tsx.snap +0 -141
  101. package/src/components/progress-tracker/__snapshots__/ProgressTrackerStepPanel.test.tsx.snap +0 -25
  102. package/src/components/radio-button/__snapshots__/RadioButton.test.tsx.snap +0 -113
  103. package/src/components/radio-button/__snapshots__/RadioGroup.test.tsx.snap +0 -26
  104. package/src/components/select/__snapshots__/Select.test.tsx.snap +0 -43
  105. package/src/components/select/__snapshots__/SelectMultiple.test.tsx.snap +0 -87
  106. package/src/components/side-navigation/__snapshots__/SideNavigation.test.tsx.snap +0 -7
  107. package/src/components/side-navigation/__snapshots__/SideNavigationItem.test.tsx.snap +0 -30
  108. package/src/components/switch/__snapshots__/Switch.test.tsx.snap +0 -179
  109. package/src/components/tabs/__snapshots__/Tab.test.tsx.snap +0 -62
  110. package/src/components/tabs/__snapshots__/TabList.test.tsx.snap +0 -22
  111. package/src/components/tabs/__snapshots__/TabPanel.test.tsx.snap +0 -25
  112. package/src/components/tabs/test.mocks.ts +0 -33
  113. package/src/components/text-field/__snapshots__/TextField.test.tsx.snap +0 -42
  114. package/src/components/tooltip/__snapshots__/Tooltip.test.tsx.snap +0 -233
  115. package/src/components/uploader/__snapshots__/Uploader.test.tsx.snap +0 -14
  116. package/src/testing/utils/commonTestsSuite.ts +0 -71
  117. package/src/utils/flattenChildren.test.tsx +0 -58
@@ -1,405 +1,209 @@
1
- import React, { ReactElement, RefObject } from 'react';
1
+ import React from 'react';
2
2
 
3
- import { mount, ReactWrapper, shallow, ShallowWrapper } from 'enzyme';
4
- import 'jest-enzyme';
5
-
6
- import { mdiCloseCircle, mdiMenuDown } from '@lumx/icons';
7
- import { Kind, Theme } from '@lumx/react/components';
3
+ import { Theme } from '@lumx/react/components';
8
4
  import { Chip } from '@lumx/react/components/chip/Chip';
9
5
  import { Dropdown } from '@lumx/react/components/dropdown/Dropdown';
10
- import { Icon } from '@lumx/react/components/icon/Icon';
6
+ import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
7
+ import { render, within } from '@testing-library/react';
8
+ import {
9
+ getAllByClassName,
10
+ getByClassName,
11
+ queryAllByClassName,
12
+ queryByClassName,
13
+ } from '@lumx/react/testing/utils/queries';
14
+ import userEvent from '@testing-library/user-event';
11
15
 
12
- import { commonTestsSuite, Wrapper } from '@lumx/react/testing/utils';
13
- import { getBasicClass } from '@lumx/react/utils/className';
14
16
  import { SelectMultiple, SelectMultipleProps } from './SelectMultiple';
15
- import { DEFAULT_PROPS } from './WithSelectContext';
16
17
  import { SelectVariant } from './constants';
17
18
 
18
19
  const CLASSNAME = SelectMultiple.className as string;
19
20
 
20
21
  jest.mock('uid', () => ({ uid: () => 'uid' }));
21
22
 
22
- type SetupProps = Partial<SelectMultipleProps>;
23
-
24
23
  /**
25
24
  * Mounts the component and returns common DOM elements / data needed in multiple tests further down.
26
25
  */
27
- const setup = (props: SetupProps = {}, shallowRendering = true) => {
26
+ const setup = (props: Partial<SelectMultipleProps> = {}) => {
28
27
  const setupProps: SelectMultipleProps = {
29
28
  children: <span>Select Component</span>,
30
29
  value: [],
31
30
  ...props,
32
31
  };
33
-
34
- const renderer: (el: ReactElement) => Wrapper = shallowRendering ? shallow : mount;
35
- const wrapper = renderer(<SelectMultiple {...setupProps} />);
36
-
37
- return {
38
- container: wrapper.find('div').first(),
39
- dropdown: wrapper.find(Dropdown),
40
- error: wrapper
41
- .findWhere(
42
- (n: ShallowWrapper | ReactWrapper) => n.name() === 'InputHelper' && n.prop('kind') === Kind.error,
43
- )
44
- .first(),
45
- helper: wrapper.findWhere(
46
- (n: ShallowWrapper | ReactWrapper) => n.name() === 'InputHelper' && n.prop('kind') === Kind.info,
47
- ),
48
- input: wrapper.find('#select-uid:not(SelectMultipleField)').first(),
49
- chip: wrapper.find('Chip'),
50
- inputWrapper: wrapper.find('.lumx-select__wrapper'),
51
- props,
52
- wrapper,
53
- };
32
+ render(<SelectMultiple {...setupProps} />);
33
+ const select = getByClassName(document.body, CLASSNAME);
34
+ const getDropdown = () => queryByClassName(document.body, Dropdown.className as string);
35
+ const helpers = queryAllByClassName(select, `${CLASSNAME}__helper`);
36
+ const inputWrapper = queryByClassName(select, `${CLASSNAME}__wrapper`);
37
+ const chip = !inputWrapper ? queryByClassName(select, Chip.className as string) : null;
38
+ const valueChips = queryByClassName(select, `${CLASSNAME}__chips`);
39
+ return { props, select, getDropdown, helpers, inputWrapper, chip, valueChips };
54
40
  };
55
41
 
56
42
  describe(`<SelectMultiple>`, () => {
57
- // 1. Test render via snapshot (default states of component).
58
- describe('Snapshots and structure', () => {
59
- it('should render defaults', () => {
60
- const { wrapper } = setup();
61
- expect(wrapper).toMatchSnapshot();
62
- expect(wrapper).toExist();
63
- expect(wrapper).toHaveClassName(CLASSNAME);
64
- });
65
-
66
- it('should render chips', () => {
67
- const { wrapper } = setup({
68
- variant: SelectVariant.chip,
69
- });
70
- expect(wrapper).toMatchSnapshot();
71
- });
72
- });
73
-
74
43
  describe('Props', () => {
75
44
  it('should have default classNames', () => {
76
- const { wrapper, container } = setup();
77
- wrapper.update();
45
+ const { select, inputWrapper, valueChips, chip } = setup();
78
46
 
79
- expect(container).toHaveClassName(CLASSNAME);
80
- expect(container).toHaveClassName(
81
- getBasicClass({ prefix: CLASSNAME, type: 'theme', value: DEFAULT_PROPS.theme }),
47
+ expect(inputWrapper).toBeInTheDocument();
48
+ expect(valueChips).toBeEmptyDOMElement();
49
+ expect(chip).not.toBeInTheDocument();
50
+ expect(select.className).toMatchInlineSnapshot(
51
+ `"lumx-select lumx-select--has-multiple lumx-select lumx-select--is-empty lumx-select--theme-light"`,
82
52
  );
83
- expect(container).toHaveClassName(getBasicClass({ prefix: CLASSNAME, type: 'isEmpty', value: true }));
84
53
  });
85
54
 
86
55
  it('should use the given `theme`', () => {
87
- const testedProp = 'theme';
88
- const modifiedProps: SetupProps = {
89
- [testedProp]: Theme.dark,
90
- };
91
-
92
- const { container } = setup(modifiedProps);
93
-
94
- expect(container).toHaveClassName(
95
- getBasicClass({ prefix: CLASSNAME, type: testedProp, value: modifiedProps[testedProp] }),
96
- );
97
- expect(container).not.toHaveClassName(
98
- getBasicClass({ prefix: CLASSNAME, type: testedProp, value: DEFAULT_PROPS.theme }),
99
- );
56
+ const { select } = setup({ theme: Theme.dark });
57
+ expect(select).toHaveClass(`${CLASSNAME}--theme-dark`);
100
58
  });
101
59
 
102
60
  it('should use the given `isValid`', () => {
103
- const testedProp = 'isValid';
104
- const modifiedProps: SetupProps = {
105
- [testedProp]: true,
106
- };
107
-
108
- const { container } = setup(modifiedProps);
109
-
110
- expect(container).toHaveClassName(
111
- getBasicClass({ prefix: CLASSNAME, type: testedProp, value: modifiedProps[testedProp] }),
112
- );
113
- });
114
-
115
- it('should use the given `hasError`', () => {
116
- const testedProp = 'hasError';
117
- const modifiedProps: SetupProps = {
118
- [testedProp]: true,
119
- };
120
-
121
- const { container } = setup(modifiedProps);
122
-
123
- expect(container).toHaveClassName(
124
- getBasicClass({ prefix: CLASSNAME, type: testedProp, value: modifiedProps[testedProp] }),
125
- );
126
- });
127
-
128
- it('should display the given `error`', () => {
129
- const { container, error } = setup({
130
- error: 'You are not bold!',
131
- hasError: true,
132
- });
133
-
134
- expect(error).toExist();
135
- expect(container).toHaveClassName(getBasicClass({ prefix: CLASSNAME, type: 'hasError', value: true }));
136
- });
137
-
138
- it('should NOT display the given `error`', () => {
139
- const { container, error } = setup({
140
- error: 'You are not bold!',
141
- hasError: false,
142
- });
143
-
144
- expect(error).not.toExist();
145
- expect(container).not.toHaveClassName(getBasicClass({ prefix: CLASSNAME, type: 'hasError', value: true }));
61
+ const { select } = setup({ isValid: true });
62
+ expect(select).toHaveClass(`${CLASSNAME}--is-valid`);
146
63
  });
147
64
 
148
65
  it('should use the given `value`', () => {
149
- const { container } = setup({
150
- value: [''],
151
- });
66
+ const { select, valueChips } = setup({ value: ['val1', 'val2'] });
152
67
 
153
- expect(container).toHaveClassName(getBasicClass({ prefix: CLASSNAME, type: 'hasValue', value: true }));
154
- expect(container).not.toHaveClassName(getBasicClass({ prefix: CLASSNAME, type: 'isEmpty', value: true }));
155
- });
156
-
157
- it('should trigger the given `onDropdownClose` on dropdown close', () => {
158
- const onDropdownClose = jest.fn();
159
- const { dropdown } = setup({ onDropdownClose });
160
-
161
- const props: any = dropdown.props();
162
-
163
- props.onClose();
164
-
165
- expect(onDropdownClose).toHaveBeenCalled();
68
+ expect(select).not.toHaveClass(`${CLASSNAME}--is-empty`);
69
+ expect(select).toHaveClass(`${CLASSNAME}--has-value`);
70
+ expect(valueChips).not.toBeEmptyDOMElement();
71
+ expect(within(valueChips as any).queryByText('val1')).toBeInTheDocument();
72
+ expect(within(valueChips as any).queryByText('val2')).toBeInTheDocument();
166
73
  });
167
74
 
168
75
  it('should pass the given `isOpen` to the dropdown', () => {
169
- const isOpen = true;
170
- const { dropdown } = setup({ isOpen });
171
-
172
- expect(dropdown).toHaveProp('isOpen', isOpen);
76
+ const { getDropdown } = setup({ isOpen: true });
77
+ expect(getDropdown()).toBeInTheDocument();
173
78
  });
174
79
 
175
- it('should display the given `helper`', () => {
176
- const { helper } = setup({
177
- helper: 'Be bold',
80
+ describe('helpers', () => {
81
+ it('should display the given `error`', () => {
82
+ const { select, helpers, props } = setup({ hasError: true, error: 'You are not bold!' });
83
+ expect(select).toHaveClass(`${CLASSNAME}--has-error`);
84
+ expect(helpers.length).toBe(1);
85
+ expect(helpers[0]).toHaveTextContent(props.error as string);
178
86
  });
179
87
 
180
- expect(helper).toExist();
181
- });
88
+ it('should NOT display the given `error`', () => {
89
+ const { select, helpers } = setup({
90
+ error: 'You are not bold!',
91
+ hasError: false,
92
+ });
93
+ expect(select).not.toHaveClass(`${CLASSNAME}--has-error`);
94
+ expect(helpers.length).toBe(0);
95
+ });
182
96
 
183
- it('should display the given `helper` and `error`', () => {
184
- const { error, helper } = setup({
185
- error: 'You are not bold!',
186
- hasError: true,
187
- helper: 'Be bold',
97
+ it('should display the given `helper`', () => {
98
+ const { helpers, props } = setup({ helper: 'Be bold' });
99
+ expect(helpers.length).toBe(1);
100
+ expect(helpers[0]).toHaveTextContent(props.helper as string);
188
101
  });
189
102
 
190
- expect(error).toExist();
191
- expect(helper).toExist();
103
+ it('should display the given `helper` and `error`', () => {
104
+ const { select, helpers, props } = setup({
105
+ error: 'You are not bold!',
106
+ hasError: true,
107
+ helper: 'Be bold',
108
+ });
109
+ expect(select).toHaveClass(`${CLASSNAME}--has-error`);
110
+ expect(helpers.length).toBe(2);
111
+ expect(helpers[0]).toHaveTextContent(props.error as string);
112
+ expect(helpers[1]).toHaveTextContent(props.helper as string);
113
+ });
192
114
  });
193
115
 
194
- it('should have a data-id as prop', () => {
195
- const { inputWrapper } = setup(
196
- {
197
- 'data-id': 'select',
198
- },
199
- false,
200
- );
116
+ describe('chip variant', () => {
117
+ it('should render chip variant', () => {
118
+ const { select, inputWrapper, chip } = setup({ variant: SelectVariant.chip });
119
+ expect(inputWrapper).not.toBeInTheDocument();
120
+ expect(chip).toBeInTheDocument();
121
+ expect(select.className).toMatchInlineSnapshot(
122
+ `"lumx-select lumx-select--has-multiple lumx-select lumx-select--is-empty lumx-select--theme-light"`,
123
+ );
124
+ });
201
125
 
202
- expect(inputWrapper.prop('data-id')).toEqual('select');
203
- });
126
+ it('should render chip variant with value', () => {
127
+ const { select, chip } = setup({ variant: SelectVariant.chip, value: ['val1', 'val2'] });
128
+ expect(chip).toHaveTextContent('val1 +1');
129
+ expect(select.className).toMatchInlineSnapshot(
130
+ `"lumx-select lumx-select--has-multiple lumx-select lumx-select--has-value lumx-select--theme-light"`,
131
+ );
132
+ });
204
133
 
205
- it('should have a data-id as prop with Chip variant', () => {
206
- const { chip } = setup(
207
- {
134
+ it('should forward prop to chip variant', () => {
135
+ const { chip } = setup({
208
136
  'data-id': 'select',
209
137
  variant: SelectVariant.chip,
210
- },
211
- false,
212
- );
213
-
214
- expect(chip.prop('data-id')).toEqual('select');
138
+ });
139
+ expect(chip).toHaveAttribute('data-id', 'select');
140
+ });
215
141
  });
216
142
  });
217
143
 
218
- // 3. Test events.
219
144
  describe('Events', () => {
220
- const onClick: jest.Mock = jest.fn();
221
-
222
- beforeEach(() => {
223
- onClick.mockClear();
224
- });
225
-
226
145
  describe('should trigger `onInputClick` when the select button is clicked', () => {
227
- it('with input variant', () => {
228
- const { input } = setup({ onInputClick: onClick, variant: SelectVariant.input }, false);
229
-
230
- input.simulate('click');
146
+ it('with input variant', async () => {
147
+ const onClick = jest.fn();
148
+ const { inputWrapper } = setup({ onInputClick: onClick, variant: SelectVariant.input });
231
149
 
150
+ await userEvent.click(inputWrapper as any);
232
151
  expect(onClick).toHaveBeenCalled();
233
152
  });
234
153
 
235
- it('with chip variant', () => {
236
- const { input } = setup({ onInputClick: onClick, variant: SelectVariant.chip }, false);
237
-
238
- input.simulate('click');
154
+ it('with chip variant', async () => {
155
+ const onClick = jest.fn();
156
+ const { chip } = setup({ onInputClick: onClick, variant: SelectVariant.chip });
239
157
 
158
+ await userEvent.click(chip as any);
240
159
  expect(onClick).toHaveBeenCalled();
241
160
  });
242
161
  });
243
162
 
244
- it('should call onClear when an item is clicked with the correct value', () => {
245
- const onClear: jest.Mock = jest.fn();
246
- const { input } = setup(
247
- {
248
- onClear,
249
- value: ['val 1', 'val 2'],
250
- variant: SelectVariant.input,
251
- clearButtonProps: { label: 'Clear' },
252
- },
253
- false,
254
- );
255
-
256
- input.find('Chip').first().simulate('click');
257
-
258
- expect(onClear).toHaveBeenCalled();
259
- });
260
-
261
- it('should call onClear when a chip is clicked with the correct value', () => {
262
- const value1 = 'Value 1';
263
- const value2 = 'Value 2';
264
-
265
- const onClear: jest.Mock = jest.fn();
266
- const { input } = setup(
267
- {
268
- onClear,
269
- value: [value1, value2],
270
- variant: SelectVariant.chip,
271
- clearButtonProps: { label: 'Clear' },
272
- },
273
- false,
274
- );
275
-
276
- (input.find('Chip').first().props() as any).onAfterClick();
277
-
278
- expect(onClear).toHaveBeenCalled();
279
- });
280
- });
281
-
282
- // 4. Test conditions (i.e. things that display or not in the UI based on props).
283
- describe('Conditions', () => {
284
- describe('Input variant', () => {
285
- it('should render a div as the select input', () => {
286
- const { dropdown, input } = setup({}, false);
287
- const dropdownRef: RefObject<HTMLDivElement> = dropdown.prop('anchorRef');
288
- expect(input.type()).toBe('div');
289
- // Impossible to get the ref passed to a div so we check if ref.current is matching the input html.
290
- expect(input.html()).toEqual(dropdownRef && dropdownRef.current && dropdownRef.current.outerHTML);
163
+ it('should call onClear when an item is clicked with the correct value', async () => {
164
+ const onClear = jest.fn();
165
+ const { valueChips } = setup({
166
+ onClear,
167
+ value: ['val 1', 'val 2'],
168
+ variant: SelectVariant.input,
291
169
  });
292
170
 
293
- describe('Has multiple values', () => {
294
- const value1 = 'Value1';
295
- const value2 = 'Value2';
296
- const value3 = 'Value3';
297
- const hasMultipleValues: Partial<SetupProps> = {
298
- value: [value1, value2, value3],
299
- variant: SelectVariant.input,
300
- };
301
-
302
- it('should render the values selected in Chips if multiple ', () => {
303
- const { input, props } = setup({ ...hasMultipleValues }, false);
304
-
305
- expect(input.find(Chip).length).toBe(props.value?.length);
306
- expect(input.find(Chip).at(0).children().text()).toBe(props.value?.[0]);
307
- expect(input.find(Chip).at(1).children().text()).toBe(props.value?.[1]);
308
- expect(input.find(Chip).at(2).children().text()).toBe(props.value?.[2]);
309
- });
310
- });
171
+ const clearButtons = getAllByClassName(
172
+ valueChips as any,
173
+ `${Chip.className as string}__after--is-clickable`,
174
+ );
311
175
 
312
- describe('No value', () => {
313
- const placeholder = 'My placeholder';
314
- const hasNoValueProps: Partial<SetupProps> = {
315
- placeholder,
316
- value: [],
317
- variant: SelectVariant.input,
318
- };
176
+ expect(clearButtons.length).toBe(2);
319
177
 
320
- it('should render the placeholder when empty', () => {
321
- const { input } = setup({ ...hasNoValueProps }, false);
178
+ await userEvent.click(clearButtons[1]);
322
179
 
323
- expect(input.find(`.${CLASSNAME}__input-native--placeholder span`).first().text()).toBe(
324
- placeholder,
325
- );
326
- });
327
- });
180
+ expect(onClear).toHaveBeenCalledWith(expect.any(Object), 'val 2');
328
181
  });
329
182
 
330
- describe('Chip variant', () => {
331
- it('should render a Chip as the select input', () => {
332
- const { input } = setup({ variant: SelectVariant.chip }, false);
333
-
334
- expect(input.type()).toEqual(Chip);
335
- });
336
-
337
- describe('Has value', () => {
338
- const value = 'Value';
339
- const hasValueProps: Partial<SetupProps> = {
340
- value: [value],
341
- variant: SelectVariant.chip,
342
- };
343
-
344
- it('should render the value', () => {
345
- const label = 'The label';
346
- const { input } = setup(
347
- {
348
- ...hasValueProps,
349
- label,
350
- },
351
- false,
352
- );
353
-
354
- expect(input.find('.lumx-chip__label').text()).toEqual(value);
355
- });
356
-
357
- it('should render a close icon if there is a value', () => {
358
- const { input } = setup({ ...hasValueProps }, false);
359
-
360
- expect(input.find(Icon).first().prop('icon')).toBe(mdiCloseCircle);
361
- });
362
- });
363
-
364
- describe('Has multiple values', () => {
365
- const value = ['v1', 'v2', 'v3'];
366
-
367
- it('should render the value and additional text if multiple values', () => {
368
- const { input } = setup(
369
- {
370
- label: 'The label',
371
- value,
372
- variant: SelectVariant.chip,
373
- },
374
- false,
375
- );
183
+ it('should clear all with chip variant', async () => {
184
+ const value1 = 'Value 1';
185
+ const value2 = 'Value 2';
376
186
 
377
- expect(input.find('.lumx-chip__label').text()).toEqual(`${value[0]}\u00A0+${value.length - 1}`);
378
- });
187
+ const onClear = jest.fn();
188
+ const { chip } = setup({
189
+ onClear,
190
+ value: [value1, value2],
191
+ variant: SelectVariant.chip,
379
192
  });
380
193
 
381
- describe('No value', () => {
382
- const hasNoValue: Partial<SetupProps> = {
383
- value: [],
384
- variant: SelectVariant.chip,
385
- };
386
-
387
- it('should render the label', () => {
388
- const label = 'The label';
389
- const { input } = setup({ ...hasNoValue, label }, false);
194
+ const clearButton = getByClassName(chip as any, `${Chip.className as string}__after--is-clickable`);
390
195
 
391
- expect(input.find('.lumx-chip__label').text()).toEqual(label);
392
- });
393
-
394
- it('should render a MenuDown icon', () => {
395
- const { input } = setup({ ...hasNoValue }, false);
196
+ await userEvent.click(clearButton);
396
197
 
397
- expect(input.find(Icon).first().prop('icon')).toBe(mdiMenuDown);
398
- });
399
- });
198
+ expect(onClear).toHaveBeenCalled();
400
199
  });
401
200
  });
402
201
 
403
202
  // Common tests suite.
404
- commonTestsSuite(setup, { className: 'wrapper' }, { className: CLASSNAME });
203
+ commonTestsSuiteRTL(setup, {
204
+ baseClassName: CLASSNAME,
205
+ forwardClassName: 'select',
206
+ forwardAttributes: 'inputWrapper',
207
+ forwardRef: 'select',
208
+ });
405
209
  });
@@ -1,15 +1,15 @@
1
- import React, { Ref, useCallback, useMemo, useRef } from 'react';
2
-
3
1
  import classNames from 'classnames';
2
+ import React, { Ref, useCallback, useMemo, useRef } from 'react';
4
3
  import { uid } from 'uid';
5
4
 
5
+ import { Placement } from '@lumx/react';
6
6
  import { Kind, Theme } from '@lumx/react/components';
7
7
  import { Dropdown } from '@lumx/react/components/dropdown/Dropdown';
8
8
  import { InputHelper } from '@lumx/react/components/input-helper/InputHelper';
9
+ import { useFocusTrap } from '@lumx/react/hooks/useFocusTrap';
10
+ import { useListenFocus } from '@lumx/react/hooks/useListenFocus';
9
11
  import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
10
12
  import { mergeRefs } from '@lumx/react/utils/mergeRefs';
11
- import { useListenFocus } from '@lumx/react/hooks/useListenFocus';
12
- import { Placement } from '@lumx/react';
13
13
 
14
14
  import { CoreSelectProps, SelectVariant } from './constants';
15
15
 
@@ -30,6 +30,7 @@ export const WithSelectContext = (
30
30
  {
31
31
  children,
32
32
  className,
33
+ focusElement,
33
34
  isMultiple,
34
35
  closeOnClick = !isMultiple,
35
36
  disabled,
@@ -58,6 +59,7 @@ export const WithSelectContext = (
58
59
  const selectId = useMemo(() => id || `select-${uid()}`, [id]);
59
60
  const anchorRef = useRef<HTMLElement>(null);
60
61
  const selectRef = useRef<HTMLDivElement>(null);
62
+ const dropdownRef = useRef<HTMLDivElement>(null);
61
63
  const isFocus = useListenFocus(anchorRef);
62
64
 
63
65
  const handleKeyboardNav = useCallback(
@@ -77,6 +79,9 @@ export const WithSelectContext = (
77
79
  anchorRef?.current?.blur();
78
80
  };
79
81
 
82
+ // Handle focus trap.
83
+ useFocusTrap(isOpen && dropdownRef.current, focusElement?.current);
84
+
80
85
  return (
81
86
  <div
82
87
  ref={mergeRefs(ref, selectRef)}
@@ -125,6 +130,7 @@ export const WithSelectContext = (
125
130
  placement={Placement.BOTTOM_START}
126
131
  onClose={onClose}
127
132
  onInfiniteScroll={onInfiniteScroll}
133
+ ref={dropdownRef}
128
134
  >
129
135
  {children}
130
136
  </Dropdown>
@@ -1,52 +1,39 @@
1
- import React, { ReactElement } from 'react';
1
+ import React from 'react';
2
2
 
3
- import { mount, shallow } from 'enzyme';
4
- import 'jest-enzyme';
5
-
6
- import { Wrapper, commonTestsSuite } from '@lumx/react/testing/utils';
3
+ import { render } from '@testing-library/react';
4
+ import { getByClassName } from '@lumx/react/testing/utils/queries';
5
+ import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
6
+ import { Theme } from '@lumx/react';
7
7
 
8
8
  import { SideNavigation, SideNavigationProps } from './SideNavigation';
9
9
 
10
10
  const CLASSNAME = SideNavigation.className as string;
11
11
 
12
- type SetupProps = Partial<SideNavigationProps>;
13
-
14
12
  /**
15
13
  * Mounts the component and returns common DOM elements / data needed in multiple tests further down.
16
14
  */
17
- const setup = ({ ...propsOverride }: SetupProps = {}, shallowRendering = true) => {
18
- const props: any = { ...propsOverride };
19
- const renderer: (el: ReactElement) => Wrapper = shallowRendering ? shallow : mount;
20
- const wrapper: Wrapper = renderer(<SideNavigation {...props} />);
21
-
22
- return {
23
- props,
24
- root: wrapper.find(`.${CLASSNAME}`),
25
- wrapper,
26
- };
15
+ const setup = (props: Partial<SideNavigationProps> = {}) => {
16
+ render(<SideNavigation {...(props as any)} />);
17
+ const sideNavigation = getByClassName(document.body, CLASSNAME);
18
+ return { props, sideNavigation };
27
19
  };
28
20
 
29
21
  describe(`<${SideNavigation.displayName}>`, () => {
30
- // 1. Test render via snapshot (default states of component).
31
- describe('Snapshots and structure', () => {
32
- // Here is an example of a basic rendering check, with snapshot.
33
-
34
- it('should render correctly', () => {
35
- const { root, wrapper } = setup();
36
- expect(wrapper).toMatchSnapshot();
37
-
38
- expect(root).toExist();
39
- expect(root).toHaveClassName(CLASSNAME);
40
- });
22
+ it('should render default', () => {
23
+ const { sideNavigation } = setup();
24
+ expect(sideNavigation.className).toMatchInlineSnapshot(`"lumx-side-navigation"`);
41
25
  });
42
26
 
43
- // 2. Test defaultProps value and important props custom values. => no default props
44
-
45
- // 3. Test events. => no events
46
-
47
- // 4. Test conditions => no conditions
27
+ it('should render dark theme', () => {
28
+ const { sideNavigation } = setup({ theme: Theme.dark });
29
+ expect(sideNavigation.className).toMatchInlineSnapshot(`"lumx-color-font-light-N lumx-side-navigation"`);
30
+ });
48
31
 
49
- // 5. Test state => no state
50
32
  // Common tests suite.
51
- commonTestsSuite(setup, { className: 'root', prop: 'root' }, { className: CLASSNAME });
33
+ commonTestsSuiteRTL(setup, {
34
+ baseClassName: CLASSNAME,
35
+ forwardClassName: 'sideNavigation',
36
+ forwardAttributes: 'sideNavigation',
37
+ forwardRef: 'sideNavigation',
38
+ });
52
39
  });