@scality/core-ui 0.161.0 → 0.163.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.
Files changed (173) hide show
  1. package/README.md +15 -15
  2. package/dist/components/accordion/Accordion.component.d.ts +0 -1
  3. package/dist/components/accordion/Accordion.component.d.ts.map +1 -1
  4. package/dist/components/barchartv2/Barchart.component.d.ts +53 -0
  5. package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -0
  6. package/dist/components/barchartv2/Barchart.component.js +86 -0
  7. package/dist/components/barchartv2/utils.d.ts +118 -0
  8. package/dist/components/barchartv2/utils.d.ts.map +1 -0
  9. package/dist/components/barchartv2/utils.js +337 -0
  10. package/dist/components/buttonv2/Buttonv2.component.d.ts +1 -1
  11. package/dist/components/buttonv2/Buttonv2.component.d.ts.map +1 -1
  12. package/dist/components/chartlegend/ChartLegend.d.ts +8 -0
  13. package/dist/components/chartlegend/ChartLegend.d.ts.map +1 -0
  14. package/dist/components/chartlegend/ChartLegend.js +65 -0
  15. package/dist/components/chartlegend/ChartLegendWrapper.d.ts +17 -0
  16. package/dist/components/chartlegend/ChartLegendWrapper.d.ts.map +1 -0
  17. package/dist/components/chartlegend/ChartLegendWrapper.js +50 -0
  18. package/dist/components/constrainedtext/Constrainedtext.component.d.ts +2 -1
  19. package/dist/components/constrainedtext/Constrainedtext.component.d.ts.map +1 -1
  20. package/dist/components/constrainedtext/Constrainedtext.component.js +5 -4
  21. package/dist/components/coreuithemeprovider/CoreUiThemeProvider.d.ts +0 -1
  22. package/dist/components/coreuithemeprovider/CoreUiThemeProvider.d.ts.map +1 -1
  23. package/dist/components/date/FormattedDateTime.d.ts +4 -1
  24. package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
  25. package/dist/components/date/FormattedDateTime.js +24 -1
  26. package/dist/components/date/FormattedDateTime.spec.js +12 -0
  27. package/dist/components/emptytable/Emptytable.component.d.ts +0 -1
  28. package/dist/components/emptytable/Emptytable.component.d.ts.map +1 -1
  29. package/dist/components/emptytable/Emptytable.component.js +1 -0
  30. package/dist/components/error-pages/ErrorPage401.component.d.ts +0 -1
  31. package/dist/components/error-pages/ErrorPage401.component.d.ts.map +1 -1
  32. package/dist/components/error-pages/ErrorPage404.component.d.ts +0 -1
  33. package/dist/components/error-pages/ErrorPage404.component.d.ts.map +1 -1
  34. package/dist/components/error-pages/ErrorPage500.component.d.ts +0 -1
  35. package/dist/components/error-pages/ErrorPage500.component.d.ts.map +1 -1
  36. package/dist/components/error-pages/ErrorPageAuth.component.d.ts.map +1 -1
  37. package/dist/components/form/Form.component.d.ts +2 -2
  38. package/dist/components/form/Form.component.d.ts.map +1 -1
  39. package/dist/components/icon/Icon.component.d.ts +5 -5
  40. package/dist/components/icon/Icon.component.d.ts.map +1 -1
  41. package/dist/components/icon/Icon.component.js +33 -31
  42. package/dist/components/infomessage/InfoMessage.component.d.ts +0 -1
  43. package/dist/components/infomessage/InfoMessage.component.d.ts.map +1 -1
  44. package/dist/components/lateralnavbarlayout/LateralNavbarLayout.component.d.ts.map +1 -1
  45. package/dist/components/layout/Layout.component.d.ts.map +1 -1
  46. package/dist/components/layout/v2/panels.d.ts.map +1 -1
  47. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +33 -0
  48. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -0
  49. package/dist/components/linetimeseriechart/linetimeseriechart.component.js +249 -0
  50. package/dist/components/modal/Modal.component.js +2 -2
  51. package/dist/components/navbar/Navbar.component.js +2 -2
  52. package/dist/components/scrollbarwrapper/ScrollbarWrapper.component.d.ts +0 -1
  53. package/dist/components/scrollbarwrapper/ScrollbarWrapper.component.d.ts.map +1 -1
  54. package/dist/components/searchinput/SearchInput.component.d.ts +1 -2
  55. package/dist/components/searchinput/SearchInput.component.d.ts.map +1 -1
  56. package/dist/components/selectv2/Selectv2.component.d.ts +5 -5
  57. package/dist/components/selectv2/Selectv2.component.d.ts.map +1 -1
  58. package/dist/components/selectv2/Selectv2.component.js +11 -6
  59. package/dist/components/statuswrapper/Statuswrapper.component.d.ts +0 -1
  60. package/dist/components/statuswrapper/Statuswrapper.component.d.ts.map +1 -1
  61. package/dist/components/steppers/Stepper.component.d.ts.map +1 -1
  62. package/dist/components/steppers/Stepper.component.js +9 -8
  63. package/dist/components/tablev2/Search.js +2 -2
  64. package/dist/components/tablev2/SingleSelectableContent.d.ts +1 -2
  65. package/dist/components/tablev2/SingleSelectableContent.d.ts.map +1 -1
  66. package/dist/components/tablev2/TableCommon.d.ts +2 -2
  67. package/dist/components/tablev2/TableCommon.d.ts.map +1 -1
  68. package/dist/components/tablev2/TableSync.d.ts +8 -0
  69. package/dist/components/tablev2/TableSync.d.ts.map +1 -0
  70. package/dist/components/tablev2/TableSync.js +11 -0
  71. package/dist/components/tablev2/Tablev2.component.d.ts +2 -1
  72. package/dist/components/tablev2/Tablev2.component.d.ts.map +1 -1
  73. package/dist/components/tablev2/Tablev2.component.js +10 -9
  74. package/dist/components/tabsv2/ScrollButton.d.ts +1 -2
  75. package/dist/components/tabsv2/ScrollButton.d.ts.map +1 -1
  76. package/dist/components/tabsv2/ScrollButton.js +2 -2
  77. package/dist/components/tabsv2/Tabsv2.component.d.ts +2 -2
  78. package/dist/components/tabsv2/Tabsv2.component.d.ts.map +1 -1
  79. package/dist/components/tabsv2/Tabsv2.component.js +2 -2
  80. package/dist/components/text/Text.component.d.ts +0 -1
  81. package/dist/components/text/Text.component.d.ts.map +1 -1
  82. package/dist/components/textarea/TextArea.component.d.ts +3 -3
  83. package/dist/components/textarea/TextArea.component.d.ts.map +1 -1
  84. package/dist/components/textbadge/TextBadge.component.d.ts +0 -1
  85. package/dist/components/textbadge/TextBadge.component.d.ts.map +1 -1
  86. package/dist/components/toast/Toast.component.d.ts +1 -1
  87. package/dist/components/toast/Toast.component.d.ts.map +1 -1
  88. package/dist/components/toast/ToastProvider.d.ts.map +1 -1
  89. package/dist/components/toast/ToastProvider.js +4 -5
  90. package/dist/components/vegachartv2/SyncedCursorCharts.d.ts +1 -2
  91. package/dist/components/vegachartv2/SyncedCursorCharts.d.ts.map +1 -1
  92. package/dist/components/vegachartv2/SyncedCursorCharts.js +3 -5
  93. package/dist/components/vegachartv2/VegaChartV2.component.d.ts +1 -2
  94. package/dist/components/vegachartv2/VegaChartV2.component.d.ts.map +1 -1
  95. package/dist/components/vegachartv2/VegaChartV2.component.js +2 -2
  96. package/dist/icons/branding.d.ts.map +1 -1
  97. package/dist/icons/scality-loading.d.ts.map +1 -1
  98. package/dist/index.d.ts +1 -0
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +1 -0
  101. package/dist/next.d.ts +2 -0
  102. package/dist/next.d.ts.map +1 -1
  103. package/dist/next.js +2 -0
  104. package/dist/style/theme.d.ts +20 -0
  105. package/dist/style/theme.d.ts.map +1 -1
  106. package/dist/style/theme.js +46 -1
  107. package/package.json +7 -4
  108. package/setupTests.js +6 -0
  109. package/src/lib/components/accordion/Accordion.component.tsx +1 -1
  110. package/src/lib/components/accordion/Accordion.test.tsx +7 -15
  111. package/src/lib/components/barchartv2/Barchart.component.test.tsx +364 -0
  112. package/src/lib/components/barchartv2/Barchart.component.tsx +321 -0
  113. package/src/lib/components/barchartv2/utils.test.ts +899 -0
  114. package/src/lib/components/barchartv2/utils.ts +534 -0
  115. package/src/lib/components/buttonv2/Buttonv2.component.tsx +1 -1
  116. package/src/lib/components/chartlegend/ChartLegend.tsx +113 -0
  117. package/src/lib/components/chartlegend/ChartLegendWrapper.tsx +85 -0
  118. package/src/lib/components/constrainedtext/Constrainedtext.component.tsx +22 -3
  119. package/src/lib/components/coreuithemeprovider/CoreUiThemeProvider.tsx +0 -1
  120. package/src/lib/components/date/FormattedDateTime.spec.tsx +24 -0
  121. package/src/lib/components/date/FormattedDateTime.tsx +42 -2
  122. package/src/lib/components/emptytable/Emptytable.component.tsx +1 -1
  123. package/src/lib/components/error-pages/ErrorPage401.component.tsx +0 -1
  124. package/src/lib/components/error-pages/ErrorPage404.component.tsx +0 -1
  125. package/src/lib/components/error-pages/ErrorPage500.component.tsx +0 -1
  126. package/src/lib/components/error-pages/ErrorPageAuth.component.tsx +0 -1
  127. package/src/lib/components/form/Form.component.tsx +1 -1
  128. package/src/lib/components/healthselectorv2/HealthSelector.component.test.tsx +3 -3
  129. package/src/lib/components/icon/Icon.component.tsx +48 -60
  130. package/src/lib/components/infomessage/InfoMessage.component.tsx +0 -1
  131. package/src/lib/components/inlineinput/InlineInput.test.tsx +22 -19
  132. package/src/lib/components/inputlist/InputList.test.tsx +21 -19
  133. package/src/lib/components/lateralnavbarlayout/LateralNavbarLayout.component.tsx +0 -1
  134. package/src/lib/components/layout/Layout.component.tsx +0 -1
  135. package/src/lib/components/layout/v2/panels.tsx +1 -1
  136. package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +502 -0
  137. package/src/lib/components/modal/Modal.component.tsx +2 -2
  138. package/src/lib/components/navbar/Navbar.component.tsx +2 -2
  139. package/src/lib/components/scrollbarwrapper/ScrollbarWrapper.component.tsx +0 -1
  140. package/src/lib/components/searchinput/SearchInput.component.tsx +0 -1
  141. package/src/lib/components/searchinput/SearchInput.test.tsx +3 -7
  142. package/src/lib/components/selectv2/Selectv2.component.tsx +24 -14
  143. package/src/lib/components/selectv2/selectv2.test.tsx +62 -57
  144. package/src/lib/components/sidebar/Sidebar.component.tsx +1 -1
  145. package/src/lib/components/statuswrapper/Statuswrapper.component.tsx +0 -1
  146. package/src/lib/components/steppers/Stepper.component.tsx +10 -8
  147. package/src/lib/components/tablev2/Search.tsx +2 -2
  148. package/src/lib/components/tablev2/SingleSelectableContent.tsx +2 -2
  149. package/src/lib/components/tablev2/TableCommon.tsx +1 -1
  150. package/src/lib/components/tablev2/TableSync.test.tsx +28 -0
  151. package/src/lib/components/tablev2/TableSync.tsx +36 -0
  152. package/src/lib/components/tablev2/Tablev2.component.tsx +11 -9
  153. package/src/lib/components/tablev2/Tablev2.test.tsx +36 -37
  154. package/src/lib/components/tabsv2/ScrollButton.tsx +2 -2
  155. package/src/lib/components/tabsv2/Tabsv2.component.tsx +6 -6
  156. package/src/lib/components/text/Text.component.tsx +4 -5
  157. package/src/lib/components/textarea/TextArea.component.tsx +3 -2
  158. package/src/lib/components/textbadge/TextBadge.component.tsx +0 -1
  159. package/src/lib/components/toast/Toast.component.tsx +1 -1
  160. package/src/lib/components/toast/ToastProvider.tsx +17 -7
  161. package/src/lib/components/vegachartv2/SyncedCursorCharts.tsx +5 -7
  162. package/src/lib/components/vegachartv2/VegaChartV2.component.tsx +2 -2
  163. package/src/lib/icons/branding.tsx +0 -2
  164. package/src/lib/icons/scality-loading.tsx +0 -2
  165. package/src/lib/index.ts +1 -0
  166. package/src/lib/next.ts +6 -0
  167. package/src/lib/style/theme.ts +53 -1
  168. package/stories/BarChart/barchart.stories.tsx +822 -0
  169. package/stories/areachart.stories.tsx +0 -1
  170. package/stories/format.mdx +4 -2
  171. package/stories/linetimeseriechart.stories.tsx +485 -0
  172. package/stories/tablev2.stories.tsx +41 -0
  173. package/tsconfig.json +5 -2
