@weng-lab/genomebrowser-ui 0.3.6 → 0.4.0-beta.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/.env.local +1 -1
- package/dist/TrackSelect/Folders/biosamples/shared/BiosampleViewSelector.d.ts +7 -0
- package/dist/TrackSelect/Folders/biosamples/shared/createFolder.d.ts +1 -13
- package/dist/TrackSelect/Folders/biosamples/shared/toTrack.d.ts +20 -0
- package/dist/TrackSelect/Folders/biosamples/shared/types.d.ts +4 -13
- package/dist/TrackSelect/Folders/genes/shared/createFolder.d.ts +1 -3
- package/dist/TrackSelect/Folders/genes/shared/toTrack.d.ts +18 -0
- package/dist/TrackSelect/Folders/index.d.ts +6 -12
- package/dist/TrackSelect/Folders/mohd/data/human.json.d.ts +2948 -0
- package/dist/TrackSelect/Folders/mohd/human.d.ts +1 -0
- package/dist/TrackSelect/Folders/mohd/shared/MohdGroupingCell.d.ts +2 -0
- package/dist/TrackSelect/Folders/mohd/shared/MohdTreeItem.d.ts +3 -0
- package/dist/TrackSelect/Folders/mohd/shared/MohdViewSelector.d.ts +7 -0
- package/dist/TrackSelect/Folders/mohd/shared/columns.d.ts +5 -0
- package/dist/TrackSelect/Folders/mohd/shared/config.d.ts +42 -0
- package/dist/TrackSelect/Folders/mohd/shared/createFolder.d.ts +9 -0
- package/dist/TrackSelect/Folders/mohd/shared/toTrack.d.ts +9 -0
- package/dist/TrackSelect/Folders/mohd/shared/types.d.ts +40 -0
- package/dist/TrackSelect/Folders/other-tracks/shared/toTrack.d.ts +5 -0
- package/dist/TrackSelect/Folders/other-tracks/shared/types.d.ts +1 -0
- package/dist/TrackSelect/Folders/types.d.ts +23 -55
- package/dist/TrackSelect/TrackSelect.d.ts +10 -7
- package/dist/TrackSelect/TreeView/TreeViewWrapper.d.ts +1 -1
- package/dist/TrackSelect/buildSelectedTree.d.ts +15 -0
- package/dist/TrackSelect/managedTracks.d.ts +13 -0
- package/dist/TrackSelect/resolveFolderView.d.ts +2 -0
- package/dist/TrackSelect/trackContext.d.ts +5 -0
- package/dist/TrackSelect/types.d.ts +12 -33
- package/dist/genomebrowser-ui.es.js +2224 -1717
- package/dist/genomebrowser-ui.es.js.map +1 -1
- package/dist/lib.d.ts +4 -4
- package/dist/muiLicense.d.ts +1 -0
- package/package.json +6 -3
- package/src/TrackSelect/Dialogs/ResetDialog.tsx +3 -2
- package/src/TrackSelect/FolderList/FolderCard.tsx +1 -1
- package/src/TrackSelect/Folders/biosamples/shared/BiosampleViewSelector.tsx +33 -0
- package/src/TrackSelect/Folders/biosamples/shared/createFolder.ts +39 -58
- package/src/TrackSelect/Folders/biosamples/shared/toTrack.ts +138 -0
- package/src/TrackSelect/Folders/biosamples/shared/types.ts +4 -16
- package/src/TrackSelect/Folders/genes/shared/createFolder.ts +10 -31
- package/src/TrackSelect/Folders/genes/shared/toTrack.ts +59 -0
- package/src/TrackSelect/Folders/index.ts +14 -17
- package/src/TrackSelect/Folders/mohd/data/human.json +2945 -0
- package/src/TrackSelect/Folders/mohd/human.ts +10 -0
- package/src/TrackSelect/Folders/mohd/shared/MohdGroupingCell.tsx +68 -0
- package/src/TrackSelect/Folders/mohd/shared/MohdTreeItem.tsx +17 -0
- package/src/TrackSelect/Folders/mohd/shared/MohdViewSelector.tsx +33 -0
- package/src/TrackSelect/Folders/mohd/shared/columns.tsx +79 -0
- package/src/TrackSelect/Folders/mohd/shared/config.tsx +71 -0
- package/src/TrackSelect/Folders/mohd/shared/createFolder.ts +144 -0
- package/src/TrackSelect/Folders/mohd/shared/toTrack.ts +164 -0
- package/src/TrackSelect/Folders/mohd/shared/types.ts +46 -0
- package/src/TrackSelect/Folders/other-tracks/shared/createFolder.ts +13 -14
- package/src/TrackSelect/Folders/other-tracks/shared/toTrack.ts +17 -0
- package/src/TrackSelect/Folders/other-tracks/shared/types.ts +1 -0
- package/src/TrackSelect/Folders/types.ts +26 -69
- package/src/TrackSelect/TrackSelect.tsx +299 -255
- package/src/TrackSelect/TreeView/CustomTreeItem.tsx +6 -6
- package/src/TrackSelect/TreeView/TreeViewWrapper.tsx +84 -6
- package/src/TrackSelect/buildSelectedTree.ts +145 -0
- package/src/TrackSelect/managedTracks.ts +92 -0
- package/src/TrackSelect/resolveFolderView.ts +20 -0
- package/src/TrackSelect/trackContext.ts +9 -0
- package/src/TrackSelect/types.ts +14 -39
- package/src/lib.ts +13 -7
- package/src/muiLicense.ts +9 -0
- package/src/vite-env.d.ts +9 -0
- package/test/TrackSelect.test.tsx +435 -0
- package/test/main.tsx +36 -352
- package/test/mocks/logo-test.tsx +11 -0
- package/test/mohdDisplay.test.tsx +45 -0
- package/test/startup.test.ts +206 -0
- package/test/trackSelectState.test.ts +176 -0
- package/vite.config.ts +1 -0
- package/vitest.config.ts +20 -0
- package/dist/TrackSelect/Folders/biosamples/shared/AssayToggle.d.ts +0 -18
- package/dist/TrackSelect/Folders/biosamples/shared/treeBuilder.d.ts +0 -28
- package/dist/TrackSelect/Folders/genes/shared/treeBuilder.d.ts +0 -13
- package/dist/TrackSelect/Folders/other-tracks/shared/treeBuilder.d.ts +0 -4
- package/dist/TrackSelect/store.d.ts +0 -4
- package/src/TrackSelect/Folders/NEW.md +0 -929
- package/src/TrackSelect/Folders/biosamples/shared/AssayToggle.tsx +0 -78
- package/src/TrackSelect/Folders/biosamples/shared/treeBuilder.ts +0 -224
- package/src/TrackSelect/Folders/genes/shared/treeBuilder.ts +0 -45
- package/src/TrackSelect/Folders/other-tracks/shared/treeBuilder.ts +0 -34
- package/src/TrackSelect/store.ts +0 -117
package/test/main.tsx
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
2
|
|
|
3
3
|
// react
|
|
4
|
-
import {
|
|
4
|
+
import { useState } from "react";
|
|
5
5
|
import { createRoot } from "react-dom/client";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
import { LicenseInfo } from "@mui/x-license";
|
|
9
|
-
const muiLicenseKey = import.meta.env.VITE_MUI_X_LICENSE_KEY;
|
|
10
|
-
if (muiLicenseKey) {
|
|
11
|
-
LicenseInfo.setLicenseKey(muiLicenseKey);
|
|
12
|
-
}
|
|
7
|
+
import "../src/muiLicense";
|
|
13
8
|
|
|
14
9
|
// mui
|
|
15
10
|
import EditIcon from "@mui/icons-material/Edit";
|
|
@@ -17,76 +12,27 @@ import { Button } from "@mui/material";
|
|
|
17
12
|
|
|
18
13
|
// weng lab
|
|
19
14
|
import {
|
|
20
|
-
BigBedConfig,
|
|
21
|
-
BigWigConfig,
|
|
22
15
|
Browser,
|
|
23
16
|
createBrowserStoreMemo,
|
|
24
17
|
createTrackStoreMemo,
|
|
25
|
-
DisplayMode,
|
|
26
|
-
Domain,
|
|
27
18
|
GQLWrapper,
|
|
28
|
-
MethylCConfig,
|
|
29
|
-
Rect,
|
|
30
|
-
Track,
|
|
31
|
-
TrackType,
|
|
32
|
-
TranscriptConfig,
|
|
33
19
|
} from "@weng-lab/genomebrowser";
|
|
34
20
|
|
|
35
21
|
// local
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
interface Transcript {
|
|
44
|
-
id: string;
|
|
45
|
-
name: string;
|
|
46
|
-
coordinates: Domain;
|
|
47
|
-
strand: string;
|
|
48
|
-
exons?: Exon[];
|
|
49
|
-
color?: string;
|
|
50
|
-
}
|
|
22
|
+
import {
|
|
23
|
+
foldersByAssembly,
|
|
24
|
+
type InitialSelectedIdsByAssembly,
|
|
25
|
+
TrackSelect,
|
|
26
|
+
type TrackSelectTrackContext,
|
|
27
|
+
} from "../src/lib";
|
|
51
28
|
|
|
52
29
|
type Assembly = "GRCh38" | "mm10";
|
|
53
30
|
|
|
54
|
-
// Callback types for track interactions (using any to avoid type conflicts with library types)
|
|
55
|
-
interface TrackCallbacks {
|
|
56
|
-
onHover: (item: any) => void;
|
|
57
|
-
onLeave: () => void;
|
|
58
|
-
onCCREClick: (item: any) => void;
|
|
59
|
-
onGeneClick: (item: any) => void;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Helper to inject callbacks based on track type
|
|
63
|
-
function injectCallbacks(track: Track, callbacks: TrackCallbacks): Track {
|
|
64
|
-
if (track.trackType === TrackType.Transcript) {
|
|
65
|
-
return {
|
|
66
|
-
...track,
|
|
67
|
-
onHover: callbacks.onHover,
|
|
68
|
-
onLeave: callbacks.onLeave,
|
|
69
|
-
onClick: callbacks.onGeneClick,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
if (track.trackType === TrackType.BigBed) {
|
|
73
|
-
return {
|
|
74
|
-
...track,
|
|
75
|
-
onHover: callbacks.onHover,
|
|
76
|
-
onLeave: callbacks.onLeave,
|
|
77
|
-
onClick: callbacks.onCCREClick,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
return track;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
31
|
function Main() {
|
|
84
32
|
const [open, setOpen] = useState(false);
|
|
85
33
|
const currentAssembly: Assembly = "GRCh38";
|
|
86
34
|
|
|
87
35
|
const browserStore = createBrowserStoreMemo({
|
|
88
|
-
// chr7:19,695,494-19,699,803
|
|
89
|
-
// chr1:11103779-11262792
|
|
90
36
|
domain: { chromosome: "chr12", start: 53380108, end: 53416378 },
|
|
91
37
|
marginWidth: 100,
|
|
92
38
|
trackWidth: 1400,
|
|
@@ -95,123 +41,18 @@ function Main() {
|
|
|
95
41
|
|
|
96
42
|
const domain = browserStore((s) => s.domain);
|
|
97
43
|
|
|
98
|
-
const
|
|
99
|
-
const removeHighlight = browserStore((s) => s.removeHighlight);
|
|
100
|
-
const onHover = useCallback(
|
|
101
|
-
(item: Rect | Transcript) => {
|
|
102
|
-
const domain =
|
|
103
|
-
"start" in item
|
|
104
|
-
? { start: item.start, end: item.end }
|
|
105
|
-
: { start: item.coordinates.start, end: item.coordinates.end };
|
|
44
|
+
const folders = foldersByAssembly[currentAssembly];
|
|
106
45
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
});
|
|
46
|
+
const trackStore = createTrackStoreMemo([], [currentAssembly]);
|
|
47
|
+
const trackContext: TrackSelectTrackContext = {
|
|
48
|
+
onGeneClick: ({ trackId, transcript }) => {
|
|
49
|
+
console.log("Gene clicked", trackId, transcript.name);
|
|
112
50
|
},
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const onLeave = useCallback(() => {
|
|
116
|
-
removeHighlight("hover-highlight");
|
|
117
|
-
}, [removeHighlight]);
|
|
118
|
-
|
|
119
|
-
const onCCREClick = useCallback((item: Rect) => {
|
|
120
|
-
console.log(item);
|
|
121
|
-
}, []);
|
|
122
|
-
const onGeneClick = useCallback((item: Transcript) => {
|
|
123
|
-
console.log(item);
|
|
124
|
-
}, []);
|
|
125
|
-
|
|
126
|
-
// Bundle callbacks for track injection
|
|
127
|
-
const callbacks = useMemo<TrackCallbacks>(
|
|
128
|
-
() => ({
|
|
129
|
-
onHover,
|
|
130
|
-
onLeave,
|
|
131
|
-
onCCREClick,
|
|
132
|
-
onGeneClick,
|
|
133
|
-
}),
|
|
134
|
-
[onHover, onLeave, onCCREClick, onGeneClick],
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
const trackStore = useLocalTracks(currentAssembly, callbacks);
|
|
138
|
-
|
|
139
|
-
const tracks = trackStore((s) => s.tracks);
|
|
140
|
-
const insertTrack = trackStore((s) => s.insertTrack);
|
|
141
|
-
const removeTrack = trackStore((s) => s.removeTrack);
|
|
142
|
-
|
|
143
|
-
const folders = useMemo(
|
|
144
|
-
() => foldersByAssembly[currentAssembly],
|
|
145
|
-
[currentAssembly],
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
const storageKey = `${currentAssembly}-selected-tracks`;
|
|
149
|
-
|
|
150
|
-
const initialSelection = useMemo(
|
|
151
|
-
() =>
|
|
152
|
-
(currentAssembly as Assembly) === "GRCh38"
|
|
153
|
-
? defaultHumanSelections
|
|
154
|
-
: defaultMouseSelections,
|
|
155
|
-
[currentAssembly],
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
// sync tracks to browser and save to localStorage
|
|
159
|
-
const handleSubmit = useCallback(
|
|
160
|
-
(selectedByFolder: Map<string, Set<string>>) => {
|
|
161
|
-
const currentIds = new Set(tracks.map((t) => t.id));
|
|
162
|
-
const selectedIds = new Set<string>();
|
|
163
|
-
const tracksToAdd: Array<{ row: unknown; folderId: string }> = [];
|
|
164
|
-
|
|
165
|
-
for (const folder of folders) {
|
|
166
|
-
const folderSelection =
|
|
167
|
-
selectedByFolder.get(folder.id) ?? new Set<string>();
|
|
168
|
-
folderSelection.forEach((id) => {
|
|
169
|
-
selectedIds.add(id);
|
|
170
|
-
if (currentIds.has(id)) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
const row = folder.rowById.get(id);
|
|
174
|
-
if (row) {
|
|
175
|
-
tracksToAdd.push({ row, folderId: folder.id });
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const tracksToRemove = tracks.filter((t) => !selectedIds.has(t.id));
|
|
181
|
-
for (const t of tracksToRemove) {
|
|
182
|
-
removeTrack(t.id);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
for (const { row, folderId } of tracksToAdd) {
|
|
186
|
-
const track = generateTrack(
|
|
187
|
-
row as BiosampleRowInfo | GeneRowInfo,
|
|
188
|
-
folderId,
|
|
189
|
-
currentAssembly,
|
|
190
|
-
callbacks,
|
|
191
|
-
);
|
|
192
|
-
if (track === null) continue;
|
|
193
|
-
insertTrack(track);
|
|
194
|
-
}
|
|
51
|
+
onBiosampleFeatureHover: ({ trackId, rect }) => {
|
|
52
|
+
console.log("Biosample feature hovered", trackId, rect.name ?? "unknown");
|
|
195
53
|
},
|
|
196
|
-
[tracks, removeTrack, insertTrack, callbacks, folders, currentAssembly],
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
// clear selections and remove all tracks
|
|
200
|
-
const handleClear = () => {
|
|
201
|
-
for (const t of tracks) {
|
|
202
|
-
removeTrack(t.id);
|
|
203
|
-
}
|
|
204
54
|
};
|
|
205
55
|
|
|
206
|
-
// On first load, if no stored selection exists, apply initial selection
|
|
207
|
-
useEffect(() => {
|
|
208
|
-
const stored = sessionStorage.getItem(storageKey);
|
|
209
|
-
if (!stored) {
|
|
210
|
-
handleSubmit(initialSelection);
|
|
211
|
-
}
|
|
212
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
213
|
-
}, []);
|
|
214
|
-
|
|
215
56
|
return (
|
|
216
57
|
<>
|
|
217
58
|
<div>
|
|
@@ -226,15 +67,16 @@ function Main() {
|
|
|
226
67
|
Select Tracks
|
|
227
68
|
</Button>
|
|
228
69
|
<TrackSelect
|
|
70
|
+
assembly={currentAssembly}
|
|
229
71
|
folders={folders}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
72
|
+
initialSelectedIds={defaultSelections}
|
|
73
|
+
sessionStorageKey={`track-select:${currentAssembly}`}
|
|
74
|
+
trackContext={trackContext}
|
|
75
|
+
trackStore={trackStore}
|
|
234
76
|
maxTracks={30}
|
|
235
77
|
open={open}
|
|
236
78
|
onClose={() => setOpen(false)}
|
|
237
|
-
title="
|
|
79
|
+
title="Select Tracks"
|
|
238
80
|
/>
|
|
239
81
|
<GQLWrapper>
|
|
240
82
|
<Browser browserStore={browserStore} trackStore={trackStore} />
|
|
@@ -245,178 +87,20 @@ function Main() {
|
|
|
245
87
|
|
|
246
88
|
createRoot(document.getElementById("root")!).render(<Main />);
|
|
247
89
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
assembly: Assembly,
|
|
264
|
-
callbacks?: TrackCallbacks,
|
|
265
|
-
): Track | null {
|
|
266
|
-
// Handle gene folders
|
|
267
|
-
if (folderId.includes("genes")) {
|
|
268
|
-
const geneRow = row as GeneRowInfo;
|
|
269
|
-
const track: Track = {
|
|
270
|
-
...defaultTranscript,
|
|
271
|
-
id: geneRow.id,
|
|
272
|
-
assembly,
|
|
273
|
-
version: geneRow.versions[geneRow.versions.length - 1], // latest version
|
|
274
|
-
};
|
|
275
|
-
return callbacks ? injectCallbacks(track, callbacks) : track;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Handle other-tracks folder
|
|
279
|
-
if (folderId.includes("other-tracks")) {
|
|
280
|
-
if (row.id === "tf-peaks") {
|
|
281
|
-
return { ...tfPeaksTrack };
|
|
282
|
-
}
|
|
283
|
-
return null;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Handle biosample folders
|
|
287
|
-
const sel = row as BiosampleRowInfo;
|
|
288
|
-
const color = ASSAY_COLORS[sel.assay.toLowerCase()] || "#000000";
|
|
289
|
-
let track: Track;
|
|
290
|
-
|
|
291
|
-
switch (sel.assay.toLowerCase()) {
|
|
292
|
-
case "chromhmm":
|
|
293
|
-
case "ccre":
|
|
294
|
-
track = {
|
|
295
|
-
...defaultBigBed,
|
|
296
|
-
id: sel.id,
|
|
297
|
-
url: sel.url ?? "",
|
|
298
|
-
title: sel.displayName,
|
|
299
|
-
color,
|
|
300
|
-
};
|
|
301
|
-
break;
|
|
302
|
-
case "wgbs":
|
|
303
|
-
track = {
|
|
304
|
-
...defaultMethylC,
|
|
305
|
-
id: sel.id,
|
|
306
|
-
title: sel.displayName,
|
|
307
|
-
urls: {
|
|
308
|
-
plusStrand: {
|
|
309
|
-
cpg: { url: sel.cpgPlus ?? "" },
|
|
310
|
-
chg: { url: "" },
|
|
311
|
-
chh: { url: "" },
|
|
312
|
-
depth: { url: sel.coverage ?? "" },
|
|
313
|
-
},
|
|
314
|
-
minusStrand: {
|
|
315
|
-
cpg: { url: sel.cpgMinus ?? "" },
|
|
316
|
-
chg: { url: "" },
|
|
317
|
-
chh: { url: "" },
|
|
318
|
-
depth: { url: sel.coverage ?? "" },
|
|
319
|
-
},
|
|
320
|
-
},
|
|
321
|
-
};
|
|
322
|
-
break;
|
|
323
|
-
default:
|
|
324
|
-
track = {
|
|
325
|
-
...defaultBigWig,
|
|
326
|
-
id: sel.id,
|
|
327
|
-
url: sel.url ?? "",
|
|
328
|
-
title: sel.displayName,
|
|
329
|
-
color,
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
return callbacks ? injectCallbacks(track, callbacks) : track;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
export const defaultBigWig: Omit<BigWigConfig, "id" | "title" | "url"> = {
|
|
337
|
-
trackType: TrackType.BigWig,
|
|
338
|
-
height: 50,
|
|
339
|
-
displayMode: DisplayMode.Full,
|
|
340
|
-
titleSize: 12,
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
export const defaultBigBed: Omit<BigBedConfig, "id" | "title" | "url"> = {
|
|
344
|
-
trackType: TrackType.BigBed,
|
|
345
|
-
height: 20,
|
|
346
|
-
displayMode: DisplayMode.Dense,
|
|
347
|
-
titleSize: 12,
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
export const defaultMethylC: Omit<MethylCConfig, "id" | "title" | "urls"> = {
|
|
351
|
-
trackType: TrackType.MethylC,
|
|
352
|
-
height: 100,
|
|
353
|
-
displayMode: DisplayMode.Split,
|
|
354
|
-
titleSize: 12,
|
|
355
|
-
color: "#648bd8",
|
|
356
|
-
colors: {
|
|
357
|
-
cpg: "#648bd8",
|
|
358
|
-
chg: "#ff944d",
|
|
359
|
-
chh: "#ff00ff",
|
|
360
|
-
depth: "#525252",
|
|
90
|
+
// Default selections for TrackSelect UI (uses folder row IDs)
|
|
91
|
+
const defaultSelections: InitialSelectedIdsByAssembly = {
|
|
92
|
+
GRCh38: {
|
|
93
|
+
"human-genes": ["human-genes/gencode-basic"],
|
|
94
|
+
"human-biosamples": [
|
|
95
|
+
"human-biosamples/ccre-aggregate",
|
|
96
|
+
"human-biosamples/dnase-aggregate",
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
mm10: {
|
|
100
|
+
"mouse-genes": ["mouse-genes/gencode-basic"],
|
|
101
|
+
"mouse-biosamples": [
|
|
102
|
+
"mouse-biosamples/ccre-aggregate",
|
|
103
|
+
"mouse-biosamples/dnase-aggregate",
|
|
104
|
+
],
|
|
361
105
|
},
|
|
362
106
|
};
|
|
363
|
-
|
|
364
|
-
export const defaultTranscript: Omit<
|
|
365
|
-
TranscriptConfig,
|
|
366
|
-
"id" | "assembly" | "version"
|
|
367
|
-
> = {
|
|
368
|
-
title: "GENCODE Genes",
|
|
369
|
-
trackType: TrackType.Transcript,
|
|
370
|
-
displayMode: DisplayMode.Squish,
|
|
371
|
-
height: 100,
|
|
372
|
-
color: "#0c184a", // screen theme default
|
|
373
|
-
canonicalColor: "#100e98", // screen theme light
|
|
374
|
-
highlightColor: "#3c69e8", // bright blue
|
|
375
|
-
titleSize: 12,
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
export function useLocalTracks(assembly: string, callbacks?: TrackCallbacks) {
|
|
379
|
-
const localTracks = getLocalTracks(assembly);
|
|
380
|
-
|
|
381
|
-
// Start empty if no stored tracks - TrackSelect will populate defaults
|
|
382
|
-
let initialTracks: Track[] = localTracks || [];
|
|
383
|
-
|
|
384
|
-
// Inject callbacks if provided (callbacks are lost on JSON serialization)
|
|
385
|
-
if (callbacks) {
|
|
386
|
-
initialTracks = initialTracks.map((t) => injectCallbacks(t, callbacks));
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const trackStore = createTrackStoreMemo(initialTracks, []);
|
|
390
|
-
const tracks = trackStore((state) => state.tracks);
|
|
391
|
-
|
|
392
|
-
// any time the track list changes, update local storage
|
|
393
|
-
useEffect(() => {
|
|
394
|
-
setLocalTracks(tracks, assembly);
|
|
395
|
-
}, [tracks, assembly]);
|
|
396
|
-
|
|
397
|
-
return trackStore;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
export function getLocalTracks(assembly: string): Track[] | null {
|
|
401
|
-
if (typeof window === "undefined" || !window.sessionStorage) return null;
|
|
402
|
-
|
|
403
|
-
const localTracks = sessionStorage.getItem(assembly + "-tracks");
|
|
404
|
-
if (!localTracks) return null;
|
|
405
|
-
const localTracksJson = JSON.parse(localTracks) as Track[];
|
|
406
|
-
return localTracksJson;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
export function setLocalTracks(tracks: Track[], assembly: string) {
|
|
410
|
-
sessionStorage.setItem(assembly + "-tracks", JSON.stringify(tracks));
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// Default selections for TrackSelect UI (uses folder row IDs)
|
|
414
|
-
const defaultHumanSelections = new Map<string, Set<string>>([
|
|
415
|
-
["human-genes", new Set(["gencode-basic"])],
|
|
416
|
-
["human-biosamples", new Set(["ccre-aggregate", "dnase-aggregate"])],
|
|
417
|
-
]);
|
|
418
|
-
|
|
419
|
-
const defaultMouseSelections = new Map<string, Set<string>>([
|
|
420
|
-
["mouse-genes", new Set(["gencode-basic"])],
|
|
421
|
-
["mouse-biosamples", new Set(["ccre-aggregate", "dnase-aggregate"])],
|
|
422
|
-
]);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
function Stub({ children }: { children?: ReactNode }) {
|
|
4
|
+
return <>{children ?? null}</>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const A = Stub;
|
|
8
|
+
export const C = Stub;
|
|
9
|
+
export const G = Stub;
|
|
10
|
+
export const T = Stub;
|
|
11
|
+
export const DNALogo = Stub;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { buildSelectedTree } from "../src/TrackSelect/buildSelectedTree";
|
|
4
|
+
vi.mock("../src/TrackSelect/Folders/mohd/shared/MohdGroupingCell", () => ({
|
|
5
|
+
MohdGroupingCell: () => null,
|
|
6
|
+
}));
|
|
7
|
+
vi.mock("../src/TrackSelect/Folders/mohd/shared/MohdTreeItem", () => ({
|
|
8
|
+
MohdTreeItem: () => null,
|
|
9
|
+
}));
|
|
10
|
+
vi.mock("../src/TrackSelect/Folders/mohd/shared/MohdViewSelector", () => ({
|
|
11
|
+
MohdViewSelector: () => null,
|
|
12
|
+
}));
|
|
13
|
+
import { humanMohdFolder } from "../src/TrackSelect/Folders/mohd/human";
|
|
14
|
+
import { MohdOmeIcon } from "../src/TrackSelect/Folders/mohd/shared/config";
|
|
15
|
+
|
|
16
|
+
describe("MOHD display helpers", () => {
|
|
17
|
+
it("renders an ome color icon", () => {
|
|
18
|
+
const html = renderToStaticMarkup(<MohdOmeIcon type="ATAC" />);
|
|
19
|
+
|
|
20
|
+
expect(html).toContain("mohd-ome-icon-atac");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("wires MOHD-specific grouping and tree components onto the folder", () => {
|
|
24
|
+
expect(humanMohdFolder.GroupingCellComponent).toBeTruthy();
|
|
25
|
+
expect(humanMohdFolder.TreeItemComponent).toBeTruthy();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("marks ome nodes in the selected tree for icon rendering", () => {
|
|
29
|
+
const selectedRows = humanMohdFolder.rows.filter(
|
|
30
|
+
(row) => row.sampleId === "MOHD_EA100001",
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const tree = buildSelectedTree({
|
|
34
|
+
folderId: humanMohdFolder.id,
|
|
35
|
+
rootLabel: humanMohdFolder.label,
|
|
36
|
+
selectedRows,
|
|
37
|
+
groupingModel: ["ome", "site", "sampleId"],
|
|
38
|
+
leafField: "description",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(tree[0]?.children?.[0]?.label).toBe("ATAC");
|
|
42
|
+
expect(tree[0]?.children?.[0]?.highlightName).toBe("ATAC");
|
|
43
|
+
expect(tree[0]?.children?.[0]?.isHighlightedItem).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
});
|