Skip to content

Commit

Permalink
feat: Library v2 components tab (openedx#1109)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisChV authored Jul 23, 2024
1 parent 3a14141 commit 77135cd
Show file tree
Hide file tree
Showing 30 changed files with 721 additions and 117 deletions.
2 changes: 1 addition & 1 deletion src/course-unit/add-component/AddComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useIntl } from '@edx/frontend-platform/i18n';
import { useToggle } from '@openedx/paragon';

import { getCourseSectionVertical } from '../data/selectors';
import { COMPONENT_TYPES } from '../constants';
import { COMPONENT_TYPES } from '../../generic/block-type-utils/constants';
import ComponentModalView from './add-component-modals/ComponentModalView';
import AddComponentButton from './add-component-btn';
import messages from './messages';
Expand Down
2 changes: 1 addition & 1 deletion src/course-unit/add-component/AddComponent.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { executeThunk } from '../../utils';
import { fetchCourseSectionVerticalData } from '../data/thunk';
import { getCourseSectionVerticalApiUrl } from '../data/api';
import { courseSectionVerticalMock } from '../__mocks__';
import { COMPONENT_TYPES } from '../constants';
import { COMPONENT_TYPES } from '../../generic/block-type-utils/constants';
import AddComponent from './AddComponent';
import messages from './messages';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import { Icon } from '@openedx/paragon';
import { EditNote as EditNoteIcon } from '@openedx/paragon/icons';

import { COMPONENT_TYPES, COMPONENT_TYPE_ICON_MAP } from '../../constants';
import { COMPONENT_TYPES, COMPONENT_TYPE_ICON_MAP } from '../../../generic/block-type-utils/constants';

