@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.
Files changed (202) hide show
  1. package/AGENTS.md +38 -1
  2. package/CONTRIBUTING.md +54 -14
  3. package/README.md +1 -1
  4. package/dist/chunks/index-23dktynd.js +80 -0
  5. package/dist/chunks/index-23dktynd.js.map +10 -0
  6. package/dist/chunks/index-8qfxv0h5.js +14160 -0
  7. package/dist/chunks/index-8qfxv0h5.js.map +108 -0
  8. package/dist/chunks/index-ag0cgnvq.js +639 -0
  9. package/dist/chunks/index-ag0cgnvq.js.map +13 -0
  10. package/dist/chunks/index-dq7b35fn.js +307 -0
  11. package/dist/chunks/index-dq7b35fn.js.map +13 -0
  12. package/dist/chunks/{contract-a061yj6r.js → index-ev2b5aaf.js} +41 -72
  13. package/dist/chunks/index-ev2b5aaf.js.map +12 -0
  14. package/dist/chunks/{contract-awa2d5b1.js → index-f5cnbrjz.js} +19 -2
  15. package/dist/chunks/{contract-awa2d5b1.js.map → index-f5cnbrjz.js.map} +4 -3
  16. package/dist/chunks/{contract-sm7j7k95.js → index-ktmvs916.js} +9 -150
  17. package/dist/chunks/index-ktmvs916.js.map +13 -0
  18. package/dist/chunks/{contract-txjy0en2.js → index-m6n8ctfc.js} +64 -72
  19. package/dist/chunks/index-m6n8ctfc.js.map +10 -0
  20. package/dist/chunks/index-mt93ff2b.js +28 -0
  21. package/dist/chunks/index-mt93ff2b.js.map +10 -0
  22. package/dist/chunks/{contract-tf8c3qpv.js → index-qd5xgx6y.js} +46 -5
  23. package/dist/chunks/index-qd5xgx6y.js.map +13 -0
  24. package/dist/chunks/index-x69p7cv7.js +149 -0
  25. package/dist/chunks/index-x69p7cv7.js.map +10 -0
  26. package/dist/client/index.js +12 -7
  27. package/dist/client/index.js.map +1 -1
  28. package/dist/core/appearance.js +1 -1
  29. package/dist/core/config.js +7 -3
  30. package/dist/core/config.js.map +1 -1
  31. package/dist/core/contract.js +24 -6
  32. package/dist/core/contract.js.map +1 -1
  33. package/dist/core/format.js +1 -1
  34. package/dist/embed/index.js +16 -12
  35. package/dist/embed/index.js.map +4 -4
  36. package/dist/index.js +12 -0
  37. package/dist/index.js.map +9 -0
  38. package/dist/local.js +20 -30
  39. package/dist/local.js.map +7 -8
  40. package/dist/react/embed/index.js +1 -1
  41. package/dist/react/embed/index.js.map +2 -2
  42. package/dist/react/index.js +2567 -11542
  43. package/dist/react/index.js.map +16 -91
  44. package/dist/react/ui/index.js +2272 -0
  45. package/dist/react/ui/index.js.map +28 -0
  46. package/dist/server.js +13 -26
  47. package/dist/server.js.map +6 -7
  48. package/dist/types/client/annotations.d.ts +27 -0
  49. package/dist/types/client/data-client.d.ts +1 -2
  50. package/dist/types/client/iframe.d.ts +4 -2
  51. package/dist/types/client/index.d.ts +1 -0
  52. package/dist/types/core/annotations.d.ts +94 -0
  53. package/dist/types/core/appearance.d.ts +2 -2
  54. package/dist/types/core/config.d.ts +48 -4
  55. package/dist/types/core/contract.d.ts +21 -11
  56. package/dist/types/core/dimension.d.ts +6 -4
  57. package/dist/types/core/filters.d.ts +44 -0
  58. package/dist/types/core/messages.d.ts +2 -0
  59. package/dist/types/core/operation-types.d.ts +5 -2
  60. package/dist/types/core/operation.d.ts +3 -4
  61. package/dist/types/core/presentation.d.ts +2 -0
  62. package/dist/types/core/queries.d.ts +16 -0
  63. package/dist/types/core/uuid.d.ts +2 -0
  64. package/dist/types/core/variables.d.ts +34 -18
  65. package/dist/types/embed/runtime-html.d.ts +2 -0
  66. package/dist/types/embed/source.d.ts +2 -1
  67. package/dist/types/index.d.ts +3 -0
  68. package/dist/types/react/annotations/AnnotationBar.d.ts +30 -0
  69. package/dist/types/react/annotations/AnnotationControls.d.ts +10 -0
  70. package/dist/types/react/annotations/AnnotationEditor.d.ts +16 -0
  71. package/dist/types/react/annotations/AnnotationMarkers.d.ts +19 -0
  72. package/dist/types/react/annotations/AnnotationSelectionLayer.d.ts +12 -0
  73. package/dist/types/react/annotations/AnnotationTarget.d.ts +9 -0
  74. package/dist/types/react/annotations/AnnotationTooltip.d.ts +9 -0
  75. package/dist/types/react/annotations/AnnotationTrigger.d.ts +10 -0
  76. package/dist/types/react/annotations/annotation-editor-state.d.ts +56 -0
  77. package/dist/types/react/annotations/annotation-screenshot.d.ts +4 -0
  78. package/dist/types/react/annotations/annotation-targets.d.ts +33 -0
  79. package/dist/types/react/annotations/getAnnotationProps.d.ts +15 -0
  80. package/dist/types/react/annotations/styles.d.ts +3 -0
  81. package/dist/types/react/annotations/useAnnotationGeometry.d.ts +22 -0
  82. package/dist/types/react/annotations/useDataAppAnnotations.d.ts +22 -0
  83. package/dist/types/react/app-context.d.ts +3 -0
  84. package/dist/types/react/bindings.d.ts +110 -0
  85. package/dist/types/react/content.d.ts +24 -11
  86. package/dist/types/react/hooks.d.ts +29 -787
  87. package/dist/types/react/index.d.ts +31 -106
  88. package/dist/types/react/mount.d.ts +6 -4
  89. package/dist/types/react/source-owner.d.ts +2 -0
  90. package/dist/types/react/style-contract.d.ts +80 -0
  91. package/dist/types/react/style-validation.d.ts +2 -0
  92. package/dist/types/react/ui/AboutData.d.ts +9 -1
  93. package/dist/types/react/ui/AppHeader.d.ts +1 -2
  94. package/dist/types/react/ui/AppLayout.d.ts +3 -9
  95. package/dist/types/react/ui/AppToolbar.d.ts +5 -8
  96. package/dist/types/react/ui/AreaChart.d.ts +7 -0
  97. package/dist/types/react/ui/BarChart.d.ts +6 -0
  98. package/dist/types/react/ui/Button.d.ts +3 -3
  99. package/dist/types/react/ui/ChartLegend.d.ts +31 -0
  100. package/dist/types/react/ui/Checkbox.d.ts +15 -4
  101. package/dist/types/react/ui/CheckboxGroup.d.ts +7 -0
  102. package/dist/types/react/ui/{Combobox.d.ts → ChoicePicker.d.ts} +10 -15
  103. package/dist/types/react/ui/{ComparisonVisual.d.ts → Comparison.d.ts} +3 -3
  104. package/dist/types/react/ui/ComposedChart.d.ts +41 -0
  105. package/dist/types/react/ui/ComposedChartLegend.d.ts +8 -0
  106. package/dist/types/react/ui/ContentSkeleton.d.ts +2 -0
  107. package/dist/types/react/ui/DataApp.d.ts +17 -58
  108. package/dist/types/react/ui/DataAppFrame.d.ts +41 -0
  109. package/dist/types/react/ui/DataBoundary.d.ts +4 -5
  110. package/dist/types/react/ui/DataSection.d.ts +6 -18
  111. package/dist/types/react/ui/DataSectionBoundary.d.ts +30 -0
  112. package/dist/types/react/ui/DataValue.d.ts +9 -0
  113. package/dist/types/react/ui/DataWidget.d.ts +2 -1
  114. package/dist/types/react/ui/DateTimeTooltip.d.ts +1 -1
  115. package/dist/types/react/ui/DimensionPicker.d.ts +1 -1
  116. package/dist/types/react/ui/ExportControl.d.ts +1 -1
  117. package/dist/types/react/ui/FilterActions.d.ts +10 -0
  118. package/dist/types/react/ui/FilterBar.d.ts +3 -0
  119. package/dist/types/react/ui/GettingStarted.d.ts +2 -4
  120. package/dist/types/react/ui/HelpPopover.d.ts +3 -4
  121. package/dist/types/react/ui/InspectionContext.d.ts +2 -0
  122. package/dist/types/react/ui/InspectionProvider.d.ts +29 -0
  123. package/dist/types/react/ui/LineChart.d.ts +7 -0
  124. package/dist/types/react/ui/Menu.d.ts +12 -0
  125. package/dist/types/react/ui/MetricWidget.d.ts +4 -3
  126. package/dist/types/react/ui/NumberField.d.ts +27 -0
  127. package/dist/types/react/ui/NumberFilterPicker.d.ts +9 -0
  128. package/dist/types/react/ui/PieChart.d.ts +11 -0
  129. package/dist/types/react/ui/PresentStory.d.ts +2 -5
  130. package/dist/types/react/ui/RadioGroup.d.ts +18 -0
  131. package/dist/types/react/ui/RefreshControl.d.ts +1 -2
  132. package/dist/types/react/ui/ScatterChart.d.ts +17 -0
  133. package/dist/types/react/ui/SearchField.d.ts +1 -2
  134. package/dist/types/react/ui/SearchInput.d.ts +3 -1
  135. package/dist/types/react/ui/Select.d.ts +14 -0
  136. package/dist/types/react/ui/SelectionMark.d.ts +2 -1
  137. package/dist/types/react/ui/Sheet.d.ts +2 -1
  138. package/dist/types/react/ui/Skeleton.d.ts +5 -2
  139. package/dist/types/react/ui/StaticDataApp.d.ts +4 -0
  140. package/dist/types/react/ui/TableWidget.d.ts +19 -16
  141. package/dist/types/react/ui/Tabs.d.ts +6 -2
  142. package/dist/types/react/ui/TextWidget.d.ts +2 -1
  143. package/dist/types/react/ui/Tooltip.d.ts +4 -3
  144. package/dist/types/react/ui/TooltipSurface.d.ts +3 -0
  145. package/dist/types/react/ui/TrendChart.d.ts +5 -0
  146. package/dist/types/react/ui/UpdatedAt.d.ts +1 -1
  147. package/dist/types/react/ui/VisualizationWidget.d.ts +12 -13
  148. package/dist/types/react/ui/WidgetViewTabs.d.ts +1 -1
  149. package/dist/types/react/ui/chart-data.d.ts +25 -0
  150. package/dist/types/react/ui/chart-primitives.d.ts +20 -0
  151. package/dist/types/react/ui/data-context.d.ts +18 -7
  152. package/dist/types/react/ui/icons.d.ts +2 -0
  153. package/dist/types/react/ui/index.d.ts +98 -0
  154. package/dist/types/react/ui/keyboard.d.ts +6 -0
  155. package/dist/types/react/ui/metric.d.ts +1 -1
  156. package/dist/types/react/ui/presentation.d.ts +5 -1
  157. package/dist/types/react/ui/shortcuts.d.ts +14 -1
  158. package/dist/types/react/ui/useAppAppearance.d.ts +1 -1
  159. package/dist/types/react/ui/variables.d.ts +4 -2
  160. package/dist/types/react/view-controls.d.ts +3 -1
  161. package/dist/types/react/view-runtime.d.ts +29 -0
  162. package/dist/types/react/view.d.ts +21 -4
  163. package/dist/types/react/widgets.d.ts +79 -0
  164. package/dist/types/server/handler.d.ts +4 -4
  165. package/dist/worker.js +20 -737
  166. package/docs/app-authoring.md +72 -50
  167. package/docs/client.md +8 -8
  168. package/docs/contract.md +42 -20
  169. package/docs/data-context.md +91 -0
  170. package/docs/embed.md +10 -2
  171. package/docs/formatting-and-appearance.md +68 -0
  172. package/docs/hosted-apps.md +9 -15
  173. package/docs/layout.md +5 -3
  174. package/docs/react-embed.md +5 -4
  175. package/docs/react.md +22 -317
  176. package/docs/server-bun.md +1 -1
  177. package/docs/server.md +4 -4
  178. package/docs/stories-and-export.md +52 -0
  179. package/docs/styling.md +62 -0
  180. package/docs/ui-quality.md +20 -0
  181. package/docs/ui.md +196 -0
  182. package/docs/variables.md +171 -0
  183. package/docs/views.md +123 -0
  184. package/docs/widgets.md +145 -0
  185. package/examples/starter-data-app/index.tsx +115 -86
  186. package/package.json +25 -12
  187. package/dist/chunks/contract-a061yj6r.js.map +0 -12
  188. package/dist/chunks/contract-g6hky7x6.js +0 -282
  189. package/dist/chunks/contract-g6hky7x6.js.map +0 -12
  190. package/dist/chunks/contract-sm7j7k95.js.map +0 -14
  191. package/dist/chunks/contract-tf8c3qpv.js.map +0 -11
  192. package/dist/chunks/contract-txjy0en2.js.map +0 -10
  193. package/dist/chunks/contract-wz59z8pq.js +0 -8
  194. package/dist/chunks/contract-wz59z8pq.js.map +0 -10
  195. package/dist/chunks/contract-yxbjea23.js +0 -328
  196. package/dist/chunks/contract-yxbjea23.js.map +0 -12
  197. package/dist/types/react/ui/RefreshRegion.d.ts +0 -9
  198. package/dist/types/react/ui/SelectableBarChart.d.ts +0 -15
  199. package/docs/releasing.md +0 -79
  200. /package/dist/chunks/{contract-mev09s5v.js → index-mev09s5v.js} +0 -0
  201. /package/dist/chunks/{contract-mev09s5v.js.map → index-mev09s5v.js.map} +0 -0
  202. /package/dist/types/react/ui/{comparison.d.ts → metric-comparison.d.ts} +0 -0
