refactor: lib component picker modal to only post message with block info (#1401)
This commit is contained in:
@@ -5,21 +5,18 @@ import {
|
||||
Tabs,
|
||||
Stack,
|
||||
} from '@openedx/paragon';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { ToastContext } from '../../generic/toast-context';
|
||||
import { useLibraryContext } from '../common/context';
|
||||
import { ComponentMenu } from '../components';
|
||||
import { canEditComponent } from '../components/ComponentEditorModal';
|
||||
import { useAddComponentToCourse } from '../data/apiHooks';
|
||||
import ComponentDetails from './ComponentDetails';
|
||||
import ComponentManagement from './ComponentManagement';
|
||||
import ComponentPreview from './ComponentPreview';
|
||||
import messages from './messages';
|
||||
import { getBlockType } from '../../generic/key-utils';
|
||||
|
||||
const ComponentInfo = () => {
|
||||
const intl = useIntl();
|
||||
const { showToast } = useContext(ToastContext);
|
||||
|
||||
const {
|
||||
sidebarComponentUsageKey: usageKey,
|
||||
@@ -34,22 +31,15 @@ const ComponentInfo = () => {
|
||||
throw new Error('usageKey is required');
|
||||
}
|
||||
|
||||
const {
|
||||
mutateAsync: addComponentToCourse,
|
||||
reset,
|
||||
} = useAddComponentToCourse(parentLocator, usageKey);
|
||||
|
||||
const canEdit = canEditComponent(usageKey);
|
||||
|
||||
const handleAddComponentToCourse = () => {
|
||||
addComponentToCourse()
|
||||
.then(() => {
|
||||
window.parent.postMessage('closeComponentPicker', '*');
|
||||
})
|
||||
.catch(() => {
|
||||
showToast(intl.formatMessage(messages.addComponentToCourseError));
|
||||
reset();
|
||||
});
|
||||
window.parent.postMessage({
|
||||
parentLocator,
|
||||
usageKey,
|
||||
type: 'pickerComponentSelected',
|
||||
category: getBlockType(usageKey),
|
||||
}, '*');
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import type MockAdapter from 'axios-mock-adapter';
|
||||
|
||||
import { mockContentSearchConfig, mockSearchResult } from '../../search-manager/data/api.mock';
|
||||
import {
|
||||
initializeMocks,
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
within,
|
||||
} from '../../testUtils';
|
||||
import mockResult from '../__mocks__/library-search.json';
|
||||
@@ -17,7 +14,6 @@ import {
|
||||
mockGetContentLibraryV2List,
|
||||
mockLibraryBlockMetadata,
|
||||
} from '../data/api.mocks';
|
||||
import { getXBlockBaseApiUrl } from '../data/api';
|
||||
|
||||
import { ComponentPicker } from './ComponentPicker';
|
||||
|
||||
@@ -27,8 +23,7 @@ mockGetCollectionMetadata.applyMock();
|
||||
mockGetContentLibraryV2List.applyMock();
|
||||
mockLibraryBlockMetadata.applyMock();
|
||||
|
||||
let axiosMock: MockAdapter;
|
||||
let mockShowToast: (message: string) => void;
|
||||
let postMessageSpy: jest.SpyInstance;
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
@@ -44,10 +39,8 @@ jest.mock('react-router-dom', () => ({
|
||||
|
||||
describe('<ComponentPicker />', () => {
|
||||
beforeEach(() => {
|
||||
const mocks = initializeMocks();
|
||||
axiosMock = mocks.axiosMock;
|
||||
mockShowToast = mocks.mockShowToast;
|
||||
axiosMock.onPost(getXBlockBaseApiUrl()).reply(200, {});
|
||||
initializeMocks();
|
||||
postMessageSpy = jest.spyOn(window.parent, 'postMessage');
|
||||
|
||||
mockSearchResult(mockResult);
|
||||
});
|
||||
@@ -67,41 +60,12 @@ describe('<ComponentPicker />', () => {
|
||||
// Click the add component from the component card
|
||||
fireEvent.click(screen.queryAllByRole('button', { name: 'Add' })[0]);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(axiosMock.history.post.length).toBe(1);
|
||||
expect(axiosMock.history.post[0].url).toBe(getXBlockBaseApiUrl());
|
||||
expect(axiosMock.history.post[0].data).toBe(JSON.stringify({
|
||||
parent_locator: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical1',
|
||||
library_content_key: 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd',
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('should show toast if error on api call from the component card button', async () => {
|
||||
axiosMock.onPost(getXBlockBaseApiUrl()).reply(500, {});
|
||||
render(<ComponentPicker />);
|
||||
|
||||
expect(await screen.findByText('Test Library 1')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByDisplayValue(/lib:sampletaxonomyorg1:tl1/i));
|
||||
|
||||
fireEvent.click(screen.getByText('Next'));
|
||||
|
||||
// Wait for the content library to load
|
||||
await screen.findByText(/Change Library/i);
|
||||
expect(await screen.findByText('Test Library 1')).toBeInTheDocument();
|
||||
|
||||
// Click the add component from the component card
|
||||
fireEvent.click(screen.queryAllByRole('button', { name: 'Add' })[0]);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(axiosMock.history.post.length).toBe(1);
|
||||
expect(axiosMock.history.post[0].url).toBe(getXBlockBaseApiUrl());
|
||||
expect(axiosMock.history.post[0].data).toBe(JSON.stringify({
|
||||
parent_locator: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical1',
|
||||
library_content_key: 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd',
|
||||
}));
|
||||
expect(mockShowToast).toHaveBeenCalledWith('Failed to add component to course');
|
||||
});
|
||||
expect(postMessageSpy).toHaveBeenCalledWith({
|
||||
parentLocator: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical1',
|
||||
usageKey: 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd',
|
||||
type: 'pickerComponentSelected',
|
||||
category: 'html',
|
||||
}, '*');
|
||||
});
|
||||
|
||||
it('should pick component using the component sidebar', async () => {
|
||||
@@ -124,46 +88,12 @@ describe('<ComponentPicker />', () => {
|
||||
// Click the add component from the component sidebar
|
||||
fireEvent.click(within(sidebar).getByRole('button', { name: 'Add to Course' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(axiosMock.history.post.length).toBe(1);
|
||||
expect(axiosMock.history.post[0].url).toBe(getXBlockBaseApiUrl());
|
||||
expect(axiosMock.history.post[0].data).toBe(JSON.stringify({
|
||||
parent_locator: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical1',
|
||||
library_content_key: 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd',
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it('should show toast if error on api call from the component sidebar button', async () => {
|
||||
axiosMock.onPost(getXBlockBaseApiUrl()).reply(500, {});
|
||||
render(<ComponentPicker />);
|
||||
|
||||
expect(await screen.findByText('Test Library 1')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByDisplayValue(/lib:sampletaxonomyorg1:tl1/i));
|
||||
|
||||
fireEvent.click(screen.getByText('Next'));
|
||||
|
||||
// Wait for the content library to load
|
||||
await screen.findByText(/Change Library/i);
|
||||
expect(await screen.findByText('Test Library 1')).toBeInTheDocument();
|
||||
|
||||
// Click on the component card to open the sidebar
|
||||
fireEvent.click(screen.queryAllByText('Introduction to Testing')[0]);
|
||||
|
||||
const sidebar = await screen.findByTestId('library-sidebar');
|
||||
|
||||
// Click the add component from the component sidebar
|
||||
fireEvent.click(within(sidebar).getByRole('button', { name: 'Add to Course' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(axiosMock.history.post.length).toBe(1);
|
||||
expect(axiosMock.history.post[0].url).toBe(getXBlockBaseApiUrl());
|
||||
expect(axiosMock.history.post[0].data).toBe(JSON.stringify({
|
||||
parent_locator: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical1',
|
||||
library_content_key: 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd',
|
||||
}));
|
||||
expect(mockShowToast).toHaveBeenCalledWith('Failed to add component to course');
|
||||
});
|
||||
expect(postMessageSpy).toHaveBeenCalledWith({
|
||||
parentLocator: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical1',
|
||||
usageKey: 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd',
|
||||
type: 'pickerComponentSelected',
|
||||
category: 'html',
|
||||
}, '*');
|
||||
});
|
||||
|
||||
it('should pick component inside a collection using the card', async () => {
|
||||
@@ -196,14 +126,12 @@ describe('<ComponentPicker />', () => {
|
||||
// Click the add component from the component card
|
||||
fireEvent.click(screen.queryAllByRole('button', { name: 'Add' })[0]);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(axiosMock.history.post.length).toBe(1);
|
||||
expect(axiosMock.history.post[0].url).toBe(getXBlockBaseApiUrl());
|
||||
expect(axiosMock.history.post[0].data).toBe(JSON.stringify({
|
||||
parent_locator: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical1',
|
||||
library_content_key: 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd',
|
||||
}));
|
||||
});
|
||||
expect(postMessageSpy).toHaveBeenCalledWith({
|
||||
parentLocator: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical1',
|
||||
usageKey: 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd',
|
||||
type: 'pickerComponentSelected',
|
||||
category: 'html',
|
||||
}, '*');
|
||||
});
|
||||
|
||||
it('should pick component inside a collection using the sidebar', async () => {
|
||||
@@ -241,14 +169,12 @@ describe('<ComponentPicker />', () => {
|
||||
// Click the add component from the collection sidebar
|
||||
fireEvent.click(within(collectionSidebar).getByRole('button', { name: 'Add to Course' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(axiosMock.history.post.length).toBe(1);
|
||||
expect(axiosMock.history.post[0].url).toBe(getXBlockBaseApiUrl());
|
||||
expect(axiosMock.history.post[0].data).toBe(JSON.stringify({
|
||||
parent_locator: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical1',
|
||||
library_content_key: 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd',
|
||||
}));
|
||||
});
|
||||
expect(postMessageSpy).toHaveBeenCalledWith({
|
||||
parentLocator: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical1',
|
||||
usageKey: 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd',
|
||||
type: 'pickerComponentSelected',
|
||||
category: 'html',
|
||||
}, '*');
|
||||
});
|
||||
|
||||
it('should return to library selection', async () => {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { updateClipboard } from '../../generic/data/api';
|
||||
import { ToastContext } from '../../generic/toast-context';
|
||||
import { type ContentHit } from '../../search-manager';
|
||||
import { useLibraryContext } from '../common/context';
|
||||
import { useAddComponentToCourse, useRemoveComponentsFromCollection } from '../data/apiHooks';
|
||||
import { useRemoveComponentsFromCollection } from '../data/apiHooks';
|
||||
import BaseComponentCard from './BaseComponentCard';
|
||||
import { canEditComponent } from './ComponentEditorModal';
|
||||
import messages from './messages';
|
||||
@@ -90,14 +90,11 @@ export const ComponentMenu = ({ usageKey }: { usageKey: string }) => {
|
||||
};
|
||||
|
||||
const ComponentCard = ({ contentHit }: ComponentCardProps) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const {
|
||||
openComponentInfoSidebar,
|
||||
componentPickerMode,
|
||||
parentLocator,
|
||||
} = useLibraryContext();
|
||||
const { showToast } = useContext(ToastContext);
|
||||
|
||||
const {
|
||||
blockType,
|
||||
@@ -112,20 +109,13 @@ const ComponentCard = ({ contentHit }: ComponentCardProps) => {
|
||||
) ?? '';/* eslint-enable */
|
||||
const displayName = formatted?.displayName ?? '';
|
||||
|
||||
const {
|
||||
mutateAsync: addComponentToCourse,
|
||||
reset,
|
||||
} = useAddComponentToCourse(parentLocator, contentHit.usageKey);
|
||||
|
||||
const handleAddComponentToCourse = () => {
|
||||
addComponentToCourse()
|
||||
.then(() => {
|
||||
window.parent.postMessage('closeComponentPicker', '*');
|
||||
})
|
||||
.catch(() => {
|
||||
showToast(intl.formatMessage(messages.addComponentToCourseError));
|
||||
reset();
|
||||
});
|
||||
window.parent.postMessage({
|
||||
parentLocator,
|
||||
usageKey,
|
||||
type: 'pickerComponentSelected',
|
||||
category: blockType,
|
||||
}, '*');
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -483,15 +483,3 @@ export async function updateComponentCollections(usageKey: string, collectionKey
|
||||
collection_keys: collectionKeys,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a component to a course.
|
||||
*/
|
||||
// istanbul ignore next
|
||||
export async function addComponentToCourse(parentLocator: string, componentUsageKey: string) {
|
||||
const client = getAuthenticatedHttpClient();
|
||||
await client.post(getXBlockBaseApiUrl(), {
|
||||
parent_locator: parentLocator,
|
||||
library_content_key: componentUsageKey,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ import {
|
||||
getXBlockAssets,
|
||||
updateComponentCollections,
|
||||
removeComponentsFromCollection,
|
||||
addComponentToCourse,
|
||||
} from './api';
|
||||
|
||||
export const libraryQueryPredicate = (query: Query, libraryId: string): boolean => {
|
||||
@@ -474,18 +473,3 @@ export const useUpdateComponentCollections = (libraryId: string, usageKey: strin
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this mutation to add a component to a course
|
||||
*/
|
||||
export const useAddComponentToCourse = (parentLocator: string | undefined, componentUsageKey: string) => (
|
||||
useMutation({
|
||||
mutationFn: () => {
|
||||
// istanbul ignore if: this should never happen
|
||||
if (!parentLocator) {
|
||||
throw new Error('parentLocator is required');
|
||||
}
|
||||
return addComponentToCourse(parentLocator, componentUsageKey);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user