@storyshelf/app 0.6.1 → 0.7.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/README.md +42 -4
- package/dist/app-types.d.mts +1 -3
- package/dist/asset-manifest.mjs +13 -5
- package/dist/asset-manifest.mjs.map +1 -1
- package/dist/assets/{htmx.min.js → htmx.mjs} +10 -1
- package/dist/assets/htmx.mjs.map +1 -0
- package/dist/assets/mark.mjs +10 -0
- package/dist/assets/mark.mjs.map +1 -0
- package/dist/boot-tasks.mjs +80 -0
- package/dist/boot-tasks.mjs.map +1 -0
- package/dist/build-state-flags.mjs +18 -0
- package/dist/build-state-flags.mjs.map +1 -0
- package/dist/build-state-setup.mjs +75 -0
- package/dist/build-state-setup.mjs.map +1 -0
- package/dist/capture-alerts.mjs +46 -0
- package/dist/capture-alerts.mjs.map +1 -0
- package/dist/capture-process.mjs +59 -0
- package/dist/capture-process.mjs.map +1 -0
- package/dist/capture-setup.mjs +95 -117
- package/dist/capture-setup.mjs.map +1 -1
- package/dist/connections.mjs +34 -0
- package/dist/connections.mjs.map +1 -0
- package/dist/consumer-setup.mjs +58 -0
- package/dist/consumer-setup.mjs.map +1 -0
- package/dist/credentials.mjs +3 -2
- package/dist/credentials.mjs.map +1 -1
- package/dist/git-status.mjs +46 -0
- package/dist/git-status.mjs.map +1 -0
- package/dist/hidden-data.mjs +24 -0
- package/dist/hidden-data.mjs.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +202 -124
- package/dist/index.mjs.map +1 -1
- package/dist/insights/budget.mjs +49 -0
- package/dist/insights/budget.mjs.map +1 -0
- package/dist/insights/deps.mjs +21 -0
- package/dist/insights/deps.mjs.map +1 -0
- package/dist/insights/errors.mjs +22 -0
- package/dist/insights/errors.mjs.map +1 -0
- package/dist/insights/health-panel.mjs +40 -0
- package/dist/insights/health-panel.mjs.map +1 -0
- package/dist/insights/job.mjs +103 -0
- package/dist/insights/job.mjs.map +1 -0
- package/dist/insights/load-health.mjs +59 -0
- package/dist/insights/load-health.mjs.map +1 -0
- package/dist/insights/load-triage.mjs +98 -0
- package/dist/insights/load-triage.mjs.map +1 -0
- package/dist/insights/panel.mjs +28 -0
- package/dist/insights/panel.mjs.map +1 -0
- package/dist/insights/request.mjs +113 -0
- package/dist/insights/request.mjs.map +1 -0
- package/dist/insights/stale.mjs +21 -0
- package/dist/insights/stale.mjs.map +1 -0
- package/dist/insights/usage-report.mjs +60 -0
- package/dist/insights/usage-report.mjs.map +1 -0
- package/dist/insights/view.mjs +48 -0
- package/dist/insights/view.mjs.map +1 -0
- package/dist/job-canceller.mjs +14 -0
- package/dist/job-canceller.mjs.map +1 -0
- package/dist/job-queue.mjs +51 -0
- package/dist/job-queue.mjs.map +1 -0
- package/dist/jobs-setup.mjs +132 -0
- package/dist/jobs-setup.mjs.map +1 -0
- package/dist/lifecycle.mjs +109 -19
- package/dist/lifecycle.mjs.map +1 -1
- package/dist/middleware/auth-gate.mjs +4 -2
- package/dist/middleware/auth-gate.mjs.map +1 -1
- package/dist/middleware/rate-limit.mjs +41 -33
- package/dist/middleware/rate-limit.mjs.map +1 -1
- package/dist/middleware/request-log.mjs +3 -2
- package/dist/middleware/request-log.mjs.map +1 -1
- package/dist/middleware/require-review.mjs +51 -0
- package/dist/middleware/require-review.mjs.map +1 -0
- package/dist/middleware/require-testing.mjs +41 -0
- package/dist/middleware/require-testing.mjs.map +1 -0
- package/dist/middleware/setup-gate.mjs.map +1 -1
- package/dist/middleware/store-scope.mjs +12 -2
- package/dist/middleware/store-scope.mjs.map +1 -1
- package/dist/notifications-pause.mjs +33 -0
- package/dist/notifications-pause.mjs.map +1 -0
- package/dist/notify.d.mts +2 -1
- package/dist/notify.mjs +78 -39
- package/dist/notify.mjs.map +1 -1
- package/dist/pages/a11y-violations.mjs +116 -0
- package/dist/pages/a11y-violations.mjs.map +1 -0
- package/dist/pages/admin-ai.mjs +78 -0
- package/dist/pages/admin-ai.mjs.map +1 -0
- package/dist/pages/admin-system-origin.mjs +123 -0
- package/dist/pages/admin-system-origin.mjs.map +1 -0
- package/dist/pages/admin-system-targets.mjs +53 -0
- package/dist/pages/admin-system-targets.mjs.map +1 -0
- package/dist/pages/admin-system.mjs +63 -13
- package/dist/pages/admin-system.mjs.map +1 -1
- package/dist/pages/build-detail.mjs +104 -76
- package/dist/pages/build-detail.mjs.map +1 -1
- package/dist/pages/build-diff-actions.mjs +87 -33
- package/dist/pages/build-diff-actions.mjs.map +1 -1
- package/dist/pages/build-diff-comments.mjs +16 -14
- package/dist/pages/build-diff-comments.mjs.map +1 -1
- package/dist/pages/build-diff-environment.mjs +82 -0
- package/dist/pages/build-diff-environment.mjs.map +1 -0
- package/dist/pages/build-diff-header.mjs +22 -4
- package/dist/pages/build-diff-header.mjs.map +1 -1
- package/dist/pages/build-diff-insight.mjs +113 -0
- package/dist/pages/build-diff-insight.mjs.map +1 -0
- package/dist/pages/build-diff-model.mjs +55 -10
- package/dist/pages/build-diff-model.mjs.map +1 -1
- package/dist/pages/build-diff-nav.mjs +28 -12
- package/dist/pages/build-diff-nav.mjs.map +1 -1
- package/dist/pages/build-diff-shortcuts.mjs +5 -1
- package/dist/pages/build-diff-shortcuts.mjs.map +1 -1
- package/dist/pages/build-diff-usage.mjs +63 -0
- package/dist/pages/build-diff-usage.mjs.map +1 -0
- package/dist/pages/build-diff-viewer.mjs +62 -5
- package/dist/pages/build-diff-viewer.mjs.map +1 -1
- package/dist/pages/build-diff.mjs +34 -13
- package/dist/pages/build-diff.mjs.map +1 -1
- package/dist/pages/build-tests.mjs +74 -0
- package/dist/pages/build-tests.mjs.map +1 -0
- package/dist/pages/component-detail.mjs +249 -0
- package/dist/pages/component-detail.mjs.map +1 -0
- package/dist/pages/component-stories.mjs +168 -0
- package/dist/pages/component-stories.mjs.map +1 -0
- package/dist/pages/compute-jobs.mjs +13 -26
- package/dist/pages/compute-jobs.mjs.map +1 -1
- package/dist/pages/design-links.mjs +182 -0
- package/dist/pages/design-links.mjs.map +1 -0
- package/dist/pages/environment-change.mjs +53 -0
- package/dist/pages/environment-change.mjs.map +1 -0
- package/dist/pages/health-panel.mjs +126 -0
- package/dist/pages/health-panel.mjs.map +1 -0
- package/dist/pages/library-grid.mjs +105 -0
- package/dist/pages/library-grid.mjs.map +1 -0
- package/dist/pages/library-model.mjs +47 -0
- package/dist/pages/library-model.mjs.map +1 -0
- package/dist/pages/library-snapshots.mjs +130 -0
- package/dist/pages/library-snapshots.mjs.map +1 -0
- package/dist/pages/library.mjs +112 -218
- package/dist/pages/library.mjs.map +1 -1
- package/dist/pages/notifications-paused.mjs +22 -0
- package/dist/pages/notifications-paused.mjs.map +1 -0
- package/dist/pages/profile.mjs +53 -48
- package/dist/pages/profile.mjs.map +1 -1
- package/dist/pages/project-builds-table.mjs +2 -2
- package/dist/pages/project-builds-table.mjs.map +1 -1
- package/dist/pages/project-builds.mjs +51 -21
- package/dist/pages/project-builds.mjs.map +1 -1
- package/dist/pages/project-settings.mjs +51 -12
- package/dist/pages/project-settings.mjs.map +1 -1
- package/dist/pages/projects-card.mjs +14 -1
- package/dist/pages/projects-card.mjs.map +1 -1
- package/dist/pages/projects.mjs +6 -2
- package/dist/pages/projects.mjs.map +1 -1
- package/dist/pages/review-changeset.mjs +116 -0
- package/dist/pages/review-changeset.mjs.map +1 -0
- package/dist/pages/review-comments.mjs +103 -0
- package/dist/pages/review-comments.mjs.map +1 -0
- package/dist/pages/review-verdict.mjs +102 -0
- package/dist/pages/review-verdict.mjs.map +1 -0
- package/dist/pages/settings-ai.mjs +69 -0
- package/dist/pages/settings-ai.mjs.map +1 -0
- package/dist/pages/settings-capture.mjs +124 -0
- package/dist/pages/settings-capture.mjs.map +1 -0
- package/dist/pages/settings-design-credential.mjs +99 -0
- package/dist/pages/settings-design-credential.mjs.map +1 -0
- package/dist/pages/settings-design-links.mjs +179 -0
- package/dist/pages/settings-design-links.mjs.map +1 -0
- package/dist/pages/settings-design.mjs +48 -0
- package/dist/pages/settings-design.mjs.map +1 -0
- package/dist/pages/settings-general.mjs +46 -21
- package/dist/pages/settings-general.mjs.map +1 -1
- package/dist/pages/settings-labels.mjs +43 -2
- package/dist/pages/settings-labels.mjs.map +1 -1
- package/dist/pages/settings-links.mjs +112 -0
- package/dist/pages/settings-links.mjs.map +1 -0
- package/dist/pages/settings-notifications-git.mjs +128 -0
- package/dist/pages/settings-notifications-git.mjs.map +1 -0
- package/dist/pages/settings-notifications.mjs +79 -42
- package/dist/pages/settings-notifications.mjs.map +1 -1
- package/dist/pages/settings-tests.mjs +33 -2
- package/dist/pages/settings-tests.mjs.map +1 -1
- package/dist/pages/target-choices.mjs +39 -0
- package/dist/pages/target-choices.mjs.map +1 -0
- package/dist/publish-setup.mjs +89 -0
- package/dist/publish-setup.mjs.map +1 -0
- package/dist/retention-timer.mjs +2 -1
- package/dist/retention-timer.mjs.map +1 -1
- package/dist/review-events.mjs +48 -0
- package/dist/review-events.mjs.map +1 -0
- package/dist/routers/admin-pages.mjs +45 -3
- package/dist/routers/admin-pages.mjs.map +1 -1
- package/dist/routers/admin-public-origin.mjs +85 -0
- package/dist/routers/admin-public-origin.mjs.map +1 -0
- package/dist/routers/assets.mjs +9 -2
- package/dist/routers/assets.mjs.map +1 -1
- package/dist/routers/auth.mjs +2 -2
- package/dist/routers/auth.mjs.map +1 -1
- package/dist/routers/baseline-guard.mjs +2 -2
- package/dist/routers/baseline-guard.mjs.map +1 -1
- package/dist/routers/baselines.handlers.mjs +64 -0
- package/dist/routers/baselines.handlers.mjs.map +1 -0
- package/dist/routers/baselines.mjs +125 -0
- package/dist/routers/baselines.mjs.map +1 -0
- package/dist/routers/build-cancel.mjs +68 -0
- package/dist/routers/build-cancel.mjs.map +1 -0
- package/dist/routers/build-changeset.mjs +70 -0
- package/dist/routers/build-changeset.mjs.map +1 -0
- package/dist/routers/build-reviews.handlers.mjs +86 -0
- package/dist/routers/build-reviews.handlers.mjs.map +1 -0
- package/dist/routers/build-reviews.mjs +74 -0
- package/dist/routers/build-reviews.mjs.map +1 -0
- package/dist/routers/builds.handlers.mjs +46 -55
- package/dist/routers/builds.handlers.mjs.map +1 -1
- package/dist/routers/builds.mjs +71 -17
- package/dist/routers/builds.mjs.map +1 -1
- package/dist/routers/capture-config.mjs +35 -0
- package/dist/routers/capture-config.mjs.map +1 -0
- package/dist/routers/comments.mjs +41 -15
- package/dist/routers/comments.mjs.map +1 -1
- package/dist/routers/component-catalog.handlers.mjs +71 -0
- package/dist/routers/component-catalog.handlers.mjs.map +1 -0
- package/dist/routers/component.handlers.mjs +121 -0
- package/dist/routers/component.handlers.mjs.map +1 -0
- package/dist/routers/connections.mjs +118 -0
- package/dist/routers/connections.mjs.map +1 -0
- package/dist/routers/design-connect.handlers.mjs +44 -0
- package/dist/routers/design-connect.handlers.mjs.map +1 -0
- package/dist/routers/design.handlers.mjs +114 -0
- package/dist/routers/design.handlers.mjs.map +1 -0
- package/dist/routers/health-report.mjs +2 -3
- package/dist/routers/health-report.mjs.map +1 -1
- package/dist/routers/health.mjs +4 -3
- package/dist/routers/health.mjs.map +1 -1
- package/dist/routers/helpers.mjs +19 -3
- package/dist/routers/helpers.mjs.map +1 -1
- package/dist/routers/insights-ui.mjs +64 -0
- package/dist/routers/insights-ui.mjs.map +1 -0
- package/dist/routers/insights.handlers.mjs +45 -0
- package/dist/routers/insights.handlers.mjs.map +1 -0
- package/dist/routers/insights.mjs +189 -0
- package/dist/routers/insights.mjs.map +1 -0
- package/dist/routers/links.handlers.mjs +80 -0
- package/dist/routers/links.handlers.mjs.map +1 -0
- package/dist/routers/links.mjs +139 -0
- package/dist/routers/links.mjs.map +1 -0
- package/dist/routers/media.mjs +18 -4
- package/dist/routers/media.mjs.map +1 -1
- package/dist/routers/members.mjs +2 -1
- package/dist/routers/members.mjs.map +1 -1
- package/dist/routers/notification-channels.mjs +17 -23
- package/dist/routers/notification-channels.mjs.map +1 -1
- package/dist/routers/oauth-state.mjs +45 -0
- package/dist/routers/oauth-state.mjs.map +1 -0
- package/dist/routers/preview-base.mjs +38 -0
- package/dist/routers/preview-base.mjs.map +1 -0
- package/dist/routers/preview-token.mjs +51 -0
- package/dist/routers/preview-token.mjs.map +1 -0
- package/dist/routers/profile.mjs +4 -2
- package/dist/routers/profile.mjs.map +1 -1
- package/dist/routers/projects.mjs +17 -1
- package/dist/routers/projects.mjs.map +1 -1
- package/dist/routers/publish-upload.mjs +78 -0
- package/dist/routers/publish-upload.mjs.map +1 -0
- package/dist/routers/published-access.mjs +52 -0
- package/dist/routers/published-access.mjs.map +1 -0
- package/dist/routers/published-files.mjs +52 -0
- package/dist/routers/published-files.mjs.map +1 -0
- package/dist/routers/review-forms.mjs +72 -0
- package/dist/routers/review-forms.mjs.map +1 -0
- package/dist/routers/review-section.handlers.mjs +85 -0
- package/dist/routers/review-section.handlers.mjs.map +1 -0
- package/dist/routers/review-switch-off.mjs +29 -0
- package/dist/routers/review-switch-off.mjs.map +1 -0
- package/dist/routers/schemas.mjs +140 -22
- package/dist/routers/schemas.mjs.map +1 -1
- package/dist/routers/scoped-models.mjs +22 -0
- package/dist/routers/scoped-models.mjs.map +1 -0
- package/dist/routers/settings-ai.mjs +35 -0
- package/dist/routers/settings-ai.mjs.map +1 -0
- package/dist/routers/settings-capture.mjs +77 -0
- package/dist/routers/settings-capture.mjs.map +1 -0
- package/dist/routers/settings-design.mjs +103 -0
- package/dist/routers/settings-design.mjs.map +1 -0
- package/dist/routers/settings-general.mjs +55 -10
- package/dist/routers/settings-general.mjs.map +1 -1
- package/dist/routers/settings-git-channel.mjs +77 -0
- package/dist/routers/settings-git-channel.mjs.map +1 -0
- package/dist/routers/settings-labels.mjs +27 -1
- package/dist/routers/settings-labels.mjs.map +1 -1
- package/dist/routers/settings-links.mjs +63 -0
- package/dist/routers/settings-links.mjs.map +1 -0
- package/dist/routers/settings-members.mjs +4 -7
- package/dist/routers/settings-members.mjs.map +1 -1
- package/dist/routers/settings-notifications.mjs +15 -10
- package/dist/routers/settings-notifications.mjs.map +1 -1
- package/dist/routers/settings-tokens.mjs +2 -5
- package/dist/routers/settings-tokens.mjs.map +1 -1
- package/dist/routers/settings-webhooks.mjs +7 -24
- package/dist/routers/settings-webhooks.mjs.map +1 -1
- package/dist/routers/settings.handlers.mjs +107 -26
- package/dist/routers/settings.handlers.mjs.map +1 -1
- package/dist/routers/settings.mjs +16 -4
- package/dist/routers/settings.mjs.map +1 -1
- package/dist/routers/signed-storybook.mjs +53 -0
- package/dist/routers/signed-storybook.mjs.map +1 -0
- package/dist/routers/snapshot-groups.mjs +63 -0
- package/dist/routers/snapshot-groups.mjs.map +1 -0
- package/dist/routers/snapshots.mjs +103 -5
- package/dist/routers/snapshots.mjs.map +1 -1
- package/dist/routers/storybook.mjs +37 -88
- package/dist/routers/storybook.mjs.map +1 -1
- package/dist/routers/tokens.mjs +2 -5
- package/dist/routers/tokens.mjs.map +1 -1
- package/dist/routers/ui.mjs +34 -5
- package/dist/routers/ui.mjs.map +1 -1
- package/dist/routers/webhooks.mjs +5 -11
- package/dist/routers/webhooks.mjs.map +1 -1
- package/dist/routers/well-known.mjs +11 -4
- package/dist/routers/well-known.mjs.map +1 -1
- package/dist/routes/api-routes.mjs +93 -0
- package/dist/routes/api-routes.mjs.map +1 -0
- package/dist/routes/classification.mjs +38 -0
- package/dist/routes/classification.mjs.map +1 -0
- package/dist/routes/openapi-classes.mjs +22 -0
- package/dist/routes/openapi-classes.mjs.map +1 -0
- package/dist/routes/page-routes.mjs +104 -0
- package/dist/routes/page-routes.mjs.map +1 -0
- package/dist/runtime.mjs +18 -13
- package/dist/runtime.mjs.map +1 -1
- package/dist/serverless.mjs +5 -0
- package/dist/store.mjs +19 -1
- package/dist/store.mjs.map +1 -1
- package/dist/targets.mjs +46 -0
- package/dist/targets.mjs.map +1 -0
- package/dist/ui/buttons.mjs +16 -3
- package/dist/ui/buttons.mjs.map +1 -1
- package/dist/ui/design-reference.mjs +97 -0
- package/dist/ui/design-reference.mjs.map +1 -0
- package/dist/ui/document.mjs +1 -2
- package/dist/ui/document.mjs.map +1 -1
- package/dist/ui/forms.mjs +6 -5
- package/dist/ui/forms.mjs.map +1 -1
- package/dist/ui/icons/paths.mjs +1 -0
- package/dist/ui/icons/paths.mjs.map +1 -1
- package/dist/ui/preview-embed.mjs +82 -0
- package/dist/ui/preview-embed.mjs.map +1 -0
- package/dist/ui/shell/client-script.mjs +85 -0
- package/dist/ui/shell/client-script.mjs.map +1 -1
- package/dist/ui/shell/nav.mjs +12 -8
- package/dist/ui/shell/nav.mjs.map +1 -1
- package/dist/ui/shell/review-script.mjs +8 -0
- package/dist/ui/shell/review-script.mjs.map +1 -1
- package/dist/ui/shell/sidebar.mjs +2 -2
- package/dist/ui/shell/sidebar.mjs.map +1 -1
- package/package.json +11 -6
- package/dist/asset-paths.mjs +0 -23
- package/dist/asset-paths.mjs.map +0 -1
- package/dist/assets/mark.svg +0 -1
- package/dist/pages/settings-status.mjs +0 -137
- package/dist/pages/settings-status.mjs.map +0 -1
- package/dist/routers/settings-status.mjs +0 -77
- package/dist/routers/settings-status.mjs.map +0 -1
- package/dist/routers/status-configs.mjs +0 -108
- package/dist/routers/status-configs.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-diff-viewer.mjs","names":[],"sources":["../../src/pages/build-diff-viewer.tsx"],"sourcesContent":["import type { Build, Project, Snapshot } from \"@storyshelf/core/schema\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport { Badge, CompareStage, VStack, statusTone } from \"../ui/components.tsx\";\nimport { viewerHead, viewerMeta, viewerTitle } from \"../ui/styles/review-layout.ts\";\nimport {
|
|
1
|
+
{"version":3,"file":"build-diff-viewer.mjs","names":[],"sources":["../../src/pages/build-diff-viewer.tsx"],"sourcesContent":["import type { Build, Project, Snapshot } from \"@storyshelf/core/schema\";\nimport { createUrlBuilder } from \"@storyshelf/core/urls\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport { Badge, Button, CompareStage, Segmented, VStack, statusTone } from \"../ui/components.tsx\";\nimport { viewerHead, viewerMeta, viewerTitle } from \"../ui/styles/review-layout.ts\";\nimport { A11yViolationsNotice, a11yCarrierOf } from \"./a11y-violations.tsx\";\nimport { EnvironmentChangeSnapshotNotice } from \"./build-diff-environment.tsx\";\nimport {\n diffPercent,\n snapshotImageUrl,\n snapshotPageUrl,\n snapshotVariant,\n} from \"./build-diff-model.ts\";\nimport { StaleSnapshotNotice } from \"./build-diff-stale.tsx\";\nimport { LinkedUsage, type LinkedUsageItem } from \"./build-diff-usage.tsx\";\nimport { DesignReferences, DesignStrip, type DesignItem } from \"./design-links.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\n/** Viewer props for the selected snapshot. */\nexport interface DiffViewerProps {\n project: Project;\n build: Build;\n selected: Snapshot;\n /** The selected story/mode's snapshots on every engine (browser tabs when more than one). */\n siblings?: Snapshot[];\n hasBaseline: Record<string, boolean>;\n /** Set when the baseline changed after this snapshot's diff was computed. */\n drifted?: \"stale\" | \"removed\";\n /** Linked packages the story uses (provenance badges + upstream deep links). */\n usage?: LinkedUsageItem[];\n /** Resolved design links of the story's component (open-in-Figma + stale/deleted notices). */\n design?: DesignItem[];\n publishedBaseDomain?: string;\n}\n\n/** Why a snapshot has no diff image, phrased for the empty pane. */\nfunction diffEmptyText(status: string): string {\n return status === \"unchanged\" || status === \"approved\"\n ? \"No diff — within threshold\"\n : \"No diff yet\";\n}\n\n/** Browser tabs (one link per engine) when the story/mode was captured on several. */\nfunction BrowserTabs(props: {\n project: Project;\n build: Build;\n selected: Snapshot;\n siblings: Snapshot[];\n}): HtmlEscapedString | Promise<HtmlEscapedString> | null {\n const { project, build, selected, siblings } = props;\n if (siblings.length < 2) return null;\n return (\n <Segmented\n label=\"Browser\"\n data-browser-tabs\n items={siblings.map((snap) => ({\n label: `${snap.browser ?? \"chromium\"} · ${snap.status}`,\n value: snap.id,\n href: snapshotPageUrl(project, build, snap.id),\n active: snap.id === selected.id,\n }))}\n />\n );\n}\n\n/** Link to the story file's component page, anchored at the story. */\nfunction ViewComponentButton(props: {\n project: Project;\n selected: Snapshot;\n base?: string;\n}): HtmlEscapedString | Promise<HtmlEscapedString> | null {\n const { project, selected, base } = props;\n if (!selected.storyImportPath) return null;\n const urls = createUrlBuilder(\"/\", base);\n return (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n href={`${urls.component(project.slug, selected.storyImportPath)}#${selected.storyId}`}\n >\n View component\n </Button>\n );\n}\n\n/** Story heading plus the comparison stage for the selected snapshot. */\nexport function DiffViewer(props: DiffViewerProps): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { project, build, selected, hasBaseline, drifted, usage, design, publishedBaseDomain } =\n props;\n const siblings = props.siblings ?? [selected];\n const percent = diffPercent(selected);\n return (\n <VStack>\n <div class={viewerHead}>\n <h2 class={viewerTitle}>\n {selected.storyTitle} / {selected.storyName}\n </h2>\n <p class={viewerMeta}>\n <Badge tone={statusTone(selected.status)} icon>\n {selected.status}\n </Badge>\n <span>\n {snapshotVariant(selected)} · {selected.viewportWidth}×{selected.viewportHeight}\n </span>\n {selected.diffPixels === null || selected.diffPixels === undefined ? null : (\n <span class=\"mono\">{selected.diffPixels} px</span>\n )}\n {percent ? <span class=\"mono\">{percent}</span> : null}\n <ViewComponentButton project={project} selected={selected} base={publishedBaseDomain} />\n </p>\n </div>\n <BrowserTabs project={project} build={build} selected={selected} siblings={siblings} />\n {drifted ? <StaleSnapshotNotice status={drifted} /> : null}\n <EnvironmentChangeSnapshotNotice snapshot={selected} migrated={build.isDefault} />\n <A11yViolationsNotice snapshot={a11yCarrierOf(selected, siblings)} />\n {usage && usage.length > 0 ? (\n <LinkedUsage usage={usage} publishedBaseDomain={publishedBaseDomain} />\n ) : null}\n {design && design.length > 0 ? <DesignStrip items={design} /> : null}\n <CompareStage\n baselineSrc={\n hasBaseline[selected.id] ? snapshotImageUrl(project, build, selected, \"baseline\") : null\n }\n currentSrc={snapshotImageUrl(project, build, selected, \"image\")}\n diffSrc={selected.diffPath ? snapshotImageUrl(project, build, selected, \"diff\") : null}\n subject={`${selected.storyTitle} / ${selected.storyName}`}\n diffEmpty={diffEmptyText(selected.status)}\n diffMeta={percent ?? undefined}\n />\n {design && design.length > 0 ? <DesignReferences items={design} /> : null}\n </VStack>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqCA,SAAS,cAAc,QAAwB;CAC7C,OAAO,WAAW,eAAe,WAAW,aACxC,+BACA;AACN;;AAGA,SAAS,YAAY,OAKqC;CACxD,MAAM,EAAE,SAAS,OAAO,UAAU,aAAa;CAC/C,IAAI,SAAS,SAAS,GAAG,OAAO;CAChC,OACE,oBAAC,WAAD;EACE,OAAM;EACN,qBAAA;EACA,OAAO,SAAS,KAAK,UAAU;GAC7B,OAAO,GAAG,KAAK,WAAW,WAAW,KAAK,KAAK;GAC/C,OAAO,KAAK;GACZ,MAAM,gBAAgB,SAAS,OAAO,KAAK,EAAE;GAC7C,QAAQ,KAAK,OAAO,SAAS;EAC/B,EAAE;CACH,CAAA;AAEL;;AAGA,SAAS,oBAAoB,OAI6B;CACxD,MAAM,EAAE,SAAS,UAAU,SAAS;CACpC,IAAI,CAAC,SAAS,iBAAiB,OAAO;CACtC,MAAM,OAAO,iBAAiB,KAAK,IAAI;CACvC,OACE,oBAAC,QAAD;EACE,SAAQ;EACR,MAAK;EACL,MAAM,GAAG,KAAK,UAAU,QAAQ,MAAM,SAAS,eAAe,EAAE,GAAG,SAAS;EAC7E,UAAA;CAEO,CAAA;AAEZ;;AAGA,SAAgB,WAAW,OAAwE;CACjG,MAAM,EAAE,SAAS,OAAO,UAAU,aAAa,SAAS,OAAO,QAAQ,wBACrE;CACF,MAAM,WAAW,MAAM,YAAY,CAAC,QAAQ;CAC5C,MAAM,UAAU,YAAY,QAAQ;CACpC,OACE,qBAAC,QAAD,EAAA,UAAA;EACE,qBAAC,OAAD;GAAK,OAAO;GAAZ,UAAA,CACE,qBAAC,MAAD;IAAI,OAAO;IAAX,UAAA;KACG,SAAS;KAAW;KAAI,SAAS;IAChC;GACJ,CAAA,GAAA,qBAAC,KAAD;IAAG,OAAO;IAAV,UAAA;KACE,oBAAC,OAAD;MAAO,MAAM,WAAW,SAAS,MAAM;MAAG,MAAA;MACvC,UAAA,SAAS;KACL,CAAA;KACP,qBAAC,QAAD,EAAA,UAAA;MACG,gBAAgB,QAAQ;MAAE;MAAI,SAAS;MAAc;MAAE,SAAS;KAC7D,EAAA,CAAA;KACL,SAAS,eAAe,QAAQ,SAAS,eAAe,KAAA,IAAY,OACnE,qBAAC,QAAD;MAAM,OAAM;MAAZ,UAAA,CAAoB,SAAS,YAAW,KAAS;;KAElD,UAAU,oBAAC,QAAD;MAAM,OAAM;MAAQ,UAAA;KAAc,CAAA,IAAI;KACjD,oBAAC,qBAAD;MAA8B;MAAmB;MAAU,MAAM;KAAsB,CAAA;IACtF;GACA,CAAA,CAAA;;EACL,oBAAC,aAAD;GAAsB;GAAgB;GAAiB;GAAoB;EAAW,CAAA;EACrF,UAAU,oBAAC,qBAAD,EAAqB,QAAQ,QAAU,CAAA,IAAI;EACtD,oBAAC,iCAAD;GAAiC,UAAU;GAAU,UAAU,MAAM;EAAY,CAAA;EACjF,oBAAC,sBAAD,EAAsB,UAAU,cAAc,UAAU,QAAQ,EAAI,CAAA;EACnE,SAAS,MAAM,SAAS,IACvB,oBAAC,aAAD;GAAoB;GAA4B;EAAsB,CAAA,IACpE;EACH,UAAU,OAAO,SAAS,IAAI,oBAAC,aAAD,EAAa,OAAO,OAAS,CAAA,IAAI;EAChE,oBAAC,cAAD;GACE,aACE,YAAY,SAAS,MAAM,iBAAiB,SAAS,OAAO,UAAU,UAAU,IAAI;GAEtF,YAAY,iBAAiB,SAAS,OAAO,UAAU,OAAO;GAC9D,SAAS,SAAS,WAAW,iBAAiB,SAAS,OAAO,UAAU,MAAM,IAAI;GAClF,SAAS,GAAG,SAAS,WAAW,KAAK,SAAS;GAC9C,WAAW,cAAc,SAAS,MAAM;GACxC,UAAU,WAAW,KAAA;EACtB,CAAA;EACA,UAAU,OAAO,SAAS,IAAI,oBAAC,kBAAD,EAAkB,OAAO,OAAS,CAAA,IAAI;CAC/D,EAAA,CAAA;AAEZ"}
|
|
@@ -3,19 +3,26 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { EmptyState } from "../ui/feedback.mjs";
|
|
5
5
|
import { DocumentLayout } from "../ui/document.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { InsightSlot } from "./build-diff-insight.mjs";
|
|
7
|
+
import { groupOf, reviewProgress } from "./build-diff-model.mjs";
|
|
8
|
+
import { DiffComments } from "./build-diff-comments.mjs";
|
|
7
9
|
import { canvasColumn, workspace } from "../ui/styles/review-layout.mjs";
|
|
8
10
|
import { DiffActionBar } from "./build-diff-actions.mjs";
|
|
9
|
-
import {
|
|
11
|
+
import { ReviewNotices } from "./build-diff-environment.mjs";
|
|
10
12
|
import { DiffHeader } from "./build-diff-header.mjs";
|
|
11
13
|
import { DiffNav } from "./build-diff-nav.mjs";
|
|
12
14
|
import { ShortcutsDialog } from "./build-diff-shortcuts.mjs";
|
|
13
15
|
import { DiffViewer } from "./build-diff-viewer.mjs";
|
|
14
16
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
15
17
|
//#region src/pages/build-diff.tsx
|
|
18
|
+
/** The snapshot the review page shows: the requested one, else the first needing review. */
|
|
19
|
+
function selectedSnapshot(snapshots, selectedId) {
|
|
20
|
+
return snapshots.find((s) => s.id === selectedId) ?? snapshots.find((s) => s.status === "changed" || s.status === "new") ?? snapshots[0];
|
|
21
|
+
}
|
|
16
22
|
/** Snapshot nav plus the viewer/comments column for the selected snapshot. */
|
|
17
23
|
function DiffReviewGrid(props) {
|
|
18
24
|
const { project, build, snapshots, comments, selected, canReview, hasBaseline, drifted } = props;
|
|
25
|
+
const { usage, design, publishedBaseDomain } = props;
|
|
19
26
|
return /* @__PURE__ */ jsxs("div", {
|
|
20
27
|
class: workspace,
|
|
21
28
|
children: [/* @__PURE__ */ jsx(DiffNav, {
|
|
@@ -30,13 +37,18 @@ function DiffReviewGrid(props) {
|
|
|
30
37
|
project,
|
|
31
38
|
build,
|
|
32
39
|
selected,
|
|
40
|
+
siblings: groupOf(snapshots, selected.id)?.members,
|
|
33
41
|
hasBaseline,
|
|
34
|
-
drifted: drifted[selected.id]
|
|
42
|
+
drifted: drifted[selected.id],
|
|
43
|
+
usage,
|
|
44
|
+
design,
|
|
45
|
+
publishedBaseDomain
|
|
35
46
|
}),
|
|
36
|
-
/* @__PURE__ */ jsx(DiffComments, {
|
|
47
|
+
project.reviewEnabled === false ? null : /* @__PURE__ */ jsx(DiffComments, {
|
|
37
48
|
project,
|
|
38
49
|
build,
|
|
39
50
|
selectedId: selected.id,
|
|
51
|
+
selectedStoryId: selected.storyId,
|
|
40
52
|
comments,
|
|
41
53
|
canReview
|
|
42
54
|
}),
|
|
@@ -46,7 +58,7 @@ function DiffReviewGrid(props) {
|
|
|
46
58
|
snapshots,
|
|
47
59
|
selected,
|
|
48
60
|
canReview,
|
|
49
|
-
drifted
|
|
61
|
+
drifted
|
|
50
62
|
})
|
|
51
63
|
] }) : /* @__PURE__ */ jsx(EmptyState, { description: "Select a snapshot to review." })
|
|
52
64
|
})]
|
|
@@ -67,8 +79,8 @@ function reviewNav(project, build) {
|
|
|
67
79
|
/** Three-up diff review page: baseline, current, and diff with keyboard review. */
|
|
68
80
|
function renderBuildDiffPage(data) {
|
|
69
81
|
const { project, build, snapshots, comments, selectedId, canReview, hasBaseline, drifted } = data;
|
|
70
|
-
const selected = snapshots
|
|
71
|
-
const
|
|
82
|
+
const selected = selectedSnapshot(snapshots, selectedId);
|
|
83
|
+
const pendingGroups = reviewProgress(snapshots).pending;
|
|
72
84
|
return /* @__PURE__ */ jsxs(DocumentLayout, {
|
|
73
85
|
title: `Review · ${build.gitBranch}`,
|
|
74
86
|
nav: reviewNav(project, build),
|
|
@@ -78,15 +90,21 @@ function renderBuildDiffPage(data) {
|
|
|
78
90
|
project,
|
|
79
91
|
build,
|
|
80
92
|
snapshots,
|
|
81
|
-
pendingCount:
|
|
93
|
+
pendingCount: pendingGroups,
|
|
82
94
|
canReview
|
|
83
95
|
}),
|
|
84
|
-
|
|
96
|
+
/* @__PURE__ */ jsx(ReviewNotices, {
|
|
85
97
|
project,
|
|
86
98
|
build,
|
|
87
|
-
|
|
99
|
+
snapshots,
|
|
100
|
+
drifted,
|
|
88
101
|
canReview
|
|
89
|
-
})
|
|
102
|
+
}),
|
|
103
|
+
/* @__PURE__ */ jsx(InsightSlot, {
|
|
104
|
+
project,
|
|
105
|
+
build,
|
|
106
|
+
data: data.insight
|
|
107
|
+
}),
|
|
90
108
|
snapshots.length === 0 ? /* @__PURE__ */ jsx(EmptyState, {
|
|
91
109
|
title: "No snapshots",
|
|
92
110
|
description: "This build has no snapshots yet. Capture may still be running."
|
|
@@ -98,13 +116,16 @@ function renderBuildDiffPage(data) {
|
|
|
98
116
|
selected,
|
|
99
117
|
canReview,
|
|
100
118
|
hasBaseline,
|
|
101
|
-
drifted
|
|
119
|
+
drifted,
|
|
120
|
+
usage: data.usage ?? [],
|
|
121
|
+
design: data.design ?? [],
|
|
122
|
+
publishedBaseDomain: data.publishedBaseDomain
|
|
102
123
|
}),
|
|
103
124
|
/* @__PURE__ */ jsx(ShortcutsDialog, {})
|
|
104
125
|
]
|
|
105
126
|
});
|
|
106
127
|
}
|
|
107
128
|
//#endregion
|
|
108
|
-
export { renderBuildDiffPage };
|
|
129
|
+
export { renderBuildDiffPage, selectedSnapshot };
|
|
109
130
|
|
|
110
131
|
//# sourceMappingURL=build-diff.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-diff.mjs","names":[],"sources":["../../src/pages/build-diff.tsx"],"sourcesContent":["import type { Build } from \"@storyshelf/core/schema\";\nimport type { Comment } from \"@storyshelf/core/schema\";\nimport type { Project } from \"@storyshelf/core/schema\";\nimport type { Snapshot } from \"@storyshelf/core/schema\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport { EmptyState } from \"../ui/components.tsx\";\nimport { DocumentLayout, type NavConfig, type RenderedContent } from \"../ui/document.tsx\";\nimport { canvasColumn, workspace } from \"../ui/styles/review-layout.ts\";\nimport { DiffActionBar } from \"./build-diff-actions.tsx\";\nimport { DiffComments } from \"./build-diff-comments.tsx\";\nimport { DiffHeader } from \"./build-diff-header.tsx\";\nimport { DiffNav } from \"./build-diff-nav.tsx\";\nimport { ShortcutsDialog } from \"./build-diff-shortcuts.tsx\";\nimport {
|
|
1
|
+
{"version":3,"file":"build-diff.mjs","names":[],"sources":["../../src/pages/build-diff.tsx"],"sourcesContent":["import type { Build } from \"@storyshelf/core/schema\";\nimport type { Comment } from \"@storyshelf/core/schema\";\nimport type { Project } from \"@storyshelf/core/schema\";\nimport type { Snapshot } from \"@storyshelf/core/schema\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport type { InsightPanelData } from \"../insights/panel.ts\";\nimport { EmptyState } from \"../ui/components.tsx\";\nimport { DocumentLayout, type NavConfig, type RenderedContent } from \"../ui/document.tsx\";\nimport { canvasColumn, workspace } from \"../ui/styles/review-layout.ts\";\nimport { DiffActionBar } from \"./build-diff-actions.tsx\";\nimport { DiffComments } from \"./build-diff-comments.tsx\";\nimport { ReviewNotices } from \"./build-diff-environment.tsx\";\nimport { DiffHeader } from \"./build-diff-header.tsx\";\nimport { InsightSlot } from \"./build-diff-insight.tsx\";\nimport { groupOf, reviewProgress } from \"./build-diff-model.ts\";\nimport { DiffNav } from \"./build-diff-nav.tsx\";\nimport { ShortcutsDialog } from \"./build-diff-shortcuts.tsx\";\nimport type { LinkedUsageItem } from \"./build-diff-usage.tsx\";\nimport { DiffViewer } from \"./build-diff-viewer.tsx\";\nimport type { DesignItem } from \"./design-links.tsx\";\n\n/** Data required to render the three-up build diff review page. */\nexport interface BuildDiffData {\n project: Project;\n build: Build;\n snapshots: Snapshot[];\n comments: Comment[];\n selectedId?: string;\n canReview: boolean;\n hasBaseline: Record<string, boolean>;\n /** Open snapshots whose baseline changed after their diff was computed. */\n drifted: Record<string, \"stale\" | \"removed\">;\n /** AI triage panel data; omitted/null hides the panel. */\n insight?: InsightPanelData | null;\n /** Linked packages the selected snapshot's story uses; omitted/empty hides the badges. */\n usage?: LinkedUsageItem[];\n /** Design links the selected snapshot's component resolves to; omitted/empty hides the strip. */\n design?: DesignItem[];\n /** Base domain for published Storybook deep links. */\n publishedBaseDomain?: string;\n}\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\ninterface DiffReviewGridProps {\n project: Project;\n build: Build;\n snapshots: Snapshot[];\n comments: Comment[];\n selected?: Snapshot;\n canReview: boolean;\n hasBaseline: Record<string, boolean>;\n drifted: Record<string, \"stale\" | \"removed\">;\n usage: LinkedUsageItem[];\n design: DesignItem[];\n publishedBaseDomain?: string;\n}\n\n/** The snapshot the review page shows: the requested one, else the first needing review. */\nexport function selectedSnapshot(snapshots: Snapshot[], selectedId?: string): Snapshot | undefined {\n return (\n snapshots.find((s) => s.id === selectedId) ??\n snapshots.find((s) => s.status === \"changed\" || s.status === \"new\") ??\n snapshots[0]\n );\n}\n\n/** Snapshot nav plus the viewer/comments column for the selected snapshot. */\nfunction DiffReviewGrid(\n props: DiffReviewGridProps,\n): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { project, build, snapshots, comments, selected, canReview, hasBaseline, drifted } = props;\n const { usage, design, publishedBaseDomain } = props;\n return (\n <div class={workspace}>\n <DiffNav project={project} build={build} snapshots={snapshots} selectedId={selected?.id} />\n <div class={canvasColumn}>\n {selected ? (\n <>\n <DiffViewer\n project={project}\n build={build}\n selected={selected}\n siblings={groupOf(snapshots, selected.id)?.members}\n hasBaseline={hasBaseline}\n drifted={drifted[selected.id]}\n usage={usage}\n design={design}\n publishedBaseDomain={publishedBaseDomain}\n />\n {project.reviewEnabled === false ? null : (\n <DiffComments\n project={project}\n build={build}\n selectedId={selected.id}\n selectedStoryId={selected.storyId}\n comments={comments}\n canReview={canReview}\n />\n )}\n <DiffActionBar\n project={project}\n build={build}\n snapshots={snapshots}\n selected={selected}\n canReview={canReview}\n drifted={drifted}\n />\n </>\n ) : (\n <EmptyState description=\"Select a snapshot to review.\" />\n )}\n </div>\n </div>\n );\n}\n\n/** Sidebar/breadcrumb context for the review page: Builds / <build> / Review. */\nfunction reviewNav(project: Project, build: Build): NavConfig {\n return {\n active: \"builds\",\n projectSlug: project.slug,\n projectName: project.name,\n trail: [\n {\n label: `${build.gitBranch} · ${build.gitSha.slice(0, 7)}`,\n href: `/projects/${project.slug}/builds/${build.id}`,\n },\n { label: \"Review\" },\n ],\n };\n}\n\n/** Three-up diff review page: baseline, current, and diff with keyboard review. */\nexport function renderBuildDiffPage(data: BuildDiffData): RenderedContent {\n const { project, build, snapshots, comments, selectedId, canReview, hasBaseline, drifted } = data;\n const selected = selectedSnapshot(snapshots, selectedId);\n const pendingGroups = reviewProgress(snapshots).pending;\n\n return (\n <DocumentLayout\n title={`Review · ${build.gitBranch}`}\n nav={reviewNav(project, build)}\n layout=\"wide\"\n >\n <DiffHeader\n project={project}\n build={build}\n snapshots={snapshots}\n pendingCount={pendingGroups}\n canReview={canReview}\n />\n <ReviewNotices\n project={project}\n build={build}\n snapshots={snapshots}\n drifted={drifted}\n canReview={canReview}\n />\n <InsightSlot project={project} build={build} data={data.insight} />\n\n {snapshots.length === 0 ? (\n <EmptyState\n title=\"No snapshots\"\n description=\"This build has no snapshots yet. Capture may still be running.\"\n />\n ) : (\n <DiffReviewGrid\n project={project}\n build={build}\n snapshots={snapshots}\n comments={comments}\n selected={selected}\n canReview={canReview}\n hasBaseline={hasBaseline}\n drifted={drifted}\n usage={data.usage ?? []}\n design={data.design ?? []}\n publishedBaseDomain={data.publishedBaseDomain}\n />\n )}\n <ShortcutsDialog />\n </DocumentLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA2DA,SAAgB,iBAAiB,WAAuB,YAA2C;CACjG,OACE,UAAU,MAAM,MAAM,EAAE,OAAO,UAAU,KACzC,UAAU,MAAM,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW,KAAK,KAClE,UAAU;AAEd;;AAGA,SAAS,eACP,OACgD;CAChD,MAAM,EAAE,SAAS,OAAO,WAAW,UAAU,UAAU,WAAW,aAAa,YAAY;CAC3F,MAAM,EAAE,OAAO,QAAQ,wBAAwB;CAC/C,OACE,qBAAC,OAAD;EAAK,OAAO;EAAZ,UAAA,CACE,oBAAC,SAAD;GAAkB;GAAgB;GAAkB;GAAW,YAAY,UAAU;EAAK,CAAA,GAC1F,oBAAC,OAAD;GAAK,OAAO;GACT,UAAA,WACC,qBAAA,UAAA,EAAA,UAAA;IACE,oBAAC,YAAD;KACW;KACF;KACG;KACV,UAAU,QAAQ,WAAW,SAAS,EAAE,CAAC,EAAE;KAC9B;KACb,SAAS,QAAQ,SAAS;KACnB;KACC;KACa;IACtB,CAAA;IACA,QAAQ,kBAAkB,QAAQ,OACjC,oBAAC,cAAD;KACW;KACF;KACP,YAAY,SAAS;KACrB,iBAAiB,SAAS;KAChB;KACC;IACZ,CAAA;IAEH,oBAAC,eAAD;KACW;KACF;KACI;KACD;KACC;KACF;IACV,CAAA;GACD,EAAA,CAAA,IAEF,oBAAC,YAAD,EAAY,aAAY,+BAAgC,CAAA;EAEvD,CAAA,CACF;;AAET;;AAGA,SAAS,UAAU,SAAkB,OAAyB;CAC5D,OAAO;EACL,QAAQ;EACR,aAAa,QAAQ;EACrB,aAAa,QAAQ;EACrB,OAAO,CACL;GACE,OAAO,GAAG,MAAM,UAAU,KAAK,MAAM,OAAO,MAAM,GAAG,CAAC;GACtD,MAAM,aAAa,QAAQ,KAAK,UAAU,MAAM;EAClD,GACA,EAAE,OAAO,SAAS,CACpB;CACF;AACF;;AAGA,SAAgB,oBAAoB,MAAsC;CACxE,MAAM,EAAE,SAAS,OAAO,WAAW,UAAU,YAAY,WAAW,aAAa,YAAY;CAC7F,MAAM,WAAW,iBAAiB,WAAW,UAAU;CACvD,MAAM,gBAAgB,eAAe,SAAS,CAAC,CAAC;CAEhD,OACE,qBAAC,gBAAD;EACE,OAAO,YAAY,MAAM;EACzB,KAAK,UAAU,SAAS,KAAK;EAC7B,QAAO;EAHT,UAAA;GAKE,oBAAC,YAAD;IACW;IACF;IACI;IACX,cAAc;IACH;GACZ,CAAA;GACD,oBAAC,eAAD;IACW;IACF;IACI;IACF;IACE;GACZ,CAAA;GACD,oBAAC,aAAD;IAAsB;IAAgB;IAAO,MAAM,KAAK;GAAU,CAAA;GAEjE,UAAU,WAAW,IACpB,oBAAC,YAAD;IACE,OAAM;IACN,aAAY;GACb,CAAA,IAED,oBAAC,gBAAD;IACW;IACF;IACI;IACD;IACA;IACC;IACE;IACJ;IACT,OAAO,KAAK,SAAS,CAAC;IACtB,QAAQ,KAAK,UAAU,CAAC;IACxB,qBAAqB,KAAK;GAC3B,CAAA;GAEH,oBAAC,iBAAD,CAAkB,CAAA;EACJ;;AAEpB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { Badge, Meta } from "../ui/feedback.mjs";
|
|
5
|
+
import { Card, SectionTitle } from "../ui/layout.mjs";
|
|
6
|
+
import { HStack, VStack } from "../ui/stacks.mjs";
|
|
7
|
+
import { summarizeTests } from "@storyshelf/core/capture";
|
|
8
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
9
|
+
//#region src/pages/build-tests.tsx
|
|
10
|
+
/** The line under the card title: counts, and what a failure means for the build. */
|
|
11
|
+
function testsSummaryNote(results) {
|
|
12
|
+
const { total, passed, failed } = summarizeTests(results);
|
|
13
|
+
if (total === 0) return "No Storybook tests ran.";
|
|
14
|
+
return failed > 0 ? `${failed} of ${total} failed. A failing test fails the build, like a failing play function.` : `All ${passed} passed.`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The build's Storybook `.test()` results: failures first with their message, passes behind a
|
|
18
|
+
* disclosure. Null when the build ran no tests (project setting off, or none in the Storybook).
|
|
19
|
+
*/
|
|
20
|
+
function TestsCard(props) {
|
|
21
|
+
if (props.results.length === 0) return null;
|
|
22
|
+
const failed = props.results.filter((result) => result.status === "failed");
|
|
23
|
+
const passed = props.results.filter((result) => result.status === "passed");
|
|
24
|
+
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
25
|
+
/* @__PURE__ */ jsx(SectionTitle, { children: "Storybook tests" }),
|
|
26
|
+
/* @__PURE__ */ jsxs(HStack, { children: [/* @__PURE__ */ jsx(Badge, {
|
|
27
|
+
tone: failed.length > 0 ? "danger" : "success",
|
|
28
|
+
children: failed.length > 0 ? `${failed.length} failed` : "all passed"
|
|
29
|
+
}), /* @__PURE__ */ jsxs(Meta, {
|
|
30
|
+
as: "span",
|
|
31
|
+
children: [
|
|
32
|
+
passed.length,
|
|
33
|
+
" passed · ",
|
|
34
|
+
failed.length,
|
|
35
|
+
" failed"
|
|
36
|
+
]
|
|
37
|
+
})] }),
|
|
38
|
+
/* @__PURE__ */ jsx(Meta, { children: testsSummaryNote(props.results) }),
|
|
39
|
+
/* @__PURE__ */ jsx(VStack, { children: failed.map((result) => /* @__PURE__ */ jsx(TestRow, { result }, result.id)) }),
|
|
40
|
+
passed.length > 0 ? /* @__PURE__ */ jsxs("details", { children: [/* @__PURE__ */ jsxs("summary", { children: [
|
|
41
|
+
"Passed tests (",
|
|
42
|
+
passed.length,
|
|
43
|
+
")"
|
|
44
|
+
] }), /* @__PURE__ */ jsx(VStack, { children: passed.map((result) => /* @__PURE__ */ jsx(TestRow, { result }, result.id)) })] }) : null
|
|
45
|
+
] });
|
|
46
|
+
}
|
|
47
|
+
function TestRow(props) {
|
|
48
|
+
const { result } = props;
|
|
49
|
+
return /* @__PURE__ */ jsxs(VStack, {
|
|
50
|
+
gap: "sm",
|
|
51
|
+
children: [/* @__PURE__ */ jsxs(HStack, {
|
|
52
|
+
align: "baseline",
|
|
53
|
+
wrap: false,
|
|
54
|
+
children: [/* @__PURE__ */ jsx(Badge, {
|
|
55
|
+
tone: result.status === "failed" ? "danger" : "success",
|
|
56
|
+
children: result.status
|
|
57
|
+
}), /* @__PURE__ */ jsxs("span", { children: [
|
|
58
|
+
result.title,
|
|
59
|
+
" ",
|
|
60
|
+
/* @__PURE__ */ jsxs(Meta, {
|
|
61
|
+
as: "span",
|
|
62
|
+
children: ["/ ", result.name]
|
|
63
|
+
})
|
|
64
|
+
] })]
|
|
65
|
+
}), result.error ? /* @__PURE__ */ jsx(Meta, {
|
|
66
|
+
as: "pre",
|
|
67
|
+
children: result.error
|
|
68
|
+
}) : null]
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { TestsCard, testsSummaryNote };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=build-tests.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-tests.mjs","names":[],"sources":["../../src/pages/build-tests.tsx"],"sourcesContent":["import { summarizeTests, type TestResult } from \"@storyshelf/core/capture\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport { Badge, Card, HStack, Meta, SectionTitle, VStack } from \"../ui/components.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\ntype Rendered = HtmlEscapedString | Promise<HtmlEscapedString>;\n\n/** The line under the card title: counts, and what a failure means for the build. */\nexport function testsSummaryNote(results: readonly TestResult[]): string {\n const { total, passed, failed } = summarizeTests(results);\n if (total === 0) {\n return \"No Storybook tests ran.\";\n }\n return failed > 0\n ? `${failed} of ${total} failed. A failing test fails the build, like a failing play function.`\n : `All ${passed} passed.`;\n}\n\n/**\n * The build's Storybook `.test()` results: failures first with their message, passes behind a\n * disclosure. Null when the build ran no tests (project setting off, or none in the Storybook).\n */\nexport function TestsCard(props: { results: readonly TestResult[] }): Rendered | null {\n if (props.results.length === 0) {\n return null;\n }\n const failed = props.results.filter((result) => result.status === \"failed\");\n const passed = props.results.filter((result) => result.status === \"passed\");\n return (\n <Card>\n <SectionTitle>Storybook tests</SectionTitle>\n <HStack>\n <Badge tone={failed.length > 0 ? \"danger\" : \"success\"}>\n {failed.length > 0 ? `${failed.length} failed` : \"all passed\"}\n </Badge>\n <Meta as=\"span\">\n {passed.length} passed · {failed.length} failed\n </Meta>\n </HStack>\n <Meta>{testsSummaryNote(props.results)}</Meta>\n <VStack>\n {failed.map((result) => (\n <TestRow key={result.id} result={result} />\n ))}\n </VStack>\n {passed.length > 0 ? (\n <details>\n <summary>Passed tests ({passed.length})</summary>\n <VStack>\n {passed.map((result) => (\n <TestRow key={result.id} result={result} />\n ))}\n </VStack>\n </details>\n ) : null}\n </Card>\n );\n}\n\nfunction TestRow(props: { result: TestResult }): Rendered {\n const { result } = props;\n return (\n <VStack gap=\"sm\">\n <HStack align=\"baseline\" wrap={false}>\n <Badge tone={result.status === \"failed\" ? \"danger\" : \"success\"}>{result.status}</Badge>\n <span>\n {result.title} <Meta as=\"span\">/ {result.name}</Meta>\n </span>\n </HStack>\n {result.error ? <Meta as=\"pre\">{result.error}</Meta> : null}\n </VStack>\n );\n}\n"],"mappings":";;;;;;;;;;AASA,SAAgB,iBAAiB,SAAwC;CACvE,MAAM,EAAE,OAAO,QAAQ,WAAW,eAAe,OAAO;CACxD,IAAI,UAAU,GACZ,OAAO;CAET,OAAO,SAAS,IACZ,GAAG,OAAO,MAAM,MAAM,0EACtB,OAAO,OAAO;AACpB;;;;;AAMA,SAAgB,UAAU,OAA4D;CACpF,IAAI,MAAM,QAAQ,WAAW,GAC3B,OAAO;CAET,MAAM,SAAS,MAAM,QAAQ,QAAQ,WAAW,OAAO,WAAW,QAAQ;CAC1E,MAAM,SAAS,MAAM,QAAQ,QAAQ,WAAW,OAAO,WAAW,QAAQ;CAC1E,OACE,qBAAC,MAAD,EAAA,UAAA;EACE,oBAAC,cAAD,EAAA,UAAc,kBAA6B,CAAA;EAC3C,qBAAC,QAAD,EAAA,UAAA,CACE,oBAAC,OAAD;GAAO,MAAM,OAAO,SAAS,IAAI,WAAW;GACzC,UAAA,OAAO,SAAS,IAAI,GAAG,OAAO,OAAO,WAAW;EAC5C,CAAA,GACP,qBAAC,MAAD;GAAM,IAAG;GAAT,UAAA;IACG,OAAO;IAAO;IAAW,OAAO;IAAO;GACpC;EACA,CAAA,CAAA,EAAA,CAAA;EACR,oBAAC,MAAD,EAAA,UAAO,iBAAiB,MAAM,OAAO,EAAQ,CAAA;EAC7C,oBAAC,QAAD,EAAA,UACG,OAAO,KAAK,WACX,oBAAC,SAAD,EAAiC,OAAS,GAA5B,OAAO,EAAqB,CAC3C,EACK,CAAA;EACP,OAAO,SAAS,IACf,qBAAC,WAAD,EAAA,UAAA,CACE,qBAAC,WAAD,EAAA,UAAA;GAAS;GAAe,OAAO;GAAO;EAAU,EAAA,CAAA,GAChD,oBAAC,QAAD,EAAA,UACG,OAAO,KAAK,WACX,oBAAC,SAAD,EAAiC,OAAS,GAA5B,OAAO,EAAqB,CAC3C,EACK,CAAA,CACD,EAAA,CAAA,IACP;CACA,EAAA,CAAA;AAEV;AAEA,SAAS,QAAQ,OAAyC;CACxD,MAAM,EAAE,WAAW;CACnB,OACE,qBAAC,QAAD;EAAQ,KAAI;EAAZ,UAAA,CACE,qBAAC,QAAD;GAAQ,OAAM;GAAW,MAAM;GAA/B,UAAA,CACE,oBAAC,OAAD;IAAO,MAAM,OAAO,WAAW,WAAW,WAAW;IAAY,UAAA,OAAO;GAAc,CAAA,GACtF,qBAAC,QAAD,EAAA,UAAA;IACG,OAAO;IAAM;IAAC,qBAAC,MAAD;KAAM,IAAG;KAAT,UAAA,CAAgB,MAAG,OAAO,IAAW;;GAChD,EAAA,CAAA,CACA;EACP,CAAA,GAAA,OAAO,QAAQ,oBAAC,MAAD;GAAM,IAAG;GAAO,UAAA,OAAO;EAAY,CAAA,IAAI,IACjD;;AAEZ"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { Button, Tabs } from "../ui/buttons.mjs";
|
|
5
|
+
import { Alert, Badge, EmptyState, Meta, statusTone } from "../ui/feedback.mjs";
|
|
6
|
+
import { Card, PageHeader, SectionTitle } from "../ui/layout.mjs";
|
|
7
|
+
import { HStack, VStack } from "../ui/stacks.mjs";
|
|
8
|
+
import { Table } from "../ui/table.mjs";
|
|
9
|
+
import { Thumbnail } from "../ui/thumbnail.mjs";
|
|
10
|
+
import { DocumentLayout } from "../ui/document.mjs";
|
|
11
|
+
import { snapshotCardBody, snapshotCardHead, snapshotCardMeta, snapshotGrid } from "../ui/styles/review.mjs";
|
|
12
|
+
import { snapshotImageUrl, snapshotVariant } from "./build-diff-model.mjs";
|
|
13
|
+
import { LinkedUsage } from "./build-diff-usage.mjs";
|
|
14
|
+
import { DesignReferences, DesignStrip } from "./design-links.mjs";
|
|
15
|
+
import { ComponentBuilds, StoriesTab } from "./component-stories.mjs";
|
|
16
|
+
import { worstStatus } from "@storyshelf/core/models";
|
|
17
|
+
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
18
|
+
import { createUrlBuilder } from "@storyshelf/core/urls";
|
|
19
|
+
//#region src/pages/component-detail.tsx
|
|
20
|
+
const TAB_LABELS = {
|
|
21
|
+
stories: "Stories",
|
|
22
|
+
states: "States",
|
|
23
|
+
history: "History",
|
|
24
|
+
design: "Design",
|
|
25
|
+
linked: "Linked"
|
|
26
|
+
};
|
|
27
|
+
/** The tabs a page offers: the catalog's two in both modes, the snapshot ones only with `testing`. */
|
|
28
|
+
function componentTabs(testing) {
|
|
29
|
+
return testing ? [
|
|
30
|
+
"states",
|
|
31
|
+
"stories",
|
|
32
|
+
"history",
|
|
33
|
+
"design",
|
|
34
|
+
"linked"
|
|
35
|
+
] : ["stories", "history"];
|
|
36
|
+
}
|
|
37
|
+
/** Component detail page: stories, states, history, design and linked packages of one story file. */
|
|
38
|
+
function renderComponentPage(data) {
|
|
39
|
+
const { project, importPath, title } = data;
|
|
40
|
+
return /* @__PURE__ */ jsxs(DocumentLayout, {
|
|
41
|
+
title: `${title} · Component`,
|
|
42
|
+
nav: {
|
|
43
|
+
active: "library",
|
|
44
|
+
projectSlug: project.slug,
|
|
45
|
+
projectName: project.name,
|
|
46
|
+
trail: [{ label: title }]
|
|
47
|
+
},
|
|
48
|
+
children: [
|
|
49
|
+
/* @__PURE__ */ jsx(PageHeader, {
|
|
50
|
+
title,
|
|
51
|
+
description: /* @__PURE__ */ jsx("span", {
|
|
52
|
+
class: "mono",
|
|
53
|
+
children: importPath
|
|
54
|
+
}),
|
|
55
|
+
meta: /* @__PURE__ */ jsx(HeaderMeta, { data })
|
|
56
|
+
}),
|
|
57
|
+
/* @__PURE__ */ jsx(PresenceNotice, { data }),
|
|
58
|
+
/* @__PURE__ */ jsx(Tabs, {
|
|
59
|
+
label: "Component sections",
|
|
60
|
+
tabs: componentTabs(data.testing).map((tab) => ({
|
|
61
|
+
label: TAB_LABELS[tab],
|
|
62
|
+
href: `?tab=${tab}`,
|
|
63
|
+
active: data.tab === tab
|
|
64
|
+
}))
|
|
65
|
+
}),
|
|
66
|
+
/* @__PURE__ */ jsx(ActiveTab, { data })
|
|
67
|
+
]
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function HeaderMeta(props) {
|
|
71
|
+
const { captured, catalog } = props.data;
|
|
72
|
+
const stories = catalog.entries.filter((entry) => entry.type === "story").length;
|
|
73
|
+
const docs = catalog.entries.length - stories;
|
|
74
|
+
const counts = `${stories} ${stories === 1 ? "story" : "stories"}${docs > 0 ? ` · ${docs} docs` : ""}`;
|
|
75
|
+
if (!captured) return /* @__PURE__ */ jsx(Fragment, { children: counts });
|
|
76
|
+
const status = worstStatus(captured.groups.map((group) => group.primary.status));
|
|
77
|
+
const modes = new Set(captured.groups.map((group) => group.modeKey)).size;
|
|
78
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
79
|
+
/* @__PURE__ */ jsx(Badge, {
|
|
80
|
+
tone: statusTone(status),
|
|
81
|
+
icon: true,
|
|
82
|
+
children: status
|
|
83
|
+
}),
|
|
84
|
+
" ",
|
|
85
|
+
counts,
|
|
86
|
+
" · ",
|
|
87
|
+
modes,
|
|
88
|
+
" ",
|
|
89
|
+
modes === 1 ? "mode" : "modes"
|
|
90
|
+
] });
|
|
91
|
+
}
|
|
92
|
+
function PresenceNotice(props) {
|
|
93
|
+
const { data } = props;
|
|
94
|
+
const urls = createUrlBuilder("/", data.publishedBaseDomain);
|
|
95
|
+
const presence = data.captured?.presence ?? data.catalog.presence;
|
|
96
|
+
const { project } = data;
|
|
97
|
+
if (presence.kind === "moved") return /* @__PURE__ */ jsx(Alert, {
|
|
98
|
+
tone: "warning",
|
|
99
|
+
title: "This component moved",
|
|
100
|
+
children: /* @__PURE__ */ jsxs(HStack, { children: [/* @__PURE__ */ jsx("span", { children: "The file now lives at" }), /* @__PURE__ */ jsx(Button, {
|
|
101
|
+
variant: "secondary",
|
|
102
|
+
size: "sm",
|
|
103
|
+
href: urls.component(project.slug, presence.to),
|
|
104
|
+
children: presence.to
|
|
105
|
+
})] })
|
|
106
|
+
});
|
|
107
|
+
if (presence.kind === "deleted") return /* @__PURE__ */ jsxs(Alert, {
|
|
108
|
+
tone: "warning",
|
|
109
|
+
title: "Not in the latest build",
|
|
110
|
+
children: [
|
|
111
|
+
"Last seen in",
|
|
112
|
+
" ",
|
|
113
|
+
/* @__PURE__ */ jsxs("a", {
|
|
114
|
+
href: urls.build(project.slug, presence.lastSeen.id),
|
|
115
|
+
children: [
|
|
116
|
+
"build ",
|
|
117
|
+
presence.lastSeen.gitSha.slice(0, 7),
|
|
118
|
+
" (",
|
|
119
|
+
presence.lastSeen.gitBranch,
|
|
120
|
+
")"
|
|
121
|
+
]
|
|
122
|
+
}),
|
|
123
|
+
". The states below are from that build."
|
|
124
|
+
]
|
|
125
|
+
});
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
function ActiveTab(props) {
|
|
129
|
+
const { data } = props;
|
|
130
|
+
if (data.tab === "stories") return /* @__PURE__ */ jsx(StoriesTab, { data });
|
|
131
|
+
if (data.tab === "history") return /* @__PURE__ */ jsx(HistoryTab, { data });
|
|
132
|
+
if (data.tab === "design") return /* @__PURE__ */ jsx(DesignTab, { design: data.design });
|
|
133
|
+
if (data.tab === "linked") return /* @__PURE__ */ jsx(LinkedTab, { data });
|
|
134
|
+
return /* @__PURE__ */ jsx(StatesTab, { data });
|
|
135
|
+
}
|
|
136
|
+
function StatesTab(props) {
|
|
137
|
+
const { data } = props;
|
|
138
|
+
const { project, captured } = data;
|
|
139
|
+
if (!captured) return /* @__PURE__ */ jsx(EmptyState, {
|
|
140
|
+
title: "Not captured yet",
|
|
141
|
+
description: "No capture has rendered this file's stories. Its stories are listed under Stories."
|
|
142
|
+
});
|
|
143
|
+
const { shownBuild } = captured;
|
|
144
|
+
const urls = createUrlBuilder("/", data.publishedBaseDomain);
|
|
145
|
+
return /* @__PURE__ */ jsx("div", {
|
|
146
|
+
class: snapshotGrid,
|
|
147
|
+
children: captured.groups.map((group) => /* @__PURE__ */ jsx("div", {
|
|
148
|
+
id: group.storyId,
|
|
149
|
+
children: /* @__PURE__ */ jsxs(Card, {
|
|
150
|
+
padded: false,
|
|
151
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
152
|
+
class: snapshotCardHead,
|
|
153
|
+
children: [/* @__PURE__ */ jsx("strong", { children: group.primary.storyName }), /* @__PURE__ */ jsxs("span", {
|
|
154
|
+
class: snapshotCardMeta,
|
|
155
|
+
children: [
|
|
156
|
+
/* @__PURE__ */ jsx(Badge, {
|
|
157
|
+
tone: statusTone(group.primary.status),
|
|
158
|
+
children: group.primary.status
|
|
159
|
+
}),
|
|
160
|
+
" ",
|
|
161
|
+
snapshotVariant(group.primary)
|
|
162
|
+
]
|
|
163
|
+
})]
|
|
164
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
165
|
+
class: snapshotCardBody,
|
|
166
|
+
children: /* @__PURE__ */ jsx("a", {
|
|
167
|
+
href: `${urls.buildDiff(project.slug, shownBuild.id)}?snapshot=${group.representative.id}`,
|
|
168
|
+
children: /* @__PURE__ */ jsx(Thumbnail, {
|
|
169
|
+
src: snapshotImageUrl(project, shownBuild, group.primary, "image"),
|
|
170
|
+
alt: `${group.primary.storyTitle} / ${group.primary.storyName}`
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
})]
|
|
174
|
+
})
|
|
175
|
+
}, group.key))
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function HistoryTab(props) {
|
|
179
|
+
const { data } = props;
|
|
180
|
+
return /* @__PURE__ */ jsxs(VStack, { children: [data.captured ? /* @__PURE__ */ jsx(CapturedHistory, {
|
|
181
|
+
data,
|
|
182
|
+
captured: data.captured
|
|
183
|
+
}) : null, /* @__PURE__ */ jsx(ComponentBuilds, { data })] });
|
|
184
|
+
}
|
|
185
|
+
function CapturedHistory(props) {
|
|
186
|
+
const { data, captured } = props;
|
|
187
|
+
return /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(SectionTitle, { children: "Captured builds" }), /* @__PURE__ */ jsx(Table, { children: /* @__PURE__ */ jsxs("table", { children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
188
|
+
/* @__PURE__ */ jsx("th", { children: "Build" }),
|
|
189
|
+
/* @__PURE__ */ jsx("th", { children: "Branch" }),
|
|
190
|
+
/* @__PURE__ */ jsx("th", { children: "States changed" }),
|
|
191
|
+
/* @__PURE__ */ jsx("th", { children: "Status" }),
|
|
192
|
+
/* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsx("span", {
|
|
193
|
+
class: "visually-hidden",
|
|
194
|
+
children: "Actions"
|
|
195
|
+
}) })
|
|
196
|
+
] }) }), /* @__PURE__ */ jsx("tbody", { children: captured.history.map((row) => /* @__PURE__ */ jsx(CapturedRow, {
|
|
197
|
+
row,
|
|
198
|
+
data
|
|
199
|
+
}, row.build.id)) })] }) })] });
|
|
200
|
+
}
|
|
201
|
+
function CapturedRow(props) {
|
|
202
|
+
const { row, data } = props;
|
|
203
|
+
const urls = createUrlBuilder("/", data.publishedBaseDomain);
|
|
204
|
+
const slug = data.project.slug;
|
|
205
|
+
return /* @__PURE__ */ jsxs("tr", { children: [
|
|
206
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("a", {
|
|
207
|
+
href: urls.build(slug, row.build.id),
|
|
208
|
+
children: row.build.gitSha.slice(0, 7)
|
|
209
|
+
}) }),
|
|
210
|
+
/* @__PURE__ */ jsx("td", { children: row.build.gitBranch }),
|
|
211
|
+
/* @__PURE__ */ jsxs("td", { children: [
|
|
212
|
+
row.changedStates,
|
|
213
|
+
" / ",
|
|
214
|
+
row.states
|
|
215
|
+
] }),
|
|
216
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(Badge, {
|
|
217
|
+
tone: statusTone(row.status),
|
|
218
|
+
children: row.status
|
|
219
|
+
}) }),
|
|
220
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(Button, {
|
|
221
|
+
variant: "ghost",
|
|
222
|
+
size: "sm",
|
|
223
|
+
href: `${urls.buildDiff(slug, row.build.id)}?snapshot=${row.snapshotId}`,
|
|
224
|
+
children: "Review"
|
|
225
|
+
}) })
|
|
226
|
+
] });
|
|
227
|
+
}
|
|
228
|
+
function DesignTab(props) {
|
|
229
|
+
if (props.design.length === 0) return /* @__PURE__ */ jsx(EmptyState, {
|
|
230
|
+
title: "No design linked",
|
|
231
|
+
description: "Declare a design link in the stories’ parameters, or map this file under Settings → Design."
|
|
232
|
+
});
|
|
233
|
+
return /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(DesignStrip, { items: props.design }), /* @__PURE__ */ jsx(DesignReferences, { items: props.design })] });
|
|
234
|
+
}
|
|
235
|
+
function LinkedTab(props) {
|
|
236
|
+
const { data } = props;
|
|
237
|
+
if (data.usage.length === 0) return /* @__PURE__ */ jsx(EmptyState, {
|
|
238
|
+
title: "No linked packages",
|
|
239
|
+
description: "This component's stories use no packages linked to another project in the latest build."
|
|
240
|
+
});
|
|
241
|
+
return /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(Meta, { children: "Packages these stories use, linked to the projects that publish them." }), /* @__PURE__ */ jsx(LinkedUsage, {
|
|
242
|
+
usage: data.usage,
|
|
243
|
+
publishedBaseDomain: data.publishedBaseDomain
|
|
244
|
+
})] });
|
|
245
|
+
}
|
|
246
|
+
//#endregion
|
|
247
|
+
export { componentTabs, renderComponentPage };
|
|
248
|
+
|
|
249
|
+
//# sourceMappingURL=component-detail.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-detail.mjs","names":[],"sources":["../../src/pages/component-detail.tsx"],"sourcesContent":["import {\n worstStatus,\n type ComponentHistoryRow,\n type ComponentPresence,\n} from \"@storyshelf/core/models\";\nimport type { Build, Project } from \"@storyshelf/core/schema\";\nimport { createUrlBuilder } from \"@storyshelf/core/urls\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport type { ComponentCatalogView } from \"../routers/component-catalog.handlers.ts\";\nimport type { ComponentReview } from \"../routers/review-section.handlers.ts\";\nimport {\n Alert,\n Badge,\n Button,\n Card,\n EmptyState,\n HStack,\n Meta,\n PageHeader,\n SectionTitle,\n Table,\n Tabs,\n Thumbnail,\n VStack,\n statusTone,\n} from \"../ui/components.tsx\";\nimport { DocumentLayout, type RenderedContent } from \"../ui/document.tsx\";\nimport {\n snapshotCardBody,\n snapshotCardHead,\n snapshotCardMeta,\n snapshotGrid,\n} from \"../ui/styles/review.ts\";\nimport type { SnapshotGroup } from \"./build-diff-model.ts\";\nimport { snapshotImageUrl, snapshotVariant } from \"./build-diff-model.ts\";\nimport { LinkedUsage, type LinkedUsageItem } from \"./build-diff-usage.tsx\";\nimport { ComponentBuilds, StoriesTab } from \"./component-stories.tsx\";\nimport { DesignReferences, DesignStrip, type DesignItem } from \"./design-links.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\ntype Rendered = HtmlEscapedString | Promise<HtmlEscapedString>;\n\n/** Tabs of the component page. */\nexport type ComponentTab = \"stories\" | \"states\" | \"history\" | \"design\" | \"linked\";\n\nconst TAB_LABELS: Record<ComponentTab, string> = {\n stories: \"Stories\",\n states: \"States\",\n history: \"History\",\n design: \"Design\",\n linked: \"Linked\",\n};\n\n/** The tabs a page offers: the catalog's two in both modes, the snapshot ones only with `testing`. */\nexport function componentTabs(testing: boolean): readonly ComponentTab[] {\n return testing ? [\"states\", \"stories\", \"history\", \"design\", \"linked\"] : [\"stories\", \"history\"];\n}\n\n/** What captured snapshots add to a component (`testing` only; absent until it was captured). */\nexport interface CapturedComponent {\n /** Newest build that has the file; its states fill the States/Design/Linked tabs. */\n shownBuild: Build;\n groups: SnapshotGroup[];\n history: ComponentHistoryRow[];\n presence: ComponentPresence;\n}\n\n/** Data behind the component page. */\nexport interface ComponentPageData {\n project: Project;\n /** Normalized story-file path (the page's identity). */\n importPath: string;\n /** Title of the newest snapshot, else the catalog's (retitles update in place). */\n title: string;\n tab: ComponentTab;\n /** Whether the instance has `testing`. */\n testing: boolean;\n /** The published Storybook's view of the file: stories, docs entries, build history. */\n catalog: ComponentCatalogView;\n captured: CapturedComponent | null;\n /** Story id the Stories tab previews (the first story when unset or unknown). */\n story: string | undefined;\n /**\n * Root the preview frame loads the Storybook from: the build's own URL when it is open to anyone\n * who reaches the app, a signed `/_s/<token>` URL for a private build (needs `testing` and a\n * `secret`), or null when no frame can load.\n */\n previewBase: string | null;\n design: DesignItem[];\n usage: LinkedUsageItem[];\n /** Story comments of the shown build; null when the project has review off. */\n review: ComponentReview | null;\n publishedBaseDomain?: string;\n}\n\n/** Component detail page: stories, states, history, design and linked packages of one story file. */\nexport function renderComponentPage(data: ComponentPageData): RenderedContent {\n const { project, importPath, title } = data;\n return (\n <DocumentLayout\n title={`${title} · Component`}\n nav={{\n active: \"library\",\n projectSlug: project.slug,\n projectName: project.name,\n trail: [{ label: title }],\n }}\n >\n <PageHeader\n title={title}\n description={<span class=\"mono\">{importPath}</span>}\n meta={<HeaderMeta data={data} />}\n />\n <PresenceNotice data={data} />\n <Tabs\n label=\"Component sections\"\n tabs={componentTabs(data.testing).map((tab) => ({\n label: TAB_LABELS[tab],\n href: `?tab=${tab}`,\n active: data.tab === tab,\n }))}\n />\n <ActiveTab data={data} />\n </DocumentLayout>\n );\n}\n\nfunction HeaderMeta(props: { data: ComponentPageData }): Rendered {\n const { captured, catalog } = props.data;\n const stories = catalog.entries.filter((entry) => entry.type === \"story\").length;\n const docs = catalog.entries.length - stories;\n const counts = `${stories} ${stories === 1 ? \"story\" : \"stories\"}${docs > 0 ? ` · ${docs} docs` : \"\"}`;\n if (!captured) {\n return <>{counts}</>;\n }\n const status = worstStatus(captured.groups.map((group) => group.primary.status));\n const modes = new Set(captured.groups.map((group) => group.modeKey)).size;\n return (\n <>\n <Badge tone={statusTone(status)} icon>\n {status}\n </Badge>{\" \"}\n {counts} · {modes} {modes === 1 ? \"mode\" : \"modes\"}\n </>\n );\n}\n\nfunction PresenceNotice(props: { data: ComponentPageData }): Rendered | null {\n const { data } = props;\n const urls = createUrlBuilder(\"/\", data.publishedBaseDomain);\n const presence = data.captured?.presence ?? data.catalog.presence;\n const { project } = data;\n if (presence.kind === \"moved\") {\n return (\n <Alert tone=\"warning\" title=\"This component moved\">\n <HStack>\n <span>The file now lives at</span>\n <Button variant=\"secondary\" size=\"sm\" href={urls.component(project.slug, presence.to)}>\n {presence.to}\n </Button>\n </HStack>\n </Alert>\n );\n }\n if (presence.kind === \"deleted\") {\n return (\n <Alert tone=\"warning\" title=\"Not in the latest build\">\n Last seen in{\" \"}\n <a href={urls.build(project.slug, presence.lastSeen.id)}>\n build {presence.lastSeen.gitSha.slice(0, 7)} ({presence.lastSeen.gitBranch})\n </a>\n . The states below are from that build.\n </Alert>\n );\n }\n return null;\n}\n\nfunction ActiveTab(props: { data: ComponentPageData }): Rendered {\n const { data } = props;\n if (data.tab === \"stories\") return <StoriesTab data={data} />;\n if (data.tab === \"history\") return <HistoryTab data={data} />;\n if (data.tab === \"design\") return <DesignTab design={data.design} />;\n if (data.tab === \"linked\") return <LinkedTab data={data} />;\n return <StatesTab data={data} />;\n}\n\nfunction StatesTab(props: { data: ComponentPageData }): Rendered {\n const { data } = props;\n const { project, captured } = data;\n if (!captured) {\n return (\n <EmptyState\n title=\"Not captured yet\"\n description=\"No capture has rendered this file's stories. Its stories are listed under Stories.\"\n />\n );\n }\n const { shownBuild } = captured;\n const urls = createUrlBuilder(\"/\", data.publishedBaseDomain);\n return (\n <div class={snapshotGrid}>\n {captured.groups.map((group): Rendered => (\n <div key={group.key} id={group.storyId}>\n <Card padded={false}>\n <div class={snapshotCardHead}>\n <strong>{group.primary.storyName}</strong>\n <span class={snapshotCardMeta}>\n <Badge tone={statusTone(group.primary.status)}>{group.primary.status}</Badge>{\" \"}\n {snapshotVariant(group.primary)}\n </span>\n </div>\n <div class={snapshotCardBody}>\n <a\n href={`${urls.buildDiff(project.slug, shownBuild.id)}?snapshot=${group.representative.id}`}\n >\n <Thumbnail\n src={snapshotImageUrl(project, shownBuild, group.primary, \"image\")}\n alt={`${group.primary.storyTitle} / ${group.primary.storyName}`}\n />\n </a>\n </div>\n </Card>\n </div>\n ))}\n </div>\n );\n}\n\nfunction HistoryTab(props: { data: ComponentPageData }): Rendered {\n const { data } = props;\n return (\n <VStack>\n {data.captured ? <CapturedHistory data={data} captured={data.captured} /> : null}\n <ComponentBuilds data={data} />\n </VStack>\n );\n}\n\nfunction CapturedHistory(props: {\n data: ComponentPageData;\n captured: CapturedComponent;\n}): Rendered {\n const { data, captured } = props;\n return (\n <Card>\n <SectionTitle>Captured builds</SectionTitle>\n <Table>\n <table>\n <thead>\n <tr>\n <th>Build</th>\n <th>Branch</th>\n <th>States changed</th>\n <th>Status</th>\n <th>\n <span class=\"visually-hidden\">Actions</span>\n </th>\n </tr>\n </thead>\n <tbody>\n {captured.history.map((row): Rendered => (\n <CapturedRow key={row.build.id} row={row} data={data} />\n ))}\n </tbody>\n </table>\n </Table>\n </Card>\n );\n}\n\nfunction CapturedRow(props: { row: ComponentHistoryRow; data: ComponentPageData }): Rendered {\n const { row, data } = props;\n const urls = createUrlBuilder(\"/\", data.publishedBaseDomain);\n const slug = data.project.slug;\n return (\n <tr>\n <td>\n <a href={urls.build(slug, row.build.id)}>{row.build.gitSha.slice(0, 7)}</a>\n </td>\n <td>{row.build.gitBranch}</td>\n <td>\n {row.changedStates} / {row.states}\n </td>\n <td>\n <Badge tone={statusTone(row.status)}>{row.status}</Badge>\n </td>\n <td>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n href={`${urls.buildDiff(slug, row.build.id)}?snapshot=${row.snapshotId}`}\n >\n Review\n </Button>\n </td>\n </tr>\n );\n}\n\nfunction DesignTab(props: { design: DesignItem[] }): Rendered {\n if (props.design.length === 0) {\n return (\n <EmptyState\n title=\"No design linked\"\n description=\"Declare a design link in the stories’ parameters, or map this file under Settings → Design.\"\n />\n );\n }\n return (\n <Card>\n <DesignStrip items={props.design} />\n <DesignReferences items={props.design} />\n </Card>\n );\n}\n\nfunction LinkedTab(props: { data: ComponentPageData }): Rendered {\n const { data } = props;\n if (data.usage.length === 0) {\n return (\n <EmptyState\n title=\"No linked packages\"\n description=\"This component's stories use no packages linked to another project in the latest build.\"\n />\n );\n }\n return (\n <Card>\n <Meta>Packages these stories use, linked to the projects that publish them.</Meta>\n <LinkedUsage usage={data.usage} publishedBaseDomain={data.publishedBaseDomain} />\n </Card>\n );\n}\n"],"mappings":"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"}
|