Skip to content

Commit

Permalink
feat: preserve library sidebar tab while switching items
Browse files Browse the repository at this point in the history
  • Loading branch information
rpenido committed Nov 28, 2024
1 parent abe68ac commit 07e6790
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 19 deletions.
33 changes: 31 additions & 2 deletions src/library-authoring/LibraryAuthoringPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,8 @@ describe('<LibraryAuthoringPage />', () => {
it('should open and close the collection sidebar', async () => {
await renderLibraryPage();

// Click on the first component. It could appear twice, in both "Recently Modified" and "Collections"
fireEvent.click((await screen.findAllByText('Collection 1'))[0]);
// Click on the first collection
fireEvent.click((await screen.findByText('Collection 1')));

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

Expand All @@ -425,6 +425,35 @@ describe('<LibraryAuthoringPage />', () => {
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();

// Click on the first collection
fireEvent.click((await screen.findByText('Collection 1')));

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

const { getByRole } = within(sidebar);

// Click on the Details tab
fireEvent.click(getByRole('tab', { name: 'Details' }));

// Change to a component
fireEvent.click((await screen.findAllByText('Introduction to Testing'))[0]);

// Check that the Details tab is still selected
expect(getByRole('tab', { name: 'Details' })).toHaveAttribute('aria-selected', 'true');

// Click on the Previews tab
fireEvent.click(getByRole('tab', { name: 'Preview' }));

// Switch back to the collection
fireEvent.click((await screen.findByText('Collection 1')));

// The Manage (default) tab should be selected because the collection does not have a Preview tab
expect(getByRole('tab', { name: 'Manage' })).toHaveAttribute('aria-selected', 'true');
});

it('can filter by capa problem type', async () => {
const problemTypes = {
'Multiple Choice': 'choiceresponse',
Expand Down
21 changes: 17 additions & 4 deletions src/library-authoring/collections/CollectionInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {
import { useCallback } from 'react';
import { useNavigate, useMatch } from 'react-router-dom';

import { useLibraryContext } from '../common/context';
import {
useLibraryContext,
type CollectionInfoTab,
COLLECTION_INFO_TABS,
isCollectionInfoTab,
COMPONENT_INFO_TABS,
} from '../common/context';
import CollectionDetails from './CollectionDetails';
import messages from './messages';
import { ContentTagsDrawer } from '../../content-tags-drawer';
Expand All @@ -24,8 +30,13 @@ const CollectionInfo = () => {
setCollectionId,
sidebarComponentInfo,
componentPickerMode,
setSidebarCurrentTab,
} = useLibraryContext();

const tab: CollectionInfoTab = (
sidebarComponentInfo?.currentTab && isCollectionInfoTab(sidebarComponentInfo.currentTab)
) ? sidebarComponentInfo?.currentTab : COLLECTION_INFO_TABS.Manage;

const sidebarCollectionId = sidebarComponentInfo?.id;
// istanbul ignore if: this should never happen
if (!sidebarCollectionId) {
Expand Down Expand Up @@ -63,15 +74,17 @@ const CollectionInfo = () => {
<Tabs
variant="tabs"
className="my-3 d-flex justify-content-around"
defaultActiveKey="manage"
defaultActiveKey={COMPONENT_INFO_TABS.Manage}
activeKey={tab}
onSelect={setSidebarCurrentTab}
>
<Tab eventKey="manage" title={intl.formatMessage(messages.manageTabTitle)}>
<Tab eventKey={COMPONENT_INFO_TABS.Manage} title={intl.formatMessage(messages.manageTabTitle)}>
<ContentTagsDrawer
id={collectionUsageKey}
variant="component"
/>
</Tab>
<Tab eventKey="details" title={intl.formatMessage(messages.detailsTabTitle)}>
<Tab eventKey={COMPONENT_INFO_TABS.Details} title={intl.formatMessage(messages.detailsTabTitle)}>
<CollectionDetails />
</Tab>
</Tabs>
Expand Down
39 changes: 35 additions & 4 deletions src/library-authoring/common/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,32 @@ export enum SidebarBodyComponentId {
CollectionInfo = 'collection-info',
}

export const COLLECTION_INFO_TABS = {
Manage: 'manage',
Details: 'details',
} as const;
export type CollectionInfoTab = typeof COLLECTION_INFO_TABS[keyof typeof COLLECTION_INFO_TABS];
export const isCollectionInfoTab = (tab: string): tab is CollectionInfoTab => (
Object.values<string>(COLLECTION_INFO_TABS).includes(tab)
);

export const COMPONENT_INFO_TABS = {
Preview: 'preview',
Manage: 'manage',
Details: 'details',
} as const;
export type ComponentInfoTab = typeof COMPONENT_INFO_TABS[keyof typeof COMPONENT_INFO_TABS];
export const isComponentInfoTab = (tab: string): tab is ComponentInfoTab => (
Object.values<string>(COMPONENT_INFO_TABS).includes(tab)
);

export interface SidebarComponentInfo {
type: SidebarBodyComponentId;
id: string;
/** Additional action on Sidebar display */
additionalAction?: SidebarAdditionalActions;
/** Current tab in the sidebar */
currentTab?: CollectionInfoTab | ComponentInfoTab;
}

export interface ComponentEditorInfo {
Expand Down Expand Up @@ -110,6 +131,7 @@ export type LibraryContextData = {
openComponentEditor: (usageKey: string, onClose?: () => void) => void;
closeComponentEditor: () => void;
resetSidebarAdditionalActions: () => void;
setSidebarCurrentTab: (tab: CollectionInfoTab | ComponentInfoTab) => void;
} & ComponentPickerType;

/**
Expand Down Expand Up @@ -209,22 +231,26 @@ export const LibraryProvider = ({
const openInfoSidebar = useCallback(() => {
setSidebarComponentInfo({ id: '', type: SidebarBodyComponentId.Info });
}, []);

const openComponentInfoSidebar = useCallback((usageKey: string, additionalAction?: SidebarAdditionalActions) => {
setSidebarComponentInfo({
setSidebarComponentInfo((prev) => ({
...prev,
id: usageKey,
type: SidebarBodyComponentId.ComponentInfo,
additionalAction,
});
}));
}, []);

const openCollectionInfoSidebar = useCallback((
newCollectionId: string,
additionalAction?: SidebarAdditionalActions,
) => {
setSidebarComponentInfo({
setSidebarComponentInfo((prev) => ({
...prev,
id: newCollectionId,
type: SidebarBodyComponentId.CollectionInfo,
additionalAction,
});
}));
}, []);

const addComponentToSelectedComponents = useCallback<ComponentSelectedEvent>((
Expand Down Expand Up @@ -257,6 +283,10 @@ export const LibraryProvider = ({
});
}, []);

const setSidebarCurrentTab = useCallback((tab: CollectionInfoTab | ComponentInfoTab) => {
setSidebarComponentInfo((prev) => (prev && { ...prev, currentTab: tab }));
}, []);

const { data: libraryData, isLoading: isLoadingLibraryData } = useContentLibrary(libraryId);

const readOnly = !!componentPickerMode || !libraryData?.canEditLibrary;
Expand Down Expand Up @@ -286,6 +316,7 @@ export const LibraryProvider = ({
openComponentEditor,
closeComponentEditor,
resetSidebarAdditionalActions,
setSidebarCurrentTab,
};
if (!componentPickerMode) {
return {
Expand Down
30 changes: 21 additions & 9 deletions src/library-authoring/component-info/ComponentInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import {
CheckBoxOutlineBlank,
} from '@openedx/paragon/icons';

import { SidebarAdditionalActions, useLibraryContext } from '../common/context';
import {
SidebarAdditionalActions,
useLibraryContext,
COMPONENT_INFO_TABS,
ComponentInfoTab,
isComponentInfoTab,
} from '../common/context';
import ComponentMenu from '../components';
import { canEditComponent } from '../components/ComponentEditorModal';
import ComponentDetails from './ComponentDetails';
Expand Down Expand Up @@ -96,20 +102,25 @@ const ComponentInfo = () => {
readOnly,
openComponentEditor,
resetSidebarAdditionalActions,
setSidebarCurrentTab,
} = useLibraryContext();

const jumpToCollections = sidebarComponentInfo?.additionalAction === SidebarAdditionalActions.JumpToAddCollections;
// Show Manage tab if JumpToAddCollections action is set in sidebarComponentInfo
const [tab, setTab] = React.useState(jumpToCollections ? 'manage' : 'preview');

const tab: ComponentInfoTab = (
sidebarComponentInfo?.currentTab && isComponentInfoTab(sidebarComponentInfo.currentTab)
) ? sidebarComponentInfo?.currentTab : COMPONENT_INFO_TABS.Preview;

useEffect(() => {
// Show Manage tab if JumpToAddCollections action is set in sidebarComponentInfo
if (jumpToCollections) {
setTab('manage');
setSidebarCurrentTab(COMPONENT_INFO_TABS.Manage);
}
}, [jumpToCollections]);

useEffect(() => {
// This is required to redo actions.
if (tab !== 'manage') {
if (tab !== COMPONENT_INFO_TABS.Manage) {
resetSidebarAdditionalActions();
}
}, [tab]);
Expand Down Expand Up @@ -158,16 +169,17 @@ const ComponentInfo = () => {
<Tabs
variant="tabs"
className="my-3 d-flex justify-content-around"
defaultActiveKey={COMPONENT_INFO_TABS.Preview}
activeKey={tab}
onSelect={(k: string) => setTab(k)}
onSelect={setSidebarCurrentTab}
>
<Tab eventKey="preview" title={intl.formatMessage(messages.previewTabTitle)}>
<Tab eventKey={COMPONENT_INFO_TABS.Preview} title={intl.formatMessage(messages.previewTabTitle)}>
<ComponentPreview />
</Tab>
<Tab eventKey="manage" title={intl.formatMessage(messages.manageTabTitle)}>
<Tab eventKey={COMPONENT_INFO_TABS.Manage} title={intl.formatMessage(messages.manageTabTitle)}>
<ComponentManagement />
</Tab>
<Tab eventKey="details" title={intl.formatMessage(messages.detailsTabTitle)}>
<Tab eventKey={COMPONENT_INFO_TABS.Details} title={intl.formatMessage(messages.detailsTabTitle)}>
<ComponentDetails />
</Tab>
</Tabs>
Expand Down

0 comments on commit 07e6790

Please sign in to comment.