From 09ffc447eec4d3d3f004f11f13212128f186a763 Mon Sep 17 00:00:00 2001 From: david0xd Date: Thu, 16 Jan 2025 20:21:37 +0100 Subject: [PATCH] Add Skeleton component --- .../browserify-plugin/snap.manifest.json | 2 +- .../packages/browserify/snap.manifest.json | 2 +- packages/snaps-sdk/src/jsx/components/Row.ts | 4 +- .../src/jsx/components/Skeleton.test.tsx | 17 ++++++++ .../snaps-sdk/src/jsx/components/Skeleton.ts | 40 +++++++++++++++++++ .../snaps-sdk/src/jsx/components/index.ts | 5 ++- .../snaps-sdk/src/jsx/validation.test.tsx | 34 ++++++++++++++++ packages/snaps-sdk/src/jsx/validation.ts | 16 +++++++- 8 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 packages/snaps-sdk/src/jsx/components/Skeleton.test.tsx create mode 100644 packages/snaps-sdk/src/jsx/components/Skeleton.ts diff --git a/packages/examples/packages/browserify-plugin/snap.manifest.json b/packages/examples/packages/browserify-plugin/snap.manifest.json index 2fcee839e4..a77ff349eb 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": "LlwXNZxywmo/klkUVJwuj5tRmXOEeV3TDyVZ6CFmHgo=", + "shasum": "a5ainZaRaivsNqUxU8m0sEIHfEUUkvJ5mBe+H3kbDDk=", "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 95f36894d5..f29767b6df 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": "tXrezMiE2cd1yYm0J12JTWpdiWy+zdYpCaqX4eqev1c=", + "shasum": "z9LJ2MFu8QkcDXMdFLB2d2UzipAL8WVWuCo7UV1YMTs=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snaps-sdk/src/jsx/components/Row.ts b/packages/snaps-sdk/src/jsx/components/Row.ts index b64511150f..44dc409f24 100644 --- a/packages/snaps-sdk/src/jsx/components/Row.ts +++ b/packages/snaps-sdk/src/jsx/components/Row.ts @@ -2,6 +2,7 @@ import { createSnapComponent } from '../component'; import type { AddressElement } from './Address'; import type { ImageElement } from './Image'; import type { LinkElement } from './Link'; +import type { SkeletonElement } from './Skeleton'; import type { TextElement } from './Text'; import type { ValueElement } from './Value'; @@ -13,7 +14,8 @@ export type RowChildren = | ImageElement | TextElement | ValueElement - | LinkElement; + | LinkElement + | SkeletonElement; /** * The props of the {@link Row} component. diff --git a/packages/snaps-sdk/src/jsx/components/Skeleton.test.tsx b/packages/snaps-sdk/src/jsx/components/Skeleton.test.tsx new file mode 100644 index 0000000000..6869203534 --- /dev/null +++ b/packages/snaps-sdk/src/jsx/components/Skeleton.test.tsx @@ -0,0 +1,17 @@ +import { Skeleton } from './Skeleton'; + +describe('Skeleton', () => { + it('renders a skeleton component', () => { + const result = ; + + expect(result).toStrictEqual({ + type: 'Skeleton', + key: null, + props: { + width: 320, + height: 32, + borderRadius: 'medium', + }, + }); + }); +}); diff --git a/packages/snaps-sdk/src/jsx/components/Skeleton.ts b/packages/snaps-sdk/src/jsx/components/Skeleton.ts new file mode 100644 index 0000000000..d45e821851 --- /dev/null +++ b/packages/snaps-sdk/src/jsx/components/Skeleton.ts @@ -0,0 +1,40 @@ +import { createSnapComponent } from '../component'; + +/** + * Definition of Skeleton border radius. + */ +export type SkeletonBorderRadius = 'none' | 'medium' | 'full' | undefined; + +/** + * The props of the {@link Skeleton} component. + * + * @param width - Width of the Skeleton. + * @param width - Height of the Skeleton. + * @param borderRadius - Border radius of the Skeleton. + */ +export type SkeletonProps = { + width: number | string; + height: number | string; + borderRadius?: SkeletonBorderRadius | undefined; +}; + +const TYPE = 'Skeleton'; + +/** + * A Skeleton component, which is used to display skeleton of loading content. + * + * @param props - The props of the component. + * @param props.width - Width of the Skeleton. + * @param props.width - Height of the Skeleton. + * @param props.borderRadius - Border radius of the Skeleton. + * @example + * + */ +export const Skeleton = createSnapComponent(TYPE); + +/** + * A Skeleton element. + * + * @see Skeleton + */ +export type SkeletonElement = ReturnType; diff --git a/packages/snaps-sdk/src/jsx/components/index.ts b/packages/snaps-sdk/src/jsx/components/index.ts index d26cbcd338..132faee1f1 100644 --- a/packages/snaps-sdk/src/jsx/components/index.ts +++ b/packages/snaps-sdk/src/jsx/components/index.ts @@ -15,6 +15,7 @@ import type { ImageElement } from './Image'; import type { LinkElement } from './Link'; import type { RowElement } from './Row'; import type { SectionElement } from './Section'; +import type { SkeletonElement } from './Skeleton'; import type { SpinnerElement } from './Spinner'; import type { TextElement } from './Text'; import type { TooltipElement } from './Tooltip'; @@ -41,6 +42,7 @@ export * from './Footer'; export * from './Container'; export * from './Section'; export * from './Banner'; +export * from './Skeleton'; /** * A built-in JSX element, which can be used in a Snap user interface. @@ -66,4 +68,5 @@ export type JSXElement = | SpinnerElement | TextElement | TooltipElement - | BannerElement; + | BannerElement + | SkeletonElement; diff --git a/packages/snaps-sdk/src/jsx/validation.test.tsx b/packages/snaps-sdk/src/jsx/validation.test.tsx index 0f2c3114c7..5f3046af4c 100644 --- a/packages/snaps-sdk/src/jsx/validation.test.tsx +++ b/packages/snaps-sdk/src/jsx/validation.test.tsx @@ -34,6 +34,7 @@ import { Section, Avatar, Banner, + Skeleton, } from './components'; import { AddressStruct, @@ -72,6 +73,7 @@ import { SectionStruct, AvatarStruct, BannerStruct, + SkeletonStruct, } from './validation'; describe('KeyStruct', () => { @@ -1615,3 +1617,35 @@ describe('BannerStruct', () => { expect(is(value, BannerStruct)).toBe(false); }); }); + +describe('SkeletonStruct', () => { + it.each([ + , + , + , + , + , + , + ])(`validates a Skeleton element`, (value) => { + expect(is(value, SkeletonStruct)).toBe(true); + }); + + it.each([ + 'foo', + 42, + null, + undefined, + {}, + [], + // @ts-expect-error - Invalid props. + , + // @ts-expect-error - Invalid props. + foo, + // @ts-expect-error - Invalid props. + } severity="info"> + foo + , + ])('does not validate "%p"', (value) => { + expect(is(value, SkeletonStruct)).toBe(false); + }); +}); diff --git a/packages/snaps-sdk/src/jsx/validation.ts b/packages/snaps-sdk/src/jsx/validation.ts index c251ba91b5..e4e99a9db6 100644 --- a/packages/snaps-sdk/src/jsx/validation.ts +++ b/packages/snaps-sdk/src/jsx/validation.ts @@ -48,7 +48,7 @@ import type { SnapsChildren, StringElement, } from './component'; -import type { AvatarElement } from './components'; +import type { AvatarElement, SkeletonElement } from './components'; import { type AddressElement, type BoldElement, @@ -804,6 +804,17 @@ export const BannerStruct: Describe = element('Banner', { ]), }); +/** + * A struct for the {@link SkeletonElement} type. + */ +export const SkeletonStruct: Describe = element('Skeleton', { + width: union([number(), string()]), + height: union([number(), string()]), + borderRadius: optional( + nullUnion([literal('none'), literal('medium'), literal('full')]), + ), +}); + /** * A struct for the {@link RowElement} type. */ @@ -815,6 +826,7 @@ export const RowStruct: Describe = element('Row', { TextStruct, ValueStruct, LinkStruct, + SkeletonStruct, ]), variant: optional( nullUnion([literal('default'), literal('warning'), literal('critical')]), @@ -860,6 +872,7 @@ export const BoxChildStruct = typedUnion([ SectionStruct, AvatarStruct, BannerStruct, + SkeletonStruct, ]); /** @@ -929,6 +942,7 @@ export const JSXElementStruct: Describe = typedUnion([ SectionStruct, AvatarStruct, BannerStruct, + SkeletonStruct, ]); /**