Skip to content

Commit

Permalink
feat: publish single library component
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielVZ96 committed Oct 21, 2024
1 parent 56e025a commit 6140adb
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/library-authoring/component-info/ComponentInfo.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,24 @@ describe('<ComponentInfo> Sidebar', () => {
const editButton = await screen.findByRole('button', { name: /Edit component/ });
await waitFor(() => expect(editButton).not.toBeDisabled());
});

it('shoud show a disabled "Publish" button when the component is already published', async () => {
initializeMocks();
render(
<ComponentInfo usageKey={mockLibraryBlockMetadata.usageKeyPublishDisabled} />,
withLibraryId(mockContentLibrary.libraryId),
);
const publishButton = await screen.findByRole('button', { name: /Publish component/ });
expect(publishButton).toBeDisabled();
});

it('should show a working "Publish" button when the component is not published', async () => {
initializeMocks();
render(
<ComponentInfo usageKey={mockLibraryBlockMetadata.usageKeyNeverPublished} />,
withLibraryId(mockContentLibrary.libraryId),
);
const publishButton = await screen.findByRole('button', { name: /Publish component/ });
await waitFor(() => expect(publishButton).not.toBeDisabled());
});
});
27 changes: 27 additions & 0 deletions src/library-authoring/component-info/ComponentInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ import ComponentManagement from './ComponentManagement';
import ComponentPreview from './ComponentPreview';
import messages from './messages';
import { getBlockType } from '../../generic/key-utils';
import { canEditComponent } from '../components/ComponentEditorModal';
import { useLibraryContext } from '../common/context';
import { useContentLibrary, useLibraryBlockMetadata, usePublishComponent } from '../data/apiHooks';
import { useCallback, useContext, useState } from 'react';
import { ToastContext } from '../../generic/toast-context';

const ComponentInfo = () => {
const intl = useIntl();
Expand Down Expand Up @@ -41,6 +46,23 @@ const ComponentInfo = () => {
category: getBlockType(usageKey),
}, '*');
};
const { libraryId, openComponentEditor } = useLibraryContext();
const publishComponent = usePublishComponent(usageKey);
const { data: libraryData } = useContentLibrary(libraryId);
const canEdit = libraryData?.canEditLibrary && canEditComponent(usageKey);
const { data: componentMetadata } = useLibraryBlockMetadata(usageKey);
// Only can be published when the component has been modified after the last published date.
const canPublish = (new Date(componentMetadata?.modified ?? 0)) > (new Date(componentMetadata?.lastPublished ?? 0 ))
const { showToast } = useContext(ToastContext);

const publish = useCallback(() => {
publishComponent.mutateAsync()
.then(() => {
showToast(intl.formatMessage(messages.publishSuccessMsg));
}).catch(() => {
showToast(intl.formatMessage(messages.publishErrorMsg));
});
}, [publishComponent, showToast, intl]);

return (
<Stack>
Expand All @@ -64,6 +86,11 @@ const ComponentInfo = () => {
{intl.formatMessage(messages.addComponentToCourse)}
</Button>
)}
<Button disabled={publishComponent.isLoading || !canPublish} onClick={publish} variant="outline-primary" className="m-1 text-nowrap flex-grow-1">
{intl.formatMessage(messages.publishComponentButtonTitle)}
</Button>
<ComponentMenu usageKey={usageKey} />
</div>
<Tabs
variant="tabs"
className="my-3 d-flex justify-content-around"
Expand Down
9 changes: 9 additions & 0 deletions src/library-authoring/component-info/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,15 @@ const messages = defineMessages({
id: 'course-authoring.library-authoring.component.add-to-course-error',
defaultMessage: 'Failed to add component to course',
description: 'Error message when adding component to course fails',
publishSuccessMsg: {
id: 'course-authoring.component-authoring.component.publish.success',
defaultMessage: 'Component published successfully',
description: 'Message when the component is published successfully.',
},
publishErrorMsg: {
id: 'course-authoring.component-authoring.component.publish.error',
defaultMessage: 'There was an error publishing the component.',
description: 'Message when there is an error when publishing the component.',
},
});

