@jbrowse/plugin-data-management 1.5.1 → 1.5.5
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/AssemblyManager/AssemblyTable.d.ts +8 -2
- package/dist/PluginStoreWidget/components/CustomPluginForm.d.ts +1 -1
- package/dist/plugin-data-management.cjs.development.js +190 -99
- 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 +202 -111
- package/dist/plugin-data-management.esm.js.map +1 -1
- package/package.json +3 -2
- package/src/AssemblyManager/AssemblyManager.tsx +3 -1
- package/src/AssemblyManager/AssemblyTable.tsx +40 -39
- package/src/HierarchicalTrackSelectorWidget/model.js +3 -2
- package/src/PluginStoreWidget/components/CustomPluginForm.tsx +151 -43
- package/src/PluginStoreWidget/components/InstalledPlugin.tsx +11 -10
- 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.5",
|
|
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": "214a3bed2d2722f0971c0d0eed37c8d801f086d7"
|
|
59
60
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
3
|
import { makeStyles } from '@material-ui/core/styles'
|
|
4
|
+
import { AnyConfigurationModel } from '@jbrowse/core/configuration/configurationSchema'
|
|
4
5
|
import {
|
|
5
6
|
Button,
|
|
6
7
|
Dialog,
|
|
@@ -52,7 +53,8 @@ const AssemblyManager = observer(
|
|
|
52
53
|
const classes = useStyles()
|
|
53
54
|
const [isFormOpen, setFormOpen] = useState(false)
|
|
54
55
|
const [isAssemblyBeingEdited, setIsAssemblyBeingEdited] = useState(false)
|
|
55
|
-
const [assemblyBeingEdited, setAssemblyBeingEdited] =
|
|
56
|
+
const [assemblyBeingEdited, setAssemblyBeingEdited] =
|
|
57
|
+
useState<AnyConfigurationModel>()
|
|
56
58
|
|
|
57
59
|
const showAssemblyTable = !isFormOpen && !isAssemblyBeingEdited
|
|
58
60
|
|
|
@@ -42,11 +42,14 @@ const AssemblyTable = observer(
|
|
|
42
42
|
setIsAssemblyBeingEdited,
|
|
43
43
|
setAssemblyBeingEdited,
|
|
44
44
|
}: {
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
rootModel: {
|
|
46
|
+
jbrowse: {
|
|
47
|
+
removeAssemblyConf: (arg: string) => void
|
|
48
|
+
assemblies: AnyConfigurationModel[]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
47
51
|
setIsAssemblyBeingEdited(arg: boolean): void
|
|
48
|
-
|
|
49
|
-
setAssemblyBeingEdited(arg: any): void
|
|
52
|
+
setAssemblyBeingEdited(arg: AnyConfigurationModel): void
|
|
50
53
|
}) => {
|
|
51
54
|
const classes = useStyles()
|
|
52
55
|
|
|
@@ -54,41 +57,39 @@ const AssemblyTable = observer(
|
|
|
54
57
|
rootModel.jbrowse.removeAssemblyConf(name)
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
const rows = rootModel.jbrowse.assemblies.map(
|
|
58
|
-
(assembly
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
},
|
|
91
|
-
)
|
|
60
|
+
const rows = rootModel.jbrowse.assemblies.map(assembly => {
|
|
61
|
+
const name = readConfObject(assembly, 'name')
|
|
62
|
+
const displayName = readConfObject(assembly, 'displayName')
|
|
63
|
+
const aliases = readConfObject(assembly, 'aliases')
|
|
64
|
+
return (
|
|
65
|
+
<TableRow key={name}>
|
|
66
|
+
<TableCell>{name}</TableCell>
|
|
67
|
+
<TableCell>{displayName}</TableCell>
|
|
68
|
+
<TableCell>{aliases ? aliases.toString() : ''}</TableCell>
|
|
69
|
+
<TableCell className={classes.buttonCell}>
|
|
70
|
+
<IconButton
|
|
71
|
+
data-testid={`${name}-edit`}
|
|
72
|
+
className={classes.button}
|
|
73
|
+
onClick={() => {
|
|
74
|
+
setIsAssemblyBeingEdited(true)
|
|
75
|
+
setAssemblyBeingEdited(assembly)
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
<CreateIcon color="primary" />
|
|
79
|
+
</IconButton>
|
|
80
|
+
<IconButton
|
|
81
|
+
data-testid={`${name}-delete`}
|
|
82
|
+
className={classes.button}
|
|
83
|
+
onClick={() => {
|
|
84
|
+
removeAssembly(name)
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
<DeleteIcon color="error" />
|
|
88
|
+
</IconButton>
|
|
89
|
+
</TableCell>
|
|
90
|
+
</TableRow>
|
|
91
|
+
)
|
|
92
|
+
})
|
|
92
93
|
|
|
93
94
|
return (
|
|
94
95
|
<TableContainer component={Paper}>
|
|
@@ -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
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
3
|
import { getParent } from 'mobx-state-tree'
|
|
4
|
-
|
|
5
|
-
import { makeStyles } from '@material-ui/core/styles'
|
|
6
4
|
import {
|
|
7
5
|
Button,
|
|
8
6
|
Dialog,
|
|
@@ -13,6 +11,7 @@ import {
|
|
|
13
11
|
ListItem,
|
|
14
12
|
Tooltip,
|
|
15
13
|
Typography,
|
|
14
|
+
makeStyles,
|
|
16
15
|
} from '@material-ui/core'
|
|
17
16
|
|
|
18
17
|
import CloseIcon from '@material-ui/icons/Close'
|
|
@@ -71,7 +70,9 @@ function PluginDialog({
|
|
|
71
70
|
</DialogTitle>
|
|
72
71
|
<DialogContent>
|
|
73
72
|
<Typography>
|
|
74
|
-
Please confirm that you want to remove {plugin}:
|
|
73
|
+
Please confirm that you want to remove {plugin}. Note: if any
|
|
74
|
+
resources in this session still use this plugin, it may cause your
|
|
75
|
+
session to crash
|
|
75
76
|
</Typography>
|
|
76
77
|
<DialogActions>
|
|
77
78
|
<Button
|
|
@@ -113,11 +114,11 @@ function InstalledPlugin({
|
|
|
113
114
|
const [dialogPlugin, setDialogPlugin] = useState<string>()
|
|
114
115
|
|
|
115
116
|
const session = getSession(model)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
const { sessionPlugins } = session as unknown as {
|
|
118
|
+
sessionPlugins: BasePlugin[]
|
|
119
|
+
}
|
|
119
120
|
const isSessionPlugin = sessionPlugins?.some(
|
|
120
|
-
|
|
121
|
+
p => pluginManager.pluginMetadata[plugin.name].url === p.url,
|
|
121
122
|
)
|
|
122
123
|
|
|
123
124
|
const rootModel = getParent(model, 3)
|
|
@@ -131,11 +132,11 @@ function InstalledPlugin({
|
|
|
131
132
|
onClose={name => {
|
|
132
133
|
if (name) {
|
|
133
134
|
const pluginMetadata = pluginManager.pluginMetadata[plugin.name]
|
|
134
|
-
|
|
135
|
+
|
|
135
136
|
if (adminMode) {
|
|
136
|
-
jbrowse.removePlugin(
|
|
137
|
+
jbrowse.removePlugin(pluginMetadata)
|
|
137
138
|
} else if (isSessionWithSessionPlugins(session)) {
|
|
138
|
-
session.removeSessionPlugin(
|
|
139
|
+
session.removeSessionPlugin(pluginMetadata)
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
setDialogPlugin(undefined)
|
|
@@ -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
|
>
|