Say how many search results there are, in a combobox
M3's search accessibility page asks that results be announced when they appear and read as a list. The bar now wraps its input in a role="combobox" that carries aria-expanded and aria-controls — ARIA gives a bare textbox neither — the results container is a role="list", and a polite live region counts the results whenever the view's DOM settles. Plan step 13, finding IN-01. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
0adfb6fff4
commit
31663c48ba
@@ -5,11 +5,16 @@
|
||||
`empty` is shown instead when the slot renders nothing (say, "No shares match"). Results are
|
||||
usually `<x-list-item>`s with a `link`, or buttons: choosing one closes the view.
|
||||
|
||||
Docked under the bar from `sm`, full screen below it with a back arrow (resources/css/
|
||||
Docked under the bar from `medium`, full screen below it with a back arrow (resources/css/
|
||||
components/search.css); `docked` keeps it docked at every width. `placeholder` ("Search"),
|
||||
`label` (the input's name when it differs from the placeholder), leading `icon` (`search`), and
|
||||
a `trailing` slot for an avatar or icon buttons in the bar. Every other attribute reaches the
|
||||
`<input type="search">`. --}}
|
||||
`<input type="search">`.
|
||||
|
||||
The bar is a combobox: the wrapper around the input carries `role="combobox"` with
|
||||
`aria-expanded` and `aria-controls`, because ARIA gives a bare textbox neither. The results are
|
||||
a list, and a polite live region says how many of them there are whenever they change — M3 asks
|
||||
for both (docs/reference/m3/components-navigation-selection-inputs.md § Search). --}}
|
||||
|
||||
@props([
|
||||
'placeholder' => null,
|
||||
@@ -22,10 +27,16 @@
|
||||
$model = $attributes->wire('model')->value() ?: null;
|
||||
$placeholder ??= __('Search');
|
||||
$id = $attributes->get('id') ?? 'material-search-'.substr(md5($model.'|'.$placeholder), 0, 10);
|
||||
|
||||
$announce = [
|
||||
'none' => __('No results'),
|
||||
'one' => __('1 result'),
|
||||
'many' => __(':count results'),
|
||||
];
|
||||
@endphp
|
||||
|
||||
<div
|
||||
x-data="materialSearch({{ $docked ? 'true' : 'false' }})"
|
||||
x-data="materialSearch({{ $docked ? 'true' : 'false' }}, @js($announce))"
|
||||
x-on:keydown.escape="if (open) { $event.stopPropagation(); close(true); }"
|
||||
x-on:focusout="leave($event)"
|
||||
x-on:pointerdown.outside="close()"
|
||||
@@ -44,24 +55,30 @@
|
||||
<x-livewire-material::icon name="arrow_back" />
|
||||
</button>
|
||||
|
||||
<input
|
||||
{{ $attributes->except(['class', 'wire:key', 'id', 'placeholder', 'type']) }}
|
||||
x-ref="input"
|
||||
id="{{ $id }}"
|
||||
type="search"
|
||||
autocomplete="off"
|
||||
enterkeyhint="search"
|
||||
placeholder="{{ $placeholder }}"
|
||||
aria-label="{{ $label ?? $placeholder }}"
|
||||
<span
|
||||
data-search-field
|
||||
role="combobox"
|
||||
aria-haspopup="dialog"
|
||||
aria-controls="{{ $id }}-view"
|
||||
aria-expanded="false"
|
||||
x-bind:aria-expanded="open.toString()"
|
||||
x-on:focus="focused()"
|
||||
x-on:click="show()"
|
||||
x-on:input="show()"
|
||||
x-on:keydown.arrow-down.prevent="show(); $nextTick(() => step(1))"
|
||||
data-search-input
|
||||
/>
|
||||
>
|
||||
<input
|
||||
{{ $attributes->except(['class', 'wire:key', 'id', 'placeholder', 'type']) }}
|
||||
x-ref="input"
|
||||
id="{{ $id }}"
|
||||
type="search"
|
||||
autocomplete="off"
|
||||
enterkeyhint="search"
|
||||
placeholder="{{ $placeholder }}"
|
||||
aria-label="{{ $label ?? $placeholder }}"
|
||||
x-on:focus="focused()"
|
||||
x-on:click="show()"
|
||||
x-on:input="show()"
|
||||
x-on:keydown.arrow-down.prevent="show(); $nextTick(() => step(1))"
|
||||
data-search-input
|
||||
/>
|
||||
</span>
|
||||
|
||||
<button type="button" data-search-clear x-on:click="clear()" aria-label="{{ __('Clear') }}">
|
||||
<x-livewire-material::icon name="close" />
|
||||
@@ -83,9 +100,11 @@
|
||||
x-on:click="choose($event)"
|
||||
>
|
||||
@if ($slot->hasActualContent())
|
||||
<div data-search-results>{{ $slot }}</div>
|
||||
<div data-search-results role="list">{{ $slot }}</div>
|
||||
@elseif (isset($empty))
|
||||
<div data-search-results><p class="px-4 py-3 type-body-md text-on-surface-variant">{{ $empty }}</p></div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<span data-search-status class="sr-only" aria-live="polite" aria-atomic="true" x-text="announcement"></span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user