@emseepea/create-react-ui-server 0.0.2 → 0.0.4
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/README.md +49 -12
- package/{dist → initializer-dist}/template/README.md +1 -1
- package/{dist → initializer-dist}/template/eval/meaning.test.mjs +6 -6
- package/{dist → initializer-dist}/template/package.json +10 -9
- package/initializer-dist/template/src/capabilities/tool.preview-planting-plan.ts +4 -0
- package/{dist → initializer-dist}/template/src/server.tsx +3 -3
- package/{dist → initializer-dist}/template/src/ui-shared.tsx +54 -55
- package/{dist → initializer-dist}/template/test/ui-shared.test.mjs +1 -1
- package/{dist → initializer-dist}/template/test-support/browser-contract.mjs +9 -9
- package/package.json +38 -15
- package/dist/template/src/capabilities/tool.preview-bean-report.ts +0 -4
- /package/{dist → initializer-dist}/LICENSE +0 -0
- /package/{dist → initializer-dist}/create.mjs +0 -0
- /package/{dist → initializer-dist}/template/src/client.tsx +0 -0
- /package/{dist → initializer-dist}/template/test/accessibility.test.mjs +0 -0
- /package/{dist → initializer-dist}/template/tsconfig.json +0 -0
package/README.md
CHANGED
|
@@ -1,25 +1,62 @@
|
|
|
1
1
|
# `@emseepea/create-react-ui-server`
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
The package builds its starter from the maintained
|
|
5
|
-
[React form example](https://github.com/emseepea/emseepea/tree/main/examples/react-tailwind-ui).
|
|
3
|
+
This directory is both the maintained example and its public npm initializer.
|
|
6
4
|
|
|
7
|
-
##
|
|
5
|
+
## Use This Template
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Use this template for an accessible form in an application that already uses
|
|
8
|
+
React. Choose the [HTML UI server](../html-ui-server/README.md) when native HTML
|
|
9
|
+
is enough and you want fewer front-end dependencies. [Compare all eight templates](https://emseepea.github.io/emseepea/examples/).
|
|
10
|
+
|
|
11
|
+
## Create a Project
|
|
11
12
|
|
|
12
13
|
```sh
|
|
13
|
-
npm init @emseepea/react-ui-server
|
|
14
|
+
npm init @emseepea/react-ui-server -- my-server
|
|
14
15
|
```
|
|
15
16
|
|
|
17
|
+
<!-- generated-project-readme -->
|
|
18
|
+
|
|
19
|
+
## React and Tailwind UI Example
|
|
20
|
+
|
|
21
|
+
Choose this example when your application already uses React and you want an
|
|
22
|
+
accessible form without writing Tailwind configuration or component styles.
|
|
23
|
+
|
|
24
|
+
This example makes the server-rendered form interactive with React and imports
|
|
25
|
+
one compiled Em See Pea stylesheet. It needs no Tailwind configuration and uses
|
|
26
|
+
the same sample states as the native example.
|
|
27
|
+
|
|
28
|
+
## Run
|
|
29
|
+
|
|
30
|
+
From this directory:
|
|
31
|
+
|
|
16
32
|
```sh
|
|
17
|
-
cd my-server
|
|
18
33
|
npm install
|
|
19
|
-
npm
|
|
20
|
-
npm run lint
|
|
34
|
+
npm run build
|
|
21
35
|
npm start
|
|
22
36
|
```
|
|
23
37
|
|
|
24
|
-
|
|
25
|
-
|
|
38
|
+
Open
|
|
39
|
+
`http://127.0.0.1:3001/` for the page or use
|
|
40
|
+
`http://127.0.0.1:3001/mcp` for Model Context Protocol (MCP).
|
|
41
|
+
|
|
42
|
+
The page previews a sample pea planting plan only. It does not send or store a report.
|
|
43
|
+
|
|
44
|
+
## Check This Example
|
|
45
|
+
|
|
46
|
+
[Ordinary tests](test/) live in `test/`.
|
|
47
|
+
The [AI tool-choice and understanding test](eval/meaning.test.mjs) lives separately in `eval/`.
|
|
48
|
+
The commands below run each suite independently.
|
|
49
|
+
|
|
50
|
+
Run its build, browser, keyboard, React, and accessibility checks:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npm test
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Check that Claude chooses the preview tool and understands that it changes nothing:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
npm run test:llm
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
If Claude is not already signed in, run `claude auth login` first.
|
|
@@ -21,7 +21,7 @@ Open
|
|
|
21
21
|
`http://127.0.0.1:3001/` for the page or use
|
|
22
22
|
`http://127.0.0.1:3001/mcp` for Model Context Protocol (MCP).
|
|
23
23
|
|
|
24
|
-
The page previews
|
|
24
|
+
The page previews a sample pea planting plan only. It does not send or store a report.
|
|
25
25
|
|
|
26
26
|
## Check This Example
|
|
27
27
|
|
|
@@ -3,18 +3,18 @@ import { toolSelectionTest } from "@emseepea/testing/semantic";
|
|
|
3
3
|
toolSelectionTest("React UI preview is not mistaken for a completed effect", {
|
|
4
4
|
server: new URL("../dist/server.js", import.meta.url),
|
|
5
5
|
question:
|
|
6
|
-
"Summarize the
|
|
6
|
+
"Summarize the snap-pea planting-plan preview. Was a report sent or stored, and did this " +
|
|
7
7
|
"operation change anything?",
|
|
8
8
|
criticalFacts: [
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"Highland Snap",
|
|
10
|
+
"Meadow Sweet",
|
|
11
11
|
"preview-only",
|
|
12
12
|
"false",
|
|
13
13
|
"No report was sent or stored"
|
|
14
14
|
],
|
|
15
15
|
criteria:
|
|
16
|
-
"The answer identifies
|
|
17
|
-
"
|
|
16
|
+
"The answer identifies Highland Snap and Meadow Sweet as the two snap-pea " +
|
|
17
|
+
"matches. It says the result is preview-only, effectPerformed is false, no report " +
|
|
18
18
|
"was sent or stored, and no external action or data change occurred.",
|
|
19
|
-
expectedTools: ["preview-
|
|
19
|
+
expectedTools: ["preview-planting-plan"],
|
|
20
20
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emseepea-starter",
|
|
3
3
|
"version": "0.0.0",
|
|
4
|
-
"
|
|
4
|
+
"description": "Create an Em See Pea server with an accessible React form.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
@@ -13,17 +13,10 @@
|
|
|
13
13
|
"test:llm:built": "emseepea-test eval",
|
|
14
14
|
"lint": "oxlint src test eval test-support"
|
|
15
15
|
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@emseepea/react": "0.0.2",
|
|
18
|
-
"@emseepea/server": "0.1.0",
|
|
19
|
-
"@emseepea/tailwind": "0.0.1",
|
|
20
|
-
"react": "19.2.8",
|
|
21
|
-
"react-dom": "19.2.8"
|
|
22
|
-
},
|
|
23
16
|
"devDependencies": {
|
|
24
17
|
"playwright": "1.62.1",
|
|
25
18
|
"axe-core": "4.13.0",
|
|
26
|
-
"@emseepea/testing": "0.2.
|
|
19
|
+
"@emseepea/testing": "0.2.2",
|
|
27
20
|
"@types/node": "24.13.3",
|
|
28
21
|
"@types/react": "19.2.18",
|
|
29
22
|
"@types/react-dom": "19.2.5",
|
|
@@ -33,5 +26,13 @@
|
|
|
33
26
|
},
|
|
34
27
|
"engines": {
|
|
35
28
|
"node": ">=22"
|
|
29
|
+
},
|
|
30
|
+
"private": true,
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@emseepea/react": "0.0.5",
|
|
33
|
+
"@emseepea/server": "0.2.2",
|
|
34
|
+
"@emseepea/tailwind": "0.0.2",
|
|
35
|
+
"react": "19.2.8",
|
|
36
|
+
"react-dom": "19.2.8"
|
|
36
37
|
}
|
|
37
38
|
}
|
|
@@ -12,9 +12,9 @@ import { renderToString } from "react-dom/server";
|
|
|
12
12
|
const stylesheet = await readFile(new URL(import.meta.resolve("@emseepea/tailwind/styles.css")), "utf8");
|
|
13
13
|
const client = await readFile(new URL("./client.js", import.meta.url), "utf8");
|
|
14
14
|
const app = createEmseepea({
|
|
15
|
-
name: "emseepea-react-
|
|
15
|
+
name: "emseepea-react-ui-server",
|
|
16
16
|
version: "0.0.0",
|
|
17
|
-
instructions: "Use preview-
|
|
17
|
+
instructions: "Use preview-planting-plan to preview a sample pea planting plan. It sends and stores nothing.",
|
|
18
18
|
...await discoverCapabilities(new URL("./capabilities/", import.meta.url)),
|
|
19
19
|
});
|
|
20
20
|
|
|
@@ -56,7 +56,7 @@ function page(view: ElicitationView, theme: "light" | "dark", styled: boolean):
|
|
|
56
56
|
const markup = renderToString(<ElicitationForm view={view} headingLevel={2} />);
|
|
57
57
|
return `<!doctype html><html lang="en" data-emseepea-theme="${theme}"><head>` +
|
|
58
58
|
`<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">` +
|
|
59
|
-
`<title>
|
|
59
|
+
`<title>Pea planting plan preview - Em See Pea</title>${styled ? '<link rel="stylesheet" href="/emseepea.css">' : ""}` +
|
|
60
60
|
`</head><body><a href="#main-content">Skip to main content</a><main id="main-content" tabindex="-1">` +
|
|
61
61
|
`<h1>React and Tailwind form example</h1><div id="app">${markup}</div></main>` +
|
|
62
62
|
`<script id="emseepea-view" type="application/json">${safeJson(view)}</script>` +
|
|
@@ -1,68 +1,68 @@
|
|
|
1
1
|
import { defineElicitationView, defineTool, type ElicitationView } from "@emseepea/server";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const peaTypeSchema = z.enum(["all", "shelling", "snap"]);
|
|
5
5
|
const previewInputSchema = z.strictObject({
|
|
6
6
|
title: z.string().trim().min(1).max(80),
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
peaType: peaTypeSchema,
|
|
8
|
+
includeTips: z.boolean(),
|
|
9
9
|
});
|
|
10
10
|
const previewOutputSchema = z.strictObject({
|
|
11
11
|
status: z.literal("preview-only"),
|
|
12
12
|
effectPerformed: z.literal(false),
|
|
13
13
|
title: z.string(),
|
|
14
14
|
matchingCount: z.number().int().nonnegative(),
|
|
15
|
-
|
|
15
|
+
varieties: z.array(z.strictObject({
|
|
16
16
|
name: z.string(),
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
growthHabit: z.enum(["bush", "climbing"]),
|
|
18
|
+
peaType: z.enum(["shelling", "snap"]),
|
|
19
|
+
tips: z.array(z.string()).optional(),
|
|
20
20
|
})),
|
|
21
21
|
notice: z.literal("No report was sent or stored."),
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
const
|
|
25
|
-
{ name: "Harbour
|
|
26
|
-
{ name: "Highland
|
|
27
|
-
{ name: "
|
|
24
|
+
const varieties = [
|
|
25
|
+
{ name: "Harbour Gem", growthHabit: "bush" as const, peaType: "shelling" as const, tips: ["compact", "harvest when pods feel full"] },
|
|
26
|
+
{ name: "Highland Snap", growthHabit: "climbing" as const, peaType: "snap" as const, tips: ["provide support", "pick pods young"] },
|
|
27
|
+
{ name: "Meadow Sweet", growthHabit: "bush" as const, peaType: "snap" as const, tips: ["suits containers", "keep soil moist"] },
|
|
28
28
|
];
|
|
29
29
|
|
|
30
|
-
export function
|
|
31
|
-
const matching =
|
|
30
|
+
export function previewPlantingPlan(input: z.output<typeof previewInputSchema>) {
|
|
31
|
+
const matching = varieties.filter((variety) => input.peaType === "all" || variety.peaType === input.peaType);
|
|
32
32
|
return {
|
|
33
33
|
status: "preview-only" as const,
|
|
34
34
|
effectPerformed: false as const,
|
|
35
35
|
title: input.title,
|
|
36
36
|
matchingCount: matching.length,
|
|
37
|
-
|
|
37
|
+
varieties: matching.map(({ tips, ...variety }) => input.includeTips ? { ...variety, tips } : variety),
|
|
38
38
|
notice: "No report was sent or stored." as const,
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export function
|
|
42
|
+
export function createPreviewPlantingPlanTool() {
|
|
43
43
|
return defineTool({
|
|
44
|
-
name: "preview-
|
|
44
|
+
name: "preview-planting-plan",
|
|
45
45
|
access: "public",
|
|
46
|
-
title: "Preview a
|
|
47
|
-
description: "Preview a sample
|
|
46
|
+
title: "Preview a Pea Planting Plan",
|
|
47
|
+
description: "Preview a sample pea planting plan without sending, storing, or changing anything.",
|
|
48
48
|
inputSchema: previewInputSchema,
|
|
49
49
|
outputSchema: previewOutputSchema,
|
|
50
50
|
handler(input) {
|
|
51
|
-
const data =
|
|
51
|
+
const data = previewPlantingPlan(input);
|
|
52
52
|
return {
|
|
53
|
-
text: `${data.title} contains ${data.matchingCount} matching
|
|
53
|
+
text: `${data.title} contains ${data.matchingCount} matching pea varieties. ${data.notice}`,
|
|
54
54
|
data,
|
|
55
55
|
};
|
|
56
56
|
},
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
const fields = (values: { title: string;
|
|
60
|
+
const fields = (values: { title: string; peaType: z.output<typeof peaTypeSchema>; includeTips: boolean }, titleErrors?: string[]) => [
|
|
61
61
|
{
|
|
62
62
|
kind: "text" as const,
|
|
63
63
|
id: "report-title",
|
|
64
64
|
name: "title",
|
|
65
|
-
label: "
|
|
65
|
+
label: "Plan title",
|
|
66
66
|
description: "Name this preview so its purpose is clear.",
|
|
67
67
|
required: true,
|
|
68
68
|
minLength: 1,
|
|
@@ -72,50 +72,49 @@ const fields = (values: { title: string; roast: z.output<typeof roastSchema>; in
|
|
|
72
72
|
},
|
|
73
73
|
{
|
|
74
74
|
kind: "select" as const,
|
|
75
|
-
id: "
|
|
76
|
-
name: "
|
|
77
|
-
label: "
|
|
78
|
-
description: "Choose which sample
|
|
75
|
+
id: "pea-type",
|
|
76
|
+
name: "peaType",
|
|
77
|
+
label: "Pea type",
|
|
78
|
+
description: "Choose which sample pea varieties the preview includes.",
|
|
79
79
|
required: true,
|
|
80
|
-
placeholder: "Choose a
|
|
81
|
-
value: values.
|
|
80
|
+
placeholder: "Choose a pea type",
|
|
81
|
+
value: values.peaType,
|
|
82
82
|
options: [
|
|
83
|
-
{ value: "all", label: "All
|
|
84
|
-
{ value: "
|
|
85
|
-
{ value: "
|
|
86
|
-
{ value: "dark", label: "Dark" },
|
|
83
|
+
{ value: "all", label: "All pea types" },
|
|
84
|
+
{ value: "shelling", label: "Shelling" },
|
|
85
|
+
{ value: "snap", label: "Snap" },
|
|
87
86
|
],
|
|
88
87
|
},
|
|
89
88
|
{
|
|
90
89
|
kind: "checkbox" as const,
|
|
91
|
-
id: "include-
|
|
92
|
-
name: "
|
|
93
|
-
label: "Include
|
|
94
|
-
description: "Add sample
|
|
95
|
-
checked: values.
|
|
90
|
+
id: "include-tips",
|
|
91
|
+
name: "includeTips",
|
|
92
|
+
label: "Include growing tips",
|
|
93
|
+
description: "Add sample growing tips to the preview.",
|
|
94
|
+
checked: values.includeTips,
|
|
96
95
|
},
|
|
97
96
|
];
|
|
98
97
|
|
|
99
98
|
const base = {
|
|
100
|
-
id: "
|
|
101
|
-
heading: "Preview a
|
|
102
|
-
intro: "Review sample
|
|
103
|
-
legend: "
|
|
99
|
+
id: "pea-planting-plan-preview",
|
|
100
|
+
heading: "Preview a pea planting plan",
|
|
101
|
+
intro: "Review a sample planting plan. This preview sends and stores nothing.",
|
|
102
|
+
legend: "Planting plan options",
|
|
104
103
|
submitLabel: "Create preview",
|
|
105
104
|
} as const;
|
|
106
|
-
const defaults = { title: "
|
|
105
|
+
const defaults = { title: "Autumn pea plan", peaType: "all" as const, includeTips: true };
|
|
107
106
|
|
|
108
107
|
export const elicitationFixtures = {
|
|
109
108
|
ready: defineElicitationView({ ...base, fields: fields(defaults), state: { kind: "ready", focusTarget: "none" } }),
|
|
110
109
|
invalid: defineElicitationView({
|
|
111
110
|
...base,
|
|
112
|
-
fields: fields({ ...defaults, title: "" }, ["Enter a
|
|
111
|
+
fields: fields({ ...defaults, title: "" }, ["Enter a plan title."]),
|
|
113
112
|
state: {
|
|
114
113
|
kind: "invalid",
|
|
115
114
|
focusTarget: "error-summary",
|
|
116
115
|
summary: {
|
|
117
|
-
heading: "Fix the
|
|
118
|
-
items: [{ fieldId: "report-title", message: "Enter a
|
|
116
|
+
heading: "Fix the planting plan options",
|
|
117
|
+
items: [{ fieldId: "report-title", message: "Enter a plan title." }],
|
|
119
118
|
},
|
|
120
119
|
},
|
|
121
120
|
}),
|
|
@@ -125,7 +124,7 @@ export const elicitationFixtures = {
|
|
|
125
124
|
state: {
|
|
126
125
|
kind: "busy",
|
|
127
126
|
focusTarget: "status",
|
|
128
|
-
status: "Preparing a sample preview. No report is being sent or stored.",
|
|
127
|
+
status: "Preparing a sample planting-plan preview. No report is being sent or stored.",
|
|
129
128
|
},
|
|
130
129
|
}),
|
|
131
130
|
terminal: defineElicitationView({
|
|
@@ -135,7 +134,7 @@ export const elicitationFixtures = {
|
|
|
135
134
|
kind: "terminal",
|
|
136
135
|
focusTarget: "terminal",
|
|
137
136
|
heading: "Preview ready",
|
|
138
|
-
message: "Three sample
|
|
137
|
+
message: "Three sample pea varieties match. No report was sent or stored.",
|
|
139
138
|
},
|
|
140
139
|
}),
|
|
141
140
|
} satisfies Readonly<Record<"ready" | "invalid" | "busy" | "terminal", ElicitationView>>;
|
|
@@ -149,27 +148,27 @@ export function fixtureForState(value: unknown): ElicitationView {
|
|
|
149
148
|
export function viewFromSubmission(value: unknown): ElicitationView {
|
|
150
149
|
const record = typeof value === "object" && value !== null ? value as Record<string, unknown> : {};
|
|
151
150
|
const title = firstString(record.title)?.trim() ?? "";
|
|
152
|
-
const
|
|
151
|
+
const peaType = peaTypeSchema.safeParse(firstString(record.peaType));
|
|
153
152
|
const input = {
|
|
154
153
|
title,
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
peaType: peaType.success ? peaType.data : "all" as const,
|
|
155
|
+
includeTips: firstString(record.includeTips) === "true",
|
|
157
156
|
};
|
|
158
157
|
if (!title) {
|
|
159
158
|
return defineElicitationView({
|
|
160
159
|
...base,
|
|
161
|
-
|
|
160
|
+
fields: fields(input, ["Enter a plan title."]),
|
|
162
161
|
state: {
|
|
163
162
|
kind: "invalid",
|
|
164
163
|
focusTarget: "error-summary",
|
|
165
164
|
summary: {
|
|
166
|
-
heading: "Fix the
|
|
167
|
-
items: [{ fieldId: "report-title", message: "Enter a
|
|
165
|
+
heading: "Fix the planting plan options",
|
|
166
|
+
items: [{ fieldId: "report-title", message: "Enter a plan title." }],
|
|
168
167
|
},
|
|
169
168
|
},
|
|
170
169
|
});
|
|
171
170
|
}
|
|
172
|
-
const data =
|
|
171
|
+
const data = previewPlantingPlan(input);
|
|
173
172
|
return defineElicitationView({
|
|
174
173
|
...base,
|
|
175
174
|
fields: fields(input),
|
|
@@ -177,7 +176,7 @@ export function viewFromSubmission(value: unknown): ElicitationView {
|
|
|
177
176
|
kind: "terminal",
|
|
178
177
|
focusTarget: "terminal",
|
|
179
178
|
heading: "Preview ready",
|
|
180
|
-
message: `${data.matchingCount} sample ${data.matchingCount === 1 ? "
|
|
179
|
+
message: `${data.matchingCount} sample pea ${data.matchingCount === 1 ? "variety matches" : "varieties match"}. ${data.notice}`,
|
|
181
180
|
},
|
|
182
181
|
});
|
|
183
182
|
}
|
|
@@ -5,7 +5,7 @@ import { fixtureForState, viewFromSubmission } from "../dist/ui-shared.js";
|
|
|
5
5
|
|
|
6
6
|
test("shared UI fixtures preserve preview-only semantics", () => {
|
|
7
7
|
assert.equal(fixtureForState("ready").state.kind, "ready");
|
|
8
|
-
const result = viewFromSubmission({ title: "
|
|
8
|
+
const result = viewFromSubmission({ title: "Autumn peas", peaType: "snap", includeTips: "true" });
|
|
9
9
|
assert.equal(result.state.kind, "terminal");
|
|
10
10
|
assert.match(result.state.message, /No report was sent or stored/);
|
|
11
11
|
});
|
|
@@ -37,7 +37,7 @@ export function testUiExample(example) {
|
|
|
37
37
|
assert.equal(await page.locator("html[lang='en']").count(), 1);
|
|
38
38
|
assert.equal(await page.locator("main#main-content").count(), 1);
|
|
39
39
|
assert.equal(await page.locator("h1").count(), 1);
|
|
40
|
-
assert.equal(await page.title(), "
|
|
40
|
+
assert.equal(await page.title(), "Pea planting plan preview - Em See Pea");
|
|
41
41
|
assert.equal(await page.locator("h1").innerText(), example.h1);
|
|
42
42
|
assert.deepEqual(
|
|
43
43
|
await page.locator("h1,h2,h3,h4,h5,h6").evaluateAll((headings) => headings.map((heading) => ({
|
|
@@ -46,8 +46,8 @@ export function testUiExample(example) {
|
|
|
46
46
|
}))),
|
|
47
47
|
[
|
|
48
48
|
{ level: 1, text: example.h1 },
|
|
49
|
-
{ level: 2, text: "Preview a
|
|
50
|
-
...(state === "invalid" ? [{ level: 3, text: "Fix the
|
|
49
|
+
{ level: 2, text: "Preview a pea planting plan" },
|
|
50
|
+
...(state === "invalid" ? [{ level: 3, text: "Fix the planting plan options" }] : []),
|
|
51
51
|
...(state === "terminal" ? [{ level: 3, text: "Preview ready" }] : []),
|
|
52
52
|
],
|
|
53
53
|
);
|
|
@@ -66,29 +66,29 @@ export function testUiExample(example) {
|
|
|
66
66
|
assert.equal(await page.evaluate(() => document.activeElement?.id), "main-content");
|
|
67
67
|
|
|
68
68
|
await page.goto(`${running.origin}/?state=invalid`);
|
|
69
|
-
await page.waitForFunction(() => document.activeElement?.id === "
|
|
69
|
+
await page.waitForFunction(() => document.activeElement?.id === "pea-planting-plan-preview--error-summary");
|
|
70
70
|
assert.equal(await page.locator("[data-emseepea-part='error-summary']").getAttribute("role"), "alert");
|
|
71
71
|
assert.equal(
|
|
72
72
|
await page.locator("[data-emseepea-part='error-summary'] a").innerText(),
|
|
73
|
-
"
|
|
73
|
+
"Plan title: Enter a plan title.",
|
|
74
74
|
);
|
|
75
75
|
assert.equal(
|
|
76
76
|
await page.locator("[aria-invalid='true']").getAttribute("aria-describedby"),
|
|
77
|
-
"
|
|
77
|
+
"pea-planting-plan-preview--field--report-title--description pea-planting-plan-preview--field--report-title--error",
|
|
78
78
|
);
|
|
79
79
|
|
|
80
80
|
await page.goto(`${running.origin}/?state=busy`);
|
|
81
|
-
await page.waitForFunction(() => document.activeElement?.id === "
|
|
81
|
+
await page.waitForFunction(() => document.activeElement?.id === "pea-planting-plan-preview--status");
|
|
82
82
|
assert.equal(await page.locator("form").getAttribute("aria-busy"), "true");
|
|
83
83
|
assert.equal(await page.locator("button[type='submit']").isDisabled(), true);
|
|
84
84
|
|
|
85
85
|
await page.goto(`${running.origin}/?state=terminal`);
|
|
86
|
-
await page.waitForFunction(() => document.activeElement?.id === "
|
|
86
|
+
await page.waitForFunction(() => document.activeElement?.id === "pea-planting-plan-preview--terminal");
|
|
87
87
|
await assertNoEffectClaim(page);
|
|
88
88
|
|
|
89
89
|
await page.goto(`${running.origin}/?style=off&state=ready`);
|
|
90
90
|
assert.equal(await page.locator("link[rel='stylesheet']").count(), 0);
|
|
91
|
-
assert.equal(await page.locator("label[for='
|
|
91
|
+
assert.equal(await page.locator("label[for='pea-planting-plan-preview--field--report-title']").count(), 1);
|
|
92
92
|
assert.equal(await page.locator("form").count(), 1);
|
|
93
93
|
|
|
94
94
|
await page.goto(`${running.origin}/?state=ready`);
|
package/package.json
CHANGED
|
@@ -1,23 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emseepea/create-react-ui-server",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"private": false,
|
|
3
|
+
"version": "0.0.4",
|
|
5
4
|
"description": "Create an Em See Pea server with an accessible React form.",
|
|
6
5
|
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/emseepea/emseepea.git",
|
|
10
|
-
"directory": "packages/create-react-ui-server"
|
|
11
|
-
},
|
|
12
|
-
"homepage": "https://emseepea.github.io/emseepea/examples/",
|
|
13
|
-
"bugs": "https://github.com/emseepea/emseepea/issues",
|
|
14
|
-
"publishConfig": { "access": "public", "provenance": true, "tag": "next" },
|
|
15
6
|
"type": "module",
|
|
16
|
-
"
|
|
17
|
-
"files": ["dist"],
|
|
7
|
+
"starterDependencies": ["@emseepea/react", "@emseepea/server", "@emseepea/tailwind", "react", "react-dom"],
|
|
18
8
|
"scripts": {
|
|
19
|
-
"build": "
|
|
20
|
-
"
|
|
9
|
+
"build": "npm run build:example && npm run build:initializer",
|
|
10
|
+
"build:example": "tsc -p tsconfig.json && esbuild src/client.tsx --bundle --minify --format=esm --platform=browser --outfile=dist/client.js",
|
|
11
|
+
"build:initializer": "node ../../scripts/build-initializer.mjs",
|
|
12
|
+
"start": "node dist/server.js",
|
|
13
|
+
"test": "npm run build && npm run test:built",
|
|
14
|
+
"test:built": "node --test test/*.test.mjs",
|
|
15
|
+
"test:llm": "npm run build && npm run test:llm:built",
|
|
16
|
+
"test:llm:built": "emseepea-test eval",
|
|
17
|
+
"lint": "oxlint src test eval",
|
|
18
|
+
"prepack": "npm run build:initializer"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@emseepea/react": "0.0.5",
|
|
22
|
+
"@emseepea/server": "0.2.2",
|
|
23
|
+
"@emseepea/tailwind": "0.0.2",
|
|
24
|
+
"react": "19.2.8",
|
|
25
|
+
"react-dom": "19.2.8",
|
|
26
|
+
"@emseepea/example-ui-shared": "0.0.0",
|
|
27
|
+
"playwright": "1.62.1",
|
|
28
|
+
"axe-core": "4.13.0",
|
|
29
|
+
"@emseepea/testing": "0.2.2",
|
|
30
|
+
"@types/node": "24.13.3",
|
|
31
|
+
"@types/react": "19.2.18",
|
|
32
|
+
"@types/react-dom": "19.2.5",
|
|
33
|
+
"esbuild": "0.28.2",
|
|
34
|
+
"typescript": "6.0.3",
|
|
35
|
+
"oxlint": "1.80.0"
|
|
21
36
|
},
|
|
22
|
-
"engines": {
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22"
|
|
39
|
+
},
|
|
40
|
+
"repository": { "type": "git", "url": "git+https://github.com/emseepea/emseepea.git", "directory": "examples/react-ui-server" },
|
|
41
|
+
"homepage": "https://emseepea.github.io/emseepea/examples/",
|
|
42
|
+
"bugs": "https://github.com/emseepea/emseepea/issues",
|
|
43
|
+
"publishConfig": { "access": "public", "provenance": true },
|
|
44
|
+
"bin": { "create-react-ui-server": "./initializer-dist/create.mjs" },
|
|
45
|
+
"files": ["initializer-dist"]
|
|
23
46
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|