From 247e9f3668cdf8c918f9bed7cb6fd2cdf2e9de6f Mon Sep 17 00:00:00 2001 From: Syed Ali Abbas Zaidi <88369802+Syed-Ali-Abbas-Zaidi@users.noreply.github.com> Date: Mon, 18 Sep 2023 20:27:40 +0500 Subject: [PATCH] feat: upgrade react router to v6 (#319) * feat: upgrade react router to v6 * refactor: removed remaining router v5 code * refactor: improve code coverage --- package-lock.json | 442 ++++++++++++++++--------- package.json | 10 +- src/App.jsx | 29 +- src/App.test.jsx | 23 +- src/__snapshots__/App.test.jsx.snap | 27 +- src/containers/GradebookPage/index.jsx | 23 +- src/containers/GradebookPage/test.jsx | 11 +- src/utils/hoc.jsx | 24 ++ src/utils/hoc.test.jsx | 38 +++ 9 files changed, 401 insertions(+), 226 deletions(-) create mode 100644 src/utils/hoc.jsx create mode 100644 src/utils/hoc.test.jsx diff --git a/package-lock.json b/package-lock.json index f922b8b..4a5e3ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,9 @@ "license": "AGPL-3.0", "dependencies": { "@edx/brand": "npm:@edx/brand-openedx@^1.2.0", - "@edx/frontend-component-footer": "12.1.0", - "@edx/frontend-component-header": "4.3.0", - "@edx/frontend-platform": "4.6.0", + "@edx/frontend-component-footer": "12.2.0", + "@edx/frontend-component-header": "4.6.0", + "@edx/frontend-platform": "5.0.0", "@edx/paragon": "20.45.0", "@edx/react-unit-test-utils": "1.7.0", "@edx/reactifex": "^2.1.1", @@ -35,8 +35,8 @@ "react-dom": "17.0.2", "react-helmet": "^6.1.0", "react-redux": "^7.2.9", - "react-router": "5.2.0", - "react-router-dom": "5.2.0", + "react-router": "6.15.0", + "react-router-dom": "6.15.0", "react-router-redux": "^5.0.0-alpha.9", "redux": "4.0.5", "redux-beacon": "^2.1.0", @@ -2191,9 +2191,9 @@ } }, "node_modules/@edx/frontend-component-footer": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-12.1.0.tgz", - "integrity": "sha512-yF2GHwb9dzaYVSNGS7ps92hFCfh3ZChf+gPO+rFYq0cRsRENFV2UV9tlfBKSiwqiu19JZCpepp4tSIIg7QUOgg==", + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-12.2.0.tgz", + "integrity": "sha512-tAI6TSvb0U15drV/eKtEs8O+oiDtTE/yaN4uDKs++RJxKVpLUlar7sug54b7cVy9Y3FPR/3Cfb/ytsuIqhLmxw==", "dependencies": { "@fortawesome/fontawesome-svg-core": "6.4.0", "@fortawesome/free-brands-svg-icons": "6.4.0", @@ -2202,7 +2202,7 @@ "@fortawesome/react-fontawesome": "0.2.0" }, "peerDependencies": { - "@edx/frontend-platform": "^4.0.0", + "@edx/frontend-platform": "^4.0.0 || ^5.0.0", "prop-types": "^15.5.10", "react": "^16.9.0 || ^17.0.0", "react-dom": "^16.9.0 || ^17.0.0" @@ -2266,75 +2266,129 @@ } }, "node_modules/@edx/frontend-component-header": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-4.3.0.tgz", - "integrity": "sha512-NqeqRqxKsSZ4LKj+pVTfcbUagtFh4atBPRDuWxJoCyV4KTaTkn/WBYRGf4D6MSvo8xHJUgVXu9ZDmxdMhRWyew==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-4.6.0.tgz", + "integrity": "sha512-zZuMgHQWfFMTquVb4iL/iQMwKRRgts8CFFLyL8R6vQL1WfHd21hndhKii2kp9lBnIJgrilIfF79RsbImb5L0og==", "dependencies": { - "@edx/paragon": "20.45.0", - "@fortawesome/fontawesome-svg-core": "6.4.0", - "@fortawesome/free-brands-svg-icons": "6.4.0", - "@fortawesome/free-regular-svg-icons": "6.4.0", - "@fortawesome/free-solid-svg-icons": "6.4.0", + "@edx/paragon": "20.46.2", + "@fortawesome/fontawesome-svg-core": "6.4.2", + "@fortawesome/free-brands-svg-icons": "6.4.2", + "@fortawesome/free-regular-svg-icons": "6.4.2", + "@fortawesome/free-solid-svg-icons": "6.4.2", "@fortawesome/react-fontawesome": "^0.2.0", - "@reduxjs/toolkit": "1.9.5", "axios-mock-adapter": "1.21.5", "babel-polyfill": "6.26.0", - "classnames": "2.3.2", - "lodash": "4.17.21", - "react-redux": "7.2.9", "react-responsive": "8.2.0", - "react-router-dom": "5.3.4", - "react-transition-group": "4.4.5", - "rosie": "2.1.0", - "timeago.js": "4.0.2" + "react-transition-group": "4.4.5" }, "peerDependencies": { - "@edx/frontend-platform": "^4.0.0", + "@edx/frontend-platform": "^4.0.0 || ^5.0.0", "prop-types": "^15.5.10", "react": "^16.9.0 || ^17.0.0", "react-dom": "^16.9.0 || ^17.0.0" } }, + "node_modules/@edx/frontend-component-header/node_modules/@edx/paragon": { + "version": "20.46.2", + "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.46.2.tgz", + "integrity": "sha512-px+KS/BV1CbiMKgfVgUofyjJi4CHUCUOLRukJbT66VPPqWP4Xon5Rns6uohoratPXMg2kNN46v2L8wIwqKQ4Lw==", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.1.1", + "@fortawesome/react-fontawesome": "^0.1.18", + "@popperjs/core": "^2.11.4", + "bootstrap": "^4.6.2", + "classnames": "^2.3.1", + "email-prop-type": "^3.0.0", + "file-selector": "^0.6.0", + "font-awesome": "^4.7.0", + "glob": "^8.0.3", + "lodash.uniqby": "^4.7.0", + "mailto-link": "^2.0.0", + "prop-types": "^15.8.1", + "react-bootstrap": "^1.6.5", + "react-colorful": "^5.6.1", + "react-dropzone": "^14.2.1", + "react-focus-on": "^3.5.4", + "react-loading-skeleton": "^3.1.0", + "react-popper": "^2.2.5", + "react-proptype-conditional-require": "^1.0.4", + "react-responsive": "^8.2.0", + "react-table": "^7.7.0", + "react-transition-group": "^4.4.2", + "tabbable": "^5.3.3", + "uncontrollable": "^7.2.1", + "uuid": "^9.0.0" + }, + "peerDependencies": { + "react": "^16.8.6 || ^17.0.0", + "react-dom": "^16.8.6 || ^17.0.0", + "react-intl": "^5.25.1" + } + }, + "node_modules/@edx/frontend-component-header/node_modules/@edx/paragon/node_modules/@fortawesome/react-fontawesome": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz", + "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.x" + } + }, "node_modules/@edx/frontend-component-header/node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz", - "integrity": "sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz", + "integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==", "hasInstallScript": true, "engines": { "node": ">=6" } }, "node_modules/@edx/frontend-component-header/node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.0.tgz", - "integrity": "sha512-Bertv8xOiVELz5raB2FlXDPKt+m94MQ3JgDfsVbrqNpLU9+UE2E18GKjLKw+d3XbeYPqg1pzyQKGsrzbw+pPaw==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz", + "integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.0" + "@fortawesome/fontawesome-common-types": "6.4.2" }, "engines": { "node": ">=6" } }, "node_modules/@edx/frontend-component-header/node_modules/@fortawesome/free-brands-svg-icons": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.4.0.tgz", - "integrity": "sha512-qvxTCo0FQ5k2N+VCXb/PZQ+QMhqRVM4OORiO6MXdG6bKolIojGU/srQ1ptvKk0JTbRgaJOfL2qMqGvBEZG7Z6g==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.4.2.tgz", + "integrity": "sha512-LKOwJX0I7+mR/cvvf6qIiqcERbdnY+24zgpUSouySml+5w8B4BJOx8EhDR/FTKAu06W12fmUIcv6lzPSwYKGGg==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.0" + "@fortawesome/fontawesome-common-types": "6.4.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@edx/frontend-component-header/node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.2.tgz", + "integrity": "sha512-0+sIUWnkgTVVXVAPQmW4vxb9ZTHv0WstOa3rBx9iPxrrrDH6bNLsDYuwXF9b6fGm+iR7DKQvQshUH/FJm3ed9Q==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.4.2" }, "engines": { "node": ">=6" } }, "node_modules/@edx/frontend-component-header/node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.0.tgz", - "integrity": "sha512-kutPeRGWm8V5dltFP1zGjQOEAzaLZj4StdQhWVZnfGFCvAPVvHh8qk5bRrU4KXnRRRNni5tKQI9PBAdI6MP8nQ==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz", + "integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==", "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.4.0" + "@fortawesome/fontawesome-common-types": "6.4.2" }, "engines": { "node": ">=6" @@ -2352,64 +2406,55 @@ "react": ">=16.3" } }, - "node_modules/@edx/frontend-component-header/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/@edx/frontend-component-header/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==", + "node_modules/@edx/frontend-component-header/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "isarray": "0.0.1" + "balanced-match": "^1.0.0" } }, - "node_modules/@edx/frontend-component-header/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/@edx/frontend-component-header/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==", + "node_modules/@edx/frontend-component-header/node_modules/email-prop-type": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/email-prop-type/-/email-prop-type-3.0.1.tgz", + "integrity": "sha512-tONZGMEOOkadp5OBftuVXU8DsceWmINxYK+pqPFB4LT5ODjrPX/esel3WGqbV7d6in5/MnZE4n4QcqOr4gh7dg==", "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" - }, - "peerDependencies": { - "react": ">=15" + "email-validator": "^2.0.4" } }, - "node_modules/@edx/frontend-component-header/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==", + "node_modules/@edx/frontend-component-header/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "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" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" }, - "peerDependencies": { - "react": ">=15" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@edx/frontend-component-header/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, "node_modules/@edx/frontend-platform": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-4.6.0.tgz", - "integrity": "sha512-NZ1I3BgUZl7bqvDwSnnL+LxqZOdOUGZU55KiwvknqiKU8RS5Lx9tc4arp+NcX1u58xy/Xbinv+mriSO6PPxQNQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-5.0.0.tgz", + "integrity": "sha512-DD9/B4rnC3BKPiWlbEFF1JIYFbWC6vUBKTyN8sf4khi4DNhhWhsobk+iNeCWNzF9UgCPRbniIqesdV1F9NXNZw==", "dependencies": { "@cospired/i18n-iso-languages": "4.1.0", "@formatjs/intl-pluralrules": "4.3.3", @@ -2436,13 +2481,13 @@ "transifex-utils.js": "i18n/scripts/transifex-utils.js" }, "peerDependencies": { - "@edx/frontend-build": ">= 8.1.0", + "@edx/frontend-build": ">= 8.1.0 || ^12.9.0-alpha.1", "@edx/paragon": ">= 10.0.0 < 21.0.0", "prop-types": "^15.7.2", "react": "^16.9.0 || ^17.0.0", "react-dom": "^16.9.0 || ^17.0.0", "react-redux": "^7.1.1", - "react-router-dom": "^5.0.1", + "react-router-dom": "^6.0.0", "redux": "^4.0.4" } }, @@ -2603,6 +2648,127 @@ "react": "^16.9.0 || ^17.0.0" } }, + "node_modules/@edx/react-unit-test-utils/node_modules/@edx/frontend-platform": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-4.6.0.tgz", + "integrity": "sha512-NZ1I3BgUZl7bqvDwSnnL+LxqZOdOUGZU55KiwvknqiKU8RS5Lx9tc4arp+NcX1u58xy/Xbinv+mriSO6PPxQNQ==", + "dependencies": { + "@cospired/i18n-iso-languages": "4.1.0", + "@formatjs/intl-pluralrules": "4.3.3", + "@formatjs/intl-relativetimeformat": "10.0.1", + "axios": "0.27.2", + "axios-cache-interceptor": "0.10.7", + "form-urlencoded": "4.1.4", + "glob": "7.2.3", + "history": "4.10.1", + "i18n-iso-countries": "4.3.1", + "jwt-decode": "3.1.2", + "localforage": "1.10.0", + "localforage-memoryStorageDriver": "0.9.2", + "lodash.camelcase": "4.3.0", + "lodash.memoize": "4.1.2", + "lodash.merge": "4.6.2", + "lodash.snakecase": "4.1.1", + "pubsub-js": "1.9.4", + "react-intl": "^5.25.0", + "universal-cookie": "4.0.4" + }, + "bin": { + "intl-imports.js": "i18n/scripts/intl-imports.js", + "transifex-utils.js": "i18n/scripts/transifex-utils.js" + }, + "peerDependencies": { + "@edx/frontend-build": ">= 8.1.0", + "@edx/paragon": ">= 10.0.0 < 21.0.0", + "prop-types": "^15.7.2", + "react": "^16.9.0 || ^17.0.0", + "react-dom": "^16.9.0 || ^17.0.0", + "react-redux": "^7.1.1", + "react-router-dom": "^5.0.1", + "redux": "^4.0.4" + } + }, + "node_modules/@edx/react-unit-test-utils/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/@edx/react-unit-test-utils/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@edx/react-unit-test-utils/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==", + "peer": true + }, + "node_modules/@edx/react-unit-test-utils/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==", + "peer": true, + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/@edx/react-unit-test-utils/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==", + "peer": true + }, + "node_modules/@edx/react-unit-test-utils/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==", + "peer": true, + "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" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/@edx/react-unit-test-utils/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==", + "peer": true, + "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" + }, + "peerDependencies": { + "react": ">=15" + } + }, "node_modules/@edx/reactifex": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@edx/reactifex/-/reactifex-2.2.0.tgz", @@ -4538,6 +4704,14 @@ "redux": "^4" } }, + "node_modules/@remix-run/router": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.8.0.tgz", + "integrity": "sha512-mrfKqIHnSZRyIzBcanNJmVQELTnX+qagEDlcKO90RgRBVOZGSGvZKeDihTRfWcqoDn5N/NkUcwWTccnpN18Tfg==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@restart/context": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz", @@ -17137,20 +17311,6 @@ "node": ">=4" } }, - "node_modules/mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" - }, - "peerDependencies": { - "prop-types": "^15.0.0", - "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, "node_modules/mini-css-extract-plugin": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz", @@ -22613,40 +22773,33 @@ } }, "node_modules/react-router": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", - "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.15.0.tgz", + "integrity": "sha512-NIytlzvzLwJkCQj2HLefmeakxxWHWAP+02EGqWEZy+DgfHHKQMUoBBjUQLOtFInBMhWtb3hiUy6MfFgwLjXhqg==", "dependencies": { - "@babel/runtime": "^7.1.2", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", - "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.8.0" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "react": ">=15" + "react": ">=16.8" } }, "node_modules/react-router-dom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", - "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.15.0.tgz", + "integrity": "sha512-aR42t0fs7brintwBGAv2+mGlCtgtFQeOzK0BM1/OiqEzRejOZtpMZepvgkscpMUnKb8YO84G7s3LsHnnDNonbQ==", "dependencies": { - "@babel/runtime": "^7.1.2", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.2.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "@remix-run/router": "1.8.0", + "react-router": "6.15.0" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "react": ">=15" + "react": ">=16.8", + "react-dom": ">=16.8" } }, "node_modules/react-router-redux": { @@ -22698,24 +22851,6 @@ "react": ">=15" } }, - "node_modules/react-router/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/react-router/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/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/react-shallow-renderer": { "version": "16.15.0", "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", @@ -23406,14 +23541,6 @@ "rimraf": "bin.js" } }, - "node_modules/rosie": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/rosie/-/rosie-2.1.0.tgz", - "integrity": "sha512-Dbzdc+prLXZuB/suRptDnBUY29SdGvND3bLg6cll8n7PNqzuyCxSlRfrkn8PqjS9n4QVsiM7RCvxCkKAkTQRjA==", - "engines": { - "node": ">=10" - } - }, "node_modules/rst-selector-parser": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", @@ -25876,11 +26003,6 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, - "node_modules/timeago.js": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/timeago.js/-/timeago.js-4.0.2.tgz", - "integrity": "sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w==" - }, "node_modules/tiny-invariant": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", diff --git a/package.json b/package.json index 957dc68..a05c2e9 100644 --- a/package.json +++ b/package.json @@ -29,9 +29,9 @@ ], "dependencies": { "@edx/brand": "npm:@edx/brand-openedx@^1.2.0", - "@edx/frontend-component-footer": "12.1.0", - "@edx/frontend-component-header": "4.3.0", - "@edx/frontend-platform": "4.6.0", + "@edx/frontend-component-footer": "12.2.0", + "@edx/frontend-component-header": "4.6.0", + "@edx/frontend-platform": "5.0.0", "@edx/paragon": "20.45.0", "@edx/react-unit-test-utils": "1.7.0", "@edx/reactifex": "^2.1.1", @@ -54,8 +54,8 @@ "react-dom": "17.0.2", "react-helmet": "^6.1.0", "react-redux": "^7.2.9", - "react-router": "5.2.0", - "react-router-dom": "5.2.0", + "react-router": "6.15.0", + "react-router-dom": "6.15.0", "react-router-redux": "^5.0.0-alpha.9", "redux": "4.0.5", "redux-beacon": "^2.1.0", diff --git a/src/App.jsx b/src/App.jsx index f154c6a..01d1b0b 100755 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; +import { Route, Routes } from 'react-router-dom'; import { AppProvider } from '@edx/frontend-platform/react'; @@ -15,21 +15,18 @@ import Head from './head/Head'; const App = () => ( - -
-
-
- - - -
-
-
-
+
+
+
+ + } + /> + +
+
); diff --git a/src/App.test.jsx b/src/App.test.jsx index 91aadf4..4ed87af 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; +import { Route, Routes } from 'react-router-dom'; import { AppProvider } from '@edx/frontend-platform/react'; import Footer from '@edx/frontend-component-footer'; @@ -17,7 +17,6 @@ import Head from './head/Head'; jest.mock('react-router-dom', () => ({ BrowserRouter: () => 'BrowserRouter', Route: () => 'Route', - Switch: () => 'Switch', })); jest.mock('@edx/frontend-platform/react', () => ({ AppProvider: () => 'AppProvider', @@ -32,7 +31,7 @@ jest.mock('@edx/frontend-component-header', () => 'Header'); const logo = 'fakeLogo.png'; let el; -let router; +let secondChild; describe('App router component', () => { test('snapshot', () => { @@ -42,7 +41,7 @@ describe('App router component', () => { beforeEach(() => { process.env.LOGO_POWERED_BY_OPEN_EDX_URL_SVG = logo; el = shallow(); - router = el.childAt(1); + secondChild = el.childAt(1); }); describe('AppProvider', () => { test('AppProvider is the parent component, passed the redux store props', () => { @@ -57,24 +56,24 @@ describe('App router component', () => { }); describe('Router', () => { test('second child of AppProvider', () => { - expect(router.type()).toBe(Router); + expect(secondChild.type()).toBe('div'); }); test('Header is above/outside-of the routing', () => { - expect(router.childAt(0).childAt(0).type()).toBe(Header); - expect(router.childAt(0).childAt(1).type()).toBe('main'); + expect(secondChild.childAt(0).type()).toBe(Header); + expect(secondChild.childAt(1).type()).toBe('main'); }); test('Routing - GradebookPage is only route', () => { - expect(router.find('main')).toEqual(shallow( + expect(secondChild.find('main')).toEqual(shallow(
- - - + + } /> +
, )); }); }); test('Footer logo drawn from env variable', () => { - expect(router.find(Footer).props().logo).toEqual(logo); + expect(secondChild.find(Footer).props().logo).toEqual(logo); }); }); }); diff --git a/src/__snapshots__/App.test.jsx.snap b/src/__snapshots__/App.test.jsx.snap index 8f505dd..8f8ccf7 100644 --- a/src/__snapshots__/App.test.jsx.snap +++ b/src/__snapshots__/App.test.jsx.snap @@ -5,20 +5,17 @@ exports[`App router component snapshot 1`] = ` store="testStore" > - -
-
-
- - - -
-
-
-
+
+
+
+ + } + path="/:courseId" + /> + +
+
+
`; diff --git a/src/containers/GradebookPage/index.jsx b/src/containers/GradebookPage/index.jsx index 38e7ed6..d5db500 100644 --- a/src/containers/GradebookPage/index.jsx +++ b/src/containers/GradebookPage/index.jsx @@ -14,6 +14,8 @@ import GradesView from 'components/GradesView'; import GradebookFilters from 'components/GradebookFilters'; import BulkManagementHistoryView from 'components/BulkManagementHistoryView'; +import { withParams, withNavigate, withLocation } from '../../utils/hoc'; + /** * * Top-level view for the Gradebook MFE. @@ -28,10 +30,11 @@ export class GradebookPage extends React.Component { componentDidMount() { const urlQuery = queryString.parse(this.props.location.search); - this.props.initializeApp(this.props.match.params.courseId, urlQuery); + this.props.initializeApp(this.props.courseId, urlQuery); } updateQueryParams(queryParams) { + const { pathname } = this.props.location; const parsed = queryString.parse(this.props.location.search); Object.keys(queryParams).forEach((key) => { if (queryParams[key]) { @@ -40,7 +43,7 @@ export class GradebookPage extends React.Component { delete parsed[key]; } }); - this.props.history.push(`?${queryString.stringify(parsed)}`); + this.props.navigate({ pathname, search: `?${queryString.stringify(parsed)}` }); } render() { @@ -60,18 +63,12 @@ export class GradebookPage extends React.Component { } } GradebookPage.defaultProps = { - location: { search: '' }, + location: { pathname: '/', search: '' }, }; GradebookPage.propTypes = { - history: PropTypes.shape({ - push: PropTypes.func, - }).isRequired, - location: PropTypes.shape({ search: PropTypes.string }), - match: PropTypes.shape({ - params: PropTypes.shape({ - courseId: PropTypes.string, - }), - }).isRequired, + navigate: PropTypes.func.isRequired, + location: PropTypes.shape({ pathname: PropTypes.string, search: PropTypes.string }), + courseId: PropTypes.string.isRequired, // redux activeView: PropTypes.string.isRequired, initializeApp: PropTypes.func.isRequired, @@ -85,4 +82,4 @@ export const mapDispatchToProps = { initializeApp: thunkActions.app.initialize, }; -export default connect(mapStateToProps, mapDispatchToProps)(GradebookPage); +export default connect(mapStateToProps, mapDispatchToProps)(withParams(withNavigate(withLocation(GradebookPage)))); diff --git a/src/containers/GradebookPage/test.jsx b/src/containers/GradebookPage/test.jsx index 6a4e41c..78728fa 100644 --- a/src/containers/GradebookPage/test.jsx +++ b/src/containers/GradebookPage/test.jsx @@ -50,14 +50,15 @@ describe('GradebookPage', () => { let el; const props = { location: { + pathname: '/', search: 'searchString', }, - match: { params: { courseId } }, + courseId, activeView: views.grades, }; beforeEach(() => { props.initializeApp = jest.fn(); - props.history = { push: jest.fn() }; + props.navigate = jest.fn(); }); test('snapshot - shows BulkManagementHistoryView if activeView === views.bulkManagementHistory', () => { el = shallow(); @@ -130,7 +131,7 @@ describe('GradebookPage', () => { const val2 = 'VALTWO!!'; const args = { [newKey]: val1, [props.location.search]: val2 }; el.instance().updateQueryParams(args); - expect(props.history.push).toHaveBeenCalledWith(`?${queryString.stringify(args)}`); + expect(props.navigate).toHaveBeenCalledWith({ pathname: '/', search: `?${queryString.stringify(args)}` }); }); it('clears values for non-truthy values', () => { queryString.parse.mockImplementation(key => ({ [key]: key })); @@ -139,8 +140,8 @@ describe('GradebookPage', () => { const val2 = false; const args = { [newKey]: val1, [props.location.search]: val2 }; el.instance().updateQueryParams(args); - expect(props.history.push).toHaveBeenCalledWith( - `?${queryString.stringify({ [newKey]: val1 })}`, + expect(props.navigate).toHaveBeenCalledWith( + { pathname: '/', search: `?${queryString.stringify({ [newKey]: val1 })}` }, ); }); }); diff --git a/src/utils/hoc.jsx b/src/utils/hoc.jsx new file mode 100644 index 0000000..e549230 --- /dev/null +++ b/src/utils/hoc.jsx @@ -0,0 +1,24 @@ +import React from 'react'; + +import { useLocation, useNavigate, useParams } from 'react-router-dom'; + +export const withParams = WrappedComponent => { + const WithParamsComponent = props => ; + return WithParamsComponent; +}; + +export const withNavigate = WrappedComponent => { + const WithNavigateComponent = props => { + const navigate = useNavigate(); + return ; + }; + return WithNavigateComponent; +}; + +export const withLocation = WrappedComponent => { + const WithLocationComponent = props => { + const location = useLocation(); + return ; + }; + return WithLocationComponent; +}; diff --git a/src/utils/hoc.test.jsx b/src/utils/hoc.test.jsx new file mode 100644 index 0000000..1b319db --- /dev/null +++ b/src/utils/hoc.test.jsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { mount } from 'enzyme'; + +import { withLocation, withNavigate } from './hoc'; + +const mockedNavigator = jest.fn(); + +jest.mock('react-router-dom', () => ({ + useNavigate: () => mockedNavigator, + useLocation: () => ({ + pathname: '/current-location', + }), +})); + +// eslint-disable-next-line react/prop-types +const MockComponent = ({ navigate, location }) => ( + // eslint-disable-next-line react/button-has-type, react/prop-types + +); +const WrappedComponent = withNavigate(withLocation(MockComponent)); + +test('Provide Navigation to Component', () => { + const wrapper = mount( + , + ); + const btn = wrapper.find('#btn'); + btn.simulate('click'); + + expect(mockedNavigator).toHaveBeenCalledWith('/some-route'); +}); + +test('Provide Location object to Component', () => { + const wrapper = mount( + , + ); + + expect(wrapper.find('#btn').text()).toContain('/current-location'); +});