Skip to content

Commit 735d993

Browse files
authored
Persistence improvements (#872)
* Persistence improvements * Fix tests
1 parent 3b771aa commit 735d993

File tree

10 files changed

+187
-23
lines changed

10 files changed

+187
-23
lines changed

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/alert/Alert.js

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ const Alert = props => {
5555
className={class_name || className}
5656
transition={fade}
5757
style={!isBootstrapColor ? {backgroundColor: color, ...style} : style}
58-
{...omit(['setProps'], otherProps)}
58+
{...omit(
59+
['persistence', 'persisted_props', 'persistence_type', 'setProps'],
60+
otherProps
61+
)}
5962
data-dash-is-loading={
6063
(loading_state && loading_state.is_loading) || undefined
6164
}
@@ -68,7 +71,9 @@ const Alert = props => {
6871
Alert.defaultProps = {
6972
color: 'success',
7073
is_open: true,
71-
duration: null
74+
duration: null,
75+
persisted_props: ['is_open'],
76+
persistence_type: 'local'
7277
};
7378

7479
Alert.propTypes = {
@@ -153,7 +158,36 @@ Alert.propTypes = {
153158
* Holds the name of the component that is loading
154159
*/
155160
component_name: PropTypes.string
156-
})
161+
}),
162+
163+
/**
164+
* Used to allow user interactions in this component to be persisted when
165+
* the component - or the page - is refreshed. If `persisted` is truthy and
166+
* hasn't changed from its previous value, a `value` that the user has
167+
* changed while using the app will keep that change, as long as
168+
* the new `value` also matches what was given originally.
169+
* Used in conjunction with `persistence_type`.
170+
*/
171+
persistence: PropTypes.oneOfType([
172+
PropTypes.bool,
173+
PropTypes.string,
174+
PropTypes.number
175+
]),
176+
177+
/**
178+
* Properties whose user interactions will persist after refreshing the
179+
* component or the page. Since only `value` is allowed this prop can
180+
* normally be ignored.
181+
*/
182+
persisted_props: PropTypes.arrayOf(PropTypes.oneOf(['is_open'])),
183+
184+
/**
185+
* Where persisted user changes will be stored:
186+
* memory: only kept in memory, reset on page refresh.
187+
* local: window.localStorage, data is kept after the browser quit.
188+
* session: window.sessionStorage, data is cleared once the browser quit.
189+
*/
190+
persistence_type: PropTypes.oneOf(['local', 'session', 'memory'])
157191
};
158192

159193
export default Alert;

