Skip to content

Commit c7a4fc2

Browse files
Merge pull request #934 from plotly/funnels
Funnel support
2 parents e930ad9 + 1242135 commit c7a4fc2

File tree

15 files changed

+174
-109
lines changed

15 files changed

+174
-109
lines changed

dev/mocks.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
"/percy/panelTest.json",
44
"/percy/bar.json",
55
"/percy/box.json",
6+
"/percy/funnel.json",
7+
"/percy/funnelarea.json",
68
"/percy/histogram.json",
79
"/percy/histogram2d.json",
810
"/percy/pie.json",

dev/percy/funnel.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"data": [
3+
{
4+
"type": "funnel",
5+
"y": ["Lead", "Pipeline", "Proposal", "Negotiation", "Closed (Won)"],
6+
"x": [ 610, 432, 231, 103, 54 ]
7+
}
8+
]
9+
}

dev/percy/funnelarea.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"data": [
3+
{
4+
"type": "funnelarea",
5+
"labels": ["Lead", "Pipeline", "Proposal", "Negotiation", "Closed (Won)"],
6+
"values": [ 610, 432, 231, 103, 54 ]
7+
}
8+
]
9+
}

dev/percy/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ export {default as box} from './box.json';
88
export {default as waterfall} from './waterfall.json';
99
export {default as sunburst} from './sunburst.json';
1010
export {default as sankey} from './sankey.json';
11+
export {default as funnel} from './funnel.json';
12+
export {default as funnelarea} from './funnelarea.json';
1113
export {default as geoTest} from './geoTest.json';

src/EditorControls.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
shamefullyAdjustSplitStyleTargetContainers,
1212
shamefullyDeleteRelatedAnalysisTransforms,
1313
shamefullyAdjustSizeref,
14+
shamefullyAdjustAxisDirection,
1415
} from './shame';
1516
import {EDITOR_ACTIONS} from './lib/constants';
1617
import isNumeric from 'fast-isnumeric';
@@ -71,7 +72,7 @@ class EditorControls extends Component {
7172
}
7273

7374
shamefullyAdjustSizeref(graphDiv, payload);
74-
75+
shamefullyAdjustAxisDirection(graphDiv, payload);
7576
shamefullyClearAxisTypes(graphDiv, payload);
7677
shamefullyAdjustAxisRef(graphDiv, payload);
7778
shamefullyAddTableColumns(graphDiv, payload);

