diff --git a/dev/percy/panelTest.json b/dev/percy/panelTest.json index ebf24d860..e6e745b20 100644 --- a/dev/percy/panelTest.json +++ b/dev/percy/panelTest.json @@ -73,7 +73,7 @@ "sizesrc": "x1", "sizemode": "diameter" }, - "fill": "tozerox", + "stackgroup": 1, "mode": "lines+markers+text", "line": { "dash": "solid", diff --git a/package.json b/package.json index e9dd5d680..a2db1d805 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "fast-isnumeric": "^1.1.1", "immutability-helper": "^2.7.1", "plotly-icons": "1.2.2", - "plotly.js": "1.40.1", + "plotly.js": "1.41.0", "prop-types": "^15.5.10", "raf": "^3.4.0", "react-color": "^2.13.8", diff --git a/src/components/containers/__tests__/Layout-test.js b/src/components/containers/__tests__/Layout-test.js index 6620b3a91..dbb12a17e 100644 --- a/src/components/containers/__tests__/Layout-test.js +++ b/src/components/containers/__tests__/Layout-test.js @@ -13,17 +13,16 @@ Layouts.forEach(Layout => { describe(`<${Layout.displayName}>`, () => { it(`wraps container with fullValue pointing to gd._fullLayout`, () => { const wrapper = mount( - + - + ) - .find('[attr="width"]') + .find('[attr="height"]') .find(NumericInput); - expect(wrapper.prop('value')).toBe(100); }); @@ -32,22 +31,22 @@ Layouts.forEach(Layout => { const wrapper = mount( - + ) - .find('[attr="width"]') + .find('[attr="height"]') .find(NumericInput); - const widthUpdate = 200; - wrapper.prop('onChange')(widthUpdate); + const heightUpdate = 200; + wrapper.prop('onChange')(heightUpdate); const payload = beforeUpdateLayout.mock.calls[0][0]; - expect(payload).toEqual({update: {width: widthUpdate}}); + expect(payload).toEqual({update: {height: heightUpdate}}); }); }); }); diff --git a/src/components/fields/TraceSelector.js b/src/components/fields/TraceSelector.js index b920e64a7..48c02e958 100644 --- a/src/components/fields/TraceSelector.js +++ b/src/components/fields/TraceSelector.js @@ -7,14 +7,11 @@ import { plotlyTraceToCustomTrace, computeTraceOptionsFromSchema, } from 'lib'; +import {TRACES_WITH_GL} from 'lib/constants'; import {TraceTypeSelector, TraceTypeSelectorButton, RadioBlocks} from 'components/widgets'; import Field from './Field'; import {CogIcon} from 'plotly-icons'; -export const glAvailable = type => { - return ['scatter', 'scatterpolar', 'scattergl', 'scatterpolargl'].includes(type); -}; - class TraceSelector extends Component { constructor(props, context) { super(props, context); @@ -125,14 +122,14 @@ class TraceSelector extends Component { }) } /> - {!glAvailable(this.props.container.type) ? ( + {!TRACES_WITH_GL.includes(this.props.container.type) ? ( '' ) : ( )} - {!(glAvailable(this.props.container.type) && this.state.showGlControls) ? ( + {!(TRACES_WITH_GL.includes(this.props.container.type) && this.state.showGlControls) ? ( '' ) : ( diff --git a/src/components/fields/__tests__/TraceSelector-test.js b/src/components/fields/__tests__/TraceSelector-test.js index 98ba85d8b..2b9c40322 100644 --- a/src/components/fields/__tests__/TraceSelector-test.js +++ b/src/components/fields/__tests__/TraceSelector-test.js @@ -147,7 +147,7 @@ describe('TraceSelector', () => { }); }); - it('updates type=scatter fill=tozeroy when type=area', () => { + it('updates type=scatter stackgroup=1 when type=area', () => { const beforeUpdateTraces = jest.fn(); const editorProps = { ...fixtures.scatter({data: [{type: 'scatter', mode: 'markers'}]}), @@ -166,6 +166,6 @@ describe('TraceSelector', () => { innerDropdown.prop('onChange')('area'); const payload = beforeUpdateTraces.mock.calls[0][0]; - expect(payload.update).toEqual({fill: 'tozeroy', type: 'scatter'}); + expect(payload.update).toEqual({type: 'scatter', mode: 'lines', stackgroup: 1}); }); }); diff --git a/src/components/fields/derived.js b/src/components/fields/derived.js index 07f3d24b6..31ea38006 100644 --- a/src/components/fields/derived.js +++ b/src/components/fields/derived.js @@ -541,7 +541,7 @@ export const HoverInfo = connectToContainer(UnconnectedFlaglist, { {label: _('B'), value: 'b'}, {label: _('C'), value: 'c'}, ]; - } else if (['scatterpolar', 'scatterpolargl'].includes(container.type)) { + } else if (['scatterpolar', 'scatterpolargl', 'barpolar'].includes(container.type)) { options = [{label: _('R'), value: 'r'}, {label: _('Theta'), value: 'theta'}]; } else if (container.type === 'pie') { options = [ diff --git a/src/components/widgets/TraceTypeSelector.js b/src/components/widgets/TraceTypeSelector.js index e5829bd8f..3f9bbbcc9 100644 --- a/src/components/widgets/TraceTypeSelector.js +++ b/src/components/widgets/TraceTypeSelector.js @@ -2,8 +2,8 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {SearchIcon, ThumnailViewIcon, GraphIcon} from 'plotly-icons'; import Modal from 'components/containers/Modal'; -import {glAvailable} from 'components/fields/TraceSelector'; import {traceTypeToPlotlyInitFigure, renderTraceIcon, plotlyTraceToCustomTrace} from 'lib'; +import {TRACES_WITH_GL} from 'lib/constants'; const renderActionItems = (actionItems, item) => actionItems @@ -83,8 +83,8 @@ class TraceTypeSelector extends Component { } = this.props; const computedValue = traceTypeToPlotlyInitFigure(value); if ( - (type.endsWith('gl') || (!glAvailable(type) && glByDefault)) && - glAvailable(computedValue.type) && + (type.endsWith('gl') || (!TRACES_WITH_GL.includes(type) && glByDefault)) && + TRACES_WITH_GL.includes(computedValue.type) && !computedValue.type.endsWith('gl') ) { computedValue.type += 'gl'; diff --git a/src/default_panels/GraphCreatePanel.js b/src/default_panels/GraphCreatePanel.js index 5eb8f894f..6cfd2df5a 100644 --- a/src/default_panels/GraphCreatePanel.js +++ b/src/default_panels/GraphCreatePanel.js @@ -108,7 +108,7 @@ const GraphCreatePanel = (props, {localize: _, setPanel}) => { - + ( - - + + + ); diff --git a/src/default_panels/StyleLayoutPanel.js b/src/default_panels/StyleLayoutPanel.js index b68cfd49d..fcc71cb20 100644 --- a/src/default_panels/StyleLayoutPanel.js +++ b/src/default_panels/StyleLayoutPanel.js @@ -12,6 +12,7 @@ import { TraceRequiredPanel, VisibilitySelect, HovermodeDropdown, + Flaglist, } from '../components'; import {HoverColor} from '../components/fields/derived'; @@ -89,6 +90,16 @@ const StyleLayoutPanel = (props, {localize: _}) => ( clearable={false} /> + + + ( + + + + + + ( + + + + + + ( 'scattergl', 'scatterpolar', 'scatterpolargl', + 'barpolar', 'pie', 'scatter3d', 'scatterternary', diff --git a/src/lib/__tests__/connectLayoutToPlot-test.js b/src/lib/__tests__/connectLayoutToPlot-test.js index 6b8903d92..fd57fcd15 100644 --- a/src/lib/__tests__/connectLayoutToPlot-test.js +++ b/src/lib/__tests__/connectLayoutToPlot-test.js @@ -13,15 +13,15 @@ Layouts.forEach(Layout => { describe(`<${Layout.displayName}>`, () => { it(`wraps container with fullValue pointing to gd._fullLayout`, () => { const wrapper = mount( - + - + ) - .find('[attr="width"]') + .find('[attr="height"]') .find(NumericInput); expect(wrapper.prop('value')).toBe(100); @@ -32,22 +32,22 @@ Layouts.forEach(Layout => { const wrapper = mount( - + ) - .find('[attr="width"]') + .find('[attr="height"]') .find(NumericInput); - const widthUpdate = 200; - wrapper.prop('onChange')(widthUpdate); + const heightUpdate = 200; + wrapper.prop('onChange')(heightUpdate); const payload = beforeUpdateLayout.mock.calls[0][0]; - expect(payload).toEqual({update: {width: widthUpdate}}); + expect(payload).toEqual({update: {height: heightUpdate}}); }); it(`automatically computes min and max defaults`, () => { diff --git a/src/lib/computeTraceOptionsFromSchema.js b/src/lib/computeTraceOptionsFromSchema.js index af7dd89f1..1c0da0522 100644 --- a/src/lib/computeTraceOptionsFromSchema.js +++ b/src/lib/computeTraceOptionsFromSchema.js @@ -128,6 +128,10 @@ function computeTraceOptionsFromSchema(schema, _, context) { value: 'scatterpolargl', label: _('Polar Scatter GL'), }, + { + value: 'barpolar', + label: _('Polar Bar'), + }, ].filter(obj => traceTypes.indexOf(obj.value) !== -1); const traceIndex = traceType => traceOptions.findIndex(opt => opt.value === traceType); diff --git a/src/lib/constants.js b/src/lib/constants.js index 42d44a9c1..cea82b550 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -73,7 +73,7 @@ export const TRACE_TO_AXIS = { gl3d: ['scatter3d', 'surface', 'mesh3d', 'cone', 'streamtube'], geo: ['scattergeo', 'choropleth'], mapbox: ['scattermapbox'], - polar: ['scatterpolar', 'scatterpolargl'], + polar: ['scatterpolar', 'scatterpolargl', 'barpolar'], }; // Note: scene, and xaxis/yaxis were added for convenience sake even though they're not subplot types @@ -117,6 +117,8 @@ export const TRANSFORMABLE_TRACES = [ 'histogram2d', ]; +export const TRACES_WITH_GL = ['scatter', 'scatterpolar', 'scattergl', 'scatterpolargl']; + export const COLORS = { charcoal: '#444444', white: '#ffffff', diff --git a/src/lib/customTraceType.js b/src/lib/customTraceType.js index 5047c2f24..5de6d03ac 100644 --- a/src/lib/customTraceType.js +++ b/src/lib/customTraceType.js @@ -16,7 +16,8 @@ export function plotlyTraceToCustomTrace(trace) { if ( (type === 'scatter' || type === 'scattergl') && - ['tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'].includes(trace.fill) + ((trace.stackgroup !== null && trace.stackgroup !== undefined) || // eslint-disable-line no-undefined + ['tozeroy', 'tozerox', 'tonexty', 'tonextx', 'toself', 'tonext'].includes(trace.fill)) ) { return 'area'; } else if ( @@ -37,7 +38,7 @@ export function traceTypeToPlotlyInitFigure(traceType, gl = '') { case 'scatter': return {type: 'scatter' + gl, mode: 'markers', fill: 'none'}; case 'area': - return {type: 'scatter' + gl, fill: 'tozeroy'}; + return {type: 'scatter' + gl, mode: 'lines', stackgroup: 1}; case 'scatterpolar': return {type: 'scatterpolar' + gl}; case 'ohlc': @@ -66,8 +67,6 @@ export function traceTypeToPlotlyInitFigure(traceType, gl = '') { case 'violin': return { type: 'violin', - box: {visible: false}, - meanline: {visible: false}, bandwidth: 0, }; case 'line3d': diff --git a/src/lib/test-utils.js b/src/lib/test-utils.js index d235cd7d3..311d992ef 100644 --- a/src/lib/test-utils.js +++ b/src/lib/test-utils.js @@ -65,7 +65,7 @@ const fixtures = { { type: 'scatter', mode: 'markers+lines', - fill: 'tozeroy', + stackgroup: 1, xsrc: 'x1', ysrc: 'y1', }, @@ -136,7 +136,7 @@ function setupGraphDiv(figure) { mockMissingSvgApis(); - plotly.plot(gd, figure); + plotly.newPlot(gd, figure); return gd; } diff --git a/src/lib/traceTypes.js b/src/lib/traceTypes.js index 3f499163e..dfa44b260 100644 --- a/src/lib/traceTypes.js +++ b/src/lib/traceTypes.js @@ -177,6 +177,11 @@ export const traceTypes = _ => [ label: _('Polar Scatter'), category: chartCategory(_).SPECIALIZED, }, + { + value: 'barpolar', + label: _('Polar Bar'), + category: chartCategory(_).SPECIALIZED, + }, { value: 'scatterternary', label: _('Ternary Scatter'),