Adopt the shared interface pattern language
This commit is contained in:
+2
-1
@@ -13,7 +13,8 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test:interface-pattern": "node scripts/test-interface-pattern.mjs"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.14",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
|
||||
const page = fs.readFileSync("src/features/workflow/WorkflowPage.tsx", "utf8");
|
||||
const runs = fs.readFileSync("src/features/workflow/WorkflowRunsDialog.tsx", "utf8");
|
||||
const inspector = fs.readFileSync("src/features/workflow/WorkflowInspector.tsx", "utf8");
|
||||
const styles = fs.readFileSync("src/styles/workflow.css", "utf8");
|
||||
|
||||
assert.ok(page.includes("DocumentationHelpLink"), "Workflow exposes configured-system help");
|
||||
assert.ok(page.includes("useUnsavedDraftGuard"), "Workflow protects dirty graph revisions during navigation");
|
||||
assert.ok(page.includes("<ConfirmDialog"), "Destructive and corrective definition actions use shared confirmation");
|
||||
assert.ok(page.includes("<Dialog"), "Definition settings and derivation use shared focus-contained dialogs");
|
||||
assert.ok(page.includes("onClick={() => addNodeFromPalette(nodeType.type)}"), "Palette nodes have a keyboard/pointer alternative to drag and drop");
|
||||
assert.ok(inspector.includes("onEdgeChange"), "Edges can be edited without reconnect dragging");
|
||||
assert.ok(runs.includes("StatusBadge"), "Run and handoff states are not conveyed by color alone");
|
||||
assert.ok(runs.includes("DismissibleAlert"), "Run failures use the shared alert contract");
|
||||
assert.ok(!page.includes("window.alert("), "Workflow must not use browser alerts");
|
||||
assert.ok(styles.includes("@media (max-width: 680px)"), "Workflow retains a narrow-viewport layout");
|
||||
assert.ok(styles.includes("@media (prefers-reduced-motion: reduce)"), "Workflow honors reduced motion");
|
||||
|
||||
console.log("Workflow interface pattern contract passed.");
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
Button,
|
||||
ConfirmDialog,
|
||||
Dialog,
|
||||
DocumentationHelpLink,
|
||||
DismissibleAlert,
|
||||
FormField,
|
||||
IconButton,
|
||||
@@ -74,6 +75,7 @@ import WorkflowRunsDialog from "./WorkflowRunsDialog";
|
||||
import {
|
||||
FALLBACK_WORKFLOW_LIBRARY,
|
||||
draftFromDefinition,
|
||||
newWorkflowNode,
|
||||
sampleWorkflowDraft,
|
||||
workflowFingerprint,
|
||||
workflowPayload,
|
||||
@@ -564,6 +566,21 @@ export default function WorkflowPage({
|
||||
setSelectedEdgeId(null);
|
||||
};
|
||||
|
||||
const addNodeFromPalette = (nodeType: string) => {
|
||||
if (!draft || graphReadOnly) return;
|
||||
const index = draft.graph.nodes.length;
|
||||
const node = newWorkflowNode(
|
||||
nodeType,
|
||||
{
|
||||
x: 80 + (index % 4) * 220,
|
||||
y: 80 + Math.floor(index / 4) * 150
|
||||
},
|
||||
nodeLibrary
|
||||
);
|
||||
updateGraph({ ...draft.graph, nodes: [...draft.graph.nodes, node] });
|
||||
setSelectedNodeId(node.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="workflow-page">
|
||||
<div className="workflow-shell">
|
||||
@@ -571,6 +588,10 @@ export default function WorkflowPage({
|
||||
<div className="workflow-panel-toolbar">
|
||||
<strong>Workflows</strong>
|
||||
<span className="workflow-toolbar-actions">
|
||||
<DocumentationHelpLink
|
||||
reference={{ topicId: "workflow.editor", documentationType: "user" }}
|
||||
label="Open Workflow documentation"
|
||||
/>
|
||||
<IconButton
|
||||
label="Refresh"
|
||||
icon={<RefreshCw size={16} />}
|
||||
@@ -593,6 +614,7 @@ export default function WorkflowPage({
|
||||
variant="primary"
|
||||
onClick={createNew}
|
||||
disabled={!canWrite}
|
||||
disabledReason={!canWrite ? "Workflow definition write permission is required." : undefined}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
@@ -870,6 +892,7 @@ export default function WorkflowPage({
|
||||
onDragStart={(event) =>
|
||||
startPaletteDrag(event, nodeType.type)
|
||||
}
|
||||
onClick={() => addNodeFromPalette(nodeType.type)}
|
||||
title={nodeType.description}
|
||||
>
|
||||
<GitFork size={16} />
|
||||
@@ -1195,6 +1218,7 @@ function WorkflowDefinitionSettingsDialog({
|
||||
<FormField
|
||||
label="Scope"
|
||||
help="The scope determines ownership, visibility, and the Policy inheritance path."
|
||||
documentation={{ topicId: "workflow.editor", documentationType: "admin" }}
|
||||
>
|
||||
<select
|
||||
value={draft.scopeType}
|
||||
@@ -1241,6 +1265,7 @@ function WorkflowDefinitionSettingsDialog({
|
||||
<FormField
|
||||
label="Definition kind"
|
||||
help="Templates can be reused or derived, but cannot be activated or started."
|
||||
documentation={{ topicId: "workflow.editor", documentationType: "user" }}
|
||||
>
|
||||
<select
|
||||
value={draft.definitionKind}
|
||||
@@ -1257,6 +1282,7 @@ function WorkflowDefinitionSettingsDialog({
|
||||
<FormField
|
||||
label="Execution mode"
|
||||
help="Guided flows require a user; automated flows cannot contain human handoffs; hybrid flows can combine both."
|
||||
documentation={{ topicId: "workflow.editor", documentationType: "user" }}
|
||||
>
|
||||
<select
|
||||
value={draft.executionMode}
|
||||
@@ -1278,6 +1304,7 @@ function WorkflowDefinitionSettingsDialog({
|
||||
<FormField
|
||||
label="Workflow View"
|
||||
help="The selected immutable View revision is applied for active runs. Individual steps may narrow it further."
|
||||
documentation={{ topicId: "workflow.editor", documentationType: "user" }}
|
||||
>
|
||||
<select
|
||||
value={draft.viewId}
|
||||
|
||||
@@ -1226,3 +1226,14 @@
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.workflow-page *,
|
||||
.workflow-page *::before,
|
||||
.workflow-page *::after {
|
||||
scroll-behavior: auto !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user