Files
twenty/packages/twenty-front-component-renderer/src/host/utils/__tests__/createDropTargetGuardProps.test.ts
T
Raphaël Bosi eb651180aa Widen the front component event allow-list (#22616)
Front components are third-party UI that runs in a sandboxed worker, so
every DOM event reaching them has to be on an explicit allow-list. That
list was small: mostly click, focus and pointer events.

This adds touch, drag and drop, focusin/focusout,
animationend/transitionend and scrollend, plus load/error on `<img>` and
toggle on `<details>`/`<dialog>`.

Two of them need the host to do more than forward the event:

- react-dom has no `onFocusIn`/`onFocusOut` props, so the host attaches
those two with `addEventListener` instead.
- a browser only fires `drop` on an element whose `dragover` default was
prevented, and the component's own `preventDefault` arrives too late
across the async worker boundary. The host prevents it synchronously as
soon as the component declares either handler.

Touch events carry their coordinates on `changedTouches`, so the first
touch fills the existing coordinate fields.

Still not crossing, since each would need a new serialized field: touch
lists, `animationName`/`propertyName`/`elapsedTime`, toggle `newState`
and `dataTransfer`.

The diff also renames a few things it touches (`filterProps` and
`EventToReact` in particular) so the host-side event path reads in
order.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22616?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-07-20 18:24:21 +02:00

45 lines
1.6 KiB
TypeScript

import { createDropTargetGuardProps } from '../createDropTargetGuardProps';
describe('createDropTargetGuardProps', () => {
it('should return undefined without drag over or drop handlers', () => {
expect(createDropTargetGuardProps({ onClick: jest.fn() })).toBeUndefined();
});
it('should prevent default on drag over and forward to the remote handler', () => {
const onDragOver = jest.fn();
const props = createDropTargetGuardProps({ onDragOver });
const event = { preventDefault: jest.fn() };
(props?.onDragOver as (event: unknown) => void)(event);
expect(event.preventDefault).toHaveBeenCalledTimes(1);
expect(onDragOver).toHaveBeenCalledWith(event);
});
it('should prevent default on drag over when only a drop handler exists', () => {
const onDrop = jest.fn();
const props = createDropTargetGuardProps({ onDrop });
const dragOverEvent = { preventDefault: jest.fn() };
const dropEvent = { preventDefault: jest.fn() };
(props?.onDragOver as (event: unknown) => void)(dragOverEvent);
(props?.onDrop as (event: unknown) => void)(dropEvent);
expect(dragOverEvent.preventDefault).toHaveBeenCalledTimes(1);
expect(dropEvent.preventDefault).toHaveBeenCalledTimes(1);
expect(onDrop).toHaveBeenCalledWith(dropEvent);
});
it('should prevent default without forwarding to a non-function handler', () => {
const props = createDropTargetGuardProps({
onDragOver: 'alert(1)',
onDrop: jest.fn(),
});
const event = { preventDefault: jest.fn() };
(props?.onDragOver as (event: unknown) => void)(event);
expect(event.preventDefault).toHaveBeenCalledTimes(1);
});
});