@vitessce/biomarker-select 3.5.8 → 3.5.10

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ComparativeHeadingSubscriber.d.ts","sourceRoot":"","sources":["../src/ComparativeHeadingSubscriber.js"],"names":[],"mappings":"AAOA,sEAuBC"}
1
+ {"version":3,"file":"ComparativeHeadingSubscriber.d.ts","sourceRoot":"","sources":["../src/ComparativeHeadingSubscriber.js"],"names":[],"mappings":"AAsCA,sEA8EC"}
@@ -1,9 +1,59 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import React from 'react';
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useCallback } from 'react';
3
3
  import { useCoordination, } from '@vitessce/vit-s';
4
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
+ '& div': {
15
+ display: 'flex',
16
+ flexDirection: 'row',
17
+ },
18
+ '& div > button': {
19
+ height: 'auto',
20
+ },
21
+ },
22
+ headingContainer: {
23
+ backgroundColor: 'transparent',
24
+ },
25
+ headingSectionContainer: {
26
+ width: '100%',
27
+ display: 'flex',
28
+ flexDirection: 'row',
29
+ position: 'relative',
30
+ },
31
+ headingText: {
32
+ margin: 0,
33
+ color: theme.palette.primaryForeground,
34
+ },
35
+ }));
5
36
  export function ComparativeHeadingSubscriber(props) {
6
37
  const { coordinationScopes, } = props;
7
- const [{ sampleSetSelection, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.COMPARATIVE_HEADING], coordinationScopes);
8
- return (_jsx(_Fragment, { children: sampleSetSelection && sampleSetSelection.length === 2 ? (_jsxs("div", { style: { width: '100%', display: 'flex', flexDirection: 'row' }, children: [_jsx("div", { style: { width: '45%' }, children: _jsx("h2", { children: sampleSetSelection?.[0]?.at(-1) }) }), _jsx("div", { style: { width: '5%' }, children: _jsx("h2", { style: { textAlign: 'right' }, children: "vs.\u00A0" }) }), _jsx("div", { style: { width: '50%' }, children: _jsx("h2", { children: sampleSetSelection?.[1]?.at(-1) }) })] })) : null }));
38
+ const [{ sampleSetSelection, featureSelection, }, { setSampleSetSelection, setFeatureSelection, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.COMPARATIVE_HEADING], coordinationScopes);
39
+ const classes = useStyles();
40
+ const swapSampleSets = useCallback(() => {
41
+ if (sampleSetSelection?.length === 2) {
42
+ setSampleSetSelection([
43
+ sampleSetSelection[1],
44
+ sampleSetSelection[0],
45
+ ]);
46
+ }
47
+ }, [sampleSetSelection, setSampleSetSelection]);
48
+ const clearSampleSetSelection = useCallback(() => {
49
+ if (sampleSetSelection?.length === 2) {
50
+ setSampleSetSelection(null);
51
+ }
52
+ }, [sampleSetSelection, setSampleSetSelection]);
53
+ const clearFeatureSelection = useCallback(() => {
54
+ if (Array.isArray(featureSelection) && featureSelection.length > 0) {
55
+ setFeatureSelection(null);
56
+ }
57
+ }, [featureSelection, setFeatureSelection]);
58
+ 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: _jsxs("div", { children: [_jsx("button", { onClick: swapSampleSets, type: "button", children: "Swap" }), _jsx("button", { onClick: clearSampleSetSelection, type: "button", children: "Clear" })] }) })] })) : (_jsx("div", { className: classes.headingSectionContainer, children: _jsx("div", { children: _jsx("h2", { className: classes.headingText, children: "All samples" }) }) })), 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] }) }, featureName))), _jsx("div", { className: classes.buttonContainer, children: _jsx("div", { children: _jsx("button", { onClick: clearFeatureSelection, type: "button", children: "Clear" }) }) })] })) : null] }));
9
59
  }
