Skip to content

Commit ba49bcf

Browse files
authored
Show duration badge on timeline event groups (#911)
1 parent 0fc16cd commit ba49bcf

File tree

4 files changed

+59
-6
lines changed

4 files changed

+59
-6
lines changed

src/views/workflow-history/workflow-history-timeline-group/__tests__/workflow-history-timeline-group.test.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { render, screen, userEvent } from '@/test-utils/rtl';
33
import { startWorkflowExecutionEvent } from '../../__fixtures__/workflow-history-single-events';
44
import type WorkflowHistoryEventStatusBadge from '../../workflow-history-event-status-badge/workflow-history-event-status-badge';
55
import type WorkflowHistoryEventsCard from '../../workflow-history-events-card/workflow-history-events-card';
6+
import type WorkflowHistoryEventsDurationBadge from '../../workflow-history-events-duration-badge/workflow-history-events-duration-badge';
67
import type WorkflowHistoryTimelineResetButton from '../../workflow-history-timeline-reset-button/workflow-history-timeline-reset-button';
78
import WorkflowHistoryTimelineGroup from '../workflow-history-timeline-group';
89
import { type styled } from '../workflow-history-timeline-group.styles';
@@ -13,6 +14,11 @@ jest.mock<typeof WorkflowHistoryEventStatusBadge>(
1314
() => jest.fn((props) => <div>{props.status}</div>)
1415
);
1516

17+
jest.mock<typeof WorkflowHistoryEventsDurationBadge>(
18+
'../../workflow-history-events-duration-badge/workflow-history-events-duration-badge',
19+
() => jest.fn(() => <div>Duration Badge</div>)
20+
);
21+
1622
jest.mock<typeof WorkflowHistoryEventsCard>(
1723
'../../workflow-history-events-card/workflow-history-events-card',
1824
() => jest.fn(() => <div>Events Card</div>)
@@ -108,6 +114,16 @@ describe('WorkflowHistoryTimelineGroup', () => {
108114

109115
expect(mockOnReset).toHaveBeenCalledTimes(1);
110116
});
117+
118+
it('renders duration badge when timeMs is provided', () => {
119+
setup({ timeMs: 1726652232190.7927 });
120+
expect(screen.getByText('Duration Badge')).toBeInTheDocument();
121+
});
122+
123+
it('does not render duration badge when timeMs is not provided', () => {
124+
setup({ timeMs: null });
125+
expect(screen.queryByText('Duration Badge')).not.toBeInTheDocument();
126+
});
111127
});
112128

