@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.
Files changed (120) hide show
  1. package/build/expressiveMoneyInput/ExpressiveMoneyInput.js +113 -0
  2. package/build/expressiveMoneyInput/ExpressiveMoneyInput.js.map +1 -0
  3. package/build/expressiveMoneyInput/ExpressiveMoneyInput.messages.js +17 -0
  4. package/build/expressiveMoneyInput/ExpressiveMoneyInput.messages.js.map +1 -0
  5. package/build/expressiveMoneyInput/ExpressiveMoneyInput.messages.mjs +13 -0
  6. package/build/expressiveMoneyInput/ExpressiveMoneyInput.messages.mjs.map +1 -0
  7. package/build/expressiveMoneyInput/ExpressiveMoneyInput.mjs +109 -0
  8. package/build/expressiveMoneyInput/ExpressiveMoneyInput.mjs.map +1 -0
  9. package/build/expressiveMoneyInput/amountInput/AmountInput.js +281 -0
  10. package/build/expressiveMoneyInput/amountInput/AmountInput.js.map +1 -0
  11. package/build/expressiveMoneyInput/amountInput/AmountInput.mjs +279 -0
  12. package/build/expressiveMoneyInput/amountInput/AmountInput.mjs.map +1 -0
  13. package/build/expressiveMoneyInput/amountInput/utils.js +87 -0
  14. package/build/expressiveMoneyInput/amountInput/utils.js.map +1 -0
  15. package/build/expressiveMoneyInput/amountInput/utils.mjs +78 -0
  16. package/build/expressiveMoneyInput/amountInput/utils.mjs.map +1 -0
  17. package/build/expressiveMoneyInput/animatedNumber/AnimatedNumber.js +50 -0
  18. package/build/expressiveMoneyInput/animatedNumber/AnimatedNumber.js.map +1 -0
  19. package/build/expressiveMoneyInput/animatedNumber/AnimatedNumber.mjs +48 -0
  20. package/build/expressiveMoneyInput/animatedNumber/AnimatedNumber.mjs.map +1 -0
  21. package/build/expressiveMoneyInput/chevron/Chevron.js +31 -0
  22. package/build/expressiveMoneyInput/chevron/Chevron.js.map +1 -0
  23. package/build/expressiveMoneyInput/chevron/Chevron.mjs +29 -0
  24. package/build/expressiveMoneyInput/chevron/Chevron.mjs.map +1 -0
  25. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.js +160 -0
  26. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.js.map +1 -0
  27. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.mjs +157 -0
  28. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.mjs.map +1 -0
  29. package/build/expressiveMoneyInput/hooks/useFocus.js +37 -0
  30. package/build/expressiveMoneyInput/hooks/useFocus.js.map +1 -0
  31. package/build/expressiveMoneyInput/hooks/useFocus.mjs +35 -0
  32. package/build/expressiveMoneyInput/hooks/useFocus.mjs.map +1 -0
  33. package/build/expressiveMoneyInput/hooks/useInputStyle.js +71 -0
  34. package/build/expressiveMoneyInput/hooks/useInputStyle.js.map +1 -0
  35. package/build/expressiveMoneyInput/hooks/useInputStyle.mjs +69 -0
  36. package/build/expressiveMoneyInput/hooks/useInputStyle.mjs.map +1 -0
  37. package/build/i18n/en.json +2 -0
  38. package/build/i18n/en.json.js +2 -0
  39. package/build/i18n/en.json.js.map +1 -1
  40. package/build/i18n/en.json.mjs +2 -0
  41. package/build/i18n/en.json.mjs.map +1 -1
  42. package/build/index.js +2 -0
  43. package/build/index.js.map +1 -1
  44. package/build/index.mjs +1 -0
  45. package/build/index.mjs.map +1 -1
  46. package/build/main.css +73 -7
  47. package/build/prompt/InlinePrompt/InlinePrompt.js +7 -0
  48. package/build/prompt/InlinePrompt/InlinePrompt.js.map +1 -1
  49. package/build/prompt/InlinePrompt/InlinePrompt.mjs +8 -1
  50. package/build/prompt/InlinePrompt/InlinePrompt.mjs.map +1 -1
  51. package/build/styles/expressiveMoneyInput/ExpressiveMoneyInput.css +58 -0
  52. package/build/styles/expressiveMoneyInput/amountInput/AmountInput.css +32 -0
  53. package/build/styles/expressiveMoneyInput/chevron/Chevron.css +12 -0
  54. package/build/styles/expressiveMoneyInput/currencySelector/CurrencySelector.css +6 -0
  55. package/build/styles/main.css +73 -7
  56. package/build/styles/moneyInput/MoneyInput.css +8 -0
  57. package/build/styles/prompt/InlinePrompt/InlinePrompt.css +7 -7
  58. package/build/types/expressiveMoneyInput/ExpressiveMoneyInput.d.ts +59 -0
  59. package/build/types/expressiveMoneyInput/ExpressiveMoneyInput.d.ts.map +1 -0
  60. package/build/types/expressiveMoneyInput/ExpressiveMoneyInput.messages.d.ts +12 -0
  61. package/build/types/expressiveMoneyInput/ExpressiveMoneyInput.messages.d.ts.map +1 -0
  62. package/build/types/expressiveMoneyInput/amountInput/AmountInput.d.ts +13 -0
  63. package/build/types/expressiveMoneyInput/amountInput/AmountInput.d.ts.map +1 -0
  64. package/build/types/expressiveMoneyInput/amountInput/utils.d.ts +22 -0
  65. package/build/types/expressiveMoneyInput/amountInput/utils.d.ts.map +1 -0
  66. package/build/types/expressiveMoneyInput/animatedNumber/AnimatedNumber.d.ts +9 -0
  67. package/build/types/expressiveMoneyInput/animatedNumber/AnimatedNumber.d.ts.map +1 -0
  68. package/build/types/expressiveMoneyInput/chevron/Chevron.d.ts +6 -0
  69. package/build/types/expressiveMoneyInput/chevron/Chevron.d.ts.map +1 -0
  70. package/build/types/expressiveMoneyInput/currencySelector/CurrencySelector.d.ts +30 -0
  71. package/build/types/expressiveMoneyInput/currencySelector/CurrencySelector.d.ts.map +1 -0
  72. package/build/types/expressiveMoneyInput/hooks/useFocus.d.ts +7 -0
  73. package/build/types/expressiveMoneyInput/hooks/useFocus.d.ts.map +1 -0
  74. package/build/types/expressiveMoneyInput/hooks/useInputStyle.d.ts +10 -0
  75. package/build/types/expressiveMoneyInput/hooks/useInputStyle.d.ts.map +1 -0
  76. package/build/types/expressiveMoneyInput/hooks/useSelectionRange.d.ts +10 -0
  77. package/build/types/expressiveMoneyInput/hooks/useSelectionRange.d.ts.map +1 -0
  78. package/build/types/expressiveMoneyInput/index.d.ts +3 -0
  79. package/build/types/expressiveMoneyInput/index.d.ts.map +1 -0
  80. package/build/types/index.d.ts +2 -0
  81. package/build/types/index.d.ts.map +1 -1
  82. package/build/types/prompt/InlinePrompt/InlinePrompt.d.ts +3 -2
  83. package/build/types/prompt/InlinePrompt/InlinePrompt.d.ts.map +1 -1
  84. package/build/types/test-utils/index.d.ts +4 -0
  85. package/build/types/test-utils/index.d.ts.map +1 -1
  86. package/package.json +3 -3
  87. package/src/expressiveMoneyInput/ExpressiveMoneyInput.autofocus.docs.mdx +12 -0
  88. package/src/expressiveMoneyInput/ExpressiveMoneyInput.css +58 -0
  89. package/src/expressiveMoneyInput/ExpressiveMoneyInput.less +13 -0
  90. package/src/expressiveMoneyInput/ExpressiveMoneyInput.messages.ts +13 -0
  91. package/src/expressiveMoneyInput/ExpressiveMoneyInput.story.tsx +232 -0
  92. package/src/expressiveMoneyInput/ExpressiveMoneyInput.tsx +156 -0
  93. package/src/expressiveMoneyInput/amountInput/AmountInput.css +32 -0
  94. package/src/expressiveMoneyInput/amountInput/AmountInput.less +43 -0
  95. package/src/expressiveMoneyInput/amountInput/AmountInput.tsx +353 -0
  96. package/src/expressiveMoneyInput/amountInput/utils.spec.ts +114 -0
  97. package/src/expressiveMoneyInput/amountInput/utils.ts +116 -0
  98. package/src/expressiveMoneyInput/animatedNumber/AnimatedNumber.tsx +40 -0
  99. package/src/expressiveMoneyInput/chevron/Chevron.css +12 -0
  100. package/src/expressiveMoneyInput/chevron/Chevron.less +13 -0
  101. package/src/expressiveMoneyInput/chevron/Chevron.tsx +35 -0
  102. package/src/expressiveMoneyInput/currencySelector/CurrencySelector.css +6 -0
  103. package/src/expressiveMoneyInput/currencySelector/CurrencySelector.less +7 -0
  104. package/src/expressiveMoneyInput/currencySelector/CurrencySelector.tsx +220 -0
  105. package/src/expressiveMoneyInput/hooks/useFocus.ts +35 -0
  106. package/src/expressiveMoneyInput/hooks/useInputStyle.ts +85 -0
  107. package/src/expressiveMoneyInput/hooks/useSelectionRange.ts +23 -0
  108. package/src/expressiveMoneyInput/index.ts +2 -0
  109. package/src/i18n/en.json +2 -0
  110. package/src/index.ts +2 -0
  111. package/src/main.css +73 -7
  112. package/src/main.less +1 -0
  113. package/src/moneyInput/MoneyInput.css +8 -0
  114. package/src/moneyInput/MoneyInput.less +5 -0
  115. package/src/prompt/InlinePrompt/InlinePrompt.css +7 -7
  116. package/src/prompt/InlinePrompt/InlinePrompt.less +7 -7
  117. package/src/prompt/InlinePrompt/InlinePrompt.spec.tsx +6 -0
  118. package/src/prompt/InlinePrompt/InlinePrompt.story.tsx +39 -0
  119. package/src/prompt/InlinePrompt/InlinePrompt.tsx +12 -2
  120. package/src/ssr.spec.tsx +1 -0
