@@ -89,6 +89,205 @@ def test_multiple_traces_native_legend():
8989 assert plotly_fig .data [2 ].mode == "lines+markers"
9090
9191
92+ def test_axis_mirror_with_spines_and_ticks ():
93+ """Test that mirror=True when both spines and ticks are visible on both sides."""
94+ fig , ax = plt .subplots ()
95+ ax .plot ([0 , 1 ], [0 , 1 ])
96+
97+ # Show all spines
98+ ax .spines ["top" ].set_visible (True )
99+ ax .spines ["bottom" ].set_visible (True )
100+ ax .spines ["left" ].set_visible (True )
101+ ax .spines ["right" ].set_visible (True )
102+
103+ # Show ticks on all sides
104+ ax .tick_params (top = True , bottom = True , left = True , right = True )
105+
106+ plotly_fig = tls .mpl_to_plotly (fig )
107+
108+ assert plotly_fig .layout .xaxis .mirror == "ticks"
109+ assert plotly_fig .layout .yaxis .mirror == "ticks"
110+
111+
112+ def test_axis_mirror_with_ticks_only ():
113+ """Test that mirror=False when spines are not visible on both sides."""
114+ fig , ax = plt .subplots ()
115+ ax .plot ([0 , 1 ], [0 , 1 ])
116+
117+ # Hide opposite spines
118+ ax .spines ["top" ].set_visible (False )
119+ ax .spines ["right" ].set_visible (False )
120+
121+ # Show ticks on all sides
122+ ax .tick_params (top = True , bottom = True , left = True , right = True )
123+
124+ plotly_fig = tls .mpl_to_plotly (fig )
125+
126+ assert plotly_fig .layout .xaxis .mirror == False
127+ assert plotly_fig .layout .yaxis .mirror == False
128+
129+
130+ def test_axis_mirror_false_with_one_sided_ticks ():
131+ """Test that mirror=True when ticks are only on one side but spines are
132+ visible on both sides."""
133+ fig , ax = plt .subplots ()
134+ ax .plot ([0 , 1 ], [0 , 1 ])
135+
136+ # Default matplotlib behavior - ticks only on bottom and left
137+ ax .tick_params (top = False , bottom = True , left = True , right = False )
138+
139+ plotly_fig = tls .mpl_to_plotly (fig )
140+
141+ assert plotly_fig .layout .xaxis .mirror == True
142+ assert plotly_fig .layout .yaxis .mirror == True
143+
144+
145+ def test_axis_mirror_mixed_configurations ():
146+ """Test different configurations for x and y axes."""
147+ fig , ax = plt .subplots ()
148+ ax .plot ([0 , 1 ], [0 , 1 ])
149+
150+ # X-axis: spines and ticks on both sides (mirror="ticks")
151+ ax .spines ["top" ].set_visible (True )
152+ ax .spines ["bottom" ].set_visible (True )
153+ ax .tick_params (top = True , bottom = True )
154+
155+ # Y-axis: spine only on one side (mirror=False)
156+ ax .spines ["right" ].set_visible (False )
157+ ax .spines ["left" ].set_visible (True )
158+ ax .tick_params (left = True , right = True )
159+
160+ plotly_fig = tls .mpl_to_plotly (fig )
161+
162+ assert plotly_fig .layout .xaxis .mirror == "ticks"
163+ assert plotly_fig .layout .yaxis .mirror == False
164+
165+
166+ def test_axis_showline_tied_to_main_spine ():
167+ """Test that showline follows the main-side spine (bottom for x, left for y)."""
168+ fig , ax = plt .subplots ()
169+ ax .plot ([0 , 1 ], [0 , 1 ])
170+
171+ # Hide the mirror-side spines only
172+ ax .spines ["top" ].set_visible (False )
173+ ax .spines ["right" ].set_visible (False )
174+
175+ plotly_fig = tls .mpl_to_plotly (fig )
176+
177+ assert plotly_fig .layout .xaxis .showline == True
178+ assert plotly_fig .layout .yaxis .showline == True
179+
180+
181+ def test_axis_showline_hidden_when_main_spine_hidden ():
182+ """Test that showline is False when the main-side spine is hidden."""
183+ fig , ax = plt .subplots ()
184+ ax .plot ([0 , 1 ], [0 , 1 ])
185+
186+ # Hide the main-side spines but keep the mirror-side ones
187+ ax .spines ["bottom" ].set_visible (False )
188+ ax .spines ["left" ].set_visible (False )
189+
190+ plotly_fig = tls .mpl_to_plotly (fig )
191+
192+ assert plotly_fig .layout .xaxis .showline == False
193+ assert plotly_fig .layout .yaxis .showline == False
194+
195+
196+ def test_ticks_hidden_when_mpl_main_ticks_hidden ():
197+ """Test that tick markers are hidden when the mpl main-side ticks are hidden."""
198+ fig , ax = plt .subplots ()
199+ ax .plot ([0 , 1 ], [0 , 1 ])
200+
201+ ax .tick_params (top = False , bottom = False , left = False , right = False )
202+
203+ plotly_fig = tls .mpl_to_plotly (fig )
204+
205+ assert plotly_fig .layout .xaxis .ticks == ""
206+ assert plotly_fig .layout .yaxis .ticks == ""
207+
208+
209+ def test_twinx_axis_position_and_ticks ():
210+ """Test that twinx secondary y-axis is on the right with visible ticks and title."""
211+ fig , ax1 = plt .subplots ()
212+ ax1 .plot ([0 , 1 , 2 ], [0 , 1 , 4 ])
213+ ax1 .set_ylabel ("left axis" )
214+ ax2 = ax1 .twinx ()
215+ ax2 .plot ([0 , 1 , 2 ], [10 , 5 , 2 ])
216+ ax2 .set_ylabel ("right axis" )
217+
218+ plotly_fig = tls .mpl_to_plotly (fig )
219+
220+ assert plotly_fig .layout .yaxis .side == "left"
221+ assert plotly_fig .layout .yaxis .title .text == "left axis"
222+ assert plotly_fig .layout .yaxis .ticks == "inside"
223+
224+ assert plotly_fig .layout .yaxis2 .side == "right"
225+ assert plotly_fig .layout .yaxis2 .title .text == "right axis"
226+ assert plotly_fig .layout .yaxis2 .ticks == "inside"
227+ assert plotly_fig .layout .yaxis2 .overlaying == "y"
228+ assert plotly_fig .layout .xaxis2 .overlaying == "x"
229+ assert plotly_fig .layout .xaxis2 .visible is False
230+ assert len (plotly_fig .data ) == 2
231+
232+
233+ def test_twiny_axis_position_and_ticks ():
234+ """Test that twiny secondary x-axis is on the top with visible ticks and title."""
235+ fig , ax1 = plt .subplots ()
236+ ax1 .plot ([0 , 1 , 2 ], [0 , 1 , 4 ])
237+ ax1 .set_xlabel ("bottom axis" )
238+ ax2 = ax1 .twiny ()
239+ ax2 .plot ([10 , 5 , 2 ], [0 , 1 , 4 ])
240+ ax2 .set_xlabel ("top axis" )
241+
242+ plotly_fig = tls .mpl_to_plotly (fig )
243+
244+ assert plotly_fig .layout .xaxis .side == "bottom"
245+ assert plotly_fig .layout .xaxis .title .text == "bottom axis"
246+ assert plotly_fig .layout .xaxis .ticks == "inside"
247+
248+ assert plotly_fig .layout .xaxis2 .side == "top"
249+ assert plotly_fig .layout .xaxis2 .title .text == "top axis"
250+ assert plotly_fig .layout .xaxis2 .ticks == "inside"
251+ assert plotly_fig .layout .xaxis2 .overlaying == "x"
252+ assert plotly_fig .layout .yaxis2 .overlaying == "y"
253+ assert plotly_fig .layout .yaxis2 .visible is False
254+ assert len (plotly_fig .data ) == 2
255+
256+
257+ def test_right_axis_ticks_hidden_when_mpl_right_ticks_hidden ():
258+ """Test that ticks are hidden on a right-side axis when right ticks are hidden in matplotlib."""
259+ fig , ax1 = plt .subplots ()
260+ ax2 = ax1 .twinx ()
261+ ax2 .tick_params (right = False )
262+
263+ plotly_fig = tls .mpl_to_plotly (fig )
264+
265+ assert plotly_fig .layout .yaxis2 .side == "right"
266+ assert plotly_fig .layout .yaxis2 .ticks == ""
267+
268+
269+ def test_sharex_stacked_lines_in_correct_subplots ():
270+ """Test that vertically stacked subplots with sharex=True place lines in separate subplots."""
271+ fig , (ax1 , ax2 ) = plt .subplots (2 , 1 , sharex = True )
272+ ax1 .plot ([1 , 2 , 3 ], [4 , 5 , 6 ])
273+ ax2 .plot ([1 , 2 , 3 ], [10 , 20 , 30 ])
274+
275+ plotly_fig = tls .mpl_to_plotly (fig )
276+
277+ assert len (plotly_fig .data ) == 2
278+ # First line belongs to the top subplot
279+ assert plotly_fig .data [0 ].yaxis in (None , "y" , "y1" )
280+ # Second line belongs to the bottom subplot
281+ assert plotly_fig .data [1 ].yaxis == "y2"
282+
283+ # Distinct non-overlapping vertical domains (top above bottom)
284+ assert plotly_fig .layout .yaxis .domain [0 ] > plotly_fig .layout .yaxis2 .domain [1 ]
285+
286+ # The bottom subplot must not overlay the top subplot
287+ assert plotly_fig .layout .yaxis2 .overlaying is None
288+ assert plotly_fig .layout .xaxis2 .overlaying is None
289+
290+
92291def test_violinplot_bodies_are_filled_polygons ():
93292 fig , ax = plt .subplots ()
94293 ax .violinplot (np .random .randn (100 , 3 ))
0 commit comments