const AddComponentIcon = ({ type }) => {
const icon = COMPONENT_TYPE_ICON_MAP[type] || EditNoteIcon;
Expand Down
47 changes: 0 additions & 47 deletions src/course-unit/constants.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,6 @@
import {
BackHand as BackHandIcon,
BookOpen as BookOpenIcon,
Edit as EditIcon,
EditNote as EditNoteIcon,
FormatListBulleted as FormatListBulletedIcon,
HelpOutline as HelpOutlineIcon,
LibraryAdd as LibraryIcon,
Lock as LockIcon,
QuestionAnswerOutline as QuestionAnswerOutlineIcon,
Science as ScienceIcon,
TextFields as TextFieldsIcon,
VideoCamera as VideoCameraIcon,
} from '@openedx/paragon/icons';

import messages from './sidebar/messages';
import addComponentMessages from './add-component/messages';

export const UNIT_ICON_TYPES = ['video', 'other', 'vertical', 'problem', 'lock'];

export const COMPONENT_TYPES = {
advanced: 'advanced',
discussion: 'discussion',
library: 'library',
html: 'html',
openassessment: 'openassessment',
problem: 'problem',
video: 'video',
dragAndDrop: 'drag-and-drop-v2',
};

export const TYPE_ICONS_MAP = {
video: VideoCameraIcon,
other: BookOpenIcon,
vertical: FormatListBulletedIcon,
problem: EditIcon,
lock: LockIcon,
};

export const COMPONENT_TYPE_ICON_MAP = {
[COMPONENT_TYPES.advanced]: ScienceIcon,
[COMPONENT_TYPES.discussion]: QuestionAnswerOutlineIcon,
[COMPONENT_TYPES.library]: LibraryIcon,
[COMPONENT_TYPES.html]: TextFieldsIcon,
[COMPONENT_TYPES.openassessment]: EditNoteIcon,
[COMPONENT_TYPES.problem]: HelpOutlineIcon,
[COMPONENT_TYPES.video]: VideoCameraIcon,
[COMPONENT_TYPES.dragAndDrop]: BackHandIcon,
};

export const getUnitReleaseStatus = (intl) => ({
release: intl.formatMessage(messages.releaseStatusTitle),
released: intl.formatMessage(messages.releasedStatusTitle),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import PropTypes from 'prop-types';
import { Icon } from '@openedx/paragon';
import { BookOpen as BookOpenIcon } from '@openedx/paragon/icons';

import { TYPE_ICONS_MAP, UNIT_ICON_TYPES } from '../../constants';
import { UNIT_TYPE_ICONS_MAP, UNIT_ICON_TYPES } from '../../../generic/block-type-utils/constants';

const UnitIcon = ({ type }) => {
const icon = TYPE_ICONS_MAP[type] || BookOpenIcon;
const icon = UNIT_TYPE_ICONS_MAP[type] || BookOpenIcon;

return <Icon src={icon} screenReaderText={type} />;
};
Expand Down
2 changes: 1 addition & 1 deletion src/course-unit/course-xblock/CourseXBlock.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import SortableItem from '../../generic/drag-helper/SortableItem';
import { scrollToElement } from '../../course-outline/utils';
import { COURSE_BLOCK_NAMES } from '../../constants';
import { copyToClipboard } from '../../generic/data/thunks';
import { COMPONENT_TYPES } from '../constants';
import { COMPONENT_TYPES } from '../../generic/block-type-utils/constants';
import XBlockMessages from './xblock-messages/XBlockMessages';
import messages from './messages';

Expand Down
3 changes: 2 additions & 1 deletion src/course-unit/course-xblock/CourseXBlock.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import { getCourseSectionVerticalApiUrl, getXBlockBaseApiUrl } from '../data/api
import { fetchCourseSectionVerticalData } from '../data/thunk';
import { executeThunk } from '../../utils';
import { getCourseId } from '../data/selectors';
import { PUBLISH_TYPES, COMPONENT_TYPES } from '../constants';
import { PUBLISH_TYPES } from '../constants';
import { COMPONENT_TYPES } from '../../generic/block-type-utils/constants';
import { courseSectionVerticalMock, courseVerticalChildrenMock } from '../__mocks__';
import CourseXBlock from './CourseXBlock';
import messages from './messages';
Expand Down
69 changes: 69 additions & 0 deletions src/generic/block-type-utils/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';
import {
BackHand as BackHandIcon,
BookOpen as BookOpenIcon,
Edit as EditIcon,
EditNote as EditNoteIcon,
FormatListBulleted as FormatListBulletedIcon,
HelpOutline as HelpOutlineIcon,
LibraryAdd as LibraryIcon,
Lock as LockIcon,
QuestionAnswerOutline as QuestionAnswerOutlineIcon,
Science as ScienceIcon,
TextFields as TextFieldsIcon,
VideoCamera as VideoCameraIcon,
Folder,
} from '@openedx/paragon/icons';

export const UNIT_ICON_TYPES = ['video', 'other', 'vertical', 'problem', 'lock'];

export const COMPONENT_TYPES = {
advanced: 'advanced',
discussion: 'discussion',
library: 'library',
html: 'html',
openassessment: 'openassessment',
problem: 'problem',
video: 'video',
dragAndDrop: 'drag-and-drop-v2',
};

export const UNIT_TYPE_ICONS_MAP: Record<string, React.ComponentType> = {
video: VideoCameraIcon,
other: BookOpenIcon,
vertical: FormatListBulletedIcon,
problem: EditIcon,
lock: LockIcon,
};

export const COMPONENT_TYPE_ICON_MAP: Record<string, React.ComponentType> = {
[COMPONENT_TYPES.advanced]: ScienceIcon,
[COMPONENT_TYPES.discussion]: QuestionAnswerOutlineIcon,
[COMPONENT_TYPES.library]: LibraryIcon,
[COMPONENT_TYPES.html]: TextFieldsIcon,
[COMPONENT_TYPES.openassessment]: EditNoteIcon,
[COMPONENT_TYPES.problem]: HelpOutlineIcon,
[COMPONENT_TYPES.video]: VideoCameraIcon,
[COMPONENT_TYPES.dragAndDrop]: BackHandIcon,
};

export const STRUCTURAL_TYPE_ICONS: Record<string, React.ComponentType> = {
vertical: UNIT_TYPE_ICONS_MAP.vertical,
sequential: Folder,
chapter: Folder,
};

export const COMPONENT_TYPE_STYLE_COLOR_MAP = {
[COMPONENT_TYPES.advanced]: 'component-style-other',
[COMPONENT_TYPES.discussion]: 'component-style-default',
[COMPONENT_TYPES.library]: 'component-style-default',
[COMPONENT_TYPES.html]: 'component-style-html',
[COMPONENT_TYPES.openassessment]: 'component-style-default',
[COMPONENT_TYPES.problem]: 'component-style-default',
[COMPONENT_TYPES.video]: 'component-style-video',
[COMPONENT_TYPES.dragAndDrop]: 'component-style-default',
vertical: 'component-style-vertical',
sequential: 'component-style-default',
chapter: 'component-style-default',
collection: 'component-style-collection',
};
47 changes: 47 additions & 0 deletions src/generic/block-type-utils/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.component-style-default {
background-color: #005C9E;

.pgn__icon {
color: white;
}
}

.component-style-html {
background-color: #9747FF;

.pgn__icon {
color: white;
}
}

.component-style-collection {
background-color: #FFCD29;

.pgn__icon {
color: black;
}
}

.component-style-video {
background-color: #358F0A;

.pgn__icon {
color: white;
}
}

.component-style-vertical {
background-color: #0B8E77;

.pgn__icon {
color: white;
}
}

.component-style-other {
background-color: #646464;

.pgn__icon {
color: white;
}
}
15 changes: 15 additions & 0 deletions src/generic/block-type-utils/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import { Article } from '@openedx/paragon/icons';
import {
COMPONENT_TYPE_ICON_MAP,
STRUCTURAL_TYPE_ICONS,
COMPONENT_TYPE_STYLE_COLOR_MAP,
} from './constants';

export function getItemIcon(blockType: string): React.ComponentType {
return STRUCTURAL_TYPE_ICONS[blockType] ?? COMPONENT_TYPE_ICON_MAP[blockType] ?? Article;
}

export function getComponentStyleColor(blockType: string): string {
return COMPONENT_TYPE_STYLE_COLOR_MAP[blockType] ?? 'bg-component';
}
1 change: 1 addition & 0 deletions src/generic/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
@import "./modal-dropzone/ModalDropzone";
@import "./configure-modal/ConfigureModal";
@import "./drag-helper/SortableItem";
@import "./block-type-utils";
1 change: 1 addition & 0 deletions src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
@import "search-modal/SearchModal";
@import "certificates/scss/Certificates";
@import "group-configurations/GroupConfigurations";
@import "library-authoring";

// To apply the glow effect to the selected Section/Subsection, in the Course Outline
div.row:has(> div > div.highlight) {
Expand Down
9 changes: 4 additions & 5 deletions src/library-authoring/LibraryAuthoringPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,12 @@ describe('<LibraryAuthoringPage />', () => {
axiosMock.onGet(getContentLibraryApiUrl(libraryData.id)).reply(200, libraryData);

const {
getByRole, getByText, queryByText,
getByRole, getByText, queryByText, findByText,
} = render(<RootWrapper />);

// Ensure the search endpoint is called
await waitFor(() => { expect(fetchMock).toHaveFetchedTimes(1, searchEndpoint, 'post'); });
// One called for LibraryComponents and another called for components count
await waitFor(() => { expect(fetchMock).toHaveFetchedTimes(2, searchEndpoint, 'post'); });

expect(getByText('Content library')).toBeInTheDocument();
expect(getByText(libraryData.title)).toBeInTheDocument();
Expand All @@ -169,14 +170,13 @@ describe('<LibraryAuthoringPage />', () => {
expect(getByText('Recently Modified')).toBeInTheDocument();
expect(getByText('Collections (0)')).toBeInTheDocument();
expect(getByText('Components (6)')).toBeInTheDocument();
expect(getByText('There are 6 components in this library')).toBeInTheDocument();
expect(await findByText('Test HTML Block')).toBeInTheDocument();

// Navigate to the components tab
fireEvent.click(getByRole('tab', { name: 'Components' }));
expect(queryByText('Recently Modified')).not.toBeInTheDocument();
expect(queryByText('Collections (0)')).not.toBeInTheDocument();
expect(queryByText('Components (6)')).not.toBeInTheDocument();
expect(getByText('There are 6 components in this library')).toBeInTheDocument();

// Navigate to the collections tab
fireEvent.click(getByRole('tab', { name: 'Collections' }));
Expand All @@ -192,7 +192,6 @@ describe('<LibraryAuthoringPage />', () => {
expect(getByText('Recently Modified')).toBeInTheDocument();
expect(getByText('Collections (0)')).toBeInTheDocument();
expect(getByText('Components (6)')).toBeInTheDocument();
expect(getByText('There are 6 components in this library')).toBeInTheDocument();
});

it('show library without components', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/library-authoring/LibraryAuthoringPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import Loading from '../generic/Loading';
import SubHeader from '../generic/sub-header/SubHeader';
import Header from '../header';
import NotFoundAlert from '../generic/NotFoundAlert';
import LibraryComponents from './LibraryComponents';
import LibraryComponents from './components/LibraryComponents';
import LibraryCollections from './LibraryCollections';
import LibraryHome from './LibraryHome';
import { useContentLibrary } from './data/apiHooks';
Expand Down Expand Up @@ -126,7 +126,7 @@ const LibraryAuthoringPage = () => {
/>
<Route
path={TabList.components}
element={<LibraryComponents libraryId={libraryId} filter={{ searchKeywords }} />}
element={<LibraryComponents libraryId={libraryId} filter={{ searchKeywords }} variant="full" />}
/>
<Route
path={TabList.collections}
Expand Down
32 changes: 0 additions & 32 deletions src/library-authoring/LibraryComponents.tsx

This file was deleted.

6 changes: 3 additions & 3 deletions src/library-authoring/LibraryHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import {

import { NoComponents, NoSearchResults } from './EmptyStates';
import LibraryCollections from './LibraryCollections';
import LibraryComponents from './LibraryComponents';
import { useLibraryComponentCount } from './data/apiHooks';
import messages from './messages';
import { LibraryComponents } from './components';

const Section = ({ title, children } : { title: string, children: React.ReactNode }) => (
<Card>
Expand Down Expand Up @@ -45,8 +45,8 @@ const LibraryHome = ({ libraryId, filter } : LibraryHomeProps) => {
<Section title={intl.formatMessage(messages.collectionsTitle, { collectionCount })}>
<LibraryCollections />
</Section>
<Section title={intl.formatMessage(messages.componentsTitle, { componentCount })}>
<LibraryComponents libraryId={libraryId} filter={filter} />
<Section title={`Components (${componentCount})`}>
<LibraryComponents libraryId={libraryId} filter={filter} variant="preview" />
</Section>
</Stack>
);
Expand Down
2 changes: 2 additions & 0 deletions src/library-authoring/__mocks__/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// eslint-disable-next-line import/prefer-default-export
export { default as libraryComponentsMock } from './libraryComponentsMock';
Loading

0 comments on commit 77135cd

Please sign in to comment.