fix(front-component-renderer): add target shim on serialized events for React compatibility
https://sonarly.com/issue/35773?type=bug Form controls (inputs, textareas, checkboxes, selects) in Front Components don't respond to user input because the Remote DOM architecture serializes events differently than standard React events. Fix: Added a `target` property to serialized events in Front Components to enable more React-compatible event handling patterns. **What changed:** 1. Added `SerializedEventTarget` type in `SerializedEventData.ts` containing form-related properties (`value`, `checked`, `scrollTop`, etc.) 2. Added `target?: SerializedEventTarget` to `SerializedEventData` type 3. Modified `serializeEvent()` in `createHtmlHostWrapper.ts` to populate the `target` object with form-related properties when a DOM target exists **Why this helps:** Before this fix, developers had to use the awkward pattern: ```typescript onChange={(event) => { const detail = (event as unknown as { detail: { value?: string } }).detail; setTextValue(detail?.value ?? ''); }} ``` After this fix, developers can use a pattern closer to standard React: ```typescript onChange={(event) => { setTextValue((event.detail as any).target?.value ?? ''); }} ``` **Limitation:** This is a partial fix. The ideal pattern `event.target.value` still won't work because `event.target` in Remote DOM refers to the remote element, not the serialized event data. A complete fix would require changes to how @remote-dom/react constructs events, which is an upstream dependency concern. Documentation should be updated to show the correct pattern for Front Component event handling. **Backward compatibility:** The existing `event.detail.value` pattern continues to work - the `target` property is additive.
This commit is contained in:
@@ -1,3 +1,17 @@
|
||||
export type SerializedEventTarget = {
|
||||
value?: string;
|
||||
checked?: boolean;
|
||||
scrollTop?: number;
|
||||
scrollLeft?: number;
|
||||
currentTime?: number;
|
||||
duration?: number;
|
||||
paused?: boolean;
|
||||
ended?: boolean;
|
||||
volume?: number;
|
||||
muted?: boolean;
|
||||
playbackRate?: number;
|
||||
};
|
||||
|
||||
export type SerializedEventData = {
|
||||
type: string;
|
||||
altKey?: boolean;
|
||||
@@ -34,4 +48,5 @@ export type SerializedEventData = {
|
||||
volume?: number;
|
||||
muted?: boolean;
|
||||
playbackRate?: number;
|
||||
target?: SerializedEventTarget;
|
||||
};
|
||||
|
||||
@@ -135,6 +135,21 @@ const serializeEvent = (event: unknown): SerializedEventData => {
|
||||
if ('playbackRate' in target && typeof target.playbackRate === 'number') {
|
||||
serialized.playbackRate = target.playbackRate;
|
||||
}
|
||||
|
||||
// Add target property for React-compatible event patterns (event.detail.target.value)
|
||||
serialized.target = {
|
||||
value: serialized.value,
|
||||
checked: serialized.checked,
|
||||
scrollTop: serialized.scrollTop,
|
||||
scrollLeft: serialized.scrollLeft,
|
||||
currentTime: serialized.currentTime,
|
||||
duration: serialized.duration,
|
||||
paused: serialized.paused,
|
||||
ended: serialized.ended,
|
||||
volume: serialized.volume,
|
||||
muted: serialized.muted,
|
||||
playbackRate: serialized.playbackRate,
|
||||
};
|
||||
}
|
||||
|
||||
return serialized;
|
||||
|
||||
Reference in New Issue
Block a user