/');
});
it('is an alert dialog when it interrupts, and describes itself by its subtitle', function () {
$alert = (string) $this->blade('
This cannot be undone.');
preg_match('/id="(material-dialog-[a-z0-9]+)"/', $alert, $id);
expect($alert)
->toContain('role="alertdialog"')
->toContain("aria-describedby=\"{$id[1]}-subtitle {$id[1]}-body\"")
->and((string) $this->blade('
Body'))
->not->toContain('role="alertdialog"')
->toMatch('/aria-describedby="material-dialog-[a-z0-9]+-subtitle"/')
->and((string) $this->blade('
Body'))
->not->toContain('aria-describedby');
});
it('gives a full-screen dialog M3\'s 56px header and action bar', function () {
expect((string) $this->blade('
Body'))
->toContain('data-md-modal-bar')
->toContain('data-md-fullscreen')
->and(File::get(__DIR__.'/../../../resources/css/components/modal.css'))
->toContain('height: var(--md-sys-measurement-space700);')
->toContain('min-height: var(--md-sys-measurement-space700);');
});
it('always offers a way out of a side sheet, as M3 requires', function () {
expect((string) $this->blade('
Body'))->toContain('aria-label="Close"')
->and((string) $this->blade('
Body'))->not->toContain('aria-label="Close"')
->and((string) $this->blade('
Body'))->toContain('aria-label="Close"')
->and((string) $this->blade('
Body'))->toContain('aria-label="Close"')
->and((string) $this->blade('
Body'))->toContain('aria-label="Close"');
});
it('puts a side sheet\'s actions on the left, in M3\'s 72dp row', function () {
expect((string) $this->blade('
Body'))
->toMatch('/
/');
});
it('slides a side sheet in from either edge', function () {
expect((string) $this->blade('Body'))
->toContain('x-trap.inert.noscroll="open && ! wide"')
->toContain('data-md-side="end"')
->toContain('data-md-drawer-sheet')
->toContain('role="dialog"')
->toContain('aria-label="Close"')
->and((string) $this->blade('Body'))->toContain('data-md-side="start"');
});
it('draws a modal bottom sheet with a drag handle, or a standard one without a scrim', function () {
expect((string) $this->blade('Body'))
->toContain('...materialBottomSheet(false, JSON.parse(')
->toContain('data-md-bottom-sheet-scrim')
->toContain('x-trap.inert.noscroll="open"')
->toContain('data-md-bottom-sheet-panel')
->toContain('data-md-bottom-sheet-handle')
->toContain('data-md-bottom-sheet-grip')
->toContain('aria-modal="true"')
->toContain('md-focus-ring md-touch-target')
->toContain('--sheet-max-height: min(50dvh, calc(100dvh - 72px))')
->and((string) $this->blade('Body'))
->toContain('...materialBottomSheet(true, JSON.parse(')
->not->toContain('data-md-bottom-sheet-scrim')
->not->toContain('aria-modal')
->toContain('data-md-standard')
->and((string) $this->blade('Body'))
->toContain('--sheet-max-height: min(90dvh, calc(100dvh - 72px))');
});
it('is M3\'s standard side sheet from expanded, and the modal one below', function () {
$html = (string) $this->blade('Body');
expect($html)
->toContain('data-md-standard')
->toContain('--sheet-width: min(400px, 400px)')
->toContain("matchMedia('(width >= 840px)')")
->toContain('x-trap.inert.noscroll="open && ! wide"')
->toContain('aria-label="Close"')
->and((string) $this->blade('Body'))
->toContain('data-md-side="start"')
->toContain('--sheet-width: min(30rem, 400px)')
->and((string) $this->blade('Body'))
->not->toContain('data-md-standard');
$css = ComponentStylesheet::read('drawer');
$expanded = ['@media (width >= 840px)'];
expect($css->has('[data-md-drawer][data-md-standard]', $expanded))->toBeTrue()
->and($css->declarations('[data-md-drawer][data-md-standard][data-md-drawer-collapsed]', $expanded))->toBe(['display' => 'none'])
// The old view's `expanded:hidden` on a standard sheet's scrim.
->and($css->declarations('[data-md-drawer][data-md-standard] > [data-md-drawer-scrim]', $expanded))->toBe(['display' => 'none'])
->and($css->declarations('[data-md-drawer][data-md-standard] > [data-md-drawer-sheet]', $expanded))->toHaveKey('box-shadow', 'none')
->and($css->has("[data-md-drawer][data-md-standard] > [data-md-drawer-sheet][data-md-side='end']", $expanded))->toBeTrue()
->and($css->has("[data-md-drawer][data-md-standard] > [data-md-drawer-sheet][data-md-side='start']", $expanded))->toBeTrue();
});
it('slides a side sheet out as it slid in, from its own edge in either direction, and fades its scrim both ways', function () {
$css = ComponentStylesheet::read('drawer');
expect($css->declarations('[data-md-drawer-sheet]'))
->toHaveKey('translate', 'var(--sheet-offset) 0')
->toHaveKey('transition-behavior', 'allow-discrete')
->not->toHaveKey('opacity')
->and($css->declarations("[data-md-drawer-sheet][data-md-side='end']:dir(rtl)"))->toBe(['--sheet-offset' => '-100%'])
->and($css->declarations("[data-md-drawer-sheet][data-md-side='start']:dir(rtl)"))->toBe(['--sheet-offset' => '100%'])
->and($css->declarations('[data-md-drawer][data-md-open] > [data-md-drawer-sheet]', ['@starting-style']))->toBe(['translate' => 'var(--sheet-offset) 0'])
->and($css->declarations('[data-md-drawer-scrim]'))->toHaveKey('opacity', '0')
->and($css->declarations('[data-md-drawer][data-md-open] > [data-md-drawer-scrim]'))->toBe(['opacity' => '1']);
});
it('draws the drawer from a stylesheet imported from the containment block, with the pane prop gone', function () {
$view = File::get(__DIR__.'/../../../resources/views/components/drawer.blade.php');
expect($view)
->not->toContain("'pane'")
->not->toContain('paneWidth')
->not->toContain('paneCloseOnEscape')
->not->toContain('pane-width')
->not->toContain('pane-close-on-escape')
->not->toContain('data-pane')
->and(File::get(__DIR__.'/../../../resources/css/all.css'))
->toContain("@import './components/drawer.css';");
});
it('gives a bottom sheet M3\'s preset heights, cycled from the drag handle', function () {
$html = (string) $this->blade('Body');
expect($html)
->toContain('--sheet-stop: 50dvh; --sheet-max-height: min(var(--sheet-stop), calc(100dvh - 72px))')
->toContain('data-md-preset')
->toContain('x-ref="probe"')
->toContain('data-md-bottom-sheet-probe')
->toContain('aria-label="Change the sheet height"')
->toContain('x-bind:aria-label="handleLabel"')
->toContain('x-on:click="activate()"')
->toMatch('/<\/span>/')
->toContain('25dvh')
->toContain('90dvh')
->toContain('Height 2 of 3')
->and((string) $this->blade('Body'))
->toContain('--sheet-stop: 60dvh;')
->toContain('30dvh')
->and((string) $this->blade('Body'))
->toContain('--sheet-stop: 50dvh;')
->and((string) $this->blade('Body'))
->toContain('--sheet-max-height: min(50dvh, calc(100dvh - 72px))')
->not->toContain('--sheet-stop')
->not->toContain('data-md-preset')
->toContain('aria-label="Close"');
});
it('draws the bottom sheet from a stylesheet imported from the containment block and by the menu', function () {
expect(File::get(__DIR__.'/../../../resources/css/all.css'))
->toContain("@import './components/bottom-sheet.css';")
->and(File::get(__DIR__.'/../../../resources/css/components/menu.css'))
->toContain("@import './bottom-sheet.css';")
->and(File::get(__DIR__.'/../../../resources/js/bottom-sheet.js'))
->toContain('data-md-bottom-sheet-handle')
->not->toContain('data-drag-handle');
$css = ComponentStylesheet::read('bottom-sheet');
// The scrim fades out as well as in, a nested sheet keeps its own state, and the handle's ring
// stands 4px off its bar.
expect($css->declarations('[data-md-bottom-sheet-scrim]'))->toHaveKey('opacity', '0')
->and($css->declarations('[data-md-bottom-sheet][data-md-open] > [data-md-bottom-sheet-scrim]'))->toBe(['opacity' => '1'])
->and($css->declarations('[data-md-bottom-sheet][data-md-open] > [data-md-bottom-sheet-panel]', ['@starting-style']))->toBe(['translate' => '0 100%'])
->and($css->declarations('[data-md-bottom-sheet-grip]:focus-visible'))->toBe(['outline-offset' => '4px'])
->and($css->css)->not->toContain('[data-md-open] [data-md-');
});