From 9c826e99f41d98e122cde0cd6b51afdc805fb7b7 Mon Sep 17 00:00:00 2001 From: Guillaume Roux Date: Wed, 4 Dec 2024 11:21:55 +0100 Subject: [PATCH] Allow `Field` in `Box` (#2926) This PR allows the usage of `Field` in `Box` to be able to put two field in a row. --- .../browserify-plugin/snap.manifest.json | 2 +- .../packages/browserify/snap.manifest.json | 2 +- .../src/interface/utils.test.tsx | 15 ++++++++++ .../src/permitted/createInterface.test.tsx | 7 +++-- .../src/jsx/components/form/Field.ts | 3 +- .../snaps-sdk/src/jsx/validation.test.tsx | 10 +++---- packages/snaps-sdk/src/jsx/validation.ts | 30 +++++++++---------- .../src/features/builder/utils.test.ts | 5 ++-- 8 files changed, 44 insertions(+), 30 deletions(-) diff --git a/packages/examples/packages/browserify-plugin/snap.manifest.json b/packages/examples/packages/browserify-plugin/snap.manifest.json index fed1f39649..741d09c663 100644 --- a/packages/examples/packages/browserify-plugin/snap.manifest.json +++ b/packages/examples/packages/browserify-plugin/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "4yLB19XYAdGgHBPFlVOzCkb/JUZCjSajPRSQWs+a3uE=", + "shasum": "d1js9Y4zJwk7n/e47V5fdMreo1FBtVKl4GtPhfckZrs=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/browserify/snap.manifest.json b/packages/examples/packages/browserify/snap.manifest.json index c650c852f4..5a4f36c49a 100644 --- a/packages/examples/packages/browserify/snap.manifest.json +++ b/packages/examples/packages/browserify/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "+0hxp1uhfCqe9KR+4RPDSPGHFTgRyGULKLn9XWwCmsY=", + "shasum": "VvMm+Zet1+q467UNweIuLLtYVoHoJdTeNjZn0xnEwO8=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snaps-controllers/src/interface/utils.test.tsx b/packages/snaps-controllers/src/interface/utils.test.tsx index 10403584d5..c00f9de819 100644 --- a/packages/snaps-controllers/src/interface/utils.test.tsx +++ b/packages/snaps-controllers/src/interface/utils.test.tsx @@ -242,6 +242,21 @@ describe('constructState', () => { }); }); + it('handles root level Field', () => { + const element = ( + + + + + + ); + + const result = constructState({}, element); + expect(result).toStrictEqual({ + foo: 'bar', + }); + }); + it('handles root level inputs with value', () => { const element = ( diff --git a/packages/snaps-rpc-methods/src/permitted/createInterface.test.tsx b/packages/snaps-rpc-methods/src/permitted/createInterface.test.tsx index 4550ec9a02..15e530e3a1 100644 --- a/packages/snaps-rpc-methods/src/permitted/createInterface.test.tsx +++ b/packages/snaps-rpc-methods/src/permitted/createInterface.test.tsx @@ -9,6 +9,7 @@ import { Form, Container, Footer, + Copyable, } from '@metamask/snaps-sdk/jsx'; import type { JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; @@ -140,7 +141,7 @@ describe('snap_createInterface', () => { error: { code: -32602, message: - 'Invalid params: At path: ui -- Expected type to be one of: "Address", "Bold", "Box", "Button", "Copyable", "Divider", "Dropdown", "RadioGroup", "FileInput", "Form", "Heading", "Input", "Image", "Italic", "Link", "Row", "Spinner", "Text", "Tooltip", "Checkbox", "Card", "Icon", "Selector", "Section", "Avatar", "Container", but received: undefined.', + 'Invalid params: At path: ui -- Expected type to be one of: "Address", "Bold", "Box", "Button", "Copyable", "Divider", "Dropdown", "RadioGroup", "Field", "FileInput", "Form", "Heading", "Input", "Image", "Italic", "Link", "Row", "Spinner", "Text", "Tooltip", "Checkbox", "Card", "Icon", "Selector", "Section", "Avatar", "Container", but received: undefined.', stack: expect.any(String), }, id: 1, @@ -179,7 +180,7 @@ describe('snap_createInterface', () => { ui: ( - + ) as JSXElement, @@ -190,7 +191,7 @@ describe('snap_createInterface', () => { error: { code: -32602, message: - 'Invalid params: At path: ui.props.children -- Expected type to be one of: "Address", "Bold", "Box", "Button", "Copyable", "Divider", "Dropdown", "RadioGroup", "FileInput", "Form", "Heading", "Input", "Image", "Italic", "Link", "Row", "Spinner", "Text", "Tooltip", "Checkbox", "Card", "Icon", "Selector", "Section", "Avatar", but received: "Field".', + 'Invalid params: At path: ui.props.children.props.children -- Expected the value to satisfy a union of `tuple | tuple | tuple | object | object | object | object | object | object`, but received: [object Object].', stack: expect.any(String), }, id: 1, diff --git a/packages/snaps-sdk/src/jsx/components/form/Field.ts b/packages/snaps-sdk/src/jsx/components/form/Field.ts index 067ab525f8..0493311842 100644 --- a/packages/snaps-sdk/src/jsx/components/form/Field.ts +++ b/packages/snaps-sdk/src/jsx/components/form/Field.ts @@ -32,8 +32,7 @@ export type FieldProps = { const TYPE = 'Field'; /** - * A field component, which is used to create a form field. This component can - * only be used as a child of the {@link Form} component. + * A field component, which is used to create a form field. * * @param props - The props of the component. * @param props.label - The label of the field. diff --git a/packages/snaps-sdk/src/jsx/validation.test.tsx b/packages/snaps-sdk/src/jsx/validation.test.tsx index 234c6f6b64..a341804f72 100644 --- a/packages/snaps-sdk/src/jsx/validation.test.tsx +++ b/packages/snaps-sdk/src/jsx/validation.test.tsx @@ -567,6 +567,11 @@ describe('BoxStruct', () => { foo bar , + + + + + , foo @@ -620,11 +625,6 @@ describe('BoxStruct', () => { alt , - - - - - , , diff --git a/packages/snaps-sdk/src/jsx/validation.ts b/packages/snaps-sdk/src/jsx/validation.ts index 3891bc6aeb..2e11dd7435 100644 --- a/packages/snaps-sdk/src/jsx/validation.ts +++ b/packages/snaps-sdk/src/jsx/validation.ts @@ -507,22 +507,6 @@ export const FieldStruct: Describe = element('Field', { children: FieldChildStruct, }); -/** - * A subset of JSX elements that are allowed as children of the Form component. - */ -export const FormChildStruct = children( - // eslint-disable-next-line @typescript-eslint/no-use-before-define - [FieldStruct, lazy(() => BoxChildStruct)], -) as unknown as Struct, null>; - -/** - * A struct for the {@link FormElement} type. - */ -export const FormStruct: Describe = element('Form', { - children: FormChildStruct, - name: string(), -}); - /** * A struct for the {@link BoldElement} type. */ @@ -584,6 +568,19 @@ export const BoxStruct: Describe = element('Box', { center: optional(boolean()), }); +/** + * A subset of JSX elements that are allowed as children of the Form component. + */ +export const FormChildStruct = BoxChildrenStruct; + +/** + * A struct for the {@link FormElement} type. + */ +export const FormStruct: Describe = element('Form', { + children: FormChildStruct, + name: string(), +}); + const FooterButtonStruct = refine(ButtonStruct, 'FooterButton', (value) => { if ( typeof value.props.children === 'string' || @@ -796,6 +793,7 @@ export const BoxChildStruct = typedUnion([ DividerStruct, DropdownStruct, RadioGroupStruct, + FieldStruct, FileInputStruct, FormStruct, HeadingStruct, diff --git a/packages/snaps-simulator/src/features/builder/utils.test.ts b/packages/snaps-simulator/src/features/builder/utils.test.ts index ca124f81be..da309a21bb 100644 --- a/packages/snaps-simulator/src/features/builder/utils.test.ts +++ b/packages/snaps-simulator/src/features/builder/utils.test.ts @@ -9,6 +9,7 @@ import { Copyable, Heading, Option, + Container, } from '@metamask/snaps-sdk/jsx'; import type { NodeModel } from '@minoru/react-dnd-treeview'; @@ -30,7 +31,7 @@ describe('isValidBoxChild', () => { }); it('returns false for invalid box children', () => { - const child = Field({ children: Input({ name: 'input' }) }); + const child = Container({ children: Input({ name: 'input' }) }); expect(isValidBoxChild(child)).toBe(false); }); }); @@ -80,7 +81,7 @@ describe('setElementChildren', () => { const element = Box({ children: [Text({ children: 'foo' }), Heading({ children: 'baz' })], }); - const children = Field({ children: Input({ name: 'input' }) }); + const children = Container({ children: Input({ name: 'input' }) }); const result = setElementChildren(element, children, isValidBoxChild); expect(result).toStrictEqual(element.props.children);