From c8a156fbb0305352a9a31d79e88a78885d96429f Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Fri, 20 Oct 2023 12:03:29 -0400 Subject: [PATCH 01/25] WIP: adding highlights to bookmarks --- packages/core/util/index.ts | 9 +- .../components/BookmarkGrid.tsx | 31 +++++- .../src/GridBookmarkWidget/model.ts | 17 ++++ plugins/grid-bookmark/src/index.ts | 4 + .../LinearGenomeView/components/Highlight.tsx | 95 +++++++++++++++++++ .../components/TracksContainer.tsx | 4 +- .../src/LinearGenomeView/model.ts | 13 +++ 7 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx diff --git a/packages/core/util/index.ts b/packages/core/util/index.ts index 3674966b92..c510d52676 100644 --- a/packages/core/util/index.ts +++ b/packages/core/util/index.ts @@ -1247,9 +1247,12 @@ export function localStorageGetItem(item: string) { } export function localStorageSetItem(str: string, item: string) { - return typeof localStorage !== 'undefined' - ? localStorage.setItem(str, item) - : undefined + const returnVal = + typeof localStorage !== 'undefined' + ? localStorage.setItem(str, item) + : undefined + window.dispatchEvent(new Event('storage')) + return returnVal } export function max(arr: number[], init = -Infinity) { diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx index 102ebaaebc..c94bd88bae 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx @@ -1,4 +1,4 @@ -import React, { Suspense, lazy, useState } from 'react' +import React, { Suspense, lazy, useEffect, useState } from 'react' import { observer } from 'mobx-react' import { Link } from '@mui/material' import { makeStyles } from 'tss-react/mui' @@ -15,6 +15,7 @@ import { navToBookmark } from '../utils' import { GridBookmarkModel, IExtendedLabeledRegionModel } from '../model' import { useResizeBar } from '@jbrowse/core/ui/useResizeBar' import ResizeBar from '@jbrowse/core/ui/ResizeBar' +import { PopoverPicker } from '@jbrowse/core/ui/ColorPicker' const EditBookmarkLabelDialog = lazy(() => import('./EditBookmarkLabelDialog')) @@ -59,9 +60,7 @@ const BookmarkGrid = observer(function ({ } }) - // reset selections if bookmarked regions change - // needed especially if bookmarked regions are deleted, then - const [widths, setWidths] = useState([ + const rowWidths = [ 40, Math.max( measureText('Bookmark link'), @@ -75,7 +74,16 @@ const BookmarkGrid = observer(function ({ measureText('Assembly'), measureGridWidth(rows.map(row => row.assemblyName)), ), - ]) + 100, + ] + + const [widths, setWidths] = useState(rowWidths) + + useEffect(() => { + if (rows.length > 0) { + setWidths(rowWidths) + } + }, [rows.length, setWidths]) return ( <> @@ -123,6 +131,19 @@ const BookmarkGrid = observer(function ({ headerName: 'Assembly', width: widths[3], }, + { + field: 'highlight', + headerName: 'Highlight', + width: widths[4], + renderCell: ({ value, row }) => ( + { + return model.updateBookmarkHighlight(row, event) + }} + /> + ), + }, ]} onCellDoubleClick={({ row }) => setDialogRow(row)} processRowUpdate={row => { diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts index 7641e1033c..119b9bb5e4 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts @@ -22,12 +22,21 @@ const LabeledRegionModel = types RegionModel, types.model('Label', { label: types.optional(types.string, ''), + highlight: types.optional( + types.string, + `rgba(${Math.round(Math.random() * 255)}, ${Math.round( + Math.random() * 255, + )}, ${Math.round(Math.random() * 255)}, 0.3)`, + ), }), ) .actions(self => ({ setLabel(label: string) { self.label = label }, + setHighlight(color: string) { + self.highlight = color + }, })) const SharedBookmarksModel = types.model('SharedBookmarksModel', { @@ -40,10 +49,12 @@ export interface ILabeledRegionModel start: number end: number reversed: boolean + highlight: string assemblyName: string label: string setRefName: (newRefName: string) => void setLabel: (label: string) => void + setHighlight: (color: string) => void } export interface IExtendedLabeledRegionModel extends ILabeledRegionModel { @@ -150,6 +161,12 @@ export default function f(_pluginManager: PluginManager) { ) { bookmark.correspondingObj.setLabel(label) }, + updateBookmarkHighlight( + bookmark: IExtendedLabeledRegionModel, + color: string, + ) { + bookmark.correspondingObj.setHighlight(color) + }, setSelectedBookmarks(bookmarks: IExtendedLabeledRegionModel[]) { self.selectedBookmarks = bookmarks }, diff --git a/plugins/grid-bookmark/src/index.ts b/plugins/grid-bookmark/src/index.ts index 7b889f7a70..8904dcb5d0 100644 --- a/plugins/grid-bookmark/src/index.ts +++ b/plugins/grid-bookmark/src/index.ts @@ -97,6 +97,10 @@ export default class extends Plugin { icon: BookmarkIcon, onClick: () => self.bookmarkCurrentRegion(), }, + { + label: 'Toggle bookmark highlights', + onClick: () => self.toggleShowBookmarkHighlights(), + }, ] }, diff --git a/plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx b/plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx new file mode 100644 index 0000000000..4aa35d5f5b --- /dev/null +++ b/plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx @@ -0,0 +1,95 @@ +import React, { useEffect, useState } from 'react' +import { observer } from 'mobx-react' +import { makeStyles } from 'tss-react/mui' +import { getSession, localStorageGetItem } from '@jbrowse/core/util' + +// locals +import { LinearGenomeViewModel } from '../model' +import { number } from 'prop-types' + +type LGV = LinearGenomeViewModel + +const useStyles = makeStyles()({ + highlight: { + height: '100%', + position: 'absolute', + textAlign: 'center', + overflow: 'hidden', + }, +}) + +export default observer(function Highlight({ model }: { model: LGV }) { + const { classes } = useStyles() + + const { showBookmarkHighlights } = model + const session = getSession(model) + const assemblyNames = new Set(session.assemblyNames) + + const localStorageKeyF = () => + typeof window !== undefined + ? `bookmarks-${[window.location.host + window.location.pathname].join( + '-', + )}` + : 'empty' + + const localStorage = JSON.parse( + localStorageGetItem(localStorageKeyF()) || '[]', + ) + + const [bookmarks, setBookmarks] = useState( + localStorage.filter((value: any) => assemblyNames.has(value.assemblyName)), + ) + + window.addEventListener('storage', () => { + setBookmarks( + localStorage.filter((value: any) => + assemblyNames.has(value.assemblyName), + ), + ) + }) + + useEffect(() => { + setBookmarks( + localStorage.filter((value: any) => + assemblyNames.has(value.assemblyName), + ), + ) + }, [localStorage?.length, JSON.stringify(localStorage)]) + + return ( + <> + {showBookmarkHighlights + ? bookmarks + .map((r: any) => { + const s = model.bpToPx({ + refName: r.refName, + coord: r.start, + }) + const e = model.bpToPx({ + refName: r.refName, + coord: r.end, + }) + return s && e + ? { + width: Math.max(Math.abs(e.offsetPx - s.offsetPx), 3), + left: Math.min(s.offsetPx, e.offsetPx) - model.offsetPx, + highlight: r.highlight, + } + : undefined + }) + .filter( + ( + f: any, + ): f is { width: number; left: number; highlight: string } => !!f, + ) + .map(({ left, width, highlight }: any, idx: number) => ( +
+ )) + : null} + + ) +}) diff --git a/plugins/linear-genome-view/src/LinearGenomeView/components/TracksContainer.tsx b/plugins/linear-genome-view/src/LinearGenomeView/components/TracksContainer.tsx index 490607e635..a5a24e9615 100644 --- a/plugins/linear-genome-view/src/LinearGenomeView/components/TracksContainer.tsx +++ b/plugins/linear-genome-view/src/LinearGenomeView/components/TracksContainer.tsx @@ -2,6 +2,7 @@ import React, { useRef } from 'react' import { makeStyles } from 'tss-react/mui' import { observer } from 'mobx-react' import { Menu } from '@jbrowse/core/ui' +import { getEnv } from '@jbrowse/core/util' // local utils import { LinearGenomeViewModel, SCALE_BAR_HEIGHT } from '..' @@ -14,7 +15,7 @@ import Gridlines from './Gridlines' import CenterLine from './CenterLine' import VerticalGuide from './VerticalGuide' import RubberbandSpan from './RubberbandSpan' -import { getEnv } from '@jbrowse/core/util' +import Highlight from './Highlight' const useStyles = makeStyles()({ tracksContainer: { @@ -108,6 +109,7 @@ const TracksContainer = observer(function TracksContainer({ } /> {additional} + {children}
) diff --git a/plugins/linear-genome-view/src/LinearGenomeView/model.ts b/plugins/linear-genome-view/src/LinearGenomeView/model.ts index c03877369e..dd03a78107 100644 --- a/plugins/linear-genome-view/src/LinearGenomeView/model.ts +++ b/plugins/linear-genome-view/src/LinearGenomeView/model.ts @@ -236,6 +236,12 @@ export function stateModelFactory(pluginManager: PluginManager) { * show the "gridlines" in the track area */ showGridlines: true, + + /** + * #property + * show the bookmark highlights on this track + */ + showBookmarkHighlights: true, }), ) .volatile(() => ({ @@ -562,6 +568,13 @@ export function stateModelFactory(pluginManager: PluginManager) { toggleShowGridlines() { self.showGridlines = !self.showGridlines }, + + /** + * #action + */ + toggleShowBookmarkHighlights() { + self.showBookmarkHighlights = !self.showBookmarkHighlights + }, /** * #action */ From 0eba41ab2314ae5017ec03b5c20acab3b1154f63 Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Mon, 6 Nov 2023 16:32:27 -0500 Subject: [PATCH 02/25] Adds label to highlights on the LGV; adds options to toggle on/off; adds new dialog for bulk editing highlight color and session wide options for showing/hiding highlights --- .../components/BookmarkGrid.tsx | 9 +- .../components/EditBookmarks.tsx | 31 ++++++ .../components/EditBookmarksDialog.tsx | 95 +++++++++++++++++++ .../components/GridBookmarkWidget.tsx | 2 + .../src/GridBookmarkWidget/model.ts | 49 +++++++++- plugins/grid-bookmark/src/index.ts | 8 ++ .../LinearGenomeView/components/Highlight.tsx | 32 ++++++- .../src/LinearGenomeView/model.ts | 25 ++++- 8 files changed, 237 insertions(+), 14 deletions(-) create mode 100644 plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarks.tsx create mode 100644 plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx index 0c97457a0e..219254ba17 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx @@ -9,13 +9,14 @@ import { measureGridWidth, measureText, } from '@jbrowse/core/util' +import { useResizeBar } from '@jbrowse/core/ui/useResizeBar' +import ResizeBar from '@jbrowse/core/ui/ResizeBar' + +const ColorPicker = lazy(() => import('@jbrowse/core/ui/ColorPicker')) // locals import { navToBookmark } from '../utils' import { GridBookmarkModel, IExtendedLabeledRegionModel } from '../model' -import { useResizeBar } from '@jbrowse/core/ui/useResizeBar' -import ResizeBar from '@jbrowse/core/ui/ResizeBar' -import { PopoverPicker } from '@jbrowse/core/ui/ColorPicker' const EditBookmarkLabelDialog = lazy(() => import('./EditBookmarkLabelDialog')) @@ -136,7 +137,7 @@ const BookmarkGrid = observer(function ({ headerName: 'Highlight', width: widths[4], renderCell: ({ value, row }) => ( - { return model.updateBookmarkHighlight(row, event) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarks.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarks.tsx new file mode 100644 index 0000000000..480381a980 --- /dev/null +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarks.tsx @@ -0,0 +1,31 @@ +import React, { Suspense, lazy, useState } from 'react' +import { Button } from '@mui/material' + +// icons +import EditIcon from '@mui/icons-material/Edit' + +// locals +import { GridBookmarkModel } from '../model' +const EditBookmarksDialog = lazy(() => import('./EditBookmarksDialog')) + +function EditBookmarks({ model }: { model: GridBookmarkModel }) { + const [open, setOpen] = useState(false) + return ( + <> + + {open ? ( + }> + setOpen(false)} /> + + ) : null} + + ) +} + +export default EditBookmarks diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx new file mode 100644 index 0000000000..2e5d9ccf0f --- /dev/null +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx @@ -0,0 +1,95 @@ +import React, { lazy, useState } from 'react' +import { observer } from 'mobx-react' +import { + Button, + DialogContent, + DialogActions, + Alert, + Stack, + Typography, + Switch, +} from '@mui/material' +import { Dialog } from '@jbrowse/core/ui' +import { ColorPicker } from '@jbrowse/core/ui/ColorPicker' + +// locals +import { GridBookmarkModel } from '../model' + +const EditBookmarksDialog = observer(function ({ + onClose, + model, +}: { + onClose: () => void + model: GridBookmarkModel +}) { + const { selectedBookmarks } = model + const editAll = selectedBookmarks.length === 0 + const [color, setColor] = useState('rgba(247, 129, 192, 0.35)') + + return ( + + + + {editAll ? ( + <> + All bookmarks will be edited. +
+ + Use the checkboxes to select individual bookmarks to edit. + + + ) : ( + 'Only selected bookmarks will be edited.' + )} +
+ Bulk highlight selector + { + setColor(event) + }} + /> + Highlight toggles + + { + model.setHighlightToggle(!model.highlightToggle) + }} + /> + + Toggle bookmark highlights on all open views + + + + { + model.setLabelToggle(!model.labelToggle) + }} + /> + + Toggle 'bookmark' icon on LGV tracks + + +
+ + + + +
+ ) +}) + +export default EditBookmarksDialog diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx index 92a26e7092..e9eb6e1da5 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx @@ -7,6 +7,7 @@ import { makeStyles } from 'tss-react/mui' import BookmarkGrid from './BookmarkGrid' import DeleteBookmarks from './DeleteBookmarks' import ExportBookmarks from './ExportBookmarks' +import EditBookmarks from './EditBookmarks' import ImportBookmarks from './ImportBookmarks' import AssemblySelector from './AssemblySelector' import ShareBookmarks from './ShareBookmarks' @@ -37,6 +38,7 @@ const GridBookmarkWidget = observer(function GridBookmarkWidget({ + Click and type within the label field to annotate your diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts index 119b9bb5e4..963344ca9e 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts @@ -9,6 +9,7 @@ import { import PluginManager from '@jbrowse/core/PluginManager' import { Region } from '@jbrowse/core/util/types' import { Region as RegionModel, ElementId } from '@jbrowse/core/util/types/mst' +import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view' import { getSession, @@ -90,7 +91,6 @@ export default function f(_pluginManager: PluginManager) { selectedBookmarks: [] as IExtendedLabeledRegionModel[], selectedAssembliesPre: undefined as string[] | undefined, })) - .views(self => ({ get bookmarkAssemblies() { return [...new Set(self.bookmarks.map(r => r.assemblyName))] @@ -101,6 +101,24 @@ export default function f(_pluginManager: PluginManager) { this.bookmarkAssemblies.filter(a => assemblyManager.get(a)), ) }, + get highlightToggle() { + const { views } = getSession(self) + const lgvs = views.filter(v => + Object.hasOwn(v, 'showBookmarkHighlights'), + ) + const res = (lgvs as LinearGenomeViewModel[]).map( + v => v.showBookmarkHighlights, + ) + return new Set(res).size === 1 && res[0] + }, + get labelToggle() { + const { views } = getSession(self) + const lgvs = views.filter(v => Object.hasOwn(v, 'showBookmarkLabels')) + const res = (lgvs as LinearGenomeViewModel[]).map( + v => v.showBookmarkLabels, + ) + return new Set(res).size === 1 && res[0] + }, })) .views(self => ({ get bookmarksWithValidAssemblies() { @@ -129,13 +147,11 @@ export default function f(_pluginManager: PluginManager) { }) }, })) - .actions(self => ({ setSelectedAssemblies(assemblies?: string[]) { self.selectedAssembliesPre = assemblies }, })) - .views(self => ({ get selectedAssemblies() { return ( @@ -167,12 +183,39 @@ export default function f(_pluginManager: PluginManager) { ) { bookmark.correspondingObj.setHighlight(color) }, + updateBulkBookmarkHighlights(color: string) { + if (self.selectedBookmarks.length === 0) { + self.bookmarks.forEach(bookmark => bookmark.setHighlight(color)) + } else { + self.selectedBookmarks.forEach(bookmark => + this.updateBookmarkHighlight(bookmark, color), + ) + } + }, setSelectedBookmarks(bookmarks: IExtendedLabeledRegionModel[]) { self.selectedBookmarks = bookmarks }, setBookmarkedRegions(regions: IMSTArray) { self.bookmarks = cast(regions) }, + setHighlightToggle(toggle: boolean) { + const { views } = getSession(self) + const lgvs = views.filter(v => + Object.hasOwn(v, 'showBookmarkHighlights'), + ) + + ;(lgvs as LinearGenomeViewModel[]).forEach(view => { + view.toggleShowBookmarkHighlights(toggle) + }) + }, + setLabelToggle(toggle: boolean) { + const { views } = getSession(self) + const lgvs = views.filter(v => Object.hasOwn(v, 'showBookmarkLabels')) + + ;(lgvs as LinearGenomeViewModel[]).forEach(view => { + view.toggleShowBookmarkLabels(toggle) + }) + }, })) .actions(self => ({ clearAllBookmarks() { diff --git a/plugins/grid-bookmark/src/index.ts b/plugins/grid-bookmark/src/index.ts index 8904dcb5d0..97b949f905 100644 --- a/plugins/grid-bookmark/src/index.ts +++ b/plugins/grid-bookmark/src/index.ts @@ -15,6 +15,8 @@ import { // icons import BookmarkIcon from '@mui/icons-material/Bookmark' import BookmarksIcon from '@mui/icons-material/Bookmarks' +import HighlightIcon from '@mui/icons-material/Highlight' +import LabelIcon from '@mui/icons-material/Label' import GridBookmarkWidgetF from './GridBookmarkWidget' import { GridBookmarkModel } from './GridBookmarkWidget/model' @@ -99,8 +101,14 @@ export default class extends Plugin { }, { label: 'Toggle bookmark highlights', + icon: HighlightIcon, onClick: () => self.toggleShowBookmarkHighlights(), }, + { + label: 'Toggle bookmark labels', + icon: LabelIcon, + onClick: () => self.toggleShowBookmarkLabels(), + }, ] }, diff --git a/plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx b/plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx index 4aa35d5f5b..8462e70f72 100644 --- a/plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx +++ b/plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx @@ -3,9 +3,11 @@ import { observer } from 'mobx-react' import { makeStyles } from 'tss-react/mui' import { getSession, localStorageGetItem } from '@jbrowse/core/util' +import BookmarkIcon from '@mui/icons-material/Bookmark' + // locals import { LinearGenomeViewModel } from '../model' -import { number } from 'prop-types' +import { Tooltip } from '@mui/material' type LGV = LinearGenomeViewModel @@ -15,13 +17,25 @@ const useStyles = makeStyles()({ position: 'absolute', textAlign: 'center', overflow: 'hidden', + display: 'flex', + alignItems: 'start', }, }) +const intensify = (rgba: string, opacity: number) => { + const values = rgba.replace(/[^\d,.]/g, '').split(',') + const originalOpacity = values.pop() + // originalOpacity !== '0' assumes if the user has set the opacity of this highlight to 0, they probably don't want to see the label either + const n = `rgba(${values.join(', ')}, ${ + originalOpacity !== '0' ? opacity : 0 + })` + return n +} + export default observer(function Highlight({ model }: { model: LGV }) { const { classes } = useStyles() - const { showBookmarkHighlights } = model + const { showBookmarkHighlights, showBookmarkLabels } = model const session = getSession(model) const assemblyNames = new Set(session.assemblyNames) @@ -74,6 +88,7 @@ export default observer(function Highlight({ model }: { model: LGV }) { width: Math.max(Math.abs(e.offsetPx - s.offsetPx), 3), left: Math.min(s.offsetPx, e.offsetPx) - model.offsetPx, highlight: r.highlight, + label: r.label, } : undefined }) @@ -82,12 +97,21 @@ export default observer(function Highlight({ model }: { model: LGV }) { f: any, ): f is { width: number; left: number; highlight: string } => !!f, ) - .map(({ left, width, highlight }: any, idx: number) => ( + .map(({ left, width, highlight, label }: any, idx: number) => (
+ > + {showBookmarkLabels ? ( + + + + ) : null} +
)) : null} diff --git a/plugins/linear-genome-view/src/LinearGenomeView/model.ts b/plugins/linear-genome-view/src/LinearGenomeView/model.ts index 51073fa406..16650f7465 100644 --- a/plugins/linear-genome-view/src/LinearGenomeView/model.ts +++ b/plugins/linear-genome-view/src/LinearGenomeView/model.ts @@ -245,6 +245,12 @@ export function stateModelFactory(pluginManager: PluginManager) { * show the bookmark highlights on this track */ showBookmarkHighlights: true, + + /** + * #property + * show the bookmark labels on this track + */ + showBookmarkLabels: true, }), ) .volatile(() => ({ @@ -583,12 +589,25 @@ export function stateModelFactory(pluginManager: PluginManager) { toggleShowGridlines() { self.showGridlines = !self.showGridlines }, - /** * #action */ - toggleShowBookmarkHighlights() { - self.showBookmarkHighlights = !self.showBookmarkHighlights + toggleShowBookmarkHighlights(toggle?: boolean) { + if (toggle !== undefined) { + self.showBookmarkHighlights = toggle + } else { + self.showBookmarkHighlights = !self.showBookmarkHighlights + } + }, + /** + * #action + */ + toggleShowBookmarkLabels(toggle?: boolean) { + if (toggle !== undefined) { + self.showBookmarkLabels = toggle + } else { + self.showBookmarkLabels = !self.showBookmarkLabels + } }, /** * #action From 785b55f9fe850c8169f598be89177bc6d2cf488c Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Thu, 9 Nov 2023 11:57:29 -0500 Subject: [PATCH 03/25] Moving Highlight to GridBookmarkPlugin, lint, various checks and UI --- .../components/BookmarkGrid.tsx | 14 +- .../components/EditBookmarksDialog.tsx | 28 ++-- .../components/GridBookmarkWidget.tsx | 2 +- .../components/Highlight/Highlight.tsx | 121 ++++++++++++++++++ .../components/Highlight/index.tsx | 22 ++++ .../src/GridBookmarkWidget/index.ts | 6 +- .../src/GridBookmarkWidget/model.ts | 4 +- .../LinearGenomeView/components/Highlight.tsx | 119 ----------------- .../components/TracksContainer.tsx | 2 - .../src/LinearGenomeView/model.ts | 14 +- 10 files changed, 172 insertions(+), 160 deletions(-) create mode 100644 plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx create mode 100644 plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/index.tsx delete mode 100644 plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx index 219254ba17..216f1e366e 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx @@ -1,4 +1,4 @@ -import React, { Suspense, lazy, useEffect, useState } from 'react' +import React, { Suspense, lazy, useState } from 'react' import { observer } from 'mobx-react' import { Link } from '@mui/material' import { makeStyles } from 'tss-react/mui' @@ -61,7 +61,7 @@ const BookmarkGrid = observer(function ({ } }) - const rowWidths = [ + const [widths, setWidths] = useState([ 40, Math.max( measureText('Bookmark link', 12) + 30, @@ -76,15 +76,7 @@ const BookmarkGrid = observer(function ({ measureGridWidth(rows.map(row => row.assemblyName)), ), 100, - ] - - const [widths, setWidths] = useState(rowWidths) - - useEffect(() => { - if (rows.length > 0) { - setWidths(rowWidths) - } - }, [rows.length, setWidths]) + ]) return ( <> diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx index 2e5d9ccf0f..a6b91a34b1 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx @@ -1,4 +1,4 @@ -import React, { lazy, useState } from 'react' +import React, { useState } from 'react' import { observer } from 'mobx-react' import { Button, @@ -23,17 +23,18 @@ const EditBookmarksDialog = observer(function ({ model: GridBookmarkModel }) { const { selectedBookmarks } = model - const editAll = selectedBookmarks.length === 0 - const [color, setColor] = useState('rgba(247, 129, 192, 0.35)') + const editNone = selectedBookmarks.length === 0 + const [color, setColor] = useState( + selectedBookmarks[0].highlight ?? 'rgba(247, 129, 192, 0.35)', + ) return ( + Bulk highlight selector - {editAll ? ( + {editNone ? ( <> - All bookmarks will be edited. -
Use the checkboxes to select individual bookmarks to edit. @@ -42,13 +43,14 @@ const EditBookmarksDialog = observer(function ({ 'Only selected bookmarks will be edited.' )}
- Bulk highlight selector - { - setColor(event) - }} - /> + {!editNone ? ( + { + setColor(event) + }} + /> + ) : null} Highlight toggles - + Click and type within the label field to annotate your diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx new file mode 100644 index 0000000000..704000a931 --- /dev/null +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx @@ -0,0 +1,121 @@ +import React from 'react' +import { observer } from 'mobx-react' +import { makeStyles } from 'tss-react/mui' +import { getSession, isSessionModelWithWidgets } from '@jbrowse/core/util' +import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view' + +// icons +import BookmarkIcon from '@mui/icons-material/Bookmark' + +// locals +import { Tooltip } from '@mui/material' +import { GridBookmarkModel } from '../../model' + +type LGV = LinearGenomeViewModel + +const useStyles = makeStyles()({ + highlight: { + height: '100%', + position: 'absolute', + textAlign: 'center', + overflow: 'hidden', + display: 'flex', + alignItems: 'start', + }, +}) + +interface BookmarkInfo { + width: number + left: number + highlight: string + label: string +} + +interface LocalStorageBookmark { + start: number + end: number + assemblyName: string + refName: string + reversed: boolean + label: string + highlight: string +} + +const intensify = (rgba: string, opacity: number) => { + if (rgba) { + const values = rgba.replaceAll(/[^\d,.]/g, '').split(',') + const originalOpacity = values.pop() + // originalOpacity !== '0' assumes if the user has set the opacity of this highlight to 0, they probably don't want to see the label either + const n = `rgba(${values.join(', ')}, ${ + originalOpacity !== '0' ? opacity : 0 + })` + return n + } + return `rgba(0,0,0,0)` +} + +const Highlight = observer(function Highlight({ model }: { model: LGV }) { + const { classes } = useStyles() + + const session = getSession(model) + if (!isSessionModelWithWidgets(session)) { + return null + } + + const { showBookmarkHighlights, showBookmarkLabels } = model + const assemblyNames = new Set(session.assemblyNames) + const { bookmarks } = session.widgets.get('GridBookmark') as GridBookmarkModel + + return ( + <> + {showBookmarkHighlights + ? bookmarks + .filter((value: LocalStorageBookmark) => + assemblyNames.has(value.assemblyName), + ) + .map((r: LocalStorageBookmark) => { + const s = model.bpToPx({ + refName: r.refName, + coord: r.start, + }) + const e = model.bpToPx({ + refName: r.refName, + coord: r.end, + }) + return s && e + ? { + width: Math.max(Math.abs(e.offsetPx - s.offsetPx), 3), + left: Math.min(s.offsetPx, e.offsetPx) - model.offsetPx, + highlight: r.highlight, + label: r.label, + } + : undefined + }) + .filter((f: BookmarkInfo | undefined): f is BookmarkInfo => !!f) + .map( + ( + { left, width, highlight, label }: BookmarkInfo, + idx: number, + ) => ( +
+ {showBookmarkLabels ? ( + + + + ) : null} +
+ ), + ) + : null} + + ) +}) + +export default Highlight diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/index.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/index.tsx new file mode 100644 index 0000000000..42f096f774 --- /dev/null +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/index.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import PluginManager from '@jbrowse/core/PluginManager' +import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view' + +// locals +import Highlight from './Highlight' + +export default function AddHighlightModelF(pluginManager: PluginManager) { + pluginManager.addToExtensionPoint( + 'LinearGenomeView-TracksContainerComponent', + // @ts-expect-error + ( + rest: React.ReactNode[] = [], + { model }: { model: LinearGenomeViewModel }, + ) => { + return [ + ...rest, + , + ] + }, + ) +} diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/index.ts b/plugins/grid-bookmark/src/GridBookmarkWidget/index.ts index c464691af7..998b438656 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/index.ts +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/index.ts @@ -1,9 +1,12 @@ import { lazy } from 'react' import { ConfigurationSchema } from '@jbrowse/core/configuration' import { WidgetType } from '@jbrowse/core/pluggableElementTypes' -import stateModelFactory from './model' import PluginManager from '@jbrowse/core/PluginManager' +// locals +import stateModelFactory from './model' +import AddHighlightModelF from './components/Highlight' + const configSchema = ConfigurationSchema('GridBookmarkWidget', {}) export default (pluginManager: PluginManager) => { @@ -16,4 +19,5 @@ export default (pluginManager: PluginManager) => { ReactComponent: lazy(() => import('./components/GridBookmarkWidget')), }) }) + AddHighlightModelF(pluginManager) } diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts index 963344ca9e..8cba9c694b 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts @@ -184,9 +184,7 @@ export default function f(_pluginManager: PluginManager) { bookmark.correspondingObj.setHighlight(color) }, updateBulkBookmarkHighlights(color: string) { - if (self.selectedBookmarks.length === 0) { - self.bookmarks.forEach(bookmark => bookmark.setHighlight(color)) - } else { + if (self.selectedBookmarks.length !== 0) { self.selectedBookmarks.forEach(bookmark => this.updateBookmarkHighlight(bookmark, color), ) diff --git a/plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx b/plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx deleted file mode 100644 index 8462e70f72..0000000000 --- a/plugins/linear-genome-view/src/LinearGenomeView/components/Highlight.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React, { useEffect, useState } from 'react' -import { observer } from 'mobx-react' -import { makeStyles } from 'tss-react/mui' -import { getSession, localStorageGetItem } from '@jbrowse/core/util' - -import BookmarkIcon from '@mui/icons-material/Bookmark' - -// locals -import { LinearGenomeViewModel } from '../model' -import { Tooltip } from '@mui/material' - -type LGV = LinearGenomeViewModel - -const useStyles = makeStyles()({ - highlight: { - height: '100%', - position: 'absolute', - textAlign: 'center', - overflow: 'hidden', - display: 'flex', - alignItems: 'start', - }, -}) - -const intensify = (rgba: string, opacity: number) => { - const values = rgba.replace(/[^\d,.]/g, '').split(',') - const originalOpacity = values.pop() - // originalOpacity !== '0' assumes if the user has set the opacity of this highlight to 0, they probably don't want to see the label either - const n = `rgba(${values.join(', ')}, ${ - originalOpacity !== '0' ? opacity : 0 - })` - return n -} - -export default observer(function Highlight({ model }: { model: LGV }) { - const { classes } = useStyles() - - const { showBookmarkHighlights, showBookmarkLabels } = model - const session = getSession(model) - const assemblyNames = new Set(session.assemblyNames) - - const localStorageKeyF = () => - typeof window !== undefined - ? `bookmarks-${[window.location.host + window.location.pathname].join( - '-', - )}` - : 'empty' - - const localStorage = JSON.parse( - localStorageGetItem(localStorageKeyF()) || '[]', - ) - - const [bookmarks, setBookmarks] = useState( - localStorage.filter((value: any) => assemblyNames.has(value.assemblyName)), - ) - - window.addEventListener('storage', () => { - setBookmarks( - localStorage.filter((value: any) => - assemblyNames.has(value.assemblyName), - ), - ) - }) - - useEffect(() => { - setBookmarks( - localStorage.filter((value: any) => - assemblyNames.has(value.assemblyName), - ), - ) - }, [localStorage?.length, JSON.stringify(localStorage)]) - - return ( - <> - {showBookmarkHighlights - ? bookmarks - .map((r: any) => { - const s = model.bpToPx({ - refName: r.refName, - coord: r.start, - }) - const e = model.bpToPx({ - refName: r.refName, - coord: r.end, - }) - return s && e - ? { - width: Math.max(Math.abs(e.offsetPx - s.offsetPx), 3), - left: Math.min(s.offsetPx, e.offsetPx) - model.offsetPx, - highlight: r.highlight, - label: r.label, - } - : undefined - }) - .filter( - ( - f: any, - ): f is { width: number; left: number; highlight: string } => !!f, - ) - .map(({ left, width, highlight, label }: any, idx: number) => ( -
- {showBookmarkLabels ? ( - - - - ) : null} -
- )) - : null} - - ) -}) diff --git a/plugins/linear-genome-view/src/LinearGenomeView/components/TracksContainer.tsx b/plugins/linear-genome-view/src/LinearGenomeView/components/TracksContainer.tsx index a5a24e9615..fcb005f093 100644 --- a/plugins/linear-genome-view/src/LinearGenomeView/components/TracksContainer.tsx +++ b/plugins/linear-genome-view/src/LinearGenomeView/components/TracksContainer.tsx @@ -15,7 +15,6 @@ import Gridlines from './Gridlines' import CenterLine from './CenterLine' import VerticalGuide from './VerticalGuide' import RubberbandSpan from './RubberbandSpan' -import Highlight from './Highlight' const useStyles = makeStyles()({ tracksContainer: { @@ -109,7 +108,6 @@ const TracksContainer = observer(function TracksContainer({ } /> {additional} - {children} ) diff --git a/plugins/linear-genome-view/src/LinearGenomeView/model.ts b/plugins/linear-genome-view/src/LinearGenomeView/model.ts index 16650f7465..1d16706bb5 100644 --- a/plugins/linear-genome-view/src/LinearGenomeView/model.ts +++ b/plugins/linear-genome-view/src/LinearGenomeView/model.ts @@ -593,21 +593,15 @@ export function stateModelFactory(pluginManager: PluginManager) { * #action */ toggleShowBookmarkHighlights(toggle?: boolean) { - if (toggle !== undefined) { - self.showBookmarkHighlights = toggle - } else { - self.showBookmarkHighlights = !self.showBookmarkHighlights - } + self.showBookmarkHighlights = + toggle !== undefined ? toggle : !self.showBookmarkHighlights }, /** * #action */ toggleShowBookmarkLabels(toggle?: boolean) { - if (toggle !== undefined) { - self.showBookmarkLabels = toggle - } else { - self.showBookmarkLabels = !self.showBookmarkLabels - } + self.showBookmarkLabels = + toggle !== undefined ? toggle : !self.showBookmarkLabels }, /** * #action From 77984ae4487e39c326ea99f3ea8d40d8bf8af8a0 Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Thu, 9 Nov 2023 14:53:53 -0500 Subject: [PATCH 04/25] listen for storage events in other tabs --- plugins/grid-bookmark/src/GridBookmarkWidget/model.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts index 8cba9c694b..6e496cee47 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts @@ -233,6 +233,16 @@ export default function f(_pluginManager: PluginManager) { .actions(self => ({ afterAttach() { const key = localStorageKeyF() + function handler(e: StorageEvent) { + if (e.key === key) { + const localStorage = JSON.parse(localStorageGetItem(key) || '[]') + self.setBookmarkedRegions(localStorage) + } + } + window.addEventListener('storage', handler) + addDisposer(self, () => { + window.removeEventListener('storage', handler) + }) addDisposer( self, autorun(() => { From 7a69acf181cdd705c49621f5e3d57893d922d7fc Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Thu, 9 Nov 2023 17:49:54 -0500 Subject: [PATCH 05/25] update menu items to submenu and tests --- .../components/EditBookmarksDialog.tsx | 2 +- .../components/Highlight/Highlight.tsx | 8 +++- plugins/grid-bookmark/src/index.ts | 44 ++++++++++++------- .../src/tests/BookmarkWidget.test.tsx | 2 + .../BookmarkWidget.test.tsx.snap | 3 ++ 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx index a6b91a34b1..f285eba018 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx @@ -25,7 +25,7 @@ const EditBookmarksDialog = observer(function ({ const { selectedBookmarks } = model const editNone = selectedBookmarks.length === 0 const [color, setColor] = useState( - selectedBookmarks[0].highlight ?? 'rgba(247, 129, 192, 0.35)', + selectedBookmarks[0]?.highlight ?? 'rgba(247, 129, 192, 0.35)', ) return ( diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx index 704000a931..b982fa36c5 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx @@ -64,7 +64,13 @@ const Highlight = observer(function Highlight({ model }: { model: LGV }) { const { showBookmarkHighlights, showBookmarkLabels } = model const assemblyNames = new Set(session.assemblyNames) - const { bookmarks } = session.widgets.get('GridBookmark') as GridBookmarkModel + const bmModel = session.widgets.get('GridBookmark') as GridBookmarkModel + + if (!bmModel) { + return null + } + + const { bookmarks } = bmModel return ( <> diff --git a/plugins/grid-bookmark/src/index.ts b/plugins/grid-bookmark/src/index.ts index 97b949f905..85a3a4bf07 100644 --- a/plugins/grid-bookmark/src/index.ts +++ b/plugins/grid-bookmark/src/index.ts @@ -90,24 +90,34 @@ export default class extends Plugin { ...superMenuItems(), { type: 'divider' }, { - label: 'Open bookmark widget', + label: 'Bookmarks', icon: BookmarksIcon, - onClick: () => self.activateBookmarkWidget(), - }, - { - label: 'Bookmark current region', - icon: BookmarkIcon, - onClick: () => self.bookmarkCurrentRegion(), - }, - { - label: 'Toggle bookmark highlights', - icon: HighlightIcon, - onClick: () => self.toggleShowBookmarkHighlights(), - }, - { - label: 'Toggle bookmark labels', - icon: LabelIcon, - onClick: () => self.toggleShowBookmarkLabels(), + subMenu: [ + { + label: 'Open bookmark widget', + icon: BookmarksIcon, + onClick: () => self.activateBookmarkWidget(), + }, + { + label: 'Bookmark current region', + icon: BookmarkIcon, + onClick: () => self.bookmarkCurrentRegion(), + }, + { + label: 'Toggle bookmark highlights', + icon: HighlightIcon, + type: 'checkbox', + checked: self.showBookmarkHighlights, + onClick: () => self.toggleShowBookmarkHighlights(), + }, + { + label: 'Toggle bookmark labels', + icon: LabelIcon, + type: 'checkbox', + checked: self.showBookmarkLabels, + onClick: () => self.toggleShowBookmarkLabels(), + }, + ], }, ] }, diff --git a/products/jbrowse-web/src/tests/BookmarkWidget.test.tsx b/products/jbrowse-web/src/tests/BookmarkWidget.test.tsx index 7a567b2fb4..fa3396818b 100644 --- a/products/jbrowse-web/src/tests/BookmarkWidget.test.tsx +++ b/products/jbrowse-web/src/tests/BookmarkWidget.test.tsx @@ -72,6 +72,7 @@ test('using the menu button to bookmark the current region', async () => { const user = userEvent.setup() await user.click(await findByTestId('trackContainer')) await user.click(await findByTestId('view_menu_icon')) + await user.click(await findByText('Bookmarks')) await user.click(await findByText('Bookmark current region')) // @ts-expect-error @@ -104,6 +105,7 @@ test('using the hotkey to navigate to the most recently created bookmark', async const user = userEvent.setup() await user.click(await findByTestId('view_menu_icon')) + await user.click(await findByText('Bookmarks')) await user.click(await findByText('Open bookmark widget')) // @ts-expect-error diff --git a/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap b/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap index 54c22ef313..6e3288c3d4 100644 --- a/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap +++ b/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap @@ -5,6 +5,7 @@ exports[`using the click and drag rubberband 1`] = ` { "assemblyName": "volvox", "end": 113, + "highlight": "rgba(0, 75, 58, 0.3)", "label": "", "refName": "ctgA", "reversed": false, @@ -18,6 +19,7 @@ exports[`using the hotkey to bookmark the current region 1`] = ` { "assemblyName": "volvox", "end": 140, + "highlight": "rgba(0, 75, 58, 0.3)", "label": "", "refName": "ctgA", "reversed": false, @@ -31,6 +33,7 @@ exports[`using the menu button to bookmark the current region 1`] = ` { "assemblyName": "volvox", "end": 140, + "highlight": "rgba(0, 75, 58, 0.3)", "label": "", "refName": "ctgA", "reversed": false, From 9c68872ba8da0c64d74ddedc0e550ed3a2378b21 Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Fri, 10 Nov 2023 07:51:25 -0500 Subject: [PATCH 06/25] make default color of highlight static --- plugins/grid-bookmark/src/GridBookmarkWidget/model.ts | 7 +------ .../src/tests/__snapshots__/BookmarkWidget.test.tsx.snap | 6 +++--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts index 6e496cee47..f875873a0d 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts @@ -23,12 +23,7 @@ const LabeledRegionModel = types RegionModel, types.model('Label', { label: types.optional(types.string, ''), - highlight: types.optional( - types.string, - `rgba(${Math.round(Math.random() * 255)}, ${Math.round( - Math.random() * 255, - )}, ${Math.round(Math.random() * 255)}, 0.3)`, - ), + highlight: types.optional(types.string, 'rgba(247, 129, 192, 0.35)'), }), ) .actions(self => ({ diff --git a/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap b/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap index 6e3288c3d4..32136cda2d 100644 --- a/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap +++ b/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap @@ -5,7 +5,7 @@ exports[`using the click and drag rubberband 1`] = ` { "assemblyName": "volvox", "end": 113, - "highlight": "rgba(0, 75, 58, 0.3)", + "highlight": "rgba(247, 129, 192, 0.35)", "label": "", "refName": "ctgA", "reversed": false, @@ -19,7 +19,7 @@ exports[`using the hotkey to bookmark the current region 1`] = ` { "assemblyName": "volvox", "end": 140, - "highlight": "rgba(0, 75, 58, 0.3)", + "highlight": "rgba(247, 129, 192, 0.35)", "label": "", "refName": "ctgA", "reversed": false, @@ -33,7 +33,7 @@ exports[`using the menu button to bookmark the current region 1`] = ` { "assemblyName": "volvox", "end": 140, - "highlight": "rgba(0, 75, 58, 0.3)", + "highlight": "rgba(247, 129, 192, 0.35)", "label": "", "refName": "ctgA", "reversed": false, From c0d6f8d99aec9fe7278f805d7951ef9ea4048a9c Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Fri, 10 Nov 2023 09:03:05 -0500 Subject: [PATCH 07/25] ensure highlights display on load new session --- .../components/Highlight/Highlight.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx index b982fa36c5..bfb24e3396 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx @@ -62,15 +62,17 @@ const Highlight = observer(function Highlight({ model }: { model: LGV }) { return null } - const { showBookmarkHighlights, showBookmarkLabels } = model - const assemblyNames = new Set(session.assemblyNames) - const bmModel = session.widgets.get('GridBookmark') as GridBookmarkModel - - if (!bmModel) { - return null + let bookmarkWidget = session.widgets.get('GridBookmark') as GridBookmarkModel + if (!bookmarkWidget) { + bookmarkWidget = session.addWidget( + 'GridBookmarkWidget', + 'GridBookmark', + ) as GridBookmarkModel } - const { bookmarks } = bmModel + const { showBookmarkHighlights, showBookmarkLabels } = model + const assemblyNames = new Set(session.assemblyNames) + const { bookmarks } = bookmarkWidget return ( <> From e67e7cc4557718816d236e819caa5acae0a2d6b4 Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Fri, 10 Nov 2023 09:19:54 -0500 Subject: [PATCH 08/25] edit dialog > highlight dialog --- .../components/GridBookmarkWidget.tsx | 4 ++-- .../{EditBookmarks.tsx => HighlightBookmarks.tsx} | 13 +++++++++---- ...marksDialog.tsx => HighlightBookmarksDialog.tsx} | 4 ++-- 3 files changed, 13 insertions(+), 8 deletions(-) rename plugins/grid-bookmark/src/GridBookmarkWidget/components/{EditBookmarks.tsx => HighlightBookmarks.tsx} (63%) rename plugins/grid-bookmark/src/GridBookmarkWidget/components/{EditBookmarksDialog.tsx => HighlightBookmarksDialog.tsx} (96%) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx index 79ce81ef3f..d1360fe7d0 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx @@ -7,7 +7,7 @@ import { makeStyles } from 'tss-react/mui' import BookmarkGrid from './BookmarkGrid' import DeleteBookmarks from './DeleteBookmarks' import ExportBookmarks from './ExportBookmarks' -import EditBookmarks from './EditBookmarks' +import HighlightBookmarks from './HighlightBookmarks' import ImportBookmarks from './ImportBookmarks' import AssemblySelector from './AssemblySelector' import ShareBookmarks from './ShareBookmarks' @@ -37,7 +37,7 @@ const GridBookmarkWidget = observer(function GridBookmarkWidget({ - + diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarks.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarks.tsx similarity index 63% rename from plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarks.tsx rename to plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarks.tsx index 480381a980..a31bf5e611 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarks.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarks.tsx @@ -6,9 +6,11 @@ import EditIcon from '@mui/icons-material/Edit' // locals import { GridBookmarkModel } from '../model' -const EditBookmarksDialog = lazy(() => import('./EditBookmarksDialog')) +const HighlightBookmarksDialog = lazy( + () => import('./HighlightBookmarksDialog'), +) -function EditBookmarks({ model }: { model: GridBookmarkModel }) { +function HighlightBookmarks({ model }: { model: GridBookmarkModel }) { const [open, setOpen] = useState(false) return ( <> @@ -21,11 +23,14 @@ function EditBookmarks({ model }: { model: GridBookmarkModel }) { {open ? ( }> - setOpen(false)} /> + setOpen(false)} + /> ) : null} ) } -export default EditBookmarks +export default HighlightBookmarks diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx similarity index 96% rename from plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx rename to plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx index f285eba018..0345fdf597 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarksDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx @@ -15,7 +15,7 @@ import { ColorPicker } from '@jbrowse/core/ui/ColorPicker' // locals import { GridBookmarkModel } from '../model' -const EditBookmarksDialog = observer(function ({ +const HighlightBookmarksDialog = observer(function ({ onClose, model, }: { @@ -94,4 +94,4 @@ const EditBookmarksDialog = observer(function ({ ) }) -export default EditBookmarksDialog +export default HighlightBookmarksDialog From 226e74860fcfab76583ef24412c7593679b283ad Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Fri, 10 Nov 2023 11:49:47 -0500 Subject: [PATCH 09/25] update tests --- .../components/HighlightBookmarksDialog.tsx | 2 + .../src/tests/BookmarkWidget.test.tsx | 91 +++++++++++++++++-- .../BookmarkWidget.test.tsx.snap | 6 +- 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx index 0345fdf597..5d9e11af49 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx @@ -54,6 +54,7 @@ const HighlightBookmarksDialog = observer(function ({ Highlight toggles { model.setHighlightToggle(!model.highlightToggle) @@ -65,6 +66,7 @@ const HighlightBookmarksDialog = observer(function ({ { model.setLabelToggle(!model.labelToggle) diff --git a/products/jbrowse-web/src/tests/BookmarkWidget.test.tsx b/products/jbrowse-web/src/tests/BookmarkWidget.test.tsx index fa3396818b..1ebf5f748c 100644 --- a/products/jbrowse-web/src/tests/BookmarkWidget.test.tsx +++ b/products/jbrowse-web/src/tests/BookmarkWidget.test.tsx @@ -13,7 +13,7 @@ beforeEach(() => { const delay = { timeout: 30000 } const opts = [{}, delay] -test('from the top level menu', async () => { +test('Open the bookmarks widget from the top level menu', async () => { const { findByText } = await createView() const user = userEvent.setup() @@ -23,7 +23,7 @@ test('from the top level menu', async () => { expect(await findByText('Bookmarked regions')).toBeTruthy() }) -test('from the view menu', async () => { +test('Open the bookmarks widget from the view menu', async () => { const { findByTestId, findByText } = await createView() const user = userEvent.setup() @@ -33,7 +33,7 @@ test('from the view menu', async () => { expect(await findByText('Bookmarked regions')).toBeTruthy() }) -test('using the click and drag rubberband', async () => { +test('Create a bookmark using the click and drag rubberband', async () => { const { session, findByTestId, findByText } = await createView() const rubberband = await findByTestId('rubberband_controls', {}, delay) @@ -47,7 +47,7 @@ test('using the click and drag rubberband', async () => { expect(bookmarks).toMatchSnapshot() }, 40000) -test('using the hotkey to bookmark the current region', async () => { +test('Create a bookmark using the hotkey to bookmark the current region', async () => { const { session, findByTestId } = await createView() const user = userEvent.setup() @@ -66,7 +66,7 @@ test('using the hotkey to bookmark the current region', async () => { expect(bookmarks).toMatchSnapshot() }) -test('using the menu button to bookmark the current region', async () => { +test('Create a bookmark using the menu button to bookmark the current region', async () => { const { session, findByTestId, findByText } = await createView() const user = userEvent.setup() @@ -80,7 +80,7 @@ test('using the menu button to bookmark the current region', async () => { expect(bookmarks).toMatchSnapshot() }, 40000) -test('using the embedded link in the widget data grid', async () => { +test('Navigate to a bookmark using the embedded link in the widget data grid', async () => { const { view, session, findByTestId, findByText } = await createView() const user = userEvent.setup() @@ -100,7 +100,7 @@ test('using the embedded link in the widget data grid', async () => { await waitFor(() => expect(view.visibleLocStrings).toBe('ctgA:201..240')) }, 40000) -test('using the hotkey to navigate to the most recently created bookmark', async () => { +test('Navigate to a bookmark using the hotkey to navigate to the most recently created bookmark', async () => { const { view, session, findByTestId, findByText } = await createView() const user = userEvent.setup() @@ -128,7 +128,7 @@ test('using the hotkey to navigate to the most recently created bookmark', async await waitFor(() => expect(view.visibleLocStrings).toBe('ctgA:201..240')) }, 40000) -test('with a single click on the data grid', async () => { +test('Edit a bookmark label with a single click on the data grid', async () => { const { session, findByText, findAllByRole } = await createView() const user = userEvent.setup() @@ -151,7 +151,7 @@ test('with a single click on the data grid', async () => { expect(field.innerHTML).toContain('new label') }) -test('with a double click via the dialog', async () => { +test('Edit a bookmark label with a double click via the dialog', async () => { const { session, findByText, findAllByRole, findByTestId } = await createView() @@ -175,3 +175,76 @@ test('with a double click via the dialog', async () => { await user.click(await findByText('Confirm')) expect(field.innerHTML).toContain('new label') }) +test('Toggle highlight visibility across all views', async () => { + const { session, findByText, findByTestId, findAllByTestId } = + await createView() + + session.addView('LinearGenomeView', { + displayedRegions: [ + { + assemblyName: 'volMyt1', + refName: 'ctgA', + start: 0, + end: 1000, + }, + ], + }) + + const user = userEvent.setup() + + await user.click(await findByText('Tools')) + await user.click(await findByText('Bookmarks')) + + // @ts-expect-error + const bookmarkWidget = session.widgets.get('GridBookmark') + bookmarkWidget.addBookmark({ + start: 200, + end: 240, + refName: 'ctgA', + assemblyName: 'volvox', + }) + + const highlight = (await findAllByTestId('BookmarkIcon'))[0] + const highlight2 = (await findAllByTestId('BookmarkIcon'))[1] + + expect(highlight).toBeDefined + expect(highlight2).toBeDefined + + await user.click(await findByText('Highlight')) + await user.click(await findByTestId('toggle_highlight_all_switch')) + await user.click(await findByText('Confirm')) + + expect(highlight).toBeUndefined + expect(highlight2).toBeUndefined +}) +test('Toggle highlight label visibility across all views', async () => { + const { session, findByText, findByTestId, findAllByTestId } = + await createView() + + const user = userEvent.setup() + + await user.click(await findByText('Tools')) + await user.click(await findByText('Bookmarks')) + + // @ts-expect-error + const bookmarkWidget = session.widgets.get('GridBookmark') + bookmarkWidget.addBookmark({ + start: 200, + end: 240, + refName: 'ctgA', + assemblyName: 'volvox', + }) + + const highlight = (await findAllByTestId('BookmarkIcon'))[0] + const highlight2 = (await findAllByTestId('BookmarkIcon'))[1] + + expect(highlight).toBeDefined + expect(highlight2).toBeDefined + + await user.click(await findByText('Highlight')) + await user.click(await findByTestId('toggle_highlight_label_all_switch')) + await user.click(await findByText('Confirm')) + + expect(highlight).toBeUndefined + expect(highlight2).toBeUndefined +}) diff --git a/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap b/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap index 32136cda2d..41d4237a24 100644 --- a/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap +++ b/products/jbrowse-web/src/tests/__snapshots__/BookmarkWidget.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`using the click and drag rubberband 1`] = ` +exports[`Create a bookmark using the click and drag rubberband 1`] = ` [ { "assemblyName": "volvox", @@ -14,7 +14,7 @@ exports[`using the click and drag rubberband 1`] = ` ] `; -exports[`using the hotkey to bookmark the current region 1`] = ` +exports[`Create a bookmark using the hotkey to bookmark the current region 1`] = ` [ { "assemblyName": "volvox", @@ -28,7 +28,7 @@ exports[`using the hotkey to bookmark the current region 1`] = ` ] `; -exports[`using the menu button to bookmark the current region 1`] = ` +exports[`Create a bookmark using the menu button to bookmark the current region 1`] = ` [ { "assemblyName": "volvox", From f6bc113849dbbd827ee29f379f25805313395966 Mon Sep 17 00:00:00 2001 From: Colin Date: Mon, 20 Nov 2023 10:46:23 -0500 Subject: [PATCH 10/25] Avoid doing manual typing for callbacks --- .../components/Highlight/Highlight.tsx | 68 +++++++------------ 1 file changed, 24 insertions(+), 44 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx index bfb24e3396..327658f239 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx @@ -1,7 +1,11 @@ import React from 'react' import { observer } from 'mobx-react' import { makeStyles } from 'tss-react/mui' -import { getSession, isSessionModelWithWidgets } from '@jbrowse/core/util' +import { + getSession, + isSessionModelWithWidgets, + notEmpty, +} from '@jbrowse/core/util' import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view' // icons @@ -24,23 +28,6 @@ const useStyles = makeStyles()({ }, }) -interface BookmarkInfo { - width: number - left: number - highlight: string - label: string -} - -interface LocalStorageBookmark { - start: number - end: number - assemblyName: string - refName: string - reversed: boolean - label: string - highlight: string -} - const intensify = (rgba: string, opacity: number) => { if (rgba) { const values = rgba.replaceAll(/[^\d,.]/g, '').split(',') @@ -78,10 +65,8 @@ const Highlight = observer(function Highlight({ model }: { model: LGV }) { <> {showBookmarkHighlights ? bookmarks - .filter((value: LocalStorageBookmark) => - assemblyNames.has(value.assemblyName), - ) - .map((r: LocalStorageBookmark) => { + .filter(value => assemblyNames.has(value.assemblyName)) + .map(r => { const s = model.bpToPx({ refName: r.refName, coord: r.start, @@ -99,28 +84,23 @@ const Highlight = observer(function Highlight({ model }: { model: LGV }) { } : undefined }) - .filter((f: BookmarkInfo | undefined): f is BookmarkInfo => !!f) - .map( - ( - { left, width, highlight, label }: BookmarkInfo, - idx: number, - ) => ( -
- {showBookmarkLabels ? ( - - - - ) : null} -
- ), - ) + .filter(notEmpty) + .map(({ left, width, highlight, label }, idx) => ( +
+ {showBookmarkLabels ? ( + + + + ) : null} +
+ )) : null} ) From 051e0555af75e619b49f5a8a0ef4de75c95df83c Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Wed, 22 Nov 2023 12:31:26 -0500 Subject: [PATCH 11/25] review feedback --- packages/core/util/index.ts | 1 - .../components/Highlight/Highlight.tsx | 20 ++++----- .../components/HighlightBookmarksDialog.tsx | 12 +++-- .../src/GridBookmarkWidget/model.ts | 45 +++++++++---------- plugins/grid-bookmark/src/index.ts | 26 +++++++++++ .../src/LinearGenomeView/model.ts | 26 ----------- 6 files changed, 66 insertions(+), 64 deletions(-) diff --git a/packages/core/util/index.ts b/packages/core/util/index.ts index d978e1d292..d33ec772e0 100644 --- a/packages/core/util/index.ts +++ b/packages/core/util/index.ts @@ -1268,7 +1268,6 @@ export function localStorageSetItem(str: string, item: string) { typeof localStorage !== 'undefined' ? localStorage.setItem(str, item) : undefined - window.dispatchEvent(new Event('storage')) return returnVal } diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx index 327658f239..7e8d9090ae 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useState } from 'react' import { observer } from 'mobx-react' import { makeStyles } from 'tss-react/mui' import { @@ -6,7 +6,7 @@ import { isSessionModelWithWidgets, notEmpty, } from '@jbrowse/core/util' -import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view' +import { IExtendedLGV } from '../../model' // icons import BookmarkIcon from '@mui/icons-material/Bookmark' @@ -15,7 +15,7 @@ import BookmarkIcon from '@mui/icons-material/Bookmark' import { Tooltip } from '@mui/material' import { GridBookmarkModel } from '../../model' -type LGV = LinearGenomeViewModel +type LGV = IExtendedLGV const useStyles = makeStyles()({ highlight: { @@ -49,13 +49,13 @@ const Highlight = observer(function Highlight({ model }: { model: LGV }) { return null } - let bookmarkWidget = session.widgets.get('GridBookmark') as GridBookmarkModel - if (!bookmarkWidget) { - bookmarkWidget = session.addWidget( - 'GridBookmarkWidget', - 'GridBookmark', - ) as GridBookmarkModel - } + const [bookmarkWidget, _] = useState( + (session.widgets.get('GridBookmark') as GridBookmarkModel) ?? + (session.addWidget( + 'GridBookmarkWidget', + 'GridBookmark', + ) as GridBookmarkModel), + ) const { showBookmarkHighlights, showBookmarkLabels } = model const assemblyNames = new Set(session.assemblyNames) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx index 5d9e11af49..c15bf50ec2 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx @@ -55,9 +55,11 @@ const HighlightBookmarksDialog = observer(function ({ { - model.setHighlightToggle(!model.highlightToggle) + model.setHighlightToggle( + !model.areBookmarksHighlightedOnAllOpenViews, + ) }} /> @@ -67,9 +69,11 @@ const HighlightBookmarksDialog = observer(function ({ { - model.setLabelToggle(!model.labelToggle) + model.setLabelToggle( + !model.areBookmarksHighlightLabelsOnAllOpenViews, + ) }} /> diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts index f875873a0d..1496d769fe 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts @@ -39,6 +39,13 @@ const SharedBookmarksModel = types.model('SharedBookmarksModel', { sharedBookmarks: types.maybe(types.array(LabeledRegionModel)), }) +export interface IExtendedLGV extends LinearGenomeViewModel { + showBookmarkHighlights: boolean + showBookmarkLabels: boolean + toggleShowBookmarkHighlights: (arg: boolean) => {} + toggleShowBookmarkLabels: (arg: boolean) => {} +} + export interface ILabeledRegionModel extends SnapshotIn { refName: string @@ -96,22 +103,22 @@ export default function f(_pluginManager: PluginManager) { this.bookmarkAssemblies.filter(a => assemblyManager.get(a)), ) }, - get highlightToggle() { + get areBookmarksHighlightedOnAllOpenViews() { const { views } = getSession(self) const lgvs = views.filter(v => Object.hasOwn(v, 'showBookmarkHighlights'), ) - const res = (lgvs as LinearGenomeViewModel[]).map( - v => v.showBookmarkHighlights, - ) + const res = (lgvs as IExtendedLGV[]).map(v => v.showBookmarkHighlights) + // if a Set(res) of the open lgv's is size 1, that means all the bookmarks have the same setting, + // &&'d with the first res will show the toggle as on (i.e. all views have highlights toggled ON) + // or off (i.e. all views have highlights toggled OFF, OR, even one view has highlights toggled OFF) return new Set(res).size === 1 && res[0] }, - get labelToggle() { + get areBookmarksHighlightLabelsOnAllOpenViews() { const { views } = getSession(self) const lgvs = views.filter(v => Object.hasOwn(v, 'showBookmarkLabels')) - const res = (lgvs as LinearGenomeViewModel[]).map( - v => v.showBookmarkLabels, - ) + const res = (lgvs as IExtendedLGV[]).map(v => v.showBookmarkLabels) + // same logic as areBookmarksHighlightedOnAllOpenViews return new Set(res).size === 1 && res[0] }, })) @@ -179,11 +186,9 @@ export default function f(_pluginManager: PluginManager) { bookmark.correspondingObj.setHighlight(color) }, updateBulkBookmarkHighlights(color: string) { - if (self.selectedBookmarks.length !== 0) { - self.selectedBookmarks.forEach(bookmark => - this.updateBookmarkHighlight(bookmark, color), - ) - } + self.selectedBookmarks.forEach(bookmark => + this.updateBookmarkHighlight(bookmark, color), + ) }, setSelectedBookmarks(bookmarks: IExtendedLabeledRegionModel[]) { self.selectedBookmarks = bookmarks @@ -193,20 +198,14 @@ export default function f(_pluginManager: PluginManager) { }, setHighlightToggle(toggle: boolean) { const { views } = getSession(self) - const lgvs = views.filter(v => - Object.hasOwn(v, 'showBookmarkHighlights'), - ) - - ;(lgvs as LinearGenomeViewModel[]).forEach(view => { - view.toggleShowBookmarkHighlights(toggle) + ;(views as IExtendedLGV[]).forEach(view => { + view.toggleShowBookmarkHighlights?.(toggle) }) }, setLabelToggle(toggle: boolean) { const { views } = getSession(self) - const lgvs = views.filter(v => Object.hasOwn(v, 'showBookmarkLabels')) - - ;(lgvs as LinearGenomeViewModel[]).forEach(view => { - view.toggleShowBookmarkLabels(toggle) + ;(views as IExtendedLGV[]).forEach(view => { + view.toggleShowBookmarkLabels?.(toggle) }) }, })) diff --git a/plugins/grid-bookmark/src/index.ts b/plugins/grid-bookmark/src/index.ts index 85a3a4bf07..075cf82399 100644 --- a/plugins/grid-bookmark/src/index.ts +++ b/plugins/grid-bookmark/src/index.ts @@ -34,7 +34,33 @@ export default class extends Plugin { const { stateModel } = pluggableElement as ViewType const lgv = stateModel as LinearGenomeViewStateModel const newStateModel = lgv + .props({ + /** + * #property + * show the bookmark highlights on this track + */ + showBookmarkHighlights: true, + /** + * #property + * show the bookmark labels on this track + */ + showBookmarkLabels: true, + }) .actions(self => ({ + /** + * #action + */ + toggleShowBookmarkHighlights(toggle?: boolean) { + self.showBookmarkHighlights = + toggle !== undefined ? toggle : !self.showBookmarkHighlights + }, + /** + * #action + */ + toggleShowBookmarkLabels(toggle?: boolean) { + self.showBookmarkLabels = + toggle !== undefined ? toggle : !self.showBookmarkLabels + }, activateBookmarkWidget() { const session = getSession(self) if (isSessionModelWithWidgets(session)) { diff --git a/plugins/linear-genome-view/src/LinearGenomeView/model.ts b/plugins/linear-genome-view/src/LinearGenomeView/model.ts index 1d16706bb5..dbe604528b 100644 --- a/plugins/linear-genome-view/src/LinearGenomeView/model.ts +++ b/plugins/linear-genome-view/src/LinearGenomeView/model.ts @@ -239,18 +239,6 @@ export function stateModelFactory(pluginManager: PluginManager) { * show the "gridlines" in the track area */ showGridlines: true, - - /** - * #property - * show the bookmark highlights on this track - */ - showBookmarkHighlights: true, - - /** - * #property - * show the bookmark labels on this track - */ - showBookmarkLabels: true, }), ) .volatile(() => ({ @@ -589,20 +577,6 @@ export function stateModelFactory(pluginManager: PluginManager) { toggleShowGridlines() { self.showGridlines = !self.showGridlines }, - /** - * #action - */ - toggleShowBookmarkHighlights(toggle?: boolean) { - self.showBookmarkHighlights = - toggle !== undefined ? toggle : !self.showBookmarkHighlights - }, - /** - * #action - */ - toggleShowBookmarkLabels(toggle?: boolean) { - self.showBookmarkLabels = - toggle !== undefined ? toggle : !self.showBookmarkLabels - }, /** * #action */ From c12b21152704526d18c2afe0301e05c2143df0f6 Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Wed, 22 Nov 2023 12:38:43 -0500 Subject: [PATCH 12/25] revert for lint --- .../components/Highlight/Highlight.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx index 7e8d9090ae..0d721d87e9 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx @@ -49,13 +49,13 @@ const Highlight = observer(function Highlight({ model }: { model: LGV }) { return null } - const [bookmarkWidget, _] = useState( - (session.widgets.get('GridBookmark') as GridBookmarkModel) ?? - (session.addWidget( - 'GridBookmarkWidget', - 'GridBookmark', - ) as GridBookmarkModel), - ) + let bookmarkWidget = session.widgets.get('GridBookmark') as GridBookmarkModel + if (!bookmarkWidget) { + bookmarkWidget = session.addWidget( + 'GridBookmarkWidget', + 'GridBookmark', + ) as GridBookmarkModel + } const { showBookmarkHighlights, showBookmarkLabels } = model const assemblyNames = new Set(session.assemblyNames) From 1abddc84bf5847b8683173d3dc31ab12d3dab06e Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Wed, 22 Nov 2023 12:45:01 -0500 Subject: [PATCH 13/25] lint --- .../src/GridBookmarkWidget/components/Highlight/Highlight.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx index 0d721d87e9..c4e3be42cc 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React from 'react' import { observer } from 'mobx-react' import { makeStyles } from 'tss-react/mui' import { From fdb751a7cf3cc2f5ce1074449c2c2e1213784761 Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Wed, 22 Nov 2023 14:45:10 -0500 Subject: [PATCH 14/25] type --- .../src/GridBookmarkWidget/components/Highlight/index.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/index.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/index.tsx index 42f096f774..c837c1859c 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/index.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/index.tsx @@ -1,18 +1,15 @@ import React from 'react' import PluginManager from '@jbrowse/core/PluginManager' -import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view' // locals import Highlight from './Highlight' +import { IExtendedLGV } from '../../model' export default function AddHighlightModelF(pluginManager: PluginManager) { pluginManager.addToExtensionPoint( 'LinearGenomeView-TracksContainerComponent', // @ts-expect-error - ( - rest: React.ReactNode[] = [], - { model }: { model: LinearGenomeViewModel }, - ) => { + (rest: React.ReactNode[] = [], { model }: { model: IExtendedLGV }) => { return [ ...rest, , From b8b9cc7f3b35a21811433742293aa338b3fd33de Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Wed, 22 Nov 2023 15:21:45 -0500 Subject: [PATCH 15/25] rev changes to index.ts --- packages/core/util/index.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/core/util/index.ts b/packages/core/util/index.ts index d33ec772e0..1160769db6 100644 --- a/packages/core/util/index.ts +++ b/packages/core/util/index.ts @@ -1264,11 +1264,9 @@ export function localStorageGetItem(item: string) { } export function localStorageSetItem(str: string, item: string) { - const returnVal = - typeof localStorage !== 'undefined' - ? localStorage.setItem(str, item) - : undefined - return returnVal + return typeof localStorage !== 'undefined' + ? localStorage.setItem(str, item) + : undefined } export function max(arr: number[], init = -Infinity) { From 16bbeec73577188cda1d707e67839b1ada09e6de Mon Sep 17 00:00:00 2001 From: Colin Date: Wed, 22 Nov 2023 19:36:05 -0500 Subject: [PATCH 16/25] Simplified behavior of the check for all views --- .../src/GridBookmarkWidget/model.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts index 1496d769fe..6dce01a20b 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts @@ -105,21 +105,15 @@ export default function f(_pluginManager: PluginManager) { }, get areBookmarksHighlightedOnAllOpenViews() { const { views } = getSession(self) - const lgvs = views.filter(v => - Object.hasOwn(v, 'showBookmarkHighlights'), + return views.every(v => + 'showBookmarkHighlights' in v ? v.showBookmarkHighlights : true, ) - const res = (lgvs as IExtendedLGV[]).map(v => v.showBookmarkHighlights) - // if a Set(res) of the open lgv's is size 1, that means all the bookmarks have the same setting, - // &&'d with the first res will show the toggle as on (i.e. all views have highlights toggled ON) - // or off (i.e. all views have highlights toggled OFF, OR, even one view has highlights toggled OFF) - return new Set(res).size === 1 && res[0] }, get areBookmarksHighlightLabelsOnAllOpenViews() { const { views } = getSession(self) - const lgvs = views.filter(v => Object.hasOwn(v, 'showBookmarkLabels')) - const res = (lgvs as IExtendedLGV[]).map(v => v.showBookmarkLabels) - // same logic as areBookmarksHighlightedOnAllOpenViews - return new Set(res).size === 1 && res[0] + return views.every(v => + 'showBookmarkLabels' in v ? v.showBookmarkLabels : true, + ) }, })) .views(self => ({ From 8bec53e4cb437cc958e21fa10d034b3c5da172cc Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Thu, 23 Nov 2023 10:37:35 -0500 Subject: [PATCH 17/25] possible side effect routine --- .../components/Highlight/Highlight.tsx | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx index c4e3be42cc..6272ac62c4 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx @@ -1,11 +1,7 @@ -import React from 'react' +import React, { useEffect, useRef } from 'react' import { observer } from 'mobx-react' import { makeStyles } from 'tss-react/mui' -import { - getSession, - isSessionModelWithWidgets, - notEmpty, -} from '@jbrowse/core/util' +import { SessionWithWidgets, getSession, notEmpty } from '@jbrowse/core/util' import { IExtendedLGV } from '../../model' // icons @@ -44,27 +40,30 @@ const intensify = (rgba: string, opacity: number) => { const Highlight = observer(function Highlight({ model }: { model: LGV }) { const { classes } = useStyles() - const session = getSession(model) - if (!isSessionModelWithWidgets(session)) { - return null - } - - let bookmarkWidget = session.widgets.get('GridBookmark') as GridBookmarkModel - if (!bookmarkWidget) { - bookmarkWidget = session.addWidget( - 'GridBookmarkWidget', - 'GridBookmark', - ) as GridBookmarkModel - } - + const session = getSession(model) as SessionWithWidgets const { showBookmarkHighlights, showBookmarkLabels } = model const assemblyNames = new Set(session.assemblyNames) - const { bookmarks } = bookmarkWidget + + const bookmarkWidget = session.widgets.get( + 'GridBookmark', + ) as GridBookmarkModel + + const bookmarks = useRef(bookmarkWidget?.bookmarks ?? []) + + useEffect(() => { + if (!bookmarkWidget) { + const newBookmarkWidget = session.addWidget( + 'GridBookmarkWidget', + 'GridBookmark', + ) as GridBookmarkModel + bookmarks.current = newBookmarkWidget.bookmarks + } + }, [session, bookmarkWidget]) return ( <> - {showBookmarkHighlights - ? bookmarks + {showBookmarkHighlights && bookmarks.current + ? bookmarks.current .filter(value => assemblyNames.has(value.assemblyName)) .map(r => { const s = model.bpToPx({ From 085f2df30e86c9344ac0ea6f37c58d9b63b00c7a Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Fri, 24 Nov 2023 15:26:34 -0500 Subject: [PATCH 18/25] improving clear all and minor text edit --- .../components/HighlightBookmarksDialog.tsx | 2 +- plugins/grid-bookmark/src/GridBookmarkWidget/model.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx index c15bf50ec2..3b41fd26d9 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx @@ -29,7 +29,7 @@ const HighlightBookmarksDialog = observer(function ({ ) return ( - + Bulk highlight selector diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts index 6dce01a20b..48fe5b6526 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/model.ts @@ -205,11 +205,11 @@ export default function f(_pluginManager: PluginManager) { })) .actions(self => ({ clearAllBookmarks() { - for (const bookmark of self.bookmarks) { - if (self.validAssemblies.has(bookmark.assemblyName)) { - self.bookmarks.remove(bookmark) - } - } + self.setBookmarkedRegions( + self.bookmarks.filter( + bookmark => !self.validAssemblies.has(bookmark.assemblyName), + ) as IMSTArray, + ) }, clearSelectedBookmarks() { for (const bookmark of self.selectedBookmarks) { From b261a6759626f13581711c0d7223077af0c21c5d Mon Sep 17 00:00:00 2001 From: Caroline Bridge Date: Mon, 27 Nov 2023 10:23:46 -0500 Subject: [PATCH 19/25] lint and use colord --- .../components/BookmarkGrid.tsx | 2 +- .../components/Highlight/Highlight.tsx | 24 +++++++------------ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx index 216f1e366e..0596643927 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx @@ -132,7 +132,7 @@ const BookmarkGrid = observer(function ({ { - return model.updateBookmarkHighlight(row, event) + model.updateBookmarkHighlight(row, event) }} /> ), diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx index 6272ac62c4..55bc0d36ce 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/Highlight/Highlight.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react' import { observer } from 'mobx-react' import { makeStyles } from 'tss-react/mui' import { SessionWithWidgets, getSession, notEmpty } from '@jbrowse/core/util' -import { IExtendedLGV } from '../../model' +import { colord } from '@jbrowse/core/util/colord' // icons import BookmarkIcon from '@mui/icons-material/Bookmark' @@ -10,6 +10,7 @@ import BookmarkIcon from '@mui/icons-material/Bookmark' // locals import { Tooltip } from '@mui/material' import { GridBookmarkModel } from '../../model' +import { IExtendedLGV } from '../../model' type LGV = IExtendedLGV @@ -24,19 +25,6 @@ const useStyles = makeStyles()({ }, }) -const intensify = (rgba: string, opacity: number) => { - if (rgba) { - const values = rgba.replaceAll(/[^\d,.]/g, '').split(',') - const originalOpacity = values.pop() - // originalOpacity !== '0' assumes if the user has set the opacity of this highlight to 0, they probably don't want to see the label either - const n = `rgba(${values.join(', ')}, ${ - originalOpacity !== '0' ? opacity : 0 - })` - return n - } - return `rgba(0,0,0,0)` -} - const Highlight = observer(function Highlight({ model }: { model: LGV }) { const { classes } = useStyles() @@ -94,7 +82,13 @@ const Highlight = observer(function Highlight({ model }: { model: LGV }) { ) : null} From 219b45ac69de798690138dcdad61ce136ae1f965 Mon Sep 17 00:00:00 2001 From: Colin Date: Wed, 13 Dec 2023 08:51:51 -0500 Subject: [PATCH 20/25] Use hamburger menu for bookmark options --- .../components/BookmarkGrid.tsx | 188 +++++++++--------- .../components/DeleteBookmarks.tsx | 31 --- .../components/ExportBookmarks.tsx | 36 ---- .../components/GridBookmarkWidget.tsx | 111 +++++++++-- .../components/HighlightBookmarks.tsx | 36 ---- .../components/ImportBookmarks.tsx | 34 ---- .../components/ShareBookmarks.tsx | 27 --- .../{ => dialogs}/DeleteBookmarksDialog.tsx | 2 +- .../{ => dialogs}/EditBookmarkLabelDialog.tsx | 3 +- .../EditHighlightColorDialog.tsx} | 37 +--- .../{ => dialogs}/ExportBookmarksDialog.tsx | 4 +- .../dialogs/HighlightSettingsDialog.tsx | 61 ++++++ .../{ => dialogs}/ImportBookmarksDialog.tsx | 6 +- .../{ => dialogs}/ShareBookmarksDialog.tsx | 4 +- 14 files changed, 257 insertions(+), 323 deletions(-) delete mode 100644 plugins/grid-bookmark/src/GridBookmarkWidget/components/DeleteBookmarks.tsx delete mode 100644 plugins/grid-bookmark/src/GridBookmarkWidget/components/ExportBookmarks.tsx delete mode 100644 plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarks.tsx delete mode 100644 plugins/grid-bookmark/src/GridBookmarkWidget/components/ImportBookmarks.tsx delete mode 100644 plugins/grid-bookmark/src/GridBookmarkWidget/components/ShareBookmarks.tsx rename plugins/grid-bookmark/src/GridBookmarkWidget/components/{ => dialogs}/DeleteBookmarksDialog.tsx (96%) rename plugins/grid-bookmark/src/GridBookmarkWidget/components/{ => dialogs}/EditBookmarkLabelDialog.tsx (99%) rename plugins/grid-bookmark/src/GridBookmarkWidget/components/{HighlightBookmarksDialog.tsx => dialogs/EditHighlightColorDialog.tsx} (57%) rename plugins/grid-bookmark/src/GridBookmarkWidget/components/{ => dialogs}/ExportBookmarksDialog.tsx (95%) create mode 100644 plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/HighlightSettingsDialog.tsx rename plugins/grid-bookmark/src/GridBookmarkWidget/components/{ => dialogs}/ImportBookmarksDialog.tsx (97%) rename plugins/grid-bookmark/src/GridBookmarkWidget/components/{ => dialogs}/ShareBookmarksDialog.tsx (97%) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx index 0596643927..e87b9fbd12 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/BookmarkGrid.tsx @@ -1,4 +1,4 @@ -import React, { Suspense, lazy, useState } from 'react' +import React, { lazy, useState } from 'react' import { observer } from 'mobx-react' import { Link } from '@mui/material' import { makeStyles } from 'tss-react/mui' @@ -11,14 +11,15 @@ import { } from '@jbrowse/core/util' import { useResizeBar } from '@jbrowse/core/ui/useResizeBar' import ResizeBar from '@jbrowse/core/ui/ResizeBar' - -const ColorPicker = lazy(() => import('@jbrowse/core/ui/ColorPicker')) +import ColorPicker from '@jbrowse/core/ui/ColorPicker' // locals import { navToBookmark } from '../utils' -import { GridBookmarkModel, IExtendedLabeledRegionModel } from '../model' +import { GridBookmarkModel } from '../model' -const EditBookmarkLabelDialog = lazy(() => import('./EditBookmarkLabelDialog')) +const EditBookmarkLabelDialog = lazy( + () => import('./dialogs/EditBookmarkLabelDialog'), +) const useStyles = makeStyles()(() => ({ link: { @@ -37,7 +38,6 @@ const BookmarkGrid = observer(function ({ model: GridBookmarkModel }) { const { classes, cx } = useStyles() - const [dialogRow, setDialogRow] = useState() const { ref, scrollLeft } = useResizeBar() const { bookmarks, @@ -62,7 +62,7 @@ const BookmarkGrid = observer(function ({ }) const [widths, setWidths] = useState([ - 40, + 50, Math.max( measureText('Bookmark link', 12) + 30, measureGridWidth(rows.map(row => row.locString)), @@ -79,96 +79,90 @@ const BookmarkGrid = observer(function ({ ]) return ( - <> -
- - ( - { - event.preventDefault() - const { views } = session - await navToBookmark(value, row.assemblyName, views, model) - }} - > - {value} - - ), - }, - { - field: 'label', - headerName: 'Label', - width: widths[2], - editable: true, - }, - { - field: 'assemblyName', - headerName: 'Assembly', - width: widths[3], - }, - { - field: 'highlight', - headerName: 'Highlight', - width: widths[4], - renderCell: ({ value, row }) => ( - { - model.updateBookmarkHighlight(row, event) - }} - /> - ), - }, - ]} - onCellDoubleClick={({ row }) => setDialogRow(row)} - processRowUpdate={row => { - const target = rows[row.id] - model.updateBookmarkLabel(target, row.label) - return row - }} - onProcessRowUpdateError={e => session.notify(e.message, 'error')} - checkboxSelection - onRowSelectionModelChange={newRowSelectionModel => { - if (bookmarksWithValidAssemblies.length > 0) { - model.setSelectedBookmarks( - newRowSelectionModel.map(value => ({ - ...rows[value as number], - })), - ) - } - }} - rowSelectionModel={selectedBookmarks.map(r => r.id)} - disableRowSelectionOnClick - /> -
- {dialogRow ? ( - }> - setDialogRow(undefined)} - model={model} - dialogRow={dialogRow} - /> - - ) : null} - +
+ + ( + { + event.preventDefault() + const { views } = session + await navToBookmark(value, row.assemblyName, views, model) + }} + > + {value} + + ), + }, + { + field: 'label', + headerName: 'Label', + width: widths[2], + editable: true, + }, + { + field: 'assemblyName', + headerName: 'Assembly', + width: widths[3], + }, + { + field: 'highlight', + headerName: 'Highlight', + width: widths[4], + renderCell: ({ value, row }) => ( + { + model.updateBookmarkHighlight(row, event) + }} + /> + ), + }, + ]} + onCellDoubleClick={({ row }) => { + getSession(model).queueDialog(onClose => [ + EditBookmarkLabelDialog, + { onClose, model, dialogRow: row }, + ]) + }} + processRowUpdate={row => { + const target = rows[row.id] + model.updateBookmarkLabel(target, row.label) + return row + }} + onProcessRowUpdateError={e => session.notify(e.message, 'error')} + checkboxSelection + onRowSelectionModelChange={newRowSelectionModel => { + if (bookmarksWithValidAssemblies.length > 0) { + model.setSelectedBookmarks( + newRowSelectionModel.map(value => ({ + ...rows[value as number], + })), + ) + } + }} + rowSelectionModel={selectedBookmarks.map(r => r.id)} + disableRowSelectionOnClick + /> +
) }) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/DeleteBookmarks.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/DeleteBookmarks.tsx deleted file mode 100644 index 38a6850789..0000000000 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/DeleteBookmarks.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React, { Suspense, lazy, useState } from 'react' -import { Button } from '@mui/material' - -// icons -import DeleteIcon from '@mui/icons-material/Delete' - -// locals -import { GridBookmarkModel } from '../model' -const DeleteBookmarksDialog = lazy(() => import('./DeleteBookmarksDialog')) - -function DeleteBookmarks({ model }: { model: GridBookmarkModel }) { - const [open, setOpen] = useState(false) - return ( - <> - - {open ? ( - }> - setOpen(false)} /> - - ) : null} - - ) -} - -export default DeleteBookmarks diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/ExportBookmarks.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/ExportBookmarks.tsx deleted file mode 100644 index 2d960aa68a..0000000000 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/ExportBookmarks.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, { Suspense, lazy, useState } from 'react' -import { observer } from 'mobx-react' - -import { Button } from '@mui/material' -import GetAppIcon from '@mui/icons-material/GetApp' - -// locals -import { GridBookmarkModel } from '../model' - -const ExportBookmarksDialog = lazy(() => import('./ExportBookmarksDialog')) - -const ExportBookmarks = observer(function ExportBookmarks({ - model, -}: { - model: GridBookmarkModel -}) { - const [open, setOpen] = useState(false) - return ( - <> - - {open ? ( - }> - setOpen(false)} model={model} /> - - ) : null} - - ) -}) - -export default ExportBookmarks diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx index d1360fe7d0..4cbc7f330e 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx @@ -1,22 +1,43 @@ -import React from 'react' +import React, { lazy } from 'react' import { observer } from 'mobx-react' -import { Alert } from '@mui/material' import { makeStyles } from 'tss-react/mui' +import { getSession } from '@jbrowse/core/util' +import { Alert } from '@mui/material' +import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton' // locals import BookmarkGrid from './BookmarkGrid' -import DeleteBookmarks from './DeleteBookmarks' -import ExportBookmarks from './ExportBookmarks' -import HighlightBookmarks from './HighlightBookmarks' -import ImportBookmarks from './ImportBookmarks' import AssemblySelector from './AssemblySelector' -import ShareBookmarks from './ShareBookmarks' - import { GridBookmarkModel } from '../model' +// icons +import Menu from '@mui/icons-material/Menu' +import GetApp from '@mui/icons-material/GetApp' +import Publish from '@mui/icons-material/Publish' +import Settings from '@mui/icons-material/Settings' +import Palette from '@mui/icons-material/Palette' +import Share from '@mui/icons-material/Share' + +// lazies +const ExportBookmarksDialog = lazy( + () => import('./dialogs/ExportBookmarksDialog'), +) +const ImportBookmarksDialog = lazy( + () => import('./dialogs/ImportBookmarksDialog'), +) +const ShareBookmarksDialog = lazy( + () => import('./dialogs/ShareBookmarksDialog'), +) +const HighlightSettingsDialog = lazy( + () => import('./dialogs/HighlightSettingsDialog'), +) +const EditHighlightColorDialog = lazy( + () => import('./dialogs/EditHighlightColorDialog'), +) + const useStyles = makeStyles()({ - card: { - marginTop: 5, + flex: { + display: 'flex', }, }) @@ -32,20 +53,72 @@ const GridBookmarkWidget = observer(function GridBookmarkWidget({ } return ( -
-
- - - - - -
+
Click and type within the label field to annotate your bookmark. Double click the label field to do so within a dialog. - +
+ { + getSession(model).queueDialog(onClose => [ + ExportBookmarksDialog, + { onClose, model }, + ]) + }, + }, + { + label: 'Import', + icon: Publish, + onClick: () => { + getSession(model).queueDialog(onClose => [ + ImportBookmarksDialog, + { model, onClose }, + ]) + }, + }, + { + label: 'Share', + icon: Share, + onClick: () => { + getSession(model).queueDialog(onClose => [ + ShareBookmarksDialog, + { model, onClose }, + ]) + }, + }, + { + label: 'Edit colors', + icon: Palette, + onClick: () => { + getSession(model).queueDialog(onClose => [ + EditHighlightColorDialog, + { model, onClose }, + ]) + }, + }, + { + label: 'Settings', + icon: Settings, + onClick: () => { + getSession(model).queueDialog(onClose => [ + HighlightSettingsDialog, + { model, onClose }, + ]) + }, + }, + ]} + > + + + + +
) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarks.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarks.tsx deleted file mode 100644 index a31bf5e611..0000000000 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarks.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, { Suspense, lazy, useState } from 'react' -import { Button } from '@mui/material' - -// icons -import EditIcon from '@mui/icons-material/Edit' - -// locals -import { GridBookmarkModel } from '../model' -const HighlightBookmarksDialog = lazy( - () => import('./HighlightBookmarksDialog'), -) - -function HighlightBookmarks({ model }: { model: GridBookmarkModel }) { - const [open, setOpen] = useState(false) - return ( - <> - - {open ? ( - }> - setOpen(false)} - /> - - ) : null} - - ) -} - -export default HighlightBookmarks diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/ImportBookmarks.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/ImportBookmarks.tsx deleted file mode 100644 index 85e2b4d303..0000000000 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/ImportBookmarks.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useState, Suspense, lazy } from 'react' -import { observer } from 'mobx-react' -import { Button } from '@mui/material' - -// icons -import ImportIcon from '@mui/icons-material/Publish' - -// locals -import { GridBookmarkModel } from '../model' - -const ImportBookmarksDialog = lazy(() => import('./ImportBookmarksDialog')) - -const ImportBookmarks = observer(function ({ - model, -}: { - model: GridBookmarkModel -}) { - const [open, setOpen] = useState(false) - - return ( - <> - - {open ? ( - }> - setOpen(false)} model={model} /> - - ) : null} - - ) -}) - -export default ImportBookmarks diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/ShareBookmarks.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/ShareBookmarks.tsx deleted file mode 100644 index 6695e8ba1a..0000000000 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/ShareBookmarks.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React, { Suspense, lazy, useState } from 'react' - -import { Button } from '@mui/material' - -import { ContentCopy as ContentCopyIcon } from '@jbrowse/core/ui/Icons' - -// locals -import { GridBookmarkModel } from '../model' -const ShareBookmarksDialog = lazy(() => import('./ShareBookmarksDialog')) - -function ShareBookmarks({ model }: { model: GridBookmarkModel }) { - const [open, setOpen] = useState(false) - return ( - <> - - {open ? ( - }> - setOpen(false)} model={model} /> - - ) : null} - - ) -} - -export default ShareBookmarks diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/DeleteBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/DeleteBookmarksDialog.tsx similarity index 96% rename from plugins/grid-bookmark/src/GridBookmarkWidget/components/DeleteBookmarksDialog.tsx rename to plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/DeleteBookmarksDialog.tsx index 11904cf49b..cb412f80b9 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/DeleteBookmarksDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/DeleteBookmarksDialog.tsx @@ -4,7 +4,7 @@ import { Button, DialogContent, DialogActions, Alert } from '@mui/material' import { Dialog } from '@jbrowse/core/ui' // locals -import { GridBookmarkModel } from '../model' +import { GridBookmarkModel } from '../../model' const DeleteBookmarksDialog = observer(function ({ onClose, diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarkLabelDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/EditBookmarkLabelDialog.tsx similarity index 99% rename from plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarkLabelDialog.tsx rename to plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/EditBookmarkLabelDialog.tsx index af0a4ff8de..ddf9580270 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/EditBookmarkLabelDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/EditBookmarkLabelDialog.tsx @@ -7,10 +7,11 @@ import { TextField, } from '@mui/material' import { Dialog } from '@jbrowse/core/ui' -import { GridBookmarkModel, IExtendedLabeledRegionModel } from '../model' import { observer } from 'mobx-react' import { assembleLocString } from '@jbrowse/core/util' +import { GridBookmarkModel, IExtendedLabeledRegionModel } from '../../model' + const EditBookmarkLabelDialog = observer(function ({ model, onClose, diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/EditHighlightColorDialog.tsx similarity index 57% rename from plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx rename to plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/EditHighlightColorDialog.tsx index 3b41fd26d9..03ab02b071 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/HighlightBookmarksDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/EditHighlightColorDialog.tsx @@ -5,17 +5,15 @@ import { DialogContent, DialogActions, Alert, - Stack, Typography, - Switch, } from '@mui/material' import { Dialog } from '@jbrowse/core/ui' import { ColorPicker } from '@jbrowse/core/ui/ColorPicker' // locals -import { GridBookmarkModel } from '../model' +import { GridBookmarkModel } from '../../model' -const HighlightBookmarksDialog = observer(function ({ +const EditHighlightColorDialog = observer(function ({ onClose, model, }: { @@ -51,35 +49,6 @@ const HighlightBookmarksDialog = observer(function ({ }} /> ) : null} - Highlight toggles - - { - model.setHighlightToggle( - !model.areBookmarksHighlightedOnAllOpenViews, - ) - }} - /> - - Toggle bookmark highlights on all open views - - - - { - model.setLabelToggle( - !model.areBookmarksHighlightLabelsOnAllOpenViews, - ) - }} - /> - - Toggle 'bookmark' icon on LGV tracks - - + +
+ ) +}) + +export default HighlightSettingsDialog diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/ImportBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/ImportBookmarksDialog.tsx similarity index 97% rename from plugins/grid-bookmark/src/GridBookmarkWidget/components/ImportBookmarksDialog.tsx rename to plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/ImportBookmarksDialog.tsx index 1e4f1c4a33..4e291e3cb1 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/ImportBookmarksDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/ImportBookmarksDialog.tsx @@ -25,9 +25,9 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore' import HelpIcon from '@mui/icons-material/Help' // locals -import { GridBookmarkModel } from '../model' -import { fromUrlSafeB64 } from '../utils' -import { readSessionFromDynamo } from '../sessionSharing' +import { GridBookmarkModel } from '../../model' +import { fromUrlSafeB64 } from '../../utils' +import { readSessionFromDynamo } from '../../sessionSharing' const useStyles = makeStyles()(theme => ({ flexItem: { diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/ShareBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/ShareBookmarksDialog.tsx similarity index 97% rename from plugins/grid-bookmark/src/GridBookmarkWidget/components/ShareBookmarksDialog.tsx rename to plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/ShareBookmarksDialog.tsx index 1689e63380..e25ac63bea 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/ShareBookmarksDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/ShareBookmarksDialog.tsx @@ -19,8 +19,8 @@ import { Dialog, ErrorMessage } from '@jbrowse/core/ui' import { ContentCopy as ContentCopyIcon } from '@jbrowse/core/ui/Icons' // locals -import { shareSessionToDynamo } from '../sessionSharing' -import { GridBookmarkModel } from '../model' +import { shareSessionToDynamo } from '../../sessionSharing' +import { GridBookmarkModel } from '../../model' const useStyles = makeStyles()(() => ({ flexItem: { From bf5903750ca74b9e70bf1d714ccc0e04ac68e461 Mon Sep 17 00:00:00 2001 From: Colin Date: Wed, 13 Dec 2023 10:48:06 -0500 Subject: [PATCH 21/25] Attempt test fixes --- .../components/GridBookmarkWidget.test.tsx | 379 +++++++++--------- .../components/GridBookmarkWidget.tsx | 4 +- .../dialogs/ExportBookmarksDialog.tsx | 20 +- 3 files changed, 205 insertions(+), 198 deletions(-) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.test.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.test.tsx index e2146e1886..e642fa8a28 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.test.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.test.tsx @@ -1,6 +1,6 @@ import React from 'react' import { saveAs } from 'file-saver' -import { render, cleanup, fireEvent, within } from '@testing-library/react' +import { render, fireEvent, within } from '@testing-library/react' import '@testing-library/jest-dom' import { createTestSession } from '@jbrowse/web/src/rootModel' @@ -16,218 +16,219 @@ jest.mock('file-saver', () => { } }) -describe('', () => { - beforeEach(() => { - // @ts-expect-error - saveAs.mockReset() - localStorage.clear() - jest.clearAllMocks() - }) +const opts = [{}, { timeout: 10000 }] as const - afterEach(cleanup) +beforeEach(() => { + // @ts-expect-error + saveAs.mockReset() + localStorage.clear() + jest.clearAllMocks() +}) - it('renders empty with no bookmarks', async () => { - const session = createTestSession() - const model = session.addWidget( - 'GridBookmarkWidget', - 'gridBookmarkWidget', - ) as GridBookmarkModel +test('renders empty with no bookmarks', async () => { + const session = createTestSession() + const model = session.addWidget( + 'GridBookmarkWidget', + 'gridBookmarkWidget', + ) as GridBookmarkModel - const { findByText } = render() + const { findByText } = render() - expect(await findByText('No rows')).toBeTruthy() - }) + expect(await findByText('No rows')).toBeTruthy() +}) - it('renders bookmarks correctly', async () => { - const session = createTestSession() - session.addAssemblyConf({ - name: 'volMyt1', - sequence: { - trackId: 'ref0', - type: 'ReferenceSequenceTrack', - adapter: { - type: 'FromConfigSequenceAdapter', - features: [ - { - refName: 'ctgA', - uniqueId: 'firstId', - start: 0, - end: 10, - seq: 'cattgttgcg', - }, - ], - }, +test('renders bookmarks correctly', async () => { + const session = createTestSession() + session.addAssemblyConf({ + name: 'volMyt1', + sequence: { + trackId: 'ref0', + type: 'ReferenceSequenceTrack', + adapter: { + type: 'FromConfigSequenceAdapter', + features: [ + { + refName: 'ctgA', + uniqueId: 'firstId', + start: 0, + end: 10, + seq: 'cattgttgcg', + }, + ], }, - }) + }, + }) - const model = session.addWidget( - 'GridBookmarkWidget', - 'gridBookmarkWidget', - ) as GridBookmarkModel + const model = session.addWidget( + 'GridBookmarkWidget', + 'gridBookmarkWidget', + ) as GridBookmarkModel - const bookmark = { - refName: 'ctgA', - start: 0, - end: 8, - assemblyName: 'volMyt1', - } + const bookmark = { + refName: 'ctgA', + start: 0, + end: 8, + assemblyName: 'volMyt1', + } - model.addBookmark(bookmark) + model.addBookmark(bookmark) - const { findByText } = render() + const { findByText } = render() - expect(await findByText('ctgA:1..8')).toBeTruthy() - }) + expect(await findByText('ctgA:1..8')).toBeTruthy() +}) - // manually works but not working in testing for some reason, might have to do with the use of localstorage - xit('deletes selected bookmarks correctly', async () => { - const session = createTestSession() - session.addAssemblyConf({ - name: 'volMyt1', - sequence: { - trackId: 'ref0', - type: 'ReferenceSequenceTrack', - adapter: { - type: 'FromConfigSequenceAdapter', - features: [ - { - refName: 'ctgA', - uniqueId: 'firstId', - start: 0, - end: 10, - seq: 'cattgttgcg', - }, - ], - }, +// manually works but not working in testing for some reason, might have to do +// with the use of localstorage +xtest('deletes selected bookmarks correctly', async () => { + const session = createTestSession() + session.addAssemblyConf({ + name: 'volMyt1', + sequence: { + trackId: 'ref0', + type: 'ReferenceSequenceTrack', + adapter: { + type: 'FromConfigSequenceAdapter', + features: [ + { + refName: 'ctgA', + uniqueId: 'firstId', + start: 0, + end: 10, + seq: 'cattgttgcg', + }, + ], }, - }) - - const model = session.addWidget( - 'GridBookmarkWidget', - 'gridBookmarkWidget', - ) as GridBookmarkModel - - const bookmark = { - refName: 'ctgA', - start: 0, - end: 8, - assemblyName: 'volMyt1', - } - - model.addBookmark(bookmark) - - const { findByText, findAllByRole } = render( - , - ) - - fireEvent.click((await findAllByRole('checkbox'))[1]) - fireEvent.click(await findByText('Delete')) - fireEvent.click(await findByText('Confirm')) - expect(await findByText('No rows')).toBeTruthy() + }, }) - it('downloads a BED file correctly', async () => { - const session = createTestSession() - session.addAssemblyConf({ - name: 'volMyt1', - sequence: { - trackId: 'ref0', - type: 'ReferenceSequenceTrack', - adapter: { - type: 'FromConfigSequenceAdapter', - features: [ - { - refName: 'ctgA', - uniqueId: 'firstId', - start: 0, - end: 10, - seq: 'cattgttgcg', - }, - ], - }, - }, - }) + const model = session.addWidget( + 'GridBookmarkWidget', + 'gridBookmarkWidget', + ) as GridBookmarkModel - const model = session.addWidget( - 'GridBookmarkWidget', - 'gridBookmarkWidget', - ) as GridBookmarkModel + const bookmark = { + refName: 'ctgA', + start: 0, + end: 8, + assemblyName: 'volMyt1', + } - const bookmark = { - refName: 'ctgA', - start: 0, - end: 8, - assemblyName: 'volMyt1', - } + model.addBookmark(bookmark) - model.addBookmark(bookmark) + const { findByText, findAllByRole } = render( + , + ) - const { findByText, findByTestId, findAllByRole } = render( - , - ) + fireEvent.click((await findAllByRole('checkbox'))[1]) + fireEvent.click(await findByText('Delete')) + fireEvent.click(await findByText('Confirm')) + expect(await findByText('No rows')).toBeTruthy() +}) - fireEvent.click((await findAllByRole('checkbox'))[1]) - fireEvent.click(await findByText('Export')) - fireEvent.click(await findByTestId('dialogDownload')) +test('downloads a BED file correctly', async () => { + const session = createTestSession() + session.addAssemblyConf({ + name: 'volMyt1', + sequence: { + trackId: 'ref0', + type: 'ReferenceSequenceTrack', + adapter: { + type: 'FromConfigSequenceAdapter', + features: [ + { + refName: 'ctgA', + uniqueId: 'firstId', + start: 0, + end: 10, + seq: 'cattgttgcg', + }, + ], + }, + }, + }) - const blob = new Blob([''], { - type: 'text/x-bed;charset=utf-8', - }) + const model = session.addWidget( + 'GridBookmarkWidget', + 'gridBookmarkWidget', + ) as GridBookmarkModel - expect(saveAs).toHaveBeenCalledWith(blob, 'jbrowse_bookmarks_volMyt1.bed') + const bookmark = { + refName: 'ctgA', + start: 0, + end: 8, + assemblyName: 'volMyt1', + } + + model.addBookmark(bookmark) + + const { findByText, findByTestId, findAllByRole } = render( + , + ) + + fireEvent.click((await findAllByRole('checkbox'))[1]) + fireEvent.click(await findByTestId('grid_bookmark_menu', ...opts)) + fireEvent.click(await findByText('Export bookmarks', ...opts)) + fireEvent.click(await findByText(/Download/, ...opts)) + + const blob = new Blob([''], { + type: 'text/x-bed;charset=utf-8', }) - it('downloads a TSV file correctly', async () => { - const session = createTestSession() - session.addAssemblyConf({ - name: 'volMyt1', - sequence: { - trackId: 'ref0', - type: 'ReferenceSequenceTrack', - adapter: { - type: 'FromConfigSequenceAdapter', - features: [ - { - refName: 'ctgA', - uniqueId: 'firstId', - start: 0, - end: 10, - seq: 'cattgttgcg', - }, - ], - }, + expect(saveAs).toHaveBeenCalledWith(blob, 'jbrowse_bookmarks_volMyt1.bed') +}, 20000) + +test('downloads a TSV file correctly', async () => { + const session = createTestSession() + session.addAssemblyConf({ + name: 'volMyt1', + sequence: { + trackId: 'ref0', + type: 'ReferenceSequenceTrack', + adapter: { + type: 'FromConfigSequenceAdapter', + features: [ + { + refName: 'ctgA', + uniqueId: 'firstId', + start: 0, + end: 10, + seq: 'cattgttgcg', + }, + ], }, - }) - - const model = session.addWidget( - 'GridBookmarkWidget', - 'gridBookmarkWidget', - ) as GridBookmarkModel - - const bookmark = { - refName: 'ctgA', - start: 0, - end: 8, - assemblyName: 'volMyt1', - } - - model.addBookmark(bookmark) - - const { findByText, findByTestId, getByRole, findAllByRole } = render( - , - ) - - fireEvent.click((await findAllByRole('checkbox'))[1]) - fireEvent.click(await findByText('Export')) - fireEvent.mouseDown(await findByText('BED')) - const listbox = within(getByRole('listbox')) - fireEvent.click(listbox.getByText('TSV')) - fireEvent.click(await findByTestId('dialogDownload')) - - const blob = new Blob([''], { - type: 'text/tab-separated-values;charset=utf-8', - }) - - expect(saveAs).toHaveBeenCalledWith(blob, 'jbrowse_bookmarks.tsv') + }, }) + + const model = session.addWidget( + 'GridBookmarkWidget', + 'gridBookmarkWidget', + ) as GridBookmarkModel + + const bookmark = { + refName: 'ctgA', + start: 0, + end: 8, + assemblyName: 'volMyt1', + } + + model.addBookmark(bookmark) + + const { findByText, findByTestId, getByRole, findAllByRole } = render( + , + ) + + fireEvent.click((await findAllByRole('checkbox'))[1]) + fireEvent.click(await findByTestId('grid_bookmark_menu')) + fireEvent.click(await findByText('Export bookmarks')) + fireEvent.mouseDown(await findByText('BED')) + const listbox = within(getByRole('listbox')) + fireEvent.click(listbox.getByText('TSV')) + fireEvent.click(await findByText(/Download/)) + + const blob = new Blob([''], { + type: 'text/tab-separated-values;charset=utf-8', + }) + + expect(saveAs).toHaveBeenCalledWith(blob, 'jbrowse_bookmarks.tsv') }) diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx index 4cbc7f330e..b41e03c990 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/GridBookmarkWidget.tsx @@ -61,11 +61,13 @@ const GridBookmarkWidget = observer(function GridBookmarkWidget({
{ + console.log('here2') getSession(model).queueDialog(onClose => [ ExportBookmarksDialog, { onClose, model }, diff --git a/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/ExportBookmarksDialog.tsx b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/ExportBookmarksDialog.tsx index a239a99022..5a4f4b33bf 100644 --- a/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/ExportBookmarksDialog.tsx +++ b/plugins/grid-bookmark/src/GridBookmarkWidget/components/dialogs/ExportBookmarksDialog.tsx @@ -10,19 +10,26 @@ import { Typography, Alert, } from '@mui/material' -import GetAppIcon from '@mui/icons-material/GetApp' import { makeStyles } from 'tss-react/mui' import { Dialog } from '@jbrowse/core/ui' +// Icons +import GetAppIcon from '@mui/icons-material/GetApp' + // locals import { GridBookmarkModel } from '../../model' import { downloadBookmarkFile } from '../../utils' -const useStyles = makeStyles()(() => ({ +const useStyles = makeStyles()({ flexItem: { margin: 5, }, -})) + container: { + display: 'flex', + flexFlow: 'column', + gap: '5px', + }, +}) const ExportBookmarksDialog = observer(function ExportBookmarksDialog({ model, @@ -35,11 +42,10 @@ const ExportBookmarksDialog = observer(function ExportBookmarksDialog({ const [fileType, setFileType] = useState('BED') const { selectedBookmarks } = model const exportAll = selectedBookmarks.length === 0 + console.log('t1') return ( - + {exportAll ? ( <> @@ -69,8 +75,6 @@ const ExportBookmarksDialog = observer(function ExportBookmarksDialog({