Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "patch",
"area": "fix",
"workstream": "Ezra",
"comment": "Update people button behavior to meet A11y requirements",
"packageName": "@azure/communication-react",
"email": "dmceachern@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"type": "patch",
"area": "fix",
"workstream": "Ezra",
"comment": "Update people button behavior to meet A11y requirements",
"packageName": "@azure/communication-react",
"email": "dmceachern@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export const CallArrangement = (props: CallArrangementProps): JSX.Element => {
const controlBarRef = useRef<FocusableElement>(null);
const peopleButtonRef = useRef<IButton>(null);
const cameraButtonRef = useRef<IButton>(null);
const sidePaneDissmissButtonRef = useRef<IButton>(null);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's diss?! ;) Dissmiss or Dismiss?


const containerRef = useRef<HTMLDivElement>(null);
const containerWidth = _useContainerWidth(containerRef);
Expand Down Expand Up @@ -204,7 +205,8 @@ export const CallArrangement = (props: CallArrangementProps): JSX.Element => {
onFetchParticipantMenuItems: props.callControlProps?.onFetchParticipantMenuItems,
mobileView: props.mobileView,
peopleButtonRef,
setParticipantActioned
setParticipantActioned,
sidePaneDissmissButtonRef
}),
[
updateSidePaneRenderer,
Expand All @@ -213,7 +215,8 @@ export const CallArrangement = (props: CallArrangementProps): JSX.Element => {
props.onFetchAvatarPersonaData,
props.mobileView,
peopleButtonRef,
setParticipantActioned
setParticipantActioned,
sidePaneDissmissButtonRef
]
);

