Skip to content

Commit

Permalink
refactor: lib component picker modal to only post message with block …
Browse files Browse the repository at this point in the history
…info (#1401)
  • Loading branch information
navinkarkera authored Oct 19, 2024
1 parent a94df2f commit 56e025a
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 163 deletions.
24 changes: 7 additions & 17 deletions src/library-authoring/component-info/ComponentInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 (
Expand Down
128 changes: 27 additions & 101 deletions src/library-authoring/component-picker/ComponentPicker.test.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -17,7 +14,6 @@ import {
mockGetContentLibraryV2List,
mockLibraryBlockMetadata,
} from '../data/api.mocks';
import { getXBlockBaseApiUrl } from '../data/api';

import { ComponentPicker } from './ComponentPicker';

Expand All @@ -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'),
Expand All @@ -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);
});
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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 () => {
Expand Down
24 changes: 7 additions & 17 deletions src/library-authoring/components/ComponentCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand All @@ -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 (
Expand Down
12 changes: 0 additions & 12 deletions src/library-authoring/data/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
}
16 changes: 0 additions & 16 deletions src/library-authoring/data/apiHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import {
getXBlockAssets,
updateComponentCollections,
removeComponentsFromCollection,
addComponentToCourse,
} from './api';

export const libraryQueryPredicate = (query: Query, libraryId: string): boolean => {
Expand Down Expand Up @@ -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);
},
})
);

0 comments on commit 56e025a

Please sign in to comment.