Skip to content

Commit b80e89f

Browse files
committed
refactor: split container publish history groups by creation time
1 parent e7d4498 commit b80e89f

3 files changed

Lines changed: 132 additions & 21 deletions

File tree

src/library-authoring/generic/history-log/HistoryLog.test.tsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,4 +249,62 @@ describe('<HistoryContainerLog />', () => {
249249
renderContainerComponent(mockLibraryContainerDraftHistory.containerKey);
250250
expect(await findByDeepTextContent(/author created.*Introduction to Testing Unit 1/i)).toBeInTheDocument();
251251
});
252+
253+
it('renders publish groups after container creation before the created entry', async () => {
254+
// Default mock: publishedAt '2026-03-14' is after createdAt '2024-01-01'
255+
renderContainerComponent(mockLibraryContainerDraftHistory.containerKey);
256+
const publishGroup = await findByDeepTextContent(/container_author published.*Intro Unit/i);
257+
const createdEntry = await findByDeepTextContent(/author created.*Introduction to Testing Unit 1/i);
258+
expect(publishGroup.compareDocumentPosition(createdEntry)).toBe(Node.DOCUMENT_POSITION_FOLLOWING);
259+
});
260+
261+
it('renders publish groups before container creation after the created entry', async () => {
262+
const originalData = mockLibraryContainerPublishHistory.data;
263+
mockLibraryContainerPublishHistory.data = [
264+
{
265+
...originalData[0],
266+
publishedAt: '2023-01-01T00:00:00Z', // before createdAt '2024-01-01'
267+
},
268+
];
269+
renderContainerComponent(mockLibraryContainerDraftHistory.containerKey);
270+
const createdEntry = await findByDeepTextContent(/author created.*Introduction to Testing Unit 1/i);
271+
expect(await screen.findByText(/2 authors contributed/i)).toBeInTheDocument();
272+
// The publish group contributors should still appear
273+
const contributorsText = screen.getByText(/2 authors contributed/i);
274+
expect(createdEntry.compareDocumentPosition(contributorsText)).toBe(Node.DOCUMENT_POSITION_FOLLOWING);
275+
mockLibraryContainerPublishHistory.data = originalData;
276+
});
277+
278+
it('renders both pre-creation and post-creation publish groups in correct order', async () => {
279+
const originalData = mockLibraryContainerPublishHistory.data;
280+
mockLibraryContainerPublishHistory.data = [
281+
{
282+
...originalData[0],
283+
publishLogUuid: 'after-uuid',
284+
publishedAt: '2026-01-01T00:00:00Z', // after createdAt '2024-01-01'
285+
directPublishedEntities: [
286+
{ ...originalData[0].directPublishedEntities[0], entityKey: 'key-after', title: 'After Unit' },
287+
],
288+
contributors: [],
289+
},
290+
{
291+
...originalData[0],
292+
publishLogUuid: 'before-uuid',
293+
publishedAt: '2023-01-01T00:00:00Z', // before createdAt '2024-01-01'
294+
directPublishedEntities: [
295+
{ ...originalData[0].directPublishedEntities[0], entityKey: 'key-before', title: 'Before Unit' },
296+
],
297+
contributors: [],
298+
},
299+
];
300+
renderContainerComponent(mockLibraryContainerDraftHistory.containerKey);
301+
const afterGroup = await findByDeepTextContent(/container_author published.*After Unit/i);
302+
const createdEntry = await findByDeepTextContent(/author created.*Introduction to Testing Unit 1/i);
303+
// After-creation group comes before the created entry
304+
expect(afterGroup.compareDocumentPosition(createdEntry)).toBe(Node.DOCUMENT_POSITION_FOLLOWING);
305+
// Before-creation group title is hidden (hideLogVert=true, no contributors), so only the vert line renders
306+
// Verify the after-group is visible but before-group title is not
307+
expect(screen.queryByText(/container_author published.*Before Unit/i)).not.toBeInTheDocument();
308+
mockLibraryContainerPublishHistory.data = originalData;
309+
});
252310
});

