@transferwise/components 46.33.0 → 46.35.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/build/index.js +195 -200
- package/build/index.js.map +1 -1
- package/build/index.mjs +189 -194
- package/build/index.mjs.map +1 -1
- package/build/types/accordion/AccordionItem/AccordionItem.d.ts.map +1 -1
- package/build/types/alert/Alert.d.ts +3 -2
- package/build/types/alert/Alert.d.ts.map +1 -1
- package/build/types/common/hooks/useMedia.d.ts.map +1 -1
- package/build/types/common/panel/Panel.d.ts.map +1 -1
- package/build/types/common/responsivePanel/ResponsivePanel.d.ts.map +1 -1
- package/build/types/dimmer/Dimmer.d.ts +1 -11
- package/build/types/dimmer/Dimmer.d.ts.map +1 -1
- package/build/types/drawer/Drawer.d.ts +4 -4
- package/build/types/index.d.ts +3 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/modal/Modal.d.ts.map +1 -1
- package/build/types/processIndicator/ProcessIndicator.d.ts +36 -19
- package/build/types/processIndicator/ProcessIndicator.d.ts.map +1 -1
- package/build/types/processIndicator/index.d.ts +2 -2
- package/build/types/processIndicator/index.d.ts.map +1 -1
- package/build/types/promoCard/PromoCard.d.ts.map +1 -1
- package/build/types/select/searchBox/SearchBox.d.ts +1 -1
- package/build/types/snackbar/Snackbar.d.ts +0 -1
- package/build/types/snackbar/Snackbar.d.ts.map +1 -1
- package/build/types/test-utils/wait.d.ts +2 -0
- package/build/types/test-utils/wait.d.ts.map +1 -0
- package/build/types/tooltip/Tooltip.d.ts +1 -1
- package/build/types/tooltip/Tooltip.d.ts.map +1 -1
- package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
- package/build/types/withDisplayFormat/WithDisplayFormat.d.ts.map +1 -1
- package/package.json +10 -14
- package/src/accordion/AccordionItem/AccordionItem.tsx +2 -4
- package/src/alert/Alert.spec.tsx +12 -0
- package/src/alert/Alert.story.tsx +11 -1
- package/src/alert/Alert.tsx +33 -14
- package/src/avatarWrapper/AvatarWrapper.story.tsx +1 -3
- package/src/button/Button.tsx +1 -1
- package/src/common/hooks/useConditionalListener/useConditionalListener.spec.js +1 -1
- package/src/common/hooks/useHasIntersected/useHasIntersected.spec.js +3 -3
- package/src/common/hooks/useMedia.spec.ts +1 -1
- package/src/common/hooks/useMedia.ts +1 -2
- package/src/common/panel/Panel.tsx +90 -92
- package/src/common/responsivePanel/ResponsivePanel.tsx +34 -38
- package/src/dateLookup/DateLookup.rtl.spec.tsx +181 -5
- package/src/dateLookup/DateLookup.testingLibrary.spec.js +171 -124
- package/src/drawer/Drawer.js +3 -3
- package/src/field/Field.tsx +3 -3
- package/src/index.ts +3 -2
- package/src/inputs/SelectInput.story.tsx +3 -2
- package/src/inputs/SelectInput.tsx +10 -2
- package/src/modal/Modal.tsx +1 -2
- package/src/processIndicator/ProcessIndicator.rtl.spec.tsx +45 -0
- package/src/processIndicator/ProcessIndicator.tsx +110 -0
- package/src/promoCard/PromoCard.tsx +1 -2
- package/src/radio/__snapshots__/Radio.rtl.spec.tsx.snap +0 -1
- package/src/snackbar/Snackbar.spec.js +8 -2
- package/src/snackbar/Snackbar.story.tsx +3 -1
- package/src/snackbar/Snackbar.tsx +1 -1
- package/src/tabs/Tabs.spec.js +46 -27
- package/src/test-utils/index.js +5 -7
- package/src/test-utils/jest.setup.js +9 -3
- package/src/test-utils/wait.ts +7 -0
- package/src/tooltip/Tooltip.tsx +44 -46
- package/src/tooltip/__snapshots__/Tooltip.spec.tsx.snap +2 -2
- package/src/upload/Upload.spec.js +34 -13
- package/src/uploadInput/UploadInput.spec.tsx +21 -23
- package/src/uploadInput/uploadItem/UploadItem.tsx +1 -3
- package/src/withDisplayFormat/WithDisplayFormat.spec.js +63 -32
- package/src/withDisplayFormat/WithDisplayFormat.tsx +4 -6
- package/src/dateLookup/DateLookup.keyboardEvents.spec.js +0 -180
- package/src/processIndicator/ProcessIndicator.js +0 -117
- package/src/processIndicator/ProcessIndicator.spec.js +0 -101
- /package/src/processIndicator/{index.js → index.ts} +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { mount } from 'enzyme';
|
|
2
2
|
import ReactDOM from 'react-dom';
|
|
3
3
|
|
|
4
|
-
import SnackbarAppendingToBody, { Snackbar
|
|
4
|
+
import SnackbarAppendingToBody, { Snackbar } from './Snackbar';
|
|
5
5
|
import { SnackbarConsumer } from './SnackbarContext';
|
|
6
6
|
import SnackbarProvider from './SnackbarProvider';
|
|
7
|
+
import { act } from 'react';
|
|
7
8
|
|
|
8
9
|
describe('Snackbar', () => {
|
|
9
10
|
const timeout = 1000;
|
|
@@ -78,7 +79,12 @@ describe('Snackbar', () => {
|
|
|
78
79
|
expect(snackbar().text()).toContain(props.text);
|
|
79
80
|
expect(snackbar()).toHaveLength(1);
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
await act(async () => {
|
|
83
|
+
await jest.runOnlyPendingTimersAsync();
|
|
84
|
+
});
|
|
85
|
+
await act(async () => {
|
|
86
|
+
await jest.runOnlyPendingTimersAsync();
|
|
87
|
+
});
|
|
82
88
|
|
|
83
89
|
expect(snackbar().text()).not.toContain(props.text);
|
|
84
90
|
});
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { action } from '@storybook/addon-actions';
|
|
2
2
|
import { number } from '@storybook/addon-knobs';
|
|
3
3
|
import { StoryContext } from '@storybook/react';
|
|
4
|
-
import { userEvent, within } from '@storybook/test';
|
|
4
|
+
import { userEvent, waitFor, within } from '@storybook/test';
|
|
5
5
|
import { Mobile, Theme, Switch, Bulb, Info, Coins } from '@transferwise/icons';
|
|
6
6
|
|
|
7
7
|
import Button from '../button';
|
|
8
8
|
import CheckboxOption from '../checkboxOption/CheckboxOption';
|
|
9
|
+
import { wait } from '../test-utils/wait';
|
|
9
10
|
|
|
10
11
|
import { Snackbar } from './Snackbar';
|
|
11
12
|
import { SnackbarConsumer } from './SnackbarContext';
|
|
@@ -122,6 +123,7 @@ export const basic = () => {
|
|
|
122
123
|
};
|
|
123
124
|
|
|
124
125
|
basic.play = async ({ canvasElement }: StoryContext) => {
|
|
126
|
+
await wait(0); // TODO: Remove
|
|
125
127
|
const canvas = within(canvasElement);
|
|
126
128
|
await userEvent.click(canvas.getByRole('button'));
|
|
127
129
|
};
|
|
@@ -7,7 +7,7 @@ import { Theme, ThemeDark, ThemeLight } from '../common';
|
|
|
7
7
|
import { DirectionContext } from '../provider/direction';
|
|
8
8
|
import withNextPortal from '../withNextPortal/withNextPortal';
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
const CSS_TRANSITION_DURATION = 400;
|
|
11
11
|
|
|
12
12
|
export interface SnackbarProps {
|
|
13
13
|
action?: {
|
package/src/tabs/Tabs.spec.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Spring } from '@react-spring/web';
|
|
2
2
|
import { mount } from 'enzyme';
|
|
3
|
+
import { act } from 'react';
|
|
3
4
|
|
|
4
5
|
import { Size, Width } from '../common';
|
|
5
6
|
|
|
@@ -8,8 +9,6 @@ import TabPanel from './TabPanel';
|
|
|
8
9
|
import Tabs from './Tabs';
|
|
9
10
|
import { getElasticDragDifference } from './utils';
|
|
10
11
|
|
|
11
|
-
jest.useFakeTimers();
|
|
12
|
-
|
|
13
12
|
jest.mock('@react-spring/web', () => ({
|
|
14
13
|
animated: {
|
|
15
14
|
div: ({ children, style }) => (
|
|
@@ -37,6 +36,7 @@ describe('Tabs', () => {
|
|
|
37
36
|
let props;
|
|
38
37
|
|
|
39
38
|
beforeEach(() => {
|
|
39
|
+
jest.useFakeTimers();
|
|
40
40
|
props = {
|
|
41
41
|
animatePanelsOnClick: true,
|
|
42
42
|
tabs: generateTabs(),
|
|
@@ -57,6 +57,11 @@ describe('Tabs', () => {
|
|
|
57
57
|
jest.clearAllMocks();
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
afterEach(async () => {
|
|
61
|
+
await jest.runOnlyPendingTimersAsync();
|
|
62
|
+
jest.useRealTimers();
|
|
63
|
+
});
|
|
64
|
+
|
|
60
65
|
it('renders with right props', () => {
|
|
61
66
|
expect(component.find(Tabs)).toHaveLength(1);
|
|
62
67
|
expect(component.find(Tabs).props()).toStrictEqual({ ...props });
|
|
@@ -172,11 +177,13 @@ describe('Tabs', () => {
|
|
|
172
177
|
expect(component.find(Tab)).toHaveLength(props.tabs.length);
|
|
173
178
|
});
|
|
174
179
|
|
|
175
|
-
it('does not animate when a tab before the selected tab goes from disabled to enabled', () => {
|
|
180
|
+
it('does not animate when a tab before the selected tab goes from disabled to enabled', async () => {
|
|
176
181
|
component.setProps({ selected: 2 });
|
|
177
182
|
expect(component.state('isAnimating')).toBe(true);
|
|
178
183
|
|
|
179
|
-
|
|
184
|
+
await act(async () => {
|
|
185
|
+
await jest.runOnlyPendingTimersAsync();
|
|
186
|
+
});
|
|
180
187
|
expect(component.state('isAnimating')).toBe(false);
|
|
181
188
|
|
|
182
189
|
component.setProps({ tabs: generateTabs([false, false, false]) });
|
|
@@ -248,14 +255,16 @@ describe('Tabs', () => {
|
|
|
248
255
|
${5} | ${Width.AUTO} | ${'240px'} | ${'-900px'}
|
|
249
256
|
`(
|
|
250
257
|
'when selecting tab number %selected when headerWidth is set to %headerWidth',
|
|
251
|
-
({ selected, headerWidth, lineTranslateX, sliderTranslateX }) => {
|
|
258
|
+
async ({ selected, headerWidth, lineTranslateX, sliderTranslateX }) => {
|
|
252
259
|
component.setProps({ headerWidth });
|
|
253
260
|
|
|
254
261
|
const getLineStyles = () => getComputedStyle(component.find('.tabs__line').getDOMNode());
|
|
255
262
|
const getSliderStyles = () =>
|
|
256
263
|
getComputedStyle(component.find('.tabs__slider').getDOMNode());
|
|
257
264
|
|
|
258
|
-
|
|
265
|
+
await act(async () => {
|
|
266
|
+
component.setProps({ selected });
|
|
267
|
+
});
|
|
259
268
|
|
|
260
269
|
expect(component.state('isAnimating')).toBe(true);
|
|
261
270
|
expect(getLineStyles().getPropertyValue('transform')).toBe(`translateX(${lineTranslateX})`);
|
|
@@ -263,7 +272,9 @@ describe('Tabs', () => {
|
|
|
263
272
|
`translateX(${sliderTranslateX})`,
|
|
264
273
|
);
|
|
265
274
|
|
|
266
|
-
|
|
275
|
+
await act(async () => {
|
|
276
|
+
await jest.runOnlyPendingTimersAsync();
|
|
277
|
+
});
|
|
267
278
|
|
|
268
279
|
expect(component.state('isAnimating')).toBe(false);
|
|
269
280
|
expect(getLineStyles().getPropertyValue('transform')).toBe(`translateX(${lineTranslateX})`);
|
|
@@ -276,15 +287,11 @@ describe('Tabs', () => {
|
|
|
276
287
|
|
|
277
288
|
const getLineStyles = () => getComputedStyle(component.find('.tabs__line').getDOMNode());
|
|
278
289
|
|
|
279
|
-
triggerSpringOnRest();
|
|
280
|
-
|
|
281
290
|
expect(getLineStyles().getPropertyValue('transform')).toBe(`translateX(60px)`);
|
|
282
291
|
expect(component.state('fullWidthTabs')).toBeFalsy();
|
|
283
292
|
|
|
284
293
|
component.setProps({ tabs: generateTabs([false, true, false, false, false, false, false]) });
|
|
285
294
|
|
|
286
|
-
triggerSpringOnRest();
|
|
287
|
-
|
|
288
295
|
expect(getLineStyles().getPropertyValue('transform')).toBe(`translateX(100%)`);
|
|
289
296
|
expect(component.state('fullWidthTabs')).toBeTruthy();
|
|
290
297
|
});
|
|
@@ -319,41 +326,57 @@ describe('Tabs', () => {
|
|
|
319
326
|
});
|
|
320
327
|
});
|
|
321
328
|
|
|
322
|
-
it('displays all tabs when animating', () => {
|
|
323
|
-
|
|
329
|
+
it('displays all tabs when animating', async () => {
|
|
330
|
+
await act(async () => {
|
|
331
|
+
component.setState({ isAnimating: true });
|
|
332
|
+
});
|
|
324
333
|
|
|
325
334
|
component.find(TabPanel).forEach((tab) => {
|
|
326
335
|
expect(tab.prop('style').display).toBe('block');
|
|
327
336
|
});
|
|
328
337
|
});
|
|
329
338
|
|
|
330
|
-
it('displays all tabs when swiping', () => {
|
|
331
|
-
|
|
339
|
+
it('displays all tabs when swiping', async () => {
|
|
340
|
+
await act(async () => {
|
|
341
|
+
component.setState({ isSwiping: true });
|
|
342
|
+
});
|
|
332
343
|
|
|
333
344
|
component.find(TabPanel).forEach((tab) => {
|
|
334
345
|
expect(tab.prop('style').display).toBe('block');
|
|
335
346
|
});
|
|
336
347
|
});
|
|
337
348
|
|
|
338
|
-
it('has `overflow: hidden` on the parent when animating/swiping', () => {
|
|
339
|
-
|
|
349
|
+
it('has `overflow: hidden` on the parent when animating/swiping', async () => {
|
|
350
|
+
await act(async () => {
|
|
351
|
+
component.setState({ isSwiping: false, isAnimating: false });
|
|
352
|
+
});
|
|
340
353
|
expect(getPanelContainerOverflow(component)).toBe('visible');
|
|
341
354
|
|
|
342
|
-
|
|
355
|
+
await act(async () => {
|
|
356
|
+
component.setState({ isSwiping: true });
|
|
357
|
+
});
|
|
343
358
|
expect(getPanelContainerOverflow(component)).toBe('hidden');
|
|
344
359
|
|
|
345
|
-
|
|
360
|
+
await act(async () => {
|
|
361
|
+
component.setState({ isSwiping: false, isAnimating: true });
|
|
362
|
+
});
|
|
346
363
|
expect(getPanelContainerOverflow(component)).toBe('hidden');
|
|
347
364
|
});
|
|
348
365
|
|
|
349
|
-
it('sets the panel width according to if animating/swiping', () => {
|
|
350
|
-
|
|
366
|
+
it('sets the panel width according to if animating/swiping', async () => {
|
|
367
|
+
await act(async () => {
|
|
368
|
+
component.setState({ isSwiping: false, isAnimating: false });
|
|
369
|
+
});
|
|
351
370
|
expect(getPanelWidth(component)).toBe('100%');
|
|
352
371
|
|
|
353
|
-
|
|
372
|
+
await act(async () => {
|
|
373
|
+
component.setState({ isSwiping: true });
|
|
374
|
+
});
|
|
354
375
|
expect(getPanelWidth(component)).toBe('300px');
|
|
355
376
|
|
|
356
|
-
|
|
377
|
+
await act(async () => {
|
|
378
|
+
component.setState({ isSwiping: false, isAnimating: true });
|
|
379
|
+
});
|
|
357
380
|
expect(getPanelWidth(component)).toBe('300px');
|
|
358
381
|
});
|
|
359
382
|
});
|
|
@@ -373,10 +396,6 @@ function createClientXY(x, y) {
|
|
|
373
396
|
return { clientX: x, clientY: y };
|
|
374
397
|
}
|
|
375
398
|
|
|
376
|
-
function triggerSpringOnRest() {
|
|
377
|
-
jest.runAllTimers();
|
|
378
|
-
}
|
|
379
|
-
|
|
380
399
|
function getPanelContainerOverflow(component) {
|
|
381
400
|
return component.find('.tabs__panel-container').prop('style').overflow;
|
|
382
401
|
}
|
package/src/test-utils/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { render } from '@testing-library/react';
|
|
2
|
-
import { renderHook } from '@testing-library/react-hooks';
|
|
1
|
+
import { render, renderHook } from '@testing-library/react';
|
|
3
2
|
import userEvent from '@testing-library/user-event';
|
|
4
3
|
|
|
5
4
|
import { Provider } from '..';
|
|
@@ -30,9 +29,8 @@ function customRenderHook(callback, { locale = DEFAULT_LOCALE, messages = en } =
|
|
|
30
29
|
});
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
export * from './window-mock';
|
|
34
|
-
export * from './story-config';
|
|
35
|
-
export * from './fake-data';
|
|
36
32
|
export * from '@testing-library/react';
|
|
37
|
-
export
|
|
38
|
-
export
|
|
33
|
+
export * from './fake-data';
|
|
34
|
+
export * from './story-config';
|
|
35
|
+
export * from './window-mock';
|
|
36
|
+
export { customRender as render, customRenderHook as renderHook, userEvent };
|
|
@@ -1,17 +1,23 @@
|
|
|
1
|
-
const Adapter = require('@
|
|
1
|
+
const { default: Adapter } = require('@cfaester/enzyme-adapter-react-18');
|
|
2
2
|
const Enzyme = require('enzyme');
|
|
3
|
+
const util = require('node:util');
|
|
3
4
|
|
|
4
5
|
global.fetch = require('jest-fetch-mock');
|
|
6
|
+
|
|
5
7
|
Enzyme.configure({ adapter: new Adapter() });
|
|
6
8
|
|
|
7
9
|
global.requestAnimationFrame = (callback) => callback();
|
|
8
10
|
|
|
11
|
+
Object.defineProperty(global, 'TextEncoder', {
|
|
12
|
+
value: util.TextEncoder,
|
|
13
|
+
});
|
|
14
|
+
|
|
9
15
|
// https://github.com/esphen/jest-prop-type-error/blob/master/index.js
|
|
10
16
|
// This mock will make tests fail when props error occurs.
|
|
11
17
|
const { error, warn } = console;
|
|
12
18
|
// eslint-disable-next-line no-console
|
|
13
19
|
console.error = (message, ...args) => {
|
|
14
|
-
if (/(Invalid prop|Failed prop type)/
|
|
20
|
+
if (/(Invalid prop|Failed prop type)/i.test(message)) {
|
|
15
21
|
throw new Error(message);
|
|
16
22
|
}
|
|
17
23
|
|
|
@@ -20,7 +26,7 @@ console.error = (message, ...args) => {
|
|
|
20
26
|
|
|
21
27
|
// eslint-disable-next-line no-console
|
|
22
28
|
console.warn = (message, ...args) => {
|
|
23
|
-
if (/(Call to useTheme outside a ThemeProvider)/
|
|
29
|
+
if (/(Call to useTheme outside a ThemeProvider)/i.test(message)) {
|
|
24
30
|
return;
|
|
25
31
|
}
|
|
26
32
|
|
package/src/tooltip/Tooltip.tsx
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
-
import { useId } from '@radix-ui/react-id';
|
|
3
2
|
import classNames from 'classnames';
|
|
4
3
|
import {
|
|
4
|
+
PropsWithChildren,
|
|
5
|
+
ReactElement,
|
|
6
|
+
ReactNode,
|
|
5
7
|
cloneElement,
|
|
8
|
+
useEffect,
|
|
9
|
+
useId,
|
|
6
10
|
useRef,
|
|
7
11
|
useState,
|
|
8
|
-
useEffect,
|
|
9
|
-
ReactNode,
|
|
10
|
-
ReactElement,
|
|
11
|
-
PropsWithChildren,
|
|
12
12
|
} from 'react';
|
|
13
13
|
import { usePopper } from 'react-popper';
|
|
14
14
|
|
|
@@ -75,49 +75,47 @@ const Tooltip = ({
|
|
|
75
75
|
}, [open]);
|
|
76
76
|
|
|
77
77
|
return (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
<span
|
|
79
|
+
ref={anchorReference}
|
|
80
|
+
className="tw-tooltip-container"
|
|
81
|
+
onMouseOver={() => setOpen(true)}
|
|
82
|
+
onFocus={() => setOpen(true)}
|
|
83
|
+
onMouseOut={() => setOpen(false)}
|
|
84
|
+
onBlur={() => setOpen(false)}
|
|
85
|
+
>
|
|
86
|
+
{children
|
|
87
|
+
? cloneElement(children as ReactElement, {
|
|
88
|
+
'aria-describedby': `${tooltipId}-tooltip`,
|
|
89
|
+
})
|
|
90
|
+
: null}
|
|
91
|
+
<div
|
|
92
|
+
// @ts-expect-error
|
|
93
|
+
ref={setPopperElement}
|
|
94
|
+
className={classNames(
|
|
95
|
+
'np-tooltip',
|
|
96
|
+
'np-panel',
|
|
97
|
+
open ? `np-panel--open np-tooltip--open` : null,
|
|
98
|
+
className,
|
|
99
|
+
)}
|
|
100
|
+
// eslint-disable-next-line react/forbid-dom-props
|
|
101
|
+
style={{ ...styles.popper }}
|
|
102
|
+
{...attributes.popper}
|
|
103
|
+
aria-hidden={!open}
|
|
104
|
+
role="tooltip"
|
|
105
|
+
id={`${tooltipId}-tooltip`}
|
|
86
106
|
>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
'np-tooltip',
|
|
97
|
-
'np-panel',
|
|
98
|
-
open ? `np-panel--open np-tooltip--open` : null,
|
|
99
|
-
className,
|
|
100
|
-
)}
|
|
101
|
-
// eslint-disable-next-line react/forbid-dom-props
|
|
102
|
-
style={{ ...styles.popper }}
|
|
103
|
-
{...attributes.popper}
|
|
104
|
-
aria-hidden={!open}
|
|
105
|
-
role="tooltip"
|
|
106
|
-
id={`${tooltipId}-tooltip`}
|
|
107
|
-
>
|
|
108
|
-
<div className="np-panel__content tooltip-inner">
|
|
109
|
-
{label}
|
|
110
|
-
<div
|
|
111
|
-
// @ts-expect-error
|
|
112
|
-
ref={setArrowElement}
|
|
113
|
-
className={classNames('np-panel__arrow')}
|
|
114
|
-
// eslint-disable-next-line react/forbid-dom-props
|
|
115
|
-
style={styles.arrow}
|
|
116
|
-
/>
|
|
117
|
-
</div>
|
|
107
|
+
<div className="np-panel__content tooltip-inner">
|
|
108
|
+
{label}
|
|
109
|
+
<div
|
|
110
|
+
// @ts-expect-error
|
|
111
|
+
ref={setArrowElement}
|
|
112
|
+
className={classNames('np-panel__arrow')}
|
|
113
|
+
// eslint-disable-next-line react/forbid-dom-props
|
|
114
|
+
style={styles.arrow}
|
|
115
|
+
/>
|
|
118
116
|
</div>
|
|
119
|
-
</
|
|
120
|
-
|
|
117
|
+
</div>
|
|
118
|
+
</span>
|
|
121
119
|
);
|
|
122
120
|
};
|
|
123
121
|
|
|
@@ -6,14 +6,14 @@ exports[`Tooltip Component renders an empty list when no items are passed 1`] =
|
|
|
6
6
|
class="tw-tooltip-container"
|
|
7
7
|
>
|
|
8
8
|
<span
|
|
9
|
-
aria-describedby="
|
|
9
|
+
aria-describedby=":r0:-tooltip"
|
|
10
10
|
>
|
|
11
11
|
Hover me
|
|
12
12
|
</span>
|
|
13
13
|
<div
|
|
14
14
|
aria-hidden="true"
|
|
15
15
|
class="np-tooltip np-panel"
|
|
16
|
-
id="
|
|
16
|
+
id=":r0:-tooltip"
|
|
17
17
|
role="tooltip"
|
|
18
18
|
style="position: absolute; left: 0px; top: 0px;"
|
|
19
19
|
>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { shallow, mount } from 'enzyme';
|
|
2
|
+
import { act } from 'react';
|
|
2
3
|
|
|
3
4
|
import { ANIMATION_DURATION_IN_MS } from '../processIndicator';
|
|
4
5
|
|
|
@@ -6,7 +7,6 @@ import { CompleteStep, UploadImageStep, MediaUploadStep, ProcessingStep } from '
|
|
|
6
7
|
|
|
7
8
|
import Upload from '.';
|
|
8
9
|
|
|
9
|
-
jest.useFakeTimers();
|
|
10
10
|
jest.mock('./utils/postData', () => ({
|
|
11
11
|
postData: async () => 'ServerResponse',
|
|
12
12
|
}));
|
|
@@ -15,12 +15,12 @@ jest.mock('./utils/asyncFileRead');
|
|
|
15
15
|
const { asyncFileRead } = require('./utils/asyncFileRead');
|
|
16
16
|
|
|
17
17
|
const defaultLocale = 'en-GB';
|
|
18
|
-
const formatMessage = (id) =>
|
|
18
|
+
const formatMessage = (id) => String(id);
|
|
19
19
|
jest.mock('react-intl', () => ({
|
|
20
20
|
injectIntl: (Component) =>
|
|
21
21
|
function (props) {
|
|
22
22
|
return (
|
|
23
|
-
<Component {...props} intl={{ locale: defaultLocale, formatMessage: (id) =>
|
|
23
|
+
<Component {...props} intl={{ locale: defaultLocale, formatMessage: (id) => String(id) }} />
|
|
24
24
|
);
|
|
25
25
|
},
|
|
26
26
|
useIntl: () => ({ formatMessage }),
|
|
@@ -87,11 +87,14 @@ const COMPLETED_STEP_PROPS = {
|
|
|
87
87
|
describe('Upload', () => {
|
|
88
88
|
let component;
|
|
89
89
|
beforeEach(() => {
|
|
90
|
+
jest.useFakeTimers();
|
|
90
91
|
component = shallow(<Upload {...props} />).dive();
|
|
91
92
|
asyncFileRead.mockImplementation(async () => 'a value');
|
|
92
93
|
});
|
|
93
94
|
|
|
94
|
-
afterEach(() => {
|
|
95
|
+
afterEach(async () => {
|
|
96
|
+
await jest.runOnlyPendingTimersAsync();
|
|
97
|
+
jest.useRealTimers();
|
|
95
98
|
jest.clearAllMocks();
|
|
96
99
|
jest.clearAllTimers();
|
|
97
100
|
});
|
|
@@ -213,6 +216,14 @@ describe('Upload', () => {
|
|
|
213
216
|
});
|
|
214
217
|
|
|
215
218
|
describe('when file is processed', () => {
|
|
219
|
+
const waitForUpload = async () => {
|
|
220
|
+
for (let i = 0; i < 4; i += 1) {
|
|
221
|
+
await act(async () => {
|
|
222
|
+
await jest.runOnlyPendingTimersAsync();
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
216
227
|
it('step changes from UploadImageStep to CompleteStep', async () => {
|
|
217
228
|
component = mount(<Upload {...props} />);
|
|
218
229
|
const upload = component.children();
|
|
@@ -220,8 +231,10 @@ describe('Upload', () => {
|
|
|
220
231
|
expect(upload.find(ProcessingStep)).toHaveLength(0);
|
|
221
232
|
expect(upload.find(CompleteStep)).toHaveLength(0);
|
|
222
233
|
|
|
223
|
-
await
|
|
224
|
-
|
|
234
|
+
await act(async () => {
|
|
235
|
+
await upload.instance().fileDropped(TEST_FILE);
|
|
236
|
+
});
|
|
237
|
+
await waitForUpload();
|
|
225
238
|
component.update();
|
|
226
239
|
|
|
227
240
|
expect(component.find(UploadImageStep)).toHaveLength(0);
|
|
@@ -236,8 +249,10 @@ describe('Upload', () => {
|
|
|
236
249
|
throw 'An error';
|
|
237
250
|
});
|
|
238
251
|
|
|
239
|
-
await
|
|
240
|
-
|
|
252
|
+
await act(async () => {
|
|
253
|
+
await upload.instance().fileDropped(TEST_FILE);
|
|
254
|
+
});
|
|
255
|
+
await waitForUpload();
|
|
241
256
|
component.update();
|
|
242
257
|
|
|
243
258
|
expect(component.find(CompleteStep).props()).toStrictEqual({
|
|
@@ -252,8 +267,10 @@ describe('Upload', () => {
|
|
|
252
267
|
it('onSuccess is called with response when httpOptions are provided', async () => {
|
|
253
268
|
component = mount(<Upload {...props} httpOptions={{ url: 'a-url' }} />).children();
|
|
254
269
|
|
|
255
|
-
await
|
|
256
|
-
|
|
270
|
+
await act(async () => {
|
|
271
|
+
await component.instance().fileDropped(TEST_FILE);
|
|
272
|
+
});
|
|
273
|
+
await waitForUpload();
|
|
257
274
|
|
|
258
275
|
expect(props.onSuccess).toHaveBeenCalledWith('ServerResponse', TEST_FILE.name);
|
|
259
276
|
});
|
|
@@ -261,11 +278,15 @@ describe('Upload', () => {
|
|
|
261
278
|
it('wont process new file while current process is in progress', async () => {
|
|
262
279
|
component = mount(<Upload {...props} httpOptions={{ url: 'a-url' }} />).children();
|
|
263
280
|
|
|
264
|
-
await
|
|
265
|
-
|
|
266
|
-
|
|
281
|
+
await act(async () => {
|
|
282
|
+
await component.instance().fileDropped(TEST_FILE);
|
|
283
|
+
});
|
|
284
|
+
const result = await act(async () => component.instance().fileDropped(TEST_FILE));
|
|
267
285
|
|
|
268
286
|
expect(result).toBe(false);
|
|
287
|
+
|
|
288
|
+
await waitForUpload();
|
|
289
|
+
|
|
269
290
|
expect(props.onSuccess).toHaveBeenCalledTimes(1);
|
|
270
291
|
});
|
|
271
292
|
});
|
|
@@ -7,8 +7,7 @@ import { mockMatchMedia, render, screen, waitFor, waitForElementToBeRemoved } fr
|
|
|
7
7
|
import UploadInput, { UploadInputProps } from './UploadInput';
|
|
8
8
|
import { TEST_IDS as UPLOAD_BUTTON_TEST_IDS } from './uploadButton/UploadButton';
|
|
9
9
|
import { TEST_IDS as UPLOAD_ITEM_TEST_IDS } from './uploadItem/UploadItem';
|
|
10
|
-
|
|
11
|
-
const spiedDateObject = jest.spyOn(global, 'Date');
|
|
10
|
+
import { act } from 'react';
|
|
12
11
|
|
|
13
12
|
mockMatchMedia();
|
|
14
13
|
|
|
@@ -42,43 +41,35 @@ describe('UploadInput', () => {
|
|
|
42
41
|
render(<UploadInput {...customProps} />);
|
|
43
42
|
|
|
44
43
|
beforeEach(() => {
|
|
45
|
-
|
|
46
|
-
getTime: jest.fn().mockReturnValue(Math.random()),
|
|
47
|
-
}));
|
|
44
|
+
jest.useFakeTimers();
|
|
48
45
|
});
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
afterEach(async () => {
|
|
48
|
+
await jest.runOnlyPendingTimersAsync();
|
|
49
|
+
jest.useRealTimers();
|
|
52
50
|
});
|
|
53
51
|
|
|
54
52
|
describe('single file upload', () => {
|
|
55
53
|
it('should trigger onUploadFiles & onFilesChange with a single FormData entry containing `file` field', async () => {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
(spiedDateObject as jest.Mock).mockImplementation(() => ({
|
|
59
|
-
getTime: jest
|
|
60
|
-
.fn()
|
|
61
|
-
.mockReturnValueOnce(mockTimeStampValue1)
|
|
62
|
-
.mockReturnValueOnce(mockTimeStampValue2),
|
|
63
|
-
}));
|
|
54
|
+
const date = Date.now();
|
|
55
|
+
jest.setSystemTime(date);
|
|
64
56
|
|
|
65
57
|
const onFilesChange = jest.fn();
|
|
66
58
|
renderComponent({ ...props, onFilesChange });
|
|
67
59
|
|
|
68
60
|
const input = screen.getByTestId(UPLOAD_BUTTON_TEST_IDS.uploadInput);
|
|
69
61
|
userEvent.upload(input, [pngFile, jpgFile]);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
expect(props.onUploadFile).toHaveBeenCalledTimes(1);
|
|
62
|
+
await act(async () => {
|
|
63
|
+
await jest.runOnlyPendingTimersAsync();
|
|
73
64
|
});
|
|
74
65
|
|
|
66
|
+
expect(props.onUploadFile).toHaveBeenCalledTimes(1);
|
|
75
67
|
expect(onFilesChange).toHaveBeenCalledTimes(2);
|
|
76
68
|
expect(onFilesChange).toHaveBeenNthCalledWith(1, [
|
|
77
69
|
{
|
|
78
70
|
filename: 'foo.png',
|
|
79
|
-
id: `foo.png_3_${
|
|
71
|
+
id: `foo.png_3_${date}`,
|
|
80
72
|
status: 'pending',
|
|
81
|
-
url: undefined,
|
|
82
73
|
},
|
|
83
74
|
]);
|
|
84
75
|
|
|
@@ -150,6 +141,10 @@ describe('UploadInput', () => {
|
|
|
150
141
|
|
|
151
142
|
const fileToDelete = screen.getAllByTestId(UPLOAD_ITEM_TEST_IDS.uploadItem)[0];
|
|
152
143
|
within(fileToDelete).getByLabelText('Remove file', { exact: false }).click();
|
|
144
|
+
await act(async () => {
|
|
145
|
+
await jest.runOnlyPendingTimersAsync();
|
|
146
|
+
});
|
|
147
|
+
|
|
153
148
|
screen.getByText('Remove').click();
|
|
154
149
|
|
|
155
150
|
await waitForElementToBeRemoved(fileToDelete);
|
|
@@ -179,7 +174,7 @@ describe('UploadInput', () => {
|
|
|
179
174
|
]);
|
|
180
175
|
});
|
|
181
176
|
|
|
182
|
-
it('should delete file with failed state without modal confirmation', () => {
|
|
177
|
+
it('should delete file with failed state without modal confirmation', async () => {
|
|
183
178
|
const files = [
|
|
184
179
|
{
|
|
185
180
|
id: 1,
|
|
@@ -197,6 +192,9 @@ describe('UploadInput', () => {
|
|
|
197
192
|
|
|
198
193
|
const fileToDelete = screen.getAllByTestId(UPLOAD_ITEM_TEST_IDS.uploadItem)[0];
|
|
199
194
|
within(fileToDelete).getByLabelText('Remove file', { exact: false }).click();
|
|
195
|
+
await act(async () => {
|
|
196
|
+
await jest.runOnlyPendingTimersAsync();
|
|
197
|
+
});
|
|
200
198
|
|
|
201
199
|
expect(fileToDelete).not.toBeInTheDocument();
|
|
202
200
|
|
|
@@ -240,7 +238,7 @@ describe('UploadInput', () => {
|
|
|
240
238
|
onUploadFile: mockOnUploadFileFn,
|
|
241
239
|
});
|
|
242
240
|
|
|
243
|
-
mockOnUploadFileFn.mockImplementation((formData: FormData) => {
|
|
241
|
+
mockOnUploadFileFn.mockImplementation(async (formData: FormData) => {
|
|
244
242
|
const file = formData.get('file');
|
|
245
243
|
return Promise.resolve({ file, id: Math.random() });
|
|
246
244
|
});
|
|
@@ -268,7 +266,7 @@ describe('UploadInput', () => {
|
|
|
268
266
|
onUploadFile: mockOnUploadFileFn,
|
|
269
267
|
});
|
|
270
268
|
|
|
271
|
-
mockOnUploadFileFn.mockImplementation((formData: FormData) => {
|
|
269
|
+
mockOnUploadFileFn.mockImplementation(async (formData: FormData) => {
|
|
272
270
|
const file = formData.get('file');
|
|
273
271
|
return Promise.resolve({ file, id: Math.random() });
|
|
274
272
|
});
|
|
@@ -83,9 +83,7 @@ const UploadItem = ({
|
|
|
83
83
|
};
|
|
84
84
|
|
|
85
85
|
const getErrorMessage = () =>
|
|
86
|
-
|
|
87
|
-
error ||
|
|
88
|
-
formatMessage(MESSAGES.uploadingFailed);
|
|
86
|
+
typeof error === 'object' ? error.message : error || formatMessage(MESSAGES.uploadingFailed);
|
|
89
87
|
|
|
90
88
|
const getDescription = () => {
|
|
91
89
|
if (error || status === Status.FAILED) {
|