@@ -0,0 +1,220 @@
1
+ import type { AvatarLayoutProps } from '../../avatarLayout';
2
+ import Button from '../../button';
3
+ import {
4
+ SelectInput,
5
+ SelectInputOptionContent,
6
+ SelectInputTriggerButton,
7
+ } from '../../inputs/SelectInput';
8
+ import { CurrencyType, Props as ExpressiveMoneyInputProps } from '../ExpressiveMoneyInput';
9
+ import { ChevronDown } from '@transferwise/icons';
10
+ import { Flag } from '@wise/art';
11
+ import {
12
+ type ButtonHTMLAttributes,
13
+ forwardRef,
14
+ type MouseEventHandler,
15
+ useMemo,
16
+ useState,
17
+ } from 'react';
18
+ import { useIntl } from 'react-intl';
19
+
20
+ import messages from '../ExpressiveMoneyInput.messages';
21
+
22
+ export interface CurrencyOption {
23
+ label?: string;
24
+ code: string;
25
+ keywords: string[] | undefined;
26
+ }
27
+
28
+ export interface CurrencySection {
29
+ title: string;
30
+ currencies: CurrencyOption[];
31
+ }
32
+
33
+ export type CurrencyOptions = CurrencySection[];
34
+
35
+ export type Props = {
36
+ id: string;
37
+ labelId: string;
38
+ options?: CurrencyOptions;
39
+ onChange?: (currency: CurrencyType) => void;
40
+ onOpen?: () => void;
41
+ addons?: AvatarLayoutProps['avatars'];
42
+ onSearchChange?: (payload: { query: string; resultCount: number }) => void;
43
+ } & Pick<ExpressiveMoneyInputProps, 'currency'>;
44
+
45
+ export const CurrencySelector = ({
46
+ id,
47
+ currency,
48
+ options = [],
49
+ labelId,
50
+ onChange,
51
+ addons,
52
+ onOpen,
53
+ onSearchChange,
54
+ }: Props) => {
55
+ const intl = useIntl();
56
+
57
+ const allCurrencyOptions = useMemo(() => getUniqueCurrencies(options), [options]);
58
+
59
+ const activeCurrencyOption = useMemo(() => {
60
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
61
+ return allCurrencyOptions.find((option) => option.code === currency)!;
62
+ }, [currency, allCurrencyOptions]);
63
+
64
+ const disabled =
65
+ !onChange ||
66
+ options.length === 0 ||
67
+ (options.length === 1 && options[0].currencies.length <= 1);
68
+
69
+ const [searchQuery, setSearchQuery] = useState<string>('');
70
+
71
+ const handleTriggerClick: MouseEventHandler = (event) => {
72
+ const triggerEl = event.currentTarget;
73
+ if (triggerEl?.getAttribute('aria-expanded') === 'false') {
74
+ onOpen?.();
75
+ }
76
+ };
77
+
78
+ const items = searchQuery
79
+ ? filterAndSortCurrenciesForQuery(allCurrencyOptions, searchQuery).map(getCurrencySelectOption)
80
+ : options.map(getCurrencyGroup);
81
+
82
+ return (
83
+ <SelectInput
84
+ compareValues="code"
85
+ disabled={disabled}
86
+ id={id}
87
+ value={activeCurrencyOption}
88
+ filterable
89
+ filterPlaceholder={intl.formatMessage(messages.currencySelectorSearchPlaceholder)}
90
+ UNSAFE_triggerButtonProps={{
91
+ id: undefined,
92
+ 'aria-labelledby': undefined,
93
+ 'aria-describedby': labelId,
94
+ 'aria-invalid': undefined,
95
+ 'aria-label': intl.formatMessage(messages.currencySelectorSelectCurrency),
96
+ }}
97
+ items={items}
98
+ renderValue={({ code, label }) => {
99
+ return (
100
+ <SelectInputOptionContent
101
+ title={code}
102
+ note={label}
103
+ icon={<Flag code={code} intrinsicSize={24} />}
104
+ />
105
+ );
106
+ }}
107
+ renderTrigger={() => (
108
+ <SelectInputTriggerButton
109
+ as={ButtonInput}
110
+ // @ts-expect-error new (v2) ButtonProps
111
+ addonStart={{
112
+ type: 'avatar',
113
+ value: [
114
+ addons ? addons[0] : null,
115
+ {
116
+ ...(addons && addons.length > 1
117
+ ? { ...addons[1] }
118
+ : {
119
+ asset: <Flag code={currency} />,
120
+ }),
121
+ },
122
+ ]
123
+ .filter(Boolean)
124
+ .filter((avatar) => !(avatar && Object.keys(avatar).length === 0)),
125
+ }}
126
+ addonEnd={disabled ? undefined : { type: 'icon', value: <ChevronDown /> }}
127
+ onClick={(event) => handleTriggerClick(event)}
128
+ >
129
+ {currency}
130
+ </SelectInputTriggerButton>
131
+ )}
132
+ onChange={(newValue) => {
133
+ onChange?.(newValue.code);
134
+ }}
135
+ onFilterChange={({ queryNormalized }) => {
136
+ setSearchQuery(queryNormalized ?? '');
137
+ if (queryNormalized) {
138
+ onSearchChange?.({
139
+ query: queryNormalized,
140
+ resultCount: filterAndSortCurrenciesForQuery(allCurrencyOptions, queryNormalized)
141
+ .length,
142
+ });
143
+ }
144
+ }}
145
+ />
146
+ );
147
+ };
148
+
149
+ export const ButtonInput = forwardRef(function ButtonInput(
150
+ { children, ...rest }: React.PropsWithChildren<ButtonHTMLAttributes<HTMLButtonElement>>,
151
+ ref: React.ForwardedRef<HTMLButtonElement | null>,
152
+ ) {
153
+ return (
154
+ <Button
155
+ ref={ref}
156
+ size="md"
157
+ v2
158
+ className="wds-currency-selector"
159
+ priority="secondary-neutral"
160
+ {...rest}
161
+ >
162
+ {children}
163
+ </Button>
164
+ );
165
+ });
166
+
167
+ const getCurrencySelectOption = (currency: CurrencyOption) => {
168
+ return {
169
+ type: 'option' as const,
170
+ value: currency,
171
+ filterMatchers: currency.keywords,
172
+ };
173
+ };
174
+
175
+ const getCurrencyGroup = (section: CurrencySection) => {
176
+ return {
177
+ type: 'group' as const,
178
+ label: section.title,
179
+ options: section.currencies.map(getCurrencySelectOption),
180
+ };
181
+ };
182
+
183
+ const getUniqueCurrencies = (options: CurrencyOptions) => {
184
+ const allCurrencyOptions = options.flatMap((section) => section.currencies);
185
+ const uniqueCurrencies = new Map<string, CurrencyOption>();
186
+
187
+ allCurrencyOptions.forEach((currencyObj) => {
188
+ uniqueCurrencies.set(currencyObj.code, currencyObj);
189
+ });
190
+
191
+ return Array.from(uniqueCurrencies.values());
192
+ };
193
+
194
+ const filterAndSortCurrenciesForQuery = (
195
+ currencies: CurrencyOption[],
196
+ query: string,
197
+ ): CurrencyOption[] => {
198
+ return (
199
+ currencies
200
+ .filter((currency) => {
201
+ return (
202
+ currency.code.toLowerCase().includes(query) ||
203
+ (currency.label ?? '').toLowerCase().includes(query) ||
204
+ currency.keywords?.some((keyword) => keyword.toLowerCase().includes(query))
205
+ );
206
+ })
207
+ // prefer exact matches, then sort alphabetically by code
208
+ .sort((a, b) => {
209
+ const aCode = a.code.toLowerCase();
210
+ const bCode = b.code.toLowerCase();
211
+ if (aCode === query) {
212
+ return -1;
213
+ }
214
+ if (bCode === query) {
215
+ return 1;
216
+ }
217
+ return aCode.localeCompare(bCode);
218
+ })
219
+ );
220
+ };
@@ -0,0 +1,35 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ const focusTimeout = 5 * 1000;
4
+
5
+ export const useFocus = () => {
6
+ const [focus, setFocus] = useState(false);
7
+ const [visualFocus, setVisualFocus] = useState(false);
8
+ const [counter, setCounter] = useState(0);
9
+
10
+ useEffect(() => {
11
+ if (focus) {
12
+ const timeout = setTimeout(() => {
13
+ setVisualFocus(false);
14
+ }, focusTimeout);
15
+ return () => clearTimeout(timeout);
16
+ }
17
+ }, [focus, counter]);
18
+
19
+ return {
20
+ focus,
21
+ setFocus: (value: boolean) => {
22
+ setFocus(value);
23
+ if (value) {
24
+ setVisualFocus(true);
25
+ }
26
+ // any call to setFocus should reset the timeout, even if the input was already in focus
27
+ // updating the counter will trigger the useEffect to reset the timeout
28
+ setCounter((prev) => prev + 1);
29
+ },
30
+ visualFocus,
31
+ setVisualFocus: (value: boolean) => {
32
+ setVisualFocus(value);
33
+ },
34
+ };
35
+ };
@@ -0,0 +1,85 @@
1
+ import { type CSSProperties, useEffect, useLayoutEffect, useState } from 'react';
2
+ import { Props as ExpressiveMoneyInputProps } from '../ExpressiveMoneyInput';
3
+
4
+ type InputStyleObject = {
5
+ value: string;
6
+ focus: boolean;
7
+ inputElement: HTMLInputElement | null;
8
+ } & Pick<ExpressiveMoneyInputProps, 'loading'>;
9
+
10
+ export const useInputStyle = ({ value, focus, inputElement, loading }: InputStyleObject) => {
11
+ const initialRender = !useTimeout(300);
12
+ const inputWidth = useFirstDefinedValue(inputElement?.clientWidth, [inputElement, value]);
13
+
14
+ const getStyle = (): CSSProperties => {
15
+ const fontSize = getFontSize(value, focus, inputWidth);
16
+
17
+ return {
18
+ fontSize,
19
+ height: fontSize,
20
+ // aligns the top of the digit with the currency button
21
+ marginTop: fontSize * -0.19,
22
+ // if the input loads with a pre-filled value, we don't want to animate the font-size immediately
23
+ transition: initialRender ? 'none' : undefined,
24
+ color: loading ? 'var(--color-interactive-secondary)' : undefined,
25
+ };
26
+ };
27
+
28
+ const [style, setStyle] = useState(getStyle());
29
+
30
+ useLayoutEffect(() => {
31
+ setStyle(getStyle());
32
+ }, [value, focus, loading, inputWidth]);
33
+
34
+ return style;
35
+ };
36
+
37
+ function getFontSize(inputValue: string, isFocused: boolean, inputWidth: number | undefined) {
38
+ const defaultFontSize = 52;
39
+ const focusFontSize = 90;
40
+ const minimumFontSize = 34;
41
+
42
+ let fontSize = isFocused ? focusFontSize : defaultFontSize;
43
+
44
+ if (typeof inputWidth === 'undefined') {
45
+ return fontSize;
46
+ }
47
+ const textLength = inputValue.length;
48
+ const maxCharactersWithoutShrinking = Math.floor(inputWidth / 40);
49
+
50
+ if (textLength > maxCharactersWithoutShrinking) {
51
+ const adjustedSize = Math.round((inputWidth / textLength) * 1.9);
52
+ fontSize = Math.min(fontSize, adjustedSize);
53
+ }
54
+
55
+ return Math.max(fontSize, minimumFontSize);
56
+ }
57
+
58
+ const useFirstDefinedValue = (newValue: number | undefined, dependencies: unknown[]) => {
59
+ const [value, setValue] = useState<number | undefined>(newValue);
60
+
61
+ useLayoutEffect(() => {
62
+ if (typeof newValue !== 'undefined' && typeof value === 'undefined') {
63
+ setValue(newValue);
64
+ }
65
+ // eslint-disable-next-line react-hooks/exhaustive-deps
66
+ }, [...dependencies, value]);
67
+
68
+ return value;
69
+ };
70
+
71
+ const useTimeout = (delay: number) => {
72
+ const [ready, setReady] = useState(false);
73
+
74
+ useEffect(() => {
75
+ const timeout = setTimeout(() => {
76
+ setReady(true);
77
+ }, delay);
78
+
79
+ return () => {
80
+ clearTimeout(timeout);
81
+ };
82
+ }, [delay]);
83
+
84
+ return ready;
85
+ };
@@ -0,0 +1,23 @@
1
+ import { type SyntheticEvent, useRef } from 'react';
2
+
3
+ export const useSelectionRange = () => {
4
+ const selection = useRef<{
5
+ selectionStart: number | null;
6
+ selectionEnd: number | null;
7
+ }>();
8
+
9
+ const handleSelect = (e: SyntheticEvent<HTMLInputElement>) => {
10
+ const input = e.target as HTMLInputElement;
11
+ const { selectionStart, selectionEnd } = input;
12
+ selection.current = { selectionStart, selectionEnd };
13
+ };
14
+ const handleSelectionBlur = () => {
15
+ selection.current = undefined;
16
+ };
17
+
18
+ return {
19
+ selection: selection.current,
20
+ handleSelect,
21
+ handleSelectionBlur,
22
+ };
23
+ };
@@ -0,0 +1,2 @@
1
+ export type { Props as ExpressiveMoneyInputProps } from './ExpressiveMoneyInput';
2
+ export { default } from './ExpressiveMoneyInput';
package/src/i18n/en.json CHANGED
@@ -16,6 +16,8 @@
16
16
  "neptune.DateLookup.selected": "selected",
