@transferwise/components 0.0.0-experimental-da6dbbf → 0.0.0-experimental-4c1cb43

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 (96) hide show
  1. package/build/index.js +635 -933
  2. package/build/index.js.map +1 -1
  3. package/build/index.mjs +625 -922
  4. package/build/index.mjs.map +1 -1
  5. package/build/main.css +0 -135
  6. package/build/styles/main.css +0 -135
  7. package/build/types/accordion/AccordionItem/AccordionItem.d.ts.map +1 -1
  8. package/build/types/common/card/Card.d.ts +2 -2
  9. package/build/types/common/card/Card.d.ts.map +1 -1
  10. package/build/types/common/hooks/useMedia.d.ts.map +1 -1
  11. package/build/types/common/panel/Panel.d.ts.map +1 -1
  12. package/build/types/common/responsivePanel/ResponsivePanel.d.ts.map +1 -1
  13. package/build/types/dateLookup/DateLookup.d.ts +6 -5
  14. package/build/types/dateLookup/DateLookup.d.ts.map +1 -1
  15. package/build/types/dimmer/Dimmer.d.ts +1 -11
  16. package/build/types/dimmer/Dimmer.d.ts.map +1 -1
  17. package/build/types/drawer/Drawer.d.ts +4 -4
  18. package/build/types/index.d.ts +3 -4
  19. package/build/types/index.d.ts.map +1 -1
  20. package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts +2 -1
  21. package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts.map +1 -1
  22. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  23. package/build/types/inputs/contexts.d.ts +2 -2
  24. package/build/types/inputs/contexts.d.ts.map +1 -1
  25. package/build/types/modal/Modal.d.ts.map +1 -1
  26. package/build/types/processIndicator/ProcessIndicator.d.ts +36 -19
  27. package/build/types/processIndicator/ProcessIndicator.d.ts.map +1 -1
  28. package/build/types/processIndicator/index.d.ts +2 -2
  29. package/build/types/processIndicator/index.d.ts.map +1 -1
  30. package/build/types/promoCard/PromoCard.d.ts +5 -16
  31. package/build/types/promoCard/PromoCard.d.ts.map +1 -1
  32. package/build/types/select/searchBox/SearchBox.d.ts +1 -1
  33. package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts +2 -1
  34. package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts.map +1 -1
  35. package/build/types/tooltip/Tooltip.d.ts +1 -1
  36. package/build/types/tooltip/Tooltip.d.ts.map +1 -1
  37. package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
  38. package/build/types/withDisplayFormat/WithDisplayFormat.d.ts +14 -14
  39. package/build/types/withDisplayFormat/WithDisplayFormat.d.ts.map +1 -1
  40. package/package.json +9 -13
  41. package/src/accordion/AccordionItem/AccordionItem.tsx +2 -4
  42. package/src/avatarWrapper/AvatarWrapper.story.tsx +1 -3
  43. package/src/button/Button.tsx +1 -1
  44. package/src/common/card/Card.tsx +43 -51
  45. package/src/common/hooks/useConditionalListener/useConditionalListener.spec.js +1 -1
  46. package/src/common/hooks/useHasIntersected/useHasIntersected.spec.js +3 -3
  47. package/src/common/hooks/useMedia.spec.ts +1 -1
  48. package/src/common/hooks/useMedia.ts +1 -2
  49. package/src/common/panel/Panel.tsx +90 -92
  50. package/src/common/responsivePanel/ResponsivePanel.tsx +34 -38
  51. package/src/dateLookup/DateLookup.rtl.spec.tsx +181 -5
  52. package/src/dateLookup/DateLookup.testingLibrary.spec.js +171 -124
  53. package/src/dateLookup/DateLookup.tsx +14 -9
  54. package/src/drawer/Drawer.js +3 -3
  55. package/src/field/Field.tsx +3 -3
  56. package/src/index.ts +3 -4
  57. package/src/inputWithDisplayFormat/InputWithDisplayFormat.tsx +2 -1
  58. package/src/inputs/SelectInput.story.tsx +2 -1
  59. package/src/inputs/SelectInput.tsx +10 -2
  60. package/src/inputs/contexts.tsx +4 -4
  61. package/src/main.css +0 -135
  62. package/src/main.less +0 -1
  63. package/src/modal/Modal.tsx +1 -2
  64. package/src/processIndicator/ProcessIndicator.rtl.spec.tsx +45 -0
  65. package/src/processIndicator/ProcessIndicator.tsx +110 -0
  66. package/src/promoCard/PromoCard.story.tsx +2 -2
  67. package/src/promoCard/PromoCard.tsx +9 -31
  68. package/src/radio/__snapshots__/Radio.rtl.spec.tsx.snap +0 -1
  69. package/src/snackbar/Snackbar.spec.js +4 -1
  70. package/src/snackbar/Snackbar.story.tsx +4 -2
  71. package/src/tabs/Tabs.spec.js +46 -27
  72. package/src/test-utils/index.js +5 -7
  73. package/src/test-utils/jest.setup.js +9 -3
  74. package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.tsx +2 -1
  75. package/src/tooltip/Tooltip.tsx +44 -46
  76. package/src/tooltip/__snapshots__/Tooltip.spec.tsx.snap +2 -2
  77. package/src/upload/Upload.spec.js +34 -13
  78. package/src/uploadInput/UploadInput.spec.tsx +21 -23
  79. package/src/uploadInput/uploadItem/UploadItem.tsx +1 -3
  80. package/src/withDisplayFormat/WithDisplayFormat.spec.js +63 -32
  81. package/src/withDisplayFormat/WithDisplayFormat.tsx +28 -28
  82. package/build/styles/carousel/Carousel.css +0 -135
  83. package/build/types/carousel/Carousel.d.ts +0 -26
  84. package/build/types/carousel/Carousel.d.ts.map +0 -1
  85. package/build/types/carousel/index.d.ts +0 -3
  86. package/build/types/carousel/index.d.ts.map +0 -1
  87. package/src/carousel/Carousel.css +0 -135
  88. package/src/carousel/Carousel.less +0 -133
  89. package/src/carousel/Carousel.spec.tsx +0 -221
  90. package/src/carousel/Carousel.story.tsx +0 -63
  91. package/src/carousel/Carousel.tsx +0 -345
  92. package/src/carousel/index.ts +0 -3
  93. package/src/dateLookup/DateLookup.keyboardEvents.spec.js +0 -180
  94. package/src/processIndicator/ProcessIndicator.js +0 -117
  95. package/src/processIndicator/ProcessIndicator.spec.js +0 -101
  96. /package/src/processIndicator/{index.js → index.ts} +0 -0
