Skip to content

Commit e8a7b17

Browse files
committed
Add debounce prop to spinner + tests
1 parent 93f2ba3 commit e8a7b17

File tree

2 files changed

+60
-9
lines changed

2 files changed

+60
-9
lines changed

src/components/Spinner.js

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import React, {Fragment} from 'react';
1+
import React, {useEffect, useState} from 'react';
22
import PropTypes from 'prop-types';
3-
import {omit, type} from 'ramda';
3+
import {omit} from 'ramda';
44
import {Spinner as RSSpinner} from 'reactstrap';
55
import {bootstrapColors} from '../private/BootstrapColors';
66

@@ -21,9 +21,22 @@ const Spinner = props => {
2121
fullscreen,
2222
fullscreenClassName,
2323
fullscreen_style,
24+
debounce,
2425
...otherProps
2526
} = props;
2627

28+
const [showSpinner, setShowSpinner] = useState(false);
29+
30+
useEffect(() => {
31+
if (loading_state) {
32+
if (loading_state.is_loading && !showSpinner) {
33+
setShowSpinner(true);
34+
} else if (!loading_state.is_loading && showSpinner) {
35+
setTimeout(() => setShowSpinner(false), debounce);
36+
}
37+
}
38+
}, [loading_state]);
39+
2740
const isBootstrapColor = bootstrapColors.has(color);
2841

2942
const fullscreenStyle = {
@@ -75,8 +88,6 @@ const Spinner = props => {
7588
...spinner_style
7689
};
7790

78-
const showSpinner = loading_state && loading_state.is_loading;
79-
8091
return (
8192
<div style={showSpinner ? hiddenStyle : {}}>
8293
{children}
@@ -105,6 +116,10 @@ const Spinner = props => {
105116

106117
Spinner._dashprivate_isLoadingComponent = true;
107118

119+
Spinner.defaultProps = {
120+
debounce: 0
121+
};
122+
108123
Spinner.propTypes = {
109124
/**
110125
* The ID of this component, used to identify dash components
@@ -162,7 +177,13 @@ Spinner.propTypes = {
162177
* Boolean that determines if the loading spinner will be displayed
163178
* full-screen or not.
164179
*/
165-
fullscreen: PropTypes.bool
180+
fullscreen: PropTypes.bool,
181+
182+
/**
183+
* When using the spinner as a loading spinner, add a time delay to the
184+
* spinner being removed to prevent flickering.
185+
*/
186+
debounce: PropTypes.number
166187
};
167188

168189
export default Spinner;

src/components/__tests__/Spinner.test.js

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import React from 'react';
2-
import {render} from '@testing-library/react';
2+
import {act, render} from '@testing-library/react';
33
import Spinner from '../Spinner';
44

5+
jest.useFakeTimers();
6+
57
describe('Spinner', () => {
68
test('renders a div with class "border-spinner"', () => {
79
const spinner = render(<Spinner />);
@@ -12,17 +14,18 @@ describe('Spinner', () => {
1214
});
1315

1416
test("renders its content if object isn't loading", () => {
15-
const {container: All, rerender} = render(
17+
const {container: container, rerender} = render(
1618
<Spinner>Some spinner content</Spinner>
1719
);
1820

19-
expect(All).toHaveTextContent('Some spinner content');
21+
expect(container).toHaveTextContent('Some spinner content');
22+
expect(container.querySelector('div.spinner-border')).toBe(null);
2023

2124
rerender(
2225
<Spinner loading_state={{is_loading: true}}>Some spinner content</Spinner>
2326
);
2427

25-
const overAll = All.firstChild;
28+
const overAll = container.firstChild;
2629
const spinner = overAll.lastChild;
2730

2831
expect(overAll).toHaveTextContent('Some spinner content');
@@ -64,4 +67,31 @@ describe('Spinner', () => {
6467
expect(spinnerSuccess).toHaveClass('text-success');
6568
expect(spinnerDark).toHaveClass('text-dark');
6669
});
70+
71+
test('spinner can be debounced with debounce prop', () => {
72+
const {container: container, rerender} = render(
73+
<Spinner loading_state={{is_loading: true}} debounce={1000}>
74+
Some spinner content
75+
</Spinner>
76+
);
77+
78+
const overAll = container.firstChild;
79+
const spinner = overAll.lastChild;
80+
81+
expect(overAll).toHaveTextContent('Some spinner content');
82+
expect(spinner.firstChild).toHaveClass('spinner-border');
83+
84+
rerender(
85+
<Spinner loading_state={{is_loading: false}} debounce={1000}>
86+
Some spinner content
87+
</Spinner>
88+
);
89+
90+
expect(overAll).toHaveTextContent('Some spinner content');
91+
expect(spinner.firstChild).toHaveClass('spinner-border');
92+
93+
act(() => jest.advanceTimersByTime(1000));
94+
95+
expect(container.querySelector('div.spinner-border')).toBe(null);
96+
});
6797
});

0 commit comments

Comments
 (0)