src/components/carousel/Carousel.js

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ const Carousel = props => {
5555
activeIndex={active_index}
5656
onSelect={idx => setProps({active_index: idx})}
5757
interval={interval || null}
58-
{...omit(['setProps'], otherProps)}
58+
{...omit(
59+
['persistence', 'persisted_props', 'persistence_type', 'setProps'],
60+
otherProps
61+
)}
5962
>
6063
{slides}
6164
</RBCarousel>
@@ -66,7 +69,9 @@ const Carousel = props => {
6669
Carousel.defaultProps = {
6770
active_index: 0,
6871
controls: true,
69-
indicators: true
72+
indicators: true,
73+
persisted_props: ['active_index'],
74+
persistence_type: 'local'
7075
};
7176

7277
Carousel.propTypes = {
@@ -202,6 +207,35 @@ Carousel.propTypes = {
202207
component_name: PropTypes.string
203208
}),
204209

210+
/**
211+
* Used to allow user interactions in this component to be persisted when
212+
* the component - or the page - is refreshed. If `persisted` is truthy and
213+
* hasn't changed from its previous value, a `value` that the user has
214+
* changed while using the app will keep that change, as long as
215+
* the new `value` also matches what was given originally.
216+
* Used in conjunction with `persistence_type`.
217+
*/
218+
persistence: PropTypes.oneOfType([
219+
PropTypes.bool,
220+
PropTypes.string,
221+
PropTypes.number
222+
]),
223+
224+
/**
225+
* Properties whose user interactions will persist after refreshing the
226+
* component or the page. Since only `value` is allowed this prop can
227+
* normally be ignored.
228+
*/
229+
persisted_props: PropTypes.arrayOf(PropTypes.oneOf(['active_index'])),
230+
231+
/**
232+
* Where persisted user changes will be stored:
233+
* memory: only kept in memory, reset on page refresh.
234+
* local: window.localStorage, data is kept after the browser quit.
235+
* session: window.sessionStorage, data is cleared once the browser quit.
236+
*/
237+
persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),
238+
205239
/**
206240
* Dash-assigned callback that gets fired when the value changes.
207241
*/

src/components/popover/Popover.js

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3+
import {omit} from 'ramda';
34

45
import {PopoverTemplate} from '../../private/OverlayTemplates';
56
import Overlay from '../../private/Overlay';
@@ -52,7 +53,10 @@ const Popover = props => {
5253
}
5354
defaultShow={is_open}
5455
popperConfig={popperConfig}
55-
{...otherProps}
56+
{...omit(
57+
['persistence', 'persisted_props', 'persistence_type'],
58+
otherProps
59+
)}
5660
>
5761
<PopoverTemplate
5862
// to ensure proper backwards compatibility, the toggle function is only
@@ -73,7 +77,9 @@ Popover.defaultProps = {
7377
delay: {show: 0, hide: 50},
7478
placement: 'right',
7579
flip: true,
76-
autohide: false
80+
autohide: false,
81+
persisted_props: ['is_open'],
82+
persistence_type: 'local'
7783
};
7884

7985
Popover.propTypes = {
@@ -230,7 +236,36 @@ Popover.propTypes = {
230236
* Holds the name of the component that is loading
231237
*/
232238
component_name: PropTypes.string
233-
})
239+
}),
240+
241+
/**
242+
* Used to allow user interactions in this component to be persisted when
243+
* the component - or the page - is refreshed. If `persisted` is truthy and
244+
* hasn't changed from its previous value, a `value` that the user has
245+
* changed while using the app will keep that change, as long as
246+
* the new `value` also matches what was given originally.
247+
* Used in conjunction with `persistence_type`.
248+
*/
249+
persistence: PropTypes.oneOfType([
250+
PropTypes.bool,
251+
PropTypes.string,
252+
PropTypes.number
253+
]),
254+
255+
/**
256+
* Properties whose user interactions will persist after refreshing the
257+
* component or the page. Since only `value` is allowed this prop can
258+
* normally be ignored.
259+
*/
260+
persisted_props: PropTypes.arrayOf(PropTypes.oneOf(['is_open'])),
261+
262+
/**
263+
* Where persisted user changes will be stored:
264+
* memory: only kept in memory, reset on page refresh.
265+
* local: window.localStorage, data is kept after the browser quit.
266+
* session: window.sessionStorage, data is cleared once the browser quit.
267+
*/
268+
persistence_type: PropTypes.oneOf(['local', 'session', 'memory'])
234269
};
235270

236271
export default Popover;

src/components/popover/__tests__/Popover.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import React from 'react';
66
import {render} from '@testing-library/react';
77
import Popover from '../Popover';
88

9+
jest.useFakeTimers();
10+
911
// TODO - Add test for offset
1012

1113
describe('Popover', () => {
@@ -38,6 +40,8 @@ describe('Popover', () => {
3840
container: document.body.appendChild(div)
3941
});
4042

43+
jest.runAllTimers();
44+
4145
expect(document.body.querySelector('.popover.show')).not.toBe(null);
4246
});
4347

@@ -46,6 +50,8 @@ describe('Popover', () => {
4650
container: document.body.appendChild(div)
4751
});
4852

53+
jest.runAllTimers();
54+
4955
expect(document.body.querySelector('.popover.show')).toBe(null);
5056
});
5157

@@ -54,6 +60,8 @@ describe('Popover', () => {
5460
container: document.body.appendChild(div)
5561
});
5662

63+
jest.runAllTimers();
64+
5765
expect(document.body.querySelector('.popover.show')).not.toBe(null);
5866
});
5967

@@ -65,6 +73,8 @@ describe('Popover', () => {
6573
{container: document.body.appendChild(div)}
6674
);
6775

76+
jest.runAllTimers();
77+
6878
expect(document.body.querySelector('.popover')).toHaveTextContent(
6979
'Popover content'
7080
);
@@ -75,6 +85,8 @@ describe('Popover', () => {
7585
container: document.body.appendChild(div)
7686
});
7787

88+
jest.runAllTimers();
89+
7890
expect(document.body.querySelector('.popover-arrow')).not.toBe(null);
7991
});
8092

