Skip to content
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

Array edits #1403

Merged
merged 18 commits into from
Feb 24, 2017
Merged

Array edits #1403

merged 18 commits into from
Feb 24, 2017

Conversation

alexcjohnson
Copy link
Collaborator

Standardizes our editing behavior for the growing list of container arrays - ie anything in the figure that is an array of objects, like annotations, shapes, sliders, updatemenus - and gets that behavior out of the individual components and collects it in the update machinery. Also ensures that multiple updates within one relayout call will behave in well-defined ways.

The behavior (which is mostly how annotations and shapes worked before this but it's extended to be more complete and robust) is:

  • If you set an entire element (eg Plotly.relayout(gd, {'annotations[2]': {...}})) it always inserts a new element into the array at that position.
  • You can delete an element by setting it to null Plotly.relayout(gd, {'annotations[2]': null})
  • You can edit attributes of existing elements Plotly.relayout(gd, {'annotations[2].text':'boo'}) but you cannot create new elements by setting attributes of nonexistent elements.
  • Order of operations is designed so any arbitrary combination of edits is possible, and will be reversible.
    • First we apply the insertions and edits, in order of ascending indices
    • Then we apply the deletions, in order of decreasing indices
    • it's not allowed to combine inserting/edit/deletion of the same index. The edit will be dropped and a warning logged.

In addition:

  • I removed any automatic conversion of positions when changing the reference axes of annotations. Previously we had plotly.js try to keep the pixel position of the annotation fixed when you changed the reference axis. This more or less worked (although a bug with it https://github.com/plotly/streambed/issues/8901 was the original impetus for me to look at this) but it became clear when considering multiple simultaneous edits that there were going to be an incredible number of edge cases that would make this unmanageable, and nearly impossible to divine what the user really wants. Anyway this is really only going to be an issue within the plot.ly workspace, and in there the set of possible individual actions is much smaller (you change axis reference by itself, with no simultaneous changes), so there will be a companion PR to this one over there that we'll need to merge along with the next plotly.js version update.
  • But we do still convert positions for log/linear axis changes. This only applies to annotations and images (but we didn't do it at all for images previously) since shapes use data coordinates so need no conversion to log. I moved this out of the Annotations.draw method and instead drive it from within _relayout.
  • Added a new layout attributes key, _arrayAttrRegexps, to support variably named container arrays. Right now the examples of this are mapbox2.layers and updatemenus[2].buttons.
  • getComponentMethod now returns Lib.noop even when the component itself isn't found. I did this so I could look for draw and drawOne methods based on the array name, but they don't always match, then we fall back on replotting, which is what we want to do anyway.

Three things to keep in mind for later:

  • I did not extend this to restyle (eg the transforms array, but I guess more are coming eg with parcoords) but we should do this too.
  • We should convert shapes and annotations to a more d3-idiomatic construction and get rid of drawOne entirely
  • The log/linear conversion stuff can all disappear too in V2.0 when we change annotations and images to data coordinates rather than linearized coordinates.

supported in all relayout edits, not yet in restyle edits
start on editComponentArray

more baby steps

more partial...

finish up refactor - but still need to test edge cases of linear -> log

delete shapes from all layers in case index changed

support editContainerArray for all layout container arrays
@rreusser
Copy link
Contributor

Unless you've figured out further details, debugging the command issue…

* ie containing objects like annotations, buttons, etc
*/
var DOMAIN_RANGE = /(^|.)(domain|range)$/;
function isDeletable(val, propStr) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this work for domain.x and domain.y ?

Copy link
Contributor

Choose a reason for hiding this comment

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

And more generally, shouldn't all info_array attributes (e.g. updemenues/sliders args, and geo.projection.parallels) be deletable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, good point. I was trying to match the previous logic and extend it gently but honestly I can't recall anymore why we had that logic - why does an empty data array need to stick around while other empty arrays can be removed entirely? Perhaps just to keep things simple for streaming? Does that point to a more robust condition on what should be deletable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Alright, letting every array be deletable and running the full test suite was fairly enlightening - There are a lot of things that fail or error out, some of which could be addressed but fundamentally for data arrays there really is a difference between [] (which means there is data of that kind, it's just presently empty) and undefined (which means the data/attribute should get some default value filled in).

As far as I can tell, for info_array attributes there is no difference between [] and undefined though. So unless we want to try and build in here some way to hunt through the schema (which sounds super painful to me, and potentially not even well-defined given that nestedProperty can, on occasion, be called on a root object that's neither a complete trace or layout object but some portion of it) I would argue for some simple tests that might frequently describe an info array as falsely not deletable but will never describe a real data array as deletable.

Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I can tell, for info_array attributes there is no difference between [] and undefined though

That's correct.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

expanded the safe-to-delete list to include all info_array attributes in 1af058a

@rreusser this also required making args optional for sliders and updatemenus (fallback to [] in command.js) since an empty array will now get pruned. Not sure there will be a real use case for this (what method could one call with no args?) but this case pops up in the tests anyhow 🏆 and it does seem more plotly-esque to not require empty parameters.

Copy link
Contributor

@rreusser rreusser Feb 23, 2017

Choose a reason for hiding this comment

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

The only use case I'm aware of is animating an empty list of frames [] which (serendipitously) functions as a pause button. I've had a bit of trouble with this behavior, so we documented pretty clearly in the web docs that you must use [null] instead of [] to prevent it from getting filtered and replaced with a bare null.

That is, in particular, it's when you create an updatemenus button with a command that pauses. It filters updatemenus[0].buttons[0].args = [[], {...}] and replaces it with [null, {...}] in that case.

// even if we don't have anything left in aobj,
// something may have happened within relayout that we
// need to wait for
var seq = [Plots.previousPromises];
Copy link
Contributor

Choose a reason for hiding this comment

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

Good eyes 🔬

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good tests 🎉

@@ -49,6 +49,7 @@ var buttonsAttrs = {

module.exports = {
_isLinkedToArray: 'updatemenu',
_arrayAttrRegexps: [/^updatemenus\[(0|[1-9][0-9]+)\]\.buttons/],
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm ok with adding _arrayAttrRegexps in this PR (there's enough new logic it in already 😫 ), but we could probably generate it at runtime down the road in the registry here?

Isn't _arrayAttrRegexps simply,

allIsLinkedToArrayAttributes.map((attr) => {
  return module.name + '\[[(0|[1-9][0-9]+)\]\.' + attr;
});

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

that wouldn't work for the other instance of this, /^mapbox([2-9]|[1-9][0-9]+)?\.layers/

Copy link
Contributor

Choose a reason for hiding this comment

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

Just wondering why you chose to attach _arrayAttrRegexps to the attribute object?

Alternatively, we could set in the module object here - and not having to skip it in the plot schema logic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, we could... I just put it there because that's where _isLinkedToArray lives and I thought it would be good to keep them together. I don't have a strong opinion either way.

Copy link
Contributor

Choose a reason for hiding this comment

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

I just put it there because that's where _isLinkedToArray lives

Very good point. Let's keep it that way 👍

@rreusser
Copy link
Contributor

rreusser commented Feb 21, 2017

Verdict on the sliders testing problem: private (underscored) properties on the container objects are not currently getting transferred over, so it was losing the cleanup callback and just attaching more and more event handlers. Solution is to relink private keys on container objects when appropriate.

for(i = 0; i < len; i++) {
Lib.relinkPrivateKeys(contOut[i], previousContOut[i]);
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@rreusser here's the fix for the command test. However if you add or delete an object, the private key will get reattached to the wrong item, or lost. I'm not sure what to do about this... I could splice the fullLayout array in sync with the layout array, which would keep the private key with the right index, but I don't know what to do when I delete an element that has private keys. Most of the time it doesn't matter, but in the case of ._remove I guess it would.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks! Hmm… scrambling at the moment for a call this afternoon, but let me think about this and process a bit.

Copy link
Contributor

Choose a reason for hiding this comment

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

@rreusser can you confirm that is working as it should?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

70c882b keeps the private keys in sync to the degree that it can - which is unless you delete elements from the array. Then they get lost. @rreusser as this isn't new behavior are you OK with it as it stands now?

Copy link
Contributor

Choose a reason for hiding this comment

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

My benchmark for success is just whether it passes the component test that was failing due to the ._remove property getting lost. It was originally just called .remove which would still get lost, so I'll check and submit that fix as a separate PR as needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Wait, no, I must be mistaken. It passes the test in its current form so the changes must have fixed it. Again, that was my only metric for this, so I'm satisfied although I wouldn't have expected fixing private keys to fix the problem. Hmmm.

Registry.getComponentMethod(dataReferencedComponents[i], 'supplyLayoutDefaults')(
gd.layout, fullLayout, fullData);
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I suppose I could list these components somehow in the registry... worth it or overkill?

BTW you'll see I added category-name coordinates to image and shape test images, annotations didn't need this because 16.json already has it, and that's the failing test that prompted this fix.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's leave it like that for now. We could always add that list to the registry down the road if it comes up somewhere else. 👍

// Simple helper functions
// none of these need any external deps

module.exports = function noop() {};
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought this was about the shortest possible module one could make... but I suppose you could do:

exports.a = 0;

Kind of silly... but needed to de-circularize, short of refactoring Lib (see #236 )

Copy link
Contributor

Choose a reason for hiding this comment

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

(resisting temptation suggest a noop factory approach)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually it's important that noop is a singleton so you can use === noop particularly on the results of Registry.getComponentMethod to see if it was found.


if(circularDeps.length > MAX_ALLOWED_CIRCULAR_DEPS) {
console.log(circularDeps.join('\n'));
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just to see where I had made circular refs when this test was failing. So now if you want to see them all just set MAX_ALLOWED_CIRCULAR_DEPS = 0 temporarily and run this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nice touch 🎆

@@ -2092,9 +2092,6 @@ function _relayout(gd, aobj) {
var oldWidth = gd._fullLayout.width,
oldHeight = gd._fullLayout.height;

// coerce the updated layout
Plots.supplyDefaults(gd);
Copy link
Contributor

@etpinard etpinard Feb 22, 2017

Choose a reason for hiding this comment

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

Really? This doesn't break the autosize teats? I thought I tried removing it at some point a few weeks back.

Oh well. One less supply defaults loop ia great news 🐎

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, this is ridiculously complicated... actually tracing through it I found a very specific new bug, though not sure yet if it's exactly related. But I think we can sort it out without reinstating that redundant supplyDefaults:

All of the pathways after _relayout in the relayout, and update wrappers include full supplyDefaults - with the exception of relayout when the edits object aobj has had everything stripped out of it and executed directly within _relayout - and THAT can happen if all keys are handled by editContainerArray, which is OK because inside editContainerArray we call supplyDefaults for that particular component array. This won't work for the regex arrays, because the array name doesn't match a component, but then we don't find a draw method either so it reverts to a full replot.

So I think the only potential pitfall of this is if a change inside the array affects something in the layout outside the array. One case I thought might fit this bill is if you provide a new axis id in a component object (new or edited) - but actually out now as previously we don't generate new axes just from component references, so:

Plotly.relayout(gd,{'annotations[1]':{text:'boo',x:-1,y:4, xref:'x2', yref:'y2'}})

on a plot with only xaxis and yaxis will get drawn on the existing axes... but the bug is that in this case the axes don't get correctly autoranged until you redraw the plot. It's ONLY this case, autorange works if your axis refs are valid.

So, are there any cases I'm not thinking of where changes inside one of these arrays affect _fullLayout outside of the array?

Copy link
Contributor

Choose a reason for hiding this comment

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

So, are there any cases I'm not thinking of where changes inside one of these arrays affect _fullLayout outside of the array?

I can't think of any other cases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fixed that bad-reference bug in 9f89dd6 - it was unrelated to removing this supplyDefaults, which I still believe is OK to do.

@@ -251,7 +251,7 @@ Plotly.plot = function(gd, data, layout, config) {
// calc and autorange for errorbars
ErrorBars.calc(gd);

// TODO: autosize extra for text markers
// TODO: autosize extra for text markers and images
Copy link
Contributor

Choose a reason for hiding this comment

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

There's actually an issue for that one #1111

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ah thanks - comment-linked in 46962af

var itemStr = item.toString(),
i;
for(i = 0; i < array.length; i++) {
if(array[i] instanceof RegExp && array[i].toString() === itemStr) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

* - delete b and edit c:
* {'annotations[1]': null, 'annotations[2].x': newX} (c is edited before b is removed)
*
* You CANNOT combine adding/deleting an item at index `i` with edits to the same index `i`
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 add tests for these CANNOT cases?

Copy link
Contributor

Choose a reason for hiding this comment

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


if(i === '') {
// replacing the entire array: too much going on, force recalc
if(ai.indexOf('updatemenus') === -1) flags.docalc = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

why does 'updatemenus' need special treatment here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

proximate reason: because this test fails after recalc because gd.calcdata is a new object. But what that shows is that we don't actually need a recalc in this case. We probably could exclude from recalc everything that doesn't contribute to autorange (so, I suspect only annotations, shapes, and eventually images need recalc) but without taking the time to investigate all the other cases in detail I erred on the side of caution.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok. That's fine. Maybe we should open (another) Registry list for components that have a calcAutorange method (like annotations and shapes) for this purpose here. But that's not necessary in this PR.

isNotAContainer = containers.indexOf(key) === -1;

return isArray(val) && isNotAContainer;
var INFO_PATTERNS = /(^|.)((domain|range)(\.[xy])?|args|parallels)$/;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, well this is the only part I dislike about this PR. I'm ok with leaving as is for now, but should think about ways to 🔪 this down the road.

Solution 1: simply don't delete info_array attributes, like data_array

Solution 2: use the plot schema to look up all info_array attributes. This doesn't have to be done inside nested property where objects are set w/o a reference to their parent object (if any). Maybe this could be done only during restyle, relayout and update.

Solution 3: something else

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

added even more complexity c6378b1 after discussion with @rreusser but opened #1410 to discuss simplification later on.

@etpinard etpinard added this to the 1.24.0 milestone Feb 23, 2017
@@ -375,6 +375,30 @@ describe('Test lib.js:', function() {
expect(obj).toEqual({a: [undefined, {}]});
});

it('does not prune inside `args` arrays', function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nicely done. Thanks!

@etpinard
Copy link
Contributor

💃 for me! Nice way to make array container a little more solid!

@alexcjohnson can you also open up a type: maintenance issue about making trace data arrays use the same logic?

@@ -484,3 +484,19 @@ exports.manageArrayContainers = function(np, newVal, undoit) {
np.set(newVal);
}
};

var ATTR_TAIL_RE = /(\.[^\[\]\.]+|\[[^\[\]\.]+\])$/;
Copy link
Contributor

@rreusser rreusser Feb 24, 2017

Choose a reason for hiding this comment

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

Could you add maybe a single line or two about the sort of pattern this matches? I'm guessing… dot… more than one of… something… that's not brackets… or something that's not brackets or a dot? So

.something
something

match while

.something[]
something[]

do not?

Copy link
Contributor

Choose a reason for hiding this comment

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

(not a big deal; just difficult to confirm we're on the same page about what the end of an attr string means by mentally parsing the regexp)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

good call - also added some tests for hasParent -> 5fb462f

Copy link
Contributor

@rreusser rreusser left a comment

Choose a reason for hiding this comment

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

Epic PR! My feedback is mainly just a couple style/clarity things and confirming my own understanding. Nothing blocking for me. 💃

* @returns {bool} `true` if it managed to complete drawing of the changes
* `false` would mean the parent should replot.
*/
exports.editContainerArray = function editContainerArray(gd, np, edits, flags) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is editContainerArray the best name for this function? Grammar-wise, applyContainerArrayChanges might be more descriptive, and functionality-wise, I didn't assume by the name that it would also supply defaults and perform drawing updates.

* edit an object -> {'annotations[2].text': 'boo'} -> {2: {'text': 'boo'}}
*
* You can combine many edits to different objects. Objects are added and edited
* in ascending order, then removed in descending order.
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for enforcing that! As discussed elsewhere, the spec does not require any particular order when iterating over object keys, so I feel better knowing we're not just figuring people deserve to suffer the consequences if they combine operations ambiguously.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The other ambiguity-removing thing I did was to make both _restyle and _relayout error out if you try to edit both an object and one of its parents. That said, perhaps based on some other conversations of ours @etpinard in this case we should just log a warning and drop the child attribute, rather than throwing an error?

Copy link
Contributor

Choose a reason for hiding this comment

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

So {'marker.color': 'red', marker: {color: 'red}} errors out? I don't have strong feelings, though plotly's general solution seems to be to log an error and do the best it can.

Copy link
Contributor

@etpinard etpinard Feb 24, 2017

Choose a reason for hiding this comment

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

To me, there are two types of plotly.js errors:

  • Invalid plot attribute combinations,
  • Invalid inputs to Plotly.___ methods.

I believe the former should never lead to errors as @rreusser said plotly.js should try to do the best it can. This is especially important for non-JS users that send "data" and "layout" and expect to get in return the best possible ™️ graph.

This case ⬆️ falls into the second category. Plotly.__ method are called by JS users only* and throwing errors should help them debug. For example in Plotly.plot(gd, fig) if gd isn't right we throw an error. I think the same should be expected for invalid relayout update objects.

*: Note that updatemenus and slider now expose some Plotly.__ methods to non-JS users now. 😑

Copy link
Contributor

Choose a reason for hiding this comment

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

Good philosophical points, @etpinard!

* (as prepared in _relayout):
*
* add an empty obj -> {'annotations[2]': 'add'} -> {2: {'': 'add'}}
* add a specific obj -> {'annotations[2]': {attrs}} -> {2: {'': {attrs}}}
Copy link
Contributor

Choose a reason for hiding this comment

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

I would have expected this to say {'annotations[2]': {attrs}} -> {2: {attrs}}, but I may have content confused with the action to insert content.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

right - it's a bit of a weird structure I ended up with as input to editContainerArray. It's probably clear, but this is prepared in _relayout and users never see it. It's structured as edits[index][attribute] - so if you're setting an array element all at once, attribute will be '', and if you're setting the whole array at once, both attribute and index will be ''

Copy link
Contributor

Choose a reason for hiding this comment

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

Perfect. 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The reason it's not {'annotations[2]': {attrs}} -> {2: {attrs}} is that {2: {attrs}} and {2: {'': {attrs}} mean different things: the former would change just attrs, leaving intact anything else that's already present in annotations[2]. The latter inserts a new object containing just {attrs}.

componentType);
}

var fullVal = edits[''][''];
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it conceivable that edits[''] could ever not be array-indexable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

per #1403 (comment) - full-array changes will always have exactly this form. That was just the easiest construction for _relayout to do without edge cases, though I agree it looks a little odd in here...

Copy link
Contributor

Choose a reason for hiding this comment

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

All good! Assumed so. Just a PR-review reflex. 👍

arrayEdits = {},
arrayStr,
i,
j;
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI: I think by far the biggest manual change required to move from eslint to standard is that variable declarations like this must manually be split. The general rule is that initialized declarations must each have their own var and uninitialized declarations are fine in the same statement. So no particular feedback here, just that every time I see these, I worry about the big cleanup pass ahead 😬

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Interesting... what's "standard"?

Copy link
Contributor

Choose a reason for hiding this comment

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

See: #1371

Copy link
Contributor

@rreusser rreusser Feb 24, 2017

Choose a reason for hiding this comment

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

Was missing link to project page in that PR. Added. It's: https://github.com/feross/standard

Copy link
Contributor

Choose a reason for hiding this comment

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

Long story short, unconfigurable eslint.

r1 = ax.range[1];
if(toLog) {
// if both limits are negative, autorange
if(r0 <= 0 && r1 <= 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is nothing new and probably not worth addressing, but lots of logic and log calculations deep inside a 350+ line function seem like they'd be difficult to test and maintain. Would be nice if there were a better place for the logic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Man, the single biggest change I'm looking forward to in v2.0 is not using linearized coordinates for log axes. All of this crap will disappear then.

// now we've collected component edits - execute them all together
for(arrayStr in arrayEdits) {
var finished = manageArrays.editContainerArray(gd,
Lib.nestedProperty(layout, arrayStr), arrayEdits[arrayStr], flags);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same feedback. If this is the only place it's used, perhaps the supplydefaults and draw parts should be outside the function that manages arrays? You have a better sense than I do though, so feel free to disregard :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For now I'll change the name per #1403 (comment) -> d29cbae but I suspect in implementing #1412 we will restructure it along the lines you're suggesting.

for(i = 0; i < len; i++) {
Lib.relinkPrivateKeys(contOut[i], previousContOut[i]);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

My benchmark for success is just whether it passes the component test that was failing due to the ._remove property getting lost. It was originally just called .remove which would still get lost, so I'll check and submit that fix as a separate PR as needed.

@@ -9,7 +9,9 @@

'use strict';

var Lib = require('./lib');
var Loggers = require('./lib/loggers');
var noop = require('./lib/noop');
Copy link
Contributor

Choose a reason for hiding this comment

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

echo "var noop = require('./lib/noop');" |wc -c
34
$ echo "function noop() {}" |wc -c               
19

No. Stop. Don't bikeshed, Ricky. Don't bikeshed. LGTM. 👍

@@ -594,7 +594,8 @@ describe('attaching component bindings', function() {
expect(gd.layout.sliders[0].active).toBe(0);

// Check that it still has one attached listener:
expect(typeof gd._internalEv._events.plotly_animatingframe).toBe('function');
expect(typeof gd._internalEv._events.plotly_animatingframe).toBe('function',
gd._internalEv._events.plotly_animatingframe);
Copy link
Contributor

Choose a reason for hiding this comment

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

The second argument to toBe adds a message? No worries, just checking to make sure there's no unintended difference in function.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes - that's true for most of the jasmine matchers, and can be really useful in debugging failures.

});
})
.catch(fail)
.then(done);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hah, this test wasn't actually running. More precisely, it was running but not being used. Who wrote that sloppy thing? 🙈

Copy link
Contributor

Choose a reason for hiding this comment

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

image

Not me 😄

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The biggest irony is my commit message there:

fix the tests I must have only thought I had run successfully

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