@transferwise/components 46.116.1 → 46.117.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/main.css +145 -156
- package/build/prompt/InlinePrompt/InlinePrompt.js +14 -8
- package/build/prompt/InlinePrompt/InlinePrompt.js.map +1 -1
- package/build/prompt/InlinePrompt/InlinePrompt.mjs +15 -9
- package/build/prompt/InlinePrompt/InlinePrompt.mjs.map +1 -1
- package/build/sentimentSurface/SentimentSurface.js +6 -5
- package/build/sentimentSurface/SentimentSurface.js.map +1 -1
- package/build/sentimentSurface/SentimentSurface.mjs +6 -5
- package/build/sentimentSurface/SentimentSurface.mjs.map +1 -1
- package/build/styles/button/Button.css +22 -22
- package/build/styles/button/Button.vars.css +21 -21
- package/build/styles/iconButton/IconButton.css +8 -8
- package/build/styles/link/Link.css +1 -0
- package/build/styles/main.css +145 -156
- package/build/styles/prompt/InlinePrompt/InlinePrompt.css +26 -105
- package/build/styles/sentimentSurface/SentimentSurface.css +88 -21
- package/build/types/button/_stories/helpers.d.ts +11 -0
- package/build/types/button/_stories/helpers.d.ts.map +1 -0
- package/build/types/prompt/InlinePrompt/InlinePrompt.d.ts +19 -3
- package/build/types/prompt/InlinePrompt/InlinePrompt.d.ts.map +1 -1
- package/build/types/sentimentSurface/SentimentSurface.d.ts +5 -4
- package/build/types/sentimentSurface/SentimentSurface.d.ts.map +1 -1
- package/build/types/sentimentSurface/SentimentSurface.types.d.ts +4 -16
- package/build/types/sentimentSurface/SentimentSurface.types.d.ts.map +1 -1
- package/build/types/test-utils/story-config.d.ts +25 -1
- package/build/types/test-utils/story-config.d.ts.map +1 -1
- package/package.json +13 -12
- package/src/button/Button.css +22 -22
- package/src/button/Button.less +1 -1
- package/src/button/Button.vars.css +21 -21
- package/src/button/Button.vars.less +63 -23
- package/src/button/{Button.accessibility.docs.mdx → _stories/Button.accessibility.docs.mdx} +1 -1
- package/src/button/_stories/Button.brightGreen.tests.story.tsx +31 -0
- package/src/button/_stories/Button.dark.tests.story.tsx +25 -0
- package/src/button/_stories/Button.default.tests.story.tsx +25 -0
- package/src/button/_stories/Button.forestGreen.tests.story.tsx +28 -0
- package/src/button/{Button.story.tsx → _stories/Button.story.tsx} +78 -113
- package/src/button/_stories/Button.tests.story.tsx +139 -0
- package/src/button/_stories/helpers.tsx +118 -0
- package/src/iconButton/IconButton.css +8 -8
- package/src/iconButton/IconButton.less +35 -4
- package/src/iconButton/IconButton.story.tsx +72 -3
- package/src/link/Link.css +1 -0
- package/src/link/Link.less +1 -0
- package/src/link/Link.story.tsx +28 -0
- package/src/main.css +145 -156
- package/src/prompt/InlinePrompt/InlinePrompt.css +26 -105
- package/src/prompt/InlinePrompt/InlinePrompt.less +31 -119
- package/src/prompt/InlinePrompt/InlinePrompt.spec.tsx +87 -29
- package/src/prompt/InlinePrompt/InlinePrompt.story.tsx +223 -31
- package/src/prompt/InlinePrompt/InlinePrompt.tsx +42 -11
- package/src/sentimentSurface/SentimentSurface.css +88 -21
- package/src/sentimentSurface/SentimentSurface.docs.mdx +32 -495
- package/src/sentimentSurface/SentimentSurface.less +151 -114
- package/src/sentimentSurface/SentimentSurface.spec.tsx +31 -11
- package/src/sentimentSurface/SentimentSurface.story.tsx +323 -108
- package/src/sentimentSurface/SentimentSurface.tests.story.tsx +90 -40
- package/src/sentimentSurface/SentimentSurface.tsx +16 -9
- package/src/sentimentSurface/SentimentSurface.types.ts +5 -20
- package/src/test-utils/story-config.ts +5 -1
- package/build/sentimentSurface/classMap.js +0 -17
- package/build/sentimentSurface/classMap.js.map +0 -1
- package/build/sentimentSurface/classMap.mjs +0 -14
- package/build/sentimentSurface/classMap.mjs.map +0 -1
- package/build/types/sentimentSurface/classMap.d.ts +0 -4
- package/build/types/sentimentSurface/classMap.d.ts.map +0 -1
- package/src/button/Button.tests.story.tsx +0 -27
- package/src/sentimentSurface/classMap.ts +0 -15
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/// @FIXME all tokens here need to be adjusted after
|
|
2
|
-
/// the new sentiment tokens land
|
|
3
|
-
|
|
4
1
|
.wds-inline-prompt {
|
|
5
2
|
display: inline-flex;
|
|
6
3
|
text-align: left;
|
|
@@ -16,18 +13,37 @@
|
|
|
16
13
|
&:has(button) {
|
|
17
14
|
position: relative;
|
|
18
15
|
z-index: 1;
|
|
16
|
+
|
|
17
|
+
&:hover {
|
|
18
|
+
background-color: var(--color-sentiment-background-surface-hover);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&:active {
|
|
22
|
+
background-color: var(--color-sentiment-background-surface-active);
|
|
23
|
+
}
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
&--muted {
|
|
22
|
-
opacity: .93;
|
|
27
|
+
opacity: 0.93;
|
|
23
28
|
filter: grayscale(1);
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
a,
|
|
31
|
+
a,
|
|
32
|
+
button {
|
|
33
|
+
color: var(--color-sentiment-content-primary);
|
|
27
34
|
text-underline-offset: calc(var(--size-4) / 2);
|
|
35
|
+
|
|
36
|
+
&:hover {
|
|
37
|
+
color: var(--color-sentiment-content-primary-hover);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&:active {
|
|
41
|
+
color: var(--color-sentiment-content-primary-active);
|
|
42
|
+
}
|
|
43
|
+
|
|
28
44
|
&:first-of-type {
|
|
29
45
|
&:before {
|
|
30
|
-
content:
|
|
46
|
+
content: "";
|
|
31
47
|
position: absolute;
|
|
32
48
|
inset: 0;
|
|
33
49
|
}
|
|
@@ -41,122 +57,18 @@
|
|
|
41
57
|
|
|
42
58
|
.tw-icon-tags,
|
|
43
59
|
.tw-icon-confetti {
|
|
44
|
-
color: var(--color-sentiment-
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
&--negative {
|
|
49
|
-
background-color: var(--color-sentiment-negative-secondary);
|
|
50
|
-
color: var(--color-sentiment-negative-primary);
|
|
51
|
-
|
|
52
|
-
a, button {
|
|
53
|
-
color: var(--color-sentiment-negative-primary);
|
|
54
|
-
|
|
55
|
-
&:hover {
|
|
56
|
-
color: var(--color-sentiment-negative-primary-hover);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
&:active {
|
|
60
|
-
color: var(--color-sentiment-negative-primary-active);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.wds-inline-prompt&:has(a, button) {
|
|
65
|
-
&:hover {
|
|
66
|
-
background-color: var(--color-sentiment-negative-secondary-hover);
|
|
67
|
-
}
|
|
68
|
-
&:active {
|
|
69
|
-
background-color: var(--color-sentiment-negative-secondary-active);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
&--positive {
|
|
75
|
-
background-color: var(--color-sentiment-positive-secondary);
|
|
76
|
-
color: var(--color-sentiment-positive-primary);
|
|
77
|
-
a, button {
|
|
78
|
-
color: var(--color-sentiment-positive-primary);
|
|
79
|
-
&:hover {
|
|
80
|
-
color: var(--color-sentiment-positive-primary-hover);
|
|
81
|
-
}
|
|
82
|
-
&:active {
|
|
83
|
-
color: var(--color-sentiment-positive-primary-active);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
.wds-inline-prompt&:has(a, button) {
|
|
87
|
-
&:hover {
|
|
88
|
-
background-color: var(--color-sentiment-positive-secondary-hover);
|
|
89
|
-
}
|
|
90
|
-
&:active {
|
|
91
|
-
background-color: var(--color-sentiment-positive-secondary-active);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
&--proposition {
|
|
97
|
-
background-color: #D2F9F7;
|
|
98
|
-
|
|
99
|
-
color: var(--color-interactive-primary);
|
|
100
|
-
|
|
101
|
-
a, button {
|
|
102
|
-
color: var(--color-interactive-primary);
|
|
103
|
-
&:hover {
|
|
104
|
-
color: var(--color-interactive-primary-hover);
|
|
105
|
-
}
|
|
106
|
-
&:active {
|
|
107
|
-
color: var(--color-interactive-primary-active);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
.wds-inline-prompt&:has(a, button) {
|
|
112
|
-
&:hover {
|
|
113
|
-
background-color: #B2F4F3;
|
|
114
|
-
}
|
|
115
|
-
&:active {
|
|
116
|
-
background-color: #91F0EE;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
&--neutral {
|
|
122
|
-
background-color: var(--color-background-neutral);
|
|
123
|
-
color: var(--color-content-primary);
|
|
124
|
-
|
|
125
|
-
a, button {
|
|
126
|
-
color: var(--color-content-primary);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.wds-inline-prompt&:has(a, button) {
|
|
130
|
-
&:hover {
|
|
131
|
-
background-color: var(--color-background-neutral-hover);
|
|
132
|
-
}
|
|
133
|
-
&:active {
|
|
134
|
-
background-color: var(--color-background-neutral-active);
|
|
135
|
-
}
|
|
60
|
+
color: var(--color-sentiment-content-primary);
|
|
136
61
|
}
|
|
137
62
|
}
|
|
138
63
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
64
|
+
.wds-inline-prompt-process-indicator {
|
|
65
|
+
width: var(--size-16);
|
|
66
|
+
height: var(--size-16);
|
|
142
67
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
&:active {
|
|
149
|
-
color: var(--color-sentiment-warning-content-active);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
.wds-inline-prompt&:has(a, button) {
|
|
154
|
-
&:hover {
|
|
155
|
-
background-color: color-mix(in srgb, var(--color-sentiment-warning-secondary) 92%, var(--color-sentiment-warning-primary));
|
|
156
|
-
}
|
|
157
|
-
&:active {
|
|
158
|
-
background-color: color-mix(in srgb, var(--color-sentiment-warning-secondary) 84%, var(--color-sentiment-warning-primary));
|
|
159
|
-
}
|
|
160
|
-
}
|
|
68
|
+
// This belongs in ProcessIndicator but there are many conflicting
|
|
69
|
+
// styles in the CSS package, so keeping it colocated for now.
|
|
70
|
+
.process-circle {
|
|
71
|
+
stroke: currentColor;
|
|
72
|
+
};
|
|
161
73
|
}
|
|
162
74
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { mockMatchMedia, render, screen } from '../../test-utils';
|
|
3
2
|
import { InlinePrompt, InlinePromptProps } from './InlinePrompt';
|
|
4
3
|
import { Sentiment } from '../../common';
|
|
5
4
|
|
|
6
5
|
mockMatchMedia();
|
|
7
6
|
|
|
7
|
+
const MEDIA = <span data-testid="custom-media">Custom Media</span>;
|
|
8
|
+
|
|
8
9
|
describe('InlinePrompt', () => {
|
|
9
10
|
const defaultProps: InlinePromptProps = {
|
|
10
11
|
children: 'Prompt message',
|
|
@@ -21,39 +22,75 @@ describe('InlinePrompt', () => {
|
|
|
21
22
|
expect(screen.getByTestId('gift-box-icon')).toBeInTheDocument();
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
);
|
|
25
|
+
[
|
|
26
|
+
{ sentiment: Sentiment.NEGATIVE as const, acceptsMedia: false, statusIconLabel: 'Error:' },
|
|
27
|
+
{ sentiment: Sentiment.WARNING as const, acceptsMedia: false, statusIconLabel: 'Warning:' },
|
|
28
|
+
{ sentiment: Sentiment.NEUTRAL as const, acceptsMedia: false, statusIconLabel: 'Information:' },
|
|
29
|
+
{ sentiment: Sentiment.POSITIVE as const, acceptsMedia: true, statusIconLabel: '' },
|
|
30
|
+
{ sentiment: 'proposition' as const, acceptsMedia: true, statusIconLabel: '' },
|
|
31
|
+
].forEach(({ sentiment, statusIconLabel, acceptsMedia }) => {
|
|
32
|
+
describe(sentiment, () => {
|
|
33
|
+
it('should apply correct styles', () => {
|
|
34
|
+
render(<InlinePrompt {...defaultProps} sentiment={sentiment} />);
|
|
34
35
|
expect(screen.getByText('Prompt message').parentElement).toHaveClass(
|
|
35
36
|
`wds-inline-prompt--${sentiment}`,
|
|
36
37
|
);
|
|
37
|
-
}
|
|
38
|
-
);
|
|
39
|
-
});
|
|
38
|
+
});
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
expect(screen.getByTestId('InlinePrompt_Muted')).toBeInTheDocument();
|
|
48
|
-
});
|
|
40
|
+
if (statusIconLabel) {
|
|
41
|
+
it('should render StatusIcon', () => {
|
|
42
|
+
render(<InlinePrompt {...defaultProps} sentiment={sentiment} />);
|
|
43
|
+
expect(screen.getByLabelText(statusIconLabel)).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
describe('muted state', () => {
|
|
48
|
+
it('should render icon and apply css', () => {
|
|
49
|
+
render(<InlinePrompt {...defaultProps} muted sentiment={sentiment} />);
|
|
50
|
+
expect(screen.getByText('Prompt message').parentElement).toHaveClass(
|
|
51
|
+
'wds-inline-prompt--muted',
|
|
52
|
+
);
|
|
53
|
+
expect(screen.getByTestId('InlinePrompt_Muted')).toBeInTheDocument();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should trump `media`', () => {
|
|
57
|
+
render(<InlinePrompt {...defaultProps} muted sentiment={sentiment} media={MEDIA} />);
|
|
58
|
+
expect(screen.getByTestId('InlinePrompt_Muted')).toBeInTheDocument();
|
|
59
|
+
expect(screen.queryByTestId('custom-media')).not.toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('loading state', () => {
|
|
64
|
+
it('should render icon and apply css', () => {
|
|
65
|
+
render(<InlinePrompt {...defaultProps} sentiment={sentiment} loading />);
|
|
66
|
+
expect(screen.getByText('Prompt message').parentElement).toHaveClass(
|
|
67
|
+
'wds-inline-prompt--loading',
|
|
68
|
+
);
|
|
69
|
+
expect(screen.getByTestId('InlinePrompt_ProcessIndicator')).toBeInTheDocument();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should trump `media`', () => {
|
|
73
|
+
render(<InlinePrompt {...defaultProps} sentiment={sentiment} loading media={MEDIA} />);
|
|
74
|
+
expect(screen.getByTestId('InlinePrompt_ProcessIndicator')).toBeInTheDocument();
|
|
75
|
+
expect(screen.queryByTestId('custom-media')).not.toBeInTheDocument();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('custom media', () => {
|
|
80
|
+
if (acceptsMedia) {
|
|
81
|
+
it('should respect `media`', () => {
|
|
82
|
+
render(<InlinePrompt {...defaultProps} sentiment={sentiment} media={MEDIA} />);
|
|
83
|
+
expect(screen.getByTestId('custom-media')).toBeInTheDocument();
|
|
84
|
+
});
|
|
85
|
+
} else {
|
|
86
|
+
it('should ignore `media`', () => {
|
|
87
|
+
render(<InlinePrompt {...defaultProps} sentiment={sentiment} media={MEDIA} />);
|
|
88
|
+
expect(screen.getByLabelText(statusIconLabel)).toBeInTheDocument();
|
|
89
|
+
expect(screen.queryByTestId('custom-media')).not.toBeInTheDocument();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
57
94
|
});
|
|
58
95
|
|
|
59
96
|
it('applies custom className, id, and data-testid', () => {
|
|
@@ -69,4 +106,25 @@ describe('InlinePrompt', () => {
|
|
|
69
106
|
expect(el).toHaveClass('custom-class');
|
|
70
107
|
expect(el).toHaveAttribute('id', 'custom-id');
|
|
71
108
|
});
|
|
109
|
+
|
|
110
|
+
describe('SentimentSurface integration', () => {
|
|
111
|
+
it('maps positive sentiment to success for SentimentSurface', () => {
|
|
112
|
+
render(
|
|
113
|
+
<InlinePrompt {...defaultProps} sentiment={Sentiment.POSITIVE} data-testid="prompt" />,
|
|
114
|
+
);
|
|
115
|
+
const el = screen.getByTestId('prompt');
|
|
116
|
+
// The component should have both the original class and the SentimentSurface class
|
|
117
|
+
expect(el).toHaveClass('wds-inline-prompt--positive');
|
|
118
|
+
expect(el).toHaveClass('wds-sentiment-surface');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('passes through other sentiments unchanged', () => {
|
|
122
|
+
render(
|
|
123
|
+
<InlinePrompt {...defaultProps} sentiment={Sentiment.NEGATIVE} data-testid="prompt" />,
|
|
124
|
+
);
|
|
125
|
+
const el = screen.getByTestId('prompt');
|
|
126
|
+
expect(el).toHaveClass('wds-inline-prompt--negative');
|
|
127
|
+
expect(el).toHaveClass('wds-sentiment-surface');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
72
130
|
});
|
|
@@ -1,39 +1,231 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
3
|
+
import { Taxi, Travel } from '@transferwise/icons';
|
|
4
|
+
import { lorem5 } from '../../test-utils';
|
|
5
|
+
import Link from '../../link';
|
|
6
|
+
import { InlinePrompt, InlinePromptProps } from './InlinePrompt';
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const AllVariants = () => {
|
|
11
|
-
return (
|
|
8
|
+
const withComponentGrid =
|
|
9
|
+
({ maxWidth = 'auto', gap = '1rem' } = {}) =>
|
|
10
|
+
(Story: () => ReactElement) => (
|
|
12
11
|
<div
|
|
13
12
|
style={{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
width: '100%',
|
|
14
|
+
display: 'flex',
|
|
15
|
+
flexDirection: 'column',
|
|
16
|
+
gap,
|
|
17
|
+
maxWidth,
|
|
19
18
|
}}
|
|
20
19
|
>
|
|
21
|
-
<
|
|
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>
|
|
20
|
+
<Story />
|
|
37
21
|
</div>
|
|
38
22
|
);
|
|
23
|
+
|
|
24
|
+
export default {
|
|
25
|
+
title: 'Internal/InlinePrompt',
|
|
26
|
+
component: InlinePrompt,
|
|
27
|
+
args: {
|
|
28
|
+
loading: false,
|
|
29
|
+
muted: false,
|
|
30
|
+
children: lorem5,
|
|
31
|
+
},
|
|
32
|
+
argTypes: {
|
|
33
|
+
sentiment: {
|
|
34
|
+
control: 'select',
|
|
35
|
+
options: ['positive', 'negative', 'neutral', 'warning', 'proposition'],
|
|
36
|
+
},
|
|
37
|
+
loading: {
|
|
38
|
+
control: 'boolean',
|
|
39
|
+
},
|
|
40
|
+
muted: {
|
|
41
|
+
control: 'boolean',
|
|
42
|
+
},
|
|
43
|
+
children: {
|
|
44
|
+
control: 'text',
|
|
45
|
+
table: {
|
|
46
|
+
type: { summary: 'ReactNode' },
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
parameters: {
|
|
51
|
+
docs: {
|
|
52
|
+
toc: true,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
} satisfies Meta<InlinePromptProps>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Convenience controls for previewing rich markup,
|
|
59
|
+
* not otherwise possible via Storybook
|
|
60
|
+
*/
|
|
61
|
+
type PreviewStoryArgs = InlinePromptProps & {
|
|
62
|
+
previewMedia: InlinePromptProps['media'];
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const previewArgGroup = {
|
|
66
|
+
category: 'Storybook Preview options',
|
|
67
|
+
type: {
|
|
68
|
+
summary: undefined,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const previewArgTypes = {
|
|
73
|
+
previewMedia: {
|
|
74
|
+
name: 'Preview with `media`',
|
|
75
|
+
control: 'boolean',
|
|
76
|
+
mapping: {
|
|
77
|
+
false: undefined,
|
|
78
|
+
true: <Taxi />,
|
|
79
|
+
},
|
|
80
|
+
table: previewArgGroup,
|
|
81
|
+
},
|
|
82
|
+
} as const;
|
|
83
|
+
|
|
84
|
+
const getPropsForPreview = (
|
|
85
|
+
args: PreviewStoryArgs,
|
|
86
|
+
): [InlinePromptProps, Partial<InlinePromptProps>] => {
|
|
87
|
+
const { previewMedia, ...props } = args;
|
|
88
|
+
|
|
89
|
+
return [
|
|
90
|
+
props,
|
|
91
|
+
{
|
|
92
|
+
media: previewMedia,
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const Playground: StoryObj<PreviewStoryArgs> = {
|
|
98
|
+
tags: ['!autodocs'],
|
|
99
|
+
argTypes: previewArgTypes,
|
|
100
|
+
args: {
|
|
101
|
+
previewMedia: false,
|
|
102
|
+
},
|
|
103
|
+
render: (args: PreviewStoryArgs) => {
|
|
104
|
+
const [props, previewProps] = getPropsForPreview(args);
|
|
105
|
+
return <InlinePrompt {...props} {...previewProps} />;
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Aside from the known `neutral`, `negative`, `warning` and `positive` sentiments, `InlinePrompt` is the first of the prompts to introduce a new `proposition` sentiment, which can be used to encourage the user to take an action that might benefit them.
|
|
111
|
+
*
|
|
112
|
+
* By default, its associated icon for the prompt is `GiftBox`.
|
|
113
|
+
*/
|
|
114
|
+
export const Sentiments: StoryObj<PreviewStoryArgs> = {
|
|
115
|
+
argTypes: previewArgTypes,
|
|
116
|
+
args: {
|
|
117
|
+
previewMedia: false,
|
|
118
|
+
},
|
|
119
|
+
render: (args: PreviewStoryArgs) => {
|
|
120
|
+
const [props, previewProps] = getPropsForPreview(args);
|
|
121
|
+
return (
|
|
122
|
+
<>
|
|
123
|
+
<InlinePrompt {...props} {...previewProps} sentiment="neutral">
|
|
124
|
+
This is a neutral prompt.
|
|
125
|
+
</InlinePrompt>
|
|
126
|
+
<InlinePrompt {...props} {...previewProps} sentiment="negative">
|
|
127
|
+
This is a negative prompt.
|
|
128
|
+
</InlinePrompt>
|
|
129
|
+
<InlinePrompt {...props} {...previewProps} sentiment="warning">
|
|
130
|
+
This is a warning prompt.
|
|
131
|
+
</InlinePrompt>
|
|
132
|
+
<InlinePrompt {...props} {...previewProps} sentiment="positive">
|
|
133
|
+
This is a positive prompt.
|
|
134
|
+
</InlinePrompt>
|
|
135
|
+
<InlinePrompt {...props} {...previewProps} sentiment="proposition">
|
|
136
|
+
This is a proposition prompt.
|
|
137
|
+
</InlinePrompt>
|
|
138
|
+
</>
|
|
139
|
+
);
|
|
140
|
+
},
|
|
141
|
+
decorators: [withComponentGrid()],
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Inline prompts are also used when forms need to check something in the background, like
|
|
146
|
+
* verifying an account number is real. Turning on the loading state will replace the icon with
|
|
147
|
+
* a spinner while waiting for the check to finish.
|
|
148
|
+
*/
|
|
149
|
+
export const Loading: StoryObj<PreviewStoryArgs> = {
|
|
150
|
+
argTypes: previewArgTypes,
|
|
151
|
+
args: {
|
|
152
|
+
loading: true,
|
|
153
|
+
previewMedia: false,
|
|
154
|
+
},
|
|
155
|
+
render: (args: PreviewStoryArgs) => {
|
|
156
|
+
const [props, previewProps] = getPropsForPreview(args);
|
|
157
|
+
return <InlinePrompt {...props} {...previewProps} />;
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Inline prompt is usually associated with a different component, such as `Input` or `ListItem`.
|
|
163
|
+
* When those components are disabled, the prompt is often used to communicate to the user why they
|
|
164
|
+
* cannot interact with them. For that reason, the prompt cannot inherit the usual disabled visual
|
|
165
|
+
* style, and it must retain its interactivity. To bring these 2 components visually closer to each
|
|
166
|
+
* other, `muted` prop introduces slightly reduced opacity and luminosity as well as a special
|
|
167
|
+
* backslash circle icon.
|
|
168
|
+
*/
|
|
169
|
+
export const Muted: StoryObj<PreviewStoryArgs> = {
|
|
170
|
+
argTypes: previewArgTypes,
|
|
171
|
+
args: {
|
|
172
|
+
muted: true,
|
|
173
|
+
previewMedia: false,
|
|
174
|
+
},
|
|
175
|
+
render: (args: PreviewStoryArgs) => {
|
|
176
|
+
const [props, previewProps] = getPropsForPreview(args);
|
|
177
|
+
return <InlinePrompt {...props} {...previewProps} />;
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* While main sentiments (`warning`, `negative`, `neutral`) must retain their associated
|
|
183
|
+
* `StatusIcons`, the `positive` and `proposition` ones allow for Icon overrides to bring it
|
|
184
|
+
* closer to the prompt's content.
|
|
185
|
+
*/
|
|
186
|
+
export const IconOverrides: StoryObj<PreviewStoryArgs> = {
|
|
187
|
+
render: (args: PreviewStoryArgs) => {
|
|
188
|
+
return (
|
|
189
|
+
<>
|
|
190
|
+
<InlinePrompt {...args} media={<Travel />} sentiment="positive">
|
|
191
|
+
Your travel account is set up and ready to use.
|
|
192
|
+
</InlinePrompt>
|
|
193
|
+
<InlinePrompt {...args} media={<Taxi />} sentiment="proposition">
|
|
194
|
+
Connect Wise with your taxi app to get exclusive discounts.
|
|
195
|
+
</InlinePrompt>
|
|
196
|
+
<InlinePrompt {...args} media={<Taxi />} sentiment="negative">
|
|
197
|
+
This icon override is ignored.
|
|
198
|
+
</InlinePrompt>
|
|
199
|
+
</>
|
|
200
|
+
);
|
|
201
|
+
},
|
|
202
|
+
decorators: [withComponentGrid()],
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* When configured with any of the supported sentiments, the colour scheme of the component will propagate to all of its supported descendants, such as instances of a `Link`, `Icon`, and `StatusIcon`.
|
|
207
|
+
*/
|
|
208
|
+
export const SentimentAwareness: StoryObj<PreviewStoryArgs> = {
|
|
209
|
+
argTypes: previewArgTypes,
|
|
210
|
+
args: {
|
|
211
|
+
previewMedia: false,
|
|
212
|
+
},
|
|
213
|
+
render: (args: PreviewStoryArgs) => {
|
|
214
|
+
const [props, previewProps] = getPropsForPreview(args);
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<>
|
|
218
|
+
<InlinePrompt {...props} {...previewProps} sentiment="negative">
|
|
219
|
+
This is a negative prompt with an <Link href="#">inline link</Link>.
|
|
220
|
+
</InlinePrompt>
|
|
221
|
+
<InlinePrompt {...props} {...previewProps} sentiment="positive">
|
|
222
|
+
This is a positive prompt with an <Link href="#">inline link</Link>.
|
|
223
|
+
</InlinePrompt>
|
|
224
|
+
<InlinePrompt {...props} {...previewProps} sentiment="proposition">
|
|
225
|
+
This is a proposition prompt with an <Link href="#">inline link</Link>.
|
|
226
|
+
</InlinePrompt>
|
|
227
|
+
</>
|
|
228
|
+
);
|
|
229
|
+
},
|
|
230
|
+
decorators: [withComponentGrid()],
|
|
39
231
|
};
|
|
@@ -4,23 +4,40 @@ import ProcessIndicator from '../../processIndicator';
|
|
|
4
4
|
import StatusIcon from '../../statusIcon';
|
|
5
5
|
import { clsx } from 'clsx';
|
|
6
6
|
import Body from '../../body';
|
|
7
|
+
import SentimentSurface from '../../sentimentSurface';
|
|
7
8
|
|
|
8
9
|
export type InlinePromptProps = {
|
|
10
|
+
/**
|
|
11
|
+
* The sentiment determines the colour scheme
|
|
12
|
+
*/
|
|
9
13
|
sentiment?:
|
|
10
14
|
| `${Sentiment.POSITIVE | Sentiment.NEGATIVE | Sentiment.NEUTRAL | Sentiment.WARNING}`
|
|
11
15
|
| 'proposition';
|
|
16
|
+
/**
|
|
17
|
+
* Replaces the icon with a spinner while waiting for the short-lived action to finish.
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
12
20
|
loading?: boolean;
|
|
13
21
|
/**
|
|
14
|
-
*
|
|
22
|
+
* While prompts cannot be fully (visually and functionally) disabled, this prop should be enabled
|
|
23
|
+
* they are associated with actually disabled component (e.g. a disabled list item or input).
|
|
24
|
+
* @default false
|
|
15
25
|
*/
|
|
16
26
|
muted?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Icon override for `proposition` and `positive` sentiments. Unsupported for remaining ones.
|
|
29
|
+
*/
|
|
30
|
+
media?: React.ReactNode;
|
|
17
31
|
id?: string;
|
|
18
32
|
className?: string;
|
|
19
33
|
'data-testid'?: string;
|
|
20
34
|
children: React.ReactNode;
|
|
21
|
-
media?: React.ReactNode;
|
|
22
35
|
};
|
|
23
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Inline prompts appear alongside a specific component on the screen. They help the user stay
|
|
39
|
+
* informed, fix something, or get more out of what they're doing.
|
|
40
|
+
*/
|
|
24
41
|
export const InlinePrompt = ({
|
|
25
42
|
sentiment = Sentiment.POSITIVE,
|
|
26
43
|
muted = false,
|
|
@@ -28,26 +45,40 @@ export const InlinePrompt = ({
|
|
|
28
45
|
className,
|
|
29
46
|
children,
|
|
30
47
|
media = null,
|
|
48
|
+
'data-testid': dataTestId,
|
|
31
49
|
...rest
|
|
32
50
|
}: InlinePromptProps) => {
|
|
51
|
+
const surfaceSentiment = sentiment === Sentiment.POSITIVE ? 'success' : sentiment;
|
|
52
|
+
|
|
33
53
|
const renderMedia = () => {
|
|
34
|
-
if (media && ['proposition', 'positive'].includes(sentiment)) {
|
|
35
|
-
return media;
|
|
36
|
-
}
|
|
37
|
-
if (sentiment === 'proposition') {
|
|
38
|
-
return <GiftBox />;
|
|
39
|
-
}
|
|
40
54
|
if (muted) {
|
|
41
55
|
return <BackslashCircle size={16} data-testid="InlinePrompt_Muted" />;
|
|
42
56
|
}
|
|
43
57
|
if (loading) {
|
|
44
|
-
return
|
|
58
|
+
return (
|
|
59
|
+
<ProcessIndicator
|
|
60
|
+
data-testid="InlinePrompt_ProcessIndicator"
|
|
61
|
+
size="xxs"
|
|
62
|
+
className="wds-inline-prompt-process-indicator"
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (sentiment === 'positive' && media) {
|
|
68
|
+
return media;
|
|
45
69
|
}
|
|
70
|
+
|
|
71
|
+
if (sentiment === 'proposition') {
|
|
72
|
+
return media || <GiftBox />;
|
|
73
|
+
}
|
|
74
|
+
|
|
46
75
|
return <StatusIcon size={16} sentiment={sentiment} />;
|
|
47
76
|
};
|
|
48
77
|
|
|
49
78
|
return (
|
|
50
|
-
<
|
|
79
|
+
<SentimentSurface
|
|
80
|
+
sentiment={surfaceSentiment}
|
|
81
|
+
data-testid={dataTestId}
|
|
51
82
|
className={clsx(
|
|
52
83
|
'wds-inline-prompt',
|
|
53
84
|
`wds-inline-prompt--${sentiment}`,
|
|
@@ -61,6 +92,6 @@ export const InlinePrompt = ({
|
|
|
61
92
|
>
|
|
62
93
|
<div className="wds-inline-prompt__media-wrapper">{renderMedia()}</div>
|
|
63
94
|
<Body>{children}</Body>
|
|
64
|
-
</
|
|
95
|
+
</SentimentSurface>
|
|
65
96
|
);
|
|
66
97
|
};
|