@transferwise/components 0.0.0-experimental-47ae02a → 0.0.0-experimental-da6dbbf

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 (89) hide show
  1. package/build/index.js +940 -642
  2. package/build/index.js.map +1 -1
  3. package/build/index.mjs +930 -633
  4. package/build/index.mjs.map +1 -1
  5. package/build/main.css +135 -0
  6. package/build/styles/carousel/Carousel.css +135 -0
  7. package/build/styles/main.css +135 -0
  8. package/build/types/accordion/AccordionItem/AccordionItem.d.ts.map +1 -1
  9. package/build/types/carousel/Carousel.d.ts +26 -0
  10. package/build/types/carousel/Carousel.d.ts.map +1 -0
  11. package/build/types/carousel/index.d.ts +3 -0
  12. package/build/types/carousel/index.d.ts.map +1 -0
  13. package/build/types/common/card/Card.d.ts +2 -2
  14. package/build/types/common/card/Card.d.ts.map +1 -1
  15. package/build/types/common/hooks/useMedia.d.ts.map +1 -1
  16. package/build/types/common/panel/Panel.d.ts.map +1 -1
  17. package/build/types/common/responsivePanel/ResponsivePanel.d.ts.map +1 -1
  18. package/build/types/dimmer/Dimmer.d.ts +11 -1
  19. package/build/types/dimmer/Dimmer.d.ts.map +1 -1
  20. package/build/types/drawer/Drawer.d.ts +4 -4
  21. package/build/types/index.d.ts +4 -3
  22. package/build/types/index.d.ts.map +1 -1
  23. package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts +1 -2
  24. package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts.map +1 -1
  25. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  26. package/build/types/modal/Modal.d.ts.map +1 -1
  27. package/build/types/processIndicator/ProcessIndicator.d.ts +19 -36
  28. package/build/types/processIndicator/ProcessIndicator.d.ts.map +1 -1
  29. package/build/types/processIndicator/index.d.ts +2 -2
  30. package/build/types/processIndicator/index.d.ts.map +1 -1
  31. package/build/types/promoCard/PromoCard.d.ts +16 -5
  32. package/build/types/promoCard/PromoCard.d.ts.map +1 -1
  33. package/build/types/select/searchBox/SearchBox.d.ts +1 -1
  34. package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts +1 -2
  35. package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts.map +1 -1
  36. package/build/types/tooltip/Tooltip.d.ts +1 -1
  37. package/build/types/tooltip/Tooltip.d.ts.map +1 -1
  38. package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
  39. package/build/types/withDisplayFormat/WithDisplayFormat.d.ts +14 -14
  40. package/build/types/withDisplayFormat/WithDisplayFormat.d.ts.map +1 -1
  41. package/package.json +11 -7
  42. package/src/accordion/AccordionItem/AccordionItem.tsx +4 -2
  43. package/src/avatarWrapper/AvatarWrapper.story.tsx +3 -1
  44. package/src/button/Button.tsx +1 -1
  45. package/src/carousel/Carousel.css +135 -0
  46. package/src/carousel/Carousel.less +133 -0
  47. package/src/carousel/Carousel.spec.tsx +221 -0
  48. package/src/carousel/Carousel.story.tsx +63 -0
  49. package/src/carousel/Carousel.tsx +345 -0
  50. package/src/carousel/index.ts +3 -0
  51. package/src/common/card/Card.tsx +51 -43
  52. package/src/common/hooks/useConditionalListener/useConditionalListener.spec.js +1 -1
  53. package/src/common/hooks/useHasIntersected/useHasIntersected.spec.js +3 -3
  54. package/src/common/hooks/useMedia.spec.ts +1 -1
  55. package/src/common/hooks/useMedia.ts +2 -1
  56. package/src/common/panel/Panel.tsx +92 -90
  57. package/src/common/responsivePanel/ResponsivePanel.tsx +38 -34
  58. package/src/dateLookup/DateLookup.keyboardEvents.spec.js +180 -0
  59. package/src/dateLookup/DateLookup.rtl.spec.tsx +5 -181
  60. package/src/dateLookup/DateLookup.testingLibrary.spec.js +124 -171
  61. package/src/drawer/Drawer.js +3 -3
  62. package/src/field/Field.tsx +3 -3
  63. package/src/index.ts +4 -3
  64. package/src/inputWithDisplayFormat/InputWithDisplayFormat.tsx +1 -2
  65. package/src/inputs/SelectInput.story.tsx +0 -1
  66. package/src/inputs/SelectInput.tsx +2 -10
  67. package/src/main.css +135 -0
  68. package/src/main.less +1 -0
  69. package/src/modal/Modal.tsx +2 -1
  70. package/src/processIndicator/ProcessIndicator.js +117 -0
  71. package/src/processIndicator/ProcessIndicator.spec.js +101 -0
  72. package/src/promoCard/PromoCard.story.tsx +2 -2
  73. package/src/promoCard/PromoCard.tsx +31 -9
  74. package/src/radio/__snapshots__/Radio.rtl.spec.tsx.snap +1 -0
  75. package/src/snackbar/Snackbar.spec.js +1 -4
  76. package/src/tabs/Tabs.spec.js +27 -46
  77. package/src/test-utils/index.js +7 -5
  78. package/src/test-utils/jest.setup.js +3 -9
  79. package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.tsx +1 -2
  80. package/src/tooltip/Tooltip.tsx +46 -44
  81. package/src/tooltip/__snapshots__/Tooltip.spec.tsx.snap +2 -2
  82. package/src/upload/Upload.spec.js +13 -34
  83. package/src/uploadInput/UploadInput.spec.tsx +23 -21
  84. package/src/uploadInput/uploadItem/UploadItem.tsx +3 -1
  85. package/src/withDisplayFormat/WithDisplayFormat.spec.js +32 -63
  86. package/src/withDisplayFormat/WithDisplayFormat.tsx +28 -28
  87. package/src/processIndicator/ProcessIndicator.rtl.spec.tsx +0 -45
  88. package/src/processIndicator/ProcessIndicator.tsx +0 -110
  89. /package/src/processIndicator/{index.ts → index.js} +0 -0
