fix(chronik): split Für-dich row markup — Dismiss is sibling of link, not nested
HTML5 forbids interactive content (<button>, <a>, <input>...) as descendants of <a>. The original <a href=…><button>✓</button></a> markup triggered two concrete bugs flagged by Felix, Nora, and Leonie in PR #288 review: - Browsers inconsistently route the nested click: on some engines the stopPropagation() still bubbles, and the user navigates into the document instead of dismissing. - The senior audience (60+) tap-selects with a slight drag, and the OS treats the interaction as anchor vs. button inconsistently — a reproducible usability failure Leonie has seen in testing before. Refactor to the Option-C layout from issue #285 comment #3573: outer <li> flex container, <a> wrapping avatar + body + time, <button> as a sibling. Independent focus stops, invalid-HTML gone, no behavioural regression. A new spec locks the invariant: `dismiss.closest('a')` must be null. Part of #285, address PR #288 review. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -113,4 +113,17 @@ describe('ChronikFuerDichBox', () => {
|
||||
expect(onMarkRead).toHaveBeenCalledTimes(1);
|
||||
expect(onMarkRead.mock.calls[0][0]).toEqual(n);
|
||||
});
|
||||
|
||||
it('Dismiss button is a sibling of the document link, never nested inside <a>', async () => {
|
||||
render(ChronikFuerDichBox, {
|
||||
unread: [notif({ id: 'x' })],
|
||||
onMarkRead: vi.fn(),
|
||||
onMarkAllRead: vi.fn()
|
||||
});
|
||||
const dismiss = document.querySelector('[data-testid="chronik-fuerdich-dismiss"]');
|
||||
expect(dismiss).not.toBeNull();
|
||||
// HTML spec forbids interactive content descendants of <a>.
|
||||
// Prevents the senior-audience tap-drag bug flagged by Leonie.
|
||||
expect(dismiss?.closest('a')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user