@storyshelf/app 0.6.2 → 0.8.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 +2 -6
- package/dist/asset-manifest.mjs +14 -7
- package/dist/asset-manifest.mjs.map +1 -1
- package/dist/assets/{htmx.min.js → htmx.mjs} +9 -1
- package/dist/assets/htmx.mjs.map +1 -0
- package/dist/assets/mark.mjs +9 -0
- package/dist/assets/mark.mjs.map +1 -0
- package/dist/boot-tasks.mjs +79 -0
- package/dist/boot-tasks.mjs.map +1 -0
- package/dist/build-state-flags.mjs +17 -0
- package/dist/build-state-flags.mjs.map +1 -0
- package/dist/build-state-setup.mjs +74 -0
- package/dist/build-state-setup.mjs.map +1 -0
- package/dist/capture-alerts.mjs +45 -0
- package/dist/capture-alerts.mjs.map +1 -0
- package/dist/capture-process.mjs +58 -0
- package/dist/capture-process.mjs.map +1 -0
- package/dist/capture-setup.mjs +96 -119
- package/dist/capture-setup.mjs.map +1 -1
- package/dist/connections.mjs +33 -0
- package/dist/connections.mjs.map +1 -0
- package/dist/consumer-setup.mjs +57 -0
- package/dist/consumer-setup.mjs.map +1 -0
- package/dist/credential-keys.mjs +1 -2
- package/dist/credentials.mjs +1 -2
- package/dist/git-status.mjs +45 -0
- package/dist/git-status.mjs.map +1 -0
- package/dist/hidden-data.mjs +23 -0
- package/dist/hidden-data.mjs.map +1 -0
- package/dist/index.d.mts +3 -5
- package/dist/index.mjs +204 -127
- package/dist/index.mjs.map +1 -1
- package/dist/insights/budget.mjs +48 -0
- package/dist/insights/budget.mjs.map +1 -0
- package/dist/insights/deps.mjs +20 -0
- package/dist/insights/deps.mjs.map +1 -0
- package/dist/insights/errors.mjs +21 -0
- package/dist/insights/errors.mjs.map +1 -0
- package/dist/insights/health-panel.mjs +39 -0
- package/dist/insights/health-panel.mjs.map +1 -0
- package/dist/insights/job.mjs +102 -0
- package/dist/insights/job.mjs.map +1 -0
- package/dist/insights/load-health.mjs +58 -0
- package/dist/insights/load-health.mjs.map +1 -0
- package/dist/insights/load-triage.mjs +97 -0
- package/dist/insights/load-triage.mjs.map +1 -0
- package/dist/insights/panel.mjs +27 -0
- package/dist/insights/panel.mjs.map +1 -0
- package/dist/insights/request.mjs +112 -0
- package/dist/insights/request.mjs.map +1 -0
- package/dist/insights/stale.mjs +20 -0
- package/dist/insights/stale.mjs.map +1 -0
- package/dist/insights/usage-report.mjs +59 -0
- package/dist/insights/usage-report.mjs.map +1 -0
- package/dist/insights/view.mjs +47 -0
- package/dist/insights/view.mjs.map +1 -0
- package/dist/job-canceller.mjs +13 -0
- package/dist/job-canceller.mjs.map +1 -0
- package/dist/job-queue.mjs +50 -0
- package/dist/job-queue.mjs.map +1 -0
- package/dist/jobs-setup.mjs +131 -0
- package/dist/jobs-setup.mjs.map +1 -0
- package/dist/lifecycle.mjs +93 -22
- package/dist/lifecycle.mjs.map +1 -1
- package/dist/middleware/auth-gate.mjs +5 -4
- package/dist/middleware/auth-gate.mjs.map +1 -1
- package/dist/middleware/csrf.mjs +1 -2
- package/dist/middleware/rate-limit.mjs +42 -35
- package/dist/middleware/rate-limit.mjs.map +1 -1
- package/dist/middleware/request-log.mjs +4 -4
- package/dist/middleware/request-log.mjs.map +1 -1
- package/dist/middleware/require-review.mjs +50 -0
- package/dist/middleware/require-review.mjs.map +1 -0
- package/dist/middleware/require-testing.mjs +40 -0
- package/dist/middleware/require-testing.mjs.map +1 -0
- package/dist/middleware/server-timing.mjs +1 -2
- package/dist/middleware/setup-gate.mjs +1 -2
- package/dist/middleware/setup-gate.mjs.map +1 -1
- package/dist/middleware/store-scope.mjs +13 -4
- package/dist/middleware/store-scope.mjs.map +1 -1
- package/dist/notifications-pause.mjs +32 -0
- package/dist/notifications-pause.mjs.map +1 -0
- package/dist/notify.d.mts +3 -4
- package/dist/notify.mjs +79 -41
- package/dist/notify.mjs.map +1 -1
- package/dist/pages/a11y-violations.mjs +115 -0
- package/dist/pages/a11y-violations.mjs.map +1 -0
- package/dist/pages/admin-ai.mjs +77 -0
- package/dist/pages/admin-ai.mjs.map +1 -0
- package/dist/pages/admin-system-origin.mjs +132 -0
- package/dist/pages/admin-system-origin.mjs.map +1 -0
- package/dist/pages/admin-system-targets.mjs +52 -0
- package/dist/pages/admin-system-targets.mjs.map +1 -0
- package/dist/pages/admin-system.mjs +64 -15
- package/dist/pages/admin-system.mjs.map +1 -1
- package/dist/pages/build-detail.mjs +105 -78
- package/dist/pages/build-detail.mjs.map +1 -1
- package/dist/pages/build-diff-actions.mjs +88 -35
- package/dist/pages/build-diff-actions.mjs.map +1 -1
- package/dist/pages/build-diff-comments.mjs +17 -16
- package/dist/pages/build-diff-comments.mjs.map +1 -1
- package/dist/pages/build-diff-default.mjs +53 -0
- package/dist/pages/build-diff-default.mjs.map +1 -0
- package/dist/pages/build-diff-environment.mjs +81 -0
- package/dist/pages/build-diff-environment.mjs.map +1 -0
- package/dist/pages/build-diff-header.mjs +23 -6
- package/dist/pages/build-diff-header.mjs.map +1 -1
- package/dist/pages/build-diff-insight.mjs +112 -0
- package/dist/pages/build-diff-insight.mjs.map +1 -0
- package/dist/pages/build-diff-model.mjs +74 -12
- package/dist/pages/build-diff-model.mjs.map +1 -1
- package/dist/pages/build-diff-nav.mjs +29 -14
- package/dist/pages/build-diff-nav.mjs.map +1 -1
- package/dist/pages/build-diff-reference.mjs +84 -0
- package/dist/pages/build-diff-reference.mjs.map +1 -0
- package/dist/pages/build-diff-shortcuts.mjs +6 -3
- package/dist/pages/build-diff-shortcuts.mjs.map +1 -1
- package/dist/pages/build-diff-stale.mjs +1 -2
- package/dist/pages/build-diff-usage.mjs +62 -0
- package/dist/pages/build-diff-usage.mjs.map +1 -0
- package/dist/pages/build-diff-viewer.mjs +97 -14
- package/dist/pages/build-diff-viewer.mjs.map +1 -1
- package/dist/pages/build-diff.mjs +37 -22
- package/dist/pages/build-diff.mjs.map +1 -1
- package/dist/pages/build-tests.mjs +73 -0
- package/dist/pages/build-tests.mjs.map +1 -0
- package/dist/pages/component-detail.mjs +248 -0
- package/dist/pages/component-detail.mjs.map +1 -0
- package/dist/pages/component-stories.mjs +167 -0
- package/dist/pages/component-stories.mjs.map +1 -0
- package/dist/pages/compute-jobs.mjs +14 -28
- package/dist/pages/compute-jobs.mjs.map +1 -1
- package/dist/pages/design-links.mjs +181 -0
- package/dist/pages/design-links.mjs.map +1 -0
- package/dist/pages/environment-change.mjs +52 -0
- package/dist/pages/environment-change.mjs.map +1 -0
- package/dist/pages/health-panel.mjs +125 -0
- package/dist/pages/health-panel.mjs.map +1 -0
- package/dist/pages/invite.mjs +1 -2
- package/dist/pages/label-detail.mjs +1 -2
- package/dist/pages/library-grid.mjs +104 -0
- package/dist/pages/library-grid.mjs.map +1 -0
- package/dist/pages/library-model.mjs +46 -0
- package/dist/pages/library-model.mjs.map +1 -0
- package/dist/pages/library-snapshots.mjs +129 -0
- package/dist/pages/library-snapshots.mjs.map +1 -0
- package/dist/pages/library.mjs +113 -220
- package/dist/pages/library.mjs.map +1 -1
- package/dist/pages/login.mjs +1 -2
- package/dist/pages/notifications-paused.mjs +21 -0
- package/dist/pages/notifications-paused.mjs.map +1 -0
- package/dist/pages/passkey-ceremony.mjs +1 -2
- package/dist/pages/profile.mjs +54 -50
- package/dist/pages/profile.mjs.map +1 -1
- package/dist/pages/project-builds-table.mjs +3 -4
- package/dist/pages/project-builds-table.mjs.map +1 -1
- package/dist/pages/project-builds.mjs +52 -23
- package/dist/pages/project-builds.mjs.map +1 -1
- package/dist/pages/project-create.mjs +1 -2
- package/dist/pages/project-settings.mjs +52 -14
- package/dist/pages/project-settings.mjs.map +1 -1
- package/dist/pages/projects-card.mjs +15 -3
- package/dist/pages/projects-card.mjs.map +1 -1
- package/dist/pages/projects.mjs +7 -4
- package/dist/pages/projects.mjs.map +1 -1
- package/dist/pages/review-changeset.mjs +115 -0
- package/dist/pages/review-changeset.mjs.map +1 -0
- package/dist/pages/review-comments.mjs +102 -0
- package/dist/pages/review-comments.mjs.map +1 -0
- package/dist/pages/review-verdict.mjs +101 -0
- package/dist/pages/review-verdict.mjs.map +1 -0
- package/dist/pages/root.mjs +1 -2
- package/dist/pages/settings-ai.mjs +68 -0
- package/dist/pages/settings-ai.mjs.map +1 -0
- package/dist/pages/settings-capture.mjs +123 -0
- package/dist/pages/settings-capture.mjs.map +1 -0
- package/dist/pages/settings-design-credential.mjs +98 -0
- package/dist/pages/settings-design-credential.mjs.map +1 -0
- package/dist/pages/settings-design-links.mjs +178 -0
- package/dist/pages/settings-design-links.mjs.map +1 -0
- package/dist/pages/settings-design.mjs +47 -0
- package/dist/pages/settings-design.mjs.map +1 -0
- package/dist/pages/settings-general.mjs +47 -23
- package/dist/pages/settings-general.mjs.map +1 -1
- package/dist/pages/settings-labels.mjs +44 -4
- package/dist/pages/settings-labels.mjs.map +1 -1
- package/dist/pages/settings-links.mjs +111 -0
- package/dist/pages/settings-links.mjs.map +1 -0
- package/dist/pages/settings-members.mjs +1 -2
- package/dist/pages/settings-notifications-git.mjs +127 -0
- package/dist/pages/settings-notifications-git.mjs.map +1 -0
- package/dist/pages/settings-notifications.mjs +80 -44
- package/dist/pages/settings-notifications.mjs.map +1 -1
- package/dist/pages/settings-tests.mjs +34 -4
- package/dist/pages/settings-tests.mjs.map +1 -1
- package/dist/pages/settings-tokens.mjs +1 -2
- package/dist/pages/settings-webhooks.mjs +1 -2
- package/dist/pages/storybook.mjs +1 -2
- package/dist/pages/target-choices.mjs +38 -0
- package/dist/pages/target-choices.mjs.map +1 -0
- package/dist/publish-setup.mjs +88 -0
- package/dist/publish-setup.mjs.map +1 -0
- package/dist/retention-timer.mjs +12 -12
- package/dist/retention-timer.mjs.map +1 -1
- package/dist/review-events.mjs +47 -0
- package/dist/review-events.mjs.map +1 -0
- package/dist/routers/admin-pages.mjs +46 -5
- package/dist/routers/admin-pages.mjs.map +1 -1
- package/dist/routers/admin-public-origin.mjs +92 -0
- package/dist/routers/admin-public-origin.mjs.map +1 -0
- package/dist/routers/admin.mjs +8 -4
- package/dist/routers/admin.mjs.map +1 -1
- package/dist/routers/assets.mjs +10 -4
- package/dist/routers/assets.mjs.map +1 -1
- package/dist/routers/attempts.mjs +1 -2
- package/dist/routers/auth-sync.mjs +1 -2
- package/dist/routers/auth.mjs +2 -3
- package/dist/routers/baseline-guard.mjs +4 -5
- package/dist/routers/baseline-guard.mjs.map +1 -1
- package/dist/routers/baseline-reference.mjs +72 -0
- package/dist/routers/baseline-reference.mjs.map +1 -0
- package/dist/routers/baselines.handlers.mjs +63 -0
- package/dist/routers/baselines.handlers.mjs.map +1 -0
- package/dist/routers/baselines.mjs +124 -0
- package/dist/routers/baselines.mjs.map +1 -0
- package/dist/routers/build-cancel.mjs +67 -0
- package/dist/routers/build-cancel.mjs.map +1 -0
- package/dist/routers/build-changeset.mjs +69 -0
- package/dist/routers/build-changeset.mjs.map +1 -0
- package/dist/routers/build-reviews.handlers.mjs +85 -0
- package/dist/routers/build-reviews.handlers.mjs.map +1 -0
- package/dist/routers/build-reviews.mjs +73 -0
- package/dist/routers/build-reviews.mjs.map +1 -0
- package/dist/routers/builds.handlers.mjs +65 -60
- package/dist/routers/builds.handlers.mjs.map +1 -1
- package/dist/routers/builds.mjs +77 -19
- package/dist/routers/builds.mjs.map +1 -1
- package/dist/routers/capture-cache.mjs +29 -0
- package/dist/routers/capture-cache.mjs.map +1 -0
- package/dist/routers/capture-config.mjs +34 -0
- package/dist/routers/capture-config.mjs.map +1 -0
- package/dist/routers/comments.mjs +42 -17
- package/dist/routers/comments.mjs.map +1 -1
- package/dist/routers/comparison-image.mjs +29 -0
- package/dist/routers/comparison-image.mjs.map +1 -0
- package/dist/routers/component-catalog.handlers.mjs +70 -0
- package/dist/routers/component-catalog.handlers.mjs.map +1 -0
- package/dist/routers/component.handlers.mjs +120 -0
- package/dist/routers/component.handlers.mjs.map +1 -0
- package/dist/routers/connections.mjs +117 -0
- package/dist/routers/connections.mjs.map +1 -0
- package/dist/routers/design-connect.handlers.mjs +43 -0
- package/dist/routers/design-connect.handlers.mjs.map +1 -0
- package/dist/routers/design.handlers.mjs +113 -0
- package/dist/routers/design.handlers.mjs.map +1 -0
- package/dist/routers/flash.mjs +1 -2
- package/dist/routers/health-report.mjs +3 -5
- package/dist/routers/health-report.mjs.map +1 -1
- package/dist/routers/health.mjs +5 -5
- package/dist/routers/health.mjs.map +1 -1
- package/dist/routers/helpers.mjs +20 -5
- package/dist/routers/helpers.mjs.map +1 -1
- package/dist/routers/htmx.mjs +1 -2
- package/dist/routers/insights-ui.mjs +63 -0
- package/dist/routers/insights-ui.mjs.map +1 -0
- package/dist/routers/insights.handlers.mjs +44 -0
- package/dist/routers/insights.handlers.mjs.map +1 -0
- package/dist/routers/insights.mjs +188 -0
- package/dist/routers/insights.mjs.map +1 -0
- package/dist/routers/labels.mjs +1 -2
- package/dist/routers/links.handlers.mjs +79 -0
- package/dist/routers/links.handlers.mjs.map +1 -0
- package/dist/routers/links.mjs +138 -0
- package/dist/routers/links.mjs.map +1 -0
- package/dist/routers/media.mjs +30 -7
- package/dist/routers/media.mjs.map +1 -1
- package/dist/routers/members.mjs +3 -3
- package/dist/routers/members.mjs.map +1 -1
- package/dist/routers/notification-channels.mjs +18 -25
- package/dist/routers/notification-channels.mjs.map +1 -1
- package/dist/routers/notification-subscriptions.mjs +1 -2
- package/dist/routers/oauth-state.mjs +44 -0
- package/dist/routers/oauth-state.mjs.map +1 -0
- package/dist/routers/preview-base.mjs +37 -0
- package/dist/routers/preview-base.mjs.map +1 -0
- package/dist/routers/preview-token.mjs +50 -0
- package/dist/routers/preview-token.mjs.map +1 -0
- package/dist/routers/profile.mjs +4 -3
- package/dist/routers/profile.mjs.map +1 -1
- package/dist/routers/projects.mjs +18 -3
- package/dist/routers/projects.mjs.map +1 -1
- package/dist/routers/publish-upload.mjs +85 -0
- package/dist/routers/publish-upload.mjs.map +1 -0
- package/dist/routers/published-access.mjs +51 -0
- package/dist/routers/published-access.mjs.map +1 -0
- package/dist/routers/published-compression.mjs +109 -0
- package/dist/routers/published-compression.mjs.map +1 -0
- package/dist/routers/published-files.mjs +69 -0
- package/dist/routers/published-files.mjs.map +1 -0
- package/dist/routers/review-forms.mjs +71 -0
- package/dist/routers/review-forms.mjs.map +1 -0
- package/dist/routers/review-next.mjs +1 -2
- package/dist/routers/review-section.handlers.mjs +84 -0
- package/dist/routers/review-section.handlers.mjs.map +1 -0
- package/dist/routers/review-switch-off.mjs +28 -0
- package/dist/routers/review-switch-off.mjs.map +1 -0
- package/dist/routers/schemas.mjs +141 -24
- package/dist/routers/schemas.mjs.map +1 -1
- package/dist/routers/scoped-models.mjs +21 -0
- package/dist/routers/scoped-models.mjs.map +1 -0
- package/dist/routers/settings-ai.mjs +34 -0
- package/dist/routers/settings-ai.mjs.map +1 -0
- package/dist/routers/settings-capture.mjs +76 -0
- package/dist/routers/settings-capture.mjs.map +1 -0
- package/dist/routers/settings-design.mjs +102 -0
- package/dist/routers/settings-design.mjs.map +1 -0
- package/dist/routers/settings-general.mjs +56 -12
- package/dist/routers/settings-general.mjs.map +1 -1
- package/dist/routers/settings-git-channel.mjs +76 -0
- package/dist/routers/settings-git-channel.mjs.map +1 -0
- package/dist/routers/settings-labels.mjs +28 -3
- package/dist/routers/settings-labels.mjs.map +1 -1
- package/dist/routers/settings-links.mjs +62 -0
- package/dist/routers/settings-links.mjs.map +1 -0
- package/dist/routers/settings-members.mjs +5 -9
- package/dist/routers/settings-members.mjs.map +1 -1
- package/dist/routers/settings-notifications.mjs +16 -12
- package/dist/routers/settings-notifications.mjs.map +1 -1
- package/dist/routers/settings-tokens.mjs +3 -7
- package/dist/routers/settings-tokens.mjs.map +1 -1
- package/dist/routers/settings-webhooks.mjs +8 -26
- package/dist/routers/settings-webhooks.mjs.map +1 -1
- package/dist/routers/settings.handlers.mjs +108 -28
- package/dist/routers/settings.handlers.mjs.map +1 -1
- package/dist/routers/settings.mjs +17 -6
- package/dist/routers/settings.mjs.map +1 -1
- package/dist/routers/signed-storybook.mjs +55 -0
- package/dist/routers/signed-storybook.mjs.map +1 -0
- package/dist/routers/snapshot-groups.mjs +62 -0
- package/dist/routers/snapshot-groups.mjs.map +1 -0
- package/dist/routers/snapshots.mjs +104 -7
- package/dist/routers/snapshots.mjs.map +1 -1
- package/dist/routers/storybook.mjs +57 -104
- package/dist/routers/storybook.mjs.map +1 -1
- package/dist/routers/tokens.mjs +3 -7
- package/dist/routers/tokens.mjs.map +1 -1
- package/dist/routers/ui.mjs +38 -7
- package/dist/routers/ui.mjs.map +1 -1
- package/dist/routers/webhooks.mjs +6 -13
- package/dist/routers/webhooks.mjs.map +1 -1
- package/dist/routers/well-known.mjs +12 -6
- package/dist/routers/well-known.mjs.map +1 -1
- package/dist/routes/api-routes.mjs +92 -0
- package/dist/routes/api-routes.mjs.map +1 -0
- package/dist/routes/classification.mjs +37 -0
- package/dist/routes/classification.mjs.map +1 -0
- package/dist/routes/openapi-classes.mjs +21 -0
- package/dist/routes/openapi-classes.mjs.map +1 -0
- package/dist/routes/page-routes.mjs +103 -0
- package/dist/routes/page-routes.mjs.map +1 -0
- package/dist/runtime.mjs +19 -15
- package/dist/runtime.mjs.map +1 -1
- package/dist/serverless.mjs +4 -0
- package/dist/store.mjs +20 -3
- package/dist/store.mjs.map +1 -1
- package/dist/targets.mjs +45 -0
- package/dist/targets.mjs.map +1 -0
- package/dist/ui/avatar.mjs +1 -2
- package/dist/ui/buttons.mjs +17 -5
- package/dist/ui/buttons.mjs.map +1 -1
- package/dist/ui/code-block.mjs +1 -2
- package/dist/ui/compare-stage.mjs +86 -54
- package/dist/ui/compare-stage.mjs.map +1 -1
- package/dist/ui/csrf-field.mjs +1 -2
- package/dist/ui/css.mjs +1 -2
- package/dist/ui/design-reference.mjs +96 -0
- package/dist/ui/design-reference.mjs.map +1 -0
- package/dist/ui/document.mjs +2 -4
- package/dist/ui/document.mjs.map +1 -1
- package/dist/ui/dropdown.mjs +1 -2
- package/dist/ui/feedback.mjs +1 -2
- package/dist/ui/filter-input.mjs +1 -2
- package/dist/ui/forms.mjs +7 -7
- package/dist/ui/forms.mjs.map +1 -1
- package/dist/ui/icons/icon.mjs +1 -2
- package/dist/ui/icons/paths.mjs +2 -2
- package/dist/ui/icons/paths.mjs.map +1 -1
- package/dist/ui/icons/sprite.mjs +1 -2
- package/dist/ui/kbd.mjs +1 -2
- package/dist/ui/layout.mjs +1 -2
- package/dist/ui/preview-embed.mjs +81 -0
- package/dist/ui/preview-embed.mjs.map +1 -0
- package/dist/ui/progress.mjs +1 -2
- package/dist/ui/segmented.mjs +1 -2
- package/dist/ui/shell/account.mjs +1 -2
- package/dist/ui/shell/bare.mjs +1 -2
- package/dist/ui/shell/boot-script.mjs +1 -2
- package/dist/ui/shell/client-script.mjs +86 -2
- package/dist/ui/shell/client-script.mjs.map +1 -1
- package/dist/ui/shell/nav.mjs +13 -10
- package/dist/ui/shell/nav.mjs.map +1 -1
- package/dist/ui/shell/review-script.mjs +25 -3
- package/dist/ui/shell/review-script.mjs.map +1 -1
- package/dist/ui/shell/sidebar.mjs +3 -4
- package/dist/ui/shell/sidebar.mjs.map +1 -1
- package/dist/ui/shell/topbar.mjs +1 -2
- package/dist/ui/stacks.mjs +1 -2
- package/dist/ui/styles/base.mjs +1 -2
- package/dist/ui/styles/behavior.mjs +1 -2
- package/dist/ui/styles/review-layout.mjs +1 -2
- package/dist/ui/styles/review.mjs +1 -2
- package/dist/ui/styles/tokens.mjs +1 -2
- package/dist/ui/styles.mjs +1 -2
- package/dist/ui/subnav.mjs +1 -2
- package/dist/ui/table.mjs +1 -2
- package/dist/ui/theme.mjs +1 -2
- package/dist/ui/thumbnail.mjs +1 -2
- package/dist/ui/time.mjs +1 -2
- package/package.json +14 -12
- 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
|
@@ -0,0 +1,73 @@
|
|
|
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
|
+
// 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
|
+
export { TestsCard, testsSummaryNote };
|
|
72
|
+
|
|
73
|
+
//# 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,248 @@
|
|
|
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
|
+
// 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
|
+
export { componentTabs, renderComponentPage };
|
|
247
|
+
|
|
248
|
+
//# 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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"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { Button } from "../ui/buttons.mjs";
|
|
5
|
+
import { Badge, EmptyState, Meta } from "../ui/feedback.mjs";
|
|
6
|
+
import { Card, SectionTitle } from "../ui/layout.mjs";
|
|
7
|
+
import { HStack, VStack } from "../ui/stacks.mjs";
|
|
8
|
+
import { Table } from "../ui/table.mjs";
|
|
9
|
+
import { PreviewEmbed } from "../ui/preview-embed.mjs";
|
|
10
|
+
import { PREVIEW_TOKEN_REFRESH, isSignedPreviewBase } from "../routers/preview-base.mjs";
|
|
11
|
+
import { StoryComments } from "./review-comments.mjs";
|
|
12
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
13
|
+
import { createUrlBuilder } from "@storyshelf/core/urls";
|
|
14
|
+
// src/pages/component-stories.tsx
|
|
15
|
+
/** Stories tab: the selected story in a sandboxed preview, and every story and docs entry. */
|
|
16
|
+
function StoriesTab(props) {
|
|
17
|
+
const { data } = props;
|
|
18
|
+
const { entries } = data.catalog;
|
|
19
|
+
const selected = selectedStory(data);
|
|
20
|
+
if (entries.length === 0) return /* @__PURE__ */ jsx(EmptyState, {
|
|
21
|
+
title: "No stories in this build",
|
|
22
|
+
description: "The build's Storybook index lists no stories for this file."
|
|
23
|
+
});
|
|
24
|
+
return /* @__PURE__ */ jsxs(VStack, { children: [
|
|
25
|
+
selected ? /* @__PURE__ */ jsx(PreviewCard, {
|
|
26
|
+
data,
|
|
27
|
+
story: selected
|
|
28
|
+
}) : null,
|
|
29
|
+
selected && data.review ? /* @__PURE__ */ jsx(StoryComments, {
|
|
30
|
+
project: data.project,
|
|
31
|
+
build: data.catalog.build,
|
|
32
|
+
storyId: selected.id,
|
|
33
|
+
comments: data.review.comments,
|
|
34
|
+
canComment: data.review.canComment
|
|
35
|
+
}) : null,
|
|
36
|
+
/* @__PURE__ */ jsx(Card, {
|
|
37
|
+
padded: false,
|
|
38
|
+
children: /* @__PURE__ */ jsx(Table, { children: /* @__PURE__ */ jsxs("table", { children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
39
|
+
/* @__PURE__ */ jsx("th", { children: "Name" }),
|
|
40
|
+
/* @__PURE__ */ jsx("th", { children: "Kind" }),
|
|
41
|
+
/* @__PURE__ */ jsx("th", { children: "Tags" }),
|
|
42
|
+
/* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsx("span", {
|
|
43
|
+
class: "visually-hidden",
|
|
44
|
+
children: "Actions"
|
|
45
|
+
}) })
|
|
46
|
+
] }) }), /* @__PURE__ */ jsx("tbody", { children: entries.map((entry) => /* @__PURE__ */ jsx(EntryRow, {
|
|
47
|
+
entry,
|
|
48
|
+
data,
|
|
49
|
+
selected: selected?.id
|
|
50
|
+
}, entry.id)) })] }) })
|
|
51
|
+
})
|
|
52
|
+
] });
|
|
53
|
+
}
|
|
54
|
+
/** The story the preview shows: `?story=` when it names one, else the first. */
|
|
55
|
+
function selectedStory(data) {
|
|
56
|
+
const stories = data.catalog.entries.filter((entry) => entry.type === "story");
|
|
57
|
+
return stories.find((entry) => entry.id === data.story) ?? stories[0];
|
|
58
|
+
}
|
|
59
|
+
const previewPath = (story) => `/iframe.html?id=${encodeURIComponent(story.id)}&viewMode=story`;
|
|
60
|
+
/** A frame under a signed URL asks for a new token before the old one expires. */
|
|
61
|
+
function refreshOf(data, story) {
|
|
62
|
+
if (data.previewBase === null || !isSignedPreviewBase(data.previewBase)) return {};
|
|
63
|
+
return { refresh: {
|
|
64
|
+
url: `/api/v1/projects/${data.project.slug}/builds/${data.catalog.build.id}/preview-token`,
|
|
65
|
+
path: previewPath(story),
|
|
66
|
+
seconds: PREVIEW_TOKEN_REFRESH
|
|
67
|
+
} };
|
|
68
|
+
}
|
|
69
|
+
function PreviewCard(props) {
|
|
70
|
+
const { data, story } = props;
|
|
71
|
+
return /* @__PURE__ */ jsxs(Card, {
|
|
72
|
+
id: "preview",
|
|
73
|
+
children: [/* @__PURE__ */ jsx(SectionTitle, { children: story.name }), data.previewBase === null ? /* @__PURE__ */ jsx(Meta, { children: "This build needs a sign-in to view, so it cannot run in a frame here. Open it in Storybook instead." }) : /* @__PURE__ */ jsx(PreviewEmbed, {
|
|
74
|
+
src: `${data.previewBase}${previewPath(story)}`,
|
|
75
|
+
title: `${story.title} / ${story.name}`,
|
|
76
|
+
...refreshOf(data, story)
|
|
77
|
+
})]
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function EntryRow(props) {
|
|
81
|
+
const { entry, data } = props;
|
|
82
|
+
return /* @__PURE__ */ jsxs("tr", {
|
|
83
|
+
id: entry.id,
|
|
84
|
+
children: [
|
|
85
|
+
/* @__PURE__ */ jsx("td", { children: entry.name }),
|
|
86
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(Badge, {
|
|
87
|
+
tone: entry.type === "docs" ? "info" : "neutral",
|
|
88
|
+
children: entry.type
|
|
89
|
+
}) }),
|
|
90
|
+
/* @__PURE__ */ jsx("td", { children: entry.tags.join(", ") }),
|
|
91
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs(HStack, { children: [entry.type === "story" && entry.id !== props.selected ? /* @__PURE__ */ jsx(Button, {
|
|
92
|
+
variant: "ghost",
|
|
93
|
+
size: "sm",
|
|
94
|
+
icon: "image",
|
|
95
|
+
href: `?tab=stories&story=${encodeURIComponent(entry.id)}#preview`,
|
|
96
|
+
children: "Preview"
|
|
97
|
+
}) : null, /* @__PURE__ */ jsx(Button, {
|
|
98
|
+
variant: "ghost",
|
|
99
|
+
size: "sm",
|
|
100
|
+
icon: "external-link",
|
|
101
|
+
href: `${storybookBase(data)}/?storyId=${encodeURIComponent(entry.id)}${entry.type === "docs" ? "&viewMode=docs" : ""}`,
|
|
102
|
+
target: "_blank",
|
|
103
|
+
rel: "noopener",
|
|
104
|
+
children: "Open"
|
|
105
|
+
})] }) })
|
|
106
|
+
]
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/** Published builds that hold the component, and whether its stories changed since the build before. */
|
|
110
|
+
function ComponentBuilds(props) {
|
|
111
|
+
const { data } = props;
|
|
112
|
+
const urls = createUrlBuilder("/", data.publishedBaseDomain);
|
|
113
|
+
const { history } = data.catalog;
|
|
114
|
+
if (history.length === 0) return /* @__PURE__ */ jsx(EmptyState, {
|
|
115
|
+
title: "No published builds",
|
|
116
|
+
description: "No catalogued build holds this file yet."
|
|
117
|
+
});
|
|
118
|
+
return /* @__PURE__ */ jsxs(Card, { children: [/* @__PURE__ */ jsx(SectionTitle, { children: "Published builds" }), /* @__PURE__ */ jsx(Table, { children: /* @__PURE__ */ jsxs("table", { children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
119
|
+
/* @__PURE__ */ jsx("th", { children: "Build" }),
|
|
120
|
+
/* @__PURE__ */ jsx("th", { children: "Branch" }),
|
|
121
|
+
/* @__PURE__ */ jsx("th", { children: "Stories" }),
|
|
122
|
+
/* @__PURE__ */ jsx("th", { children: "Changes" })
|
|
123
|
+
] }) }), /* @__PURE__ */ jsx("tbody", { children: history.map((row) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
124
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("a", {
|
|
125
|
+
href: urls.build(data.project.slug, row.build.id),
|
|
126
|
+
children: row.build.gitSha.slice(0, 7)
|
|
127
|
+
}) }),
|
|
128
|
+
/* @__PURE__ */ jsx("td", { children: row.build.gitBranch }),
|
|
129
|
+
/* @__PURE__ */ jsx("td", { children: row.component.storyCount }),
|
|
130
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(ChangeCell, { row }) })
|
|
131
|
+
] }, row.build.id)) })] }) })] });
|
|
132
|
+
}
|
|
133
|
+
function ChangeCell(props) {
|
|
134
|
+
const { row } = props;
|
|
135
|
+
if (row.change === "new") return /* @__PURE__ */ jsx(Badge, {
|
|
136
|
+
tone: "info",
|
|
137
|
+
children: "new here"
|
|
138
|
+
});
|
|
139
|
+
if (row.change === "unchanged") return /* @__PURE__ */ jsx(Meta, {
|
|
140
|
+
as: "span",
|
|
141
|
+
children: "unchanged"
|
|
142
|
+
});
|
|
143
|
+
const parts = changeParts(row);
|
|
144
|
+
return /* @__PURE__ */ jsxs(HStack, { children: [/* @__PURE__ */ jsx(Badge, {
|
|
145
|
+
tone: "warning",
|
|
146
|
+
children: "changed"
|
|
147
|
+
}), parts ? /* @__PURE__ */ jsx(Meta, {
|
|
148
|
+
as: "span",
|
|
149
|
+
children: parts
|
|
150
|
+
}) : null] });
|
|
151
|
+
}
|
|
152
|
+
/** "2 added · 1 removed · 1 renamed", or empty when the counts are not known. */
|
|
153
|
+
function changeParts(row) {
|
|
154
|
+
const { changes } = row;
|
|
155
|
+
if (!changes) return "";
|
|
156
|
+
return [
|
|
157
|
+
changes.added > 0 ? `${changes.added} added` : "",
|
|
158
|
+
changes.removed > 0 ? `${changes.removed} removed` : "",
|
|
159
|
+
changes.renamed > 0 ? `${changes.renamed} renamed` : ""
|
|
160
|
+
].filter((part) => part !== "").join(" · ");
|
|
161
|
+
}
|
|
162
|
+
function storybookBase(data) {
|
|
163
|
+
return createUrlBuilder("/", data.publishedBaseDomain).storybookBuild(data.project.slug, data.catalog.build.id);
|
|
164
|
+
}
|
|
165
|
+
export { ComponentBuilds, StoriesTab };
|
|
166
|
+
|
|
167
|
+
//# sourceMappingURL=component-stories.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-stories.mjs","names":[],"sources":["../../src/pages/component-stories.tsx"],"sourcesContent":["/** The catalog's tabs of the component page: its stories with a click-to-load preview, and its builds. */\nimport type { CatalogEntry } from \"@storyshelf/core/capture\";\nimport { createUrlBuilder } from \"@storyshelf/core/urls\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport type { CatalogHistoryRow } from \"../routers/component-catalog.handlers.ts\";\nimport { isSignedPreviewBase, PREVIEW_TOKEN_REFRESH } from \"../routers/preview-base.ts\";\nimport {\n Badge,\n Button,\n Card,\n EmptyState,\n HStack,\n Meta,\n PreviewEmbed,\n SectionTitle,\n Table,\n VStack,\n} from \"../ui/components.tsx\";\nimport type { ComponentPageData } from \"./component-detail.tsx\";\nimport { StoryComments } from \"./review-comments.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\ntype Rendered = HtmlEscapedString | Promise<HtmlEscapedString>;\n\n/** Stories tab: the selected story in a sandboxed preview, and every story and docs entry. */\nexport function StoriesTab(props: { data: ComponentPageData }): Rendered {\n const { data } = props;\n const { entries } = data.catalog;\n const selected = selectedStory(data);\n if (entries.length === 0) {\n return (\n <EmptyState\n title=\"No stories in this build\"\n description=\"The build's Storybook index lists no stories for this file.\"\n />\n );\n }\n return (\n <VStack>\n {selected ? <PreviewCard data={data} story={selected} /> : null}\n {selected && data.review ? (\n <StoryComments\n project={data.project}\n build={data.catalog.build}\n storyId={selected.id}\n comments={data.review.comments}\n canComment={data.review.canComment}\n />\n ) : null}\n <Card padded={false}>\n <Table>\n <table>\n <thead>\n <tr>\n <th>Name</th>\n <th>Kind</th>\n <th>Tags</th>\n <th>\n <span class=\"visually-hidden\">Actions</span>\n </th>\n </tr>\n </thead>\n <tbody>\n {entries.map((entry): Rendered => (\n <EntryRow key={entry.id} entry={entry} data={data} selected={selected?.id} />\n ))}\n </tbody>\n </table>\n </Table>\n </Card>\n </VStack>\n );\n}\n\n/** The story the preview shows: `?story=` when it names one, else the first. */\nfunction selectedStory(data: ComponentPageData): CatalogEntry | undefined {\n const stories = data.catalog.entries.filter((entry) => entry.type === \"story\");\n return stories.find((entry) => entry.id === data.story) ?? stories[0];\n}\n\nconst previewPath = (story: CatalogEntry): string =>\n `/iframe.html?id=${encodeURIComponent(story.id)}&viewMode=story`;\n\n/** A frame under a signed URL asks for a new token before the old one expires. */\nfunction refreshOf(\n data: ComponentPageData,\n story: CatalogEntry,\n): { refresh?: { url: string; path: string; seconds: number } } {\n if (data.previewBase === null || !isSignedPreviewBase(data.previewBase)) {\n return {};\n }\n return {\n refresh: {\n url: `/api/v1/projects/${data.project.slug}/builds/${data.catalog.build.id}/preview-token`,\n path: previewPath(story),\n seconds: PREVIEW_TOKEN_REFRESH,\n },\n };\n}\n\nfunction PreviewCard(props: { data: ComponentPageData; story: CatalogEntry }): Rendered {\n const { data, story } = props;\n return (\n <Card id=\"preview\">\n <SectionTitle>{story.name}</SectionTitle>\n {data.previewBase === null ? (\n <Meta>\n This build needs a sign-in to view, so it cannot run in a frame here. Open it in Storybook\n instead.\n </Meta>\n ) : (\n <PreviewEmbed\n src={`${data.previewBase}${previewPath(story)}`}\n title={`${story.title} / ${story.name}`}\n {...refreshOf(data, story)}\n />\n )}\n </Card>\n );\n}\n\nfunction EntryRow(props: {\n entry: CatalogEntry;\n data: ComponentPageData;\n selected: string | undefined;\n}): Rendered {\n const { entry, data } = props;\n return (\n <tr id={entry.id}>\n <td>{entry.name}</td>\n <td>\n <Badge tone={entry.type === \"docs\" ? \"info\" : \"neutral\"}>{entry.type}</Badge>\n </td>\n <td>{entry.tags.join(\", \")}</td>\n <td>\n <HStack>\n {entry.type === \"story\" && entry.id !== props.selected ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n icon=\"image\"\n href={`?tab=stories&story=${encodeURIComponent(entry.id)}#preview`}\n >\n Preview\n </Button>\n ) : null}\n <Button\n variant=\"ghost\"\n size=\"sm\"\n icon=\"external-link\"\n href={`${storybookBase(data)}/?storyId=${encodeURIComponent(entry.id)}${entry.type === \"docs\" ? \"&viewMode=docs\" : \"\"}`}\n target=\"_blank\"\n rel=\"noopener\"\n >\n Open\n </Button>\n </HStack>\n </td>\n </tr>\n );\n}\n\n/** Published builds that hold the component, and whether its stories changed since the build before. */\nexport function ComponentBuilds(props: { data: ComponentPageData }): Rendered {\n const { data } = props;\n const urls = createUrlBuilder(\"/\", data.publishedBaseDomain);\n const { history } = data.catalog;\n if (history.length === 0) {\n return (\n <EmptyState\n title=\"No published builds\"\n description=\"No catalogued build holds this file yet.\"\n />\n );\n }\n return (\n <Card>\n <SectionTitle>Published builds</SectionTitle>\n <Table>\n <table>\n <thead>\n <tr>\n <th>Build</th>\n <th>Branch</th>\n <th>Stories</th>\n <th>Changes</th>\n </tr>\n </thead>\n <tbody>\n {history.map((row): Rendered => (\n <tr key={row.build.id}>\n <td>\n <a href={urls.build(data.project.slug, row.build.id)}>\n {row.build.gitSha.slice(0, 7)}\n </a>\n </td>\n <td>{row.build.gitBranch}</td>\n <td>{row.component.storyCount}</td>\n <td>\n <ChangeCell row={row} />\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </Table>\n </Card>\n );\n}\n\nfunction ChangeCell(props: { row: CatalogHistoryRow }): Rendered {\n const { row } = props;\n if (row.change === \"new\") {\n return <Badge tone=\"info\">new here</Badge>;\n }\n if (row.change === \"unchanged\") {\n return <Meta as=\"span\">unchanged</Meta>;\n }\n const parts = changeParts(row);\n return (\n <HStack>\n <Badge tone=\"warning\">changed</Badge>\n {parts ? <Meta as=\"span\">{parts}</Meta> : null}\n </HStack>\n );\n}\n\n/** \"2 added · 1 removed · 1 renamed\", or empty when the counts are not known. */\nfunction changeParts(row: CatalogHistoryRow): string {\n const { changes } = row;\n if (!changes) {\n return \"\";\n }\n return [\n changes.added > 0 ? `${changes.added} added` : \"\",\n changes.removed > 0 ? `${changes.removed} removed` : \"\",\n changes.renamed > 0 ? `${changes.renamed} renamed` : \"\",\n ]\n .filter((part) => part !== \"\")\n .join(\" · \");\n}\n\nfunction storybookBase(data: ComponentPageData): string {\n return createUrlBuilder(\"/\", data.publishedBaseDomain).storybookBuild(\n data.project.slug,\n data.catalog.build.id,\n );\n}\n"],"mappings":"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"}
|