@@ -0,0 +1,117 @@
1
+ import classNames from 'classnames';
2
+ import PropTypes from 'prop-types';
3
+ import { Component } from 'react';
4
+
5
+ import { Status, Size } from '../common';
6
+
7
+ const radius = { xxs: 6, xs: 11, sm: 22, xl: 61 };
8
+ export const ANIMATION_DURATION_IN_MS = 1500;
9
+
10
+ class ProcessIndicator extends Component {
11
+ constructor(props) {
12
+ super(props);
13
+ this.state = {
14
+ status: props.status,
15
+ size: props.size,
16
+ };
17
+ this.interval = null;
18
+ this.timeout = null;
19
+ }
20
+
21
+ /**
22
+ * Create interval for animation duration (1500ms)
23
+ * Update state only at the end of every interval
24
+ * (end of animation loop) if props changed before end of animation
25
+ */
26
+ componentDidMount() {
27
+ this.interval = setInterval(() => {
28
+ const statusFromState = this.state.status;
29
+ const sizeFromState = this.state.size;
30
+
31
+ const statusFromProps = this.props.status;
32
+ const sizeFromProps = this.props.size;
33
+
34
+ if (statusFromState !== statusFromProps) {
35
+ this.setState({ status: statusFromProps }, this.runCallBack(statusFromProps));
36
+ }
37
+
38
+ if (sizeFromState !== sizeFromProps) {
39
+ this.setState({ size: sizeFromProps });
40
+ }
41
+ }, ANIMATION_DURATION_IN_MS);
42
+ }
43
+
44
+ /**
45
+ * Only trigger render if comopnent's state got
46
+ * updated from interval callback
47
+ *
48
+ * @param nextProps
49
+ * @param nextState
50
+ */
51
+ shouldComponentUpdate(nextProps, nextState) {
52
+ const isSameStatus = nextProps.status === nextState.status;
53
+ const isSameSize = nextProps.size === nextState.size;
54
+
55
+ return isSameStatus && isSameSize;
56
+ }
57
+
58
+ // Clear interval before destroying component
59
+ componentWillUnmount() {
60
+ clearInterval(this.interval);
61
+ clearTimeout(this.timeout);
62
+ }
63
+
64
+ runCallBack = (statusFromProps) => {
65
+ const { onAnimationCompleted } = this.props;
66
+ if (onAnimationCompleted) {
67
+ this.timeouts = setTimeout(() => {
68
+ onAnimationCompleted(statusFromProps);
69
+ }, ANIMATION_DURATION_IN_MS);
70
+ }
71
+ };
72
+
73
+ render() {
74
+ const { className, 'data-testid': dataTestId } = this.props;
75
+ const { size, status } = this.state;
76
+ const classes = classNames(`process process-${size}`, className, {
77
+ [`process-danger`]: status === Status.FAILED,
78
+ [`process-stopped`]: status === Status.HIDDEN,
79
+ [`process-success`]: status === Status.SUCCEEDED,
80
+ });
81
+
82
+ return (
83
+ <span className={classes} data-testid={dataTestId}>
84
+ <span className="process-icon-container">
85
+ <span className="process-icon-horizontal" />
86
+ <span className="process-icon-vertical" />
87
+ </span>
88
+ <svg xmlns="http://www.w3.org/2000/svg">
89
+ <circle
90
+ className="process-circle"
91
+ cx="50%"
92
+ cy="50%"
93
+ r={radius[this.state.size]}
94
+ fillOpacity="0.0"
95
+ />
96
+ </svg>
97
+ </span>
98
+ );
99
+ }
100
+ }
101
+ ProcessIndicator.propTypes = {
102
+ status: PropTypes.oneOf(['processing', 'failed', 'succeeded', 'hidden']),
103
+ size: PropTypes.oneOf(['xxs', 'xs', 'sm', 'xl']),
104
+ onAnimationCompleted: PropTypes.func,
105
+ className: PropTypes.string,
106
+ 'data-testid': PropTypes.string,
107
+ };
108
+
109
+ ProcessIndicator.defaultProps = {
110
+ status: Status.PROCESSING,
111
+ size: Size.SMALL,
112
+ onAnimationCompleted: null,
113
+ className: undefined,
114
+ 'data-testid': null,
115
+ };
116
+
117
+ export default ProcessIndicator;
@@ -0,0 +1,101 @@
1
+ import { mount } from 'enzyme';
2
+
3
+ import { Status, Size } from '../common';
4
+
5
+ import ProcessIndicator from '.';
6
+
7
+ describe('processIndicator', () => {
8
+ let wrapper;
9
+ const ANIMATION_DURATION = 1500;
10
+ const props = {
11
+ status: Status.PROCESSING,
12
+ size: Size.Small,
13
+ onAnimationCompleted: jest.fn(),
14
+ };
15
+
16
+ beforeEach(() => {
17
+ jest.useFakeTimers();
18
+
19
+ wrapper = mount(<ProcessIndicator {...props} />);
20
+ });
21
+
22
+ afterEach(() => {
23
+ jest.useRealTimers();
24
+ jest.clearAllMocks();
25
+ jest.clearAllTimers();
26
+ });
27
+
28
+ it('has right classes when rendered with provided props', () => {
29
+ expect(wrapper.find('.process')).toHaveLength(1);
30
+ expect(wrapper.find('.process-sm')).toHaveLength(1);
31
+
32
+ expect(wrapper.find('.process-lg')).toHaveLength(0);
33
+ expect(wrapper.find('.process-xs')).toHaveLength(0);
34
+ expect(wrapper.find('.process-danger')).toHaveLength(0);
35
+ expect(wrapper.find('.process-stopped')).toHaveLength(0);
36
+ expect(wrapper.find('.process-success')).toHaveLength(0);
37
+ });
38
+
39
+ it('updates component state with delay on props change to create smooth transition between CSS animations', () => {
40
+ expect(wrapper.state().status).toBe('processing');
41
+
42
+ wrapper.setProps({ status: 'succeeded' });
43
+ expect(wrapper.props().status).toBe('succeeded');
44
+ expect(wrapper.state().status).toBe('processing');
45
+
46
+ jest.advanceTimersByTime(ANIMATION_DURATION);
47
+ wrapper.update();
48
+
49
+ expect(wrapper.state().status).toBe('succeeded');
50
+ });
51
+
52
+ it('updates size classes with delay', () => {
53
+ expect(wrapper.find('.process-sm')).toHaveLength(1);
54
+ wrapper.setProps({ size: 'xl' });
55
+ expect(wrapper.find('.process-xl')).toHaveLength(0);
56
+
57
+ jest.advanceTimersByTime(ANIMATION_DURATION);
58
+ wrapper.update();
59
+
60
+ expect(wrapper.find('.process-xs')).toHaveLength(0);
61
+ expect(wrapper.find('.process-sm')).toHaveLength(0);
62
+ expect(wrapper.find('.process-xl')).toHaveLength(1);
63
+ });
64
+
65
+ it('properly updates status classes with delay', () => {
66
+ wrapper.setProps({ status: 'succeeded' });
67
+ expect(wrapper.find('.process-success')).toHaveLength(0);
68
+ jest.advanceTimersByTime(ANIMATION_DURATION);
69
+ wrapper.update();
70
+ expect(wrapper.find('.process-success')).toHaveLength(1);
71
+ expect(wrapper.find('.process-danger')).toHaveLength(0);
72
+ expect(wrapper.find('.process-stopped')).toHaveLength(0);
73
+
74
+ wrapper.setProps({ status: 'failed' });
75
+ expect(wrapper.find('.process-danger')).toHaveLength(0);
76
+ jest.advanceTimersByTime(ANIMATION_DURATION);
77
+ wrapper.update();
78
+ expect(wrapper.find('.process-success')).toHaveLength(0);
79
+ expect(wrapper.find('.process-danger')).toHaveLength(1);
80
+ expect(wrapper.find('.process-stopped')).toHaveLength(0);
81
+
82
+ wrapper.setProps({ status: 'hidden' });
83
+ expect(wrapper.find('.process-stopped')).toHaveLength(0);
84
+ jest.advanceTimersByTime(ANIMATION_DURATION);
85
+ wrapper.update();
86
+ expect(wrapper.find('.process-success')).toHaveLength(0);
87
+ expect(wrapper.find('.process-danger')).toHaveLength(0);
88
+ expect(wrapper.find('.process-stopped')).toHaveLength(1);
89
+ });
90
+
91
+ it('calls onAnimationCompleted with Delay', () => {
92
+ expect(props.onAnimationCompleted).not.toHaveBeenCalled();
93
+ wrapper.setProps({ status: Status.FAILED });
94
+ expect(props.onAnimationCompleted).not.toHaveBeenCalled();
95
+ jest.advanceTimersByTime(ANIMATION_DURATION * 2);
96
+ expect(props.onAnimationCompleted).toHaveBeenCalledWith(Status.FAILED);
97
+ wrapper.setProps({ status: Status.SUCCEEDED });
98
+ jest.advanceTimersByTime(ANIMATION_DURATION * 2);
99
+ expect(props.onAnimationCompleted).toHaveBeenCalledWith(Status.SUCCEEDED);
100
+ });
101
+ });
@@ -1,7 +1,7 @@
1
- import { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { StarFill } from '@transferwise/icons';
3
3
 
4
- import PromoCard, { PromoCardCheckedProps, PromoCardLinkProps } from './PromoCard';
4
+ import PromoCard, { type PromoCardCheckedProps, type PromoCardLinkProps } from './PromoCard';
5
5
 
6
6
  const meta: Meta<typeof PromoCard> = {
7
7
  component: PromoCard,
@@ -1,18 +1,19 @@
1
+ import { useId } from '@radix-ui/react-id';
1
2
  import { Check } from '@transferwise/icons';
2
3
  import classNames from 'classnames';
3
- import React, { forwardRef, FunctionComponent, useEffect, useId, useState } from 'react';
4
+ import React, { forwardRef, type FunctionComponent, useEffect, useState } from 'react';
4
5
 
5
6
  import Body from '../body';
6
7
  import { Typography } from '../common';
7
- import Card, { CardProps } from '../common/card';
8
+ import Card, { type CardProps } from '../common/card';
8
9
  import Display from '../display';
9
10
  import Image from '../image/Image';
10
11
  import Title from '../title';
11
12
 
12
13
  import { usePromoCardContext } from './PromoCardContext';
13
- import PromoCardIndicator, { PromoCardIndicatorProps } from './PromoCardIndicator';
14
+ import PromoCardIndicator, { type PromoCardIndicatorProps } from './PromoCardIndicator';
14
15
 
15
- export type ReferenceType = React.Ref<HTMLInputElement>;
16
+ export type ReferenceType = React.Ref<HTMLInputElement> | React.Ref<HTMLDivElement>;
16
17
  export type RelatedTypes =
17
18
  | ''
18
19
  | 'alternate'
@@ -67,6 +68,9 @@ export interface PromoCardCommonProps {
67
68
  /** Specify an onClick event handler */
68
69
  onClick?: () => void;
69
70
 
71
+ /** Specify an onKeyDown event handler */
72
+ onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
73
+
70
74
  /** Optional prop to specify the ID used for testing */
71
75
  testId?: string;
72
76
 
@@ -75,6 +79,8 @@ export interface PromoCardCommonProps {
75
79
 
76
80
  /** Set to false to use body font style for the title */
77
81
  useDisplayFont?: boolean;
82
+
83
+ ref?: ReferenceType;
78
84
  }
79
85
 
80
86
  export interface PromoCardLinkProps extends PromoCardCommonProps, Omit<CardProps, 'children'> {
@@ -90,6 +96,14 @@ export interface PromoCardLinkProps extends PromoCardCommonProps, Omit<CardProps
90
96
  /** Optionally specify the language of the linked URL */
91
97
  hrefLang?: string;
92
98
 
99
+ /** Optional property that can be pass a ref for the anchor. */
100
+ anchorRef?: React.Ref<HTMLAnchorElement>;
101
+
102
+ /**
103
+ * Optional prop to specify the ID of the anchor element which can be useful when using a ref.
104
+ */
105
+ anchorId?: string;
106
+
93
107
  /**
94
108
  * Relationship between the PromoCard href URL and the current page. See
95
109
  * [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
@@ -104,7 +118,7 @@ export interface PromoCardLinkProps extends PromoCardCommonProps, Omit<CardProps
104
118
  isChecked?: never;
105
119
  tabIndex?: never;
106
120
  type?: never;
107
- reference?: never;
121
+ ref?: ReferenceType;
108
122
  value?: never;
109
123
  }
110
124
 
@@ -119,7 +133,7 @@ export interface PromoCardCheckedProps extends PromoCardCommonProps, Omit<CardPr
119
133
  tabIndex?: number;
120
134
 
121
135
  /** Optional property to provide component Ref */
122
- reference?: ReferenceType;
136
+ ref?: ReferenceType;
123
137
 
124
138
  /** Optional prop to specify the input type of the PromoCard */
125
139
  type?: 'checkbox' | 'radio';
@@ -130,6 +144,8 @@ export interface PromoCardCheckedProps extends PromoCardCommonProps, Omit<CardPr
130
144
  /** Only applies to <a />s */
131
145
  download?: never;
132
146
  href?: never;
147
+ anchorRef?: never;
148
+ anchorId?: never;
133
149
  hrefLang?: never;
134
150
  rel?: never;
135
151
  target?: never;
@@ -201,6 +217,7 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
201
217
  isChecked,
202
218
  isDisabled,
203
219
  onClick,
220
+ onKeyDown,
204
221
  rel,
205
222
  tabIndex,
206
223
  target,
@@ -210,9 +227,11 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
210
227
  value,
211
228
  isSmall,
212
229
  useDisplayFont = true,
230
+ anchorRef,
231
+ anchorId,
213
232
  ...props
214
233
  },
215
- reference,
234
+ ref: ReferenceType,
216
235
  ) => {
217
236
  // Set the `checked` state to the value of `defaultChecked` if it is truthy,
218
237
  // or the value of `isChecked` if it is truthy, or `false` if neither
@@ -275,7 +294,8 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
275
294
  id: componentId,
276
295
  isDisabled: isDisabled || contextIsDisabled,
277
296
  onClick,
278
- ref: reference,
297
+ onKeyDown,
298
+ ref,
279
299
  'data-testid': testId,
280
300
  isSmall,
281
301
  };
@@ -290,6 +310,8 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
290
310
  hrefLang,
291
311
  rel,
292
312
  target,
313
+ ref: anchorRef,
314
+ id: anchorId,
293
315
  }
294
316
  : {};
295
317
 
@@ -310,7 +332,7 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
310
332
  handleClick();
311
333
  }
