Commit b8a1c6e9 authored by Lyza Danger Gardner's avatar Lyza Danger Gardner Committed by Lyza Gardner

Add support for visually-hidden ToastMessages

parent 0e34ece6
...@@ -16,7 +16,9 @@ import { withServices } from '../service-context'; ...@@ -16,7 +16,9 @@ import { withServices } from '../service-context';
/** /**
* An individual toast message: a brief and transient success or error message. * An individual toast message: a brief and transient success or error message.
* The message may be dismissed by clicking on it. * The message may be dismissed by clicking on it. `visuallyHidden` toast
* messages will not be visible but are still available to screen readers.
*
* Otherwise, the `toastMessenger` service handles removing messages after a * Otherwise, the `toastMessenger` service handles removing messages after a
* certain amount of time. * certain amount of time.
* *
...@@ -42,6 +44,7 @@ function ToastMessage({ message, onDismiss }) { ...@@ -42,6 +44,7 @@ function ToastMessage({ message, onDismiss }) {
/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions */ /* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions */
<Card <Card
classes={classnames('p-0 flex border', { classes={classnames('p-0 flex border', {
'sr-only': message.visuallyHidden,
'border-red-error': message.type === 'error', 'border-red-error': message.type === 'error',
'border-yellow-notice': message.type === 'notice', 'border-yellow-notice': message.type === 'notice',
'border-green-success': message.type === 'success', 'border-green-success': message.type === 'success',
......
...@@ -90,6 +90,17 @@ describe('ToastMessages', () => { ...@@ -90,6 +90,17 @@ describe('ToastMessages', () => {
assert.calledOnce(fakeToastMessenger.dismiss); assert.calledOnce(fakeToastMessenger.dismiss);
}); });
it('should set a screen-reader-only class on `visuallyHidden` messages', () => {
const message = fakeSuccessMessage();
message.visuallyHidden = true;
fakeStore.getToastMessages.returns([message]);
const wrapper = createComponent();
const messageContainer = wrapper.find('ToastMessage').getDOMNode();
assert.include(messageContainer.className, 'sr-only');
});
it('should not dismiss the message if a "More info" link is clicked', () => { it('should not dismiss the message if a "More info" link is clicked', () => {
fakeStore.getToastMessages.returns([fakeNoticeMessage()]); fakeStore.getToastMessages.returns([fakeNoticeMessage()]);
......
...@@ -39,11 +39,15 @@ describe('ToastMessengerService', () => { ...@@ -39,11 +39,15 @@ describe('ToastMessengerService', () => {
it('adds a new success toast message to the store', () => { it('adds a new success toast message to the store', () => {
fakeStore.hasToastMessage.returns(false); fakeStore.hasToastMessage.returns(false);
service.success('hooray'); service.success('hooray', { visuallyHidden: true });
assert.calledWith( assert.calledWith(
fakeStore.addToastMessage, fakeStore.addToastMessage,
sinon.match({ type: 'success', message: 'hooray' }) sinon.match({
type: 'success',
message: 'hooray',
visuallyHidden: true,
})
); );
}); });
......
...@@ -11,6 +11,8 @@ const MESSAGE_DISMISS_DELAY = 500; ...@@ -11,6 +11,8 @@ const MESSAGE_DISMISS_DELAY = 500;
* @typedef MessageOptions * @typedef MessageOptions
* @prop {boolean} [autoDismiss=true] - Whether the toast message automatically disappears. * @prop {boolean} [autoDismiss=true] - Whether the toast message automatically disappears.
* @prop {string} [moreInfoURL=''] - Optional URL for users to visit for "more info" * @prop {string} [moreInfoURL=''] - Optional URL for users to visit for "more info"
* @prop {boolean} [visuallyHidden=false] - When `true`, message will be visually
* hidden but still available to screen readers.
*/ */
/** /**
...@@ -58,7 +60,7 @@ export class ToastMessengerService { ...@@ -58,7 +60,7 @@ export class ToastMessengerService {
_addMessage( _addMessage(
type, type,
messageText, messageText,
{ autoDismiss = true, moreInfoURL = '' } = {} { autoDismiss = true, moreInfoURL = '', visuallyHidden = false } = {}
) { ) {
// Do not add duplicate messages (messages with the same type and text) // Do not add duplicate messages (messages with the same type and text)
if (this._store.hasToastMessage(type, messageText)) { if (this._store.hasToastMessage(type, messageText)) {
...@@ -66,7 +68,13 @@ export class ToastMessengerService { ...@@ -66,7 +68,13 @@ export class ToastMessengerService {
} }
const id = generateHexString(10); const id = generateHexString(10);
const message = { type, id, message: messageText, moreInfoURL }; const message = {
type,
id,
message: messageText,
moreInfoURL,
visuallyHidden,
};
this._store.addToastMessage({ this._store.addToastMessage({
isDismissed: false, isDismissed: false,
......
...@@ -11,6 +11,7 @@ describe('store/modules/toast-messages', () => { ...@@ -11,6 +11,7 @@ describe('store/modules/toast-messages', () => {
id: 'myToast', id: 'myToast',
type: 'anyType', type: 'anyType',
message: 'This is a message', message: 'This is a message',
visuallyHidden: false,
}; };
}); });
...@@ -27,6 +28,7 @@ describe('store/modules/toast-messages', () => { ...@@ -27,6 +28,7 @@ describe('store/modules/toast-messages', () => {
assert.equal(messages[0].id, 'myToast'); assert.equal(messages[0].id, 'myToast');
assert.equal(messages[0].type, 'anyType'); assert.equal(messages[0].type, 'anyType');
assert.equal(messages[0].message, 'This is a message'); assert.equal(messages[0].message, 'This is a message');
assert.isFalse(messages[0].visuallyHidden);
}); });
it('adds duplicate messages to the array of messages in state', () => { it('adds duplicate messages to the array of messages in state', () => {
......
...@@ -7,6 +7,7 @@ import { createStoreModule, makeAction } from '../create-store'; ...@@ -7,6 +7,7 @@ import { createStoreModule, makeAction } from '../create-store';
* @prop {string} message * @prop {string} message
* @prop {string} moreInfoURL * @prop {string} moreInfoURL
* @prop {boolean} isDismissed * @prop {boolean} isDismissed
* @prop {boolean} visuallyHidden
*/ */
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment