diff --git a/src/generic/loading-button/LoadingButton.test.tsx b/src/generic/loading-button/LoadingButton.test.tsx
index dbcd5fdad..871fce6ee 100644
--- a/src/generic/loading-button/LoadingButton.test.tsx
+++ b/src/generic/loading-button/LoadingButton.test.tsx
@@ -72,4 +72,24 @@ describe('', () => {
expect(buttonElement).not.toHaveAttribute('aria-disabled', 'true');
expect(container.getElementsByClassName('icon-spin').length).toBe(0);
});
+
+ it('doesnt run the onClick function if the button is loading (disabled)', async () => {
+ let resolver: () => void;
+ const promise = new Promise((resolve) => {
+ resolver = resolve;
+ });
+ const longFunction = jest.fn().mockReturnValue(promise);
+ const { getByRole } = render(RootWrapper(longFunction));
+ const buttonElement = getByRole('button');
+ fireEvent.click(buttonElement);
+ // First click should call the function
+ expect(longFunction).toHaveBeenCalledTimes(1);
+ fireEvent.click(buttonElement);
+ // Second click should not call the function, because the button is disabled
+ expect(longFunction).toHaveBeenCalledTimes(1);
+ await act(async () => { resolver(); });
+ // After the promise is resolved, the button should be enabled
+ fireEvent.click(buttonElement);
+ expect(longFunction).toHaveBeenCalledTimes(2);
+ });
});
diff --git a/src/generic/loading-button/index.tsx b/src/generic/loading-button/index.tsx
index 5b9cbd4a4..89d4caf40 100644
--- a/src/generic/loading-button/index.tsx
+++ b/src/generic/loading-button/index.tsx
@@ -8,13 +8,12 @@ import {
StatefulButton,
} from '@openedx/paragon';
-interface LoadingButtonProps {
+interface LoadingButtonProps extends React.ButtonHTMLAttributes {
label: string;
onClick?: (e: any) => (Promise | void);
- disabled?: boolean;
size?: string;
variant?: string;
- className?: string;
+ isLoading?: boolean;
}
/**
@@ -26,17 +25,33 @@ const LoadingButton: React.FC = ({
disabled,
size,
variant,
- className,
+ isLoading,
+ ...props
}) => {
- const [state, setState] = useState('');
- // This is used to prevent setting the isLoading state after the component has been unmounted.
+ // Button state depends on isLoading and disabled flags
+ const getState = () => {
+ if (isLoading) { return 'pending'; }
+ if (disabled) { return 'disabled'; }
+ return '';
+ };
+ const [state, setState] = useState(getState);
+
+ useEffect(() => {
+ setState(getState);
+ }, [isLoading, disabled]);
+
const componentMounted = useRef(true);
+ // This is used to prevent setting the isLoading state after the component has been unmounted.
useEffect(() => () => {
componentMounted.current = false;
}, []);
const loadingOnClick = useCallback(async (e: any) => {
+ if (disabled) {
+ return;
+ }
+
if (!onClick) {
return;
}
@@ -55,13 +70,13 @@ const LoadingButton: React.FC = ({
return (
);
};
diff --git a/src/library-authoring/create-collection/CreateCollectionModal.tsx b/src/library-authoring/create-collection/CreateCollectionModal.tsx
index fccd59885..175b02ea8 100644
--- a/src/library-authoring/create-collection/CreateCollectionModal.tsx
+++ b/src/library-authoring/create-collection/CreateCollectionModal.tsx
@@ -1,7 +1,6 @@
import React from 'react';
import {
ActionRow,
- Button,
Form,
ModalDialog,
} from '@openedx/paragon';
@@ -10,6 +9,7 @@ import { useIntl } from '@edx/frontend-platform/i18n';
import { Formik } from 'formik';
import * as Yup from 'yup';
import FormikControl from '../../generic/FormikControl';
+import LoadingButton from '../../generic/loading-button';
import { useLibraryContext } from '../common/context/LibraryContext';
import messages from './messages';
import { useCreateLibraryCollection } from '../data/apiHooks';
@@ -67,55 +67,54 @@ const CreateCollectionModal = () => {
onSubmit={handleCreate}
>
{(formikProps) => (
- <>
+
)}
diff --git a/src/library-authoring/create-unit/CreateUnitModal.tsx b/src/library-authoring/create-unit/CreateUnitModal.tsx
index 33f491460..30ae48546 100644
--- a/src/library-authoring/create-unit/CreateUnitModal.tsx
+++ b/src/library-authoring/create-unit/CreateUnitModal.tsx
@@ -68,21 +68,19 @@ const CreateUnitModal = () => {
onSubmit={handleCreate}
>
{(formikProps) => (
- <>
+
)}