Skip to content

Use plot title as default filename for "Download plot" button#7828

Merged
emilykl merged 13 commits into
v4.0from
5124-feature
Jun 17, 2026
Merged

Use plot title as default filename for "Download plot" button#7828
emilykl merged 13 commits into
v4.0from
5124-feature

Conversation

@emilykl

@emilykl emilykl commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Addresses #5124

  • Use layout.title.text as default download filename when neither config.toImageButtonOptions.filename or gd.fn are defined.
    • "slugify" title by lowercasing, removing HTML tags, removing most special characters, and replacing whitespace with hyphens
    • Ignore MathJax titles entirely, to avoid generating horrible filenames like ihbarfracdpsidt-v-frac-hbar22mnabla2p.png (fall back to subtitle if title is MathJax)
    • Add helper function to get title and subtitle from _fullLayout (so that template values are respected) while ignoring the "Click to add title" placeholder
  • Change fallback filename to plot-image rather than newplot
  • Change notifier wording for image download slightly, to better match button tooltip (remove references to "snapshot")
  • Add/modify tests for the above

Steps for testing

  1. Pull this branch and run devtools (npm run start)
  2. Load any plot in the devtools
  3. Click the "Download plot as PNG" button (camera icon) and verify that the downloaded plot filename corresponds to the plot title
  4. Repeat for several more plots, including the below:
    a. scatter_marker_line_dash (normal example, filename should match)
    b. bar-like_traces_tozero (title is defined in template, filename should match)
    c. automargin-small-width (no title, should default to plot-image.png)
    d. mathjax (MathJax title, should default to plot-image.png)

@emilykl emilykl requested a review from camdecoster June 8, 2026 21:15
@archmoj

archmoj commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Nice!
What's the expected behavior when title.text is set to a dot?

Plotly.newPlot(gd, [{y: [1, 2]}], {title: {text: "."}});

Right now it returns png.png!

Comment thread src/snapshot/download.js
// so ignore the title entirely if it contains LaTeX markup
if (!svgTextUtils.matchTex(plotTitle)) {
potentialFilename = Lib.slugify(plotTitle, 40);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You may also consider adding the subtitle.text if present.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm open to that, but I'm having trouble thinking of a real-world example where adding the subtitle would result in a better filename.

I suppose maybe in the case where you have multiple plots with the same title but different subtitles, that could differentiate the filenames.

Do you have any particular examples in mind?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here is a common example where subtitle could be used to provide the time stamp of the graph.

Plotly.newPlot(gd, [{y: [1, 2]}], {title: {text: "Hourly Temp. (°C) Forecast - Montréal", subtitle: {text: "9 June 2026"}}});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's a positive example, if we add the subtitle we would get hourly-temp-c-forecast-montreal-9-june-2.png instead of hourly-temp-c-forecast-montreal.png. Though I also think the filename with just the title is fine.

However I also see cases where adding the subtitle gives a worse result, for example this plot in the docs where we would end up with life-expectancy-life-expectancy-by-europ.png rather than life-expectancy.png.

I've updated the logic to fall back to the subtitle in cases where the title is missing or consists of MathJax, which I think might be the best of both worlds.

@emilykl

emilykl commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Nice! What's the expected behavior when title.text is set to a dot?

Plotly.newPlot(gd, [{y: [1, 2]}], {title: {text: "."}});

Right now it returns png.png!

Good catch!! That's a wild edge case, I guess I would have expected it to return ..png, which also isn't a good outcome.

I suppose . should either be treated the same as whitespace characters, or simply removed, I'll make that change.

@emilykl

emilykl commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Nice! What's the expected behavior when title.text is set to a dot?

Plotly.newPlot(gd, [{y: [1, 2]}], {title: {text: "."}});

Right now it returns png.png!

Good catch!! That's a wild edge case, I guess I would have expected it to return ..png, which also isn't a good outcome.

I suppose . should either be treated the same as whitespace characters, or simply removed, I'll make that change.

@archmoj Updated — I added . to the list of forbidden characters, so it will be removed, and therefore a plot with the title . results in a download named plot-image.png.

Comment thread src/lib/slugify.js Outdated
Comment thread src/lib/slugify.js Outdated
.replace(UNICODE_REPLACEMENT_CHAR_REGEX, '') // Drop Unicode replacement chars left by previous step
.replace(HTML_TAGS_REGEX, ' ') // Remove < > tags, such as <br> (replace with space)
.replace(FORBIDDEN_CHARS_REGEX, '') // Remove forbidden filename characters
.toLowerCase() // Lowercase everything

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure about lowering the case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm going to keep the lowercasing for now, since it's consistent with the lowercasing in newplot.png and also matches the way most URLs are lowercased. However open to changing it in the future depending on feedback.

Comment thread src/lib/svg_text_utils.js Outdated
Comment thread src/snapshot/download.js Outdated
Comment thread lib/locales/cs.js

@camdecoster camdecoster left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good. I made one small suggestion, but feel free to ignore it.

Comment thread src/lib/svg_text_utils.js Outdated
Co-authored-by: Cameron DeCoster <cameron.decoster@gmail.com>
@emilykl emilykl merged commit 7b9c347 into v4.0 Jun 17, 2026
83 checks passed
@emilykl emilykl deleted the 5124-feature branch June 17, 2026 20:08
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.

3 participants