Skip to content

Commit d3e4d3d

Browse files
committed
refactor: stop using meilisearch to get a single collection
1 parent cd3b912 commit d3e4d3d

16 files changed

+194
-135
lines changed

src/library-authoring/LibraryAuthoringPage.test.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ import {
1111
} from '../testUtils';
1212
import mockResult from './__mocks__/library-search.json';
1313
import mockEmptyResult from '../search-modal/__mocks__/empty-search-result.json';
14-
import { mockContentLibrary, mockLibraryBlockTypes, mockXBlockFields } from './data/api.mocks';
14+
import {
15+
mockContentLibrary,
16+
mockGetCollectionMetadata,
17+
mockLibraryBlockTypes,
18+
mockXBlockFields,
19+
} from './data/api.mocks';
1520
import { mockContentSearchConfig } from '../search-manager/data/api.mock';
1621
import { mockBroadcastChannel } from '../generic/data/api.mock';
1722
import { LibraryLayout } from '.';
1823
import { getLibraryCollectionsApiUrl } from './data/api';
1924

25+
mockGetCollectionMetadata.applyMock();
2026
mockContentSearchConfig.applyMock();
2127
mockContentLibrary.applyMock();
2228
mockLibraryBlockTypes.applyMock();
@@ -459,17 +465,17 @@ describe('<LibraryAuthoringPage />', () => {
459465
});
460466

