From 47d2df413740270dc0f9ce9f099ef9b77261015d Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Wed, 2 Sep 2026 15:48:28 +0000 Subject: [PATCH 01/10] Convert fill polygons to filled scatter traces --- plotly/matplotlylib/renderer.py | 25 ++++++++++++++++++++++ plotly/matplotlylib/tests/test_renderer.py | 18 ++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py index 0a3c829945..69c781280d 100644 --- a/plotly/matplotlylib/renderer.py +++ b/plotly/matplotlylib/renderer.py @@ -908,6 +908,12 @@ def draw_path(self, **props): elif isinstance(props["mplobj"], mpatches.StepPatch): self.msg += " Drawing a step path\n" self._draw_step_path(props) + elif ( + isinstance(props["mplobj"], mpatches.Polygon) + and props["coordinates"] == "data" + ): + self.msg += " Drawing a filled polygon\n" + self._draw_filled_polygon(props) else: self.msg += " This path isn't a bar, not drawing\n" warnings.warn( @@ -970,6 +976,25 @@ def _draw_step_path(self, props): ) ) + def _draw_filled_polygon(self, props): + """Draw a matplotlib Polygon patch as a filled scatter trace.""" + style = props["style"] + verts = props["data"] + facecolor = _export_color(style["facecolor"]) + edgecolor = _export_color(style["edgecolor"]) + self.plotly_fig.add_trace( + go.Scatter( + x=self._convert_x_dates([v[0] for v in verts]), + y=[v[1] for v in verts], + mode="lines", + line=go.scatter.Line(color=edgecolor, width=style["edgewidth"]), + fill="toself", + fillcolor=facecolor, + xaxis="x{0}".format(self.axis_ct), + yaxis="y{0}".format(self.axis_ct), + ) + ) + def draw_text(self, **props): """Create an annotation dict for a text obj. diff --git a/plotly/matplotlylib/tests/test_renderer.py b/plotly/matplotlylib/tests/test_renderer.py index 90f73bff36..2cc7be3dea 100644 --- a/plotly/matplotlylib/tests/test_renderer.py +++ b/plotly/matplotlylib/tests/test_renderer.py @@ -1150,6 +1150,24 @@ def test_uneven_custom_date_xtickvals_are_converted(): ) +def test_fill_converts(): + """plt.fill polygons convert to filled scatter traces.""" + x = np.linspace(0, 2 * np.pi, 50) + fig, ax = plt.subplots() + ax.fill(x, np.sin(x), "g") + + plotly_fig = tls.mpl_to_plotly(fig) + + assert len(plotly_fig.data) == 1 + trace = plotly_fig.data[0] + assert trace.type == "scatter" + assert trace.fill == "toself" + assert np.allclose(trace.x, x) + assert np.allclose(trace.y, np.sin(x)) + assert trace.fillcolor == "#007F00" + assert trace.line.color == "rgba(0,0,0,0)" + + def test_custom_date_xtickvals_given_as_numbers_are_converted(): """Custom date ticks given as matplotlib date numbers must be converted to date strings.""" From 211c63d41c8f9c1452ea7b9458aeb9f732f7ff07 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Tue, 6 Oct 2026 08:41:32 +0000 Subject: [PATCH 02/10] Support legend labels on filled polygon traces --- plotly/matplotlylib/renderer.py | 9 ++++++++ plotly/matplotlylib/tests/test_renderer.py | 24 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py index 69c781280d..a5cd4e3518 100644 --- a/plotly/matplotlylib/renderer.py +++ b/plotly/matplotlylib/renderer.py @@ -982,8 +982,17 @@ def _draw_filled_polygon(self, props): verts = props["data"] facecolor = _export_color(style["facecolor"]) edgecolor = _export_color(style["edgecolor"]) + mplobj = props.get("mplobj") + label = mplobj.get_label() if mplobj is not None else None + if not label or (isinstance(label, str) and label.startswith("_")): + label = None + showlegend = False + else: + showlegend = None self.plotly_fig.add_trace( go.Scatter( + name=label, + showlegend=showlegend, x=self._convert_x_dates([v[0] for v in verts]), y=[v[1] for v in verts], mode="lines", diff --git a/plotly/matplotlylib/tests/test_renderer.py b/plotly/matplotlylib/tests/test_renderer.py index 2cc7be3dea..1b29b6178f 100644 --- a/plotly/matplotlylib/tests/test_renderer.py +++ b/plotly/matplotlylib/tests/test_renderer.py @@ -1168,6 +1168,30 @@ def test_fill_converts(): assert trace.line.color == "rgba(0,0,0,0)" +def test_fill_with_legend_label(): + """plt.fill polygons with labels export their name and show in legend.""" + fig, ax = plt.subplots() + ax.fill([0, 1, 2], [0, 2, 0], "b", label="Shaded Area") + ax.fill([0, 1, 2], [0, 1, 0], "g") + ax.plot([0, 1, 2], [1, 1, 1], "r-", label="Threshold") + ax.legend() + + plotly_fig = tls.mpl_to_plotly(fig) + + assert plotly_fig.layout.showlegend == True + assert len(plotly_fig.data) == 3 + + labeled_fill = next( + t for t in plotly_fig.data if t.fill == "toself" and t.name == "Shaded Area" + ) + assert labeled_fill.showlegend is not False + + unlabeled_fill = next( + t for t in plotly_fig.data if t.fill == "toself" and t.name is None + ) + assert unlabeled_fill.showlegend is False + + def test_custom_date_xtickvals_given_as_numbers_are_converted(): """Custom date ticks given as matplotlib date numbers must be converted to date strings.""" From adf16d7dd1af7770353304d5b04105ed254f1e33 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Tue, 6 Oct 2026 08:46:11 +0000 Subject: [PATCH 03/10] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32590d344f..b3bf115d85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased + ### Added - Add support for converting matplotlib reference lines (`axhline`, `axvline`, and horizontal/vertical `axline`) as well as axes-coordinate lines into Plotly layout shapes in `mpl_to_plotly`, including support for date axes [[#5719](https://github.com/plotly/plotly.py/pull/5719)], with thanks to @robertoffmoura for the contribution! +- Add support for converting matplotlib fill polygons (`plt.fill`) to filled scatter traces in `mpl_to_plotly`, with thanks to @robertoffmoura for the contribution! ### Fixed - Fix `mpl_to_plotly` tick marker mirroring, axis positioning, and trace visibility for twinned and overlaid subplots (such as `twinx` and `twiny`) [[#5310](https://github.com/plotly/plotly.py/pull/5310)], with thanks to @robertoffmoura for the contribution! From 37b9679baf9e2132ddd71b77178759ff92dba9a1 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Tue, 6 Oct 2026 09:03:23 +0000 Subject: [PATCH 04/10] Update changelog entry with PR number 5795 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3bf115d85..9ab5a5ba03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - Add support for converting matplotlib reference lines (`axhline`, `axvline`, and horizontal/vertical `axline`) as well as axes-coordinate lines into Plotly layout shapes in `mpl_to_plotly`, including support for date axes [[#5719](https://github.com/plotly/plotly.py/pull/5719)], with thanks to @robertoffmoura for the contribution! -- Add support for converting matplotlib fill polygons (`plt.fill`) to filled scatter traces in `mpl_to_plotly`, with thanks to @robertoffmoura for the contribution! +- Add support for converting matplotlib fill polygons (`plt.fill`) to filled scatter traces in `mpl_to_plotly` [[#5795](https://github.com/plotly/plotly.py/pull/5795)], with thanks to @robertoffmoura for the contribution! ### Fixed - Fix `mpl_to_plotly` tick marker mirroring, axis positioning, and trace visibility for twinned and overlaid subplots (such as `twinx` and `twiny`) [[#5310](https://github.com/plotly/plotly.py/pull/5310)], with thanks to @robertoffmoura for the contribution! From c00d54f60fff0f82bf5a2118144dfc40eb34df4f Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Fri, 23 Oct 2026 00:07:33 +0000 Subject: [PATCH 05/10] Read the polygon label from props['mplobj'] directly --- plotly/matplotlylib/renderer.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py index a5cd4e3518..231ae24998 100644 --- a/plotly/matplotlylib/renderer.py +++ b/plotly/matplotlylib/renderer.py @@ -982,8 +982,7 @@ def _draw_filled_polygon(self, props): verts = props["data"] facecolor = _export_color(style["facecolor"]) edgecolor = _export_color(style["edgecolor"]) - mplobj = props.get("mplobj") - label = mplobj.get_label() if mplobj is not None else None + label = props["mplobj"].get_label() if not label or (isinstance(label, str) and label.startswith("_")): label = None showlegend = False From 27631a35847fed522c025901e2b3b5bc3296f2be Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Fri, 23 Oct 2026 00:08:17 +0000 Subject: [PATCH 06/10] Share legend label handling in a _legend_label helper --- plotly/matplotlylib/renderer.py | 37 +++++++++++++++------------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py index 231ae24998..88bb55e673 100644 --- a/plotly/matplotlylib/renderer.py +++ b/plotly/matplotlylib/renderer.py @@ -41,6 +41,20 @@ def _per_path(values, i, default): return values[i % n] if n else default +def _legend_label(label): + """Return the (name, showlegend) pair for a trace from an artist label. + + matplotlib marks artists it leaves out of legends with "_nolegend_" and + auto-generated "_childN" labels. Empty labels and labels starting with + "_" give (None, False), which hides the trace from the plotly legend. + Other labels give (label, None), which keeps plotly's default legend + entry. + """ + if not label or (isinstance(label, str) and label.startswith("_")): + return None, False + return label, None + + def _convert_collection_dash(linestyle): """Convert a matplotlib collection line style to a plotly dash string. @@ -532,14 +546,7 @@ def draw_marked_line(self, **props): ), ) if props["coordinates"] == "data": - label = props["label"] - # matplotlib uses "_nolegend_" and auto-generated "_childN" - # labels for artists that must not appear in a legend - if not label or (isinstance(label, str) and label.startswith("_")): - label = None - showlegend = False - else: - showlegend = None + label, showlegend = _legend_label(props["label"]) marked_line = go.Scatter( mode=mode, name=label, @@ -951,12 +958,7 @@ def _draw_step_path(self, props): if len(x) < 2: self.msg += " Step path has fewer than 2 points, not drawing\n" return - label = props["mplobj"].get_label() - if not label or (isinstance(label, str) and label.startswith("_")): - label = None - showlegend = False - else: - showlegend = None + label, showlegend = _legend_label(props["mplobj"].get_label()) self.plotly_fig.add_trace( go.Scatter( x=x, @@ -982,12 +984,7 @@ def _draw_filled_polygon(self, props): verts = props["data"] facecolor = _export_color(style["facecolor"]) edgecolor = _export_color(style["edgecolor"]) - label = props["mplobj"].get_label() - if not label or (isinstance(label, str) and label.startswith("_")): - label = None - showlegend = False - else: - showlegend = None + label, showlegend = _legend_label(props["mplobj"].get_label()) self.plotly_fig.add_trace( go.Scatter( name=label, From e141300ef183163e222592526b054bd95977eda6 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Fri, 23 Oct 2026 00:08:56 +0000 Subject: [PATCH 07/10] Build filled polygon traces in a shared _add_filled_trace helper --- plotly/matplotlylib/renderer.py | 63 ++++++++++++++++----------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py index 88bb55e673..75ba7503db 100644 --- a/plotly/matplotlylib/renderer.py +++ b/plotly/matplotlylib/renderer.py @@ -857,6 +857,25 @@ def flush(): flush() + def _add_filled_trace( + self, verts, facecolor, edgecolor, linewidth, name=None, showlegend=None + ): + """Add a filled scatter trace outlining `verts` on the current axes.""" + self.plotly_fig.add_trace( + go.Scatter( + name=name, + showlegend=showlegend, + x=self._convert_x_dates([v[0] for v in verts]), + y=[v[1] for v in verts], + mode="lines", + line=go.scatter.Line(color=_export_color(edgecolor), width=linewidth), + fill="toself", + fillcolor=_export_color(facecolor), + xaxis="x{0}".format(self.axis_ct), + yaxis="y{0}".format(self.axis_ct), + ) + ) + def _draw_filled_path_collection(self, props): """Draw a path collection (e.g. violin plot bodies) as filled polygons.""" facecolors = mpltools.convert_rgba_array(props["styles"]["facecolor"]) @@ -864,22 +883,11 @@ def _draw_filled_path_collection(self, props): linewidths = mpltools.convert_linewidth_array(props["styles"]["linewidth"]) for i, (verts, codes) in enumerate(props["paths"]): - facecolor = _per_path(facecolors, i, "rgba(0,0,0,0)") - edgecolor = _per_path(edgecolors, i, "rgba(0,0,0,0)") - linewidth = _per_path(linewidths, i, 0) - self.plotly_fig.add_trace( - go.Scatter( - x=self._convert_x_dates([v[0] for v in verts]), - y=[v[1] for v in verts], - mode="lines", - line=go.scatter.Line( - color=_export_color(edgecolor), width=linewidth - ), - fill="toself", - fillcolor=_export_color(facecolor), - xaxis="x{0}".format(self.axis_ct), - yaxis="y{0}".format(self.axis_ct), - ) + self._add_filled_trace( + verts, + facecolor=_per_path(facecolors, i, "rgba(0,0,0,0)"), + edgecolor=_per_path(edgecolors, i, "rgba(0,0,0,0)"), + linewidth=_per_path(linewidths, i, 0), ) def draw_path(self, **props): @@ -981,23 +989,14 @@ def _draw_step_path(self, props): def _draw_filled_polygon(self, props): """Draw a matplotlib Polygon patch as a filled scatter trace.""" style = props["style"] - verts = props["data"] - facecolor = _export_color(style["facecolor"]) - edgecolor = _export_color(style["edgecolor"]) label, showlegend = _legend_label(props["mplobj"].get_label()) - self.plotly_fig.add_trace( - go.Scatter( - name=label, - showlegend=showlegend, - x=self._convert_x_dates([v[0] for v in verts]), - y=[v[1] for v in verts], - mode="lines", - line=go.scatter.Line(color=edgecolor, width=style["edgewidth"]), - fill="toself", - fillcolor=facecolor, - xaxis="x{0}".format(self.axis_ct), - yaxis="y{0}".format(self.axis_ct), - ) + self._add_filled_trace( + props["data"], + facecolor=style["facecolor"], + edgecolor=style["edgecolor"], + linewidth=style["edgewidth"], + name=label, + showlegend=showlegend, ) def draw_text(self, **props): From 79c0e126a09a7af7a00e0a86fdae501038546060 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Fri, 23 Oct 2026 00:44:05 +0000 Subject: [PATCH 08/10] Use an identity check for showlegend in test_fill_with_legend_label --- plotly/matplotlylib/tests/test_renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plotly/matplotlylib/tests/test_renderer.py b/plotly/matplotlylib/tests/test_renderer.py index 1b29b6178f..10c2646812 100644 --- a/plotly/matplotlylib/tests/test_renderer.py +++ b/plotly/matplotlylib/tests/test_renderer.py @@ -1178,7 +1178,7 @@ def test_fill_with_legend_label(): plotly_fig = tls.mpl_to_plotly(fig) - assert plotly_fig.layout.showlegend == True + assert plotly_fig.layout.showlegend is True assert len(plotly_fig.data) == 3 labeled_fill = next( From c576c8407c897eb571e1b11cb2b27d7d4e231407 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Fri, 23 Oct 2026 00:44:08 +0000 Subject: [PATCH 09/10] Remove extra blank line under Unreleased in changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ab5a5ba03..d85a725998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased - ### Added - Add support for converting matplotlib reference lines (`axhline`, `axvline`, and horizontal/vertical `axline`) as well as axes-coordinate lines into Plotly layout shapes in `mpl_to_plotly`, including support for date axes [[#5719](https://github.com/plotly/plotly.py/pull/5719)], with thanks to @robertoffmoura for the contribution! - Add support for converting matplotlib fill polygons (`plt.fill`) to filled scatter traces in `mpl_to_plotly` [[#5795](https://github.com/plotly/plotly.py/pull/5795)], with thanks to @robertoffmoura for the contribution! From 1a77a0e1c82717a561ae89e6e3a4021579e8a993 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Fri, 23 Oct 2026 01:01:12 +0000 Subject: [PATCH 10/10] Order traces within each axes by matplotlib zorder --- plotly/matplotlylib/mpltools.py | 1 + plotly/matplotlylib/renderer.py | 55 ++++++++++++++++++---- plotly/matplotlylib/tests/test_renderer.py | 23 +++++++++ 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/plotly/matplotlylib/mpltools.py b/plotly/matplotlylib/mpltools.py index 892282418e..b886b85007 100644 --- a/plotly/matplotlylib/mpltools.py +++ b/plotly/matplotlylib/mpltools.py @@ -329,6 +329,7 @@ def get_markerstyle_from_collection(props): # markersize=convert_size_array(props['styles']['size']), # TODO! markersize=convert_size_array(props["mplobj"].get_sizes()), edgecolor=convert_rgba_array(props["styles"]["edgecolor"]), + zorder=props["styles"]["zorder"], ) return markerstyle diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py index 75ba7503db..9f1da88e2f 100644 --- a/plotly/matplotlylib/renderer.py +++ b/plotly/matplotlylib/renderer.py @@ -111,6 +111,29 @@ def __init__(self): self._processing_legend = False self._legend_visible = False self.axes_list = [] + self._trace_zorders = [] + + def _add_trace(self, trace, zorder): + """Add a trace to the figure, recording its matplotlib zorder.""" + self.plotly_fig.add_trace(trace) + self._trace_zorders.append(zorder) + + def _sort_traces_by_zorder(self): + """Order the current axes' traces by matplotlib zorder. + + plotly draws later traces on top and matplotlib draws artists in + ascending zorder, so the traces are stably sorted by zorder. Traces + with equal zorder keep the order they were drawn in. + """ + n = len(self._trace_zorders) + start = len(self.plotly_fig.data) - n + order = sorted(range(n), key=self._trace_zorders.__getitem__) + if order != list(range(n)): + axes_traces = self.plotly_fig.data[start:] + self.plotly_fig.data = self.plotly_fig.data[:start] + tuple( + axes_traces[i] for i in order + ) + self._trace_zorders = [] def _convert_x_dates(self, x): """Convert x values to date strings when the x-axis is a date axis.""" @@ -318,6 +341,7 @@ def close_axes(self, ax): """ self.draw_bars(self.current_bars) + self._sort_traces_by_zorder() self.msg += " Closing axes\n" self.x_is_mpl_date = False @@ -447,7 +471,7 @@ def draw_bar(self, coll): ) # TODO ditto if len(bar["x"]) > 1: self.msg += " Heck yeah, I drew that bar chart\n" - self.plotly_fig.add_trace(bar) + self._add_trace(bar, trace[0]["zorder"]) if bar_gap is not None: self.plotly_fig["layout"]["bargap"] = bar_gap else: @@ -560,7 +584,8 @@ def draw_marked_line(self, **props): ) if self.x_is_mpl_date: marked_line["x"] = self._convert_x_dates(marked_line["x"]) - self.plotly_fig.add_trace(marked_line) + style = props["linestyle"] or props["markerstyle"] + self._add_trace(marked_line, style["zorder"]) self.msg += " Heck yeah, I drew that line\n" elif props["coordinates"] == "axes" and self._processing_legend: # dealing with legend graphical elements @@ -778,7 +803,7 @@ def _draw_line_collection(self, props): def flush(): if current_style is not None and grouped_x: ec, lw, d = current_style - self.plotly_fig.add_trace( + self._add_trace( go.Scatter( x=grouped_x, y=grouped_y, @@ -791,7 +816,8 @@ def flush(): ), xaxis="x{0}".format(self.axis_ct), yaxis="y{0}".format(self.axis_ct), - ) + ), + props["styles"]["zorder"], ) for i, (verts, codes) in enumerate(props["paths"]): @@ -858,10 +884,17 @@ def flush(): flush() def _add_filled_trace( - self, verts, facecolor, edgecolor, linewidth, name=None, showlegend=None + self, + verts, + facecolor, + edgecolor, + linewidth, + zorder, + name=None, + showlegend=None, ): """Add a filled scatter trace outlining `verts` on the current axes.""" - self.plotly_fig.add_trace( + self._add_trace( go.Scatter( name=name, showlegend=showlegend, @@ -873,7 +906,8 @@ def _add_filled_trace( fillcolor=_export_color(facecolor), xaxis="x{0}".format(self.axis_ct), yaxis="y{0}".format(self.axis_ct), - ) + ), + zorder, ) def _draw_filled_path_collection(self, props): @@ -888,6 +922,7 @@ def _draw_filled_path_collection(self, props): facecolor=_per_path(facecolors, i, "rgba(0,0,0,0)"), edgecolor=_per_path(edgecolors, i, "rgba(0,0,0,0)"), linewidth=_per_path(linewidths, i, 0), + zorder=props["styles"]["zorder"], ) def draw_path(self, **props): @@ -967,7 +1002,7 @@ def _draw_step_path(self, props): self.msg += " Step path has fewer than 2 points, not drawing\n" return label, showlegend = _legend_label(props["mplobj"].get_label()) - self.plotly_fig.add_trace( + self._add_trace( go.Scatter( x=x, y=y, @@ -983,7 +1018,8 @@ def _draw_step_path(self, props): fillcolor=_export_color(style["facecolor"]), xaxis="x{0}".format(self.axis_ct), yaxis="y{0}".format(self.axis_ct), - ) + ), + style["zorder"], ) def _draw_filled_polygon(self, props): @@ -995,6 +1031,7 @@ def _draw_filled_polygon(self, props): facecolor=style["facecolor"], edgecolor=style["edgecolor"], linewidth=style["edgewidth"], + zorder=style["zorder"], name=label, showlegend=showlegend, ) diff --git a/plotly/matplotlylib/tests/test_renderer.py b/plotly/matplotlylib/tests/test_renderer.py index 10c2646812..3b60d852f8 100644 --- a/plotly/matplotlylib/tests/test_renderer.py +++ b/plotly/matplotlylib/tests/test_renderer.py @@ -1192,6 +1192,29 @@ def test_fill_with_legend_label(): assert unlabeled_fill.showlegend is False +def test_fill_stacks_below_line_by_zorder(): + """A fill (zorder 1) exports before a line (zorder 2), so plotly draws + the line on top as matplotlib does.""" + fig, ax = plt.subplots() + ax.fill([0, 1, 1], [0, 0, 1], "b") + ax.plot([0, 1], [0.5, 0.5], "r") + + plotly_fig = tls.mpl_to_plotly(fig) + + assert [t.fill for t in plotly_fig.data] == ["toself", None] + + +def test_fill_with_higher_zorder_stacks_above_line(): + """A fill given a higher zorder than a line exports after the line.""" + fig, ax = plt.subplots() + ax.fill([0, 1, 1], [0, 0, 1], "b", zorder=3) + ax.plot([0, 1], [0.5, 0.5], "r") + + plotly_fig = tls.mpl_to_plotly(fig) + + assert [t.fill for t in plotly_fig.data] == [None, "toself"] + + def test_custom_date_xtickvals_given_as_numbers_are_converted(): """Custom date ticks given as matplotlib date numbers must be converted to date strings."""