diff --git a/src/library-authoring/add-content/AddContentContainer.test.tsx b/src/library-authoring/add-content/AddContentContainer.test.tsx
index e8f53c3fd..95e9e4ccb 100644
--- a/src/library-authoring/add-content/AddContentContainer.test.tsx
+++ b/src/library-authoring/add-content/AddContentContainer.test.tsx
@@ -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(, {
- path: '/library/:libraryId/*',
- params: { libraryId },
- extraWrapper: ({ children }) => { children },
-});
+const render = (collectionId?: string) => {
+ const params: { libraryId: string, collectionId?: string } = { libraryId };
+ if (collectionId) {
+ params.collectionId = collectionId;
+ }
+ return baseRender(, {
+ path: '/library/:libraryId/*',
+ params,
+ extraWrapper: ({ children }) => (
+ { children }
+
+ ),
+ });
+};
describe('', () => {
it('should render content buttons', () => {
@@ -47,6 +59,29 @@ describe('', () => {
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('', () => {
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:
diff --git a/src/library-authoring/add-content/AddContentContainer.tsx b/src/library-authoring/add-content/AddContentContainer.tsx
index 27a5bed84..3ae948b8a 100644
--- a/src/library-authoring/add-content/AddContentContainer.tsx
+++ b/src/library-authoring/add-content/AddContentContainer.tsx
@@ -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 (
{collectionId ? (
diff --git a/src/library-authoring/add-content/messages.ts b/src/library-authoring/add-content/messages.ts
index 6971849d7..b1be04e89 100644
--- a/src/library-authoring/add-content/messages.ts
+++ b/src/library-authoring/add-content/messages.ts
@@ -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.',