Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runfix: address keyboard navigation issues in call ui (WPB-6075) #16538

Merged
merged 1 commit into from
Jan 17, 2024
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
47 changes: 15 additions & 32 deletions src/script/components/calling/FullscreenVideoCall.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {ConversationClassifiedBar} from 'Components/input/ClassifiedBar';
import {isMediaDevice} from 'src/script/guards/MediaDevice';
import {MediaDeviceType} from 'src/script/media/MediaDeviceType';
import {useKoSubscribableChildren} from 'Util/ComponentUtil';
import {isEnterKey, KEY} from 'Util/KeyboardUtil';
import {handleKeyDown, isEscapeKey} from 'Util/KeyboardUtil';
import {t} from 'Util/LocalizerUtil';
import {preventFocusOutside} from 'Util/util';

Expand Down Expand Up @@ -206,7 +206,6 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
setSelectedAudioOptions([microphone, speaker]);
switchMicrophoneInput(microphone.id);
switchSpeakerOutput(speaker.id);
setAudioOptionsOpen(false);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We now rely on the onClick or onKeyDown event to toggle the menu off

};

const videoOptions = [
Expand Down Expand Up @@ -239,7 +238,6 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
const camera = videoOptions[0].options.find(item => item.value === selectedOption) ?? selectedVideoOptions[0];
setSelectedVideoOptions([camera]);
switchCameraInput(camera.id);
setVideoOptionsOpen(false);
};

