@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,375 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import type {
|
|
3
|
-
EditWriteFormSection,
|
|
4
|
-
EntityDefinition,
|
|
5
|
-
EntityEditScreenDefinition,
|
|
6
|
-
} from "@cosmicdrift/kumiko-framework/ui-types";
|
|
7
|
-
import {
|
|
8
|
-
computeEditViewModel,
|
|
9
|
-
type Dispatcher,
|
|
10
|
-
type EditWriteFormSectionViewModel,
|
|
11
|
-
} from "@cosmicdrift/kumiko-headless";
|
|
12
|
-
import { fireEvent, render, screen as rtlScreen, waitFor } from "@testing-library/react";
|
|
13
|
-
import type { ComponentType, ReactNode } from "react";
|
|
14
|
-
import { DispatcherProvider } from "../../context/dispatcher-context";
|
|
15
|
-
import { createStaticLocaleResolver, LocaleProvider, type TranslationsByLocale } from "../../i18n";
|
|
16
|
-
import { kumikoDefaultTranslations } from "../../i18n-defaults";
|
|
17
|
-
import {
|
|
18
|
-
type BannerProps,
|
|
19
|
-
type ButtonProps,
|
|
20
|
-
type CardProps,
|
|
21
|
-
type CorePrimitives,
|
|
22
|
-
PrimitivesProvider,
|
|
23
|
-
type SectionProps,
|
|
24
|
-
} from "../../primitives";
|
|
25
|
-
import { WriteFormSection } from "../write-form-section";
|
|
26
|
-
|
|
27
|
-
const passChildren = ({ children }: { readonly children?: ReactNode }): ReactNode => children;
|
|
28
|
-
const noop = () => {};
|
|
29
|
-
|
|
30
|
-
const testButton: ComponentType<ButtonProps> = ({ children, onClick, testId, disabled, icon }) => (
|
|
31
|
-
<button type="button" data-testid={testId} data-icon={icon} onClick={onClick} disabled={disabled}>
|
|
32
|
-
{children}
|
|
33
|
-
</button>
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
const testInput: ComponentType<{
|
|
37
|
-
name?: string;
|
|
38
|
-
value?: unknown;
|
|
39
|
-
onChange?: (v: unknown) => void;
|
|
40
|
-
}> = ({ name = "field", value, onChange }) => (
|
|
41
|
-
<input
|
|
42
|
-
aria-label={name}
|
|
43
|
-
data-testid={`input-${name}`}
|
|
44
|
-
value={typeof value === "string" ? value : ""}
|
|
45
|
-
onChange={(e) => onChange?.(e.target.value)}
|
|
46
|
-
/>
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
const testBanner: ComponentType<BannerProps> = ({ children, testId }) => (
|
|
50
|
-
<div data-testid={testId}>{children}</div>
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
// Mirrors DefaultSection's real actions slot closely enough to let tests
|
|
54
|
-
// assert the submit button lands in the footer, not the body (fw#2675).
|
|
55
|
-
// Also renders subtitle into the DOM (like DefaultSection's own subtitle
|
|
56
|
-
// slot) so description-passthrough tests prove real render output, not just
|
|
57
|
-
// a captured prop.
|
|
58
|
-
const testSection: ComponentType<SectionProps> = ({ testId, subtitle, children, actions }) => (
|
|
59
|
-
<div data-testid={testId}>
|
|
60
|
-
{subtitle !== undefined && (
|
|
61
|
-
<p data-testid={testId !== undefined ? `${testId}-subtitle` : undefined}>{subtitle}</p>
|
|
62
|
-
)}
|
|
63
|
-
<div data-testid={testId !== undefined ? `${testId}-body` : undefined}>{children}</div>
|
|
64
|
-
{actions !== undefined && (
|
|
65
|
-
<div data-testid={testId !== undefined ? `${testId}-actions` : undefined}>{actions}</div>
|
|
66
|
-
)}
|
|
67
|
-
</div>
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
// Mirrors DefaultCard's slots (title/subtitle/headerActions) closely enough
|
|
71
|
-
// to distinguish "framed via Card" from "framed via Section" in tests —
|
|
72
|
-
// tabs mode (hideTitle) uses Card because Section flattens inside this
|
|
73
|
-
// component's host Form.
|
|
74
|
-
const testCard: ComponentType<CardProps> = ({ testId, slots, children }) => (
|
|
75
|
-
<div data-testid={testId}>
|
|
76
|
-
{slots?.title !== undefined && (
|
|
77
|
-
<h3 data-testid={testId !== undefined ? `${testId}-title` : undefined}>{slots.title}</h3>
|
|
78
|
-
)}
|
|
79
|
-
{slots?.subtitle !== undefined && (
|
|
80
|
-
<p data-testid={testId !== undefined ? `${testId}-subtitle` : undefined}>{slots.subtitle}</p>
|
|
81
|
-
)}
|
|
82
|
-
<div data-testid={testId !== undefined ? `${testId}-body` : undefined}>{children}</div>
|
|
83
|
-
{slots?.headerActions !== undefined && (
|
|
84
|
-
<div data-testid={testId !== undefined ? `${testId}-actions` : undefined}>
|
|
85
|
-
{slots.headerActions}
|
|
86
|
-
</div>
|
|
87
|
-
)}
|
|
88
|
-
</div>
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
function testPrimitives(): CorePrimitives {
|
|
92
|
-
return {
|
|
93
|
-
Button: testButton,
|
|
94
|
-
Banner: testBanner,
|
|
95
|
-
Field: passChildren,
|
|
96
|
-
Input: testInput,
|
|
97
|
-
DataTable: noop,
|
|
98
|
-
Form: noop,
|
|
99
|
-
Section: testSection,
|
|
100
|
-
Card: testCard,
|
|
101
|
-
Grid: passChildren,
|
|
102
|
-
GridCell: passChildren,
|
|
103
|
-
Text: noop,
|
|
104
|
-
Heading: noop,
|
|
105
|
-
Dialog: noop,
|
|
106
|
-
Modal: noop,
|
|
107
|
-
Lightbox: noop,
|
|
108
|
-
ConfigSourceBadge: noop,
|
|
109
|
-
ConfigCascadeView: noop,
|
|
110
|
-
Link: noop,
|
|
111
|
-
} as unknown as CorePrimitives;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function stubDispatcher(writeImpl?: Dispatcher["write"]): {
|
|
115
|
-
dispatcher: Dispatcher;
|
|
116
|
-
writes: Array<{ type: string; payload: unknown }>;
|
|
117
|
-
} {
|
|
118
|
-
const writes: Array<{ type: string; payload: unknown }> = [];
|
|
119
|
-
const dispatcher: Dispatcher = {
|
|
120
|
-
write: (async (type, payload) => {
|
|
121
|
-
writes.push({ type, payload });
|
|
122
|
-
if (writeImpl) return writeImpl(type, payload);
|
|
123
|
-
return { isSuccess: true, data: { id: "n1" } };
|
|
124
|
-
}) as Dispatcher["write"],
|
|
125
|
-
query: (async () => ({ isSuccess: true, data: {} })) as Dispatcher["query"],
|
|
126
|
-
batch: (async () => ({ isSuccess: true, results: [] })) as Dispatcher["batch"],
|
|
127
|
-
statusStore: {
|
|
128
|
-
getState: () => "online",
|
|
129
|
-
subscribe: () => () => {},
|
|
130
|
-
} as unknown as Dispatcher["statusStore"],
|
|
131
|
-
async *stream() {},
|
|
132
|
-
pendingWrites: () => [],
|
|
133
|
-
pendingFiles: () => [],
|
|
134
|
-
};
|
|
135
|
-
return { dispatcher, writes };
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const noteSection: EditWriteFormSectionViewModel = {
|
|
139
|
-
kind: "writeForm",
|
|
140
|
-
title: "Add note",
|
|
141
|
-
columns: 1,
|
|
142
|
-
handler: "orders:write:add-note",
|
|
143
|
-
fields: [
|
|
144
|
-
{
|
|
145
|
-
field: "note",
|
|
146
|
-
label: "Note",
|
|
147
|
-
type: "text",
|
|
148
|
-
value: "",
|
|
149
|
-
visible: true,
|
|
150
|
-
readOnly: false,
|
|
151
|
-
required: true,
|
|
152
|
-
},
|
|
153
|
-
],
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
function renderWriteForm(
|
|
157
|
-
section: EditWriteFormSectionViewModel,
|
|
158
|
-
dispatcher: Dispatcher,
|
|
159
|
-
onSubmitted: () => void,
|
|
160
|
-
extraTranslations?: TranslationsByLocale,
|
|
161
|
-
) {
|
|
162
|
-
return render(
|
|
163
|
-
<LocaleProvider
|
|
164
|
-
resolver={createStaticLocaleResolver({ locale: "en-US" })}
|
|
165
|
-
fallbackBundles={
|
|
166
|
-
extraTranslations !== undefined
|
|
167
|
-
? [extraTranslations, kumikoDefaultTranslations]
|
|
168
|
-
: [kumikoDefaultTranslations]
|
|
169
|
-
}
|
|
170
|
-
>
|
|
171
|
-
<DispatcherProvider dispatcher={dispatcher}>
|
|
172
|
-
<PrimitivesProvider value={testPrimitives()}>
|
|
173
|
-
<WriteFormSection section={section} featureName="orders" onSubmitted={onSubmitted} />
|
|
174
|
-
</PrimitivesProvider>
|
|
175
|
-
</DispatcherProvider>
|
|
176
|
-
</LocaleProvider>,
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
describe("WriteFormSection", () => {
|
|
181
|
-
test("submit button renders in the section's actions footer, not the body (fw#2675)", () => {
|
|
182
|
-
const { dispatcher } = stubDispatcher();
|
|
183
|
-
renderWriteForm(noteSection, dispatcher, noop);
|
|
184
|
-
|
|
185
|
-
const actions = rtlScreen.getByTestId("write-form-Add note-actions");
|
|
186
|
-
const body = rtlScreen.getByTestId("write-form-Add note-body");
|
|
187
|
-
const button = rtlScreen.getByTestId("write-form-section-submit");
|
|
188
|
-
|
|
189
|
-
expect(actions.contains(button)).toBe(true);
|
|
190
|
-
expect(body.contains(button)).toBe(false);
|
|
191
|
-
expect(button.dataset["icon"]).toBeUndefined();
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test("submit dispatches through the section's configured write handler with the entered values", async () => {
|
|
195
|
-
const { dispatcher, writes } = stubDispatcher();
|
|
196
|
-
let submittedCount = 0;
|
|
197
|
-
renderWriteForm(noteSection, dispatcher, () => {
|
|
198
|
-
submittedCount += 1;
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
fireEvent.change(rtlScreen.getByLabelText("note"), { target: { value: "Called back" } });
|
|
202
|
-
fireEvent.click(rtlScreen.getByTestId("write-form-section-submit"));
|
|
203
|
-
|
|
204
|
-
await waitFor(() => expect(submittedCount).toBe(1));
|
|
205
|
-
expect(writes).toEqual([{ type: "orders:write:add-note", payload: { note: "Called back" } }]);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
test("a failed write surfaces the error banner and does not call onSubmitted", async () => {
|
|
209
|
-
const { dispatcher } = stubDispatcher(async () => ({
|
|
210
|
-
isSuccess: false,
|
|
211
|
-
error: { code: "conflict", httpStatus: 409, i18nKey: "errors.conflict", message: "conflict" },
|
|
212
|
-
}));
|
|
213
|
-
let submittedCount = 0;
|
|
214
|
-
renderWriteForm(noteSection, dispatcher, () => {
|
|
215
|
-
submittedCount += 1;
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
fireEvent.change(rtlScreen.getByLabelText("note"), { target: { value: "x" } });
|
|
219
|
-
fireEvent.click(rtlScreen.getByTestId("write-form-section-submit"));
|
|
220
|
-
|
|
221
|
-
await waitFor(() => expect(rtlScreen.getByTestId("write-form-section-error")).toBeTruthy());
|
|
222
|
-
expect(submittedCount).toBe(0);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
test("submit is blocked by schema validation when a required field is left empty", async () => {
|
|
226
|
-
const { dispatcher, writes } = stubDispatcher();
|
|
227
|
-
renderWriteForm(noteSection, dispatcher, () => {
|
|
228
|
-
throw new Error("must not submit while required field is empty");
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
fireEvent.click(rtlScreen.getByTestId("write-form-section-submit"));
|
|
232
|
-
|
|
233
|
-
await waitFor(() => expect(writes).toHaveLength(0));
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
// Pins the only route a writeForm section has to thread the host record's
|
|
237
|
-
// id into its payload (see EditWriteFormSection.handler's doc): a
|
|
238
|
-
// visible:false field never renders an input, but its resolved value still
|
|
239
|
-
// seeds `initial` and so rides along in the submit payload untouched.
|
|
240
|
-
test("a visible:false field's prefilled value rides along in the submit payload", async () => {
|
|
241
|
-
const sectionWithHiddenId: EditWriteFormSectionViewModel = {
|
|
242
|
-
...noteSection,
|
|
243
|
-
fields: [
|
|
244
|
-
{
|
|
245
|
-
field: "orderId",
|
|
246
|
-
label: "Order",
|
|
247
|
-
type: "text",
|
|
248
|
-
value: "order-42",
|
|
249
|
-
visible: false,
|
|
250
|
-
readOnly: false,
|
|
251
|
-
required: false,
|
|
252
|
-
},
|
|
253
|
-
...noteSection.fields,
|
|
254
|
-
],
|
|
255
|
-
};
|
|
256
|
-
const { dispatcher, writes } = stubDispatcher();
|
|
257
|
-
renderWriteForm(sectionWithHiddenId, dispatcher, noop);
|
|
258
|
-
|
|
259
|
-
expect(rtlScreen.queryByTestId("input-orderId")).toBeNull();
|
|
260
|
-
|
|
261
|
-
fireEvent.change(rtlScreen.getByLabelText("note"), { target: { value: "hi" } });
|
|
262
|
-
fireEvent.click(rtlScreen.getByTestId("write-form-section-submit"));
|
|
263
|
-
|
|
264
|
-
await waitFor(() => expect(writes).toHaveLength(1));
|
|
265
|
-
expect(writes[0]?.payload).toEqual({ orderId: "order-42", note: "hi" });
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
// Tabs mode (hideTitle) frames through Card instead of Section — Section
|
|
270
|
-
// flattens to a borderless divider inside the host RenderEdit's own Form,
|
|
271
|
-
// which would otherwise leave the submit button with no card chrome.
|
|
272
|
-
describe("WriteFormSection — tabs mode (hideTitle)", () => {
|
|
273
|
-
test("renders via Card, no title text, submit button lands in headerActions", () => {
|
|
274
|
-
const { dispatcher } = stubDispatcher();
|
|
275
|
-
render(
|
|
276
|
-
<LocaleProvider
|
|
277
|
-
resolver={createStaticLocaleResolver({ locale: "en-US" })}
|
|
278
|
-
fallbackBundles={[kumikoDefaultTranslations]}
|
|
279
|
-
>
|
|
280
|
-
<DispatcherProvider dispatcher={dispatcher}>
|
|
281
|
-
<PrimitivesProvider value={testPrimitives()}>
|
|
282
|
-
<WriteFormSection
|
|
283
|
-
section={noteSection}
|
|
284
|
-
featureName="orders"
|
|
285
|
-
onSubmitted={noop}
|
|
286
|
-
hideTitle
|
|
287
|
-
/>
|
|
288
|
-
</PrimitivesProvider>
|
|
289
|
-
</DispatcherProvider>
|
|
290
|
-
</LocaleProvider>,
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
expect(rtlScreen.queryByTestId("write-form-Add note-title")).toBeNull();
|
|
294
|
-
const actions = rtlScreen.getByTestId("write-form-Add note-actions");
|
|
295
|
-
const button = rtlScreen.getByTestId("write-form-section-submit");
|
|
296
|
-
expect(actions.contains(button)).toBe(true);
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
// section.description exists on EditWriteFormSection to make a self-service
|
|
301
|
-
// write form usable without training (same doc-promise as EditFieldsSection),
|
|
302
|
-
// but WriteFormSection never read it — the ViewModel translated it correctly,
|
|
303
|
-
// the renderer just dropped it on the floor. Built through the real
|
|
304
|
-
// computeEditViewModel pipeline (not a hand-built ViewModel) to prove the
|
|
305
|
-
// full Spec -> ViewModel -> Renderer path, matching render-edit-screen-
|
|
306
|
-
// description.test.tsx's approach for the sibling entityEdit-level bug.
|
|
307
|
-
describe("WriteFormSection — section.description as subtitle", () => {
|
|
308
|
-
function computeNoteSection(
|
|
309
|
-
descriptionSpec: string,
|
|
310
|
-
translate: (key: string) => string,
|
|
311
|
-
): EditWriteFormSectionViewModel {
|
|
312
|
-
const sectionSpec: EditWriteFormSection = {
|
|
313
|
-
kind: "writeForm",
|
|
314
|
-
title: "Add note",
|
|
315
|
-
description: descriptionSpec,
|
|
316
|
-
columns: 1,
|
|
317
|
-
handler: "orders:write:add-note",
|
|
318
|
-
fieldDefs: {
|
|
319
|
-
note: {
|
|
320
|
-
type: "text",
|
|
321
|
-
maxLength: 500,
|
|
322
|
-
required: true,
|
|
323
|
-
searchable: false,
|
|
324
|
-
sortable: false,
|
|
325
|
-
},
|
|
326
|
-
},
|
|
327
|
-
fields: ["note"],
|
|
328
|
-
};
|
|
329
|
-
const screen: EntityEditScreenDefinition = {
|
|
330
|
-
id: "order-detail",
|
|
331
|
-
type: "entityEdit",
|
|
332
|
-
entity: "order",
|
|
333
|
-
layout: { sections: [sectionSpec] },
|
|
334
|
-
};
|
|
335
|
-
const entity: EntityDefinition = { fields: {} };
|
|
336
|
-
const viewModel = computeEditViewModel({
|
|
337
|
-
screen,
|
|
338
|
-
entity,
|
|
339
|
-
values: {},
|
|
340
|
-
translate,
|
|
341
|
-
featureName: "orders",
|
|
342
|
-
});
|
|
343
|
-
const section = viewModel.sections[0];
|
|
344
|
-
if (section?.kind !== "writeForm") throw new Error("expected a writeForm section");
|
|
345
|
-
return section;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
test("a plain-text description renders as the section's subtitle", () => {
|
|
349
|
-
const { dispatcher } = stubDispatcher();
|
|
350
|
-
const section = computeNoteSection("Explain why you're adding this note.", (key) => key);
|
|
351
|
-
|
|
352
|
-
renderWriteForm(section, dispatcher, noop);
|
|
353
|
-
|
|
354
|
-
expect(rtlScreen.getByTestId("write-form-Add note-subtitle").textContent).toBe(
|
|
355
|
-
"Explain why you're adding this note.",
|
|
356
|
-
);
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
test("a description that is a known i18n key renders translated, not as the raw key", () => {
|
|
360
|
-
const { dispatcher } = stubDispatcher();
|
|
361
|
-
const translations: Record<string, string> = {
|
|
362
|
-
"orders:write-form.explainer": "Notes are visible to the whole team.",
|
|
363
|
-
};
|
|
364
|
-
const section = computeNoteSection(
|
|
365
|
-
"orders:write-form.explainer",
|
|
366
|
-
(key) => translations[key] ?? key,
|
|
367
|
-
);
|
|
368
|
-
|
|
369
|
-
renderWriteForm(section, dispatcher, noop);
|
|
370
|
-
|
|
371
|
-
const subtitle = rtlScreen.getByTestId("write-form-Add note-subtitle");
|
|
372
|
-
expect(subtitle.textContent).toBe("Notes are visible to the whole team.");
|
|
373
|
-
expect(subtitle.textContent).not.toBe("orders:write-form.explainer");
|
|
374
|
-
});
|
|
375
|
-
});
|