Skip to content

Commit

Permalink
feat: improve collection sidebar (#1320)
Browse files Browse the repository at this point in the history
* feat: improve collection sidebar

* feat: add comments to splice blockTypesArray code

Co-authored-by: Jillian <[email protected]>
---------

Co-authored-by: Jillian <[email protected]>
Co-authored-by: Chris Chávez <[email protected]>
  • Loading branch information
3 people authored Sep 28, 2024
1 parent c80483c commit 4d67e8b
Show file tree
Hide file tree
Showing 29 changed files with 1,154 additions and 138 deletions.
27 changes: 26 additions & 1 deletion src/library-authoring/LibraryAuthoringPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@ import {
} from '../testUtils';
import mockResult from './__mocks__/library-search.json';
import mockEmptyResult from '../search-modal/__mocks__/empty-search-result.json';
import { mockContentLibrary, mockLibraryBlockTypes, mockXBlockFields } from './data/api.mocks';
import {
mockContentLibrary,
mockGetCollectionMetadata,
mockLibraryBlockTypes,
mockXBlockFields,
} from './data/api.mocks';
import { mockContentSearchConfig } from '../search-manager/data/api.mock';
import { mockBroadcastChannel } from '../generic/data/api.mock';
import { LibraryLayout } from '.';
import { getLibraryCollectionsApiUrl } from './data/api';

mockGetCollectionMetadata.applyMock();
mockContentSearchConfig.applyMock();
mockContentLibrary.applyMock();
mockLibraryBlockTypes.applyMock();
Expand Down Expand Up @@ -458,6 +464,25 @@ describe('<LibraryAuthoringPage />', () => {
await waitFor(() => expect(screen.queryByTestId('library-sidebar')).not.toBeInTheDocument());
});

it('should open and close the collection sidebar', async () => {
await renderLibraryPage();

// Click on the first component. It could appear twice, in both "Recently Modified" and "Collections"
fireEvent.click((await screen.findAllByText('Collection 1'))[0]);

const sidebar = screen.getByTestId('library-sidebar');

const { getByRole, getByText } = within(sidebar);

// The mock data for the sidebar has a title of "Test Collection"
await waitFor(() => expect(getByText('Test Collection')).toBeInTheDocument());

const closeButton = getByRole('button', { name: /close/i });
fireEvent.click(closeButton);

await waitFor(() => expect(screen.queryByTestId('library-sidebar')).not.toBeInTheDocument());
});

it('can filter by capa problem type', async () => {
const problemTypes = {
'Multiple Choice': 'choiceresponse',
Expand Down
2 changes: 1 addition & 1 deletion src/library-authoring/__mocks__/collection-search.json
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@
}
],
"created": 1726740779.564664,
"modified": 1726740811.684142,
"modified": 1726840811.684142,
"usage_key": "lib-collection:OpenedX:CSPROB2:collection-from-meilisearch",
"context_key": "lib:OpenedX:CSPROB2",
"org": "OpenedX",
Expand Down
1 change: 1 addition & 0 deletions src/library-authoring/__mocks__/library-search.json
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@
"hits": [
{
"display_name": "Collection 1",
"block_id": "col1",
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque et mi ac nisi accumsan imperdiet vitae at odio. Vivamus tempor nec lorem eget lacinia. Vivamus efficitur lacus non dapibus porta. Nulla venenatis luctus nisi id posuere. Sed sollicitudin magna a sem ultrices accumsan. Praesent volutpat tortor vitae luctus rutrum. Integer.",
"id": 1,
"type": "collection",
Expand Down
161 changes: 161 additions & 0 deletions src/library-authoring/collections/CollectionDetails.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import type MockAdapter from 'axios-mock-adapter';
import fetchMock from 'fetch-mock-jest';

import { mockContentSearchConfig, mockGetBlockTypes } from '../../search-manager/data/api.mock';
import {
initializeMocks,
fireEvent,
render,
screen,
waitFor,
within,
} from '../../testUtils';
import * as api from '../data/api';
import { mockContentLibrary, mockGetCollectionMetadata } from '../data/api.mocks';
import CollectionDetails from './CollectionDetails';

let axiosMock: MockAdapter;
let mockShowToast: (message: string) => void;

mockGetCollectionMetadata.applyMock();
mockContentSearchConfig.applyMock();
mockGetBlockTypes.applyMock();

const { collectionId } = mockGetCollectionMetadata;
const { description: originalDescription } = mockGetCollectionMetadata.collectionData;

const library = mockContentLibrary.libraryData;

describe('<CollectionDetails />', () => {
beforeEach(() => {
const mocks = initializeMocks();
axiosMock = mocks.axiosMock;
mockShowToast = mocks.mockShowToast;
});

afterEach(() => {
jest.clearAllMocks();
axiosMock.restore();
fetchMock.mockReset();
});

it('should render Collection Details', async () => {
render(<CollectionDetails library={library} collectionId={collectionId} />);

// Collection Description
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();
expect(screen.getByText(originalDescription)).toBeInTheDocument();

// Collection History
expect(screen.getByText('Collection History')).toBeInTheDocument();
// Modified date
expect(screen.getByText('September 20, 2024')).toBeInTheDocument();
// Created date
expect(screen.getByText('September 19, 2024')).toBeInTheDocument();
});

it('should allow modifying the description', async () => {
render(<CollectionDetails library={library} collectionId={collectionId} />);
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();

expect(screen.getByText(originalDescription)).toBeInTheDocument();

const url = api.getLibraryCollectionApiUrl(library.id, collectionId);
axiosMock.onPatch(url).reply(200);

const textArea = screen.getByRole('textbox');

// Change the description to the same value
fireEvent.focus(textArea);
fireEvent.change(textArea, { target: { value: originalDescription } });
fireEvent.blur(textArea);

await waitFor(() => {
expect(axiosMock.history.patch).toHaveLength(0);
expect(mockShowToast).not.toHaveBeenCalled();
});

// Change the description to a new value
fireEvent.focus(textArea);
fireEvent.change(textArea, { target: { value: 'New description' } });
fireEvent.blur(textArea);

await waitFor(() => {
expect(axiosMock.history.patch).toHaveLength(1);
expect(axiosMock.history.patch[0].url).toEqual(url);
expect(axiosMock.history.patch[0].data).toEqual(JSON.stringify({ description: 'New description' }));
expect(mockShowToast).toHaveBeenCalledWith('Collection updated successfully.');
});
});

it('should show error while modifing the description', async () => {
render(<CollectionDetails library={library} collectionId={collectionId} />);
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();

expect(screen.getByText(originalDescription)).toBeInTheDocument();

const url = api.getLibraryCollectionApiUrl(library.id, collectionId);
axiosMock.onPatch(url).reply(500);

const textArea = screen.getByRole('textbox');

// Change the description to a new value
fireEvent.focus(textArea);
fireEvent.change(textArea, { target: { value: 'New description' } });
fireEvent.blur(textArea);

await waitFor(() => {
expect(axiosMock.history.patch).toHaveLength(1);
expect(axiosMock.history.patch[0].url).toEqual(url);
expect(axiosMock.history.patch[0].data).toEqual(JSON.stringify({ description: 'New description' }));
expect(mockShowToast).toHaveBeenCalledWith('Failed to update collection.');
});
});

it('should render Collection stats', async () => {
mockGetBlockTypes('someBlocks');
render(<CollectionDetails library={library} collectionId={collectionId} />);
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();

expect(screen.getByText('Collection Stats')).toBeInTheDocument();
expect(await screen.findByText('Total')).toBeInTheDocument();

[
{ blockType: 'Total', count: 3 },
{ blockType: 'Text', count: 2 },
{ blockType: 'Problem', count: 1 },
].forEach(({ blockType, count }) => {
const blockCount = screen.getByText(blockType).closest('div') as HTMLDivElement;
expect(within(blockCount).getByText(count.toString())).toBeInTheDocument();
});
});

it('should render Collection stats for empty collection', async () => {
mockGetBlockTypes('noBlocks');
render(<CollectionDetails library={library} collectionId={collectionId} />);
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();

expect(screen.getByText('Collection Stats')).toBeInTheDocument();
expect(await screen.findByText('This collection is currently empty.')).toBeInTheDocument();
});

it('should render Collection stats for big collection', async () => {
mockGetBlockTypes('moreBlocks');
render(<CollectionDetails library={library} collectionId={collectionId} />);
expect(await screen.findByText('Description / Card Preview Text')).toBeInTheDocument();

expect(screen.getByText('Collection Stats')).toBeInTheDocument();
expect(await screen.findByText('36')).toBeInTheDocument();

[
{ blockType: 'Total', count: 36 },
{ blockType: 'Video', count: 8 },
{ blockType: 'Problem', count: 7 },
{ blockType: 'Text', count: 6 },
{ blockType: 'Other', count: 15 },
].forEach(({ blockType, count }) => {
const blockCount = screen.getByText(blockType).closest('div') as HTMLDivElement;
expect(within(blockCount).getByText(count.toString())).toBeInTheDocument();
});
});
});
Loading

0 comments on commit 4d67e8b

Please sign in to comment.