Charts
chart_panel renders a real Chart.js chart inside a dashboard row, backed by
a vendored copy of Chart.js — no CDN, no host gem dependency, works
air-gapped and under a strict CSP. If JavaScript never runs (disabled, the
asset 404s, CSP blocks it), the panel degrades to the same CSS bar chart
AdminSuite has always rendered — nothing is hidden until the real chart has
actually been built.
The DSL
Inside portal.dashboard do ... row do ... end end (see Portals &
dashboards):
row do
chart_panel "Signups this week",
data: -> { Signup.group_by_day(:created_at).count.map { |day, n| { label: day.strftime("%a"), value: n } } },
type: :bar,
height: 220,
color: :indigo,
span: 6
end
chart_panel(title, data: nil, span: nil, **options, &block):
title— panel heading (String)data:— aProc(or block) returning anArrayof rows; see Data contract belowtype:—:bar(default),:line,:area, or:doughnut— see Chart typesheight:— pixel height of the chart body; default192. Applied as an inline style, identically, to both the degraded bars and the live canvas, so there’s no layout shift when Chart.js takes over.color:— a theme color token (:indigo,:amber,:green,:red,:cyan,:violet; defaults to the dashboard’s theme primary). Ignored for:doughnut, which uses a fixed multi-color palette instead.total:— optional value shown as a large number in the panel header (e.g. a running total alongside the chart)span:— grid width, 1-12 (same as every other panel type)
Data contract
data: must resolve (directly, or via the Proc/block) to an Array of rows.
Each row should be a Hash with :label and :value keys — but the
partial is deliberately forgiving about anything less than that:
- String-keyed rows are tolerated.
{"label" => "Mon", "value" => 3}works exactly like{label: "Mon", value: 3}— rows are symbolized before use, the same normalizationAdminSuite::Renderer#data_tableapplies to JSONB-sourced rows. (Before 0.5.0 this silently rendered a blank/zero-height bar instead of the real value.) - Non-Hash rows are dropped, not raised on. If
data:returns[{ label: "ok", value: 3 }, 5, nil], the5andnilentries are filtered out before rendering; the good row still renders normally. - Non-numeric
values degrade to zero, not a crash. Avaluethat’s a Boolean,Hash,Array,nil, or a non-numeric String is coerced viaFloat()with a rescue to0.0— so a junk value renders as a zero-height bar (or an empty doughnut slice) instead of 500ing the whole dashboard. The display value (used in the bar’s tooltip and the doughnut list) is kept as originally provided, so a numeric String like"3"still shows"3"in the tooltip, not"3.0". - A raising
data:proc degrades to the empty state, not a 500. If the proc itself raises, the error is logged viaRails.logger.warn(with a short backtrace) and the panel renders “No chart data.” — the rest of the dashboard is unaffected.
Chart types
:bar(default) — vertical bars:line— a line chart:area— a line chart withfill: true(Chart.js has no separate “area” type; this is what:areamaps to):doughnut— a doughnut chart, using a fixed 8-color palette
An unrecognized type: (a typo, or any value outside these four) falls back
to :bar and logs a warning naming the panel and the bad value — it never
raises, regardless of what’s passed (a Symbol, String, Integer, Boolean,
whatever).
Degraded (no-JS) rendering per type
:bar/:line/:area— the same percentage-height CSS bar chart, with labels underneath.:doughnut— a doughnut has no meaningful “bar” degraded form, so it renders a plain label/value list instead (one row per data point).
The no-JS degraded state
The CSS bar chart (or, for :doughnut, the label/value list) is rendered
directly into the page by the server on every request, regardless of
JavaScript. The Stimulus controller (admin-suite--chart) only touches the
DOM — building a <canvas>, constructing the Chart instance, and hiding
the bars/list — after window.Chart is confirmed loaded and a real chart
has been built. If the vendored asset is blocked, 404s, or JS is off
entirely, the bars simply stay visible; there is no flash of missing content
and no blank box.
Chart.js assets (vendor/chart.umd.min.js) load only on pages that actually
render at least one chart with data — a single content_for(:chart_assets)
hook, deduplicated across multiple chart panels on the same page.
height: and the 192px default
Before 0.5.0, the chart body was a fixed 64px strip — fine for a sparkline
row of CSS bars, but not enough room for a real chart with axes and a
legend. As of 0.5.0 the default is 192px, and it’s configurable per
panel via height:. This is a host-visible change: an existing dashboard
using chart_panel with no height: option will render its charts taller
than before. Both the degraded bars and the live canvas read the exact same
height value, so there’s no layout shift between the two.
Host partial override
Like every other panel type, chart_panel renders through
app/views/admin_suite/panels/_chart.html.erb, resolved via
AdminSuite.config.partials[:panel_chart]:
AdminSuite.configure do |config|
config.partials[:panel_chart] = "admin/panels/custom_chart"
end
If you override the partial entirely, note that the shipped Stimulus
controller (admin-suite--chart) expects
data-admin-suite--chart-series-value (a JSON array of {label, value}),
-type-value, -color-value, and -height-value data attributes on its
mount element — reuse that contract (or write your own controller) rather
than relying on the built-in one matching a differently-shaped override.