src/library-authoring/generic/history-log/HistoryLog.tsx

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
useLibraryContainerPublishHistory,
1111
} from '@src/library-authoring/data/apiHooks';
1212
import { HistoryCreatedLogGroup, HistoryDraftLogGroup, HistoryPublishLogGroup } from './HistoryLogGroup';
13+
import { useMemo } from 'react';
14+
import { LibraryPublishHistoryGroup } from '@src/library-authoring/data/api';
1315

1416
export const HistoryComponentLog = ({ componentId }: { componentId: string; }) => {
1517
const {
@@ -87,10 +89,45 @@ export const HistoryContainerLog = ({ containerId }: { containerId: string; }) =
8789
isPending: isPendingContainer,
8890
} = useContainer(containerId);
8991

92+
const creationTime = creationEntry?.changedAt;
93+
94+
const {
95+
groupsAfterCreation,
96+
groupsBeforeCreation,
97+
} = useMemo(() => {
98+
return {
99+
groupsAfterCreation: publishHistoryGroups?.filter(
100+
(group) => !creationTime || group.publishedAt >= creationTime,
101+
) ?? [],
102+
groupsBeforeCreation: publishHistoryGroups?.filter(
103+
(group) => creationTime && group.publishedAt < creationTime,
104+
) ?? [],
105+
};
106+
}, [publishHistoryGroups, creationTime]);
107+
90108
if (isPendingDraftHistory || isPendingContainer || isPendingPublishHistoryGroups || isPendingCreationEntry) {
91109
return <LoadingSpinner />;
92110
}
93111

112+
const hasBeforeCreationGroups = groupsBeforeCreation.length > 0;
113+
114+
const renderPublishGroups = (
115+
groups: LibraryPublishHistoryGroup[],
116+
isBeforeGroup: boolean,
117+
) =>
118+
groups.map((publishGroup, index, arr) => {
119+
const isLast = index === arr.length - 1;
120+
return (
121+
<div key={`${publishGroup.publishLogUuid}-${publishGroup.directPublishedEntities[0].entityKey}`}>
122+
<HistoryPublishLogGroup
123+
{...publishGroup}
124+
itemId={publishGroup.scopeEntityKey || containerId}
125+
hideLogVert={isLast && isBeforeGroup}
126+
/>
127+
</div>
128+
);
129+
});
130+
94131
return (
95132
<div className="history-log">
96133
{draftHistory && draftHistory.length !== 0 && (
@@ -99,24 +136,17 @@ export const HistoryContainerLog = ({ containerId }: { containerId: string; }) =
99136
entries={draftHistory}
100137
/>
101138
)}
102-
{publishHistoryGroups && publishHistoryGroups.length !== 0 && (
103-
publishHistoryGroups.map((publishGroup) => (
104-
<div key={`${publishGroup.publishLogUuid}-${publishGroup.directPublishedEntities[0].entityKey}`}>
105-
<HistoryPublishLogGroup
106-
{...publishGroup}
107-
itemId={publishGroup.scopeEntityKey || containerId}
108-
/>
109-
</div>
110-
))
111-
)}
139+
{renderPublishGroups(groupsAfterCreation, false)}
112140
{creationEntry && (
113141
<HistoryCreatedLogGroup
114142
user={creationEntry.contributor?.username}
115143
displayName={creationEntry.title}
116144
itemType={creationEntry.itemType}
117145
createdAt={creationEntry.changedAt}
146+
showLogVert={hasBeforeCreationGroups}
118147
/>
119148
)}
149+
{hasBeforeCreationGroups && renderPublishGroups(groupsBeforeCreation, true)}
120150
</div>
121151
);
122152
};

src/library-authoring/generic/history-log/HistoryLogGroup.tsx

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface HistoryCreatedLogGroupProps {
2525
displayName: string;
2626
itemType: string;
2727
createdAt: string;
28+
showLogVert?: boolean;
2829
}
2930

