Quest log sheet on Discover #109
@@ -1,7 +1,7 @@
|
|||||||
import { QueryClientProvider } from '@tanstack/react-query'
|
import { QueryClientProvider } from '@tanstack/react-query'
|
||||||
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
||||||
import { createMemoryRouter, RouterProvider, useParams } from 'react-router'
|
import { createMemoryRouter, RouterProvider, useParams } from 'react-router'
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi, type Mock } from 'vitest'
|
import { afterEach, beforeEach, describe, expect, it, onTestFinished, vi, type Mock } from 'vitest'
|
||||||
import { ApiProvider, createApiClient, createQueryClient, createVerifiedSession } from '../api/index.ts'
|
import { ApiProvider, createApiClient, createQueryClient, createVerifiedSession } from '../api/index.ts'
|
||||||
import { createNetworkStatus } from '../pwa/networkStatus.ts'
|
import { createNetworkStatus } from '../pwa/networkStatus.ts'
|
||||||
import { stubAnimate, type FakeAnimation } from '../test/animate.ts'
|
import { stubAnimate, type FakeAnimation } from '../test/animate.ts'
|
||||||
@@ -685,7 +685,22 @@ describe('DiscoverPage', () => {
|
|||||||
expect(sent(server.fetchMock)).toEqual(['DELETE /api/v1/dinners/q1'])
|
expect(sent(server.fetchMock)).toEqual(['DELETE /api/v1/dinners/q1'])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('keeps focus in the confirmation, off its disabled buttons, while a removal is under way', async () => {
|
it.each([
|
||||||
|
['keeps a disabled button focused', false],
|
||||||
|
['moves focus off a disabled button to the page', true],
|
||||||
|
])('keeps focus in the confirmation, off its disabled buttons, while a removal is under way when the browser %s', async (_, fixup) => {
|
||||||
|
// Some browsers move focus from a control to the page as it is disabled.
|
||||||
|
const setAttribute = Element.prototype.setAttribute
|
||||||
|
if (fixup) {
|
||||||
|
vi.spyOn(Element.prototype, 'setAttribute').mockImplementation(function (this: Element, name, value) {
|
||||||
|
// jsdom ignores blur() on a disabled control, so focus leaves just before.
|
||||||
|
if (name === 'disabled' && this === document.activeElement && this instanceof HTMLElement) this.blur()
|
||||||
|
setAttribute.call(this, name, value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onTestFinished(() => {
|
||||||
|
vi.restoreAllMocks()
|
||||||
|
})
|
||||||
const archive = deferred()
|
const archive = deferred()
|
||||||
const server = stubQuestLog([PAD_THAI, GUMBO], { 'DELETE /api/v1/dinners/q1': () => archive.promise })
|
const server = stubQuestLog([PAD_THAI, GUMBO], { 'DELETE /api/v1/dinners/q1': () => archive.promise })
|
||||||
await renderPage()
|
await renderPage()
|
||||||
|
|||||||
@@ -59,12 +59,14 @@ export function Confirm({
|
|||||||
}: ConfirmProps) {
|
}: ConfirmProps) {
|
||||||
const titleId = useId()
|
const titleId = useId()
|
||||||
const sheetRef = useRef<HTMLDivElement>(null)
|
const sheetRef = useRef<HTMLDivElement>(null)
|
||||||
// A focused button that becomes disabled loses focus to the page, outside
|
// A focused button that becomes disabled either keeps focus while disabled or
|
||||||
// the dialog; the dialog takes it instead.
|
// loses it to the page, depending on the browser; the dialog takes it instead.
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const dialog = sheetRef.current?.closest<HTMLElement>('[role="dialog"]')
|
const dialog = sheetRef.current?.closest<HTMLElement>('[role="dialog"]')
|
||||||
|
if (!dialog) return
|
||||||
const active = document.activeElement
|
const active = document.activeElement
|
||||||
if (dialog && active instanceof HTMLElement && active.matches(':disabled') && dialog.contains(active)) dialog.focus()
|
const lost = active === null || active === document.body || (dialog.contains(active) && active.matches(':disabled'))
|
||||||
|
if (lost) dialog.focus()
|
||||||
}, [busy, dismissible])
|
}, [busy, dismissible])
|
||||||
function dismiss() {
|
function dismiss() {
|
||||||
if (dismissible) onCancel()
|
if (dismissible) onCancel()
|
||||||
|
|||||||
Reference in New Issue
Block a user