From 9d73e070548c713717b3b56e2894196dbb1a5847 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Fri, 19 Jan 2024 14:06:55 +0100 Subject: [PATCH] Error overlay refactors --- .../internal/components/Dialog/Dialog.tsx | 7 ++- .../LeftRightDialogHeader.tsx | 7 ++- .../internal/components/Toast/Toast.tsx | 11 +++- .../internal/container/Errors.tsx | 56 +++++++------------ .../internal/helpers/stack-frame.ts | 2 +- .../src/internal/helpers/stack-frame.ts | 2 +- 6 files changed, 41 insertions(+), 44 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/Dialog.tsx b/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/Dialog.tsx index 65d2bc7460e9f..39c8005d16622 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/Dialog.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/Dialog.tsx @@ -6,7 +6,7 @@ export type DialogProps = { type: 'error' | 'warning' 'aria-labelledby': string 'aria-describedby': string - onClose?: (e: MouseEvent | TouchEvent) => void + onClose?: () => void } const Dialog: React.FC = function Dialog({ @@ -24,7 +24,10 @@ const Dialog: React.FC = function Dialog({ const onDialog = React.useCallback((node: HTMLDivElement | null) => { setDialog(node) }, []) - useOnClickOutside(dialog, onClose) + useOnClickOutside(dialog, (e) => { + e.preventDefault() + return onClose?.() + }) // Make HTMLElements with `role=link` accessible to be triggered by the // keyboard, i.e. [Enter]. diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/LeftRightDialogHeader/LeftRightDialogHeader.tsx b/packages/next/src/client/components/react-dev-overlay/internal/components/LeftRightDialogHeader/LeftRightDialogHeader.tsx index 337028dc5cfc7..78199c9a5e111 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/components/LeftRightDialogHeader/LeftRightDialogHeader.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/components/LeftRightDialogHeader/LeftRightDialogHeader.tsx @@ -36,18 +36,21 @@ const LeftRightDialogHeader: React.FC = function handler(e: KeyboardEvent) { if (e.key === 'ArrowLeft') { + e.preventDefault() e.stopPropagation() if (buttonLeft.current) { buttonLeft.current.focus() } previous && previous() } else if (e.key === 'ArrowRight') { + e.preventDefault() e.stopPropagation() if (buttonRight.current) { buttonRight.current.focus() } next && next() } else if (e.key === 'Escape') { + e.preventDefault() e.stopPropagation() if (root instanceof ShadowRoot) { const a = root.activeElement @@ -57,9 +60,7 @@ const LeftRightDialogHeader: React.FC = } } - if (close) { - close() - } + close?.() } } diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/Toast/Toast.tsx b/packages/next/src/client/components/react-dev-overlay/internal/components/Toast/Toast.tsx index a0594d8af2f33..b29664842acff 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/components/Toast/Toast.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/components/Toast/Toast.tsx @@ -2,7 +2,7 @@ import * as React from 'react' export type ToastProps = { children?: React.ReactNode - onClick?: (ev: React.MouseEvent) => void + onClick?: () => void className?: string } @@ -12,7 +12,14 @@ export const Toast: React.FC = function Toast({ className, }) { return ( -
+
{ + e.preventDefault() + return onClick?.() + }} + className={className} + >
{children}
) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx index 3a799356d28e1..24286e3496051 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx @@ -1,4 +1,4 @@ -import * as React from 'react' +import { useState, useEffect, useMemo, useCallback } from 'react' import { ACTION_UNHANDLED_ERROR, ACTION_UNHANDLED_REJECTION, @@ -56,16 +56,16 @@ function getErrorSignature(ev: SupportedErrorEvent): string { return '' } -export const Errors: React.FC = function Errors({ +export function Errors({ errors, initialDisplayState, versionInfo, -}) { - const [lookups, setLookups] = React.useState( +}: ErrorsProps) { + const [lookups, setLookups] = useState( {} as { [eventId: string]: ReadyErrorEvent } ) - const [readyErrors, nextError] = React.useMemo< + const [readyErrors, nextError] = useMemo< [ReadyErrorEvent[], SupportedErrorEvent | null] >(() => { let ready: ReadyErrorEvent[] = [] @@ -95,11 +95,11 @@ export const Errors: React.FC = function Errors({ return [ready, next] }, [errors, lookups]) - const isLoading = React.useMemo(() => { + const isLoading = useMemo(() => { return readyErrors.length < 1 && Boolean(errors.length) }, [errors.length, readyErrors.length]) - React.useEffect(() => { + useEffect(() => { if (nextError == null) { return } @@ -125,30 +125,28 @@ export const Errors: React.FC = function Errors({ }, [nextError]) const [displayState, setDisplayState] = - React.useState(initialDisplayState) - const [activeIdx, setActiveIndex] = React.useState(0) - const previous = React.useCallback((e?: MouseEvent | TouchEvent) => { - e?.preventDefault() - setActiveIndex((v) => Math.max(0, v - 1)) - }, []) - const next = React.useCallback( - (e?: MouseEvent | TouchEvent) => { - e?.preventDefault() + useState(initialDisplayState) + const [activeIdx, setActiveIndex] = useState(0) + const previous = useCallback( + () => setActiveIndex((v) => Math.max(0, v - 1)), + [] + ) + const next = useCallback( + () => setActiveIndex((v) => Math.max(0, Math.min(readyErrors.length - 1, v + 1)) - ) - }, + ), [readyErrors.length] ) - const activeError = React.useMemo( + const activeError = useMemo( () => readyErrors[activeIdx] ?? null, [activeIdx, readyErrors] ) // Reset component state when there are no errors to be displayed. // This should never happen, but lets handle it. - React.useEffect(() => { + useEffect(() => { if (errors.length < 1) { setLookups({}) setDisplayState('hidden') @@ -156,21 +154,9 @@ export const Errors: React.FC = function Errors({ } }, [errors.length]) - const minimize = React.useCallback((e?: MouseEvent | TouchEvent) => { - e?.preventDefault() - setDisplayState('minimized') - }, []) - const hide = React.useCallback((e?: MouseEvent | TouchEvent) => { - e?.preventDefault() - setDisplayState('hidden') - }, []) - const fullscreen = React.useCallback( - (e?: React.MouseEvent) => { - e?.preventDefault() - setDisplayState('fullscreen') - }, - [] - ) + const minimize = useCallback(() => setDisplayState('minimized'), []) + const hide = useCallback(() => setDisplayState('hidden'), []) + const fullscreen = useCallback(() => setDisplayState('fullscreen'), []) // This component shouldn't be rendered with no errors, but if it is, let's // handle it gracefully by rendering nothing. diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts index ded4de3f0bde3..71239f916f34c 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts @@ -32,7 +32,7 @@ export type OriginalStackFrame = sourcePackage?: string } -export function getOriginalStackFrame( +function getOriginalStackFrame( source: StackFrame, type: 'server' | 'edge-server' | null, errorMessage: string diff --git a/packages/react-dev-overlay/src/internal/helpers/stack-frame.ts b/packages/react-dev-overlay/src/internal/helpers/stack-frame.ts index 1f28c4b3fe29d..94e50933a85a1 100644 --- a/packages/react-dev-overlay/src/internal/helpers/stack-frame.ts +++ b/packages/react-dev-overlay/src/internal/helpers/stack-frame.ts @@ -30,7 +30,7 @@ export type OriginalStackFrame = originalCodeFrame: null } -export function getOriginalStackFrame( +function getOriginalStackFrame( source: StackFrame, type: 'server' | 'edge-server' | null, errorMessage: string