@sealcode/sealgen 0.15.22 → 0.15.23
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/lib/forms/controls/checkboxed-list-input.js +29 -22
- package/lib/forms/controls/checkboxed-list-input.test.js +173 -0
- package/lib/forms/fields/field.js +1 -1
- package/package.json +1 -1
- package/src/forms/controls/checkboxed-list-input.test.ts +189 -0
- package/src/forms/controls/checkboxed-list-input.ts +34 -23
- package/src/forms/fields/field.ts +3 -1
|
@@ -8,34 +8,41 @@ class CheckboxedListInput extends FormFieldControl {
|
|
|
8
8
|
this.options = options;
|
|
9
9
|
}
|
|
10
10
|
async render(fctx) {
|
|
11
|
-
const {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const {
|
|
12
|
+
valid,
|
|
13
|
+
message,
|
|
14
|
+
parsed: currentValue
|
|
15
|
+
} = await this.field.getValue(fctx.ctx, fctx.data, true);
|
|
15
16
|
const pickedValues = Object.entries(currentValue || {}).filter(([, is_on]) => is_on).map(([key]) => key);
|
|
16
17
|
const [options, isVisible] = await Promise.all([
|
|
17
18
|
this.field.generateOptions(fctx.ctx),
|
|
18
19
|
this.field.isVisible(fctx.ctx)
|
|
19
20
|
]);
|
|
20
21
|
const { name, required } = this.field;
|
|
21
|
-
return
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
22
|
+
return inputWrapper(
|
|
23
|
+
[name, getRequiredClass(required)],
|
|
24
|
+
tempstream`<label
|
|
25
|
+
>${this.options.label || this.field.name}</label
|
|
26
|
+
>${isVisible ? options.map(
|
|
27
|
+
({ value, label }) => inputWrapper(
|
|
28
|
+
[name],
|
|
29
|
+
/* HTML */
|
|
30
|
+
`
|
|
31
|
+
<input
|
|
32
|
+
type="checkbox"
|
|
33
|
+
id="${name}.${value}"
|
|
34
|
+
name="${name}.${value}"
|
|
35
|
+
${pickedValues.includes(value) ? "checked" : ""}
|
|
36
|
+
form=${fctx.form_id}
|
|
37
|
+
/>
|
|
38
|
+
<label for="${name}.${value}"
|
|
39
|
+
>${label || value}</label
|
|
40
|
+
>
|
|
41
|
+
`
|
|
42
|
+
)
|
|
43
|
+
) : ""}
|
|
44
|
+
${~valid && fctx.validate ? `<div class="input__error">${message}</div>` : ""}`
|
|
45
|
+
);
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
export {
|
|
@@ -7,6 +7,7 @@ import { Form } from "../form.js";
|
|
|
7
7
|
import { CheckboxedListInput } from "./checkboxed-list-input.js";
|
|
8
8
|
import { getBrowser } from "../../utils/browser-creator.js";
|
|
9
9
|
import assert from "assert";
|
|
10
|
+
import { sleep } from "../../utils/sleep.js";
|
|
10
11
|
describe("checkboxed-list", () => {
|
|
11
12
|
describe("basic tests", async () => {
|
|
12
13
|
let server;
|
|
@@ -58,6 +59,178 @@ describe("checkboxed-list", () => {
|
|
|
58
59
|
await page.getByRole("button", { name: "Wy\u015Blij" }).click();
|
|
59
60
|
assert.deepStrictEqual(received_values, { syllables: { co: "on", de: "on" } });
|
|
60
61
|
});
|
|
62
|
+
describe("behavior with (non)required", () => {
|
|
63
|
+
it("allows to send a form when no value is selected and fiels is not required", async () => {
|
|
64
|
+
const port = await getPort();
|
|
65
|
+
console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
|
|
66
|
+
const app = new Koa();
|
|
67
|
+
const router = new Router();
|
|
68
|
+
const fields = {
|
|
69
|
+
syllables: new CheckboxedListField(false, async () => {
|
|
70
|
+
await sleep(10);
|
|
71
|
+
return ["sa", "mple", "co", "de"].map((s) => ({
|
|
72
|
+
value: s,
|
|
73
|
+
label: s.toLocaleUpperCase()
|
|
74
|
+
}));
|
|
75
|
+
})
|
|
76
|
+
};
|
|
77
|
+
let submit_called = false;
|
|
78
|
+
mount(
|
|
79
|
+
router,
|
|
80
|
+
"/",
|
|
81
|
+
new class extends Form {
|
|
82
|
+
constructor() {
|
|
83
|
+
super(...arguments);
|
|
84
|
+
this.fields = fields;
|
|
85
|
+
this.controls = [new CheckboxedListInput(fields.syllables)];
|
|
86
|
+
}
|
|
87
|
+
async onSubmit(ctx) {
|
|
88
|
+
submit_called = true;
|
|
89
|
+
}
|
|
90
|
+
async canAccess(_) {
|
|
91
|
+
return { canAccess: true, message: "" };
|
|
92
|
+
}
|
|
93
|
+
}(),
|
|
94
|
+
true
|
|
95
|
+
);
|
|
96
|
+
app.use(router.routes()).use(router.allowedMethods());
|
|
97
|
+
server = app.listen(port);
|
|
98
|
+
browser = await getBrowser();
|
|
99
|
+
const context = await browser.newContext();
|
|
100
|
+
page = await context.newPage();
|
|
101
|
+
await page.goto(`http://localhost:${port}`);
|
|
102
|
+
await page.getByRole("button", { name: "Wy\u015Blij" }).click();
|
|
103
|
+
await sleep(100);
|
|
104
|
+
assert(submit_called);
|
|
105
|
+
});
|
|
106
|
+
it("does not allow to send a form when no value is selected and field IS required", async () => {
|
|
107
|
+
const port = await getPort();
|
|
108
|
+
console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
|
|
109
|
+
const app = new Koa();
|
|
110
|
+
const router = new Router();
|
|
111
|
+
const fields = {
|
|
112
|
+
syllables: new CheckboxedListField(true, async () => {
|
|
113
|
+
await sleep(10);
|
|
114
|
+
return ["sa", "mple", "co", "de"].map((s) => ({
|
|
115
|
+
value: s,
|
|
116
|
+
label: s.toLocaleUpperCase()
|
|
117
|
+
}));
|
|
118
|
+
})
|
|
119
|
+
};
|
|
120
|
+
let submit_called = false;
|
|
121
|
+
mount(
|
|
122
|
+
router,
|
|
123
|
+
"/",
|
|
124
|
+
new class extends Form {
|
|
125
|
+
constructor() {
|
|
126
|
+
super(...arguments);
|
|
127
|
+
this.fields = fields;
|
|
128
|
+
this.controls = [new CheckboxedListInput(fields.syllables)];
|
|
129
|
+
}
|
|
130
|
+
async onSubmit(ctx) {
|
|
131
|
+
submit_called = true;
|
|
132
|
+
}
|
|
133
|
+
async canAccess(_) {
|
|
134
|
+
return { canAccess: true, message: "" };
|
|
135
|
+
}
|
|
136
|
+
}(),
|
|
137
|
+
true
|
|
138
|
+
);
|
|
139
|
+
app.use(router.routes()).use(router.allowedMethods());
|
|
140
|
+
server = app.listen(port);
|
|
141
|
+
browser = await getBrowser();
|
|
142
|
+
const context = await browser.newContext();
|
|
143
|
+
page = await context.newPage();
|
|
144
|
+
await page.goto(`http://localhost:${port}`);
|
|
145
|
+
await page.getByRole("button", { name: "Wy\u015Blij" }).click();
|
|
146
|
+
await sleep(100);
|
|
147
|
+
assert(submit_called == false);
|
|
148
|
+
await page.getByText(/empty value/).click({ timeout: 500 });
|
|
149
|
+
});
|
|
150
|
+
it("allows to send a form when no value is selected and field IS required", async () => {
|
|
151
|
+
const port = await getPort();
|
|
152
|
+
console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
|
|
153
|
+
const app = new Koa();
|
|
154
|
+
const router = new Router();
|
|
155
|
+
const fields = {
|
|
156
|
+
syllables: new CheckboxedListField(true, async () => {
|
|
157
|
+
await sleep(10);
|
|
158
|
+
return ["sa", "mple", "co", "de"].map((s) => ({
|
|
159
|
+
value: s,
|
|
160
|
+
label: s.toLocaleUpperCase()
|
|
161
|
+
}));
|
|
162
|
+
})
|
|
163
|
+
};
|
|
164
|
+
let submit_called = false;
|
|
165
|
+
mount(
|
|
166
|
+
router,
|
|
167
|
+
"/",
|
|
168
|
+
new class extends Form {
|
|
169
|
+
constructor() {
|
|
170
|
+
super(...arguments);
|
|
171
|
+
this.fields = fields;
|
|
172
|
+
this.controls = [new CheckboxedListInput(fields.syllables)];
|
|
173
|
+
}
|
|
174
|
+
async onSubmit(ctx) {
|
|
175
|
+
submit_called = true;
|
|
176
|
+
}
|
|
177
|
+
async canAccess(_) {
|
|
178
|
+
return { canAccess: true, message: "" };
|
|
179
|
+
}
|
|
180
|
+
}(),
|
|
181
|
+
true
|
|
182
|
+
);
|
|
183
|
+
app.use(router.routes()).use(router.allowedMethods());
|
|
184
|
+
server = app.listen(port);
|
|
185
|
+
browser = await getBrowser();
|
|
186
|
+
const context = await browser.newContext();
|
|
187
|
+
page = await context.newPage();
|
|
188
|
+
await page.goto(`http://localhost:${port}`);
|
|
189
|
+
await page.getByLabel("DE").check();
|
|
190
|
+
await page.getByRole("button", { name: "Wy\u015Blij" }).click();
|
|
191
|
+
await sleep(100);
|
|
192
|
+
assert(submit_called);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
it("renders the label", async () => {
|
|
196
|
+
const port = await getPort();
|
|
197
|
+
console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
|
|
198
|
+
const app = new Koa();
|
|
199
|
+
const router = new Router();
|
|
200
|
+
const fields = {
|
|
201
|
+
syllables: new CheckboxedListField(true, async () => {
|
|
202
|
+
return ["sa", "mple", "co", "de"].map((s) => ({
|
|
203
|
+
value: s,
|
|
204
|
+
label: s.toLocaleUpperCase()
|
|
205
|
+
}));
|
|
206
|
+
})
|
|
207
|
+
};
|
|
208
|
+
mount(
|
|
209
|
+
router,
|
|
210
|
+
"/",
|
|
211
|
+
new class extends Form {
|
|
212
|
+
constructor() {
|
|
213
|
+
super(...arguments);
|
|
214
|
+
this.fields = fields;
|
|
215
|
+
this.controls = [new CheckboxedListInput(fields.syllables, { label: "ABC" })];
|
|
216
|
+
}
|
|
217
|
+
async onSubmit(ctx) {
|
|
218
|
+
}
|
|
219
|
+
async canAccess(_) {
|
|
220
|
+
return { canAccess: true, message: "" };
|
|
221
|
+
}
|
|
222
|
+
}(),
|
|
223
|
+
true
|
|
224
|
+
);
|
|
225
|
+
app.use(router.routes()).use(router.allowedMethods());
|
|
226
|
+
server = app.listen(port);
|
|
227
|
+
browser = await getBrowser();
|
|
228
|
+
const context = await browser.newContext();
|
|
229
|
+
page = await context.newPage();
|
|
230
|
+
await page.goto(`http://localhost:${port}`);
|
|
231
|
+
await page.getByRole("button", { name: "Wy\u015Blij" }).click();
|
|
232
|
+
await page.getByText(/ABC/).click({ timeout: 500 });
|
|
233
|
+
});
|
|
61
234
|
});
|
|
62
235
|
});
|
|
63
236
|
//# sourceMappingURL=checkboxed-list-input.test.js.map
|
|
@@ -40,7 +40,7 @@ class FormField {
|
|
|
40
40
|
if (raw == void 0 || raw == null || raw == "") {
|
|
41
41
|
return {
|
|
42
42
|
parsed: null,
|
|
43
|
-
message: this.required ? "empty value" : "",
|
|
43
|
+
message: this.required ? "empty value, but field is required" : "",
|
|
44
44
|
raw,
|
|
45
45
|
...validate ? { valid: !this.required } : {}
|
|
46
46
|
};
|
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import { Form } from "../form.js";
|
|
|
9
9
|
import { CheckboxedListInput } from "./checkboxed-list-input.js";
|
|
10
10
|
import { getBrowser } from "../../utils/browser-creator.js";
|
|
11
11
|
import assert from "assert";
|
|
12
|
+
import { sleep } from "../../utils/sleep.js";
|
|
12
13
|
|
|
13
14
|
describe("checkboxed-list", () => {
|
|
14
15
|
describe("basic tests", async () => {
|
|
@@ -66,5 +67,193 @@ describe("checkboxed-list", () => {
|
|
|
66
67
|
await page.getByRole("button", { name: "Wyślij" }).click();
|
|
67
68
|
assert.deepStrictEqual(received_values, { syllables: { co: "on", de: "on" } });
|
|
68
69
|
});
|
|
70
|
+
|
|
71
|
+
describe("behavior with (non)required", () => {
|
|
72
|
+
it("allows to send a form when no value is selected and fiels is not required", async () => {
|
|
73
|
+
const port = await getPort();
|
|
74
|
+
console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
|
|
75
|
+
const app = new Koa();
|
|
76
|
+
const router = new Router();
|
|
77
|
+
|
|
78
|
+
const fields = {
|
|
79
|
+
syllables: new CheckboxedListField(false, async () => {
|
|
80
|
+
await sleep(10);
|
|
81
|
+
return ["sa", "mple", "co", "de"].map((s) => ({
|
|
82
|
+
value: s,
|
|
83
|
+
label: s.toLocaleUpperCase(),
|
|
84
|
+
}));
|
|
85
|
+
}),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
let submit_called = false;
|
|
89
|
+
|
|
90
|
+
mount(
|
|
91
|
+
router,
|
|
92
|
+
"/",
|
|
93
|
+
new (class extends Form<typeof fields, unknown> {
|
|
94
|
+
fields = fields;
|
|
95
|
+
controls = [new CheckboxedListInput(fields.syllables)];
|
|
96
|
+
|
|
97
|
+
async onSubmit(ctx: Context) {
|
|
98
|
+
submit_called = true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async canAccess(_: Context) {
|
|
102
|
+
return { canAccess: true, message: "" };
|
|
103
|
+
}
|
|
104
|
+
})(),
|
|
105
|
+
true
|
|
106
|
+
);
|
|
107
|
+
app.use(router.routes()).use(router.allowedMethods());
|
|
108
|
+
server = app.listen(port);
|
|
109
|
+
|
|
110
|
+
browser = await getBrowser();
|
|
111
|
+
const context = await browser.newContext();
|
|
112
|
+
page = await context.newPage();
|
|
113
|
+
await page.goto(`http://localhost:${port}`);
|
|
114
|
+
await page.getByRole("button", { name: "Wyślij" }).click();
|
|
115
|
+
await sleep(100);
|
|
116
|
+
assert(submit_called);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("does not allow to send a form when no value is selected and field IS required", async () => {
|
|
120
|
+
const port = await getPort();
|
|
121
|
+
console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
|
|
122
|
+
const app = new Koa();
|
|
123
|
+
const router = new Router();
|
|
124
|
+
|
|
125
|
+
const fields = {
|
|
126
|
+
syllables: new CheckboxedListField(true, async () => {
|
|
127
|
+
await sleep(10);
|
|
128
|
+
return ["sa", "mple", "co", "de"].map((s) => ({
|
|
129
|
+
value: s,
|
|
130
|
+
label: s.toLocaleUpperCase(),
|
|
131
|
+
}));
|
|
132
|
+
}),
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
let submit_called = false;
|
|
136
|
+
|
|
137
|
+
mount(
|
|
138
|
+
router,
|
|
139
|
+
"/",
|
|
140
|
+
new (class extends Form<typeof fields, unknown> {
|
|
141
|
+
fields = fields;
|
|
142
|
+
controls = [new CheckboxedListInput(fields.syllables)];
|
|
143
|
+
|
|
144
|
+
async onSubmit(ctx: Context) {
|
|
145
|
+
submit_called = true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async canAccess(_: Context) {
|
|
149
|
+
return { canAccess: true, message: "" };
|
|
150
|
+
}
|
|
151
|
+
})(),
|
|
152
|
+
true
|
|
153
|
+
);
|
|
154
|
+
app.use(router.routes()).use(router.allowedMethods());
|
|
155
|
+
server = app.listen(port);
|
|
156
|
+
|
|
157
|
+
browser = await getBrowser();
|
|
158
|
+
const context = await browser.newContext();
|
|
159
|
+
page = await context.newPage();
|
|
160
|
+
await page.goto(`http://localhost:${port}`);
|
|
161
|
+
await page.getByRole("button", { name: "Wyślij" }).click();
|
|
162
|
+
await sleep(100);
|
|
163
|
+
assert(submit_called == false);
|
|
164
|
+
await page.getByText(/empty value/).click({ timeout: 500 }); // should display an error message for that field
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("allows to send a form when no value is selected and field IS required", async () => {
|
|
168
|
+
const port = await getPort();
|
|
169
|
+
console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
|
|
170
|
+
const app = new Koa();
|
|
171
|
+
const router = new Router();
|
|
172
|
+
|
|
173
|
+
const fields = {
|
|
174
|
+
syllables: new CheckboxedListField(true, async () => {
|
|
175
|
+
await sleep(10);
|
|
176
|
+
return ["sa", "mple", "co", "de"].map((s) => ({
|
|
177
|
+
value: s,
|
|
178
|
+
label: s.toLocaleUpperCase(),
|
|
179
|
+
}));
|
|
180
|
+
}),
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
let submit_called = false;
|
|
184
|
+
|
|
185
|
+
mount(
|
|
186
|
+
router,
|
|
187
|
+
"/",
|
|
188
|
+
new (class extends Form<typeof fields, unknown> {
|
|
189
|
+
fields = fields;
|
|
190
|
+
controls = [new CheckboxedListInput(fields.syllables)];
|
|
191
|
+
|
|
192
|
+
async onSubmit(ctx: Context) {
|
|
193
|
+
submit_called = true;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async canAccess(_: Context) {
|
|
197
|
+
return { canAccess: true, message: "" };
|
|
198
|
+
}
|
|
199
|
+
})(),
|
|
200
|
+
true
|
|
201
|
+
);
|
|
202
|
+
app.use(router.routes()).use(router.allowedMethods());
|
|
203
|
+
server = app.listen(port);
|
|
204
|
+
|
|
205
|
+
browser = await getBrowser();
|
|
206
|
+
const context = await browser.newContext();
|
|
207
|
+
page = await context.newPage();
|
|
208
|
+
await page.goto(`http://localhost:${port}`);
|
|
209
|
+
await page.getByLabel("DE").check();
|
|
210
|
+
await page.getByRole("button", { name: "Wyślij" }).click();
|
|
211
|
+
await sleep(100);
|
|
212
|
+
assert(submit_called);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("renders the label", async () => {
|
|
217
|
+
const port = await getPort();
|
|
218
|
+
console.log(`Using port ${port} form checkboxed-list-input.test.ts`);
|
|
219
|
+
const app = new Koa();
|
|
220
|
+
const router = new Router();
|
|
221
|
+
|
|
222
|
+
const fields = {
|
|
223
|
+
syllables: new CheckboxedListField(true, async () => {
|
|
224
|
+
return ["sa", "mple", "co", "de"].map((s) => ({
|
|
225
|
+
value: s,
|
|
226
|
+
label: s.toLocaleUpperCase(),
|
|
227
|
+
}));
|
|
228
|
+
}),
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
mount(
|
|
232
|
+
router,
|
|
233
|
+
"/",
|
|
234
|
+
new (class extends Form<typeof fields, unknown> {
|
|
235
|
+
fields = fields;
|
|
236
|
+
controls = [new CheckboxedListInput(fields.syllables, { label: "ABC" })];
|
|
237
|
+
|
|
238
|
+
async onSubmit(ctx: Context) {
|
|
239
|
+
// noop
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async canAccess(_: Context) {
|
|
243
|
+
return { canAccess: true, message: "" };
|
|
244
|
+
}
|
|
245
|
+
})(),
|
|
246
|
+
true
|
|
247
|
+
);
|
|
248
|
+
app.use(router.routes()).use(router.allowedMethods());
|
|
249
|
+
server = app.listen(port);
|
|
250
|
+
|
|
251
|
+
browser = await getBrowser();
|
|
252
|
+
const context = await browser.newContext();
|
|
253
|
+
page = await context.newPage();
|
|
254
|
+
await page.goto(`http://localhost:${port}`);
|
|
255
|
+
await page.getByRole("button", { name: "Wyślij" }).click();
|
|
256
|
+
await page.getByText(/ABC/).click({ timeout: 500 }); // should display an error message for that field
|
|
257
|
+
});
|
|
69
258
|
});
|
|
70
259
|
});
|
|
@@ -13,10 +13,11 @@ export class CheckboxedListInput extends FormFieldControl {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
async render(fctx: FormControlContext) {
|
|
16
|
-
const {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
const {
|
|
17
|
+
valid,
|
|
18
|
+
message,
|
|
19
|
+
parsed: currentValue,
|
|
20
|
+
} = await this.field.getValue(fctx.ctx, fctx.data, true);
|
|
20
21
|
const pickedValues = Object.entries(currentValue || {})
|
|
21
22
|
.filter(([, is_on]) => is_on)
|
|
22
23
|
.map(([key]) => key);
|
|
@@ -26,24 +27,34 @@ export class CheckboxedListInput extends FormFieldControl {
|
|
|
26
27
|
]);
|
|
27
28
|
|
|
28
29
|
const { name, required } = this.field;
|
|
29
|
-
return
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
30
|
+
return inputWrapper(
|
|
31
|
+
[name, getRequiredClass(required)],
|
|
32
|
+
tempstream/* HTML */ `<label
|
|
33
|
+
>${this.options.label || this.field.name}</label
|
|
34
|
+
>${isVisible
|
|
35
|
+
? options.map(({ value, label }) =>
|
|
36
|
+
inputWrapper(
|
|
37
|
+
[name],
|
|
38
|
+
/* HTML */ `
|
|
39
|
+
<input
|
|
40
|
+
type="checkbox"
|
|
41
|
+
id="${name}.${value}"
|
|
42
|
+
name="${name}.${value}"
|
|
43
|
+
${pickedValues.includes(value)
|
|
44
|
+
? "checked"
|
|
45
|
+
: ""}
|
|
46
|
+
form=${fctx.form_id}
|
|
47
|
+
/>
|
|
48
|
+
<label for="${name}.${value}"
|
|
49
|
+
>${label || value}</label
|
|
50
|
+
>
|
|
51
|
+
`
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
: ""}
|
|
55
|
+
${~valid && fctx.validate
|
|
56
|
+
? `<div class="input__error">${message}</div>`
|
|
57
|
+
: ""}`
|
|
58
|
+
);
|
|
48
59
|
}
|
|
49
60
|
}
|
|
@@ -130,7 +130,9 @@ export abstract class FormField<
|
|
|
130
130
|
if (raw == undefined || raw == null || raw == "") {
|
|
131
131
|
return {
|
|
132
132
|
parsed: null,
|
|
133
|
-
message: this.required
|
|
133
|
+
message: this.required
|
|
134
|
+
? "empty value, but field is required"
|
|
135
|
+
: "",
|
|
134
136
|
raw,
|
|
135
137
|
...(validate ? { valid: !this.required } : {}),
|
|
136
138
|
};
|