Skip to content

Fix row and column titles in make_subplots when edge cells are spanned - #5793

Open
joaopedroassad wants to merge 2 commits into
plotly:mainfrom
joaopedroassad:fix-subplot-titles-spans
Open

joaopedroassad wants to merge 2 commits into
plotly:mainfrom
joaopedroassad:fix-subplot-titles-spans

Conversation

@joaopedroassad

@joaopedroassad joaopedroassad commented Oct 6, 2026 •

Copy link
Copy Markdown

Link to issue

Closes #5792

Description of change

make_subplots now keeps every row and column title aligned with its own row/column when the edge cell it is anchored to is empty or covered by a subplot with rowspan/colspan. Before, that cell was skipped, which pushed the remaining titles onto the wrong rows/columns and dropped the last one.

Demo

from plotly.subplots import make_subplots

fig = make_subplots(
    rows=2,
    cols=2,
    specs=[[{"colspan": 2}, None], [{}, {}]],
    row_titles=["Row A", "Row B"],
)
print([(a.text, round(a.y, 3)) for a in fig.layout.annotations])

Before:

Screenshot 2026-10-05 at 22 54 10

[('Row A', 0.212)] (Row A next to row 2, Row B missing)

After:

Screenshot 2026-10-05 at 23 26 43

[('Row A', 0.787), ('Row B', 0.212)]

Testing strategy

Added test_row_and_column_titles_with_spanning_subplots, parametrized over three layouts (colspan covering the last cell of a row, rowspan in the last column, and a rowspan with start_cell="bottom-left" that leaves a top-row cell empty). It checks that the title positions match the same grid without spans. It fails on main and passes with this change. The existing subplot tests still pass, and regular grids (padding, uneven widths/heights, shared axes, secondary_y) produce exactly the same annotations as before.

Additional information (optional)

When the cell does hold a subplot that covers exactly that row/column, its own domain is still used, so l/r/t/b padding behaves as it did before. Only empty or spanned cells fall back to the grid cell geometry.

Guidelines

@chrikrah chrikrah left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joaopedroassad Approving: the new test fails on main, and a wider sweep across uneven heights and widths found no title that moves. The one thing left is a CHANGELOG.md conflict with main; the code needs nothing.

$ pytest -q tests/test_core/test_subplots            # at bc7ae12
72 passed, 403 warnings in 1.80s
$ pytest -q tests/test_core/test_subplots            # base 9447f1c
69 passed, 403 warnings in 1.74s
$ cp <base>/plotly/_subplots.py plotly/ && pytest -q tests/test_core/test_subplots -k spanning
3 failed, 69 deselected in 0.51s

non-blocking: the three parametrized layouts all use equal rows and columns. I compared spanned and plain grids for annotation text, x and y on three more layouts (3x3 with both spans, a full-height rowspan, a full-width colspan), crossed with both start_cell values, row_heights=[1, 2, 3] and column_widths=[3, 1, 2]. The branch gives 0 of 24 mismatches, main gives 20 of 24. The legacy row_width order matches too. One uneven case in the test would pin the heights[-1 - r] branch in _title_domain.

# not run: tests/test_optional, the rest of tests/test_core

@joaopedroassad would you add one uneven row_heights case to the parametrization? Happy to send the layouts I used.

@joaopedroassad
joaopedroassad force-pushed the fix-subplot-titles-spans branch from bc7ae12 to 5ed69f2 Compare October 10, 2026 16:06
@joaopedroassad

Copy link
Copy Markdown
Author

Thanks @chrikrah.

I added two uneven cases to the parametrization: row_heights=[3, 1] with column_widths=[1, 2] on the default top-left start, and row_heights=[1, 3] with column_widths=[2, 1] on start_cell="bottom-left".

In make_subplots, top-left is the row_dir < 0 case, so the default-start case is the one that hits heights[-1 - r]. I checked that by swapping that line for heights[r]: the top-left uneven case fails and the other four still pass. On main all five cases fail.

I also rebased onto main to clear the CHANGELOG.md conflict.

@chrikrah chrikrah left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joaopedroassad approving at 5ed69f2. The two uneven cases cover both arms of the heights line in _title_domain: each arm, swapped for the other, fails exactly one of them. The CHANGELOG conflict is gone too.

$ pytest -q tests/test_core/test_subplots              # head 5ed69f2
74 passed, 403 warnings in 1.61s
$ pytest -q tests/test_core/test_subplots              # merge base 3f8a650
69 passed, 403 warnings in 1.59s
$ cp <3f8a650>/plotly/_subplots.py plotly/ && pytest -q tests/test_core/test_subplots -k spanning
5 failed, 69 deselected in 0.71s
# _title_domain edited to: height = heights[r]
$ pytest -q tests/test_core/test_subplots -k spanning
1 failed, 4 passed, 69 deselected in 0.48s       # spanned3, top-left, row_heights=[3, 1]
# _title_domain edited to: height = heights[-1 - r]
$ pytest -q tests/test_core/test_subplots -k spanning
1 failed, 4 passed, 69 deselected in 0.45s       # spanned4, bottom-left, row_heights=[1, 3]
# Python 3.12, pytest 9.1.1
# not run: tests/test_optional, the rest of tests/test_core

@T4rk1n this one is assigned to you. Is it ready for your review?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: make_subplots drops or misplaces row_titles/column_titles when an edge cell is spanned

3 participants