Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"graphql-redis-subscriptions/ioredis": "^5.6.0",
"@lingui/core": "5.1.2",
"@types/qs": "6.9.16",
"@types/react": "18.3.27",
"@types/react-dom": "18.3.0",
"@wyw-in-js/transform@npm:0.6.0": "patch:@wyw-in-js/transform@npm%3A0.7.0#~/.yarn/patches/@wyw-in-js-transform-npm-0.7.0-ba641dc99f.patch",
"@wyw-in-js/transform@npm:0.7.0": "patch:@wyw-in-js/transform@npm%3A0.7.0#~/.yarn/patches/@wyw-in-js-transform-npm-0.7.0-ba641dc99f.patch",
"@opentelemetry/api": "1.9.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,22 @@ const generateCommonEventsType = (
});
writer.writeLine(');');
writer.blankLine();
writer.writeLine('return new CustomEvent(eventType, {');
writer.writeLine('const event = new CustomEvent(eventType, {');
writer.indent(() => {
writer.writeLine('detail: eventData,');
});
writer.writeLine('}) as RemoteEvent<SerializedEventData>;');
writer.blankLine();
writer.writeLine('applySerializedEventProperties(');
writer.indent(() => {
writer.writeLine(
'event as unknown as Record<string, unknown>,',
);
writer.writeLine('eventData,');
});
writer.writeLine(');');
writer.blankLine();
writer.writeLine('return event;');
});
writer.writeLine('},');
});
Expand Down Expand Up @@ -400,11 +411,18 @@ export const generateRemoteElements = (
});

sourceFile.addImportDeclaration({
moduleSpecifier: '@/constants/SerializedEventData',
namedImports: [
'applySerializedEventTargetProperties',
{ name: 'SerializedEventData', isTypeOnly: true },
],
moduleSpecifier: '@/constants/applySerializedEventProperties',
namedImports: ['applySerializedEventProperties'],
});

sourceFile.addImportDeclaration({
moduleSpecifier: '@/constants/applySerializedEventTargetProperties',
namedImports: ['applySerializedEventTargetProperties'],
});

sourceFile.addImportDeclaration({
moduleSpecifier: '@/types/SerializedEventData',
namedImports: [{ name: 'SerializedEventData', isTypeOnly: true }],
});

const commonPropertyNames = new Set(Object.keys(commonProperties));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import {
createHtmlTagClickStory,
createHtmlTagFocusStory,
createHtmlTagPointerStory,
} from '@/__stories__/shared/test-utils/createHtmlElementStory';

