@transferwise/components 45.17.1 → 45.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.esm.js +425 -368
- package/build/index.esm.js.map +1 -1
- package/build/index.js +425 -366
- package/build/index.js.map +1 -1
- package/build/main.css +1 -1
- package/build/styles/inputs/Input.css +1 -1
- package/build/styles/inputs/SelectInput.css +1 -1
- package/build/styles/inputs/TextArea.css +1 -1
- package/build/styles/instructionsList/InstructionsList.css +1 -1
- package/build/styles/main.css +1 -1
- package/build/styles/stepper/Stepper.css +1 -1
- package/build/styles/tooltip/Tooltip.css +1 -1
- package/build/types/button/Button.d.ts.map +1 -1
- package/build/types/common/polymorphicWithOverrides/PolymorphicWithOverrides.d.ts +13 -0
- package/build/types/common/polymorphicWithOverrides/PolymorphicWithOverrides.d.ts.map +1 -0
- package/build/types/index.d.ts +2 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputs/SelectInput.d.ts +18 -6
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/inputs/_BottomSheet.d.ts.map +1 -1
- package/build/types/inputs/_Popover.d.ts.map +1 -1
- package/build/types/instructionsList/InstructionsList.d.ts +10 -3
- package/build/types/instructionsList/InstructionsList.d.ts.map +1 -1
- package/build/types/processIndicator/ProcessIndicator.d.ts +1 -1
- package/build/types/tooltip/Tooltip.d.ts +2 -1
- package/build/types/tooltip/Tooltip.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/button/Button.story.tsx +6 -0
- package/src/button/Button.tsx +6 -1
- package/src/common/polymorphicWithOverrides/PolymorphicWithOverrides.tsx +19 -0
- package/src/index.ts +9 -1
- package/src/inputs/Input.css +1 -1
- package/src/inputs/SelectInput.css +1 -1
- package/src/inputs/SelectInput.less +20 -5
- package/src/inputs/SelectInput.story.tsx +85 -35
- package/src/inputs/SelectInput.tsx +176 -106
- package/src/inputs/TextArea.css +1 -1
- package/src/inputs/_BottomSheet.tsx +47 -37
- package/src/inputs/_Popover.less +1 -1
- package/src/inputs/_Popover.tsx +30 -27
- package/src/inputs/_common.less +6 -0
- package/src/inputs/_common.ts +4 -4
- package/src/instructionsList/InstructionList.story.tsx +39 -0
- package/src/instructionsList/InstructionsList.css +1 -1
- package/src/instructionsList/InstructionsList.less +3 -15
- package/src/instructionsList/InstructionsList.spec.tsx +35 -0
- package/src/instructionsList/InstructionsList.tsx +40 -25
- package/src/main.css +1 -1
- package/src/processIndicator/ProcessIndicator.js +2 -2
- package/src/ssr.spec.js +1 -0
- package/src/stepper/Stepper.css +1 -1
- package/src/stepper/Stepper.less +1 -1
- package/src/tooltip/Tooltip.css +1 -1
- package/src/tooltip/Tooltip.less +13 -0
- package/src/tooltip/Tooltip.spec.tsx +97 -29
- package/src/tooltip/Tooltip.tsx +24 -31
- package/src/tooltip/__snapshots__/Tooltip.spec.tsx.snap +31 -0
- package/src/instructionsList/InstructionList.story.js +0 -27
- package/src/instructionsList/InstructionsList.spec.js +0 -29
|
@@ -12,6 +12,7 @@ import classNames from 'classnames';
|
|
|
12
12
|
import { useState } from 'react';
|
|
13
13
|
|
|
14
14
|
import { CloseButton } from '../common/closeButton';
|
|
15
|
+
import FocusBoundary from '../common/focusBoundary/FocusBoundary';
|
|
15
16
|
import { PreventScroll } from '../common/preventScroll/PreventScroll';
|
|
16
17
|
import { Size } from '../common/propsValues/size';
|
|
17
18
|
|
|
@@ -84,47 +85,56 @@ export function BottomSheet({
|
|
|
84
85
|
<div className="np-bottom-sheet-v2-backdrop" />
|
|
85
86
|
</Transition.Child>
|
|
86
87
|
|
|
87
|
-
<
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
88
|
+
<FocusBoundary>
|
|
89
|
+
<FloatingFocusManager
|
|
90
|
+
context={context}
|
|
91
|
+
initialFocus={initialFocusRef}
|
|
92
|
+
guards={false}
|
|
93
|
+
modal={false}
|
|
94
|
+
>
|
|
95
|
+
<div className="np-bottom-sheet-v2">
|
|
96
|
+
<Transition.Child
|
|
97
|
+
className="np-bottom-sheet-v2-content"
|
|
98
|
+
enter="np-bottom-sheet-v2-content--enter"
|
|
99
|
+
enterFrom="np-bottom-sheet-v2-content--enter-from"
|
|
100
|
+
leave="np-bottom-sheet-v2-content--leave"
|
|
101
|
+
leaveTo="np-bottom-sheet-v2-content--leave-to"
|
|
101
102
|
>
|
|
102
|
-
<div className="np-bottom-sheet-v2-header">
|
|
103
|
-
<CloseButton
|
|
104
|
-
size={Size.SMALL}
|
|
105
|
-
onClick={() => {
|
|
106
|
-
onClose?.();
|
|
107
|
-
}}
|
|
108
|
-
/>
|
|
109
|
-
</div>
|
|
110
103
|
<div
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
'np-bottom-sheet-v2-content-inner--padding-md': padding === 'md',
|
|
116
|
-
},
|
|
117
|
-
)}
|
|
104
|
+
key={floatingKey} // Force inner state invalidation on open
|
|
105
|
+
ref={refs.setFloating}
|
|
106
|
+
className="np-bottom-sheet-v2-content-inner-container"
|
|
107
|
+
{...getFloatingProps()}
|
|
118
108
|
>
|
|
119
|
-
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
109
|
+
<div className="np-bottom-sheet-v2-header">
|
|
110
|
+
<CloseButton
|
|
111
|
+
size={Size.SMALL}
|
|
112
|
+
onClick={() => {
|
|
113
|
+
onClose?.();
|
|
114
|
+
}}
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
<div
|
|
118
|
+
className={classNames(
|
|
119
|
+
'np-bottom-sheet-v2-content-inner',
|
|
120
|
+
title && 'np-bottom-sheet-v2-content-inner--has-title',
|
|
121
|
+
{
|
|
122
|
+
'np-bottom-sheet-v2-content-inner--padding-md': padding === 'md',
|
|
123
|
+
},
|
|
124
|
+
)}
|
|
125
|
+
>
|
|
126
|
+
{title ? (
|
|
127
|
+
<h2 className="np-bottom-sheet-v2-title np-text-title-body">{title}</h2>
|
|
128
|
+
) : null}
|
|
129
|
+
<div className="np-bottom-sheet-v2-body np-text-body-default">
|
|
130
|
+
{children}
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
123
133
|
</div>
|
|
124
|
-
</
|
|
125
|
-
</
|
|
126
|
-
</
|
|
127
|
-
</
|
|
134
|
+
</Transition.Child>
|
|
135
|
+
</div>
|
|
136
|
+
</FloatingFocusManager>
|
|
137
|
+
</FocusBoundary>
|
|
128
138
|
</Transition>
|
|
129
139
|
</ThemeProvider>
|
|
130
140
|
</FloatingPortal>
|
package/src/inputs/_Popover.less
CHANGED
package/src/inputs/_Popover.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import { ThemeProvider, useTheme } from '@wise/components-theming';
|
|
|
17
17
|
import classNames from 'classnames';
|
|
18
18
|
import { useState } from 'react';
|
|
19
19
|
|
|
20
|
+
import FocusBoundary from '../common/focusBoundary/FocusBoundary';
|
|
20
21
|
import { PreventScroll } from '../common/preventScroll/PreventScroll';
|
|
21
22
|
|
|
22
23
|
export interface PopoverProps {
|
|
@@ -88,36 +89,38 @@ export function Popover({
|
|
|
88
89
|
|
|
89
90
|
<FloatingPortal>
|
|
90
91
|
<ThemeProvider theme={theme} screenMode={screenMode} isNotRootProvider>
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
>
|
|
100
|
-
<
|
|
101
|
-
key={floatingKey} // Force inner state invalidation on open
|
|
102
|
-
ref={refs.setFloating}
|
|
103
|
-
className="np-popover-v2-container"
|
|
104
|
-
// eslint-disable-next-line react/forbid-dom-props
|
|
105
|
-
style={floatingStyles}
|
|
106
|
-
{...getFloatingProps()}
|
|
107
|
-
>
|
|
92
|
+
<Transition
|
|
93
|
+
show={open}
|
|
94
|
+
leave="transition-opacity"
|
|
95
|
+
leaveTo="opacity-0"
|
|
96
|
+
beforeEnter={() => {
|
|
97
|
+
setFloatingKey((prev) => prev + 1);
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
100
|
+
<FocusBoundary>
|
|
101
|
+
<FloatingFocusManager context={context} guards={false} modal={false}>
|
|
108
102
|
<div
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
103
|
+
key={floatingKey} // Force inner state invalidation on open
|
|
104
|
+
ref={refs.setFloating}
|
|
105
|
+
className="np-popover-v2-container"
|
|
106
|
+
// eslint-disable-next-line react/forbid-dom-props
|
|
107
|
+
style={floatingStyles}
|
|
108
|
+
{...getFloatingProps()}
|
|
112
109
|
>
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
<div
|
|
111
|
+
className={classNames('np-popover-v2', title && 'np-popover-v2--has-title', {
|
|
112
|
+
'np-popover-v2--padding-md': padding === 'md',
|
|
113
|
+
})}
|
|
114
|
+
>
|
|
115
|
+
{title ? (
|
|
116
|
+
<h2 className="np-popover-v2-title np-text-title-body">{title}</h2>
|
|
117
|
+
) : null}
|
|
118
|
+
<div className="np-popover-v2-content np-text-body-default">{children}</div>
|
|
119
|
+
</div>
|
|
117
120
|
</div>
|
|
118
|
-
</
|
|
119
|
-
</
|
|
120
|
-
</
|
|
121
|
+
</FloatingFocusManager>
|
|
122
|
+
</FocusBoundary>
|
|
123
|
+
</Transition>
|
|
121
124
|
</ThemeProvider>
|
|
122
125
|
</FloatingPortal>
|
|
123
126
|
</>
|
package/src/inputs/_common.less
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@import (reference) '../../node_modules/@transferwise/neptune-css/src/less/core/_typography.less';
|
|
2
|
+
|
|
1
3
|
.np-form-control {
|
|
2
4
|
--ring-width: 1px;
|
|
3
5
|
--ring-color: var(--color-interactive-secondary);
|
|
@@ -33,6 +35,7 @@
|
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
&--size-auto {
|
|
38
|
+
.np-text-body-large;
|
|
36
39
|
padding-top: var(--size-12);
|
|
37
40
|
padding-bottom: var(--size-12);
|
|
38
41
|
}
|
|
@@ -46,14 +49,17 @@
|
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
&--size-sm {
|
|
52
|
+
.np-text-body-default;
|
|
49
53
|
height: var(--size-32) !important;
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
&--size-md {
|
|
57
|
+
.np-text-body-large;
|
|
53
58
|
height: var(--size-48) !important;
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
&--size-lg {
|
|
62
|
+
.np-text-title-subsection;
|
|
57
63
|
height: var(--size-72) !important;
|
|
58
64
|
}
|
|
59
65
|
}
|
package/src/inputs/_common.ts
CHANGED
|
@@ -11,10 +11,10 @@ export function formControlClassNameBase({ size = 'auto' }: FormControlPropsBase
|
|
|
11
11
|
'form-control', // TODO: Deprecate
|
|
12
12
|
'np-form-control',
|
|
13
13
|
{
|
|
14
|
-
'np-form-control--size-auto
|
|
15
|
-
'np-form-control--size-sm
|
|
16
|
-
'np-form-control--size-md
|
|
17
|
-
'np-form-control--size-lg
|
|
14
|
+
'np-form-control--size-auto': size === 'auto',
|
|
15
|
+
'np-form-control--size-sm': size === 'sm',
|
|
16
|
+
'np-form-control--size-md': size === 'md',
|
|
17
|
+
'np-form-control--size-lg': size === 'lg',
|
|
18
18
|
},
|
|
19
19
|
);
|
|
20
20
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Link from '../link';
|
|
2
|
+
|
|
3
|
+
import InstructionsList from './InstructionsList';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
component: InstructionsList,
|
|
7
|
+
title: 'Typography/InstructionsList',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const Basic = () => {
|
|
11
|
+
return (
|
|
12
|
+
<>
|
|
13
|
+
<InstructionsList
|
|
14
|
+
dos={[
|
|
15
|
+
'Do an initial money transfer',
|
|
16
|
+
'Invite at least 3 friends',
|
|
17
|
+
<span key="12">
|
|
18
|
+
Paying extra{' '}
|
|
19
|
+
<Link href="" type="link-large">
|
|
20
|
+
hidden fees
|
|
21
|
+
</Link>{' '}
|
|
22
|
+
for transfers
|
|
23
|
+
</span>,
|
|
24
|
+
]}
|
|
25
|
+
donts={['Paying extra hidden fees for transfers', 'Use bad exchange rate']}
|
|
26
|
+
/>
|
|
27
|
+
<InstructionsList
|
|
28
|
+
dos={[
|
|
29
|
+
{ content: 'Multiple currencies', 'aria-label': 'Supports multiple currencies' },
|
|
30
|
+
{ content: 'Existing recipients', 'aria-label': 'Supports existing recipients' },
|
|
31
|
+
]}
|
|
32
|
+
donts={[
|
|
33
|
+
{ content: 'Create recipients', 'aria-label': "Doesn't support creating recipients" },
|
|
34
|
+
{ content: 'Edit recipients', 'aria-label': "Doesn't support editing recipients" },
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
</>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.tw-instructions{display:flex;flex-direction:column}.tw-instructions .instruction{display:flex;margin-top:8px;margin-top:var(--padding-x-small)}.tw-instructions .instruction .do,.tw-instructions .instruction .dont{flex-shrink:0;margin-bottom:0;margin-right:
|
|
1
|
+
.tw-instructions{display:flex;flex-direction:column}.tw-instructions .instruction{display:flex;margin-top:8px;margin-top:var(--padding-x-small)}.tw-instructions .instruction .do,.tw-instructions .instruction .dont{flex-shrink:0;margin-bottom:0;margin-right:8px;margin-right:var(--padding-x-small)}.tw-instructions .instruction .do{color:var(--color-sentiment-positive)}.tw-instructions .instruction .dont{color:var(--color-sentiment-negative)}
|
|
@@ -9,28 +9,16 @@
|
|
|
9
9
|
.do,
|
|
10
10
|
.dont {
|
|
11
11
|
flex-shrink: 0;
|
|
12
|
-
margin-right: var(--padding-small);
|
|
12
|
+
margin-right: var(--padding-x-small);
|
|
13
13
|
margin-bottom: 0;
|
|
14
|
-
|
|
15
|
-
.np-theme-personal & {
|
|
16
|
-
margin-right: var(--padding-x-small);
|
|
17
|
-
}
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
.do {
|
|
21
|
-
color: var(--color-
|
|
22
|
-
|
|
23
|
-
.np-theme-personal & {
|
|
24
|
-
color: var(--color-sentiment-positive);
|
|
25
|
-
}
|
|
17
|
+
color: var(--color-sentiment-positive);
|
|
26
18
|
}
|
|
27
19
|
|
|
28
20
|
.dont {
|
|
29
|
-
color: var(--color-
|
|
30
|
-
|
|
31
|
-
.np-theme-personal & {
|
|
32
|
-
color: var(--color-sentiment-negative);
|
|
33
|
-
}
|
|
21
|
+
color: var(--color-sentiment-negative);
|
|
34
22
|
}
|
|
35
23
|
}
|
|
36
24
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { render, screen } from '../test-utils';
|
|
2
|
+
|
|
3
|
+
import InstructionsList from '.';
|
|
4
|
+
|
|
5
|
+
describe('InstructionsList', () => {
|
|
6
|
+
it('should render dos list only', () => {
|
|
7
|
+
const dos = ['Test this component', 'With multiple dos'];
|
|
8
|
+
render(<InstructionsList dos={dos} />);
|
|
9
|
+
|
|
10
|
+
expect(screen.getByText(dos[0])).toBeInTheDocument();
|
|
11
|
+
expect(screen.getByText(dos[1])).toBeInTheDocument();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should render donts list only', () => {
|
|
15
|
+
const donts = ['Card validation', 'ID verification'];
|
|
16
|
+
|
|
17
|
+
render(<InstructionsList donts={donts} />);
|
|
18
|
+
expect(screen.getByText(donts[0])).toBeInTheDocument();
|
|
19
|
+
expect(screen.getByText(donts[1])).toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should render do/dont lists with description for icons', () => {
|
|
23
|
+
const dos = [{ content: 'Card validation', 'aria-label': 'Provides card validation' }];
|
|
24
|
+
const donts = [{ content: 'ID verification', 'aria-label': 'No ID verification' }];
|
|
25
|
+
|
|
26
|
+
const { container } = render(<InstructionsList dos={dos} donts={donts} />);
|
|
27
|
+
expect(screen.getByText(dos[0].content)).toBeInTheDocument();
|
|
28
|
+
expect(screen.getByText(donts[0].content)).toBeInTheDocument();
|
|
29
|
+
|
|
30
|
+
const instructions = container.querySelectorAll('.instruction');
|
|
31
|
+
expect(instructions).toHaveLength(2);
|
|
32
|
+
expect(instructions[0]).toHaveAttribute('aria-label', dos[0]['aria-label']);
|
|
33
|
+
expect(instructions[1]).toHaveAttribute('aria-label', donts[0]['aria-label']);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -1,46 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import { CrossCircleFill as DontIcon, CheckCircleFill as DoIcon } from '@transferwise/icons';
|
|
2
|
+
import { ReactNode, isValidElement } from 'react';
|
|
4
3
|
|
|
5
4
|
import Body from '../body/Body';
|
|
6
|
-
import { CommonProps } from '../common';
|
|
7
|
-
import { Typography } from '../common/propsValues/typography';
|
|
5
|
+
import { Typography, CommonProps } from '../common';
|
|
8
6
|
|
|
9
|
-
type
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
type InstructionNode = {
|
|
8
|
+
content: ReactNode;
|
|
9
|
+
['aria-label']: string;
|
|
12
10
|
};
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
type Props = CommonProps &
|
|
13
|
+
(
|
|
14
|
+
| {
|
|
15
|
+
dos?: ReactNode[];
|
|
16
|
+
donts?: ReactNode[];
|
|
17
|
+
}
|
|
18
|
+
| {
|
|
19
|
+
dos?: InstructionNode[];
|
|
20
|
+
donts?: InstructionNode[];
|
|
21
|
+
}
|
|
22
|
+
);
|
|
19
23
|
|
|
24
|
+
const InstructionsList = ({ dos, donts }: Props) => {
|
|
20
25
|
return (
|
|
21
26
|
<div className="tw-instructions">
|
|
22
27
|
{dos &&
|
|
23
28
|
dos.map((doThis, index) => (
|
|
24
29
|
// eslint-disable-next-line react/no-array-index-key
|
|
25
|
-
<
|
|
26
|
-
<DoIcon size={24} className="do" />
|
|
27
|
-
<Body className="text-primary" type={Typography.BODY_LARGE}>
|
|
28
|
-
{doThis}
|
|
29
|
-
</Body>
|
|
30
|
-
</div>
|
|
30
|
+
<Instruction key={index} item={doThis} type="do" />
|
|
31
31
|
))}
|
|
32
32
|
{donts &&
|
|
33
33
|
donts.map((dont, index) => (
|
|
34
34
|
// eslint-disable-next-line react/no-array-index-key
|
|
35
|
-
<
|
|
36
|
-
<DontIcon size={24} className="dont" />
|
|
37
|
-
<Body className="text-primary" type={Typography.BODY_LARGE}>
|
|
38
|
-
{dont}
|
|
39
|
-
</Body>
|
|
40
|
-
</div>
|
|
35
|
+
<Instruction key={index} item={dont} type="dont" />
|
|
41
36
|
))}
|
|
42
37
|
</div>
|
|
43
38
|
);
|
|
44
39
|
};
|
|
45
40
|
|
|
41
|
+
function Instruction({ item, type }: { item: ReactNode | InstructionNode; type: 'do' | 'dont' }) {
|
|
42
|
+
const isInstructionNode =
|
|
43
|
+
typeof item === 'object' && item !== null && 'content' in item && 'aria-label' in item;
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
className="instruction"
|
|
47
|
+
aria-label={isInstructionNode ? (item['aria-label'] as string) : undefined}
|
|
48
|
+
>
|
|
49
|
+
{type === 'do' ? (
|
|
50
|
+
<DoIcon size={24} className={type} />
|
|
51
|
+
) : (
|
|
52
|
+
<DontIcon size={24} className={type} />
|
|
53
|
+
)}
|
|
54
|
+
<Body className="text-primary" type={Typography.BODY_LARGE}>
|
|
55
|
+
{isInstructionNode ? item.content : item}
|
|
56
|
+
</Body>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
46
61
|
export default InstructionsList;
|