Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/components/NotificationSystem/NotificationSystem.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ type CustomOptions = {
* No announcement will be made if this is not provided.
*/
screenReaderAnnouncement?: string;
/**
* Screen reader announcer to use for this message (overrides notification system default)
*/
announcerIdentity?: string;
};

export type UpdateOptionsType = UpdateOptions & CustomOptions;
Expand Down
150 changes: 137 additions & 13 deletions src/components/NotificationSystem/NotificationSystem.utils.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React from 'react';
import { toast } from 'react-toastify';

import ScreenReaderAnnouncer from '../ScreenReaderAnnouncer';

import { ATTENTION, DEFAULTS } from './NotificationSystem.constants';
import * as utils from './NotificationSystem.utils';
import { toast } from 'react-toastify';

const { getContainerID, calculateAutoClose, notify, update, dismiss, isActive } = utils;

Expand Down Expand Up @@ -65,14 +68,26 @@ describe('NotificationSystem utils', () => {
});

describe('notify', () => {
const toastId = 'test';
const notificationSystemId = 'test_containerbla';
const autoClose = 5000;
const onClose = jest.fn();
const attention = ATTENTION.MEDIUM;
const screenReaderAnnouncement = 'some announcement';
const announcerIdentity = 'some_announcer_id';

let spies;

const setup = () => {
spies = {
autoClose: jest.spyOn(utils, 'calculateAutoClose'),
getContainerID: jest.spyOn(utils, 'getContainerID'),
announce: jest.spyOn(ScreenReaderAnnouncer, 'announce').mockReturnValue(),
};
};

it('should fire the right functions and returns correctly when firing the toast function', () => {
const autoCloseSpy = jest.spyOn(utils, 'calculateAutoClose');
const getContainerIDSpy = jest.spyOn(utils, 'getContainerID');
const toastId = 'test';
const notificationSystemId = 'test_containerbla';
const autoClose = 5000;
const onClose = jest.fn();
const attention = ATTENTION.MEDIUM;
setup();

const options = { toastId, onClose, autoClose, notificationSystemId, attention };
expect(notify(<div />, options)).toBe(toastId);
Expand All @@ -83,15 +98,81 @@ describe('NotificationSystem utils', () => {
onClose: onClose,
toastId: toastId,
});
expect(autoCloseSpy).toHaveBeenCalledWith(options);
expect(getContainerIDSpy).toHaveBeenCalledWith(notificationSystemId, ATTENTION.MEDIUM);
expect(spies.autoClose).toHaveBeenCalledWith(options);
expect(spies.getContainerID).toHaveBeenCalledWith(notificationSystemId, ATTENTION.MEDIUM);
expect(spies.announce).not.toHaveBeenCalled();
});

it('should announce the screenReaderAnnouncement if provided, using the notificationSystemId', () => {
setup();

const options = {
toastId,
onClose,
autoClose,
notificationSystemId,
attention,
screenReaderAnnouncement,
};
expect(notify(<div />, options)).toBe(toastId);

expect(toast).toHaveBeenCalledWith(<div />, {
autoClose: autoClose,
containerId: 'test_containerbla_medium_notification_container',
onClose: onClose,
toastId: toastId,
});
expect(spies.autoClose).toHaveBeenCalledWith(options);
expect(spies.getContainerID).toHaveBeenCalledWith(notificationSystemId, ATTENTION.MEDIUM);
expect(spies.announce).toHaveBeenCalledWith(
{ body: screenReaderAnnouncement },
notificationSystemId
);
});

it('should announce the screenReaderAnnouncement using the announcerIdentity, if provided', () => {
setup();

const options = {
toastId,
onClose,
autoClose,
notificationSystemId,
attention,
screenReaderAnnouncement,
announcerIdentity,
};
expect(notify(<div />, options)).toBe(toastId);

expect(toast).toHaveBeenCalledWith(<div />, {
autoClose: autoClose,
containerId: 'test_containerbla_medium_notification_container',
onClose: onClose,
toastId: toastId,
});
expect(spies.autoClose).toHaveBeenCalledWith(options);
expect(spies.getContainerID).toHaveBeenCalledWith(notificationSystemId, ATTENTION.MEDIUM);
expect(spies.announce).toHaveBeenCalledWith(
{ body: screenReaderAnnouncement },
announcerIdentity
);
});
});

