Skip to content

Commit 2e995fc

Browse files
[A11y Bugfix] Add announcements for people button/ people flyout (#2588)
* Add announcements for people button/ people flyout * Change files * Duplicate change files for beta release * fix lint
1 parent 08aac1e commit 2e995fc

9 files changed

Lines changed: 87 additions & 16 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "Add narrator announcements to participant button copy link and peoplepane copylink button in CallWithChat composite.",
4+
"packageName": "@azure/communication-react",
5+
"email": "94866715+dmceachernmsft@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "Add narrator announcements to participant button copy link and peoplepane copylink button in CallWithChat composite.",
4+
"packageName": "@azure/communication-react",
5+
"email": "94866715+dmceachernmsft@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}

packages/communication-react/review/beta/communication-react.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2786,6 +2786,7 @@ export type ParticipantsButtonSelector = (state: CallClientState, props: Calling
27862786

27872787
// @public
27882788
export interface ParticipantsButtonStrings {
2789+
copyInviteLinkActionedAriaLabel: string;
27892790
copyInviteLinkButtonLabel: string;
27902791
label: string;
27912792
menuHeader: string;

packages/communication-react/review/stable/communication-react.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2169,6 +2169,7 @@ export type ParticipantsButtonSelector = (state: CallClientState, props: Calling
21692169

21702170
// @public
21712171
export interface ParticipantsButtonStrings {
2172+
copyInviteLinkActionedAriaLabel: string;
21722173
copyInviteLinkButtonLabel: string;
21732174
label: string;
21742175
menuHeader: string;

packages/react-components/review/beta/react-components.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,7 @@ export interface ParticipantsButtonProps extends ControlBarButtonProps {
13101310

13111311
// @public
13121312
export interface ParticipantsButtonStrings {
1313+
copyInviteLinkActionedAriaLabel: string;
13131314
copyInviteLinkButtonLabel: string;
13141315
label: string;
13151316
menuHeader: string;

packages/react-components/review/stable/react-components.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,7 @@ export interface ParticipantsButtonProps extends ControlBarButtonProps {
11131113

11141114
// @public
11151115
export interface ParticipantsButtonStrings {
1116+
copyInviteLinkActionedAriaLabel: string;
11161117
copyInviteLinkButtonLabel: string;
11171118
label: string;
11181119
menuHeader: string;

packages/react-components/src/components/ParticipantsButton.tsx

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
} from '@fluentui/react';
1212
import { _formatString } from '@internal/acs-ui-common';
1313
import copy from 'copy-to-clipboard';
14-
import React, { useCallback, useMemo } from 'react';
14+
import React, { useCallback, useMemo, useState } from 'react';
1515
import {
1616
ParticipantList,
1717
ParticipantListProps,
@@ -29,6 +29,7 @@ import { _HighContrastAwareIcon } from './HighContrastAwareIcon';
2929
import { _preventDismissOnEvent as preventDismissOnEvent } from '@internal/acs-ui-common';
3030
/* @conditional-compile-remove(rooms) */
3131
import { _usePermissions } from '../permissions/PermissionsProvider';
32+
import { Announcer } from './Announcer';
3233

3334
/**
3435
* Styles for the {@link ParticipantsButton} menu.
@@ -82,6 +83,10 @@ export interface ParticipantsButtonStrings {
8283
* Label of menu button to mute all participants
8384
*/
8485
muteAllButtonLabel: string;
86+
/**
87+
* Narrator announcement for when the invite link has been copied by the user to the clipboard
88+
*/
89+
copyInviteLinkActionedAriaLabel: string;
8590
}
8691

8792
/**
@@ -178,6 +183,8 @@ export const ParticipantsButton = (props: ParticipantsButtonProps): JSX.Element
178183

179184
const disabled = props.disabled;
180185

186+
const [copyInviteLinkAnnouncerStrings, setCopyInviteLinkAnnouncerStrings] = useState<string>('');
187+
181188
const onRenderPeopleIcon = (): JSX.Element => (
182189
<_HighContrastAwareIcon disabled={disabled} iconName="ControlButtonParticipants" />
183190
);
@@ -227,6 +234,20 @@ export const ParticipantsButton = (props: ParticipantsButtonProps): JSX.Element
227234
const strings = useMemo(() => ({ ...localeStrings, ...props.strings }), [localeStrings, props.strings]);
228235
const participantCount = participants.length;
229236

237+
/**
238+
* sets the announcement string for when the link is copied.
239+
*/
240+
const toggleAnnouncerString = useCallback(() => {
241+
setCopyInviteLinkAnnouncerStrings(strings.copyInviteLinkActionedAriaLabel);
242+
/**
243+
* Clears the announcer string after the user clicks the
244+
* copyInviteLink button allowing it to be re-announced.
245+
*/
246+
setTimeout(() => {
247+
setCopyInviteLinkAnnouncerStrings('');
248+
}, 3000);
249+
}, [strings.copyInviteLinkActionedAriaLabel]);
250+
230251
const generateDefaultParticipantsSubMenuProps = useCallback((): IContextualMenuItem[] => {
231252
const items: IContextualMenuItem[] = [];
232253

@@ -313,7 +334,10 @@ export const ParticipantsButton = (props: ParticipantsButtonProps): JSX.Element
313334
title: strings.copyInviteLinkButtonLabel,
314335
itemProps: { styles: styles?.menuStyles?.menuItemStyles },
315336
iconProps: { iconName: 'Link' },
316-
onClick: onCopyCallback
337+
onClick: () => {
338+
onCopyCallback();
339+
toggleAnnouncerString();
340+
}
317341
});
318342
}
319343

@@ -329,18 +353,22 @@ export const ParticipantsButton = (props: ParticipantsButtonProps): JSX.Element
329353
excludeMe,
330354
ids.participantButtonPeopleMenuItem,
331355
generateDefaultParticipantsSubMenuProps,
332-
onCopyCallback
356+
onCopyCallback,
357+
toggleAnnouncerString
333358
]);
334359

335360
return (
336-
<ControlBarButton
337-
{...props}
338-
disabled={disabled}
339-
menuProps={props.menuProps ?? defaultMenuProps}
340-
menuIconProps={{ hidden: true }}
341-
onRenderIcon={onRenderIcon ?? onRenderPeopleIcon}
342-
strings={strings}
343-
labelKey={props.labelKey ?? 'participantsButtonLabel'}
344-
/>
361+
<>
362+
<Announcer announcementString={copyInviteLinkAnnouncerStrings} ariaLive={'polite'} />
363+
<ControlBarButton
364+
{...props}
365+
disabled={disabled}
366+
menuProps={props.menuProps ?? defaultMenuProps}
367+
menuIconProps={{ hidden: true }}
368+
onRenderIcon={onRenderIcon ?? onRenderPeopleIcon}
369+
strings={strings}
370+
labelKey={props.labelKey ?? 'participantsButtonLabel'}
371+
/>
372+
</>
345373
);
346374
};

packages/react-components/src/localization/locales/en-US/strings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@
9090
"menuHeader": "In this call",
9191
"participantsListButtonLabel": "{numParticipants} people",
9292
"muteAllButtonLabel": "Mute all",
93-
"copyInviteLinkButtonLabel": "Copy invite link"
93+
"copyInviteLinkButtonLabel": "Copy invite link",
94+
"copyInviteLinkActionedAriaLabel": "Invite link copied"
9495
},
9596
"screenShareButton": {
9697
"onLabel": "Stop presenting",

packages/react-composites/src/composites/common/AddPeopleButton.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT license.
33
import { concatStyleSets, DefaultButton, IButtonStyles, PrimaryButton, Stack, useTheme } from '@fluentui/react';
44
import copy from 'copy-to-clipboard';
5-
import React, { useMemo } from 'react';
5+
import React, { useCallback, useMemo, useState } from 'react';
66
import { CallWithChatCompositeStrings } from '../../index-public';
77
/* @conditional-compile-remove(one-to-n-calling) @conditional-compile-remove(PSTN-calls) */
88
import { CallCompositeStrings } from '../../index-public';
@@ -21,6 +21,7 @@ import { AddPeopleDropdown } from './AddPeopleDropdown';
2121
import { PhoneNumberIdentifier } from '@azure/communication-common';
2222
/* @conditional-compile-remove(PSTN-calls) */
2323
import { AddPhoneNumberOptions } from '@azure/communication-calling';
24+
import { Announcer } from '@internal/react-components';
2425

2526
/** @private */
2627
export interface AddPeopleButtonProps {
@@ -41,10 +42,25 @@ export const AddPeopleButton = (props: AddPeopleButtonProps): JSX.Element => {
4142

4243
const theme = useTheme();
4344

45+
const [copyInviteLinkAnnouncerStrings, setCopyInviteLinkAnnouncerStrings] = useState<string>('');
46+
4447
const copyLinkButtonStylesThemed = useMemo(
4548
(): IButtonStyles => concatStyleSets(copyLinkButtonStyles, themedCopyLinkButtonStyles(mobileView, theme)),
4649
[mobileView, theme]
4750
);
51+
/**
52+
* sets the announcement string for when the link is copied.
53+
*/
54+
const toggleAnnouncerString = useCallback(() => {
55+
setCopyInviteLinkAnnouncerStrings(strings.copyInviteLinkActionedAriaLabel);
56+
/**
57+
* Clears the announcer string after the user clicks the
58+
* copyInviteLink button allowing it to be re-announced.
59+
*/
60+
setTimeout(() => {
61+
setCopyInviteLinkAnnouncerStrings('');
62+
}, 3000);
63+
}, [strings.copyInviteLinkActionedAriaLabel]);
4864

4965
/* @conditional-compile-remove(PSTN-calls) */
5066
if (mobileView) {
@@ -77,8 +93,12 @@ export const AddPeopleButton = (props: AddPeopleButtonProps): JSX.Element => {
7793
<Stack>
7894
{inviteLink && (
7995
<Stack.Item styles={copyLinkButtonContainerStyles}>
96+
<Announcer announcementString={copyInviteLinkAnnouncerStrings} ariaLive={'polite'} />
8097
<PrimaryButton
81-
onClick={() => copy(inviteLink ?? '')}
98+
onClick={() => {
99+
copy(inviteLink ?? '');
100+
toggleAnnouncerString();
101+
}}
82102
styles={copyLinkButtonStylesThemed}
83103
onRenderIcon={() => <CallWithChatCompositeIcon iconName="Link" style={linkIconStyles} />}
84104
text={strings.copyInviteLinkButtonLabel}
@@ -92,10 +112,14 @@ export const AddPeopleButton = (props: AddPeopleButtonProps): JSX.Element => {
92112
<Stack tokens={peoplePaneContainerTokens}>
93113
{inviteLink && (
94114
<Stack styles={copyLinkButtonStackStyles}>
115+
<Announcer announcementString={copyInviteLinkAnnouncerStrings} ariaLive={'polite'} />
95116
<DefaultButton
96117
text={strings.copyInviteLinkButtonLabel}
97118
onRenderIcon={() => <CallWithChatCompositeIcon iconName="Link" style={linkIconStyles} />}
98-
onClick={() => copy(inviteLink ?? '')}
119+
onClick={() => {
120+
copy(inviteLink ?? '');
121+
toggleAnnouncerString();
122+
}}
99123
styles={copyLinkButtonStylesThemed}
100124
/>
101125
</Stack>

0 commit comments

Comments
 (0)