@multiplatform.one/frappe-ui 7.6.3 → 7.7.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/cjs/ChartCard.cjs +52 -0
- package/dist/cjs/ChartCard.native.js +72 -0
- package/dist/cjs/ChartCard.native.js.map +1 -0
- package/dist/cjs/FrappeChartCard.cjs +3 -3
- package/dist/cjs/FrappeChartCard.native.js +3 -3
- package/dist/cjs/FrappeChartCard.native.js.map +1 -1
- package/dist/cjs/FrappeDashboard.cjs +6 -27
- package/dist/cjs/FrappeDashboard.native.js +6 -33
- package/dist/cjs/FrappeDashboard.native.js.map +1 -1
- package/dist/cjs/fields/CodeEditor/index.cjs +4 -0
- package/dist/cjs/fields/CodeEditor/index.native.js +4 -0
- package/dist/cjs/fields/CodeEditor/index.native.js.map +1 -1
- package/dist/cjs/formEngine/createPrefill.cjs +37 -0
- package/dist/cjs/formEngine/createPrefill.native.js +40 -0
- package/dist/cjs/formEngine/createPrefill.native.js.map +1 -0
- package/dist/cjs/formEngine/dirtyTracking.cjs +55 -0
- package/dist/cjs/formEngine/dirtyTracking.native.js +77 -0
- package/dist/cjs/formEngine/dirtyTracking.native.js.map +1 -0
- package/dist/cjs/formEngine/doctypeMeta.cjs +18 -0
- package/dist/cjs/formEngine/doctypeMeta.native.js +21 -0
- package/dist/cjs/formEngine/doctypeMeta.native.js.map +1 -0
- package/dist/cjs/formEngine/fetchFrom.cjs +67 -0
- package/dist/cjs/formEngine/fetchFrom.native.js +110 -0
- package/dist/cjs/formEngine/fetchFrom.native.js.map +1 -0
- package/dist/cjs/formEngine/fieldPermissions.cjs +55 -0
- package/dist/cjs/formEngine/fieldPermissions.native.js +80 -0
- package/dist/cjs/formEngine/fieldPermissions.native.js.map +1 -0
- package/dist/cjs/formEngine/formActions.cjs +189 -0
- package/dist/cjs/formEngine/formActions.native.js +199 -0
- package/dist/cjs/formEngine/formActions.native.js.map +1 -0
- package/dist/cjs/formEngine/formLayout.cjs +245 -0
- package/dist/cjs/formEngine/formLayout.native.js +388 -0
- package/dist/cjs/formEngine/formLayout.native.js.map +1 -0
- package/dist/cjs/formEngine/index.cjs +48 -0
- package/dist/cjs/formEngine/index.native.js +51 -0
- package/dist/cjs/formEngine/index.native.js.map +1 -0
- package/dist/cjs/formEngine/useFormEngine.cjs +387 -0
- package/dist/cjs/formEngine/useFormEngine.native.js +476 -0
- package/dist/cjs/formEngine/useFormEngine.native.js.map +1 -0
- package/dist/cjs/index.cjs +19 -1
- package/dist/cjs/index.native.js +19 -1
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/esm/ChartCard.mjs +27 -0
- package/dist/esm/ChartCard.mjs.map +1 -0
- package/dist/esm/ChartCard.native.js +33 -0
- package/dist/esm/ChartCard.native.js.map +1 -0
- package/dist/esm/FrappeChartCard.mjs +1 -1
- package/dist/esm/FrappeChartCard.native.js +1 -1
- package/dist/esm/FrappeChartCard.native.js.map +1 -1
- package/dist/esm/FrappeDashboard.mjs +6 -27
- package/dist/esm/FrappeDashboard.mjs.map +1 -1
- package/dist/esm/FrappeDashboard.native.js +6 -33
- package/dist/esm/FrappeDashboard.native.js.map +1 -1
- package/dist/esm/fields/CodeEditor/index.mjs +4 -0
- package/dist/esm/fields/CodeEditor/index.mjs.map +1 -1
- package/dist/esm/fields/CodeEditor/index.native.js +4 -0
- package/dist/esm/fields/CodeEditor/index.native.js.map +1 -1
- package/dist/esm/formEngine/createPrefill.mjs +11 -0
- package/dist/esm/formEngine/createPrefill.mjs.map +1 -0
- package/dist/esm/formEngine/createPrefill.native.js +11 -0
- package/dist/esm/formEngine/createPrefill.native.js.map +1 -0
- package/dist/esm/formEngine/dirtyTracking.mjs +28 -0
- package/dist/esm/formEngine/dirtyTracking.mjs.map +1 -0
- package/dist/esm/formEngine/dirtyTracking.native.js +47 -0
- package/dist/esm/formEngine/dirtyTracking.native.js.map +1 -0
- package/dist/esm/formEngine/doctypeMeta.mjs +2 -0
- package/dist/esm/formEngine/doctypeMeta.mjs.map +1 -0
- package/dist/esm/formEngine/doctypeMeta.native.js +2 -0
- package/dist/esm/formEngine/doctypeMeta.native.js.map +1 -0
- package/dist/esm/formEngine/fetchFrom.mjs +40 -0
- package/dist/esm/formEngine/fetchFrom.mjs.map +1 -0
- package/dist/esm/formEngine/fetchFrom.native.js +80 -0
- package/dist/esm/formEngine/fetchFrom.native.js.map +1 -0
- package/dist/esm/formEngine/fieldPermissions.mjs +28 -0
- package/dist/esm/formEngine/fieldPermissions.mjs.map +1 -0
- package/dist/esm/formEngine/fieldPermissions.native.js +50 -0
- package/dist/esm/formEngine/fieldPermissions.native.js.map +1 -0
- package/dist/esm/formEngine/formActions.mjs +146 -0
- package/dist/esm/formEngine/formActions.mjs.map +1 -0
- package/dist/esm/formEngine/formActions.native.js +153 -0
- package/dist/esm/formEngine/formActions.native.js.map +1 -0
- package/dist/esm/formEngine/formLayout.mjs +217 -0
- package/dist/esm/formEngine/formLayout.mjs.map +1 -0
- package/dist/esm/formEngine/formLayout.native.js +357 -0
- package/dist/esm/formEngine/formLayout.native.js.map +1 -0
- package/dist/esm/formEngine/index.mjs +8 -0
- package/dist/esm/formEngine/index.mjs.map +1 -0
- package/dist/esm/formEngine/index.native.js +8 -0
- package/dist/esm/formEngine/index.native.js.map +1 -0
- package/dist/esm/formEngine/useFormEngine.mjs +362 -0
- package/dist/esm/formEngine/useFormEngine.mjs.map +1 -0
- package/dist/esm/formEngine/useFormEngine.native.js +448 -0
- package/dist/esm/formEngine/useFormEngine.native.js.map +1 -0
- package/dist/esm/index.mjs +4 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +4 -2
- package/dist/esm/index.native.js.map +1 -1
- package/dist/jsx/ChartCard.mjs +27 -0
- package/dist/jsx/ChartCard.mjs.map +1 -0
- package/dist/jsx/ChartCard.native.js +72 -0
- package/dist/jsx/ChartCard.native.js.map +1 -0
- package/dist/jsx/FrappeChartCard.mjs +1 -1
- package/dist/jsx/FrappeChartCard.native.js +3 -3
- package/dist/jsx/FrappeChartCard.native.js.map +1 -1
- package/dist/jsx/FrappeDashboard.mjs +6 -27
- package/dist/jsx/FrappeDashboard.mjs.map +1 -1
- package/dist/jsx/FrappeDashboard.native.js +6 -33
- package/dist/jsx/FrappeDashboard.native.js.map +1 -1
- package/dist/jsx/fields/CodeEditor/index.mjs +4 -0
- package/dist/jsx/fields/CodeEditor/index.mjs.map +1 -1
- package/dist/jsx/fields/CodeEditor/index.native.js +4 -0
- package/dist/jsx/fields/CodeEditor/index.native.js.map +1 -1
- package/dist/jsx/formEngine/createPrefill.mjs +11 -0
- package/dist/jsx/formEngine/createPrefill.mjs.map +1 -0
- package/dist/jsx/formEngine/createPrefill.native.js +40 -0
- package/dist/jsx/formEngine/createPrefill.native.js.map +1 -0
- package/dist/jsx/formEngine/dirtyTracking.mjs +28 -0
- package/dist/jsx/formEngine/dirtyTracking.mjs.map +1 -0
- package/dist/jsx/formEngine/dirtyTracking.native.js +77 -0
- package/dist/jsx/formEngine/dirtyTracking.native.js.map +1 -0
- package/dist/jsx/formEngine/doctypeMeta.mjs +2 -0
- package/dist/jsx/formEngine/doctypeMeta.mjs.map +1 -0
- package/dist/jsx/formEngine/doctypeMeta.native.js +21 -0
- package/dist/jsx/formEngine/doctypeMeta.native.js.map +1 -0
- package/dist/jsx/formEngine/fetchFrom.mjs +40 -0
- package/dist/jsx/formEngine/fetchFrom.mjs.map +1 -0
- package/dist/jsx/formEngine/fetchFrom.native.js +110 -0
- package/dist/jsx/formEngine/fetchFrom.native.js.map +1 -0
- package/dist/jsx/formEngine/fieldPermissions.mjs +28 -0
- package/dist/jsx/formEngine/fieldPermissions.mjs.map +1 -0
- package/dist/jsx/formEngine/fieldPermissions.native.js +80 -0
- package/dist/jsx/formEngine/fieldPermissions.native.js.map +1 -0
- package/dist/jsx/formEngine/formActions.mjs +146 -0
- package/dist/jsx/formEngine/formActions.mjs.map +1 -0
- package/dist/jsx/formEngine/formActions.native.js +199 -0
- package/dist/jsx/formEngine/formActions.native.js.map +1 -0
- package/dist/jsx/formEngine/formLayout.mjs +217 -0
- package/dist/jsx/formEngine/formLayout.mjs.map +1 -0
- package/dist/jsx/formEngine/formLayout.native.js +388 -0
- package/dist/jsx/formEngine/formLayout.native.js.map +1 -0
- package/dist/jsx/formEngine/index.mjs +8 -0
- package/dist/jsx/formEngine/index.mjs.map +1 -0
- package/dist/jsx/formEngine/index.native.js +51 -0
- package/dist/jsx/formEngine/index.native.js.map +1 -0
- package/dist/jsx/formEngine/useFormEngine.mjs +362 -0
- package/dist/jsx/formEngine/useFormEngine.mjs.map +1 -0
- package/dist/jsx/formEngine/useFormEngine.native.js +476 -0
- package/dist/jsx/formEngine/useFormEngine.native.js.map +1 -0
- package/dist/jsx/index.js +4 -2
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +4 -2
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +19 -1
- package/dist/jsx/index.native.js.map +1 -1
- package/package.json +23 -17
- package/src/ChartCard.tsx +31 -0
- package/src/FileBrowser/FileBrowser.spec.tsx +16 -0
- package/src/FileBrowser/FileBrowser.stories.tsx +18 -1
- package/src/FileBrowser/folderTree.spec.tsx +68 -0
- package/src/FrappeCalendar.stories.tsx +1 -2
- package/src/FrappeChartCard.stories.tsx +1 -1
- package/src/FrappeChartCard.tsx +1 -1
- package/src/FrappeDashboard.stories.tsx +6 -5
- package/src/FrappeDashboard.tsx +6 -33
- package/src/FrappeGantt.stories.tsx +1 -2
- package/src/FrappeList.stories.tsx +3 -10
- package/src/components/attachments.stories.tsx +9 -2
- package/src/dependsOn.ts +2 -2
- package/src/fields/CodeEditor/CodeEditor.spec.tsx +37 -0
- package/src/fields/CodeEditor/CodeEditor.stories.tsx +83 -66
- package/src/fields/CodeEditor/index.tsx +4 -0
- package/src/fields/DateRange/DateRange.spec.tsx +143 -0
- package/src/fields/FileUpload.spec.tsx +334 -0
- package/src/fields/FileUploadField/FileUploadField.spec.tsx +166 -0
- package/src/fields/HTML/Html.spec.tsx +179 -0
- package/src/fields/Icon/Icon.stories.tsx +138 -0
- package/src/fields/Link.stories.tsx +2 -0
- package/src/fields/LongText/LongText.spec.tsx +217 -0
- package/src/fields/OTPInput/OTPInput.spec.tsx +167 -0
- package/src/fields/Password/Password.stories.tsx +21 -0
- package/src/fields/Phone/Phone.spec.tsx +151 -0
- package/src/fields/ReadOnly/ReadOnly.spec.tsx +229 -0
- package/src/fields/SmallText/SmallText.spec.tsx +175 -0
- package/src/fields/shared/EditorPreviewPanes.spec.tsx +122 -0
- package/src/formEngine/createPrefill.ts +28 -0
- package/src/formEngine/dirtyTracking.spec.ts +91 -0
- package/src/formEngine/dirtyTracking.ts +64 -0
- package/src/formEngine/doctypeMeta.ts +117 -0
- package/src/formEngine/fetchFrom.spec.ts +92 -0
- package/src/formEngine/fetchFrom.ts +111 -0
- package/src/formEngine/fieldPermissions.spec.ts +61 -0
- package/src/formEngine/fieldPermissions.ts +68 -0
- package/src/formEngine/formActions.ts +285 -0
- package/src/formEngine/formLayout.spec.ts +373 -0
- package/src/formEngine/formLayout.ts +392 -0
- package/src/formEngine/index.ts +33 -0
- package/src/formEngine/seam.spec.ts +148 -0
- package/src/formEngine/useFormEngine.ts +496 -0
- package/src/frappeRemainder.designLaw.spec.ts +9 -0
- package/src/frappeViews.designLaw.spec.ts +1 -0
- package/src/index.spec.ts +7 -3
- package/src/index.ts +51 -1
- package/src/schemaForm/SchemaForm.spec.tsx +4 -1
- package/types/ChartCard.d.ts +13 -0
- package/types/ChartCard.d.ts.map +1 -0
- package/types/FrappeDashboard.d.ts +2 -10
- package/types/FrappeDashboard.d.ts.map +1 -1
- package/types/dependsOn.d.ts +2 -2
- package/types/fields/CodeEditor/index.d.ts.map +1 -1
- package/types/formEngine/createPrefill.d.ts +19 -0
- package/types/formEngine/createPrefill.d.ts.map +1 -0
- package/types/formEngine/dirtyTracking.d.ts +36 -0
- package/types/formEngine/dirtyTracking.d.ts.map +1 -0
- package/types/formEngine/doctypeMeta.d.ts +117 -0
- package/types/formEngine/doctypeMeta.d.ts.map +1 -0
- package/types/formEngine/fetchFrom.d.ts +43 -0
- package/types/formEngine/fetchFrom.d.ts.map +1 -0
- package/types/formEngine/fieldPermissions.d.ts +38 -0
- package/types/formEngine/fieldPermissions.d.ts.map +1 -0
- package/types/formEngine/formActions.d.ts +95 -0
- package/types/formEngine/formActions.d.ts.map +1 -0
- package/types/formEngine/formLayout.d.ts +91 -0
- package/types/formEngine/formLayout.d.ts.map +1 -0
- package/types/formEngine/index.d.ts +27 -0
- package/types/formEngine/index.d.ts.map +1 -0
- package/types/formEngine/useFormEngine.d.ts +61 -0
- package/types/formEngine/useFormEngine.d.ts.map +1 -0
- package/types/index.d.ts +4 -1
- package/types/index.d.ts.map +1 -1
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* table-cell chromeless branch, and registry resolution for the
|
|
5
5
|
* "Code" and "JSON" fieldtypes.
|
|
6
6
|
*/
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
7
10
|
import { TableCellContext } from "@multiplatform.one/forms";
|
|
8
11
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
9
12
|
import { act, cleanup, fireEvent } from "@testing-library/react";
|
|
@@ -12,6 +15,24 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
12
15
|
import { getFieldComponent, type FieldRenderProps } from "../../fieldRegistry";
|
|
13
16
|
import { CodeEditorField, frappeCodeLanguage } from ".";
|
|
14
17
|
|
|
18
|
+
function codeEditorDir() {
|
|
19
|
+
try {
|
|
20
|
+
const meta = import.meta.url;
|
|
21
|
+
if (meta.startsWith("file:")) return dirname(fileURLToPath(meta));
|
|
22
|
+
} catch {
|
|
23
|
+
/* vite-node may serve a non-file import.meta.url */
|
|
24
|
+
}
|
|
25
|
+
return join(process.cwd(), "src/fields/CodeEditor");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readCodeEditorSource() {
|
|
29
|
+
return readFileSync(join(codeEditorDir(), "index.tsx"), "utf8");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function readCodeEditorStories() {
|
|
33
|
+
return readFileSync(join(codeEditorDir(), "CodeEditor.stories.tsx"), "utf8");
|
|
34
|
+
}
|
|
35
|
+
|
|
15
36
|
afterEach(cleanup);
|
|
16
37
|
|
|
17
38
|
describe("frappeCodeLanguage (stock ControlCode language_map)", () => {
|
|
@@ -80,6 +101,22 @@ describe("CodeEditorField (standalone)", () => {
|
|
|
80
101
|
);
|
|
81
102
|
expect(result.getTextArea()).toBeNull();
|
|
82
103
|
});
|
|
104
|
+
|
|
105
|
+
it("ships CSF3 story objects so native gallery keeps args (KS blank-pane)", () => {
|
|
106
|
+
const stories = readCodeEditorStories();
|
|
107
|
+
expect(stories).toMatch(/export const Main = \{/);
|
|
108
|
+
expect(stories).not.toMatch(/Main\.args\s*=/);
|
|
109
|
+
expect(stories).toContain("value: PYTHON_FIXTURE");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("forwards knob containerRadius onto the source textarea (F14)", () => {
|
|
113
|
+
const source = readCodeEditorSource();
|
|
114
|
+
expect(source).toContain("...knobProps.containerRadius");
|
|
115
|
+
const result = renderWithProviders(
|
|
116
|
+
<CodeEditorField label="Script" value="print(1)" language="python" onChange={() => {}} />,
|
|
117
|
+
);
|
|
118
|
+
expect(result.getTextArea()).toBeTruthy();
|
|
119
|
+
});
|
|
83
120
|
});
|
|
84
121
|
|
|
85
122
|
describe("registry resolution (Code / JSON fieldtypes)", () => {
|
|
@@ -30,11 +30,11 @@ export default {
|
|
|
30
30
|
title: "FrappeUI/CodeEditorField",
|
|
31
31
|
component: CodeEditorField,
|
|
32
32
|
parameters: {
|
|
33
|
-
status: { type: "
|
|
33
|
+
status: { type: "stable" },
|
|
34
34
|
docs: {
|
|
35
35
|
description: {
|
|
36
36
|
component:
|
|
37
|
-
"Frappe `Code` / `JSON` fieldtypes: monospace source pane + shiki-highlighted preview (unified components Highlighter). Language follows the stock df.options convention via `frappeCodeLanguage()`.",
|
|
37
|
+
"Frappe `Code` / `JSON` fieldtypes: monospace source pane + shiki-highlighted preview (unified components Highlighter). Language follows the stock df.options convention via `frappeCodeLanguage()`. Source textarea honours knob `containerRadius` (F14).",
|
|
38
38
|
},
|
|
39
39
|
},
|
|
40
40
|
},
|
|
@@ -74,76 +74,93 @@ function Controlled(props: Omit<StoryProps, "onChange">) {
|
|
|
74
74
|
);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
export const Main = (args
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} as Partial<StoryProps>;
|
|
77
|
+
// CSF3 objects (not `export const Main = (args) => …; Main.args = …`).
|
|
78
|
+
// Native Kitchen Sink `storyIndex.ts` treats a function export as
|
|
79
|
+
// `{ render: fn }` and drops `fn.args`, so CSF2 Main/Json/ReadOnly
|
|
80
|
+
// painted an empty Code pane on OTA (value/readOnly never arrived).
|
|
81
|
+
export const Main = {
|
|
82
|
+
name: "Main",
|
|
83
|
+
args: {
|
|
84
|
+
label: "Server script",
|
|
85
|
+
helperText: 'Language from df.options ("Python")',
|
|
86
|
+
value: PYTHON_FIXTURE,
|
|
87
|
+
language: frappeCodeLanguage("Python"),
|
|
88
|
+
} as Partial<StoryProps>,
|
|
89
|
+
render: (args: StoryProps) => <Controlled {...args} />,
|
|
90
|
+
};
|
|
92
91
|
|
|
93
|
-
export const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<YStack gap="$4">
|
|
101
|
-
<CodeEditorField
|
|
102
|
-
form={form as unknown as AnyFormApi}
|
|
103
|
-
name="script"
|
|
104
|
-
label="Script (form-integrated)"
|
|
105
|
-
language="python"
|
|
106
|
-
required
|
|
107
|
-
helperText="Bound to TanStack Form field state"
|
|
108
|
-
/>
|
|
109
|
-
</YStack>
|
|
110
|
-
</Form>
|
|
111
|
-
);
|
|
92
|
+
export const Json = {
|
|
93
|
+
args: {
|
|
94
|
+
label: "Filters (JSON fieldtype)",
|
|
95
|
+
value: JSON_FIXTURE,
|
|
96
|
+
language: "json",
|
|
97
|
+
} as Partial<StoryProps>,
|
|
98
|
+
render: (args: StoryProps) => <Controlled {...args} />,
|
|
112
99
|
};
|
|
113
100
|
|
|
114
|
-
export const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
101
|
+
export const FormIntegrated = {
|
|
102
|
+
name: "Form Integrated",
|
|
103
|
+
render: function FormIntegratedRender() {
|
|
104
|
+
const form = useForm({
|
|
105
|
+
defaultValues: { script: PYTHON_FIXTURE },
|
|
106
|
+
onSubmit: async ({ value }) => action("onSubmit")(value),
|
|
107
|
+
});
|
|
108
|
+
return (
|
|
109
|
+
<Form form={form}>
|
|
110
|
+
<YStack gap="$4">
|
|
111
|
+
<CodeEditorField
|
|
112
|
+
form={form as unknown as AnyFormApi}
|
|
113
|
+
name="script"
|
|
114
|
+
label="Script (form-integrated)"
|
|
115
|
+
language="python"
|
|
116
|
+
required
|
|
117
|
+
helperText="Bound to TanStack Form field state"
|
|
118
|
+
/>
|
|
119
|
+
</YStack>
|
|
120
|
+
</Form>
|
|
121
|
+
);
|
|
122
|
+
},
|
|
123
|
+
};
|
|
121
124
|
|
|
122
|
-
export const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
value: 'const answer = 42;\nconsole.log("state fixtures");',
|
|
132
|
-
language: "javascript",
|
|
133
|
-
} as Partial<StoryProps>;
|
|
125
|
+
export const ReadOnly = {
|
|
126
|
+
args: {
|
|
127
|
+
label: "Read-only script",
|
|
128
|
+
value: PYTHON_FIXTURE,
|
|
129
|
+
language: "python",
|
|
130
|
+
readOnly: true,
|
|
131
|
+
} as Partial<StoryProps>,
|
|
132
|
+
render: (args: StoryProps) => <Controlled {...args} />,
|
|
133
|
+
};
|
|
134
134
|
|
|
135
|
-
export const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
export const States = {
|
|
136
|
+
args: {
|
|
137
|
+
value: 'const answer = 42;\nconsole.log("state fixtures");',
|
|
138
|
+
language: "javascript",
|
|
139
|
+
} as Partial<StoryProps>,
|
|
140
|
+
render: (args: StoryProps) => (
|
|
141
|
+
<YStack gap="$5">
|
|
142
|
+
<Controlled {...args} label="Error" error="Script fails to parse at line 3" />
|
|
143
|
+
<Controlled {...args} label="Disabled" disabled />
|
|
144
|
+
<Controlled {...args} label="Compact" compact />
|
|
145
|
+
<CodeEditorField label="Skeleton" skeleton onChange={() => {}} />
|
|
139
146
|
</YStack>
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
),
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const DarkScheme = {
|
|
151
|
+
args: {
|
|
152
|
+
label: "Server script (dark)",
|
|
153
|
+
value: PYTHON_FIXTURE,
|
|
154
|
+
language: "python",
|
|
155
|
+
} as Partial<StoryProps>,
|
|
156
|
+
render: (args: StoryProps) => (
|
|
157
|
+
<Theme name="dark">
|
|
158
|
+
<YStack backgroundColor="$background" padding="$4">
|
|
159
|
+
<Controlled {...args} />
|
|
160
|
+
</YStack>
|
|
161
|
+
</Theme>
|
|
162
|
+
),
|
|
163
|
+
};
|
|
147
164
|
|
|
148
165
|
type CodeRow = { id: string; label: string; value: string };
|
|
149
166
|
|
|
@@ -213,6 +213,10 @@ export function CodeEditorField({
|
|
|
213
213
|
maxRows={maxRows}
|
|
214
214
|
textAreaProps={{
|
|
215
215
|
fontFamily: "$mono",
|
|
216
|
+
// F14: TextArea's inner <textarea> takes radius from `size` unless
|
|
217
|
+
// containerRadius lands after the size recipe. FieldLayout already
|
|
218
|
+
// gets knobProps; the measured source surface must too.
|
|
219
|
+
...knobProps.containerRadius,
|
|
216
220
|
autoCapitalize: "none",
|
|
217
221
|
autoCorrect: false,
|
|
218
222
|
spellCheck: false,
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DateRangeField contract specs. The `Date Range` fieldtype composes the forms
|
|
3
|
+
* DateRangePicker, so the adapter's contract is which props survive the hop.
|
|
4
|
+
*
|
|
5
|
+
* One of them does not, and that is pinned here deliberately: `value` and
|
|
6
|
+
* `onChange` are DECLARED on DateRangeFieldProps and then destructured away
|
|
7
|
+
* (`value: _value`, `onChange: _onChange`) instead of being handed to
|
|
8
|
+
* DateRangePicker. The registry feeds `props.value` straight into the adapter,
|
|
9
|
+
* so a saved Date Range never paints and a picked range never emits. These
|
|
10
|
+
* tests describe today's behaviour and will go red the moment someone wires it
|
|
11
|
+
* up — which is the point: that is the signal to update them alongside the fix.
|
|
12
|
+
* Reported on MPO-123.
|
|
13
|
+
*/
|
|
14
|
+
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
15
|
+
import { cleanup } from "@testing-library/react";
|
|
16
|
+
import type React from "react";
|
|
17
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
18
|
+
import { getFieldComponent, type FieldRenderProps } from "../../fieldRegistry";
|
|
19
|
+
import { DateRangeField } from ".";
|
|
20
|
+
|
|
21
|
+
afterEach(cleanup);
|
|
22
|
+
|
|
23
|
+
const trigger = (container: HTMLElement) =>
|
|
24
|
+
container.querySelector("input") as HTMLInputElement | null;
|
|
25
|
+
|
|
26
|
+
/** The accessible control: role="combobox", not the decorative inner input. */
|
|
27
|
+
const combobox = (container: HTMLElement) =>
|
|
28
|
+
container.querySelector('[data-testid="date-picker-trigger"]');
|
|
29
|
+
|
|
30
|
+
describe("DateRangeField composes the forms DateRangePicker", () => {
|
|
31
|
+
it("renders a single range trigger, not two date inputs", () => {
|
|
32
|
+
const result = renderWithProviders(<DateRangeField label="Period" />);
|
|
33
|
+
expect(result.container.querySelectorAll("input")).toHaveLength(1);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("carries the picker's own placeholder when the field sets none", () => {
|
|
37
|
+
const result = renderWithProviders(<DateRangeField label="Period" />);
|
|
38
|
+
expect(trigger(result.container)?.getAttribute("placeholder")).toContain("date");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("forwards an explicit placeholder", () => {
|
|
42
|
+
const result = renderWithProviders(<DateRangeField label="Period" placeholder="From – To" />);
|
|
43
|
+
expect(trigger(result.container)?.getAttribute("placeholder")).toBe("From – To");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("forwards readOnly, which swaps the popover trigger for a flat read-only box", () => {
|
|
47
|
+
const result = renderWithProviders(<DateRangeField label="Period" readOnly />);
|
|
48
|
+
expect(trigger(result.container)?.hasAttribute("readonly")).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("forwards required to the control, not just to the label", () => {
|
|
52
|
+
// The accessible control is the combobox box, not the inner <input>: that
|
|
53
|
+
// input is readOnly / tabIndex -1 / pointerEvents none, so the aria state
|
|
54
|
+
// rides the role="combobox" wrapper. Asserting it on the input passes
|
|
55
|
+
// vacuously against a picker that sets neither.
|
|
56
|
+
const result = renderWithProviders(<DateRangeField label="Period" required />);
|
|
57
|
+
expect(combobox(result.container)?.getAttribute("aria-required")).toBe("true");
|
|
58
|
+
expect(combobox(result.container)?.getAttribute("role")).toBe("combobox");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("forwards error, which marks the control invalid", () => {
|
|
62
|
+
const result = renderWithProviders(<DateRangeField label="Period" error="pick a range" />);
|
|
63
|
+
expect(combobox(result.container)?.getAttribute("aria-invalid")).toBe("true");
|
|
64
|
+
expect(result.container.textContent).toContain("pick a range");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("leaves both aria states off a field that is neither required nor errored", () => {
|
|
68
|
+
const result = renderWithProviders(<DateRangeField label="Period" />);
|
|
69
|
+
expect(combobox(result.container)?.getAttribute("aria-required")).toBeNull();
|
|
70
|
+
expect(combobox(result.container)?.getAttribute("aria-invalid")).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("forwards the label and helperText", () => {
|
|
74
|
+
const result = renderWithProviders(
|
|
75
|
+
<DateRangeField label="Period" helperText="inclusive of both ends" />,
|
|
76
|
+
);
|
|
77
|
+
expect(result.container.textContent).toContain("Period");
|
|
78
|
+
expect(result.container.textContent).toContain("inclusive of both ends");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("skeleton branch renders no trigger", () => {
|
|
82
|
+
const result = renderWithProviders(<DateRangeField label="Period" skeleton />);
|
|
83
|
+
expect(trigger(result.container)).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("DateRangeField value gap (MPO-123)", () => {
|
|
88
|
+
it("declares value on its props type", () => {
|
|
89
|
+
// Type-level promise: the adapter advertises a controlled value.
|
|
90
|
+
const props: React.ComponentProps<typeof DateRangeField> = { value: "2026-01-01" };
|
|
91
|
+
expect(props.value).toBe("2026-01-01");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("but a controlled value does not reach the control — the trigger stays empty", () => {
|
|
95
|
+
const result = renderWithProviders(
|
|
96
|
+
<DateRangeField label="Period" value="2026-01-01,2026-01-31" />,
|
|
97
|
+
);
|
|
98
|
+
expect(trigger(result.container)?.value).toBe("");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("and the registry hands that value straight to the adapter, so the drop is on this file", () => {
|
|
102
|
+
const Component = getFieldComponent("Date Range")!;
|
|
103
|
+
const element = (Component as Function)({
|
|
104
|
+
value: "2026-01-01,2026-01-31",
|
|
105
|
+
onChange: () => {},
|
|
106
|
+
} as FieldRenderProps) as React.ReactElement<Record<string, any>>;
|
|
107
|
+
expect(element.type).toBe(DateRangeField);
|
|
108
|
+
expect(element.props.value).toBe("2026-01-01,2026-01-31");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe("registry resolution (Date Range fieldtype)", () => {
|
|
113
|
+
function propsFor(overrides: Partial<FieldRenderProps> = {}): FieldRenderProps {
|
|
114
|
+
return { value: "", onChange: () => {}, ...overrides };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
it("resolves 'Date Range' to DateRangeField, and not to the Date adapter", () => {
|
|
118
|
+
const Component = getFieldComponent("Date Range")!;
|
|
119
|
+
expect(Component).toBeTruthy();
|
|
120
|
+
expect(Component).not.toBe(getFieldComponent("Date"));
|
|
121
|
+
const element = (Component as Function)(propsFor({ label: "Period" })) as React.ReactElement<
|
|
122
|
+
Record<string, any>
|
|
123
|
+
>;
|
|
124
|
+
expect(element.type).toBe(DateRangeField);
|
|
125
|
+
expect(element.props.label).toBe("Period");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("passes helperText / readOnly / skeleton / compact through", () => {
|
|
129
|
+
const Component = getFieldComponent("Date Range")!;
|
|
130
|
+
const element = (Component as Function)(
|
|
131
|
+
propsFor({
|
|
132
|
+
helperText: "both ends inclusive",
|
|
133
|
+
readOnly: true,
|
|
134
|
+
skeleton: true,
|
|
135
|
+
compact: true,
|
|
136
|
+
}),
|
|
137
|
+
) as React.ReactElement<Record<string, any>>;
|
|
138
|
+
expect(element.props.helperText).toBe("both ends inclusive");
|
|
139
|
+
expect(element.props.readOnly).toBe(true);
|
|
140
|
+
expect(element.props.skeleton).toBe(true);
|
|
141
|
+
expect(element.props.compact).toBe(true);
|
|
142
|
+
});
|
|
143
|
+
});
|