From 647a44ea0a4f2b6719dc940818464b7ba2ba45c0 Mon Sep 17 00:00:00 2001 From: wbrgss Date: Thu, 6 Aug 2020 17:55:23 -0400 Subject: [PATCH 01/11] Allow for className on parent of completed loading children, add parent_style prop --- src/components/Loading.react.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/Loading.react.js b/src/components/Loading.react.js index 42e560843..5d02b3599 100644 --- a/src/components/Loading.react.js +++ b/src/components/Loading.react.js @@ -5,6 +5,7 @@ import DefaultSpinner from '../fragments/Loading/spinners/DefaultSpinner.jsx'; import CubeSpinner from '../fragments/Loading/spinners/CubeSpinner.jsx'; import CircleSpinner from '../fragments/Loading/spinners/CircleSpinner.jsx'; import DotSpinner from '../fragments/Loading/spinners/DotSpinner.jsx'; +import {mergeRight} from 'ramda'; function getSpinner(spinnerType) { switch (spinnerType) { @@ -44,6 +45,7 @@ export default class Loading extends Component { color, className, style, + parent_style, fullscreen, debug, type: spinnerType, @@ -53,7 +55,13 @@ export default class Loading extends Component { const Spinner = isLoading && getSpinner(spinnerType); return ( -
+
{this.props.children}
{isLoading && ( @@ -113,7 +121,9 @@ Loading.propTypes = { debug: PropTypes.bool, /** - * Additional CSS class for the spinner root DOM node + * Additional CSS styling, applied to one of: + * - the spinner root DOM node, if the component's children are currently loading + * - the parent div of the children, once they are loaded */ className: PropTypes.string, @@ -122,6 +132,11 @@ Loading.propTypes = { */ style: PropTypes.object, + /** + * Additional CSS styling for the outermost dcc.Loading parent div DOM node + */ + parent_style: PropTypes.object, + /** * Primary colour used for the loading spinners */ From 7875c2e252d7b3b297bc12e3d61799e7fdd2491f Mon Sep 17 00:00:00 2001 From: Will Burgess Date: Fri, 7 Aug 2020 18:43:09 -0400 Subject: [PATCH 02/11] (suggestion) avoid unnecessary mergeRight call in ternary eval Co-authored-by: alexcjohnson --- src/components/Loading.react.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Loading.react.js b/src/components/Loading.react.js index 5d02b3599..7bef725c8 100644 --- a/src/components/Loading.react.js +++ b/src/components/Loading.react.js @@ -57,10 +57,10 @@ export default class Loading extends Component { return (
{this.props.children}
From a6f4fb28a964839d5ca58170f50e0801c495548b Mon Sep 17 00:00:00 2001 From: wbrgss Date: Mon, 10 Aug 2020 13:37:29 -0400 Subject: [PATCH 03/11] Add parent_className prop and only apply className to Spinner --- src/components/Loading.react.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/components/Loading.react.js b/src/components/Loading.react.js index 7bef725c8..e04317ede 100644 --- a/src/components/Loading.react.js +++ b/src/components/Loading.react.js @@ -45,6 +45,7 @@ export default class Loading extends Component { color, className, style, + parent_className, parent_style, fullscreen, debug, @@ -56,10 +57,11 @@ export default class Loading extends Component { return (
{this.props.children} @@ -121,12 +123,15 @@ Loading.propTypes = { debug: PropTypes.bool, /** - * Additional CSS styling, applied to one of: - * - the spinner root DOM node, if the component's children are currently loading - * - the parent div of the children, once they are loaded + * CSS class for the spinner root DOM node */ className: PropTypes.string, + /** + * Additional CSS class for the outermost dcc.Loading parent div DOM node + */ + parent_className: PropTypes.string, + /** * Additional CSS styling for the spinner root DOM node */ From ac07ec8ae87383de13cae194cb3e9680cbe9b399 Mon Sep 17 00:00:00 2001 From: wbrgss Date: Thu, 13 Aug 2020 18:10:22 -0400 Subject: [PATCH 04/11] Test new loading style attributes --- .../loading/test_loading_component.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/integration/loading/test_loading_component.py b/tests/integration/loading/test_loading_component.py index 905100c11..8afd85efc 100644 --- a/tests/integration/loading/test_loading_component.py +++ b/tests/integration/loading/test_loading_component.py @@ -278,3 +278,42 @@ def get_graph_visibility(): assert len(dash_dcc.find_elements(".js-plotly-plot .bars path")) == 4 assert dash_dcc.driver.execute_script(test_identity) assert get_graph_visibility() == "visible" + +def test_ldcp007_class_and_style_props(dash_dcc): + lock = Lock() + + app = dash.Dash(__name__) + + print(dcc.__version__) + + app.layout = html.Div([ + html.Button("click", id="btn"), + dcc.Loading( + id="loading", + className='spinner-class', + parent_className='parent-class', + style={'background-color': 'rgb(255,192,203)'}, + # rgb(240, 248, 255) = aliceblue + parent_style={'border': '3px solid rgb(240, 248, 255)'}, + children=html.Div(id='loading-child') + ) + ]) + + @app.callback(Output("loading-child", "children"), [Input("btn", "n_clicks")]) + def updateDiv(n_clicks): + if n_clicks is None: + return + + with lock: + return 'sample text content' + + dash_dcc.start_server(app) + + dash_dcc.wait_for_style_to_equal('.parent-class', 'border-color', 'rgb(240, 248, 255)') + + with lock: + button = dash_dcc.find_element("#btn") + button.click() + dash_dcc.wait_for_style_to_equal('.spinner-class', 'background-color', 'rgba(255, 192, 203, 1)') + + assert not dash_dcc.get_logs() From 5493917725b326b667674101e89063867d917192 Mon Sep 17 00:00:00 2001 From: wbrgss Date: Thu, 13 Aug 2020 18:19:23 -0400 Subject: [PATCH 05/11] black --- .../loading/test_loading_component.py | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/tests/integration/loading/test_loading_component.py b/tests/integration/loading/test_loading_component.py index 8afd85efc..46016ebe2 100644 --- a/tests/integration/loading/test_loading_component.py +++ b/tests/integration/loading/test_loading_component.py @@ -279,6 +279,7 @@ def get_graph_visibility(): assert dash_dcc.driver.execute_script(test_identity) assert get_graph_visibility() == "visible" + def test_ldcp007_class_and_style_props(dash_dcc): lock = Lock() @@ -286,18 +287,20 @@ def test_ldcp007_class_and_style_props(dash_dcc): print(dcc.__version__) - app.layout = html.Div([ - html.Button("click", id="btn"), - dcc.Loading( - id="loading", - className='spinner-class', - parent_className='parent-class', - style={'background-color': 'rgb(255,192,203)'}, - # rgb(240, 248, 255) = aliceblue - parent_style={'border': '3px solid rgb(240, 248, 255)'}, - children=html.Div(id='loading-child') - ) - ]) + app.layout = html.Div( + [ + html.Button("click", id="btn"), + dcc.Loading( + id="loading", + className="spinner-class", + parent_className="parent-class", + style={"background-color": "rgb(255,192,203)"}, + # rgb(240, 248, 255) = aliceblue + parent_style={"border": "3px solid rgb(240, 248, 255)"}, + children=html.Div(id="loading-child"), + ), + ] + ) @app.callback(Output("loading-child", "children"), [Input("btn", "n_clicks")]) def updateDiv(n_clicks): @@ -305,15 +308,19 @@ def updateDiv(n_clicks): return with lock: - return 'sample text content' + return "sample text content" dash_dcc.start_server(app) - dash_dcc.wait_for_style_to_equal('.parent-class', 'border-color', 'rgb(240, 248, 255)') + dash_dcc.wait_for_style_to_equal( + ".parent-class", "border-color", "rgb(240, 248, 255)" + ) with lock: button = dash_dcc.find_element("#btn") button.click() - dash_dcc.wait_for_style_to_equal('.spinner-class', 'background-color', 'rgba(255, 192, 203, 1)') + dash_dcc.wait_for_style_to_equal( + ".spinner-class", "background-color", "rgba(255, 192, 203, 1)" + ) assert not dash_dcc.get_logs() From 2460926f6c98861ca55bcc0770f1256056d0400e Mon Sep 17 00:00:00 2001 From: wbrgss Date: Fri, 14 Aug 2020 17:40:10 -0400 Subject: [PATCH 06/11] Loading component graph test --- .../loading/test_loading_component.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/integration/loading/test_loading_component.py b/tests/integration/loading/test_loading_component.py index 46016ebe2..2d625174b 100644 --- a/tests/integration/loading/test_loading_component.py +++ b/tests/integration/loading/test_loading_component.py @@ -324,3 +324,46 @@ def updateDiv(n_clicks): ) assert not dash_dcc.get_logs() + + +def test_ldcp008_graph_in_loading_fits_container_height(dash_dcc): + lock = Lock() + + app = dash.Dash(__name__) + + print(dcc.__version__) + + app.layout = html.Div( + className="outer-container", + children=[ + html.Div( + dcc.Loading( + parent_style={"height": "100%"}, + children=dcc.Graph( + style={"height": "100%"}, + figure={ + "data": [ + { + "x": [1, 2, 3, 4], + "y": [4, 1, 6, 9], + "line": {"shape": "spline"}, + } + ] + }, + ), + ), + ) + ], + style={"display": "flex", "height": "300px",}, + ) + + dash_dcc.start_server(app) + + with lock: + dash_dcc.wait_for_style_to_equal(".js-plotly-plot", "height", "300px") + + assert dash_dcc.wait_for_element(".js-plotly-plot").size.get( + "height" + ) == dash_dcc.wait_for_element(".outer-container").size.get("height") + + assert not dash_dcc.get_logs() From 8daac2a8a0bcc4c6f3fec49dccabf09394196f03 Mon Sep 17 00:00:00 2001 From: wbrgss Date: Fri, 14 Aug 2020 17:45:12 -0400 Subject: [PATCH 07/11] Prop description --- src/components/Loading.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Loading.react.js b/src/components/Loading.react.js index e04317ede..b46b8ebc2 100644 --- a/src/components/Loading.react.js +++ b/src/components/Loading.react.js @@ -123,7 +123,7 @@ Loading.propTypes = { debug: PropTypes.bool, /** - * CSS class for the spinner root DOM node + * Additional CSS class for the spinner root DOM node */ className: PropTypes.string, From b0eba74106c9cdfb8bec346c7c22d95b28b9dc43 Mon Sep 17 00:00:00 2001 From: wbrgss Date: Fri, 14 Aug 2020 17:48:24 -0400 Subject: [PATCH 08/11] extra comma --- tests/integration/loading/test_loading_component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/loading/test_loading_component.py b/tests/integration/loading/test_loading_component.py index 2d625174b..3dbe4a32c 100644 --- a/tests/integration/loading/test_loading_component.py +++ b/tests/integration/loading/test_loading_component.py @@ -354,7 +354,7 @@ def test_ldcp008_graph_in_loading_fits_container_height(dash_dcc): ), ) ], - style={"display": "flex", "height": "300px",}, + style={"display": "flex", "height": "300px"}, ) dash_dcc.start_server(app) From d5cee9649bcc2fb763b5bc2510281081b03f67d1 Mon Sep 17 00:00:00 2001 From: wbrgss Date: Tue, 18 Aug 2020 11:19:22 -0400 Subject: [PATCH 09/11] remove print statements --- tests/integration/loading/test_loading_component.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/integration/loading/test_loading_component.py b/tests/integration/loading/test_loading_component.py index 3dbe4a32c..11ba9fdbb 100644 --- a/tests/integration/loading/test_loading_component.py +++ b/tests/integration/loading/test_loading_component.py @@ -285,8 +285,6 @@ def test_ldcp007_class_and_style_props(dash_dcc): app = dash.Dash(__name__) - print(dcc.__version__) - app.layout = html.Div( [ html.Button("click", id="btn"), @@ -331,8 +329,6 @@ def test_ldcp008_graph_in_loading_fits_container_height(dash_dcc): app = dash.Dash(__name__) - print(dcc.__version__) - app.layout = html.Div( className="outer-container", children=[ From 458bbf46673777d22dae5df05b1e7839758f5bc3 Mon Sep 17 00:00:00 2001 From: wbrgss Date: Tue, 18 Aug 2020 11:32:12 -0400 Subject: [PATCH 10/11] fix integration tests by removing explicit keyword args --- tests/test_integration_1.py | 524 ++++++++++++++++++++---------------- 1 file changed, 292 insertions(+), 232 deletions(-) diff --git a/tests/test_integration_1.py b/tests/test_integration_1.py index 7cedf563e..011d6a9c0 100644 --- a/tests/test_integration_1.py +++ b/tests/test_integration_1.py @@ -43,22 +43,25 @@ def test_loading_slider(self): app = dash.Dash(__name__) - app.layout = html.Div([ - html.Button(id='test-btn'), - html.Label(id='test-div', children=['Horizontal Slider']), - dcc.Slider( - id='horizontal-slider', - min=0, - max=9, - marks={i: 'Label {}'.format(i) if i == 1 else str(i) - for i in range(1, 6)}, - value=5, - ), - ]) + app.layout = html.Div( + [ + html.Button(id="test-btn"), + html.Label(id="test-div", children=["Horizontal Slider"]), + dcc.Slider( + id="horizontal-slider", + min=0, + max=9, + marks={ + i: "Label {}".format(i) if i == 1 else str(i) + for i in range(1, 6) + }, + value=5, + ), + ] + ) @app.callback( - Output('horizontal-slider', 'value'), - [Input('test-btn', 'n_clicks')] + Output("horizontal-slider", "value"), [Input("test-btn", "n_clicks")] ) def user_delayed_value(n_clicks): with lock: @@ -76,7 +79,7 @@ def user_delayed_value(n_clicks): ) with lock: - self.driver.find_element_by_id('test-btn').click() + self.driver.find_element_by_id("test-btn").click() self.wait_for_element_by_css_selector( '#horizontal-slider[data-dash-is-loading="true"]' @@ -87,26 +90,30 @@ def user_delayed_value(n_clicks): ) for entry in self.get_log(): - raise Exception('browser error logged during test', entry) + raise Exception("browser error logged during test", entry) def test_horizontal_slider(self): app = dash.Dash(__name__) - app.layout = html.Div([ - html.Label('Horizontal Slider'), - dcc.Slider( - id='horizontal-slider', - min=0, - max=9, - marks={i: 'Label {}'.format(i) if i == 1 else str(i) - for i in range(1, 6)}, - value=5, - ), - ]) + app.layout = html.Div( + [ + html.Label("Horizontal Slider"), + dcc.Slider( + id="horizontal-slider", + min=0, + max=9, + marks={ + i: "Label {}".format(i) if i == 1 else str(i) + for i in range(1, 6) + }, + value=5, + ), + ] + ) self.startServer(app) - self.wait_for_element_by_css_selector('#horizontal-slider') - self.snapshot('horizontal slider') + self.wait_for_element_by_css_selector("#horizontal-slider") + self.snapshot("horizontal slider") h_slider = self.driver.find_element_by_css_selector( '#horizontal-slider div[role="slider"]' @@ -114,27 +121,32 @@ def test_horizontal_slider(self): h_slider.click() for entry in self.get_log(): - raise Exception('browser error logged during test', entry) + raise Exception("browser error logged during test", entry) def test_vertical_slider(self): app = dash.Dash(__name__) - app.layout = html.Div([ - html.Label('Vertical Slider'), - dcc.Slider( - id='vertical-slider', - min=0, - max=9, - marks={i: 'Label {}'.format(i) if i == 1 else str(i) - for i in range(1, 6)}, - value=5, - vertical=True, - ), - ], style={'height': '500px'}) + app.layout = html.Div( + [ + html.Label("Vertical Slider"), + dcc.Slider( + id="vertical-slider", + min=0, + max=9, + marks={ + i: "Label {}".format(i) if i == 1 else str(i) + for i in range(1, 6) + }, + value=5, + vertical=True, + ), + ], + style={"height": "500px"}, + ) self.startServer(app) - self.wait_for_element_by_css_selector('#vertical-slider') - self.snapshot('vertical slider') + self.wait_for_element_by_css_selector("#vertical-slider") + self.snapshot("vertical slider") v_slider = self.driver.find_element_by_css_selector( '#vertical-slider div[role="slider"]' @@ -142,29 +154,32 @@ def test_vertical_slider(self): v_slider.click() for entry in self.get_log(): - raise Exception('browser error logged during test', entry) + raise Exception("browser error logged during test", entry) def test_loading_range_slider(self): lock = Lock() app = dash.Dash(__name__) - app.layout = html.Div([ - html.Button(id='test-btn'), - html.Label(id='test-div', children=['Horizontal Range Slider']), - dcc.RangeSlider( - id='horizontal-range-slider', - min=0, - max=9, - marks={i: 'Label {}'.format(i) if i == 1 else str(i) - for i in range(1, 6)}, - value=[4, 6], - ), - ]) + app.layout = html.Div( + [ + html.Button(id="test-btn"), + html.Label(id="test-div", children=["Horizontal Range Slider"]), + dcc.RangeSlider( + id="horizontal-range-slider", + min=0, + max=9, + marks={ + i: "Label {}".format(i) if i == 1 else str(i) + for i in range(1, 6) + }, + value=[4, 6], + ), + ] + ) @app.callback( - Output('horizontal-range-slider', 'value'), - [Input('test-btn', 'n_clicks')] + Output("horizontal-range-slider", "value"), [Input("test-btn", "n_clicks")] ) def delayed_value(children): with lock: @@ -182,7 +197,7 @@ def delayed_value(children): ) with lock: - self.driver.find_element_by_id('test-btn').click() + self.driver.find_element_by_id("test-btn").click() self.wait_for_element_by_css_selector( '#horizontal-range-slider[data-dash-is-loading="true"]' @@ -193,26 +208,30 @@ def delayed_value(children): ) for entry in self.get_log(): - raise Exception('browser error logged during test', entry) + raise Exception("browser error logged during test", entry) def test_horizontal_range_slider(self): app = dash.Dash(__name__) - app.layout = html.Div([ - html.Label('Horizontal Range Slider'), - dcc.RangeSlider( - id='horizontal-range-slider', - min=0, - max=9, - marks={i: 'Label {}'.format(i) if i == 1 else str(i) - for i in range(1, 6)}, - value=[4, 6], - ), - ]) + app.layout = html.Div( + [ + html.Label("Horizontal Range Slider"), + dcc.RangeSlider( + id="horizontal-range-slider", + min=0, + max=9, + marks={ + i: "Label {}".format(i) if i == 1 else str(i) + for i in range(1, 6) + }, + value=[4, 6], + ), + ] + ) self.startServer(app) - self.wait_for_element_by_css_selector('#horizontal-range-slider') - self.snapshot('horizontal range slider') + self.wait_for_element_by_css_selector("#horizontal-range-slider") + self.snapshot("horizontal range slider") h_slider_1 = self.driver.find_element_by_css_selector( '#horizontal-range-slider div.rc-slider-handle-1[role="slider"]' @@ -225,27 +244,32 @@ def test_horizontal_range_slider(self): h_slider_2.click() for entry in self.get_log(): - raise Exception('browser error logged during test', entry) + raise Exception("browser error logged during test", entry) def test_vertical_range_slider(self): app = dash.Dash(__name__) - app.layout = html.Div([ - html.Label('Vertical Range Slider'), - dcc.RangeSlider( - id='vertical-range-slider', - min=0, - max=9, - marks={i: 'Label {}'.format(i) if i == 1 else str(i) - for i in range(1, 6)}, - value=[4, 6], - vertical=True, - ), - ], style={'height': '500px'}) + app.layout = html.Div( + [ + html.Label("Vertical Range Slider"), + dcc.RangeSlider( + id="vertical-range-slider", + min=0, + max=9, + marks={ + i: "Label {}".format(i) if i == 1 else str(i) + for i in range(1, 6) + }, + value=[4, 6], + vertical=True, + ), + ], + style={"height": "500px"}, + ) self.startServer(app) - self.wait_for_element_by_css_selector('#vertical-range-slider') - self.snapshot('vertical range slider') + self.wait_for_element_by_css_selector("#vertical-range-slider") + self.snapshot("vertical range slider") v_slider_1 = self.driver.find_element_by_css_selector( '#vertical-range-slider div.rc-slider-handle-1[role="slider"]' @@ -258,230 +282,266 @@ def test_vertical_range_slider(self): v_slider_2.click() for entry in self.get_log(): - raise Exception('browser error logged during test', entry) + raise Exception("browser error logged during test", entry) def test_tabs_in_vertical_mode(self): app = dash.Dash(__name__) - app.layout = html.Div([ - dcc.Tabs(id="tabs", value='tab-3', children=[ - dcc.Tab(label='Tab one', value='tab-1', id='tab-1', children=[ - html.Div('Tab One Content') - ]), - dcc.Tab(label='Tab two', value='tab-2', id='tab-2', children=[ - html.Div('Tab Two Content') - ]), - dcc.Tab(label='Tab three', value='tab-3', id='tab-3', children=[ - html.Div('Tab Three Content') - ]), - ], vertical=True), - html.Div(id='tabs-content') - ]) + app.layout = html.Div( + [ + dcc.Tabs( + id="tabs", + value="tab-3", + children=[ + dcc.Tab( + label="Tab one", + value="tab-1", + id="tab-1", + children=[html.Div("Tab One Content")], + ), + dcc.Tab( + label="Tab two", + value="tab-2", + id="tab-2", + children=[html.Div("Tab Two Content")], + ), + dcc.Tab( + label="Tab three", + value="tab-3", + id="tab-3", + children=[html.Div("Tab Three Content")], + ), + ], + vertical=True, + ), + html.Div(id="tabs-content"), + ] + ) self.startServer(app=app) - self.wait_for_text_to_equal('#tab-3', 'Tab three') + self.wait_for_text_to_equal("#tab-3", "Tab three") - self.snapshot('Tabs - vertical mode') + self.snapshot("Tabs - vertical mode") def test_tabs_without_children(self): app = dash.Dash(__name__) - app.layout = html.Div([ - html.H1('Dash Tabs component demo'), - dcc.Tabs(id="tabs", value='tab-2', children=[ - dcc.Tab(label='Tab one', value='tab-1', id='tab-1'), - dcc.Tab(label='Tab two', value='tab-2', id='tab-2'), - ]), - html.Div(id='tabs-content') - ]) - - @app.callback(dash.dependencies.Output('tabs-content', 'children'), - [dash.dependencies.Input('tabs', 'value')]) + app.layout = html.Div( + [ + html.H1("Dash Tabs component demo"), + dcc.Tabs( + id="tabs", + value="tab-2", + children=[ + dcc.Tab(label="Tab one", value="tab-1", id="tab-1"), + dcc.Tab(label="Tab two", value="tab-2", id="tab-2"), + ], + ), + html.Div(id="tabs-content"), + ] + ) + + @app.callback( + dash.dependencies.Output("tabs-content", "children"), + [dash.dependencies.Input("tabs", "value")], + ) def render_content(tab): - if tab == 'tab-1': - return html.Div([ - html.H3('Test content 1') - ], id='test-tab-1') - elif tab == 'tab-2': - return html.Div([ - html.H3('Test content 2') - ], id='test-tab-2') + if tab == "tab-1": + return html.Div([html.H3("Test content 1")], id="test-tab-1") + elif tab == "tab-2": + return html.Div([html.H3("Test content 2")], id="test-tab-2") self.startServer(app=app) - self.wait_for_text_to_equal('#tabs-content', 'Test content 2') - self.snapshot('initial tab - tab 2') + self.wait_for_text_to_equal("#tabs-content", "Test content 2") + self.snapshot("initial tab - tab 2") - selected_tab = self.wait_for_element_by_css_selector('#tab-1') + selected_tab = self.wait_for_element_by_css_selector("#tab-1") selected_tab.click() time.sleep(1) - self.wait_for_text_to_equal('#tabs-content', 'Test content 1') + self.wait_for_text_to_equal("#tabs-content", "Test content 1") def test_tabs_with_children_undefined(self): app = dash.Dash(__name__) - app.layout = html.Div([ - html.H1('Dash Tabs component demo'), - dcc.Tabs(id="tabs", value='tab-1'), - html.Div(id='tabs-content') - ]) + app.layout = html.Div( + [ + html.H1("Dash Tabs component demo"), + dcc.Tabs(id="tabs", value="tab-1"), + html.Div(id="tabs-content"), + ] + ) self.startServer(app=app) - self.wait_for_element_by_css_selector('#tabs-content') + self.wait_for_element_by_css_selector("#tabs-content") - self.snapshot('Tabs component with children undefined') + self.snapshot("Tabs component with children undefined") def test_tabs_without_value(self): app = dash.Dash(__name__) - app.layout = html.Div([ - html.H1('Dash Tabs component demo'), - dcc.Tabs(id="tabs-without-value", children=[ - dcc.Tab(label='Tab One', value='tab-1'), - dcc.Tab(label='Tab Two', value='tab-2'), - ]), - html.Div(id='tabs-content') - ]) - - @app.callback(Output('tabs-content', 'children'), - [Input('tabs-without-value', 'value')]) + app.layout = html.Div( + [ + html.H1("Dash Tabs component demo"), + dcc.Tabs( + id="tabs-without-value", + children=[ + dcc.Tab(label="Tab One", value="tab-1"), + dcc.Tab(label="Tab Two", value="tab-2"), + ], + ), + html.Div(id="tabs-content"), + ] + ) + + @app.callback( + Output("tabs-content", "children"), [Input("tabs-without-value", "value")] + ) def render_content(tab): - if tab == 'tab-1': - return html.H3('Default selected Tab content 1') - elif tab == 'tab-2': - return html.H3('Tab content 2') + if tab == "tab-1": + return html.H3("Default selected Tab content 1") + elif tab == "tab-2": + return html.H3("Tab content 2") self.startServer(app=app) - self.wait_for_text_to_equal('#tabs-content', 'Default selected Tab content 1') + self.wait_for_text_to_equal("#tabs-content", "Default selected Tab content 1") - self.snapshot('Tab 1 should be selected by default') + self.snapshot("Tab 1 should be selected by default") def test_location_link(self): app = dash.Dash(__name__) - app.layout = html.Div([ - html.Div(id='waitfor'), - dcc.Location(id='test-location', refresh=False), - - dcc.Link( - html.Button('I am a clickable button'), - id='test-link', - href='/test/pathname'), - dcc.Link( - html.Button('I am a clickable hash button'), - id='test-link-hash', - href='#test'), - dcc.Link( - html.Button('I am a clickable search button'), - id='test-link-search', - href='?testQuery=testValue', - refresh=False), - html.Button('I am a magic button that updates pathname', - id='test-button'), - html.A('link to click', href='/test/pathname/a', id='test-a'), - html.A('link to click', href='#test-hash', id='test-a-hash'), - html.A('link to click', href='?queryA=valueA', id='test-a-query'), - html.Div(id='test-pathname', children=[]), - html.Div(id='test-hash', children=[]), - html.Div(id='test-search', children=[]), - ]) + app.layout = html.Div( + [ + html.Div(id="waitfor"), + dcc.Location(id="test-location", refresh=False), + dcc.Link( + html.Button("I am a clickable button"), + id="test-link", + href="/test/pathname", + ), + dcc.Link( + html.Button("I am a clickable hash button"), + id="test-link-hash", + href="#test", + ), + dcc.Link( + html.Button("I am a clickable search button"), + id="test-link-search", + href="?testQuery=testValue", + refresh=False, + ), + html.Button( + "I am a magic button that updates pathname", id="test-button" + ), + html.A("link to click", href="/test/pathname/a", id="test-a"), + html.A("link to click", href="#test-hash", id="test-a-hash"), + html.A("link to click", href="?queryA=valueA", id="test-a-query"), + html.Div(id="test-pathname", children=[]), + html.Div(id="test-hash", children=[]), + html.Div(id="test-search", children=[]), + ] + ) @app.callback( - output=Output(component_id='test-pathname', - component_property='children'), - inputs=[Input(component_id='test-location', component_property='pathname')]) + Output(component_id="test-pathname", component_property="children"), + [Input(component_id="test-location", component_property="pathname")], + ) def update_test_pathname(pathname): return pathname @app.callback( - output=Output(component_id='test-hash', - component_property='children'), - inputs=[Input(component_id='test-location', component_property='hash')]) + Output(component_id="test-hash", component_property="children"), + [Input(component_id="test-location", component_property="hash")], + ) def update_test_hash(hash_val): if hash_val is None: - return '' + return "" return hash_val @app.callback( - output=Output(component_id='test-search', - component_property='children'), - inputs=[Input(component_id='test-location', component_property='search')]) + Output(component_id="test-search", component_property="children"), + [Input(component_id="test-location", component_property="search")], + ) def update_test_search(search): if search is None: - return '' + return "" return search @app.callback( - output=Output(component_id='test-location', - component_property='pathname'), - inputs=[Input(component_id='test-button', - component_property='n_clicks')], - state=[State(component_id='test-location', component_property='pathname')]) + Output(component_id="test-location", component_property="pathname"), + [Input(component_id="test-button", component_property="n_clicks")], + [State(component_id="test-location", component_property="pathname")], + ) def update_pathname(n_clicks, current_pathname): if n_clicks is not None: - return '/new/pathname' + return "/new/pathname" return current_pathname self.startServer(app=app) time.sleep(1) - self.snapshot('link -- location') + self.snapshot("link -- location") # Check that link updates pathname - self.wait_for_element_by_css_selector('#test-link').click() + self.wait_for_element_by_css_selector("#test-link").click() self.assertEqual( - self.driver.current_url.replace('http://localhost:8050', ''), - '/test/pathname') - self.wait_for_text_to_equal('#test-pathname', '/test/pathname') + self.driver.current_url.replace("http://localhost:8050", ""), + "/test/pathname", + ) + self.wait_for_text_to_equal("#test-pathname", "/test/pathname") # Check that hash is updated in the Location - self.wait_for_element_by_css_selector('#test-link-hash').click() - self.wait_for_text_to_equal('#test-pathname', '/test/pathname') - self.wait_for_text_to_equal('#test-hash', '#test') - self.snapshot('link -- /test/pathname#test') + self.wait_for_element_by_css_selector("#test-link-hash").click() + self.wait_for_text_to_equal("#test-pathname", "/test/pathname") + self.wait_for_text_to_equal("#test-hash", "#test") + self.snapshot("link -- /test/pathname#test") # Check that search is updated in the Location -- note that this goes through href and therefore wipes the hash - self.wait_for_element_by_css_selector('#test-link-search').click() - self.wait_for_text_to_equal('#test-search', '?testQuery=testValue') - self.wait_for_text_to_equal('#test-hash', '') - self.snapshot('link -- /test/pathname?testQuery=testValue') + self.wait_for_element_by_css_selector("#test-link-search").click() + self.wait_for_text_to_equal("#test-search", "?testQuery=testValue") + self.wait_for_text_to_equal("#test-hash", "") + self.snapshot("link -- /test/pathname?testQuery=testValue") # Check that pathname is updated through a Button click via props - self.wait_for_element_by_css_selector('#test-button').click() - self.wait_for_text_to_equal('#test-pathname', '/new/pathname') - self.wait_for_text_to_equal('#test-search', '?testQuery=testValue') - self.snapshot('link -- /new/pathname?testQuery=testValue') + self.wait_for_element_by_css_selector("#test-button").click() + self.wait_for_text_to_equal("#test-pathname", "/new/pathname") + self.wait_for_text_to_equal("#test-search", "?testQuery=testValue") + self.snapshot("link -- /new/pathname?testQuery=testValue") # Check that pathname is updated through an a tag click via props - self.wait_for_element_by_css_selector('#test-a').click() + self.wait_for_element_by_css_selector("#test-a").click() try: - self.wait_for_element_by_css_selector('#waitfor') + self.wait_for_element_by_css_selector("#waitfor") except Exception as e: - print(self.wait_for_element_by_css_selector( - '#_dash-app-content').get_attribute('innerHTML')) + print( + self.wait_for_element_by_css_selector( + "#_dash-app-content" + ).get_attribute("innerHTML") + ) raise e - self.wait_for_text_to_equal('#test-pathname', '/test/pathname/a') - self.wait_for_text_to_equal('#test-search', '') - self.wait_for_text_to_equal('#test-hash', '') - self.snapshot('link -- /test/pathname/a') + self.wait_for_text_to_equal("#test-pathname", "/test/pathname/a") + self.wait_for_text_to_equal("#test-search", "") + self.wait_for_text_to_equal("#test-hash", "") + self.snapshot("link -- /test/pathname/a") # Check that hash is updated through an a tag click via props - self.wait_for_element_by_css_selector('#test-a-hash').click() - self.wait_for_text_to_equal('#test-pathname', '/test/pathname/a') - self.wait_for_text_to_equal('#test-search', '') - self.wait_for_text_to_equal('#test-hash', '#test-hash') - self.snapshot('link -- /test/pathname/a#test-hash') + self.wait_for_element_by_css_selector("#test-a-hash").click() + self.wait_for_text_to_equal("#test-pathname", "/test/pathname/a") + self.wait_for_text_to_equal("#test-search", "") + self.wait_for_text_to_equal("#test-hash", "#test-hash") + self.snapshot("link -- /test/pathname/a#test-hash") # Check that hash is updated through an a tag click via props - self.wait_for_element_by_css_selector('#test-a-query').click() - self.wait_for_element_by_css_selector('#waitfor') - self.wait_for_text_to_equal('#test-pathname', '/test/pathname/a') - self.wait_for_text_to_equal('#test-search', '?queryA=valueA') - self.wait_for_text_to_equal('#test-hash', '') - self.snapshot('link -- /test/pathname/a?queryA=valueA') + self.wait_for_element_by_css_selector("#test-a-query").click() + self.wait_for_element_by_css_selector("#waitfor") + self.wait_for_text_to_equal("#test-pathname", "/test/pathname/a") + self.wait_for_text_to_equal("#test-search", "?queryA=valueA") + self.wait_for_text_to_equal("#test-hash", "") + self.snapshot("link -- /test/pathname/a?queryA=valueA") From 82aaab12afbe8dcf724d7df21951e934a1d92559 Mon Sep 17 00:00:00 2001 From: wbrgss Date: Tue, 18 Aug 2020 12:04:46 -0400 Subject: [PATCH 11/11] update CHANGELOG --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04635e5d9..7e8b22f0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] +### Added +- [#840](https://github.com/plotly/dash-table/pull/840) Add styling properties to `dcc.Loading` component + + `parent_className`: Add CSS class for the outermost `dcc.Loading` parent div DOM node + + `parent_style`: Add CSS style property for the outermost `dcc.Loading` parent div DOM node + + provides a workaround for the previous behaviour the of `className` property, which changed in [#740](https://github.com/plotly/dash-core-components/pull/740). `parent_className` (or inline styles in `parent_style`) now allow CSS rules to be applied to the outermost `dcc.Loading` div, which is no longer covered by `className` on loading completion as of Dash Core Components `>= 1.9.1` (Dash `>= 1.11.0`). + ## [1.10.2] - 2020-07-27 - [#835](https://github.com/plotly/dash-core-components/pull/835) - Upgraded Plotly.js to [1.54.7](https://github.com/plotly/plotly.js/releases/tag/v1.54.7)