17
17
  "neptune.DateLookup.twentyYears": "20 years",
18
18
  "neptune.DateLookup.year": "year",
19
+ "neptune.ExpressiveMoneyInput.currency.search.placeholder": "Type a currency / country",
20
+ "neptune.ExpressiveMoneyInput.currency.select.currency": "Select currency",
19
21
  "neptune.FlowNavigation.back": "back to previous step",
20
22
  "neptune.Info.ariaLabel": "More information",
21
23
  "neptune.Label.optional": "(Optional)",
package/src/index.ts CHANGED
@@ -62,6 +62,7 @@ export type {
62
62
  CurrencyOptionItem,
63
63
  MoneyInputProps,
64
64
  } from './moneyInput';
65
+ export type { ExpressiveMoneyInputProps } from './expressiveMoneyInput';
65
66
  export type { NavigationOptionListProps } from './navigationOptionsList';
66
67
  export type { NavigationOptionProps } from './navigationOption/NavigationOption';
67
68
  export type { OverlayHeaderProps } from './overlayHeader';
@@ -188,6 +189,7 @@ export { default as Markdown } from './markdown';
188
189
  export { default as Modal } from './modal';
189
190
  export { default as Money } from './money';
190
191
  export { default as MoneyInput } from './moneyInput';
192
+ export { default as ExpressiveMoneyInput } from './expressiveMoneyInput';
191
193
  export { default as NavigationOption } from './navigationOption';
