Skip to content

Commit d278280

Browse files
🔪 supplyPlotProps (#438)
1 parent 188fc34 commit d278280

25 files changed

+223
-441
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ Simple component that takes in props and renders.
101101

102102
* `<TraceAccordion />`: `<Panel />` whose children are replicated into `<Folds />` connected to traces via `connectTraceToPlot()`.
103103
* `<LayoutPanel />`: `<Panel />` whose children are connected to the `layout` figure key
104+
* `<LayoutSection />`: `<Section />` whose children are connected to the `layout` figure key
104105
* `<TraceRequiredPanel />`: `<LayoutPanel />` renders `<PanelEmpty />` 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.
105106
* `<AnnotationAccordion />`: `<Panel />` whose children are replicated into `<Folds />` connected to annotations via `connectAnnotationToLayout()`. For use in a `<LayoutPanel />`.
106107
* `<ShapeAccordion />`: `<Panel />` whose children are replicated into `<Folds />` connected to shapes via `connectShapeToLayout()`. For use in a `<LayoutPanel />`.
@@ -117,7 +118,6 @@ For use in containers bound to traces e.g. as children of `<TraceAccordion />`:
117118
* `<LineDashSelector />`: renders as a `<Dropdown />` useful for `data[].line.dash`
118119
* `<LineShapeSelector />`: renders as a `<Dropdown />` useful for `data[].line.shape`
119120
* `<SymbolSelector />`: renders as a `<Dropdown />` useful for `data[].marker.symbol`
120-
* `<LayoutNumericFraction />` and `<LayoutNumericFractionInverse />`: renders as a `<Numeric />` for use in trace-connected containers where normal `<Numerics />` would be bound to the `data` key instead of the `layout` key in the figure e.g. `layout.bargap` or `layout.barwidth`.
121121
* `<PositioningRef />`: renders as a `<Dropdown />` useful for `layout.*.xref/yref` where the allowable values are `paper|[axis]`
122122
* `<ErrorBars/>`: renders a set of controls that control a trace's error bars (`visibility`, `type`, `value`, `valueminus`, `array`, `arrayminus`)
123123

src/EditorControls.js

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import DefaultEditor from './DefaultEditor';
22
import PropTypes from 'prop-types';
33
import React, {Component} from 'react';
44
import {bem} from './lib';
5-
import {maybeClearAxisTypes} from './shame';
5+
import {
6+
shamefullyClearAxisTypes,
7+
shamefullyAdjustAxisRef,
8+
shamefullyAdjustGeo,
9+
} from './shame';
610
import {EDITOR_ACTIONS} from './lib/constants';
711
import isNumeric from 'fast-isnumeric';
812
import nestedProperty from 'plotly.js/src/lib/nested_property';
@@ -43,31 +47,6 @@ class EditorControls extends Component {
4347
};
4448
}
4549

