From a7cb97dafaf372357747d27f4c252922b43c7612 Mon Sep 17 00:00:00 2001 From: David Glymph Date: Tue, 8 Apr 2025 11:02:51 -0400 Subject: [PATCH 1/9] add new page for enriched queries --- src/pages/EnrichedQueries.jsx | 21 ++++++++++++++++++ src/pages/explore/Explore.jsx | 41 ++++++++++++++++++++++++++--------- 2 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 src/pages/EnrichedQueries.jsx diff --git a/src/pages/EnrichedQueries.jsx b/src/pages/EnrichedQueries.jsx new file mode 100644 index 00000000..7b4a6652 --- /dev/null +++ b/src/pages/EnrichedQueries.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { Col, Grid, Row } from 'react-bootstrap'; +import { Link } from 'react-router-dom'; + +export default function EnrichedQueries() { + return ( + + + + + ← View all tools +

Enrichment Analysis

+
+ Tool +
+
+ +
+
+ ); +} diff --git a/src/pages/explore/Explore.jsx b/src/pages/explore/Explore.jsx index 90944cb4..ea21d886 100644 --- a/src/pages/explore/Explore.jsx +++ b/src/pages/explore/Explore.jsx @@ -6,6 +6,7 @@ import { Switch, Route, Link, useRouteMatch, } from 'react-router-dom'; import DrugChemicalPairs from './DrugDiseasePairs'; +import EnrichedQueries from '../EnrichedQueries'; export default function Explore() { const match = useRouteMatch(); @@ -15,6 +16,9 @@ export default function Explore() { + + + @@ -31,20 +35,37 @@ function Index() {

Explore

- Click a link below to view a curated dataset that can be further explored in the ROBOKOP query builder or answer explorer. + Click a link below to view a tool or a curated dataset that can be further explored in the ROBOKOP query builder or answer explorer.


- - Drug to Disease Pairs - -

- These drug-disease pairs were generated using a machine learning model to align with the nodes - in the ROBOKOP knowledge graph. They highlight potential associations between various drugs and - a broad range of diseases, suggesting possible avenues for further research. These connections - can serve as a starting point for a new query by hovering over a pair and clicking “Start a Query”. -

+
+ + Enrichment Analysis{' '} + + Tool + + +

+ This tool allows you to query the ROBOKOP knowledge graph using a list of nodes. +

+
+ +
+ + Drug to Disease Pairs + +

+ These drug-disease pairs were generated using a machine learning model to align with the nodes + in the ROBOKOP knowledge graph. They highlight potential associations between various drugs and + a broad range of diseases, suggesting possible avenues for further research. These connections + can serve as a starting point for a new query by hovering over a pair and clicking “Start a Query”. +

+
From 83e36c35ccc38123e75e402ce7cb64fdc66a2f1a Mon Sep 17 00:00:00 2001 From: David Glymph Date: Tue, 29 Apr 2025 04:02:54 -0400 Subject: [PATCH 2/9] add initial page --- src/pages/{ => EnrichedQueries}/EnrichedQueries.jsx | 11 +++++++++-- src/pages/explore/Explore.jsx | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) rename src/pages/{ => EnrichedQueries}/EnrichedQueries.jsx (51%) diff --git a/src/pages/EnrichedQueries.jsx b/src/pages/EnrichedQueries/EnrichedQueries.jsx similarity index 51% rename from src/pages/EnrichedQueries.jsx rename to src/pages/EnrichedQueries/EnrichedQueries.jsx index 7b4a6652..f5241c84 100644 --- a/src/pages/EnrichedQueries.jsx +++ b/src/pages/EnrichedQueries/EnrichedQueries.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { Col, Grid, Row } from 'react-bootstrap'; import { Link } from 'react-router-dom'; +import { Button } from '@material-ui/core'; export default function EnrichedQueries() { return ( @@ -10,8 +11,14 @@ export default function EnrichedQueries() { ← View all tools

Enrichment Analysis

-
- Tool +

+ This tool helps discover common connections between nodes. Given a list of input nodes, a relationship, and an output type, it will return a list of nodes that are best connected to the input nodes via the relationship. +

+ +
+
+ +
diff --git a/src/pages/explore/Explore.jsx b/src/pages/explore/Explore.jsx index ea21d886..a87906bb 100644 --- a/src/pages/explore/Explore.jsx +++ b/src/pages/explore/Explore.jsx @@ -6,7 +6,7 @@ import { Switch, Route, Link, useRouteMatch, } from 'react-router-dom'; import DrugChemicalPairs from './DrugDiseasePairs'; -import EnrichedQueries from '../EnrichedQueries'; +import EnrichedQueries from '../EnrichedQueries/EnrichedQueries'; export default function Explore() { const match = useRouteMatch(); From 28f6e3f0982a39621d3e9d13d6410f8c5cec4f62 Mon Sep 17 00:00:00 2001 From: David Glymph Date: Tue, 29 Apr 2025 04:31:20 -0400 Subject: [PATCH 3/9] migrate enriched queries code from other repo --- package-lock.json | 723 +++++++++--------- package.json | 8 +- src/hooks/use-query.js | 144 ++++ src/pages/EnrichedQueries/EnrichedQueries.jsx | 102 ++- src/pages/EnrichedQueries/NodeInputBox.jsx | 303 ++++++++ src/pages/EnrichedQueries/Select.jsx | 45 ++ src/pages/EnrichedQueries/name-resolver.js | 23 + 7 files changed, 968 insertions(+), 380 deletions(-) create mode 100644 src/hooks/use-query.js create mode 100644 src/pages/EnrichedQueries/NodeInputBox.jsx create mode 100644 src/pages/EnrichedQueries/Select.jsx create mode 100644 src/pages/EnrichedQueries/name-resolver.js diff --git a/package-lock.json b/package-lock.json index 2f9eed3b..42485805 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@auth0/auth0-react": "^1.6.0", + "@floating-ui/react": "^0.27.8", "@material-ui/core": "^4.11.0", "@material-ui/data-grid": "^4.0.0-alpha.2", "@material-ui/icons": "^4.9.1", @@ -17,7 +18,7 @@ "@tanstack/react-table": "^8.20.5", "ag-grid-community": "^23.2.1", "ag-grid-react": "^23.2.1", - "axios": "^0.21.1", + "axios": "^0.22.0", "axios-retry": "^3.1.8", "body-parser": "^1.20.0", "bootstrap": "^3.4.1", @@ -36,10 +37,10 @@ "path": "^0.12.7", "rc-slider": "^9.3.1", "rc-tooltip": "^4.2.1", - "react": "^16.13.1", + "react": "^17.0.2", "react-bootstrap": "^0.33.1", "react-bootstrap-dialog": "^0.13.0", - "react-dom": "^16.13.1", + "react-dom": "^17.0.2", "react-dropzone": "^11.0.2", "react-graph-vis": "^1.0.5", "react-icons": "^3.10.0", @@ -53,6 +54,7 @@ "react-virtualized": "^9.21.2", "react-widgets": "^4.5.0", "regenerator-runtime": "^0.13.7", + "rich-textarea": "^0.26.4", "shortid": "^2.2.15", "slugify": "^1.4.5", "sqlite3": "^5.1.7" @@ -3848,11 +3850,15 @@ } }, "node_modules/@babel/runtime": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.5.tgz", - "integrity": "sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "license": "MIT", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/runtime-corejs2": { @@ -3893,9 +3899,10 @@ "dev": true }, "node_modules/@babel/runtime/node_modules/regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" }, "node_modules/@babel/template": { "version": "7.10.4", @@ -4053,6 +4060,59 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" }, + "node_modules/@floating-ui/core": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", + "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.8.tgz", + "integrity": "sha512-EQJ4Th328y2wyHR3KzOUOoTW2UKjFk53fmyahfwExnFQ8vnsMYqKc+fFPOkeYtj5tcp1DUMiNJ7BFhed7e9ONw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.9", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -5450,46 +5510,6 @@ } } }, - "node_modules/@material-ui/core/node_modules/csstype": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.2.tgz", - "integrity": "sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw==" - }, - "node_modules/@material-ui/core/node_modules/dom-helpers": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", - "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/@material-ui/core/node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/@material-ui/core/node_modules/react-transition-group": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", - "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, "node_modules/@material-ui/data-grid": { "version": "4.0.0-alpha.2", "resolved": "https://registry.npmjs.org/@material-ui/data-grid/-/data-grid-4.0.0-alpha.2.tgz", @@ -5562,26 +5582,27 @@ } }, "node_modules/@material-ui/styles": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.10.0.tgz", - "integrity": "sha512-XPwiVTpd3rlnbfrgtEJ1eJJdFCXZkHxy8TrdieaTvwxNYj42VnnCyFzxYeNW9Lhj4V1oD8YtQ6S5Gie7bZDf7Q==", + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", + "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", "deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.4.4", "@emotion/hash": "^0.8.0", - "@material-ui/types": "^5.1.0", - "@material-ui/utils": "^4.9.6", + "@material-ui/types": "5.1.0", + "@material-ui/utils": "^4.11.3", "clsx": "^1.0.4", "csstype": "^2.5.2", "hoist-non-react-statics": "^3.3.2", - "jss": "^10.0.3", - "jss-plugin-camel-case": "^10.0.3", - "jss-plugin-default-unit": "^10.0.3", - "jss-plugin-global": "^10.0.3", - "jss-plugin-nested": "^10.0.3", - "jss-plugin-props-sort": "^10.0.3", - "jss-plugin-rule-value-function": "^10.0.3", - "jss-plugin-vendor-prefixer": "^10.0.3", + "jss": "^10.5.1", + "jss-plugin-camel-case": "^10.5.1", + "jss-plugin-default-unit": "^10.5.1", + "jss-plugin-global": "^10.5.1", + "jss-plugin-nested": "^10.5.1", + "jss-plugin-props-sort": "^10.5.1", + "jss-plugin-rule-value-function": "^10.5.1", + "jss-plugin-vendor-prefixer": "^10.5.1", "prop-types": "^15.7.2" }, "engines": { @@ -5592,9 +5613,9 @@ "url": "https://opencollective.com/material-ui" }, "peerDependencies": { - "@types/react": "^16.8.6", - "react": "^16.8.0", - "react-dom": "^16.8.0" + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -5603,13 +5624,13 @@ } }, "node_modules/@material-ui/system": { - "version": "4.9.14", - "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.14.tgz", - "integrity": "sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==", - "deprecated": "You can now upgrade to @mui/system. See the guide: https://mui.com/guides/migration-v4/", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", + "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.4.4", - "@material-ui/utils": "^4.9.6", + "@material-ui/utils": "^4.11.3", "csstype": "^2.5.2", "prop-types": "^15.7.2" }, @@ -5621,9 +5642,9 @@ "url": "https://opencollective.com/material-ui" }, "peerDependencies": { - "@types/react": "^16.8.6", - "react": "^16.8.0", - "react-dom": "^16.8.0" + "@types/react": "^16.8.6 || ^17.0.0", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -5645,20 +5666,21 @@ } }, "node_modules/@material-ui/utils": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.10.2.tgz", - "integrity": "sha512-eg29v74P7W5r6a4tWWDAAfZldXIzfyO1am2fIsC39hdUUHm/33k6pGOKPbgDjg/U/4ifmgAePy/1OjkKN6rFRw==", + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", + "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.4.4", "prop-types": "^15.7.2", - "react-is": "^16.8.0" + "react-is": "^16.8.0 || ^17.0.0" }, "engines": { "node": ">=8.0.0" }, "peerDependencies": { - "react": "^16.8.0", - "react-dom": "^16.8.0" + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" } }, "node_modules/@mswjs/cookies": { @@ -6183,36 +6205,6 @@ "react-test-renderer": ">=16.9.0" } }, - "node_modules/@testing-library/react-hooks/node_modules/@babel/runtime": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", - "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.4" - } - }, - "node_modules/@testing-library/react-hooks/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, - "node_modules/@testing-library/react/node_modules/@babel/runtime": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", - "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.4" - } - }, - "node_modules/@testing-library/react/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@testing-library/user-event": { "version": "13.2.1", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.2.1.tgz", @@ -6229,18 +6221,6 @@ "@testing-library/dom": ">=7.21.4" } }, - "node_modules/@testing-library/user-event/node_modules/@babel/runtime": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", - "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -7906,11 +7886,12 @@ } }, "node_modules/axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.22.0.tgz", + "integrity": "sha512-Z0U3uhqQeg1oNcihswf4ZD57O3NrR1+ZXhxaROaWpDmsDTx7T2HNBV2ulBtie2hwJptu8UvgnJoK+BIqdzh/1w==", + "license": "MIT", "dependencies": { - "follow-redirects": "^1.10.0" + "follow-redirects": "^1.14.4" } }, "node_modules/axios-retry": { @@ -7921,25 +7902,6 @@ "is-retry-allowed": "^1.1.0" } }, - "node_modules/axios/node_modules/follow-redirects": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz", - "integrity": "sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -10246,6 +10208,7 @@ "version": "2.0.8", "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.3", "is-in-browser": "^1.0.2" @@ -11081,9 +11044,10 @@ "dev": true }, "node_modules/dom-align": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.0.tgz", - "integrity": "sha512-YkoezQuhp3SLFGdOlr5xkqZ640iXrnHAwVYcDg8ZKRUtO7mSzSC2BA5V0VuyAwPSJA4CLIc6EDDJh4bEsD2+zA==" + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz", + "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==", + "license": "MIT" }, "node_modules/dom-converter": { "version": "0.2.0", @@ -12137,18 +12101,6 @@ "node": ">=10" } }, - "node_modules/eslint-plugin-jest-dom/node_modules/@testing-library/dom/node_modules/@babel/runtime": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", - "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/eslint-plugin-jest-dom/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -13663,24 +13615,23 @@ } }, "node_modules/follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "dev": true, - "dependencies": { - "debug": "=3.1.0" - }, + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", "engines": { "node": ">=4.0" - } - }, - "node_modules/follow-redirects/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "dependencies": { - "ms": "2.0.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, "node_modules/for-in": { @@ -14657,9 +14608,10 @@ } }, "node_modules/hyphenate-style-name": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", - "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", + "license": "BSD-3-Clause" }, "node_modules/iconv-lite": { "version": "0.4.24", @@ -15439,7 +15391,8 @@ "node_modules/is-in-browser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", - "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=" + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==", + "license": "MIT" }, "node_modules/is-installed-globally": { "version": "0.3.2", @@ -20112,12 +20065,13 @@ } }, "node_modules/jss": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/jss/-/jss-10.3.0.tgz", - "integrity": "sha512-B5sTRW9B6uHaUVzSo9YiMEOEp3UX8lWevU0Fsv+xtRnsShmgCfIYX44bTH8bPJe6LQKqEXku3ulKuHLbxBS97Q==", + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", + "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.1", - "csstype": "^2.6.5", + "csstype": "^3.0.2", "is-in-browser": "^1.1.3", "tiny-warning": "^1.0.2" }, @@ -20127,72 +20081,85 @@ } }, "node_modules/jss-plugin-camel-case": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.3.0.tgz", - "integrity": "sha512-tadWRi/SLWqLK3EUZEdDNJL71F3ST93Zrl9JYMjV0QDqKPAl0Liue81q7m/nFUpnSTXczbKDy4wq8rI8o7WFqA==", + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", + "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.1", "hyphenate-style-name": "^1.0.3", - "jss": "^10.3.0" + "jss": "10.10.0" } }, "node_modules/jss-plugin-default-unit": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.3.0.tgz", - "integrity": "sha512-tT5KkIXAsZOSS9WDSe8m8lEHIjoEOj4Pr0WrG0WZZsMXZ1mVLFCSsD2jdWarQWDaRNyMj/I4d7czRRObhOxSuw==", + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", + "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.1", - "jss": "^10.3.0" + "jss": "10.10.0" } }, "node_modules/jss-plugin-global": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.3.0.tgz", - "integrity": "sha512-etYTG/y3qIR/vxZnKY+J3wXwObyBDNhBiB3l/EW9/pE3WHE//BZdK8LFvQcrCO48sZW1Z6paHo6klxUPP7WbzA==", + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", + "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.1", - "jss": "^10.3.0" + "jss": "10.10.0" } }, "node_modules/jss-plugin-nested": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.3.0.tgz", - "integrity": "sha512-qWiEkoXNEkkZ+FZrWmUGpf+zBsnEOmKXhkjNX85/ZfWhH9dfGxUCKuJFuOWFM+rjQfxV4csfesq4hY0jk8Qt0w==", + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", + "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.1", - "jss": "^10.3.0", + "jss": "10.10.0", "tiny-warning": "^1.0.2" } }, "node_modules/jss-plugin-props-sort": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.3.0.tgz", - "integrity": "sha512-boetORqL/lfd7BWeFD3K+IyPqyIC+l3CRrdZr+NPq7Noqp+xyg/0MR7QisgzpxCEulk+j2CRcEUoZsvgPC4nTg==", + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", + "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.1", - "jss": "^10.3.0" + "jss": "10.10.0" } }, "node_modules/jss-plugin-rule-value-function": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.3.0.tgz", - "integrity": "sha512-7WiMrKIHH3rwxTuJki9+7nY11r1UXqaUZRhHvqTD4/ZE+SVhvtD5Tx21ivNxotwUSleucA/8boX+NF21oXzr5Q==", + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", + "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.1", - "jss": "^10.3.0", + "jss": "10.10.0", "tiny-warning": "^1.0.2" } }, "node_modules/jss-plugin-vendor-prefixer": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.3.0.tgz", - "integrity": "sha512-sZQbrcZyP5V0ADjCLwUA1spVWoaZvM7XZ+2fSeieZFBj31cRsnV7X70FFDerMHeiHAXKWzYek+67nMDjhrZAVQ==", + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", + "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.1", "css-vendor": "^2.0.8", - "jss": "^10.3.0" + "jss": "10.10.0" } }, + "node_modules/jss/node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, "node_modules/jsx-ast-utils": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", @@ -20813,11 +20780,12 @@ } }, "node_modules/loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "dependencies": { - "js-tokens": "^3.0.0" + "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" @@ -21421,17 +21389,18 @@ } }, "node_modules/mini-create-react-context": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz", - "integrity": "sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", + "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.5.5", + "@babel/runtime": "^7.12.1", "tiny-warning": "^1.0.3" }, "peerDependencies": { "prop-types": "^15.0.0", - "react": "^0.14.0 || ^15.0.0 || ^16.0.0" + "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/minimalistic-assert": { @@ -21647,15 +21616,6 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "license": "MIT" }, - "node_modules/moment": { - "version": "2.27.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", - "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==", - "peer": true, - "engines": { - "node": "*" - } - }, "node_modules/morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", @@ -25100,17 +25060,6 @@ "loose-envify": "^1.0.0" } }, - "node_modules/prop-types/node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -25391,19 +25340,20 @@ } }, "node_modules/rc-align": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-4.0.1.tgz", - "integrity": "sha512-RQ5Fhxl0LW+zsxbY8dxAcpXdaHkHH2jzRSSpvBTS7G9LMK3T+WRcn4ovjg/eqAESM6TdTx0hfqWF2S1pO75jxQ==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-4.0.15.tgz", + "integrity": "sha512-wqJtVH60pka/nOX7/IspElA8gjPNQKIx/ZqJ6heATCkXpe1Zg4cPVrMD2vC96wjsFFL8WsmhPbx9tdMo1qqlIA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.10.1", "classnames": "2.x", "dom-align": "^1.7.0", - "rc-util": "^5.0.1", + "rc-util": "^5.26.0", "resize-observer-polyfill": "^1.5.1" }, "peerDependencies": { - "react": "*", - "react-dom": "*" + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, "node_modules/rc-animate": { @@ -25461,14 +25411,25 @@ } }, "node_modules/rc-util": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.0.5.tgz", - "integrity": "sha512-zLIdNm6qz+hQbB5T1fmzHFFgPuRl3uB2eS2iLR/mewUWvgC3l7NzRYRVlHoCEEFVUkKEEsHuJXG1J52FInl5lA==", + "version": "5.44.4", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz", + "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==", + "license": "MIT", "dependencies": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, + "node_modules/rc-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -25478,13 +25439,13 @@ } }, "node_modules/react": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", - "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "object-assign": "^4.1.1" }, "engines": { "node": ">=0.10.0" @@ -25537,6 +25498,22 @@ "react-bootstrap": "*" } }, + "node_modules/react-bootstrap/node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "license": "BSD-3-Clause", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, "node_modules/react-bootstrap/node_modules/uncontrollable": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.1.1.tgz", @@ -25555,6 +25532,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/react-component-managers/-/react-component-managers-3.2.2.tgz", "integrity": "sha512-SqtB09hS1ir0koBNybvNbNAB3k/r7IbIGbXSxvkkTV0m50s+4oJ59KYsbPAQ/2DhE169Rc5V26d674EcGcDbGA==", + "license": "MIT", "dependencies": { "prop-types": "^15.6.1", "spy-on-component": "^1.1.0" @@ -25565,17 +25543,27 @@ } }, "node_modules/react-dom": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", - "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" + "scheduler": "^0.20.2" }, "peerDependencies": { - "react": "^16.13.1" + "react": "17.0.2" + } + }, + "node_modules/react-dom/node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" } }, "node_modules/react-dropzone": { @@ -25626,17 +25614,6 @@ "react": "*" } }, - "node_modules/react-input-autosize": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.2.tgz", - "integrity": "sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw==", - "dependencies": { - "prop-types": "^15.5.8" - }, - "peerDependencies": { - "react": "^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0" - } - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -25657,6 +25634,18 @@ "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" } }, + "node_modules/react-json-view/node_modules/react-textarea-autosize": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz", + "integrity": "sha512-F6bI1dgib6fSvG8so1HuArPUv+iVEfPliuLWusLF+gAKz0FbB4jLrWUrTAeq1afnPT2c9toEZYUdz/y1uKMy4A==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.6.0" + }, + "peerDependencies": { + "react": ">=0.14.0 <17.0.0" + } + }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -25676,9 +25665,10 @@ } }, "node_modules/react-overlays": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.9.1.tgz", - "integrity": "sha512-b0asy/zHtRd0i2+2/uNxe3YVprF3bRT1guyr791DORjCzE/HSBMog+ul83CdtKQ1kZ+pLnxWCu5W3BMysFhHdQ==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.9.3.tgz", + "integrity": "sha512-u2T7nOLnK+Hrntho4p0Nxh+BsJl0bl4Xuwj/Y0a56xywLMetgAfyjnDVrudLXsNcKGaspoC+t3C1V80W9QQTdQ==", + "license": "MIT", "dependencies": { "classnames": "^2.2.5", "dom-helpers": "^3.2.1", @@ -25692,6 +25682,22 @@ "react-dom": ">=16.3.0" } }, + "node_modules/react-overlays/node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "license": "BSD-3-Clause", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, "node_modules/react-prop-types": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz", @@ -25781,39 +25787,16 @@ "react-dom": "^16.8.0" } }, - "node_modules/react-select/node_modules/dom-helpers": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.4.tgz", - "integrity": "sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^2.6.7" - } - }, - "node_modules/react-select/node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/react-select/node_modules/react-transition-group": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", - "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "node_modules/react-select/node_modules/react-input-autosize": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.2.tgz", + "integrity": "sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" + "prop-types": "^15.5.8" }, "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" + "react": "^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0" } }, "node_modules/react-split-pane": { @@ -25878,41 +25861,36 @@ "react": "^16.13.1" } }, - "node_modules/react-textarea-autosize": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz", - "integrity": "sha512-F6bI1dgib6fSvG8so1HuArPUv+iVEfPliuLWusLF+gAKz0FbB4jLrWUrTAeq1afnPT2c9toEZYUdz/y1uKMy4A==", - "dependencies": { - "prop-types": "^15.6.0" - }, - "peerDependencies": { - "react": ">=0.14.0 <17.0.0" - } - }, "node_modules/react-transition-group": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", - "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", "dependencies": { - "dom-helpers": "^3.4.0", + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" + "prop-types": "^15.6.2" }, "peerDependencies": { - "react": ">=15.0.0", - "react-dom": ">=15.0.0" + "react": ">=16.6.0", + "react-dom": ">=16.6.0" } }, - "node_modules/react-transition-group/node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/react-transition-group/node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/react-transition-group/node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" } }, "node_modules/react-virtualized": { @@ -25962,6 +25940,22 @@ "react-dom": ">=0.14.0" } }, + "node_modules/react-widgets/node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "license": "BSD-3-Clause", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, "node_modules/read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -26315,7 +26309,8 @@ "node_modules/resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" }, "node_modules/resolve": { "version": "1.7.0", @@ -26435,6 +26430,15 @@ "node": ">=0.10.0" } }, + "node_modules/rich-textarea": { + "version": "0.26.4", + "resolved": "https://registry.npmjs.org/rich-textarea/-/rich-textarea-0.26.4.tgz", + "integrity": "sha512-RMEEUg3eNkHQc8+Joq+W+70KS5dZGXpWaIU5qiIouNAGBhVOX1vC1TpPUeysfLmqlqQOuqP81rWpgaXldkJP7A==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.14.0" + } + }, "node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -26550,6 +26554,7 @@ "version": "0.19.1", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "dev": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -27455,7 +27460,8 @@ "node_modules/spy-on-component": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/spy-on-component/-/spy-on-component-1.1.3.tgz", - "integrity": "sha512-a7jgnoBSdkcDWIQQwtEgUq4etajwG6+wGIjfC9ARUKwKOdHxJd+utgHTgLn81ETizpsw4xddUS3W8VePedtaIQ==" + "integrity": "sha512-a7jgnoBSdkcDWIQQwtEgUq4etajwG6+wGIjfC9ARUKwKOdHxJd+utgHTgLn81ETizpsw4xddUS3W8VePedtaIQ==", + "license": "MIT" }, "node_modules/sqlite3": { "version": "5.1.7", @@ -27949,6 +27955,12 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, "node_modules/table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -28511,21 +28523,6 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/ua-parser-js": { "version": "0.7.28", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", @@ -28994,16 +28991,6 @@ "node": ">= 0.4.0" } }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "peer": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/v8-compile-cache": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", @@ -29122,20 +29109,6 @@ "url": "https://opencollective.com/visjs" } }, - "node_modules/vis-util": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-4.3.4.tgz", - "integrity": "sha512-hJIZNrwf4ML7FYjs+m+zjJfaNvhjk3/1hbMdQZVnwwpOFJS/8dMG8rdbOHXcKoIEM6U5VOh3HNpaDXxGkOZGpw==", - "license": "(Apache-2.0 OR MIT)", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/visjs" - } - }, "node_modules/vis-uuid": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/vis-uuid/-/vis-uuid-1.1.3.tgz", diff --git a/package.json b/package.json index 8988f48d..1773de23 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ }, "dependencies": { "@auth0/auth0-react": "^1.6.0", + "@floating-ui/react": "^0.27.8", "@material-ui/core": "^4.11.0", "@material-ui/data-grid": "^4.0.0-alpha.2", "@material-ui/icons": "^4.9.1", @@ -53,7 +54,7 @@ "@tanstack/react-table": "^8.20.5", "ag-grid-community": "^23.2.1", "ag-grid-react": "^23.2.1", - "axios": "^0.21.1", + "axios": "^0.22.0", "axios-retry": "^3.1.8", "body-parser": "^1.20.0", "bootstrap": "^3.4.1", @@ -72,10 +73,10 @@ "path": "^0.12.7", "rc-slider": "^9.3.1", "rc-tooltip": "^4.2.1", - "react": "^16.13.1", + "react": "^17.0.2", "react-bootstrap": "^0.33.1", "react-bootstrap-dialog": "^0.13.0", - "react-dom": "^16.13.1", + "react-dom": "^17.0.2", "react-dropzone": "^11.0.2", "react-graph-vis": "^1.0.5", "react-icons": "^3.10.0", @@ -89,6 +90,7 @@ "react-virtualized": "^9.21.2", "react-widgets": "^4.5.0", "regenerator-runtime": "^0.13.7", + "rich-textarea": "^0.26.4", "shortid": "^2.2.15", "slugify": "^1.4.5", "sqlite3": "^5.1.7" diff --git a/src/hooks/use-query.js b/src/hooks/use-query.js new file mode 100644 index 00000000..11fa2fc5 --- /dev/null +++ b/src/hooks/use-query.js @@ -0,0 +1,144 @@ +import React, { + createContext, useCallback, useContext, useEffect, useRef, useState, +} from 'react'; + +const CACHE_TTL_MS = 30 * 60 * 1000; +const PRUNE_INTERVAL_MS = 5 * 60 * 1000; + +const QueryCacheContext = createContext(null); + +export const QueryCacheProvider = ({ children }) => { + const [cache, setCache] = useState(new Map()); + + const get = useCallback((key) => { + const entry = cache.get(key); + if (!entry) return undefined; + + const now = Date.now(); + if (now - entry.timestamp > CACHE_TTL_MS) { + setCache((prev) => { + const next = new Map(prev); + next.delete(key); + return next; + }); + return undefined; + } + + return entry.data; + }, [cache]); + + const set = useCallback((key, value) => { + setCache((prev) => { + const next = new Map(prev); + next.set(key, { data: value, timestamp: Date.now() }); + return next; + }); + }, []); + + useEffect(() => { + const interval = setInterval(() => { + setCache((prev) => { + const now = Date.now(); + const next = new Map(prev); + // eslint-disable-next-line no-restricted-syntax + for (const [key, entry] of next.entries()) { + if (now - entry.timestamp > CACHE_TTL_MS) { + next.delete(key); + } + } + return next; + }); + }, PRUNE_INTERVAL_MS); + + return () => clearInterval(interval); + }, []); + + return ( + + {children} + + ); +}; + +export const useQuery = ({ + queryFn, + queryKey, + debounceMs = 0, + keepStaleData = false, +}) => { + const [data, setData] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + const cache = useContext(QueryCacheContext); + if (!cache) throw new Error('You must use `useQuery` hook within `QueryCacheProvider`'); + + const timeoutRef = useRef(null); + const abortControllerRef = useRef(null); + + useEffect(() => { + let ignore = false; + + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + + timeoutRef.current = setTimeout(() => { + (async () => { + if (!keepStaleData) setData(null); + setIsLoading(true); + setError(null); + + const cachedData = cache.get(queryKey); + if (cachedData) { + setData(cachedData); + setIsLoading(false); + return; + } + + const controller = new AbortController(); + abortControllerRef.current = controller; + + try { + const d = await queryFn(controller.signal); + + if (ignore) return; + + cache.set(queryKey, d); + setData(d); + setIsLoading(false); + } catch (err) { + if (!controller.signal.aborted) { + setError((Boolean(err) && err.message) || 'Unknown error'); + setIsLoading(false); + } + } + })(); + }, debounceMs); + + return () => { + ignore = true; + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + }; + + // Don't rerender if queryFn is different, this can cause an endless render loop if + // the component calling useQuery uses a closure like queryFn: () => fetchFn(someLocalVar). + + // This could be fixed by wrapping the closure in a useCallback in the calling component + // or including a dependency array in this hook. All should work fine if all the queryFn + // depends on is included in the queryKey. + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [queryKey, cache, debounceMs]); + + return { data, isLoading, error }; +}; diff --git a/src/pages/EnrichedQueries/EnrichedQueries.jsx b/src/pages/EnrichedQueries/EnrichedQueries.jsx index f5241c84..9920111c 100644 --- a/src/pages/EnrichedQueries/EnrichedQueries.jsx +++ b/src/pages/EnrichedQueries/EnrichedQueries.jsx @@ -1,9 +1,18 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Col, Grid, Row } from 'react-bootstrap'; import { Link } from 'react-router-dom'; import { Button } from '@material-ui/core'; +import Select from './Select'; +import { QueryCacheProvider } from '../../hooks/use-query'; +import NodeInputBox from './NodeInputBox'; export default function EnrichedQueries() { + const [curies, setCuries] = useState([]); + const [inputNodeType, setInputNodeType] = useState(undefined); + const [inputNodeTaxa, setInputNodeTaxa] = useState(''); + const [relationship, setRelationship] = useState(undefined); + const [outputType, setOutputType] = useState(undefined); + return ( @@ -12,13 +21,102 @@ export default function EnrichedQueries() { ← View all tools

Enrichment Analysis

- This tool helps discover common connections between nodes. Given a list of input nodes, a relationship, and an output type, it will return a list of nodes that are best connected to the input nodes via the relationship. + This tool helps discover common connections between nodes. Given a + list of input nodes, a relationship, and an output type, it will + return a list of nodes that are best connected to the input nodes + via the relationship.


+
+
+ setInputNodeTaxa(e.target.value)} + /> +
+
+ + + + + +
+ +
+
+ +
{JSON.stringify(curies, null, 2)}
diff --git a/src/pages/EnrichedQueries/NodeInputBox.jsx b/src/pages/EnrichedQueries/NodeInputBox.jsx new file mode 100644 index 00000000..1b4b2fc3 --- /dev/null +++ b/src/pages/EnrichedQueries/NodeInputBox.jsx @@ -0,0 +1,303 @@ +import { RichTextarea } from 'rich-textarea'; +import React, { + forwardRef, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { + autoUpdate, + FloatingFocusManager, + FloatingPortal, + size, + useDismiss, + useId, + useInteractions, + useListNavigation, + useRole, + flip, useFloating, +} from '@floating-ui/react'; + +import { useQuery } from '../../hooks/use-query'; +import nameLookup from './name-resolver'; + +export default function NodeInputBox({ onCurieListChange, inputNodeTaxa, inputNodeType }) { + const [open, setOpen] = useState(false); + const [value, setValue] = useState(''); + const [activeIndex, setActiveIndex] = useState(null); + const [validNames, setValidNames] = useState(new Map()); + + useEffect(() => { + const curies = value + .split('\n') + .map((line) => validNames.get(line)) + .filter((line) => line !== undefined); + onCurieListChange(curies); + }, [value, validNames, onCurieListChange]); + + const [selection, setSelection] = useState({ + top: 0, left: 0, selectionStart: 0, selectionEnd: 0, + }); + + const listRef = useRef([]); + + const { refs, floatingStyles, context } = useFloating({ + whileElementsMounted: autoUpdate, + open, + onOpenChange: setOpen, + placement: 'bottom-start', + middleware: [ + flip(), + size({ + padding: 16, + apply({ availableHeight, availableWidth, elements }) { + elements.floating.style.maxHeight = `${availableHeight}px`; + elements.floating.style.maxWidth = `${availableWidth}px`; + }, + }), + ], + }); + + const role = useRole(context, { role: 'listbox' }); + const dismiss = useDismiss(context); + const listNav = useListNavigation(context, { + listRef, + activeIndex, + onNavigate: setActiveIndex, + openOnArrowKeyDown: false, + virtual: true, + loop: true, + }); + + const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions( + [role, dismiss, listNav], + ); + + const getCurrentLineIndex = useCallback( + () => value.slice(0, selection.selectionStart).split('\n').length - 1, + [value, selection], + ); + + useEffect(() => { + refs.setPositionReference({ + getBoundingClientRect: () => ({ + x: 0, + y: 0, + top: selection.top, + left: selection.left, + bottom: selection.top + 20, + right: selection.left, + width: 0, + height: 20, + }), + }); + }, [selection, refs]); + + const currentLineText = useMemo(() => { + const currentLineIndex = getCurrentLineIndex(); + return value.split('\n')[currentLineIndex]; + }, [getCurrentLineIndex, value]); + + const { + data: options, + isLoading, + } = useQuery({ + queryFn: async (signal) => { + if (currentLineText.length === 0) return []; + return nameLookup({ + signal, + name: currentLineText, + taxaFilter: inputNodeTaxa.trim().split(',').map(((t) => t.trim())), + biolinkTypeFilter: inputNodeType, + }); + }, + debounceMs: 250, + queryKey: currentLineText, + keepStaleData: true, + }); + + function handleSelectItem() { + setActiveIndex(null); + setOpen(false); + + if (!options || options.length === 0 || activeIndex === null) return; + + const selectedOption = options[activeIndex]; + + setValidNames((prev) => { + const nextMap = new Map(prev); + nextMap.set(selectedOption.label, selectedOption.curie); + return nextMap; + }); + + setValue((prev) => { + const lines = prev.split('\n'); + const currentLineIndex = getCurrentLineIndex(); + lines[currentLineIndex] = selectedOption.label; + return lines.join('\n'); + }); + } + + return ( +
+ {/* REFERENCE */} + + Input nodes + + { + setValue(e.target.value); + }, + 'aria-autocomplete': 'list', + onKeyDown: (e) => { + if (open) { + if (e.key === 'Enter') { + e.preventDefault(); + handleSelectItem(); + } + } + + const noModifiers = !e.ctrlKey && !e.altKey && !e.metaKey; + + if ( + (e.ctrlKey && e.code === 'Space') || + (noModifiers && e.key.length === 1 && e.key.match(/\S| /)) + ) { + setOpen(true); + } + }, + })} + > + {(content) => content.split('\n').map((line, i) => ( + + + {`${line}\n`} + + + ))} + + + {/* FLOATING */} + {open && ( + + +
+ {isLoading && ( +
+ Loading… +
+ )} + {options === null || (options.length === 0 && !isLoading) ? ( +
+ No matching results, please try a different query +
+ ) : ( + <> + {Boolean(options) && options.map((option, i) => ( + +
+ {option.label} + + {option.curie} + +
+
+ ))} + + )} +
+
+
+ )} +
+ ); +} + +const Item = React.memo( + forwardRef( + ({ children, active, ...rest }, ref) => { + const id = useId(); + return ( +
+ {children} +
+ ); + }, + ), +); diff --git a/src/pages/EnrichedQueries/Select.jsx b/src/pages/EnrichedQueries/Select.jsx new file mode 100644 index 00000000..fd4181a5 --- /dev/null +++ b/src/pages/EnrichedQueries/Select.jsx @@ -0,0 +1,45 @@ +import React from 'react'; + +export default function Select({ + options, + onChange, + value, + label, + notSelectedOption = 'N/A', +}) { + return ( +
+ + {label} + + +
+ ); +} diff --git a/src/pages/EnrichedQueries/name-resolver.js b/src/pages/EnrichedQueries/name-resolver.js new file mode 100644 index 00000000..ff573acf --- /dev/null +++ b/src/pages/EnrichedQueries/name-resolver.js @@ -0,0 +1,23 @@ +import axios from 'axios'; + +export default async function nameLookup({ + name, + limit = 100, + biolinkTypeFilter, + taxaFilter, + signal, +}) { + const { data } = await axios.get('https://robokop-name-resolver.apps.renci.org/lookup', { + signal, + params: { + string: name, + autocomplete: true, + offset: 0, + limit, + biolink_type: biolinkTypeFilter, + only_taxa: (taxaFilter || []).join('|'), + }, + }); + + return data; +} From b2eb1cb36ab354ad8d5802e25c37337864ea2795 Mon Sep 17 00:00:00 2001 From: David Glymph Date: Tue, 29 Apr 2025 04:46:44 -0400 Subject: [PATCH 4/9] fix filters --- src/pages/EnrichedQueries/EnrichedQueries.jsx | 6 +++--- src/pages/EnrichedQueries/NodeInputBox.jsx | 2 +- src/pages/EnrichedQueries/Select.jsx | 4 ++-- src/pages/EnrichedQueries/name-resolver.js | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/EnrichedQueries/EnrichedQueries.jsx b/src/pages/EnrichedQueries/EnrichedQueries.jsx index 9920111c..03e3b2e6 100644 --- a/src/pages/EnrichedQueries/EnrichedQueries.jsx +++ b/src/pages/EnrichedQueries/EnrichedQueries.jsx @@ -53,14 +53,14 @@ export default function EnrichedQueries() {
- Input node taxa filter (optional) + Input node taxa filter (optional, comma separated)
- +
{JSON.stringify(curies, null, 2)}
diff --git a/src/pages/EnrichedQueries/NodeInputBox.jsx b/src/pages/EnrichedQueries/NodeInputBox.jsx index 1b4b2fc3..ebb9acd8 100644 --- a/src/pages/EnrichedQueries/NodeInputBox.jsx +++ b/src/pages/EnrichedQueries/NodeInputBox.jsx @@ -145,7 +145,7 @@ export default function NodeInputBox({ onCurieListChange, inputNodeTaxa, inputNo {/* REFERENCE */} { const v = e.target.value; - onChange(v === notSelectedOption ? undefined : value); + onChange(v === notSelectedOption ? undefined : v); }} > {[notSelectedOption, ...options].map((option) => ( diff --git a/src/pages/EnrichedQueries/name-resolver.js b/src/pages/EnrichedQueries/name-resolver.js index ff573acf..711a2adf 100644 --- a/src/pages/EnrichedQueries/name-resolver.js +++ b/src/pages/EnrichedQueries/name-resolver.js @@ -15,7 +15,7 @@ export default async function nameLookup({ offset: 0, limit, biolink_type: biolinkTypeFilter, - only_taxa: (taxaFilter || []).join('|'), + only_taxa: (taxaFilter || []).join('|') || undefined, }, }); From 3791abb60cdd7170ea59b3a145f676ee54993e67 Mon Sep 17 00:00:00 2001 From: David Glymph Date: Tue, 29 Apr 2025 05:00:00 -0400 Subject: [PATCH 5/9] get biolink types --- src/pages/EnrichedQueries/EnrichedQueries.jsx | 36 ++++++++----------- src/pages/EnrichedQueries/Select.jsx | 4 +-- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/pages/EnrichedQueries/EnrichedQueries.jsx b/src/pages/EnrichedQueries/EnrichedQueries.jsx index 03e3b2e6..eb93c1e1 100644 --- a/src/pages/EnrichedQueries/EnrichedQueries.jsx +++ b/src/pages/EnrichedQueries/EnrichedQueries.jsx @@ -1,17 +1,24 @@ -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { Col, Grid, Row } from 'react-bootstrap'; import { Link } from 'react-router-dom'; import { Button } from '@material-ui/core'; import Select from './Select'; import { QueryCacheProvider } from '../../hooks/use-query'; import NodeInputBox from './NodeInputBox'; +import BiolinkContext from '~/context/biolink'; export default function EnrichedQueries() { + const { concepts: categories, predicates } = useContext(BiolinkContext); + const [curies, setCuries] = useState([]); const [inputNodeType, setInputNodeType] = useState(undefined); const [inputNodeTaxa, setInputNodeTaxa] = useState(''); - const [relationship, setRelationship] = useState(undefined); - const [outputType, setOutputType] = useState(undefined); + const [relationship, setRelationship] = useState('related_to'); + const [outputType, setOutputType] = useState('NamedThing'); + + if (!categories.length || !predicates.length) { + return null; + } return ( @@ -41,12 +48,8 @@ export default function EnrichedQueries() { > p.predicate.split(':')[1]).sort()} onChange={setRelationship} value={relationship} /> c.split(':')[1]).sort()} - onChange={setInputNodeType} - value={inputNodeType} + setInputNodeTaxa(e.target.value)} /> -
- - Input node taxa filter (optional, comma separated) - - setInputNodeTaxa(e.target.value)} - /> -
+ - - - + + + + +
+ c.split(':')[1]).sort()} + onChange={setOutputType} + value={outputType} + /> +
-
+
+ {isLoading ? 'Stop Query' : 'Submit Query'} + + + {results.length > 0 && ( + + )} - + {results.length > 0 && ( +
+

Results

+ + + + + + + + + + {results + .sort((a, b) => b.p_value - a.p_value) + .slice(0, 500) + .map(({ id, name, p_value }) => ( + + + + + + ))} + +
IDNameP-value
{id}{name}{p_value.toFixed(6)}
+
+ )} - +
From 298c89f902a16be4c0f964d5ead2fee43e2acff6 Mon Sep 17 00:00:00 2001 From: David Glymph Date: Thu, 22 May 2025 10:26:34 -0400 Subject: [PATCH 9/9] correct p-value sorting --- src/pages/EnrichedQueries/EnrichedQueries.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/EnrichedQueries/EnrichedQueries.jsx b/src/pages/EnrichedQueries/EnrichedQueries.jsx index f654d5a4..85b12ee3 100644 --- a/src/pages/EnrichedQueries/EnrichedQueries.jsx +++ b/src/pages/EnrichedQueries/EnrichedQueries.jsx @@ -247,7 +247,7 @@ export default function EnrichedQueries() { {results - .sort((a, b) => b.p_value - a.p_value) + .sort((a, b) => a.p_value - b.p_value) .slice(0, 500) .map(({ id, name, p_value }) => (