192
194
  export { default as NavigationOptionsList } from './navigationOptionsList';
193
195
  export { default as Nudge } from './nudge';
package/src/main.css CHANGED
@@ -526,26 +526,26 @@
526
526
  background-color: var(--color-sentiment-positive-secondary-active);
527
527
  }
528
528
  .wds-inline-prompt--proposition {
529
- background-color: var(--color-sentiment-positive-secondary);
530
- color: var(--color-sentiment-positive-primary);
529
+ background-color: #D2F9F7;
530
+ color: var(--color-interactive-primary);
531
531
  }
532
532
  .wds-inline-prompt--proposition a,
533
533
  .wds-inline-prompt--proposition button {
534
- color: var(--color-sentiment-positive-primary);
534
+ color: var(--color-interactive-primary);
535
535
  }
536
536
  .wds-inline-prompt--proposition a:hover,
537
537
  .wds-inline-prompt--proposition button:hover {
538
- color: var(--color-sentiment-positive-primary-hover);
538
+ color: var(--color-interactive-primary-hover);
539
539
  }
540
540
  .wds-inline-prompt--proposition a:active,
541
541
  .wds-inline-prompt--proposition button:active {
542
- color: var(--color-sentiment-positive-primary-active);
542
+ color: var(--color-interactive-primary-active);
543
543
  }