@@ -0,0 +1,2 @@
1
+ export function SampleSetPairManagerSubscriber(props: any): JSX.Element;
2
+ //# sourceMappingURL=SampleSetPairManagerSubscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SampleSetPairManagerSubscriber.d.ts","sourceRoot":"","sources":["../src/SampleSetPairManagerSubscriber.js"],"names":[],"mappings":"AA6BA,wEAoHC"}
@@ -0,0 +1,76 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useMemo, useCallback } from 'react';
3
+ import { isEqual } from 'lodash-es';
4
+ import clsx from 'clsx';
5
+ import { makeStyles } from '@material-ui/core';
6
+ import { TitleInfo, useReady, useCoordination, useLoaders, useComparisonMetadata, useMatchingLoader, useColumnNameMapping, } from '@vitessce/vit-s';
7
+ import { ViewType, DataType, COMPONENT_COORDINATION_TYPES, ViewHelpMapping } from '@vitessce/constants-internal';
8
+ const useStyles = makeStyles(() => ({
9
+ selectedPair: {
10
+ fontWeight: 'bold',
11
+ },
12
+ pairUl: {
13
+ paddingLeft: '15px',
14
+ marginTop: 0,
15
+ fontSize: '12px',
16
+ '& button': {
17
+ fontSize: '12px',
18
+ },
19
+ },
20
+ }));
21
+ export function SampleSetPairManagerSubscriber(props) {
22
+ const { coordinationScopes, removeGridComponent, theme, title = 'Sample Sets', closeButtonVisible, helpText = ViewHelpMapping.SAMPLE_SET_PAIR_MANAGER, } = props;
23
+ const classes = useStyles();
24
+ const loaders = useLoaders();
25
+ // Get "props" from the coordination space.
26
+ const [{ dataset, obsType, sampleType, sampleSetSelection, }, { setSampleSetSelection, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.SAMPLE_SET_PAIR_MANAGER], coordinationScopes);
27
+ const sampleSetsLoader = useMatchingLoader(loaders, dataset, DataType.SAMPLE_SETS, { sampleType });
28
+ const sampleSetsColumnNameMappingReversed = useColumnNameMapping(sampleSetsLoader, true);
29
+ const [{ comparisonMetadata }, cmpMetadataStatus] = useComparisonMetadata(loaders, dataset, false, {}, {}, { obsType, sampleType });
30
+ const isReady = useReady([
31
+ cmpMetadataStatus,
32
+ ]);
33
+ const stratificationOptions = useMemo(() => {
34
+ /*
35
+ return array of objects like {
36
+ stratificationId: 'aki-vs-hr',
37
+ name: 'Acute kidney injury (AKI) vs. Healthy reference',
38
+ stratificationType: 'sampleSet',
39
+ sampleSets: [
40
+ ['Disease Type', 'AKI'],
41
+ ['Disease Type', 'Reference'],
42
+ ],
43
+ },
44
+ */
45
+ if (comparisonMetadata?.sample_group_pairs) {
46
+ return comparisonMetadata.sample_group_pairs.map((sampleGroupPair) => {
47
+ const [sampleGroupCol, sampleGroupValues] = sampleGroupPair;
48
+ const [sampleGroupCtrl, sampleGroupCase] = sampleGroupValues;
49
+ const groupName = sampleSetsColumnNameMappingReversed?.[sampleGroupCol];
50
+ return {
51
+ stratificationId: `${sampleGroupCol}_${sampleGroupCtrl}-vs-${sampleGroupCase}`,
52
+ name: `${groupName}: ${sampleGroupCtrl} vs. ${sampleGroupCase}`,
53
+ stratificationType: 'sampleSet',
54
+ sampleSets: [
55
+ // With sampleSets coming from the comparison_metadata,
56
+ // need to use loader options from obsSets and sampleSets to get mapping
57
+ // from column name to group name.
58
+ [groupName, sampleGroupCtrl],
59
+ [groupName, sampleGroupCase],
60
+ ],
61
+ };
62
+ });
63
+ }
64
+ return null;
65
+ }, [comparisonMetadata, sampleSetsColumnNameMappingReversed]);
66
+ const onSelectPair = useCallback((sampleSetPair) => {
67
+ setSampleSetSelection(sampleSetPair);
68
+ }, [sampleSetSelection, setSampleSetSelection]);
69
+ return (_jsx(TitleInfo, { title: title, closeButtonVisible: closeButtonVisible, removeGridComponent: removeGridComponent, isScroll: true, theme: theme, isReady: isReady, helpText: helpText, children: _jsx("ul", { className: classes.pairUl, children: stratificationOptions?.map((pairObj) => {
70
+ const isSelected = Array.isArray(sampleSetSelection)
71
+ && sampleSetSelection.length === 2
72
+ && (isEqual(pairObj.sampleSets, sampleSetSelection)
73
+ || isEqual(pairObj.sampleSets, [sampleSetSelection?.[1], sampleSetSelection?.[0]]));
74
+ return (_jsxs("li", { className: clsx({ [classes.selectedPair]: isSelected }), children: [pairObj.name, "\u00A0", !isSelected ? (_jsx("button", { type: "button", onClick: () => onSelectPair(pairObj.sampleSets), children: "Select" })) : null] }, pairObj.stratificationId));
75
+ }) }) }));
76
+ }
@@ -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,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"}
1
+ {"version":3,"file":"default-async-functions.d.ts","sourceRoot":"","sources":["../src/default-async-functions.js"],"names":[],"mappings":"AAyHA;;;;;;;GAOG;AACH,qDALG;IAAyB,WAAW,EAA5B,WAAW;CACnB,WAAQ,MAAM,kBACN,IAAI,GAAG,kBAAkB,GACvB,OAAO,CAAC,MAAM,EAAE,CAAC,CAiB7B;AAkBD;;;;;;;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;iCAjQsD,uBAAuB;wCADsE,iBAAiB;4BAAjB,iBAAiB"}
@@ -44,14 +44,18 @@ function loadCellTypeNodes() {
44
44
  * @returns {Promise<KgNode[]>}
45
45
  */
46
46
  function loadPathwayNodes() {
47
- return fetch(`${KG_BASE_URL}/Reactome_2022.nodes.csv`)
48
- .then(res => res.text())
47
+ const reactomeNodes = fetch(`${KG_BASE_URL}/Reactome_2022.nodes.csv`);
48
+ // TODO: load both GO and Reactome nodes, concat together.
49
+ // const goNodes = fetch(`${KG_BASE_URL}/GO_Biological_Process_2021.nodes.csv`);
50
+ return reactomeNodes.then(res => res.text())
49
51
  .then((res) => {
50
52
  const result = csvParse(res);
51
53
  return result.map((/** @type {any} */ d) => ({
52
54
  kgId: d.id,
53
- label: d.pathway,
54
- term: `reactome:${d.acc}`,
55
+ label: d.pathway, // For reactome
56
+ term: `reactome:${d.acc}`, // For reactome
57
+ // label: d.ontology_label, // For GO_BP
58
+ // term: d.acc, // For GO_BP
55
59
  nodeType: 'pathway',
56
60
  }));
57
61
  });
@@ -124,7 +128,10 @@ export async function autocompleteFeature({ queryClient }, partial, targetModali
124
128
  return results.map((/** @type {any} */ result) => result.item);
125
129
  }
126
130
  async function loadPathwayToGeneEdges() {
127
- return fetch(`${KG_BASE_URL}/Reactome_2022.Reactome.Gene.edges.csv`)
131
+ const reactomeEdges = fetch(`${KG_BASE_URL}/Reactome_2022.Reactome.Gene.edges.csv`);
132
+ // TODO: load both GO and Reactome edges, concat together.
133
+ // const goEdges = fetch(`${KG_BASE_URL}/GO_Biological_Process_2021.GO_BP.Gene.edges.csv`);
134
+ return reactomeEdges
128
135
  .then(res => res.text())
129
136
  .then((res) => {
130
137
  const result = csvParse(res);
@@ -1,4 +1,5 @@
1
1
  export { BiomarkerSelectSubscriber } from "./BiomarkerSelectSubscriber.js";
2
2
  export { ComparativeHeadingSubscriber } from "./ComparativeHeadingSubscriber.js";
3
+ export { SampleSetPairManagerSubscriber } from "./SampleSetPairManagerSubscriber.js";
3
4
  export { autocompleteFeature, transformFeature, getAlternativeTerms, getTermMapping } from "./default-async-functions.js";
4
5
  //# sourceMappingURL=index.d.ts.map
package/dist-tsc/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { BiomarkerSelectSubscriber } from './BiomarkerSelectSubscriber.js';
2
2
  export { ComparativeHeadingSubscriber } from './ComparativeHeadingSubscriber.js';
3
+ export { SampleSetPairManagerSubscriber } from './SampleSetPairManagerSubscriber.js';
3
4
  export { autocompleteFeature, transformFeature, getAlternativeTerms, getTermMapping, } from './default-async-functions.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/biomarker-select",
3
- "version": "3.5.8",
3
+ "version": "3.5.10",
4
4
  "author": "Gehlenborg Lab",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
@@ -24,9 +24,10 @@
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.8",
28
- "@vitessce/vit-s": "3.5.8",
29
- "@vitessce/types": "3.5.8"
27
+ "clsx": "^1.1.1",
28
+ "@vitessce/constants-internal": "3.5.10",
29
+ "@vitessce/vit-s": "3.5.10",
30
+ "@vitessce/types": "3.5.10"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@testing-library/jest-dom": "^5.16.4",
@@ -1,9 +1,40 @@
1
- import React from 'react';
1
+ import React, { useCallback } from 'react';
2
2
  import {
3
3
  useCoordination,
4
4
  } from '@vitessce/vit-s';
5
5
  import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
6
+ import { makeStyles } from '@material-ui/core';
6
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
+ '& div': {
17
+ display: 'flex',
18
+ flexDirection: 'row',
19
+ },
20
+ '& div > button': {
21
+ height: 'auto',
22
+ },
23
+ },
24
+ headingContainer: {
25
+ backgroundColor: 'transparent',
26
+ },
27
+ headingSectionContainer: {
28
+ width: '100%',
29
+ display: 'flex',
30
+ flexDirection: 'row',
31
+ position: 'relative',
32
+ },
33
+ headingText: {
34
+ margin: 0,
35
+ color: theme.palette.primaryForeground,
36
+ },
37
+ }));
7
38
 
8
39
  export function ComparativeHeadingSubscriber(props) {
9
40
  const {
@@ -12,20 +43,75 @@ export function ComparativeHeadingSubscriber(props) {
12
43
 
13
44
  const [{
14
45
  sampleSetSelection,
46
+ featureSelection,
47
+ }, {
48
+ setSampleSetSelection,
49
+ setFeatureSelection,
15
50
  }] = useCoordination(
16
51
  COMPONENT_COORDINATION_TYPES[ViewType.COMPARATIVE_HEADING],
17
52
  coordinationScopes,
18
53
  );
19
54
 
55
+ const classes = useStyles();
56
+
57
+ const swapSampleSets = useCallback(() => {
58
+ if (sampleSetSelection?.length === 2) {
59
+ setSampleSetSelection([
60
+ sampleSetSelection[1],
61
+ sampleSetSelection[0],
62
+ ]);
63
+ }
64
+ }, [sampleSetSelection, setSampleSetSelection]);
65
+
66
+ const clearSampleSetSelection = useCallback(() => {
67
+ if (sampleSetSelection?.length === 2) {
68
+ setSampleSetSelection(null);
69
+ }
70
+ }, [sampleSetSelection, setSampleSetSelection]);
71
+
72
+ const clearFeatureSelection = useCallback(() => {
73
+ if (Array.isArray(featureSelection) && featureSelection.length > 0) {
74
+ setFeatureSelection(null);
75
+ }
76
+ }, [featureSelection, setFeatureSelection]);
77
+
20
78
  return (
21
- <>
79
+ <div className={classes.headingContainer}>
22
80
  {sampleSetSelection && sampleSetSelection.length === 2 ? (
23
- <div style={{ width: '100%', display: 'flex', flexDirection: 'row' }}>
24
- <div style={{ width: '45%' }}><h2>{sampleSetSelection?.[0]?.at(-1)}</h2></div>
25
- <div style={{ width: '5%' }}><h2 style={{ textAlign: 'right' }}>vs.&nbsp;</h2></div>
26
- <div style={{ width: '50%' }}><h2>{sampleSetSelection?.[1]?.at(-1)}</h2></div>
81
+ <div className={classes.headingSectionContainer}>
82
+ <div style={{ width: '45%' }}><h2 className={classes.headingText}>{sampleSetSelection?.[0]?.at(-1)}</h2></div>
83
+ <div style={{ width: '5%' }}><h2 className={classes.headingText} style={{ textAlign: 'right' }}>vs.&nbsp;</h2></div>
84
+ <div style={{ width: '50%' }}><h2 className={classes.headingText}>{sampleSetSelection?.[1]?.at(-1)}</h2></div>
85
+ <div className={classes.buttonContainer}>
86
+ <div>
87
+ <button onClick={swapSampleSets} type="button">Swap</button>
88
+ <button onClick={clearSampleSetSelection} type="button">Clear</button>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ ) : (
93
+ <div className={classes.headingSectionContainer}>
94
+ <div><h2 className={classes.headingText}>All samples</h2></div>
95
+ </div>
96
+ )}
97
+ {featureSelection && featureSelection.length > 0 ? (
98
+ <div className={classes.headingSectionContainer}>
99
+ <div><h3 className={classes.headingText}>Selected Biomarkers:&nbsp;</h3></div>
100
+ {featureSelection?.map((featureName, i) => (
101
+ <div key={featureName}>
102
+ <h3 className={classes.headingText}>
103
+ {featureName}
104
+ {i < featureSelection.length - 1 ? (<span>,&nbsp;</span>) : null}
105
+ </h3>
106
+ </div>
107
+ ))}
108
+ <div className={classes.buttonContainer}>
109
+ <div>
110
+ <button onClick={clearFeatureSelection} type="button">Clear</button>
111
+ </div>
112
+ </div>
27
113
  </div>
28
114
  ) : null}
29
- </>
115
+ </div>
30
116
  );
31
117
  }
@@ -0,0 +1,146 @@
1
+ import React, { useMemo, useCallback } from 'react';
2
+ import { isEqual } from 'lodash-es';
3
+ import clsx from 'clsx';
4
+ import { makeStyles } from '@material-ui/core';
5
+ import {
6
+ TitleInfo,
7
+ useReady,
8
+ useCoordination,
9
+ useLoaders,
10
+ useComparisonMetadata,
11
+ useMatchingLoader,
12
+ useColumnNameMapping,
13
+ } from '@vitessce/vit-s';
14
+ import { ViewType, DataType, COMPONENT_COORDINATION_TYPES, ViewHelpMapping } from '@vitessce/constants-internal';
15
+
16
+ const useStyles = makeStyles(() => ({
17
+ selectedPair: {
18
+ fontWeight: 'bold',
19
+ },
20
+ pairUl: {
21
+ paddingLeft: '15px',
22
+ marginTop: 0,
23
+ fontSize: '12px',
24
+ '& button': {
25
+ fontSize: '12px',
26
+ },
27
+ },
28
+ }));
29
+
30
+ export function SampleSetPairManagerSubscriber(props) {
31
+ const {
32
+ coordinationScopes,
33
+ removeGridComponent,
34
+ theme,
35
+ title = 'Sample Sets',
36
+ closeButtonVisible,
37
+ helpText = ViewHelpMapping.SAMPLE_SET_PAIR_MANAGER,
38
+ } = props;
39
+
40
+ const classes = useStyles();
41
+ const loaders = useLoaders();
42
+
43
+ // Get "props" from the coordination space.
44
+ const [{
45
+ dataset,
46
+ obsType,
47
+ sampleType,
48
+ sampleSetSelection,
49
+ }, {
50
+ setSampleSetSelection,
51
+ }] = useCoordination(
52
+ COMPONENT_COORDINATION_TYPES[ViewType.SAMPLE_SET_PAIR_MANAGER],
53
+ coordinationScopes,
54
+ );
55
+
56
+ const sampleSetsLoader = useMatchingLoader(
57
+ loaders, dataset, DataType.SAMPLE_SETS, { sampleType },
58
+ );
59
+ const sampleSetsColumnNameMappingReversed = useColumnNameMapping(sampleSetsLoader, true);
60
+
61
+ const [{ comparisonMetadata }, cmpMetadataStatus] = useComparisonMetadata(
62
+ loaders, dataset, false, {}, {}, { obsType, sampleType },
63
+ );
64
+
65
+ const isReady = useReady([
66
+ cmpMetadataStatus,
67
+ ]);
68
+
69
+ const stratificationOptions = useMemo(() => {
70
+ /*
71
+ return array of objects like {
72
+ stratificationId: 'aki-vs-hr',
73
+ name: 'Acute kidney injury (AKI) vs. Healthy reference',
74
+ stratificationType: 'sampleSet',
75
+ sampleSets: [
76
+ ['Disease Type', 'AKI'],
77
+ ['Disease Type', 'Reference'],
78
+ ],
79
+ },
80
+ */
81
+ if (comparisonMetadata?.sample_group_pairs) {
82
+ return comparisonMetadata.sample_group_pairs.map((sampleGroupPair) => {
83
+ const [sampleGroupCol, sampleGroupValues] = sampleGroupPair;
84
+ const [sampleGroupCtrl, sampleGroupCase] = sampleGroupValues;
85
+ const groupName = sampleSetsColumnNameMappingReversed?.[sampleGroupCol];
86
+ return {
87
+ stratificationId: `${sampleGroupCol}_${sampleGroupCtrl}-vs-${sampleGroupCase}`,
88
+ name: `${groupName}: ${sampleGroupCtrl} vs. ${sampleGroupCase}`,
89
+ stratificationType: 'sampleSet',
90
+ sampleSets: [
91
+ // With sampleSets coming from the comparison_metadata,
92
+ // need to use loader options from obsSets and sampleSets to get mapping
93
+ // from column name to group name.
94
+ [groupName, sampleGroupCtrl],
95
+ [groupName, sampleGroupCase],
96
+ ],
97
+ };
98
+ });
99
+ }
100
+ return null;
101
+ }, [comparisonMetadata, sampleSetsColumnNameMappingReversed]);
102
+
103
+ const onSelectPair = useCallback((sampleSetPair) => {
104
+ setSampleSetSelection(sampleSetPair);
105
+ }, [sampleSetSelection, setSampleSetSelection]);
106
+
107
+
108
+ return (
109
+ <TitleInfo
110
+ title={title}
111
+ closeButtonVisible={closeButtonVisible}
112
+ removeGridComponent={removeGridComponent}
113
+ isScroll
114
+ theme={theme}
115
+ isReady={isReady}
116
+ helpText={helpText}
117
+ >
118
+ <ul className={classes.pairUl}>
119
+ {stratificationOptions?.map((pairObj) => {
120
+ const isSelected = Array.isArray(sampleSetSelection)
121
+ && sampleSetSelection.length === 2
122
+ && (
123
+ isEqual(pairObj.sampleSets, sampleSetSelection)
124
+ || isEqual(pairObj.sampleSets, [sampleSetSelection?.[1], sampleSetSelection?.[0]])
125
+ );
126
+ return (
127
+ <li
128
+ key={pairObj.stratificationId}
129
+ className={clsx({ [classes.selectedPair]: isSelected })}
130
+ >
131
+ {pairObj.name}&nbsp;
132
+ {!isSelected ? (
133
+ <button
134
+ type="button"
135
+ onClick={() => onSelectPair(pairObj.sampleSets)}
136
+ >
137
+ Select
138
+ </button>
139
+ ) : null}
140
+ </li>
141
+ );
142
+ })}
143
+ </ul>
144
+ </TitleInfo>
145
+ );
146
+ }
@@ -49,14 +49,18 @@ function loadCellTypeNodes() {
49
49
  * @returns {Promise<KgNode[]>}
50
50
  */
51
51
  function loadPathwayNodes() {
52
- return fetch(`${KG_BASE_URL}/Reactome_2022.nodes.csv`)
53
- .then(res => res.text())
52
+ const reactomeNodes = fetch(`${KG_BASE_URL}/Reactome_2022.nodes.csv`);
53
+ // TODO: load both GO and Reactome nodes, concat together.
54
+ // const goNodes = fetch(`${KG_BASE_URL}/GO_Biological_Process_2021.nodes.csv`);
55
+ return reactomeNodes.then(res => res.text())
54
56
  .then((res) => {
55
57
  const result = csvParse(res);
56
58
  return result.map((/** @type {any} */ d) => ({
57
59
  kgId: d.id,
58
- label: d.pathway,
59
- term: `reactome:${d.acc}`,
60
+ label: d.pathway, // For reactome
61
+ term: `reactome:${d.acc}`, // For reactome
62
+ // label: d.ontology_label, // For GO_BP
63
+ // term: d.acc, // For GO_BP
60
64
  nodeType: 'pathway',
61
65
  }));
62
66
  });
@@ -141,7 +145,10 @@ export async function autocompleteFeature({ queryClient }, partial, targetModali
141
145
  }
142
146
 
143
147
  async function loadPathwayToGeneEdges() {
144
- return fetch(`${KG_BASE_URL}/Reactome_2022.Reactome.Gene.edges.csv`)
148
+ const reactomeEdges = fetch(`${KG_BASE_URL}/Reactome_2022.Reactome.Gene.edges.csv`);
149
+ // TODO: load both GO and Reactome edges, concat together.
150
+ // const goEdges = fetch(`${KG_BASE_URL}/GO_Biological_Process_2021.GO_BP.Gene.edges.csv`);
151
+ return reactomeEdges
145
152
  .then(res => res.text())
146
153
  .then((res) => {
147
154
  const result = csvParse(res);
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { BiomarkerSelectSubscriber } from './BiomarkerSelectSubscriber.js';
2
2
  export { ComparativeHeadingSubscriber } from './ComparativeHeadingSubscriber.js';
3
+ export { SampleSetPairManagerSubscriber } from './SampleSetPairManagerSubscriber.js';
3
4
  export {
4
5
  autocompleteFeature,
5
6
  transformFeature,