@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,359 +1,190 @@
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 { Kind, Theme } from '@lumx/react/components';
3
+ import { Theme } from '@lumx/react/components';
7
4
  import { Chip } from '@lumx/react/components/chip/Chip';
8
- import { Icon } from '@lumx/react/components/icon/Icon';
9
-
10
- import { commonTestsSuite, Wrapper } from '@lumx/react/testing/utils';
11
- import { getBasicClass } from '@lumx/react/utils/className';
12
-
13
- import { mdiCloseCircle, mdiMenuDown } from '@lumx/icons';
14
5
  import { Dropdown } from '@lumx/react/components/dropdown/Dropdown';
6
+ import { getByClassName, queryAllByClassName, queryByClassName } from '@lumx/react/testing/utils/queries';
7
+ import { render, within } from '@testing-library/react';
8
+ import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
9
+ import userEvent from '@testing-library/user-event';
10
+
15
11
  import { Select, SelectProps, SelectVariant } from './Select';
16
- import { DEFAULT_PROPS } from './WithSelectContext';
17
12
 
18
13
  const CLASSNAME = Select.className as string;
19
14
 
20
15
  jest.mock('uid', () => ({ uid: () => 'uid' }));
21
16
 
22
- type SetupProps = Partial<SelectProps>;
23
-
24
17
  /**
25
18
  * Mounts the component and returns common DOM elements / data needed in multiple tests further down.
26
19
  */
27
- const setup = ({ ...propsOverride }: SetupProps = {}, shallowRendering = true) => {
20
+ const setup = (propsOverride: Partial<SelectProps> = {}) => {
28
21
  const props: SelectProps = {
29
22
  children: <span>Select Component</span>,
30
23
  value: '',
31
24
  ...propsOverride,
32
25
  };
33
-
34
- const renderer: (el: ReactElement) => Wrapper = shallowRendering ? shallow : mount;
35
- const wrapper: ShallowWrapper | ReactWrapper = renderer(<Select {...props} />);
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(SelectField)').first(),
49
- chip: wrapper.find('Chip'),
50
- inputWrapper: wrapper.find('.lumx-select__wrapper'),
51
- props,
52
- wrapper,
53
- };
26
+ render(<Select {...props} />);
27
+ const select = getByClassName(document.body, CLASSNAME);
28
+ const getDropdown = () => queryByClassName(document.body, Dropdown.className as string);
29
+ const helpers = queryAllByClassName(select, `${CLASSNAME}__helper`);
30
+ const inputWrapper = queryByClassName(select, `${CLASSNAME}__wrapper`);
31
+ const chip = queryByClassName(select, Chip.className as string);
32
+ return { props, select, getDropdown, helpers, inputWrapper, chip };
54
33
  };
55
34
 
