Skip to content

Commit

Permalink
feat(beforemask): add beforeMask prop for custom shape (#267)
Browse files Browse the repository at this point in the history
Co-authored-by: Danilo Woznica <[email protected]>
  • Loading branch information
liuli and danilowoz authored Dec 5, 2021
1 parent 62161fb commit b20fe47
Show file tree
Hide file tree
Showing 14 changed files with 445 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ const MyLoader = () => (
| **`foregroundOpacity?: number`** <br /> Defaults to `1` | React DOM only | Animation opacity (0 = transparent, 1 = opaque)<br/>used to solve an issue in [Safari](#safari--ios) |
| **`style?: React.CSSProperties`** <br /> Defaults to `{}` | React DOM only | |
| **`uniqueKey?: string`** <br /> Defaults to random unique id | React DOM only | Use the same value of prop key, <br/>that will solve inconsistency on the SSR, see more [here](https://github.com/danilowoz/react-content-loader/issues/78). |
| **`beforeMask?: JSX.Element`** <br /> Defaults to null | React DOM<br/>React Native | Define custom shapes before content, <br/>see more [here](https://github.com/danilowoz/react-content-loader/issues/266). |

See all options [live](https://danilowoz.com/react-content-loader/)

Expand Down
42 changes: 42 additions & 0 deletions docs/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -426,3 +426,45 @@ export const contentLoaderVsSVG = () => {
</>
)
}

/**
* beforeMask
*/
export const BeforeMask = () => {
return (
<>
<SyntaxCode>
{`<ContentLoader
viewBox="0 0 308 88"
beforeMask={
<rect width="306" height="86" y="1" x="1" stroke="#dee0e3" strokeWidth="1" fill="#fff" />
}
>
<rect x="12" y="13" rx="4" ry="4" width="20" height="20"></rect>
<rect x="40" y="16" rx="4" ry="4" width="80" height="14"></rect>
<rect x="12" y="41" rx="4" ry="4" width="270" height="12"></rect>
<rect x="12" y="61" rx="4" ry="4" width="270" height="12"></rect>
</ContentLoader>`}
</SyntaxCode>
<ContentLoader
viewBox="0 0 300 88"
beforeMask={
<rect
width="298"
height="86"
y="1"
x="1"
stroke="#dee0e3"
strokeWidth="1"
fill="#fff"
/>
}
>
<rect x="12" y="13" rx="4" ry="4" width="20" height="20"></rect>
<rect x="40" y="16" rx="4" ry="4" width="80" height="14"></rect>
<rect x="12" y="41" rx="4" ry="4" width="270" height="12"></rect>
<rect x="12" y="61" rx="4" ry="4" width="270" height="12"></rect>
</ContentLoader>
</>
)
}
6 changes: 5 additions & 1 deletion src/native/Svg.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Component } from 'react'
import React, { Component, isValidElement } from 'react'
import { Animated } from 'react-native'
import Svg, {
ClipPath,
Expand All @@ -22,6 +22,7 @@ class NativeSvg extends Component<IContentLoaderProps> {
speed: 1.2,
interval: 0.25,
style: {},
beforeMask: null,
}

animatedValue = new Animated.Value(-1)
Expand Down Expand Up @@ -76,6 +77,7 @@ class NativeSvg extends Component<IContentLoaderProps> {
foregroundColor,
rtl,
style,
beforeMask,
...props
} = this.props

Expand All @@ -101,6 +103,8 @@ class NativeSvg extends Component<IContentLoaderProps> {

return (
<Svg style={svgStyle} {...props}>
{beforeMask && isValidElement(beforeMask) ? beforeMask : null}

<Rect
x="0"
y="0"
Expand Down
10 changes: 10 additions & 0 deletions src/native/__tests__/ContentLoader.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ describe('ContentLoader', () => {
speed={10}
style={{ marginBottom: '10px' }}
width={200}
beforeMask={<Rect />}
>
<Rect />
</ContentLoader>
Expand Down Expand Up @@ -121,6 +122,15 @@ describe('ContentLoader', () => {
expect(typeof propsFromFullField.rtl).toBe('boolean')
expect(propsFromFullField.rtl).toBe(true)
})

it("`beforeMask` is a JSX Element and it's used", () => {
// defaultProps
expect(typeof propsFromEmpty.beforeMask).toBe('object')
expect(propsFromEmpty.beforeMask).toBe(null)
// custom props
expect(typeof propsFromFullField.beforeMask).toBe('object')
expect(propsFromFullField.beforeMask).toEqual(<Rect />)
})
})

describe('when using SVG', () => {
Expand Down
29 changes: 28 additions & 1 deletion src/native/__tests__/Svg.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react'
import Svg, { ClipPath, LinearGradient, Stop } from 'react-native-svg'
import * as renderer from 'react-test-renderer'

import ContentLoader from '..'
import ContentLoader, { Rect } from '..'

interface IPredicateArgs {
type: any
Expand Down Expand Up @@ -77,4 +77,31 @@ describe('Svg', () => {
expect(rectClipPath.props.fill).toBe(`url(#${linearGradient.props.id})`)
})
})

describe('beforeMask', () => {
it('beforeMask is used', () => {
const wrapperWithBeforeMask = renderer.create(
<ContentLoader beforeMask={<Rect x="123" />} />
).root

const beforeMask = wrapperWithBeforeMask.findByProps({
x: '123',
})

expect(beforeMask.props.x).toBe('123')
})

it('beforeMask should be a JSX Element', () => {
const wrapperWithBeforeMask = renderer.create(
// @ts-ignore
<ContentLoader beforeMask={() => <Rect x="123" />} />
).root

expect(() => {
wrapperWithBeforeMask.findByProps({
x: '123',
})
}).toThrow('No instances found with props: {"x":"123"}')
})
})
})
94 changes: 94 additions & 0 deletions src/native/__tests__/__snapshots__/snapshots.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,100 @@ exports[`ContentLoader snapshots renders correctly the basic version 1`] = `
</Svg>
`;

exports[`ContentLoader snapshots renders correctly with beforeMask 1`] = `
<Svg
interval={0.25}
style={Object {}}
>
<Rect
x="123"
/>
<Rect
x="456"
/>
<Rect
clipPath="url(#snapshots-animated-diff)"
fill="url(#snapshots-diff)"
height="100%"
width="100%"
x="0"
y="0"
/>
<Defs>
<ClipPath
id="snapshots-animated-diff"
>
<Rect />
</ClipPath>
<LinearGradient
id="snapshots-diff"
style={Object {}}
x1="-100%"
x2="0%"
y1={0}
y2={0}
>
<Stop
offset={0}
stopColor="#f5f6f7"
/>
<Stop
offset={0.5}
stopColor="#eee"
/>
<Stop
offset={1}
stopColor="#f5f6f7"
/>
</LinearGradient>
</Defs>
</Svg>
`;

exports[`ContentLoader snapshots renders correctly with beforeMask 2`] = `
<Svg
interval={0.25}
style={Object {}}
>
<Rect
clipPath="url(#snapshots-animated-diff)"
fill="url(#snapshots-diff)"
height="100%"
width="100%"
x="0"
y="0"
/>
<Defs>
<ClipPath
id="snapshots-animated-diff"
>
<Rect />
</ClipPath>
<LinearGradient
id="snapshots-diff"
style={Object {}}
x1="-100%"
x2="0%"
y1={0}
y2={0}
>
<Stop
offset={0}
stopColor="#f5f6f7"
/>
<Stop
offset={0.5}
stopColor="#eee"
/>
<Stop
offset={1}
stopColor="#f5f6f7"
/>
</LinearGradient>
</Defs>
</Svg>
`;

exports[`ContentLoader snapshots renders correctly with viewBox defined 1`] = `
<Svg
height={124}
Expand Down
32 changes: 31 additions & 1 deletion src/native/__tests__/snapshots.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react'
import * as renderer from 'react-test-renderer'

import ContentLoader from '../ContentLoader'
import ContentLoader, { Rect } from '../ContentLoader'

describe('ContentLoader snapshots', () => {
test('renders correctly the basic version', () => {
Expand Down Expand Up @@ -49,4 +49,34 @@ describe('ContentLoader snapshots', () => {

expect(tree).toMatchSnapshot()
})

test('renders correctly with beforeMask', () => {
let wrapper = renderer.create(
<ContentLoader
uniqueKey="snapshots"
beforeMask={
<>
<Rect x="123" />
<Rect x="456" />
</>
}
>
<Rect />
</ContentLoader>
)
let tree = wrapper.toJSON()

expect(tree).toMatchSnapshot()

// with wrong type
wrapper = renderer.create(
// @ts-ignore
<ContentLoader uniqueKey="snapshots" beforeMask={() => <Rect />}>
<Rect />
</ContentLoader>
)
tree = wrapper.toJSON()

expect(tree).toMatchSnapshot()
})
})
1 change: 1 addition & 0 deletions src/native/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface IContentLoaderProps extends SvgProps {
speed?: number
interval?: number
uniqueKey?: string
beforeMask?: JSX.Element
}

export { default as Facebook } from './presets/FacebookStyle'
Expand Down
3 changes: 3 additions & 0 deletions src/web/Svg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const SVG: React.FC<IContentLoaderProps> = ({
speed,
style,
title,
beforeMask,
...props
}) => {
const fixedId = uniqueKey || uid()
Expand All @@ -40,6 +41,7 @@ const SVG: React.FC<IContentLoaderProps> = ({
{...props}
>
{title ? <title id={idAria}>{title}</title> : null}
{beforeMask && React.isValidElement(beforeMask) ? beforeMask : null}
<rect
role="presentation"
x="0"
Expand Down Expand Up @@ -123,6 +125,7 @@ SVG.defaultProps = {
speed: 1.2,
style: {},
title: 'Loading...',
beforeMask: null,
}

export default SVG
10 changes: 10 additions & 0 deletions src/web/__tests__/ContentLoader.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('ContentLoader', () => {
title="My custom loading title"
uniqueKey="my-id"
width={200}
beforeMask={<rect />}
>
<rect />
</ContentLoader>
Expand Down Expand Up @@ -200,5 +201,14 @@ describe('ContentLoader', () => {
expect(typeof propsFromFullfield.uniqueKey).toBe('string')
expect(propsFromFullfield.uniqueKey).toBe('my-id')
})

it("`beforeMask` is a JSX Element and it's used", () => {
// defaultProps
expect(typeof propsFromEmpty.beforeMask).toBe('object')
expect(propsFromEmpty.beforeMask).toBe(null)
// custom props
expect(typeof propsFromFullfield.beforeMask).toBe('object')
expect(propsFromFullfield.beforeMask).toEqual(<rect />)
})
})
})
27 changes: 27 additions & 0 deletions src/web/__tests__/Svg.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,31 @@ describe('Svg', () => {
expect(title.props.children.length).not.toBe(0)
})
})

describe('beforeMask', () => {
it('beforeMask is used', () => {
const wrapperWithBeforeMask = renderer.create(
<Svg beforeMask={<rect role="beforeMask" />} />
).root

const beforeMask = wrapperWithBeforeMask.findByProps({
role: 'beforeMask',
})

expect(beforeMask.props.role).toBe('beforeMask')
})

it('beforeMask should be a JSX Element', () => {
const wrapperWithBeforeMask = renderer.create(
// @ts-ignore
<Svg beforeMask={() => <rect role="beforeMask" />} />
).root

expect(() => {
wrapperWithBeforeMask.findByProps({
role: 'beforeMask',
})
}).toThrow('No instances found with props: {"role":"beforeMask"}')
})
})
})
Loading

0 comments on commit b20fe47

Please sign in to comment.