Skip to content

Commit ace3617

Browse files
feat(react-skeleton): Add size and shape props to Skeleton for context-based propagation to children (#35787)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ValentinaKozlova <11574680+ValentinaKozlova@users.noreply.github.com> Co-authored-by: Valentyna <v.kozlova13@gmail.com>
1 parent fb4409e commit ace3617

File tree

14 files changed

+124
-62
lines changed

14 files changed

+124
-62
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "feat(react-skeleton): Add size and shape props to Skeleton component",
4+
"packageName": "@fluentui/react-skeleton",
5+
"email": "v.kozlova13@gmail.com",
6+
"dependentChangeType": "patch"
7+
}

packages/react-components/react-skeleton/library/etc/react-skeleton.api.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export interface SkeletonContextValue {
3333
animation?: 'wave' | 'pulse';
3434
// (undocumented)
3535
appearance?: 'opaque' | 'translucent';
36+
// (undocumented)
37+
shape?: 'circle' | 'square' | 'rectangle';
38+
// (undocumented)
39+
size?: SkeletonItemSize;
3640
}
3741

3842
// @public (undocumented)
@@ -42,11 +46,9 @@ export const SkeletonItem: ForwardRefComponent<SkeletonItemProps>;
4246
export const skeletonItemClassNames: SlotClassNames<SkeletonItemSlots>;
4347

4448
// @public
45-
export type SkeletonItemProps = ComponentProps<SkeletonItemSlots> & {
49+
export type SkeletonItemProps = ComponentProps<SkeletonItemSlots> & Pick<SkeletonProps, 'size' | 'shape'> & {
4650
animation?: 'wave' | 'pulse';
4751
appearance?: 'opaque' | 'translucent';
48-
size?: SkeletonItemSize;
49-
shape?: 'circle' | 'square' | 'rectangle';
5052
};
5153

5254
// @public (undocumented)
@@ -62,6 +64,8 @@ export type SkeletonProps = Omit<ComponentProps<Partial<SkeletonSlots>>, 'width'
6264
animation?: 'wave' | 'pulse';
6365
appearance?: 'opaque' | 'translucent';
6466
width?: number | string;
67+
size?: SkeletonItemSize;
68+
shape?: 'circle' | 'square' | 'rectangle';
6569
};
6670

6771
// @public (undocumented)
@@ -70,7 +74,7 @@ export type SkeletonSlots = {
7074
};
7175

7276
// @public
73-
export type SkeletonState = ComponentState<SkeletonSlots> & Required<Pick<SkeletonProps, 'animation' | 'appearance'>>;
77+
export type SkeletonState = ComponentState<SkeletonSlots> & Required<Pick<SkeletonProps, 'animation' | 'appearance'>> & Pick<SkeletonProps, 'size' | 'shape'>;
7478

7579
// @public
7680
export const useSkeleton_unstable: (props: SkeletonProps, ref: React_2.Ref<HTMLElement>) => SkeletonState;

