From 6525ca292385e43c0ed84fa8b1266b00923d5890 Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Sat, 13 Jun 2020 19:47:48 +0200 Subject: [PATCH 01/15] Add update_title parameter --- dash/dash.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dash/dash.py b/dash/dash.py index 577ad51607..67d50206d2 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -252,6 +252,7 @@ def __init__( prevent_initial_callbacks=False, show_undo_redo=False, plugins=None, + update_title=True, **obsolete ): _validate.check_obsolete(obsolete) From 3061cba2494b20e1f3f62fd62663fda07c8e46b5 Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Sun, 28 Jun 2020 15:12:34 +0200 Subject: [PATCH 02/15] Save update_title to config --- dash/dash.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dash/dash.py b/dash/dash.py index 67d50206d2..20c9e2b61a 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -252,7 +252,7 @@ def __init__( prevent_initial_callbacks=False, show_undo_redo=False, plugins=None, - update_title=True, + update_title="Updating...", **obsolete ): _validate.check_obsolete(obsolete) @@ -300,6 +300,7 @@ def __init__( ), prevent_initial_callbacks=prevent_initial_callbacks, show_undo_redo=show_undo_redo, + update_title=update_title, ) self.config.set_read_only( [ From eb427b09dfe1ec29c29ca3bd34c17a754840fc10 Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Sun, 28 Jun 2020 15:16:38 +0200 Subject: [PATCH 03/15] Pass update_title to frontend --- dash/dash.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dash/dash.py b/dash/dash.py index 20c9e2b61a..26bdc37720 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -508,6 +508,7 @@ def _config(self): "props_check": self._dev_tools.props_check, "show_undo_redo": self.config.show_undo_redo, "suppress_callback_exceptions": self.config.suppress_callback_exceptions, + "update_title": self.config.update_title, } if self._dev_tools.hot_reload: config["hot_reload"] = { From 28630d9c22c6dfa7ac5e865a4ee725e9abfe95ee Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Sun, 28 Jun 2020 15:51:21 +0200 Subject: [PATCH 04/15] Use update_title in DocumentTitle component --- dash-renderer/src/components/core/DocumentTitle.react.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dash-renderer/src/components/core/DocumentTitle.react.js b/dash-renderer/src/components/core/DocumentTitle.react.js index 46eba06bfc..d9dce63c9b 100644 --- a/dash-renderer/src/components/core/DocumentTitle.react.js +++ b/dash-renderer/src/components/core/DocumentTitle.react.js @@ -5,14 +5,16 @@ import PropTypes from 'prop-types'; class DocumentTitle extends Component { constructor(props) { super(props); + const {update_title} = props.config; this.state = { initialTitle: document.title, + update_title: update_title, }; } UNSAFE_componentWillReceiveProps(props) { if (props.pendingCallbacks.length) { - document.title = 'Updating...'; + document.title = this.state.update_title; } else { document.title = this.state.initialTitle; } @@ -29,8 +31,10 @@ class DocumentTitle extends Component { DocumentTitle.propTypes = { pendingCallbacks: PropTypes.array.isRequired, + update_title: PropTypes.string, }; export default connect(state => ({ + config: state.config, pendingCallbacks: state.pendingCallbacks, }))(DocumentTitle); From 2f7cdc4afb8987f9983156adadfcb0b73ea62b13 Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Sun, 28 Jun 2020 16:16:40 +0200 Subject: [PATCH 05/15] Do not change title if update_title is null --- dash-renderer/src/components/core/DocumentTitle.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash-renderer/src/components/core/DocumentTitle.react.js b/dash-renderer/src/components/core/DocumentTitle.react.js index d9dce63c9b..66f62a4a59 100644 --- a/dash-renderer/src/components/core/DocumentTitle.react.js +++ b/dash-renderer/src/components/core/DocumentTitle.react.js @@ -13,7 +13,7 @@ class DocumentTitle extends Component { } UNSAFE_componentWillReceiveProps(props) { - if (props.pendingCallbacks.length) { + if (this.state.update_title && props.pendingCallbacks.length) { document.title = this.state.update_title; } else { document.title = this.state.initialTitle; From f5f97cce7d0567ed52bde9acd19922d6f0783d76 Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Sun, 28 Jun 2020 19:51:53 +0200 Subject: [PATCH 06/15] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02d473d8d9..7aa472aed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [1.13.4] - 2020-06-25 ### Fixed +- [#1315](https://github.com/plotly/dash/pull/1315) Add `update_title` parameter to set or disable the "Updating...." document title during updates. Closes [#856](https://github.com/plotly/dash/issues/856) and [#732](https://github.com/plotly/dash/issues/732) - [#1310](https://github.com/plotly/dash/pull/1310) Fix a regression since 1.13.0 preventing more than one loading state from being shown at a time. ## [1.13.3] - 2020-06-19 From 1db09c90df994bfed2192f27ed3b880c96bd8e07 Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Mon, 29 Jun 2020 16:33:14 +0200 Subject: [PATCH 07/15] Put changelog entry in Unreleased section --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aa472aed5..e7853c3e70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,12 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [1.13.4] - 2020-06-25 +## Unreleased ### Fixed - [#1315](https://github.com/plotly/dash/pull/1315) Add `update_title` parameter to set or disable the "Updating...." document title during updates. Closes [#856](https://github.com/plotly/dash/issues/856) and [#732](https://github.com/plotly/dash/issues/732) + +## [1.13.4] - 2020-06-25 +### Fixed - [#1310](https://github.com/plotly/dash/pull/1310) Fix a regression since 1.13.0 preventing more than one loading state from being shown at a time. ## [1.13.3] - 2020-06-19 From 804617873cb1c80713f592d3c6ddae3a8321ab84 Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Mon, 29 Jun 2020 16:36:08 +0200 Subject: [PATCH 08/15] Simplify update_title state assignment Co-authored-by: alexcjohnson --- dash-renderer/src/components/core/DocumentTitle.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash-renderer/src/components/core/DocumentTitle.react.js b/dash-renderer/src/components/core/DocumentTitle.react.js index 0b9f81ac64..525b36db67 100644 --- a/dash-renderer/src/components/core/DocumentTitle.react.js +++ b/dash-renderer/src/components/core/DocumentTitle.react.js @@ -8,7 +8,7 @@ class DocumentTitle extends Component { const {update_title} = props.config; this.state = { initialTitle: document.title, - update_title: update_title, + update_title, }; } From 745ba77ac022b9e0eb8c65ef54332712b009f4b0 Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Mon, 29 Jun 2020 17:03:02 +0200 Subject: [PATCH 09/15] Fix config PropType --- dash-renderer/src/components/core/DocumentTitle.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash-renderer/src/components/core/DocumentTitle.react.js b/dash-renderer/src/components/core/DocumentTitle.react.js index 525b36db67..911f369501 100644 --- a/dash-renderer/src/components/core/DocumentTitle.react.js +++ b/dash-renderer/src/components/core/DocumentTitle.react.js @@ -31,7 +31,7 @@ class DocumentTitle extends Component { DocumentTitle.propTypes = { isLoading: PropTypes.bool.isRequired, - update_title: PropTypes.string, + config: PropTypes.object, }; export default connect(state => ({ From 57f9b37251721672c2a39da03e4cb1cb2648a53b Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Mon, 29 Jun 2020 20:19:01 +0200 Subject: [PATCH 10/15] Update CHANGELOG.md Co-authored-by: alexcjohnson --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7853c3e70..59f833c726 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased -### Fixed +### Added - [#1315](https://github.com/plotly/dash/pull/1315) Add `update_title` parameter to set or disable the "Updating...." document title during updates. Closes [#856](https://github.com/plotly/dash/issues/856) and [#732](https://github.com/plotly/dash/issues/732) ## [1.13.4] - 2020-06-25 From 740b91325f2c9c3a9bc91182d64af81b0a385fdb Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Mon, 29 Jun 2020 21:14:42 +0200 Subject: [PATCH 11/15] Make config PropType more explicit Co-authored-by: alexcjohnson --- dash-renderer/src/components/core/DocumentTitle.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash-renderer/src/components/core/DocumentTitle.react.js b/dash-renderer/src/components/core/DocumentTitle.react.js index 911f369501..3bd03aba57 100644 --- a/dash-renderer/src/components/core/DocumentTitle.react.js +++ b/dash-renderer/src/components/core/DocumentTitle.react.js @@ -31,7 +31,7 @@ class DocumentTitle extends Component { DocumentTitle.propTypes = { isLoading: PropTypes.bool.isRequired, - config: PropTypes.object, + config: PropTypes.shape({update_title: PropTypes.string}), }; export default connect(state => ({ From 71ee5ec052003a32891344a921faa93d382e36b4 Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Tue, 30 Jun 2020 08:03:59 +0200 Subject: [PATCH 12/15] Add tests for update_title in test_loading_states.py --- .../renderer/test_loading_states.py | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/tests/integration/renderer/test_loading_states.py b/tests/integration/renderer/test_loading_states.py index 0e490c32cd..f7019ccdfd 100644 --- a/tests/integration/renderer/test_loading_states.py +++ b/tests/integration/renderer/test_loading_states.py @@ -167,3 +167,111 @@ def find_text(spec): find_spinners() find_text({1: 1, 2: 1, 3: 1, 4: 1}) + + +def test_update_title_default(dash_duo): + lock = Lock() + + app = dash.Dash(__name__) + + app.layout = html.Div( + children=[ + html.H3("Press button see document title updating"), + html.Div(id="output"), + html.Button("Update", id="button", n_clicks=0), + ] + ) + + @app.callback( + Output("output", "children"), + [Input("button", "n_clicks")] + ) + def update(n): + with lock: + return n + + with lock: + dash_duo.start_server(app) + dash_duo.find_element("#button").click() + assert dash_duo.driver.title == "Updating..." + + +def test_update_title_None(dash_duo): + lock = Lock() + + app = dash.Dash(__name__, update_title=None) + + app.layout = html.Div( + children=[ + html.H3("Press button see document title updating"), + html.Div(id="output"), + html.Button("Update", id="button", n_clicks=0), + ] + ) + + @app.callback( + Output("output", "children"), + [Input("button", "n_clicks")] + ) + def update(n): + with lock: + return n + + with lock: + dash_duo.start_server(app) + dash_duo.find_element("#button").click() + assert dash_duo.driver.title == "Dash" + + +def test_update_title_empty(dash_duo): + lock = Lock() + + app = dash.Dash(__name__, update_title="") + + app.layout = html.Div( + children=[ + html.H3("Press button see document title updating"), + html.Div(id="output"), + html.Button("Update", id="button", n_clicks=0), + ] + ) + + @app.callback( + Output("output", "children"), + [Input("button", "n_clicks")] + ) + def update(n): + with lock: + return n + + with lock: + dash_duo.start_server(app) + dash_duo.find_element("#button").click() + assert dash_duo.driver.title == "Dash" + + +def test_update_title_custom(dash_duo): + lock = Lock() + + app = dash.Dash(__name__, update_title="Hello World") + + app.layout = html.Div( + children=[ + html.H3("Press button see document title updating"), + html.Div(id="output"), + html.Button("Update", id="button", n_clicks=0), + ] + ) + + @app.callback( + Output("output", "children"), + [Input("button", "n_clicks")] + ) + def update(n): + with lock: + return n + + with lock: + dash_duo.start_server(app) + dash_duo.find_element("#button").click() + assert dash_duo.driver.title == "Hello World" From bb2a6f7922029acf6b6d95d3587b2ec81a96e930 Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Tue, 30 Jun 2020 17:06:54 +0200 Subject: [PATCH 13/15] Fix test function names --- tests/integration/renderer/test_loading_states.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/renderer/test_loading_states.py b/tests/integration/renderer/test_loading_states.py index f7019ccdfd..adf7146de0 100644 --- a/tests/integration/renderer/test_loading_states.py +++ b/tests/integration/renderer/test_loading_states.py @@ -169,7 +169,7 @@ def find_text(spec): find_text({1: 1, 2: 1, 3: 1, 4: 1}) -def test_update_title_default(dash_duo): +def test_rdls003_update_title_default(dash_duo): lock = Lock() app = dash.Dash(__name__) @@ -196,7 +196,7 @@ def update(n): assert dash_duo.driver.title == "Updating..." -def test_update_title_None(dash_duo): +def test_rdls004_update_title_None(dash_duo): lock = Lock() app = dash.Dash(__name__, update_title=None) @@ -223,7 +223,7 @@ def update(n): assert dash_duo.driver.title == "Dash" -def test_update_title_empty(dash_duo): +def test_rdls005_update_title_empty(dash_duo): lock = Lock() app = dash.Dash(__name__, update_title="") @@ -250,7 +250,7 @@ def update(n): assert dash_duo.driver.title == "Dash" -def test_update_title_custom(dash_duo): +def test_rdls006_update_title_custom(dash_duo): lock = Lock() app = dash.Dash(__name__, update_title="Hello World") From 571d6ceaa084a808a49b764fdd97d2787b827d6d Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Wed, 1 Jul 2020 13:04:08 +0200 Subject: [PATCH 14/15] Elaborate test sequence --- .../renderer/test_loading_states.py | 103 ++++-------------- 1 file changed, 20 insertions(+), 83 deletions(-) diff --git a/tests/integration/renderer/test_loading_states.py b/tests/integration/renderer/test_loading_states.py index adf7146de0..f53cb65d85 100644 --- a/tests/integration/renderer/test_loading_states.py +++ b/tests/integration/renderer/test_loading_states.py @@ -1,7 +1,9 @@ from multiprocessing import Lock +import pytest import dash from dash.dependencies import Input, Output +from dash.testing.wait import until import dash_core_components as dcc import dash_html_components as html @@ -169,65 +171,19 @@ def find_text(spec): find_text({1: 1, 2: 1, 3: 1, 4: 1}) -def test_rdls003_update_title_default(dash_duo): +@pytest.mark.parametrize( + "kwargs, expected_update_title", + [ + ({}, "Updating..."), + ({"update_title": None}, "Dash"), + ({"update_title": ""}, "Dash"), + ({"update_title": "Hello World"}, "Hello World"), + ] +) +def test_rdls003_update_title(dash_duo, kwargs, expected_update_title): + app = dash.Dash("Dash", **kwargs) lock = Lock() - app = dash.Dash(__name__) - - app.layout = html.Div( - children=[ - html.H3("Press button see document title updating"), - html.Div(id="output"), - html.Button("Update", id="button", n_clicks=0), - ] - ) - - @app.callback( - Output("output", "children"), - [Input("button", "n_clicks")] - ) - def update(n): - with lock: - return n - - with lock: - dash_duo.start_server(app) - dash_duo.find_element("#button").click() - assert dash_duo.driver.title == "Updating..." - - -def test_rdls004_update_title_None(dash_duo): - lock = Lock() - - app = dash.Dash(__name__, update_title=None) - - app.layout = html.Div( - children=[ - html.H3("Press button see document title updating"), - html.Div(id="output"), - html.Button("Update", id="button", n_clicks=0), - ] - ) - - @app.callback( - Output("output", "children"), - [Input("button", "n_clicks")] - ) - def update(n): - with lock: - return n - - with lock: - dash_duo.start_server(app) - dash_duo.find_element("#button").click() - assert dash_duo.driver.title == "Dash" - - -def test_rdls005_update_title_empty(dash_duo): - lock = Lock() - - app = dash.Dash(__name__, update_title="") - app.layout = html.Div( children=[ html.H3("Press button see document title updating"), @@ -242,36 +198,17 @@ def test_rdls005_update_title_empty(dash_duo): ) def update(n): with lock: - return n + # check for update-title while processing callback + until(lambda: dash_duo.driver.title == expected_update_title, timeout=1) + return n with lock: dash_duo.start_server(app) - dash_duo.find_element("#button").click() - assert dash_duo.driver.title == "Dash" - - -def test_rdls006_update_title_custom(dash_duo): - lock = Lock() - - app = dash.Dash(__name__, update_title="Hello World") + # check for update-title on load + until(lambda: dash_duo.driver.title == expected_update_title, timeout=1) - app.layout = html.Div( - children=[ - html.H3("Press button see document title updating"), - html.Div(id="output"), - html.Button("Update", id="button", n_clicks=0), - ] - ) - - @app.callback( - Output("output", "children"), - [Input("button", "n_clicks")] - ) - def update(n): - with lock: - return n + # check for original title after loading + until(lambda: dash_duo.driver.title == "Dash", timeout=1) with lock: - dash_duo.start_server(app) dash_duo.find_element("#button").click() - assert dash_duo.driver.title == "Hello World" From 58b1472ea09d272ce332908ff3af38732d54443e Mon Sep 17 00:00:00 2001 From: Stefan Lehmann Date: Thu, 2 Jul 2020 07:59:11 +0200 Subject: [PATCH 15/15] Move test out of callback --- tests/integration/renderer/test_loading_states.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/renderer/test_loading_states.py b/tests/integration/renderer/test_loading_states.py index f53cb65d85..b946d72e33 100644 --- a/tests/integration/renderer/test_loading_states.py +++ b/tests/integration/renderer/test_loading_states.py @@ -198,13 +198,11 @@ def test_rdls003_update_title(dash_duo, kwargs, expected_update_title): ) def update(n): with lock: - # check for update-title while processing callback - until(lambda: dash_duo.driver.title == expected_update_title, timeout=1) - return n + return n with lock: dash_duo.start_server(app) - # check for update-title on load + # check for update-title during startup until(lambda: dash_duo.driver.title == expected_update_title, timeout=1) # check for original title after loading @@ -212,3 +210,5 @@ def update(n): with lock: dash_duo.find_element("#button").click() + # check for update-title while processing callback + until(lambda: dash_duo.driver.title == expected_update_title, timeout=1)