@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
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/client-workflows",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "14.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@swc/helpers": "^0.5.17",
|
|
29
29
|
"date-fns": "^4.1.0",
|
|
30
|
-
"@shipfox/api-definitions-dto": "
|
|
31
|
-
"@shipfox/api-workflows-dto": "
|
|
30
|
+
"@shipfox/api-definitions-dto": "12.0.0",
|
|
31
|
+
"@shipfox/api-workflows-dto": "12.1.0",
|
|
32
|
+
"@shipfox/client-logs": "14.0.0",
|
|
32
33
|
"@shipfox/client-api": "6.0.1",
|
|
33
|
-
"@shipfox/client-
|
|
34
|
-
"@shipfox/client-
|
|
35
|
-
"@shipfox/client-
|
|
36
|
-
"@shipfox/client-
|
|
37
|
-
"@shipfox/
|
|
38
|
-
"@shipfox/react-ui": "0.3.7"
|
|
34
|
+
"@shipfox/client-projects": "14.0.0",
|
|
35
|
+
"@shipfox/client-shell": "14.0.0",
|
|
36
|
+
"@shipfox/client-triggers": "14.0.0",
|
|
37
|
+
"@shipfox/client-ui": "14.0.0",
|
|
38
|
+
"@shipfox/react-ui": "0.5.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@tanstack/react-query": "^5.101.0",
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import {humanDuration} from '@shipfox/react-ui/utils';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
JobDisplayDuration,
|
|
4
|
+
JobExecutionDisplayStatus,
|
|
5
|
+
JobExecutionTime,
|
|
6
|
+
} from '#core/workflow-run.js';
|
|
3
7
|
|
|
4
8
|
export function formatJobExecutionTimeLabel(time: JobExecutionTime): string {
|
|
5
9
|
switch (time.state) {
|
|
@@ -16,6 +20,7 @@ export function formatJobExecutionTimeLabel(time: JobExecutionTime): string {
|
|
|
16
20
|
|
|
17
21
|
export function formatJobDurationAccessibleLabel(
|
|
18
22
|
duration: JobDisplayDuration | null,
|
|
23
|
+
displayStatus?: JobExecutionDisplayStatus,
|
|
19
24
|
): string | undefined {
|
|
20
25
|
if (duration === null) return undefined;
|
|
21
26
|
|
|
@@ -24,7 +29,11 @@ export function formatJobDurationAccessibleLabel(
|
|
|
24
29
|
case 'queue':
|
|
25
30
|
return duration.state === 'live' ? `queueing ${label}` : `queued ${label}`;
|
|
26
31
|
case 'run':
|
|
27
|
-
|
|
32
|
+
if (duration.state !== 'live') return `ran ${label}`;
|
|
33
|
+
if (displayStatus === 'running') return `running ${label}`;
|
|
34
|
+
return displayStatus === 'pending'
|
|
35
|
+
? `runner assigned ${label}`
|
|
36
|
+
: `execution elapsed ${label}`;
|
|
28
37
|
default: {
|
|
29
38
|
const exhaustive: never = duration;
|
|
30
39
|
return exhaustive;
|
|
@@ -22,7 +22,11 @@ function LiveDurationText({fromIso}: {fromIso: string}) {
|
|
|
22
22
|
function DurationText({children}: {children: string}) {
|
|
23
23
|
// The status icon names the state; the node aria-label carries the verb.
|
|
24
24
|
return (
|
|
25
|
-
<Code
|
|
25
|
+
<Code
|
|
26
|
+
as="span"
|
|
27
|
+
variant="label"
|
|
28
|
+
className="shrink-0 tabular-nums text-foreground-neutral-subtle"
|
|
29
|
+
>
|
|
26
30
|
{children}
|
|
27
31
|
</Code>
|
|
28
32
|
);
|
|
@@ -7,6 +7,7 @@ import type {WorkflowRunDetail} from '#core/workflow-run.js';
|
|
|
7
7
|
import type {JobGraphModel, JobGraphNavigationKey} from './graph-model.js';
|
|
8
8
|
import {nextJobGraphNodeId} from './graph-model.js';
|
|
9
9
|
import {JobNode, TriggerNode} from './job-node.js';
|
|
10
|
+
import type {JobGraphSelectionSource} from './types.js';
|
|
10
11
|
|
|
11
12
|
const NODE_WIDTH = 208;
|
|
12
13
|
const NODE_HEIGHT = 48;
|
|
@@ -27,7 +28,7 @@ export function JobGraphContent({
|
|
|
27
28
|
'triggerDisplayLabel' | 'triggerLabel' | 'triggerProvider' | 'triggerSource'
|
|
28
29
|
>;
|
|
29
30
|
selectedJobId?: string | undefined;
|
|
30
|
-
onSelectJob: (jobId: string | undefined) => void;
|
|
31
|
+
onSelectJob: (jobId: string | undefined, source?: JobGraphSelectionSource) => void;
|
|
31
32
|
}) {
|
|
32
33
|
const nodeRefs = useRef(new Map<string, HTMLButtonElement>());
|
|
33
34
|
const [hoveredJobId, setHoveredJobId] = useState<string | undefined>();
|
|
@@ -74,13 +75,13 @@ export function JobGraphContent({
|
|
|
74
75
|
if (!nextNodeId) return;
|
|
75
76
|
|
|
76
77
|
event.preventDefault();
|
|
77
|
-
onSelectJob(nextNodeId);
|
|
78
|
+
onSelectJob(nextNodeId, 'keyboard');
|
|
78
79
|
nodeRefs.current.get(nextNodeId)?.focus();
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
return (
|
|
82
83
|
<TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>
|
|
83
|
-
<div className="min-h-0 overflow-auto bg-background-neutral-
|
|
84
|
+
<div className="min-h-0 overflow-auto bg-background-neutral-background">
|
|
84
85
|
<div className="relative" style={{width: contentWidth, minHeight: contentHeight}}>
|
|
85
86
|
<GraphEdges model={model} hoveredJobId={hoveredJobId} />
|
|
86
87
|
<div
|
|
@@ -101,7 +102,9 @@ export function JobGraphContent({
|
|
|
101
102
|
node={node}
|
|
102
103
|
selected={node.id === selectedJobId}
|
|
103
104
|
ref={setNodeRef(node.id)}
|
|
104
|
-
onSelect={() =>
|
|
105
|
+
onSelect={() =>
|
|
106
|
+
onSelectJob(node.id === selectedJobId ? undefined : node.id, 'pointer')
|
|
107
|
+
}
|
|
105
108
|
onKeyDown={handleKeyDown}
|
|
106
109
|
onHoverStart={() => setHoveredJobId(node.id)}
|
|
107
110
|
onHoverEnd={() =>
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
workflowJob,
|
|
7
7
|
workflowJobExecutionDto,
|
|
8
8
|
workflowRunDetail,
|
|
9
|
+
workflowStepDto,
|
|
9
10
|
} from '#test/fixtures/workflow-run.js';
|
|
10
11
|
import {JobGraph} from './job-graph.js';
|
|
11
12
|
|
|
@@ -46,10 +47,7 @@ describe('JobGraph', () => {
|
|
|
46
47
|
render(
|
|
47
48
|
<JobGraph
|
|
48
49
|
run={makeRun({
|
|
49
|
-
jobs: [
|
|
50
|
-
makeJob({name: 'linux', status: 'running'}),
|
|
51
|
-
makeJob({name: 'macos', status: 'running', position: 1}),
|
|
52
|
-
],
|
|
50
|
+
jobs: [makeRunningJob({name: 'linux'}), makeRunningJob({name: 'macos', position: 1})],
|
|
53
51
|
})}
|
|
54
52
|
/>,
|
|
55
53
|
);
|
|
@@ -99,7 +97,7 @@ describe('JobGraph', () => {
|
|
|
99
97
|
run={makeRun({
|
|
100
98
|
jobs: [
|
|
101
99
|
makeJob({name: 'build'}),
|
|
102
|
-
|
|
100
|
+
makeRunningJob({name: 'deploy', position: 1, dependencies: ['build']}),
|
|
103
101
|
],
|
|
104
102
|
})}
|
|
105
103
|
/>,
|
|
@@ -161,7 +159,7 @@ describe('JobGraph', () => {
|
|
|
161
159
|
/>,
|
|
162
160
|
);
|
|
163
161
|
|
|
164
|
-
const build = screen.getByRole('button', {name: 'build,
|
|
162
|
+
const build = screen.getByRole('button', {name: 'build, Succeeded, 2 executions'});
|
|
165
163
|
const deploy = screen.getByRole('button', {name: 'deploy, Pending'});
|
|
166
164
|
expect(within(build).getByText('2')).toBeInTheDocument();
|
|
167
165
|
expect(within(build).queryByText('2 exec')).not.toBeInTheDocument();
|
|
@@ -224,7 +222,7 @@ describe('JobGraph', () => {
|
|
|
224
222
|
dependencies: ['build'],
|
|
225
223
|
status: 'succeeded',
|
|
226
224
|
});
|
|
227
|
-
const testJob =
|
|
225
|
+
const testJob = makeRunningJob({
|
|
228
226
|
name: 'test',
|
|
229
227
|
position: 2,
|
|
230
228
|
dependencies: ['build'],
|
|
@@ -257,7 +255,7 @@ describe('JobGraph', () => {
|
|
|
257
255
|
test('routes skipped-column join edges away from intermediate nodes', () => {
|
|
258
256
|
const packageJob = makeJob({name: 'package', position: 0, status: 'succeeded'});
|
|
259
257
|
const securityScan = makeJob({name: 'security-scan', position: 1, status: 'succeeded'});
|
|
260
|
-
const smokeTests =
|
|
258
|
+
const smokeTests = makeRunningJob({
|
|
261
259
|
name: 'smoke-tests',
|
|
262
260
|
position: 2,
|
|
263
261
|
dependencies: ['package'],
|
|
@@ -296,3 +294,16 @@ function makeRun(overrides: Partial<WorkflowRunDetail> = {}): WorkflowRunDetail
|
|
|
296
294
|
function makeJob(overrides: Partial<WorkflowRunJobDetailDto> & {name: string}): Job {
|
|
297
295
|
return workflowJob(overrides);
|
|
298
296
|
}
|
|
297
|
+
|
|
298
|
+
function makeRunningJob(overrides: Partial<WorkflowRunJobDetailDto> & {name: string}): Job {
|
|
299
|
+
return makeJob({
|
|
300
|
+
...overrides,
|
|
301
|
+
status: 'running',
|
|
302
|
+
job_executions: [
|
|
303
|
+
workflowJobExecutionDto({
|
|
304
|
+
status: 'running',
|
|
305
|
+
steps: [workflowStepDto({status: 'running'})],
|
|
306
|
+
}),
|
|
307
|
+
],
|
|
308
|
+
});
|
|
309
|
+
}
|
|
@@ -3,6 +3,7 @@ import {useState} from 'react';
|
|
|
3
3
|
import type {WorkflowRunDetail} from '#core/workflow-run.js';
|
|
4
4
|
import type {JobGraphModel} from './graph-model.js';
|
|
5
5
|
import {JobGraphContent} from './job-graph-content.js';
|
|
6
|
+
import type {JobGraphSelectionSource} from './types.js';
|
|
6
7
|
|
|
7
8
|
export function JobGraphView({
|
|
8
9
|
model,
|
|
@@ -19,7 +20,9 @@ export function JobGraphView({
|
|
|
19
20
|
>;
|
|
20
21
|
selectedJobId?: string | undefined;
|
|
21
22
|
defaultSelectedJobId?: string | undefined;
|
|
22
|
-
onSelectedJobChange?:
|
|
23
|
+
onSelectedJobChange?:
|
|
24
|
+
| ((jobId: string | undefined, source?: JobGraphSelectionSource) => void)
|
|
25
|
+
| undefined;
|
|
23
26
|
className?: string | undefined;
|
|
24
27
|
}) {
|
|
25
28
|
const [localSelectedJobId, setLocalSelectedJobId] = useState<string | undefined>(
|
|
@@ -27,9 +30,9 @@ export function JobGraphView({
|
|
|
27
30
|
);
|
|
28
31
|
const selected = selectedJobId ?? localSelectedJobId;
|
|
29
32
|
|
|
30
|
-
function selectJob(jobId: string | undefined) {
|
|
33
|
+
function selectJob(jobId: string | undefined, source?: JobGraphSelectionSource) {
|
|
31
34
|
setLocalSelectedJobId(jobId);
|
|
32
|
-
onSelectedJobChange?.(jobId);
|
|
35
|
+
onSelectedJobChange?.(jobId, source);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
return (
|
|
@@ -2,7 +2,7 @@ import type {WorkflowRunJobDetailDto} from '@shipfox/api-workflows-dto';
|
|
|
2
2
|
import type {Meta, StoryObj} from '@storybook/react';
|
|
3
3
|
import {within} from 'storybook/test';
|
|
4
4
|
import type {Job, WorkflowRunDetail} from '#core/workflow-run.js';
|
|
5
|
-
import {workflowJob, workflowRunDetail} from '#test/fixtures/workflow-run.js';
|
|
5
|
+
import {workflowJob, workflowRunDetail, workflowStepDto} from '#test/fixtures/workflow-run.js';
|
|
6
6
|
import {JobGraph} from './job-graph.js';
|
|
7
7
|
|
|
8
8
|
const meta = {
|
|
@@ -11,7 +11,7 @@ const meta = {
|
|
|
11
11
|
parameters: {layout: 'centered'},
|
|
12
12
|
decorators: [
|
|
13
13
|
(Story) => (
|
|
14
|
-
<div className="h-520 w-900 overflow-auto bg-background-neutral-
|
|
14
|
+
<div className="h-520 w-900 overflow-auto bg-background-neutral-background p-16">
|
|
15
15
|
<Story />
|
|
16
16
|
</div>
|
|
17
17
|
),
|
|
@@ -93,6 +93,23 @@ export const Empty: Story = {
|
|
|
93
93
|
args: {run: makeRun({jobs: []})},
|
|
94
94
|
};
|
|
95
95
|
|
|
96
|
+
export const LargeWideAndTall: Story = {
|
|
97
|
+
args: {
|
|
98
|
+
run: makeRun({
|
|
99
|
+
jobs: [
|
|
100
|
+
...linearJobs(10),
|
|
101
|
+
...Array.from({length: 10}, (_, index) =>
|
|
102
|
+
makeJob({
|
|
103
|
+
name: `parallel-${String(index + 1).padStart(2, '0')}`,
|
|
104
|
+
position: 20 + index,
|
|
105
|
+
status: index < 2 ? 'running' : 'pending',
|
|
106
|
+
}),
|
|
107
|
+
),
|
|
108
|
+
],
|
|
109
|
+
}),
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
96
113
|
export const Selected: Story = {
|
|
97
114
|
args: {
|
|
98
115
|
run: makeRun({
|
|
@@ -111,23 +128,6 @@ export const Selected: Story = {
|
|
|
111
128
|
},
|
|
112
129
|
};
|
|
113
130
|
|
|
114
|
-
export const LargeWideAndTall: Story = {
|
|
115
|
-
args: {
|
|
116
|
-
run: makeRun({
|
|
117
|
-
jobs: [
|
|
118
|
-
...linearJobs(10),
|
|
119
|
-
...Array.from({length: 10}, (_, index) =>
|
|
120
|
-
makeJob({
|
|
121
|
-
name: `parallel-${String(index + 1).padStart(2, '0')}`,
|
|
122
|
-
position: 20 + index,
|
|
123
|
-
status: index < 2 ? 'running' : 'pending',
|
|
124
|
-
}),
|
|
125
|
-
),
|
|
126
|
-
],
|
|
127
|
-
}),
|
|
128
|
-
},
|
|
129
|
-
};
|
|
130
|
-
|
|
131
131
|
function linearJobs(count: number): Job[] {
|
|
132
132
|
return Array.from({length: count}, (_, index) =>
|
|
133
133
|
makeJob({
|
|
@@ -178,5 +178,9 @@ function makeRun(overrides: Partial<WorkflowRunDetail> = {}): WorkflowRunDetail
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
function makeJob(overrides: Partial<WorkflowRunJobDetailDto> & {name: string}): Job {
|
|
181
|
-
|
|
181
|
+
const job = workflowJob(overrides);
|
|
182
|
+
if (overrides.status === 'running' && job.jobExecutions.length === 0) {
|
|
183
|
+
return workflowJob({...overrides, steps: [workflowStepDto({status: 'running'})]});
|
|
184
|
+
}
|
|
185
|
+
return job;
|
|
182
186
|
}
|
|
@@ -2,7 +2,11 @@ import type {WorkflowRunJobDetailDto} from '@shipfox/api-workflows-dto';
|
|
|
2
2
|
import type {Meta, StoryObj} from '@storybook/react';
|
|
3
3
|
import type {KeyboardEventHandler} from 'react';
|
|
4
4
|
import type {JobExecutionStatus, JobStatus} from '#core/workflow-run.js';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
workflowJob,
|
|
7
|
+
workflowJobExecutionDto,
|
|
8
|
+
workflowStepDto,
|
|
9
|
+
} from '#test/fixtures/workflow-run.js';
|
|
6
10
|
import type {JobGraphNode} from './graph-model.js';
|
|
7
11
|
import {JobNode} from './job-node.js';
|
|
8
12
|
|
|
@@ -16,7 +20,7 @@ const meta = {
|
|
|
16
20
|
parameters: {layout: 'centered'},
|
|
17
21
|
decorators: [
|
|
18
22
|
(Story) => (
|
|
19
|
-
<div className="bg-background-neutral-
|
|
23
|
+
<div className="bg-background-neutral-background p-16">
|
|
20
24
|
<Story />
|
|
21
25
|
</div>
|
|
22
26
|
),
|
|
@@ -183,7 +187,7 @@ export const ListeningProgression: Story = {
|
|
|
183
187
|
<JobNode
|
|
184
188
|
node={makeNode({
|
|
185
189
|
id: 'job-listening-running',
|
|
186
|
-
label: '
|
|
190
|
+
label: 'listening, 0 executions',
|
|
187
191
|
mode: 'listening',
|
|
188
192
|
status: 'running',
|
|
189
193
|
listenerStatus: 'listening',
|
|
@@ -200,7 +204,7 @@ export const ListeningProgression: Story = {
|
|
|
200
204
|
<JobNode
|
|
201
205
|
node={makeNode({
|
|
202
206
|
id: 'job-one-running-execution',
|
|
203
|
-
label: '
|
|
207
|
+
label: 'listening, 1 running execution',
|
|
204
208
|
mode: 'listening',
|
|
205
209
|
status: 'running',
|
|
206
210
|
listenerStatus: 'listening',
|
|
@@ -217,7 +221,7 @@ export const ListeningProgression: Story = {
|
|
|
217
221
|
<JobNode
|
|
218
222
|
node={makeNode({
|
|
219
223
|
id: 'job-mixed-executions',
|
|
220
|
-
label: '
|
|
224
|
+
label: 'listening, mixed executions',
|
|
221
225
|
mode: 'listening',
|
|
222
226
|
status: 'running',
|
|
223
227
|
listenerStatus: 'listening',
|
|
@@ -330,6 +334,7 @@ function makeNode({
|
|
|
330
334
|
queued_at: queuedAt,
|
|
331
335
|
started_at: startedAt,
|
|
332
336
|
finished_at: finishedAt,
|
|
337
|
+
...(status === 'running' ? {steps: [workflowStepDto({status: 'running'})]} : {}),
|
|
333
338
|
}),
|
|
334
339
|
]
|
|
335
340
|
: jobExecutions,
|
|
@@ -359,6 +364,6 @@ function makeExecutionsByStatus(
|
|
|
359
364
|
timed_out_at: null,
|
|
360
365
|
created_at: '2026-06-26T11:54:00.000Z',
|
|
361
366
|
updated_at: '2026-06-26T12:00:00.000Z',
|
|
362
|
-
steps: [],
|
|
367
|
+
steps: status === 'running' ? [workflowStepDto({status: 'running'})] : [],
|
|
363
368
|
}));
|
|
364
369
|
}
|
|
@@ -2,14 +2,18 @@ import type {WorkflowRunJobDetailDto} from '@shipfox/api-workflows-dto';
|
|
|
2
2
|
import {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';
|
|
3
3
|
import {act, render, screen, within} from '@testing-library/react';
|
|
4
4
|
import userEvent from '@testing-library/user-event';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
workflowJob,
|
|
7
|
+
workflowJobExecutionDto,
|
|
8
|
+
workflowStepDto,
|
|
9
|
+
} from '#test/fixtures/workflow-run.js';
|
|
6
10
|
import type {JobGraphNode} from './graph-model.js';
|
|
7
11
|
import {JobNode} from './job-node.js';
|
|
8
12
|
|
|
9
13
|
const NOW = Date.parse('2026-06-26T12:00:00.000Z');
|
|
10
14
|
|
|
11
15
|
type NodeOverrides = Omit<Partial<WorkflowRunJobDetailDto>, 'job_executions'> & {
|
|
12
|
-
name
|
|
16
|
+
name?: string | null;
|
|
13
17
|
job_executions?: WorkflowRunJobDetailDto['job_executions'];
|
|
14
18
|
queued_at?: string | null;
|
|
15
19
|
started_at?: string | null;
|
|
@@ -32,6 +36,9 @@ function makeNode(overrides: NodeOverrides): JobGraphNode {
|
|
|
32
36
|
queued_at: queued_at ?? null,
|
|
33
37
|
started_at: started_at ?? null,
|
|
34
38
|
finished_at: finished_at ?? null,
|
|
39
|
+
...(jobOverrides.status === 'running'
|
|
40
|
+
? {steps: [workflowStepDto({status: 'running'})]}
|
|
41
|
+
: {}),
|
|
35
42
|
}),
|
|
36
43
|
];
|
|
37
44
|
} else if (job_executions !== undefined) {
|
|
@@ -45,11 +52,14 @@ function makeNode(overrides: NodeOverrides): JobGraphNode {
|
|
|
45
52
|
});
|
|
46
53
|
}
|
|
47
54
|
|
|
48
|
-
function renderNode(
|
|
55
|
+
function renderNode(
|
|
56
|
+
node: JobGraphNode,
|
|
57
|
+
{live = false, selected = false}: {live?: boolean; selected?: boolean} = {},
|
|
58
|
+
) {
|
|
49
59
|
const element = (
|
|
50
60
|
<JobNode
|
|
51
61
|
node={node}
|
|
52
|
-
selected={
|
|
62
|
+
selected={selected}
|
|
53
63
|
onSelect={() => undefined}
|
|
54
64
|
onKeyDown={() => undefined}
|
|
55
65
|
onHoverStart={() => undefined}
|
|
@@ -84,6 +94,28 @@ function setVisibility(state: 'visible' | 'hidden') {
|
|
|
84
94
|
Object.defineProperty(document, 'visibilityState', {configurable: true, value: state});
|
|
85
95
|
}
|
|
86
96
|
|
|
97
|
+
describe('JobNode interaction', () => {
|
|
98
|
+
test('presents the selectable node as an elevated interactive surface', () => {
|
|
99
|
+
renderNode(makeNode({name: 'build', status: 'pending'}));
|
|
100
|
+
|
|
101
|
+
expect(screen.getByRole('button', {name: 'build, Pending'})).toHaveClass(
|
|
102
|
+
'cursor-pointer',
|
|
103
|
+
'bg-background-neutral-base',
|
|
104
|
+
'shadow-button-neutral',
|
|
105
|
+
'hover:bg-background-components-hover',
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('uses a quiet neutral surface for the selected node', () => {
|
|
110
|
+
renderNode(makeNode({name: 'build', status: 'pending'}), {selected: true});
|
|
111
|
+
|
|
112
|
+
expect(screen.getByRole('button', {name: 'build, Pending'})).toHaveClass(
|
|
113
|
+
'bg-background-components-hover',
|
|
114
|
+
'hover:bg-background-components-pressed',
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
87
119
|
describe('JobNode duration', () => {
|
|
88
120
|
beforeEach(() => {
|
|
89
121
|
setMatchMedia(false);
|
|
@@ -200,7 +232,7 @@ describe('JobNode duration', () => {
|
|
|
200
232
|
renderNode(node);
|
|
201
233
|
|
|
202
234
|
expect(screen.queryByText('2m 14s')).not.toBeInTheDocument();
|
|
203
|
-
expect(screen.getByRole('button', {name: 'deploy-window,
|
|
235
|
+
expect(screen.getByRole('button', {name: 'deploy-window, Listening'})).toBeInTheDocument();
|
|
204
236
|
});
|
|
205
237
|
});
|
|
206
238
|
|
|
@@ -227,12 +259,12 @@ describe('JobNode listening indicator', () => {
|
|
|
227
259
|
|
|
228
260
|
renderNode(node);
|
|
229
261
|
|
|
230
|
-
expect(screen.getByRole('button', {name: 'deploy-window,
|
|
262
|
+
expect(screen.getByRole('button', {name: 'deploy-window, Listening'})).toBeInTheDocument();
|
|
231
263
|
expect(screen.queryByLabelText('Waiting for events to start job')).not.toBeInTheDocument();
|
|
232
264
|
|
|
233
|
-
await user.hover(screen.getByRole('img', {name: '
|
|
265
|
+
await user.hover(screen.getByRole('img', {name: 'Listening'}));
|
|
234
266
|
|
|
235
|
-
expect(await screen.findByRole('tooltip')).toHaveTextContent('
|
|
267
|
+
expect(await screen.findByRole('tooltip')).toHaveTextContent('Listening');
|
|
236
268
|
});
|
|
237
269
|
|
|
238
270
|
test('does not show the listener icon before the listener is armed or after it resolves', () => {
|
|
@@ -277,6 +309,12 @@ describe('JobNode status indicator', () => {
|
|
|
277
309
|
const node = makeNode({
|
|
278
310
|
name: 'deploy',
|
|
279
311
|
status: 'running',
|
|
312
|
+
job_executions: [
|
|
313
|
+
workflowJobExecutionDto({
|
|
314
|
+
status: 'running',
|
|
315
|
+
steps: [workflowStepDto({status: 'running'})],
|
|
316
|
+
}),
|
|
317
|
+
],
|
|
280
318
|
});
|
|
281
319
|
|
|
282
320
|
renderNode(node);
|
|
@@ -286,7 +324,15 @@ describe('JobNode status indicator', () => {
|
|
|
286
324
|
expect(await screen.findByRole('tooltip')).toHaveTextContent('Running');
|
|
287
325
|
});
|
|
288
326
|
|
|
289
|
-
test('
|
|
327
|
+
test('uses the job key when the static name is missing', () => {
|
|
328
|
+
const node = makeNode({key: 'deploy-prod', name: null, status: 'succeeded'});
|
|
329
|
+
|
|
330
|
+
renderNode(node);
|
|
331
|
+
|
|
332
|
+
expect(screen.getByRole('button', {name: 'deploy-prod, Succeeded'})).toBeInTheDocument();
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test('shows a running lifecycle without a running step as pending', () => {
|
|
290
336
|
const node = makeNode({
|
|
291
337
|
name: 'deploy',
|
|
292
338
|
status: 'pending',
|
|
@@ -295,6 +341,23 @@ describe('JobNode status indicator', () => {
|
|
|
295
341
|
|
|
296
342
|
renderNode(node);
|
|
297
343
|
|
|
344
|
+
expect(screen.getByRole('button', {name: 'deploy, Pending'})).toBeInTheDocument();
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test('shows a pending lifecycle as running while a step is active', () => {
|
|
348
|
+
const node = makeNode({
|
|
349
|
+
name: 'deploy',
|
|
350
|
+
status: 'pending',
|
|
351
|
+
job_executions: [
|
|
352
|
+
workflowJobExecutionDto({
|
|
353
|
+
status: 'pending',
|
|
354
|
+
steps: [workflowStepDto({status: 'running'})],
|
|
355
|
+
}),
|
|
356
|
+
],
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
renderNode(node);
|
|
360
|
+
|
|
298
361
|
expect(screen.getByRole('button', {name: 'deploy, Running'})).toBeInTheDocument();
|
|
299
362
|
});
|
|
300
363
|
});
|
|
@@ -317,10 +380,17 @@ describe('JobNode execution count indicator', () => {
|
|
|
317
380
|
name: 'release-gates',
|
|
318
381
|
mode: 'listening',
|
|
319
382
|
status: 'running',
|
|
383
|
+
listener_status: 'listening',
|
|
320
384
|
job_executions: [
|
|
321
385
|
workflowJobExecutionDto({status: 'pending'}),
|
|
322
|
-
workflowJobExecutionDto({
|
|
323
|
-
|
|
386
|
+
workflowJobExecutionDto({
|
|
387
|
+
status: 'running',
|
|
388
|
+
steps: [workflowStepDto({status: 'running'})],
|
|
389
|
+
}),
|
|
390
|
+
workflowJobExecutionDto({
|
|
391
|
+
status: 'running',
|
|
392
|
+
steps: [workflowStepDto({status: 'running'})],
|
|
393
|
+
}),
|
|
324
394
|
workflowJobExecutionDto({status: 'succeeded'}),
|
|
325
395
|
workflowJobExecutionDto({status: 'succeeded'}),
|
|
326
396
|
workflowJobExecutionDto({status: 'succeeded'}),
|
|
@@ -331,7 +401,7 @@ describe('JobNode execution count indicator', () => {
|
|
|
331
401
|
|
|
332
402
|
renderNode(node);
|
|
333
403
|
|
|
334
|
-
const button = screen.getByRole('button', {name: 'release-gates,
|
|
404
|
+
const button = screen.getByRole('button', {name: 'release-gates, Listening, 8 executions'});
|
|
335
405
|
expect(within(button).getByText('8')).toBeInTheDocument();
|
|
336
406
|
expect(within(button).queryByText('8 exec')).not.toBeInTheDocument();
|
|
337
407
|
expect(button.querySelector('[data-execution-status-segment]')).not.toBeInTheDocument();
|
|
@@ -9,7 +9,14 @@ import {cn} from '@shipfox/react-ui/utils';
|
|
|
9
9
|
import type {KeyboardEventHandler, Ref} from 'react';
|
|
10
10
|
import {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';
|
|
11
11
|
import {WorkflowStatusIcon} from '#components/workflow-status/workflow-status-icon.js';
|
|
12
|
-
import
|
|
12
|
+
import {
|
|
13
|
+
defaultJobExecution,
|
|
14
|
+
deriveJobDisplayStatus,
|
|
15
|
+
deriveJobExecutionDisplayStatus,
|
|
16
|
+
type JobExecution,
|
|
17
|
+
type JobExecutionStatus,
|
|
18
|
+
type WorkflowRunDetail,
|
|
19
|
+
} from '#core/workflow-run.js';
|
|
13
20
|
import type {JobGraphNode} from './graph-model.js';
|
|
14
21
|
import {formatJobDurationAccessibleLabel} from './job-duration-format.js';
|
|
15
22
|
import {JobDurationLabel} from './job-duration-label.js';
|
|
@@ -33,14 +40,14 @@ export function TriggerNode({
|
|
|
33
40
|
<button
|
|
34
41
|
type="button"
|
|
35
42
|
aria-label={label}
|
|
36
|
-
className="flex items-center justify-center rounded-full
|
|
43
|
+
className="flex cursor-help items-center justify-center rounded-full bg-background-neutral-base shadow-button-neutral transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none"
|
|
37
44
|
style={{width: TRIGGER_SIZE, height: TRIGGER_SIZE}}
|
|
38
45
|
>
|
|
39
46
|
<TriggerSourceIcon
|
|
40
47
|
provider={trigger.triggerProvider}
|
|
41
48
|
source={trigger.triggerSource}
|
|
42
49
|
aria-hidden
|
|
43
|
-
className="size-14 shrink-0 text-foreground-neutral-
|
|
50
|
+
className="size-14 shrink-0 text-foreground-neutral-subtle"
|
|
44
51
|
/>
|
|
45
52
|
</button>
|
|
46
53
|
</TooltipTrigger>
|
|
@@ -69,16 +76,16 @@ export function JobNode({
|
|
|
69
76
|
ref?: Ref<HTMLButtonElement>;
|
|
70
77
|
}) {
|
|
71
78
|
useTimeTick();
|
|
72
|
-
const status =
|
|
73
|
-
|
|
74
|
-
node.jobExecutions.some((jobExecution) => jobExecution.status === 'running')
|
|
75
|
-
? 'running'
|
|
76
|
-
: node.status;
|
|
79
|
+
const status = deriveJobDisplayStatus(node);
|
|
80
|
+
const execution = defaultJobExecution(node);
|
|
77
81
|
const visual = getWorkflowStatusVisual(status);
|
|
78
82
|
const accessibleLabel = [
|
|
79
83
|
node.displayName,
|
|
80
84
|
visual.label,
|
|
81
|
-
formatJobDurationAccessibleLabel(
|
|
85
|
+
formatJobDurationAccessibleLabel(
|
|
86
|
+
node.displayDuration,
|
|
87
|
+
execution ? deriveJobExecutionDisplayStatus(execution) : undefined,
|
|
88
|
+
),
|
|
82
89
|
node.executionCountVisible
|
|
83
90
|
? executionCountAccessibleLabel(node.jobExecutions.length)
|
|
84
91
|
: undefined,
|
|
@@ -99,19 +106,19 @@ export function JobNode({
|
|
|
99
106
|
onPointerEnter={onHoverStart}
|
|
100
107
|
onPointerLeave={onHoverEnd}
|
|
101
108
|
className={cn(
|
|
102
|
-
'group relative flex h-48 w-208 items-center gap-8 rounded-8
|
|
103
|
-
selected && 'bg-background-components-hover',
|
|
109
|
+
'group relative flex h-48 w-208 cursor-pointer items-center gap-8 rounded-8 bg-background-neutral-base px-10 text-left shadow-button-neutral transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus focus-visible:outline-none',
|
|
110
|
+
selected && 'bg-background-components-hover hover:bg-background-components-pressed',
|
|
104
111
|
node.carriedOver && 'opacity-[0.55]',
|
|
105
112
|
)}
|
|
106
113
|
>
|
|
107
114
|
{selected ? (
|
|
108
115
|
<span
|
|
109
116
|
aria-hidden="true"
|
|
110
|
-
className="absolute inset-y-6 left-0 w-
|
|
117
|
+
className="absolute inset-y-6 left-0 w-px rounded-full bg-border-highlights-interactive"
|
|
111
118
|
/>
|
|
112
119
|
) : null}
|
|
113
120
|
<div className="flex min-w-0 flex-1 items-center gap-8">
|
|
114
|
-
<WorkflowStatusIcon status={status}
|
|
121
|
+
<WorkflowStatusIcon status={status} size={14} />
|
|
115
122
|
<JobLabel label={node.displayName} />
|
|
116
123
|
</div>
|
|
117
124
|
<JobDurationLabel duration={node.displayDuration} />
|
|
@@ -129,7 +136,7 @@ function ExecutionCountText({executions}: {executions: JobExecution[]}) {
|
|
|
129
136
|
<TooltipTrigger asChild>
|
|
130
137
|
<span
|
|
131
138
|
aria-hidden="true"
|
|
132
|
-
className="inline-flex h-20 min-w-28 shrink-0 items-center justify-end gap-4 text-foreground-neutral-
|
|
139
|
+
className="inline-flex h-20 min-w-28 shrink-0 items-center justify-end gap-4 text-foreground-neutral-subtle"
|
|
133
140
|
>
|
|
134
141
|
<Icon name="loopRightLine" className="size-12" />
|
|
135
142
|
<Code as="span" variant="label" className="text-current">
|
|
@@ -199,7 +206,7 @@ const EXECUTION_STATUSES = [
|
|
|
199
206
|
function executionStatusCounts(executions: JobExecution[]) {
|
|
200
207
|
return executions.reduce(
|
|
201
208
|
(counts, execution) => {
|
|
202
|
-
counts[execution
|
|
209
|
+
counts[deriveJobExecutionDisplayStatus(execution)] += 1;
|
|
203
210
|
return counts;
|
|
204
211
|
},
|
|
205
212
|
{pending: 0, running: 0, succeeded: 0, failed: 0, cancelled: 0},
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type {WorkflowRunDetail} from '#core/workflow-run.js';
|
|
2
2
|
|
|
3
|
+
export type JobGraphSelectionSource = 'pointer' | 'keyboard';
|
|
4
|
+
|
|
3
5
|
export interface JobGraphProps {
|
|
4
6
|
run: WorkflowRunDetail;
|
|
5
7
|
selectedJobId?: string | undefined;
|
|
6
8
|
defaultSelectedJobId?: string | undefined;
|
|
7
|
-
onSelectedJobChange?:
|
|
9
|
+
onSelectedJobChange?:
|
|
10
|
+
| ((jobId: string | undefined, source?: JobGraphSelectionSource) => void)
|
|
11
|
+
| undefined;
|
|
8
12
|
className?: string | undefined;
|
|
9
13
|
}
|