113129
function setup({
@@ -134,6 +150,10 @@ function setup({
134150
},
135151
badges,
136152
resetToDecisionEventId,
153+
workflowCloseStatus,
154+
workflowIsArchived = false,
155+
workflowCloseTimeMs = null,
156+
timeMs = null,
137157
}: Partial<Props>) {
138158
const mockOnReset = jest.fn();
139159
const user = userEvent.setup();
@@ -152,6 +172,10 @@ function setup({
152172
badges={badges}
153173
resetToDecisionEventId={resetToDecisionEventId}
154174
onReset={mockOnReset}
175+
workflowCloseStatus={workflowCloseStatus}
176+
workflowIsArchived={workflowIsArchived}
177+
workflowCloseTimeMs={workflowCloseTimeMs}
178+
timeMs={timeMs}
155179
/>
156180
);
157181
return { mockOnReset, user };

src/views/workflow-history/workflow-history-timeline-group/workflow-history-timeline-group.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import useStyletronClasses from '@/hooks/use-styletron-classes';
77

88
import WorkflowHistoryEventStatusBadge from '../workflow-history-event-status-badge/workflow-history-event-status-badge';
99
import WorkflowHistoryEventsCard from '../workflow-history-events-card/workflow-history-events-card';
10+
import WorkflowHistoryEventsDurationBadge from '../workflow-history-events-duration-badge/workflow-history-events-duration-badge';
1011
import WorkflowHistoryTimelineResetButton from '../workflow-history-timeline-reset-button/workflow-history-timeline-reset-button';
1112

1213
import {
@@ -20,6 +21,11 @@ export default function WorkflowHistoryTimelineGroup({
2021
status,
2122
label,
2223
timeLabel,
24+
timeMs,
25+
closeTimeMs,
26+
workflowCloseTimeMs,
27+
workflowCloseStatus,
28+
workflowIsArchived,
2329
events,
2430
isLastEvent,
2531
eventsMetadata,
@@ -65,6 +71,17 @@ export default function WorkflowHistoryTimelineGroup({
6571
))}
6672
</>
6773
)}
74+
{timeMs && (
75+
<WorkflowHistoryEventsDurationBadge
76+
startTime={timeMs}
77+
closeTime={closeTimeMs}
78+
eventsCount={events.length}
79+
hasMissingEvents={hasMissingEvents}
80+
workflowCloseTime={workflowCloseTimeMs}
81+
workflowIsArchived={workflowIsArchived}
82+
workflowCloseStatus={workflowCloseStatus}
83+
/>
84+
)}
6885
<div suppressHydrationWarning className={cls.eventsTime}>
6986
{timeLabel}
7087
</div>

src/views/workflow-history/workflow-history-timeline-group/workflow-history-timeline-group.types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { type WorkflowExecutionCloseStatus } from '@/__generated__/proto-ts/uber/cadence/api/v1/WorkflowExecutionCloseStatus';
2+
13
import {
24
type GetIsEventExpanded,
35
type ToggleIsEventExpanded,
@@ -17,11 +19,16 @@ export type Props = Pick<
1719
| 'status'
1820
| 'badges'
1921
| 'resetToDecisionEventId'
22+
| 'timeMs'
23+
| 'closeTimeMs'
2024
> & {
2125
isLastEvent: boolean;
2226
decodedPageUrlParams: WorkflowHistoryProps['params'];
2327
getIsEventExpanded: GetIsEventExpanded;
2428
onEventToggle: ToggleIsEventExpanded;
2529
onReset?: () => void;
2630
selected?: boolean;
31+
workflowIsArchived: boolean;
32+
workflowCloseStatus?: WorkflowExecutionCloseStatus | null;
33+
workflowCloseTimeMs?: number | null;
2734
};

src/views/workflow-history/workflow-history.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import SectionLoadingIndicator from '@/components/section-loading-indicator/sect
1919
import useStyletronClasses from '@/hooks/use-styletron-classes';
2020
import useThrottledState from '@/hooks/use-throttled-state';
2121
import { type GetWorkflowHistoryResponse } from '@/route-handlers/get-workflow-history/get-workflow-history.types';
22+
import parseGrpcTimestamp from '@/utils/datetime/parse-grpc-timestamp';
2223
import decodeUrlParams from '@/utils/decode-url-params';
2324
import request from '@/utils/request';
2425
import { type RequestError } from '@/utils/request/request-error';
@@ -177,6 +178,10 @@ export default function WorkflowHistory({ params }: Props) {
177178
compactEndIndex: -1,
178179
});
179180

181+
const wokflowCloseTimeMs = workflowExecutionInfo?.closeTime
182+
? parseGrpcTimestamp(workflowExecutionInfo?.closeTime)
183+
: null;
184+
180185
// search for the event selected in the URL on initial page load
181186
const {
182187
initialEventFound,
@@ -410,12 +415,7 @@ export default function WorkflowHistory({ params }: Props) {
410415
itemContent={(index, [groupId, group]) => (
411416
<WorkflowHistoryTimelineGroup
412417
key={groupId}
413-
status={group.status}
414-
label={group.label}
415-
timeLabel={group.timeLabel}
416-
events={group.events}
417-
eventsMetadata={group.eventsMetadata}
418-
badges={group.badges}
418+
{...group}
419419
hasMissingEvents={
420420
group.hasMissingEvents && !reachedAvailableHistoryEnd
421421
}
@@ -432,6 +432,11 @@ export default function WorkflowHistory({ params }: Props) {
432432
selected={group.events.some(
433433
(e) => e.eventId === queryParams.historySelectedEventId
434434
)}
435+
workflowCloseStatus={workflowExecutionInfo?.closeStatus}
436+
workflowIsArchived={
437+
workflowExecutionInfo?.isArchived || false
438+
}
439+
workflowCloseTimeMs={wokflowCloseTimeMs}
435440
/>
436441
)}
437442
components={{

0 commit comments

Comments
 (0)