Skip to content

Commit 16de4b5

Browse files
committed
[devtool] fix overlay styles are missing (#83721)
1 parent 39c3cd6 commit 16de4b5

File tree

5 files changed

+86
-31
lines changed

5 files changed

+86
-31
lines changed

packages/next/src/build/webpack/loaders/devtool/devtool-style-inject.js

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -85,42 +85,50 @@ function startObservingForPortal() {
8585

8686
// Set up MutationObserver to watch for the portal element
8787
const observer = new MutationObserver((mutations) => {
88-
if (mutations.length === 0 || mutations[0].addedNodes.length === 0) {
88+
if (mutations.length === 0) {
8989
return
9090
}
9191

92-
// Check if mutation is script[data-nextjs-dev-overlay] tag, which is the
93-
// parent of the nextjs-portal element
94-
const mutationNode = mutations[0].addedNodes[0]
95-
let portalNode = null
96-
if (
97-
// app router: body > script[data-nextjs-dev-overlay] > nextjs-portal
98-
mutationNode.tagName === 'SCRIPT' &&
99-
mutationNode.getAttribute('data-nextjs-dev-overlay')
100-
) {
101-
portalNode = mutationNode.firstChild
102-
} else if (
103-
// pages router: body > nextjs-portal
104-
mutationNode.tagName === 'NEXTJS-PORTAL'
105-
) {
106-
portalNode = mutationNode
107-
}
108-
if (!portalNode) {
109-
return
110-
}
111-
112-
// Wait until shadow root is available
113-
const checkShadowRoot = () => {
114-
if (getShadowRoot()) {
115-
flushCachedElements()
116-
observer.disconnect()
117-
cache.isObserving = false
118-
} else {
119-
// Try again after a short delay
120-
setTimeout(checkShadowRoot, 20)
92+
// Check all mutations and all added nodes
93+
for (const mutation of mutations) {
94+
if (mutation.addedNodes.length === 0) continue
95+
96+
for (const addedNode of mutation.addedNodes) {
97+
if (addedNode.nodeType !== Node.ELEMENT_NODE) continue
98+
99+
const mutationNode = addedNode
100+
101+
let portalNode = null
102+
if (
103+
// app router: body > script[data-nextjs-dev-overlay] > nextjs-portal
104+
mutationNode.tagName === 'SCRIPT' &&
105+
mutationNode.getAttribute('data-nextjs-dev-overlay')
106+
) {
107+
portalNode = mutationNode.firstChild
108+
} else if (
109+
// pages router: body > nextjs-portal
110+
mutationNode.tagName === 'NEXTJS-PORTAL'
111+
) {
112+
portalNode = mutationNode
113+
}
114+
115+
if (portalNode) {
116+
// Wait until shadow root is available
117+
const checkShadowRoot = () => {
118+
if (getShadowRoot()) {
119+
flushCachedElements()
120+
observer.disconnect()
121+
cache.isObserving = false
122+
} else {
123+
// Try again after a short delay
124+
setTimeout(checkShadowRoot, 20)
125+
}
126+
}
127+
checkShadowRoot()
128+
return // Exit early once we find a portal
129+
}
121130
}
122131
}
123-
checkShadowRoot()
124132
})
125133

126134
observer.observe(document.body, {

test/development/error-overlay/index.test.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,28 @@ describe('DevErrorOverlay', () => {
5454
expect(request.status).toBe(200)
5555
}
5656
})
57+
58+
it('should load dev overlay styles successfully', async () => {
59+
const browser = await next.browser('/hydration-error')
60+
61+
await assertHasRedbox(browser)
62+
const redbox = browser.locateRedbox()
63+
64+
// check the data-nextjs-dialog-header="true" DOM element styles under redbox is applied
65+
const dialogHeader = redbox.locator('[data-nextjs-dialog-header="true"]')
66+
expect(await dialogHeader.isVisible()).toBe(true)
67+
// get computed styles
68+
const computedStyles = await dialogHeader.evaluate((element) => {
69+
return window.getComputedStyle(element)
70+
})
71+
const styles = {
72+
backgroundColor: computedStyles.backgroundColor,
73+
color: computedStyles.color,
74+
}
75+
76+
expect(styles).toEqual({
77+
backgroundColor: 'rgba(0, 0, 0, 0)',
78+
color: 'rgb(117, 117, 117)',
79+
})
80+
})
5781
})
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { AppProps } from 'next/app'
2+
3+
export default function App({ Component, pageProps }: AppProps) {
4+
return <Component {...pageProps} />
5+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Html, Head, Main, NextScript } from 'next/document'
2+
3+
export default function Document() {
4+
return (
5+
<Html lang="en">
6+
<Head />
7+
<body>
8+
<Main />
9+
<NextScript />
10+
</body>
11+
</Html>
12+
)
13+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default function Home() {
2+
return (
3+
<div>{typeof window === 'undefined' ? <p>Server</p> : <p>Client</p>}</div>
4+
)
5+
}

0 commit comments

Comments
 (0)