package/src/main.css CHANGED
@@ -643,141 +643,6 @@ div.critical-comms .critical-comms-body {
643
643
  border-radius: 16px 16px 0 0;
644
644
  border-radius: var(--radius-medium) var(--radius-medium) 0 0;
645
645
  }
646
- .carousel-wrapper {
647
- overflow: hidden;
648
- }
649
- .carousel {
650
- display: flex;
651
- align-items: center;
652
- overflow-x: scroll;
653
- overflow-y: hidden;
654
- scroll-snap-type: x mandatory;
655
- scroll-behavior: smooth;
656
- gap: 16px;
657
- gap: var(--size-16);
658
- padding: 8px;
659
- padding: var(--size-8);
660
- margin: 8px;
661
- margin: var(--size-8);
662
- }
663
- @media (max-width: 767px) {
664
- .carousel {
665
- gap: 8px;
666
- gap: var(--size-8);
667
- }
668
- }
669
- .carousel__header {
670
- display: flex;
671
- align-items: center;
672
- overflow: hidden;
673
- min-height: 32px;
674
- min-height: var(--size-32);
675
- padding-bottom: 16px;
676
- padding-bottom: var(--size-16);
677
- }
678
- .carousel__card,
679
- .carousel__card:hover,
680
- .carousel__card:focus,
681
- .carousel__card:focus-within {
682
- -webkit-text-decoration: none;
683
- text-decoration: none;
684
- transition: none !important;
685
- box-shadow: none !important;
686
- }
687
- .carousel__card {
688
- display: block;
689
- position: relative;
690
- text-align: left;
691
- border: none;
692
- overflow: hidden;
693
- background: rgba(134,167,189,0.10196);
694
- background: var(--color-background-neutral);
695
- border-radius: 32px;
696
- border-radius: var(--size-32);
697
- scroll-snap-align: center;
698
- -webkit-scroll-snap-align: center;
699
- transition: all 0.4s !important;
700
- }
701
- @media (min-width: 1200px) {
702
- .carousel__card {
703
- min-width: 280px;
704
- width: 280px;
705
- height: 280px;
706
- }
707
- }
708
- @media (max-width: 1199px) {
709
- .carousel__card {
710
- min-width: 242px;
711
- width: 242px;
712
- height: 242px;
713
- }
714
- }
715
- @media (max-width: 767px) {
716
- .carousel__card {
717
- min-width: 336px;
718
- width: 336px;
719
- height: 336px;
720
- scroll-snap-stop: always;
721
- }
722
- }
723
- .carousel__card:focus,
724
- .carousel__card:has(:focus-visible) {
725
- outline: var(--ring-outline-color) solid var(--ring-outline-width) !important;
726
- outline-offset: var(--ring-outline-offset) !important;
727
- }
728
- .carousel__card:hover {
729
- background-color: var(--color-background-neutral-hover);
730
- }
731
- .carousel__card:focus {
732
- background-color: var(--color-background-neutral-hover);
733
- }
734
- .carousel__card-content {
735
- height: 100%;
736
- font-weight: normal;
737
- padding: 24px;
738
- padding: var(--size-24);
739
- }
740
- .carousel__scroll-button {
741
- width: 32px;
742
- width: var(--size-32);
743
- height: 32px;
744
- height: var(--size-32);
745
- align-items: center;
746
- justify-content: center;
747
- }
748
- .carousel__indicators {
749
- display: flex;
750
- justify-content: center;
751
- padding-top: 8px;
752
- padding-top: var(--size-8);
753
- gap: 8px;
754
- gap: var(--size-8);
755
- }
756
- .carousel__indicator {
757
- width: 12px;
758
- width: var(--size-12);
759
- height: 12px;
760
- height: var(--size-12);
761
- border-radius: 8px;
762
- border-radius: var(--size-8);
763
- background: #c9cbce;
764
- background: var(--color-interactive-secondary);
765
- border: none;
766
- -webkit-appearance: none;
767
- -moz-appearance: none;
768
- appearance: none;
769
- transition: all 0.1s;
770
- }
771
- .carousel__indicator:hover {
772
- width: 16px;
773
- width: var(--size-16);
774
- }
775
- .carousel__indicator--selected,
776
- .carousel__indicator--selected:hover {
777
- background: var(--color-interactive-primary);
778
- width: 24px;
779
- width: var(--size-24);
780
- }
781
646
  .np-checkbox-button input[type="checkbox"] {
782
647
  position: absolute;
783
648
  width: 24px;
package/src/main.less CHANGED
@@ -5,7 +5,6 @@
5
5
  @import "./badge/Badge.less";
6
6
  @import "./button/Button.less";
7
7
  @import "./card/Card.less";
8
- @import "./carousel/Carousel.less";
9
8
  @import "./checkboxButton/CheckboxButton.less";
10
9
  @import "./chips/Chip.less";
11
10
  @import "./circularButton/CircularButton.less";
@@ -1,6 +1,5 @@
1
- import { useId } from '@radix-ui/react-id';
2
1
  import classNames from 'classnames';
3
- import { ReactNode, useRef } from 'react';
2
+ import { ReactNode, useId, useRef } from 'react';
4
3
  import { CSSTransition } from 'react-transition-group';
5
4
 
6
5
  import {
@@ -0,0 +1,45 @@
1
+ import { act } from 'react';
2
+
3
+ import { render, screen } from '../test-utils';
4
+ import ProcessIndicator, { ProcessIndicatorProps } from './ProcessIndicator';
5
+
6
+ describe('ProcessIndicator', () => {
7
+ beforeEach(() => {
8
+ jest.useFakeTimers();
9
+ });
10
+
11
+ afterEach(async () => {
12
+ await jest.runOnlyPendingTimersAsync();
13
+ jest.useRealTimers();
14
+ });
15
+
16
+ it('supports transitioning between states', async () => {
17
+ const handleAnimationCompleted = jest.fn();
18
+ const initialProps = {
19
+ 'data-testid': 'process-indicator',
20
+ onAnimationCompleted: handleAnimationCompleted,
21
+ } satisfies ProcessIndicatorProps;
22
+ const view = render(<ProcessIndicator {...initialProps} />);
23
+
24
+ expect(screen.getByTestId('process-indicator')).not.toHaveClass('process-success');
25
+ expect(screen.getByTestId('process-indicator')).not.toHaveClass('process-xl');
26
+
27
+ const updatedProps = {
28
+ status: 'succeeded',
29
+ size: 'xl',
30
+ } satisfies ProcessIndicatorProps;
31
+ view.rerender(<ProcessIndicator {...initialProps} {...updatedProps} />);
32
+ await act(async () => {
33
+ await jest.runOnlyPendingTimersAsync();
34
+ });
35
+
36
+ expect(screen.getByTestId('process-indicator')).toHaveClass('process-success');
37
+ expect(screen.getByTestId('process-indicator')).toHaveClass('process-xl');
38
+ expect(handleAnimationCompleted).not.toHaveBeenCalled();
39
+
40
+ await jest.runOnlyPendingTimersAsync();
41
+
42
+ expect(handleAnimationCompleted).toHaveBeenCalledWith(updatedProps.status);
43
+ expect(handleAnimationCompleted).toHaveBeenCalledTimes(1);
44
+ });
45
+ });
@@ -0,0 +1,110 @@
1
+ import classNames from 'classnames';
2
+ import { Component } from 'react';
3
+
4
+ import { Status, Size } from '../common';
5
+
6
+ const radius = { xxs: 6, xs: 11, sm: 22, xl: 61 };
7
+
8
+ const ANIMATION_DURATION_IN_MS = 1500;
9
+
10
+ type ProcessIndicatorStatus =
11
+ `${Status.PROCESSING | Status.FAILED | Status.SUCCEEDED | Status.HIDDEN}`;
12
+
13
+ export interface ProcessIndicatorProps {
14
+ status?: ProcessIndicatorStatus;
15
+ size?: 'xxs' | `${Size.EXTRA_SMALL | Size.SMALL | Size.EXTRA_LARGE}`;
16
+ className?: string;
17
+ 'data-testid'?: string;
18
+ onAnimationCompleted?: (status: ProcessIndicatorStatus) => void;
19
+ }
20
+
21
+ type ProcessIndicatorState = Required<Pick<ProcessIndicatorProps, 'status' | 'size'>>;
22
+
23
+ export default class ProcessIndicator extends Component<
24
+ ProcessIndicatorProps,
25
+ ProcessIndicatorState
26
+ > {
27
+ declare props: ProcessIndicatorProps &
28
+ Required<Pick<ProcessIndicatorProps, keyof typeof ProcessIndicator.defaultProps>>;
29
+
30
+ static defaultProps = {
31
+ status: 'processing',
32
+ size: 'sm',
33
+ } satisfies Partial<ProcessIndicatorProps>;
34
+
35
+ interval = 0;
36
+ timeout = 0;
37
+
38
+ constructor(props: ProcessIndicator['props']) {
39
+ super(props);
40
+ this.state = {
41
+ status: props.status,
42
+ size: props.size,
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Create interval for animation duration (1500ms)
48
+ * Update state only at the end of every interval
49
+ * (end of animation loop) if props changed before end of animation
50
+ */
51
+ componentDidMount() {
52
+ this.interval = window.setInterval(() => {
53
+ const { status: targetStatus, size: targetSize, onAnimationCompleted } = this.props;
54
+ const { status: currentStatus, size: currentSize } = this.state;
55
+
56
+ if (currentStatus !== targetStatus) {
57
+ this.setState({ status: targetStatus }, () => {
58
+ if (onAnimationCompleted) {
59
+ this.timeout = window.setTimeout(() => {
60
+ onAnimationCompleted(targetStatus);
61
+ }, ANIMATION_DURATION_IN_MS);
62
+ }
63
+ });
64
+ }
65
+
66
+ if (currentSize !== targetSize) {
67
+ this.setState({ size: targetSize });
68
+ }
69
+ }, ANIMATION_DURATION_IN_MS);
70
+ }
71
+
72
+ /**
73
+ * Only trigger render if comopnent's state got
74
+ * updated from interval callback
75
+ */
76
+ shouldComponentUpdate(nextProps: ProcessIndicator['props'], nextState: ProcessIndicatorState) {
77
+ const isSameStatus = nextProps.status === nextState.status;
78
+ const isSameSize = nextProps.size === nextState.size;
79
+
80
+ return isSameStatus && isSameSize;
81
+ }
82
+
83
+ // Clear interval before destroying component
84
+ componentWillUnmount() {
85
+ window.clearInterval(this.interval);
86
+ window.clearTimeout(this.timeout);
87
+ }
88
+
89
+ render() {
90
+ const { className, 'data-testid': dataTestId } = this.props;
91
+ const { size, status } = this.state;
92
+ const classes = classNames(`process process-${size}`, className, {
93
+ [`process-danger`]: status === Status.FAILED,
94
+ [`process-stopped`]: status === Status.HIDDEN,
95
+ [`process-success`]: status === Status.SUCCEEDED,
96
+ });
97
+
98
+ return (
99
+ <span className={classes} data-testid={dataTestId}>
100
+ <span className="process-icon-container">
101
+ <span className="process-icon-horizontal" />
102
+ <span className="process-icon-vertical" />
103
+ </span>
104
+ <svg xmlns="http://www.w3.org/2000/svg">
105
+ <circle className="process-circle" cx="50%" cy="50%" r={radius[size]} fillOpacity="0.0" />
106
+ </svg>
107
+ </span>
108
+ );
109
+ }
110
+ }
@@ -1,7 +1,7 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import { Meta, StoryObj } from '@storybook/react';
2
2
  import { StarFill } from '@transferwise/icons';
3
3
 
4
- import PromoCard, { type PromoCardCheckedProps, type PromoCardLinkProps } from './PromoCard';
4
+ import PromoCard, { PromoCardCheckedProps, PromoCardLinkProps } from './PromoCard';
5
5
 
6
6
  const meta: Meta<typeof PromoCard> = {
7
7
  component: PromoCard,
@@ -1,19 +1,18 @@
1
- import { useId } from '@radix-ui/react-id';
2
1
  import { Check } from '@transferwise/icons';
3
2
  import classNames from 'classnames';
4
- import React, { forwardRef, type FunctionComponent, useEffect, useState } from 'react';
3
+ import React, { forwardRef, FunctionComponent, useEffect, useId, useState } from 'react';
5
4
 
6
5
  import Body from '../body';
7
6
  import { Typography } from '../common';
8
- import Card, { type CardProps } from '../common/card';
7
+ import Card, { CardProps } from '../common/card';
9
8
  import Display from '../display';
10
9
  import Image from '../image/Image';
11
10
  import Title from '../title';
12
11
 
13
12
  import { usePromoCardContext } from './PromoCardContext';
14
- import PromoCardIndicator, { type PromoCardIndicatorProps } from './PromoCardIndicator';
13
+ import PromoCardIndicator, { PromoCardIndicatorProps } from './PromoCardIndicator';
15
14
 
16
- export type ReferenceType = React.Ref<HTMLInputElement> | React.Ref<HTMLDivElement>;
15
+ export type ReferenceType = React.Ref<HTMLInputElement>;
17
16
  export type RelatedTypes =
18
17
  | ''
19
18
  | 'alternate'
@@ -68,9 +67,6 @@ export interface PromoCardCommonProps {
68
67
  /** Specify an onClick event handler */
69
68
  onClick?: () => void;
70
69
 
71
- /** Specify an onKeyDown event handler */
72
- onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
73
-
74
70
  /** Optional prop to specify the ID used for testing */
75
71
  testId?: string;
76
72
 
@@ -79,8 +75,6 @@ export interface PromoCardCommonProps {
79
75
 
80
76
  /** Set to false to use body font style for the title */
81
77
  useDisplayFont?: boolean;
82
-
83
- ref?: ReferenceType;
84
78
  }
85
79
 
86
80
  export interface PromoCardLinkProps extends PromoCardCommonProps, Omit<CardProps, 'children'> {
@@ -96,14 +90,6 @@ export interface PromoCardLinkProps extends PromoCardCommonProps, Omit<CardProps
96
90
  /** Optionally specify the language of the linked URL */
97
91
  hrefLang?: string;
98
92
 
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
-
107
93
  /**
108
94
  * Relationship between the PromoCard href URL and the current page. See
109
95
  * [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
@@ -118,7 +104,7 @@ export interface PromoCardLinkProps extends PromoCardCommonProps, Omit<CardProps
118
104
  isChecked?: never;
119
105
  tabIndex?: never;
120
106
  type?: never;
121
- ref?: ReferenceType;
107
+ reference?: never;
122
108
  value?: never;
123
109
  }
124
110
 
@@ -133,7 +119,7 @@ export interface PromoCardCheckedProps extends PromoCardCommonProps, Omit<CardPr
133
119
  tabIndex?: number;
134
120
 
135
121
  /** Optional property to provide component Ref */
136
- ref?: ReferenceType;
122
+ reference?: ReferenceType;
137
123
 
138
124
  /** Optional prop to specify the input type of the PromoCard */
139
125
  type?: 'checkbox' | 'radio';
@@ -144,8 +130,6 @@ export interface PromoCardCheckedProps extends PromoCardCommonProps, Omit<CardPr
144
130
  /** Only applies to <a />s */
145
131
  download?: never;
146
132
  href?: never;
147
- anchorRef?: never;
148
- anchorId?: never;
149
133
  hrefLang?: never;
150
134
  rel?: never;
151
135
  target?: never;
@@ -217,7 +201,6 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
217
201
  isChecked,
218
202
  isDisabled,
219
203
  onClick,
220
- onKeyDown,
221
204
  rel,
222
205
  tabIndex,
223
206
  target,
@@ -227,11 +210,9 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
227
210
  value,
228
211
  isSmall,
229
212
  useDisplayFont = true,
230
- anchorRef,
231
- anchorId,
232
213
  ...props
233
214
  },
234
- ref: ReferenceType,
215
+ reference,
235
216
  ) => {
236
217
  // Set the `checked` state to the value of `defaultChecked` if it is truthy,
237
218
  // or the value of `isChecked` if it is truthy, or `false` if neither
@@ -294,8 +275,7 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
294
275
  id: componentId,
295
276
  isDisabled: isDisabled || contextIsDisabled,
296
277
  onClick,
297
- onKeyDown,
298
- ref,
278
+ ref: reference,
299
279
  'data-testid': testId,
300
280
  isSmall,
301
281
  };
@@ -310,8 +290,6 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
310
290
  hrefLang,
311
291
  rel,
312
292
  target,
313
- ref: anchorRef,
314
- id: anchorId,
315
293
  }
316
294
  : {};
317
295
 
@@ -332,7 +310,7 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
332
310
  handleClick();
333
311
  }
334
312
  },
335
- ref,
313
+ ref: reference,
336
314
  tabIndex: 0,
337
315
  }
338
316
  : {};
@@ -29,7 +29,6 @@ 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
-
33
32
  </span>
34
33
  <span
35
34
  class="np-radio__avatar m-l-auto"
@@ -4,6 +4,7 @@ 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';
7
8
 
8
9
  describe('Snackbar', () => {
9
10
  const timeout = 1000;
@@ -78,7 +79,9 @@ describe('Snackbar', () => {
78
79
  expect(snackbar().text()).toContain(props.text);
79
80
  expect(snackbar()).toHaveLength(1);
80
81
 
81
- jest.advanceTimersByTime(timeout + CSS_TRANSITION_DURATION + 500);
82
+ await act(async () => {
83
+ await jest.advanceTimersByTimeAsync(timeout + CSS_TRANSITION_DURATION + 500);
84
+ });
82
85
 
83
86
  expect(snackbar().text()).not.toContain(props.text);
84
87
  });
@@ -1,7 +1,7 @@
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';
@@ -123,5 +123,7 @@ export const basic = () => {
123
123
 
124
124
  basic.play = async ({ canvasElement }: StoryContext) => {
125
125
  const canvas = within(canvasElement);
126
- await userEvent.click(canvas.getByRole('button'));
126
+ await waitFor(async () => {
127
+ await userEvent.click(canvas.getByRole('button'));
128
+ });
127
129
  };
@@ -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
- triggerSpringOnRest();
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
- component.setProps({ selected });
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
- triggerSpringOnRest();
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
- component.setState({ isAnimating: true });
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
- component.setState({ isSwiping: true });
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
- component.setState({ isSwiping: false, isAnimating: false });
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
- component.setState({ isSwiping: true });
355
+ await act(async () => {
356
+ component.setState({ isSwiping: true });
357
+ });
343
358
  expect(getPanelContainerOverflow(component)).toBe('hidden');
344
359
 
345
- component.setState({ isSwiping: false, isAnimating: true });
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
- component.setState({ isSwiping: false, isAnimating: false });
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
- component.setState({ isSwiping: true });
372
+ await act(async () => {
373
+ component.setState({ isSwiping: true });
374
+ });
354
375
  expect(getPanelWidth(component)).toBe('300px');
355
376
 
356
- component.setState({ isSwiping: false, isAnimating: true });
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
  }