diff --git a/packages/terra-alert/CHANGELOG.md b/packages/terra-alert/CHANGELOG.md
index 77ae978e5d4..e50b0eeb390 100644
--- a/packages/terra-alert/CHANGELOG.md
+++ b/packages/terra-alert/CHANGELOG.md
@@ -2,6 +2,9 @@
## Unreleased
+* Added
+ * Added visually hidden default title for screen readers to read with custom titles.
+
## 4.80.0 - (September 21, 2023)
* Changed
diff --git a/packages/terra-alert/package.json b/packages/terra-alert/package.json
index 19f16a5c26f..17fa625bd7d 100644
--- a/packages/terra-alert/package.json
+++ b/packages/terra-alert/package.json
@@ -33,6 +33,7 @@
"terra-icon": "^3.57.1",
"terra-responsive-element": "^5.37.0",
"terra-theme-context": "^1.0.0",
+ "terra-visually-hidden-text": "^2.36.0",
"uuid": "3.4.0"
},
"scripts": {
diff --git a/packages/terra-alert/src/Alert.jsx b/packages/terra-alert/src/Alert.jsx
index 31575fe88af..96555886811 100644
--- a/packages/terra-alert/src/Alert.jsx
+++ b/packages/terra-alert/src/Alert.jsx
@@ -7,6 +7,7 @@ import { v4 as uuidv4 } from 'uuid';
import ResponsiveElement from 'terra-responsive-element';
import Button from 'terra-button';
+import VisuallyHiddenText from 'terra-visually-hidden-text';
import {
IconAlert,
IconError,
@@ -202,6 +203,7 @@ const Alert = ({
{(title || defaultTitle) && (
+ {title && defaultTitle && }
{title || defaultTitle}
)}
diff --git a/packages/terra-alert/tests/jest/Alert.test.jsx b/packages/terra-alert/tests/jest/Alert.test.jsx
index 76ff91b76c5..2ae2efbaf3c 100644
--- a/packages/terra-alert/tests/jest/Alert.test.jsx
+++ b/packages/terra-alert/tests/jest/Alert.test.jsx
@@ -10,6 +10,7 @@ import IconInformation from 'terra-icon/lib/icon/IconInformation';
import IconSuccess from 'terra-icon/lib/icon/IconSuccess';
import IconWarning from 'terra-icon/lib/icon/IconWarning';
import Button from 'terra-button';
+import VisuallyHiddenText from 'terra-visually-hidden-text';
import { v4 as uuidv4 } from 'uuid';
import Alert from '../../src/Alert';
@@ -66,6 +67,20 @@ describe('Alert with props', () => {
expect(alert.prop('disableAlertActionFocus')).toEqual(true);
expect(wrapper).toMatchSnapshot();
});
+
+ it('should render an alert with provided title', () => {
+ const wrapper = shallowWithIntl(
).dive();
+ const alertTitle = wrapper.find('.title');
+
+ expect(alertTitle.prop('children')).toEqual(
+ [
+
,
+ 'Custom Title',
+ ],
+ );
+ expect(alertTitle.text()).toEqual('
Custom Title');
+ expect(wrapper).toMatchSnapshot();
+ });
});
describe('Dismissible Alert that includes actions section', () => {
@@ -73,10 +88,13 @@ describe('Dismissible Alert that includes actions section', () => {
const mockOnDismiss = jest.fn();
const wrapper = shallowWithIntl(
This is a test).dive();
- expect(wrapper.find(Button).length).toEqual(1);
- expect(wrapper.find(Button).prop('text')).toEqual('Terra.alert.dismiss');
- expect(wrapper.find(Button).prop('onClick')).toEqual(mockOnDismiss);
- expect(wrapper.find(Button).prop('variant')).toEqual('neutral');
+ const dismissButton = wrapper.find(Button);
+ expect(wrapper.find('.title').text()).toEqual('Terra.alert.alert');
+ expect(dismissButton.length).toEqual(1);
+ expect(dismissButton.prop('text')).toEqual('Terra.alert.dismiss');
+ expect(dismissButton.prop('onClick')).toEqual(mockOnDismiss);
+ expect(dismissButton.prop('variant')).toEqual('neutral');
+ expect(dismissButton.prop('aria-describedby')).toEqual('alert-title-00000000-0000-0000-0000-000000000000');
expect(wrapper).toMatchSnapshot();
});
});
@@ -199,11 +217,20 @@ describe('Alert of type custom with custom title and text content', () => {
describe('Alert of type info with custom title and HTML content', () => {
it('should render an Alert component of type info with custom title and HTML content', () => {
- const wrapper = shallowWithIntl(
Four score and seven years ago . . .);
+ const wrapper = shallowWithIntl(
Four score and seven years ago . . .).dive();
- expect(wrapper.prop('title')).toEqual('Gettysburg Address');
- expect(wrapper.prop('type')).toEqual('info');
- expect(wrapper.find('span').text()).toEqual('Four score and seven years ago . . .');
+ const alertDiv = wrapper.find('div.alert-base');
+ expect(alertDiv.prop('className')).toEqual('alert-base info wide');
+ expect(alertDiv.prop('role')).toEqual('status');
+ expect(wrapper.find(IconInformation).length).toEqual(1);
+ expect(wrapper.find('.title').prop('children')).toEqual(
+ [
+
,
+ 'Gettysburg Address',
+ ],
+ );
+ expect(wrapper.find('.title').text()).toEqual('
Gettysburg Address');
+ expect(wrapper.find('.section').find('span').text()).toEqual('Four score and seven years ago . . .');
expect(wrapper).toMatchSnapshot();
});
});
@@ -226,7 +253,7 @@ describe('Alert of type success with an action button text content', () => {
});
});
-describe('Dismissable Alert of type custom with action button, custom title and text content', () => {
+describe('Dismissible Alert of type custom with action button, custom title and text content', () => {
it('should render an Alert component of type custom with an action button', () => {
const mockOnClick = jest.fn();
const mockOnDismiss = jest.fn();
@@ -251,10 +278,10 @@ describe('Dismissable Alert of type custom with action button, custom title and
});
});
-describe('Dismissible Alert', () => {
+describe('Notifications titles', () => {
let wrapper;
- describe('Custom Alert with no title prop', () => {
+ describe('Custom notification with no title prop', () => {
beforeEach(() => {
wrapper = shallowWithIntl(
{
,
).dive();
});
- it('should set the alert message ID', () => {
+ it('should set the notification message ID', () => {
const alertContent = wrapper.find('.section');
expect(alertContent.prop('id')).toEqual('alert-message-00000000-0000-0000-0000-000000000000');
});
- it('should set the dismiss button aria-describedby to the alert description', () => {
+ it('should have no title', () => {
+ const alertTitle = wrapper.find('.title');
+ expect(alertTitle.length).toEqual(0);
+ });
+
+ it('should set the dismiss button aria-describedby to the notification description', () => {
const dismissButton = wrapper.find('Button');
expect(dismissButton.prop('aria-describedby')).toEqual('alert-message-00000000-0000-0000-0000-000000000000');
});
});
- describe('Custom Alert with custom title', () => {
+ describe('Custom notification with custom title', () => {
beforeEach(() => {
wrapper = shallowWithIntl(
{
expect(alertTitle.prop('id')).toEqual('alert-title-00000000-0000-0000-0000-000000000000');
});
+ it('should set the alert title', () => {
+ const alertTitle = wrapper.find('.title');
+ expect(alertTitle.prop('children')).toEqual(['', 'Help!']);
+ expect(alertTitle.text()).toEqual('Help!');
+ });
+
it('should set the dismiss button aria-describedby to the alert title', () => {
const dismissButton = wrapper.find('Button');
expect(dismissButton.prop('aria-describedby')).toEqual('alert-title-00000000-0000-0000-0000-000000000000');
});
});
- describe('Success Alert with no title prop', () => {
+ describe('Success notification with no title prop', () => {
beforeEach(() => {
wrapper = shallowWithIntl(
{
).dive();
});
- it('should set the alert message ID', () => {
+ it('should set the notification message ID', () => {
const alertContent = wrapper.find('.section');
expect(alertContent.prop('id')).toEqual('alert-message-00000000-0000-0000-0000-000000000000');
});
- it('should set the alert title ID', () => {
+ it('should set the notification title ID', () => {
const alertTitle = wrapper.find('.title');
expect(alertTitle.prop('id')).toEqual('alert-title-00000000-0000-0000-0000-000000000000');
});
- it('should set the dismiss button aria-describedby to the alert title', () => {
+ it('should use the default title', () => {
+ const alertTitle = wrapper.find('.title');
+ expect(alertTitle.prop('children')).toEqual([undefined, 'Terra.alert.success']);
+ expect(alertTitle.text()).toEqual('Terra.alert.success');
+ });
+
+ it('should set the dismiss button aria-describedby to the notification title', () => {
const dismissButton = wrapper.find('Button');
expect(dismissButton.prop('aria-describedby')).toEqual('alert-title-00000000-0000-0000-0000-000000000000');
});
});
- describe('Success Alert with blank title', () => {
+ describe('Success notification with blank title', () => {
beforeEach(() => {
wrapper = shallowWithIntl(
{
it('should use the default title', () => {
const alertTitle = wrapper.find('.title');
- expect(alertTitle.prop('children')).toEqual('Terra.alert.success');
+ expect(alertTitle.prop('children')).toEqual(['', 'Terra.alert.success']);
+ expect(alertTitle.text()).toEqual('Terra.alert.success');
});
- it('should set the alert message ID', () => {
+ it('should set the notification message ID', () => {
const alertContent = wrapper.find('.section');
expect(alertContent.prop('id')).toEqual('alert-message-00000000-0000-0000-0000-000000000000');
});
- it('should set the alert title ID', () => {
+ it('should set the notification title ID', () => {
const alertTitle = wrapper.find('.title');
expect(alertTitle.prop('id')).toEqual('alert-title-00000000-0000-0000-0000-000000000000');
});
- it('should set the dismiss button aria-describedby to the alert title', () => {
+ it('should set the dismiss button aria-describedby to the notification title', () => {
+ const dismissButton = wrapper.find('Button');
+ expect(dismissButton.prop('aria-describedby')).toEqual('alert-title-00000000-0000-0000-0000-000000000000');
+ });
+ });
+
+ describe('Success notification with title', () => {
+ beforeEach(() => {
+ wrapper = shallowWithIntl(
+ { }}
+ >
+ This is a success alert.
+ ,
+ ).dive();
+ });
+
+ it('should use the custom title', () => {
+ const alertTitle = wrapper.find('.title');
+ expect(alertTitle.prop('children')).toEqual(
+ [
+ ,
+ 'Custom Success Title',
+ ],
+ );
+ expect(alertTitle.text()).toEqual('Custom Success Title');
+ });
+
+ it('should set the notification message ID', () => {
+ const alertContent = wrapper.find('.section');
+ expect(alertContent.prop('id')).toEqual('alert-message-00000000-0000-0000-0000-000000000000');
+ });
+
+ it('should set the notification title ID', () => {
+ const alertTitle = wrapper.find('.title');
+ expect(alertTitle.prop('id')).toEqual('alert-title-00000000-0000-0000-0000-000000000000');
+ });
+
+ it('should set the dismiss button aria-describedby to the notification title', () => {
const dismissButton = wrapper.find('Button');
expect(dismissButton.prop('aria-describedby')).toEqual('alert-title-00000000-0000-0000-0000-000000000000');
});
diff --git a/packages/terra-alert/tests/jest/__snapshots__/Alert.test.jsx.snap b/packages/terra-alert/tests/jest/__snapshots__/Alert.test.jsx.snap
index 7ba97c2fc2a..4f73464c3b7 100644
--- a/packages/terra-alert/tests/jest/__snapshots__/Alert.test.jsx.snap
+++ b/packages/terra-alert/tests/jest/__snapshots__/Alert.test.jsx.snap
@@ -173,43 +173,53 @@ exports[`Alert of type error with text content should render an Alert component
`;
exports[`Alert of type info with custom title and HTML content should render an Alert component of type info with custom title and HTML content 1`] = `
-
-
- Four score and seven years ago . . .
-
-
+
+
+
+
+
+
+
+
+
+
+ Gettysburg Address
+
+
+ Four score and seven years ago . . .
+
+
+
+
+
+
`;
exports[`Alert of type info with text content should render an Alert component of type info 1`] = `
@@ -722,6 +732,53 @@ exports[`Alert with props should render an alert with provided role 1`] = `
`;
+exports[`Alert with props should render an alert with provided title 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+ Custom Title
+
+
+
+
+
+
+`;
+
exports[`Alert with props should render disableAlertActionFocus when provided 1`] = `
`;
-exports[`Dismissable Alert of type custom with action button, custom title and text content should render an Alert component of type custom with an action button 1`] = `
+exports[`Dismissible Alert of type custom with action button, custom title and text content should render an Alert component of type custom with an action button 1`] = `