Expand Down
7 changes: 7 additions & 0 deletions src/library-authoring/data/api.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ export async function mockLibraryBlockMetadata(usageKey: string): Promise<api.Li
case thisMock.usageKeyNeverPublished: return thisMock.dataNeverPublished;
case thisMock.usageKeyPublished: return thisMock.dataPublished;
case thisMock.usageKeyWithCollections: return thisMock.dataWithCollections;
case thisMock.usageKeyPublishDisabled: return thisMock.dataPublishDisabled;
case thisMock.usageKeyThirdPartyXBlock: return thisMock.dataThirdPartyXBlock;
case thisMock.usageKeyForTags: return thisMock.dataPublished;
default: throw new Error(`No mock has been set up for usageKey "${usageKey}"`);
Expand Down Expand Up @@ -340,6 +341,12 @@ mockLibraryBlockMetadata.dataPublished = {
tagsCount: 0,
collections: [],
} satisfies api.LibraryBlockMetadata;
mockLibraryBlockMetadata.usageKeyPublishDisabled = 'lb:Axim:TEST2-disabled:html:571fe018-f3ce-45c9-8f53-5dafcb422fd2';
mockLibraryBlockMetadata.dataPublishDisabled = {
...mockLibraryBlockMetadata.dataPublished,
id: mockLibraryBlockMetadata.usageKeyPublishDisabled,
modified: '2024-06-11T13:54:21Z',
} satisfies api.LibraryBlockMetadata;
mockLibraryBlockMetadata.usageKeyThirdPartyXBlock = mockXBlockFields.usageKeyThirdParty;
mockLibraryBlockMetadata.dataThirdPartyXBlock = {
...mockLibraryBlockMetadata.dataPublished,
Expand Down
12 changes: 12 additions & 0 deletions src/library-authoring/data/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ export const getXBlockFieldsApiUrl = (usageKey: string) => `${getApiBaseUrl()}/a
* Get the URL for the xblock OLX API
*/
export const getXBlockOLXApiUrl = (usageKey: string) => `${getLibraryBlockMetadataUrl(usageKey)}olx/`;
/**
* Get the URL for the xblock Publish API
*/
export const getXBlockPublishApiUrl = (usageKey: string) => `${getApiBaseUrl()}/api/libraries/v2/blocks/${usageKey}/publish/`;
/**
* Get the URL for the xblock Assets List API
*/
Expand Down Expand Up @@ -412,6 +416,14 @@ export async function setXBlockOLX(usageKey: string, newOLX: string): Promise<st
return data.olx;
}

/**
* Publish the given XBlock.
*/
export async function publishXBlock(usageKey: string) {
const client = getAuthenticatedHttpClient();
await client.post(getXBlockPublishApiUrl(usageKey));
}

/**
* Fetch the asset (static file) list for the given XBlock.
*/
Expand Down
15 changes: 15 additions & 0 deletions src/library-authoring/data/apiHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
getXBlockAssets,
updateComponentCollections,
removeComponentsFromCollection,
publishXBlock,
} from './api';

export const libraryQueryPredicate = (query: Query, libraryId: string): boolean => {
Expand Down Expand Up @@ -356,6 +357,20 @@ export const useUpdateXBlockOLX = (usageKey: string) => {
});
};

/**
* Publish changes to a library component
*/
export const usePublishComponent = (usageKey: string) => {
const queryClient = useQueryClient();
const contentLibraryId = getLibraryId(usageKey);
return useMutation({
mutationFn: () => publishXBlock( usageKey),

Check failure on line 367 in src/library-authoring/data/apiHooks.ts

View workflow job for this annotation

GitHub Actions / tests (18)

There should be no space after this paren
onSettled: () => {
invalidateComponentData(queryClient, contentLibraryId, usageKey);
},
});
}

Check failure on line 372 in src/library-authoring/data/apiHooks.ts

View workflow job for this annotation

GitHub Actions / tests (18)

Expected indentation of 0 spaces but found 2

Check failure on line 372 in src/library-authoring/data/apiHooks.ts

View workflow job for this annotation

GitHub Actions / tests (18)

Missing semicolon

/** Get the list of assets (static files) attached to a library component */
export const useXBlockAssets = (usageKey: string) => (
useQuery({
Expand Down

0 comments on commit 6140adb

Please sign in to comment.