Skip to content

Commit e76050b

Browse files
authored
Expand all toggle (#704)
* expand all toggle * update EventCollapseState to EventExpansionState
1 parent 662a7f1 commit e76050b

13 files changed

+459
-61
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { renderHook, act } from '@/test-utils/rtl';
2+
3+
import useEventExpansionToggle from '../use-event-expansion-toggle';
4+
5+
describe('useEventExpansionToggle', () => {
6+
const mockVisibleEvents = [
7+
{ eventId: '1' },
8+
{ eventId: '2' },
9+
{ eventId: '3' },
10+
];
11+
12+
it('should initialize with provided initialState as true (all events expanded)', () => {
13+
const { result } = renderHook(() =>
14+
useEventExpansionToggle({
15+
initialState: true,
16+
visibleEvents: mockVisibleEvents,
17+
})
18+
);
19+
expect(result.current.expandedEvents).toBe(true);
20+
});
21+
22+
it('should initialize with provided initialState as an object with specific events expanded', () => {
23+
const { result } = renderHook(() =>
24+
useEventExpansionToggle({
25+
initialState: { '1': true, '2': false },
26+
visibleEvents: mockVisibleEvents,
27+
})
28+
);
29+
expect(result.current.expandedEvents).toEqual({ '1': true, '2': false });
30+
});
31+
32+
it('should toggle all events expansion state', () => {
33+
const { result } = renderHook(() =>
34+
useEventExpansionToggle({
35+
initialState: { '1': true, '2': false },
36+
visibleEvents: mockVisibleEvents,
37+
})
38+
);
39+
40+
act(() => {
41+
result.current.toggleIsExpandAllEvents();
42+
});
43+
expect(result.current.expandedEvents).toBe(true);
44+
45+
act(() => {
46+
result.current.toggleIsExpandAllEvents();
47+
});
48+
expect(result.current.expandedEvents).toEqual({});
49+
});
50+
51+
it('should expand a specific event when toggled', () => {
52+
const { result } = renderHook(() =>
53+
useEventExpansionToggle({
54+
initialState: {},
55+
visibleEvents: mockVisibleEvents,
56+
})
57+
);
58+
59+
act(() => {
60+
result.current.toggleIsEventExpanded('1');
61+
});
62+
expect(result.current.expandedEvents).toEqual({ '1': true });
63+
});
64+
65+
it('should collapse a specific event when toggled if already expanded', () => {
66+
const { result } = renderHook(() =>
67+
useEventExpansionToggle({
68+
initialState: { '1': true },
69+
visibleEvents: mockVisibleEvents,
70+
})
71+
);
72+
73+
act(() => {
74+
result.current.toggleIsEventExpanded('1');
75+
});
76+
expect(result.current.expandedEvents).toEqual({});
77+
});
78+
79+
it('should collapse only the toggled event when all events are expanded', () => {
80+
const { result } = renderHook(() =>
81+
useEventExpansionToggle({
82+
initialState: true,
83+
visibleEvents: mockVisibleEvents,
84+
})
85+
);
86+
87+
act(() => {
88+
result.current.toggleIsEventExpanded('1');
89+
});
90+
91+
expect(result.current.expandedEvents).toEqual({
92+
'2': true,
93+
'3': true,
94+
});
95+
});
96+
97+
it('should expand all events when each event has been individually expanded', () => {
98+
const { result } = renderHook(() =>
99+
useEventExpansionToggle({
100+
initialState: {},
101+
visibleEvents: mockVisibleEvents,
102+
})
103+
);
104+
105+
act(() => {
106+
result.current.toggleIsEventExpanded('1');
107+
result.current.toggleIsEventExpanded('2');
108+
result.current.toggleIsEventExpanded('3');
109+
});
110+
111+
expect(result.current.expandedEvents).toBe(true);
112+
});
113+
114+
it('should check if an event is expanded using getIsEventExpanded', () => {
115+
const { result } = renderHook(() =>
116+
useEventExpansionToggle({
117+
initialState: { '1': true, '2': false },
118+
visibleEvents: mockVisibleEvents,
119+
})
120+
);
121+
122+
expect(result.current.getIsEventExpanded('1')).toBe(true);
123+
expect(result.current.getIsEventExpanded('2')).toBe(false);
124+
expect(result.current.getIsEventExpanded('3')).toBe(false);
125+
});
126+
});
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { useCallback, useState } from 'react';
2+
3+
import omit from 'lodash/omit';
4+
5+
import { type HistoryEvent } from '@/__generated__/proto-ts/uber/cadence/api/v1/HistoryEvent';
6+
7+
import {
8+
type EventExpansionState,
9+
type GetIsEventExpanded,
10+
type ToggleIsEventExpanded,
11+
type ToggleIsExpandAllEvents,
12+
} from './use-event-expansion-toggle.types';
13+
14+
export default function useEventExpansionToggle({
15+
initialState = {},
16+
visibleEvents,
17+
}: {
18+
initialState?: EventExpansionState;
19+
visibleEvents: Pick<HistoryEvent, 'eventId'>[];
20+
}) {
21+
const [expandedEvents, setExpandedEvents] =
22+
useState<EventExpansionState>(initialState);
23+
24+
const isExpandAllEvents = expandedEvents === true;
25+
const toggleIsExpandAllEvents = useCallback<ToggleIsExpandAllEvents>(() => {
26+
setExpandedEvents((prev) => {
27+
if (prev === true) {
28+
return {};
29+
}
30+
31+
return true;
32+
});
33+
}, []);
34+
35+
const getIsEventExpanded = useCallback<GetIsEventExpanded>(
36+
(eventId: string) => {
37+
if (expandedEvents === true) {
38+
return true;
39+
}
40+
41+
return Boolean(expandedEvents[eventId]);
42+
},
43+
[expandedEvents]
44+
);
45+
46+
const toggleIsEventExpanded = useCallback<ToggleIsEventExpanded>(
47+
(eventId: string) => {
48+
setExpandedEvents((prev) => {
49+
let newState: Record<string, boolean>;
50+
if (prev === true) {
51+
const retainedExpansion = visibleEvents.reduce(
52+
(result, { eventId }) => {
53+
result[eventId] = true;
54+
return result;
55+
},
56+
{} as Record<string, boolean>
57+
);
58+
newState = omit(retainedExpansion, eventId);
59+
} else {
60+
if (prev[eventId] === true) {
61+
newState = omit(prev, eventId);
62+
} else {
63+
newState = {
64+
...prev,
65+
[eventId]: true,
66+
};
67+
}
68+
}
69+
if (visibleEvents.every(({ eventId }) => newState[eventId])) {
70+
return true;
71+
}
72+
return newState;
73+
});
74+
},
75+
[visibleEvents]
76+
);
77+
78+
return {
79+
expandedEvents,
80+
setExpandedEvents,
81+
82+
isExpandAllEvents,
83+
toggleIsExpandAllEvents,
84+
85+
getIsEventExpanded,
86+
toggleIsEventExpanded,
87+
};
88+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export type EventExpansionState = Record<string, boolean> | true;
2+
3+
export type ToggleIsExpandAllEvents = () => void;
4+
5+
export type GetIsEventExpanded = (eventId: string) => boolean;
6+
7+
export type OnEventExpandToggle = (eventId: string) => void;
8+
9+
export type ToggleIsEventExpanded = (eventId: string) => void;

0 commit comments

Comments
 (0)