@scality/core-ui 0.135.0 → 0.137.0
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.
- package/dist/components/searchinput/SearchInput.component.d.ts +0 -1
- package/dist/components/searchinput/SearchInput.component.d.ts.map +1 -1
- package/dist/components/searchinput/SearchInput.component.js +1 -1
- package/dist/components/selectv2/Selectv2.component.d.ts +1 -1
- package/dist/components/selectv2/Selectv2.component.d.ts.map +1 -1
- package/dist/components/selectv2/Selectv2.component.js +5 -9
- package/dist/components/tablev2/Search.d.ts +1 -1
- package/dist/components/tablev2/Search.d.ts.map +1 -1
- package/dist/components/tablev2/Search.js +1 -1
- package/dist/components/tablev2/TableCommon.d.ts +1 -1
- package/dist/components/tablev2/TableCommon.js +3 -3
- package/dist/components/tablev2/Tablestyle.d.ts +1 -1
- package/dist/components/tablev2/Tablestyle.d.ts.map +1 -1
- package/dist/components/tablev2/Tablev2.component.d.ts +5 -1
- package/dist/components/tablev2/Tablev2.component.d.ts.map +1 -1
- package/dist/components/tablev2/Tablev2.component.js +6 -0
- package/dist/components/tablev2/useSyncedScroll.d.ts +2 -1
- package/dist/components/tablev2/useSyncedScroll.d.ts.map +1 -1
- package/dist/components/tablev2/useSyncedScroll.js +17 -19
- package/dist/components/tabsv2/StyledTabs.d.ts +1 -1
- package/dist/components/tabsv2/StyledTabs.d.ts.map +1 -1
- package/dist/components/tabsv2/Tabsv2.component.js +5 -1
- package/dist/components/toggle/Toggle.component.d.ts +1 -1
- package/dist/components/toggle/Toggle.component.d.ts.map +1 -1
- package/dist/components/toggle/Toggle.component.js +8 -11
- package/dist/organisms/attachments/AttachmentConfirmationModal.d.ts +4 -3
- package/dist/organisms/attachments/AttachmentConfirmationModal.d.ts.map +1 -1
- package/dist/organisms/attachments/AttachmentConfirmationModal.js +1 -0
- package/dist/organisms/attachments/AttachmentTable.d.ts +10 -10
- package/dist/organisms/attachments/AttachmentTable.d.ts.map +1 -1
- package/dist/organisms/attachments/AttachmentTable.js +2 -2
- package/dist/organisms/attachments/AttachmentTypes.d.ts +4 -3
- package/dist/organisms/attachments/AttachmentTypes.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/lib/components/searchinput/SearchInput.component.tsx +0 -2
- package/src/lib/components/searchinput/SearchInput.test.tsx +88 -0
- package/src/lib/components/selectv2/Selectv2.component.tsx +7 -11
- package/src/lib/components/selectv2/selectv2.test.tsx +190 -200
- package/src/lib/components/tablev2/Search.tsx +1 -2
- package/src/lib/components/tablev2/TableCommon.tsx +5 -5
- package/src/lib/components/tablev2/Tablestyle.tsx +1 -1
- package/src/lib/components/tablev2/Tablev2.component.tsx +14 -0
- package/src/lib/components/tablev2/useSyncedScroll.ts +22 -24
- package/src/lib/components/tabsv2/StyledTabs.ts +1 -1
- package/src/lib/components/tabsv2/Tabsv2.component.tsx +1 -1
- package/src/lib/components/toggle/Toggle.component.tsx +9 -12
- package/src/lib/components/toggle/Toggle.test.tsx +56 -0
- package/src/lib/organisms/attachments/AttachmentConfirmationModal.tsx +11 -5
- package/src/lib/organisms/attachments/AttachmentTable.tsx +53 -31
- package/src/lib/organisms/attachments/AttachmentTypes.ts +10 -3
- package/stories/SearchInput/searchinput.guideline.mdx +20 -0
- package/stories/{searchinput.stories.tsx → SearchInput/searchinput.stories.tsx} +13 -20
- package/stories/Select/selectv2.stories.tsx +23 -5
- package/stories/Toggle/toggle.guideline.mdx +20 -0
- package/stories/{toggle.stories.tsx → Toggle/toggle.stories.tsx} +17 -10
|
@@ -10,7 +10,6 @@ export type Props = {
|
|
|
10
10
|
value: string;
|
|
11
11
|
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
12
12
|
onReset?: () => void;
|
|
13
|
-
disableToggle: boolean;
|
|
14
13
|
disabled?: boolean;
|
|
15
14
|
id?: string;
|
|
16
15
|
size?: InputSize;
|
|
@@ -57,7 +56,6 @@ const ClearButton = styled.div`
|
|
|
57
56
|
const SearchInput = forwardRef(
|
|
58
57
|
(
|
|
59
58
|
{
|
|
60
|
-
disableToggle,
|
|
61
59
|
placeholder,
|
|
62
60
|
value,
|
|
63
61
|
onChange,
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import { SearchInput, Props } from './SearchInput.component';
|
|
4
|
+
import { QueryClient, QueryClientProvider } from 'react-query';
|
|
5
|
+
import userEvent from '@testing-library/user-event';
|
|
6
|
+
|
|
7
|
+
const queryClient = new QueryClient();
|
|
8
|
+
|
|
9
|
+
const SearchInputRender = (props: Props) => {
|
|
10
|
+
return (
|
|
11
|
+
<QueryClientProvider client={queryClient}>
|
|
12
|
+
<SearchInput {...props} />
|
|
13
|
+
</QueryClientProvider>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe('SearchInput', () => {
|
|
18
|
+
const selectors = {
|
|
19
|
+
searchInput: () => screen.getByRole('searchbox'),
|
|
20
|
+
clearButton: () => screen.queryByRole('button'),
|
|
21
|
+
};
|
|
22
|
+
it('should render the SearchInput component', () => {
|
|
23
|
+
render(<SearchInputRender value="" onChange={() => {}} />);
|
|
24
|
+
|
|
25
|
+
const searchInput = selectors.searchInput();
|
|
26
|
+
expect(searchInput).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should render the SearchInput component with placeholder', () => {
|
|
30
|
+
render(
|
|
31
|
+
<SearchInputRender value="" onChange={() => {}} placeholder="Search" />,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const searchInput = screen.queryByPlaceholderText('Example: Search');
|
|
35
|
+
expect(searchInput).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should render the SearchInput component with disabled prop', () => {
|
|
39
|
+
render(<SearchInputRender value="" onChange={() => {}} disabled />);
|
|
40
|
+
|
|
41
|
+
const searchInput = selectors.searchInput();
|
|
42
|
+
expect(searchInput).toBeInTheDocument();
|
|
43
|
+
expect(searchInput).toBeDisabled();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should change value instantly but call the onChange function with a 300ms delay after the end of typing', async () => {
|
|
47
|
+
const onChange = jest.fn();
|
|
48
|
+
render(<SearchInputRender value="" onChange={onChange} />);
|
|
49
|
+
const searchInput = selectors.searchInput();
|
|
50
|
+
userEvent.type(searchInput, 'test');
|
|
51
|
+
expect(searchInput).toHaveValue('test');
|
|
52
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
53
|
+
await waitFor(
|
|
54
|
+
() => {
|
|
55
|
+
expect(onChange).toHaveBeenCalled();
|
|
56
|
+
},
|
|
57
|
+
{ timeout: 350 },
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should have a clear button when the input is not empty', () => {
|
|
62
|
+
render(<SearchInputRender value="" onChange={() => {}} />);
|
|
63
|
+
|
|
64
|
+
// clear button should not be rendered as value is empty
|
|
65
|
+
let clearButton = selectors.clearButton();
|
|
66
|
+
expect(clearButton).not.toBeInTheDocument();
|
|
67
|
+
|
|
68
|
+
const searchInput = selectors.searchInput();
|
|
69
|
+
userEvent.type(searchInput, 'test');
|
|
70
|
+
|
|
71
|
+
// clear button should now be rendered
|
|
72
|
+
clearButton = selectors.clearButton();
|
|
73
|
+
expect(clearButton).toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should call the onReset function when the clear button is clicked and clear the input value', async () => {
|
|
77
|
+
const onReset = jest.fn();
|
|
78
|
+
render(
|
|
79
|
+
<SearchInputRender value="test" onChange={() => {}} onReset={onReset} />,
|
|
80
|
+
);
|
|
81
|
+
const searchInput = selectors.searchInput();
|
|
82
|
+
const clearButton = selectors.clearButton();
|
|
83
|
+
expect(clearButton).toBeInTheDocument();
|
|
84
|
+
clearButton && userEvent.click(clearButton);
|
|
85
|
+
expect(onReset).toHaveBeenCalled();
|
|
86
|
+
expect(searchInput).toHaveValue('');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -135,7 +135,11 @@ const InternalOption = (width, isDefaultVariant) => (props) => {
|
|
|
135
135
|
? 'sc-highlighted-matching-text'
|
|
136
136
|
: '';
|
|
137
137
|
return (
|
|
138
|
-
<span
|
|
138
|
+
<span
|
|
139
|
+
role={highlightStyle ? 'mark' : undefined}
|
|
140
|
+
key={i}
|
|
141
|
+
className={highlightStyle}
|
|
142
|
+
>
|
|
139
143
|
{part}
|
|
140
144
|
</span>
|
|
141
145
|
);
|
|
@@ -238,7 +242,6 @@ const MenuList = (props) => {
|
|
|
238
242
|
selectedIndex * optionHeight - (ITEMS_PER_SCROLL_WINDOW - 1) * optionHeight;
|
|
239
243
|
useEffect(() => {
|
|
240
244
|
if (listRef && listRef.current) {
|
|
241
|
-
// @ts-ignore
|
|
242
245
|
listRef.current.scrollTo(
|
|
243
246
|
getScrollOffset(
|
|
244
247
|
listRef.current,
|
|
@@ -282,6 +285,7 @@ const ValueContainer = ({ children, ...props }) => {
|
|
|
282
285
|
const icon = selectedOption ? selectedOption.icon : null;
|
|
283
286
|
const ariaProps = {
|
|
284
287
|
innerProps: {
|
|
288
|
+
disabled: true,
|
|
285
289
|
role: props.selectProps.isSearchable ? 'combobox' : 'listbox',
|
|
286
290
|
'aria-expanded': props.selectProps.menuIsOpen,
|
|
287
291
|
'aria-autocomplete': 'list',
|
|
@@ -302,7 +306,6 @@ export type SelectProps = {
|
|
|
302
306
|
placeholder?: string;
|
|
303
307
|
disabled?: boolean;
|
|
304
308
|
children?: React.ReactNode;
|
|
305
|
-
defaultValue?: string;
|
|
306
309
|
value?: string;
|
|
307
310
|
onFocus?: (event: FocusEvent) => void;
|
|
308
311
|
onBlur?: (event: FocusEvent) => void;
|
|
@@ -310,6 +313,7 @@ export type SelectProps = {
|
|
|
310
313
|
variant?: 'default' | 'rounded';
|
|
311
314
|
size?: '1' | '2/3' | '1/2' | '1/3';
|
|
312
315
|
className?: string;
|
|
316
|
+
/** use menuPositon='fixed' inside modal to avoid display issue */
|
|
313
317
|
menuPosition?: 'fixed' | 'absolute';
|
|
314
318
|
};
|
|
315
319
|
type SelectOptionProps = {
|
|
@@ -357,7 +361,6 @@ function SelectWithOptionContext(props: SelectProps) {
|
|
|
357
361
|
function SelectBox({
|
|
358
362
|
placeholder = 'Select...',
|
|
359
363
|
disabled = false,
|
|
360
|
-
defaultValue,
|
|
361
364
|
value,
|
|
362
365
|
onChange,
|
|
363
366
|
variant = 'default',
|
|
@@ -366,11 +369,6 @@ function SelectBox({
|
|
|
366
369
|
id,
|
|
367
370
|
...rest
|
|
368
371
|
}: SelectProps) {
|
|
369
|
-
if (defaultValue && value) {
|
|
370
|
-
console.error(
|
|
371
|
-
'The `defaultValue` will be overridden by the `value` if they are set at the same time.',
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
372
|
const [keyboardFocusEnabled, setKeyboardFocusEnabled] = useState(false);
|
|
375
373
|
const [searchSelection, setSearchSelection] = useState('');
|
|
376
374
|
const [searchValue, setSearchValue] = useState('');
|
|
@@ -414,7 +412,6 @@ function SelectBox({
|
|
|
414
412
|
// Force to reset the value
|
|
415
413
|
useEffect(() => {
|
|
416
414
|
if (
|
|
417
|
-
!defaultValue &&
|
|
418
415
|
!isEmptyStringInOptions &&
|
|
419
416
|
value === '' &&
|
|
420
417
|
selectRef.current &&
|
|
@@ -436,7 +433,6 @@ function SelectBox({
|
|
|
436
433
|
value={
|
|
437
434
|
searchSelection || options.find((opt) => opt.value === value)
|
|
438
435
|
}
|
|
439
|
-
defaultValue={defaultValue}
|
|
440
436
|
inputValue={options.length > NOPT_SEARCH ? searchValue : undefined}
|
|
441
437
|
selectedOption={options.find((opt) => opt.value === value)}
|
|
442
438
|
keyboardFocusEnabled={keyboardFocusEnabled}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
screen,
|
|
3
|
-
render as testingRender,
|
|
4
|
-
within,
|
|
5
|
-
} from '@testing-library/react';
|
|
1
|
+
import { screen, render as testingRender } from '@testing-library/react';
|
|
6
2
|
import userEvent from '@testing-library/user-event';
|
|
7
3
|
import React, { useState } from 'react';
|
|
8
4
|
import { QueryClient, QueryClientProvider } from 'react-query';
|
|
9
|
-
import { debug } from 'jest-preview';
|
|
10
|
-
import { Icon } from '../icon/Icon.component';
|
|
11
5
|
import { Option, Select } from '../selectv2/Selectv2.component';
|
|
12
6
|
|
|
13
7
|
const render = (args) => {
|
|
@@ -27,26 +21,24 @@ const generateOptionsData = (n: number) =>
|
|
|
27
21
|
|
|
28
22
|
const generateOptions = (n: number) => {
|
|
29
23
|
return generateOptionsData(n).map((o, i) => (
|
|
30
|
-
<Option key={i} value={o.value}
|
|
24
|
+
<Option key={i} {...o} value={o.value}>
|
|
31
25
|
{o.label}
|
|
32
26
|
</Option>
|
|
33
27
|
));
|
|
34
28
|
};
|
|
29
|
+
const optionsWithScrollSearchBar = generateOptions(10); // more than 8 options should display searchbar + scrollbar
|
|
30
|
+
|
|
31
|
+
const simpleOptions = generateOptions(4); // less than 5 options should not displays any scroll/search bar
|
|
35
32
|
|
|
36
33
|
const SelectWrapper = (props) => {
|
|
37
|
-
const [value, setValue] = useState(null);
|
|
34
|
+
const [value, setValue] = useState<string | null>(null);
|
|
38
35
|
return (
|
|
39
36
|
<Select value={value} onChange={(value) => setValue(value)} {...props}>
|
|
40
|
-
{props.children}
|
|
37
|
+
{props.children || simpleOptions}
|
|
41
38
|
</Select>
|
|
42
39
|
);
|
|
43
40
|
};
|
|
44
41
|
|
|
45
|
-
const variants = ['default', 'rounded'];
|
|
46
|
-
const optionsWithScrollSearchBar = generateOptions(10); // more than 8 options should display searchbar + scrollbar
|
|
47
|
-
|
|
48
|
-
const simpleOptions = generateOptions(4); // less than 5 options should not displays any scroll/search bar
|
|
49
|
-
|
|
50
42
|
const SelectReset = (props) => {
|
|
51
43
|
const [value, setValue] = useState('default');
|
|
52
44
|
|
|
@@ -64,220 +56,218 @@ const SelectReset = (props) => {
|
|
|
64
56
|
};
|
|
65
57
|
|
|
66
58
|
describe('SelectV2', () => {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const toggleSelect = (container) => {
|
|
80
|
-
userEvent.click(container.querySelector('.sc-select__control'));
|
|
59
|
+
const selectors = {
|
|
60
|
+
option: (name: string | RegExp) => screen.getByRole('option', { name }),
|
|
61
|
+
options: () => screen.queryAllByRole('option'),
|
|
62
|
+
select: (withSearch?: boolean, name?: string) => {
|
|
63
|
+
if (withSearch) {
|
|
64
|
+
return screen.getByRole('combobox', { name });
|
|
65
|
+
}
|
|
66
|
+
return screen.getByRole('listbox', { name });
|
|
67
|
+
},
|
|
68
|
+
input: () => screen.getByRole('textbox'),
|
|
69
|
+
noOptions: () => screen.getByText(/No options/i),
|
|
70
|
+
highlightedText: () => screen.getByRole('mark'),
|
|
81
71
|
};
|
|
82
72
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const { container } = render(
|
|
87
|
-
<SelectWrapper variant={variant}>{simpleOptions}</SelectWrapper>,
|
|
88
|
-
);
|
|
89
|
-
// should open on click
|
|
90
|
-
toBeClose(container);
|
|
91
|
-
toggleSelect(container); // open
|
|
92
|
-
|
|
93
|
-
toBeOpenWith(container, simpleOptions.length);
|
|
94
|
-
toggleSelect(container); // close
|
|
95
|
-
|
|
96
|
-
userEvent.tab(); // remove focus
|
|
73
|
+
it('should throw error if <Option/> is outside <Select/>', () => {
|
|
74
|
+
expect(() => render(<Option value="Option 1" />)).toThrowError();
|
|
75
|
+
});
|
|
97
76
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
77
|
+
it('should open/close on click', () => {
|
|
78
|
+
render(<SelectWrapper />);
|
|
79
|
+
const select = selectors.select();
|
|
80
|
+
expect(select).toBeInTheDocument();
|
|
81
|
+
let options = selectors.options();
|
|
82
|
+
expect(options).toHaveLength(0);
|
|
83
|
+
|
|
84
|
+
// should open on click
|
|
85
|
+
userEvent.click(select);
|
|
86
|
+
simpleOptions.forEach((opt) => {
|
|
87
|
+
const option = selectors.option(opt.props.label);
|
|
88
|
+
expect(option).toBeInTheDocument();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
userEvent.click(select);
|
|
92
|
+
options = selectors.options();
|
|
93
|
+
expect(options).toHaveLength(0);
|
|
94
|
+
});
|
|
103
95
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
it('should open/close with keyboard', () => {
|
|
97
|
+
render(<SelectWrapper />);
|
|
98
|
+
const select = selectors.select();
|
|
99
|
+
expect(select).toBeInTheDocument();
|
|
100
|
+
const options = selectors.options();
|
|
101
|
+
expect(options).toHaveLength(0);
|
|
102
|
+
|
|
103
|
+
// should open on Enter
|
|
104
|
+
userEvent.tab();
|
|
105
|
+
userEvent.keyboard('{Enter}');
|
|
106
|
+
simpleOptions.forEach((opt) => {
|
|
107
|
+
const option = selectors.option(opt.props.label);
|
|
108
|
+
expect(option).toBeInTheDocument();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// should close on Enter
|
|
112
|
+
userEvent.keyboard('{Enter}');
|
|
113
|
+
expect(options).toHaveLength(0);
|
|
114
|
+
|
|
115
|
+
// should open on ArrowDown
|
|
116
|
+
userEvent.tab();
|
|
117
|
+
userEvent.keyboard('{ArrowDown}');
|
|
118
|
+
simpleOptions.forEach((opt) => {
|
|
119
|
+
const option = selectors.option(opt.props.label);
|
|
120
|
+
expect(option).toBeInTheDocument();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
107
123
|
|
|
108
|
-
|
|
109
|
-
userEvent.tab(); // remove focus
|
|
110
|
-
});
|
|
111
|
-
},
|
|
112
|
-
);
|
|
113
|
-
test.each(variants)('should display custom placeholder', (variant) => {
|
|
124
|
+
it('should display custom placeholder', () => {
|
|
114
125
|
const placeholder = 'My placeholder...';
|
|
115
|
-
|
|
116
|
-
|
|
126
|
+
render(<SelectWrapper placeholder={placeholder} />);
|
|
127
|
+
expect(screen.getByText(placeholder)).toBeInTheDocument();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should be disabled', () => {
|
|
131
|
+
render(
|
|
132
|
+
<SelectWrapper value="1" disabled={true}>
|
|
117
133
|
{simpleOptions}
|
|
118
134
|
</SelectWrapper>,
|
|
119
135
|
);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
).
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
},
|
|
130
|
-
];
|
|
131
|
-
const { container } = render(
|
|
132
|
-
<Select variant={variant} defaultValue={options[0]} options={options} />,
|
|
133
|
-
);
|
|
134
|
-
expect(container.querySelector('.sc-select__placeholder')).toBeNull();
|
|
135
|
-
expect(
|
|
136
|
-
container.querySelector('.sc-select__single-value'),
|
|
137
|
-
).toHaveTextContent('Label 0');
|
|
136
|
+
userEvent.tab();
|
|
137
|
+
const select = selectors.select();
|
|
138
|
+
expect(select).not.toHaveFocus();
|
|
139
|
+
// use input instead of select because select will still trigger the open/close action
|
|
140
|
+
// despite select container not being clickable and input being disabled
|
|
141
|
+
const input = selectors.input();
|
|
142
|
+
userEvent.click(input);
|
|
143
|
+
const options = selectors.options();
|
|
144
|
+
expect(options).toHaveLength(0);
|
|
138
145
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
146
|
+
|
|
147
|
+
it('should display no option', () => {
|
|
148
|
+
render(
|
|
149
|
+
<SelectWrapper>
|
|
150
|
+
<></>
|
|
151
|
+
</SelectWrapper>,
|
|
144
152
|
);
|
|
145
|
-
|
|
153
|
+
const select = selectors.select();
|
|
154
|
+
userEvent.click(select);
|
|
155
|
+
const noOptions = selectors.noOptions();
|
|
156
|
+
expect(noOptions).toBeInTheDocument();
|
|
146
157
|
});
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
|
|
159
|
+
it('should filter and highlight on search', () => {
|
|
160
|
+
render(<SelectWrapper>{optionsWithScrollSearchBar} </SelectWrapper>);
|
|
161
|
+
const select = selectors.select(true);
|
|
162
|
+
userEvent.click(select);
|
|
163
|
+
const input = selectors.input();
|
|
164
|
+
|
|
165
|
+
userEvent.type(input, '2');
|
|
166
|
+
const options = selectors.options();
|
|
167
|
+
expect(options).toHaveLength(1);
|
|
168
|
+
const searchedText = selectors.highlightedText();
|
|
169
|
+
expect(searchedText).toHaveTextContent('2');
|
|
153
170
|
});
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
test.each(variants)('should display option', (variant) => {
|
|
166
|
-
const { container, getByTestId } = render(
|
|
167
|
-
<Select variant={variant}>
|
|
168
|
-
<Option data-testid="disabledOption" value="0" disabled>
|
|
169
|
-
Label 1
|
|
170
|
-
</Option>
|
|
171
|
-
<Option
|
|
172
|
-
data-testid="option2"
|
|
173
|
-
value="1"
|
|
174
|
-
icon={<Icon name="Deletion-marker" />}
|
|
175
|
-
>
|
|
176
|
-
Label 2
|
|
177
|
-
</Option>
|
|
178
|
-
</Select>,
|
|
179
|
-
);
|
|
180
|
-
toggleSelect(container);
|
|
181
|
-
expect(getByTestId('disabledOption')).toHaveAttribute(
|
|
182
|
-
'aria-disabled',
|
|
183
|
-
'true',
|
|
184
|
-
);
|
|
185
|
-
const icon = getByTestId('option2').querySelector('i');
|
|
186
|
-
expect(icon).not.toBeNull();
|
|
187
|
-
expect(icon).toHaveAttribute('aria-label', 'Deletion-marker ');
|
|
171
|
+
|
|
172
|
+
it('should unfocus the search input when the select is closed', () => {
|
|
173
|
+
render(<SelectWrapper>{optionsWithScrollSearchBar} </SelectWrapper>);
|
|
174
|
+
const select = selectors.select(true);
|
|
175
|
+
userEvent.click(select);
|
|
176
|
+
let input = selectors.input();
|
|
177
|
+
expect(input).toHaveFocus();
|
|
178
|
+
const option = selectors.option(/Item 1/);
|
|
179
|
+
userEvent.click(option);
|
|
180
|
+
input = selectors.input();
|
|
181
|
+
expect(input).not.toHaveFocus();
|
|
188
182
|
});
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
(
|
|
192
|
-
|
|
193
|
-
jest.spyOn(console, 'error');
|
|
194
|
-
console.error.mockImplementation(() => {});
|
|
195
|
-
expect(() => render(<Option />)).toThrowError();
|
|
196
|
-
console.error.mockRestore(); // restore console.error
|
|
197
|
-
},
|
|
198
|
-
);
|
|
199
|
-
test.each(variants)('should highlight text on search', (variant) => {
|
|
200
|
-
const { container, getByTestId } = render(
|
|
201
|
-
<SelectWrapper variant={variant}>
|
|
202
|
-
{optionsWithScrollSearchBar}
|
|
203
|
-
</SelectWrapper>,
|
|
183
|
+
|
|
184
|
+
it('should be possible to use searchbar when option is selected', () => {
|
|
185
|
+
render(
|
|
186
|
+
<SelectWrapper value="1">{optionsWithScrollSearchBar}</SelectWrapper>,
|
|
204
187
|
);
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
188
|
+
expect(screen.getByText(/Item 1/)).toBeVisible();
|
|
189
|
+
const select = selectors.select(true);
|
|
190
|
+
userEvent.click(select);
|
|
191
|
+
const input = selectors.input();
|
|
192
|
+
userEvent.type(input, '2');
|
|
193
|
+
expect(screen.queryByText(/Item 1/)).not.toBeInTheDocument();
|
|
194
|
+
const options = selectors.options();
|
|
195
|
+
expect(options).toHaveLength(1);
|
|
212
196
|
});
|
|
213
|
-
test.each(variants)('should select/unselect option', (variant) => {
|
|
214
|
-
const { container, getByTestId } = render(
|
|
215
|
-
<SelectWrapper variant={variant}>{simpleOptions}</SelectWrapper>,
|
|
216
|
-
);
|
|
217
|
-
// select option
|
|
218
|
-
toggleSelect(container); // open select
|
|
219
197
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
toggleSelect(container); // reopen select
|
|
198
|
+
it('should select/unselect option with keyboard', () => {
|
|
199
|
+
render(<SelectWrapper />);
|
|
200
|
+
const select = selectors.select();
|
|
201
|
+
userEvent.tab();
|
|
202
|
+
userEvent.keyboard('{ArrowDown}');
|
|
226
203
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
);
|
|
230
|
-
// should be focused on the selected option
|
|
231
|
-
expect(getByTestId('option0')).toHaveAttribute('aria-selected', 'true');
|
|
232
|
-
userEvent.click(getByTestId('option1')); // click on another option
|
|
204
|
+
// should select first option
|
|
205
|
+
userEvent.keyboard('{Enter}');
|
|
206
|
+
expect(select).toHaveTextContent('Item 0');
|
|
233
207
|
|
|
234
|
-
|
|
208
|
+
// should select second option
|
|
209
|
+
userEvent.tab();
|
|
210
|
+
userEvent.keyboard('{ArrowDown}');
|
|
211
|
+
userEvent.keyboard('{ArrowDown}');
|
|
235
212
|
|
|
236
|
-
|
|
213
|
+
userEvent.keyboard('{Enter}');
|
|
214
|
+
expect(select).toHaveTextContent('Item 1');
|
|
215
|
+
});
|
|
237
216
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
'sc-select__option--is-selected',
|
|
217
|
+
it('should scroll to selected value when opening select', () => {
|
|
218
|
+
render(
|
|
219
|
+
<SelectWrapper value={optionsWithScrollSearchBar[9].props.value}>
|
|
220
|
+
{optionsWithScrollSearchBar}
|
|
221
|
+
</SelectWrapper>,
|
|
244
222
|
);
|
|
245
|
-
|
|
223
|
+
const select = selectors.select(true);
|
|
224
|
+
userEvent.click(select);
|
|
225
|
+
const option = selectors.option(/Item 9/);
|
|
226
|
+
expect(screen.queryByRole('option', { name: /Item 1/i })).toBeNull();
|
|
227
|
+
expect(option).toBeVisible();
|
|
246
228
|
});
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
);
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
expect(
|
|
254
|
-
'sc-select__option--is-focused',
|
|
255
|
-
);
|
|
256
|
-
expect(getByTestId('option1')).toHaveClass('sc-select__option--is-focused');
|
|
257
|
-
userEvent.keyboard('{ArrowUp}');
|
|
258
|
-
expect(getByTestId('option0')).toHaveClass('sc-select__option--is-focused');
|
|
259
|
-
expect(getByTestId('option1')).not.toHaveClass(
|
|
260
|
-
'sc-select__option--is-focused',
|
|
261
|
-
);
|
|
229
|
+
|
|
230
|
+
it('should be able to reset the value', () => {
|
|
231
|
+
render(<SelectReset>{simpleOptions}</SelectReset>);
|
|
232
|
+
const button = screen.getByText(/reset/);
|
|
233
|
+
userEvent.click(button);
|
|
234
|
+
const select = selectors.select();
|
|
235
|
+
expect(select).toHaveTextContent('Select...');
|
|
262
236
|
});
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
237
|
+
|
|
238
|
+
it('should not be possible to select an option if it is disabled', () => {
|
|
239
|
+
render(
|
|
240
|
+
<SelectWrapper>
|
|
241
|
+
<Option value="1" disabled>
|
|
242
|
+
Item 1
|
|
243
|
+
</Option>
|
|
244
|
+
<Option value="2">Item 2</Option>
|
|
245
|
+
</SelectWrapper>,
|
|
266
246
|
);
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
247
|
+
const select = selectors.select();
|
|
248
|
+
userEvent.click(select);
|
|
249
|
+
const option = selectors.option(/Item 1/);
|
|
250
|
+
|
|
251
|
+
userEvent.click(option);
|
|
252
|
+
const option2 = selectors.option(/Item 2/);
|
|
253
|
+
expect(option2).toBeVisible();
|
|
271
254
|
});
|
|
272
255
|
|
|
273
|
-
it('should
|
|
274
|
-
const onChange = jest.fn();
|
|
256
|
+
it('should display a tooltip if the option is disabled with a reason', () => {
|
|
275
257
|
render(
|
|
276
|
-
<
|
|
277
|
-
<Option value="
|
|
278
|
-
|
|
258
|
+
<SelectWrapper>
|
|
259
|
+
<Option value="1" disabled disabledReason="This option is disabled">
|
|
260
|
+
Item 1
|
|
261
|
+
</Option>
|
|
262
|
+
</SelectWrapper>,
|
|
279
263
|
);
|
|
280
|
-
|
|
264
|
+
const select = selectors.select();
|
|
265
|
+
userEvent.click(select);
|
|
266
|
+
const option = selectors.option(/Item 1/);
|
|
267
|
+
expect(option).toHaveAttribute('aria-disabled', 'true');
|
|
268
|
+
userEvent.hover(option);
|
|
269
|
+
const tooltip = screen.getByText(/This option is disabled/);
|
|
270
|
+
expect(tooltip).toBeInTheDocument();
|
|
281
271
|
});
|
|
282
272
|
|
|
283
273
|
it('should select with the right selector', async () => {
|
|
@@ -17,7 +17,7 @@ export type SearchProps = {
|
|
|
17
17
|
value?: string;
|
|
18
18
|
locale?: TableLocalType;
|
|
19
19
|
totalCount?: number;
|
|
20
|
-
} & Omit<Props, '
|
|
20
|
+
} & Omit<Props, 'onChange'>;
|
|
21
21
|
|
|
22
22
|
const SearchContainer = styled.div`
|
|
23
23
|
display: flex;
|
|
@@ -88,7 +88,6 @@ export function TableSearch(props: SearchProps) {
|
|
|
88
88
|
<SearchInput
|
|
89
89
|
value={value}
|
|
90
90
|
placeholder={translations[locale].search}
|
|
91
|
-
disableToggle
|
|
92
91
|
size="1"
|
|
93
92
|
onChange={(evt) => {
|
|
94
93
|
if (typeof onChange === 'function') {
|