uikit-react-public 0.50.3 → 0.50.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.
@@ -20,6 +20,10 @@ The `<Alert>` component is used to convey contextual messages with visual hierar
20
20
 
21
21
  Use the component with `Alert.Title` and `Alert.Message` as children to maintain semantic separation of content.
22
22
 
23
+ Use the `variant` prop to select `info`, `success`, `warning`, or `error`.
24
+ The legacy `type` prop remains available for backwards compatibility but is
25
+ deprecated.
26
+
23
27
  ## Action button
24
28
 
25
29
  Pass a `Button` to `actionButton` to add an action. Alert sets the button to the extra-small size and applies the colour associated with the alert type.
@@ -29,10 +29,16 @@ const meta = {
29
29
  layout: 'centered',
30
30
  },
31
31
  argTypes: {
32
+ variant: {
33
+ options: ['info', 'success', 'warning', 'error'],
34
+ control: { type: 'select' },
35
+ type: 'string',
36
+ },
32
37
  type: {
33
38
  options: ['info', 'success', 'warning', 'error'],
34
39
  control: { type: 'select' },
35
40
  type: 'string',
41
+ description: 'Deprecated. Use variant instead.',
36
42
  },
37
43
  className: { control: { type: 'text' } },
38
44
  testId: { control: { type: 'text' } },
@@ -60,7 +66,7 @@ const meta = {
60
66
  },
61
67
  },
62
68
  args: {
63
- type: 'info',
69
+ variant: 'info',
64
70
  testId: 'ucl-uikit-alert',
65
71
  },
66
72
  tags: ['autodocs'],
@@ -71,36 +77,36 @@ type Story = StoryObj<typeof meta>;
71
77
 
72
78
  const Template = (args: any) => (
73
79
  <Alert {...args}>
74
- <Alert.Title>Title of {args.type} alert</Alert.Title>
75
- <Alert.Message>This is a {args.type} alert message.</Alert.Message>
80
+ <Alert.Title>Title of {args.variant} alert</Alert.Title>
81
+ <Alert.Message>This is a {args.variant} alert message.</Alert.Message>
76
82
  </Alert>
77
83
  );
78
84
 
79
85
  export const Info: Story = {
80
86
  render: Template,
81
87
  args: {
82
- type: 'info',
88
+ variant: 'info',
83
89
  },
84
90
  };
85
91
 
86
92
  export const Success: Story = {
87
93
  render: Template,
88
94
  args: {
89
- type: 'success',
95
+ variant: 'success',
90
96
  },
91
97
  };
92
98
 
93
99
  export const Warning: Story = {
94
100
  render: Template,
95
101
  args: {
96
- type: 'warning',
102
+ variant: 'warning',
97
103
  },
98
104
  };
99
105
 
100
106
  export const Error: Story = {
101
107
  render: Template,
102
108
  args: {
103
- type: 'error',
109
+ variant: 'error',
104
110
  },
105
111
  };
106
112
 
@@ -111,19 +117,19 @@ export const WithActionButton: Story = {
111
117
  {...args}
112
118
  actionButton={
113
119
  <Button
114
- leftIcon={<Icon.ChevronRight />}
115
- rightIcon={<Icon.ChevronRight />}
120
+ leftIcon={<Icon.ArrowRight />}
121
+ rightIcon={<Icon.ArrowRight />}
116
122
  >
117
123
  Action
118
124
  </Button>
119
125
  }
120
126
  >
121
- <Alert.Title>Title of {args.type} alert</Alert.Title>
122
- <Alert.Message>This is a {args.type} alert message.</Alert.Message>
127
+ <Alert.Title>Title of {args.variant} alert</Alert.Title>
128
+ <Alert.Message>This is a {args.variant} alert message.</Alert.Message>
123
129
  </Alert>
124
130
  ),
125
131
  args: {
126
- type: 'info',
132
+ variant: 'info',
127
133
  },
128
134
  };
129
135
 
