Skip to content

Commit 33a18c1

Browse files
Merge pull request #45 from plotly/DecoupleUnpackPlotPropsFromDataSrc
Decouple unpack plot props from data src
2 parents 49123bc + c9310b6 commit 33a18c1

File tree

7 files changed

+101
-51
lines changed

7 files changed

+101
-51
lines changed

src/components/DataSelector.js

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,74 @@
1+
import DropdownWidget from './widgets/Dropdown';
2+
import PropTypes from 'prop-types';
13
import React, {Component} from 'react';
2-
import Dropdown from './Dropdown';
4+
import nestedProperty from 'plotly.js/src/lib/nested_property';
5+
import {bem, connectToPlot} from '../lib';
6+
7+
function attributeIsData(meta = {}) {
8+
return meta.valType === 'data_array' || meta.arrayOk;
9+
}
10+
11+
class DataSelector extends Component {
12+
constructor(props, context) {
13+
super(props, context);
14+
15+
this.setLocals(props, context);
16+
this.updatePlot = this.updatePlot.bind(this);
17+
}
18+
19+
static unpackPlotProps(props, context, plotProps) {
20+
if (attributeIsData(plotProps.attrMeta)) {
21+
plotProps.isVisible = true;
22+
}
23+
}
24+
25+
componentWillReceiveProps(nextProps, nextContext) {
26+
this.setLocals(nextProps, nextContext);
27+
}
28+
29+
setLocals(props, context) {
30+
this.dataSrcExists = false;
31+
if (attributeIsData(props.attrMeta)) {
32+
this.dataSrcExists = true;
33+
this.srcAttr = props.attr + 'src';
34+
this.srcProperty = nestedProperty(props.trace, this.srcAttr);
35+
}
36+
}
37+
38+
fullValue() {
39+
if (this.dataSrcExists) {
40+
return this.srcProperty.get();
41+
}
42+
return this.props.fullValue();
43+
}
44+
45+
updatePlot(value) {
46+
const attr = this.dataSrcExists ? this.srcAttr : this.props.attr;
47+
const update = {[attr]: [value]};
48+
this.props.onUpdate && this.props.onUpdate(update, [this.props.index]);
49+
}
350

4-
export default class DataSelector extends Component {
551
render() {
6-
return <Dropdown {...this.props} isDataSrc />;
52+
return (
53+
<div className={bem('field')}>
54+
<div className={bem('field', 'title')}>
55+
<div className={bem('field', 'title-text')}>{this.props.label}</div>
56+
</div>
57+
<div className={bem('field', 'widget')}>
58+
<DropdownWidget
59+
options={this.props.options}
60+
value={this.fullValue()}
61+
onChange={this.updatePlot}
62+
clearable={this.props.clearable}
63+
/>
64+
</div>
65+
</div>
66+
);
767
}
868
}
69+
70+
DataSelector.contextTypes = {
71+
plotSchema: PropTypes.object,
72+
};
73+
74+
export default connectToPlot(DataSelector);

src/components/Dropdown.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, {Component} from 'react';
22
import DropdownWidget from './widgets/Dropdown';
33
import {bem, connectToPlot} from '../lib';
44

5-
class Dropdown extends Component {
5+
export class UnconnectedDropdown extends Component {
66
render() {
77
return (
88
<div className={bem('field')}>
@@ -22,4 +22,4 @@ class Dropdown extends Component {
2222
}
2323
}
2424

25-
export default connectToPlot(Dropdown);
25+
export default connectToPlot(UnconnectedDropdown);

src/components/Section.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ class Section extends Component {
2929
let child = children[i];
3030

3131
let isAttr = !!child.props.attr;
32-
let plotProps = isAttr ? unpackPlotProps(child.props, context) : {};
32+
let plotProps = isAttr
33+
? unpackPlotProps(child.props, context, child.constructor)
34+
: {};
3335
let childProps = Object.assign({plotProps}, child.props);
3436
childProps.key = i;
3537

src/components/TraceSelector.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Dropdown from './Dropdown';
1+
import {UnconnectedDropdown} from './Dropdown';
22
import PropTypes from 'prop-types';
33
import React, {Component} from 'react';
44
import nestedProperty from 'plotly.js/src/lib/nested_property';
@@ -51,12 +51,12 @@ class TraceSelector extends Component {
5151
}
5252

5353
render() {
54-
const modifiedPlotProps = Object.assign({}, this.plotProps, {
54+
const props = Object.assign({}, this.props, {
5555
fullValue: this.fullValue,
5656
updatePlot: this.updatePlot,
5757
});
5858

59-
return <Dropdown {...this.props} plotProps={modifiedPlotProps} />;
59+
return <UnconnectedDropdown {...props} />;
6060
}
6161
}
6262

src/components/__tests__/DataSelector-test.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import DataSelector from '../DataSelector';
12
import DropdownWidget from '../widgets/Dropdown';
23
import React from 'react';
34
import {TestEditor, fixtures, plotly} from '../../lib/test-utils';
@@ -16,23 +17,17 @@ function render(overrides = {}) {
1617
describe('DataSelector', () => {
1718
it('contains options defined by dataSources', () => {
1819
const {dataSources} = fixtures.scatter();
19-
const wrapper = render({dataSources});
20+
const wrapper = render({dataSources}).find(DropdownWidget);
2021
expect(wrapper.prop('options')).toEqual(Object.keys(dataSources));
2122
});
2223

23-
it('sets srcAttr and srcProperty when attr is data_array', () => {
24-
const wrapper = render();
25-
expect(wrapper.prop('srcAttr')).toBe('xsrc');
26-
expect(wrapper.prop('srcProperty').get()).toBe('x1');
24+
it('uses gd.data dataSrc value not fullValue when data_array', () => {
25+
const wrapper = render().find(DropdownWidget);
26+
expect(wrapper.prop('value')).toBe('x1');
2727
});
2828

2929
// arrayOk not implemented in defaultEditor yet
30-
it('sets srcAttr and srcProperty when attr is arrayOk', () => {});
31-
32-
it('uses srcProperty as fullValue', () => {
33-
const wrapper = render();
34-
expect(wrapper.prop('fullValue')()).toBe('x1');
35-
});
30+
it('uses gd.data dataSrc value not fullValue when arrayOk', () => {});
3631

3732
it('calls updatePlot with srcAttr', () => {
3833
const onUpdate = jest.fn();

src/lib/connectToPlot.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default function connectToPlot(BaseComponent) {
2222
} else {
2323
// Otherwise, this is just a bare component (not in a section) and it needs
2424
// processing:
25-
this.plotProps = unpackPlotProps(props, context);
25+
this.plotProps = unpackPlotProps(props, context, BaseComponent);
2626
}
2727
}
2828

src/lib/unpackPlotProps.js

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import nestedProperty from 'plotly.js/src/lib/nested_property';
22

3-
export default function unpackPlotProps(props, context) {
3+
export default function unpackPlotProps(props, context, ComponentClass) {
44
const plotProps = {};
55

6+
// Indexing and referencing:
67
plotProps.attr = props.attr;
78
plotProps.index = context.traceIndex;
89

@@ -14,47 +15,33 @@ export default function unpackPlotProps(props, context) {
1415
// Property accessors:
1516
plotProps.fullProperty = nestedProperty(plotProps.fullTrace, plotProps.attr);
1617
plotProps.property = nestedProperty(plotProps.trace, plotProps.attr);
18+
plotProps.fullValue = () => plotProps.fullProperty.get();
1719

18-
let dataSrcExists = false;
19-
if (props.isDataSrc) {
20-
const traceAttr = `${plotProps.fullTrace.type}.attributes.${props.attr}`;
21-
const attr = nestedProperty(context.plotSchema.traces, traceAttr).get();
22-
if (attr && (attr.valType === 'data_array' || attr.arrayOk)) {
23-
dataSrcExists = true;
24-
plotProps.srcAttr = plotProps.attr + 'src';
25-
plotProps.srcProperty = nestedProperty(
26-
plotProps.trace,
27-
plotProps.srcAttr
28-
);
29-
}
30-
}
20+
// Property descriptions and meta:
21+
plotProps.attrMeta =
22+
nestedProperty(
23+
context.plotSchema.traces,
24+
`${plotProps.fullTrace.type}.attributes.${plotProps.attr}`
25+
).get() || {};
3126

27+
// Update data functions:
3228
plotProps.onUpdate = context.onUpdate;
33-
34-
plotProps.fullValue = function fullValue() {
35-
if (dataSrcExists) {
36-
// we use the non-full version for src information as Plotly.js does
37-
// not pass src information into fullData or fullLayout. It is a
38-
// "user land" only attribute.
39-
return plotProps.srcProperty.get();
40-
} else {
41-
return plotProps.fullProperty.get();
42-
}
43-
};
44-
45-
// An update callback:
4629
plotProps.updatePlot = function updatePlot(value) {
47-
const attr = dataSrcExists ? plotProps.srcAttr : plotProps.attr;
48-
const update = {[attr]: [value]};
30+
const update = {[plotProps.attr]: [value]};
4931
plotProps.onUpdate && plotProps.onUpdate(update, [plotProps.index]);
5032
};
5133

34+
// Visibility:
5235
const fv = plotProps.fullValue();
53-
if (props.show || dataSrcExists || (fv !== undefined && fv !== null)) {
36+
if (props.show || (fv !== undefined && fv !== null)) {
5437
plotProps.isVisible = true;
5538
} else {
5639
plotProps.isVisible = false;
5740
}
5841

42+
// Allow Component Classes to further augment plotProps:
43+
ComponentClass.unpackPlotProps &&
44+
ComponentClass.unpackPlotProps(props, context, plotProps);
45+
5946
return plotProps;
6047
}

0 commit comments

Comments
 (0)