Site builder: security & data-loss hardening + unified asset picker + audit backlog #3
@@ -48,4 +48,35 @@ describe('clickableProps', () => {
|
||||
expect(onActivate).not.toHaveBeenCalled();
|
||||
expect(e.preventDefault).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('onKeyDown activates when the event target is the row itself', () => {
|
||||
const onActivate = vi.fn();
|
||||
const props = clickableProps(onActivate);
|
||||
const rowEl = {};
|
||||
const e = {
|
||||
key: 'Enter',
|
||||
target: rowEl,
|
||||
currentTarget: rowEl,
|
||||
preventDefault: vi.fn(),
|
||||
} as unknown as Parameters<ReturnType<typeof clickableProps>['onKeyDown']>[0];
|
||||
props.onKeyDown(e);
|
||||
expect(onActivate).toHaveBeenCalledTimes(1);
|
||||
expect(e.preventDefault).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('onKeyDown ignores a bubbled event from a nested control (e.g. a child button)', () => {
|
||||
const onActivate = vi.fn();
|
||||
const props = clickableProps(onActivate);
|
||||
const rowEl = {};
|
||||
const childButtonEl = {};
|
||||
const e = {
|
||||
key: 'Enter',
|
||||
target: childButtonEl,
|
||||
currentTarget: rowEl,
|
||||
preventDefault: vi.fn(),
|
||||
} as unknown as Parameters<ReturnType<typeof clickableProps>['onKeyDown']>[0];
|
||||
props.onKeyDown(e);
|
||||
expect(onActivate).not.toHaveBeenCalled();
|
||||
expect(e.preventDefault).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,12 @@ export function clickableProps(onActivate: () => void): ClickableProps {
|
||||
tabIndex: 0,
|
||||
onClick: onActivate,
|
||||
onKeyDown: (e: KeyboardEvent) => {
|
||||
// Ignore keydown events that bubbled up from a nested interactive
|
||||
// element (e.g. a child <button>). Handling only events targeted at
|
||||
// this element lets the browser's native "click" synthesis for a
|
||||
// focused child control proceed instead of being hijacked by the
|
||||
// row's own Enter/Space activation.
|
||||
if (e.target !== e.currentTarget) return;
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
onActivate();
|
||||
|
||||
Reference in New Issue
Block a user