Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
16 changes: 16 additions & 0 deletions src/__mocks__/clipboardSubsection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default {
content: {
id: 67,
userId: 3,
created: '2024-01-16T13:09:11.540615Z',
purpose: 'clipboard',
status: 'ready',
blockType: 'sequential',
blockTypeDisplay: 'Subsection',
olxUrl: 'http://localhost:18010/api/content-staging/v1/staged-content/67/olx',
displayName: 'Sequences',
},
sourceUsageKey: 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@sequential_0270f6de40fc',
sourceContextTitle: 'Demonstration Course',
sourceEditUrl: 'http://localhost:18010/container/block-v1:edX+DemoX+Demo_Course+type@sequential+block@sequential_0270f6de40fc',
};
1 change: 1 addition & 0 deletions src/__mocks__/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as clipboardUnit } from './clipboardUnit';
export { default as clipboardSubsection } from './clipboardSubsection';
export { default as clipboardXBlock } from './clipboardXBlock';
4 changes: 3 additions & 1 deletion src/generic/clipboard/hooks/useClipboard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { renderHook } from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';

import {
clipboardSubsection,
clipboardUnit,
clipboardXBlock,
} from '../../../__mocks__';
Expand Down Expand Up @@ -42,7 +43,7 @@ describe('useClipboard', () => {

axiosMock
.onPost(getClipboardUrl())
.reply(200, clipboardUnit);
.reply(200, clipboardSubsection);

await result.current.copyToClipboard(unitId);

Expand Down Expand Up @@ -89,6 +90,7 @@ describe('useClipboard', () => {
describe('broadcast channel message handling', () => {
it('updates states correctly on receiving a broadcast message', async () => {
const { result, rerender } = renderHook(() => useClipboard(true), { wrapper: makeWrapper() });
// Subsections cannot be pasted:
clipboardBroadcastChannelMock.postMessage({ data: clipboardUnit });

rerender();
Expand Down
17 changes: 17 additions & 0 deletions src/library-authoring/add-content/AddContent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,23 @@ describe('<AddContent />', () => {
await waitFor(() => expect(axiosMock.history.post[0].url).toEqual(pasteUrl));
});

it('should show error toast on paste failure', async () => {
// Simulate having an HTML block in the clipboard:
mockClipboardHtml.applyMock();

const pasteUrl = getLibraryPasteClipboardUrl(libraryId);
axiosMock.onPost(pasteUrl).reply(500, { block_type: 'Unsupported block type.' });

render();
const pasteButton = await screen.findByRole('button', { name: /paste from clipboard/i });
fireEvent.click(pasteButton);

await waitFor(() => expect(axiosMock.history.post[0].url).toEqual(pasteUrl));
expect(mockShowToast).toHaveBeenCalledWith(
'There was an error pasting the content: {"block_type":"Unsupported block type."}',
);
});

it('should paste content inside a collection', async () => {
// Simulate having an HTML block in the clipboard:
const getClipboardSpy = mockClipboardHtml.applyMock();
Expand Down
15 changes: 11 additions & 4 deletions src/library-authoring/add-content/AddContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,15 @@ export const parseErrorMsg = (
) => {
try {
const { response: { data } } = error;
const detail = data && (Array.isArray(data) ? data.join() : String(data));
let detail = '';
if (Array.isArray(data)) {
detail = data.join(', ');
} else if (typeof data === 'string') {
/* istanbul ignore next */
detail = data.substring(0, 400); // In case this is a giant HTML response, only show the first little bit.
} else if (data) {
detail = JSON.stringify(data);
}
if (detail) {
return intl.formatMessage(detailedMessage, { detail });
}
Expand All @@ -213,7 +221,7 @@ const AddContent = () => {
const pasteClipboardMutation = useLibraryPasteClipboard();
const { showToast } = useContext(ToastContext);
const canEdit = useSelector(getCanEdit);
const { showPasteXBlock, sharedClipboardData } = useClipboard(canEdit);
const { showPasteUnit, showPasteXBlock, sharedClipboardData } = useClipboard(canEdit);

const [isAddLibraryContentModalOpen, showAddLibraryContentModal, closeAddLibraryContentModal] = useToggle();
const [isAdvancedListOpen, showAdvancedList, closeAdvancedList] = useToggle();
Expand Down Expand Up @@ -277,7 +285,7 @@ const AddContent = () => {

// Include the 'Paste from Clipboard' button if there is an Xblock in the clipboard
// that can be pasted
if (showPasteXBlock) {
if (showPasteXBlock || showPasteUnit) {
const pasteButton = {
name: intl.formatMessage(messages.pasteButton),
disabled: false,
Expand Down Expand Up @@ -313,7 +321,6 @@ const AddContent = () => {
}
pasteClipboardMutation.mutateAsync({
libraryId,
blockId: `${uuid4()}`,
}).then((data) => {
linkComponent(data.id);
showToast(intl.formatMessage(messages.successPasteClipboardMessage));
Expand Down
10 changes: 1 addition & 9 deletions src/library-authoring/data/api.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ export async function mockCreateLibraryBlock(
}
mockCreateLibraryBlock.newHtmlData = {
id: 'lb:Axim:TEST:html:123',
defKey: '123',
blockType: 'html',
displayName: 'New Text Component',
hasUnpublishedChanges: true,
Expand All @@ -201,7 +200,6 @@ mockCreateLibraryBlock.newHtmlData = {
} satisfies api.LibraryBlockMetadata;
mockCreateLibraryBlock.newProblemData = {
id: 'lb:Axim:TEST:problem:prob1',
defKey: 'prob1',
blockType: 'problem',
displayName: 'New Problem',
hasUnpublishedChanges: true,
Expand All @@ -216,7 +214,6 @@ mockCreateLibraryBlock.newProblemData = {
} satisfies api.LibraryBlockMetadata;
mockCreateLibraryBlock.newVideoData = {
id: 'lb:Axim:TEST:video:vid1',
defKey: 'vid1',
blockType: 'video',
displayName: 'New Video',
hasUnpublishedChanges: true,
Expand Down Expand Up @@ -349,7 +346,6 @@ mockLibraryBlockMetadata.usageKeyError404 = 'lb:Axim:error404:html:123';
mockLibraryBlockMetadata.usageKeyNeverPublished = 'lb:Axim:TEST1:html:571fe018-f3ce-45c9-8f53-5dafcb422fd1';
mockLibraryBlockMetadata.dataNeverPublished = {
id: 'lb:Axim:TEST1:html:571fe018-f3ce-45c9-8f53-5dafcb422fd1',
defKey: null,
blockType: 'html',
displayName: 'Introduction to Testing 1',
lastPublished: null,
Expand All @@ -365,7 +361,6 @@ mockLibraryBlockMetadata.dataNeverPublished = {
mockLibraryBlockMetadata.usageKeyPublished = 'lb:Axim:TEST2:html:571fe018-f3ce-45c9-8f53-5dafcb422fd2';
mockLibraryBlockMetadata.dataPublished = {
id: 'lb:Axim:TEST2:html:571fe018-f3ce-45c9-8f53-5dafcb422fd2',
defKey: null,
blockType: 'html',
displayName: 'Introduction to Testing 2',
lastPublished: '2024-06-22T00:00:00',
Expand Down Expand Up @@ -394,7 +389,6 @@ mockLibraryBlockMetadata.usageKeyForTags = mockContentTaxonomyTagsData.largeTags
mockLibraryBlockMetadata.usageKeyWithCollections = 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd';
mockLibraryBlockMetadata.dataWithCollections = {
id: 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fdd',
defKey: null,
blockType: 'html',
displayName: 'Introduction to Testing 2',
lastPublished: '2024-06-21T00:00:00',
Expand All @@ -411,7 +405,6 @@ mockLibraryBlockMetadata.usageKeyPublishedWithChanges = 'lb:Axim:TEST:html:571fe
mockLibraryBlockMetadata.usageKeyPublishedWithChangesV2 = 'lb:Axim:TEST:html:571fe018-f3ce-45c9-8f53-5dafcb422fv2';
mockLibraryBlockMetadata.dataPublishedWithChanges = {
id: 'lb:Axim:TEST2:html:571fe018-f3ce-45c9-8f53-5dafcb422fvv',
defKey: null,
blockType: 'html',
displayName: 'Introduction to Testing 2',
lastPublished: '2024-06-22T00:00:00',
Expand Down Expand Up @@ -489,7 +482,7 @@ mockGetContainerMetadata.containerIdError = 'lct:org:lib:unit:container_error';
mockGetContainerMetadata.containerIdLoading = 'lct:org:lib:unit:container_loading';
mockGetContainerMetadata.containerIdForTags = mockContentTaxonomyTagsData.largeTagsId;
mockGetContainerMetadata.containerData = {
containerKey: 'lct:org:lib:unit:test-unit-9a2072',
id: 'lct:org:lib:unit:test-unit-9a2072',
containerType: 'unit',
displayName: 'Test Unit',
created: '2024-09-19T10:00:00Z',
Expand Down Expand Up @@ -544,7 +537,6 @@ mockGetContainerChildren.sixChildren = 'lct:org1:Demo_Course:unit:unit-6';
mockGetContainerChildren.childTemplate = {
id: 'lb:org1:Demo_course:html:text',
blockType: 'html',
defKey: 'def_key',
displayName: 'text block',
lastPublished: null,
publishedBy: null,
Expand Down
14 changes: 3 additions & 11 deletions src/library-authoring/data/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ export interface CollectionMetadata {
export interface LibraryBlockMetadata {
id: string;
blockType: string;
defKey: string | null;
displayName: string;
lastPublished: string | null;
publishedBy: string | null;
Expand All @@ -266,7 +265,6 @@ export interface UpdateLibraryDataRequest {

export interface LibraryPasteClipboardRequest {
libraryId: string;
blockId: string;
}

export interface UpdateXBlockFieldsRequest {
Expand Down Expand Up @@ -422,16 +420,10 @@ export async function getBlockTypes(libraryId: string): Promise<BlockTypeMetadat
*/
export async function libraryPasteClipboard({
libraryId,
blockId,
}: LibraryPasteClipboardRequest): Promise<LibraryBlockMetadata> {
const client = getAuthenticatedHttpClient();
const { data } = await client.post(
getLibraryPasteClipboardUrl(libraryId),
{
block_id: blockId,
},
);
return data;
const { data } = await client.post(getLibraryPasteClipboardUrl(libraryId), {});
return camelCaseObject(data);
}

/**
Expand Down Expand Up @@ -589,7 +581,7 @@ export async function createLibraryContainer(libraryId: string, containerData: C
}

export interface Container {
containerKey: string;
id: string;
containerType: 'unit';
displayName: string;
lastPublished: string | null;
Expand Down
4 changes: 0 additions & 4 deletions src/library-authoring/data/apiHooks.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ describe('library api hooks', () => {
{
id: 'lb:org1:Demo_course:html:text',
block_type: 'html',
def_key: 'def_key',
display_name: 'text block',
last_published: null,
published_by: null,
Expand All @@ -208,7 +207,6 @@ describe('library api hooks', () => {
{
id: 'lb:org1:Demo_course:video:video1',
block_type: 'video',
def_key: 'def_key',
display_name: 'video block',
last_published: null,
published_by: null,
Expand All @@ -229,7 +227,6 @@ describe('library api hooks', () => {
{
id: 'lb:org1:Demo_course:html:text',
blockType: 'html',
defKey: 'def_key',
displayName: 'text block',
lastPublished: null,
publishedBy: null,
Expand All @@ -244,7 +241,6 @@ describe('library api hooks', () => {
{
id: 'lb:org1:Demo_course:video:video1',
blockType: 'video',
defKey: 'def_key',
displayName: 'video block',
lastPublished: null,
publishedBy: null,
Expand Down