Skip to content

Commit

Permalink
feat: Close "Create Group" modal on Esc key press on the first step W…
Browse files Browse the repository at this point in the history
…PB-9718 (#17581)
  • Loading branch information
svitovyda authored Jun 17, 2024
1 parent 94c8eff commit a01fa21
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 37 deletions.
7 changes: 4 additions & 3 deletions src/script/components/ModalComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*
*/

import React, {useEffect, useId, useRef, useState, useCallback} from 'react';
import React, {useEffect, useId, useRef, useState, useCallback, HTMLProps} from 'react';

import {CSSObject} from '@emotion/react';
import {TabIndex} from '@wireapp/react-ui-kit/lib/types/enums';
Expand All @@ -26,7 +26,7 @@ import {noop, preventFocusOutside} from 'Util/util';

import {Icon} from './Icon';

interface ModalComponentProps {
interface ModalComponentProps extends HTMLProps<HTMLDivElement> {
children: React.ReactNode;
isShown: boolean;
id?: string;
Expand Down Expand Up @@ -93,6 +93,7 @@ const ModalComponent: React.FC<ModalComponentProps> = ({
showLoading = false,
wrapperCSS,
children,
onKeyDown,
...rest
}) => {
const [displayNone, setDisplayNone] = useState<boolean>(!isShown);
Expand Down Expand Up @@ -161,7 +162,7 @@ const ModalComponent: React.FC<ModalComponentProps> = ({
onClick={event => event.stopPropagation()}
role="button"
tabIndex={TabIndex.UNFOCUSABLE}
onKeyDown={event => event.stopPropagation()}
onKeyDown={event => (onKeyDown ? onKeyDown(event) : event.stopPropagation())}
css={{...(hasVisibleClass ? ModalContentVisibleStyles : ModalContentStyles), ...wrapperCSS}}
>
{hasVisibleClass ? children : null}
Expand Down
24 changes: 13 additions & 11 deletions src/script/components/Modals/GroupCreation/GroupCreationModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*
*/

import React, {useContext, useEffect, useMemo, useState} from 'react';
import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react';

import {RECEIPT_MODE} from '@wireapp/api-client/lib/conversation/data/ConversationReceiptModeUpdateData';
import {ConversationProtocol} from '@wireapp/api-client/lib/conversation/NewConversation';
Expand All @@ -40,7 +40,7 @@ import {UserSearchableList} from 'Components/UserSearchableList';
import {generateConversationUrl} from 'src/script/router/routeGenerator';
import {createNavigate, createNavigateKeyboard} from 'src/script/router/routerBindings';
import {useKoSubscribableChildren} from 'Util/ComponentUtil';
import {handleEnterDown, isKeyboardEvent, offEscKey, onEscKey} from 'Util/KeyboardUtil';
import {handleEnterDown, handleEscDown, isKeyboardEvent} from 'Util/KeyboardUtil';
import {replaceLink, t} from 'Util/LocalizerUtil';
import {sortUsersByPriority} from 'Util/StringUtil';

Expand Down Expand Up @@ -135,8 +135,6 @@ const GroupCreationModal: React.FC<GroupCreationModalProps> = ({
setSelectedProtocol(protocolOptions.find(protocol => protocol.value === selectedProtocol.value)!);
}, [defaultProtocol]);

const onEscape = () => setIsShown(false);

const stateIsPreferences = groupCreationState === GroupCreationModalState.PREFERENCES;
const stateIsParticipants = groupCreationState === GroupCreationModalState.PARTICIPANTS;
const isServicesRoom = accessState === ACCESS_STATE.TEAM.SERVICES;
Expand All @@ -162,13 +160,16 @@ const GroupCreationModal: React.FC<GroupCreationModalProps> = ({

const filteredContacts = contacts.filter(user => user.isAvailable());

useEffect(() => {
if (stateIsPreferences) {
onEscKey(onEscape);
return;
}
offEscKey(onEscape);
}, [stateIsPreferences]);
const handleEscape = useCallback(
(event: React.KeyboardEvent<HTMLElement> | KeyboardEvent): void => {
handleEscDown(event, () => {
if (stateIsPreferences) {
setIsShown(false);
}
});
},
[setIsShown, stateIsPreferences],
);

useEffect(() => {
let timerId: number;
Expand Down Expand Up @@ -325,6 +326,7 @@ const GroupCreationModal: React.FC<GroupCreationModalProps> = ({
isShown={isShown}
onClosed={onClose}
data-uie-name="group-creation-label"
onKeyDown={stateIsPreferences ? handleEscape : undefined}
>
<div className="modal__header modal__header--list">
{stateIsParticipants && (
Expand Down
32 changes: 9 additions & 23 deletions src/script/util/KeyboardUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,40 +83,26 @@ export const isRemovalAction = (key: string): boolean => removalKeys.includes(ke

export const isSpaceOrEnterKey = (key: string): boolean => key === KEY.SPACE || key === KEY.ENTER;

type KeyboardHandler = (event: KeyboardEvent) => void;

const escKeyHandlers: KeyboardHandler[] = [];

document.addEventListener('keydown', event => {
if (event.key === 'Escape') {
escKeyHandlers.forEach(handler => handler(event));
}
});

export const onEscKey = (handler: KeyboardHandler) => escKeyHandlers.push(handler);

export const offEscKey = (handler: KeyboardHandler) => {
const index = escKeyHandlers.indexOf(handler);
if (index >= 0) {
escKeyHandlers.splice(index, 1);
}
};

export const handleKeyDown = (
event: React.KeyboardEvent<Element> | KeyboardEvent,
callback: (event?: React.KeyboardEvent<Element> | KeyboardEvent) => void,
event: ReactKeyboardEvent<Element> | KeyboardEvent,
callback: (event?: ReactKeyboardEvent<Element> | KeyboardEvent) => void,
) => {
if (event.key === KEY.ENTER || event.key === KEY.SPACE) {
callback(event);
}
return true;
};

export const handleEnterDown = (event: React.KeyboardEvent<HTMLElement> | KeyboardEvent, callback: () => void) => {
export const handleEnterDown = (event: ReactKeyboardEvent<HTMLElement> | KeyboardEvent, callback: () => void): void => {
if (event.key === KEY.ENTER) {
callback();
}
return true;
};

export const handleEscDown = (event: ReactKeyboardEvent<HTMLElement> | KeyboardEvent, callback: () => void): void => {
if (event.key === KEY.ESC) {
callback();
}
};

/**
Expand Down

0 comments on commit a01fa21

Please sign in to comment.