Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement grid bookmark widget #2080

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
92d82fb
Scaffold grid bookmark widget
elliothershberg Jun 15, 2021
0b7114c
Add bookmark widget to lgv hamburger menu
elliothershberg Jun 15, 2021
5ae5563
Set up styles
elliothershberg Jun 16, 2021
37eb8cb
Add placeholder table
elliothershberg Jun 16, 2021
689afff
Render locstring cell as link
elliothershberg Jun 17, 2021
362905b
Make bookmarks observable
elliothershberg Jun 17, 2021
6b3f306
Add optional key attribute to MST Region type
elliothershberg Jun 18, 2021
34de1aa
Pass selected region from rubberband to bookmark
elliothershberg Jun 18, 2021
fd30e17
Render rows of bookmarks from MST model
elliothershberg Jun 18, 2021
b1f2a3b
Update MST model, make method for add bookmarks
elliothershberg Jun 18, 2021
b19e90f
Convert MST array to JS before passing to datagrid
elliothershberg Jun 18, 2021
1c5ef99
Move bookmark array from widget to session model
elliothershberg Jun 18, 2021
6cf1daf
Use session method on rubberband
elliothershberg Jun 18, 2021
707d40f
Render rows from session in bookmark widget
elliothershberg Jun 18, 2021
ebd25ee
Refactor rubberband
elliothershberg Jun 18, 2021
f92b663
Merge branch 'main' into 157_grid_bookmark_widget
elliothershberg Jun 28, 2021
f51616a
Update snaps
elliothershberg Jun 28, 2021
dcd47b7
Remove key attribute from Region mst model
elliothershberg Jun 29, 2021
4d1c96c
Implement navigation from links
elliothershberg Jun 29, 2021
91d85f3
Avoid bookmarking same region twice
elliothershberg Jun 29, 2021
c213b8b
Update height and width of bookmark widget
elliothershberg Jun 29, 2021
761421c
Add column for removing bookmarks
elliothershberg Jun 29, 2021
433958e
Add icon to bookmark option for rubberband
elliothershberg Jun 29, 2021
4ab634b
Add menu item to bookmark current region in LGV
elliothershberg Jun 30, 2021
a91945f
Remove unused exports and theme arg
elliothershberg Jun 30, 2021
c631489
Add option to download BED or TSV of bookmarks
elliothershberg Jun 30, 2021
341471d
Create option to clear all bookmarks
elliothershberg Jul 1, 2021
41bd6a1
Remove unused import
elliothershberg Jul 1, 2021
de6195d
Disable DataGrid row selection on click
elliothershberg Jul 1, 2021
c0ad5b7
Add camera icon for svg export
elliothershberg Jul 1, 2021
5c859b0
Add component level tests for bookmark widget
elliothershberg Jul 2, 2021
8a8c543
Add integration tests for bookmark functionality
elliothershberg Jul 2, 2021
a5c8a44
Fix region selection in test
elliothershberg Jul 6, 2021
2603d43
Merge branch 'main' into 157_grid_bookmark_widget
elliothershberg Jul 7, 2021
c736f73
Fix CLI README formatting
elliothershberg Jul 8, 2021
0cbadd6
Make bookmark table more compact
elliothershberg Jul 9, 2021
c3e6574
Update render test to use snap
elliothershberg Jul 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useState } from 'react'
import { observer } from 'mobx-react'

import {
Button,
IconButton,
Dialog,
DialogTitle,
Typography,
makeStyles,
} from '@material-ui/core'
import ClearAllIcon from '@material-ui/icons/ClearAll'
import CloseIcon from '@material-ui/icons/Close'

import { getSession } from '@jbrowse/core/util'

import { GridBookmarkModel } from '../model'

const useStyles = makeStyles(() => ({
closeDialog: {
position: 'absolute',
right: 0,
top: 0,
},
dialogContainer: {
margin: 15,
},
clearButton: {
marginBottom: 5,
},
}))

function ClearBookmarks({ model }: { model: GridBookmarkModel }) {
const classes = useStyles()
const [dialogOpen, setDialogOpen] = useState(false)

// @ts-ignore
const { clearAllBookmarks } = getSession(model)

return (
<>
<Button
className={classes.clearButton}
startIcon={<ClearAllIcon />}
aria-label="clear bookmarks"
onClick={() => setDialogOpen(true)}
>
Clear bookmarks
</Button>
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)}>
<DialogTitle>
<IconButton
className={classes.closeDialog}
aria-label="close-dialog"
onClick={() => setDialogOpen(false)}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<div className={classes.dialogContainer}>
<>
<Typography>Clear all bookmarks?</Typography>
<br />
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Button
variant="contained"
color="primary"
onClick={() => {
clearAllBookmarks()
setDialogOpen(false)
}}
>
Confirm
</Button>
</div>
</>
</div>
</Dialog>
</>
)
}

export default observer(ClearBookmarks)
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, { useState } from 'react'
import { observer } from 'mobx-react'

