a11y(transcription): hide visible @mention empty-state from AT and fold empty-query check
Round-4 polish from Leonie (S-2), Felix (#3), Sara (#4): - Add aria-hidden="true" to the visible empty-state <p> so VoiceOver does not double-announce — the persistent sr-only live region is now the sole AT source of truth (NVDA already de-duped, VoiceOver did not). - Extract `searchQuery.trim() === ''` into an `isQueryEmpty` $derived; both the announcer branch and the visible empty-state branch now read from the single intent-named alias. - Cover the singular branch of the persistent live region (1 item -> "1 Person gefunden" / "1 person found" / "1 persona encontrada"). Plural was already covered; this closes the missing-branch gap. - Extend the existing "no aria-live on visible <p>" test to also assert aria-hidden="true" so a regression on the AT-source-of-truth contract goes red immediately. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -198,7 +198,25 @@ describe('MentionDropdown — search input', () => {
|
||||
expect(live!.textContent ?? '').toContain('3');
|
||||
});
|
||||
|
||||
it('keeps the visible empty-state copy without its own aria-live (the persistent live region announces; Leonie #3 on PR #629)', async () => {
|
||||
it('announces the singular form when exactly one item is present (Sara #4 on PR #629)', async () => {
|
||||
render(MentionDropdown, {
|
||||
props: {
|
||||
model: baseModel({
|
||||
items: [makePerson('p1', 'Anna Schmidt')]
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const listbox = document.querySelector('[role="listbox"]');
|
||||
expect(listbox).not.toBeNull();
|
||||
const live = listbox!.querySelector('p[aria-live="polite"]');
|
||||
expect(live).not.toBeNull();
|
||||
// Singular branch — "1 Person gefunden" / "1 person found" / "1 persona encontrada"
|
||||
// (locale-dependent; resolved via the Paraglide message helper).
|
||||
expect(live!.textContent ?? '').toContain(m.person_mention_results_count_singular());
|
||||
});
|
||||
|
||||
it('keeps the visible empty-state copy without its own aria-live and hides it from AT (Leonie #3 on PR #629 round 3; Leonie S-2 round 4)', async () => {
|
||||
render(MentionDropdown, { props: { model: baseModel(), editorQuery: 'WdG' } });
|
||||
|
||||
// Visible empty-state <p> exists with the empty-result copy ...
|
||||
@@ -208,6 +226,10 @@ describe('MentionDropdown — search input', () => {
|
||||
// ... but it must NOT carry its own aria-live (the persistent sr-only
|
||||
// region above the conditional is the announcer now).
|
||||
expect(empty!.hasAttribute('aria-live')).toBe(false);
|
||||
// ... and it MUST be hidden from screen readers via aria-hidden="true"
|
||||
// so VoiceOver does not double-announce (the persistent sr-only region
|
||||
// is the sole AT source of truth). Leonie S-2 on PR #629 round 4.
|
||||
expect(empty!.getAttribute('aria-hidden')).toBe('true');
|
||||
});
|
||||
|
||||
it('renders the magnifier icon at h-5 w-5 with text-ink-2 (Leonie BLOCKER on PR #629)', async () => {
|
||||
|
||||
Reference in New Issue
Block a user