Skip to content

Commit

Permalink
Use block type label instead of Library block_types REST API [FC-0062] (
Browse files Browse the repository at this point in the history
#1361)

* style: avoid using reserved word "type" as variable name

use componentType or blockType instead.

* refactor: let BlockTypeLabel handle displaying the component label

including the child count, if one is provided.

This change removes hooks for the block_types REST API

* test: add tests for BlockTypeLabel

---------

Co-authored-by: Chris Chávez <[email protected]>
  • Loading branch information
pomegranited and ChrisChV authored Oct 4, 2024
1 parent 9c1fd5a commit b957f3b
Show file tree
Hide file tree
Showing 19 changed files with 118 additions and 153 deletions.
2 changes: 0 additions & 2 deletions src/library-authoring/LibraryAuthoringPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import mockEmptyResult from '../search-modal/__mocks__/empty-search-result.json'
import {
mockContentLibrary,
mockGetCollectionMetadata,
mockLibraryBlockTypes,
mockXBlockFields,
} from './data/api.mocks';
import { mockContentSearchConfig } from '../search-manager/data/api.mock';
Expand All @@ -25,7 +24,6 @@ import { getLibraryCollectionsApiUrl } from './data/api';
mockGetCollectionMetadata.applyMock();
mockContentSearchConfig.applyMock();
mockContentLibrary.applyMock();
mockLibraryBlockTypes.applyMock();
mockXBlockFields.applyMock();
mockBroadcastChannel();

Expand Down
15 changes: 0 additions & 15 deletions src/library-authoring/LibraryRecentlyModified.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React, { useMemo } from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { orderBy } from 'lodash';

Expand All @@ -7,7 +6,6 @@ import { type CollectionHit, type ContentHit, SearchSortOption } from '../search
import LibrarySection, { LIBRARY_SECTION_PREVIEW_LIMIT } from './components/LibrarySection';
import messages from './messages';
import ComponentCard from './components/ComponentCard';
import { useLibraryBlockTypes } from './data/apiHooks';
import CollectionCard from './components/CollectionCard';
import { useLibraryContext } from './common/context';

Expand All @@ -19,7 +17,6 @@ const RecentlyModified: React.FC<Record<never, never>> = () => {
totalHits,
totalCollectionHits,
} = useSearchContext();
const { libraryId } = useLibraryContext();

const componentCount = totalHits + totalCollectionHits;
// Since we only display a fixed number of items in preview,
Expand All @@ -32,17 +29,6 @@ const RecentlyModified: React.FC<Record<never, never>> = () => {
...collectionList,
], ['modified'], ['desc']).slice(0, LIBRARY_SECTION_PREVIEW_LIMIT);

const { data: blockTypesData } = useLibraryBlockTypes(libraryId);
const blockTypes = useMemo(() => {
const result = {};
if (blockTypesData) {
blockTypesData.forEach(blockType => {
result[blockType.blockType] = blockType;
});
}
return result;
}, [blockTypesData]);

return componentCount > 0
? (
<LibrarySection
Expand All @@ -60,7 +46,6 @@ const RecentlyModified: React.FC<Record<never, never>> = () => {
<ComponentCard
key={contentHit.id}
contentHit={contentHit as ContentHit}
blockTypeDisplayName={blockTypes[(contentHit as ContentHit).blockType]?.displayName ?? ''}
/>
)
))}
Expand Down
2 changes: 0 additions & 2 deletions src/library-authoring/add-content/AddContentWorkflow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,13 @@ import * as textEditorHooks from '../../editors/containers/TextEditor/hooks';
import {
mockContentLibrary,
mockCreateLibraryBlock,
mockLibraryBlockTypes,
mockXBlockFields,
} from '../data/api.mocks';
import { mockBroadcastChannel, mockClipboardEmpty } from '../../generic/data/api.mock';
import { mockContentSearchConfig, mockSearchResult } from '../../search-manager/data/api.mock';
import LibraryLayout from '../LibraryLayout';

