@transferwise/components 46.112.1 → 46.113.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/expressiveMoneyInput/ExpressiveMoneyInput.js +113 -0
- package/build/expressiveMoneyInput/ExpressiveMoneyInput.js.map +1 -0
- package/build/expressiveMoneyInput/ExpressiveMoneyInput.messages.js +17 -0
- package/build/expressiveMoneyInput/ExpressiveMoneyInput.messages.js.map +1 -0
- package/build/expressiveMoneyInput/ExpressiveMoneyInput.messages.mjs +13 -0
- package/build/expressiveMoneyInput/ExpressiveMoneyInput.messages.mjs.map +1 -0
- package/build/expressiveMoneyInput/ExpressiveMoneyInput.mjs +109 -0
- package/build/expressiveMoneyInput/ExpressiveMoneyInput.mjs.map +1 -0
- package/build/expressiveMoneyInput/amountInput/AmountInput.js +281 -0
- package/build/expressiveMoneyInput/amountInput/AmountInput.js.map +1 -0
- package/build/expressiveMoneyInput/amountInput/AmountInput.mjs +279 -0
- package/build/expressiveMoneyInput/amountInput/AmountInput.mjs.map +1 -0
- package/build/expressiveMoneyInput/amountInput/utils.js +87 -0
- package/build/expressiveMoneyInput/amountInput/utils.js.map +1 -0
- package/build/expressiveMoneyInput/amountInput/utils.mjs +78 -0
- package/build/expressiveMoneyInput/amountInput/utils.mjs.map +1 -0
- package/build/expressiveMoneyInput/animatedNumber/AnimatedNumber.js +50 -0
- package/build/expressiveMoneyInput/animatedNumber/AnimatedNumber.js.map +1 -0
- package/build/expressiveMoneyInput/animatedNumber/AnimatedNumber.mjs +48 -0
- package/build/expressiveMoneyInput/animatedNumber/AnimatedNumber.mjs.map +1 -0
- package/build/expressiveMoneyInput/chevron/Chevron.js +31 -0
- package/build/expressiveMoneyInput/chevron/Chevron.js.map +1 -0
- package/build/expressiveMoneyInput/chevron/Chevron.mjs +29 -0
- package/build/expressiveMoneyInput/chevron/Chevron.mjs.map +1 -0
- package/build/expressiveMoneyInput/currencySelector/CurrencySelector.js +160 -0
- package/build/expressiveMoneyInput/currencySelector/CurrencySelector.js.map +1 -0
- package/build/expressiveMoneyInput/currencySelector/CurrencySelector.mjs +157 -0
- package/build/expressiveMoneyInput/currencySelector/CurrencySelector.mjs.map +1 -0
- package/build/expressiveMoneyInput/hooks/useFocus.js +37 -0
- package/build/expressiveMoneyInput/hooks/useFocus.js.map +1 -0
- package/build/expressiveMoneyInput/hooks/useFocus.mjs +35 -0
- package/build/expressiveMoneyInput/hooks/useFocus.mjs.map +1 -0
- package/build/expressiveMoneyInput/hooks/useInputStyle.js +71 -0
- package/build/expressiveMoneyInput/hooks/useInputStyle.js.map +1 -0
- package/build/expressiveMoneyInput/hooks/useInputStyle.mjs +69 -0
- package/build/expressiveMoneyInput/hooks/useInputStyle.mjs.map +1 -0
- package/build/i18n/en.json +2 -0
- package/build/i18n/en.json.js +2 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +2 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/index.mjs +1 -0
- package/build/index.mjs.map +1 -1
- package/build/main.css +73 -7
- package/build/prompt/InlinePrompt/InlinePrompt.js +7 -0
- package/build/prompt/InlinePrompt/InlinePrompt.js.map +1 -1
- package/build/prompt/InlinePrompt/InlinePrompt.mjs +8 -1
- package/build/prompt/InlinePrompt/InlinePrompt.mjs.map +1 -1
- package/build/styles/expressiveMoneyInput/ExpressiveMoneyInput.css +58 -0
- package/build/styles/expressiveMoneyInput/amountInput/AmountInput.css +32 -0
- package/build/styles/expressiveMoneyInput/chevron/Chevron.css +12 -0
- package/build/styles/expressiveMoneyInput/currencySelector/CurrencySelector.css +6 -0
- package/build/styles/main.css +73 -7
- package/build/styles/moneyInput/MoneyInput.css +8 -0
- package/build/styles/prompt/InlinePrompt/InlinePrompt.css +7 -7
- package/build/types/expressiveMoneyInput/ExpressiveMoneyInput.d.ts +59 -0
- package/build/types/expressiveMoneyInput/ExpressiveMoneyInput.d.ts.map +1 -0
- package/build/types/expressiveMoneyInput/ExpressiveMoneyInput.messages.d.ts +12 -0
- package/build/types/expressiveMoneyInput/ExpressiveMoneyInput.messages.d.ts.map +1 -0
- package/build/types/expressiveMoneyInput/amountInput/AmountInput.d.ts +13 -0
- package/build/types/expressiveMoneyInput/amountInput/AmountInput.d.ts.map +1 -0
- package/build/types/expressiveMoneyInput/amountInput/utils.d.ts +22 -0
- package/build/types/expressiveMoneyInput/amountInput/utils.d.ts.map +1 -0
- package/build/types/expressiveMoneyInput/animatedNumber/AnimatedNumber.d.ts +9 -0
- package/build/types/expressiveMoneyInput/animatedNumber/AnimatedNumber.d.ts.map +1 -0
- package/build/types/expressiveMoneyInput/chevron/Chevron.d.ts +6 -0
- package/build/types/expressiveMoneyInput/chevron/Chevron.d.ts.map +1 -0
- package/build/types/expressiveMoneyInput/currencySelector/CurrencySelector.d.ts +30 -0
- package/build/types/expressiveMoneyInput/currencySelector/CurrencySelector.d.ts.map +1 -0
- package/build/types/expressiveMoneyInput/hooks/useFocus.d.ts +7 -0
- package/build/types/expressiveMoneyInput/hooks/useFocus.d.ts.map +1 -0
- package/build/types/expressiveMoneyInput/hooks/useInputStyle.d.ts +10 -0
- package/build/types/expressiveMoneyInput/hooks/useInputStyle.d.ts.map +1 -0
- package/build/types/expressiveMoneyInput/hooks/useSelectionRange.d.ts +10 -0
- package/build/types/expressiveMoneyInput/hooks/useSelectionRange.d.ts.map +1 -0
- package/build/types/expressiveMoneyInput/index.d.ts +3 -0
- package/build/types/expressiveMoneyInput/index.d.ts.map +1 -0
- package/build/types/index.d.ts +2 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/prompt/InlinePrompt/InlinePrompt.d.ts +3 -2
- package/build/types/prompt/InlinePrompt/InlinePrompt.d.ts.map +1 -1
- package/build/types/test-utils/index.d.ts +4 -0
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/expressiveMoneyInput/ExpressiveMoneyInput.autofocus.docs.mdx +12 -0
- package/src/expressiveMoneyInput/ExpressiveMoneyInput.css +58 -0
- package/src/expressiveMoneyInput/ExpressiveMoneyInput.less +13 -0
- package/src/expressiveMoneyInput/ExpressiveMoneyInput.messages.ts +13 -0
- package/src/expressiveMoneyInput/ExpressiveMoneyInput.story.tsx +232 -0
- package/src/expressiveMoneyInput/ExpressiveMoneyInput.tsx +156 -0
- package/src/expressiveMoneyInput/amountInput/AmountInput.css +32 -0
- package/src/expressiveMoneyInput/amountInput/AmountInput.less +43 -0
- package/src/expressiveMoneyInput/amountInput/AmountInput.tsx +353 -0
- package/src/expressiveMoneyInput/amountInput/utils.spec.ts +114 -0
- package/src/expressiveMoneyInput/amountInput/utils.ts +116 -0
- package/src/expressiveMoneyInput/animatedNumber/AnimatedNumber.tsx +40 -0
- package/src/expressiveMoneyInput/chevron/Chevron.css +12 -0
- package/src/expressiveMoneyInput/chevron/Chevron.less +13 -0
- package/src/expressiveMoneyInput/chevron/Chevron.tsx +35 -0
- package/src/expressiveMoneyInput/currencySelector/CurrencySelector.css +6 -0
- package/src/expressiveMoneyInput/currencySelector/CurrencySelector.less +7 -0
- package/src/expressiveMoneyInput/currencySelector/CurrencySelector.tsx +220 -0
- package/src/expressiveMoneyInput/hooks/useFocus.ts +35 -0
- package/src/expressiveMoneyInput/hooks/useInputStyle.ts +85 -0
- package/src/expressiveMoneyInput/hooks/useSelectionRange.ts +23 -0
- package/src/expressiveMoneyInput/index.ts +2 -0
- package/src/i18n/en.json +2 -0
- package/src/index.ts +2 -0
- package/src/main.css +73 -7
- package/src/main.less +1 -0
- package/src/moneyInput/MoneyInput.css +8 -0
- package/src/moneyInput/MoneyInput.less +5 -0
- package/src/prompt/InlinePrompt/InlinePrompt.css +7 -7
- package/src/prompt/InlinePrompt/InlinePrompt.less +7 -7
- package/src/prompt/InlinePrompt/InlinePrompt.spec.tsx +6 -0
- package/src/prompt/InlinePrompt/InlinePrompt.story.tsx +39 -0
- package/src/prompt/InlinePrompt/InlinePrompt.tsx +12 -2
- package/src/ssr.spec.tsx +1 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/* eslint-disable jsx-a11y/anchor-is-valid, jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
|
|
2
|
+
import { Meta, StoryObj, Decorator } from '@storybook/react-webpack5';
|
|
3
|
+
import { fn } from 'storybook/test';
|
|
4
|
+
import { Flag } from '@wise/art';
|
|
5
|
+
import { Rewards, Tags } from '@transferwise/icons';
|
|
6
|
+
import { storySourceWithoutNoise } from '../../.storybook/helpers';
|
|
7
|
+
import { lorem10, lorem5 } from '../test-utils';
|
|
8
|
+
import { Sentiment } from '../common';
|
|
9
|
+
import Link from '../link';
|
|
10
|
+
import Money from '../money';
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import Button from '../button';
|
|
13
|
+
import Modal from '../modal';
|
|
14
|
+
import ListItem from '../listItem';
|
|
15
|
+
import List from '../list';
|
|
16
|
+
import ExpressiveMoneyInput, { Props as ExpressiveMoneyInputProps } from './ExpressiveMoneyInput';
|
|
17
|
+
import type { Props as CurrencySelectorProps } from './currencySelector/CurrencySelector';
|
|
18
|
+
|
|
19
|
+
const getCurrencySelectorProps = ({
|
|
20
|
+
logEvents = false,
|
|
21
|
+
secondAvatar = false,
|
|
22
|
+
} = {}): Partial<CurrencySelectorProps> => ({
|
|
23
|
+
addons: secondAvatar ? [{ profileName: 'Mikkael Jordan' }] : undefined,
|
|
24
|
+
options: [
|
|
25
|
+
{
|
|
26
|
+
title: 'Popular',
|
|
27
|
+
currencies: [
|
|
28
|
+
{ code: 'USD', label: 'US Dollar', keywords: ['dollar', 'us'] },
|
|
29
|
+
{ code: 'AUD', label: 'Australia Dollar', keywords: ['dollar', 'us'] },
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: 'Others',
|
|
34
|
+
currencies: [
|
|
35
|
+
{ code: 'GBP', label: 'Pound', keywords: ['british'] },
|
|
36
|
+
{ code: 'EUR', label: 'Euro', keywords: ['euro'] },
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
onOpen: logEvents ? fn() : () => {},
|
|
41
|
+
onSearchChange: logEvents ? fn() : (payload) => {},
|
|
42
|
+
onChange: logEvents ? fn() : (currency) => {},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const withScrollbarProtector: Decorator = (Story) => (
|
|
46
|
+
<>
|
|
47
|
+
<style>{`.docs-story > div {overflow-y:hidden;}`}</style>
|
|
48
|
+
<Story />
|
|
49
|
+
</>
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
export default {
|
|
53
|
+
title: 'Forms/ExpressiveMoneyInput',
|
|
54
|
+
component: ExpressiveMoneyInput,
|
|
55
|
+
tags: ['contribution'],
|
|
56
|
+
parameters: {
|
|
57
|
+
docs: {
|
|
58
|
+
toc: true,
|
|
59
|
+
canvas: {
|
|
60
|
+
sourceState: 'hidden',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
args: {
|
|
65
|
+
label: 'You send',
|
|
66
|
+
currency: 'GBP',
|
|
67
|
+
amount: 1234.56,
|
|
68
|
+
currencySelector: getCurrencySelectorProps(),
|
|
69
|
+
loading: false,
|
|
70
|
+
inlinePrompt: { message: lorem10, sentiment: Sentiment.POSITIVE },
|
|
71
|
+
showChevron: false,
|
|
72
|
+
autoFocus: false,
|
|
73
|
+
onAmountChange: fn(),
|
|
74
|
+
onFocusChange: fn(),
|
|
75
|
+
},
|
|
76
|
+
decorators: [withScrollbarProtector],
|
|
77
|
+
} satisfies Meta<ExpressiveMoneyInputProps>;
|
|
78
|
+
|
|
79
|
+
type Story = StoryObj<ExpressiveMoneyInputProps>;
|
|
80
|
+
|
|
81
|
+
export const Playground: Story = {
|
|
82
|
+
tags: ['!autodocs'],
|
|
83
|
+
render: (args: ExpressiveMoneyInputProps) => <ExpressiveMoneyInput {...args} />,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const WithInitialAmount = {
|
|
87
|
+
args: {
|
|
88
|
+
amount: 1500,
|
|
89
|
+
inlinePrompt: undefined,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const NullAmount = {
|
|
94
|
+
args: {
|
|
95
|
+
amount: null,
|
|
96
|
+
inlinePrompt: undefined,
|
|
97
|
+
currencySelector: getCurrencySelectorProps({ logEvents: true }),
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const WithLoading = {
|
|
102
|
+
args: {
|
|
103
|
+
loading: true,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The chevron should be visible only when the input is inactive
|
|
109
|
+
* and the current value is 0 or null.
|
|
110
|
+
*/
|
|
111
|
+
export const WithChevron = {
|
|
112
|
+
args: {
|
|
113
|
+
showChevron: true,
|
|
114
|
+
amount: 0,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const WithBalanceText = storySourceWithoutNoise<ExpressiveMoneyInputProps>({
|
|
119
|
+
args: {
|
|
120
|
+
currencySelector: getCurrencySelectorProps({ logEvents: true }),
|
|
121
|
+
},
|
|
122
|
+
render: function Render(args: ExpressiveMoneyInputProps) {
|
|
123
|
+
const availableBalance = 1500;
|
|
124
|
+
const [sourceAmount, setSourceAmount] = React.useState(args.amount);
|
|
125
|
+
return (
|
|
126
|
+
<ExpressiveMoneyInput
|
|
127
|
+
{...args}
|
|
128
|
+
amount={sourceAmount}
|
|
129
|
+
inlinePrompt={{
|
|
130
|
+
message: (
|
|
131
|
+
<>
|
|
132
|
+
{`Available balance `}
|
|
133
|
+
<Link
|
|
134
|
+
onClick={() => {
|
|
135
|
+
setSourceAmount(availableBalance);
|
|
136
|
+
}}
|
|
137
|
+
>
|
|
138
|
+
<Money amount={availableBalance} currency={args.currency} />
|
|
139
|
+
</Link>
|
|
140
|
+
</>
|
|
141
|
+
),
|
|
142
|
+
}}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
export const WithInlinePromptSentiment = {
|
|
149
|
+
render: (args: ExpressiveMoneyInputProps) => {
|
|
150
|
+
const inlinePrompts = [
|
|
151
|
+
{ message: lorem5, sentiment: 'negative' },
|
|
152
|
+
{ message: lorem5, sentiment: 'warning' },
|
|
153
|
+
{ message: lorem5, sentiment: 'neutral' },
|
|
154
|
+
{ message: lorem5, sentiment: 'positive' },
|
|
155
|
+
{ message: lorem5, sentiment: 'positive', media: <Tags /> },
|
|
156
|
+
{ message: lorem5, sentiment: 'proposition' },
|
|
157
|
+
{ message: lorem5, sentiment: 'proposition', media: <Rewards /> },
|
|
158
|
+
] as const;
|
|
159
|
+
|
|
160
|
+
return inlinePrompts.map((inlinePrompt, index) => (
|
|
161
|
+
<ExpressiveMoneyInput key={index} {...args} inlinePrompt={inlinePrompt} />
|
|
162
|
+
));
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export const WithSecondaryCurrencySelectorAvatar = {
|
|
167
|
+
args: {
|
|
168
|
+
currencySelector: getCurrencySelectorProps({ logEvents: true, secondAvatar: true }),
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export const WithCustomCurrencySelector = {
|
|
173
|
+
render: (args: ExpressiveMoneyInputProps) => {
|
|
174
|
+
const [open, setOpen] = React.useState(false);
|
|
175
|
+
return (
|
|
176
|
+
<>
|
|
177
|
+
<ExpressiveMoneyInput
|
|
178
|
+
{...args}
|
|
179
|
+
currencySelector={{
|
|
180
|
+
customRender: ({ id, labelId }) => (
|
|
181
|
+
<Button
|
|
182
|
+
v2
|
|
183
|
+
size="sm"
|
|
184
|
+
priority="primary"
|
|
185
|
+
aria-describedby={labelId}
|
|
186
|
+
onClick={() => setOpen(true)}
|
|
187
|
+
>
|
|
188
|
+
Currency Selector
|
|
189
|
+
</Button>
|
|
190
|
+
),
|
|
191
|
+
}}
|
|
192
|
+
/>
|
|
193
|
+
<Modal
|
|
194
|
+
open={open}
|
|
195
|
+
title="Custom UX for currency selectors"
|
|
196
|
+
body={
|
|
197
|
+
<List>
|
|
198
|
+
<ListItem
|
|
199
|
+
media={
|
|
200
|
+
<ListItem.AvatarView>
|
|
201
|
+
<Flag code="gb" />
|
|
202
|
+
</ListItem.AvatarView>
|
|
203
|
+
}
|
|
204
|
+
title="GBP"
|
|
205
|
+
control={<ListItem.Radio name="currency-key" checked={false} />}
|
|
206
|
+
/>
|
|
207
|
+
<ListItem
|
|
208
|
+
media={
|
|
209
|
+
<ListItem.AvatarView>
|
|
210
|
+
<Flag code="au" />
|
|
211
|
+
</ListItem.AvatarView>
|
|
212
|
+
}
|
|
213
|
+
title="AUD"
|
|
214
|
+
control={<ListItem.Radio name="currency-key" checked />}
|
|
215
|
+
/>
|
|
216
|
+
</List>
|
|
217
|
+
}
|
|
218
|
+
onClose={() => setOpen(false)}
|
|
219
|
+
/>
|
|
220
|
+
</>
|
|
221
|
+
);
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const WithAutofocus = {
|
|
226
|
+
tags: ['!autodocs', '!dev'],
|
|
227
|
+
args: {
|
|
228
|
+
inlinePrompt: undefined,
|
|
229
|
+
autoFocus: true,
|
|
230
|
+
},
|
|
231
|
+
render: (args: ExpressiveMoneyInputProps) => <ExpressiveMoneyInput {...args} />,
|
|
232
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import Body from '../body';
|
|
2
|
+
import { Label } from '../label/Label';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { AnimatePresence, motion } from 'framer-motion';
|
|
5
|
+
import { useId, type ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
type Props as CurrencySelectorProps,
|
|
9
|
+
CurrencySelector,
|
|
10
|
+
} from './currencySelector/CurrencySelector';
|
|
11
|
+
import { CommonProps } from '../common';
|
|
12
|
+
import { AmountInput } from './amountInput/AmountInput';
|
|
13
|
+
import { Chevron } from './chevron/Chevron';
|
|
14
|
+
import { InlinePrompt, type InlinePromptProps } from '../prompt/InlinePrompt';
|
|
15
|
+
|
|
16
|
+
type AmountType = number | null;
|
|
17
|
+
export type CurrencyType = string;
|
|
18
|
+
|
|
19
|
+
type DefaultCurrencySelectorInstanceType = Pick<
|
|
20
|
+
CurrencySelectorProps,
|
|
21
|
+
'addons' | 'options' | 'onChange' | 'onOpen' | 'onSearchChange'
|
|
22
|
+
>;
|
|
23
|
+
type CustomCurrencySelectorInstanceType = {
|
|
24
|
+
customRender?: (props: { id: string; labelId: string }) => ReactNode;
|
|
25
|
+
};
|
|
26
|
+
type CurrencySelectorType = DefaultCurrencySelectorInstanceType &
|
|
27
|
+
CustomCurrencySelectorInstanceType;
|
|
28
|
+
|
|
29
|
+
export type Props = {
|
|
30
|
+
label?: ReactNode;
|
|
31
|
+
currencySelector?: CurrencySelectorType;
|
|
32
|
+
amount?: AmountType;
|
|
33
|
+
/**
|
|
34
|
+
* The currency code, e.g. `USD`, `EUR`, `GBP`, etc. Governs the flag rendered in the currency selector.
|
|
35
|
+
*/
|
|
36
|
+
currency: CurrencyType;
|
|
37
|
+
inlinePrompt?: {
|
|
38
|
+
sentiment?: InlinePromptProps['sentiment'];
|
|
39
|
+
message: InlinePromptProps['children'];
|
|
40
|
+
media?: InlinePromptProps['media'];
|
|
41
|
+
};
|
|
42
|
+
showChevron?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* If set, it auto-focuses the amount input upon component mount.<br />
|
|
45
|
+
* ⚠️ **Use with caution**, as it may impact user experience and
|
|
46
|
+
* fail [WCAG 2.4.3 requirements](https://www.w3.org/WAI/WCAG21/Understanding/focus-order.html)
|
|
47
|
+
*/
|
|
48
|
+
autoFocus?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Dims the input to indicate a loading state. Does not disable it.
|
|
51
|
+
*/
|
|
52
|
+
loading?: boolean;
|
|
53
|
+
onAmountChange: (amount: AmountType) => void;
|
|
54
|
+
onFocusChange?: (focused: boolean) => void;
|
|
55
|
+
} & CommonProps;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* This component has been kindly contributed by our friends at the `Send` team 🎉.
|
|
59
|
+
*
|
|
60
|
+
* Some patterns and implementation details used in this component may differ
|
|
61
|
+
* from what's commonly used in the Design System. The overall build and QA was managed
|
|
62
|
+
* by the contributing team directly, and such did not follow a typical DS lifecycle.
|
|
63
|
+
*
|
|
64
|
+
* While we house this component and will help facilitate its future iterations,
|
|
65
|
+
* direct contributions by consuming product teams are highly encouraged.
|
|
66
|
+
*
|
|
67
|
+
* > ⚠️ **Prerequisite:** <br />The component depends on the
|
|
68
|
+
* [framer-motion](https://www.npmjs.com/package/framer-motion) package, which has been
|
|
69
|
+
* deliberately excluded from the Design System bundle and kept as an opt-in only.
|
|
70
|
+
* **Make sure to add it to your project dependencies.**
|
|
71
|
+
*/
|
|
72
|
+
export default function ExpressiveMoneyInput({
|
|
73
|
+
label,
|
|
74
|
+
currency,
|
|
75
|
+
currencySelector = { options: [] } as DefaultCurrencySelectorInstanceType,
|
|
76
|
+
amount,
|
|
77
|
+
onAmountChange,
|
|
78
|
+
className,
|
|
79
|
+
inlinePrompt,
|
|
80
|
+
showChevron,
|
|
81
|
+
autoFocus,
|
|
82
|
+
loading,
|
|
83
|
+
onFocusChange,
|
|
84
|
+
}: Props) {
|
|
85
|
+
const inputId = useId();
|
|
86
|
+
const labelId = useId();
|
|
87
|
+
const customAlertId = useId();
|
|
88
|
+
const currencyId = useId();
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<div className={clsx('wds-expressive-money-input', className)}>
|
|
92
|
+
<Label id={labelId} htmlFor={inputId} className={clsx('m-b-1', 'font-weight-normal')}>
|
|
93
|
+
{label}
|
|
94
|
+
</Label>
|
|
95
|
+
<div
|
|
96
|
+
className={clsx('d-flex')}
|
|
97
|
+
role="group"
|
|
98
|
+
aria-labelledby={labelId}
|
|
99
|
+
{...(inlinePrompt ? { 'aria-describedby': customAlertId } : {})}
|
|
100
|
+
>
|
|
101
|
+
<div className="wds-expressive-money-input-currency-selector">
|
|
102
|
+
{currencySelector.customRender?.({ id: currencyId, labelId }) ?? (
|
|
103
|
+
<CurrencySelector
|
|
104
|
+
id={currencyId}
|
|
105
|
+
labelId={labelId}
|
|
106
|
+
currency={currency}
|
|
107
|
+
{...currencySelector}
|
|
108
|
+
/>
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
111
|
+
<AmountInput
|
|
112
|
+
id={inputId}
|
|
113
|
+
describedById={currencyId}
|
|
114
|
+
amount={amount}
|
|
115
|
+
currency={currency}
|
|
116
|
+
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
117
|
+
autoFocus={autoFocus}
|
|
118
|
+
loading={loading}
|
|
119
|
+
onChange={onAmountChange}
|
|
120
|
+
onFocusChange={onFocusChange}
|
|
121
|
+
/>
|
|
122
|
+
<div className={clsx('d-flex align-items-center', 'wds-expressive-money-input-chevron')}>
|
|
123
|
+
<Chevron shouldShow={Boolean(showChevron)} />
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
<AnimatePresence initial={false}>
|
|
127
|
+
{inlinePrompt && (
|
|
128
|
+
<div className={clsx('d-flex justify-content-end', inlinePrompt && 'm-t-1')}>
|
|
129
|
+
<motion.div
|
|
130
|
+
key={customAlertId}
|
|
131
|
+
initial={{ opacity: 0, height: 0 }}
|
|
132
|
+
animate={{
|
|
133
|
+
opacity: 1,
|
|
134
|
+
height: 'auto',
|
|
135
|
+
transition: { delay: 0.75, duration: 0.3 },
|
|
136
|
+
}}
|
|
137
|
+
exit={{ opacity: 0, height: 0 }}
|
|
138
|
+
>
|
|
139
|
+
{inlinePrompt.sentiment && Object.keys(inlinePrompt.sentiment).length > 0 ? (
|
|
140
|
+
<InlinePrompt
|
|
141
|
+
id={customAlertId}
|
|
142
|
+
media={inlinePrompt.media}
|
|
143
|
+
sentiment={inlinePrompt.sentiment}
|
|
144
|
+
>
|
|
145
|
+
{inlinePrompt.message}
|
|
146
|
+
</InlinePrompt>
|
|
147
|
+
) : (
|
|
148
|
+
<Body>{inlinePrompt.message}</Body>
|
|
149
|
+
)}
|
|
150
|
+
</motion.div>
|
|
151
|
+
</div>
|
|
152
|
+
)}
|
|
153
|
+
</AnimatePresence>
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
.wds-amount-input-container {
|
|
2
|
+
width: 100%;
|
|
3
|
+
}
|
|
4
|
+
.wds-amount-input-input-container {
|
|
5
|
+
display: flex;
|
|
6
|
+
justify-content: right;
|
|
7
|
+
width: 100%;
|
|
8
|
+
transition: font-size 0.4s cubic-bezier(0.3, 0, 0.1, 1), height 0.4s cubic-bezier(0.3, 0, 0.1, 1), margin-top 0.4s cubic-bezier(0.3, 0, 0.1, 1), color 0.4s ease;
|
|
9
|
+
color: var(--color-interactive-primary);
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
margin-bottom: 0 !important;
|
|
12
|
+
}
|
|
13
|
+
@media (prefers-reduced-motion: reduce) {
|
|
14
|
+
.wds-amount-input-input-container {
|
|
15
|
+
transition: none;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
.wds-amount-input-input {
|
|
19
|
+
border: none;
|
|
20
|
+
outline: none;
|
|
21
|
+
flex-grow: 1;
|
|
22
|
+
text-align: right;
|
|
23
|
+
background-color: transparent;
|
|
24
|
+
}
|
|
25
|
+
.wds-amount-input-input:focus-visible {
|
|
26
|
+
outline: none;
|
|
27
|
+
}
|
|
28
|
+
.wds-amount-input-placeholder {
|
|
29
|
+
flex-grow: 0;
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
.wds-amount-input {
|
|
2
|
+
&-container {
|
|
3
|
+
width: 100%;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
&-input-container {
|
|
7
|
+
display: flex;
|
|
8
|
+
justify-content: right;
|
|
9
|
+
width: 100%;
|
|
10
|
+
transition:
|
|
11
|
+
font-size 0.4s cubic-bezier(0.3, 0, 0.1, 1),
|
|
12
|
+
height 0.4s cubic-bezier(0.3, 0, 0.1, 1),
|
|
13
|
+
margin-top 0.4s cubic-bezier(0.3, 0, 0.1, 1),
|
|
14
|
+
color 0.4s ease;
|
|
15
|
+
color: var(--color-interactive-primary);
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
margin-bottom: 0 !important;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@media (prefers-reduced-motion: reduce) {
|
|
21
|
+
&-input-container {
|
|
22
|
+
transition: none;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&-input {
|
|
27
|
+
border: none;
|
|
28
|
+
outline: none;
|
|
29
|
+
flex-grow: 1;
|
|
30
|
+
text-align: right;
|
|
31
|
+
background-color: transparent;
|
|
32
|
+
|
|
33
|
+
&:focus-visible {
|
|
34
|
+
outline: none;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&-placeholder {
|
|
39
|
+
flex-grow: 0;
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
}
|
|
43
|
+
}
|