Plan step 12, containment.md C-01. The handle button was 32x4px: the padding that makes M3's 48dp hit target sat on the wrapper, which is not the control. `touch-target` on the button and 22px above and below it — SheetDefaults.kt's DragHandleVerticalPadding — make the pressed area 48x48 while the handle is still drawn 32x4. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
96 lines
4.6 KiB
PHP
96 lines
4.6 KiB
PHP
<?php
|
|
|
|
use Livewire\Component;
|
|
use Livewire\Livewire;
|
|
|
|
it('opens a native dialog entangled with a Livewire property, out of the morph\'s reach', function () {
|
|
$component = new class extends Component
|
|
{
|
|
public bool $confirming = false;
|
|
|
|
public function render(): string
|
|
{
|
|
return <<<'BLADE'
|
|
<div>
|
|
<x-modal wire:model="confirming" title="Delete this share?" subtitle="This cannot be undone." icon="delete">
|
|
<x-slot:actions><button>Delete</button></x-slot:actions>
|
|
</x-modal>
|
|
</div>
|
|
BLADE;
|
|
}
|
|
};
|
|
|
|
Livewire::test($component)
|
|
->assertSeeHtml('<dialog')
|
|
->assertSeeHtml('wire:ignore.self')
|
|
->assertSeeHtml('open: window.Livewire.find(')
|
|
->assertSeeHtml("entangle('confirming').live")
|
|
->assertSeeHtml('close() { this.open = typeof this.open === \'boolean\' ? false : null }')
|
|
->assertSeeHtml('rounded-corner-xl bg-surface-container-high')
|
|
->assertSeeHtml('type-headline-sm')
|
|
->assertSee('This cannot be undone.')
|
|
->assertSeeHtml('text-center')
|
|
->assertDontSeeHtml('wire:model="confirming"');
|
|
});
|
|
|
|
it('uses the surrounding Alpine scope without wire:model, and stays open when persistent', function () {
|
|
$html = (string) $this->blade('<x-modal title="Help" persistent fullscreen>Text</x-modal>');
|
|
|
|
expect($html)
|
|
->toContain('x-data="{ close() { this.open = false } }"')
|
|
->toContain('x-on:cancel.prevent=""')
|
|
->not->toContain('x-on:click.self')
|
|
->toContain('max-medium:h-dvh')
|
|
->toContain('aria-label="Close"');
|
|
});
|
|
|
|
it('keeps a full-screen dialog\'s subtitle on a phone, where its bar carries the title', function () {
|
|
$html = (string) $this->blade('<x-modal title="Enable 2FA" subtitle="Scan the code" fullscreen>Text</x-modal>');
|
|
|
|
expect($html)
|
|
->toMatch('/<h2 id="[^"]+-title" class="type-headline-sm max-medium:hidden">/')
|
|
->toContain('<p class="type-body-md text-on-surface-variant mt-4 max-medium:mt-0">Scan the code</p>')
|
|
->not->toContain('<div class="mb-4 max-medium:hidden">')
|
|
->and((string) $this->blade('<x-modal title="Help" fullscreen>Text</x-modal>'))->toContain('<div class="mb-4 max-medium:hidden">')
|
|
->and((string) $this->blade('<x-modal subtitle="Only a subtitle">Text</x-modal>'))->toContain('<p class="type-body-md text-on-surface-variant">Only a subtitle</p>');
|
|
});
|
|
|
|
it('leaves a pane open on Escape unless it is asked to close then too', function () {
|
|
expect((string) $this->blade('<x-drawer pane>Body</x-drawer>'))
|
|
->toContain('x-on:keydown.window.escape="if (open && ! wide) close()"')
|
|
->and((string) $this->blade('<x-drawer pane pane-close-on-escape>Body</x-drawer>'))
|
|
->toContain('x-on:keydown.window.escape="if (open) close()"')
|
|
->and((string) $this->blade('<x-drawer pane pane-close-on-escape :close-on-escape="false">Body</x-drawer>'))
|
|
->not->toContain('keydown.window.escape');
|
|
});
|
|
|
|
it('slides a side sheet in from either edge, and is a pane from expanded when asked', function () {
|
|
expect((string) $this->blade('<x-drawer title="Details" with-close-button>Body</x-drawer>'))
|
|
->toContain('x-trap.inert.noscroll="open && ! wide"')
|
|
->toContain('end-0 medium:rounded-s-corner-lg')
|
|
->toContain('bg-surface-container-low')
|
|
->toContain('role="dialog"')
|
|
->toContain('aria-label="Close"')
|
|
->and((string) $this->blade('<x-drawer side="start">Body</x-drawer>'))->toContain('start-0 medium:rounded-e-corner-lg')
|
|
->and((string) $this->blade('<x-drawer pane pane-width="28rem">Body</x-drawer>'))
|
|
->toContain('data-pane')
|
|
->toContain('--pane-width: 28rem')
|
|
->toContain("matchMedia('(width >= 52.5rem)')");
|
|
});
|
|
|
|
it('draws a modal bottom sheet with a drag handle, or a standard one without a scrim', function () {
|
|
expect((string) $this->blade('<x-bottom-sheet title="Share via">Body</x-bottom-sheet>'))
|
|
->toContain('...materialBottomSheet(false)')
|
|
->toContain('bg-scrim/32')
|
|
->toContain('x-trap.inert.noscroll="open"')
|
|
->toContain('rounded-t-corner-xl bg-surface-container-low')
|
|
->toContain('data-drag-handle')
|
|
->toContain('aria-modal="true"')
|
|
->toContain('py-5.5')
|
|
->toContain('touch-target h-1 w-8')
|
|
->and((string) $this->blade('<x-bottom-sheet standard>Body</x-bottom-sheet>'))
|
|
->toContain('...materialBottomSheet(true)')
|
|
->not->toContain('bg-scrim/32')
|
|
->not->toContain('aria-modal');
|
|
});
|