Skip to content

Commit 06a4178

Browse files
authored
Merge pull request #1408 from plotly/1402-cb-graph-layout-fix
persist callback graph layout when callbacks fire
2 parents 02d06ba + fe60ea5 commit 06a4178

File tree

4 files changed

+42
-3
lines changed

4 files changed

+42
-3
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ This project adheres to [Semantic Versioning](https://semver.org/).
66
### Changed
77
- [#1376](https://github.com/plotly/dash/pull/1376) Extends the `getTransform` logic in the renderer to handle `persistenceTransforms` for both nested and non-nested persisted props. This was used to to fix [dcc#700](https://github.com/plotly/dash-core-components/issues/700) in conjunction with [dcc#854](https://github.com/plotly/dash-core-components/pull/854) by using persistenceTransforms to strip the time part of the datetime so that datepickers can persist when defined in callbacks.
88

9+
### Fixed
10+
- [#1408](https://github.com/plotly/dash/pull/1408) Fixes a bug where the callback graph layout would reset whenever a callback fired, losing user-initiated layout changes ([#1402](https://github.com/plotly/dash/issues/1402)) or creating a new force layout ([#1401](https://github.com/plotly/dash/issues/1401))
11+
912
## [1.16.0] - 2020-09-03
1013
### Added
1114
- [#1371](https://github.com/plotly/dash/pull/1371) You can now get [CSP `script-src` hashes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src) of all added inline scripts by calling `app.csp_hashes()` (both Dash internal inline scripts, and those added with `app.clientside_callback`) .

dash-renderer/src/components/error/CallbackGraph/CallbackGraphContainer.react.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,13 @@ function CallbackGraph() {
195195
cy.nodes().each(n => {
196196
positions[n.id()] = n.position();
197197
});
198+
199+
// Hack! We're mutating the redux store directly here, rather than
200+
// dispatching an action, because we don't want this to trigger a
201+
// rerender, we just want the layout to persist when the callback graph
202+
// is rerendered - either because there's new profile information to
203+
// display or because the graph was closed and reopened. The latter is
204+
// the reason we're not using component state to store the layout.
198205
profile.graphLayout = {
199206
name: 'preset',
200207
fit: false,

dash-renderer/src/reducers/profile.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const defaultProfile = {
2121
const defaultState = {
2222
updated: [],
2323
resources: {},
24-
callbacks: {}
24+
callbacks: {},
25+
graphLayout: null
2526
};
2627

2728
const profile = (state = defaultState, action) => {
@@ -36,7 +37,11 @@ const profile = (state = defaultState, action) => {
3637
const newState = {
3738
updated: [id],
3839
resources: state.resources,
39-
callbacks: state.callbacks
40+
callbacks: state.callbacks,
41+
// graphLayout is never passed in via actions, because we don't
42+
// want it to trigger a rerender of the callback graph.
43+
// See CallbackGraphContainer.react
44+
graphLayout: state.graphLayout
4045
};
4146

4247
newState.callbacks[id] =

tests/integration/devtools/test_devtools_ui.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def test_dvui003_callback_graph(dash_duo):
8989
cbProfiles[k].network.time = 33;
9090
cbProfiles[k].total = 77;
9191
});
92-
"""
92+
"""
9393
)
9494

9595
dash_duo.find_element(".dash-debug-menu").click()
@@ -99,3 +99,27 @@ def test_dvui003_callback_graph(dash_duo):
9999
dash_duo.find_element('canvas[data-id="layer2-node"]')
100100

101101
dash_duo.percy_snapshot("devtools - callback graph", convert_canvases=True)
102+
103+
pos = dash_duo.driver.execute_script(
104+
"""
105+
const pos = store.getState().profile.graphLayout.positions['new-item.value'];
106+
pos.y -= 100;
107+
return pos.y;
108+
"""
109+
)
110+
111+
# hide and redraw the callback graph so we get the new position
112+
dash_duo.find_element(".dash-debug-menu__button--callbacks").click()
113+
114+
# fire callbacks so the profile state is regenerated
115+
dash_duo.find_element("#add").click()
116+
dash_duo.find_element(".dash-debug-menu__button--callbacks").click()
117+
dash_duo.wait_for_text_to_equal("#totals", "0 of 1 items completed - 0%")
118+
sleep(2)
119+
# the manually moved node is still in its new position
120+
assert pos == dash_duo.driver.execute_script(
121+
"""
122+
const pos = store.getState().profile.graphLayout.positions['new-item.value'];
123+
return pos.y;
124+
"""
125+
)

0 commit comments

Comments
 (0)