B146 — focus dialog title on open (B42 pattern); consolidate mobile test stub
- Move initial focus to sr-only title instead of void/body (a11y). - Fold matchMedia mock into stubMobileViewport helper. Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
This commit is contained in:
parent
56e60ce589
commit
fced1f3e1d
2 changed files with 24 additions and 23 deletions
|
|
@ -1,7 +1,7 @@
|
|||
'use client'
|
||||
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useEffect } from 'react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import { CampaignGlobalSearchBody } from '@/components/campaign/dashboard/CampaignGlobalSearchMount'
|
||||
import { CampaignHomeActionStrip } from '@/components/campaign/dashboard/CampaignHomeActionStrip'
|
||||
|
|
@ -117,6 +117,7 @@ export const CampaignQuickActionsOverlay = ({
|
|||
}) => {
|
||||
const isMobile = useIsMobile()
|
||||
const { clear } = useHomeSearch()
|
||||
const titleRef = useRef<HTMLHeadingElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) clear()
|
||||
|
|
@ -127,10 +128,12 @@ export const CampaignQuickActionsOverlay = ({
|
|||
<Drawer open={open} onOpenChange={onOpenChange} showSwipeHandle>
|
||||
<DrawerContent
|
||||
id="CampaignQuickActionsOverlay"
|
||||
initialFocus={false}
|
||||
initialFocus={titleRef}
|
||||
className="max-h-[85dvh] border-t border-border bg-background text-foreground [--drawer-height:auto]"
|
||||
>
|
||||
<DrawerTitle className="sr-only">Ações rápidas</DrawerTitle>
|
||||
<DrawerTitle ref={titleRef} tabIndex={-1} className="sr-only outline-none">
|
||||
Ações rápidas
|
||||
</DrawerTitle>
|
||||
<DrawerDescription className="sr-only">
|
||||
Ações do contexto e busca na campanha
|
||||
</DrawerDescription>
|
||||
|
|
@ -147,10 +150,15 @@ export const CampaignQuickActionsOverlay = ({
|
|||
<DialogContent
|
||||
id="CampaignQuickActionsOverlay"
|
||||
className="flex max-h-[min(85dvh,40rem)] w-[calc(100vw-2rem)] max-w-lg flex-col gap-4 overflow-hidden p-4 sm:p-6"
|
||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||
onOpenAutoFocus={(event) => {
|
||||
event.preventDefault()
|
||||
titleRef.current?.focus()
|
||||
}}
|
||||
>
|
||||
<DialogHeader className="sr-only">
|
||||
<DialogTitle>Ações rápidas</DialogTitle>
|
||||
<DialogTitle ref={titleRef} tabIndex={-1} className="outline-none">
|
||||
Ações rápidas
|
||||
</DialogTitle>
|
||||
<DialogDescription>Ações do contexto e busca na campanha</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col overflow-y-auto">
|
||||
|
|
|
|||
|
|
@ -58,6 +58,15 @@ let innerWidthSpy: ReturnType<typeof vi.spyOn> | undefined
|
|||
|
||||
const stubMobileViewport = () => {
|
||||
innerWidthSpy = vi.spyOn(window, 'innerWidth', 'get').mockReturnValue(375)
|
||||
matchMediaMock.mockImplementation((query: string) => ({
|
||||
matches: query.includes('max-width'),
|
||||
media: query,
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
}))
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
@ -359,23 +368,16 @@ describe('CampaignQuickActionsOverlay (B126)', () => {
|
|||
|
||||
const input = screen.getByLabelText('Buscar na campanha')
|
||||
const actionsChrome = document.querySelector('[data-slot="quick-actions-chrome"]')
|
||||
const title = screen.getByText('Ações rápidas')
|
||||
|
||||
expect(document.activeElement).not.toBe(input)
|
||||
expect(document.activeElement).toBe(title)
|
||||
expect(actionsChrome?.getAttribute('data-retracted')).toBeNull()
|
||||
expect(actionsChrome?.className).not.toContain('grid-rows-[0fr]')
|
||||
})
|
||||
|
||||
it('shows swipe handle and no dialog close on mobile (B146)', () => {
|
||||
stubMobileViewport()
|
||||
matchMediaMock.mockImplementation((query: string) => ({
|
||||
matches: query.includes('max-width'),
|
||||
media: query,
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mocked(postCampaignJson).mockResolvedValue({
|
||||
ok: true,
|
||||
|
|
@ -403,15 +405,6 @@ describe('CampaignQuickActionsOverlay (B126)', () => {
|
|||
|
||||
it('uses mobile drawer with actions above search', () => {
|
||||
stubMobileViewport()
|
||||
matchMediaMock.mockImplementation((query: string) => ({
|
||||
matches: query.includes('max-width'),
|
||||
media: query,
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mocked(postCampaignJson).mockResolvedValue({
|
||||
ok: true,
|
||||
|
|
|
|||
Loading…
Reference in a new issue