Skip to content

Commit 63b5109

Browse files
committed
feat(react): redesign skeleton animation api
1 parent e4c4c46 commit 63b5109

20 files changed

Lines changed: 717 additions & 117 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@tiny-design/react": major
3+
"@tiny-design/tokens": major
4+
---
5+
6+
Redesign Skeleton animation APIs, component structure, and related configuration.

packages/react/src/config-provider/config-context.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ import React from 'react';
22
import { SizeType } from '../_utils/props';
33
import { SpaceSize } from '../space/types';
44
import { Locale } from '../locale/types';
5+
import { SkeletonAnimation } from '../skeleton/types';
56
import { ThemeConfig } from './token-utils';
67

78
export type ThemeMode = 'light' | 'dark' | 'system';
89

10+
export interface SkeletonConfig {
11+
animation?: SkeletonAnimation;
12+
}
13+
914
export interface ConfigContextProps {
1015
prefixCls?: string;
1116
componentSize?: SizeType;
12-
shimmer?: boolean;
17+
skeleton?: SkeletonConfig;
1318
space?: SpaceSize;
1419
theme?: ThemeMode;
1520
themeConfig?: ThemeConfig;
@@ -21,7 +26,7 @@ export interface ConfigContextProps {
2126
export const ConfigContext = React.createContext<ConfigContextProps>({
2227
prefixCls: 'ty',
2328
componentSize: 'md',
24-
shimmer: false,
29+
skeleton: undefined,
2530
space: 'sm',
2631
getPopupContainer: () => document.body,
2732
getTargetContainer: () => window,

packages/react/src/config-provider/config-provider.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const ConfigProviderImpl = (props: ConfigProviderProps): React.ReactElement => {
1616
locale,
1717
prefixCls,
1818
componentSize,
19-
shimmer,
19+
skeleton,
2020
space,
2121
getPopupContainer,
2222
getTargetContainer,
@@ -49,7 +49,10 @@ const ConfigProviderImpl = (props: ConfigProviderProps): React.ReactElement => {
4949
() => ({
5050
prefixCls: prefixCls ?? parentConfig.prefixCls,
5151
componentSize: componentSize ?? parentConfig.componentSize,
52-
shimmer: shimmer ?? parentConfig.shimmer,
52+
skeleton: {
53+
...parentConfig.skeleton,
54+
...skeleton,
55+
},
5356
space: space ?? parentConfig.space,
5457
theme: mode ?? parentConfig.theme,
5558
themeConfig: themeConfig ?? parentConfig.themeConfig,
@@ -65,7 +68,7 @@ const ConfigProviderImpl = (props: ConfigProviderProps): React.ReactElement => {
6568
mode,
6669
parentConfig,
6770
prefixCls,
68-
shimmer,
71+
skeleton,
6972
space,
7073
themeConfig,
7174
]

packages/react/src/config-provider/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,8 @@ const { componentSize, getPopupContainer, locale } = ConfigProvider.useConfig();
226226
| ----------------- | ------------------------------------------------------------- | ------------------------------------------------- | --------- |
227227
| prefixCls | set prefix class. | string | ty |
228228
| componentSize | component size. | enum: `lg` &#124; `md` &#124; `sm` | `md` |
229-
| shimmer | display shimmer effect for [Skeleton](#/components/skeleton). | boolean | false |
230-
| space | set Space size, ref [Space](#/components/space). | enum: `sm` &#124; `md` &#124; `lg` or `number`. | `sm` |
229+
| skeleton | global config for [Skeleton](../components/skeleton), such as animation. | `{ animation?: false \| 'pulse' \| 'shimmer' }` | - |
230+
| space | set Space size, ref [Space](../components/space). | enum: `sm` &#124; `md` &#124; `lg` or `number`. | `sm` |
231231
| locale | set locale for components (e.g. `en_US`, `zh_CN`). | Locale | - |
232232
| getPopupContainer | set the container for popup-based components within this provider scope. | `(trigger?: HTMLElement \| null) => HTMLElement` | provider popup holder |
233233
| getTargetContainer | set the default scroll target for components such as `Anchor`, `Sticky`, and `BackTop`, and the scroll-lock target for layers such as `Overlay` and `Tour`. | `() => HTMLElement \| Window` | `() => window` |

packages/react/src/config-provider/index.zh_CN.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,8 @@ const { componentSize, getPopupContainer, locale } = ConfigProvider.useConfig();
226226
| ----------------- | ------------------------------------------------------------- | ------------------------------------------------- | --------- |
227227
| prefixCls | 设置类名前缀 | string | ty |
228228
| componentSize | 组件大小 | enum: `lg` &#124; `md` &#124; `sm` | `md` |
229-
| shimmer |[Skeleton](#/components/skeleton) 显示微光动画效果 | boolean | false |
230-
| space | 设置 Space 间距,参考 [Space](#/components/space) | enum: `sm` &#124; `md` &#124; `lg` or `number`. | `sm` |
229+
| skeleton |[Skeleton](../components/skeleton) 提供全局配置,例如动画效果 | `{ animation?: false \| 'pulse' \| 'shimmer' }` | - |
230+
| space | 设置 Space 间距,参考 [Space](../components/space) | enum: `sm` &#124; `md` &#124; `lg` or `number`. | `sm` |
231231
| locale | 设置组件语言包(如 `en_US``zh_CN`| Locale | - |
232232
| getPopupContainer | 为当前 provider 作用域内的弹层组件指定挂载容器 | `(trigger?: HTMLElement \| null) => HTMLElement` | provider popup holder |
233233
| getTargetContainer |`Anchor``Sticky``BackTop` 等组件设置默认滚动容器,同时为 `Overlay``Tour` 这类层级组件设置滚动锁目标 | `() => HTMLElement \| Window` | `() => window` |

packages/react/src/image/demo/Placeholder.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default function PlaceholderDemo() {
3636
height={180}
3737
src={src}
3838
alt="Mountain lake"
39-
placeholder={<Skeleton active style={{ width: '100%', height: '100%' }} />}
39+
placeholder={<Skeleton animation="shimmer" width="100%" height="100%" />}
4040
/>
4141
</Flex>
4242
);

packages/react/src/skeleton/__tests__/__snapshots__/skeleton.test.tsx.snap

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ exports[`<Skeleton /> should match the snapshot 1`] = `
44
<DocumentFragment>
55
<div
66
aria-busy="true"
7-
class="ty-skeleton"
87
role="status"
9-
/>
8+
>
9+
<div
10+
aria-hidden="true"
11+
class="ty-skeleton ty-skeleton_round"
12+
/>
13+
</div>
1014
</DocumentFragment>
1115
`;

packages/react/src/skeleton/__tests__/skeleton.test.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,30 @@ describe('<Skeleton />', () => {
1010

1111
it('should render correctly', () => {
1212
const { container } = render(<Skeleton />);
13-
expect(container.firstChild).toHaveClass('ty-skeleton');
13+
expect(container.firstChild).toHaveAttribute('role', 'status');
14+
expect(container.querySelector('.ty-skeleton')).toBeInTheDocument();
15+
expect(container.querySelector('.ty-skeleton')).toHaveClass('ty-skeleton_round');
1416
});
1517

16-
it('should render with active animation', () => {
17-
const { container } = render(<Skeleton active />);
18-
expect(container.firstChild).toHaveClass('ty-skeleton_active');
18+
it('should render with shimmer animation', () => {
19+
const { container } = render(<Skeleton animation="shimmer" />);
20+
expect(container.querySelector('.ty-skeleton')).toHaveClass('ty-skeleton_animated_shimmer');
1921
});
2022

21-
it('should render rounded', () => {
22-
const { container } = render(<Skeleton rounded />);
23-
expect(container.firstChild).toHaveClass('ty-skeleton_rounded');
23+
it('should render shaped block', () => {
24+
const { container } = render(<Skeleton shape="circle" width={40} height={40} />);
25+
expect(container.querySelector('.ty-skeleton')).toHaveClass('ty-skeleton_circle');
2426
});
2527

26-
it('should render children', () => {
27-
const { getByText } = render(<Skeleton>Loading content</Skeleton>);
28+
it('should render children when loading is false', () => {
29+
const { getByText, queryByRole } = render(<Skeleton loading={false}>Loading content</Skeleton>);
2830
expect(getByText('Loading content')).toBeInTheDocument();
31+
expect(queryByRole('status')).not.toBeInTheDocument();
32+
});
33+
34+
it('should render structured skeleton', () => {
35+
const { container } = render(<Skeleton avatar title paragraph={{ rows: 2 }} />);
36+
expect(container.querySelector('.ty-skeleton__group')).toBeInTheDocument();
37+
expect(container.querySelectorAll('.ty-skeleton__text-row')).toHaveLength(3);
2938
});
3039
});
Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
import React from 'react';
2-
import { Skeleton } from '@tiny-design/react';
2+
import { Flex, Radio, Skeleton } from '@tiny-design/react';
3+
4+
type AnimationMode = 'shimmer' | 'pulse';
35

46
export default function ActiveDemo() {
7+
const [animation, setAnimation] = React.useState<AnimationMode>('shimmer');
8+
59
return (
6-
<>
7-
<Skeleton active style={{ width: 300 }} />
8-
<Skeleton active />
9-
<Skeleton active />
10-
<Skeleton active />
11-
</>
10+
<Flex vertical gap={16}>
11+
<Radio.Group value={animation} onChange={(value) => setAnimation(value as AnimationMode)}>
12+
<Radio value="shimmer">shimmer</Radio>
13+
<Radio value="pulse">pulse</Radio>
14+
</Radio.Group>
15+
16+
<div>
17+
<Skeleton animation={animation} width={300} />
18+
<Skeleton animation={animation} />
19+
<Skeleton animation={animation} />
20+
<Skeleton animation={animation} />
21+
</div>
22+
</Flex>
1223
);
13-
}
24+
}

packages/react/src/skeleton/demo/Basic.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { Skeleton } from '@tiny-design/react';
44
export default function BasicDemo() {
55
return (
66
<>
7-
<Skeleton style={{ width: 300 }} />
7+
<Skeleton width={300} />
88
<Skeleton />
99
<Skeleton />
1010
<Skeleton />
1111
</>
1212
);
13-
}
13+
}

0 commit comments

Comments
 (0)