@@ -1,16 +1,11 @@
1
1
  import React from 'react';
2
2
  import { render, screen, waitFor } from '@testing-library/react';
3
3
  import { SearchInput, Props } from './SearchInput.component';
4
- import { QueryClient, QueryClientProvider } from 'react-query';
5
4
  import userEvent from '@testing-library/user-event';
6
5
 
7
- const queryClient = new QueryClient();
8
-
9
6
  const SearchInputRender = (props: Props) => {
10
7
  return (
11
- <QueryClientProvider client={queryClient}>
12
- <SearchInput {...props} />
13
- </QueryClientProvider>
8
+ <SearchInput {...props} />
14
9
  );
15
10
  };
16
11
 
@@ -19,8 +14,9 @@ describe('SearchInput', () => {
19
14
  searchInput: () => screen.getByRole('searchbox'),
20
15
  clearButton: () => screen.queryByRole('button'),
21
16
  };
22
- it('should render the SearchInput component', () => {
17
+ it('should render the SearchInput component', async () => {
23
18
  render(<SearchInputRender value="" onChange={() => {}} />);
19
+ await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
24
20
 
25
21
  const searchInput = selectors.searchInput();
26
22
  expect(searchInput).toBeInTheDocument();
@@ -1,4 +1,4 @@
1
- import React, {
1
+ import {
2
2
  createContext,
3
3
  useContext,
4
4
  useState,
@@ -8,6 +8,10 @@ import React, {
8
8
  ForwardRefExoticComponent,
9
9
  RefAttributes,
10
10
  useImperativeHandle,
11
+ ReactNode,
12
+ Ref,
13
+ useMemo,
14
+ useCallback,
11
15
  } from 'react';
12
16
  import { ScrollbarWrapper, Tooltip } from '../../index';
13
17
  import {
@@ -31,10 +35,10 @@ const NOPT_SEARCH = 8;
31
35
  export type OptionProps = {
32
36
  title?: string;
33
37
  disabled?: boolean;
34
- icon?: React.ReactNode;
35
- children?: React.ReactNode;
38
+ icon?: ReactNode;
39
+ children?: ReactNode;
36
40
  value: string;
37
- disabledReason?: React.ReactNode;
41
+ disabledReason?: ReactNode;
38
42
  };
39
43
  const usePreviousValue = (value) => {
40
44
  const ref = useRef(null);
@@ -335,7 +339,7 @@ export type SelectProps = {
335
339
  id: string;
336
340
  placeholder?: string;
337
341
  disabled?: boolean;
338
- children?: React.ReactNode;
342
+ children?: ReactNode;
339
343
  value?: string;
340
344
  onFocus?: (event: FocusEvent) => void;
341
345
  onBlur?: (event: FocusEvent) => void;
@@ -349,11 +353,11 @@ export type SelectProps = {
349
353
 
350
354
  type SelectOptionProps = {
351
355
  value: string;
352
- label: React.ReactNode;
356
+ label: ReactNode;
353
357
  isDisabled: boolean;
354
- icon?: React.ReactNode;
358
+ icon?: ReactNode;
355
359
  optionProps: any;
356
- disabledReason?: React.ReactNode;
360
+ disabledReason?: ReactNode;
357
361
  };
358
362
 
359
363
  type SelectComponentType<
@@ -388,7 +392,7 @@ function SelectBox<
388
392
  selectRef,
389
393
  ...rest
390
394
  }: SelectProps & {
391
- selectRef?: React.Ref<SelectRef<OptionType, IsMulti, GroupType>>;
395
+ selectRef?: Ref<SelectRef<OptionType, IsMulti, GroupType>>;
392
396
  }) {
393
397
  const [keyboardFocusEnabled, setKeyboardFocusEnabled] = useState(false);
394
398
  const [searchSelection, setSearchSelection] = useState('');
@@ -560,22 +564,28 @@ const SelectWithOptionContext = forwardRef<
560
564
  >((props, ref) => {
561
565
  const [options, setOptions] = useState<Record<string, SelectOptionProps>>({});
562
566
 
563
- const register = (option: SelectOptionProps) => {
567
+ const register = useCallback((option: SelectOptionProps) => {
564
568
  setOptions((prevOptions) => ({
565
569
  ...prevOptions,
566
570
  [option.value]: option,
567
571
  }));
568
- };
572
+ }, []);
569
573
 
570
- const unregister = (value: string) => {
574
+ const unregister = useCallback((value: string) => {
571
575
  setOptions((prevOptions) => {
572
576
  const { [value]: _, ...rest } = prevOptions;
573
577
  return rest;
574
578
  });
575
- };
579
+ }, []);
580
+
581
+ const contextValue = useMemo(() => ({
582
+ options,
583
+ register,
584
+ unregister
585
+ }), [options, register, unregister]);
576
586
 
577
587
  return (
578
- <OptionContext.Provider value={{ options, register, unregister }}>
588
+ <OptionContext.Provider value={contextValue}>
579
589
  <>
580
590
  <SelectBox {...props} selectRef={ref} />
581
591
  {props.children}
@@ -1,15 +1,10 @@
1
- import { screen, render as testingRender } from '@testing-library/react';
1
+ import { act, screen, render as testingRender, waitFor } from '@testing-library/react';
2
2
  import userEvent from '@testing-library/user-event';
3
3
  import React, { useState, useRef } from 'react';
4
- import { QueryClient, QueryClientProvider } from 'react-query';
5
4
  import { Option, Select, SelectRef } from '../selectv2/Selectv2.component';
6
5
 
7
6
  const render = (args) => {
8
- return testingRender(
9
- <QueryClientProvider client={new QueryClient()}>
10
- {args}
11
- </QueryClientProvider>,
12
- );
7
+ return testingRender(args);
13
8
  };
14
9
 
15
10
  const generateOptionsData = (n: number) =>
@@ -74,27 +69,29 @@ describe('SelectV2', () => {
74
69
  expect(() => render(<Option value="Option 1" />)).toThrowError();
75
70
  });
76
71
 
77
- it('should open/close on click', () => {
72
+ it('should open/close on click', async () => {
78
73
  render(<SelectWrapper />);
74
+ await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
79
75
  const select = selectors.select();
80
76
  expect(select).toBeInTheDocument();
81
77
  let options = selectors.options();
82
78
  expect(options).toHaveLength(0);
83
79
 
84
80
  // should open on click
85
- userEvent.click(select);
81
+ await act(() => userEvent.click(select));
86
82
  simpleOptions.forEach((opt) => {
87
83
  const option = selectors.option(opt.props.label);
88
84
  expect(option).toBeInTheDocument();
89
85
  });
90
86
 
91
- userEvent.click(select);
87
+ await act(() => userEvent.click(select));
92
88
  options = selectors.options();
93
89
  expect(options).toHaveLength(0);
94
90
  });
95
91
 
96
- it('should open/close with keyboard', () => {
92
+ it('should open/close with keyboard', async () => {
97
93
  render(<SelectWrapper />);
94
+ await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
98
95
  const select = selectors.select();
99
96
  expect(select).toBeInTheDocument();
100
97
  const options = selectors.options();
@@ -102,7 +99,7 @@ describe('SelectV2', () => {
102
99
 
103
100
  // should open on Enter
104
101
  userEvent.tab();
105
- userEvent.keyboard('{Enter}');
102
+ await act(() => userEvent.keyboard('{Enter}'));
106
103
  simpleOptions.forEach((opt) => {
107
104
  const option = selectors.option(opt.props.label);
108
105
  expect(option).toBeInTheDocument();
@@ -121,13 +118,14 @@ describe('SelectV2', () => {
121
118
  });
122
119
  });
123
120
 
124
- it('should display custom placeholder', () => {
121
+ it('should display custom placeholder', async () => {
125
122
  const placeholder = 'My placeholder...';
126
123
  render(<SelectWrapper placeholder={placeholder} />);
124
+ await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
127
125
  expect(screen.getByText(placeholder)).toBeInTheDocument();
128
126
  });
129
127
 
130
- it('should be disabled', () => {
128
+ it('should be disabled', async () => {
131
129
  render(
132
130
  <SelectWrapper value="1" disabled={true}>
133
131
  {simpleOptions}
@@ -139,27 +137,29 @@ describe('SelectV2', () => {
139
137
  // use input instead of select because select will still trigger the open/close action
140
138
  // despite select container not being clickable and input being disabled
141
139
  const input = selectors.input();
142
- userEvent.click(input);
140
+ await act(() => userEvent.click(input));
143
141
  const options = selectors.options();
144
142
  expect(options).toHaveLength(0);
145
143
  });
146
144
 
147
- it('should display no option', () => {
145
+ it('should display no option', async () => {
148
146
  render(
149
147
  <SelectWrapper>
150
148
  <></>
151
149
  </SelectWrapper>,
152
150
  );
151
+ await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
153
152
  const select = selectors.select();
154
- userEvent.click(select);
153
+ await act(() => userEvent.click(select));
155
154
  const noOptions = selectors.noOptions();
156
155
  expect(noOptions).toBeInTheDocument();
157
156
  });
158
157
 
159
- it('should filter and highlight on search', () => {
158
+ it('should filter and highlight on search', async () => {
160
159
  render(<SelectWrapper>{optionsWithScrollSearchBar} </SelectWrapper>);
160
+ await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
161
161
  const select = selectors.select(true);
162
- userEvent.click(select);
162
+ await act(() => userEvent.click(select));
163
163
  const input = selectors.input();
164
164
 
165
165
  userEvent.type(input, '2');
@@ -169,25 +169,26 @@ describe('SelectV2', () => {
169
169
  expect(searchedText).toHaveTextContent('2');
170
170
  });
171
171
 
172
- it('should unfocus the search input when the select is closed', () => {
172
+ it('should unfocus the search input when the select is closed', async () => {
173
173
  render(<SelectWrapper>{optionsWithScrollSearchBar} </SelectWrapper>);
174
+ await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
174
175
  const select = selectors.select(true);
175
- userEvent.click(select);
176
+ await act(() => userEvent.click(select));
176
177
  let input = selectors.input();
177
178
  expect(input).toHaveFocus();
178
179
  const option = selectors.option(/Item 1/);
179
- userEvent.click(option);
180
+ await act(() => userEvent.click(option));
180
181
  input = selectors.input();
181
182
  expect(input).not.toHaveFocus();
182
183
  });
183
184
 
184
- it('should be possible to use searchbar when option is selected', () => {
185
+ it('should be possible to use searchbar when option is selected', async () => {
185
186
  render(
186
187
  <SelectWrapper value="1">{optionsWithScrollSearchBar}</SelectWrapper>,
187
188
  );
188
189
  expect(screen.getByText(/Item 1/)).toBeVisible();
189
190
  const select = selectors.select(true);
190
- userEvent.click(select);
191
+ await act(() => userEvent.click(select));
191
192
  const input = selectors.input();
192
193
  userEvent.type(input, '2');
193
194
  expect(screen.queryByText(/Item 1/)).not.toBeInTheDocument();
@@ -195,47 +196,48 @@ describe('SelectV2', () => {
195
196
  expect(options).toHaveLength(1);
196
197
  });
197
198
 
198
- it('should select/unselect option with keyboard', () => {
199
+ it('should select/unselect option with keyboard', async () => {
199
200
  render(<SelectWrapper />);
201
+ await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
200
202
  const select = selectors.select();
201
203
  userEvent.tab();
202
- userEvent.keyboard('{ArrowDown}');
204
+ act(() => userEvent.keyboard('{ArrowDown}'));
203
205
 
204
206
  // should select first option
205
- userEvent.keyboard('{Enter}');
207
+ await act(() => userEvent.keyboard('{Enter}'));
206
208
  expect(select).toHaveTextContent('Item 0');
207
209
 
208
210
  // should select second option
209
211
  userEvent.tab();
210
- userEvent.keyboard('{ArrowDown}');
211
- userEvent.keyboard('{ArrowDown}');
212
+ await act(() => userEvent.keyboard('{ArrowDown}'));
213
+ await act(() => userEvent.keyboard('{ArrowDown}'));
212
214
 
213
- userEvent.keyboard('{Enter}');
215
+ await act(() => userEvent.keyboard('{Enter}'));
214
216
  expect(select).toHaveTextContent('Item 1');
215
217
  });
216
218
 
217
- it('should scroll to selected value when opening select', () => {
219
+ it('should scroll to selected value when opening select', async () => {
218
220
  render(
219
221
  <SelectWrapper value={optionsWithScrollSearchBar[9].props.value}>
220
222
  {optionsWithScrollSearchBar}
221
223
  </SelectWrapper>,
222
224
  );
223
225
  const select = selectors.select(true);
224
- userEvent.click(select);
226
+ await act(() => userEvent.click(select));
225
227
  const option = selectors.option(/Item 9/);
226
228
  expect(screen.queryByRole('option', { name: /Item 1/i })).toBeNull();
227
229
  expect(option).toBeVisible();
228
230
  });
229
231
 
230
- it('should be able to reset the value', () => {
232
+ it('should be able to reset the value', async () => {
231
233
  render(<SelectReset>{simpleOptions}</SelectReset>);
232
234
  const button = screen.getByText(/reset/);
233
- userEvent.click(button);
235
+ await act(() => userEvent.click(button));
234
236
  const select = selectors.select();
235
237
  expect(select).toHaveTextContent('Select...');
236
238
  });
237
239
 
238
- it('should not be possible to select an option if it is disabled', () => {
240
+ it('should not be possible to select an option if it is disabled', async () => {
239
241
  render(
240
242
  <SelectWrapper>
241
243
  <Option value="1" disabled>
@@ -245,15 +247,15 @@ describe('SelectV2', () => {
245
247
  </SelectWrapper>,
246
248
  );
247
249
  const select = selectors.select();
248
- userEvent.click(select);
250
+ await act(() => userEvent.click(select));
249
251
  const option = selectors.option(/Item 1/);
250
252
 
251
- userEvent.click(option);
253
+ await act(() => userEvent.click(option));
252
254
  const option2 = selectors.option(/Item 2/);
253
255
  expect(option2).toBeVisible();
254
256
  });
255
257
 
256
- it('should display a tooltip if the option is disabled with a reason', () => {
258
+ it('should display a tooltip if the option is disabled with a reason', async () => {
257
259
  render(
258
260
  <SelectWrapper>
259
261
  <Option value="1" disabled disabledReason="This option is disabled">
@@ -262,10 +264,10 @@ describe('SelectV2', () => {
262
264
  </SelectWrapper>,
263
265
  );
264
266
  const select = selectors.select();
265
- userEvent.click(select);
267
+ await act(() => userEvent.click(select));
266
268
  const option = selectors.option(/Item 1/);
267
269
  expect(option).toHaveAttribute('aria-disabled', 'true');
268
- userEvent.hover(option);
270
+ await act(() => userEvent.hover(option));
269
271
  const tooltip = screen.getByText(/This option is disabled/);
270
272
  expect(tooltip).toBeInTheDocument();
271
273
  });
@@ -309,9 +311,9 @@ describe('SelectV2', () => {
309
311
  // It's not our case here, so it makes thing difficult to select the right select
310
312
  // I workaround this by using setting the aria-label to the select container (cf: test below)
311
313
  const singleSelect = screen.getByRole('listbox');
312
- await userEvent.click(singleSelect);
314
+ await act(() => userEvent.click(singleSelect));
313
315
 
314
- await userEvent.click(screen.getByRole('option', { name: /account 1/i }));
316
+ await act(() => userEvent.click(screen.getByRole('option', { name: /account 1/i })));
315
317
  });
316
318
 
317
319
  it('should be testable if we have several select', async () => {
@@ -372,13 +374,13 @@ describe('SelectV2', () => {
372
374
 
373
375
  render(<MyWrapperWith2Select />);
374
376
 
375
- await userEvent.click(screen.getByLabelText(/select account/i));
377
+ await act(() => userEvent.click(screen.getByLabelText(/select account/i)));
376
378
 
377
- await userEvent.click(screen.getByRole('option', { name: /account 1/i }));
379
+ await act(() => userEvent.click(screen.getByRole('option', { name: /account 1/i })));
378
380
 
379
- await userEvent.click(screen.getByLabelText(/select user/i));
381
+ await act(() => userEvent.click(screen.getByLabelText(/select user/i)));
380
382
 
381
- await userEvent.click(screen.getByRole('option', { name: /user 1/i }));
383
+ await act(() => userEvent.click(screen.getByRole('option', { name: /user 1/i })));
382
384
  });
383
385
 
384
386
  it('should be testable even if we have several select with the same value, the placeholder should be different', async () => {
@@ -439,8 +441,8 @@ describe('SelectV2', () => {
439
441
 
440
442
  render(<MyWrapperWith2Select />);
441
443
 
442
- await userEvent.click(screen.getByLabelText(/select account/i));
443
- await userEvent.click(screen.getByLabelText(/Select Second Account/i));
444
+ await act(() => userEvent.click(screen.getByLabelText(/select account/i)));
445
+ await act(() => userEvent.click(screen.getByLabelText(/Select Second Account/i)));
444
446
 
445
447
  /**
446
448
  * This is possible because only 1 select can be open at a time
@@ -450,7 +452,7 @@ describe('SelectV2', () => {
450
452
  * const selectContainer = select?.parentElement?.parentElement;
451
453
  * const option = within(selectContainer).getByRole('option', { name: /account 1/i });
452
454
  */
453
- await userEvent.click(screen.getByRole('option', { name: /account 1/i }));
455
+ await act(() => userEvent.click(screen.getByRole('option', { name: /account 1/i })));
454
456
  });
455
457
 
456
458
  describe('Ref API', () => {
@@ -478,7 +480,7 @@ describe('SelectV2', () => {
478
480
  render(<RefTestComponent />);
479
481
  expect(selectors.input()).not.toHaveFocus();
480
482
 
481
- userEvent.click(screen.getByRole('button', { name: /Focus/i }));
483
+ await act(() => userEvent.click(screen.getByRole('button', { name: /Focus/i })));
482
484
  expect(selectors.input()).toHaveFocus();
483
485
  });
484
486
 
@@ -511,14 +513,14 @@ describe('SelectV2', () => {
511
513
  render(<RefTestComponent />);
512
514
  expect(selectors.options()).toHaveLength(0);
513
515
 
514
- userEvent.click(screen.getByRole('button', { name: /Open Menu/i }));
516
+ await act(() => userEvent.click(screen.getByRole('button', { name: /Open Menu/i })));
515
517
  expect(selectors.options().length).toBeGreaterThan(0);
516
518
  simpleOptions.forEach((opt) => {
517
519
  const option = selectors.option(opt.props.label);
518
520
  expect(option).toBeInTheDocument();
519
521
  });
520
522
 
521
- userEvent.click(screen.getByRole('button', { name: /Close Menu/i }));
523
+ await act(() => userEvent.click(screen.getByRole('button', { name: /Close Menu/i })));
522
524
  expect(selectors.options()).toHaveLength(0);
523
525
  });
524
526
 
@@ -565,13 +567,16 @@ describe('SelectV2', () => {
565
567
 
566
568
  render(<RefTestComponent />);
567
569
 
568
- const select = selectors.select();
570
+ let select;
571
+ await act(() => {
572
+ select = selectors.select();
573
+ });
569
574
  expect(select).toHaveTextContent('Select with ref');
570
575
 
571
- userEvent.click(screen.getByRole('button', { name: /Set Value/i }));
576
+ await act(() => userEvent.click(screen.getByRole('button', { name: /Set Value/i })));
572
577
  expect(select).toHaveTextContent('Item 0');
573
578
 
574
- userEvent.click(screen.getByRole('button', { name: /Clear/i }));
579
+ await act(() => userEvent.click(screen.getByRole('button', { name: /Clear/i })));
575
580
  expect(select).toHaveTextContent('Select with ref');
576
581
  });
577
582
 
@@ -599,10 +604,10 @@ describe('SelectV2', () => {
599
604
 
600
605
  render(<RefTestComponent />);
601
606
 
602
- userEvent.click(screen.getByRole('button', { name: /Focus/i }));
607
+ await act(() => userEvent.click(screen.getByRole('button', { name: /Focus/i })));
603
608
  expect(selectors.input()).toHaveFocus();
604
609
 
605
- userEvent.click(screen.getByRole('button', { name: /Blur/i }));
610
+ await act(() => userEvent.click(screen.getByRole('button', { name: /Blur/i })));
606
611
  expect(selectors.input()).not.toHaveFocus();
607
612
  });
608
613
  });
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import { useState } from 'react';
2
2
  import styled, { css } from 'styled-components';
3
3
 
4
4
  import {
@@ -1,4 +1,3 @@
1
- import * as React from 'react';
2
1
  import styled, { css } from 'styled-components';
3
2
 
4
3
  import {
@@ -1,5 +1,5 @@
1
1
  /// <reference path="./Stepper.component.d.ts" />
2
- import { createContext, useContext, useState } from 'react';
2
+ import { createContext, useContext, useState, useCallback, useMemo } from 'react';
3
3
  import { Steppers } from './Steppers.component';
4
4
  import { Box } from '../box/Box';
5
5
  export interface StepperContextType {
@@ -33,19 +33,21 @@ export const Stepper: Stepper = ({ steps }) => {
33
33
  props: Record<string, unknown>;
34
34
  }>({ step: 0, props: {} });
35
35
 
36
- const next = (props: Record<string, unknown>) => {
37
- setStepProps({ step: stepProps.step + 1, props });
38
- };
36
+ const next = useCallback((props: Record<string, unknown>) => {
37
+ setStepProps(current => ({ step: current.step + 1, props }));
38
+ }, []);
39
39
 
40
- const prev = (props: Record<string, unknown>) => {
41
- setStepProps({ step: stepProps.step - 1, props });
42
- };
40
+ const prev = useCallback((props: Record<string, unknown>) => {
41
+ setStepProps(current => ({ step: current.step - 1, props }));
42
+ }, []);
43
43
 
44
44
  const { Component } = steps[stepProps.step];
45
45
  const StepperContext = window.StepperContext;
46
46
 
47
+ const stepperValue = useMemo(() => ({ next, prev }), [next, prev]);
48
+
47
49
  return (
48
- <StepperContext.Provider value={{ next, prev }}>
50
+ <StepperContext.Provider value={stepperValue}>
49
51
  <Box display="flex" gap={32} flex={1} height="100%">
50
52
  <Steppers
51
53
  activeStep={stepProps.step}
@@ -1,5 +1,5 @@
1
- import React from 'react';
2
1
  import styled from 'styled-components';
2
+ import { useEffect } from 'react';
3
3
  import { useTableContext } from './Tablev2.component';
4
4
  import { SearchInput } from '../searchinput/SearchInput.component';
5
5
  import { Props } from '../searchinput/SearchInput.component';
@@ -74,7 +74,7 @@ export function TableSearch(props: SearchProps) {
74
74
  entityName = { en: { singular: 'result', plural: 'results' } },
75
75
  } = useTableContext();
76
76
  const totalDispayedRows = totalCount ? totalCount : rows.length;
77
- React.useEffect(() => {
77
+ useEffect(() => {
78
78
  setGlobalFilter(value);
79
79
  }, [value, setGlobalFilter, preGlobalFilteredRows]);
80
80
  return (
@@ -1,4 +1,4 @@
1
- import React, { memo, useEffect } from 'react';
1
+ import { memo, useEffect } from 'react';
2
2
  import { areEqual } from 'react-window';
3
3
  import { Row } from 'react-table';
4
4
  import { useTableContext } from './Tablev2.component';
@@ -32,7 +32,7 @@ export type SingleSelectableContentProps<
32
32
  customItemKey?: (index: number, data: DATA_ROW) => string;
33
33
  hasScrollbar?: boolean;
34
34
  isLoadingMoreItems?: boolean;
35
- children?: (rows: React.JSX.Element) => React.JSX.Element;
35
+ children?: (rows: JSX.Element) => JSX.Element;
36
36
  };
37
37
 
38
38
  export function SingleSelectableContent<
@@ -1,4 +1,4 @@
1
- import React, { ComponentType, LegacyRef, useCallback, useState } from 'react';
1
+ import { ComponentType, LegacyRef, useCallback, useState } from 'react';
2
2
  import { Row } from 'react-table';
3
3
  import AutoSizer from 'react-virtualized-auto-sizer';
4
4
  import {
@@ -0,0 +1,28 @@
1
+ import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
2
+ import React from 'react';
3
+ import { TableSync } from './TableSync';
4
+
5
+ describe('TableSync', () => {
6
+ it('should render correctly', async () => {
7
+ const onSync = jest.fn();
8
+ render(
9
+ <TableSync onSync={onSync} />
10
+ );
11
+ await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
12
+
13
+ const button = screen.getByRole('button');
14
+ expect(button).toBeInTheDocument();
15
+ });
16
+
17
+ it('should call onSync when clicked', async () => {
18
+ const onSync = jest.fn();
19
+ render(
20
+ <TableSync onSync={onSync} />
21
+ );
22
+ await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
23
+
24
+ const button = screen.getByRole('button');
25
+ await act(() => fireEvent.click(button));
26
+ expect(onSync).toHaveBeenCalledTimes(1);
27
+ });
28
+ });
@@ -0,0 +1,36 @@
1
+ import { ButtonHTMLAttributes } from 'react';
2
+ import styled from 'styled-components';
3
+ import { Button } from '../buttonv2/Buttonv2.component';
4
+ import { Icon } from '../icon/Icon.component';
5
+
6
+ export type TableSyncProps = {
7
+ onSync: () => void;
8
+ tooltipOverlay?: string;
9
+ loading?: boolean;
10
+ } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'>;
11
+
12
+ const TableSyncContainer = styled.div`
13
+ display: flex;
14
+ align-items: center;
15
+ `;
16
+
17
+ export function TableSync({
18
+ onSync,
19
+ tooltipOverlay,
20
+ loading = false,
21
+ ...rest
22
+ }: TableSyncProps) {
23
+ return (
24
+ <TableSyncContainer>
25
+ <Button
26
+ icon={<Icon name="Sync" />}
27
+ tooltip={tooltipOverlay ? { overlay: tooltipOverlay } : undefined}
28
+ onClick={onSync}
29
+ aria-label="Synchronize table data"
30
+ role="button"
31
+ isLoading={loading}
32
+ {...rest}
33
+ />
34
+ </TableSyncContainer>
35
+ );
36
+ }