@transferwise/components 46.115.1 → 46.116.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.
Files changed (77) hide show
  1. package/build/alert/Alert.js +2 -1
  2. package/build/alert/Alert.js.map +1 -1
  3. package/build/alert/Alert.mjs +2 -1
  4. package/build/alert/Alert.mjs.map +1 -1
  5. package/build/criticalBanner/CriticalCommsBanner.js +1 -0
  6. package/build/criticalBanner/CriticalCommsBanner.js.map +1 -1
  7. package/build/criticalBanner/CriticalCommsBanner.mjs +1 -0
  8. package/build/criticalBanner/CriticalCommsBanner.mjs.map +1 -1
  9. package/build/main.css +428 -44
  10. package/build/mocks.js +7 -0
  11. package/build/mocks.js.map +1 -1
  12. package/build/mocks.mjs +7 -1
  13. package/build/mocks.mjs.map +1 -1
  14. package/build/sentimentSurface/SentimentSurface.js +43 -0
  15. package/build/sentimentSurface/SentimentSurface.js.map +1 -0
  16. package/build/sentimentSurface/SentimentSurface.mjs +39 -0
  17. package/build/sentimentSurface/SentimentSurface.mjs.map +1 -0
  18. package/build/sentimentSurface/classMap.js +17 -0
  19. package/build/sentimentSurface/classMap.js.map +1 -0
  20. package/build/sentimentSurface/classMap.mjs +14 -0
  21. package/build/sentimentSurface/classMap.mjs.map +1 -0
  22. package/build/statusIcon/StatusIcon.js +10 -1
  23. package/build/statusIcon/StatusIcon.js.map +1 -1
  24. package/build/statusIcon/StatusIcon.mjs +10 -1
  25. package/build/statusIcon/StatusIcon.mjs.map +1 -1
  26. package/build/styles/inputs/Input.css +2 -4
  27. package/build/styles/inputs/TextArea.css +2 -4
  28. package/build/styles/main.css +428 -44
  29. package/build/styles/popover/Popover.css +2 -4
  30. package/build/styles/sentimentSurface/SentimentSurface.css +420 -0
  31. package/build/styles/statusIcon/StatusIcon.css +4 -36
  32. package/build/types/alert/Alert.d.ts.map +1 -1
  33. package/build/types/criticalBanner/CriticalCommsBanner.d.ts +2 -1
  34. package/build/types/criticalBanner/CriticalCommsBanner.d.ts.map +1 -1
  35. package/build/types/mocks.d.ts +1 -0
  36. package/build/types/mocks.d.ts.map +1 -1
  37. package/build/types/sentimentSurface/SentimentSurface.d.ts +30 -0
  38. package/build/types/sentimentSurface/SentimentSurface.d.ts.map +1 -0
  39. package/build/types/sentimentSurface/SentimentSurface.types.d.ts +80 -0
  40. package/build/types/sentimentSurface/SentimentSurface.types.d.ts.map +1 -0
  41. package/build/types/sentimentSurface/classMap.d.ts +4 -0
  42. package/build/types/sentimentSurface/classMap.d.ts.map +1 -0
  43. package/build/types/sentimentSurface/index.d.ts +3 -0
  44. package/build/types/sentimentSurface/index.d.ts.map +1 -0
  45. package/build/types/statusIcon/StatusIcon.d.ts.map +1 -1
  46. package/build/types/test-utils/window-mock.d.ts +1 -0
  47. package/build/types/test-utils/window-mock.d.ts.map +1 -1
  48. package/package.json +2 -2
  49. package/src/alert/Alert.tsx +3 -1
  50. package/src/criticalBanner/CriticalCommsBanner.tsx +3 -2
  51. package/src/expressiveMoneyInput/ExpressiveMoneyInput.spec.tsx +229 -0
  52. package/src/expressiveMoneyInput/amountInput/AmountInput.spec.tsx +282 -0
  53. package/src/expressiveMoneyInput/currencySelector/CurrencySelector.spec.tsx +160 -0
  54. package/src/inputs/Input.css +2 -4
  55. package/src/inputs/SelectInput.spec.tsx +7 -1
  56. package/src/inputs/TextArea.css +2 -4
  57. package/src/main.css +428 -44
  58. package/src/main.less +2 -0
  59. package/src/mocks.ts +7 -0
  60. package/src/moneyInput/MoneyInput.spec.tsx +9 -1
  61. package/src/popover/Popover.css +2 -4
  62. package/src/provider/theme/ThemeProvider.story.tsx +78 -11
  63. package/src/sentimentSurface/SentimentSurface.css +420 -0
  64. package/src/sentimentSurface/SentimentSurface.docs.mdx +549 -0
  65. package/src/sentimentSurface/SentimentSurface.less +293 -0
  66. package/src/sentimentSurface/SentimentSurface.spec.tsx +140 -0
  67. package/src/sentimentSurface/SentimentSurface.story.tsx +303 -0
  68. package/src/sentimentSurface/SentimentSurface.tests.story.tsx +72 -0
  69. package/src/sentimentSurface/SentimentSurface.tsx +72 -0
  70. package/src/sentimentSurface/SentimentSurface.types.ts +104 -0
  71. package/src/sentimentSurface/classMap.ts +15 -0
  72. package/src/sentimentSurface/index.ts +8 -0
  73. package/src/statusIcon/StatusIcon.css +4 -36
  74. package/src/statusIcon/StatusIcon.less +3 -41
  75. package/src/statusIcon/StatusIcon.tsx +14 -1
  76. package/src/test-utils/jest.setup.ts +0 -5
  77. package/src/test-utils/window-mock.ts +5 -0
