@instructure/ui-alerts 10.18.0 → 10.18.1-snapshot-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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [10.18.1-snapshot-1](https://github.com/instructure/instructure-ui/compare/v10.18.0...v10.18.1-snapshot-1) (2025-05-28)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **ui-alerts:** add variantScreenReaderLabel prop to Alert to improve screenreader usability ([814a0ea](https://github.com/instructure/instructure-ui/commit/814a0eaebc0cededff575a8ea0cd05f8ec27b6de))
12
+
13
+
14
+
15
+
16
+
6
17
  # [10.18.0](https://github.com/instructure/instructure-ui/compare/v10.17.0...v10.18.0) (2025-05-26)
7
18
 
8
19
 
package/es/Alert/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
2
- const _excluded = ["margin", "styles", "children", "onDismiss"];
2
+ const _excluded = ["margin", "styles", "children", "onDismiss", "variantScreenReaderLabel"];
3
3
  var _dec, _dec2, _class, _Alert;
4
4
  /*
5
5
  * The MIT License (MIT)
@@ -39,7 +39,7 @@ import { withStyle } from '@instructure/emotion';
39
39
  import generateStyle from './styles';
40
40
  import generateComponentTheme from './theme';
41
41
  import { propTypes, allowedProps } from './props';
42
- import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
42
+ import { jsxs as _jsxs, jsx as _jsx } from "@emotion/react/jsx-runtime";
43
43
  /**
44
44
  ---
45
45
  category: components
@@ -120,8 +120,8 @@ let Alert = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, gene
120
120
  }
121
121
  }
122
122
  createScreenreaderContentNode() {
123
- return _jsx(ScreenReaderContent, {
124
- children: this.props.children
123
+ return _jsxs(ScreenReaderContent, {
124
+ children: [this.props.variantScreenReaderLabel || '', " ", this.props.children]
125
125
  });
126
126
  }
127
127
  createScreenreaderAlert() {
@@ -197,6 +197,7 @@ let Alert = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, gene
197
197
  styles = _this$props3.styles,
198
198
  children = _this$props3.children,
199
199
  onDismiss = _this$props3.onDismiss,
200
+ variantScreenReaderLabel = _this$props3.variantScreenReaderLabel,
200
201
  props = _objectWithoutProperties(_this$props3, _excluded);
201
202
  return _jsxs(View, {
202
203
  ...passthroughProps({
@@ -207,9 +208,12 @@ let Alert = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, gene
207
208
  css: styles === null || styles === void 0 ? void 0 : styles.alert,
208
209
  onKeyUp: this.handleKeyUp,
209
210
  elementRef: this.handleRef,
210
- children: [this.renderIcon(), _jsx("div", {
211
+ children: [this.renderIcon(), _jsxs("div", {
211
212
  css: styles === null || styles === void 0 ? void 0 : styles.content,
212
- children: children
213
+ children: [variantScreenReaderLabel && _jsx("span", {
214
+ css: styles === null || styles === void 0 ? void 0 : styles.variantScreenReaderLabel,
215
+ children: variantScreenReaderLabel
216
+ }), children]
213
217
  }), this.renderCloseButton()]
214
218
  });
215
219
  }
package/es/Alert/props.js CHANGED
@@ -36,7 +36,8 @@ const propTypes = {
36
36
  onDismiss: PropTypes.func,
37
37
  transition: PropTypes.oneOf(['none', 'fade']),
38
38
  open: PropTypes.bool,
39
- hasShadow: PropTypes.bool
39
+ hasShadow: PropTypes.bool,
40
+ variantScreenReaderLabel: PropTypes.string
40
41
  };
41
42
  const allowedProps = ['children', 'variant', 'margin', 'liveRegion', 'liveRegionPoliteness', 'isLiveRegionAtomic', 'screenReaderOnly', 'timeout', 'renderCloseButtonLabel', 'onDismiss', 'transition', 'open', 'hasShadow'];
42
43
  export { propTypes, allowedProps };
@@ -117,6 +117,13 @@ const generateStyle = (componentTheme, props) => {
117
117
  fontWeight: componentTheme.contentFontWeight,
118
118
  lineHeight: componentTheme.contentLineHeight,
119
119
  padding: componentTheme.contentPadding
120
+ },
121
+ variantScreenReaderLabel: {
122
+ position: 'absolute',
123
+ height: '1px',
124
+ width: '1px',
125
+ overflow: 'hidden',
126
+ margin: '-1px'
120
127
  }
121
128
  };
122
129
  };
@@ -26,7 +26,7 @@ var _styles = _interopRequireDefault(require("./styles"));
26
26
  var _theme = _interopRequireDefault(require("./theme"));
27
27
  var _props = require("./props");
28
28
  var _jsxRuntime = require("@emotion/react/jsx-runtime");
29
- const _excluded = ["margin", "styles", "children", "onDismiss"];
29
+ const _excluded = ["margin", "styles", "children", "onDismiss", "variantScreenReaderLabel"];
30
30
  var _dec, _dec2, _class, _Alert;
31
31
  /*
32
32
  * The MIT License (MIT)
@@ -131,8 +131,8 @@ let Alert = exports.Alert = (_dec = (0, _withDeterministicId.withDeterministicId
131
131
  }
132
132
  }
133
133
  createScreenreaderContentNode() {
134
- return (0, _jsxRuntime.jsx)(_ScreenReaderContent.ScreenReaderContent, {
135
- children: this.props.children
134
+ return (0, _jsxRuntime.jsxs)(_ScreenReaderContent.ScreenReaderContent, {
135
+ children: [this.props.variantScreenReaderLabel || '', " ", this.props.children]
136
136
  });
137
137
  }
138
138
  createScreenreaderAlert() {
@@ -208,6 +208,7 @@ let Alert = exports.Alert = (_dec = (0, _withDeterministicId.withDeterministicId
208
208
  styles = _this$props3.styles,
209
209
  children = _this$props3.children,
210
210
  onDismiss = _this$props3.onDismiss,
211
+ variantScreenReaderLabel = _this$props3.variantScreenReaderLabel,
211
212
  props = (0, _objectWithoutProperties2.default)(_this$props3, _excluded);
212
213
  return (0, _jsxRuntime.jsxs)(_View.View, {
213
214
  ...(0, _passthroughProps.passthroughProps)({
@@ -218,9 +219,12 @@ let Alert = exports.Alert = (_dec = (0, _withDeterministicId.withDeterministicId
218
219
  css: styles === null || styles === void 0 ? void 0 : styles.alert,
219
220
  onKeyUp: this.handleKeyUp,
220
221
  elementRef: this.handleRef,
221
- children: [this.renderIcon(), (0, _jsxRuntime.jsx)("div", {
222
+ children: [this.renderIcon(), (0, _jsxRuntime.jsxs)("div", {
222
223
  css: styles === null || styles === void 0 ? void 0 : styles.content,
223
- children: children
224
+ children: [variantScreenReaderLabel && (0, _jsxRuntime.jsx)("span", {
225
+ css: styles === null || styles === void 0 ? void 0 : styles.variantScreenReaderLabel,
226
+ children: variantScreenReaderLabel
227
+ }), children]
224
228
  }), this.renderCloseButton()]
225
229
  });
226
230
  }
@@ -43,6 +43,7 @@ const propTypes = exports.propTypes = {
43
43
  onDismiss: _propTypes.default.func,
44
44
  transition: _propTypes.default.oneOf(['none', 'fade']),
45
45
  open: _propTypes.default.bool,
46
- hasShadow: _propTypes.default.bool
46
+ hasShadow: _propTypes.default.bool,
47
+ variantScreenReaderLabel: _propTypes.default.string
47
48
  };
48
49
  const allowedProps = exports.allowedProps = ['children', 'variant', 'margin', 'liveRegion', 'liveRegionPoliteness', 'isLiveRegionAtomic', 'screenReaderOnly', 'timeout', 'renderCloseButtonLabel', 'onDismiss', 'transition', 'open', 'hasShadow'];
@@ -123,6 +123,13 @@ const generateStyle = (componentTheme, props) => {
123
123
  fontWeight: componentTheme.contentFontWeight,
124
124
  lineHeight: componentTheme.contentLineHeight,
125
125
  padding: componentTheme.contentPadding
126
+ },
127
+ variantScreenReaderLabel: {
128
+ position: 'absolute',
129
+ height: '1px',
130
+ width: '1px',
131
+ overflow: 'hidden',
132
+ margin: '-1px'
126
133
  }
127
134
  };
128
135
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-alerts",
3
- "version": "10.18.0",
3
+ "version": "10.18.1-snapshot-1",
4
4
  "description": "An alert component",
5
5
  "author": "Instructure, Inc. Engineering and Product Design",
6
6
  "module": "./es/index.js",
@@ -23,11 +23,11 @@
23
23
  },
24
24
  "license": "MIT",
25
25
  "devDependencies": {
26
- "@instructure/ui-axe-check": "10.18.0",
27
- "@instructure/ui-babel-preset": "10.18.0",
28
- "@instructure/ui-color-utils": "10.18.0",
29
- "@instructure/ui-scripts": "10.18.0",
30
- "@instructure/ui-test-utils": "10.18.0",
26
+ "@instructure/ui-axe-check": "10.18.1-snapshot-1",
27
+ "@instructure/ui-babel-preset": "10.18.1-snapshot-1",
28
+ "@instructure/ui-color-utils": "10.18.1-snapshot-1",
29
+ "@instructure/ui-scripts": "10.18.1-snapshot-1",
30
+ "@instructure/ui-test-utils": "10.18.1-snapshot-1",
31
31
  "@testing-library/jest-dom": "^6.6.3",
32
32
  "@testing-library/react": "^16.0.1",
33
33
  "@testing-library/user-event": "^14.5.2",
@@ -35,16 +35,16 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@babel/runtime": "^7.26.0",
38
- "@instructure/console": "10.18.0",
39
- "@instructure/emotion": "10.18.0",
40
- "@instructure/shared-types": "10.18.0",
41
- "@instructure/ui-a11y-content": "10.18.0",
42
- "@instructure/ui-buttons": "10.18.0",
43
- "@instructure/ui-icons": "10.18.0",
44
- "@instructure/ui-motion": "10.18.0",
45
- "@instructure/ui-react-utils": "10.18.0",
46
- "@instructure/ui-themes": "10.18.0",
47
- "@instructure/ui-view": "10.18.0",
38
+ "@instructure/console": "10.18.1-snapshot-1",
39
+ "@instructure/emotion": "10.18.1-snapshot-1",
40
+ "@instructure/shared-types": "10.18.1-snapshot-1",
41
+ "@instructure/ui-a11y-content": "10.18.1-snapshot-1",
42
+ "@instructure/ui-buttons": "10.18.1-snapshot-1",
43
+ "@instructure/ui-icons": "10.18.1-snapshot-1",
44
+ "@instructure/ui-motion": "10.18.1-snapshot-1",
45
+ "@instructure/ui-react-utils": "10.18.1-snapshot-1",
46
+ "@instructure/ui-themes": "10.18.1-snapshot-1",
47
+ "@instructure/ui-view": "10.18.1-snapshot-1",
48
48
  "keycode": "^2",
49
49
  "prop-types": "^15.8.1"
50
50
  },
@@ -20,6 +20,7 @@ type: example
20
20
  renderCloseButtonLabel="Close"
21
21
  margin="small"
22
22
  transition="none"
23
+ variantScreenReaderLabel="Success, "
23
24
  >
24
25
  Sample success alert text. I will close w/o a transition out if you close me
25
26
  </Alert>
@@ -27,6 +28,7 @@ type: example
27
28
  variant="info"
28
29
  renderCloseButtonLabel="Close"
29
30
  margin="small"
31
+ variantScreenReaderLabel="Information, "
30
32
  >
31
33
  Sample info text. I will fade out if you close me.
32
34
  </Alert>
@@ -34,6 +36,7 @@ type: example
34
36
  variant="error"
35
37
  renderCloseButtonLabel="Close"
36
38
  margin="small"
39
+ variantScreenReaderLabel="Error, "
37
40
  >
38
41
  Sample error text that continues for a while
39
42
  to demonstrate what happens when the content stretches over
@@ -43,6 +46,7 @@ type: example
43
46
  <Alert
44
47
  variant="warning"
45
48
  margin="small"
49
+ variantScreenReaderLabel="Warning, "
46
50
  >
47
51
  Sample warning text. This alert is not dismissible and cannot be closed.
48
52
  </Alert>
@@ -59,6 +63,7 @@ type: example
59
63
  variant="info"
60
64
  margin="small"
61
65
  timeout={5000}
66
+ variantScreenReaderLabel="Information, "
62
67
  >
63
68
  Sample info text. I will fade out after 5 seconds
64
69
  </Alert>
@@ -111,6 +116,16 @@ For more information about live regions, see
111
116
  this.setState({ alerts })
112
117
  }
113
118
 
119
+ getScreenReaderLabel(variant) {
120
+ const labels = {
121
+ info: 'Information, ',
122
+ success: 'Success, ',
123
+ warning: 'Warning, ',
124
+ error: 'Error, '
125
+ }
126
+ return labels[variant] || ''
127
+ }
128
+
114
129
  render() {
115
130
  return (
116
131
  <div>
@@ -125,6 +140,9 @@ For more information about live regions, see
125
140
  liveRegion={() => document.getElementById('flash-messages')}
126
141
  liveRegionPoliteness={alert.politeness}
127
142
  margin="small 0"
143
+ variantScreenReaderLabel={this.getScreenReaderLabel(
144
+ alert.variant
145
+ )}
128
146
  >
129
147
  This is {alert.politeness === 'polite' ? 'a' : 'an'}{' '}
130
148
  {alert.politeness} {alert.variant} alert
@@ -325,6 +343,7 @@ type: embed
325
343
  <Figure.Item>Use the Error alert to notify user of an error</Figure.Item>
326
344
  <Figure.Item>Use the Warning alert to notify user of a warning</Figure.Item>
327
345
  <Figure.Item>Use the Success alert to notify user of a success event or action</Figure.Item>
346
+ <Figure.Item>Use the `variantScreenReaderLabel` prop to indicate the alert variant to screen reader users</Figure.Item>
328
347
  </Figure>
329
348
  <Figure recommendation="no" title="Don't">
330
349
  <Figure.Item>Have alert messaging that is more than two lines long</Figure.Item>
@@ -169,7 +169,11 @@ class Alert extends Component<AlertProps, AlertState> {
169
169
  }
170
170
 
171
171
  createScreenreaderContentNode() {
172
- return <ScreenReaderContent>{this.props.children}</ScreenReaderContent>
172
+ return (
173
+ <ScreenReaderContent>
174
+ {this.props.variantScreenReaderLabel || ''} {this.props.children}
175
+ </ScreenReaderContent>
176
+ )
173
177
  }
174
178
 
175
179
  createScreenreaderAlert() {
@@ -260,7 +264,14 @@ class Alert extends Component<AlertProps, AlertState> {
260
264
  renderAlert() {
261
265
  // prevent onDismiss from being passed to the View component
262
266
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
263
- const { margin, styles, children, onDismiss, ...props } = this.props
267
+ const {
268
+ margin,
269
+ styles,
270
+ children,
271
+ onDismiss,
272
+ variantScreenReaderLabel,
273
+ ...props
274
+ } = this.props
264
275
  return (
265
276
  <View
266
277
  {...passthroughProps({ ...props })}
@@ -271,7 +282,14 @@ class Alert extends Component<AlertProps, AlertState> {
271
282
  elementRef={this.handleRef}
272
283
  >
273
284
  {this.renderIcon()}
274
- <div css={styles?.content}>{children}</div>
285
+ <div css={styles?.content}>
286
+ {variantScreenReaderLabel && (
287
+ <span css={styles?.variantScreenReaderLabel}>
288
+ {variantScreenReaderLabel}
289
+ </span>
290
+ )}
291
+ {children}
292
+ </div>
275
293
  {this.renderCloseButton()}
276
294
  </View>
277
295
  )
@@ -46,6 +46,10 @@ type AlertOwnProps = {
46
46
  * Determines color and icon
47
47
  */
48
48
  variant?: 'info' | 'success' | 'warning' | 'error'
49
+ /**
50
+ * How the screen reader should announce the alert variant. While the `variant` prop sets the color and icon for the alert component, this label should be a textual representation of that information. So e.g. if the variant is `info`, this label could be "Information," or "Information alert,". Note the `,` at the end of the label which helps the screenreader to be more natural sounding.
51
+ */
52
+ variantScreenReaderLabel?: string
49
53
  /**
50
54
  * Function that returns the DIV where screenreader alerts will be placed.
51
55
  */
@@ -115,7 +119,9 @@ type AlertProps = AlertOwnProps &
115
119
  WithStyleProps<AlertTheme, AlertStyle> &
116
120
  WithDeterministicIdProps
117
121
 
118
- type AlertStyle = ComponentStyle<'alert' | 'icon' | 'closeButton' | 'content'>
122
+ type AlertStyle = ComponentStyle<
123
+ 'alert' | 'icon' | 'closeButton' | 'content' | 'variantScreenReaderLabel'
124
+ >
119
125
 
120
126
  const propTypes: PropValidators<PropKeys> = {
121
127
  children: PropTypes.node,
@@ -130,7 +136,8 @@ const propTypes: PropValidators<PropKeys> = {
130
136
  onDismiss: PropTypes.func,
131
137
  transition: PropTypes.oneOf(['none', 'fade']),
132
138
  open: PropTypes.bool,
133
- hasShadow: PropTypes.bool
139
+ hasShadow: PropTypes.bool,
140
+ variantScreenReaderLabel: PropTypes.string
134
141
  }
135
142
 
136
143
  const allowedProps: AllowedPropKeys = [
@@ -114,6 +114,13 @@ const generateStyle = (
114
114
  fontWeight: componentTheme.contentFontWeight,
115
115
  lineHeight: componentTheme.contentLineHeight,
116
116
  padding: componentTheme.contentPadding
117
+ },
118
+ variantScreenReaderLabel: {
119
+ position: 'absolute',
120
+ height: '1px',
121
+ width: '1px',
122
+ overflow: 'hidden',
123
+ margin: '-1px'
117
124
  }
118
125
  }
119
126
  }