@shipfox/client-workflows 20.0.0 → 22.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +51 -0
- package/dist/components/job-detail/job-detail-view.d.ts.map +1 -1
- package/dist/components/job-detail/job-detail-view.js +232 -44
- package/dist/components/job-detail/job-detail-view.js.map +1 -1
- package/dist/components/job-detail/job-empty-states.js +2 -2
- package/dist/components/job-detail/job-empty-states.js.map +1 -1
- package/dist/components/job-detail/step-attempt-log-panel.d.ts +7 -1
- package/dist/components/job-detail/step-attempt-log-panel.d.ts.map +1 -1
- package/dist/components/job-detail/step-attempt-log-panel.js +37 -10
- package/dist/components/job-detail/step-attempt-log-panel.js.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +2 -2
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/step-list/step-list-model.d.ts +1 -0
- package/dist/components/step-list/step-list-model.d.ts.map +1 -1
- package/dist/components/step-list/step-list-model.js +1 -1
- package/dist/components/step-list/step-list-model.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +2 -1
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js +25 -9
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts +9 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.js +22 -11
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filters.js +1 -1
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -1
- 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 +13 -7
- 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.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +43 -38
- 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.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +34 -22
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts +3 -3
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +25 -18
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +4 -4
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js +20 -7
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +1 -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.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +103 -78
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/components/workflow-source-panel/workflow-source-content.js +1 -1
- package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -1
- package/dist/pages/project-workflows-page.d.ts.map +1 -1
- package/dist/pages/project-workflows-page.js +171 -158
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-job-detail-page.d.ts.map +1 -1
- package/dist/pages/workflow-job-detail-page.js +3 -2
- package/dist/pages/workflow-job-detail-page.js.map +1 -1
- package/dist/pages/workflow-run-detail-page.js +2 -1
- package/dist/pages/workflow-run-detail-page.js.map +1 -1
- package/dist/pages/workflow-run-list-page.d.ts.map +1 -1
- package/dist/pages/workflow-run-list-page.js +9 -11
- package/dist/pages/workflow-run-list-page.js.map +1 -1
- package/dist/routes/job-detail.d.ts +1 -1
- package/dist/routes/job-detail.js +1 -1
- package/dist/routes/job-detail.js.map +1 -1
- package/dist/routes/run-detail.d.ts +1 -1
- package/dist/routes/run-detail.js +1 -1
- package/dist/routes/run-detail.js.map +1 -1
- package/dist/routes/runs.d.ts +1 -1
- package/dist/routes/runs.js +1 -1
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/workflows.d.ts +3 -0
- package/dist/routes/workflows.js +3 -0
- package/dist/routes/workflows.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/components/job-detail/job-detail-view.tsx +264 -49
- package/src/components/job-detail/job-empty-states.tsx +2 -2
- package/src/components/job-detail/step-attempt-log-panel.test.tsx +30 -12
- package/src/components/job-detail/step-attempt-log-panel.tsx +68 -10
- package/src/components/job-graph/job-graph-content.tsx +2 -2
- package/src/components/job-graph/job-graph.stories.tsx +1 -1
- package/src/components/job-graph/job-node.stories.tsx +1 -1
- package/src/components/step-list/step-list-model.ts +1 -1
- package/src/components/step-list/step-list.test.tsx +29 -1
- package/src/components/step-list/step-list.tsx +29 -11
- package/src/components/workflow-run-list/job-status-strip.tsx +25 -12
- package/src/components/workflow-run-list/workflow-run-filters.tsx +1 -1
- package/src/components/workflow-run-list/workflow-run-list-content.tsx +7 -2
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +42 -41
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +53 -4
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +28 -22
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +9 -20
- package/src/components/workflow-run-list/workflow-run-row.tsx +26 -19
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +3 -3
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +11 -2
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +4 -7
- package/src/components/workflow-run-view/run-workspace-nav.test.tsx +40 -2
- package/src/components/workflow-run-view/run-workspace-nav.tsx +28 -9
- package/src/components/workflow-run-view/workflow-run-states.tsx +1 -4
- package/src/components/workflow-run-view/workflow-run-view.stories.tsx +142 -0
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +42 -8
- package/src/components/workflow-run-view/workflow-run-view.tsx +112 -96
- package/src/components/workflow-source-panel/workflow-source-content.tsx +2 -2
- package/src/pages/project-workflows-page.tsx +135 -125
- package/src/pages/workflow-job-detail-page.test.tsx +32 -0
- package/src/pages/workflow-job-detail-page.tsx +5 -1
- package/src/pages/workflow-run-detail-page.tsx +1 -1
- package/src/pages/workflow-run-list-page.tsx +9 -11
- package/src/pages/workflow-run-pages.test.tsx +26 -0
- package/src/routes/job-detail.tsx +1 -1
- package/src/routes/run-detail.tsx +1 -1
- package/src/routes/runs.tsx +1 -1
- package/src/routes/workflows.tsx +1 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -12,6 +12,7 @@ import {Callout} from '@shipfox/react-ui/callout';
|
|
|
12
12
|
import {EmptyState} from '@shipfox/react-ui/empty-state';
|
|
13
13
|
import {Icon, type IconName} from '@shipfox/react-ui/icon';
|
|
14
14
|
import {LoadErrorState} from '@shipfox/react-ui/load-error-state';
|
|
15
|
+
import {Panel} from '@shipfox/react-ui/panel';
|
|
15
16
|
import {RelativeTime, RelativeTimeProvider} from '@shipfox/react-ui/relative-time';
|
|
16
17
|
import {
|
|
17
18
|
Sheet,
|
|
@@ -170,152 +171,161 @@ function WorkflowDefinitionsList({
|
|
|
170
171
|
}) {
|
|
171
172
|
if (isPending) {
|
|
172
173
|
return (
|
|
173
|
-
<
|
|
174
|
-
<Skeleton className="h-
|
|
175
|
-
<Skeleton className="h-
|
|
176
|
-
<Skeleton className="h-
|
|
177
|
-
</
|
|
174
|
+
<Panel role="status" aria-label="Loading workflows" className="divide-y">
|
|
175
|
+
<Skeleton className="h-44 w-full rounded-none" />
|
|
176
|
+
<Skeleton className="h-44 w-full rounded-none" />
|
|
177
|
+
<Skeleton className="h-44 w-full rounded-none" />
|
|
178
|
+
</Panel>
|
|
178
179
|
);
|
|
179
180
|
}
|
|
180
181
|
|
|
181
182
|
if (isError && definitions.length === 0) {
|
|
182
183
|
return (
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
184
|
+
<Panel>
|
|
185
|
+
<LoadErrorState
|
|
186
|
+
title="Couldn't load workflows"
|
|
187
|
+
description="Definitions could not be loaded. Source metadata remains visible."
|
|
188
|
+
onRetry={onRetry}
|
|
189
|
+
retryLabel="Retry loading workflows"
|
|
190
|
+
variant="panel"
|
|
191
|
+
/>
|
|
192
|
+
</Panel>
|
|
189
193
|
);
|
|
190
194
|
}
|
|
191
195
|
|
|
192
196
|
if (definitions.length === 0) {
|
|
193
|
-
return
|
|
197
|
+
return (
|
|
198
|
+
<Panel>
|
|
199
|
+
<WorkflowEmptyState sync={sync} />
|
|
200
|
+
</Panel>
|
|
201
|
+
);
|
|
194
202
|
}
|
|
195
203
|
|
|
196
204
|
return (
|
|
197
205
|
<>
|
|
198
|
-
<
|
|
199
|
-
<
|
|
200
|
-
<
|
|
201
|
-
<
|
|
202
|
-
<
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
206
|
+
<Panel>
|
|
207
|
+
<div className="hidden md:block">
|
|
208
|
+
<Table>
|
|
209
|
+
<TableHeader>
|
|
210
|
+
<TableRow>
|
|
211
|
+
<TableHead className="w-40"></TableHead>
|
|
212
|
+
<TableHead>Workflow</TableHead>
|
|
213
|
+
<TableHead className="w-180">Updated</TableHead>
|
|
214
|
+
<TableHead className="w-80 text-right"></TableHead>
|
|
215
|
+
</TableRow>
|
|
216
|
+
</TableHeader>
|
|
217
|
+
<TableBody>
|
|
218
|
+
{definitions.map((definition) => {
|
|
219
|
+
const runErrorMessage =
|
|
220
|
+
runError?.definitionId === definition.id ? runError.message : null;
|
|
221
|
+
const isRunning = runningDefinitionId === definition.id;
|
|
213
222
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
</div>
|
|
250
|
-
</TableCell>
|
|
251
|
-
<TableCell className="text-foreground-neutral-muted">
|
|
252
|
-
<RelativeTime value={definition.updatedAt} />
|
|
253
|
-
</TableCell>
|
|
254
|
-
<TableCell>
|
|
255
|
-
{definition.manualTrigger ? (
|
|
256
|
-
<div className="flex justify-end opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100">
|
|
257
|
-
<Button size="xs" isLoading={isRunning} onClick={() => onRun(definition)}>
|
|
258
|
-
Run
|
|
259
|
-
</Button>
|
|
223
|
+
return (
|
|
224
|
+
// The workflow-name cell holds a `<button>` so the row is
|
|
225
|
+
// keyboard-reachable (Tab focuses, Enter/Space activates
|
|
226
|
+
// via native button semantics). The TableRow itself is no
|
|
227
|
+
// longer clickable: a row-level onClick would be invisible
|
|
228
|
+
// to keyboard users and require custom keydown handling.
|
|
229
|
+
// The `group` class on the row still drives the Run button
|
|
230
|
+
// reveal on hover or focus-within.
|
|
231
|
+
<TableRow key={definition.id} className="group">
|
|
232
|
+
<TableCell>
|
|
233
|
+
<Icon
|
|
234
|
+
name={sourceIcon(definition.source)}
|
|
235
|
+
className="size-16 text-foreground-neutral-muted"
|
|
236
|
+
aria-hidden="true"
|
|
237
|
+
/>
|
|
238
|
+
</TableCell>
|
|
239
|
+
<TableCell className="max-w-260">
|
|
240
|
+
<div className="flex min-w-0 flex-col gap-tight">
|
|
241
|
+
<button
|
|
242
|
+
type="button"
|
|
243
|
+
onClick={() => onOpenDefinition(definition)}
|
|
244
|
+
className="flex min-w-0 flex-col gap-tight rounded-4 text-left outline-none focus-visible:shadow-border-interactive-with-active"
|
|
245
|
+
>
|
|
246
|
+
<Text size="sm" bold className="truncate">
|
|
247
|
+
{definition.name}
|
|
248
|
+
</Text>
|
|
249
|
+
<Code className="truncate text-foreground-neutral-muted">
|
|
250
|
+
{definition.configPath ?? 'Manual definition'}
|
|
251
|
+
</Code>
|
|
252
|
+
</button>
|
|
253
|
+
{runErrorMessage ? (
|
|
254
|
+
<Text size="xs" className="text-tag-error-text">
|
|
255
|
+
{runErrorMessage}
|
|
256
|
+
</Text>
|
|
257
|
+
) : null}
|
|
260
258
|
</div>
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
259
|
+
</TableCell>
|
|
260
|
+
<TableCell className="text-foreground-neutral-muted">
|
|
261
|
+
<RelativeTime value={definition.updatedAt} />
|
|
262
|
+
</TableCell>
|
|
263
|
+
<TableCell>
|
|
264
|
+
{definition.manualTrigger ? (
|
|
265
|
+
<div className="flex justify-end opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100">
|
|
266
|
+
<Button size="xs" isLoading={isRunning} onClick={() => onRun(definition)}>
|
|
267
|
+
Run
|
|
268
|
+
</Button>
|
|
269
|
+
</div>
|
|
270
|
+
) : null}
|
|
271
|
+
</TableCell>
|
|
272
|
+
</TableRow>
|
|
273
|
+
);
|
|
274
|
+
})}
|
|
275
|
+
</TableBody>
|
|
276
|
+
</Table>
|
|
277
|
+
</div>
|
|
269
278
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
279
|
+
<div className="flex flex-col md:hidden">
|
|
280
|
+
{definitions.map((definition) => {
|
|
281
|
+
const runErrorMessage =
|
|
282
|
+
runError?.definitionId === definition.id ? runError.message : null;
|
|
283
|
+
const isRunning = runningDefinitionId === definition.id;
|
|
275
284
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
>
|
|
281
|
-
<button
|
|
282
|
-
type="button"
|
|
283
|
-
className="flex min-w-0 items-start gap-inline text-left"
|
|
284
|
-
onClick={() => onOpenDefinition(definition)}
|
|
285
|
+
return (
|
|
286
|
+
<div
|
|
287
|
+
key={definition.id}
|
|
288
|
+
className="flex flex-col gap-inline border-b border-border-neutral-base p-panel-compact last:border-b-0"
|
|
285
289
|
>
|
|
286
|
-
<
|
|
287
|
-
|
|
288
|
-
className="
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
290
|
+
<button
|
|
291
|
+
type="button"
|
|
292
|
+
className="flex min-w-0 items-start gap-inline text-left"
|
|
293
|
+
onClick={() => onOpenDefinition(definition)}
|
|
294
|
+
>
|
|
295
|
+
<Icon
|
|
296
|
+
name={sourceIcon(definition.source)}
|
|
297
|
+
className="size-16 shrink-0 text-foreground-neutral-muted"
|
|
298
|
+
aria-hidden="true"
|
|
299
|
+
/>
|
|
300
|
+
<div className="flex min-w-0 flex-col gap-tight">
|
|
301
|
+
<Text size="sm" bold className="break-words">
|
|
302
|
+
{definition.name}
|
|
303
|
+
</Text>
|
|
304
|
+
<Code className="break-words text-foreground-neutral-muted">
|
|
305
|
+
{definition.configPath ?? 'Manual definition'}
|
|
306
|
+
</Code>
|
|
307
|
+
</div>
|
|
308
|
+
</button>
|
|
309
|
+
<div className="flex items-center justify-between gap-inline">
|
|
310
|
+
<Text size="xs" className="text-foreground-neutral-muted">
|
|
311
|
+
Updated <RelativeTime value={definition.updatedAt} />
|
|
294
312
|
</Text>
|
|
295
|
-
|
|
296
|
-
{
|
|
297
|
-
|
|
313
|
+
{definition.manualTrigger ? (
|
|
314
|
+
<Button size="sm" isLoading={isRunning} onClick={() => onRun(definition)}>
|
|
315
|
+
Run
|
|
316
|
+
</Button>
|
|
317
|
+
) : null}
|
|
298
318
|
</div>
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
</Text>
|
|
304
|
-
{definition.manualTrigger ? (
|
|
305
|
-
<Button size="sm" isLoading={isRunning} onClick={() => onRun(definition)}>
|
|
306
|
-
Run
|
|
307
|
-
</Button>
|
|
319
|
+
{runErrorMessage ? (
|
|
320
|
+
<Text size="xs" className="text-tag-error-text">
|
|
321
|
+
{runErrorMessage}
|
|
322
|
+
</Text>
|
|
308
323
|
) : null}
|
|
309
324
|
</div>
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
) : null}
|
|
315
|
-
</div>
|
|
316
|
-
);
|
|
317
|
-
})}
|
|
318
|
-
</div>
|
|
325
|
+
);
|
|
326
|
+
})}
|
|
327
|
+
</div>
|
|
328
|
+
</Panel>
|
|
319
329
|
|
|
320
330
|
{isFetchNextPageError ? (
|
|
321
331
|
<Callout role="alert" type="error">
|
|
@@ -355,7 +365,7 @@ function WorkflowEmptyState({sync}: {sync: DefinitionSyncSummary | null}) {
|
|
|
355
365
|
? 'No workflow definitions found.'
|
|
356
366
|
: 'Workflow sync has not reported yet.';
|
|
357
367
|
|
|
358
|
-
return <EmptyState icon="flowChart" title="No workflows" description={message} />;
|
|
368
|
+
return <EmptyState icon="flowChart" title="No workflows" description={message} variant="panel" />;
|
|
359
369
|
}
|
|
360
370
|
|
|
361
371
|
function WorkflowSyncAlert({sync}: {sync: DefinitionSyncSummary | null | undefined}) {
|
|
@@ -41,6 +41,19 @@ describe('WorkflowJobDetailPage', () => {
|
|
|
41
41
|
jobAnnotations.value = [];
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
test('lets the job detail route inherit its shell canvas', async () => {
|
|
45
|
+
configureApiClient({fetchImpl: vi.fn(() => new Promise<Response>(() => undefined))});
|
|
46
|
+
|
|
47
|
+
const {container} = renderJobPath();
|
|
48
|
+
|
|
49
|
+
expect(await screen.findByRole('region', {name: 'Loading workflow run'})).toBeInTheDocument();
|
|
50
|
+
|
|
51
|
+
const pageRoot = container.querySelector('[data-workflow-page-root="job-detail"]');
|
|
52
|
+
|
|
53
|
+
expect(pageRoot).not.toBeNull();
|
|
54
|
+
expect(pageRoot).not.toHaveClass('bg-background-subtle-base');
|
|
55
|
+
});
|
|
56
|
+
|
|
44
57
|
test('links the job to its annotations without rendering one', async () => {
|
|
45
58
|
jobAnnotations.value = [
|
|
46
59
|
{
|
|
@@ -88,6 +101,7 @@ describe('WorkflowJobDetailPage', () => {
|
|
|
88
101
|
});
|
|
89
102
|
|
|
90
103
|
test('resolves an exact job execution and step attempt from a deep link', async () => {
|
|
104
|
+
const user = userEvent.setup();
|
|
91
105
|
configureApiClient({fetchImpl: vi.fn(jobDetailFetch)});
|
|
92
106
|
|
|
93
107
|
renderJobPath(
|
|
@@ -104,6 +118,24 @@ describe('WorkflowJobDetailPage', () => {
|
|
|
104
118
|
await screen.findByRole('region', {name: 'tests output, attempt 2'}),
|
|
105
119
|
).toBeInTheDocument();
|
|
106
120
|
expect(screen.getByRole('region', {name: 'release logs'})).toBeInTheDocument();
|
|
121
|
+
expect(screen.getByRole('textbox', {name: 'Search logs'})).toBeInTheDocument();
|
|
122
|
+
expect(screen.getByRole('button', {name: 'Refresh logs'})).toBeInTheDocument();
|
|
123
|
+
expect(screen.getByRole('button', {name: 'Log settings'})).toBeInTheDocument();
|
|
124
|
+
const testsAttempt = screen.getByRole('button', {name: 'tests, Succeeded, attempt 2'});
|
|
125
|
+
await user.click(testsAttempt);
|
|
126
|
+
await waitFor(() =>
|
|
127
|
+
expect(
|
|
128
|
+
screen.queryByRole('region', {name: 'tests output, attempt 2'}),
|
|
129
|
+
).not.toBeInTheDocument(),
|
|
130
|
+
);
|
|
131
|
+
expect(screen.getByRole('textbox', {name: 'Search logs'})).toBeDisabled();
|
|
132
|
+
expect(screen.getByRole('button', {name: 'Refresh logs'})).toBeDisabled();
|
|
133
|
+
|
|
134
|
+
await user.click(testsAttempt);
|
|
135
|
+
expect(
|
|
136
|
+
await screen.findByRole('region', {name: 'tests output, attempt 2'}),
|
|
137
|
+
).toBeInTheDocument();
|
|
138
|
+
expect(screen.getByRole('textbox', {name: 'Search logs'})).not.toBeDisabled();
|
|
107
139
|
expect(screen.queryByRole('tab')).not.toBeInTheDocument();
|
|
108
140
|
expect(screen.getByRole('img', {name: 'Job status: Succeeded'})).toBeInTheDocument();
|
|
109
141
|
expect(screen.getByRole('heading', {name: 'deploy-web'})).toBeInTheDocument();
|
|
@@ -56,7 +56,10 @@ export function WorkflowJobDetailPage({
|
|
|
56
56
|
[navigate],
|
|
57
57
|
);
|
|
58
58
|
return (
|
|
59
|
-
<div
|
|
59
|
+
<div
|
|
60
|
+
data-workflow-page-root="job-detail"
|
|
61
|
+
className="flex min-h-0 min-w-0 flex-1 overflow-hidden"
|
|
62
|
+
>
|
|
60
63
|
<WorkflowRunView
|
|
61
64
|
projectId={projectId}
|
|
62
65
|
workspaceSlug={workspaceSlug}
|
|
@@ -67,6 +70,7 @@ export function WorkflowJobDetailPage({
|
|
|
67
70
|
jobSearch={search}
|
|
68
71
|
jobContent={
|
|
69
72
|
<JobDetailView
|
|
73
|
+
key={jobId}
|
|
70
74
|
workspaceSlug={workspaceSlug}
|
|
71
75
|
projectSlug={projectSlug}
|
|
72
76
|
workflowRunId={workflowRunId}
|
|
@@ -17,7 +17,7 @@ export function WorkflowRunDetailPage({
|
|
|
17
17
|
search = {},
|
|
18
18
|
}: WorkflowRunDetailPageProps) {
|
|
19
19
|
return (
|
|
20
|
-
<div className="flex min-h-0 flex-1 overflow-hidden
|
|
20
|
+
<div data-workflow-page-root="run-detail" className="flex min-h-0 flex-1 overflow-hidden">
|
|
21
21
|
<WorkflowRunView
|
|
22
22
|
projectId={projectId}
|
|
23
23
|
workspaceSlug={workspaceSlug}
|
|
@@ -49,17 +49,15 @@ export function WorkflowRunsPage({
|
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
return (
|
|
52
|
-
<div className="flex min-h-0 flex-1 overflow-hidden
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
/>
|
|
62
|
-
</div>
|
|
52
|
+
<div data-workflow-page-root="runs" className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
|
53
|
+
<WorkflowRunList
|
|
54
|
+
projectId={projectId}
|
|
55
|
+
workspaceSlug={workspaceSlug}
|
|
56
|
+
projectSlug={projectSlug}
|
|
57
|
+
search={search}
|
|
58
|
+
onFiltersChange={onFiltersChange}
|
|
59
|
+
onClearFilters={onClearFilters}
|
|
60
|
+
/>
|
|
63
61
|
</div>
|
|
64
62
|
);
|
|
65
63
|
}
|
|
@@ -72,6 +72,19 @@ describe('WorkflowRunPages', () => {
|
|
|
72
72
|
expect(screen.queryByLabelText('Loading workflow run')).not.toBeInTheDocument();
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
+
test('lets the list route inherit its shell canvas and width', async () => {
|
|
76
|
+
configureApiClient({fetchImpl: vi.fn(() => new Promise<Response>(() => undefined))});
|
|
77
|
+
|
|
78
|
+
const {container} = renderRunsPath();
|
|
79
|
+
|
|
80
|
+
await screen.findByLabelText('Workflow runs');
|
|
81
|
+
const pageRoot = container.querySelector('[data-workflow-page-root="runs"]');
|
|
82
|
+
|
|
83
|
+
expect(pageRoot).not.toBeNull();
|
|
84
|
+
expect(pageRoot).not.toHaveClass('bg-background-neutral-base');
|
|
85
|
+
expect(pageRoot).not.toHaveClass('max-w-[1120px]');
|
|
86
|
+
});
|
|
87
|
+
|
|
75
88
|
test('keeps the list route and filters in place instead of redirecting to a run', async () => {
|
|
76
89
|
configureApiClient({fetchImpl: createRunsListFetch()});
|
|
77
90
|
|
|
@@ -184,6 +197,19 @@ describe('WorkflowRunPages', () => {
|
|
|
184
197
|
expect(currentSearch(router).tab).toBeUndefined();
|
|
185
198
|
});
|
|
186
199
|
|
|
200
|
+
test('lets the run detail route inherit its shell canvas', async () => {
|
|
201
|
+
configureApiClient({fetchImpl: vi.fn(() => new Promise<Response>(() => undefined))});
|
|
202
|
+
|
|
203
|
+
const {container} = renderRunPath();
|
|
204
|
+
|
|
205
|
+
expect(await screen.findByRole('region', {name: 'Loading workflow run'})).toBeInTheDocument();
|
|
206
|
+
|
|
207
|
+
const pageRoot = container.querySelector('[data-workflow-page-root="run-detail"]');
|
|
208
|
+
|
|
209
|
+
expect(pageRoot).not.toBeNull();
|
|
210
|
+
expect(pageRoot).not.toHaveClass('bg-background-subtle-base');
|
|
211
|
+
});
|
|
212
|
+
|
|
187
213
|
test('maps the removed Jobs tab URL to the graph Summary', async () => {
|
|
188
214
|
configureApiClient({fetchImpl: createRunDetailFetch()});
|
|
189
215
|
|
|
@@ -4,7 +4,7 @@ import {validateWorkflowJobSearch, workflowJobRouteParams} from './inputs.js';
|
|
|
4
4
|
import {ProjectRoute} from './project-route.js';
|
|
5
5
|
|
|
6
6
|
export default defineRoute({
|
|
7
|
-
staticData: {
|
|
7
|
+
staticData: {frame: 'data'},
|
|
8
8
|
validateSearch: validateWorkflowJobSearch,
|
|
9
9
|
component: () => {
|
|
10
10
|
const {workspaceSlug, projectSlug, workflowRunId, jobId} =
|
|
@@ -4,7 +4,7 @@ import {validateWorkflowRunsSearch, workflowRouteParams} from './inputs.js';
|
|
|
4
4
|
import {ProjectRoute} from './project-route.js';
|
|
5
5
|
|
|
6
6
|
export default defineRoute({
|
|
7
|
-
staticData: {
|
|
7
|
+
staticData: {frame: 'data'},
|
|
8
8
|
validateSearch: validateWorkflowRunsSearch,
|
|
9
9
|
component: () => {
|
|
10
10
|
const {workspaceSlug, projectSlug, workflowRunId} = useRouteParams(workflowRouteParams);
|
package/src/routes/runs.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import {validateWorkflowRunsSearch, workflowRouteParams} from './inputs.js';
|
|
|
4
4
|
import {ProjectRoute} from './project-route.js';
|
|
5
5
|
|
|
6
6
|
export default defineRoute({
|
|
7
|
-
staticData: {
|
|
7
|
+
staticData: {frame: 'data'},
|
|
8
8
|
validateSearch: validateWorkflowRunsSearch,
|
|
9
9
|
component: () => {
|
|
10
10
|
const {workspaceSlug, projectSlug} = useRouteParams(workflowRouteParams);
|
package/src/routes/workflows.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import {ProjectWorkflowsPage} from '#pages/project-workflows-page.js';
|
|
|
3
3
|
import {ProjectRoute} from './project-route.js';
|
|
4
4
|
|
|
5
5
|
export default defineRoute({
|
|
6
|
+
staticData: {frame: 'data'},
|
|
6
7
|
component: () => {
|
|
7
8
|
return (
|
|
8
9
|
<ProjectRoute>{(project) => <ProjectWorkflowsPage projectId={project.id} />}</ProjectRoute>
|