import {
IconButton,
Button,
Dialog,
DialogTitle,
Typography,
makeStyles,
} from '@material-ui/core'
import DeleteIcon from '@material-ui/icons/Delete'
import CloseIcon from '@material-ui/icons/Close'

import { getSession } from '@jbrowse/core/util'

import { GridBookmarkModel } from '../model'

const useStyles = makeStyles(() => ({
closeDialog: {
position: 'absolute',
right: 0,
top: 0,
},
dialogContainer: {
margin: 15,
},
}))

function DeleteBookmark({
locString,
model,
}: {
locString: string
model: GridBookmarkModel
}) {
const classes = useStyles()
const [dialogOpen, setDialogOpen] = useState(false)

// @ts-ignore
const { removeBookmark } = getSession(model)

return (
<>
<IconButton
data-testid="deleteBookmark"
aria-label="delete"
onClick={() => setDialogOpen(true)}
>
<DeleteIcon />
</IconButton>
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)}>
<DialogTitle>
<IconButton
className={classes.closeDialog}
aria-label="close-dialog"
onClick={() => setDialogOpen(false)}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<div className={classes.dialogContainer}>
<>
<Typography>
Remove <code>{locString}</code>?
</Typography>
<br />
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Button
variant="contained"
color="primary"
onClick={() => {
removeBookmark(locString)
setDialogOpen(false)
}}
>
Confirm
</Button>
</div>
</>
</div>
</Dialog>
</>
)
}

export default observer(DeleteBookmark)
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React, { useState } from 'react'
import { observer } from 'mobx-react'
import { saveAs } from 'file-saver'

import {
IconButton,
Button,
Dialog,
DialogTitle,
Select,
MenuItem,
makeStyles,
} from '@material-ui/core'
import CloseIcon from '@material-ui/icons/Close'
import GetAppIcon from '@material-ui/icons/GetApp'

import { getSession } from '@jbrowse/core/util'
import { Region } from '@jbrowse/core/util/types'

import { GridBookmarkModel } from '../model'

function downloadBookmarkFile(bookmarkedRegions: Region[], fileFormat: string) {
const fileHeader =
fileFormat === 'TSV'
? 'chrom\tstart\tend\tassembly_name\tcoord_range\n'
: ''

const fileContents = bookmarkedRegions
.map(b => {
if (fileFormat === 'BED') {
return `${b.refName}\t${b.start}\t${b.end}\n`
} else {
const locString = `${b.refName}:${b.start}..${b.end}`
return `${b.refName}\t${b.start}\t${b.end}\t${b.assemblyName}\t${locString}\n`
}
})
.reduce((a, b) => a + b, fileHeader)

const blob = new Blob([fileContents || ''], {
type:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the code sensitive to these MIME types?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just thought there was something specialized where the mime types needed to be special. I guess it probably isn't that consequential, but is there a source for how these were chosen?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just ends up telling the file downloader what the file type is when it's downloaded, works for me locally at least on MacOS.

Types were both found from Wikipedia:

fileFormat === 'BED'
? 'text/x-bed;charset=utf-8'
: 'text/tab-separated-values;charset=utf-8',
})

saveAs(blob, `jbrowse_bookmarks.${fileFormat === 'BED' ? 'bed' : 'tsv'}`)
}

const useStyles = makeStyles(() => ({
closeDialog: {
position: 'absolute',
right: 0,
top: 0,
},
dialogContainer: {
margin: 15,
},
downloadButton: {
marginBottom: 5,
},
flexItem: {
margin: 5,
},
flexContainer: {
display: 'flex',
justifyContent: 'space-evenly',
width: 200,
},
}))

function DownloadBookmarks({ model }: { model: GridBookmarkModel }) {
const classes = useStyles()
const [dialogOpen, setDialogOpen] = useState(false)
const [fileType, setFileType] = useState('BED')

const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
setFileType(event.target.value as string)
}

// @ts-ignore
const { bookmarkedRegions } = getSession(model)

return (
<>
<Button
className={classes.downloadButton}
startIcon={<GetAppIcon />}
onClick={() => setDialogOpen(true)}
>
Download
</Button>
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)}>
<DialogTitle>
<IconButton
className={classes.closeDialog}
aria-label="close-dialog"
onClick={() => setDialogOpen(false)}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<div className={classes.dialogContainer}>
<>
<div className={classes.flexContainer}>
<Select
className={classes.flexItem}
data-testid="selectFileType"
value={fileType}
onChange={handleChange}
>
<MenuItem value="BED">BED</MenuItem>
<MenuItem value="TSV">TSV</MenuItem>
</Select>
<Button
className={classes.flexItem}
data-testid="dialogDownload"
variant="contained"
color="primary"
startIcon={<GetAppIcon />}
onClick={() => {
downloadBookmarkFile(bookmarkedRegions, fileType)
setDialogOpen(false)
}}
>
Download
</Button>
</div>
</>
</div>
</Dialog>
</>
)
}

export default observer(DownloadBookmarks)
Loading