const meta: Meta<typeof FrontComponentRenderer> = {
Expand All @@ -27,3 +28,7 @@ export const Click = createHtmlTagClickStory({
export const FocusBlur = createHtmlTagFocusStory({
frontComponentBundleName: 'svg-focus-blur',
});

export const Pointer = createHtmlTagPointerStory({
frontComponentBundleName: 'svg-pointer',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
EventLog,
useEventLog,
} from '@/__stories__/shared/front-components/event-log';
import { FrontComponentCard } from '@/__stories__/shared/front-components/front-component-card';
import { SVG_ROOT_STYLE } from '@/__stories__/shared/front-components/styles';
import { useState } from 'react';
import { defineFrontComponent } from 'twenty-sdk/define';

const SvgPointerFrontComponent = () => {
const [interactionCount, setInteractionCount] = useState(0);
const [pointerCoordinates, setPointerCoordinates] = useState('');
const { entries, pushEvent } = useEventLog();

return (
<FrontComponentCard title="svg:pointer">
<svg
data-testid="subject"
viewBox="0 0 200 120"
onPointerDown={(event) => {
setInteractionCount((previous) => previous + 1);
setPointerCoordinates(`${event.clientX},${event.clientY}`);
pushEvent(event);
}}
onPointerMove={(event) => {
pushEvent(event);
}}
tabIndex={0}
style={SVG_ROOT_STYLE}
>
<rect x="20" y="20" width="160" height="80" fill="#2563eb" />
</svg>
<span data-testid="front-component-value">{interactionCount}</span>
<span data-testid="pointer-coordinates">{pointerCoordinates}</span>
<EventLog entries={entries} />
</FrontComponentCard>
);
};

export default defineFrontComponent({
universalIdentifier: 'fc-svg-c-pointer-0000000-0000-0000-0000-000000000021',
name: 'svg-pointer-front-component',
description: 'Front component covering pointer events on <svg>',
component: SvgPointerFrontComponent,
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ export type LoggedEventEntry = {
scrollLeft?: number;
deltaX?: number;
deltaY?: number;
clientX?: number;
clientY?: number;
offsetX?: number;
offsetY?: number;
movementX?: number;
movementY?: number;
};

const EVENT_LOG_STYLE = {
Expand Down Expand Up @@ -216,6 +222,36 @@ export const useEventLog = () => {
entry.deltaY = deltaY;
}

const clientX = pickFromRecords(records, 'clientX', isNumberValue);
if (isDefined(clientX)) {
entry.clientX = clientX;
}

const clientY = pickFromRecords(records, 'clientY', isNumberValue);
if (isDefined(clientY)) {
entry.clientY = clientY;
}

const offsetX = pickFromRecords(records, 'offsetX', isNumberValue);
if (isDefined(offsetX)) {
entry.offsetX = offsetX;
}

const offsetY = pickFromRecords(records, 'offsetY', isNumberValue);
if (isDefined(offsetY)) {
entry.offsetY = offsetY;
}

const movementX = pickFromRecords(records, 'movementX', isNumberValue);
if (isDefined(movementX)) {
entry.movementX = movementX;
}

const movementY = pickFromRecords(records, 'movementY', isNumberValue);
if (isDefined(movementY)) {
entry.movementY = movementY;
}

return [...previousEntries, entry];
});
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { type StoryObj } from '@storybook/react-vite';
import { userEvent, within } from 'storybook/test';
import { expect, fireEvent, userEvent, waitFor, within } from 'storybook/test';

import { type FrontComponentRenderer } from '@/host/components/FrontComponentRenderer';
import { expectEventLogged } from '@/__stories__/shared/test-utils/matchers/expectEventLogged';
import { expectFrontComponentMounted } from '@/__stories__/shared/test-utils/matchers/expectFrontComponentMounted';
import { runFrontComponentStory } from '@/__stories__/shared/test-utils/runFrontComponentStory';
import { type FrontComponentRenderer } from '@/host/components/FrontComponentRenderer';

type Story = StoryObj<typeof FrontComponentRenderer>;

Expand All @@ -30,6 +30,33 @@ export const createHtmlTagClickStory = ({
},
});

export const createHtmlTagPointerStory = ({
frontComponentBundleName,
}: CreateHtmlTagStoryParams): Story =>
runFrontComponentStory({
frontComponentBundleName,
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

await expectFrontComponentMounted(canvas);

const subject = await canvas.findByTestId('subject');

fireEvent.pointerDown(subject, { clientX: 123, clientY: 45 });

await expectEventLogged({
canvas,
matcher: { type: 'pointerdown', clientX: 123, clientY: 45 },
});

await waitFor(() => {
const coordinates = canvas.getByTestId('pointer-coordinates');

expect(coordinates.textContent).toBe('123,45');
});
},
});

export const createHtmlTagFocusStory = ({
frontComponentBundleName,
}: CreateHtmlTagStoryParams): Story =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ type LoggedEventMatcher = {
ctrlKey?: boolean;
metaKey?: boolean;
altKey?: boolean;
clientX?: number;
clientY?: number;
offsetX?: number;
offsetY?: number;
movementX?: number;
movementY?: number;
files?: { name?: string; type?: string }[];
};

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { type SerializedEventData } from '@/types/SerializedEventData';

const SERIALIZED_EVENT_PROPERTY_KEYS = [
'altKey',
'ctrlKey',
'metaKey',
'shiftKey',
'clientX',
'clientY',
'x',
'y',
'pageX',
'pageY',
'screenX',
'screenY',
'offsetX',
'offsetY',
'movementX',
'movementY',
'button',
'buttons',
'pointerId',
'pointerType',
'pressure',
'tangentialPressure',
'tiltX',
'tiltY',
'twist',
'width',
'height',
'isPrimary',
'key',
'code',
'repeat',
'deltaX',
'deltaY',
'deltaZ',
'deltaMode',
] as const satisfies readonly (keyof SerializedEventData)[];

export const applySerializedEventProperties = (
event: Record<string, unknown>,
eventData: SerializedEventData,
): void => {
for (const key of SERIALIZED_EVENT_PROPERTY_KEYS) {
if (key in eventData) {
event[key] = eventData[key];
}
}
};
Loading
Loading