Skip to content

Commit b5893df

Browse files
Add banner on workflow summary page to highlight diagnostics issues (#976)
Add banner that highlights issues on the Workflow Summary page and prompts the user to view the Diagnostics page (using the diagnostics issues count hook added in #975)
1 parent 01b41cf commit b5893df

7 files changed

+167
-3
lines changed

src/views/workflow-summary-tab/__tests__/workflow-summary-tab.test.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@ import WorkflowSummaryTab from '../workflow-summary-tab';
1212
jest.mock('../workflow-summary-tab-details/workflow-summary-tab-details', () =>
1313
jest.fn(() => <div>MockWorkflowSummaryTabDetails</div>)
1414
);
15+
1516
jest.mock(
1617
'../workflow-summary-tab-json-view/workflow-summary-tab-json-view',
1718
() => jest.fn(() => <div>MockWorkflowSummaryTabJsonView</div>)
1819
);
1920

21+
jest.mock(
22+
'../workflow-summary-tab-diagnostics-banner/workflow-summary-tab-diagnostics-banner',
23+
() => jest.fn(() => <div>MockWorkflowSummaryTabDiagnosticsBanner</div>)
24+
);
25+
2026
describe('WorkflowSummaryTab', () => {
2127
beforeEach(() => {
2228
jest.clearAllMocks();
@@ -30,7 +36,7 @@ describe('WorkflowSummaryTab', () => {
3036
workflowTab: 'summary',
3137
};
3238

33-
it('should render WorkflowSummaryTabDetails and WorkflowSummaryTabJsonView', async () => {
39+
it('should render tab deatils, JSON view, and diagnostics banner', async () => {
3440
render(
3541
<Suspense>
3642
<WorkflowSummaryTab params={params} />
@@ -69,5 +75,8 @@ describe('WorkflowSummaryTab', () => {
6975
expect(
7076
await screen.findByText('MockWorkflowSummaryTabJsonView')
7177
).toBeInTheDocument();
78+
expect(
79+
await screen.findByText('MockWorkflowSummaryTabDiagnosticsBanner')
80+
).toBeInTheDocument();
7281
});
7382
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import React from 'react';
2+
3+
import { render, screen } from '@/test-utils/rtl';
4+
5+
import * as useWorkflowDiagnosticsIssuesCountModule from '@/views/shared/hooks/use-workflow-diagnostics-issues-count';
6+
7+
import WorkflowSummaryTabDiagnosticsBanner from '../workflow-summary-tab-diagnostics-banner';
8+
9+
jest.mock('@/views/shared/hooks/use-workflow-diagnostics-issues-count', () =>
10+
jest.fn(() => undefined)
11+
);
12+
13+
describe(WorkflowSummaryTabDiagnosticsBanner.name, () => {
14+
afterEach(() => {
15+
jest.restoreAllMocks();
16+
});
17+
18+
it('should render banner with singular issue text when count is 1', () => {
19+
setup({ mockIssuesCount: 1 });
20+
21+
expect(
22+
screen.getByText('1 issue was detected on this workflow')
23+
).toBeInTheDocument();
24+
expect(screen.getByText('View issue')).toBeInTheDocument();
25+
});
26+
27+
it('should render banner with plural issues text when count is greater than 1', () => {
28+
setup({ mockIssuesCount: 5 });
29+
30+
expect(
31+
screen.getByText('5 issues were detected on this workflow')
32+
).toBeInTheDocument();
33+
expect(screen.getByText('View issues')).toBeInTheDocument();
34+
});
35+
36+
it('should not render anything when issues count is undefined', () => {
37+
const { container } = setup({ mockIssuesCount: undefined });
38+
39+
expect(container.firstChild?.firstChild).toBeNull();
40+
});
41+
42+
it('should not render anything when there are 0 issues', () => {
43+
const { container } = setup({ mockIssuesCount: 0 });
44+
45+
expect(container.firstChild?.firstChild).toBeNull();
46+
});
47+
48+
it('should render link to diagnostics page with correct href', () => {
49+
setup({ mockIssuesCount: 3 });
50+
51+
const link = screen.getByRole('link', { name: 'View issues' });
52+
expect(link).toHaveAttribute(
53+
'href',
54+
'/domains/mock-domain/cluster_1/workflows/mock-workflow-id/mock-run-id/diagnostics'
55+
);
56+
});
57+
});
58+
59+
function setup({ mockIssuesCount }: { mockIssuesCount?: number }) {
60+
jest
61+
.spyOn(useWorkflowDiagnosticsIssuesCountModule, 'default')
62+
.mockReturnValue(mockIssuesCount);
63+
64+
const result = render(
65+
<WorkflowSummaryTabDiagnosticsBanner
66+
domain="mock-domain"
67+
cluster="cluster_1"
68+
workflowId="mock-workflow-id"
69+
runId="mock-run-id"
70+
/>
71+
);
72+
73+
return {
74+
...result,
75+
};
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { styled as createStyled, type Theme } from 'baseui';
2+
import { type StyleObject } from 'styletron-react';
3+
4+
export const styled = {
5+
Banner: createStyled(
6+
'div',
7+
({ $theme }: { $theme: Theme }): StyleObject => ({
8+
display: 'flex',
9+
backgroundColor: $theme.colors.backgroundWarningLight,
10+
alignItems: 'center',
11+
justifyContent: 'space-between',
12+
padding: `${$theme.sizing.scale400} ${$theme.sizing.scale500}`,
13+
borderRadius: $theme.borders.radius400,
14+
})
15+
),
16+
BannerTextContainer: createStyled(
17+
'div',
18+
({ $theme }: { $theme: Theme }): StyleObject => ({
19+
...$theme.typography.LabelSmall,
20+
display: 'flex',
21+
gap: $theme.sizing.scale600,
22+
alignItems: 'center',
23+
})
24+
),
25+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use client';
2+
3+
import React from 'react';
4+
5+
import { Button } from 'baseui/button';
6+
import Link from 'next/link';
7+
import { RiStethoscopeLine } from 'react-icons/ri';
8+
9+
import useWorkflowDiagnosticsIssuesCount from '@/views/shared/hooks/use-workflow-diagnostics-issues-count';
10+
11+
import { styled } from './workflow-summary-tab-diagnostics-banner.styles';
12+
import { type Props } from './workflow-summary-tab-diagnostics-banner.types';
13+
14+
export default function WorkflowSummaryTabDiagnosticsBanner({
15+
domain,
16+
cluster,
17+
workflowId,
18+
runId,
19+
}: Props) {
20+
const issuesCount = useWorkflowDiagnosticsIssuesCount({
21+
domain,
22+
cluster,
23+
workflowId,
24+
runId,
25+
});
26+
27+
if (issuesCount === undefined || issuesCount === 0) return null;
28+
29+
return (
30+
<styled.Banner>
31+
<styled.BannerTextContainer>
32+
<RiStethoscopeLine size="20px" />
33+
{`${
34+
issuesCount === 1 ? '1 issue was' : `${issuesCount} issues were`
35+
} detected on this workflow`}
36+
</styled.BannerTextContainer>
37+
<Button
38+
size="mini"
39+
$as={Link}
40+
href={`/domains/${domain}/${cluster}/workflows/${workflowId}/${runId}/diagnostics`}
41+
>
42+
{`View ${issuesCount === 1 ? 'issue' : 'issues'}`}
43+
</Button>
44+
</styled.Banner>
45+
);
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type Props = {
2+
domain: string;
3+
cluster: string;
4+
workflowId: string;
5+
runId: string;
6+
};

src/views/workflow-summary-tab/workflow-summary-tab.styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const cssStylesObj = {
1313
flex: '1 0 300px',
1414
display: 'flex',
1515
flexDirection: 'column',
16-
gap: theme.sizing.scale900,
16+
gap: theme.sizing.scale800,
1717
}),
1818
jsonArea: {
1919
flex: '1 0 300px',

src/views/workflow-summary-tab/workflow-summary-tab.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22
import React from 'react';
33

4-
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
4+
import { useQuery } from '@tanstack/react-query';
55
import queryString from 'query-string';
66

77
import PageSection from '@/components/page-section/page-section';
@@ -22,6 +22,7 @@ import { useDescribeWorkflow } from '../workflow-page/hooks/use-describe-workflo
2222

2323
import getWorkflowResultJson from './helpers/get-workflow-result-json';
2424
import WorkflowSummaryTabDetails from './workflow-summary-tab-details/workflow-summary-tab-details';
25+
import WorkflowSummaryTabDiagnosticsBanner from './workflow-summary-tab-diagnostics-banner/workflow-summary-tab-diagnostics-banner';
2526
import WorkflowSummaryTabJsonView from './workflow-summary-tab-json-view/workflow-summary-tab-json-view';
2627
import { cssStyles } from './workflow-summary-tab.styles';
2728

@@ -86,6 +87,7 @@ export default function WorkflowSummaryTab({
8687
<PageSection>
8788
<div className={cls.pageContainer}>
8889
<div className={cls.mainContent}>
90+
<WorkflowSummaryTabDiagnosticsBanner {...params} />
8991
<WorkflowSummaryTabDetails
9092
firstHistoryEvent={firstEvent}
9193
closeHistoryEvent={closeEvent}

0 commit comments

Comments
 (0)