Skip to content

Commit 0d71532

Browse files
More changes
Signed-off-by: Adhitya Mamallan <[email protected]>
1 parent 6674cb3 commit 0d71532

File tree

6 files changed

+146
-67
lines changed

6 files changed

+146
-67
lines changed

src/views/workflow-history-v2/workflow-history-event-details/__tests__/workflow-history-event-details.test.tsx

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,20 @@ import { render, screen } from '@/test-utils/rtl';
22

33
import { type WorkflowPageParams } from '@/views/workflow-page/workflow-page.types';
44

5+
import type WorkflowHistoryPanelDetailsEntry from '../../workflow-history-panel-details-entry/workflow-history-panel-details-entry';
56
import WorkflowHistoryEventDetails from '../workflow-history-event-details';
67
import { type EventDetailsEntries } from '../workflow-history-event-details.types';
78

8-
jest.mock(
9-
'../../workflow-history-group-details-json/workflow-history-group-details-json',
9+
jest.mock<typeof WorkflowHistoryPanelDetailsEntry>(
10+
'../../workflow-history-panel-details-entry/workflow-history-panel-details-entry',
1011
() =>
11-
jest.fn(
12-
({
13-
entryPath,
14-
entryValue,
15-
isNegative,
16-
}: {
17-
entryPath: string;
18-
entryValue: any;
19-
isNegative?: boolean;
20-
}) => (
21-
<div data-testid="group-details-json">
22-
JSON: {entryPath} = {JSON.stringify(entryValue)}
23-
{isNegative && ' (negative)'}
24-
</div>
25-
)
26-
)
12+
jest.fn(({ detail }) => (
13+
<div data-testid="panel-details-entry">
14+
Panel Entry: {detail.path} ={' '}
15+
{JSON.stringify(detail.isGroup ? detail.groupEntries : detail.value)}
16+
{detail.isNegative && ' (negative)'}
17+
</div>
18+
))
2719
);
2820

2921
jest.mock(
@@ -41,7 +33,7 @@ describe(WorkflowHistoryEventDetails.name, () => {
4133
setup({ eventDetails: [] });
4234

4335
expect(screen.getByText('No Details')).toBeInTheDocument();
44-
expect(screen.queryByTestId('group-details-json')).not.toBeInTheDocument();
36+
expect(screen.queryByTestId('panel-details-entry')).not.toBeInTheDocument();
4537
expect(screen.queryByTestId('event-details-group')).not.toBeInTheDocument();
4638
});
4739

@@ -68,7 +60,7 @@ describe(WorkflowHistoryEventDetails.name, () => {
6860

6961
setup({ eventDetails });
7062

71-
expect(screen.queryByTestId('group-details-json')).not.toBeInTheDocument();
63+
expect(screen.queryByTestId('panel-details-entry')).not.toBeInTheDocument();
7264
expect(screen.getByTestId('event-details-group')).toBeInTheDocument();
7365
expect(
7466
screen.getByText('Event Details Group (2 entries)')
@@ -102,8 +94,10 @@ describe(WorkflowHistoryEventDetails.name, () => {
10294

10395
setup({ eventDetails });
10496

105-
expect(screen.getByTestId('group-details-json')).toBeInTheDocument();
106-
expect(screen.getByText(/JSON: path1 = "value1"/)).toBeInTheDocument();
97+
expect(screen.getByTestId('panel-details-entry')).toBeInTheDocument();
98+
expect(
99+
screen.getByText(/Panel Entry: path1 = "value1"/)
100+
).toBeInTheDocument();
107101
expect(screen.getByTestId('event-details-group')).toBeInTheDocument();
108102
expect(
109103
screen.getByText('Event Details Group (1 entries)')
@@ -148,19 +142,21 @@ describe(WorkflowHistoryEventDetails.name, () => {
148142

149143
setup({ eventDetails });
150144

151-
const jsonComponents = screen.getAllByTestId('group-details-json');
152-
expect(jsonComponents).toHaveLength(2);
153-
expect(screen.getByText(/JSON: path1 = "value1"/)).toBeInTheDocument();
145+
const panelEntries = screen.getAllByTestId('panel-details-entry');
146+
expect(panelEntries).toHaveLength(2);
154147
expect(
155-
screen.getByText(/JSON: path2 = \{"nested":"value2"\}/)
148+
screen.getByText(/Panel Entry: path1 = "value1"/)
149+
).toBeInTheDocument();
150+
expect(
151+
screen.getByText(/Panel Entry: path2 = \{"nested":"value2"\}/)
156152
).toBeInTheDocument();
157153
expect(screen.getByTestId('event-details-group')).toBeInTheDocument();
158154
expect(
159155
screen.getByText('Event Details Group (1 entries)')
160156
).toBeInTheDocument();
161157
});
162158

163-
it('passes correct props to WorkflowHistoryGroupDetailsJson', () => {
159+
it('passes correct props to WorkflowHistoryPanelDetailsEntry', () => {
164160
const eventDetails: EventDetailsEntries = [
165161
{
166162
key: 'key1',
@@ -178,7 +174,9 @@ describe(WorkflowHistoryEventDetails.name, () => {
178174

179175
setup({ eventDetails });
180176

181-
expect(screen.getByText(/JSON: path1 = "value1"/)).toBeInTheDocument();
177+
expect(
178+
screen.getByText(/Panel Entry: path1 = "value1"/)
179+
).toBeInTheDocument();
182180
expect(screen.getByText(/\(negative\)/)).toBeInTheDocument();
183181
});
184182
});

src/views/workflow-history-v2/workflow-history-event-details/workflow-history-event-details.tsx

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { useMemo } from 'react';
22

3-
import WorkflowHistoryEventDetailsEntry from '@/views/workflow-history/workflow-history-event-details-entry/workflow-history-event-details-entry';
43
import WorkflowHistoryEventDetailsGroup from '@/views/workflow-history/workflow-history-event-details-group/workflow-history-event-details-group';
54
import { type WorkflowPageParams } from '@/views/workflow-page/workflow-page.types';
65

6+
import WorkflowHistoryPanelDetailsEntry from '../workflow-history-panel-details-entry/workflow-history-panel-details-entry';
7+
78
import { styled } from './workflow-history-event-details.styles';
89
import { type EventDetailsEntries } from './workflow-history-event-details.types';
910

@@ -18,7 +19,7 @@ export default function WorkflowHistoryEventDetails({
1819
() =>
1920
eventDetails.reduce<[EventDetailsEntries, EventDetailsEntries]>(
2021
([panels, rest], entry) => {
21-
if (entry.renderConfig?.showInPanels && !entry.isGroup) {
22+
if (entry.renderConfig?.showInPanels) {
2223
panels.push(entry);
2324
} else {
2425
rest.push(entry);
@@ -42,24 +43,10 @@ export default function WorkflowHistoryEventDetails({
4243
{panelDetails.map((detail) => {
4344
return (
4445
<styled.PanelContainer key={detail.path}>
45-
{!detail.isGroup ? (
46-
<WorkflowHistoryEventDetailsEntry
47-
entryKey={detail.key}
48-
entryPath={detail.path}
49-
entryValue={detail.value}
50-
isNegative={detail.isNegative}
51-
renderConfig={detail.renderConfig}
52-
{...workflowPageParams}
53-
/>
54-
) : (
55-
<WorkflowHistoryEventDetailsGroup
56-
entries={restDetails}
57-
decodedPageUrlParams={{
58-
...workflowPageParams,
59-
workflowTab: 'history',
60-
}}
61-
/>
62-
)}
46+
<WorkflowHistoryPanelDetailsEntry
47+
detail={detail}
48+
{...workflowPageParams}
49+
/>
6350
</styled.PanelContainer>
6451
);
6552
})}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { styled as createStyled, type Theme } from 'baseui';
2+
3+
export const styled = {
4+
PanelContainer: createStyled<'div', { $isNegative?: boolean }>(
5+
'div',
6+
({ $theme, $isNegative }: { $theme: Theme; $isNegative?: boolean }) => ({
7+
padding: $theme.sizing.scale600,
8+
backgroundColor: $isNegative
9+
? $theme.colors.backgroundNegativeLight
10+
: $theme.colors.backgroundSecondary,
11+
borderRadius: $theme.borders.radius300,
12+
height: '100%',
13+
})
14+
),
15+
PanelLabel: createStyled<'div', { $isNegative?: boolean }>(
16+
'div',
17+
({ $theme, $isNegative }: { $theme: Theme; $isNegative?: boolean }) => ({
18+
color: $isNegative
19+
? $theme.colors.contentNegative
20+
: $theme.colors.contentPrimary,
21+
...$theme.typography.LabelSmall,
22+
})
23+
),
24+
PanelValue: createStyled<'div', { $isNegative?: boolean }>(
25+
'div',
26+
({ $theme, $isNegative }: { $theme: Theme; $isNegative?: boolean }) => ({
27+
padding: `${$theme.sizing.scale500} 0`,
28+
color: $isNegative
29+
? $theme.colors.contentNegative
30+
: $theme.colors.contentPrimary,
31+
})
32+
),
33+
};
Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,45 @@
1+
import WorkflowHistoryEventDetailsGroup from '@/views/workflow-history/workflow-history-event-details-group/workflow-history-event-details-group';
2+
3+
import { styled } from './workflow-history-panel-details-entry.styles';
14
import { type Props } from './workflow-history-panel-details-entry.types';
25

36
export default function WorkflowHistoryPanelDetailsEntry({
4-
entryKey,
5-
entryPath,
6-
entryValue,
7-
renderConfig,
8-
isNegative,
9-
...decodedPageUrlParams
7+
detail,
8+
...workflowPageParams
109
}: Props) {
11-
const ValueComponent = renderConfig?.valueComponent;
10+
const ValueComponent = detail.renderConfig?.valueComponent;
1211

13-
if (ValueComponent !== undefined) {
12+
if (ValueComponent !== undefined && !detail.isGroup) {
1413
return (
1514
<ValueComponent
16-
entryKey={entryKey}
17-
entryPath={entryPath}
18-
entryValue={entryValue}
19-
isNegative={isNegative}
20-
{...decodedPageUrlParams}
15+
entryKey={detail.key}
16+
entryPath={detail.path}
17+
entryValue={detail.value}
18+
isNegative={detail.isNegative}
19+
{...workflowPageParams}
2120
/>
2221
);
2322
}
2423

25-
return String(entryValue);
24+
return (
25+
<styled.PanelContainer $isNegative={detail.isNegative}>
26+
<styled.PanelLabel $isNegative={detail.isNegative}>
27+
{detail.path}
28+
</styled.PanelLabel>
29+
<styled.PanelValue $isNegative={detail.isNegative}>
30+
{detail.isGroup ? (
31+
<WorkflowHistoryEventDetailsGroup
32+
entries={detail.groupEntries}
33+
parentGroupPath={detail.path}
34+
decodedPageUrlParams={{
35+
...workflowPageParams,
36+
workflowTab: 'history',
37+
}}
38+
/>
39+
) : (
40+
detail.value
41+
)}
42+
</styled.PanelValue>
43+
</styled.PanelContainer>
44+
);
2645
}
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import { type WorkflowPageParams } from '@/views/workflow-page/workflow-page.types';
2+
13
import {
2-
type EventDetailsConfig,
3-
type EventDetailsValueComponentProps,
4+
type EventDetailsGroupEntry,
5+
type EventDetailsSingleEntry,
46
} from '../workflow-history-event-details/workflow-history-event-details.types';
57

6-
export type Props = EventDetailsValueComponentProps & {
7-
renderConfig: EventDetailsConfig | null;
8-
};
8+
export type Props = {
9+
detail: EventDetailsSingleEntry | EventDetailsGroupEntry;
10+
} & WorkflowPageParams;

src/views/workflow-history/config/workflow-history-event-details.config.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,32 @@ import { type WorkflowHistoryEventDetailsConfig } from '../workflow-history-even
99
import WorkflowHistoryEventDetailsJson from '../workflow-history-event-details-json/workflow-history-event-details-json';
1010
import WorkflowHistoryEventDetailsPlaceholderText from '../workflow-history-event-details-placeholder-text/workflow-history-event-details-placeholder-text';
1111

12+
/**
13+
* Configuration array for customizing how workflow history event details are rendered.
14+
* Each config entry defines matching criteria and rendering behavior for specific event fields.
15+
* Configs are evaluated in order, and the first matching config is applied to each field.
16+
*/
1217
const workflowHistoryEventDetailsConfig = [
18+
/**
19+
* Hides fields with null or undefined values from the event details display.
20+
*/
1321
{
1422
name: 'Filter empty value',
1523
customMatcher: ({ value }) => value === null || value === undefined,
1624
hide: () => true,
1725
},
26+
/**
27+
* Hides internal fields (taskId, eventType) that are not useful for display.
28+
*/
1829
{
1930
name: 'Filter unneeded values',
2031
pathRegex: '(taskId|eventType)$',
2132
hide: () => true,
2233
},
34+
/**
35+
* Displays a placeholder text for timeout/retry fields that are set to 0 (not configured).
36+
* Also removes the "Seconds" suffix from labels since formatted durations may be in minutes/hours.
37+
*/
2338
{
2439
name: 'Not set placeholder',
2540
customMatcher: ({ value, path }) => {
@@ -34,11 +49,17 @@ const workflowHistoryEventDetailsConfig = [
3449
valueComponent: () =>
3550
createElement(WorkflowHistoryEventDetailsPlaceholderText),
3651
},
52+
/**
53+
* Formats Date objects as human-readable time strings.
54+
*/
3755
{
3856
name: 'Date object as time string',
3957
customMatcher: ({ value }) => value instanceof Date,
4058
valueComponent: ({ entryValue }) => formatDate(entryValue),
4159
},
60+
/**
61+
* Renders task list names as clickable links that navigate to the task list view.
62+
*/
4263
{
4364
name: 'Tasklists as links',
4465
key: 'taskList',
@@ -50,20 +71,32 @@ const workflowHistoryEventDetailsConfig = [
5071
});
5172
},
5273
},
74+
/**
75+
* Renders JSON fields (input, result, details, etc.) as formatted PrettyJson components.
76+
* Uses forceWrap to ensure proper wrapping of long JSON content.
77+
*/
5378
{
5479
name: 'Json as PrettyJson',
5580
pathRegex:
5681
'(input|result|details|failureDetails|Error|lastCompletionResult|heartbeatDetails|lastFailureDetails)$',
5782
valueComponent: WorkflowHistoryEventDetailsJson,
5883
forceWrap: true,
5984
},
85+
/**
86+
* Formats duration fields (ending in TimeoutSeconds, BackoffSeconds, or InSeconds) as human-readable durations.
87+
* Removes the "Seconds" suffix from labels since formatted durations may be in minutes/hours.
88+
*/
6089
{
6190
name: 'Duration & interval seconds',
6291
pathRegex: '(TimeoutSeconds|BackoffSeconds|InSeconds)$',
6392
getLabel: ({ key }) => key.replace(/InSeconds|Seconds|$/, ''), // remove seconds suffix from label as formatted duration can be minutes/hours etc.
6493
valueComponent: ({ entryValue }) =>
6594
formatDuration({ seconds: entryValue > 0 ? entryValue : 0, nanos: 0 }),
6695
},
96+
/**
97+
* Renders workflow execution objects as clickable links that navigate to the workflow view.
98+
* Applies to parentWorkflowExecution, externalWorkflowExecution, and workflowExecution fields.
99+
*/
67100
{
68101
name: 'WorkflowExecution as link',
69102
pathRegex:
@@ -77,6 +110,10 @@ const workflowHistoryEventDetailsConfig = [
77110
});
78111
},
79112
},
113+
/**
114+
* Renders run ID fields as clickable links that navigate to the corresponding workflow run.
115+
* Applies to firstExecutionRunId, originalExecutionRunId, newExecutionRunId, and continuedExecutionRunId.
116+
*/
80117
{
81118
name: 'RunIds as link',
82119
pathRegex:
@@ -90,6 +127,9 @@ const workflowHistoryEventDetailsConfig = [
90127
});
91128
},
92129
},
130+
/**
131+
* Renames the "attempt" field label to "retryAttempt" for better clarity.
132+
*/
93133
{
94134
name: 'Retry config attempt as retryAttempt',
95135
key: 'attempt',

0 commit comments

Comments
 (0)