@vitessce/biomarker-select 3.5.0
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/LICENSE +21 -0
- package/dist/index.js +32224 -0
- package/dist-tsc/BiomarkerSelectSubscriber.d.ts +14 -0
- package/dist-tsc/BiomarkerSelectSubscriber.d.ts.map +1 -0
- package/dist-tsc/BiomarkerSelectSubscriber.js +53 -0
- package/dist-tsc/cart.d.ts +2 -0
- package/dist-tsc/cart.d.ts.map +1 -0
- package/dist-tsc/cart.js +27 -0
- package/dist-tsc/confirmatory-stepper.d.ts +2 -0
- package/dist-tsc/confirmatory-stepper.d.ts.map +1 -0
- package/dist-tsc/confirmatory-stepper.js +65 -0
- package/dist-tsc/default-async-functions.d.ts +26 -0
- package/dist-tsc/default-async-functions.d.ts.map +1 -0
- package/dist-tsc/default-async-functions.js +168 -0
- package/dist-tsc/index.d.ts +3 -0
- package/dist-tsc/index.d.ts.map +1 -0
- package/dist-tsc/index.js +2 -0
- package/dist-tsc/missing-types.d.ts +3 -0
- package/dist-tsc/missing-types.d.ts.map +1 -0
- package/dist-tsc/missing-types.js +1 -0
- package/dist-tsc/scmd-ui.d.ts +2 -0
- package/dist-tsc/scmd-ui.d.ts.map +1 -0
- package/dist-tsc/scmd-ui.js +15 -0
- package/dist-tsc/select-agnostic.d.ts +2 -0
- package/dist-tsc/select-agnostic.d.ts.map +1 -0
- package/dist-tsc/select-agnostic.js +66 -0
- package/dist-tsc/select-specific.d.ts +2 -0
- package/dist-tsc/select-specific.d.ts.map +1 -0
- package/dist-tsc/select-specific.js +61 -0
- package/dist-tsc/select-stratification.d.ts +2 -0
- package/dist-tsc/select-stratification.d.ts.map +1 -0
- package/dist-tsc/select-stratification.js +18 -0
- package/dist-tsc/styles.d.ts +2 -0
- package/dist-tsc/styles.d.ts.map +1 -0
- package/dist-tsc/styles.js +32 -0
- package/package.json +52 -0
- package/src/BiomarkerSelectSubscriber.js +107 -0
- package/src/cart.js +95 -0
- package/src/confirmatory-stepper.js +209 -0
- package/src/default-async-functions.js +187 -0
- package/src/index.js +5 -0
- package/src/missing-types.ts +2 -0
- package/src/scmd-ui.js +75 -0
- package/src/select-agnostic.js +172 -0
- package/src/select-specific.js +111 -0
- package/src/select-stratification.js +90 -0
- package/src/styles.js +33 -0
package/src/scmd-ui.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { Button, ButtonGroup, Grid, Tooltip } from '@material-ui/core';
|
|
4
|
+
import { ConfirmatoryStepper } from './confirmatory-stepper.js';
|
|
5
|
+
import { useStyles } from './styles.js';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export function ScmdUi(props) {
|
|
9
|
+
const {
|
|
10
|
+
mode: modeProp,
|
|
11
|
+
setMode,
|
|
12
|
+
step: stepProp,
|
|
13
|
+
setStep,
|
|
14
|
+
currentModalityAgnosticSelection,
|
|
15
|
+
setCurrentModalityAgnosticSelection,
|
|
16
|
+
currentModalitySpecificSelection,
|
|
17
|
+
setCurrentModalitySpecificSelection,
|
|
18
|
+
currentStratificationSelection,
|
|
19
|
+
setCurrentStratificationSelection,
|
|
20
|
+
|
|
21
|
+
autocompleteNode,
|
|
22
|
+
|
|
23
|
+
getEdges,
|
|
24
|
+
stratifications,
|
|
25
|
+
onFinish,
|
|
26
|
+
} = props;
|
|
27
|
+
const classes = useStyles();
|
|
28
|
+
|
|
29
|
+
const mode = modeProp || 'confirmatory';
|
|
30
|
+
const step = mode === 'confirmatory' ? (stepProp || 'select-biomarkers') : (stepProp || 'define-stratification');
|
|
31
|
+
|
|
32
|
+
const [tempIsVisible, setTempIsVisible] = useState(true);
|
|
33
|
+
|
|
34
|
+
const isConfirmatoryMode = mode === 'confirmatory';
|
|
35
|
+
return (
|
|
36
|
+
<Grid container style={{ border: '0px solid red' }}>
|
|
37
|
+
{/* Header */}
|
|
38
|
+
<Grid item container xs={12} className={classes.header}>
|
|
39
|
+
<Grid item xs={8} />
|
|
40
|
+
<Grid item container xs={4} justifyContent="flex-end">
|
|
41
|
+
<ButtonGroup variant="outlined" size="small" color="secondary" aria-label="Toggle between confirmatory and exploratory modes">
|
|
42
|
+
<Tooltip arrow title="Start from biomarker(s) of interest">
|
|
43
|
+
<Button variant={isConfirmatoryMode ? 'contained' : 'outlined'} onClick={() => setMode('confirmatory')}>Confirmatory (Hypothesis-driven)</Button>
|
|
44
|
+
</Tooltip>
|
|
45
|
+
<Tooltip arrow title="Lacking biomarker(s) of interest">
|
|
46
|
+
<Button variant={!isConfirmatoryMode ? 'contained' : 'outlined'} onClick={() => setMode('exploratory')}>Exploratory (Hypothesis-generating)</Button>
|
|
47
|
+
</Tooltip>
|
|
48
|
+
</ButtonGroup>
|
|
49
|
+
</Grid>
|
|
50
|
+
</Grid>
|
|
51
|
+
{/* Stepper */}
|
|
52
|
+
{tempIsVisible ? (
|
|
53
|
+
<div style={{ border: '2px solid #eee', borderRadius: '5px' }}>
|
|
54
|
+
{mode === 'confirmatory' ? (
|
|
55
|
+
<ConfirmatoryStepper
|
|
56
|
+
autocompleteNode={autocompleteNode}
|
|
57
|
+
|
|
58
|
+
currentModalityAgnosticSelection={currentModalityAgnosticSelection}
|
|
59
|
+
setCurrentModalityAgnosticSelection={setCurrentModalityAgnosticSelection}
|
|
60
|
+
currentModalitySpecificSelection={currentModalitySpecificSelection}
|
|
61
|
+
setCurrentModalitySpecificSelection={setCurrentModalitySpecificSelection}
|
|
62
|
+
currentStratificationSelection={currentStratificationSelection}
|
|
63
|
+
setCurrentStratificationSelection={setCurrentStratificationSelection}
|
|
64
|
+
|
|
65
|
+
getEdges={getEdges}
|
|
66
|
+
stratifications={stratifications}
|
|
67
|
+
onFinish={onFinish}
|
|
68
|
+
/>
|
|
69
|
+
) : null}
|
|
70
|
+
{mode === 'exploratory' ? <span>TODO</span> : null}
|
|
71
|
+
</div>
|
|
72
|
+
) : null}
|
|
73
|
+
</Grid>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import React, { useRef, useEffect } from 'react';
|
|
2
|
+
import { Grid, Button, TextField, Typography } from '@material-ui/core';
|
|
3
|
+
import { Add as AddIcon, Info as InfoIcon } from '@material-ui/icons';
|
|
4
|
+
import { Autocomplete } from '@material-ui/lab';
|
|
5
|
+
import { VariableSizeList } from 'react-window';
|
|
6
|
+
import { useStyles } from './styles.js';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const LIST_ROW_HEIGHT = 48;
|
|
10
|
+
|
|
11
|
+
// We use a context to pass the extra props from ListboxComponent to the outer div element.
|
|
12
|
+
const OuterElementContext = React.createContext({});
|
|
13
|
+
|
|
14
|
+
const OuterElementType = React.forwardRef((props, ref) => {
|
|
15
|
+
const outerProps = React.useContext(OuterElementContext);
|
|
16
|
+
return <div ref={ref} {...props} {...outerProps} />;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
function ListRow(props) {
|
|
21
|
+
const { data, index, style } = props;
|
|
22
|
+
|
|
23
|
+
return React.cloneElement(data[index], {
|
|
24
|
+
style: {
|
|
25
|
+
...style,
|
|
26
|
+
top: style.top + 8,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function useResetCache(itemCount) {
|
|
32
|
+
const ref = useRef(null);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (ref.current != null) {
|
|
35
|
+
ref.current.resetAfterIndex(0, true);
|
|
36
|
+
}
|
|
37
|
+
}, [itemCount]);
|
|
38
|
+
return ref;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Reference: https://mui.com/material-ui/react-autocomplete/#virtualization
|
|
42
|
+
const ListboxComponent = React.forwardRef((props, ref) => {
|
|
43
|
+
const { children, ...other } = props;
|
|
44
|
+
|
|
45
|
+
const itemCount = children.length;
|
|
46
|
+
const gridRef = useResetCache(itemCount);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div ref={ref}>
|
|
50
|
+
<OuterElementContext.Provider value={other}>
|
|
51
|
+
<VariableSizeList
|
|
52
|
+
height={Math.min(8, itemCount) * LIST_ROW_HEIGHT}
|
|
53
|
+
itemSize={() => LIST_ROW_HEIGHT}
|
|
54
|
+
width="100%"
|
|
55
|
+
innerElementType="ul"
|
|
56
|
+
outerElementType={OuterElementType}
|
|
57
|
+
overscanCount={5}
|
|
58
|
+
ref={gridRef}
|
|
59
|
+
itemCount={itemCount}
|
|
60
|
+
itemData={children}
|
|
61
|
+
>
|
|
62
|
+
{ListRow}
|
|
63
|
+
</VariableSizeList>
|
|
64
|
+
</OuterElementContext.Provider>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export function SelectAgnostic(props) {
|
|
70
|
+
const {
|
|
71
|
+
autocompleteNode,
|
|
72
|
+
currentModalityAgnosticSelection,
|
|
73
|
+
setCurrentModalityAgnosticSelection,
|
|
74
|
+
} = props;
|
|
75
|
+
const classes = useStyles();
|
|
76
|
+
|
|
77
|
+
const [selectedItem, setSelectedItem] = React.useState(null);
|
|
78
|
+
|
|
79
|
+
const [potentialItems, setPotentialItems] = React.useState([]);
|
|
80
|
+
|
|
81
|
+
const handleChange = async (event) => {
|
|
82
|
+
const { value } = event.target;
|
|
83
|
+
if (!value) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const autocompleteResult = await autocompleteNode(value);
|
|
87
|
+
setPotentialItems(autocompleteResult);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
function confirmSelectedItem() {
|
|
92
|
+
// eslint-disable-next-line max-len
|
|
93
|
+
if (selectedItem && !currentModalityAgnosticSelection?.find(item => item.kgId === selectedItem?.kgId)) {
|
|
94
|
+
setCurrentModalityAgnosticSelection([
|
|
95
|
+
...(currentModalityAgnosticSelection || []),
|
|
96
|
+
selectedItem,
|
|
97
|
+
]);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return (
|
|
101
|
+
<>
|
|
102
|
+
<Grid item container xs={12}>
|
|
103
|
+
<Typography variant="h6">
|
|
104
|
+
Search by gene, protein, pathway (by term name), or cell type:
|
|
105
|
+
</Typography>
|
|
106
|
+
</Grid>
|
|
107
|
+
<Grid item container xs={12}>
|
|
108
|
+
<Grid item xs={4}>
|
|
109
|
+
<Autocomplete
|
|
110
|
+
options={potentialItems}
|
|
111
|
+
autoComplete
|
|
112
|
+
includeInputInList
|
|
113
|
+
onInputChange={handleChange}
|
|
114
|
+
onChange={(event, item) => setSelectedItem(item)}
|
|
115
|
+
classes={{ input: classes.searchInput }}
|
|
116
|
+
renderInput={params => (
|
|
117
|
+
<TextField label="Search" variant="outlined" onChange={handleChange} {...params} />
|
|
118
|
+
)}
|
|
119
|
+
ListboxComponent={ListboxComponent}
|
|
120
|
+
getOptionLabel={option => option.label}
|
|
121
|
+
renderOption={option => (
|
|
122
|
+
<Typography noWrap>{option.label} ({option.nodeType})</Typography>
|
|
123
|
+
)}
|
|
124
|
+
/>
|
|
125
|
+
</Grid>
|
|
126
|
+
<Grid
|
|
127
|
+
item
|
|
128
|
+
xs={8}
|
|
129
|
+
style={{
|
|
130
|
+
border: selectedItem ? '1px solid gray' : '1px solid transparent',
|
|
131
|
+
borderRadius: '4px',
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
{selectedItem ? (
|
|
135
|
+
<>
|
|
136
|
+
<Grid container item xs={12}>
|
|
137
|
+
<Grid item xs={9}>
|
|
138
|
+
<Typography variant="h4" title={selectedItem.term}>
|
|
139
|
+
{selectedItem.label}
|
|
140
|
+
</Typography>
|
|
141
|
+
</Grid>
|
|
142
|
+
<Grid item xs={3} style={{ position: 'relative' }}>
|
|
143
|
+
<Button
|
|
144
|
+
className={classes.selectButton}
|
|
145
|
+
variant="contained"
|
|
146
|
+
startIcon={<AddIcon />}
|
|
147
|
+
onClick={confirmSelectedItem}
|
|
148
|
+
>
|
|
149
|
+
Select
|
|
150
|
+
</Button>
|
|
151
|
+
</Grid>
|
|
152
|
+
</Grid>
|
|
153
|
+
<Grid container item xs={12}>
|
|
154
|
+
<InfoIcon />
|
|
155
|
+
<Typography variant="h6">About this {selectedItem.nodeType}</Typography>
|
|
156
|
+
</Grid>
|
|
157
|
+
<Grid container item xs={12} key={selectedItem.term}>
|
|
158
|
+
<iframe
|
|
159
|
+
title={`Embedded metadata page for ontology term ${selectedItem.term}`}
|
|
160
|
+
src={`https://identifiers.org/${selectedItem.term}`}
|
|
161
|
+
width="100%"
|
|
162
|
+
height="500"
|
|
163
|
+
style={{ border: 0 }}
|
|
164
|
+
/>
|
|
165
|
+
</Grid>
|
|
166
|
+
</>
|
|
167
|
+
) : null}
|
|
168
|
+
</Grid>
|
|
169
|
+
</Grid>
|
|
170
|
+
</>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { useQueries } from '@tanstack/react-query';
|
|
3
|
+
import { Grid, FormHelperText, Typography, Select, FormControl } from '@material-ui/core';
|
|
4
|
+
import { DataGrid } from '@mui/x-data-grid';
|
|
5
|
+
import { useStyles } from './styles.js';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
const columns = [
|
|
9
|
+
{
|
|
10
|
+
field: 'targetLabel',
|
|
11
|
+
headerName: 'Gene symbol',
|
|
12
|
+
width: 200,
|
|
13
|
+
editable: false,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
field: 'sourceRelationship',
|
|
17
|
+
headerName: 'Relationship to modality-agnostic selections',
|
|
18
|
+
width: 400,
|
|
19
|
+
editable: false,
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export function SelectSpecific(props) {
|
|
25
|
+
const {
|
|
26
|
+
currentModalityAgnosticSelection,
|
|
27
|
+
currentModalitySpecificSelection,
|
|
28
|
+
setCurrentModalitySpecificSelection,
|
|
29
|
+
getEdges,
|
|
30
|
+
} = props;
|
|
31
|
+
const classes = useStyles();
|
|
32
|
+
|
|
33
|
+
const queries = useQueries({
|
|
34
|
+
queries: currentModalityAgnosticSelection?.map(item => ({
|
|
35
|
+
queryKey: [item.nodeType, item.kgId],
|
|
36
|
+
queryFn: async () => {
|
|
37
|
+
const matchingGenes = await getEdges(item, 'gene');
|
|
38
|
+
return matchingGenes.map(d => ({ target: d, source: item }));
|
|
39
|
+
},
|
|
40
|
+
})) || [],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const anyLoading = queries.some(q => q.isFetching);
|
|
44
|
+
const anyError = queries.some(q => q.isError);
|
|
45
|
+
// eslint-disable-next-line no-nested-ternary
|
|
46
|
+
const dataStatus = anyLoading ? 'loading' : (anyError ? 'error' : 'success');
|
|
47
|
+
|
|
48
|
+
const data = queries
|
|
49
|
+
.flatMap(q => q.data)
|
|
50
|
+
.filter(Boolean);
|
|
51
|
+
|
|
52
|
+
const rows = useMemo(() => data.map(d => ({
|
|
53
|
+
id: d.target.kgId,
|
|
54
|
+
targetLabel: d.target.label,
|
|
55
|
+
sourceRelationship: (d.source.nodeType === 'gene'
|
|
56
|
+
? 'Directly-selected gene'
|
|
57
|
+
: `Member of ${d.source.label} ${d.source.nodeType}`
|
|
58
|
+
),
|
|
59
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
60
|
+
})), [currentModalityAgnosticSelection, dataStatus]);
|
|
61
|
+
|
|
62
|
+
// Derive the set of selected row ids from the
|
|
63
|
+
// currentModalitySpecificSelection.
|
|
64
|
+
const rowSelectionModel = useMemo(
|
|
65
|
+
() => currentModalitySpecificSelection?.map(d => d.kgId) || [],
|
|
66
|
+
[rows, currentModalitySpecificSelection],
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
function handleSelection(newRowSelectionModel) {
|
|
70
|
+
const newSelection = newRowSelectionModel
|
|
71
|
+
.map(kgId => data.find(d => d.target.kgId === kgId)?.target)
|
|
72
|
+
.filter(Boolean);
|
|
73
|
+
setCurrentModalitySpecificSelection(newSelection);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<>
|
|
78
|
+
<Grid item xs={6}>
|
|
79
|
+
<Typography variant="h6">Select a feature type:</Typography>
|
|
80
|
+
</Grid>
|
|
81
|
+
<Grid item container xs={6}>
|
|
82
|
+
<Grid item>
|
|
83
|
+
<FormControl fullWidth>
|
|
84
|
+
<Select
|
|
85
|
+
native
|
|
86
|
+
defaultValue="gene"
|
|
87
|
+
classes={{ select: classes.selectInput }}
|
|
88
|
+
inputProps={{
|
|
89
|
+
name: 'age',
|
|
90
|
+
id: 'uncontrolled-native',
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<option value="gene">Gene (RNA-seq)</option>
|
|
94
|
+
</Select>
|
|
95
|
+
<FormHelperText>Feature type (experimental modality)</FormHelperText>
|
|
96
|
+
</FormControl>
|
|
97
|
+
</Grid>
|
|
98
|
+
</Grid>
|
|
99
|
+
<Grid item container xs={12} style={{ height: '450px' }}>
|
|
100
|
+
<DataGrid
|
|
101
|
+
rows={rows}
|
|
102
|
+
columns={columns}
|
|
103
|
+
pageSize={10}
|
|
104
|
+
checkboxSelection
|
|
105
|
+
onSelectionModelChange={handleSelection}
|
|
106
|
+
rowSelectionModel={rowSelectionModel}
|
|
107
|
+
/>
|
|
108
|
+
</Grid>
|
|
109
|
+
</>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Grid, RadioGroup, FormControl, FormLabel, Radio, FormControlLabel, Typography } from '@material-ui/core';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export function SelectStratification(props) {
|
|
6
|
+
const {
|
|
7
|
+
stratifications,
|
|
8
|
+
currentStratificationSelection: currentStratificationSelectionProp,
|
|
9
|
+
setCurrentStratificationSelection: setCurrentStratificationSelectionProp,
|
|
10
|
+
} = props;
|
|
11
|
+
|
|
12
|
+
// Convert back and forth from the special value '__all__' to null.
|
|
13
|
+
const currentStratificationSelection = currentStratificationSelectionProp === null
|
|
14
|
+
? '__all__'
|
|
15
|
+
: currentStratificationSelectionProp.stratificationId;
|
|
16
|
+
function setCurrentStratificationSelection(event, value) {
|
|
17
|
+
setCurrentStratificationSelectionProp(
|
|
18
|
+
stratifications.find(s => s.stratificationId === value) ?? null,
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const sampleSetOptions = stratifications?.filter(s => s.stratificationType === 'sampleSet');
|
|
23
|
+
const structuralRegionOptions = stratifications?.filter(s => s.stratificationType === 'structural-region');
|
|
24
|
+
|
|
25
|
+
const hasSampleSetOptions = sampleSetOptions && sampleSetOptions.length > 0;
|
|
26
|
+
const hasStructuralRegionOptions = structuralRegionOptions && structuralRegionOptions.length > 0;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Grid item xs={12}>
|
|
30
|
+
<Typography variant="h6">
|
|
31
|
+
Select a stratification option:
|
|
32
|
+
</Typography>
|
|
33
|
+
<FormControl>
|
|
34
|
+
<RadioGroup
|
|
35
|
+
aria-labelledby="demo-radio-buttons-group-label"
|
|
36
|
+
value={currentStratificationSelection}
|
|
37
|
+
onChange={setCurrentStratificationSelection}
|
|
38
|
+
name="radio-buttons-group"
|
|
39
|
+
>
|
|
40
|
+
<FormControlLabel value="__all__" control={<Radio />} label="No stratification" />
|
|
41
|
+
{hasSampleSetOptions ? (
|
|
42
|
+
<>
|
|
43
|
+
<FormLabel>By participant group (clinical):</FormLabel>
|
|
44
|
+
{sampleSetOptions.map(s => (
|
|
45
|
+
<FormControlLabel
|
|
46
|
+
value={s.stratificationId}
|
|
47
|
+
control={<Radio />}
|
|
48
|
+
key={s.name}
|
|
49
|
+
label={s.name}
|
|
50
|
+
/>
|
|
51
|
+
))}
|
|
52
|
+
</>
|
|
53
|
+
) : null}
|
|
54
|
+
{hasStructuralRegionOptions ? (
|
|
55
|
+
<>
|
|
56
|
+
<FormLabel>By segmentation-defined spatial region (structural):</FormLabel>
|
|
57
|
+
{structuralRegionOptions.map(s => (
|
|
58
|
+
<FormControlLabel
|
|
59
|
+
value={s.stratificationId}
|
|
60
|
+
control={<Radio />}
|
|
61
|
+
key={s.name}
|
|
62
|
+
label={s.name}
|
|
63
|
+
/>
|
|
64
|
+
))}
|
|
65
|
+
</>
|
|
66
|
+
) : null}
|
|
67
|
+
</RadioGroup>
|
|
68
|
+
</FormControl>
|
|
69
|
+
{/* // TODO: store participant-level and region-level stratification options independently
|
|
70
|
+
<Typography variant="h6">
|
|
71
|
+
Define case-control stratification (region-level):
|
|
72
|
+
</Typography>
|
|
73
|
+
<FormControl>
|
|
74
|
+
<RadioGroup
|
|
75
|
+
aria-labelledby="demo-radio-buttons-group-label-2"
|
|
76
|
+
value={currentStratificationSelection}
|
|
77
|
+
onChange={setCurrentStratificationSelection}
|
|
78
|
+
name="radio-buttons-group-2"
|
|
79
|
+
>
|
|
80
|
+
<FormLabel>By segmentation-defined spatial region (structural):</FormLabel>
|
|
81
|
+
{stratifications ? stratifications
|
|
82
|
+
.filter(s => s.stratificationType === 'structural-region').map((s) => (
|
|
83
|
+
<FormControlLabel value={s.stratificationId} control={<Radio />} label={s.name} />
|
|
84
|
+
)) : null}
|
|
85
|
+
</RadioGroup>
|
|
86
|
+
</FormControl>
|
|
87
|
+
*/}
|
|
88
|
+
</Grid>
|
|
89
|
+
);
|
|
90
|
+
}
|
package/src/styles.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { makeStyles } from '@material-ui/core';
|
|
2
|
+
|
|
3
|
+
export const useStyles = makeStyles(() => ({
|
|
4
|
+
header: {
|
|
5
|
+
marginTop: '10px',
|
|
6
|
+
marginBottom: '10px',
|
|
7
|
+
},
|
|
8
|
+
fullWidthBox: {
|
|
9
|
+
width: '100%',
|
|
10
|
+
},
|
|
11
|
+
selectButton: {
|
|
12
|
+
position: 'absolute',
|
|
13
|
+
right: 0,
|
|
14
|
+
},
|
|
15
|
+
accordion: {
|
|
16
|
+
width: '100%',
|
|
17
|
+
margin: '0 !important',
|
|
18
|
+
},
|
|
19
|
+
accordionDetails: {
|
|
20
|
+
paddingTop: 0,
|
|
21
|
+
},
|
|
22
|
+
cartUl: {
|
|
23
|
+
marginTop: 0,
|
|
24
|
+
marginBottom: 0,
|
|
25
|
+
},
|
|
26
|
+
selectInput: {
|
|
27
|
+
height: 'auto !important',
|
|
28
|
+
},
|
|
29
|
+
searchInput: {
|
|
30
|
+
lineHeight: 'initial',
|
|
31
|
+
height: 'auto !important',
|
|
32
|
+
},
|
|
33
|
+
}));
|