diff --git a/CHANGELOG.md b/CHANGELOG.md index ab68bd85b6..51436b40c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). +## Unreleased +### 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 ### 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. diff --git a/dash-renderer/src/components/core/DocumentTitle.react.js b/dash-renderer/src/components/core/DocumentTitle.react.js index e192f32ce1..3bd03aba57 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, }; } UNSAFE_componentWillReceiveProps(props) { - if (props.isLoading) { - document.title = 'Updating...'; + if (this.state.update_title && props.isLoading) { + document.title = this.state.update_title; } else { document.title = this.state.initialTitle; } @@ -29,8 +31,10 @@ class DocumentTitle extends Component { DocumentTitle.propTypes = { isLoading: PropTypes.bool.isRequired, + config: PropTypes.shape({update_title: PropTypes.string}), }; export default connect(state => ({ isLoading: state.isLoading, + config: state.config, }))(DocumentTitle); diff --git a/dash/dash.py b/dash/dash.py index 2fc3a68a1c..3bcf626cd5 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="Updating...", **obsolete ): _validate.check_obsolete(obsolete) @@ -299,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( [ @@ -506,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"] = { diff --git a/tests/integration/renderer/test_loading_states.py b/tests/integration/renderer/test_loading_states.py index 0e490c32cd..b946d72e33 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 @@ -167,3 +169,46 @@ def find_text(spec): find_spinners() find_text({1: 1, 2: 1, 3: 1, 4: 1}) + + +@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.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) + # check for update-title during startup + until(lambda: dash_duo.driver.title == expected_update_title, timeout=1) + + # check for original title after loading + until(lambda: dash_duo.driver.title == "Dash", timeout=1) + + 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)