diff --git a/@plotly/dash-test-components/src/components/ExternalComponent.js b/@plotly/dash-test-components/src/components/ExternalComponent.js index 64d2efc515..8c592e33de 100644 --- a/@plotly/dash-test-components/src/components/ExternalComponent.js +++ b/@plotly/dash-test-components/src/components/ExternalComponent.js @@ -2,10 +2,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -const ExternalComponent = ({ id, text, input_id, extra_component }) => { +const ExternalComponent = ({ id, text, input_id, extra_component, extra_component_temp }) => { const ctx = window.dash_component_api.useDashContext(); const ExternalWrapper = window.dash_component_api.ExternalWrapper; - return (
{text && { id: input_id } }} - componentPath={[...ctx.componentPath, 'external']} + componentPath={[JSON.stringify(ctx.componentPath), 'text']} + temp={true} />} { extra_component && }
) @@ -39,6 +40,7 @@ ExternalComponent.propTypes = { namespace: PropTypes.string, props: PropTypes.object, }), + extra_component_temp: PropTypes.bool, }; export default ExternalComponent; diff --git a/CHANGELOG.md b/CHANGELOG.md index 499c393eca..90c2d0bd2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). - [#3279](https://github.com/plotly/dash/pull/3279) Fix an issue where persisted values were incorrectly pruned when updated via callback. Now, callback returned values are correctly stored in the persistence storage. Fix [#2678](https://github.com/plotly/dash/issues/2678) - [#3298](https://github.com/plotly/dash/pull/3298) Fix dev_only resources filtering. - [#3315](https://github.com/plotly/dash/pull/3315) Fix pages module is package check. +- [#3319](https://github.com/plotly/dash/pull/3319) Fix issue where `ExternalWrapper` would remove props from the parent component, now there is a `temp` that is passed to check if it should be removed on unmount. ## Added - [#3294](https://github.com/plotly/dash/pull/3294) Added the ability to pass `allow_optional` to Input and State to allow callbacks to work even if these components are not in the dash layout. diff --git a/dash/dash-renderer/src/wrapper/ExternalWrapper.tsx b/dash/dash-renderer/src/wrapper/ExternalWrapper.tsx index a6b1dad13f..78807b2145 100644 --- a/dash/dash-renderer/src/wrapper/ExternalWrapper.tsx +++ b/dash/dash-renderer/src/wrapper/ExternalWrapper.tsx @@ -13,12 +13,13 @@ import { type Props = { component: DashComponent; componentPath: DashLayoutPath; + temp?: boolean; // If true, the component will be removed on unmount. }; /** * For rendering components that are out of the regular layout tree. */ -function ExternalWrapper({component, componentPath}: Props) { +function ExternalWrapper({component, componentPath, temp = false}: Props) { const dispatch: any = useDispatch(); const [inserted, setInserted] = useState(false); @@ -33,7 +34,9 @@ function ExternalWrapper({component, componentPath}: Props) { ); setInserted(true); return () => { - dispatch(removeComponent({componentPath})); + if (temp) { + dispatch(removeComponent({componentPath})); + } }; }, []); diff --git a/tests/integration/renderer/test_external_component.py b/tests/integration/renderer/test_external_component.py index c0ab76fcc3..ec2fcfef12 100644 --- a/tests/integration/renderer/test_external_component.py +++ b/tests/integration/renderer/test_external_component.py @@ -59,3 +59,69 @@ def click(*_): dash_duo.wait_for_text_to_equal("#out", "clicked") assert dash_duo.get_logs() == [] + + +def test_rext002_render_external_component_temp(dash_duo): + app = Dash() + app.layout = html.Div( + [ + dcc.Tabs( + [ + dcc.Tab( + label="Tab 1", + children=[ + ExternalComponent( + id="ext", + extra_component={ + "type": "Div", + "namespace": "dash_html_components", + "props": { + "id": "extra", + "children": [ + html.Div( + "extra children", + id={"type": "extra", "index": 1}, + ) + ], + }, + }, + extra_component_temp=True, + ), + ], + ), + dcc.Tab( + label="Tab 2", + children=[ + ExternalComponent( + id="without-id", + text="without-id", + ), + ], + ), + ] + ), + ] + ) + + dash_duo.start_server(app) + dash_duo.wait_for_text_to_equal("#extra", "extra children") + + dash_duo.find_element(".tab:nth-child(2)").click() + assert ( + dash_duo.find_element("#without-id > input").get_attribute("value") + == "without-id" + ) + + dash_duo.find_element(".tab").click() + dash_duo.find_element("#ext") + assert ( + len(dash_duo.find_elements("#ext > *")) == 0 + ), "extra component should be removed" + + dash_duo.find_element(".tab:nth-child(2)").click() + assert ( + dash_duo.find_element("#without-id > input").get_attribute("value") + == "without-id" + ) + + assert dash_duo.get_logs() == []