@webiny/app-admin 6.5.0 → 6.5.1-beta.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/base/Base/DefaultFieldRenderers.js +4 -0
- package/base/Base/DefaultFieldRenderers.js.map +1 -1
- package/base/Base/FieldRenderers/BirthdayInputRenderer.d.ts +21 -0
- package/base/Base/FieldRenderers/BirthdayInputRenderer.js +39 -0
- package/base/Base/FieldRenderers/BirthdayInputRenderer.js.map +1 -0
- package/base/Base/FieldRenderers/BirthdayInputRenderer.test.d.ts +1 -0
- package/base/Base/FieldRenderers/BirthdayInputRenderer.test.js +123 -0
- package/base/Base/FieldRenderers/BirthdayInputRenderer.test.js.map +1 -0
- package/base/Base/UserMenu/SwitchTenant.d.ts +20 -0
- package/base/Base/UserMenu/SwitchTenant.js +45 -0
- package/base/Base/UserMenu/SwitchTenant.js.map +1 -0
- package/base/Base/UserMenu.js +4 -0
- package/base/Base/UserMenu.js.map +1 -1
- package/domain/Identity.d.ts +8 -0
- package/domain/Identity.js +6 -0
- package/domain/Identity.js.map +1 -1
- package/exports/admin/security.d.ts +4 -1
- package/exports/admin/security.js +4 -1
- package/features/formModel/FieldBuilder.test.js +126 -1
- package/features/formModel/FieldBuilder.test.js.map +1 -1
- package/features/formModel/fieldTypes/DateTimeFieldType.d.ts +65 -4
- package/features/formModel/fieldTypes/DateTimeFieldType.js +70 -8
- package/features/formModel/fieldTypes/DateTimeFieldType.js.map +1 -1
- package/features/formModel/fieldTypes/dateLimits.d.ts +37 -0
- package/features/formModel/fieldTypes/dateLimits.js +64 -0
- package/features/formModel/fieldTypes/dateLimits.js.map +1 -0
- package/features/formModel/fieldTypes/dateLimits.test.d.ts +1 -0
- package/features/formModel/fieldTypes/dateLimits.test.js +60 -0
- package/features/formModel/fieldTypes/dateLimits.test.js.map +1 -0
- package/features/formModel/renderers.d.ts +1 -0
- package/features/formModel/renderers.js +1 -0
- package/features/security/LogIn/LogInError.d.ts +15 -0
- package/features/security/LogIn/LogInError.js +25 -0
- package/features/security/LogIn/LogInError.js.map +1 -0
- package/features/security/LogIn/LogInGateway.d.ts +2 -2
- package/features/security/LogIn/LogInGateway.js +22 -5
- package/features/security/LogIn/LogInGateway.js.map +1 -1
- package/features/security/LogIn/LogInRepository.d.ts +1 -2
- package/features/security/LogIn/LogInRepository.js +6 -2
- package/features/security/LogIn/LogInRepository.js.map +1 -1
- package/features/security/LogIn/LogInUseCase.d.ts +7 -2
- package/features/security/LogIn/LogInUseCase.js +53 -3
- package/features/security/LogIn/LogInUseCase.js.map +1 -1
- package/features/security/LogIn/LogInUseCase.test.d.ts +1 -0
- package/features/security/LogIn/LogInUseCase.test.js +321 -0
- package/features/security/LogIn/LogInUseCase.test.js.map +1 -0
- package/features/security/LogIn/abstractions.d.ts +48 -3
- package/features/security/LogIn/abstractions.js.map +1 -1
- package/features/security/LogIn/createLoginMutation.js +40 -27
- package/features/security/LogIn/createLoginMutation.js.map +1 -1
- package/features/security/LogIn/feature.js +2 -0
- package/features/security/LogIn/feature.js.map +1 -1
- package/features/security/LogIn/index.d.ts +1 -0
- package/features/security/LogIn/index.js +1 -0
- package/features/security/LoginTenantResolver/LoginTenantResolver.d.ts +14 -0
- package/features/security/LoginTenantResolver/LoginTenantResolver.js +52 -0
- package/features/security/LoginTenantResolver/LoginTenantResolver.js.map +1 -0
- package/features/security/LoginTenantResolver/abstractions.d.ts +38 -0
- package/features/security/LoginTenantResolver/abstractions.js +5 -0
- package/features/security/LoginTenantResolver/abstractions.js.map +1 -0
- package/features/security/LoginTenantResolver/feature.d.ts +3 -0
- package/features/security/LoginTenantResolver/feature.js +17 -0
- package/features/security/LoginTenantResolver/feature.js.map +1 -0
- package/features/security/LoginTenantResolver/index.d.ts +2 -0
- package/features/security/LoginTenantResolver/index.js +2 -0
- package/package.json +19 -19
- package/presentation/security/components/LoginNoTenantAccess.d.ts +27 -0
- package/presentation/security/components/LoginNoTenantAccess.js +22 -0
- package/presentation/security/components/LoginNoTenantAccess.js.map +1 -0
- package/presentation/security/components/LoginTenantPicker.d.ts +30 -0
- package/presentation/security/components/LoginTenantPicker.js +32 -0
- package/presentation/security/components/LoginTenantPicker.js.map +1 -0
- package/presentation/security/hooks/useAuthentication.d.ts +2 -2
- package/presentation/security/hooks/useAuthentication.js.map +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { Container } from "@webiny/di";
|
|
4
4
|
import { FormModelFeature } from "./feature.js";
|
|
@@ -8,6 +8,7 @@ import { TextFieldBuilder } from "./fieldTypes/TextFieldType.js";
|
|
|
8
8
|
import { NumberFieldBuilder } from "./fieldTypes/NumberFieldType.js";
|
|
9
9
|
import { BooleanFieldBuilder } from "./fieldTypes/BooleanFieldType.js";
|
|
10
10
|
import { DateTimeFieldBuilder } from "./fieldTypes/DateTimeFieldType.js";
|
|
11
|
+
import { DateFormatter } from "../dateFormatter/abstractions.js";
|
|
11
12
|
function createRegistry() {
|
|
12
13
|
const container = new Container();
|
|
13
14
|
IdentityContextFeature.register(container);
|
|
@@ -166,6 +167,130 @@ describe("DateTimeFieldBuilder", ()=>{
|
|
|
166
167
|
expect(subtype(new DateTimeFieldBuilder()).build("d").renderer).toBe("dateTimeInput");
|
|
167
168
|
}
|
|
168
169
|
});
|
|
170
|
+
it("renders a date-only field with the birthday picker", ()=>{
|
|
171
|
+
const config = new DateTimeFieldBuilder().dateOnly().renderer("birthdayInput").build("dob");
|
|
172
|
+
expect(config.renderer).toBe("birthdayInput");
|
|
173
|
+
expect(config.rendererSettings).toBeUndefined();
|
|
174
|
+
});
|
|
175
|
+
it("keeps a birthday picker set before dateOnly() and drops it for other subtypes", ()=>{
|
|
176
|
+
const birthday = ()=>new DateTimeFieldBuilder().renderer("birthdayInput");
|
|
177
|
+
const dateOnly = birthday().dateOnly().build("dob");
|
|
178
|
+
expect(dateOnly.renderer).toBe("birthdayInput");
|
|
179
|
+
expect(dateOnly.rendererSettings).toEqual({
|
|
180
|
+
type: "date"
|
|
181
|
+
});
|
|
182
|
+
expect(birthday().timeOnly().build("t").renderer).toBe("dateTimeInput");
|
|
183
|
+
});
|
|
184
|
+
describe("date limits", ()=>{
|
|
185
|
+
afterEach(()=>{
|
|
186
|
+
vi.useRealTimers();
|
|
187
|
+
});
|
|
188
|
+
const isValid = async (config, value)=>(await config.schema.safeParseAsync(value)).success;
|
|
189
|
+
it("hands fixed and relative limits to the renderer, whatever the call order", ()=>{
|
|
190
|
+
const config = new DateTimeFieldBuilder().dateOnly().dateGte("1900-01-01").dateLte({
|
|
191
|
+
expression: "today-18y"
|
|
192
|
+
}).renderer("birthdayInput").build("dob");
|
|
193
|
+
expect(config.rendererSettings).toEqual({
|
|
194
|
+
dateLimits: {
|
|
195
|
+
gte: [
|
|
196
|
+
"1900-01-01"
|
|
197
|
+
],
|
|
198
|
+
lte: [
|
|
199
|
+
{
|
|
200
|
+
expression: "today-18y"
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
it("checks a value against fixed limits with their messages", async ()=>{
|
|
207
|
+
const config = new DateTimeFieldBuilder().dateOnly().dateGte("1950-01-01", "Too early.").dateLte("2000-12-31", "Too late.").build("d");
|
|
208
|
+
expect(await isValid(config, "1975-06-15")).toBe(true);
|
|
209
|
+
expect(await isValid(config, "1950-01-01")).toBe(true);
|
|
210
|
+
expect(await isValid(config, "2000-12-31")).toBe(true);
|
|
211
|
+
const late = await config.schema.safeParseAsync("2005-01-01");
|
|
212
|
+
expect(late.success).toBe(false);
|
|
213
|
+
expect(late.error?.issues[0].message).toBe("Too late.");
|
|
214
|
+
const early = await config.schema.safeParseAsync("1949-12-31");
|
|
215
|
+
expect(early.error?.issues[0].message).toBe("Too early.");
|
|
216
|
+
});
|
|
217
|
+
it("works out an expression against the local date at the moment of the check", async ()=>{
|
|
218
|
+
vi.useFakeTimers();
|
|
219
|
+
vi.setSystemTime(new Date(2026, 9, 19, 12));
|
|
220
|
+
const config = new DateTimeFieldBuilder().dateOnly().dateLte({
|
|
221
|
+
expression: "today-18y"
|
|
222
|
+
}).build("dob");
|
|
223
|
+
expect(await isValid(config, "2008-10-19")).toBe(true);
|
|
224
|
+
expect(await isValid(config, "2008-10-20")).toBe(false);
|
|
225
|
+
const message = (await config.schema.safeParseAsync("2008-10-20")).error?.issues[0].message;
|
|
226
|
+
expect(message).toBe("Must be on or before 2008-10-19.");
|
|
227
|
+
vi.setSystemTime(new Date(2026, 9, 20, 12));
|
|
228
|
+
expect(await isValid(config, "2008-10-20")).toBe(true);
|
|
229
|
+
});
|
|
230
|
+
it("puts the limit into a message's {date} in the admin's date format", async ()=>{
|
|
231
|
+
vi.useFakeTimers();
|
|
232
|
+
vi.setSystemTime(new Date(2026, 9, 19, 12));
|
|
233
|
+
const formatter = {
|
|
234
|
+
format: vi.fn((date)=>`formatted ${date}`)
|
|
235
|
+
};
|
|
236
|
+
const message = async (builder, value)=>(await builder.build("d").schema.safeParseAsync(value)).error?.issues[0].message;
|
|
237
|
+
expect(await message(new DateTimeFieldBuilder(formatter).dateOnly().dateLte({
|
|
238
|
+
expression: "today-18y"
|
|
239
|
+
}, "Born on or before {date}, please."), "2010-01-01")).toBe("Born on or before formatted 2008-10-19, please.");
|
|
240
|
+
expect(formatter.format).toHaveBeenLastCalledWith("2008-10-19", {
|
|
241
|
+
hour: void 0,
|
|
242
|
+
minute: void 0,
|
|
243
|
+
timeZone: "UTC"
|
|
244
|
+
});
|
|
245
|
+
expect(await message(new DateTimeFieldBuilder(formatter).dateOnly().dateGte("1950-01-01"), "1949-12-31")).toBe("Must be on or after formatted 1950-01-01.");
|
|
246
|
+
expect(await message(new DateTimeFieldBuilder(formatter).timeOnly().dateLte("12:00:00"), "13:00:00")).toBe("Must be on or before 12:00:00.");
|
|
247
|
+
});
|
|
248
|
+
it("formats {date} with the registered DateFormatter", async ()=>{
|
|
249
|
+
const container = new Container();
|
|
250
|
+
container.registerInstance(DateFormatter, {
|
|
251
|
+
format: (date)=>`formatted ${date}`
|
|
252
|
+
});
|
|
253
|
+
IdentityContextFeature.register(container);
|
|
254
|
+
FormModelFeature.register(container);
|
|
255
|
+
const registry = container.resolve(FieldBuilderRegistry);
|
|
256
|
+
const config = registry.datetime().dateOnly().dateLte("2000-12-31").build("d");
|
|
257
|
+
const result = await config.schema.safeParseAsync("2005-01-01");
|
|
258
|
+
expect(result.error?.issues[0].message).toBe("Must be on or before formatted 2000-12-31.");
|
|
259
|
+
});
|
|
260
|
+
it("keeps the field's own schema", async ()=>{
|
|
261
|
+
const config = new DateTimeFieldBuilder().dateOnly().dateLte("2000-12-31").schema(z.string().regex(/^19/, "Must be in the 1900s.")).build("d");
|
|
262
|
+
expect(await isValid(config, "1990-01-01")).toBe(true);
|
|
263
|
+
const result = await config.schema.safeParseAsync("2000-01-01");
|
|
264
|
+
expect(result.error?.issues[0].message).toBe("Must be in the 1900s.");
|
|
265
|
+
expect(await isValid(config, "2005-01-01")).toBe(false);
|
|
266
|
+
});
|
|
267
|
+
it("checks every value of a list and lets an empty value through", async ()=>{
|
|
268
|
+
const config = new DateTimeFieldBuilder().list().dateOnly().dateGte("2000-01-01").build("d");
|
|
269
|
+
expect(await isValid(config, [
|
|
270
|
+
"2001-01-01",
|
|
271
|
+
"2002-01-01"
|
|
272
|
+
])).toBe(true);
|
|
273
|
+
expect(await isValid(config, [
|
|
274
|
+
"2001-01-01",
|
|
275
|
+
"1999-01-01"
|
|
276
|
+
])).toBe(false);
|
|
277
|
+
expect(await isValid(config, "")).toBe(true);
|
|
278
|
+
});
|
|
279
|
+
it("fails a value checked against an expression it can't read", async ()=>{
|
|
280
|
+
const config = new DateTimeFieldBuilder().dateOnly().dateLte({
|
|
281
|
+
expression: "now-18y"
|
|
282
|
+
}).build("d");
|
|
283
|
+
const result = await config.schema.safeParseAsync("2000-01-01");
|
|
284
|
+
expect(result.error?.issues[0].message).toBe('Invalid date limit "now-18y".');
|
|
285
|
+
});
|
|
286
|
+
it("leaves a field without limits as it was", ()=>{
|
|
287
|
+
const config = new DateTimeFieldBuilder().dateOnly().build("d");
|
|
288
|
+
expect(config.schema).toBeUndefined();
|
|
289
|
+
expect(config.rendererSettings).toEqual({
|
|
290
|
+
type: "date"
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
});
|
|
169
294
|
it("normalizes withTimezone values", ()=>{
|
|
170
295
|
const builder = new DateTimeFieldBuilder().withTimezone();
|
|
171
296
|
expect(builder.normalizeValue("2026-05-01T14:30:00+02:00")).toBe("2026-05-01T14:30:00+02:00");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/formModel/FieldBuilder.test.js","sources":["../../../src/features/formModel/FieldBuilder.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { z } from \"zod\";\nimport { Container } from \"@webiny/di\";\nimport { FormModelFeature } from \"./feature.js\";\nimport { IdentityContextFeature } from \"~/features/security/IdentityContext/feature.js\";\nimport { FieldBuilderRegistry, type IFieldBuilderRegistry } from \"./abstractions.js\";\nimport { TextFieldBuilder } from \"./fieldTypes/TextFieldType.js\";\nimport { NumberFieldBuilder } from \"./fieldTypes/NumberFieldType.js\";\nimport { BooleanFieldBuilder } from \"./fieldTypes/BooleanFieldType.js\";\nimport { DateTimeFieldBuilder } from \"./fieldTypes/DateTimeFieldType.js\";\n\nfunction createRegistry(): IFieldBuilderRegistry {\n const container = new Container();\n IdentityContextFeature.register(container);\n FormModelFeature.register(container);\n return container.resolve(FieldBuilderRegistry);\n}\n\ndescribe(\"TextFieldBuilder\", () => {\n it(\"should build a text field config with all fluent methods\", () => {\n const builder = new TextFieldBuilder()\n .label(\"Title\")\n .placeholder(\"Enter title\")\n .required(\"Title is required\")\n .defaultValue(\"Untitled\")\n .renderer(\"custom-text\")\n .schema(z.string().min(1));\n\n const config = builder.build(\"title\");\n\n expect(config.name).toBe(\"title\");\n expect(config.type).toBe(\"text\");\n expect(config.label).toBe(\"Title\");\n expect(config.placeholder).toBe(\"Enter title\");\n expect(config.required).toBe(true);\n expect(config.requiredMessage).toBe(\"Title is required\");\n expect(config.defaultValue).toBe(\"Untitled\");\n expect(config.renderer).toBe(\"custom-text\");\n expect(config.hidden).toBe(false);\n expect(config.disabled).toBe(false);\n expect(config.schema).toBeDefined();\n });\n\n it(\"should support hidden() method\", () => {\n const config = new TextFieldBuilder().hidden().build(\"secret\");\n expect(config.hidden).toBe(true);\n });\n\n it(\"should support disabled() method\", () => {\n const config = new TextFieldBuilder().disabled().build(\"readonly\");\n expect(config.disabled).toBe(true);\n });\n\n it(\"should default disabled to false\", () => {\n const config = new TextFieldBuilder().build(\"field\");\n expect(config.disabled).toBe(false);\n });\n});\n\ndescribe(\"TextFieldBuilder - beforeChange / afterChange\", () => {\n it(\"should store beforeChange callbacks in config\", () => {\n const cb1 = (value: unknown) => value;\n const cb2 = (value: unknown) => value;\n\n const config = new TextFieldBuilder().beforeChange(cb1).beforeChange(cb2).build(\"field\");\n\n expect(config.beforeChangeCallbacks).toHaveLength(2);\n expect(config.beforeChangeCallbacks![0]).toBe(cb1);\n expect(config.beforeChangeCallbacks![1]).toBe(cb2);\n });\n\n it(\"should store afterChange callbacks in config\", () => {\n const cb = () => {};\n const config = new TextFieldBuilder().afterChange(cb).build(\"field\");\n\n expect(config.afterChangeCallbacks).toHaveLength(1);\n expect(config.afterChangeCallbacks![0]).toBe(cb);\n });\n\n it(\"should not have callback arrays when none are added\", () => {\n const config = new TextFieldBuilder().build(\"field\");\n expect(config.beforeChangeCallbacks).toBeUndefined();\n expect(config.afterChangeCallbacks).toBeUndefined();\n });\n\n it(\"should support chaining callbacks with other builder methods\", () => {\n const config = new TextFieldBuilder()\n .label(\"Title\")\n .beforeChange(value => value)\n .required(\"Required\")\n .afterChange(() => {})\n .build(\"title\");\n\n expect(config.label).toBe(\"Title\");\n expect(config.required).toBe(true);\n expect(config.beforeChangeCallbacks).toHaveLength(1);\n expect(config.afterChangeCallbacks).toHaveLength(1);\n });\n});\n\ndescribe(\"TextFieldBuilder - options\", () => {\n it(\"should build a text field config with static options\", () => {\n const options = [\n { label: \"English\", value: \"en\" },\n { label: \"German\", value: \"de\" }\n ];\n\n const config = new TextFieldBuilder()\n .label(\"Language\")\n .options(options)\n .required()\n .build(\"language\");\n\n expect(config.name).toBe(\"language\");\n expect(config.type).toBe(\"text\");\n expect(config.label).toBe(\"Language\");\n expect(config.required).toBe(true);\n expect(config.options).toEqual(options);\n expect(config.renderer).toBe(\"select\");\n });\n\n it(\"should support reactive options function\", () => {\n const optionsFn = () => [{ label: \"A\", value: \"a\" }];\n const config = new TextFieldBuilder().options(optionsFn).build(\"dynamic\");\n expect(typeof config.options).toBe(\"function\");\n });\n\n it(\"should auto-switch renderer to dropdown when options are set\", () => {\n const config = new TextFieldBuilder().options([{ label: \"A\", value: \"a\" }]).build(\"field\");\n expect(config.renderer).toBe(\"select\");\n });\n\n it(\"should not override an explicitly set renderer\", () => {\n const config = new TextFieldBuilder()\n .renderer(\"radioButtons\")\n .options([{ label: \"A\", value: \"a\" }])\n .build(\"field\");\n expect(config.renderer).toBe(\"radioButtons\");\n });\n});\n\ndescribe(\"NumberFieldBuilder - options\", () => {\n it(\"should build a number field config with options\", () => {\n const options = [\n { label: \"One\", value: \"1\" },\n { label: \"Two\", value: \"2\" }\n ];\n\n const config = new NumberFieldBuilder().label(\"Count\").options(options).build(\"count\");\n\n expect(config.type).toBe(\"number\");\n expect(config.options).toEqual(options);\n expect(config.renderer).toBe(\"select\");\n });\n\n it(\"should not override an explicitly set renderer\", () => {\n const config = new NumberFieldBuilder()\n .renderer(\"radioButtons\")\n .options([{ label: \"1\", value: \"1\" }])\n .build(\"field\");\n expect(config.renderer).toBe(\"radioButtons\");\n });\n});\n\ndescribe(\"DateTimeFieldBuilder\", () => {\n it(\"should build a datetime field with default renderer\", () => {\n const config = new DateTimeFieldBuilder().label(\"Created\").build(\"created\");\n expect(config.type).toBe(\"datetime\");\n expect(config.renderer).toBe(\"dateTimeInput\");\n expect(config.label).toBe(\"Created\");\n });\n\n it(\"keeps the list renderer when a subtype is set after list()\", () => {\n const subtypes = [\n (b: DateTimeFieldBuilder) => b.dateOnly(),\n (b: DateTimeFieldBuilder) => b.timeOnly(),\n (b: DateTimeFieldBuilder) => b.withTimezone(),\n (b: DateTimeFieldBuilder) => b.withoutTimezone()\n ];\n for (const subtype of subtypes) {\n expect(subtype(new DateTimeFieldBuilder().list()).build(\"d\").renderer).toBe(\n \"dateTimeInputs\"\n );\n expect(subtype(new DateTimeFieldBuilder()).build(\"d\").renderer).toBe(\"dateTimeInput\");\n }\n });\n\n it(\"normalizes withTimezone values\", () => {\n const builder = new DateTimeFieldBuilder().withTimezone();\n expect(builder.normalizeValue(\"2026-05-01T14:30:00+02:00\")).toBe(\n \"2026-05-01T14:30:00+02:00\"\n );\n expect(builder.normalizeValue(\"2026-05-01T14:30-05:30\")).toBe(\"2026-05-01T14:30:00-05:30\");\n expect(builder.normalizeValue(\"2026-05-01T09:00:00.000Z\")).toBe(\n \"2026-05-01T09:00:00.000+00:00\"\n );\n expect(builder.normalizeValue(\"2026-05-01T14:30:00\")).toBe(\"2026-05-01T14:30:00\");\n });\n\n it(\"normalizes withoutTimezone values to a stable UTC instant\", () => {\n const builder = new DateTimeFieldBuilder().withoutTimezone();\n const value = \"2026-05-01T12:30:00.000Z\";\n expect(builder.normalizeValue(value)).toBe(value);\n expect(builder.normalizeValue(builder.normalizeValue(value))).toBe(value);\n });\n});\n\ndescribe(\"FieldBuilderRegistry\", () => {\n it(\"should create text builders via registry.text()\", () => {\n const registry = createRegistry();\n const builder = registry.text();\n expect(builder).toBeInstanceOf(TextFieldBuilder);\n });\n\n it(\"should create number builders via registry.number()\", () => {\n const registry = createRegistry();\n const builder = registry.number();\n expect(builder).toBeInstanceOf(NumberFieldBuilder);\n });\n\n it(\"should create boolean builders via registry.boolean()\", () => {\n const registry = createRegistry();\n const builder = registry.boolean();\n expect(builder).toBeInstanceOf(BooleanFieldBuilder);\n });\n\n it(\"should create datetime builders via registry.datetime()\", () => {\n const registry = createRegistry();\n const builder = registry.datetime();\n expect(builder).toBeInstanceOf(DateTimeFieldBuilder);\n });\n\n it(\"should support chaining on registry-created builders\", () => {\n const registry = createRegistry();\n const config = registry.text().label(\"Name\").required().build(\"name\");\n expect(config.label).toBe(\"Name\");\n expect(config.required).toBe(true);\n });\n\n it(\"should support options on text builders from registry\", () => {\n const registry = createRegistry();\n const config = registry\n .text()\n .options([{ label: \"A\", value: \"a\" }])\n .build(\"field\");\n expect(config.options).toHaveLength(1);\n expect(config.renderer).toBe(\"select\");\n });\n});\n\ndescribe(\"normalizeValue\", () => {\n it(\"should be identity for TextFieldBuilder\", () => {\n const builder = new TextFieldBuilder();\n expect(builder.normalizeValue(\"hello\")).toBe(\"hello\");\n expect(builder.normalizeValue(42)).toBe(42);\n expect(builder.normalizeValue(null)).toBe(null);\n });\n\n it(\"should coerce string to number for NumberFieldBuilder\", () => {\n const builder = new NumberFieldBuilder();\n expect(builder.normalizeValue(\"42\")).toBe(42);\n expect(builder.normalizeValue(\"3.14\")).toBe(3.14);\n expect(builder.normalizeValue(\"0\")).toBe(0);\n });\n\n it(\"should return null for non-numeric values in NumberFieldBuilder\", () => {\n const builder = new NumberFieldBuilder();\n expect(builder.normalizeValue(null)).toBe(null);\n expect(builder.normalizeValue(undefined)).toBe(null);\n expect(builder.normalizeValue(\"\")).toBe(null);\n expect(builder.normalizeValue(\"abc\")).toBe(null);\n });\n\n it(\"should coerce to boolean for BooleanFieldBuilder\", () => {\n const builder = new BooleanFieldBuilder();\n expect(builder.normalizeValue(1)).toBe(true);\n expect(builder.normalizeValue(0)).toBe(false);\n expect(builder.normalizeValue(\"\")).toBe(false);\n expect(builder.normalizeValue(\"yes\")).toBe(true);\n expect(builder.normalizeValue(null)).toBe(false);\n });\n\n it(\"should be identity for DateTimeFieldBuilder\", () => {\n const builder = new DateTimeFieldBuilder();\n expect(builder.normalizeValue(\"2024-01-01\")).toBe(\"2024-01-01\");\n expect(builder.normalizeValue(null)).toBe(null);\n });\n\n it(\"should capture normalizeValue into config at build time\", () => {\n const config = new NumberFieldBuilder().label(\"Count\").build(\"count\");\n expect(config.normalizeValue).toBeDefined();\n expect(config.normalizeValue!(\"42\")).toBe(42);\n expect(config.normalizeValue!(null)).toBe(null);\n });\n});\n"],"names":["createRegistry","container","Container","IdentityContextFeature","FormModelFeature","FieldBuilderRegistry","describe","it","builder","TextFieldBuilder","z","config","expect","cb1","value","cb2","cb","options","optionsFn","NumberFieldBuilder","DateTimeFieldBuilder","subtypes","b","subtype","registry","BooleanFieldBuilder","undefined"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"features/formModel/FieldBuilder.test.js","sources":["../../../src/features/formModel/FieldBuilder.test.ts"],"sourcesContent":["import { afterEach, describe, it, expect, vi } from \"vitest\";\nimport { z } from \"zod\";\nimport { Container } from \"@webiny/di\";\nimport { FormModelFeature } from \"./feature.js\";\nimport { IdentityContextFeature } from \"~/features/security/IdentityContext/feature.js\";\nimport { FieldBuilderRegistry, type IFieldBuilderRegistry } from \"./abstractions.js\";\nimport { TextFieldBuilder } from \"./fieldTypes/TextFieldType.js\";\nimport { NumberFieldBuilder } from \"./fieldTypes/NumberFieldType.js\";\nimport { BooleanFieldBuilder } from \"./fieldTypes/BooleanFieldType.js\";\nimport { DateTimeFieldBuilder } from \"./fieldTypes/DateTimeFieldType.js\";\nimport { DateFormatter } from \"~/features/dateFormatter/abstractions.js\";\n\nfunction createRegistry(): IFieldBuilderRegistry {\n const container = new Container();\n IdentityContextFeature.register(container);\n FormModelFeature.register(container);\n return container.resolve(FieldBuilderRegistry);\n}\n\ndescribe(\"TextFieldBuilder\", () => {\n it(\"should build a text field config with all fluent methods\", () => {\n const builder = new TextFieldBuilder()\n .label(\"Title\")\n .placeholder(\"Enter title\")\n .required(\"Title is required\")\n .defaultValue(\"Untitled\")\n .renderer(\"custom-text\")\n .schema(z.string().min(1));\n\n const config = builder.build(\"title\");\n\n expect(config.name).toBe(\"title\");\n expect(config.type).toBe(\"text\");\n expect(config.label).toBe(\"Title\");\n expect(config.placeholder).toBe(\"Enter title\");\n expect(config.required).toBe(true);\n expect(config.requiredMessage).toBe(\"Title is required\");\n expect(config.defaultValue).toBe(\"Untitled\");\n expect(config.renderer).toBe(\"custom-text\");\n expect(config.hidden).toBe(false);\n expect(config.disabled).toBe(false);\n expect(config.schema).toBeDefined();\n });\n\n it(\"should support hidden() method\", () => {\n const config = new TextFieldBuilder().hidden().build(\"secret\");\n expect(config.hidden).toBe(true);\n });\n\n it(\"should support disabled() method\", () => {\n const config = new TextFieldBuilder().disabled().build(\"readonly\");\n expect(config.disabled).toBe(true);\n });\n\n it(\"should default disabled to false\", () => {\n const config = new TextFieldBuilder().build(\"field\");\n expect(config.disabled).toBe(false);\n });\n});\n\ndescribe(\"TextFieldBuilder - beforeChange / afterChange\", () => {\n it(\"should store beforeChange callbacks in config\", () => {\n const cb1 = (value: unknown) => value;\n const cb2 = (value: unknown) => value;\n\n const config = new TextFieldBuilder().beforeChange(cb1).beforeChange(cb2).build(\"field\");\n\n expect(config.beforeChangeCallbacks).toHaveLength(2);\n expect(config.beforeChangeCallbacks![0]).toBe(cb1);\n expect(config.beforeChangeCallbacks![1]).toBe(cb2);\n });\n\n it(\"should store afterChange callbacks in config\", () => {\n const cb = () => {};\n const config = new TextFieldBuilder().afterChange(cb).build(\"field\");\n\n expect(config.afterChangeCallbacks).toHaveLength(1);\n expect(config.afterChangeCallbacks![0]).toBe(cb);\n });\n\n it(\"should not have callback arrays when none are added\", () => {\n const config = new TextFieldBuilder().build(\"field\");\n expect(config.beforeChangeCallbacks).toBeUndefined();\n expect(config.afterChangeCallbacks).toBeUndefined();\n });\n\n it(\"should support chaining callbacks with other builder methods\", () => {\n const config = new TextFieldBuilder()\n .label(\"Title\")\n .beforeChange(value => value)\n .required(\"Required\")\n .afterChange(() => {})\n .build(\"title\");\n\n expect(config.label).toBe(\"Title\");\n expect(config.required).toBe(true);\n expect(config.beforeChangeCallbacks).toHaveLength(1);\n expect(config.afterChangeCallbacks).toHaveLength(1);\n });\n});\n\ndescribe(\"TextFieldBuilder - options\", () => {\n it(\"should build a text field config with static options\", () => {\n const options = [\n { label: \"English\", value: \"en\" },\n { label: \"German\", value: \"de\" }\n ];\n\n const config = new TextFieldBuilder()\n .label(\"Language\")\n .options(options)\n .required()\n .build(\"language\");\n\n expect(config.name).toBe(\"language\");\n expect(config.type).toBe(\"text\");\n expect(config.label).toBe(\"Language\");\n expect(config.required).toBe(true);\n expect(config.options).toEqual(options);\n expect(config.renderer).toBe(\"select\");\n });\n\n it(\"should support reactive options function\", () => {\n const optionsFn = () => [{ label: \"A\", value: \"a\" }];\n const config = new TextFieldBuilder().options(optionsFn).build(\"dynamic\");\n expect(typeof config.options).toBe(\"function\");\n });\n\n it(\"should auto-switch renderer to dropdown when options are set\", () => {\n const config = new TextFieldBuilder().options([{ label: \"A\", value: \"a\" }]).build(\"field\");\n expect(config.renderer).toBe(\"select\");\n });\n\n it(\"should not override an explicitly set renderer\", () => {\n const config = new TextFieldBuilder()\n .renderer(\"radioButtons\")\n .options([{ label: \"A\", value: \"a\" }])\n .build(\"field\");\n expect(config.renderer).toBe(\"radioButtons\");\n });\n});\n\ndescribe(\"NumberFieldBuilder - options\", () => {\n it(\"should build a number field config with options\", () => {\n const options = [\n { label: \"One\", value: \"1\" },\n { label: \"Two\", value: \"2\" }\n ];\n\n const config = new NumberFieldBuilder().label(\"Count\").options(options).build(\"count\");\n\n expect(config.type).toBe(\"number\");\n expect(config.options).toEqual(options);\n expect(config.renderer).toBe(\"select\");\n });\n\n it(\"should not override an explicitly set renderer\", () => {\n const config = new NumberFieldBuilder()\n .renderer(\"radioButtons\")\n .options([{ label: \"1\", value: \"1\" }])\n .build(\"field\");\n expect(config.renderer).toBe(\"radioButtons\");\n });\n});\n\ndescribe(\"DateTimeFieldBuilder\", () => {\n it(\"should build a datetime field with default renderer\", () => {\n const config = new DateTimeFieldBuilder().label(\"Created\").build(\"created\");\n expect(config.type).toBe(\"datetime\");\n expect(config.renderer).toBe(\"dateTimeInput\");\n expect(config.label).toBe(\"Created\");\n });\n\n it(\"keeps the list renderer when a subtype is set after list()\", () => {\n const subtypes = [\n (b: DateTimeFieldBuilder) => b.dateOnly(),\n (b: DateTimeFieldBuilder) => b.timeOnly(),\n (b: DateTimeFieldBuilder) => b.withTimezone(),\n (b: DateTimeFieldBuilder) => b.withoutTimezone()\n ];\n for (const subtype of subtypes) {\n expect(subtype(new DateTimeFieldBuilder().list()).build(\"d\").renderer).toBe(\n \"dateTimeInputs\"\n );\n expect(subtype(new DateTimeFieldBuilder()).build(\"d\").renderer).toBe(\"dateTimeInput\");\n }\n });\n\n it(\"renders a date-only field with the birthday picker\", () => {\n const config = new DateTimeFieldBuilder().dateOnly().renderer(\"birthdayInput\").build(\"dob\");\n expect(config.renderer).toBe(\"birthdayInput\");\n expect(config.rendererSettings).toBeUndefined();\n });\n\n it(\"keeps a birthday picker set before dateOnly() and drops it for other subtypes\", () => {\n const birthday = () => new DateTimeFieldBuilder().renderer(\"birthdayInput\");\n const dateOnly = birthday().dateOnly().build(\"dob\");\n expect(dateOnly.renderer).toBe(\"birthdayInput\");\n expect(dateOnly.rendererSettings).toEqual({ type: \"date\" });\n\n expect(birthday().timeOnly().build(\"t\").renderer).toBe(\"dateTimeInput\");\n });\n\n describe(\"date limits\", () => {\n afterEach(() => {\n vi.useRealTimers();\n });\n\n const isValid = async (config: { schema?: z.ZodTypeAny }, value: unknown) =>\n (await config.schema!.safeParseAsync(value)).success;\n\n it(\"hands fixed and relative limits to the renderer, whatever the call order\", () => {\n const config = new DateTimeFieldBuilder()\n .dateOnly()\n .dateGte(\"1900-01-01\")\n .dateLte({ expression: \"today-18y\" })\n .renderer(\"birthdayInput\")\n .build(\"dob\");\n expect(config.rendererSettings).toEqual({\n dateLimits: { gte: [\"1900-01-01\"], lte: [{ expression: \"today-18y\" }] }\n });\n });\n\n it(\"checks a value against fixed limits with their messages\", async () => {\n const config = new DateTimeFieldBuilder()\n .dateOnly()\n .dateGte(\"1950-01-01\", \"Too early.\")\n .dateLte(\"2000-12-31\", \"Too late.\")\n .build(\"d\");\n expect(await isValid(config, \"1975-06-15\")).toBe(true);\n expect(await isValid(config, \"1950-01-01\")).toBe(true);\n expect(await isValid(config, \"2000-12-31\")).toBe(true);\n\n const late = await config.schema!.safeParseAsync(\"2005-01-01\");\n expect(late.success).toBe(false);\n expect(late.error?.issues[0].message).toBe(\"Too late.\");\n\n const early = await config.schema!.safeParseAsync(\"1949-12-31\");\n expect(early.error?.issues[0].message).toBe(\"Too early.\");\n });\n\n it(\"works out an expression against the local date at the moment of the check\", async () => {\n vi.useFakeTimers();\n vi.setSystemTime(new Date(2026, 9, 19, 12));\n const config = new DateTimeFieldBuilder()\n .dateOnly()\n .dateLte({ expression: \"today-18y\" })\n .build(\"dob\");\n expect(await isValid(config, \"2008-10-19\")).toBe(true);\n expect(await isValid(config, \"2008-10-20\")).toBe(false);\n\n const message = (await config.schema!.safeParseAsync(\"2008-10-20\")).error?.issues[0]\n .message;\n expect(message).toBe(\"Must be on or before 2008-10-19.\");\n\n // The next day, the same form allows the next birthday.\n vi.setSystemTime(new Date(2026, 9, 20, 12));\n expect(await isValid(config, \"2008-10-20\")).toBe(true);\n });\n\n it(\"puts the limit into a message's {date} in the admin's date format\", async () => {\n vi.useFakeTimers();\n vi.setSystemTime(new Date(2026, 9, 19, 12));\n const formatter = { format: vi.fn((date: unknown) => `formatted ${date}`) };\n const message = async (builder: DateTimeFieldBuilder, value: string) =>\n (await builder.build(\"d\").schema!.safeParseAsync(value)).error?.issues[0].message;\n\n expect(\n await message(\n new DateTimeFieldBuilder(formatter)\n .dateOnly()\n .dateLte({ expression: \"today-18y\" }, \"Born on or before {date}, please.\"),\n \"2010-01-01\"\n )\n ).toBe(\"Born on or before formatted 2008-10-19, please.\");\n // A date-only limit is shown without a time, and in UTC so it keeps its day.\n expect(formatter.format).toHaveBeenLastCalledWith(\"2008-10-19\", {\n hour: undefined,\n minute: undefined,\n timeZone: \"UTC\"\n });\n\n expect(\n await message(\n new DateTimeFieldBuilder(formatter).dateOnly().dateGte(\"1950-01-01\"),\n \"1949-12-31\"\n )\n ).toBe(\"Must be on or after formatted 1950-01-01.\");\n\n // A time has no date to format.\n expect(\n await message(\n new DateTimeFieldBuilder(formatter).timeOnly().dateLte(\"12:00:00\"),\n \"13:00:00\"\n )\n ).toBe(\"Must be on or before 12:00:00.\");\n });\n\n it(\"formats {date} with the registered DateFormatter\", async () => {\n const container = new Container();\n container.registerInstance(DateFormatter, {\n format: (date: unknown) => `formatted ${date}`\n });\n IdentityContextFeature.register(container);\n FormModelFeature.register(container);\n const registry = container.resolve(FieldBuilderRegistry);\n const config = registry.datetime().dateOnly().dateLte(\"2000-12-31\").build(\"d\");\n const result = await config.schema!.safeParseAsync(\"2005-01-01\");\n expect(result.error?.issues[0].message).toBe(\n \"Must be on or before formatted 2000-12-31.\"\n );\n });\n\n it(\"keeps the field's own schema\", async () => {\n const config = new DateTimeFieldBuilder()\n .dateOnly()\n .dateLte(\"2000-12-31\")\n .schema(z.string().regex(/^19/, \"Must be in the 1900s.\"))\n .build(\"d\");\n expect(await isValid(config, \"1990-01-01\")).toBe(true);\n const result = await config.schema!.safeParseAsync(\"2000-01-01\");\n expect(result.error?.issues[0].message).toBe(\"Must be in the 1900s.\");\n expect(await isValid(config, \"2005-01-01\")).toBe(false);\n });\n\n it(\"checks every value of a list and lets an empty value through\", async () => {\n const config = new DateTimeFieldBuilder()\n .list()\n .dateOnly()\n .dateGte(\"2000-01-01\")\n .build(\"d\");\n expect(await isValid(config, [\"2001-01-01\", \"2002-01-01\"])).toBe(true);\n expect(await isValid(config, [\"2001-01-01\", \"1999-01-01\"])).toBe(false);\n expect(await isValid(config, \"\")).toBe(true);\n });\n\n it(\"fails a value checked against an expression it can't read\", async () => {\n const config = new DateTimeFieldBuilder()\n .dateOnly()\n .dateLte({ expression: \"now-18y\" })\n .build(\"d\");\n const result = await config.schema!.safeParseAsync(\"2000-01-01\");\n expect(result.error?.issues[0].message).toBe('Invalid date limit \"now-18y\".');\n });\n\n it(\"leaves a field without limits as it was\", () => {\n const config = new DateTimeFieldBuilder().dateOnly().build(\"d\");\n expect(config.schema).toBeUndefined();\n expect(config.rendererSettings).toEqual({ type: \"date\" });\n });\n });\n\n it(\"normalizes withTimezone values\", () => {\n const builder = new DateTimeFieldBuilder().withTimezone();\n expect(builder.normalizeValue(\"2026-05-01T14:30:00+02:00\")).toBe(\n \"2026-05-01T14:30:00+02:00\"\n );\n expect(builder.normalizeValue(\"2026-05-01T14:30-05:30\")).toBe(\"2026-05-01T14:30:00-05:30\");\n expect(builder.normalizeValue(\"2026-05-01T09:00:00.000Z\")).toBe(\n \"2026-05-01T09:00:00.000+00:00\"\n );\n expect(builder.normalizeValue(\"2026-05-01T14:30:00\")).toBe(\"2026-05-01T14:30:00\");\n });\n\n it(\"normalizes withoutTimezone values to a stable UTC instant\", () => {\n const builder = new DateTimeFieldBuilder().withoutTimezone();\n const value = \"2026-05-01T12:30:00.000Z\";\n expect(builder.normalizeValue(value)).toBe(value);\n expect(builder.normalizeValue(builder.normalizeValue(value))).toBe(value);\n });\n});\n\ndescribe(\"FieldBuilderRegistry\", () => {\n it(\"should create text builders via registry.text()\", () => {\n const registry = createRegistry();\n const builder = registry.text();\n expect(builder).toBeInstanceOf(TextFieldBuilder);\n });\n\n it(\"should create number builders via registry.number()\", () => {\n const registry = createRegistry();\n const builder = registry.number();\n expect(builder).toBeInstanceOf(NumberFieldBuilder);\n });\n\n it(\"should create boolean builders via registry.boolean()\", () => {\n const registry = createRegistry();\n const builder = registry.boolean();\n expect(builder).toBeInstanceOf(BooleanFieldBuilder);\n });\n\n it(\"should create datetime builders via registry.datetime()\", () => {\n const registry = createRegistry();\n const builder = registry.datetime();\n expect(builder).toBeInstanceOf(DateTimeFieldBuilder);\n });\n\n it(\"should support chaining on registry-created builders\", () => {\n const registry = createRegistry();\n const config = registry.text().label(\"Name\").required().build(\"name\");\n expect(config.label).toBe(\"Name\");\n expect(config.required).toBe(true);\n });\n\n it(\"should support options on text builders from registry\", () => {\n const registry = createRegistry();\n const config = registry\n .text()\n .options([{ label: \"A\", value: \"a\" }])\n .build(\"field\");\n expect(config.options).toHaveLength(1);\n expect(config.renderer).toBe(\"select\");\n });\n});\n\ndescribe(\"normalizeValue\", () => {\n it(\"should be identity for TextFieldBuilder\", () => {\n const builder = new TextFieldBuilder();\n expect(builder.normalizeValue(\"hello\")).toBe(\"hello\");\n expect(builder.normalizeValue(42)).toBe(42);\n expect(builder.normalizeValue(null)).toBe(null);\n });\n\n it(\"should coerce string to number for NumberFieldBuilder\", () => {\n const builder = new NumberFieldBuilder();\n expect(builder.normalizeValue(\"42\")).toBe(42);\n expect(builder.normalizeValue(\"3.14\")).toBe(3.14);\n expect(builder.normalizeValue(\"0\")).toBe(0);\n });\n\n it(\"should return null for non-numeric values in NumberFieldBuilder\", () => {\n const builder = new NumberFieldBuilder();\n expect(builder.normalizeValue(null)).toBe(null);\n expect(builder.normalizeValue(undefined)).toBe(null);\n expect(builder.normalizeValue(\"\")).toBe(null);\n expect(builder.normalizeValue(\"abc\")).toBe(null);\n });\n\n it(\"should coerce to boolean for BooleanFieldBuilder\", () => {\n const builder = new BooleanFieldBuilder();\n expect(builder.normalizeValue(1)).toBe(true);\n expect(builder.normalizeValue(0)).toBe(false);\n expect(builder.normalizeValue(\"\")).toBe(false);\n expect(builder.normalizeValue(\"yes\")).toBe(true);\n expect(builder.normalizeValue(null)).toBe(false);\n });\n\n it(\"should be identity for DateTimeFieldBuilder\", () => {\n const builder = new DateTimeFieldBuilder();\n expect(builder.normalizeValue(\"2024-01-01\")).toBe(\"2024-01-01\");\n expect(builder.normalizeValue(null)).toBe(null);\n });\n\n it(\"should capture normalizeValue into config at build time\", () => {\n const config = new NumberFieldBuilder().label(\"Count\").build(\"count\");\n expect(config.normalizeValue).toBeDefined();\n expect(config.normalizeValue!(\"42\")).toBe(42);\n expect(config.normalizeValue!(null)).toBe(null);\n });\n});\n"],"names":["createRegistry","container","Container","IdentityContextFeature","FormModelFeature","FieldBuilderRegistry","describe","it","builder","TextFieldBuilder","z","config","expect","cb1","value","cb2","cb","options","optionsFn","NumberFieldBuilder","DateTimeFieldBuilder","subtypes","b","subtype","birthday","dateOnly","afterEach","vi","isValid","late","early","Date","message","formatter","date","undefined","DateFormatter","registry","result","BooleanFieldBuilder"],"mappings":"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"}
|
|
@@ -1,18 +1,41 @@
|
|
|
1
|
-
import { type IFieldTypeFactory } from "../abstractions.js";
|
|
1
|
+
import { type IFieldConfig, type IFieldTypeFactory } from "../abstractions.js";
|
|
2
2
|
import { FieldBuilder } from "../FieldBuilder.js";
|
|
3
|
+
import { DateFormatter } from "../../../features/dateFormatter/abstractions.js";
|
|
4
|
+
import { type DateBound } from "./dateLimits.js";
|
|
3
5
|
export declare class DateTimeFieldBuilder extends FieldBuilder<"datetime"> {
|
|
6
|
+
private readonly dateFormatter?;
|
|
4
7
|
private _subtype;
|
|
5
|
-
|
|
8
|
+
private _dateGte;
|
|
9
|
+
private _dateLte;
|
|
10
|
+
/**
|
|
11
|
+
* @param dateFormatter shows a limit in its message's `{date}` in the admin's date format;
|
|
12
|
+
* without it, the limit shows as it is stored.
|
|
13
|
+
*/
|
|
14
|
+
constructor(dateFormatter?: DateFormatter.Interface | undefined);
|
|
6
15
|
list(): this;
|
|
7
16
|
/** Subtype methods reset the renderer; a list field keeps the list renderer. */
|
|
8
17
|
private _defaultRenderer;
|
|
9
18
|
/**
|
|
10
19
|
* Calendar date only.
|
|
11
|
-
* @example
|
|
20
|
+
* @example
|
|
21
|
+
* datetime().dateOnly()
|
|
22
|
+
* // day, then month, then year; suits dates far in the past, like a date of birth.
|
|
23
|
+
* // The picker offers only the dates the `dateGte`/`dateLte` limits allow.
|
|
24
|
+
* datetime()
|
|
25
|
+
* .dateOnly()
|
|
26
|
+
* .dateGte({ expression: "today-100y" })
|
|
27
|
+
* .dateLte({ expression: "today-18y" }, "You must be 18 or older.")
|
|
28
|
+
* .renderer("birthdayInput")
|
|
12
29
|
*
|
|
13
30
|
* Value format: `"2026-05-01"` (YYYY-MM-DD)
|
|
14
31
|
*/
|
|
15
32
|
dateOnly(): this;
|
|
33
|
+
/**
|
|
34
|
+
* The CMS form sets a field's renderer before its subtype, so a birthday picker set
|
|
35
|
+
* earlier survives `dateOnly()`, since it renders the same value. Every other subtype
|
|
36
|
+
* resets it.
|
|
37
|
+
*/
|
|
38
|
+
private _isBirthdayPicker;
|
|
16
39
|
/**
|
|
17
40
|
* Time only, no date component.
|
|
18
41
|
* @example datetime().timeOnly()
|
|
@@ -109,6 +132,38 @@ export declare class DateTimeFieldBuilder extends FieldBuilder<"datetime"> {
|
|
|
109
132
|
label: string;
|
|
110
133
|
value: () => Date;
|
|
111
134
|
}>): this;
|
|
135
|
+
/**
|
|
136
|
+
* The value must be on or after the given one: a fixed value, or, on a date-only field, an
|
|
137
|
+
* expression worked out from the editor's local date every time the value is checked.
|
|
138
|
+
* Expressions: `today`, or `today` plus or minus a number of days (`d`), months (`m`) or
|
|
139
|
+
* years (`y`). The birthday picker offers only the dates the limits allow.
|
|
140
|
+
* @example
|
|
141
|
+
* datetime().dateOnly().dateGte("1900-01-01")
|
|
142
|
+
* datetime().dateOnly().dateGte({ expression: "today-100y" }) // not older than 100 years
|
|
143
|
+
* datetime().dateOnly().dateGte({ expression: "today+1d" }, "Pick a date in the future.")
|
|
144
|
+
*/
|
|
145
|
+
dateGte(bound: DateBound, message?: string): this;
|
|
146
|
+
/**
|
|
147
|
+
* The value must be on or before the given one: a fixed value, or, on a date-only field, an
|
|
148
|
+
* expression worked out from the editor's local date every time the value is checked.
|
|
149
|
+
* In `message`, `{date}` is the limit in the admin's date format; the default message is
|
|
150
|
+
* "Must be on or before {date}." (`dateGte`: "Must be on or after {date}.").
|
|
151
|
+
* @example
|
|
152
|
+
* datetime().dateOnly().dateLte("2026-12-31")
|
|
153
|
+
* datetime()
|
|
154
|
+
* .dateOnly()
|
|
155
|
+
* .dateLte({ expression: "today-18y" }, "You must be 18 or older.")
|
|
156
|
+
* .renderer("birthdayInput")
|
|
157
|
+
*/
|
|
158
|
+
dateLte(bound: DateBound, message?: string): this;
|
|
159
|
+
/**
|
|
160
|
+
* The limits are applied here, so a `renderer()` or `schema()` call after `dateGte()` or
|
|
161
|
+
* `dateLte()` doesn't drop them: the check runs after the field's own schema, and the
|
|
162
|
+
* renderer finds them in `rendererSettings.dateLimits`.
|
|
163
|
+
*/
|
|
164
|
+
build(name: string): IFieldConfig;
|
|
165
|
+
/** A limit in the admin's date format: the date alone on a date-only field. */
|
|
166
|
+
private _formatDateLimit;
|
|
112
167
|
/**
|
|
113
168
|
* Customize the display format shown in the picker trigger.
|
|
114
169
|
* Uses date-fns format tokens.
|
|
@@ -121,7 +176,9 @@ export declare class DateTimeFieldBuilder extends FieldBuilder<"datetime"> {
|
|
|
121
176
|
normalizeValue(value: unknown): unknown;
|
|
122
177
|
}
|
|
123
178
|
declare class DateTimeFieldTypeFactory implements IFieldTypeFactory {
|
|
179
|
+
private readonly dateFormatter?;
|
|
124
180
|
readonly type = "datetime";
|
|
181
|
+
constructor(dateFormatter?: DateFormatter.Interface | undefined);
|
|
125
182
|
create(_registry: any): DateTimeFieldBuilder;
|
|
126
183
|
}
|
|
127
184
|
export declare const DateTimeFieldType: typeof DateTimeFieldTypeFactory & {
|
|
@@ -148,6 +205,8 @@ declare module "../abstractions.js" {
|
|
|
148
205
|
* Configuration methods (chain after variant):
|
|
149
206
|
* - `.presets([...])` — quick-select buttons (all variants)
|
|
150
207
|
* - `.displayFormat("MM/dd/yyyy")` — customize trigger display
|
|
208
|
+
* - `.dateGte(bound)` / `.dateLte(bound)` — limits: a fixed value, or
|
|
209
|
+
* `{ expression: "today-18y" }` on a date-only field
|
|
151
210
|
*
|
|
152
211
|
* Variant-specific options (passed to variant method):
|
|
153
212
|
* - `yearOnly({ range: [2020, 2030] })` — year range
|
|
@@ -156,7 +215,9 @@ declare module "../abstractions.js" {
|
|
|
156
215
|
*
|
|
157
216
|
* @example
|
|
158
217
|
* datetime() // full UTC datetime (default)
|
|
159
|
-
* datetime().dateOnly() //
|
|
218
|
+
* datetime().dateOnly() // due dates, holidays
|
|
219
|
+
* datetime().dateOnly().renderer("birthdayInput") // dates of birth
|
|
220
|
+
* datetime().dateOnly().dateLte({ expression: "today-18y" }) // 18 or older
|
|
160
221
|
* datetime().timeOnly() // opening hours, daily reminders
|
|
161
222
|
* datetime().withTimezone() // events tied to a specific locale
|
|
162
223
|
* datetime().withoutTimezone() // timestamps, logs, sortable moments
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
1
2
|
import { FieldType } from "../abstractions.js";
|
|
2
3
|
import { FieldBuilder } from "../FieldBuilder.js";
|
|
4
|
+
import { DateFormatter } from "../../dateFormatter/abstractions.js";
|
|
5
|
+
import { createDateLimitsSchema } from "./dateLimits.js";
|
|
3
6
|
class DateTimeFieldBuilder extends FieldBuilder {
|
|
4
|
-
constructor(){
|
|
5
|
-
super("datetime"), this._subtype = "date";
|
|
7
|
+
constructor(dateFormatter){
|
|
8
|
+
super("datetime"), this.dateFormatter = dateFormatter, this._subtype = "date", this._dateGte = [], this._dateLte = [];
|
|
6
9
|
this._config.renderer = "dateTimeInput";
|
|
7
10
|
this._config.rendererSettings = {
|
|
8
11
|
type: "date"
|
|
@@ -18,13 +21,16 @@ class DateTimeFieldBuilder extends FieldBuilder {
|
|
|
18
21
|
}
|
|
19
22
|
dateOnly() {
|
|
20
23
|
this._subtype = "date";
|
|
21
|
-
this._config.renderer = this._defaultRenderer();
|
|
24
|
+
this._config.renderer = this._isBirthdayPicker() && !this._config.isList ? "birthdayInput" : this._defaultRenderer();
|
|
22
25
|
this._config.rendererSettings = {
|
|
23
26
|
...this._config.rendererSettings,
|
|
24
27
|
type: "date"
|
|
25
28
|
};
|
|
26
29
|
return this;
|
|
27
30
|
}
|
|
31
|
+
_isBirthdayPicker() {
|
|
32
|
+
return "birthdayInput" === this._config.renderer;
|
|
33
|
+
}
|
|
28
34
|
timeOnly() {
|
|
29
35
|
this._subtype = "time";
|
|
30
36
|
this._config.renderer = this._defaultRenderer();
|
|
@@ -131,6 +137,54 @@ class DateTimeFieldBuilder extends FieldBuilder {
|
|
|
131
137
|
};
|
|
132
138
|
return this;
|
|
133
139
|
}
|
|
140
|
+
dateGte(bound, message) {
|
|
141
|
+
this._dateGte.push({
|
|
142
|
+
bound,
|
|
143
|
+
message
|
|
144
|
+
});
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
dateLte(bound, message) {
|
|
148
|
+
this._dateLte.push({
|
|
149
|
+
bound,
|
|
150
|
+
message
|
|
151
|
+
});
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
build(name) {
|
|
155
|
+
const config = super.build(name);
|
|
156
|
+
if (0 === this._dateGte.length && 0 === this._dateLte.length) return config;
|
|
157
|
+
const dateLimits = {
|
|
158
|
+
gte: this._dateGte.map((limit)=>limit.bound),
|
|
159
|
+
lte: this._dateLte.map((limit)=>limit.bound)
|
|
160
|
+
};
|
|
161
|
+
const limitsSchema = createDateLimitsSchema(this._dateGte, this._dateLte, this._formatDateLimit());
|
|
162
|
+
return {
|
|
163
|
+
...config,
|
|
164
|
+
rendererSettings: {
|
|
165
|
+
...config.rendererSettings,
|
|
166
|
+
dateLimits
|
|
167
|
+
},
|
|
168
|
+
schema: config.schema ? z.intersection(config.schema, limitsSchema) : limitsSchema
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
_formatDateLimit() {
|
|
172
|
+
const formatter = this.dateFormatter;
|
|
173
|
+
if (!formatter) return;
|
|
174
|
+
switch(this._subtype){
|
|
175
|
+
case "date":
|
|
176
|
+
return (date)=>formatter.format(date, {
|
|
177
|
+
hour: void 0,
|
|
178
|
+
minute: void 0,
|
|
179
|
+
timeZone: "UTC"
|
|
180
|
+
});
|
|
181
|
+
case "dateTimeWithTimezone":
|
|
182
|
+
case "dateTimeWithoutTimezone":
|
|
183
|
+
return (date)=>formatter.format(date);
|
|
184
|
+
default:
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
134
188
|
displayFormat(format) {
|
|
135
189
|
this._config.rendererSettings = {
|
|
136
190
|
...this._config.rendererSettings,
|
|
@@ -234,16 +288,24 @@ function normalizeMultipleYears(value) {
|
|
|
234
288
|
});
|
|
235
289
|
}
|
|
236
290
|
class DateTimeFieldTypeFactory {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
constructor(){
|
|
291
|
+
constructor(dateFormatter){
|
|
292
|
+
this.dateFormatter = dateFormatter;
|
|
241
293
|
this.type = "datetime";
|
|
242
294
|
}
|
|
295
|
+
create(_registry) {
|
|
296
|
+
return new DateTimeFieldBuilder(this.dateFormatter);
|
|
297
|
+
}
|
|
243
298
|
}
|
|
244
299
|
const DateTimeFieldType = FieldType.createImplementation({
|
|
245
300
|
implementation: DateTimeFieldTypeFactory,
|
|
246
|
-
dependencies: [
|
|
301
|
+
dependencies: [
|
|
302
|
+
[
|
|
303
|
+
DateFormatter,
|
|
304
|
+
{
|
|
305
|
+
optional: true
|
|
306
|
+
}
|
|
307
|
+
]
|
|
308
|
+
]
|
|
247
309
|
});
|
|
248
310
|
export { DateTimeFieldBuilder, DateTimeFieldType };
|
|
249
311
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/formModel/fieldTypes/DateTimeFieldType.js","sources":["../../../../src/features/formModel/fieldTypes/DateTimeFieldType.ts"],"sourcesContent":["import { FieldType, type IFieldTypeFactory } from \"../abstractions.js\";\nimport { FieldBuilder } from \"../FieldBuilder.js\";\n\ntype DateTimeSubtype =\n | \"date\"\n | \"time\"\n | \"dateTimeWithTimezone\"\n | \"dateTimeWithoutTimezone\"\n | \"month\"\n | \"week\"\n | \"year\"\n | \"dateRange\"\n | \"multipleDates\"\n | \"multipleMonths\"\n | \"multipleYears\";\n\nexport class DateTimeFieldBuilder extends FieldBuilder<\"datetime\"> {\n private _subtype: DateTimeSubtype = \"date\";\n\n constructor() {\n super(\"datetime\");\n this._config.renderer = \"dateTimeInput\";\n this._config.rendererSettings = { type: \"date\" };\n }\n\n override list(): this {\n super.list();\n this._config.renderer = \"dateTimeInputs\";\n return this;\n }\n\n /** Subtype methods reset the renderer; a list field keeps the list renderer. */\n private _defaultRenderer(): \"dateTimeInput\" | \"dateTimeInputs\" {\n return this._config.isList ? \"dateTimeInputs\" : \"dateTimeInput\";\n }\n\n /**\n * Calendar date only.\n * @example datetime().dateOnly()\n *\n * Value format: `\"2026-05-01\"` (YYYY-MM-DD)\n */\n dateOnly(): this {\n this._subtype = \"date\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = { ...this._config.rendererSettings, type: \"date\" };\n return this;\n }\n\n /**\n * Time only, no date component.\n * @example datetime().timeOnly()\n *\n * Value format: `\"14:30:00\"` (HH:mm:ss)\n */\n timeOnly(): this {\n this._subtype = \"time\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = { ...this._config.rendererSettings, type: \"time\" };\n return this;\n }\n\n /**\n * Date and time with explicit timezone offset.\n * @example datetime().withTimezone()\n *\n * Value format: `\"2026-05-01T14:30:00+02:00\"` (ISO 8601 with offset)\n */\n withTimezone(): this {\n this._subtype = \"dateTimeWithTimezone\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"dateTimeWithTimezone\"\n };\n return this;\n }\n\n /**\n * Date and time in UTC, no timezone offset.\n * @example datetime().withoutTimezone()\n *\n * Value format: `\"2026-05-01T14:30:00.000Z\"` (ISO 8601 UTC)\n */\n withoutTimezone(): this {\n this._subtype = \"dateTimeWithoutTimezone\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"dateTimeWithoutTimezone\"\n };\n return this;\n }\n\n /**\n * Month and year picker.\n * @example datetime().monthOnly()\n *\n * Value format: `\"2026-05\"` (YYYY-MM)\n */\n monthOnly(): this {\n this._subtype = \"month\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = { ...this._config.rendererSettings, type: \"month\" };\n return this;\n }\n\n /**\n * ISO week picker. Selecting any day highlights and selects the full week.\n * @example\n * datetime().weekOnly()\n * datetime().weekOnly({ startsOn: 1 }) // weeks start on Monday\n *\n * Value format: `\"2026-W18\"` (YYYY-Www)\n */\n weekOnly(options?: { startsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 }): this {\n this._subtype = \"week\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"week\",\n ...(options?.startsOn !== undefined && { weekStartsOn: options.startsOn })\n };\n return this;\n }\n\n /**\n * Year picker with a scrollable grid.\n * @example\n * datetime().yearOnly()\n * datetime().yearOnly({ range: [2010, 2030] })\n *\n * Value format: `2026` (number)\n */\n yearOnly(options?: { range?: [number, number] }): this {\n this._subtype = \"year\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"year\",\n ...(options?.range && { yearRange: options.range })\n };\n return this;\n }\n\n /**\n * Date range picker with a two-month calendar.\n * @example datetime().dateRange()\n *\n * Value format: `{ from: \"2026-05-01\", to: \"2026-05-15\" }`\n */\n dateRange(): this {\n this._subtype = \"dateRange\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = { ...this._config.rendererSettings, type: \"dateRange\" };\n return this;\n }\n\n /**\n * Select multiple individual dates.\n * @example datetime().multipleDates()\n *\n * Value format: `[\"2026-05-01\", \"2026-05-03\"]`\n */\n multipleDates(): this {\n this._subtype = \"multipleDates\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"multipleDates\"\n };\n return this;\n }\n\n /**\n * Select multiple months.\n * @example datetime().multipleMonths()\n *\n * Value format: `[\"2026-01\", \"2026-03\"]`\n */\n multipleMonths(): this {\n this._subtype = \"multipleMonths\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"multipleMonths\"\n };\n return this;\n }\n\n /**\n * Select multiple years.\n * @example\n * datetime().multipleYears()\n * datetime().multipleYears({ range: [2020, 2035] })\n *\n * Value format: `[2024, 2025, 2026]`\n */\n multipleYears(options?: { range?: [number, number] }): this {\n this._subtype = \"multipleYears\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"multipleYears\",\n ...(options?.range && { yearRange: options.range })\n };\n return this;\n }\n\n /**\n * Add quick-select preset buttons to the picker. Available on all variants.\n * Each preset's `value` function is called at click time, and the variant\n * handles formatting the Date into its value type.\n * @example\n * datetime().dateOnly().presets([\n * { label: \"Today\", value: () => new Date() },\n * { label: \"In a week\", value: () => addDays(new Date(), 7) }\n * ])\n */\n presets(presets: Array<{ label: string; value: () => Date }>): this {\n this._config.rendererSettings = { ...this._config.rendererSettings, presets };\n return this;\n }\n\n /**\n * Customize the display format shown in the picker trigger.\n * Uses date-fns format tokens.\n * @example\n * datetime().dateOnly().displayFormat(\"MM/dd/yyyy\") // \"05/01/2026\"\n * datetime().dateOnly().displayFormat(\"dd MMM yyyy\") // \"01 May 2026\"\n * datetime().monthOnly().displayFormat(\"MMMM yyyy\") // \"May 2026\"\n */\n displayFormat(format: string): this {\n this._config.rendererSettings = { ...this._config.rendererSettings, displayFormat: format };\n return this;\n }\n\n override normalizeValue(value: unknown): unknown {\n if (value == null || value === \"\") {\n return value;\n }\n switch (this._subtype) {\n case \"date\":\n return normalizeDateOnly(String(value));\n case \"time\":\n return normalizeTime(String(value));\n case \"dateTimeWithoutTimezone\":\n return normalizeDateTimeWithoutTimezone(String(value));\n case \"dateTimeWithTimezone\":\n return normalizeDateTimeWithTimezone(String(value));\n case \"month\":\n return normalizeMonth(String(value));\n case \"week\":\n return normalizeWeek(String(value));\n case \"year\":\n return normalizeYear(value);\n case \"dateRange\":\n case \"multipleDates\":\n case \"multipleMonths\":\n return value;\n case \"multipleYears\":\n return normalizeMultipleYears(value);\n default:\n return value;\n }\n }\n}\n\nfunction normalizeDateOnly(value: string): string {\n try {\n return new Date(value).toISOString().slice(0, 10);\n } catch {\n return value;\n }\n}\n\nfunction normalizeTime(value: string): string {\n if (!value.includes(\":\")) {\n return value;\n }\n const parts = value.split(\":\");\n if (parts.length === 3) {\n return value;\n }\n if (parts.length === 2) {\n return `${value}:00`;\n }\n return value;\n}\n\nfunction normalizeDateTimeWithoutTimezone(value: string): string {\n if (!value.includes(\" \") && !value.includes(\"T\")) {\n return value;\n }\n try {\n const normalized = value.includes(\" \") ? value.replace(\" \", \"T\") : value;\n const withZ =\n normalized.endsWith(\"Z\") ||\n normalized.includes(\"+\") ||\n /\\d-\\d{2}:\\d{2}$/.test(normalized)\n ? normalized\n : `${normalized}.000Z`;\n return new Date(withZ).toISOString();\n } catch {\n return value;\n }\n}\n\nfunction normalizeDateTimeWithTimezone(value: string): string {\n if (!value.includes(\"T\")) {\n return value;\n }\n const [initialDate, rest] = value.split(\"T\");\n if (!rest) {\n return value;\n }\n\n const match = rest.match(/^(.+?)(Z|[+-]\\d{2}:\\d{2})$/);\n if (!match) {\n return value;\n }\n const [, timePart, zone] = match;\n\n let date: string;\n try {\n date = new Date(initialDate).toISOString().slice(0, 10);\n } catch {\n return value;\n }\n\n // A UTC value is valid; express it with the explicit offset the API expects.\n const tz = zone === \"Z\" ? \"+00:00\" : zone;\n\n return `${date}T${normalizeTime(timePart)}${tz}`;\n}\n\nfunction normalizeMonth(value: string): string {\n if (/^\\d{4}-\\d{2}$/.test(value)) {\n return value;\n }\n try {\n const d = new Date(value);\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, \"0\");\n return `${y}-${m}`;\n } catch {\n return value;\n }\n}\n\nfunction normalizeWeek(value: string): string {\n if (/^\\d{4}-W\\d{2}$/.test(value)) {\n return value;\n }\n return value;\n}\n\nfunction normalizeYear(value: unknown): number | unknown {\n const num = Number(value);\n if (!Number.isNaN(num) && Number.isFinite(num)) {\n return Math.round(num);\n }\n return value;\n}\n\nfunction normalizeMultipleYears(value: unknown): unknown {\n if (!Array.isArray(value)) {\n return value;\n }\n return value.map(v => {\n const num = Number(v);\n return !Number.isNaN(num) && Number.isFinite(num) ? Math.round(num) : v;\n });\n}\n\nclass DateTimeFieldTypeFactory implements IFieldTypeFactory {\n readonly type = \"datetime\";\n create(_registry: any) {\n return new DateTimeFieldBuilder();\n }\n}\n\nexport const DateTimeFieldType = FieldType.createImplementation({\n implementation: DateTimeFieldTypeFactory,\n dependencies: []\n});\n\ndeclare module \"../abstractions.js\" {\n interface IFieldBuilderRegistry {\n /**\n * Defines a field that stores date and/or time values.\n *\n * Variant methods (pick one):\n * - `dateOnly()` — \"2026-05-01\"\n * - `timeOnly()` — \"14:30:00\"\n * - `withoutTimezone()` — \"2026-05-01T14:30:00.000Z\"\n * - `withTimezone()` — \"2026-05-01T14:30:00+02:00\"\n * - `monthOnly()` — \"2026-05\"\n * - `weekOnly()` — \"2026-W18\"\n * - `yearOnly()` — 2026\n * - `dateRange()` — { from: \"2026-05-01\", to: \"2026-05-15\" }\n * - `multipleDates()` — [\"2026-05-01\", \"2026-05-03\"]\n * - `multipleMonths()` — [\"2026-01\", \"2026-03\"]\n * - `multipleYears()` — [2024, 2025, 2026]\n *\n * Configuration methods (chain after variant):\n * - `.presets([...])` — quick-select buttons (all variants)\n * - `.displayFormat(\"MM/dd/yyyy\")` — customize trigger display\n *\n * Variant-specific options (passed to variant method):\n * - `yearOnly({ range: [2020, 2030] })` — year range\n * - `multipleYears({ range: [2020, 2035] })` — year range\n * - `weekOnly({ startsOn: 1 })` — first day of week (0=Sun, 1=Mon)\n *\n * @example\n * datetime() // full UTC datetime (default)\n * datetime().dateOnly() // birthdays, due dates, holidays\n * datetime().timeOnly() // opening hours, daily reminders\n * datetime().withTimezone() // events tied to a specific locale\n * datetime().withoutTimezone() // timestamps, logs, sortable moments\n * datetime().monthOnly() // billing cycles, report periods\n * datetime().weekOnly() // sprint planning, weekly schedules\n * datetime().yearOnly({ range: [2010, 2030] }) // fiscal years\n * datetime().dateRange() // vacation requests, booking windows\n * datetime().multipleDates() // blackout dates, recurring events\n * datetime().multipleMonths() // seasonal availability\n * datetime().multipleYears({ range: [2020, 2035] }) // multi-year budgets\n * datetime().dateOnly().presets([...]).displayFormat(\"dd/MM/yyyy\")\n */\n datetime(): DateTimeFieldBuilder;\n }\n}\n"],"names":["DateTimeFieldBuilder","FieldBuilder","options","undefined","presets","format","value","normalizeDateOnly","String","normalizeTime","normalizeDateTimeWithoutTimezone","normalizeDateTimeWithTimezone","normalizeMonth","normalizeWeek","normalizeYear","normalizeMultipleYears","Date","parts","normalized","withZ","initialDate","rest","match","timePart","zone","date","tz","d","y","m","num","Number","Math","Array","v","DateTimeFieldTypeFactory","_registry","DateTimeFieldType","FieldType"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"features/formModel/fieldTypes/DateTimeFieldType.js","sources":["../../../../src/features/formModel/fieldTypes/DateTimeFieldType.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { FieldType, type IFieldConfig, type IFieldTypeFactory } from \"../abstractions.js\";\nimport { FieldBuilder } from \"../FieldBuilder.js\";\nimport { DateFormatter } from \"~/features/dateFormatter/abstractions.js\";\nimport {\n createDateLimitsSchema,\n type DateBound,\n type DateLimitFormatter,\n type IDateLimit,\n type IDateLimits\n} from \"./dateLimits.js\";\n\ntype DateTimeSubtype =\n | \"date\"\n | \"time\"\n | \"dateTimeWithTimezone\"\n | \"dateTimeWithoutTimezone\"\n | \"month\"\n | \"week\"\n | \"year\"\n | \"dateRange\"\n | \"multipleDates\"\n | \"multipleMonths\"\n | \"multipleYears\";\n\nexport class DateTimeFieldBuilder extends FieldBuilder<\"datetime\"> {\n private _subtype: DateTimeSubtype = \"date\";\n private _dateGte: IDateLimit[] = [];\n private _dateLte: IDateLimit[] = [];\n\n /**\n * @param dateFormatter shows a limit in its message's `{date}` in the admin's date format;\n * without it, the limit shows as it is stored.\n */\n constructor(private readonly dateFormatter?: DateFormatter.Interface) {\n super(\"datetime\");\n this._config.renderer = \"dateTimeInput\";\n this._config.rendererSettings = { type: \"date\" };\n }\n\n override list(): this {\n super.list();\n this._config.renderer = \"dateTimeInputs\";\n return this;\n }\n\n /** Subtype methods reset the renderer; a list field keeps the list renderer. */\n private _defaultRenderer(): \"dateTimeInput\" | \"dateTimeInputs\" {\n return this._config.isList ? \"dateTimeInputs\" : \"dateTimeInput\";\n }\n\n /**\n * Calendar date only.\n * @example\n * datetime().dateOnly()\n * // day, then month, then year; suits dates far in the past, like a date of birth.\n * // The picker offers only the dates the `dateGte`/`dateLte` limits allow.\n * datetime()\n * .dateOnly()\n * .dateGte({ expression: \"today-100y\" })\n * .dateLte({ expression: \"today-18y\" }, \"You must be 18 or older.\")\n * .renderer(\"birthdayInput\")\n *\n * Value format: `\"2026-05-01\"` (YYYY-MM-DD)\n */\n dateOnly(): this {\n this._subtype = \"date\";\n this._config.renderer =\n this._isBirthdayPicker() && !this._config.isList\n ? \"birthdayInput\"\n : this._defaultRenderer();\n this._config.rendererSettings = { ...this._config.rendererSettings, type: \"date\" };\n return this;\n }\n\n /**\n * The CMS form sets a field's renderer before its subtype, so a birthday picker set\n * earlier survives `dateOnly()`, since it renders the same value. Every other subtype\n * resets it.\n */\n private _isBirthdayPicker(): boolean {\n return this._config.renderer === \"birthdayInput\";\n }\n\n /**\n * Time only, no date component.\n * @example datetime().timeOnly()\n *\n * Value format: `\"14:30:00\"` (HH:mm:ss)\n */\n timeOnly(): this {\n this._subtype = \"time\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = { ...this._config.rendererSettings, type: \"time\" };\n return this;\n }\n\n /**\n * Date and time with explicit timezone offset.\n * @example datetime().withTimezone()\n *\n * Value format: `\"2026-05-01T14:30:00+02:00\"` (ISO 8601 with offset)\n */\n withTimezone(): this {\n this._subtype = \"dateTimeWithTimezone\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"dateTimeWithTimezone\"\n };\n return this;\n }\n\n /**\n * Date and time in UTC, no timezone offset.\n * @example datetime().withoutTimezone()\n *\n * Value format: `\"2026-05-01T14:30:00.000Z\"` (ISO 8601 UTC)\n */\n withoutTimezone(): this {\n this._subtype = \"dateTimeWithoutTimezone\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"dateTimeWithoutTimezone\"\n };\n return this;\n }\n\n /**\n * Month and year picker.\n * @example datetime().monthOnly()\n *\n * Value format: `\"2026-05\"` (YYYY-MM)\n */\n monthOnly(): this {\n this._subtype = \"month\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = { ...this._config.rendererSettings, type: \"month\" };\n return this;\n }\n\n /**\n * ISO week picker. Selecting any day highlights and selects the full week.\n * @example\n * datetime().weekOnly()\n * datetime().weekOnly({ startsOn: 1 }) // weeks start on Monday\n *\n * Value format: `\"2026-W18\"` (YYYY-Www)\n */\n weekOnly(options?: { startsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 }): this {\n this._subtype = \"week\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"week\",\n ...(options?.startsOn !== undefined && { weekStartsOn: options.startsOn })\n };\n return this;\n }\n\n /**\n * Year picker with a scrollable grid.\n * @example\n * datetime().yearOnly()\n * datetime().yearOnly({ range: [2010, 2030] })\n *\n * Value format: `2026` (number)\n */\n yearOnly(options?: { range?: [number, number] }): this {\n this._subtype = \"year\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"year\",\n ...(options?.range && { yearRange: options.range })\n };\n return this;\n }\n\n /**\n * Date range picker with a two-month calendar.\n * @example datetime().dateRange()\n *\n * Value format: `{ from: \"2026-05-01\", to: \"2026-05-15\" }`\n */\n dateRange(): this {\n this._subtype = \"dateRange\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = { ...this._config.rendererSettings, type: \"dateRange\" };\n return this;\n }\n\n /**\n * Select multiple individual dates.\n * @example datetime().multipleDates()\n *\n * Value format: `[\"2026-05-01\", \"2026-05-03\"]`\n */\n multipleDates(): this {\n this._subtype = \"multipleDates\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"multipleDates\"\n };\n return this;\n }\n\n /**\n * Select multiple months.\n * @example datetime().multipleMonths()\n *\n * Value format: `[\"2026-01\", \"2026-03\"]`\n */\n multipleMonths(): this {\n this._subtype = \"multipleMonths\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"multipleMonths\"\n };\n return this;\n }\n\n /**\n * Select multiple years.\n * @example\n * datetime().multipleYears()\n * datetime().multipleYears({ range: [2020, 2035] })\n *\n * Value format: `[2024, 2025, 2026]`\n */\n multipleYears(options?: { range?: [number, number] }): this {\n this._subtype = \"multipleYears\";\n this._config.renderer = this._defaultRenderer();\n this._config.rendererSettings = {\n ...this._config.rendererSettings,\n type: \"multipleYears\",\n ...(options?.range && { yearRange: options.range })\n };\n return this;\n }\n\n /**\n * Add quick-select preset buttons to the picker. Available on all variants.\n * Each preset's `value` function is called at click time, and the variant\n * handles formatting the Date into its value type.\n * @example\n * datetime().dateOnly().presets([\n * { label: \"Today\", value: () => new Date() },\n * { label: \"In a week\", value: () => addDays(new Date(), 7) }\n * ])\n */\n presets(presets: Array<{ label: string; value: () => Date }>): this {\n this._config.rendererSettings = { ...this._config.rendererSettings, presets };\n return this;\n }\n\n /**\n * The value must be on or after the given one: a fixed value, or, on a date-only field, an\n * expression worked out from the editor's local date every time the value is checked.\n * Expressions: `today`, or `today` plus or minus a number of days (`d`), months (`m`) or\n * years (`y`). The birthday picker offers only the dates the limits allow.\n * @example\n * datetime().dateOnly().dateGte(\"1900-01-01\")\n * datetime().dateOnly().dateGte({ expression: \"today-100y\" }) // not older than 100 years\n * datetime().dateOnly().dateGte({ expression: \"today+1d\" }, \"Pick a date in the future.\")\n */\n dateGte(bound: DateBound, message?: string): this {\n this._dateGte.push({ bound, message });\n return this;\n }\n\n /**\n * The value must be on or before the given one: a fixed value, or, on a date-only field, an\n * expression worked out from the editor's local date every time the value is checked.\n * In `message`, `{date}` is the limit in the admin's date format; the default message is\n * \"Must be on or before {date}.\" (`dateGte`: \"Must be on or after {date}.\").\n * @example\n * datetime().dateOnly().dateLte(\"2026-12-31\")\n * datetime()\n * .dateOnly()\n * .dateLte({ expression: \"today-18y\" }, \"You must be 18 or older.\")\n * .renderer(\"birthdayInput\")\n */\n dateLte(bound: DateBound, message?: string): this {\n this._dateLte.push({ bound, message });\n return this;\n }\n\n /**\n * The limits are applied here, so a `renderer()` or `schema()` call after `dateGte()` or\n * `dateLte()` doesn't drop them: the check runs after the field's own schema, and the\n * renderer finds them in `rendererSettings.dateLimits`.\n */\n override build(name: string): IFieldConfig {\n const config = super.build(name);\n if (this._dateGte.length === 0 && this._dateLte.length === 0) {\n return config;\n }\n const dateLimits: IDateLimits = {\n gte: this._dateGte.map(limit => limit.bound),\n lte: this._dateLte.map(limit => limit.bound)\n };\n const limitsSchema = createDateLimitsSchema(\n this._dateGte,\n this._dateLte,\n this._formatDateLimit()\n );\n return {\n ...config,\n rendererSettings: { ...config.rendererSettings, dateLimits },\n schema: config.schema ? z.intersection(config.schema, limitsSchema) : limitsSchema\n };\n }\n\n /** A limit in the admin's date format: the date alone on a date-only field. */\n private _formatDateLimit(): DateLimitFormatter | undefined {\n const formatter = this.dateFormatter;\n if (!formatter) {\n return undefined;\n }\n switch (this._subtype) {\n case \"date\":\n // A `YYYY-MM-DD` limit is midnight UTC, so it's shown in UTC to keep its day.\n return date =>\n formatter.format(date, {\n hour: undefined,\n minute: undefined,\n timeZone: \"UTC\"\n });\n case \"dateTimeWithTimezone\":\n case \"dateTimeWithoutTimezone\":\n return date => formatter.format(date);\n default:\n return undefined;\n }\n }\n\n /**\n * Customize the display format shown in the picker trigger.\n * Uses date-fns format tokens.\n * @example\n * datetime().dateOnly().displayFormat(\"MM/dd/yyyy\") // \"05/01/2026\"\n * datetime().dateOnly().displayFormat(\"dd MMM yyyy\") // \"01 May 2026\"\n * datetime().monthOnly().displayFormat(\"MMMM yyyy\") // \"May 2026\"\n */\n displayFormat(format: string): this {\n this._config.rendererSettings = { ...this._config.rendererSettings, displayFormat: format };\n return this;\n }\n\n override normalizeValue(value: unknown): unknown {\n if (value == null || value === \"\") {\n return value;\n }\n switch (this._subtype) {\n case \"date\":\n return normalizeDateOnly(String(value));\n case \"time\":\n return normalizeTime(String(value));\n case \"dateTimeWithoutTimezone\":\n return normalizeDateTimeWithoutTimezone(String(value));\n case \"dateTimeWithTimezone\":\n return normalizeDateTimeWithTimezone(String(value));\n case \"month\":\n return normalizeMonth(String(value));\n case \"week\":\n return normalizeWeek(String(value));\n case \"year\":\n return normalizeYear(value);\n case \"dateRange\":\n case \"multipleDates\":\n case \"multipleMonths\":\n return value;\n case \"multipleYears\":\n return normalizeMultipleYears(value);\n default:\n return value;\n }\n }\n}\n\nfunction normalizeDateOnly(value: string): string {\n try {\n return new Date(value).toISOString().slice(0, 10);\n } catch {\n return value;\n }\n}\n\nfunction normalizeTime(value: string): string {\n if (!value.includes(\":\")) {\n return value;\n }\n const parts = value.split(\":\");\n if (parts.length === 3) {\n return value;\n }\n if (parts.length === 2) {\n return `${value}:00`;\n }\n return value;\n}\n\nfunction normalizeDateTimeWithoutTimezone(value: string): string {\n if (!value.includes(\" \") && !value.includes(\"T\")) {\n return value;\n }\n try {\n const normalized = value.includes(\" \") ? value.replace(\" \", \"T\") : value;\n const withZ =\n normalized.endsWith(\"Z\") ||\n normalized.includes(\"+\") ||\n /\\d-\\d{2}:\\d{2}$/.test(normalized)\n ? normalized\n : `${normalized}.000Z`;\n return new Date(withZ).toISOString();\n } catch {\n return value;\n }\n}\n\nfunction normalizeDateTimeWithTimezone(value: string): string {\n if (!value.includes(\"T\")) {\n return value;\n }\n const [initialDate, rest] = value.split(\"T\");\n if (!rest) {\n return value;\n }\n\n const match = rest.match(/^(.+?)(Z|[+-]\\d{2}:\\d{2})$/);\n if (!match) {\n return value;\n }\n const [, timePart, zone] = match;\n\n let date: string;\n try {\n date = new Date(initialDate).toISOString().slice(0, 10);\n } catch {\n return value;\n }\n\n // A UTC value is valid; express it with the explicit offset the API expects.\n const tz = zone === \"Z\" ? \"+00:00\" : zone;\n\n return `${date}T${normalizeTime(timePart)}${tz}`;\n}\n\nfunction normalizeMonth(value: string): string {\n if (/^\\d{4}-\\d{2}$/.test(value)) {\n return value;\n }\n try {\n const d = new Date(value);\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, \"0\");\n return `${y}-${m}`;\n } catch {\n return value;\n }\n}\n\nfunction normalizeWeek(value: string): string {\n if (/^\\d{4}-W\\d{2}$/.test(value)) {\n return value;\n }\n return value;\n}\n\nfunction normalizeYear(value: unknown): number | unknown {\n const num = Number(value);\n if (!Number.isNaN(num) && Number.isFinite(num)) {\n return Math.round(num);\n }\n return value;\n}\n\nfunction normalizeMultipleYears(value: unknown): unknown {\n if (!Array.isArray(value)) {\n return value;\n }\n return value.map(v => {\n const num = Number(v);\n return !Number.isNaN(num) && Number.isFinite(num) ? Math.round(num) : v;\n });\n}\n\nclass DateTimeFieldTypeFactory implements IFieldTypeFactory {\n readonly type = \"datetime\";\n\n constructor(private readonly dateFormatter?: DateFormatter.Interface) {}\n\n create(_registry: any) {\n return new DateTimeFieldBuilder(this.dateFormatter);\n }\n}\n\nexport const DateTimeFieldType = FieldType.createImplementation({\n implementation: DateTimeFieldTypeFactory,\n dependencies: [[DateFormatter, { optional: true }]]\n});\n\ndeclare module \"../abstractions.js\" {\n interface IFieldBuilderRegistry {\n /**\n * Defines a field that stores date and/or time values.\n *\n * Variant methods (pick one):\n * - `dateOnly()` — \"2026-05-01\"\n * - `timeOnly()` — \"14:30:00\"\n * - `withoutTimezone()` — \"2026-05-01T14:30:00.000Z\"\n * - `withTimezone()` — \"2026-05-01T14:30:00+02:00\"\n * - `monthOnly()` — \"2026-05\"\n * - `weekOnly()` — \"2026-W18\"\n * - `yearOnly()` — 2026\n * - `dateRange()` — { from: \"2026-05-01\", to: \"2026-05-15\" }\n * - `multipleDates()` — [\"2026-05-01\", \"2026-05-03\"]\n * - `multipleMonths()` — [\"2026-01\", \"2026-03\"]\n * - `multipleYears()` — [2024, 2025, 2026]\n *\n * Configuration methods (chain after variant):\n * - `.presets([...])` — quick-select buttons (all variants)\n * - `.displayFormat(\"MM/dd/yyyy\")` — customize trigger display\n * - `.dateGte(bound)` / `.dateLte(bound)` — limits: a fixed value, or\n * `{ expression: \"today-18y\" }` on a date-only field\n *\n * Variant-specific options (passed to variant method):\n * - `yearOnly({ range: [2020, 2030] })` — year range\n * - `multipleYears({ range: [2020, 2035] })` — year range\n * - `weekOnly({ startsOn: 1 })` — first day of week (0=Sun, 1=Mon)\n *\n * @example\n * datetime() // full UTC datetime (default)\n * datetime().dateOnly() // due dates, holidays\n * datetime().dateOnly().renderer(\"birthdayInput\") // dates of birth\n * datetime().dateOnly().dateLte({ expression: \"today-18y\" }) // 18 or older\n * datetime().timeOnly() // opening hours, daily reminders\n * datetime().withTimezone() // events tied to a specific locale\n * datetime().withoutTimezone() // timestamps, logs, sortable moments\n * datetime().monthOnly() // billing cycles, report periods\n * datetime().weekOnly() // sprint planning, weekly schedules\n * datetime().yearOnly({ range: [2010, 2030] }) // fiscal years\n * datetime().dateRange() // vacation requests, booking windows\n * datetime().multipleDates() // blackout dates, recurring events\n * datetime().multipleMonths() // seasonal availability\n * datetime().multipleYears({ range: [2020, 2035] }) // multi-year budgets\n * datetime().dateOnly().presets([...]).displayFormat(\"dd/MM/yyyy\")\n */\n datetime(): DateTimeFieldBuilder;\n }\n}\n"],"names":["DateTimeFieldBuilder","FieldBuilder","dateFormatter","options","undefined","presets","bound","message","name","config","dateLimits","limit","limitsSchema","createDateLimitsSchema","z","formatter","date","format","value","normalizeDateOnly","String","normalizeTime","normalizeDateTimeWithoutTimezone","normalizeDateTimeWithTimezone","normalizeMonth","normalizeWeek","normalizeYear","normalizeMultipleYears","Date","parts","normalized","withZ","initialDate","rest","match","timePart","zone","tz","d","y","m","num","Number","Math","Array","v","DateTimeFieldTypeFactory","_registry","DateTimeFieldType","FieldType","DateFormatter"],"mappings":"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"}
|