@utahdts/utah-design-system
Version:
Utah Design System React Library
82 lines (73 loc) • 2.65 kB
JSX
import { useMemo } from 'react';
import { ariaLiveTypes } from '../../../enums/ariaLiveTypes';
/** @typedef {import('@utahdts/utah-design-system').AriaLiveType} AriaLiveType */
/**
* @typedef MessageObject {
* @property {string} id
* @property {import('react').ReactNode} message
* }
*/
/**
* @param {number} numberOfLists
* @returns {MessageObject[][]}
*/
function setupDefaultLists(numberOfLists) {
return Array.from({ length: numberOfLists }).map(() => []);
}
const NUMBER_REGIONS = 30;
/**
* @param {object} props
* @param {AriaLiveType} props.ariaLiveType
* @param {import('react').ReactNode[]} props.messages
* @returns {import('react').JSX.Element}
*/
export function AriaLiveMessages({ ariaLiveType, messages }) {
const messagesLists = useMemo(
() => (
// divide out messages in to equally chunked lists
// NUMBER_REGIONS specifies how many queues there are for this list
// when a new message comes in it goes to the list that was most stale
// this way new messages rotate through the divs
messages.reduce(
(lists, message, i) => {
// give messages an ID for their react key
lists[i % NUMBER_REGIONS]?.push({ id: `${i}-${message}`, message });
return lists;
},
setupDefaultLists(NUMBER_REGIONS)
)
),
[messages]
);
/** @type {'alert' | 'log'} */
let role;
switch (ariaLiveType) {
case ariaLiveTypes.ASSERTIVE: role = 'alert'; break;
case ariaLiveTypes.POLITE: role = 'log'; break;
default:
throw new Error(`AriaLiveMessages: Unknown ariaLiveType (${ariaLiveType})`);
}
return (
<div className="aria-live-regions visually-hidden utah-design-system" key={`aria-live-region-${ariaLiveType}`}>
{
messagesLists
// pull the last message off each list queue
.map((messagesList) => messagesList[messagesList.length - 1])
// regions: must have same key no matter the message for each region AND must always have all the regions (otherwise it may stutter)
.map((messagesListMessage, i) => (
messagesListMessage
? (
// eslint-disable-next-line react/no-array-index-key
<div aria-live={ariaLiveType} key={`${ariaLiveType}-messages-${i}`} role={role}>
{messagesListMessage.message}
</div>
)
: (
// eslint-disable-next-line react/no-array-index-key
<div aria-live={ariaLiveType} key={`${ariaLiveType}-messages-${i}`} role={role} />
)
))
}
</div>
);
}