Skip to content

Commit

Permalink
fix dev overlay pseudo html collapsing (#62728)
Browse files Browse the repository at this point in the history
Dev overlay should show the full stack trace after clicking the
uncollase button to display the full component stack

Closes NEXT-2658
  • Loading branch information
huozhi committed Mar 1, 2024
1 parent 8034042 commit 4d4b45e
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand All @@ -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
? {
Expand All @@ -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}
Expand Down Expand Up @@ -154,6 +154,7 @@ export function PseudoHtmlDiff({
serverContent,
isHtmlTagsWarning,
hydrationMismatchType,
MAX_NON_COLLAPSED_FRAMES,
])

return (
Expand Down
49 changes: 38 additions & 11 deletions test/development/acceptance-app/component-stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(`
Expand Down
126 changes: 123 additions & 3 deletions test/development/acceptance-app/hydration-error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ describe('Error overlay for hydration errors', () => {
<InnerLayoutRouter>
<Mismatch>
<div>
"server"
"client""
<main>
"server"
"client""
`)
} else {
expect(pseudoHtml).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -430,7 +431,9 @@ describe('Error overlay for hydration errors', () => {
^^^
<span>
...
<span>"
<span>
<p>
^^^"
`)
} else {
expect(pseudoHtml).toMatchInlineSnapshot(`
Expand All @@ -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()
})
})
5 changes: 3 additions & 2 deletions test/development/acceptance/component-stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ createNextDescribe(
<main>
<Component>
<div>
"server"
"client""
<p>
"server"
"client""
`)
} else {
expect(await getRedboxComponentStack(browser)).toMatchInlineSnapshot(`
Expand Down
6 changes: 3 additions & 3 deletions test/lib/development-sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion test/lib/next-test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 4d4b45e

Please sign in to comment.