diff --git a/packages/examples/packages/browserify-plugin/snap.manifest.json b/packages/examples/packages/browserify-plugin/snap.manifest.json index e0c764b96e..beb24fd426 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": "/Q9cCxwaArV8k2VM4puQEsbni/bpNbcPEUi1s+XOL/U=", + "shasum": "N8UDq+EKKrGSUr/xn+g8Fn0ebj34zd+6Urgv1S7pzuM=", "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 649c3152ba..74cb179e35 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": "+fLeOqcKGPU0z+W2NcYDN9RwCl1ft1avMUe5fG1SUX0=", + "shasum": "j2B/JJ/tTdTWMJo+c5S8vZpnt4pRk9Xp3MhYBmDaz1s=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snaps-sdk/src/jsx/component.ts b/packages/snaps-sdk/src/jsx/component.ts index 43ca510f4e..b14ff0918a 100644 --- a/packages/snaps-sdk/src/jsx/component.ts +++ b/packages/snaps-sdk/src/jsx/component.ts @@ -57,6 +57,11 @@ export type Nestable = Type | Nestable[]; */ export type SnapsChildren = Nestable; +/** + * A type type that can be a generic JSX element, a boolean, or null. + */ +export type GenericSnapChildren = GenericSnapElement | boolean | null; + /** * A JSX node, which can be an element, a string, null, or an array of nodes. */ diff --git a/packages/snaps-sdk/src/jsx/components/form/Field.test.tsx b/packages/snaps-sdk/src/jsx/components/form/Field.test.tsx index a5186c286a..9098b07035 100644 --- a/packages/snaps-sdk/src/jsx/components/form/Field.test.tsx +++ b/packages/snaps-sdk/src/jsx/components/form/Field.test.tsx @@ -322,4 +322,30 @@ describe('Field', () => { }, }); }); + + it('renders a field with a conditional', () => { + const result = ( + + + {false && } + + ); + + expect(result).toStrictEqual({ + type: 'Field', + key: null, + props: { + children: [ + { + type: 'Input', + key: null, + props: { + name: 'foo', + }, + }, + false, + ], + }, + }); + }); }); diff --git a/packages/snaps-sdk/src/jsx/components/form/Field.ts b/packages/snaps-sdk/src/jsx/components/form/Field.ts index df9c2ad804..067ab525f8 100644 --- a/packages/snaps-sdk/src/jsx/components/form/Field.ts +++ b/packages/snaps-sdk/src/jsx/components/form/Field.ts @@ -1,4 +1,4 @@ -import type { GenericSnapElement } from '../../component'; +import type { GenericSnapChildren } from '../../component'; import { createSnapComponent } from '../../component'; import type { CheckboxElement } from './Checkbox'; import type { DropdownElement } from './Dropdown'; @@ -18,9 +18,9 @@ export type FieldProps = { label?: string | undefined; error?: string | undefined; children: - | [InputElement, GenericSnapElement] - | [GenericSnapElement, InputElement] - | [GenericSnapElement, InputElement, GenericSnapElement] + | [InputElement, GenericSnapChildren] + | [GenericSnapChildren, InputElement] + | [GenericSnapChildren, InputElement, GenericSnapChildren] | DropdownElement | RadioGroupElement | FileInputElement diff --git a/packages/snaps-sdk/src/jsx/validation.ts b/packages/snaps-sdk/src/jsx/validation.ts index 79109fef4c..e06d762a72 100644 --- a/packages/snaps-sdk/src/jsx/validation.ts +++ b/packages/snaps-sdk/src/jsx/validation.ts @@ -37,6 +37,7 @@ import { } from '../internals'; import type { EmptyObject } from '../types'; import type { + GenericSnapChildren, GenericSnapElement, JsonObject, Key, @@ -153,6 +154,27 @@ function children( >; } +/** + * A helper function for creating a struct which allows a single child of a specific + * type, as well as `null` and `boolean`. + * + * @param struct - The struct to allow as a single child. + * @returns The struct for the children. + */ +function singleChild( + struct: Type, +): Struct | boolean | null, null> { + return nullable( + selectiveUnion((value) => { + if (typeof value === 'boolean') { + return boolean(); + } + + return struct; + }), + ) as unknown as Struct | boolean | null, null>; +} + /** * A helper function for creating a struct for a JSX element. * @@ -312,30 +334,30 @@ export const FileInputStruct: Describe = element( /** * A subset of JSX elements that represent the tuple Box + Input of the Field children. */ -// eslint-disable-next-line @typescript-eslint/no-use-before-define -const BOX_INPUT_LEFT = [lazy(() => BoxChildStruct), InputStruct] as [ - typeof BoxChildStruct, - typeof InputStruct, -]; +const BOX_INPUT_LEFT = [ + // eslint-disable-next-line @typescript-eslint/no-use-before-define + singleChild(lazy(() => BoxChildStruct)), + InputStruct, +] as [typeof BoxChildStruct, typeof InputStruct]; /** * A subset of JSX elements that represent the tuple Input + Box of the Field children. */ -// eslint-disable-next-line @typescript-eslint/no-use-before-define -const BOX_INPUT_RIGHT = [InputStruct, lazy(() => BoxChildStruct)] as [ - typeof InputStruct, - typeof BoxChildStruct, -]; +const BOX_INPUT_RIGHT = [ + InputStruct, + // eslint-disable-next-line @typescript-eslint/no-use-before-define + singleChild(lazy(() => BoxChildStruct)), +] as [typeof InputStruct, typeof BoxChildStruct]; /** * A subset of JSX elements that represent the tuple Box + Input + Box of the Field children. */ const BOX_INPUT_BOTH = [ // eslint-disable-next-line @typescript-eslint/no-use-before-define - lazy(() => BoxChildStruct), + singleChild(lazy(() => BoxChildStruct)), InputStruct, // eslint-disable-next-line @typescript-eslint/no-use-before-define - lazy(() => BoxChildStruct), + singleChild(lazy(() => BoxChildStruct)), ] as [typeof BoxChildStruct, typeof InputStruct, typeof BoxChildStruct]; /** @@ -377,9 +399,9 @@ const FieldChildStruct = nullUnion([ tuple(BOX_INPUT_BOTH), ...FIELD_CHILDREN_ARRAY, ]) as unknown as Struct< - | [InputElement, GenericSnapElement] - | [GenericSnapElement, InputElement] - | [GenericSnapElement, InputElement, GenericSnapElement] + | [InputElement, GenericSnapChildren] + | [GenericSnapChildren, InputElement] + | [GenericSnapChildren, InputElement, GenericSnapChildren] | DropdownElement | RadioGroupElement | FileInputElement