diff --git a/package-lock.json b/package-lock.json index ac87a1f829..b1c40d5907 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,6 @@ "downshift": "^9.0.8", "fast-deep-equal": "^3.1.3", "fast-xml-parser": "^4.4.1", - "history": "^4.10.1", "hoist-non-react-statics": "^3.3.2", "i18next": "^23.14.0", "i18next-http-backend": "^2.6.1", @@ -36,14 +35,12 @@ "moment": "^2.30.1", "moment-timezone": "^0.5.45", "prop-types": "^15.8.1", - "qhistory": "^1.1.0", - "qs": "^6.13.0", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", "react-datepicker": "^6.0.0", "react-dom": "^18.3.1", "react-redux": "^9.1.2", - "react-router-dom": "^5.2.0", + "react-router-dom": "^6.26.2", "redux": "^5.0.1", "redux-logger": "^3.0.6", "styled-components": "^6.1.13", @@ -2940,6 +2937,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", + "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/pluginutils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", @@ -5090,6 +5095,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -5794,6 +5800,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -6015,6 +6022,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -6026,6 +6034,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, "engines": { "node": ">= 0.4" } @@ -6988,6 +6997,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -7141,6 +7151,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -7176,6 +7187,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -7187,6 +7199,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -7198,6 +7211,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -7231,19 +7245,6 @@ "node": ">= 0.4" } }, - "node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -7610,14 +7611,6 @@ "node": ">=12" } }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -8028,11 +8021,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -8976,6 +8964,7 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9245,14 +9234,6 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, - "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dependencies": { - "isarray": "0.0.1" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -9527,31 +9508,6 @@ "node": ">=6" } }, - "node_modules/qhistory": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/qhistory/-/qhistory-1.1.0.tgz", - "integrity": "sha512-mb5x+kKmEVSnUAX/loL20Y0kr1l4RZf7Al0Z1aNlwRL7nZo/QE46lRzLDro6CTNKcSUjiTOSQuFGRk4sU+FRUQ==", - "dependencies": { - "invariant": "^2.2.2" - }, - "peerDependencies": { - "history": ">=4.0.0" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -9732,46 +9688,35 @@ } }, "node_modules/react-router": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", - "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", + "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==", "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "@remix-run/router": "1.19.2" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "react": ">=15" + "react": ">=16.8" } }, "node_modules/react-router-dom": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", - "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", + "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.4", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "@remix-run/router": "1.19.2", + "react-router": "6.26.2" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "react": ">=15" + "react": ">=16.8", + "react-dom": ">=16.8" } }, - "node_modules/react-router/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -10003,11 +9948,6 @@ "node": ">=4" } }, - "node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, "node_modules/restore-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", @@ -10246,6 +10186,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -10303,6 +10244,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -10853,11 +10795,6 @@ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -11228,11 +11165,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, "node_modules/vite": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz", diff --git a/package.json b/package.json index ea8f342aaf..d2ab577c31 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "type": "git", "url": "https://github.com/greenbone/gsa/" }, - "author": "Bj\u00f6rn Ricks ", + "author": "Björn Ricks ", "license": "AGPL-3.0+", "type": "module", "scripts": { @@ -50,7 +50,6 @@ "downshift": "^9.0.8", "fast-deep-equal": "^3.1.3", "fast-xml-parser": "^4.4.1", - "history": "^4.10.1", "hoist-non-react-statics": "^3.3.2", "i18next": "^23.14.0", "i18next-http-backend": "^2.6.1", @@ -59,14 +58,12 @@ "moment": "^2.30.1", "moment-timezone": "^0.5.45", "prop-types": "^15.8.1", - "qhistory": "^1.1.0", - "qs": "^6.13.0", "react": "^18.3.1", "react-beautiful-dnd": "^13.1.1", "react-datepicker": "^6.0.0", "react-dom": "^18.3.1", "react-redux": "^9.1.2", - "react-router-dom": "^5.2.0", + "react-router-dom": "^6.26.2", "redux": "^5.0.1", "redux-logger": "^3.0.6", "styled-components": "^6.1.13", @@ -118,4 +115,4 @@ "prettier --write" ] } -} \ No newline at end of file +} diff --git a/src/web/authorized.jsx b/src/web/authorized.jsx index 687ac32046..adacf84657 100644 --- a/src/web/authorized.jsx +++ b/src/web/authorized.jsx @@ -3,108 +3,65 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import React from 'react'; - -import {connect} from 'react-redux'; - -import {withRouter} from 'react-router-dom'; - -import {isDefined} from 'gmp/utils/identity'; - +import {useEffect, useCallback} from 'react'; import {setIsLoggedIn} from './store/usersettings/actions'; -import {isLoggedIn} from 'web/store/usersettings/selectors'; - -import compose from 'web/utils/compose'; +import {isLoggedIn as selectIsLoggedIn} from 'web/store/usersettings/selectors'; import PropTypes from 'web/utils/proptypes'; -import withGmp from 'web/utils/withGmp'; - -class Authorized extends React.Component { - constructor(...args) { - super(...args); - - this.responseError = this.responseError.bind(this); - } - - componentDidMount() { - const {gmp} = this.props; - - this.responseError = this.responseError.bind(this); - - this.unsubscribe = gmp.addHttpErrorHandler(this.responseError); - - this.checkIsLoggedIn(); - } - - componentWillUnmount() { - if (isDefined(this.unsubscribe)) { - this.unsubscribe(); - } - } - - componentDidUpdate() { - this.checkIsLoggedIn(); - } - - responseError(xhr) { - const {logout} = this.props; - - if (xhr.status === 401) { - logout(); - return Promise.resolve(xhr); - } - return Promise.reject(xhr); - } - - checkIsLoggedIn() { - if (!this.props.isLoggedIn) { - this.toLoginPage(); +import useGmp from 'web/hooks/useGmp'; +import {useDispatch, useSelector} from 'react-redux'; +import {useNavigate, useLocation} from 'react-router-dom'; + +const Authorized = ({children}) => { + const gmp = useGmp(); + const dispatch = useDispatch(); + const navigate = useNavigate(); + const location = useLocation(); + const isLoggedIn = useSelector(selectIsLoggedIn); + + const logout = useCallback(() => { + gmp.logout(); + dispatch(setIsLoggedIn(false)); + }, [dispatch, gmp]); + + const responseError = useCallback( + xhr => { + if (xhr.status === 401) { + logout(); + return Promise.resolve(xhr); + } + return Promise.reject(new Error(xhr)); + }, + [logout], + ); + + const checkIsLoggedIn = useCallback(() => { + if (!isLoggedIn) { + navigate('/login', { + state: {next: location.pathname}, + }); } - } + }, [isLoggedIn, location.pathname, navigate]); - toLoginPage() { - const {history, location} = this.props; + useEffect(() => { + const unsubscribe = gmp.addHttpErrorHandler(responseError); + checkIsLoggedIn(); - if (location.pathname === '/login') { - // already at login page - return; - } + return () => { + if (unsubscribe) { + unsubscribe(); + } + }; + }, [gmp, responseError, checkIsLoggedIn]); - history.replace('/login', { - next: this.props.location.pathname, - }); - } + useEffect(() => { + checkIsLoggedIn(); + }, [isLoggedIn, checkIsLoggedIn]); - render() { - return this.props.isLoggedIn ? this.props.children : null; - } -} + return isLoggedIn ? children : null; +}; Authorized.propTypes = { - gmp: PropTypes.gmp.isRequired, - history: PropTypes.object.isRequired, - isLoggedIn: PropTypes.bool.isRequired, - location: PropTypes.object.isRequired, - logout: PropTypes.func.isRequired, + children: PropTypes.node.isRequired, }; -const mapStateToProps = rootState => ({ - isLoggedIn: isLoggedIn(rootState), -}); - -const mapDispatchToProps = (dispatch, {gmp}) => ({ - logout: () => { - gmp.logout(); - dispatch(setIsLoggedIn(false)); - }, -}); - -export default compose( - withGmp, - withRouter, - connect( - mapStateToProps, - mapDispatchToProps, - ), -)(Authorized); - -// vim: set ts=2 sw=2 tw=80: +export default Authorized; diff --git a/src/web/components/bar/menubar.jsx b/src/web/components/bar/menubar.jsx index 9d1315365b..388586c6b0 100644 --- a/src/web/components/bar/menubar.jsx +++ b/src/web/components/bar/menubar.jsx @@ -54,9 +54,8 @@ const MenuBarPlaceholder = styled.div` // eslint-disable-next-line no-shadow const MenuBar = ({isLoggedIn}) => { - const caps = useCapabilities(); - + if (!isLoggedIn || !isDefined(caps)) { return null; } @@ -115,7 +114,7 @@ const MenuBar = ({isLoggedIn}) => {
    - + {may_op_scans && ( {caps.mayAccess('tasks') && ( @@ -170,13 +169,13 @@ const MenuBar = ({isLoggedIn}) => { {caps.mayAccess('audits') && ( )} - {caps.featureEnabled('COMPLIANCE_REPORTS') && - caps.mayAccess('audits') && ( - - )} + {caps.featureEnabled('COMPLIANCE_REPORTS') && + caps.mayAccess('audits') && ( + + )} )} @@ -294,8 +293,6 @@ const mapStateToProps = rootState => ({ isLoggedIn: isLoggedIn(rootState), }); -export default compose( - connect(mapStateToProps), -)(MenuBar); +export default compose(connect(mapStateToProps))(MenuBar); // vim: set ts=2 sw=2 tw=80: diff --git a/src/web/components/conditionalRoute/ConditionalRoute.jsx b/src/web/components/conditionalRoute/ConditionalRoute.jsx index 97323b30fc..6e9cecb75b 100644 --- a/src/web/components/conditionalRoute/ConditionalRoute.jsx +++ b/src/web/components/conditionalRoute/ConditionalRoute.jsx @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; -import {Route, Redirect} from 'react-router-dom'; +import {Navigate} from 'react-router-dom'; import useCapabilities from 'web/hooks/useCapabilities'; const ConditionalRoute = ({component: Component, feature, ...rest}) => { @@ -13,12 +13,7 @@ const ConditionalRoute = ({component: Component, feature, ...rest}) => { const isEnabled = capabilities.featureEnabled(feature); return ( - - isEnabled ? : - } - {...rest} - /> + <>{isEnabled ? : } ); }; diff --git a/src/web/components/conditionalRoute/__tests__/ConditionalRoute.jsx b/src/web/components/conditionalRoute/__tests__/ConditionalRoute.jsx index a88608e852..f2ae3ba811 100644 --- a/src/web/components/conditionalRoute/__tests__/ConditionalRoute.jsx +++ b/src/web/components/conditionalRoute/__tests__/ConditionalRoute.jsx @@ -5,7 +5,7 @@ import {describe, test, expect} from '@gsa/testing'; import {screen, rendererWith} from 'web/utils/testing'; -import {Route, MemoryRouter} from 'react-router-dom'; +import {Route, Routes} from 'react-router-dom'; import ConditionalRoute from 'web/components/conditionalRoute/ConditionalRoute'; import Capabilities from 'gmp/capabilities/capabilities'; @@ -36,10 +36,15 @@ describe('ConditionalRoute', () => { }, ])('$description', ({feature, expectedText}) => { render( - - -
    Not Found
    } /> -
    , + + + } + /> + Not Found} /> + , ); expect(screen.getByText(expectedText)).toBeVisible(); diff --git a/src/web/components/dialog/__tests__/__snapshots__/multistepfooter.jsx.snap b/src/web/components/dialog/__tests__/__snapshots__/multistepfooter.jsx.snap index 5bd2debaff..430912e5f7 100644 --- a/src/web/components/dialog/__tests__/__snapshots__/multistepfooter.jsx.snap +++ b/src/web/components/dialog/__tests__/__snapshots__/multistepfooter.jsx.snap @@ -40,28 +40,6 @@ exports[`MultiStepFooter tests > should render 1`] = ` align-items: center; } -.c8 { - margin-left: -5px; -} - -.c8>* { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - -.c8>* { - margin-left: 5px; -} - -.c7 { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - .c3 { display: inline-block; padding: 0 15px; @@ -161,6 +139,28 @@ exports[`MultiStepFooter tests > should render 1`] = ` background: #11ab51; } +.c8 { + margin-left: -5px; +} + +.c8>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c8>* { + margin-left: 5px; +} + +.c7 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + .c2 { -webkit-box-pack: justify; -webkit-justify-content: space-between; @@ -249,28 +249,6 @@ exports[`MultiStepFooter tests > should render loading and disable cancel button align-items: center; } -.c8 { - margin-left: -5px; -} - -.c8>* { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - -.c8>* { - margin-left: 5px; -} - -.c7 { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - .c3 { display: inline-block; padding: 0 15px; @@ -381,6 +359,28 @@ exports[`MultiStepFooter tests > should render loading and disable cancel button background: #11ab51; } +.c8 { + margin-left: -5px; +} + +.c8>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c8>* { + margin-left: 5px; +} + +.c7 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + .c2 { -webkit-box-pack: justify; -webkit-justify-content: space-between; diff --git a/src/web/components/link/__tests__/certlink.jsx b/src/web/components/link/__tests__/certlink.jsx index 65dc8d5ff2..61b5fd5c13 100644 --- a/src/web/components/link/__tests__/certlink.jsx +++ b/src/web/components/link/__tests__/certlink.jsx @@ -8,8 +8,12 @@ import {describe, test, expect} from '@gsa/testing'; import {fireEvent, rendererWith} from 'web/utils/testing'; import CertLink from '../certlink'; +import {beforeEach} from 'vitest'; describe('CertLink tests', () => { + beforeEach(() => { + window.history.pushState({}, 'Test page', '/'); + }); test('should render CertLink', () => { const {render} = rendererWith({capabilities: true, router: true}); const {element} = render(); @@ -30,48 +34,47 @@ describe('CertLink tests', () => { }); test('should route to certbund details', () => { - const {render, history} = rendererWith({capabilities: true, router: true}); + const {render} = rendererWith({capabilities: true, router: true}); const {element} = render(); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/certbund/foo'); + expect(window.location.pathname).toEqual('/certbund/foo'); }); test('should route to dfncert details', () => { - const {render, history} = rendererWith({capabilities: true, router: true}); + const {render} = rendererWith({capabilities: true, router: true}); const {element} = render(); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - - expect(history.location.pathname).toEqual('/dfncert/foo'); + expect(window.location.pathname).toEqual('/dfncert/foo'); }); test('should not route to unknown type', () => { - const {render, history} = rendererWith({capabilities: true, router: true}); + const {render} = rendererWith({capabilities: true, router: true}); const {element} = render(); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); }); test('should not route in text mode', () => { - const {render, history} = rendererWith({capabilities: true, router: true}); + const {render} = rendererWith({capabilities: true, router: true}); const {element} = render( , ); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); }); }); diff --git a/src/web/components/link/__tests__/cvelink.jsx b/src/web/components/link/__tests__/cvelink.jsx index 6b74d62342..90323079a9 100644 --- a/src/web/components/link/__tests__/cvelink.jsx +++ b/src/web/components/link/__tests__/cvelink.jsx @@ -10,6 +10,9 @@ import {fireEvent, rendererWith} from 'web/utils/testing'; import CveLink from '../cvelink'; describe('CveLink tests', () => { + beforeEach(() => { + window.history.pushState({}, 'Test page', '/'); + }); test('should render CveLink', () => { const {render} = rendererWith({capabilities: true, router: true}); const {element} = render(); @@ -29,25 +32,25 @@ describe('CveLink tests', () => { }); test('should route to details', () => { - const {render, history} = rendererWith({capabilities: true, router: true}); + const {render} = rendererWith({capabilities: true, router: true}); const {element} = render(); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/cve/foo'); + expect(window.location.pathname).toEqual('/cve/foo'); }); test('should not route to details in text mode', () => { - const {render, history} = rendererWith({capabilities: true, router: true}); + const {render} = rendererWith({capabilities: true, router: true}); const {element} = render(); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); }); }); diff --git a/src/web/components/link/__tests__/detailslink.jsx b/src/web/components/link/__tests__/detailslink.jsx index fd78aced37..2d211019d7 100644 --- a/src/web/components/link/__tests__/detailslink.jsx +++ b/src/web/components/link/__tests__/detailslink.jsx @@ -12,6 +12,9 @@ import {fireEvent, rendererWith} from 'web/utils/testing'; import DetailsLink from '../detailslink'; describe('DetailsLink tests', () => { + beforeEach(() => { + window.history.pushState({}, 'Test page', '/'); + }); test('should render DetailsLink', () => { const {render} = rendererWith({capabilities: true, router: true}); const {element} = render( @@ -25,7 +28,7 @@ describe('DetailsLink tests', () => { }); test('should route to url', () => { - const {render, history} = rendererWith({capabilities: true, router: true}); + const {render} = rendererWith({capabilities: true, router: true}); const {element} = render( @@ -33,15 +36,15 @@ describe('DetailsLink tests', () => { , ); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/foo/1'); + expect(window.location.pathname).toEqual('/foo/1'); }); test('should url encode id', () => { - const {render, history} = rendererWith({capabilities: true, router: true}); + const {render} = rendererWith({capabilities: true, router: true}); const {element} = render( @@ -49,17 +52,17 @@ describe('DetailsLink tests', () => { , ); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual( + expect(window.location.pathname).toEqual( '/foo/cpe%3A%2Fa%3Ajenkins%3Ajenkins%3A2.141', ); }); test('should not route to url in text mode', () => { - const {render, history} = rendererWith({capabilities: true, router: true}); + const {render} = rendererWith({capabilities: true, router: true}); const {element} = render( @@ -67,16 +70,16 @@ describe('DetailsLink tests', () => { , ); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); }); test('should not route to url without capabilities', () => { const capabilities = new Capabilities(); - const {render, history} = rendererWith({capabilities, router: true}); + const {render} = rendererWith({capabilities, router: true}); const {element} = render( @@ -84,11 +87,11 @@ describe('DetailsLink tests', () => { , ); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); }); }); diff --git a/src/web/components/link/__tests__/link.jsx b/src/web/components/link/__tests__/link.jsx index 2282d3b976..437d8face0 100644 --- a/src/web/components/link/__tests__/link.jsx +++ b/src/web/components/link/__tests__/link.jsx @@ -11,6 +11,9 @@ import Link from '../link'; import Filter from 'gmp/models/filter'; describe('Link tests', () => { + beforeEach(() => { + window.history.pushState({}, 'Test page', '/'); + }); test('render Link', () => { const {render} = rendererWith({router: true}); @@ -20,32 +23,32 @@ describe('Link tests', () => { }); test('should route to url on click', () => { - const {render, history} = rendererWith({router: true}); + const {render} = rendererWith({router: true}); const {element} = render(Foo); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/foo'); + expect(window.location.pathname).toEqual('/foo'); }); test('should route to absolute url on click', () => { - const {render, history} = rendererWith({router: true}); + const {render} = rendererWith({router: true}); const {element} = render(Foo); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/foo'); + expect(window.location.pathname).toEqual('/foo'); }); test('should route to url with filter on click', () => { const filter = Filter.fromString('foo=bar'); - const {render, history} = rendererWith({router: true}); + const {render} = rendererWith({router: true}); const {element} = render( @@ -53,16 +56,18 @@ describe('Link tests', () => { , ); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/foo'); - expect(history.location.query).toEqual({filter: 'foo=bar'}); + expect(window.location.pathname).toEqual('/foo'); + + const searchParams = new URLSearchParams(window.location.search); + expect(searchParams.get('filter')).toEqual('foo=bar'); }); test('should route to url with query on click', () => { - const {render, history} = rendererWith({router: true}); + const {render} = rendererWith({router: true}); const query = {foo: 'bar'}; const {element} = render( @@ -71,16 +76,18 @@ describe('Link tests', () => { , ); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/foo'); - expect(history.location.query).toEqual(query); + expect(window.location.pathname).toEqual('/foo'); + + const searchParams = new URLSearchParams(window.location.search); + expect(searchParams.get('foo')).toEqual('bar'); }); test('should route to url with anchor on click', () => { - const {render, history} = rendererWith({router: true}); + const {render} = rendererWith({router: true}); const {element} = render( @@ -88,16 +95,16 @@ describe('Link tests', () => { , ); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); fireEvent.click(element); - expect(history.location.pathname).toEqual('/foo'); - expect(history.location.hash).toEqual('#bar'); + expect(window.location.pathname).toEqual('/foo'); + expect(window.location.hash).toEqual('#bar'); }); test('should not route to url in text mode', () => { - const {render, history} = rendererWith({router: true}); + const {render} = rendererWith({router: true}); const {element} = render( @@ -107,7 +114,7 @@ describe('Link tests', () => { fireEvent.click(element); - expect(history.location.pathname).toEqual('/'); + expect(window.location.pathname).toEqual('/'); }); test('should render styles', () => { diff --git a/src/web/components/link/link.jsx b/src/web/components/link/link.jsx index 9c8c7964fd..c628d96bf8 100644 --- a/src/web/components/link/link.jsx +++ b/src/web/components/link/link.jsx @@ -3,15 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; - import styled from 'styled-components'; - import {Link as RLink} from 'react-router-dom'; - import {isDefined, isString} from 'gmp/utils/identity'; - import PropTypes from 'web/utils/proptypes'; RLink.displayName = 'RouterLink'; @@ -45,7 +40,7 @@ export const withTextOnly = Component => { return TextOnly; }; -let Link = ({anchor, to = '', filter, query, ...other}) => { +const LinkComponent = ({anchor, to = '', filter, query, ...other}) => { let pathname = ''; if (to.startsWith('/')) { @@ -54,29 +49,32 @@ let Link = ({anchor, to = '', filter, query, ...other}) => { pathname += '/' + to; } - const location = { + const searchParams = new URLSearchParams(isDefined(query) ? query : {}); + if (isDefined(filter)) { + searchParams.set( + 'filter', + isString(filter) ? filter : filter.toFilterString(), + ); + } + + const fullPath = { pathname, - query: isDefined(query) ? {...query} : {}, - hash: isDefined(anchor) ? '#' + anchor : undefined, + search: searchParams.toString().replace(/\+/g, '%20'), + hash: isDefined(anchor) ? '#' + anchor : '', }; - if (isDefined(filter)) { - location.query.filter = isString(filter) ? filter : filter.toFilterString(); - } - return ; + return ; }; -Link.propTypes = { +LinkComponent.propTypes = { anchor: PropTypes.string, filter: PropTypes.oneOfType([PropTypes.filter, PropTypes.string]), query: PropTypes.object, to: PropTypes.string.isRequired, }; -Link = styled(withTextOnly(Link))` +const Link = styled(withTextOnly(LinkComponent))` display: inline-flex; `; export default Link; - -// vim: set ts=2 sw=2 tw=80: diff --git a/src/web/components/menu/__tests__/__snapshots__/usermenu.jsx.snap b/src/web/components/menu/__tests__/__snapshots__/usermenu.jsx.snap index ff58c4ba57..75be74124b 100644 --- a/src/web/components/menu/__tests__/__snapshots__/usermenu.jsx.snap +++ b/src/web/components/menu/__tests__/__snapshots__/usermenu.jsx.snap @@ -1,6 +1,40 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`UserMenu component tests > should render UserMenu 1`] = ` +.c2 { + width: 24px; + height: 24px; + line-height: 24px; +} + +.c2 * { + height: inherit; + width: inherit; +} + +.c10 { + width: 16px; + height: 16px; + line-height: 16px; +} + +.c10 * { + height: inherit; + width: inherit; +} + +.c11 { + cursor: pointer; + width: 16px; + height: 16px; + line-height: 16px; +} + +.c11 * { + height: inherit; + width: inherit; +} + .c7 { display: -webkit-box; display: -webkit-flex; @@ -19,13 +53,6 @@ exports[`UserMenu component tests > should render UserMenu 1`] = ` align-items: center; } -.c12 { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - .c9 { margin-left: -5px; } @@ -48,38 +75,11 @@ exports[`UserMenu component tests > should render UserMenu 1`] = ` display: inline-flex; } -.c2 { - width: 24px; - height: 24px; - line-height: 24px; -} - -.c2 * { - height: inherit; - width: inherit; -} - -.c10 { - width: 16px; - height: 16px; - line-height: 16px; -} - -.c10 * { - height: inherit; - width: inherit; -} - -.c11 { - cursor: pointer; - width: 16px; - height: 16px; - line-height: 16px; -} - -.c11 * { - height: inherit; - width: inherit; +.c12 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; } .c1 { diff --git a/src/web/components/menu/__tests__/usermenu.jsx b/src/web/components/menu/__tests__/usermenu.jsx index bf444b67c2..9f4dfa0497 100644 --- a/src/web/components/menu/__tests__/usermenu.jsx +++ b/src/web/components/menu/__tests__/usermenu.jsx @@ -37,7 +37,7 @@ describe('UserMenu component tests', () => { }); test('should route to usersettings on click', () => { - const {render, history} = rendererWith({ + const {render} = rendererWith({ gmp: {}, store: true, router: true, @@ -48,7 +48,7 @@ describe('UserMenu component tests', () => { fireEvent.click(userSettingsElement); - expect(history.location.pathname).toMatch('usersettings'); + expect(window.location.pathname).toMatch('usersettings'); }); test('should logout user on click', async () => { diff --git a/src/web/components/menu/usermenu.jsx b/src/web/components/menu/usermenu.jsx index 9aef15e649..d4b6623c52 100644 --- a/src/web/components/menu/usermenu.jsx +++ b/src/web/components/menu/usermenu.jsx @@ -5,7 +5,7 @@ import React from 'react'; -import {useHistory} from 'react-router-dom'; +import {useNavigate} from 'react-router-dom'; import styled, {keyframes} from 'styled-components'; @@ -121,13 +121,13 @@ const UserMenuContainer = () => { const [userTimezone] = useUserTimezone(); const [userName] = useUserName(); const gmp = useGmp(); - const history = useHistory(); + const navigate = useNavigate(); const handleLogout = event => { event.preventDefault(); gmp.doLogout().then(() => { - history.push('/login?type=logout'); + navigate('/login?type=logout'); }); }; diff --git a/src/web/components/observer/locationobserver.jsx b/src/web/components/observer/locationobserver.jsx index c43c990d96..53d5baa4c9 100644 --- a/src/web/components/observer/locationobserver.jsx +++ b/src/web/components/observer/locationobserver.jsx @@ -7,7 +7,7 @@ import React from 'react'; import {connect} from 'react-redux'; -import {withRouter} from 'react-router-dom'; +import {withRouter} from 'web/utils/withRouter'; import Logger from 'gmp/log'; @@ -71,12 +71,9 @@ LocationObserver.propTypes = { export default compose( withGmp, withRouter, - connect( - undefined, - (dispatch, {gmp}) => ({ - renewSessionTimeout: () => dispatch(renewSessionTimeout(gmp)()), - }), - ), + connect(undefined, (dispatch, {gmp}) => ({ + renewSessionTimeout: () => dispatch(renewSessionTimeout(gmp)()), + })), )(LocationObserver); // vim: set ts=2 sw=2 tw=80: diff --git a/src/web/components/powerfilter/__tests__/__snapshots__/booleanfiltergroup.jsx.snap b/src/web/components/powerfilter/__tests__/__snapshots__/booleanfiltergroup.jsx.snap index 7bc3e91f64..d0443b1317 100644 --- a/src/web/components/powerfilter/__tests__/__snapshots__/booleanfiltergroup.jsx.snap +++ b/src/web/components/powerfilter/__tests__/__snapshots__/booleanfiltergroup.jsx.snap @@ -19,28 +19,6 @@ exports[`BooleanFilterGroup tests > should render 1`] = ` align-items: center; } -.c5 { - margin-left: -5px; -} - -.c5>* { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - -.c5>* { - margin-left: 5px; -} - -.c4 { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - .c0 { display: -webkit-box; display: -webkit-flex; @@ -74,6 +52,28 @@ exports[`BooleanFilterGroup tests > should render 1`] = ` padding-right: 10px; } +.c5 { + margin-left: -5px; +} + +.c5>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c5>* { + margin-left: 5px; +} + +.c4 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + .c6 { display: -webkit-inline-box; display: -webkit-inline-flex; diff --git a/src/web/components/powerfilter/__tests__/__snapshots__/createnamedfiltergroup.jsx.snap b/src/web/components/powerfilter/__tests__/__snapshots__/createnamedfiltergroup.jsx.snap index 05cde3acdd..d2579e75cd 100644 --- a/src/web/components/powerfilter/__tests__/__snapshots__/createnamedfiltergroup.jsx.snap +++ b/src/web/components/powerfilter/__tests__/__snapshots__/createnamedfiltergroup.jsx.snap @@ -41,54 +41,6 @@ exports[`CreateNamedFilterGroup tests > should render 1`] = ` display: inline-flex; } -.c10 { - color: #c12c30; - font-weight: bold; - font-size: 19px; - padding-bottom: 1px; - padding-left: 4px; - display: none; -} - -.c8 { - font-family: inherit; - font-size: inherit; - line-height: inherit; - display: block; - height: 22px; - color: #4C4C4C; - background-color: #fff; - background-image: none; - border: 1px solid #bfbfbf; - border-radius: 2px; - padding: 1px 8px; - cursor: not-allowed; - background-color: #f3f3f3; - opacity: 0.65; -} - -.c8:-webkit-autofill { - box-shadow: 0 0 0 1000px white inset; -} - -.c9 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-box-pack: start; - -ms-flex-pack: start; - -webkit-justify-content: start; - justify-content: start; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - .c4 { display: -webkit-inline-box; display: -webkit-inline-flex; @@ -138,6 +90,54 @@ exports[`CreateNamedFilterGroup tests > should render 1`] = ` align-items: center; } +.c8 { + font-family: inherit; + font-size: inherit; + line-height: inherit; + display: block; + height: 22px; + color: #4C4C4C; + background-color: #fff; + background-image: none; + border: 1px solid #bfbfbf; + border-radius: 2px; + padding: 1px 8px; + cursor: not-allowed; + background-color: #f3f3f3; + opacity: 0.65; +} + +.c8:-webkit-autofill { + box-shadow: 0 0 0 1000px white inset; +} + +.c9 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c10 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; +} + .c1 { margin-top: 15px; } diff --git a/src/web/components/powerfilter/__tests__/__snapshots__/filtersearchgroup.jsx.snap b/src/web/components/powerfilter/__tests__/__snapshots__/filtersearchgroup.jsx.snap index 2432bfab64..03e1f8f9a6 100644 --- a/src/web/components/powerfilter/__tests__/__snapshots__/filtersearchgroup.jsx.snap +++ b/src/web/components/powerfilter/__tests__/__snapshots__/filtersearchgroup.jsx.snap @@ -35,15 +35,6 @@ exports[`FilterSearchGroup tests > should render 1`] = ` padding-bottom: 10px; } -.c4 { - color: #c12c30; - font-weight: bold; - font-size: 19px; - padding-bottom: 1px; - padding-left: 4px; - display: none; -} - .c2 { font-family: inherit; font-size: inherit; @@ -80,6 +71,15 @@ exports[`FilterSearchGroup tests > should render 1`] = ` align-items: center; } +.c4 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; +} +
    diff --git a/src/web/components/powerfilter/__tests__/__snapshots__/filterstringgroup.jsx.snap b/src/web/components/powerfilter/__tests__/__snapshots__/filterstringgroup.jsx.snap index d0fec2f880..f762240cdd 100644 --- a/src/web/components/powerfilter/__tests__/__snapshots__/filterstringgroup.jsx.snap +++ b/src/web/components/powerfilter/__tests__/__snapshots__/filterstringgroup.jsx.snap @@ -52,15 +52,6 @@ exports[`FilterStringGroup tests > should render 1`] = ` padding-right: 10px; } -.c6 { - color: #c12c30; - font-weight: bold; - font-size: 19px; - padding-bottom: 1px; - padding-left: 4px; - display: none; -} - .c4 { font-family: inherit; font-size: inherit; @@ -101,6 +92,15 @@ exports[`FilterStringGroup tests > should render 1`] = ` align-items: center; } +.c6 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; +} +
    diff --git a/src/web/components/powerfilter/__tests__/__snapshots__/minqodgroup.jsx.snap b/src/web/components/powerfilter/__tests__/__snapshots__/minqodgroup.jsx.snap index 4639acb333..f76e5c38f7 100644 --- a/src/web/components/powerfilter/__tests__/__snapshots__/minqodgroup.jsx.snap +++ b/src/web/components/powerfilter/__tests__/__snapshots__/minqodgroup.jsx.snap @@ -19,28 +19,6 @@ exports[`MinQodGroup tests > should render 1`] = ` align-items: center; } -.c5 { - margin-left: -5px; -} - -.c5>* { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - -.c5>* { - margin-left: 5px; -} - -.c4 { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - .c0 { display: -webkit-box; display: -webkit-flex; @@ -148,6 +126,28 @@ exports[`MinQodGroup tests > should render 1`] = ` bottom: 0; } +.c5 { + margin-left: -5px; +} + +.c5>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c5>* { + margin-left: 5px; +} + +.c4 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} +
    diff --git a/src/web/components/table/__tests__/__snapshots__/detailstable.jsx.snap b/src/web/components/table/__tests__/__snapshots__/detailstable.jsx.snap index 47a12aef28..ee9f45906c 100644 --- a/src/web/components/table/__tests__/__snapshots__/detailstable.jsx.snap +++ b/src/web/components/table/__tests__/__snapshots__/detailstable.jsx.snap @@ -19,6 +19,14 @@ exports[`DetailsTable tests > should render 1`] = ` align-items: stretch; } +.c2 { + width: 10%; +} + +.c3 { + width: 90%; +} + .c0 { border: 0; border-spacing: 0px; @@ -36,14 +44,6 @@ exports[`DetailsTable tests > should render 1`] = ` padding-right: 5px; } -.c2 { - width: 10%; -} - -.c3 { - width: 90%; -} - @media print { .c0 { border-collapse: collapse; diff --git a/src/web/entities/container.jsx b/src/web/entities/container.jsx index a29c90f3a2..b7871952bb 100644 --- a/src/web/entities/container.jsx +++ b/src/web/entities/container.jsx @@ -3,10 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; -import {withRouter} from 'react-router-dom'; +import {withRouter} from 'web/utils/withRouter'; import {connect} from 'react-redux'; @@ -321,17 +320,18 @@ class EntitiesContainer extends React.Component { this.changeFilter(RESET_FILTER); } - handleFilterReset() { - const {history, location} = this.props; - const query = {...location.query}; + handleFilterReset = () => { + const {navigate, location, searchParams} = this.props; - // remove filter param from url - delete query.filter; + searchParams.delete('filter'); - history.push({pathname: location.pathname, query}); + navigate({ + pathname: location.pathname, + search: searchParams.toString(), + }); this.changeFilter(); - } + }; openTagDialog() { this.setState({tagDialogVisible: true}); @@ -583,9 +583,11 @@ EntitiesContainer.propTypes = { entitiesCounts: PropTypes.counts, entitiesError: PropTypes.error, filter: PropTypes.filter, + searchParams: PropTypes.object, + location: PropTypes.object, gmp: PropTypes.gmp.isRequired, gmpname: PropTypes.string.isRequired, - history: PropTypes.object.isRequired, + navigate: PropTypes.object.isRequired, isLoading: PropTypes.bool.isRequired, listExportFileName: PropTypes.string, loadSettings: PropTypes.func.isRequired, @@ -604,9 +606,8 @@ EntitiesContainer.propTypes = { const mapStateToProps = rootState => { const userDefaultsSelector = getUserSettingsDefaults(rootState); const username = getUsername(rootState); - const listExportFileName = userDefaultsSelector.getValueByName( - 'listexportfilename', - ); + const listExportFileName = + userDefaultsSelector.getValueByName('listexportfilename'); return { listExportFileName, username, diff --git a/src/web/entities/withEntitiesContainer.jsx b/src/web/entities/withEntitiesContainer.jsx index 0db78be131..7f3f179db9 100644 --- a/src/web/entities/withEntitiesContainer.jsx +++ b/src/web/entities/withEntitiesContainer.jsx @@ -5,7 +5,7 @@ import React from 'react'; -import {useLocation} from 'react-router-dom'; +import {useSearchParams} from 'react-router-dom'; import {connect} from 'react-redux'; @@ -27,99 +27,98 @@ import EntitiesContainer from './container'; const noop = () => {}; -const withEntitiesContainer = ( - gmpname, - { - entitiesSelector, - loadEntities: loadEntitiesFunc, - reloadInterval = noop, - fallbackFilter, - }, -) => Component => { - let EntitiesContainerWrapper = ({ - children, - filter, - loadEntities, - notify, - ...props - }) => ( - reloadInterval(props)} - reload={(newFilter = filter) => loadEntities(newFilter)} - name={gmpname} - > - {({reload}) => ( - - {pageProps => } - - )} - - ); - - EntitiesContainerWrapper.propTypes = { - filter: PropTypes.filter, - loadEntities: PropTypes.func.isRequired, - notify: PropTypes.func.isRequired, - }; - - const mapStateToProps = (state, {filter}) => { - const eSelector = entitiesSelector(state); - const entities = eSelector.getEntities(filter); - return { - entities, - entitiesCounts: eSelector.getEntitiesCounts(filter), - entitiesError: eSelector.getEntitiesError(filter), +const withEntitiesContainer = + ( + gmpname, + { + entitiesSelector, + loadEntities: loadEntitiesFunc, + reloadInterval = noop, + fallbackFilter, + }, + ) => + Component => { + let EntitiesContainerWrapper = ({ + children, filter, - isLoading: eSelector.isLoadingEntities(filter), - loadedFilter: eSelector.getLoadedFilter(filter), - }; - }; - - const mapDispatchToProps = (dispatch, {gmp}) => ({ - loadEntities: filter => dispatch(loadEntitiesFunc(gmp)(filter)), - updateFilter: filter => dispatch(pageFilter(gmpname, filter)), - onInteraction: () => dispatch(renewSessionTimeout(gmp)()), - }); - - EntitiesContainerWrapper = compose( - withDialogNotification, - withDownload, - withGmp, - connect( - mapStateToProps, - mapDispatchToProps, - ), - )(EntitiesContainerWrapper); - - return props => { - const location = useLocation(); - return ( - - {({notify}) => ( - ( + reloadInterval(props)} + reload={(newFilter = filter) => loadEntities(newFilter)} + name={gmpname} + > + {({reload}) => ( + - {({filter}) => ( - - )} - + {pageProps => } + )} - + ); + + EntitiesContainerWrapper.propTypes = { + filter: PropTypes.filter, + loadEntities: PropTypes.func.isRequired, + notify: PropTypes.func.isRequired, + }; + + const mapStateToProps = (state, {filter}) => { + const eSelector = entitiesSelector(state); + const entities = eSelector.getEntities(filter); + return { + entities, + entitiesCounts: eSelector.getEntitiesCounts(filter), + entitiesError: eSelector.getEntitiesError(filter), + filter, + isLoading: eSelector.isLoadingEntities(filter), + loadedFilter: eSelector.getLoadedFilter(filter), + }; + }; + + const mapDispatchToProps = (dispatch, {gmp}) => ({ + loadEntities: filter => dispatch(loadEntitiesFunc(gmp)(filter)), + updateFilter: filter => dispatch(pageFilter(gmpname, filter)), + onInteraction: () => dispatch(renewSessionTimeout(gmp)()), + }); + + EntitiesContainerWrapper = compose( + withDialogNotification, + withDownload, + withGmp, + connect(mapStateToProps, mapDispatchToProps), + )(EntitiesContainerWrapper); + + return props => { + const [searchParams] = useSearchParams(); + return ( + + {({notify}) => ( + + {({filter}) => ( + + )} + + )} + + ); + }; }; -}; export default withEntitiesContainer; diff --git a/src/web/entity/component.jsx b/src/web/entity/component.jsx index 662e4ac833..0f22134ec3 100644 --- a/src/web/entity/component.jsx +++ b/src/web/entity/component.jsx @@ -23,14 +23,16 @@ import {generateFilename} from 'web/utils/render'; import withGmp from 'web/utils/withGmp'; -export const goto_details = (type, props) => ({data}) => { - const {history} = props; - return history.push('/' + type + '/' + data.id); -}; +export const goto_details = + (type, props) => + ({data}) => { + const {navigate} = props; + return navigate('/' + type + '/' + data.id); + }; export const goto_list = (type, props) => () => { - const {history} = props; - return history.push('/' + type); + const {navigate} = props; + return navigate('/' + type); }; class EntityComponent extends React.Component { @@ -173,10 +175,7 @@ const mapDispatchToProps = (dispatch, {name, gmp}) => { export default compose( withGmp, - connect( - mapStateToProps, - mapDispatchToProps, - ), + connect(mapStateToProps, mapDispatchToProps), )(EntityComponent); // vim: set ts=2 sw=2 tw=80: diff --git a/src/web/entity/withEntityContainer.jsx b/src/web/entity/withEntityContainer.jsx index e0b1ebbf1e..c0a48bd7dd 100644 --- a/src/web/entity/withEntityContainer.jsx +++ b/src/web/entity/withEntityContainer.jsx @@ -7,7 +7,7 @@ import React from 'react'; import {connect} from 'react-redux'; -import {withRouter} from 'react-router-dom'; +import {withRouter} from 'web/utils/withRouter'; import Filter from 'gmp/models/filter'; @@ -45,71 +45,70 @@ export const permissionsSubjectFilter = id => id, ).all(); -const withEntityContainer = ( - entityType, - { - load, - entitySelector, - mapStateToProps: componentMapStateToProps, - reloadInterval = defaultEntityReloadIntervalFunc, - }, -) => Component => { - const EntityContainerWrapper = props => ( - reloadInterval(props)} - reload={(id = props.id) => props.load(id)} - name={entityType} - > - {({reload}) => ( - - {cprops => } - - )} - - ); - - EntityContainerWrapper.propTypes = { - id: PropTypes.id.isRequired, - load: PropTypes.func.isRequired, - }; +const withEntityContainer = + ( + entityType, + { + load, + entitySelector, + mapStateToProps: componentMapStateToProps, + reloadInterval = defaultEntityReloadIntervalFunc, + }, + ) => + Component => { + const EntityContainerWrapper = props => ( + reloadInterval(props)} + reload={(id = props.id) => props.load(id)} + name={entityType} + > + {({reload}) => ( + + {cprops => } + + )} + + ); + + EntityContainerWrapper.propTypes = { + id: PropTypes.id.isRequired, + load: PropTypes.func.isRequired, + }; - const mapDispatchToProps = (dispatch, {gmp}) => ({ - onInteraction: () => dispatch(renewSessionTimeout(gmp)()), - load: id => dispatch(load(gmp)(id)), - }); - - const mapStateToProps = (rootState, {gmp, id, match, ...props}) => { - if (!isDefined(id)) { - id = decodeURIComponent(match.params.id); // decodeURIComponent needs to be done for CPE IDs - } - const entitySel = entitySelector(rootState); - const otherProps = isDefined(componentMapStateToProps) - ? componentMapStateToProps(rootState, { - gmp, - id, - ...props, - }) - : undefined; - return { - isLoading: entitySel.isLoadingEntity(id), - ...otherProps, - id, - entity: entitySel.getEntity(id), - entityError: entitySel.getEntityError(id), + const mapDispatchToProps = (dispatch, {gmp}) => ({ + onInteraction: () => dispatch(renewSessionTimeout(gmp)()), + load: id => dispatch(load(gmp)(id)), + }); + + const mapStateToProps = (rootState, {gmp, id, params, ...props}) => { + if (!isDefined(id)) { + id = decodeURIComponent(params.id); // decodeURIComponent needs to be done for CPE IDs + } + const entitySel = entitySelector(rootState); + const otherProps = isDefined(componentMapStateToProps) + ? componentMapStateToProps(rootState, { + gmp, + id, + ...props, + }) + : undefined; + return { + isLoading: entitySel.isLoadingEntity(id), + ...otherProps, + id, + entity: entitySel.getEntity(id), + entityError: entitySel.getEntityError(id), + }; }; - }; - return compose( - withGmp, - withRouter, - withDialogNotification, - withDownload, - connect( - mapStateToProps, - mapDispatchToProps, - ), - )(EntityContainerWrapper); -}; + return compose( + withGmp, + withRouter, + withDialogNotification, + withDownload, + connect(mapStateToProps, mapDispatchToProps), + )(EntityContainerWrapper); + }; export default withEntityContainer; diff --git a/src/web/hooks/__tests__/usePageFilter.jsx b/src/web/hooks/__tests__/usePageFilter.jsx index 7d4d008b57..3f817ec015 100644 --- a/src/web/hooks/__tests__/usePageFilter.jsx +++ b/src/web/hooks/__tests__/usePageFilter.jsx @@ -18,14 +18,14 @@ import usePageFilter from '../usePageFilter'; import {pageFilter} from 'web/store/pages/actions'; import {vi} from 'vitest'; -const mockUseHistory = testing.fn(); +const mockUseNavigate = testing.fn(); vi.mock('react-router-dom', async () => { const actual = await vi.importActual('react-router-dom'); return { ...actual, useLocation: () => ({pathname: '/'}), - useHistory: () => mockUseHistory(), + useNavigate: () => mockUseNavigate(), }; }); @@ -42,14 +42,12 @@ describe('usePageFilter tests', () => { }, }; - const {renderHook, store, history} = rendererWith({ + const {renderHook, store} = rendererWith({ gmp, store: true, router: true, }); - history.push({query: locationQuery}); - store.dispatch(loadingActions.success({rowsperpage: {value: '42'}})); store.dispatch( defaultFilterLoadingActions.success('somePage', defaultSettingFilter), @@ -76,7 +74,11 @@ describe('usePageFilter tests', () => { }, }; - const {renderHook, store} = rendererWith({store: true, gmp}); + const {renderHook, store} = rendererWith({ + store: true, + gmp, + router: true, + }); store.dispatch(loadingActions.success({rowsperpage: {value: '42'}})); store.dispatch( diff --git a/src/web/hooks/usePageFilter.js b/src/web/hooks/usePageFilter.js index e7b9f472b1..f38fd2d689 100644 --- a/src/web/hooks/usePageFilter.js +++ b/src/web/hooks/usePageFilter.js @@ -5,7 +5,7 @@ import {useCallback, useEffect, useState} from 'react'; -import {useLocation, useHistory} from 'react-router-dom'; +import {useSearchParams} from 'react-router-dom'; import {useDispatch} from 'react-redux'; @@ -52,6 +52,7 @@ const useDefaultFilter = pageName => * still loading, function to change the filter, function to remove the * filter and function to reset the filter */ + const usePageFilter = ( pageName, gmpName, @@ -62,8 +63,7 @@ const usePageFilter = ( ) => { const gmp = useGmp(); const dispatch = useDispatch(); - const location = useLocation(); - const history = useHistory(); + const [searchParams, setSearchParams] = useSearchParams(); const [defaultSettingFilter, defaultSettingsFilterError] = useDefaultFilter(gmpName); @@ -80,9 +80,9 @@ const usePageFilter = ( // use null as value for not set at all let returnedFilter; - // only use location directly if locationQueryFilterString is undefined + // only use searchParams directly if locationQueryFilterString is undefined const locationQueryFilterString = - initialLocationQueryFilterString || location?.query?.filter; + initialLocationQueryFilterString || searchParams.get('filter'); const [locationQueryFilter, setLocationQueryFilter] = useState( hasValue(locationQueryFilterString) @@ -163,15 +163,13 @@ const usePageFilter = ( }, [changeFilter]); const resetFilter = useCallback(() => { - const query = {...location.query}; - - // remove filter param from url - delete query.filter; + const query = new URLSearchParams(searchParams); + query.delete('filter'); - history.push({pathname: location.pathname, query}); + setSearchParams(query); changeFilter(); - }, [changeFilter, history, location]); + }, [changeFilter, setSearchParams, searchParams]); return [ returnedFilter, diff --git a/src/web/pages/audits/__tests__/__snapshots__/details.jsx.snap b/src/web/pages/audits/__tests__/__snapshots__/details.jsx.snap index 7c56606273..8c732ad812 100644 --- a/src/web/pages/audits/__tests__/__snapshots__/details.jsx.snap +++ b/src/web/pages/audits/__tests__/__snapshots__/details.jsx.snap @@ -59,35 +59,48 @@ exports[`Audit Details tests > should render full audit details 1`] = ` align-items: center; } -.c2 { +.c5 { + margin-left: -5px; +} + +.c5>* { display: -webkit-inline-box; display: -webkit-inline-flex; display: -ms-inline-flexbox; display: inline-flex; } -.c5 { - margin-left: -5px; +.c5>* { + margin-left: 5px; } -.c5>* { +.c4 { display: -webkit-inline-box; display: -webkit-inline-flex; display: -ms-inline-flexbox; display: inline-flex; } -.c5>* { +.c6>*:not(:last-child)::after { + content: '•'; margin-left: 5px; } -.c4 { +.c2 { display: -webkit-inline-box; display: -webkit-inline-flex; display: -ms-inline-flexbox; display: inline-flex; } +.c9 { + width: 10%; +} + +.c10 { + width: 90%; +} + .c7 { border: 0; border-spacing: 0px; @@ -97,11 +110,6 @@ exports[`Audit Details tests > should render full audit details 1`] = ` width: 100%; } -.c6>*:not(:last-child)::after { - content: '•'; - margin-left: 5px; -} - .c8 td { padding: 4px 4px 4px 0; } @@ -110,14 +118,6 @@ exports[`Audit Details tests > should render full audit details 1`] = ` padding-right: 5px; } -.c9 { - width: 10%; -} - -.c10 { - width: 90%; -} - @media print { .c7 { border-collapse: collapse; diff --git a/src/web/pages/audits/component.jsx b/src/web/pages/audits/component.jsx index a3deda1916..810a831ec6 100644 --- a/src/web/pages/audits/component.jsx +++ b/src/web/pages/audits/component.jsx @@ -19,7 +19,7 @@ import {isDefined} from 'gmp/utils/identity'; import {hasId} from 'gmp/utils/id'; import withDownload from 'web/components/form/withDownload'; -import {withRouter} from 'react-router-dom'; +import {withRouter} from 'web/utils/withRouter'; import { OPENVAS_DEFAULT_SCANNER_ID, @@ -588,7 +588,7 @@ AuditComponent.propTypes = { onStopped: PropTypes.func, }; -const mapStateToProps = (rootState, {match}) => { +const mapStateToProps = rootState => { const alertSel = alertSelector(rootState); const userDefaults = getUserSettingsDefaults(rootState); const policiesSel = policiesSelector(rootState); diff --git a/src/web/pages/extras/__tests__/cvsscalculatorpage.jsx b/src/web/pages/extras/__tests__/cvsscalculatorpage.jsx index 78092c03c9..6f9e5b325f 100644 --- a/src/web/pages/extras/__tests__/cvsscalculatorpage.jsx +++ b/src/web/pages/extras/__tests__/cvsscalculatorpage.jsx @@ -3,8 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - -import {describe, test, expect, testing} from '@gsa/testing'; +import {describe, test, expect, testing, beforeEach} from '@gsa/testing'; import {fireEvent, rendererWith, waitFor, wait} from 'web/utils/testing'; @@ -37,10 +36,14 @@ const location = { }; describe('CvssCalculator page tests', () => { + beforeEach(() => { + window.history.pushState({}, 'Test Title', '/'); + }); test('Should render with default values', () => { const {render} = rendererWith({ gmp, store: true, + router: true, }); const {element, getAllByTestId} = render(); @@ -75,14 +78,19 @@ describe('CvssCalculator page tests', () => { }); test('Should render userVector from url', async () => { + window.history.pushState( + {}, + 'Test Title', + `?cvssVector=AV:N/AC:L/Au:N/C:P/I:P/A:P`, + ); + const {render} = rendererWith({ gmp, store: true, + router: true, }); - const {element, getAllByTestId} = render( - , - ); + const {element, getAllByTestId} = render(); await wait(); @@ -117,6 +125,7 @@ describe('CvssCalculator page tests', () => { const {render} = rendererWith({ gmp, store: true, + router: true, }); const {element, getAllByTestId} = render( @@ -164,14 +173,19 @@ describe('CvssCalculator page tests', () => { }); test('Changing displayed select values should change userVector', async () => { + window.history.pushState( + {}, + 'Test Title', + `?cvssVector=AV:N/AC:L/Au:N/C:P/I:P/A:P`, + ); + const {render} = rendererWith({ gmp, store: true, + router: true, }); - const {element, getAllByTestId} = render( - , - ); + const {element, getAllByTestId} = render(); await wait(); diff --git a/src/web/pages/extras/cvssV4/CvssV4Calculator.jsx b/src/web/pages/extras/cvssV4/CvssV4Calculator.jsx index c75584b8f3..ceea5aa7c1 100644 --- a/src/web/pages/extras/cvssV4/CvssV4Calculator.jsx +++ b/src/web/pages/extras/cvssV4/CvssV4Calculator.jsx @@ -24,6 +24,7 @@ import SeverityBar from 'web/components/bar/severitybar'; import styled from 'styled-components'; import TextField from 'web/components/form/textfield'; import MetricsGroups from 'web/pages/extras/cvssV4/MetricsGroups'; +import {useSearchParams} from 'react-router-dom'; const StyledTextField = styled(TextField)` width: 180px; @@ -31,7 +32,9 @@ const StyledTextField = styled(TextField)` const cvssV4Prefix = 'CVSS:4.0/'; -const CvssV4Calculator = ({location}) => { +const CvssV4Calculator = () => { + const [searchParams] = useSearchParams(); + const initialState = useMemo(() => { return expectedMetricOptionsOrdered.reduce((obj, item) => { obj[item[0]] = item[1]; @@ -50,13 +53,14 @@ const CvssV4Calculator = ({location}) => { const cvssVector = `${cvssV4Prefix}${removeUnusedMetrics(selectedOptions)}`; useEffect(() => { - if (location?.query?.cvssVector?.includes(cvssV4Prefix)) { - const newOptions = processVector(location.query.cvssVector); + const cvssVectorParam = searchParams.get('cvssVector'); + if (cvssVectorParam?.includes(cvssV4Prefix)) { + const newOptions = processVector(cvssVectorParam); setSelectedOptions({...initialState, ...newOptions}); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [location]); + }, [searchParams]); const handleInputCVSSVectorChange = value => { setInputCVSSVector(value); diff --git a/src/web/pages/extras/cvssV4/__tests__/cvssV4Calculator.jsx b/src/web/pages/extras/cvssV4/__tests__/cvssV4Calculator.jsx index ebc129c008..6e9f124c90 100644 --- a/src/web/pages/extras/cvssV4/__tests__/cvssV4Calculator.jsx +++ b/src/web/pages/extras/cvssV4/__tests__/cvssV4Calculator.jsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import {describe, test, expect} from '@gsa/testing'; +import {describe, test, expect, beforeEach} from '@gsa/testing'; import {fireEvent, rendererWith, wait} from 'web/utils/testing'; import CvssV4Calculator from 'web/pages/extras/cvssV4/CvssV4Calculator'; @@ -12,22 +12,18 @@ const gmp = {}; const baseCVSSVector = 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N'; -const location = { - query: { - cvssVector: baseCVSSVector, - }, -}; - describe('CvssV4Calculator page tests', () => { + beforeEach(() => { + window.history.pushState({}, 'Test Title', '/'); + }); test('Should render with default values', async () => { const {render} = rendererWith({ gmp, store: true, + router: true, }); - const {getByText, within} = render( - , - ); + const {getByText, within} = render(); const cvssVectorEl = getByText('CVSS Base Vector'); const spanElement = within(cvssVectorEl.parentElement).getByText( @@ -38,24 +34,22 @@ describe('CvssV4Calculator page tests', () => { }); test('Should render userVector from url', async () => { - const locationModified = { - query: { - cvssVector: - 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N/CR:M/MSC:H', - }, - }; + const cvssVector = + 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N/CR:M/MSC:H'; + + window.history.pushState({}, 'Test Title', `?cvssVector=${cvssVector}`); + const {render} = rendererWith({ gmp, store: true, + router: true, }); - const {getByText, within} = render( - , - ); + const {getByText, within} = render(); const cvssVectorEl = getByText('CVSS Base Vector'); const spanElement = within(cvssVectorEl.parentElement).getByText( - locationModified.query.cvssVector, + cvssVector, ); expect(spanElement).toBeVisible(); @@ -65,10 +59,11 @@ describe('CvssV4Calculator page tests', () => { const {render} = rendererWith({ gmp, store: true, + router: true, }); const {getByText, within, element, getAllByTestId} = render( - , + , ); await wait(); @@ -102,10 +97,11 @@ describe('CvssV4Calculator page tests', () => { const {render} = rendererWith({ gmp, store: true, + router: true, }); const {element, getAllByTestId, getByText, within} = render( - , + , ); await wait(); diff --git a/src/web/pages/extras/cvsscalculatorpage.jsx b/src/web/pages/extras/cvsscalculatorpage.jsx index a7e1b16102..a5635ca675 100644 --- a/src/web/pages/extras/cvsscalculatorpage.jsx +++ b/src/web/pages/extras/cvsscalculatorpage.jsx @@ -4,7 +4,7 @@ */ import React, {useState, useEffect} from 'react'; -import PropTypes from 'prop-types'; +import {useSearchParams} from 'react-router-dom'; import styled from 'styled-components'; @@ -54,6 +54,7 @@ const ToolBarIcons = () => ( const CvssV2Calculator = props => { const [, renewSession] = useUserSessionTimeout(); + const [searchParams] = useSearchParams(); const [state, setState] = useState({ accessVector: 'Local', @@ -68,18 +69,15 @@ const CvssV2Calculator = props => { }); useEffect(() => { - const {location} = props; + const cvssVector = searchParams.get('cvssVector'); if ( - isDefined(location) && - isDefined(location.query) && - isDefined(location.query.cvssVector) + cvssVector && + !cvssVector.includes('CVSS:3') && + !cvssVector.includes('CVSS:4') ) { - const {cvssVector} = location.query; - if (!cvssVector.includes('CVSS:3')) { - setState(vals => ({...vals, cvssVector, userVector: cvssVector})); - handleVectorChange(); - } + setState(vals => ({...vals, cvssVector, userVector: cvssVector})); + handleVectorChange(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -351,6 +349,7 @@ const CvssV2Calculator = props => { const CvssV3Calculator = props => { const [, renewSession] = useUserSessionTimeout(); + const [searchParams] = useSearchParams(); const [state, setState] = useState({ attackVector: 'Network', @@ -367,18 +366,11 @@ const CvssV3Calculator = props => { }); useEffect(() => { - const {location} = props; + const cvssVector = searchParams.get('cvssVector'); - if ( - isDefined(location) && - isDefined(location.query) && - isDefined(location.query.cvssVector) - ) { - const {cvssVector} = location.query; - if (cvssVector.includes('CVSS:3')) { - setState(vals => ({...vals, cvssVector, userVector: cvssVector})); - handleVectorChange(); - } + if (cvssVector?.includes('CVSS:3')) { + setState(vals => ({...vals, cvssVector, userVector: cvssVector})); + handleVectorChange(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -706,14 +698,11 @@ const CvssCalculator = props => ( - + ); -CvssCalculator.propTypes = { - location: PropTypes.object.isRequired, -}; export default CvssCalculator; // vim: set ts=2 sw=2 tw=80: diff --git a/src/web/pages/hosts/dashboard/topologydisplay.jsx b/src/web/pages/hosts/dashboard/topologydisplay.jsx index f2c0e3dad1..f1835673da 100644 --- a/src/web/pages/hosts/dashboard/topologydisplay.jsx +++ b/src/web/pages/hosts/dashboard/topologydisplay.jsx @@ -3,10 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; -import {withRouter} from 'react-router-dom'; +import {withRouter} from 'web/utils/withRouter'; import {_, _l} from 'gmp/locale/lang'; @@ -115,9 +114,9 @@ export class HostsTopologyDisplay extends React.Component { } handleDataClick(data) { - const {history} = this.props; + const {navigate} = this.props; - history.push(`/host/${data.id}`); + navigate(`/host/${data.id}`); } render() { @@ -151,7 +150,7 @@ export class HostsTopologyDisplay extends React.Component { HostsTopologyDisplay.propTypes = { filter: PropTypes.filter, - history: PropTypes.object.isRequired, + navigate: PropTypes.object.isRequired, }; const DISPLAY_ID = 'host-by-topology'; diff --git a/src/web/pages/hosts/dashboard/vulnscoredisplay.jsx b/src/web/pages/hosts/dashboard/vulnscoredisplay.jsx index 370944562c..ebe54913b9 100644 --- a/src/web/pages/hosts/dashboard/vulnscoredisplay.jsx +++ b/src/web/pages/hosts/dashboard/vulnscoredisplay.jsx @@ -5,7 +5,7 @@ import React from 'react'; -import {withRouter} from 'react-router-dom'; +import {withRouter} from 'web/utils/withRouter'; import styled from 'styled-components'; @@ -86,9 +86,9 @@ export class HostsVulnScoreDisplay extends React.Component { } handleDataClick(data) { - const {history} = this.props; + const {navigate} = this.props; - history.push(`/host/${data.id}`); + navigate(`/host/${data.id}`); } render() { @@ -125,7 +125,7 @@ export class HostsVulnScoreDisplay extends React.Component { HostsVulnScoreDisplay.propTypes = { filter: PropTypes.filter, - history: PropTypes.object.isRequired, + navigate: PropTypes.func.isRequired, }; HostsVulnScoreDisplay = compose( diff --git a/src/web/pages/login/__tests__/loginpage.jsx b/src/web/pages/login/__tests__/loginpage.jsx index 757563f632..6871cdfbe0 100644 --- a/src/web/pages/login/__tests__/loginpage.jsx +++ b/src/web/pages/login/__tests__/loginpage.jsx @@ -155,10 +155,10 @@ describe('LoginPage tests', () => { clearToken, settings: {}, }; - const {render, history} = rendererWith({gmp, router: true, store: true}); + const {render} = rendererWith({gmp, router: true, store: true}); render(); - expect(history.location.pathname).toMatch(/^\/$/); + expect(window.location.pathname).toMatch(/^\/dashboard$/); }); }); diff --git a/src/web/pages/login/loginpage.jsx b/src/web/pages/login/loginpage.jsx index 1745152e28..67a6bb77ec 100644 --- a/src/web/pages/login/loginpage.jsx +++ b/src/web/pages/login/loginpage.jsx @@ -3,12 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import {connect} from 'react-redux'; -import {withRouter} from 'react-router-dom'; +import {withRouter} from 'web/utils/withRouter'; import styled from 'styled-components'; @@ -102,7 +101,7 @@ class LoginPage extends React.Component { data => { const {locale, timezone, sessionTimeout} = data; - const {location, history} = this.props; + const {location, navigate} = this.props; this.props.setTimezone(timezone); this.props.setLocale(locale); @@ -117,9 +116,9 @@ class LoginPage extends React.Component { location.state.next && location.state.next !== location.pathname ) { - history.replace(location.state.next); + navigate(location.state.next, {replace: true}); } else { - history.replace('/'); + navigate('/dashboard', {replace: true}); } }, rej => { @@ -130,11 +129,11 @@ class LoginPage extends React.Component { } componentDidMount() { - const {history, isLoggedIn = false} = this.props; // eslint-disable-line no-shadow + const {navigate, isLoggedIn = false} = this.props; // eslint-disable-line no-shadow // redirect user to main page if he is already logged in if (isLoggedIn) { - history.replace('/'); + navigate('/dashboard', {replace: true}); } } @@ -182,7 +181,7 @@ class LoginPage extends React.Component { LoginPage.propTypes = { gmp: PropTypes.gmp.isRequired, - history: PropTypes.object.isRequired, + navigate: PropTypes.func.isRequired, isLoggedIn: PropTypes.bool, location: PropTypes.object.isRequired, setIsLoggedIn: PropTypes.func.isRequired, diff --git a/src/web/pages/omp.jsx b/src/web/pages/omp.jsx index 485264a5bf..6cbce3769b 100644 --- a/src/web/pages/omp.jsx +++ b/src/web/pages/omp.jsx @@ -3,12 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - -import React from 'react'; - -import {withRouter} from 'react-router-dom'; - -import PropTypes from 'web/utils/proptypes'; +import {useNavigate, useSearchParams} from 'react-router-dom'; +import {useEffect} from 'react'; /** * Component to redirect old secinfo urls like @@ -17,13 +13,18 @@ import PropTypes from 'web/utils/proptypes'; * * to the current replacement pages */ -class LegacyOmpPage extends React.Component { - componentDidMount() { - const {location, history} = this.props; - const {cmd, info_type, info_id = ''} = location.query; + +const OmpComponent = () => { + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + + useEffect(() => { + const cmd = searchParams.get('cmd'); + const info_type = searchParams.get('info_type'); + const info_id = searchParams.get('info_id') || ''; if (cmd !== 'get_info') { - history.replace('/notfound'); + navigate('/notfound', {replace: true}); return; } @@ -31,33 +32,27 @@ class LegacyOmpPage extends React.Component { switch (info_type) { case 'nvt': - history.replace('/nvt/' + id); + navigate('/nvt/' + id, {replace: true}); break; case 'cve': - history.replace('/cve/' + id); + navigate('/cve/' + id, {replace: true}); break; case 'cpe': - history.replace('/cpe/' + id); + navigate('/cpe/' + id, {replace: true}); break; case 'cert_bund_adv': - history.replace('/certbund/' + id); + navigate('/certbund/' + id, {replace: true}); break; case 'dfn_cert_adv': - history.replace('/dfncert/' + id); + navigate('/dfncert/' + id, {replace: true}); break; default: - history.replace('/notfound'); + navigate('/notfound', {replace: true}); break; } - } - - render() { - return null; - } -} + }, [navigate, searchParams]); -LegacyOmpPage.propTypes = { - history: PropTypes.object.isRequired, + return null; }; -export default withRouter(LegacyOmpPage); +export default OmpComponent; diff --git a/src/web/pages/operatingsystems/dashboard/vulnscoredisplay.jsx b/src/web/pages/operatingsystems/dashboard/vulnscoredisplay.jsx index 729a3e8a54..96c4409643 100644 --- a/src/web/pages/operatingsystems/dashboard/vulnscoredisplay.jsx +++ b/src/web/pages/operatingsystems/dashboard/vulnscoredisplay.jsx @@ -5,7 +5,7 @@ import React from 'react'; -import {withRouter} from 'react-router-dom'; +import {withRouter} from 'web/utils/withRouter'; import styled from 'styled-components'; @@ -88,9 +88,9 @@ export class OsVulnScoreDisplay extends React.Component { } handleDataClick(data) { - const {history} = this.props; + const {navigate} = this.props; - history.push(`/operatingsystem/${data.id}`); + navigate(`/operatingsystem/${data.id}`); } render() { @@ -127,7 +127,7 @@ export class OsVulnScoreDisplay extends React.Component { OsVulnScoreDisplay.propTypes = { filter: PropTypes.filter, - history: PropTypes.object.isRequired, + navigate: PropTypes.func.isRequired, }; OsVulnScoreDisplay.displayId = 'os-by-most-vulnerable'; diff --git a/src/web/pages/page.jsx b/src/web/pages/page.jsx index db27412f0e..d9a15ba645 100644 --- a/src/web/pages/page.jsx +++ b/src/web/pages/page.jsx @@ -3,10 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; -import {withRouter} from 'react-router-dom'; +import {withRouter} from 'web/utils/withRouter'; import styled from 'styled-components'; diff --git a/src/web/pages/performance/performancepage.jsx b/src/web/pages/performance/performancepage.jsx index 7c8edb172e..e0d5e57adf 100644 --- a/src/web/pages/performance/performancepage.jsx +++ b/src/web/pages/performance/performancepage.jsx @@ -52,6 +52,7 @@ import {renderSelectItems} from 'web/utils/render'; import {styledExcludeProps} from 'web/utils/styledConfig'; import StartEndTimeSelection from './startendtimeselection'; +import {withRouter} from 'web/utils/withRouter'; const DURATION_HOUR = 60 * 60; const DURATION_DAY = DURATION_HOUR * 24; @@ -174,8 +175,10 @@ class PerformancePage extends React.Component { } componentDidMount() { - const {start, end, scanner} = this.props.location.query; - const {gmp, timezone} = this.props; + const {gmp, timezone, searchParams} = this.props; + const start = searchParams.get('start'); + const end = searchParams.get('end'); + const scanner = searchParams.get('scanner'); gmp.performance.get().then(response => { this.setState({reports: response.data}); @@ -353,6 +356,7 @@ PerformancePage.propTypes = { scanners: PropTypes.arrayOf(PropTypes.model), timezone: PropTypes.string.isRequired, onInteraction: PropTypes.func.isRequired, + searchParams: PropTypes.object, }; const SENSOR_SCANNER_FILTER = Filter.fromString( @@ -376,6 +380,7 @@ const mapStateToProps = rootState => { export default compose( withGmp, + withRouter, connect(mapStateToProps, mapDispatchToProps), )(PerformancePage); diff --git a/src/web/pages/policies/__tests__/__snapshots__/details.jsx.snap b/src/web/pages/policies/__tests__/__snapshots__/details.jsx.snap index 5b8077164c..384eef093c 100644 --- a/src/web/pages/policies/__tests__/__snapshots__/details.jsx.snap +++ b/src/web/pages/policies/__tests__/__snapshots__/details.jsx.snap @@ -81,13 +81,6 @@ exports[`Policy Details tests > should render full Details 1`] = ` align-items: center; } -.c10 { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - .c9 { margin-left: -5px; } @@ -110,6 +103,13 @@ exports[`Policy Details tests > should render full Details 1`] = ` display: inline-flex; } +.c10 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + .c1 { border: 0; border-spacing: 0px; diff --git a/src/web/pages/reportconfigs/__tests__/__snapshots__/component.jsx.snap b/src/web/pages/reportconfigs/__tests__/__snapshots__/component.jsx.snap index 46cc076154..8652502d54 100644 --- a/src/web/pages/reportconfigs/__tests__/__snapshots__/component.jsx.snap +++ b/src/web/pages/reportconfigs/__tests__/__snapshots__/component.jsx.snap @@ -382,6 +382,55 @@ exports[`Report Config Component tests > should open create dialog and call GMP padding-right: 10px; } +.c14 { + font-family: inherit; + font-size: inherit; + line-height: inherit; + display: block; + height: 22px; + color: #4C4C4C; + background-color: #fff; + background-image: none; + border: 1px solid #bfbfbf; + border-radius: 2px; + padding: 1px 8px; +} + +.c14:-webkit-autofill { + box-shadow: 0 0 0 1000px white inset; +} + +.c15 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: start; + justify-content: start; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.c16 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; +} + .c23 { background-color: transparent; border: none; @@ -470,15 +519,6 @@ exports[`Report Config Component tests > should open create dialog and call GMP cursor: pointer; } -.c16 { - color: #c12c30; - font-weight: bold; - font-size: 19px; - padding-bottom: 1px; - padding-left: 4px; - display: none; -} - .c21 { cursor: default; } @@ -490,46 +530,6 @@ exports[`Report Config Component tests > should open create dialog and call GMP display: flex; } -.c14 { - font-family: inherit; - font-size: inherit; - line-height: inherit; - display: block; - height: 22px; - color: #4C4C4C; - background-color: #fff; - background-image: none; - border: 1px solid #bfbfbf; - border-radius: 2px; - padding: 1px 8px; -} - -.c14:-webkit-autofill { - box-shadow: 0 0 0 1000px white inset; -} - -.c15 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-box-flex: 1; - -webkit-flex-grow: 1; - -ms-flex-positive: 1; - flex-grow: 1; - -webkit-box-pack: start; - -ms-flex-pack: start; - -webkit-justify-content: start; - justify-content: start; - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} -
    should render full Details 1`] = ` table-layout: auto; } -.c7 { - vertical-align: top; -} - .c2 td { padding: 4px 4px 4px 0; } @@ -86,6 +82,10 @@ exports[`Report Config Details tests > should render full Details 1`] = ` padding-right: 5px; } +.c7 { + vertical-align: top; +} + .c3 { width: 10%; } @@ -402,10 +402,6 @@ exports[`Report Config Details tests > should render orphaned config details 1`] table-layout: auto; } -.c6 { - vertical-align: top; -} - .c2 td { padding: 4px 4px 4px 0; } @@ -414,6 +410,10 @@ exports[`Report Config Details tests > should render orphaned config details 1`] padding-right: 5px; } +.c6 { + vertical-align: top; +} + .c3 { width: 10%; } diff --git a/src/web/pages/reports/auditdeltadetailspage.jsx b/src/web/pages/reports/auditdeltadetailspage.jsx index 2a65928c1f..09ead8258d 100644 --- a/src/web/pages/reports/auditdeltadetailspage.jsx +++ b/src/web/pages/reports/auditdeltadetailspage.jsx @@ -3,12 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React, {useEffect, useState} from 'react'; import {useDispatch, useSelector, shallowEqual} from 'react-redux'; -import {useRouteMatch} from 'react-router-dom'; +import {useParams} from 'react-router-dom'; import useTranslation from 'web/hooks/useTranslation'; @@ -17,7 +16,7 @@ import logger from 'gmp/log'; import Filter, { ALL_FILTER, RESET_FILTER, - RESULTS_FILTER_FILTER + RESULTS_FILTER_FILTER, } from 'gmp/models/filter'; import {isActive} from 'gmp/models/task'; @@ -114,12 +113,12 @@ const DeltaAuditReportDetails = props => { sortReverse: false, }, }); - + const [_] = useTranslation(); const gmp = useGmp(); const dispatch = useDispatch(); - const match = useRouteMatch(); - const {id: reportId, deltaid: deltaReportId} = match.params; + const params = useParams(); + const {id: reportId, deltaid: deltaReportId} = params; const reportFormatsSel = useSelector(reportFormatsSelector); const reportConfigsSel = useSelector(reportConfigsSelector); @@ -513,9 +512,9 @@ const load = const DeltaAuditReportDetailsWrapper = ({defaultFilter, ...props}) => { const gmp = useGmp(); const dispatch = useDispatch(); - const match = useRouteMatch(); + const params = useParams('/auditreport/delta/:id/:deltaid'); - const {id: reportId, deltaid: deltaReportId} = match.params; + const {id: reportId, deltaid: deltaReportId} = params; const deltaSel = useSelector(deltaAuditReportSelector, shallowEqual); const entity = deltaSel.getEntity(reportId, deltaReportId); const reportFilter = entity?.report?.filter; @@ -562,4 +561,4 @@ DeltaAuditReportDetailsWrapper.propTypes = { export default compose( withDialogNotification, withDownload, -)(DeltaAuditReportDetailsWrapper); \ No newline at end of file +)(DeltaAuditReportDetailsWrapper); diff --git a/src/web/pages/reports/auditdetailspage.jsx b/src/web/pages/reports/auditdetailspage.jsx index b0585d6ffa..73ff234257 100644 --- a/src/web/pages/reports/auditdetailspage.jsx +++ b/src/web/pages/reports/auditdetailspage.jsx @@ -3,12 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React, {useEffect, useState} from 'react'; import {useDispatch, useSelector, shallowEqual} from 'react-redux'; -import {useRouteMatch} from 'react-router-dom'; +import {useParams} from 'react-router-dom'; import useTranslation from 'web/hooks/useTranslation'; @@ -17,7 +16,7 @@ import logger from 'gmp/log'; import Filter, { ALL_FILTER, RESET_FILTER, - RESULTS_FILTER_FILTER + RESULTS_FILTER_FILTER, } from 'gmp/models/filter'; import {isActive} from 'gmp/models/task'; @@ -149,8 +148,8 @@ const ReportDetails = props => { const [_] = useTranslation(); const gmp = useGmp(); const dispatch = useDispatch(); - const match = useRouteMatch(); - const {id: reportId} = match.params; + const params = useParams('/audit/:id'); + const {id: reportId} = params; const pSelector = useSelector(getPage, shallowEqual); const pageFilter = pSelector?.getFilter(getReportPageName(reportId)); @@ -613,7 +612,7 @@ const load = // eslint-disable-next-line no-shadow dispatch, gmp, - match, + params, pageFilter, reportFilter, }) => @@ -637,16 +636,16 @@ const load = // use fallback filter filter = DEFAULT_FILTER; } - dispatch(setPageFilter(getReportPageName(match.params.id), filter)); + dispatch(setPageFilter(getReportPageName(params.id), filter)); return dispatch(loadAuditReportWithThreshold(gmp)(reportId, {filter})); }; const ReportDetailsWrapper = props => { const dispatch = useDispatch(); const gmp = useGmp(); - const match = useRouteMatch(); + const params = useParams(); - const {id: reportId} = match.params; + const {id: reportId} = params; const reportSel = useSelector(auditReportSelector, shallowEqual); const pSelector = useSelector(getPage, shallowEqual); @@ -667,7 +666,7 @@ const ReportDetailsWrapper = props => { ...props, dispatch, gmp, - match, + params, defaultFilter: filter, reportFilter, reportId, @@ -677,7 +676,7 @@ const ReportDetailsWrapper = props => { ...props, dispatch, gmp, - match, + params, defaultFilter: filter, reportFilter, reportId, @@ -702,4 +701,4 @@ const ReportDetailsWrapper = props => { export default compose( withDialogNotification, withDownload, -)(ReportDetailsWrapper); \ No newline at end of file +)(ReportDetailsWrapper); diff --git a/src/web/pages/reports/auditreportslistpage.jsx b/src/web/pages/reports/auditreportslistpage.jsx index b12d3739a0..57949e3490 100644 --- a/src/web/pages/reports/auditreportslistpage.jsx +++ b/src/web/pages/reports/auditreportslistpage.jsx @@ -3,10 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React, {useEffect, useState} from 'react'; -import {useHistory} from 'react-router-dom'; +import {useNavigate} from 'react-router-dom'; import useTranslation from 'web/hooks/useTranslation'; @@ -49,14 +48,14 @@ import AuditReportsDashboard, { const ToolBarIcons = () => { const [_] = useTranslation(); return ( - - - - ) + + + + ); }; const AuditReportsPage = ({ @@ -68,7 +67,7 @@ const AuditReportsPage = ({ }) => { const [selectedDeltaReport, setSelectedDeltaReport] = useState(); const [beforeSelectFilter, setBeforeSelectFilter] = useState(); - const history = useHistory(); + const navigate = useNavigate(); const [_] = useTranslation(); useEffect(() => { @@ -88,7 +87,7 @@ const AuditReportsPage = ({ if (isDefined(selectedDeltaReport)) { onFilterChanged(beforeSelectFilter); - history.push( + navigate( '/auditreport/delta/' + selectedDeltaReport.id + '/' + report.id, ); } else { @@ -144,7 +143,7 @@ const AuditReportsPage = ({ AuditReportsPage.propTypes = { filter: PropTypes.filter, - history: PropTypes.object.isRequired, + navigate: PropTypes.func.isRequired, onChanged: PropTypes.func.isRequired, onDelete: PropTypes.func.isRequired, onError: PropTypes.func.isRequired, @@ -162,8 +161,8 @@ const FALLBACK_AUDIT_REPORT_LIST_FILTER = Filter.fromString( ); export default withEntitiesContainer('auditreport', { - fallbackFilter: FALLBACK_AUDIT_REPORT_LIST_FILTER, - entitiesSelector, - loadEntities, - reloadInterval: reportsReloadInterval, - })(AuditReportsPage); \ No newline at end of file + fallbackFilter: FALLBACK_AUDIT_REPORT_LIST_FILTER, + entitiesSelector, + loadEntities, + reloadInterval: reportsReloadInterval, +})(AuditReportsPage); diff --git a/src/web/pages/reports/deltadetailspage.jsx b/src/web/pages/reports/deltadetailspage.jsx index 6a854d3d34..4683940529 100644 --- a/src/web/pages/reports/deltadetailspage.jsx +++ b/src/web/pages/reports/deltadetailspage.jsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import {connect} from 'react-redux'; @@ -15,7 +14,8 @@ import logger from 'gmp/log'; import Filter, { ALL_FILTER, RESET_FILTER, - RESULTS_FILTER_FILTER} from 'gmp/models/filter'; + RESULTS_FILTER_FILTER, +} from 'gmp/models/filter'; import {isActive} from 'gmp/models/task'; import {first} from 'gmp/utils/array'; @@ -76,6 +76,7 @@ import TargetComponent from '../targets/component'; import Page from './deltadetailscontent'; import FilterDialog from './detailsfilterdialog'; +import {withRouter} from 'web/utils/withRouter'; const log = logger.getLogger('web.pages.report.deltadetailspage'); @@ -99,7 +100,6 @@ const getFilter = (entity = {}) => { class DeltaReportDetails extends React.Component { constructor(...args) { super(...args); - this.state = { activeTab: 0, showFilterDialog: false, @@ -318,7 +318,7 @@ class DeltaReportDetails extends React.Component { includeOverrides, reportConfigId, reportFormatId, - storeAsDefault + storeAsDefault, } = state; const newFilter = reportFilter.copy(); @@ -564,7 +564,6 @@ DeltaReportDetails.propTypes = { loadSettings: PropTypes.func.isRequired, loadTarget: PropTypes.func.isRequired, location: PropTypes.object.isRequired, - match: PropTypes.object.isRequired, reload: PropTypes.func.isRequired, reportComposerDefaults: PropTypes.object, reportExportFileName: PropTypes.string, @@ -605,8 +604,9 @@ const mapDispatchToProps = (dispatch, {gmp}) => { }; }; -const mapStateToProps = (rootState, {match}) => { - const {id, deltaid} = match.params; +const mapStateToProps = (rootState, {params}) => { + const {id, deltaid} = params; + const filterSel = filterSelector(rootState); const deltaSel = deltaReportSelector(rootState); const reportFormatsSel = reportFormatsSelector(rootState); @@ -708,6 +708,7 @@ export default compose( withGmp, withDialogNotification, withDownload, + withRouter, connect(mapStateToProps, mapDispatchToProps), )(DeltaReportDetailsWrapper); diff --git a/src/web/pages/reports/details/toolbaricons.jsx b/src/web/pages/reports/details/toolbaricons.jsx index 7a61def7f5..76554d924c 100644 --- a/src/web/pages/reports/details/toolbaricons.jsx +++ b/src/web/pages/reports/details/toolbaricons.jsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import _ from 'gmp/locale'; @@ -120,7 +119,7 @@ const ToolBarIcons = ({ end: isDefined(report.scan_end) ? report.scan_end.toISOString() : undefined, - scanner: isDefined(report.slave) ? report.slave.id : undefined, + ...(isDefined(report.slave) && {scanner: report.slave.id}), }} > diff --git a/src/web/pages/reports/detailspage.jsx b/src/web/pages/reports/detailspage.jsx index 63a76527f3..9f56cea385 100644 --- a/src/web/pages/reports/detailspage.jsx +++ b/src/web/pages/reports/detailspage.jsx @@ -3,10 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import {connect} from 'react-redux'; +import {withRouter} from 'web/utils/withRouter'; import _ from 'gmp/locale'; @@ -685,7 +685,6 @@ ReportDetails.propTypes = { loadSettings: PropTypes.func.isRequired, loadTarget: PropTypes.func.isRequired, location: PropTypes.object.isRequired, - match: PropTypes.object.isRequired, pageFilter: PropTypes.filter, reload: PropTypes.func.isRequired, reportComposerDefaults: PropTypes.object, @@ -781,7 +780,7 @@ ReportDetailsWrapper.propTypes = { const getReportPageName = id => `report-${id}`; -const mapDispatchToProps = (dispatch, {gmp, match}) => ({ +const mapDispatchToProps = (dispatch, {gmp, params}) => ({ onInteraction: () => dispatch(renewSessionTimeout(gmp)()), loadFilters: () => dispatch(loadFilters(gmp)(RESULTS_FILTER_FILTER)), loadSettings: () => dispatch(loadUserSettingDefaults(gmp)()), @@ -794,12 +793,11 @@ const mapDispatchToProps = (dispatch, {gmp, match}) => ({ loadReportComposerDefaults: () => dispatch(loadReportComposerDefaults(gmp)()), saveReportComposerDefaults: reportComposerDefaults => dispatch(saveReportComposerDefaults(gmp)(reportComposerDefaults)), - updateFilter: f => - dispatch(setPageFilter(getReportPageName(match.params.id), f)), + updateFilter: f => dispatch(setPageFilter(getReportPageName(params.id), f)), }); -const mapStateToProps = (rootState, {match}) => { - const {id} = match.params; +const mapStateToProps = (rootState, {params}) => { + const {id} = params; const filterSel = filterSelector(rootState); const reportSel = reportSelector(rootState); const reportConfigsSel = reportConfigsSelector(rootState); @@ -847,6 +845,7 @@ export default compose( withGmp, withDialogNotification, withDownload, + withRouter, connect(mapStateToProps, mapDispatchToProps), )(ReportDetailsWrapper); diff --git a/src/web/pages/reports/listpage.jsx b/src/web/pages/reports/listpage.jsx index 946f2a0966..24ef66be9a 100644 --- a/src/web/pages/reports/listpage.jsx +++ b/src/web/pages/reports/listpage.jsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import {connect} from 'react-redux'; @@ -159,11 +158,13 @@ class Page extends React.Component { const {selectedDeltaReport, beforeSelectFilter} = this.state; if (isDefined(selectedDeltaReport)) { - const {history} = this.props; + const {navigate} = this.props; onFilterChanged(beforeSelectFilter); - history.push('/report/delta/' + selectedDeltaReport.id + '/' + report.id); + navigate('/report/delta/' + selectedDeltaReport.id + '/' + report.id, { + replace: true, + }); } else { const {filter = new Filter()} = this.props; @@ -192,11 +193,8 @@ class Page extends React.Component { render() { const {filter, onFilterChanged, onInteraction, tasks} = this.props; - const { - containerTaskDialogVisible, - importDialogVisible, - task_id, - } = this.state; + const {containerTaskDialogVisible, importDialogVisible, task_id} = + this.state; return ( @@ -252,7 +250,7 @@ class Page extends React.Component { Page.propTypes = { filter: PropTypes.filter, gmp: PropTypes.gmp.isRequired, - history: PropTypes.object.isRequired, + navigate: PropTypes.func.isRequired, loadTasks: PropTypes.func.isRequired, tasks: PropTypes.arrayOf(PropTypes.model), onChanged: PropTypes.func.isRequired, @@ -284,10 +282,7 @@ const FALLBACK_REPORT_LIST_FILTER = Filter.fromString( export default compose( withGmp, - connect( - mapStateToProps, - mapDispatchToProps, - ), + connect(mapStateToProps, mapDispatchToProps), withEntitiesContainer('report', { fallbackFilter: FALLBACK_REPORT_LIST_FILTER, entitiesSelector, diff --git a/src/web/pages/scanconfigs/__tests__/__snapshots__/details.jsx.snap b/src/web/pages/scanconfigs/__tests__/__snapshots__/details.jsx.snap index 0960b0fddc..339788e5fc 100644 --- a/src/web/pages/scanconfigs/__tests__/__snapshots__/details.jsx.snap +++ b/src/web/pages/scanconfigs/__tests__/__snapshots__/details.jsx.snap @@ -81,13 +81,6 @@ exports[`Scan Config Details tests > should render full Details 1`] = ` align-items: center; } -.c10 { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - .c9 { margin-left: -5px; } @@ -110,6 +103,13 @@ exports[`Scan Config Details tests > should render full Details 1`] = ` display: inline-flex; } +.c10 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + .c1 { border: 0; border-spacing: 0px; diff --git a/src/web/pages/scanconfigs/__tests__/__snapshots__/editdialog.jsx.snap b/src/web/pages/scanconfigs/__tests__/__snapshots__/editdialog.jsx.snap index fa5712f392..f8ce81efb6 100644 --- a/src/web/pages/scanconfigs/__tests__/__snapshots__/editdialog.jsx.snap +++ b/src/web/pages/scanconfigs/__tests__/__snapshots__/editdialog.jsx.snap @@ -281,59 +281,6 @@ exports[`EditScanConfigDialog component tests > should render dialog 1`] = ` width: inherit; } -.c25 { - overflow: hidden; - -webkit-transition: 0.4s; - transition: 0.4s; -} - -.c44 { - overflow: hidden; - -webkit-transition: 0.4s; - transition: 0.4s; - display: none; - height: 0; -} - -.c18 { - margin: 10px 0px; - padding-bottom: 1px; - border-bottom: 2px solid #e5e5e5; - position: relative; -} - -.c19 { - margin: 0 0 1px 0; -} - -.c20 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-box-pack: start; - -ms-flex-pack: start; - -webkit-justify-content: flex-start; - justify-content: flex-start; - -webkit-align-items: stretch; - -webkit-box-align: stretch; - -ms-flex-align: stretch; - align-items: stretch; -} - -.c22 { - word-break: break-all; - min-width: 100px; -} - -.c23 { - margin-left: 3px; - margin-top: -2px; -} - .c1 { position: relative; display: -webkit-box; @@ -601,90 +548,6 @@ exports[`EditScanConfigDialog component tests > should render dialog 1`] = ` padding-right: 10px; } -.c16 { - color: #c12c30; - font-weight: bold; - font-size: 19px; - padding-bottom: 1px; - padding-left: 4px; - display: none; -} - -.c27 { - border: 0; - border-spacing: 0px; - font-size: 12px; - text-align: left; - table-layout: auto; - width: 100%; -} - -.c46 { - border: 0; - border-spacing: 0px; - font-size: 12px; - text-align: left; - table-layout: fixed; - width: 100%; -} - -.c28 th, -.c28 td { - padding: 4px 10px; - border-bottom: 1px solid #e5e5e5; -} - -.c28 tfoot tr { - background: #fff; -} - -.c28 tfoot tr td { - border-bottom: 1px solid #e5e5e5; -} - -.c29 a { - -webkit-text-decoration: none; - text-decoration: none; - color: #000; -} - -.c29 a:hover { - -webkit-text-decoration: underline; - text-decoration: underline; - color: #000; -} - -.c30 { - background-color: #fff; - color: #000; - border-top: 1px solid #e5e5e5; - font-weight: bold; -} - -.c31 { - background-color: #fff; - color: #000; - border-top: 1px solid #e5e5e5; - font-weight: bold; - width: 9em; -} - -.c47 { - background-color: #fff; - color: #000; - border-top: 1px solid #e5e5e5; - font-weight: bold; - width: 30%; -} - -.c48 { - background-color: #fff; - color: #000; - border-top: 1px solid #e5e5e5; - font-weight: bold; - width: 10%; -} - .c14 { font-family: inherit; font-size: inherit; @@ -743,6 +606,15 @@ exports[`EditScanConfigDialog component tests > should render dialog 1`] = ` align-items: center; } +.c16 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; +} + .c37 { display: -webkit-inline-box; display: -webkit-inline-flex; @@ -806,6 +678,134 @@ exports[`EditScanConfigDialog component tests > should render dialog 1`] = ` align-items: center; } +.c25 { + overflow: hidden; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +.c44 { + overflow: hidden; + -webkit-transition: 0.4s; + transition: 0.4s; + display: none; + height: 0; +} + +.c18 { + margin: 10px 0px; + padding-bottom: 1px; + border-bottom: 2px solid #e5e5e5; + position: relative; +} + +.c19 { + margin: 0 0 1px 0; +} + +.c20 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: start; + -ms-flex-pack: start; + -webkit-justify-content: flex-start; + justify-content: flex-start; + -webkit-align-items: stretch; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.c22 { + word-break: break-all; + min-width: 100px; +} + +.c23 { + margin-left: 3px; + margin-top: -2px; +} + +.c27 { + border: 0; + border-spacing: 0px; + font-size: 12px; + text-align: left; + table-layout: auto; + width: 100%; +} + +.c46 { + border: 0; + border-spacing: 0px; + font-size: 12px; + text-align: left; + table-layout: fixed; + width: 100%; +} + +.c28 th, +.c28 td { + padding: 4px 10px; + border-bottom: 1px solid #e5e5e5; +} + +.c28 tfoot tr { + background: #fff; +} + +.c28 tfoot tr td { + border-bottom: 1px solid #e5e5e5; +} + +.c29 a { + -webkit-text-decoration: none; + text-decoration: none; + color: #000; +} + +.c29 a:hover { + -webkit-text-decoration: underline; + text-decoration: underline; + color: #000; +} + +.c30 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; +} + +.c31 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 9em; +} + +.c47 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 30%; +} + +.c48 { + background-color: #fff; + color: #000; + border-top: 1px solid #e5e5e5; + font-weight: bold; + width: 10%; +} + .c49 { overflow-wrap: break-word; } @@ -2609,6 +2609,19 @@ exports[`EditScanConfigDialog component tests > should render dialog for config height: 100%; } +.c17 { + font-size: 14px; + margin-bottom: 0px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; +} + .c10 { display: -webkit-box; display: -webkit-flex; @@ -2642,15 +2655,6 @@ exports[`EditScanConfigDialog component tests > should render dialog for config padding-right: 10px; } -.c16 { - color: #c12c30; - font-weight: bold; - font-size: 19px; - padding-bottom: 1px; - padding-left: 4px; - display: none; -} - .c14 { font-family: inherit; font-size: inherit; @@ -2691,17 +2695,13 @@ exports[`EditScanConfigDialog component tests > should render dialog for config align-items: center; } -.c17 { - font-size: 14px; - margin-bottom: 0px; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-pack: center; - -ms-flex-pack: center; - -webkit-justify-content: center; - justify-content: center; +.c16 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; } @@ -3177,6 +3177,19 @@ exports[`EditScanConfigDialog component tests > should render dialog inline noti height: 100%; } +.c17 { + font-size: 14px; + margin-bottom: 0px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -ms-flex-pack: center; + -webkit-justify-content: center; + justify-content: center; +} + .c10 { display: -webkit-box; display: -webkit-flex; @@ -3210,15 +3223,6 @@ exports[`EditScanConfigDialog component tests > should render dialog inline noti padding-right: 10px; } -.c16 { - color: #c12c30; - font-weight: bold; - font-size: 19px; - padding-bottom: 1px; - padding-left: 4px; - display: none; -} - .c14 { font-family: inherit; font-size: inherit; @@ -3259,17 +3263,13 @@ exports[`EditScanConfigDialog component tests > should render dialog inline noti align-items: center; } -.c17 { - font-size: 14px; - margin-bottom: 0px; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-box-pack: center; - -ms-flex-pack: center; - -webkit-justify-content: center; - justify-content: center; +.c16 { + color: #c12c30; + font-weight: bold; + font-size: 19px; + padding-bottom: 1px; + padding-left: 4px; + display: none; } diff --git a/src/web/pages/tasks/__tests__/__snapshots__/autodeletereportsgroup.jsx.snap b/src/web/pages/tasks/__tests__/__snapshots__/autodeletereportsgroup.jsx.snap index 12fe4743ae..d828689415 100644 --- a/src/web/pages/tasks/__tests__/__snapshots__/autodeletereportsgroup.jsx.snap +++ b/src/web/pages/tasks/__tests__/__snapshots__/autodeletereportsgroup.jsx.snap @@ -37,28 +37,6 @@ exports[`AutoDeleteReportsGroup tests > should render dialog group 1`] = ` align-items: center; } -.c7 { - margin-left: -5px; -} - -.c7>* { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - -.c7>* { - margin-left: 5px; -} - -.c6 { - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; -} - .c0 { display: -webkit-box; display: -webkit-flex; @@ -92,6 +70,28 @@ exports[`AutoDeleteReportsGroup tests > should render dialog group 1`] = ` padding-right: 10px; } +.c7 { + margin-left: -5px; +} + +.c7>* { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c7>* { + margin-left: 5px; +} + +.c6 { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + .c4 { display: -webkit-inline-box; display: -webkit-inline-flex; diff --git a/src/web/pages/tasks/__tests__/__snapshots__/details.jsx.snap b/src/web/pages/tasks/__tests__/__snapshots__/details.jsx.snap index e743288e07..d88ca107ed 100644 --- a/src/web/pages/tasks/__tests__/__snapshots__/details.jsx.snap +++ b/src/web/pages/tasks/__tests__/__snapshots__/details.jsx.snap @@ -59,35 +59,48 @@ exports[`Task Details tests > should render full task details 1`] = ` align-items: center; } -.c2 { +.c5 { + margin-left: -5px; +} + +.c5>* { display: -webkit-inline-box; display: -webkit-inline-flex; display: -ms-inline-flexbox; display: inline-flex; } -.c5 { - margin-left: -5px; +.c5>* { + margin-left: 5px; } -.c5>* { +.c4 { display: -webkit-inline-box; display: -webkit-inline-flex; display: -ms-inline-flexbox; display: inline-flex; } -.c5>* { +.c6>*:not(:last-child)::after { + content: '•'; margin-left: 5px; } -.c4 { +.c2 { display: -webkit-inline-box; display: -webkit-inline-flex; display: -ms-inline-flexbox; display: inline-flex; } +.c9 { + width: 10%; +} + +.c10 { + width: 90%; +} + .c7 { border: 0; border-spacing: 0px; @@ -97,11 +110,6 @@ exports[`Task Details tests > should render full task details 1`] = ` width: 100%; } -.c6>*:not(:last-child)::after { - content: '•'; - margin-left: 5px; -} - .c8 td { padding: 4px 4px 4px 0; } @@ -110,14 +118,6 @@ exports[`Task Details tests > should render full task details 1`] = ` padding-right: 5px; } -.c9 { - width: 10%; -} - -.c10 { - width: 90%; -} - @media print { .c7 { border-collapse: collapse; diff --git a/src/web/pages/tasks/dashboard/highresults.jsx b/src/web/pages/tasks/dashboard/highresults.jsx index 34a43c8b2a..9190c8009b 100644 --- a/src/web/pages/tasks/dashboard/highresults.jsx +++ b/src/web/pages/tasks/dashboard/highresults.jsx @@ -5,7 +5,7 @@ import React from 'react'; -import {withRouter} from 'react-router-dom'; +import {withRouter} from 'web/utils/withRouter'; import {format as d3format} from 'd3-format'; @@ -73,9 +73,9 @@ export class TasksHighResultsDisplay extends React.Component { } handleDataClick(data) { - const {history} = this.props; + const {navigate} = this.props; - history.push(`/task/${data.id}`); + navigate(`/task/${data.id}`); } render() { @@ -109,7 +109,7 @@ export class TasksHighResultsDisplay extends React.Component { TasksHighResultsDisplay.propTypes = { filter: PropTypes.filter, - history: PropTypes.object.isRequired, + navigate: PropTypes.func.isRequired, }; TasksHighResultsDisplay = compose( diff --git a/src/web/pages/tasks/dashboard/mosthighresults.jsx b/src/web/pages/tasks/dashboard/mosthighresults.jsx index 2fbe5bf242..3e827ed1bf 100644 --- a/src/web/pages/tasks/dashboard/mosthighresults.jsx +++ b/src/web/pages/tasks/dashboard/mosthighresults.jsx @@ -5,7 +5,7 @@ import React from 'react'; -import {withRouter} from 'react-router-dom'; +import {withRouter} from 'web/utils/withRouter'; import {format as d3format} from 'd3-format'; @@ -66,9 +66,9 @@ export class TasksMostHighResultsDisplay extends React.Component { } handleDataClick(data) { - const {history} = this.props; + const {navigate} = this.props; - history.push(`/task/${data.id}`); + navigate(`/task/${data.id}`); } render() { @@ -107,7 +107,7 @@ export class TasksMostHighResultsDisplay extends React.Component { TasksMostHighResultsDisplay.propTypes = { filter: PropTypes.filter, - history: PropTypes.object.isRequired, + navigate: PropTypes.func.isRequired, }; TasksMostHighResultsDisplay = compose( diff --git a/src/web/pages/tasks/detailspage.jsx b/src/web/pages/tasks/detailspage.jsx index 4cf49ebf50..b44518cbb4 100644 --- a/src/web/pages/tasks/detailspage.jsx +++ b/src/web/pages/tasks/detailspage.jsx @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ - import React from 'react'; import _ from 'gmp/locale'; @@ -182,18 +181,19 @@ export const ToolBarIcons = ({ )} - {!isDefined(entity.current_report) && isDefined(entity.last_report) && ( - - - - )} + {!isDefined(entity.current_report) && + isDefined(entity.last_report) && ( + + + + )} - qhistory(history, stringify, parse); +const LoggedOutRoutes = () => ( + + } /> + } /> + } /> + +); -const HISTORY = createQueryHistory(); +const LoggedInRoutes = () => { + return ( + + + + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } + /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } + /> + } /> + } /> + } /> + } + /> + } /> + } + /> + } + /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } + /> + } /> -const Routes = () => ( - - - - - - - - - - + } /> + + } + /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + } + /> + + } + /> + } /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + } /> + + + + + ); +}; - - - - - - - - -); +const AppRoutes = () => { + const isLoggedIn = useSelector(selectIsLoggedIn); -Routes.propTypes = { - gmp: PropTypes.gmp.isRequired, + return ( + {isLoggedIn ? : } + ); }; -export default withGmp(Routes); - -// vim: set ts=2 sw=2 tw=80: +export default AppRoutes; diff --git a/src/web/utils/testing.jsx b/src/web/utils/testing.jsx index 626b33b3dc..788f70f2d8 100644 --- a/src/web/utils/testing.jsx +++ b/src/web/utils/testing.jsx @@ -22,14 +22,13 @@ import { within, renderHook as rtlRenderHook, } from '@testing-library/react/pure'; + import userEvent from '@testing-library/user-event'; -import {Router} from 'react-router-dom'; +import {BrowserRouter} from 'react-router-dom'; import {Provider} from 'react-redux'; -import {createMemoryHistory} from 'history'; - import EverythingCapabilities from 'gmp/capabilities/everything'; import {hasValue, isDefined} from 'gmp/utils/identity'; @@ -38,7 +37,6 @@ import GmpContext from 'web/components/provider/gmpprovider'; import CapabilitiesContext from 'web/components/provider/capabilitiesprovider'; import LicenseProvider from 'web/components/provider/licenseprovider'; -import {createQueryHistory} from 'web/routes'; import configureStore from 'web/store'; import {StyleSheetManager} from 'styled-components'; @@ -116,7 +114,6 @@ const withProvider = const TestingGmpProvider = withProvider('gmp', 'value')(GmpContext.Provider); const TestingStoreProvider = withProvider('store')(Provider); -const TestingRouter = withProvider('history')(Router); const TestingCapabilitiesProvider = withProvider( 'capabilities', 'value', @@ -136,11 +133,6 @@ export const rendererWith = ( store = configureStore({testing: true}); } - let history; - if (router === true) { - history = createQueryHistory(createMemoryHistory({initialEntries: ['/']})); - } - if (capabilities === true) { capabilities = new EverythingCapabilities(); } @@ -150,7 +142,11 @@ export const rendererWith = ( - {children} + {router ? ( + {children} + ) : ( + children + )} @@ -163,7 +159,6 @@ export const rendererWith = ( render: ui => render({ui}), gmp, store, - history, renderHook: hook => rtlRenderHook(hook, {wrapper}), }; }; diff --git a/src/web/utils/withRouter.jsx b/src/web/utils/withRouter.jsx new file mode 100644 index 0000000000..fc5d45aa49 --- /dev/null +++ b/src/web/utils/withRouter.jsx @@ -0,0 +1,32 @@ +/* SPDX-FileCopyrightText: 2024 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { + useLocation, + useNavigate, + useParams, + useSearchParams, +} from 'react-router-dom'; + +export const withRouter = Component => { + function ComponentWithRouterProp(props) { + const location = useLocation(); + const navigate = useNavigate(); + const params = useParams(); + const [searchParams] = useSearchParams(); + + return ( + + ); + } + + return ComponentWithRouterProp; +};