From 9b32998f9a2496d994f7b3a78b03242d561adf1e Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Fri, 20 Jun 2025 17:05:33 +0200 Subject: [PATCH 1/2] [devtools] set up panel ui issues tab infra --- .../devtools-panel-tab/devtools-panel-tab.tsx | 18 ++++- .../issues-tab/issues-tab.tsx | 81 +++++++++++++++++++ .../devtools-panel/devtools-panel.tsx | 16 ++++ .../components/dialog/dialog-header.tsx | 18 +---- .../dev-overlay/container/errors.tsx | 4 +- .../next-devtools/dev-overlay/dev-overlay.tsx | 4 + .../dev-overlay/styles/component-styles.tsx | 2 + 7 files changed, 126 insertions(+), 17 deletions(-) create mode 100644 packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab.tsx diff --git a/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/devtools-panel-tab.tsx b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/devtools-panel-tab.tsx index 580c8f67abb..7b3c379027b 100644 --- a/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/devtools-panel-tab.tsx +++ b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/devtools-panel-tab.tsx @@ -1,7 +1,11 @@ import type { DevToolsPanelTabType } from '../devtools-panel' import type { Corners } from '../../../shared' +import type { DebugInfo } from '../../../../shared/types' +import type { ReadyRuntimeError } from '../../../utils/get-error-by-type' +import type { HydrationErrorState } from '../../../../shared/hydration-error' import { SettingsTab } from './settings-tab' +import { IssuesTab } from './issues-tab/issues-tab' export function DevToolsPanelTab({ activeTab, @@ -9,12 +13,18 @@ export function DevToolsPanelTab({ scale, handlePositionChange, handleScaleChange, + debugInfo, + runtimeErrors, + getSquashedHydrationErrorDetails, }: { activeTab: DevToolsPanelTabType devToolsPosition: Corners scale: number handlePositionChange: (e: React.ChangeEvent) => void handleScaleChange: (e: React.ChangeEvent) => void + debugInfo: DebugInfo + runtimeErrors: ReadyRuntimeError[] + getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null }) { switch (activeTab) { case 'settings': @@ -29,7 +39,13 @@ export function DevToolsPanelTab({ case 'route': return
Route
case 'issues': - return
Issues
+ return ( + + ) default: return null } diff --git a/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab.tsx b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab.tsx new file mode 100644 index 00000000000..1500394d0ed --- /dev/null +++ b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab.tsx @@ -0,0 +1,81 @@ +import type { DebugInfo } from '../../../../../shared/types' +import type { ReadyRuntimeError } from '../../../../utils/get-error-by-type' +import type { HydrationErrorState } from '../../../../../shared/hydration-error' + +import { + GenericErrorDescription, + HydrationErrorDescription, +} from '../../../../container/errors' +import { EnvironmentNameLabel } from '../../../errors/environment-name-label/environment-name-label' +import { ErrorMessage } from '../../../errors/error-message/error-message' +import { ErrorOverlayToolbar } from '../../../errors/error-overlay-toolbar/error-overlay-toolbar' +import { ErrorTypeLabel } from '../../../errors/error-type-label/error-type-label' +import { css } from '../../../../utils/css' +import { useActiveRuntimeError } from '../../../../hooks/use-active-runtime-error' + +export function IssuesTab({ + debugInfo, + runtimeErrors, + getSquashedHydrationErrorDetails, +}: { + debugInfo: DebugInfo + runtimeErrors: ReadyRuntimeError[] + getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null +}) { + const { isLoading, errorCode, errorType, hydrationWarning, activeError } = + useActiveRuntimeError({ runtimeErrors, getSquashedHydrationErrorDetails }) + + if (isLoading) { + // TODO: better loading state + return null + } + + if (!activeError) { + return null + } + + const errorMessage = hydrationWarning ? ( + + ) : ( + + ) + + return ( +
+ {/* TODO: Sidebar */} + +
+
+
+ + + {activeError.error.environmentName && ( + + )} + + +
+ +
+ + {/* TODO: Content */} +
Content
+
+
+ ) +} + +export const DEVTOOLS_PANEL_TAB_ISSUES_STYLES = css` + [data-nextjs-devtools-panel-tab-issues] { + display: flex; + } +` diff --git a/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.tsx b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.tsx index 0ce10c1b378..3947ffde198 100644 --- a/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.tsx +++ b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.tsx @@ -1,4 +1,6 @@ import type { OverlayDispatch, OverlayState, Corners } from '../../shared' +import type { ReadyRuntimeError } from '../../utils/get-error-by-type' +import type { HydrationErrorState } from '../../../shared/hydration-error' import { useState } from 'react' @@ -26,10 +28,14 @@ export function DevToolsPanel({ state, dispatch, issueCount, + runtimeErrors, + getSquashedHydrationErrorDetails, }: { state: OverlayState dispatch: OverlayDispatch issueCount: number + runtimeErrors: ReadyRuntimeError[] + getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null }) { const [activeTab, setActiveTab] = useState<'issues' | 'route' | 'settings'>( 'settings' @@ -144,6 +150,11 @@ export function DevToolsPanel({ scale={state.scale} handlePositionChange={handlePositionChange} handleScaleChange={handleScaleChange} + debugInfo={state.debugInfo} + runtimeErrors={runtimeErrors} + getSquashedHydrationErrorDetails={ + getSquashedHydrationErrorDetails + } /> @@ -156,6 +167,11 @@ export function DevToolsPanel({ } export const DEVTOOLS_PANEL_STYLES = css` + /* TODO: Better override dialog header style. This conflicts with issues tab content. */ + [data-nextjs-devtools-panel-dialog-header] { + margin-bottom: 0 !important; + } + [data-nextjs-devtools-panel-overlay] { padding: initial; margin: auto; diff --git a/packages/next/src/next-devtools/dev-overlay/components/dialog/dialog-header.tsx b/packages/next/src/next-devtools/dev-overlay/components/dialog/dialog-header.tsx index c53a613cf9e..1bb681557a4 100644 --- a/packages/next/src/next-devtools/dev-overlay/components/dialog/dialog-header.tsx +++ b/packages/next/src/next-devtools/dev-overlay/components/dialog/dialog-header.tsx @@ -1,19 +1,9 @@ -import * as React from 'react' +export type DialogHeaderProps = React.HTMLAttributes -export type DialogHeaderProps = { - children?: React.ReactNode - className?: string -} - -const DialogHeader: React.FC = function DialogHeader({ - children, - className, -}) { +export function DialogHeader(props: DialogHeaderProps) { return ( -
- {children} +
+ {props.children}
) } - -export { DialogHeader } diff --git a/packages/next/src/next-devtools/dev-overlay/container/errors.tsx b/packages/next/src/next-devtools/dev-overlay/container/errors.tsx index c9674c3efe1..556d225e4fa 100644 --- a/packages/next/src/next-devtools/dev-overlay/container/errors.tsx +++ b/packages/next/src/next-devtools/dev-overlay/container/errors.tsx @@ -30,11 +30,11 @@ function isNextjsLink(text: string): boolean { return text.startsWith('https://nextjs.org') } -function HydrationErrorDescription({ message }: { message: string }) { +export function HydrationErrorDescription({ message }: { message: string }) { return } -function GenericErrorDescription({ error }: { error: Error }) { +export function GenericErrorDescription({ error }: { error: Error }) { const environmentName = 'environmentName' in error ? error.environmentName : '' const envPrefix = environmentName ? `[ ${environmentName} ] ` : '' diff --git a/packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx b/packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx index e1bbed1ba0d..8c3d52226cb 100644 --- a/packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx +++ b/packages/next/src/next-devtools/dev-overlay/dev-overlay.tsx @@ -54,6 +54,10 @@ export function DevOverlay({ state={state} dispatch={dispatch} issueCount={totalErrorCount} + runtimeErrors={runtimeErrors} + getSquashedHydrationErrorDetails={ + getSquashedHydrationErrorDetails + } /> )} diff --git a/packages/next/src/next-devtools/dev-overlay/styles/component-styles.tsx b/packages/next/src/next-devtools/dev-overlay/styles/component-styles.tsx index 1c262fe33e4..aad9a7e5fe2 100644 --- a/packages/next/src/next-devtools/dev-overlay/styles/component-styles.tsx +++ b/packages/next/src/next-devtools/dev-overlay/styles/component-styles.tsx @@ -29,6 +29,7 @@ import { DEVTOOLS_PANEL_FOOTER_STYLES } from '../components/devtools-panel/devto import { DEVTOOLS_PANEL_VERSION_INFO_STYLES } from '../components/devtools-panel/devtools-panel-version-info' import { DEVTOOLS_PANEL_TAB_SETTINGS_STYLES } from '../components/devtools-panel/devtools-panel-tab/settings-tab' import { CALL_STACK_STYLES } from '../components/call-stack/call-stack' +import { DEVTOOLS_PANEL_TAB_ISSUES_STYLES } from '../components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab' export function ComponentStyles() { return ( @@ -64,6 +65,7 @@ export function ComponentStyles() { ${DEVTOOLS_PANEL_FOOTER_STYLES} ${DEVTOOLS_PANEL_VERSION_INFO_STYLES} ${DEVTOOLS_PANEL_TAB_SETTINGS_STYLES} + ${DEVTOOLS_PANEL_TAB_ISSUES_STYLES} `} ) From 61b1b95973c4367676fbe1ea6a2f995331b7026d Mon Sep 17 00:00:00 2001 From: devjiwonchoi Date: Fri, 20 Jun 2025 18:55:04 +0200 Subject: [PATCH 2/2] sync story for issue tab --- .../components/devtools-panel/devtools-panel.stories.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.stories.tsx b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.stories.tsx index 9578637b3b7..a1219170d06 100644 --- a/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.stories.tsx +++ b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.stories.tsx @@ -4,6 +4,7 @@ import type { OverlayState } from '../../shared' import { DevToolsPanel } from './devtools-panel' import { INITIAL_OVERLAY_STATE } from '../../shared' import { withShadowPortal } from '../../storybook/with-shadow-portal' +import { runtimeErrors } from '../../storybook/errors' const meta: Meta = { component: DevToolsPanel, @@ -34,6 +35,8 @@ export const Default: Story = { state, dispatch: () => {}, issueCount: 0, + runtimeErrors: [], + getSquashedHydrationErrorDetails: () => null, }, } @@ -42,6 +45,8 @@ export const WithIssues: Story = { state, dispatch: () => {}, issueCount: 3, + runtimeErrors, + getSquashedHydrationErrorDetails: () => null, }, } @@ -58,6 +63,8 @@ export const Turbopack: Story = { state, dispatch: () => {}, issueCount: 0, + runtimeErrors: [], + getSquashedHydrationErrorDetails: () => null, }, } @@ -74,5 +81,7 @@ export const Rspack: Story = { state, dispatch: () => {}, issueCount: 0, + runtimeErrors: [], + getSquashedHydrationErrorDetails: () => null, }, }