@jbrowse/plugin-data-management 1.5.1 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PluginStoreWidget/components/CustomPluginForm.d.ts +1 -1
- package/dist/plugin-data-management.cjs.development.js +159 -54
- package/dist/plugin-data-management.cjs.development.js.map +1 -1
- package/dist/plugin-data-management.cjs.production.min.js +1 -1
- package/dist/plugin-data-management.cjs.production.min.js.map +1 -1
- package/dist/plugin-data-management.esm.js +160 -55
- package/dist/plugin-data-management.esm.js.map +1 -1
- package/package.json +3 -2
- package/src/HierarchicalTrackSelectorWidget/model.js +3 -2
- package/src/PluginStoreWidget/components/CustomPluginForm.tsx +151 -43
- package/src/PluginStoreWidget/components/InstalledPlugin.tsx +5 -1
- package/src/PluginStoreWidget/components/PluginStoreWidget.test.js +7 -6
- package/src/PluginStoreWidget/components/PluginStoreWidget.tsx +16 -7
- package/src/PluginStoreWidget/components/__snapshots__/PluginStoreWidget.test.js.snap +20 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-data-management",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "JBrowse 2 linear genome view",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@gmod/ucsc-hub": "^0.1.3",
|
|
39
39
|
"@material-ui/icons": "^4.9.1",
|
|
40
|
+
"clsx": "^1.1.0",
|
|
40
41
|
"pluralize": "^8.0.0",
|
|
41
42
|
"react-virtualized-auto-sizer": "^1.0.2",
|
|
42
43
|
"react-vtree": "^3.0.0-beta.1",
|
|
@@ -55,5 +56,5 @@
|
|
|
55
56
|
"publishConfig": {
|
|
56
57
|
"access": "public"
|
|
57
58
|
},
|
|
58
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "94fdfbc34787ab8f12a87e00038da74b247b42fa"
|
|
59
60
|
}
|
|
@@ -9,9 +9,10 @@ const hasAnyOverlap = (a1 = [], a2 = []) =>
|
|
|
9
9
|
function passesFilter(filter, config) {
|
|
10
10
|
const name = getTrackName(config)
|
|
11
11
|
const categories = readConfObject(config, 'category') || []
|
|
12
|
-
const
|
|
12
|
+
const filterLower = filter.toLowerCase()
|
|
13
13
|
return (
|
|
14
|
-
!!name.
|
|
14
|
+
!!name.toLowerCase().includes(filterLower) ||
|
|
15
|
+
categories.filter(cat => !!cat.toLowerCase().includes(filterLower)).length
|
|
15
16
|
)
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -1,36 +1,50 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
3
|
import { getRoot } from 'mobx-state-tree'
|
|
4
|
+
import clsx from 'clsx'
|
|
4
5
|
|
|
5
6
|
import {
|
|
6
7
|
Button,
|
|
8
|
+
Collapse,
|
|
7
9
|
Dialog,
|
|
8
10
|
DialogActions,
|
|
9
11
|
DialogTitle,
|
|
10
12
|
DialogContent,
|
|
13
|
+
DialogContentText,
|
|
11
14
|
TextField,
|
|
12
|
-
Typography,
|
|
13
15
|
makeStyles,
|
|
14
16
|
} from '@material-ui/core'
|
|
15
17
|
|
|
16
18
|
// icons
|
|
17
19
|
import IconButton from '@material-ui/core/IconButton'
|
|
18
20
|
import CloseIcon from '@material-ui/icons/Close'
|
|
21
|
+
import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
|
|
22
|
+
|
|
23
|
+
import { PluginDefinition } from '@jbrowse/core/PluginLoader'
|
|
19
24
|
|
|
20
25
|
// locals
|
|
21
26
|
import { PluginStoreModel } from '../model'
|
|
22
27
|
|
|
23
|
-
const useStyles = makeStyles(
|
|
24
|
-
|
|
28
|
+
const useStyles = makeStyles(theme => ({
|
|
29
|
+
closeButton: {
|
|
25
30
|
position: 'absolute',
|
|
26
|
-
right:
|
|
27
|
-
top:
|
|
31
|
+
right: theme.spacing(1),
|
|
32
|
+
top: theme.spacing(1),
|
|
28
33
|
},
|
|
29
|
-
|
|
30
|
-
margin: 15,
|
|
34
|
+
dialogContent: {
|
|
31
35
|
display: 'flex',
|
|
32
36
|
flexDirection: 'column',
|
|
33
37
|
},
|
|
38
|
+
expand: {
|
|
39
|
+
transform: 'rotate(0deg)',
|
|
40
|
+
marginLeft: 'auto',
|
|
41
|
+
transition: theme.transitions.create('transform', {
|
|
42
|
+
duration: theme.transitions.duration.shortest,
|
|
43
|
+
}),
|
|
44
|
+
},
|
|
45
|
+
expandOpen: {
|
|
46
|
+
transform: 'rotate(180deg)',
|
|
47
|
+
},
|
|
34
48
|
}))
|
|
35
49
|
|
|
36
50
|
function CustomPluginForm({
|
|
@@ -39,62 +53,156 @@ function CustomPluginForm({
|
|
|
39
53
|
model,
|
|
40
54
|
}: {
|
|
41
55
|
open: boolean
|
|
42
|
-
onClose
|
|
56
|
+
onClose(): void
|
|
43
57
|
model: PluginStoreModel
|
|
44
58
|
}) {
|
|
45
59
|
const classes = useStyles()
|
|
46
|
-
const [
|
|
47
|
-
const [
|
|
60
|
+
const [umdPluginName, setUMDPluginName] = useState('')
|
|
61
|
+
const [umdPluginUrl, setUMDPluginUrl] = useState('')
|
|
62
|
+
const [esmPluginUrl, setESMPluginUrl] = useState('')
|
|
63
|
+
const [cjsPluginUrl, setCJSPluginUrl] = useState('')
|
|
64
|
+
const [advancedOptionsOpen, setAdvancedOptionsOpen] = useState(false)
|
|
65
|
+
|
|
66
|
+
function handleChange(event: React.ChangeEvent<HTMLTextAreaElement>) {
|
|
67
|
+
const { name, value } = event.target
|
|
68
|
+
if (name === 'umdName') {
|
|
69
|
+
setUMDPluginName(value)
|
|
70
|
+
}
|
|
71
|
+
if (name === 'umdUrl') {
|
|
72
|
+
setUMDPluginUrl(value)
|
|
73
|
+
}
|
|
74
|
+
if (name === 'esmUrl') {
|
|
75
|
+
setESMPluginUrl(value)
|
|
76
|
+
}
|
|
77
|
+
if (name === 'cjsUrl') {
|
|
78
|
+
setCJSPluginUrl(value)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function handleOpenAdvancedOptions() {
|
|
83
|
+
setAdvancedOptionsOpen(!advancedOptionsOpen)
|
|
84
|
+
}
|
|
85
|
+
|
|
48
86
|
const rootModel = getRoot(model)
|
|
49
87
|
const { jbrowse } = rootModel
|
|
50
88
|
|
|
89
|
+
const ready = Boolean(
|
|
90
|
+
(umdPluginName && umdPluginUrl) || esmPluginUrl || cjsPluginUrl,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
function handleSubmit() {
|
|
94
|
+
if (!ready) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
const pluginDefinition: PluginDefinition = {}
|
|
98
|
+
if (umdPluginName && umdPluginUrl) {
|
|
99
|
+
pluginDefinition.name = umdPluginName
|
|
100
|
+
pluginDefinition.umdUrl = umdPluginUrl
|
|
101
|
+
}
|
|
102
|
+
if (esmPluginUrl) {
|
|
103
|
+
pluginDefinition.esmUrl = esmPluginUrl
|
|
104
|
+
}
|
|
105
|
+
if (cjsPluginUrl) {
|
|
106
|
+
pluginDefinition.cjsUrl = cjsPluginUrl
|
|
107
|
+
}
|
|
108
|
+
jbrowse.addPlugin(pluginDefinition)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function handleClose() {
|
|
112
|
+
setUMDPluginName('')
|
|
113
|
+
setUMDPluginUrl('')
|
|
114
|
+
setESMPluginUrl('')
|
|
115
|
+
setCJSPluginUrl('')
|
|
116
|
+
onClose()
|
|
117
|
+
}
|
|
118
|
+
|
|
51
119
|
return (
|
|
52
|
-
<Dialog open={open} onClose={
|
|
120
|
+
<Dialog open={open} onClose={handleClose}>
|
|
53
121
|
<DialogTitle>
|
|
54
122
|
Add custom plugin
|
|
55
|
-
<IconButton
|
|
123
|
+
<IconButton
|
|
124
|
+
size="medium"
|
|
125
|
+
className={classes.closeButton}
|
|
126
|
+
onClick={() => onClose()}
|
|
127
|
+
>
|
|
56
128
|
<CloseIcon />
|
|
57
129
|
</IconButton>
|
|
58
130
|
</DialogTitle>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
</
|
|
131
|
+
<form onSubmit={handleSubmit}>
|
|
132
|
+
<DialogContent className={classes.dialogContent}>
|
|
133
|
+
<DialogContentText>
|
|
134
|
+
Enter the name of the plugin and its URL. The name should match what
|
|
135
|
+
is defined in the plugin's build.
|
|
136
|
+
</DialogContentText>
|
|
65
137
|
<TextField
|
|
66
|
-
id="name-input"
|
|
67
|
-
name="
|
|
138
|
+
id="umd-name-input"
|
|
139
|
+
name="umdName"
|
|
68
140
|
label="Plugin name"
|
|
69
141
|
variant="outlined"
|
|
70
|
-
value={
|
|
71
|
-
onChange={
|
|
142
|
+
value={umdPluginName}
|
|
143
|
+
onChange={handleChange}
|
|
72
144
|
/>
|
|
73
145
|
<TextField
|
|
74
|
-
id="url-input"
|
|
75
|
-
name="
|
|
146
|
+
id="umd-url-input"
|
|
147
|
+
name="umdUrl"
|
|
76
148
|
label="Plugin URL"
|
|
77
149
|
variant="outlined"
|
|
78
|
-
value={
|
|
79
|
-
onChange={
|
|
150
|
+
value={umdPluginUrl}
|
|
151
|
+
onChange={handleChange}
|
|
80
152
|
/>
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
153
|
+
<DialogContentText onClick={handleOpenAdvancedOptions}>
|
|
154
|
+
<IconButton
|
|
155
|
+
className={clsx(classes.expand, {
|
|
156
|
+
[classes.expandOpen]: advancedOptionsOpen,
|
|
157
|
+
})}
|
|
158
|
+
aria-expanded={advancedOptionsOpen}
|
|
159
|
+
aria-label="show more"
|
|
160
|
+
>
|
|
161
|
+
<ExpandMoreIcon />
|
|
162
|
+
</IconButton>
|
|
163
|
+
Advanced options
|
|
164
|
+
</DialogContentText>
|
|
165
|
+
<Collapse in={advancedOptionsOpen}>
|
|
166
|
+
<div className={classes.dialogContent}>
|
|
167
|
+
<DialogContentText>
|
|
168
|
+
The above fields assume that the plugin is built in UMD format.
|
|
169
|
+
If your plugin is in another format, or you have additional
|
|
170
|
+
builds you want to add (such as a CJS build for using NodeJS
|
|
171
|
+
APIs in desktop), you can enter the URLs for those builds below.
|
|
172
|
+
</DialogContentText>
|
|
173
|
+
<TextField
|
|
174
|
+
id="esm-url-input"
|
|
175
|
+
name="esmUrl"
|
|
176
|
+
label="ESM build URL"
|
|
177
|
+
variant="outlined"
|
|
178
|
+
value={esmPluginUrl}
|
|
179
|
+
onChange={handleChange}
|
|
180
|
+
/>
|
|
181
|
+
<TextField
|
|
182
|
+
id="cjs-url-input"
|
|
183
|
+
name="cjsUrl"
|
|
184
|
+
label="CJS build URL"
|
|
185
|
+
variant="outlined"
|
|
186
|
+
value={cjsPluginUrl}
|
|
187
|
+
onChange={handleChange}
|
|
188
|
+
/>
|
|
189
|
+
</div>
|
|
190
|
+
</Collapse>
|
|
191
|
+
</DialogContent>
|
|
192
|
+
<DialogActions>
|
|
193
|
+
<Button variant="contained" onClick={handleClose}>
|
|
194
|
+
Cancel
|
|
195
|
+
</Button>
|
|
196
|
+
<Button
|
|
197
|
+
variant="contained"
|
|
198
|
+
color="primary"
|
|
199
|
+
onClick={handleSubmit}
|
|
200
|
+
disabled={!ready}
|
|
201
|
+
>
|
|
202
|
+
Submit
|
|
203
|
+
</Button>
|
|
204
|
+
</DialogActions>
|
|
205
|
+
</form>
|
|
98
206
|
</Dialog>
|
|
99
207
|
)
|
|
100
208
|
}
|
|
@@ -131,7 +131,11 @@ function InstalledPlugin({
|
|
|
131
131
|
onClose={name => {
|
|
132
132
|
if (name) {
|
|
133
133
|
const pluginMetadata = pluginManager.pluginMetadata[plugin.name]
|
|
134
|
-
const pluginUrl =
|
|
134
|
+
const pluginUrl =
|
|
135
|
+
pluginMetadata.url ||
|
|
136
|
+
pluginMetadata.esmUrl ||
|
|
137
|
+
pluginMetadata.umdUrl ||
|
|
138
|
+
pluginMetadata.cjsUrl
|
|
135
139
|
if (adminMode) {
|
|
136
140
|
jbrowse.removePlugin(pluginUrl)
|
|
137
141
|
} else if (isSessionWithSessionPlugins(session)) {
|
|
@@ -65,15 +65,15 @@ describe('<PluginStoreWidget />', () => {
|
|
|
65
65
|
)
|
|
66
66
|
await findByText('multiple sequence alignment browser plugin for JBrowse 2')
|
|
67
67
|
fireEvent.click(getByText('Add custom plugin'))
|
|
68
|
-
fireEvent.change(getByLabelText('Plugin
|
|
68
|
+
fireEvent.change(getByLabelText('Plugin URL'), {
|
|
69
69
|
target: {
|
|
70
|
-
value:
|
|
70
|
+
value:
|
|
71
|
+
'https://unpkg.com/jbrowse-plugin-msaview/dist/jbrowse-plugin-msaview.esm.js',
|
|
71
72
|
},
|
|
72
73
|
})
|
|
73
|
-
fireEvent.change(getByLabelText('Plugin
|
|
74
|
+
fireEvent.change(getByLabelText('Plugin name'), {
|
|
74
75
|
target: {
|
|
75
|
-
value:
|
|
76
|
-
'https://unpkg.com/jbrowse-plugin-msaview/dist/jbrowse-plugin-msaview.umd.production.min.js',
|
|
76
|
+
value: 'MsaView',
|
|
77
77
|
},
|
|
78
78
|
})
|
|
79
79
|
fireEvent.click(getByText('Submit'))
|
|
@@ -85,7 +85,8 @@ describe('<PluginStoreWidget />', () => {
|
|
|
85
85
|
expect(getSnapshot(getParent(session)).jbrowse.plugins).toEqual([
|
|
86
86
|
{
|
|
87
87
|
name: 'MsaView',
|
|
88
|
-
|
|
88
|
+
umdUrl:
|
|
89
|
+
'https://unpkg.com/jbrowse-plugin-msaview/dist/jbrowse-plugin-msaview.esm.js',
|
|
89
90
|
},
|
|
90
91
|
])
|
|
91
92
|
})
|
|
@@ -59,12 +59,14 @@ function PluginStoreWidget({ model }: { model: PluginStoreModel }) {
|
|
|
59
59
|
const { pluginManager } = getEnv(model)
|
|
60
60
|
|
|
61
61
|
useEffect(() => {
|
|
62
|
-
|
|
62
|
+
const controller = new AbortController()
|
|
63
|
+
const { signal } = controller
|
|
63
64
|
|
|
64
65
|
;(async () => {
|
|
65
66
|
try {
|
|
66
67
|
const response = await fetch(
|
|
67
68
|
'https://jbrowse.org/plugin-store/plugins.json',
|
|
69
|
+
{ signal },
|
|
68
70
|
)
|
|
69
71
|
if (!response.ok) {
|
|
70
72
|
const err = await response.text()
|
|
@@ -73,7 +75,7 @@ function PluginStoreWidget({ model }: { model: PluginStoreModel }) {
|
|
|
73
75
|
)
|
|
74
76
|
}
|
|
75
77
|
const array = await response.json()
|
|
76
|
-
if (!
|
|
78
|
+
if (!signal.aborted) {
|
|
77
79
|
setPluginArray(array.plugins)
|
|
78
80
|
}
|
|
79
81
|
} catch (e) {
|
|
@@ -83,7 +85,7 @@ function PluginStoreWidget({ model }: { model: PluginStoreModel }) {
|
|
|
83
85
|
})()
|
|
84
86
|
|
|
85
87
|
return () => {
|
|
86
|
-
|
|
88
|
+
controller.abort()
|
|
87
89
|
}
|
|
88
90
|
}, [])
|
|
89
91
|
|
|
@@ -155,11 +157,18 @@ function PluginStoreWidget({ model }: { model: PluginStoreModel }) {
|
|
|
155
157
|
<Typography color="error">{`${error}`}</Typography>
|
|
156
158
|
) : pluginArray ? (
|
|
157
159
|
pluginArray
|
|
158
|
-
.filter(plugin =>
|
|
159
|
-
|
|
160
|
+
.filter(plugin => {
|
|
161
|
+
// If pugin only has cjsUrl, don't display outside desktop
|
|
162
|
+
if (
|
|
163
|
+
!isElectron &&
|
|
164
|
+
!(plugin.esmUrl || plugin.url || plugin.umdUrl)
|
|
165
|
+
) {
|
|
166
|
+
return false
|
|
167
|
+
}
|
|
168
|
+
return plugin.name
|
|
160
169
|
.toLowerCase()
|
|
161
|
-
.includes(model.filterText.toLowerCase())
|
|
162
|
-
)
|
|
170
|
+
.includes(model.filterText.toLowerCase())
|
|
171
|
+
})
|
|
163
172
|
.map(plugin => (
|
|
164
173
|
<PluginCard
|
|
165
174
|
key={(plugin as JBrowsePlugin).name}
|
|
@@ -393,6 +393,26 @@ exports[`<PluginStoreWidget /> renders with the available plugins 1`] = `
|
|
|
393
393
|
LollipopPlugin
|
|
394
394
|
</p>
|
|
395
395
|
</li>
|
|
396
|
+
<li
|
|
397
|
+
class="MuiListItem-root MuiListItem-gutters"
|
|
398
|
+
>
|
|
399
|
+
<svg
|
|
400
|
+
aria-hidden="true"
|
|
401
|
+
class="MuiSvgIcon-root makeStyles-lockedPluginTooltip"
|
|
402
|
+
focusable="false"
|
|
403
|
+
title="This plugin was installed by an administrator, you cannot remove it."
|
|
404
|
+
viewBox="0 0 24 24"
|
|
405
|
+
>
|
|
406
|
+
<path
|
|
407
|
+
d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"
|
|
408
|
+
/>
|
|
409
|
+
</svg>
|
|
410
|
+
<p
|
|
411
|
+
class="MuiTypography-root MuiTypography-body1"
|
|
412
|
+
>
|
|
413
|
+
ArcRenderer
|
|
414
|
+
</p>
|
|
415
|
+
</li>
|
|
396
416
|
<li
|
|
397
417
|
class="MuiListItem-root MuiListItem-gutters"
|
|
398
418
|
>
|