@declarion/react 0.22.0 → 0.24.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/dist-lib/{ActionRunner-C2A73D9b.js → ActionRunner-eixtbT5K.js} +1267 -1161
- package/dist-lib/{DraggableChildrenTable-CzbVcMu7.js → DraggableChildrenTable-ByuH27zv.js} +46 -46
- package/dist-lib/{Grid-8k-ysclX.js → Grid-hWuSXZWA.js} +1 -1
- package/dist-lib/{MarkdownRenderer--n1GTrwy.js → MarkdownRenderer-x-5sbiRs.js} +1 -1
- package/dist-lib/components/fields/RefCombobox.d.ts +45 -0
- package/dist-lib/components/filters/FilterChips.d.ts +6 -2
- package/dist-lib/components/filters/FilterDrawer.d.ts +12 -1
- package/dist-lib/components/pages/list/filter-meta.d.ts +2 -1
- package/dist-lib/components/pages/list/use-filter-value-labels.d.ts +24 -0
- package/dist-lib/components/primitives/Fields.d.ts +11 -1
- package/dist-lib/hooks/useCursorEntityData.d.ts +8 -0
- package/dist-lib/index.d.ts +2 -0
- package/dist-lib/index.js +3503 -3253
- package/dist-lib/lib/filters.d.ts +30 -2
- package/dist-lib/testing.d.ts +19 -1
- package/dist-lib/testing.js +65 -43
- package/dist-lib/types/schema.d.ts +35 -1
- package/package.json +1 -1
|
@@ -11,6 +11,25 @@ export interface FilterNode {
|
|
|
11
11
|
}
|
|
12
12
|
/** All supported filter operator codes. */
|
|
13
13
|
export type FilterOp = "eq" | "not_eq" | "gt" | "gte" | "lt" | "lte" | "in" | "not_in" | "between" | "contains" | "starts_with" | "is_empty" | "is_not_empty" | "today" | "this_week" | "this_month" | "last_7_days" | "last_30_days" | "last_hour" | "last_24_hours";
|
|
14
|
+
/**
|
|
15
|
+
* Resolution status of one filter value, produced by the filter-value label
|
|
16
|
+
* resolver (`useFilterValueLabels`). `special` / `resolved` carry a `label`
|
|
17
|
+
* and are terminal-synchronous (no flicker); `loading` is a ref fetch in
|
|
18
|
+
* flight (render a skeleton, never the raw uuid); `unresolved` is a ref that
|
|
19
|
+
* could not be labeled (deleted / no access); `plain` is a literal/number/date
|
|
20
|
+
* (rendered raw, as before).
|
|
21
|
+
*/
|
|
22
|
+
export type FilterValueStatus = "special" | "resolved" | "loading" | "unresolved" | "plain";
|
|
23
|
+
/** Resolver result for one `(field, value)`: a label when known, the raw
|
|
24
|
+
* coerced value always (the terminal fallback). */
|
|
25
|
+
export interface FilterValueLabel {
|
|
26
|
+
status: FilterValueStatus;
|
|
27
|
+
label?: string;
|
|
28
|
+
raw: string;
|
|
29
|
+
}
|
|
30
|
+
/** Maps one stored filter `(field, value)` to its human label. The single
|
|
31
|
+
* source of chip + drawer value labels; see `useFilterValueLabels`. */
|
|
32
|
+
export type FilterValueResolver = (field: string, value: unknown) => FilterValueLabel;
|
|
14
33
|
/** Operators that don't take a value. */
|
|
15
34
|
export declare const NO_VALUE_OPS: Set<FilterOp>;
|
|
16
35
|
/** Human-friendly display names for operators. */
|
|
@@ -30,8 +49,17 @@ export declare function hasOrGroups(nodes: FilterNode[]): boolean;
|
|
|
30
49
|
export declare function flatFieldConditions(nodes: FilterNode[]): FilterNode[];
|
|
31
50
|
/** Count active field conditions in the filter tree. */
|
|
32
51
|
export declare function countActiveFilters(nodes: FilterNode[]): number;
|
|
33
|
-
/**
|
|
34
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Format a filter node for chip display as a string (op label + value).
|
|
54
|
+
*
|
|
55
|
+
* When a `resolveValue` resolver is passed, each value is humanized to its
|
|
56
|
+
* label for the synchronous statuses (`special` / `resolved`) and falls back
|
|
57
|
+
* to the raw value otherwise. Without a resolver, values render raw exactly as
|
|
58
|
+
* before. This string form is used in OR-group joins and as a fallback; the
|
|
59
|
+
* single-chip renderer (`FilterChips`) uses a richer per-value renderer that
|
|
60
|
+
* also shows a skeleton while a ref label loads.
|
|
61
|
+
*/
|
|
62
|
+
export declare function formatFilterChip(node: FilterNode, resolveValue?: FilterValueResolver): string;
|
|
35
63
|
/** Create a simple equality filter node. */
|
|
36
64
|
export declare function eqFilter(field: string, value: string): FilterNode;
|
|
37
65
|
/** Remove a filter node by field name, recursing into OR/AND groups. */
|
package/dist-lib/testing.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ReactElement } from "react";
|
|
1
|
+
import { type ReactElement, type ReactNode } from "react";
|
|
2
2
|
import { type RenderResult } from "@testing-library/react";
|
|
3
3
|
import { QueryClient } from "@tanstack/react-query";
|
|
4
4
|
import { type ActionExecuteRequest, type ActionExecuteResponse } from "./api/data";
|
|
@@ -64,3 +64,21 @@ export declare function cleanupDeclarion(): void;
|
|
|
64
64
|
* live queryClient.
|
|
65
65
|
*/
|
|
66
66
|
export declare function renderWithDeclarion(ui: ReactElement, opts?: RenderWithDeclarionOptions): RenderWithDeclarionResult;
|
|
67
|
+
export interface PreviewHarnessProps extends RenderWithDeclarionOptions {
|
|
68
|
+
children: ReactNode;
|
|
69
|
+
/** Default true: seeds an admin user so useSchema's auth gate opens and the
|
|
70
|
+
* screen renders authenticated. Set false to render a pre-login screen
|
|
71
|
+
* (SignIn / SignUp / Forgot / Reset / TwoFA / AuthShell) in its logged-out
|
|
72
|
+
* state, where useSchema stays disabled (no fetch) as in production. */
|
|
73
|
+
authenticated?: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Component form of {@link renderWithDeclarion} for live previews / stories /
|
|
77
|
+
* Storybook: wraps `children` in the SDK's real providers (QueryClient +
|
|
78
|
+
* MemoryRouter + ActionRunnerProvider + the Sonner outlet) and seeds the same
|
|
79
|
+
* read/action seams from props, so an SDK screen renders against fixtures with
|
|
80
|
+
* no network. Unlike renderWithDeclarion (which calls testing-library's render),
|
|
81
|
+
* this returns an element you compose directly. Providers + seeds are created
|
|
82
|
+
* once per mount (useState initializer) so a static snapshot is deterministic.
|
|
83
|
+
*/
|
|
84
|
+
export declare function PreviewHarness({ children, authenticated, ...opts }: PreviewHarnessProps): ReactElement;
|
package/dist-lib/testing.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { It as e, dt as t, ft as n, rt as r, t as i, ut as a } from "./ActionRunner-eixtbT5K.js";
|
|
2
|
+
import { useState as o } from "react";
|
|
3
|
+
import { MemoryRouter as s } from "react-router-dom";
|
|
4
|
+
import { QueryClient as c, QueryClientProvider as l } from "@tanstack/react-query";
|
|
5
|
+
import { Toaster as u } from "sonner";
|
|
6
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
7
|
+
import { render as p } from "@testing-library/react";
|
|
7
8
|
//#region src/testing.tsx
|
|
8
|
-
function
|
|
9
|
+
function m(e = {}) {
|
|
9
10
|
return {
|
|
10
11
|
languages: [],
|
|
11
12
|
structures: {},
|
|
@@ -18,7 +19,7 @@ function p(e = {}) {
|
|
|
18
19
|
...e
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
|
-
function
|
|
22
|
+
function h(e, t = {}, n) {
|
|
22
23
|
return {
|
|
23
24
|
data: e,
|
|
24
25
|
meta: {
|
|
@@ -32,27 +33,27 @@ function m(e, t = {}, n) {
|
|
|
32
33
|
...n === void 0 ? {} : { $refs: n }
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
|
-
function
|
|
36
|
+
function g(e) {
|
|
36
37
|
return e;
|
|
37
38
|
}
|
|
38
|
-
var
|
|
39
|
-
function
|
|
39
|
+
var _ = Symbol("declarion.testing.actionEnvelope");
|
|
40
|
+
function v(e) {
|
|
40
41
|
return {
|
|
41
42
|
...e,
|
|
42
|
-
[
|
|
43
|
+
[_]: !0
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
|
-
function
|
|
46
|
-
if (e && typeof e == "object" && e[
|
|
46
|
+
function y(e) {
|
|
47
|
+
if (e && typeof e == "object" && e[_]) {
|
|
47
48
|
let t = { ...e };
|
|
48
|
-
return delete t[
|
|
49
|
+
return delete t[_], t;
|
|
49
50
|
}
|
|
50
51
|
return {
|
|
51
52
|
status: "success",
|
|
52
53
|
result: e
|
|
53
54
|
};
|
|
54
55
|
}
|
|
55
|
-
function
|
|
56
|
+
function b() {
|
|
56
57
|
let t = {
|
|
57
58
|
id: "test-tenant",
|
|
58
59
|
code: "test",
|
|
@@ -71,7 +72,7 @@ function y() {
|
|
|
71
72
|
};
|
|
72
73
|
e.getState().setUser(n, t);
|
|
73
74
|
}
|
|
74
|
-
function
|
|
75
|
+
function x(e) {
|
|
75
76
|
if (e === void 0) return { rows: [] };
|
|
76
77
|
if (Array.isArray(e)) return { rows: e };
|
|
77
78
|
if (Array.isArray(e.data)) {
|
|
@@ -84,64 +85,85 @@ function b(e) {
|
|
|
84
85
|
}
|
|
85
86
|
return e;
|
|
86
87
|
}
|
|
87
|
-
function
|
|
88
|
-
let
|
|
89
|
-
|
|
90
|
-
let n =
|
|
88
|
+
function S(e) {
|
|
89
|
+
let i = e.entityData ?? {};
|
|
90
|
+
n((e, t) => {
|
|
91
|
+
let n = i[e], r = x(n), a = r.meta?.per_page ?? 50, o = r.meta?.total ?? r.rows.length;
|
|
91
92
|
return t.count === "only" ? {
|
|
92
93
|
data: [],
|
|
93
94
|
meta: {
|
|
94
95
|
page: 1,
|
|
95
96
|
per_page: a,
|
|
96
97
|
total: o,
|
|
97
|
-
total_pages:
|
|
98
|
-
count_mode:
|
|
98
|
+
total_pages: r.meta?.total_pages ?? Math.max(1, Math.ceil(o / a)),
|
|
99
|
+
count_mode: r.meta?.count_mode ?? "exact"
|
|
99
100
|
}
|
|
100
101
|
} : {
|
|
101
|
-
data:
|
|
102
|
+
data: r.rows,
|
|
102
103
|
meta: {
|
|
103
|
-
page:
|
|
104
|
+
page: r.meta?.page ?? 1,
|
|
104
105
|
per_page: a,
|
|
105
|
-
has_more:
|
|
106
|
+
has_more: r.meta?.has_more ?? !1
|
|
106
107
|
},
|
|
107
|
-
...
|
|
108
|
+
...r.refs === void 0 ? {} : { $refs: r.refs }
|
|
108
109
|
};
|
|
109
110
|
});
|
|
110
111
|
let o = e.entityItems ?? {};
|
|
111
|
-
|
|
112
|
-
let n = typeof t == "string" ? t :
|
|
112
|
+
t((e, t) => {
|
|
113
|
+
let n = typeof t == "string" ? t : r(t);
|
|
113
114
|
return { data: o[e]?.[n] };
|
|
114
115
|
});
|
|
115
116
|
let s = e.actionResults ?? {};
|
|
116
|
-
|
|
117
|
+
a(async (e, t) => {
|
|
117
118
|
if (!(e in s)) throw Error(`renderWithDeclarion: no action result registered for "${e}" (seed it via actionResults)`);
|
|
118
119
|
let n = s[e];
|
|
119
|
-
return
|
|
120
|
+
return y(typeof n == "function" ? await n(t) : n);
|
|
120
121
|
});
|
|
121
122
|
}
|
|
122
|
-
function
|
|
123
|
-
|
|
123
|
+
function C() {
|
|
124
|
+
n(null), t(null), a(null), e.setState({
|
|
124
125
|
user: null,
|
|
125
126
|
isAuthenticated: !1,
|
|
126
127
|
activeTenant: null,
|
|
127
128
|
tenants: []
|
|
128
129
|
});
|
|
129
130
|
}
|
|
130
|
-
function
|
|
131
|
-
let n = new
|
|
131
|
+
function w(e, t = {}) {
|
|
132
|
+
let n = new c({ defaultOptions: { queries: {
|
|
132
133
|
retry: !1,
|
|
133
134
|
gcTime: Infinity
|
|
134
135
|
} } });
|
|
135
|
-
|
|
136
|
-
let
|
|
136
|
+
b(), n.setQueryData(["schema", !0], t.schema ?? m()), S(t);
|
|
137
|
+
let r = p(/* @__PURE__ */ f(l, {
|
|
137
138
|
client: n,
|
|
138
|
-
children: [/* @__PURE__ */
|
|
139
|
+
children: [/* @__PURE__ */ d(s, {
|
|
139
140
|
initialEntries: [t.route ?? "/"],
|
|
140
|
-
children: /* @__PURE__ */
|
|
141
|
-
}), /* @__PURE__ */ u
|
|
141
|
+
children: /* @__PURE__ */ d(i, { children: e })
|
|
142
|
+
}), /* @__PURE__ */ d(u, {})]
|
|
142
143
|
}));
|
|
143
|
-
return Object.assign(
|
|
144
|
+
return Object.assign(r, { queryClient: n });
|
|
145
|
+
}
|
|
146
|
+
function T({ children: t, authenticated: n = !0, ...r }) {
|
|
147
|
+
let [a] = o(() => {
|
|
148
|
+
let t = new c({ defaultOptions: { queries: {
|
|
149
|
+
retry: !1,
|
|
150
|
+
gcTime: Infinity
|
|
151
|
+
} } });
|
|
152
|
+
return n ? (b(), t.setQueryData(["schema", !0], r.schema ?? m())) : e.setState({
|
|
153
|
+
user: null,
|
|
154
|
+
isAuthenticated: !1,
|
|
155
|
+
activeTenant: null,
|
|
156
|
+
tenants: []
|
|
157
|
+
}), S(r), t;
|
|
158
|
+
});
|
|
159
|
+
return /* @__PURE__ */ f(l, {
|
|
160
|
+
client: a,
|
|
161
|
+
children: [/* @__PURE__ */ d(s, {
|
|
162
|
+
initialEntries: [r.route ?? "/"],
|
|
163
|
+
children: /* @__PURE__ */ d(i, { children: t })
|
|
164
|
+
}), /* @__PURE__ */ d(u, {})]
|
|
165
|
+
});
|
|
144
166
|
}
|
|
145
|
-
typeof afterEach == "function" && afterEach(() =>
|
|
167
|
+
typeof afterEach == "function" && afterEach(() => C());
|
|
146
168
|
//#endregion
|
|
147
|
-
export {
|
|
169
|
+
export { T as PreviewHarness, v as actionEnvelope, g as actionResults, C as cleanupDeclarion, h as entityDataFixture, w as renderWithDeclarion, m as schemaFixture };
|
|
@@ -44,6 +44,29 @@ export interface RefConfig {
|
|
|
44
44
|
display?: RefDisplay;
|
|
45
45
|
fields?: string[];
|
|
46
46
|
}
|
|
47
|
+
/** One declared filter-only option ({code, label}). `code` is stored into the
|
|
48
|
+
* filter when chosen (a literal or a `$user.*` token resolved server-side);
|
|
49
|
+
* `label` is the localized display string. Mirrors engine.FilterSpecialValue. */
|
|
50
|
+
export interface FilterSpecialValue {
|
|
51
|
+
code: string;
|
|
52
|
+
label: LocalizedString;
|
|
53
|
+
}
|
|
54
|
+
/** Per-screen override of a field's filter_special_values, keyed in
|
|
55
|
+
* Screen.filter_special_values by the filter field code. */
|
|
56
|
+
export interface ScreenFilterSpecialValues {
|
|
57
|
+
mode?: "append" | "replace" | "clear";
|
|
58
|
+
values?: FilterSpecialValue[];
|
|
59
|
+
}
|
|
60
|
+
/** Normalizes a handler-param ref (scalar entity-code string OR structured
|
|
61
|
+
* object) into a RefConfig. One helper shared by filters, SmartDetailPage
|
|
62
|
+
* fields, and ActionDialog params so the scalar shorthand and structured form
|
|
63
|
+
* reach the relation combobox identically. */
|
|
64
|
+
export declare function normalizeRefConfig(ref: string | RefConfig | undefined): RefConfig | undefined;
|
|
65
|
+
/** Computes the effective filter_special_values for a field on a screen by
|
|
66
|
+
* merging the field-level list with an optional screen override per its mode
|
|
67
|
+
* (append [default] / replace / clear). Pure; mirrors the engine's
|
|
68
|
+
* EffectiveFilterSpecialValues so the SDK and loader agree. */
|
|
69
|
+
export declare function effectiveFilterSpecialValues(fieldValues: FilterSpecialValue[] | undefined, override: ScreenFilterSpecialValues | undefined): FilterSpecialValue[];
|
|
47
70
|
export interface UnitConfig {
|
|
48
71
|
storage: string;
|
|
49
72
|
display?: string;
|
|
@@ -98,6 +121,10 @@ export interface EntityField {
|
|
|
98
121
|
enum?: string;
|
|
99
122
|
files?: FileFieldDef;
|
|
100
123
|
ref?: RefConfig;
|
|
124
|
+
/** Filter-only declared options (e.g. "Myself" -> $user.id) surfaced in this
|
|
125
|
+
* field's filter control. Inert everywhere else. A screen MAY override these
|
|
126
|
+
* per-screen via Screen.filter_special_values. */
|
|
127
|
+
filter_special_values?: FilterSpecialValue[];
|
|
101
128
|
structure?: string;
|
|
102
129
|
array?: boolean;
|
|
103
130
|
copyable?: boolean;
|
|
@@ -529,6 +556,10 @@ export interface Screen {
|
|
|
529
556
|
list_resizable?: boolean;
|
|
530
557
|
detail_screen?: string;
|
|
531
558
|
fixed_filters?: Record<string, unknown>;
|
|
559
|
+
/** Per-screen override of field-level filter_special_values, keyed by filter
|
|
560
|
+
* field code. Append/replace/clear the entity field's declared values for
|
|
561
|
+
* just this screen. Mirrors engine.Screen.FilterSpecialValues. */
|
|
562
|
+
filter_special_values?: Record<string, ScreenFilterSpecialValues>;
|
|
532
563
|
views?: ViewDefinition[];
|
|
533
564
|
/** Action slot promotion for list screens (rule-of-2). UI honors first 2.
|
|
534
565
|
* Note: primary_record_actions (detail header) was removed in Task 5 of plan
|
|
@@ -639,7 +670,10 @@ export interface HandlerParam {
|
|
|
639
670
|
description?: LocalizedString;
|
|
640
671
|
color?: string;
|
|
641
672
|
}>;
|
|
642
|
-
ref
|
|
673
|
+
/** `type: ref` param target. Scalar entity-code string (shorthand) OR a
|
|
674
|
+
* structured RefConfig (non-id lookup / display). Normalize via
|
|
675
|
+
* normalizeRefConfig before use. */
|
|
676
|
+
ref?: string | RefConfig;
|
|
643
677
|
display?: HandlerParamDisplay;
|
|
644
678
|
/**
|
|
645
679
|
* Narrows the param's option set. Values may be literals or templates
|