diff --git a/README.md b/README.md index 50aee5a7c..f7cab56cf 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ Simple component that takes in props and renders. * ``: `` whose children are replicated into `` connected to traces via `connectTraceToPlot()`. * ``: `` whose children are connected to the `layout` figure key +* ``: `
` whose children are connected to the `layout` figure key * ``: `` renders `` if no trace data is set, can add extra conditions (i.e. an array of functions that will be run) with the `extraConditions` prop and a matching array with extraEmptyPanelMessages to show when those conditions are not met. * ``: `` whose children are replicated into `` connected to annotations via `connectAnnotationToLayout()`. For use in a ``. * ``: `` whose children are replicated into `` connected to shapes via `connectShapeToLayout()`. For use in a ``. @@ -117,7 +118,6 @@ For use in containers bound to traces e.g. as children of ``: * ``: renders as a `` useful for `data[].line.dash` * ``: renders as a `` useful for `data[].line.shape` * ``: renders as a `` useful for `data[].marker.symbol` -* `` and ``: renders as a `` for use in trace-connected containers where normal `` would be bound to the `data` key instead of the `layout` key in the figure e.g. `layout.bargap` or `layout.barwidth`. * ``: renders as a `` useful for `layout.*.xref/yref` where the allowable values are `paper|[axis]` * ``: renders a set of controls that control a trace's error bars (`visibility`, `type`, `value`, `valueminus`, `array`, `arrayminus`) diff --git a/src/EditorControls.js b/src/EditorControls.js index 2df76060e..68535dbcc 100644 --- a/src/EditorControls.js +++ b/src/EditorControls.js @@ -2,7 +2,11 @@ import DefaultEditor from './DefaultEditor'; import PropTypes from 'prop-types'; import React, {Component} from 'react'; import {bem} from './lib'; -import {maybeClearAxisTypes} from './shame'; +import { + shamefullyClearAxisTypes, + shamefullyAdjustAxisRef, + shamefullyAdjustGeo, +} from './shame'; import {EDITOR_ACTIONS} from './lib/constants'; import isNumeric from 'fast-isnumeric'; import nestedProperty from 'plotly.js/src/lib/nested_property'; @@ -43,31 +47,6 @@ class EditorControls extends Component { }; } - maybeAdjustAxisRef(payload) { - const {graphDiv} = this.props; - if (payload.tracesNeedingAxisAdjustment) { - payload.tracesNeedingAxisAdjustment.forEach(trace => { - const axis = trace[payload.axisAttrToAdjust].charAt(0); - const currentAxisIdNumber = Number( - trace[payload.axisAttrToAdjust].slice(1) - ); - const adjustedAxisIdNumber = currentAxisIdNumber - 1; - - const currentAxisLayoutProperties = { - ...graphDiv.layout[payload.axisAttrToAdjust + currentAxisIdNumber], - }; - - // for cases when we're adjusting x2 => x, so that it becomes x not x1 - graphDiv.data[trace.index][payload.axisAttrToAdjust] = - adjustedAxisIdNumber === 1 ? axis : axis + adjustedAxisIdNumber; - - graphDiv.layout[ - payload.axisAttrToAdjust + adjustedAxisIdNumber - ] = currentAxisLayoutProperties; - }); - } - } - handleUpdate({type, payload}) { const {graphDiv} = this.props; @@ -77,11 +56,8 @@ class EditorControls extends Component { this.props.beforeUpdateTraces(payload); } - // until we start utilizing Plotly.react in `react-plotly.js` - // force clear axes types when a `src` has changed. - maybeClearAxisTypes(graphDiv, payload.traceIndexes, payload.update); - - this.maybeAdjustAxisRef(payload); + shamefullyClearAxisTypes(graphDiv, payload); + shamefullyAdjustAxisRef(graphDiv, payload); for (let i = 0; i < payload.traceIndexes.length; i++) { for (const attr in payload.update) { @@ -108,6 +84,8 @@ class EditorControls extends Component { break; case EDITOR_ACTIONS.UPDATE_LAYOUT: + shamefullyAdjustGeo(graphDiv, payload); + if (this.props.beforeUpdateLayout) { this.props.beforeUpdateLayout(payload); } diff --git a/src/components/containers/AxesFold.js b/src/components/containers/AxesFold.js index 7bc40899e..0d763af0e 100644 --- a/src/components/containers/AxesFold.js +++ b/src/components/containers/AxesFold.js @@ -5,18 +5,12 @@ import React, {Component} from 'react'; import {connectAxesToLayout} from 'lib'; class AxesFold extends Component { - renderAxesSelector() { - if (this.props.options.length > 1) { - return ; - } - return null; - } - render() { - return this.props.children ? ( + const {children, options} = this.props; + return options.length && children ? ( - {this.renderAxesSelector()} - {this.props.children} + {options.length === 1 ? null : } + {children} ) : null; } diff --git a/src/components/containers/Fold.js b/src/components/containers/Fold.js index 192c7f330..4cb2c69d7 100644 --- a/src/components/containers/Fold.js +++ b/src/components/containers/Fold.js @@ -26,25 +26,18 @@ class Fold extends Component { this.foldVisible = false; React.Children.forEach(nextProps.children, child => { - if (!child) { + if (!child || this.foldVisible) { return; } if (child.props.attr) { // attr components force fold open if they are visible - let plotProps; - if (child.type.supplyPlotProps) { - plotProps = child.type.supplyPlotProps(child.props, nextContext); - if (child.type.modifyPlotProps) { - child.type.modifyPlotProps(child.props, nextContext, plotProps); - } - } else { - plotProps = unpackPlotProps(child.props, nextContext); + const plotProps = unpackPlotProps(child.props, nextContext); + if (child.type.modifyPlotProps) { + child.type.modifyPlotProps(child.props, nextContext, plotProps); } - if (plotProps.isVisible) { - this.foldVisible = true; - } + this.foldVisible = this.foldVisible || plotProps.isVisible; return; } diff --git a/src/components/containers/Section.js b/src/components/containers/Section.js index 2cd50ea44..f196a2ec5 100644 --- a/src/components/containers/Section.js +++ b/src/components/containers/Section.js @@ -1,4 +1,4 @@ -import React, {Component, cloneElement} from 'react'; +import React, {Component} from 'react'; import PropTypes from 'prop-types'; import { containerConnectedContextTypes, @@ -11,45 +11,38 @@ class Section extends Component { constructor(props, context) { super(props, context); - this.children = null; this.sectionVisible = false; - this.processAndSetChildren(props, context); + this.determineVisibility(props, context); } componentWillReceiveProps(nextProps, nextContext) { - this.processAndSetChildren(nextProps, nextContext); + this.determineVisibility(nextProps, nextContext); } - processAndSetChildren(nextProps, nextContext) { + determineVisibility(nextProps, nextContext) { const {isVisible} = unpackPlotProps(nextProps, nextContext); - this.sectionVisible = isVisible === true; + this.sectionVisible = Boolean(isVisible); + + React.Children.forEach(nextProps.children, child => { + if (!child || this.foldVisible) { + return; + } - this.children = React.Children.map(nextProps.children, child => { if (child.props.attr) { - let plotProps; - if (child.type.supplyPlotProps) { - plotProps = child.type.supplyPlotProps(child.props, nextContext); - if (child.type.modifyPlotProps) { - child.type.modifyPlotProps(child.props, nextContext, plotProps); - } - } else { - plotProps = unpackPlotProps(child.props, nextContext); + const plotProps = unpackPlotProps(child.props, nextContext); + if (child.type.modifyPlotProps) { + child.type.modifyPlotProps(child.props, nextContext, plotProps); } - - // assign plotProps as a prop of children. If they are connectedToContainer - // it will see plotProps and skip recomputing them. this.sectionVisible = this.sectionVisible || plotProps.isVisible; - return cloneElement(child, {plotProps}); + return; } if (!(child.type.plotly_editor_traits || {}).no_visibility_forcing) { // non-attr components force visibility (unless they don't via traits) this.sectionVisible = true; - return child; + return; } - - return child; }); } @@ -60,7 +53,7 @@ class Section extends Component { return (
{this.props.name && } - {this.children} + {this.props.children}
); } diff --git a/src/components/containers/__tests__/Section-test.js b/src/components/containers/__tests__/Section-test.js index 1b20bdff5..753323c43 100644 --- a/src/components/containers/__tests__/Section-test.js +++ b/src/components/containers/__tests__/Section-test.js @@ -27,8 +27,6 @@ describe('Section', () => { ).find(Section); expect(wrapper.children().length).toBe(1); - expect(wrapper.find(Flaglist).props().plotProps.isVisible).toBe(true); - expect(wrapper.find(Numeric).props().plotProps.isVisible).toBe(false); }); it('is visible if it contains any non attr children', () => { diff --git a/src/components/containers/derived.js b/src/components/containers/derived.js index 927e403f3..32e4f2ab4 100644 --- a/src/components/containers/derived.js +++ b/src/components/containers/derived.js @@ -6,6 +6,7 @@ import PropTypes from 'prop-types'; import {connectLayoutToPlot, containerConnectedContextTypes} from 'lib'; const LayoutPanel = connectLayoutToPlot(Panel); +const LayoutSection = connectLayoutToPlot(Section); const TraceTypeSection = (props, context) => { const {fullContainer, fullData} = context; @@ -34,4 +35,4 @@ TraceTypeSection.defaultProps = { traceTypes: [], }; -export {LayoutPanel, TraceTypeSection}; +export {LayoutPanel, LayoutSection, TraceTypeSection}; diff --git a/src/components/containers/index.js b/src/components/containers/index.js index 27e3b1865..572fa55c4 100644 --- a/src/components/containers/index.js +++ b/src/components/containers/index.js @@ -12,7 +12,7 @@ import Section from './Section'; import TraceAccordion from './TraceAccordion'; import TransformAccordion from './TransformAccordion'; import TraceMarkerSection from './TraceMarkerSection'; -import {LayoutPanel, TraceTypeSection} from './derived'; +import {LayoutPanel, LayoutSection, TraceTypeSection} from './derived'; import TraceRequiredPanel from './TraceRequiredPanel'; import SingleSidebarItem from './SingleSidebarItem'; import ModalProvider from './ModalProvider'; @@ -35,6 +35,7 @@ export { TraceMarkerSection, TraceRequiredPanel, LayoutPanel, + LayoutSection, AxesFold, SingleSidebarItem, TraceTypeSection, diff --git a/src/components/fields/AxisCreator.js b/src/components/fields/AxisCreator.js index 11d4c6fde..6d3d89398 100644 --- a/src/components/fields/AxisCreator.js +++ b/src/components/fields/AxisCreator.js @@ -10,7 +10,6 @@ import { traceTypeToAxisType, getAxisTitle, axisIdToAxisName, - unpackPlotProps, } from 'lib'; class UnconnectedNewAxisCreator extends Component { @@ -184,7 +183,6 @@ AxisCreator.contextTypes = { }; export default connectToContainer(AxisCreator, { - supplyPlotProps: (props, context) => unpackPlotProps(props, {...context}), modifyPlotProps: (props, context, plotProps) => { const {data} = context; const {fullContainer} = plotProps; diff --git a/src/components/fields/__tests__/TraceSelector-test.js b/src/components/fields/__tests__/TraceSelector-test.js index 357f06c7f..346a34f9d 100644 --- a/src/components/fields/__tests__/TraceSelector-test.js +++ b/src/components/fields/__tests__/TraceSelector-test.js @@ -14,6 +14,7 @@ describe('TraceSelector', () => { ...fixtures.scatter({data: [{mode: null, xsrc: null, ysrc: null}]}), onUpdate: jest.fn(), }; + const wrapper = mount( @@ -23,8 +24,6 @@ describe('TraceSelector', () => { ).find(TraceSelector); const innerDropdown = wrapper.find(Dropdown); - - expect(wrapper.props().plotProps.container.mode).toBe('markers'); expect(innerDropdown.prop('value')).toEqual('scatter'); }); @@ -44,7 +43,6 @@ describe('TraceSelector', () => { ).find(TraceSelector); const innerDropdown = wrapper.find(Dropdown); - expect(innerDropdown.prop('value')).toEqual('line'); }); @@ -66,7 +64,6 @@ describe('TraceSelector', () => { ).find(TraceSelector); const innerDropdown = wrapper.find(Dropdown); - expect(wrapper.props().plotProps.container.mode).toBe('lines+markers'); expect(innerDropdown.prop('value')).toEqual('line'); }); diff --git a/src/components/fields/derived.js b/src/components/fields/derived.js index eba79f4a6..3fad42559 100644 --- a/src/components/fields/derived.js +++ b/src/components/fields/derived.js @@ -5,12 +5,10 @@ import {UnconnectedNumeric} from './Numeric'; import {UnconnectedAxisRangeValue} from './AxisRangeValue'; import {UnconnectedRadio} from './Radio'; import { - connectLayoutToPlot, connectToContainer, getAllAxes, getAxisTitle, axisIdToAxisName, - supplyLayoutPlotProps, } from 'lib'; export const AxisAnchorDropdown = connectToContainer(UnconnectedDropdown, { @@ -283,29 +281,16 @@ export const NumericFractionDomain = connectToContainer( { modifyPlotProps: (props, context, plotProps) => { numericFractionModifyPlotProps(props, context, plotProps); - if (context.container.overlaying) { + if (context.container && context.container.overlaying) { plotProps.isVisible = null; } }, } ); -export const LayoutNumericFraction = connectLayoutToPlot( - connectToContainer(UnconnectedNumericFraction, { - supplyPlotProps: supplyLayoutPlotProps, - modifyPlotProps: numericFractionModifyPlotProps, - }) -); - -export const LayoutNumeric = connectLayoutToPlot( - connectToContainer(UnconnectedNumeric, { - supplyPlotProps: supplyLayoutPlotProps, - }) -); - -export const LayoutNumericFractionInverse = connectLayoutToPlot( - connectToContainer(UnconnectedNumericFraction, { - supplyPlotProps: supplyLayoutPlotProps, +export const NumericFractionInverse = connectToContainer( + UnconnectedNumericFraction, + { modifyPlotProps: (props, context, plotProps) => { const {attrMeta, fullValue, updatePlot} = plotProps; if (isNumeric(fullValue)) { @@ -331,7 +316,7 @@ export const LayoutNumericFractionInverse = connectLayoutToPlot( } } }, - }) + } ); export const AnnotationArrowRef = connectToContainer(UnconnectedDropdown, { @@ -476,65 +461,6 @@ function computeAxesRefOptions(axes, propsAttr) { return options; } -export const GeoScope = connectLayoutToPlot( - connectToContainer(UnconnectedDropdown, { - supplyPlotProps: (props, context) => { - const {localize: _} = props; - const options = [ - {label: _('World'), value: 'world'}, - {label: _('USA'), value: 'usa'}, - {label: _('Europe'), value: 'europe'}, - {label: _('Asia'), value: 'asia'}, - {label: _('Africa'), value: 'africa'}, - {label: _('North America'), value: 'north america'}, - {label: _('South America'), value: 'south america'}, - ]; - return {...supplyLayoutPlotProps(props, context), options}; - }, - }) -); - -export const GeoProjections = connectLayoutToPlot( - connectToContainer(UnconnectedDropdown, { - supplyPlotProps: (props, context) => { - const {localize: _} = props; - let options = [ - {label: _('Equirectangular'), value: 'equirectangular'}, - {label: _('Mercator'), value: 'mercator'}, - {label: _('Orthographic'), value: 'orthographic'}, - {label: _('Natural Earth'), value: 'naturalEarth'}, - {label: _('Kavrayskiy7'), value: 'kavrayskiy7'}, - {label: _('Miller'), value: 'miller'}, - {label: _('Robinson'), value: 'robinson'}, - {label: _('Eckert4'), value: 'eckert4'}, - {label: _('Azimuthal Equal Area'), value: 'azimuthalEqualArea'}, - {label: _('Azimuthal Equidistant'), value: 'azimuthalEquidistant'}, - {label: _('Conic Equal Area'), value: 'conicEqualArea'}, - {label: _('Conic Conformal'), value: 'conicConformal'}, - {label: _('Conic Equidistant'), value: 'conicEquidistant'}, - {label: _('Gnomonic'), value: 'gnomonic'}, - {label: _('Stereographic'), value: 'stereographic'}, - {label: _('Mollweide'), value: 'mollweide'}, - {label: _('Hammer'), value: 'hammer'}, - {label: _('Transverse Mercator'), value: 'transverseMercator'}, - {label: _('Winkel Tripel'), value: 'winkel3'}, - {label: _('Aitoff'), value: 'aitoff'}, - {label: _('Sinusoidal'), value: 'sinusoidal'}, - ]; - - if ( - context.fullLayout && - context.fullLayout.geo && - context.fullLayout.geo.scope === 'usa' - ) { - options = [{label: _('Albers USA'), value: 'albers usa'}]; - } - - return {...supplyLayoutPlotProps(props, context), options}; - }, - }) -); - export const HoverInfo = connectToContainer(UnconnectedFlaglist, { modifyPlotProps: (props, context, plotProps) => { const {localize: _} = props; diff --git a/src/components/fields/index.js b/src/components/fields/index.js index 017094369..eaee04ea7 100644 --- a/src/components/fields/index.js +++ b/src/components/fields/index.js @@ -29,16 +29,11 @@ import { CanvasSize, ContourNumeric, FillDropdown, - GeoProjections, - GeoScope, HoverInfo, NumericFraction, NumericFractionDomain, PositioningNumeric, NumericFractionInverse, - LayoutNumericFraction, - LayoutNumeric, - LayoutNumericFractionInverse, RangesliderVisible, TraceOrientation, AxisOverlayDropdown, @@ -66,13 +61,8 @@ export { FillDropdown, Flaglist, FontSelector, - GeoProjections, - GeoScope, HoverInfo, Info, - LayoutNumericFraction, - LayoutNumeric, - LayoutNumericFractionInverse, NumericFraction, NumericFractionDomain, NumericFractionInverse, diff --git a/src/components/index.js b/src/components/index.js index fecb01853..cdb0dcc59 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -20,13 +20,8 @@ import { FillDropdown, Flaglist, FontSelector, - GeoProjections, - GeoScope, HoverInfo, Info, - LayoutNumericFraction, - LayoutNumeric, - LayoutNumericFractionInverse, NumericFraction, NumericFractionDomain, PositioningNumeric, @@ -58,6 +53,7 @@ import { AxesFold, Fold, LayoutPanel, + LayoutSection, Panel, Section, TraceAccordion, @@ -92,6 +88,7 @@ export { ArrowSelector, AxesFold, AxesRange, + LayoutSection, AxesSelector, Button, CanvasSize, @@ -105,17 +102,12 @@ export { Flaglist, Fold, FontSelector, - GeoProjections, - GeoScope, HoverInfo, Info, NumericFraction, NumericFractionDomain, PositioningNumeric, NumericFractionInverse, - LayoutNumericFraction, - LayoutNumeric, - LayoutNumericFractionInverse, LayoutPanel, LineDashSelector, LineShapeSelector, diff --git a/src/default_panels/GraphCreatePanel.js b/src/default_panels/GraphCreatePanel.js index 792d1acd3..f97b097d2 100644 --- a/src/default_panels/GraphCreatePanel.js +++ b/src/default_panels/GraphCreatePanel.js @@ -4,17 +4,15 @@ import { DataSelector, Dropdown, ErrorBars, - GeoProjections, - GeoScope, Radio, Section, + LayoutSection, AxisCreator, TraceAccordion, TraceSelector, TextEditor, Numeric, TraceTypeSection, - LayoutNumeric, } from '../components'; import {localize} from '../lib'; @@ -84,20 +82,6 @@ const GraphCreatePanel = ({localize: _}) => { attr="thetaunit" clearable={false} /> - -
@@ -150,18 +134,54 @@ const GraphCreatePanel = ({localize: _}) => { }, ]} /> - - + + + + diff --git a/src/default_panels/StyleAxesPanel.js b/src/default_panels/StyleAxesPanel.js index e5918170a..db7474046 100644 --- a/src/default_panels/StyleAxesPanel.js +++ b/src/default_panels/StyleAxesPanel.js @@ -66,17 +66,10 @@ class StyleAxesPanel extends Component { !axis._name.includes('radial')} + axisFilter={axis => + !axis._name.includes('radial') && !axis._name.includes('angular') + } > -
+ @@ -217,6 +214,14 @@ class StyleAxesPanel extends Component { {label: _('Hide'), value: false}, ]} /> + @@ -242,6 +247,14 @@ class StyleAxesPanel extends Component { ]} /> + -
( ( -
- - - - -
+ + + + + +
@@ -185,8 +185,7 @@ const StyleTracesPanel = ({localize: _}) => ( label={_('Smoothing')} attr="line.smoothing" showSlider - min={0} - max={1.3} + step={0.1} /> { - it('connectToContainer accepts supplyPlotProps function', () => { - const onUpdate = jest.fn(); - const fixtureProps = fixtures.scatter({layout: {width: 10}}); - const ConnectedNumeric = connectTraceToPlot( - connectToContainer(Numeric, { - supplyPlotProps: (props, context) => { - const plotProps = unpackPlotProps(props, context); - plotProps.connectToContainerModifiedPlotProp = true; - return plotProps; - }, - }) - ); - - const numeric = mount( - - - - ).find(Numeric); - - expect(numeric.prop('connectToContainerModifiedPlotProp')).toBe(true); - }); -}); diff --git a/src/lib/__tests__/nestedContainerConnections-test.js b/src/lib/__tests__/nestedContainerConnections-test.js index 75f8745ca..04bab60b9 100644 --- a/src/lib/__tests__/nestedContainerConnections-test.js +++ b/src/lib/__tests__/nestedContainerConnections-test.js @@ -7,8 +7,6 @@ import { connectLayoutToPlot, connectToContainer, connectTraceToPlot, - getLayoutContext, - unpackPlotProps, } from '..'; describe('Plot Connection', () => { @@ -89,70 +87,24 @@ describe('Plot Connection', () => { expect(wrapper.length).toBe(0); }); - it('can supplyPlotProps within
and nested Layout and Trace', () => { - const beforeUpdateLayout = jest.fn(); + it('can modify plotProps with
', () => { const fixtureProps = fixtures.scatter({layout: {width: 10}}); const TracePanel = connectTraceToPlot(Panel); - const LayoutConnectedNumeric = connectLayoutToPlot( - connectToContainer(Numeric, { - supplyPlotProps: (props, context) => { - return unpackPlotProps(props, { - ...context, - ...getLayoutContext(context), - }); - }, - }) - ); - - mount( - - -
- -
-
-
- ) - .find('[attr="width"]') - .find(NumericInput) - .find('.js-numeric-increase') - .simulate('click'); - - expect(beforeUpdateLayout).toBeCalled(); - const payload = beforeUpdateLayout.mock.calls[0][0]; - expect(payload).toEqual({update: {width: 11}}); - }); - - it('can supply and modify plotProps with
', () => { - const fixtureProps = fixtures.scatter({layout: {width: 10}}); - const TracePanel = connectTraceToPlot(Panel); - const supplyLayoutPlotProps = (props, context) => { - return unpackPlotProps(props, { - ...context, - ...getLayoutContext(context), - }); - }; const MAXWIDTH = 1000; - const LayoutWidth = connectLayoutToPlot( - connectToContainer(Numeric, { - supplyPlotProps: supplyLayoutPlotProps, - modifyPlotProps: (props, context, plotProps) => { - plotProps.max = MAXWIDTH; - }, - }) - ); + const LayoutSection = connectLayoutToPlot(Section); + const ModifiedNumeric = connectToContainer(Numeric, { + modifyPlotProps: (props, context, plotProps) => { + plotProps.max = MAXWIDTH; + }, + }); const wrapper = mount( -
- -
+ + +
) diff --git a/src/lib/connectLayoutToPlot.js b/src/lib/connectLayoutToPlot.js index 170c738dc..5f0a08849 100644 --- a/src/lib/connectLayoutToPlot.js +++ b/src/lib/connectLayoutToPlot.js @@ -1,66 +1,41 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import nestedProperty from 'plotly.js/src/lib/nested_property'; -import {getDisplayName, unpackPlotProps} from '../lib'; +import {getDisplayName} from '../lib'; import {EDITOR_ACTIONS} from './constants'; -export const getLayoutContext = context => { - const {layout, fullLayout, plotly, onUpdate} = context; - - const updateContainer = update => { - if (!onUpdate) { - return; - } - onUpdate({ - type: EDITOR_ACTIONS.UPDATE_LAYOUT, - payload: { - update, - }, - }); - }; - - let getValObject; - if (plotly) { - getValObject = attr => - plotly.PlotSchema.getLayoutValObject( - fullLayout, - nestedProperty({}, attr).parts - ); - } - - return { - getValObject, - updateContainer, - container: layout, - fullContainer: fullLayout, - }; -}; - -export default function connectLayoutToPlot(WrappedComponent, config = {}) { +export default function connectLayoutToPlot(WrappedComponent) { class LayoutConnectedComponent extends Component { - static supplyPlotProps(props, context) { - if (config.supplyPlotProps) { - return config.supplyPlotProps(props, context); - } - if (WrappedComponent.supplyPlotProps) { - return WrappedComponent.supplyPlotProps(props, context); - } - return unpackPlotProps(props, context); - } + getChildContext() { + const {layout, fullLayout, plotly, onUpdate} = this.context; - // Run the inner modifications first and allow more recent modifyPlotProp - // config function to modify last. - static modifyPlotProps(props, context, plotProps) { - if (WrappedComponent.modifyPlotProps) { - WrappedComponent.modifyPlotProps(props, context, plotProps); - } - if (config.modifyPlotProps) { - config.modifyPlotProps(props, context, plotProps); + const updateContainer = update => { + if (!onUpdate) { + return; + } + onUpdate({ + type: EDITOR_ACTIONS.UPDATE_LAYOUT, + payload: { + update, + }, + }); + }; + + let getValObject; + if (plotly) { + getValObject = attr => + plotly.PlotSchema.getLayoutValObject( + fullLayout, + nestedProperty({}, attr).parts + ); } - } - getChildContext() { - return getLayoutContext(this.context); + return { + getValObject, + updateContainer, + container: layout, + fullContainer: fullLayout, + }; } render() { diff --git a/src/lib/connectToContainer.js b/src/lib/connectToContainer.js index 73590d5b8..16ac21684 100644 --- a/src/lib/connectToContainer.js +++ b/src/lib/connectToContainer.js @@ -20,18 +20,6 @@ export const containerConnectedContextTypes = { export default function connectToContainer(WrappedComponent, config = {}) { class ContainerConnectedComponent extends Component { - // The most recent supplyPlotProps is used to supply the initial plotProps. - // This means any config routines are run before the inner components. - static supplyPlotProps(props, context) { - if (config.supplyPlotProps) { - return config.supplyPlotProps(props, context); - } - if (WrappedComponent.supplyPlotProps) { - return WrappedComponent.supplyPlotProps(props, context); - } - return unpackPlotProps(props, context); - } - // Run the inner modifications first and allow more recent modifyPlotProp // config function to modify last. static modifyPlotProps(props, context, plotProps) { @@ -54,23 +42,12 @@ export default function connectToContainer(WrappedComponent, config = {}) { } setLocals(props, context) { - if (props.plotProps) { - // If we have already been connected with plotProps and computed their - // values then we do not need to recompute them. - this.plotProps = props.plotProps; - } else { - // Otherwise, this is just a bare component (not in a section) and it needs - // processing: - this.plotProps = ContainerConnectedComponent.supplyPlotProps( - props, - context - ); - ContainerConnectedComponent.modifyPlotProps( - props, - context, - this.plotProps - ); - } + this.plotProps = unpackPlotProps(props, context); + ContainerConnectedComponent.modifyPlotProps( + props, + context, + this.plotProps + ); } render() { diff --git a/src/lib/index.js b/src/lib/index.js index 37524b2f7..23cf6a629 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -8,7 +8,7 @@ import connectRangeSelectorToAxis from './connectRangeSelectorToAxis'; import connectTransformToTrace from './connectTransformToTrace'; import connectAggregationToTransform from './connectAggregationToTransform'; import connectAxesToLayout from './connectAxesToLayout'; -import connectLayoutToPlot, {getLayoutContext} from './connectLayoutToPlot'; +import connectLayoutToPlot from './connectLayoutToPlot'; import connectToContainer, { containerConnectedContextTypes, } from './connectToContainer'; @@ -29,7 +29,6 @@ import { plotlyTraceToCustomTrace, } from './customTraceType'; import * as PlotlyIcons from 'plotly-icons'; -import supplyLayoutPlotProps from './supplyLayoutPlotProps'; import striptags from './striptags'; import { capitalize, @@ -94,7 +93,6 @@ export { getAllAxes, getAxisTitle, getDisplayName, - getLayoutContext, isPlainObject, localize, localizeString, @@ -102,7 +100,6 @@ export { renderTraceIcon, unpackPlotProps, walkObject, - supplyLayoutPlotProps, tooLight, striptags, traceTypeToAxisType, diff --git a/src/lib/supplyLayoutPlotProps.js b/src/lib/supplyLayoutPlotProps.js deleted file mode 100644 index cfad0d877..000000000 --- a/src/lib/supplyLayoutPlotProps.js +++ /dev/null @@ -1,11 +0,0 @@ -import {unpackPlotProps, getLayoutContext} from './'; - -// Workaround the issue with nested layouts inside trace component. -// See: -// https://github.com/plotly/react-chart-editor/issues/58#issuecomment-345492794 -export default function supplyLayoutPlotProps(props, context) { - return unpackPlotProps(props, { - ...context, - ...getLayoutContext(context), - }); -} diff --git a/src/shame.js b/src/shame.js index 70a35290e..6d2575159 100644 --- a/src/shame.js +++ b/src/shame.js @@ -9,7 +9,7 @@ import nestedProperty from 'plotly.js/src/lib/nested_property'; // We should be able to remove this once the plotly.react method has // been integrated into react-plotly.js and released: // https://github.com/plotly/react-plotly.js/issues/2 -export const maybeClearAxisTypes = (graphDiv, traceIndexes, update) => { +export const shamefullyClearAxisTypes = (graphDiv, {traceIndexes, update}) => { if (!Array.isArray(graphDiv._fullData)) { return; } @@ -43,3 +43,42 @@ function clearAxisTypes(gd, traces) { } } } + +export const shamefullyAdjustAxisRef = (graphDiv, payload) => { + if (payload.tracesNeedingAxisAdjustment) { + payload.tracesNeedingAxisAdjustment.forEach(trace => { + const axis = trace[payload.axisAttrToAdjust].charAt(0); + const currentAxisIdNumber = Number( + trace[payload.axisAttrToAdjust].slice(1) + ); + const adjustedAxisIdNumber = currentAxisIdNumber - 1; + + const currentAxisLayoutProperties = { + ...graphDiv.layout[payload.axisAttrToAdjust + currentAxisIdNumber], + }; + + // for cases when we're adjusting x2 => x, so that it becomes x not x1 + graphDiv.data[trace.index][payload.axisAttrToAdjust] = + adjustedAxisIdNumber === 1 ? axis : axis + adjustedAxisIdNumber; + + graphDiv.layout[ + payload.axisAttrToAdjust + adjustedAxisIdNumber + ] = currentAxisLayoutProperties; + }); + } +}; + +export const shamefullyAdjustGeo = ({layout: {geo = {}}}, {update}) => { + if (update['geo.scope']) { + update['geo.projection'] = {}; + update['geo.center'] = {}; + } + if ( + // requesting projection change + update['geo.projection.type'] && + (update['geo.projection.type'] === 'albers usa' || geo.scope === 'usa') + ) { + update['geo.scope'] = {}; + update['geo.center'] = {}; + } +};