From 9abbe366528123ce5e7c63a3d777643d9ea7ea10 Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Thu, 22 Sep 2022 18:22:23 -0700 Subject: [PATCH 01/65] Incrementing version number for next set of minor updates. --- frontend/package.json | 2 +- frontend/src/components/App/App.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 87271d35e..18e60e0d3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "1.0.7-beta", + "version": "1.0.8-beta", "private": true, "scripts": { "build:local": "env-cmd -f .envs/.react react-scripts build", diff --git a/frontend/src/components/App/App.tsx b/frontend/src/components/App/App.tsx index 44863c7d0..ad6f02761 100644 --- a/frontend/src/components/App/App.tsx +++ b/frontend/src/components/App/App.tsx @@ -79,7 +79,7 @@ export type Props = { searchQuery: ActiveSearchQuery; }; -const metagridVersion = '1.0.7-beta'; +const metagridVersion = '1.0.8-beta'; const App: React.FC = ({ searchQuery }) => { // Third-party tool integration From 3f659f40e0107fa58080f539dec4ac0e23503320 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Sep 2022 18:30:38 -0700 Subject: [PATCH 02/65] Pin dependencies (#461) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- frontend/package.json | 40 +++++------ frontend/yarn.lock | 160 +++++++++++++++++++++--------------------- 2 files changed, 100 insertions(+), 100 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 18e60e0d3..e2ba16f20 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -57,32 +57,32 @@ }, "dependencies": { "@ant-design/icons": "4.6.2", - "@babel/plugin-syntax-flow": "^7.16.7", - "@babel/plugin-transform-react-jsx": "^7.17.3", + "@babel/plugin-syntax-flow": "7.16.7", + "@babel/plugin-transform-react-jsx": "7.17.3", "@react-keycloak/web": "3.4.0", "antd": "4.15.1", - "autoprefixer": "^10.4.4", - "axios": "^0.26.1", + "autoprefixer": "10.4.4", + "axios": "0.26.1", "dotenv": "8.2.0", "env-cmd": "10.1.0", "humps": "2.0.1", "keycloak-js": "17.0.1", "moment": "2.29.4", - "prop-types": "^15.8.1", + "prop-types": "15.8.1", "query-string": "7.0.0", "react": "17.0.2", "react-async": "10.0.1", "react-dom": "17.0.2", "react-hotjar": "2.2.1", - "react-joyride": "^2.4.0", + "react-joyride": "2.4.0", "react-router-dom": "5.3.3", - "react-scripts": "^5.0.1", - "typescript": "^4.6.3", + "react-scripts": "5.0.1", + "typescript": "4.6.3", "uuid": "8.3.2" }, "devDependencies": { - "@babel/core": "^7.17.9", - "@testing-library/dom": "^8.13.0", + "@babel/core": "7.17.9", + "@testing-library/dom": "8.13.0", "@testing-library/jest-dom": "5.11.10", "@testing-library/react": "11.2.6", "@testing-library/user-event": "13.1.2", @@ -95,19 +95,19 @@ "@types/uuid": "8.3.4", "@typescript-eslint/eslint-plugin": "5.30.7", "@typescript-eslint/parser": "5.30.7", - "dayjs": "^1.11.1", - "eslint": "^8.12.0", + "dayjs": "1.11.1", + "eslint": "8.12.0", "eslint-config-airbnb": "19.0.4", - "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-react": "^7.29.4", + "eslint-config-airbnb-typescript": "17.0.0", + "eslint-config-prettier": "8.5.0", + "eslint-plugin-import": "2.26.0", + "eslint-plugin-jsx-a11y": "6.5.1", + "eslint-plugin-prettier": "4.0.0", + "eslint-plugin-react": "7.29.4", "eslint-plugin-react-hooks": "4.6.0", "msw": "0.28.1", - "postcss": "^8.4.12", + "postcss": "8.4.12", "prettier": "2.2.1", - "setimmediate": "^1.0.5" + "setimmediate": "1.0.5" } } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 362e622c6..8594670f5 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -83,6 +83,27 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== +"@babel/core@7.17.9", "@babel/core@^7.11.1", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.9.tgz#6bae81a06d95f4d0dec5bb9d74bbc1f58babdcfe" + integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.9" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-module-transforms" "^7.17.7" + "@babel/helpers" "^7.17.9" + "@babel/parser" "^7.17.9" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.9" + "@babel/types" "^7.17.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + "@babel/core@^7.1.0": version "7.12.7" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.7.tgz#bf55363c08c8352a37691f7216ec30090bf7e3bf" @@ -105,27 +126,6 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.11.1", "@babel/core@^7.16.0", "@babel/core@^7.17.9", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.9.tgz#6bae81a06d95f4d0dec5bb9d74bbc1f58babdcfe" - integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.9" - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-module-transforms" "^7.17.7" - "@babel/helpers" "^7.17.9" - "@babel/parser" "^7.17.9" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.9" - "@babel/types" "^7.17.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" - "@babel/core@^7.12.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.13.tgz#b73a87a3a3e7d142a66248bf6ad88b9ceb093425" @@ -1038,7 +1038,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-flow@^7.16.7": +"@babel/plugin-syntax-flow@7.16.7", "@babel/plugin-syntax-flow@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" integrity sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ== @@ -1569,6 +1569,17 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.16.7" +"@babel/plugin-transform-react-jsx@7.17.3", "@babel/plugin-transform-react-jsx@^7.16.7": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1" + integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-jsx" "^7.16.7" + "@babel/types" "^7.17.0" + "@babel/plugin-transform-react-jsx@^7.12.12", "@babel/plugin-transform-react-jsx@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.13.tgz#6c9f993b9f6fb6f0e32a4821ed59349748576a3e" @@ -1580,17 +1591,6 @@ "@babel/plugin-syntax-jsx" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/plugin-transform-react-jsx@^7.16.7", "@babel/plugin-transform-react-jsx@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1" - integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-jsx" "^7.16.7" - "@babel/types" "^7.17.0" - "@babel/plugin-transform-react-pure-annotations@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz#05d46f0ab4d1339ac59adf20a1462c91b37a1a42" @@ -2698,33 +2698,33 @@ "@svgr/plugin-svgo" "^5.5.0" loader-utils "^2.0.0" -"@testing-library/dom@^7.28.1": - version "7.28.1" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.28.1.tgz#dea78be6e1e6db32ddcb29a449e94d9700c79eb9" - integrity sha512-acv3l6kDwZkQif/YqJjstT3ks5aaI33uxGNVIQmdKzbZ2eMKgg3EV2tB84GDdc72k3Kjhl6mO8yUt6StVIdRDg== +"@testing-library/dom@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.13.0.tgz#bc00bdd64c7d8b40841e27a70211399ad3af46f5" + integrity sha512-9VHgfIatKNXQNaZTtLnalIy0jNZzY35a4S3oi08YAt9Hv1VsfZ/DfA45lM8D/UhtHBGJ4/lGwp0PZkVndRkoOQ== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" "@types/aria-query" "^4.2.0" - aria-query "^4.2.2" + aria-query "^5.0.0" chalk "^4.1.0" - dom-accessibility-api "^0.5.4" + dom-accessibility-api "^0.5.9" lz-string "^1.4.4" - pretty-format "^26.6.2" + pretty-format "^27.0.2" -"@testing-library/dom@^8.13.0": - version "8.13.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.13.0.tgz#bc00bdd64c7d8b40841e27a70211399ad3af46f5" - integrity sha512-9VHgfIatKNXQNaZTtLnalIy0jNZzY35a4S3oi08YAt9Hv1VsfZ/DfA45lM8D/UhtHBGJ4/lGwp0PZkVndRkoOQ== +"@testing-library/dom@^7.28.1": + version "7.28.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.28.1.tgz#dea78be6e1e6db32ddcb29a449e94d9700c79eb9" + integrity sha512-acv3l6kDwZkQif/YqJjstT3ks5aaI33uxGNVIQmdKzbZ2eMKgg3EV2tB84GDdc72k3Kjhl6mO8yUt6StVIdRDg== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" "@types/aria-query" "^4.2.0" - aria-query "^5.0.0" + aria-query "^4.2.2" chalk "^4.1.0" - dom-accessibility-api "^0.5.9" + dom-accessibility-api "^0.5.4" lz-string "^1.4.4" - pretty-format "^27.0.2" + pretty-format "^26.6.2" "@testing-library/jest-dom@5.11.10": version "5.11.10" @@ -3855,7 +3855,7 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@^10.4.4: +autoprefixer@10.4.4, autoprefixer@^10.4.4: version "10.4.4" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.4.tgz#3e85a245b32da876a893d3ac2ea19f01e7ea5a1e" integrity sha512-Tm8JxsB286VweiZ5F0anmbyGiNI3v3wGv3mz9W+cxEDYB/6jbnj6GM9H9mK3wIL8ftgl+C07Lcwb8PG5PCCPzA== @@ -3872,7 +3872,7 @@ axe-core@^4.3.5: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== -axios@^0.26.1: +axios@0.26.1: version "0.26.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== @@ -4872,7 +4872,7 @@ date-fns@^2.15.0: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b" integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ== -dayjs@^1.11.1: +dayjs@1.11.1: version "1.11.1" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.1.tgz#90b33a3dda3417258d48ad2771b415def6545eb0" integrity sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA== @@ -5371,7 +5371,7 @@ eslint-config-airbnb-base@^15.0.0: object.entries "^1.1.5" semver "^6.3.0" -eslint-config-airbnb-typescript@^17.0.0: +eslint-config-airbnb-typescript@17.0.0: version "17.0.0" resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.0.0.tgz#360dbcf810b26bbcf2ff716198465775f1c49a07" integrity sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g== @@ -5387,7 +5387,7 @@ eslint-config-airbnb@19.0.4: object.assign "^4.1.2" object.entries "^1.1.5" -eslint-config-prettier@^8.5.0: +eslint-config-prettier@8.5.0: version "8.5.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== @@ -5436,7 +5436,7 @@ eslint-plugin-flowtype@^8.0.3: lodash "^4.17.21" string-natural-compare "^3.0.1" -eslint-plugin-import@^2.25.3, eslint-plugin-import@^2.26.0: +eslint-plugin-import@2.26.0, eslint-plugin-import@^2.25.3: version "2.26.0" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== @@ -5462,7 +5462,7 @@ eslint-plugin-jest@^25.3.0: dependencies: "@typescript-eslint/experimental-utils" "^5.0.0" -eslint-plugin-jsx-a11y@^6.5.1: +eslint-plugin-jsx-a11y@6.5.1, eslint-plugin-jsx-a11y@^6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz#cdbf2df901040ca140b6ec14715c988889c2a6d8" integrity sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g== @@ -5480,7 +5480,7 @@ eslint-plugin-jsx-a11y@^6.5.1: language-tags "^1.0.5" minimatch "^3.0.4" -eslint-plugin-prettier@^4.0.0: +eslint-plugin-prettier@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz#8b99d1e4b8b24a762472b4567992023619cb98e0" integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== @@ -5497,7 +5497,7 @@ eslint-plugin-react-hooks@^4.3.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.4.0.tgz#71c39e528764c848d8253e1aa2c7024ed505f6c4" integrity sha512-U3RVIfdzJaeKDQKEJbz5p3NW8/L80PCATJAfuojwbaEL+gBjfGdhUcGde+WGUW46Q5sr/NgxevsIiDtNXrvZaQ== -eslint-plugin-react@^7.27.1, eslint-plugin-react@^7.29.4: +eslint-plugin-react@7.29.4, eslint-plugin-react@^7.27.1: version "7.29.4" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz#4717de5227f55f3801a5fd51a16a4fa22b5914d2" integrity sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ== @@ -5573,7 +5573,7 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^3.1.1" -eslint@^8.12.0, eslint@^8.3.0: +eslint@8.12.0, eslint@^8.3.0: version "8.12.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.12.0.tgz#c7a5bd1cfa09079aae64c9076c07eada66a46e8e" integrity sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q== @@ -8907,15 +8907,7 @@ postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^7.0.35: - version "7.0.39" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" - integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== - dependencies: - picocolors "^0.2.1" - source-map "^0.6.1" - -postcss@^8.3.5, postcss@^8.4.12, postcss@^8.4.4, postcss@^8.4.6, postcss@^8.4.7: +postcss@8.4.12, postcss@^8.3.5, postcss@^8.4.4, postcss@^8.4.6, postcss@^8.4.7: version "8.4.12" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== @@ -8924,6 +8916,14 @@ postcss@^8.3.5, postcss@^8.4.12, postcss@^8.4.4, postcss@^8.4.6, postcss@^8.4.7: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^7.0.35: + version "7.0.39" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -9021,6 +9021,15 @@ prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" +prop-types@15.8.1, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + prop-types@^15.6.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -9030,15 +9039,6 @@ prop-types@^15.6.2: object-assign "^4.1.1" react-is "^16.8.1" -prop-types@^15.8.1: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -9632,7 +9632,7 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-joyride@^2.4.0: +react-joyride@2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/react-joyride/-/react-joyride-2.4.0.tgz#273a99fea4804a48155e7cc7bae308dcbc8cb725" integrity sha512-U0uDFspaAEZucsvYpEIEPnoWD0QwTFk06IgIlinmTDPHgoS+V0q16w1+JqHeEKkR8Q79DWEeBIJYMvCJ7jT2EQ== @@ -9687,7 +9687,7 @@ react-router@5.3.3: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-scripts@^5.0.1: +react-scripts@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.1.tgz#6285dbd65a8ba6e49ca8d651ce30645a6d980003" integrity sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ== @@ -10286,10 +10286,10 @@ set-cookie-parser@^2.4.6: resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz#d0da0ed388bc8f24e706a391f9c9e252a13c58b2" integrity sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg== -setimmediate@^1.0.5: +setimmediate@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== setprototypeof@1.1.0: version "1.1.0" @@ -11037,7 +11037,7 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.6.3: +typescript@4.6.3: version "4.6.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c" integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== From df1fe81c198f9634c1726fc3bca7c652932ba715 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Sep 2022 18:38:08 -0700 Subject: [PATCH 03/65] Update dependency @types/jest to v28.1.8 (#462) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- frontend/package.json | 2 +- frontend/yarn.lock | 76 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index e2ba16f20..ccdfb488d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -87,7 +87,7 @@ "@testing-library/react": "11.2.6", "@testing-library/user-event": "13.1.2", "@types/humps": "2.0.0", - "@types/jest": "28.1.6", + "@types/jest": "28.1.8", "@types/node": "14.14.37", "@types/react": "17.0.3", "@types/react-dom": "17.0.3", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 8594670f5..f0d3065bd 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2265,6 +2265,13 @@ "@types/node" "*" jest-mock "^27.5.1" +"@jest/expect-utils@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" + integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== + dependencies: + jest-get-type "^28.0.2" + "@jest/fake-timers@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" @@ -2396,6 +2403,18 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jest/types@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" + integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== + dependencies: + "@jest/schemas" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.3.0": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" @@ -2978,12 +2997,12 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" -"@types/jest@28.1.6": - version "28.1.6" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-28.1.6.tgz#d6a9cdd38967d2d746861fb5be6b120e38284dd4" - integrity sha512-0RbGAFMfcBJKOmqRazM8L98uokwuwD5F8rHrv/ZMbrZBwVOWZUyPG6VFNscjYr/vjM3Vu4fRrCPbOs42AfemaQ== +"@types/jest@28.1.8": + version "28.1.8" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-28.1.8.tgz#6936409f3c9724ea431efd412ea0238a0f03b09b" + integrity sha512-8TJkV++s7B6XqnDrzR1m/TT0A0h948Pnl/097veySPN67VRAgQ4gZ7n2KfJo2rVq6njQjdxU3GCCyDvAeuHoiw== dependencies: - jest-matcher-utils "^28.0.0" + expect "^28.0.0" pretty-format "^28.0.0" "@types/js-levenshtein@^1.1.0": @@ -3198,6 +3217,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yargs@^17.0.8": + version "17.0.12" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.12.tgz#0745ff3e4872b4ace98616d4b7e37ccbd75f9526" + integrity sha512-Nz4MPhecOFArtm81gFQvQqdV7XYCrWKx5uUt6GNHredFHn1i2mtWqXTON7EPXMtNi1qjtjEM/VCHDhcHsAMLXQ== + dependencies: + "@types/yargs-parser" "*" + "@typescript-eslint/eslint-plugin@5.30.7": version "5.30.7" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.7.tgz#1621dabc1ae4084310e19e9efc80dfdbb97e7493" @@ -5717,6 +5743,17 @@ expect@^27.5.1: jest-matcher-utils "^27.5.1" jest-message-util "^27.5.1" +expect@^28.0.0: + version "28.1.3" + resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" + integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== + dependencies: + "@jest/expect-utils" "^28.1.3" + jest-get-type "^28.0.2" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + express@^4.17.3: version "4.17.3" resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" @@ -7056,7 +7093,7 @@ jest-matcher-utils@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-matcher-utils@^28.0.0: +jest-matcher-utils@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== @@ -7081,6 +7118,21 @@ jest-message-util@^27.5.1: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" + integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^28.1.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^28.1.3" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" @@ -7227,6 +7279,18 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" + integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" From 9d4fc4a7d3819724592fa381fa71c6672fa37406 Mon Sep 17 00:00:00 2001 From: Sasha Ames Date: Thu, 13 Oct 2022 15:33:42 -0700 Subject: [PATCH 04/65] Support backend settings in config (#482) * enable settings on the backend for urls * add the settings for workflow * add missing import * add newline --- .github/workflows/backend.yml | 4 ++++ backend/config/settings/base.py | 5 +++++ backend/metagrid/api_proxy/views.py | 26 ++++++++++++++++++++------ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml index 338b41cfd..ffa942ac2 100644 --- a/.github/workflows/backend.yml +++ b/.github/workflows/backend.yml @@ -17,6 +17,10 @@ env: KEYCLOAK_REALM: metagrid KEYCLOAK_CLIENT_ID: backend DATABASE_URL: pgsql://postgres:postgres@localhost:5432/postgres + REACT_APP_ESGF_NODE_URL: https://esgf-node.llnl.gov/esg-search/search + REACT_APP_WGET_API_URL: https://greyworm1-rh7.llnl.gov/wget + REACT_APP_ESGF_NODE_STATUS_URL: https://aims4.llnl.gov/prometheus/api/v1/query?query=probe_success%7Bjob%3D%22http_2xx%22%2C+target%3D~%22.%2Athredds.%2A%22%7D + jobs: build: diff --git a/backend/config/settings/base.py b/backend/config/settings/base.py index 197c4ff48..da674717f 100755 --- a/backend/config/settings/base.py +++ b/backend/config/settings/base.py @@ -296,3 +296,8 @@ # https://github.com/adamchainz/django-cors-headers#setup CORS_ORIGIN_ALLOW_ALL = False CORS_ORIGIN_WHITELIST = env.list("CORS_ORIGIN_WHITELIST") + + +SEARCH_URL = env("REACT_APP_ESGF_NODE_URL") +WGET_URL = env("REACT_APP_WGET_API_URL") +STATUS_URL = env("REACT_APP_ESGF_NODE_STATUS_URL") diff --git a/backend/metagrid/api_proxy/views.py b/backend/metagrid/api_proxy/views.py index 0ffb77d9c..cc9d14e1d 100644 --- a/backend/metagrid/api_proxy/views.py +++ b/backend/metagrid/api_proxy/views.py @@ -2,6 +2,7 @@ from urllib.parse import urlparse import requests +from django.conf import settings from django.http import HttpResponse, HttpResponseBadRequest from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods @@ -10,9 +11,12 @@ @require_http_methods(["GET", "POST"]) @csrf_exempt def do_search(request): - - return do_request(request, "https://esgf-node.llnl.gov/esg-search/search") - + esgf_host = getattr( + settings, + "REACT_APP_SEARCH_URL", + "https://esgf-node.llnl.gov/esg-search/search", + ) + return do_request(request, esgf_host) @require_http_methods(["POST"]) @csrf_exempt @@ -49,9 +53,12 @@ def do_citation(request): @require_http_methods(["GET", "POST"]) @csrf_exempt def do_status(request): - resp = requests.get( - "https://aims4.llnl.gov/prometheus/api/v1/query?query=probe_success%7Bjob%3D%22http_2xx%22%2C+target%3D~%22.%2Athredds.%2A%22%7D" + status_url = getattr( + settings, + "REACT_APP_ESGF_NODE_STATUS_URL", + "https://aims4.llnl.gov/prometheus/api/v1/query?query=probe_success%7Bjob%3D%22http_2xx%22%2C+target%3D~%22.%2Athredds.%2A%22%7D", ) + resp = requests.get(status_url) if resp.status_code == 200: # pragma: no cover return HttpResponse(resp.text) else: # pragma: no cover @@ -61,7 +68,14 @@ def do_status(request): @require_http_methods(["GET", "POST"]) @csrf_exempt def do_wget(request): - return do_request(request, "https://esgf-node.llnl.gov/esg-search/wget") + return do_request( + request, + getattr( + settings, + "REACT_APP_WGET_API_URL", + "https://esgf-node.llnl.gov/esg-search/wget", + ), + ) def do_request(request, urlbase): From 6c029e354b7561420f61965ecc8844cded5f8ba9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Oct 2022 18:15:04 -0700 Subject: [PATCH 05/65] Update react monorepo to v18 (#478) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- frontend/package.json | 4 ++-- frontend/yarn.lock | 29 +++++++++++++---------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index ccdfb488d..734567eec 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -70,9 +70,9 @@ "moment": "2.29.4", "prop-types": "15.8.1", "query-string": "7.0.0", - "react": "17.0.2", + "react": "18.2.0", "react-async": "10.0.1", - "react-dom": "17.0.2", + "react-dom": "18.2.0", "react-hotjar": "2.2.1", "react-joyride": "2.4.0", "react-router-dom": "5.3.3", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index f0d3065bd..c7d267bf5 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -9645,14 +9645,13 @@ react-dev-utils@^12.0.1: strip-ansi "^6.0.1" text-table "^0.2.0" -react-dom@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== +react-dom@18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" + scheduler "^0.23.0" react-error-overlay@^6.0.11: version "6.0.11" @@ -9806,13 +9805,12 @@ react-scripts@5.0.1: optionalDependencies: fsevents "^2.3.2" -react@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react@18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" readable-stream@^2.0.1: version "2.3.7" @@ -10172,13 +10170,12 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" schema-utils@2.7.0: version "2.7.0" From 7b35d0663cad9ccc9a12ad76c21e5707b5fcf081 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:40:55 -0700 Subject: [PATCH 06/65] Pin dependencies (#477) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> From 9bad38bc40505e964759a89df51edceb3b0b46e2 Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Tue, 25 Oct 2022 15:09:06 -0700 Subject: [PATCH 07/65] Fixed tiny flake8 complaint --- backend/metagrid/api_proxy/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/metagrid/api_proxy/views.py b/backend/metagrid/api_proxy/views.py index cc9d14e1d..9724f75e3 100644 --- a/backend/metagrid/api_proxy/views.py +++ b/backend/metagrid/api_proxy/views.py @@ -18,6 +18,7 @@ def do_search(request): ) return do_request(request, esgf_host) + @require_http_methods(["POST"]) @csrf_exempt def do_citation(request): From 31f34d77c04be7fd9d5cbd56e3c59b7856ce405e Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Wed, 26 Oct 2022 18:37:37 -0700 Subject: [PATCH 08/65] hotfix to the last update so that local containers have the required environment variables. Otherwise local build of backend will fail. --- backend/.envs/.django | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/backend/.envs/.django b/backend/.envs/.django index 3d53483b2..f0a656158 100644 --- a/backend/.envs/.django +++ b/backend/.envs/.django @@ -12,3 +12,15 @@ CORS_ORIGIN_WHITELIST=http://localhost:3000 KEYCLOAK_URL=http://keycloak:8080/auth KEYCLOAK_REALM=metagrid KEYCLOAK_CLIENT_ID=backend + +# ESGF wget API +# https://github.com/ESGF/esgf-wget +REACT_APP_WGET_API_URL=https://pcmdi8vm.llnl.gov/wget + +# ESGF Search API +# https://esgf.github.io/esg-search/ESGF_Search_RESTful_API.html +REACT_APP_ESGF_NODE_URL=https://esgf-node.llnl.gov/esg-search/search + +# ESGF Node Status API +# https://github.com/ESGF/esgf-utils/blob/master/node_status/query_prom.py +REACT_APP_ESGF_NODE_STATUS_URL=https://aims4.llnl.gov/prometheus/api/v1/query?query=probe_success%7Bjob%3D%22http_2xx%22%2C+target%3D~%22.%2Athredds.%2A%22%7D From 9f95688f10e58d006a4648a29838984a76c2d6f1 Mon Sep 17 00:00:00 2001 From: Sasha Ames Date: Wed, 2 Nov 2022 12:02:08 -0700 Subject: [PATCH 09/65] Update .django --- backend/.envs/.django | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/.envs/.django b/backend/.envs/.django index f0a656158..d1d6ba6b0 100644 --- a/backend/.envs/.django +++ b/backend/.envs/.django @@ -15,7 +15,7 @@ KEYCLOAK_CLIENT_ID=backend # ESGF wget API # https://github.com/ESGF/esgf-wget -REACT_APP_WGET_API_URL=https://pcmdi8vm.llnl.gov/wget +REACT_APP_WGET_API_URL=https://nimbus3.llnl.gov/wget # ESGF Search API # https://esgf.github.io/esg-search/ESGF_Search_RESTful_API.html @@ -23,4 +23,4 @@ REACT_APP_ESGF_NODE_URL=https://esgf-node.llnl.gov/esg-search/search # ESGF Node Status API # https://github.com/ESGF/esgf-utils/blob/master/node_status/query_prom.py -REACT_APP_ESGF_NODE_STATUS_URL=https://aims4.llnl.gov/prometheus/api/v1/query?query=probe_success%7Bjob%3D%22http_2xx%22%2C+target%3D~%22.%2Athredds.%2A%22%7D +REACT_APP_ESGF_NODE_STATUS_URL=https://aims2.llnl.gov/metagrid-backend/proxy/status From 7d50069efbcd5fbd6478f321e81704f3e405706a Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Thu, 16 Mar 2023 21:03:22 -0700 Subject: [PATCH 10/65] Updating a few dependencies in the backend --- backend/requirements/base.txt | 4 ++-- backend/requirements/local.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/requirements/base.txt b/backend/requirements/base.txt index 4da005046..9333dd66a 100644 --- a/backend/requirements/base.txt +++ b/backend/requirements/base.txt @@ -1,7 +1,7 @@ # Core # ------------------------------------------------------------------------------ pytz==2022.1 # https://github.com/stub42/pytz -django==3.2.15 # https://www.djangoproject.com/ +django==3.2.18 # https://www.djangoproject.com/ django-environ==0.8.1 # https://github.com/joke2k/django-environ gunicorn==20.1.0 # https://github.com/benoitc/gunicorn newrelic==7.6.0.173 # https://pypi.org/project/newrelic/ @@ -11,7 +11,7 @@ whitenoise==5.3.0 # https://github.com/evansd/whitenoise # Database # ------------------------------------------------------------------------------ -psycopg2-binary==2.9.3 # https://github.com/psycopg/psycopg2 +psycopg2-binary==2.9.5 # https://github.com/psycopg/psycopg2 # REST API # ------------------------------------------------------------------------------ diff --git a/backend/requirements/local.txt b/backend/requirements/local.txt index 023c91970..543c4cc81 100644 --- a/backend/requirements/local.txt +++ b/backend/requirements/local.txt @@ -20,5 +20,5 @@ factory-boy==3.2.1 # https://github.com/FactoryBoy/factory_boy # Developer Tools # ------------------------------------------------------------------------------ ipdb==0.13.9 # https://github.com/gotcha/ipdb -ipython==7.31.1 # https://github.com/ipython/ipython -django-extensions==3.1.5 # https://github.com/django-extensions/django-extensions +ipython==8.11.0 # https://github.com/ipython/ipython +django-extensions==3.2.1 # https://github.com/django-extensions/django-extensions From f0fee9aef7a75b9c8a6f1a962c4d9d7aec976771 Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Thu, 16 Mar 2023 21:12:29 -0700 Subject: [PATCH 11/65] Updating the Flake8 pre-commit config file to see if that resolves pre-commit issues... --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5310ff047..3276b46c1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,8 +12,8 @@ repos: # Back-end # ------------------------------------------------------------------------------ - - repo: https://gitlab.com/pycqa/flake8 - rev: 3.9.0 + - repo: https://github.com/pycqa/flake8 + rev: 3.9.2 hooks: - id: flake8 args: ["--config=backend/setup.cfg"] From c01f64dc5a3c3fc274ff57e4646e125b1de671b9 Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Thu, 16 Mar 2023 22:25:35 -0700 Subject: [PATCH 12/65] Fixed typo in .coveragerc which caused migrations to be included in tests. Removed unneccessary type checking in the import headers of the test models. Updated coverage for some files that didn't have 100% coverage. Updated some django dependencies after testing to make sure nothing broke. Updated some deprecated imports to remove warnings. Updated pytest and coverage libraries. --- backend/.coveragerc | 4 ++-- .../metagrid/cart/migrations/0001_initial.py | 7 ++++--- backend/metagrid/cart/tests/factories.py | 4 ++-- backend/metagrid/cart/tests/test_models.py | 6 +----- .../projects/migrations/0001_initial.py | 2 +- .../migrations/scripts/import_functions.py | 6 +----- backend/metagrid/projects/tests/test_models.py | 7 +------ backend/metagrid/users/admin.py | 2 +- .../metagrid/users/migrations/0001_initial.py | 6 ++++-- backend/metagrid/users/models.py | 2 +- backend/requirements/local.txt | 18 +++++++++--------- 11 files changed, 27 insertions(+), 37 deletions(-) diff --git a/backend/.coveragerc b/backend/.coveragerc index 013c1eda4..6d00222ba 100644 --- a/backend/.coveragerc +++ b/backend/.coveragerc @@ -5,8 +5,8 @@ omit = *urls.py, *manage.py, *wsgi.py*, - *docs/* - *migrations/*, + *docs/*, + */migrations/*, *settings*, *postgres-data/*, *tests/*, diff --git a/backend/metagrid/cart/migrations/0001_initial.py b/backend/metagrid/cart/migrations/0001_initial.py index 7abc58cd5..08f8888d6 100644 --- a/backend/metagrid/cart/migrations/0001_initial.py +++ b/backend/metagrid/cart/migrations/0001_initial.py @@ -1,10 +1,11 @@ # Generated by Django 3.2.13 on 2022-06-01 02:13 -from django.conf import settings +import uuid + import django.contrib.postgres.fields -from django.db import migrations, models import django.db.models.deletion -import uuid +from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/backend/metagrid/cart/tests/factories.py b/backend/metagrid/cart/tests/factories.py index 20cedce60..4e9730f57 100644 --- a/backend/metagrid/cart/tests/factories.py +++ b/backend/metagrid/cart/tests/factories.py @@ -14,8 +14,8 @@ class JSONFactory(factory.DictFactory): @classmethod def _generate(cls, create, attrs): - obj = super()._generate(create, attrs) - return json.dumps(obj) + obj = super()._generate(create, attrs) # pragma: no cover + return json.dumps(obj) # pragma: no cover class CartFactory(factory.django.DjangoModelFactory): diff --git a/backend/metagrid/cart/tests/test_models.py b/backend/metagrid/cart/tests/test_models.py index df936a7b5..bf55375bd 100644 --- a/backend/metagrid/cart/tests/test_models.py +++ b/backend/metagrid/cart/tests/test_models.py @@ -1,10 +1,6 @@ -from typing import TYPE_CHECKING - +from metagrid.cart.models import Cart, Search from metagrid.cart.tests.factories import CartFactory, SearchFactory -if TYPE_CHECKING: - from metagrid.cart.models import Cart, Search - class TestCart: def test__str__(self): diff --git a/backend/metagrid/projects/migrations/0001_initial.py b/backend/metagrid/projects/migrations/0001_initial.py index fbaec3d32..cf4dd88a7 100644 --- a/backend/metagrid/projects/migrations/0001_initial.py +++ b/backend/metagrid/projects/migrations/0001_initial.py @@ -1,7 +1,7 @@ # Generated by Django 3.2.13 on 2022-05-13 03:10 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/backend/metagrid/projects/migrations/scripts/import_functions.py b/backend/metagrid/projects/migrations/scripts/import_functions.py index be2e463a1..b82b66ca7 100644 --- a/backend/metagrid/projects/migrations/scripts/import_functions.py +++ b/backend/metagrid/projects/migrations/scripts/import_functions.py @@ -8,11 +8,7 @@ ProjectFacet, ) - -from metagrid.initial_projects_data import ( - projects, - group_descriptions, -) +from metagrid.initial_projects_data import group_descriptions, projects def insert_data(apps, schema_editor): diff --git a/backend/metagrid/projects/tests/test_models.py b/backend/metagrid/projects/tests/test_models.py index 0a992ad50..8034d92c6 100644 --- a/backend/metagrid/projects/tests/test_models.py +++ b/backend/metagrid/projects/tests/test_models.py @@ -1,8 +1,6 @@ -from typing import TYPE_CHECKING - import pytest -from metagrid.projects.models import ProjectFacet +from metagrid.projects.models import Facet, FacetGroup, Project, ProjectFacet from metagrid.projects.tests.factories import ( FacetFactory, FacetGroupFactory, @@ -12,9 +10,6 @@ pytestmark = pytest.mark.django_db -if TYPE_CHECKING: - from metagrid.projects.models import Facet, FacetGroup, Project - class TestProjectModel: @pytest.fixture(autouse=True) diff --git a/backend/metagrid/users/admin.py b/backend/metagrid/users/admin.py index 916bf4621..f825c972a 100644 --- a/backend/metagrid/users/admin.py +++ b/backend/metagrid/users/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from .models import User diff --git a/backend/metagrid/users/migrations/0001_initial.py b/backend/metagrid/users/migrations/0001_initial.py index 4d9424193..5f591751e 100644 --- a/backend/metagrid/users/migrations/0001_initial.py +++ b/backend/metagrid/users/migrations/0001_initial.py @@ -1,9 +1,11 @@ # Generated by Django 3.2.13 on 2022-06-01 02:56 -from django.db import migrations, models +import uuid + import django.utils.timezone +from django.db import migrations, models + import metagrid.users.models -import uuid class Migration(migrations.Migration): diff --git a/backend/metagrid/users/models.py b/backend/metagrid/users/models.py index 2ca3b058c..b7f28626a 100644 --- a/backend/metagrid/users/models.py +++ b/backend/metagrid/users/models.py @@ -3,7 +3,7 @@ from django.contrib.auth.models import AbstractUser, BaseUserManager from django.db import models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from metagrid.cart.models import Cart diff --git a/backend/requirements/local.txt b/backend/requirements/local.txt index 543c4cc81..5bf32df78 100644 --- a/backend/requirements/local.txt +++ b/backend/requirements/local.txt @@ -2,23 +2,23 @@ # Code quality # ------------------------------------------------------------------------------ -flake8==3.9.2 # https://github.com/PyCQA/flake8 -flake8-isort==4.1.1 # https://github.com/gforcada/flake8-isort -black==20.8b1 # https://github.com/ambv/black -mypy==0.931 # https://github.com/python/mypy +flake8==6.0.0 # https://github.com/PyCQA/flake8 +flake8-isort==6.0.0 # https://github.com/gforcada/flake8-isort +black==23.1.0 # https://github.com/ambv/black +mypy==1.0.1 # https://github.com/python/mypy # Testing # ------------------------------------------------------------------------------ -django-stubs==1.9.0 # https://github.com/typeddjango/django-stubs -pytest==6.2.5 # https://github.com/pytest-dev/pytest -pytest-cov==2.12.1 # https://github.com/pytest-dev/pytest-cov +django-stubs==1.15.0 # https://github.com/typeddjango/django-stubs +pytest==7.2.1 # https://github.com/pytest-dev/pytest +pytest-cov==4.0.0 # https://github.com/pytest-dev/pytest-cov pytest-django==4.5.2 # https://github.com/pytest-dev/pytest-django -pytest-sugar==0.9.4 # https://github.com/Frozenball/pytest-sugar +pytest-sugar==0.9.6 # https://github.com/Frozenball/pytest-sugar pytest-watch==4.2.0 # https://github.com/joeyespo/pytest-watch factory-boy==3.2.1 # https://github.com/FactoryBoy/factory_boy # Developer Tools # ------------------------------------------------------------------------------ -ipdb==0.13.9 # https://github.com/gotcha/ipdb +ipdb==0.13.11 # https://github.com/gotcha/ipdb ipython==8.11.0 # https://github.com/ipython/ipython django-extensions==3.2.1 # https://github.com/django-extensions/django-extensions From 086dc61153f9d183227811fd5856b6f460820a41 Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Fri, 17 Mar 2023 12:57:45 -0700 Subject: [PATCH 13/65] Upgraded to Django 4.1.7, along with some other python packages. --- backend/requirements/base.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/requirements/base.txt b/backend/requirements/base.txt index 9333dd66a..997d2763d 100644 --- a/backend/requirements/base.txt +++ b/backend/requirements/base.txt @@ -1,13 +1,13 @@ # Core # ------------------------------------------------------------------------------ -pytz==2022.1 # https://github.com/stub42/pytz -django==3.2.18 # https://www.djangoproject.com/ -django-environ==0.8.1 # https://github.com/joke2k/django-environ +pytz==2022.7.1 # https://github.com/stub42/pytz +django==4.1.7 # https://www.djangoproject.com/ +django-environ==0.9.0 # https://github.com/joke2k/django-environ gunicorn==20.1.0 # https://github.com/benoitc/gunicorn -newrelic==7.6.0.173 # https://pypi.org/project/newrelic/ -argon2-cffi==20.1.0 # https://github.com/hynek/argon2_cffi -requests==2.27.1 # https://github.com/psf/requests -whitenoise==5.3.0 # https://github.com/evansd/whitenoise +newrelic==8.7.0 # https://pypi.org/project/newrelic/ +argon2-cffi==21.3 # https://github.com/hynek/argon2_cffi +requests==2.28.2 # https://github.com/psf/requests +whitenoise==6.4.0 # https://github.com/evansd/whitenoise # Database # ------------------------------------------------------------------------------ From a071e6a990580fd6e46c73079a8e7fbcf7d657f2 Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Fri, 17 Mar 2023 13:11:46 -0700 Subject: [PATCH 14/65] Upgraded more python packages while testing to make sure nothing breaks. --- backend/requirements/base.txt | 20 ++++++++++---------- docs/requirements.txt | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/requirements/base.txt b/backend/requirements/base.txt index 997d2763d..bf513c252 100644 --- a/backend/requirements/base.txt +++ b/backend/requirements/base.txt @@ -15,20 +15,20 @@ psycopg2-binary==2.9.5 # https://github.com/psycopg/psycopg2 # REST API # ------------------------------------------------------------------------------ -djangorestframework==3.13.1 # https://github.com/encode/django-rest-framework -Markdown==3.3.6 # https://pypi.org/project/Markdown/ -django-filter==2.4.0 # https://github.com/carltongibson/django-filter -django-cors-headers==3.11.0 # https://github.com/adamchainz/django-cors-headers -django-allauth==0.47.0 # https://github.com/pennersr/django-allauth -dj-rest-auth==2.2.2 # https://github.com/jazzband/dj-rest-auth -djangorestframework-simplejwt==4.8.0 # https://github.com/SimpleJWT/django-rest-framework-simplejwt/ -drf-yasg==1.20.0 # https://github.com/axnsan12/drf-yasg +djangorestframework==3.14.0 # https://github.com/encode/django-rest-framework +Markdown==3.4.1 # https://pypi.org/project/Markdown/ +django-filter==22.1 # https://github.com/carltongibson/django-filter +django-cors-headers==3.14.0 # https://github.com/adamchainz/django-cors-headers +django-allauth==0.52.0 # https://github.com/pennersr/django-allauth +dj-rest-auth==3.0.0 # https://github.com/jazzband/dj-rest-auth +djangorestframework-simplejwt==5.2.2 # https://github.com/SimpleJWT/django-rest-framework-simplejwt/ +drf-yasg==1.21.5 # https://github.com/axnsan12/drf-yasg # Code quality # ------------------------------------------------------------------------------ -pre-commit==2.17.0 # https://github.com/pre-commit/pre-commit +pre-commit==3.1.1 # https://github.com/pre-commit/pre-commit # Model Tools # ------------------------------------------------------------------------------ -django-model-utils==4.2.0 # https://github.com/jazzband/django-model-utils +django-model-utils==4.3.1 # https://github.com/jazzband/django-model-utils django_unique_upload==0.2.1 # https://github.com/agconti/django-unique-upload diff --git a/docs/requirements.txt b/docs/requirements.txt index 6512ec63f..44d40820b 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,2 @@ -mkdocs==1.3.1 # https://www.mkdocs.org/ +mkdocs==1.4.2 # https://www.mkdocs.org/ mdx_truly_sane_lists==1.3 # https://github.com/radude/mdx_truly_sane_lists From 5d43c9cd532adb0eecc926d3b4b70f33b065fbbf Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:18:44 -0700 Subject: [PATCH 15/65] Reverted dj-rest-auth as it caused issues with tests failing (although the application from users perspective was working fine) --- backend/requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements/base.txt b/backend/requirements/base.txt index bf513c252..029f30764 100644 --- a/backend/requirements/base.txt +++ b/backend/requirements/base.txt @@ -20,7 +20,7 @@ Markdown==3.4.1 # https://pypi.org/project/Markdown/ django-filter==22.1 # https://github.com/carltongibson/django-filter django-cors-headers==3.14.0 # https://github.com/adamchainz/django-cors-headers django-allauth==0.52.0 # https://github.com/pennersr/django-allauth -dj-rest-auth==3.0.0 # https://github.com/jazzband/dj-rest-auth +dj-rest-auth==2.2.2 # https://github.com/jazzband/dj-rest-auth djangorestframework-simplejwt==5.2.2 # https://github.com/SimpleJWT/django-rest-framework-simplejwt/ drf-yasg==1.21.5 # https://github.com/axnsan12/drf-yasg From 7510ad616d9ee8ec9088f3a1956d46c02edd3d8c Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:19:53 -0700 Subject: [PATCH 16/65] Updated dj-rest-auth to latest (before breaking changes) --- backend/requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/requirements/base.txt b/backend/requirements/base.txt index 029f30764..8cdeb6c6e 100644 --- a/backend/requirements/base.txt +++ b/backend/requirements/base.txt @@ -20,7 +20,7 @@ Markdown==3.4.1 # https://pypi.org/project/Markdown/ django-filter==22.1 # https://github.com/carltongibson/django-filter django-cors-headers==3.14.0 # https://github.com/adamchainz/django-cors-headers django-allauth==0.52.0 # https://github.com/pennersr/django-allauth -dj-rest-auth==2.2.2 # https://github.com/jazzband/dj-rest-auth +dj-rest-auth==2.2.8 # https://github.com/jazzband/dj-rest-auth djangorestframework-simplejwt==5.2.2 # https://github.com/SimpleJWT/django-rest-framework-simplejwt/ drf-yasg==1.21.5 # https://github.com/axnsan12/drf-yasg From 55158b02afc346cd384c21f108f8ff9ddc01411d Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Tue, 21 Mar 2023 19:31:16 -0700 Subject: [PATCH 17/65] Updating front-end to use the latest react-router-dom major version 6 --- frontend/package.json | 2 +- frontend/src/components/App/App.tsx | 92 ++++++++------ frontend/src/components/Cart/SearchesCard.tsx | 8 +- frontend/src/components/Cart/index.tsx | 13 +- frontend/src/components/NavBar/LeftMenu.tsx | 11 +- frontend/src/contexts/ReactJoyrideContext.tsx | 19 +-- frontend/yarn.lock | 118 ++++-------------- 7 files changed, 113 insertions(+), 150 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 734567eec..8d6797053 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -75,7 +75,7 @@ "react-dom": "18.2.0", "react-hotjar": "2.2.1", "react-joyride": "2.4.0", - "react-router-dom": "5.3.3", + "react-router-dom": "^6.9.0", "react-scripts": "5.0.1", "typescript": "4.6.3", "uuid": "8.3.2" diff --git a/frontend/src/components/App/App.tsx b/frontend/src/components/App/App.tsx index ad6f02761..5c45d59d4 100644 --- a/frontend/src/components/App/App.tsx +++ b/frontend/src/components/App/App.tsx @@ -11,7 +11,7 @@ import { Affix, Breadcrumb, Button, Layout, message, Result } from 'antd'; import React, { ReactElement } from 'react'; import { useAsync } from 'react-async'; import { hotjar } from 'react-hotjar'; -import { Link, Redirect, Route, Switch } from 'react-router-dom'; +import { Link, Navigate, Route, Routes } from 'react-router-dom'; import { v4 as uuidv4 } from 'uuid'; import { addUserSearchQuery, @@ -477,7 +477,8 @@ const App: React.FC = ({ searchQuery }) => { /* istanbul ignore next */ if (!publicUrl && previousPublicUrl) { const newFrom = `/${previousPublicUrl}`; - return ; + // return ; + } />; } return <>; @@ -485,28 +486,32 @@ const App: React.FC = ({ searchQuery }) => { return ( <> - - - + + } /> + } /> + {/* + */} {generateRedirects()} - +
- ( - - )} - /> + + + } + /> + - + ( + element={ = ({ searchQuery }) => { onSetActiveFacets={handleOnSetActiveFacets} /> - )} + } /> ( + element={ - )} + } /> ( + path="/cart/*" + element={ - )} + } /> - + - + ( + element={ <> @@ -573,11 +578,11 @@ const App: React.FC = ({ searchQuery }) => { onShareSearchQuery={handleShareSearchQuery} > - )} + } /> ( + element={ <> @@ -591,11 +596,11 @@ const App: React.FC = ({ searchQuery }) => { isLoading={nodeStatusIsLoading} /> - )} + } /> ( + path="/cart/*" + element={ <> @@ -612,9 +617,9 @@ const App: React.FC = ({ searchQuery }) => { onRemoveSearchQuery={handleRemoveSearchQuery} /> - )} + } /> - ( = ({ searchQuery }) => { } /> )} + /> */} + + Back to Home + + } + /> + } /> - +

