feat: Libraries v2: Advanced Component Info & OLX Editor (#1346)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
/* istanbul ignore file */
|
||||
import { mockContentTaxonomyTagsData } from '../../content-tags-drawer/data/api.mocks';
|
||||
import { getBlockType } from '../../generic/key-utils';
|
||||
import { createAxiosError } from '../../testUtils';
|
||||
import * as api from './api';
|
||||
|
||||
@@ -283,3 +284,53 @@ mockGetCollectionMetadata.collectionData = {
|
||||
mockGetCollectionMetadata.applyMock = () => {
|
||||
jest.spyOn(api, 'getCollectionMetadata').mockImplementation(mockGetCollectionMetadata);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock for `getXBlockOLX()`
|
||||
*
|
||||
* This mock returns different data/responses depending on the ID of the block
|
||||
* that you request. Use `mockXBlockOLX.applyMock()` to apply it to the whole
|
||||
* test suite.
|
||||
*/
|
||||
export async function mockXBlockOLX(usageKey: string): Promise<string> {
|
||||
const thisMock = mockXBlockOLX;
|
||||
switch (usageKey) {
|
||||
case thisMock.usageKeyHtml: return thisMock.olxHtml;
|
||||
default: {
|
||||
const blockType = getBlockType(usageKey);
|
||||
return `<${blockType}>This is mock OLX for usageKey "${usageKey}"</${blockType}>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mock of a "regular" HTML (Text) block:
|
||||
mockXBlockOLX.usageKeyHtml = mockXBlockFields.usageKeyHtml;
|
||||
mockXBlockOLX.olxHtml = `
|
||||
<html display_name="${mockXBlockFields.dataHtml.displayName}">
|
||||
${mockXBlockFields.dataHtml.data}
|
||||
</html>
|
||||
`;
|
||||
/** Apply this mock. Returns a spy object that can tell you if it's been called. */
|
||||
mockXBlockOLX.applyMock = () => jest.spyOn(api, 'getXBlockOLX').mockImplementation(mockXBlockOLX);
|
||||
|
||||
/**
|
||||
* Mock for `setXBlockOLX()`
|
||||
*/
|
||||
export async function mockSetXBlockOLX(_usageKey: string, newOLX: string): Promise<string> {
|
||||
return newOLX;
|
||||
}
|
||||
/** Apply this mock. Returns a spy object that can tell you if it's been called. */
|
||||
mockSetXBlockOLX.applyMock = () => jest.spyOn(api, 'setXBlockOLX').mockImplementation(mockSetXBlockOLX);
|
||||
|
||||
/**
|
||||
* Mock for `getXBlockAssets()`
|
||||
*
|
||||
* Use `getXBlockAssets.applyMock()` to apply it to the whole test suite.
|
||||
*/
|
||||
export async function mockXBlockAssets(): ReturnType<typeof api['getXBlockAssets']> {
|
||||
return [
|
||||
{ path: 'static/image1.png', url: 'https://cdn.test.none/image1.png', size: 12_345_000 },
|
||||
{ path: 'static/data.csv', url: 'https://cdn.test.none/data.csv', size: 8_000 },
|
||||
];
|
||||
}
|
||||
/** Apply this mock. Returns a spy object that can tell you if it's been called. */
|
||||
mockXBlockAssets.applyMock = () => jest.spyOn(api, 'getXBlockAssets').mockImplementation(mockXBlockAssets);
|
||||
|
||||
@@ -41,6 +41,10 @@ export const getXBlockFieldsApiUrl = (usageKey: string) => `${getApiBaseUrl()}/a
|
||||
* Get the URL for the xblock OLX API
|
||||
*/
|
||||
export const getXBlockOLXApiUrl = (usageKey: string) => `${getApiBaseUrl()}/api/libraries/v2/blocks/${usageKey}/olx/`;
|
||||
/**
|
||||
* Get the URL for the xblock Assets List API
|
||||
*/
|
||||
export const getXBlockAssetsApiUrl = (usageKey: string) => `${getApiBaseUrl()}/api/libraries/v2/blocks/${usageKey}/assets/`;
|
||||
/**
|
||||
* Get the URL for the Library Collections API.
|
||||
*/
|
||||
@@ -300,11 +304,31 @@ export async function createCollection(libraryId: string, collectionData: Create
|
||||
/**
|
||||
* Fetch the OLX for the given XBlock.
|
||||
*/
|
||||
// istanbul ignore next
|
||||
export async function getXBlockOLX(usageKey: string): Promise<string> {
|
||||
const { data } = await getAuthenticatedHttpClient().get(getXBlockOLXApiUrl(usageKey));
|
||||
return data.olx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the OLX for the given XBlock.
|
||||
* Returns the OLX as it was actually saved.
|
||||
*/
|
||||
// istanbul ignore next
|
||||
export async function setXBlockOLX(usageKey: string, newOLX: string): Promise<string> {
|
||||
const { data } = await getAuthenticatedHttpClient().post(getXBlockOLXApiUrl(usageKey), { olx: newOLX });
|
||||
return data.olx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the asset (static file) list for the given XBlock.
|
||||
*/
|
||||
// istanbul ignore next
|
||||
export async function getXBlockAssets(usageKey: string): Promise<{ path: string; url: string; size: number }[]> {
|
||||
const { data } = await getAuthenticatedHttpClient().get(getXBlockAssetsApiUrl(usageKey));
|
||||
return data.files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection metadata.
|
||||
*/
|
||||
|
||||
@@ -30,6 +30,8 @@ import {
|
||||
updateCollectionComponents,
|
||||
type CreateLibraryCollectionDataRequest,
|
||||
getCollectionMetadata,
|
||||
setXBlockOLX,
|
||||
getXBlockAssets,
|
||||
} from './api';
|
||||
|
||||
export const libraryQueryPredicate = (query: Query, libraryId: string): boolean => {
|
||||
@@ -75,6 +77,8 @@ export const xblockQueryKeys = {
|
||||
xblockFields: (usageKey: string) => [...xblockQueryKeys.xblock(usageKey), 'fields'],
|
||||
/** OLX (XML representation of the fields/content) */
|
||||
xblockOLX: (usageKey: string) => [...xblockQueryKeys.xblock(usageKey), 'OLX'],
|
||||
/** assets (static files) */
|
||||
xblockAssets: (usageKey: string) => [...xblockQueryKeys.xblock(usageKey), 'assets'],
|
||||
componentMetadata: (usageKey: string) => [...xblockQueryKeys.xblock(usageKey), 'componentMetadata'],
|
||||
};
|
||||
|
||||
@@ -255,7 +259,7 @@ export const useCreateLibraryCollection = (libraryId: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
/* istanbul ignore next */ // This is only used in developer builds, and the associated UI doesn't work in test or prod
|
||||
/** Get the OLX source of a library component */
|
||||
export const useXBlockOLX = (usageKey: string) => (
|
||||
useQuery({
|
||||
queryKey: xblockQueryKeys.xblockOLX(usageKey),
|
||||
@@ -264,6 +268,34 @@ export const useXBlockOLX = (usageKey: string) => (
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Update the OLX of a library component (advanced feature)
|
||||
*/
|
||||
export const useUpdateXBlockOLX = (usageKey: string) => {
|
||||
const contentLibraryId = getLibraryId(usageKey);
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (newOLX: string) => setXBlockOLX(usageKey, newOLX),
|
||||
onSuccess: (olxFromServer) => {
|
||||
queryClient.setQueryData(xblockQueryKeys.xblockOLX(usageKey), olxFromServer);
|
||||
// Reload the other data for this component:
|
||||
invalidateComponentData(queryClient, contentLibraryId, usageKey);
|
||||
// And the description and display name etc. may have changed, so refresh everything in the library too:
|
||||
queryClient.invalidateQueries({ queryKey: libraryAuthoringQueryKeys.contentLibrary(contentLibraryId) });
|
||||
queryClient.invalidateQueries({ predicate: (query) => libraryQueryPredicate(query, contentLibraryId) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/** Get the list of assets (static files) attached to a library component */
|
||||
export const useXBlockAssets = (usageKey: string) => (
|
||||
useQuery({
|
||||
queryKey: xblockQueryKeys.xblockAssets(usageKey),
|
||||
queryFn: () => getXBlockAssets(usageKey),
|
||||
enabled: !!usageKey,
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Get the metadata for a collection in a library
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user