diff --git a/docs/source/about/changelog.rst b/docs/source/about/changelog.rst index 7587273ec..3db857a26 100644 --- a/docs/source/about/changelog.rst +++ b/docs/source/about/changelog.rst @@ -25,14 +25,15 @@ Unreleased **Removed** -- :pull:`840` - remove ``IDOM_FEATURE_INDEX_AS_DEFAULT_KEY`` option +- :pull:`840` - Remove ``IDOM_FEATURE_INDEX_AS_DEFAULT_KEY`` option - :pull:`835` - ``serve_static_files`` option from backend configuration **Added** -- :pull:`835` - ability to customize the ```` element of IDOM's built-in client. +- :pull:`835` - Ability to customize the ```` element of IDOM's built-in client. - :pull:`835` - ``vdom_to_html`` utility function. - :pull:`843` - Ability to subscribe to changes that are made to mutable options. +- :pull:`699` - Support for form element serialization **Fixed** diff --git a/src/client/packages/idom-client-react/src/event-to-object.js b/src/client/packages/idom-client-react/src/event-to-object.js index b11e65366..e35f652e5 100644 --- a/src/client/packages/idom-client-react/src/event-to-object.js +++ b/src/client/packages/idom-client-react/src/event-to-object.js @@ -49,6 +49,20 @@ const elementTransformCategories = { return {}; } }, + hasElements: (element) => { + const { elements } = element; + const indices = [...Array(elements.length).keys()]; + return { + elements: indices.map((index) => serializeDomElement(elements[index])), + }; + }, + hasName: (element) => { + const { name } = element; + // In some edge cases, "name" may not be a string. For example, in the case of + // `
`, the "name" attribute of the `
` will + // be the `` element. + return typeof name === "string" ? { name } : {}; + }, }; function defaultElementTransform(element) { @@ -69,6 +83,22 @@ const elementTagCategories = { ], hasCurrentTime: ["AUDIO", "VIDEO"], hasFiles: ["INPUT"], + hasElements: ["FORM"], + hasName: [ + "BUTTON", + "FORM", + "FIELDSET", + "IFRAME", + "INPUT", + "KEYGEN", + "OBJECT", + "OUTPUT", + "SELECT", + "TEXTAREA", + "MAP", + "META", + "PARAM", + ], }; const elementTransforms = {}; diff --git a/src/client/packages/idom-client-react/tests/event-to-object.test.js b/src/client/packages/idom-client-react/tests/event-to-object.test.js index 3dd3852fd..596a9dd4e 100644 --- a/src/client/packages/idom-client-react/tests/event-to-object.test.js +++ b/src/client/packages/idom-client-react/tests/event-to-object.test.js @@ -4,22 +4,40 @@ import * as assert from "uvu/assert"; import { serializeEvent } from "../src/event-to-object.js"; import "./tooling/setup.js"; -function assertEqualSerializedEventData(eventData, expectedSerializedData) { - const mockBoundingRect = { - left: 0, - top: 0, - right: 0, - bottom: 0, - x: 0, - y: 0, - width: 0, - }; +const mockBoundingRect = { + left: 0, + top: 0, + right: 0, + bottom: 0, + x: 0, + y: 0, + width: 0, +}; - const mockElement = { - tagName: null, - getBoundingClientRect: () => mockBoundingRect, - }; +const mockElement = { + tagName: null, + getBoundingClientRect: () => mockBoundingRect, +}; +const allTargetData = { + files: [ + { + lastModified: 0, + name: "something", + type: "some-type", + size: 0, + }, + ], + value: "something", + currentTime: 35, + tagName: null, // overwritten in tests + elements: [ + { ...mockElement, tagName: "INPUT", value: "first" }, + { ...mockElement, tagName: "INPUT", value: "second" }, + ], +}; + +function assertEqualSerializedEventData(eventData, expectedSerializedData) { const commonEventData = { target: mockElement, currentTarget: mockElement, @@ -38,20 +56,6 @@ function assertEqualSerializedEventData(eventData, expectedSerializedData) { ); } -const allTargetData = { - files: [ - { - lastModified: 0, - name: "something", - type: "some-type", - size: 0, - }, - ], - value: "something", - currentTime: 35, - tagName: null, // overwritten in tests -}; - [ { case: "adds 'files' and 'value' attributes for INPUT if type=file", @@ -76,6 +80,24 @@ const allTargetData = { tagName, output: { target: { currentTime: allTargetData.currentTime } }, })), + ...["FORM"].map((tagName) => ({ + case: `adds 'elements' attribute for ${tagName} element`, + tagName, + output: { + target: { + elements: [ + { + value: "first", + boundingClientRect: mockBoundingRect, + }, + { + value: "second", + boundingClientRect: mockBoundingRect, + }, + ], + }, + }, + })), ].forEach((expectation) => { test(`serializeEvent() ${expectation.case}`, () => { const eventData = { @@ -113,6 +135,7 @@ const allEventData = { clientX: "clientX", clientY: "clientY", ctrlKey: "ctrlKey", + form: "form", metaKey: "metaKey", pageX: "pageX", pageY: "pageY",