Skip to content

Add Indicator, Histogram2d, Icicle trace types + layout uirevision (#422)#423

Open
jqnatividad wants to merge 14 commits into
plotly:mainfrom
dathere:feature/trace-type-additions
Open

Add Indicator, Histogram2d, Icicle trace types + layout uirevision (#422)#423
jqnatividad wants to merge 14 commits into
plotly:mainfrom
dathere:feature/trace-type-additions

Conversation

@jqnatividad

Copy link
Copy Markdown
Contributor

Implements the four additions from #422, field-verified against the plotly.js v3.7.0 plot-schema.json.

Depends on #421. This branch is stacked on feature/trace-attribute-backfill, so until #421 merges the diff here also shows #421's commits; they collapse to just the #422 work once #421 lands. Please merge #421 first.

What's added

  • Indicator — KPI number / delta / gauge displays, with Mode (number/delta/gauge flaglist), Number, Delta/Direction, and a full Gauge (GaugeAxis, GaugeBar, Step, Threshold). Indicator::new(value) sets only value, leaving mode to the plotly.js default (consistent with sibling constructors).
  • Histogram2d — 2D histogram heatmap. Reuses histogram::{Bins, HistFunc, HistNorm} and heat_map::Smoothing; adds histfunc/histnorm/nbinsx/nbinsy/xbins/ybins/autobinx/autobiny. new(x, y) bins raw samples; new_xyz(x, y, z) takes a pre-computed matrix.
  • Icicle — hierarchical icicle chart (sibling of Treemap/Sunburst), with icicle-specific Tiling (reusing common::Orientation), a marker without treemap's pad/corner_radius/depth_fade, plus Leaf and Root.
  • layout.uirevision — preserves UI state (zoom/pan/selection/legend visibility) across Plotly.react/newPlot re-renders, complementing the trace-level uirevision added in Backfill common trace attributes for plotly.js 3.7 parity #421.

Notes

  • GaugeAxis covers all 27 meaningful indicator.gauge.axis attributes (tick placement, styling, label/exponent/number formatting, labelalias, tickformatstops), reusing common::{TickMode, ExponentFormat, TickFormatStop} and layout::{ArrayShow, TicksDirection} rather than layout::Axis — the latter carries ~70 attributes (grid lines, spikes, range sliders, subplot anchoring) that a gauge axis doesn't support and would silently serialize as ignored JSON.
  • New PlotType variants (Histogram2d, Icicle, Indicator) serialize via rename_all = "lowercase"; re-exported from traces/mod.rs and both lib.rs lists.
  • CHANGELOG updated under [Unreleased] → Added.

Verification

  • cargo build -p plotly (default) and --features all — clean
  • cargo test -p plotly --features all — 363 unit + 34 doc tests green (exact-JSON round-trips for each new trace, plus a layout uirevision test)
  • cargo clippy -p plotly --features all --all-targets -- -D warnings -A deprecated — clean
  • cargo +nightly fmt --all — applied

Browser-render smoke test not run (no chromedriver in this environment); JSON correctness rests on schema-grounded field names + round-trip tests.

🤖 Generated with Claude Code

jqnatividad and others added 12 commits July 11, 2026 07:49
Replaces the three vendored plotly.min.js copies (plotly, plotly_static,
docs/book) with v3.7.0 and bumps the pinned CDN version strings in
plot.rs, the jupyter notebook template, plotly_static template, and the
book header.

Closes plotly#419

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Align the example CDN references with the newly bundled 3.7.0. The
wasm-yew examples were pinned at 2.14.0, and multiple_plots_example
used plotly-latest.min.js, which the CDN freezes at v1.58.5 (2021) —
a 1.x runtime against a library that now emits 3.x JSON.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The scatter_geo, scatter_map, and scatter_mapbox modules each defined a
byte-identical Selection/SelectionMarker pair. Move the single definition
to crate::common and re-export it from each module (pub use) so the public
paths plotly::traces::scatter_geo::Selection etc. keep resolving — no
serialized-output change, all existing trace tests pass unchanged.

This is the shared building block for backfilling selected/unselected
styling onto the older cartesian traces.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bring the older trace types up to plotly.js 3.7.0 parity on the common
"universal" attributes that the newer traces already expose:

- legendrank, legendwidth, uirevision on Scatter, Bar, BoxPlot, Violin,
  Histogram, HeatMap, Contour, Candlestick, Ohlc, Scatter3D, Surface,
  ScatterPolar (Scatter3D already had legendrank).
- uirevision only on Sunburst, Treemap, Table (these do not participate
  in the legend, so legendrank/legendwidth do not apply).
- offsetgroup/alignmentgroup on Scatter (Bar/BoxPlot/Violin/Histogram
  already had them).

All fields are additive Option<T> with serde renames; existing tests pass
unchanged. Contour uses a hand-written builder (no FieldSetter derive), so
its fields, Default impl, and setters were added by hand. Added focused
rename-guard tests for Scatter and Contour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add plotly.js interaction/layering attributes to the cartesian traces
that lacked them, reusing the shared common::Selection struct:

- selected, unselected, selectedpoints on Scatter, Bar, BoxPlot, Histogram.
- selectedpoints only on Candlestick and Ohlc (finance traces expose the
  selection array but not selected/unselected marker styling).
- zorder on Scatter, Bar, BoxPlot, Histogram, HeatMap, Contour, Candlestick,
  Ohlc (SVG draw order; Violin already had it).

Additive Option<T> fields with serde renames. Contour (hand-written builder)
got its field, Default entry, and setter by hand. Extended the Scatter and
Contour rename-guard tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a PeriodAlignment enum to common and the plotly.js period-positioning
attributes to the cartesian traces that support them:

- xperiod/xperiod0/xperiodalignment and yperiod/yperiod0/yperiodalignment
  on Scatter, Bar, BoxPlot, HeatMap, Contour.
- x-period only on Candlestick and Ohlc (x is the date axis for finance
  traces; they have no y-period).

Histogram is intentionally excluded (it bins raw data via xbins and has no
period attributes). Additive Option<T> fields; Contour's hand-written
builder got fields, Default entries, and setters by hand. Extended the
Scatter rename-guard test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expose plotly.js scatter fill styling beyond a solid fillcolor:

- fillpattern reuses the existing common::Pattern struct.
- fillgradient uses a new common::FillGradient struct (type/start/stop/
  colorscale), distinct from the marker-oriented common::Gradient.

Additive Option<T> fields on Scatter; extended the rename-guard test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
These hierarchy/table traces lack a `showlegend` attribute but DO expose
`legendrank`, `legendwidth`, `legend`, and `legendgrouptitle` in the
plotly.js 3.7.0 schema, so they participate in the legend. The initial
backfill wrongly gated legend participation on `showlegend` presence and
gave them `uirevision` only.

Verified against the authoritative plotly.js 3.7.0 plot-schema.json.
Adds rename-guard tests for the three traces.
… uirevision (plotly#422)

Add three new trace types and the layout-level uirevision attribute, with
field names verified against the plotly.js v3.7.0 plot-schema.json.

- Indicator: KPI number/delta/gauge displays (Gauge, Delta, Number config)
- Histogram2d: 2D histogram heatmap (histfunc/histnorm/nbinsx/nbinsy/xbins/
  ybins), reusing histogram's Bins/HistFunc/HistNorm and heat_map's Smoothing
- Icicle: hierarchical icicle chart (sibling of Treemap/Sunburst)
- layout.uirevision: preserve UI state (zoom/pan/selection) across re-renders,
  complementing the trace-level uirevision added in plotly#421

Includes PlotType variants, re-exports, exact-JSON round-trip tests, doctests,
and CHANGELOG entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the minimal GaugeAxis (range + a few ticks) with the complete set of
27 attributes plotly.js exposes on indicator.gauge.axis: tick mode/placement
(tickmode/nticks/tick0/dtick/tickvals/ticktext), tick styling
(ticks/ticklen/tickwidth/tickcolor/tickangle/tickfont), label controls
(showticklabels/tickprefix/ticksuffix + their ArrayShow flags), exponent and
number formatting (showexponent/exponentformat/minexponent/separatethousands/
tickformat/tickformatstops/ticklabelstep), and labelalias.

Reuses existing enums (common::TickMode/ExponentFormat/TickFormatStop,
layout::ArrayShow/TicksDirection) rather than layout::Axis, which carries ~70
attributes (grid lines, spikes, range sliders, subplot anchoring) that a gauge
axis does not support and would silently serialize as ignored JSON.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jqnatividad and others added 2 commits July 11, 2026 12:59
histogram2d `z` is a data_array ("aggregation data") aligned 1:1 with the
`x`/`y` samples and aggregated per bin via `hist_func` — not a pre-binned 2D
matrix like `heatmap.z`. Reword the struct and `new_xyz` docs accordingly
(directing pre-computed matrices to `HeatMap`), and replace the matrix
round-trip test with an aligned 1D aggregation example using `hist_func(Sum)`.

Addresses roborev review job 3598.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gauge.axis.ticks accepts "outside", "inside", or "" (disabled). TicksDirection
only covers inside/outside, so "no ticks" was inexpressible. Switch to
common::Ticks (Outside/Inside/None -> "outside"/"inside"/"") and add a test that
Ticks::None serializes to "".

Addresses roborev review job 3599 (the tickmode finding in that review was a
false positive: the v3.7.0 schema lists gauge.axis.tickmode as
["auto","linear","array"], so TickMode::Auto is valid).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jqnatividad added a commit to dathere/qsv that referenced this pull request Jul 11, 2026
…y.rs #423) (#4184)

* feat(viz): KPI overview row, icicle charts & layout uirevision (plotly.rs #423)

Adopt dathere/plotly branch `feature/trace-type-additions` (PR plotly/plotly.rs#423,
a strict superset of the prior pin) and use the new API in `viz`:

- viz smart now leads with a full-width KPI overview row of plotly `Indicator`
  tiles: a built-in completeness gauge plus the headline numeric measures (sum for
  extensive, mean for intensive). A measure becomes a gauge when the data
  dictionary supplies a validated `x-qsv.gauge_range` (drawn only when the range
  actually contains the observed value; a range also forces the mean so it lands
  in-scale) and a "vs target" delta when it supplies `x-qsv.target` (a
  semantically-justified goal, never a fabricated prior-period baseline). Renders
  in both the inline and typed-grid paths and stays invisible to --max-charts,
  the panel-count notices, and the render-path thresholds (it is a domain band,
  not a cartesian subplot). HTML-only.

- Add `viz icicle` subcommand and `--hierarchy-style icicle`, a level-aligned
  sibling of treemap/sunburst.

- Set layout-level `uirevision` centrally in apply_theme so user zoom/pan/
  selection/legend state survives the Plotly.newPlot re-renders that inject the
  fullscreen modebar button and recolor on theme toggle.

describegpt emission of gauge_range/target is deferred; until then the hints are
consumed from any dictionary that carries them, with completeness + number tiles
working with no dictionary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(viz): don't orphan the 8th cartesian axis behind the KPI row (roborev #3602)

The leading KPI row is domain-positioned and consumes no cartesian axis, but it
still occupies panel index 0 in `smart_grid_parts`, so `pos = n + 1` labelled the
cartesian panels behind it x2..x9. With exactly MAX_SUBPLOTS (8) cartesian panels
on the typed-grid path the 8th chart landed on x9, which the typed `Layout`
(x1..x8) has no slot for — `assign_typed_axis` silently drops it, orphaning the
trace onto the default axis.

Subtract a one-slot `axis_offset` when the leading panel is the KPI row so the
cartesian axes number x1..x8. The KPI row and geo panels never coexist in this
assembler (geo forces the inline path for HTML; image exports carry no KPI row),
so a single leading offset is sufficient. Adds a regression test that drives the
exact 8-cartesian-panel boundary via `--max-charts 8`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(viz): regenerate gallery with KPI overview row & add icicle showcase

The smart dashboards now lead with a KPI overview row (completeness gauge +
top-measure number tiles); regenerated the 7 affected smart_*.html figures so
they reflect it. Added a dedicated `viz icicle` example to gen_gallery.py so the
new chart type is showcased (gallery is now 38 figures).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(viz): correct icicle default-orientation comment; document pre-gen KPI-row lag (roborev #3604)

The icicle code comment claimed a top-down default, but plotly leaves
tiling.orientation at horizontal ("h") — root on the left, children fanning
right (confirmed in plotly.rs Tiling docs + live render). The gallery
description was already accurate; fixed the misleading code comment instead.

Also documented, per developer deferral, that the LLM-dependent pre-generated
dashboards intentionally lag the KPI/Completeness row until describegpt emits
the gauge_range/target dictionary hints and they can be regenerated in one pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (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