✨(frontend) improve screen reader support in DocShare modal
adds relevant aria-labels to enhance accessibility for assistive technologies Signed-off-by: Cyril <c.gromoff@gmail.com>
This commit is contained in:
@@ -69,7 +69,7 @@ export const QuickSearch = ({
|
||||
label={label}
|
||||
shouldFilter={false}
|
||||
ref={ref}
|
||||
tabIndex={0}
|
||||
tabIndex={-1}
|
||||
value={selectedValue}
|
||||
onValueChange={handleValueChange}
|
||||
>
|
||||
|
||||
@@ -18,6 +18,7 @@ type DocRoleDropdownProps = {
|
||||
onSelectRole: (role: Role) => void;
|
||||
rolesAllowed?: Role[];
|
||||
isLastOwner?: boolean;
|
||||
ariaLabel?: string;
|
||||
};
|
||||
|
||||
export const DocRoleDropdown = ({
|
||||
@@ -29,6 +30,7 @@ export const DocRoleDropdown = ({
|
||||
rolesAllowed,
|
||||
access,
|
||||
isLastOwner = false,
|
||||
ariaLabel,
|
||||
}: DocRoleDropdownProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { transRole, translatedRoles } = useTrans();
|
||||
@@ -113,11 +115,15 @@ export const DocRoleDropdown = ({
|
||||
return (
|
||||
<DropdownMenu
|
||||
topMessage={topMessage}
|
||||
label="doc-role-dropdown"
|
||||
label={t('{{action}}, current role: {{role}}', {
|
||||
action: ariaLabel,
|
||||
role: transRole(currentRole),
|
||||
})}
|
||||
showArrow={true}
|
||||
arrowCss={css`
|
||||
color: var(--c--theme--colors--primary-800) !important;
|
||||
`}
|
||||
testId="doc-role-dropdown"
|
||||
options={[
|
||||
...roles,
|
||||
{
|
||||
|
||||
@@ -78,6 +78,9 @@ const DocShareAccessRequestItem = ({ doc, accessRequest }: Props) => {
|
||||
onSelectRole={setRole}
|
||||
canUpdate={doc.abilities.accesses_manage}
|
||||
rolesAllowed={accessRequest.abilities.set_role_to}
|
||||
ariaLabel={t('Change role for {{name}}', {
|
||||
name: accessRequest.user.full_name || accessRequest.user.email,
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
color="tertiary"
|
||||
|
||||
@@ -153,6 +153,7 @@ export const DocShareAddMemberList = ({
|
||||
onClick={() => void onInvite()}
|
||||
disabled={isLoading}
|
||||
aria-label={inviteLabel}
|
||||
data-testid="doc-share-invite-button"
|
||||
>
|
||||
{t('Invite')}
|
||||
</Button>
|
||||
|
||||
@@ -112,6 +112,9 @@ export const DocShareInvitationItem = ({
|
||||
canUpdate={canUpdate}
|
||||
doc={doc}
|
||||
access={invitation}
|
||||
ariaLabel={t('Change role for {{email}}', {
|
||||
email: invitation.email,
|
||||
})}
|
||||
/>
|
||||
|
||||
{canUpdate && (
|
||||
|
||||
@@ -77,6 +77,9 @@ export const DocShareMemberItem = ({
|
||||
rolesAllowed={access.abilities.set_role_to}
|
||||
access={access}
|
||||
doc={doc}
|
||||
ariaLabel={t('Change role for {{name}}', {
|
||||
name: access.user.full_name || access.user.email,
|
||||
})}
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
|
||||
const [selectedUsers, setSelectedUsers] = useState<User[]>([]);
|
||||
const [userQuery, setUserQuery] = useState('');
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [liveAnnouncement, setLiveAnnouncement] = useState('');
|
||||
|
||||
const [listHeight, setListHeight] = useState<string>('400px');
|
||||
const canShare = doc.abilities.accesses_manage && isRootDoc;
|
||||
@@ -88,6 +89,19 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
|
||||
setSelectedUsers((prev) => [...prev, user]);
|
||||
setUserQuery('');
|
||||
setInputValue('');
|
||||
|
||||
// Announce to screen readers
|
||||
const userName = user.full_name || user.email;
|
||||
setLiveAnnouncement(
|
||||
t(
|
||||
'{{name}} added to invite list. Add more members or press Tab to select role and invite.',
|
||||
{
|
||||
name: userName,
|
||||
},
|
||||
),
|
||||
);
|
||||
// Clear announcement after it's been read
|
||||
setTimeout(() => setLiveAnnouncement(''), 100);
|
||||
};
|
||||
|
||||
const { data: membersQuery } = useDocAccesses({
|
||||
@@ -114,6 +128,16 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
|
||||
}
|
||||
const newArray = [...prevState];
|
||||
newArray.splice(index, 1);
|
||||
|
||||
// Announce to screen readers
|
||||
const userName = row.full_name || row.email;
|
||||
setLiveAnnouncement(
|
||||
t('{{name}} removed from invite list', {
|
||||
name: userName,
|
||||
}),
|
||||
);
|
||||
setTimeout(() => setLiveAnnouncement(''), 100);
|
||||
|
||||
return newArray;
|
||||
});
|
||||
};
|
||||
@@ -175,12 +199,22 @@ export const DocShareModal = ({ doc, onClose, isRootDoc = true }: Props) => {
|
||||
<ButtonCloseModal
|
||||
aria-label={t('Close the share modal')}
|
||||
onClick={onClose}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
hideCloseButton
|
||||
>
|
||||
<ShareModalStyle />
|
||||
{/* Screen reader announcements */}
|
||||
<div
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
aria-atomic="true"
|
||||
className="sr-only"
|
||||
>
|
||||
{liveAnnouncement}
|
||||
</div>
|
||||
<Box
|
||||
$height="auto"
|
||||
$maxHeight={canViewAccesses ? modalContentHeight : 'none'}
|
||||
|
||||
Reference in New Issue
Block a user