@weng-lab/genomebrowser-ui 0.3.6 → 0.4.0-beta.1

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.
Files changed (91) hide show
  1. package/.env.local +1 -1
  2. package/dist/TrackSelect/Folders/biosamples/shared/BiosampleViewSelector.d.ts +7 -0
  3. package/dist/TrackSelect/Folders/biosamples/shared/createFolder.d.ts +1 -13
  4. package/dist/TrackSelect/Folders/biosamples/shared/toTrack.d.ts +20 -0
  5. package/dist/TrackSelect/Folders/biosamples/shared/types.d.ts +4 -13
  6. package/dist/TrackSelect/Folders/genes/shared/columns.d.ts +2 -2
  7. package/dist/TrackSelect/Folders/genes/shared/createFolder.d.ts +1 -3
  8. package/dist/TrackSelect/Folders/genes/shared/toTrack.d.ts +18 -0
  9. package/dist/TrackSelect/Folders/genes/shared/types.d.ts +2 -0
  10. package/dist/TrackSelect/Folders/index.d.ts +6 -12
  11. package/dist/TrackSelect/Folders/mohd/data/human.json.d.ts +2948 -0
  12. package/dist/TrackSelect/Folders/mohd/human.d.ts +1 -0
  13. package/dist/TrackSelect/Folders/mohd/shared/MohdGroupingCell.d.ts +2 -0
  14. package/dist/TrackSelect/Folders/mohd/shared/MohdTreeItem.d.ts +3 -0
  15. package/dist/TrackSelect/Folders/mohd/shared/MohdViewSelector.d.ts +7 -0
  16. package/dist/TrackSelect/Folders/mohd/shared/columns.d.ts +5 -0
  17. package/dist/TrackSelect/Folders/mohd/shared/config.d.ts +42 -0
  18. package/dist/TrackSelect/Folders/mohd/shared/createFolder.d.ts +9 -0
  19. package/dist/TrackSelect/Folders/mohd/shared/toTrack.d.ts +9 -0
  20. package/dist/TrackSelect/Folders/mohd/shared/types.d.ts +40 -0
  21. package/dist/TrackSelect/Folders/other-tracks/shared/toTrack.d.ts +5 -0
  22. package/dist/TrackSelect/Folders/other-tracks/shared/types.d.ts +1 -0
  23. package/dist/TrackSelect/Folders/types.d.ts +23 -55
  24. package/dist/TrackSelect/TrackSelect.d.ts +10 -7
  25. package/dist/TrackSelect/TreeView/TreeViewWrapper.d.ts +1 -1
  26. package/dist/TrackSelect/buildSelectedTree.d.ts +15 -0
  27. package/dist/TrackSelect/managedTracks.d.ts +13 -0
  28. package/dist/TrackSelect/resolveFolderView.d.ts +2 -0
  29. package/dist/TrackSelect/trackContext.d.ts +5 -0
  30. package/dist/TrackSelect/types.d.ts +12 -33
  31. package/dist/genomebrowser-ui.es.js +2231 -1732
  32. package/dist/genomebrowser-ui.es.js.map +1 -1
  33. package/dist/lib.d.ts +4 -4
  34. package/dist/muiLicense.d.ts +1 -0
  35. package/package.json +6 -3
  36. package/src/TrackSelect/Dialogs/ClearDialog.tsx +3 -8
  37. package/src/TrackSelect/Dialogs/ResetDialog.tsx +5 -4
  38. package/src/TrackSelect/FolderList/FolderCard.tsx +1 -1
  39. package/src/TrackSelect/Folders/biosamples/shared/BiosampleViewSelector.tsx +33 -0
  40. package/src/TrackSelect/Folders/biosamples/shared/createFolder.ts +39 -58
  41. package/src/TrackSelect/Folders/biosamples/shared/toTrack.ts +138 -0
  42. package/src/TrackSelect/Folders/biosamples/shared/types.ts +4 -16
  43. package/src/TrackSelect/Folders/genes/shared/columns.tsx +2 -2
  44. package/src/TrackSelect/Folders/genes/shared/createFolder.ts +11 -31
  45. package/src/TrackSelect/Folders/genes/shared/toTrack.ts +59 -0
  46. package/src/TrackSelect/Folders/genes/shared/types.ts +2 -0
  47. package/src/TrackSelect/Folders/index.ts +14 -17
  48. package/src/TrackSelect/Folders/mohd/data/human.json +2945 -0
  49. package/src/TrackSelect/Folders/mohd/human.ts +10 -0
  50. package/src/TrackSelect/Folders/mohd/shared/MohdGroupingCell.tsx +68 -0
  51. package/src/TrackSelect/Folders/mohd/shared/MohdTreeItem.tsx +17 -0
  52. package/src/TrackSelect/Folders/mohd/shared/MohdViewSelector.tsx +33 -0
  53. package/src/TrackSelect/Folders/mohd/shared/columns.tsx +79 -0
  54. package/src/TrackSelect/Folders/mohd/shared/config.tsx +71 -0
  55. package/src/TrackSelect/Folders/mohd/shared/createFolder.ts +144 -0
  56. package/src/TrackSelect/Folders/mohd/shared/toTrack.ts +164 -0
  57. package/src/TrackSelect/Folders/mohd/shared/types.ts +46 -0
  58. package/src/TrackSelect/Folders/other-tracks/shared/createFolder.ts +13 -14
  59. package/src/TrackSelect/Folders/other-tracks/shared/toTrack.ts +17 -0
  60. package/src/TrackSelect/Folders/other-tracks/shared/types.ts +1 -0
  61. package/src/TrackSelect/Folders/types.ts +26 -69
  62. package/src/TrackSelect/TrackSelect.tsx +301 -257
  63. package/src/TrackSelect/TreeView/CustomTreeItem.tsx +9 -9
  64. package/src/TrackSelect/TreeView/TreeViewWrapper.tsx +84 -6
  65. package/src/TrackSelect/buildSelectedTree.ts +145 -0
  66. package/src/TrackSelect/managedTracks.ts +92 -0
  67. package/src/TrackSelect/resolveFolderView.ts +20 -0
  68. package/src/TrackSelect/trackContext.ts +9 -0
  69. package/src/TrackSelect/types.ts +14 -39
  70. package/src/lib.ts +13 -7
  71. package/src/muiLicense.ts +9 -0
  72. package/src/vite-env.d.ts +9 -0
  73. package/test/TrackSelect.test.tsx +435 -0
  74. package/test/main.tsx +36 -352
  75. package/test/mocks/logo-test.tsx +11 -0
  76. package/test/mohdDisplay.test.tsx +45 -0
  77. package/test/startup.test.ts +206 -0
  78. package/test/trackSelectState.test.ts +176 -0
  79. package/vite.config.ts +1 -0
  80. package/vitest.config.ts +20 -0
  81. package/dist/TrackSelect/Folders/biosamples/shared/AssayToggle.d.ts +0 -18
  82. package/dist/TrackSelect/Folders/biosamples/shared/treeBuilder.d.ts +0 -28
  83. package/dist/TrackSelect/Folders/genes/shared/treeBuilder.d.ts +0 -13
  84. package/dist/TrackSelect/Folders/other-tracks/shared/treeBuilder.d.ts +0 -4
  85. package/dist/TrackSelect/store.d.ts +0 -4
  86. package/src/TrackSelect/Folders/NEW.md +0 -929
  87. package/src/TrackSelect/Folders/biosamples/shared/AssayToggle.tsx +0 -78
  88. package/src/TrackSelect/Folders/biosamples/shared/treeBuilder.ts +0 -224
  89. package/src/TrackSelect/Folders/genes/shared/treeBuilder.ts +0 -45
  90. package/src/TrackSelect/Folders/other-tracks/shared/treeBuilder.ts +0 -34
  91. 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 { useCallback, useEffect, useMemo, useState } from "react";
