fix: cannot open item sidebar after closing with 'X' button (#2241)

When closing an item (component, unit, section, container, etc) sidebar, and trying to reopen it inmediately by clicking the card, it was not opening because navigateTo was being used, but the URL already was the same you were being navigated to. So we also have to update the sidebar item info in the sidebar context in order for it to reopen properly.
This commit is contained in:
Daniel Valenzuela
2025-07-04 05:38:37 -04:00
committed by GitHub
parent dd2853900b
commit ae82486d72
9 changed files with 51 additions and 40 deletions

View File

@@ -398,7 +398,7 @@ describe('<LibraryAuthoringPage />', () => {
});
}, 10000);
it('should open and close the component sidebar', async () => {
it('should open, close and re-open the component sidebar', async () => {
const mockResult0 = { ...mockResult }.results[0].hits[0];
const displayName = 'Introduction to Testing';
expect(mockResult0.display_name).toStrictEqual(displayName);
@@ -416,6 +416,10 @@ describe('<LibraryAuthoringPage />', () => {
fireEvent.click(closeButton);
await waitFor(() => expect(screen.queryByTestId('library-sidebar')).not.toBeInTheDocument());
fireEvent.click((await screen.findAllByText(displayName))[0]);
await waitFor(() => expect(screen.queryByTestId('library-sidebar')).toBeInTheDocument());
});
it('should open component sidebar, showing manage tab on clicking add to collection menu item - component', async () => {

View File

@@ -10,6 +10,7 @@ import { useParams } from 'react-router-dom';
import { useStateWithUrlSearchParam } from '../../../hooks';
import { useComponentPickerContext } from './ComponentPickerContext';
import { useLibraryContext } from './LibraryContext';
import { useLibraryRoutes } from '../../routes';
export enum SidebarBodyItemId {
AddContent = 'add-content',
@@ -86,6 +87,7 @@ export type SidebarContextData = {
openCollectionInfoSidebar: (collectionId: string) => void;
openComponentInfoSidebar: (usageKey: string) => void;
openContainerInfoSidebar: (usageKey: string) => void;
openItemSidebar: (selectedItemId: string, type: SidebarBodyItemId) => void;
sidebarItemInfo?: SidebarItemInfo;
sidebarAction: SidebarActions;
setSidebarAction: (action: SidebarActions) => void;
@@ -172,6 +174,12 @@ export const SidebarProvider = ({
});
}, []);
const { navigateTo } = useLibraryRoutes();
const openItemSidebar = useCallback((selectedItemId: string, type: SidebarBodyItemId) => {
navigateTo({ selectedItemId });
setSidebarItemInfo({ id: selectedItemId, type });
}, [navigateTo, setSidebarItemInfo]);
// Set the initial sidebar state based on the URL parameters and context.
const { selectedItemId } = useParams();
const { collectionId, containerId } = useLibraryContext();
@@ -236,6 +244,7 @@ export const SidebarProvider = ({
sidebarItemInfo,
openCollectionInfoSidebar,
openContainerInfoSidebar,
openItemSidebar,
sidebarAction,
setSidebarAction,
resetSidebarAction,
@@ -254,6 +263,7 @@ export const SidebarProvider = ({
sidebarItemInfo,
openCollectionInfoSidebar,
openContainerInfoSidebar,
openItemSidebar,
sidebarAction,
setSidebarAction,
resetSidebarAction,
@@ -281,6 +291,7 @@ export function useSidebarContext(): SidebarContextData {
openComponentInfoSidebar: () => {},
openCollectionInfoSidebar: () => {},
openContainerInfoSidebar: () => {},
openItemSidebar: () => {},
sidebarAction: SidebarActions.None,
setSidebarAction: () => {},
resetSidebarAction: () => {},

View File

@@ -116,7 +116,7 @@ type CollectionCardProps = {
const CollectionCard = ({ hit } : CollectionCardProps) => {
const { componentPickerMode } = useComponentPickerContext();
const { setCollectionId, showOnlyPublished } = useLibraryContext();
const { openCollectionInfoSidebar, sidebarItemInfo } = useSidebarContext();
const { openCollectionInfoSidebar, openItemSidebar, sidebarItemInfo } = useSidebarContext();
const {
type: itemType,
@@ -144,7 +144,7 @@ const CollectionCard = ({ hit } : CollectionCardProps) => {
if (doubleClicked) {
navigateTo({ collectionId });
} else {
navigateTo({ selectedItemId: collectionId });
openItemSidebar(collectionId, SidebarBodyItemId.CollectionInfo);
}
// In component picker mode, we want to open the sidebar or the collection
@@ -154,7 +154,7 @@ const CollectionCard = ({ hit } : CollectionCardProps) => {
} else {
openCollectionInfoSidebar(collectionId);
}
}, [collectionId, navigateTo, openCollectionInfoSidebar, setCollectionId, componentPickerMode]);
}, [collectionId, navigateTo, openItemSidebar, openCollectionInfoSidebar, setCollectionId, componentPickerMode]);
return (
<BaseCard

View File

@@ -6,6 +6,7 @@ import {
initializeMocks,
} from '../../testUtils';
import { LibraryProvider } from '../common/context/LibraryContext';
import { SidebarProvider } from '../common/context/SidebarContext';
import { getClipboardUrl } from '../../generic/data/api';
import { ContentHit } from '../../search-manager';
import ComponentCard from './ComponentCard';
@@ -53,7 +54,9 @@ const render = () => baseRender(<ComponentCard hit={contentHit} />, {
params: { libraryId },
extraWrapper: ({ children }) => (
<LibraryProvider libraryId={libraryId}>
{ children }
<SidebarProvider>
{ children }
</SidebarProvider>
</LibraryProvider>
),
});

View File

@@ -7,7 +7,6 @@ import { type ContentHit, PublishStatus } from '../../search-manager';
import { useComponentPickerContext } from '../common/context/ComponentPickerContext';
import { useLibraryContext } from '../common/context/LibraryContext';
import { SidebarBodyItemId, useSidebarContext } from '../common/context/SidebarContext';
import { useLibraryRoutes } from '../routes';
import AddComponentWidget from './AddComponentWidget';
import BaseCard from './BaseCard';
import { ComponentMenu } from './ComponentMenu';
@@ -18,7 +17,7 @@ type ComponentCardProps = {
const ComponentCard = ({ hit }: ComponentCardProps) => {
const { showOnlyPublished } = useLibraryContext();
const { openComponentInfoSidebar, sidebarItemInfo } = useSidebarContext();
const { openComponentInfoSidebar, openItemSidebar, sidebarItemInfo } = useSidebarContext();
const { componentPickerMode } = useComponentPickerContext();
const {
@@ -35,16 +34,15 @@ const ComponentCard = ({ hit }: ComponentCardProps) => {
showOnlyPublished ? formatted.published?.displayName : formatted.displayName
) ?? '';
const { navigateTo } = useLibraryRoutes();
const selectComponent = useCallback(() => {
if (!componentPickerMode) {
navigateTo({ selectedItemId: usageKey });
openItemSidebar(usageKey, SidebarBodyItemId.ComponentInfo);
} else {
// In component picker mode, we want to open the sidebar
// without changing the URL
openComponentInfoSidebar(usageKey);
}
}, [usageKey, navigateTo, openComponentInfoSidebar]);
}, [usageKey, openItemSidebar, openComponentInfoSidebar, componentPickerMode]);
const selected = sidebarItemInfo?.type === SidebarBodyItemId.ComponentInfo
&& sidebarItemInfo.id === usageKey;

View File

@@ -10,7 +10,7 @@ import { MoreVert } from '@openedx/paragon/icons';
import { getBlockType } from '@src/generic/key-utils';
import { useLibraryContext } from '../common/context/LibraryContext';
import { SidebarActions, useSidebarContext } from '../common/context/SidebarContext';
import { SidebarActions, SidebarBodyItemId, useSidebarContext } from '../common/context/SidebarContext';
import { useClipboard } from '../../generic/clipboard';
import { ToastContext } from '../../generic/toast-context';
import {
@@ -36,11 +36,11 @@ export const ComponentMenu = ({ usageKey }: { usageKey: string }) => {
const {
sidebarItemInfo,
openComponentInfoSidebar,
closeLibrarySidebar,
setSidebarAction,
openItemSidebar,
} = useSidebarContext();
const { navigateTo, insideCollection } = useLibraryRoutes();
const { insideCollection } = useLibraryRoutes();
const canEdit = usageKey && canEditComponent(usageKey);
const { showToast } = useContext(ToastContext);
@@ -93,9 +93,9 @@ export const ComponentMenu = ({ usageKey }: { usageKey: string }) => {
};
const handleEdit = useCallback(() => {
navigateTo({ selectedItemId: usageKey });
openItemSidebar(usageKey, SidebarBodyItemId.ComponentInfo);
openComponentEditor(usageKey);
}, [usageKey, navigateTo]);
}, [usageKey, openItemSidebar, openComponentEditor]);
const scheduleJumpToCollection = useRunOnNextRender(() => {
// TODO: Ugly hack to make sure sidebar shows add to collection section
@@ -104,13 +104,12 @@ export const ComponentMenu = ({ usageKey }: { usageKey: string }) => {
});
const showManageCollections = useCallback(() => {
navigateTo({ selectedItemId: usageKey });
openItemSidebar(usageKey, SidebarBodyItemId.ComponentInfo);
scheduleJumpToCollection();
}, [
scheduleJumpToCollection,
openComponentInfoSidebar,
usageKey,
navigateTo,
openItemSidebar,
]);
const containerType = containerId ? getBlockType(containerId) : 'collection';

View File

@@ -16,7 +16,7 @@ import { ToastContext } from '@src/generic/toast-context';
import { type ContainerHit, Highlight, PublishStatus } from '../../search-manager';
import { useComponentPickerContext } from '../common/context/ComponentPickerContext';
import { useLibraryContext } from '../common/context/LibraryContext';
import { SidebarActions, useSidebarContext } from '../common/context/SidebarContext';
import { SidebarActions, SidebarBodyItemId, useSidebarContext } from '../common/context/SidebarContext';
import { useRemoveItemsFromCollection } from '../data/apiHooks';
import { useLibraryRoutes } from '../routes';
import messages from './messages';
@@ -245,7 +245,7 @@ type ContainerCardProps = {
const ContainerCard = ({ hit } : ContainerCardProps) => {
const { componentPickerMode } = useComponentPickerContext();
const { showOnlyPublished } = useLibraryContext();
const { openContainerInfoSidebar, sidebarItemInfo } = useSidebarContext();
const { openContainerInfoSidebar, openItemSidebar, sidebarItemInfo } = useSidebarContext();
const {
blockType: itemType,
@@ -276,11 +276,11 @@ const ContainerCard = ({ hit } : ContainerCardProps) => {
// without changing the URL
openContainerInfoSidebar(containerKey);
} else if (!doubleClicked) {
navigateTo({ selectedItemId: containerKey });
openItemSidebar(containerKey, SidebarBodyItemId.ContainerInfo);
} else {
navigateTo({ containerId: containerKey });
}
}, [containerKey, openContainerInfoSidebar, navigateTo]);
}, [containerKey, openContainerInfoSidebar, openItemSidebar, navigateTo]);
return (
<BaseCard

View File

@@ -23,7 +23,7 @@ import { Container } from '../data/api';
import { ToastContext } from '../../generic/toast-context';
import TagCount from '../../generic/tag-count';
import { useLibraryRoutes } from '../routes';
import { SidebarActions, useSidebarContext } from '../common/context/SidebarContext';
import { SidebarActions, SidebarBodyItemId, useSidebarContext } from '../common/context/SidebarContext';
import { useRunOnNextRender } from '../../utils';
import { ContainerMenu } from '../containers/ContainerCard';
@@ -46,8 +46,7 @@ const ContainerRow = ({ containerKey, container, readOnly }: ContainerRowProps)
const { showToast } = useContext(ToastContext);
const updateMutation = useUpdateContainer(container.originalId, containerKey);
const { showOnlyPublished } = useLibraryContext();
const { navigateTo } = useLibraryRoutes();
const { setSidebarAction } = useSidebarContext();
const { setSidebarAction, openItemSidebar } = useSidebarContext();
const handleSaveDisplayName = async (newDisplayName: string) => {
try {
@@ -67,10 +66,10 @@ const ContainerRow = ({ containerKey, container, readOnly }: ContainerRowProps)
setTimeout(() => setSidebarAction(SidebarActions.JumpToManageTags), 250);
});
const jumpToManageTags = () => {
navigateTo({ selectedItemId: container.originalId });
const jumpToManageTags = useCallback(() => {
openItemSidebar(container.originalId, SidebarBodyItemId.ContainerInfo);
scheduleJumpToTags();
};
}, [openItemSidebar, scheduleJumpToTags, container.originalId]);
return (
<>
@@ -127,7 +126,7 @@ export const LibraryContainerChildren = ({ containerKey, readOnly }: LibraryCont
const [orderedChildren, setOrderedChildren] = useState<LibraryContainerMetadataWithUniqueId[]>([]);
const { showOnlyPublished, readOnly: libReadOnly } = useLibraryContext();
const { navigateTo } = useLibraryRoutes();
const { sidebarItemInfo } = useSidebarContext();
const { sidebarItemInfo, openItemSidebar } = useSidebarContext();
const [activeDraggingId, setActiveDraggingId] = useState<string | null>(null);
const orderMutator = useUpdateContainerChildren(containerKey);
const { showToast } = useContext(ToastContext);
@@ -169,11 +168,11 @@ export const LibraryContainerChildren = ({ containerKey, readOnly }: LibraryCont
const handleChildClick = useCallback((child: LibraryContainerMetadataWithUniqueId, numberOfClicks: number) => {
const doubleClicked = numberOfClicks > 1;
if (!doubleClicked) {
navigateTo({ selectedItemId: child.originalId });
openItemSidebar(child.originalId, SidebarBodyItemId.ContainerInfo);
} else {
navigateTo({ containerId: child.originalId });
}
}, [navigateTo]);
}, [openItemSidebar, navigateTo]);
const getComponentStyle = useCallback((childId: string) => {
const style: { marginBottom: string, borderRadius: string, outline?: string } = {

View File

@@ -26,9 +26,8 @@ import {
useUpdateXBlockFields,
} from '../data/apiHooks';
import { LibraryBlock } from '../LibraryBlock';
import { useLibraryRoutes } from '../routes';
import messages from './messages';
import { SidebarActions, useSidebarContext } from '../common/context/SidebarContext';
import { SidebarActions, SidebarBodyItemId, useSidebarContext } from '../common/context/SidebarContext';
import { ToastContext } from '../../generic/toast-context';
import { canEditComponent } from '../components/ComponentEditorModal';
import { useRunOnNextRender } from '../../utils';
@@ -57,8 +56,7 @@ const BlockHeader = ({ block, readOnly }: ComponentBlockProps) => {
const intl = useIntl();
const { showOnlyPublished } = useLibraryContext();
const { showToast } = useContext(ToastContext);
const { navigateTo } = useLibraryRoutes();
const { setSidebarAction } = useSidebarContext();
const { setSidebarAction, openItemSidebar } = useSidebarContext();
const updateMutation = useUpdateXBlockFields(block.originalId);
@@ -84,7 +82,7 @@ const BlockHeader = ({ block, readOnly }: ComponentBlockProps) => {
/* istanbul ignore next */
const jumpToManageTags = () => {
navigateTo({ selectedItemId: block.originalId });
openItemSidebar(block.originalId, SidebarBodyItemId.ComponentInfo);
scheduleJumpToTags();
};
@@ -132,19 +130,18 @@ const BlockHeader = ({ block, readOnly }: ComponentBlockProps) => {
/** ComponentBlock to render preview of given component under Unit */
const ComponentBlock = ({ block, readOnly, isDragging }: ComponentBlockProps) => {
const { showOnlyPublished } = useLibraryContext();
const { navigateTo } = useLibraryRoutes();
const { openComponentEditor } = useLibraryContext();
const { sidebarItemInfo } = useSidebarContext();
const { sidebarItemInfo, openItemSidebar } = useSidebarContext();
const handleComponentSelection = useCallback((numberOfClicks: number) => {
navigateTo({ selectedItemId: block.originalId });
openItemSidebar(block.originalId, SidebarBodyItemId.ComponentInfo);
const canEdit = canEditComponent(block.originalId);
if (numberOfClicks > 1 && canEdit) {
// Open editor on double click.
openComponentEditor(block.originalId);
}
}, [block, navigateTo, canEditComponent, openComponentEditor]);
}, [block, openItemSidebar, canEditComponent, openComponentEditor]);
useEffect(() => {
if (block.isNew) {