@altertable/data-app 0.65.0 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +38 -1
- package/CONTRIBUTING.md +54 -14
- package/README.md +1 -1
- package/dist/chunks/index-23dktynd.js +80 -0
- package/dist/chunks/index-23dktynd.js.map +10 -0
- package/dist/chunks/index-8qfxv0h5.js +14160 -0
- package/dist/chunks/index-8qfxv0h5.js.map +108 -0
- package/dist/chunks/index-ag0cgnvq.js +639 -0
- package/dist/chunks/index-ag0cgnvq.js.map +13 -0
- package/dist/chunks/index-dq7b35fn.js +307 -0
- package/dist/chunks/index-dq7b35fn.js.map +13 -0
- package/dist/chunks/{contract-a061yj6r.js → index-ev2b5aaf.js} +41 -72
- package/dist/chunks/index-ev2b5aaf.js.map +12 -0
- package/dist/chunks/{contract-awa2d5b1.js → index-f5cnbrjz.js} +19 -2
- package/dist/chunks/{contract-awa2d5b1.js.map → index-f5cnbrjz.js.map} +4 -3
- package/dist/chunks/{contract-sm7j7k95.js → index-ktmvs916.js} +9 -150
- package/dist/chunks/index-ktmvs916.js.map +13 -0
- package/dist/chunks/{contract-txjy0en2.js → index-m6n8ctfc.js} +64 -72
- package/dist/chunks/index-m6n8ctfc.js.map +10 -0
- package/dist/chunks/index-mt93ff2b.js +28 -0
- package/dist/chunks/index-mt93ff2b.js.map +10 -0
- package/dist/chunks/{contract-tf8c3qpv.js → index-qd5xgx6y.js} +46 -5
- package/dist/chunks/index-qd5xgx6y.js.map +13 -0
- package/dist/chunks/index-x69p7cv7.js +149 -0
- package/dist/chunks/index-x69p7cv7.js.map +10 -0
- package/dist/client/index.js +12 -7
- package/dist/client/index.js.map +1 -1
- package/dist/core/appearance.js +1 -1
- package/dist/core/config.js +7 -3
- package/dist/core/config.js.map +1 -1
- package/dist/core/contract.js +24 -6
- package/dist/core/contract.js.map +1 -1
- package/dist/core/format.js +1 -1
- package/dist/embed/index.js +16 -12
- package/dist/embed/index.js.map +4 -4
- package/dist/index.js +12 -0
- package/dist/index.js.map +9 -0
- package/dist/local.js +20 -30
- package/dist/local.js.map +7 -8
- package/dist/react/embed/index.js +1 -1
- package/dist/react/embed/index.js.map +2 -2
- package/dist/react/index.js +2567 -11542
- package/dist/react/index.js.map +16 -91
- package/dist/react/ui/index.js +2272 -0
- package/dist/react/ui/index.js.map +28 -0
- package/dist/server.js +13 -26
- package/dist/server.js.map +6 -7
- package/dist/types/client/annotations.d.ts +27 -0
- package/dist/types/client/data-client.d.ts +1 -2
- package/dist/types/client/iframe.d.ts +4 -2
- package/dist/types/client/index.d.ts +1 -0
- package/dist/types/core/annotations.d.ts +94 -0
- package/dist/types/core/appearance.d.ts +2 -2
- package/dist/types/core/config.d.ts +48 -4
- package/dist/types/core/contract.d.ts +21 -11
- package/dist/types/core/dimension.d.ts +6 -4
- package/dist/types/core/filters.d.ts +44 -0
- package/dist/types/core/messages.d.ts +2 -0
- package/dist/types/core/operation-types.d.ts +5 -2
- package/dist/types/core/operation.d.ts +3 -4
- package/dist/types/core/presentation.d.ts +2 -0
- package/dist/types/core/queries.d.ts +16 -0
- package/dist/types/core/uuid.d.ts +2 -0
- package/dist/types/core/variables.d.ts +34 -18
- package/dist/types/embed/runtime-html.d.ts +2 -0
- package/dist/types/embed/source.d.ts +2 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/react/annotations/AnnotationBar.d.ts +30 -0
- package/dist/types/react/annotations/AnnotationControls.d.ts +10 -0
- package/dist/types/react/annotations/AnnotationEditor.d.ts +16 -0
- package/dist/types/react/annotations/AnnotationMarkers.d.ts +19 -0
- package/dist/types/react/annotations/AnnotationSelectionLayer.d.ts +12 -0
- package/dist/types/react/annotations/AnnotationTarget.d.ts +9 -0
- package/dist/types/react/annotations/AnnotationTooltip.d.ts +9 -0
- package/dist/types/react/annotations/AnnotationTrigger.d.ts +10 -0
- package/dist/types/react/annotations/annotation-editor-state.d.ts +56 -0
- package/dist/types/react/annotations/annotation-screenshot.d.ts +4 -0
- package/dist/types/react/annotations/annotation-targets.d.ts +33 -0
- package/dist/types/react/annotations/getAnnotationProps.d.ts +15 -0
- package/dist/types/react/annotations/styles.d.ts +3 -0
- package/dist/types/react/annotations/useAnnotationGeometry.d.ts +22 -0
- package/dist/types/react/annotations/useDataAppAnnotations.d.ts +22 -0
- package/dist/types/react/app-context.d.ts +3 -0
- package/dist/types/react/bindings.d.ts +110 -0
- package/dist/types/react/content.d.ts +24 -11
- package/dist/types/react/hooks.d.ts +29 -787
- package/dist/types/react/index.d.ts +31 -106
- package/dist/types/react/mount.d.ts +6 -4
- package/dist/types/react/source-owner.d.ts +2 -0
- package/dist/types/react/style-contract.d.ts +80 -0
- package/dist/types/react/style-validation.d.ts +2 -0
- package/dist/types/react/ui/AboutData.d.ts +9 -1
- package/dist/types/react/ui/AppHeader.d.ts +1 -2
- package/dist/types/react/ui/AppLayout.d.ts +3 -9
- package/dist/types/react/ui/AppToolbar.d.ts +5 -8
- package/dist/types/react/ui/AreaChart.d.ts +7 -0
- package/dist/types/react/ui/BarChart.d.ts +6 -0
- package/dist/types/react/ui/Button.d.ts +3 -3
- package/dist/types/react/ui/ChartLegend.d.ts +31 -0
- package/dist/types/react/ui/Checkbox.d.ts +15 -4
- package/dist/types/react/ui/CheckboxGroup.d.ts +7 -0
- package/dist/types/react/ui/{Combobox.d.ts → ChoicePicker.d.ts} +10 -15
- package/dist/types/react/ui/{ComparisonVisual.d.ts → Comparison.d.ts} +3 -3
- package/dist/types/react/ui/ComposedChart.d.ts +41 -0
- package/dist/types/react/ui/ComposedChartLegend.d.ts +8 -0
- package/dist/types/react/ui/ContentSkeleton.d.ts +2 -0
- package/dist/types/react/ui/DataApp.d.ts +17 -58
- package/dist/types/react/ui/DataAppFrame.d.ts +41 -0
- package/dist/types/react/ui/DataBoundary.d.ts +4 -5
- package/dist/types/react/ui/DataSection.d.ts +6 -18
- package/dist/types/react/ui/DataSectionBoundary.d.ts +30 -0
- package/dist/types/react/ui/DataValue.d.ts +9 -0
- package/dist/types/react/ui/DataWidget.d.ts +2 -1
- package/dist/types/react/ui/DateTimeTooltip.d.ts +1 -1
- package/dist/types/react/ui/DimensionPicker.d.ts +1 -1
- package/dist/types/react/ui/ExportControl.d.ts +1 -1
- package/dist/types/react/ui/FilterActions.d.ts +10 -0
- package/dist/types/react/ui/FilterBar.d.ts +3 -0
- package/dist/types/react/ui/GettingStarted.d.ts +2 -4
- package/dist/types/react/ui/HelpPopover.d.ts +3 -4
- package/dist/types/react/ui/InspectionContext.d.ts +2 -0
- package/dist/types/react/ui/InspectionProvider.d.ts +29 -0
- package/dist/types/react/ui/LineChart.d.ts +7 -0
- package/dist/types/react/ui/Menu.d.ts +12 -0
- package/dist/types/react/ui/MetricWidget.d.ts +4 -3
- package/dist/types/react/ui/NumberField.d.ts +27 -0
- package/dist/types/react/ui/NumberFilterPicker.d.ts +9 -0
- package/dist/types/react/ui/PieChart.d.ts +11 -0
- package/dist/types/react/ui/PresentStory.d.ts +2 -5
- package/dist/types/react/ui/RadioGroup.d.ts +18 -0
- package/dist/types/react/ui/RefreshControl.d.ts +1 -2
- package/dist/types/react/ui/ScatterChart.d.ts +17 -0
- package/dist/types/react/ui/SearchField.d.ts +1 -2
- package/dist/types/react/ui/SearchInput.d.ts +3 -1
- package/dist/types/react/ui/Select.d.ts +14 -0
- package/dist/types/react/ui/SelectionMark.d.ts +2 -1
- package/dist/types/react/ui/Sheet.d.ts +2 -1
- package/dist/types/react/ui/Skeleton.d.ts +5 -2
- package/dist/types/react/ui/StaticDataApp.d.ts +4 -0
- package/dist/types/react/ui/TableWidget.d.ts +19 -16
- package/dist/types/react/ui/Tabs.d.ts +6 -2
- package/dist/types/react/ui/TextWidget.d.ts +2 -1
- package/dist/types/react/ui/Tooltip.d.ts +4 -3
- package/dist/types/react/ui/TooltipSurface.d.ts +3 -0
- package/dist/types/react/ui/TrendChart.d.ts +5 -0
- package/dist/types/react/ui/UpdatedAt.d.ts +1 -1
- package/dist/types/react/ui/VisualizationWidget.d.ts +12 -13
- package/dist/types/react/ui/WidgetViewTabs.d.ts +1 -1
- package/dist/types/react/ui/chart-data.d.ts +25 -0
- package/dist/types/react/ui/chart-primitives.d.ts +20 -0
- package/dist/types/react/ui/data-context.d.ts +18 -7
- package/dist/types/react/ui/icons.d.ts +2 -0
- package/dist/types/react/ui/index.d.ts +98 -0
- package/dist/types/react/ui/keyboard.d.ts +6 -0
- package/dist/types/react/ui/metric.d.ts +1 -1
- package/dist/types/react/ui/presentation.d.ts +5 -1
- package/dist/types/react/ui/shortcuts.d.ts +14 -1
- package/dist/types/react/ui/useAppAppearance.d.ts +1 -1
- package/dist/types/react/ui/variables.d.ts +4 -2
- package/dist/types/react/view-controls.d.ts +3 -1
- package/dist/types/react/view-runtime.d.ts +29 -0
- package/dist/types/react/view.d.ts +21 -4
- package/dist/types/react/widgets.d.ts +79 -0
- package/dist/types/server/handler.d.ts +4 -4
- package/dist/worker.js +20 -737
- package/docs/app-authoring.md +72 -50
- package/docs/client.md +8 -8
- package/docs/contract.md +42 -20
- package/docs/data-context.md +91 -0
- package/docs/embed.md +10 -2
- package/docs/formatting-and-appearance.md +68 -0
- package/docs/hosted-apps.md +9 -15
- package/docs/layout.md +5 -3
- package/docs/react-embed.md +5 -4
- package/docs/react.md +22 -317
- package/docs/server-bun.md +1 -1
- package/docs/server.md +4 -4
- package/docs/stories-and-export.md +52 -0
- package/docs/styling.md +62 -0
- package/docs/ui-quality.md +20 -0
- package/docs/ui.md +196 -0
- package/docs/variables.md +171 -0
- package/docs/views.md +123 -0
- package/docs/widgets.md +145 -0
- package/examples/starter-data-app/index.tsx +115 -86
- package/package.json +25 -12
- package/dist/chunks/contract-a061yj6r.js.map +0 -12
- package/dist/chunks/contract-g6hky7x6.js +0 -282
- package/dist/chunks/contract-g6hky7x6.js.map +0 -12
- package/dist/chunks/contract-sm7j7k95.js.map +0 -14
- package/dist/chunks/contract-tf8c3qpv.js.map +0 -11
- package/dist/chunks/contract-txjy0en2.js.map +0 -10
- package/dist/chunks/contract-wz59z8pq.js +0 -8
- package/dist/chunks/contract-wz59z8pq.js.map +0 -10
- package/dist/chunks/contract-yxbjea23.js +0 -328
- package/dist/chunks/contract-yxbjea23.js.map +0 -12
- package/dist/types/react/ui/RefreshRegion.d.ts +0 -9
- package/dist/types/react/ui/SelectableBarChart.d.ts +0 -15
- package/docs/releasing.md +0 -79
- /package/dist/chunks/{contract-mev09s5v.js → index-mev09s5v.js} +0 -0
- /package/dist/chunks/{contract-mev09s5v.js.map → index-mev09s5v.js.map} +0 -0
- /package/dist/types/react/ui/{comparison.d.ts → metric-comparison.d.ts} +0 -0
package/docs/ui.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Direct UI composition
|
|
2
|
+
|
|
3
|
+
Import from `@altertable/data-app/react/ui` for setup/static screens, custom
|
|
4
|
+
controls, or a deliberately custom shell. Standard data apps use declared views
|
|
5
|
+
and bound widgets from `/react`.
|
|
6
|
+
|
|
7
|
+
`<DataApp>` here renders a static screen and accepts optional direct CSV data.
|
|
8
|
+
Direct widget forms accept local values or rows. `<DataWidget>` supplies a generic
|
|
9
|
+
frame for deliberately custom content. Use `<GettingStarted>` for
|
|
10
|
+
connection setup. Fetched data belongs to a declared view; avoid inventing
|
|
11
|
+
request states to populate a shell.
|
|
12
|
+
|
|
13
|
+
Direct `<DateRangePicker>`, `<DimensionPicker>`, and `useAppVariables()` support
|
|
14
|
+
custom control ownership. The caller supplies values and change handlers.
|
|
15
|
+
Custom tables use `<DataTable>` and its cell helpers; controls and overlays
|
|
16
|
+
include `<SearchField>`, `<ChoicePicker>`, `<Tabs>`, `<Sheet>`, and `<HelpPopover>`.
|
|
17
|
+
|
|
18
|
+
Standalone `<AboutData>`, glossary components, and `<PresentStory>` support
|
|
19
|
+
custom inspection and presentation. Supply registered context and evidence, and
|
|
20
|
+
derive findings from the displayed data. Standard widgets and `<DataApp>`
|
|
21
|
+
already own these experiences.
|
|
22
|
+
|
|
23
|
+
For framework mounting, `<DataAppProvider app={app}>` supplies app identity, shared requests and one inspection sheet. Wrap custom shells in it; app components inherit identity and appearance.
|
|
24
|
+
Call `injectDataAppStyles()` before mounting either entry; imports do not install
|
|
25
|
+
styles.
|
|
26
|
+
|
|
27
|
+
## Keyboard actions
|
|
28
|
+
|
|
29
|
+
Use `isPlainKeyEvent()` from `/react/ui` in custom key handlers so local actions
|
|
30
|
+
leave shortcut modifiers and IME composition untouched:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
onKeyDown={event => {
|
|
34
|
+
if (event.key === 'Enter' && isPlainKeyEvent(event.nativeEvent)) {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
submit();
|
|
37
|
+
}
|
|
38
|
+
}}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Pass `{ allowShift: true }` when the handler also supports Shift gestures.
|
|
42
|
+
|
|
43
|
+
## Choose a selection control
|
|
44
|
+
|
|
45
|
+
Use `<DimensionPicker>` for typed field filters and `<ChoicePicker>` for searchable
|
|
46
|
+
values. A single value uses a checkmark and closes after selection; multiple
|
|
47
|
+
values use checkbox indicators and keep the popup open. These pickers expose
|
|
48
|
+
listbox options, including search and loading feedback.
|
|
49
|
+
|
|
50
|
+
Use `<MenuTrigger>`, `<MenuButton>`, `<MenuPopover>`, `<Menu>`, and `<MenuItem>`
|
|
51
|
+
for commands or short choice menus
|
|
52
|
+
such as sort order or display mode. Actions use `onAction`. Set
|
|
53
|
+
`selectionMode="single"` for mutually exclusive choices (radio menu items), or
|
|
54
|
+
`selectionMode="multiple"` for independent toggles (checkbox menu items). Menus
|
|
55
|
+
support arrow keys, typeahead, Escape, and focus return to their trigger. Keep
|
|
56
|
+
search fields and other form inputs outside menus.
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<MenuTrigger>
|
|
60
|
+
<MenuButton>Sort order</MenuButton>
|
|
61
|
+
<MenuPopover>
|
|
62
|
+
<Menu
|
|
63
|
+
aria-label="Sort order"
|
|
64
|
+
selectionMode="single"
|
|
65
|
+
selectedKeys={[sortOrder]}
|
|
66
|
+
onSelectionChange={keys => {
|
|
67
|
+
if (keys !== 'all') setSortOrder(String([...keys][0]));
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
<MenuItem id="highest">Highest first</MenuItem>
|
|
71
|
+
<MenuItem id="lowest">Lowest first</MenuItem>
|
|
72
|
+
</Menu>
|
|
73
|
+
</MenuPopover>
|
|
74
|
+
</MenuTrigger>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`<MenuButton>` accepts text or icons; give icon-only buttons an accessible name.
|
|
78
|
+
`<MenuPopover>` owns placement and popup styling.
|
|
79
|
+
|
|
80
|
+
Use `<MenuSection>` to group choices with their own selection state and
|
|
81
|
+
`<MenuSeparator>` between groups or commands. Menu selection closes the popup
|
|
82
|
+
by default; set `shouldCloseOnSelect={false}` for repeated toggles. Selection
|
|
83
|
+
indicators derive from the menu or section's selection mode; do not add checkbox
|
|
84
|
+
markup to single-choice items.
|
|
85
|
+
|
|
86
|
+
## Charts
|
|
87
|
+
|
|
88
|
+
Choose a chart using the [visualization guide](widgets.md#choose-a-visualization).
|
|
89
|
+
Import `<BarChart>`, `<LineChart>`, `<AreaChart>`, `<PieChart>`, or `<ScatterChart>` from
|
|
90
|
+
`/react/ui`; `<ComposedChart>` exposes the series primitives for custom plots.
|
|
91
|
+
Compose the visual inside `<VisualizationWidget dataset={dataset} source={result}>`
|
|
92
|
+
so rows, loading state, evidence, and inspection come from that dataset.
|
|
93
|
+
Pass ordered items with unique, nonblank IDs and finite numbers. Bar and pie
|
|
94
|
+
values must be nonnegative. Use `formatValue` for domain formatting.
|
|
95
|
+
|
|
96
|
+
Line and area charts show equally spaced samples; include missing periods in the
|
|
97
|
+
input. These charts space items evenly, so they do not represent irregular time
|
|
98
|
+
intervals. Distinguish a missing observation from measured zero when preparing
|
|
99
|
+
the samples. Pie slices represent mutually exclusive parts of one total; shares
|
|
100
|
+
use the sum of supplied items, so include Other when showing a subset of the
|
|
101
|
+
whole. Scatter points represent independent X/Y observations.
|
|
102
|
+
|
|
103
|
+
### Composed charts
|
|
104
|
+
|
|
105
|
+
Use `<ComposedChart>` from `/react/ui` for multiple series or mixed marks inside
|
|
106
|
+
`<VisualizationWidget>`. Pass the displayed rows and select fields with `dataKey`.
|
|
107
|
+
`<ComposedChart.Bar>`, `<ComposedChart.Line>`, and `<ComposedChart.Area>` share the
|
|
108
|
+
standalone charts' themed series primitives; axes, scatter, tooltip, and reference
|
|
109
|
+
primitives are also available.
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
<VisualizationWidget dataset={revenue} source={result}>
|
|
113
|
+
{rows => (
|
|
114
|
+
<ComposedChart data={rows} ariaLabel="Monthly revenue and target in euros">
|
|
115
|
+
<ComposedChart.XAxis dataKey="month" />
|
|
116
|
+
<ComposedChart.YAxis />
|
|
117
|
+
<ComposedChart.Tooltip />
|
|
118
|
+
<ComposedChart.Legend />
|
|
119
|
+
<ComposedChart.Bar dataKey="revenue" name="Revenue (€)" />
|
|
120
|
+
<ComposedChart.Line
|
|
121
|
+
dataKey="target"
|
|
122
|
+
name="Target (€)"
|
|
123
|
+
stroke="var(--atbl-chart-2)"
|
|
124
|
+
/>
|
|
125
|
+
</ComposedChart>
|
|
126
|
+
)}
|
|
127
|
+
</VisualizationWidget>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Label series and units clearly. See component JSDoc and the
|
|
131
|
+
[Recharts API](https://recharts.github.io/en-US/api/ComposedChart/) for options.
|
|
132
|
+
The dataset owns loading, empty results, evidence, CSV, and story content; keep
|
|
133
|
+
its columns aligned with the displayed measures.
|
|
134
|
+
|
|
135
|
+
### Legends
|
|
136
|
+
|
|
137
|
+
Include `<ComposedChart.Legend />` to derive labels and markers from the series;
|
|
138
|
+
omit it when no legend is needed. For any chart, compose `<ChartLegend>` with
|
|
139
|
+
`<ChartLegend.Item>`, `<ChartLegend.Marker>`, and `<ChartLegend.Label>` using the
|
|
140
|
+
same names and colors as the plot.
|
|
141
|
+
`<PieChart>` includes a legend by default; `showLegend={false}` omits it.
|
|
142
|
+
|
|
143
|
+
Legends align cells in a container-responsive grid, truncate labels, and reserve
|
|
144
|
+
an overflow cell for “+X more.” Use `maxVisibleItems` to choose the limit or
|
|
145
|
+
`layout="vertical"` for a list. Legends describe series; authored toggles or a
|
|
146
|
+
composed legend's `onClick` leave series visibility to the app.
|
|
147
|
+
|
|
148
|
+
## Filter controls
|
|
149
|
+
|
|
150
|
+
Direct controls own accessible input and call the supplied change handler;
|
|
151
|
+
filter declarations own URL state, validation, and operation meaning.
|
|
152
|
+
|
|
153
|
+
| Control | Use |
|
|
154
|
+
| ---------------------------------- | ----------------------------------------------------------------- |
|
|
155
|
+
| `<SearchField>` | Search text |
|
|
156
|
+
| `<Select>` | One choice from a small fixed list |
|
|
157
|
+
| `<ChoicePicker>` | Searchable choices with explicit `selectionMode` |
|
|
158
|
+
| `<RadioGroup>` and `<Radio>` | Visible exclusive choices |
|
|
159
|
+
| `<CheckboxGroup>` and `<Checkbox>` | Visible independent choices |
|
|
160
|
+
| `<SegmentedControl>` | Compact radio choices |
|
|
161
|
+
| `<NumberField>` | Localized numeric input; empty is `null` |
|
|
162
|
+
| `<NumberRangeField>` | Exact, inclusive minimum/maximum; omitted bounds are unrestricted |
|
|
163
|
+
| `<DateRangePicker>` | Explicit or relative periods |
|
|
164
|
+
| `<DimensionPicker>` | Typed categorical predicates with fixed or facet options |
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
<RadioGroup label="Metric" value={metric} onChange={setMetric}>
|
|
168
|
+
<Radio value="orders">Orders</Radio>
|
|
169
|
+
<Radio value="revenue">Revenue</Radio>
|
|
170
|
+
</RadioGroup>
|
|
171
|
+
|
|
172
|
+
<CheckboxGroup label="Statuses" values={statuses} onChange={setStatuses}>
|
|
173
|
+
<Checkbox value="paid" label="Paid" />
|
|
174
|
+
<Checkbox value="pending" label="Pending" />
|
|
175
|
+
</CheckboxGroup>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
A standalone `<Checkbox>` uses `checked` and `onChange`; a group item supplies
|
|
179
|
+
`value`. `<SegmentedControl>` accepts the same labeled options as `<Select>` and
|
|
180
|
+
keeps radio-group semantics. Numeric range controls retain invalid draft bounds
|
|
181
|
+
and emit only valid intervals. Numeric input and compact selects respect the
|
|
182
|
+
shared mobile text-size minimum.
|
|
183
|
+
|
|
184
|
+
Use `<FilterBar>` to arrange predicates and `<VariableBar>` for mixed app
|
|
185
|
+
parameters. Each filter shows its current value; categorical single selection includes All.
|
|
186
|
+
`<FilterActions>` provides one Clear icon with a tooltip, plus optional paired
|
|
187
|
+
Apply/Cancel actions for app-owned drafts. The caller owns those actions; the
|
|
188
|
+
components do not infer query state.
|
|
189
|
+
|
|
190
|
+
`<NumberFilterPicker>` composes numeric fields inside a dedicated popover. Edits
|
|
191
|
+
remain local until Apply; Cancel and dismissal discard them. Use `<NumberField>`
|
|
192
|
+
and `<NumberRangeField>` as inline input primitives when composing forms.
|
|
193
|
+
|
|
194
|
+
Use `<Button variant="primary">` for the main committing action, such as Apply.
|
|
195
|
+
Primary buttons use the theme foreground as their fill and the theme background
|
|
196
|
+
for contrasting text. Use outline and ghost buttons for secondary actions.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Variables and filters
|
|
2
|
+
|
|
3
|
+
## Time views and field filters
|
|
4
|
+
|
|
5
|
+
`createDataHooks(client).defineTimeView()` owns the `period` variable, calendar
|
|
6
|
+
controls, and displayed-period label. Declare `time: { contract, defaultValue }`,
|
|
7
|
+
an operation, `isEmpty` (a predicate), and `emptyFallback` (a title and optional
|
|
8
|
+
description for an empty result). With no additional variables, its default
|
|
9
|
+
input is the calendar request. With additional variables, it is `{ period, ...variables }`.
|
|
10
|
+
Supply an `input` mapper for a different operation shape and `bindings` to extract
|
|
11
|
+
nested period or field-filter inputs. Mappings must preserve the selected values.
|
|
12
|
+
|
|
13
|
+
`dimensionFilter()` from `/contract` requires exactly one option source: fixed `options` or a `facet`.
|
|
14
|
+
Use `defineFacetFilter()` to bind a facet operation and its typed input. `<DimensionPicker>` offers missing values separately and keeps selected values
|
|
15
|
+
available when they have zero matches.
|
|
16
|
+
|
|
17
|
+
## Declare controls once
|
|
18
|
+
|
|
19
|
+
Use `searchVariable()`, `choiceVariable()`, and `dateRangeVariable()` in a view's
|
|
20
|
+
`variables`. `<DataApp view={view}>` generates their controls and keeps values in the URL.
|
|
21
|
+
Keep local search out of the operation input when it only filters loaded rows.
|
|
22
|
+
|
|
23
|
+
The [date range contract](contract.md#shared-date-ranges) owns source coverage,
|
|
24
|
+
maximum range, time zone, and comparison rules. `defineTimeView()` is the compact
|
|
25
|
+
path for a view whose primary input is a period. Use `defineDataView()` for fixed
|
|
26
|
+
snapshots or other input shapes.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
const activity = defineTimeView({
|
|
30
|
+
dataContext,
|
|
31
|
+
operation: 'activity',
|
|
32
|
+
time: { contract: calendar, defaultValue: { kind: 'preset', id: 'last-7' } },
|
|
33
|
+
variables: { region: searchVariable({ key: 'region' }) },
|
|
34
|
+
input: values => ({
|
|
35
|
+
filters: { period: values.period, region: values.region },
|
|
36
|
+
}),
|
|
37
|
+
bindings: {
|
|
38
|
+
period: input => input.filters.period,
|
|
39
|
+
region: input => input.filters.region,
|
|
40
|
+
},
|
|
41
|
+
isEmpty: data => data.rows.length === 0,
|
|
42
|
+
emptyFallback: { title: 'No activity' },
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The operation, calendar, and row shape are app-owned. Bindings must extract
|
|
47
|
+
selected values unchanged.
|
|
48
|
+
|
|
49
|
+
## URL state and custom controls
|
|
50
|
+
|
|
51
|
+
Relative date presets remain relative. Explicit dates use `start`, `end`, and
|
|
52
|
+
`compare` for the `period` variable; other date variables derive these keys from
|
|
53
|
+
their own key. Invalid URL values fall back to validated defaults. Do not use
|
|
54
|
+
reserved inspection, presentation, or navigation keys for app variables.
|
|
55
|
+
|
|
56
|
+
Choose push history for meaningful selections and replace history for typing.
|
|
57
|
+
Back/Forward restores the same values and controls. Use generated controls for
|
|
58
|
+
standard data apps. Direct pickers and standalone URL state belong to
|
|
59
|
+
[direct UI composition](ui.md).
|
|
60
|
+
|
|
61
|
+
## Fixed options and query-backed facets
|
|
62
|
+
|
|
63
|
+
Declare fixed choices with `dimensionFilter()` from `/contract`:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const region = dimensionFilter({
|
|
67
|
+
key: 'region',
|
|
68
|
+
label: 'Region',
|
|
69
|
+
valueType: 'string',
|
|
70
|
+
selectionMode: 'multiple',
|
|
71
|
+
options: [{ value: 'Europe', label: 'Europe' }],
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
For query-backed choices use the hooks factory's `defineFacetFilter()`. The
|
|
76
|
+
facet operation returns parsed dimension options. Its input mapper reads current
|
|
77
|
+
resolved variables, including other filters:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const { defineFacetFilter, defineTimeView } = createDataHooks(client);
|
|
81
|
+
const region = defineFacetFilter({
|
|
82
|
+
key: 'region',
|
|
83
|
+
label: 'Region',
|
|
84
|
+
valueType: 'string',
|
|
85
|
+
selectionMode: 'multiple',
|
|
86
|
+
facet: {
|
|
87
|
+
operation: 'regions',
|
|
88
|
+
input: values => ({ period: values.period as DateRangeRequest }),
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The client's operation map defines `regions` and its typed period input;
|
|
94
|
+
import `DateRangeRequest` from `/contract`. Put `region` in the view's `variables`
|
|
95
|
+
alongside its period. Use `parseFacetOptions()` to validate facet results.
|
|
96
|
+
For the operation's input parser, use `parseDimensionSelection(value, region)` from `/contract`.
|
|
97
|
+
A dimension without an explicit selection represents all members.
|
|
98
|
+
Map parsed selections to the query's declared parameters; do not construct SQL
|
|
99
|
+
from filter values. Choose selection modes supported by the registered query.
|
|
100
|
+
|
|
101
|
+
Choose `selectionMode: 'single'` when one value replaces another. Its picker closes
|
|
102
|
+
after choosing a value and shows a checkmark for the current choice. Use
|
|
103
|
+
`selectionMode: 'multiple'` for independent values; checkbox indicators and an open
|
|
104
|
+
popup allow repeated selections. `All` is an explicit choice for a single-value
|
|
105
|
+
filter and the meaning of an empty selection for a multiple-value filter.
|
|
106
|
+
|
|
107
|
+
## Fixed choices
|
|
108
|
+
|
|
109
|
+
`choiceVariable()` requires labeled `options` and a valid `defaultValue`.
|
|
110
|
+
`multiChoiceVariable()` stores unique option IDs with an optional selection limit.
|
|
111
|
+
An empty fixed-choice collection means no choices; categorical filters use
|
|
112
|
+
`dimensionFilter()` when an empty selection should mean all records.
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
const variables = {
|
|
116
|
+
search: searchVariable({ key: 'q', label: 'Search' }),
|
|
117
|
+
metric: choiceVariable({
|
|
118
|
+
key: 'metric',
|
|
119
|
+
label: 'Metric',
|
|
120
|
+
defaultValue: 'orders',
|
|
121
|
+
options: [
|
|
122
|
+
{ id: 'orders', label: 'Orders' },
|
|
123
|
+
{ id: 'revenue', label: 'Revenue' },
|
|
124
|
+
],
|
|
125
|
+
}),
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Fixed single choices generate `<Select>`; multiple choices generate
|
|
130
|
+
`<ChoicePicker selectionMode="multiple">`. Direct controls can present the same
|
|
131
|
+
values as radio groups, checkbox groups, or segmented choices without changing
|
|
132
|
+
URL state.
|
|
133
|
+
|
|
134
|
+
## Numeric and boolean predicates
|
|
135
|
+
|
|
136
|
+
Import `numberFilter()` and `booleanFilter()` from `/contract` and put their
|
|
137
|
+
results in the view's `variables`. Both have an explicit `{ kind: 'all' }`
|
|
138
|
+
unrestricted state. Numeric filters support inclusive, open-ended ranges and
|
|
139
|
+
comparisons: `eq`, `ne`, `gt`, `gte`, `lt`, and `lte`. Boolean filters distinguish
|
|
140
|
+
Any from `{ kind: 'is', value: true }` and `{ kind: 'is', value: false }`.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const amount = numberFilter({ key: 'amount', label: 'Amount', min: 0 });
|
|
144
|
+
const active = booleanFilter({ key: 'active', label: 'Active' });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Use `parseNumberSelection()` and `parseBooleanSelection()` in the operation's
|
|
148
|
+
input parser with the same filter declarations. Numeric predicates generate `<NumberFilterPicker>` panels. Draft edits reach
|
|
149
|
+
operation input only after Apply; Cancel and dismissal preserve the applied
|
|
150
|
+
value. Invalid ranges disable Apply.
|
|
151
|
+
Input mappings and bindings must preserve these predicates unchanged, as with
|
|
152
|
+
categorical filters. Map parsed operators and values to declared query parameters;
|
|
153
|
+
offer only operators supported by the registered query.
|
|
154
|
+
|
|
155
|
+
## Exclusion, clear, and reset
|
|
156
|
+
|
|
157
|
+
Set `allowExclusion: true` on `dimensionFilter()` to support
|
|
158
|
+
`{ kind: 'exclude', members }`. `<DimensionPicker>` then exposes Include/Exclude
|
|
159
|
+
alongside selected members. Enable exclusion only when the registered query supports
|
|
160
|
+
it, including the intended behavior for missing values.
|
|
161
|
+
|
|
162
|
+
Generated controls show active values within each filter and one collective
|
|
163
|
+
Clear icon with a tooltip. Clear removes restrictions from search and predicate filters.
|
|
164
|
+
`resetAll()` restores all
|
|
165
|
+
configured variable defaults, including fixed choices and periods. Nonempty
|
|
166
|
+
default filters can therefore be active immediately after Reset.
|
|
167
|
+
|
|
168
|
+
Direct `useAppVariables()` callers can use `clearAll()` and `resetAll()` for one
|
|
169
|
+
atomic history update. `<FilterActions>` also supports app-owned draft state
|
|
170
|
+
with paired Apply/Cancel handlers; derive queries and visible controls from applied
|
|
171
|
+
values while a draft is being edited.
|
package/docs/views.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Views and displayed results
|
|
2
|
+
|
|
3
|
+
## Bind a view
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { createDataClient } from '@altertable/data-app/client';
|
|
7
|
+
import {
|
|
8
|
+
createDataHooks,
|
|
9
|
+
DataApp,
|
|
10
|
+
DataSection,
|
|
11
|
+
} from '@altertable/data-app/react';
|
|
12
|
+
import type { operations } from '#app/operations.ts';
|
|
13
|
+
|
|
14
|
+
const client = createDataClient<typeof operations>();
|
|
15
|
+
const { defineDataView } = createDataHooks(client);
|
|
16
|
+
const activityView = defineDataView({
|
|
17
|
+
dataContext,
|
|
18
|
+
operation: 'activity',
|
|
19
|
+
describeInput: () => 'all activity',
|
|
20
|
+
isEmpty: data => data.rows.length === 0,
|
|
21
|
+
emptyFallback: { title: 'No activity' },
|
|
22
|
+
});
|
|
23
|
+
const content = activityView.content(result => (
|
|
24
|
+
<ActivityWidgets source={result} />
|
|
25
|
+
));
|
|
26
|
+
|
|
27
|
+
function App() {
|
|
28
|
+
return (
|
|
29
|
+
<DataApp view={activityView} story={story} datasets={[activityDataset]}>
|
|
30
|
+
<DataSection content={content} />
|
|
31
|
+
</DataApp>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Omit `variables` when there are no controls. Omit `input` when the resolved
|
|
37
|
+
variables match the operation input; nested or different inputs need a mapper.
|
|
38
|
+
The operation and `ActivityWidgets` are app-owned. The bound widgets derive
|
|
39
|
+
loading content from their source; see [widgets](widgets.md) and the
|
|
40
|
+
[complete starter](../examples/starter-data-app/index.tsx). Define
|
|
41
|
+
[filters](variables.md), [context and evidence](data-context.md), and
|
|
42
|
+
[story and datasets](stories-and-export.md) for the app's question.
|
|
43
|
+
|
|
44
|
+
Render static text immediately; skeletonize only dynamic content. Keep section
|
|
45
|
+
introductions outside request boundaries so they remain visible on empty and
|
|
46
|
+
error states.
|
|
47
|
+
|
|
48
|
+
Use `<DataSection>` for each independently fetched subtree and `view.content()`
|
|
49
|
+
to share its loading and ready layout. Refreshes retain displayed content.
|
|
50
|
+
|
|
51
|
+
Use `<DataValue>` for a dynamic value within static prose:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
<p>
|
|
55
|
+
Orders in the last 7 days: <DataValue metric={weeklyOrders} source={result} />
|
|
56
|
+
</p>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Bind the whole sentence with `<TextWidget>` when its wording depends on the result.
|
|
60
|
+
Use `<DataValue scope={result.scope} />` for the displayed scope label.
|
|
61
|
+
|
|
62
|
+
Use the same [date range contract](contract.md#shared-date-ranges) for the
|
|
63
|
+
operation and its view. For nested inputs in `defineTimeView()`, use
|
|
64
|
+
`bindings.period` to extract the selected period; see the
|
|
65
|
+
[time-view example](variables.md#declare-controls-once).
|
|
66
|
+
|
|
67
|
+
Use `result.scope` inside `view.content()` as a reading for scope text. In stories and exports,
|
|
68
|
+
`view.scope(snapshot)` returns the same label from the displayed input, using
|
|
69
|
+
`describeInput` or the view's date label.
|
|
70
|
+
|
|
71
|
+
Declare standard controls in the view's [variables](variables.md). Keep
|
|
72
|
+
local-only filters out of the operation's `input`.
|
|
73
|
+
|
|
74
|
+
For large tables, use query-backed pagination with a stable sort and total
|
|
75
|
+
count. Client pagination and search cover only the rows already returned.
|
|
76
|
+
|
|
77
|
+
Declare evidence references on `view.dataset()` for charts and tables. `<MetricWidget>` uses
|
|
78
|
+
its metric definition for the label, format, and evidence; keep view-specific
|
|
79
|
+
descriptions on the widget.
|
|
80
|
+
|
|
81
|
+
`<MetricWidget>` and `<Comparison>` share a metric reading. Comparisons
|
|
82
|
+
follow the displayed result's range.
|
|
83
|
+
|
|
84
|
+
Use the [format helpers](formatting-and-appearance.md) for metric formats and values in tables,
|
|
85
|
+
charts, and custom views.
|
|
86
|
+
|
|
87
|
+
## Preserve displayed results
|
|
88
|
+
|
|
89
|
+
The callback in `view.content()` receives a displayed source with `loading`,
|
|
90
|
+
`data`, `input`, and `scope`. Bind widgets to that source. Its input remains the
|
|
91
|
+
one that produced the visible data during refreshes and failures.
|
|
92
|
+
|
|
93
|
+
Keep the client stable across renders; create it outside the component.
|
|
94
|
+
|
|
95
|
+
## Request ownership
|
|
96
|
+
|
|
97
|
+
`<DataApp>` owns the toolbar, controls, inspection, and page feedback for its
|
|
98
|
+
primary `view`. It always renders children, so introductions and static
|
|
99
|
+
context remain visible while a section loads. It requires both
|
|
100
|
+
[story and CSV export](stories-and-export.md) for a data request.
|
|
101
|
+
|
|
102
|
+
Place `<DataSection content={content}>` around each independently fetched subtree.
|
|
103
|
+
Declare its shared loading and ready layout with `view.content()`. The section
|
|
104
|
+
inherits empty copy from its view, with an optional local override, and handles
|
|
105
|
+
initial errors and retries. Primary content shares the app's displayed result;
|
|
106
|
+
independent content uses its own context and executed queries.
|
|
107
|
+
|
|
108
|
+
Keep related datasets in one view so export and story share a coherent snapshot.
|
|
109
|
+
|
|
110
|
+
| State | Display |
|
|
111
|
+
| --------------- | --------------------------------------------------------- |
|
|
112
|
+
| Initial loading | The authored loading fallback; selectors do not run |
|
|
113
|
+
| Initial error | A source-aware error with retry when recovery is possible |
|
|
114
|
+
| Empty result | The authored empty fallback |
|
|
115
|
+
| Ready | Data and the input that produced it |
|
|
116
|
+
| Updating | The prior result and its original input, fully readable |
|
|
117
|
+
| Failed refresh | The prior result and its evidence, with recovery feedback |
|
|
118
|
+
|
|
119
|
+
`isEmpty` is app-owned. A measured zero is valid data unless the analysis explicitly
|
|
120
|
+
says otherwise. Use the displayed input in content, export, and story callbacks for scope labels.
|
|
121
|
+
|
|
122
|
+
`<DataApp>` owns request execution, refresh, and cancellation. Keep raw request
|
|
123
|
+
state out of app code.
|
package/docs/widgets.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Widgets and narrative
|
|
2
|
+
|
|
3
|
+
## Choose a widget
|
|
4
|
+
|
|
5
|
+
Metrics, tables, and visualizations from `/react` consume view bindings and a source. For static displays, use
|
|
6
|
+
[direct UI composition](ui.md).
|
|
7
|
+
Manage requests in the [view and section](views.md); show skeletons for loading
|
|
8
|
+
readings without inventing values. Use `isEmpty` for the predicate and
|
|
9
|
+
`emptyFallback` for empty-result copy.
|
|
10
|
+
|
|
11
|
+
| Need | Component |
|
|
12
|
+
| ------------------------------------------------ | ----------------------- |
|
|
13
|
+
| Metric with formatting, comparison, and evidence | `<MetricWidget>` |
|
|
14
|
+
| Custom chart or alternate chart views | `<VisualizationWidget>` |
|
|
15
|
+
| Defined columns, local search, and pagination | `<TableWidget>` |
|
|
16
|
+
| Narrative panel | `<TextWidget>` |
|
|
17
|
+
| Borderless explanatory prose | `<TextContent>` |
|
|
18
|
+
| One data-dependent phrase within static prose | `<DataValue>` |
|
|
19
|
+
|
|
20
|
+
Use [layout](layout.md) for `<Stack>`, `<Grid>`, and responsive `<GridItem>` spans.
|
|
21
|
+
`<Skeleton>` supports custom fallback content; `/react/ui` supplies
|
|
22
|
+
`<DataAppSkeleton>` for iframe startup.
|
|
23
|
+
|
|
24
|
+
## Choose a visualization
|
|
25
|
+
|
|
26
|
+
Choose the component by the reader's question. Compose dataset visuals inside
|
|
27
|
+
`<VisualizationWidget>`; use `<MetricWidget>` for a headline value. Built-in
|
|
28
|
+
charts come from `/react/ui`; `<Ranking>`, `<Breakdown>`, and `<Comparison>`
|
|
29
|
+
are also available from `/react`.
|
|
30
|
+
|
|
31
|
+
| Question or need | Component | Guidance |
|
|
32
|
+
| --------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
33
|
+
| How do categories compare? | `<BarChart>` | Compare nonnegative category values; order by value for rank or by a meaningful category order. |
|
|
34
|
+
| Which items lead? | `<Ranking>` | Use a compact ordered list with values and optional detail. Tracks scale to the largest visible item, not a total. |
|
|
35
|
+
| How does a measure change over time? | `<LineChart>` | Use ordered, equally spaced samples to emphasize the trend. |
|
|
36
|
+
| How large is the measure over time? | `<AreaChart>` | Use the same samples as a line chart when filled magnitude relative to zero helps answer the question. |
|
|
37
|
+
| What makes up the whole? | `<PieChart>` | Use a few mutually exclusive parts of one total; include the remainder as Other. |
|
|
38
|
+
| What share of an observed total does each part represent? | `<Breakdown>` | Use a compact display of values and shares with an explicit total, including when only some parts are shown. |
|
|
39
|
+
| How do several measures relate on one plot? | `<ComposedChart>` | Compose bars, lines, areas, or scatter series; label units clearly and use separate axes for different units. |
|
|
40
|
+
| How are two numeric measures related? | `<ScatterChart>` | Use independent X/Y observations to explore relationships, clusters, and outliers. |
|
|
41
|
+
| How did one metric change between periods? | `<Comparison>` | Use the metric reading and its displayed comparison period. |
|
|
42
|
+
| What are the exact values or row details? | `<TableWidget>` | Use a bound table for lookup, search, and precise comparisons. |
|
|
43
|
+
|
|
44
|
+
Prefer bars or a ranking when readers need to compare similarly sized categories;
|
|
45
|
+
use a pie for a simple composition question. Prefer a line when the trend is
|
|
46
|
+
enough; use area when the fill adds meaning. Keep exact values available in a
|
|
47
|
+
table when they matter. Follow the [chart input constraints](ui.md#charts). For multiple or mixed series,
|
|
48
|
+
use the [composed chart primitives](ui.md#composed-charts) within the same bound
|
|
49
|
+
visualization workflow.
|
|
50
|
+
|
|
51
|
+
Use widget `views` when a chart and another representation answer the same
|
|
52
|
+
question from the same dataset.
|
|
53
|
+
|
|
54
|
+
## Declare datasets and metrics
|
|
55
|
+
|
|
56
|
+
Bind reusable selections to the view. Declare evidence references directly;
|
|
57
|
+
the view validates them against its own context. A dataset's columns supply both formatted
|
|
58
|
+
table cells and raw CSV values. Widgets derive their readings, evidence, and empty fallback from the binding.
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
const countries = activityView.dataset({
|
|
62
|
+
name: 'Countries',
|
|
63
|
+
select: data => data.countries,
|
|
64
|
+
rowKey: row => row.country,
|
|
65
|
+
evidence: { id: 'countries', glossaryIds: ['identities'] },
|
|
66
|
+
columns: {
|
|
67
|
+
country: { value: row => row.country },
|
|
68
|
+
count: { value: row => row.count, format: { kind: 'count' } },
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
const total = activityView.metric(
|
|
72
|
+
{
|
|
73
|
+
id: 'tracked-identities',
|
|
74
|
+
glossaryId: 'identities',
|
|
75
|
+
format: { kind: 'count', compact: true },
|
|
76
|
+
},
|
|
77
|
+
data => ({
|
|
78
|
+
current: data.count,
|
|
79
|
+
previous: data.previousCount,
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
const content = activityView.content(result => (
|
|
83
|
+
<Grid columns={2}>
|
|
84
|
+
<MetricWidget metric={total} source={result} />
|
|
85
|
+
<TableWidget dataset={countries} source={result} />
|
|
86
|
+
<VisualizationWidget dataset={countries} source={result}>
|
|
87
|
+
{rows => <CountryChart rows={rows} />}
|
|
88
|
+
</VisualizationWidget>
|
|
89
|
+
</Grid>
|
|
90
|
+
));
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Column keys supply unique IDs and readable default labels. Use `label` for a
|
|
94
|
+
specific display/export header. Declare selectors, raw value accessors, and
|
|
95
|
+
row-key callbacks explicitly; row keys must be stable and unique.
|
|
96
|
+
Metric comparisons require a
|
|
97
|
+
view date binding. `read()` returns a loading-aware value for custom prose.
|
|
98
|
+
Pass the binding and `source={snapshot}` to reuse a widget in a story. Use the
|
|
99
|
+
source supplied by that binding's own view; reconstructed or foreign sources
|
|
100
|
+
are rejected before selection.
|
|
101
|
+
Selectors do not run during loading. Table search, pagination, descriptions, and
|
|
102
|
+
actions remain local choices. For custom cells, use `format: row => ...`; CSV
|
|
103
|
+
still uses the raw `value` accessor. Datasets default to “No results”; supply
|
|
104
|
+
`emptyFallback` for specific copy. See [export](stories-and-export.md).
|
|
105
|
+
|
|
106
|
+
## Narrative text
|
|
107
|
+
|
|
108
|
+
Use `<TextContent>` for prose within a page or custom layout, and `<TextWidget>`
|
|
109
|
+
when the explanation belongs in a titled panel alongside other widgets.
|
|
110
|
+
|
|
111
|
+
Give text a purpose: frame the question, explain how to interpret a comparison,
|
|
112
|
+
qualify a finding, or suggest what to explore next. Choose the content for the
|
|
113
|
+
reader's question.
|
|
114
|
+
|
|
115
|
+
For data-dependent text, pass `metric` or `dataset` and `source` to `<TextWidget>`.
|
|
116
|
+
It derives its title and evidence. A metric formats its current value by default;
|
|
117
|
+
use a child renderer for authored prose. Dataset renderers receive displayed rows,
|
|
118
|
+
including an empty selection, so they can explain zero activity.
|
|
119
|
+
|
|
120
|
+
Use `<DataValue metric={total} source={result} />` for a formatted value inside
|
|
121
|
+
static prose, or pass a dataset and row renderer. Its default loading fallback is
|
|
122
|
+
an inline skeleton. Use `<DataValue scope={result.scope} />` for a scope label.
|
|
123
|
+
All selectors use the displayed source; loading selectors do not run.
|
|
124
|
+
Use `<TextContent>` for static instructions; local filters should feed the same
|
|
125
|
+
filtered data to the text and its related visualization.
|
|
126
|
+
|
|
127
|
+
## Custom visuals and controls
|
|
128
|
+
|
|
129
|
+
`<Ranking>`, `<Breakdown>`, and `<Comparison>`
|
|
130
|
+
compose inside authored widgets. Use [built-in charts](ui.md#charts) from `/react/ui`
|
|
131
|
+
inside `<VisualizationWidget dataset={dataset} source={result}>` for displayed rows.
|
|
132
|
+
The dataset owns evidence and empty copy; an empty row selection shows that fallback. A visualization's `views` declaration keeps
|
|
133
|
+
alternate-view selection shared with its inspection sheet. Give views stable IDs
|
|
134
|
+
and keep interactive state above the widget, since inspection may render it again.
|
|
135
|
+
|
|
136
|
+
`<TableWidget>` searches and paginates only the supplied rows. Use query-backed
|
|
137
|
+
pagination with a stable sort and total count for larger datasets. The `limit`
|
|
138
|
+
mode shows a bounded preview without local pagination. Custom tables can use
|
|
139
|
+
`<DataTable>`, `<DataTableEmptyRow>`, `<DataTableTimestamp>`, and `<DataTableShare>`.
|
|
140
|
+
|
|
141
|
+
For custom controls, tables, and overlays, see [direct UI composition](ui.md).
|
|
142
|
+
For embedded theme and chrome, see [parent presentation](embed.md#parent-presentation).
|
|
143
|
+
|
|
144
|
+
`<TooltipProvider>` shares hover timing outside `<DataApp>`; the app already
|
|
145
|
+
provides it.
|