diff --git a/@plotly/dash-test-components/src/components/ComponentAsProp.js b/@plotly/dash-test-components/src/components/ComponentAsProp.js index 867202b16c..52cf52d848 100644 --- a/@plotly/dash-test-components/src/components/ComponentAsProp.js +++ b/@plotly/dash-test-components/src/components/ComponentAsProp.js @@ -3,13 +3,14 @@ import PropTypes from 'prop-types'; const ComponentAsProp = (props) => { - const { element, id, shapeEl, list_of_shapes } = props; + const { element, id, shapeEl, list_of_shapes, multi_components } = props; return (
{shapeEl && shapeEl.header} {element} {shapeEl && shapeEl.footer} {list_of_shapes && } + {multi_components &&
{multi_components.map(m =>
{m.first} - {m.second}
)}
}
) } @@ -28,6 +29,14 @@ ComponentAsProp.propTypes = { label: PropTypes.node, value: PropTypes.number, }) + ), + + multi_components: PropTypes.arrayOf( + PropTypes.exact({ + id: PropTypes.string, + first: PropTypes.node, + second: PropTypes.node, + }) ) } diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e82dcd10b..41c01cd93a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). +## [UNRELEASED] + +### Fixed + +- [#2152](https://github.com/plotly/dash/pull/2152) Fix bug [#2128](https://github.com/plotly/dash/issues/2128) preventing rendering of multiple components inside a dictionary. + ## [2.6.1] - 2022-08-01 ### Fixed diff --git a/dash/dash-renderer/src/TreeContainer.js b/dash/dash-renderer/src/TreeContainer.js index 7d0fe968fd..04864c9db3 100644 --- a/dash/dash-renderer/src/TreeContainer.js +++ b/dash/dash-renderer/src/TreeContainer.js @@ -17,7 +17,6 @@ import { mergeRight, pick, pickBy, - pipe, propOr, path as rpath, pathOr, @@ -246,76 +245,73 @@ class BaseTreeContainer extends Component { ], _dashprivate_config ); - const props = pipe( - dissoc('children'), - ...childrenProps - .map(childrenProp => { - if (childrenProp.includes('.')) { - let path = childrenProp.split('.'); - let node; - let nodeValue; - if (childrenProp.includes('[]')) { - let frontPath = [], - backPath = [], - found = false; - path.forEach(p => { - if (!found) { - if (p.includes('[]')) { - found = true; - frontPath.push(p.replace('[]', '')); - } else { - frontPath.push(p); - } - } else { - backPath.push(p); - } - }); - - node = rpath(frontPath, _dashprivate_layout.props); - if (node === undefined) { - return; + let props = dissoc('children', _dashprivate_layout.props); + + for (let i = 0; i < childrenProps.length; i++) { + const childrenProp = childrenProps[i]; + if (childrenProp.includes('.')) { + let path = childrenProp.split('.'); + let node; + let nodeValue; + if (childrenProp.includes('[]')) { + let frontPath = [], + backPath = [], + found = false; + path.forEach(p => { + if (!found) { + if (p.includes('[]')) { + found = true; + frontPath.push(p.replace('[]', '')); + } else { + frontPath.push(p); } - if (!node.length) { - return assocPath(frontPath, node); - } - const firstNode = rpath(backPath, node[0]); - if (!firstNode) { - return assocPath(frontPath, node); - } - nodeValue = node.map((element, i) => { - const elementPath = concat( - frontPath, - concat([i], backPath) - ); - return assocPath( - backPath, - this.wrapChildrenProp( - rpath(backPath, element), - elementPath - ), - element - ); - }); - path = frontPath; } else { - node = rpath(path, _dashprivate_layout.props); - if (node === undefined) { - return; - } - nodeValue = this.wrapChildrenProp(node, path); + backPath.push(p); } - return assocPath(path, nodeValue); + }); + + node = rpath(frontPath, props); + if (node === undefined || !node.length) { + continue; + } + const firstNode = rpath(backPath, node[0]); + if (!firstNode) { + continue; } - const node = _dashprivate_layout.props[childrenProp]; - if (node !== undefined) { - return assoc( - childrenProp, - this.wrapChildrenProp(node, [childrenProp]) + nodeValue = node.map((element, i) => { + const elementPath = concat( + frontPath, + concat([i], backPath) + ); + return assocPath( + backPath, + this.wrapChildrenProp( + rpath(backPath, element), + elementPath + ), + element ); + }); + path = frontPath; + } else { + node = rpath(path, props); + if (node === undefined) { + continue; } - }) - .filter(e => e !== undefined) - )(_dashprivate_layout.props); + nodeValue = this.wrapChildrenProp(node, path); + } + props = assocPath(path, nodeValue, props); + continue; + } + const node = props[childrenProp]; + if (node !== undefined) { + props = assoc( + childrenProp, + this.wrapChildrenProp(node, [childrenProp]), + props + ); + } + } if (type(props.id) === 'Object') { // Turn object ids (for wildcards) into unique strings. diff --git a/tests/integration/renderer/test_component_as_prop.py b/tests/integration/renderer/test_component_as_prop.py index 25a8c655a6..08b279fa94 100644 --- a/tests/integration/renderer/test_component_as_prop.py +++ b/tests/integration/renderer/test_component_as_prop.py @@ -1,7 +1,7 @@ from dash import Dash, Input, Output, callback_context from dash_test_components import ComponentAsProp -from dash.html import Button, Div +from dash.html import Button, Div, Span def test_rdcap001_component_as_prop(dash_duo): @@ -90,6 +90,21 @@ def test_rdcap001_component_as_prop(dash_duo): ] }, ), + ComponentAsProp( + id="multi-component", + multi_components=[ + { + "id": "multi", + "first": Span("first"), + "second": Span("second"), + }, + { + "id": "multi2", + "first": Span("foo"), + "second": Span("bar"), + } + ], + ), ] ) @@ -187,4 +202,7 @@ def updated_from_list(*_): dash_duo.wait_for_text_to_equal("#first-in-shape", "one") dash_duo.wait_for_text_to_equal("#second-in-shape", "two") + dash_duo.wait_for_text_to_equal("#multi", "first - second") + dash_duo.wait_for_text_to_equal("#multi2", "foo - bar") + assert dash_duo.get_logs() == []