@@ -0,0 +1,303 @@
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import SentimentSurface from './SentimentSurface';
3
+ import { storyConfig } from '../test-utils';
4
+ import type { Sentiment, Emphasis } from './SentimentSurface.types';
5
+
6
+ const withContainer = (Story: () => JSX.Element) => (
7
+ <div style={{ display: 'flex', justifyContent: 'center', padding: '2rem' }}>
8
+ <Story />
9
+ </div>
10
+ );
11
+
12
+ const withComponentGrid = (Story: () => JSX.Element) => (
13
+ <div
14
+ style={{
15
+ width: '100%',
16
+ display: 'flex',
17
+ flexDirection: 'column',
18
+ gap: '1rem',
19
+ maxWidth: '800px',
20
+ }}
21
+ >
22
+ <Story />
23
+ </div>
24
+ );
25
+
26
+ /**
27
+ * SentimentSurface is a polymorphic container component that applies contextual background colours
28
+ * and text styling based on sentiment types.
29
+ */
30
+ const meta: Meta<typeof SentimentSurface> = {
31
+ component: SentimentSurface,
32
+ title: 'Content/SentimentSurface',
33
+ argTypes: {
34
+ sentiment: {
35
+ control: 'select',
36
+ options: ['negative', 'warning', 'neutral', 'success', 'proposition'],
37
+ description: 'The sentiment type that determines the colour scheme',
38
+ },
39
+ emphasis: {
40
+ control: 'select',
41
+ options: ['base', 'elevated'],
42
+ description: 'The emphasis level affecting background and text contrast',
43
+ table: {
44
+ defaultValue: { summary: 'base' },
45
+ },
46
+ },
47
+ as: {
48
+ control: 'text',
49
+ description: 'The underlying HTML element to render',
50
+ table: {
51
+ defaultValue: { summary: 'div' },
52
+ },
53
+ },
54
+ children: {
55
+ control: 'text',
56
+ description: 'Content to render inside the surface',
57
+ },
58
+ className: {
59
+ control: 'text',
60
+ description: 'Additional CSS classes',
61
+ },
62
+ id: {
63
+ control: 'text',
64
+ description: 'Unique identifier for the component',
65
+ },
66
+ testId: {
67
+ control: 'text',
68
+ description: 'Test ID for React Testing Library',
69
+ },
70
+ },
71
+ args: {
72
+ sentiment: 'neutral',
73
+ emphasis: 'base',
74
+ children: 'This is a sentiment surface',
75
+ },
76
+ decorators: [withContainer],
77
+ parameters: {
78
+ docs: {
79
+ toc: true,
80
+ },
81
+ },
82
+ };
83
+
84
+ export default meta;
85
+
86
+ type Story = StoryObj<typeof SentimentSurface>;
87
+
88
+ /**
89
+ * The Playground story allows you to experiment with all the props of the SentimentSurface component.
90
+ * Use the controls below to customise the sentiment type, emphasis level, and content.
91
+ */
92
+ export const Playground: Story = {
93
+ args: {
94
+ sentiment: 'negative',
95
+ emphasis: 'base',
96
+ children: (
97
+ <div
98
+ style={{
99
+ padding: '24px',
100
+ backgroundColor: 'var(--color-sentiment-background-surface)',
101
+ color: 'var(--color-sentiment-content-primary)',
102
+ }}
103
+ >
104
+ This is a custom div demostrating a sentiment surface.
105
+ </div>
106
+ ),
107
+ },
108
+ };
109
+
110
+ /**
111
+ * Each sentiment type has two emphasis levels – base and elevated – to provide different levels of visual prominence.
112
+ */
113
+ export const EmphasisLevels: Story = {
114
+ render: (args) => (
115
+ <>
116
+ <SentimentSurface {...args} sentiment="negative" emphasis="base">
117
+ <div
118
+ style={{
119
+ padding: '24px',
120
+ backgroundColor: 'var(--color-sentiment-background-surface)',
121
+ color: 'var(--color-sentiment-content-primary)',
122
+ }}
123
+ >
124
+ Negative - Base emphasis
125
+ </div>
126
+ </SentimentSurface>
127
+ <SentimentSurface {...args} sentiment="negative" emphasis="elevated">
128
+ <div
129
+ style={{
130
+ padding: '24px',
131
+ backgroundColor: 'var(--color-sentiment-background-surface)',
132
+ color: 'var(--color-sentiment-content-primary)',
133
+ }}
134
+ >
135
+ Negative - Elevated emphasis
136
+ </div>
137
+ </SentimentSurface>
138
+ <SentimentSurface {...args} sentiment="success" emphasis="base">
139
+ <div
140
+ style={{
141
+ padding: '24px',
142
+ backgroundColor: 'var(--color-sentiment-background-surface)',
143
+ color: 'var(--color-sentiment-content-primary)',
144
+ }}
145
+ >
146
+ Success - Base emphasis
147
+ </div>
148
+ </SentimentSurface>
149
+ <SentimentSurface {...args} sentiment="success" emphasis="elevated">
150
+ <div
151
+ style={{
152
+ padding: '24px',
153
+ backgroundColor: 'var(--color-sentiment-background-surface)',
154
+ color: 'var(--color-sentiment-content-primary)',
155
+ }}
156
+ >
157
+ Success - Elevated emphasis
158
+ </div>
159
+ </SentimentSurface>
160
+ </>
161
+ ),
162
+ parameters: {
163
+ controls: { disable: true },
164
+ },
165
+ decorators: [withComponentGrid],
166
+ };
167
+
168
+ const sentiments: Sentiment[] = ['negative', 'warning', 'neutral', 'success', 'proposition'];
169
+ const emphasisLevels: Emphasis[] = ['base', 'elevated'];
170
+ const tokenCategories = [
171
+ {
172
+ name: 'Content',
173
+ tokens: [
174
+ '--color-sentiment-content-primary',
175
+ '--color-sentiment-content-primary-hover',
176
+ '--color-sentiment-content-primary-active',
177
+ ],
178
+ },
179
+ {
180
+ name: 'Interactive Primary',
181
+ tokens: [
182
+ '--color-sentiment-interactive-primary',
183
+ '--color-sentiment-interactive-primary-hover',
184
+ '--color-sentiment-interactive-primary-active',
185
+ ],
186
+ },
187
+ {
188
+ name: 'Interactive Secondary',
189
+ tokens: [
190
+ '--color-sentiment-interactive-secondary',
191
+ '--color-sentiment-interactive-secondary-hover',
192
+ '--color-sentiment-interactive-secondary-active',
193
+ ],
194
+ },
195
+ {
196
+ name: 'Interactive Control',
197
+ tokens: [
198
+ '--color-sentiment-interactive-control',
199
+ '--color-sentiment-interactive-control-hover',
200
+ '--color-sentiment-interactive-control-active',
201
+ ],
202
+ },
203
+ {
204
+ name: 'Background Surface',
205
+ tokens: [
206
+ '--color-sentiment-background-surface',
207
+ '--color-sentiment-background-surface-hover',
208
+ '--color-sentiment-background-surface-active',
209
+ ],
210
+ },
211
+ ];
212
+
213
+ const TokenSwatch = ({ token }: { token: string }) => (
214
+ <div style={{ display: 'flex', alignItems: 'center', gap: '12px', marginBottom: '4px' }}>
215
+ <div
216
+ style={{
217
+ width: '32px',
218
+ height: '32px',
219
+ borderRadius: '4px',
220
+ backgroundColor: `var(${token})`,
221
+ border: '1px solid rgba(0,0,0,0.1)',
222
+ flexShrink: 0,
223
+ }}
224
+ />
225
+ <code style={{ fontSize: '12px', color: 'var(--color-sentiment-content-primary)' }}>
226
+ {token}
227
+ </code>
228
+ </div>
229
+ );
230
+
231
+ /**
232
+ * Visual demonstration of the CSS custom properties (tokens) set by each sentiment and emphasis combination.
233
+ * These tokens enable child components to automatically adapt their styling to the sentiment context.
234
+ */
235
+ export const Tokens: Story = {
236
+ render: () => (
237
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '32px', maxWidth: '100%' }}>
238
+ {sentiments.map((sentiment) =>
239
+ emphasisLevels.map((emphasis) => (
240
+ <SentimentSurface
241
+ key={`${sentiment}-${emphasis}`}
242
+ sentiment={sentiment}
243
+ emphasis={emphasis}
244
+ style={{
245
+ padding: '24px',
246
+ borderRadius: '16px',
247
+ backgroundColor: 'var(--color-sentiment-background-surface)',
248
+ color: 'var(--color-sentiment-content-primary)',
249
+ }}
250
+ >
251
+ <div style={{ marginBottom: '16px' }}>
252
+ <strong
253
+ style={{
254
+ fontSize: '16px',
255
+ textTransform: 'capitalize',
256
+ }}
257
+ >
258
+ {sentiment} - {emphasis}
259
+ </strong>
260
+ </div>
261
+ <div
262
+ style={{
263
+ display: 'grid',
264
+ gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
265
+ gap: '24px',
266
+ }}
267
+ >
268
+ {tokenCategories.map((category) => (
269
+ <div key={category.name}>
270
+ <div
271
+ style={{
272
+ fontSize: '12px',
273
+ fontWeight: 600,
274
+ marginBottom: '8px',
275
+ textTransform: 'uppercase',
276
+ letterSpacing: '0.5px',
277
+ opacity: 0.7,
278
+ }}
279
+ >
280
+ {category.name}
281
+ </div>
282
+ {category.tokens.map((token) => (
283
+ <TokenSwatch key={token} token={token} />
284
+ ))}
285
+ </div>
286
+ ))}
287
+ </div>
288
+ </SentimentSurface>
289
+ )),
290
+ )}
291
+ </div>
292
+ ),
293
+ parameters: {
294
+ controls: { disable: true },
295
+ },
296
+ decorators: [
297
+ (Story: () => JSX.Element) => (
298
+ <div style={{ padding: '2rem', maxWidth: '1200px' }}>
299
+ <Story />
300
+ </div>
301
+ ),
302
+ ],
303
+ };
@@ -0,0 +1,72 @@
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import SentimentSurface from './SentimentSurface';
3
+
4
+ export default {
5
+ component: SentimentSurface,
6
+ title: 'Content/SentimentSurface/Tests',
7
+ } satisfies Meta<typeof SentimentSurface>;
8
+
9
+ type Story = StoryObj<typeof SentimentSurface>;
10
+
11
+ export const NestedSentiments: Story = {
12
+ render: () => (
13
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '24px' }}>
14
+ <SentimentSurface
15
+ sentiment="negative"
16
+ emphasis="base"
17
+ style={{
18
+ backgroundColor: 'var(--color-sentiment-background-surface)',
19
+ color: 'var(--color-sentiment-content-primary)',
20
+ }}
21
+ >
22
+ Outer: Negative, base
23
+ <div style={{ margin: '16px 0 0 16px' }}>
24
+ <SentimentSurface
25
+ sentiment="success"
26
+ emphasis="elevated"
27
+ style={{
28
+ backgroundColor: 'var(--color-sentiment-background-surface)',
29
+ color: 'var(--color-sentiment-content-primary)',
30
+ }}
31
+ >
32
+ Inner: Success, elevated
33
+ <div style={{ margin: '12px 0 0 12px' }}>
34
+ <SentimentSurface
35
+ sentiment="warning"
36
+ emphasis="base"
37
+ style={{
38
+ backgroundColor: 'var(--color-sentiment-background-surface)',
39
+ color: 'var(--color-sentiment-content-primary)',
40
+ }}
41
+ >
42
+ Deepest: Warning, base
43
+ </SentimentSurface>
44
+ </div>
45
+ </SentimentSurface>
46
+ </div>
47
+ </SentimentSurface>
48
+ <SentimentSurface
49
+ sentiment="proposition"
50
+ emphasis="elevated"
51
+ style={{
52
+ backgroundColor: 'var(--color-sentiment-background-surface)',
53
+ color: 'var(--color-sentiment-content-primary)',
54
+ }}
55
+ >
56
+ Outer: Proposition, elevated
57
+ <div style={{ margin: '16px 0 0 16px' }}>
58
+ <SentimentSurface
59
+ sentiment="neutral"
60
+ emphasis="base"
61
+ style={{
62
+ backgroundColor: 'var(--color-sentiment-background-surface)',
63
+ color: 'var(--color-sentiment-content-primary)',
64
+ }}
65
+ >
66
+ Inner: Neutral, base
67
+ </SentimentSurface>
68
+ </div>
69
+ </SentimentSurface>
70
+ </div>
71
+ ),
72
+ };
@@ -0,0 +1,72 @@
1
+ import { forwardRef, ElementType, ForwardedRef } from 'react';
2
+ import { clsx } from 'clsx';
3
+
4
+ import {
5
+ SentimentSurfaceComponentProps,
6
+ SentimentSurfaceComponent,
7
+ } from './SentimentSurface.types';
8
+ import { getSentimentSurfaceClassName } from './classMap';
9
+
10
+ /**
11
+ * SentimentSurface is a polymorphic container component that applies contextual background colours
12
+ * and text styling based on sentiment types (negative, warning, neutral, success, proposition).
13
+ * It's designed to visually communicate the nature or importance of its content through colour.
14
+ *
15
+ * @param {ElementType} [as='div'] - Optional prop to override the HTML element rendered.
16
+ * @param {Sentiment} sentiment - Required prop to set the sentiment type (negative, warning, neutral, success, proposition).
17
+ * @param {Emphasis} [emphasis='base'] - Optional prop to specify the emphasis level (base or elevated).
18
+ * @param {ReactNode} [children] - Content to render inside the surface.
19
+ * @param {string} [className] - Additional CSS classes to apply.
20
+ * @param {CSSProperties} [style] - Inline styles to apply.
21
+ * @param {string} [id] - Unique identifier for the component.
22
+ * @param {string} [testId] - A unique string that appears as data attribute `data-testid` in the rendered code.
23
+ *
24
+ * @component
25
+ * @example
26
+ * ```tsx
27
+ * // Basic usage with negative sentiment
28
+ * <SentimentSurface sentiment="negative">
29
+ * Your payment has failed
30
+ * </SentimentSurface>
31
+ * ```
32
+ *
33
+ * @see {@link SentimentSurface} for further information.
34
+ * @see {@link https://storybook.wise.design/?path=/docs/sentiment-surface--docs|Storybook Wise Design}
35
+ */
36
+ // @ts-expect-error - Generic forwardRef limitation. See: https://fettblog.eu/typescript-react-generic-forward-refs/
37
+ const SentimentSurface: SentimentSurfaceComponent = forwardRef(function SentimentSurface<
38
+ T extends ElementType = 'div',
39
+ >(
40
+ {
41
+ as,
42
+ sentiment,
43
+ emphasis = 'base',
44
+ className,
45
+ style,
46
+ children,
47
+ id,
48
+ testId,
49
+ ...props
50
+ }: SentimentSurfaceComponentProps<T>,
51
+ ref: ForwardedRef<HTMLElement>,
52
+ ) {
53
+ const Element = as ?? 'div';
54
+ const classNames = clsx(getSentimentSurfaceClassName(sentiment, emphasis), className);
55
+ const sentimentProps = {
56
+ ref,
57
+ id,
58
+ 'data-testid': testId,
59
+ className: classNames,
60
+ style,
61
+ ...props,
62
+ };
63
+
64
+ return (
65
+ // @ts-expect-error - Generic forwardRef limitation. See: https://fettblog.eu/typescript-react-generic-forward-refs/
66
+ <Element {...sentimentProps}>{children}</Element>
67
+ );
68
+ });
69
+
70
+ SentimentSurface.displayName = 'SentimentSurface';
71
+
72
+ export default SentimentSurface;
@@ -0,0 +1,104 @@
1
+ import {
2
+ ReactNode,
3
+ ElementType,
4
+ CSSProperties,
5
+ ComponentPropsWithoutRef,
6
+ HTMLAttributes,
7
+ } from 'react';
8
+
9
+ /**
10
+ * Sentiment types for the SentimentSurface component
11
+ */
12
+ export type Sentiment = 'negative' | 'warning' | 'neutral' | 'success' | 'proposition';
13
+
14
+ /**
15
+ * Emphasis levels for the SentimentSurface component
16
+ */
17
+ export type Emphasis = 'base' | 'elevated';
18
+
19
+ /**
20
+ * Common properties for the SentimentSurface component
21
+ */
22
+ export interface CommonProps {
23
+ /**
24
+ * The sentiment type that determines the colour scheme
25
+ */
26
+ sentiment: Sentiment;
27
+
28
+ /**
29
+ * The emphasis level affecting background and text contrast
30
+ * @default 'base'
31
+ */
32
+ emphasis?: Emphasis;
33
+
34
+ /**
35
+ * Content to render inside the surface
36
+ */
37
+ children?: ReactNode;
38
+
39
+ /**
40
+ * Additional CSS classes
41
+ */
42
+ className?: string;
43
+
44
+ /**
45
+ * Inline styles
46
+ */
47
+ style?: CSSProperties;
48
+
49
+ /**
50
+ * Unique identifier for the component
51
+ */
52
+ id?: string;
53
+
54
+ /**
55
+ * A unique string that appears as data attribute `data-testid` in the rendered code, serving as a hook for automated tests
56
+ */
57
+ testId?: string;
58
+
59
+ /**
60
+ * `data-testid` is strictly controlled through the `testId` prop.
61
+ * This lets consumers know that this data attribute will not be applied.
62
+ */
63
+ 'data-testid'?: never;
64
+ }
65
+
66
+ /**
67
+ * Props when rendering as a div or custom element
68
+ */
69
+ export type SentimentSurfaceDivProps<T extends ElementType = 'div'> = Omit<
70
+ HTMLAttributes<HTMLDivElement>,
71
+ keyof CommonProps
72
+ > &
73
+ CommonProps & {
74
+ /**
75
+ * The underlying HTML element to render
76
+ * @default 'div'
77
+ */
78
+ as?: T;
79
+ };
80
+
81
+ /**
82
+ * All possible props for the SentimentSurface component
83
+ */
84
+ export type SentimentSurfaceProps<T extends ElementType = 'div'> = SentimentSurfaceDivProps<T>;
85
+
86
+ /**
87
+ * Combines SentimentSurface props with all valid HTML attributes for the specified element type.
88
+ * Supports polymorphic rendering via the `as` prop while maintaining full type safety.
89
+ *
90
+ * @example
91
+ * <SentimentSurface sentiment="negative" role="alert" aria-live="polite">
92
+ * Error message
93
+ * </SentimentSurface>
94
+ */
95
+ export type SentimentSurfaceComponentProps<T extends ElementType = 'div'> =
96
+ SentimentSurfaceProps<T> & Omit<ComponentPropsWithoutRef<T>, keyof SentimentSurfaceProps<T>>;
97
+
98
+ /**
99
+ * The SentimentSurface component type signature
100
+ */
101
+ export interface SentimentSurfaceComponent {
102
+ <T extends ElementType = 'div'>(props: SentimentSurfaceComponentProps<T>): ReactNode;
103
+ displayName?: string;
104
+ }
@@ -0,0 +1,15 @@
1
+ import type { Sentiment, Emphasis } from './SentimentSurface.types';
2
+
3
+ const BASE_CLASS = 'wds-sentiment-surface';
4
+
5
+ export const sentimentClassMap: Record<Sentiment, string> = {
6
+ negative: `${BASE_CLASS}-negative`,
7
+ warning: `${BASE_CLASS}-warning`,
8
+ neutral: `${BASE_CLASS}-neutral`,
9
+ success: `${BASE_CLASS}-success`,
10
+ proposition: `${BASE_CLASS}-proposition`,
11
+ };
12
+
13
+ export const getSentimentSurfaceClassName = (sentiment: Sentiment, emphasis: Emphasis): string => {
14
+ return `${BASE_CLASS} ${sentimentClassMap[sentiment]}-${emphasis}`;
15
+ };
@@ -0,0 +1,8 @@
1
+ export { default } from './SentimentSurface';
2
+ export type {
3
+ SentimentSurfaceComponent,
4
+ SentimentSurfaceProps,
5
+ CommonProps,
6
+ Sentiment,
7
+ Emphasis,
8
+ } from './SentimentSurface.types';
@@ -1,38 +1,6 @@
1
- .status-circle .light {
2
- color: var(--color-contrast-overlay);
1
+ .wds-sentiment-surface.status-circle {
2
+ background-color: var(--color-sentiment-interactive-primary);
3
3
  }
4
- .np-theme-personal--bright-green .status-circle .light {
5
- color: var(--color-white);
6
- }
7
- .status-circle .dark {
8
- color: #37517e;
9
- color: var(--color-content-primary);
10
- }
11
- .status-circle.warning,
12
- .status-circle.pending {
13
- background-color: var(--color-sentiment-warning);
14
- }
15
- .np-theme-personal .status-circle.warning .status-icon,
16
- .np-theme-personal .status-circle.pending .status-icon {
17
- color: var(--color-dark);
18
- }
19
- .status-circle.negative,
20
- .status-circle.error {
21
- background-color: var(--color-sentiment-negative);
22
- }
23
- .status-circle.negative .status-icon,
24
- .status-circle.error .status-icon {
25
- color: var(--color-sentiment-negative-secondary);
26
- }
27
- .status-circle.neutral {
28
- background-color: #5d7079;
29
- background-color: var(--color-content-secondary);
30
- }
31
- .status-circle.positive,
32
- .status-circle.success {
33
- background-color: var(--color-sentiment-positive);
34
- }
35
- .status-circle.positive .status-icon,
36
- .status-circle.success .status-icon {
37
- color: var(--color-sentiment-positive-secondary);
4
+ .wds-sentiment-surface.status-circle .status-icon {
5
+ color: var(--color-sentiment-interactive-control);
38
6
  }
@@ -1,44 +1,6 @@
1
- .status-circle {
2
- .light {
3
- color: var(--color-contrast-overlay);
4
-
5
- .np-theme-personal--bright-green & {
6
- color: var(--color-white);
7
- }
8
- }
9
-
10
- .dark {
11
- color: var(--color-content-primary);
12
- }
13
-
14
- &.warning,
15
- &.pending {
16
- background-color: var(--color-sentiment-warning);
17
-
18
- .status-icon {
19
- .np-theme-personal & {
20
- color: var(--color-dark);
21
- }
22
- }
23
- }
24
- }
25
-
26
- .status-circle.negative,
27
- .status-circle.error {
28
- background-color: var(--color-sentiment-negative);
29
- .status-icon {
30
- color: var(--color-sentiment-negative-secondary);
31
- }
32
- }
33
-
34
- .status-circle.neutral {
35
- background-color: var(--color-content-secondary);
36
- }
37
-
38
- .status-circle.positive,
39
- .status-circle.success {
40
- background-color: var(--color-sentiment-positive);
1
+ .wds-sentiment-surface.status-circle {
2
+ background-color: var(--color-sentiment-interactive-primary);
41
3
  .status-icon {
42
- color: var(--color-sentiment-positive-secondary);
4
+ color: var(--color-sentiment-interactive-control);
43
5
  }
44
6
  }
@@ -1,7 +1,9 @@
1
1
  import { Info, Alert, Cross, Check, ClockBorderless } from '@transferwise/icons';
2
2
  import { clsx } from 'clsx';
3
3
  import { useIntl } from 'react-intl';
4
+ import { PropsWithChildren } from 'react';
4
5
 
6
+ import SentimentSurface from '../sentimentSurface';
5
7
  import { SizeSmall, SizeMedium, SizeLarge, Sentiment, Size, Breakpoint, Status } from '../common';
6
8
  import Circle, { CircleProps } from '../common/circle';
7
9
  import { useMedia } from '../common/hooks/useMedia';
@@ -81,9 +83,20 @@ const StatusIcon = ({ sentiment = 'neutral', size: sizeProp = 40, iconLabel }: S
81
83
  const iconColor = sentiment === 'warning' || sentiment === 'pending' ? 'dark' : 'light';
82
84
  const isTinyViewport = useMedia(`(max-width: ${Breakpoint.ZOOM_400}px)`);
83
85
  const size = mapLegacySize[sizeProp] ?? sizeProp;
86
+ // eslint-disable-next-line react/no-unstable-nested-components
87
+ const SentimentSurfaceSetting = (props: PropsWithChildren<Pick<CircleProps, 'className'>>) => (
88
+ <SentimentSurface
89
+ as="span"
90
+ // @ts-expect-error sentiment and SentimentSurface types mismatch
91
+ sentiment={
92
+ sentiment === 'positive' ? 'success' : sentiment === 'pending' ? 'warning' : sentiment
93
+ }
94
+ {...props}
95
+ />
96
+ );
84
97
  return (
85
98
  <Circle
86
- as="span"
99
+ as={SentimentSurfaceSetting}
87
100
  size={isTinyViewport && size < 40 ? 32 : size}
88
101
  data-testid="status-icon"
89
102
  className={clsx('status-circle', sentiment)}