Skip to content
This repository was archived by the owner on Jun 4, 2024. It is now read-only.

Commit b3362dc

Browse files
Loading states api (#93)
* Pass loading prop down to components instead of rendering div * First pass at providing 'loading' prop to dash components * Add componentName and propName props to loading object, cleanup * Cleanup * Updated core components tarball * Change prop names for loading state to be more Pythonic * Use latest dcc that fixes Input reliant tests * Left-over from rebasing * Update everything to it's latest version expect html-components * Add tarball back in * Change props.status to props.loading_state to be more specific * Pass loading prop down to components instead of rendering div * First pass at providing 'loading' prop to dash components * Add componentName and propName props to loading object, cleanup * Cleanup * Updated core components tarball * Change prop names for loading state to be more Pythonic * Rebuild bundles * Fixed simple.py regression * Replace dcc tarball with rc1 version * Try with new componentWillRecieveProps changes in dcc tarball * Revert everything in dev-requirements except dcc tarball * Revert back to old dev-requirements.txt completely * Refactored requestQueue map into forEach * Add tarball back in dev-requirements * Revert back to old dev-requirements * Fix tests by checking if controllerId is null * Remove newlines from NotifyObservers parameters * Version bump to 0.16.0 * Change const to let * Update version to 0.16.0rc1 * Revert dcc upgrade in deps * Calculate loading_state in TreeContainer so children have access to it too * Add confirm dialog test from DCC * Remove dcc test again * Cleanup * Only rerender TreeContainer if props are new * Release 0.18.0rc3 with more dcc test fixes * Remove loading prop from APIcontroller causing unneccesary re-renders * Bump rc version * Update package.json as well * Reset simple.py * Refactored recursivelyRender call in TreeContainer * Fix formatting
1 parent 7065325 commit b3362dc

8 files changed

+187
-54
lines changed

Diff for: CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

5+
## [UNRELEASED]
6+
### Added
7+
- Loading states API [#267](https://github.com/plotly/dash/issues/267)
58
## [0.19.0] - 2019-02-25
69
## Added
710
- Added which properties fired to update prop request. [#124](https://github.com/plotly/dash-renderer/pull/124)

Diff for: dash_renderer/dash_renderer.dev.js

+90-26
Original file line numberDiff line numberDiff line change
@@ -33092,11 +33092,9 @@ Object.defineProperty(exports, "__esModule", {
3309233092
value: true
3309333093
});
3309433094

33095-
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
33096-
33097-
var _ramda = __webpack_require__(/*! ramda */ "./node_modules/ramda/index.js");
33095+
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
3309833096

33099-
var _ramda2 = _interopRequireDefault(_ramda);
33097+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
3310033098

3310133099
var _react = __webpack_require__(/*! react */ "react");
3310233100

@@ -33114,6 +33112,12 @@ var _NotifyObservers = __webpack_require__(/*! ./components/core/NotifyObservers
3311433112

3311533113
var _NotifyObservers2 = _interopRequireDefault(_NotifyObservers);
3311633114

33115+
var _reactRedux = __webpack_require__(/*! react-redux */ "./node_modules/react-redux/lib/index.js");
33116+
33117+
var _ramda = __webpack_require__(/*! ramda */ "./node_modules/ramda/index.js");
33118+
33119+
var _constants = __webpack_require__(/*! ./constants/constants */ "./src/constants/constants.js");
33120+
3311733121
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
3311833122

3311933123
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
@@ -33141,68 +33145,120 @@ var TreeContainer = function (_Component) {
3314133145
}, {
3314233146
key: 'render',
3314333147
value: function render() {
33144-
return _render(this.props.layout);
33148+
return recursivelyRender(this.props.layout, this.props.requestQueue);
3314533149
}
3314633150
}]);
3314733151

3314833152
return TreeContainer;
3314933153
}(_react.Component);
3315033154

33151-
exports.default = TreeContainer;
33152-
33153-
3315433155
TreeContainer.propTypes = {
33155-
layout: _propTypes2.default.object
33156+
layout: _propTypes2.default.object,
33157+
requestQueue: _propTypes2.default.object
3315633158
};
3315733159

