@vitessce/biomarker-select 3.5.7 → 3.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +368 -19
- package/dist-tsc/BiomarkerSelectSubscriber.d.ts.map +1 -1
- package/dist-tsc/BiomarkerSelectSubscriber.js +65 -9
- package/dist-tsc/ComparativeHeadingSubscriber.d.ts +2 -0
- package/dist-tsc/ComparativeHeadingSubscriber.d.ts.map +1 -0
- package/dist-tsc/ComparativeHeadingSubscriber.js +45 -0
- package/dist-tsc/default-async-functions.d.ts.map +1 -1
- package/dist-tsc/default-async-functions.js +1 -0
- package/dist-tsc/index.d.ts +1 -0
- package/dist-tsc/index.js +1 -0
- package/dist-tsc/scmd-ui.d.ts.map +1 -1
- package/dist-tsc/scmd-ui.js +3 -2
- package/dist-tsc/{confirmatory-stepper.d.ts → stepper-confirmatory.d.ts} +1 -1
- package/dist-tsc/stepper-confirmatory.d.ts.map +1 -0
- package/dist-tsc/stepper-exploratory.d.ts +2 -0
- package/dist-tsc/stepper-exploratory.d.ts.map +1 -0
- package/dist-tsc/stepper-exploratory.js +61 -0
- package/package.json +4 -4
- package/src/BiomarkerSelectSubscriber.js +82 -8
- package/src/ComparativeHeadingSubscriber.js +87 -0
- package/src/default-async-functions.js +1 -0
- package/src/index.js +1 -0
- package/src/scmd-ui.js +18 -2
- package/src/stepper-exploratory.js +175 -0
- package/dist-tsc/confirmatory-stepper.d.ts.map +0 -1
- /package/dist-tsc/{confirmatory-stepper.js → stepper-confirmatory.js} +0 -0
- /package/src/{confirmatory-stepper.js → stepper-confirmatory.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComparativeHeadingSubscriber.d.ts","sourceRoot":"","sources":["../src/ComparativeHeadingSubscriber.js"],"names":[],"mappings":"AA+BA,sEAuDC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useCallback } from 'react';
|
|
3
|
+
import { useCoordination, } from '@vitessce/vit-s';
|
|
4
|
+
import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
|
|
5
|
+
import { makeStyles } from '@material-ui/core';
|
|
6
|
+
const useStyles = makeStyles(theme => ({
|
|
7
|
+
buttonContainer: {
|
|
8
|
+
position: 'absolute',
|
|
9
|
+
right: 0,
|
|
10
|
+
display: 'flex',
|
|
11
|
+
flexDirection: 'column',
|
|
12
|
+
justifyContent: 'center',
|
|
13
|
+
height: '100%',
|
|
14
|
+
},
|
|
15
|
+
headingContainer: {
|
|
16
|
+
backgroundColor: 'transparent',
|
|
17
|
+
},
|
|
18
|
+
headingSectionContainer: {
|
|
19
|
+
width: '100%',
|
|
20
|
+
display: 'flex',
|
|
21
|
+
flexDirection: 'row',
|
|
22
|
+
position: 'relative',
|
|
23
|
+
},
|
|
24
|
+
headingText: {
|
|
25
|
+
margin: 0,
|
|
26
|
+
color: theme.palette.primaryForeground,
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
export function ComparativeHeadingSubscriber(props) {
|
|
30
|
+
const { coordinationScopes, } = props;
|
|
31
|
+
const [{ sampleSetSelection, featureSelection, }, { setSampleSetSelection,
|
|
32
|
+
// TODO: buttons for modification of featureSelection
|
|
33
|
+
// setFeatureSelection,
|
|
34
|
+
}] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.COMPARATIVE_HEADING], coordinationScopes);
|
|
35
|
+
const classes = useStyles();
|
|
36
|
+
const swapSampleSets = useCallback(() => {
|
|
37
|
+
if (sampleSetSelection?.length === 2) {
|
|
38
|
+
setSampleSetSelection([
|
|
39
|
+
sampleSetSelection[1],
|
|
40
|
+
sampleSetSelection[0],
|
|
41
|
+
]);
|
|
42
|
+
}
|
|
43
|
+
}, [sampleSetSelection, setSampleSetSelection]);
|
|
44
|
+
return (_jsxs("div", { className: classes.headingContainer, children: [sampleSetSelection && sampleSetSelection.length === 2 ? (_jsxs("div", { className: classes.headingSectionContainer, children: [_jsx("div", { style: { width: '45%' }, children: _jsx("h2", { className: classes.headingText, children: sampleSetSelection?.[0]?.at(-1) }) }), _jsx("div", { style: { width: '5%' }, children: _jsx("h2", { className: classes.headingText, style: { textAlign: 'right' }, children: "vs.\u00A0" }) }), _jsx("div", { style: { width: '50%' }, children: _jsx("h2", { className: classes.headingText, children: sampleSetSelection?.[1]?.at(-1) }) }), _jsx("div", { className: classes.buttonContainer, children: _jsx("button", { onClick: swapSampleSets, type: "button", children: "Swap" }) })] })) : null, featureSelection && featureSelection.length > 0 ? (_jsxs("div", { className: classes.headingSectionContainer, children: [_jsx("div", { children: _jsx("h3", { className: classes.headingText, children: "Selected Biomarkers:\u00A0" }) }), featureSelection?.map((featureName, i) => (_jsx("div", { children: _jsxs("h3", { className: classes.headingText, children: [featureName, i < featureSelection.length - 1 ? (_jsx("span", { children: ",\u00A0" })) : null] }) })))] })) : null] }));
|
|
45
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-async-functions.d.ts","sourceRoot":"","sources":["../src/default-async-functions.js"],"names":[],"mappings":"AAqHA;;;;;;;GAOG;AACH,qDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,WAAQ,MAAM,kBACN,IAAI,GAAG,kBAAkB,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC,CAiB7B;AAeD;;;;;;;GAOG;AACH,kDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,QAAQ,MAAM,kBACN,kBAAkB,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"default-async-functions.d.ts","sourceRoot":"","sources":["../src/default-async-functions.js"],"names":[],"mappings":"AAqHA;;;;;;;GAOG;AACH,qDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,WAAQ,MAAM,kBACN,IAAI,GAAG,kBAAkB,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC,CAiB7B;AAeD;;;;;;;GAOG;AACH,kDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,QAAQ,MAAM,kBACN,kBAAkB,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAmC7B;AAED;;;;;;GAMG;AACH,qDAJG;IAAyB,WAAW,EAA5B,WAAW;CACnB,SAAQ,MAAM,GACJ,OAAO,CAAC,MAAM,EAAE,CAAC,CA2B7B;AAED;;;;;;;GAOG;AACH,gDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,kBAAQ,MAAM,kBACN,MAAM,GACJ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAoBxC;iCA1PsD,uBAAuB;wCADsE,iBAAiB;4BAAjB,iBAAiB"}
|
|
@@ -161,6 +161,7 @@ export async function transformFeature({ queryClient }, node, targetModality) {
|
|
|
161
161
|
staleTime: Infinity,
|
|
162
162
|
queryFn: loadPathwayToGeneEdges,
|
|
163
163
|
});
|
|
164
|
+
// TODO: support matching using ontology term (rather than requiring kgId)?
|
|
164
165
|
const matchingEdges = pathwayGeneEdges.filter((/** @type {KgEdge} */ d) => d.source === node.kgId);
|
|
165
166
|
const matchingGeneIds = matchingEdges.map((/** @type {KgEdge} */ d) => d.target);
|
|
166
167
|
const matchingGenes = geneNodes.filter(d => matchingGeneIds.includes(d.kgId));
|
package/dist-tsc/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { BiomarkerSelectSubscriber } from "./BiomarkerSelectSubscriber.js";
|
|
2
|
+
export { ComparativeHeadingSubscriber } from "./ComparativeHeadingSubscriber.js";
|
|
2
3
|
export { autocompleteFeature, transformFeature, getAlternativeTerms, getTermMapping } from "./default-async-functions.js";
|
|
3
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist-tsc/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { BiomarkerSelectSubscriber } from './BiomarkerSelectSubscriber.js';
|
|
2
|
+
export { ComparativeHeadingSubscriber } from './ComparativeHeadingSubscriber.js';
|
|
2
3
|
export { autocompleteFeature, transformFeature, getAlternativeTerms, getTermMapping, } from './default-async-functions.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scmd-ui.d.ts","sourceRoot":"","sources":["../src/scmd-ui.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scmd-ui.d.ts","sourceRoot":"","sources":["../src/scmd-ui.js"],"names":[],"mappings":"AAQA,gDAkFC"}
|
package/dist-tsc/scmd-ui.js
CHANGED
|
@@ -2,7 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
/* eslint-disable no-unused-vars */
|
|
3
3
|
import React, { useState } from 'react';
|
|
4
4
|
import { Button, ButtonGroup, Grid, Tooltip } from '@material-ui/core';
|
|
5
|
-
import { ConfirmatoryStepper } from './confirmatory
|
|
5
|
+
import { ConfirmatoryStepper } from './stepper-confirmatory.js';
|
|
6
|
+
import { ExploratoryStepper } from './stepper-exploratory.js';
|
|
6
7
|
import { useStyles } from './styles.js';
|
|
7
8
|
export function ScmdUi(props) {
|
|
8
9
|
const { mode: modeProp, setMode, step: stepProp, setStep, currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection, currentModalitySpecificSelection, setCurrentModalitySpecificSelection, currentStratificationSelection, setCurrentStratificationSelection, autocompleteNode, getEdges, stratifications, onFinish, } = props;
|
|
@@ -11,5 +12,5 @@ export function ScmdUi(props) {
|
|
|
11
12
|
const step = mode === 'confirmatory' ? (stepProp || 'select-biomarkers') : (stepProp || 'define-stratification');
|
|
12
13
|
const [tempIsVisible, setTempIsVisible] = useState(true);
|
|
13
14
|
const isConfirmatoryMode = mode === 'confirmatory';
|
|
14
|
-
return (_jsxs(Grid, { container: true, style: { border: '0px solid red' }, children: [_jsxs(Grid, { item: true, container: true, xs: 12, className: classes.header, children: [_jsx(Grid, { item: true, xs: 8 }), _jsx(Grid, { item: true, container: true, xs: 4, justifyContent: "flex-end", children: _jsxs(ButtonGroup, { variant: "outlined", size: "small", color: "secondary", "aria-label": "Toggle between confirmatory and exploratory modes", children: [_jsx(Tooltip, { arrow: true, title: "Start from biomarker(s) of interest", children: _jsx(Button, { variant: isConfirmatoryMode ? 'contained' : 'outlined', onClick: () => setMode('confirmatory'), children: "Confirmatory (Hypothesis-driven)" }) }), _jsx(Tooltip, { arrow: true, title: "Lacking biomarker(s) of interest", children: _jsx(Button, { variant: !isConfirmatoryMode ? 'contained' : 'outlined', onClick: () => setMode('exploratory'), children: "Exploratory (Hypothesis-generating)" }) })] }) })] }), tempIsVisible ? (_jsxs("div", { style: { border: '2px solid #eee', borderRadius: '5px' }, children: [mode === 'confirmatory' ? (_jsx(ConfirmatoryStepper, { autocompleteNode: autocompleteNode, currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, currentModalitySpecificSelection: currentModalitySpecificSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, currentStratificationSelection: currentStratificationSelection, setCurrentStratificationSelection: setCurrentStratificationSelection, getEdges: getEdges, stratifications: stratifications, onFinish: onFinish })) : null, mode === 'exploratory' ? _jsx(
|
|
15
|
+
return (_jsxs(Grid, { container: true, style: { border: '0px solid red' }, children: [_jsxs(Grid, { item: true, container: true, xs: 12, className: classes.header, children: [_jsx(Grid, { item: true, xs: 8 }), _jsx(Grid, { item: true, container: true, xs: 4, justifyContent: "flex-end", children: _jsxs(ButtonGroup, { variant: "outlined", size: "small", color: "secondary", "aria-label": "Toggle between confirmatory and exploratory modes", children: [_jsx(Tooltip, { arrow: true, title: "Start from biomarker(s) of interest", children: _jsx(Button, { variant: isConfirmatoryMode ? 'contained' : 'outlined', onClick: () => setMode('confirmatory'), children: "Confirmatory (Hypothesis-driven)" }) }), _jsx(Tooltip, { arrow: true, title: "Lacking biomarker(s) of interest", children: _jsx(Button, { variant: !isConfirmatoryMode ? 'contained' : 'outlined', onClick: () => setMode('exploratory'), children: "Exploratory (Hypothesis-generating)" }) })] }) })] }), tempIsVisible ? (_jsxs("div", { style: { border: '2px solid #eee', borderRadius: '5px' }, children: [mode === 'confirmatory' ? (_jsx(ConfirmatoryStepper, { autocompleteNode: autocompleteNode, currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, currentModalitySpecificSelection: currentModalitySpecificSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, currentStratificationSelection: currentStratificationSelection, setCurrentStratificationSelection: setCurrentStratificationSelection, getEdges: getEdges, stratifications: stratifications, onFinish: onFinish })) : null, mode === 'exploratory' ? (_jsx(ExploratoryStepper, { autocompleteNode: autocompleteNode, currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, currentModalitySpecificSelection: currentModalitySpecificSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, currentStratificationSelection: currentStratificationSelection, setCurrentStratificationSelection: setCurrentStratificationSelection, getEdges: getEdges, stratifications: stratifications, onFinish: onFinish })) : null] })) : null] }));
|
|
15
16
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export function ConfirmatoryStepper(props: any): JSX.Element;
|
|
2
|
-
//# sourceMappingURL=confirmatory
|
|
2
|
+
//# sourceMappingURL=stepper-confirmatory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stepper-confirmatory.d.ts","sourceRoot":"","sources":["../src/stepper-confirmatory.js"],"names":[],"mappings":"AAcA,6DAkMC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stepper-exploratory.d.ts","sourceRoot":"","sources":["../src/stepper-exploratory.js"],"names":[],"mappings":"AAUA,4DAoKC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useEffect } from 'react';
|
|
3
|
+
import { Stepper, Step, StepLabel, Button, Grid, Box, Typography } from '@material-ui/core';
|
|
4
|
+
import { ConfirmatoryCart } from './cart.js';
|
|
5
|
+
import { SelectStratification } from './select-stratification.js';
|
|
6
|
+
import { useStyles } from './styles.js';
|
|
7
|
+
const steps = [
|
|
8
|
+
'Define case-control stratification',
|
|
9
|
+
];
|
|
10
|
+
export function ExploratoryStepper(props) {
|
|
11
|
+
const { autocompleteNode, currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection, currentModalitySpecificSelection, setCurrentModalitySpecificSelection, currentStratificationSelection, setCurrentStratificationSelection, getEdges, stratifications, onFinish, } = props;
|
|
12
|
+
const classes = useStyles();
|
|
13
|
+
const [activeStep, setActiveStep] = React.useState(0);
|
|
14
|
+
const [skipped, setSkipped] = React.useState(new Set());
|
|
15
|
+
const isStepOptional = step => step === 2;
|
|
16
|
+
const isStepSkipped = step => skipped.has(step);
|
|
17
|
+
const handleNext = () => {
|
|
18
|
+
let newSkipped = skipped;
|
|
19
|
+
if (isStepSkipped(activeStep)) {
|
|
20
|
+
newSkipped = new Set(newSkipped.values());
|
|
21
|
+
newSkipped.delete(activeStep);
|
|
22
|
+
}
|
|
23
|
+
setActiveStep(prevActiveStep => prevActiveStep + 1);
|
|
24
|
+
setSkipped(newSkipped);
|
|
25
|
+
};
|
|
26
|
+
const handleBack = () => {
|
|
27
|
+
setActiveStep(prevActiveStep => prevActiveStep - 1);
|
|
28
|
+
};
|
|
29
|
+
const handleSkip = () => {
|
|
30
|
+
if (!isStepOptional(activeStep)) {
|
|
31
|
+
// You probably want to guard against something like this,
|
|
32
|
+
// it should never occur unless someone's actively trying to break something.
|
|
33
|
+
throw new Error("You can't skip a step that isn't optional.");
|
|
34
|
+
}
|
|
35
|
+
setActiveStep(prevActiveStep => prevActiveStep + 1);
|
|
36
|
+
setSkipped((prevSkipped) => {
|
|
37
|
+
const newSkipped = new Set(prevSkipped.values());
|
|
38
|
+
newSkipped.add(activeStep);
|
|
39
|
+
return newSkipped;
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
const handleReset = () => {
|
|
43
|
+
setActiveStep(0);
|
|
44
|
+
};
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (activeStep === steps.length) {
|
|
47
|
+
onFinish();
|
|
48
|
+
}
|
|
49
|
+
}, [activeStep, steps]);
|
|
50
|
+
return (_jsxs(Grid, { item: true, container: true, xs: 12, children: [_jsx(Grid, { item: true, container: true, xs: 12, children: _jsx(Box, { className: classes.fullWidthBox, children: _jsx(Stepper, { activeStep: activeStep, children: steps.map((label, index) => {
|
|
51
|
+
const stepProps = {};
|
|
52
|
+
const labelProps = {};
|
|
53
|
+
if (isStepOptional(index)) {
|
|
54
|
+
labelProps.optional = (_jsx(Typography, { variant: "caption", children: "Optional" }));
|
|
55
|
+
}
|
|
56
|
+
if (isStepSkipped(index)) {
|
|
57
|
+
stepProps.completed = false;
|
|
58
|
+
}
|
|
59
|
+
return (_jsx(Step, { ...stepProps, children: _jsx(StepLabel, { ...labelProps, children: label }) }, label));
|
|
60
|
+
}) }) }) }), _jsxs(Grid, { item: true, container: true, xs: 12, style: { marginTop: '20px' }, alignItems: "flex-start", children: [_jsx(Grid, { item: true, container: true, xs: 8, children: activeStep === steps.length ? (_jsx(_Fragment, { children: _jsx(Typography, { style: { marginTop: '20px', marginBottom: '10px' }, children: "All steps completed - you're finished" }) })) : (_jsx(_Fragment, { children: activeStep === 0 ? (_jsx(SelectStratification, { autocompleteNode: autocompleteNode, currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, currentModalitySpecificSelection: currentModalitySpecificSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, currentStratificationSelection: currentStratificationSelection, setCurrentStratificationSelection: setCurrentStratificationSelection, getEdges: getEdges, stratifications: stratifications })) : null })) }), _jsx(Grid, { item: true, container: true, xs: 4, children: _jsx(ConfirmatoryCart, { currentModalityAgnosticSelection: currentModalityAgnosticSelection, setCurrentModalityAgnosticSelection: setCurrentModalityAgnosticSelection, currentModalitySpecificSelection: currentModalitySpecificSelection, setCurrentModalitySpecificSelection: setCurrentModalitySpecificSelection, currentStratificationSelection: currentStratificationSelection, setCurrentStratificationSelection: setCurrentStratificationSelection, stratifications: stratifications }) }), _jsx(Box, { className: classes.fullWidthBox, children: activeStep === steps.length ? (_jsx(_Fragment, { children: _jsxs(Box, { style: { display: 'flex', flexDirection: 'row', paddingTop: '20px' }, children: [_jsx(Box, { style: { flex: '1 1 auto' } }), _jsx(Button, { onClick: handleReset, children: "Reset" })] }) })) : (_jsxs(Box, { style: { display: 'flex', flexDirection: 'row', paddingTop: '20px' }, children: [_jsx(Button, { color: "inherit", disabled: activeStep === 0, onClick: handleBack, style: { mr: 1 }, children: "Back" }), _jsx(Box, { style: { flex: '1 1 auto' } }), isStepOptional(activeStep) && (_jsx(Button, { color: "inherit", onClick: handleSkip, style: { marginRight: '10px' }, children: "Skip" })), _jsx(Button, { onClick: handleNext, children: activeStep === steps.length - 1 ? 'Finish' : 'Next' })] })) })] })] }));
|
|
61
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitessce/biomarker-select",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.9",
|
|
4
4
|
"author": "Gehlenborg Lab",
|
|
5
5
|
"homepage": "http://vitessce.io",
|
|
6
6
|
"repository": {
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"react-window": "^1.8.10",
|
|
25
25
|
"fuse.js": "^7.0.0",
|
|
26
26
|
"d3-dsv": "^1.1.1",
|
|
27
|
-
"@vitessce/constants-internal": "3.5.
|
|
28
|
-
"@vitessce/vit-s": "3.5.
|
|
29
|
-
"@vitessce/types": "3.5.
|
|
27
|
+
"@vitessce/constants-internal": "3.5.9",
|
|
28
|
+
"@vitessce/vit-s": "3.5.9",
|
|
29
|
+
"@vitessce/types": "3.5.9"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@testing-library/jest-dom": "^5.16.4",
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
/* eslint-disable max-len */
|
|
2
2
|
/* eslint-disable no-unused-vars */
|
|
3
|
-
import React, { useState, useCallback } from 'react';
|
|
3
|
+
import React, { useState, useCallback, useMemo } from 'react';
|
|
4
4
|
import {
|
|
5
5
|
useAsyncFunction,
|
|
6
6
|
useViewConfigStoreApi,
|
|
7
7
|
useSetViewConfig,
|
|
8
8
|
useViewConfig,
|
|
9
9
|
useCoordination,
|
|
10
|
+
useLoaders,
|
|
11
|
+
useComparisonMetadata,
|
|
12
|
+
useMatchingLoader,
|
|
13
|
+
useColumnNameMapping,
|
|
10
14
|
} from '@vitessce/vit-s';
|
|
11
|
-
import { AsyncFunctionType, ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
|
|
15
|
+
import { AsyncFunctionType, ViewType, COMPONENT_COORDINATION_TYPES, DataType } from '@vitessce/constants-internal';
|
|
12
16
|
import { ScmdUi } from './scmd-ui.js';
|
|
13
17
|
|
|
14
|
-
|
|
15
18
|
/**
|
|
16
19
|
*
|
|
17
20
|
* @param {object} props
|
|
@@ -21,10 +24,15 @@ import { ScmdUi } from './scmd-ui.js';
|
|
|
21
24
|
export function BiomarkerSelectSubscriber(props) {
|
|
22
25
|
const {
|
|
23
26
|
coordinationScopes,
|
|
24
|
-
stratificationOptions,
|
|
27
|
+
stratificationOptions: stratificationOptionsProp, // TODO: Remove; Get from comparisonMetadata instead
|
|
25
28
|
} = props;
|
|
26
29
|
|
|
30
|
+
const loaders = useLoaders();
|
|
31
|
+
|
|
27
32
|
const [{
|
|
33
|
+
dataset,
|
|
34
|
+
obsType,
|
|
35
|
+
sampleType,
|
|
28
36
|
sampleSetSelection,
|
|
29
37
|
featureSelection,
|
|
30
38
|
}, {
|
|
@@ -40,6 +48,16 @@ export function BiomarkerSelectSubscriber(props) {
|
|
|
40
48
|
const setViewConfig = useSetViewConfig(viewConfigStoreApi);
|
|
41
49
|
|
|
42
50
|
|
|
51
|
+
// Need obsSets and sampleSets options to obtain mapping between
|
|
52
|
+
// group names and column names.
|
|
53
|
+
const obsSetsLoader = useMatchingLoader(
|
|
54
|
+
loaders, dataset, DataType.OBS_SETS, { obsType },
|
|
55
|
+
);
|
|
56
|
+
const sampleSetsLoader = useMatchingLoader(
|
|
57
|
+
loaders, dataset, DataType.SAMPLE_SETS, { sampleType },
|
|
58
|
+
);
|
|
59
|
+
const sampleSetsColumnNameMappingReversed = useColumnNameMapping(sampleSetsLoader, true);
|
|
60
|
+
|
|
43
61
|
// TODO: make isSelecting a coordination type plugin.
|
|
44
62
|
// TODO: use store hooks from @vitessce/vit-s to update the view config based on the selections.
|
|
45
63
|
const [isSelecting, setIsSelecting] = useState(true);
|
|
@@ -62,6 +80,43 @@ export function BiomarkerSelectSubscriber(props) {
|
|
|
62
80
|
[transformFeature],
|
|
63
81
|
);
|
|
64
82
|
|
|
83
|
+
const [{ comparisonMetadata }, cmpMetadataStatus, cmpMetadataUrls] = useComparisonMetadata(
|
|
84
|
+
loaders, dataset, false, {}, {}, { obsType, sampleType },
|
|
85
|
+
);
|
|
86
|
+
const stratificationOptions = useMemo(() => {
|
|
87
|
+
/*
|
|
88
|
+
return array of objects like {
|
|
89
|
+
stratificationId: 'aki-vs-hr',
|
|
90
|
+
name: 'Acute kidney injury (AKI) vs. Healthy reference',
|
|
91
|
+
stratificationType: 'sampleSet', // key changed from 'groupType'. value changed from 'clinical'
|
|
92
|
+
sampleSets: [
|
|
93
|
+
['Disease Type', 'AKI'],
|
|
94
|
+
['Disease Type', 'Reference'],
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
*/
|
|
98
|
+
if (comparisonMetadata?.sample_group_pairs) {
|
|
99
|
+
return comparisonMetadata.sample_group_pairs.map((sampleGroupPair) => {
|
|
100
|
+
const [sampleGroupCol, sampleGroupValues] = sampleGroupPair;
|
|
101
|
+
const [sampleGroupCtrl, sampleGroupCase] = sampleGroupValues;
|
|
102
|
+
const groupName = sampleSetsColumnNameMappingReversed?.[sampleGroupCol];
|
|
103
|
+
return {
|
|
104
|
+
stratificationId: `${sampleGroupCol}_${sampleGroupCtrl}-vs-${sampleGroupCase}`,
|
|
105
|
+
name: `${groupName}: ${sampleGroupCtrl} vs. ${sampleGroupCase}`,
|
|
106
|
+
stratificationType: 'sampleSet',
|
|
107
|
+
sampleSets: [
|
|
108
|
+
// With sampleSets coming from the comparison_metadata,
|
|
109
|
+
// need to use loader options from obsSets and sampleSets to get mapping
|
|
110
|
+
// from column name to group name.
|
|
111
|
+
[groupName, sampleGroupCtrl],
|
|
112
|
+
[groupName, sampleGroupCase],
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}, [comparisonMetadata, sampleSetsColumnNameMappingReversed]);
|
|
119
|
+
|
|
65
120
|
return (
|
|
66
121
|
<>
|
|
67
122
|
{isSelecting ? (
|
|
@@ -80,20 +135,39 @@ export function BiomarkerSelectSubscriber(props) {
|
|
|
80
135
|
getEdges={getEdges}
|
|
81
136
|
stratifications={stratificationOptions}
|
|
82
137
|
onFinish={() => {
|
|
83
|
-
|
|
138
|
+
if (mode === 'exploratory') {
|
|
139
|
+
// mode is exploratory, configure accordingly.
|
|
140
|
+
|
|
141
|
+
// TODO
|
|
142
|
+
|
|
143
|
+
} else {
|
|
144
|
+
// mode is confirmatory, configure accordingly.
|
|
145
|
+
|
|
146
|
+
// TODO
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const newViewConfig = {
|
|
84
150
|
...viewConfig,
|
|
85
151
|
coordinationSpace: {
|
|
86
152
|
...viewConfig.coordinationSpace,
|
|
87
153
|
sampleSetFilter: {
|
|
88
154
|
...viewConfig.coordinationSpace.sampleSetFilter,
|
|
89
|
-
|
|
155
|
+
__comparison__: currentStratificationSelection?.sampleSets,
|
|
90
156
|
},
|
|
91
157
|
sampleSetSelection: {
|
|
92
158
|
...viewConfig.coordinationSpace.sampleSetSelection,
|
|
93
|
-
|
|
159
|
+
__comparison__: currentStratificationSelection?.sampleSets,
|
|
160
|
+
},
|
|
161
|
+
featureSelection: {
|
|
162
|
+
...viewConfig.coordinationSpace.featureSelection,
|
|
163
|
+
__comparison__: currentModalitySpecificSelection ? currentModalitySpecificSelection.map(d => d.label) : null,
|
|
94
164
|
},
|
|
95
165
|
},
|
|
96
|
-
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// TODO: can the normal coordination value setters be used instead?
|
|
169
|
+
// (e.g., setFeatureSelection, setSampleSetSelection)
|
|
170
|
+
setViewConfig(newViewConfig);
|
|
97
171
|
}}
|
|
98
172
|
/>
|
|
99
173
|
) : null}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
useCoordination,
|
|
4
|
+
} from '@vitessce/vit-s';
|
|
5
|
+
import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
|
|
6
|
+
import { makeStyles } from '@material-ui/core';
|
|
7
|
+
|
|
8
|
+
const useStyles = makeStyles(theme => ({
|
|
9
|
+
buttonContainer: {
|
|
10
|
+
position: 'absolute',
|
|
11
|
+
right: 0,
|
|
12
|
+
display: 'flex',
|
|
13
|
+
flexDirection: 'column',
|
|
14
|
+
justifyContent: 'center',
|
|
15
|
+
height: '100%',
|
|
16
|
+
},
|
|
17
|
+
headingContainer: {
|
|
18
|
+
backgroundColor: 'transparent',
|
|
19
|
+
},
|
|
20
|
+
headingSectionContainer: {
|
|
21
|
+
width: '100%',
|
|
22
|
+
display: 'flex',
|
|
23
|
+
flexDirection: 'row',
|
|
24
|
+
position: 'relative',
|
|
25
|
+
},
|
|
26
|
+
headingText: {
|
|
27
|
+
margin: 0,
|
|
28
|
+
color: theme.palette.primaryForeground,
|
|
29
|
+
},
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
export function ComparativeHeadingSubscriber(props) {
|
|
33
|
+
const {
|
|
34
|
+
coordinationScopes,
|
|
35
|
+
} = props;
|
|
36
|
+
|
|
37
|
+
const [{
|
|
38
|
+
sampleSetSelection,
|
|
39
|
+
featureSelection,
|
|
40
|
+
}, {
|
|
41
|
+
setSampleSetSelection,
|
|
42
|
+
// TODO: buttons for modification of featureSelection
|
|
43
|
+
// setFeatureSelection,
|
|
44
|
+
}] = useCoordination(
|
|
45
|
+
COMPONENT_COORDINATION_TYPES[ViewType.COMPARATIVE_HEADING],
|
|
46
|
+
coordinationScopes,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const classes = useStyles();
|
|
50
|
+
|
|
51
|
+
const swapSampleSets = useCallback(() => {
|
|
52
|
+
if (sampleSetSelection?.length === 2) {
|
|
53
|
+
setSampleSetSelection([
|
|
54
|
+
sampleSetSelection[1],
|
|
55
|
+
sampleSetSelection[0],
|
|
56
|
+
]);
|
|
57
|
+
}
|
|
58
|
+
}, [sampleSetSelection, setSampleSetSelection]);
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className={classes.headingContainer}>
|
|
62
|
+
{sampleSetSelection && sampleSetSelection.length === 2 ? (
|
|
63
|
+
<div className={classes.headingSectionContainer}>
|
|
64
|
+
<div style={{ width: '45%' }}><h2 className={classes.headingText}>{sampleSetSelection?.[0]?.at(-1)}</h2></div>
|
|
65
|
+
<div style={{ width: '5%' }}><h2 className={classes.headingText} style={{ textAlign: 'right' }}>vs. </h2></div>
|
|
66
|
+
<div style={{ width: '50%' }}><h2 className={classes.headingText}>{sampleSetSelection?.[1]?.at(-1)}</h2></div>
|
|
67
|
+
<div className={classes.buttonContainer}>
|
|
68
|
+
<button onClick={swapSampleSets} type="button">Swap</button>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
) : null}
|
|
72
|
+
{featureSelection && featureSelection.length > 0 ? (
|
|
73
|
+
<div className={classes.headingSectionContainer}>
|
|
74
|
+
<div><h3 className={classes.headingText}>Selected Biomarkers: </h3></div>
|
|
75
|
+
{featureSelection?.map((featureName, i) => (
|
|
76
|
+
<div>
|
|
77
|
+
<h3 className={classes.headingText}>
|
|
78
|
+
{featureName}
|
|
79
|
+
{i < featureSelection.length - 1 ? (<span>, </span>) : null}
|
|
80
|
+
</h3>
|
|
81
|
+
</div>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
) : null}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -180,6 +180,7 @@ export async function transformFeature({ queryClient }, node, targetModality) {
|
|
|
180
180
|
queryFn: loadPathwayToGeneEdges,
|
|
181
181
|
});
|
|
182
182
|
|
|
183
|
+
// TODO: support matching using ontology term (rather than requiring kgId)?
|
|
183
184
|
const matchingEdges = pathwayGeneEdges.filter((/** @type {KgEdge} */ d) => d.source === node.kgId);
|
|
184
185
|
const matchingGeneIds = matchingEdges.map((/** @type {KgEdge} */ d) => d.target);
|
|
185
186
|
const matchingGenes = geneNodes.filter(d => matchingGeneIds.includes(d.kgId));
|
package/src/index.js
CHANGED
package/src/scmd-ui.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* eslint-disable no-unused-vars */
|
|
2
2
|
import React, { useState } from 'react';
|
|
3
3
|
import { Button, ButtonGroup, Grid, Tooltip } from '@material-ui/core';
|
|
4
|
-
import { ConfirmatoryStepper } from './confirmatory
|
|
4
|
+
import { ConfirmatoryStepper } from './stepper-confirmatory.js';
|
|
5
|
+
import { ExploratoryStepper } from './stepper-exploratory.js';
|
|
5
6
|
import { useStyles } from './styles.js';
|
|
6
7
|
|
|
7
8
|
|
|
@@ -67,7 +68,22 @@ export function ScmdUi(props) {
|
|
|
67
68
|
onFinish={onFinish}
|
|
68
69
|
/>
|
|
69
70
|
) : null}
|
|
70
|
-
{mode === 'exploratory' ?
|
|
71
|
+
{mode === 'exploratory' ? (
|
|
72
|
+
<ExploratoryStepper
|
|
73
|
+
autocompleteNode={autocompleteNode}
|
|
74
|
+
|
|
75
|
+
currentModalityAgnosticSelection={currentModalityAgnosticSelection}
|
|
76
|
+
setCurrentModalityAgnosticSelection={setCurrentModalityAgnosticSelection}
|
|
77
|
+
currentModalitySpecificSelection={currentModalitySpecificSelection}
|
|
78
|
+
setCurrentModalitySpecificSelection={setCurrentModalitySpecificSelection}
|
|
79
|
+
currentStratificationSelection={currentStratificationSelection}
|
|
80
|
+
setCurrentStratificationSelection={setCurrentStratificationSelection}
|
|
81
|
+
|
|
82
|
+
getEdges={getEdges}
|
|
83
|
+
stratifications={stratifications}
|
|
84
|
+
onFinish={onFinish}
|
|
85
|
+
/>
|
|
86
|
+
) : null}
|
|
71
87
|
</div>
|
|
72
88
|
) : null}
|
|
73
89
|
</Grid>
|