Skip to content

Commit 2329bf3

Browse files
authored
Merge pull request #139 from dvtng/decimal-count
Render fractional-width skeleton if count is non-integer
2 parents 8826d0c + 084ecab commit 2329bf3

File tree

8 files changed

+95
-29
lines changed

8 files changed

+95
-29
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 3.1.0
2+
3+
### Features
4+
5+
- If `count` is set to a decimal number like 3.5, the component will display 3
6+
full-width skeletons followed by 1 half-width skeleton. (#136)
7+
18
## 3.0.3
29

310
### Bug Fixes

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,12 @@ return (
108108
<tbody>
109109
<tr>
110110
<td><code>count?: number</code></td>
111-
<td>The number of lines of skeletons to render.</td>
111+
<td>
112+
The number of lines of skeletons to render. If
113+
<code>count</code> is a decimal number like 3.5,
114+
three full skeletons and one half-width skeleton will be
115+
rendered.
116+
</td>
112117
<td><code>1</code></td>
113118
</tr>
114119
<tr>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-loading-skeleton",
3-
"version": "3.0.3",
3+
"version": "3.1.0",
44
"description": "Make beautiful, animated loading skeletons that automatically adapt to your app.",
55
"keywords": [
66
"react",

src/Skeleton.tsx

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,42 @@ export function Skeleton({
9696

9797
const elements: ReactElement[] = []
9898

99-
// Without the <br />, the skeleton lines will all run together if
100-
// `width` is specified
101-
for (let i = 0; i < count; i++) {
99+
const countCeil = Math.ceil(count)
100+
101+
for (let i = 0; i < countCeil; i++) {
102+
let thisStyle = style
103+
104+
if (countCeil > count && i === countCeil - 1) {
105+
// count is not an integer and we've reached the last iteration of
106+
// the loop, so add a "fractional" skeleton.
107+
//
108+
// For example, if count is 3.5, we've already added 3 full
109+
// skeletons, so now we add one more skeleton that is 0.5 times the
110+
// original width.
111+
112+
const width = thisStyle.width ?? '100%' // 100% is the default since that's what's in the CSS
113+
114+
const fractionalPart = count % 1
115+
116+
const fractionalWidth =
117+
typeof width === 'number'
118+
? width * fractionalPart
119+
: `calc(${width} * ${fractionalPart})`
120+
121+
thisStyle = { ...thisStyle, width: fractionalWidth }
122+
}
123+
102124
const skeletonSpan = (
103-
<span className={className} style={style} key={i}>
125+
<span className={className} style={thisStyle} key={i}>
104126
&zwnj;
105127
</span>
106128
)
107129

108130
if (inline) {
109131
elements.push(skeletonSpan)
110132
} else {
133+
// Without the <br />, the skeleton lines will all run together if
134+
// `width` is specified
111135
elements.push(
112136
<React.Fragment key={i}>
113137
{skeletonSpan}

src/__stories__/Post.stories.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ export default {
77
title: 'Post',
88
} as Meta
99

10-
export const Default: React.VFC = () => (
10+
export const Default: React.FC = () => (
1111
<SideBySide>
1212
<Post loading />
1313
<Post loading={false} />
1414
</SideBySide>
1515
)
1616

17-
export const Large: React.VFC = () => (
17+
export const Large: React.FC = () => (
1818
<SideBySide>
1919
<Post loading size="large" />
2020
<Post loading={false} size="large" />

src/__stories__/Skeleton.stories.tsx

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ export default {
1616
title: 'Skeleton',
1717
} as Meta
1818

19-
export const Basic: React.VFC = () => <Skeleton count={5} width={400} />
19+
export const Basic: React.FC = () => <Skeleton count={5} width={400} />
2020

21-
export const Inline: React.VFC = () => (
21+
export const Inline: React.FC = () => (
2222
<SideBySide>
2323
<div>
2424
<Skeleton width={100} inline style={{ marginRight: '0.5rem' }} />
@@ -30,13 +30,13 @@ export const Inline: React.VFC = () => (
3030
</SideBySide>
3131
)
3232

33-
export const InlineWithText: React.VFC = () => (
33+
export const InlineWithText: React.FC = () => (
3434
<div>
3535
Some random text <Skeleton width={150} inline /> Some more random text
3636
</div>
3737
)
3838

39-
export const BlockWrapper: React.VFC = () => (
39+
export const BlockWrapper: React.FC = () => (
4040
<SideBySide>
4141
<Skeleton count={5} wrapper={Box} />
4242
<div>
@@ -52,7 +52,7 @@ function InlineWrapperWithMargin({ children }: PropsWithChildren<unknown>): Reac
5252
return <span style={{ marginRight: '0.25rem' }}>{children}</span>
5353
}
5454

55-
export const InlineWrapper: React.VFC = () => (
55+
export const InlineWrapper: React.FC = () => (
5656
<div style={{ lineHeight: 1.5 }}>
5757
<SideBySide>
5858
<div>
@@ -77,7 +77,7 @@ export const InlineWrapper: React.VFC = () => (
7777
</div>
7878
)
7979

80-
export const DifferentDurations: React.VFC = () => (
80+
export const DifferentDurations: React.FC = () => (
8181
<div style={{ width: 500 }}>
8282
<Skeleton duration={1} />
8383
<Skeleton duration={2} />
@@ -86,7 +86,7 @@ export const DifferentDurations: React.VFC = () => (
8686
</div>
8787
)
8888

89-
export const DifferentWidths: React.VFC = () => (
89+
export const DifferentWidths: React.FC = () => (
9090
<div style={{ display: 'flex', flexDirection: 'column' }}>
9191
<Skeleton />
9292
<Skeleton width={50} />
@@ -96,7 +96,7 @@ export const DifferentWidths: React.VFC = () => (
9696
</div>
9797
)
9898

99-
export const DifferentHeights: React.VFC = () => (
99+
export const DifferentHeights: React.FC = () => (
100100
<div style={{ display: 'flex', flexDirection: 'column' }}>
101101
<Skeleton />
102102
<Skeleton height={200} />
@@ -106,11 +106,21 @@ export const DifferentHeights: React.VFC = () => (
106106
</div>
107107
)
108108

109-
export const CustomStyles: React.VFC = () => (
109+
export const CustomStyles: React.FC = () => (
110110
<Skeleton height="100px" style={{ borderRadius: 10, height: 50, width: 50 }} />
111111
)
112112

113-
export const Circle: React.VFC = () => <Skeleton height={50} width={50} circle />
113+
export const Circle: React.FC = () => <Skeleton height={50} width={50} circle />
114+
115+
export const DecimalCount: React.FC = () => <Skeleton count={3.5} />
116+
117+
export const DecimalCountPercentWidth: React.FC = () => (
118+
<Skeleton width="50%" count={3.5} />
119+
)
120+
121+
export const DecimalCountInline: React.FC = () => (
122+
<Skeleton width={100} inline count={3.5} style={{ marginRight: '1rem' }} />
123+
)
114124

115125
// Use https://bennettfeely.com/clippy/ to try out other shapes
116126
const StarWrapper: React.FC<PropsWithChildren<unknown>> = ({ children }) => (
@@ -127,7 +137,7 @@ const StarWrapper: React.FC<PropsWithChildren<unknown>> = ({ children }) => (
127137
</div>
128138
)
129139

130-
export const Stars: React.VFC = () => (
140+
export const Stars: React.FC = () => (
131141
<Skeleton
132142
height="100%"
133143
wrapper={StarWrapper}
@@ -137,9 +147,9 @@ export const Stars: React.VFC = () => (
137147
/>
138148
)
139149

140-
export const RightToLeft: React.VFC = () => <Skeleton direction="rtl" />
150+
export const RightToLeft: React.FC = () => <Skeleton direction="rtl" />
141151

142-
export const DisableAnimation: React.VFC = () => {
152+
export const DisableAnimation: React.FC = () => {
143153
const [enabled, setEnabled] = useState(true)
144154

145155
return (
@@ -158,7 +168,7 @@ export const DisableAnimation: React.VFC = () => {
158168
)
159169
}
160170

161-
export const PercentWidthInFlex: React.VFC = () => (
171+
export const PercentWidthInFlex: React.FC = () => (
162172
<div>
163173
<p>
164174
This is a test for{' '}
@@ -179,7 +189,7 @@ export const PercentWidthInFlex: React.VFC = () => (
179189
</div>
180190
)
181191

182-
export const FillEntireContainer: React.VFC = () => (
192+
export const FillEntireContainer: React.FC = () => (
183193
<div>
184194
<p>
185195
This is a test for{' '}
@@ -231,7 +241,7 @@ function HeightComparison({
231241
)
232242
}
233243

234-
export const HeightQuirk: React.VFC = () => (
244+
export const HeightQuirk: React.FC = () => (
235245
<div>
236246
<p>
237247
This is a demonstration of a Skeleton quirk that was reported in{' '}
@@ -287,7 +297,7 @@ export const HeightQuirk: React.VFC = () => (
287297
</div>
288298
)
289299

290-
export const ShadowDOM: React.VFC = () => {
300+
export const ShadowDOM: React.FC = () => {
291301
const hostRef = useRef<HTMLDivElement | null>(null)
292302
const [portalDestination, setPortalDestination] = useState<HTMLDivElement>()
293303

src/__stories__/SkeletonTheme.stories.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const blueHighlightColor = '#5294e0'
1616
const lightBaseColor = '#c0c0c0'
1717
const lightHighlightColor = '#A0A0A0'
1818

19-
export const WithColors: React.VFC = () => (
19+
export const WithColors: React.FC = () => (
2020
<div>
2121
<SkeletonTheme baseColor={blueBaseColor} highlightColor={blueHighlightColor}>
2222
<Post loading />
@@ -27,7 +27,7 @@ export const WithColors: React.VFC = () => (
2727
</div>
2828
)
2929

30-
export const NoBorderRadius: React.VFC = () => (
30+
export const NoBorderRadius: React.FC = () => (
3131
<SkeletonTheme
3232
baseColor={blueBaseColor}
3333
highlightColor={blueHighlightColor}
@@ -37,7 +37,7 @@ export const NoBorderRadius: React.VFC = () => (
3737
</SkeletonTheme>
3838
)
3939

40-
export const LightAndDarkThemes: React.VFC = () => {
40+
export const LightAndDarkThemes: React.FC = () => {
4141
const [theme, setTheme] = React.useState<'light' | 'dark'>('light')
4242

4343
const handleToggle = () => {
@@ -74,7 +74,7 @@ export const LightAndDarkThemes: React.VFC = () => {
7474
)
7575
}
7676

77-
export const PropsExplicitlySetToUndefined: React.VFC = () => (
77+
export const PropsExplicitlySetToUndefined: React.FC = () => (
7878
<div>
7979
<p>
8080
This is a test for{' '}

src/__tests__/Skeleton.test.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,23 @@ it('renders a skeleton with a wrapper', () => {
118118

119119
expect(box.querySelector(skeletonSelector)).toBeVisible()
120120
})
121+
122+
it('renders a half-width skeleton when count = 1.5', () => {
123+
render(<Skeleton count={1.5} />)
124+
125+
const skeletons = getAllSkeletons()
126+
expect(skeletons).toHaveLength(2)
127+
128+
expect(skeletons[0]).toHaveStyle({ width: '' })
129+
expect(skeletons[1]).toHaveStyle({ width: 'calc(100% * 0.5)' })
130+
})
131+
132+
it('renders a 3/4-width skeleton when count = 1.75 and width is set in pixels', () => {
133+
render(<Skeleton count={1.75} width={100} />)
134+
135+
const skeletons = getAllSkeletons()
136+
expect(skeletons).toHaveLength(2)
137+
138+
expect(skeletons[0]).toHaveStyle({ width: '100px' })
139+
expect(skeletons[1]).toHaveStyle({ width: '75px' })
140+
})

0 commit comments

Comments
 (0)