diff --git a/src/common/ActionButtonWithModal/ActionButtonWithModal.js b/src/common/ActionButtonWithModal/ActionButtonWithModal.js new file mode 100644 index 0000000..783b76b --- /dev/null +++ b/src/common/ActionButtonWithModal/ActionButtonWithModal.js @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/) + * + * This is free software, licensed under the GNU General Public License v3. + * See /LICENSE for more information. + */ + +import React, { useState, useEffect } from "react"; +import PropTypes from "prop-types"; +import { useAPIPost } from "../../api/hooks"; +import { API_STATE } from "../../api/utils"; +import Button from "../../bootstrap/Button"; +import { + Modal, + ModalHeader, + ModalBody, + ModalFooter, +} from "../../bootstrap/Modal"; +import { useAlert } from "../../context/alertContext/AlertContext"; + +ActionButtonWithModal.propTypes = { + actionTrigger: PropTypes.elementType.isRequired, + actionUrl: PropTypes.string.isRequired, + modalTitle: PropTypes.string.isRequired, + modalMessage: PropTypes.string.isRequired, + modalActionText: PropTypes.string, + modalActionProps: PropTypes.object, + successMessage: PropTypes.string, + errorMessage: PropTypes.string, +}; + +function ActionButtonWithModal({ + actionTrigger: ActionTriggerComponent, + actionUrl, + modalTitle, + modalMessage, + modalActionText, + modalActionProps, + successMessage, + errorMessage, +}) { + const [triggered, setTriggered] = useState(false); + const [modalShown, setModalShown] = useState(false); + const [triggerActionStatus, triggerAction] = useAPIPost(actionUrl); + + const [setAlert] = useAlert(); + useEffect(() => { + if (triggerActionStatus.state === API_STATE.SUCCESS) { + setAlert( + successMessage || _("Action successful."), + API_STATE.SUCCESS + ); + } + if (triggerActionStatus.state === API_STATE.ERROR) { + setAlert(errorMessage || _("Action failed.")); + } + }, [triggerActionStatus, setAlert, successMessage, errorMessage]); + + const actionHandler = () => { + setTriggered(true); + triggerAction(); + setModalShown(false); + }; + + return ( + <> + + setModalShown(true)} + /> + > + ); +} + +ActionModal.propTypes = { + shown: PropTypes.bool.isRequired, + setShown: PropTypes.func.isRequired, + onAction: PropTypes.func.isRequired, + title: PropTypes.string.isRequired, + message: PropTypes.string.isRequired, + actionText: PropTypes.string, +}; + +function ActionModal({ + shown, + setShown, + onAction, + title, + message, + actionText, + actionProps, +}) { + return ( + + + + {message} + + + setShown(false)} + > + {_("Cancel")} + + + {actionText || _("Confirm")} + + + + ); +} + +export default ActionButtonWithModal; diff --git a/src/common/RebootButton.md b/src/common/ActionButtonWithModal/ActionButtonWithModal.md similarity index 100% rename from src/common/RebootButton.md rename to src/common/ActionButtonWithModal/ActionButtonWithModal.md diff --git a/src/common/RebootButton.js b/src/common/RebootButton.js deleted file mode 100644 index 380b30d..0000000 --- a/src/common/RebootButton.js +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/) - * - * This is free software, licensed under the GNU General Public License v3. - * See /LICENSE for more information. - */ - -import React, { useState, useEffect } from "react"; - -import PropTypes from "prop-types"; - -import { useAPIPost } from "../api/hooks"; -import { API_STATE } from "../api/utils"; -import Button from "../bootstrap/Button"; -import { Modal, ModalHeader, ModalBody, ModalFooter } from "../bootstrap/Modal"; -import { useAlert } from "../context/alertContext/AlertContext"; -import { ForisURLs } from "../utils/forisUrls"; - -RebootButton.propTypes = { - /** Additional props to be passed to the button */ - props: PropTypes.object, -}; - -function RebootButton(props) { - const [triggered, setTriggered] = useState(false); - const [modalShown, setModalShown] = useState(false); - const [triggerRebootStatus, triggerReboot] = useAPIPost(ForisURLs.reboot); - - const [setAlert] = useAlert(); - useEffect(() => { - if (triggerRebootStatus.state === API_STATE.ERROR) { - setAlert(_("Reboot request failed.")); - } - }); - - const rebootHandler = () => { - setTriggered(true); - triggerReboot(); - setModalShown(false); - }; - - return ( - <> - - setModalShown(true)} - {...props} - > - {_("Reboot")} - - > - ); -} - -RebootModal.propTypes = { - shown: PropTypes.bool.isRequired, - setShown: PropTypes.func.isRequired, - onReboot: PropTypes.func.isRequired, -}; - -function RebootModal({ shown, setShown, onReboot }) { - return ( - - - - {_("Are you sure you want to restart the router?")} - - - setShown(false)} - > - {_("Cancel")} - - - {_("Confirm reboot")} - - - - ); -} - -export default RebootButton; diff --git a/src/common/__tests__/RebootButton.test.js b/src/common/__tests__/ActionButtonWithModal.test.js similarity index 57% rename from src/common/__tests__/RebootButton.test.js rename to src/common/__tests__/ActionButtonWithModal.test.js index 74c5b8e..11858a6 100644 --- a/src/common/__tests__/RebootButton.test.js +++ b/src/common/__tests__/ActionButtonWithModal.test.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/) + * Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/) * * This is free software, licensed under the GNU General Public License v3. * See /LICENSE for more information. @@ -18,15 +18,16 @@ import mockAxios from "jest-mock-axios"; import { mockJSONError } from "testUtils/network"; import { mockSetAlert } from "testUtils/alertContextMock"; -import RebootButton from "../RebootButton"; +import ActionButtonWithModal from "../ActionButtonWithModal/ActionButtonWithModal"; -describe("", () => { +describe("", () => { let componentContainer; beforeEach(() => { const { container } = render( <> - + + > ); componentContainer = container; @@ -37,27 +38,27 @@ describe("", () => { }); it("Render modal.", () => { - expect(queryByText(componentContainer, "Confirm reboot")).toBeNull(); - fireEvent.click(getByText(componentContainer, "Reboot")); + expect(queryByText(componentContainer, "Confirm action")).toBeNull(); + fireEvent.click(getByText(componentContainer, "Action")); expect(componentContainer).toMatchSnapshot(); }); - it("Confirm reboot.", () => { - fireEvent.click(getByText(componentContainer, "Reboot")); - fireEvent.click(getByText(componentContainer, "Confirm reboot")); + it("Confirm action.", () => { + fireEvent.click(getByText(componentContainer, "action")); + fireEvent.click(getByText(componentContainer, "Confirm action")); expect(mockAxios.post).toHaveBeenCalledWith( - "/reforis/api/reboot", + "/reforis/api/action", undefined, expect.anything() ); }); it("Hold error.", async () => { - fireEvent.click(getByText(componentContainer, "Reboot")); - fireEvent.click(getByText(componentContainer, "Confirm reboot")); + fireEvent.click(getByText(componentContainer, "Action")); + fireEvent.click(getByText(componentContainer, "Confirm action")); mockJSONError(); await wait(() => - expect(mockSetAlert).toBeCalledWith("Reboot request failed.") + expect(mockSetAlert).toBeCalledWith("Action request failed.") ); }); }); diff --git a/src/index.js b/src/index.js index 8052fb1..c030740 100644 --- a/src/index.js +++ b/src/index.js @@ -40,7 +40,7 @@ export { Spinner, SpinnerElement } from "./bootstrap/Spinner"; export { Modal, ModalBody, ModalFooter, ModalHeader } from "./bootstrap/Modal"; // Common -export { default as RebootButton } from "./common/RebootButton"; +export { default as ActionButtonWithModal } from "./common/ActionButtonWithModal/ActionButtonWithModal"; export { default as WiFiSettings } from "./common/WiFiSettings/WiFiSettings"; export { default as ResetWiFiSettings } from "./common/WiFiSettings/ResetWiFiSettings"; export { default as RichTable } from "./common/RichTable/RichTable"; diff --git a/styleguide.config.js b/styleguide.config.js index 2a701f7..fdd8e62 100644 --- a/styleguide.config.js +++ b/styleguide.config.js @@ -32,7 +32,7 @@ module.exports = { description: "Set of main components.", sections: [ { - name: "Foris Form", + name: "ForisForm", components: [ "src/form/components/ForisForm.js", "src/form/components/alerts.js", @@ -42,14 +42,16 @@ module.exports = { usageMode: "expand", }, { - name: "Rich Table", + name: "RichTable", components: ["src/common/RichTable/RichTable.js"], exampleMode: "expand", usageMode: "expand", }, { - name: "Reboot Button", - components: ["src/common/RebootButton.js"], + name: "ActionButtonWithModal", + components: [ + "src/common/ActionButtonWithModal/ActionButtonWithModal.js", + ], exampleMode: "expand", usageMode: "expand", },
{message}
{_("Are you sure you want to restart the router?")}