diff --git a/.circleci/config.yml b/.circleci/config.yml index bb7db32..1a805da 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -39,7 +39,7 @@ jobs: name: Install dependencies (dash) command: | git clone git@github.com:plotly/dash.git - git clone git@github.com:plotly/dash-core-components.git + git clone -b renderer-regression git@github.com:plotly/dash-core-components.git git clone git@github.com:plotly/dash-html-components.git git clone git@github.com:plotly/dash-table.git . venv/bin/activate diff --git a/src/TreeContainer.js b/src/TreeContainer.js index 5303aaa..6083a30 100644 --- a/src/TreeContainer.js +++ b/src/TreeContainer.js @@ -5,6 +5,7 @@ import PropTypes from 'prop-types'; import Registry from './registry'; import {connect} from 'react-redux'; import { + any, contains, filter, forEach, @@ -18,7 +19,6 @@ import { propOr, type } from 'ramda'; -import {STATUS} from './constants/constants'; import { notifyObservers, updateProps } from './actions'; const SIMPLE_COMPONENT_TYPES = ['String', 'Number', 'Null', 'Boolean']; @@ -75,38 +75,6 @@ class TreeContainer extends Component { ); } - getLoadingState(id, requestQueue) { - // loading prop coming from TreeContainer - let isLoading = false; - let loadingProp; - let loadingComponent; - - if (requestQueue && requestQueue.filter) { - forEach(r => { - const controllerId = isNil(r.controllerId) ? '' : r.controllerId; - if (r.status === 'loading' && contains(id, controllerId)) { - isLoading = true; - [loadingComponent, loadingProp] = r.controllerId.split('.'); - } - }, requestQueue); - - const thisRequest = requestQueue.filter(r => { - const controllerId = isNil(r.controllerId) ? '' : r.controllerId; - return contains(id, controllerId); - }); - if (thisRequest.status === STATUS.OK) { - isLoading = false; - } - } - - // Set loading state - return { - is_loading: isLoading, - prop_name: loadingProp, - component_name: loadingComponent, - }; - } - getSetProps() { return newProps => { const { @@ -145,7 +113,10 @@ class TreeContainer extends Component { } shouldComponentUpdate(nextProps) { - return nextProps._dashprivate_layout !== this.props._dashprivate_layout; + const { _dashprivate_layout, _dashprivate_loadingState } = nextProps; + + return _dashprivate_layout !== this.props._dashprivate_layout || + _dashprivate_loadingState.is_loading !== this.props._dashprivate_loadingState.is_loading; } getLayoutProps() { @@ -156,16 +127,15 @@ class TreeContainer extends Component { const { _dashprivate_dispatch, _dashprivate_layout, - _dashprivate_requestQueue + _dashprivate_loadingState } = this.props; const layoutProps = this.getLayoutProps(); const children = this.getChildren(layoutProps.children); - const loadingState = this.getLoadingState(layoutProps.id, _dashprivate_requestQueue); const setProps = this.getSetProps(_dashprivate_dispatch); - return this.getComponent(_dashprivate_layout, children, loadingState, setProps); + return this.getComponent(_dashprivate_layout, children, _dashprivate_loadingState, setProps); } } @@ -173,6 +143,7 @@ TreeContainer.propTypes = { _dashprivate_dependencies: PropTypes.any, _dashprivate_dispatch: PropTypes.func, _dashprivate_layout: PropTypes.object, + _dashprivate_loadingState: PropTypes.object, _dashprivate_paths: PropTypes.any, _dashprivate_requestQueue: PropTypes.object, }; @@ -194,12 +165,78 @@ function mergeProps(stateProps, dispatchProps, ownProps) { _dashprivate_dependencies: stateProps.dependencies, _dashprivate_dispatch: dispatchProps.dispatch, _dashprivate_layout: ownProps._dashprivate_layout, - _dashprivate_loading: ownProps._dashprivate_loading, + _dashprivate_loadingState: getLoadingState(ownProps._dashprivate_layout, stateProps.requestQueue), _dashprivate_paths: stateProps.paths, _dashprivate_requestQueue: stateProps.requestQueue, }; } +function getLoadingState(layout, requestQueue) { + const ids = isLoadingComponent(layout) ? + getNestedIds(layout) : + (layout && layout.props.id ? + [layout.props.id] : + []); + + let isLoading = false; + let loadingProp; + let loadingComponent; + + if (requestQueue) { + forEach(r => { + const controllerId = isNil(r.controllerId) ? '' : r.controllerId; + if (r.status === 'loading' && any(id => contains(id, controllerId), ids)) { + isLoading = true; + [loadingComponent, loadingProp] = r.controllerId.split('.'); + } + }, requestQueue); + } + + // Set loading state + return { + is_loading: isLoading, + prop_name: loadingProp, + component_name: loadingComponent, + }; +} + +function getNestedIds(layout) { + const ids = []; + const queue = [layout]; + + while (queue.length) { + const elementLayout = queue.shift(); + + const props = elementLayout && + elementLayout.props; + + if (!props) { + continue; + } + + const { children, id } = props; + + if (id) { + ids.push(id); + } + + if (children) { + const filteredChildren = filter( + child => !isSimpleComponent(child) && !isLoadingComponent(child), + Array.isArray(children) ? children : [children] + ); + + queue.push(...filteredChildren); + } + } + + return ids; +} + +function isLoadingComponent(layout) { + return Registry.resolve(layout.type, layout.namespace)._dashprivate_isLoadingComponent; +} + export const AugmentedTreeContainer = connect(mapStateToProps, mapDispatchToProps, mergeProps)(TreeContainer); export default AugmentedTreeContainer;