56
35
  describe(`<${Select.displayName}>`, () => {
57
- // 1. Test render via snapshot (default states of component).
58
- describe('Snapshots and structure', () => {
59
- it('should render correctly', () => {
60
- const { container, wrapper } = setup();
61
- expect(wrapper).toMatchSnapshot();
62
-
63
- expect(container).toExist();
64
- expect(container).toHaveClassName(CLASSNAME);
65
- });
66
- });
67
-
68
- // 2. Test defaultProps value and important props custom values.
69
36
  describe('Props', () => {
70
37
  it('should have default classNames', () => {
71
- const { wrapper, container } = setup();
72
- wrapper.update();
73
-
74
- expect(container).toHaveClassName(CLASSNAME);
75
- expect(container).toHaveClassName(
76
- getBasicClass({ prefix: CLASSNAME, type: 'theme', value: DEFAULT_PROPS.theme }),
38
+ const { select, getDropdown } = setup();
39
+ expect(getDropdown()).not.toBeInTheDocument();
40
+ expect(select.className).toMatchInlineSnapshot(
41
+ `"lumx-select lumx-select--has-unique lumx-select lumx-select--is-empty lumx-select--theme-light"`,
77
42
  );
78
- expect(container).toHaveClassName(getBasicClass({ prefix: CLASSNAME, type: 'isEmpty', value: true }));
79
43
  });
80
44
 
81
45
  it('should use the given `theme`', () => {
82
- const testedProp = 'theme';
83
- const modifiedProps: SetupProps = {
84
- [testedProp]: Theme.dark,
85
- };
86
-
87
- const { container } = setup(modifiedProps);
88
-
89
- expect(container).toHaveClassName(
90
- getBasicClass({ prefix: CLASSNAME, type: testedProp, value: modifiedProps[testedProp] }),
91
- );
92
- expect(container).not.toHaveClassName(
93
- getBasicClass({ prefix: CLASSNAME, type: testedProp, value: DEFAULT_PROPS.theme }),
94
- );
46
+ const { select } = setup({ theme: Theme.dark });
47
+ expect(select).toHaveClass(`${CLASSNAME}--theme-dark`);
95
48
  });
96
49
 
97
50
  it('should use the given `isValid`', () => {
98
- const testedProp = 'isValid';
99
- const modifiedProps: SetupProps = {
100
- [testedProp]: true,
101
- };
102
-
103
- const { container } = setup(modifiedProps);
104
-
105
- expect(container).toHaveClassName(
106
- getBasicClass({ prefix: CLASSNAME, type: testedProp, value: modifiedProps[testedProp] }),
107
- );
51
+ const { select } = setup({ isValid: true });
52
+ expect(select).toHaveClass(`${CLASSNAME}--is-valid`);
108
53
  });
109
54
 
110
55
  it('should use the given `hasError`', () => {
111
- const testedProp = 'hasError';
112
- const modifiedProps: SetupProps = {
113
- [testedProp]: true,
114
- };
115
-
116
- const { container } = setup(modifiedProps);
117
-
118
- expect(container).toHaveClassName(
119
- getBasicClass({ prefix: CLASSNAME, type: testedProp, value: modifiedProps[testedProp] }),
120
- );
121
- });
122
-
123
- it('should display the given `error`', () => {
124
- const { container, error } = setup({
125
- error: 'You are not bold!',
126
- hasError: true,
127
- });
128
-
129
- expect(error).toExist();
130
- expect(container).toHaveClassName(getBasicClass({ prefix: CLASSNAME, type: 'hasError', value: true }));
131
- });
132
-
133
- it('should NOT display the given `error`', () => {
134
- const { container, error } = setup({
135
- error: 'You are not bold!',
136
- hasError: false,
137
- });
138
-
139
- expect(error).not.toExist();
140
- expect(container).not.toHaveClassName(getBasicClass({ prefix: CLASSNAME, type: 'hasError', value: true }));
56
+ const { select } = setup({ hasError: true });
57
+ expect(select).toHaveClass(`${CLASSNAME}--has-error`);
141
58
  });
142
59
 
143
60
  it('should use the given `value`', () => {
144
- const testedProp = 'value';
145
- const modifiedProps: SetupProps = {
146
- [testedProp]: 'val1',
147
- };
148
-
149
- const { container } = setup(modifiedProps);
150
-
151
- expect(container).toHaveClassName(getBasicClass({ prefix: CLASSNAME, type: 'hasUnique', value: true }));
152
- expect(container).toHaveClassName(getBasicClass({ prefix: CLASSNAME, type: 'hasValue', value: true }));
153
- expect(container).not.toHaveClassName(getBasicClass({ prefix: CLASSNAME, type: 'isEmpty', value: true }));
154
- });
155
-
156
- it('should trigger the given `onDropdownClose` on dropdown close', () => {
157
- const onDropdownClose = jest.fn();
158
- const { dropdown } = setup({ onDropdownClose });
159
-
160
- const props: any = dropdown.props();
161
-
162
- props.onClose();
163
-
164
- expect(onDropdownClose).toHaveBeenCalled();
61
+ const { select, inputWrapper, props } = setup({ value: 'val1' });
62
+ expect(select).toHaveClass(`${CLASSNAME}--has-unique`);
63
+ expect(select).toHaveClass(`${CLASSNAME}--has-value`);
64
+ expect(select).not.toHaveClass(`${CLASSNAME}--is-empty`);
65
+ expect(inputWrapper).toHaveTextContent(props.value);
165
66
  });
166
67
 
167
68
  it('should pass the given `isOpen` to the dropdown', () => {
168
- const isOpen = true;
169
- const { dropdown } = setup({ isOpen });
170
-
171
- expect(dropdown).toHaveProp('isOpen', isOpen);
172
- });
173
-
174
- it('should display the given `helper`', () => {
175
- const { helper } = setup({
176
- helper: 'Be bold',
69
+ const { getDropdown } = setup({ isOpen: true });
70
+ expect(getDropdown()).toBeInTheDocument();
71
+ });
72
+ describe('helpers', () => {
73
+ it('should display the given `error`', () => {
74
+ const { select, helpers, props } = setup({ hasError: true, error: 'You are not bold!' });
75
+ expect(select).toHaveClass(`${CLASSNAME}--has-error`);
76
+ expect(helpers.length).toBe(1);
77
+ expect(helpers[0]).toHaveTextContent(props.error as string);
177
78
  });
178
79
 
179
- expect(helper).toExist();
180
- });
80
+ it('should NOT display the given `error`', () => {
81
+ const { select, helpers } = setup({
82
+ error: 'You are not bold!',
83
+ hasError: false,
84
+ });
85
+ expect(select).not.toHaveClass(`${CLASSNAME}--has-error`);
86
+ expect(helpers.length).toBe(0);
87
+ });
181
88
 
182
- it('should display the given `helper` and `error`', () => {
183
- const { error, helper } = setup({
184
- error: 'You are not bold!',
185
- hasError: true,
186
- helper: 'Be bold',
89
+ it('should display the given `helper`', () => {
90
+ const { helpers, props } = setup({ helper: 'Be bold' });
91
+ expect(helpers.length).toBe(1);
92
+ expect(helpers[0]).toHaveTextContent(props.helper as string);
187
93
  });
188
94
 
189
- expect(error).toExist();
190
- expect(helper).toExist();
95
+ it('should display the given `helper` and `error`', () => {
96
+ const { select, helpers, props } = setup({
97
+ error: 'You are not bold!',
98
+ hasError: true,
99
+ helper: 'Be bold',
100
+ });
101
+ expect(select).toHaveClass(`${CLASSNAME}--has-error`);
102
+ expect(helpers.length).toBe(2);
103
+ expect(helpers[0]).toHaveTextContent(props.error as string);
104
+ expect(helpers[1]).toHaveTextContent(props.helper as string);
105
+ });
191
106
  });
192
107
 
193
108
  it('should have a data-id as prop', () => {
194
- const { inputWrapper } = setup(
195
- {
196
- 'data-id': 'select',
197
- },
198
- false,
199
- );
109
+ const { inputWrapper } = setup({ 'data-id': 'select' });
110
+ expect(inputWrapper).toHaveAttribute('data-id', 'select');
111
+ });
112
+
113
+ describe('chip variant', () => {
114
+ it('should render chip variant', () => {
115
+ const { select, inputWrapper, chip } = setup({ variant: SelectVariant.chip });
116
+ expect(inputWrapper).not.toBeInTheDocument();
117
+ expect(chip).toBeInTheDocument();
118
+ expect(select.className).toMatchInlineSnapshot(
119
+ `"lumx-select lumx-select--has-unique lumx-select lumx-select--is-empty lumx-select--theme-light"`,
120
+ );
121
+ });
200
122
 
201
- expect(inputWrapper.prop('data-id')).toEqual('select');
202
- });
123
+ it('should render chip variant with value', () => {
124
+ const { select, chip, props } = setup({ variant: SelectVariant.chip, value: 'val1' });
125
+ expect(chip).toHaveTextContent(props.value);
126
+ expect(select.className).toMatchInlineSnapshot(
127
+ `"lumx-select lumx-select--has-unique lumx-select lumx-select--has-value lumx-select--theme-light"`,
128
+ );
129
+ });
203
130
 
204
- it('should have a data-id as prop with Chip variant', () => {
205
- const { chip } = setup(
206
- {
131
+ it('should forward prop to chip variant', () => {
132
+ const { chip } = setup({
207
133
  'data-id': 'select',
208
134
  variant: SelectVariant.chip,
209
- },
210
- false,
211
- );
212
-
213
- expect(chip.prop('data-id')).toEqual('select');
135
+ });
136
+ expect(chip).toHaveAttribute('data-id', 'select');
137
+ });
214
138
  });
215
139
  });
216
140
 
217
- // 3. Test events.
218
141
  describe('Events', () => {
219
- const onClick: jest.Mock = jest.fn();
142
+ it('should trigger `onDropdownClose` on escape', async () => {
143
+ const onDropdownClose = jest.fn();
144
+ const { getDropdown } = setup({ isOpen: true, onDropdownClose });
220
145
 
221
- beforeEach(() => {
222
- onClick.mockClear();
146
+ const dropdown = getDropdown();
147
+ expect(dropdown).toBeInTheDocument();
148
+
149
+ await userEvent.keyboard('[Escape]');
150
+ expect(onDropdownClose).toHaveBeenCalled();
223
151
  });
224
152
 
225
153
  describe('should trigger `onInputClick` when the select button is clicked', () => {
226
- it('with input variant', () => {
227
- const { input } = setup({ onInputClick: onClick, variant: SelectVariant.input }, false);
228
-
229
- input.simulate('click');
154
+ it('with input variant', async () => {
155
+ const onClick = jest.fn();
156
+ const { inputWrapper } = setup({ onInputClick: onClick, variant: SelectVariant.input });
230
157
 
158
+ await userEvent.click(inputWrapper as any);
231
159
  expect(onClick).toHaveBeenCalled();
232
160
  });
233
161
 
234
- it('with chip variant', () => {
235
- const { input } = setup({ onInputClick: onClick, variant: SelectVariant.chip }, false);
236
-
237
- input.simulate('click');
162
+ it('with chip variant', async () => {
163
+ const onClick = jest.fn();
164
+ const { chip } = setup({ onInputClick: onClick, variant: SelectVariant.chip });
238
165
 
166
+ await userEvent.click(chip as any);
239
167
  expect(onClick).toHaveBeenCalled();
240
168
  });
241
169
  });
242
170
 
243
- it('should call onClear when clear icon is clicked in select input', () => {
171
+ it('should call onClear when clear icon is clicked in select input', async () => {
244
172
  const value = 'Value';
245
- const onClear: jest.Mock = jest.fn();
246
- const { input } = setup({ value, onClear, clearButtonProps: { label: 'Clear' } }, false);
247
-
248
- input.find('[aria-label="Clear"]').first().simulate('click');
249
- expect(onClear).toHaveBeenCalled();
250
- });
251
- });
252
-
253
- // 4. Test conditions (i.e. things that display or not in the UI based on props).
254
- describe('Conditions', () => {
255
- describe('Input variant', () => {
256
- it('should render a div as the select input', () => {
257
- const { dropdown, input } = setup({}, false);
258
- const dropdownRef: RefObject<HTMLDivElement> = dropdown.prop('anchorRef');
259
- expect(input.type()).toBe('div');
260
- // Impossible to get the ref passed to a div so we check if ref.current is matching the input html.
261
- expect(input.html()).toEqual(dropdownRef && dropdownRef.current && dropdownRef.current.outerHTML);
262
- });
173
+ const onClear = jest.fn();
174
+ const { select, props } = setup({ value, onClear, clearButtonProps: { label: 'Clear' } });
263
175
 
264
- describe('Has value', () => {
265
- const value = 'Value';
266
- const hasValueProps: Partial<SetupProps> = {
267
- value,
268
- variant: SelectVariant.input,
269
- };
176
+ const clearButton = within(select).getByRole('button', { name: props.clearButtonProps?.label });
270
177
 
271
- it('should render the value selected if not multiple ', () => {
272
- const { input } = setup({ ...hasValueProps }, false);
273
-
274
- expect(input.find(`.${CLASSNAME}__input-native span`).first().text()).toBe(value);
275
- });
276
- });
277
-
278
- describe('No value', () => {
279
- const placeholder = 'My placeholder';
280
- const hasNoValueProps: Partial<SetupProps> = {
281
- placeholder,
282
- value: '',
283
- variant: SelectVariant.input,
284
- };
285
-
286
- it('should render the placeholder when empty', () => {
287
- const { input } = setup({ ...hasNoValueProps }, false);
288
-
289
- expect(input.find(`.${CLASSNAME}__input-native--placeholder span`).first().text()).toBe(
290
- placeholder,
291
- );
292
- });
293
- });
294
- });
295
-
296
- describe('Chip variant', () => {
297
- it('should render a Chip as the select input', () => {
298
- const { input } = setup({ variant: SelectVariant.chip }, false);
299
-
300
- expect(input.type()).toEqual(Chip);
301
- });
302
-
303
- describe('Has value', () => {
304
- const value = 'Value';
305
- const hasValueProps: Partial<SetupProps> = {
306
- value,
307
- variant: SelectVariant.chip,
308
- };
309
-
310
- it('should render the value', () => {
311
- const label = 'The label';
312
- const { input } = setup(
313
- {
314
- ...hasValueProps,
315
- label,
316
- },
317
- false,
318
- );
319
-
320
- expect(input.find('.lumx-chip__label').text()).toEqual(value);
321
- });
322
-
323
- it('should render a close icon if there is a value', () => {
324
- const { input } = setup({ ...hasValueProps }, false);
325
-
326
- expect(input.find(Icon).first().prop('icon')).toBe(mdiCloseCircle);
327
- });
328
- });
329
-
330
- describe('No value', () => {
331
- const hasNoValue: Partial<SetupProps> = {
332
- value: '',
333
- variant: SelectVariant.chip,
334
- };
335
-
336
- it('should render the label', () => {
337
- const label = 'The label';
338
- const { input } = setup({ ...hasNoValue, label }, false);
339
-
340
- expect(input.find('.lumx-chip__label').text()).toEqual(label);
341
- });
342
-
343
- it('should render a MenuDown icon', () => {
344
- const { input } = setup({ ...hasNoValue }, false);
345
-
346
- expect(input.find(Icon).first().prop('icon')).toBe(mdiMenuDown);
347
- });
348
- });
178
+ await userEvent.click(clearButton);
179
+ expect(onClear).toHaveBeenCalled();
349
180
  });
350
181
  });
351
182
 
352
- // 5. Test state.
353
- describe('State', () => {
354
- // Nothing to do here.
355
- });
356
-
357
183
  // Common tests suite.
358
- commonTestsSuite(setup, { className: 'wrapper' }, { className: CLASSNAME });
184
+ commonTestsSuiteRTL(setup, {
185
+ baseClassName: CLASSNAME,
186
+ forwardClassName: 'select',
187
+ forwardAttributes: 'inputWrapper',
188
+ forwardRef: 'select',
189
+ });
359
190
  });
@@ -1,9 +1,20 @@
1
1
  /* istanbul ignore file */
2
2
  import { mdiTram } from '@lumx/icons/';
3
- import { Chip, List, ListItem, SelectMultiple, Size } from '@lumx/react';
3
+ import {
4
+ Chip,
5
+ Dialog,
6
+ List,
7
+ ListDivider,
8
+ ListItem,
9
+ ListSubheader,
10
+ SelectMultiple,
11
+ Size,
12
+ TextField,
13
+ Toolbar,
14
+ } from '@lumx/react';
4
15
  import { useBooleanState } from '@lumx/react/hooks/useBooleanState';
5
16
  import noop from 'lodash/noop';
6
- import React, { MouseEventHandler, SyntheticEvent, useState } from 'react';
17
+ import React, { MouseEventHandler, SyntheticEvent, useRef, useState } from 'react';
7
18
  import { SelectVariant } from './constants';
8
19
 
9
20
  export default { title: 'LumX components/select/Select Multiple' };
@@ -225,3 +236,95 @@ export const ChipsCustomSelectMultiple = ({ theme }: any) => {
225
236
  </SelectMultiple>
226
237
  );
227
238
  };
239
+
240
+ /**
241
+ * Test select focus trap (focus is contained inside the dialog then inside the select dropdown)
242
+ */
243
+ export const SelectWithinADialog = ({ theme }: any) => {
244
+ const searchFieldRef = useRef(null);
245
+
246
+ const [searchText, setSearchText] = useState<string>();
247
+ const [values, setValues] = useState<string[]>([]);
248
+ const [isOpen, closeSelect, , toggleSelect] = useBooleanState(false);
249
+
250
+ const clearSelected = (event: SyntheticEvent, value: string) => {
251
+ event.stopPropagation();
252
+ setValues(value ? values.filter((val) => val !== value) : []);
253
+ };
254
+
255
+ const selectItem = (item: string) => () => {
256
+ if (values.includes(item)) {
257
+ return;
258
+ }
259
+
260
+ closeSelect();
261
+ setValues([...values, item]);
262
+ };
263
+
264
+ const filteredChoices =
265
+ searchText && searchText.length > 0 ? CHOICES.filter((choice) => choice.includes(searchText)) : CHOICES;
266
+
267
+ return (
268
+ <>
269
+ <Dialog isOpen>
270
+ <header>
271
+ <Toolbar label={<span className="lumx-typography-title">Dialog header</span>} />
272
+ </header>
273
+ <div className="lumx-spacing-padding-horizontal-huge lumx-spacing-padding-bottom-huge">
274
+ {/* Testing hidden input do not count in th focus trap*/}
275
+ <input hidden type="file" />
276
+ <input type="hidden" />
277
+
278
+ <div className="lumx-spacing-margin-bottom-huge">The select should capture the focus on open.</div>
279
+
280
+ <SelectMultiple
281
+ isOpen={isOpen}
282
+ value={values}
283
+ onClear={clearSelected}
284
+ clearButtonProps={{ label: 'Clear' }}
285
+ label={LABEL}
286
+ placeholder={PLACEHOLDER}
287
+ theme={theme}
288
+ onInputClick={toggleSelect}
289
+ onDropdownClose={closeSelect}
290
+ icon={mdiTram}
291
+ focusElement={searchFieldRef}
292
+ >
293
+ <List isClickable>
294
+ <>
295
+ <ListSubheader>
296
+ <TextField
297
+ clearButtonProps={{ label: 'Clear' }}
298
+ placeholder="Search"
299
+ role="searchbox"
300
+ inputRef={searchFieldRef}
301
+ onChange={setSearchText}
302
+ value={searchText}
303
+ />
304
+ </ListSubheader>
305
+ <ListDivider role="presentation" />
306
+ </>
307
+
308
+ {filteredChoices.length > 0
309
+ ? filteredChoices.map((choice) => (
310
+ <ListItem
311
+ isSelected={values.includes(choice)}
312
+ key={choice}
313
+ onItemSelected={selectItem(choice)}
314
+ size={Size.tiny}
315
+ >
316
+ {choice}
317
+ </ListItem>
318
+ ))
319
+ : [
320
+ <ListItem key={0} size={Size.tiny}>
321
+ No data
322
+ </ListItem>,
323
+ ]}
324
+ </List>
325
+ </SelectMultiple>
326
+ </div>
327
+ </Dialog>
328
+ </>
329
+ );
330
+ };