src/components/containers/SubplotAccordion.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class SubplotAccordion extends Component {
6464
// of right type that have attr 'subplot': 'ternary' in their data.
6565

6666
/**
67-
Example:
67+
Example:
6868
{
6969
"data": [
7070
{
@@ -126,7 +126,9 @@ class SubplotAccordion extends Component {
126126
data.forEach((d, i) => {
127127
if (
128128
(d.type === 'pie' && d.values) ||
129-
['pie', 'table', 'sunburst', 'sankey', 'parcoords', 'parcats'].includes(d.type)
129+
['pie', 'table', 'sunburst', 'sankey', 'parcoords', 'parcats', 'funnelarea'].includes(
130+
d.type
131+
)
130132
) {
131133
counter[d.type]++;
132134
const currentCount = counter[d.type];

src/components/containers/TraceMarkerSection.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@ class TraceMarkerSection extends Component {
1515
setLocals(context) {
1616
const _ = this.context.localize;
1717
const traceType = context.fullContainer.type;
18-
if (['bar', 'histogram'].includes(traceType)) {
18+
if (['bar', 'histogram', 'funnel', 'waterfall'].includes(traceType)) {
1919
this.name = _('Bars');
20-
} else if (traceType === 'pie') {
21-
this.name = _('Pie Segments');
22-
} else if (traceType === 'sunburst') {
23-
this.name = _('Sunburst Segments');
20+
} else if (['funnelarea', 'pie', 'sunburst'].includes(traceType)) {
21+
this.name = _('Segments');
2422
} else {
2523
this.name = _('Points');
2624
}

src/components/fields/DataSelector.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export class UnconnectedDataSelector extends Component {
4040
'scatter',
4141
'scattergl',
4242
'bar',
43+
'funnel',
4344
'heatmap',
4445
'heatmapgl',
4546
'violin',

src/components/fields/TextPosition.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,17 @@ export default connectToContainer(UnconnectedTextPosition, {
9191
{label: _('Bottom Center'), value: 'bottom center'},
9292
{label: _('Bottom Right'), value: 'bottom right'},
9393
];
94-
if (['pie', 'bar', 'waterfall'].includes(context.container.type)) {
94+
if (['pie', 'bar', 'funnel', 'waterfall'].includes(context.container.type)) {
9595
options = [
9696
{label: _('Inside'), value: 'inside'},
9797
{label: _('Outside'), value: 'outside'},
9898
{label: _('Auto'), value: 'auto'},
9999
{label: _('None'), value: 'none'},
100100
];
101101
}
102+
if (['funnelarea'].includes(context.container.type)) {
103+
options = [{label: _('Inside'), value: 'inside'}, {label: _('None'), value: 'none'}];
104+
}
102105
plotProps.options = options;
103106
plotProps.clearable = false;
104107
},

src/components/fields/derived.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -500,12 +500,23 @@ function computeAxesRefOptions(axes, propsAttr) {
500500
export const TextInfo = connectToContainer(UnconnectedFlaglist, {
501501
modifyPlotProps: (props, context, plotProps) => {
502502
const {localize: _, container} = context;
503-
const options = [
503+
504+
let options = [
504505
{label: _('Label'), value: 'label'},
505506
{label: _('Value'), value: 'value'},
506507
{label: _('%'), value: 'percent'},
507508
];
508509

510+
if (container.type === 'funnel') {
511+
options = [
512+
{label: _('Label'), value: 'label'},
513+
{label: _('Value'), value: 'value'},
514+
{label: _('% initial'), value: 'percent initial'},
515+
{label: _('% previous'), value: 'percent previous'},
516+
{label: _('% total'), value: 'percent total'},
517+
];
518+
}
519+
509520
if (container.text) {
510521
options.push({label: _('Text'), value: 'text'});
511522
}
@@ -591,11 +602,11 @@ export const HoverInfo = connectToContainer(UnconnectedFlaglist, {
591602
options = [];
592603
}
593604

594-
if (container.labels && ['pie', 'sunburst'].includes(container.type)) {
605+
if (container.labels && ['pie', 'sunburst', 'funnelarea'].includes(container.type)) {
595606
options.push({label: _('Label'), value: 'label'});
596607
}
597608

598-
if (container.values && ['pie', 'sunburst'].includes(container.type)) {
609+
if (container.values && ['pie', 'sunburst', 'funnelarea'].includes(container.type)) {
599610
options.push({label: _('Value'), value: 'value'});
600611
}
601612

src/default_panels/StyleTracesPanel.js

Lines changed: 106 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -91,38 +91,44 @@ const StyleTracesPanel = (props, {localize: _}) => (
9191
<Numeric label={_('Max Tube segments')} attr="maxdisplayed" />
9292
</PlotlySection>
9393
<MultiColorPicker label={_('Color')} attr="color" />
94-
<TraceTypeSection name={_('Waterfall')} traceTypes={['waterfall']} mode="trace">
95-
<LayoutSection attr="name">
96-
<Radio
97-
label={_('Mode')}
98-
attr="waterfallmode"
99-
options={[{label: _('Group'), value: 'group'}, {label: _('Overlay'), value: 'overlay'}]}
100-
/>
101-
<NumericFraction label={_('Gap')} attr="waterfallgap" />
102-
<NumericFraction label={_('Group gap')} attr="waterfallgroupgap" />
103-
</LayoutSection>
104-
</TraceTypeSection>
105-
<TraceTypeSection name={_('Pie Colors')} traceTypes={['pie']} mode="trace">
94+
<TraceTypeSection
95+
name={_('Segment Colors')}
96+
traceTypes={['pie', 'sunburst', 'funnelarea']}
97+
mode="trace"
98+
>
10699
<LayoutSection attr="name">
107100
<ColorwayPicker label={_('Colors')} attr="piecolorway" />
108101
<Radio
109102
label={_('Extended Colors')}
110103
attr="extendpiecolors"
111104
options={[{label: _('On'), value: true}, {label: _('Off'), value: false}]}
112105
/>
113-
</LayoutSection>
114-
</TraceTypeSection>
115-
<TraceTypeSection name={_('Sunburst Colors')} traceTypes={['sunburst']} mode="trace">
116-
<LayoutSection attr="name">
117106
<ColorwayPicker label={_('Colors')} attr="sunburstcolorway" />
118107
<Radio
119108
label={_('Extended Colors')}
120109
attr="extendsunburstcolors"
121110
options={[{label: _('On'), value: true}, {label: _('Off'), value: false}]}
122111
/>
112+
<ColorwayPicker label={_('Colors')} attr="funnelareacolorway" />
113+
<Radio
114+
label={_('Extended Colors')}
115+
attr="extendfunnelareacolors"
116+
options={[{label: _('On'), value: true}, {label: _('Off'), value: false}]}
117+
/>
123118
</LayoutSection>
124119
</TraceTypeSection>
125-
<PlotlySection name={_('Pie Title')} attr="title.text">
120+
<PlotlySection name={_('Funnel Dimensions')} traceTypes={['funnelarea']} attr="aspectratio">
121+
<Numeric
122+
label={_('Aspect Ratio')}
123+
attr="aspectratio"
124+
step={0.01}
125+
min={0}
126+
max={2}
127+
showSlider
128+
/>
129+
<NumericFraction label={_('Base Ratio')} attr="baseratio" />
130+
</PlotlySection>
131+
<PlotlySection name={_('Subplot Title')} attr="title.text">
126132
<TextEditor label={_('Name')} attr="title.text" />
127133
<Dropdown
128134
label={'Title Position'}
@@ -231,7 +237,7 @@ const StyleTracesPanel = (props, {localize: _}) => (
231237
</PlotlySection>
232238
<TraceTypeSection
233239
name={_('Bar Grouping, Sizing and Spacing')}
234-
traceTypes={['bar', 'histogram']}
240+
traceTypes={['bar', 'histogram', 'funnel', 'waterfall']}
235241
mode="trace"
236242
>
237243
<LayoutSection attr="name">
@@ -258,6 +264,32 @@ const StyleTracesPanel = (props, {localize: _}) => (
258264
/>
259265
<NumericFractionInverse label={_('Bar Width')} attr="bargap" />
260266
<NumericFraction label={_('Bar Padding')} attr="bargroupgap" />
267+
268+
<Dropdown
269+
label={_('Bar Mode')}
270+
attr="funnelmode"
271+
options={[
272+
{label: _('Grouped'), value: 'group'},
273+
{label: _('Stacked'), value: 'stack'},
274+
{label: _('Overlaid'), value: 'overlay'},
275+
]}
276+
clearable={false}
277+
/>
278+
<NumericFractionInverse label={_('Bar Width')} attr="funnelgap" />
279+
<NumericFraction label={_('Bar Padding')} attr="funnelgroupgap" />
280+
281+
<Dropdown
282+
label={_('Bar Mode')}
283+
attr="waterfallmode"
284+
options={[
285+
{label: _('Grouped'), value: 'group'},
286+
{label: _('Stacked'), value: 'stack'},
287+
{label: _('Overlaid'), value: 'overlay'},
288+
]}
289+
clearable={false}
290+
/>
291+
<NumericFractionInverse label={_('Bar Width')} attr="waterfallgap" />
292+
<NumericFraction label={_('Bar Padding')} attr="waterfallgroupgap" />
261293
</LayoutSection>
262294
</TraceTypeSection>
263295
<PlotlySection name={_('Binning')}>
@@ -363,6 +395,60 @@ const StyleTracesPanel = (props, {localize: _}) => (
363395
<MultiColorPicker label={_('Border Color')} attr="marker.line.color" />
364396
<Numeric label={_('Max Number of Points')} attr="marker.maxdisplayed" />
365397
</TraceMarkerSection>
398+
<PlotlySection name={_('Connector Styles')}>
399+
<Radio
400+
attr="connector.visible"
401+
options={[{label: _('Show'), value: true}, {label: _('Hide'), value: false}]}
402+
/>
403+
<MultiColorPicker label={_('Fill Color')} attr="connector.fillcolor" />
404+
<Numeric label={_('Line Width')} attr="connector.line.width" />
405+
<MultiColorPicker label={_('Line Color')} attr="connector.line.color" />
406+
<LineDashSelector label={_('Line Type')} attr="connector.line.dash" />
407+
<Dropdown
408+
label={_('Line Shape')}
409+
options={[
410+
{label: _('Spanning'), value: 'spanning'},
411+
{label: _('Between'), value: 'between'},
412+
]}
413+
attr="connector.mode"
414+
clearable={false}
415+
/>
416+
</PlotlySection>
417+
<PlotlySection name={_('Increasing Marker Styles')}>
418+
<TextEditor label={_('Name')} attr="increasing.name" richTextOnly />
419+
<Numeric label={_('Width')} attr="increasing.line.width" />
420+
<MultiColorPicker label={_('Line Color')} attr="increasing.line.color" />
421+
<MultiColorPicker label={_('Marker Color')} attr="increasing.marker.color" />
422+
<MultiColorPicker label={_('Line Color')} attr="increasing.marker.line.color" />
423+
<Numeric label={_('Line Width')} attr="increasing.marker.line.width" />
424+
<MultiColorPicker label={_('Fill Color')} attr="increasing.fillcolor" />
425+
<LineDashSelector label={_('Type')} attr="increasing.line.dash" />
426+
<Radio
427+
label={_('Show in Legend')}
428+
attr="increasing.showlegend"
429+
options={[{label: _('Show'), value: true}, {label: _('Hide'), value: false}]}
430+
/>
431+
</PlotlySection>
432+
<PlotlySection name={_('Decreasing Marker Styles')}>
433+
<TextEditor label={_('Name')} attr="decreasing.name" richTextOnly />
434+
<Numeric label={_('Width')} attr="decreasing.line.width" />
435+
<MultiColorPicker label={_('Line Color')} attr="decreasing.line.color" />
436+
<MultiColorPicker label={_('Marker Color')} attr="decreasing.marker.color" />
437+
<MultiColorPicker label={_('Line Color')} attr="decreasing.marker.line.color" />
438+
<Numeric label={_('Line Width')} attr="decreasing.marker.line.width" />
439+
<MultiColorPicker label={_('Fill Color')} attr="decreasing.fillcolor" />
440+
<LineDashSelector label={_('Type')} attr="decreasing.line.dash" />
441+
<Radio
442+
label={_('Show in Legend')}
443+
attr="decreasing.showlegend"
444+
options={[{label: _('Show'), value: true}, {label: _('Hide'), value: false}]}
445+
/>
446+
</PlotlySection>
447+
<PlotlySection name={_('Total Marker Styles')}>
448+
<MultiColorPicker label={_('Marker Color')} attr="totals.marker.color" />
449+
<MultiColorPicker label={_('Line Color')} attr="totals.marker.line.color" />
450+
<Numeric label={_('Line Width')} attr="totals.marker.line.width" />
451+
</PlotlySection>
366452
<PlotlySection name={_('Ticks')}>
367453
<Numeric label={_('Width')} attr="tickwidth" />
368454
</PlotlySection>
@@ -597,59 +683,6 @@ const StyleTracesPanel = (props, {localize: _}) => (
597683
<NumericFraction label={_('Y')} attr="lightposition.y" />
598684
<NumericFraction label={_('Z')} attr="lightposition.z" />
599685
</PlotlySection>
600-
<PlotlySection name={_('Increasing Marker Styles')}>
601-
<TextEditor label={_('Name')} attr="increasing.name" richTextOnly />
602-
<Numeric label={_('Width')} attr="increasing.line.width" />
603-
<MultiColorPicker label={_('Line Color')} attr="increasing.line.color" />
604-
<MultiColorPicker label={_('Marker Color')} attr="increasing.marker.color" />
605-
<MultiColorPicker label={_('Line Color')} attr="increasing.marker.line.color" />
606-
<Numeric label={_('Line Width')} attr="increasing.marker.line.width" />
607-
<MultiColorPicker label={_('Fill Color')} attr="increasing.fillcolor" />
608-
<LineDashSelector label={_('Type')} attr="increasing.line.dash" />
609-
<Radio
610-
label={_('Show in Legend')}
611-
attr="increasing.showlegend"
612-
options={[{label: _('Show'), value: true}, {label: _('Hide'), value: false}]}
613-
/>
614-
</PlotlySection>
615-
<PlotlySection name={_('Decreasing Marker Styles')}>
616-
<TextEditor label={_('Name')} attr="decreasing.name" richTextOnly />
617-
<Numeric label={_('Width')} attr="decreasing.line.width" />
618-
<MultiColorPicker label={_('Line Color')} attr="decreasing.line.color" />
619-
<MultiColorPicker label={_('Marker Color')} attr="decreasing.marker.color" />
620-
<MultiColorPicker label={_('Line Color')} attr="decreasing.marker.line.color" />
621-
<Numeric label={_('Line Width')} attr="decreasing.marker.line.width" />
622-
<MultiColorPicker label={_('Fill Color')} attr="decreasing.fillcolor" />
623-
<LineDashSelector label={_('Type')} attr="decreasing.line.dash" />
624-
<Radio
625-
label={_('Show in Legend')}
626-
attr="decreasing.showlegend"
627-
options={[{label: _('Show'), value: true}, {label: _('Hide'), value: false}]}
628-
/>
629-
</PlotlySection>
630-
<PlotlySection name={_('Total Marker Styles')}>
631-
<MultiColorPicker label={_('Marker Color')} attr="totals.marker.color" />
632-
<MultiColorPicker label={_('Line Color')} attr="totals.marker.line.color" />
633-
<Numeric label={_('Line Width')} attr="totals.marker.line.width" />
634-
</PlotlySection>
635-
<PlotlySection name={_('Connector Styles')}>
636-
<Radio
637-
attr="connector.visible"
638-
options={[{label: _('Show'), value: true}, {label: _('Hide'), value: false}]}
639-
/>
640-
<Numeric label={_('Line Width')} attr="connector.line.width" />
641-
<MultiColorPicker label={_('Line Color')} attr="connector.line.color" />
642-
<LineDashSelector label={_('Line Type')} attr="connector.line.dash" />
643-
<Dropdown
644-
label={_('Line Shape')}
645-
options={[
646-
{label: _('Spanning'), value: 'spanning'},
647-
{label: _('Between'), value: 'between'},
648-
]}
649-
attr="connector.mode"
650-
clearable={false}
651-
/>
652-
</PlotlySection>
653686
<PlotlySection name={_('Scaling')}>
654687
<GroupCreator label={_('Scale Group')} prefix={_('Group')} attr="scalegroup" />
655688
<Radio
@@ -729,7 +762,8 @@ const StyleTracesPanel = (props, {localize: _}) => (
729762
<MultiColorPicker label={_('Line Color')} attr="link.line.color" />
730763
<Numeric label={_('Line Width')} attr="link.line.width" min={0} />
731764
</PlotlySection>
732-
<PlotlySection name={_('Hover/Tooltip Text')}>
765+
<PlotlySection name={_('Hover/Tooltip')}>
766+
<HoveronDropdown attr="hoveron" label={_('Hover on')} />
733767
<HoverTemplateSwitch attr="hovertemplate" label={_('Mode')} />
734768
<HoverInfo attr="hoverinfo" label={_('Show')} />
735769
<HoverTemplateText attr="hovertemplate" label={_('Template')} />
@@ -762,9 +796,6 @@ const StyleTracesPanel = (props, {localize: _}) => (
762796
<Text label={_('Value Format')} attr="valueformat" />
763797
<Text label={_('Value Suffix')} attr="valuesuffix" />
764798
</PlotlySection>
765-
<PlotlySection name={_('Hover Action')}>
766-
<HoveronDropdown attr="hoveron" label={_('Hover on')} />
767-
</PlotlySection>
768799
<TraceTypeSection
769800
name={_('Error Bars X')}
770801
traceTypes={['scatter', 'scattergl', 'scatter3d', 'bar']}

0 commit comments

Comments
 (0)