Enforce the shared WebUI pattern language
Dependency Audit / dependency-audit (push) Successful in 1m43s
Deployment Installer / deployment-installer (push) Successful in 6s
Security Audit / security-audit (push) Successful in 11m17s

This commit is contained in:
2026-08-18 13:17:22 +02:00
parent 5bb8028147
commit a1b80eda27
6 changed files with 248 additions and 65 deletions
+143 -2
View File
@@ -42,6 +42,9 @@ CENTRAL_COMPONENTS = {
"ContentGrid": pathlib.Path(
"govoplan-core/webui/src/components/ContentGrid.tsx"
),
"ContentSection": pathlib.Path(
"govoplan-core/webui/src/components/ContentSection.tsx"
),
"FormGrid": pathlib.Path("govoplan-core/webui/src/components/ContentGrid.tsx"),
"FormLayout": pathlib.Path(
"govoplan-core/webui/src/components/ContentGrid.tsx"
@@ -68,6 +71,48 @@ CENTRAL_COMPONENTS = {
"MetricGrid": pathlib.Path(
"govoplan-core/webui/src/components/MetricGrid.tsx"
),
"MetricCard": pathlib.Path(
"govoplan-core/webui/src/components/MetricCard.tsx"
),
"FilterBar": pathlib.Path(
"govoplan-core/webui/src/components/FilterBar.tsx"
),
"StatePanel": pathlib.Path(
"govoplan-core/webui/src/components/StatePanel.tsx"
),
"CountBadge": pathlib.Path(
"govoplan-core/webui/src/components/CountBadge.tsx"
),
"SelectionList": pathlib.Path(
"govoplan-core/webui/src/components/SelectionList.tsx"
),
"SelectionListItem": pathlib.Path(
"govoplan-core/webui/src/components/SelectionList.tsx"
),
"SelectionListItemContent": pathlib.Path(
"govoplan-core/webui/src/components/SelectionList.tsx"
),
"WorkspaceLayout": pathlib.Path(
"govoplan-core/webui/src/components/WorkspaceLayout.tsx"
),
"WorkspaceFrame": pathlib.Path(
"govoplan-core/webui/src/components/WorkspaceFrame.tsx"
),
"DefinitionPalette": pathlib.Path(
"govoplan-core/webui/src/components/DefinitionPalette.tsx"
),
"DefinitionPaletteGroup": pathlib.Path(
"govoplan-core/webui/src/components/DefinitionPalette.tsx"
),
"DefinitionPaletteItem": pathlib.Path(
"govoplan-core/webui/src/components/DefinitionPalette.tsx"
),
"DefinitionNodeIcon": pathlib.Path(
"govoplan-core/webui/src/components/DefinitionNodeIcon.tsx"
),
"FloatingStatus": pathlib.Path(
"govoplan-core/webui/src/components/FloatingStatus.tsx"
),
}
REQUIRED_CONSUMERS = {
"ActionToolbar": (
@@ -82,6 +127,11 @@ REQUIRED_CONSUMERS = {
pathlib.Path("govoplan-campaign/webui/src/features/campaigns/GlobalSettingsPage.tsx"),
pathlib.Path("govoplan-notifications/webui/src/features/notifications/NotificationSettingsPanel.tsx"),
),
"ContentSection": (
pathlib.Path("govoplan-datasources/webui/src/features/datasources/DatasourcesPage.tsx"),
pathlib.Path("govoplan-dist-lists/webui/src/features/distributionLists/DistributionListsPage.tsx"),
pathlib.Path("govoplan-templates/webui/src/features/templates/TemplatesPage.tsx"),
),
"FormGrid": (
pathlib.Path("govoplan-core/webui/src/components/mail/MailServerSettingsPanel.tsx"),
pathlib.Path("govoplan-calendar/webui/src/features/calendar/CalendarEventDialog.tsx"),
@@ -114,10 +164,91 @@ REQUIRED_CONSUMERS = {
pathlib.Path("govoplan-campaign/webui/src/features/operator/OperatorQueuePage.tsx"),
pathlib.Path("govoplan-notifications/webui/src/features/notifications/NotificationSummaryWidget.tsx"),
),
"MetricCard": (
pathlib.Path("govoplan-admin/webui/src/features/admin/AdminOverviewPanel.tsx"),
pathlib.Path("govoplan-approvals/webui/src/features/approvals/ApprovalsPage.tsx"),
pathlib.Path("govoplan-campaign/webui/src/features/campaigns/ReviewSendPage.tsx"),
pathlib.Path("govoplan-voting/webui/src/features/voting/VotingPage.tsx"),
),
"FilterBar": (
pathlib.Path("govoplan-cases/webui/src/features/cases/CasesPage.tsx"),
pathlib.Path("govoplan-dataflow/webui/src/features/dataflow/DataflowPage.tsx"),
pathlib.Path("govoplan-records/webui/src/features/records/RecordsPage.tsx"),
pathlib.Path("govoplan-tasks/webui/src/features/tasks/TasksPage.tsx"),
),
"StatePanel": (
pathlib.Path("govoplan-committee/webui/src/features/committee/CommitteePage.tsx"),
pathlib.Path("govoplan-notifications/webui/src/features/notifications/NotificationCenterPage.tsx"),
pathlib.Path("govoplan-postbox/webui/src/features/postbox/PostboxPage.tsx"),
pathlib.Path("govoplan-risk-compliance/webui/src/features/riskCompliance/RiskCompliancePage.tsx"),
),
"CountBadge": (
pathlib.Path("govoplan-core/webui/src/layout/Titlebar.tsx"),
pathlib.Path("govoplan-mail/webui/src/features/mail/MailboxPage.tsx"),
pathlib.Path("govoplan-search/webui/src/features/search/SearchPage.tsx"),
),
"SelectionListItemContent": (
pathlib.Path("govoplan-approvals/webui/src/features/approvals/ApprovalsPage.tsx"),
pathlib.Path("govoplan-datasources/webui/src/features/datasources/DatasourcesPage.tsx"),
pathlib.Path("govoplan-voting/webui/src/features/voting/VotingPage.tsx"),
),
"WorkspaceLayout": (
pathlib.Path("govoplan-dataflow/webui/src/features/dataflow/DataflowPage.tsx"),
pathlib.Path("govoplan-notifications/webui/src/features/notifications/NotificationCenterPage.tsx"),
pathlib.Path("govoplan-workflow/webui/src/features/workflow/WorkflowPage.tsx"),
),
"WorkspaceFrame": (
pathlib.Path("govoplan-cases/webui/src/features/cases/CasesPage.tsx"),
pathlib.Path("govoplan-portal/webui/src/features/portal/PortalPage.tsx"),
pathlib.Path("govoplan-records/webui/src/features/records/RecordsPage.tsx"),
),
"DefinitionPalette": (
pathlib.Path("govoplan-dataflow/webui/src/features/dataflow/DataflowPage.tsx"),
pathlib.Path("govoplan-workflow/webui/src/features/workflow/WorkflowPage.tsx"),
),
"DefinitionNodeIcon": (
pathlib.Path("govoplan-dataflow/webui/src/features/dataflow/DataflowNode.tsx"),
pathlib.Path("govoplan-workflow/webui/src/features/workflow/WorkflowNode.tsx"),
),
"FloatingStatus": (
pathlib.Path("govoplan-dataflow/webui/src/features/dataflow/DataflowPage.tsx"),
pathlib.Path("govoplan-workflow/webui/src/features/workflow/WorkflowPage.tsx"),
),
}
LEGACY_CSS_SELECTOR = re.compile(
r"(?<![-\w])\.(?:admin-details-grid|detail-list|metric-grid)(?![-\w])"
)
RETIRED_LOCAL_CSS_CLASSES = {
"admin-assignment-grid",
"approval-metrics",
"dataflow-node-count",
"dataflow-shell",
"datasources-metrics",
"datasources-shell",
"dist-lists-metrics",
"dist-lists-shell",
"notifications-count",
"notifications-empty-state",
"notifications-shell",
"review-flow-execution-summary",
"review-flow-fact-grid",
"risk-metrics",
"search-filter-count",
"tasks-empty-detail",
"tasks-shell",
"templates-shell",
"voting-metrics",
"voting-shell",
"workflow-shell",
"dataflow-palette-items",
"workflow-palette-items",
"dataflow-working-indicator",
"workflow-working-indicator",
"datasources-detail-section",
"dist-lists-section",
"templates-section",
}
LOCAL_METRIC_HELPER = re.compile(r"\b(?:function\s+Metric\b|const\s+Metric\s*=)")
CSS_BLOCK = re.compile(r"([^{}]+)\{([^{}]*)\}")
CSS_COMMENT = re.compile(r"/\*.*?\*/", re.DOTALL)
DIALOG_CLASS = re.compile(r"\.([A-Za-z0-9_-]*(?:dialog|modal)[A-Za-z0-9_-]*)", re.IGNORECASE)
@@ -234,10 +365,16 @@ def main() -> int:
)
for path, content in style_text.items():
legacy_match = LEGACY_CSS_SELECTOR.search(CSS_COMMENT.sub("", content))
uncommented = CSS_COMMENT.sub("", content)
legacy_match = LEGACY_CSS_SELECTOR.search(uncommented)
if legacy_match:
line = content.count("\n", 0, legacy_match.start()) + 1
errors.append(f"Legacy shared layout selector is not allowed: {path}:{line}")
for class_name in sorted(RETIRED_LOCAL_CSS_CLASSES):
retired = re.search(rf"(?<![-\w])\.{re.escape(class_name)}(?![-\w])\s*(?:,|\{{)", uncommented)
if retired:
line = content.count("\n", 0, retired.start()) + 1
errors.append(f"Retired module-local shared anatomy selector is not allowed: {path}:{line} ({class_name})")
dialog_exceptions = dialog_width_exceptions(style_text)
baseline = exception_baseline()
@@ -259,6 +396,10 @@ def main() -> int:
if path != owner and definition.search(content):
errors.append(f"Module-local {name} definition is not allowed: {path}")
for path, content in source_text.items():
if LOCAL_METRIC_HELPER.search(content):
errors.append(f"Module-local Metric helper is not allowed; compose MetricCard directly: {path}")
usage_counts: dict[str, int] = {}
for name in CENTRAL_COMPONENTS:
usage = re.compile(rf"<{name}\b")
@@ -291,7 +432,7 @@ def main() -> int:
migrated = ", ".join(
f"{name}={usage_counts[name]} files"
for name in ("ActionToolbar", "ContentGrid", "FormGrid", "FormSection", "DialogForm", "DialogSection", "MetricGrid", "DescriptionList")
for name in ("ActionToolbar", "WorkspaceFrame", "WorkspaceLayout", "FilterBar", "StatePanel", "SelectionList", "CountBadge", "DefinitionPalette", "DefinitionNodeIcon", "FloatingStatus", "ContentSection", "ContentGrid", "FormGrid", "FormSection", "DialogForm", "DialogSection", "MetricGrid", "MetricCard", "DescriptionList")
)
print(f"Shared WebUI primitive contract passed: {migrated}; {len(dialog_exceptions)} reviewed dialog width exceptions; no raw legacy anatomy.")
return 0