package/docs/react.md CHANGED
@@ -5,331 +5,36 @@ React 19.2 or newer and React DOM 19.2 or newer are peer dependencies.
5
5
 
6
6
  ## Mount the app
7
7
 
8
- `mountDataApp({ config, component })` mounts into `#root`, sets the document
9
- title and language, attaches navigation to an available iframe transport, and
10
- installs `<DataAppProvider>`. Uncaught React rendering errors are logged and
11
- reported to the iframe host as fatal app failures. When mounting through another
12
- framework, wrap the app in `<DataAppProvider>` yourself.
8
+ Use `mountDataApp({ app, component })` to mount into `#root`. When mounting
9
+ through another framework, wrap the root in `<DataAppProvider app={app}>` from `/react/ui`.
10
+ `<DataApp>` inherits identity and appearance from that root. Mounting sets the document title before React renders.
13
11
 
14
12
  ```tsx
15
13
  import { injectDataAppStyles, mountDataApp } from '@altertable/data-app/react';
16
14
 
17
15
  injectDataAppStyles();
18
- mountDataApp({ config, component: App });
16
+ mountDataApp({ app, component: App });
19
17
  ```
20
18
 
21
- Importing the package does not inject styles. For server rendering, call the
22
- injector on the client before mounting or hydrating. With a restrictive CSP,
23
- pass a permitted nonce; the first call sets it for the document. No CSS loader
24
- or separate stylesheet asset is needed.
19
+ Call `injectDataAppStyles()` before mounting; no separate stylesheet is needed.
20
+ For a restrictive CSP, pass the permitted nonce on the first call.
21
+ See [Styling](styling.md) for appearance, CSS tokens, and overrides.
25
22
 
