diff --git a/CHANGELOG.md b/CHANGELOG.md index bff7b5e..1e670df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,20 @@ and this project adheres to ## [Unreleased] +## [6.5.0] - 2024-11-13 + +### Added + +- Added & updated Weblate translations +- Added RichTable component with pagination and sorting +- Added @tanstack/react-table v8.20.5 to dependencies + +### Changed + +- Updated documentation +- Replaced RebootButton with ActionButtonWithModal component +- Fixed import path for CustomizationContextMock in customTestRender.js + ## [6.4.0] - 2024-10-02 ### Changed @@ -405,7 +419,8 @@ and this project adheres to ## [0.0.7] - 2019-09-02 [unreleased]: - https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.4.0...dev + https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.5.0...dev +[6.5.0]: https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.4.0...v6.5.0 [6.4.0]: https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.3.0...v6.4.0 [6.3.0]: https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.2.1...v6.3.0 [6.2.1]: https://gitlab.nic.cz/turris/reforis/foris-js/-/compare/v6.2.0...v6.2.1 diff --git a/package-lock.json b/package-lock.json index 3cb34d6..882a7f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,19 @@ { "name": "foris", - "version": "6.4.0", + "version": "6.5.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "foris", - "version": "6.4.0", + "version": "6.5.0", "license": "GPL-3.0", "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-regular-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", + "@tanstack/react-table": "^8.20.5", "axios": "^1.7.2", "immutability-helper": "^3.1.1", "moment": "^2.30.1", @@ -3583,6 +3584,39 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@tanstack/react-table": { + "version": "8.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.5.tgz", + "integrity": "sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.20.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.20.5.tgz", + "integrity": "sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@testing-library/dom": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-5.6.1.tgz", @@ -15493,7 +15527,6 @@ "version": "16.9.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.9.0.tgz", "integrity": "sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -16247,7 +16280,6 @@ "version": "0.15.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz", "integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -21102,6 +21134,19 @@ "@sinonjs/commons": "^3.0.0" } }, + "@tanstack/react-table": { + "version": "8.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.5.tgz", + "integrity": "sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==", + "requires": { + "@tanstack/table-core": "8.20.5" + } + }, + "@tanstack/table-core": { + "version": "8.20.5", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.20.5.tgz", + "integrity": "sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==" + }, "@testing-library/dom": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-5.6.1.tgz", @@ -30083,7 +30128,6 @@ "version": "16.9.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.9.0.tgz", "integrity": "sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==", - "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -30648,7 +30692,6 @@ "version": "0.15.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz", "integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==", - "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" diff --git a/package.json b/package.json index 7fa058e..994c90f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "foris", - "version": "6.4.0", + "version": "6.5.0", "description": "Foris JS library is a set of components and utils for reForis application and plugins.", "author": "CZ.NIC, z.s.p.o.", "repository": { @@ -18,6 +18,7 @@ "@fortawesome/free-regular-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", + "@tanstack/react-table": "^8.20.5", "axios": "^1.7.2", "immutability-helper": "^3.1.1", "moment": "^2.30.1", diff --git a/src/common/ActionButtonWithModal/ActionButtonWithModal.js b/src/common/ActionButtonWithModal/ActionButtonWithModal.js new file mode 100644 index 0000000..f500741 --- /dev/null +++ b/src/common/ActionButtonWithModal/ActionButtonWithModal.js @@ -0,0 +1,135 @@ +/* + * 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 = { + /** Component that triggers the action. */ + actionTrigger: PropTypes.elementType.isRequired, + /** URL to send the action to. */ + actionUrl: PropTypes.string.isRequired, + /** Title of the modal. */ + modalTitle: PropTypes.string.isRequired, + /** Message of the modal. */ + modalMessage: PropTypes.string.isRequired, + /** Text of the action button in the modal. */ + modalActionText: PropTypes.string, + /** Props for the action button in the modal. */ + modalActionProps: PropTypes.object, + /** Message to display on successful action. */ + successMessage: PropTypes.string, + /** Message to display on failed action. */ + 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, + actionProps: PropTypes.object, +}; + +function ActionModal({ + shown, + setShown, + onAction, + title, + message, + actionText, + actionProps, +}) { + return ( + + + +

{message}

+
+ + + + +
+ ); +} + +export default ActionButtonWithModal; diff --git a/src/common/ActionButtonWithModal/ActionButtonWithModal.md b/src/common/ActionButtonWithModal/ActionButtonWithModal.md new file mode 100644 index 0000000..69c589a --- /dev/null +++ b/src/common/ActionButtonWithModal/ActionButtonWithModal.md @@ -0,0 +1,39 @@ +RebootButton component is a button that opens a modal dialog to confirm the +reboot of the device. + +## Usage + +```jsx +import React, { useEffect, createContext } from "react"; + +import Button from "../../bootstrap/Button"; +import { AlertContextProvider } from "../../context/alertContext/AlertContext"; +import ActionButtonWithModal from "./ActionButtonWithModal"; + +window.AlertContext = React.createContext(); + +const RebootButtonExample = () => { + const ActionButton = (props) => { + return ; + }; + + return ( + +