Skip to content

Commit 0eaef99

Browse files
authored
Merge pull request #898 from plotly/sankey
Sankey
2 parents 117d758 + b4664f7 commit 0eaef99

File tree

14 files changed

+676
-33
lines changed

14 files changed

+676
-33
lines changed

dev/dataSources.js

Lines changed: 415 additions & 0 deletions
Large diffs are not rendered by default.

dev/mocks.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"/percy/violin.json",
1010
"/percy/waterfall.json",
1111
"/percy/sunburst.json",
12+
"/percy/sankey.json",
1213
"0.json",
1314
"1.json",
1415
"10.json",

dev/percy/index.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import panelTest from './panelTest.json';
2-
import histogram from './histogram.json';
3-
import histogram2d from './histogram2d.json';
4-
import pie from './pie.json';
5-
import violin from './violin.json';
6-
import bar from './bar.json';
7-
import box from './box.json';
8-
import waterfall from './waterfall.json';
9-
import sunburst from './sunburst.json';
10-
11-
export {panelTest, histogram, histogram2d, pie, violin, bar, box, waterfall, sunburst};
1+
export {default as panelTest} from './panelTest.json';
2+
export {default as histogram} from './histogram.json';
3+
export {default as histogram2d} from './histogram2d.json';
4+
export {default as pie} from './pie.json';
5+
export {default as violin} from './violin.json';
6+
export {default as bar} from './bar.json';
7+
export {default as box} from './box.json';
8+
export {default as waterfall} from './waterfall.json';
9+
export {default as sunburst} from './sunburst.json';
10+
export {default as sankey} from './sankey.json';

