Skip to content

feat(traces): add Funnel trace type#432

Open
jqnatividad wants to merge 1 commit into
plotly:mainfrom
dathere:feat/funnel-trace
Open

feat(traces): add Funnel trace type#432
jqnatividad wants to merge 1 commit into
plotly:mainfrom
dathere:feat/funnel-trace

Conversation

@jqnatividad

Copy link
Copy Markdown
Contributor

What

Adds the Funnel trace type, plus the layout-level funnelmode/funnelgap/funnelgroupgap attributes and a FunnelMode enum.

The bundled plotly.min.js has registered funnel all along:

moduleType:"trace", name:"funnel",
categories:["bar-like","cartesian","svg","oriented","showLegend","zoomScale"]

so this is a pure Rust-wrapper addition — no JS work, no bundle bump. Because funnel is cartesian, it composes with subplot grids and with static image export.

Field set

Modelled on Bar, filtered to what plotly.js actually declares for funnel:

  • offset and width are arrayOk: false in the schema, so they are scalar Option<f64> rather than Dim<f64>.
  • No base, error bars, calendars, or point selection — funnel's supplyDefaults sets moduleHasSelected: false and the attribute block has no equivalents.
  • textinfo is Option<String>. plotly.js takes a +-joined flaglist (label|text|percent initial|percent previous|percent total|value, plus none), and enumerating the combinations the way HoverInfo does would explode. This follows the existing precedent in pie.rs, which declares text_info: Option<String> and documents .text_info("label+percent"). Happy to switch to a typed flaglist if you'd prefer one.
  • Connector is a new small struct (visible/line/fillcolor) in the same module; nothing equivalent existed, since waterfall is not implemented.

Layout attributes

funnelmode/funnelgap/funnelgroupgap are included alongside the existing waterfallmode/violinmode/boxmode fields. Without funnelmode, stacked and grouped multi-trace funnels are unreachable. This mirrors WaterfallMode, which already exists without a corresponding trace.

Out of scope

funnelarea is a different trace — domain-based and pie-like rather than cartesian — and is deliberately not included here.

Tests

  • Doctest on Funnel asserting the serialized JSON.
  • default_funnel, serialize_funnel (all fields), serialize_connector, and a horizontal-funnel-in-a-subplot case covering orientation, multi-flag textinfo, and xaxis/yaxis placement.
  • serialize_funnel_mode in layout::modes.

cargo test -p plotly --features all, cargo clippy --features all -- -D warnings -A deprecated, and cargo +nightly fmt --all -- --check all pass.

plotly.js has shipped the `funnel` trace since well before the bundled
3.7.0 build — only the Rust wrapper was missing. `funnel` is bar-like and
cartesian, so unlike `funnelarea` (domain-based, pie-like, out of scope
here) it composes with subplot grids and static image export.

Field set follows `Bar`, filtered to the attributes plotly.js actually
declares for `funnel`: `offset` and `width` are scalar-only (`arrayOk:
false` in the schema) and there is no `base`, error bars, calendars or
point selection. `textinfo` is a plain `String` because plotly.js takes a
`+`-joined flaglist; this follows the existing precedent in `pie.rs`
rather than enumerating the combinations.

Also adds the layout-level `funnelmode`/`funnelgap`/`funnelgroupgap`
attributes with a `FunnelMode` enum, mirroring the existing
waterfall/violin/box mode fields — without `funnelmode`, stacked and
grouped multi-trace funnels are unreachable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant