Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/library-authoring/LibraryAuthoringPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import mockEmptyResult from '../search-modal/__mocks__/empty-search-result.json'
import {
mockContentLibrary,
mockGetCollectionMetadata,
mockGetContainerMetadata,
mockGetLibraryTeam,
mockXBlockFields,
} from './data/api.mocks';
Expand All @@ -28,6 +29,7 @@ let axiosMock;
let mockShowToast;

mockGetCollectionMetadata.applyMock();
mockGetContainerMetadata.applyMock();
mockContentSearchConfig.applyMock();
mockContentLibrary.applyMock();
mockGetLibraryTeam.applyMock();
Expand Down Expand Up @@ -436,6 +438,25 @@ describe('<LibraryAuthoringPage />', () => {
await waitFor(() => expect(screen.queryByTestId('library-sidebar')).not.toBeInTheDocument());
});

it('should open and close the unit sidebar', async () => {
await renderLibraryPage();

// Click on the first unit
fireEvent.click((await screen.findByText('Test Unit')));

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

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

// The mock data for the sidebar has a title of "Test Unit"
await waitFor(() => expect(getByText('Test Unit')).toBeInTheDocument());

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

await waitFor(() => expect(screen.queryByTestId('library-sidebar')).not.toBeInTheDocument());
});

it('should preserve the tab while switching from a component to a collection', async () => {
await renderLibraryPage();

Expand Down
5 changes: 3 additions & 2 deletions src/library-authoring/LibraryAuthoringPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const HeaderActions = () => {

if (!componentPickerMode) {
// Reset URL to library home
navigateTo({ componentId: '', collectionId: '' });
navigateTo({ componentId: '', collectionId: '', unitId: '' });
}
}, [navigateTo, sidebarComponentInfo, closeLibrarySidebar, openLibrarySidebar]);

Expand Down Expand Up @@ -143,6 +143,7 @@ const LibraryAuthoringPage = ({ returnToLibrarySelection }: LibraryAuthoringPage
showOnlyPublished,
componentId,
collectionId,
unitId,
} = useLibraryContext();
const { openInfoSidebar, sidebarComponentInfo } = useSidebarContext();

Expand Down Expand Up @@ -173,7 +174,7 @@ const LibraryAuthoringPage = ({ returnToLibrarySelection }: LibraryAuthoringPage

useEffect(() => {
if (!componentPickerMode) {
openInfoSidebar(componentId, collectionId);
openInfoSidebar(componentId, collectionId, unitId);
}
}, []);

Expand Down
33 changes: 13 additions & 20 deletions src/library-authoring/LibraryLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,30 +54,23 @@ const LibraryLayout = () => {

return (
<Routes>
<Route
path={ROUTES.COMPONENTS}
element={context(<LibraryAuthoringPage />)}
/>
<Route
path={ROUTES.UNITS}
element={context(<LibraryAuthoringPage />)}
/>
<Route
path={ROUTES.COLLECTIONS}
element={context(<LibraryAuthoringPage />)}
/>
<Route
path={ROUTES.COMPONENT}
element={context(<LibraryAuthoringPage />)}
/>
{[
ROUTES.HOME,
ROUTES.COMPONENT,
ROUTES.COMPONENTS,
ROUTES.COLLECTIONS,
ROUTES.UNITS,
].map((route) => (
<Route
key={route}
path={route}
element={context(<LibraryAuthoringPage />)}
/>
))}
<Route
path={ROUTES.COLLECTION}
element={context(<LibraryCollectionPage />)}
/>
<Route
path={ROUTES.HOME}
element={context(<LibraryAuthoringPage />)}
/>
</Routes>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const LibraryCollectionPage = () => {
} = useCollection(libraryId, collectionId);

useEffect(() => {
openInfoSidebar(componentId, collectionId);
openInfoSidebar(componentId, collectionId, '');
}, []);

const { data: libraryData, isLoading: isLibLoading } = useContentLibrary(libraryId);
Expand Down
21 changes: 19 additions & 2 deletions src/library-authoring/common/context/LibraryContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@ export type LibraryContextData = {
libraryData?: ContentLibrary;
readOnly: boolean;
isLoadingLibraryData: boolean;
/** The ID of the current collection/component/unit, on the sidebar OR page */
collectionId: string | undefined;
setCollectionId: (collectionId?: string) => void;
componentId: string | undefined;
setComponentId: (componentId?: string) => void;
unitId: string | undefined;
setUnitId: (unitId?: string) => void;
// Only show published components
showOnlyPublished: boolean;
// "Create New Collection" modal
Expand Down Expand Up @@ -106,11 +109,21 @@ export const LibraryProvider = ({

// Parse the initial collectionId and/or componentId from the current URL params
const params = useParams();
const {
collectionId: urlCollectionId,
componentId: urlComponentId,
unitId: urlUnitId,
selectedItemId: urlSelectedItemId,
} = params;
const selectedItemIdIsUnit = !!urlSelectedItemId?.startsWith('lct:');
const [componentId, setComponentId] = useState(
skipUrlUpdate ? undefined : params.componentId,
skipUrlUpdate ? undefined : urlComponentId,
);
const [collectionId, setCollectionId] = useState(
skipUrlUpdate ? undefined : params.collectionId,
skipUrlUpdate ? undefined : urlCollectionId || (!selectedItemIdIsUnit ? urlSelectedItemId : undefined),
);
const [unitId, setUnitId] = useState(
skipUrlUpdate ? undefined : urlUnitId || (selectedItemIdIsUnit ? urlSelectedItemId : undefined),
);

const context = useMemo<LibraryContextData>(() => {
Expand All @@ -121,6 +134,8 @@ export const LibraryProvider = ({
setCollectionId,
componentId,
setComponentId,
unitId,
setUnitId,
readOnly,
isLoadingLibraryData,
showOnlyPublished,
Expand All @@ -144,6 +159,8 @@ export const LibraryProvider = ({
setCollectionId,
componentId,
setComponentId,
unitId,
setUnitId,
readOnly,
isLoadingLibraryData,
showOnlyPublished,
Expand Down
33 changes: 29 additions & 4 deletions src/library-authoring/common/context/SidebarContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum SidebarBodyComponentId {
Info = 'info',
ComponentInfo = 'component-info',
CollectionInfo = 'collection-info',
UnitInfo = 'unit-info',
}

export const COLLECTION_INFO_TABS = {
Expand All @@ -33,9 +34,20 @@ export const isComponentInfoTab = (tab: string): tab is ComponentInfoTab => (
Object.values<string>(COMPONENT_INFO_TABS).includes(tab)
);

type SidebarInfoTab = ComponentInfoTab | CollectionInfoTab;
export const UNIT_INFO_TABS = {
Preview: 'preview',
Organize: 'organize',
Usage: 'usage',
Settings: 'settings',
} as const;
export type UnitInfoTab = typeof UNIT_INFO_TABS[keyof typeof UNIT_INFO_TABS];
export const isUnitInfoTab = (tab: string): tab is UnitInfoTab => (
Object.values<string>(UNIT_INFO_TABS).includes(tab)
);

type SidebarInfoTab = ComponentInfoTab | CollectionInfoTab | UnitInfoTab;
const toSidebarInfoTab = (tab: string): SidebarInfoTab | undefined => (
isComponentInfoTab(tab) || isCollectionInfoTab(tab)
isComponentInfoTab(tab) || isCollectionInfoTab(tab) || isUnitInfoTab(tab)
? tab : undefined
);

Expand All @@ -53,10 +65,11 @@ export enum SidebarActions {
export type SidebarContextData = {
closeLibrarySidebar: () => void;
openAddContentSidebar: () => void;
openInfoSidebar: (componentId?: string, collectionId?: string) => void;
openInfoSidebar: (componentId?: string, collectionId?: string, unitId?: string) => void;
openLibrarySidebar: () => void;
openCollectionInfoSidebar: (collectionId: string) => void;
openComponentInfoSidebar: (usageKey: string) => void;
openUnitInfoSidebar: (usageKey: string) => void;
sidebarComponentInfo?: SidebarComponentInfo;
sidebarAction: SidebarActions;
setSidebarAction: (action: SidebarActions) => void;
Expand Down Expand Up @@ -131,11 +144,20 @@ export const SidebarProvider = ({
});
}, []);

const openInfoSidebar = useCallback((componentId?: string, collectionId?: string) => {
const openUnitInfoSidebar = useCallback((usageKey: string) => {
setSidebarComponentInfo({
id: usageKey,
type: SidebarBodyComponentId.UnitInfo,
});
}, []);

const openInfoSidebar = useCallback((componentId?: string, collectionId?: string, unitId?: string) => {
if (componentId) {
openComponentInfoSidebar(componentId);
} else if (collectionId) {
openCollectionInfoSidebar(collectionId);
} else if (unitId) {
openUnitInfoSidebar(unitId);
} else {
openLibrarySidebar();
}
Expand All @@ -150,6 +172,7 @@ export const SidebarProvider = ({
openComponentInfoSidebar,
sidebarComponentInfo,
openCollectionInfoSidebar,
openUnitInfoSidebar,
sidebarAction,
setSidebarAction,
resetSidebarAction,
Expand All @@ -166,6 +189,7 @@ export const SidebarProvider = ({
openComponentInfoSidebar,
sidebarComponentInfo,
openCollectionInfoSidebar,
openUnitInfoSidebar,
sidebarAction,
setSidebarAction,
resetSidebarAction,
Expand All @@ -191,6 +215,7 @@ export function useSidebarContext(): SidebarContextData {
openLibrarySidebar: () => {},
openComponentInfoSidebar: () => {},
openCollectionInfoSidebar: () => {},
openUnitInfoSidebar: () => {},
sidebarAction: SidebarActions.None,
setSidebarAction: () => {},
resetSidebarAction: () => {},
Expand Down
15 changes: 14 additions & 1 deletion src/library-authoring/components/ContainerCard.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useCallback } from 'react';
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import {
ActionRow,
Expand All @@ -11,6 +12,8 @@ import { Link } from 'react-router-dom';
import { type ContainerHit, PublishStatus } from '../../search-manager';
import { useComponentPickerContext } from '../common/context/ComponentPickerContext';
import { useLibraryContext } from '../common/context/LibraryContext';
import { useSidebarContext } from '../common/context/SidebarContext';
import { useLibraryRoutes } from '../routes';
import BaseCard from './BaseCard';
import messages from './messages';

Expand Down Expand Up @@ -53,6 +56,7 @@ type ContainerCardProps = {
const ContainerCard = ({ hit } : ContainerCardProps) => {
const { componentPickerMode } = useComponentPickerContext();
const { showOnlyPublished } = useLibraryContext();
const { openUnitInfoSidebar } = useSidebarContext();

const {
blockType: itemType,
Expand All @@ -61,6 +65,7 @@ const ContainerCard = ({ hit } : ContainerCardProps) => {
numChildren,
published,
publishStatus,
usageKey: unitId,
} = hit;

const numChildrenCount = showOnlyPublished ? (
Expand All @@ -71,7 +76,15 @@ const ContainerCard = ({ hit } : ContainerCardProps) => {
showOnlyPublished ? formatted.published?.displayName : formatted.displayName
) ?? '';

const openContainer = () => {};
const { navigateTo } = useLibraryRoutes();

const openContainer = useCallback(() => {
if (itemType === 'unit') {
openUnitInfoSidebar(unitId);

navigateTo({ unitId });
}
}, [unitId, itemType, openUnitInfoSidebar, navigateTo]);

return (
<BaseCard
Expand Down
Loading