fix: add component to collection on paste [FC-0062] (#1450)
Link component to collection if pasted in a collection page. Fixes: https://github.com/openedx/frontend-app-authoring/issues/1435
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
initializeMocks,
|
||||
} from '../../testUtils';
|
||||
import { mockContentLibrary } from '../data/api.mocks';
|
||||
import { getCreateLibraryBlockUrl, getLibraryPasteClipboardUrl } from '../data/api';
|
||||
import { getCreateLibraryBlockUrl, getLibraryCollectionComponentApiUrl, getLibraryPasteClipboardUrl } from '../data/api';
|
||||
import { mockBroadcastChannel, mockClipboardEmpty, mockClipboardHtml } from '../../generic/data/api.mock';
|
||||
import { LibraryProvider } from '../common/context';
|
||||
import AddContentContainer from './AddContentContainer';
|
||||
@@ -14,11 +14,23 @@ import AddContentContainer from './AddContentContainer';
|
||||
mockBroadcastChannel();
|
||||
|
||||
const { libraryId } = mockContentLibrary;
|
||||
const render = () => baseRender(<AddContentContainer />, {
|
||||
path: '/library/:libraryId/*',
|
||||
params: { libraryId },
|
||||
extraWrapper: ({ children }) => <LibraryProvider libraryId={libraryId}>{ children }</LibraryProvider>,
|
||||
});
|
||||
const render = (collectionId?: string) => {
|
||||
const params: { libraryId: string, collectionId?: string } = { libraryId };
|
||||
if (collectionId) {
|
||||
params.collectionId = collectionId;
|
||||
}
|
||||
return baseRender(<AddContentContainer />, {
|
||||
path: '/library/:libraryId/*',
|
||||
params,
|
||||
extraWrapper: ({ children }) => (
|
||||
<LibraryProvider
|
||||
libraryId={libraryId}
|
||||
collectionId={collectionId}
|
||||
>{ children }
|
||||
</LibraryProvider>
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
describe('<AddContentContainer />', () => {
|
||||
it('should render content buttons', () => {
|
||||
@@ -47,6 +59,29 @@ describe('<AddContentContainer />', () => {
|
||||
fireEvent.click(textButton);
|
||||
|
||||
await waitFor(() => expect(axiosMock.history.post[0].url).toEqual(url));
|
||||
await waitFor(() => expect(axiosMock.history.patch.length).toEqual(0));
|
||||
});
|
||||
|
||||
it('should create a content in a collection', async () => {
|
||||
const { axiosMock } = initializeMocks();
|
||||
mockClipboardEmpty.applyMock();
|
||||
const collectionId = 'some-collection-id';
|
||||
const url = getCreateLibraryBlockUrl(libraryId);
|
||||
const collectionComponentUrl = getLibraryCollectionComponentApiUrl(
|
||||
libraryId,
|
||||
collectionId,
|
||||
);
|
||||
axiosMock.onPost(url).reply(200, { id: 'some-component-id' });
|
||||
axiosMock.onPatch(collectionComponentUrl).reply(200);
|
||||
|
||||
render(collectionId);
|
||||
|
||||
const textButton = screen.getByRole('button', { name: /text/i });
|
||||
fireEvent.click(textButton);
|
||||
|
||||
await waitFor(() => expect(axiosMock.history.post[0].url).toEqual(url));
|
||||
await waitFor(() => expect(axiosMock.history.patch.length).toEqual(1));
|
||||
await waitFor(() => expect(axiosMock.history.patch[0].url).toEqual(collectionComponentUrl));
|
||||
});
|
||||
|
||||
it('should render paste button if clipboard contains pastable xblock', async () => {
|
||||
@@ -76,6 +111,59 @@ describe('<AddContentContainer />', () => {
|
||||
await waitFor(() => expect(axiosMock.history.post[0].url).toEqual(pasteUrl));
|
||||
});
|
||||
|
||||
it('should paste content inside a collection', async () => {
|
||||
const { axiosMock } = initializeMocks();
|
||||
// Simulate having an HTML block in the clipboard:
|
||||
const getClipboardSpy = mockClipboardHtml.applyMock();
|
||||
|
||||
const pasteUrl = getLibraryPasteClipboardUrl(libraryId);
|
||||
const collectionId = 'some-collection-id';
|
||||
const collectionComponentUrl = getLibraryCollectionComponentApiUrl(
|
||||
libraryId,
|
||||
collectionId,
|
||||
);
|
||||
axiosMock.onPatch(collectionComponentUrl).reply(200);
|
||||
axiosMock.onPost(pasteUrl).reply(200, { id: 'some-component-id' });
|
||||
|
||||
render(collectionId);
|
||||
|
||||
expect(getClipboardSpy).toHaveBeenCalled(); // Hmm, this is getting called four times! Refactor to use react-query.
|
||||
|
||||
const pasteButton = await screen.findByRole('button', { name: /paste from clipboard/i });
|
||||
fireEvent.click(pasteButton);
|
||||
|
||||
await waitFor(() => expect(axiosMock.history.post[0].url).toEqual(pasteUrl));
|
||||
await waitFor(() => expect(axiosMock.history.patch.length).toEqual(1));
|
||||
await waitFor(() => expect(axiosMock.history.patch[0].url).toEqual(collectionComponentUrl));
|
||||
});
|
||||
|
||||
it('should show error toast on linking failure', async () => {
|
||||
const { axiosMock, mockShowToast } = initializeMocks();
|
||||
// Simulate having an HTML block in the clipboard:
|
||||
const getClipboardSpy = mockClipboardHtml.applyMock();
|
||||
|
||||
const pasteUrl = getLibraryPasteClipboardUrl(libraryId);
|
||||
const collectionId = 'some-collection-id';
|
||||
const collectionComponentUrl = getLibraryCollectionComponentApiUrl(
|
||||
libraryId,
|
||||
collectionId,
|
||||
);
|
||||
axiosMock.onPatch(collectionComponentUrl).reply(500);
|
||||
axiosMock.onPost(pasteUrl).reply(200, { id: 'some-component-id' });
|
||||
|
||||
render(collectionId);
|
||||
|
||||
expect(getClipboardSpy).toHaveBeenCalled(); // Hmm, this is getting called four times! Refactor to use react-query.
|
||||
|
||||
const pasteButton = await screen.findByRole('button', { name: /paste from clipboard/i });
|
||||
fireEvent.click(pasteButton);
|
||||
|
||||
await waitFor(() => expect(axiosMock.history.post[0].url).toEqual(pasteUrl));
|
||||
await waitFor(() => expect(axiosMock.history.patch.length).toEqual(1));
|
||||
await waitFor(() => expect(axiosMock.history.patch[0].url).toEqual(collectionComponentUrl));
|
||||
expect(mockShowToast).toHaveBeenCalledWith('There was an error linking the content to this collection.');
|
||||
});
|
||||
|
||||
it('should handle failure to paste content', async () => {
|
||||
const { axiosMock, mockShowToast } = initializeMocks();
|
||||
// Simulate having an HTML block in the clipboard:
|
||||
|
||||
@@ -158,6 +158,14 @@ const AddContentContainer = () => {
|
||||
contentTypes.push(pasteButton);
|
||||
}
|
||||
|
||||
const linkComponent = (usageKey: string) => {
|
||||
updateComponentsMutation.mutateAsync([usageKey]).then(() => {
|
||||
showToast(intl.formatMessage(messages.successAssociateComponentMessage));
|
||||
}).catch(() => {
|
||||
showToast(intl.formatMessage(messages.errorAssociateComponentMessage));
|
||||
});
|
||||
};
|
||||
|
||||
const onPaste = () => {
|
||||
if (!isBlockTypeEnabled(sharedClipboardData.content?.blockType)) {
|
||||
showToast(intl.formatMessage(messages.unsupportedBlockPasteClipboardMessage));
|
||||
@@ -166,7 +174,8 @@ const AddContentContainer = () => {
|
||||
pasteClipboardMutation.mutateAsync({
|
||||
libraryId,
|
||||
blockId: `${uuid4()}`,
|
||||
}).then(() => {
|
||||
}).then((data) => {
|
||||
linkComponent(data.id);
|
||||
showToast(intl.formatMessage(messages.successPasteClipboardMessage));
|
||||
}).catch((error) => {
|
||||
showToast(parsePasteErrorMsg(error));
|
||||
@@ -179,10 +188,8 @@ const AddContentContainer = () => {
|
||||
blockType,
|
||||
definitionId: `${uuid4()}`,
|
||||
}).then((data) => {
|
||||
linkComponent(data.id);
|
||||
const hasEditor = canEditComponent(data.id);
|
||||
updateComponentsMutation.mutateAsync([data.id]).catch(() => {
|
||||
showToast(intl.formatMessage(messages.errorAssociateComponentMessage));
|
||||
});
|
||||
if (hasEditor) {
|
||||
openComponentEditor(data.id);
|
||||
} else {
|
||||
@@ -210,6 +217,10 @@ const AddContentContainer = () => {
|
||||
showToast(intl.formatMessage(messages.pastingClipboardMessage));
|
||||
}
|
||||
|
||||
if (updateComponentsMutation.isLoading) {
|
||||
showToast(intl.formatMessage(messages.linkingComponentMessage));
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack direction="vertical">
|
||||
{collectionId ? (
|
||||
|
||||
@@ -66,6 +66,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'There was an error creating the content.',
|
||||
description: 'Message when creation of content in library is on error',
|
||||
},
|
||||
linkingComponentMessage: {
|
||||
id: 'course-authoring.library-authoring.linking-collection-content.progress.text',
|
||||
defaultMessage: 'Adding component to collection...',
|
||||
description: 'Message when component is being linked to collection in library',
|
||||
},
|
||||
successAssociateComponentMessage: {
|
||||
id: 'course-authoring.library-authoring.associate-collection-content.success.text',
|
||||
defaultMessage: 'Content linked successfully.',
|
||||
|
||||
Reference in New Issue
Block a user