refactor: lib component picker modal to only post message with block info (#1401)

This commit is contained in:
Navin Karkera
2024-10-20 00:25:25 +05:30
committed by GitHub
parent a94df2fdf0
commit 56e025a4f0
5 changed files with 41 additions and 163 deletions

View File

@@ -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 (

View File

@@ -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 () => {

View File

@@ -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 (

View File

@@ -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,
});
}

View File

@@ -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);
},
})
);