@scality/core-ui 0.134.0 → 0.136.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/constrainedtext/Constrainedtext.component.d.ts.map +1 -1
- package/dist/components/constrainedtext/Constrainedtext.component.js +4 -1
- package/dist/components/icon/Icon.component.d.ts.map +1 -1
- package/dist/components/icon/Icon.component.js +9 -3
- 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/AttachmentTable.d.ts.map +1 -1
- package/dist/organisms/attachments/AttachmentTable.js +2 -2
- package/package.json +2 -2
- package/src/lib/components/constrainedtext/Constrainedtext.component.tsx +4 -1
- package/src/lib/components/icon/Icon.component.tsx +12 -5
- 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/AttachmentTable.tsx +0 -2
- 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
|
@@ -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') {
|
|
@@ -44,14 +44,14 @@ export const VirtualizedRows = <
|
|
|
44
44
|
listRef,
|
|
45
45
|
itemKey,
|
|
46
46
|
}: VirtualizedRowsType<DATA_ROW>) => (
|
|
47
|
-
<AutoSizer>
|
|
48
|
-
{({ height
|
|
47
|
+
<AutoSizer disableWidth>
|
|
48
|
+
{({ height }) => {
|
|
49
49
|
return (
|
|
50
50
|
<List
|
|
51
|
-
height={height}
|
|
51
|
+
height={height - 1}
|
|
52
52
|
itemCount={rows.length} // how many items we are going to render
|
|
53
53
|
itemSize={convertRemToPixels(tableRowHeight[rowHeight])} // height of each row in pixel
|
|
54
|
-
width={
|
|
54
|
+
width={'100%'}
|
|
55
55
|
itemKey={itemKey}
|
|
56
56
|
itemData={rows}
|
|
57
57
|
ref={listRef}
|
|
@@ -81,7 +81,7 @@ export const VirtualizedRows = <
|
|
|
81
81
|
);
|
|
82
82
|
|
|
83
83
|
export const useTableScrollbar = () => {
|
|
84
|
-
const
|
|
84
|
+
const { hasScrollbar, setHasScrollbar } = useTableContext();
|
|
85
85
|
const [scrollBarWidth, setScrollBarWidth] = useState(0);
|
|
86
86
|
|
|
87
87
|
const handleScrollbarWidth = useCallback((node) => {
|
|
@@ -105,6 +105,10 @@ type TableContextType<
|
|
|
105
105
|
en: { singular: string; plural: string };
|
|
106
106
|
fr?: { singular: string; plural: string };
|
|
107
107
|
};
|
|
108
|
+
syncScrollListener: ((event: Event) => void) | null;
|
|
109
|
+
setSyncScrollListener: (listener: (event: Event) => void) => void;
|
|
110
|
+
setHasScrollbar: React.Dispatch<React.SetStateAction<boolean>>;
|
|
111
|
+
hasScrollbar?: boolean;
|
|
108
112
|
};
|
|
109
113
|
const TableContext = React.createContext<TableContextType | null>(null);
|
|
110
114
|
|
|
@@ -215,6 +219,12 @@ function Table<
|
|
|
215
219
|
|
|
216
220
|
const [rowHeight, setRowHeight] = React.useState<TableHeightKeyType>('h40');
|
|
217
221
|
|
|
222
|
+
const [syncScrollListener, setSyncScrollListener] = React.useState<
|
|
223
|
+
((event: Event) => void) | null
|
|
224
|
+
>(null);
|
|
225
|
+
|
|
226
|
+
const [hasScrollbar, setHasScrollbar] = React.useState<boolean>(false);
|
|
227
|
+
|
|
218
228
|
const {
|
|
219
229
|
headerGroups,
|
|
220
230
|
rows,
|
|
@@ -310,6 +320,10 @@ function Table<
|
|
|
310
320
|
toggleAllRowsSelected,
|
|
311
321
|
status,
|
|
312
322
|
entityName,
|
|
323
|
+
syncScrollListener,
|
|
324
|
+
setSyncScrollListener,
|
|
325
|
+
setHasScrollbar,
|
|
326
|
+
hasScrollbar,
|
|
313
327
|
};
|
|
314
328
|
return (
|
|
315
329
|
<TableContext.Provider
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { useEffect, useState, useCallback } from 'react';
|
|
1
|
+
import { useEffect, useState, useCallback, useRef } from 'react';
|
|
2
2
|
import { Row } from 'react-table';
|
|
3
3
|
import { FixedSizeList } from 'react-window';
|
|
4
|
+
import { useTableContext } from './Tablev2.component';
|
|
4
5
|
|
|
5
6
|
export default function useSyncedScroll<
|
|
6
7
|
DATA_ROW extends Record<string, unknown> = Record<string, unknown>,
|
|
7
8
|
>(): {
|
|
8
9
|
headerRef: (element: HTMLDivElement) => void;
|
|
9
|
-
bodyRef:
|
|
10
|
+
bodyRef: React.RefObject<FixedSizeList<Row<DATA_ROW>[]>>;
|
|
10
11
|
} {
|
|
11
|
-
const
|
|
12
|
-
useState<((event: Event) => void) | null>(null);
|
|
13
|
-
const [tableBody, setTableBody] =
|
|
14
|
-
useState<FixedSizeList<Row<DATA_ROW>[]> | null>(null);
|
|
12
|
+
const { syncScrollListener, setSyncScrollListener } = useTableContext();
|
|
15
13
|
|
|
16
14
|
const headerRef = useCallback(
|
|
17
15
|
(element: HTMLDivElement) => {
|
|
@@ -24,41 +22,41 @@ export default function useSyncedScroll<
|
|
|
24
22
|
});
|
|
25
23
|
}
|
|
26
24
|
};
|
|
27
|
-
if (!
|
|
28
|
-
|
|
25
|
+
if (!syncScrollListener) {
|
|
26
|
+
setSyncScrollListener(() => {
|
|
29
27
|
return callback;
|
|
30
28
|
});
|
|
31
29
|
}
|
|
32
30
|
}
|
|
33
31
|
},
|
|
34
|
-
[
|
|
32
|
+
[syncScrollListener],
|
|
35
33
|
);
|
|
36
34
|
|
|
37
|
-
const bodyRef =
|
|
38
|
-
setTableBody(tableBody);
|
|
39
|
-
}, []);
|
|
35
|
+
const bodyRef = useRef<FixedSizeList<Row<DATA_ROW>[]> | null>(null);
|
|
40
36
|
|
|
41
37
|
useEffect(() => {
|
|
42
|
-
if (
|
|
38
|
+
if (bodyRef.current && syncScrollListener) {
|
|
43
39
|
/*
|
|
44
40
|
We intentionally use _outerRef prop here despite the fact that it is
|
|
45
41
|
internal use only and not typed, as it is the only way for us to access to the scrollable element
|
|
46
42
|
*/
|
|
47
43
|
//@ts-expect-error
|
|
48
|
-
(
|
|
44
|
+
(bodyRef.current._outerRef as HTMLDivElement).addEventListener(
|
|
49
45
|
'scroll',
|
|
50
|
-
|
|
46
|
+
syncScrollListener,
|
|
51
47
|
);
|
|
52
|
-
|
|
53
|
-
return () => {
|
|
54
|
-
//@ts-expect-error
|
|
55
|
-
if (tableBody && tableBody._outerRef) {
|
|
56
|
-
//@ts-expect-error
|
|
57
|
-
tableBody._outerRef.removeEventListener('scroll', listener);
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
48
|
}
|
|
61
|
-
|
|
49
|
+
return () => {
|
|
50
|
+
//@ts-expect-error
|
|
51
|
+
if (bodyRef.current && bodyRef.current._outerRef) {
|
|
52
|
+
//@ts-expect-error
|
|
53
|
+
bodyRef.current._outerRef.removeEventListener(
|
|
54
|
+
'scroll',
|
|
55
|
+
syncScrollListener,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}, [bodyRef.current, syncScrollListener]);
|
|
62
60
|
|
|
63
61
|
return { headerRef, bodyRef };
|
|
64
62
|
}
|
|
@@ -185,8 +185,8 @@ function Tabs({
|
|
|
185
185
|
});
|
|
186
186
|
return (
|
|
187
187
|
<TabsContext.Provider value={true}>
|
|
188
|
-
{/*@ts-expect-error containerType is not yet a valid prop for react */}
|
|
189
188
|
<TabsContainer
|
|
189
|
+
// @ts-expect-error containerType is not yet a valid prop for react
|
|
190
190
|
style={{ containerType: 'size' }}
|
|
191
191
|
className={['sc-tabs', className].join(' ')}
|
|
192
192
|
tabLineColor={tabLineColor}
|