@@ -25,7 +25,7 @@ def findAsyncPlotlyJs(scripts):
2525
2626
2727@pytest .mark .parametrize ("is_eager" , [True , False ])
28- def test_candlestick (dash_dcc , is_eager ):
28+ def test_grva001_candlestick (dash_dcc , is_eager ):
2929 app = dash .Dash (__name__ , eager_loading = is_eager )
3030 app .layout = html .Div (
3131 [
@@ -75,7 +75,7 @@ def update_graph(n_clicks):
7575
7676
7777@pytest .mark .parametrize ("is_eager" , [True , False ])
78- def test_graphs_with_different_figures (dash_dcc , is_eager ):
78+ def test_grva002_graphs_with_different_figures (dash_dcc , is_eager ):
7979 app = dash .Dash (__name__ , eager_loading = is_eager )
8080 app .layout = html .Div (
8181 [
@@ -160,7 +160,7 @@ def show_relayout_data(data):
160160
161161
162162@pytest .mark .parametrize ("is_eager" , [True , False ])
163- def test_empty_graph (dash_dcc , is_eager ):
163+ def test_grva003_empty_graph (dash_dcc , is_eager ):
164164 app = dash .Dash (__name__ , eager_loading = is_eager )
165165
166166 app .layout = html .Div (
@@ -193,7 +193,7 @@ def render_content(click, prev_graph):
193193
194194
195195@pytest .mark .parametrize ("is_eager" , [True , False ])
196- def test_graph_prepend_trace (dash_dcc , is_eager ):
196+ def test_grva004_graph_prepend_trace (dash_dcc , is_eager ):
197197 app = dash .Dash (__name__ , eager_loading = is_eager )
198198
199199 def generate_with_id (id , data = None ):
@@ -358,7 +358,7 @@ def display_data(trigger, fig):
358358
359359
360360@pytest .mark .parametrize ("is_eager" , [True , False ])
361- def test_graph_extend_trace (dash_dcc , is_eager ):
361+ def test_grva005_graph_extend_trace (dash_dcc , is_eager ):
362362 app = dash .Dash (__name__ , eager_loading = is_eager )
363363
364364 def generate_with_id (id , data = None ):
@@ -521,7 +521,7 @@ def display_data(trigger, fig):
521521
522522
523523@pytest .mark .parametrize ("is_eager" , [True , False ])
524- def test_unmounted_graph_resize (dash_dcc , is_eager ):
524+ def test_grva006_unmounted_graph_resize (dash_dcc , is_eager ):
525525 app = dash .Dash (__name__ , eager_loading = is_eager )
526526
527527 app .layout = html .Div (
@@ -619,7 +619,7 @@ def test_unmounted_graph_resize(dash_dcc, is_eager):
619619 dash_dcc .driver .set_window_size (window_size ["width" ], window_size ["height" ])
620620
621621
622- def test_external_plotlyjs_prevents_lazy (dash_dcc ):
622+ def test_grva007_external_plotlyjs_prevents_lazy (dash_dcc ):
623623 app = dash .Dash (
624624 __name__ ,
625625 eager_loading = False ,
@@ -658,3 +658,168 @@ def load_chart(n_clicks):
658658 scripts = dash_dcc .driver .find_elements (By .CSS_SELECTOR , "script" )
659659 assert findSyncPlotlyJs (scripts ) is None
660660 assert findAsyncPlotlyJs (scripts ) is None
661+
662+
663+ def test_grva008_shapes_not_lost (dash_dcc ):
664+ # See issue #879 and pr #905
665+ app = dash .Dash (__name__ )
666+
667+ fig = {"data" : [], "layout" : {"dragmode" : "drawrect" }}
668+ graph = dcc .Graph (id = "graph" , figure = fig , style = {"height" : "400px" })
669+
670+ app .layout = html .Div (
671+ [
672+ graph ,
673+ html .Br (),
674+ html .Button (id = "button" , children = "Clone figure" ),
675+ html .Div (id = "output" , children = "" ),
676+ ]
677+ )
678+
679+ app .clientside_callback (
680+ """
681+ function clone_figure(_, figure) {
682+ const new_figure = {...figure};
683+ const shapes = new_figure.layout.shapes || [];
684+ return [new_figure, shapes.length];
685+ }
686+ """ ,
687+ Output ("graph" , "figure" ),
688+ Output ("output" , "children" ),
689+ Input ("button" , "n_clicks" ),
690+ State ("graph" , "figure" ),
691+ )
692+
693+ dash_dcc .start_server (app )
694+ button = dash_dcc .wait_for_element ("#button" )
695+ dash_dcc .wait_for_text_to_equal ("#output" , "0" )
696+
697+ # Draw a shape
698+ dash_dcc .click_and_hold_at_coord_fractions ("#graph" , 0.25 , 0.25 )
699+ dash_dcc .move_to_coord_fractions ("#graph" , 0.35 , 0.75 )
700+ dash_dcc .release ()
701+
702+ # Click to trigger an update of the output, the shape should survive
703+ dash_dcc .wait_for_text_to_equal ("#output" , "0" )
704+ button .click ()
705+ dash_dcc .wait_for_text_to_equal ("#output" , "1" )
706+
707+ # Draw another shape
708+ dash_dcc .click_and_hold_at_coord_fractions ("#graph" , 0.75 , 0.25 )
709+ dash_dcc .move_to_coord_fractions ("#graph" , 0.85 , 0.75 )
710+ dash_dcc .release ()
711+
712+ # Click to trigger an update of the output, the shape should survive
713+ dash_dcc .wait_for_text_to_equal ("#output" , "1" )
714+ button .click ()
715+ dash_dcc .wait_for_text_to_equal ("#output" , "2" )
716+
717+
718+ @pytest .mark .parametrize ("mutate_fig" , [True , False ])
719+ def test_grva009_originals_maintained_for_responsive_override (mutate_fig , dash_dcc ):
720+ # In #905 we made changes to prevent shapes from being lost.
721+ # This test makes sure that the overrides applied by the `responsive`
722+ # prop are "undone" when the `responsive` prop changes.
723+
724+ app = dash .Dash (__name__ )
725+
726+ graph = dcc .Graph (
727+ id = "graph" ,
728+ figure = {"data" : [{"y" : [1 , 2 ]}], "layout" : {"width" : 300 , "height" : 250 }},
729+ style = {"height" : "400px" , "width" : "500px" },
730+ )
731+ responsive_size = [500 , 400 ]
732+ fixed_size = [300 , 250 ]
733+
734+ app .layout = html .Div (
735+ [
736+ graph ,
737+ html .Br (),
738+ html .Button (id = "edit_figure" , children = "Edit figure" ),
739+ html .Button (id = "edit_responsive" , children = "Edit responsive" ),
740+ html .Div (id = "output" , children = "" ),
741+ ]
742+ )
743+
744+ if mutate_fig :
745+ # Modify the layout in place (which still has changes made by responsive)
746+ change_fig = """
747+ figure.layout.title = {text: String(n_fig || 0)};
748+ const new_figure = {...figure};
749+ """
750+ else :
751+ # Or create a new one each time
752+ change_fig = """
753+ const new_figure = {
754+ data: [{y: [1, 2]}],
755+ layout: {width: 300, height: 250, title: {text: String(n_fig || 0)}}
756+ };
757+ """
758+
759+ callback = (
760+ """
761+ function clone_figure(n_fig, n_resp, figure) {
762+ """
763+ + change_fig
764+ + """
765+ let responsive = [true, false, 'auto'][(n_resp || 0) % 3];
766+ return [new_figure, responsive, (n_fig || 0) + ' ' + responsive];
767+ }
768+ """
769+ )
770+
771+ app .clientside_callback (
772+ callback ,
773+ Output ("graph" , "figure" ),
774+ Output ("graph" , "responsive" ),
775+ Output ("output" , "children" ),
776+ Input ("edit_figure" , "n_clicks" ),
777+ Input ("edit_responsive" , "n_clicks" ),
778+ State ("graph" , "figure" ),
779+ )
780+
781+ dash_dcc .start_server (app )
782+ edit_figure = dash_dcc .wait_for_element ("#edit_figure" )
783+ edit_responsive = dash_dcc .wait_for_element ("#edit_responsive" )
784+
785+ def graph_dims ():
786+ return dash_dcc .driver .execute_script (
787+ """
788+ const layout = document.querySelector('.js-plotly-plot')._fullLayout;
789+ return [layout.width, layout.height];
790+ """
791+ )
792+
793+ dash_dcc .wait_for_text_to_equal ("#output" , "0 true" )
794+ dash_dcc .wait_for_text_to_equal (".gtitle" , "0" )
795+ assert graph_dims () == responsive_size
796+
797+ edit_figure .click ()
798+ dash_dcc .wait_for_text_to_equal ("#output" , "1 true" )
799+ dash_dcc .wait_for_text_to_equal (".gtitle" , "1" )
800+ assert graph_dims () == responsive_size
801+
802+ edit_responsive .click ()
803+ dash_dcc .wait_for_text_to_equal ("#output" , "1 false" )
804+ dash_dcc .wait_for_text_to_equal (".gtitle" , "1" )
805+ assert graph_dims () == fixed_size
806+
807+ edit_figure .click ()
808+ dash_dcc .wait_for_text_to_equal ("#output" , "2 false" )
809+ dash_dcc .wait_for_text_to_equal (".gtitle" , "2" )
810+ assert graph_dims () == fixed_size
811+
812+ edit_responsive .click ()
813+ dash_dcc .wait_for_text_to_equal ("#output" , "2 auto" )
814+ dash_dcc .wait_for_text_to_equal (".gtitle" , "2" )
815+ assert graph_dims () == fixed_size
816+
817+ edit_figure .click ()
818+ dash_dcc .wait_for_text_to_equal ("#output" , "3 auto" )
819+ dash_dcc .wait_for_text_to_equal (".gtitle" , "3" )
820+ assert graph_dims () == fixed_size
821+
822+ edit_responsive .click ()
823+ dash_dcc .wait_for_text_to_equal ("#output" , "3 true" )
824+ dash_dcc .wait_for_text_to_equal (".gtitle" , "3" )
825+ assert graph_dims () == responsive_size
0 commit comments