@shipfox/client-workflows 12.0.2 → 13.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/preview.tsx +3 -3
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +56 -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-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 +5 -4
- package/dist/components/job-graph/job-node.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-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 +8 -7
- 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 +3 -3
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +16 -7
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- 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-view.d.ts +3 -2
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +15 -11
- package/dist/components/workflow-run-view/workflow-run-view.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/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.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 +30 -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 +61 -5
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +150 -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-graph-view.test.tsx +19 -8
- package/src/components/job-graph/job-graph.stories.tsx +23 -19
- package/src/components/job-graph/job-node.stories.tsx +10 -5
- package/src/components/job-graph/job-node.test.tsx +55 -10
- package/src/components/job-graph/job-node.tsx +16 -9
- 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-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 +13 -12
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +6 -8
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +27 -2
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +19 -9
- 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-view.test.tsx +88 -30
- package/src/components/workflow-run-view/workflow-run-view.tsx +24 -14
- 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/workflow-run.ts +76 -7
- 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 +45 -0
- package/src/pages/workflow-run-list-page.tsx +65 -0
- package/src/pages/{workflow-run-page.test.tsx → workflow-run-pages.test.tsx} +211 -47
- package/src/routes/inputs.test.ts +193 -12
- package/src/routes/inputs.ts +170 -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/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/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.tsx +0 -94
|
@@ -1,73 +1,103 @@
|
|
|
1
1
|
import {TimeTickerProvider} from '@shipfox/react-ui/time-ticker';
|
|
2
|
+
import {Header} from '@shipfox/react-ui/typography';
|
|
2
3
|
import {cn} from '@shipfox/react-ui/utils';
|
|
3
|
-
import {useState} from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import {useId, useMemo, useState} from 'react';
|
|
5
|
+
import {
|
|
6
|
+
applyWorkflowRunFilterPatch,
|
|
7
|
+
clearWorkflowRunFilters,
|
|
8
|
+
hasWorkflowRunFilters,
|
|
9
|
+
type WorkflowRunFilterPatch,
|
|
10
|
+
type WorkflowRunsSearch,
|
|
11
|
+
} from '#routes/inputs.js';
|
|
12
|
+
import {runMatchesFilters, workflowRunFacets} from './run-display.js';
|
|
5
13
|
import type {WorkflowRunListViewProps} from './types.js';
|
|
14
|
+
import {WorkflowRunFilters} from './workflow-run-filters.js';
|
|
6
15
|
import {WorkflowRunListContent} from './workflow-run-list-content.js';
|
|
7
|
-
|
|
16
|
+
|
|
17
|
+
const EMPTY_SEARCH: WorkflowRunsSearch = {};
|
|
8
18
|
|
|
9
19
|
export function WorkflowRunListView({
|
|
10
20
|
runs,
|
|
11
21
|
query,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
selectedWorkflowRunId,
|
|
22
|
+
workspaceSlug,
|
|
23
|
+
projectSlug,
|
|
15
24
|
className,
|
|
16
|
-
search =
|
|
17
|
-
statusFilter = 'all',
|
|
25
|
+
search = EMPTY_SEARCH,
|
|
18
26
|
onFiltersChange,
|
|
27
|
+
onClearFilters,
|
|
28
|
+
hasNextPage = false,
|
|
29
|
+
isFetchingNextPage = false,
|
|
30
|
+
isFetchNextPageError = false,
|
|
31
|
+
onLoadMore,
|
|
19
32
|
}: WorkflowRunListViewProps) {
|
|
20
|
-
const
|
|
21
|
-
const [
|
|
33
|
+
const headingId = useId();
|
|
34
|
+
const [localSearch, setLocalSearch] = useState<WorkflowRunsSearch>(search);
|
|
22
35
|
const currentSearch = onFiltersChange ? search : localSearch;
|
|
23
|
-
const currentStatusFilter = onFiltersChange ? statusFilter : localStatusFilter;
|
|
24
36
|
|
|
25
|
-
const filteredRuns =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
const filteredRuns = useMemo(
|
|
38
|
+
() => runs.filter((run) => runMatchesFilters(run, currentSearch)),
|
|
39
|
+
[runs, currentSearch],
|
|
40
|
+
);
|
|
41
|
+
const facets = useMemo(() => workflowRunFacets(runs, currentSearch), [runs, currentSearch]);
|
|
42
|
+
const hasActiveFilters = hasWorkflowRunFilters(currentSearch);
|
|
43
|
+
|
|
44
|
+
function handleFiltersChange(patch: WorkflowRunFilterPatch) {
|
|
45
|
+
if (onFiltersChange) onFiltersChange(patch);
|
|
46
|
+
else setLocalSearch((previous) => applyWorkflowRunFilterPatch(previous, patch));
|
|
47
|
+
}
|
|
29
48
|
|
|
30
49
|
function handleClearFilters() {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
// Uncontrolled, the view owns the filters, so it clears them itself and still reports the
|
|
51
|
+
// change; a caller that passes only `onClearFilters` would otherwise see nothing happen.
|
|
52
|
+
if (!onFiltersChange) setLocalSearch(clearWorkflowRunFilters);
|
|
53
|
+
else if (!onClearFilters) onFiltersChange(CLEAR_ALL_FILTERS);
|
|
54
|
+
onClearFilters?.();
|
|
36
55
|
}
|
|
37
56
|
|
|
38
57
|
return (
|
|
39
58
|
<TimeTickerProvider intervalMs={1000} reducedMotionIntervalMs={10_000}>
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
className,
|
|
44
|
-
)}
|
|
45
|
-
aria-label="Workflow runs"
|
|
59
|
+
<section
|
|
60
|
+
aria-labelledby={headingId}
|
|
61
|
+
className={cn('flex min-h-0 min-w-0 flex-1 flex-col gap-12', className)}
|
|
46
62
|
>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}}
|
|
63
|
+
{/* The tab strip above already reads "Runs", so the page heading is for structure and
|
|
64
|
+
assistive tech rather than a second visible title competing with it. */}
|
|
65
|
+
<Header id={headingId} variant="h1" className="sr-only">
|
|
66
|
+
Workflow runs
|
|
67
|
+
</Header>
|
|
68
|
+
|
|
69
|
+
<WorkflowRunFilters
|
|
70
|
+
search={currentSearch}
|
|
71
|
+
facets={facets}
|
|
72
|
+
onChange={handleFiltersChange}
|
|
73
|
+
onClear={handleClearFilters}
|
|
74
|
+
hasActiveFilters={hasActiveFilters}
|
|
60
75
|
/>
|
|
76
|
+
|
|
61
77
|
<WorkflowRunListContent
|
|
62
78
|
query={query}
|
|
63
79
|
totalRuns={runs.length}
|
|
64
80
|
runs={filteredRuns}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
workspaceSlug={workspaceSlug}
|
|
82
|
+
projectSlug={projectSlug}
|
|
83
|
+
hasActiveFilters={hasActiveFilters}
|
|
68
84
|
onClearFilters={handleClearFilters}
|
|
85
|
+
hasNextPage={hasNextPage}
|
|
86
|
+
isFetchingNextPage={isFetchingNextPage}
|
|
87
|
+
isFetchNextPageError={isFetchNextPageError}
|
|
88
|
+
{...(onLoadMore ? {onLoadMore} : {})}
|
|
69
89
|
/>
|
|
70
|
-
</
|
|
90
|
+
</section>
|
|
71
91
|
</TimeTickerProvider>
|
|
72
92
|
);
|
|
73
93
|
}
|
|
94
|
+
|
|
95
|
+
const CLEAR_ALL_FILTERS: WorkflowRunFilterPatch = {
|
|
96
|
+
search: undefined,
|
|
97
|
+
status: undefined,
|
|
98
|
+
branch: undefined,
|
|
99
|
+
actor: undefined,
|
|
100
|
+
event: undefined,
|
|
101
|
+
after: undefined,
|
|
102
|
+
before: undefined,
|
|
103
|
+
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type {JobStatusDto} from '@shipfox/api-workflows-dto';
|
|
1
2
|
import {Code} from '@shipfox/react-ui/typography';
|
|
2
3
|
import type {Meta, StoryObj} from '@storybook/react';
|
|
3
4
|
import type {ReactNode} from 'react';
|
|
4
5
|
import {expect, userEvent, within} from 'storybook/test';
|
|
5
6
|
import type {WorkflowRunListItem, WorkflowRunStatus} from '#core/workflow-run.js';
|
|
6
|
-
import {sequencedWorkflowRunListItem} from '#test/fixtures/workflow-run.js';
|
|
7
|
+
import {sequencedWorkflowRunListItem, workflowRunJobsFixture} from '#test/fixtures/workflow-run.js';
|
|
7
8
|
import type {WorkflowRunListQuery} from './types.js';
|
|
8
9
|
import {WorkflowRunListView} from './workflow-run-list-view.js';
|
|
9
10
|
|
|
@@ -21,30 +22,68 @@ function makeQuery(overrides: Partial<WorkflowRunListQuery> = {}): WorkflowRunLi
|
|
|
21
22
|
};
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
const JOB_STRIP_NAME = /jobs:/u;
|
|
26
|
+
const STATUS_FILTER = /^Status\b.*filter$/u;
|
|
27
|
+
|
|
28
|
+
let commitSequence = 0;
|
|
29
|
+
|
|
30
|
+
// Distinct, plausible SHAs: a story that repeats one commit down the list reads as a bug in
|
|
31
|
+
// the row rather than as sample data.
|
|
32
|
+
function nextCommit(): string {
|
|
33
|
+
commitSequence += 1;
|
|
34
|
+
return (commitSequence * 0x9e3779b1).toString(16).padStart(8, '0').repeat(5).slice(0, 40);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function makeRun(
|
|
38
|
+
status: WorkflowRunStatus,
|
|
39
|
+
name: string,
|
|
40
|
+
minutesAgo: number,
|
|
41
|
+
{
|
|
42
|
+
ref = 'refs/heads/main',
|
|
43
|
+
actor = 'octocat',
|
|
44
|
+
jobs = [],
|
|
45
|
+
}: {ref?: string; actor?: string; jobs?: JobStatusDto[]} = {},
|
|
46
|
+
): WorkflowRunListItem {
|
|
47
|
+
return sequencedWorkflowRunListItem(status, name, minutesAgo, {
|
|
48
|
+
workflow_name: name,
|
|
49
|
+
trigger_reference: {repository: 'acme/checkout-api', ref, commit: nextCommit(), actor},
|
|
50
|
+
...workflowRunJobsFixture(jobs),
|
|
51
|
+
});
|
|
26
52
|
}
|
|
27
53
|
|
|
28
54
|
const SAMPLE_RUNS: WorkflowRunListItem[] = [
|
|
29
|
-
makeRun('running', 'deploy-web', 1
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
makeRun('
|
|
34
|
-
|
|
55
|
+
makeRun('running', 'deploy-web', 1, {
|
|
56
|
+
ref: 'refs/pull/482/head',
|
|
57
|
+
jobs: ['succeeded', 'succeeded', 'running', 'pending'],
|
|
58
|
+
}),
|
|
59
|
+
makeRun('failed', 'integration-tests', 4, {
|
|
60
|
+
ref: 'refs/heads/release/v2',
|
|
61
|
+
actor: 'hubot',
|
|
62
|
+
jobs: ['succeeded', 'failed', 'skipped', 'skipped'],
|
|
63
|
+
}),
|
|
64
|
+
makeRun('succeeded', 'build-image', 12, {jobs: ['succeeded', 'succeeded']}),
|
|
65
|
+
makeRun('cancelled', 'lint-and-type', 38, {
|
|
66
|
+
actor: 'dependabot',
|
|
67
|
+
jobs: ['succeeded', 'cancelled'],
|
|
68
|
+
}),
|
|
69
|
+
makeRun('pending', 'release-prod', 2),
|
|
70
|
+
makeRun('succeeded', 'release-production-multi-region-with-canary-and-smoke-tests', 95, {
|
|
71
|
+
ref: 'refs/tags/v2.14.0',
|
|
72
|
+
jobs: ['succeeded', 'succeeded', 'succeeded', 'succeeded', 'succeeded'],
|
|
73
|
+
}),
|
|
35
74
|
];
|
|
36
75
|
|
|
37
|
-
// One story on the full
|
|
38
|
-
// driven by args; search,
|
|
39
|
-
//
|
|
40
|
-
// real height so `flex-1`
|
|
76
|
+
// One story on the full-width page. The data states (loading / empty / errors / runs) are
|
|
77
|
+
// driven by args; search, the filter menus, "clear filters" and "no matches" are live in the
|
|
78
|
+
// rendered toolbar since the view owns that state. The decorator gives the list the 1120px
|
|
79
|
+
// content cap and a real height, so `flex-1` scrolls the way it does in the app shell.
|
|
41
80
|
const meta = {
|
|
42
81
|
title: 'Workflows/WorkflowRunList',
|
|
43
82
|
component: WorkflowRunListView,
|
|
44
83
|
parameters: {layout: 'centered'},
|
|
45
84
|
decorators: [
|
|
46
85
|
(Story) => (
|
|
47
|
-
<div className="flex h-600">
|
|
86
|
+
<div className="flex h-600 w-[1120px] max-w-full bg-background-neutral-base p-24">
|
|
48
87
|
<Story />
|
|
49
88
|
</div>
|
|
50
89
|
),
|
|
@@ -52,8 +91,8 @@ const meta = {
|
|
|
52
91
|
args: {
|
|
53
92
|
runs: SAMPLE_RUNS,
|
|
54
93
|
query: makeQuery(),
|
|
55
|
-
|
|
56
|
-
|
|
94
|
+
workspaceSlug: 'acme',
|
|
95
|
+
projectSlug: 'checkout-api',
|
|
57
96
|
},
|
|
58
97
|
} satisfies Meta<typeof WorkflowRunListView>;
|
|
59
98
|
|
|
@@ -62,8 +101,69 @@ type Story = StoryObj<typeof meta>;
|
|
|
62
101
|
|
|
63
102
|
export const Playground: Story = {};
|
|
64
103
|
|
|
65
|
-
|
|
66
|
-
|
|
104
|
+
/**
|
|
105
|
+
* The row under its one-line threshold, at the 720px a 768px viewport leaves the column.
|
|
106
|
+
*
|
|
107
|
+
* The row's breakpoints are container queries, so narrowing the wrapper is enough to reach
|
|
108
|
+
* this state: identity and numerics hold line one, provenance drops beneath, and the actor
|
|
109
|
+
* and job strip give up their columns. The filter row still renders inline because the sheet
|
|
110
|
+
* it collapses into is keyed to the viewport, not the container.
|
|
111
|
+
*/
|
|
112
|
+
export const NarrowLayout: Story = {
|
|
113
|
+
decorators: [
|
|
114
|
+
(Story) => (
|
|
115
|
+
<div className="flex w-[720px] max-w-full">
|
|
116
|
+
<Story />
|
|
117
|
+
</div>
|
|
118
|
+
),
|
|
119
|
+
],
|
|
120
|
+
play: async ({canvasElement}) => {
|
|
121
|
+
const canvas = within(canvasElement);
|
|
122
|
+
|
|
123
|
+
await expect(await canvas.findByText('deploy-web')).toBeVisible();
|
|
124
|
+
// The branch stays on the provenance line; the actor is the column that gives way. It is
|
|
125
|
+
// still in the DOM, so visibility is what distinguishes "dropped for width" from
|
|
126
|
+
// "never rendered". `dependabot` is the one actor a single run carries.
|
|
127
|
+
await expect(canvas.getByText('release/v2')).toBeVisible();
|
|
128
|
+
await expect(canvas.getByText('dependabot')).not.toBeVisible();
|
|
129
|
+
await expect(canvas.queryByRole('img', {name: JOB_STRIP_NAME})).not.toBeInTheDocument();
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Row density against the three strip sizes the threshold has to survive: comfortably under
|
|
135
|
+
* it, comfortably over it, and far enough over that the overflow count carries the failure.
|
|
136
|
+
*/
|
|
137
|
+
export const JobStripDensity: Story = {
|
|
138
|
+
args: {
|
|
139
|
+
runs: [
|
|
140
|
+
makeRun('succeeded', 'two-jobs', 2, {jobs: ['succeeded', 'succeeded']}),
|
|
141
|
+
makeRun('failed', 'twelve-jobs', 6, {
|
|
142
|
+
jobs: [
|
|
143
|
+
'succeeded',
|
|
144
|
+
'succeeded',
|
|
145
|
+
'succeeded',
|
|
146
|
+
'failed',
|
|
147
|
+
'succeeded',
|
|
148
|
+
'succeeded',
|
|
149
|
+
'skipped',
|
|
150
|
+
'skipped',
|
|
151
|
+
'succeeded',
|
|
152
|
+
'succeeded',
|
|
153
|
+
'succeeded',
|
|
154
|
+
'succeeded',
|
|
155
|
+
],
|
|
156
|
+
}),
|
|
157
|
+
makeRun('running', 'forty-jobs', 9, {
|
|
158
|
+
jobs: [
|
|
159
|
+
...(Array.from({length: 8}, () => 'succeeded') as JobStatusDto[]),
|
|
160
|
+
...(Array.from({length: 4}, () => 'running') as JobStatusDto[]),
|
|
161
|
+
'failed',
|
|
162
|
+
...(Array.from({length: 27}, () => 'pending') as JobStatusDto[]),
|
|
163
|
+
],
|
|
164
|
+
}),
|
|
165
|
+
],
|
|
166
|
+
},
|
|
67
167
|
};
|
|
68
168
|
|
|
69
169
|
export const DataStates: Story = {
|
|
@@ -94,16 +194,36 @@ export const TestNoMatches: Story = {
|
|
|
94
194
|
const canvas = within(canvasElement);
|
|
95
195
|
await userEvent.type(canvas.getByLabelText('Search runs'), 'no-such-run');
|
|
96
196
|
await expect(await canvas.findByText('No matching runs')).toBeInTheDocument();
|
|
197
|
+
await expect(canvas.getByRole('button', {name: 'Show all runs'})).toBeInTheDocument();
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export const TestMultiSelectFilter: Story = {
|
|
202
|
+
play: async ({canvasElement}) => {
|
|
203
|
+
const canvas = within(canvasElement);
|
|
204
|
+
await userEvent.click(canvas.getByRole('button', {name: STATUS_FILTER}));
|
|
205
|
+
const menu = await within(document.body).findByRole('menu');
|
|
206
|
+
await userEvent.click(within(menu).getByRole('menuitemcheckbox', {name: 'Failed'}));
|
|
207
|
+
await userEvent.click(within(menu).getByRole('menuitemcheckbox', {name: 'Cancelled'}));
|
|
208
|
+
await userEvent.keyboard('{Escape}');
|
|
209
|
+
|
|
210
|
+
// Radix keeps the rest of the document `aria-hidden` until the menu finishes closing, so
|
|
211
|
+
// the trigger is queried with a retrying matcher rather than read on the next tick.
|
|
212
|
+
await expect(await canvas.findByRole('button', {name: STATUS_FILTER})).toHaveTextContent(
|
|
213
|
+
'Status · 2',
|
|
214
|
+
);
|
|
215
|
+
await expect(canvas.getByText('integration-tests')).toBeInTheDocument();
|
|
216
|
+
await expect(canvas.queryByText('build-image')).not.toBeInTheDocument();
|
|
97
217
|
},
|
|
98
218
|
};
|
|
99
219
|
|
|
100
220
|
function StateExample({label, children}: {label: string; children: ReactNode}) {
|
|
101
221
|
return (
|
|
102
|
-
<div className="flex w-
|
|
222
|
+
<div className="flex min-w-0 flex-col gap-8">
|
|
103
223
|
<Code variant="label" className="text-foreground-neutral-subtle">
|
|
104
224
|
{label}
|
|
105
225
|
</Code>
|
|
106
|
-
<div className="flex h-560 rounded-8 border border-border-neutral-base bg-background-neutral-base">
|
|
226
|
+
<div className="flex h-560 rounded-8 border border-border-neutral-base bg-background-neutral-base p-16">
|
|
107
227
|
{children}
|
|
108
228
|
</div>
|
|
109
229
|
</div>
|
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
import {useMemo} from 'react';
|
|
1
|
+
import {useCallback, useMemo} from 'react';
|
|
2
2
|
import type {WorkflowRunListItem} from '#core/workflow-run.js';
|
|
3
3
|
import {useWorkflowRunsInfiniteQuery} from '#hooks/api/workflow-runs.js';
|
|
4
4
|
import type {WorkflowRunListProps} from './types.js';
|
|
5
5
|
import {WorkflowRunListView} from './workflow-run-list-view.js';
|
|
6
6
|
|
|
7
7
|
export function WorkflowRunList({
|
|
8
|
-
workspaceId,
|
|
9
8
|
projectId,
|
|
10
|
-
|
|
9
|
+
workspaceSlug,
|
|
10
|
+
projectSlug,
|
|
11
11
|
className,
|
|
12
|
-
search
|
|
13
|
-
statusFilter = 'all',
|
|
12
|
+
search,
|
|
14
13
|
onFiltersChange,
|
|
14
|
+
onClearFilters,
|
|
15
|
+
hasNextPage,
|
|
16
|
+
isFetchingNextPage,
|
|
17
|
+
isFetchNextPageError,
|
|
18
|
+
onLoadMore,
|
|
15
19
|
}: WorkflowRunListProps) {
|
|
16
20
|
const query = useWorkflowRunsInfiniteQuery(projectId, {});
|
|
21
|
+
const handleLoadMore = useCallback(() => {
|
|
22
|
+
void query.fetchNextPage();
|
|
23
|
+
}, [query.fetchNextPage]);
|
|
17
24
|
const runs = useMemo<WorkflowRunListItem[]>(
|
|
18
25
|
() => query.data?.pages.flatMap((page) => page.runs) ?? [],
|
|
19
26
|
[query.data],
|
|
@@ -23,13 +30,16 @@ export function WorkflowRunList({
|
|
|
23
30
|
<WorkflowRunListView
|
|
24
31
|
runs={runs}
|
|
25
32
|
query={query}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
selectedWorkflowRunId={selectedWorkflowRunId}
|
|
33
|
+
workspaceSlug={workspaceSlug}
|
|
34
|
+
projectSlug={projectSlug}
|
|
29
35
|
className={className}
|
|
30
|
-
search
|
|
31
|
-
statusFilter={statusFilter}
|
|
36
|
+
{...(search ? {search} : {})}
|
|
32
37
|
{...(onFiltersChange ? {onFiltersChange} : {})}
|
|
38
|
+
{...(onClearFilters ? {onClearFilters} : {})}
|
|
39
|
+
hasNextPage={hasNextPage ?? query.hasNextPage}
|
|
40
|
+
isFetchingNextPage={isFetchingNextPage ?? query.isFetchingNextPage}
|
|
41
|
+
isFetchNextPageError={isFetchNextPageError ?? query.isFetchNextPageError}
|
|
42
|
+
onLoadMore={onLoadMore ?? handleLoadMore}
|
|
33
43
|
/>
|
|
34
44
|
);
|
|
35
45
|
}
|