@shipfox/client-workflows 12.0.2 → 14.0.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/.storybook/main.ts +16 -1
- package/.storybook/preview.tsx +3 -3
- package/.storybook/workspace-source.ts +31 -0
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +79 -0
- package/dist/components/job-graph/job-duration-format.d.ts +2 -2
- package/dist/components/job-graph/job-duration-format.d.ts.map +1 -1
- package/dist/components/job-graph/job-duration-format.js +4 -2
- package/dist/components/job-graph/job-duration-format.js.map +1 -1
- package/dist/components/job-graph/job-duration-label.js +1 -1
- package/dist/components/job-graph/job-duration-label.js.map +1 -1
- package/dist/components/job-graph/job-graph-content.d.ts +2 -1
- package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +3 -3
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/job-graph/job-graph-view.d.ts +2 -1
- package/dist/components/job-graph/job-graph-view.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-view.js +2 -2
- package/dist/components/job-graph/job-graph-view.js.map +1 -1
- package/dist/components/job-graph/job-node.d.ts +1 -1
- package/dist/components/job-graph/job-node.d.ts.map +1 -1
- package/dist/components/job-graph/job-node.js +10 -9
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/job-graph/types.d.ts +2 -1
- package/dist/components/job-graph/types.d.ts.map +1 -1
- package/dist/components/job-graph/types.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +2 -2
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-duration-label.js +1 -1
- package/dist/components/workflow-run-duration-label.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts +21 -0
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -0
- package/dist/components/workflow-run-list/job-status-strip.js +185 -0
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -0
- package/dist/components/workflow-run-list/run-display.d.ts +27 -3
- package/dist/components/workflow-run-list/run-display.d.ts.map +1 -1
- package/dist/components/workflow-run-list/run-display.js +78 -7
- package/dist/components/workflow-run-list/run-display.js.map +1 -1
- package/dist/components/workflow-run-list/types.d.ts +23 -24
- package/dist/components/workflow-run-list/types.d.ts.map +1 -1
- package/dist/components/workflow-run-list/types.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts +22 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js +68 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts +18 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js +236 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts +8 -4
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-content.js +40 -12
- package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts +29 -2
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +103 -44
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +60 -43
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.js +20 -8
- package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts +19 -9
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +179 -112
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-number-label.d.ts +15 -0
- package/dist/components/workflow-run-number-label.d.ts.map +1 -0
- package/dist/components/workflow-run-number-label.js +41 -0
- package/dist/components/workflow-run-number-label.js.map +1 -0
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts +3 -3
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +10 -9
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +4 -9
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +107 -74
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts +6 -0
- package/dist/components/workflow-run-tabs/index.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/index.js +7 -0
- package/dist/components/workflow-run-tabs/index.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +11 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +71 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +2 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.js +11 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +18 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.js +107 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-count.d.ts +6 -0
- package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-count.js +27 -0
- package/dist/components/workflow-run-tabs/run-tab-count.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +8 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.js +82 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.js.map +1 -0
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts +2 -2
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +1 -1
- package/dist/components/workflow-run-view/agent-config-failure-callout.js +4 -4
- package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +1 -1
- package/dist/components/workflow-run-view/job-card.d.ts +2 -2
- package/dist/components/workflow-run-view/job-card.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-card.js +51 -32
- package/dist/components/workflow-run-view/job-card.js.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.js +20 -8
- package/dist/components/workflow-run-view/job-execution-switcher.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-selection.js +40 -1
- package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.d.ts +1 -0
- package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +10 -1
- package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts +10 -5
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +136 -85
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/components/workflow-source-panel/index.d.ts +1 -0
- package/dist/components/workflow-source-panel/index.d.ts.map +1 -1
- package/dist/components/workflow-source-panel/index.js +1 -0
- package/dist/components/workflow-source-panel/index.js.map +1 -1
- package/dist/components/workflow-source-panel/workflow-source-content.d.ts +12 -0
- package/dist/components/workflow-source-panel/workflow-source-content.d.ts.map +1 -0
- package/dist/components/workflow-source-panel/workflow-source-content.js +53 -0
- package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -0
- package/dist/components/workflow-source-panel/workflow-source-panel.d.ts +1 -1
- package/dist/components/workflow-source-panel/workflow-source-panel.d.ts.map +1 -1
- package/dist/components/workflow-source-panel/workflow-source-panel.js +7 -54
- package/dist/components/workflow-source-panel/workflow-source-panel.js.map +1 -1
- package/dist/components/workflow-status/status-visuals.d.ts +4 -4
- package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
- package/dist/components/workflow-status/status-visuals.js +7 -0
- package/dist/components/workflow-status/status-visuals.js.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -4
- package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.js +6 -4
- package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +10 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js +11 -0
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +4 -2
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js +8 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/step.d.ts +1 -1
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js +2 -0
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +52 -4
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js +22 -3
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/workflow-run-tabs.d.ts +9 -0
- package/dist/core/workflow-run-tabs.d.ts.map +1 -0
- package/dist/core/workflow-run-tabs.js +3 -0
- package/dist/core/workflow-run-tabs.js.map +1 -0
- package/dist/core/workflow-run.d.ts +6 -6
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js +3 -3
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/feature.d.ts +7 -7
- package/dist/feature.js +5 -5
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.d.ts +4 -3
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +31 -3
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/hooks/api/workflow-runs.d.ts +4 -4
- package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
- package/dist/hooks/api/workflow-runs.js +12 -4
- package/dist/hooks/api/workflow-runs.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/pages/project-workflows-page.js +46 -1
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-detail-page.d.ts +11 -0
- package/dist/pages/workflow-run-detail-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-detail-page.js +52 -0
- package/dist/pages/workflow-run-detail-page.js.map +1 -0
- package/dist/pages/workflow-run-list-page.d.ts +10 -0
- package/dist/pages/workflow-run-list-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-list-page.js +43 -0
- package/dist/pages/workflow-run-list-page.js.map +1 -0
- package/dist/routes/inputs.d.ts +94 -5
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +199 -13
- package/dist/routes/inputs.js.map +1 -1
- package/dist/routes/project-route.d.ts +6 -0
- package/dist/routes/project-route.d.ts.map +1 -0
- package/dist/routes/project-route.js +25 -0
- package/dist/routes/project-route.js.map +1 -0
- package/dist/routes/run-detail.d.ts.map +1 -1
- package/dist/routes/run-detail.js +12 -7
- package/dist/routes/run-detail.js.map +1 -1
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/runs.js +11 -6
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +6 -5
- package/dist/routes/workflows.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/components/job-graph/job-duration-format.ts +11 -2
- package/src/components/job-graph/job-duration-label.tsx +5 -1
- package/src/components/job-graph/job-graph-content.tsx +7 -4
- package/src/components/job-graph/job-graph-view.test.tsx +19 -8
- package/src/components/job-graph/job-graph-view.tsx +6 -3
- package/src/components/job-graph/job-graph.stories.tsx +24 -20
- package/src/components/job-graph/job-node.stories.tsx +11 -6
- package/src/components/job-graph/job-node.test.tsx +82 -12
- package/src/components/job-graph/job-node.tsx +22 -15
- package/src/components/job-graph/types.ts +5 -1
- package/src/components/step-list/step-list.stories.tsx +2 -3
- package/src/components/step-list/step-list.tsx +3 -2
- package/src/components/workflow-run-duration-label.tsx +1 -1
- package/src/components/workflow-run-list/job-status-strip.tsx +189 -0
- package/src/components/workflow-run-list/run-display.test.ts +169 -30
- package/src/components/workflow-run-list/run-display.ts +116 -9
- package/src/components/workflow-run-list/types.ts +21 -17
- package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +100 -0
- package/src/components/workflow-run-list/workflow-run-filters.tsx +243 -0
- package/src/components/workflow-run-list/workflow-run-list-content.tsx +44 -16
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +128 -27
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +441 -104
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +73 -43
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +140 -20
- package/src/components/workflow-run-list/workflow-run-list.tsx +20 -10
- package/src/components/workflow-run-list/workflow-run-row.tsx +173 -106
- package/src/components/workflow-run-number-label.test.tsx +45 -0
- package/src/components/workflow-run-number-label.tsx +47 -0
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +7 -8
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +15 -14
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +29 -38
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +30 -24
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +102 -77
- package/src/components/workflow-run-tabs/index.ts +5 -0
- package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +69 -0
- package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +97 -0
- package/src/components/workflow-run-tabs/run-annotations-empty.tsx +11 -0
- package/src/components/workflow-run-tabs/run-jobs-list.tsx +151 -0
- package/src/components/workflow-run-tabs/run-tab-count.tsx +28 -0
- package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +52 -0
- package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +117 -0
- package/src/components/workflow-run-tabs/run-tab-strip.tsx +97 -0
- package/src/components/workflow-run-view/agent-config-failure-callout.stories.tsx +2 -3
- package/src/components/workflow-run-view/agent-config-failure-callout.tsx +4 -4
- package/src/components/workflow-run-view/job-card.stories.tsx +11 -10
- package/src/components/workflow-run-view/job-card.test.tsx +115 -0
- package/src/components/workflow-run-view/job-card.tsx +74 -35
- package/src/components/workflow-run-view/job-execution-switcher.tsx +31 -9
- package/src/components/workflow-run-view/workflow-run-selection.test.ts +48 -0
- package/src/components/workflow-run-view/workflow-run-selection.ts +48 -1
- package/src/components/workflow-run-view/workflow-run-states.tsx +12 -1
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +152 -94
- package/src/components/workflow-run-view/workflow-run-view.tsx +219 -95
- package/src/components/workflow-source-panel/index.ts +1 -0
- package/src/components/workflow-source-panel/workflow-source-content.tsx +84 -0
- package/src/components/workflow-source-panel/workflow-source-panel.tsx +14 -81
- package/src/components/workflow-status/status-visuals.test.ts +9 -0
- package/src/components/workflow-status/status-visuals.ts +6 -4
- package/src/components/workflow-status/workflow-status-icon.stories.tsx +10 -2
- package/src/components/workflow-status/workflow-status-icon.test.tsx +18 -0
- package/src/components/workflow-status/workflow-status-icon.tsx +17 -24
- package/src/core/entities/job-execution.ts +22 -0
- package/src/core/entities/job-status.test.ts +104 -0
- package/src/core/entities/job.ts +18 -3
- package/src/core/entities/step.ts +4 -0
- package/src/core/entities/workflow-run.ts +76 -7
- package/src/core/workflow-run-tabs.ts +8 -0
- package/src/core/workflow-run.test.ts +24 -9
- package/src/core/workflow-run.ts +17 -2
- package/src/feature.ts +5 -5
- package/src/hooks/api/workflow-run-mapper.ts +30 -3
- package/src/hooks/api/workflow-runs.test.tsx +2 -0
- package/src/hooks/api/workflow-runs.ts +12 -7
- package/src/index.ts +7 -2
- package/src/page-harness-budget.test.ts +1 -1
- package/src/pages/project-workflows-page.test.tsx +59 -5
- package/src/pages/project-workflows-page.tsx +34 -1
- package/src/pages/workflow-run-detail-page.tsx +72 -0
- package/src/pages/workflow-run-list-page.tsx +65 -0
- package/src/pages/workflow-run-pages.test.tsx +823 -0
- package/src/routes/inputs.test.ts +240 -12
- package/src/routes/inputs.ts +215 -11
- package/src/routes/project-route.tsx +25 -0
- package/src/routes/run-detail.tsx +15 -8
- package/src/routes/runs.tsx +14 -7
- package/src/routes/workflows.tsx +5 -4
- package/src/storybook-config.test.ts +27 -0
- package/test/fixtures/workflow-run.ts +76 -7
- package/test/pages.tsx +18 -8
- package/test/render.tsx +8 -2
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.test.json +7 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts +0 -10
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts.map +0 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.js +0 -55
- package/dist/components/workflow-run-list/workflow-run-list-header.js.map +0 -1
- package/dist/pages/workflow-run-first-time-use.d.ts +0 -6
- package/dist/pages/workflow-run-first-time-use.d.ts.map +0 -1
- package/dist/pages/workflow-run-first-time-use.js +0 -17
- package/dist/pages/workflow-run-first-time-use.js.map +0 -1
- package/dist/pages/workflow-run-page.d.ts +0 -10
- package/dist/pages/workflow-run-page.d.ts.map +0 -1
- package/dist/pages/workflow-run-page.js +0 -90
- package/dist/pages/workflow-run-page.js.map +0 -1
- package/src/components/workflow-run-list/workflow-run-list-header.tsx +0 -53
- package/src/pages/workflow-run-first-time-use.tsx +0 -17
- package/src/pages/workflow-run-page.test.tsx +0 -519
- package/src/pages/workflow-run-page.tsx +0 -94
|
@@ -26,8 +26,6 @@ import {WorkflowRunSummary} from './workflow-run-summary.js';
|
|
|
26
26
|
const ROOT_RUN_ID = '11111111-1111-4111-8111-111111111111';
|
|
27
27
|
const CURRENT_RUN_ID = '22222222-2222-4222-8222-222222222222';
|
|
28
28
|
const NEXT_RUN_ID = '33333333-3333-4333-8333-333333333333';
|
|
29
|
-
const WORKSPACE_ID = '44444444-4444-4444-8444-444444444444';
|
|
30
|
-
const PROJECT_ID = '55555555-5555-4555-8555-555555555555';
|
|
31
29
|
const SWITCH_ATTEMPT_PATTERN = /Switch attempt/;
|
|
32
30
|
const ATTEMPT_3_PATTERN = /Attempt 3/;
|
|
33
31
|
const STORYBOOK_NOW = '2026-06-26T12:00:00.000Z';
|
|
@@ -58,10 +56,10 @@ const RUN_ATTEMPTS_RESPONSE = runAttemptsResponseDto({
|
|
|
58
56
|
});
|
|
59
57
|
|
|
60
58
|
const withFrame: Decorator = (Story) => (
|
|
61
|
-
<div className="min-h-screen bg-background-neutral-
|
|
59
|
+
<div className="min-h-screen bg-background-neutral-background">
|
|
62
60
|
<div className="mx-auto flex min-h-screen w-full max-w-[1120px] flex-col overflow-hidden border-x border-border-neutral-base bg-background-subtle-base">
|
|
63
61
|
<Story />
|
|
64
|
-
<div className="min-h-0 flex-1 bg-background-neutral-
|
|
62
|
+
<div className="min-h-0 flex-1 bg-background-neutral-background p-16" />
|
|
65
63
|
</div>
|
|
66
64
|
</div>
|
|
67
65
|
);
|
|
@@ -71,12 +69,12 @@ const withAttemptApi: Decorator = (Story) => {
|
|
|
71
69
|
const rootRoute = createRootRoute({component: Outlet});
|
|
72
70
|
const runRoute = createRoute({
|
|
73
71
|
getParentRoute: () => rootRoute,
|
|
74
|
-
path: '/
|
|
72
|
+
path: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
|
|
75
73
|
component: () => <Story />,
|
|
76
74
|
});
|
|
77
75
|
const router = createRouter({
|
|
78
76
|
history: createMemoryHistory({
|
|
79
|
-
initialEntries: [`/
|
|
77
|
+
initialEntries: [`/w/acme/p/project/runs/${CURRENT_RUN_ID}`],
|
|
80
78
|
}),
|
|
81
79
|
routeTree: rootRoute.addChildren([runRoute]),
|
|
82
80
|
});
|
|
@@ -155,37 +153,13 @@ const ATTEMPT_SUMMARY_ARGS = {
|
|
|
155
153
|
}),
|
|
156
154
|
status: 'failed',
|
|
157
155
|
}),
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
workspaceSlug: 'acme',
|
|
157
|
+
projectSlug: 'project',
|
|
160
158
|
latestAttempt: 3,
|
|
161
159
|
};
|
|
162
160
|
|
|
163
161
|
export const Playground: Story = {};
|
|
164
162
|
|
|
165
|
-
export const WithSourceButton: Story = {
|
|
166
|
-
args: {
|
|
167
|
-
run: workflowRunDetail({
|
|
168
|
-
status: 'succeeded',
|
|
169
|
-
source_snapshot: {format: 'yaml', content: 'jobs:\n build:\n steps: []'},
|
|
170
|
-
}),
|
|
171
|
-
sourceAvailable: true,
|
|
172
|
-
sourceOpen: false,
|
|
173
|
-
sourcePanelId: 'workflow-source-panel',
|
|
174
|
-
},
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
export const SourceOpen: Story = {
|
|
178
|
-
args: {
|
|
179
|
-
run: workflowRunDetail({
|
|
180
|
-
status: 'succeeded',
|
|
181
|
-
source_snapshot: {format: 'yaml', content: 'jobs:\n build:\n steps: []'},
|
|
182
|
-
}),
|
|
183
|
-
sourceAvailable: true,
|
|
184
|
-
sourceOpen: true,
|
|
185
|
-
sourcePanelId: 'workflow-source-panel',
|
|
186
|
-
},
|
|
187
|
-
};
|
|
188
|
-
|
|
189
163
|
export const WithAttempts: Story = {
|
|
190
164
|
decorators: [withAttemptApi],
|
|
191
165
|
args: ATTEMPT_SUMMARY_ARGS,
|
|
@@ -325,7 +299,7 @@ const ACTION_VARIANTS = [
|
|
|
325
299
|
>;
|
|
326
300
|
}>;
|
|
327
301
|
|
|
328
|
-
export const
|
|
302
|
+
export const ActionVariants: Story = {
|
|
329
303
|
render: () => (
|
|
330
304
|
<div className="flex flex-col">
|
|
331
305
|
{ACTION_VARIANTS.map(({label, run, props}, index) => (
|
|
@@ -335,9 +309,6 @@ export const ActionVariantsWithSource: Story = {
|
|
|
335
309
|
...run,
|
|
336
310
|
id: `22222222-2222-4222-8222-${String(index + 2).padStart(12, '0')}`,
|
|
337
311
|
}}
|
|
338
|
-
sourceAvailable
|
|
339
|
-
sourceOpen={label === 'Running'}
|
|
340
|
-
sourcePanelId={`workflow-source-panel-${index}`}
|
|
341
312
|
{...props}
|
|
342
313
|
/>
|
|
343
314
|
))}
|
|
@@ -352,8 +323,6 @@ export const ActionVariantsWithAttempts: Story = {
|
|
|
352
323
|
{ACTION_VARIANTS.map(({label, run, props}, index) => (
|
|
353
324
|
<WorkflowRunSummary
|
|
354
325
|
key={label}
|
|
355
|
-
workspaceId={WORKSPACE_ID}
|
|
356
|
-
projectId={PROJECT_ID}
|
|
357
326
|
run={{
|
|
358
327
|
...run,
|
|
359
328
|
id: `22222222-2222-4222-8222-${String(index + 2).padStart(12, '0')}`,
|
|
@@ -369,6 +338,8 @@ export const ActionVariantsWithAttempts: Story = {
|
|
|
369
338
|
rerunMode: null,
|
|
370
339
|
}),
|
|
371
340
|
}}
|
|
341
|
+
workspaceSlug="acme"
|
|
342
|
+
projectSlug="project"
|
|
372
343
|
latestAttempt={3}
|
|
373
344
|
{...props}
|
|
374
345
|
/>
|
|
@@ -412,3 +383,23 @@ export const LongTriggerMetadata: Story = {
|
|
|
412
383
|
}),
|
|
413
384
|
},
|
|
414
385
|
};
|
|
386
|
+
|
|
387
|
+
export const NarrowLongContent: Story = {
|
|
388
|
+
decorators: [
|
|
389
|
+
(Story) => (
|
|
390
|
+
<div className="w-[360px] max-w-full overflow-hidden border-x border-border-neutral-base">
|
|
391
|
+
<Story />
|
|
392
|
+
</div>
|
|
393
|
+
),
|
|
394
|
+
],
|
|
395
|
+
args: {
|
|
396
|
+
run: workflowRunDetail({
|
|
397
|
+
status: 'running',
|
|
398
|
+
name: 'release-production-multi-region-with-canary-and-post-deploy-validation',
|
|
399
|
+
trigger_provider: 'github',
|
|
400
|
+
trigger_source: 'github-enterprise-cloud-production-organization',
|
|
401
|
+
trigger_event: 'workflow_dispatch_with_release_candidate_payload',
|
|
402
|
+
}),
|
|
403
|
+
onCancel: noop,
|
|
404
|
+
},
|
|
405
|
+
};
|
|
@@ -29,13 +29,16 @@ describe('WorkflowRunSummary', () => {
|
|
|
29
29
|
restoreElementWidthDescriptors();
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
test('renders status, trigger metadata, and trigger time', async () => {
|
|
33
|
-
renderSummary();
|
|
32
|
+
test('renders status, run number, trigger metadata, and trigger time', async () => {
|
|
33
|
+
renderSummary({workflow_name: 'CI', number: 5184});
|
|
34
34
|
|
|
35
35
|
const summary = await screen.findByRole('region', {name: 'deploy-web'});
|
|
36
36
|
|
|
37
|
+
expect(summary).toHaveClass('bg-background-neutral-background', 'px-16');
|
|
38
|
+
expect(summary.firstElementChild).not.toHaveClass('max-w-[1120px]');
|
|
37
39
|
expect(within(summary).getByRole('heading', {name: 'deploy-web'})).toBeInTheDocument();
|
|
38
40
|
expect(within(summary).getAllByText('Running')).not.toHaveLength(0);
|
|
41
|
+
expect(within(summary).getByText('CI #5184')).toBeInTheDocument();
|
|
39
42
|
expect(within(summary).getByText('fire')).toBeInTheDocument();
|
|
40
43
|
expect(within(summary).queryByText('manual')).not.toBeInTheDocument();
|
|
41
44
|
expect(within(summary).getByText(RELATIVE_TIME_TEXT_PATTERN)).toBeInTheDocument();
|
|
@@ -46,6 +49,30 @@ describe('WorkflowRunSummary', () => {
|
|
|
46
49
|
).not.toBeInTheDocument();
|
|
47
50
|
});
|
|
48
51
|
|
|
52
|
+
test('omits the run number before the server assigns one', async () => {
|
|
53
|
+
const run = {...workflowRunDetail({workflow_name: 'CI'}), number: null};
|
|
54
|
+
render(<WorkflowRunSummary run={run} />);
|
|
55
|
+
|
|
56
|
+
const summary = await screen.findByRole('region', {name: 'deploy-web'});
|
|
57
|
+
|
|
58
|
+
expect(within(summary).queryByText('CI #1')).not.toBeInTheDocument();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('separates a standalone run number from the run timestamp', async () => {
|
|
62
|
+
const run = workflowRunDetail({
|
|
63
|
+
workflow_name: 'CI',
|
|
64
|
+
trigger_source: '',
|
|
65
|
+
trigger_event: '',
|
|
66
|
+
});
|
|
67
|
+
render(<WorkflowRunSummary run={run} />);
|
|
68
|
+
|
|
69
|
+
const summary = await screen.findByRole('region', {name: 'deploy-web'});
|
|
70
|
+
|
|
71
|
+
expect(within(summary).getByText('CI #1')).toBeInTheDocument();
|
|
72
|
+
const timestamp = within(summary).getByText(RELATIVE_TIME_TEXT_PATTERN);
|
|
73
|
+
expect(timestamp.previousElementSibling).toHaveAttribute('aria-hidden', 'true');
|
|
74
|
+
});
|
|
75
|
+
|
|
49
76
|
test('uses the selected run attempt for summary status and trigger time', async () => {
|
|
50
77
|
const rootCreatedAt = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
|
51
78
|
const attemptCreatedAt = new Date(Date.now() - 5 * 60 * 1000).toISOString();
|
|
@@ -103,27 +130,6 @@ describe('WorkflowRunSummary', () => {
|
|
|
103
130
|
);
|
|
104
131
|
});
|
|
105
132
|
|
|
106
|
-
test('renders source control only when source is available', async () => {
|
|
107
|
-
const user = userEvent.setup();
|
|
108
|
-
const onSourceToggle = vi.fn();
|
|
109
|
-
renderSummary(
|
|
110
|
-
{source_snapshot: {format: 'yaml', content: 'name: deploy-web'}},
|
|
111
|
-
{
|
|
112
|
-
sourceAvailable: true,
|
|
113
|
-
sourceOpen: false,
|
|
114
|
-
sourcePanelId: 'workflow-source-panel',
|
|
115
|
-
onSourceToggle,
|
|
116
|
-
},
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
const sourceButton = await screen.findByRole('button', {name: 'View source'});
|
|
120
|
-
await user.click(sourceButton);
|
|
121
|
-
|
|
122
|
-
expect(sourceButton).toHaveAttribute('aria-controls', 'workflow-source-panel');
|
|
123
|
-
expect(sourceButton).toHaveAttribute('aria-expanded', 'false');
|
|
124
|
-
expect(onSourceToggle).toHaveBeenCalledTimes(1);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
133
|
test('shows the selected attempt duration, not the top-level run duration', async () => {
|
|
128
134
|
renderSummary({
|
|
129
135
|
started_at: '2026-05-07T00:00:00.000Z',
|
|
@@ -168,7 +174,7 @@ describe('WorkflowRunSummary', () => {
|
|
|
168
174
|
expect(duration).toHaveAttribute('aria-label', 'running 2m 14s');
|
|
169
175
|
});
|
|
170
176
|
|
|
171
|
-
test('
|
|
177
|
+
test('does not render a whole-run source control', async () => {
|
|
172
178
|
renderSummary();
|
|
173
179
|
|
|
174
180
|
await screen.findByRole('region', {name: 'deploy-web'});
|
|
@@ -12,8 +12,9 @@ import {RelativeTime} from '@shipfox/react-ui/relative-time';
|
|
|
12
12
|
import {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';
|
|
13
13
|
import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';
|
|
14
14
|
import {Header, Text} from '@shipfox/react-ui/typography';
|
|
15
|
-
import
|
|
15
|
+
import {Link} from '@tanstack/react-router';
|
|
16
16
|
import {useId} from 'react';
|
|
17
|
+
import {WorkflowRunNumberLabel} from '#components/workflow-run-number-label.js';
|
|
17
18
|
import {
|
|
18
19
|
isWorkflowRunTerminal,
|
|
19
20
|
type Job,
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
type WorkflowRunDetail,
|
|
22
23
|
type WorkflowRunRerunMode,
|
|
23
24
|
} from '#core/workflow-run.js';
|
|
25
|
+
import {validateWorkflowRunsSearch, workflowRunListSearchParams} from '#routes/inputs.js';
|
|
24
26
|
import {WorkflowRunDurationLabel} from '../workflow-run-duration-label.js';
|
|
25
27
|
import {getWorkflowStatusVisual} from '../workflow-status/status-visuals.js';
|
|
26
28
|
import {WorkflowRunAttemptSwitcher} from './workflow-run-attempt-switcher.js';
|
|
@@ -32,14 +34,9 @@ const STATUS_BADGE_LABEL_WIDTH_CH = Math.max(
|
|
|
32
34
|
type WorkflowRunAction = 'cancel' | 'rerun-all' | 'rerun-menu' | 'none';
|
|
33
35
|
|
|
34
36
|
export interface WorkflowRunSummaryProps {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
workspaceSlug?: string | undefined;
|
|
38
|
+
projectSlug?: string | undefined;
|
|
37
39
|
run: WorkflowRunDetail;
|
|
38
|
-
sourceAvailable?: boolean | undefined;
|
|
39
|
-
sourceOpen?: boolean | undefined;
|
|
40
|
-
sourcePanelId?: string | undefined;
|
|
41
|
-
sourceButtonRef?: Ref<HTMLButtonElement> | undefined;
|
|
42
|
-
onSourceToggle?: (() => void) | undefined;
|
|
43
40
|
cancelling?: boolean | undefined;
|
|
44
41
|
onCancel?: (() => void) | undefined;
|
|
45
42
|
rerunPending?: boolean | undefined;
|
|
@@ -48,14 +45,9 @@ export interface WorkflowRunSummaryProps {
|
|
|
48
45
|
}
|
|
49
46
|
|
|
50
47
|
export function WorkflowRunSummary({
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
workspaceSlug,
|
|
49
|
+
projectSlug,
|
|
53
50
|
run,
|
|
54
|
-
sourceAvailable = false,
|
|
55
|
-
sourceOpen = false,
|
|
56
|
-
sourcePanelId,
|
|
57
|
-
sourceButtonRef,
|
|
58
|
-
onSourceToggle,
|
|
59
51
|
cancelling = false,
|
|
60
52
|
onCancel,
|
|
61
53
|
rerunPending = false,
|
|
@@ -65,9 +57,10 @@ export function WorkflowRunSummary({
|
|
|
65
57
|
const headingId = useId();
|
|
66
58
|
const status = getWorkflowStatusVisual(run.runAttempt.status);
|
|
67
59
|
const action = workflowRunActionForRun(run);
|
|
60
|
+
const hasAction = canRenderWorkflowRunAction(action, onCancel, onRerun);
|
|
68
61
|
const attemptSwitcher =
|
|
69
|
-
latestAttempt && latestAttempt > 1 &&
|
|
70
|
-
? {
|
|
62
|
+
latestAttempt && latestAttempt > 1 && workspaceSlug && projectSlug
|
|
63
|
+
? {workspaceSlug, projectSlug, latestAttempt}
|
|
71
64
|
: null;
|
|
72
65
|
const displayDuration = run.runAttempt.displayDuration;
|
|
73
66
|
const {ref: headingTextRef, isTruncated: isHeadingTruncated} =
|
|
@@ -75,68 +68,88 @@ export function WorkflowRunSummary({
|
|
|
75
68
|
|
|
76
69
|
return (
|
|
77
70
|
<TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>
|
|
78
|
-
<section
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
title={isHeadingTruncated ? run.name : undefined}
|
|
96
|
-
className="block min-w-0 truncate"
|
|
71
|
+
<section aria-labelledby={headingId} className="bg-background-neutral-background px-16 py-12">
|
|
72
|
+
<div className="grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-x-12 gap-y-8 overflow-hidden max-[480px]:grid-cols-1">
|
|
73
|
+
<nav
|
|
74
|
+
aria-label="Breadcrumb"
|
|
75
|
+
className="col-start-1 row-start-1 min-w-0 max-[480px]:col-start-auto max-[480px]:row-start-auto"
|
|
76
|
+
>
|
|
77
|
+
<ol className="flex min-w-0 items-center gap-8">
|
|
78
|
+
{workspaceSlug && projectSlug ? (
|
|
79
|
+
<li className="shrink-0">
|
|
80
|
+
<Link
|
|
81
|
+
to="/w/$workspaceSlug/p/$projectSlug/runs"
|
|
82
|
+
params={{workspaceSlug, projectSlug}}
|
|
83
|
+
search={
|
|
84
|
+
((previous: Record<string, unknown>) =>
|
|
85
|
+
workflowRunListSearchParams(validateWorkflowRunsSearch(previous))) as never
|
|
86
|
+
}
|
|
87
|
+
className="rounded-4 text-xs font-medium text-foreground-neutral-subtle underline decoration-border-neutral-strong underline-offset-4 outline-none hover:text-foreground-neutral-base focus-visible:shadow-border-interactive-with-active"
|
|
97
88
|
>
|
|
98
|
-
|
|
99
|
-
</
|
|
100
|
-
</
|
|
101
|
-
</TooltipTrigger>
|
|
102
|
-
{isHeadingTruncated ? (
|
|
103
|
-
<TooltipContent>
|
|
104
|
-
<Text as="span" size="xs" className="max-w-[360px] break-words">
|
|
105
|
-
{run.name}
|
|
106
|
-
</Text>
|
|
107
|
-
</TooltipContent>
|
|
89
|
+
Runs
|
|
90
|
+
</Link>
|
|
91
|
+
</li>
|
|
108
92
|
) : null}
|
|
109
|
-
|
|
110
|
-
|
|
93
|
+
{workspaceSlug && projectSlug ? (
|
|
94
|
+
<li aria-hidden="true" className="shrink-0 text-xs text-foreground-neutral-subtle">
|
|
95
|
+
/
|
|
96
|
+
</li>
|
|
97
|
+
) : null}
|
|
98
|
+
<li aria-current="page" className="flex min-w-0 items-center gap-8">
|
|
99
|
+
<Badge variant={status.badge} size="xs">
|
|
100
|
+
<span className="text-center" style={{width: `${STATUS_BADGE_LABEL_WIDTH_CH}ch`}}>
|
|
101
|
+
{status.label}
|
|
102
|
+
</span>
|
|
103
|
+
</Badge>
|
|
111
104
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
105
|
+
<Tooltip>
|
|
106
|
+
<TooltipTrigger asChild>
|
|
107
|
+
<Header id={headingId} variant="h3" className="min-w-0 truncate">
|
|
108
|
+
<span
|
|
109
|
+
ref={headingTextRef}
|
|
110
|
+
title={isHeadingTruncated ? run.name : undefined}
|
|
111
|
+
className="block min-w-0 truncate"
|
|
112
|
+
>
|
|
113
|
+
{run.name}
|
|
114
|
+
</span>
|
|
115
|
+
</Header>
|
|
116
|
+
</TooltipTrigger>
|
|
117
|
+
{isHeadingTruncated ? (
|
|
118
|
+
<TooltipContent>
|
|
119
|
+
<Text as="span" size="xs" className="max-w-[360px] break-words">
|
|
120
|
+
{run.name}
|
|
121
|
+
</Text>
|
|
122
|
+
</TooltipContent>
|
|
123
|
+
) : null}
|
|
124
|
+
</Tooltip>
|
|
125
|
+
</li>
|
|
126
|
+
</ol>
|
|
127
|
+
</nav>
|
|
128
|
+
|
|
129
|
+
{hasAction ? (
|
|
130
|
+
<div className="col-start-2 row-start-1 flex min-w-max items-center gap-6 justify-self-end max-[480px]:col-start-auto max-[480px]:row-start-auto max-[480px]:justify-self-start">
|
|
131
|
+
<WorkflowRunActionSlot
|
|
132
|
+
action={action}
|
|
133
|
+
cancelling={cancelling}
|
|
134
|
+
onCancel={onCancel}
|
|
135
|
+
rerunPending={rerunPending}
|
|
136
|
+
onRerun={onRerun}
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
) : null}
|
|
140
|
+
|
|
141
|
+
<div className="col-span-2 row-start-2 flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-subtle max-[480px]:col-span-1 max-[480px]:row-start-auto">
|
|
142
|
+
{run.number !== null ? (
|
|
143
|
+
<>
|
|
144
|
+
<WorkflowRunNumberLabel run={run} />
|
|
145
|
+
{attemptSwitcher || run.triggerDisplayLabel ? <MetadataSeparator /> : null}
|
|
146
|
+
</>
|
|
132
147
|
) : null}
|
|
133
|
-
</div>
|
|
134
148
|
|
|
135
|
-
<div className="col-span-2 row-start-2 flex min-w-0 items-center gap-12 overflow-hidden text-foreground-neutral-muted">
|
|
136
149
|
{attemptSwitcher ? (
|
|
137
150
|
<WorkflowRunAttemptSwitcher
|
|
138
|
-
|
|
139
|
-
|
|
151
|
+
workspaceSlug={attemptSwitcher.workspaceSlug}
|
|
152
|
+
projectSlug={attemptSwitcher.projectSlug}
|
|
140
153
|
run={run}
|
|
141
154
|
latestAttempt={attemptSwitcher.latestAttempt}
|
|
142
155
|
/>
|
|
@@ -151,7 +164,7 @@ export function WorkflowRunSummary({
|
|
|
151
164
|
<button
|
|
152
165
|
type="button"
|
|
153
166
|
aria-label={run.triggerLabel}
|
|
154
|
-
className="inline-flex max-w-full min-w-0 items-center gap-4 rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-
|
|
167
|
+
className="inline-flex max-w-full min-w-0 cursor-help items-center gap-4 rounded-6 border-0 bg-transparent p-0 text-left text-foreground-neutral-subtle outline-none focus-visible:shadow-button-neutral-focus"
|
|
155
168
|
>
|
|
156
169
|
<TriggerSourceIcon
|
|
157
170
|
provider={run.triggerProvider}
|
|
@@ -174,10 +187,12 @@ export function WorkflowRunSummary({
|
|
|
174
187
|
</>
|
|
175
188
|
) : null}
|
|
176
189
|
|
|
177
|
-
{attemptSwitcher || run.triggerDisplayLabel ?
|
|
190
|
+
{run.number !== null || attemptSwitcher || run.triggerDisplayLabel ? (
|
|
191
|
+
<MetadataSeparator />
|
|
192
|
+
) : null}
|
|
178
193
|
<RelativeTime
|
|
179
194
|
value={run.runAttempt.createdAt}
|
|
180
|
-
className="shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-
|
|
195
|
+
className="shrink-0 whitespace-nowrap text-xs leading-20 text-foreground-neutral-subtle"
|
|
181
196
|
/>
|
|
182
197
|
|
|
183
198
|
{displayDuration ? (
|
|
@@ -185,7 +200,7 @@ export function WorkflowRunSummary({
|
|
|
185
200
|
<MetadataSeparator />
|
|
186
201
|
<WorkflowRunDurationLabel
|
|
187
202
|
duration={displayDuration}
|
|
188
|
-
className="text-foreground-neutral-
|
|
203
|
+
className="text-foreground-neutral-subtle"
|
|
189
204
|
/>
|
|
190
205
|
</>
|
|
191
206
|
) : null}
|
|
@@ -280,6 +295,16 @@ function workflowRunActionForRun(run: WorkflowRunDetail): WorkflowRunAction {
|
|
|
280
295
|
return 'rerun-menu';
|
|
281
296
|
}
|
|
282
297
|
|
|
298
|
+
function canRenderWorkflowRunAction(
|
|
299
|
+
action: WorkflowRunAction,
|
|
300
|
+
onCancel: (() => void) | undefined,
|
|
301
|
+
onRerun: ((mode: WorkflowRunRerunMode) => void) | undefined,
|
|
302
|
+
): boolean {
|
|
303
|
+
if (action === 'cancel') return onCancel !== undefined;
|
|
304
|
+
if (action === 'rerun-all' || action === 'rerun-menu') return onRerun !== undefined;
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
|
|
283
308
|
function hasFailedOrCancelledJobs(run: WorkflowRunDetail): boolean {
|
|
284
309
|
if (!workflowRunHasJobs(run)) return false;
|
|
285
310
|
|
|
@@ -290,6 +315,6 @@ function workflowRunHasJobs(run: WorkflowRunDetail): run is WorkflowRunDetail &
|
|
|
290
315
|
return 'jobs' in run && Array.isArray(run.jobs);
|
|
291
316
|
}
|
|
292
317
|
|
|
293
|
-
function MetadataSeparator() {
|
|
318
|
+
export function MetadataSeparator() {
|
|
294
319
|
return <span aria-hidden="true" className="h-12 w-px shrink-0 bg-border-neutral-base" />;
|
|
295
320
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export {RunAnnotationSummaryLine} from './run-annotation-summary-line.js';
|
|
2
|
+
export {RunAnnotationsEmpty} from './run-annotations-empty.js';
|
|
3
|
+
export {RunJobsList} from './run-jobs-list.js';
|
|
4
|
+
export {RunTabCount} from './run-tab-count.js';
|
|
5
|
+
export {RunTabStrip} from './run-tab-strip.js';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMemoryHistory,
|
|
3
|
+
createRootRoute,
|
|
4
|
+
createRoute,
|
|
5
|
+
createRouter,
|
|
6
|
+
Outlet,
|
|
7
|
+
RouterProvider,
|
|
8
|
+
} from '@tanstack/react-router';
|
|
9
|
+
import {act, render, screen} from '@testing-library/react';
|
|
10
|
+
import type {RunAnnotationSummary} from '#core/workflow-run-tabs.js';
|
|
11
|
+
import type {WorkflowRunsSearch} from '#routes/inputs.js';
|
|
12
|
+
import {RunAnnotationSummaryLine} from './run-annotation-summary-line.js';
|
|
13
|
+
|
|
14
|
+
const ANNOTATION_SUMMARY: RunAnnotationSummary = {
|
|
15
|
+
total: 5,
|
|
16
|
+
error: 2,
|
|
17
|
+
warning: 1,
|
|
18
|
+
info: 2,
|
|
19
|
+
success: 0,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
describe('RunAnnotationSummaryLine', () => {
|
|
23
|
+
test('links the severity breakdown into the Annotations panel', async () => {
|
|
24
|
+
await renderSummaryLine();
|
|
25
|
+
|
|
26
|
+
expect(screen.getByText('5 annotations')).toBeInTheDocument();
|
|
27
|
+
expect(screen.getByRole('link', {name: '2 errors'})).toHaveAttribute(
|
|
28
|
+
'href',
|
|
29
|
+
'/w/acme/p/project/runs/run-1?tab=annotations&severity=error',
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('clears a stale annotation when linking to a severity', async () => {
|
|
34
|
+
await renderSummaryLine({annotation: 'annotation-old'});
|
|
35
|
+
|
|
36
|
+
expect(screen.getByRole('link', {name: '2 errors'})).toHaveAttribute(
|
|
37
|
+
'href',
|
|
38
|
+
'/w/acme/p/project/runs/run-1?tab=annotations&severity=error',
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
async function renderSummaryLine(search: WorkflowRunsSearch = {}) {
|
|
44
|
+
const rootRoute = createRootRoute({component: Outlet});
|
|
45
|
+
const runRoute = createRoute({
|
|
46
|
+
getParentRoute: () => rootRoute,
|
|
47
|
+
path: '/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId',
|
|
48
|
+
component: () => (
|
|
49
|
+
<RunAnnotationSummaryLine
|
|
50
|
+
summary={ANNOTATION_SUMMARY}
|
|
51
|
+
workspaceSlug="acme"
|
|
52
|
+
projectSlug="project"
|
|
53
|
+
workflowRunId="run-1"
|
|
54
|
+
search={search}
|
|
55
|
+
/>
|
|
56
|
+
),
|
|
57
|
+
});
|
|
58
|
+
const router = createRouter({
|
|
59
|
+
history: createMemoryHistory({initialEntries: ['/w/acme/p/project/runs/run-1']}),
|
|
60
|
+
routeTree: rootRoute.addChildren([runRoute]),
|
|
61
|
+
});
|
|
62
|
+
await router.load();
|
|
63
|
+
let result: ReturnType<typeof render> | undefined;
|
|
64
|
+
await act(() => {
|
|
65
|
+
result = render(<RouterProvider router={router} />);
|
|
66
|
+
});
|
|
67
|
+
if (!result) throw new Error('Run annotation summary did not render.');
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {Text} from '@shipfox/react-ui/typography';
|
|
2
|
+
import {Link} from '@tanstack/react-router';
|
|
3
|
+
import type {RunAnnotationSummary} from '#core/workflow-run-tabs.js';
|
|
4
|
+
import {
|
|
5
|
+
WORKFLOW_RUN_ANNOTATION_SEVERITIES,
|
|
6
|
+
type WorkflowRunAnnotationSeverity,
|
|
7
|
+
type WorkflowRunsSearch,
|
|
8
|
+
workflowRunSearchParams,
|
|
9
|
+
} from '#routes/inputs.js';
|
|
10
|
+
import {MetadataSeparator} from '../workflow-run-summary/workflow-run-summary.js';
|
|
11
|
+
|
|
12
|
+
export interface RunAnnotationSummaryLineProps {
|
|
13
|
+
summary?: RunAnnotationSummary | undefined;
|
|
14
|
+
workspaceSlug?: string | undefined;
|
|
15
|
+
projectSlug?: string | undefined;
|
|
16
|
+
workflowRunId?: string | undefined;
|
|
17
|
+
search?: WorkflowRunsSearch | undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function RunAnnotationSummaryLine({
|
|
21
|
+
summary,
|
|
22
|
+
workspaceSlug,
|
|
23
|
+
projectSlug,
|
|
24
|
+
workflowRunId,
|
|
25
|
+
search = {},
|
|
26
|
+
}: RunAnnotationSummaryLineProps) {
|
|
27
|
+
if (!summary) return null;
|
|
28
|
+
|
|
29
|
+
const visibleSeverities = WORKFLOW_RUN_ANNOTATION_SEVERITIES.filter(
|
|
30
|
+
(severity) => summary[severity] > 0,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="flex flex-wrap items-center gap-x-8 gap-y-4 text-foreground-neutral-subtle">
|
|
35
|
+
<Text as="span" size="xs" className="shrink-0 text-foreground-neutral-subtle">
|
|
36
|
+
{summary.total} {pluralize('annotation', summary.total)}
|
|
37
|
+
</Text>
|
|
38
|
+
{visibleSeverities.map((severity) => (
|
|
39
|
+
<span key={severity} className="inline-flex shrink-0 items-center gap-8">
|
|
40
|
+
<MetadataSeparator />
|
|
41
|
+
<SeverityLink
|
|
42
|
+
count={summary[severity]}
|
|
43
|
+
severity={severity}
|
|
44
|
+
workspaceSlug={workspaceSlug}
|
|
45
|
+
projectSlug={projectSlug}
|
|
46
|
+
workflowRunId={workflowRunId}
|
|
47
|
+
search={search}
|
|
48
|
+
/>
|
|
49
|
+
</span>
|
|
50
|
+
))}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function SeverityLink({
|
|
56
|
+
count,
|
|
57
|
+
severity,
|
|
58
|
+
workspaceSlug,
|
|
59
|
+
projectSlug,
|
|
60
|
+
workflowRunId,
|
|
61
|
+
search,
|
|
62
|
+
}: {
|
|
63
|
+
count: number;
|
|
64
|
+
severity: WorkflowRunAnnotationSeverity;
|
|
65
|
+
workspaceSlug?: string | undefined;
|
|
66
|
+
projectSlug?: string | undefined;
|
|
67
|
+
workflowRunId?: string | undefined;
|
|
68
|
+
search: WorkflowRunsSearch;
|
|
69
|
+
}) {
|
|
70
|
+
const label = `${count} ${pluralize(severity, count)}`;
|
|
71
|
+
if (!workspaceSlug || !projectSlug || !workflowRunId) {
|
|
72
|
+
return <span className="text-foreground-highlight-interactive">{label}</span>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const searchWithoutAnnotation = {...search};
|
|
76
|
+
delete searchWithoutAnnotation.annotation;
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Link
|
|
80
|
+
to="/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId"
|
|
81
|
+
params={{workspaceSlug, projectSlug, workflowRunId}}
|
|
82
|
+
search={
|
|
83
|
+
workflowRunSearchParams(
|
|
84
|
+
{...searchWithoutAnnotation, tab: 'annotations', severity},
|
|
85
|
+
search,
|
|
86
|
+
) as never
|
|
87
|
+
}
|
|
88
|
+
className="text-foreground-highlight-interactive outline-none hover:underline focus-visible:shadow-border-interactive-with-active"
|
|
89
|
+
>
|
|
90
|
+
{label}
|
|
91
|
+
</Link>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function pluralize(word: string, count: number): string {
|
|
96
|
+
return count === 1 ? word : `${word}s`;
|
|
97
|
+
}
|