@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.
- package/build/index.js +940 -642
- package/build/index.js.map +1 -1
- package/build/index.mjs +930 -633
- package/build/index.mjs.map +1 -1
- package/build/main.css +135 -0
- package/build/styles/carousel/Carousel.css +135 -0
- package/build/styles/main.css +135 -0
- package/build/types/accordion/AccordionItem/AccordionItem.d.ts.map +1 -1
- package/build/types/carousel/Carousel.d.ts +26 -0
- package/build/types/carousel/Carousel.d.ts.map +1 -0
- package/build/types/carousel/index.d.ts +3 -0
- package/build/types/carousel/index.d.ts.map +1 -0
- 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/dimmer/Dimmer.d.ts +11 -1
- 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 +4 -3
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.d.ts +1 -2
- package/build/types/inputWithDisplayFormat/InputWithDisplayFormat.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 +19 -36
- 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 +16 -5
- 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 +1 -2
- 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 +11 -7
- package/src/accordion/AccordionItem/AccordionItem.tsx +4 -2
- package/src/avatarWrapper/AvatarWrapper.story.tsx +3 -1
- package/src/button/Button.tsx +1 -1
- package/src/carousel/Carousel.css +135 -0
- package/src/carousel/Carousel.less +133 -0
- package/src/carousel/Carousel.spec.tsx +221 -0
- package/src/carousel/Carousel.story.tsx +63 -0
- package/src/carousel/Carousel.tsx +345 -0
- package/src/carousel/index.ts +3 -0
- package/src/common/card/Card.tsx +51 -43
- 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 +2 -1
- package/src/common/panel/Panel.tsx +92 -90
- package/src/common/responsivePanel/ResponsivePanel.tsx +38 -34
- package/src/dateLookup/DateLookup.keyboardEvents.spec.js +180 -0
- package/src/dateLookup/DateLookup.rtl.spec.tsx +5 -181
- package/src/dateLookup/DateLookup.testingLibrary.spec.js +124 -171
- package/src/drawer/Drawer.js +3 -3
- package/src/field/Field.tsx +3 -3
- package/src/index.ts +4 -3
- package/src/inputWithDisplayFormat/InputWithDisplayFormat.tsx +1 -2
- package/src/inputs/SelectInput.story.tsx +0 -1
- package/src/inputs/SelectInput.tsx +2 -10
- package/src/main.css +135 -0
- package/src/main.less +1 -0
- package/src/modal/Modal.tsx +2 -1
- package/src/processIndicator/ProcessIndicator.js +117 -0
- package/src/processIndicator/ProcessIndicator.spec.js +101 -0
- package/src/promoCard/PromoCard.story.tsx +2 -2
- package/src/promoCard/PromoCard.tsx +31 -9
- package/src/radio/__snapshots__/Radio.rtl.spec.tsx.snap +1 -0
- package/src/snackbar/Snackbar.spec.js +1 -4
- package/src/tabs/Tabs.spec.js +27 -46
- package/src/test-utils/index.js +7 -5
- package/src/test-utils/jest.setup.js +3 -9
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.tsx +1 -2
- package/src/tooltip/Tooltip.tsx +46 -44
- package/src/tooltip/__snapshots__/Tooltip.spec.tsx.snap +2 -2
- package/src/upload/Upload.spec.js +13 -34
- package/src/uploadInput/UploadInput.spec.tsx +23 -21
- package/src/uploadInput/uploadItem/UploadItem.tsx +3 -1
- package/src/withDisplayFormat/WithDisplayFormat.spec.js +32 -63
- package/src/withDisplayFormat/WithDisplayFormat.tsx +28 -28
- package/src/processIndicator/ProcessIndicator.rtl.spec.tsx +0 -45
- package/src/processIndicator/ProcessIndicator.tsx +0 -110
- /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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
335
|
+
ref,
|
|
314
336
|
tabIndex: 0,
|
|
315
337
|
}
|
|
316
338
|
: {};
|
|
@@ -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
|
-
|
|
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
|
});
|
package/src/tabs/Tabs.spec.js
CHANGED
|
@@ -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',
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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',
|
|
330
|
-
|
|
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',
|
|
340
|
-
|
|
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',
|
|
350
|
-
|
|
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
|
-
|
|
356
|
-
component.setState({ isSwiping: true });
|
|
357
|
-
});
|
|
342
|
+
component.setState({ isSwiping: true });
|
|
358
343
|
expect(getPanelContainerOverflow(component)).toBe('hidden');
|
|
359
344
|
|
|
360
|
-
|
|
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',
|
|
367
|
-
|
|
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
|
-
|
|
373
|
-
component.setState({ isSwiping: true });
|
|
374
|
-
});
|
|
353
|
+
component.setState({ isSwiping: true });
|
|
375
354
|
expect(getPanelWidth(component)).toBe('300px');
|
|
376
355
|
|
|
377
|
-
|
|
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
|
}
|
package/src/test-utils/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { render
|
|
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
|
|
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
|
|
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)/
|
|
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)/
|
|
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>
|