33158-
function _render(component) {
33159-
if (_ramda2.default.contains(_ramda2.default.type(component), ['String', 'Number', 'Null', 'Boolean'])) {
33160+
function recursivelyRender(component, requestQueue) {
33161+
if ((0, _ramda.contains)((0, _ramda.type)(component), ['String', 'Number', 'Null', 'Boolean'])) {
3316033162
return component;
3316133163
}
3316233164

33165+
if ((0, _ramda.isEmpty)(component)) {
33166+
return null;
33167+
}
33168+
3316333169
// Create list of child elements
3316433170
var children = void 0;
3316533171

33166-
var componentProps = _ramda2.default.propOr({}, 'props', component);
33172+
var componentProps = (0, _ramda.propOr)({}, 'props', component);
3316733173

33168-
if (!_ramda2.default.has('props', component) || !_ramda2.default.has('children', component.props) || typeof component.props.children === 'undefined') {
33174+
if (!(0, _ramda.has)('props', component) || !(0, _ramda.has)('children', component.props) || typeof component.props.children === 'undefined') {
3316933175
// No children
3317033176
children = [];
33171-
} else if (_ramda2.default.contains(_ramda2.default.type(component.props.children), ['String', 'Number', 'Null', 'Boolean'])) {
33177+
} else if ((0, _ramda.contains)((0, _ramda.type)(component.props.children), ['String', 'Number', 'Null', 'Boolean'])) {
3317233178
children = [component.props.children];
3317333179
} else {
3317433180
// One or multiple objects
3317533181
// Recursively render the tree
3317633182
// TODO - I think we should pass in `key` here.
33177-
children = (Array.isArray(componentProps.children) ? componentProps.children : [componentProps.children]).map(_render);
33183+
children = (Array.isArray(componentProps.children) ? componentProps.children : [componentProps.children]).map(function (child) {
33184+
return recursivelyRender(child, requestQueue);
33185+
});
3317833186
}
3317933187

3318033188
if (!component.type) {
3318133189
/* eslint-disable no-console */
33182-
console.error(_ramda2.default.type(component), component);
33190+
console.error((0, _ramda.type)(component), component);
3318333191
/* eslint-enable no-console */
3318433192
throw new Error('component.type is undefined');
3318533193
}
3318633194
if (!component.namespace) {
3318733195
/* eslint-disable no-console */
33188-
console.error(_ramda2.default.type(component), component);
33196+
console.error((0, _ramda.type)(component), component);
3318933197
/* eslint-enable no-console */
3319033198
throw new Error('component.namespace is undefined');
3319133199
}
3319233200
var element = _registry2.default.resolve(component.type, component.namespace);
3319333201

33194-
var parent = _react2.default.createElement.apply(_react2.default, [element, _ramda2.default.omit(['children'], component.props)].concat(_toConsumableArray(children)));
33202+
var parent = _react2.default.createElement.apply(_react2.default, [element, (0, _ramda.omit)(['children'], component.props)].concat(_toConsumableArray(children)));
33203+
33204+
// loading prop coming from TreeContainer
33205+
var isLoading = false;
33206+
var loadingProp = void 0;
33207+
var loadingComponent = void 0;
33208+
33209+
var id = componentProps.id;
33210+
33211+
if (requestQueue && requestQueue.filter) {
33212+
(0, _ramda.forEach)(function (r) {
33213+
var controllerId = (0, _ramda.isNil)(r.controllerId) ? '' : r.controllerId;
33214+
if (r.status === 'loading' && (0, _ramda.contains)(id, controllerId)) {
33215+
isLoading = true;
33216+
33217+
var _r$controllerId$split = r.controllerId.split('.');
33218+
33219+
var _r$controllerId$split2 = _slicedToArray(_r$controllerId$split, 2);
33220+
33221+
loadingComponent = _r$controllerId$split2[0];
33222+
loadingProp = _r$controllerId$split2[1];
33223+
}
33224+
}, requestQueue);
33225+
33226+
var thisRequest = requestQueue.filter(function (r) {
33227+
var controllerId = (0, _ramda.isNil)(r.controllerId) ? '' : r.controllerId;
33228+
return (0, _ramda.contains)(id, controllerId);
33229+
});
33230+
if (thisRequest.status === _constants.STATUS.OK) {
33231+
isLoading = false;
33232+
}
33233+
}
33234+
33235+
// Set loading state
33236+
var loading_state = {
33237+
is_loading: isLoading,
33238+
prop_name: loadingProp,
33239+
component_name: loadingComponent
33240+
};
3319533241

3319633242
return _react2.default.createElement(
3319733243
_NotifyObservers2.default,
33198-
{ key: componentProps.id, id: componentProps.id },
33244+
{
33245+
key: componentProps.id,
33246+
id: componentProps.id,
33247+
loading_state: loading_state
33248+
},
3319933249
parent
3320033250
);
3320133251
}
3320233252

33203-
_render.propTypes = {
33204-
children: _propTypes2.default.object
33205-
};
33253+
function mapStateToProps(state, ownProps) {
33254+
return {
33255+
layout: ownProps.layout,
33256+
loading: ownProps.loading,
33257+
requestQueue: state.requestQueue
33258+
};
33259+
}
33260+
33261+
exports.default = (0, _reactRedux.connect)(mapStateToProps)(TreeContainer);
3320633262

3320733263
/***/ }),
3320833264

@@ -34182,8 +34238,6 @@ Object.defineProperty(exports, "__esModule", {
3418234238

3418334239
var _reactRedux = __webpack_require__(/*! react-redux */ "./node_modules/react-redux/lib/index.js");
3418434240

34185-
var _ramda = __webpack_require__(/*! ramda */ "./node_modules/ramda/index.js");
34186-
3418734241
var _actions = __webpack_require__(/*! ../../actions */ "./src/actions/index.js");
3418834242

3418934243
var _react = __webpack_require__(/*! react */ "react");
@@ -34194,6 +34248,8 @@ var _propTypes = __webpack_require__(/*! prop-types */ "./node_modules/prop-type
3419434248

3419534249
var _propTypes2 = _interopRequireDefault(_propTypes);
3419634250

34251+
var _ramda = __webpack_require__(/*! ramda */ "./node_modules/ramda/index.js");
34252+
3419734253
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
3419834254

3419934255
/*
@@ -34220,6 +34276,8 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
3422034276
children: ownProps.children,
3422134277
dependencies: stateProps.dependencies,
3422234278
paths: stateProps.paths,
34279+
loading_state: ownProps.loading_state,
34280+
requestQueue: stateProps.requestQueue,
3422334281

3422434282
setProps: function setProps(newProps) {
3422534283
var payload = {
@@ -34242,7 +34300,8 @@ function NotifyObserversComponent(_ref) {
3424234300
id = _ref.id,
3424334301
paths = _ref.paths,
3424434302
dependencies = _ref.dependencies,
34245-
setProps = _ref.setProps;
34303+
setProps = _ref.setProps,
34304+
loading_state = _ref.loading_state;
3424634305

3424734306
var thisComponentSharesState = dependencies && dependencies.find(function (dependency) {
3424834307
return dependency.inputs.find(function (input) {
@@ -34275,6 +34334,10 @@ function NotifyObserversComponent(_ref) {
3427534334
extraProps.setProps = setProps;
3427634335
}
3427734336

34337+
if (children.props && !children.props.loading_state) {
34338+
extraProps.loading_state = loading_state;
34339+
}
34340+
3427834341
if (!(0, _ramda.isEmpty)(extraProps)) {
3427934342
return _react2.default.cloneElement(children, extraProps);
3428034343
}
@@ -34284,7 +34347,8 @@ function NotifyObserversComponent(_ref) {
3428434347
NotifyObserversComponent.propTypes = {
3428534348
id: _propTypes2.default.string.isRequired,
3428634349
children: _propTypes2.default.node.isRequired,
34287-
path: _propTypes2.default.array.isRequired
34350+
path: _propTypes2.default.array.isRequired,
34351+
loading_state: _propTypes2.default.object
3428834352
};
3428934353

3429034354
exports.default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps, mergeProps)(NotifyObserversComponent);

Diff for: dash_renderer/dash_renderer.dev.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: dash_renderer/dash_renderer.min.js

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: dash_renderer/dash_renderer.min.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: dash_renderer/favicon.ico

14.7 KB
Binary file not shown.

Diff for: src/TreeContainer.js

+79-19
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,60 @@
11
'use strict';
22

3-
import R from 'ramda';
43
import React, {Component} from 'react';
54
import PropTypes from 'prop-types';
65
import Registry from './registry';
76
import NotifyObservers from './components/core/NotifyObservers.react';
7+
import {connect} from 'react-redux';
8+
import {
9+
isNil,
10+
omit,
11+
contains,
12+
isEmpty,
13+
forEach,
14+
propOr,
15+
type,
16+
has,
17+
} from 'ramda';
18+
import {STATUS} from './constants/constants';
819

9-
export default class TreeContainer extends Component {
20+
class TreeContainer extends Component {
1021
shouldComponentUpdate(nextProps) {
1122
return nextProps.layout !== this.props.layout;
1223
}
1324

1425
render() {
15-
return render(this.props.layout);
26+
return recursivelyRender(this.props.layout, this.props.requestQueue);
1627
}
1728
}
1829

1930
TreeContainer.propTypes = {
2031
layout: PropTypes.object,
32+
requestQueue: PropTypes.object,
2133
};
2234

23-
function render(component) {
24-
if (
25-
R.contains(R.type(component), ['String', 'Number', 'Null', 'Boolean'])
26-
) {
35+
function recursivelyRender(component, requestQueue) {
36+
if (contains(type(component), ['String', 'Number', 'Null', 'Boolean'])) {
2737
return component;
2838
}
2939

40+
if (isEmpty(component)) {
41+
return null;
42+
}
43+
3044
// Create list of child elements
3145
let children;
3246

33-
const componentProps = R.propOr({}, 'props', component);
47+
const componentProps = propOr({}, 'props', component);
3448

3549
if (
36-
!R.has('props', component) ||
37-
!R.has('children', component.props) ||
50+
!has('props', component) ||
51+
!has('children', component.props) ||
3852
typeof component.props.children === 'undefined'
3953
) {
4054
// No children
4155
children = [];
4256
} else if (
43-
R.contains(R.type(component.props.children), [
57+
contains(type(component.props.children), [
4458
'String',
4559
'Number',
4660
'Null',
@@ -55,32 +69,78 @@ function render(component) {
5569
children = (Array.isArray(componentProps.children)
5670
? componentProps.children
5771
: [componentProps.children]
58-
).map(render);
72+
).map(child => recursivelyRender(child, requestQueue));
5973
}
6074

6175
if (!component.type) {
6276
/* eslint-disable no-console */
63-
console.error(R.type(component), component);
77+
console.error(type(component), component);
6478
/* eslint-enable no-console */
6579
throw new Error('component.type is undefined');
6680
}
6781
if (!component.namespace) {
6882
/* eslint-disable no-console */
69-
console.error(R.type(component), component);
83+
console.error(type(component), component);
7084
/* eslint-enable no-console */
7185
throw new Error('component.namespace is undefined');
7286
}
7387
const element = Registry.resolve(component.type, component.namespace);
7488

7589
const parent = React.createElement(
7690
element,
77-
R.omit(['children'], component.props),
91+
omit(['children'], component.props),
7892
...children
7993
);
8094

81-
return <NotifyObservers key={componentProps.id} id={componentProps.id}>{parent}</NotifyObservers>;
95+
// loading prop coming from TreeContainer
96+
let isLoading = false;
97+
let loadingProp;
98+
let loadingComponent;
99+
100+
const id = componentProps.id;
101+
102+
if (requestQueue && requestQueue.filter) {
103+
forEach(r => {
104+
const controllerId = isNil(r.controllerId) ? '' : r.controllerId;
105+
if (r.status === 'loading' && contains(id, controllerId)) {
106+
isLoading = true;
107+
[loadingComponent, loadingProp] = r.controllerId.split('.');
108+
}
109+
}, requestQueue);
110+
111+
const thisRequest = requestQueue.filter(r => {
112+
const controllerId = isNil(r.controllerId) ? '' : r.controllerId;
113+
return contains(id, controllerId);
114+
});
115+
if (thisRequest.status === STATUS.OK) {
116+
isLoading = false;
117+
}
118+
}
119+
120+
// Set loading state
121+
const loading_state = {
122+
is_loading: isLoading,
123+
prop_name: loadingProp,
124+
component_name: loadingComponent,
125+
};
126+
127+
return (
128+
<NotifyObservers
129+
key={componentProps.id}
130+
id={componentProps.id}
131+
loading_state={loading_state}
132+
>
133+
{parent}
134+
</NotifyObservers>
135+
);
136+
}
137+
138+
function mapStateToProps(state, ownProps) {
139+
return {
140+
layout: ownProps.layout,
141+
loading: ownProps.loading,
142+
requestQueue: state.requestQueue,
143+
};
82144
}
83145

84-
render.propTypes = {
85-
children: PropTypes.object,
86-
};
146+
export default connect(mapStateToProps)(TreeContainer);

0 commit comments

Comments
 (0)