Enforce the shared WebUI pattern language
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user