Skip to content

Commit 0065005

Browse files
authored
Merge pull request #319 from plotly/error-bars
Added x | y | z | error bars
2 parents df2767a + da91b61 commit 0065005

File tree

7 files changed

+197
-7
lines changed

7 files changed

+197
-7
lines changed

src/components/fields/DataSelector.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import Field from './Field';
55
import nestedProperty from 'plotly.js/src/lib/nested_property';
66
import {connectToContainer} from 'lib';
77

8-
function attributeIsData(meta = {}) {
8+
export function attributeIsData(meta = {}) {
99
return meta.valType === 'data_array' || meta.arrayOk;
1010
}
1111

12-
class DataSelector extends Component {
12+
export class UnconnectedDataSelector extends Component {
1313
constructor(props, context) {
1414
super(props, context);
1515

@@ -91,14 +91,14 @@ class DataSelector extends Component {
9191
}
9292
}
9393

94-
DataSelector.propTypes = {
94+
UnconnectedDataSelector.propTypes = {
9595
fullValue: PropTypes.any,
9696
updatePlot: PropTypes.func,
9797
container: PropTypes.object,
9898
...Field.propTypes,
9999
};
100100

101-
DataSelector.contextTypes = {
101+
UnconnectedDataSelector.contextTypes = {
102102
dataSources: PropTypes.object,
103103
dataSourceOptions: PropTypes.array,
104104
dataSourceValueRenderer: PropTypes.func,
@@ -111,4 +111,4 @@ function modifyPlotProps(props, context, plotProps) {
111111
}
112112
}
113113

114-
export default connectToContainer(DataSelector, {modifyPlotProps});
114+
export default connectToContainer(UnconnectedDataSelector, {modifyPlotProps});

src/components/fields/ErrorBars.js

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import PropTypes from 'prop-types';
2+
import React, {Component, Fragment} from 'react';
3+
import {DataSelector, Radio, Numeric} from '../index';
4+
import RadioBlocks from '../widgets/RadioBlocks';
5+
import Field from './Field';
6+
import {connectToContainer} from 'lib';
7+
8+
class ErrorBars extends Component {
9+
constructor(props, context) {
10+
super(props, context);
11+
this.updatePlot = this.updatePlot.bind(this);
12+
}
13+
14+
updatePlot(value) {
15+
if (value === 'symmetric') {
16+
this.props.updatePlot({
17+
...this.props.fullValue,
18+
visible: true,
19+
symmetric: true,
20+
});
21+
}
22+
23+
if (value === 'asymmetric') {
24+
this.props.updatePlot({
25+
...this.props.fullValue,
26+
visible: true,
27+
symmetric: false,
28+
});
29+
}
30+
31+
if (value === 'hidden') {
32+
this.props.updatePlot({
33+
...this.props.fullValue,
34+
visible: false,
35+
});
36+
}
37+
}
38+
39+
getMode() {
40+
let mode;
41+
42+
if (!this.props.fullValue.visible) {
43+
mode = 'hidden';
44+
}
45+
46+
if (
47+
this.props.fullValue.visible &&
48+
(this.props.fullValue.symmetric ||
49+
typeof this.props.fullValue.symmetric === 'undefined')
50+
) {
51+
// when this.props.fullValue.type === 'sqrt',
52+
// then this.props.fullValue.symmetric is undefined, but 'sqrt' is only
53+
// applicable when we want symmetric error bars
54+
// https://github.com/plotly/plotly.js/issues/2359
55+
mode = 'symmetric';
56+
}
57+
58+
if (
59+
this.props.fullValue.visible &&
60+
this.props.fullValue.symmetric === false
61+
) {
62+
// it has to be explicitly set to false, because we don't want it to catch
63+
// cases when it's undefined
64+
mode = 'asymmetric';
65+
}
66+
67+
return mode;
68+
}
69+
70+
renderModeSelector() {
71+
const {localize: _} = this.props;
72+
73+
return (
74+
<Field>
75+
<RadioBlocks
76+
alignment="center"
77+
onOptionChange={this.updatePlot}
78+
activeOption={this.getMode()}
79+
options={[
80+
{label: _('Symmetric'), value: 'symmetric'},
81+
{label: _('Asymmetric'), value: 'asymmetric'},
82+
{label: _('Hidden'), value: 'hidden'},
83+
]}
84+
/>
85+
</Field>
86+
);
87+
}
88+
89+
renderErrorBarControls() {
90+
const {localize: _} = this.props;
91+
const mode = this.getMode();
92+
const showCustomDataControl = this.props.fullValue.type === 'data';
93+
94+
if (mode === 'symmetric') {
95+
return (
96+
<Fragment>
97+
<Radio
98+
label={_('Error Type')}
99+
attr={`${this.props.attr}.type`}
100+
options={[
101+
{label: _('%'), value: 'percent'},
102+
{label: _('Constant'), value: 'constant'},
103+
{label: _('√'), value: 'sqrt'},
104+
{label: _('Data'), value: 'data'},
105+
]}
106+
/>
107+
<Numeric label={_('Value')} attr={`${this.props.attr}.value`} />
108+
{showCustomDataControl ? (
109+
<DataSelector
110+
label={_(`Custom Data`)}
111+
attr={`${this.props.attr}.array`}
112+
/>
113+
) : null}
114+
</Fragment>
115+
);
116+
}
117+
118+
if (mode === 'asymmetric') {
119+
return (
120+
<Fragment>
121+
<Radio
122+
label={_('Error Type')}
123+
attr={`${this.props.attr}.type`}
124+
options={[
125+
{label: _('%'), value: 'percent'},
126+
{label: _('Constant'), value: 'constant'},
127+
{label: _('Data'), value: 'data'},
128+
]}
129+
/>
130+
<Numeric label={_('Value')} attr={`${this.props.attr}.value`} />
131+
<Numeric
132+
label={_('Value (-)')}
133+
attr={`${this.props.attr}.valueminus`}
134+
/>
135+
{showCustomDataControl ? (
136+
<Fragment>
137+
<DataSelector
138+
label={_(`Error (+)`)}
139+
attr={`${this.props.attr}.array`}
140+
/>
141+
<DataSelector
142+
label={_(`Error (-)`)}
143+
attr={`${this.props.attr}.arrayminus`}
144+
/>
145+
</Fragment>
146+
) : null}
147+
</Fragment>
148+
);
149+
}
150+
151+
return null;
152+
}
153+
154+
render() {
155+
return (
156+
<Fragment>
157+
{this.renderModeSelector()}
158+
{this.renderErrorBarControls()}
159+
</Fragment>
160+
);
161+
}
162+
}
163+
164+
ErrorBars.propTypes = {
165+
attr: PropTypes.string,
166+
localize: PropTypes.func,
167+
fullValue: PropTypes.object,
168+
updatePlot: PropTypes.func,
169+
};
170+
171+
export default connectToContainer(ErrorBars);

src/components/fields/derived.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import isNumeric from 'fast-isnumeric';
2-
import {UnconnectedNumeric} from './Numeric';
32
import {UnconnectedDropdown} from './Dropdown';
4-
import {UnconnectedRadio} from './Radio';
53
import {UnconnectedFlaglist} from './Flaglist';
4+
import {UnconnectedNumeric} from './Numeric';
5+
import {UnconnectedRadio} from './Radio';
66
import {
77
connectLayoutToPlot,
88
connectToContainer,

src/components/fields/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import DataSelector from './DataSelector';
1212
import Numeric from './Numeric';
1313
import SymbolSelector from './SymbolSelector';
1414
import TraceSelector from './TraceSelector';
15+
import ErrorBars from './ErrorBars';
1516
import {
1617
AnnotationArrowRef,
1718
AnnotationRef,
@@ -40,6 +41,7 @@ export {
4041
ContourNumeric,
4142
DataSelector,
4243
Dropdown,
44+
ErrorBars,
4345
FillDropdown,
4446
Flaglist,
4547
FontSelector,

src/components/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
ContourNumeric,
1111
DataSelector,
1212
Dropdown,
13+
ErrorBars,
1314
FillDropdown,
1415
Flaglist,
1516
FontSelector,
@@ -63,6 +64,7 @@ export {
6364
ContourNumeric,
6465
DataSelector,
6566
Dropdown,
67+
ErrorBars,
6668
FillDropdown,
6769
Flaglist,
6870
Fold,

src/default_panels/GraphCreatePanel.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
33
import {
44
DataSelector,
55
Dropdown,
6+
ErrorBars,
67
GeoProjections,
78
GeoScope,
89
Radio,
@@ -62,6 +63,18 @@ const GraphCreatePanel = ({localize: _}) => {
6263
<DataSelector label={_('C')} attr="c" />
6364
</Section>
6465

66+
<Section name={_('Error Bars X')}>
67+
<ErrorBars localize={_} attr="error_x" />
68+
</Section>
69+
70+
<Section name={_('Error Bars Y')}>
71+
<ErrorBars localize={_} attr="error_y" />
72+
</Section>
73+
74+
<Section name={_('Error Bars Z')}>
75+
<ErrorBars localize={_} attr="error_z" />
76+
</Section>
77+
6578
<Section name={_('Options')}>
6679
<DataSelector label={_('Intensity')} attr="intensity" />
6780
<DataSelector label={_('Facecolor')} attr="facecolor" />

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
ColorPicker,
2626
ColorscalePicker,
2727
ContourNumeric,
28+
ErrorBars,
2829
DataSelector,
2930
Dropdown,
3031
Flaglist,
@@ -75,6 +76,7 @@ export {
7576
ColorPicker,
7677
ColorscalePicker,
7778
ContourNumeric,
79+
ErrorBars,
7880
DataSelector,
7981
Dropdown,
8082
EDITOR_ACTIONS,

0 commit comments

Comments
 (0)