@@ -83,6 +95,8 @@ describe('Popover', () => {
8395
container: document.body.appendChild(div)
8496
});
8597

98+
jest.runAllTimers();
99+
86100
expect(document.body.querySelector('.popover-arrow')).toBe(null);
87101
});
88102

@@ -96,6 +110,8 @@ describe('Popover', () => {
96110
}
97111
);
98112

113+
jest.runAllTimers();
114+
99115
expect(document.body.querySelector('.popover-body')).not.toBe(null);
100116
});
101117

@@ -109,6 +125,8 @@ describe('Popover', () => {
109125
}
110126
);
111127

128+
jest.runAllTimers();
129+
112130
expect(document.body.querySelector('.popover-body')).toBe(null);
113131
});
114132
});

src/components/tabs/Tabs.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ Tabs.propTypes = {
241241

242242
/**
243243
* Properties whose user interactions will persist after refreshing the
244-
* component or the page. Since only `value` is allowed this prop can
244+
* component or the page. Since only `active_tab` is allowed this prop can
245245
* normally be ignored.
246246
*/
247247
persisted_props: PropTypes.arrayOf(PropTypes.oneOf(['active_tab'])),

src/components/toast/Toast.js

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,16 @@ const Toast = props => {
6464
data-dash-is-loading={
6565
(loading_state && loading_state.is_loading) || undefined
6666
}
67-
{...omit(['n_dismiss_timestamp'], otherProps)}
67+
{...omit(
68+
[
69+
'n_dismiss_timestamp',
70+
'persistence',
71+
'persisted_props',
72+
'persistence_type',
73+
'setProps'
74+
],
75+
otherProps
76+
)}
6877
>
6978
<RBToast.Header
7079
style={header_style}
@@ -102,7 +111,9 @@ Toast.defaultProps = {
102111
is_open: true,
103112
n_dismiss: 0,
104113
n_dismiss_timestamp: -1,
105-
dismissable: false
114+
dismissable: false,
115+
persisted_props: ['is_open'],
116+
persistence_type: 'local'
106117
};
107118

108119
Toast.propTypes = {
@@ -252,7 +263,36 @@ Toast.propTypes = {
252263
* Holds the name of the component that is loading
253264
*/
254265
component_name: PropTypes.string
255-
})
266+
}),
267+
268+
/**
269+
* Used to allow user interactions in this component to be persisted when
270+
* the component - or the page - is refreshed. If `persisted` is truthy and
271+
* hasn't changed from its previous value, a `value` that the user has
272+
* changed while using the app will keep that change, as long as
273+
* the new `value` also matches what was given originally.
274+
* Used in conjunction with `persistence_type`.
275+
*/
276+
persistence: PropTypes.oneOfType([
277+
PropTypes.bool,
278+
PropTypes.string,
279+
PropTypes.number
280+
]),
281+
282+
/**
283+
* Properties whose user interactions will persist after refreshing the
284+
* component or the page. Since only `value` is allowed this prop can
285+
* normally be ignored.
286+
*/
287+
persisted_props: PropTypes.arrayOf(PropTypes.oneOf(['is_open'])),
288+
289+
/**
290+
* Where persisted user changes will be stored:
291+
* memory: only kept in memory, reset on page refresh.
292+
* local: window.localStorage, data is kept after the browser quit.
293+
* session: window.sessionStorage, data is cleared once the browser quit.
294+
*/
295+
persistence_type: PropTypes.oneOf(['local', 'session', 'memory'])
256296
};
257297

258298
export default Toast;

src/private/Overlay.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ const Overlay = ({
164164

165165
// Update the isOpen state, when defaultShow changes
166166
useEffect(() => {
167-
setIsOpen(defaultShow);
167+
setTimeout(() => setIsOpen(defaultShow), 50);
168168
}, [defaultShow]);
169169

170170
// If legacy is a trigger, then need to set root close to true to allow

0 commit comments

Comments
 (0)