feat(document-picker): optional inputId prop for external label wiring (#795)
The input always carries an id (generated doc-picker-input-{uid} default,
mirroring the listbox id scheme). When a caller passes inputId it wires a
visible <label for> — the aria-label fallback is dropped then so the
visible label wins. JourneyAddBar stays untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,17 +10,21 @@ import {
|
|||||||
interface Props {
|
interface Props {
|
||||||
alreadyAddedIds?: Set<string>;
|
alreadyAddedIds?: Set<string>;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
/** Set when a visible <label for> is wired externally — replaces the aria-label fallback. */
|
||||||
|
inputId?: string;
|
||||||
onSelect: (doc: DocumentOption) => void;
|
onSelect: (doc: DocumentOption) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
alreadyAddedIds = new Set(),
|
alreadyAddedIds = new Set(),
|
||||||
placeholder = m.journey_add_document(),
|
placeholder = m.journey_add_document(),
|
||||||
|
inputId = undefined,
|
||||||
onSelect
|
onSelect
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const uid = $props.id();
|
const uid = $props.id();
|
||||||
const listboxId = `doc-picker-listbox-${uid}`;
|
const listboxId = `doc-picker-listbox-${uid}`;
|
||||||
|
const resolvedInputId = $derived(inputId ?? `doc-picker-input-${uid}`);
|
||||||
|
|
||||||
const picker = createDocumentTypeahead();
|
const picker = createDocumentTypeahead();
|
||||||
|
|
||||||
@@ -82,7 +86,8 @@ function handleKeydown(e: KeyboardEvent) {
|
|||||||
type="text"
|
type="text"
|
||||||
role="combobox"
|
role="combobox"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
aria-label={placeholder}
|
id={resolvedInputId}
|
||||||
|
aria-label={inputId ? undefined : placeholder}
|
||||||
aria-expanded={picker.isOpen}
|
aria-expanded={picker.isOpen}
|
||||||
aria-controls={picker.isOpen && !picker.loading && !picker.error && picker.results.length > 0
|
aria-controls={picker.isOpen && !picker.loading && !picker.error && picker.results.length > 0
|
||||||
? listboxId
|
? listboxId
|
||||||
|
|||||||
@@ -241,3 +241,21 @@ describe('DocumentPickerDropdown — ARIA listbox integrity', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('DocumentPickerDropdown — external label wiring (#795)', () => {
|
||||||
|
it('renders a generated default id on the input and keeps the aria-label fallback', async () => {
|
||||||
|
render(DocumentPickerDropdown, { onSelect: vi.fn() });
|
||||||
|
|
||||||
|
const input = page.getByRole('combobox').element() as HTMLInputElement;
|
||||||
|
expect(input.id).toMatch(/^doc-picker-input-/);
|
||||||
|
expect(input.getAttribute('aria-label')).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the provided inputId and drops the aria-label so an external label wins', async () => {
|
||||||
|
render(DocumentPickerDropdown, { onSelect: vi.fn(), inputId: 'story-doc-picker' });
|
||||||
|
|
||||||
|
const input = page.getByRole('combobox').element() as HTMLInputElement;
|
||||||
|
expect(input.id).toBe('story-doc-picker');
|
||||||
|
expect(input.getAttribute('aria-label')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user