46-
maybeAdjustAxisRef(payload) {
47-
const {graphDiv} = this.props;
48-
if (payload.tracesNeedingAxisAdjustment) {
49-
payload.tracesNeedingAxisAdjustment.forEach(trace => {
50-
const axis = trace[payload.axisAttrToAdjust].charAt(0);
51-
const currentAxisIdNumber = Number(
52-
trace[payload.axisAttrToAdjust].slice(1)
53-
);
54-
const adjustedAxisIdNumber = currentAxisIdNumber - 1;
55-
56-
const currentAxisLayoutProperties = {
57-
...graphDiv.layout[payload.axisAttrToAdjust + currentAxisIdNumber],
58-
};
59-
60-
// for cases when we're adjusting x2 => x, so that it becomes x not x1
61-
graphDiv.data[trace.index][payload.axisAttrToAdjust] =
62-
adjustedAxisIdNumber === 1 ? axis : axis + adjustedAxisIdNumber;
63-
64-
graphDiv.layout[
65-
payload.axisAttrToAdjust + adjustedAxisIdNumber
66-
] = currentAxisLayoutProperties;
67-
});
68-
}
69-
}
70-
7150
handleUpdate({type, payload}) {
7251
const {graphDiv} = this.props;
7352

@@ -77,11 +56,8 @@ class EditorControls extends Component {
7756
this.props.beforeUpdateTraces(payload);
7857
}
7958

80-
// until we start utilizing Plotly.react in `react-plotly.js`
81-
// force clear axes types when a `src` has changed.
82-
maybeClearAxisTypes(graphDiv, payload.traceIndexes, payload.update);
83-
84-
this.maybeAdjustAxisRef(payload);
59+
shamefullyClearAxisTypes(graphDiv, payload);
60+
shamefullyAdjustAxisRef(graphDiv, payload);
8561

8662
for (let i = 0; i < payload.traceIndexes.length; i++) {
8763
for (const attr in payload.update) {
@@ -108,6 +84,8 @@ class EditorControls extends Component {
10884
break;
10985

11086
case EDITOR_ACTIONS.UPDATE_LAYOUT:
87+
shamefullyAdjustGeo(graphDiv, payload);
88+
11189
if (this.props.beforeUpdateLayout) {
11290
this.props.beforeUpdateLayout(payload);
11391
}

src/components/containers/AxesFold.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,12 @@ import React, {Component} from 'react';
55
import {connectAxesToLayout} from 'lib';
66

77
class AxesFold extends Component {
8-
renderAxesSelector() {
9-
if (this.props.options.length > 1) {
10-
return <AxesSelector axesOptions={this.props.options} />;
11-
}
12-
return null;
13-
}
14-
158
render() {
16-
return this.props.children ? (
9+
const {children, options} = this.props;
10+
return options.length && children ? (
1711
<Fold {...this.props}>
18-
{this.renderAxesSelector()}
19-
{this.props.children}
12+
{options.length === 1 ? null : <AxesSelector axesOptions={options} />}
13+
{children}
2014
</Fold>
2115
) : null;
2216
}

src/components/containers/Fold.js

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,18 @@ class Fold extends Component {
2626
this.foldVisible = false;
2727

2828
React.Children.forEach(nextProps.children, child => {
29-
if (!child) {
29+
if (!child || this.foldVisible) {
3030
return;
3131
}
3232

3333
if (child.props.attr) {
3434
// attr components force fold open if they are visible
35-
let plotProps;
36-
if (child.type.supplyPlotProps) {
37-
plotProps = child.type.supplyPlotProps(child.props, nextContext);
38-
if (child.type.modifyPlotProps) {
39-
child.type.modifyPlotProps(child.props, nextContext, plotProps);
40-
}
41-
} else {
42-
plotProps = unpackPlotProps(child.props, nextContext);
35+
const plotProps = unpackPlotProps(child.props, nextContext);
36+
if (child.type.modifyPlotProps) {
37+
child.type.modifyPlotProps(child.props, nextContext, plotProps);
4338
}
4439

45-
if (plotProps.isVisible) {
46-
this.foldVisible = true;
47-
}
40+
this.foldVisible = this.foldVisible || plotProps.isVisible;
4841
return;
4942
}
5043

src/components/containers/Section.js

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {Component, cloneElement} from 'react';
1+
import React, {Component} from 'react';
22
import PropTypes from 'prop-types';
33
import {
44
containerConnectedContextTypes,
@@ -11,45 +11,38 @@ class Section extends Component {
1111
constructor(props, context) {
1212
super(props, context);
1313

14-
this.children = null;
1514
this.sectionVisible = false;
1615

17-
this.processAndSetChildren(props, context);
16+
this.determineVisibility(props, context);
1817
}
1918

2019
componentWillReceiveProps(nextProps, nextContext) {
21-
this.processAndSetChildren(nextProps, nextContext);
20+
this.determineVisibility(nextProps, nextContext);
2221
}
2322

24-
processAndSetChildren(nextProps, nextContext) {
23+
determineVisibility(nextProps, nextContext) {
2524
const {isVisible} = unpackPlotProps(nextProps, nextContext);
26-
this.sectionVisible = isVisible === true;
25+
this.sectionVisible = Boolean(isVisible);
26+
27+
React.Children.forEach(nextProps.children, child => {
28+
if (!child || this.foldVisible) {
29+
return;
30+
}
2731

28-
this.children = React.Children.map(nextProps.children, child => {
2932
if (child.props.attr) {
30-
let plotProps;
31-
if (child.type.supplyPlotProps) {
32-
plotProps = child.type.supplyPlotProps(child.props, nextContext);
33-
if (child.type.modifyPlotProps) {
34-
child.type.modifyPlotProps(child.props, nextContext, plotProps);
35-
}
36-
} else {
37-
plotProps = unpackPlotProps(child.props, nextContext);
33+
const plotProps = unpackPlotProps(child.props, nextContext);
34+
if (child.type.modifyPlotProps) {
35+
child.type.modifyPlotProps(child.props, nextContext, plotProps);
3836
}
39-
40-
// assign plotProps as a prop of children. If they are connectedToContainer
41-
// it will see plotProps and skip recomputing them.
4237
this.sectionVisible = this.sectionVisible || plotProps.isVisible;
43-
return cloneElement(child, {plotProps});
38+
return;
4439
}
4540

4641
if (!(child.type.plotly_editor_traits || {}).no_visibility_forcing) {
4742
// non-attr components force visibility (unless they don't via traits)
4843
this.sectionVisible = true;
49-
return child;
44+
return;
5045
}
51-
52-
return child;
5346
});
5447
}
5548

@@ -60,7 +53,7 @@ class Section extends Component {
6053
return (
6154
<div className="section">
6255
{this.props.name && <SectionHeader name={this.props.name} />}
63-
{this.children}
56+
{this.props.children}
6457
</div>
6558
);
6659
}

src/components/containers/__tests__/Section-test.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ describe('Section', () => {
2727
).find(Section);
2828

2929
expect(wrapper.children().length).toBe(1);
30-
expect(wrapper.find(Flaglist).props().plotProps.isVisible).toBe(true);
31-
expect(wrapper.find(Numeric).props().plotProps.isVisible).toBe(false);
3230
});
3331

3432
it('is visible if it contains any non attr children', () => {

src/components/containers/derived.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
66
import {connectLayoutToPlot, containerConnectedContextTypes} from 'lib';
77

88
const LayoutPanel = connectLayoutToPlot(Panel);
9+
const LayoutSection = connectLayoutToPlot(Section);
910

1011
const TraceTypeSection = (props, context) => {
1112
const {fullContainer, fullData} = context;
@@ -34,4 +35,4 @@ TraceTypeSection.defaultProps = {
3435
traceTypes: [],
3536
};
3637

37-
export {LayoutPanel, TraceTypeSection};
38+
export {LayoutPanel, LayoutSection, TraceTypeSection};

src/components/containers/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Section from './Section';
1212
import TraceAccordion from './TraceAccordion';
1313
import TransformAccordion from './TransformAccordion';
1414
import TraceMarkerSection from './TraceMarkerSection';
15-
import {LayoutPanel, TraceTypeSection} from './derived';
15+
import {LayoutPanel, LayoutSection, TraceTypeSection} from './derived';
1616
import TraceRequiredPanel from './TraceRequiredPanel';
1717
import SingleSidebarItem from './SingleSidebarItem';
1818
import ModalProvider from './ModalProvider';
@@ -35,6 +35,7 @@ export {
3535
TraceMarkerSection,
3636
TraceRequiredPanel,
3737
LayoutPanel,
38+
LayoutSection,
3839
AxesFold,
3940
SingleSidebarItem,
4041
TraceTypeSection,

src/components/fields/AxisCreator.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
traceTypeToAxisType,
1111
getAxisTitle,
1212
axisIdToAxisName,
13-
unpackPlotProps,
1413
} from 'lib';
1514

1615
class UnconnectedNewAxisCreator extends Component {
@@ -184,7 +183,6 @@ AxisCreator.contextTypes = {
184183
};
185184

186185
export default connectToContainer(AxisCreator, {
187-
supplyPlotProps: (props, context) => unpackPlotProps(props, {...context}),
188186
modifyPlotProps: (props, context, plotProps) => {
189187
const {data} = context;
190188
const {fullContainer} = plotProps;

src/components/fields/__tests__/TraceSelector-test.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ describe('TraceSelector', () => {
1414
...fixtures.scatter({data: [{mode: null, xsrc: null, ysrc: null}]}),
1515
onUpdate: jest.fn(),
1616
};
17+
1718
const wrapper = mount(
1819
<TestEditor {...editorProps} plotly={plotly}>
1920
<TraceSection traceIndexes={[0]}>
@@ -23,8 +24,6 @@ describe('TraceSelector', () => {
2324
).find(TraceSelector);
2425

2526
const innerDropdown = wrapper.find(Dropdown);
26-
27-
expect(wrapper.props().plotProps.container.mode).toBe('markers');
2827
expect(innerDropdown.prop('value')).toEqual('scatter');
2928
});
3029

@@ -44,7 +43,6 @@ describe('TraceSelector', () => {
4443
).find(TraceSelector);
4544

4645
const innerDropdown = wrapper.find(Dropdown);
47-
4846
expect(innerDropdown.prop('value')).toEqual('line');
4947
});
5048

@@ -66,7 +64,6 @@ describe('TraceSelector', () => {
6664
).find(TraceSelector);
6765

6866
const innerDropdown = wrapper.find(Dropdown);
69-
expect(wrapper.props().plotProps.container.mode).toBe('lines+markers');
7067
expect(innerDropdown.prop('value')).toEqual('line');
7168
});
7269

0 commit comments

Comments
 (0)