3031
export interface HistoryDraftLogGroupProps {
@@ -34,10 +35,12 @@ export interface HistoryDraftLogGroupProps {
3435

3536
export interface HistoryLogGroupEntriesProps {
3637
entries: LibraryHistoryEntry[];
38+
hideLastLogVert?: boolean;
3739
}
3840

3941
export interface HistoryPublishLogGroupProps extends LibraryPublishHistoryGroup {
4042
itemId: string;
43+
hideLogVert?: boolean;
4144
}
4245

4346
interface ContributorAvatarProps {
@@ -102,6 +105,7 @@ const HistoryLogGroupTitle = ({
102105

103106
const HistoryLogGroupEntries = ({
104107
entries,
108+
hideLastLogVert,
105109
}: HistoryLogGroupEntriesProps) => {
106110
const intl = useIntl();
107111

@@ -121,7 +125,8 @@ const HistoryLogGroupEntries = ({
121125
return (
122126
<Stack gap={0}>
123127
<div className="history-log-vert" />
124-
{entries.map((entry) => {
128+
{entries.map((entry, index, arr) => {
129+
const isLast = index === arr.length - 1;
125130
const entryMessage = getEntryMessage(entry);
126131

127132
return (
@@ -149,7 +154,7 @@ const HistoryLogGroupEntries = ({
149154
</span>
150155
</Stack>
151156
</Stack>
152-
<div className="history-log-vert" />
157+
{!isLast && !hideLastLogVert && <div className="history-log-vert" />}
153158
</div>
154159
);
155160
})}
@@ -162,6 +167,7 @@ export const HistoryCreatedLogGroup = ({
162167
displayName,
163168
itemType,
164169
createdAt,
170+
showLogVert,
165171
}: HistoryCreatedLogGroupProps) => {
166172
const intl = useIntl();
167173

@@ -176,6 +182,7 @@ export const HistoryCreatedLogGroup = ({
176182
dateMessage={moment(createdAt).fromNow()}
177183
disableCollapsible
178184
/>
185+
{showLogVert && <div className="history-log-vert" />}
179186
</div>
180187
);
181188
};
@@ -247,6 +254,7 @@ export const HistoryPublishLogGroup = ({
247254
publishedBy,
248255
publishedAt,
249256
contributors,
257+
hideLogVert,
250258
}: HistoryPublishLogGroupProps) => {
251259
const intl = useIntl();
252260
const [isOpenCollapsible, openCollapsible, closeCollapsible] = useToggle(false);
@@ -298,21 +306,36 @@ export const HistoryPublishLogGroup = ({
298306
) :
299307
(
300308
<>
301-
<HistoryLogGroupTitle titleMessage={titleMessage} dateMessage={dateMessage} disableCollapsible />
309+
{!hideLogVert && (
310+
<HistoryLogGroupTitle titleMessage={titleMessage} dateMessage={dateMessage} disableCollapsible />
311+
)}
302312
<div className="history-log-vert" />
303313
</>
304314
)}
305315
{hasContributors && (
306316
<Stack direction="horizontal">
307-
<div
308-
className={classNames(
309-
'history-log-vert',
310-
{
311-
'history-log-vert-long': !isOpenCollapsible,
312-
},
317+
{!hideLogVert && (
318+
<div
319+
className={classNames(
320+
'history-log-vert',
321+
{
322+
'history-log-vert-long': !isOpenCollapsible,
323+
},
324+
)}
325+
/>
326+
)}
327+
{!isOpenCollapsible &&
328+
(
329+
<div
330+
className={classNames(
331+
{
332+
'ml-4 pl-1': hideLogVert,
333+
},
334+
)}
335+
>
336+
<ContributorsAvatars contributors={contributors} />
337+
</div>
313338
)}
314-
/>
315-
{!isOpenCollapsible && <ContributorsAvatars contributors={contributors} />}
316339
</Stack>
317340
)}
318341
</div>

0 commit comments

Comments
 (0)