@altertable/data-app 0.65.0 → 0.66.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 +37 -3
- package/README.md +1 -1
- package/dist/chunks/{contract-sm7j7k95.js → contract-13j5zb3c.js} +2 -144
- package/dist/chunks/contract-13j5zb3c.js.map +13 -0
- package/dist/chunks/contract-19ckn3n7.js +78 -0
- package/dist/chunks/contract-19ckn3n7.js.map +10 -0
- package/dist/chunks/{contract-a061yj6r.js → contract-e11m4y7f.js} +34 -70
- package/dist/chunks/contract-e11m4y7f.js.map +12 -0
- package/dist/chunks/{contract-g6hky7x6.js → contract-h8a6f559.js} +8 -4
- package/dist/chunks/contract-h8a6f559.js.map +12 -0
- package/dist/chunks/contract-havnjmdr.js +13011 -0
- package/dist/chunks/contract-havnjmdr.js.map +99 -0
- package/dist/chunks/{contract-txjy0en2.js → contract-m6n8ctfc.js} +64 -72
- package/dist/chunks/contract-m6n8ctfc.js.map +10 -0
- package/dist/chunks/contract-pk603qj7.js +146 -0
- package/dist/chunks/contract-pk603qj7.js.map +10 -0
- package/dist/chunks/{contract-yxbjea23.js → contract-rfbakka4.js} +179 -2
- package/dist/chunks/contract-rfbakka4.js.map +13 -0
- package/dist/client/index.js +11 -6
- package/dist/client/index.js.map +1 -1
- package/dist/core/appearance.js +1 -1
- package/dist/core/contract.js +13 -3
- package/dist/core/contract.js.map +1 -1
- package/dist/embed/index.js +9 -7
- package/dist/embed/index.js.map +2 -2
- package/dist/local.js +1 -1
- package/dist/local.js.map +2 -2
- package/dist/react/embed/index.js +1 -1
- package/dist/react/index.js +2532 -11545
- package/dist/react/index.js.map +16 -91
- package/dist/react/ui/index.js +1614 -0
- package/dist/react/ui/index.js.map +22 -0
- package/dist/server.js +1 -1
- package/dist/server.js.map +2 -2
- package/dist/types/client/annotations.d.ts +23 -0
- package/dist/types/client/index.d.ts +1 -0
- package/dist/types/core/annotations.d.ts +92 -0
- package/dist/types/core/appearance.d.ts +2 -2
- package/dist/types/core/contract.d.ts +2 -0
- package/dist/types/core/presentation.d.ts +2 -0
- package/dist/types/core/variables.d.ts +2 -5
- package/dist/types/react/annotations/AnnotationBar.d.ts +24 -0
- package/dist/types/react/annotations/AnnotationControls.d.ts +10 -0
- package/dist/types/react/annotations/AnnotationEditor.d.ts +15 -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 +19 -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 +30 -105
- package/dist/types/react/source-owner.d.ts +2 -0
- package/dist/types/react/style-contract.d.ts +60 -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/{ComparisonVisual.d.ts → Comparison.d.ts} +3 -3
- 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 +42 -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/ExportControl.d.ts +1 -1
- 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/MetricWidget.d.ts +4 -3
- package/dist/types/react/ui/PieChart.d.ts +8 -0
- package/dist/types/react/ui/PresentStory.d.ts +2 -5
- 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/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/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/data-context.d.ts +18 -7
- package/dist/types/react/ui/icons.d.ts +1 -0
- package/dist/types/react/ui/index.d.ts +74 -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 +7 -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/docs/app-authoring.md +58 -50
- package/docs/data-context.md +89 -0
- package/docs/formatting-and-appearance.md +52 -0
- package/docs/hosted-apps.md +0 -13
- package/docs/layout.md +4 -2
- package/docs/react-embed.md +2 -2
- package/docs/react.md +20 -316
- package/docs/stories-and-export.md +52 -0
- package/docs/styling.md +55 -0
- package/docs/ui-quality.md +20 -0
- package/docs/ui.md +41 -0
- package/docs/variables.md +98 -0
- package/docs/views.md +127 -0
- package/docs/widgets.md +142 -0
- package/examples/starter-data-app/index.tsx +82 -55
- package/package.json +12 -4
- package/dist/chunks/contract-a061yj6r.js.map +0 -12
- package/dist/chunks/contract-g6hky7x6.js.map +0 -12
- package/dist/chunks/contract-sm7j7k95.js.map +0 -14
- package/dist/chunks/contract-txjy0en2.js.map +0 -10
- 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/types/react/ui/{comparison.d.ts → metric-comparison.d.ts} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { DataContext } from './ui/data-context.js';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import type { DataView, DisplayedSnapshot } from '../core/data-view.js';
|
|
4
|
+
import type { DisclosedQuery } from '../core/contract.js';
|
|
5
|
+
import type { EmptyContent } from './ui/presentation.js';
|
|
6
|
+
export type ViewResult<Data, Input> = {
|
|
7
|
+
dataContext: DataContext;
|
|
8
|
+
view: DataView<Data, Input>;
|
|
9
|
+
snapshot?: DisplayedSnapshot<Data, Input>;
|
|
10
|
+
refetch: () => unknown;
|
|
11
|
+
cancel: () => unknown;
|
|
12
|
+
refreshing: boolean;
|
|
13
|
+
queries?: DisclosedQuery[];
|
|
14
|
+
controls?: ReactNode;
|
|
15
|
+
emptyFallback: EmptyContent;
|
|
16
|
+
};
|
|
17
|
+
/** The executor is private; declarations expose data composition, not subscription hooks. */
|
|
18
|
+
export declare class DeclaredView<Data, Input, Definition = unknown> {
|
|
19
|
+
#private;
|
|
20
|
+
constructor(useResult: () => ViewResult<Data, Input>, definition?: Definition);
|
|
21
|
+
static definition<Data, Input, Definition>(view: DeclaredView<Data, Input, Definition>): Definition;
|
|
22
|
+
static useResult<Data, Input>(view: DeclaredView<Data, Input>): ViewResult<Data, Input>;
|
|
23
|
+
}
|
|
24
|
+
export declare function useDeclaredResult<Data, Input>(view: DeclaredView<Data, Input>): ViewResult<Data, Input>;
|
|
25
|
+
export declare const PrimaryViewContext: import("react").Context<{
|
|
26
|
+
declaration: object;
|
|
27
|
+
result: ViewResult<unknown, unknown>;
|
|
28
|
+
} | null>;
|
|
29
|
+
export declare function getViewDefinition<Data, Input, Definition>(view: DeclaredView<Data, Input, Definition>): Definition;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DataContext } from './ui/data-context.js';
|
|
1
2
|
import type { EmptyContent } from './ui/presentation.js';
|
|
2
3
|
import type { AppVariableValues, DateRangeVariable, VariableCollection } from '../core/variables.js';
|
|
3
4
|
import type { DateRangeRequest } from '../core/contract.js';
|
|
@@ -16,21 +17,37 @@ export type ViewDate<Variables extends VariableCollection, Input> = {
|
|
|
16
17
|
export type ViewBindings<Variables extends VariableCollection, Input> = Partial<{
|
|
17
18
|
[Key in keyof Variables]: (input: Input) => ResolvedVariables<Variables>[Key];
|
|
18
19
|
}>;
|
|
20
|
+
type SameViewInput<Variables extends VariableCollection, Input> = [
|
|
21
|
+
ResolvedVariables<Variables>
|
|
22
|
+
] extends [Input] ? [Input] extends [ResolvedVariables<Variables>] ? [Input] extends [Record<string, never>] ? true : [keyof Input] extends [keyof Variables] ? true : false : false : false;
|
|
23
|
+
type ViewInputMapping<Variables extends VariableCollection, Input> = SameViewInput<Variables, Input> extends true ? {
|
|
24
|
+
input?: (values: ResolvedVariables<Variables>) => Input;
|
|
25
|
+
} : {
|
|
26
|
+
input: (values: ResolvedVariables<Variables>) => Input;
|
|
27
|
+
};
|
|
19
28
|
export type DataViewDefinition<Name extends string, Variables extends VariableCollection, Input, Data> = {
|
|
20
29
|
operation: Name;
|
|
21
|
-
|
|
22
|
-
input: (values: ResolvedVariables<Variables>) => Input;
|
|
30
|
+
dataContext: DataContext;
|
|
23
31
|
bindings?: ViewBindings<Variables, Input>;
|
|
24
32
|
/** App-owned semantics: measured zero need not mean an empty result. */
|
|
25
33
|
isEmpty: (data: Data) => boolean;
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
/** Inherited by DataSection unless it supplies its own copy. */
|
|
35
|
+
emptyFallback: EmptyContent;
|
|
36
|
+
} & (keyof Variables extends never ? {
|
|
37
|
+
variables?: Variables;
|
|
38
|
+
} : {
|
|
39
|
+
variables: Variables;
|
|
40
|
+
}) & ViewInputMapping<NoInfer<Variables>, Input> & ({
|
|
28
41
|
date: ViewDate<Variables, Input>;
|
|
29
42
|
describeInput?: (input: Input) => string;
|
|
30
43
|
} | {
|
|
31
44
|
date?: never;
|
|
32
45
|
describeInput: (input: Input) => string;
|
|
33
46
|
});
|
|
47
|
+
export type ResolvedDataViewDefinition<Name extends string, Variables extends VariableCollection, Input, Data> = Omit<DataViewDefinition<Name, Variables, Input, Data>, 'variables' | 'input'> & {
|
|
48
|
+
variables: Variables;
|
|
49
|
+
input: (values: ResolvedVariables<Variables>) => Input;
|
|
50
|
+
};
|
|
34
51
|
export declare function describeViewInput<Input>(definition: {
|
|
35
52
|
variables: VariableCollection;
|
|
36
53
|
bindings?: Partial<Record<string, (input: Input) => unknown>>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type Dataset, type Metric, type ViewSource } from './bindings.js';
|
|
2
|
+
import { type MetricWidgetProps as DisplayMetricProps } from './ui/MetricWidget.js';
|
|
3
|
+
import { type TableWidgetProps as DisplayTableProps, type TableDisplayMode } from './ui/TableWidget.js';
|
|
4
|
+
import { type VisualizationWidgetProps as DisplayVisualProps, type VisualizationWidgetView as DisplayVisualizationView } from './ui/VisualizationWidget.js';
|
|
5
|
+
import { type TextWidgetProps as DisplayTextProps } from './ui/TextWidget.js';
|
|
6
|
+
import { type DataValueProps as DisplayValueProps } from './ui/DataValue.js';
|
|
7
|
+
import type { MetricValues } from '../core/reading.js';
|
|
8
|
+
import type { MetricDefinition } from './ui/metric.js';
|
|
9
|
+
import type { ReactNode } from 'react';
|
|
10
|
+
import type { DataReading } from '../core/reading.js';
|
|
11
|
+
export type MetricWidgetProps<Data = unknown, Input = unknown> = Omit<Extract<DisplayMetricProps, {
|
|
12
|
+
metric: MetricDefinition;
|
|
13
|
+
}>, 'metric' | 'reading'> & {
|
|
14
|
+
metric: Metric<Data, Input>;
|
|
15
|
+
source: ViewSource<Data, Input>;
|
|
16
|
+
};
|
|
17
|
+
export declare function MetricWidget<Data, Input>({ metric, source, ...presentation }: MetricWidgetProps<Data, Input>): import("react").JSX.Element;
|
|
18
|
+
type BoundTableProps<Row> = Extract<DisplayTableProps<Row>, {
|
|
19
|
+
reading: DataReading<readonly Row[]>;
|
|
20
|
+
}>;
|
|
21
|
+
type TablePresentation<Row> = Pick<BoundTableProps<Row>, 'title' | 'columns' | 'rowKey' | 'reading' | 'evidence' | 'emptyFallback'>;
|
|
22
|
+
export type TableWidgetProps<Row, Data = unknown, Input = unknown> = Omit<BoundTableProps<Row>, keyof TablePresentation<Row> | 'limit' | 'pagination'> & TableDisplayMode & {
|
|
23
|
+
dataset: Dataset<Data, Input, Row>;
|
|
24
|
+
source: ViewSource<Data, Input>;
|
|
25
|
+
title?: ReactNode;
|
|
26
|
+
};
|
|
27
|
+
export declare function TableWidget<Row, Data, Input>({ dataset, source, title, ...presentation }: TableWidgetProps<Row, Data, Input>): import("react").JSX.Element;
|
|
28
|
+
type BoundVisual<Row> = Extract<DisplayVisualProps<readonly Row[]>, {
|
|
29
|
+
reading: DataReading<readonly Row[]>;
|
|
30
|
+
}>;
|
|
31
|
+
export type VisualizationWidgetView<Row> = DisplayVisualizationView<readonly Row[]>;
|
|
32
|
+
type VisualContent<Row> = {
|
|
33
|
+
children: (rows: readonly Row[]) => ReactNode;
|
|
34
|
+
views?: never;
|
|
35
|
+
viewLabel?: never;
|
|
36
|
+
initialView?: never;
|
|
37
|
+
} | {
|
|
38
|
+
children?: never;
|
|
39
|
+
views: readonly VisualizationWidgetView<Row>[];
|
|
40
|
+
viewLabel: string;
|
|
41
|
+
initialView?: string;
|
|
42
|
+
};
|
|
43
|
+
export type VisualizationWidgetProps<Row, Data = unknown, Input = unknown> = Omit<BoundVisual<Row>, 'reading' | 'isEmpty' | 'emptyFallback' | 'evidence' | 'title' | 'children' | 'views' | 'viewLabel' | 'initialView'> & VisualContent<Row> & {
|
|
44
|
+
dataset: Dataset<Data, Input, Row>;
|
|
45
|
+
source: ViewSource<Data, Input>;
|
|
46
|
+
title?: ReactNode;
|
|
47
|
+
};
|
|
48
|
+
export declare function VisualizationWidget<Row, Data, Input>({ dataset, source, title, ...presentation }: VisualizationWidgetProps<Row, Data, Input>): import("react").JSX.Element;
|
|
49
|
+
type NarrativeBinding<Data, Input, Row> = {
|
|
50
|
+
metric: Metric<Data, Input>;
|
|
51
|
+
dataset?: never;
|
|
52
|
+
source: ViewSource<Data, Input>;
|
|
53
|
+
children?: (values: MetricValues) => ReactNode;
|
|
54
|
+
} | {
|
|
55
|
+
metric?: never;
|
|
56
|
+
dataset: Dataset<Data, Input, Row>;
|
|
57
|
+
source: ViewSource<Data, Input>;
|
|
58
|
+
children: (rows: readonly Row[]) => ReactNode;
|
|
59
|
+
};
|
|
60
|
+
export type TextWidgetProps<Data = unknown, Input = unknown, Row = never> = Omit<Extract<DisplayTextProps<unknown>, {
|
|
61
|
+
reading: DataReading<unknown>;
|
|
62
|
+
}>, 'reading' | 'evidence' | 'children' | 'title' | 'emptyFallback'> & NarrativeBinding<Data, Input, Row> & {
|
|
63
|
+
title?: ReactNode;
|
|
64
|
+
};
|
|
65
|
+
export declare function TextWidget<Data, Input, Row>(props: TextWidgetProps<Data, Input, Row>): import("react").JSX.Element;
|
|
66
|
+
type InlinePresentation = Omit<DisplayValueProps<unknown>, 'reading' | 'children' | 'loadingFallback'> & {
|
|
67
|
+
loadingFallback?: ReactNode;
|
|
68
|
+
};
|
|
69
|
+
export type DataValueProps<Data = unknown, Input = unknown, Row = never> = InlinePresentation & ((NarrativeBinding<Data, Input, Row> & {
|
|
70
|
+
scope?: never;
|
|
71
|
+
}) | {
|
|
72
|
+
metric?: never;
|
|
73
|
+
dataset?: never;
|
|
74
|
+
source?: never;
|
|
75
|
+
scope: DataReading<string>;
|
|
76
|
+
children?: (scope: string) => ReactNode;
|
|
77
|
+
});
|
|
78
|
+
export declare function DataValue<Data, Input, Row>(props: DataValueProps<Data, Input, Row>): import("react").JSX.Element;
|
|
79
|
+
export {};
|
package/docs/app-authoring.md
CHANGED
|
@@ -27,63 +27,71 @@ readers can inspect the source of each claim.
|
|
|
27
27
|
|
|
28
28
|
Connect visualizations with introductions and explanations. Use `<TextWidget>`
|
|
29
29
|
for a narrative panel with the standard widget frame, or `<TextContent>` for
|
|
30
|
-
borderless prose.
|
|
31
|
-
|
|
32
|
-
and
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
and
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
30
|
+
borderless prose. Render static titles, descriptions, and instructions immediately. Use metric and dataset
|
|
31
|
+
bindings for dynamic values and `<DataValue>` for values within static prose.
|
|
32
|
+
Skeletonize only the content that needs data. Reuse bindings and the displayed
|
|
33
|
+
source in narrative so values, formatting, and evidence follow filter changes,
|
|
34
|
+
refresh, and failure.
|
|
35
|
+
|
|
36
|
+
| Task | Documentation |
|
|
37
|
+
| ------------------------------------------------ | ----------------------------------------------------------------- |
|
|
38
|
+
| Choose components, CSS tokens, and styling hooks | [Styling](styling.md) |
|
|
39
|
+
| Define queries, inputs, and result parsing | [Operations](contract.md) |
|
|
40
|
+
| Build views, filters, and request states | [Views](views.md) |
|
|
41
|
+
| Introduce and explain visualizations | [Narrative text](widgets.md#narrative-text) |
|
|
42
|
+
| Find formatters and presentation helpers | [App helpers](formatting-and-appearance.md) |
|
|
43
|
+
| Register source names | [Source identifiers](data-context.md#register-source-identifiers) |
|
|
44
|
+
| Choose date and field filters | [Filter variables](variables.md) |
|
|
45
|
+
| Handle refresh and stale results | [Displayed results](views.md#preserve-displayed-results) |
|
|
46
|
+
| Export displayed data as CSV | [CSV export](stories-and-export.md#export-displayed-data-as-csv) |
|
|
47
|
+
| Render widgets and custom visuals | [Widgets](widgets.md) |
|
|
48
|
+
| Bind definitions and source evidence | [Data context](data-context.md#bind-evidence) |
|
|
49
|
+
|
|
50
|
+
Declare reusable [datasets and metrics](widgets.md#declare-datasets-and-metrics)
|
|
51
|
+
on the view so tables, exports, and stories share values and evidence.
|
|
52
|
+
|
|
53
|
+
Every data app supplies a [story and CSV export](stories-and-export.md)
|
|
54
|
+
from the displayed result. Export all distinct datasets at their displayed grain;
|
|
55
|
+
reuse a dataset when several visuals derive from the same rows. Present the
|
|
56
|
+
findings that answer the reader's question, rather than every row or chart.
|
|
57
|
+
Use the [standard layout](layout.md) and built-in toolbar actions.
|
|
58
|
+
|
|
59
|
+
### Organize the exploration
|
|
60
|
+
|
|
61
|
+
Use sections for a focused question and for findings readers should compare
|
|
62
|
+
side by side. Add app-level `<Tabs>` from `/react/ui` when the exploration has
|
|
63
|
+
distinct analytical questions, such as Overview, Retention, and Segments, each
|
|
64
|
+
with its own context and group of widgets. Lead with the most useful overview
|
|
65
|
+
and label tabs by the question or subject they explore.
|
|
66
|
+
|
|
67
|
+
Use `<VisualizationWidget>`'s `views` for alternate representations of the same
|
|
68
|
+
dataset, such as a chart and its rows. Keep these choices within the widget;
|
|
69
|
+
use filter variables when the reader is changing the data scope.
|
|
70
|
+
|
|
71
|
+
Navigation tabs, widget views, and declared data views have different roles.
|
|
72
|
+
A declared view owns inputs, requests, and displayed results; a tab does not
|
|
73
|
+
require a separate data view. Keep related datasets in one view for a coherent
|
|
74
|
+
snapshot. Use independent views and `<DataSection>` boundaries when content
|
|
75
|
+
needs separate requests. Keep filter placement consistent across tabs and make
|
|
76
|
+
each filter's scope clear. Derive findings, story, and export from the displayed
|
|
77
|
+
results and their inputs.
|
|
78
|
+
|
|
79
|
+
Choose each visual for the question it answers; see
|
|
80
|
+
[visualization selection](widgets.md#choose-a-visualization).
|
|
75
81
|
|
|
76
82
|
## Verify the app
|
|
77
83
|
|
|
84
|
+
Follow the [UI quality contract](ui-quality.md) for hierarchy, responsive
|
|
85
|
+
composition, typography, and control states.
|
|
86
|
+
|
|
78
87
|
Verify findings against the source and the user's question. Distinguish measured
|
|
79
88
|
zero, unavailable values, and empty results. Check filters, refresh, loading,
|
|
80
89
|
empty, error, and stale states, then present the story. Download CSV from the
|
|
81
90
|
standalone and embedded toolbar and verify its filename, columns, raw values,
|
|
82
91
|
and filter scope against the displayed result. Export and Present story must be
|
|
83
|
-
available once
|
|
84
|
-
errors
|
|
92
|
+
available once results are shown; initial loading, empty, and initial
|
|
93
|
+
errors keep both actions visible and disabled. Inspect both experiences
|
|
85
94
|
at phone and desktop widths in light and dark themes.
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
stay backend-owned. Edit app-owned files; installed package files are dependencies.
|
|
96
|
+
Keep credentials, authorization, and enforced query limits in the host/backend.
|
|
97
|
+
Edit app-owned files; installed package files are dependencies.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Data context and evidence
|
|
2
|
+
|
|
3
|
+
Set `dataContext: context` in each view. The app and its sections inherit the
|
|
4
|
+
view's description, glossary, and permitted query evidence. Render registered identifiers where `<DataIdentifier>` is used.
|
|
5
|
+
|
|
6
|
+
Physical source identifiers name inspected tables and columns. Glossary entries
|
|
7
|
+
explain business meaning. Query names link those definitions and displayed claims
|
|
8
|
+
to their execution evidence. Keep all three explicit.
|
|
9
|
+
|
|
10
|
+
## Register source identifiers
|
|
11
|
+
|
|
12
|
+
Use `defineDataIdentifiers()` from `/react` to register exact catalog, schema,
|
|
13
|
+
table, and field names. Use `<DataIdentifier>` in descriptions and glossary
|
|
14
|
+
entries to render those source references consistently.
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { defineDataIdentifiers } from '@altertable/data-app/react';
|
|
18
|
+
|
|
19
|
+
const identifiers = defineDataIdentifiers({
|
|
20
|
+
tables: {
|
|
21
|
+
events: {
|
|
22
|
+
catalog: 'product_analytics',
|
|
23
|
+
schema: 'analytics',
|
|
24
|
+
name: 'events',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
columns: { identity: { table: 'events', name: 'identity_uuid' } },
|
|
28
|
+
});
|
|
29
|
+
const { DataIdentifier } = identifiers;
|
|
30
|
+
|
|
31
|
+
<DataIdentifier id="tables.events" />;
|
|
32
|
+
<DataIdentifier id="columns.events.identity" />;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Pass `identifiers.definitions` to `createDataContext()` to reuse the source
|
|
36
|
+
registry.
|
|
37
|
+
|
|
38
|
+
## Bind evidence
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
const queries = defineQueryNames({ activity: 'feature-activity' });
|
|
42
|
+
// In the server operation: queryNames: queries
|
|
43
|
+
const context = createDataContext(queries)({
|
|
44
|
+
identifiers: identifiers.definitions,
|
|
45
|
+
description: (
|
|
46
|
+
<>
|
|
47
|
+
Explore activity in <DataIdentifier id="tables.events" />.
|
|
48
|
+
</>
|
|
49
|
+
),
|
|
50
|
+
glossary: {
|
|
51
|
+
identities: {
|
|
52
|
+
term: 'Tracked identities',
|
|
53
|
+
definition: (
|
|
54
|
+
<>
|
|
55
|
+
Distinct <DataIdentifier id="columns.events.identity" /> values.
|
|
56
|
+
</>
|
|
57
|
+
),
|
|
58
|
+
queryNames: [queries.activity],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
const evidence = context.evidence({
|
|
63
|
+
id: 'identities',
|
|
64
|
+
glossaryIds: ['identities'],
|
|
65
|
+
queryNames: [queries.activity],
|
|
66
|
+
});
|
|
67
|
+
// activityView is declared with dataContext: context.
|
|
68
|
+
const trackedIdentities = activityView.metric(
|
|
69
|
+
{
|
|
70
|
+
id: 'identities',
|
|
71
|
+
glossaryId: 'identities',
|
|
72
|
+
format: { kind: 'count' },
|
|
73
|
+
},
|
|
74
|
+
data => ({ current: data.count })
|
|
75
|
+
);
|
|
76
|
+
const finding = context.finding({
|
|
77
|
+
id: 'activity',
|
|
78
|
+
headline: 'What people do',
|
|
79
|
+
visual: <ActivityChart />,
|
|
80
|
+
evidence: { id: 'activity-evidence', queryNames: [queries.activity] },
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Import `defineQueryNames()` from `/contract` and the context/identifier factories from `/react`. Use the same registry in `defineOperation({ queryNames: queries, ... })`.
|
|
85
|
+
|
|
86
|
+
Use `view.metric(definition, select)` to register and bind a metric in one call.
|
|
87
|
+
Declare dataset evidence references inside `view.dataset()`; the view registers
|
|
88
|
+
and validates them too.
|
|
89
|
+
Widgets and stories share its values and evidence; see [datasets and metrics](widgets.md#declare-datasets-and-metrics).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Formatting and appearance
|
|
2
|
+
|
|
3
|
+
Use package helpers so a value has the same meaning in prose, tables, charts,
|
|
4
|
+
and stories. TypeScript and JSDoc describe options and supported values.
|
|
5
|
+
|
|
6
|
+
## Format values
|
|
7
|
+
|
|
8
|
+
Import formatting helpers from `@altertable/data-app/format`.
|
|
9
|
+
|
|
10
|
+
| Helper | Use |
|
|
11
|
+
| ------------------- | ------------------------------------------------------ |
|
|
12
|
+
| `formatNumber()` | General numerical values |
|
|
13
|
+
| `formatCount()` | Nonnegative integer counts, optionally compact |
|
|
14
|
+
| `formatPercent()` | Ratios represented as a fraction, such as 0.12 for 12% |
|
|
15
|
+
| `formatMetric()` | A metric definition's count, ratio, or currency format |
|
|
16
|
+
| `formatDateRange()` | Inclusive calendar ranges |
|
|
17
|
+
| `pluralize()` | Count-dependent labels |
|
|
18
|
+
|
|
19
|
+
Missing values render distinctly from measured zero. Define metric formatting
|
|
20
|
+
once with `view.metric()`; comparisons derive from its displayed source. Previous
|
|
21
|
+
values, range labels, and favorable direction belong to that metric and displayed
|
|
22
|
+
input; see [data context](data-context.md) and [views](views.md).
|
|
23
|
+
|
|
24
|
+
Import `chartColor()` from `/react` to select colors from the configured palette.
|
|
25
|
+
`<PeriodSummary>` describes reporting periods; `<UpdatedAt>` and
|
|
26
|
+
`<DateTimeTooltip>` expose readable timestamps with exact-date details.
|
|
27
|
+
`<DataTableTimestamp>` and `<DataTableShare>` format custom table cells.
|
|
28
|
+
|
|
29
|
+
## Configure identity and appearance
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import type { DataAppConfig } from '@altertable/data-app/config';
|
|
33
|
+
|
|
34
|
+
const config = {
|
|
35
|
+
title: 'Product activity',
|
|
36
|
+
scope: { organization: 'Acme', environment: 'Production' },
|
|
37
|
+
appearance: {
|
|
38
|
+
theme: 'system',
|
|
39
|
+
accentColor: '#405d47',
|
|
40
|
+
density: 'comfortable',
|
|
41
|
+
},
|
|
42
|
+
} satisfies DataAppConfig;
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Scope labels describe the configured connection; they do not grant access.
|
|
46
|
+
`dataAppTitle()` produces the scoped document title used by `mountDataApp()`.
|
|
47
|
+
|
|
48
|
+
Appearance controls the brand palette, typography, density, corner radius, and
|
|
49
|
+
elevation. Those settings apply consistently to the app instead of tuning each
|
|
50
|
+
widget. Theme preference is reader-owned in standalone apps. A trusted parent's
|
|
51
|
+
resolved theme takes precedence and hides local theme controls; see
|
|
52
|
+
[parent presentation](embed.md#parent-presentation).
|
package/docs/hosted-apps.md
CHANGED
|
@@ -24,16 +24,3 @@ For execution details, see [browser-owned operations](client.md#browser-owned-op
|
|
|
24
24
|
4. Confirm the host can query the same catalogs, tables, and fields.
|
|
25
25
|
[Verify the app](app-authoring.md#verify-the-app) in the hosted runtime against
|
|
26
26
|
the local version's filters and findings.
|
|
27
|
-
|
|
28
|
-
## Preview in this repository
|
|
29
|
-
|
|
30
|
-
```fish
|
|
31
|
-
bun install --frozen-lockfile
|
|
32
|
-
bun run build
|
|
33
|
-
bun browser-tests/server.ts
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Open [the starter preview](http://127.0.0.1:27418/starter-data-app).
|
|
37
|
-
Its test host executes the sample SQL through the iframe bridge using SQLite.
|
|
38
|
-
|
|
39
|
-
For checks, see [Contributing](../CONTRIBUTING.md).
|
package/docs/layout.md
CHANGED
|
@@ -6,9 +6,11 @@ for sections, `<Grid>` for peer widgets, and `<GridItem>` for spans. Use
|
|
|
6
6
|
external spacing. Keep widget customization inside the widget so its parent can maintain consistent
|
|
7
7
|
spacing between sections and cards.
|
|
8
8
|
|
|
9
|
-
By default, section and widget gaps use `--
|
|
9
|
+
By default, section and widget gaps use `--atbl-layout-gap`, with density configured once
|
|
10
10
|
in `DataAppConfig.appearance`. The package owns wrapping and span collapse based
|
|
11
|
-
on the available container width.
|
|
11
|
+
on the available container width and configured gap. Custom length values for
|
|
12
|
+
`--atbl-layout-gap`, `--atbl-space-sm`, and `--atbl-space-md` also drive span
|
|
13
|
+
collapse; a two-column span activates only when two minimum-width tracks fit.
|
|
12
14
|
|
|
13
15
|
```tsx
|
|
14
16
|
<Stack>
|
package/docs/react-embed.md
CHANGED
|
@@ -93,8 +93,8 @@ are mutually exclusive.
|
|
|
93
93
|
|
|
94
94
|
## Loading an embedded app
|
|
95
95
|
|
|
96
|
-
Use `<DataAppSkeleton>` from `/react` while the host builds or starts an app.
|
|
97
|
-
Call `injectDataAppShellStyles()` from `/react` before rendering the placeholder.
|
|
96
|
+
Use `<DataAppSkeleton>` from `/react/ui` while the host builds or starts an app.
|
|
97
|
+
Call `injectDataAppShellStyles()` from `/react/ui` before rendering the placeholder.
|
|
98
98
|
The host owns when to show it and supplies any surrounding header or footer.
|
|
99
99
|
`/react/embed` itself remains independent of UI components and styles.
|
|
100
100
|
|