Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions src/course-unit/add-component/AddComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ const AddComponent = ({
>
<ComponentPicker
showOnlyPublished
extraFilter={['NOT block_type = "unit"']}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oo good catches!

componentPickerMode={isAddLibraryContentModalOpen ? 'single' : 'multiple'}
onComponentSelected={handleLibraryV2Selection}
onChangeComponentSelection={setSelectedComponents}
Expand Down
10 changes: 8 additions & 2 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,14 @@ const App = () => {
<Route path="/libraries-v1" element={<StudioHome />} />
<Route path="/library/create" element={<CreateLibrary />} />
<Route path="/library/:libraryId/*" element={<LibraryLayout />} />
<Route path="/component-picker" element={<ComponentPicker />} />
<Route path="/component-picker/multiple" element={<ComponentPicker componentPickerMode="multiple" />} />
<Route
path="/component-picker"
element={<ComponentPicker extraFilter={['NOT block_type = "unit"']} />}
/>
<Route
path="/component-picker/multiple"
element={<ComponentPicker componentPickerMode="multiple" extraFilter={['NOT block_type = "unit"']} />}
/>
<Route path="/legacy/preview-changes/:usageKey" element={<PreviewChangesEmbed />} />
<Route path="/course/:courseId/*" element={<CourseAuthoringRoutes />} />
<Route path="/course_rerun/:courseId" element={<CourseRerun />} />
Expand Down
5 changes: 5 additions & 0 deletions src/library-authoring/LibraryAuthoringPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ const LibraryAuthoringPage = ({ returnToLibrarySelection }: LibraryAuthoringPage
libraryData,
isLoadingLibraryData,
showOnlyPublished,
extraFilter: contextExtraFilter,
componentId,
collectionId,
unitId,
Expand Down Expand Up @@ -223,6 +224,10 @@ const LibraryAuthoringPage = ({ returnToLibrarySelection }: LibraryAuthoringPage
extraFilter.push('last_published IS NOT NULL');
}

if (contextExtraFilter) {
extraFilter.push(...contextExtraFilter);
}

const activeTypeFilters = {
components: 'type = "library_block"',
collections: 'type = "collection"',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
mockXBlockFields,
} from '../data/api.mocks';
import {
getContentLibraryApiUrl, getCreateLibraryBlockUrl, getLibraryCollectionComponentApiUrl, getLibraryPasteClipboardUrl,
getContentLibraryApiUrl, getCreateLibraryBlockUrl, getLibraryCollectionItemsApiUrl, getLibraryPasteClipboardUrl,
getXBlockFieldsApiUrl,
} from '../data/api';
import { mockBroadcastChannel, mockClipboardEmpty, mockClipboardHtml } from '../../generic/data/api.mock';
Expand Down Expand Up @@ -137,7 +137,7 @@ describe('<AddContentContainer />', () => {
const url = getCreateLibraryBlockUrl(libraryId);
const usageKey = mockXBlockFields.usageKeyNewHtml;
const updateBlockUrl = getXBlockFieldsApiUrl(usageKey);
const collectionComponentUrl = getLibraryCollectionComponentApiUrl(
const collectionComponentUrl = getLibraryCollectionItemsApiUrl(
libraryId,
collectionId,
);
Expand Down Expand Up @@ -195,7 +195,7 @@ describe('<AddContentContainer />', () => {

const pasteUrl = getLibraryPasteClipboardUrl(libraryId);
const collectionId = 'some-collection-id';
const collectionComponentUrl = getLibraryCollectionComponentApiUrl(
const collectionComponentUrl = getLibraryCollectionItemsApiUrl(
libraryId,
collectionId,
);
Expand All @@ -220,7 +220,7 @@ describe('<AddContentContainer />', () => {

const pasteUrl = getLibraryPasteClipboardUrl(libraryId);
const collectionId = 'some-collection-id';
const collectionComponentUrl = getLibraryCollectionComponentApiUrl(
const collectionComponentUrl = getLibraryCollectionItemsApiUrl(
libraryId,
collectionId,
);
Expand Down
8 changes: 4 additions & 4 deletions src/library-authoring/add-content/AddContentContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { getCanEdit } from '../../course-unit/data/selectors';
import {
useCreateLibraryBlock,
useLibraryPasteClipboard,
useAddComponentsToCollection,
useAddItemsToCollection,
useBlockTypesMetadata,
} from '../data/apiHooks';
import { useLibraryContext } from '../common/context/LibraryContext';
Expand Down Expand Up @@ -195,7 +195,7 @@ const AddContentContainer = () => {
openCreateUnitModal,
openComponentEditor,
} = useLibraryContext();
const updateComponentsMutation = useAddComponentsToCollection(libraryId, collectionId);
const updateItemsMutation = useAddItemsToCollection(libraryId, collectionId);
const createBlockMutation = useCreateLibraryBlock();
const pasteClipboardMutation = useLibraryPasteClipboard();
const { showToast } = useContext(ToastContext);
Expand Down Expand Up @@ -273,8 +273,8 @@ const AddContentContainer = () => {
contentTypes.push(pasteButton);
}

const linkComponent = (usageKey: string) => {
updateComponentsMutation.mutateAsync([usageKey]).catch(() => {
const linkComponent = (opaque_key: string) => {
updateItemsMutation.mutateAsync([opaque_key]).catch(() => {
showToast(intl.formatMessage(messages.errorAssociateComponentMessage));
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ describe('<PickLibraryContentModal />', () => {
});

it('can pick components from the modal', async () => {
const mockAddComponentsToCollection = jest.fn();
jest.spyOn(api, 'addComponentsToCollection').mockImplementation(mockAddComponentsToCollection);
const mockAddItemsToCollection = jest.fn();
jest.spyOn(api, 'addItemsToCollection').mockImplementation(mockAddItemsToCollection);

render();

Expand All @@ -67,7 +67,7 @@ describe('<PickLibraryContentModal />', () => {
fireEvent.click(screen.queryAllByRole('button', { name: 'Add to Collection' })[0]);

await waitFor(() => {
expect(mockAddComponentsToCollection).toHaveBeenCalledWith(
expect(mockAddItemsToCollection).toHaveBeenCalledWith(
libraryId,
'collectionId',
['lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd'],
Expand All @@ -78,8 +78,8 @@ describe('<PickLibraryContentModal />', () => {
});

it('show error when api call fails', async () => {
const mockAddComponentsToCollection = jest.fn().mockRejectedValue(new Error('Failed to add components'));
jest.spyOn(api, 'addComponentsToCollection').mockImplementation(mockAddComponentsToCollection);
const mockAddItemsToCollection = jest.fn().mockRejectedValue(new Error('Failed to add components'));
jest.spyOn(api, 'addItemsToCollection').mockImplementation(mockAddItemsToCollection);
render();

// Wait for the content library to load
Expand All @@ -95,7 +95,7 @@ describe('<PickLibraryContentModal />', () => {
fireEvent.click(screen.queryAllByRole('button', { name: 'Add to Collection' })[0]);

await waitFor(() => {
expect(mockAddComponentsToCollection).toHaveBeenCalledWith(
expect(mockAddItemsToCollection).toHaveBeenCalledWith(
libraryId,
'collectionId',
['lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd'],
Expand Down
4 changes: 2 additions & 2 deletions src/library-authoring/add-content/PickLibraryContentModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ActionRow, Button, StandardModal } from '@openedx/paragon';
import { ToastContext } from '../../generic/toast-context';
import { useLibraryContext } from '../common/context/LibraryContext';
import type { SelectedComponent } from '../common/context/ComponentPickerContext';
import { useAddComponentsToCollection } from '../data/apiHooks';
import { useAddItemsToCollection } from '../data/apiHooks';
import messages from './messages';

interface PickLibraryContentModalFooterProps {
Expand Down Expand Up @@ -51,7 +51,7 @@ export const PickLibraryContentModal: React.FC<PickLibraryContentModalProps> = (
throw new Error('libraryId and componentPicker are required');
}

const updateComponentsMutation = useAddComponentsToCollection(libraryId, collectionId);
const updateComponentsMutation = useAddItemsToCollection(libraryId, collectionId);

const { showToast } = useContext(ToastContext);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { mockContentSearchConfig, mockGetBlockTypes } from '../../search-manager
import { mockBroadcastChannel, mockClipboardEmpty } from '../../generic/data/api.mock';
import { LibraryLayout } from '..';
import { ContentTagsDrawer } from '../../content-tags-drawer';
import { getLibraryCollectionComponentApiUrl } from '../data/api';
import { getLibraryCollectionItemsApiUrl } from '../data/api';

let axiosMock: MockAdapter;
let mockShowToast;
Expand Down Expand Up @@ -351,7 +351,7 @@ describe('<LibraryCollectionPage />', () => {
});

it('should remove component from collection and hides sidebar', async () => {
const url = getLibraryCollectionComponentApiUrl(
const url = getLibraryCollectionItemsApiUrl(
mockContentLibrary.libraryId,
mockCollection.collectionId,
);
Expand All @@ -370,8 +370,8 @@ describe('<LibraryCollectionPage />', () => {
fireEvent.click(await screen.findByText('Remove from collection'));
await waitFor(() => {
expect(axiosMock.history.delete.length).toEqual(1);
expect(mockShowToast).toHaveBeenCalledWith('Component successfully removed');
});
expect(mockShowToast).toHaveBeenCalledWith('Item successfully removed');
// Should close sidebar as component was removed
await waitFor(() => expect(screen.queryByTestId('library-sidebar')).not.toBeInTheDocument());
});
Expand Down
8 changes: 7 additions & 1 deletion src/library-authoring/collections/LibraryCollectionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ const LibraryCollectionPage = () => {
}

const { componentPickerMode } = useComponentPickerContext();
const { showOnlyPublished, setCollectionId, componentId } = useLibraryContext();
const {
showOnlyPublished, extraFilter: contextExtraFilter, setCollectionId, componentId,
} = useLibraryContext();
const { sidebarComponentInfo, openInfoSidebar } = useSidebarContext();

const {
Expand Down Expand Up @@ -182,6 +184,10 @@ const LibraryCollectionPage = () => {
extraFilter.push('last_published IS NOT NULL');
}

if (contextExtraFilter) {
extraFilter.push(...contextExtraFilter);
}

return (
<div className="d-flex">
<div className="flex-grow-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {

export interface SelectedComponent {
usageKey: string;
blockType: string;
blockType?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does a Component ever have a usageKey but not a blockType ? Or is this really an ItemPicker that can select non-components?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was planning not to pass blockType when selecting 'Units', but I ended up passing it as the containerType has a similar meaning. Reverted 0962eab

}

export type ComponentSelectedEvent = (selectedComponent: SelectedComponent) => void;
Expand Down
6 changes: 6 additions & 0 deletions src/library-authoring/common/context/LibraryContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export type LibraryContextData = {
setUnitId: (unitId?: string) => void;
// Only show published components
showOnlyPublished: boolean;
// Additional filtering
extraFilter?: string[];
// "Create New Collection" modal
isCreateCollectionModalOpen: boolean;
openCreateCollectionModal: () => void;
Expand Down Expand Up @@ -66,6 +68,7 @@ type LibraryProviderProps = {
children?: React.ReactNode;
libraryId: string;
showOnlyPublished?: boolean;
extraFilter?: string[]
// If set, will initialize the current collection and/or component from the current URL
skipUrlUpdate?: boolean;

Expand All @@ -83,6 +86,7 @@ export const LibraryProvider = ({
children,
libraryId,
showOnlyPublished = false,
extraFilter = [],
skipUrlUpdate = false,
componentPicker,
}: LibraryProviderProps) => {
Expand Down Expand Up @@ -139,6 +143,7 @@ export const LibraryProvider = ({
readOnly,
isLoadingLibraryData,
showOnlyPublished,
extraFilter,
isCreateCollectionModalOpen,
openCreateCollectionModal,
closeCreateCollectionModal,
Expand All @@ -164,6 +169,7 @@ export const LibraryProvider = ({
readOnly,
isLoadingLibraryData,
showOnlyPublished,
extraFilter,
isCreateCollectionModalOpen,
openCreateCollectionModal,
closeCreateCollectionModal,
Expand Down
12 changes: 8 additions & 4 deletions src/library-authoring/component-info/ComponentManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import {

import { useLibraryContext } from '../common/context/LibraryContext';
import { SidebarActions, useSidebarContext } from '../common/context/SidebarContext';
import { useLibraryBlockMetadata } from '../data/apiHooks';
import { ContentTagsDrawer, useContentTaxonomyTagsData } from '../../content-tags-drawer';
import { useLibraryBlockMetadata, useUpdateComponentCollections } from '../data/apiHooks';
import StatusWidget from '../generic/status-widget';
import { ManageCollections } from '../generic/manage-collections';
import messages from './messages';
import { ContentTagsDrawer, useContentTaxonomyTagsData } from '../../content-tags-drawer';
import ManageCollections from './ManageCollections';

const ComponentManagement = () => {
const intl = useIntl();
Expand Down Expand Up @@ -130,7 +130,11 @@ const ComponentManagement = () => {
</Collapsible.Visible>
</Collapsible.Trigger>
<Collapsible.Body className="collapsible-body">
<ManageCollections usageKey={usageKey} collections={componentMetadata.collections} />
<ManageCollections
opaqueKey={usageKey}
collections={componentMetadata.collections}
useUpdateCollectionsHook={useUpdateComponentCollections}
/>
</Collapsible.Body>
</Collapsible.Advanced>
</Stack>
Expand Down
45 changes: 0 additions & 45 deletions src/library-authoring/component-info/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,51 +136,6 @@ const messages = defineMessages({
defaultMessage: 'Component Preview',
description: 'Title for preview modal',
},
manageCollectionsText: {
id: 'course-authoring.library-authoring.component.manage-tab.collections.text',
defaultMessage: 'Manage Collections',
description: 'Header and button text for collection section in manage tab',
},
manageCollectionsAddBtnText: {
id: 'course-authoring.library-authoring.component.manage-tab.collections.btn-text',
defaultMessage: 'Add to Collection',
description: 'Button text for collection section in manage tab',
},
manageCollectionsSearchPlaceholder: {
id: 'course-authoring.library-authoring.component.manage-tab.collections.search-placeholder',
defaultMessage: 'Search',
description: 'Placeholder text for collection search in manage tab',
},
manageCollectionsSelectionLabel: {
id: 'course-authoring.library-authoring.component.manage-tab.collections.selection-aria-label',
defaultMessage: 'Collection selection',
description: 'Aria label text for collection selection box',
},
manageCollectionsToComponentSuccess: {
id: 'course-authoring.library-authoring.component.manage-tab.collections.add-success',
defaultMessage: 'Component collections updated',
description: 'Message to display on updating component collections',
},
manageCollectionsToComponentFailed: {
id: 'course-authoring.library-authoring.component.manage-tab.collections.add-failed',
defaultMessage: 'Failed to update Component collections',
description: 'Message to display on failure of updating component collections',
},
manageCollectionsToComponentConfirmBtn: {
id: 'course-authoring.library-authoring.component.manage-tab.collections.add-confirm-btn',
defaultMessage: 'Confirm',
description: 'Button text to confirm collections for a component',
},
manageCollectionsToComponentCancelBtn: {
id: 'course-authoring.library-authoring.component.manage-tab.collections.add-cancel-btn',
defaultMessage: 'Cancel',
description: 'Button text to cancel collections selection for a component',
},
componentNotOrganizedIntoCollection: {
id: 'course-authoring.library-authoring.component.manage-tab.collections.no-collections',
defaultMessage: 'This component is not organized into any collection.',
description: 'Message to display in manage collections section when component is not part of any collection.',
},
componentPickerSingleSelect: {
id: 'course-authoring.library-authoring.component-picker.single-select',
defaultMessage: 'Add to Course', // TODO: Change this message to a generic one?
Expand Down
4 changes: 3 additions & 1 deletion src/library-authoring/component-picker/ComponentPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const defaultSelectionChangedCallback: ComponentSelectionChangedEvent = (selecti
window.parent.postMessage({ type: 'pickerSelectionChanged', selections }, '*');
};

type ComponentPickerProps = { libraryId?: string, showOnlyPublished?: boolean } & (
type ComponentPickerProps = { libraryId?: string, showOnlyPublished?: boolean, extraFilter?: string[] } & (
{
componentPickerMode?: 'single',
onComponentSelected?: ComponentSelectedEvent,
Expand All @@ -54,6 +54,7 @@ export const ComponentPicker: React.FC<ComponentPickerProps> = ({
/** Restrict the component picker to a specific library */
libraryId,
showOnlyPublished,
extraFilter,
componentPickerMode = 'single',
/** This default callback is used to send the selected component back to the parent window,
* when the component picker is used in an iframe.
Expand Down Expand Up @@ -105,6 +106,7 @@ export const ComponentPicker: React.FC<ComponentPickerProps> = ({
<LibraryProvider
libraryId={selectedLibrary}
showOnlyPublished={calcShowOnlyPublished}
extraFilter={extraFilter}
skipUrlUpdate
>
<SidebarProvider>
Expand Down
Loading