feat: Adding loading and done states to the publish library button [FC-0097] (#2237)
Adds loading and done states to the publish library button.
This commit is contained in:
@@ -4,9 +4,15 @@ import {
|
||||
FormattedTime,
|
||||
useIntl,
|
||||
} from '@edx/frontend-platform/i18n';
|
||||
import { Button, Container, Stack } from '@openedx/paragon';
|
||||
import {
|
||||
Button,
|
||||
Container,
|
||||
Icon,
|
||||
Stack,
|
||||
StatefulButton,
|
||||
} from '@openedx/paragon';
|
||||
import { SpinnerSimple } from '@openedx/paragon/icons';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
const CustomFormattedDate = ({ date }: { date: string }) => (
|
||||
@@ -84,8 +90,9 @@ type StatusWidgedProps = {
|
||||
created: string | null;
|
||||
publishedBy: string | null;
|
||||
numBlocks?: number;
|
||||
onCommit?: () => void;
|
||||
onCommit?: () => Promise<void>;
|
||||
onCommitLabel?: string;
|
||||
onCommitStatus?: 'pending' | 'error' | 'idle' | 'success';
|
||||
onRevert?: () => void;
|
||||
};
|
||||
|
||||
@@ -116,6 +123,7 @@ const StatusWidget = ({
|
||||
numBlocks,
|
||||
onCommit,
|
||||
onCommitLabel,
|
||||
onCommitStatus,
|
||||
onRevert,
|
||||
}: StatusWidgedProps) => {
|
||||
const intl = useIntl();
|
||||
@@ -125,6 +133,7 @@ const StatusWidget = ({
|
||||
let statusMessage: string;
|
||||
let extraStatusMessage: string | undefined;
|
||||
let bodyMessage: React.ReactNode | undefined;
|
||||
let publishButtonState: 'pending' | 'complete' | 'default' = 'default';
|
||||
|
||||
if (!lastPublished) {
|
||||
// Entity is never published (new)
|
||||
@@ -167,6 +176,12 @@ const StatusWidget = ({
|
||||
}
|
||||
}
|
||||
|
||||
if (onCommitStatus === 'pending') {
|
||||
publishButtonState = 'pending';
|
||||
} else if (isPublished) {
|
||||
publishButtonState = 'complete';
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Container className={classNames('status-widget', {
|
||||
@@ -189,9 +204,21 @@ const StatusWidget = ({
|
||||
{bodyMessage}
|
||||
</span>
|
||||
{onCommit && (
|
||||
<Button disabled={isPublished} onClick={onCommit}>
|
||||
{onCommitLabel || intl.formatMessage(messages.publishButtonLabel)}
|
||||
</Button>
|
||||
<StatefulButton
|
||||
variant="primary"
|
||||
state={publishButtonState}
|
||||
labels={{
|
||||
default: onCommitLabel || intl.formatMessage(messages.publishButtonLabel),
|
||||
complete: intl.formatMessage(messages.publishedStatusLabel),
|
||||
pending: intl.formatMessage(messages.publishingButtonLabelState),
|
||||
}}
|
||||
icons={{
|
||||
complete: null,
|
||||
pending: <Icon src={SpinnerSimple} className="icon-spin" />,
|
||||
}}
|
||||
disabledStates={['pending', 'complete']}
|
||||
onClick={onCommit}
|
||||
/>
|
||||
)}
|
||||
{onRevert && (
|
||||
<div className="d-flex justify-content-end">
|
||||
|
||||
@@ -46,6 +46,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Publish',
|
||||
description: 'Label of publish button for an entity.',
|
||||
},
|
||||
publishingButtonLabelState: {
|
||||
id: 'course-authoring.library-authoring.generic.status-widget.publishing-button',
|
||||
defaultMessage: 'Publishing',
|
||||
description: 'Label of publish button for an entity in the publishing state.',
|
||||
},
|
||||
discardChangesButtonLabel: {
|
||||
id: 'course-authoring.library-authoring.generic.status-widget.discard-button',
|
||||
defaultMessage: 'Discard Changes',
|
||||
|
||||
@@ -6,11 +6,12 @@ import {
|
||||
screen,
|
||||
waitFor,
|
||||
initializeMocks,
|
||||
} from '../../testUtils';
|
||||
} from '@src/testUtils';
|
||||
import { mockContentLibrary } from '../data/api.mocks';
|
||||
import { getCommitLibraryChangesUrl } from '../data/api';
|
||||
import { LibraryProvider } from '../common/context/LibraryContext';
|
||||
import LibraryInfo from './LibraryInfo';
|
||||
import * as apiHooks from '../data/apiHooks';
|
||||
|
||||
const {
|
||||
libraryId: mockLibraryId,
|
||||
@@ -105,7 +106,10 @@ describe('<LibraryInfo />', () => {
|
||||
|
||||
expect(await screen.findByText(libraryData.org)).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Published')).toBeInTheDocument();
|
||||
// First 'Published' from the state
|
||||
expect(screen.getAllByText('Published')[0]).toBeInTheDocument();
|
||||
// Second 'Published' from the published button
|
||||
expect(screen.getAllByText('Published')[1]).toBeInTheDocument();
|
||||
expect(screen.getByText('July 26, 2024')).toBeInTheDocument();
|
||||
expect(screen.getByText('staff')).toBeInTheDocument();
|
||||
});
|
||||
@@ -115,7 +119,10 @@ describe('<LibraryInfo />', () => {
|
||||
|
||||
expect(await screen.findByText(libraryData.org)).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Published')).toBeInTheDocument();
|
||||
// First 'Published' from the state
|
||||
expect(screen.getAllByText('Published')[0]).toBeInTheDocument();
|
||||
// Second 'Published' from the published button
|
||||
expect(screen.getAllByText('Published')[1]).toBeInTheDocument();
|
||||
expect(screen.getByText('July 26, 2024')).toBeInTheDocument();
|
||||
expect(screen.queryByText('staff')).not.toBeInTheDocument();
|
||||
});
|
||||
@@ -136,6 +143,31 @@ describe('<LibraryInfo />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should publish library 2', async () => {
|
||||
const useCommitLibraryChangesSpy = jest
|
||||
.spyOn(apiHooks, 'useCommitLibraryChanges')
|
||||
.mockReturnValue(
|
||||
// @ts-ignore
|
||||
{
|
||||
mutate: jest.fn(),
|
||||
mutateAsync: jest.fn(),
|
||||
status: 'pending',
|
||||
},
|
||||
);
|
||||
|
||||
render();
|
||||
|
||||
expect(await screen.findByText(libraryData.org)).toBeInTheDocument();
|
||||
|
||||
const publishButton = screen.getByRole('button', { name: /publish/i });
|
||||
fireEvent.click(publishButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/publishing/i)).toBeInTheDocument();
|
||||
});
|
||||
useCommitLibraryChangesSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should show error on publish library', async () => {
|
||||
const url = getCommitLibraryChangesUrl(libraryData.id);
|
||||
axiosMock.onPost(url).reply(500);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useCallback, useContext } from 'react';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { useToggle } from '@openedx/paragon';
|
||||
import { ToastContext } from '../../generic/toast-context';
|
||||
|
||||
import { ToastContext } from '@src/generic/toast-context';
|
||||
import DeleteModal from '@src/generic/delete-modal/DeleteModal';
|
||||
|
||||
import { useLibraryContext } from '../common/context/LibraryContext';
|
||||
import { useCommitLibraryChanges, useRevertLibraryChanges } from '../data/apiHooks';
|
||||
import StatusWidget from '../generic/status-widget';
|
||||
import messages from './messages';
|
||||
import DeleteModal from '../../generic/delete-modal/DeleteModal';
|
||||
|
||||
const LibraryPublishStatus = () => {
|
||||
const intl = useIntl();
|
||||
@@ -18,14 +19,14 @@ const LibraryPublishStatus = () => {
|
||||
const revertLibraryChanges = useRevertLibraryChanges();
|
||||
const { showToast } = useContext(ToastContext);
|
||||
|
||||
const commit = useCallback(() => {
|
||||
const commit = useCallback(async () => {
|
||||
if (libraryData) {
|
||||
commitLibraryChanges.mutateAsync(libraryData.id)
|
||||
.then(() => {
|
||||
showToast(intl.formatMessage(messages.publishSuccessMsg));
|
||||
}).catch(() => {
|
||||
showToast(intl.formatMessage(messages.publishErrorMsg));
|
||||
});
|
||||
try {
|
||||
await commitLibraryChanges.mutateAsync(libraryData.id);
|
||||
showToast(intl.formatMessage(messages.publishSuccessMsg));
|
||||
} catch (e) {
|
||||
showToast(intl.formatMessage(messages.publishErrorMsg));
|
||||
}
|
||||
}
|
||||
}, [libraryData]);
|
||||
|
||||
@@ -51,6 +52,7 @@ const LibraryPublishStatus = () => {
|
||||
<StatusWidget
|
||||
{...libraryData}
|
||||
onCommit={!readOnly ? commit : undefined}
|
||||
onCommitStatus={commitLibraryChanges.status}
|
||||
onCommitLabel={intl.formatMessage(messages.publishLibraryButtonLabel)}
|
||||
onRevert={!readOnly ? openConfirmModal : undefined}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user