Skip to content

Commit 7189f32

Browse files
authored
Merge pull request #3264 from BSd3v/better-component-path-setprops
Updating `componentPath` from the `DashWrapper` `setProps`
2 parents 8297e54 + 3c85458 commit 7189f32

File tree

3 files changed

+101
-2
lines changed

3 files changed

+101
-2
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
All notable changes to `dash` will be documented in this file.
33
This project adheres to [Semantic Versioning](https://semver.org/).
44

5+
## [unreleased]
6+
7+
## Fixed
8+
- [#3264](https://github.com/plotly/dash/pull/3264) Fixed an issue where moving components inside of children would not update the `setProps` path, leading to hashes being incorrect
9+
510
## [3.0.2] - 2025-04-01
611

712
## Changed

dash/dash-renderer/src/wrapper/DashWrapper.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ function DashWrapper({
6565
const dispatch = useDispatch();
6666
const memoizedKeys: MutableRefObject<MemoizedKeysType> = useRef({});
6767
const newRender = useRef(false);
68+
const renderedPath = useRef<DashLayoutPath>(componentPath);
6869
let renderComponent: any = null;
6970
let renderComponentProps: any = null;
7071
let renderH: any = null;
@@ -90,6 +91,7 @@ function DashWrapper({
9091
} else {
9192
newRender.current = false;
9293
}
94+
renderedPath.current = componentPath;
9395
}, [_newRender]);
9496

9597
const setProps = (newProps: UpdatePropsPayload) => {
@@ -101,7 +103,10 @@ function DashWrapper({
101103
dispatch((dispatch, getState) => {
102104
const currentState = getState();
103105
const {graphs} = currentState;
104-
const oldLayout = getComponentLayout(componentPath, currentState);
106+
const oldLayout = getComponentLayout(
107+
renderedPath.current,
108+
currentState
109+
);
105110
if (!oldLayout) return;
106111
const {props: oldProps} = oldLayout;
107112
if (!oldProps) return;
@@ -144,7 +149,7 @@ function DashWrapper({
144149
dispatch(
145150
updateProps({
146151
props: changedProps,
147-
itempath: componentPath,
152+
itempath: renderedPath.current,
148153
renderType: 'internal'
149154
})
150155
);
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from dash import Dash, Input, Output, html, dcc, State, ALL
2+
3+
4+
class Section:
5+
def __init__(self, idx):
6+
self.idx = idx
7+
self.options = ["A", "B", "C"]
8+
9+
@property
10+
def section_id(self):
11+
return {"type": "section-container", "id": self.idx}
12+
13+
@property
14+
def dropdown_id(self):
15+
return {"type": "dropdown", "id": self.idx}
16+
17+
@property
18+
def swap_btn_id(self):
19+
return {"type": "swap-btn", "id": self.idx}
20+
21+
def get_layout(self) -> html.Div:
22+
layout = html.Div(
23+
id=self.section_id,
24+
children=[
25+
html.H1(f"I am section {self.idx}"),
26+
html.Button(
27+
"SWAP",
28+
id=self.swap_btn_id,
29+
n_clicks=0,
30+
className=f"swap_button_{self.idx}",
31+
),
32+
dcc.Dropdown(
33+
self.options,
34+
id=self.dropdown_id,
35+
multi=True,
36+
value=[],
37+
className=f"dropdown_{self.idx}",
38+
),
39+
],
40+
)
41+
return layout
42+
43+
44+
def test_roc001_reorder_children(dash_duo):
45+
app = Dash()
46+
47+
app.layout = html.Div(
48+
id="main-app", children=[*[Section(idx=i).get_layout() for i in range(2)]]
49+
)
50+
51+
@app.callback(
52+
Output("main-app", "children"),
53+
Input({"type": "swap-btn", "id": ALL}, "n_clicks"),
54+
State("main-app", "children"),
55+
prevent_initial_call=True,
56+
)
57+
def swap_button_action(n_clicks, children):
58+
if any(n > 0 for n in n_clicks):
59+
return children[::-1]
60+
61+
dash_duo.start_server(app)
62+
63+
for i in range(2):
64+
dash_duo.wait_for_text_to_equal("h1", f"I am section {i}")
65+
dash_duo.wait_for_text_to_equal(
66+
f".dropdown_{i} .Select-multi-value-wrapper", "Select..."
67+
)
68+
dash_duo.find_element(f".dropdown_{i}").click()
69+
dash_duo.find_element(f".dropdown_{i} .VirtualizedSelectOption").click()
70+
dash_duo.wait_for_text_to_equal(
71+
f".dropdown_{i} .Select-multi-value-wrapper", "×A\n "
72+
)
73+
dash_duo.find_element(f".dropdown_{i}").click()
74+
dash_duo.find_element(f".dropdown_{i} .VirtualizedSelectOption").click()
75+
dash_duo.wait_for_text_to_equal(
76+
f".dropdown_{i} .Select-multi-value-wrapper", "×A\n ×B\n "
77+
)
78+
dash_duo.find_element(f".dropdown_{i}").click()
79+
dash_duo.find_element(f".dropdown_{i} .VirtualizedSelectOption").click()
80+
dash_duo.wait_for_text_to_equal(
81+
f".dropdown_{i} .Select-multi-value-wrapper", "×A\n ×B\n ×C\n "
82+
)
83+
dash_duo.find_element(f".swap_button_{i}").click()
84+
dash_duo.wait_for_text_to_equal(
85+
f".dropdown_{0} .Select-multi-value-wrapper", "×A\n ×B\n ×C\n "
86+
)
87+
dash_duo.wait_for_text_to_equal(
88+
f".dropdown_{1} .Select-multi-value-wrapper", "×A\n ×B\n ×C\n "
89+
)

0 commit comments

Comments
 (0)