544
544
  .wds-inline-prompt.wds-inline-prompt--proposition:has(a, button):hover {
545
- background-color: var(--color-sentiment-positive-secondary-hover);
545
+ background-color: #B2F4F3;
546
546
  }
547
547
  .wds-inline-prompt.wds-inline-prompt--proposition:has(a, button):active {
548
- background-color: var(--color-sentiment-positive-secondary-active);
548
+ background-color: #91F0EE;
549
549
  }
550
550
  .wds-inline-prompt--neutral {
551
551
  background-color: rgba(134,167,189,0.10196);
@@ -4421,6 +4421,14 @@ button.np-link {
4421
4421
  flex-shrink: 0;
4422
4422
  width: auto;
4423
4423
  }
4424
+ .tw-money-input .input-group-addon:not(.amount-currency-select-btn):not(.tw-money-input__fixed-currency):not(:has(~ .tw-money-input__fixed-currency)) {
4425
+ padding-right: 0 !important;
4426
+ }
4427
+ [dir="rtl"] .tw-money-input .input-group-addon:not(.amount-currency-select-btn):not(.tw-money-input__fixed-currency):not(:has(~ .tw-money-input__fixed-currency)) {
4428
+ padding-left: 0 !important;
4429
+ padding-right: 0 !important;
4430
+ padding-right: initial !important;
4431
+ }
4424
4432
  .tw-money-input .amount-currency-select-btn {
4425
4433
  flex-shrink: 0;
4426
4434
  width: auto;
@@ -4447,6 +4455,64 @@ button.np-link {
4447
4455
  box-shadow: inset 0 0 0 1px #c9cbce !important;
4448
4456
  box-shadow: inset 0 0 0 1px var(--color-interactive-secondary) !important;
4449
4457
  }
4458
+ .wds-amount-input-container {
4459
+ width: 100%;
4460
+ }
4461
+ .wds-amount-input-input-container {
4462
+ display: flex;
4463
+ justify-content: right;
4464
+ width: 100%;
4465
+ 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;
4466
+ color: var(--color-interactive-primary);
4467
+ overflow: hidden;
4468
+ margin-bottom: 0 !important;
4469
+ }
4470
+ @media (prefers-reduced-motion: reduce) {
4471
+ .wds-amount-input-input-container {
4472
+ transition: none;
4473
+ }
4474
+ }
4475
+ .wds-amount-input-input {
4476
+ border: none;
4477
+ outline: none;
4478
+ flex-grow: 1;
4479
+ text-align: right;
4480
+ background-color: transparent;
4481
+ }
4482
+ .wds-amount-input-input:focus-visible {
4483
+ outline: none;
4484
+ }
4485
+ .wds-amount-input-placeholder {
4486
+ flex-grow: 0;
4487
+ display: flex;
4488
+ align-items: center;
4489
+ }
4490
+ .wds-currency-selector:disabled {
4491
+ opacity: 1 !important;
4492
+ cursor: auto !important;
4493
+ cursor: initial !important;
4494
+ mix-blend-mode: initial !important;
4495
+ }
4496
+ .wds-chevron-container {
4497
+ width: 32px;
4498
+ width: var(--size-32);
4499
+ overflow: hidden;
4500
+ color: var(--color-interactive-primary);
4501
+ margin-left: 8px;
4502
+ margin-left: var(--size-8);
4503
+ transition: width 0.3s ease;
4504
+ }
4505
+ .wds-chevron-hidden {
4506
+ width: 0;
4507
+ }
4508
+ .wds-expressive-money-input-currency-selector {
4509
+ flex-shrink: 0;
4510
+ margin-right: 24px;
4511
+ margin-right: var(--size-24);
4512
+ }
4513
+ .wds-expressive-money-input-chevron {
4514
+ transform: translateY(-5%);
4515
+ }
4450
4516
  .np-navigation-option {
4451
4517
  -webkit-text-decoration: none;
4452
4518
  text-decoration: none;
package/src/main.less CHANGED
@@ -51,6 +51,7 @@
51
51
  @import "./logo/Logo.less";
52
52
  @import "./modal/Modal.less";
53
53
  @import "./moneyInput/MoneyInput.less";
54
+ @import "./expressiveMoneyInput/ExpressiveMoneyInput.less";
54
55
  @import "./navigationOption/NavigationOption.less";
55
56
  @import "./navigationOptionsList/NavigationOptionsList.less";
56
57
  @import "./nudge/Nudge.less";
@@ -38,6 +38,14 @@
38
38
  flex-shrink: 0;
39
39
  width: auto;
40
40
  }
41
+ .tw-money-input .input-group-addon:not(.amount-currency-select-btn):not(.tw-money-input__fixed-currency):not(:has(~ .tw-money-input__fixed-currency)) {
42
+ padding-right: 0 !important;
43
+ }
44
+ [dir="rtl"] .tw-money-input .input-group-addon:not(.amount-currency-select-btn):not(.tw-money-input__fixed-currency):not(:has(~ .tw-money-input__fixed-currency)) {
45
+ padding-left: 0 !important;
46
+ padding-right: 0 !important;
47
+ padding-right: initial !important;
48
+ }
41
49
  .tw-money-input .amount-currency-select-btn {
42
50
  flex-shrink: 0;
43
51
  width: auto;
@@ -32,11 +32,16 @@
32
32
 
33
33
  .input-group-addon {
34
34
  flex-shrink: 0;
35
+
35
36
  &:not(.amount-currency-select-btn) {
36
37
  display: flex;
37
38
  align-items: center;
38
39
  flex-shrink: 0;
39
40
  width: auto;
41
+
42
+ &:not(.tw-money-input__fixed-currency):not(:has(~ .tw-money-input__fixed-currency)) {
43
+ .padding(right, 0) !important;
44
+ }
40
45
  }
41
46
  }
42
47
 
@@ -91,26 +91,26 @@
91
91
  background-color: var(--color-sentiment-positive-secondary-active);
92
92
  }
93
93
  .wds-inline-prompt--proposition {
94
- background-color: var(--color-sentiment-positive-secondary);
95
- color: var(--color-sentiment-positive-primary);
94
+ background-color: #D2F9F7;
95
+ color: var(--color-interactive-primary);
96
96
  }
97
97
  .wds-inline-prompt--proposition a,
98
98
  .wds-inline-prompt--proposition button {
99
- color: var(--color-sentiment-positive-primary);
99
+ color: var(--color-interactive-primary);
100
100
  }
101
101
  .wds-inline-prompt--proposition a:hover,
102
102
  .wds-inline-prompt--proposition button:hover {
103
- color: var(--color-sentiment-positive-primary-hover);
103
+ color: var(--color-interactive-primary-hover);
104
104
  }
105
105
  .wds-inline-prompt--proposition a:active,
106
106
  .wds-inline-prompt--proposition button:active {
107
- color: var(--color-sentiment-positive-primary-active);
107
+ color: var(--color-interactive-primary-active);
108
108
  }
109
109
  .wds-inline-prompt.wds-inline-prompt--proposition:has(a, button):hover {
110
- background-color: var(--color-sentiment-positive-secondary-hover);
110
+ background-color: #B2F4F3;
111
111
  }
112
112
  .wds-inline-prompt.wds-inline-prompt--proposition:has(a, button):active {
113
- background-color: var(--color-sentiment-positive-secondary-active);
113
+ background-color: #91F0EE;
114
114
  }
115
115
  .wds-inline-prompt--neutral {
116
116
  background-color: rgba(134,167,189,0.10196);
@@ -94,26 +94,26 @@
94
94
  }
95
95
 
96
96
  &--proposition {
97
- background-color: var(--color-sentiment-positive-secondary);
97
+ background-color: #D2F9F7;
98
98
 
99
- color: var(--color-sentiment-positive-primary);
99
+ color: var(--color-interactive-primary);
100
100
 
101
101
  a, button {
102
- color: var(--color-sentiment-positive-primary);
102
+ color: var(--color-interactive-primary);
103
103
  &:hover {
104
- color: var(--color-sentiment-positive-primary-hover);
104
+ color: var(--color-interactive-primary-hover);
105
105
  }
106
106
  &:active {
107
- color: var(--color-sentiment-positive-primary-active);
107
+ color: var(--color-interactive-primary-active);
108
108
  }
109
109
  }
110
110
 
111
111
  .wds-inline-prompt&:has(a, button) {
112
112
  &:hover {
113
- background-color: var(--color-sentiment-positive-secondary-hover);
113
+ background-color: #B2F4F3;
114
114
  }
115
115
  &:active {
116
- background-color: var(--color-sentiment-positive-secondary-active);
116
+ background-color: #91F0EE;
117
117
  }
118
118
  }
119
119
  }
@@ -15,6 +15,12 @@ describe('InlinePrompt', () => {
15
15
  expect(screen.getByText('Hello world')).toBeInTheDocument();
16
16
  });
17
17
 
18
+ it('renders with GiftBox icon as default for `proposition` sentiment', () => {
19
+ render(<InlinePrompt {...defaultProps} sentiment="proposition" />);
20
+ expect(screen.getByText('Prompt message')).toBeInTheDocument();
21
+ expect(screen.getByTestId('gift-box-icon')).toBeInTheDocument();
22
+ });
23
+
18
24
  describe('renders with each sentiment', () => {
19
25
  it.each([Sentiment.POSITIVE, Sentiment.NEUTRAL, Sentiment.NEGATIVE, Sentiment.WARNING])(
20
26
  'renders with sentiment %s',
@@ -0,0 +1,39 @@
1
+ import { lorem10, lorem5 } from '../../test-utils';
2
+ import { InlinePrompt } from './InlinePrompt';
3
+ import { CardWise, Rewards } from '@transferwise/icons';
4
+
5
+ export default {
6
+ title: 'Internal/InlinePrompt',
7
+ component: InlinePrompt,
8
+ };
9
+
10
+ export const AllVariants = () => {
11
+ return (
12
+ <div
13
+ style={{
14
+ display: 'grid',
15
+ gridAutoFlow: 'row',
16
+ rowGap: 'var(--size-12)',
17
+ justifyItems: 'start',
18
+ width: 'fit-content',
19
+ }}
20
+ >
21
+ <InlinePrompt sentiment="positive">{lorem5}</InlinePrompt>
22
+ <InlinePrompt media={<Rewards />} sentiment="positive">
23
+ {lorem10}
24
+ </InlinePrompt>
25
+ <InlinePrompt sentiment="negative">{lorem5}</InlinePrompt>
26
+ <InlinePrompt sentiment="negative">{lorem10}</InlinePrompt>
27
+ <InlinePrompt sentiment="neutral">{lorem5}</InlinePrompt>
28
+ <InlinePrompt sentiment="neutral">{lorem10}</InlinePrompt>
29
+ <InlinePrompt sentiment="warning">{lorem5}</InlinePrompt>
30
+ <InlinePrompt sentiment="warning">{lorem10}</InlinePrompt>
31
+ <InlinePrompt sentiment="proposition">{lorem5}</InlinePrompt>
32
+ <InlinePrompt sentiment="proposition">{lorem10}</InlinePrompt>
33
+ <InlinePrompt sentiment="proposition" media={<CardWise />}>
34
+ {lorem10}
35
+ </InlinePrompt>
36
+ <InlinePrompt>{lorem5}</InlinePrompt>
37
+ </div>
38
+ );
39
+ };