Skip to content

Sanitize orphan subplot logic and 'last trace' deletion #268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 19 commits into from

Conversation

etpinard
Copy link
Contributor

@mdtusz @alexcjohnson

This is a very important PR. So might as well bring in some other folks that should understand its implications ( @cldougl @cpsievert @chriddyp @bpostlethwaite ).

What this PR fixes

  • Plotly.deleteTraces when applied to the last trace of a subplot now properly deletes that last trace but keeps its axes / scene / geo on the plot (see Plotly.deleteTraces bug #73 for more info)
  • Plotly.deleteTraces now works on heatmap and contour traces (yep, that didn't work before)
  • Plotly.deleteTraces now properly delete color bars associated with deleted traces
  • Plotly.deleteTraces now works on geo traces

What concepts does this PR put forward

(1) plots can have orphan subplots (i.e. subplot without data attached to them), e.g.:

data: [],
layout: {
  xaxis: {},
  yaxis: {}
}

renders a blank set of axes (internally it is now considered _hasCartesian true)

and

data: [],
layout: {
  scene: {}
}

renders a blank scenes (internally _hasGL3D true) and similarly for { geo: {} } layouts.

(2) trace-referenced subplots generate layout subplots for free e.g.

data: [{ type: 'scatter3d', scene: 'scene' }],
layout: {}

add a scene the user layout behind scenes in the defaults step. Similar for cartesian and geo plots.

(3) Deleting the last trace of a subplots deletes the trace in the user data, but keeps the its subplot in the user layout. Combine with concept (1) above, deleting the last trace of a subplot via Plotly.deleteTraces keep the subplot on the plot.

(4) To delete a subplot, one must first delete all its traces and then delete the subplot via relayout e.g. Plotly.relayout('graph', 'scene', null);

(5) Plotting

data: [],
layout: {}

draws a blank plot as previously.

- generalizes the 'find subplot' step where we look
  for subplots in user data and (now also) in layout before
  filling in the defaults into their 'full' counterparts.
- Before, gl3d and geo defaults where also considering data-referenced
  subplots. Now, orphan (or data-less) subplot are valid.
- make sure that graph with only orphan cartesian are
  considered 'cartesian'
- make sure that axes associated with GL2D traces aren't
  considered 'cartesian'
- by making layout with { xaxis: {}, yaxis: {} } a 'hasCartesian'
  plot, deleting the last cartesian trace retains the axes.
- data-only subplot get a corresponding layout subplot 'for free'
- plots with orphan scenes/geos are considered HasGL3D and
  hasGeo respectively.
- by keeping track of traces in each geo from call to call,
- make sure that its module's plot method is called
  so that it is properly removed from the DOM.
- ensures the deleted geos are only properly removed from the DOM.
- ensures the deleted contour, heatmap and colorbar
  are properly removed
@etpinard etpinard added this to the subplotly.js milestone Feb 19, 2016
@etpinard etpinard mentioned this pull request Feb 19, 2016
@etpinard
Copy link
Contributor Author

TODO:

  • Plotly.relayout('graph', { 'xaxis': null, 'yaxis': null }); is falling. Fixed in a2f2ce8
  • Figure out what to do with axis-referenced annotations and shapes. Should they generate cartesian axes for free? Answer: not at the moment. The xref and yref default logic is such that values linked to non-existent axes are coerced to 'paper', which is sensible default.
  • deleting pie traces does not work. Fixed in 245f8a4

// if gl3d or geo is present on graph. This is retain backward compatible.
//
// TODO drop this in version 2.0
var ignoreOrphan = (layoutOut._hasGL3D || layoutOut._hasGeo);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a very important point, to preserve backward compatibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

... because some old gl3d and geo plots have layout.xaxis and/or layout.yaxis in them, which would generate blank cartesian axes without the line above.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is presumably just an issue just for plots made and stored in the workspace right? Can we confirm that we're not generating this situation anymore? (Or perhaps we are until we properly handle type changes in restyle) And then how hard would it be to write a command to find and clear these orphans out of stored plots, so we never have to put this logic in? Otherwise I don't see how we're going to be able to take it out later.

I suppose we can put this logic in now, deploy it, then at some later time make and run a cleaning command, then come in after THAT and delete this logic... I just worry that if we don't do it now it will become harder later, and therefore will never happen, and this quirk will live forever.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

an we confirm that we're not generating this situation anymore?

scatter3d traces are still generated with a layout.xaxis and layout.yaxis in the workspace.

expect(countGeos()).toBe(1);
expect(countColorBars()).toBe(1);

Plotly.deleteTraces(gd, [0]).then(function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we flatten these out to make use of chaining on the promises?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah you got me. I was just being lazy.

Copy link
Contributor

Choose a reason for hiding this comment

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

🍻

Plotly.relayout(gd, {xaxis: null, yaxis: null}).then(function() {
expect(countScatterTraces()).toEqual(1);
expect(countSubplots()).toEqual(1);
expect(gd.layout.xaxis.range).toBeCloseToArray([-4.79980, 74.48580], 4);
Copy link
Contributor

Choose a reason for hiding this comment

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

So handy. 🎉

@mdtusz
Copy link
Contributor

mdtusz commented Feb 23, 2016

💃

@alexcjohnson
Copy link
Collaborator

Just chatting with @etpinard - one more thing popped up: With the orphan logic, if you change trace type and thereby change subplot type (say heatmap -> surface and back again) you'll keep both subplots. @etpinard points out that restyle is the right place to delete the extra subplot when this happens.

  • remove the extra subplot when restyling empties one out

@etpinard
Copy link
Contributor Author

Closed in favour of #289.

@etpinard etpinard closed this Feb 26, 2016
@etpinard etpinard deleted the delete-last-trace branch February 26, 2016 19:34
@etpinard etpinard mentioned this pull request Feb 29, 2016
5 tasks
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