dev/percy/sankey.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"data": [
3+
{
4+
"type": "sankey",
5+
"mode": "markers",
6+
"link": {
7+
"value": [
8+
124.729,
9+
0.597,
10+
26.862
11+
],
12+
"valuesrc": "x1",
13+
"source": [
14+
0,
15+
1,
16+
1
17+
],
18+
"sourcesrc": "x1",
19+
"target": [
20+
1,
21+
2,
22+
3
23+
],
24+
"targetsrc": "x1"
25+
},
26+
"node": {
27+
"label": [
28+
"A",
29+
"B",
30+
"C"
31+
],
32+
"labelsrc": "x1"
33+
}
34+
}
35+
],
36+
"layout": {
37+
"xaxis": {
38+
"range": [
39+
-1,
40+
6
41+
],
42+
"autorange": true
43+
},
44+
"yaxis": {
45+
"range": [
46+
-1,
47+
4
48+
],
49+
"autorange": true
50+
},
51+
"autosize": true
52+
},
53+
"frames": []
54+
}

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
"fast-isnumeric": "^1.1.2",
1616
"immutability-helper": "^3.0.0",
1717
"plotly-icons": "1.3.11",
18-
"plotly.js": "1.46.1",
18+
"plotly.js": "1.47.0",
1919
"prop-types": "^15.7.2",
2020
"raf": "^3.4.1",
2121
"react-color": "^2.17.0",
2222
"react-colorscales": "0.7.3",
2323
"react-day-picker": "^7.3.0",
24-
"react-dropzone": "^10.1.0",
24+
"react-dropzone": "^10.1.3",
2525
"react-plotly.js": "^2.3.0",
2626
"react-rangeslider": "^2.2.0",
2727
"react-resizable-rotatable-draggable": "^0.2.0",
@@ -79,7 +79,7 @@
7979
"style-loader": "0.23.1",
8080
"webpack": "4.29.6",
8181
"webpack-cli": "3.3.0",
82-
"webpack-dev-server": "3.2.1"
82+
"webpack-dev-server": "3.3.0"
8383
},
8484
"peerDependencies": {
8585
"react": ">15",

src/__stories__/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const panelsToTest = {
2626
violin: ['GraphCreatePanel', 'StyleTracesPanel'],
2727
waterfall: ['GraphCreatePanel', 'StyleTracesPanel'],
2828
sunburst: ['GraphCreatePanel', 'StyleTracesPanel'],
29+
sankey: ['GraphCreatePanel', 'StyleTracesPanel'],
2930
};
3031

3132
window.URL.createObjectURL = function() {

src/components/containers/SubplotAccordion.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,16 @@ class SubplotAccordion extends Component {
118118
pie: 0,
119119
table: 0,
120120
sunburst: 0,
121+
sankey: 0,
122+
parcoords: 0,
123+
parcats: 0,
121124
};
122125

123126
data.forEach((d, i) => {
124-
if ((d.type === 'pie' && d.values) || d.type === 'table' || d.type === 'sunburst') {
127+
if (
128+
(d.type === 'pie' && d.values) ||
129+
['pie', 'table', 'sunburst', 'sankey', 'parcoords', 'parcats'].includes(d.type)
130+
) {
125131
counter[d.type]++;
126132
const currentCount = counter[d.type];
127133

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import Field from './Field';
2+
import PropTypes from 'prop-types';
3+
import React, {Component} from 'react';
4+
import {connectToContainer} from 'lib';
5+
import RadioBlocks from '../widgets/RadioBlocks';
6+
import DataSelector from './DataSelector';
7+
import MultiColorPicker from './MultiColorPicker';
8+
import {MULTI_VALUED, COLORS} from 'lib/constants';
9+
10+
class UnconnectedColorArrayPicker extends Component {
11+
constructor(props, context) {
12+
super(props, context);
13+
14+
const {fullContainer} = props;
15+
this.rootAttr = props.attr.split('.')[0];
16+
17+
let type = null;
18+
if (
19+
!fullContainer[this.rootAttr] ||
20+
(fullContainer[this.rootAttr] && !Array.isArray(fullContainer[this.rootAttr].color))
21+
) {
22+
type = 'constant';
23+
} else if (fullContainer[this.rootAttr] && Array.isArray(fullContainer[this.rootAttr].color)) {
24+
type = 'variable';
25+
}
26+
27+
this.state = {
28+
type,
29+
value: {
30+
constant: type === 'constant' ? props.fullValue : COLORS.mutedBlue,
31+
variable: type === 'variable' ? props.fullValue : null,
32+
},
33+
selectedConstantColorOption: type === 'constant' && props.multiValued ? 'multiple' : 'single',
34+
};
35+
36+
this.setType = this.setType.bind(this);
37+
this.setValue = this.setValue.bind(this);
38+
this.setColor = this.setColor.bind(this);
39+
this.setColorScale = this.setColorScale.bind(this);
40+
this.onConstantColorOptionChange = this.onConstantColorOptionChange.bind(this);
41+
}
42+
43+
setType(type) {
44+
this.setState({type});
45+
this.context.updateContainer(
46+
type === 'constant'
47+
? {[this.rootAttr + '.color']: this.state.value[type], [this.rootAttr + '.colorsrc']: null}
48+
: {[this.rootAttr + '.color']: this.state.value[type], [this.rootAttr + '.colorsrc']: null}
49+
);
50+
}
51+
52+
setValue(inputValue) {
53+
const {type} = this.state;
54+
this.setState(
55+
type === 'constant'
56+
? {value: {constant: inputValue, variable: this.state.value[type]}}
57+
: {value: {variable: inputValue, constant: this.state.value[type]}}
58+
);
59+
this.props.updatePlot(inputValue);
60+
}
61+
62+
setColor(inputValue) {
63+
const {type} = this.state;
64+
65+
this.setState(
66+
type === 'constant'
67+
? {value: {constant: inputValue, variable: this.state.value.variable}}
68+
: {value: {variable: inputValue, constant: this.state.value.constant}}
69+
);
70+
this.props.updatePlot(inputValue);
71+
}
72+
73+
setColorScale(inputValue) {
74+
this.setState({colorscale: inputValue});
75+
this.context.updateContainer({[this.rootAttr + '.colorscale']: inputValue});
76+
}
77+
78+
onConstantColorOptionChange(value) {
79+
this.setState({
80+
selectedConstantColorOption: value,
81+
});
82+
}
83+
84+
render() {
85+
const {attr, fullValue} = this.props;
86+
const {localize: _} = this.context;
87+
const {type} = this.state;
88+
const options = [
89+
{label: _('Constant'), value: 'constant'},
90+
{label: _('Variable'), value: 'variable'},
91+
];
92+
const multiValued =
93+
this.props.multiValued || (Array.isArray(fullValue) && fullValue.includes(MULTI_VALUED));
94+
95+
return (
96+
<Field {...this.props} multiValued={multiValued} attr={attr}>
97+
<RadioBlocks options={options} activeOption={type} onOptionChange={this.setType} />
98+
{type === 'constant' ? (
99+
<MultiColorPicker
100+
attr={this.rootAttr + '.color'}
101+
multiColorMessage={_(
102+
'Each trace will be colored according to the selected colorscale.'
103+
)}
104+
singleColorMessage={_('All traces will be colored in the the same color.')}
105+
setColor={this.setColor}
106+
setColorScale={this.setColorScale}
107+
onConstantColorOptionChange={this.onConstantColorOptionChange}
108+
parentSelectedConstantColorOption={this.state.selectedConstantColorOption}
109+
/>
110+
) : multiValued ? null : (
111+
<DataSelector
112+
suppressMultiValuedMessage
113+
attr={this.rootAttr + '.color'}
114+
updatePlot={this.setValue}
115+
/>
116+
)}
117+
</Field>
118+
);
119+
}
120+
}
121+
122+
UnconnectedColorArrayPicker.propTypes = {
123+
fullValue: PropTypes.any,
124+
updatePlot: PropTypes.func,
125+
...Field.propTypes,
126+
};
127+
128+
UnconnectedColorArrayPicker.contextTypes = {
129+
localize: PropTypes.func,
130+
updateContainer: PropTypes.func,
131+
};
132+
133+
export default connectToContainer(UnconnectedColorArrayPicker);

src/components/fields/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import ColorPicker from './ColorPicker';
44
import ColorscalePicker from './ColorscalePicker';
55
import PieColorscalePicker from './PieColorscalePicker';
66
import ColorwayPicker from './ColorwayPicker';
7+
import ColorArrayPicker from './ColorArrayPicker';
78
import Dropdown from './Dropdown';
89
import Dropzone from './Dropzone';
910
import FontSelector from './FontSelector';
@@ -45,6 +46,7 @@ export {
4546
ColorPicker,
4647
ColorscalePicker,
4748
ColorwayPicker,
49+
ColorArrayPicker,
4850
DataSelector,
4951
Dropdown,
5052
ErrorBars,

src/default_panels/GraphCreatePanel.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,20 @@ const GraphCreatePanel = (props, {localize: _, setPanel}) => {
6868
}}
6969
attr="z"
7070
/>
71-
<DataSelector
72-
label={{
73-
'*': _('Measure'),
74-
}}
75-
attr="measure"
76-
/>
71+
<DataSelector label={_('Measure')} attr="measure" />
72+
73+
<PlotlySection name={_('Nodes')}>
74+
<DataSelector label={_('Labels')} attr="node.label" />
75+
<DataSelector label={_('Groups')} attr="node.groups" />
76+
<DataSelector label={_('X')} attr="node.x" />
77+
<DataSelector label={_('Y')} attr="node.y" />
78+
</PlotlySection>
79+
<PlotlySection name={_('Links')}>
80+
<DataSelector label={_('Sources')} attr="link.source" />
81+
<DataSelector label={_('Targets')} attr="link.target" />
82+
<DataSelector label={_('Values')} attr="link.value" />
83+
<DataSelector label={_('Labels')} attr="link.label" />
84+
</PlotlySection>
7785
<Radio
7886
label={_('Orientation')}
7987
attr="orientation"

0 commit comments

Comments
 (0)