461467
it('should open and close the collection sidebar', async () => {
462-
const displayName = 'Collection 1';
463468
await renderLibraryPage();
464469

465470
// Click on the first component. It could appear twice, in both "Recently Modified" and "Collections"
466-
fireEvent.click((await screen.findAllByText(displayName))[0]);
471+
fireEvent.click((await screen.findAllByText('Collection 1'))[0]);
467472

468473
const sidebar = screen.getByTestId('library-sidebar');
469474

470475
const { getByRole, getByText } = within(sidebar);
471476

472-
await waitFor(() => expect(getByText(displayName)).toBeInTheDocument());
477+
// The mock data for the sidebar has a title of "Test Collection"
478+
await waitFor(() => expect(getByText('Test Collection')).toBeInTheDocument());
473479

474480
const closeButton = getByRole('button', { name: /close/i });
475481
fireEvent.click(closeButton);

src/library-authoring/__mocks__/library-search.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@
284284
"hits": [
285285
{
286286
"display_name": "Collection 1",
287+
"block_id": "col1",
287288
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque et mi ac nisi accumsan imperdiet vitae at odio. Vivamus tempor nec lorem eget lacinia. Vivamus efficitur lacus non dapibus porta. Nulla venenatis luctus nisi id posuere. Sed sollicitudin magna a sem ultrices accumsan. Praesent volutpat tortor vitae luctus rutrum. Integer.",
288289
"id": 1,
289290
"type": "collection",

src/library-authoring/collections/CollectionDetails.test.tsx

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type MockAdapter from 'axios-mock-adapter';
22
import fetchMock from 'fetch-mock-jest';
33

44
import { mockContentSearchConfig, mockGetBlockTypes } from '../../search-manager/data/api.mock';
5-
import { type CollectionHit, formatSearchHit } from '../../search-manager/data/api';
65
import {
76
initializeMocks,
87
fireEvent,
@@ -11,19 +10,21 @@ import {
1110
waitFor,
1211
within,
1312
} from '../../testUtils';
14-
import mockResult from '../__mocks__/collection-search.json';
1513
import * as api from '../data/api';
16-
import { mockContentLibrary } from '../data/api.mocks';
14+
import { mockContentLibrary, mockGetCollectionMetadata } from '../data/api.mocks';
1715
import CollectionDetails from './CollectionDetails';
1816

1917
let axiosMock: MockAdapter;
2018
let mockShowToast: (message: string) => void;
2119

20+
mockGetCollectionMetadata.applyMock();
2221
mockContentSearchConfig.applyMock();
2322
mockGetBlockTypes.applyMock();
2423

24+
const { collectionId } = mockGetCollectionMetadata;
25+
const { description: originalDescription } = mockGetCollectionMetadata.collectionData;
26+
2527
const library = mockContentLibrary.libraryData;
26-
const collectionHit = formatSearchHit(mockResult.results[2].hits[0]) as CollectionHit;
2728

2829
describe('<CollectionDetails />', () => {
2930
beforeEach(() => {
@@ -39,12 +40,11 @@ describe('<CollectionDetails />', () => {
3940
});
4041

4142
it('should render Collection Details', async () => {
42-
render(<CollectionDetails library={library} collection={collectionHit} />);
43+
render(<CollectionDetails library={library} collectionId={collectionId} />);
4344

4445
// Collection Description
45-
expect(screen.getByText('Description / Card Preview Text')).toBeInTheDocument();
46-
const { description } = collectionHit;
47-
expect(screen.getByText(description)).toBeInTheDocument();
46+
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();
47+
expect(screen.getByText(originalDescription)).toBeInTheDocument();
4848

4949
// Collection History
5050
expect(screen.getByText('Collection History')).toBeInTheDocument();
@@ -55,17 +55,12 @@ describe('<CollectionDetails />', () => {
5555
});
5656

5757
it('should allow modifying the description', async () => {
58-
render(<CollectionDetails library={library} collection={collectionHit} />);
59-
60-
const {
61-
description: originalDescription,
62-
blockId,
63-
contextKey,
64-
} = collectionHit;
58+
render(<CollectionDetails library={library} collectionId={collectionId} />);
59+
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();
6560

6661
expect(screen.getByText(originalDescription)).toBeInTheDocument();
6762

68-
const url = api.getLibraryCollectionApiUrl(contextKey, blockId);
63+
const url = api.getLibraryCollectionApiUrl(library.id, collectionId);
6964
axiosMock.onPatch(url).reply(200);
7065

7166
const textArea = screen.getByRole('textbox');
@@ -94,17 +89,12 @@ describe('<CollectionDetails />', () => {
9489
});
9590

9691
it('should show error while modifing the description', async () => {
97-
render(<CollectionDetails library={library} collection={collectionHit} />);
98-
99-
const {
100-
description: originalDescription,
101-
blockId,
102-
contextKey,
103-
} = collectionHit;
92+
render(<CollectionDetails library={library} collectionId={collectionId} />);
93+
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();
10494

10595
expect(screen.getByText(originalDescription)).toBeInTheDocument();
10696

107-
const url = api.getLibraryCollectionApiUrl(contextKey, blockId);
97+
const url = api.getLibraryCollectionApiUrl(library.id, collectionId);
10898
axiosMock.onPatch(url).reply(500);
10999

110100
const textArea = screen.getByRole('textbox');
@@ -124,7 +114,8 @@ describe('<CollectionDetails />', () => {
124114

125115
it('should render Collection stats', async () => {
126116
mockGetBlockTypes('someBlocks');
127-
render(<CollectionDetails library={library} collection={collectionHit} />);
117+
render(<CollectionDetails library={library} collectionId={collectionId} />);
118+
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();
128119

129120
expect(screen.getByText('Collection Stats')).toBeInTheDocument();
130121
expect(await screen.findByText('Total')).toBeInTheDocument();
@@ -141,15 +132,17 @@ describe('<CollectionDetails />', () => {
141132

142133
it('should render Collection stats for empty collection', async () => {
143134
mockGetBlockTypes('noBlocks');
144-
render(<CollectionDetails library={library} collection={collectionHit} />);
135+
render(<CollectionDetails library={library} collectionId={collectionId} />);
136+
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();
145137

146138
expect(screen.getByText('Collection Stats')).toBeInTheDocument();
147139
expect(await screen.findByText('This collection is currently empty.')).toBeInTheDocument();
148140
});
149141

150142
it('should render Collection stats for big collection', async () => {
151143
mockGetBlockTypes('moreBlocks');
152-
render(<CollectionDetails library={library} collection={collectionHit} />);
144+
render(<CollectionDetails library={library} collectionId={collectionId} />);
145+
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();
153146

154147
expect(screen.getByText('Collection Stats')).toBeInTheDocument();
155148
expect(await screen.findByText('36')).toBeInTheDocument();

src/library-authoring/collections/CollectionDetails.tsx

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
22
import { Icon, Stack } from '@openedx/paragon';
3-
import { useContext, useState } from 'react';
3+
import { useContext, useEffect, useState } from 'react';
44
import classNames from 'classnames';
55

66
import { getItemIcon } from '../../generic/block-type-utils';
77
import { ToastContext } from '../../generic/toast-context';
8-
import { BlockTypeLabel, type CollectionHit, useGetBlockTypes } from '../../search-manager';
8+
import { BlockTypeLabel, useGetBlockTypes } from '../../search-manager';
99
import type { ContentLibrary } from '../data/api';
10-
import { useUpdateCollection } from '../data/apiHooks';
10+
import { useCollection, useUpdateCollection } from '../data/apiHooks';
1111
import HistoryWidget from '../generic/history-widget';
1212
import messages from './messages';
1313

@@ -37,13 +37,14 @@ const BlockCount = ({
3737
};
3838

3939
interface CollectionStatsWidgetProps {
40-
collection: CollectionHit,
40+
libraryId: string,
41+
collectionId: string,
4142
}
4243

43-
const CollectionStatsWidget = ({ collection }: CollectionStatsWidgetProps) => {
44+
const CollectionStatsWidget = ({ libraryId, collectionId }: CollectionStatsWidgetProps) => {
4445
const { data: blockTypes } = useGetBlockTypes([
45-
`context_key = "${collection.contextKey}"`,
46-
`collections.key = "${collection.blockId}"`,
46+
`context_key = "${libraryId}"`,
47+
`collections.key = "${collectionId}"`,
4748
]);
4849

4950
if (!blockTypes) {
@@ -100,20 +101,26 @@ const CollectionStatsWidget = ({ collection }: CollectionStatsWidgetProps) => {
100101

101102
interface CollectionDetailsProps {
102103
library: ContentLibrary,
103-
collection: CollectionHit,
104+
collectionId: string,
104105
}
105106

106-
const CollectionDetails = ({ library, collection }: CollectionDetailsProps) => {
107+
const CollectionDetails = ({ library, collectionId }: CollectionDetailsProps) => {
107108
const intl = useIntl();
108109
const { showToast } = useContext(ToastContext);
109110

110-
const [description, setDescription] = useState(collection.description);
111+
const updateMutation = useUpdateCollection(library.id, collectionId);
112+
const { data: collection } = useCollection(library.id, collectionId);
111113

112-
const updateMutation = useUpdateCollection(collection.contextKey, collection.blockId);
114+
const [description, setDescription] = useState(collection?.description || '');
115+
116+
useEffect(() => {
117+
if (collection) {
118+
setDescription(collection.description);
119+
}
120+
}, [collection]);
113121

114-
// istanbul ignore if: this should never happen
115122
if (!collection) {
116-
throw new Error('A collection must be provided to CollectionDetails');
123+
return null;
117124
}
118125

119126
const onSubmit = (e: React.FocusEvent<HTMLTextAreaElement>) => {
@@ -151,16 +158,16 @@ const CollectionDetails = ({ library, collection }: CollectionDetailsProps) => {
151158
<h3 className="h5">
152159
{intl.formatMessage(messages.detailsTabStatsTitle)}
153160
</h3>
154-
<CollectionStatsWidget collection={collection} />
161+
<CollectionStatsWidget libraryId={library.id} collectionId={collectionId} />
155162
</div>
156163
<hr className="w-100" />
157164
<div>
158165
<h3 className="h5">
159166
{intl.formatMessage(messages.detailsTabHistoryTitle)}
160167
</h3>
161168
<HistoryWidget
162-
created={collection.created ? new Date(collection.created * 1000) : null}
163-
modified={collection.modified ? new Date(collection.modified * 1000) : null}
169+
created={collection.created ? new Date(collection.created) : null}
170+
modified={collection.modified ? new Date(collection.modified) : null}
164171
/>
165172
</div>
166173
</Stack>

src/library-authoring/collections/CollectionInfo.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ import messages from './messages';
1414

1515
interface CollectionInfoProps {
1616
library: ContentLibrary,
17-
collection: CollectionHit,
17+
collectionId: string,
1818
}
1919

20-
const CollectionInfo = ({ library, collection }: CollectionInfoProps) => {
20+
const CollectionInfo = ({ library, collectionId }: CollectionInfoProps) => {
2121
const intl = useIntl();
22-
const url = `/library/${library.id}/collection/${collection.blockId}/`;
22+
const url = `/library/${library.id}/collection/${collectionId}/`;
2323
const urlMatch = useMatch(url);
2424

2525
return (
@@ -28,7 +28,7 @@ const CollectionInfo = ({ library, collection }: CollectionInfoProps) => {
2828
<div className="d-flex flex-wrap">
2929
<Button
3030
as={Link}
31-
to={`/library/${library.id}/collection/${collection.blockId}/`}
31+
to={url}
3232
variant="outline-primary"
3333
className="m-1 text-nowrap flex-grow-1"
3434
disabled={!!urlMatch}
@@ -47,9 +47,8 @@ const CollectionInfo = ({ library, collection }: CollectionInfoProps) => {
4747
</Tab>
4848
<Tab eventKey="details" title={intl.formatMessage(messages.detailsTabTitle)}>
4949
<CollectionDetails
50-
key={collection.id} // This is necessary to force a re-render when the collection changes
5150
library={library}
52-
collection={collection}
51+
collectionId={collectionId}
5352
/>
5453
</Tab>
5554
</Tabs>

0 commit comments

Comments
 (0)