packages/react-components/react-skeleton/library/src/Skeleton.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
export type { SkeletonContextValues, SkeletonProps, SkeletonSlots, SkeletonState } from './components/Skeleton/index';
1+
export type {
2+
SkeletonContextValues,
3+
SkeletonItemSize,
4+
SkeletonProps,
5+
SkeletonSlots,
6+
SkeletonState,
7+
} from './components/Skeleton/index';
28
export {
39
Skeleton,
410
renderSkeleton_unstable,

packages/react-components/react-skeleton/library/src/SkeletonItem.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
export type {
2-
SkeletonItemProps,
3-
SkeletonItemSize,
4-
SkeletonItemSlots,
5-
SkeletonItemState,
6-
} from './components/SkeletonItem/index';
1+
export type { SkeletonItemProps, SkeletonItemSlots, SkeletonItemState } from './components/SkeletonItem/index';
72
export {
83
SkeletonItem,
94
renderSkeletonItem_unstable,

packages/react-components/react-skeleton/library/src/components/Skeleton/Skeleton.test.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import * as React from 'react';
22
import { render } from '@testing-library/react';
3+
import { renderHook } from '@testing-library/react-hooks';
34
import { Skeleton } from './Skeleton';
5+
import { useSkeletonItem_unstable } from '../SkeletonItem/useSkeletonItem';
6+
import { SkeletonContextProvider } from '../../contexts/SkeletonContext';
47
import { isConformant } from '../../testing/isConformant';
58

69
describe('Skeleton', () => {
@@ -23,4 +26,32 @@ describe('Skeleton', () => {
2326
const result = render(<Skeleton />);
2427
expect(result.getByRole('progressbar').getAttribute('aria-busy')).toBeDefined();
2528
});
29+
it('passes size prop to SkeletonItem via context', () => {
30+
const wrapper = ({ children }: { children: React.ReactNode }) => (
31+
<SkeletonContextProvider value={{ size: 24 }}>{children}</SkeletonContextProvider>
32+
);
33+
const { result } = renderHook(() => useSkeletonItem_unstable({}, React.createRef()), { wrapper });
34+
expect(result.current.size).toBe(24);
35+
});
36+
it('passes shape prop to SkeletonItem via context', () => {
37+
const wrapper = ({ children }: { children: React.ReactNode }) => (
38+
<SkeletonContextProvider value={{ shape: 'circle' }}>{children}</SkeletonContextProvider>
39+
);
40+
const { result } = renderHook(() => useSkeletonItem_unstable({}, React.createRef()), { wrapper });
41+
expect(result.current.shape).toBe('circle');
42+
});
43+
it('allows SkeletonItem to override Skeleton size prop', () => {
44+
const wrapper = ({ children }: { children: React.ReactNode }) => (
45+
<SkeletonContextProvider value={{ size: 24 }}>{children}</SkeletonContextProvider>
46+
);
47+
const { result } = renderHook(() => useSkeletonItem_unstable({ size: 48 }, React.createRef()), { wrapper });
48+
expect(result.current.size).toBe(48);
49+
});
50+
it('allows SkeletonItem to override Skeleton shape prop', () => {
51+
const wrapper = ({ children }: { children: React.ReactNode }) => (
52+
<SkeletonContextProvider value={{ shape: 'circle' }}>{children}</SkeletonContextProvider>
53+
);
54+
const { result } = renderHook(() => useSkeletonItem_unstable({ shape: 'square' }, React.createRef()), { wrapper });
55+
expect(result.current.shape).toBe('square');
56+
});
2657
});

packages/react-components/react-skeleton/library/src/components/Skeleton/Skeleton.types.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ export type SkeletonSlots = {
99
root: NonNullable<Slot<'div', 'span'>>;
1010
};
1111

12+
/**
13+
* Sizes for the SkeletonItem
14+
*/
15+
export type SkeletonItemSize = 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | 48 | 56 | 64 | 72 | 96 | 120 | 128;
16+
1217
/**
1318
* Skeleton Props
1419
*/
@@ -31,6 +36,19 @@ export type SkeletonProps = Omit<ComponentProps<Partial<SkeletonSlots>>, 'width'
3136
* @deprecated Use `className` prop to set width
3237
*/
3338
width?: number | string;
39+
40+
/**
41+
* Sets the size of the SkeletonItems inside the Skeleton in pixels.
42+
* Size is restricted to a limited set of values recommended for most uses (see SkeletonItemSize).
43+
* This value can be overridden by the individual SkeletonItem's `size` prop.
44+
*/
45+
size?: SkeletonItemSize;
46+
47+
/**
48+
* Sets the shape of the SkeletonItems inside the Skeleton.
49+
* This value can be overridden by the individual SkeletonItem's `shape` prop.
50+
*/
51+
shape?: 'circle' | 'square' | 'rectangle';
3452
};
3553

3654
export type SkeletonContextValues = {
@@ -40,4 +58,6 @@ export type SkeletonContextValues = {
4058
/**
4159
* State used in rendering Skeleton
4260
*/
43-
export type SkeletonState = ComponentState<SkeletonSlots> & Required<Pick<SkeletonProps, 'animation' | 'appearance'>>;
61+
export type SkeletonState = ComponentState<SkeletonSlots> &
62+
Required<Pick<SkeletonProps, 'animation' | 'appearance'>> &
63+
Pick<SkeletonProps, 'size' | 'shape'>;

packages/react-components/react-skeleton/library/src/components/Skeleton/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
export { Skeleton } from './Skeleton';
2-
export type { SkeletonContextValues, SkeletonProps, SkeletonSlots, SkeletonState } from './Skeleton.types';
2+
export type {
3+
SkeletonContextValues,
4+
SkeletonItemSize,
5+
SkeletonProps,
6+
SkeletonSlots,
7+
SkeletonState,
8+
} from './Skeleton.types';
39
export { renderSkeleton_unstable } from './renderSkeleton';
410
export { useSkeleton_unstable } from './useSkeleton';
511
export { useSkeletonContextValues } from './useSkeletonContextValues';

packages/react-components/react-skeleton/library/src/components/Skeleton/useSkeleton.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { useSkeletonContext } from '../../contexts/SkeletonContext';
1616
*/
1717
export const useSkeleton_unstable = (props: SkeletonProps, ref: React.Ref<HTMLElement>): SkeletonState => {
1818
const { animation: contextAnimation, appearance: contextAppearance } = useSkeletonContext();
19-
const { animation = contextAnimation ?? 'wave', appearance = contextAppearance ?? 'opaque' } = props;
19+
const { animation = contextAnimation ?? 'wave', appearance = contextAppearance ?? 'opaque', size, shape } = props;
2020

2121
const root = slot.always(
2222
getIntrinsicElementProps('div', {
@@ -30,5 +30,5 @@ export const useSkeleton_unstable = (props: SkeletonProps, ref: React.Ref<HTMLEl
3030
}),
3131
{ elementType: 'div' },
3232
);
33-
return { animation, appearance, components: { root: 'div' }, root };
33+
return { animation, appearance, size, shape, components: { root: 'div' }, root };
3434
};

packages/react-components/react-skeleton/library/src/components/Skeleton/useSkeletonContextValues.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import * as React from 'react';
44
import type { SkeletonContextValues, SkeletonState } from '../Skeleton';
55

66
export const useSkeletonContextValues = (state: SkeletonState): SkeletonContextValues => {
7-
const { animation, appearance } = state;
7+
const { animation, appearance, size, shape } = state;
88

99
const skeletonGroup = React.useMemo(
1010
() => ({
1111
animation,
1212
appearance,
13+
size,
14+
shape,
1315
}),
14-
[animation, appearance],
16+
[animation, appearance, size, shape],
1517
);
1618

1719
return { skeletonGroup };

packages/react-components/react-skeleton/library/src/components/SkeletonItem/SkeletonItem.types.ts

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,27 @@
11
import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';
2+
import type { SkeletonProps } from '../Skeleton/Skeleton.types';
23

34
export type SkeletonItemSlots = {
45
root: Slot<'div', 'span'>;
56
};
67

7-
/**
8-
* Sizes for the SkeletonItem
9-
*/
10-
export type SkeletonItemSize = 8 | 12 | 16 | 20 | 24 | 28 | 32 | 36 | 40 | 48 | 56 | 64 | 72 | 96 | 120 | 128;
11-
128
/**
139
* SkeletonItem Props
1410
*/
15-
export type SkeletonItemProps = ComponentProps<SkeletonItemSlots> & {
16-
/**
17-
* Sets the animation of the SkeletonItem
18-
* @default wave
19-
*/
20-
animation?: 'wave' | 'pulse';
11+
export type SkeletonItemProps = ComponentProps<SkeletonItemSlots> &
12+
Pick<SkeletonProps, 'size' | 'shape'> & {
13+
/**
14+
* Sets the animation of the SkeletonItem
15+
* @default wave
16+
*/
17+
animation?: 'wave' | 'pulse';
2118

22-
/**
23-
* Sets the appearance of the SkeletonItem
24-
* @default opaque
25-
*/
26-
appearance?: 'opaque' | 'translucent';
27-
28-
/**
29-
* Sets the size of the SkeletonItem in pixels.
30-
* Size is restricted to a limited set of values recommended for most uses(see SkeletonItemSize).
31-
* To set a non-supported size, set `width` and `height` to override the rendered size.
32-
* @default 16
33-
*/
34-
size?: SkeletonItemSize;
35-
36-
/**
37-
* Sets the shape of the SkeletonItem.
38-
* @default rectangle
39-
*/
40-
shape?: 'circle' | 'square' | 'rectangle';
41-
};
19+
/**
20+
* Sets the appearance of the SkeletonItem
21+
* @default opaque
22+
*/
23+
appearance?: 'opaque' | 'translucent';
24+
};
4225

4326
/**
4427
* State used in rendering SkeletonItem

0 commit comments

Comments
 (0)