@transferwise/components 46.145.0 → 46.146.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/main.css +503 -558
- package/build/prompt/InfoPrompt/InfoPrompt.js +1 -1
- package/build/prompt/InfoPrompt/InfoPrompt.js.map +1 -1
- package/build/prompt/InfoPrompt/InfoPrompt.mjs +2 -2
- package/build/prompt/InfoPrompt/InfoPrompt.mjs.map +1 -1
- package/build/sentimentSurface/SentimentSurface.js +9 -2
- package/build/sentimentSurface/SentimentSurface.js.map +1 -1
- package/build/sentimentSurface/SentimentSurface.mjs +9 -2
- package/build/sentimentSurface/SentimentSurface.mjs.map +1 -1
- package/build/styles/main.css +503 -558
- package/build/styles/sentimentSurface/SentimentSurface.css +325 -337
- package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts +3 -2
- package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts.map +1 -1
- package/build/types/sentimentSurface/SentimentSurface.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/criticalBanner/CriticalCommsBanner.test.tsx +1 -1
- package/src/main.css +503 -558
- package/src/prompt/InfoPrompt/InfoPrompt.story.tsx +42 -0
- package/src/prompt/InfoPrompt/InfoPrompt.test.tsx +65 -1
- package/src/prompt/InfoPrompt/InfoPrompt.tsx +15 -4
- package/src/sentimentSurface/SentimentSurface.css +325 -337
- package/src/sentimentSurface/SentimentSurface.docs.mdx +2 -0
- package/src/sentimentSurface/SentimentSurface.less +2 -322
- package/src/sentimentSurface/SentimentSurface.story.tsx +4 -0
- package/src/sentimentSurface/SentimentSurface.test.story.tsx +1 -5
- package/src/sentimentSurface/SentimentSurface.test.tsx +84 -3
- package/src/sentimentSurface/SentimentSurface.tsx +10 -2
|
@@ -6,6 +6,7 @@ import { Confetti, GiftBox, Star, Suitcase, Briefcase, Plane } from '@transferwi
|
|
|
6
6
|
import { lorem10, lorem20 } from '../../test-utils';
|
|
7
7
|
import Button from '../../button';
|
|
8
8
|
import Title from '../../title';
|
|
9
|
+
import Link from '../../link';
|
|
9
10
|
import { InfoPrompt, InfoPromptProps, InfoPromptMedia } from './InfoPrompt';
|
|
10
11
|
|
|
11
12
|
const withComponentGrid =
|
|
@@ -232,6 +233,47 @@ export const WithAction: StoryObj<InfoPromptProps> = {
|
|
|
232
233
|
),
|
|
233
234
|
};
|
|
234
235
|
|
|
236
|
+
/**
|
|
237
|
+
* The `description` prop accepts rich content (ReactNode), allowing inline interactive elements
|
|
238
|
+
* such as links that can trigger a popover.
|
|
239
|
+
*
|
|
240
|
+
* **Important**: If the description contains an interactive element, do not use the `action` prop
|
|
241
|
+
* simultaneously — InfoPrompt should have at most one interaction.
|
|
242
|
+
*/
|
|
243
|
+
export const WithRichDescription: StoryObj<InfoPromptProps> = {
|
|
244
|
+
render: (args: InfoPromptProps) => (
|
|
245
|
+
<>
|
|
246
|
+
<InfoPrompt
|
|
247
|
+
{...args}
|
|
248
|
+
sentiment="neutral"
|
|
249
|
+
description={
|
|
250
|
+
<>
|
|
251
|
+
Your transfer is subject to our{' '}
|
|
252
|
+
<Link href="/fees" target="_blank" type="link-default">
|
|
253
|
+
fee schedule
|
|
254
|
+
</Link>
|
|
255
|
+
.
|
|
256
|
+
</>
|
|
257
|
+
}
|
|
258
|
+
/>
|
|
259
|
+
<InfoPrompt
|
|
260
|
+
{...args}
|
|
261
|
+
sentiment="warning"
|
|
262
|
+
title="Verification Required"
|
|
263
|
+
description={
|
|
264
|
+
<>
|
|
265
|
+
Please{' '}
|
|
266
|
+
<Link href="/verify" type="link-default">
|
|
267
|
+
verify your identity
|
|
268
|
+
</Link>{' '}
|
|
269
|
+
to continue sending money.
|
|
270
|
+
</>
|
|
271
|
+
}
|
|
272
|
+
/>
|
|
273
|
+
</>
|
|
274
|
+
),
|
|
275
|
+
};
|
|
276
|
+
|
|
235
277
|
/**
|
|
236
278
|
* When `onDismiss` is provided, a close button appears allowing users to dismiss the prompt.
|
|
237
279
|
* Note: The component itself is not automatically removed - you must handle state management.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { fireEvent, mockMatchMedia, render, screen, userEvent
|
|
1
|
+
import { act, fireEvent, mockMatchMedia, render, screen, userEvent } from '../../test-utils';
|
|
2
|
+
import { WDS_LIVE_REGION_DELAY_MS } from '../../common/constants';
|
|
2
3
|
import { resetLiveRegionAnnouncementQueue } from '../../common/liveRegion/LiveRegion';
|
|
3
4
|
import { InfoPrompt, InfoPromptProps } from './InfoPrompt';
|
|
4
5
|
|
|
@@ -15,11 +16,74 @@ describe('InfoPrompt', () => {
|
|
|
15
16
|
resetLiveRegionAnnouncementQueue();
|
|
16
17
|
});
|
|
17
18
|
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
jest.clearAllTimers();
|
|
21
|
+
jest.useRealTimers();
|
|
22
|
+
});
|
|
23
|
+
|
|
18
24
|
it('renders description', () => {
|
|
19
25
|
render(<InfoPrompt {...defaultProps} />);
|
|
20
26
|
expect(screen.getByText('Prompt description')).toBeInTheDocument();
|
|
21
27
|
});
|
|
22
28
|
|
|
29
|
+
it('renders rich-content description (ReactNode)', () => {
|
|
30
|
+
render(
|
|
31
|
+
<InfoPrompt
|
|
32
|
+
description={
|
|
33
|
+
<>
|
|
34
|
+
See our <a href="/fees">fee schedule</a>.
|
|
35
|
+
</>
|
|
36
|
+
}
|
|
37
|
+
/>,
|
|
38
|
+
);
|
|
39
|
+
expect(screen.getByRole('link', { name: 'fee schedule', hidden: true })).toBeInTheDocument();
|
|
40
|
+
expect(screen.getByRole('link', { name: 'fee schedule', hidden: true })).toHaveAttribute(
|
|
41
|
+
'href',
|
|
42
|
+
'/fees',
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('re-announces when rich-content description text changes', () => {
|
|
47
|
+
jest.useFakeTimers();
|
|
48
|
+
|
|
49
|
+
const { rerender } = render(
|
|
50
|
+
<InfoPrompt
|
|
51
|
+
description={
|
|
52
|
+
<>
|
|
53
|
+
See our <a href="/fees">fee schedule</a>.
|
|
54
|
+
</>
|
|
55
|
+
}
|
|
56
|
+
/>,
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
act(() => {
|
|
60
|
+
jest.advanceTimersByTime(WDS_LIVE_REGION_DELAY_MS);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const liveRegion = screen.getByRole('status');
|
|
64
|
+
expect(liveRegion.firstElementChild).not.toHaveAttribute('aria-hidden');
|
|
65
|
+
expect(liveRegion).toHaveTextContent('See our fee schedule.');
|
|
66
|
+
|
|
67
|
+
rerender(
|
|
68
|
+
<InfoPrompt
|
|
69
|
+
description={
|
|
70
|
+
<>
|
|
71
|
+
Read the updated <a href="/fees">pricing guide</a>.
|
|
72
|
+
</>
|
|
73
|
+
}
|
|
74
|
+
/>,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
expect(liveRegion.firstElementChild).toHaveAttribute('aria-hidden', 'true');
|
|
78
|
+
|
|
79
|
+
act(() => {
|
|
80
|
+
jest.advanceTimersByTime(WDS_LIVE_REGION_DELAY_MS);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(liveRegion.firstElementChild).not.toHaveAttribute('aria-hidden');
|
|
84
|
+
expect(liveRegion).toHaveTextContent('Read the updated pricing guide.');
|
|
85
|
+
});
|
|
86
|
+
|
|
23
87
|
it('renders title when provided', () => {
|
|
24
88
|
render(<InfoPrompt {...defaultProps} title="Test Title" />);
|
|
25
89
|
expect(screen.getByText('Test Title')).toBeInTheDocument();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HTMLAttributes, ReactNode, useState } from 'react';
|
|
1
|
+
import { Children, HTMLAttributes, ReactNode, isValidElement, useState } from 'react';
|
|
2
2
|
import { LiveRegion, Sentiment, Typography } from '../../common';
|
|
3
3
|
import type { AriaLive } from '../../common';
|
|
4
4
|
import { GiftBox } from '@transferwise/icons';
|
|
@@ -50,9 +50,10 @@ export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'ar
|
|
|
50
50
|
*/
|
|
51
51
|
title?: string;
|
|
52
52
|
/**
|
|
53
|
-
* Description
|
|
53
|
+
* Description content for the prompt (required). Accepts plain text or rich content (ReactNode).
|
|
54
|
+
* If the description contains an interactive element, the `action` prop must not be used simultaneously.
|
|
54
55
|
*/
|
|
55
|
-
description:
|
|
56
|
+
description: ReactNode;
|
|
56
57
|
className?: string;
|
|
57
58
|
/**
|
|
58
59
|
* Sets the ARIA live region politeness level.
|
|
@@ -86,7 +87,17 @@ export const InfoPrompt = ({
|
|
|
86
87
|
...restProps
|
|
87
88
|
}: InfoPromptProps) => {
|
|
88
89
|
const [shouldFire, setShouldFire] = useState<boolean>();
|
|
89
|
-
const announceOnChange = [
|
|
90
|
+
const announceOnChange = [
|
|
91
|
+
title,
|
|
92
|
+
Children.toArray(description)
|
|
93
|
+
.map((child) =>
|
|
94
|
+
isValidElement<{ children?: ReactNode }>(child) ? child.props.children : child,
|
|
95
|
+
)
|
|
96
|
+
.join(''),
|
|
97
|
+
action?.label,
|
|
98
|
+
]
|
|
99
|
+
.filter(Boolean)
|
|
100
|
+
.join('|');
|
|
90
101
|
const statusIconSentiment =
|
|
91
102
|
sentiment === 'success'
|
|
92
103
|
? Sentiment.POSITIVE
|