Skip to content

Commit b20fe47

Browse files
liulidanilowoz
andauthored
feat(beforemask): add beforeMask prop for custom shape (#267)
Co-authored-by: Danilo Woznica <[email protected]>
1 parent 62161fb commit b20fe47

File tree

14 files changed

+445
-3
lines changed

14 files changed

+445
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ const MyLoader = () => (
125125
| **`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) |
126126
| **`style?: React.CSSProperties`** <br /> Defaults to `{}` | React DOM only | |
127127
| **`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). |
128+
| **`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). |
128129

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

docs/index.stories.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,45 @@ export const contentLoaderVsSVG = () => {
426426
</>
427427
)
428428
}
429+
430+
/**
431+
* beforeMask
432+
*/
433+
export const BeforeMask = () => {
434+
return (
435+
<>
436+
<SyntaxCode>
437+
{`<ContentLoader
438+
viewBox="0 0 308 88"
439+
beforeMask={
440+
<rect width="306" height="86" y="1" x="1" stroke="#dee0e3" strokeWidth="1" fill="#fff" />
441+
}
442+
>
443+
<rect x="12" y="13" rx="4" ry="4" width="20" height="20"></rect>
444+
<rect x="40" y="16" rx="4" ry="4" width="80" height="14"></rect>
445+
<rect x="12" y="41" rx="4" ry="4" width="270" height="12"></rect>
446+
<rect x="12" y="61" rx="4" ry="4" width="270" height="12"></rect>
447+
</ContentLoader>`}
448+
</SyntaxCode>
449+
<ContentLoader
450+
viewBox="0 0 300 88"
451+
beforeMask={
452+
<rect
453+
width="298"
454+
height="86"
455+
y="1"
456+
x="1"
457+
stroke="#dee0e3"
458+
strokeWidth="1"
459+
fill="#fff"
460+
/>
461+
}
462+
>
463+
<rect x="12" y="13" rx="4" ry="4" width="20" height="20"></rect>
464+
<rect x="40" y="16" rx="4" ry="4" width="80" height="14"></rect>
465+
<rect x="12" y="41" rx="4" ry="4" width="270" height="12"></rect>
466+
<rect x="12" y="61" rx="4" ry="4" width="270" height="12"></rect>
467+
</ContentLoader>
468+
</>
469+
)
470+
}

src/native/Svg.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { Component } from 'react'
1+
import React, { Component, isValidElement } from 'react'
22
import { Animated } from 'react-native'
33
import Svg, {
44
ClipPath,
@@ -22,6 +22,7 @@ class NativeSvg extends Component<IContentLoaderProps> {
2222
speed: 1.2,
2323
interval: 0.25,
2424
style: {},
25+
beforeMask: null,
2526
}
2627

2728
animatedValue = new Animated.Value(-1)
@@ -76,6 +77,7 @@ class NativeSvg extends Component<IContentLoaderProps> {
7677
foregroundColor,
7778
rtl,
7879
style,
80+
beforeMask,
7981
...props
8082
} = this.props
8183

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

102104
return (
103105
<Svg style={svgStyle} {...props}>
106+
{beforeMask && isValidElement(beforeMask) ? beforeMask : null}
107+
104108
<Rect
105109
x="0"
106110
y="0"

src/native/__tests__/ContentLoader.test.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ describe('ContentLoader', () => {
4444
speed={10}
4545
style={{ marginBottom: '10px' }}
4646
width={200}
47+
beforeMask={<Rect />}
4748
>
4849
<Rect />
4950
</ContentLoader>
@@ -121,6 +122,15 @@ describe('ContentLoader', () => {
121122
expect(typeof propsFromFullField.rtl).toBe('boolean')
122123
expect(propsFromFullField.rtl).toBe(true)
123124
})
125+
126+
it("`beforeMask` is a JSX Element and it's used", () => {
127+
// defaultProps
128+
expect(typeof propsFromEmpty.beforeMask).toBe('object')
129+
expect(propsFromEmpty.beforeMask).toBe(null)
130+
// custom props
131+
expect(typeof propsFromFullField.beforeMask).toBe('object')
132+
expect(propsFromFullField.beforeMask).toEqual(<Rect />)
133+
})
124134
})
125135

126136
describe('when using SVG', () => {

src/native/__tests__/Svg.test.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react'
22
import Svg, { ClipPath, LinearGradient, Stop } from 'react-native-svg'
33
import * as renderer from 'react-test-renderer'
44

5-
import ContentLoader from '..'
5+
import ContentLoader, { Rect } from '..'
66

77
interface IPredicateArgs {
88
type: any
@@ -77,4 +77,31 @@ describe('Svg', () => {
7777
expect(rectClipPath.props.fill).toBe(`url(#${linearGradient.props.id})`)
7878
})
7979
})
80+
81+
describe('beforeMask', () => {
82+
it('beforeMask is used', () => {
83+
const wrapperWithBeforeMask = renderer.create(
84+
<ContentLoader beforeMask={<Rect x="123" />} />
85+
).root
86+
87+
const beforeMask = wrapperWithBeforeMask.findByProps({
88+
x: '123',
89+
})
90+
91+
expect(beforeMask.props.x).toBe('123')
92+
})
93+
94+
it('beforeMask should be a JSX Element', () => {
95+
const wrapperWithBeforeMask = renderer.create(
96+
// @ts-ignore
97+
<ContentLoader beforeMask={() => <Rect x="123" />} />
98+
).root
99+
100+
expect(() => {
101+
wrapperWithBeforeMask.findByProps({
102+
x: '123',
103+
})
104+
}).toThrow('No instances found with props: {"x":"123"}')
105+
})
106+
})
80107
})

