fix dev overlay pseudo html collapsing (#62728)
Dev overlay should show the full stack trace after clicking the uncollase button to display the full component stack Closes NEXT-2658
This commit is contained in:
parent
8034042215
commit
4d4b45ec2c
6 changed files with 180 additions and 31 deletions
|
@ -2,9 +2,6 @@ import { useMemo, Fragment, useState } from 'react'
|
|||
import type { ComponentStackFrame } from '../../helpers/parse-component-stack'
|
||||
import { CollapseIcon } from '../../icons/CollapseIcon'
|
||||
|
||||
// In total it will display 6 rows.
|
||||
const MAX_NON_COLLAPSED_FRAMES = 6
|
||||
|
||||
/**
|
||||
*
|
||||
* Format component stack into pseudo HTML
|
||||
|
@ -51,6 +48,8 @@ export function PseudoHtmlDiff({
|
|||
hydrationMismatchType: 'tag' | 'text'
|
||||
} & React.HTMLAttributes<HTMLPreElement>) {
|
||||
const isHtmlTagsWarning = hydrationMismatchType === 'tag'
|
||||
// For text mismatch, mismatched text will take 2 rows, so we display 4 rows of component stack
|
||||
const MAX_NON_COLLAPSED_FRAMES = isHtmlTagsWarning ? 6 : 4
|
||||
const shouldCollapse = componentStackFrames.length > MAX_NON_COLLAPSED_FRAMES
|
||||
const [isHtmlCollapsed, toggleCollapseHtml] = useState(shouldCollapse)
|
||||
|
||||
|
@ -77,16 +76,10 @@ export function PseudoHtmlDiff({
|
|||
const isLastFewFrames =
|
||||
!isHtmlTagsWarning && index >= componentList.length - 6
|
||||
|
||||
if (
|
||||
nestedHtmlStack.length >= MAX_NON_COLLAPSED_FRAMES &&
|
||||
isHtmlCollapsed
|
||||
) {
|
||||
return
|
||||
}
|
||||
if ((isHtmlTagsWarning && isRelatedTag) || isLastFewFrames) {
|
||||
const codeLine = (
|
||||
<span>
|
||||
<span>{spaces}</span>
|
||||
{spaces}
|
||||
<span
|
||||
{...(isHighlightedTag
|
||||
? {
|
||||
|
@ -112,7 +105,14 @@ export function PseudoHtmlDiff({
|
|||
)
|
||||
nestedHtmlStack.push(wrappedCodeLine)
|
||||
} else {
|
||||
if ((isHtmlTagsWarning && !isHtmlCollapsed) || isLastFewFrames) {
|
||||
if (
|
||||
nestedHtmlStack.length >= MAX_NON_COLLAPSED_FRAMES &&
|
||||
isHtmlCollapsed
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!isHtmlCollapsed || isLastFewFrames) {
|
||||
nestedHtmlStack.push(
|
||||
<span key={nestedHtmlStack.length}>
|
||||
{spaces}
|
||||
|
@ -154,6 +154,7 @@ export function PseudoHtmlDiff({
|
|||
serverContent,
|
||||
isHtmlTagsWarning,
|
||||
hydrationMismatchType,
|
||||
MAX_NON_COLLAPSED_FRAMES,
|
||||
])
|
||||
|
||||
return (
|
||||
|
|
|
@ -27,20 +27,47 @@ describe('Component Stack in error overlay', () => {
|
|||
<main>
|
||||
<Component>
|
||||
<div>
|
||||
"server"
|
||||
"client""
|
||||
<p>
|
||||
"server"
|
||||
"client""
|
||||
`)
|
||||
|
||||
await session.toggleComponentStack()
|
||||
await session.toggleCollapseComponentStack()
|
||||
expect(await session.getRedboxComponentStack()).toMatchInlineSnapshot(`
|
||||
"<InnerLayoutRouter>
|
||||
<Mismatch>
|
||||
<main>
|
||||
<Component>
|
||||
<div>
|
||||
<p>
|
||||
"server"
|
||||
"client""
|
||||
"<Root>
|
||||
<ServerRoot>
|
||||
<AppRouter>
|
||||
<ErrorBoundary>
|
||||
<ErrorBoundaryHandler>
|
||||
<Router>
|
||||
<HotReload>
|
||||
<ReactDevOverlay>
|
||||
<DevRootNotFoundBoundary>
|
||||
<NotFoundBoundary>
|
||||
<NotFoundErrorBoundary>
|
||||
<RedirectBoundary>
|
||||
<RedirectErrorBoundary>
|
||||
<RootLayout>
|
||||
<html>
|
||||
<body>
|
||||
<OuterLayoutRouter>
|
||||
<RenderFromTemplateContext>
|
||||
<ScrollAndFocusHandler>
|
||||
<InnerScrollAndFocusHandler>
|
||||
<ErrorBoundary>
|
||||
<LoadingBoundary>
|
||||
<NotFoundBoundary>
|
||||
<NotFoundErrorBoundary>
|
||||
<RedirectBoundary>
|
||||
<RedirectErrorBoundary>
|
||||
<InnerLayoutRouter>
|
||||
<Mismatch>
|
||||
<main>
|
||||
<Component>
|
||||
<div>
|
||||
<p>
|
||||
"server"
|
||||
"client""
|
||||
`)
|
||||
} else {
|
||||
expect(await session.getRedboxComponentStack()).toMatchInlineSnapshot(`
|
||||
|
|
|
@ -58,8 +58,9 @@ describe('Error overlay for hydration errors', () => {
|
|||
<InnerLayoutRouter>
|
||||
<Mismatch>
|
||||
<div>
|
||||
"server"
|
||||
"client""
|
||||
<main>
|
||||
"server"
|
||||
"client""
|
||||
`)
|
||||
} else {
|
||||
expect(pseudoHtml).toMatchInlineSnapshot(`
|
||||
|
@ -430,7 +431,9 @@ describe('Error overlay for hydration errors', () => {
|
|||
^^^
|
||||
<span>
|
||||
...
|
||||
<span>"
|
||||
<span>
|
||||
<p>
|
||||
^^^"
|
||||
`)
|
||||
} else {
|
||||
expect(pseudoHtml).toMatchInlineSnapshot(`
|
||||
|
@ -447,4 +450,121 @@ describe('Error overlay for hydration errors', () => {
|
|||
|
||||
await cleanup()
|
||||
})
|
||||
|
||||
it('should collapse and uncollapse properly when there are many frames', async () => {
|
||||
const { cleanup, session } = await sandbox(
|
||||
next,
|
||||
new Map([
|
||||
[
|
||||
'app/page.js',
|
||||
outdent`
|
||||
'use client'
|
||||
|
||||
const isServer = typeof window === 'undefined'
|
||||
|
||||
function Mismatch() {
|
||||
return (
|
||||
<p>
|
||||
<span>
|
||||
|
||||
hello {isServer ? 'server' : 'client'}
|
||||
</span>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<Mismatch />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
`,
|
||||
],
|
||||
])
|
||||
)
|
||||
|
||||
await session.waitForAndOpenRuntimeError()
|
||||
expect(await session.hasRedbox()).toBe(true)
|
||||
|
||||
const pseudoHtml = await session.getRedboxComponentStack()
|
||||
expect(pseudoHtml).toMatchInlineSnapshot(`
|
||||
"...
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<Mismatch>
|
||||
<p>
|
||||
<span>
|
||||
"server"
|
||||
"client""
|
||||
`)
|
||||
|
||||
await session.toggleCollapseComponentStack()
|
||||
|
||||
const fullPseudoHtml = await session.getRedboxComponentStack()
|
||||
if (isTurbopack) {
|
||||
expect(fullPseudoHtml).toMatchInlineSnapshot(`
|
||||
"<Root>
|
||||
<ServerRoot>
|
||||
<AppRouter>
|
||||
<ErrorBoundary>
|
||||
<ErrorBoundaryHandler>
|
||||
<Router>
|
||||
<HotReload>
|
||||
<ReactDevOverlay>
|
||||
<DevRootNotFoundBoundary>
|
||||
<NotFoundBoundary>
|
||||
<NotFoundErrorBoundary>
|
||||
<RedirectBoundary>
|
||||
<RedirectErrorBoundary>
|
||||
<RootLayout>
|
||||
<html>
|
||||
<body>
|
||||
<OuterLayoutRouter>
|
||||
<RenderFromTemplateContext>
|
||||
<ScrollAndFocusHandler>
|
||||
<InnerScrollAndFocusHandler>
|
||||
<ErrorBoundary>
|
||||
<LoadingBoundary>
|
||||
<NotFoundBoundary>
|
||||
<NotFoundErrorBoundary>
|
||||
<RedirectBoundary>
|
||||
<RedirectErrorBoundary>
|
||||
<InnerLayoutRouter>
|
||||
<Page>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<Mismatch>
|
||||
<p>
|
||||
<span>
|
||||
"server"
|
||||
"client""
|
||||
`)
|
||||
} else {
|
||||
expect(fullPseudoHtml).toMatchInlineSnapshot(`
|
||||
"<Page>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<Mismatch>
|
||||
<p>
|
||||
<span>
|
||||
"server"
|
||||
"client""
|
||||
`)
|
||||
}
|
||||
|
||||
await cleanup()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -22,8 +22,9 @@ createNextDescribe(
|
|||
<main>
|
||||
<Component>
|
||||
<div>
|
||||
"server"
|
||||
"client""
|
||||
<p>
|
||||
"server"
|
||||
"client""
|
||||
`)
|
||||
} else {
|
||||
expect(await getRedboxComponentStack(browser)).toMatchInlineSnapshot(`
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
waitFor,
|
||||
waitForAndOpenRuntimeError,
|
||||
getRedboxDescriptionWarning,
|
||||
toggleComponentStack,
|
||||
toggleCollapseComponentStack,
|
||||
} from './next-test-utils'
|
||||
import webdriver from './next-webdriver'
|
||||
import { NextInstance } from './next-modes/base'
|
||||
|
@ -139,8 +139,8 @@ export async function sandbox(
|
|||
async getRedboxComponentStack() {
|
||||
return getRedboxComponentStack(browser)
|
||||
},
|
||||
async toggleComponentStack() {
|
||||
return toggleComponentStack(browser)
|
||||
async toggleCollapseComponentStack() {
|
||||
return toggleCollapseComponentStack(browser)
|
||||
},
|
||||
async getVersionCheckerText() {
|
||||
return getVersionCheckerText(browser)
|
||||
|
|
|
@ -1068,7 +1068,7 @@ export async function getRedboxComponentStack(
|
|||
return componentStackFrameTexts.join('\n').trim()
|
||||
}
|
||||
|
||||
export async function toggleComponentStack(
|
||||
export async function toggleCollapseComponentStack(
|
||||
browser: BrowserInterface
|
||||
): Promise<void> {
|
||||
await browser
|
||||
|
|
Loading…
Reference in a new issue