uikit-react-public 0.32.3 → 0.32.5

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.
@@ -49,6 +49,17 @@ export const Error: Story = {
49
49
  },
50
50
  };
51
51
 
52
+ export const Success: Story = {
53
+ name: 'Variant: Success',
54
+ args: {
55
+ action: 'close',
56
+ children: 'This is a success snackbar',
57
+ size: 'small',
58
+ variant: 'success',
59
+ onClose: () => alert('Close action triggered'),
60
+ },
61
+ };
62
+
52
63
  export const LongText: Story = {
53
64
  name: 'With long text',
54
65
  args: {
@@ -14,7 +14,7 @@ export interface SnackbarBaseProps extends HTMLAttributes<HTMLDivElement> {
14
14
  onUndo?: () => void;
15
15
  size?: 'small' | 'large';
16
16
  testId?: string;
17
- variant?: 'default' | 'error';
17
+ variant?: 'default' | 'error' | 'success';
18
18
  ref?: Ref<HTMLDivElement>;
19
19
  }
20
20
 
@@ -35,8 +35,17 @@ const Snackbar = ({
35
35
  ...props
36
36
  }: SnackbarProps) => {
37
37
  const [theme] = useTheme();
38
- const IconComponent =
39
- variant === 'error' ? Icon.AlertTriangle : Icon.CheckCircle;
38
+ const IconComponent = {
39
+ default: Icon.Info,
40
+ error: Icon.AlertTriangle,
41
+ success: Icon.CheckCircle,
42
+ }[variant];
43
+ // Success token currently differs from Figma (#008E49).
44
+ const backgroundColor = {
45
+ default: theme.colour.fill.brandPrimary,
46
+ error: theme.colour.fill.critical,
47
+ success: theme.colour.fill.success,
48
+ }[variant];
40
49
  const role = variant === 'error' ? 'alert' : 'status';
41
50
  const iconSize = size === 'small' ? 20 : 32;
42
51
  const padding = size === 'small' ? theme.padding.p16 : theme.padding.p24;
@@ -56,11 +65,7 @@ const Snackbar = ({
56
65
  z-index: 9999;
57
66
  padding: ${padding};
58
67
  color: ${theme.colour.text.inverse};
59
- background-color: ${
60
- variant === 'error'
61
- ? theme.primitiveColour.red['10'].$value.hex
62
- : theme.colour.bg.inversePrimary
63
- };
68
+ background-color: ${backgroundColor};
64
69
  `;
65
70
 
66
71
  const style = cx(NAME, baseStyles, marginsStyle(props, theme), className);
@@ -168,7 +173,7 @@ const Snackbar = ({
168
173
  level='lg-semibold'
169
174
  className={undoButtonTextStyle}
170
175
  >
171
- UNDO
176
+ Undo
172
177
  </Text>
173
178
  </button>
174
179
  )}
@@ -116,6 +116,17 @@ export const Interactive: Story = {
116
116
  >
117
117
  Add error snackbar
118
118
  </Button>
119
+ <Button
120
+ onClick={() =>
121
+ addSnackbar({
122
+ children: 'Success snackbar',
123
+ size: 'small',
124
+ variant: 'success',
125
+ })
126
+ }
127
+ >
128
+ Add success snackbar
129
+ </Button>
119
130
  </div>
120
131
  <Snackbars
121
132
  {...args}
@@ -2,18 +2,32 @@ import { describe, expect, test, vi, beforeEach } from 'vitest';
2
2
  import { render, screen } from '@testing-library/react';
3
3
  // import '@testing-library/jest-dom';
4
4
  import Snackbar from '../Snackbar';
5
- import { ThemeContextProvider } from '../../../theme/useTheme';
5
+ import Icon from '../../Icon';
6
+ import { ThemeContextProvider, themes } from '../../../theme/useTheme';
7
+
8
+ const tokenTestTheme = {
9
+ ...themes.light,
10
+ colour: {
11
+ ...themes.light.colour,
12
+ fill: {
13
+ ...themes.light.colour.fill,
14
+ brandPrimary: '#010101',
15
+ critical: '#020202',
16
+ success: '#030303',
17
+ },
18
+ },
19
+ };
6
20
 
7
21
  describe('Snackbar', () => {
8
22
  beforeEach(() => {
9
- vi.clearAllMocks();
23
+ vi.restoreAllMocks();
10
24
  });
11
25
 
12
26
  // Snapshot tests
13
27
 
14
28
  test('snapshot: minimal props', () => {
15
29
  const renderResult = render(
16
- <ThemeContextProvider>
30
+ <ThemeContextProvider theme='light'>
17
31
  <Snackbar action='close'>Test snackbar</Snackbar>
18
32
  </ThemeContextProvider>
19
33
  );
@@ -22,7 +36,7 @@ describe('Snackbar', () => {
22
36
 
23
37
  test('snapshot: custom test id', () => {
24
38
  const renderResult = render(
25
- <ThemeContextProvider>
39
+ <ThemeContextProvider theme='light'>
26
40
  <Snackbar
27
41
  action='undo'
28
42
  testId='custom-test-id'
@@ -39,7 +53,7 @@ describe('Snackbar', () => {
39
53
  test('Test ID: Default', () => {
40
54
  const defaultTestId = 'ucl-uikit-snackbar';
41
55
  render(
42
- <ThemeContextProvider>
56
+ <ThemeContextProvider theme='light'>
43
57
  <Snackbar action='close'>Test snackbar</Snackbar>
44
58
  </ThemeContextProvider>
45
59
  );
@@ -49,7 +63,7 @@ describe('Snackbar', () => {
49
63
 
50
64
  test('Test ID: Custom', () => {
51
65
  render(
52
- <ThemeContextProvider>
66
+ <ThemeContextProvider theme='light'>
53
67
  <Snackbar
54
68
  action='undo'
55
69
  testId='custom-test-id'
@@ -66,7 +80,7 @@ describe('Snackbar', () => {
66
80
  const ref = { current: null as HTMLDivElement | null };
67
81
 
68
82
  render(
69
- <ThemeContextProvider>
83
+ <ThemeContextProvider theme='light'>
70
84
  <Snackbar ref={ref}>Test snackbar</Snackbar>
71
85
  </ThemeContextProvider>
72
86
  );
@@ -76,7 +90,7 @@ describe('Snackbar', () => {
76
90
 
77
91
  test('Default variant uses polite status semantics', () => {
78
92
  render(
79
- <ThemeContextProvider>
93
+ <ThemeContextProvider theme='light'>
80
94
  <Snackbar action='undo'>Test snackbar</Snackbar>
81
95
  </ThemeContextProvider>
82
96
  );
@@ -87,7 +101,7 @@ describe('Snackbar', () => {
87
101
 
88
102
  test('Error variant uses assertive alert semantics', () => {
89
103
  render(
90
- <ThemeContextProvider>
104
+ <ThemeContextProvider theme='light'>
91
105
  <Snackbar
92
106
  action='close'
93
107
  variant='error'
@@ -101,9 +115,50 @@ describe('Snackbar', () => {
101
115
  expect(snackbar.textContent).toBe('Test snackbar');
102
116
  });
103
117
 
118
+ test('Success variant uses polite status semantics', () => {
119
+ render(
120
+ <ThemeContextProvider theme='light'>
121
+ <Snackbar
122
+ action='close'
123
+ variant='success'
124
+ >
125
+ Test snackbar
126
+ </Snackbar>
127
+ </ThemeContextProvider>
128
+ );
129
+
130
+ const snackbar = screen.getByRole('status');
131
+ expect(snackbar.textContent).toBe('Test snackbar');
132
+ });
133
+
134
+ test.each([
135
+ ['default', 'rgb(1, 1, 1)'],
136
+ ['error', 'rgb(2, 2, 2)'],
137
+ ['success', 'rgb(3, 3, 3)'],
138
+ ] as const)(
139
+ '%s variant uses its semantic background token',
140
+ (variant, colour) => {
141
+ render(
142
+ <ThemeContextProvider theme={tokenTestTheme}>
143
+ <Snackbar
144
+ action='close'
145
+ variant={variant}
146
+ >
147
+ Test snackbar
148
+ </Snackbar>
149
+ </ThemeContextProvider>
150
+ );
151
+
152
+ expect(
153
+ getComputedStyle(screen.getByTestId('ucl-uikit-snackbar'))
154
+ .backgroundColor
155
+ ).toBe(colour);
156
+ }
157
+ );
158
+
104
159
  test('Close button has a default accessible name', () => {
105
160
  render(
106
- <ThemeContextProvider>
161
+ <ThemeContextProvider theme='light'>
107
162
  <Snackbar action='close'>Test snackbar</Snackbar>
108
163
  </ThemeContextProvider>
109
164
  );
@@ -117,7 +172,7 @@ describe('Snackbar', () => {
117
172
 
118
173
  test('Close button accessible name can be customised', () => {
119
174
  render(
120
- <ThemeContextProvider>
175
+ <ThemeContextProvider theme='light'>
121
176
  <Snackbar
122
177
  action='close'
123
178
  closeButtonAriaLabel='Dismiss saved notification'
@@ -137,7 +192,7 @@ describe('Snackbar', () => {
137
192
  test('Close callback is called', () => {
138
193
  const onClose = vi.fn();
139
194
  render(
140
- <ThemeContextProvider>
195
+ <ThemeContextProvider theme='light'>
141
196
  <Snackbar
142
197
  action='close'
143
198
  onClose={onClose}
@@ -154,7 +209,7 @@ describe('Snackbar', () => {
154
209
  test('Undo callback is called', () => {
155
210
  const onUndo = vi.fn();
156
211
  render(
157
- <ThemeContextProvider>
212
+ <ThemeContextProvider theme='light'>
158
213
  <Snackbar
159
214
  action='undo'
160
215
  onUndo={onUndo}
@@ -168,9 +223,43 @@ describe('Snackbar', () => {
168
223
  expect(onUndo).toHaveBeenCalled();
169
224
  });
170
225
 
226
+ test('Undo action uses sentence case label', () => {
227
+ render(
228
+ <ThemeContextProvider theme='light'>
229
+ <Snackbar action='undo'>Test snackbar</Snackbar>
230
+ </ThemeContextProvider>
231
+ );
232
+
233
+ expect(screen.getByRole('button', { name: 'Undo' })).toBeDefined();
234
+ });
235
+
236
+ test('Default variant renders info icon', () => {
237
+ vi.spyOn(Icon, 'Info').mockImplementation((props) => (
238
+ <svg
239
+ data-testid='info-icon'
240
+ {...props}
241
+ />
242
+ ));
243
+
244
+ render(
245
+ <ThemeContextProvider theme='light'>
246
+ <Snackbar action='close'>Test snackbar</Snackbar>
247
+ </ThemeContextProvider>
248
+ );
249
+
250
+ expect(screen.getByTestId('info-icon')).toBeDefined();
251
+ });
252
+
171
253
  test('Error variant renders warning icon', () => {
254
+ vi.spyOn(Icon, 'AlertTriangle').mockImplementation((props) => (
255
+ <svg
256
+ data-testid='alert-triangle-icon'
257
+ {...props}
258
+ />
259
+ ));
260
+
172
261
  render(
173
- <ThemeContextProvider>
262
+ <ThemeContextProvider theme='light'>
174
263
  <Snackbar
175
264
  action='close'
176
265
  variant='error'
@@ -180,15 +269,34 @@ describe('Snackbar', () => {
180
269
  </ThemeContextProvider>
181
270
  );
182
271
 
183
- const [icon] = screen.getAllByTestId('ucl-uikit-icon');
184
- const warningIconPath = icon.querySelector('path');
272
+ expect(screen.getByTestId('alert-triangle-icon')).toBeDefined();
273
+ });
274
+
275
+ test('Success variant renders check icon', () => {
276
+ vi.spyOn(Icon, 'CheckCircle').mockImplementation((props) => (
277
+ <svg
278
+ data-testid='check-circle-icon'
279
+ {...props}
280
+ />
281
+ ));
282
+
283
+ render(
284
+ <ThemeContextProvider theme='light'>
285
+ <Snackbar
286
+ action='close'
287
+ variant='success'
288
+ >
289
+ Test snackbar
290
+ </Snackbar>
291
+ </ThemeContextProvider>
292
+ );
185
293
 
186
- expect(warningIconPath?.getAttribute('d')).toContain('M10.29 3.86');
294
+ expect(screen.getByTestId('check-circle-icon')).toBeDefined();
187
295
  });
188
296
 
189
297
  test('Decorative icons are hidden from assistive technology', () => {
190
298
  render(
191
- <ThemeContextProvider>
299
+ <ThemeContextProvider theme='light'>
192
300
  <Snackbar action='close'>Test snackbar</Snackbar>
193
301
  </ThemeContextProvider>
194
302
  );
@@ -203,7 +311,7 @@ describe('Snackbar', () => {
203
311
  test("Close callback cannot be called when action is 'undo'", () => {
204
312
  const onClose = vi.fn();
205
313
  render(
206
- <ThemeContextProvider>
314
+ <ThemeContextProvider theme='light'>
207
315
  <Snackbar
208
316
  action='undo'
209
317
  onClose={onClose}
@@ -221,7 +329,7 @@ describe('Snackbar', () => {
221
329
  test("Undo callback cannot be called when action is 'close'", () => {
222
330
  const onUndo = vi.fn();
223
331
  render(
224
- <ThemeContextProvider>
332
+ <ThemeContextProvider theme='light'>
225
333
  <Snackbar
226
334
  action='close'
227
335
  onUndo={onUndo}
@@ -2,7 +2,7 @@
2
2
 
3
3
  exports[`Snackbar > snapshot: custom test id 1`] = `
4
4
  <div
5
- class="ucl-uikit-snackbar css-10q5wpo"
5
+ class="ucl-uikit-snackbar css-4vd8ej"
6
6
  data-testid="custom-test-id"
7
7
  >
8
8
  <svg
@@ -20,11 +20,13 @@ exports[`Snackbar > snapshot: custom test id 1`] = `
20
20
  width="32"
21
21
  xmlns="http://www.w3.org/2000/svg"
22
22
  >
23
- <path
24
- d="M22 11.08V12a10 10 0 1 1-5.93-9.14"
23
+ <circle
24
+ cx="12"
25
+ cy="12"
26
+ r="10"
25
27
  />
26
28
  <path
27
- d="M22 4 12 14.01l-3-3"
29
+ d="M12 16v-4m0-4h.01"
28
30
  />
29
31
  </svg>
30
32
  <span
@@ -38,14 +40,14 @@ exports[`Snackbar > snapshot: custom test id 1`] = `
38
40
  class="css-1xjm0bq"
39
41
  >
40
42
  <button
41
- class="css-bt8z9v"
43
+ class="css-1s4q0nw"
42
44
  data-testid="custom-test-id-undo-button"
43
45
  >
44
46
  <span
45
47
  class="ucl-uikit-text css-1ccjqc4"
46
48
  data-testid="ucl-uikit-text"
47
49
  >
48
- UNDO
50
+ Undo
49
51
  </span>
50
52
  </button>
51
53
  </span>
@@ -54,7 +56,7 @@ exports[`Snackbar > snapshot: custom test id 1`] = `
54
56
 
55
57
  exports[`Snackbar > snapshot: minimal props 1`] = `
56
58
  <div
57
- class="ucl-uikit-snackbar css-10q5wpo"
59
+ class="ucl-uikit-snackbar css-4vd8ej"
58
60
  data-testid="ucl-uikit-snackbar"
59
61
  >
60
62
  <svg
@@ -72,11 +74,13 @@ exports[`Snackbar > snapshot: minimal props 1`] = `
72
74
  width="32"
73
75
  xmlns="http://www.w3.org/2000/svg"
74
76
  >
75
- <path
76
- d="M22 11.08V12a10 10 0 1 1-5.93-9.14"
77
+ <circle
78
+ cx="12"
79
+ cy="12"
80
+ r="10"
77
81
  />
78
82
  <path
79
- d="M22 4 12 14.01l-3-3"
83
+ d="M12 16v-4m0-4h.01"
80
84
  />
81
85
  </svg>
82
86
  <span
@@ -91,7 +95,7 @@ exports[`Snackbar > snapshot: minimal props 1`] = `
91
95
  >
92
96
  <button
93
97
  aria-label="Dismiss notification"
94
- class="ucl-uikit-icon-button css-wreorn"
98
+ class="ucl-uikit-icon-button css-uv5ig"
95
99
  data-testid="ucl-uikit-snackbar-close-button"
96
100
  >
97
101
  <svg
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "uikit-react-public",
3
3
  "private": false,
4
4
  "license": "UNLICENSED",
5
- "version": "0.32.3",
5
+ "version": "0.32.5",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",