const unreadMessagesCount = useAppState(state => state.unreadMessagesCount);
Expand All @@ -249,27 +247,14 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({

const totalPages = callPages.length;

const isSpaceOrEnterKey = (event: React.KeyboardEvent<HTMLButtonElement>) =>
[KEY.ENTER, KEY.SPACE].includes(event.key);

const handleToggleCameraKeydown = (event: React.KeyboardEvent<HTMLButtonElement>) => {
if (isSpaceOrEnterKey(event)) {
toggleCamera(call);
}

return true;
};

Comment on lines -252 to -262
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This logic can be achieved with the handleKeyDown() keyboard util
handleKeyDown() execute the callback in arguments on a key press of spacebar or enter

// To be changed when design chooses a breakpoint, the conditional can be integrated to the ui-kit directly
const horizontalSmBreakpoint = useMatchMedia('max-width: 680px');
const horizontalXsBreakpoint = useMatchMedia('max-width: 500px');
const verticalBreakpoint = useMatchMedia('max-height: 420px');

useEffect(() => {
const onKeyDown = (event: KeyboardEvent): void => {
if (!isEnterKey(event)) {
event.preventDefault();
}
event.preventDefault();
Comment on lines -270 to +257
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We preventDefault for all key presses, including enter, and add onKeyDown events to buttons affected

preventFocusOutside(event, 'video-calling');
};
document.addEventListener('keydown', onKeyDown);
Expand Down Expand Up @@ -361,6 +346,7 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
<button
data-uie-name="pagination-next"
onClick={() => changePage(currentPage + 1, call)}
onKeyDown={event => handleKeyDown(event, () => changePage(currentPage + 1, call))}
type="button"
className="button-reset-default"
css={{
Expand All @@ -378,6 +364,7 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
data-uie-name="pagination-previous"
type="button"
onClick={() => changePage(currentPage - 1, call)}
onKeyDown={event => handleKeyDown(event, () => changePage(currentPage - 1, call))}
className="button-reset-default"
css={{
...paginationButtonStyles,
Expand Down Expand Up @@ -411,6 +398,7 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
className="video-controls__button"
css={videoControlInActiveStyles}
onClick={minimize}
onKeyDown={event => handleKeyDown(event, () => minimize())}
type="button"
aria-labelledby="minimize-label"
data-uie-name="do-call-controls-video-minimize"
Expand All @@ -437,6 +425,7 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
className="video-controls__button"
data-uie-value={!isMuted ? 'inactive' : 'active'}
onClick={() => toggleMute(call, !isMuted)}
onKeyDown={event => handleKeyDown(event, () => toggleMute(call, !isMuted))}
css={!isMuted ? videoControlActiveStyles : videoControlInActiveStyles}
type="button"
aria-labelledby="mute-label"
Expand All @@ -455,13 +444,8 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
<button
className="device-toggle-button"
css={audioOptionsOpen ? videoControlActiveStyles : videoControlInActiveStyles}
onClick={event => {
const target = event.target as Element;
// We want to ensure to only toggling the menu open or closed when clicking the icon, not the select menu
if (!target.closest('#select-microphone')) {
setAudioOptionsOpen(prev => !prev);
}
}}
onClick={() => setAudioOptionsOpen(prev => !prev)}
onKeyDown={event => handleKeyDown(event, () => setAudioOptionsOpen(prev => !prev))}
Comment on lines -458 to +448
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we don't need to filter out the select element in that iteration, both clicks and keypresses to select an option will toggle the menu off

onBlur={event => {
if (!event.currentTarget.contains(event.relatedTarget)) {
setAudioOptionsOpen(false);
Expand All @@ -487,6 +471,7 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
String(selectedOption?.value).includes('input'),
);
}}
onKeyDown={event => isEscapeKey(event) && setAudioOptionsOpen(false)}
menuPlacement="top"
menuIsOpen
wrapperCSS={{marginBottom: 0}}
Expand All @@ -506,7 +491,7 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
className="video-controls__button"
data-uie-value={selfSharesCamera ? 'active' : 'inactive'}
onClick={() => toggleCamera(call)}
onKeyDown={handleToggleCameraKeydown}
onKeyDown={event => handleKeyDown(event, () => toggleCamera(call))}
role="switch"
aria-checked={selfSharesCamera}
tabIndex={TabIndex.FOCUSABLE}
Expand All @@ -529,13 +514,8 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
<button
className="device-toggle-button"
css={videoOptionsOpen ? videoControlActiveStyles : videoControlInActiveStyles}
onClick={event => {
const target = event.target as Element;
// We want to ensure to only toggling the menu open or closed when clicking the icon, not the select menu
if (!target.closest('#select-camera')) {
setVideoOptionsOpen(prev => !prev);
}
}}
onClick={() => setVideoOptionsOpen(prev => !prev)}
onKeyDown={event => handleKeyDown(event, () => setVideoOptionsOpen(prev => !prev))}
onBlur={event => {
if (!event.currentTarget.contains(event.relatedTarget)) {
setVideoOptionsOpen(false);
Expand All @@ -549,6 +529,7 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
autoFocus
value={selectedVideoOptions}
onChange={selectedOption => updateVideoOptions(String(selectedOption?.value))}
onKeyDown={event => isEscapeKey(event) && setVideoOptionsOpen(false)}
id="select-camera"
dataUieName="select-camera"
controlShouldRenderValue={false}
Expand Down Expand Up @@ -582,6 +563,7 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
: videoControlInActiveStyles
}
onClick={() => toggleScreenshare(call)}
onKeyDown={event => handleKeyDown(event, () => toggleScreenshare(call))}
type="button"
aria-labelledby="screen-share-label"
data-uie-value={selfSharesScreen ? 'active' : 'inactive'}
Expand All @@ -603,6 +585,7 @@ const FullscreenVideoCall: React.FC<FullscreenVideoCallProps> = ({
<button
className="video-controls__button video-controls__button--red"
onClick={() => leave(call)}
onKeyDown={event => handleKeyDown(event, () => leave(call))}
type="button"
aria-labelledby="leave-label"
data-uie-name="do-call-controls-video-call-cancel"
Expand Down
3 changes: 3 additions & 0 deletions src/script/components/calling/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import React from 'react';

import {CSSObject} from '@emotion/react';

import {handleKeyDown} from 'Util/KeyboardUtil';

export interface PaginationProps {
currentPage: number;
onChangePage: (newPage: number) => void;
Expand Down Expand Up @@ -64,6 +66,7 @@ const Pagination: React.FC<PaginationProps> = ({totalPages, currentPage, onChang
data-uie-status={isCurrentPage ? 'active' : 'inactive'}
key={page}
onClick={() => onChangePage(page)}
onKeyDown={event => handleKeyDown(event, () => onChangePage(page))}
type="button"
className="button-reset-default"
css={{
Expand Down
Loading