diff --git a/draftlogs/6927_add.md b/draftlogs/6927_add.md new file mode 100644 index 00000000000..c0e77d41684 --- /dev/null +++ b/draftlogs/6927_add.md @@ -0,0 +1,2 @@ + - Add "between" option to shape layer for placing them above grid lines and below traces [[#6927](https://github.com/plotly/plotly.js/pull/6927)], + with thanks to @my-tien for the contribution! diff --git a/src/components/shapes/attributes.js b/src/components/shapes/attributes.js index 589110eb1d1..6a6e5a3a846 100644 --- a/src/components/shapes/attributes.js +++ b/src/components/shapes/attributes.js @@ -110,10 +110,13 @@ module.exports = templatedArray('shape', { layer: { valType: 'enumerated', - values: ['below', 'above'], + values: ['below', 'above', 'between'], dflt: 'above', editType: 'arraydraw', - description: 'Specifies whether shapes are drawn below or above traces.' + description: [ + 'Specifies whether shapes are drawn below gridlines (*below*),', + 'between gridlines and traces (*between*) or above traces (*above*).' + ].join(' ') }, xref: extendFlat({}, annAttrs.xref, { diff --git a/src/components/shapes/draw.js b/src/components/shapes/draw.js index 26339816c75..417c884c3f2 100644 --- a/src/components/shapes/draw.js +++ b/src/components/shapes/draw.js @@ -91,10 +91,12 @@ function drawOne(gd, index) { // TODO: use d3 idioms instead of deleting and redrawing every time if(!options._input || options.visible !== true) return; - if(options.layer !== 'below') { + if(options.layer === 'above') { drawShape(gd._fullLayout._shapeUpperLayer); } else if(options.xref === 'paper' || options.yref === 'paper') { drawShape(gd._fullLayout._shapeLowerLayer); + } else if(options.layer === 'between') { + drawShape(plotinfo.shapelayerBetween); } else { if(plotinfo._hadPlotinfo) { var mainPlot = plotinfo.mainplotinfo || plotinfo; diff --git a/src/components/shapes/draw_newshape/attributes.js b/src/components/shapes/draw_newshape/attributes.js index 4cad7c7d27f..633870414b6 100644 --- a/src/components/shapes/draw_newshape/attributes.js +++ b/src/components/shapes/draw_newshape/attributes.js @@ -117,9 +117,12 @@ module.exports = overrideAll({ }, layer: { valType: 'enumerated', - values: ['below', 'above'], + values: ['below', 'above', 'between'], dflt: 'above', - description: 'Specifies whether new shapes are drawn below or above traces.' + description: [ + 'Specifies whether new shapes are drawn below gridlines (*below*),', + 'between gridlines and traces (*between*) or above traces (*above*).' + ].join(' ') }, drawdirection: { valType: 'enumerated', diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index e57c5572d01..28e46a64069 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -465,6 +465,10 @@ function makeSubplotLayer(gd, plotinfo) { plotinfo.gridlayer = ensureSingle(plotgroup, 'g', 'gridlayer'); plotinfo.zerolinelayer = ensureSingle(plotgroup, 'g', 'zerolinelayer'); + var betweenLayer = ensureSingle(plotgroup, 'g', 'layer-between'); + plotinfo.shapelayerBetween = ensureSingle(betweenLayer, 'g', 'shapelayer'); + plotinfo.imagelayerBetween = ensureSingle(betweenLayer, 'g', 'imagelayer'); + ensureSingle(plotgroup, 'path', 'xlines-below'); ensureSingle(plotgroup, 'path', 'ylines-below'); plotinfo.overlinesBelow = ensureSingle(plotgroup, 'g', 'overlines-below'); diff --git a/test/image/baselines/zz-shapes_layer_below_traces.png b/test/image/baselines/zz-shapes_layer_below_traces.png new file mode 100644 index 00000000000..d6e05eb6f0f Binary files /dev/null and b/test/image/baselines/zz-shapes_layer_below_traces.png differ diff --git a/test/image/mocks/zz-shapes_layer_below_traces.json b/test/image/mocks/zz-shapes_layer_below_traces.json new file mode 100644 index 00000000000..4f198489920 --- /dev/null +++ b/test/image/mocks/zz-shapes_layer_below_traces.json @@ -0,0 +1,137 @@ +{ + "data": [ + { + "name": "scatter", + "x": [ + "A", + "B", + "C", + "D", + 2000.0 + ], + "y": [ + 100, + 200, + 150, + 190, + 50 + ], + "type": "scatter" + }, + { + "name": "bar", + "x": [ + "A", + "B", + "C", + "D", + 2000.0 + ], + "y": [ + 50, + 70, + 90, + 35, + 55 + ], + "type": "bar", + "yaxis":"y2" + } + ], + "layout": { + "shapes": [ + { + "name": "dashdot line", + "fillcolor": "rgba(179,179,179,1)", + "layer": "above", + "line": { + "color": "rgba(66,127,109,1)", + "dash": "dashdot" + }, + "type": "line", + "x0": 0.4, + "x1": 0.4, + "xref": "x", + "y0": 0.0, + "y1": 1.0, + "yref": "paper" + }, + { + "name": "horizontal line", + "fillcolor": "rgba(179,179,179,1)", + "layer": "above", + "line": { + "color": "rgba(0,0,0,1)" + }, + "type": "line", + "x0": 0.0, + "x1": 1.0, + "xref": "paper", + "y0": 120.0, + "y1": 120.0, + "yref": "y" + }, + { + "name": "blue rect", + "fillcolor": "rgba(108,173,225,1)", + "layer": "between", + "label": { + "text": "hello, I am between gridlines and traces!" + }, + "line": { + "color": "rgba(108,173,225,1)" + }, + "type": "rect", + "x0": "B", + "x1": "D", + "xref": "x", + "y0": 35, + "y1": 65.0 + }, + { + "name": "big rect", + "fillcolor": "rgba(179,179,179,1)", + "layer": "below", + "line": { + "color": "rgba(179,179,179,1)" + }, + "type": "rect", + "x0": "C", + "x1": 0.25, + "xref": "x", + "y0": 0.0, + "y1": 1.0, + "yref": "paper" + }, + { + "name": "dashed rect", + "fillcolor": "rgba(130,143,198,1)", + "layer": "between", + "line": { + "color": "rgba(0,0,0,1)", + "dash": "dash", + "width": 3.0 + }, + "type": "rect", + "x0": "D", + "x1": 5, + "y0": 140, + "y1": 200 + } + ], + "yaxis": { + "gridcolor": "red", + "insiderange": [ + "0", + "215" + ], + "type": "linear" + }, + "yaxis2": { + "gridwidth": 2, + "side": "right", + "overlaying": "y" + } + + } +} diff --git a/test/jasmine/tests/splom_test.js b/test/jasmine/tests/splom_test.js index b85c14882bd..1702d63e2f5 100644 --- a/test/jasmine/tests/splom_test.js +++ b/test/jasmine/tests/splom_test.js @@ -825,7 +825,7 @@ describe('Test splom interactions:', function() { .then(function() { _assert({ subplotCnt: 25, - innerSubplotNodeCnt: 18, + innerSubplotNodeCnt: 19, hasSplomGrid: false, bgCnt: 0 }); @@ -845,7 +845,7 @@ describe('Test splom interactions:', function() { // grid layer would be above xaxis layer, // if we didn't clear subplot children. expect(gridIndex).toBe(2, ' index'); - expect(xaxisIndex).toBe(15, ' index'); + expect(xaxisIndex).toBe(16, ' index'); return Plotly.restyle(gd, 'dimensions', [dimsLarge]); }) @@ -857,7 +857,7 @@ describe('Test splom interactions:', function() { // new subplots though have reduced number of children. innerSubplotNodeCnt: function(d) { var p = d.match(SUBPLOT_PATTERN); - return (p[1] > 5 || p[2] > 5) ? 4 : 18; + return (p[1] > 5 || p[2] > 5) ? 4 : 19; }, hasSplomGrid: true, bgCnt: 0 diff --git a/test/plot-schema.json b/test/plot-schema.json index 9b3a8816729..4ce7600457d 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -3720,13 +3720,14 @@ } }, "layer": { - "description": "Specifies whether new shapes are drawn below or above traces.", + "description": "Specifies whether new shapes are drawn below gridlines (*below*), between gridlines and traces (*between*) or above traces (*above*).", "dflt": "above", "editType": "none", "valType": "enumerated", "values": [ "below", - "above" + "above", + "between" ] }, "legend": { @@ -7708,13 +7709,14 @@ } }, "layer": { - "description": "Specifies whether shapes are drawn below or above traces.", + "description": "Specifies whether shapes are drawn below gridlines (*below*), between gridlines and traces (*between*) or above traces (*above*).", "dflt": "above", "editType": "arraydraw", "valType": "enumerated", "values": [ "below", - "above" + "above", + "between" ] }, "legend": {