@@ -131,7 +137,7 @@ export const CommonMargins: Story = {
131
137
  render: Template,
132
138
  name: 'With common margins',
133
139
  args: {
134
- type: 'info',
140
+ variant: 'info',
135
141
  m: '24',
136
142
  },
137
143
  parameters: {
@@ -16,10 +16,13 @@ import capitalise from '../../utils/capitalise';
16
16
 
17
17
  export const NAME = 'ucl-uikit-alert';
18
18
 
19
- export type AlertType = 'info' | 'success' | 'warning' | 'error';
19
+ export type AlertVariant = 'info' | 'success' | 'warning' | 'error';
20
+
21
+ /** @deprecated Use AlertVariant instead. */
22
+ export type AlertType = AlertVariant;
20
23
 
21
24
  export interface AlertContextProps {
22
- type: AlertType;
25
+ type: AlertVariant;
23
26
  }
24
27
 
25
28
  export const AlertContext = createContext<AlertContextProps | null>(null);
@@ -33,17 +36,30 @@ export function useAlertContext() {
33
36
  }
34
37
 
35
38
  export interface AlertBaseProps extends HTMLAttributes<HTMLDivElement> {
36
- type: AlertType;
39
+ variant?: AlertVariant;
40
+ /** @deprecated Use variant instead. */
41
+ type?: AlertVariant;
37
42
  iconProps?: Omit<IconProps, 'svg'>;
38
43
  contentProps?: HTMLAttributes<HTMLDivElement>;
39
44
  actionButton?: ReactElement<ButtonProps>;
40
45
  testId?: string;
41
46
  }
42
47
 
43
- export type AlertProps = AlertBaseProps & MarginProps;
48
+ export type AlertProps = AlertBaseProps &
49
+ MarginProps &
50
+ (
51
+ | {
52
+ variant: AlertVariant;
53
+ }
54
+ | {
55
+ variant?: undefined;
56
+ type: AlertVariant;
57
+ }
58
+ );
44
59
 
45
60
  const Alert = ({
46
- type,
61
+ variant,
62
+ type: deprecatedType,
47
63
  iconProps,
48
64
  contentProps,
49
65
  actionButton,
@@ -52,7 +68,13 @@ const Alert = ({
52
68
  className,
53
69
  ...props
54
70
  }: AlertProps) => {
55
- const typeName = capitalise(type);
71
+ const resolvedVariant = variant ?? deprecatedType;
72
+
73
+ if (!resolvedVariant) {
74
+ throw new Error('Alert requires a variant');
75
+ }
76
+
77
+ const variantName = capitalise(resolvedVariant);
56
78
 
57
79
  const [theme] = useTheme();
58
80
 
@@ -91,27 +113,27 @@ const Alert = ({
91
113
  NAME,
92
114
  baseStyles,
93
115
  marginsStyle(props, theme),
94
- type === 'info' && infoStyle,
95
- type === 'success' && successStyle,
96
- type === 'warning' && warningStyle,
97
- type === 'error' && errorStyle,
116
+ resolvedVariant === 'info' && infoStyle,
117
+ resolvedVariant === 'success' && successStyle,
118
+ resolvedVariant === 'warning' && warningStyle,
119
+ resolvedVariant === 'error' && errorStyle,
98
120
  className
99
121
  );
100
122
 
101
123
  const iconStyle = cx(
102
- type === 'info' &&
124
+ resolvedVariant === 'info' &&
103
125
  css`
104
126
  color: ${theme.colour.icon.brand};
105
127
  `,
106
- type === 'success' &&
128
+ resolvedVariant === 'success' &&
107
129
  css`
108
130
  color: ${theme.colour.icon.success};
109
131
  `,
110
- type === 'warning' &&
132
+ resolvedVariant === 'warning' &&
111
133
  css`
112
134
  color: ${theme.colour.icon.warning};
113
135
  `,
114
- type === 'error' &&
136
+ resolvedVariant === 'error' &&
115
137
  css`
116
138
  color: ${theme.colour.icon.critical};
117
139
  `
@@ -141,9 +163,9 @@ const Alert = ({
141
163
  success: Icon.CheckCircle,
142
164
  warning: Icon.AlertTriangle,
143
165
  error: Icon.XCircle,
144
- }[type];
166
+ }[resolvedVariant];
145
167
 
146
- const actionButtonVariant: Record<AlertType, ButtonVariant> = {
168
+ const actionButtonVariant: Record<AlertVariant, ButtonVariant> = {
147
169
  info: 'accent',
148
170
  success: 'primary-success',
149
171
  warning: 'primary-warning',
@@ -152,7 +174,7 @@ const Alert = ({
152
174
 
153
175
  const renderedActionButton = actionButton
154
176
  ? cloneElement(actionButton, {
155
- variant: actionButtonVariant[type],
177
+ variant: actionButtonVariant[resolvedVariant],
156
178
  size: 'xsmall',
157
179
  className: cx(actionButtonStyle, actionButton.props.className),
158
180
  })
@@ -162,7 +184,7 @@ const Alert = ({
162
184
  <>
163
185
  <IconComp
164
186
  className={iconStyle}
165
- aria-label={typeName}
187
+ aria-label={variantName}
166
188
  {...iconProps}
167
189
  />
168
190
  <div
@@ -175,7 +197,7 @@ const Alert = ({
175
197
  );
176
198
 
177
199
  return (
178
- <AlertContext.Provider value={{ type }}>
200
+ <AlertContext.Provider value={{ type: resolvedVariant }}>
179
201
  <div
180
202
  className={style}
181
203
  data-testid={testId}
@@ -18,10 +18,10 @@ const hexToRgb = (hex: string) => {
18
18
  describe('Alert', () => {
19
19
  // Snapshot tests
20
20
 
21
- test('snapshot: type=info, no title', () => {
21
+ test('snapshot: variant=info, no title', () => {
22
22
  const renderResult = render(
23
23
  <ThemeContextProvider>
24
- <Alert type='info'>
24
+ <Alert variant='info'>
25
25
  <Alert.Message>This is an informational alert.</Alert.Message>
26
26
  </Alert>
27
27
  </ThemeContextProvider>
@@ -29,10 +29,10 @@ describe('Alert', () => {
29
29
  expect(renderResult.container.firstChild).toMatchSnapshot();
30
30
  });
31
31
 
32
- test('snapshot: type=success', () => {
32
+ test('snapshot: variant=success', () => {
33
33
  const renderResult = render(
34
34
  <ThemeContextProvider>
35
- <Alert type='success'>
35
+ <Alert variant='success'>
36
36
  <Alert.Title>Success</Alert.Title>
37
37
  <Alert.Message>Your operation completed successfully.</Alert.Message>
38
38
  </Alert>
@@ -41,10 +41,10 @@ describe('Alert', () => {
41
41
  expect(renderResult.container.firstChild).toMatchSnapshot();
42
42
  });
43
43
 
44
- test('snapshot: type=warning', () => {
44
+ test('snapshot: variant=warning', () => {
45
45
  const renderResult = render(
46
46
  <ThemeContextProvider>
47
- <Alert type='warning'>
47
+ <Alert variant='warning'>
48
48
  <Alert.Title>Warning</Alert.Title>
49
49
  <Alert.Message>
50
50
  This operation may have unintended consequences.
@@ -55,10 +55,10 @@ describe('Alert', () => {
55
55
  expect(renderResult.container.firstChild).toMatchSnapshot();
56
56
  });
57
57
 
58
- test('snapshot: type=error', () => {
58
+ test('snapshot: variant=error', () => {
59
59
  const renderResult = render(
60
60
  <ThemeContextProvider>
61
- <Alert type='error'>
61
+ <Alert variant='error'>
62
62
  <Alert.Title>Error Alert</Alert.Title>
63
63
  <Alert.Message>The operation failed.</Alert.Message>
64
64
  </Alert>
@@ -74,11 +74,11 @@ describe('Alert', () => {
74
74
  ['error', theme.colour.fill.critical],
75
75
  ] as const)(
76
76
  'uses the semantic %s colour for its action button',
77
- (type, expectedColour) => {
77
+ (variant, expectedColour) => {
78
78
  render(
79
79
  <ThemeContextProvider>
80
80
  <Alert
81
- type={type}
81
+ variant={variant}
82
82
  actionButton={<Button>Action</Button>}
83
83
  >
84
84
  <Alert.Message>Alert message</Alert.Message>
@@ -101,11 +101,11 @@ describe('Alert', () => {
101
101
  render(
102
102
  <ThemeContextProvider>
103
103
  <Alert
104
- type='info'
104
+ variant='info'
105
105
  actionButton={
106
106
  <Button
107
- leftIcon={<Icon.ChevronRight />}
108
- rightIcon={<Icon.ChevronRight />}
107
+ leftIcon={<Icon.ArrowRight />}
108
+ rightIcon={<Icon.ArrowRight />}
109
109
  onClick={onClick}
110
110
  >
111
111
  Action
@@ -127,7 +127,7 @@ describe('Alert', () => {
127
127
  render(
128
128
  <ThemeContextProvider>
129
129
  <Alert
130
- type='info'
130
+ variant='info'
131
131
  actionButton={<Button>Action</Button>}
132
132
  >
133
133
  <Alert.Message>Alert message</Alert.Message>
@@ -138,4 +138,32 @@ describe('Alert', () => {
138
138
  const alert = screen.getByRole('alert');
139
139
  expect(getComputedStyle(alert).gap).toBe('32px');
140
140
  });
141
+
142
+ test('supports the deprecated type prop', () => {
143
+ render(
144
+ <ThemeContextProvider>
145
+ <Alert type='error'>
146
+ <Alert.Message>Legacy alert</Alert.Message>
147
+ </Alert>
148
+ </ThemeContextProvider>
149
+ );
150
+
151
+ expect(screen.getByLabelText('Error')).toBeDefined();
152
+ });
153
+
154
+ test('prefers variant when variant and deprecated type are both provided', () => {
155
+ render(
156
+ <ThemeContextProvider>
157
+ <Alert
158
+ variant='success'
159
+ type='error'
160
+ >
161
+ <Alert.Message>Alert message</Alert.Message>
162
+ </Alert>
163
+ </ThemeContextProvider>
164
+ );
165
+
166
+ expect(screen.getByLabelText('Success')).toBeDefined();
167
+ expect(screen.queryByLabelText('Error')).toBeNull();
168
+ });
141
169
  });
@@ -1,6 +1,6 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
- exports[`Alert > snapshot: type=error 1`] = `
3
+ exports[`Alert > snapshot: variant=error 1`] = `
4
4
  <div
5
5
  class="ucl-uikit-alert css-1eemwvm"
6
6
  data-testid="ucl-uikit-alert"
@@ -54,7 +54,7 @@ exports[`Alert > snapshot: type=error 1`] = `
54
54
  </div>
55
55
  `;
56
56
 
57
- exports[`Alert > snapshot: type=info, no title 1`] = `
57
+ exports[`Alert > snapshot: variant=info, no title 1`] = `
58
58
  <div
59
59
  class="ucl-uikit-alert css-1d2q0wf"
60
60
  data-testid="ucl-uikit-alert"
@@ -97,7 +97,7 @@ exports[`Alert > snapshot: type=info, no title 1`] = `
97
97
  </div>
98
98
  `;
99
99
 
100
- exports[`Alert > snapshot: type=success 1`] = `
100
+ exports[`Alert > snapshot: variant=success 1`] = `
101
101
  <div
102
102
  class="ucl-uikit-alert css-1p0xqd2"
103
103
  data-testid="ucl-uikit-alert"
@@ -149,7 +149,7 @@ exports[`Alert > snapshot: type=success 1`] = `
149
149
  </div>
150
150
  `;
151
151
 
152
- exports[`Alert > snapshot: type=warning 1`] = `
152
+ exports[`Alert > snapshot: variant=warning 1`] = `
153
153
  <div
154
154
  class="ucl-uikit-alert css-1ks795a"
155
155
  data-testid="ucl-uikit-alert"
@@ -1,6 +1,6 @@
1
1
  export { default } from './Alert';
2
2
  export { default as AlertTitle } from './AlertTitle';
3
3
  export { default as AlertMessage } from './AlertMessage';
4
- export type { AlertProps } from './Alert';
4
+ export type { AlertProps, AlertVariant, AlertType } from './Alert';
5
5
  export type { AlertTitleProps } from './AlertTitle';
6
6
  export type { AlertMessageProps } from './AlertMessage';
@@ -17,7 +17,7 @@ export const usage = {
17
17
  destructiveNoPadding: `<Button variant='tertiary-destructive-no-padding'>Destructive Action</Button>`,
18
18
  onDark: `<Button variant='secondary' onLight={false}>Secondary Button</Button>`,
19
19
  withIcon: `<Button icon={<Icon.AlertTriangle />}>Button</Button>`,
20
- withTwoIcons: `<Button leftIcon={<Icon.ChevronRight />} rightIcon={<Icon.ChevronRight />}>Action</Button>`,
20
+ withTwoIcons: `<Button leftIcon={<Icon.ArrowRight />} rightIcon={<Icon.ArrowRight />}>Action</Button>`,
21
21
  loading: `<Button loading>Loading Button</Button>`,
22
22
  link: `<Button as='a' href='https://www.ucl.ac.uk' target='_blank'>Go to UCL&#39;s website</Button>`,
23
23
  fullWidth: `<Button fullWidth>Full Width Button</Button>`,
@@ -365,8 +365,8 @@ export const WithTwoIcons: Story = {
365
365
  name: 'With two icons',
366
366
  args: {
367
367
  children: 'Action',
368
- leftIcon: <Icon.ChevronRight />,
369
- rightIcon: <Icon.ChevronRight />,
368
+ leftIcon: <Icon.ArrowRight />,
369
+ rightIcon: <Icon.ArrowRight />,
370
370
  },
371
371
  };
372
372
 
@@ -83,8 +83,8 @@ describe('Button', () => {
83
83
  render(
84
84
  <ThemeContextProvider>
85
85
  <Button
86
- leftIcon={<Icon.ChevronRight />}
87
- rightIcon={<Icon.ChevronRight />}
86
+ leftIcon={<Icon.ArrowRight />}
87
+ rightIcon={<Icon.ArrowRight />}
88
88
  size='xsmall'
89
89
  >
90
90
  Action
@@ -191,7 +191,7 @@ export { default as Table } from './Table';
191
191
  export type { TableProps } from './Table';
192
192
 
193
193
  export { default as Alert } from './Alert';
194
- export type { AlertProps } from './Alert';
194
+ export type { AlertProps, AlertVariant, AlertType } from './Alert';
195
195
 
196
196
  export { default as Tabs } from './Tabs';
197
197
  export type {
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.50.3",
5
+ "version": "0.50.5",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",