diff --git a/packages/snaps-sdk/src/jsx/components/SettingCell.test.tsx b/packages/snaps-sdk/src/jsx/components/SettingCell.test.tsx new file mode 100644 index 0000000000..2312e059b7 --- /dev/null +++ b/packages/snaps-sdk/src/jsx/components/SettingCell.test.tsx @@ -0,0 +1,28 @@ +import { Input } from './form'; +import { SettingCell } from './SettingCell'; + +describe('SettingCell', () => { + it('renders a setting cell', () => { + const result = ( + + + + ); + + expect(result).toStrictEqual({ + type: 'SettingCell', + key: null, + props: { + title: 'Title', + description: 'Description', + children: { + type: 'Input', + key: null, + props: { + name: 'setting1', + }, + }, + }, + }); + }); +}); diff --git a/packages/snaps-sdk/src/jsx/components/SettingCell.ts b/packages/snaps-sdk/src/jsx/components/SettingCell.ts new file mode 100644 index 0000000000..53c2f74960 --- /dev/null +++ b/packages/snaps-sdk/src/jsx/components/SettingCell.ts @@ -0,0 +1,44 @@ +import { + createSnapComponent, + type GenericSnapElement, + type SnapsChildren, +} from '../component'; + +/** + * The props of the {@link SettingCell} component. + * + * @property title - The title. + * @property description - The description. + * @property children - The children to show within the cell. + */ +export type SettingCellProps = { + title: string; + description: string; + children: SnapsChildren; +}; + +const TYPE = 'SettingCell'; + +/** + * A setting cell component which can be used to display a setting control. + * + * @param props - The props of the component. + * @param props.title - The title. + * @param props.description - The description. + * @param props.children - The children to show within the cell. + * @returns A setting cell element. + * @example + * + * + * + */ +export const SettingCell = createSnapComponent( + TYPE, +); + +/** + * A setting cell element. + * + * @see SettingCell + */ +export type SettingCellElement = ReturnType; diff --git a/packages/snaps-sdk/src/jsx/components/index.ts b/packages/snaps-sdk/src/jsx/components/index.ts index bc64836bb2..1a6051b110 100644 --- a/packages/snaps-sdk/src/jsx/components/index.ts +++ b/packages/snaps-sdk/src/jsx/components/index.ts @@ -14,6 +14,7 @@ import type { ImageElement } from './Image'; import type { LinkElement } from './Link'; import type { RowElement } from './Row'; import type { SectionElement } from './Section'; +import type { SettingCellElement } from './SettingCell'; import type { SpinnerElement } from './Spinner'; import type { TextElement } from './Text'; import type { TooltipElement } from './Tooltip'; @@ -39,6 +40,7 @@ export * from './Tooltip'; export * from './Footer'; export * from './Container'; export * from './Section'; +export * from './SettingCell'; /** * A built-in JSX element, which can be used in a Snap user interface. @@ -63,4 +65,5 @@ export type JSXElement = | SectionElement | SpinnerElement | TextElement - | TooltipElement; + | TooltipElement + | SettingCellElement; diff --git a/packages/snaps-sdk/src/jsx/validation.test.tsx b/packages/snaps-sdk/src/jsx/validation.test.tsx index f755a75d70..7fdbf97d31 100644 --- a/packages/snaps-sdk/src/jsx/validation.test.tsx +++ b/packages/snaps-sdk/src/jsx/validation.test.tsx @@ -33,6 +33,7 @@ import { SelectorOption, Section, Avatar, + SettingCell, } from './components'; import { AddressStruct, @@ -70,6 +71,7 @@ import { SelectorStruct, SectionStruct, AvatarStruct, + SettingCellStruct, } from './validation'; describe('KeyStruct', () => { @@ -1414,6 +1416,51 @@ describe('SectionStruct', () => { }); }); +describe('SettingCellStruct', () => { + it.each([ + + + , + + + + + , + +
+ +
+
, + ])('validates a setting cell element', (value) => { + expect(is(value, SettingCellStruct)).toBe(true); + }); + + it.each([ + 'foo', + 42, + null, + undefined, + {}, + [], + // @ts-expect-error - Invalid props. + , + // @ts-expect-error - Invalid props. + + + , + + + , + + alt + , + // @ts-expect-error - Invalid props. + , + ])('does not validate "%p"', (value) => { + expect(is(value, SettingCellStruct)).toBe(false); + }); +}); + describe('isJSXElement', () => { it.each([ foo, diff --git a/packages/snaps-sdk/src/jsx/validation.ts b/packages/snaps-sdk/src/jsx/validation.ts index be3702912b..77f0e7e633 100644 --- a/packages/snaps-sdk/src/jsx/validation.ts +++ b/packages/snaps-sdk/src/jsx/validation.ts @@ -84,6 +84,7 @@ import { type SelectorOptionElement, IconName, } from './components'; +import type { SettingCellElement } from './components/SettingCell'; /** * A struct for the {@link Key} type. @@ -633,6 +634,18 @@ export const SectionStruct: Describe = element('Section', { ), }); +/** + * A struct for the {@link SettingCellElement} type. + */ +export const SettingCellStruct: Describe = element( + 'SettingCell', + { + children: BoxChildrenStruct, + title: string(), + description: string(), + }, +); + /** * A subset of JSX elements that are allowed as children of the Footer component. * This set should include a single button or a tuple of two buttons. @@ -821,6 +834,7 @@ export const BoxChildStruct = typedUnion([ SelectorStruct, SectionStruct, AvatarStruct, + SettingCellStruct, ]); /** @@ -886,6 +900,7 @@ export const JSXElementStruct: Describe = typedUnion([ SelectorOptionStruct, SectionStruct, AvatarStruct, + SettingCellStruct, ]); /**