Skip to content

Fix Plotly figures in initially hidden tabs not filling the tab width - #14940

Open
devmcp wants to merge 2 commits into
quarto-dev:mainfrom
devmcp:bugfix/plotly-tabset-resize
Open

devmcp wants to merge 2 commits into
quarto-dev:mainfrom
devmcp:bugfix/plotly-tabset-resize

Conversation

@devmcp

@devmcp devmcp commented Sep 23, 2026

Copy link
Copy Markdown

Description

This PR addresses the width part of #4705: Plotly figures in a tab that is hidden on page load don't fill the tab when it's shown. The same problem was reported in #14939, which was closed as a duplicate of #4705.

It doesn't fix the table problem that #4705 is titled for, where text cells in a fixed-size go.Table stay blank in a hidden tab, so #4705 should stay open. In my testing, re-rendering fixed-size figures with Plotly.react(figure, figure.data, figure.layout) when their tab is shown fills in the blank cells, but I've left that out to keep this PR focused.

Plotly figures in a tab that is hidden on page load are drawn while their container has no size, so they fall back to Plotly's default size of 700×450px. Nothing redraws them when the tab is shown: quarto.js only dispatches slideenter (for htmlwidgets).

This PR changes src/resources/formats/html/quarto.js so that when a tab is shown (Bootstrap's shown.bs.tab or tabby's tabby event):

  • Plotly figures in the newly shown tab are redrawn straight away with Plotly.relayout(figure, { autosize: true }), so they're already the right size in the first frame the browser paints. Figures with a fixed width and height are skipped.
  • A window resize event is also dispatched, for other libraries that resize when the window does.

The Plotly-specific redraw is needed because Plotly's own resize handler waits 100ms before redrawing. Relying on the resize event alone shows the figure at the wrong size for a few frames before it jumps to the right size. quarto.js already has library-specific handling here (slideenter for htmlwidgets, jQuery events for Shiny), so I've followed that pattern. If Plotly isn't available as a global, figures still reach the right size through the resize event, just slightly later.

Notes:

  • Figures shown with config={"responsive": False} are now also sized to fit the tab when it's shown, as they would have been if drawn while visible. They still don't follow later window resizes.
  • R plotly figures were already redrawn without a flash through htmlwidgets' slideenter handling. I checked that they behave the same with this change (R plotly 4.12.1).
  • Reveal.js has its own fireSlideEnter() in src/resources/formats/revealjs/plugins/support/support.js. I've left it unchanged to keep this PR focused.

Tests

  • New fixture tests/docs/playwright/html/tabsets/plotly-tabsets.qmd, plus a Playwright test in html-tabsets.spec.ts. The test clicks the initially hidden tab and measures the Plotly figure in the first animation frame after the click, before the browser paints, checking that it's as wide as its container.
  • Without this PR the test fails: the figure is 399px narrower than its container (700px vs 1099px). It also fails with only the resize event, because of the delayed redraw. With this PR it passes in Chromium, Firefox and WebKit (10 repeats each with --workers=1).
  • I checked the tabby path (used without Bootstrap, e.g. theme: none) manually.
  • I also ran the html-search-tabsets, html-themes and dashboard-hash-navigation specs locally in all three browsers, and they pass. Two dashboard tests fail intermittently on my machine when run in parallel, at the same rate with and without this change.

Checklist

I have (if applicable):

  • referenced the GitHub issue this PR closes
  • updated the appropriate changelog in the PR
  • ensured the present test suite passes
    • I ran the related Playwright specs locally (see above) and am relying on CI for the full suite.
  • added new tests
  • created a separate documentation PR in Quarto's website repo and linked it to this PR
    • Not needed: this is a bug fix with no documented behaviour change.
AI-assisted PR
  • AI tool used: Claude Code
  • Codebase grounding: local clone
  • Human review: I have reviewed, tested, and verified the AI-generated content before submitting.

Plotly resizes responsive figures from a window resize listener, but
when a tab was shown quarto.js only dispatched slideenter, so figures
drawn while their tab was hidden kept Plotly's default 700px width.
Also dispatch a window resize event from fireSlideEnter().

Closes quarto-dev#14939

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@posit-snyk-bot

posit-snyk-bot commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@devmcp
devmcp marked this pull request as draft September 23, 2026 16:38
Plotly waits 100ms before handling window resize, so figures drawn
while their tab was hidden were briefly shown at Plotly's default
size before resizing. Redraw them with Plotly.relayout as soon as the
tab is shown, and make the test check the first painted frame.

Refs quarto-dev#14939

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants