Skip to content

Commit 82aba72

Browse files
committed
fixup! feat: improve collection sidebar
1 parent 72f5694 commit 82aba72

File tree

7 files changed

+113
-46
lines changed

7 files changed

+113
-46
lines changed

src/library-authoring/collections/CollectionDetails.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ const CollectionDetails = ({ library, collection }: CollectionDetailsProps) => {
115115
};
116116

117117
return (
118-
<Stack gap={3}>
118+
<Stack
119+
gap={3}
120+
>
119121
<div>
120122
<h3 className="h5">
121123
{intl.formatMessage(messages.detailsTabDescriptionTitle)}

src/library-authoring/collections/CollectionInfo.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ const CollectionInfo = ({ library, collection }: CollectionInfoProps) => {
2727
Manage tab placeholder
2828
</Tab>
2929
<Tab eventKey="details" title={intl.formatMessage(messages.detailsTabTitle)}>
30-
<CollectionDetails library={library} collection={collection} />
30+
<CollectionDetails
31+
key={collection.id} // This is necessary to force a re-render when the collection changes
32+
library={library}
33+
collection={collection}
34+
/>
3135
</Tab>
3236
</Tabs>
3337
);

src/library-authoring/collections/CollectionInfoHeader.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ const CollectionInfoHeader = ({ library, collection } : CollectionInfoHeaderProp
3939
}).finally(() => {
4040
setIsActive(false);
4141
});
42+
} else {
43+
setIsActive(false);
4244
}
4345
},
4446
[collection, showToast, intl],

src/library-authoring/common/context.tsx

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useToggle } from '@openedx/paragon';
22
import React from 'react';
3+
import type { CollectionHit } from '../../search-manager';
34

45
export enum SidebarBodyComponentId {
56
AddContent = 'add-content',
@@ -18,7 +19,8 @@ export interface LibraryContextData {
1819
isCreateCollectionModalOpen: boolean;
1920
openCreateCollectionModal: () => void;
2021
closeCreateCollectionModal: () => void;
21-
openCollectionInfoSidebar: () => void;
22+
openCollectionInfoSidebar: (collectionHit: CollectionHit) => void
23+
currentCollectionHit?: CollectionHit;
2224
}
2325

2426
export const LibraryContext = React.createContext({
@@ -30,7 +32,8 @@ export const LibraryContext = React.createContext({
3032
isCreateCollectionModalOpen: false,
3133
openCreateCollectionModal: () => {},
3234
closeCreateCollectionModal: () => {},
33-
openCollectionInfoSidebar: () => {},
35+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
36+
openCollectionInfoSidebar: (_collectionHit: CollectionHit) => {},
3437
} as LibraryContextData);
3538

3639
/**
@@ -39,29 +42,38 @@ export const LibraryContext = React.createContext({
3942
export const LibraryProvider = (props: { children?: React.ReactNode }) => {
4043
const [sidebarBodyComponent, setSidebarBodyComponent] = React.useState<SidebarBodyComponentId | null>(null);
4144
const [currentComponentUsageKey, setCurrentComponentUsageKey] = React.useState<string>();
45+
const [currentCollectionHit, setcurrentCollectionHit] = React.useState<CollectionHit>();
4246
const [isCreateCollectionModalOpen, openCreateCollectionModal, closeCreateCollectionModal] = useToggle(false);
4347

44-
const closeLibrarySidebar = React.useCallback(() => {
48+
const resetSidebar = React.useCallback(() => {
49+
setCurrentComponentUsageKey(undefined);
50+
setcurrentCollectionHit(undefined);
4551
setSidebarBodyComponent(null);
52+
}, []);
53+
54+
const closeLibrarySidebar = React.useCallback(() => {
55+
resetSidebar();
4656
setCurrentComponentUsageKey(undefined);
4757
}, []);
4858
const openAddContentSidebar = React.useCallback(() => {
49-
setCurrentComponentUsageKey(undefined);
59+
resetSidebar();
5060
setSidebarBodyComponent(SidebarBodyComponentId.AddContent);
5161
}, []);
5262
const openInfoSidebar = React.useCallback(() => {
53-
setCurrentComponentUsageKey(undefined);
63+
resetSidebar();
5464
setSidebarBodyComponent(SidebarBodyComponentId.Info);
5565
}, []);
5666
const openComponentInfoSidebar = React.useCallback(
5767
(usageKey: string) => {
68+
resetSidebar();
5869
setCurrentComponentUsageKey(usageKey);
5970
setSidebarBodyComponent(SidebarBodyComponentId.ComponentInfo);
6071
},
6172
[],
6273
);
63-
const openCollectionInfoSidebar = React.useCallback(() => {
64-
setCurrentComponentUsageKey(undefined);
74+
const openCollectionInfoSidebar = React.useCallback((collectionHit: CollectionHit) => {
75+
resetSidebar();
76+
setcurrentCollectionHit(collectionHit);
6577
setSidebarBodyComponent(SidebarBodyComponentId.CollectionInfo);
6678
}, []);
6779

@@ -76,6 +88,7 @@ export const LibraryProvider = (props: { children?: React.ReactNode }) => {
7688
openCreateCollectionModal,
7789
closeCreateCollectionModal,
7890
openCollectionInfoSidebar,
91+
currentCollectionHit,
7992
}), [
8093
sidebarBodyComponent,
8194
closeLibrarySidebar,
@@ -87,6 +100,7 @@ export const LibraryProvider = (props: { children?: React.ReactNode }) => {
87100
openCreateCollectionModal,
88101
closeCreateCollectionModal,
89102
openCollectionInfoSidebar,
103+
currentCollectionHit,
90104
]);
91105

92106
return (

src/library-authoring/components/CollectionCard.tsx

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,54 @@
1-
import { useIntl } from '@edx/frontend-platform/i18n';
1+
import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
22
import {
33
ActionRow,
4+
Dropdown,
45
Icon,
56
IconButton,
67
} from '@openedx/paragon';
78
import { MoreVert } from '@openedx/paragon/icons';
8-
import { useNavigate } from 'react-router-dom';
9+
import { useContext } from 'react';
10+
import { Link } from 'react-router-dom';
911

1012
import { type CollectionHit } from '../../search-manager';
11-
import messages from './messages';
13+
import { LibraryContext } from '../common/context';
1214
import BaseComponentCard from './BaseComponentCard';
15+
import messages from './messages';
16+
17+
export const CollectionMenu = ({ collectionHit }: { collectionHit: CollectionHit }) => {
18+
const intl = useIntl();
19+
20+
return (
21+
<Dropdown id="collection-card-dropdown" onClick={(e) => e.stopPropagation()}>
22+
<Dropdown.Toggle
23+
id="collection-card-menu-toggle"
24+
as={IconButton}
25+
src={MoreVert}
26+
iconAs={Icon}
27+
variant="primary"
28+
alt={intl.formatMessage(messages.collectionCardMenuAlt)}
29+
data-testid="collection-card-menu-toggle"
30+
/>
31+
<Dropdown.Menu>
32+
<Dropdown.Item
33+
as={Link}
34+
to={`/library/${collectionHit.contextKey}/collection/${collectionHit.blockId}/`}
35+
>
36+
<FormattedMessage {...messages.menuEdit} />
37+
</Dropdown.Item>
38+
</Dropdown.Menu>
39+
</Dropdown>
40+
);
41+
};
1342

1443
type CollectionCardProps = {
1544
collectionHit: CollectionHit,
1645
};
1746

1847
const CollectionCard = ({ collectionHit }: CollectionCardProps) => {
1948
const intl = useIntl();
20-
const navigate = useNavigate();
49+
const {
50+
openCollectionInfoSidebar,
51+
} = useContext(LibraryContext);
2152

2253
const {
2354
type,
@@ -39,16 +70,11 @@ const CollectionCard = ({ collectionHit }: CollectionCardProps) => {
3970
tags={tags}
4071
actions={(
4172
<ActionRow>
42-
<IconButton
43-
src={MoreVert}
44-
iconAs={Icon}
45-
variant="primary"
46-
alt={intl.formatMessage(messages.collectionCardMenuAlt)}
47-
/>
73+
<CollectionMenu collectionHit={collectionHit} />
4874
</ActionRow>
4975
)}
5076
blockTypeDisplayName={blockTypeDisplayName}
51-
openInfoSidebar={() => navigate(`/library/${collectionHit.contextKey}/collection/${collectionHit.blockId}`)}
77+
openInfoSidebar={() => openCollectionInfoSidebar(collectionHit)}
5278
/>
5379
);
5480
};

src/library-authoring/components/messages.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ const messages = defineMessages({
2222
description: 'Collection type text with children count',
2323
},
2424
menuEdit: {
25-
id: 'course-authoring.library-authoring.component.menu.edit',
25+
id: 'course-authoring.library-authoring.component-collection.menu.edit',
2626
defaultMessage: 'Edit',
27-
description: 'Menu item for edit a component.',
27+
description: 'Menu item for edit a component/collection.',
2828
},
2929
menuCopyToClipboard: {
3030
id: 'course-authoring.library-authoring.component.menu.copy',

src/library-authoring/library-sidebar/LibrarySidebar.tsx

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,21 @@ import {
66
} from '@openedx/paragon';
77
import { Close } from '@openedx/paragon/icons';
88
import { useIntl } from '@edx/frontend-platform/i18n';
9-
import messages from '../messages';
9+
import { SearchParams } from 'meilisearch';
10+
11+
import {
12+
SearchContextProvider,
13+
} from '../../search-manager';
1014
import { AddContentContainer, AddContentHeader } from '../add-content';
15+
import { CollectionInfo, CollectionInfoHeader } from '../collections';
16+
import { ContentLibrary } from '../data/api';
1117
import { LibraryContext, SidebarBodyComponentId } from '../common/context';
12-
import { LibraryInfo, LibraryInfoHeader } from '../library-info';
1318
import { ComponentInfo, ComponentInfoHeader } from '../component-info';
14-
import { ContentLibrary } from '../data/api';
15-
import { CollectionInfo, CollectionInfoHeader } from '../collections';
16-
import { type CollectionHit } from '../../search-manager/data/api';
19+
import { LibraryInfo, LibraryInfoHeader } from '../library-info';
20+
import messages from '../messages';
1721

1822
type LibrarySidebarProps = {
1923
library: ContentLibrary,
20-
collection?: CollectionHit,
2124
};
2225

2326
/**
@@ -29,12 +32,13 @@ type LibrarySidebarProps = {
2932
* You can add more components in `bodyComponentMap`.
3033
* Use the returned actions to open and close this sidebar.
3134
*/
32-
const LibrarySidebar = ({ library, collection }: LibrarySidebarProps) => {
35+
const LibrarySidebar = ({ library }: LibrarySidebarProps) => {
3336
const intl = useIntl();
3437
const {
3538
sidebarBodyComponent,
3639
closeLibrarySidebar,
3740
currentComponentUsageKey,
41+
currentCollectionHit,
3842
} = useContext(LibraryContext);
3943

4044
const bodyComponentMap = {
@@ -43,7 +47,9 @@ const LibrarySidebar = ({ library, collection }: LibrarySidebarProps) => {
4347
[SidebarBodyComponentId.ComponentInfo]: (
4448
currentComponentUsageKey && <ComponentInfo usageKey={currentComponentUsageKey} />
4549
),
46-
[SidebarBodyComponentId.CollectionInfo]: collection && <CollectionInfo library={library} collection={collection} />,
50+
[SidebarBodyComponentId.CollectionInfo]: (
51+
currentCollectionHit && <CollectionInfo library={library} collection={currentCollectionHit} />
52+
),
4753
unknown: null,
4854
};
4955

@@ -54,31 +60,44 @@ const LibrarySidebar = ({ library, collection }: LibrarySidebarProps) => {
5460
currentComponentUsageKey && <ComponentInfoHeader library={library} usageKey={currentComponentUsageKey} />
5561
),
5662
[SidebarBodyComponentId.CollectionInfo]: (
57-
collection && <CollectionInfoHeader library={library} collection={collection} />
63+
currentCollectionHit && <CollectionInfoHeader library={library} collection={currentCollectionHit} />
5864
),
5965
unknown: null,
6066
};
6167

6268
const buildBody = () : React.ReactNode => bodyComponentMap[sidebarBodyComponent || 'unknown'];
6369
const buildHeader = (): React.ReactNode => headerComponentMap[sidebarBodyComponent || 'unknown'];
6470

71+
const collectionQuery: SearchParams | undefined = currentCollectionHit ? {
72+
filter: ['type = "collection"', `context_key = "${library.id}"`, `block_id = "${currentCollectionHit.blockId}"`],
73+
limit: 1,
74+
} : undefined;
75+
6576
return (
66-
<Stack gap={4} className="p-3 text-primary-700">
67-
<Stack direction="horizontal" className="d-flex justify-content-between">
68-
{buildHeader()}
69-
<IconButton
70-
className="mt-1"
71-
src={Close}
72-
iconAs={Icon}
73-
alt={intl.formatMessage(messages.closeButtonAlt)}
74-
onClick={closeLibrarySidebar}
75-
size="inline"
76-
/>
77+
<SearchContextProvider
78+
extraFilter={[
79+
`context_key = "${library.id}"`,
80+
...(currentCollectionHit ? [`collections.key = "${currentCollectionHit.blockId}"`] : []),
81+
]}
82+
overrideQueries={{ ...(collectionQuery ? { collections: collectionQuery } : {}) }}
83+
>
84+
<Stack gap={4} className="p-3 text-primary-700">
85+
<Stack direction="horizontal" className="d-flex justify-content-between">
86+
{buildHeader()}
87+
<IconButton
88+
className="mt-1"
89+
src={Close}
90+
iconAs={Icon}
91+
alt={intl.formatMessage(messages.closeButtonAlt)}
92+
onClick={closeLibrarySidebar}
93+
size="inline"
94+
/>
95+
</Stack>
96+
<div>
97+
{buildBody()}
98+
</div>
7799
</Stack>
78-
<div>
79-
{buildBody()}
80-
</div>
81-
</Stack>
100+
</SearchContextProvider>
82101
);
83102
};
84103

0 commit comments

Comments
 (0)