Skip to content

Commit eb7aab4

Browse files
Merge branch 'master' into add-retries-to-summary-fields
2 parents ad922a4 + dd85890 commit eb7aab4

File tree

7 files changed

+155
-68
lines changed

7 files changed

+155
-68
lines changed

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,31 @@ describe('WorkflowHistory', () => {
190190
});
191191
});
192192

193-
it('should show no results when filtered events are empty', async () => {
194-
await setup({ emptyEvents: true });
193+
it('should show no results when filtered events are empty and no next page', async () => {
194+
await setup({ emptyEvents: true, hasNextPage: false });
195195
expect(await screen.findByText('No Results')).toBeInTheDocument();
196196
});
197197

198+
it('should not show no results when filtered events are empty but has next page', async () => {
199+
await setup({
200+
emptyEvents: true,
201+
hasNextPage: true,
202+
});
203+
204+
// Should not show "No Results" when there's a next page
205+
expect(screen.queryByText('No Results')).not.toBeInTheDocument();
206+
207+
// Should show the load more footer component instead
208+
expect(screen.getByText('Load more')).toBeInTheDocument();
209+
});
210+
211+
it('should not show no results when there are filtered events', async () => {
212+
await setup({});
213+
await waitFor(() => {
214+
expect(screen.queryByText('No Results')).not.toBeInTheDocument();
215+
});
216+
});
217+
198218
it('should show ungrouped table when ungrouped view is enabled', async () => {
199219
setup({ pageQueryParamsValues: { ungroupedHistoryViewEnabled: true } });
200220
expect(await screen.findByText('Ungrouped Table')).toBeInTheDocument();

src/views/workflow-history/helpers/__tests__/workflow-history-grouper.test.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,31 @@ describe(WorkflowHistoryGrouper.name, () => {
471471
expect(Object.keys(groups).length).toBe(0);
472472
});
473473

474+
it('should notify subscribers after pending events are processed', async () => {
475+
const { grouper, waitForProcessing } = setup();
476+
const scheduledEvent = createScheduleActivityEvent('7');
477+
grouper.updateEvents([scheduledEvent]);
478+
await waitForProcessing();
479+
// subscribe to state changes
480+
const mockOnChange = jest.fn();
481+
grouper.onChange(mockOnChange);
482+
// add pending event on history after scheduled event is processed and onChange is subscribed
483+
await grouper.updatePendingEvents({
484+
pendingStartActivities: [pendingActivityTaskStartEvent],
485+
pendingStartDecision: null,
486+
});
487+
488+
await waitForProcessing();
489+
expect(mockOnChange).toHaveBeenCalled();
490+
491+
const callArgs = mockOnChange.mock.calls[0][0];
492+
expect(callArgs.groups['7']).toBeDefined();
493+
expect(callArgs.groups['7'].events).toEqual([
494+
scheduledEvent,
495+
pendingActivityTaskStartEvent,
496+
]);
497+
});
498+
474499
it('should handle pending decision buffer clearing when decision changes', async () => {
475500
const { grouper, waitForProcessing } = setup();
476501

src/views/workflow-history/helpers/workflow-history-grouper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,10 @@ export default class WorkflowHistoryGrouper {
413413
);
414414

415415
this.updatePendingDecision(currentPendingDecision, newPendingDecision);
416+
417+
// Report progress to all subscribers
418+
const state = this.getState();
419+
this.subscribers.forEach((callback) => callback(state));
416420
}
417421

418422
/**

src/views/workflow-history/hooks/__tests__/use-initial-selected-event.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,34 @@ describe('useInitialSelectedEvent', () => {
190190
expect(result.current.initialEventFound).toBe(true);
191191
expect(result.current.initialEventGroupIndex).toBe(3);
192192
});
193+
194+
// add test cases for filteredEventGroupsEntries being empty after filtering
195+
it.only('should return initialEventGroupIndex as undefined when filteredEventGroupsEntries no longer contains the event', () => {
196+
const initialFilteredEventGroupsEntries: [string, HistoryEventsGroup][] = [
197+
['2', mockEventGroups['2']],
198+
];
199+
200+
// initial render with filteredEventGroupsEntries containing the event
201+
const { result, rerender } = renderHook(
202+
({
203+
filteredEventGroupsEntries = initialFilteredEventGroupsEntries,
204+
}: {
205+
filteredEventGroupsEntries?: [string, HistoryEventsGroup][];
206+
} = {}) =>
207+
useInitialSelectedEvent({
208+
selectedEventId: '2',
209+
eventGroups: mockEventGroups,
210+
filteredEventGroupsEntries: filteredEventGroupsEntries,
211+
})
212+
);
213+
expect(result.current.initialEventFound).toBe(true);
214+
expect(result.current.initialEventGroupIndex).toBe(0);
215+
216+
//rerender with empty filteredEventGroupsEntries no longer containing the event
217+
rerender({
218+
filteredEventGroupsEntries: [],
219+
});
220+
expect(result.current.initialEventGroupIndex).toBe(undefined);
221+
expect(result.current.initialEventFound).toBe(true);
222+
});
193223
});

src/views/workflow-history/hooks/use-initial-selected-event.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ import { useMemo, useRef, useState } from 'react';
33
import { type UseInitialSelectedEventParams } from './use-initial-selected-event.types';
44

55
/*
6-
* This hook is used to search for the event and the group of the event that
7-
* was selected when the component is mounted. It returns a boolean indicating if the
8-
* initial event should be searched for, a boolean indicating if the initial
9-
* event was found, and the index of the group that contains the event.
6+
* This hook is used to search for the event within event groups. It captures the first selected event when the component is mounted
7+
* and keeps searching for the event in all event groups until it is found.
8+
* Afterwards it return the index of the group in the filtered set of event groups that is presented on the UI and retrun undefined if filtered groups do not contain the event/group.
109
*/
1110
export default function useInitialSelectedEvent({
1211
selectedEventId,
@@ -35,7 +34,7 @@ export default function useInitialSelectedEvent({
3534
// If group index not change do not search again.
3635
if (
3736
foundGroupIndexRef.current &&
38-
filteredEventGroupsEntries[foundGroupIndexRef.current][0] === groupId
37+
filteredEventGroupsEntries[foundGroupIndexRef.current]?.[0] === groupId
3938
)
4039
return foundGroupIndexRef.current;
4140

src/views/workflow-history/hooks/use-workflow-history-fetcher.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export default function useWorkflowHistoryFetcher(
2525
) {
2626
const queryClient = useQueryClient();
2727
const fetcherRef = useRef<WorkflowHistoryFetcher | null>(null);
28-
const lastFlattenedPagesCountRef = useRef<number>(-1);
2928

3029
if (!fetcherRef.current) {
3130
fetcherRef.current = new WorkflowHistoryFetcher(queryClient, params);
@@ -46,12 +45,13 @@ export default function useWorkflowHistoryFetcher(
4645

4746
useEffect(() => {
4847
if (!fetcherRef.current) return;
48+
let lastFlattenedPagesCount = -1;
4949
const unsubscribe = fetcherRef.current.onChange((state) => {
5050
const pagesCount = state.data?.pages?.length || 0;
5151
// If the pages count is greater than the last flattened pages count, then we need to flatten the pages and call the onEventsChange callback
5252
// Depending on ref variable instead of historyQuery is because historyQuery is throttled.
53-
if (pagesCount > lastFlattenedPagesCountRef.current) {
54-
lastFlattenedPagesCountRef.current = pagesCount;
53+
if (pagesCount > lastFlattenedPagesCount) {
54+
lastFlattenedPagesCount = pagesCount;
5555
onEventsChange(
5656
state.data?.pages?.flatMap((page) => page.history?.events || []) || []
5757
);

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

Lines changed: 67 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ export default function WorkflowHistory({ params }: Props) {
324324
return <SectionLoadingIndicator />;
325325
}
326326

327+
const showHasNoResults =
328+
filteredEventGroupsEntries.length === 0 && !hasNextPage;
329+
327330
return (
328331
<div className={cls.container}>
329332
<WorkflowHistoryHeader
@@ -387,7 +390,7 @@ export default function WorkflowHistory({ params }: Props) {
387390
}}
388391
/>
389392
<PageSection className={cls.contentSection}>
390-
{filteredEventGroupsEntries.length > 0 && (
393+
{!showHasNoResults && (
391394
<>
392395
{isUngroupedHistoryViewEnabled ? (
393396
<section className={cls.ungroupedEventsContainer}>
@@ -414,63 +417,69 @@ export default function WorkflowHistory({ params }: Props) {
414417
</section>
415418
) : (
416419
<div className={cls.eventsContainer}>
417-
<div role="list" className={cls.compactSection}>
418-
<Virtuoso
419-
data={filteredEventGroupsEntries}
420-
ref={compactSectionListRef}
421-
rangeChanged={({ startIndex, endIndex }) =>
422-
setTimelineListVisibleRange((currentRanges) => ({
423-
...currentRanges,
424-
compactStartIndex: startIndex,
425-
compactEndIndex: endIndex,
426-
}))
427-
}
428-
{...(initialEventGroupIndex === undefined
429-
? {}
430-
: {
431-
initialTopMostItemIndex: initialEventGroupIndex,
432-
})}
433-
itemContent={(index, [groupId, group]) => (
434-
<div role="listitem" className={cls.compactCardContainer}>
435-
<WorkflowHistoryCompactEventCard
436-
key={groupId}
437-
{...group}
438-
statusReady={
439-
!group.hasMissingEvents ||
440-
reachedEndOfAvailableHistory
441-
}
442-
workflowCloseStatus={
443-
workflowExecutionInfo?.closeStatus
444-
}
445-
workflowIsArchived={
446-
workflowExecutionInfo?.isArchived || false
447-
}
448-
workflowCloseTimeMs={workflowCloseTimeMs}
449-
showLabelPlaceholder={!group.label}
450-
selected={group.events.some(
451-
(e) =>
452-
e.eventId === queryParams.historySelectedEventId
453-
)}
454-
disabled={!Boolean(group.events[0].eventId)}
455-
onClick={() => {
456-
if (group.events[0].eventId)
457-
setQueryParams({
458-
historySelectedEventId: group.events[0].eventId,
420+
{filteredEventGroupsEntries.length > 0 && (
421+
<div role="list" className={cls.compactSection}>
422+
<Virtuoso
423+
data={filteredEventGroupsEntries}
424+
ref={compactSectionListRef}
425+
rangeChanged={({ startIndex, endIndex }) =>
426+
setTimelineListVisibleRange((currentRanges) => ({
427+
...currentRanges,
428+
compactStartIndex: startIndex,
429+
compactEndIndex: endIndex,
430+
}))
431+
}
432+
{...(initialEventGroupIndex === undefined
433+
? {}
434+
: {
435+
initialTopMostItemIndex: initialEventGroupIndex,
436+
})}
437+
itemContent={(index, [groupId, group]) => (
438+
<div
439+
role="listitem"
440+
className={cls.compactCardContainer}
441+
>
442+
<WorkflowHistoryCompactEventCard
443+
key={groupId}
444+
{...group}
445+
statusReady={
446+
!group.hasMissingEvents ||
447+
reachedEndOfAvailableHistory
448+
}
449+
workflowCloseStatus={
450+
workflowExecutionInfo?.closeStatus
451+
}
452+
workflowIsArchived={
453+
workflowExecutionInfo?.isArchived || false
454+
}
455+
workflowCloseTimeMs={workflowCloseTimeMs}
456+
showLabelPlaceholder={!group.label}
457+
selected={group.events.some(
458+
(e) =>
459+
e.eventId === queryParams.historySelectedEventId
460+
)}
461+
disabled={!Boolean(group.events[0].eventId)}
462+
onClick={() => {
463+
if (group.events[0].eventId)
464+
setQueryParams({
465+
historySelectedEventId:
466+
group.events[0].eventId,
467+
});
468+
timelineSectionListRef.current?.scrollToIndex({
469+
index,
470+
align: 'start',
471+
behavior: 'auto',
459472
});
460-
timelineSectionListRef.current?.scrollToIndex({
461-
index,
462-
align: 'start',
463-
behavior: 'auto',
464-
});
465-
}}
466-
/>
467-
</div>
468-
)}
469-
endReached={() => {
470-
manualFetchNextPage();
471-
}}
472-
/>
473-
</div>
473+
}}
474+
/>
475+
</div>
476+
)}
477+
endReached={() => {
478+
manualFetchNextPage();
479+
}}
480+
/>
481+
</div>
482+
)}
474483
<section className={cls.timelineSection}>
475484
<Virtuoso
476485
useWindowScroll
@@ -542,7 +551,7 @@ export default function WorkflowHistory({ params }: Props) {
542551
)}
543552
</>
544553
)}
545-
{filteredEventGroupsEntries.length === 0 && (
554+
{showHasNoResults && (
546555
<div className={cls.noResultsContainer}>No Results</div>
547556
)}
548557
{resetToDecisionEventId && (

0 commit comments

Comments
 (0)