Complete guided Scheduling interface patterns

This commit is contained in:
2026-08-03 10:36:14 +02:00
parent 3d8272b28e
commit c17cbdae63
9 changed files with 334 additions and 52 deletions
@@ -6,10 +6,12 @@ const pagePath = fileURLToPath(new URL("../src/features/scheduling/SchedulingPag
const publicPagePath = fileURLToPath(new URL("../src/features/scheduling/SchedulingPublicPage.tsx", import.meta.url));
const apiPath = fileURLToPath(new URL("../src/api/scheduling.ts", import.meta.url));
const modulePath = fileURLToPath(new URL("../src/module.ts", import.meta.url));
const widgetPath = fileURLToPath(new URL("../src/features/scheduling/SchedulingRequestsWidget.tsx", import.meta.url));
const page = readFileSync(pagePath, "utf8");
const publicPage = readFileSync(publicPagePath, "utf8");
const api = readFileSync(apiPath, "utf8");
const moduleSource = readFileSync(modulePath, "utf8");
const widget = readFileSync(widgetPath, "utf8");
assert.match(page, /usePlatformUiCapability<CalendarPickerUiCapability>\("calendar\.picker"\)/);
assert.match(page, /hasScope\(auth, "calendar:calendar:read"\)/);
@@ -101,10 +103,18 @@ assert.match(participantGrid, /minimumSlots=\{3\}/);
assert.ok(participantGrid.indexOf('id: "copy-invitation"') < participantGrid.indexOf('id: "send-invitation"'));
assert.ok(participantGrid.indexOf('id: "send-invitation"') < participantGrid.indexOf('id: "revoke-invitation"'));
assert.match(participantGrid, /schedulingInvitationActionBlocks\(request, participant, now\)/);
assert.match(participantGrid, /disabledReason: copyDisabledReason/);
assert.match(participantGrid, /disabledReason: deliveryDisabledReason/);
assert.match(participantGrid, /disabledReason: revokeDisabledReason/);
assert.match(participantGrid, /disabledReason: saving \? I18N\.saving : copyDisabledReason/);
assert.match(participantGrid, /disabledReason: saving \? I18N\.saving : deliveryDisabledReason/);
assert.match(participantGrid, /disabledReason: saving \? I18N\.saving : revokeDisabledReason/);
assert.match(page, /<ConfirmDialog[\s\S]*title=\{I18N\.revokeInvitationLabel\}[\s\S]*tone="danger"/);
assert.match(page, /setConsequentialAction\(\{ kind: "close"/);
assert.match(page, /setConsequentialAction\(\{ kind: "reminder"/);
assert.match(page, /setConsequentialAction\(\{ kind: "holds"/);
assert.match(page, /setConsequentialAction\(\{ kind: "final-event"/);
assert.match(page, /setDecisionTarget\(\{ requestId: selected\.id, slot \}\)/);
assert.match(page, /open=\{Boolean\(consequentialAction && consequentialActionCopy\)\}/);
assert.match(page, /open=\{Boolean\(decisionTarget\)\}/);
assert.match(page, /disabledReason=\{saving \? I18N\.saving/);
assert.match(page, /navigator\.clipboard\.writeText\(value\)/);
assert.match(page, /navigator\.clipboard\.write\(\[new ClipboardItem/);
assert.match(page, /schedulingPublicInvitationUrl\(response\.action_url, window\.location\.origin\)/);
@@ -155,7 +165,11 @@ assert.match(page, /Promise\.allSettled/);
assert.match(moduleSource, /publicRoutes:[\s\S]*path: "\/scheduling\/public\/:requestId\/:token"/);
assert.match(moduleSource, /SchedulingPublicPage/);
assert.match(publicPage, /Card,[\s\S]*DismissibleAlert,[\s\S]*FormField,[\s\S]*LoadingFrame,[\s\S]*from "@govoplan\/core-webui"/);
assert.match(publicPage, /Card,[\s\S]*DismissibleAlert,[\s\S]*DocumentationHelpLink,[\s\S]*FormField,[\s\S]*LoadingFrame,[\s\S]*PasswordField,[\s\S]*from "@govoplan\/core-webui"/);
assert.match(publicPage, /<PasswordField[\s\S]*autoComplete="current-password"/);
assert.doesNotMatch(publicPage, /<input[\s\S]{0,120}type="password"/);
assert.match(publicPage, /topicId: "scheduling\.find-and-decide-meeting-time"/);
assert.match(publicPage, /disabledReason=\{saving \? I18N\.saving/);
assert.match(publicPage, /getPublicSchedulingParticipation\(settings, requestId, token, \{\}\)/);
assert.match(publicPage, /applySchedulingAvailabilityChoice\(/);
assert.match(publicPage, /option_revision: slot\.revision/);
@@ -163,4 +177,9 @@ assert.match(publicPage, /idempotency_key: newIdempotencyKey\(\)/);
assert.doesNotMatch(publicPage, /window\.(?:alert|confirm)\(/);
assert.doesNotMatch(publicPage, /(?:localStorage|sessionStorage).*token|token.*(?:localStorage|sessionStorage)/);
assert.match(widget, /DocumentationHelpLink/);
assert.match(widget, /to: `\/scheduling\?request_id=\$\{encodeURIComponent\(request\.id\)\}`/);
assert.match(widget, /label=\{request\.status === "collecting" \? I18N\.open : I18N\.draft\}/);
assert.doesNotMatch(widget, /Loading scheduling requests|Open scheduling|No scheduling requests are awaiting responses/);
console.log("Scheduling pages satisfy the two-pane editor, public response, and policy contracts.");