feat: Unit card previews [FC-0083] (#1774)
Adds block tiles to the Unit card to indicate type and quantity of children in the container.
This commit is contained in:
@@ -498,6 +498,56 @@ mockGetContainerMetadata.applyMock = () => {
|
||||
jest.spyOn(api, 'getContainerMetadata').mockImplementation(mockGetContainerMetadata);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock for `getContainerChildren()`
|
||||
*
|
||||
* This mock returns a fixed response for the given container ID.
|
||||
*/
|
||||
export async function mockGetContainerChildren(containerId: string): Promise<api.LibraryBlockMetadata[]> {
|
||||
let numChildren: number;
|
||||
switch (containerId) {
|
||||
case mockGetContainerChildren.fiveChildren:
|
||||
numChildren = 5;
|
||||
break;
|
||||
case mockGetContainerChildren.sixChildren:
|
||||
numChildren = 6;
|
||||
break;
|
||||
default:
|
||||
numChildren = 0;
|
||||
break;
|
||||
}
|
||||
return Promise.resolve(
|
||||
Array(numChildren).fill(mockGetContainerChildren.childTemplate).map((child, idx) => (
|
||||
{
|
||||
...child,
|
||||
// Generate a unique ID for each child block to avoid "duplicate key" errors in tests
|
||||
id: `lb:org1:Demo_course:html:text-${idx}`,
|
||||
}
|
||||
)),
|
||||
);
|
||||
}
|
||||
mockGetContainerChildren.fiveChildren = 'lct:org1:Demo_Course:unit:unit-5';
|
||||
mockGetContainerChildren.sixChildren = 'lct:org1:Demo_Course:unit:unit-6';
|
||||
mockGetContainerChildren.childTemplate = {
|
||||
id: 'lb:org1:Demo_course:html:text',
|
||||
blockType: 'html',
|
||||
defKey: 'def_key',
|
||||
displayName: 'text block',
|
||||
lastPublished: null,
|
||||
publishedBy: null,
|
||||
lastDraftCreated: null,
|
||||
lastDraftCreatedBy: null,
|
||||
hasUnpublishedChanges: false,
|
||||
created: null,
|
||||
modified: null,
|
||||
tagsCount: 0,
|
||||
collections: [] as api.CollectionMetadata[],
|
||||
} satisfies api.LibraryBlockMetadata;
|
||||
/** Apply this mock. Returns a spy object that can tell you if it's been called. */
|
||||
mockGetContainerChildren.applyMock = () => {
|
||||
jest.spyOn(api, 'getContainerChildren').mockImplementation(mockGetContainerChildren);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock for `getXBlockOLX()`
|
||||
*
|
||||
|
||||
@@ -111,6 +111,10 @@ export const getLibraryContainersApiUrl = (libraryId: string) => `${getApiBaseUr
|
||||
* Get the URL for the container detail api.
|
||||
*/
|
||||
export const getLibraryContainerApiUrl = (containerId: string) => `${getApiBaseUrl()}/api/libraries/v2/containers/${containerId}/`;
|
||||
/**
|
||||
* Get the URL for a single container children api.
|
||||
*/
|
||||
export const getLibraryContainerChildrenApiUrl = (containerId: string) => `${getLibraryContainerApiUrl(containerId)}children/`;
|
||||
|
||||
export interface ContentLibrary {
|
||||
id: string;
|
||||
@@ -616,3 +620,12 @@ export async function updateContainerMetadata(
|
||||
const client = getAuthenticatedHttpClient();
|
||||
await client.patch(getLibraryContainerApiUrl(containerId), snakeCaseObject(containerData));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a library container's children's metadata.
|
||||
*/
|
||||
export async function getContainerChildren(containerId: string): Promise<LibraryBlockMetadata[]> {
|
||||
const client = getAuthenticatedHttpClient();
|
||||
const { data } = await client.get(getLibraryContainerChildrenApiUrl(containerId));
|
||||
return camelCaseObject(data);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
getLibraryCollectionApiUrl,
|
||||
getBlockTypesMetaDataUrl,
|
||||
getLibraryContainerApiUrl,
|
||||
getLibraryContainerChildrenApiUrl,
|
||||
} from './api';
|
||||
import {
|
||||
useCommitLibraryChanges,
|
||||
@@ -23,6 +24,7 @@ import {
|
||||
useCollection,
|
||||
useBlockTypesMetadata,
|
||||
useContainer,
|
||||
useContainerChildren,
|
||||
} from './apiHooks';
|
||||
|
||||
let axiosMock;
|
||||
@@ -152,4 +154,79 @@ describe('library api hooks', () => {
|
||||
expect(result.current.data).toEqual({ testData: 'test-value' });
|
||||
expect(axiosMock.history.get[0].url).toEqual(url);
|
||||
});
|
||||
|
||||
it('should get container children', async () => {
|
||||
const containerId = 'lct:lib:org:unit:unit1';
|
||||
const url = getLibraryContainerChildrenApiUrl(containerId);
|
||||
|
||||
axiosMock.onGet(url).reply(200, [
|
||||
{
|
||||
id: 'lb:org1:Demo_course:html:text',
|
||||
block_type: 'html',
|
||||
def_key: 'def_key',
|
||||
display_name: 'text block',
|
||||
last_published: null,
|
||||
published_by: null,
|
||||
last_draft_created: null,
|
||||
last_draft_created_by: null,
|
||||
has_unpublished_changes: false,
|
||||
created: null,
|
||||
modified: null,
|
||||
tags_count: 0,
|
||||
collections: ['col1', 'col2'],
|
||||
},
|
||||
{
|
||||
id: 'lb:org1:Demo_course:video:video1',
|
||||
block_type: 'video',
|
||||
def_key: 'def_key',
|
||||
display_name: 'video block',
|
||||
last_published: null,
|
||||
published_by: null,
|
||||
last_draft_created: null,
|
||||
last_draft_created_by: null,
|
||||
has_unpublished_changes: false,
|
||||
created: null,
|
||||
modified: null,
|
||||
tags_count: 0,
|
||||
collections: ['col2'],
|
||||
},
|
||||
]);
|
||||
const { result } = renderHook(() => useContainerChildren(containerId), { wrapper });
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBeFalsy();
|
||||
});
|
||||
expect(result.current.data).toEqual([
|
||||
{
|
||||
id: 'lb:org1:Demo_course:html:text',
|
||||
blockType: 'html',
|
||||
defKey: 'def_key',
|
||||
displayName: 'text block',
|
||||
lastPublished: null,
|
||||
publishedBy: null,
|
||||
lastDraftCreated: null,
|
||||
lastDraftCreatedBy: null,
|
||||
hasUnpublishedChanges: false,
|
||||
created: null,
|
||||
modified: null,
|
||||
tagsCount: 0,
|
||||
collections: ['col1', 'col2'],
|
||||
},
|
||||
{
|
||||
id: 'lb:org1:Demo_course:video:video1',
|
||||
blockType: 'video',
|
||||
defKey: 'def_key',
|
||||
displayName: 'video block',
|
||||
lastPublished: null,
|
||||
publishedBy: null,
|
||||
lastDraftCreated: null,
|
||||
lastDraftCreatedBy: null,
|
||||
hasUnpublishedChanges: false,
|
||||
created: null,
|
||||
modified: null,
|
||||
tagsCount: 0,
|
||||
collections: ['col2'],
|
||||
},
|
||||
]);
|
||||
expect(axiosMock.history.get[0].url).toEqual(url);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,6 +51,7 @@ import {
|
||||
getContainerMetadata,
|
||||
updateContainerMetadata,
|
||||
type UpdateContainerDataRequest,
|
||||
getContainerChildren,
|
||||
} from './api';
|
||||
import { VersionSpec } from '../LibraryBlock';
|
||||
|
||||
@@ -95,6 +96,11 @@ export const libraryAuthoringQueryKeys = {
|
||||
'blockTypes',
|
||||
libraryId,
|
||||
],
|
||||
container: (libraryId?: string, containerId?: string) => [
|
||||
...libraryAuthoringQueryKeys.all,
|
||||
libraryId,
|
||||
containerId,
|
||||
],
|
||||
};
|
||||
|
||||
export const xblockQueryKeys = {
|
||||
@@ -114,11 +120,12 @@ export const xblockQueryKeys = {
|
||||
};
|
||||
|
||||
export const containerQueryKeys = {
|
||||
all: ['container'],
|
||||
all: ['container', 'children'],
|
||||
/**
|
||||
* Base key for data specific to a container
|
||||
*/
|
||||
container: (usageKey?: string) => [...containerQueryKeys.all, usageKey],
|
||||
children: (usageKey?: string) => [...containerQueryKeys.all, usageKey, 'children'],
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -613,3 +620,14 @@ export const useUpdateContainer = (containerId: string) => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the metadata and children for a container in a library
|
||||
*/
|
||||
export const useContainerChildren = (containerId: string) => (
|
||||
useQuery({
|
||||
enabled: !!containerId,
|
||||
queryKey: containerQueryKeys.children(containerId),
|
||||
queryFn: () => getContainerChildren(containerId!),
|
||||
})
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user