312
334
  },
313
- ref: reference,
335
+ ref,
314
336
  tabIndex: 0,
315
337
  }
316
338
  : {};
@@ -29,6 +29,7 @@ exports[`Radio shows the avatar when supplied 1`] = `
29
29
  class="np-text-body-large np-radio__text"
30
30
  >
31
31
  This is a label
32
+
32
33
  </span>
33
34
  <span
34
35
  class="np-radio__avatar m-l-auto"
@@ -4,7 +4,6 @@ import ReactDOM from 'react-dom';
4
4
  import SnackbarAppendingToBody, { Snackbar, CSS_TRANSITION_DURATION } from './Snackbar';
5
5
  import { SnackbarConsumer } from './SnackbarContext';
6
6
  import SnackbarProvider from './SnackbarProvider';
7
- import { act } from 'react';
8
7
 
9
8
  describe('Snackbar', () => {
10
9
  const timeout = 1000;
@@ -79,9 +78,7 @@ describe('Snackbar', () => {
79
78
  expect(snackbar().text()).toContain(props.text);
80
79
  expect(snackbar()).toHaveLength(1);
81
80
 
82
- await act(async () => {
83
- await jest.advanceTimersByTimeAsync(timeout + CSS_TRANSITION_DURATION + 500);
84
- });
81
+ jest.advanceTimersByTime(timeout + CSS_TRANSITION_DURATION + 500);
85
82
 
86
83
  expect(snackbar().text()).not.toContain(props.text);
87
84
  });
@@ -1,6 +1,5 @@
1
1
  import { Spring } from '@react-spring/web';
2
2
  import { mount } from 'enzyme';
3
- import { act } from 'react';
4
3
 
5
4
  import { Size, Width } from '../common';
6
5
 
@@ -9,6 +8,8 @@ import TabPanel from './TabPanel';
9
8
  import Tabs from './Tabs';
10
9
  import { getElasticDragDifference } from './utils';
11
10
 
11
+ jest.useFakeTimers();
12
+
12
13
  jest.mock('@react-spring/web', () => ({
13
14
  animated: {
14
15
  div: ({ children, style }) => (
@@ -36,7 +37,6 @@ describe('Tabs', () => {
36
37
  let props;
37
38
 
38
39
  beforeEach(() => {
39
- jest.useFakeTimers();
40
40
  props = {
41
41
  animatePanelsOnClick: true,
42
42
  tabs: generateTabs(),
@@ -57,11 +57,6 @@ describe('Tabs', () => {
57
57
  jest.clearAllMocks();
58
58
  });
59
59
 
60
- afterEach(async () => {
61
- await jest.runOnlyPendingTimersAsync();
62
- jest.useRealTimers();
63
- });
64
-
65
60
  it('renders with right props', () => {
66
61
  expect(component.find(Tabs)).toHaveLength(1);
67
62
  expect(component.find(Tabs).props()).toStrictEqual({ ...props });
@@ -177,13 +172,11 @@ describe('Tabs', () => {
177
172
  expect(component.find(Tab)).toHaveLength(props.tabs.length);
178
173
  });
179
174
 
180
- it('does not animate when a tab before the selected tab goes from disabled to enabled', async () => {
175
+ it('does not animate when a tab before the selected tab goes from disabled to enabled', () => {
181
176
  component.setProps({ selected: 2 });
182
177
  expect(component.state('isAnimating')).toBe(true);
183
178
 
184
- await act(async () => {
185
- await jest.runOnlyPendingTimersAsync();
186
- });
179
+ triggerSpringOnRest();
187
180
  expect(component.state('isAnimating')).toBe(false);
188
181
 
189
182
  component.setProps({ tabs: generateTabs([false, false, false]) });
@@ -255,16 +248,14 @@ describe('Tabs', () => {
255
248
  ${5} | ${Width.AUTO} | ${'240px'} | ${'-900px'}
256
249
  `(
257
250
  'when selecting tab number %selected when headerWidth is set to %headerWidth',
258
- async ({ selected, headerWidth, lineTranslateX, sliderTranslateX }) => {
251
+ ({ selected, headerWidth, lineTranslateX, sliderTranslateX }) => {
259
252
  component.setProps({ headerWidth });
260
253
 
261
254
  const getLineStyles = () => getComputedStyle(component.find('.tabs__line').getDOMNode());
262
255
  const getSliderStyles = () =>
263
256
  getComputedStyle(component.find('.tabs__slider').getDOMNode());
264
257
 
265
- await act(async () => {
266
- component.setProps({ selected });
267
- });
258
+ component.setProps({ selected });
268
259
 
269
260
  expect(component.state('isAnimating')).toBe(true);
270
261
  expect(getLineStyles().getPropertyValue('transform')).toBe(`translateX(${lineTranslateX})`);
@@ -272,9 +263,7 @@ describe('Tabs', () => {
272
263
  `translateX(${sliderTranslateX})`,
273
264
  );
274
265
 
275
- await act(async () => {
276
- await jest.runOnlyPendingTimersAsync();
277
- });
266
+ triggerSpringOnRest();
278
267
 
279
268
  expect(component.state('isAnimating')).toBe(false);
280
269
  expect(getLineStyles().getPropertyValue('transform')).toBe(`translateX(${lineTranslateX})`);
@@ -287,11 +276,15 @@ describe('Tabs', () => {
287
276
 
288
277
  const getLineStyles = () => getComputedStyle(component.find('.tabs__line').getDOMNode());
289
278
 
279
+ triggerSpringOnRest();
280
+
290
281
  expect(getLineStyles().getPropertyValue('transform')).toBe(`translateX(60px)`);
291
282
  expect(component.state('fullWidthTabs')).toBeFalsy();
292
283
 
293
284
  component.setProps({ tabs: generateTabs([false, true, false, false, false, false, false]) });
294
285
 
286
+ triggerSpringOnRest();
287
+
295
288
  expect(getLineStyles().getPropertyValue('transform')).toBe(`translateX(100%)`);
296
289
  expect(component.state('fullWidthTabs')).toBeTruthy();
297
290
  });
@@ -326,57 +319,41 @@ describe('Tabs', () => {
326
319
  });
327
320
  });
328
321
 
329
- it('displays all tabs when animating', async () => {
330
- await act(async () => {
331
- component.setState({ isAnimating: true });
332
- });
322
+ it('displays all tabs when animating', () => {
323
+ component.setState({ isAnimating: true });
333
324
 
334
325
  component.find(TabPanel).forEach((tab) => {
335
326
  expect(tab.prop('style').display).toBe('block');
336
327
  });
337
328
  });
338
329
 
339
- it('displays all tabs when swiping', async () => {
340
- await act(async () => {
341
- component.setState({ isSwiping: true });
342
- });
330
+ it('displays all tabs when swiping', () => {
331
+ component.setState({ isSwiping: true });
343
332
 
344
333
  component.find(TabPanel).forEach((tab) => {
345
334
  expect(tab.prop('style').display).toBe('block');
346
335
  });
347
336
  });
348
337
 
349
- it('has `overflow: hidden` on the parent when animating/swiping', async () => {
350
- await act(async () => {
351
- component.setState({ isSwiping: false, isAnimating: false });
352
- });
338
+ it('has `overflow: hidden` on the parent when animating/swiping', () => {
339
+ component.setState({ isSwiping: false, isAnimating: false });
353
340
  expect(getPanelContainerOverflow(component)).toBe('visible');
354
341
 
355
- await act(async () => {
356
- component.setState({ isSwiping: true });
357
- });
342
+ component.setState({ isSwiping: true });
358
343
  expect(getPanelContainerOverflow(component)).toBe('hidden');
359
344
 
360
- await act(async () => {
361
- component.setState({ isSwiping: false, isAnimating: true });
362
- });
345
+ component.setState({ isSwiping: false, isAnimating: true });
363
346
  expect(getPanelContainerOverflow(component)).toBe('hidden');
364
347
  });
365
348
 
366
- it('sets the panel width according to if animating/swiping', async () => {
367
- await act(async () => {
368
- component.setState({ isSwiping: false, isAnimating: false });
369
- });
349
+ it('sets the panel width according to if animating/swiping', () => {
350
+ component.setState({ isSwiping: false, isAnimating: false });
370
351
  expect(getPanelWidth(component)).toBe('100%');
371
352
 
372
- await act(async () => {
373
- component.setState({ isSwiping: true });
374
- });
353
+ component.setState({ isSwiping: true });
375
354
  expect(getPanelWidth(component)).toBe('300px');
376
355
 
377
- await act(async () => {
378
- component.setState({ isSwiping: false, isAnimating: true });
379
- });
356
+ component.setState({ isSwiping: false, isAnimating: true });
380
357
  expect(getPanelWidth(component)).toBe('300px');
381
358
  });
382
359
  });
@@ -396,6 +373,10 @@ function createClientXY(x, y) {
396
373
  return { clientX: x, clientY: y };
397
374
  }
398
375
 
376
+ function triggerSpringOnRest() {
377
+ jest.runAllTimers();
378
+ }
379
+
399
380
  function getPanelContainerOverflow(component) {
400
381
  return component.find('.tabs__panel-container').prop('style').overflow;
401
382
  }
@@ -1,4 +1,5 @@
1
- import { render, renderHook } from '@testing-library/react';
1
+ import { render } from '@testing-library/react';
2
+ import { renderHook } from '@testing-library/react-hooks';
2
3
  import userEvent from '@testing-library/user-event';
3
4
 
4
5
  import { Provider } from '..';
@@ -29,8 +30,9 @@ function customRenderHook(callback, { locale = DEFAULT_LOCALE, messages = en } =
29
30
  });
30
31
  }
31
32
 
32
- export * from '@testing-library/react';
33
- export * from './fake-data';
34
- export * from './story-config';
35
33
  export * from './window-mock';
36
- export { customRender as render, customRenderHook as renderHook, userEvent };
34
+ export * from './story-config';
35
+ export * from './fake-data';
36
+ export * from '@testing-library/react';
37
+ export { userEvent };
38
+ export { customRender as render, customRenderHook as renderHook };
@@ -1,23 +1,17 @@
1
- const { default: Adapter } = require('@cfaester/enzyme-adapter-react-18');
1
+ const Adapter = require('@wojtekmaj/enzyme-adapter-react-17');
2
2
  const Enzyme = require('enzyme');
3
- const util = require('node:util');
4
3
 
5
4
  global.fetch = require('jest-fetch-mock');
6
-
7
5
  Enzyme.configure({ adapter: new Adapter() });
8
6
 
9
7
  global.requestAnimationFrame = (callback) => callback();
10
8
 
11
- Object.defineProperty(global, 'TextEncoder', {
12
- value: util.TextEncoder,
13
- });
14
-
15
9
  // https://github.com/esphen/jest-prop-type-error/blob/master/index.js
16
10
  // This mock will make tests fail when props error occurs.
17
11
  const { error, warn } = console;
18
12
  // eslint-disable-next-line no-console
19
13
  console.error = (message, ...args) => {
20
- if (/(Invalid prop|Failed prop type)/i.test(message)) {
14
+ if (/(Invalid prop|Failed prop type)/gi.test(message)) {
21
15
  throw new Error(message);
22
16
  }
23
17
 
@@ -26,7 +20,7 @@ console.error = (message, ...args) => {
26
20
 
27
21
  // eslint-disable-next-line no-console
28
22
  console.warn = (message, ...args) => {
29
- if (/(Call to useTheme outside a ThemeProvider)/i.test(message)) {
23
+ if (/(Call to useTheme outside a ThemeProvider)/gi.test(message)) {
30
24
  return;
31
25
  }
32
26
 
@@ -1,8 +1,7 @@
1
1
  import { TextArea, type TextAreaProps } from '../inputs/TextArea';
2
2
  import WithDisplayFormat, { type WithDisplayFormatProps } from '../withDisplayFormat';
3
3
 
4
- export interface TextareaWithDisplayFormatProps
5
- extends Omit<WithDisplayFormatProps<TextAreaProps>, 'render'> {}
4
+ export interface TextareaWithDisplayFormatProps extends Omit<WithDisplayFormatProps, 'render'> {}
6
5
 
7
6
  const TextareaWithDisplayFormat = (props: TextareaWithDisplayFormatProps) => (
8
7
  <WithDisplayFormat<TextAreaProps>