diff --git a/frontend/src/components/Cart/SearchesCard.tsx b/frontend/src/components/Cart/SearchesCard.tsx index 58a645a47..742e2eec4 100644 --- a/frontend/src/components/Cart/SearchesCard.tsx +++ b/frontend/src/components/Cart/SearchesCard.tsx @@ -7,7 +7,7 @@ import { import { Col, Typography } from 'antd'; import React from 'react'; import { useAsync } from 'react-async'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; import { fetchSearchResults, generateSearchURLQuery } from '../../api'; import { clickableRoute } from '../../api/routes'; import { savedSearchTourTargets } from '../../common/reactJoyrideSteps'; @@ -41,7 +41,8 @@ const SearchesCard: React.FC = ({ onRunSearchQuery, onRemoveSearchQuery, }) => { - const history = useHistory(); + // const history = useHistory(); + const navigate = useNavigate(); const { uuid, project, @@ -107,7 +108,8 @@ const SearchesCard: React.FC = ({ data-testid={`apply-${index + 1}`} key="search" onClick={() => { - history.push('/search'); + // history.push('/search'); + navigate('/search'); onRunSearchQuery(searchQuery); }} /> diff --git a/frontend/src/components/Cart/index.tsx b/frontend/src/components/Cart/index.tsx index 1fe4ab9e0..f548c7c90 100644 --- a/frontend/src/components/Cart/index.tsx +++ b/frontend/src/components/Cart/index.tsx @@ -1,7 +1,7 @@ import { BookOutlined, ShoppingCartOutlined } from '@ant-design/icons'; import { Tabs } from 'antd'; import React from 'react'; -import { useHistory } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import { cartTourTargets } from '../../common/reactJoyrideSteps'; import { RawSearchResults } from '../Search/types'; import Items from './Items'; @@ -26,18 +26,21 @@ const Cart: React.FC = ({ onRemoveSearchQuery, }) => { const [activeTab, setActiveTab] = React.useState('items'); - const history = useHistory(); + // const history = useHistory(); + const navigate = useNavigate(); + const location = useLocation(); React.useEffect(() => { - if (history.location.pathname.includes('searches')) { + if (location.pathname.includes('searches')) { setActiveTab('searches'); } else { setActiveTab('items'); } - }, [history.location.pathname]); + }, [location.pathname]); const handleTabClick = (key: string): void => { - history.push(key); + // history.push(key); + navigate(key); setActiveTab(key); }; diff --git a/frontend/src/components/NavBar/LeftMenu.tsx b/frontend/src/components/NavBar/LeftMenu.tsx index 5e3da6839..4d7fee4a1 100644 --- a/frontend/src/components/NavBar/LeftMenu.tsx +++ b/frontend/src/components/NavBar/LeftMenu.tsx @@ -1,7 +1,7 @@ import { SearchOutlined } from '@ant-design/icons'; import { Form, Input, Select, Spin } from 'antd'; import React from 'react'; -import { useHistory } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import { ResponseError } from '../../api'; import { navBarTargets } from '../../common/reactJoyrideSteps'; import { RawProject, RawProjects } from '../Facets/types'; @@ -28,7 +28,9 @@ const LeftMenu: React.FC = ({ }) => { const [form] = Form.useForm(); const [text, setText] = React.useState(''); - const history = useHistory(); + // const history = useHistory(); + const navigate = useNavigate(); + const location = useLocation(); /** * Sets the project and search value using the search form. @@ -36,8 +38,9 @@ const LeftMenu: React.FC = ({ */ const onFinish = (values: { [key: string]: string }): void => { /* istanbul ignore else */ - if (!history.location.pathname.endsWith('search')) { - history.push('/search'); + if (!location.pathname.endsWith('search')) { + // history.push('/search'); + navigate('/search'); } const selectedProj: RawProject | undefined = (projects as RawProjects).find( diff --git a/frontend/src/contexts/ReactJoyrideContext.tsx b/frontend/src/contexts/ReactJoyrideContext.tsx index 06c682278..4ee600671 100644 --- a/frontend/src/contexts/ReactJoyrideContext.tsx +++ b/frontend/src/contexts/ReactJoyrideContext.tsx @@ -1,6 +1,6 @@ import React from 'react'; import Joyride, { ACTIONS, CallBackProps, EVENTS, STATUS } from 'react-joyride'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; import { JoyrideTour } from '../common/JoyrideTour'; import { getCurrentAppPage } from '../common/reactJoyrideSteps'; import { AppPage } from '../common/types'; @@ -25,7 +25,8 @@ type Props = { children: React.ReactNode }; const defaultTour = new JoyrideTour('Empty Tour'); export const ReactJoyrideProvider: React.FC = ({ children }) => { - const history = useHistory(); + // const history = useHistory(); + const navigate = useNavigate(); const [running, setRunning] = React.useState(false); const [getTour, setTour] = React.useState(defaultTour); const [getStepIndex, setStepIndex] = React.useState(0); @@ -91,19 +92,23 @@ export const ReactJoyrideProvider: React.FC = ({ children }) => { const setCurrentAppPage = (page: AppPage): void => { if (getCurrentAppPage() !== page) { setTimeout(() => { - if (history) { + if (navigate) { switch (page) { case AppPage.Main: - history.push('/search'); + // history.push('/search'); + navigate('/search'); break; case AppPage.Cart: - history.push('/cart/items'); + // history.push('/cart/items'); + navigate('/cart/items'); break; case AppPage.NodeStatus: - history.push('/nodes'); + // history.push('/nodes'); + navigate('/nodes'); break; case AppPage.SavedSearches: - history.push('/cart/searches'); + // history.push('/cart/searches'); + navigate('/cart/searches'); break; default: } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index c7d267bf5..c6acc585e 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1972,14 +1972,14 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.0", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.0", "@babel/runtime@^7.9.2": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.12.13", "@babel/runtime@^7.16.3": +"@babel/runtime@^7.16.3": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== @@ -2543,6 +2543,11 @@ "@react-keycloak/core" "^3.2.0" hoist-non-react-statics "^3.3.2" +"@remix-run/router@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.4.0.tgz#74935d538e4df8893e47831a7aea362f295bcd39" + integrity sha512-BJ9SxXux8zAg991UmT8slpwpsd31K1dHHbD3Ba4VzD+liLQ4WAMSxQp2d2ZPRPfN0jN2NPRowcSSoM7lCaF08Q== + "@rollup/plugin-babel@^5.2.0": version "5.3.1" resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283" @@ -6274,19 +6279,7 @@ headers-utils@^3.0.2: resolved "https://registry.yarnpkg.com/headers-utils/-/headers-utils-3.0.2.tgz#dfc65feae4b0e34357308aefbcafa99c895e59ef" integrity sha512-xAxZkM1dRyGV2Ou5bzMxBPNLoRCjcX+ya7KSWybQD2KwLphxsapUVK6x/02o7f4VU6GPSXch9vNY2+gkU8tYWQ== -history@^4.9.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" - 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" - -hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -6790,11 +6783,6 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -7665,7 +7653,7 @@ lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -7826,14 +7814,6 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -mini-create-react-context@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" - integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== - dependencies: - "@babel/runtime" "^7.12.1" - tiny-warning "^1.0.3" - mini-css-extract-plugin@^2.4.5: version "2.6.0" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.0.tgz#578aebc7fc14d32c0ad304c2c34f08af44673f5e" @@ -8372,13 +8352,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -9094,15 +9067,6 @@ prop-types@15.8.1, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" -prop-types@^15.6.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.8.1" - proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -9680,7 +9644,7 @@ react-hotjar@2.2.1: resolved "https://registry.yarnpkg.com/react-hotjar/-/react-hotjar-2.2.1.tgz#5f06d15a146960262dba612657c828c8b58da8a3" integrity sha512-rkHdsFDfI7HCKeAwkHPf8Vq7ZbpX0oE4/5GgVvNambWw8z0PSKKxi59qWYX7H+dDYjchiL3z3hrAFQ+PohgQOA== -react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -9721,34 +9685,20 @@ react-refresh@^0.11.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== -react-router-dom@5.3.3: - version "5.3.3" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.3.tgz#8779fc28e6691d07afcaf98406d3812fe6f11199" - integrity sha512-Ov0tGPMBgqmbu5CDmN++tv2HQ9HlWDuWIIqn4b88gjlAN5IHI+4ZUZRcpz9Hl0azFIwihbLDYw1OiHGRo7ZIng== - dependencies: - "@babel/runtime" "^7.12.13" - history "^4.9.0" - loose-envify "^1.3.1" - prop-types "^15.6.2" - react-router "5.3.3" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - -react-router@5.3.3: - version "5.3.3" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.3.3.tgz#8e3841f4089e728cf82a429d92cdcaa5e4a3a288" - integrity sha512-mzQGUvS3bM84TnbtMYR8ZjKnuPJ71IjSzR+DE6UkUqvN4czWIqEs17yLL8xkAycv4ev0AiN+IGrWu88vJs/p2w== - dependencies: - "@babel/runtime" "^7.12.13" - 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" +react-router-dom@^6.9.0: + version "6.9.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.9.0.tgz#dd8b4e461453bd4cad2e6404493d1a5b4bfea758" + integrity sha512-/seUAPY01VAuwkGyVBPCn1OXfVbaWGGu4QN9uj0kCPcTyNYgL1ldZpxZUpRU7BLheKQI4Twtl/OW2nHRF1u26Q== + dependencies: + "@remix-run/router" "1.4.0" + react-router "6.9.0" + +react-router@6.9.0: + version "6.9.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.9.0.tgz#0f503d9becbc62d9e4ddc0f9bd4026e0fd29fbf5" + integrity sha512-51lKevGNUHrt6kLuX3e/ihrXoXCa9ixY/nVWRLlob4r/l0f45x3SzBvYJe3ctleLUQQ5fVa4RGgJOTH7D9Umhw== + dependencies: + "@remix-run/router" "1.4.0" react-scripts@5.0.1: version "5.0.1" @@ -10025,11 +9975,6 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-pathname@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" - integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== - resolve-url-loader@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz#d50d4ddc746bb10468443167acf800dcd6c3ad57" @@ -10922,16 +10867,6 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== -tiny-invariant@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" - integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== - -tiny-warning@^1.0.0, tiny-warning@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" - integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -11252,11 +11187,6 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" -value-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" - integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== - vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" From e44285f4459225c01515e1dcbe584a6e1b5e5a00 Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Tue, 21 Mar 2023 19:46:02 -0700 Subject: [PATCH 18/65] Removed some comments and updated some tests. Tests still failing, need to troubleshoot. --- frontend/src/components/App/App.tsx | 17 ----------------- .../src/components/Cart/SearchesCard.test.tsx | 6 +++--- frontend/src/components/Cart/SearchesCard.tsx | 2 -- frontend/src/components/Cart/index.test.tsx | 8 ++++---- frontend/src/components/Cart/index.tsx | 2 -- frontend/src/components/NavBar/LeftMenu.tsx | 2 -- frontend/src/contexts/ReactJoyrideContext.tsx | 5 ----- 7 files changed, 7 insertions(+), 35 deletions(-) diff --git a/frontend/src/components/App/App.tsx b/frontend/src/components/App/App.tsx index 5c45d59d4..c3484bdbd 100644 --- a/frontend/src/components/App/App.tsx +++ b/frontend/src/components/App/App.tsx @@ -477,7 +477,6 @@ const App: React.FC = ({ searchQuery }) => { /* istanbul ignore next */ if (!publicUrl && previousPublicUrl) { const newFrom = `/${previousPublicUrl}`; - // return ; } />; } @@ -489,8 +488,6 @@ const App: React.FC = ({ searchQuery }) => { } /> } /> - {/* - */} {generateRedirects()}

@@ -619,20 +616,6 @@ const App: React.FC = ({ searchQuery }) => { } /> - {/* ( - - Back to Home - - } - /> - )} - /> */} { - const mockHistoryPush = jest.fn(); + const mockNavigate = jest.fn(); jest.mock( 'react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), - useHistory: () => ({ - push: mockHistoryPush, + useNavigate: () => ({ + push: mockNavigate, }), } as Record) ); diff --git a/frontend/src/components/Cart/SearchesCard.tsx b/frontend/src/components/Cart/SearchesCard.tsx index 742e2eec4..9cdaa545f 100644 --- a/frontend/src/components/Cart/SearchesCard.tsx +++ b/frontend/src/components/Cart/SearchesCard.tsx @@ -41,7 +41,6 @@ const SearchesCard: React.FC = ({ onRunSearchQuery, onRemoveSearchQuery, }) => { - // const history = useHistory(); const navigate = useNavigate(); const { uuid, @@ -108,7 +107,6 @@ const SearchesCard: React.FC = ({ data-testid={`apply-${index + 1}`} key="search" onClick={() => { - // history.push('/search'); navigate('/search'); onRunSearchQuery(searchQuery); }} diff --git a/frontend/src/components/Cart/index.test.tsx b/frontend/src/components/Cart/index.test.tsx index 494171b7d..3de8e500f 100644 --- a/frontend/src/components/Cart/index.test.tsx +++ b/frontend/src/components/Cart/index.test.tsx @@ -16,16 +16,16 @@ const defaultProps: Props = { onRemoveSearchQuery: jest.fn(), }; -let mockHistoryPush: () => void; +let mockNavigate: () => void; beforeEach(() => { - mockHistoryPush = jest.fn(); + mockNavigate = jest.fn(); jest.mock( 'react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), - useHistory: () => ({ - push: mockHistoryPush, + useNavigate: () => ({ + push: mockNavigate, }), } as Record) ); diff --git a/frontend/src/components/Cart/index.tsx b/frontend/src/components/Cart/index.tsx index f548c7c90..d84c49110 100644 --- a/frontend/src/components/Cart/index.tsx +++ b/frontend/src/components/Cart/index.tsx @@ -26,7 +26,6 @@ const Cart: React.FC = ({ onRemoveSearchQuery, }) => { const [activeTab, setActiveTab] = React.useState('items'); - // const history = useHistory(); const navigate = useNavigate(); const location = useLocation(); @@ -39,7 +38,6 @@ const Cart: React.FC = ({ }, [location.pathname]); const handleTabClick = (key: string): void => { - // history.push(key); navigate(key); setActiveTab(key); }; diff --git a/frontend/src/components/NavBar/LeftMenu.tsx b/frontend/src/components/NavBar/LeftMenu.tsx index 4d7fee4a1..400cd68bc 100644 --- a/frontend/src/components/NavBar/LeftMenu.tsx +++ b/frontend/src/components/NavBar/LeftMenu.tsx @@ -28,7 +28,6 @@ const LeftMenu: React.FC = ({ }) => { const [form] = Form.useForm(); const [text, setText] = React.useState(''); - // const history = useHistory(); const navigate = useNavigate(); const location = useLocation(); @@ -39,7 +38,6 @@ const LeftMenu: React.FC = ({ const onFinish = (values: { [key: string]: string }): void => { /* istanbul ignore else */ if (!location.pathname.endsWith('search')) { - // history.push('/search'); navigate('/search'); } diff --git a/frontend/src/contexts/ReactJoyrideContext.tsx b/frontend/src/contexts/ReactJoyrideContext.tsx index 4ee600671..66c9ffcf9 100644 --- a/frontend/src/contexts/ReactJoyrideContext.tsx +++ b/frontend/src/contexts/ReactJoyrideContext.tsx @@ -25,7 +25,6 @@ type Props = { children: React.ReactNode }; const defaultTour = new JoyrideTour('Empty Tour'); export const ReactJoyrideProvider: React.FC = ({ children }) => { - // const history = useHistory(); const navigate = useNavigate(); const [running, setRunning] = React.useState(false); const [getTour, setTour] = React.useState(defaultTour); @@ -95,19 +94,15 @@ export const ReactJoyrideProvider: React.FC = ({ children }) => { if (navigate) { switch (page) { case AppPage.Main: - // history.push('/search'); navigate('/search'); break; case AppPage.Cart: - // history.push('/cart/items'); navigate('/cart/items'); break; case AppPage.NodeStatus: - // history.push('/nodes'); navigate('/nodes'); break; case AppPage.SavedSearches: - // history.push('/cart/searches'); navigate('/cart/searches'); break; default: From 113c68aba038ce882cec64175135a38bca688ef1 Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Tue, 28 Mar 2023 17:06:05 -0700 Subject: [PATCH 19/65] Updated tests to use memory router to account for the useNavigate changes that came with react-router-dom v6. Tests are passing now. --- frontend/.envs/.react | 20 +++++++++++----- frontend/src/components/App/App.tsx | 9 ++++---- .../src/components/Cart/SearchesCard.test.tsx | 7 ++++-- .../src/components/NavBar/RightMenu.test.tsx | 23 ++++--------------- frontend/src/components/NavBar/index.test.tsx | 19 +++------------ .../src/components/Support/index.test.tsx | 21 +++++++++++++---- .../src/contexts/ReactJoyrideContext.test.tsx | 11 +++++++-- frontend/src/index.tsx | 6 ++--- frontend/src/test/custom-render.tsx | 6 +++-- 9 files changed, 64 insertions(+), 58 deletions(-) diff --git a/frontend/.envs/.react b/frontend/.envs/.react index 4bb42ac7e..e619961b9 100644 --- a/frontend/.envs/.react +++ b/frontend/.envs/.react @@ -1,31 +1,39 @@ +# =====================FRONTEND CONFIG==================== + +# Redirect the frontend to home page when old subdirectory is used (optional) +REACT_APP_PREVIOUS_URL=/metagrid + # MetaGrid API -# ------------------------------------------------------------------------------ # https://github.com/aims-group/metagrid/tree/master/backend REACT_APP_METAGRID_API_URL=http://localhost:8000 # ESGF wget API -# ------------------------------------------------------------------------------ # https://github.com/ESGF/esgf-wget REACT_APP_WGET_API_URL=https://esgf-node.llnl.gov/esg-search/wget # ESGF Search API -# ------------------------------------------------------------------------------ # https://esgf.github.io/esg-search/ESGF_Search_RESTful_API.html REACT_APP_ESGF_NODE_URL=https://esgf-node.llnl.gov # ESGF Node Status API -# ------------------------------------------------------------------------------ # https://github.com/ESGF/esgf-utils/blob/master/node_status/query_prom.py REACT_APP_ESGF_NODE_STATUS_URL=https://aims4.llnl.gov/prometheus/api/v1/query?query=probe_success%7Bjob%3D%22http_2xx%22%2C+target%3D~%22.%2Athredds.%2A%22%7D - # Keycloak -# ------------------------------------------------------------------------------ # https://github.com/keycloak/keycloak REACT_APP_KEYCLOAK_REALM=metagrid REACT_APP_KEYCLOAK_URL=http://keycloak:8080/auth REACT_APP_KEYCLOAK_CLIENT_ID=frontend +# react-hotjar +# https://github.com/abdalla/react-hotjar +REACT_APP_HOTJAR_ID= +REACT_APP_HOTJAR_SV= + +# react-ga +# https://github.com/react-ga/react-ga +REACT_APP_GOOGLE_ANALYTICS_TRACKING_ID= + # Temporary fix to remove Keycloak sourcemap error # ------------------------------------------------------------------------------ # https://github.com/react-keycloak/react-keycloak/issues/176 diff --git a/frontend/src/components/App/App.tsx b/frontend/src/components/App/App.tsx index c3484bdbd..5c2106192 100644 --- a/frontend/src/components/App/App.tsx +++ b/frontend/src/components/App/App.tsx @@ -203,11 +203,11 @@ const App: React.FC = ({ searchQuery }) => { }, [runFetchNodeStatus]); React.useEffect(() => { - void fetchProjects() + fetchProjects() .then((data) => { const projectName = searchQuery ? searchQuery.project.name : ''; /* istanbul ignore else */ - if (projectName && projectName !== '' && data) { + if (data && projectName && projectName !== '') { const rawProj: RawProject | undefined = data.results.find((proj) => { return ( proj.name.toLowerCase() === (projectName as string).toLowerCase() @@ -227,7 +227,7 @@ const App: React.FC = ({ searchQuery }) => { }); } ); - }, [searchQuery]); + }, [fetchProjects]); const handleTextSearch = ( selectedProject: RawProject, @@ -476,8 +476,7 @@ const App: React.FC = ({ searchQuery }) => { const generateRedirects = (): ReactElement => { /* istanbul ignore next */ if (!publicUrl && previousPublicUrl) { - const newFrom = `/${previousPublicUrl}`; - } />; + } />; } return <>; diff --git a/frontend/src/components/Cart/SearchesCard.test.tsx b/frontend/src/components/Cart/SearchesCard.test.tsx index 811c5e6af..1306ed56f 100644 --- a/frontend/src/components/Cart/SearchesCard.test.tsx +++ b/frontend/src/components/Cart/SearchesCard.test.tsx @@ -52,7 +52,9 @@ it('displays alert error when api fails to return response', async () => { ) ); - const { getByRole } = render(); + const { getByRole } = render(, { + wrapper: MemoryRouter, + }); // Check alert renders const alert = await waitFor(() => getByRole('img', { name: 'close-circle' })); @@ -64,7 +66,8 @@ it('displays "N/A" for Filename Searches when none are applied', () => { + />, + { wrapper: MemoryRouter } ); // Shows number of files const filenameSearchesField = getByText('Filename Searches:').parentNode; diff --git a/frontend/src/components/NavBar/RightMenu.test.tsx b/frontend/src/components/NavBar/RightMenu.test.tsx index f0b3fb1aa..a10b4ae51 100644 --- a/frontend/src/components/NavBar/RightMenu.test.tsx +++ b/frontend/src/components/NavBar/RightMenu.test.tsx @@ -1,6 +1,5 @@ import { fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; -import { BrowserRouter as Router } from 'react-router-dom'; import { customRender } from '../../test/custom-render'; import Support from '../Support'; import RightMenu, { Props } from './RightMenu'; @@ -15,11 +14,7 @@ const rightMenuProps: Props = { }; it('sets the active menu item based on the location pathname', async () => { - const { getByRole } = customRender( - - - - ); + const { getByRole } = customRender(); const cartItemsLink = await waitFor(() => getByRole('img', { name: 'shopping-cart' }) @@ -36,9 +31,7 @@ it('sets the active menu item based on the location pathname', async () => { it('display the user"s given name after authentication and signs out', async () => { const { getByTestId, getByText } = customRender( - - - , + , { authenticated: true, idTokenParsed: { given_name: 'John', email: 'johndoe@url.com' }, @@ -62,9 +55,7 @@ it('display the user"s given name after authentication and signs out', async () it('display the user"s email after authentication if they did not provide a name and signs out', async () => { const { getByTestId, getByText } = customRender( - - - , + , { authenticated: true, idTokenParsed: { email: 'johndoe@url.com' }, @@ -88,9 +79,7 @@ it('display the user"s email after authentication if they did not provide a name it('displays sign in button when user hasn"t logged in', async () => { const { getByRole, getByTestId } = customRender( - - - + ); // Check applicable components render @@ -105,9 +94,7 @@ it('displays sign in button when user hasn"t logged in', async () => { it('displays help menu when help button is clicked', async () => { const { getByText, getByTestId } = customRender( - - - + ); // Check applicable components render diff --git a/frontend/src/components/NavBar/index.test.tsx b/frontend/src/components/NavBar/index.test.tsx index d5b9fce32..f0e1736ae 100644 --- a/frontend/src/components/NavBar/index.test.tsx +++ b/frontend/src/components/NavBar/index.test.tsx @@ -1,6 +1,5 @@ import { fireEvent, waitFor } from '@testing-library/react'; import React from 'react'; -import { BrowserRouter as Router } from 'react-router-dom'; import { rest, server } from '../../api/mock/setup-env'; import apiRoutes from '../../api/routes'; import { customRender } from '../../test/custom-render'; @@ -14,11 +13,7 @@ const defaultProps: Props = { }; it('renders LeftMenu and RightMenu components', async () => { - const { getByTestId } = customRender( - - - - ); + const { getByTestId } = customRender(); const rightMenuComponent = await waitFor(() => getByTestId('right-menu')); expect(rightMenuComponent).toBeTruthy(); @@ -31,11 +26,7 @@ it('renders error message when projects can"t be fetched', async () => { server.use( rest.get(apiRoutes.projects.path, (_req, res, ctx) => res(ctx.status(404))) ); - const { getByRole } = customRender( - - - - ); + const { getByRole } = customRender(); const alertComponent = await waitFor(() => getByRole('img', { name: 'close-circle' }) @@ -44,11 +35,7 @@ it('renders error message when projects can"t be fetched', async () => { }); it('opens the drawer onClick and closes with onClose', async () => { - const { getByRole, getByTestId } = customRender( - - - - ); + const { getByRole, getByTestId } = customRender(); await waitFor(() => expect(getByTestId('left-menu')).toBeTruthy()); expect(getByTestId('right-menu')).toBeTruthy(); diff --git a/frontend/src/components/Support/index.test.tsx b/frontend/src/components/Support/index.test.tsx index 6f68538c1..eb18eb5bd 100644 --- a/frontend/src/components/Support/index.test.tsx +++ b/frontend/src/components/Support/index.test.tsx @@ -1,5 +1,6 @@ import { fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; import { getCurrentAppPage, TourTitles } from '../../common/reactJoyrideSteps'; import { AppPage } from '../../common/types'; import { ReactJoyrideProvider } from '../../contexts/ReactJoyrideContext'; @@ -40,7 +41,10 @@ describe('Testing the support form and buttons', () => { const { getByTestId, getByRole } = render( - + , + { + wrapper: MemoryRouter, + } ); // Check support modal rendered @@ -65,7 +69,10 @@ describe('Testing the support form and buttons', () => { const { getByTestId, getByRole } = render( - + , + { + wrapper: MemoryRouter, + } ); // Check support modal rendered @@ -90,7 +97,10 @@ describe('Testing the support form and buttons', () => { const { getByTestId, getByRole } = render( - + , + { + wrapper: MemoryRouter, + } ); // Check support modal rendered @@ -115,7 +125,10 @@ describe('Testing the support form and buttons', () => { const { getByTestId, getByRole } = render( - + , + { + wrapper: MemoryRouter, + } ); // Check support modal rendered diff --git a/frontend/src/contexts/ReactJoyrideContext.test.tsx b/frontend/src/contexts/ReactJoyrideContext.test.tsx index 89e1a32c0..e92939840 100644 --- a/frontend/src/contexts/ReactJoyrideContext.test.tsx +++ b/frontend/src/contexts/ReactJoyrideContext.test.tsx @@ -1,5 +1,6 @@ import { waitFor, render, fireEvent } from '@testing-library/react'; import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; import { getCurrentAppPage, TourTitles } from '../common/reactJoyrideSteps'; import { AppPage } from '../common/types'; import Support from '../components/Support'; @@ -12,7 +13,10 @@ describe('test ReactJoyrideProvider', () => {

renders

- + , + { + wrapper: MemoryRouter, + } ); // Wait for render to get user auth info @@ -49,7 +53,10 @@ describe('test ReactJoyrideProvider', () => { const { getByTestId, getByRole } = render( - + , + { + wrapper: MemoryRouter, + } ); // Check support modal rendered diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index e186fc55f..6b90e8ded 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { ReactKeycloakProvider } from '@react-keycloak/web'; import ReactDOM from 'react-dom'; -import { BrowserRouter as Router } from 'react-router-dom'; +import { BrowserRouter } from 'react-router-dom'; import { getSearchFromUrl } from './common/utils'; import App from './components/App/App'; import { AuthProvider } from './contexts/AuthContext'; @@ -15,11 +15,11 @@ ReactDOM.render( initOptions={keycloakProviderInitConfig} > - + - + , document.getElementById('root') diff --git a/frontend/src/test/custom-render.tsx b/frontend/src/test/custom-render.tsx index 819ffbcef..5337e8634 100644 --- a/frontend/src/test/custom-render.tsx +++ b/frontend/src/test/custom-render.tsx @@ -2,7 +2,7 @@ import { ReactKeycloakProvider } from '@react-keycloak/web'; import { render, RenderResult } from '@testing-library/react'; import { KeycloakInstance } from 'keycloak-js'; import React, { ComponentType } from 'react'; -import { BrowserRouter as Router } from 'react-router-dom'; +import { MemoryRouter } from 'react-router-dom'; import { AuthProvider } from '../contexts/AuthContext'; import { keycloakProviderInitConfig } from '../lib/keycloak'; @@ -70,7 +70,9 @@ export const customRender = ( initOptions={keycloakProviderInitConfig} > - {children} + + {children} + ); From 5e5998572f9be2ace0aee1abe688c27ef281484a Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Tue, 28 Mar 2023 20:07:03 -0700 Subject: [PATCH 20/65] Updated some more frontend packages, however a few updates are breaking and will cause errors, so ignored those. --- frontend/package.json | 20 +- frontend/yarn.lock | 1069 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 959 insertions(+), 130 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 8d6797053..d10b9a45d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -56,12 +56,12 @@ } }, "dependencies": { - "@ant-design/icons": "4.6.2", + "@ant-design/icons": "5.0.1", "@babel/plugin-syntax-flow": "7.16.7", "@babel/plugin-transform-react-jsx": "7.17.3", "@react-keycloak/web": "3.4.0", "antd": "4.15.1", - "autoprefixer": "10.4.4", + "autoprefixer": "10.4.14", "axios": "0.26.1", "dotenv": "8.2.0", "env-cmd": "10.1.0", @@ -74,7 +74,7 @@ "react-async": "10.0.1", "react-dom": "18.2.0", "react-hotjar": "2.2.1", - "react-joyride": "2.4.0", + "react-joyride": "2.5.3", "react-router-dom": "^6.9.0", "react-scripts": "5.0.1", "typescript": "4.6.3", @@ -83,30 +83,30 @@ "devDependencies": { "@babel/core": "7.17.9", "@testing-library/dom": "8.13.0", - "@testing-library/jest-dom": "5.11.10", + "@testing-library/jest-dom": "5.16.5", "@testing-library/react": "11.2.6", "@testing-library/user-event": "13.1.2", - "@types/humps": "2.0.0", + "@types/humps": "2.0.2", "@types/jest": "28.1.8", "@types/node": "14.14.37", "@types/react": "17.0.3", "@types/react-dom": "17.0.3", - "@types/react-router-dom": "5.1.7", + "@types/react-router-dom": "5.3.3", "@types/uuid": "8.3.4", "@typescript-eslint/eslint-plugin": "5.30.7", "@typescript-eslint/parser": "5.30.7", - "dayjs": "1.11.1", + "dayjs": "1.11.7", "eslint": "8.12.0", "eslint-config-airbnb": "19.0.4", "eslint-config-airbnb-typescript": "17.0.0", "eslint-config-prettier": "8.5.0", - "eslint-plugin-import": "2.26.0", - "eslint-plugin-jsx-a11y": "6.5.1", + "eslint-plugin-import": "2.27.5", + "eslint-plugin-jsx-a11y": "6.7.1", "eslint-plugin-prettier": "4.0.0", "eslint-plugin-react": "7.29.4", "eslint-plugin-react-hooks": "4.6.0", "msw": "0.28.1", - "postcss": "8.4.12", + "postcss": "8.4.21", "prettier": "2.2.1", "setimmediate": "1.0.5" } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index c6acc585e..7243bb452 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adobe/css-tools@^4.0.1": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.2.0.tgz#e1a84fca468f4b337816fcb7f0964beb620ba855" + integrity sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA== + "@ampproject/remapping@^2.1.0": version "2.1.2" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" @@ -16,12 +21,35 @@ dependencies: "@ctrl/tinycolor" "^3.4.0" +"@ant-design/colors@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.0.0.tgz#eb7eecead124c3533aea05d61254f0a17f2b61b3" + integrity sha512-iVm/9PfGCbC0dSMBrz7oiEXZaaGH7ceU40OJEfKmyuzR9R5CRimJYPlRiFtMQGQcbNMea/ePcoIebi4ASGYXtg== + dependencies: + "@ctrl/tinycolor" "^3.4.0" + "@ant-design/icons-svg@^4.0.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.1.0.tgz#480b025f4b20ef7fe8f47d4a4846e4fee84ea06c" integrity sha512-Fi03PfuUqRs76aI3UWYpP864lkrfPo0hluwGqh7NJdLhvH4iRDc3jbJqZIvRDLHKbXrvAfPPV3+zjUccfFvWOQ== -"@ant-design/icons@4.6.2", "@ant-design/icons@^4.6.2": +"@ant-design/icons-svg@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz#8630da8eb4471a4aabdaed7d1ff6a97dcb2cf05a" + integrity sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw== + +"@ant-design/icons@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.0.1.tgz#febb1fdc5776f58187b2c953ac9a4496069d045b" + integrity sha512-ZyF4ksXCcdtwA/1PLlnFLcF/q8/MhwxXhKHh4oCHDA4Ip+ZzAHoICtyp4wZWfiCVDP0yuz3HsjyvuldHFb3wjA== + dependencies: + "@ant-design/colors" "^7.0.0" + "@ant-design/icons-svg" "^4.2.1" + "@babel/runtime" "^7.11.2" + classnames "^2.2.6" + rc-util "^5.9.4" + +"@ant-design/icons@^4.6.2": version "4.6.2" resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-4.6.2.tgz#290f2e8cde505ab081fda63e511e82d3c48be982" integrity sha512-QsBG2BxBYU/rxr2eb8b2cZ4rPKAPBpzAR+0v6rrZLp/lnyvflLH3tw1vregK+M7aJauGWjIGNdFmUfpAOtw25A== @@ -1986,6 +2014,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.20.7": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== + dependencies: + regenerator-runtime "^0.13.11" + "@babel/template@^7.10.4", "@babel/template@^7.12.7": version "7.12.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc" @@ -2159,6 +2194,18 @@ resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f" integrity sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ== +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" + integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== + "@eslint/eslintrc@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6" @@ -2174,10 +2221,39 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@gilbarbara/deep-equal@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@gilbarbara/deep-equal/-/deep-equal-0.1.1.tgz#91c8f291da6bf7e21cdbfb585fb072d59963acf3" - integrity sha512-SjSBspHXlclODLtSoPIQwBhfeBjncC05NlNoFELJ6xZQkyYDJsVCcs7+f+etHR2cYPbHLjnh1C06lQlCbMEWEA== +"@eslint/eslintrc@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" + integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.5.1" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.37.0": + version "8.37.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.37.0.tgz#cf1b5fa24217fe007f6487a26d765274925efa7d" + integrity sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A== + +"@gilbarbara/deep-equal@^0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz#1a106721368dba5e7e9fb7e9a3a6f9efbd8df36d" + integrity sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA== + +"@humanwhocodes/config-array@^0.11.8": + version "0.11.8" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" "@humanwhocodes/config-array@^0.9.2": version "0.9.5" @@ -2188,6 +2264,11 @@ debug "^4.1.1" minimatch "^3.0.4" +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" @@ -2494,11 +2575,24 @@ "@nodelib/fs.stat" "2.0.3" run-parallel "^1.1.9" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + "@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== +"@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + "@nodelib/fs.walk@^1.2.3": version "1.2.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" @@ -2507,6 +2601,14 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@open-draft/until@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" @@ -2750,17 +2852,18 @@ lz-string "^1.4.4" pretty-format "^26.6.2" -"@testing-library/jest-dom@5.11.10": - version "5.11.10" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.11.10.tgz#1cd90715023e1627f5ed26ab3b38e6f22d77046c" - integrity sha512-FuKiq5xuk44Fqm0000Z9w0hjOdwZRNzgx7xGGxQYepWFZy+OYUMOT/wPI4nLYXCaVltNVpU1W/qmD88wLWDsqQ== +"@testing-library/jest-dom@5.16.5": + version "5.16.5" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e" + integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA== dependencies: + "@adobe/css-tools" "^4.0.1" "@babel/runtime" "^7.9.2" "@types/testing-library__jest-dom" "^5.9.1" - aria-query "^4.2.2" + aria-query "^5.0.0" chalk "^3.0.0" - css "^3.0.0" css.escape "^1.5.1" + dom-accessibility-api "^0.5.6" lodash "^4.17.15" redent "^3.0.0" @@ -2950,6 +3053,11 @@ resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== +"@types/history@^4.7.11": + version "4.7.11" + resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64" + integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA== + "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" @@ -2962,10 +3070,10 @@ dependencies: "@types/node" "*" -"@types/humps@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/humps/-/humps-2.0.0.tgz#9ad07801cd34f9d9ce379fa66f62d3049937d815" - integrity sha512-bP/s9HUT2oTJ0c3XGcHGISwTNs6r8eUPNfU6DuOSU8EHgtHqwvoDEyj76jPhKT/0MszS1PF/hHolxRrHSLYUPQ== +"@types/humps@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@types/humps/-/humps-2.0.2.tgz#6643e2beb13e0e7a22c65b2128b4315a727ecbe0" + integrity sha512-FuyQoVNSW6mjSLxLVRq8YK1pi8P5zyL2pahEuCtMqlbmV4jyWYITz4eE2fZIErNovIfKU32tPbCl44VbOUaEiw== "@types/inquirer@^7.3.1": version "7.3.1" @@ -3087,12 +3195,12 @@ dependencies: "@types/react" "*" -"@types/react-router-dom@5.1.7": - version "5.1.7" - resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.7.tgz#a126d9ea76079ffbbdb0d9225073eb5797ab7271" - integrity sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg== +"@types/react-router-dom@5.3.3": + version "5.3.3" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83" + integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw== dependencies: - "@types/history" "*" + "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router" "*" @@ -3566,6 +3674,11 @@ acorn-jsx@^5.3.1: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + acorn-node@^1.6.1: version "1.8.2" resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" @@ -3590,6 +3703,11 @@ acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +acorn@^8.8.0: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + address@^1.0.1, address@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -3784,6 +3902,21 @@ aria-query@^5.0.0: resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c" integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg== +aria-query@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -3816,6 +3949,17 @@ array-includes@^3.1.4: get-intrinsic "^1.1.1" is-string "^1.0.7" +array-includes@^3.1.5, array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + is-string "^1.0.7" + array-tree-filter@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190" @@ -3835,6 +3979,16 @@ array.prototype.flat@^1.2.5: define-properties "^1.1.3" es-abstract "^1.19.0" +array.prototype.flat@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + array.prototype.flatmap@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz#908dc82d8a406930fdf38598d51e7411d18d4446" @@ -3844,6 +3998,27 @@ array.prototype.flatmap@^1.2.5: define-properties "^1.1.3" es-abstract "^1.19.0" +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" + integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -3881,12 +4056,19 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +autoprefixer@10.4.14: + version "10.4.14" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d" + integrity sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ== + dependencies: + browserslist "^4.21.5" + caniuse-lite "^1.0.30001464" + fraction.js "^4.2.0" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" -autoprefixer@10.4.4, autoprefixer@^10.4.4: +autoprefixer@^10.4.4: version "10.4.4" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.4.tgz#3e85a245b32da876a893d3ac2ea19f01e7ea5a1e" integrity sha512-Tm8JxsB286VweiZ5F0anmbyGiNI3v3wGv3mz9W+cxEDYB/6jbnj6GM9H9mK3wIL8ftgl+C07Lcwb8PG5PCCPzA== @@ -3898,11 +4080,21 @@ autoprefixer@10.4.4, autoprefixer@^10.4.4: picocolors "^1.0.0" postcss-value-parser "^4.2.0" +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + axe-core@^4.3.5: version "4.4.1" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== +axe-core@^4.6.2: + version "4.6.3" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece" + integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg== + axios@0.26.1: version "0.26.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" @@ -3915,6 +4107,13 @@ axobject-query@^2.2.0: resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== +axobject-query@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1" + integrity sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg== + dependencies: + deep-equal "^2.0.5" + babel-jest@^27.4.2, babel-jest@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" @@ -4167,6 +4366,16 @@ browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.1, browserslist@^4 node-releases "^2.0.3" picocolors "^1.0.0" +browserslist@^4.21.5: + version "4.21.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + dependencies: + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -4258,6 +4467,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001317, caniuse-lite@^1.0.30001332: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001334.tgz#892e9965b35285033fc2b8a8eff499fe02f13d8b" integrity sha512-kbaCEBRRVSoeNs74sCuq92MJyGrMtjWVfhltoHUCW4t4pXFvGjUBrfo47weBRViHkiV3eBYyIsfl956NtHGazw== +caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001464: + version "1.0.30001472" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001472.tgz#3f484885f2a2986c019dc416e65d9d62798cdd64" + integrity sha512-xWC/0+hHHQgj3/vrKYY0AAzeIUgr7L9wlELIcAvZdDUHlhL/kNxMdnQLOSOQfP8R51ZzPhmHdyMkI0MMpmxCfg== + case-sensitive-paths-webpack-plugin@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" @@ -4780,15 +4994,6 @@ css.escape@^1.5.1: resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= -css@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" - integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== - dependencies: - inherits "^2.0.4" - source-map "^0.6.1" - source-map-resolve "^0.6.0" - cssdb@^6.5.0: version "6.5.0" resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-6.5.0.tgz#61264b71f29c834f09b59cb3e5b43c8226590122" @@ -4884,7 +5089,7 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.5.tgz#7fdec6a28a67ae18647c51668a9ff95bb2fa7bb8" integrity sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ== -damerau-levenshtein@^1.0.7: +damerau-levenshtein@^1.0.7, damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== @@ -4903,10 +5108,10 @@ date-fns@^2.15.0: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b" integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ== -dayjs@1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.1.tgz#90b33a3dda3417258d48ad2771b415def6545eb0" - integrity sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA== +dayjs@1.11.7: + version "1.11.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" + integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== debug@2.6.9, debug@^2.6.0, debug@^2.6.9: version "2.6.9" @@ -4951,10 +5156,28 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -deep-diff@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-1.0.2.tgz#afd3d1f749115be965e89c63edc7abb1506b9c26" - integrity sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg== +deep-equal@^2.0.5: + version "2.2.0" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.0.tgz#5caeace9c781028b9ff459f33b779346637c43e6" + integrity sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw== + dependencies: + call-bind "^1.0.2" + es-get-iterator "^1.1.2" + get-intrinsic "^1.1.3" + is-arguments "^1.1.1" + is-array-buffer "^3.0.1" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" @@ -4985,6 +5208,14 @@ define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +define-properties@^1.1.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" @@ -5095,6 +5326,11 @@ dom-accessibility-api@^0.5.4: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz#b06d059cdd4a4ad9a79275f9d414a5c126241166" integrity sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ== +dom-accessibility-api@^0.5.6: + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + dom-accessibility-api@^0.5.9: version "0.5.13" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.13.tgz#102ee5f25eacce09bdf1cfa5a298f86da473be4b" @@ -5220,6 +5456,11 @@ electron-to-chromium@^1.4.118: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.127.tgz#4ef19d5d920abe2676d938f4170729b44f7f423a" integrity sha512-nhD6S8nKI0O2MueC6blNOEZio+/PWppE/pevnf3LOlQA/fKPCrDp2Ao4wx4LFwmIkJpVdFdn2763YWLy9ENIZg== +electron-to-chromium@^1.4.284: + version "1.4.342" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.342.tgz#3c7e199c3aa89c993df4b6f5223d6d26988f58e6" + integrity sha512-dTei3VResi5bINDENswBxhL+N0Mw5YnfWyTqO75KGsVldurEkhC9+CelJVAse8jycWyP8pv3VSj4BSyP8wTWJA== + emittery@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" @@ -5341,11 +5582,82 @@ es-abstract@^1.19.0, es-abstract@^1.19.1: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" +es-abstract@^1.20.4: + version "1.21.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" + integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== + dependencies: + array-buffer-byte-length "^1.0.0" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.2.0" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" + +es-get-iterator@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + es-module-lexer@^0.9.0: version "0.9.3" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -5418,10 +5730,10 @@ eslint-config-airbnb@19.0.4: object.assign "^4.1.2" object.entries "^1.1.5" -eslint-config-prettier@8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== +eslint-config-prettier@8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" + integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== eslint-config-react-app@^7.0.1: version "7.0.1" @@ -5451,6 +5763,15 @@ eslint-import-resolver-node@^0.3.6: debug "^3.2.7" resolve "^1.20.0" +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== + dependencies: + debug "^3.2.7" + is-core-module "^2.11.0" + resolve "^1.22.1" + eslint-module-utils@^2.7.3: version "2.7.3" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" @@ -5459,6 +5780,13 @@ eslint-module-utils@^2.7.3: debug "^3.2.7" find-up "^2.1.0" +eslint-module-utils@^2.7.4: + version "2.7.4" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" + integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== + dependencies: + debug "^3.2.7" + eslint-plugin-flowtype@^8.0.3: version "8.0.3" resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz#e1557e37118f24734aa3122e7536a038d34a4912" @@ -5467,7 +5795,28 @@ eslint-plugin-flowtype@^8.0.3: lodash "^4.17.21" string-natural-compare "^3.0.1" -eslint-plugin-import@2.26.0, eslint-plugin-import@^2.25.3: +eslint-plugin-import@2.27.5: + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" + has "^1.0.3" + is-core-module "^2.11.0" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" + tsconfig-paths "^3.14.1" + +eslint-plugin-import@^2.25.3: version "2.26.0" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== @@ -5493,7 +5842,29 @@ eslint-plugin-jest@^25.3.0: dependencies: "@typescript-eslint/experimental-utils" "^5.0.0" -eslint-plugin-jsx-a11y@6.5.1, eslint-plugin-jsx-a11y@^6.5.1: +eslint-plugin-jsx-a11y@6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz#fca5e02d115f48c9a597a6894d5bcec2f7a76976" + integrity sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA== + dependencies: + "@babel/runtime" "^7.20.7" + aria-query "^5.1.3" + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + ast-types-flow "^0.0.7" + axe-core "^4.6.2" + axobject-query "^3.1.1" + damerau-levenshtein "^1.0.8" + emoji-regex "^9.2.2" + has "^1.0.3" + jsx-ast-utils "^3.3.3" + language-tags "=1.0.5" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + semver "^6.3.0" + +eslint-plugin-jsx-a11y@^6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz#cdbf2df901040ca140b6ec14715c988889c2a6d8" integrity sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g== @@ -5511,10 +5882,10 @@ eslint-plugin-jsx-a11y@6.5.1, eslint-plugin-jsx-a11y@^6.5.1: language-tags "^1.0.5" minimatch "^3.0.4" -eslint-plugin-prettier@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz#8b99d1e4b8b24a762472b4567992023619cb98e0" - integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== +eslint-plugin-prettier@4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== dependencies: prettier-linter-helpers "^1.0.0" @@ -5528,7 +5899,28 @@ eslint-plugin-react-hooks@^4.3.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.4.0.tgz#71c39e528764c848d8253e1aa2c7024ed505f6c4" integrity sha512-U3RVIfdzJaeKDQKEJbz5p3NW8/L80PCATJAfuojwbaEL+gBjfGdhUcGde+WGUW46Q5sr/NgxevsIiDtNXrvZaQ== -eslint-plugin-react@7.29.4, eslint-plugin-react@^7.27.1: +eslint-plugin-react@7.32.2: + version "7.32.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10" + integrity sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== + dependencies: + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" + doctrine "^2.1.0" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" + prop-types "^15.8.1" + resolve "^2.0.0-next.4" + semver "^6.3.0" + string.prototype.matchall "^4.0.8" + +eslint-plugin-react@^7.27.1: version "7.29.4" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz#4717de5227f55f3801a5fd51a16a4fa22b5914d2" integrity sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ== @@ -5593,6 +5985,11 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" + integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== + eslint-webpack-plugin@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.1.1.tgz#83dad2395e5f572d6f4d919eedaa9cf902890fcb" @@ -5604,7 +6001,53 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^3.1.1" -eslint@8.12.0, eslint@^8.3.0: +eslint@8.37.0: + version "8.37.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.37.0.tgz#1f660ef2ce49a0bfdec0b0d698e0b8b627287412" + integrity sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.0.2" + "@eslint/js" "8.37.0" + "@humanwhocodes/config-array" "^0.11.8" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-visitor-keys "^3.4.0" + espree "^9.5.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + +eslint@^8.3.0: version "8.12.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.12.0.tgz#c7a5bd1cfa09079aae64c9076c07eada66a46e8e" integrity sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q== @@ -5654,6 +6097,15 @@ espree@^9.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^3.3.0" +espree@^9.5.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" + integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== + dependencies: + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.0" + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -5666,6 +6118,13 @@ esquery@^1.4.0: dependencies: estraverse "^5.1.0" +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -5972,6 +6431,13 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.8: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + fork-ts-checker-webpack-plugin@^6.5.0: version "6.5.1" resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.1.tgz#fd689e2d9de6ac76abb620909eea56438cd0f232" @@ -6059,11 +6525,26 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -6092,6 +6573,15 @@ get-intrinsic@^1.0.1, get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@ has "^1.0.3" has-symbols "^1.0.1" +get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -6167,6 +6657,13 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@^13.19.0: + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== + dependencies: + type-fest "^0.20.2" + globals@^13.6.0: version "13.8.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.8.0.tgz#3e20f504810ce87a8d72e55aecf8435b50f4c1b3" @@ -6181,6 +6678,13 @@ globals@^13.9.0: dependencies: type-fest "^0.20.2" +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + globby@^11.0.4, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -6193,6 +6697,13 @@ globby@^11.0.4, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" @@ -6203,6 +6714,11 @@ graceful-fs@^4.2.6, graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + graphql@^15.4.0: version "15.5.0" resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" @@ -6230,6 +6746,11 @@ has-bigints@^1.0.1: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -6240,6 +6761,18 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" @@ -6428,7 +6961,7 @@ human-signals@^2.1.0: humps@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/humps/-/humps-2.0.1.tgz#dd02ea6081bd0568dc5d073184463957ba9ef9aa" - integrity sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao= + integrity sha512-E0eIbrFWUhwfXJmsbdjRQFQPrl5pTEoKlz163j1mTqqUnU9PgR4AgB8AIITzuB3vLBdxZXyZ9TDIrwB2OASz4g== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" @@ -6518,7 +7051,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6561,6 +7094,15 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +internal-slot@^1.0.4, internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -6571,6 +7113,23 @@ ipaddr.js@^2.0.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -6595,6 +7154,11 @@ is-boolean-object@^1.1.0: dependencies: call-bind "^1.0.0" +is-callable@^1.1.3, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + is-callable@^1.1.4, is-callable@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" @@ -6612,6 +7176,13 @@ is-core-module@^2.1.0: dependencies: has "^1.0.3" +is-core-module@^2.11.0, is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + is-core-module@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" @@ -6631,6 +7202,13 @@ is-date-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== +is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + is-docker@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" @@ -6663,10 +7241,20 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-lite@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/is-lite/-/is-lite-0.8.1.tgz#a9bd03c90ea723d450c78c991b84f78e7e3126f9" - integrity sha512-ekSwuewzOmwFnzzAOWuA5fRFPqOeTrLIL3GWT7hdVVi+oLuD+Rau8gCmkb94vH5hjXc1Q/CfIW/y/td1RrNQIg== +is-lite@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/is-lite/-/is-lite-0.8.2.tgz#26ab98b32aae8cc8b226593b9a641d2bf4bd3b6a" + integrity sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw== + +is-lite@^0.9.2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/is-lite/-/is-lite-0.9.2.tgz#4b19e9a26b7c99ed50f748bcf088db57893d0730" + integrity sha512-qZuxbaEiKLOKhX4sbHLfhFN9iA3YciuZLb37/DfXCpWnz8p7qNL2lwkpxYMXfjlS8eEEjpULPZxAUI8N6FYvYQ== + +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== is-module@^1.0.0: version "1.0.0" @@ -6698,6 +7286,11 @@ is-obj@^1.0.1: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" @@ -6733,7 +7326,12 @@ is-root@^2.1.0: resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== -is-shared-array-buffer@^1.0.1: +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + +is-shared-array-buffer@^1.0.1, is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== @@ -6764,11 +7362,27 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.1" +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -6776,6 +7390,14 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -6783,6 +7405,11 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -7349,6 +7976,11 @@ js-levenshtein@^1.1.6: resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== +js-sdsl@^4.1.4: + version "4.4.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" + integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== + js-sha256@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" @@ -7503,6 +8135,14 @@ jsx-ast-utils@^3.2.1: array-includes "^3.1.4" object.assign "^4.1.2" +jsx-ast-utils@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" + integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== + dependencies: + array-includes "^3.1.5" + object.assign "^4.1.3" + keycloak-js@17.0.1: version "17.0.1" resolved "https://registry.yarnpkg.com/keycloak-js/-/keycloak-js-17.0.1.tgz#403ea75b3e938ddc780f99ecbd73e1b6905f826f" @@ -7536,7 +8176,7 @@ language-subtag-registry@~0.3.2: resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" integrity sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg== -language-tags@^1.0.5: +language-tags@=1.0.5, language-tags@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" integrity sha1-0yHbxNowuovzAk4ED6XBRmH5GTo= @@ -7841,7 +8481,7 @@ minimatch@3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.4, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -7929,6 +8569,11 @@ nanoid@^3.3.1: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.2.tgz#c89622fafb4381cd221421c69ec58547a1eec557" integrity sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA== +nanoid@^3.3.4: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -7949,16 +8594,6 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -nested-property@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/nested-property/-/nested-property-1.0.1.tgz#2001105b5c69413411b876bba9b86f4316af613f" - integrity sha512-BnBBoo/8bBNRdAnJc7+m79oWk7dXwW1+vCesaEQhfDGVwXGLMvmI4NwYgLTW94R/x+R2s/yr2g/hB/4w/YSAvA== - -nested-property@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/nested-property/-/nested-property-4.0.0.tgz#a67b5a31991e701e03cdbaa6453bc5b1011bb88d" - integrity sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA== - no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -7994,6 +8629,11 @@ node-releases@^2.0.3: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476" integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ== +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -8050,6 +8690,11 @@ object-inspect@^1.12.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== +object-inspect@^1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + object-inspect@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" @@ -8060,6 +8705,14 @@ object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -8075,6 +8728,16 @@ object.assign@^4.1.0, object.assign@^4.1.1, object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" +object.assign@^4.1.3, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + object.entries@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" @@ -8084,6 +8747,15 @@ object.entries@^1.1.5: define-properties "^1.1.3" es-abstract "^1.19.1" +object.entries@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" + integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + object.fromentries@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" @@ -8093,6 +8765,15 @@ object.fromentries@^2.0.5: define-properties "^1.1.3" es-abstract "^1.19.1" +object.fromentries@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" + integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + object.getownpropertydescriptors@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" @@ -8109,6 +8790,14 @@ object.hasown@^1.1.0: define-properties "^1.1.3" es-abstract "^1.19.1" +object.hasown@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" + integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== + dependencies: + define-properties "^1.1.4" + es-abstract "^1.20.4" + object.values@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" @@ -8128,6 +8817,15 @@ object.values@^1.1.5: define-properties "^1.1.3" es-abstract "^1.19.1" +object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" @@ -8944,12 +9642,12 @@ postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@8.4.12, postcss@^8.3.5, postcss@^8.4.4, postcss@^8.4.6, postcss@^8.4.7: - version "8.4.12" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" - integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== +postcss@8.4.21: + version "8.4.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" + integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== dependencies: - nanoid "^3.3.1" + nanoid "^3.3.4" picocolors "^1.0.0" source-map-js "^1.0.2" @@ -8961,6 +9659,15 @@ postcss@^7.0.35: picocolors "^0.2.1" source-map "^0.6.1" +postcss@^8.3.5, postcss@^8.4.4, postcss@^8.4.6, postcss@^8.4.7: + version "8.4.12" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" + integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== + dependencies: + nanoid "^3.3.1" + picocolors "^1.0.0" + source-map-js "^1.0.2" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -8978,10 +9685,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" - integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== +prettier@2.8.7: + version "2.8.7" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" + integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== pretty-bytes@^5.3.0: version "5.5.0" @@ -9627,17 +10334,18 @@ react-fast-compare@^3.2.0: resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== -react-floater@^0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/react-floater/-/react-floater-0.7.3.tgz#f57947960682586866ec21540e73c9049ca9f787" - integrity sha512-d1wAEph+xRxQ0RJ3woMmYLlZHTaCIsja7Bv6JNo2ezsVUgdMan4CxOR4Do4/xgpmRFfsQMdlygexLAZZypWirw== +react-floater@^0.7.6: + version "0.7.6" + resolved "https://registry.yarnpkg.com/react-floater/-/react-floater-0.7.6.tgz#a98ee90e3d51200c6e6a564ff33496f3c0d7cfee" + integrity sha512-tt/15k/HpaShbtvWCwsQYLR+ebfUuYbl+oAUJ3DcEDkgYKeUcSkDey2PdAIERdVwzdFZANz47HbwoET2/Rduxg== dependencies: deepmerge "^4.2.2" exenv "^1.2.2" - is-lite "^0.8.1" + is-lite "^0.8.2" popper.js "^1.16.0" + prop-types "^15.8.1" react-proptype-conditional-require "^1.0.4" - tree-changes "^0.5.1" + tree-changes "^0.9.1" react-hotjar@2.2.1: version "2.2.1" @@ -9659,21 +10367,20 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-joyride@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/react-joyride/-/react-joyride-2.4.0.tgz#273a99fea4804a48155e7cc7bae308dcbc8cb725" - integrity sha512-U0uDFspaAEZucsvYpEIEPnoWD0QwTFk06IgIlinmTDPHgoS+V0q16w1+JqHeEKkR8Q79DWEeBIJYMvCJ7jT2EQ== +react-joyride@2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/react-joyride/-/react-joyride-2.5.3.tgz#3e753f80502a74abcc956babec4873d204345911" + integrity sha512-DKKvb/JAAsHm0x/RWO3WI6NOtTMHDso5v8MTauxTSz2dFs7Tu1rWg1BDBWmEMj6pUCvem7hblFbCiDAcvhs8tQ== dependencies: - deep-diff "^1.0.2" deepmerge "^4.2.2" exenv "^1.2.2" - is-lite "^0.8.1" - nested-property "^4.0.0" - react-floater "^0.7.3" + is-lite "^0.9.2" + prop-types "^15.8.1" + react-floater "^0.7.6" react-is "^16.13.1" scroll "^3.0.1" scrollparent "^2.0.1" - tree-changes "^0.9.0" + tree-changes "^0.9.2" react-proptype-conditional-require@^1.0.4: version "1.0.4" @@ -9832,6 +10539,11 @@ regenerate@^1.4.0, regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + regenerator-runtime@^0.13.4: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" @@ -9869,6 +10581,15 @@ regexp.prototype.flags@^1.4.1: call-bind "^1.0.2" define-properties "^1.1.3" +regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" + regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" @@ -10008,6 +10729,15 @@ resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.22.1: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.3: version "2.0.0-next.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" @@ -10016,6 +10746,15 @@ resolve@^2.0.0-next.3: is-core-module "^2.2.0" path-parse "^1.0.6" +resolve@^2.0.0-next.4: + version "2.0.0-next.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" + integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -10085,6 +10824,15 @@ safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -10391,14 +11139,6 @@ source-map-loader@^3.0.0: iconv-lite "^0.6.3" source-map-js "^1.0.1" -source-map-resolve@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" - integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - source-map-support@^0.5.6, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -10494,6 +11234,13 @@ statuses@^2.0.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.0.tgz#aa7b107e018eb33e08e8aee2e7337e762dda1028" integrity sha512-w9jNUUQdpuVoYqXxnyOakhckBbOxRaoYqJscyIBYCS5ixyCnO7nQn7zBZvP9zf5QOPZcz2DLUpE3KsNPbJBOFA== +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + strict-event-emitter@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.1.0.tgz#fd742c1fb7e3852f0b964ecdae2d7666a6fb7ef8" @@ -10560,6 +11307,29 @@ string.prototype.matchall@^4.0.6: regexp.prototype.flags "^1.4.1" side-channel "^1.0.4" +string.prototype.matchall@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" + integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.4.3" + side-channel "^1.0.4" + +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimend@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" @@ -10576,6 +11346,15 @@ string.prototype.trimend@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimstart@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" @@ -10592,6 +11371,15 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -10936,21 +11724,13 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= -tree-changes@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/tree-changes/-/tree-changes-0.5.1.tgz#e31cc8a0f56c8c401f0a88243d9165dbea4f570c" - integrity sha512-O873xzV2xRZ6N059Mn06QzmGKEE21LlvIPbsk2G+GS9ZX5OCur6PIwuuh0rWpAPvLWQZPj0XObyG27zZyLHUzw== - dependencies: - deep-diff "^1.0.2" - nested-property "1.0.1" - -tree-changes@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/tree-changes/-/tree-changes-0.9.0.tgz#5dabc013f0f02d3f7f764596369adb35bf1b208a" - integrity sha512-k1lOWtcWfPzCyx45W3KsYkwNGf4DfnVb5VHV8/Shs8pr9bqhzjaIHgAnIyu9mydbYmluOVSgj2gdSOnIp+If3A== +tree-changes@^0.9.1, tree-changes@^0.9.2: + version "0.9.3" + resolved "https://registry.yarnpkg.com/tree-changes/-/tree-changes-0.9.3.tgz#89433ab3b4250c2910d386be1f83912b7144efcc" + integrity sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ== dependencies: - "@gilbarbara/deep-equal" "^0.1.0" - is-lite "^0.8.1" + "@gilbarbara/deep-equal" "^0.1.1" + is-lite "^0.8.2" tryer@^1.0.1: version "1.0.1" @@ -11026,6 +11806,15 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -11048,6 +11837,16 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -11131,6 +11930,14 @@ upath@^1.2.0: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== +update-browserslist-db@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.0" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" @@ -11437,6 +12244,28 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" From 119d5a9a62742477d2364782113e0d1ac46449c8 Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Tue, 28 Mar 2023 20:13:36 -0700 Subject: [PATCH 21/65] Updated yarn.lock file to see if tests pass. --- frontend/yarn.lock | 259 +++------------------------------------------ 1 file changed, 15 insertions(+), 244 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 7243bb452..fc47cdaec 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2194,18 +2194,6 @@ resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f" integrity sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ== -"@eslint-community/eslint-utils@^4.2.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" - integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== - dependencies: - eslint-visitor-keys "^3.3.0" - -"@eslint-community/regexpp@^4.4.0": - version "4.5.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" - integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== - "@eslint/eslintrc@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6" @@ -2221,40 +2209,11 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@eslint/eslintrc@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" - integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.5.1" - globals "^13.19.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@eslint/js@8.37.0": - version "8.37.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.37.0.tgz#cf1b5fa24217fe007f6487a26d765274925efa7d" - integrity sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A== - "@gilbarbara/deep-equal@^0.1.1": version "0.1.2" resolved "https://registry.yarnpkg.com/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz#1a106721368dba5e7e9fb7e9a3a6f9efbd8df36d" integrity sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA== -"@humanwhocodes/config-array@^0.11.8": - version "0.11.8" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" - integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== - dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" - minimatch "^3.0.5" - "@humanwhocodes/config-array@^0.9.2": version "0.9.5" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" @@ -2264,11 +2223,6 @@ debug "^4.1.1" minimatch "^3.0.4" -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" @@ -2575,24 +2529,11 @@ "@nodelib/fs.stat" "2.0.3" run-parallel "^1.1.9" -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - "@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== -"@nodelib/fs.stat@2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - "@nodelib/fs.walk@^1.2.3": version "1.2.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" @@ -2601,14 +2542,6 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" -"@nodelib/fs.walk@^1.2.8": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - "@open-draft/until@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" @@ -3674,11 +3607,6 @@ acorn-jsx@^5.3.1: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - acorn-node@^1.6.1: version "1.8.2" resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" @@ -3703,11 +3631,6 @@ acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== -acorn@^8.8.0: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - address@^1.0.1, address@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -4008,17 +3931,6 @@ array.prototype.flatmap@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.tosorted@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" - integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - es-shim-unscopables "^1.0.0" - get-intrinsic "^1.1.3" - asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -5730,10 +5642,10 @@ eslint-config-airbnb@19.0.4: object.assign "^4.1.2" object.entries "^1.1.5" -eslint-config-prettier@8.8.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348" - integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA== +eslint-config-prettier@8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" + integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== eslint-config-react-app@^7.0.1: version "7.0.1" @@ -5882,10 +5794,10 @@ eslint-plugin-jsx-a11y@^6.5.1: language-tags "^1.0.5" minimatch "^3.0.4" -eslint-plugin-prettier@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" - integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== +eslint-plugin-prettier@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz#8b99d1e4b8b24a762472b4567992023619cb98e0" + integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== dependencies: prettier-linter-helpers "^1.0.0" @@ -5899,28 +5811,7 @@ eslint-plugin-react-hooks@^4.3.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.4.0.tgz#71c39e528764c848d8253e1aa2c7024ed505f6c4" integrity sha512-U3RVIfdzJaeKDQKEJbz5p3NW8/L80PCATJAfuojwbaEL+gBjfGdhUcGde+WGUW46Q5sr/NgxevsIiDtNXrvZaQ== -eslint-plugin-react@7.32.2: - version "7.32.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10" - integrity sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== - dependencies: - array-includes "^3.1.6" - array.prototype.flatmap "^1.3.1" - array.prototype.tosorted "^1.1.1" - doctrine "^2.1.0" - estraverse "^5.3.0" - jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.1.2" - object.entries "^1.1.6" - object.fromentries "^2.0.6" - object.hasown "^1.1.2" - object.values "^1.1.6" - prop-types "^15.8.1" - resolve "^2.0.0-next.4" - semver "^6.3.0" - string.prototype.matchall "^4.0.8" - -eslint-plugin-react@^7.27.1: +eslint-plugin-react@7.29.4, eslint-plugin-react@^7.27.1: version "7.29.4" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz#4717de5227f55f3801a5fd51a16a4fa22b5914d2" integrity sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ== @@ -5985,11 +5876,6 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint-visitor-keys@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" - integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== - eslint-webpack-plugin@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.1.1.tgz#83dad2395e5f572d6f4d919eedaa9cf902890fcb" @@ -6001,53 +5887,7 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^3.1.1" -eslint@8.37.0: - version "8.37.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.37.0.tgz#1f660ef2ce49a0bfdec0b0d698e0b8b627287412" - integrity sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.2" - "@eslint/js" "8.37.0" - "@humanwhocodes/config-array" "^0.11.8" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-visitor-keys "^3.4.0" - espree "^9.5.1" - esquery "^1.4.2" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - grapheme-splitter "^1.0.4" - ignore "^5.2.0" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-sdsl "^4.1.4" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.1" - strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" - text-table "^0.2.0" - -eslint@^8.3.0: +eslint@8.12.0, eslint@^8.3.0: version "8.12.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.12.0.tgz#c7a5bd1cfa09079aae64c9076c07eada66a46e8e" integrity sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q== @@ -6097,15 +5937,6 @@ espree@^9.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^3.3.0" -espree@^9.5.1: - version "9.5.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" - integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== - dependencies: - acorn "^8.8.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.0" - esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -6118,13 +5949,6 @@ esquery@^1.4.0: dependencies: estraverse "^5.1.0" -esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== - dependencies: - estraverse "^5.1.0" - esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -6657,13 +6481,6 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.19.0: - version "13.20.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" - integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== - dependencies: - type-fest "^0.20.2" - globals@^13.6.0: version "13.8.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.8.0.tgz#3e20f504810ce87a8d72e55aecf8435b50f4c1b3" @@ -6714,11 +6531,6 @@ graceful-fs@^4.2.6, graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - graphql@^15.4.0: version "15.5.0" resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" @@ -7286,11 +7098,6 @@ is-obj@^1.0.1: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - is-plain-obj@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" @@ -7976,11 +7783,6 @@ js-levenshtein@^1.1.6: resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== -js-sdsl@^4.1.4: - version "4.4.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" - integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== - js-sha256@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" @@ -8481,7 +8283,7 @@ minimatch@3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -8790,14 +8592,6 @@ object.hasown@^1.1.0: define-properties "^1.1.3" es-abstract "^1.19.1" -object.hasown@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" - integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== - dependencies: - define-properties "^1.1.4" - es-abstract "^1.20.4" - object.values@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" @@ -9685,10 +9479,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@2.8.7: - version "2.8.7" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" - integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== +prettier@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== pretty-bytes@^5.3.0: version "5.5.0" @@ -10746,15 +10540,6 @@ resolve@^2.0.0-next.3: is-core-module "^2.2.0" path-parse "^1.0.6" -resolve@^2.0.0-next.4: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -11307,20 +11092,6 @@ string.prototype.matchall@^4.0.6: regexp.prototype.flags "^1.4.1" side-channel "^1.0.4" -string.prototype.matchall@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" - integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.3" - side-channel "^1.0.4" - string.prototype.trim@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" From 5e49c80fa62593d8847368d2830a98c6a28977cb Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Thu, 4 May 2023 20:14:18 -0700 Subject: [PATCH 22/65] Feature/500 info notifications (#520) * Refactored the joyrideTutorials in order to reduce the chances for errors with misspelling target names. Created unique target calssnames to simplify the process of adding and using targets. Removed the chance of duplicate target names causing errors etc. Added a test startup component that should show when first opening metagrid. Startup window still needs some work to improve visuals. * Added a welcome and changelog template component to use with thh startup display. Made the startup display change based on existing version so that a new changelog will appear when version is changed. Modified the startup component behavior to hadle a welcome and changelog template based on whether its first-time startup or not. * Updated django-all-auth to 0.54.0 * Upgraded the antd library to latest version. Updated components to work with the latests antd library and added some styling to correct some undesired changes. Added a temporary drawer component which can be opened by clicking a new message button in the top-right navbar. Fixed some styling issues with window resizing to make the top navbar work better with smaller windows and improve visual behavior. Removed redundant components that are no longer needed with the new antd library (which is now working with typescript well). Created a popup window that shows at the start for new users. Created a startup template system that allows different templates to be used for displaying popup messages. More fixed needed for tests and the welcome popup tutorials need work. * Worked to correct issues with tests hanging by reverting back the antd library. Also updated to node version 18. Restored previously deleted files and will remove them later when all tests are passing. Still need to correct tests to pass. Still need to complete messaging feature and update tutorial. * Updated tests so that they pass, coverage will still be needed. * Updated the Startup template functionality and added actions that can be triggered by the templates. Updated the welcome message buttons to work properly and select between pages to start tutorials. * Removed redundant and unneccessary components as they are now directly taken from antd library. Removed commented out import statements. * Refactored some names and added messages for the right drawer. Added ability to create messages for the message bar and provide content from markdown files. Updated the changelog to have useful information on latest changes. Need to troubleshoot tests and complete coverage. * Added markdown file support for the messages. Did refactoring so messages on the right drawer and popups will show the markdown. Created new markdown files with version info and example message. Added flexibility to popup so that styles can be modified on a per message basis. Modified the changelog template and types. * Created a new welcome tour which will show users the help buttons to encourage them to use them if they have issues. Updated the navbar tour to include the new news button. Created new test files and updated tests to bring back 100% text coverage. Removed obsolete tour target related files, as they've been updated with target object class. Troubleshooted and ran tests to fix some bugs and added more functionality for jest tests. Deleted test markdown files and updated message markdown slightly. * modify the messages for release (#522) --------- Co-authored-by: Sasha Ames --- .github/workflows/frontend.yml | 4 +- .github/workflows/pre-commit.yml | 4 +- backend/requirements/base.txt | 2 +- frontend/package.json | 4 + frontend/public/changelog/v1.0.7-beta.md | 10 + frontend/public/changelog/v1.0.8-beta.md | 15 + frontend/public/messages/metagrid_messages.md | 13 + frontend/public/messages/test_message.md | 3 + frontend/src/common/JoyrideTour.ts | 8 + frontend/src/common/TargetObject.test.ts | 41 + frontend/src/common/TargetObject.ts | 56 ++ frontend/src/common/TourTargets.test.ts | 47 - frontend/src/common/TourTargets.ts | 72 -- frontend/src/common/reactJoyrideSteps.ts | 427 +++++---- frontend/src/components/App/App.test.tsx | 18 +- frontend/src/components/App/App.tsx | 9 +- frontend/src/components/Cart/Items.tsx | 11 +- frontend/src/components/Cart/Searches.tsx | 2 +- .../src/components/Cart/SearchesCard.test.tsx | 2 +- frontend/src/components/Cart/SearchesCard.tsx | 14 +- frontend/src/components/Cart/Summary.tsx | 5 +- frontend/src/components/Cart/index.tsx | 4 +- .../src/components/DataDisplay/Popover.tsx | 2 +- .../src/components/DataDisplay/ToolTip.tsx | 2 +- frontend/src/components/Facets/FacetsForm.tsx | 28 +- .../src/components/Facets/ProjectForm.tsx | 15 +- frontend/src/components/Facets/index.tsx | 6 +- frontend/src/components/Feedback/Alert.tsx | 25 - frontend/src/components/Feedback/Modal.tsx | 9 +- .../src/components/Feedback/Skeleton.test.tsx | 19 - frontend/src/components/Feedback/Skeleton.tsx | 16 - frontend/src/components/Feedback/Spin.tsx | 6 - .../components/Messaging/MessageCard.test.tsx | 13 + .../src/components/Messaging/MessageCard.tsx | 22 + .../components/Messaging/RightDrawer.test.tsx | 11 + .../src/components/Messaging/RightDrawer.tsx | 57 ++ .../components/Messaging/StartPopup.test.tsx | 150 +++ .../src/components/Messaging/StartPopup.tsx | 129 +++ .../Messaging/Templates/ChangeLog.tsx | 21 + .../Messaging/Templates/Welcome.tsx | 120 +++ .../Messaging/messageDisplayData.ts | 44 + frontend/src/components/Messaging/types.ts | 46 + .../src/components/NavBar/LeftMenu.test.tsx | 2 +- frontend/src/components/NavBar/LeftMenu.tsx | 11 +- frontend/src/components/NavBar/NavBar.css | 32 +- .../src/components/NavBar/RightMenu.test.tsx | 20 + frontend/src/components/NavBar/RightMenu.tsx | 64 +- frontend/src/components/NavBar/index.test.tsx | 2 +- frontend/src/components/NavBar/index.tsx | 5 +- .../src/components/NodeStatus/NodeSummary.tsx | 2 +- .../src/components/NodeStatus/index.test.tsx | 16 +- frontend/src/components/NodeStatus/index.tsx | 13 +- frontend/src/components/Search/Citation.tsx | 3 +- frontend/src/components/Search/FilesTable.tsx | 19 +- frontend/src/components/Search/Table.tsx | 34 +- frontend/src/components/Search/Tabs.tsx | 12 +- frontend/src/components/Search/index.tsx | 15 +- frontend/src/components/Support/index.tsx | 1 + frontend/src/setupTests.ts | 28 + frontend/yarn.lock | 900 ++++++++++++++---- 60 files changed, 1941 insertions(+), 750 deletions(-) create mode 100644 frontend/public/changelog/v1.0.7-beta.md create mode 100644 frontend/public/changelog/v1.0.8-beta.md create mode 100644 frontend/public/messages/metagrid_messages.md create mode 100644 frontend/public/messages/test_message.md create mode 100644 frontend/src/common/TargetObject.test.ts create mode 100644 frontend/src/common/TargetObject.ts delete mode 100644 frontend/src/common/TourTargets.test.ts delete mode 100644 frontend/src/common/TourTargets.ts delete mode 100644 frontend/src/components/Feedback/Alert.tsx delete mode 100644 frontend/src/components/Feedback/Skeleton.test.tsx delete mode 100644 frontend/src/components/Feedback/Skeleton.tsx delete mode 100644 frontend/src/components/Feedback/Spin.tsx create mode 100644 frontend/src/components/Messaging/MessageCard.test.tsx create mode 100644 frontend/src/components/Messaging/MessageCard.tsx create mode 100644 frontend/src/components/Messaging/RightDrawer.test.tsx create mode 100644 frontend/src/components/Messaging/RightDrawer.tsx create mode 100644 frontend/src/components/Messaging/StartPopup.test.tsx create mode 100644 frontend/src/components/Messaging/StartPopup.tsx create mode 100644 frontend/src/components/Messaging/Templates/ChangeLog.tsx create mode 100644 frontend/src/components/Messaging/Templates/Welcome.tsx create mode 100644 frontend/src/components/Messaging/messageDisplayData.ts create mode 100644 frontend/src/components/Messaging/types.ts diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 7fbdba7ad..fb94d4792 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -24,10 +24,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Use Node.js 17.x + - name: Use Node.js 18.x uses: actions/setup-node@v2 with: - node-version: "17.x" + node-version: "18.x" - name: Cache node modules uses: actions/cache@v2 diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index acf60454e..b85a9e083 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -13,10 +13,10 @@ jobs: uses: actions/checkout@v2 # Required to run the local ESLint hook - - name: Use Node.js 17.x + - name: Use Node.js 18.x uses: actions/setup-node@v2 with: - node-version: "17.x" + node-version: "18.x" - name: Cache node modules uses: actions/cache@v2 diff --git a/backend/requirements/base.txt b/backend/requirements/base.txt index 8cdeb6c6e..ccb8a3169 100644 --- a/backend/requirements/base.txt +++ b/backend/requirements/base.txt @@ -19,7 +19,7 @@ djangorestframework==3.14.0 # https://github.com/encode/django-rest-framework Markdown==3.4.1 # https://pypi.org/project/Markdown/ django-filter==22.1 # https://github.com/carltongibson/django-filter django-cors-headers==3.14.0 # https://github.com/adamchainz/django-cors-headers -django-allauth==0.52.0 # https://github.com/pennersr/django-allauth +django-allauth==0.54.0 # https://github.com/pennersr/django-allauth dj-rest-auth==2.2.8 # https://github.com/jazzband/dj-rest-auth djangorestframework-simplejwt==5.2.2 # https://github.com/SimpleJWT/django-rest-framework-simplejwt/ drf-yasg==1.21.5 # https://github.com/axnsan12/drf-yasg diff --git a/frontend/package.json b/frontend/package.json index d10b9a45d..c94b15481 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -53,6 +53,9 @@ "./src/components/App/App.tsx": { "lines": 100 } + }, + "moduleNameMapper": { + "react-markdown": "/node_modules/react-markdown/react-markdown.min.js" } }, "dependencies": { @@ -75,6 +78,7 @@ "react-dom": "18.2.0", "react-hotjar": "2.2.1", "react-joyride": "2.5.3", + "react-markdown": "^8.0.7", "react-router-dom": "^6.9.0", "react-scripts": "5.0.1", "typescript": "4.6.3", diff --git a/frontend/public/changelog/v1.0.7-beta.md b/frontend/public/changelog/v1.0.7-beta.md new file mode 100644 index 000000000..d5af88acb --- /dev/null +++ b/frontend/public/changelog/v1.0.7-beta.md @@ -0,0 +1,10 @@ +## Summary + +This is the 'Deployment' update! Moving forward, deployment configuration has been improved, along with a few front-end UI improvements such as a new collapse/expand functionality added to the search facets section. + +**Changes** + +1. Added expand/collapse button for the search facets. +2. Updated deployment configuration implementation. +3. Fixed wget download issues with multiple dataset results. +4. Various package updates and some other minor bug fixes. diff --git a/frontend/public/changelog/v1.0.8-beta.md b/frontend/public/changelog/v1.0.8-beta.md new file mode 100644 index 000000000..f1d809fae --- /dev/null +++ b/frontend/public/changelog/v1.0.8-beta.md @@ -0,0 +1,15 @@ +## Summary + +This is the 'Messages' update! Moving forward, when a new Metagrid version is released, a window will open listing what are the latest changes in the update. This version includes a new 'News' tab at the top where you can view any important Metagrid related messages. First time users will also be prompted with a welcome message which includes links to the current U.I tours. For more details regarding this update, view the list below. + +**Changes** + +1. Added new notification drawer on the right which provides admins a way to communicate with users information relevant to Metagrid. Markdown docs can be displayed and content modified at run-time will be shown. +2. Created new Welcome dialog for first time users which includes buttons to start feature tours or view latest changes. +3. Created Change Log dialog that allows users to see details about latest update +4. Refactored the Joyride tours to improve ease and reliability of future updates +5. Updated test suite to handle latest major package updates and modifications +6. Migrated to the react-router-dom major version 6 +7. Upgraded to Django 4.1.7 and upgraded various backend dependencies +8. Added support for backend url settings +9. Updated various minor frontend dependencies including keycloak-js diff --git a/frontend/public/messages/metagrid_messages.md b/frontend/public/messages/metagrid_messages.md new file mode 100644 index 000000000..a8380a8c7 --- /dev/null +++ b/frontend/public/messages/metagrid_messages.md @@ -0,0 +1,13 @@ +# Welcome to the Metagrid Beta test v1.0.8 + +Use the Help link to find information on how to contact support or report any issues you find. + +## CORDEX data *not* supported + +Metagrid uses an updated user accounts system. Unfortunately for anyone looking for CORDEX data, these new accounts cannot be used to authenticate when running a CORDEX Wget script. Please use an ESGF *legacy* OpenID obtained at any of the ESGF CoG instances listed here: https://esgf.github.io/nodes.html + +## Upcoming changes to ESGF @LLNL + +We are excited to be planning to have an "official" release of the Metagrid platform onto scalable infrastructure. In the meantime we will be testing new features. +* Globus Transfer feature planned to be released in v1.0.9. + diff --git a/frontend/public/messages/test_message.md b/frontend/public/messages/test_message.md new file mode 100644 index 000000000..2a47d9a16 --- /dev/null +++ b/frontend/public/messages/test_message.md @@ -0,0 +1,3 @@ +# This is just a test + +Blah blah diff --git a/frontend/src/common/JoyrideTour.ts b/frontend/src/common/JoyrideTour.ts index cdf89f6ce..2ef6f63e3 100644 --- a/frontend/src/common/JoyrideTour.ts +++ b/frontend/src/common/JoyrideTour.ts @@ -55,6 +55,14 @@ export class JoyrideTour { await func(); } + /** + * + * @param target The element to highlight (CSS selector or an HTML Element) + * @param content The content of the tour window + * @param placement Default location for displaying the window + * @param action A function to call when the tour passes this step + * @returns This tour object + */ addNextStep( target: string, content: string, diff --git a/frontend/src/common/TargetObject.test.ts b/frontend/src/common/TargetObject.test.ts new file mode 100644 index 000000000..d5e6dbee3 --- /dev/null +++ b/frontend/src/common/TargetObject.test.ts @@ -0,0 +1,41 @@ +import { TargetObject, createTestTour } from './TargetObject'; + +describe('Test TourTarget class', () => { + it('returns target object with uuid', () => { + const targetObject: TargetObject = new TargetObject(); + expect(targetObject).toBeTruthy(); + expect(targetObject.class()).toContain('joyrideTarget-'); + expect(targetObject.selector()).toContain('.joyrideTarget-'); + }); + it('returns target object with specified className', () => { + const targetObject: TargetObject = new TargetObject('testClass'); + expect(targetObject).toBeTruthy(); + expect(targetObject.class()).toEqual('testClass'); + expect(targetObject.selector()).toEqual('#root .testClass'); + expect(targetObject.class('testState')).toEqual( + 'testClass target-state_testState' + ); + expect(targetObject.selector('testState')).toEqual( + '#root .testClass .target-state_testState' + ); + }); + it('returns target object with uuid', () => { + const targetObject: TargetObject = new TargetObject('testGroup', 'testId'); + expect(targetObject).toBeTruthy(); + expect(targetObject.class()).toEqual('testGroup_target-testId'); + expect(targetObject.selector()).toEqual('.testGroup_target-testId'); + }); + + it('Successfully creates an empty test joyride tour', () => { + createTestTour('testGroup', {}); + }); + + it('Successfully creates joyride tour fron test targets', () => { + const testTargets = { + test1: new TargetObject('test1'), + test2: new TargetObject('test2'), + test3: new TargetObject('test3'), + }; + createTestTour('testGroup', testTargets); + }); +}); diff --git a/frontend/src/common/TargetObject.ts b/frontend/src/common/TargetObject.ts new file mode 100644 index 000000000..2931f080e --- /dev/null +++ b/frontend/src/common/TargetObject.ts @@ -0,0 +1,56 @@ +import { v4 as uuidv4 } from 'uuid'; +import { JoyrideTour } from './JoyrideTour'; + +export class TargetObject { + private className: string; + + private selectorName: string; + + /** + * + * @param args + */ + public constructor(...args: string[]) { + if (args.length === 0) { + const targetId = uuidv4(); + this.className = `joyrideTarget-${targetId}`; + this.selectorName = `.joyrideTarget-${targetId}`; + } else if (args.length === 1) { + const className = args[0]; + this.className = className; + this.selectorName = `#root .${className}`; + } else { + const groupName = args[0]; + const id = args[1]; + this.className = `${groupName}_target-${id}`; + this.selectorName = `.${groupName}_target-${id}`; + } + } + + class(state?: string): string { + return `${this.className}${state ? ` target-state_${state}` : ''}`; + } + + selector(state?: string): string { + return `${this.selectorName}${state ? ` .target-state_${state}` : ''}`; + } +} + +export const createTestTour = ( + groupName: string, + targetGroup: { + [target: string]: TargetObject; + } +): JoyrideTour => { + const testTour = new JoyrideTour(`Test Tour of ${Object.name} Targets`); + Object.entries(targetGroup).forEach((entry) => { + const name = entry[0]; + const target = entry[1]; + testTour.addNextStep( + target.selector(), + `Group: ${groupName}__ Name: ${name}__ Selector: ${target.selector()}` + ); + }); + + return testTour; +}; diff --git a/frontend/src/common/TourTargets.test.ts b/frontend/src/common/TourTargets.test.ts deleted file mode 100644 index b3e18fe87..000000000 --- a/frontend/src/common/TourTargets.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { TourTargets } from './TourTargets'; - -describe('Test TourTarget class', () => { - it('returns default tour targets', () => { - expect(new TourTargets('Test Targets')).toBeTruthy(); - }); - - it('Creates appropriate tour targets and strings', () => { - const testTargets = new TourTargets('TestTargets'); - testTargets.create('firstTarget').create('nextTarget'); - - // Make sure bad target id returns default - expect(testTargets.getTarget('badTargetId')).toBeUndefined(); - expect(testTargets.getClass('badTargetId')).toEqual('navbar-logo'); - expect(testTargets.getSelector('badTargetId')).toEqual( - '#root .navbar-logo' - ); - - // Good target returns proper values - expect(testTargets.getTarget('firstTarget')?.name).toEqual('firstTarget'); - expect(testTargets.getTarget('firstTarget')?.class).toEqual( - testTargets.getClass('firstTarget') - ); - expect(testTargets.getTarget('firstTarget')?.selector).toEqual( - testTargets.getSelector('firstTarget') - ); - expect(testTargets.getClass('firstTarget', 'test')).toContain( - 'target-state_test' - ); - expect(testTargets.getSelector('firstTarget', 'test')).toContain( - '.target-state_test' - ); - }); - - it('returns a joyride tour of targets', () => { - const testTargets = new TourTargets('TestTargets'); - testTargets.create('firstTarget').create('nextTarget'); - - const testTour = testTargets.createTestTourOfTargets(); - - // Test that a test tour is created - expect(testTour.getSteps().length).toEqual(2); - expect(testTour.getActionByStepIndex(0)?.step.target).toEqual( - testTargets.getSelector('firstTarget') - ); - }); -}); diff --git a/frontend/src/common/TourTargets.ts b/frontend/src/common/TourTargets.ts deleted file mode 100644 index 3ecbafd05..000000000 --- a/frontend/src/common/TourTargets.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { JoyrideTour } from './JoyrideTour'; - -/** - * name: The identifier to use when locating the target - * class: The actual classname of the target - * selector: The css selector which can be used to get the target - */ -export type TourTarget = { - name: string; - class: string; - selector: string; -}; - -export class TourTargets { - private name: string; - - private targets: Map; - - private defaultTarget: TourTarget; - - constructor(name: string) { - this.name = name; - this.targets = new Map(); - this.defaultTarget = { - name: 'default', - class: 'navbar-logo', - selector: '#root .navbar-logo', - }; - } - - create(targetId: string): TourTargets { - const newTarget: TourTarget = { - name: targetId, - class: `${this.name}_target-${targetId}`, - selector: `.${this.name}_target-${targetId}`, - }; - this.targets.set(targetId, newTarget); - return this; - } - - getClass(targetId: string, state?: string): string { - const target = this.targets.get(targetId); - if (target) { - return `${target.class}${state ? ` target-state_${state}` : ''}`; - } - return this.defaultTarget.class; - } - - getSelector(targetId: string, state?: string): string { - const target = this.targets.get(targetId); - if (target) { - return `${target.selector}${state ? `.target-state_${state}` : ''}`; - } - return this.defaultTarget.selector; - } - - getTarget(targetId: string): TourTarget | undefined { - return this.targets.get(targetId); - } - - createTestTourOfTargets(): JoyrideTour { - const testTour = new JoyrideTour(`Test Tour of ${this.name} Targets`); - this.targets.forEach((target) => { - testTour.addNextStep( - target.selector, - `Target Name: ${target.name} __ Target Selector: ${target.selector}` - ); - }); - - return testTour; - } -} diff --git a/frontend/src/common/reactJoyrideSteps.ts b/frontend/src/common/reactJoyrideSteps.ts index 4a01bbcbb..c536bf54a 100644 --- a/frontend/src/common/reactJoyrideSteps.ts +++ b/frontend/src/common/reactJoyrideSteps.ts @@ -1,5 +1,5 @@ import { JoyrideTour } from './JoyrideTour'; -import { TourTargets } from './TourTargets'; +import { TargetObject } from './TargetObject'; import { AppPage } from './types'; export const getCurrentAppPage = (): number => { @@ -75,220 +75,246 @@ const searchLibraryIsEmpty = (): boolean => { return false; }; +export const defaultTarget = new TargetObject('navbar-logo'); + +export const miscTargets = { + defaultTarget, + questionBtn: new TargetObject(), +}; + +export const navBarTargets = { + topSearchBar: new TargetObject(), + topNavBar: new TargetObject(), + searchPageBtn: new TargetObject(), + cartPageBtn: new TargetObject(), + savedSearchPageBtn: new TargetObject(), + nodeStatusBtn: new TargetObject(), + newsBtn: new TargetObject(), + signInBtn: new TargetObject(), + helpBtn: new TargetObject(), +}; + +export const searchTableTargets = { + queryString: new TargetObject(), + resultsFoundText: new TargetObject(), + searchResultsTable: new TargetObject(), + addSelectedToCartBtn: new TargetObject(), + saveSearchBtn: new TargetObject(), + copySearchLinkBtn: new TargetObject(), +}; + +export const leftSidebarTargets = { + leftSideBar: new TargetObject(), + selectProjectBtn: new TargetObject(), + projectSelectLeftSideBtn: new TargetObject(), + projectWebsiteBtn: new TargetObject(), + searchFacetsForm: new TargetObject(), + facetFormGeneral: new TargetObject(), + facetFormFields: new TargetObject(), + facetFormCollapseAllBtn: new TargetObject(), + facetFormExpandAllBtn: new TargetObject(), + facetFormAdditional: new TargetObject(), + facetFormAdditionalFields: new TargetObject(), + facetFormFilename: new TargetObject(), + facetFormFilenameFields: new TargetObject(), +}; + +export const topDataRowTargets = { + searchResultsRowExpandIcon: new TargetObject(), + searchResultsRowContractIcon: new TargetObject(), + cartAddBtn: new TargetObject(), + nodeStatusIcon: new TargetObject(), + datasetTitle: new TargetObject(), + fileCount: new TargetObject(), + totalSize: new TargetObject(), + versionText: new TargetObject(), + downloadScriptForm: new TargetObject(), + downloadScriptOptions: new TargetObject(), + downloadScriptBtn: new TargetObject(), +}; + +export const innerDataRowTargets = { + filesTab: new TargetObject(), + metadataTab: new TargetObject(), + metadataLookupField: new TargetObject(), + citationTab: new TargetObject(), + additionalTab: new TargetObject(), + filesTitle: new TargetObject(), + dataSize: new TargetObject(), + downloadDataBtn: new TargetObject(), + copyUrlBtn: new TargetObject(), + checksum: new TargetObject(), +}; + +export const cartTourTargets = { + cartSummary: new TargetObject(), + datasetBtn: new TargetObject(), + libraryBtn: new TargetObject(), + downloadAllType: new TargetObject(), + downloadAllBtn: new TargetObject(), + removeItemsBtn: new TargetObject(), +}; + +export const savedSearchTourTargets = { + savedSearches: new TargetObject(), + projectDescription: new TargetObject(), + searchQueryString: new TargetObject(), + applySearch: new TargetObject(), + jsonBtn: new TargetObject(), + removeBtn: new TargetObject(), +}; + +export const nodeTourTargets = { + updateTime: new TargetObject(), + nodeStatusSummary: new TargetObject(), + nodeColHeader: new TargetObject(), + onlineColHeader: new TargetObject(), + sourceColHeader: new TargetObject(), +}; + // Used when creating the tour, as the title that user sees export enum TourTitles { Main = 'Main Search Page Tour', Cart = 'Data Cart Tour', Searches = 'Saved Searches Tour', Node = 'Node Status Tour', + Welcome = 'Welcome Tour', } -export const navBarTargets = new TourTargets('nav-bar-tour') - .create('topSearchBar') - .create('topNavBar') - .create('searchPageBtn') - .create('cartPageBtn') - .create('savedSearchPageBtn') - .create('nodeStatusBtn') - .create('signInBtn'); - -export const searchTableTargets = new TourTargets('search-table-tour') - .create('queryString') - .create('resultsFoundText') - .create('searchResultsTable') - .create('addSelectedToCartBtn') - .create('saveSearchBtn') - .create('copySearchLinkBtn'); - -export const leftSidebarTargets = new TourTargets('left-sidebar-tour') - .create('leftSideBar') - .create('selectProjectBtn') - .create('projectSelectLeftSideBtn') - .create('projectWebsiteBtn') - .create('searchFacetsForm') - .create('facetFormExpandAllBtn') - .create('facetFormCollapseAllBtn') - .create('facetFormGeneral') - .create('facetFormFields') - .create('facetFormAdditional') - .create('facetFormAdditionalFields') - .create('facetFormFilename') - .create('facetFormFilenameFields'); - -export const topDataRowTargets = new TourTargets('top-data-row-tour') - .create('searchResultsRowExpandIcon') - .create('searchResultsRowContractIcon') - .create('cartAddBtn') - .create('nodeStatusIcon') - .create('datasetTitle') - .create('fileCount') - .create('totalSize') - .create('versionText') - .create('downloadScriptForm') - .create('downloadScriptOptions') - .create('downloadScriptBtn'); - -export const innerDataRowTargets = new TourTargets('inner-data-row-tour') - .create('filesTab') - .create('metadataTab') - .create('metadataLookupField') - .create('citationTab') - .create('additionalTab') - .create('filesTitle') - .create('dataSize') - .create('downloadDataBtn') - .create('copyUrlBtn') - .create('checksum'); - -export const cartTourTargets = new TourTargets('cart-tour') - .create('cartSummary') - .create('datasetBtn') - .create('libraryBtn') - .create('downloadAllType') - .create('downloadAllBtn') - .create('removeItemsBtn'); - -export const savedSearchTourTargets = new TourTargets('saved-search-tour') - .create('savedSearches') - .create('projectDescription') - .create('searchQueryString') - .create('applySearch') - .create('jsonBtn') - .create('removeBtn'); - -export const nodeTourTargets = new TourTargets('node-tour') - .create('updateTime') - .create('nodeStatusSummary') - .create('nodeColHeader') - .create('onlineColHeader') - .create('sourceColHeader'); - const addDataRowTourSteps = (tour: JoyrideTour): JoyrideTour => { tour .addNextStep( - topDataRowTargets.getSelector('datasetTitle'), + topDataRowTargets.datasetTitle.selector(), 'Each row provides access to a specific dataset. The title of the dataset is shown here.', 'top-start' ) .addNextStep( - topDataRowTargets.getSelector('nodeStatusIcon'), + topDataRowTargets.nodeStatusIcon.selector(), "This icon shows the current status of the node which hosts this dataset. When hovering over the icon you will see more detail as to the node's status.", 'top-start' ) .addNextStep( - topDataRowTargets.getSelector('fileCount'), + topDataRowTargets.fileCount.selector(), 'This shows how many separate files are contained in this dataset.', 'top-start' ) .addNextStep( - topDataRowTargets.getSelector('totalSize'), + topDataRowTargets.totalSize.selector(), 'This shows the total size of the dataset with all of its files.', 'top-start' ) .addNextStep( - topDataRowTargets.getSelector('versionText'), + topDataRowTargets.versionText.selector(), 'The version number or preparation date is shown in this column (depending on the dataset).', 'top-start' ) .addNextStep( - topDataRowTargets.getSelector('downloadScriptForm'), + topDataRowTargets.downloadScriptForm.selector(), 'If you wish to download the entire dataset, you can do so by first obtaining the download script.', 'top-start' ) .addNextStep( - topDataRowTargets.getSelector('downloadScriptOptions'), + topDataRowTargets.downloadScriptOptions.selector(), 'This drop-down allows you to select which type of script you wish to download. Currently wget is the only form supported.', 'top' ) .addNextStep( - topDataRowTargets.getSelector('downloadScriptBtn'), + topDataRowTargets.downloadScriptBtn.selector(), 'Clicking this button will begin the download of your script.', 'top' ) .addNextStep( - topDataRowTargets.getSelector('searchResultsRowExpandIcon'), + topDataRowTargets.searchResultsRowExpandIcon.selector(), 'To view more information about a specific dataset, you can expand the row by clicking this little arrow icon...', 'top-start', /* istanbul ignore next */ async () => { clickFirstElement( - topDataRowTargets.getSelector('searchResultsRowExpandIcon') + topDataRowTargets.searchResultsRowExpandIcon.selector() ); await delay(500); } ) .addNextStep( - innerDataRowTargets.getSelector('filesTab'), + innerDataRowTargets.filesTab.selector(), 'The file information tab is open by default. Within this tab, it is possible to view individual files in the dataset for access and download.', 'top-start' ) .addNextStep( - innerDataRowTargets.getSelector('filesTitle'), + innerDataRowTargets.filesTitle.selector(), 'This shows the title of a specific file contained within the dataset.', 'top-start' ) .addNextStep( - innerDataRowTargets.getSelector('dataSize'), + innerDataRowTargets.dataSize.selector(), 'This shows the size of the specific file in the dataset.', 'top-start' ) .addNextStep( - innerDataRowTargets.getSelector('downloadDataBtn'), + innerDataRowTargets.downloadDataBtn.selector(), 'Clicking this button will initiate a direct download of this data file via HTTPS.', 'top-start' ) .addNextStep( - innerDataRowTargets.getSelector('copyUrlBtn'), + innerDataRowTargets.copyUrlBtn.selector(), 'Clicking this button will copy the OPEN DAP URL of this file directly to your clipboard.', 'top-start' ) .addNextStep( - innerDataRowTargets.getSelector('checksum'), + innerDataRowTargets.checksum.selector(), 'The checksum of the specified file is shown here.', 'top-start' ) .addNextStep( - innerDataRowTargets.getSelector('metadataTab'), + innerDataRowTargets.metadataTab.selector(), 'This is the Metadata tab. If you click it, you can view metadata for the dataset...', 'top-start', /* istanbul ignore next */ async () => { await delay(300); - clickFirstElement(innerDataRowTargets.getSelector('metadataTab')); + clickFirstElement(innerDataRowTargets.metadataTab.selector()); } ) .addNextStep( - innerDataRowTargets.getSelector('metadataLookupField'), + innerDataRowTargets.metadataLookupField.selector(), 'Besides seeing the metadata listed below, this field can help you search for a specific key/value pair of metadata.', 'top-start', /* istanbul ignore next */ async () => { await delay(300); - if (elementExists(innerDataRowTargets.getClass('citationTab'))) { - clickFirstElement(innerDataRowTargets.getSelector('citationTab')); + if (elementExists(innerDataRowTargets.citationTab.class())) { + clickFirstElement(innerDataRowTargets.citationTab.selector()); + } else if (!elementExists(innerDataRowTargets.additionalTab.class())) { + clickFirstElement( + topDataRowTargets.searchResultsRowContractIcon.selector() + ); } } ) .addNextStep( - innerDataRowTargets.getSelector('citationTab'), + innerDataRowTargets.citationTab.selector(), 'Citation information for the dataset can be viewed within this tab...', 'top-start', /* istanbul ignore next */ async () => { await delay(300); - if (elementExists(innerDataRowTargets.getClass('additionalTab'))) { - clickFirstElement(innerDataRowTargets.getSelector('additionalTab')); + if (elementExists(innerDataRowTargets.additionalTab.class())) { + clickFirstElement(innerDataRowTargets.additionalTab.selector()); + } else { + clickFirstElement( + topDataRowTargets.searchResultsRowContractIcon.selector() + ); } } ) .addNextStep( - innerDataRowTargets.getSelector('additionalTab'), + innerDataRowTargets.additionalTab.selector(), 'You can view additional data and sources by clicking this tab.', 'top-start', /* istanbul ignore next */ async () => { clickFirstElement( - topDataRowTargets.getSelector('searchResultsRowContractIcon') + topDataRowTargets.searchResultsRowContractIcon.selector() ); await delay(300); } @@ -297,6 +323,23 @@ const addDataRowTourSteps = (tour: JoyrideTour): JoyrideTour => { return tour; }; +export const welcomeTour = new JoyrideTour(TourTitles.Welcome) + .addNextStep( + 'body', + 'Just a note: We are continually striving to improve the Metagrid user interface and make it more intuitive. However, if you ever feel stuck, please try out the interface tours. The following is a quick tour showing where you can access support.', + 'center' + ) + .addNextStep( + navBarTargets.helpBtn.selector(), + 'This help button will open the Metagrid support dialog, which contains interface tours (like this one) as well as helpful resources.', + 'bottom' + ) + .addNextStep( + miscTargets.questionBtn.selector(), + 'This question button will also open the Metagrid support dialog. Note that the tour button shown in the support dialog will be specific to the current page you are on.', + 'top-end' + ); + export const createMainPageTour = (): JoyrideTour => { const tour = new JoyrideTour(TourTitles.Main) .addNextStep( @@ -305,42 +348,52 @@ export const createMainPageTour = (): JoyrideTour => { 'center' ) .addNextStep( - navBarTargets.getSelector('topSearchBar'), + navBarTargets.topSearchBar.selector(), 'This is the top search bar! You can select a project, then enter a search term and click the magnifying glass button to quickly start your search and view results in the table below.', 'bottom' ) .addNextStep( - navBarTargets.getSelector('topNavBar'), + navBarTargets.topNavBar.selector(), 'This area lets you navigate between pages of Metagrid.', 'bottom' ) .addNextStep( - navBarTargets.getSelector('searchPageBtn'), + navBarTargets.searchPageBtn.selector(), "Clicking this button takes you to the main search page (Metagrid's home page.)", 'bottom' ) .addNextStep( - navBarTargets.getSelector('cartPageBtn'), + navBarTargets.cartPageBtn.selector(), 'This button takes you to the data cart page where you can view the data you have selected for download.', 'bottom' ) .addNextStep( - navBarTargets.getSelector('savedSearchPageBtn'), + navBarTargets.savedSearchPageBtn.selector(), 'To view your currently saved searches, you would click here.', 'bottom' ) .addNextStep( - navBarTargets.getSelector('nodeStatusBtn'), + navBarTargets.nodeStatusBtn.selector(), 'If you are curious about data node status, you can visit the status page by clicking here.', 'bottom' ) .addNextStep( - navBarTargets.getSelector('signInBtn'), + navBarTargets.newsBtn.selector(), + "Clicking the news button will open up the message center to the right, where you'll find important notes from the admins and developers. You can also view changelog information regarding the latest version of Metagrid", + 'bottom' + ) + .addNextStep( + navBarTargets.signInBtn.selector(), 'Clicking this button will allow you to sign in to your profile.', 'bottom' ) .addNextStep( - leftSidebarTargets.getSelector('selectProjectBtn'), + navBarTargets.helpBtn.selector(), + "Clicking this 'Help' button will open the support dialog, where you can view interface tours (like this), or get links to helpful documentation.", + 'bottom' + ) + .addNextStep( + leftSidebarTargets.selectProjectBtn.selector(), 'To begin a search, you would first select a project from this drop-down.', 'right' ); @@ -349,17 +402,17 @@ export const createMainPageTour = (): JoyrideTour => { if (mainTableEmpty()) { tour .addNextStep( - leftSidebarTargets.getSelector('projectSelectLeftSideBtn'), + leftSidebarTargets.projectSelectLeftSideBtn.selector(), 'Then you click this button to load the results for the project you selected...', 'right', () => { clickFirstElement( - leftSidebarTargets.getSelector('projectSelectLeftSideBtn') + leftSidebarTargets.projectSelectLeftSideBtn.selector() ); } ) .addNextStep( - leftSidebarTargets.getSelector('projectSelectLeftSideBtn'), + leftSidebarTargets.projectSelectLeftSideBtn.selector(), "NOTE: The search results may take a few seconds to load... Click 'next' to continue.", 'right', async () => { @@ -370,7 +423,7 @@ export const createMainPageTour = (): JoyrideTour => { ); } else { tour.addNextStep( - leftSidebarTargets.getSelector('projectSelectLeftSideBtn'), + leftSidebarTargets.projectSelectLeftSideBtn.selector(), 'Then you click this button to load results for the project you selected.', 'right' ); @@ -378,116 +431,112 @@ export const createMainPageTour = (): JoyrideTour => { tour .addNextStep( - leftSidebarTargets.getSelector('projectWebsiteBtn'), + leftSidebarTargets.projectWebsiteBtn.selector(), 'Once a project is selected, if you wish, you can go view the project website by clicking this button.', 'right' ) .addNextStep( - leftSidebarTargets.getSelector('searchFacetsForm'), + leftSidebarTargets.searchFacetsForm.selector(), 'This area contains various groups of facets and parameters that you can use to filter results from your selected project.', 'right' ) .addNextStep( - leftSidebarTargets.getSelector('facetFormGeneral'), + leftSidebarTargets.facetFormGeneral.selector(), 'To filter by facets provided within this group, you would open this collapsable form by clicking on it...', 'right-end', /* istanbul ignore next */ async () => { // Open general facets - clickFirstElement(leftSidebarTargets.getSelector('facetFormGeneral')); + clickFirstElement(leftSidebarTargets.facetFormGeneral.selector()); await delay(300); } ) .addNextStep( - leftSidebarTargets.getSelector('facetFormFields'), + leftSidebarTargets.facetFormFields.selector(), 'These are facets that are available within this group. The drop-downs allow you to select multiple items you wish to include in your search. Note that you can search for elements in the drop-down by typing within the input area.', 'right-start', /* istanbul ignore next */ async () => { // Close general facets - clickFirstElement(leftSidebarTargets.getSelector('facetFormGeneral')); + clickFirstElement(leftSidebarTargets.facetFormGeneral.selector()); await delay(300); // Close facet panels if more than one is open - if ( - elementExists(leftSidebarTargets.getClass('facetFormCollapseAllBtn')) - ) { + if (elementExists(leftSidebarTargets.facetFormCollapseAllBtn.class())) { clickFirstElement( - leftSidebarTargets.getSelector('facetFormCollapseAllBtn') + leftSidebarTargets.facetFormCollapseAllBtn.selector() ); await delay(50); } } ) .addNextStep( - leftSidebarTargets.getSelector('facetFormExpandAllBtn'), + leftSidebarTargets.facetFormExpandAllBtn.selector(), 'You can quickly expand all the facet panels by clicking this button.', 'right-end', /* istanbul ignore next */ async () => { // Expand all facets - clickFirstElement( - leftSidebarTargets.getSelector('facetFormExpandAllBtn') - ); + clickFirstElement(leftSidebarTargets.facetFormExpandAllBtn.selector()); await delay(300); } ) .addNextStep( - leftSidebarTargets.getSelector('facetFormCollapseAllBtn'), + leftSidebarTargets.facetFormCollapseAllBtn.selector(), "Note that there is a scroll bar on the right when the panels don't all fit on the page. Clicking the collapse all button will close all the open facet panels.", 'right-end', /* istanbul ignore next */ async () => { // Open general facets clickFirstElement( - leftSidebarTargets.getSelector('facetFormCollapseAllBtn') + leftSidebarTargets.facetFormCollapseAllBtn.selector() ); await delay(300); } ) .addNextStep( - leftSidebarTargets.getSelector('facetFormAdditionalFields'), + leftSidebarTargets.facetFormAdditionalFields.selector(), 'This section contains additional properties that you can select to further refine your search results, including the Version Type, Result Type and Version Date Range. Hovering over the question mark icon will further explain the parameter.', 'right-end', /* istanbul ignore next */ async () => { // Open filename section - clickFirstElement(leftSidebarTargets.getSelector('facetFormFilename')); + clickFirstElement(leftSidebarTargets.facetFormFilename.selector()); await delay(300); } ) .addNextStep( - leftSidebarTargets.getSelector('facetFormFilenameFields'), + leftSidebarTargets.facetFormFilenameFields.selector(), 'This section lets you filter your results to include a specific filename. To filter by filename, you would type in the name or names as a list of comma separated values then click the magnifying glass icon to add it as a search parameter.', 'right-end', /* istanbul ignore next */ () => { // Close filename section - clickFirstElement(leftSidebarTargets.getSelector('facetFormFilename')); + clickFirstElement(leftSidebarTargets.facetFormFilename.selector()); window.scrollTo(0, 0); } ) .addNextStep( - searchTableTargets.getSelector('queryString'), + searchTableTargets.queryString.selector(), "When performing a search, you'll be able to view the resulting query generated by your selections here.", 'bottom' ) .addNextStep( - searchTableTargets.getSelector('resultsFoundText'), + searchTableTargets.resultsFoundText.selector(), 'This will display how many results were returned from your search.', 'bottom' ) .addNextStep( - searchTableTargets.getSelector('saveSearchBtn'), + searchTableTargets.saveSearchBtn.selector(), 'If you are happy with your search results and plan to perform this search again, you can save your search by clicking this button.', 'left' ) .addNextStep( - searchTableTargets.getSelector('copySearchLinkBtn'), + searchTableTargets.copySearchLinkBtn.selector(), 'You can also share your search with others as a specific URL by clicking this button. The button will copy the link to your clipboard for you to then paste at your convenience.', 'bottom-start' ) .addNextStep( - searchTableTargets.getSelector('searchResultsTable'), + searchTableTargets.searchResultsTable.selector(), 'These are your search results! Each row in the results table is a specific dataset that matches your criteria.', 'top-start' ) @@ -504,7 +553,7 @@ export const createMainPageTour = (): JoyrideTour => { } ) .addNextStep( - searchTableTargets.getSelector('addSelectedToCartBtn'), + searchTableTargets.addSelectedToCartBtn.selector(), 'Then to add them to your cart, you would click this button.', 'bottom-start', /* istanbul ignore next */ @@ -516,12 +565,12 @@ export const createMainPageTour = (): JoyrideTour => { } ) .addNextStep( - topDataRowTargets.getSelector('cartAddBtn', 'plus'), + topDataRowTargets.cartAddBtn.selector('plus'), "You can also directly add a specific dataset to the cart by clicking it's plus button here.", 'top-start' ) .addNextStep( - topDataRowTargets.getSelector('cartAddBtn', 'minus'), + topDataRowTargets.cartAddBtn.selector('minus'), 'Or you can remove a dataset from the cart by clicking its minus button here.', 'top-start' ); @@ -559,11 +608,11 @@ export const createCartItemsTour = ( 'center' ) .addNextStep( - cartTourTargets.getSelector('datasetBtn'), + cartTourTargets.datasetBtn.selector(), 'Note that we are currently in the data cart tab.' ) .addNextStep( - cartTourTargets.getSelector('libraryBtn'), + cartTourTargets.libraryBtn.selector(), 'Clicking this would switch you to the search library tab. However we will stay in the data cart for this tour.' ); @@ -592,17 +641,17 @@ export const createCartItemsTour = ( if (mainTableEmpty()) { tour .addNextStep( - leftSidebarTargets.getSelector('projectSelectLeftSideBtn'), + leftSidebarTargets.projectSelectLeftSideBtn.selector(), 'First we will click this button to load results from a project into the search table...', 'right', () => { clickFirstElement( - leftSidebarTargets.getSelector('projectSelectLeftSideBtn') + leftSidebarTargets.projectSelectLeftSideBtn.selector() ); } ) .addNextStep( - leftSidebarTargets.getSelector('projectSelectLeftSideBtn'), + leftSidebarTargets.projectSelectLeftSideBtn.selector(), "NOTE: The search results may take a few seconds to load... Click 'next' to continue.", 'right', async () => { @@ -612,23 +661,19 @@ export const createCartItemsTour = ( } tour .addNextStep( - searchTableTargets.getSelector('searchResultsTable'), + searchTableTargets.searchResultsTable.selector(), "Let's go ahead and add some datasets to the cart...", 'top-start', /* istanbul ignore next */ async () => { - clickFirstElement( - topDataRowTargets.getSelector('cartAddBtn', 'plus') - ); + clickFirstElement(topDataRowTargets.cartAddBtn.selector('plus')); await delay(500); - clickFirstElement( - topDataRowTargets.getSelector('cartAddBtn', 'plus') - ); + clickFirstElement(topDataRowTargets.cartAddBtn.selector('plus')); await delay(500); } ) .addNextStep( - navBarTargets.getSelector('cartPageBtn'), + navBarTargets.cartPageBtn.selector(), 'Now that there are datasets in the cart, we will go view them in the cart page...', 'bottom', /* istanbul ignore next */ @@ -641,7 +686,7 @@ export const createCartItemsTour = ( tour .addNextStep( - cartTourTargets.getSelector('cartSummary'), + cartTourTargets.cartSummary.selector(), 'This shows a summary of all the datasets in the cart. From here you can see the total datasets, files and total file size at a glance. Note: The summary is visible to both the data cart and search library.' ) .addNextStep( @@ -649,7 +694,7 @@ export const createCartItemsTour = ( 'This table shows the datasets that have been added to the cart.' ) .addNextStep( - topDataRowTargets.getSelector('cartAddBtn', 'minus'), + topDataRowTargets.cartAddBtn.selector('minus'), 'You can remove a dataset from the cart by clicking its minus button here.', 'top-start' ); @@ -669,12 +714,12 @@ export const createCartItemsTour = ( } ) .addNextStep( - cartTourTargets.getSelector('downloadAllType'), + cartTourTargets.downloadAllType.selector(), 'This will select which download script to use (only wget is available currently).', 'top-start' ) .addNextStep( - cartTourTargets.getSelector('downloadAllBtn'), + cartTourTargets.downloadAllBtn.selector(), 'Then you would click this button to get the download script needed for all currently selected datasets in the cart.', 'top-start', /* istanbul ignore next */ @@ -686,7 +731,7 @@ export const createCartItemsTour = ( } ) .addNextStep( - cartTourTargets.getSelector('removeItemsBtn'), + cartTourTargets.removeItemsBtn.selector(), 'We can remove all items from the cart with this button.', 'right-start' ) @@ -697,7 +742,7 @@ export const createCartItemsTour = ( // Clean-up step for when the tour is complete (or skipped) return async () => { if (cartItemsAdded) { - clickFirstElement(cartTourTargets.getSelector('removeItemsBtn')); + clickFirstElement(cartTourTargets.removeItemsBtn.selector()); await delay(500); clickFirstElement('.ant-popover-buttons .ant-btn-primary'); await delay(300); @@ -724,11 +769,11 @@ export const createSearchCardTour = ( 'center' ) .addNextStep( - cartTourTargets.getSelector('libraryBtn'), + cartTourTargets.libraryBtn.selector(), 'Note that we are currently in the search library tab.' ) .addNextStep( - cartTourTargets.getSelector('datasetBtn'), + cartTourTargets.datasetBtn.selector(), 'Clicking this would switch you to the data cart tab. We will remain on the search tab for this tour.' ); @@ -757,17 +802,17 @@ export const createSearchCardTour = ( if (mainTableEmpty()) { tour .addNextStep( - leftSidebarTargets.getSelector('projectSelectLeftSideBtn'), + leftSidebarTargets.projectSelectLeftSideBtn.selector(), 'First we will click this button to load results from a project into the search table...', 'right', () => { clickFirstElement( - leftSidebarTargets.getSelector('projectSelectLeftSideBtn') + leftSidebarTargets.projectSelectLeftSideBtn.selector() ); } ) .addNextStep( - leftSidebarTargets.getSelector('projectSelectLeftSideBtn'), + leftSidebarTargets.projectSelectLeftSideBtn.selector(), "NOTE: The search results may take a few seconds to load... Click 'next' to continue.", 'right', async () => { @@ -779,17 +824,17 @@ export const createSearchCardTour = ( } tour .addNextStep( - searchTableTargets.getSelector('saveSearchBtn'), + searchTableTargets.saveSearchBtn.selector(), 'To save the current search to the library, we need to click this button...', 'bottom-start', /* istanbul ignore next */ async () => { - clickFirstElement(searchTableTargets.getSelector('saveSearchBtn')); + clickFirstElement(searchTableTargets.saveSearchBtn.selector()); await delay(500); } ) .addNextStep( - navBarTargets.getSelector('savedSearchPageBtn'), + navBarTargets.savedSearchPageBtn.selector(), 'We can now go back to the search library and view our recently added search...', 'bottom', /* istanbul ignore next */ @@ -801,34 +846,34 @@ export const createSearchCardTour = ( } tour .addNextStep( - cartTourTargets.getSelector('cartSummary'), + cartTourTargets.cartSummary.selector(), 'This shows a summary of all the datasets in the data cart. The summary is visible to both the data cart and search library.' ) .addNextStep( - savedSearchTourTargets.getSelector('savedSearches'), + savedSearchTourTargets.savedSearches.selector(), 'Your saved searches are shown as cards in this row.', 'bottom' ) .addNextStep( - savedSearchTourTargets.getSelector('projectDescription'), + savedSearchTourTargets.projectDescription.selector(), 'This is the project selected for the search.', 'top' ) .addNextStep( - savedSearchTourTargets.getSelector('searchQueryString'), + savedSearchTourTargets.searchQueryString.selector(), 'This shows the query used by the search to list results.' ) .addNextStep( - savedSearchTourTargets.getSelector('applySearch'), + savedSearchTourTargets.applySearch.selector(), 'Clicking this button will apply your saved search to the main results page.' ) .addNextStep( - savedSearchTourTargets.getSelector('jsonBtn'), + savedSearchTourTargets.jsonBtn.selector(), 'Clicking this button will show the JSON data associated with this search.', 'right' ) .addNextStep( - savedSearchTourTargets.getSelector('removeBtn'), + savedSearchTourTargets.removeBtn.selector(), 'This button will remove this search from your saved searches.', 'left-start' ) @@ -839,7 +884,7 @@ export const createSearchCardTour = ( // Clean-up step for when the tour is complete (or skipped) return async () => { if (searchSaved) { - clickFirstElement(savedSearchTourTargets.getSelector('removeBtn')); + clickFirstElement(savedSearchTourTargets.removeBtn.selector()); await delay(500); } }; @@ -857,38 +902,38 @@ export const createNodeStatusTour = (): JoyrideTour => { 'center' ) .addNextStep( - nodeTourTargets.getSelector('updateTime'), + nodeTourTargets.updateTime.selector(), 'This is the timestamp for the last time the node status was updated.' ) .addNextStep( - nodeTourTargets.getSelector('nodeStatusSummary'), + nodeTourTargets.nodeStatusSummary.selector(), 'This area provides an overall summary of the number of nodes that are available, how many are currently online and how many are currently offline.' ) .addNextStep( - nodeTourTargets.getSelector('nodeColHeader'), + nodeTourTargets.nodeColHeader.selector(), 'This column lists the various nodes that are registered to serve the data with Metagrid. Clicking the header will toggle the sort between ascending and descending like so...', 'top', /* istanbul ignore next */ async () => { - clickFirstElement(nodeTourTargets.getSelector('nodeColHeader')); + clickFirstElement(nodeTourTargets.nodeColHeader.selector()); await delay(500); } ) .addNextStep( - nodeTourTargets.getSelector('onlineColHeader'), + nodeTourTargets.onlineColHeader.selector(), 'This column shows the online status of each node. A green check-mark indicates the node is online whereas a red x mark indicates it is offline. As with the node column, you can click this to sort by node status like so...', 'top', /* istanbul ignore next */ async () => { - clickFirstElement(nodeTourTargets.getSelector('onlineColHeader')); + clickFirstElement(nodeTourTargets.onlineColHeader.selector()); await delay(700); - clickFirstElement(nodeTourTargets.getSelector('onlineColHeader')); + clickFirstElement(nodeTourTargets.onlineColHeader.selector()); await delay(700); - clickFirstElement(nodeTourTargets.getSelector('nodeColHeader')); + clickFirstElement(nodeTourTargets.nodeColHeader.selector()); } ) .addNextStep( - nodeTourTargets.getSelector('sourceColHeader'), + nodeTourTargets.sourceColHeader.selector(), 'This column shows links to the THREDDS catalog of its respective node.' ) .addNextStep( diff --git a/frontend/src/components/App/App.test.tsx b/frontend/src/components/App/App.test.tsx index bbdbd11e4..a451e6cbc 100644 --- a/frontend/src/components/App/App.test.tsx +++ b/frontend/src/components/App/App.test.tsx @@ -1125,22 +1125,28 @@ describe('User search library', () => { }); describe('User support', () => { - it('renders user support modal when clicking fixed button and is closeable', () => { - const { getByRole } = customRender(); + it('renders user support modal when clicking help button and is closeable', () => { + const { getByRole, getByText, findByText } = customRender( + + ); // support button renders - const supportBtn = getByRole('img', { name: 'question', hidden: true }); + const supportBtn = getByRole('img', { name: 'question' }); expect(supportBtn).toBeTruthy(); // click support button fireEvent.click(supportBtn); // GitHub icon renders - const githubIcon = getByRole('img', { name: 'github', hidden: true }); - expect(githubIcon).toBeTruthy(); + const metagridSupportHeader = findByText(' MetaGrid Support'); + expect(metagridSupportHeader).toBeTruthy(); + + // click close button + // const closeBtn = getAllByRole('img', { name: 'close' })[1]; + // fireEvent.click(closeBtn); // click close button - const closeBtn = getByRole('img', { name: 'close' }); + const closeBtn = getByText('Close Support'); fireEvent.click(closeBtn); }); }); diff --git a/frontend/src/components/App/App.tsx b/frontend/src/components/App/App.tsx index 5c2106192..bf3814248 100644 --- a/frontend/src/components/App/App.tsx +++ b/frontend/src/components/App/App.tsx @@ -51,7 +51,10 @@ import { VersionType, } from '../Search/types'; import Support from '../Support'; +import StartPopup from '../Messaging/StartPopup'; +import startupDisplayData from '../Messaging/messageDisplayData'; import './App.css'; +import { miscTargets } from '../../common/reactJoyrideSteps'; const styles: CSSinJS = { bodySider: { @@ -79,7 +82,7 @@ export type Props = { searchQuery: ActiveSearchQuery; }; -const metagridVersion = '1.0.8-beta'; +const metagridVersion: string = startupDisplayData.messageToShow; const App: React.FC = ({ searchQuery }) => { // Third-party tool integration @@ -651,6 +654,7 @@ const App: React.FC = ({ searchQuery }) => { = ({ searchQuery }) => { type="primary" shape="circle" style={{ width: '48px', height: '48px' }} - icon={} + icon={} onClick={() => setSupportModalVisible(true)} > @@ -669,6 +673,7 @@ const App: React.FC = ({ searchQuery }) => { visible={supportModalVisible} onClose={() => setSupportModalVisible(false)} /> +
); diff --git a/frontend/src/components/Cart/Items.tsx b/frontend/src/components/Cart/Items.tsx index c510a7804..32199a99c 100644 --- a/frontend/src/components/Cart/Items.tsx +++ b/frontend/src/components/Cart/Items.tsx @@ -3,7 +3,7 @@ import { DownloadOutlined, QuestionCircleOutlined, } from '@ant-design/icons'; -import { Col, Form, message, Row, Select } from 'antd'; +import { Col, Form, message, Popconfirm, Row, Select } from 'antd'; import React from 'react'; import { fetchWgetScript, @@ -13,7 +13,7 @@ import { import { cartTourTargets } from '../../common/reactJoyrideSteps'; import { CSSinJS } from '../../common/types'; import Empty from '../DataDisplay/Empty'; -import Popconfirm from '../Feedback/Popconfirm'; +// import Popconfirm from '../Feedback/Popconfirm'; import Button from '../General/Button'; import Table from '../Search/Table'; import { RawSearchResults } from '../Search/types'; @@ -88,12 +88,13 @@ const Items: React.FC = ({ userCart, onUpdateCart, onClearCart }) => {
{userCart.length > 0 && ( } onConfirm={onClearCart} >
{!objectIsEmpty(availableFacets) && ( -
+
= ({ - message, - description, - type, - showIcon = true, -}) => ( - -); - -export default Alert; diff --git a/frontend/src/components/Feedback/Modal.tsx b/frontend/src/components/Feedback/Modal.tsx index b7f2f65c7..186463055 100644 --- a/frontend/src/components/Feedback/Modal.tsx +++ b/frontend/src/components/Feedback/Modal.tsx @@ -1,29 +1,34 @@ import { Button, Modal as ModalD } from 'antd'; -import React from 'react'; +import React, { CSSProperties } from 'react'; type Props = { visible: boolean; title?: React.ReactNode; + closeText: string; onClose?: () => void; centered?: boolean; children: React.ReactNode; + style?: CSSProperties; }; const Modal: React.FC = ({ visible, title, onClose, + closeText, centered, children, + style, }) => ( - Close + {closeText} , ]} > diff --git a/frontend/src/components/Feedback/Skeleton.test.tsx b/frontend/src/components/Feedback/Skeleton.test.tsx deleted file mode 100644 index 9c00699c4..000000000 --- a/frontend/src/components/Feedback/Skeleton.test.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; - -import Skeleton from './Skeleton'; - -it('returns component', () => { - const { getByTestId } = render(); - const skeleton = getByTestId('skeleton'); - expect(skeleton).toBeTruthy(); -}); - -it('returns component without active animation effect', () => { - const { getByTestId } = render(); - const skeleton = getByTestId('skeleton'); - expect(skeleton).toBeTruthy(); - - const activeClass = document.querySelector('.ant-skeleton-active'); - expect(activeClass).toBeNull(); -}); diff --git a/frontend/src/components/Feedback/Skeleton.tsx b/frontend/src/components/Feedback/Skeleton.tsx deleted file mode 100644 index 75211235e..000000000 --- a/frontend/src/components/Feedback/Skeleton.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { Skeleton as SkeletonD } from 'antd'; -import React from 'react'; - -type Props = { - title?: Record; - paragraph?: Record; - active?: boolean; -}; - -const Skeleton: React.FC = ({ title, paragraph, active = false }) => ( -
- -
-); - -export default Skeleton; diff --git a/frontend/src/components/Feedback/Spin.tsx b/frontend/src/components/Feedback/Spin.tsx deleted file mode 100644 index d55c7bdf9..000000000 --- a/frontend/src/components/Feedback/Spin.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { Spin as SpinD } from 'antd'; -import React from 'react'; - -const Spin: React.FC = () => ; - -export default Spin; diff --git a/frontend/src/components/Messaging/MessageCard.test.tsx b/frontend/src/components/Messaging/MessageCard.test.tsx new file mode 100644 index 000000000..309786ff6 --- /dev/null +++ b/frontend/src/components/Messaging/MessageCard.test.tsx @@ -0,0 +1,13 @@ +import { render } from '@testing-library/react'; +import React from 'react'; +import MessageCard from './MessageCard'; + +it.only('renders message component with default markdown when file is wrong.', () => { + const { getByText } = render( + + ); + + // Check component renders + const text = getByText('Content is empty.'); + expect(text).toBeTruthy(); +}); diff --git a/frontend/src/components/Messaging/MessageCard.tsx b/frontend/src/components/Messaging/MessageCard.tsx new file mode 100644 index 000000000..352dfe0b7 --- /dev/null +++ b/frontend/src/components/Messaging/MessageCard.tsx @@ -0,0 +1,22 @@ +import React, { useEffect } from 'react'; +import ReactMarkdown from 'react-markdown'; +import { MarkdownMessage } from './types'; + +const MessageCard: React.FC = ({ fileName }) => { + const [content, setContent] = React.useState('Content is empty.'); + + /* istanbul ignore next */ + useEffect(() => { + fetch(fileName) + .then((res) => res.text()) + .then((text) => setContent(text)) + .catch((error) => { + // eslint-disable-next-line + console.error(error); + }); + }, []); + + return {content}; +}; + +export default MessageCard; diff --git a/frontend/src/components/Messaging/RightDrawer.test.tsx b/frontend/src/components/Messaging/RightDrawer.test.tsx new file mode 100644 index 000000000..c063ba054 --- /dev/null +++ b/frontend/src/components/Messaging/RightDrawer.test.tsx @@ -0,0 +1,11 @@ +import { render } from '@testing-library/react'; +import React from 'react'; +import RightDrawer from './RightDrawer'; + +it('renders right drawer component.', () => { + const { getByText } = render( {}} />); + + // Check component renders + const text = getByText('Notifications'); + expect(text).toBeTruthy(); +}); diff --git a/frontend/src/components/Messaging/RightDrawer.tsx b/frontend/src/components/Messaging/RightDrawer.tsx new file mode 100644 index 000000000..c0f65d5ed --- /dev/null +++ b/frontend/src/components/Messaging/RightDrawer.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { Drawer, Space, Button, Collapse, Card } from 'antd'; +import { rightDrawerChanges, rightDrawerMessages } from './messageDisplayData'; +import MessageCard from './MessageCard'; +import { MarkdownMessage } from './types'; + +export type Props = { + visible: boolean; + onClose: () => void; +}; + +const RightDrawer: React.FC = ({ visible, onClose }) => { + return ( + + + + } + > + + + {rightDrawerMessages.map((message: MarkdownMessage) => { + return ( + + + + ); + })} + + + + + {rightDrawerChanges.map((change: MarkdownMessage) => { + return ( + + + + ); + })} + + + + ); +}; + +export default RightDrawer; diff --git a/frontend/src/components/Messaging/StartPopup.test.tsx b/frontend/src/components/Messaging/StartPopup.test.tsx new file mode 100644 index 000000000..0d83176e4 --- /dev/null +++ b/frontend/src/components/Messaging/StartPopup.test.tsx @@ -0,0 +1,150 @@ +import { render, fireEvent } from '@testing-library/react'; +import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import StartPopup from './StartPopup'; +import StartupMessages from './messageDisplayData'; +import { TourTitles } from '../../common/reactJoyrideSteps'; + +// const defaultMessageId = StartupMessages.defaultMessageId; +// const currentMessageId = StartupMessages.messageToShow; + +const { defaultMessageId, messageToShow } = StartupMessages; + +let mockNavigate: () => void; + +beforeEach(() => { + mockNavigate = jest.fn(); + jest.mock( + 'react-router-dom', + () => + ({ + ...jest.requireActual('react-router-dom'), + useNavigate: () => ({ + push: mockNavigate, + }), + } as Record) + ); + window.localStorage.clear(); +}); + +afterEach(() => { + jest.clearAllMocks(); +}); + +describe('Start popup tests', () => { + it('renders start popup with welcome message if no local data exists.', () => { + const { getByTestId } = render( + + + + ); + + // Check welcome template rendered (default) + const welcomeHeader = getByTestId('welcomeTemplate'); + expect(welcomeHeader).toBeTruthy(); + }); + + it('renders start popup with welcome message and starts search tour.', () => { + const { getByTestId, getByText } = render( + + + + ); + + // Check welcome template rendered (default) + const welcomeHeader = getByTestId('welcomeTemplate'); + expect(welcomeHeader).toBeTruthy(); + + const searchTourBtn = getByText(TourTitles.Main); + expect(welcomeHeader).toBeTruthy(); + fireEvent.click(searchTourBtn); + }); + + it('renders start popup with welcome message and starts cart tour.', () => { + const { getByTestId, getByText } = render( + + + + ); + + // Check welcome template rendered (default) + const welcomeHeader = getByTestId('welcomeTemplate'); + expect(welcomeHeader).toBeTruthy(); + + const cartTourBtn = getByText(TourTitles.Cart); + expect(welcomeHeader).toBeTruthy(); + fireEvent.click(cartTourBtn); + }); + + it('renders start popup with welcome message and starts saved search tour.', () => { + const { getByTestId, getByText } = render( + + + + ); + + // Check welcome template rendered (default) + const welcomeHeader = getByTestId('welcomeTemplate'); + expect(welcomeHeader).toBeTruthy(); + + const searchesTourBtn = getByText(TourTitles.Searches); + expect(welcomeHeader).toBeTruthy(); + fireEvent.click(searchesTourBtn); + }); + + it('renders start popup with welcome message and starts node page tour.', () => { + const { getByTestId, getByText } = render( + + + + ); + + // Check welcome template rendered (default) + const welcomeHeader = getByTestId('welcomeTemplate'); + expect(welcomeHeader).toBeTruthy(); + + const nodeTourBtn = getByText(TourTitles.Node); + expect(welcomeHeader).toBeTruthy(); + fireEvent.click(nodeTourBtn); + }); + + it('renders start popup with message data missing.', () => { + StartupMessages.defaultMessageId = 'test'; + const { getByText } = render( + + + + ); + StartupMessages.defaultMessageId = defaultMessageId; + + // Check welcome template rendered (default) + const missing = getByText('Message Data Missing'); + expect(missing).toBeTruthy(); + }); + + it('renders start popup with wrong version specified', () => { + window.localStorage.setItem('lastMessageSeen', 'test'); + const { getByTestId } = render( + + + + ); + + // Check changelog template rendered + const changelog = getByTestId('changelogTemplate'); + expect(changelog).toBeTruthy(); + }); + + it('start popup doesnt render when correct version is specified', () => { + window.localStorage.setItem('lastMessageSeen', messageToShow); + const { queryByText } = render( + + + + ); + + // Check that popup doesn't render + const github = queryByText('GitHub Issues'); + expect(github).toBeFalsy(); + }); +}); diff --git a/frontend/src/components/Messaging/StartPopup.tsx b/frontend/src/components/Messaging/StartPopup.tsx new file mode 100644 index 000000000..48278038e --- /dev/null +++ b/frontend/src/components/Messaging/StartPopup.tsx @@ -0,0 +1,129 @@ +import { GithubOutlined } from '@ant-design/icons'; +import React, { CSSProperties, useEffect } from 'react'; +import Modal from '../Feedback/Modal'; +import messageDisplayData from './messageDisplayData'; +import WelcomeTemplate from './Templates/Welcome'; +import ChangeLogTemplate from './Templates/ChangeLog'; +import { MessageActions, MessageData, MessageTemplates } from './types'; +import { + RawTourState, + ReactJoyrideContext, +} from '../../contexts/ReactJoyrideContext'; +import { welcomeTour } from '../../common/reactJoyrideSteps'; + +const getMessageSeen = (): string | null => { + return localStorage.getItem('lastMessageSeen'); +}; + +const setMessageSeen = (): void => { + localStorage.setItem('lastMessageSeen', messageDisplayData.messageToShow); +}; + +const getMessageData = (msgId: string): MessageData | undefined => { + const messages: MessageData[] = messageDisplayData.messageData; + const msgData = messages.find((msg) => { + return msg.messageId === msgId; + }); + return msgData; +}; + +const getMessageTemplate = ( + msgData: MessageData | undefined, + msgActions: MessageActions +): JSX.Element => { + if (!msgData) { + return

Message Data Missing

; + } + const { template, data: props } = msgData; + switch (template) { + case MessageTemplates.ChangeLog: + return ( + + ); + default: + return ( + + ); + } +}; + +const StartPopup: React.FC = () => { + const startData = messageDisplayData; + // Startup visibility + const [visible, setVisible] = React.useState(false); + const [title, setTitle] = React.useState(<>); + const [style, setStyle] = React.useState(); + + // Tutorial state + const tourState: RawTourState = React.useContext(ReactJoyrideContext); + const { setTour, startTour } = tourState; + + const hideMessage = (): void => { + setVisible(false); + + // Show welcome tour if welcome message is shown + const startupMessageSeen = getMessageSeen(); + if (!startupMessageSeen) { + setTour(welcomeTour); + startTour(); + } + setMessageSeen(); + }; + + const showMessage = (msgId: string): void => { + /* istanbul ignore next */ + const actions: MessageActions = { + close: hideMessage, + viewChanges: (): void => showMessage(startData.messageToShow), + }; + const messageData = getMessageData(msgId); + const titleComponent = getMessageTemplate(messageData, actions); + setStyle(messageData?.style); + setTitle(titleComponent); + setVisible(true); + }; + + useEffect(() => { + const startupMessageSeen = getMessageSeen(); + if (!startupMessageSeen) { + showMessage(startData.defaultMessageId); + } else if (startupMessageSeen !== startData.messageToShow) { + showMessage(startData.messageToShow); + } + }, []); + + return ( +
+ +

+ Questions, suggestions, or problems? Please visit our GitHub page to + open an issue. +

+ +
+
+ ); +}; + +export default StartPopup; diff --git a/frontend/src/components/Messaging/Templates/ChangeLog.tsx b/frontend/src/components/Messaging/Templates/ChangeLog.tsx new file mode 100644 index 000000000..4254ec9fc --- /dev/null +++ b/frontend/src/components/Messaging/Templates/ChangeLog.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { TemplateProps, ChangeLogData } from '../types'; +import MessageCard from '../MessageCard'; + +const ChangeLogTemplate: React.FC = ({ templateData }) => { + const props: ChangeLogData = templateData as ChangeLogData; + return ( + <> +

+ What's New with Metagrid v{props.version} +

+

+ {props.changesFile && ( + + )} +

+ + ); +}; + +export default ChangeLogTemplate; diff --git a/frontend/src/components/Messaging/Templates/Welcome.tsx b/frontend/src/components/Messaging/Templates/Welcome.tsx new file mode 100644 index 000000000..1b3c09852 --- /dev/null +++ b/frontend/src/components/Messaging/Templates/Welcome.tsx @@ -0,0 +1,120 @@ +import { Button, Card, Col, Row } from 'antd'; +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import { JoyrideTour } from '../../../common/JoyrideTour'; +import { + createMainPageTour, + createCartItemsTour, + createSearchCardTour, + createNodeStatusTour, + TourTitles, +} from '../../../common/reactJoyrideSteps'; +import { + RawTourState, + ReactJoyrideContext, +} from '../../../contexts/ReactJoyrideContext'; +import { MessageActions, TemplateProps, WelcomeData } from '../types'; + +const WelcomeTemplate: React.FC = ({ + templateData, + templateActions, +}) => { + const data: WelcomeData = templateData as WelcomeData; + const actions: MessageActions = templateActions; + + // Tutorial state + const tourState: RawTourState = React.useContext(ReactJoyrideContext); + const { setTour, startTour, setCurrentAppPage } = tourState; + const navigator = useNavigate(); + + const startSpecificTour = (tour: JoyrideTour): void => { + setTour(tour); + actions.close(); + startTour(); + }; + + const startMainPageTour = (): void => { + navigator('/search'); + startSpecificTour(createMainPageTour()); + }; + + const startCartPageTour = (): void => { + navigator('/cart/items'); + startSpecificTour(createCartItemsTour(setCurrentAppPage)); + }; + + const startSearchCardTour = (): void => { + navigator('/cart/searches'); + startSpecificTour(createSearchCardTour(setCurrentAppPage)); + }; + + const startNodeStatusTour = (): void => { + navigator('/nodes'); + startSpecificTour(createNodeStatusTour()); + }; + + return ( + <> +

Welcome!

+

{data.welcomeMessage}

+ + + + + + + + + + + + + + + + +
+

+ To view the latest changes to Metagrid click button below: +
+ +

+

Documentation

+

+ To view the latest documentation and FAQ, please visit this page: +
+ + https://esgf.github.io/esgf-user-support/metagrid.html + +

+ + ); +}; + +export default WelcomeTemplate; diff --git a/frontend/src/components/Messaging/messageDisplayData.ts b/frontend/src/components/Messaging/messageDisplayData.ts new file mode 100644 index 000000000..b7ee1f2e9 --- /dev/null +++ b/frontend/src/components/Messaging/messageDisplayData.ts @@ -0,0 +1,44 @@ +import { StartPopupData, MessageTemplates, MarkdownMessage } from './types'; + +export const rightDrawerMessages: MarkdownMessage[] = [ + { title: 'Messages', fileName: 'messages/metagrid_messages.md' }, +]; + +export const rightDrawerChanges: MarkdownMessage[] = [ + { title: 'V1.0.8', fileName: 'changelog/v1.0.8-beta.md' }, + { title: 'V1.0.7', fileName: 'changelog/v1.0.7-beta.md' }, +]; + +const startupMessages: StartPopupData = { + messageToShow: 'v1.0.8-beta', + defaultMessageId: 'welcome', + messageData: [ + { + messageId: 'v1.0.8-beta', + template: MessageTemplates.ChangeLog, + style: { minWidth: '700px' }, + data: { + changesFile: 'changelog/v1.0.8-beta.md', + version: '1.0.8 Beta', + }, + }, + { + messageId: 'v1.0.7-beta', + template: MessageTemplates.ChangeLog, + data: { + changesFile: 'changelog/v1.0.7-beta.md', + version: '1.0.7 Beta', + }, + }, + { + messageId: 'welcome', + template: MessageTemplates.Welcome, + data: { + welcomeMessage: + "If you wish to become familiar with Metagrid's search and download features, we recommend checking out the interface tours below:", + }, + }, + ], +}; + +export default startupMessages; diff --git a/frontend/src/components/Messaging/types.ts b/frontend/src/components/Messaging/types.ts new file mode 100644 index 000000000..24dff0557 --- /dev/null +++ b/frontend/src/components/Messaging/types.ts @@ -0,0 +1,46 @@ +import { CSSProperties } from 'react'; + +export enum MessageTemplates { + Welcome, + ChangeLog, + Notice, +} + +export type ChangeLogData = { + version: string; + changesFile: string; +}; + +export type WelcomeData = { + welcomeMessage: string; +}; + +export type MessageActions = { + close: () => void; + viewChanges: () => void; +}; + +export type ValidTemplateData = ChangeLogData | WelcomeData; + +export type TemplateProps = { + templateData: ValidTemplateData; + templateActions: MessageActions; +}; + +export type MessageData = { + messageId: string; + template: MessageTemplates; + data: ValidTemplateData; + style?: CSSProperties; +}; + +export type StartPopupData = { + messageToShow: string; + defaultMessageId: string; + messageData: MessageData[]; +}; + +export type MarkdownMessage = { + fileName: string; + title: string; +}; diff --git a/frontend/src/components/NavBar/LeftMenu.test.tsx b/frontend/src/components/NavBar/LeftMenu.test.tsx index 28905ecae..576fcd5c7 100644 --- a/frontend/src/components/NavBar/LeftMenu.test.tsx +++ b/frontend/src/components/NavBar/LeftMenu.test.tsx @@ -1,5 +1,5 @@ -import { fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; +import { fireEvent, render, waitFor } from '@testing-library/react'; import { BrowserRouter as Router } from 'react-router-dom'; import { projectsFixture } from '../../api/mock/fixtures'; import LeftMenu, { Props } from './LeftMenu'; diff --git a/frontend/src/components/NavBar/LeftMenu.tsx b/frontend/src/components/NavBar/LeftMenu.tsx index 400cd68bc..e7dbc4db2 100644 --- a/frontend/src/components/NavBar/LeftMenu.tsx +++ b/frontend/src/components/NavBar/LeftMenu.tsx @@ -1,11 +1,10 @@ import { SearchOutlined } from '@ant-design/icons'; -import { Form, Input, Select, Spin } from 'antd'; +import { Alert, Form, Input, Select, Spin } from 'antd'; import React from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { ResponseError } from '../../api'; import { navBarTargets } from '../../common/reactJoyrideSteps'; import { RawProject, RawProjects } from '../Facets/types'; -import Alert from '../Feedback/Alert'; import Button from '../General/Button'; const styles = { @@ -68,7 +67,7 @@ const LeftMenu: React.FC = ({ return (
= ({ = ({ placeholder="Search for a keyword" /> - + + {!authenticated ? ( +
); }; diff --git a/frontend/src/components/NavBar/index.test.tsx b/frontend/src/components/NavBar/index.test.tsx index f0e1736ae..32880bafc 100644 --- a/frontend/src/components/NavBar/index.test.tsx +++ b/frontend/src/components/NavBar/index.test.tsx @@ -1,5 +1,5 @@ -import { fireEvent, waitFor } from '@testing-library/react'; import React from 'react'; +import { fireEvent, waitFor } from '@testing-library/react'; import { rest, server } from '../../api/mock/setup-env'; import apiRoutes from '../../api/routes'; import { customRender } from '../../test/custom-render'; diff --git a/frontend/src/components/NavBar/index.tsx b/frontend/src/components/NavBar/index.tsx index f635f3d5a..d0ae68d7d 100644 --- a/frontend/src/components/NavBar/index.tsx +++ b/frontend/src/components/NavBar/index.tsx @@ -38,7 +38,6 @@ const NavBar: React.FC = ({ />
-
= ({ onTextSearch={onTextSearch} >
-
+
= ({ supportModalVisible={supportModalVisible} >
- - = ({ nodeStatus }) => { return (
Node diff --git a/frontend/src/components/NodeStatus/index.test.tsx b/frontend/src/components/NodeStatus/index.test.tsx index 83b1ac70f..9a0077b56 100644 --- a/frontend/src/components/NodeStatus/index.test.tsx +++ b/frontend/src/components/NodeStatus/index.test.tsx @@ -43,9 +43,7 @@ it('renders the node status and columns sort', () => { it('renders an error message when no node status information is available', async () => { const { getByRole } = render(); - const alertMsg = await waitFor(() => - getByRole('img', { name: 'close-circle', hidden: true }) - ); + const alertMsg = await waitFor(() => getByRole('alert')); expect(alertMsg).toBeTruthy(); }); @@ -59,9 +57,7 @@ it('renders an error message when there is an api error', async () => { > ); - const alertMsg = await waitFor(() => - getByRole('img', { name: 'close-circle', hidden: true }) - ); + const alertMsg = await waitFor(() => getByRole('alert')); expect(alertMsg).toBeTruthy(); const errorMsgDiv = getByText(errorMsg); @@ -76,9 +72,7 @@ it('renders error message that feature is disabled', async () => { ); - const alertMsg = await waitFor(() => - getByRole('img', { name: 'close-circle', hidden: true }) - ); + const alertMsg = await waitFor(() => getByRole('alert')); expect(alertMsg).toBeTruthy(); const errorMsgDiv = getByText(errorMsg); @@ -92,9 +86,7 @@ it('renders fallback network error msg', async () => { ); - const alertMsg = await waitFor(() => - getByRole('img', { name: 'close-circle', hidden: true }) - ); + const alertMsg = await waitFor(() => getByRole('alert')); expect(alertMsg).toBeTruthy(); const errorMsgDiv = getByText(errorMsg); diff --git a/frontend/src/components/NodeStatus/index.tsx b/frontend/src/components/NodeStatus/index.tsx index 93fb7d595..e425490ea 100644 --- a/frontend/src/components/NodeStatus/index.tsx +++ b/frontend/src/components/NodeStatus/index.tsx @@ -1,12 +1,11 @@ import { CheckCircleTwoTone, CloseCircleTwoTone } from '@ant-design/icons'; -import { Table as TableD } from 'antd'; +import { Alert, Table as TableD } from 'antd'; import { SortOrder } from 'antd/lib/table/interface'; import React from 'react'; import { ResponseError } from '../../api'; import apiRoutes from '../../api/routes'; import { nodeTourTargets } from '../../common/reactJoyrideSteps'; import { CSSinJS } from '../../common/types'; -import Alert from '../Feedback/Alert'; import { NodeStatusArray, NodeStatusElement } from './types'; const styles = { headerContainer: { margin: '12px' } } as CSSinJS; @@ -47,7 +46,7 @@ const NodeStatus: React.FC = ({ nodeStatus, apiError, isLoading }) => { const columns = [ { title: ( -
Node
+
Node
), dataIndex: 'name', align: 'center' as const, @@ -57,9 +56,7 @@ const NodeStatus: React.FC = ({ nodeStatus, apiError, isLoading }) => { }, { title: ( -
- Online -
+
Online
), dataIndex: 'isOnline', align: 'center' as const, @@ -83,7 +80,7 @@ const NodeStatus: React.FC = ({ nodeStatus, apiError, isLoading }) => { dataIndex: 'source', render: (source: string) => ( = ({ nodeStatus, apiError, isLoading }) => { (
-

+

Status as of {timestamp}

diff --git a/frontend/src/components/Search/Citation.tsx b/frontend/src/components/Search/Citation.tsx index a7e4f2ea9..b59385260 100644 --- a/frontend/src/components/Search/Citation.tsx +++ b/frontend/src/components/Search/Citation.tsx @@ -1,9 +1,8 @@ import React from 'react'; +import { Alert, Skeleton } from 'antd'; import { PromiseFn, useAsync } from 'react-async'; import { fetchDatasetCitation } from '../../api'; import { splitStringByChar } from '../../common/utils'; -import Alert from '../Feedback/Alert'; -import Skeleton from '../Feedback/Skeleton'; import { RawCitation } from './types'; type CitationInfoProps = { diff --git a/frontend/src/components/Search/FilesTable.tsx b/frontend/src/components/Search/FilesTable.tsx index 863f3ce29..948db2240 100644 --- a/frontend/src/components/Search/FilesTable.tsx +++ b/frontend/src/components/Search/FilesTable.tsx @@ -6,7 +6,7 @@ import { RightCircleOutlined, ShareAltOutlined, } from '@ant-design/icons'; -import { Form, message, Table as TableD } from 'antd'; +import { Alert, Form, message, Table as TableD } from 'antd'; import { SizeType } from 'antd/lib/config-provider/SizeContext'; import { TablePaginationConfig } from 'antd/lib/table'; import React from 'react'; @@ -16,7 +16,6 @@ import { innerDataRowTargets } from '../../common/reactJoyrideSteps'; import { CSSinJS } from '../../common/types'; import { formatBytes, splitStringByChar } from '../../common/utils'; import ToolTip from '../DataDisplay/ToolTip'; -import Alert from '../Feedback/Alert'; import Button from '../General/Button'; import { Pagination, @@ -197,9 +196,7 @@ const FilesTable: React.FC = ({ id, numResults = 0, filenameVars }) => { key: 'title', render: (title: string) => { return ( -

- {title} -
+
{title}
); }, }, @@ -210,7 +207,7 @@ const FilesTable: React.FC = ({ id, numResults = 0, filenameVars }) => { key: 'size', render: (size: number) => { return ( -
+
{formatBytes(size)}
); @@ -231,7 +228,7 @@ const FilesTable: React.FC = ({ id, numResults = 0, filenameVars }) => { > - - +
)} diff --git a/frontend/src/components/Cart/recoil/atoms.ts b/frontend/src/components/Cart/recoil/atoms.ts new file mode 100644 index 000000000..9d1931759 --- /dev/null +++ b/frontend/src/components/Cart/recoil/atoms.ts @@ -0,0 +1,19 @@ +import { atom } from 'recoil'; +import { RawSearchResults } from '../../Search/types'; + +enum CartStateKeys { + cartItemSelections = 'cartItemSelections', + cartDownloadIsLoading = 'downloadIsLoading', +} + +export const cartDownloadIsLoading = atom({ + key: CartStateKeys.cartDownloadIsLoading, + default: false, +}); + +export const cartItemSelections = atom({ + key: CartStateKeys.cartItemSelections, + default: [], +}); + +export default CartStateKeys; diff --git a/frontend/src/components/Globus/DatasetDownload.tsx b/frontend/src/components/Globus/DatasetDownload.tsx new file mode 100644 index 000000000..cdc8f0118 --- /dev/null +++ b/frontend/src/components/Globus/DatasetDownload.tsx @@ -0,0 +1,637 @@ +import { CheckCircleFilled, DownloadOutlined } from '@ant-design/icons'; +import { Button, Form, message, Modal, Radio, Select, Space } from 'antd'; +import PKCE from 'js-pkce'; +import React, { useEffect } from 'react'; +import { useRecoilState } from 'recoil'; +import { + saveSessionValue, + loadSessionValue, + fetchWgetScript, + openDownloadURL, + ResponseError, + startGlobusTransfer, +} from '../../api'; +import { + GlobusStateValue, + GlobusEndpointData, + GlobusTokenResponse, +} from '../../common/types'; +import { cartTourTargets } from '../../common/reactJoyrideSteps'; +import { globusClientID, globusRedirectUrl } from '../../env'; +import { RawSearchResults } from '../Search/types'; +import CartStateKeys, { + cartItemSelections, + cartDownloadIsLoading, +} from '../Cart/recoil/atoms'; +import GlobusStateKeys, { + globusUseDefaultEndpoint, + globusDefaultEndpoint, +} from './recoil/atom'; + +// Reference: https://github.com/bpedroza/js-pkce +const GlobusAuth = new PKCE({ + client_id: globusClientID, // Update this using your native client ID + redirect_uri: globusRedirectUrl, // Update this if you are deploying this anywhere else (Globus Auth will redirect back here once you have logged in) + authorization_endpoint: 'https://auth.globus.org/v2/oauth2/authorize', // No changes needed + token_endpoint: 'https://auth.globus.org/v2/oauth2/token', // No changes needed + requested_scopes: + 'openid profile email offline_access urn:globus:auth:scope:transfer.api.globus.org:all', // Update with any scopes you would need, e.g. transfer +}); + +type ModalFormState = 'signin' | 'endpoint' | 'both' | 'none'; + +type ModalState = { + onCancelAction: () => void; + onOkAction: () => void; + show: boolean; + state: ModalFormState; +}; + +// const globusStatusKey = 'globusDownloadStatus'; + +// Statically defined list of dataset download options +const downloadOptions = ['Globus', 'wget']; + +const DatasetDownloadForm: React.FC = () => { + const [downloadForm] = Form.useForm(); + + // User wants to use default endpoint + const [ + useGlobusDefaultEndpoint, + setUseGlobusDefaultEndpoint, + ] = useRecoilState(globusUseDefaultEndpoint); + + const [ + defaultGlobusEndpoint, + setDefaultGlobusEndpoint, + ] = useRecoilState(globusDefaultEndpoint); + + const [itemSelections, setItemSelections] = useRecoilState( + cartItemSelections + ); + + const [downloadIsLoading, setDownloadIsLoading] = useRecoilState( + cartDownloadIsLoading + ); + + // Component internal state + const [globusStepsModal, setGlobusStepsModal] = React.useState({ + show: false, + state: 'both', + onOkAction: () => { + setGlobusStepsModal({ ...globusStepsModal, show: false }); + }, + onCancelAction: async () => { + setGlobusStepsModal({ ...globusStepsModal, show: false }); + await endDownloadSteps(); + }, + }); + const [ + useDefaultConfirmModal, + setUseDefaultConfirmModal, + ] = React.useState({ + show: false, + state: 'none', + onOkAction: () => { + setUseDefaultConfirmModal({ ...useDefaultConfirmModal, show: false }); + }, + onCancelAction: () => { + setUseDefaultConfirmModal({ ...useDefaultConfirmModal, show: false }); + }, + }); + + function redirectToNewURL(newUrl: string): void { + setTimeout(() => { + window.location.replace(newUrl); + }, 200); + } + + function redirectToRootUrl(): void { + // Redirect back to the root URL (simple but brittle way to clear the query params) + const splitUrl = window.location.href.split('?'); + if (splitUrl.length > 1) { + const params = new URLSearchParams(window.location.search); + if (endpointUrlReady(params) || tokenUrlReady(params)) { + const newUrl = splitUrl[0]; + redirectToNewURL(newUrl); + } + } + } + + async function getGlobusTransferToken(): Promise { + const token = await loadSessionValue( + GlobusStateKeys.transferToken + ); + if (token && token.expires_in && token.created_on) { + const createTime = token.created_on; + const lifeTime = token.expires_in; + const expires = createTime + lifeTime; + const curTime = Math.floor(Date.now() / 1000); + + if (curTime <= expires) { + return token; + } + return null; + } + return null; + } + + async function getGlobusTokens(): Promise< + [GlobusTokenResponse | null, string | null] + > { + const refreshToken = await loadSessionValue( + GlobusStateKeys.refreshToken + ); + const transferToken = await getGlobusTransferToken(); + return [transferToken, refreshToken]; + } + + async function getEndpointData(): Promise< + [boolean | null, GlobusEndpointData | null, GlobusEndpointData | null] + > { + const useDefault = await loadSessionValue( + GlobusStateKeys.useDefaultEndpoint + ); + const defaultEndpoint = await loadSessionValue( + GlobusStateKeys.defaultEndpoint + ); + const selectedEndpoint = await loadSessionValue( + GlobusStateKeys.userSelectedEndpoint + ); + + return [useDefault, defaultEndpoint, selectedEndpoint]; + } + + const handleWgetDownload = (): void => { + if (itemSelections !== null) { + const ids = itemSelections.map((item) => item.id); + // eslint-disable-next-line no-void + void message.success( + 'The wget script is generating, please wait momentarily.', + 10 + ); + setDownloadIsLoading(true); + fetchWgetScript(ids) + .then((url) => { + openDownloadURL(url); + setDownloadIsLoading(false); + }) + .catch((error: ResponseError) => { + // eslint-disable-next-line no-void + void message.error(error.message); + setDownloadIsLoading(false); + }); + } + }; + + const handleGlobusDownload = async ( + globusTransferToken: GlobusTokenResponse | null, + refreshToken: string | null, + endpoint: GlobusEndpointData | null + ): Promise => { + if (!endpoint) { + message.warning(`Globus endpoint was undefined.`); + return; + } + + setDownloadIsLoading(true); + const loadedSelections = await loadSessionValue( + CartStateKeys.cartItemSelections + ); + if (loadedSelections && loadedSelections.length > 0) { + setItemSelections(loadedSelections); + const ids = loadedSelections.map((item) => item.id); + + if (globusTransferToken && refreshToken) { + startGlobusTransfer( + globusTransferToken.access_token, + refreshToken, + endpoint?.endpointId || '', + endpoint?.path || '', + ids + ) + .then((resp) => { + if (resp.status === 200) { + message.info(`Globus transfer task submitted successfully!`); + saveSessionValue(CartStateKeys.cartItemSelections, []); + } else { + message.warning( + `Globus transfer task struggled: ${resp.statusText}` + ); + } + }) + .catch((error: ResponseError) => { + message.error(`Globus transfer task failed: ${error.message}`); + }) + .finally(async () => { + await endDownloadSteps(); + }); + } + } else { + await endDownloadSteps(); + } + }; + + const handleDownloadForm = (downloadType: 'wget' | 'Globus'): void => { + /* istanbul ignore else */ + if (downloadType === 'wget') { + handleWgetDownload(); + } else if (downloadType === 'Globus') { + const prepareDownload = async (): Promise => { + await performGlobusDownloadStep(); + }; + prepareDownload(); + } + }; + + const showGlobusSigninPrompt = (formState: ModalFormState): void => { + setGlobusStepsModal({ + ...globusStepsModal, + onOkAction: async () => { + setGlobusStepsModal({ ...globusStepsModal, show: false }); + await loginWithGlobus(); + }, + show: true, + state: formState, + }); + }; + + const showGlobusEndpointPrompt = (): void => { + setGlobusStepsModal({ + ...globusStepsModal, + onOkAction: async () => { + setGlobusStepsModal({ ...globusStepsModal, show: false }); + await redirectToSelectGlobusEndpoint(); + }, + show: true, + state: 'endpoint', + }); + }; + + const showGlobusDownloadPrompt = ( + transferToken: GlobusTokenResponse | null, + refreshToken: string | null, + endpoint: GlobusEndpointData | null + ): void => { + setGlobusStepsModal({ + ...globusStepsModal, + onOkAction: () => { + setGlobusStepsModal({ ...globusStepsModal, show: false }); + handleGlobusDownload(transferToken, refreshToken, endpoint); + }, + show: true, + state: 'none', + }); + }; + + function tokensReady( + refreshToken: string | null, + globusTransferToken: GlobusTokenResponse | null + ): boolean { + if (refreshToken && globusTransferToken) { + return true; + } + return false; + } + + function endpointIsReady( + useDefault: boolean | null, + defaultEndpoint: GlobusEndpointData | null, + userEndpoint: GlobusEndpointData | null + ): boolean { + if (useDefault !== null) { + if ((useDefault && defaultEndpoint) || userEndpoint) { + return true; + } + } + // Check the UI state as backup if state wasn't saved + if ((useGlobusDefaultEndpoint && defaultEndpoint) || userEndpoint) { + return true; + } + + return false; + } + + function endpointUrlReady(params: URLSearchParams): boolean { + return params.has('endpoint'); + } + + function tokenUrlReady(params: URLSearchParams): boolean { + return params.has('code') && params.has('state'); + } + + async function getUrlTokens(): Promise { + const url = window.location.href; + try { + const tokenResponse = (await GlobusAuth.exchangeForAccessToken( + url + )) as GlobusTokenResponse; + + if (tokenResponse) { + if (tokenResponse.refresh_token) { + await saveSessionValue( + GlobusStateKeys.refreshToken, + tokenResponse.refresh_token + ); + } else { + await saveSessionValue(GlobusStateKeys.refreshToken, null); + } + + // Try to find and get the transfer token + if (tokenResponse.other_tokens) { + const otherTokens: GlobusTokenResponse[] = [ + ...(tokenResponse.other_tokens as GlobusTokenResponse[]), + ]; + otherTokens.forEach(async (tokenBlob) => { + if ( + tokenBlob.resource_server && + tokenBlob.resource_server === 'transfer.api.globus.org' + ) { + const newTransferToken = { ...tokenBlob }; + newTransferToken.created_on = Math.floor(Date.now() / 1000); + await saveSessionValue( + GlobusStateKeys.transferToken, + newTransferToken + ); + } + }); + } else { + await saveSessionValue(GlobusStateKeys.transferToken, null); + } + } + } catch (error: unknown) { + message.error('Error occured when obtaining transfer permissions.'); + } finally { + // This isn't strictly necessary but it ensures no code reuse. + sessionStorage.removeItem('pkce_code_verifier'); + sessionStorage.removeItem('pkce_state'); + } + } + + async function getUrlEndpoint( + params: URLSearchParams + ): Promise { + // The url has endpoint information, so process it + const endpoint = params.get('endpoint'); + const label = params.get('label'); + const path = params.get('path'); + const globfs = params.get('globfs'); + const endpointId = params.get('endpoint_id'); + + const endpointInfo: GlobusEndpointData = { + endpoint, + label, + path, + globfs, + endpointId, + }; + await saveSessionValue(GlobusStateKeys.userSelectedEndpoint, endpointInfo); + return endpointInfo; + } + + async function saveEndpointAsDefault( + userEndpoint: GlobusStateValue + ): Promise { + if (userEndpoint) { + setDefaultGlobusEndpoint(userEndpoint); + await saveSessionValue(GlobusStateKeys.defaultEndpoint, userEndpoint); + } + } + + async function redirectToSelectGlobusEndpoint(): Promise { + await saveSessionValue(GlobusStateKeys.continueGlobusPrepSteps, true); + const endpointSearchURL = `https://app.globus.org/file-manager?action=${globusRedirectUrl}&method=GET&cancelUrl=${globusRedirectUrl}`; + redirectToNewURL(endpointSearchURL); + } + + async function loginWithGlobus(): Promise { + await saveSessionValue(GlobusStateKeys.continueGlobusPrepSteps, true); + sessionStorage.removeItem('pkce_code_verifier'); + sessionStorage.removeItem('pkce_state'); + const authUrl: string = GlobusAuth.authorizeUrl(); + redirectToNewURL(authUrl); + } + + async function endDownloadSteps(): Promise { + setDownloadIsLoading(false); + await saveSessionValue(GlobusStateKeys.userSelectedEndpoint, null); + await saveSessionValue(GlobusStateKeys.continueGlobusPrepSteps, false); + redirectToRootUrl(); + } + + async function performGlobusDownloadStep(): Promise { + const [transferToken, refreshToken] = await getGlobusTokens(); + const [ + useDefaultEndpoint, + defaultEndpoint, + userSelectedEndpoint, + ] = await getEndpointData(); + const tReady = tokensReady(refreshToken, transferToken); + const eReady = endpointIsReady( + useDefaultEndpoint, + defaultEndpoint, + userSelectedEndpoint + ); + const urlParams = new URLSearchParams(window.location.search); + const tUrlReady = tokenUrlReady(urlParams); + const eUrlReady = endpointUrlReady(urlParams); + if (tReady && eReady) { + setDownloadIsLoading(true); + if (useDefaultEndpoint) { + handleGlobusDownload(transferToken, refreshToken, defaultEndpoint); + } else { + handleGlobusDownload(transferToken, refreshToken, userSelectedEndpoint); + } + } else if (tReady) { + if (endpointUrlReady(urlParams)) { + const userEndpoint = await getUrlEndpoint(urlParams); + setUseDefaultConfirmModal({ + ...useDefaultConfirmModal, + onOkAction: async () => { + await saveEndpointAsDefault(userEndpoint); + setUseDefaultConfirmModal({ + ...useDefaultConfirmModal, + show: false, + }); + showGlobusDownloadPrompt(transferToken, refreshToken, userEndpoint); + }, + onCancelAction: (): void => { + setUseDefaultConfirmModal({ + ...useDefaultConfirmModal, + show: false, + }); + showGlobusDownloadPrompt(transferToken, refreshToken, userEndpoint); + }, + show: true, + state: 'none', + }); + } else { + showGlobusEndpointPrompt(); + } + } else if (eReady) { + if (tokenUrlReady(urlParams)) { + await getUrlTokens(); + showGlobusDownloadPrompt( + transferToken, + refreshToken, + userSelectedEndpoint + ); + } else { + showGlobusSigninPrompt('signin'); + } + } else if (tUrlReady) { + await getUrlTokens(); + showGlobusEndpointPrompt(); + } else if (eUrlReady) { + const userEndpoint = await getUrlEndpoint(urlParams); + setUseDefaultConfirmModal({ + ...useDefaultConfirmModal, + onOkAction: async () => { + await saveEndpointAsDefault(userEndpoint); + setUseDefaultConfirmModal({ ...useDefaultConfirmModal, show: false }); + showGlobusSigninPrompt('signin'); + }, + onCancelAction: (): void => { + setUseDefaultConfirmModal({ ...useDefaultConfirmModal, show: false }); + showGlobusSigninPrompt('signin'); + }, + show: true, + state: 'both', + }); + } else { + showGlobusSigninPrompt('both'); + } + } + + useEffect(() => { + const initializePage = async (): Promise => { + const continueProcess = await loadSessionValue( + GlobusStateKeys.continueGlobusPrepSteps + ); + const itemCartSelections = await loadSessionValue( + CartStateKeys.cartItemSelections + ); + const defaultEndpoint = await loadSessionValue( + GlobusStateKeys.defaultEndpoint + ); + + if (itemCartSelections) { + setItemSelections(itemCartSelections); + } + if (defaultEndpoint) { + setDefaultGlobusEndpoint(defaultEndpoint); + } + if (continueProcess) { + await performGlobusDownloadStep(); + } + }; + initializePage(); + }, []); + + return ( + <> +
+ handleDownloadForm(downloadType as 'wget' | 'Globus') + } + initialValues={{ + downloadType: downloadOptions[0], + }} + > + + + + +
+ +
+
+ {defaultGlobusEndpoint && itemSelections.length !== 0 && ( + + { + setUseGlobusDefaultEndpoint(e.target.value as boolean); + saveSessionValue( + GlobusStateKeys.useDefaultEndpoint, + e.target.value as boolean + ); + }} + value={useGlobusDefaultEndpoint} + > + + + Default Endpoint + + Specify Endpoint + + + + )} +
+ +

Do you want to save this endpoint as default?

+
+ +

Steps for Globus transfer:

+
    +
  1. + {(globusStepsModal.state === 'both' || + globusStepsModal.state === 'signin') && + '-> '} + Redirect to obtain transfer permission from Globus. + {(globusStepsModal.state === 'none' || + globusStepsModal.state === 'endpoint') && } +
  2. +
  3. + {globusStepsModal.state === 'endpoint' && '-> '} + Redirect to select an endpoint in Globus. + {(globusStepsModal.state === 'none' || + globusStepsModal.state === 'signin') && } +
  4. + +
  5. + {globusStepsModal.state === 'none' && '-> '} Start Globus transfer. +
  6. +
+

Do you wish to proceed?

+
+ + ); +}; + +export default DatasetDownloadForm; diff --git a/frontend/src/components/Globus/recoil/atom.ts b/frontend/src/components/Globus/recoil/atom.ts new file mode 100644 index 000000000..f86f1b747 --- /dev/null +++ b/frontend/src/components/Globus/recoil/atom.ts @@ -0,0 +1,71 @@ +import { atom, DefaultValue } from 'recoil'; +import { loadSessionValue, saveSessionValue } from '../../../api'; +import { GlobusStateValue } from '../../../common/types'; + +// Folder structure based on: https://wes-rast.medium.com/recoil-project-structure-best-practices-79e74a475caa + +enum GlobusStateKeys { + accessToken = 'globusAccessToken', + continueGlobusPrepSteps = 'continueGlobusPreparationSteps', + useDefaultEndpoint = 'useDefaultEndpoint', + defaultEndpoint = 'defaultGlobusEndpoint', + userSelectedEndpoint = 'userSelectedEndpoint', + refreshToken = 'globusRefreshToken', + tokenResponse = 'tokenResponse', + transferToken = 'globusTransferToken', +} + +export const sessionStorageEffect = (key: string) => ({ + setSelf, + onSet, + trigger, +}: { + setSelf: (value: GlobusStateValue | DefaultValue) => void; + onSet: ( + func: ( + newValue: GlobusStateValue | DefaultValue, + oldValue: GlobusStateValue | DefaultValue, + isReset: boolean + ) => void + ) => void; + trigger: 'get' | 'set'; +}) => { + // If there's a persisted value - set it on load + const loadPersisted = async (): Promise => { + const savedValue = await loadSessionValue(key); + + if (savedValue != null) { + setSelf(savedValue); + } + }; + + // Asynchronously set the persisted data + if (trigger === 'get') { + loadPersisted(); + } + + // Subscribe to state changes and persist them to session storage + onSet( + ( + newValue: GlobusStateValue | DefaultValue, + oldValue: GlobusStateValue | DefaultValue, + isReset: boolean + ) => { + return isReset + ? saveSessionValue(key, DefaultValue) + : saveSessionValue(key, newValue); + } + ); +}; + +export const globusDefaultEndpoint = atom({ + key: GlobusStateKeys.defaultEndpoint, + default: null, +}); + +export const globusUseDefaultEndpoint = atom({ + key: GlobusStateKeys.useDefaultEndpoint, + default: false, +}); + +export default GlobusStateKeys; diff --git a/frontend/src/components/NavBar/RightMenu.tsx b/frontend/src/components/NavBar/RightMenu.tsx index cc6db24d2..66b3d5064 100644 --- a/frontend/src/components/NavBar/RightMenu.tsx +++ b/frontend/src/components/NavBar/RightMenu.tsx @@ -187,11 +187,35 @@ const RightMenu: React.FC = ({ )} - + {/* !isSignedIntoGlobus() ? ( + + + + ) : ( + + + + )*/} + - - ) : ( - - - - )*/}
+ + } + > + ( + + + + View Task In Globus + + } + /> + + + )} + /> + + + + )}
); }; diff --git a/frontend/src/components/General/Button.tsx b/frontend/src/components/General/Button.tsx index d10424d43..6a6a4422f 100644 --- a/frontend/src/components/General/Button.tsx +++ b/frontend/src/components/General/Button.tsx @@ -24,6 +24,7 @@ type Props = { loading?: boolean; shape?: 'circle' | 'round'; size?: 'large' | 'middle' | 'small'; + style?: React.CSSProperties | undefined; }; const Button: React.FC = ({ @@ -40,6 +41,7 @@ const Button: React.FC = ({ children, shape, size, + style, }) => ( = ({ loading={loading} shape={shape} size={size} + style={style} > {children} diff --git a/frontend/src/components/Globus/DatasetDownload.tsx b/frontend/src/components/Globus/DatasetDownload.tsx index 84038a562..c26935a6e 100644 --- a/frontend/src/components/Globus/DatasetDownload.tsx +++ b/frontend/src/components/Globus/DatasetDownload.tsx @@ -11,11 +11,6 @@ import { ResponseError, startGlobusTransfer, } from '../../api'; -import { - GlobusStateValue, - GlobusEndpointData, - GlobusTokenResponse, -} from '../../common/types'; import { cartTourTargets } from '../../common/reactJoyrideSteps'; import { globusClientID, @@ -30,7 +25,15 @@ import CartStateKeys, { import GlobusStateKeys, { globusUseDefaultEndpoint, globusDefaultEndpoint, + globusTaskItems, } from './recoil/atom'; +import { + GlobusStateValue, + GlobusTokenResponse, + GlobusEndpointData, + GlobusTaskItem, + MAX_TASK_LIST_LENGTH, +} from './types'; // Reference: https://github.com/bpedroza/js-pkce const GlobusAuth = new PKCE({ @@ -76,6 +79,10 @@ const DatasetDownloadForm: React.FC = () => { setDefaultGlobusEndpoint, ] = useRecoilState(globusDefaultEndpoint); + const [taskItems, setTaskItems] = useRecoilState( + globusTaskItems + ); + const [itemSelections, setItemSelections] = useRecoilState( cartItemSelections ); @@ -124,6 +131,16 @@ const DatasetDownloadForm: React.FC = () => { } ); + function addNewTask(newTask: GlobusTaskItem): void { + const newItemsList = [...taskItems]; + if (taskItems.length >= MAX_TASK_LIST_LENGTH) { + newItemsList.pop(); + } + newItemsList.unshift(newTask); + setTaskItems(newItemsList); + saveSessionValue(GlobusStateKeys.globusTaskItems, newItemsList); + } + function redirectToNewURL(newUrl: string): void { setTimeout(() => { window.location.replace(newUrl); @@ -208,17 +225,6 @@ const DatasetDownloadForm: React.FC = () => { } }; - const createTaskOverviewURL = ( - transRespData: Record - ): string => { - if (transRespData && transRespData.taskid) { - const taskId = transRespData.taskid as string; - - return `https://app.globus.org/activity/${taskId}/overview`; - } - return ''; - }; - const handleGlobusDownload = async ( globusTransferToken: GlobusTokenResponse | null, refreshToken: string | null, @@ -247,24 +253,37 @@ const DatasetDownloadForm: React.FC = () => { ) .then((resp) => { if (resp.status === 200) { - message.info(`Globus transfer task submitted successfully!`); - setItemSelections([]); saveSessionValue(CartStateKeys.cartItemSelections, []); - const taskStatusURL = createTaskOverviewURL( - resp.data as Record - ); - if (taskStatusURL !== '') { - message.info( -

- Click here to view Task Status:{' '} - - Task Status - -

, - 15 - ); + const transRespData = resp.data as Record; + if (transRespData && transRespData.taskid) { + const taskId = transRespData.taskid as string; + const taskItem: GlobusTaskItem = { + submitDate: new Date(Date.now()).toLocaleString(), + taskId, + taskStatusURL: `https://app.globus.org/activity/${taskId}/overview`, + }; + addNewTask(taskItem); + + if (taskItem.taskStatusURL !== '') { + message.info( +

+ Globus transfer task submitted successfully! +
+ + View Task Status + +

, + 5 + ); + } + } else { + message.info(`Globus transfer task submitted successfully!`); } } else { message.warning( @@ -634,6 +653,12 @@ const DatasetDownloadForm: React.FC = () => { const defaultEndpoint = await loadSessionValue( GlobusStateKeys.defaultEndpoint ); + const useDefaultEndpoint = await loadSessionValue( + GlobusStateKeys.useDefaultEndpoint + ); + const savedTaskItems = await loadSessionValue( + GlobusStateKeys.globusTaskItems + ); if (itemCartSelections) { setItemSelections(itemCartSelections); @@ -641,6 +666,12 @@ const DatasetDownloadForm: React.FC = () => { if (defaultEndpoint) { setDefaultGlobusEndpoint(defaultEndpoint); } + if (useDefaultEndpoint) { + setUseGlobusDefaultEndpoint(useDefaultEndpoint); + } + if (savedTaskItems) { + setTaskItems(savedTaskItems); + } if (continueProcess) { await performGlobusDownloadStep(); } diff --git a/frontend/src/components/Globus/recoil/atom.ts b/frontend/src/components/Globus/recoil/atom.ts index f86f1b747..bf1b78d61 100644 --- a/frontend/src/components/Globus/recoil/atom.ts +++ b/frontend/src/components/Globus/recoil/atom.ts @@ -1,6 +1,6 @@ import { atom, DefaultValue } from 'recoil'; import { loadSessionValue, saveSessionValue } from '../../../api'; -import { GlobusStateValue } from '../../../common/types'; +import { GlobusStateValue, GlobusTaskItem } from '../types'; // Folder structure based on: https://wes-rast.medium.com/recoil-project-structure-best-practices-79e74a475caa @@ -13,6 +13,7 @@ enum GlobusStateKeys { refreshToken = 'globusRefreshToken', tokenResponse = 'tokenResponse', transferToken = 'globusTransferToken', + globusTaskItems = 'globusTaskItems', } export const sessionStorageEffect = (key: string) => ({ @@ -68,4 +69,9 @@ export const globusUseDefaultEndpoint = atom({ default: false, }); +export const globusTaskItems = atom({ + key: GlobusStateKeys.globusTaskItems, + default: [], +}); + export default GlobusStateKeys; diff --git a/frontend/src/components/Globus/types.ts b/frontend/src/components/Globus/types.ts new file mode 100644 index 000000000..35c35faee --- /dev/null +++ b/frontend/src/components/Globus/types.ts @@ -0,0 +1,34 @@ +import ITokenResponse from 'js-pkce/dist/ITokenResponse'; + +export const MAX_TASK_LIST_LENGTH = 10; + +export interface GlobusTokenResponse extends ITokenResponse { + id_token: string; + resource_server: string; + other_tokens: unknown; + created_on: number; + expires_in: number; + error?: unknown; +} + +export type GlobusEndpointData = { + endpoint: string | null; + label: string | null; + path: string | null; + globfs: string | null; + endpointId?: string | null; +}; + +export type GlobusStateValue = + | null + | boolean + | string + | GlobusEndpointData + | GlobusTokenResponse + | Record; + +export type GlobusTaskItem = { + taskId: string; + submitDate: string; + taskStatusURL: string; +}; diff --git a/frontend/src/components/Search/Table.tsx b/frontend/src/components/Search/Table.tsx index 28632c7cd..6cf766328 100644 --- a/frontend/src/components/Search/Table.tsx +++ b/frontend/src/components/Search/Table.tsx @@ -234,7 +234,7 @@ const Table: React.FC = ({ { title: 'Download Options', key: 'download', - width: 150, + width: 200, render: (record: RawSearchResult) => { const supportedDownloadTypes = record.access; const formKey = `download-${record.id}`; @@ -312,7 +312,7 @@ const Table: React.FC = ({ columns.push({ title: 'Globus Ready', dataIndex: 'data_node', - width: 50, + width: 65, render: (data_node: string) => (
= ({ dataSource={results} rowKey="id" size="small" - scroll={{ y: 'calc(100vh)' }} + scroll={{ x: '100%', y: 'calc(70vh)' }} /> ); }; From e0617f20d1e85247b18574deee5f1b47955b5c5c Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:39:08 -0700 Subject: [PATCH 43/65] Found and fixed the issue with message update closing too quickly. Fixed it by making sure the message displays first using the async await call. --- .../src/components/Globus/DatasetDownload.tsx | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/Globus/DatasetDownload.tsx b/frontend/src/components/Globus/DatasetDownload.tsx index c26935a6e..621cee989 100644 --- a/frontend/src/components/Globus/DatasetDownload.tsx +++ b/frontend/src/components/Globus/DatasetDownload.tsx @@ -251,9 +251,10 @@ const DatasetDownloadForm: React.FC = () => { endpoint?.path || '', ids ) - .then((resp) => { + .then(async (resp) => { if (resp.status === 200) { setItemSelections([]); + setDownloadIsLoading(false); saveSessionValue(CartStateKeys.cartItemSelections, []); const transRespData = resp.data as Record; @@ -267,7 +268,7 @@ const DatasetDownloadForm: React.FC = () => { addNewTask(taskItem); if (taskItem.taskStatusURL !== '') { - message.info( + await message.info(

Globus transfer task submitted successfully!
@@ -283,16 +284,23 @@ const DatasetDownloadForm: React.FC = () => { ); } } else { - message.info(`Globus transfer task submitted successfully!`); + await message.info( + `Globus transfer task submitted successfully!`, + 5 + ); } } else { - message.warning( - `Globus transfer task struggled: ${resp.statusText}` + await message.warning( + `Globus transfer task struggled: ${resp.statusText}`, + 5 ); } }) - .catch((error: ResponseError) => { - message.error(`Globus transfer task failed: ${error.message}`); + .catch(async (error: ResponseError) => { + await message.error( + `Globus transfer task failed: ${error.message}`, + 5 + ); }) .finally(async () => { await endDownloadSteps(); From 56ac6c60633ebb36ef303596d8fe69d1a1f46368 Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Tue, 27 Jun 2023 19:52:44 -0700 Subject: [PATCH 44/65] Updated the joyride tutorial to include the globus ready icon and the globus ready filters. Didn't add tutorial steps for the submit task history, since that would require transfers to exist. May update that in future. --- frontend/public/changelog/v1.0.9-beta.md | 4 +- frontend/src/common/reactJoyrideSteps.ts | 41 ++++++++++++++++--- frontend/src/components/Facets/FacetsForm.tsx | 18 ++++++-- frontend/src/components/Search/Table.tsx | 2 +- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/frontend/public/changelog/v1.0.9-beta.md b/frontend/public/changelog/v1.0.9-beta.md index 2ef166f62..addbb16e7 100644 --- a/frontend/public/changelog/v1.0.9-beta.md +++ b/frontend/public/changelog/v1.0.9-beta.md @@ -6,11 +6,13 @@ This is the 'Globus Transfer' update! You now have the ability to select a Globu 1. Added ability to transfer datasets (when available) through Globus. 2. Added several features related to the Globus Transfer functionality - - Added filter option to show only results that can be transfered with Globus + - Added filter option to show only results that can be transferred with Globus - Created a new column for Globus Ready status, to indicate visually which datasets can be transfered with Globus - Created tooltips on the Globus Ready icon to indicate what data node the dataset comes from - Incorporated Globus Transfer related U.I features that allows user to select a new endpoint or use a existing default that they already saved - Provided notifications and logic to alert users when they try to transfer a dataset that is not Globus Ready + - Added ability to store recent Globus transfer tasks as they are submitted, for later reference + - After a successful transfer, users can now click a link and view the submitted task on the Globus site 3. Utilized new functions that take advantage of Django's session storage, for persistent storage of needed data 4. Introduced the use or Recoil and shared state among various components, which will allow improved flexibility for adding features moving forward 5. Several updates to packages and refactoring of code to improve code base and application reliability diff --git a/frontend/src/common/reactJoyrideSteps.ts b/frontend/src/common/reactJoyrideSteps.ts index c536bf54a..7aeb9946d 100644 --- a/frontend/src/common/reactJoyrideSteps.ts +++ b/frontend/src/common/reactJoyrideSteps.ts @@ -1,3 +1,4 @@ +import { globusEnabledNodes } from '../env'; import { JoyrideTour } from './JoyrideTour'; import { TargetObject } from './TargetObject'; import { AppPage } from './types'; @@ -108,6 +109,9 @@ export const leftSidebarTargets = { selectProjectBtn: new TargetObject(), projectSelectLeftSideBtn: new TargetObject(), projectWebsiteBtn: new TargetObject(), + filterByGlobusTransfer: new TargetObject(), + filterByGlobusTransferAny: new TargetObject(), + filterByGlobusTransferOnly: new TargetObject(), searchFacetsForm: new TargetObject(), facetFormGeneral: new TargetObject(), facetFormFields: new TargetObject(), @@ -131,6 +135,7 @@ export const topDataRowTargets = { downloadScriptForm: new TargetObject(), downloadScriptOptions: new TargetObject(), downloadScriptBtn: new TargetObject(), + globusReadyStatusIcon: new TargetObject(), }; export const innerDataRowTargets = { @@ -223,6 +228,11 @@ const addDataRowTourSteps = (tour: JoyrideTour): JoyrideTour => { 'Clicking this button will begin the download of your script.', 'top' ) + .addNextStep( + topDataRowTargets.globusReadyStatusIcon.selector(), + 'This icon indicates whether the dataset can be transferred with Globus. A check mark means it is Globus Ready and can be transferred through Globus. When hovering over the icon you will see more detail as to what node this dataset is coming from and whether the node is Globus ready.', + 'top-start' + ) .addNextStep( topDataRowTargets.searchResultsRowExpandIcon.selector(), 'To view more information about a specific dataset, you can expand the row by clicking this little arrow icon...', @@ -429,12 +439,33 @@ export const createMainPageTour = (): JoyrideTour => { ); } + tour.addNextStep( + leftSidebarTargets.projectWebsiteBtn.selector(), + 'Once a project is selected, if you wish, you can go view the project website by clicking this button.', + 'right' + ); + + // Add tour elements for globus ready filter (if globus enabled nodes has been configured) + if (globusEnabledNodes.length > 0) { + tour + .addNextStep( + leftSidebarTargets.filterByGlobusTransfer.selector(), + 'This section allows you to filter search results based on globus transfer availability. There are a set of data nodes that provide the Globus Transfer option, however not all do. You can filter to show all datasets, or only those that can be transferred via globus.', + 'right' + ) + .addNextStep( + leftSidebarTargets.filterByGlobusTransferAny.selector(), + 'Selecting this option will leave the filter off and allow you to see all datasets, including ones that may not have Globus transfer as an option.', + 'right' + ) + .addNextStep( + leftSidebarTargets.filterByGlobusTransferOnly.selector(), + 'Selecting this option will filter all datasets, so that only the ones that have Globus transfer as an option will be visible.', + 'right' + ); + } + tour - .addNextStep( - leftSidebarTargets.projectWebsiteBtn.selector(), - 'Once a project is selected, if you wish, you can go view the project website by clicking this button.', - 'right' - ) .addNextStep( leftSidebarTargets.searchFacetsForm.selector(), 'This area contains various groups of facets and parameters that you can use to filter results from your selected project.', diff --git a/frontend/src/components/Facets/FacetsForm.tsx b/frontend/src/components/Facets/FacetsForm.tsx index 07b12e9c9..cb48a842c 100644 --- a/frontend/src/components/Facets/FacetsForm.tsx +++ b/frontend/src/components/Facets/FacetsForm.tsx @@ -256,7 +256,7 @@ const FacetsForm: React.FC = ({ }} > {globusEnabledNodes.length > 0 && ( - <> +

Filter By Transfer Options

@@ -264,13 +264,23 @@ const FacetsForm: React.FC = ({ onChange={handleOnGlobusReadyChanged} value={globusReadyOnly} > - Any - Only Globus Transferrable + + Any + + + Only Globus Transferrable +
- +
)} diff --git a/frontend/src/components/Search/Table.tsx b/frontend/src/components/Search/Table.tsx index 6cf766328..41636cc37 100644 --- a/frontend/src/components/Search/Table.tsx +++ b/frontend/src/components/Search/Table.tsx @@ -316,7 +316,7 @@ const Table: React.FC = ({ render: (data_node: string) => (
From c1198723abc7438b47f93e352139a31f391d544e Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Wed, 28 Jun 2023 19:50:56 -0700 Subject: [PATCH 45/65] Added logic to ensure transfers aren't done too quickly (which may cause errors), modified the transfer messaging to wait for the message to finish before allwing more transfers. Attempted to add disable logic for transfer button to improve reliability, however may be changed later if needed. Updated the messages content and added more information to changelog message. Added tooltips for the endpoint settings on Globus transfer page (in place of tutorial steps). --- frontend/public/changelog/v1.0.9-beta.md | 2 +- frontend/public/messages/metagrid_messages.md | 3 +- frontend/public/messages/test_message.md | 3 - .../src/components/Globus/DatasetDownload.tsx | 109 +++++++++++------- .../Messaging/messageDisplayData.ts | 1 + 5 files changed, 72 insertions(+), 46 deletions(-) delete mode 100644 frontend/public/messages/test_message.md diff --git a/frontend/public/changelog/v1.0.9-beta.md b/frontend/public/changelog/v1.0.9-beta.md index addbb16e7..a2bf36a68 100644 --- a/frontend/public/changelog/v1.0.9-beta.md +++ b/frontend/public/changelog/v1.0.9-beta.md @@ -1,6 +1,6 @@ ## Summary -This is the 'Globus Transfer' update! You now have the ability to select a Globus Transfer option within the data cart (for select Globus Ready datasets). When transferring with Globus, you will be redirected to provide permission and to select your desired endpoint. Then you'll be able to save your endpoint as default and start the Globus transfer process. Since this is a brand new feature, there is a chance things will not work perfectly but we hope to address issues and make future improvements and updates as we move forward. +This is the 'Globus Transfer' update! You now have the ability to use the Globus Transfer option within the data cart (only for Globus Ready datasets). When transferring with Globus, you will be redirected to provide permission and to select your desired endpoint. Then you'll be able to save your endpoint as default and start the Globus transfer process. Since this is a brand new feature, there is a chance things will not work perfectly but we hope to address issues and make future improvements and updates as we move forward. **If you are not familiar with Globus, we highly recommend you visit the Globus website**: [https://www.globus.org/get-started](https://www.globus.org/get-started), to learn more and get started, before attempting to use the Globus transfer feature. **Changes** diff --git a/frontend/public/messages/metagrid_messages.md b/frontend/public/messages/metagrid_messages.md index 3e2d8b810..2728f9b19 100644 --- a/frontend/public/messages/metagrid_messages.md +++ b/frontend/public/messages/metagrid_messages.md @@ -10,4 +10,5 @@ Metagrid uses an updated user accounts system. Unfortunately for anyone looking We are excited to be planning to have an "official" release of the Metagrid platform onto scalable infrastructure. In the meantime we will be testing new features. -- Globus Transfer feature updates planned to be released in v1.1.0. +- More feature updates planned to be released in v1.1.0. +- Globus Transfer feature will be improved as needed in v1.1.0 diff --git a/frontend/public/messages/test_message.md b/frontend/public/messages/test_message.md deleted file mode 100644 index 2a47d9a16..000000000 --- a/frontend/public/messages/test_message.md +++ /dev/null @@ -1,3 +0,0 @@ -# This is just a test - -Blah blah diff --git a/frontend/src/components/Globus/DatasetDownload.tsx b/frontend/src/components/Globus/DatasetDownload.tsx index 621cee989..e519432cf 100644 --- a/frontend/src/components/Globus/DatasetDownload.tsx +++ b/frontend/src/components/Globus/DatasetDownload.tsx @@ -34,6 +34,7 @@ import { GlobusTaskItem, MAX_TASK_LIST_LENGTH, } from './types'; +import ToolTip from '../DataDisplay/ToolTip'; // Reference: https://github.com/bpedroza/js-pkce const GlobusAuth = new PKCE({ @@ -92,6 +93,13 @@ const DatasetDownloadForm: React.FC = () => { ); // Component internal state + const [downloadActive, setDownloadActive] = React.useState(true); + + const [ + selectedDownloadType, + setSelectedDownloadType, + ] = React.useState(downloadOptions[0]); + const [globusStepsModal, setGlobusStepsModal] = React.useState({ show: false, state: 'both', @@ -244,6 +252,9 @@ const DatasetDownloadForm: React.FC = () => { const ids = loadedSelections.map((item) => (item ? item.id : '')); if (globusTransferToken && refreshToken) { + let messageContent: React.ReactNode | string = null; + let messageType: 'success' | 'warning' | 'error' = 'success'; + startGlobusTransfer( globusTransferToken.access_token, refreshToken, @@ -251,7 +262,7 @@ const DatasetDownloadForm: React.FC = () => { endpoint?.path || '', ids ) - .then(async (resp) => { + .then((resp) => { if (resp.status === 200) { setItemSelections([]); setDownloadIsLoading(false); @@ -268,7 +279,7 @@ const DatasetDownloadForm: React.FC = () => { addNewTask(taskItem); if (taskItem.taskStatusURL !== '') { - await message.info( + messageContent = (

Globus transfer task submitted successfully!
@@ -279,30 +290,31 @@ const DatasetDownloadForm: React.FC = () => { > View Task Status -

, - 5 +

); } } else { - await message.info( - `Globus transfer task submitted successfully!`, - 5 - ); + messageContent = `Globus transfer task submitted successfully!`; } } else { - await message.warning( - `Globus transfer task struggled: ${resp.statusText}`, - 5 - ); + messageContent = `Globus transfer task struggled: ${resp.statusText}`; + messageType = 'warning'; } }) - .catch(async (error: ResponseError) => { - await message.error( - `Globus transfer task failed: ${error.message}`, - 5 - ); + .catch((error: ResponseError) => { + messageContent = `Globus transfer task failed: ${error.message}`; + messageType = 'error'; }) .finally(async () => { + setDownloadActive(false); + if (messageType === 'success') { + await message.success(messageContent, 5); + } else if (messageType === 'warning') { + await message.warning(messageContent, 5); + } else { + await message.error(messageContent, 5); + } + setDownloadActive(true); await endDownloadSteps(); }); } @@ -703,7 +715,15 @@ const DatasetDownloadForm: React.FC = () => { name="downloadType" className={cartTourTargets.downloadAllType.class()} > - { + const downloadType = rawType?.toString(); + if (downloadType) { + setSelectedDownloadType(downloadType); + } + }} + > {downloadOptions.map((option) => ( {option} @@ -718,34 +738,41 @@ const DatasetDownloadForm: React.FC = () => { type="primary" htmlType="submit" icon={} - disabled={itemSelections.length === 0} + disabled={itemSelections.length === 0 || !downloadActive} loading={downloadIsLoading} > - Download + {selectedDownloadType === 'Globus' ? 'Transfer' : 'Download'}
- {defaultGlobusEndpoint && itemSelections.length !== 0 && ( - - { - setUseGlobusDefaultEndpoint(e.target.value as boolean); - saveSessionValue( - GlobusStateKeys.useDefaultEndpoint, - e.target.value as boolean - ); - }} - value={useGlobusDefaultEndpoint} - > - - - Default Endpoint - - Specify Endpoint - - - - )} + {selectedDownloadType === 'Globus' && + defaultGlobusEndpoint && + itemSelections.length !== 0 && + downloadActive && ( + + { + setUseGlobusDefaultEndpoint(e.target.value as boolean); + saveSessionValue( + GlobusStateKeys.useDefaultEndpoint, + e.target.value as boolean + ); + }} + value={useGlobusDefaultEndpoint} + > + + + + Default Endpoint + + + + Specify Endpoint + + + + + )} Date: Thu, 29 Jun 2023 14:20:55 -0700 Subject: [PATCH 46/65] Resolved issue with blank message popup when clicking remove all items, resolved issue with blank error messages. If error.message is empty, there will be a generic message displayed: unknown error occurred. Changed the breadcrumbs organization so that search page is clearly identified as home page, and updated home button to redirect to the home page to resolve issue 533 --- frontend/public/changelog/v1.0.9-beta.md | 1 + frontend/src/components/App/App.tsx | 77 ++++++++++++------- frontend/src/components/Cart/Items.tsx | 7 +- .../src/components/Globus/DatasetDownload.tsx | 12 ++- frontend/src/components/Search/FilesTable.tsx | 16 ++-- frontend/src/components/Search/Table.tsx | 12 ++- 6 files changed, 83 insertions(+), 42 deletions(-) diff --git a/frontend/public/changelog/v1.0.9-beta.md b/frontend/public/changelog/v1.0.9-beta.md index a2bf36a68..f27d14cee 100644 --- a/frontend/public/changelog/v1.0.9-beta.md +++ b/frontend/public/changelog/v1.0.9-beta.md @@ -16,3 +16,4 @@ This is the 'Globus Transfer' update! You now have the ability to use the Globus 3. Utilized new functions that take advantage of Django's session storage, for persistent storage of needed data 4. Introduced the use or Recoil and shared state among various components, which will allow improved flexibility for adding features moving forward 5. Several updates to packages and refactoring of code to improve code base and application reliability +6. Bug fixes and minor improvements to the User Interface diff --git a/frontend/src/components/App/App.tsx b/frontend/src/components/App/App.tsx index 8134c0b00..0a2f4ca57 100644 --- a/frontend/src/components/App/App.tsx +++ b/frontend/src/components/App/App.tsx @@ -156,9 +156,13 @@ const App: React.FC = ({ searchQuery }) => { setUserCart(combinedCarts); }) .catch((error: ResponseError) => { - void message.error({ - content: error.message, - }); + if (error.message) { + message.error({ + content: error.message, + }); + } else { + message.error('An unknown error has occurred.'); + } }); void fetchUserSearchQueries(accessToken) @@ -179,9 +183,13 @@ const App: React.FC = ({ searchQuery }) => { setUserSearchQueries(databaseItems.concat(searchQueriesToAdd)); }) .catch((error: ResponseError) => { - void message.error({ - content: error.message, - }); + if (error.message) { + message.error({ + content: error.message, + }); + } else { + message.error('An unknown error has occurred.'); + } }); } }, [isAuthenticated, pk, accessToken]); @@ -226,9 +234,13 @@ const App: React.FC = ({ searchQuery }) => { .catch( /* istanbul ignore next */ (error: ResponseError) => { - void message.error({ - content: error.message, - }); + if (error.message) { + message.error({ + content: error.message, + }); + } else { + message.error('An unknown error has occurred.'); + } } ); }, [fetchProjects]); @@ -238,7 +250,7 @@ const App: React.FC = ({ searchQuery }) => { text: string ): void => { if (activeSearchQuery.textInputs.includes(text as never)) { - void message.error(`Input "${text}" has already been applied`); + message.error(`Input "${text}" has already been applied`); } else { setActiveSearchQuery({ ...activeSearchQuery, @@ -250,7 +262,7 @@ const App: React.FC = ({ searchQuery }) => { const handleOnSetFilenameVars = (filenameVar: string): void => { if (activeSearchQuery.filenameVars.includes(filenameVar as never)) { - void message.error(`Input "${filenameVar}" has already been applied`); + message.error(`Input "${filenameVar}" has already been applied`); } else { setActiveSearchQuery({ ...activeSearchQuery, @@ -342,7 +354,7 @@ const App: React.FC = ({ searchQuery }) => { newCart = [...userCart, ...itemsNotInCart]; setUserCart(newCart); - void message.success({ + message.success({ content: 'Added item(s) to your cart', icon: , }); @@ -352,7 +364,7 @@ const App: React.FC = ({ searchQuery }) => { ); setUserCart(newCart); - void message.success({ + message.success({ content: 'Removed item(s) from your cart', icon: , }); @@ -390,7 +402,7 @@ const App: React.FC = ({ searchQuery }) => { }; if (searchAlreadyExists(userSearchQueries, savedSearch)) { - void message.success({ + message.success({ content: 'Search query is already in your library', icon: , }); @@ -399,7 +411,7 @@ const App: React.FC = ({ searchQuery }) => { const saveSuccess = (): void => { setUserSearchQueries([...userSearchQueries, savedSearch]); - void message.success({ + message.success({ content: 'Saved search query to your library', icon: , }); @@ -411,9 +423,13 @@ const App: React.FC = ({ searchQuery }) => { saveSuccess(); }) .catch((error: ResponseError) => { - void message.error({ - content: error.message, - }); + if (error.message) { + message.error({ + content: error.message, + }); + } else { + message.error('An unknown error has occurred.'); + } }); } else { saveSuccess(); @@ -425,8 +441,8 @@ const App: React.FC = ({ searchQuery }) => { // copy link to clipboard /* istanbul ignore if */ if (navigator && navigator.clipboard) { - void navigator.clipboard.writeText(getUrlFromSearch(activeSearchQuery)); - void message.success({ + navigator.clipboard.writeText(getUrlFromSearch(activeSearchQuery)); + message.success({ content: 'Search copied to clipboard!', icon: , }); @@ -442,7 +458,7 @@ const App: React.FC = ({ searchQuery }) => { (searchItem: UserSearchQuery) => searchItem.uuid !== searchUUID ) ); - void message.success({ + message.success({ content: 'Removed search query from your library', icon: , }); @@ -454,9 +470,13 @@ const App: React.FC = ({ searchQuery }) => { deleteSuccess(); }) .catch((error: ResponseError) => { - void message.error({ - content: error.message, - }); + if (error.message) { + message.error({ + content: error.message, + }); + } else { + message.error('An unknown error has occurred.'); + } }); } else { deleteSuccess(); @@ -562,7 +582,6 @@ const App: React.FC = ({ searchQuery }) => { Home - Search = ({ searchQuery }) => { <> - Home + + Home + Data Node Status @@ -604,7 +625,9 @@ const App: React.FC = ({ searchQuery }) => { <> - Home + + Home + Cart diff --git a/frontend/src/components/Cart/Items.tsx b/frontend/src/components/Cart/Items.tsx index 96336505f..c1f4fefc2 100644 --- a/frontend/src/components/Cart/Items.tsx +++ b/frontend/src/components/Cart/Items.tsx @@ -54,7 +54,12 @@ const Items: React.FC = ({ userCart, onUpdateCart, onClearCart }) => {
{userCart.length > 0 && ( + Do you wish to remove all +
items from your cart? +

+ } icon={} onConfirm={onClearCart} > diff --git a/frontend/src/components/Globus/DatasetDownload.tsx b/frontend/src/components/Globus/DatasetDownload.tsx index e519432cf..2f3cd1e05 100644 --- a/frontend/src/components/Globus/DatasetDownload.tsx +++ b/frontend/src/components/Globus/DatasetDownload.tsx @@ -214,8 +214,7 @@ const DatasetDownloadForm: React.FC = () => { const handleWgetDownload = (): void => { if (itemSelections !== null) { const ids = itemSelections.map((item) => item.id); - // eslint-disable-next-line no-void - void message.success( + message.success( 'The wget script is generating, please wait momentarily.', 10 ); @@ -226,8 +225,13 @@ const DatasetDownloadForm: React.FC = () => { setDownloadIsLoading(false); }) .catch((error: ResponseError) => { - // eslint-disable-next-line no-void - void message.error(error.message); + if (error.message) { + message.error({ + content: error.message, + }); + } else { + message.error('An unknown error has occurred.'); + } setDownloadIsLoading(false); }); } diff --git a/frontend/src/components/Search/FilesTable.tsx b/frontend/src/components/Search/FilesTable.tsx index 948db2240..ace8ddd4e 100644 --- a/frontend/src/components/Search/FilesTable.tsx +++ b/frontend/src/components/Search/FilesTable.tsx @@ -250,12 +250,16 @@ const FilesTable: React.FC = ({ id, numResults = 0, filenameVars }) => { if (navigator && navigator.clipboard) { void navigator.clipboard .writeText(downloadUrls.OPENDAP) - .catch( - (e: PromiseRejectedResult) => - // eslint-disable-next-line - void message.error(e.reason) - ); - void message.success({ + .catch((e: PromiseRejectedResult) => { + if (e.reason) { + message.error({ + content: e.reason as string, + }); + } else { + message.error('An unknown error has occurred.'); + } + }); + message.success({ content: 'OPENDAP URL copied to clipboard!', icon: ( diff --git a/frontend/src/components/Search/Table.tsx b/frontend/src/components/Search/Table.tsx index 41636cc37..ba75b5261 100644 --- a/frontend/src/components/Search/Table.tsx +++ b/frontend/src/components/Search/Table.tsx @@ -247,8 +247,7 @@ const Table: React.FC = ({ ): void => { /* istanbul ignore else */ if (downloadType === 'wget') { - // eslint-disable-next-line no-void - void message.success( + message.success( 'The wget script is generating, please wait momentarily.' ); fetchWgetScript(record.id, filenameVars) @@ -256,8 +255,13 @@ const Table: React.FC = ({ openDownloadURL(url); }) .catch((error: ResponseError) => { - // eslint-disable-next-line no-void - void message.error(error.message); + if (error.message) { + message.error({ + content: error.message, + }); + } else { + message.error('An unknown error has occurred.'); + } }); } }; From 0bf930790b040d8cee5f7127173ed01f22f9f6ba Mon Sep 17 00:00:00 2001 From: Sasha Ames Date: Thu, 29 Jun 2023 14:30:09 -0700 Subject: [PATCH 47/65] Messages to accompany Globus Transfer beta release (#534) * info on Globus Transfer * Update index.tsx Added styling fixes and updated. --------- Co-authored-by: Carlos Downie <42552189+downiec@users.noreply.github.com> --- frontend/public/messages/metagrid_messages.md | 5 +++++ frontend/src/components/Support/index.tsx | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/frontend/public/messages/metagrid_messages.md b/frontend/public/messages/metagrid_messages.md index 2728f9b19..3f25b92ce 100644 --- a/frontend/public/messages/metagrid_messages.md +++ b/frontend/public/messages/metagrid_messages.md @@ -2,6 +2,11 @@ Use the Help link to find information on how to contact support or report any issues you find. +## Globus Transfers enabled + +This version of Metagrid supports the user of Globus to transfer ESGF datasets to your institutional or personal endpoint. The feature can be accessed at the bottom of the Data Cart page. At present only data published at LLNL is available for Globus Transfer via Metagrid. Other sites may continue to have data transferrable using the *legacy* CoG interface. +For more information about Globus Transfers please see: https://app.globus.org/help + ## CORDEX data _not_ supported Metagrid uses an updated user accounts system. Unfortunately for anyone looking for CORDEX data, these new accounts cannot be used to authenticate when running a CORDEX Wget script. Please use an ESGF _legacy_ OpenID obtained at any of the ESGF CoG instances listed here: https://esgf.github.io/nodes.html diff --git a/frontend/src/components/Support/index.tsx b/frontend/src/components/Support/index.tsx index 92c2d0154..ad99b586a 100644 --- a/frontend/src/components/Support/index.tsx +++ b/frontend/src/components/Support/index.tsx @@ -78,6 +78,13 @@ const Support: React.FC = ({ visible, onClose }) => { https://esgf.github.io/esgf-user-support/metagrid.html

+

+ If you need help on Globus Transfers, please visit this page for + more information: + + https://app.globus.org/help + +

User Interface Tours

From bd9d00e9cb33cc538ecefac5cec693b4dfad341c Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Thu, 29 Jun 2023 17:14:48 -0700 Subject: [PATCH 48/65] Created notification functions to simplify code a bit and to adjust location of notifications to be below the top navigation bar. Functions allow messages to be standardized and empty error messages can give the same generic error message when needed. Styling can be standardized in one place. --- frontend/src/common/utils.ts | 49 +++++++++++++ frontend/src/components/App/App.tsx | 68 +++++-------------- .../src/components/Globus/DatasetDownload.tsx | 36 ++++------ frontend/src/components/Search/FilesTable.tsx | 20 +++--- frontend/src/components/Search/Table.tsx | 17 ++--- 5 files changed, 95 insertions(+), 95 deletions(-) diff --git a/frontend/src/common/utils.ts b/frontend/src/common/utils.ts index b097ff6d0..c6085e299 100644 --- a/frontend/src/common/utils.ts +++ b/frontend/src/common/utils.ts @@ -1,3 +1,5 @@ +import { CSSProperties, ReactNode } from 'react'; +import { message } from 'antd'; import { UserSearchQueries, UserSearchQuery } from '../components/Cart/types'; import { ActiveFacets } from '../components/Facets/types'; import { @@ -9,6 +11,53 @@ import { VersionType, } from '../components/Search/types'; +export type NotificationType = 'success' | 'info' | 'warning' | 'error'; +export async function showNotice( + content: React.ReactNode | string, + config?: { + duration?: number; + icon?: ReactNode; + type?: NotificationType; + style?: CSSProperties; + key?: string | number; + } +): Promise { + const msgConfig = { + content, + duration: config?.duration, + icon: config?.icon, + style: { marginTop: '60px', ...config?.style }, + key: config?.key, + }; + + switch (config?.type) { + case 'success': + await message.success(msgConfig); + break; + case 'warning': + await message.warning(msgConfig); + break; + case 'error': + await message.error(msgConfig); + break; + case 'info': + await message.info(msgConfig); + break; + default: + await message.success(msgConfig); + } +} + +export async function showError( + errorMsg: React.ReactNode | string +): Promise { + let msg = errorMsg; + if (!errorMsg || errorMsg === '') { + msg = 'An unknown error has occurred.'; + } + await showNotice(msg, { duration: 5, type: 'error' }); +} + /** * Checks if an object is empty. */ diff --git a/frontend/src/components/App/App.tsx b/frontend/src/components/App/App.tsx index 0a2f4ca57..d2867623a 100644 --- a/frontend/src/components/App/App.tsx +++ b/frontend/src/components/App/App.tsx @@ -8,7 +8,7 @@ import { ShareAltOutlined, ShoppingCartOutlined, } from '@ant-design/icons'; -import { Affix, Breadcrumb, Button, Layout, message, Result } from 'antd'; +import { Affix, Breadcrumb, Button, Layout, Result } from 'antd'; import React, { ReactElement } from 'react'; import { useAsync } from 'react-async'; import { hotjar } from 'react-hotjar'; @@ -29,6 +29,8 @@ import { combineCarts, getUrlFromSearch, searchAlreadyExists, + showError, + showNotice, unsavedLocalSearches, } from '../../common/utils'; import { AuthContext } from '../../contexts/AuthContext'; @@ -156,13 +158,7 @@ const App: React.FC = ({ searchQuery }) => { setUserCart(combinedCarts); }) .catch((error: ResponseError) => { - if (error.message) { - message.error({ - content: error.message, - }); - } else { - message.error('An unknown error has occurred.'); - } + showError(error.message); }); void fetchUserSearchQueries(accessToken) @@ -183,13 +179,7 @@ const App: React.FC = ({ searchQuery }) => { setUserSearchQueries(databaseItems.concat(searchQueriesToAdd)); }) .catch((error: ResponseError) => { - if (error.message) { - message.error({ - content: error.message, - }); - } else { - message.error('An unknown error has occurred.'); - } + showError(error.message); }); } }, [isAuthenticated, pk, accessToken]); @@ -234,13 +224,7 @@ const App: React.FC = ({ searchQuery }) => { .catch( /* istanbul ignore next */ (error: ResponseError) => { - if (error.message) { - message.error({ - content: error.message, - }); - } else { - message.error('An unknown error has occurred.'); - } + showError(error.message); } ); }, [fetchProjects]); @@ -250,7 +234,7 @@ const App: React.FC = ({ searchQuery }) => { text: string ): void => { if (activeSearchQuery.textInputs.includes(text as never)) { - message.error(`Input "${text}" has already been applied`); + showError(`Input "${text}" has already been applied`); } else { setActiveSearchQuery({ ...activeSearchQuery, @@ -262,7 +246,7 @@ const App: React.FC = ({ searchQuery }) => { const handleOnSetFilenameVars = (filenameVar: string): void => { if (activeSearchQuery.filenameVars.includes(filenameVar as never)) { - message.error(`Input "${filenameVar}" has already been applied`); + showError(`Input "${filenameVar}" has already been applied`); } else { setActiveSearchQuery({ ...activeSearchQuery, @@ -353,9 +337,7 @@ const App: React.FC = ({ searchQuery }) => { newCart = [...userCart, ...itemsNotInCart]; setUserCart(newCart); - - message.success({ - content: 'Added item(s) to your cart', + showNotice('Added item(s) to your cart', { icon: , }); } else if (operation === 'remove') { @@ -364,8 +346,7 @@ const App: React.FC = ({ searchQuery }) => { ); setUserCart(newCart); - message.success({ - content: 'Removed item(s) from your cart', + showNotice('Removed item(s) from your cart', { icon: , }); } @@ -402,17 +383,16 @@ const App: React.FC = ({ searchQuery }) => { }; if (searchAlreadyExists(userSearchQueries, savedSearch)) { - message.success({ - content: 'Search query is already in your library', + showNotice('Search query is already in your library', { icon: , + type: 'info', }); return; } const saveSuccess = (): void => { setUserSearchQueries([...userSearchQueries, savedSearch]); - message.success({ - content: 'Saved search query to your library', + showNotice('Saved search query to your library', { icon: , }); }; @@ -423,13 +403,7 @@ const App: React.FC = ({ searchQuery }) => { saveSuccess(); }) .catch((error: ResponseError) => { - if (error.message) { - message.error({ - content: error.message, - }); - } else { - message.error('An unknown error has occurred.'); - } + showError(error.message); }); } else { saveSuccess(); @@ -442,8 +416,7 @@ const App: React.FC = ({ searchQuery }) => { /* istanbul ignore if */ if (navigator && navigator.clipboard) { navigator.clipboard.writeText(getUrlFromSearch(activeSearchQuery)); - message.success({ - content: 'Search copied to clipboard!', + showNotice('Search copied to clipboard!', { icon: , }); } @@ -458,8 +431,7 @@ const App: React.FC = ({ searchQuery }) => { (searchItem: UserSearchQuery) => searchItem.uuid !== searchUUID ) ); - message.success({ - content: 'Removed search query from your library', + showNotice('Removed search query from your library', { icon: , }); }; @@ -470,13 +442,7 @@ const App: React.FC = ({ searchQuery }) => { deleteSuccess(); }) .catch((error: ResponseError) => { - if (error.message) { - message.error({ - content: error.message, - }); - } else { - message.error('An unknown error has occurred.'); - } + showError(error.message); }); } else { deleteSuccess(); diff --git a/frontend/src/components/Globus/DatasetDownload.tsx b/frontend/src/components/Globus/DatasetDownload.tsx index 2f3cd1e05..e59e32238 100644 --- a/frontend/src/components/Globus/DatasetDownload.tsx +++ b/frontend/src/components/Globus/DatasetDownload.tsx @@ -1,5 +1,5 @@ import { CheckCircleFilled, DownloadOutlined } from '@ant-design/icons'; -import { Button, Form, message, Modal, Radio, Select, Space } from 'antd'; +import { Button, Form, Modal, Radio, Select, Space } from 'antd'; import PKCE from 'js-pkce'; import React, { useEffect } from 'react'; import { useRecoilState } from 'recoil'; @@ -35,6 +35,7 @@ import { MAX_TASK_LIST_LENGTH, } from './types'; import ToolTip from '../DataDisplay/ToolTip'; +import { NotificationType, showError, showNotice } from '../../common/utils'; // Reference: https://github.com/bpedroza/js-pkce const GlobusAuth = new PKCE({ @@ -214,10 +215,10 @@ const DatasetDownloadForm: React.FC = () => { const handleWgetDownload = (): void => { if (itemSelections !== null) { const ids = itemSelections.map((item) => item.id); - message.success( - 'The wget script is generating, please wait momentarily.', - 10 - ); + showNotice('The wget script is generating, please wait momentarily.', { + duration: 7, + type: 'info', + }); setDownloadIsLoading(true); fetchWgetScript(ids) .then((url) => { @@ -225,13 +226,7 @@ const DatasetDownloadForm: React.FC = () => { setDownloadIsLoading(false); }) .catch((error: ResponseError) => { - if (error.message) { - message.error({ - content: error.message, - }); - } else { - message.error('An unknown error has occurred.'); - } + showError(error.message); setDownloadIsLoading(false); }); } @@ -243,7 +238,7 @@ const DatasetDownloadForm: React.FC = () => { endpoint: GlobusEndpointData | null ): Promise => { if (!endpoint) { - message.warning(`Globus endpoint was undefined.`); + showNotice('Globus endpoint was undefined.', { type: 'warning' }); return; } @@ -257,7 +252,7 @@ const DatasetDownloadForm: React.FC = () => { if (globusTransferToken && refreshToken) { let messageContent: React.ReactNode | string = null; - let messageType: 'success' | 'warning' | 'error' = 'success'; + let messageType: NotificationType = 'success'; startGlobusTransfer( globusTransferToken.access_token, @@ -311,13 +306,10 @@ const DatasetDownloadForm: React.FC = () => { }) .finally(async () => { setDownloadActive(false); - if (messageType === 'success') { - await message.success(messageContent, 5); - } else if (messageType === 'warning') { - await message.warning(messageContent, 5); - } else { - await message.error(messageContent, 5); - } + await showNotice(messageContent, { + duration: 5, + type: messageType, + }); setDownloadActive(true); await endDownloadSteps(); }); @@ -522,7 +514,7 @@ const DatasetDownloadForm: React.FC = () => { } } } catch (error: unknown) { - message.error('Error occured when obtaining transfer permissions.'); + showError('Error occured when obtaining transfer permissions.'); } finally { // This isn't strictly necessary but it ensures no code reuse. sessionStorage.removeItem('pkce_code_verifier'); diff --git a/frontend/src/components/Search/FilesTable.tsx b/frontend/src/components/Search/FilesTable.tsx index ace8ddd4e..d4827efb1 100644 --- a/frontend/src/components/Search/FilesTable.tsx +++ b/frontend/src/components/Search/FilesTable.tsx @@ -6,7 +6,7 @@ import { RightCircleOutlined, ShareAltOutlined, } from '@ant-design/icons'; -import { Alert, Form, message, Table as TableD } from 'antd'; +import { Alert, Form, Table as TableD } from 'antd'; import { SizeType } from 'antd/lib/config-provider/SizeContext'; import { TablePaginationConfig } from 'antd/lib/table'; import React from 'react'; @@ -14,7 +14,12 @@ import { DeferFn, useAsync } from 'react-async'; import { fetchDatasetFiles, openDownloadURL } from '../../api'; import { innerDataRowTargets } from '../../common/reactJoyrideSteps'; import { CSSinJS } from '../../common/types'; -import { formatBytes, splitStringByChar } from '../../common/utils'; +import { + formatBytes, + showError, + showNotice, + splitStringByChar, +} from '../../common/utils'; import ToolTip from '../DataDisplay/ToolTip'; import Button from '../General/Button'; import { @@ -251,16 +256,9 @@ const FilesTable: React.FC = ({ id, numResults = 0, filenameVars }) => { void navigator.clipboard .writeText(downloadUrls.OPENDAP) .catch((e: PromiseRejectedResult) => { - if (e.reason) { - message.error({ - content: e.reason as string, - }); - } else { - message.error('An unknown error has occurred.'); - } + showError(e.reason as string); }); - message.success({ - content: 'OPENDAP URL copied to clipboard!', + showNotice('OPENDAP URL copied to clipboard!', { icon: ( ), diff --git a/frontend/src/components/Search/Table.tsx b/frontend/src/components/Search/Table.tsx index ba75b5261..14af7d6bb 100644 --- a/frontend/src/components/Search/Table.tsx +++ b/frontend/src/components/Search/Table.tsx @@ -5,13 +5,13 @@ import { PlusOutlined, RightCircleOutlined, } from '@ant-design/icons'; -import { Form, message, Select, Table as TableD } from 'antd'; +import { Form, Select, Table as TableD } from 'antd'; import { SizeType } from 'antd/lib/config-provider/SizeContext'; import { TablePaginationConfig } from 'antd/lib/table'; import React from 'react'; import { fetchWgetScript, openDownloadURL, ResponseError } from '../../api'; import { topDataRowTargets } from '../../common/reactJoyrideSteps'; -import { formatBytes } from '../../common/utils'; +import { formatBytes, showError, showNotice } from '../../common/utils'; import { UserCart } from '../Cart/types'; import ToolTip from '../DataDisplay/ToolTip'; import Button from '../General/Button'; @@ -247,21 +247,16 @@ const Table: React.FC = ({ ): void => { /* istanbul ignore else */ if (downloadType === 'wget') { - message.success( - 'The wget script is generating, please wait momentarily.' + showNotice( + 'The wget script is generating, please wait momentarily.', + { type: 'info' } ); fetchWgetScript(record.id, filenameVars) .then((url) => { openDownloadURL(url); }) .catch((error: ResponseError) => { - if (error.message) { - message.error({ - content: error.message, - }); - } else { - message.error('An unknown error has occurred.'); - } + showError(error.message); }); } }; From d65fb2cd3ce3ab3b029a413957c9c3abe693e068 Mon Sep 17 00:00:00 2001 From: Carlos Downie <42552189+downiec@users.noreply.github.com> Date: Thu, 13 Jul 2023 19:12:13 -0700 Subject: [PATCH 49/65] Fixed the issues encountered with the manage_metagrid.sh functions on aims2 and updated the references to the docker-compose command to be 'docker compose' (with a space) to match the newer docker compose recommendations. Resolved an issue where the items in the cart were not correctly displaying the node status and were instead showing a question mark. Updated traefik version to 2.10 and updated the config file. --- .gitignore | 1 + backend/UpdateProjectData_README.txt | 22 ++++++++------- .../production/postgres/maintenance/backup | 2 +- .../production/postgres/maintenance/restore | 2 +- backend/metagrid/cart/views.py | 5 ---- backend/updateProjects.sh | 8 +++--- docs/docs/contributors/backend_development.md | 8 +++--- docs/docs/contributors/document.md | 2 +- .../docs/contributors/frontend_development.md | 2 +- .../contributors/getting_started_local.md | 10 +++---- .../getting_started_production.md | 28 +++++++++---------- .../html/_sources/dev/how_to_document.rst.txt | 2 +- docs/docs/html/_sources/dev/howto.rst.txt | 2 +- .../html/_sources/dev/howtodocument.rst.txt | 2 +- docs/docs/html/_sources/howto.rst.txt | 2 +- frontend/src/components/App/App.tsx | 1 + frontend/src/components/Cart/Items.tsx | 10 ++++++- frontend/src/components/Cart/index.tsx | 4 +++ manage_metagrid.sh | 13 +++++---- metagrid_configs/metagrid_config | 22 --------------- traefik/Dockerfile | 2 +- 21 files changed, 71 insertions(+), 79 deletions(-) diff --git a/.gitignore b/.gitignore index 7e6c2c48e..7ac10759a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .pytest_cache/ # Config backups +metagrid_configs/metagrid_config metagrid_configs/backups ### Linux template diff --git a/backend/UpdateProjectData_README.txt b/backend/UpdateProjectData_README.txt index f7df37896..7e78b74c5 100644 --- a/backend/UpdateProjectData_README.txt +++ b/backend/UpdateProjectData_README.txt @@ -3,22 +3,24 @@ STEPS TO UPDATE PROJECTS, FACETS OR CATEGORIES 1. Edit the initial data file with the desired changes: metagrid/backend/metagrid/initial_projects_data.py 2. Change to the backend directory: cd metagrid/backend/ -3. Make sure the docker containers are up and running. If not running, you can run the backend container with this command: -docker-compose -f docker-compose.prod.yml up --build -d +3. Make sure the docker traefik and backend containers are up and running. +If not running, you can run the containers by going to the traefik directory first and running this command: +sudo docker compose -f docker-compose.prod.yml up --build -d +Then do the same in the backend directory (production backend depends on traefik) RUN UPDATE AND CLEAR TABLES If you need to clear tables to remove existing facets/projects or change their order then do the steps below: Option 1: 4. Use the updateProjects.sh script. Just run the script using clear option: -./updateProject.sh --clear +sudo ./updateProject.sh --clear DONE! Option 2: Manually update without the script and clear tables: -4. sudo docker-compose -f docker-compose.prod.yml build django # Build the container -5. docker-compose -f docker-compose.prod.yml run --rm django python manage.py migrate projects zero -6. docker-compose -f docker-compose.prod.yml run --rm django python manage.py migrate projects +4. sudo docker compose -f docker-compose.prod.yml build django # Build the container +5. sudo docker compose -f docker-compose.prod.yml run --rm django python manage.py migrate projects zero +6. sudo docker compose -f docker-compose.prod.yml run --rm django python manage.py migrate projects DONE! @@ -28,14 +30,14 @@ If there are deletions or more significant changes, run steps above which includ Option 1: 4. Use the updateProjects.sh script. Just run the default script: -./updateProject.sh +sudo ./updateProject.sh DONE! Option 2: Manually update without the script and don't clear tables: -4. sudo docker-compose -f docker-compose.prod.yml build django # Build the container -5. docker-compose -f docker-compose.prod.yml run --rm django python manage.py migrate --fake projects 0001_initial -6. docker-compose -f docker-compose.prod.yml run --rm django python manage.py migrate projects +4. sudo docker compose -f docker-compose.prod.yml build django # Build the container +5. sudo docker compose -f docker-compose.prod.yml run --rm django python manage.py migrate --fake projects 0001_initial +6. sudo docker compose -f docker-compose.prod.yml run --rm django python manage.py migrate projects DONE! diff --git a/backend/docker/production/postgres/maintenance/backup b/backend/docker/production/postgres/maintenance/backup index ee0c9d63c..f72304c05 100644 --- a/backend/docker/production/postgres/maintenance/backup +++ b/backend/docker/production/postgres/maintenance/backup @@ -4,7 +4,7 @@ ### Create a database backup. ### ### Usage: -### $ docker-compose -f .yml (exec |run --rm) postgres backup +### $ docker compose -f .yml (exec |run --rm) postgres backup set -o errexit diff --git a/backend/docker/production/postgres/maintenance/restore b/backend/docker/production/postgres/maintenance/restore index 9661ca7f1..c68f17d71 100644 --- a/backend/docker/production/postgres/maintenance/restore +++ b/backend/docker/production/postgres/maintenance/restore @@ -7,7 +7,7 @@ ### <1> filename of an existing backup. ### ### Usage: -### $ docker-compose -f .yml (exec |run --rm) postgres restore <1> +### $ docker compose -f .yml (exec |run --rm) postgres restore <1> set -o errexit diff --git a/backend/metagrid/cart/views.py b/backend/metagrid/cart/views.py index 1104953c2..e901ec961 100644 --- a/backend/metagrid/cart/views.py +++ b/backend/metagrid/cart/views.py @@ -18,11 +18,6 @@ def get_queryset(self): queryset = self.queryset.filter(user=user).prefetch_related() return queryset - def get_queryset_email(self): - user_email = self.request.user.get_email_field_name() - queryset = self.queryset.filter(user=user_email).prefetch_related() - return queryset - class SearchViewSet(viewsets.ModelViewSet): queryset = Search.objects.all().order_by("id") diff --git a/backend/updateProjects.sh b/backend/updateProjects.sh index d0072c23e..318ded895 100755 --- a/backend/updateProjects.sh +++ b/backend/updateProjects.sh @@ -25,7 +25,7 @@ elif [[ "$containerName" == "$prodPostgres" ]]; then echo "---PRODUCTION ENVIRONMENT UPDATE---" useSudo='sudo' echo "---REBUILDING DJANGO CONTAINER---" - sudo docker-compose -f $dockerCompose build django + sudo docker compose -f $dockerCompose build django echo "---DONE---" else echo "This script should be run when the backend containers are active." @@ -34,14 +34,14 @@ fi if [[ "$OPTION" == "$clearTables" ]]; then echo "---CLEARING EXISTING TABLES TO ALLOW DATA MIGRATION---" - $useSudo docker-compose -f $dockerCompose run --rm django python manage.py migrate projects zero + $useSudo docker compose -f $dockerCompose run --rm django python manage.py migrate projects zero echo "---DONE---" else echo "---UPDATING MIGRATION TABLE TO ALLOW DATA MIGRATION---" - $useSudo docker-compose -f $dockerCompose run --rm django python manage.py migrate --fake projects 0001_initial + $useSudo docker compose -f $dockerCompose run --rm django python manage.py migrate --fake projects 0001_initial echo "---DONE---" fi echo "---RUNNING MIGRATION UPDATE---" -$useSudo docker-compose -f $dockerCompose run --rm django python manage.py migrate projects +$useSudo docker compose -f $dockerCompose run --rm django python manage.py migrate projects echo "---DONE---" diff --git a/docs/docs/contributors/backend_development.md b/docs/docs/contributors/backend_development.md index fcbc5508c..c8ff3ca6d 100644 --- a/docs/docs/contributors/backend_development.md +++ b/docs/docs/contributors/backend_development.md @@ -119,7 +119,7 @@ Run the command to start an app ```bash cd metagrid -docker-compose -p metagrid_backend_dev run --rm django python manage.py startapp +docker compose -p metagrid_backend_dev run --rm django python manage.py startapp ``` Register the app under `INSTALLED_APPS` @@ -210,7 +210,7 @@ MetaGrid's back-end follows the [Black](https://black.readthedocs.io/en/stable/t Run a command inside the docker container: ```bash -docker-compose -p metagrid_backend_dev run --rm django [command] +docker compose -p metagrid_backend_dev run --rm django [command] ``` ### Django migrations @@ -278,9 +278,9 @@ To run the tests, check your test coverage, and generate an HTML coverage report ```bash # Optional, stop existing Django containers so tests can run without conflicts -docker-compose -f docker-compose.yml down +docker compose -f docker-compose.yml down # Runs the tests -docker-compose -p metagrid_backend_dev run --rm django pytest +docker compose -p metagrid_backend_dev run --rm django pytest ``` Note: Run commands above within the 'metagrid/backend' directory. diff --git a/docs/docs/contributors/document.md b/docs/docs/contributors/document.md index 3aebf08f0..f8b2a3bde 100644 --- a/docs/docs/contributors/document.md +++ b/docs/docs/contributors/document.md @@ -5,7 +5,7 @@ This project uses [MkDocs](https://www.mkdocs.org/) documentation generator. If you set up your project by walking through [Getting Started For Local Development](../getting_started_local), run the following command: cd docs - docker-compose -p metagrid_docs_dev up + docker compose -p metagrid_docs_dev up Navigate to port 8001 on your host to see the documentation site locally (e.g. [`localhost:8001`](http://localhost:8001/)). MkDocs supports hot-reloading, so changes to any of the `.md` files will reload the site. diff --git a/docs/docs/contributors/frontend_development.md b/docs/docs/contributors/frontend_development.md index 37f1db22e..358d86226 100644 --- a/docs/docs/contributors/frontend_development.md +++ b/docs/docs/contributors/frontend_development.md @@ -198,7 +198,7 @@ The MetaGrid front-end follows the Airbnb JavaScript and React/JSX style guides. Run a command inside the docker container: ```bash -docker-compose -p metagrid_frontend_dev run --rm react [command] +docker compose -p metagrid_frontend_dev run --rm react [command] ``` ### `yarn start:local` diff --git a/docs/docs/contributors/getting_started_local.md b/docs/docs/contributors/getting_started_local.md index 4b3c4cad2..25d37d576 100644 --- a/docs/docs/contributors/getting_started_local.md +++ b/docs/docs/contributors/getting_started_local.md @@ -106,7 +106,7 @@ Open the project in a terminal and `cd backend`. This can take a while, especially the first time you run this particular command on your development system but subsequent runs will occur quickly: ```bash -docker-compose -p metagrid_backend_dev up --build +docker compose -p metagrid_backend_dev up --build ``` ### 3.2 Additional Configuration @@ -141,21 +141,21 @@ This user will be used for logging into registered Keycloak clients, including t #### Addressing Keycloak Boot Issue -Keycloak has a known fatal issue where if it is interrupted during boot (stopping `docker-compose up` prematurely), the command that adds the admin user fails. +Keycloak has a known fatal issue where if it is interrupted during boot (stopping `docker compose up` prematurely), the command that adds the admin user fails. As a result, the Keycloak docker service will not start and outputs the error **_"User with username 'admin' already..."_**. If you run into this problem, follow these workaround steps: 1. Stop all back-end containers - `docker-compose -p metagrid_backend_dev down` + `docker compose -p metagrid_backend_dev down` 2. Comment out the two relevant lines (`./backend/.envs/.local/.keycloak`) - `#KEYCLOAK_USER: admin` - `#KEYCLOAK_PASSWORD: pass` 3. Rebuild and restart the containers - `docker-compose -p metagrid_backend_dev up --build` + `docker compose -p metagrid_backend_dev up --build` 4. Un-do commenting - `KEYCLOAK_USER: admin` - `KEYCLOAK_PASSWORD: pass` @@ -174,7 +174,7 @@ Open the project in a terminal and `cd frontend`. This can take a while, especially the first time you run this particular command on your development system but subsequent runs will occur quickly: ```bash -docker-compose -p metagrid_frontend_dev up --build +docker compose -p metagrid_frontend_dev up --build ``` ### 4.2 Accessible Services diff --git a/docs/docs/contributors/getting_started_production.md b/docs/docs/contributors/getting_started_production.md index b03a861b4..4a591c784 100644 --- a/docs/docs/contributors/getting_started_production.md +++ b/docs/docs/contributors/getting_started_production.md @@ -59,13 +59,13 @@ Once you've finished the configuration, you will be ready to start the service c Using the manage_metagrid.sh script you can start or stop all or specific docker containers by selecting the appropriate option in the menu. If you wish to start or stop a container manually, you need to go to the specific service directory, for example the frontend or backend, the run the command below: ```bash -docker-compose -f docker-compose.prod.yml up --build +docker compose -f docker-compose.prod.yml up --build ``` To run the stack and detach the containers, run: ```bash -docker-compose -f docker-compose.prod.yml up --build -d +docker compose -f docker-compose.prod.yml up --build -d ``` ## Post Build Steps @@ -96,7 +96,7 @@ You can read more about this feature and how to configure it, at [Automatic HTTP In production, you must apply Django migrations manually since they are not automatically applied to the database when you rebuild the docker-compose containers. To do so, with the backend docker container running, run the command below in the backend directory: ```bash -docker-compose -f docker-compose.prod.yml run --rm django python manage.py migrate +docker compose -f docker-compose.prod.yml run --rm django python manage.py migrate ``` NOTE: If this step is skipped, you may see issues loading the project drop-down and search table results. @@ -134,7 +134,7 @@ Otherwise if you wish to clear the tables and start fresh, then run: To run a command inside the docker container (front-end, backend, traefik) go to the appropriate directory and run: ```bash -docker-compose -f docker-compose.prod.yml run --rm django [command] +docker compose -f docker-compose.prod.yml run --rm django [command] ``` ##### Creating a Superuser @@ -142,7 +142,7 @@ docker-compose -f docker-compose.prod.yml run --rm django [command] With backend docker container running, run command below in the backend directory to create a superuser. Useful for logging into Django Admin page to manage the database. ```bash -docker-compose -f docker-compose.prod.yml run --rm django python manage.py createsuperuser +docker compose -f docker-compose.prod.yml run --rm django python manage.py createsuperuser ``` ### 4. Supervisor @@ -152,7 +152,7 @@ docker-compose -f docker-compose.prod.yml run --rm django python manage.py creat Once you are ready with your initial setup, you want to make sure that your application is run by a process manager to survive reboots and auto restarts in case of an error. -Although we recommend using Supervisor, you can use the process manager you are most familiar with. All it needs to do is to run `docker-compose -f production.yml up --build` for `traefik`, `backend`, and `frontend`. +Although we recommend using Supervisor, you can use the process manager you are most familiar with. All it needs to do is to run `docker compose -f production.yml up --build` for `traefik`, `backend`, and `frontend`. #### 4.1 Install Supervisor @@ -192,7 +192,7 @@ The directory for where to store the `.ini` files vary based on the OS: ```ini [program:metagrid-traefik] -command=docker-compose -f docker-compose.prod.yml up --build +command=docker compose -f docker-compose.prod.yml up --build directory=/home//metagrid/traefik redirect_stderr=true autostart=true @@ -204,7 +204,7 @@ priority=10 ```ini [program:metagrid-backend] -command=docker-compose -f docker-compose.prod.yml up --build +command=docker compose -f docker-compose.prod.yml up --build directory=/home//metagrid/backend redirect_stderr=true autostart=true @@ -216,7 +216,7 @@ priority=10 ```ini [program:metagrid-frontend] -command=docker-compose -f docker-compose.prod.yml up --build +command=docker compose -f docker-compose.prod.yml up --build directory=/home//metagrid/frontend redirect_stderr=true autostart=true @@ -258,15 +258,15 @@ Then either use the manage_metagrid.sh scripts to stop services, or you can go t ```bash cd ./backend # Shutting off backend service -docker-compose -f docker-compose.prod.yml down # Shut down the container +docker compose -f docker-compose.prod.yml down # Shut down the container cd ./frontend # Shutting off frontend service -docker-compose -f docker-compose.prod.yml down +docker compose -f docker-compose.prod.yml down ``` When you are ready to restore services, you can do so manually using docker-compose: ```bash -docker-compose -f docker-compose.prod.yml up --build # Start the container +docker compose -f docker-compose.prod.yml up --build # Start the container ``` Or let supervisor restore all: @@ -282,11 +282,11 @@ These commands can be run on any `docker-compose.prod.yml` file. ### Check logs ```bash -docker-compose -f docker-compose.prod.yml logs +docker compose -f docker-compose.prod.yml logs ``` ### Check status of containers ```bash -docker-compose -f docker-compose.prod.yml ps +docker compose -f docker-compose.prod.yml ps ``` diff --git a/docs/docs/html/_sources/dev/how_to_document.rst.txt b/docs/docs/html/_sources/dev/how_to_document.rst.txt index 14cc76f7d..aadf59168 100644 --- a/docs/docs/html/_sources/dev/how_to_document.rst.txt +++ b/docs/docs/html/_sources/dev/how_to_document.rst.txt @@ -12,7 +12,7 @@ Documentation can be written as rst files in the `metagrid/docs/_source`. To build and serve docs, use the commands: :: - docker-compose -f local.yml up docs + docker compose -f local.yml up docs Changes to files in `docs/_source` will be picked up and reloaded automatically. diff --git a/docs/docs/html/_sources/dev/howto.rst.txt b/docs/docs/html/_sources/dev/howto.rst.txt index 38756fa4b..2a561f478 100644 --- a/docs/docs/html/_sources/dev/howto.rst.txt +++ b/docs/docs/html/_sources/dev/howto.rst.txt @@ -10,7 +10,7 @@ Documentation can be written as rst files in the `metagrid/docs/_source`. To build and serve docs, use the commands: :: - docker-compose -f local.yml up docs + docker compose -f local.yml up docs Changes to files in `docs/_source` will be picked up and reloaded automatically. diff --git a/docs/docs/html/_sources/dev/howtodocument.rst.txt b/docs/docs/html/_sources/dev/howtodocument.rst.txt index 38756fa4b..2a561f478 100644 --- a/docs/docs/html/_sources/dev/howtodocument.rst.txt +++ b/docs/docs/html/_sources/dev/howtodocument.rst.txt @@ -10,7 +10,7 @@ Documentation can be written as rst files in the `metagrid/docs/_source`. To build and serve docs, use the commands: :: - docker-compose -f local.yml up docs + docker compose -f local.yml up docs Changes to files in `docs/_source` will be picked up and reloaded automatically. diff --git a/docs/docs/html/_sources/howto.rst.txt b/docs/docs/html/_sources/howto.rst.txt index 38756fa4b..2a561f478 100644 --- a/docs/docs/html/_sources/howto.rst.txt +++ b/docs/docs/html/_sources/howto.rst.txt @@ -10,7 +10,7 @@ Documentation can be written as rst files in the `metagrid/docs/_source`. To build and serve docs, use the commands: :: - docker-compose -f local.yml up docs + docker compose -f local.yml up docs Changes to files in `docs/_source` will be picked up and reloaded automatically. diff --git a/frontend/src/components/App/App.tsx b/frontend/src/components/App/App.tsx index d2867623a..dff8df4f2 100644 --- a/frontend/src/components/App/App.tsx +++ b/frontend/src/components/App/App.tsx @@ -604,6 +604,7 @@ const App: React.FC = ({ searchQuery }) => { onClearCart={handleClearCart} onRunSearchQuery={handleRunSearchQuery} onRemoveSearchQuery={handleRemoveSearchQuery} + nodeStatus={nodeStatus} /> } diff --git a/frontend/src/components/Cart/Items.tsx b/frontend/src/components/Cart/Items.tsx index c1f4fefc2..42c8cedd1 100644 --- a/frontend/src/components/Cart/Items.tsx +++ b/frontend/src/components/Cart/Items.tsx @@ -14,6 +14,7 @@ import { RawSearchResults } from '../Search/types'; import DatasetDownload from '../Globus/DatasetDownload'; import { saveSessionValue } from '../../api'; import CartStateKeys, { cartItemSelections } from './recoil/atoms'; +import { NodeStatusArray } from '../NodeStatus/types'; const styles: CSSinJS = { summary: { @@ -32,9 +33,15 @@ export type Props = { userCart: RawSearchResults | []; onUpdateCart: (item: RawSearchResults, operation: 'add' | 'remove') => void; onClearCart: () => void; + nodeStatus?: NodeStatusArray; }; -const Items: React.FC = ({ userCart, onUpdateCart, onClearCart }) => { +const Items: React.FC = ({ + userCart, + onUpdateCart, + onClearCart, + nodeStatus, +}) => { const [itemSelections, setItemSelections] = useRecoilState( cartItemSelections ); @@ -79,6 +86,7 @@ const Items: React.FC = ({ userCart, onUpdateCart, onClearCart }) => { void; onRunSearchQuery: (savedSearch: UserSearchQuery) => void; onRemoveSearchQuery: (uuid: string) => void; + nodeStatus?: NodeStatusArray; }; const Cart: React.FC = ({ @@ -24,6 +26,7 @@ const Cart: React.FC = ({ onClearCart, onRunSearchQuery, onRemoveSearchQuery, + nodeStatus, }) => { const [activeTab, setActiveTab] = React.useState('items'); const navigate = useNavigate(); @@ -58,6 +61,7 @@ const Cart: React.FC = ({ userCart={userCart} onUpdateCart={onUpdateCart} onClearCart={onClearCart} + nodeStatus={nodeStatus} /> diff --git a/manage_metagrid.sh b/manage_metagrid.sh index 747ef25bc..f013cc3d6 100755 --- a/manage_metagrid.sh +++ b/manage_metagrid.sh @@ -5,8 +5,11 @@ CONFIG_DIR=metagrid_configs BACKUP_DIR=$CONFIG_DIR/backups BACKUP_FORMAT=config_backup_$(date +'%F_%I-%M-%S') +set -e + #Custom functions function configure() { + mkdir -p $BACKUP_DIR cp $CONFIG_DIR/$METAGRID_CONFIG $BACKUP_DIR/$BACKUP_FORMAT $DEFAULT_EDITOR $CONFIG_DIR/$METAGRID_CONFIG saveConfigs $CONFIG_DIR/$METAGRID_CONFIG @@ -22,7 +25,7 @@ function saveConfigs() { function setCurrentConfig() { clear - fileCount=$(ls "$BACKUP_DIR"| wc -l) + fileCount=$(ls "$BACKUP_DIR" | wc -l) if [ "$fileCount" -lt "1" ]; then echo "There aren't any config files in the backup directory." read -p "Press enter to continue..." option @@ -56,7 +59,7 @@ function setCurrentConfig() { function startService() { echo "Starting $1" cd $1 - docker-compose -f docker-compose.prod.yml up --build $2 + docker compose -f docker-compose.prod.yml up --build $2 cd .. } @@ -64,7 +67,7 @@ function startService() { function stopService() { echo "Stopping $1" cd $1 - docker-compose -f docker-compose.prod.yml down --remove-orphans + docker compose -f docker-compose.prod.yml down --remove-orphans cd .. } @@ -73,7 +76,7 @@ function stopService() { function startLocalService() { echo "Starting $1" cd $1 - docker-compose -f docker-compose.yml up --build $2 + docker compose -f docker-compose.yml up --build $2 cd .. } @@ -81,7 +84,7 @@ function startLocalService() { function stopLocalService() { echo "Stopping $1" cd $1 - docker-compose -f docker-compose.yml down --remove-orphans + docker compose -f docker-compose.yml down --remove-orphans cd .. } diff --git a/metagrid_configs/metagrid_config b/metagrid_configs/metagrid_config index 683878c55..ed5f073e3 100644 --- a/metagrid_configs/metagrid_config +++ b/metagrid_configs/metagrid_config @@ -8,28 +8,23 @@ DOMAIN_SUBDIRECTORY=metagrid-backend # =====================BACKEND CONFIG==================== # General - DJANGO_SETTINGS_MODULE=config.settings.production DJANGO_SECRET_KEY= DJANGO_ADMIN_URL= DJANGO_ALLOWED_HOSTS= # Security - DJANGO_SECURE_SSL_REDIRECT=False # django-cors-headers - CORS_ORIGIN_WHITELIST=https://localhost:3000 # django-all-auth - KEYCLOAK_URL= KEYCLOAK_REALM= KEYCLOAK_CLIENT_ID= # postgress - POSTGRES_HOST=postgres POSTGRES_PORT=5432 POSTGRES_DB=postgres @@ -39,60 +34,43 @@ POSTGRES_PASSWORD=postgres # =====================FRONTEND CONFIG==================== # Redirect the frontend to home page when old subdirectory is used (optional) - REACT_APP_PREVIOUS_URL= # MetaGrid API - # https://github.com/aims-group/metagrid/tree/master/backend - REACT_APP_METAGRID_API_URL= # Globus - REACT_APP_GLOBUS_REDIRECT=http://localhost:3000/cart/items REACT_APP_CLIENT_ID=7fa7ac4a-a051-4b26-836f-b292b5c5b268 REACT_APP_GLOBUS_NODES= # ESGF wget API - # https://github.com/ESGF/esgf-wget - REACT_APP_WGET_API_URL= # ESGF Search API - # https://esgf.github.io/esg-search/ESGF_Search_RESTful_API.html - REACT_APP_ESGF_NODE_URL=https://esgf-fedtest.llnl.gov/esg-search/search # https://esgf.github.io/esg-search/ESGF_Search_RESTful_API.html - REACT_APP_SEARCH_URL= # ESGF Node Status API - # https://github.com/ESGF/esgf-utils/blob/master/node_status/query_prom.py - REACT_APP_ESGF_NODE_STATUS_URL= # Keycloak - # https://github.com/keycloak/keycloak - REACT_APP_KEYCLOAK_URL= REACT_APP_KEYCLOAK_REALM= REACT_APP_KEYCLOAK_CLIENT_ID= # react-hotjar - # https://github.com/abdalla/react-hotjar - REACT_APP_HOTJAR_ID= REACT_APP_HOTJAR_SV= # react-ga - # https://github.com/react-ga/react-ga - REACT_APP_GOOGLE_ANALYTICS_TRACKING_ID= diff --git a/traefik/Dockerfile b/traefik/Dockerfile index eae4199b3..d5b7d24a6 100644 --- a/traefik/Dockerfile +++ b/traefik/Dockerfile @@ -1,4 +1,4 @@ -FROM traefik:2.8 +FROM traefik:2.10 RUN apk add gettext RUN mkdir -p /etc/traefik/acme From 2eae86b3faee9e1a41ad438af9aa8f85b342e541 Mon Sep 17 00:00:00 2001 From: Sasha Ames Date: Fri, 21 Jul 2023 19:10:11 -0700 Subject: [PATCH 50/65] Nodes link and Django, packages upgrade (#540) * trying to add the link * Adjusted location of the federated nodes link and added the link to the ESGF logo as well. * Updated django to latest version, updated the test backend.tml to include the new SOLR_URL setting. * Updated postgres library * Updated psycopg-binary to see if tests will pass * update link hostname * Updated psycopg to include pyscopg 3 * Upgraded various backend packages and downgraded psycopg3 to psycopg2 * Updated the postgres version used in tessts, to match the current postress version being used in production and local. --------- Co-authored-by: Carlos Downie <42552189+downiec@users.noreply.github.com> --- .github/workflows/backend.yml | 3 ++- backend/docker/local/django/Dockerfile | 2 +- backend/docker/production/django/Dockerfile | 2 +- backend/requirements/base.txt | 10 +++++----- backend/requirements/local.txt | 17 +++++++++-------- frontend/src/components/NavBar/LeftMenu.tsx | 4 ++-- frontend/src/components/NavBar/NavBar.css | 1 + frontend/src/components/NavBar/index.tsx | 13 ++++++++++--- 8 files changed, 31 insertions(+), 21 deletions(-) diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml index d50c078a2..7904b33b2 100644 --- a/.github/workflows/backend.yml +++ b/.github/workflows/backend.yml @@ -20,6 +20,7 @@ env: REACT_APP_SEARCH_URL: https://esgf-node.llnl.gov/esg-search/search REACT_APP_WGET_API_URL: https://esgf-node.llnl.gov/esg-search/wget REACT_APP_ESGF_NODE_STATUS_URL: https://aims2.llnl.gov/metagrid-backend/proxy/status + REACT_APP_ESGF_SOLR_URL: https://esgf-fedtest.llnl.gov/solr jobs: build: @@ -27,7 +28,7 @@ jobs: services: postgres: - image: postgres:11.6 + image: postgres:12.6 env: POSTGRES_PASSWORD: postgres ports: diff --git a/backend/docker/local/django/Dockerfile b/backend/docker/local/django/Dockerfile index 9ba91b0f8..402b20c2c 100644 --- a/backend/docker/local/django/Dockerfile +++ b/backend/docker/local/django/Dockerfile @@ -16,7 +16,7 @@ RUN apt-get update \ # Requirements are installed here to ensure they will be cached. COPY ./requirements /requirements -RUN pip install -r /requirements/local.txt +RUN pip3 install -r /requirements/local.txt COPY ./docker/production/django/entrypoint /entrypoint RUN sed -i 's/\r$//g' /entrypoint diff --git a/backend/docker/production/django/Dockerfile b/backend/docker/production/django/Dockerfile index 4496579f0..db4130860 100644 --- a/backend/docker/production/django/Dockerfile +++ b/backend/docker/production/django/Dockerfile @@ -18,7 +18,7 @@ RUN addgroup --system django \ # Requirements are installed here to ensure they will be cached. COPY ./requirements /requirements -RUN pip install --no-cache-dir -r /requirements/production.txt \ +RUN pip3 install --no-cache-dir -r /requirements/production.txt \ && rm -rf /requirements COPY ./docker/production/django/entrypoint /entrypoint diff --git a/backend/requirements/base.txt b/backend/requirements/base.txt index 3f965e1e0..5a6b2372f 100644 --- a/backend/requirements/base.txt +++ b/backend/requirements/base.txt @@ -1,17 +1,17 @@ # Core # ------------------------------------------------------------------------------ -pytz==2022.7.1 # https://github.com/stub42/pytz -django==4.1.9 # https://www.djangoproject.com/ -django-environ==0.9.0 # https://github.com/joke2k/django-environ +pytz==2023.3 # https://github.com/stub42/pytz +django==4.2.3 # https://www.djangoproject.com/ +django-environ==0.10.0 # https://github.com/joke2k/django-environ gunicorn==20.1.0 # https://github.com/benoitc/gunicorn -newrelic==8.7.0 # https://pypi.org/project/newrelic/ +newrelic==8.8.1 # https://pypi.org/project/newrelic/ argon2-cffi==21.3 # https://github.com/hynek/argon2_cffi requests==2.31.0 # https://github.com/psf/requests whitenoise==6.4.0 # https://github.com/evansd/whitenoise # Database # ------------------------------------------------------------------------------ -psycopg2-binary==2.9.5 # https://github.com/psycopg/psycopg2 +psycopg2-binary==2.9.6 # https://github.com/psycopg/psycopg2 # REST API # ------------------------------------------------------------------------------ diff --git a/backend/requirements/local.txt b/backend/requirements/local.txt index 5bf32df78..48cf122f2 100644 --- a/backend/requirements/local.txt +++ b/backend/requirements/local.txt @@ -4,21 +4,22 @@ # ------------------------------------------------------------------------------ flake8==6.0.0 # https://github.com/PyCQA/flake8 flake8-isort==6.0.0 # https://github.com/gforcada/flake8-isort -black==23.1.0 # https://github.com/ambv/black -mypy==1.0.1 # https://github.com/python/mypy +black==23.7.0 # https://github.com/ambv/black +mypy==1.4.1 # https://github.com/python/mypy +mypy-extensions==1.0.0 # https://pypi.org/project/mypy-extensions/ # Testing # ------------------------------------------------------------------------------ -django-stubs==1.15.0 # https://github.com/typeddjango/django-stubs -pytest==7.2.1 # https://github.com/pytest-dev/pytest -pytest-cov==4.0.0 # https://github.com/pytest-dev/pytest-cov +django-stubs==4.2.3 # https://github.com/typeddjango/django-stubs +pytest==7.3.2 # https://github.com/pytest-dev/pytest +pytest-cov==4.1.0 # https://github.com/pytest-dev/pytest-cov pytest-django==4.5.2 # https://github.com/pytest-dev/pytest-django -pytest-sugar==0.9.6 # https://github.com/Frozenball/pytest-sugar +pytest-sugar==0.9.7 # https://github.com/Frozenball/pytest-sugar pytest-watch==4.2.0 # https://github.com/joeyespo/pytest-watch factory-boy==3.2.1 # https://github.com/FactoryBoy/factory_boy # Developer Tools # ------------------------------------------------------------------------------ -ipdb==0.13.11 # https://github.com/gotcha/ipdb +ipdb==0.13.13 # https://github.com/gotcha/ipdb ipython==8.11.0 # https://github.com/ipython/ipython -django-extensions==3.2.1 # https://github.com/django-extensions/django-extensions +django-extensions==3.2.3 # https://github.com/django-extensions/django-extensions diff --git a/frontend/src/components/NavBar/LeftMenu.tsx b/frontend/src/components/NavBar/LeftMenu.tsx index e7dbc4db2..97b644d6b 100644 --- a/frontend/src/components/NavBar/LeftMenu.tsx +++ b/frontend/src/components/NavBar/LeftMenu.tsx @@ -94,10 +94,10 @@ const LeftMenu: React.FC = ({ setText(e.target.value)} placeholder="Search for a keyword" diff --git a/frontend/src/components/NavBar/NavBar.css b/frontend/src/components/NavBar/NavBar.css index 9b2fe015b..3462594a1 100644 --- a/frontend/src/components/NavBar/NavBar.css +++ b/frontend/src/components/NavBar/NavBar.css @@ -10,6 +10,7 @@ .navbar-logo { width: 128px; float: left; + margin-left: 10px; } .navbar-container .navbar-left { diff --git a/frontend/src/components/NavBar/index.tsx b/frontend/src/components/NavBar/index.tsx index d0ae68d7d..fcbf55974 100644 --- a/frontend/src/components/NavBar/index.tsx +++ b/frontend/src/components/NavBar/index.tsx @@ -30,13 +30,20 @@ const NavBar: React.FC = ({ return (