@cosmicdrift/kumiko-renderer 0.328.0 → 0.328.1
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/app/action-form-shim.d.ts +14 -0
- package/{src/app/action-form-shim.ts → dist/app/action-form-shim.js} +26 -46
- package/dist/app/app-features-context.d.ts +8 -0
- package/dist/app/app-features-context.js +16 -0
- package/dist/app/column-renderers.d.ts +30 -0
- package/dist/app/column-renderers.js +34 -0
- package/dist/app/config-edit-shim.d.ts +11 -0
- package/{src/app/config-edit-shim.ts → dist/app/config-edit-shim.js} +12 -25
- package/dist/app/content-editors.d.ts +38 -0
- package/dist/app/content-editors.js +26 -0
- package/dist/app/content-preview.d.ts +11 -0
- package/dist/app/content-preview.js +30 -0
- package/dist/app/custom-screens.d.ts +11 -0
- package/dist/app/custom-screens.js +20 -0
- package/dist/app/dashboard-body.d.ts +16 -0
- package/dist/app/dashboard-body.js +11 -0
- package/dist/app/extension-form-submit.d.ts +32 -0
- package/dist/app/extension-form-submit.js +73 -0
- package/dist/app/extension-sections.d.ts +72 -0
- package/dist/app/extension-sections.js +34 -0
- package/dist/app/feature-schema.d.ts +4 -0
- package/{src/app/feature-schema.ts → dist/app/feature-schema.js} +12 -22
- package/dist/app/form-schema.d.ts +4 -0
- package/dist/app/form-schema.js +85 -0
- package/dist/app/kumiko-screen.d.ts +43 -0
- package/dist/app/kumiko-screen.js +2206 -0
- package/dist/app/layout-fields.d.ts +3 -0
- package/dist/app/layout-fields.js +20 -0
- package/dist/app/list-facets.d.ts +48 -0
- package/dist/app/list-facets.js +185 -0
- package/dist/app/nav.d.ts +61 -0
- package/dist/app/nav.js +139 -0
- package/dist/app/projection-detail-shim.d.ts +8 -0
- package/dist/app/projection-detail-shim.js +71 -0
- package/dist/app/projection-list-shim.d.ts +13 -0
- package/{src/app/projection-list-shim.ts → dist/app/projection-list-shim.js} +26 -41
- package/dist/app/qn.d.ts +7 -0
- package/{src/app/qn.ts → dist/app/qn.js} +9 -11
- package/dist/app/qualify-screen-id.d.ts +2 -0
- package/{src/app/qualify-screen-id.ts → dist/app/qualify-screen-id.js} +5 -6
- package/dist/app/reference-facet-bridge.d.ts +10 -0
- package/dist/app/reference-facet-bridge.js +22 -0
- package/dist/app/return-to.d.ts +31 -0
- package/dist/app/return-to.js +188 -0
- package/dist/app/row-actions.d.ts +77 -0
- package/dist/app/row-actions.js +416 -0
- package/dist/app/screen-access.d.ts +8 -0
- package/dist/app/screen-access.js +48 -0
- package/dist/app/secret-mint-body.d.ts +10 -0
- package/dist/app/secret-mint-body.js +140 -0
- package/dist/app/secrets-edit-body.d.ts +8 -0
- package/dist/app/secrets-edit-body.js +76 -0
- package/dist/app/use-embedded-screen.d.ts +6 -0
- package/dist/app/use-embedded-screen.js +19 -0
- package/dist/app/variable-chips.d.ts +7 -0
- package/dist/app/variable-chips.js +16 -0
- package/dist/app/write-failed-error.d.ts +6 -0
- package/dist/app/write-failed-error.js +21 -0
- package/dist/components/embedded-list-field.d.ts +10 -0
- package/dist/components/embedded-list-field.js +259 -0
- package/dist/components/grid-cell-for-field.d.ts +25 -0
- package/dist/components/grid-cell-for-field.js +31 -0
- package/dist/components/multi-select-checkboxes.d.ts +14 -0
- package/dist/components/multi-select-checkboxes.js +36 -0
- package/dist/components/reference-create-dialog.d.ts +13 -0
- package/dist/components/reference-create-dialog.js +46 -0
- package/dist/components/related-list-section.d.ts +25 -0
- package/dist/components/related-list-section.js +273 -0
- package/dist/components/render-edit-action-button.d.ts +22 -0
- package/dist/components/render-edit-action-button.js +43 -0
- package/dist/components/render-edit-logic.d.ts +6 -0
- package/dist/components/render-edit-logic.js +63 -0
- package/dist/components/render-edit-types.d.ts +209 -0
- package/dist/components/render-edit-types.js +1 -0
- package/dist/components/render-edit.d.ts +6 -0
- package/dist/components/render-edit.js +1169 -0
- package/dist/components/render-field.d.ts +38 -0
- package/dist/components/render-field.js +544 -0
- package/dist/components/render-list.d.ts +117 -0
- package/dist/components/render-list.js +232 -0
- package/dist/components/write-form-section.d.ts +17 -0
- package/dist/components/write-form-section.js +104 -0
- package/dist/context/dispatcher-context.d.ts +10 -0
- package/{src/context/dispatcher-context.tsx → dist/context/dispatcher-context.js} +16 -29
- package/dist/context/draft-storage-context.d.ts +12 -0
- package/dist/context/draft-storage-context.js +18 -0
- package/dist/context/user-roles-context.d.ts +7 -0
- package/{src/context/user-roles-context.tsx → dist/context/user-roles-context.js} +7 -15
- package/dist/format-when.d.ts +1 -0
- package/{src/format-when.ts → dist/format-when.js} +7 -7
- package/dist/hooks/reference-limits.d.ts +3 -0
- package/{src/hooks/reference-limits.ts → dist/hooks/reference-limits.js} +0 -1
- package/dist/hooks/use-ai-text.d.ts +52 -0
- package/dist/hooks/use-ai-text.js +90 -0
- package/dist/hooks/use-disclosure.d.ts +7 -0
- package/dist/hooks/use-disclosure.js +8 -0
- package/dist/hooks/use-form.d.ts +7 -0
- package/dist/hooks/use-form.js +36 -0
- package/dist/hooks/use-list-url-state.d.ts +43 -0
- package/dist/hooks/use-list-url-state.js +131 -0
- package/dist/hooks/use-mutation.d.ts +9 -0
- package/dist/hooks/use-mutation.js +37 -0
- package/dist/hooks/use-query.d.ts +14 -0
- package/dist/hooks/use-query.js +118 -0
- package/dist/hooks/use-reference-lookup.d.ts +14 -0
- package/dist/hooks/use-reference-lookup.js +63 -0
- package/dist/hooks/use-store.d.ts +3 -0
- package/dist/hooks/use-store.js +38 -0
- package/dist/hooks/use-stream-handler.d.ts +14 -0
- package/dist/hooks/use-stream-handler.js +93 -0
- package/dist/i18n-defaults.d.ts +2 -0
- package/dist/i18n-defaults.js +201 -0
- package/dist/i18n.d.ts +64 -0
- package/dist/i18n.js +166 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +55 -0
- package/dist/inside-drawer.d.ts +2 -0
- package/{src/inside-drawer.ts → dist/inside-drawer.js} +2 -5
- package/dist/page-header-slot.d.ts +2 -0
- package/{src/page-header-slot.ts → dist/page-header-slot.js} +2 -5
- package/dist/primitives.d.ts +1351 -0
- package/dist/primitives.js +73 -0
- package/dist/screen-fills-height.d.ts +3 -0
- package/dist/screen-fills-height.js +3 -0
- package/dist/share-links.d.ts +4 -0
- package/dist/share-links.js +11 -0
- package/dist/sort-by-accessor.d.ts +5 -0
- package/dist/sort-by-accessor.js +16 -0
- package/dist/sse/live-events.d.ts +23 -0
- package/dist/sse/live-events.js +28 -0
- package/dist/tokens.d.ts +83 -0
- package/dist/tokens.js +80 -0
- package/package.json +16 -9
- package/src/__tests__/error-i18n-defaults.test.ts +0 -82
- package/src/__tests__/format-when.test.ts +0 -12
- package/src/__tests__/i18n.test.tsx +0 -283
- package/src/__tests__/merge-search-params-into-initial.test.ts +0 -525
- package/src/__tests__/qn.test.ts +0 -83
- package/src/__tests__/share-links.test.ts +0 -26
- package/src/__tests__/sort-by-accessor.test.ts +0 -48
- package/src/__tests__/stringify-nav-params.test.ts +0 -33
- package/src/__tests__/use-list-url-state.test.tsx +0 -250
- package/src/app/__tests__/action-form-field-labels.test.tsx +0 -92
- package/src/app/__tests__/action-form-shim.test.ts +0 -137
- package/src/app/__tests__/build-list-query-payload.test.ts +0 -107
- package/src/app/__tests__/config-edit-shim.test.ts +0 -57
- package/src/app/__tests__/content-editors.test.tsx +0 -128
- package/src/app/__tests__/content-preview.test.tsx +0 -184
- package/src/app/__tests__/entity-edit-create-search-params.test.tsx +0 -236
- package/src/app/__tests__/entity-edit-fill-height.test.tsx +0 -118
- package/src/app/__tests__/entity-edit-jsonb-embedded-submit.test.tsx +0 -239
- package/src/app/__tests__/entity-list-create-singleton.test.tsx +0 -128
- package/src/app/__tests__/entity-list-row-action-entity-target.test.tsx +0 -424
- package/src/app/__tests__/entity-list-row-action-icon.test.tsx +0 -248
- package/src/app/__tests__/entity-list-row-action-kumiko-actions-view.test.tsx +0 -162
- package/src/app/__tests__/entity-list-row-action-refetch.test.tsx +0 -401
- package/src/app/__tests__/entity-list-screen-padding.test.tsx +0 -128
- package/src/app/__tests__/feature-schema.test.ts +0 -40
- package/src/app/__tests__/form-schema.test.ts +0 -247
- package/src/app/__tests__/list-daterange-facet.test.ts +0 -165
- package/src/app/__tests__/list-filter-facets.test.tsx +0 -521
- package/src/app/__tests__/nav.test.ts +0 -198
- package/src/app/__tests__/projection-detail-actions.test.tsx +0 -719
- package/src/app/__tests__/projection-detail-reference-field.test.tsx +0 -165
- package/src/app/__tests__/projection-detail-shim.test.ts +0 -188
- package/src/app/__tests__/projection-detail-singleton.test.tsx +0 -191
- package/src/app/__tests__/projection-list-bad-shape.test.tsx +0 -152
- package/src/app/__tests__/projection-list-reference-column.test.tsx +0 -219
- package/src/app/__tests__/projection-list-search-sort.test.tsx +0 -212
- package/src/app/__tests__/qualified-names.test.ts +0 -37
- package/src/app/__tests__/return-to.test.ts +0 -396
- package/src/app/__tests__/row-actions-navigate-tab.test.ts +0 -223
- package/src/app/__tests__/row-default-edit-action.test.tsx +0 -324
- package/src/app/__tests__/screen-access-allows.test.ts +0 -34
- package/src/app/__tests__/secret-mint-body.test.tsx +0 -404
- package/src/app/__tests__/variable-chips.test.tsx +0 -70
- package/src/app/app-features-context.tsx +0 -25
- package/src/app/column-renderers.tsx +0 -68
- package/src/app/content-editors.tsx +0 -85
- package/src/app/content-preview.tsx +0 -67
- package/src/app/custom-screens.tsx +0 -29
- package/src/app/dashboard-body.tsx +0 -33
- package/src/app/extension-form-submit.tsx +0 -123
- package/src/app/extension-sections.tsx +0 -111
- package/src/app/form-schema.ts +0 -91
- package/src/app/kumiko-screen.tsx +0 -3801
- package/src/app/layout-fields.ts +0 -31
- package/src/app/list-facets.ts +0 -259
- package/src/app/nav.tsx +0 -258
- package/src/app/projection-detail-shim.ts +0 -86
- package/src/app/reference-facet-bridge.tsx +0 -58
- package/src/app/return-to.tsx +0 -234
- package/src/app/row-actions.ts +0 -640
- package/src/app/screen-access.ts +0 -63
- package/src/app/secret-mint-body.tsx +0 -259
- package/src/app/secrets-edit-body.tsx +0 -194
- package/src/app/use-embedded-screen.ts +0 -27
- package/src/app/variable-chips.tsx +0 -39
- package/src/app/write-failed-error.ts +0 -27
- package/src/components/__tests__/embedded-list-field.test.tsx +0 -531
- package/src/components/__tests__/multi-select-checkboxes.test.tsx +0 -162
- package/src/components/__tests__/related-list-section-empty-state.test.tsx +0 -143
- package/src/components/__tests__/related-list-section.test.tsx +0 -1733
- package/src/components/__tests__/render-edit-action-button.test.tsx +0 -205
- package/src/components/__tests__/render-edit-hidden-section.test.tsx +0 -128
- package/src/components/__tests__/render-edit-logic.test.ts +0 -314
- package/src/components/__tests__/render-edit-screen-description.test.tsx +0 -152
- package/src/components/__tests__/render-edit-submit-actions.test.tsx +0 -460
- package/src/components/__tests__/render-field-app-locale.test.tsx +0 -142
- package/src/components/__tests__/render-field-enum-option.test.tsx +0 -99
- package/src/components/__tests__/render-field-icon-derivation.test.tsx +0 -119
- package/src/components/__tests__/render-field-json-format.test.tsx +0 -164
- package/src/components/__tests__/render-field-money-embedded-list-consistency.test.tsx +0 -212
- package/src/components/__tests__/render-field-money-roundtrip.test.tsx +0 -217
- package/src/components/__tests__/render-field-number-grouping.test.tsx +0 -76
- package/src/components/__tests__/render-field-number-unit.test.tsx +0 -227
- package/src/components/__tests__/render-field-password-format.test.tsx +0 -101
- package/src/components/__tests__/render-field-select-display.test.tsx +0 -134
- package/src/components/__tests__/render-field-unit-format.test.tsx +0 -107
- package/src/components/__tests__/render-field-unsupported-types.test.tsx +0 -195
- package/src/components/__tests__/render-list-toolbar-icon-only.test.tsx +0 -146
- package/src/components/__tests__/write-form-section.test.tsx +0 -375
- package/src/components/embedded-list-field.tsx +0 -354
- package/src/components/grid-cell-for-field.tsx +0 -88
- package/src/components/multi-select-checkboxes.tsx +0 -93
- package/src/components/reference-create-dialog.tsx +0 -91
- package/src/components/related-list-section.tsx +0 -491
- package/src/components/render-edit-action-button.tsx +0 -109
- package/src/components/render-edit-logic.ts +0 -85
- package/src/components/render-edit-types.ts +0 -226
- package/src/components/render-edit.tsx +0 -1871
- package/src/components/render-field.tsx +0 -951
- package/src/components/render-list.tsx +0 -593
- package/src/components/write-form-section.tsx +0 -198
- package/src/context/draft-storage-context.tsx +0 -39
- package/src/hooks/__tests__/use-ai-text.test.tsx +0 -177
- package/src/hooks/__tests__/use-disclosure.test.tsx +0 -25
- package/src/hooks/__tests__/use-form.test.tsx +0 -65
- package/src/hooks/__tests__/use-mutation.test.tsx +0 -77
- package/src/hooks/__tests__/use-query.test.tsx +0 -149
- package/src/hooks/__tests__/use-stream-handler.test.tsx +0 -107
- package/src/hooks/use-ai-text.ts +0 -172
- package/src/hooks/use-disclosure.ts +0 -20
- package/src/hooks/use-form.ts +0 -78
- package/src/hooks/use-list-url-state.ts +0 -207
- package/src/hooks/use-mutation.ts +0 -61
- package/src/hooks/use-query.ts +0 -166
- package/src/hooks/use-reference-lookup.ts +0 -76
- package/src/hooks/use-store.ts +0 -48
- package/src/hooks/use-stream-handler.ts +0 -134
- package/src/i18n-defaults.ts +0 -240
- package/src/i18n.tsx +0 -286
- package/src/index.ts +0 -279
- package/src/primitives.tsx +0 -1567
- package/src/screen-fills-height.ts +0 -3
- package/src/share-links.ts +0 -12
- package/src/sort-by-accessor.ts +0 -20
- package/src/sse/live-events.tsx +0 -56
- package/src/tokens.tsx +0 -170
|
@@ -1,521 +0,0 @@
|
|
|
1
|
-
// fw#2224: ProjectionListBody had neither `filter` nor faceted filters —
|
|
2
|
-
// only EntityListBody did (see kumiko-screen.tsx's buildFilterPayload/
|
|
3
|
-
// buildFilterFacets doc). This proves both directions of the fix: entityList
|
|
4
|
-
// keeps its exact pre-refactor payload/facet behavior after the shared
|
|
5
|
-
// function extraction, and projectionList gains the same capability —
|
|
6
|
-
// screen.filter and screen.facets reach payload.filter/payload.filters,
|
|
7
|
-
// with boolean facets coerced from URL-state strings to real booleans.
|
|
8
|
-
|
|
9
|
-
import { describe, expect, test } from "bun:test";
|
|
10
|
-
import {
|
|
11
|
-
type EntityDefinition,
|
|
12
|
-
type EntityListScreenDefinition,
|
|
13
|
-
type ProjectionListScreenDefinition,
|
|
14
|
-
translationValueOtherText,
|
|
15
|
-
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
16
|
-
import type { Dispatcher } from "@cosmicdrift/kumiko-headless";
|
|
17
|
-
import { act, render, waitFor } from "@testing-library/react";
|
|
18
|
-
import { type ComponentType, type ReactNode, useState } from "react";
|
|
19
|
-
import { DispatcherProvider } from "../../context/dispatcher-context";
|
|
20
|
-
import { createStaticLocaleResolver, LocaleProvider } from "../../i18n";
|
|
21
|
-
import { kumikoDefaultTranslations } from "../../i18n-defaults";
|
|
22
|
-
import { type CorePrimitives, type DataTableProps, PrimitivesProvider } from "../../primitives";
|
|
23
|
-
import type { FeatureSchema } from "../feature-schema";
|
|
24
|
-
import { KumikoScreen } from "../kumiko-screen";
|
|
25
|
-
import type { NavApi } from "../nav";
|
|
26
|
-
import { NavProvider } from "../nav";
|
|
27
|
-
|
|
28
|
-
let capturedProps: DataTableProps | undefined;
|
|
29
|
-
const captureDataTable: ComponentType<DataTableProps> = (props) => {
|
|
30
|
-
capturedProps = props;
|
|
31
|
-
return null;
|
|
32
|
-
};
|
|
33
|
-
// Indirection defeats TS narrowing `capturedProps` to `undefined` at read
|
|
34
|
-
// sites — the compiler can't see that `captureDataTable` (a React render
|
|
35
|
-
// callback) reassigns it between the reset and the read.
|
|
36
|
-
const getCapturedProps = (): DataTableProps | undefined => capturedProps;
|
|
37
|
-
const noop = (): ReactNode => null;
|
|
38
|
-
const passChildren = ({ children }: { readonly children?: ReactNode }): ReactNode => children;
|
|
39
|
-
|
|
40
|
-
const testPrimitives: CorePrimitives = {
|
|
41
|
-
Button: noop,
|
|
42
|
-
Banner: passChildren,
|
|
43
|
-
Field: passChildren,
|
|
44
|
-
Input: noop,
|
|
45
|
-
DataTable: captureDataTable,
|
|
46
|
-
Form: passChildren,
|
|
47
|
-
Section: passChildren,
|
|
48
|
-
Card: passChildren,
|
|
49
|
-
Grid: passChildren,
|
|
50
|
-
GridCell: passChildren,
|
|
51
|
-
Text: passChildren,
|
|
52
|
-
Heading: noop,
|
|
53
|
-
Dialog: noop,
|
|
54
|
-
Modal: noop,
|
|
55
|
-
Lightbox: noop,
|
|
56
|
-
ConfigSourceBadge: noop,
|
|
57
|
-
ConfigCascadeView: noop,
|
|
58
|
-
Link: noop,
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
let queryCalls: Array<{ readonly type: string; readonly payload: unknown }> = [];
|
|
62
|
-
|
|
63
|
-
function stubDispatcher(): Dispatcher {
|
|
64
|
-
return {
|
|
65
|
-
write: (async () => ({ isSuccess: true, data: {} })) as unknown as Dispatcher["write"],
|
|
66
|
-
query: (async (type: string, payload: unknown) => {
|
|
67
|
-
queryCalls.push({ type, payload });
|
|
68
|
-
return { isSuccess: true, data: { rows: [], nextCursor: null } };
|
|
69
|
-
}) as unknown as Dispatcher["query"],
|
|
70
|
-
batch: (async () => ({ isSuccess: true, results: [] })) as unknown as Dispatcher["batch"],
|
|
71
|
-
statusStore: {
|
|
72
|
-
getState: () => "online",
|
|
73
|
-
subscribe: () => () => {},
|
|
74
|
-
} as unknown as Dispatcher["statusStore"],
|
|
75
|
-
async *stream() {},
|
|
76
|
-
pendingWrites: () => [],
|
|
77
|
-
pendingFiles: () => [],
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Stateful nav so setSearchParams (called by useListUrlState.setFilter)
|
|
82
|
-
// actually re-renders the tree — a plain mock object wouldn't trigger React,
|
|
83
|
-
// and toggling a facet would never reach a second query() call.
|
|
84
|
-
function StatefulNav({ children }: { readonly children: ReactNode }): ReactNode {
|
|
85
|
-
const [params, setParams] = useState<Record<string, string>>({});
|
|
86
|
-
const value: NavApi = {
|
|
87
|
-
route: { screenId: "unused" },
|
|
88
|
-
navigate: () => {},
|
|
89
|
-
replace: () => {},
|
|
90
|
-
hrefFor: () => "",
|
|
91
|
-
searchParams: params,
|
|
92
|
-
setSearchParams: (updates) => {
|
|
93
|
-
setParams((prev) => {
|
|
94
|
-
const next = { ...prev };
|
|
95
|
-
for (const [k, v] of Object.entries(updates)) {
|
|
96
|
-
if (v === null) delete next[k];
|
|
97
|
-
else next[k] = v;
|
|
98
|
-
}
|
|
99
|
-
return next;
|
|
100
|
-
});
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
return <NavProvider value={value}>{children}</NavProvider>;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function renderScreen(
|
|
107
|
-
schema: FeatureSchema,
|
|
108
|
-
qn: string,
|
|
109
|
-
dispatcher: Dispatcher = stubDispatcher(),
|
|
110
|
-
timeZone = "UTC",
|
|
111
|
-
): void {
|
|
112
|
-
render(
|
|
113
|
-
<LocaleProvider
|
|
114
|
-
resolver={createStaticLocaleResolver({ locale: "de-DE", timeZone })}
|
|
115
|
-
fallbackBundles={[kumikoDefaultTranslations]}
|
|
116
|
-
>
|
|
117
|
-
<DispatcherProvider dispatcher={dispatcher}>
|
|
118
|
-
<StatefulNav>
|
|
119
|
-
<PrimitivesProvider value={testPrimitives}>
|
|
120
|
-
<KumikoScreen schema={schema} qn={qn} />
|
|
121
|
-
</PrimitivesProvider>
|
|
122
|
-
</StatefulNav>
|
|
123
|
-
</DispatcherProvider>
|
|
124
|
-
</LocaleProvider>,
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function stubDispatcherByType(
|
|
129
|
-
responses: Readonly<Record<string, { rows: readonly Record<string, unknown>[] }>>,
|
|
130
|
-
): Dispatcher {
|
|
131
|
-
return {
|
|
132
|
-
write: (async () => ({ isSuccess: true, data: {} })) as unknown as Dispatcher["write"],
|
|
133
|
-
query: (async (type: string, payload: unknown) => {
|
|
134
|
-
queryCalls.push({ type, payload });
|
|
135
|
-
const rows = responses[type]?.rows ?? [];
|
|
136
|
-
return { isSuccess: true, data: { rows, nextCursor: null } };
|
|
137
|
-
}) as unknown as Dispatcher["query"],
|
|
138
|
-
batch: (async () => ({ isSuccess: true, results: [] })) as unknown as Dispatcher["batch"],
|
|
139
|
-
statusStore: {
|
|
140
|
-
getState: () => "online",
|
|
141
|
-
subscribe: () => () => {},
|
|
142
|
-
} as unknown as Dispatcher["statusStore"],
|
|
143
|
-
async *stream() {},
|
|
144
|
-
pendingWrites: () => [],
|
|
145
|
-
pendingFiles: () => [],
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
describe("entityList facets — unchanged after the shared buildFilterFacets/buildFilterPayload extraction (fw#2224)", () => {
|
|
150
|
-
test("a filterable boolean field renders as a facet, and toggling it sends a coerced boolean in payload.filters", async () => {
|
|
151
|
-
queryCalls = [];
|
|
152
|
-
capturedProps = undefined;
|
|
153
|
-
const entity: EntityDefinition = {
|
|
154
|
-
fields: {
|
|
155
|
-
active: { type: "boolean", filterable: true, required: false },
|
|
156
|
-
},
|
|
157
|
-
};
|
|
158
|
-
const screen: EntityListScreenDefinition = {
|
|
159
|
-
id: "unit-list",
|
|
160
|
-
type: "entityList",
|
|
161
|
-
entity: "unit",
|
|
162
|
-
columns: ["active"],
|
|
163
|
-
};
|
|
164
|
-
const schema: FeatureSchema = {
|
|
165
|
-
featureName: "units",
|
|
166
|
-
entities: { unit: entity },
|
|
167
|
-
screens: [screen],
|
|
168
|
-
} as FeatureSchema;
|
|
169
|
-
|
|
170
|
-
renderScreen(schema, "units:screen:unit-list");
|
|
171
|
-
|
|
172
|
-
await waitFor(() => expect(capturedProps).toBeDefined());
|
|
173
|
-
const props = getCapturedProps();
|
|
174
|
-
if (props === undefined) throw new Error("DataTable was not rendered");
|
|
175
|
-
expect(props.filterFacets).toHaveLength(1);
|
|
176
|
-
expect(props.filterFacets?.[0]?.field).toBe("active");
|
|
177
|
-
expect(props.filterFacets?.[0]?.options).toEqual([
|
|
178
|
-
{ value: "true", label: expect.any(String) },
|
|
179
|
-
{ value: "false", label: expect.any(String) },
|
|
180
|
-
]);
|
|
181
|
-
|
|
182
|
-
const countBeforeToggle = queryCalls.length;
|
|
183
|
-
await act(async () => {
|
|
184
|
-
props.onFilterChange?.("active", ["true"]);
|
|
185
|
-
});
|
|
186
|
-
await waitFor(() => expect(queryCalls.length).toBeGreaterThan(countBeforeToggle));
|
|
187
|
-
const lastPayload = queryCalls[queryCalls.length - 1]?.payload;
|
|
188
|
-
expect(lastPayload).toMatchObject({
|
|
189
|
-
filters: [{ field: "active", op: "in", value: [true] }],
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
describe("projectionList filter + facets (fw#2224)", () => {
|
|
195
|
-
test("screen.filter is sent verbatim in the query payload", async () => {
|
|
196
|
-
queryCalls = [];
|
|
197
|
-
const screen: ProjectionListScreenDefinition = {
|
|
198
|
-
id: "member-list",
|
|
199
|
-
type: "projectionList",
|
|
200
|
-
query: "ledger:query:member:list",
|
|
201
|
-
columns: ["status"],
|
|
202
|
-
filter: { field: "tier", op: "eq", value: "gold" },
|
|
203
|
-
};
|
|
204
|
-
const schema: FeatureSchema = {
|
|
205
|
-
featureName: "ledger",
|
|
206
|
-
entities: {},
|
|
207
|
-
screens: [screen],
|
|
208
|
-
} as FeatureSchema;
|
|
209
|
-
|
|
210
|
-
renderScreen(schema, "ledger:screen:member-list");
|
|
211
|
-
|
|
212
|
-
await waitFor(() => expect(queryCalls.length).toBeGreaterThan(0));
|
|
213
|
-
expect(queryCalls[0]?.payload).toMatchObject({
|
|
214
|
-
filter: { field: "tier", op: "eq", value: "gold" },
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
test("a boolean facet renders with the screen's explicit labels, and toggling it sends a coerced boolean in payload.filters", async () => {
|
|
219
|
-
queryCalls = [];
|
|
220
|
-
capturedProps = undefined;
|
|
221
|
-
const screen: ProjectionListScreenDefinition = {
|
|
222
|
-
id: "member-list",
|
|
223
|
-
type: "projectionList",
|
|
224
|
-
query: "ledger:query:member:list",
|
|
225
|
-
columns: ["active"],
|
|
226
|
-
facets: [
|
|
227
|
-
{
|
|
228
|
-
field: "active",
|
|
229
|
-
type: "boolean",
|
|
230
|
-
label: "Active",
|
|
231
|
-
trueLabel: "Active",
|
|
232
|
-
falseLabel: "Inactive",
|
|
233
|
-
},
|
|
234
|
-
],
|
|
235
|
-
};
|
|
236
|
-
const schema: FeatureSchema = {
|
|
237
|
-
featureName: "ledger",
|
|
238
|
-
entities: {},
|
|
239
|
-
screens: [screen],
|
|
240
|
-
} as FeatureSchema;
|
|
241
|
-
|
|
242
|
-
renderScreen(schema, "ledger:screen:member-list");
|
|
243
|
-
|
|
244
|
-
await waitFor(() => expect(capturedProps).toBeDefined());
|
|
245
|
-
const props = getCapturedProps();
|
|
246
|
-
if (props === undefined) throw new Error("DataTable was not rendered");
|
|
247
|
-
expect(props.filterFacets).toEqual([
|
|
248
|
-
{
|
|
249
|
-
field: "active",
|
|
250
|
-
label: "Active",
|
|
251
|
-
options: [
|
|
252
|
-
{ value: "true", label: "Active" },
|
|
253
|
-
{ value: "false", label: "Inactive" },
|
|
254
|
-
],
|
|
255
|
-
},
|
|
256
|
-
]);
|
|
257
|
-
|
|
258
|
-
const countBeforeToggle = queryCalls.length;
|
|
259
|
-
await act(async () => {
|
|
260
|
-
props.onFilterChange?.("active", ["true"]);
|
|
261
|
-
});
|
|
262
|
-
await waitFor(() => expect(queryCalls.length).toBeGreaterThan(countBeforeToggle));
|
|
263
|
-
const lastPayload = queryCalls[queryCalls.length - 1]?.payload;
|
|
264
|
-
expect(lastPayload).toMatchObject({
|
|
265
|
-
filters: [{ field: "active", op: "in", value: [true] }],
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
// fw#2373: labels without a translation key must pass through unchanged —
|
|
270
|
-
// otherwise shipping translate() alone regresses Members filters to raw keys
|
|
271
|
-
// when locale packs are missing.
|
|
272
|
-
test("projection facet labels without a translation key pass through unchanged", async () => {
|
|
273
|
-
queryCalls = [];
|
|
274
|
-
capturedProps = undefined;
|
|
275
|
-
const screen: ProjectionListScreenDefinition = {
|
|
276
|
-
id: "member-list",
|
|
277
|
-
type: "projectionList",
|
|
278
|
-
query: "ledger:query:member:list",
|
|
279
|
-
columns: ["tier", "active"],
|
|
280
|
-
facets: [
|
|
281
|
-
{
|
|
282
|
-
field: "tier",
|
|
283
|
-
type: "select",
|
|
284
|
-
label: "ledger.member.tier",
|
|
285
|
-
options: [
|
|
286
|
-
{ value: "gold", label: "ledger.member.tier.gold" },
|
|
287
|
-
{ value: "silver", label: "ledger.member.tier.silver" },
|
|
288
|
-
],
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
field: "active",
|
|
292
|
-
type: "boolean",
|
|
293
|
-
label: "ledger.member.active",
|
|
294
|
-
trueLabel: "ledger.member.active.true",
|
|
295
|
-
falseLabel: "ledger.member.active.false",
|
|
296
|
-
},
|
|
297
|
-
],
|
|
298
|
-
};
|
|
299
|
-
const schema: FeatureSchema = {
|
|
300
|
-
featureName: "ledger",
|
|
301
|
-
entities: {},
|
|
302
|
-
screens: [screen],
|
|
303
|
-
} as FeatureSchema;
|
|
304
|
-
|
|
305
|
-
renderScreen(schema, "ledger:screen:member-list");
|
|
306
|
-
|
|
307
|
-
await waitFor(() => expect(capturedProps).toBeDefined());
|
|
308
|
-
const props = getCapturedProps();
|
|
309
|
-
if (props === undefined) throw new Error("DataTable was not rendered");
|
|
310
|
-
expect(props.filterFacets).toEqual([
|
|
311
|
-
{
|
|
312
|
-
field: "tier",
|
|
313
|
-
label: "ledger.member.tier",
|
|
314
|
-
options: [
|
|
315
|
-
{ value: "gold", label: "ledger.member.tier.gold" },
|
|
316
|
-
{ value: "silver", label: "ledger.member.tier.silver" },
|
|
317
|
-
],
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
field: "active",
|
|
321
|
-
label: "ledger.member.active",
|
|
322
|
-
options: [
|
|
323
|
-
{ value: "true", label: "ledger.member.active.true" },
|
|
324
|
-
{ value: "false", label: "ledger.member.active.false" },
|
|
325
|
-
],
|
|
326
|
-
},
|
|
327
|
-
]);
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
test("projection facet labels resolve when a translation key exists", async () => {
|
|
331
|
-
queryCalls = [];
|
|
332
|
-
capturedProps = undefined;
|
|
333
|
-
const screen: ProjectionListScreenDefinition = {
|
|
334
|
-
id: "member-list",
|
|
335
|
-
type: "projectionList",
|
|
336
|
-
query: "ledger:query:member:list",
|
|
337
|
-
columns: ["tier"],
|
|
338
|
-
facets: [
|
|
339
|
-
{
|
|
340
|
-
field: "tier",
|
|
341
|
-
type: "select",
|
|
342
|
-
label: "kumiko.actions.save",
|
|
343
|
-
options: [{ value: "gold", label: "kumiko.actions.cancel" }],
|
|
344
|
-
},
|
|
345
|
-
],
|
|
346
|
-
};
|
|
347
|
-
const schema: FeatureSchema = {
|
|
348
|
-
featureName: "ledger",
|
|
349
|
-
entities: {},
|
|
350
|
-
screens: [screen],
|
|
351
|
-
} as FeatureSchema;
|
|
352
|
-
|
|
353
|
-
renderScreen(schema, "ledger:screen:member-list");
|
|
354
|
-
|
|
355
|
-
await waitFor(() => expect(capturedProps).toBeDefined());
|
|
356
|
-
const props = getCapturedProps();
|
|
357
|
-
if (props === undefined) throw new Error("DataTable was not rendered");
|
|
358
|
-
const facet = props.filterFacets?.[0];
|
|
359
|
-
const de = kumikoDefaultTranslations["de"];
|
|
360
|
-
const en = kumikoDefaultTranslations["en"];
|
|
361
|
-
if (en === undefined) throw new Error("missing en default translations");
|
|
362
|
-
const save = de?.["kumiko.actions.save"] ?? en["kumiko.actions.save"];
|
|
363
|
-
const cancel = de?.["kumiko.actions.cancel"] ?? en["kumiko.actions.cancel"];
|
|
364
|
-
expect(facet?.label).toBe(save === undefined ? undefined : translationValueOtherText(save));
|
|
365
|
-
expect(facet?.options?.[0]?.label).toBe(
|
|
366
|
-
cancel === undefined ? undefined : translationValueOtherText(cancel),
|
|
367
|
-
);
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
test("a reference facet loads its options from the referenced entity's list query, and selecting one sends an id filter", async () => {
|
|
371
|
-
queryCalls = [];
|
|
372
|
-
capturedProps = undefined;
|
|
373
|
-
const dispatcher = stubDispatcherByType({
|
|
374
|
-
"ledger:query:member:list": { rows: [] },
|
|
375
|
-
"ledger:query:tenant:list": {
|
|
376
|
-
rows: [
|
|
377
|
-
{ id: "t1", name: "Acme" },
|
|
378
|
-
{ id: "t2", name: "Globex" },
|
|
379
|
-
],
|
|
380
|
-
},
|
|
381
|
-
});
|
|
382
|
-
const screen: ProjectionListScreenDefinition = {
|
|
383
|
-
id: "member-list",
|
|
384
|
-
type: "projectionList",
|
|
385
|
-
query: "ledger:query:member:list",
|
|
386
|
-
columns: ["tenantId"],
|
|
387
|
-
facets: [
|
|
388
|
-
{
|
|
389
|
-
field: "tenantId",
|
|
390
|
-
type: "reference",
|
|
391
|
-
label: "Tenant",
|
|
392
|
-
entity: "tenant",
|
|
393
|
-
labelField: "name",
|
|
394
|
-
},
|
|
395
|
-
],
|
|
396
|
-
};
|
|
397
|
-
const schema: FeatureSchema = {
|
|
398
|
-
featureName: "ledger",
|
|
399
|
-
entities: {},
|
|
400
|
-
screens: [screen],
|
|
401
|
-
} as FeatureSchema;
|
|
402
|
-
|
|
403
|
-
renderScreen(schema, "ledger:screen:member-list", dispatcher);
|
|
404
|
-
|
|
405
|
-
await waitFor(() =>
|
|
406
|
-
expect(queryCalls.some((c) => c.type === "ledger:query:tenant:list")).toBe(true),
|
|
407
|
-
);
|
|
408
|
-
await waitFor(() => expect(getCapturedProps()?.filterFacets?.[0]?.options).toHaveLength(2));
|
|
409
|
-
const props = getCapturedProps();
|
|
410
|
-
if (props === undefined) throw new Error("DataTable was not rendered");
|
|
411
|
-
expect(props.filterFacets).toEqual([
|
|
412
|
-
{
|
|
413
|
-
field: "tenantId",
|
|
414
|
-
label: "Tenant",
|
|
415
|
-
options: [
|
|
416
|
-
{ value: "t1", label: "Acme" },
|
|
417
|
-
{ value: "t2", label: "Globex" },
|
|
418
|
-
],
|
|
419
|
-
},
|
|
420
|
-
]);
|
|
421
|
-
|
|
422
|
-
const countBeforeSelect = queryCalls.length;
|
|
423
|
-
await act(async () => {
|
|
424
|
-
props.onFilterChange?.("tenantId", ["t1"]);
|
|
425
|
-
});
|
|
426
|
-
await waitFor(() => expect(queryCalls.length).toBeGreaterThan(countBeforeSelect));
|
|
427
|
-
const lastPayload = queryCalls[queryCalls.length - 1]?.payload;
|
|
428
|
-
expect(lastPayload).toMatchObject({
|
|
429
|
-
filters: [{ field: "tenantId", op: "in", value: ["t1"] }],
|
|
430
|
-
});
|
|
431
|
-
});
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
// fw#3104: a dateRange facet is the first facet that does NOT travel in
|
|
435
|
-
// payload.filters — its two bounds go out as the top-level params the screen
|
|
436
|
-
// declares. A screen may declare it as its only facet, so the wiring must not
|
|
437
|
-
// hang off the option-facet path.
|
|
438
|
-
describe("projectionList dateRange facet (fw#3104)", () => {
|
|
439
|
-
const dateRangeScreen: ProjectionListScreenDefinition = {
|
|
440
|
-
id: "event-list",
|
|
441
|
-
type: "projectionList",
|
|
442
|
-
query: "ledger:query:event:list",
|
|
443
|
-
columns: ["createdAt"],
|
|
444
|
-
facets: [
|
|
445
|
-
{
|
|
446
|
-
field: "createdAt",
|
|
447
|
-
type: "dateRange",
|
|
448
|
-
label: "When",
|
|
449
|
-
params: { from: "from", to: "to" },
|
|
450
|
-
},
|
|
451
|
-
],
|
|
452
|
-
};
|
|
453
|
-
const schema: FeatureSchema = {
|
|
454
|
-
featureName: "ledger",
|
|
455
|
-
entities: {},
|
|
456
|
-
screens: [dateRangeScreen],
|
|
457
|
-
} as FeatureSchema;
|
|
458
|
-
|
|
459
|
-
test("reaches the DataTable as a dateRange facet, not as a filter dropdown", async () => {
|
|
460
|
-
queryCalls = [];
|
|
461
|
-
capturedProps = undefined;
|
|
462
|
-
renderScreen(schema, "ledger:screen:event-list");
|
|
463
|
-
|
|
464
|
-
await waitFor(() => expect(capturedProps).toBeDefined());
|
|
465
|
-
const props = getCapturedProps();
|
|
466
|
-
if (props === undefined) throw new Error("DataTable was not rendered");
|
|
467
|
-
expect(props.dateRangeFacets).toEqual([
|
|
468
|
-
{ field: "createdAt", label: "When", from: "", to: "" },
|
|
469
|
-
]);
|
|
470
|
-
expect(props.filterFacets).toBeUndefined();
|
|
471
|
-
// Reset still has to be reachable on a dateRange-only screen.
|
|
472
|
-
expect(props.onFilterReset).toBeDefined();
|
|
473
|
-
expect(queryCalls[0]?.payload).not.toHaveProperty("from");
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
test("picking a bound sends it as the declared query param, in the viewer's zone", async () => {
|
|
477
|
-
queryCalls = [];
|
|
478
|
-
capturedProps = undefined;
|
|
479
|
-
renderScreen(schema, "ledger:screen:event-list", stubDispatcher(), "Europe/Vienna");
|
|
480
|
-
|
|
481
|
-
await waitFor(() => expect(capturedProps).toBeDefined());
|
|
482
|
-
const before = queryCalls.length;
|
|
483
|
-
await act(async () => {
|
|
484
|
-
getCapturedProps()?.onDateRangeChange?.("createdAt", "to", "2020-06-14");
|
|
485
|
-
});
|
|
486
|
-
await waitFor(() => expect(queryCalls.length).toBeGreaterThan(before));
|
|
487
|
-
const payload = queryCalls[queryCalls.length - 1]?.payload;
|
|
488
|
-
// Open interval: only the bound the user picked travels.
|
|
489
|
-
expect(payload).toMatchObject({ to: "2020-06-14T21:59:59.999999999Z" });
|
|
490
|
-
expect(payload).not.toHaveProperty("from");
|
|
491
|
-
expect(payload).not.toHaveProperty("filters");
|
|
492
|
-
expect(getCapturedProps()?.dateRangeFacets).toEqual([
|
|
493
|
-
{ field: "createdAt", label: "When", from: "", to: "2020-06-14" },
|
|
494
|
-
]);
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
test("a `from` past the current `to` is clamped before it can reach the handler's refine", async () => {
|
|
498
|
-
queryCalls = [];
|
|
499
|
-
capturedProps = undefined;
|
|
500
|
-
renderScreen(schema, "ledger:screen:event-list", stubDispatcher(), "Europe/Vienna");
|
|
501
|
-
|
|
502
|
-
await waitFor(() => expect(capturedProps).toBeDefined());
|
|
503
|
-
await act(async () => {
|
|
504
|
-
getCapturedProps()?.onDateRangeChange?.("createdAt", "to", "2020-06-14");
|
|
505
|
-
});
|
|
506
|
-
await act(async () => {
|
|
507
|
-
getCapturedProps()?.onDateRangeChange?.("createdAt", "from", "2020-06-20");
|
|
508
|
-
});
|
|
509
|
-
await waitFor(() =>
|
|
510
|
-
expect(getCapturedProps()?.dateRangeFacets).toEqual([
|
|
511
|
-
{ field: "createdAt", label: "When", from: "2020-06-20", to: "2020-06-20" },
|
|
512
|
-
]),
|
|
513
|
-
);
|
|
514
|
-
const payload = queryCalls[queryCalls.length - 1]?.payload as Record<string, string>;
|
|
515
|
-
const from = payload["from"];
|
|
516
|
-
const to = payload["to"];
|
|
517
|
-
expect(from).toBeString();
|
|
518
|
-
expect(to).toBeString();
|
|
519
|
-
expect((from ?? "") <= (to ?? "")).toBe(true);
|
|
520
|
-
});
|
|
521
|
-
});
|