Data Widgets
Data widgets are a set of React components for building data dashboards inside custom views. They integrate with data queries to fetch, display, and interact with query results.
The same widgets draw in an AI chat answer: the show_data tool takes a query and one
widget with its props as JSON, and the chat renders it as a card inside the answer. See
Data widgets in answers.
|
Quick Start
A data dashboard is a DataDashboard holding DataDashlet panels. Each
dashlet runs one query and hands its result to render as a single dataset,
data. Every widget takes that dataset as data={data} and binds fields by
name:
<custom name="my-dashboard" title="My Dashboard">
<template><![CDATA[<>
<DataDashboard>
<DataDashlet query="my.query" colSpan={6} height={400}
render={(data) => (
<DataWidget data={data} title="My Chart">
<DataChart data={data} type="bar" x="name" series={[{ field: "value" }]} />
</DataWidget>
)}
/>
<DataDashlet query="my.other-query" colSpan={6}
render={(data) => (
<DataWidget data={data} title="My Table" padding={0}>
<DataTable data={data} sortable />
</DataWidget>
)}
/>
</DataDashboard>
</>]]></template>
</custom>
Keep the render callback to widgets and field names. It is still plain
JSX, so a shape no widget prop can express can be built there and passed to
a widget as records (with columns), but values, colours and orderings
belong in widget props and in the query.
A field a widget binds that the query does not return shows an error naming the columns the query does return, rather than an empty widget.
The <dataset> element is optional when using DataDashlet — data fetching is handled via the query REST API.
|
The Dataset
render receives the query result as it came from the server, plus what the
dashlet controls:
| Property | Type | Description |
|---|---|---|
|
|
Result rows, with a selection column’s values replaced by their titles |
|
|
The same rows with stored values |
|
|
Column metadata, including declared format and aggregate |
|
|
|
|
|
Counted rows, or |
|
|
The window the server applied |
|
|
Query-scope totals the query declares under |
|
|
The parameter values the query ran with |
|
|
|
|
|
Re-fetch the data |
|
|
Change the pagination offset |
|
|
Opens the filter dialog for dashlet-specific params (only when the query has filterable params not covered by shared dashboard params) |
A widget given records instead of data treats them as of unknown
completeness: nothing counted them.
Values are formatted by the format, currency (or currencyField) and
scale their query column declares, unless a widget prop overrides it. A missing value shows as a
dash, never as zero.
Layout Components
DataDashboard
A CSS grid container (12 columns) that arranges dashlets. Supports shared dashboard-level filters that propagate to all child dashlets.
| Prop | Type | Description |
|---|---|---|
|
|
Shared filter config: |
|
|
Gap between dashlets (default: |
|
|
|
When params is set, a floating filter button appears in the top-right corner.
Clicking it opens a filter dialog with the specified parameters (metadata and
defaults are fetched from the query’s describe endpoint). Applying the filter
updates all dashlets that share those parameter names.
<DataDashboard params={{ query: "sale.kpi", names: ["fromDate", "toDate"] }}>
<DataDashlet query="sale.kpi" ... />
<DataDashlet query="sale.daily-trends" ... />
</DataDashboard>
DataDashlet
Fetches data from a named query and passes the dataset to a render function.
| Prop | Type | Description |
|---|---|---|
|
|
Name of the data query to execute |
|
|
Query parameters to pass |
|
|
Maps query param names to shared param names (e.g., |
|
|
Page size for server-side pagination |
|
|
Grid column span (1-12, default: full width) |
|
|
Fixed height for the dashlet |
|
|
|
When the server cut the result down to its row cap, the dashlet notes it under its content, with the counted total.
Use paramMap when a query uses different parameter names than the shared
dashboard params:
<!-- This query uses startDate/endDate instead of fromDate/toDate -->
<DataDashlet query="other.query"
paramMap={{ startDate: "fromDate", endDate: "toDate" }}
render={...} />
DataWidget
A card container with optional header (title, icon, pagination, refresh).
| Prop | Type | Description |
|---|---|---|
|
|
Supplies the loading state, the window for paging and the filter button |
|
|
Header title |
|
|
Header subtitle |
|
|
Material icon name |
|
|
Body padding (default: |
|
|
Refresh button callback, typically |
|
|
Pagination callback, typically |
|
|
A muted line under the content, such as the rule the figures are read against |
|
Override what |
Refresh and paging are opt-in: a query’s own limit, such as a top 10, is a window worth showing but not a page to move through.
Chart
DataChart
Renders charts using ECharts. Supports multiple chart types, dual value axes, mixed series types, and grouped/stacked layouts.
| Prop | Type | Description |
|---|---|---|
|
|
The dashlet’s dataset |
|
|
Chart type (see below) |
|
|
Category field (default: the first column) |
|
|
|
|
|
Value axis options by index: |
|
|
One series per value of this field, with a single series |
|
|
Stack every series (default: |
|
|
|
|
|
Smooth line curves (default: |
|
|
Show legend (default: |
|
|
Colour values |
|
|
Category label rotation angle (default: |
Supported chart types: bar, line, pie, donut, area, hbar,
scatter, radar, funnel. Axis labels and tooltips format values by the
axis format, or by the series column’s declared format.
A pie, donut or funnel refuses a negative value, and a grouped chart refuses two rows for one category in one group: the query should aggregate them.
Tables
DataTable
A sortable, exportable data table with optional grouping and totals.
| Prop | Type | Description |
|---|---|---|
|
|
The dashlet’s dataset |
|
|
Columns to show, in order, and how each is drawn (shows all if omitted; see Fields) |
|
|
Enable column sorting (default: |
|
|
Enable CSV export (default: |
|
|
Compact row height |
|
|
Show the totals row (default: |
|
|
Fields to group rows by, outermost first |
The totals row covers the columns the query aggregates. Sums and counts add
up, minima and maxima combine, and an average has no total, since an average
of averages ignores group sizes. Rows are totalled only when they are the
whole answer; for a page or a truncated result the row shows the query’s
<summary> totals, captioned as query totals, or nothing.
Amounts whose column names a currencyField are shown in each row’s currency
and are totalled only across rows in one currency: a total, a group subtotal or
a pivot cell over several currencies is left empty rather than adding them up.
Such a column’s query total is not shown, since it would add every currency.
For server-side pagination, give the dashlet a limit and the widget an
onPageChange:
<DataDashlet query="my.query" limit={10}
render={(data) => (
<DataWidget data={data} title="Paginated Table" padding={0}
onPageChange={data.setOffset} onRefresh={data.refresh}>
<DataTable data={data} sortable />
</DataWidget>
)}
/>
DataPivot
A pivot table with hierarchical row grouping, sortable columns, totals, and optional inline bars or heatmap shading.
| Prop | Type | Description |
|---|---|---|
|
|
The dashlet’s dataset |
|
|
Row dimensions, outermost first (hierarchical) |
|
|
Column dimensions for dynamic column pivoting |
|
|
|
|
|
Show row and grand totals (default: |
|
|
Show inline bar indicators (default: |
|
|
Shade each cell by its size against the measure’s largest cell at the same row level, negative values in red; a group is compared with its peer groups, never with the rows it adds up, and the total column and grand total stay unshaded (default: |
Every cell, row total and grand total is computed from the underlying rows,
per measure, so a grand average is the average of the rows rather than of
the group averages. When the query already aggregated, a measure whose column
is an average cannot be summed or averaged again; take its min or max, or
select its sum and count in the query.
When rows has several fields (e.g. ["customer", "product"]), the pivot
renders collapsible row groups. Use the + / - buttons to expand or
collapse all groups.
Indicators
DataKPI
A single metric display with icon, label, formatted value and an optional trend line.
| Prop | Type | Description |
|---|---|---|
|
|
The dashlet’s dataset |
|
|
The value’s field |
|
|
The row to read (default: |
|
|
A literal value, instead of |
|
|
Display label |
|
|
Material icon name |
|
|
|
|
|
Text around the value |
|
|
Change against another value (see below) |
|
|
A field whose values across all rows, in their order, draw a trend line; row |
|
|
Accent color: |
|
|
Visual style: |
comparison is { field | value, label?, display?, better? }: the value to
compare against, from another field of the same row or a literal; display
is percent (default) or absolute; better is higher, lower or
neutral (default). An unchanged value is neutral, a percent change against
zero is not shown, and nothing is coloured good or bad until better says
which way is good.
<DataKPI data={data} field="revenue" label="Revenue" icon="payments"
comparison={{ field: "previousRevenue", label: "vs last month", better: "higher" }} />
DataProgress
A progress indicator in bar, ring, or gauge style.
| Prop | Type | Description |
|---|---|---|
|
|
The dashlet’s dataset |
|
|
The value’s field, read from row |
|
|
The target’s field, in the same row |
|
|
Literal value and target (target default: |
|
|
Display label |
|
|
Display format: |
|
|
|
|
|
What the text states: |
|
|
Decimal places of the percentage (default: |
|
|
|
|
|
A fixed colour; without one, |
|
|
|
|
|
Size for ring/gauge: |
|
|
Show the formatted value (default: |
The default thresholds are danger from 0, warning from 50 and success from 80 percent; a given array replaces them entirely, and the band with the highest minimum the value reaches wins. The fill clamps to 0–100 while the displayed value does not. A missing value, or a target of zero or less, shows as unavailable.
DataCard
The fields of one row, stacked or on a grid, for a detail summary.
| Prop | Type | Description |
|---|---|---|
|
|
The dashlet’s dataset |
|
|
The fields to show (see Fields) |
|
|
The row to show (default: |
|
|
A CSS grid track list; without one, fields stack |
|
|
Space between fields |
|
|
Additional content below the fields |
Fields
DataList, DataCard and DataTable draw values through the same field
descriptors:
| Property | Type | Description |
|---|---|---|
|
|
The record key |
|
|
|
|
|
A label above the value, or a table column’s title |
|
|
A Material icon before the value, in the value’s colour or the accent colour |
|
|
A second field shown after this one as a range, |
|
Override the column’s declared presentation |
|
|
|
Value → colour token or CSS colour |
|
|
Value → display text |
|
|
Text appearance |
|
|
|
|
|
1-based placement on the container’s |
|
|
|
colors and labels match the stored value first, then the shown title.
Fields render in the order they are listed; grid placement only moves them
visually, so keyboard and screen-reader order follow the list.
Lists and Flows
DataList
A list of rows: ranked bars, or rows drawn from fields.
| Prop | Type | Description |
|---|---|---|
|
|
The dashlet’s dataset |
|
|
Ranked bars: the label and value fields (default: the first two columns) |
|
|
Rows drawn from fields instead (see Fields) |
|
|
The field that identifies a row; required with |
|
|
CSS grid tracks for each row’s fields |
|
|
Space between a row’s fields |
|
|
|
|
|
Maximum number of rows (default: |
|
|
Show the formatted value next to the bar |
|
|
Show rank numbers |
|
|
Bar color (semantic name or CSS color) |
A negative value draws as a red bar rather than an empty one.
<DataList data={data} itemKey="requestId"
gridColumns="auto minmax(0, 1fr) auto"
fields={[
{ field: "status", type: "dot", colors: { ABSENT: "danger", PRESENT: "success" } },
{ field: "name", bold: true },
{ field: "period", align: "end" },
{ field: "absenceType", muted: true, col: 2 },
{ field: "approval", type: "badge", col: 3 },
]} />
DataFlow
A horizontal step/pipeline visualization with optional tags.
| Prop | Type | Description |
|---|---|---|
|
|
The dashlet’s dataset: one step per row |
|
|
The step label and value fields |
|
|
The field whose value picks a step’s colour (default: |
|
|
Value → colour, e.g. |
|
|
Value format (default: the column’s declared format) |
|
|
Explicit steps instead of rows |
|
|
Optional tag badges displayed below the pipeline |
An explicit step is { label, value, format?, prefix?, suffix?, color?, icon?, action? }.
The action field navigates to a view when the step icon is clicked (e.g.
"sale.orders"). Each tag object is { label, color? }. Labels show as
written.
Utility Components
DataEmpty
Placeholder component for loading, error, and empty states. Used internally
by DataDashlet but can also be used directly.
| Prop | Type | Description |
|---|---|---|
|
|
|
|
|
Error message to display (for |
DataParams
A form component for rendering query parameter inputs. Used internally by the filter dialog but can also be used standalone for inline parameter forms.
| Prop | Type | Description |
|---|---|---|
|
|
Parameter metadata array |
|
|
Current parameter values |
|
|
Called with updated values |
|
|
|
|
|
Label for submit button |
|
|
Submit button callback |
Supports scalar types (text, number, date, boolean), selections, and
relational types (many-to-one, many-to-many) with auto-complete.
Complete Example
<custom name="sale-analytics" title="Sales Analytics">
<template><![CDATA[<>
<DataDashboard params={{ query: "sale.kpi", names: ["fromDate", "toDate"] }}>
<DataDashlet query="sale.kpi" colSpan={12}
render={(data) => (
<DataLayout cols={4} gap="1rem">
<DataWidget>
<DataKPI data={data} field="totalOrders" label="Total Orders"
icon="shopping_cart" color="primary" />
</DataWidget>
<DataWidget>
<DataKPI data={data} field="totalRevenue" label="Total Revenue"
icon="payments" format="compact" color="success" />
</DataWidget>
</DataLayout>
)}
/>
<DataDashlet query="sale.order-status" colSpan={12}
render={(data) => (
<DataWidget data={data} title="Sales Pipeline">
<DataFlow data={data} label="status" value="revenue"
colors={{ OPEN: "primary", CLOSED: "success", CANCELED: "danger" }} />
</DataWidget>
)}
/>
<DataDashlet query="sale.daily-trends" colSpan={6} height={400}
render={(data) => (
<DataWidget data={data} title="Revenue vs Orders">
<DataChart data={data} type="bar" x="date"
series={[
{ field: "revenue" },
{ field: "orderCount", type: "line", axis: 1 },
]} />
</DataWidget>
)}
/>
<DataDashlet query="sale.top-customers" colSpan={6}
render={(data) => (
<DataWidget data={data} title="Top Customers" icon="group">
<DataList data={data} label="customer" value="revenue"
showRank color="primary" />
</DataWidget>
)}
/>
{/* Dashlet with its own filter (customerId): the filter button comes from data */}
<DataDashlet query="sale.customer-orders" colSpan={12}
render={(data) => (
<DataWidget data={data} title="Customer Orders" padding={0}
onRefresh={data.refresh}>
<DataTable data={data} compact sortable />
</DataWidget>
)}
/>
<DataDashlet query="sale.customer-product-pivot" colSpan={12} height={500}
render={(data) => (
<DataWidget data={data} title="Customer x Product" padding={0}>
<DataPivot data={data} rows={["customer", "product"]}
measures={[
{ field: "draftQty" },
{ field: "openQty" },
{ field: "closedQty" },
{ field: "totalQty" },
]}
showTotals showBars />
</DataWidget>
)}
/>
</DataDashboard>
</>]]></template>
</custom>