diff --git a/src/plots/cartesian/axis_ids.js b/src/plots/cartesian/axis_ids.js index 8e0bf5003d3..eb36d1bf1c2 100644 --- a/src/plots/cartesian/axis_ids.js +++ b/src/plots/cartesian/axis_ids.js @@ -114,3 +114,13 @@ exports.idSort = function(id1, id2) { if(letter1 !== letter2) return letter1 > letter2 ? 1 : -1; return +(id1.substr(1) || 1) - +(id2.substr(1) || 1); }; + +exports.getAxisGroup = function getAxisGroup(fullLayout, axId) { + var matchGroups = fullLayout._axisMatchGroups; + + for(var i = 0; i < matchGroups.length; i++) { + var group = matchGroups[i]; + if(group[axId]) return 'g' + i; + } + return axId; +}; diff --git a/src/plots/plots.js b/src/plots/plots.js index 9fa2616a69e..cde651366e8 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -394,6 +394,8 @@ plots.supplyDefaults = function(gd, opts) { newFullLayout._scatterStackOpts = {}; // for the first scatter trace on each subplot (so it knows tonext->tozero) newFullLayout._firstScatter = {}; + // for grouped bar/box/violin trace to share config across traces + newFullLayout._alignmentOpts = {}; // for traces to request a default rangeslider on their x axes // eg set `_requestRangeslider.x2 = true` for xaxis2 diff --git a/src/traces/bar/attributes.js b/src/traces/bar/attributes.js index ea0694f077c..5cf9003da59 100644 --- a/src/traces/bar/attributes.js +++ b/src/traces/bar/attributes.js @@ -174,6 +174,30 @@ module.exports = { marker: marker, + offsetgroup: { + valType: 'string', + role: 'info', + dflt: '', + editType: 'calc', + description: [ + 'Set several traces linked to the same position axis', + 'or matching axes to the same', + 'offsetgroup where bars of the same position coordinate will line up.' + ].join(' ') + }, + alignmentgroup: { + valType: 'string', + role: 'info', + dflt: '', + editType: 'calc', + description: [ + 'Set several traces linked to the same position axis', + 'or matching axes to the same', + 'alignmentgroup. This controls whether bars compute their positional', + 'range dependently or independently.' + ].join(' ') + }, + selected: { marker: { opacity: scatterAttrs.selected.marker.opacity, diff --git a/src/traces/bar/cross_trace_calc.js b/src/traces/bar/cross_trace_calc.js index 2a0224dc35e..916e853eec5 100644 --- a/src/traces/bar/cross_trace_calc.js +++ b/src/traces/bar/cross_trace_calc.js @@ -14,6 +14,7 @@ var BADNUM = require('../../constants/numerical').BADNUM; var Registry = require('../../registry'); var Axes = require('../../plots/cartesian/axes'); +var getAxisGroup = require('../../plots/cartesian/axis_ids').getAxisGroup; var Sieve = require('./sieve.js'); /* @@ -279,26 +280,42 @@ function setOffsetAndWidthInGroupMode(gd, pa, sieve) { var distinctPositions = sieve.distinctPositions; var minDiff = sieve.minDiff; var calcTraces = sieve.traces; + var nTraces = calcTraces.length; // if there aren't any overlapping positions, // let them have full width even if mode is group var overlap = (positions.length !== distinctPositions.length); - - var nTraces = calcTraces.length; var barGroupWidth = minDiff * (1 - bargap); - var barWidthPlusGap = (overlap) ? barGroupWidth / nTraces : barGroupWidth; - var barWidth = barWidthPlusGap * (1 - bargroupgap); + + var groupId = getAxisGroup(fullLayout, pa._id) + calcTraces[0][0].trace.orientation; + var alignmentGroups = fullLayout._alignmentOpts[groupId] || {}; for(var i = 0; i < nTraces; i++) { var calcTrace = calcTraces[i]; - var t = calcTrace[0].t; + var trace = calcTrace[0].trace; - // computer bar group center and bar offset - var offsetFromCenter = overlap ? - ((2 * i + 1 - nTraces) * barWidthPlusGap - barWidth) / 2 : - -barWidth / 2; + var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {}; + var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length; - // store bar width and offset for this trace + var barWidthPlusGap; + if(nOffsetGroups) { + barWidthPlusGap = barGroupWidth / nOffsetGroups; + } else { + barWidthPlusGap = overlap ? barGroupWidth / nTraces : barGroupWidth; + } + + var barWidth = barWidthPlusGap * (1 - bargroupgap); + + var offsetFromCenter; + if(nOffsetGroups) { + offsetFromCenter = ((2 * trace._offsetIndex + 1 - nOffsetGroups) * barWidthPlusGap - barWidth) / 2; + } else { + offsetFromCenter = overlap ? + ((2 * i + 1 - nTraces) * barWidthPlusGap - barWidth) / 2 : + -barWidth / 2; + } + + var t = calcTrace[0].t; t.barwidth = barWidth; t.poffset = offsetFromCenter; t.bargroupwidth = barGroupWidth; diff --git a/src/traces/bar/defaults.js b/src/traces/bar/defaults.js index b32a14d5cf2..b6715411524 100644 --- a/src/traces/bar/defaults.js +++ b/src/traces/bar/defaults.js @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var Lib = require('../../lib'); @@ -15,9 +14,10 @@ var Registry = require('../../registry'); var handleXYDefaults = require('../scatter/xy_defaults'); var handleStyleDefaults = require('../bar/style_defaults'); +var getAxisGroup = require('../../plots/cartesian/axis_ids').getAxisGroup; var attributes = require('./attributes'); -module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { +function supplyDefaults(traceIn, traceOut, defaultColor, layout) { function coerce(attr, dflt) { return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } @@ -78,4 +78,68 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'}); Lib.coerceSelectionMarkerOpacity(traceOut, coerce); +} + +function handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce) { + var orientation = traceOut.orientation; + // N.B. grouping is done across all trace trace types that support it + var posAxId = traceOut[{v: 'x', h: 'y'}[orientation] + 'axis']; + var groupId = getAxisGroup(fullLayout, posAxId) + orientation; + + var alignmentOpts = fullLayout._alignmentOpts || {}; + var alignmentgroup = coerce('alignmentgroup'); + + var alignmentGroups = alignmentOpts[groupId]; + if(!alignmentGroups) alignmentGroups = alignmentOpts[groupId] = {}; + + var alignmentGroupOpts = alignmentGroups[alignmentgroup]; + + if(alignmentGroupOpts) { + alignmentGroupOpts.traces.push(traceOut); + } else { + alignmentGroupOpts = alignmentGroups[alignmentgroup] = { + traces: [traceOut], + alignmentIndex: Object.keys(alignmentGroups).length, + offsetGroups: {} + }; + } + + var offsetgroup = coerce('offsetgroup'); + var offsetGroups = alignmentGroupOpts.offsetGroups; + var offsetGroupOpts = offsetGroups[offsetgroup]; + + if(offsetgroup) { + if(!offsetGroupOpts) { + offsetGroupOpts = offsetGroups[offsetgroup] = { + offsetIndex: Object.keys(offsetGroups).length + }; + } + + traceOut._offsetIndex = offsetGroupOpts.offsetIndex; + } +} + +function crossTraceDefaults(fullData, fullLayout) { + var traceIn, traceOut; + + function coerce(attr) { + return Lib.coerce(traceOut._input, traceOut, attributes, attr); + } + + for(var i = 0; i < fullData.length; i++) { + traceOut = fullData[i]; + + if(traceOut.type === 'bar') { + traceIn = traceOut._input; + if(fullLayout.barmode === 'group') { + handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce); + } + } + } +} + +module.exports = { + supplyDefaults: supplyDefaults, + crossTraceDefaults: crossTraceDefaults, + handleGroupingDefaults: handleGroupingDefaults }; diff --git a/src/traces/bar/index.js b/src/traces/bar/index.js index fa579dbf3fa..2da83f64c5b 100644 --- a/src/traces/bar/index.js +++ b/src/traces/bar/index.js @@ -6,14 +6,14 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var Bar = {}; Bar.attributes = require('./attributes'); Bar.layoutAttributes = require('./layout_attributes'); -Bar.supplyDefaults = require('./defaults'); +Bar.supplyDefaults = require('./defaults').supplyDefaults; +Bar.crossTraceDefaults = require('./defaults').crossTraceDefaults; Bar.supplyLayoutDefaults = require('./layout_defaults'); Bar.calc = require('./calc'); Bar.crossTraceCalc = require('./cross_trace_calc').crossTraceCalc; diff --git a/src/traces/bar/layout_defaults.js b/src/traces/bar/layout_defaults.js index 7abb2920dfa..1f688164dd0 100644 --- a/src/traces/bar/layout_defaults.js +++ b/src/traces/bar/layout_defaults.js @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var Registry = require('../../registry'); @@ -15,7 +14,6 @@ var Lib = require('../../lib'); var layoutAttributes = require('./layout_attributes'); - module.exports = function(layoutIn, layoutOut, fullData) { function coerce(attr, dflt) { return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt); diff --git a/src/traces/box/attributes.js b/src/traces/box/attributes.js index 7bfd9bce3ce..93318226ee3 100644 --- a/src/traces/box/attributes.js +++ b/src/traces/box/attributes.js @@ -9,6 +9,7 @@ 'use strict'; var scatterAttrs = require('../scatter/attributes'); +var barAttrs = require('../bar/attributes'); var colorAttrs = require('../../components/color/attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -250,6 +251,9 @@ module.exports = { }, fillcolor: scatterAttrs.fillcolor, + offsetgroup: barAttrs.offsetgroup, + alignmentgroup: barAttrs.alignmentgroup, + selected: { marker: scatterAttrs.selected.marker, editType: 'style' diff --git a/src/traces/box/cross_trace_calc.js b/src/traces/box/cross_trace_calc.js index 8c5d4d2fc7e..566c47d368a 100644 --- a/src/traces/box/cross_trace_calc.js +++ b/src/traces/box/cross_trace_calc.js @@ -10,6 +10,7 @@ var Axes = require('../../plots/cartesian/axes'); var Lib = require('../../lib'); +var getAxisGroup = require('../../plots/cartesian/axis_ids').getAxisGroup; var orientations = ['v', 'h']; @@ -51,9 +52,6 @@ function setPositionOffset(traceType, gd, boxList, posAxis) { var axId = posAxis._id; var axLetter = axId.charAt(0); - // N.B. reused in violin - var numKey = traceType === 'violin' ? '_numViolins' : '_numBoxes'; - var i, j, calcTrace; var pointList = []; var shownPts = 0; @@ -76,8 +74,9 @@ function setPositionOffset(traceType, gd, boxList, posAxis) { // check for forced minimum dtick Axes.minDtick(posAxis, boxdv.minDiff, boxdv.vals[0], true); - var num = fullLayout[numKey]; - var group = (fullLayout[traceType + 'mode'] === 'group' && num > 1); + var numKey = traceType === 'violin' ? '_numViolins' : '_numBoxes'; + var numTotal = fullLayout[numKey]; + var group = fullLayout[traceType + 'mode'] === 'group' && numTotal > 1; var groupFraction = 1 - fullLayout[traceType + 'gap']; var groupGapFraction = 1 - fullLayout[traceType + 'groupgap']; @@ -104,9 +103,23 @@ function setPositionOffset(traceType, gd, boxList, posAxis) { bPos = 0; } else { dPos = dPos0; - bdPos = dPos * groupFraction * groupGapFraction / (group ? num : 1); - bPos = group ? 2 * dPos * (-0.5 + (t.num + 0.5) / num) * groupFraction : 0; - wHover = dPos * (group ? groupFraction / num : 1); + + if(group) { + var groupId = getAxisGroup(fullLayout, posAxis._id) + trace.orientation; + var alignmentGroups = fullLayout._alignmentOpts[groupId] || {}; + var alignmentGroupOpts = alignmentGroups[trace.alignmentgroup] || {}; + var nOffsetGroups = Object.keys(alignmentGroupOpts.offsetGroups || {}).length; + var num = nOffsetGroups || numTotal; + var shift = nOffsetGroups ? trace._offsetIndex : t.num; + + bdPos = dPos * groupFraction * groupGapFraction / num; + bPos = 2 * dPos * (-0.5 + (shift + 0.5) / num) * groupFraction; + wHover = dPos * groupFraction / num; + } else { + bdPos = dPos * groupFraction * groupGapFraction; + bPos = 0; + wHover = dPos; + } } t.dPos = dPos; t.bPos = bPos; diff --git a/src/traces/box/defaults.js b/src/traces/box/defaults.js index b3d6500e5d4..b8e315cd0fc 100644 --- a/src/traces/box/defaults.js +++ b/src/traces/box/defaults.js @@ -11,7 +11,7 @@ var Lib = require('../../lib'); var Registry = require('../../registry'); var Color = require('../../components/color'); - +var handleGroupingDefaults = require('../bar/defaults').handleGroupingDefaults; var attributes = require('./attributes'); function supplyDefaults(traceIn, traceOut, defaultColor, layout) { @@ -109,8 +109,30 @@ function handlePointsDefaults(traceIn, traceOut, coerce, opts) { Lib.coerceSelectionMarkerOpacity(traceOut, coerce); } +function crossTraceDefaults(fullData, fullLayout) { + var traceIn, traceOut; + + function coerce(attr) { + return Lib.coerce(traceOut._input, traceOut, attributes, attr); + } + + for(var i = 0; i < fullData.length; i++) { + traceOut = fullData[i]; + var traceType = traceOut.type; + + if(traceType === 'box' || traceType === 'violin') { + traceIn = traceOut._input; + if(fullLayout[traceType + 'mode'] === 'group') { + handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce); + } + } + } +} + module.exports = { supplyDefaults: supplyDefaults, + crossTraceDefaults: crossTraceDefaults, + handleSampleDefaults: handleSampleDefaults, handlePointsDefaults: handlePointsDefaults }; diff --git a/src/traces/box/index.js b/src/traces/box/index.js index e8ffe05cc8a..174a3d6bb36 100644 --- a/src/traces/box/index.js +++ b/src/traces/box/index.js @@ -13,6 +13,7 @@ var Box = {}; Box.attributes = require('./attributes'); Box.layoutAttributes = require('./layout_attributes'); Box.supplyDefaults = require('./defaults').supplyDefaults; +Box.crossTraceDefaults = require('./defaults').crossTraceDefaults; Box.supplyLayoutDefaults = require('./layout_defaults').supplyLayoutDefaults; Box.calc = require('./calc'); Box.crossTraceCalc = require('./cross_trace_calc').crossTraceCalc; diff --git a/src/traces/box/layout_defaults.js b/src/traces/box/layout_defaults.js index 027ab407ff7..6c23d343a47 100644 --- a/src/traces/box/layout_defaults.js +++ b/src/traces/box/layout_defaults.js @@ -13,10 +13,13 @@ var Lib = require('../../lib'); var layoutAttributes = require('./layout_attributes'); function _supply(layoutIn, layoutOut, fullData, coerce, traceType) { - var hasTraceType; var category = traceType + 'Layout'; + var hasTraceType = false; + for(var i = 0; i < fullData.length; i++) { - if(Registry.traceIs(fullData[i], category)) { + var trace = fullData[i]; + + if(Registry.traceIs(trace, category)) { hasTraceType = true; break; } diff --git a/src/traces/histogram/attributes.js b/src/traces/histogram/attributes.js index 5391922a702..4433288d720 100644 --- a/src/traces/histogram/attributes.js +++ b/src/traces/histogram/attributes.js @@ -188,6 +188,9 @@ module.exports = { marker: barAttrs.marker, + offsetgroup: barAttrs.offsetgroup, + alignmentgroup: barAttrs.alignmentgroup, + selected: barAttrs.selected, unselected: barAttrs.unselected, diff --git a/src/traces/histogram/cross_trace_defaults.js b/src/traces/histogram/cross_trace_defaults.js index 4df1814fc71..0a7663cc43d 100644 --- a/src/traces/histogram/cross_trace_defaults.js +++ b/src/traces/histogram/cross_trace_defaults.js @@ -6,12 +6,13 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var Lib = require('../../lib'); var nestedProperty = Lib.nestedProperty; +var handleGroupingDefaults = require('../bar/defaults').handleGroupingDefaults; +var getAxisGroup = require('../../plots/cartesian/axis_ids').getAxisGroup; var attributes = require('./attributes'); var BINATTRS = { @@ -65,6 +66,8 @@ module.exports = function crossTraceDefaults(fullData, fullLayout) { direction: binDirection }; } + + handleGroupingDefaults(traceOut._input, traceOut, fullLayout, coerce); } for(group in allBinOpts) { @@ -111,13 +114,3 @@ module.exports = function crossTraceDefaults(fullData, fullLayout) { } } }; - -function getAxisGroup(fullLayout, axId) { - var matchGroups = fullLayout._axisMatchGroups; - - for(var i = 0; i < matchGroups.length; i++) { - var group = matchGroups[i]; - if(group[axId]) return 'g' + i; - } - return axId; -} diff --git a/src/traces/histogram/defaults.js b/src/traces/histogram/defaults.js index 747559f352a..b8e94518e72 100644 --- a/src/traces/histogram/defaults.js +++ b/src/traces/histogram/defaults.js @@ -6,7 +6,6 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var Registry = require('../../registry'); diff --git a/src/traces/violin/attributes.js b/src/traces/violin/attributes.js index ba71026aadf..1da220b2ab3 100644 --- a/src/traces/violin/attributes.js +++ b/src/traces/violin/attributes.js @@ -238,6 +238,9 @@ module.exports = { ].join(' ') }, + offsetgroup: boxAttrs.offsetgroup, + alignmentgroup: boxAttrs.alignmentgroup, + selected: boxAttrs.selected, unselected: boxAttrs.unselected, diff --git a/src/traces/violin/index.js b/src/traces/violin/index.js index 9714268d519..92531e2cbf8 100644 --- a/src/traces/violin/index.js +++ b/src/traces/violin/index.js @@ -12,6 +12,7 @@ module.exports = { attributes: require('./attributes'), layoutAttributes: require('./layout_attributes'), supplyDefaults: require('./defaults'), + crossTraceDefaults: require('../box/defaults').crossTraceDefaults, supplyLayoutDefaults: require('./layout_defaults'), calc: require('./calc'), crossTraceCalc: require('./cross_trace_calc'), diff --git a/test/image/baselines/bar-alignment-offset.png b/test/image/baselines/bar-alignment-offset.png new file mode 100644 index 00000000000..3c004bb6918 Binary files /dev/null and b/test/image/baselines/bar-alignment-offset.png differ diff --git a/test/image/baselines/bar-grouping-vs-defaults.png b/test/image/baselines/bar-grouping-vs-defaults.png new file mode 100644 index 00000000000..6b5bb751dc1 Binary files /dev/null and b/test/image/baselines/bar-grouping-vs-defaults.png differ diff --git a/test/image/baselines/bar-offsetgroups.png b/test/image/baselines/bar-offsetgroups.png new file mode 100644 index 00000000000..b5f400efa4a Binary files /dev/null and b/test/image/baselines/bar-offsetgroups.png differ diff --git a/test/image/baselines/box-alignment-offset.png b/test/image/baselines/box-alignment-offset.png new file mode 100644 index 00000000000..49531120be4 Binary files /dev/null and b/test/image/baselines/box-alignment-offset.png differ diff --git a/test/image/baselines/groups-over-matching-axes.png b/test/image/baselines/groups-over-matching-axes.png new file mode 100644 index 00000000000..635d61e7fac Binary files /dev/null and b/test/image/baselines/groups-over-matching-axes.png differ diff --git a/test/image/baselines/histogram-offsetgroups.png b/test/image/baselines/histogram-offsetgroups.png new file mode 100644 index 00000000000..e2ecc685531 Binary files /dev/null and b/test/image/baselines/histogram-offsetgroups.png differ diff --git a/test/image/baselines/violin-offsetgroups.png b/test/image/baselines/violin-offsetgroups.png new file mode 100644 index 00000000000..b1507b55d78 Binary files /dev/null and b/test/image/baselines/violin-offsetgroups.png differ diff --git a/test/image/mocks/bar-alignment-offset.json b/test/image/mocks/bar-alignment-offset.json new file mode 100644 index 00000000000..a163a42fb91 --- /dev/null +++ b/test/image/mocks/bar-alignment-offset.json @@ -0,0 +1,547 @@ +{ + "data": [ + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x", + "yaxis": "y", + "alignmentgroup": "a", + "offsetgroup": 0, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": true, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x", + "yaxis": "y", + "alignmentgroup": "a", + "offsetgroup": 1, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": true, + "hoverinfo": "text", + "hovertext": "a2", + "xaxis": "x", + "yaxis": "y", + "alignmentgroup": "a", + "offsetgroup": 2, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x2", + "yaxis": "y2", + "alignmentgroup": "a", + "offsetgroup": 0, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x2", + "yaxis": "y2", + "alignmentgroup": "a", + "offsetgroup": 1, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x2", + "yaxis": "y2", + "alignmentgroup": "a", + "offsetgroup": 1, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x3", + "yaxis": "y3", + "alignmentgroup": "a", + "offsetgroup": 0, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x3", + "yaxis": "y3", + "alignmentgroup": "a", + "offsetgroup": 1, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "b1", + "xaxis": "x3", + "yaxis": "y3", + "alignmentgroup": "b", + "offsetgroup": 1, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x4", + "yaxis": "y5", + "alignmentgroup": "a", + "offsetgroup": 0, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x4", + "yaxis": "y5", + "alignmentgroup": "a", + "offsetgroup": 1, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a2", + "xaxis": "x4", + "yaxis": "y4", + "alignmentgroup": "a", + "offsetgroup": 2, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x5", + "yaxis": "y7", + "alignmentgroup": "a", + "offsetgroup": 0, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x5", + "yaxis": "y7", + "alignmentgroup": "a", + "offsetgroup": 1, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x5", + "yaxis": "y6", + "alignmentgroup": "a", + "offsetgroup": 1, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x6", + "yaxis": "y9", + "alignmentgroup": "a", + "offsetgroup": 0, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x6", + "yaxis": "y9", + "alignmentgroup": "a", + "offsetgroup": 1, + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + { + "type": "bar", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "b1", + "xaxis": "x6", + "yaxis": "y8", + "alignmentgroup": "b", + "offsetgroup": 1, + "x": [ + 1 + ], + "y": [ + 1 + ] + } + ], + "layout": { + "legend": { + "x": 1, + "xanchor": "right", + "y": 1, + "yanchor": "bottom", + "tracegroupgap": 0 + }, + "margin": { + "l": 25, + "r": 25, + "t": 60, + "b": 80 + }, + "title": { + "text": "Each zone refers to the relationship
between the blue bar and the opaque orange bar", + "x": 0, + "xref": "paper", + "y": 0.96, + "yref": "cont", + "font": { + "size": 20 + } + }, + "shapes": [ + { + "type": "line", + "xref": "paper", + "yref": "paper", + "x0": 0.49, + "x1": 0.49, + "y0": -0.5, + "y1": 1 + } + ], + "xaxis": { + "domain": [ + 0, + 0.14666666666666667 + ], + "anchor": "y", + "title": "=alignment
≠offset" + }, + "yaxis": { + "domain": [ + 0, + 1 + ], + "anchor": "x" + }, + "xaxis2": { + "domain": [ + 0.18666666666666665, + 0.3133333333333333 + ], + "anchor": "y2", + "title": "=alignment
=offset" + }, + "yaxis2": { + "domain": [ + 0, + 1 + ], + "anchor": "x2" + }, + "xaxis3": { + "domain": [ + 0.35333333333333333, + 0.48 + ], + "anchor": "y3", + "title": "≠alignment
=offset" + }, + "yaxis3": { + "domain": [ + 0, + 1 + ], + "anchor": "x3" + }, + "xaxis4": { + "domain": [ + 0.52, + 0.6466666666666666 + ], + "anchor": "y4", + "title": "=alignment
≠offset" + }, + "yaxis4": { + "domain": [ + 0, + 0.45 + ], + "anchor": "x4" + }, + "yaxis5": { + "domain": [ + 0.55, + 1 + ], + "anchor": "x4" + }, + "xaxis5": { + "domain": [ + 0.6866666666666666, + 0.8133333333333332 + ], + "anchor": "y6", + "title": "=alignment
=offset" + }, + "yaxis6": { + "domain": [ + 0, + 0.45 + ], + "anchor": "x5" + }, + "yaxis7": { + "domain": [ + 0.55, + 1 + ], + "anchor": "x5" + }, + "xaxis6": { + "domain": [ + 0.8533333333333333, + 1 + ], + "anchor": "y8", + "title": "≠alignment
=offset" + }, + "yaxis8": { + "domain": [ + 0, + 0.45 + ], + "anchor": "x6" + }, + "yaxis9": { + "domain": [ + 0.55, + 1 + ], + "anchor": "x6" + } + } +} diff --git a/test/image/mocks/bar-grouping-vs-defaults.json b/test/image/mocks/bar-grouping-vs-defaults.json new file mode 100644 index 00000000000..1fe679360ba --- /dev/null +++ b/test/image/mocks/bar-grouping-vs-defaults.json @@ -0,0 +1,61 @@ +{ + "data": [ + { + "type": "bar", + "y": [ 1, 2, 1 ], + "yaxis": "y2" + }, + { + "type": "bar", + "y": [ 2, 1, 2 ] + }, + { + "type": "bar", + "y": [ 1, 3, 0 ] + }, + { + "type": "bar", + "y": [ 1, 2, 1 ], + "alignmentgroup": "top", + "hovertext": "alignmentgroup: top", + "xaxis": "x2", + "yaxis": "y2" + }, + { + "type": "bar", + "y": [ 2, 1, 2 ], + "hovertext": "alignmentgroup: top
offsetgroup: 1", + "alignmentgroup": "bottom", + "offsetgroup": "1", + "xaxis": "x2" + }, + { + "type": "bar", + "y": [ 1, 3, 0 ], + "hovertext": "alignmentgroup: top
offsetgroup: 2", + "alignmentgroup": "bottom", + "offsetgroup": "2", + "xaxis": "x2" + } + ], + "layout": { + "showlegend": false, + "grid": { + "rows": 2, + "columns": 2, + "roworder": "bottom to top" + }, + "colorway": [ "blue", "orange", "green" ], + "margin": { "t": 20 }, + "xaxis": { + "title": { + "text": "no alignmentgroup
no offsetgroup" + } + }, + "xaxis2": { + "title": { + "text": "with alignmentgroup
with offsetgroup" + } + } + } +} diff --git a/test/image/mocks/bar-offsetgroups.json b/test/image/mocks/bar-offsetgroups.json new file mode 100644 index 00000000000..c6281133827 --- /dev/null +++ b/test/image/mocks/bar-offsetgroups.json @@ -0,0 +1,88 @@ +{ + "data": [ + { + "type": "bar", + "x": [ "A", "B", "C", "D" ], + "y": [ 1, 2, 3, 4 ], + "offsetgroup": 1, + "hovertext": "offsetgroup: 1" + }, + { + "type": "bar", + "x": [ "A", "B", "C", "D" ], + "y": [ 2, 3, 1, 5 ], + "offsetgroup": 2, + "hovertext": "offsetgroup: 2" + }, + { + "type": "bar", + "x": [ "A", "B", "C", "D" ], + "y": [ 1, 2, 3, 4 ], + "yaxis": "y2", + "offsetgroup": 1, + "hovertext": "offsetgroup: 1" + }, + { + "type": "bar", + "x": [ "A", "B", "C", "D" ], + "y": [ 2, 3, 1, 5 ], + "yaxis": "y2", + "offsetgroup": 2, + "hovertext": "offsetgroup: 2" + }, + { + "type": "bar", + "x": [ "A", "B", "C", "D" ], + "y": [ 1, 2, 3, 4 ], + "offsetgroup": 1, + "hovertext": "offsetgroup: 1", + "xaxis": "x2" + }, + { + "type": "bar", + "x": [ "A", "B", "C", "D" ], + "y": [ 2, 3, 1, 5 ], + "offsetgroup": 2, + "hovertext": "offsetgroup: 2", + "xaxis": "x2" + }, + { + "type": "bar", + "x": [ "A", "B", "C", "D" ], + "y": [ 1, 2, 3, 4 ], + "yaxis": "y2", + "offsetgroup": 3, + "hovertext": "offsetgroup: 3", + "xaxis": "x2" + }, + { + "type": "bar", + "x": [ "A", "B", "C", "D" ], + "y": [ 2, 3, 1, 5 ], + "yaxis": "y2", + "offsetgroup": 4, + "hovertext": "offsetgroup: 4", + "xaxis": "x2" + } + ], + "layout": { + "showlegend": false, + "grid": { + "rows": 2, + "columns": 2 + }, + "title": { + "text": "Bar offset groups" + }, + "xaxis": { + "title": { + "text": "two distinct offset groups" + } + }, + "xaxis2": { + "title": { + "text": "four distinct offset groups" + } + } + } +} diff --git a/test/image/mocks/box-alignment-offset.json b/test/image/mocks/box-alignment-offset.json new file mode 100644 index 00000000000..24fbf2cf5b0 --- /dev/null +++ b/test/image/mocks/box-alignment-offset.json @@ -0,0 +1,944 @@ +{ + "data": [ + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x", + "yaxis": "y", + "alignmentgroup": "a", + "offsetgroup": 0, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": true, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x", + "yaxis": "y", + "alignmentgroup": "a", + "offsetgroup": 1, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": true, + "hoverinfo": "text", + "hovertext": "a2", + "xaxis": "x", + "yaxis": "y", + "alignmentgroup": "a", + "offsetgroup": 2, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x2", + "yaxis": "y2", + "alignmentgroup": "a", + "offsetgroup": 0, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x2", + "yaxis": "y2", + "alignmentgroup": "a", + "offsetgroup": 1, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x2", + "yaxis": "y2", + "alignmentgroup": "a", + "offsetgroup": 1, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x3", + "yaxis": "y3", + "alignmentgroup": "a", + "offsetgroup": 0, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x3", + "yaxis": "y3", + "alignmentgroup": "a", + "offsetgroup": 1, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "b1", + "xaxis": "x3", + "yaxis": "y3", + "alignmentgroup": "b", + "offsetgroup": 1, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x4", + "yaxis": "y5", + "alignmentgroup": "a", + "offsetgroup": 0, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x4", + "yaxis": "y5", + "alignmentgroup": "a", + "offsetgroup": 1, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a2", + "xaxis": "x4", + "yaxis": "y4", + "alignmentgroup": "a", + "offsetgroup": 2, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x5", + "yaxis": "y7", + "alignmentgroup": "a", + "offsetgroup": 0, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x5", + "yaxis": "y7", + "alignmentgroup": "a", + "offsetgroup": 1, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x5", + "yaxis": "y6", + "alignmentgroup": "a", + "offsetgroup": 1, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 0.6, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a0", + "xaxis": "x6", + "yaxis": "y9", + "alignmentgroup": "a", + "offsetgroup": 0, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "orange" + }, + "opacity": 1, + "name": "B", + "legendgroup": "B", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "a1", + "xaxis": "x6", + "yaxis": "y9", + "alignmentgroup": "a", + "offsetgroup": 1, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + }, + { + "type": "box", + "marker": { + "color": "blue" + }, + "opacity": 0.7, + "name": "A", + "legendgroup": "A", + "showlegend": false, + "hoverinfo": "text", + "hovertext": "b1", + "xaxis": "x6", + "yaxis": "y8", + "alignmentgroup": "b", + "offsetgroup": 1, + "y": [ + 0.2, + 0.2, + 0.6, + 1, + 0.5, + 0.4, + 0.2, + 0.7, + 0.9, + 0.1, + 0.5, + 0.3 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] + } + ], + "layout": { + "legend": { + "x": 1, + "xanchor": "right", + "y": 1, + "yanchor": "bottom", + "tracegroupgap": 0 + }, + "margin": { + "l": 25, + "r": 25, + "t": 60, + "b": 80 + }, + "title": { + "text": "Each zone refers to the relationship
between the blue box and the opaque orange box", + "x": 0, + "xref": "paper", + "y": 0.96, + "yref": "cont", + "font": { + "size": 20 + } + }, + "shapes": [ + { + "type": "line", + "xref": "paper", + "yref": "paper", + "x0": 0.49, + "x1": 0.49, + "y0": -0.5, + "y1": 1 + } + ], + "xaxis": { + "domain": [ + 0, + 0.14666666666666667 + ], + "anchor": "y", + "title": "=alignment
≠offset" + }, + "yaxis": { + "domain": [ + 0, + 1 + ], + "anchor": "x" + }, + "xaxis2": { + "domain": [ + 0.18666666666666665, + 0.3133333333333333 + ], + "anchor": "y2", + "title": "=alignment
=offset" + }, + "yaxis2": { + "domain": [ + 0, + 1 + ], + "anchor": "x2" + }, + "xaxis3": { + "domain": [ + 0.35333333333333333, + 0.48 + ], + "anchor": "y3", + "title": "≠alignment
=offset" + }, + "yaxis3": { + "domain": [ + 0, + 1 + ], + "anchor": "x3" + }, + "xaxis4": { + "domain": [ + 0.52, + 0.6466666666666666 + ], + "anchor": "y4", + "title": "=alignment
≠offset" + }, + "yaxis4": { + "domain": [ + 0, + 0.45 + ], + "anchor": "x4" + }, + "yaxis5": { + "domain": [ + 0.55, + 1 + ], + "anchor": "x4" + }, + "xaxis5": { + "domain": [ + 0.6866666666666666, + 0.8133333333333332 + ], + "anchor": "y6", + "title": "=alignment
=offset" + }, + "yaxis6": { + "domain": [ + 0, + 0.45 + ], + "anchor": "x5" + }, + "yaxis7": { + "domain": [ + 0.55, + 1 + ], + "anchor": "x5" + }, + "xaxis6": { + "domain": [ + 0.8533333333333333, + 1 + ], + "anchor": "y8", + "title": "≠alignment
=offset" + }, + "yaxis8": { + "domain": [ + 0, + 0.45 + ], + "anchor": "x6" + }, + "yaxis9": { + "domain": [ + 0.55, + 1 + ], + "anchor": "x6" + }, + "boxmode": "group" + } +} diff --git a/test/image/mocks/groups-over-matching-axes.json b/test/image/mocks/groups-over-matching-axes.json new file mode 100644 index 00000000000..7a31a6ac370 --- /dev/null +++ b/test/image/mocks/groups-over-matching-axes.json @@ -0,0 +1,111 @@ +{ + "data": [ + { + "type": "violin", + "x": ["A", "B"], + "y": [1, 2], + "offsetgroup": 1, + "hovertext": "offsetgroup: 1" + }, + { + "type": "violin", + "x": ["A", "B"], + "y": [2, 3], + "offsetgroup": 2, + "hovertext": "offsetgroup: 2" + }, + { + "type": "box", + "x": ["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"], + "y": [1, 2, 2, 2, 3, 1, 2, 2, 2, 3], + "yaxis": "y2", + "offsetgroup": 1, + "hovertext": "offsetgroup: 1" + }, + { + "type": "box", + "x": ["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"], + "y": [1, 2, 2, 2, 3, 1, 2, 2, 2, 3], + "yaxis": "y2", + "offsetgroup": 2, + "hovertext": "offsetgroup: 2" + }, + { + "type": "violin", + "x": ["A", "B"], + "y": [1, 2], + "offsetgroup": 1, + "hovertext": "offsetgroup: 1", + "xaxis": "x2" + }, + { + "type": "violin", + "x": ["A", "B"], + "y": [2, 3], + "offsetgroup": 2, + "hovertext": "offsetgroup: 2", + "xaxis": "x2" + }, + { + "type": "box", + "x": ["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"], + "y": [1, 2, 2, 2, 3, 1, 2, 2, 2, 3], + "yaxis": "y2", + "offsetgroup": 3, + "hovertext": "offsetgroup: 3", + "xaxis": "x2" + }, + { + "type": "box", + "x": ["A", "A", "A", "A", "A", "B", "B", "B", "B", "B"], + "y": [1, 2, 2, 2, 3, 1, 2, 2, 2, 3], + "yaxis": "y2", + "offsetgroup": 4, + "hovertext": "offsetgroup: 4", + "xaxis": "x2" + }, + { + "type": "bar", + "name": "some bar", + "marker": {"color": "black"}, + "x": ["B"], + "y": [2], + "offsetgroup": 3, + "hovertext": "offsetgroup: 3", + "xaxis": "x2" + }, + { + "type": "bar", + "name": "some other bar", + "marker": {"color": "black"}, + "x": ["A"], + "y": [0.5], + "offsetgroup": 1, + "hovertext": "offsetgroup: 1", + "yaxis": "y2" + } + ], + "layout": { + "boxmode": "group", + "violinmode": "group", + "showlegend": false, + "grid": { + "rows": 2, + "columns": 2 + }, + "title": { + "text": "4 offsetgroups in total across matching x-axes" + }, + "xaxis": { + "title": { + "text": "offsetgroup 1-2-1-2" + } + }, + "xaxis2": { + "title": { + "text": "offsetgroup 1-2-3-4" + }, + "matches": "x" + } + } +} diff --git a/test/image/mocks/histogram-offsetgroups.json b/test/image/mocks/histogram-offsetgroups.json new file mode 100644 index 00000000000..42cb0d4f567 --- /dev/null +++ b/test/image/mocks/histogram-offsetgroups.json @@ -0,0 +1,80 @@ +{ + "data": [ + { + "type": "histogram", + "y": [ 1, 2, 3, 4 ], + "offsetgroup": 1, + "text": "offsetgroup: 1" + }, + { + "type": "histogram", + "y": [ 2, 3, 1, 5 ], + "offsetgroup": 2, + "text": "offsetgroup: 2" + }, + { + "type": "histogram", + "y": [ 1, 2, 3, 4 ], + "yaxis": "y2", + "offsetgroup": 1, + "text": "offsetgroup: 1" + }, + { + "type": "histogram", + "y": [ 2, 3, 1, 5 ], + "yaxis": "y2", + "offsetgroup": 2, + "text": "offsetgroup: 2" + }, + { + "type": "histogram", + "y": [ 1, 2, 3, 4 ], + "offsetgroup": 1, + "xaxis": "x2", + "text": "offsetgroup: 1" + }, + { + "type": "histogram", + "y": [ 2, 3, 1, 5 ], + "offsetgroup": 2, + "xaxis": "x2", + "text": "offsetgroup: 2" + }, + { + "type": "histogram", + "y": [ 1, 2, 3, 4 ], + "yaxis": "y2", + "offsetgroup": 3, + "xaxis": "x2", + "text": "offsetgroup: 3" + }, + { + "type": "histogram", + "y": [ 2, 3, 1, 5 ], + "yaxis": "y2", + "offsetgroup": 4, + "xaxis": "x2", + "text": "offsetgroup: 4" + } + ], + "layout": { + "showlegend": false, + "grid": { + "rows": 2, + "columns": 2 + }, + "title": { + "text": "Histogram offset groups" + }, + "yaxis": { + "title": { + "text": "two distinct
offset groups" + } + }, + "yaxis2": { + "title": { + "text": "four distinct
offset groups" + } + } + } +} diff --git a/test/image/mocks/violin-offsetgroups.json b/test/image/mocks/violin-offsetgroups.json new file mode 100644 index 00000000000..4aaac563003 --- /dev/null +++ b/test/image/mocks/violin-offsetgroups.json @@ -0,0 +1,89 @@ +{ + "data": [ + { + "type": "violin", + "x": [ "A", "B", "C", "D" ], + "y": [ 1, 2, 3, 4 ], + "offsetgroup": 1, + "hovertext": "offsetgroup: 1" + }, + { + "type": "violin", + "x": [ "A", "B", "C", "D" ], + "y": [ 2, 3, 1, 5 ], + "offsetgroup": 2, + "hovertext": "offsetgroup: 2" + }, + { + "type": "violin", + "x": [ "A", "B", "C", "D" ], + "y": [ 1, 2, 3, 4 ], + "yaxis": "y2", + "offsetgroup": 1, + "hovertext": "offsetgroup: 1" + }, + { + "type": "violin", + "x": [ "A", "B", "C", "D" ], + "y": [ 2, 3, 1, 5 ], + "yaxis": "y2", + "offsetgroup": 2, + "hovertext": "offsetgroup: 2" + }, + { + "type": "violin", + "x": [ "A", "B", "C", "D" ], + "y": [ 1, 2, 3, 4 ], + "offsetgroup": 1, + "hovertext": "offsetgroup: 1", + "xaxis": "x2" + }, + { + "type": "violin", + "x": [ "A", "B", "C", "D" ], + "y": [ 2, 3, 1, 5 ], + "offsetgroup": 2, + "hovertext": "offsetgroup: 2", + "xaxis": "x2" + }, + { + "type": "violin", + "x": [ "A", "B", "C", "D" ], + "y": [ 1, 2, 3, 4 ], + "yaxis": "y2", + "offsetgroup": 3, + "hovertext": "offsetgroup: 3", + "xaxis": "x2" + }, + { + "type": "violin", + "x": [ "A", "B", "C", "D" ], + "y": [ 2, 3, 1, 5 ], + "yaxis": "y2", + "offsetgroup": 4, + "hovertext": "offsetgroup: 4", + "xaxis": "x2" + } + ], + "layout": { + "violinmode": "group", + "showlegend": false, + "grid": { + "rows": 2, + "columns": 2 + }, + "title": { + "text": "Violin offset groups" + }, + "xaxis": { + "title": { + "text": "two distinct offset groups" + } + }, + "xaxis2": { + "title": { + "text": "four distinct offset groups" + } + } + } +} diff --git a/test/jasmine/tests/bar_test.js b/test/jasmine/tests/bar_test.js index e79ea3627a5..5a0934734be 100644 --- a/test/jasmine/tests/bar_test.js +++ b/test/jasmine/tests/bar_test.js @@ -210,6 +210,22 @@ describe('Bar.supplyDefaults', function() { expect(traceOut.xcalendar).toBe('coptic'); expect(traceOut.ycalendar).toBe('ethiopian'); }); + + it('should not include alignementgroup/offsetgroup when barmode is not *group*', function() { + var gd = { + data: [{type: 'bar', y: [1], alignmentgroup: 'a', offsetgroup: '1'}], + layout: {barmode: 'group'} + }; + + supplyAllDefaults(gd); + expect(gd._fullData[0].alignmentgroup).toBe('a', 'alignementgroup'); + expect(gd._fullData[0].offsetgroup).toBe('1', 'offsetgroup'); + + gd.layout.barmode = 'stack'; + supplyAllDefaults(gd); + expect(gd._fullData[0].alignmentgroup).toBe(undefined, 'alignementgroup'); + expect(gd._fullData[0].offsetgroup).toBe(undefined, 'offsetgroup'); + }); }); describe('bar calc / crossTraceCalc (formerly known as setPositions)', function() { diff --git a/test/jasmine/tests/box_test.js b/test/jasmine/tests/box_test.js index 694735de757..cd655fc47d7 100644 --- a/test/jasmine/tests/box_test.js +++ b/test/jasmine/tests/box_test.js @@ -8,6 +8,7 @@ var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var failTest = require('../assets/fail_test'); var mouseEvent = require('../assets/mouse_event'); +var supplyAllDefaults = require('../assets/supply_defaults'); var customAssertions = require('../assets/custom_assertions'); var assertHoverLabelContent = customAssertions.assertHoverLabelContent; @@ -150,6 +151,22 @@ describe('Test boxes supplyDefaults', function() { expect(traceOut.marker).toBeDefined(); expect(traceOut.text).toBeDefined(); }); + + it('should not include alignementgroup/offsetgroup when boxmode is not *group*', function() { + var gd = { + data: [{type: 'box', y: [1], alignmentgroup: 'a', offsetgroup: '1'}], + layout: {boxmode: 'group'} + }; + + supplyAllDefaults(gd); + expect(gd._fullData[0].alignmentgroup).toBe('a', 'alignementgroup'); + expect(gd._fullData[0].offsetgroup).toBe('1', 'offsetgroup'); + + gd.layout.boxmode = 'overlay'; + supplyAllDefaults(gd); + expect(gd._fullData[0].alignmentgroup).toBe(undefined, 'alignementgroup'); + expect(gd._fullData[0].offsetgroup).toBe(undefined, 'offsetgroup'); + }); }); describe('Test box hover:', function() { diff --git a/test/jasmine/tests/violin_test.js b/test/jasmine/tests/violin_test.js index 9cca890e9c7..930e9c1dbbc 100644 --- a/test/jasmine/tests/violin_test.js +++ b/test/jasmine/tests/violin_test.js @@ -159,6 +159,22 @@ describe('Test violin defaults', function() { expect(traceOut.scalemode).toBe('width'); expect(traceOut.scalegroup).toBe(''); }); + + it('should not include alignementgroup/offsetgroup when violinmode is not *group*', function() { + var gd = { + data: [{type: 'violin', y: [1], alignmentgroup: 'a', offsetgroup: '1'}], + layout: {violinmode: 'group'} + }; + + supplyAllDefaults(gd); + expect(gd._fullData[0].alignmentgroup).toBe('a', 'alignementgroup'); + expect(gd._fullData[0].offsetgroup).toBe('1', 'offsetgroup'); + + gd.layout.violinmode = 'overlay'; + supplyAllDefaults(gd); + expect(gd._fullData[0].alignmentgroup).toBe(undefined, 'alignementgroup'); + expect(gd._fullData[0].offsetgroup).toBe(undefined, 'offsetgroup'); + }); }); describe('Test violin calc:', function() {