diff --git a/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-content-layout.tsx b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-content-layout.tsx new file mode 100644 index 000000000000..29cde6fb285c --- /dev/null +++ b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-content-layout.tsx @@ -0,0 +1,70 @@ +import type { DebugInfo } from '../../../../../shared/types' +import type { ErrorType } from '../../../errors/error-type-label/error-type-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 { EnvironmentNameLabel } from '../../../errors/environment-name-label/environment-name-label' +import { IssueFeedbackButton } from '../../../errors/error-overlay-toolbar/issue-feedback-button' +import { css } from '../../../../utils/css' + +// This behaves like the ErrorOverlayLayout. +export function IssuesTabContentLayout({ + error, + errorType, + message, + debugInfo, + children, + errorCode, + environmentName, +}: { + error: Error & { environmentName?: string } + errorType: ErrorType + message: string + debugInfo: DebugInfo + children: React.ReactNode + + errorCode?: string | null + environmentName?: string +}) { + return ( +
+
+
+ + + {environmentName && ( + + )} + + + } + /> +
+ +
+ {children} +
+ ) +} + +// The components in this file shares the style with the Error Overlay. +export const DEVTOOLS_PANEL_TAB_ISSUES_CONTENT_LAYOUT_STYLES = css` + [data-nextjs-devtools-panel-tab-issues-content-layout] { + flex: 1; + display: flex; + flex-direction: column; + overflow-y: auto; + min-height: 0; + padding: 14px; + } +` diff --git a/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-content.tsx b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-content.tsx index 283c47e0db78..9c8ab83a41f4 100644 --- a/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-content.tsx +++ b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-content.tsx @@ -1,19 +1,7 @@ import type { OverlayState } from '../../../../shared' -import type { DebugInfo } from '../../../../../shared/types' import type { ReadyRuntimeError } from '../../../../utils/get-error-by-type' -import type { ErrorType } from '../../../errors/error-type-label/error-type-label' import { Suspense, useMemo, useState } from 'react' - -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 { IssueFeedbackButton } from '../../../errors/error-overlay-toolbar/issue-feedback-button' import { Terminal } from '../../../terminal' import { HotlinkedText } from '../../../hot-linked-text' import { PseudoHtmlDiff } from '../../../../container/runtime-error/component-stack-pseudo-html' @@ -23,16 +11,22 @@ import { CallStack } from '../../../call-stack/call-stack' import { NEXTJS_HYDRATION_ERROR_LINK } from '../../../../../shared/react-19-hydration-error' import { ErrorContentSkeleton } from '../../../../container/runtime-error/error-content-skeleton' import { css } from '../../../../utils/css' +import { getErrorTextFromBuildErrorMessage } from '../../../../container/build-error' +import { IssuesTabContentLayout } from './issues-tab-content-layout' +import type { DebugInfo } from '../../../../../shared/types' +import type { ErrorType } from '../../../errors/error-type-label/error-type-label' +import { IssuesTabEmptyContent } from './issues-tab-empty-content' +// This consists of the Build Error, Runtime Error, etc. export function IssuesTabContent({ notes, buildError, hydrationWarning, errorDetails, activeError, - errorCode, errorType, debugInfo, + errorCode, }: { notes: string | null buildError: OverlayState['buildError'] @@ -41,49 +35,63 @@ export function IssuesTabContent({ hydrationWarning: string | null notes: string | null reactOutputComponentDiff: string | null - } - activeError: ReadyRuntimeError - errorCode: string | undefined - errorType: ErrorType + } | null + activeError: ReadyRuntimeError | null + errorType: ErrorType | null debugInfo: DebugInfo + errorCode: string | null | undefined }) { if (buildError) { - return + return } - const errorMessage = hydrationWarning ? ( - - ) : ( - + return ( + ) +} + +function ErrorContent({ + notes, + hydrationWarning, + errorDetails, + activeError, + errorType, + debugInfo, + errorCode, +}: { + notes: string | null + hydrationWarning: string | null + errorDetails: { + hydrationWarning: string | null + notes: string | null + reactOutputComponentDiff: string | null + } | null + activeError: ReadyRuntimeError | null + errorType: ErrorType | null + debugInfo: DebugInfo + errorCode: string | null | undefined +}) { + if (!activeError || !errorType) { + return + } return ( -
-
-
- - - {activeError.error.environmentName && ( - - )} - - - } - /> -
- -
+
{notes ? ( <> @@ -106,7 +114,7 @@ export function IssuesTabContent({

) : null}
- {errorDetails.reactOutputComponentDiff ? ( + {errorDetails?.reactOutputComponentDiff ? ( @@ -114,7 +122,7 @@ export function IssuesTabContent({ }> -
+ ) } @@ -161,14 +169,29 @@ function RuntimeError({ error }: { error: ReadyRuntimeError }) { ) } +function BuildError({ + message, + debugInfo, +}: { + message: string + debugInfo: DebugInfo +}) { + const error = new Error(message) + const formattedMessage = useMemo( + () => getErrorTextFromBuildErrorMessage(message) || 'Failed to compile', + [message] + ) + return ( + + + + ) +} + // The components in this file shares the style with the Error Overlay. -export const DEVTOOLS_PANEL_TAB_ISSUES_CONTENT_STYLES = css` - [data-nextjs-devtools-panel-tab-issues-content-container] { - flex: 1; - display: flex; - flex-direction: column; - overflow-y: auto; - min-height: 0; - padding: 14px; - } -` +export const DEVTOOLS_PANEL_TAB_ISSUES_CONTENT_STYLES = css`` diff --git a/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-empty-content.tsx b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-empty-content.tsx new file mode 100644 index 000000000000..89643c7e4d92 --- /dev/null +++ b/packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-empty-content.tsx @@ -0,0 +1,64 @@ +import { Warning } from '../../../../icons/warning' +import { css } from '../../../../utils/css' + +export function IssuesTabEmptyContent() { + return ( +
+
+
+ +
+

+ No Issues Found +

+

+ Issues will appear here when they occur. +

+
+
+ ) +} + +export const DEVTOOLS_PANEL_TAB_ISSUES_EMPTY_CONTENT_STYLES = css` + [data-nextjs-devtools-panel-tab-issues-empty] { + display: flex; + flex: 1; + padding: 12px; + min-height: 0; + } + + [data-nextjs-devtools-panel-tab-issues-empty-content] { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + flex: 1; + border: 1px dashed var(--color-gray-alpha-500); + border-radius: 4px; + } + + [data-nextjs-devtools-panel-tab-issues-empty-icon] { + margin-bottom: 16px; + padding: 8px; + border: 1px solid var(--color-gray-alpha-400); + border-radius: 6px; + + background-color: var(--color-background-100); + display: flex; + align-items: center; + justify-content: center; + } + + [data-nextjs-devtools-panel-tab-issues-empty-title] { + color: var(--color-gray-1000); + font-size: 16px; + font-weight: 500; + line-height: var(--line-height-20); + } + + [data-nextjs-devtools-panel-tab-issues-empty-subtitle] { + color: var(--color-gray-900); + font-size: 14px; + line-height: var(--line-height-21); + } +` 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 index bebfc262bf58..e701ac0dae14 100644 --- 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 @@ -7,7 +7,6 @@ import { IssuesTabSidebar } from './issues-tab-sidebar' import { IssuesTabContent } from './issues-tab-content' import { css } from '../../../../utils/css' import { useActiveRuntimeError } from '../../../../hooks/use-active-runtime-error' -import { Warning } from '../../../../icons/warning' export function IssuesTab({ debugInfo, @@ -31,43 +30,26 @@ export function IssuesTab({ errorDetails, } = useActiveRuntimeError({ runtimeErrors, getSquashedHydrationErrorDetails }) - if (!activeError) { - return ( -
-
-
- -
-

- No Issues Found -

-

- Issues will appear here when they occur. -

-
-
- ) - } - return (
- + {buildError ? null : ( + + )} - {/* This is the copy of the Error Overlay content. */}
) @@ -79,46 +61,4 @@ export const DEVTOOLS_PANEL_TAB_ISSUES_STYLES = css` flex: 1; min-height: 0; } - - [data-nextjs-devtools-panel-tab-issues-empty] { - display: flex; - flex: 1; - padding: 12px; - min-height: 0; - } - - [data-nextjs-devtools-panel-tab-issues-empty-content] { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - flex: 1; - border: 1px dashed var(--color-gray-alpha-500); - border-radius: 4px; - } - - [data-nextjs-devtools-panel-tab-issues-empty-icon] { - margin-bottom: 16px; - padding: 8px; - border: 1px solid var(--color-gray-alpha-400); - border-radius: 6px; - - background-color: var(--color-background-100); - display: flex; - align-items: center; - justify-content: center; - } - - [data-nextjs-devtools-panel-tab-issues-empty-title] { - color: var(--color-gray-1000); - font-size: 16px; - font-weight: 500; - line-height: var(--line-height-20); - } - - [data-nextjs-devtools-panel-tab-issues-empty-subtitle] { - color: var(--color-gray-900); - font-size: 14px; - line-height: var(--line-height-21); - } ` diff --git a/packages/next/src/next-devtools/dev-overlay/container/build-error.tsx b/packages/next/src/next-devtools/dev-overlay/container/build-error.tsx index ad3eff8b70be..1261955262f8 100644 --- a/packages/next/src/next-devtools/dev-overlay/container/build-error.tsx +++ b/packages/next/src/next-devtools/dev-overlay/container/build-error.tsx @@ -8,7 +8,7 @@ export interface BuildErrorProps extends ErrorBaseProps { message: string } -const getErrorTextFromBuildErrorMessage = (multiLineMessage: string) => { +export const getErrorTextFromBuildErrorMessage = (multiLineMessage: string) => { const lines = multiLineMessage.split('\n') // The multi-line build error message looks like: // :: 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 5e3362cbc328..3d5f19b2267f 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 @@ -37,6 +37,8 @@ import { ISSUE_FEEDBACK_BUTTON_STYLES } from '../components/errors/error-overlay import { ERROR_CONTENT_SKELETON_STYLES } from '../container/runtime-error/error-content-skeleton' import { SEGMENTS_EXPLORER_TAB_STYLES } from '../components/devtools-panel/devtools-panel-tab/segments-explorer-tab' import { SEGMENTS_EXPLORER_STYLES } from '../components/errors/dev-tools-indicator/dev-tools-info/segments-explorer' +import { DEVTOOLS_PANEL_TAB_ISSUES_CONTENT_LAYOUT_STYLES } from '../components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-content-layout' +import { DEVTOOLS_PANEL_TAB_ISSUES_EMPTY_CONTENT_STYLES } from '../components/devtools-panel/devtools-panel-tab/issues-tab/issues-tab-empty-content' export function ComponentStyles() { return ( @@ -74,9 +76,11 @@ export function ComponentStyles() { ${DEVTOOLS_PANEL_VERSION_INFO_STYLES} ${DEVTOOLS_PANEL_TAB_SETTINGS_STYLES} ${DEVTOOLS_PANEL_TAB_ISSUES_STYLES} + ${DEVTOOLS_PANEL_TAB_ISSUES_EMPTY_CONTENT_STYLES} ${DEVTOOLS_PANEL_TAB_ISSUES_SIDEBAR_STYLES} ${DEVTOOLS_PANEL_TAB_ISSUES_SIDEBAR_FRAME_SKELETON_STYLES} ${DEVTOOLS_PANEL_TAB_ISSUES_CONTENT_STYLES} + ${DEVTOOLS_PANEL_TAB_ISSUES_CONTENT_LAYOUT_STYLES} ${ISSUE_FEEDBACK_BUTTON_STYLES} ${ERROR_CONTENT_SKELETON_STYLES} ${SEGMENTS_EXPLORER_TAB_STYLES}