@transferwise/components 46.125.0 → 46.127.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/avatarView/AvatarView.js.map +1 -1
- package/build/avatarView/AvatarView.mjs.map +1 -1
- package/build/common/locale/index.js +13 -0
- package/build/common/locale/index.js.map +1 -1
- package/build/common/locale/index.mjs +13 -1
- package/build/common/locale/index.mjs.map +1 -1
- package/build/expressiveMoneyInput/currencySelector/CurrencySelector.js +31 -1
- package/build/expressiveMoneyInput/currencySelector/CurrencySelector.js.map +1 -1
- package/build/expressiveMoneyInput/currencySelector/CurrencySelector.mjs +32 -2
- package/build/expressiveMoneyInput/currencySelector/CurrencySelector.mjs.map +1 -1
- package/build/field/Field.js +1 -0
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.mjs +1 -0
- package/build/field/Field.mjs.map +1 -1
- package/build/index.js +3 -0
- package/build/index.js.map +1 -1
- package/build/index.mjs +2 -1
- package/build/index.mjs.map +1 -1
- package/build/inputs/Input.js.map +1 -1
- package/build/inputs/Input.mjs.map +1 -1
- package/build/inputs/SearchInput.js.map +1 -1
- package/build/inputs/SearchInput.mjs.map +1 -1
- package/build/inputs/SelectInput.js.map +1 -1
- package/build/inputs/SelectInput.mjs.map +1 -1
- package/build/inputs/TextArea.js.map +1 -1
- package/build/inputs/TextArea.mjs.map +1 -1
- package/build/listItem/ListItem.js +2 -2
- package/build/listItem/ListItem.js.map +1 -1
- package/build/listItem/ListItem.mjs +2 -2
- package/build/listItem/ListItem.mjs.map +1 -1
- package/build/listItem/Prompt/ListItemPrompt.js +6 -4
- package/build/listItem/Prompt/ListItemPrompt.js.map +1 -1
- package/build/listItem/Prompt/ListItemPrompt.mjs +7 -2
- package/build/listItem/Prompt/ListItemPrompt.mjs.map +1 -1
- package/build/main.css +52 -21
- package/build/moneyInput/MoneyInput.js +6 -1
- package/build/moneyInput/MoneyInput.js.map +1 -1
- package/build/moneyInput/MoneyInput.mjs +6 -1
- package/build/moneyInput/MoneyInput.mjs.map +1 -1
- package/build/prompt/ActionPrompt/ActionPrompt.js +27 -4
- package/build/prompt/ActionPrompt/ActionPrompt.js.map +1 -1
- package/build/prompt/ActionPrompt/ActionPrompt.mjs +27 -4
- package/build/prompt/ActionPrompt/ActionPrompt.mjs.map +1 -1
- package/build/prompt/InfoPrompt/InfoPrompt.js +113 -0
- package/build/prompt/InfoPrompt/InfoPrompt.js.map +1 -0
- package/build/prompt/InfoPrompt/InfoPrompt.mjs +111 -0
- package/build/prompt/InfoPrompt/InfoPrompt.mjs.map +1 -0
- package/build/prompt/PrimitivePrompt/PrimitivePrompt.js.map +1 -1
- package/build/prompt/PrimitivePrompt/PrimitivePrompt.mjs.map +1 -1
- package/build/radioOption/RadioOption.js.map +1 -1
- package/build/radioOption/RadioOption.mjs.map +1 -1
- package/build/slidingPanel/SlidingPanel.js.map +1 -1
- package/build/slidingPanel/SlidingPanel.mjs.map +1 -1
- package/build/statusIcon/StatusIcon.js +2 -0
- package/build/statusIcon/StatusIcon.js.map +1 -1
- package/build/statusIcon/StatusIcon.mjs +2 -0
- package/build/statusIcon/StatusIcon.mjs.map +1 -1
- package/build/styles/main.css +52 -21
- package/build/styles/prompt/InfoPrompt/InfoPrompt.css +31 -0
- package/build/styles/sentimentSurface/SentimentSurface.css +21 -21
- package/build/table/TableCell.js.map +1 -1
- package/build/table/TableCell.mjs.map +1 -1
- package/build/typeahead/Typeahead.js +1 -0
- package/build/typeahead/Typeahead.js.map +1 -1
- package/build/typeahead/Typeahead.mjs +1 -0
- package/build/typeahead/Typeahead.mjs.map +1 -1
- package/build/types/avatarView/AvatarView.d.ts +1 -1
- package/build/types/avatarView/AvatarView.d.ts.map +1 -1
- package/build/types/common/locale/index.d.ts +8 -0
- package/build/types/common/locale/index.d.ts.map +1 -1
- package/build/types/expressiveMoneyInput/currencySelector/CurrencySelector.d.ts.map +1 -1
- package/build/types/index.d.ts +3 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputs/Input.d.ts.map +1 -1
- package/build/types/inputs/SearchInput.d.ts.map +1 -1
- package/build/types/inputs/SelectInput.d.ts +1 -1
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/inputs/TextArea.d.ts.map +1 -1
- package/build/types/listItem/ListItem.d.ts +1 -1
- package/build/types/listItem/Prompt/ListItemPrompt.d.ts +2 -3
- package/build/types/listItem/Prompt/ListItemPrompt.d.ts.map +1 -1
- package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
- package/build/types/primitives/PrimitiveAnchor/PrimitiveAnchor.types.d.ts.map +1 -1
- package/build/types/primitives/PrimitiveButton/PrimitiveButton.types.d.ts.map +1 -1
- package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts +4 -2
- package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts.map +1 -1
- package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts +56 -0
- package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts.map +1 -0
- package/build/types/prompt/InfoPrompt/index.d.ts +3 -0
- package/build/types/prompt/InfoPrompt/index.d.ts.map +1 -0
- package/build/types/prompt/PrimitivePrompt/PrimitivePrompt.d.ts +5 -5
- package/build/types/prompt/PrimitivePrompt/PrimitivePrompt.d.ts.map +1 -1
- package/build/types/prompt/index.d.ts +2 -0
- package/build/types/prompt/index.d.ts.map +1 -1
- package/build/types/radioOption/RadioOption.d.ts.map +1 -1
- package/build/types/slidingPanel/SlidingPanel.d.ts.map +1 -1
- package/build/types/statusIcon/StatusIcon.d.ts +2 -1
- package/build/types/statusIcon/StatusIcon.d.ts.map +1 -1
- package/build/types/table/TableCell.d.ts.map +1 -1
- package/build/types/withDisplayFormat/WithDisplayFormat.d.ts.map +1 -1
- package/build/withDisplayFormat/WithDisplayFormat.js.map +1 -1
- package/build/withDisplayFormat/WithDisplayFormat.mjs.map +1 -1
- package/package.json +3 -3
- package/src/accordion/Accordion.test.js +0 -6
- package/src/accordion/AccordionItem/AccordionItem.test.js +0 -10
- package/src/actionButton/ActionButton.test.tsx +0 -4
- package/src/avatarLayout/AvatarLayout.story.tsx +3 -3
- package/src/avatarView/AvatarView.story.tsx +29 -24
- package/src/avatarView/AvatarView.tsx +1 -1
- package/src/avatarWrapper/AvatarWrapper.test.tsx +0 -53
- package/src/checkbox/Checkbox.test.tsx +0 -5
- package/src/chevron/Chevron.test.tsx +0 -7
- package/src/chips/Chips.test.tsx +0 -8
- package/src/common/RadioButton/RadioButton.test.tsx +0 -18
- package/src/common/bottomSheet/BottomSheet.test.story.tsx +98 -0
- package/src/common/bottomSheet/BottomSheet.test.tsx +0 -9
- package/src/common/card/Card.test.tsx +0 -6
- package/src/common/closeButton/CloseButton.test.tsx +0 -4
- package/src/common/locale/index.test.ts +36 -1
- package/src/common/locale/index.ts +13 -0
- package/src/common/panel/Panel.test.tsx +0 -6
- package/src/expressiveMoneyInput/currencySelector/CurrencySelector.tsx +5 -1
- package/src/flowNavigation/FlowNavigation.test.js +0 -10
- package/src/index.ts +3 -1
- package/src/inputs/Input.tsx +8 -9
- package/src/inputs/SearchInput.tsx +8 -9
- package/src/inputs/SelectInput.test.story.tsx +86 -0
- package/src/inputs/SelectInput.tsx +1 -1
- package/src/inputs/TextArea.tsx +6 -7
- package/src/listItem/ListItem.tsx +2 -2
- package/src/listItem/Prompt/ListItemPrompt.story.tsx +71 -9
- package/src/listItem/Prompt/ListItemPrompt.test.tsx +31 -0
- package/src/listItem/Prompt/ListItemPrompt.tsx +8 -2
- package/src/logo/Logo.story.tsx +24 -5
- package/src/main.css +52 -21
- package/src/main.less +2 -1
- package/src/moneyInput/MoneyInput.test.story.tsx +104 -0
- package/src/moneyInput/MoneyInput.tsx +20 -2
- package/src/overlayHeader/OverlayHeader.test.tsx +0 -3
- package/src/popover/Popover.test.tsx +0 -25
- package/src/primitives/PrimitiveAnchor/PrimitiveAnchor.types.ts +1 -3
- package/src/primitives/PrimitiveButton/PrimitiveButton.types.ts +1 -3
- package/src/promoCard/PromoCard.test.tsx +0 -6
- package/src/promoCard/PromoCardGroup.test.tsx +0 -5
- package/src/prompt/ActionPrompt/ActionPrompt.accessibility.docs.mdx +65 -0
- package/src/prompt/ActionPrompt/ActionPrompt.less +1 -1
- package/src/prompt/ActionPrompt/ActionPrompt.story.tsx +4 -1
- package/src/prompt/ActionPrompt/ActionPrompt.test.story.tsx +147 -0
- package/src/prompt/ActionPrompt/ActionPrompt.test.tsx +2 -7
- package/src/prompt/ActionPrompt/ActionPrompt.tsx +48 -7
- package/src/prompt/InfoPrompt/InfoPrompt.css +31 -0
- package/src/prompt/InfoPrompt/InfoPrompt.less +37 -0
- package/src/prompt/InfoPrompt/InfoPrompt.story.tsx +312 -0
- package/src/prompt/InfoPrompt/InfoPrompt.test.story.tsx +246 -0
- package/src/prompt/InfoPrompt/InfoPrompt.test.tsx +224 -0
- package/src/prompt/InfoPrompt/InfoPrompt.tsx +148 -0
- package/src/prompt/InfoPrompt/index.ts +2 -0
- package/src/prompt/InlinePrompt/InlinePrompt.story.tsx +13 -10
- package/src/prompt/InlinePrompt/InlinePrompt.test.tsx +13 -1
- package/src/prompt/PrimitivePrompt/PrimitivePrompt.less +1 -1
- package/src/prompt/PrimitivePrompt/PrimitivePrompt.tsx +5 -5
- package/src/prompt/index.ts +5 -0
- package/src/radioOption/RadioOption.tsx +2 -1
- package/src/sentimentSurface/SentimentSurface.css +21 -21
- package/src/sentimentSurface/SentimentSurface.less +13 -13
- package/src/sentimentSurface/SentimentSurface.story.tsx +1 -1
- package/src/sentimentSurface/SentimentSurface.test.story.tsx +48 -1
- package/src/slidingPanel/SlidingPanel.tsx +4 -2
- package/src/ssr.test.tsx +2 -0
- package/src/statusIcon/StatusIcon.tsx +8 -1
- package/src/table/TableCell.tsx +1 -3
- package/src/tile/Tile.test.tsx +0 -10
- package/src/tooltip/Tooltip.test.tsx +0 -10
- package/src/withDisplayFormat/WithDisplayFormat.tsx +13 -14
- package/src/accordion/AccordionItem/__snapshots__/AccordionItem.test.js.snap +0 -124
- package/src/accordion/__snapshots__/Accordion.test.js.snap +0 -3
- package/src/actionButton/__snapshots__/ActionButton.test.tsx.snap +0 -12
- package/src/avatarWrapper/__snapshots__/AvatarWrapper.test.tsx.snap +0 -156
- package/src/checkbox/__snapshots__/Checkbox.test.tsx.snap +0 -40
- package/src/chevron/__snapshots__/Chevron.test.tsx.snap +0 -24
- package/src/chips/__snapshots__/Chips.test.tsx.snap +0 -153
- package/src/common/RadioButton/__snapshots__/RadioButton.test.tsx.snap +0 -58
- package/src/common/bottomSheet/__snapshots__/BottomSheet.test.tsx.snap +0 -80
- package/src/common/card/__snapshots__/Card.test.tsx.snap +0 -10
- package/src/common/closeButton/__snapshots__/CloseButton.test.tsx.snap +0 -30
- package/src/common/flowHeader/FlowHeader.test.tsx +0 -22
- package/src/common/flowHeader/__snapshots__/FlowHeader.test.tsx.snap +0 -33
- package/src/common/panel/__snapshots__/Panel.test.tsx.snap +0 -3
- package/src/flowNavigation/__snapshots__/FlowNavigation.test.js.snap +0 -262
- package/src/logo/Logo.test.tsx +0 -55
- package/src/logo/__snapshots__/Logo.test.tsx.snap +0 -281
- package/src/overlayHeader/__snapshots__/OverlayHeader.test.tsx.snap +0 -65
- package/src/popover/__snapshots__/Popover.test.tsx.snap +0 -51
- package/src/promoCard/__snapshots__/PromoCard.test.tsx.snap +0 -40
- package/src/promoCard/__snapshots__/PromoCardGroup.test.tsx.snap +0 -80
- package/src/tile/__snapshots__/Tile.test.tsx.snap +0 -55
- package/src/tooltip/__snapshots__/Tooltip.test.tsx.snap +0 -32
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
3
|
+
import { action } from 'storybook/actions';
|
|
4
|
+
import { Confetti, GiftBox, Star, Travel } from '@transferwise/icons';
|
|
5
|
+
import { InfoPrompt, InfoPromptProps } from './InfoPrompt';
|
|
6
|
+
|
|
7
|
+
const withComponentGrid =
|
|
8
|
+
({ maxWidth = 'auto', gap = '1rem' } = {}) =>
|
|
9
|
+
(Story: () => ReactElement) => (
|
|
10
|
+
<div
|
|
11
|
+
style={{
|
|
12
|
+
width: '100%',
|
|
13
|
+
display: 'flex',
|
|
14
|
+
flexDirection: 'column',
|
|
15
|
+
justifyContent: 'flex-start',
|
|
16
|
+
alignItems: 'flex-start',
|
|
17
|
+
alignContent: 'flex-start',
|
|
18
|
+
gap,
|
|
19
|
+
maxWidth,
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
<Story />
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
title: 'Prompts/InfoPrompt',
|
|
28
|
+
component: InfoPrompt,
|
|
29
|
+
tags: ['new'],
|
|
30
|
+
decorators: [withComponentGrid()],
|
|
31
|
+
args: {
|
|
32
|
+
description: 'Your payment is being processed.',
|
|
33
|
+
},
|
|
34
|
+
argTypes: {
|
|
35
|
+
sentiment: {
|
|
36
|
+
control: 'select',
|
|
37
|
+
options: ['success', 'negative', 'neutral', 'warning', 'proposition'],
|
|
38
|
+
},
|
|
39
|
+
title: {
|
|
40
|
+
control: 'text',
|
|
41
|
+
table: {
|
|
42
|
+
type: { summary: 'ReactNode' },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
description: {
|
|
46
|
+
control: 'text',
|
|
47
|
+
table: {
|
|
48
|
+
type: { summary: 'ReactNode' },
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
parameters: {
|
|
53
|
+
docs: {
|
|
54
|
+
toc: true,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
} satisfies Meta<InfoPromptProps>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Convenience controls for previewing rich markup,
|
|
61
|
+
* not otherwise possible via Storybook
|
|
62
|
+
*/
|
|
63
|
+
type PreviewStoryArgs = InfoPromptProps & {
|
|
64
|
+
previewMedia: boolean;
|
|
65
|
+
previewOnDismiss: boolean;
|
|
66
|
+
previewAction: boolean;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const previewArgGroup = {
|
|
70
|
+
category: 'Storybook Preview options',
|
|
71
|
+
type: {
|
|
72
|
+
summary: undefined,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const previewArgTypes = {
|
|
77
|
+
previewMedia: {
|
|
78
|
+
name: 'Preview with `media`',
|
|
79
|
+
control: 'boolean',
|
|
80
|
+
table: previewArgGroup,
|
|
81
|
+
},
|
|
82
|
+
previewOnDismiss: {
|
|
83
|
+
name: 'Preview with `onDismiss`',
|
|
84
|
+
control: 'boolean',
|
|
85
|
+
table: previewArgGroup,
|
|
86
|
+
},
|
|
87
|
+
previewAction: {
|
|
88
|
+
name: 'Preview with `action`',
|
|
89
|
+
control: 'boolean',
|
|
90
|
+
table: previewArgGroup,
|
|
91
|
+
},
|
|
92
|
+
} as const;
|
|
93
|
+
|
|
94
|
+
const getPropsForPreview = (
|
|
95
|
+
args: PreviewStoryArgs,
|
|
96
|
+
): [InfoPromptProps, Partial<InfoPromptProps>] => {
|
|
97
|
+
const { previewMedia, previewOnDismiss, previewAction, ...props } = args;
|
|
98
|
+
|
|
99
|
+
return [
|
|
100
|
+
props,
|
|
101
|
+
{
|
|
102
|
+
media: previewMedia ? { asset: <Star title="Starred" /> } : undefined,
|
|
103
|
+
onDismiss: previewOnDismiss ? action('onDismiss') : undefined,
|
|
104
|
+
action: previewAction
|
|
105
|
+
? { label: 'Learn more', onClick: action('action.onClick') }
|
|
106
|
+
: undefined,
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const Playground: StoryObj<PreviewStoryArgs> = {
|
|
112
|
+
tags: ['!autodocs'],
|
|
113
|
+
argTypes: previewArgTypes,
|
|
114
|
+
args: {
|
|
115
|
+
previewMedia: false,
|
|
116
|
+
previewOnDismiss: false,
|
|
117
|
+
previewAction: false,
|
|
118
|
+
},
|
|
119
|
+
render: (args: PreviewStoryArgs) => {
|
|
120
|
+
const [props, previewProps] = getPropsForPreview(args);
|
|
121
|
+
return <InfoPrompt {...props} {...previewProps} />;
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* InfoPrompt supports multiple sentiments to communicate different types of messages:
|
|
127
|
+
* - `neutral` (default): General information
|
|
128
|
+
* - `success`: Success messages
|
|
129
|
+
* - `negative`: Error messages
|
|
130
|
+
* - `warning`: Warning messages
|
|
131
|
+
* - `proposition`: Promotional content (uses GiftBox icon by default)
|
|
132
|
+
*/
|
|
133
|
+
export const Sentiments: StoryObj<InfoPromptProps> = {
|
|
134
|
+
render: (args: InfoPromptProps) => (
|
|
135
|
+
<>
|
|
136
|
+
<InfoPrompt {...args} sentiment="neutral" description="This is a neutral message." />
|
|
137
|
+
<InfoPrompt {...args} sentiment="success" description="Your payment was successful!" />
|
|
138
|
+
<InfoPrompt {...args} sentiment="negative" description="Something went wrong." />
|
|
139
|
+
<InfoPrompt {...args} sentiment="warning" description="Please review your details." />
|
|
140
|
+
<InfoPrompt {...args} sentiment="proposition" description="Check out our new features!" />
|
|
141
|
+
</>
|
|
142
|
+
),
|
|
143
|
+
decorators: [withComponentGrid()],
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* InfoPrompt can include both a title and description for more detailed messaging.
|
|
148
|
+
*/
|
|
149
|
+
export const WithTitle: StoryObj<InfoPromptProps> = {
|
|
150
|
+
render: (args: InfoPromptProps) => (
|
|
151
|
+
<>
|
|
152
|
+
<InfoPrompt
|
|
153
|
+
{...args}
|
|
154
|
+
sentiment="success"
|
|
155
|
+
title="Payment Successful"
|
|
156
|
+
description="Your money is on its way to the recipient."
|
|
157
|
+
/>
|
|
158
|
+
<InfoPrompt
|
|
159
|
+
{...args}
|
|
160
|
+
sentiment="warning"
|
|
161
|
+
title="Payment Delayed"
|
|
162
|
+
description="Payments sent today might not arrive in time for the holidays."
|
|
163
|
+
/>
|
|
164
|
+
<InfoPrompt
|
|
165
|
+
{...args}
|
|
166
|
+
sentiment="negative"
|
|
167
|
+
title="Payment Failed"
|
|
168
|
+
description="We couldn't process your payment. Please try again."
|
|
169
|
+
/>
|
|
170
|
+
</>
|
|
171
|
+
),
|
|
172
|
+
decorators: [withComponentGrid()],
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* When `onDismiss` is provided, a close button appears allowing users to dismiss the prompt.
|
|
177
|
+
* Note: The component itself is not automatically removed - you must handle state management.
|
|
178
|
+
*/
|
|
179
|
+
export const Dismissible: StoryObj<InfoPromptProps> = {
|
|
180
|
+
render: (args: InfoPromptProps) => (
|
|
181
|
+
<>
|
|
182
|
+
<InfoPrompt
|
|
183
|
+
{...args}
|
|
184
|
+
sentiment="success"
|
|
185
|
+
title="Success"
|
|
186
|
+
description="Your payment was successful."
|
|
187
|
+
onDismiss={action('onDismiss')}
|
|
188
|
+
/>
|
|
189
|
+
<InfoPrompt
|
|
190
|
+
{...args}
|
|
191
|
+
sentiment="neutral"
|
|
192
|
+
description="This message can be dismissed."
|
|
193
|
+
onDismiss={action('onDismiss')}
|
|
194
|
+
/>
|
|
195
|
+
</>
|
|
196
|
+
),
|
|
197
|
+
decorators: [withComponentGrid()],
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Use the `action` prop to add a clickable link below the message.
|
|
202
|
+
* Actions can have an `href` for navigation or an `onClick` for custom behavior.
|
|
203
|
+
*/
|
|
204
|
+
export const WithAction: StoryObj<InfoPromptProps> = {
|
|
205
|
+
render: (args: InfoPromptProps) => (
|
|
206
|
+
<>
|
|
207
|
+
<InfoPrompt
|
|
208
|
+
{...args}
|
|
209
|
+
sentiment="neutral"
|
|
210
|
+
description="New feature available"
|
|
211
|
+
action={{ label: 'Learn more', href: '/features', target: '_blank' }}
|
|
212
|
+
/>
|
|
213
|
+
<InfoPrompt
|
|
214
|
+
{...args}
|
|
215
|
+
sentiment="warning"
|
|
216
|
+
title="Action Required"
|
|
217
|
+
description="Please verify your identity to continue."
|
|
218
|
+
action={{ label: 'Verify now', onClick: action('verify') }}
|
|
219
|
+
/>
|
|
220
|
+
<InfoPrompt
|
|
221
|
+
{...args}
|
|
222
|
+
sentiment="proposition"
|
|
223
|
+
title="Special Offer"
|
|
224
|
+
description="Get 50% off your next transfer!"
|
|
225
|
+
action={{ label: 'Claim offer', onClick: action('claim') }}
|
|
226
|
+
/>
|
|
227
|
+
</>
|
|
228
|
+
),
|
|
229
|
+
decorators: [withComponentGrid()],
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Use the `media` prop to override the default status icon with a custom icon.
|
|
234
|
+
* This is useful for matching the prompt's visual language to its content.
|
|
235
|
+
*
|
|
236
|
+
* Success and proposition sentiments support both standard checkmark and confetti variations.
|
|
237
|
+
*/
|
|
238
|
+
export const CustomMedia: StoryObj<InfoPromptProps> = {
|
|
239
|
+
render: (args: InfoPromptProps) => (
|
|
240
|
+
<>
|
|
241
|
+
<InfoPrompt
|
|
242
|
+
{...args}
|
|
243
|
+
sentiment="success"
|
|
244
|
+
description="Saved to favorites!"
|
|
245
|
+
media={{ asset: <Star title="Starred" /> }}
|
|
246
|
+
/>
|
|
247
|
+
<InfoPrompt
|
|
248
|
+
{...args}
|
|
249
|
+
sentiment="success"
|
|
250
|
+
title="Congratulations!"
|
|
251
|
+
description="You've completed all the steps."
|
|
252
|
+
media={{ asset: <Confetti size={24} title="Celebration" /> }}
|
|
253
|
+
/>
|
|
254
|
+
<InfoPrompt
|
|
255
|
+
{...args}
|
|
256
|
+
sentiment="proposition"
|
|
257
|
+
title="Check out more!"
|
|
258
|
+
description="New ways to send money with Wise!"
|
|
259
|
+
media={{ asset: <GiftBox title="Gift" /> }}
|
|
260
|
+
/>
|
|
261
|
+
<InfoPrompt
|
|
262
|
+
{...args}
|
|
263
|
+
sentiment="success"
|
|
264
|
+
description="Your travel account is ready!"
|
|
265
|
+
media={{ asset: <Travel title="Travel" /> }}
|
|
266
|
+
/>
|
|
267
|
+
</>
|
|
268
|
+
),
|
|
269
|
+
decorators: [withComponentGrid()],
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* InfoPrompt fully supports accessibility attributes through spread props.
|
|
274
|
+
* Use `role="status"` for non-critical updates or `role="alert"` for important messages.
|
|
275
|
+
*/
|
|
276
|
+
export const Accessibility: StoryObj<InfoPromptProps> = {
|
|
277
|
+
render: (args: InfoPromptProps) => (
|
|
278
|
+
<>
|
|
279
|
+
<InfoPrompt
|
|
280
|
+
{...args}
|
|
281
|
+
sentiment="success"
|
|
282
|
+
description="Your profile has been saved"
|
|
283
|
+
role="status"
|
|
284
|
+
/>
|
|
285
|
+
<InfoPrompt
|
|
286
|
+
{...args}
|
|
287
|
+
sentiment="negative"
|
|
288
|
+
title="Error"
|
|
289
|
+
description="Your session has expired. Please log in again."
|
|
290
|
+
role="alert"
|
|
291
|
+
/>
|
|
292
|
+
</>
|
|
293
|
+
),
|
|
294
|
+
decorators: [withComponentGrid()],
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Complete example showing all features combined:
|
|
299
|
+
* sentiment, title, description, action, onDismiss, and custom media.
|
|
300
|
+
*/
|
|
301
|
+
export const CompleteExample: StoryObj<InfoPromptProps> = {
|
|
302
|
+
render: () => (
|
|
303
|
+
<InfoPrompt
|
|
304
|
+
sentiment="proposition"
|
|
305
|
+
title="Invite Friends & Earn"
|
|
306
|
+
description="Share Wise with friends and get £50 for each friend who makes their first transfer."
|
|
307
|
+
media={{ asset: <GiftBox title="Gift" /> }}
|
|
308
|
+
action={{ label: 'Invite friends', onClick: action('invite') }}
|
|
309
|
+
onDismiss={action('onDismiss')}
|
|
310
|
+
/>
|
|
311
|
+
),
|
|
312
|
+
};
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { useState, MouseEvent } from 'react';
|
|
2
|
+
import { userEvent, within, expect, waitFor } from 'storybook/test';
|
|
3
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
4
|
+
import { InfoPrompt, type InfoPromptProps } from './InfoPrompt';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'Prompts/InfoPrompt/Tests',
|
|
8
|
+
component: InfoPrompt,
|
|
9
|
+
tags: ['!autodocs', '!manifest', 'new'],
|
|
10
|
+
args: {
|
|
11
|
+
sentiment: 'neutral',
|
|
12
|
+
description: 'This is an informational message.',
|
|
13
|
+
},
|
|
14
|
+
} satisfies Meta<typeof InfoPrompt>;
|
|
15
|
+
|
|
16
|
+
export default meta;
|
|
17
|
+
|
|
18
|
+
type Story = StoryObj<typeof meta>;
|
|
19
|
+
|
|
20
|
+
const wait = async (duration = 500) =>
|
|
21
|
+
new Promise<void>((resolve) => {
|
|
22
|
+
setTimeout(resolve, duration);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Test that clicking the dismiss button removes the prompt.
|
|
27
|
+
*/
|
|
28
|
+
export const DismissInteraction: Story = {
|
|
29
|
+
play: async ({ canvasElement, step }) => {
|
|
30
|
+
const canvas = within(canvasElement);
|
|
31
|
+
const dismissButton = canvas.getByRole('button', { name: /close/i });
|
|
32
|
+
|
|
33
|
+
await step('Verify prompt is visible', async () => {
|
|
34
|
+
await waitFor(async () =>
|
|
35
|
+
expect(canvas.getByText('This message can be dismissed.')).toBeInTheDocument(),
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await step('Click the dismiss button', async () => {
|
|
40
|
+
await userEvent.click(dismissButton);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
await step('Verify prompt is removed', async () => {
|
|
44
|
+
await waitFor(async () =>
|
|
45
|
+
expect(canvas.queryByText('This message can be dismissed.')).not.toBeInTheDocument(),
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
render: function Render(args: InfoPromptProps) {
|
|
50
|
+
const [isVisible, setIsVisible] = useState(true);
|
|
51
|
+
|
|
52
|
+
if (!isVisible) {
|
|
53
|
+
return <div data-testid="dismissed">Prompt dismissed</div>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<InfoPrompt
|
|
58
|
+
{...args}
|
|
59
|
+
description="This message can be dismissed."
|
|
60
|
+
onDismiss={() => setIsVisible(false)}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Test keyboard accessibility - dismiss prompt via Enter key.
|
|
68
|
+
*/
|
|
69
|
+
export const DismissViaKeyboard: Story = {
|
|
70
|
+
play: async ({ canvasElement, step }) => {
|
|
71
|
+
const canvas = within(canvasElement);
|
|
72
|
+
|
|
73
|
+
await step('Tab to the dismiss button', async () => {
|
|
74
|
+
await wait();
|
|
75
|
+
await userEvent.tab();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
await step('Press Enter to dismiss', async () => {
|
|
79
|
+
await userEvent.keyboard('{Enter}');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await step('Verify prompt is removed', async () => {
|
|
83
|
+
await waitFor(async () =>
|
|
84
|
+
expect(canvas.queryByText('Press Enter to dismiss.')).not.toBeInTheDocument(),
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
render: function Render(args: InfoPromptProps) {
|
|
89
|
+
const [isVisible, setIsVisible] = useState(true);
|
|
90
|
+
|
|
91
|
+
if (!isVisible) {
|
|
92
|
+
return <div data-testid="dismissed">Prompt dismissed</div>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<InfoPrompt
|
|
97
|
+
{...args}
|
|
98
|
+
description="Press Enter to dismiss."
|
|
99
|
+
onDismiss={() => setIsVisible(false)}
|
|
100
|
+
/>
|
|
101
|
+
);
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Test action link click interaction.
|
|
107
|
+
*/
|
|
108
|
+
export const ActionClickInteraction: Story = {
|
|
109
|
+
play: async ({ canvasElement, step }) => {
|
|
110
|
+
const canvas = within(canvasElement);
|
|
111
|
+
const actionLink = canvas.getByRole('link', { name: 'Learn more' });
|
|
112
|
+
|
|
113
|
+
await step('Verify prompt with action is visible', async () => {
|
|
114
|
+
await waitFor(async () =>
|
|
115
|
+
expect(canvas.getByText('Click the action link.')).toBeInTheDocument(),
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
await step('Click the action link', async () => {
|
|
120
|
+
await userEvent.click(actionLink);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await step('Verify action was triggered', async () => {
|
|
124
|
+
await waitFor(async () => expect(canvas.getByText('Action clicked!')).toBeInTheDocument());
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
render: function Render(args: InfoPromptProps) {
|
|
128
|
+
const [actionClicked, setActionClicked] = useState(false);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<>
|
|
132
|
+
<InfoPrompt
|
|
133
|
+
{...args}
|
|
134
|
+
description="Click the action link."
|
|
135
|
+
action={{
|
|
136
|
+
label: 'Learn more',
|
|
137
|
+
href: '#',
|
|
138
|
+
onClick: (e: MouseEvent) => {
|
|
139
|
+
e.preventDefault();
|
|
140
|
+
setActionClicked(true);
|
|
141
|
+
},
|
|
142
|
+
}}
|
|
143
|
+
/>
|
|
144
|
+
{actionClicked && <div>Action clicked!</div>}
|
|
145
|
+
</>
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Test multiple prompts with different sentiments can be dismissed independently.
|
|
152
|
+
*/
|
|
153
|
+
export const MultipleDismissInteraction: Story = {
|
|
154
|
+
play: async ({ canvasElement, step }) => {
|
|
155
|
+
const canvas = within(canvasElement);
|
|
156
|
+
const dismissButtons = canvas.getAllByRole('button', { name: /close/i });
|
|
157
|
+
|
|
158
|
+
await step('Verify all prompts are visible', async () => {
|
|
159
|
+
await waitFor(async () => {
|
|
160
|
+
await expect(canvas.getByText('Success message')).toBeInTheDocument();
|
|
161
|
+
await expect(canvas.getByText('Warning message')).toBeInTheDocument();
|
|
162
|
+
await expect(canvas.getByText('Negative message')).toBeInTheDocument();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
await step('Dismiss the warning prompt', async () => {
|
|
167
|
+
// Click the second dismiss button (warning)
|
|
168
|
+
await userEvent.click(dismissButtons[1]);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
await step('Verify only warning prompt is removed', async () => {
|
|
172
|
+
await waitFor(async () => {
|
|
173
|
+
await expect(canvas.getByText('Success message')).toBeInTheDocument();
|
|
174
|
+
await expect(canvas.queryByText('Warning message')).not.toBeInTheDocument();
|
|
175
|
+
await expect(canvas.getByText('Negative message')).toBeInTheDocument();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
render: function Render() {
|
|
180
|
+
const [prompts, setPrompts] = useState([
|
|
181
|
+
{ id: 1, sentiment: 'success' as const, description: 'Success message' },
|
|
182
|
+
{ id: 2, sentiment: 'warning' as const, description: 'Warning message' },
|
|
183
|
+
{ id: 3, sentiment: 'negative' as const, description: 'Negative message' },
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
const handleDismiss = (id: number) => {
|
|
187
|
+
setPrompts((prev) => prev.filter((p) => p.id !== id));
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
|
192
|
+
{prompts.map((prompt) => (
|
|
193
|
+
<InfoPrompt
|
|
194
|
+
key={prompt.id}
|
|
195
|
+
sentiment={prompt.sentiment}
|
|
196
|
+
description={prompt.description}
|
|
197
|
+
onDismiss={() => handleDismiss(prompt.id)}
|
|
198
|
+
/>
|
|
199
|
+
))}
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Test that touch interactions work for navigation (mobile).
|
|
207
|
+
*/
|
|
208
|
+
export const TouchInteraction: Story = {
|
|
209
|
+
play: async ({ canvasElement, step }) => {
|
|
210
|
+
const canvas = within(canvasElement);
|
|
211
|
+
const actionLink = canvas.getByRole('link', { name: 'Navigate' });
|
|
212
|
+
|
|
213
|
+
await step('Verify prompt with action is visible', async () => {
|
|
214
|
+
await waitFor(async () => expect(canvas.getByText('Tap the prompt.')).toBeInTheDocument());
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
await step('Click the action (simulating touch)', async () => {
|
|
218
|
+
await userEvent.click(actionLink);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
await step('Verify navigation was triggered', async () => {
|
|
222
|
+
await waitFor(async () => expect(canvas.getByText('Navigated!')).toBeInTheDocument());
|
|
223
|
+
});
|
|
224
|
+
},
|
|
225
|
+
render: function Render(args: InfoPromptProps) {
|
|
226
|
+
const [navigated, setNavigated] = useState(false);
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<>
|
|
230
|
+
<InfoPrompt
|
|
231
|
+
{...args}
|
|
232
|
+
description="Tap the prompt."
|
|
233
|
+
action={{
|
|
234
|
+
label: 'Navigate',
|
|
235
|
+
href: '#',
|
|
236
|
+
onClick: (e: MouseEvent) => {
|
|
237
|
+
e.preventDefault();
|
|
238
|
+
setNavigated(true);
|
|
239
|
+
},
|
|
240
|
+
}}
|
|
241
|
+
/>
|
|
242
|
+
{navigated && <div>Navigated!</div>}
|
|
243
|
+
</>
|
|
244
|
+
);
|
|
245
|
+
},
|
|
246
|
+
};
|