4
+ import { useState } from "react";
5
5
  import { createRoot } from "react-dom/client";
6
6
 
7
- // license
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 { foldersByAssembly, TrackSelect } from "../src/lib";
37
- import type { BiosampleRowInfo } from "../src/TrackSelect/Folders/biosamples/shared/types";
38
- import type { GeneRowInfo } from "../src/TrackSelect/Folders/genes/shared/types";
39
- import type { OtherTrackInfo } from "../src/TrackSelect/Folders/other-tracks/shared/types";
40
- import { Exon } from "@weng-lab/genomebrowser/dist/components/tracks/transcript/types";
41
- import { tfPeaksTrack } from "../src/TrackSelect/Custom/TfPeaks";
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 addHighlight = browserStore((s) => s.addHighlight);
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
- addHighlight({
108
- id: "hover-highlight",
109
- domain,
110
- color: item.color || "blue",
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
- [addHighlight],
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
- storageKey={storageKey}
231
- initialSelection={initialSelection}
232
- onSubmit={handleSubmit}
233
- onClear={handleClear}
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="Biosample Tracks"
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
- const ASSAY_COLORS: Record<string, string> = {
249
- dnase: "#06da93",
250
- h3k4me3: "#ff0000",
251
- h3k27ac: "#ffcd00",
252
- ctcf: "#00b0d0",
253
- atac: "#02c7b9",
254
- rnaseq: "#00aa00",
255
- chromhmm: "#00ff00",
256
- ccre: "#000000",
257
- wgbs: "#648bd8",
258
- };
259
-
260
- function generateTrack(
261
- row: BiosampleRowInfo | GeneRowInfo | OtherTrackInfo,
262
- folderId: string,
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
+ });