fix: focus on last active element when dialog closes (#3447)

* fix: focus on last active element when dialog closes

* useState instead of ref
This commit is contained in:
Aakansha Doshi 2021-04-15 20:29:00 +05:30 committed by GitHub
parent 793b69e592
commit c0047269c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 15 additions and 14 deletions

View File

@ -1,5 +1,5 @@
import clsx from "clsx"; import clsx from "clsx";
import React, { useEffect } from "react"; import React, { useEffect, useState } from "react";
import { useCallbackRefState } from "../hooks/useCallbackRefState"; import { useCallbackRefState } from "../hooks/useCallbackRefState";
import { t } from "../i18n"; import { t } from "../i18n";
import { useIsMobile } from "../components/App"; import { useIsMobile } from "../components/App";
@ -20,6 +20,8 @@ export const Dialog = (props: {
theme?: AppState["theme"]; theme?: AppState["theme"];
}) => { }) => {
const [islandNode, setIslandNode] = useCallbackRefState<HTMLDivElement>(); const [islandNode, setIslandNode] = useCallbackRefState<HTMLDivElement>();
const [lastActiveElement] = useState(document.activeElement);
useEffect(() => { useEffect(() => {
if (!islandNode) { if (!islandNode) {
return; return;
@ -66,12 +68,17 @@ export const Dialog = (props: {
return focusableElements ? Array.from(focusableElements) : []; return focusableElements ? Array.from(focusableElements) : [];
}; };
const onClose = () => {
(lastActiveElement as HTMLElement).focus();
props.onCloseRequest();
};
return ( return (
<Modal <Modal
className={clsx("Dialog", props.className)} className={clsx("Dialog", props.className)}
labelledBy="dialog-title" labelledBy="dialog-title"
maxWidth={props.small ? 550 : 800} maxWidth={props.small ? 550 : 800}
onCloseRequest={props.onCloseRequest} onCloseRequest={onClose}
theme={props.theme} theme={props.theme}
> >
<Island ref={setIslandNode}> <Island ref={setIslandNode}>
@ -79,7 +86,7 @@ export const Dialog = (props: {
<span className="Dialog__titleContent">{props.title}</span> <span className="Dialog__titleContent">{props.title}</span>
<button <button
className="Modal__close" className="Modal__close"
onClick={props.onCloseRequest} onClick={onClose}
aria-label={t("buttons.close")} aria-label={t("buttons.close")}
> >
{useIsMobile() ? back : close} {useIsMobile() ? back : close}

View File

@ -2,6 +2,7 @@ import React, { useState } from "react";
import { t } from "../i18n"; import { t } from "../i18n";
import { Dialog } from "./Dialog"; import { Dialog } from "./Dialog";
import { useExcalidrawContainer } from "./App";
export const ErrorDialog = ({ export const ErrorDialog = ({
message, message,
@ -11,6 +12,7 @@ export const ErrorDialog = ({
onClose?: () => void; onClose?: () => void;
}) => { }) => {
const [modalIsShown, setModalIsShown] = useState(!!message); const [modalIsShown, setModalIsShown] = useState(!!message);
const excalidrawContainer = useExcalidrawContainer();
const handleClose = React.useCallback(() => { const handleClose = React.useCallback(() => {
setModalIsShown(false); setModalIsShown(false);
@ -18,8 +20,9 @@ export const ErrorDialog = ({
if (onClose) { if (onClose) {
onClose(); onClose();
} }
document.querySelector<HTMLElement>(".excalidraw-container")?.focus(); // TODO: Fix the A11y issues so this is never needed since we should always focus on last active element
}, [onClose]); excalidrawContainer?.focus();
}, [onClose, excalidrawContainer]);
return ( return (
<> <>

View File

@ -244,11 +244,9 @@ export const ExportDialog = ({
onExportToBackend?: ExportCB; onExportToBackend?: ExportCB;
}) => { }) => {
const [modalIsShown, setModalIsShown] = useState(false); const [modalIsShown, setModalIsShown] = useState(false);
const triggerButton = useRef<HTMLButtonElement>(null);
const handleClose = React.useCallback(() => { const handleClose = React.useCallback(() => {
setModalIsShown(false); setModalIsShown(false);
triggerButton.current?.focus();
}, []); }, []);
return ( return (
@ -263,7 +261,6 @@ export const ExportDialog = ({
aria-label={t("buttons.export")} aria-label={t("buttons.export")}
showAriaLabel={useIsMobile()} showAriaLabel={useIsMobile()}
title={t("buttons.export")} title={t("buttons.export")}
ref={triggerButton}
/> />
{modalIsShown && ( {modalIsShown && (
<Dialog onCloseRequest={handleClose} title={t("buttons.export")}> <Dialog onCloseRequest={handleClose} title={t("buttons.export")}>

View File

@ -671,10 +671,6 @@ const LayerUI = ({
{appState.showHelpDialog && ( {appState.showHelpDialog && (
<HelpDialog <HelpDialog
onClose={() => { onClose={() => {
const helpIcon = document.querySelector(
".help-icon",
)! as HTMLElement;
helpIcon.focus();
setAppState({ showHelpDialog: false }); setAppState({ showHelpDialog: false });
}} }}
/> />

View File

@ -591,8 +591,6 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
handleClose = () => { handleClose = () => {
this.setState({ modalIsShown: false }); this.setState({ modalIsShown: false });
const collabIcon = document.querySelector(".CollabButton") as HTMLElement;
collabIcon.focus();
}; };
onUsernameChange = (username: string) => { onUsernameChange = (username: string) => {