26
- ## Bind a view
23
+ Use `/react` for declared views, bound widgets, and layouts. Use
24
+ [`/react/ui`](ui.md) for setup/static screens and direct UI composition.
27
25
 
28
- ```tsx
29
- import { createDataClient } from '@altertable/data-app/client';
30
- import {
31
- createDataHooks,
32
- dateRangeVariable,
33
- DataApp,
34
- Grid,
35
- MetricWidget,
36
- VisualizationWidget,
37
- Ranking,
38
- } from '@altertable/data-app/react';
39
- import type { operations } from '#app/operations.ts';
40
- import { calendar } from '#app/contracts.ts';
41
- import { dataContext, trackedIdentities } from '#app/data-context.tsx';
42
- import config from '#config';
43
-
44
- const period = dateRangeVariable({
45
- key: 'period',
46
- contract: calendar,
47
- comparison: true,
48
- defaultValue: { kind: 'preset', id: 'last-30' },
49
- });
50
- const { defineDataView, useView } =
51
- createDataHooks(createDataClient<typeof operations>());
52
- const activityView = defineDataView({
53
- operation: 'activity',
54
- variables: { period },
55
- input: ({ period }) => period,
56
- date: { variable: 'period', input: input => input },
57
- isEmpty: data => data.features.length === 0,
58
- empty: { title: 'No activity in this range' },
59
- });
60
- const featureEvidence = dataContext.evidence({
61
- id: 'feature-use',
62
- queryNames: [dataContext.queryNames.activity],
63
- });
64
- const content = activityView.content(result => (
65
- <Grid columns={2}>
66
- <MetricWidget
67
- metric={trackedIdentities}
68
- reading={result.metric(data => ({
69
- current: data.count,
70
- previous: data.previousCount,
71
- }))}
72
- />
73
- <VisualizationWidget
74
- title="Feature use"
75
- evidence={featureEvidence}
76
- reading={result.select(data => data.features)}
77
- isEmpty={features => features.length === 0}
78
- empty={{ title: 'No features' }}
79
- skeleton={{ variant: 'ranking', rows: 6 }}
80
- >
81
- {features => <Ranking items={features} />}
82
- </VisualizationWidget>
83
- </Grid>
84
- ));
85
- function App() {
86
- const activity = useView(activityView);
87
- return (
88
- <DataApp
89
- config={config}
90
- dataContext={dataContext}
91
- request={activity}
92
- csvExport={({ data }) => ({
93
- filename: 'activity.csv',
94
- tables: [
95
- {
96
- name: 'Tracked identities',
97
- columns: ['Current count', 'Previous count'],
98
- rows: [[data.count, data.previousCount]],
99
- },
100
- {
101
- name: 'Feature use',
102
- columns: ['Feature ID', 'Count'],
103
- rows: data.features.map(feature => [feature.id, feature.value]),
104
- },
105
- ],
106
- })}
107
- story={({ data }) => [
108
- dataContext.finding({
109
- id: 'feature-use',
110
- headline: 'Feature use in the selected period',
111
- visual: <Ranking items={data.features} />,
112
- evidence: featureEvidence,
113
- }),
114
- ]}
115
- {...content}
116
- />
117
- );
118
- }
119
- ```
120
-
121
- The `date` binding identifies the controlling variable and extracts its range from the operation input. Nested inputs use, for example, `input: (input) => input.period`. The runtime rejects mappings that silently change the selected range or comparison. Non-date views supply `describeInput`; date views can override it when other inputs also need describing.
122
-
123
- Share the [date range contract](contract.md#shared-date-ranges) between the
124
- operation parser and the view.
125
-
126
- `useView()` generates controls for date, text and fixed-option select variables; custom controls use `result.variables.bind(name)`. `input` chooses which variables reach the operation, so local search can stay local. Hooks belong in the enclosing component.
127
-
128
- For large tables, use query-backed pagination with a stable sort and total
129
- count. Client pagination and search cover only the rows already returned.
130
-
131
- Bound `<VisualizationWidget>` and `<TableWidget>` components require `evidence` from `context.evidence(...)`. A bound `<MetricWidget>` gets evidence from its metric definition. Evidence must name at least one glossary entry or query. Static widgets may omit it.
132
-
133
- `<MetricWidget>` and `<ComparisonVisual>` both accept the same `metric` and `reading`. The comparison is enabled by the displayed result's range. The definition supplies formatting and evidence; a reading cannot override those or provide a second value. `favorableDirection` is optional; changes are neutral until the author defines whether up or down is favorable.
134
-
135
- Use the [app helpers](#reuse-app-helpers) for metric formats and values in tables,
136
- charts, and custom views.
137
-
138
- `defineDataContent()` remains available for manually managed requests. Its optional `{ date: (input) => rangeRequest }` binds comparison readings. `<DataSection>` handles independent requests. Low-level widgets, tabs and layout components remain available for custom interfaces.
139
-
140
- ## Layout
141
-
142
- Use `<Stack>` for sections, `<Grid>` for peer widgets, and `<GridItem>` for spans.
143
- They share the app layout gap and own responsive behavior. Follow the
144
- [layout contract](layout.md) for spacing ownership and composition guidance.
26
+ ## Choose a task
145
27
 
146
- ## Narrative text
147
-
148
- Use `<TextContent>` for prose within a page or custom layout, and `<TextWidget>`
149
- when the explanation belongs in a titled panel alongside other widgets.
150
-
151
- Give text a purpose: frame the question, explain how to interpret a comparison,
152
- qualify a finding, or suggest what to explore next. Choose the content for the
153
- reader's question and the decisions the exploration supports.
154
-
155
- For data-dependent text, use `reading={result.select((data, input) => ...)}` and
156
- provide `evidence`. Derive both the explanation and its scope from those displayed
157
- values so it stays consistent with the visualizations while filters change.
158
- Static instructions can use ordinary children; local filters should feed the same
159
- filtered data to the text and its related visualization.
160
-
161
- ## Reuse app helpers
162
-
163
- Use the shared helpers for common app tasks. Follow the entry points below to
164
- find their exports, types, and usage constraints.
165
-
166
- | Task | Entry point |
167
- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
168
- | Format numbers, counts, percentages, currency, date ranges, and plural labels (`pluralize`) | [Format helpers](https://github.com/altertable-ai/data-app/blob/main/src/core/format.ts) |
169
- | Choose chart colors | [Chart colors](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/chartColor.ts) |
170
- | Search items and highlight matches | [Search helpers](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/searchItems.ts) |
171
- | Read and synchronize URL query state | [URL state helpers](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/search.ts) |
172
- | Render timestamps, freshness, table shares, periods, and metric comparisons | [React exports](https://github.com/altertable-ai/data-app/blob/main/src/react/index.ts) |
173
- | Configure appearance and theme preferences | [Appearance helpers](https://github.com/altertable-ai/data-app/blob/main/src/core/appearance.ts) |
174
- | Build the app's scoped document title | [Configuration helpers](https://github.com/altertable-ai/data-app/blob/main/src/core/config.ts) |
175
-
176
- ## Register source identifiers
177
-
178
- Use `defineDataIdentifiers()` from `/react` to register exact catalog, schema,
179
- table, and field names. Use `<DataIdentifier>` in descriptions and glossary
180
- entries to render those source references consistently.
181
-
182
- ```tsx
183
- import { defineDataIdentifiers } from '@altertable/data-app/react';
184
-
185
- const identifiers = defineDataIdentifiers({
186
- tables: {
187
- events: {
188
- catalog: 'product_analytics',
189
- schema: 'analytics',
190
- name: 'events',
191
- },
192
- },
193
- columns: { identity: { table: 'events', name: 'identity_uuid' } },
194
- });
195
- const { DataIdentifier } = identifiers;
196
-
197
- <DataIdentifier id="tables.events" />;
198
- <DataIdentifier id="columns.events.identity" />;
199
- ```
28
+ | Task | Guide |
29
+ | -------------------------------------------------------- | --------------------------------------------------------- |
30
+ | Declare a view and handle loading, refresh, and failures | [Views](views.md) |
31
+ | Configure date, text, select, and dimension controls | [Variables](variables.md) |
32
+ | Render metrics, charts, tables, and narrative | [Widgets](widgets.md) |
33
+ | Compose sections and responsive grids | [Layout](layout.md) |
34
+ | Register sources, definitions, metrics, and evidence | [Data context](data-context.md) |
35
+ | Present findings and export displayed data | [Stories and export](stories-and-export.md) |
36
+ | Format values and configure appearance | [Formatting and appearance](formatting-and-appearance.md) |
37
+ | Host an iframe from React | [React embed](react-embed.md) |
200
38
 
201
- Pass `identifiers.definitions` to `createDataContext()` so the context carries the
202
- same source registry. Source identifiers name physical data; glossary entries
203
- explain its business meaning, and query evidence records how it was queried.
204
-
205
- ## Bind evidence
206
-
207
- ```tsx
208
- const queries = defineQueryNames({ activity: 'feature-activity' });
209
- // In the server operation: queryNames: queries
210
- const context = createDataContext(queries)({
211
- identifiers: identifiers.definitions,
212
- description: (
213
- <>
214
- Explore activity in <DataIdentifier id="tables.events" />.
215
- </>
216
- ),
217
- glossary: {
218
- identities: {
219
- term: 'Tracked identities',
220
- definition: (
221
- <>
222
- Distinct <DataIdentifier id="columns.events.identity" /> values.
223
- </>
224
- ),
225
- queryNames: [queries.activity],
226
- },
227
- },
228
- });
229
- const evidence = context.evidence({
230
- id: 'identities',
231
- glossaryIds: ['identities'],
232
- queryNames: [queries.activity],
233
- });
234
- const trackedIdentities = context.metric({
235
- id: 'actions',
236
- glossaryId: 'identities',
237
- label: 'Tracked identities',
238
- format: { kind: 'count' },
239
- });
240
- const finding = context.finding({
241
- id: 'activity',
242
- headline: 'What people do',
243
- visual: <ActivityChart />,
244
- evidence: { id: 'activity-evidence', queryNames: [queries.activity] },
245
- });
246
- ```
247
-
248
- Import `defineQueryNames()` from `/contract` and the context/identifier factories from `/react`. Use the same registry in `defineOperation({ queryNames: queries, ... })`. Unknown glossary/query references fail type checks and registry validation; the server also validates returned query names.
249
-
250
- ## Time views and field filters
251
-
252
- `createDataHooks(client).defineTimeView()` owns the `period` variable, calendar
253
- controls, and displayed-period label. Declare `time: { contract, defaultValue }`,
254
- an operation, `isEmpty`, and `empty`. With no additional variables, its default
255
- input is the calendar request. With additional variables, it is `{ period, ...variables }`.
256
- Supply an `input` mapper for a different operation shape and `bindings` to extract
257
- nested period or field-filter inputs. Mappings must preserve the selected values.
258
-
259
- `dimensionFilter()` from `/contract` requires exactly one option source: fixed `options` or a `facet`.
260
- Use `defineFacetFilter()` to bind a facet operation and its typed input. The generated
261
- `<DimensionPicker>` preserves cached options during refresh and failure, offers
262
- missing values separately, and retains selected values absent from a result with
263
- zero counts. `<SelectableBarChart>` can share controlled selection with the picker.
264
-
265
- ## Preserve displayed results
266
-
267
- The callback in `view.content()` receives the displayed result and its original
268
- input during refreshes and failures. Use that input when labeling the data.
269
-
270
- Previous data and evidence are retained only when the input changes within the
271
- same operation. Switching to another operation shows its own cached response or
272
- an initial loading/error state; it never inherits another operation's result.
273
-
274
- Operation and facet caches are scoped to the `DataClient` instance. Hooks created
275
- from the same client share queries; separate clients do not share results, stale
276
- data, or cancellation even under one provider. Keep client instances stable
277
- across renders to preserve their cache.
278
-
279
- ## Findings and inspection
280
-
281
- `<DataWidget>` composes a body, toolbar feedback, and footer. Widgets and their
282
- inspection sheets render the same visual and controls. Keep interactive state
283
- above both mounts when authoring custom children.
284
-
285
- When a trusted parent supplies [parent presentation](embed.md#parent-presentation),
286
- `<DataApp>` follows its resolved theme and suppresses local theme controls,
287
- including in presentations. On an embedded surface (`surface: 'embedded'`),
288
- only toolbar actions remain above the app body; the title, scope, description, and
289
- footer are omitted. Variables and request states remain available.
290
-
291
- ## Present data with stories
292
-
293
- Include a story in every analytical app. Stories turn the exploration's findings
294
- into a presentation. Set the `story` prop on `<DataApp>`
295
- to enable **Present story** in the toolbar; use `<PresentStory>` directly for a
296
- custom shell. Start from the [data app starter](../examples/starter-data-app/index.tsx).
297
-
298
- Return one to four findings with a headline, a visual, and registered source
299
- evidence. Use `context.finding()` to bind the evidence.
300
-
301
- The callback receives the displayed data and its original input, including
302
- during refresh or failure. Derive the story from that snapshot so it agrees with
303
- the visible exploration. Initial loading, empty results, and initial errors have
304
- no story to present.
305
-
306
- ## Export displayed data as CSV
307
-
308
- Every request-backed `<DataApp>` requires `story` and `csvExport`. Select all distinct named datasets
309
- from the displayed snapshot so the built-in **Export** action is available
310
- whenever analytical results are shown. Include this alongside the app's story;
311
- setup and static shells may omit it. One dataset downloads directly as CSV. Multiple
312
- datasets offer individual CSV downloads and **Export all** as a ZIP archive.
313
-
314
- Example:
315
-
316
- ```tsx
317
- <DataApp
318
- config={config}
319
- dataContext={dataContext}
320
- request={result}
321
- story={story}
322
- csvExport={({ data, input }) => ({
323
- filename: `counts-${input.groupName || 'all'}.csv`,
324
- tables: [
325
- {
326
- name: 'Sample counts',
327
- columns: ['Group', 'Sample count'],
328
- rows: data.map(row => [row.groupName, row.sampleCount]),
329
- },
330
- ],
331
- })}
332
- >
333
- {(data, input) => <Results data={data} input={input} />}
334
- </DataApp>
335
- ```
39
+ Start with [app authoring](app-authoring.md) and the
40
+ [single-file starter](../examples/starter-data-app/index.tsx).
@@ -24,6 +24,6 @@ server-only `ALTERTABLE_LAKEHOUSE_USERNAME` and
24
24
  `ALTERTABLE_LAKEHOUSE_PASSWORD`, with an optional `ALTERTABLE_API_BASE`.
25
25
  Missing credentials fail the query. Keep these variables out of browser code.
26
26
 
27
- Local serving permits SQL disclosure and does not authenticate hosted viewers.
27
+ Local serving does not authenticate hosted viewers.
28
28
  Use [the portable server handler](server.md) with per-request authorization when
29
29
  hosting an app for other people.
package/docs/server.md CHANGED
@@ -15,7 +15,7 @@ export const handleDataRequest = createDataHandler(
15
15
  const viewer = await authenticate(request);
16
16
  return {
17
17
  lakehouse: await lakehouseFor(viewer, operation),
18
- canDiscloseSql: false,
18
+ queryParams: { orgId: viewer.organizationId },
19
19
  };
20
20
  }
21
21
  );
@@ -23,12 +23,12 @@ export const handleDataRequest = createDataHandler(
23
23
 
24
24
  The app supplies `authenticate()` and `lakehouseFor()`, then routes `/api/data/*`
25
25
  requests to the handler. Authorize every viewer and operation, and scope the
26
- returned lakehouse to the viewer's permitted data. Origin and Fetch Metadata
26
+ returned lakehouse to the viewer's permitted data. Supply trusted registry
27
+ parameters through `queryParams`; query callers cannot override these values. Origin and Fetch Metadata
27
28
  checks reject cross-site browser requests; they do not authenticate viewers.
28
29
 
29
30
  The handler validates operation input and output, enforces query row and duration
30
- bounds, propagates cancellation, and returns request IDs with errors. SQL is
31
- disclosed only when both the operation policy and `canDiscloseSql` allow it.
31
+ bounds, propagates cancellation, and returns query evidence and request IDs.
32
32
  Keep credentials and operation implementations on the server.
33
33
 
34
34
  See [operation contracts](contract.md), the [client](client.md), and the
@@ -0,0 +1,52 @@
1
+ # Stories and CSV export
2
+
3
+ Every `<DataApp>` from `/react` supplies both `story` and `datasets`, derived from
4
+ the displayed snapshot. Story and export selectors do not run before a result is available.
5
+ Setup and static screens use `<DataApp>` from `/react/ui`; they may omit both
6
+ or pass a direct `CsvExport` object.
7
+
8
+ ## Present data with stories
9
+
10
+ Set `story` on `<DataApp>` to enable **Present story** in the toolbar.
11
+ For custom shells, `/react/ui` provides `<PresentStory>`; see [direct UI composition](ui.md). Start from the [data app starter](../examples/starter-data-app/index.tsx).
12
+
13
+ Return one to four findings with a headline, a visual, and registered source
14
+ evidence. Set `evidence` to the registered dataset or metric binding. The app
15
+ validates that every finding belongs to its view.
16
+
17
+ The callback receives the displayed source and its original input, including
18
+ during refresh or failure. Derive the story from that snapshot so it agrees with
19
+ the visible exploration. Initial loading, empty results, and initial errors have
20
+ no story to present; the toolbar keeps its story action visible and disabled.
21
+
22
+ ## Export displayed data as CSV
23
+
24
+ Select all distinct named datasets from the displayed snapshot. One dataset downloads directly as CSV. Multiple
25
+ datasets offer individual CSV downloads and **Export all** as a ZIP archive.
26
+ While no displayed snapshot is available, the export action stays visible and disabled.
27
+
28
+ Declare [datasets](widgets.md#declare-datasets-and-metrics) once and pass
29
+ `datasets={[countries, otherDataset]}` to `<DataApp>`. The toolbar
30
+ exports those datasets with raw values and a safe filename derived from the
31
+ displayed scope. List every distinct dataset the app shows.
32
+
33
+ Reuse bound metrics in story visuals:
34
+
35
+ ```tsx
36
+ story={snapshot => {
37
+ const totalReading = total.read(snapshot);
38
+ return [{
39
+ id: 'total',
40
+ headline: `Tracked identities: ${formatMetric(totalReading.value.current, total.definition.format)}`,
41
+ context: activityView.scope(snapshot),
42
+ visual: <MetricWidget metric={total} source={snapshot} />,
43
+ evidence: total,
44
+ }];
45
+ }}
46
+ ```
47
+
48
+ Use the [format helpers](formatting-and-appearance.md) for story prose.
49
+
50
+ Dataset accessors return raw values; do not preformat numbers as display
51
+ labels. Null and undefined become empty cells, while measured zero stays zero.
52
+ The helpers handle quoting and spreadsheet formula protection.
@@ -0,0 +1,62 @@
1
+ # Styling
2
+
3
+ Use components and typed props first: `<Stack>` for sections, `<Grid>` for peers,
4
+ and `<TextContent>` for prose. Standard widgets use [views and bindings](widgets.md);
5
+ custom controls and direct widget shells use [`/react/ui`](ui.md).
6
+
7
+ Configure theme, palette, accent, typography, density, radius, and elevation through
8
+ `app.appearance`; see [appearance](formatting-and-appearance.md). Call
9
+ `injectDataAppStyles()` before mounting. Use one `<DataApp>` per document.
10
+
11
+ ## Customize
12
+
13
+ Add app-owned classes through `className`. Render package components rather than copying their classes onto markup;
14
+ private descendants are not customization hooks. Public names and stable roots
15
+ are listed in the [typed contract](https://github.com/altertable-ai/data-app/blob/main/src/react/style-contract.ts).
16
+ CSS comments document [inherited defaults](https://github.com/altertable-ai/data-app/blob/main/src/react/tokens.css),
17
+ [interaction overrides](https://github.com/altertable-ai/data-app/blob/main/src/react/interaction.css),
18
+ and [focus overrides](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/Focus.css).
19
+ Widget-specific overrides live beside their uses in
20
+ [metrics](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/MetricWidget.css),
21
+ [bar charts](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/BarChart.css),
22
+ [code](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/QueryList.css),
23
+ and [layout](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/Grid.css).
24
+ The `--atbl-` prefix means Altertable.
25
+
26
+ ```css
27
+ .app-action {
28
+ --atbl-control-height: 40px;
29
+ --atbl-focus-color: var(--atbl-accent);
30
+ }
31
+ ```
32
+
33
+ Set tokens on `:root` for the document or on a component for local overrides.
34
+ Inherited tokens are usable in app CSS. Optional overrides fall back where they
35
+ are consumed, so local colors, fonts, and spacing compose. Portals inherit from
36
+ their actual DOM ancestors. Normal unlayered app CSS overrides package rules.
37
+ `DataAppStyle` checks public inline custom-property names.
38
+
39
+ `--atbl-control-text-size` controls editable text, including compact search fields,
40
+ date segments, and annotation editors. It defaults to the body text size.
41
+ Package controls and custom controls with `data-atbl-control="text"` enforce a
42
+ 16px minimum on narrow or touch viewports to prevent browser focus zoom. Keep
43
+ that minimum when adding app-owned input styles.
44
+
45
+ When overriding `--atbl-accent` directly, also choose `--atbl-on-accent` with
46
+ sufficient contrast. Prefer appearance configuration for brand and chart colors.
47
+
48
+ ## Native controls
49
+
50
+ Use package controls when possible. For a custom native control:
51
+
52
+ ```tsx
53
+ <button type="button" data-atbl-control="action" data-atbl-focus="ring">
54
+ Run
55
+ </button>
56
+ ```
57
+
58
+ These hooks provide cursor and focus styling; keep native semantics, accessible
59
+ names, disabled state, and keyboard behavior. `DataAppStyleHooks` checks hook
60
+ values. Use `default` for tooltip targets that only reveal information on hover
61
+ or tap, and `action` for controls that perform an action. Use `inset` focus inside
62
+ clipped surfaces and `group` when a wrapper owns an input's outline. See [UI quality](ui-quality.md) for rendered verification.
@@ -0,0 +1,20 @@
1
+ # UI quality
2
+
3
+ Lead with a supported finding and visible scope. Explain each chart's purpose,
4
+ measures, units, and source evidence. Use package typography and formatters;
5
+ keep primary values prominent and preserve exact values in records and exports.
6
+
7
+ Compose with `<Stack>`, `<Grid>`, and `<TextContent>`; parents own spacing and
8
+ widgets own padding. Let labels and values wrap, and keep tables and menus
9
+ scrolling inside their frames. Use the [layout](layout.md) and [styling](styling.md)
10
+ guides for customization.
11
+
12
+ Keep static context visible during loading. Skeletonize only dynamic content;
13
+ refresh, errors, and retry retain the displayed result and scope. Loading
14
+ geometry should match ready content.
15
+
16
+ Verify a real app at phone and desktop widths, including 320px, in both themes
17
+ and with enlarged text. Exercise long labels, localized values, many filters,
18
+ and loading, empty, refresh, and error states. Check containment, contrast,
19
+ keyboard focus, date/menu navigation, modal dismissal, and reduced motion.
20
+ Inspect rendered screenshots as well as computed styles.