Skip to content

Commit 98fc29d

Browse files
authored
fix(entrykit): improve escape handling (#3623)
1 parent 55dae5f commit 98fc29d

3 files changed

Lines changed: 49 additions & 24 deletions

File tree

.changeset/kind-carpets-peel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@latticexyz/entrykit": patch
3+
---
4+
5+
Improved escape key handling when account modal is open. And fixed development warnings about missing dialog title/description.

packages/entrykit/src/AccountModal.tsx

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Modal } from "./ui/Modal";
22
import { useAccountModal } from "./useAccountModal";
33
import { twMerge } from "tailwind-merge";
44
import { AccountModalContent } from "./AccountModalContent";
5-
import { DialogClose, DialogTitle } from "@radix-ui/react-dialog";
5+
import { DialogClose } from "@radix-ui/react-dialog";
66
import { CloseIcon } from "./icons/CloseIcon";
77
import { Logo } from "./icons/Logo";
88
import { ErrorBoundary } from "react-error-boundary";
@@ -13,8 +13,6 @@ export function AccountModal() {
1313
const { accountModalOpen, toggleAccountModal } = useAccountModal();
1414
return (
1515
<Modal open={accountModalOpen} onOpenChange={toggleAccountModal}>
16-
{/* TODO: move this into `<Modal>` props? */}
17-
<DialogTitle className="sr-only">Connect with EntryKit</DialogTitle>
1816
{accountModalOpen ? (
1917
<div
2018
className={twMerge(
@@ -25,17 +23,6 @@ export function AccountModal() {
2523
"links:decoration-neutral-500 hover:links:decoration-orange-500",
2624
)}
2725
>
28-
<div className="absolute top-0 right-0">
29-
<DialogClose
30-
className={twMerge(
31-
"pointer-events-auto leading-none p-2 transition",
32-
"text-white/20 hover:text-white/40",
33-
)}
34-
title="Close"
35-
>
36-
<CloseIcon className="m-0" />
37-
</DialogClose>
38-
</div>
3926
<ErrorBoundary FallbackComponent={ErrorFallback}>
4027
<AccountModalContent />
4128
<ErrorsOverlay />
@@ -52,6 +39,18 @@ export function AccountModal() {
5239
</span>
5340
<span>Powered by MUD</span>
5441
</a>
42+
43+
<div className="absolute top-0 right-0">
44+
<DialogClose
45+
className={twMerge(
46+
"pointer-events-auto leading-none p-2 transition",
47+
"text-white/20 hover:text-white/40",
48+
)}
49+
title="Close"
50+
>
51+
<CloseIcon className="m-0" />
52+
</DialogClose>
53+
</div>
5554
</div>
5655
) : null}
5756
</Modal>

packages/entrykit/src/ui/Modal.tsx

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
import { ReactNode } from "react";
2-
import { Root as DialogRoot, DialogPortal, DialogContent } from "@radix-ui/react-dialog";
1+
import { ReactNode, useEffect } from "react";
2+
import {
3+
Root as DialogRoot,
4+
DialogPortal,
5+
DialogContent,
6+
DialogTitle,
7+
DialogDescription,
8+
} from "@radix-ui/react-dialog";
39
import { Shadow } from "./Shadow";
410
import { twMerge } from "tailwind-merge";
511

@@ -10,6 +16,25 @@ export type Props = {
1016
};
1117

1218
export function Modal({ open, onOpenChange, children }: Props) {
19+
// Focus trapping doesn't seem to completely work with our iframe approach,
20+
// so tabbing until you get to the document body means Escape doesn't work.
21+
// We'll patch this behavior for now with our own listener.
22+
useEffect(() => {
23+
function onKeyDown(event: KeyboardEvent) {
24+
if (event.defaultPrevented) return;
25+
26+
if (event.key === "Escape" && open) {
27+
event.preventDefault();
28+
onOpenChange?.(false);
29+
}
30+
}
31+
32+
window.addEventListener("keydown", onKeyDown);
33+
return () => {
34+
window.removeEventListener("keydown", onKeyDown);
35+
};
36+
}, [onOpenChange, open]);
37+
1338
return (
1439
<DialogRoot open={open} onOpenChange={onOpenChange}>
1540
{/* This intentionally does not use `<DialogTrigger>` because it doesn't play nicely with `<Shadow>` trigger (our primary use case). */}
@@ -28,14 +53,10 @@ export function Modal({ open, onOpenChange, children }: Props) {
2853
)}
2954
>
3055
<div>
31-
<DialogContent
32-
className="outline-none w-full max-w-[26rem] mx-auto"
33-
// TODO description
34-
aria-describedby={undefined}
35-
onOpenAutoFocus={(event) => {
36-
event.preventDefault();
37-
}}
38-
>
56+
<DialogContent className="outline-none w-full max-w-[26rem] mx-auto">
57+
<DialogTitle className="sr-only">EntryKit</DialogTitle>
58+
<DialogDescription className="sr-only">Sign in to this app</DialogDescription>
59+
3960
{children}
4061
</DialogContent>
4162
</div>

0 commit comments

Comments
 (0)