From 86527a807648cf37da06cccc5866a282e46ae0a2 Mon Sep 17 00:00:00 2001 From: Guillaume Roux Date: Thu, 12 Sep 2024 12:54:48 +0200 Subject: [PATCH] Fix type issue with Field --- packages/snaps-sdk/src/jsx/component.ts | 5 +++ .../src/jsx/components/form/Field.test.tsx | 20 +++++++++ .../src/jsx/components/form/Field.ts | 8 ++-- packages/snaps-sdk/src/jsx/validation.ts | 45 ++++++++++++++----- 4 files changed, 62 insertions(+), 16 deletions(-) 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..6ee3c6f7fc 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,24 @@ describe('Field', () => { }, }); }); + + it('renders a field with a conditional', () => { + const result = ( + + + {false && } + + ); + + expect(result).toStrictEqual({ + type: 'Field', + key: null, + props: { + children: { + type: 'Input', + name: 'foo', + }, + }, + }); + }); }); 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 7f98a654e8..661f68e7e5 100644 --- a/packages/snaps-sdk/src/jsx/validation.ts +++ b/packages/snaps-sdk/src/jsx/validation.ts @@ -153,6 +153,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. * @@ -311,30 +332,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]; /**