[change] modernize ActivityIndicator

Rewrite ActivityIndicator to use function components and hooks.
Rewrite the tests to replace enzyme with testing-library.
This commit is contained in:
Nicolas Gallagher
2020-02-04 13:32:51 -08:00
parent 35236a3cc2
commit 999c2ad122
3 changed files with 135 additions and 401 deletions
@@ -1,47 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ActivityIndicator prop "animating" is "false" 1`] = ` exports[`components/ActivityIndicator prop "animating" is "false" 1`] = `
<View <div
accessibilityRole="progressbar"
aria-valuemax="1" aria-valuemax="1"
aria-valuemin="0" aria-valuemin="0"
style={ class="css-view-1dbjc4n r-alignItems-1awozwy r-justifyContent-1777fci"
Object { role="progressbar"
"alignItems": "center",
"justifyContent": "center",
}
}
> >
<View <div
style={ class="css-view-1dbjc4n r-animationDuration-17bb2tj r-animationIterationCount-1muvv40 r-animationKeyframes-127358a r-animationPlayState-1abnn5w r-animationTimingFunction-1ldzwu0 r-height-z80fyv r-visibility-11j9u27 r-width-19wmn03"
Object {
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationKeyframes": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationPlayState": "paused",
"animationTimingFunction": "linear",
"height": 20,
"visibility": "hidden",
"width": 20,
}
}
> >
<svg <svg
height="100%" height="100%"
@@ -53,73 +20,31 @@ exports[`components/ActivityIndicator prop "animating" is "false" 1`] = `
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; opacity: 0.2;"
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/> />
<circle <circle
cx="16" cx="16"
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; stroke-dasharray: 80; stroke-dashoffset: 60;"
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/> />
</svg> </svg>
</View> </div>
</View> </div>
`; `;
exports[`components/ActivityIndicator prop "animating" is "true" 1`] = ` exports[`components/ActivityIndicator prop "animating" is "true" 1`] = `
<View <div
accessibilityRole="progressbar"
aria-valuemax="1" aria-valuemax="1"
aria-valuemin="0" aria-valuemin="0"
style={ class="css-view-1dbjc4n r-alignItems-1awozwy r-justifyContent-1777fci"
Object { role="progressbar"
"alignItems": "center",
"justifyContent": "center",
}
}
> >
<View <div
style={ class="css-view-1dbjc4n r-animationDuration-17bb2tj r-animationIterationCount-1muvv40 r-animationKeyframes-127358a r-animationTimingFunction-1ldzwu0 r-height-z80fyv r-width-19wmn03"
Object {
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationKeyframes": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationTimingFunction": "linear",
"height": 20,
"width": 20,
}
}
> >
<svg <svg
height="100%" height="100%"
@@ -131,31 +56,20 @@ exports[`components/ActivityIndicator prop "animating" is "true" 1`] = `
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; opacity: 0.2;"
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/> />
<circle <circle
cx="16" cx="16"
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; stroke-dasharray: 80; stroke-dashoffset: 60;"
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/> />
</svg> </svg>
</View> </div>
</View> </div>
`; `;
exports[`components/ActivityIndicator prop "color" 1`] = ` exports[`components/ActivityIndicator prop "color" 1`] = `
@@ -169,72 +83,29 @@ exports[`components/ActivityIndicator prop "color" 1`] = `
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: red; opacity: 0.2;"
Object {
"opacity": 0.2,
"stroke": "red",
}
}
/> />
<circle <circle
cx="16" cx="16"
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: red; stroke-dasharray: 80; stroke-dashoffset: 60;"
Object {
"stroke": "red",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/> />
</svg> </svg>
`; `;
exports[`components/ActivityIndicator prop "hidesWhenStopped" is "false" 1`] = ` exports[`components/ActivityIndicator prop "hidesWhenStopped" is "false" 1`] = `
<View <div
accessibilityRole="progressbar"
aria-valuemax="1" aria-valuemax="1"
aria-valuemin="0" aria-valuemin="0"
style={ class="css-view-1dbjc4n r-alignItems-1awozwy r-justifyContent-1777fci"
Object { role="progressbar"
"alignItems": "center",
"justifyContent": "center",
}
}
> >
<View <div
style={ class="css-view-1dbjc4n r-animationDuration-17bb2tj r-animationIterationCount-1muvv40 r-animationKeyframes-127358a r-animationPlayState-1abnn5w r-animationTimingFunction-1ldzwu0 r-height-z80fyv r-width-19wmn03"
Object {
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationKeyframes": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationPlayState": "paused",
"animationTimingFunction": "linear",
"height": 20,
"width": 20,
}
}
> >
<svg <svg
height="100%" height="100%"
@@ -246,75 +117,31 @@ exports[`components/ActivityIndicator prop "hidesWhenStopped" is "false" 1`] = `
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; opacity: 0.2;"
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/> />
<circle <circle
cx="16" cx="16"
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; stroke-dasharray: 80; stroke-dashoffset: 60;"
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/> />
</svg> </svg>
</View> </div>
</View> </div>
`; `;
exports[`components/ActivityIndicator prop "hidesWhenStopped" is "true" 1`] = ` exports[`components/ActivityIndicator prop "hidesWhenStopped" is "true" 1`] = `
<View <div
accessibilityRole="progressbar"
aria-valuemax="1" aria-valuemax="1"
aria-valuemin="0" aria-valuemin="0"
style={ class="css-view-1dbjc4n r-alignItems-1awozwy r-justifyContent-1777fci"
Object { role="progressbar"
"alignItems": "center",
"justifyContent": "center",
}
}
> >
<View <div
style={ class="css-view-1dbjc4n r-animationDuration-17bb2tj r-animationIterationCount-1muvv40 r-animationKeyframes-127358a r-animationPlayState-1abnn5w r-animationTimingFunction-1ldzwu0 r-height-z80fyv r-visibility-11j9u27 r-width-19wmn03"
Object {
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationKeyframes": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationPlayState": "paused",
"animationTimingFunction": "linear",
"height": 20,
"visibility": "hidden",
"width": 20,
}
}
> >
<svg <svg
height="100%" height="100%"
@@ -326,73 +153,31 @@ exports[`components/ActivityIndicator prop "hidesWhenStopped" is "true" 1`] = `
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; opacity: 0.2;"
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/> />
<circle <circle
cx="16" cx="16"
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; stroke-dasharray: 80; stroke-dashoffset: 60;"
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/> />
</svg> </svg>
</View> </div>
</View> </div>
`; `;
exports[`components/ActivityIndicator prop "size" is "large" 1`] = ` exports[`components/ActivityIndicator prop "size" is "large" 1`] = `
<View <div
accessibilityRole="progressbar"
aria-valuemax="1" aria-valuemax="1"
aria-valuemin="0" aria-valuemin="0"
style={ class="css-view-1dbjc4n r-alignItems-1awozwy r-justifyContent-1777fci"
Object { role="progressbar"
"alignItems": "center",
"justifyContent": "center",
}
}
> >
<View <div
style={ class="css-view-1dbjc4n r-animationDuration-17bb2tj r-animationIterationCount-1muvv40 r-animationKeyframes-127358a r-animationTimingFunction-1ldzwu0 r-height-1r8g8re r-width-1acpoxo"
Object {
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationKeyframes": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationTimingFunction": "linear",
"height": 36,
"width": 36,
}
}
> >
<svg <svg
height="100%" height="100%"
@@ -404,73 +189,32 @@ exports[`components/ActivityIndicator prop "size" is "large" 1`] = `
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; opacity: 0.2;"
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/> />
<circle <circle
cx="16" cx="16"
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; stroke-dasharray: 80; stroke-dashoffset: 60;"
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/> />
</svg> </svg>
</View> </div>
</View> </div>
`; `;
exports[`components/ActivityIndicator prop "size" is a number 1`] = ` exports[`components/ActivityIndicator prop "size" is a number 1`] = `
<View <div
accessibilityRole="progressbar"
aria-valuemax="1" aria-valuemax="1"
aria-valuemin="0" aria-valuemin="0"
style={ class="css-view-1dbjc4n r-alignItems-1awozwy r-justifyContent-1777fci"
Object { role="progressbar"
"alignItems": "center",
"justifyContent": "center",
}
}
> >
<View <div
style={ class="css-view-1dbjc4n r-animationDuration-17bb2tj r-animationIterationCount-1muvv40 r-animationKeyframes-127358a r-animationTimingFunction-1ldzwu0"
Object { style="height: 30px; width: 30px;"
"animationDuration": "0.75s",
"animationIterationCount": "infinite",
"animationKeyframes": Array [
Object {
"0%": Object {
"transform": Array [
Object {
"rotate": "0deg",
},
],
},
"100%": Object {
"transform": Array [
Object {
"rotate": "360deg",
},
],
},
},
],
"animationTimingFunction": "linear",
"height": 30,
"width": 30,
}
}
> >
<svg <svg
height="100%" height="100%"
@@ -482,29 +226,18 @@ exports[`components/ActivityIndicator prop "size" is a number 1`] = `
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; opacity: 0.2;"
Object {
"opacity": 0.2,
"stroke": "#1976D2",
}
}
/> />
<circle <circle
cx="16" cx="16"
cy="16" cy="16"
fill="none" fill="none"
r="14" r="14"
strokeWidth="4" stroke-width="4"
style={ style="stroke: #1976D2; stroke-dasharray: 80; stroke-dashoffset: 60;"
Object {
"stroke": "#1976D2",
"strokeDasharray": 80,
"strokeDashoffset": 60,
}
}
/> />
</svg> </svg>
</View> </div>
</View> </div>
`; `;
@@ -2,47 +2,50 @@
import ActivityIndicator from '..'; import ActivityIndicator from '..';
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { render } from '@testing-library/react';
describe('components/ActivityIndicator', () => { describe('components/ActivityIndicator', () => {
describe('prop "animating"', () => { describe('prop "animating"', () => {
test('is "true"', () => { test('is "true"', () => {
const component = shallow(<ActivityIndicator animating={true} />); const { container } = render(<ActivityIndicator animating={true} />);
expect(component).toMatchSnapshot(); expect(container.firstChild).toMatchSnapshot();
}); });
test('is "false"', () => { test('is "false"', () => {
const component = shallow(<ActivityIndicator animating={false} />); const { container } = render(<ActivityIndicator animating={false} />);
expect(component).toMatchSnapshot(); expect(container.firstChild).toMatchSnapshot();
}); });
}); });
test('prop "color"', () => { test('prop "color"', () => {
const component = shallow(<ActivityIndicator color="red" />).find('svg'); const { container } = render(<ActivityIndicator color="red" />);
expect(component).toMatchSnapshot(); const svg = container.firstChild.querySelector('svg');
expect(svg).toMatchSnapshot();
}); });
describe('prop "hidesWhenStopped"', () => { describe('prop "hidesWhenStopped"', () => {
test('is "true"', () => { test('is "true"', () => {
const component = shallow(<ActivityIndicator animating={false} hidesWhenStopped={true} />); const { container } = render(<ActivityIndicator animating={false} hidesWhenStopped={true} />);
expect(component).toMatchSnapshot(); expect(container.firstChild).toMatchSnapshot();
}); });
test('is "false"', () => { test('is "false"', () => {
const component = shallow(<ActivityIndicator animating={false} hidesWhenStopped={false} />); const { container } = render(
expect(component).toMatchSnapshot(); <ActivityIndicator animating={false} hidesWhenStopped={false} />
);
expect(container.firstChild).toMatchSnapshot();
}); });
}); });
describe('prop "size"', () => { describe('prop "size"', () => {
test('is "large"', () => { test('is "large"', () => {
const component = shallow(<ActivityIndicator size="large" />); const { container } = render(<ActivityIndicator size="large" />);
expect(component).toMatchSnapshot(); expect(container.firstChild).toMatchSnapshot();
}); });
test('is a number', () => { test('is a number', () => {
const component = shallow(<ActivityIndicator size={30} />); const { container } = render(<ActivityIndicator size={30} />);
expect(component).toMatchSnapshot(); expect(container.firstChild).toMatchSnapshot();
}); });
}); });
}); });
@@ -10,10 +10,9 @@
import type { ViewProps } from '../View'; import type { ViewProps } from '../View';
import applyNativeMethods from '../../modules/applyNativeMethods';
import StyleSheet from '../StyleSheet'; import StyleSheet from '../StyleSheet';
import View from '../View'; import View from '../View';
import React from 'react'; import React, { forwardRef } from 'react';
const createSvgCircle = style => ( const createSvgCircle = style => (
<circle cx="16" cy="16" fill="none" r="14" strokeWidth="4" style={style} /> <circle cx="16" cy="16" fill="none" r="14" strokeWidth="4" style={style} />
@@ -27,54 +26,53 @@ type ActivityIndicatorProps = {
size?: 'small' | 'large' | number size?: 'small' | 'large' | number
}; };
class ActivityIndicator extends React.Component<ActivityIndicatorProps> { const ActivityIndicator = forwardRef<ActivityIndicatorProps, *>((props, ref) => {
static displayName = 'ActivityIndicator'; const {
animating = true,
color = '#1976D2',
hidesWhenStopped = true,
size = 'small',
style,
...other
} = props;
render() { const svg = (
const { <svg height="100%" viewBox="0 0 32 32" width="100%">
animating = true, {createSvgCircle({
color = '#1976D2', stroke: color,
hidesWhenStopped = true, opacity: 0.2
size = 'small', })}
style, {createSvgCircle({
...other stroke: color,
} = this.props; strokeDasharray: 80,
strokeDashoffset: 60
})}
</svg>
);
const svg = ( return (
<svg height="100%" viewBox="0 0 32 32" width="100%"> <View
{createSvgCircle({ {...other}
stroke: color, accessibilityRole="progressbar"
opacity: 0.2 aria-valuemax="1"
})} aria-valuemin="0"
{createSvgCircle({ ref={ref}
stroke: color, style={[styles.container, style]}
strokeDasharray: 80, >
strokeDashoffset: 60
})}
</svg>
);
return (
<View <View
{...other} children={svg}
accessibilityRole="progressbar" style={[
aria-valuemax="1" typeof size === 'number' ? { height: size, width: size } : indicatorSizes[size],
aria-valuemin="0" styles.animation,
style={[styles.container, style]} !animating && styles.animationPause,
> !animating && hidesWhenStopped && styles.hidesWhenStopped
<View ]}
children={svg} />
style={[ </View>
typeof size === 'number' ? { height: size, width: size } : indicatorSizes[size], );
styles.animation, });
!animating && styles.animationPause,
!animating && hidesWhenStopped && styles.hidesWhenStopped ActivityIndicator.displayName = 'ActivityIndicator';
]}
/>
</View>
);
}
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@@ -111,4 +109,4 @@ const indicatorSizes = StyleSheet.create({
} }
}); });
export default applyNativeMethods(ActivityIndicator); export default ActivityIndicator;