@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.
- package/build/index.js +635 -933
- package/build/index.js.map +1 -1
- package/build/index.mjs +625 -922
- package/build/index.mjs.map +1 -1
- package/build/main.css +0 -135
- package/build/styles/main.css +0 -135
- package/build/types/accordion/AccordionItem/AccordionItem.d.ts.map +1 -1
- package/build/types/common/card/Card.d.ts +2 -2
- package/build/types/common/card/Card.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/dateLookup/DateLookup.d.ts +6 -5
- package/build/types/dateLookup/DateLookup.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 -4
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts +2 -1
- package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts.map +1 -1
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/inputs/contexts.d.ts +2 -2
- package/build/types/inputs/contexts.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 +5 -16
- package/build/types/promoCard/PromoCard.d.ts.map +1 -1
- package/build/types/select/searchBox/SearchBox.d.ts +1 -1
- package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts +2 -1
- package/build/types/textareaWithDisplayFormat/TextareaWithDisplayFormat.d.ts.map +1 -1
- 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 +14 -14
- package/build/types/withDisplayFormat/WithDisplayFormat.d.ts.map +1 -1
- package/package.json +9 -13
- package/src/accordion/AccordionItem/AccordionItem.tsx +2 -4
- package/src/avatarWrapper/AvatarWrapper.story.tsx +1 -3
- package/src/button/Button.tsx +1 -1
- package/src/common/card/Card.tsx +43 -51
- 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/dateLookup/DateLookup.tsx +14 -9
- package/src/drawer/Drawer.js +3 -3
- package/src/field/Field.tsx +3 -3
- package/src/index.ts +3 -4
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.tsx +2 -1
- package/src/inputs/SelectInput.story.tsx +2 -1
- package/src/inputs/SelectInput.tsx +10 -2
- package/src/inputs/contexts.tsx +4 -4
- package/src/main.css +0 -135
- package/src/main.less +0 -1
- 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.story.tsx +2 -2
- package/src/promoCard/PromoCard.tsx +9 -31
- package/src/radio/__snapshots__/Radio.rtl.spec.tsx.snap +0 -1
- package/src/snackbar/Snackbar.spec.js +4 -1
- package/src/snackbar/Snackbar.story.tsx +4 -2
- 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/textareaWithDisplayFormat/TextareaWithDisplayFormat.tsx +2 -1
- 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 +28 -28
- package/build/styles/carousel/Carousel.css +0 -135
- package/build/types/carousel/Carousel.d.ts +0 -26
- package/build/types/carousel/Carousel.d.ts.map +0 -1
- package/build/types/carousel/index.d.ts +0 -3
- package/build/types/carousel/index.d.ts.map +0 -1
- package/src/carousel/Carousel.css +0 -135
- package/src/carousel/Carousel.less +0 -133
- package/src/carousel/Carousel.spec.tsx +0 -221
- package/src/carousel/Carousel.story.tsx +0 -63
- package/src/carousel/Carousel.tsx +0 -345
- package/src/carousel/index.ts +0 -3
- 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
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
package/src/modal/Modal.tsx
CHANGED
|
@@ -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
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { StarFill } from '@transferwise/icons';
|
|
3
3
|
|
|
4
|
-
import 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,
|
|
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, {
|
|
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, {
|
|
13
|
+
import PromoCardIndicator, { PromoCardIndicatorProps } from './PromoCardIndicator';
|
|
15
14
|
|
|
16
|
-
export type ReferenceType = React.Ref<HTMLInputElement
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
: {};
|
|
@@ -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
|
-
|
|
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
|
|
126
|
+
await waitFor(async () => {
|
|
127
|
+
await userEvent.click(canvas.getByRole('button'));
|
|
128
|
+
});
|
|
127
129
|
};
|
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
|
}
|