@transferwise/components 46.155.0 → 46.155.1
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/AvatarLayout/AvatarLayout.js.map +1 -1
- package/build/AvatarLayout/AvatarLayout.mjs.map +1 -1
- package/build/AvatarView/AvatarView.js.map +1 -1
- package/build/AvatarView/AvatarView.mjs.map +1 -1
- package/build/AvatarView/Dot/Dot.js.map +1 -1
- package/build/AvatarView/Dot/Dot.mjs.map +1 -1
- package/build/Body/Body.js.map +1 -1
- package/build/Body/Body.mjs.map +1 -1
- package/build/Chips/Chip/Chip.js.map +1 -1
- package/build/Chips/Chip/Chip.mjs.map +1 -1
- package/build/DefinitionList/DefinitionList.js +2 -1
- package/build/DefinitionList/DefinitionList.js.map +1 -1
- package/build/DefinitionList/DefinitionList.mjs +2 -1
- package/build/DefinitionList/DefinitionList.mjs.map +1 -1
- package/build/Display/Display.js.map +1 -1
- package/build/Display/Display.mjs.map +1 -1
- package/build/ExpressiveMoneyInput/AmountInput/AmountInput.js +2 -2
- package/build/ExpressiveMoneyInput/AmountInput/AmountInput.js.map +1 -1
- package/build/ExpressiveMoneyInput/AmountInput/AmountInput.mjs +1 -1
- package/build/ExpressiveMoneyInput/AmountInput/AmountInput.mjs.map +1 -1
- package/build/ExpressiveMoneyInput/AnimatedNumber/AnimatedNumber.js +4 -4
- package/build/ExpressiveMoneyInput/AnimatedNumber/AnimatedNumber.js.map +1 -1
- package/build/ExpressiveMoneyInput/AnimatedNumber/AnimatedNumber.mjs +1 -1
- package/build/ExpressiveMoneyInput/AnimatedNumber/AnimatedNumber.mjs.map +1 -1
- package/build/ExpressiveMoneyInput/Chevron/Chevron.js +2 -2
- package/build/ExpressiveMoneyInput/Chevron/Chevron.js.map +1 -1
- package/build/ExpressiveMoneyInput/Chevron/Chevron.mjs +1 -1
- package/build/ExpressiveMoneyInput/Chevron/Chevron.mjs.map +1 -1
- package/build/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.js.map +1 -1
- package/build/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.mjs.map +1 -1
- package/build/ExpressiveMoneyInput/ExpressiveMoneyInput.js +3 -3
- package/build/ExpressiveMoneyInput/ExpressiveMoneyInput.js.map +1 -1
- package/build/ExpressiveMoneyInput/ExpressiveMoneyInput.mjs +1 -1
- package/build/ExpressiveMoneyInput/ExpressiveMoneyInput.mjs.map +1 -1
- package/build/ExpressiveMoneyInput/hooks/useInputStyle.js.map +1 -1
- package/build/ExpressiveMoneyInput/hooks/useInputStyle.mjs.map +1 -1
- package/build/IconButton/IconButton.js.map +1 -1
- package/build/IconButton/IconButton.mjs.map +1 -1
- package/build/Link/Link.js.map +1 -1
- package/build/Link/Link.mjs.map +1 -1
- package/build/Nudge/Nudge.js +11 -2
- package/build/Nudge/Nudge.js.map +1 -1
- package/build/Nudge/Nudge.mjs +12 -3
- package/build/Nudge/Nudge.mjs.map +1 -1
- package/build/Sticky/Sticky.js +1 -0
- package/build/Sticky/Sticky.js.map +1 -1
- package/build/Sticky/Sticky.mjs +1 -0
- package/build/Sticky/Sticky.mjs.map +1 -1
- package/build/Title/Title.js +1 -0
- package/build/Title/Title.js.map +1 -1
- package/build/Title/Title.mjs +1 -0
- package/build/Title/Title.mjs.map +1 -1
- package/build/_deprecated/Select/Option/Option.js.map +1 -1
- package/build/_deprecated/Select/Option/Option.mjs.map +1 -1
- package/build/_deprecated/Select/SearchBox/SearchBox.js.map +1 -1
- package/build/_deprecated/Select/SearchBox/SearchBox.mjs.map +1 -1
- package/build/common/Circle/Circle.js.map +1 -1
- package/build/common/Circle/Circle.mjs.map +1 -1
- package/build/main.css +36 -1
- package/build/styles/DefinitionList/DefinitionList.css +30 -0
- package/build/styles/Nudge/Nudge.css +1 -1
- package/build/styles/Sticky/Sticky.css +4 -0
- package/build/styles/main.css +36 -1
- package/build/types/AvatarLayout/AvatarLayout.d.ts +2 -2
- package/build/types/AvatarLayout/AvatarLayout.d.ts.map +1 -1
- package/build/types/AvatarLayout/index.d.ts +1 -1
- package/build/types/AvatarLayout/index.d.ts.map +1 -1
- package/build/types/AvatarView/AvatarView.d.ts +2 -2
- package/build/types/AvatarView/AvatarView.d.ts.map +1 -1
- package/build/types/AvatarView/Dot/Dot.d.ts +1 -1
- package/build/types/AvatarView/Dot/Dot.d.ts.map +1 -1
- package/build/types/AvatarView/index.d.ts +1 -1
- package/build/types/AvatarView/index.d.ts.map +1 -1
- package/build/types/Body/Body.d.ts +11 -0
- package/build/types/Body/Body.d.ts.map +1 -1
- package/build/types/Body/index.d.ts +1 -0
- package/build/types/Body/index.d.ts.map +1 -1
- package/build/types/Chips/Chip/Chip.d.ts +2 -2
- package/build/types/Chips/Chip/Chip.d.ts.map +1 -1
- package/build/types/Chips/index.d.ts +1 -1
- package/build/types/Chips/index.d.ts.map +1 -1
- package/build/types/DefinitionList/DefinitionList.d.ts.map +1 -1
- package/build/types/Display/Display.d.ts +2 -2
- package/build/types/Display/Display.d.ts.map +1 -1
- package/build/types/Display/index.d.ts +1 -0
- package/build/types/Display/index.d.ts.map +1 -1
- package/build/types/ExpressiveMoneyInput/AmountInput/AmountInput.d.ts +3 -3
- package/build/types/ExpressiveMoneyInput/AmountInput/AmountInput.d.ts.map +1 -1
- package/build/types/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.d.ts +3 -3
- package/build/types/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.d.ts.map +1 -1
- package/build/types/ExpressiveMoneyInput/ExpressiveMoneyInput.d.ts +4 -4
- package/build/types/ExpressiveMoneyInput/ExpressiveMoneyInput.d.ts.map +1 -1
- package/build/types/ExpressiveMoneyInput/hooks/useInputStyle.d.ts +1 -1
- package/build/types/ExpressiveMoneyInput/hooks/useInputStyle.d.ts.map +1 -1
- package/build/types/ExpressiveMoneyInput/index.d.ts +1 -1
- package/build/types/ExpressiveMoneyInput/index.d.ts.map +1 -1
- package/build/types/IconButton/IconButton.d.ts +1 -1
- package/build/types/IconButton/IconButton.d.ts.map +1 -1
- package/build/types/IconButton/index.d.ts +1 -1
- package/build/types/IconButton/index.d.ts.map +1 -1
- package/build/types/Link/Link.d.ts +2 -2
- package/build/types/Link/Link.d.ts.map +1 -1
- package/build/types/Link/index.d.ts +1 -1
- package/build/types/Link/index.d.ts.map +1 -1
- package/build/types/Nudge/Nudge.d.ts +28 -5
- package/build/types/Nudge/Nudge.d.ts.map +1 -1
- package/build/types/Nudge/index.d.ts +1 -1
- package/build/types/Nudge/index.d.ts.map +1 -1
- package/build/types/Sticky/Sticky.d.ts.map +1 -1
- package/build/types/Title/Title.d.ts +12 -2
- package/build/types/Title/Title.d.ts.map +1 -1
- package/build/types/Title/index.d.ts +1 -0
- package/build/types/Title/index.d.ts.map +1 -1
- package/build/types/_deprecated/Select/Option/Option.d.ts +2 -2
- package/build/types/_deprecated/Select/Option/Option.d.ts.map +1 -1
- package/build/types/common/Circle/Circle.d.ts +1 -1
- package/build/types/common/Circle/Circle.d.ts.map +1 -1
- package/build/types/common/Circle/index.d.ts +1 -1
- package/build/types/common/Circle/index.d.ts.map +1 -1
- package/build/types/index.d.ts +4 -0
- package/build/types/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/AvatarLayout/AvatarLayout.tsx +3 -3
- package/src/AvatarLayout/index.ts +1 -1
- package/src/AvatarView/AvatarView.tsx +4 -4
- package/src/AvatarView/Dot/Dot.tsx +1 -1
- package/src/AvatarView/index.ts +1 -1
- package/src/Body/Body.tsx +2 -2
- package/src/Body/index.ts +1 -0
- package/src/Chips/Chip/Chip.tsx +2 -2
- package/src/Chips/index.ts +1 -1
- package/src/DefinitionList/DefinitionList.css +30 -0
- package/src/DefinitionList/DefinitionList.less +24 -0
- package/src/DefinitionList/DefinitionList.tsx +1 -0
- package/src/DefinitionList/_stories/DefinitionList.test.story.tsx +168 -0
- package/src/Display/Display.tsx +2 -2
- package/src/Display/index.ts +1 -0
- package/src/Drawer/Drawer.test.tsx +2 -3
- package/src/ExpressiveMoneyInput/AmountInput/AmountInput.tsx +4 -4
- package/src/ExpressiveMoneyInput/AnimatedNumber/AnimatedNumber.tsx +1 -1
- package/src/ExpressiveMoneyInput/Chevron/Chevron.tsx +1 -1
- package/src/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.tsx +3 -3
- package/src/ExpressiveMoneyInput/ExpressiveMoneyInput.test.tsx +1 -1
- package/src/ExpressiveMoneyInput/ExpressiveMoneyInput.tsx +5 -8
- package/src/ExpressiveMoneyInput/_stories/ExpressiveMoneyInput.story.tsx +1 -1
- package/src/ExpressiveMoneyInput/_stories/ExpressiveMoneyInput.test.story.tsx +1 -1
- package/src/ExpressiveMoneyInput/hooks/useInputStyle.ts +1 -1
- package/src/ExpressiveMoneyInput/index.ts +1 -1
- package/src/IconButton/IconButton.tsx +2 -2
- package/src/IconButton/_stories/IconButton.story.tsx +8 -8
- package/src/IconButton/index.ts +1 -1
- package/src/Link/Link.tsx +2 -2
- package/src/Link/index.ts +1 -1
- package/src/Nudge/Nudge.css +1 -1
- package/src/Nudge/Nudge.less +1 -1
- package/src/Nudge/Nudge.test.tsx +43 -0
- package/src/Nudge/Nudge.tsx +43 -8
- package/src/Nudge/index.ts +1 -1
- package/src/Sticky/Sticky.css +4 -0
- package/src/Sticky/Sticky.less +6 -0
- package/src/Sticky/Sticky.test.tsx +1 -0
- package/src/Sticky/Sticky.tsx +1 -0
- package/src/Sticky/_stories/Sticky.test.story.tsx +177 -0
- package/src/Title/Title.tsx +25 -20
- package/src/Title/index.ts +1 -0
- package/src/_deprecated/LegacyListItem/_stories/LegacyListItem.story.tsx +2 -3
- package/src/_deprecated/Select/Option/Option.test.tsx +1 -1
- package/src/_deprecated/Select/Option/Option.tsx +2 -2
- package/src/_deprecated/Select/SearchBox/SearchBox.tsx +2 -2
- package/src/common/Circle/Circle.tsx +2 -2
- package/src/common/Circle/index.ts +1 -1
- package/src/index.ts +4 -0
- package/src/main.css +36 -1
- package/src/main.less +1 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { formatAmount } from '@transferwise/formatting';
|
|
2
2
|
import { clsx } from 'clsx';
|
|
3
|
-
import { AnimatePresence } from '
|
|
3
|
+
import { AnimatePresence } from 'motion/react';
|
|
4
4
|
import { type ChangeEvent, type KeyboardEvent, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
5
|
import { useIntl } from 'react-intl';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import type { ExpressiveMoneyInputProps } from '../ExpressiveMoneyInput';
|
|
8
8
|
import { AnimatedNumber } from '../AnimatedNumber/AnimatedNumber';
|
|
9
9
|
import { useFocus } from '../hooks/useFocus';
|
|
10
10
|
import { useInputStyle } from '../hooks/useInputStyle';
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
isInputPossiblyOverflowing,
|
|
20
20
|
} from './utils';
|
|
21
21
|
|
|
22
|
-
type
|
|
22
|
+
type AmountInputProps = {
|
|
23
23
|
id: string;
|
|
24
24
|
describedById?: string;
|
|
25
25
|
amount?: number | null;
|
|
@@ -38,7 +38,7 @@ export const AmountInput = ({
|
|
|
38
38
|
onChange,
|
|
39
39
|
onFocusChange,
|
|
40
40
|
loading,
|
|
41
|
-
}:
|
|
41
|
+
}: AmountInputProps) => {
|
|
42
42
|
const intl = useIntl();
|
|
43
43
|
const { focus, setFocus, visualFocus, setVisualFocus } = useFocus();
|
|
44
44
|
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
SelectInputOptionContent,
|
|
6
6
|
SelectInputTriggerButton,
|
|
7
7
|
} from '../../Inputs/SelectInput';
|
|
8
|
-
import { CurrencyType,
|
|
8
|
+
import type { CurrencyType, ExpressiveMoneyInputProps } from '../ExpressiveMoneyInput';
|
|
9
9
|
import { ChevronDown } from '@transferwise/icons';
|
|
10
10
|
import { Flag } from '@wise/art';
|
|
11
11
|
import {
|
|
@@ -34,7 +34,7 @@ export interface CurrencySection {
|
|
|
34
34
|
|
|
35
35
|
export type CurrencyOptions = CurrencySection[];
|
|
36
36
|
|
|
37
|
-
export type
|
|
37
|
+
export type CurrencySelectorProps = {
|
|
38
38
|
id: string;
|
|
39
39
|
labelId: string;
|
|
40
40
|
options?: CurrencyOptions;
|
|
@@ -53,7 +53,7 @@ export const CurrencySelector = ({
|
|
|
53
53
|
addons,
|
|
54
54
|
onOpen,
|
|
55
55
|
onSearchChange,
|
|
56
|
-
}:
|
|
56
|
+
}: CurrencySelectorProps) => {
|
|
57
57
|
const intl = useIntl();
|
|
58
58
|
|
|
59
59
|
const allCurrencyOptions = useMemo(() => getUniqueCurrencies(options), [options]);
|
|
@@ -2,7 +2,7 @@ import { screen, render, mockMatchMedia, mockResizeObserver, waitFor } from '../
|
|
|
2
2
|
import { Sentiment } from '../common';
|
|
3
3
|
|
|
4
4
|
import ExpressiveMoneyInput from './ExpressiveMoneyInput';
|
|
5
|
-
import type {
|
|
5
|
+
import type { ExpressiveMoneyInputProps } from './ExpressiveMoneyInput';
|
|
6
6
|
import { Confetti } from '@transferwise/icons';
|
|
7
7
|
|
|
8
8
|
const defaultProps: ExpressiveMoneyInputProps = {
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import Body from '../Body';
|
|
2
2
|
import { clsx } from 'clsx';
|
|
3
|
-
import { AnimatePresence, motion } from '
|
|
3
|
+
import { AnimatePresence, motion } from 'motion/react';
|
|
4
4
|
import { useId, type ReactNode } from 'react';
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
type Props as CurrencySelectorProps,
|
|
8
|
-
CurrencySelector,
|
|
9
|
-
} from './CurrencySelector/CurrencySelector';
|
|
6
|
+
import { CurrencySelector, type CurrencySelectorProps } from './CurrencySelector/CurrencySelector';
|
|
10
7
|
import { CommonProps } from '../common';
|
|
11
8
|
import { AmountInput } from './AmountInput/AmountInput';
|
|
12
9
|
import { Chevron } from './Chevron/Chevron';
|
|
@@ -26,7 +23,7 @@ type CustomCurrencySelectorInstanceType = {
|
|
|
26
23
|
type CurrencySelectorType = DefaultCurrencySelectorInstanceType &
|
|
27
24
|
CustomCurrencySelectorInstanceType;
|
|
28
25
|
|
|
29
|
-
export type
|
|
26
|
+
export type ExpressiveMoneyInputProps = {
|
|
30
27
|
label?: ReactNode;
|
|
31
28
|
currencySelector?: CurrencySelectorType;
|
|
32
29
|
amount?: AmountType;
|
|
@@ -65,7 +62,7 @@ export type Props = {
|
|
|
65
62
|
* direct contributions by consuming product teams are highly encouraged.
|
|
66
63
|
*
|
|
67
64
|
* > ⚠️ **Prerequisite:** <br />The component depends on the
|
|
68
|
-
* [
|
|
65
|
+
* [motion](https://www.npmjs.com/package/motion) package, which has been
|
|
69
66
|
* deliberately excluded from the Design System bundle and kept as an opt-in only.
|
|
70
67
|
* **Make sure to add it to your project dependencies.**
|
|
71
68
|
*/
|
|
@@ -81,7 +78,7 @@ export default function ExpressiveMoneyInput({
|
|
|
81
78
|
autoFocus,
|
|
82
79
|
loading,
|
|
83
80
|
onFocusChange,
|
|
84
|
-
}:
|
|
81
|
+
}: ExpressiveMoneyInputProps) {
|
|
85
82
|
const inputId = useId();
|
|
86
83
|
const labelId = useId();
|
|
87
84
|
const customAlertId = useId();
|
|
@@ -19,7 +19,7 @@ import Modal from '../../Modal';
|
|
|
19
19
|
import ListItem from '../../ListItem';
|
|
20
20
|
import List from '../../List';
|
|
21
21
|
import ExpressiveMoneyInput, { type ExpressiveMoneyInputProps } from '..';
|
|
22
|
-
import type {
|
|
22
|
+
import type { CurrencySelectorProps } from '../CurrencySelector/CurrencySelector';
|
|
23
23
|
|
|
24
24
|
const getCurrencySelectorProps = ({
|
|
25
25
|
logEvents = false,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
2
|
import { fn } from 'storybook/test';
|
|
3
|
-
import ExpressiveMoneyInput, {
|
|
3
|
+
import ExpressiveMoneyInput, { type ExpressiveMoneyInputProps } from '../ExpressiveMoneyInput';
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof ExpressiveMoneyInput> = {
|
|
6
6
|
component: ExpressiveMoneyInput,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { ExpressiveMoneyInputProps, CurrencyType } from './ExpressiveMoneyInput';
|
|
2
2
|
export { default } from './ExpressiveMoneyInput';
|
|
@@ -3,7 +3,7 @@ import { PrimitiveAnchor, PrimitiveButton } from '../Primitives';
|
|
|
3
3
|
import Circle from '../common/Circle';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
|
|
6
|
-
export type
|
|
6
|
+
export type IconButtonProps = {
|
|
7
7
|
/** @default 48 */
|
|
8
8
|
size?: 16 | 24 | 32 | 40 | 48 | 56 | 72;
|
|
9
9
|
/** @default 'primary' */
|
|
@@ -27,7 +27,7 @@ const IconButton = forwardRef(function IconButton(
|
|
|
27
27
|
priority = 'primary',
|
|
28
28
|
type = 'default',
|
|
29
29
|
...props
|
|
30
|
-
}:
|
|
30
|
+
}: IconButtonProps,
|
|
31
31
|
ref,
|
|
32
32
|
) {
|
|
33
33
|
const isLink = href !== '#';
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
createSandboxStory,
|
|
19
19
|
globalScope,
|
|
20
20
|
} from '../../../.storybook/components/sandbox/SandboxEditor';
|
|
21
|
-
import IconButton, {
|
|
21
|
+
import IconButton, { type IconButtonProps } from '../IconButton';
|
|
22
22
|
import SentimentSurface from '../../SentimentSurface';
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -46,7 +46,7 @@ export default {
|
|
|
46
46
|
|
|
47
47
|
type Story = StoryObj<typeof IconButton>;
|
|
48
48
|
|
|
49
|
-
const sizes:
|
|
49
|
+
const sizes: IconButtonProps['size'][] = [16, 24, 32, 40, 48, 56, 72];
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Explore size, priority, type, and disabled state via the controls panel.
|
|
@@ -107,7 +107,7 @@ export const Priority: Story = {
|
|
|
107
107
|
</div>
|
|
108
108
|
),
|
|
109
109
|
],
|
|
110
|
-
render: (args:
|
|
110
|
+
render: (args: IconButtonProps) => (
|
|
111
111
|
<>
|
|
112
112
|
<IconButton priority="primary" type={args.type} aria-label="Primary" onClick={fn()}>
|
|
113
113
|
<Plus />
|
|
@@ -152,7 +152,7 @@ export const Type: Story = {
|
|
|
152
152
|
</div>
|
|
153
153
|
),
|
|
154
154
|
],
|
|
155
|
-
render: (args:
|
|
155
|
+
render: (args: IconButtonProps) => (
|
|
156
156
|
<>
|
|
157
157
|
<IconButton priority={args.priority} type="default" aria-label="Default" onClick={fn()}>
|
|
158
158
|
<Plus />
|
|
@@ -195,7 +195,7 @@ export const Size: Story = {
|
|
|
195
195
|
</div>
|
|
196
196
|
),
|
|
197
197
|
],
|
|
198
|
-
render: (args:
|
|
198
|
+
render: (args: IconButtonProps) => (
|
|
199
199
|
<>
|
|
200
200
|
{sizes.map((size) => (
|
|
201
201
|
<div
|
|
@@ -254,7 +254,7 @@ export const Disabled: Story = {
|
|
|
254
254
|
target: { table: { disable: true } },
|
|
255
255
|
onClick: { table: { disable: true } },
|
|
256
256
|
},
|
|
257
|
-
render: (args:
|
|
257
|
+
render: (args: IconButtonProps) => (
|
|
258
258
|
<IconButton disabled priority={args.priority} type={args.type} aria-label="Disabled">
|
|
259
259
|
<Plus />
|
|
260
260
|
</IconButton>
|
|
@@ -284,7 +284,7 @@ export const AsAnchor: Story = {
|
|
|
284
284
|
disabled: { table: { disable: true } },
|
|
285
285
|
onClick: { table: { disable: true } },
|
|
286
286
|
},
|
|
287
|
-
render: (args:
|
|
287
|
+
render: (args: IconButtonProps) => (
|
|
288
288
|
<IconButton {...args} aria-label="External link">
|
|
289
289
|
<ArrowLeft />
|
|
290
290
|
</IconButton>
|
|
@@ -305,7 +305,7 @@ export const SentimentAwareness: Story = {
|
|
|
305
305
|
options: [16, 24, 32, 40, 48, 56, 72],
|
|
306
306
|
},
|
|
307
307
|
},
|
|
308
|
-
render: (args:
|
|
308
|
+
render: (args: IconButtonProps) => {
|
|
309
309
|
const priorities = [
|
|
310
310
|
{ key: 'primary', label: 'primary' },
|
|
311
311
|
{ key: 'secondary', label: 'secondary' },
|
package/src/IconButton/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { IconButtonProps } from './IconButton';
|
|
2
2
|
export { default } from './IconButton';
|
package/src/Link/Link.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import { LinkLarge, LinkDefault, Typography } from '../common';
|
|
|
8
8
|
|
|
9
9
|
import messages from './Link.messages';
|
|
10
10
|
|
|
11
|
-
export type
|
|
11
|
+
export type LinkProps = PrimitiveAnchorProps &
|
|
12
12
|
Pick<PrimitiveButtonProps, 'onClick'> & { type?: LinkLarge | LinkDefault };
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -26,7 +26,7 @@ const Link = ({
|
|
|
26
26
|
onClick,
|
|
27
27
|
disabled,
|
|
28
28
|
...props
|
|
29
|
-
}:
|
|
29
|
+
}: LinkProps) => {
|
|
30
30
|
const { formatMessage } = useIntl();
|
|
31
31
|
const isButton = Boolean(onClick) && href === undefined;
|
|
32
32
|
const isBlank = target === '_blank';
|
package/src/Link/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default } from './Link';
|
|
2
|
-
export type {
|
|
2
|
+
export type { LinkProps } from './Link';
|
package/src/Nudge/Nudge.css
CHANGED
package/src/Nudge/Nudge.less
CHANGED
package/src/Nudge/Nudge.test.tsx
CHANGED
|
@@ -42,6 +42,49 @@ describe('Nudge', () => {
|
|
|
42
42
|
expect(defaultProps.onDismiss).toHaveBeenCalledTimes(1);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
+
it('renders action only when both action and link/href are supplied', () => {
|
|
46
|
+
const consoleWarn = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
47
|
+
|
|
48
|
+
render(
|
|
49
|
+
<Nudge
|
|
50
|
+
{...defaultProps}
|
|
51
|
+
action={{
|
|
52
|
+
text: 'Learn more',
|
|
53
|
+
href: '#action-link',
|
|
54
|
+
'aria-label': 'Learn more about rewards',
|
|
55
|
+
}}
|
|
56
|
+
/>,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
expect(screen.queryByText('CTA')).not.toBeInTheDocument();
|
|
60
|
+
|
|
61
|
+
const actionLink = screen.getByText('Learn more');
|
|
62
|
+
expect(actionLink).toBeInTheDocument();
|
|
63
|
+
expect(actionLink).toHaveAttribute('href', '#action-link');
|
|
64
|
+
|
|
65
|
+
consoleWarn.mockRestore();
|
|
66
|
+
});
|
|
67
|
+
it('shows a warning in development when both action and link/href are supplied', () => {
|
|
68
|
+
const consoleWarn = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
69
|
+
|
|
70
|
+
render(
|
|
71
|
+
<Nudge
|
|
72
|
+
{...defaultProps}
|
|
73
|
+
action={{
|
|
74
|
+
text: 'Learn more',
|
|
75
|
+
href: '#action-link',
|
|
76
|
+
'aria-label': 'Learn more about rewards',
|
|
77
|
+
}}
|
|
78
|
+
/>,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(consoleWarn).toHaveBeenCalledWith(
|
|
82
|
+
'Nudge received both action and link/href props. Rendering action only. Remove link/href to silence this warning.',
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
consoleWarn.mockRestore();
|
|
86
|
+
});
|
|
87
|
+
|
|
45
88
|
describe('Nudge persistance', () => {
|
|
46
89
|
beforeEach(() => {
|
|
47
90
|
jest.resetAllMocks();
|
package/src/Nudge/Nudge.tsx
CHANGED
|
@@ -25,7 +25,7 @@ const getLocalStorage = (): string[] => {
|
|
|
25
25
|
return storage.map((item) => String(item));
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
} catch
|
|
28
|
+
} catch {}
|
|
29
29
|
|
|
30
30
|
return [];
|
|
31
31
|
};
|
|
@@ -47,18 +47,41 @@ type MediaNameType =
|
|
|
47
47
|
| `${Assets.DOCUMENTS}`;
|
|
48
48
|
|
|
49
49
|
type BaseProps = {
|
|
50
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* @deprecated Use `mediaName` instead.
|
|
52
|
+
* Legacy custom media content is no longer used by `Nudge`.
|
|
53
|
+
*/
|
|
51
54
|
media?: ReactNode;
|
|
52
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Illustration asset name shown at the start of the nudge.
|
|
57
|
+
*/
|
|
53
58
|
mediaName?: MediaNameType;
|
|
59
|
+
/** Main nudge message content. */
|
|
54
60
|
title: ReactNode;
|
|
61
|
+
/**
|
|
62
|
+
* Legacy CTA label content.
|
|
63
|
+
* Rendered only when `action` is not provided.
|
|
64
|
+
*/
|
|
55
65
|
link?: ReactNode;
|
|
66
|
+
/**
|
|
67
|
+
* Legacy CTA destination for `link`.
|
|
68
|
+
* Ignored when `action` is provided.
|
|
69
|
+
*/
|
|
56
70
|
href?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Legacy click handler for `link`.
|
|
73
|
+
* Ignored when `action` is provided.
|
|
74
|
+
*/
|
|
57
75
|
onClick?: (event?: MouseEvent<HTMLSpanElement>) => void;
|
|
58
76
|
/** Fired when the user clicks on close button */
|
|
59
77
|
onDismiss?: () => void;
|
|
60
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Preferred CTA configuration rendered under the nudge body.
|
|
80
|
+
* Single CTA rule: when `action` is provided, legacy `link`/`href` is not rendered.
|
|
81
|
+
* If your label is short, use `aria-label` to provide more context.
|
|
82
|
+
*/
|
|
61
83
|
action?: AlertAction;
|
|
84
|
+
/** Optional class name applied to the root nudge container. */
|
|
62
85
|
className?: string;
|
|
63
86
|
};
|
|
64
87
|
|
|
@@ -81,7 +104,7 @@ export interface RequiredPersistProps extends BaseProps {
|
|
|
81
104
|
isPreviouslyDismissed?: (value: boolean) => void;
|
|
82
105
|
}
|
|
83
106
|
|
|
84
|
-
export type
|
|
107
|
+
export type NudgeProps = OptionalId | RequiredPersistProps;
|
|
85
108
|
|
|
86
109
|
const Nudge = ({
|
|
87
110
|
mediaName,
|
|
@@ -95,8 +118,21 @@ const Nudge = ({
|
|
|
95
118
|
id,
|
|
96
119
|
className,
|
|
97
120
|
action,
|
|
98
|
-
}:
|
|
121
|
+
}: NudgeProps) => {
|
|
99
122
|
const intl = useIntl();
|
|
123
|
+
const hasLegacyLink = Boolean(link);
|
|
124
|
+
const hasAction = action != null;
|
|
125
|
+
const shouldRenderLegacyLink = hasLegacyLink && !hasAction;
|
|
126
|
+
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (process.env.NODE_ENV !== 'production' && hasAction && (hasLegacyLink || href != null)) {
|
|
129
|
+
// eslint-disable-next-line no-console -- Intentional developer warning for conflicting API usage.
|
|
130
|
+
console.warn(
|
|
131
|
+
'Nudge received both action and link/href props. Rendering action only. Remove link/href to silence this warning.',
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}, [hasAction, hasLegacyLink, href]);
|
|
135
|
+
|
|
100
136
|
const getIsDismissed = useCallback(
|
|
101
137
|
() => (persistDismissal && id ? !!getLocalStorage()?.find((item) => item === id) : false),
|
|
102
138
|
[persistDismissal, id],
|
|
@@ -166,8 +202,7 @@ const Nudge = ({
|
|
|
166
202
|
<Body type={Typography.BODY_LARGE} className={clsx('wds-nudge-body')}>
|
|
167
203
|
{title}
|
|
168
204
|
</Body>
|
|
169
|
-
{
|
|
170
|
-
{link && (
|
|
205
|
+
{shouldRenderLegacyLink && (
|
|
171
206
|
<Link
|
|
172
207
|
href={href}
|
|
173
208
|
type={Typography.LINK_LARGE}
|
package/src/Nudge/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ describe('Sticky', () => {
|
|
|
21
21
|
|
|
22
22
|
test('renders with default props', () => {
|
|
23
23
|
expect(screen.getByTestId('test-sticky-panel')).toBeVisible();
|
|
24
|
+
expect(screen.getByTestId('test-sticky-panel')).toHaveClass('wds-sticky');
|
|
24
25
|
expect(screen.getByTestId('test-sticky-panel')).toHaveClass('sliding-panel--open-bottom');
|
|
25
26
|
expect(screen.getByText('Test Content')).toBeInTheDocument();
|
|
26
27
|
});
|
package/src/Sticky/Sticky.tsx
CHANGED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
import { Card, Home, Payments, Recipients } from '@transferwise/icons';
|
|
3
|
+
import type { CSSProperties } from 'react';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import { expect, userEvent, within } from 'storybook/test';
|
|
6
|
+
|
|
7
|
+
import { withVariantConfig } from '../../../.storybook/helpers';
|
|
8
|
+
import Button from '../../Button';
|
|
9
|
+
import { wait } from '../../test-utils/wait';
|
|
10
|
+
import Sticky from '../Sticky';
|
|
11
|
+
|
|
12
|
+
const SAFE_AREA_INSET = 64;
|
|
13
|
+
const DEMO_HEIGHT = 400;
|
|
14
|
+
const DEMO_WIDTH = 390;
|
|
15
|
+
const SAFE_AREA_VIEWPORT = 'safe-area-demo';
|
|
16
|
+
|
|
17
|
+
type SafeAreaDemoStyle = CSSProperties & {
|
|
18
|
+
'--wds-sticky-safe-area-inset-bottom': string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const safeAreaDemoStyle: SafeAreaDemoStyle = {
|
|
22
|
+
'--wds-sticky-safe-area-inset-bottom': `${SAFE_AREA_INSET}px`,
|
|
23
|
+
background: 'var(--color-background-screen)',
|
|
24
|
+
height: DEMO_HEIGHT,
|
|
25
|
+
overflow: 'hidden',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const navigationItems = [
|
|
29
|
+
{ Icon: Home, label: 'Home' },
|
|
30
|
+
{ Icon: Card, label: 'Card' },
|
|
31
|
+
{ Icon: Recipients, label: 'Recipients' },
|
|
32
|
+
{ Icon: Payments, label: 'Payments' },
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
function SafeAreaBottomBar() {
|
|
36
|
+
return (
|
|
37
|
+
<nav
|
|
38
|
+
aria-label="Safe area bottom bar"
|
|
39
|
+
data-testid="safe-area-bottom-bar"
|
|
40
|
+
style={{
|
|
41
|
+
alignItems: 'center',
|
|
42
|
+
background: 'var(--color-background-elevated)',
|
|
43
|
+
borderTop: '1px solid var(--color-border-neutral)',
|
|
44
|
+
bottom: 0,
|
|
45
|
+
display: 'flex',
|
|
46
|
+
height: SAFE_AREA_INSET,
|
|
47
|
+
justifyContent: 'space-around',
|
|
48
|
+
left: 0,
|
|
49
|
+
padding: '0 8px',
|
|
50
|
+
position: 'fixed',
|
|
51
|
+
right: 0,
|
|
52
|
+
zIndex: 1031,
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
{navigationItems.map(({ Icon, label }) => (
|
|
56
|
+
<span
|
|
57
|
+
key={label}
|
|
58
|
+
style={{
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
color: 'var(--color-content-primary)',
|
|
61
|
+
display: 'flex',
|
|
62
|
+
flexDirection: 'column',
|
|
63
|
+
fontSize: 11,
|
|
64
|
+
gap: 2,
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
<Icon size={24} />
|
|
68
|
+
{label}
|
|
69
|
+
</span>
|
|
70
|
+
))}
|
|
71
|
+
</nav>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function SafeAreaDemo() {
|
|
76
|
+
const [open, setOpen] = useState(false);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<main style={safeAreaDemoStyle}>
|
|
80
|
+
<section style={{ maxWidth: 520, padding: 32 }}>
|
|
81
|
+
<h1 style={{ fontSize: 32, lineHeight: 1.15, margin: '0 0 16px' }}>
|
|
82
|
+
Sticky respects safe area at bottom of screen
|
|
83
|
+
</h1>
|
|
84
|
+
<Button v2 onClick={() => setOpen((currentOpen) => !currentOpen)}>
|
|
85
|
+
{open ? 'Hide Sticky' : 'Show Sticky'}
|
|
86
|
+
</Button>
|
|
87
|
+
</section>
|
|
88
|
+
|
|
89
|
+
<SafeAreaBottomBar />
|
|
90
|
+
|
|
91
|
+
<Sticky open={open} position="bottom" testId="safe-area-sticky">
|
|
92
|
+
<div className="p-a-2" data-testid="sticky-content">
|
|
93
|
+
Sticky
|
|
94
|
+
</div>
|
|
95
|
+
</Sticky>
|
|
96
|
+
</main>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const meta = {
|
|
101
|
+
component: Sticky,
|
|
102
|
+
parameters: {
|
|
103
|
+
controls: { disable: true },
|
|
104
|
+
layout: 'fullscreen',
|
|
105
|
+
},
|
|
106
|
+
tags: ['!autodocs', '!manifest'],
|
|
107
|
+
title: 'Dialogs/Sticky/Tests',
|
|
108
|
+
} satisfies Meta<typeof Sticky>;
|
|
109
|
+
|
|
110
|
+
export default meta;
|
|
111
|
+
|
|
112
|
+
type Story = StoryObj<typeof Sticky>;
|
|
113
|
+
|
|
114
|
+
const SafeAreaRegression: Story = {
|
|
115
|
+
parameters: {
|
|
116
|
+
chromatic: { delay: 500 },
|
|
117
|
+
viewport: {
|
|
118
|
+
options: {
|
|
119
|
+
[SAFE_AREA_VIEWPORT]: {
|
|
120
|
+
name: 'Safe area demo',
|
|
121
|
+
styles: { height: `${DEMO_HEIGHT}px`, width: `${DEMO_WIDTH}px` },
|
|
122
|
+
type: 'mobile',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
globals: {
|
|
128
|
+
viewport: SAFE_AREA_VIEWPORT,
|
|
129
|
+
},
|
|
130
|
+
play: async ({ canvasElement, step }) => {
|
|
131
|
+
const canvas = within(canvasElement);
|
|
132
|
+
|
|
133
|
+
const expectStickyAboveSafeArea = async () => {
|
|
134
|
+
await wait(400);
|
|
135
|
+
|
|
136
|
+
const sticky = canvas.getByTestId('safe-area-sticky');
|
|
137
|
+
const stickyContent = canvas.getByTestId('sticky-content');
|
|
138
|
+
const safeArea = canvas.getByTestId('safe-area-bottom-bar');
|
|
139
|
+
|
|
140
|
+
await expect(sticky).toBeVisible();
|
|
141
|
+
await expect(window.getComputedStyle(sticky).paddingBottom).toBe(`${SAFE_AREA_INSET}px`);
|
|
142
|
+
await expect(Math.round(stickyContent.getBoundingClientRect().bottom)).toBe(
|
|
143
|
+
Math.round(safeArea.getBoundingClientRect().top),
|
|
144
|
+
);
|
|
145
|
+
await expect(stickyContent.getBoundingClientRect().bottom).toBeLessThanOrEqual(
|
|
146
|
+
safeArea.getBoundingClientRect().top,
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
await step('Open Sticky from the top button', async () => {
|
|
151
|
+
await userEvent.click(canvas.getByRole('button', { name: /^show sticky$/i }));
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
await step('Keep Sticky above the bottom safe area', expectStickyAboveSafeArea);
|
|
155
|
+
|
|
156
|
+
await step('Toggle Sticky closed and open again', async () => {
|
|
157
|
+
const hideStickyButton = canvas.getByRole('button', { name: /^hide sticky$/i });
|
|
158
|
+
|
|
159
|
+
await userEvent.click(hideStickyButton);
|
|
160
|
+
await wait(400);
|
|
161
|
+
await expect(canvas.queryByTestId('safe-area-sticky')).not.toBeInTheDocument();
|
|
162
|
+
|
|
163
|
+
await userEvent.click(canvas.getByRole('button', { name: /^show sticky$/i }));
|
|
164
|
+
await expectStickyAboveSafeArea();
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
render: () => <SafeAreaDemo />,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Reproduces the MobileNav replacement from DS-7193 with a fixed bottom safe area.
|
|
172
|
+
* It verifies that Sticky clears the inset.
|
|
173
|
+
*/
|
|
174
|
+
export const MobileNavSafeArea: Story = {
|
|
175
|
+
...SafeAreaRegression,
|
|
176
|
+
...withVariantConfig(['mobile'], SafeAreaRegression),
|
|
177
|
+
};
|