src/native/__tests__/__snapshots__/snapshots.test.tsx.snap

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,100 @@ exports[`ContentLoader snapshots renders correctly the basic version 1`] = `
8686
</Svg>
8787
`;
8888

89+
exports[`ContentLoader snapshots renders correctly with beforeMask 1`] = `
90+
<Svg
91+
interval={0.25}
92+
style={Object {}}
93+
>
94+
<Rect
95+
x="123"
96+
/>
97+
<Rect
98+
x="456"
99+
/>
100+
<Rect
101+
clipPath="url(#snapshots-animated-diff)"
102+
fill="url(#snapshots-diff)"
103+
height="100%"
104+
width="100%"
105+
x="0"
106+
y="0"
107+
/>
108+
<Defs>
109+
<ClipPath
110+
id="snapshots-animated-diff"
111+
>
112+
<Rect />
113+
</ClipPath>
114+
<LinearGradient
115+
id="snapshots-diff"
116+
style={Object {}}
117+
x1="-100%"
118+
x2="0%"
119+
y1={0}
120+
y2={0}
121+
>
122+
<Stop
123+
offset={0}
124+
stopColor="#f5f6f7"
125+
/>
126+
<Stop
127+
offset={0.5}
128+
stopColor="#eee"
129+
/>
130+
<Stop
131+
offset={1}
132+
stopColor="#f5f6f7"
133+
/>
134+
</LinearGradient>
135+
</Defs>
136+
</Svg>
137+
`;
138+
139+
exports[`ContentLoader snapshots renders correctly with beforeMask 2`] = `
140+
<Svg
141+
interval={0.25}
142+
style={Object {}}
143+
>
144+
<Rect
145+
clipPath="url(#snapshots-animated-diff)"
146+
fill="url(#snapshots-diff)"
147+
height="100%"
148+
width="100%"
149+
x="0"
150+
y="0"
151+
/>
152+
<Defs>
153+
<ClipPath
154+
id="snapshots-animated-diff"
155+
>
156+
<Rect />
157+
</ClipPath>
158+
<LinearGradient
159+
id="snapshots-diff"
160+
style={Object {}}
161+
x1="-100%"
162+
x2="0%"
163+
y1={0}
164+
y2={0}
165+
>
166+
<Stop
167+
offset={0}
168+
stopColor="#f5f6f7"
169+
/>
170+
<Stop
171+
offset={0.5}
172+
stopColor="#eee"
173+
/>
174+
<Stop
175+
offset={1}
176+
stopColor="#f5f6f7"
177+
/>
178+
</LinearGradient>
179+
</Defs>
180+
</Svg>
181+
`;
182+
89183
exports[`ContentLoader snapshots renders correctly with viewBox defined 1`] = `
90184
<Svg
91185
height={124}

src/native/__tests__/snapshots.test.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react'
22
import * as renderer from 'react-test-renderer'
33

4-
import ContentLoader from '../ContentLoader'
4+
import ContentLoader, { Rect } from '../ContentLoader'
55

66
describe('ContentLoader snapshots', () => {
77
test('renders correctly the basic version', () => {
@@ -49,4 +49,34 @@ describe('ContentLoader snapshots', () => {
4949

5050
expect(tree).toMatchSnapshot()
5151
})
52+
53+
test('renders correctly with beforeMask', () => {
54+
let wrapper = renderer.create(
55+
<ContentLoader
56+
uniqueKey="snapshots"
57+
beforeMask={
58+
<>
59+
<Rect x="123" />
60+
<Rect x="456" />
61+
</>
62+
}
63+
>
64+
<Rect />
65+
</ContentLoader>
66+
)
67+
let tree = wrapper.toJSON()
68+
69+
expect(tree).toMatchSnapshot()
70+
71+
// with wrong type
72+
wrapper = renderer.create(
73+
// @ts-ignore
74+
<ContentLoader uniqueKey="snapshots" beforeMask={() => <Rect />}>
75+
<Rect />
76+
</ContentLoader>
77+
)
78+
tree = wrapper.toJSON()
79+
80+
expect(tree).toMatchSnapshot()
81+
})
5282
})

src/native/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface IContentLoaderProps extends SvgProps {
1010
speed?: number
1111
interval?: number
1212
uniqueKey?: string
13+
beforeMask?: JSX.Element
1314
}
1415

1516
export { default as Facebook } from './presets/FacebookStyle'

src/web/Svg.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const SVG: React.FC<IContentLoaderProps> = ({
1919
speed,
2020
style,
2121
title,
22+
beforeMask,
2223
...props
2324
}) => {
2425
const fixedId = uniqueKey || uid()
@@ -40,6 +41,7 @@ const SVG: React.FC<IContentLoaderProps> = ({
4041
{...props}
4142
>
4243
{title ? <title id={idAria}>{title}</title> : null}
44+
{beforeMask && React.isValidElement(beforeMask) ? beforeMask : null}
4345
<rect
4446
role="presentation"
4547
x="0"
@@ -123,6 +125,7 @@ SVG.defaultProps = {
123125
speed: 1.2,
124126
style: {},
125127
title: 'Loading...',
128+
beforeMask: null,
126129
}
127130

128131
export default SVG

src/web/__tests__/ContentLoader.test.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ describe('ContentLoader', () => {
5151
title="My custom loading title"
5252
uniqueKey="my-id"
5353
width={200}
54+
beforeMask={<rect />}
5455
>
5556
<rect />
5657
</ContentLoader>
@@ -200,5 +201,14 @@ describe('ContentLoader', () => {
200201
expect(typeof propsFromFullfield.uniqueKey).toBe('string')
201202
expect(propsFromFullfield.uniqueKey).toBe('my-id')
202203
})
204+
205+
it("`beforeMask` is a JSX Element and it's used", () => {
206+
// defaultProps
207+
expect(typeof propsFromEmpty.beforeMask).toBe('object')
208+
expect(propsFromEmpty.beforeMask).toBe(null)
209+
// custom props
210+
expect(typeof propsFromFullfield.beforeMask).toBe('object')
211+
expect(propsFromFullfield.beforeMask).toEqual(<rect />)
212+
})
203213
})
204214
})

0 commit comments

Comments
 (0)