Skip to content

Commit 88f579d

Browse files
authored
feat: enable to select collections as components in problems picker (#2)
* feat: enable to select collections as components in the problems picker modal * refactor: remove unused ContentType import and simplify LibraryContent usage * feat: add block_id to collections in library search mock data * perf: enhance collection indexing and selection * fix: solve typo * test: clarify selection logic for components in collection * feat: integrate CollectionIndexProvider for optimized collection indexing * fix: remove unnecessary nullish coalescing for collectionKeys in AddComponentWidget * fix: update import path for useSearchContext in ComponentPickerContext * feat: optimize filtered hits calculation using useMemo in LibraryContent * feat: implement fetchAllSearchResults for comprehensive search result retrieval * feat: increase default limit for search results from 20 to 100 * fix: remove unused getNextPageParam from useContentSearchResults * feat: enhance fetchAllSearchResults to handle pagination more robustly
1 parent 14e122a commit 88f579d

File tree

12 files changed

+484
-81
lines changed

12 files changed

+484
-81
lines changed

src/library-authoring/LibraryAuthoringPage.tsx

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
} from '../search-manager';
3939
import LibraryContent from './LibraryContent';
4040
import { LibrarySidebar } from './library-sidebar';
41-
import { useComponentPickerContext } from './common/context/ComponentPickerContext';
41+
import { useComponentPickerContext, CollectionIndexProvider } from './common/context/ComponentPickerContext';
4242
import { useLibraryContext } from './common/context/LibraryContext';
4343
import { SidebarBodyComponentId, useSidebarContext } from './common/context/SidebarContext';
4444
import { allLibraryPageTabs, ContentType, useLibraryRoutes } from './routes';
@@ -233,10 +233,9 @@ const LibraryAuthoringPage = ({
233233

234234
const activeTypeFilters = {
235235
components: 'type = "library_block"',
236-
collections: 'type = "collection"',
237236
units: 'block_type = "unit"',
238237
};
239-
if (activeKey !== ContentType.home) {
238+
if (activeKey !== ContentType.home && activeKey !== ContentType.collections) {
240239
extraFilter.push(activeTypeFilters[activeKey]);
241240
}
242241

@@ -285,38 +284,40 @@ const LibraryAuthoringPage = ({
285284
extraFilter={extraFilter}
286285
overrideTypesFilter={overrideTypesFilter}
287286
>
288-
<SubHeader
289-
title={<SubHeaderTitle title={libraryData.title} />}
290-
subtitle={!componentPickerMode ? intl.formatMessage(messages.headingSubtitle) : undefined}
291-
breadcrumbs={breadcumbs}
292-
headerActions={<HeaderActions />}
293-
hideBorder
294-
/>
295-
<Tabs
296-
variant="tabs"
297-
activeKey={activeKey}
298-
onSelect={handleTabChange}
299-
className="my-3"
300-
>
301-
{visibleTabsToRender}
302-
</Tabs>
303-
<ActionRow className="my-3">
304-
<SearchKeywordsField className="mr-3" />
305-
<FilterByTags />
306-
{!(insideCollections || insideUnits) && <FilterByBlockType />}
307-
<LibraryFilterByPublished key={
308-
// It is necessary to re-render `LibraryFilterByPublished` every time `FilterByBlockType`
309-
// appears or disappears, this is because when the menu is opened it is rendered
310-
// in a previous state, causing an inconsistency in its position.
311-
// By changing the key we can re-render the component.
312-
!(insideCollections || insideUnits) ? 'filter-published-1' : 'filter-published-2'
313-
}
287+
<CollectionIndexProvider>
288+
<SubHeader
289+
title={<SubHeaderTitle title={libraryData.title} />}
290+
subtitle={!componentPickerMode ? intl.formatMessage(messages.headingSubtitle) : undefined}
291+
breadcrumbs={breadcumbs}
292+
headerActions={<HeaderActions />}
293+
hideBorder
314294
/>
315-
<ClearFiltersButton />
316-
<ActionRow.Spacer />
317-
<SearchSortWidget />
318-
</ActionRow>
319-
<LibraryContent contentType={activeKey} />
295+
<Tabs
296+
variant="tabs"
297+
activeKey={activeKey}
298+
onSelect={handleTabChange}
299+
className="my-3"
300+
>
301+
{visibleTabsToRender}
302+
</Tabs>
303+
<ActionRow className="my-3">
304+
<SearchKeywordsField className="mr-3" />
305+
<FilterByTags />
306+
{!(insideCollections || insideUnits) && <FilterByBlockType />}
307+
<LibraryFilterByPublished key={
308+
// It is necessary to re-render `LibraryFilterByPublished` every time `FilterByBlockType`
309+
// appears or disappears, this is because when the menu is opened it is rendered
310+
// in a previous state, causing an inconsistency in its position.
311+
// By changing the key we can re-render the component.
312+
!(insideCollections || insideUnits) ? 'filter-published-1' : 'filter-published-2'
313+
}
314+
/>
315+
<ClearFiltersButton />
316+
<ActionRow.Spacer />
317+
<SearchSortWidget />
318+
</ActionRow>
319+
<LibraryContent contentType={activeKey} />
320+
</CollectionIndexProvider>
320321
</SearchContextProvider>
321322
</Container>
322323
{!componentPickerMode && <StudioFooterSlot containerProps={{ size: undefined }} />}

src/library-authoring/LibraryContent.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect } from 'react';
1+
import { useEffect, useMemo } from 'react';
22
import { LoadingSpinner } from '../generic/Loading';
33
import { useSearchContext } from '../search-manager';
44
import { NoComponents, NoSearchResults } from './EmptyStates';
@@ -43,6 +43,20 @@ const LibraryContent = ({ contentType = ContentType.home }: LibraryContentProps)
4343
const { openCreateCollectionModal } = useLibraryContext();
4444
const { openAddContentSidebar, openComponentInfoSidebar } = useSidebarContext();
4545

46+
/**
47+
* Filter collections on the frontend to display only collection cards in the Collections tab.
48+
* This approach is used instead of backend filtering to ensure that all components (including those
49+
* within collections) remain available in the 'hits' array. This is necessary for the component
50+
* selection workflow when adding components to xblocks by choosing the whole collection in Collections tab.
51+
* Note: LibraryAuthoringPage.tsx has been modified to skip backend filtering for this purpose.
52+
*/
53+
const filteredHits = useMemo(
54+
() => (contentType === ContentType.collections
55+
? hits.filter((hit) => hit.type === 'collection')
56+
: hits),
57+
[hits, contentType],
58+
);
59+
4660
useEffect(() => {
4761
if (usageKey) {
4862
openComponentInfoSidebar(usageKey);
@@ -76,7 +90,7 @@ const LibraryContent = ({ contentType = ContentType.home }: LibraryContentProps)
7690

7791
return (
7892
<div className="library-cards-grid">
79-
{hits.map((contentHit) => {
93+
{filteredHits.map((contentHit) => {
8094
const CardComponent = LibraryItemCard[contentHit.type] || ComponentCard;
8195

8296
return <CardComponent key={contentHit.id} hit={contentHit} />;

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
},
6868
{
6969
"display_name": "Collection 2",
70+
"block_id": "col2",
7071
"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. Descrition 58",
7172
"id": 2,
7273
"type": "collection",
@@ -99,6 +100,7 @@
99100
},
100101
{
101102
"display_name": "Collection 3",
103+
"block_id": "col3",
102104
"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. Descrition 57",
103105
"id": 3,
104106
"type": "collection",
@@ -131,6 +133,7 @@
131133
},
132134
{
133135
"display_name": "Collection 4",
136+
"block_id": "col4",
134137
"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. Descrition 56",
135138
"id": 4,
136139
"type": "collection",
@@ -163,6 +166,7 @@
163166
},
164167
{
165168
"display_name": "Collection 5",
169+
"block_id": "col5",
166170
"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. Descrition 55",
167171
"id": 5,
168172
"type": "collection",
@@ -195,6 +199,7 @@
195199
},
196200
{
197201
"display_name": "Collection 6",
202+
"block_id": "col6",
198203
"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. Descrition 54",
199204
"id": 6,
200205
"type": "collection",

src/library-authoring/collections/LibraryCollectionComponents.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { useSearchContext } from '../../search-manager';
44
import messages from './messages';
55
import { useSidebarContext } from '../common/context/SidebarContext';
66
import LibraryContent from '../LibraryContent';
7-
import { ContentType } from '../routes';
87

98
const LibraryCollectionComponents = () => {
109
const { totalHits: componentCount, isFiltered } = useSearchContext();
@@ -25,7 +24,7 @@ const LibraryCollectionComponents = () => {
2524
return (
2625
<Stack direction="vertical" gap={3}>
2726
<h3 className="text-gray">Content ({componentCount})</h3>
28-
<LibraryContent contentType={ContentType.collections} />
27+
<LibraryContent />
2928
</Stack>
3029
);
3130
};

src/library-authoring/collections/LibraryCollectionPage.tsx

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
} from '../../search-manager';
3030
import { SubHeaderTitle } from '../LibraryAuthoringPage';
3131
import { useCollection, useContentLibrary } from '../data/apiHooks';
32-
import { useComponentPickerContext } from '../common/context/ComponentPickerContext';
32+
import { useComponentPickerContext, CollectionIndexProvider } from '../common/context/ComponentPickerContext';
3333
import { useLibraryContext } from '../common/context/LibraryContext';
3434
import { SidebarBodyComponentId, useSidebarContext } from '../common/context/SidebarContext';
3535
import messages from './messages';
@@ -208,22 +208,24 @@ const LibraryCollectionPage = () => {
208208
<SearchContextProvider
209209
extraFilter={extraFilter}
210210
>
211-
<SubHeader
212-
title={<SubHeaderTitle title={collectionData.title} />}
213-
breadcrumbs={breadcrumbs}
214-
headerActions={<HeaderActions />}
215-
hideBorder
216-
/>
217-
<ActionRow className="my-3">
218-
<SearchKeywordsField className="mr-3" />
219-
<FilterByTags />
220-
<FilterByBlockType />
221-
<LibraryFilterByPublished />
222-
<ClearFiltersButton />
223-
<ActionRow.Spacer />
224-
<SearchSortWidget />
225-
</ActionRow>
226-
<LibraryCollectionComponents />
211+
<CollectionIndexProvider>
212+
<SubHeader
213+
title={<SubHeaderTitle title={collectionData.title} />}
214+
breadcrumbs={breadcrumbs}
215+
headerActions={<HeaderActions />}
216+
hideBorder
217+
/>
218+
<ActionRow className="my-3">
219+
<SearchKeywordsField className="mr-3" />
220+
<FilterByTags />
221+
<FilterByBlockType />
222+
<LibraryFilterByPublished />
223+
<ClearFiltersButton />
224+
<ActionRow.Spacer />
225+
<SearchSortWidget />
226+
</ActionRow>
227+
<LibraryCollectionComponents />
228+
</CollectionIndexProvider>
227229
</SearchContextProvider>
228230
</Container>
229231
{!componentPickerMode && <StudioFooterSlot containerProps={{ size: undefined }} />}

0 commit comments

Comments
 (0)