describe('update', () => {
let toastId;
let spies;

const setup = () => {
toastId = notify(<div />, { notificationSystemId: 'id' });
spies = {
getContainerID: jest.spyOn(utils, 'getContainerID'),
announce: jest.spyOn(ScreenReaderAnnouncer, 'announce').mockReturnValue(),
};
};

it('should fire the right functions when updating an existing toast', () => {
const toastId = notify(<div />, { notificationSystemId: 'id' });
const getContainerIDSpy = jest.spyOn(utils, 'getContainerID');
setup();

update(toastId, {
toastId: 'new',
Expand All @@ -104,7 +185,50 @@ describe('NotificationSystem utils', () => {
toastId: 'new',
render: <p />,
});
expect(getContainerIDSpy).toHaveBeenCalledWith('id', 'medium');
expect(spies.getContainerID).toHaveBeenCalledWith('id', 'medium');
expect(spies.announce).not.toHaveBeenCalled();
});

it('should announce the screenReaderAnnouncement if provided, using the notificationSystemId', () => {
setup();

update(toastId, {
toastId: 'new',
render: <p />,
attention: ATTENTION.MEDIUM,
notificationSystemId: 'id',
screenReaderAnnouncement: 'some screenreader announcement',
});
expect(toast.update).toHaveBeenCalledWith(toastId, {
containerId: 'id_medium_notification_container',
toastId: 'new',
render: <p />,
});
expect(spies.getContainerID).toHaveBeenCalledWith('id', 'medium');
expect(spies.announce).toHaveBeenCalledWith({ body: 'some screenreader announcement' }, 'id');
});

it('should announce the screenReaderAnnouncement using the announcerIdentity, if provided', () => {
setup();

update(toastId, {
toastId: 'new',
render: <p />,
attention: ATTENTION.MEDIUM,
notificationSystemId: 'id',
screenReaderAnnouncement: 'some screenreader announcement',
announcerIdentity: 'some_announcer_id',
});
expect(toast.update).toHaveBeenCalledWith(toastId, {
containerId: 'id_medium_notification_container',
toastId: 'new',
render: <p />,
});
expect(spies.getContainerID).toHaveBeenCalledWith('id', 'medium');
expect(spies.announce).toHaveBeenCalledWith(
{ body: 'some screenreader announcement' },
'some_announcer_id'
);
});
});

Expand Down
29 changes: 24 additions & 5 deletions src/components/NotificationSystem/NotificationSystem.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,20 @@ export const calculateAutoClose = (options: NotifyOptionsType): number | false =
* @returns the toastId of the triggered notification
*/
export const notify = (content: ToastContent, options: NotifyOptionsType): Id => {
const { notificationSystemId, screenReaderAnnouncement, toastId, attention, onClose, role } =
options;
const {
notificationSystemId,
screenReaderAnnouncement,
toastId,
attention,
onClose,
role,
announcerIdentity,
} = options;
if (screenReaderAnnouncement) {
ScreenReaderAnnouncer.announce({ body: screenReaderAnnouncement }, notificationSystemId);
ScreenReaderAnnouncer.announce(
{ body: screenReaderAnnouncement },
announcerIdentity || notificationSystemId
);
}
return toast(content, {
toastId: toastId,
Expand All @@ -60,9 +70,18 @@ export const notify = (content: ToastContent, options: NotifyOptionsType): Id =>
* @param options several options to pass in (for details check type)
*/
export const update = (toastId: Id, options: UpdateOptionsType): void => {
const { notificationSystemId, attention, screenReaderAnnouncement, ...updateOptions } = options;
const {
notificationSystemId,
attention,
screenReaderAnnouncement,
announcerIdentity,
...updateOptions
} = options;
if (screenReaderAnnouncement) {
ScreenReaderAnnouncer.announce({ body: screenReaderAnnouncement }, notificationSystemId);
ScreenReaderAnnouncer.announce(
{ body: screenReaderAnnouncement },
announcerIdentity || notificationSystemId
);
}
toast.update(toastId, {
...updateOptions,
Expand Down
Loading