mockContentSearchConfig.applyMock();
mockLibraryBlockTypes.applyMock();
mockClipboardEmpty.applyMock();
mockBroadcastChannel();
mockContentLibrary.applyMock();
Expand Down
2 changes: 1 addition & 1 deletion src/library-authoring/collections/CollectionDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const CollectionStatsWidget = ({ libraryId, collectionId }: CollectionStatsWidge
{blockTypesArray.map(({ blockType, count }) => (
<BlockCount
key={blockType}
label={<BlockTypeLabel type={blockType} />}
label={<BlockTypeLabel blockType={blockType} />}
blockType={blockType}
count={count}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
import mockResult from '../__mocks__/collection-search.json';
import {
mockContentLibrary,
mockLibraryBlockTypes,
mockXBlockFields,
mockGetCollectionMetadata,
} from '../data/api.mocks';
Expand All @@ -24,7 +23,6 @@ mockGetCollectionMetadata.applyMock();
mockContentSearchConfig.applyMock();
mockGetBlockTypes.applyMock();
mockContentLibrary.applyMock();
mockLibraryBlockTypes.applyMock();
mockXBlockFields.applyMock();
mockBroadcastChannel();

Expand Down
18 changes: 10 additions & 8 deletions src/library-authoring/components/BaseComponentCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ import {

import { getItemIcon, getComponentStyleColor } from '../../generic/block-type-utils';
import TagCount from '../../generic/tag-count';
import { ContentHitTags, Highlight } from '../../search-manager';
import { BlockTypeLabel, ContentHitTags, Highlight } from '../../search-manager';

type BaseComponentCardProps = {
type: string,
componentType: string,
displayName: string,
description: string,
numChildren?: number,
tags: ContentHitTags,
actions: React.ReactNode,
blockTypeDisplayName: string,
openInfoSidebar: () => void
};

const BaseComponentCard = ({
type,
componentType,
displayName,
description,
numChildren,
tags,
actions,
blockTypeDisplayName,
openInfoSidebar,
} : BaseComponentCardProps) => {
const tagCount = useMemo(() => {
Expand All @@ -37,7 +37,7 @@ const BaseComponentCard = ({
+ (tags.level2?.length || 0) + (tags.level3?.length || 0);
}, [tags]);

const componentIcon = getItemIcon(type);
const componentIcon = getItemIcon(componentType);

return (
<Container className="library-component-card">
Expand All @@ -51,7 +51,7 @@ const BaseComponentCard = ({
}}
>
<Card.Header
className={`library-component-header ${getComponentStyleColor(type)}`}
className={`library-component-header ${getComponentStyleColor(componentType)}`}
title={
<Icon src={componentIcon} className="library-component-header-icon" />
}
Expand All @@ -62,7 +62,9 @@ const BaseComponentCard = ({
<Stack direction="horizontal" className="d-flex justify-content-between">
<Stack direction="horizontal" gap={1}>
<Icon src={componentIcon} size="sm" />
<span className="small">{blockTypeDisplayName}</span>
<span className="small">
<BlockTypeLabel blockType={componentType} count={numChildren} />
</span>
</Stack>
<TagCount count={tagCount} />
</Stack>
Expand Down
11 changes: 3 additions & 8 deletions src/library-authoring/components/CollectionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,35 +44,30 @@ type CollectionCardProps = {
};

const CollectionCard = ({ collectionHit }: CollectionCardProps) => {
const intl = useIntl();
const {
openCollectionInfoSidebar,
} = useLibraryContext();

const {
type,
type: componentType,
formatted,
tags,
numChildren,
} = collectionHit;
const { displayName = '', description = '' } = formatted;
const blockTypeDisplayName = numChildren ? intl.formatMessage(
messages.collectionTypeWithCount,
{ numChildren },
) : intl.formatMessage(messages.collectionType);

return (
<BaseComponentCard
type={type}
componentType={componentType}
displayName={displayName}
description={description}
tags={tags}
numChildren={numChildren}
actions={(
<ActionRow>
<CollectionMenu collectionHit={collectionHit} />
</ActionRow>
)}
blockTypeDisplayName={blockTypeDisplayName}
openInfoSidebar={() => openCollectionInfoSidebar(collectionHit.blockId)}
/>
);
Expand Down
2 changes: 1 addition & 1 deletion src/library-authoring/components/ComponentCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const clipboardBroadcastChannelMock = {
(global as any).BroadcastChannel = jest.fn(() => clipboardBroadcastChannelMock);

const libraryId = 'lib:org1:Demo_Course';
const render = () => baseRender(<ComponentCard contentHit={contentHit} blockTypeDisplayName="text" />, {
const render = () => baseRender(<ComponentCard contentHit={contentHit} />, {
extraWrapper: ({ children }) => <LibraryProvider libraryId={libraryId}>{ children }</LibraryProvider>,
});

Expand Down
6 changes: 2 additions & 4 deletions src/library-authoring/components/ComponentCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import BaseComponentCard from './BaseComponentCard';

type ComponentCardProps = {
contentHit: ContentHit,
blockTypeDisplayName: string,
};

export const ComponentMenu = ({ usageKey }: { usageKey: string }) => {
Expand Down Expand Up @@ -63,7 +62,7 @@ export const ComponentMenu = ({ usageKey }: { usageKey: string }) => {
);
};

const ComponentCard = ({ contentHit, blockTypeDisplayName } : ComponentCardProps) => {
const ComponentCard = ({ contentHit } : ComponentCardProps) => {
const {
openComponentInfoSidebar,
} = useLibraryContext();
Expand All @@ -83,7 +82,7 @@ const ComponentCard = ({ contentHit, blockTypeDisplayName } : ComponentCardProps

return (
<BaseComponentCard
type={blockType}
componentType={blockType}
displayName={displayName}
description={description}
tags={tags}
Expand All @@ -92,7 +91,6 @@ const ComponentCard = ({ contentHit, blockTypeDisplayName } : ComponentCardProps
<ComponentMenu usageKey={usageKey} />
</ActionRow>
)}
blockTypeDisplayName={blockTypeDisplayName}
openInfoSidebar={() => openComponentInfoSidebar(usageKey)}
/>
);
Expand Down
3 changes: 1 addition & 2 deletions src/library-authoring/components/LibraryComponents.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ import {
initializeMocks,
} from '../../testUtils';
import { getContentSearchConfigUrl } from '../../search-manager/data/api';
import { mockLibraryBlockTypes, mockContentLibrary } from '../data/api.mocks';
import { mockContentLibrary } from '../data/api.mocks';
import mockEmptyResult from '../../search-modal/__mocks__/empty-search-result.json';
import { LibraryProvider } from '../common/context';
import { libraryComponentsMock } from '../__mocks__';
import LibraryComponents from './LibraryComponents';

const searchEndpoint = 'http://mock.meilisearch.local/multi-search';

mockLibraryBlockTypes.applyMock();
mockContentLibrary.applyMock();
const mockFetchNextPage = jest.fn();
const mockUseSearchContext = jest.fn();
Expand Down
18 changes: 1 addition & 17 deletions src/library-authoring/components/LibraryComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import React, { useMemo } from 'react';

import { LoadingSpinner } from '../../generic/Loading';
import { useLoadOnScroll } from '../../hooks';
import { useSearchContext } from '../../search-manager';
import { NoComponents, NoSearchResults } from '../EmptyStates';
import { useLibraryBlockTypes } from '../data/apiHooks';
import ComponentCard from './ComponentCard';
import { LIBRARY_SECTION_PREVIEW_LIMIT } from './LibrarySection';
import { useLibraryContext } from '../common/context';
Expand All @@ -30,22 +27,10 @@ const LibraryComponents = ({ variant }: LibraryComponentsProps) => {
isLoading,
isFiltered,
} = useSearchContext();
const { libraryId, openAddContentSidebar } = useLibraryContext();
const { openAddContentSidebar } = useLibraryContext();

const componentList = variant === 'preview' ? hits.slice(0, LIBRARY_SECTION_PREVIEW_LIMIT) : hits;

// TODO get rid of "useLibraryBlockTypes". Use <BlockTypeLabel> instead.
const { data: blockTypesData } = useLibraryBlockTypes(libraryId);
const blockTypes = useMemo(() => {
const result = {};
if (blockTypesData) {
blockTypesData.forEach(blockType => {
result[blockType.blockType] = blockType;
});
}
return result;
}, [blockTypesData]);

useLoadOnScroll(
hasNextPage,
isFetchingNextPage,
Expand All @@ -67,7 +52,6 @@ const LibraryComponents = ({ variant }: LibraryComponentsProps) => {
<ComponentCard
key={contentHit.id}
contentHit={contentHit}
blockTypeDisplayName={blockTypes[contentHit.blockType]?.displayName ?? ''}
/>
)) }
</div>
Expand Down
10 changes: 0 additions & 10 deletions src/library-authoring/components/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,6 @@ const messages = defineMessages({
defaultMessage: 'Collection actions menu',
description: 'Alt/title text for the collection card menu button.',
},
collectionType: {
id: 'course-authoring.library-authoring.collection.type',
defaultMessage: 'Collection',
description: 'Collection type text',
},
collectionTypeWithCount: {
id: 'course-authoring.library-authoring.collection.type-with-count',
defaultMessage: 'Collection ({numChildren})',
description: 'Collection type text with children count',
},
menuOpen: {
id: 'course-authoring.library-authoring.collection.menu.open',
defaultMessage: 'Open',
Expand Down
43 changes: 0 additions & 43 deletions src/library-authoring/data/api.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,6 @@ import { mockContentTaxonomyTagsData } from '../../content-tags-drawer/data/api.
import { createAxiosError } from '../../testUtils';
import * as api from './api';

/**
* Mock for `getLibraryBlockTypes()`
*/
export async function mockLibraryBlockTypes(): Promise<api.LibraryBlockType[]> {
return [
{ blockType: 'about', displayName: 'overview' },
{ blockType: 'annotatable', displayName: 'Annotation' },
{ blockType: 'chapter', displayName: 'Section' },
{ blockType: 'conditional', displayName: 'Conditional' },
{ blockType: 'course', displayName: 'Empty' },
{ blockType: 'course_info', displayName: 'Text' },
{ blockType: 'discussion', displayName: 'Discussion' },
{ blockType: 'done', displayName: 'Completion' },
{ blockType: 'drag-and-drop-v2', displayName: 'Drag and Drop' },
{ blockType: 'edx_sga', displayName: 'Staff Graded Assignment' },
{ blockType: 'google-calendar', displayName: 'Google Calendar' },
{ blockType: 'google-document', displayName: 'Google Document' },
{ blockType: 'html', displayName: 'Text' },
{ blockType: 'library', displayName: 'Library' },
{ blockType: 'library_content', displayName: 'Randomized Content Block' },
{ blockType: 'lti', displayName: 'LTI' },
{ blockType: 'lti_consumer', displayName: 'LTI Consumer' },
{ blockType: 'openassessment', displayName: 'Open Response Assessment' },
{ blockType: 'poll', displayName: 'Poll' },
{ blockType: 'problem', displayName: 'Problem' },
{ blockType: 'scorm', displayName: 'Scorm module' },
{ blockType: 'sequential', displayName: 'Subsection' },
{ blockType: 'split_test', displayName: 'Content Experiment' },
{ blockType: 'staffgradedxblock', displayName: 'Staff Graded Points' },
{ blockType: 'static_tab', displayName: 'Empty' },
{ blockType: 'survey', displayName: 'Survey' },
{ blockType: 'thumbs', displayName: 'Thumbs' },
{ blockType: 'unit', displayName: 'Unit' },
{ blockType: 'vertical', displayName: 'Unit' },
{ blockType: 'video', displayName: 'Video' },
{ blockType: 'videoalpha', displayName: 'Video' },
{ blockType: 'word_cloud', displayName: 'Word cloud' },
];
}
mockLibraryBlockTypes.applyMock = () => {
jest.spyOn(api, 'getLibraryBlockTypes').mockImplementation(mockLibraryBlockTypes);
};

/**
* Mock for `getContentLibrary()`
*
Expand Down
13 changes: 0 additions & 13 deletions src/library-authoring/data/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL;
*/
export const getContentLibraryApiUrl = (libraryId: string) => `${getApiBaseUrl()}/api/libraries/v2/${libraryId}/`;

/**
* Get the URL for getting block types of a library (what types can be created).
*/
export const getLibraryBlockTypesUrl = (libraryId: string) => `${getApiBaseUrl()}/api/libraries/v2/${libraryId}/block_types/`;

/**
* Get the URL for create content in library.
*/
Expand Down Expand Up @@ -182,14 +177,6 @@ export interface CreateLibraryCollectionDataRequest {

export type UpdateCollectionComponentsRequest = Partial<CreateLibraryCollectionDataRequest>;

/**
* Fetch the list of XBlock types that can be added to this library
*/
export async function getLibraryBlockTypes(libraryId: string): Promise<LibraryBlockType[]> {
const { data } = await getAuthenticatedHttpClient().get(getLibraryBlockTypesUrl(libraryId));
return camelCaseObject(data);
}

/**
* Fetch a content library by its ID.
*/
Expand Down
Loading

0 comments on commit b957f3b

Please sign in to comment.