Expand Down Expand Up @@ -589,6 +592,7 @@ export const CallArrangement = (props: CallArrangementProps): JSX.Element => {
}
onToggleTeamsMeetingConferenceModal={toggleTeamsMeetingConferenceModal}
teamsMeetingConferenceModalPresent={showTeamsMeetingConferenceModal}
sidePaneDissmissButtonRef={sidePaneDissmissButtonRef}
/>
)}
</Stack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const usePeoplePane = (props: {
onPermitOthersVideo?: () => Promise<void>;
/* @conditional-compile-remove(media-access) */
meetingMediaAccess?: MediaAccess;
sidePaneDissmissButtonRef?: RefObject<IButton>;
}): {
openPeoplePane: () => void;
closePeoplePane: () => void;
Expand Down Expand Up @@ -102,7 +103,8 @@ export const usePeoplePane = (props: {
/* @conditional-compile-remove(media-access) */
onPermitOthersVideo,
/* @conditional-compile-remove(media-access) */
meetingMediaAccess
meetingMediaAccess,
sidePaneDissmissButtonRef
} = props;

const closePane = useCallback(() => {
Expand Down Expand Up @@ -328,9 +330,10 @@ export const usePeoplePane = (props: {
headingText={localeStrings.peoplePaneTitle}
dismissSidePaneButtonAriaLabel={localeStrings.dismissSidePaneButtonLabel}
mobileView={mobileView ?? false}
dismissButtonComponentRef={sidePaneDissmissButtonRef}
/>
),
[mobileView, closePane, localeStrings]
[mobileView, closePane, localeStrings, sidePaneDissmissButtonRef]
);

const onFetchParticipantMenuItemsForCallComposite = useCallback(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React, { useMemo, useRef, useEffect, useState, useCallback, useImperativeHandle, forwardRef } from 'react';
import React, {
useMemo,
useRef,
useEffect,
useState,
useCallback,
useImperativeHandle,
forwardRef,
RefObject
} from 'react';
import { CallAdapterProvider } from '../../CallComposite/adapter/CallAdapterProvider';
import { CallAdapter } from '../../CallComposite';
import { PeopleButton } from './PeopleButton';
Expand Down Expand Up @@ -78,17 +87,16 @@ export interface CommonCallControlBarProps {
onUserSetOverflowGalleryPositionChange?: (position: 'Responsive' | 'horizontalTop') => void;
onUserSetGalleryLayout?: (layout: VideoGalleryLayout) => void;
userSetGalleryLayout?: VideoGalleryLayout;
peopleButtonRef?: React.RefObject<IButton>;
cameraButtonRef?: React.RefObject<IButton>;
videoBackgroundPickerRef?: React.RefObject<IButton>;
peopleButtonRef?: RefObject<IButton>;
cameraButtonRef?: RefObject<IButton>;
videoBackgroundPickerRef?: RefObject<IButton>;
onSetDialpadPage?: () => void;
dtmfDialerPresent?: boolean;
onStopLocalSpotlight?: () => void;
useTeamsCaptions?: boolean;

onToggleTeamsMeetingConferenceModal?: () => void;

teamsMeetingConferenceModalPresent?: boolean;
sidePaneDissmissButtonRef?: RefObject<IButton>;
}

const inferCommonCallControlOptions = (
Expand Down Expand Up @@ -580,6 +588,7 @@ export const CommonCallControlBar = forwardRef<FocusableElement, CommonCallContr
strings={peopleButtonStrings}
styles={commonButtonStyles}
componentRef={props.peopleButtonRef}
peoplePaneDismissButtonRef={props.sidePaneDissmissButtonRef}
/>
)}
{customButtons['secondary']
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React, { useMemo } from 'react';
import React, { useCallback, useMemo, RefObject } from 'react';
import { ControlBarButton, ControlBarButtonProps, ControlBarButtonStyles, useTheme } from '@internal/react-components';
import { concatStyleSets } from '@fluentui/react';
import { concatStyleSets, IButton } from '@fluentui/react';
import { CallCompositeIcon } from '../icons';

const icon = (): JSX.Element => <CallCompositeIcon iconName={'ControlBarPeopleButton'} />;

/**
* @private
* props for the PeopleButton component
*/
export const PeopleButton = (props: ControlBarButtonProps): JSX.Element => {
const { strings, onRenderOnIcon, onRenderOffIcon, onClick } = props;
export interface PeopleButtonProps extends ControlBarButtonProps {
peoplePaneDismissButtonRef?: RefObject<IButton>;
}

/**
* @private
*/
export const PeopleButton = (props: PeopleButtonProps): JSX.Element => {
const { strings, onRenderOnIcon, onRenderOffIcon, onClick, peoplePaneDismissButtonRef } = props;
const theme = useTheme();
const styles: ControlBarButtonStyles = useMemo(
() =>
Expand All @@ -27,6 +35,16 @@ export const PeopleButton = (props: ControlBarButtonProps): JSX.Element => {
[props.styles, theme.palette.neutralLight]
);

const handleTab = useCallback(
(event: React.KeyboardEvent<HTMLElement>) => {
if (event.key === 'Tab' && !event.shiftKey && peoplePaneDismissButtonRef?.current) {
peoplePaneDismissButtonRef.current.focus();
event.preventDefault();
}
},
[peoplePaneDismissButtonRef]
);

return (
<ControlBarButton
{...props}
Expand All @@ -35,6 +53,7 @@ export const PeopleButton = (props: ControlBarButtonProps): JSX.Element => {
onRenderOnIcon={onRenderOnIcon ?? icon}
onRenderOffIcon={onRenderOffIcon ?? icon}
onClick={onClick}
onKeyDown={handleTab}
styles={styles}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
headingMoreButtonStyles
} from './styles/ParticipantContainer.styles';
import { ParticipantList, ParticipantListProps, ParticipantMenuItemsCallback } from '@internal/react-components';
import { FocusZone, Stack, Text, TooltipHost, TooltipOverflowMode, getId, useTheme } from '@fluentui/react';
import { Stack, Text, TooltipHost, TooltipOverflowMode, getId, useTheme } from '@fluentui/react';
import { DefaultButton, IContextualMenuProps } from '@fluentui/react';
import { AvatarPersona, AvatarPersonaDataCallback } from './AvatarPersona';
import { useId } from '@fluentui/react-hooks';
Expand Down Expand Up @@ -97,7 +97,6 @@ export const ParticipantListWithHeading = (props: {
(headingMoreButtonMenuProps?.items && headingMoreButtonMenuProps.items.length > 0)) && (
<Stack.Item>
<DefaultButton
autoFocus
data-ui-id="people-pane-header-more-button"
ariaLabel={headingMoreButtonAriaLabel}
styles={headingMoreButtonStyles(theme)}
Expand All @@ -109,7 +108,7 @@ export const ParticipantListWithHeading = (props: {
</Stack.Item>
)}
</Stack>
<FocusZone className={participantListContainerStyle} shouldFocusOnMount={true}>
<Stack className={participantListContainerStyle}>
<ParticipantList
{...participantListProps}
pinnedParticipants={pinnedParticipants}
Expand Down Expand Up @@ -139,7 +138,7 @@ export const ParticipantListWithHeading = (props: {
showParticipantOverflowTooltip={!props.isMobile}
participantAriaLabelledBy={subheadingUniqueId}
/>
</FocusZone>
</Stack>
</Stack>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { CommandBarButton, DefaultButton, Stack, concatStyleSets } from '@fluentui/react';
import { CommandBarButton, DefaultButton, IButton, Stack, concatStyleSets } from '@fluentui/react';
import { useTheme } from '@internal/react-components';
import React, { useMemo } from 'react';
import React, { useMemo, RefObject } from 'react';
import { sidePaneHeaderContainerStyles, sidePaneHeaderStyles } from '../common/styles/ParticipantContainer.styles';
import {
mobilePaneBackButtonStyles,
Expand All @@ -22,6 +22,8 @@ export const SidePaneHeader = (props: {
dismissSidePaneButtonAriaDescription?: string;
onClose: () => void;
mobileView: boolean;
peopleButtonRef?: RefObject<IButton>;
dismissButtonComponentRef?: RefObject<IButton>;
}): JSX.Element => {
const theme = useTheme();
const sidePaneCloseButtonStyles = useMemo(
Expand Down Expand Up @@ -57,8 +59,10 @@ export const SidePaneHeader = (props: {
ariaLabel={props.dismissSidePaneButtonAriaLabel}
styles={sidePaneCloseButtonStyles}
iconProps={{ iconName: 'cancel' }}
onClick={props.onClose}
autoFocus
onClick={() => {
props.onClose();
}}
componentRef={props.dismissButtonComponentRef}
/>
</Stack.Item>
</Stack>
Expand Down