Merge pull request #136 from fsolla/cursor/pool-i128-b91ce9e1-0a68
B101 — Strip de ações: bleed sem faixa branca + hit area via padding
This commit is contained in:
commit
aae788a64c
5 changed files with 82 additions and 20 deletions
|
|
@ -27,8 +27,8 @@ export type CampaignHomeActionButtonProps = {
|
|||
disabled?: boolean
|
||||
}
|
||||
|
||||
const actionControlClassName =
|
||||
'group flex min-h-11 w-[4.75rem] shrink-0 snap-start flex-col items-center gap-2 rounded-md text-center outline-none focus-visible:ring-2 focus-visible:ring-ring active:opacity-90 disabled:pointer-events-none disabled:opacity-50'
|
||||
export const actionControlClassName =
|
||||
'group box-content flex min-h-11 w-[4.75rem] shrink-0 snap-start flex-col items-center gap-2 rounded-md px-2 text-center outline-none focus-visible:ring-2 focus-visible:ring-ring active:opacity-90 disabled:pointer-events-none disabled:opacity-50'
|
||||
|
||||
const circleClassName =
|
||||
'flex size-14 items-center justify-center rounded-full bg-muted text-foreground transition-[transform,colors] duration-150 ease-out motion-reduce:transition-none pointer-fine:group-hover:scale-[1.05] pointer-coarse:group-data-[pressing=true]:scale-[1.05] motion-reduce:scale-100 group-hover:bg-muted/80 group-focus-visible:bg-muted/80'
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ export const CampaignHomeActionStrip = ({
|
|||
>
|
||||
<ul
|
||||
role="list"
|
||||
className="m-0 flex min-w-max list-none snap-x snap-proximity gap-2 p-0 pb-1"
|
||||
className="m-0 flex min-w-max list-none snap-x snap-proximity gap-0 px-4 pb-1 md:px-0"
|
||||
>
|
||||
{actions?.map(({ id, ...button }) => (
|
||||
<li key={id ?? button.href ?? button.label} className="m-0 list-none p-0">
|
||||
|
|
|
|||
|
|
@ -78,16 +78,18 @@ export const CampaignQuickActionsDrawer = ({
|
|||
aria-hidden={!isDock}
|
||||
>
|
||||
{showActions ? (
|
||||
<CampaignHomeActionStrip
|
||||
actions={actions.map((action) => ({
|
||||
id: action.id,
|
||||
label: action.label,
|
||||
icon: action.icon,
|
||||
description: action.description,
|
||||
href: action.href,
|
||||
}))}
|
||||
className="w-full"
|
||||
/>
|
||||
<div className="-mx-4 w-[calc(100%+2rem)]">
|
||||
<CampaignHomeActionStrip
|
||||
actions={actions.map((action) => ({
|
||||
id: action.id,
|
||||
label: action.label,
|
||||
icon: action.icon,
|
||||
description: action.description,
|
||||
href: action.href,
|
||||
}))}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<CampaignGlobalSearchBody />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|||
|
||||
import {
|
||||
CampaignHomeActionButton,
|
||||
actionControlClassName,
|
||||
type CampaignHomeActionButtonProps,
|
||||
} from '@/components/campaign/dashboard/CampaignHomeActionButton'
|
||||
import {
|
||||
|
|
@ -90,6 +91,16 @@ describe('CampaignHomeActionButton', () => {
|
|||
expect(screen.getByLabelText('Em breve').getAttribute('aria-disabled')).toBe('true')
|
||||
})
|
||||
|
||||
it('uses horizontal padding for hit area without shrinking the visual column (B101)', () => {
|
||||
renderActionButton({ label: 'Registrar', icon: BarChart3 })
|
||||
const control = screen.getByRole('button', { name: 'Registrar' })
|
||||
expect(control.className).toContain('px-2')
|
||||
expect(control.className).toContain('box-content')
|
||||
expect(control.className).toContain('w-[4.75rem]')
|
||||
expect(actionControlClassName).toContain('px-2')
|
||||
expect(actionControlClassName).toContain('box-content')
|
||||
})
|
||||
|
||||
it('opens the description drawer on long-press when the pointer is coarse', () => {
|
||||
resetCampaignCoarsePointerForTests()
|
||||
matchMediaMock.mockImplementation((query: string) => ({
|
||||
|
|
@ -180,7 +191,12 @@ describe('CampaignHomeActionStrip', () => {
|
|||
const scroller = getScroller(container)
|
||||
expect(scroller?.className).toContain('overflow-x-auto')
|
||||
expect(scroller?.className).toContain('scrollbar-width:none')
|
||||
expect(scroller?.querySelector('ul[role="list"]')).toBeTruthy()
|
||||
const list = scroller?.querySelector('ul[role="list"]')
|
||||
expect(list).toBeTruthy()
|
||||
expect(list?.className).toContain('gap-0')
|
||||
expect(list?.className).toContain('px-4')
|
||||
expect(list?.className).toContain('md:px-0')
|
||||
expect(list?.className).not.toContain('gap-2')
|
||||
})
|
||||
|
||||
it('scrolls horizontally on pointer-fine drag past the threshold', () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { BarChart3 } from 'lucide-react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { CampaignGlobalSearchProvider } from '@/components/campaign/dashboard/CampaignGlobalSearchMount'
|
||||
import { CampaignQuickActionsDrawer } from '@/components/campaign/shell/CampaignQuickActionsDrawer'
|
||||
|
|
@ -37,6 +38,27 @@ const idleSuggestPayload = {
|
|||
demands: [],
|
||||
}
|
||||
|
||||
const matchMediaMock = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
matchMediaMock.mockImplementation((query: string) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
}))
|
||||
vi.stubGlobal('matchMedia', matchMediaMock)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
vi.mocked(postCampaignJson).mockReset()
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
const renderQuickActionsChrome = (ui: ReactNode) =>
|
||||
render(
|
||||
<CampaignQuickActionsSnapProvider>
|
||||
|
|
@ -49,11 +71,6 @@ const SnapReadout = () => {
|
|||
return <div data-testid="snap">{snapPoint ?? 'null'}</div>
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
vi.mocked(postCampaignJson).mockReset()
|
||||
})
|
||||
|
||||
describe('CampaignQuickActionsDrawer (B100)', () => {
|
||||
it('loads in dock snap with global search visible', () => {
|
||||
vi.mocked(postCampaignJson).mockResolvedValue({
|
||||
|
|
@ -71,6 +88,33 @@ describe('CampaignQuickActionsDrawer (B100)', () => {
|
|||
expect(screen.getByRole('button', { name: 'Ocultar ações rápidas' })).toBeTruthy()
|
||||
})
|
||||
|
||||
it('bleeds the action strip edge-to-edge inside the drawer gutter (B101)', () => {
|
||||
vi.mocked(postCampaignJson).mockResolvedValue({
|
||||
ok: true,
|
||||
payload: idleSuggestPayload,
|
||||
})
|
||||
|
||||
renderQuickActionsChrome(
|
||||
<CampaignQuickActionsDrawer
|
||||
actions={[
|
||||
{
|
||||
id: 'test',
|
||||
label: 'Registrar',
|
||||
icon: BarChart3,
|
||||
description: 'Teste.',
|
||||
href: '/campanha',
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
)
|
||||
|
||||
const context = document.getElementById('quickActionContext')
|
||||
const stripBleed = context?.firstElementChild
|
||||
expect(stripBleed?.className).toContain('-mx-4')
|
||||
expect(stripBleed?.className).toContain('w-[calc(100%+2rem)]')
|
||||
expect(stripBleed?.querySelector('[aria-label="Ações rápidas"]')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('posts home-search when the drawer query is active', async () => {
|
||||
vi.mocked(postCampaignJson).mockResolvedValue({
|
||||
ok: true,
|
||||
|
|
|
|||
Loading…
Reference in a new issue