fix(a11y): Datei-Feld nativ „required“, ungültige Felder verweisen auf die Fehlerzeile
- logo-dialog.ts: das Datei-Feld trägt `required` statt `aria-required` (für ein Datei-Feld gilt aria-required nicht; das Formular hat novalidate und prüft selbst, es ändert sich nichts am Ablauf). - logo-dialog.ts und publish-dialog.ts: die Fehlerzeile hat eine ID (logo-upload-error, publish-error); solange ein Feld ungültig ist, steht sie in seinem aria-describedby, hinter dem Hinweis des Feldes (Datei, Anzeigename, Tags). Ein Screenreader liest den Fehler so mit dem Feld vor. Beim nächsten Senden fällt der Verweis mit aria-invalid weg, der Hinweis bleibt. Im Dialog „Logo hochladen“ löscht auch eine neue Dateiwahl die Markierung aller Felder (sie löscht ja den Fehlertext, auf den sie verwiesen). - editor/dom.ts: setFieldInvalid(control, errorId, invalid) für beide Dialoge; eigene Tests (dom.test.ts). - Tests: required statt aria-required; ID einmalig; je Feld Verweis samt Hinweis in Reihenfolge, Rücknahme, neue Dateiwahl; der vorhandene Hilfsfall `invalid` beider Dialoge prüft den Verweis für jedes Feld mit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
29c9b2efaf
commit
8167a18857
6 files changed
+216
-10
No files matched your search
@@ -0,0 +1,58 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { el, setFieldInvalid } from './dom';
|
||||
|
||||
describe('setFieldInvalid', () => {
|
||||
const field = (describedBy?: string) => el('input', { 'aria-describedby': describedBy });
|
||||
|
||||
it('markiert das Feld und verweist auf die Fehlerzeile', () => {
|
||||
const input = field();
|
||||
setFieldInvalid(input, 'fehler', true);
|
||||
expect(input.getAttribute('aria-invalid')).toBe('true');
|
||||
expect(input.getAttribute('aria-describedby')).toBe('fehler');
|
||||
});
|
||||
|
||||
it('der Hinweis des Feldes bleibt davor stehen', () => {
|
||||
const input = field('hinweis');
|
||||
setFieldInvalid(input, 'fehler', true);
|
||||
expect(input.getAttribute('aria-describedby')).toBe('hinweis fehler');
|
||||
});
|
||||
|
||||
it('mehrfach markiert verweist es nur einmal auf die Fehlerzeile', () => {
|
||||
const input = field('a b');
|
||||
setFieldInvalid(input, 'fehler', true);
|
||||
setFieldInvalid(input, 'fehler', true);
|
||||
expect(input.getAttribute('aria-describedby')).toBe('a b fehler');
|
||||
});
|
||||
|
||||
it('zurücknehmen entfernt Markierung und Verweis, die Hinweise bleiben', () => {
|
||||
const input = field('hinweis');
|
||||
setFieldInvalid(input, 'fehler', true);
|
||||
setFieldInvalid(input, 'fehler', false);
|
||||
expect(input.hasAttribute('aria-invalid')).toBe(false);
|
||||
expect(input.getAttribute('aria-describedby')).toBe('hinweis');
|
||||
});
|
||||
|
||||
it('ohne Hinweis verschwindet aria-describedby ganz', () => {
|
||||
const input = field();
|
||||
setFieldInvalid(input, 'fehler', true);
|
||||
setFieldInvalid(input, 'fehler', false);
|
||||
expect(input.hasAttribute('aria-describedby')).toBe(false);
|
||||
expect(input.hasAttribute('aria-invalid')).toBe(false);
|
||||
});
|
||||
|
||||
it('ein Feld, das nie markiert war, bleibt beim Zurücknehmen unberührt', () => {
|
||||
const plain = field();
|
||||
setFieldInvalid(plain, 'fehler', false);
|
||||
expect(plain.getAttributeNames()).toEqual([]);
|
||||
const withHint = field('hinweis');
|
||||
setFieldInvalid(withHint, 'fehler', false);
|
||||
expect(withHint.getAttribute('aria-describedby')).toBe('hinweis');
|
||||
});
|
||||
|
||||
it('eine andere Fehlerzeile bleibt, wo sie ist (nur die genannte ID wird verwaltet)', () => {
|
||||
const input = field('hinweis anderer');
|
||||
setFieldInvalid(input, 'fehler', true);
|
||||
setFieldInvalid(input, 'fehler', false);
|
||||
expect(input.getAttribute('aria-describedby')).toBe('hinweis anderer');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user