diff --git a/src/components/annotations/annotation_defaults.js b/src/components/annotations/annotation_defaults.js index 36acea5d457..15aed2c04e1 100644 --- a/src/components/annotations/annotation_defaults.js +++ b/src/components/annotations/annotation_defaults.js @@ -23,6 +23,10 @@ module.exports = function handleAnnotationDefaults(annIn, fullLayout) { return Lib.coerce(annIn, annOut, attributes, attr, dflt); } + var visible = coerce('visible'); + + if(!visible) return annOut; + coerce('opacity'); coerce('align'); coerce('bgcolor'); diff --git a/src/components/annotations/attributes.js b/src/components/annotations/attributes.js index cea6afb9a4a..4c9b5f12024 100644 --- a/src/components/annotations/attributes.js +++ b/src/components/annotations/attributes.js @@ -17,6 +17,15 @@ var extendFlat = require('../../lib/extend').extendFlat; module.exports = { _isLinkedToArray: true, + visible: { + valType: 'boolean', + role: 'info', + dflt: true, + description: [ + 'Determines whether or not this annotation is visible.' + ].join(' ') + }, + text: { valType: 'string', role: 'info', diff --git a/src/components/annotations/calc_autorange.js b/src/components/annotations/calc_autorange.js index ba2261f3f4c..99abb765c99 100644 --- a/src/components/annotations/calc_autorange.js +++ b/src/components/annotations/calc_autorange.js @@ -17,7 +17,7 @@ var draw = require('./draw').draw; module.exports = function calcAutorange(gd) { var fullLayout = gd._fullLayout, - annotationList = fullLayout.annotations; + annotationList = Lib.filterVisible(fullLayout.annotations); if(!annotationList.length || !gd._fullData.length) return; diff --git a/src/components/annotations/draw.js b/src/components/annotations/draw.js index 2b104001477..92b2734ac73 100644 --- a/src/components/annotations/draw.js +++ b/src/components/annotations/draw.js @@ -47,7 +47,9 @@ function draw(gd) { fullLayout._infolayer.selectAll('.annotation').remove(); for(var i = 0; i < fullLayout.annotations.length; i++) { - drawOne(gd, i); + if(fullLayout.annotations[i].visible) { + drawOne(gd, i); + } } return Plots.previousPromises(gd); @@ -140,8 +142,6 @@ function drawOne(gd, index, opt, value) { // where we fail here when they add/remove annotations if(!optionsIn) return; - var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref}; - // alter the input annotation as requested var optionsEdit = {}; if(typeof opt === 'string' && opt) optionsEdit[opt] = value; @@ -153,7 +153,11 @@ function drawOne(gd, index, opt, value) { Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]); } + // return early in visible: false updates + if(optionsIn.visible === false) return; + var gs = fullLayout._size; + var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref}; var axLetters = ['x', 'y']; for(i = 0; i < 2; i++) { diff --git a/src/components/images/attributes.js b/src/components/images/attributes.js index a7f35575256..02d04a78297 100644 --- a/src/components/images/attributes.js +++ b/src/components/images/attributes.js @@ -14,6 +14,15 @@ var cartesianConstants = require('../../plots/cartesian/constants'); module.exports = { _isLinkedToArray: true, + visible: { + valType: 'boolean', + role: 'info', + dflt: true, + description: [ + 'Determines whether or not this image is visible.' + ].join(' ') + }, + source: { valType: 'string', role: 'info', diff --git a/src/components/images/defaults.js b/src/components/images/defaults.js index 1be89b4f05a..3546d0f9d8a 100644 --- a/src/components/images/defaults.js +++ b/src/components/images/defaults.js @@ -12,36 +12,34 @@ var Axes = require('../../plots/cartesian/axes'); var Lib = require('../../lib'); var attributes = require('./attributes'); +var name = 'images'; module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { + var contIn = Array.isArray(layoutIn[name]) ? layoutIn[name] : [], + contOut = layoutOut[name] = []; - if(!layoutIn.images || !Array.isArray(layoutIn.images)) return; + for(var i = 0; i < contIn.length; i++) { + var itemIn = contIn[i] || {}, + itemOut = {}; + imageDefaults(itemIn, itemOut, layoutOut); - var containerIn = layoutIn.images, - containerOut = layoutOut.images = []; - - - for(var i = 0; i < containerIn.length; i++) { - var image = containerIn[i]; - - if(!image.source) continue; - - var defaulted = imageDefaults(containerIn[i] || {}, containerOut[i] || {}, layoutOut); - containerOut.push(defaulted); + contOut.push(itemOut); } }; function imageDefaults(imageIn, imageOut, fullLayout) { - imageOut = imageOut || {}; - function coerce(attr, dflt) { return Lib.coerce(imageIn, imageOut, attributes, attr, dflt); } - coerce('source'); + var source = coerce('source'); + var visible = coerce('visible', !!source); + + if(!visible) return imageOut; + coerce('layer'); coerce('x'); coerce('y'); @@ -52,12 +50,12 @@ function imageDefaults(imageIn, imageOut, fullLayout) { coerce('sizing'); coerce('opacity'); - for(var i = 0; i < 2; i++) { - var tdMock = { _fullLayout: fullLayout }, - axLetter = ['x', 'y'][i]; + var gdMock = { _fullLayout: fullLayout }, + axLetters = ['x', 'y']; + for(var i = 0; i < 2; i++) { // 'paper' is the fallback axref - Axes.coerceRef(imageIn, imageOut, tdMock, axLetter, 'paper'); + Axes.coerceRef(imageIn, imageOut, gdMock, axLetters[i], 'paper'); } return imageOut; diff --git a/src/components/images/draw.js b/src/components/images/draw.js index a39a534adc2..b5ad8021558 100644 --- a/src/components/images/draw.js +++ b/src/components/images/draw.js @@ -14,25 +14,23 @@ var Axes = require('../../plots/cartesian/axes'); var xmlnsNamespaces = require('../../constants/xmlns_namespaces'); module.exports = function draw(gd) { - var fullLayout = gd._fullLayout, imageDataAbove = [], imageDataSubplot = [], imageDataBelow = []; - if(!fullLayout.images) return; - - // Sort into top, subplot, and bottom layers for(var i = 0; i < fullLayout.images.length; i++) { var img = fullLayout.images[i]; - if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') { - imageDataSubplot.push(img); - } else if(img.layer === 'above') { - imageDataAbove.push(img); - } else { - imageDataBelow.push(img); + if(img.visible) { + if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') { + imageDataSubplot.push(img); + } else if(img.layer === 'above') { + imageDataAbove.push(img); + } else { + imageDataBelow.push(img); + } } } diff --git a/src/components/shapes/attributes.js b/src/components/shapes/attributes.js index f90a218a4a4..446a2db51bc 100644 --- a/src/components/shapes/attributes.js +++ b/src/components/shapes/attributes.js @@ -17,6 +17,15 @@ var scatterLineAttrs = scatterAttrs.line; module.exports = { _isLinkedToArray: true, + visible: { + valType: 'boolean', + role: 'info', + dflt: true, + description: [ + 'Determines whether or not this shape is visible.' + ].join(' ') + }, + type: { valType: 'enumerated', values: ['circle', 'rect', 'path', 'line'], diff --git a/src/components/shapes/calc_autorange.js b/src/components/shapes/calc_autorange.js index 0aaaf499d13..a1d2ce0ba72 100644 --- a/src/components/shapes/calc_autorange.js +++ b/src/components/shapes/calc_autorange.js @@ -9,6 +9,7 @@ 'use strict'; +var Lib = require('../../lib'); var Axes = require('../../plots/cartesian/axes'); var constants = require('./constants'); @@ -17,7 +18,7 @@ var helpers = require('./helpers'); module.exports = function calcAutorange(gd) { var fullLayout = gd._fullLayout, - shapeList = fullLayout.shapes; + shapeList = Lib.filterVisible(fullLayout.shapes); if(!shapeList.length || !gd._fullData.length) return; diff --git a/src/components/shapes/draw.js b/src/components/shapes/draw.js index 7b5e67d99b7..487a44c4472 100644 --- a/src/components/shapes/draw.js +++ b/src/components/shapes/draw.js @@ -49,7 +49,9 @@ function draw(gd) { fullLayout._shapeSubplotLayer.selectAll('path').remove(); for(var i = 0; i < fullLayout.shapes.length; i++) { - drawOne(gd, i); + if(fullLayout.shapes[i].visible) { + drawOne(gd, i); + } } // may need to resurrect this if we put text (LaTeX) in shapes @@ -169,8 +171,6 @@ function updateShape(gd, index, opt, value) { // TODO: clean this up and remove it. if(!optionsIn) return; - var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref}; - // alter the input shape as requested var optionsEdit = {}; if(typeof opt === 'string' && opt) optionsEdit[opt] = value; @@ -182,7 +182,12 @@ function updateShape(gd, index, opt, value) { Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]); } - var posAttrs = ['x0', 'x1', 'y0', 'y1']; + // return early in visible: false updates + if(optionsIn.visible === false) return; + + var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref}, + posAttrs = ['x0', 'x1', 'y0', 'y1']; + for(i = 0; i < 4; i++) { var posAttr = posAttrs[i]; // if we don't have an explicit position already, diff --git a/src/components/shapes/shape_defaults.js b/src/components/shapes/shape_defaults.js index 88e4c98ba93..04f70fb30b6 100644 --- a/src/components/shapes/shape_defaults.js +++ b/src/components/shapes/shape_defaults.js @@ -22,6 +22,10 @@ module.exports = function handleShapeDefaults(shapeIn, fullLayout) { return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt); } + var visible = coerce('visible'); + + if(!visible) return shapeOut; + coerce('layer'); coerce('opacity'); coerce('fillcolor'); diff --git a/src/lib/filter_visible.js b/src/lib/filter_visible.js index cfea8b73941..64dee017ba4 100644 --- a/src/lib/filter_visible.js +++ b/src/lib/filter_visible.js @@ -9,14 +9,21 @@ 'use strict'; -module.exports = function filterVisible(dataIn) { - var dataOut = []; +/** Filter out object items with visible !== true + * insider array container. + * + * @param {array of objects} container + * @return {array of objects} of length <= container + * + */ +module.exports = function filterVisible(container) { + var out = []; - for(var i = 0; i < dataIn.length; i++) { - var trace = dataIn[i]; + for(var i = 0; i < container.length; i++) { + var item = container[i]; - if(trace.visible === true) dataOut.push(trace); + if(item.visible === true) out.push(item); } - return dataOut; + return out; }; diff --git a/src/lib/index.js b/src/lib/index.js index 895dfde0bb9..27705ff8d8b 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -76,6 +76,8 @@ lib.error = loggersModule.error; lib.notifier = require('./notifier'); lib.filterUnique = require('./filter_unique'); +lib.filterVisible = require('./filter_visible'); + /** * swap x and y of the same attribute in container cont diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index cbb4ad51e10..c707886b07f 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -1917,13 +1917,14 @@ function _relayout(gd, aobj) { objList = layout[objType] || [], obji = objList[objNum] || {}; - // new API, remove annotation / shape with `null` - if(vi === null) aobj[ai] = 'remove'; - // if p.parts is just an annotation number, and val is either // 'add' or an entire annotation to add, the undo is 'remove' // if val is 'remove' then undo is the whole annotation object if(p.parts.length === 2) { + + // new API, remove annotation / shape with `null` + if(vi === null) aobj[ai] = 'remove'; + if(aobj[ai] === 'add' || Lib.isPlainObject(aobj[ai])) { undoit[ai] = 'remove'; } diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index 94cf5d7a8bf..cc832e45699 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -19,7 +19,6 @@ var Drawing = require('../../components/drawing'); var setConvert = require('../cartesian/set_convert'); var extendFlat = require('../../lib/extend').extendFlat; var Axes = require('../cartesian/axes'); -var filterVisible = require('../../lib/filter_visible'); var dragElement = require('../../components/dragelement'); var Titles = require('../../components/titles'); var prepSelect = require('../cartesian/select'); @@ -94,7 +93,7 @@ proto.plot = function(ternaryData, fullLayout) { var moduleData = traceHash[moduleNames[i]]; var _module = moduleData[0]._module; - _module.plot(_this, filterVisible(moduleData), ternaryLayout); + _module.plot(_this, Lib.filterVisible(moduleData), ternaryLayout); } _this.traceHash = traceHash; diff --git a/test/image/mocks/annotations.json b/test/image/mocks/annotations.json index 9f88a7a5179..2bf9989b306 100644 --- a/test/image/mocks/annotations.json +++ b/test/image/mocks/annotations.json @@ -17,6 +17,7 @@ {"text":"right bottom","showarrow":false,"xref":"paper","yref":"paper","xanchor":"right","yanchor":"bottom","x":0.5,"y":1}, {"text":"move with page","xref":"paper","yref":"paper","x":0.75,"y":1}, {"text":"opacity","opacity":0.5,"x":5,"y":5}, + {"text":"not-visible", "visible": false}, {"text":"left
justified","showarrow":false,"align":"left","x":1,"y":4}, {"text":"center
justified","showarrow":false,"x":2,"y":4}, {"text":"right
justified","showarrow":false,"align":"right","x":3,"y":4}, diff --git a/test/image/mocks/layout_image.json b/test/image/mocks/layout_image.json index 67e9bcd1612..d993e8f61dd 100644 --- a/test/image/mocks/layout_image.json +++ b/test/image/mocks/layout_image.json @@ -51,6 +51,19 @@ "opacity": 0.4, "layer": "below" }, + { + "visible": false, + "source": "https://images.plot.ly/language-icons/api-home/python-logo.png", + "xref": "x", + "yref": "y", + "x": 1, + "y": 3, + "sizex": 2, + "sizey": 2, + "sizing": "stretch", + "opacity": 0.4, + "layer": "below" + }, { "source": "https://images.plot.ly/language-icons/api-home/matlab-logo.png", "xref": "x", diff --git a/test/image/mocks/shapes_below_traces.json b/test/image/mocks/shapes_below_traces.json index 9758896af57..fe0bd8427b7 100644 --- a/test/image/mocks/shapes_below_traces.json +++ b/test/image/mocks/shapes_below_traces.json @@ -91,6 +91,7 @@ "y1": 1, "yref": "paper" }, + { "visible": false }, { "fillcolor": "#f6e8c3", "layer": "below", diff --git a/test/jasmine/tests/annotations_test.js b/test/jasmine/tests/annotations_test.js index 72a7ab7675e..4816097dea5 100644 --- a/test/jasmine/tests/annotations_test.js +++ b/test/jasmine/tests/annotations_test.js @@ -6,6 +6,7 @@ var Lib = require('@src/lib'); var Dates = require('@src/lib/dates'); var d3 = require('d3'); +var customMatchers = require('../assets/custom_matchers'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); @@ -42,9 +43,11 @@ describe('annotations relayout', function() { 'use strict'; var mock = require('@mocks/annotations.json'); - var len = mock.layout.annotations.length; var gd; + // there is 1 visible: false item + var len = mock.layout.annotations.length - 1; + beforeEach(function(done) { gd = createGraphDiv(); @@ -68,16 +71,21 @@ describe('annotations relayout', function() { Plotly.relayout(gd, 'annotations[' + len + ']', ann).then(function() { expect(countAnnotations()).toEqual(len + 1); - return Plotly.relayout(gd, 'annotations[' + 0 + ']', 'remove'); + return Plotly.relayout(gd, 'annotations[0]', 'remove'); }) .then(function() { expect(countAnnotations()).toEqual(len); - return Plotly.relayout(gd, 'annotations[' + 0 + ']', null); + return Plotly.relayout(gd, 'annotations[0]', null); }) .then(function() { expect(countAnnotations()).toEqual(len - 1); + return Plotly.relayout(gd, 'annotations[0].visible', false); + }) + .then(function() { + expect(countAnnotations()).toEqual(len - 2); + return Plotly.relayout(gd, { annotations: [] }); }) .then(function() { @@ -86,4 +94,112 @@ describe('annotations relayout', function() { done(); }); }); + + it('should be able update annotations', function(done) { + + function assertText(index, expected) { + var query = '.annotation[data-index="' + index + '"]', + actual = d3.select(query).select('text').text(); + + expect(actual).toEqual(expected); + } + + assertText(0, 'left top'); + + Plotly.relayout(gd, 'annotations[0].text', 'hello').then(function() { + assertText(0, 'hello'); + + return Plotly.relayout(gd, 'annotations[0].text', null); + }) + .then(function() { + assertText(0, 'new text'); + }) + .then(done); + + }); +}); + +describe('annotations autosize', function() { + 'use strict'; + + var mock = Lib.extendDeep({}, require('@mocks/annotations-autorange.json')); + var gd; + + beforeAll(function() { + jasmine.addMatchers(customMatchers); + }); + + afterEach(destroyGraphDiv); + + it('should adapt to relayout calls', function(done) { + gd = createGraphDiv(); + + function assertRanges(x, y, x2, y2, x3, y3) { + var fullLayout = gd._fullLayout; + var PREC = 1; + + // xaxis2 need a bit more tolerance to pass on CI + // this most likely due to the different text bounding box values + // on headfull vs headless browsers. + var PREC2 = 0.1; + + expect(fullLayout.xaxis.range).toBeCloseToArray(x, PREC, '- xaxis'); + expect(fullLayout.yaxis.range).toBeCloseToArray(y, PREC, '- yaxis'); + expect(fullLayout.xaxis2.range).toBeCloseToArray(x2, PREC2, 'xaxis2'); + expect(fullLayout.yaxis2.range).toBeCloseToArray(y2, PREC, 'yaxis2'); + expect(fullLayout.xaxis3.range).toBeCloseToArray(x3, PREC, 'xaxis3'); + expect(fullLayout.yaxis3.range).toBeCloseToArray(y3, PREC, 'yaxis3'); + } + + Plotly.plot(gd, mock).then(function() { + assertRanges( + [0.97, 2.03], [0.97, 2.03], + [-0.32, 3.38], [0.42, 2.58], + [0.9, 2.1], [0.86, 2.14] + ); + + return Plotly.relayout(gd, { + 'annotations[0].visible': false, + 'annotations[4].visible': false, + 'annotations[8].visible': false + }); + }) + .then(function() { + assertRanges( + [1.44, 2.02], [0.97, 2.03], + [1.31, 2.41], [0.42, 2.58], + [1.44, 2.1], [0.86, 2.14] + ); + + return Plotly.relayout(gd, { + 'annotations[2].visible': false, + 'annotations[5].visible': false, + 'annotations[9].visible': false + }); + }) + .then(function() { + assertRanges( + [1.44, 2.02], [0.99, 1.52], + [0.5, 2.5], [0.42, 2.58], + [0.5, 2.5], [0.86, 2.14] + ); + + return Plotly.relayout(gd, { + 'annotations[0].visible': true, + 'annotations[2].visible': true, + 'annotations[4].visible': true, + 'annotations[5].visible': true, + 'annotations[8].visible': true, + 'annotations[9].visible': true + }); + }) + .then(function() { + assertRanges( + [0.97, 2.03], [0.97, 2.03], + [-0.32, 3.38], [0.42, 2.58], + [0.9, 2.1], [0.86, 2.14] + ); + }) + .then(done); + }); }); diff --git a/test/jasmine/tests/layout_images_test.js b/test/jasmine/tests/layout_images_test.js index b40999cfc1e..6aedc708425 100644 --- a/test/jasmine/tests/layout_images_test.js +++ b/test/jasmine/tests/layout_images_test.js @@ -27,7 +27,7 @@ describe('Layout images', function() { Images.supplyLayoutDefaults(layoutIn, layoutOut); - expect(layoutOut.images.length).toEqual(0); + expect(layoutOut.images).toEqual([{ visible: false }]); }); it('should reject when not an array', function() { @@ -40,7 +40,7 @@ describe('Layout images', function() { Images.supplyLayoutDefaults(layoutIn, layoutOut); - expect(layoutOut.images).not.toBeDefined(); + expect(layoutOut.images).toEqual([]); }); it('should coerce the correct defaults', function() { @@ -48,6 +48,7 @@ describe('Layout images', function() { var expected = { source: jsLogo, + visible: true, layer: 'above', x: 0, y: 0, @@ -319,30 +320,48 @@ describe('Layout images', function() { assertImages(0); return Plotly.relayout(gd, 'images[0]', makeImage(jsLogo, 0.1, 0.1)); - }).then(function() { + }) + .then(function() { assertImages(1); return Plotly.relayout(gd, 'images[1]', makeImage(pythonLogo, 0.9, 0.9)); - }).then(function() { + }) + .then(function() { assertImages(2); return Plotly.relayout(gd, 'images[2]', makeImage(pythonLogo, 0.2, 0.5)); - }).then(function() { + }) + .then(function() { + assertImages(3); + expect(gd.layout.images.length).toEqual(3); + + return Plotly.relayout(gd, 'images[1].visible', false); + }) + .then(function() { + assertImages(2); + expect(gd.layout.images.length).toEqual(3); + + return Plotly.relayout(gd, 'images[1].visible', true); + }) + .then(function() { assertImages(3); expect(gd.layout.images.length).toEqual(3); return Plotly.relayout(gd, 'images[2]', null); - }).then(function() { + }) + .then(function() { assertImages(2); expect(gd.layout.images.length).toEqual(2); return Plotly.relayout(gd, 'images[1]', null); - }).then(function() { + }) + .then(function() { assertImages(1); expect(gd.layout.images.length).toEqual(1); return Plotly.relayout(gd, 'images[0]', null); - }).then(function() { + }) + .then(function() { assertImages(0); expect(gd.layout.images).toEqual([]); diff --git a/test/jasmine/tests/shapes_test.js b/test/jasmine/tests/shapes_test.js index 1eb8dab4ce1..c00f104a90d 100644 --- a/test/jasmine/tests/shapes_test.js +++ b/test/jasmine/tests/shapes_test.js @@ -7,6 +7,7 @@ var Lib = require('@src/lib'); var Axes = PlotlyInternal.Axes; var d3 = require('d3'); +var customMatchers = require('../assets/custom_matchers'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); @@ -183,18 +184,23 @@ describe('Test shapes:', function() { expect(countShapePathsInUpperLayer()).toEqual(pathCount + 1); expect(getLastShape(gd)).toEqual(shape); expect(countShapes(gd)).toEqual(index + 1); - }) - .then(function() { + return Plotly.relayout(gd, 'shapes[' + index + ']', 'remove'); }) .then(function() { expect(countShapePathsInUpperLayer()).toEqual(pathCount); expect(countShapes(gd)).toEqual(index); - return Plotly.relayout(gd, 'shapes[' + 1 + ']', null); + return Plotly.relayout(gd, 'shapes[2].visible', false); }) .then(function() { expect(countShapePathsInUpperLayer()).toEqual(pathCount - 1); + expect(countShapes(gd)).toEqual(index); + + return Plotly.relayout(gd, 'shapes[1]', null); + }) + .then(function() { + expect(countShapePathsInUpperLayer()).toEqual(pathCount - 2); expect(countShapes(gd)).toEqual(index - 1); }) .then(done); @@ -250,6 +256,69 @@ describe('Test shapes:', function() { }); }); +describe('shapes autosize', function() { + 'use strict'; + + var gd; + + beforeAll(function() { + jasmine.addMatchers(customMatchers); + }); + + afterEach(destroyGraphDiv); + + it('should adapt to relayout calls', function(done) { + gd = createGraphDiv(); + + var mock = { + data: [{}], + layout: { + shapes: [{ + type: 'line', + x0: 0, + y0: 0, + x1: 1, + y1: 1 + }, { + type: 'line', + x0: 0, + y0: 0, + x1: 2, + y1: 2 + }] + } + }; + + function assertRanges(x, y) { + var fullLayout = gd._fullLayout; + var PREC = 1; + + expect(fullLayout.xaxis.range).toBeCloseToArray(x, PREC, '- xaxis'); + expect(fullLayout.yaxis.range).toBeCloseToArray(y, PREC, '- yaxis'); + } + + Plotly.plot(gd, mock).then(function() { + assertRanges([0, 2], [0, 2]); + + return Plotly.relayout(gd, { 'shapes[1].visible': false }); + }) + .then(function() { + assertRanges([0, 1], [0, 1]); + + return Plotly.relayout(gd, { 'shapes[1].visible': true }); + }) + .then(function() { + assertRanges([0, 2], [0, 2]); + + return Plotly.relayout(gd, { 'shapes[0].x1': 3 }); + }) + .then(function() { + assertRanges([0, 3], [0, 2]); + }) + .then(done); + }); +}); + describe('Test shapes: a plot with shapes and an overlaid axis', function() { 'use strict';