Skip to content

Commit 10da229

Browse files
authored
fix: secondary y axis match and visibility when faceted (#32)
* Update README.md * fix: secondary yaxis placement * fix: secondary yaxis matching
1 parent 6a236af commit 10da229

File tree

3 files changed

+25
-11
lines changed

3 files changed

+25
-11
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ Planned additions (contributions welcome):
5656
- WebGL rendering option for `line()` and `scatter()` (large datasets)
5757

5858
**Figure utilities** (facet/animation-aware)
59-
- `add_trace()` — add a trace to base figure and all animation frames
6059
- `fill_between()` — fill area between two traces (uncertainty bands)
6160
- `sync_axes()` — consistent axis ranges across facets and animation frames
6261
- `add_secondary_x()` — secondary x-axis (like `add_secondary_y()`)

tests/test_figures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,8 +505,8 @@ def test_facets_with_custom_title(self) -> None:
505505

506506
combined = add_secondary_y(base, secondary, secondary_y_title="Custom Title")
507507

508-
# Title should be on the first secondary axis
509-
assert combined.layout.yaxis4.title.text == "Custom Title"
508+
# Title should be on the rightmost secondary axis (yaxis6 for 3 facets)
509+
assert combined.layout.yaxis6.title.text == "Custom Title"
510510

511511

512512
class TestAddSecondaryYAnimation:

xarray_plotly/figures.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,13 @@ def add_secondary_y(
308308
# Build mapping from primary y-axes to secondary y-axes
309309
y_mapping = _build_secondary_y_mapping(base_axes)
310310

311+
# Build x-y correspondence from base_axes (which x-axis pairs with which y-axis)
312+
x_for_y = {yaxis: xaxis for xaxis, yaxis in base_axes}
313+
314+
# Find the rightmost x-axis (highest number) to determine which secondary axis shows ticks
315+
rightmost_x = max(x_for_y.values(), key=lambda x: int(x[1:]) if x != "x" else 1)
316+
rightmost_primary_y = next(y for y, x in x_for_y.items() if x == rightmost_x)
317+
311318
# Create new figure with base's layout
312319
combined = go.Figure(layout=copy.deepcopy(base.layout))
313320

@@ -322,24 +329,32 @@ def add_secondary_y(
322329
trace_copy.yaxis = y_mapping[original_yaxis]
323330
combined.add_trace(trace_copy)
324331

332+
# Get the rightmost secondary y-axis name for linking
333+
rightmost_secondary_y = y_mapping[rightmost_primary_y]
334+
325335
# Configure secondary y-axes
326336
for primary_yaxis, secondary_yaxis in y_mapping.items():
327-
# Get title - only set on first secondary axis or use provided title
337+
is_rightmost = primary_yaxis == rightmost_primary_y
338+
339+
# Get title - only set on rightmost secondary axis
328340
title = None
329-
if secondary_y_title is not None:
330-
# Only set title on the first secondary axis to avoid repetition
331-
if primary_yaxis == "y":
341+
if is_rightmost:
342+
if secondary_y_title is not None:
332343
title = secondary_y_title
333-
elif primary_yaxis == "y" and secondary.layout.yaxis and secondary.layout.yaxis.title:
334-
# Try to get from secondary's layout
335-
title = secondary.layout.yaxis.title.text
344+
elif secondary.layout.yaxis and secondary.layout.yaxis.title:
345+
title = secondary.layout.yaxis.title.text
336346

337347
# Configure the secondary axis
348+
# Anchor to the corresponding x-axis so it appears on the right side of its subplot
338349
axis_config = {
339350
"title": title,
340351
"overlaying": primary_yaxis,
341352
"side": "right",
342-
"anchor": "free" if primary_yaxis != "y" else None,
353+
"anchor": x_for_y[primary_yaxis],
354+
# Only show ticks on the rightmost secondary axis
355+
"showticklabels": is_rightmost,
356+
# Link non-rightmost axes to the rightmost for consistent scaling
357+
"matches": None if is_rightmost else rightmost_secondary_y,
343358
}
344359
# Remove None values
345360
axis_config = {k: v for k, v in axis_config.items() if v is not None}

0 commit comments

Comments
 (0)