ee15b3be5d
### What? When Safari is in the background and HMR triggers a full page reload, Safari hijacks application focus. ### Why? Having a `role="dialog"` is correctly prompting Safari to autofocus the first focusable element (the close button). However, Safari's behavior seems to also bring the application to the foreground when a background focus event occurs. ### How? This only adds the role when the document is focused. #### Before https://github.com/vercel/next.js/assets/1939140/9d2cce52-c6ee-4d49-9262-03620efad86c #### After https://github.com/vercel/next.js/assets/1939140/dc7d337c-b621-49e9-9a17-03b5d8b5c3f4 Confirmed voiceover behavior still appears to be correct <img width="1371" alt="CleanShot 2023-08-07 at 12 14 34@2x" src="https://github.com/vercel/next.js/assets/1939140/e53acfbc-cf6b-4d74-8b83-cf98edb2c2ab"> slack x-ref: https://vercel.slack.com/archives/C03KAR5DCKC/p1691264077313599 Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
83 lines
2.5 KiB
TypeScript
83 lines
2.5 KiB
TypeScript
import { createNext, FileRef } from 'e2e-utils'
|
|
import webdriver from 'next-webdriver'
|
|
import { NextInstance } from 'test/lib/next-modes/base'
|
|
import { join } from 'path'
|
|
import { BrowserInterface } from 'test/lib/browsers/base'
|
|
import { check } from 'next-test-utils'
|
|
|
|
describe('client-dev-overlay', () => {
|
|
let next: NextInstance
|
|
let browser: BrowserInterface
|
|
|
|
beforeAll(async () => {
|
|
next = await createNext({
|
|
files: {
|
|
pages: new FileRef(join(__dirname, 'app/pages')),
|
|
},
|
|
})
|
|
})
|
|
beforeEach(async () => {
|
|
browser = await webdriver(next.url, '')
|
|
})
|
|
afterAll(() => next.destroy())
|
|
|
|
// The `BrowserInterface.hasElementByCssSelector` cannot be used for elements inside a shadow DOM.
|
|
function elementExistsInNextJSPortalShadowDOM(selector: string) {
|
|
return browser.eval(
|
|
`!!document.querySelector('nextjs-portal').shadowRoot.querySelector('${selector}')`
|
|
) as any
|
|
}
|
|
const selectors = {
|
|
fullScreenDialog: '[data-nextjs-dialog]',
|
|
toast: '[data-nextjs-toast]',
|
|
minimizeButton: '[data-nextjs-errors-dialog-left-right-close-button]',
|
|
hideButton: '[data-nextjs-toast-errors-hide-button]',
|
|
}
|
|
function getToast() {
|
|
return browser.elementByCss(selectors.toast)
|
|
}
|
|
function getMinimizeButton() {
|
|
return browser.elementByCss(selectors.minimizeButton)
|
|
}
|
|
function getHideButton() {
|
|
return browser.elementByCss(selectors.hideButton)
|
|
}
|
|
|
|
it('should be able to fullscreen the minimized overlay', async () => {
|
|
await getMinimizeButton().click()
|
|
await getToast().click()
|
|
|
|
await check(async () => {
|
|
return (await elementExistsInNextJSPortalShadowDOM(
|
|
selectors.fullScreenDialog
|
|
))
|
|
? 'success'
|
|
: 'missing'
|
|
}, 'success')
|
|
})
|
|
|
|
it('should be able to minimize the fullscreen overlay', async () => {
|
|
await getMinimizeButton().click()
|
|
expect(await elementExistsInNextJSPortalShadowDOM(selectors.toast)).toBe(
|
|
true
|
|
)
|
|
})
|
|
|
|
it('should be able to hide the minimized overlay', async () => {
|
|
await getMinimizeButton().click()
|
|
await getHideButton().click()
|
|
|
|
await check(async () => {
|
|
const exists = await elementExistsInNextJSPortalShadowDOM('div')
|
|
return exists ? 'found' : 'success'
|
|
}, 'success')
|
|
})
|
|
|
|
it('should have a role of "dialog" if the page is focused', async () => {
|
|
await check(async () => {
|
|
return (await elementExistsInNextJSPortalShadowDOM('[role="dialog"]'))
|
|
? 'exists'
|
|
: 'missing'
|
|
}, 'exists')
|
|
})
|
|
})
|