blade(''); expect($html) ->toContain('x-persist="material-toast"') ->toContain('x-data="materialSnackbar"') ->toContain('data-md-toast') ->toContain('data-md-position="bottom"') ->and(ComponentStylesheet::read('toast')->declarations('[data-md-toast-snackbar]'))->toMatchArray([ 'background-color' => 'var(--md-sys-color-inverse-surface)', 'color' => 'var(--md-sys-color-inverse-on-surface)', ]); }); it('draws no state icon, and reaches 48px from its 40px controls', function () { $html = (string) $this->blade(''); $css = ComponentStylesheet::read('toast'); // M3 tells you to avoid an icon in a snackbar; the type is left to pick the announcement role. expect(substr_count($html, 'toBe(1) ->and($html)->not->toContain('text-inverse-success') ->toContain('data-md-toast-action') ->toContain('data-md-toast-dismiss') ->and($css->declarations(':is([data-md-toast-action], [data-md-toast-dismiss])')) ->toHaveKey('border-radius', 'var(--md-sys-shape-corner-full)') ->and($css->declarations(':is([data-md-toast-action], [data-md-toast-dismiss])::after'))->toMatchArray([ 'min-width' => 'var(--md-sys-measurement-space600)', 'min-height' => 'var(--md-sys-measurement-space600)', ]) ->and($css->declarations('[data-md-toast-dismiss]'))->toMatchArray([ 'width' => 'var(--md-sys-measurement-space500)', 'height' => 'var(--md-sys-measurement-space500)', ]); }); it('grows to M3\'s two-line height, and wraps the action below on a compact window', function () { $html = (string) $this->blade(''); $css = ComponentStylesheet::read('toast'); // SnackbarTokens: 48dp for one line, 68dp for two — a description is that second line. expect($html) ->toContain('x-bind:data-md-two-line="current.description ? \'\' : null"') ->toContain('x-bind:data-md-toast-wrap="current.description && current.action ? \'\' : null"') ->and($css->declarations('[data-md-toast-snackbar]'))->toHaveKey('min-height', 'var(--md-sys-measurement-space600)') ->and($css->declarations('[data-md-toast-snackbar][data-md-two-line]'))->toBe(['min-height' => '68px']) ->and($css->declarations('[data-md-toast-content][data-md-toast-wrap]', ['@media (width < 600px)']))->toBe(['flex-basis' => '100%']) ->and($css->declarations('[data-md-toast-action]'))->toHaveKey('height', 'var(--md-sys-measurement-space500)'); }); it('can sit at the start, growing its margin from medium', function () { $css = ComponentStylesheet::read('toast'); expect((string) $this->blade(''))->toContain('data-md-position="bottom-start"') ->and($css->declarations("[data-md-toast][data-md-position='bottom-start']"))->toBe(['justify-content' => 'flex-start']) ->and($css->declarations("[data-md-toast][data-md-position='bottom-start']", ['@media (width >= 600px)']))->toBe([ 'inset-inline-start' => 'var(--md-sys-measurement-space300)', ]); }); it('falls back to bottom for an unknown position', function () { expect((string) $this->blade(''))->toContain('data-md-position="bottom"'); }); it('marks the snackbar and its action for tests and styling', function () { $html = (string) $this->blade(''); expect($html) ->toMatch('/]*\bdata-md-toast-snackbar\b/') ->toMatch('/]*\bdata-md-toast-action\b[^>]*x-on:click="act\(\)"/') ->toMatch('/]*\bdata-md-toast-dismiss\b[^>]*x-on:click="dismiss\(\)"/'); }); it('keeps the live region in the page, polite, around the snackbar that comes and goes', function () { $html = (string) $this->blade(''); expect($html) ->toMatch('/]*x-data="materialSnackbar"[^>]*aria-live="polite"/') ->toContain('aria-atomic="true"') ->toContain("x-bind:role=\"current && (current.type === 'error' || current.type === 'warning') ? 'alert' : 'status'\"") ->and(substr_count($html, 'aria-live'))->toBe(1); }); it('lifts above a bottom bar, publishing its own height for a FAB to clear', function () { expect(ComponentStylesheet::read('toast')->declarations('[data-md-toast]'))->toMatchArray([ 'bottom' => 'calc(var(--material-bottom-bar, 0px) + var(--md-sys-measurement-space200))', ]); });