Skip to content

Commit

Permalink
Merge pull request #139 from dvtng/decimal-count
Browse files Browse the repository at this point in the history
Render fractional-width skeleton if count is non-integer
  • Loading branch information
srmagura authored Apr 10, 2022
2 parents 8826d0c + 084ecab commit 2329bf3
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 29 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 3.1.0

### Features

- If `count` is set to a decimal number like 3.5, the component will display 3
full-width skeletons followed by 1 half-width skeleton. (#136)

## 3.0.3

### Bug Fixes
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,12 @@ return (
<tbody>
<tr>
<td><code>count?: number</code></td>
<td>The number of lines of skeletons to render.</td>
<td>
The number of lines of skeletons to render. If
<code>count</code> is a decimal number like 3.5,
three full skeletons and one half-width skeleton will be
rendered.
</td>
<td><code>1</code></td>
</tr>
<tr>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-loading-skeleton",
"version": "3.0.3",
"version": "3.1.0",
"description": "Make beautiful, animated loading skeletons that automatically adapt to your app.",
"keywords": [
"react",
Expand Down
32 changes: 28 additions & 4 deletions src/Skeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,42 @@ export function Skeleton({

const elements: ReactElement[] = []

// Without the <br />, the skeleton lines will all run together if
// `width` is specified
for (let i = 0; i < count; i++) {
const countCeil = Math.ceil(count)

for (let i = 0; i < countCeil; i++) {
let thisStyle = style

if (countCeil > count && i === countCeil - 1) {
// count is not an integer and we've reached the last iteration of
// the loop, so add a "fractional" skeleton.
//
// For example, if count is 3.5, we've already added 3 full
// skeletons, so now we add one more skeleton that is 0.5 times the
// original width.

const width = thisStyle.width ?? '100%' // 100% is the default since that's what's in the CSS

const fractionalPart = count % 1

const fractionalWidth =
typeof width === 'number'
? width * fractionalPart
: `calc(${width} * ${fractionalPart})`

thisStyle = { ...thisStyle, width: fractionalWidth }
}

const skeletonSpan = (
<span className={className} style={style} key={i}>
<span className={className} style={thisStyle} key={i}>
&zwnj;
</span>
)

if (inline) {
elements.push(skeletonSpan)
} else {
// Without the <br />, the skeleton lines will all run together if
// `width` is specified
elements.push(
<React.Fragment key={i}>
{skeletonSpan}
Expand Down
4 changes: 2 additions & 2 deletions src/__stories__/Post.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ export default {
title: 'Post',
} as Meta

export const Default: React.VFC = () => (
export const Default: React.FC = () => (
<SideBySide>
<Post loading />
<Post loading={false} />
</SideBySide>
)

export const Large: React.VFC = () => (
export const Large: React.FC = () => (
<SideBySide>
<Post loading size="large" />
<Post loading={false} size="large" />
Expand Down
44 changes: 27 additions & 17 deletions src/__stories__/Skeleton.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export default {
title: 'Skeleton',
} as Meta

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

export const Inline: React.VFC = () => (
export const Inline: React.FC = () => (
<SideBySide>
<div>
<Skeleton width={100} inline style={{ marginRight: '0.5rem' }} />
Expand All @@ -30,13 +30,13 @@ export const Inline: React.VFC = () => (
</SideBySide>
)

export const InlineWithText: React.VFC = () => (
export const InlineWithText: React.FC = () => (
<div>
Some random text <Skeleton width={150} inline /> Some more random text
</div>
)

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

export const InlineWrapper: React.VFC = () => (
export const InlineWrapper: React.FC = () => (
<div style={{ lineHeight: 1.5 }}>
<SideBySide>
<div>
Expand All @@ -77,7 +77,7 @@ export const InlineWrapper: React.VFC = () => (
</div>
)

export const DifferentDurations: React.VFC = () => (
export const DifferentDurations: React.FC = () => (
<div style={{ width: 500 }}>
<Skeleton duration={1} />
<Skeleton duration={2} />
Expand All @@ -86,7 +86,7 @@ export const DifferentDurations: React.VFC = () => (
</div>
)

export const DifferentWidths: React.VFC = () => (
export const DifferentWidths: React.FC = () => (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<Skeleton />
<Skeleton width={50} />
Expand All @@ -96,7 +96,7 @@ export const DifferentWidths: React.VFC = () => (
</div>
)

export const DifferentHeights: React.VFC = () => (
export const DifferentHeights: React.FC = () => (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<Skeleton />
<Skeleton height={200} />
Expand All @@ -106,11 +106,21 @@ export const DifferentHeights: React.VFC = () => (
</div>
)

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

export const Circle: React.VFC = () => <Skeleton height={50} width={50} circle />
export const Circle: React.FC = () => <Skeleton height={50} width={50} circle />

export const DecimalCount: React.FC = () => <Skeleton count={3.5} />

export const DecimalCountPercentWidth: React.FC = () => (
<Skeleton width="50%" count={3.5} />
)

export const DecimalCountInline: React.FC = () => (
<Skeleton width={100} inline count={3.5} style={{ marginRight: '1rem' }} />
)

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

export const Stars: React.VFC = () => (
export const Stars: React.FC = () => (
<Skeleton
height="100%"
wrapper={StarWrapper}
Expand All @@ -137,9 +147,9 @@ export const Stars: React.VFC = () => (
/>
)

export const RightToLeft: React.VFC = () => <Skeleton direction="rtl" />
export const RightToLeft: React.FC = () => <Skeleton direction="rtl" />

export const DisableAnimation: React.VFC = () => {
export const DisableAnimation: React.FC = () => {
const [enabled, setEnabled] = useState(true)

return (
Expand All @@ -158,7 +168,7 @@ export const DisableAnimation: React.VFC = () => {
)
}

export const PercentWidthInFlex: React.VFC = () => (
export const PercentWidthInFlex: React.FC = () => (
<div>
<p>
This is a test for{' '}
Expand All @@ -179,7 +189,7 @@ export const PercentWidthInFlex: React.VFC = () => (
</div>
)

export const FillEntireContainer: React.VFC = () => (
export const FillEntireContainer: React.FC = () => (
<div>
<p>
This is a test for{' '}
Expand Down Expand Up @@ -231,7 +241,7 @@ function HeightComparison({
)
}

export const HeightQuirk: React.VFC = () => (
export const HeightQuirk: React.FC = () => (
<div>
<p>
This is a demonstration of a Skeleton quirk that was reported in{' '}
Expand Down Expand Up @@ -287,7 +297,7 @@ export const HeightQuirk: React.VFC = () => (
</div>
)

export const ShadowDOM: React.VFC = () => {
export const ShadowDOM: React.FC = () => {
const hostRef = useRef<HTMLDivElement | null>(null)
const [portalDestination, setPortalDestination] = useState<HTMLDivElement>()

Expand Down
8 changes: 4 additions & 4 deletions src/__stories__/SkeletonTheme.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const blueHighlightColor = '#5294e0'
const lightBaseColor = '#c0c0c0'
const lightHighlightColor = '#A0A0A0'

export const WithColors: React.VFC = () => (
export const WithColors: React.FC = () => (
<div>
<SkeletonTheme baseColor={blueBaseColor} highlightColor={blueHighlightColor}>
<Post loading />
Expand All @@ -27,7 +27,7 @@ export const WithColors: React.VFC = () => (
</div>
)

export const NoBorderRadius: React.VFC = () => (
export const NoBorderRadius: React.FC = () => (
<SkeletonTheme
baseColor={blueBaseColor}
highlightColor={blueHighlightColor}
Expand All @@ -37,7 +37,7 @@ export const NoBorderRadius: React.VFC = () => (
</SkeletonTheme>
)

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

const handleToggle = () => {
Expand Down Expand Up @@ -74,7 +74,7 @@ export const LightAndDarkThemes: React.VFC = () => {
)
}

export const PropsExplicitlySetToUndefined: React.VFC = () => (
export const PropsExplicitlySetToUndefined: React.FC = () => (
<div>
<p>
This is a test for{' '}
Expand Down
20 changes: 20 additions & 0 deletions src/__tests__/Skeleton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,23 @@ it('renders a skeleton with a wrapper', () => {

expect(box.querySelector(skeletonSelector)).toBeVisible()
})

it('renders a half-width skeleton when count = 1.5', () => {
render(<Skeleton count={1.5} />)

const skeletons = getAllSkeletons()
expect(skeletons).toHaveLength(2)

expect(skeletons[0]).toHaveStyle({ width: '' })
expect(skeletons[1]).toHaveStyle({ width: 'calc(100% * 0.5)' })
})

it('renders a 3/4-width skeleton when count = 1.75 and width is set in pixels', () => {
render(<Skeleton count={1.75} width={100} />)

const skeletons = getAllSkeletons()
expect(skeletons).toHaveLength(2)

expect(skeletons[0]).toHaveStyle({ width: '100px' })
expect(skeletons[1]).toHaveStyle({ width: '75px' })
})

0 comments on commit 2329bf3

Please sign in to comment.