Skip to content

Commit 09ffc44

Browse files
committed
Add Skeleton component
1 parent 12ec6e7 commit 09ffc44

File tree

8 files changed

+115
-5
lines changed

8 files changed

+115
-5
lines changed

packages/examples/packages/browserify-plugin/snap.manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"url": "https://github.com/MetaMask/snaps.git"
88
},
99
"source": {
10-
"shasum": "LlwXNZxywmo/klkUVJwuj5tRmXOEeV3TDyVZ6CFmHgo=",
10+
"shasum": "a5ainZaRaivsNqUxU8m0sEIHfEUUkvJ5mBe+H3kbDDk=",
1111
"location": {
1212
"npm": {
1313
"filePath": "dist/bundle.js",

packages/examples/packages/browserify/snap.manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"url": "https://github.com/MetaMask/snaps.git"
88
},
99
"source": {
10-
"shasum": "tXrezMiE2cd1yYm0J12JTWpdiWy+zdYpCaqX4eqev1c=",
10+
"shasum": "z9LJ2MFu8QkcDXMdFLB2d2UzipAL8WVWuCo7UV1YMTs=",
1111
"location": {
1212
"npm": {
1313
"filePath": "dist/bundle.js",

packages/snaps-sdk/src/jsx/components/Row.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createSnapComponent } from '../component';
22
import type { AddressElement } from './Address';
33
import type { ImageElement } from './Image';
44
import type { LinkElement } from './Link';
5+
import type { SkeletonElement } from './Skeleton';
56
import type { TextElement } from './Text';
67
import type { ValueElement } from './Value';
78

@@ -13,7 +14,8 @@ export type RowChildren =
1314
| ImageElement
1415
| TextElement
1516
| ValueElement
16-
| LinkElement;
17+
| LinkElement
18+
| SkeletonElement;
1719

1820
/**
1921
* The props of the {@link Row} component.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Skeleton } from './Skeleton';
2+
3+
describe('Skeleton', () => {
4+
it('renders a skeleton component', () => {
5+
const result = <Skeleton width={320} height={32} borderRadius="medium" />;
6+
7+
expect(result).toStrictEqual({
8+
type: 'Skeleton',
9+
key: null,
10+
props: {
11+
width: 320,
12+
height: 32,
13+
borderRadius: 'medium',
14+
},
15+
});
16+
});
17+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { createSnapComponent } from '../component';
2+
3+
/**
4+
* Definition of Skeleton border radius.
5+
*/
6+
export type SkeletonBorderRadius = 'none' | 'medium' | 'full' | undefined;
7+
8+
/**
9+
* The props of the {@link Skeleton} component.
10+
*
11+
* @param width - Width of the Skeleton.
12+
* @param width - Height of the Skeleton.
13+
* @param borderRadius - Border radius of the Skeleton.
14+
*/
15+
export type SkeletonProps = {
16+
width: number | string;
17+
height: number | string;
18+
borderRadius?: SkeletonBorderRadius | undefined;
19+
};
20+
21+
const TYPE = 'Skeleton';
22+
23+
/**
24+
* A Skeleton component, which is used to display skeleton of loading content.
25+
*
26+
* @param props - The props of the component.
27+
* @param props.width - Width of the Skeleton.
28+
* @param props.width - Height of the Skeleton.
29+
* @param props.borderRadius - Border radius of the Skeleton.
30+
* @example
31+
* <Skeleton height={32} width={50%} />
32+
*/
33+
export const Skeleton = createSnapComponent<SkeletonProps, typeof TYPE>(TYPE);
34+
35+
/**
36+
* A Skeleton element.
37+
*
38+
* @see Skeleton
39+
*/
40+
export type SkeletonElement = ReturnType<typeof Skeleton>;

packages/snaps-sdk/src/jsx/components/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type { ImageElement } from './Image';
1515
import type { LinkElement } from './Link';
1616
import type { RowElement } from './Row';
1717
import type { SectionElement } from './Section';
18+
import type { SkeletonElement } from './Skeleton';
1819
import type { SpinnerElement } from './Spinner';
1920
import type { TextElement } from './Text';
2021
import type { TooltipElement } from './Tooltip';
@@ -41,6 +42,7 @@ export * from './Footer';
4142
export * from './Container';
4243
export * from './Section';
4344
export * from './Banner';
45+
export * from './Skeleton';
4446

4547
/**
4648
* A built-in JSX element, which can be used in a Snap user interface.
@@ -66,4 +68,5 @@ export type JSXElement =
6668
| SpinnerElement
6769
| TextElement
6870
| TooltipElement
69-
| BannerElement;
71+
| BannerElement
72+
| SkeletonElement;

packages/snaps-sdk/src/jsx/validation.test.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
Section,
3535
Avatar,
3636
Banner,
37+
Skeleton,
3738
} from './components';
3839
import {
3940
AddressStruct,
@@ -72,6 +73,7 @@ import {
7273
SectionStruct,
7374
AvatarStruct,
7475
BannerStruct,
76+
SkeletonStruct,
7577
} from './validation';
7678

7779
describe('KeyStruct', () => {
@@ -1615,3 +1617,35 @@ describe('BannerStruct', () => {
16151617
expect(is(value, BannerStruct)).toBe(false);
16161618
});
16171619
});
1620+
1621+
describe('SkeletonStruct', () => {
1622+
it.each([
1623+
<Skeleton width={320} height={32} />,
1624+
<Skeleton width="30%" height="30%" />,
1625+
<Skeleton width={32} height="30%" />,
1626+
<Skeleton width="30%" height={32} />,
1627+
<Skeleton width="30%" height={32} borderRadius="full" />,
1628+
<Skeleton width={32} height="30%" borderRadius="medium" />,
1629+
])(`validates a Skeleton element`, (value) => {
1630+
expect(is(value, SkeletonStruct)).toBe(true);
1631+
});
1632+
1633+
it.each([
1634+
'foo',
1635+
42,
1636+
null,
1637+
undefined,
1638+
{},
1639+
[],
1640+
// @ts-expect-error - Invalid props.
1641+
<Skeleton />,
1642+
// @ts-expect-error - Invalid props.
1643+
<Skeleton foo="bar">foo</Skeleton>,
1644+
// @ts-expect-error - Invalid props.
1645+
<Skeleton title={<Copyable value="bar" />} severity="info">
1646+
<Text>foo</Text>
1647+
</Skeleton>,
1648+
])('does not validate "%p"', (value) => {
1649+
expect(is(value, SkeletonStruct)).toBe(false);
1650+
});
1651+
});

packages/snaps-sdk/src/jsx/validation.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import type {
4848
SnapsChildren,
4949
StringElement,
5050
} from './component';
51-
import type { AvatarElement } from './components';
51+
import type { AvatarElement, SkeletonElement } from './components';
5252
import {
5353
type AddressElement,
5454
type BoldElement,
@@ -804,6 +804,17 @@ export const BannerStruct: Describe<BannerElement> = element('Banner', {
804804
]),
805805
});
806806

807+
/**
808+
* A struct for the {@link SkeletonElement} type.
809+
*/
810+
export const SkeletonStruct: Describe<SkeletonElement> = element('Skeleton', {
811+
width: union([number(), string()]),
812+
height: union([number(), string()]),
813+
borderRadius: optional(
814+
nullUnion([literal('none'), literal('medium'), literal('full')]),
815+
),
816+
});
817+
807818
/**
808819
* A struct for the {@link RowElement} type.
809820
*/
@@ -815,6 +826,7 @@ export const RowStruct: Describe<RowElement> = element('Row', {
815826
TextStruct,
816827
ValueStruct,
817828
LinkStruct,
829+
SkeletonStruct,
818830
]),
819831
variant: optional(
820832
nullUnion([literal('default'), literal('warning'), literal('critical')]),
@@ -860,6 +872,7 @@ export const BoxChildStruct = typedUnion([
860872
SectionStruct,
861873
AvatarStruct,
862874
BannerStruct,
875+
SkeletonStruct,
863876
]);
864877

865878
/**
@@ -929,6 +942,7 @@ export const JSXElementStruct: Describe<JSXElement> = typedUnion([
929942
SectionStruct,
930943
AvatarStruct,
931944
BannerStruct,
945+
SkeletonStruct,
932946
]);
933947

934948
/**

0 commit comments

Comments
 (0)