Skip to content

Commit f584d23

Browse files
committed
Add Skeleton component
1 parent 687b582 commit f584d23

File tree

6 files changed

+113
-3
lines changed

6 files changed

+113
-3
lines changed

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

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,
@@ -807,6 +807,17 @@ export const BannerStruct: Describe<BannerElement> = element('Banner', {
807807
]),
808808
});
809809

810+
/**
811+
* A struct for the {@link SkeletonElement} type.
812+
*/
813+
export const SkeletonStruct: Describe<SkeletonElement> = element('Skeleton', {
814+
width: union([number(), string()]),
815+
height: union([number(), string()]),
816+
borderRadius: optional(
817+
nullUnion([literal('none'), literal('medium'), literal('full')]),
818+
),
819+
});
820+
810821
/**
811822
* A struct for the {@link RowElement} type.
812823
*/
@@ -818,6 +829,7 @@ export const RowStruct: Describe<RowElement> = element('Row', {
818829
TextStruct,
819830
ValueStruct,
820831
LinkStruct,
832+
SkeletonStruct,
821833
]),
822834
variant: optional(
823835
nullUnion([literal('default'), literal('warning'), literal('critical')]),
@@ -863,6 +875,7 @@ export const BoxChildStruct = typedUnion([
863875
SectionStruct,
864876
AvatarStruct,
865877
BannerStruct,
878+
SkeletonStruct,
866879
]);
867880

868881
/**
@@ -932,6 +945,7 @@ export const JSXElementStruct: Describe<JSXElement> = typedUnion([
932945
SectionStruct,
933946
AvatarStruct,
934947
BannerStruct,
948+
SkeletonStruct,
935949
]);
936950

937951
/**

0 commit comments

Comments
 (0)