Skip to content

Commit 8674f75

Browse files
Split workflow summary JSON into two on wide screens (#1037)
* Add new props to WorkflowSummaryJsonView (defaultTab: Default tab that is opened on load, hideTabToggle: Replaces the tab toggle with a static title when true) * Add two static title JSON views to workflow summary (one for input and one for result) that show up only on wide screens * Show the result by default for a completed workflow on narrower screens where we don't show both input and result
1 parent 4e6a05c commit 8674f75

File tree

8 files changed

+142
-40
lines changed

8 files changed

+142
-40
lines changed

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ jest.mock('../workflow-summary-details/workflow-summary-details', () =>
1414
);
1515

1616
jest.mock('../workflow-summary-json-view/workflow-summary-json-view', () =>
17-
jest.fn(() => <div>MockWorkflowSummaryJsonView</div>)
17+
jest.fn(({ hideTabToggle }) => (
18+
<div>{`MockWorkflowSummaryJsonView ${hideTabToggle ? 'without toggle' : 'with toggle'}`}</div>
19+
))
1820
);
1921

2022
jest.mock(
@@ -35,7 +37,7 @@ describe('WorkflowSummary', () => {
3537
workflowTab: 'summary',
3638
};
3739

38-
it('should render tab deatils, JSON view, and diagnostics banner', async () => {
40+
it('should render tab details, JSON view, and diagnostics banner', async () => {
3941
render(
4042
<Suspense>
4143
<WorkflowSummary params={params} />
@@ -71,9 +73,16 @@ describe('WorkflowSummary', () => {
7173
expect(
7274
await screen.findByText('MockWorkflowSummaryDetails')
7375
).toBeInTheDocument();
76+
77+
// This will be hidden on wide screens
7478
expect(
75-
await screen.findByText('MockWorkflowSummaryJsonView')
76-
).toBeInTheDocument();
79+
await screen.findAllByText('MockWorkflowSummaryJsonView with toggle')
80+
).toHaveLength(1);
81+
// These will be hidden on narrow screens
82+
expect(
83+
await screen.findAllByText('MockWorkflowSummaryJsonView without toggle')
84+
).toHaveLength(2);
85+
7786
expect(
7887
await screen.findByText('MockWorkflowSummaryDiagnosticsBanner')
7988
).toBeInTheDocument();

src/views/workflow-summary/workflow-summary-json-view/__tests__/workflow-summary-json-view.test.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,49 @@ describe('WorkflowSummaryJsonView Component', () => {
9797
queryByText('Workflow is archived, result is only available in')
9898
).toBeInTheDocument();
9999
});
100+
101+
it('should default to input tab when defaultTab is not provided', () => {
102+
const { getByText } = setup({});
103+
// With the mocked copy button, we can check if the text contains input data
104+
const copyButton = getByText(/Copy Button/);
105+
expect(copyButton.innerHTML).toMatch(
106+
losslessJsonStringify(losslessInputJson, null, '\t')
107+
);
108+
});
109+
110+
it('should initialize with input tab when defaultTab is "input"', () => {
111+
const { getByText } = setup({ defaultTab: 'input' });
112+
const copyButton = getByText(/Copy Button/);
113+
expect(copyButton.innerHTML).toMatch(
114+
losslessJsonStringify(losslessInputJson, null, '\t')
115+
);
116+
});
117+
118+
it('should initialize with result tab when defaultTab is "result"', () => {
119+
const { getByText } = setup({ defaultTab: 'result' });
120+
const copyButton = getByText(/Copy Button/);
121+
expect(copyButton.innerHTML).toMatch(
122+
losslessJsonStringify(losselessResultJson, null, '\t')
123+
);
124+
});
125+
126+
it('should hide segmented control and show static title when hideTabToggle is true with input tab', () => {
127+
const { queryByText, getByText } = setup({
128+
hideTabToggle: true,
129+
defaultTab: 'input',
130+
});
131+
expect(queryByText('SegmentedControlRounded Mock')).not.toBeInTheDocument();
132+
expect(getByText('Input')).toBeInTheDocument();
133+
});
134+
135+
it('should hide segmented control and show static title when hideTabToggle is true with result tab', () => {
136+
const { queryByText, getByText } = setup({
137+
hideTabToggle: true,
138+
defaultTab: 'result',
139+
});
140+
expect(queryByText('SegmentedControlRounded Mock')).not.toBeInTheDocument();
141+
expect(getByText('Result')).toBeInTheDocument();
142+
});
100143
});
101144

102145
const losslessInputJson = {
@@ -113,13 +156,17 @@ const setup = ({
113156
resultJson = losselessResultJson,
114157
isWorkflowRunning = false,
115158
isArchived = false,
159+
defaultTab,
160+
hideTabToggle = false,
116161
}: Partial<Props>) => {
117162
return render(
118163
<WorkflowSummaryJsonView
119164
inputJson={inputJson}
120165
resultJson={resultJson}
121166
isWorkflowRunning={isWorkflowRunning}
122167
isArchived={isArchived}
168+
defaultTab={defaultTab}
169+
hideTabToggle={hideTabToggle}
123170
domain="test-domain"
124171
cluster="test-cluster"
125172
runId="test-run-id"
Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
export const jsonViewTabsOptions = [
2-
{
3-
key: 'input',
4-
label: 'Input',
5-
},
6-
{
7-
key: 'result',
8-
label: 'Result',
9-
},
10-
];
1+
import { type WorkflowSummaryJsonTab } from './workflow-summary-json-view.types';
2+
3+
export const jsonTabLabelMap: Record<WorkflowSummaryJsonTab, string> = {
4+
input: 'Input',
5+
result: 'Result',
6+
};

src/views/workflow-summary/workflow-summary-json-view/workflow-summary-json-view.styles.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import type {
66
} from '@/hooks/use-styletron-classes';
77

88
const cssStylesObj = {
9+
jsonStaticTitle: (theme) => ({
10+
...theme.typography.LabelSmall,
11+
}),
912
jsonViewContainer: (theme) => ({
1013
padding: theme.sizing.scale600,
1114
backgroundColor: theme.colors.backgroundSecondary,

src/views/workflow-summary/workflow-summary-json-view/workflow-summary-json-view.tsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ import SegmentedControlRounded from '@/components/segmented-control-rounded/segm
1010
import useStyletronClasses from '@/hooks/use-styletron-classes';
1111
import losslessJsonStringify from '@/utils/lossless-json-stringify';
1212

13-
import { jsonViewTabsOptions } from './workflow-summary-json-view.constants';
13+
import { jsonTabLabelMap } from './workflow-summary-json-view.constants';
1414
import { cssStyles, overrides } from './workflow-summary-json-view.styles';
15-
import type { Props } from './workflow-summary-json-view.types';
15+
import type {
16+
Props,
17+
WorkflowSummaryJsonTab,
18+
} from './workflow-summary-json-view.types';
1619

1720
export default function WorkflowSummaryJsonView({
1821
inputJson,
@@ -23,6 +26,8 @@ export default function WorkflowSummaryJsonView({
2326
cluster,
2427
runId,
2528
workflowId,
29+
defaultTab = 'input',
30+
hideTabToggle,
2631
}: Props) {
2732
const { cls } = useStyletronClasses(cssStyles);
2833
const jsonMap: Record<string, PrettyJsonValue> = useMemo(
@@ -32,9 +37,8 @@ export default function WorkflowSummaryJsonView({
3237
}),
3338
[inputJson, resultJson]
3439
);
35-
const [activeTab, setActiveTab] = useState<string>(
36-
jsonViewTabsOptions[0].key
37-
);
40+
const [activeTab, setActiveTab] =
41+
useState<WorkflowSummaryJsonTab>(defaultTab);
3842

3943
const textToCopy = useMemo(() => {
4044
return losslessJsonStringify(jsonMap[activeTab], null, '\t');
@@ -43,11 +47,23 @@ export default function WorkflowSummaryJsonView({
4347
return (
4448
<div className={cls.jsonViewContainer}>
4549
<div className={cls.jsonViewHeader}>
46-
<SegmentedControlRounded
47-
activeKey={activeTab}
48-
options={jsonViewTabsOptions}
49-
onChange={({ activeKey }) => setActiveTab(activeKey.toString())}
50-
/>
50+
{hideTabToggle ? (
51+
<div className={cls.jsonStaticTitle}>
52+
{jsonTabLabelMap[activeTab]}
53+
</div>
54+
) : (
55+
<SegmentedControlRounded
56+
activeKey={activeTab}
57+
options={Object.entries(jsonTabLabelMap).map(([key, label]) => ({
58+
key,
59+
label,
60+
}))}
61+
onChange={({ activeKey }) =>
62+
setActiveTab(activeKey === 'result' ? 'result' : 'input')
63+
}
64+
/>
65+
)}
66+
5167
<CopyTextButton
5268
textToCopy={textToCopy}
5369
overrides={overrides.copyButton}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { type PrettyJsonValue } from '@/components/pretty-json/pretty-json.types';
22
import { type WorkflowPageParams } from '@/views/workflow-page/workflow-page.types';
33

4+
export type WorkflowSummaryJsonTab = 'input' | 'result';
5+
46
export type Props = {
57
inputJson: PrettyJsonValue;
68
resultJson: PrettyJsonValue;
79
isWorkflowRunning: boolean;
810
isArchived: boolean;
11+
defaultTab?: WorkflowSummaryJsonTab;
12+
hideTabToggle?: boolean;
913
} & WorkflowPageParams;

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,19 @@ const cssStylesObj = {
1515
flexDirection: 'column',
1616
gap: theme.sizing.scale800,
1717
}),
18-
jsonArea: {
18+
jsonPanel: (theme) => ({
1919
flex: '1 0 300px',
20-
},
20+
[theme.mediaQuery.large]: {
21+
display: 'none',
22+
},
23+
}),
24+
jsonPanelWide: (theme) => ({
25+
display: 'none',
26+
[theme.mediaQuery.large]: {
27+
display: 'block',
28+
flex: '1 0 300px',
29+
},
30+
}),
2131
} satisfies StyletronCSSObject;
2232

2333
export const cssStyles: StyletronCSSObjectOf<typeof cssStylesObj> =

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

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import getWorkflowResultJson from './helpers/get-workflow-result-json';
2424
import WorkflowSummaryDetails from './workflow-summary-details/workflow-summary-details';
2525
import WorkflowSummaryDiagnosticsBanner from './workflow-summary-diagnostics-banner/workflow-summary-diagnostics-banner';
2626
import WorkflowSummaryJsonView from './workflow-summary-json-view/workflow-summary-json-view';
27+
import { type Props as JsonViewProps } from './workflow-summary-json-view/workflow-summary-json-view.types';
2728
import { cssStyles } from './workflow-summary.styles';
2829

2930
export default function WorkflowSummary({
@@ -84,6 +85,17 @@ export default function WorkflowSummary({
8485
!closeEvent.attributes ||
8586
!getWorkflowIsCompleted(closeEvent.attributes);
8687

88+
const baseJsonViewProps: JsonViewProps = {
89+
inputJson:
90+
formattedStartEvent && 'input' in formattedStartEvent
91+
? (formattedStartEvent?.input as PrettyJsonValue)
92+
: [],
93+
resultJson,
94+
isWorkflowRunning,
95+
isArchived,
96+
...params,
97+
};
98+
8799
return (
88100
<PageSection>
89101
<div className={cls.pageContainer}>
@@ -97,22 +109,27 @@ export default function WorkflowSummary({
97109
workflowDetails={workflowDetails}
98110
decodedPageUrlParams={decodedParams}
99111
/>
100-
{/* <div>Taskslist</div> */}
101112
</div>
102-
<div className={cls.jsonArea}>
113+
{/* On narrow screens */}
114+
<div className={cls.jsonPanel}>
115+
<WorkflowSummaryJsonView
116+
{...baseJsonViewProps}
117+
defaultTab={isWorkflowRunning ? 'input' : 'result'}
118+
/>
119+
</div>
120+
{/* On wide screens */}
121+
<div className={cls.jsonPanelWide}>
122+
<WorkflowSummaryJsonView
123+
{...baseJsonViewProps}
124+
defaultTab="input"
125+
hideTabToggle
126+
/>
127+
</div>
128+
<div className={cls.jsonPanelWide}>
103129
<WorkflowSummaryJsonView
104-
inputJson={
105-
formattedStartEvent && 'input' in formattedStartEvent
106-
? (formattedStartEvent?.input as PrettyJsonValue)
107-
: []
108-
}
109-
resultJson={resultJson}
110-
isWorkflowRunning={isWorkflowRunning}
111-
isArchived={isArchived}
112-
domain={params.domain}
113-
cluster={params.cluster}
114-
runId={params.runId}
115-
workflowId={params.workflowId}
130+
{...baseJsonViewProps}
131+
defaultTab="result"
132+
hideTabToggle
116133
/>
117134
</div>
118135
</div>

0 commit comments

Comments
 (0)