@jbrowse/plugin-data-management 2.2.2 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/AddTrackWidget/components/AddTrackWidget.js +28 -5
  2. package/dist/AddTrackWidget/components/AddTrackWidget.js.map +1 -1
  3. package/dist/AddTrackWidget/components/ConfirmTrack.d.ts +2 -3
  4. package/dist/AddTrackWidget/components/ConfirmTrack.js +30 -177
  5. package/dist/AddTrackWidget/components/ConfirmTrack.js.map +1 -1
  6. package/dist/AddTrackWidget/components/DefaultAddTrackWorkflow.js.map +1 -1
  7. package/dist/AddTrackWidget/components/TextIndexingConfig.d.ts +6 -0
  8. package/dist/AddTrackWidget/components/TextIndexingConfig.js +109 -0
  9. package/dist/AddTrackWidget/components/TextIndexingConfig.js.map +1 -0
  10. package/dist/AddTrackWidget/components/TrackAdapterSelector.d.ts +6 -0
  11. package/dist/AddTrackWidget/components/TrackAdapterSelector.js +50 -0
  12. package/dist/AddTrackWidget/components/TrackAdapterSelector.js.map +1 -0
  13. package/dist/AddTrackWidget/components/TrackTypeSelector.d.ts +6 -0
  14. package/dist/AddTrackWidget/components/TrackTypeSelector.js +27 -0
  15. package/dist/AddTrackWidget/components/TrackTypeSelector.js.map +1 -0
  16. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js +3 -4
  17. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js.map +1 -1
  18. package/dist/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.js +3 -1
  19. package/dist/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.js.map +1 -1
  20. package/dist/HierarchicalTrackSelectorWidget/model.d.ts +2 -1
  21. package/dist/HierarchicalTrackSelectorWidget/model.js +5 -5
  22. package/dist/HierarchicalTrackSelectorWidget/model.js.map +1 -1
  23. package/dist/ucsc-trackhub/index.d.ts +2 -2
  24. package/dist/ucsc-trackhub/index.js.map +1 -1
  25. package/dist/ucsc-trackhub/model.d.ts +3 -2
  26. package/dist/ucsc-trackhub/model.js +41 -40
  27. package/dist/ucsc-trackhub/model.js.map +1 -1
  28. package/dist/ucsc-trackhub/ucscAssemblies.js.map +1 -1
  29. package/esm/AddTrackWidget/components/AddTrackWidget.js +6 -6
  30. package/esm/AddTrackWidget/components/AddTrackWidget.js.map +1 -1
  31. package/esm/AddTrackWidget/components/ConfirmTrack.d.ts +2 -3
  32. package/esm/AddTrackWidget/components/ConfirmTrack.js +28 -175
  33. package/esm/AddTrackWidget/components/ConfirmTrack.js.map +1 -1
  34. package/esm/AddTrackWidget/components/DefaultAddTrackWorkflow.js.map +1 -1
  35. package/esm/AddTrackWidget/components/TextIndexingConfig.d.ts +6 -0
  36. package/esm/AddTrackWidget/components/TextIndexingConfig.js +81 -0
  37. package/esm/AddTrackWidget/components/TextIndexingConfig.js.map +1 -0
  38. package/esm/AddTrackWidget/components/TrackAdapterSelector.d.ts +6 -0
  39. package/esm/AddTrackWidget/components/TrackAdapterSelector.js +45 -0
  40. package/esm/AddTrackWidget/components/TrackAdapterSelector.js.map +1 -0
  41. package/esm/AddTrackWidget/components/TrackTypeSelector.d.ts +6 -0
  42. package/esm/AddTrackWidget/components/TrackTypeSelector.js +22 -0
  43. package/esm/AddTrackWidget/components/TrackTypeSelector.js.map +1 -0
  44. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js +3 -4
  45. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js.map +1 -1
  46. package/esm/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.js +3 -1
  47. package/esm/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.js.map +1 -1
  48. package/esm/HierarchicalTrackSelectorWidget/model.d.ts +2 -1
  49. package/esm/HierarchicalTrackSelectorWidget/model.js +5 -5
  50. package/esm/HierarchicalTrackSelectorWidget/model.js.map +1 -1
  51. package/esm/ucsc-trackhub/index.d.ts +2 -2
  52. package/esm/ucsc-trackhub/index.js.map +1 -1
  53. package/esm/ucsc-trackhub/model.d.ts +3 -2
  54. package/esm/ucsc-trackhub/model.js +41 -40
  55. package/esm/ucsc-trackhub/model.js.map +1 -1
  56. package/esm/ucsc-trackhub/ucscAssemblies.js.map +1 -1
  57. package/package.json +3 -4
  58. package/src/AddTrackWidget/components/AddTrackWidget.test.tsx +3 -4
  59. package/src/AddTrackWidget/components/AddTrackWidget.tsx +6 -9
  60. package/src/AddTrackWidget/components/ConfirmTrack.tsx +36 -296
  61. package/src/AddTrackWidget/components/DefaultAddTrackWorkflow.tsx +0 -1
  62. package/src/AddTrackWidget/components/TextIndexingConfig.tsx +134 -0
  63. package/src/AddTrackWidget/components/TrackAdapterSelector.tsx +73 -0
  64. package/src/AddTrackWidget/components/TrackTypeSelector.tsx +46 -0
  65. package/src/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.tsx +4 -4
  66. package/src/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.tsx +5 -1
  67. package/src/HierarchicalTrackSelectorWidget/components/__snapshots__/HierarchicalTrackSelector.test.tsx.snap +1 -1
  68. package/src/HierarchicalTrackSelectorWidget/model.ts +7 -7
  69. package/src/PluginStoreWidget/components/__snapshots__/PluginStoreWidget.test.js.snap +0 -22
  70. package/src/ucsc-trackhub/{index.js → index.ts} +0 -0
  71. package/src/ucsc-trackhub/model.ts +119 -0
  72. package/src/ucsc-trackhub/{ucscAssemblies.js → ucscAssemblies.ts} +0 -0
  73. package/src/ucsc-trackhub/model.js +0 -125
@@ -1,56 +1,32 @@
1
1
  import React, { useEffect, useState } from 'react'
2
2
  import {
3
- Card,
4
- CardContent,
5
3
  Checkbox,
6
4
  FormControl,
7
5
  FormControlLabel,
8
- IconButton,
9
- InputLabel,
10
- InputAdornment,
11
- ListSubheader,
12
6
  Link,
13
- List,
14
- ListItem,
15
- MenuItem,
16
- Paper,
17
7
  TextField,
18
8
  Typography,
19
9
  } from '@mui/material'
20
10
  import { makeStyles } from 'tss-react/mui'
21
- import { readConfObject } from '@jbrowse/core/configuration'
22
11
  import {
23
12
  supportedIndexingAdapters,
24
13
  getSession,
25
- getEnv,
26
14
  isElectron,
27
15
  } from '@jbrowse/core/util'
28
- import PluginManager from '@jbrowse/core/PluginManager'
29
16
  import { observer } from 'mobx-react'
30
17
  import { UNKNOWN } from '@jbrowse/core/util/tracks'
31
- import { AdapterMetadata } from '@jbrowse/core/pluggableElementTypes/AdapterType'
18
+ import { AssemblySelector } from '@jbrowse/core/ui'
32
19
 
33
- // icons
34
- import DeleteIcon from '@mui/icons-material/Delete'
35
- import AddIcon from '@mui/icons-material//Add'
36
20
  // locals
37
21
  import { AddTrackModel } from '../model'
22
+ import TextIndexingConfig from './TextIndexingConfig'
23
+ import TrackTypeSelector from './TrackTypeSelector'
24
+ import TrackAdapterSelector from './TrackAdapterSelector'
38
25
 
39
26
  const useStyles = makeStyles()(theme => ({
40
27
  spacing: {
41
28
  marginBottom: theme.spacing(3),
42
29
  },
43
- paper: {
44
- display: 'flex',
45
- flexDirection: 'column',
46
- padding: theme.spacing(1),
47
- },
48
- spacer: {
49
- height: theme.spacing(8),
50
- },
51
- card: {
52
- marginTop: theme.spacing(1),
53
- },
54
30
  }))
55
31
 
56
32
  function StatusMessage({
@@ -61,215 +37,22 @@ function StatusMessage({
61
37
  trackType: string
62
38
  }) {
63
39
  const { classes } = useStyles()
64
- return trackAdapter.type === 'SNPCoverageAdapter' ? (
40
+ const { type, subadapter } = trackAdapter
41
+ return type === 'SNPCoverageAdapter' ? (
65
42
  <Typography className={classes.spacing}>
66
- Selected <code>{trackType}</code>. Using adapter{' '}
67
- <code>{trackAdapter.type}</code> with subadapter{' '}
68
- <code>{trackAdapter.subadapter?.type}</code>. Please enter a track name
69
- and, if necessary, update the track type.
43
+ Selected <code>{trackType}</code>. Using adapter <code>{type}</code> with
44
+ subadapter <code>{subadapter?.type}</code>. Please enter a track name and,
45
+ if necessary, update the track type.
70
46
  </Typography>
71
47
  ) : (
72
48
  <Typography className={classes.spacing}>
73
- Using adapter <code>{trackAdapter.type}</code> and guessing track type{' '}
49
+ Using adapter <code>{type}</code> and guessing track type{' '}
74
50
  <code>{trackType}</code>. Please enter a track name and, if necessary,
75
51
  update the track type.
76
52
  </Typography>
77
53
  )
78
54
  }
79
55
 
80
- /**
81
- * categorizeAdapters takes a list of adapters and sorts their menu item elements under an appropriate ListSubheader
82
- * element. In this way, adapters that are from external plugins can have headers that differentiate them from the
83
- * out-of-the-box plugins.
84
- * @param adaptersList - a list of adapters found in the PluginManager
85
- * @returns a series of JSX elements that are ListSubheaders followed by the adapters
86
- * found under that subheader
87
- */
88
- function categorizeAdapters(
89
- adaptersList: { name: string; adapterMetadata: AdapterMetadata }[],
90
- ) {
91
- let currentCategory = ''
92
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
- const items: any = []
94
- adaptersList.forEach(adapter => {
95
- if (adapter.adapterMetadata?.category) {
96
- if (currentCategory !== adapter.adapterMetadata?.category) {
97
- currentCategory = adapter.adapterMetadata?.category
98
- items.push(
99
- <ListSubheader
100
- key={adapter.adapterMetadata?.category}
101
- value={adapter.adapterMetadata?.category}
102
- >
103
- {adapter.adapterMetadata?.category}
104
- </ListSubheader>,
105
- )
106
- }
107
- items.push(
108
- <MenuItem key={adapter.name} value={adapter.name}>
109
- {adapter.adapterMetadata?.displayName
110
- ? adapter.adapterMetadata?.displayName
111
- : adapter.name}
112
- </MenuItem>,
113
- )
114
- }
115
- })
116
- return items
117
- }
118
-
119
- function getAdapterTypes(pluginManager: PluginManager) {
120
- return pluginManager.getElementTypesInGroup('adapter') as {
121
- name: string
122
- adapterMetadata: AdapterMetadata
123
- }[]
124
- }
125
-
126
- function getTrackTypes(pluginManager: PluginManager) {
127
- return pluginManager.getElementTypesInGroup('track') as { name: string }[]
128
- }
129
-
130
- const TextIndexingConfig = observer(({ model }: { model: AddTrackModel }) => {
131
- const { classes } = useStyles()
132
- const [value1, setValue1] = useState('')
133
- const [value2, setValue2] = useState('')
134
- const [attributes, setAttributes] = useState(['Name', 'ID'])
135
- const [exclude, setExclude] = useState(['CDS', 'exon'])
136
- const sections = [
137
- {
138
- label: 'Indexing attributes',
139
- values: attributes,
140
- },
141
- {
142
- label: 'Feature types to exclude',
143
- values: exclude,
144
- },
145
- ]
146
- useEffect(() => {
147
- model.setTextIndexingConf({ attributes, exclude })
148
- }, [model, attributes, exclude])
149
-
150
- return (
151
- <Paper className={classes.paper}>
152
- <InputLabel>Indexing configuration</InputLabel>
153
- {sections.map((section, index) => (
154
- <Card raised key={section.label} className={classes.card}>
155
- <CardContent>
156
- <InputLabel>{section.label}</InputLabel>
157
- <List disablePadding>
158
- {section.values.map((val: string, idx: number) => (
159
- <ListItem key={idx} disableGutters>
160
- <TextField
161
- value={val}
162
- InputProps={{
163
- endAdornment: (
164
- <InputAdornment position="end">
165
- <IconButton
166
- color="secondary"
167
- onClick={() => {
168
- const newAttr = section.values.filter((a, i) => {
169
- return i !== idx
170
- })
171
- index === 0
172
- ? setAttributes(newAttr)
173
- : setExclude(newAttr)
174
- }}
175
- >
176
- <DeleteIcon />
177
- </IconButton>
178
- </InputAdornment>
179
- ),
180
- }}
181
- />
182
- </ListItem>
183
- ))}
184
- <ListItem disableGutters>
185
- <TextField
186
- value={index === 0 ? value1 : value2}
187
- placeholder="add new"
188
- onChange={event => {
189
- index === 0
190
- ? setValue1(event.target.value)
191
- : setValue2(event.target.value)
192
- }}
193
- InputProps={{
194
- endAdornment: (
195
- <InputAdornment position="end">
196
- <IconButton
197
- onClick={() => {
198
- if (index === 0) {
199
- const newAttr: string[] = attributes
200
- newAttr.push(value1)
201
- setAttributes(newAttr)
202
- setValue1('')
203
- } else {
204
- const newFeat: string[] = exclude
205
- newFeat.push(value2)
206
- setExclude(newFeat)
207
- setValue2('')
208
- }
209
- }}
210
- disabled={index === 0 ? value1 === '' : value2 === ''}
211
- color="secondary"
212
- data-testid={`stringArrayAdd-Feat`}
213
- >
214
- <AddIcon />
215
- </IconButton>
216
- </InputAdornment>
217
- ),
218
- }}
219
- />
220
- </ListItem>
221
- </List>
222
- </CardContent>
223
- </Card>
224
- ))}
225
- </Paper>
226
- )
227
- })
228
-
229
- const TrackAdapterSelector = observer(({ model }: { model: AddTrackModel }) => {
230
- const { classes } = useStyles()
231
- const { trackAdapter } = model
232
- const { pluginManager } = getEnv(model)
233
- const adapters = getAdapterTypes(pluginManager)
234
- return (
235
- <TextField
236
- className={classes.spacing}
237
- value={trackAdapter?.type !== 'UNKNOWN' ? trackAdapter?.type : ''}
238
- label="adapterType"
239
- helperText="Select an adapter type"
240
- select
241
- fullWidth
242
- onChange={event => model.setAdapterHint(event.target.value)}
243
- SelectProps={{
244
- // @ts-ignore
245
- SelectDisplayProps: { 'data-testid': 'adapterTypeSelect' },
246
- }}
247
- >
248
- {adapters
249
- // Excludes any adapter with the 'adapterMetadata.hiddenFromGUI' property, and anything with the 'adapterMetadata.category' property
250
- .filter(
251
- elt =>
252
- !elt.adapterMetadata?.hiddenFromGUI &&
253
- !elt.adapterMetadata?.category,
254
- )
255
- .map(elt => (
256
- <MenuItem key={elt.name} value={elt.name}>
257
- {elt.adapterMetadata?.displayName
258
- ? elt.adapterMetadata?.displayName
259
- : elt.name}
260
- </MenuItem>
261
- ))}
262
- {
263
- // adapters with the 'adapterMetadata.category' property are categorized
264
- // by the value of the property here
265
- categorizeAdapters(
266
- adapters.filter(elt => !elt.adapterMetadata?.hiddenFromGUI),
267
- )
268
- }
269
- </TextField>
270
- )
271
- })
272
-
273
56
  function UnknownAdapterPrompt({ model }: { model: AddTrackModel }) {
274
57
  const { classes } = useStyles()
275
58
  return (
@@ -299,72 +82,23 @@ function UnknownAdapterPrompt({ model }: { model: AddTrackModel }) {
299
82
  )
300
83
  }
301
84
 
302
- const TrackTypeSelector = observer(({ model }: { model: AddTrackModel }) => {
303
- const { classes } = useStyles()
304
- const session = getSession(model)
305
- const { trackType } = model
306
- const trackTypes = getTrackTypes(getEnv(session).pluginManager)
307
-
308
- return (
309
- <TextField
310
- className={classes.spacing}
311
- value={trackType}
312
- label="trackType"
313
- helperText="Select a track type"
314
- select
315
- fullWidth
316
- onChange={event => {
317
- model.setTrackType(event.target.value)
318
- }}
319
- SelectProps={{
320
- // @ts-ignore
321
- SelectDisplayProps: { 'data-testid': 'trackTypeSelect' },
322
- }}
323
- >
324
- {trackTypes.map(({ name }) => (
325
- <MenuItem key={name} value={name}>
326
- {name}
327
- </MenuItem>
328
- ))}
329
- </TextField>
330
- )
331
- })
332
-
333
- const TrackAssemblySelector = observer(
334
- ({ model }: { model: AddTrackModel }) => {
335
- const session = getSession(model)
336
- const { assembly } = model
337
- return (
338
- <TextField
339
- value={assembly}
340
- label="assemblyName"
341
- helperText="Assembly to which the track will be added"
342
- select
343
- fullWidth
344
- onChange={event => model.setAssembly(event.target.value)}
345
- SelectProps={{
346
- // @ts-ignore
347
- SelectDisplayProps: { 'data-testid': 'assemblyNameSelect' },
348
- }}
349
- >
350
- {session.assemblies
351
- .map(conf => readConfObject(conf, 'name'))
352
- .map(name => (
353
- <MenuItem key={name} value={name}>
354
- {name}
355
- </MenuItem>
356
- ))}
357
- </TextField>
358
- )
359
- },
360
- )
361
-
362
- function ConfirmTrack({ model }: { model: AddTrackModel }) {
85
+ export default observer(function ConfirmTrack({
86
+ model,
87
+ }: {
88
+ model: AddTrackModel
89
+ }) {
363
90
  const { classes } = useStyles()
364
91
  const [check, setCheck] = useState(true)
92
+ const session = getSession(model)
365
93
  const { trackName, trackAdapter, trackType, warningMessage, adapterHint } =
366
94
  model
367
95
 
96
+ useEffect(() => {
97
+ if (adapterHint === '' && trackAdapter) {
98
+ model.setAdapterHint(trackAdapter.type)
99
+ }
100
+ }, [adapterHint, trackAdapter, trackAdapter?.type, model])
101
+
368
102
  if (model.unsupported) {
369
103
  return (
370
104
  <Typography className={classes.spacing}>
@@ -394,10 +128,6 @@ function ConfirmTrack({ model }: { model: AddTrackModel }) {
394
128
  return <UnknownAdapterPrompt model={model} />
395
129
  }
396
130
 
397
- if (adapterHint === '' && trackAdapter) {
398
- model.setAdapterHint(trackAdapter.type)
399
- }
400
-
401
131
  if (!trackAdapter?.type) {
402
132
  return <Typography>Could not recognize this data type.</Typography>
403
133
  }
@@ -422,7 +152,19 @@ function ConfirmTrack({ model }: { model: AddTrackModel }) {
422
152
  />
423
153
  <TrackAdapterSelector model={model} />
424
154
  <TrackTypeSelector model={model} />
425
- <TrackAssemblySelector model={model} />
155
+ <AssemblySelector
156
+ session={session}
157
+ helperText="Select assembly to add track to"
158
+ selected={model.assembly}
159
+ onChange={asm => model.setAssembly(asm)}
160
+ TextFieldProps={{
161
+ fullWidth: true,
162
+ SelectProps: {
163
+ // @ts-ignore
164
+ SelectDisplayProps: { 'data-testid': 'assemblyNameSelect' },
165
+ },
166
+ }}
167
+ />
426
168
  {isElectron && supportedForIndexing && (
427
169
  <FormControl>
428
170
  <FormControlLabel
@@ -444,6 +186,4 @@ function ConfirmTrack({ model }: { model: AddTrackModel }) {
444
186
  ) : null}
445
187
  </div>
446
188
  )
447
- }
448
-
449
- export default observer(ConfirmTrack)
189
+ })
@@ -22,7 +22,6 @@ import { observer } from 'mobx-react'
22
22
  // locals
23
23
  import ConfirmTrack from './ConfirmTrack'
24
24
  import TrackSourceSelect from './TrackSourceSelect'
25
-
26
25
  import { AddTrackModel } from '../model'
27
26
 
28
27
  const useStyles = makeStyles()(theme => ({
@@ -0,0 +1,134 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import {
3
+ Card,
4
+ CardContent,
5
+ IconButton,
6
+ InputLabel,
7
+ InputAdornment,
8
+ List,
9
+ ListItem,
10
+ Paper,
11
+ TextField,
12
+ } from '@mui/material'
13
+ import { makeStyles } from 'tss-react/mui'
14
+ import { observer } from 'mobx-react'
15
+
16
+ // icons
17
+ import DeleteIcon from '@mui/icons-material/Delete'
18
+ import AddIcon from '@mui/icons-material/Add'
19
+
20
+ // locals
21
+ import { AddTrackModel } from '../model'
22
+
23
+ const useStyles = makeStyles()(theme => ({
24
+ paper: {
25
+ display: 'flex',
26
+ flexDirection: 'column',
27
+ padding: theme.spacing(1),
28
+ },
29
+
30
+ card: {
31
+ marginTop: theme.spacing(1),
32
+ },
33
+ }))
34
+
35
+ const TextIndexingConfig = observer(({ model }: { model: AddTrackModel }) => {
36
+ const { classes } = useStyles()
37
+ const [value1, setValue1] = useState('')
38
+ const [value2, setValue2] = useState('')
39
+ const [attributes, setAttributes] = useState(['Name', 'ID'])
40
+ const [exclude, setExclude] = useState(['CDS', 'exon'])
41
+ const sections = [
42
+ {
43
+ label: 'Indexing attributes',
44
+ values: attributes,
45
+ },
46
+ {
47
+ label: 'Feature types to exclude',
48
+ values: exclude,
49
+ },
50
+ ]
51
+ useEffect(() => {
52
+ model.setTextIndexingConf({ attributes, exclude })
53
+ }, [model, attributes, exclude])
54
+
55
+ return (
56
+ <Paper className={classes.paper}>
57
+ <InputLabel>Indexing configuration</InputLabel>
58
+ {sections.map((section, index) => (
59
+ <Card raised key={section.label} className={classes.card}>
60
+ <CardContent>
61
+ <InputLabel>{section.label}</InputLabel>
62
+ <List disablePadding>
63
+ {section.values.map((val: string, idx: number) => (
64
+ <ListItem key={idx} disableGutters>
65
+ <TextField
66
+ value={val}
67
+ InputProps={{
68
+ endAdornment: (
69
+ <InputAdornment position="end">
70
+ <IconButton
71
+ color="secondary"
72
+ onClick={() => {
73
+ const newAttr = section.values.filter((a, i) => {
74
+ return i !== idx
75
+ })
76
+ index === 0
77
+ ? setAttributes(newAttr)
78
+ : setExclude(newAttr)
79
+ }}
80
+ >
81
+ <DeleteIcon />
82
+ </IconButton>
83
+ </InputAdornment>
84
+ ),
85
+ }}
86
+ />
87
+ </ListItem>
88
+ ))}
89
+ <ListItem disableGutters>
90
+ <TextField
91
+ value={index === 0 ? value1 : value2}
92
+ placeholder="add new"
93
+ onChange={event => {
94
+ index === 0
95
+ ? setValue1(event.target.value)
96
+ : setValue2(event.target.value)
97
+ }}
98
+ InputProps={{
99
+ endAdornment: (
100
+ <InputAdornment position="end">
101
+ <IconButton
102
+ onClick={() => {
103
+ if (index === 0) {
104
+ const newAttr: string[] = attributes
105
+ newAttr.push(value1)
106
+ setAttributes(newAttr)
107
+ setValue1('')
108
+ } else {
109
+ const newFeat: string[] = exclude
110
+ newFeat.push(value2)
111
+ setExclude(newFeat)
112
+ setValue2('')
113
+ }
114
+ }}
115
+ disabled={index === 0 ? value1 === '' : value2 === ''}
116
+ color="secondary"
117
+ data-testid={`stringArrayAdd-Feat`}
118
+ >
119
+ <AddIcon />
120
+ </IconButton>
121
+ </InputAdornment>
122
+ ),
123
+ }}
124
+ />
125
+ </ListItem>
126
+ </List>
127
+ </CardContent>
128
+ </Card>
129
+ ))}
130
+ </Paper>
131
+ )
132
+ })
133
+
134
+ export default TextIndexingConfig
@@ -0,0 +1,73 @@
1
+ import React from 'react'
2
+ import { ListSubheader, MenuItem, TextField } from '@mui/material'
3
+ import { makeStyles } from 'tss-react/mui'
4
+ import { getEnv } from '@jbrowse/core/util'
5
+ import { observer } from 'mobx-react'
6
+ import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType'
7
+
8
+ // locals
9
+ import { AddTrackModel } from '../model'
10
+
11
+ const useStyles = makeStyles()(theme => ({
12
+ spacing: {
13
+ marginBottom: theme.spacing(3),
14
+ },
15
+ }))
16
+
17
+ // collate adapters into a map with
18
+ // key: category
19
+ // value: array of adapters with that category
20
+ function categorizeAdapters(adaptersList: AdapterType[]) {
21
+ const map = {} as { [key: string]: AdapterType[] }
22
+ adaptersList.forEach(adapter => {
23
+ const key = adapter.adapterMetadata?.category || 'Default'
24
+ if (!map[key]) {
25
+ map[key] = []
26
+ }
27
+ map[key].push(adapter)
28
+ })
29
+ return map
30
+ }
31
+
32
+ const TrackAdapterSelector = observer(({ model }: { model: AddTrackModel }) => {
33
+ const { classes } = useStyles()
34
+ const { trackAdapter } = model
35
+ const { pluginManager } = getEnv(model)
36
+ return (
37
+ <TextField
38
+ className={classes.spacing}
39
+ value={trackAdapter?.type !== 'UNKNOWN' ? trackAdapter?.type : ''}
40
+ label="Adapter type"
41
+ variant="outlined"
42
+ helperText="Select an adapter type"
43
+ select
44
+ fullWidth
45
+ onChange={event => model.setAdapterHint(event.target.value)}
46
+ SelectProps={{
47
+ // @ts-ignore
48
+ SelectDisplayProps: { 'data-testid': 'adapterTypeSelect' },
49
+ }}
50
+ >
51
+ {Object.entries(
52
+ categorizeAdapters(
53
+ pluginManager
54
+ .getAdapterElements()
55
+ .filter(e => !e.adapterMetadata?.hiddenFromGUI),
56
+ ),
57
+ ).map(([key, val]) => {
58
+ // returning array avoids needing to use a react fragment which
59
+ // Select/TextField sub-elements disagree with
60
+ return [
61
+ <ListSubheader>{key}</ListSubheader>,
62
+ val.map(elt => (
63
+ <MenuItem key={elt.name} value={elt.name}>
64
+ {elt.displayName}
65
+ </MenuItem>
66
+ )),
67
+ ]
68
+ })}
69
+ </TextField>
70
+ )
71
+ })
72
+
73
+ export default TrackAdapterSelector
@@ -0,0 +1,46 @@
1
+ import React from 'react'
2
+ import { MenuItem, TextField } from '@mui/material'
3
+ import { getEnv } from '@jbrowse/core/util'
4
+ import { observer } from 'mobx-react'
5
+ import { makeStyles } from 'tss-react/mui'
6
+
7
+ // locals
8
+ import { AddTrackModel } from '../model'
9
+
10
+ const useStyles = makeStyles()(theme => ({
11
+ spacing: {
12
+ marginBottom: theme.spacing(3),
13
+ },
14
+ }))
15
+
16
+ const TrackTypeSelector = observer(({ model }: { model: AddTrackModel }) => {
17
+ const { classes } = useStyles()
18
+ const { pluginManager } = getEnv(model)
19
+ const { trackType } = model
20
+ const trackTypes = pluginManager.getTrackElements()
21
+
22
+ return (
23
+ <TextField
24
+ className={classes.spacing}
25
+ value={trackType}
26
+ variant="outlined"
27
+ label="Track type"
28
+ helperText="Select track type"
29
+ select
30
+ fullWidth
31
+ onChange={event => model.setTrackType(event.target.value)}
32
+ SelectProps={{
33
+ // @ts-ignore
34
+ SelectDisplayProps: { 'data-testid': 'trackTypeSelect' },
35
+ }}
36
+ >
37
+ {trackTypes.map(({ name, displayName }) => (
38
+ <MenuItem key={name} value={name}>
39
+ {displayName}
40
+ </MenuItem>
41
+ ))}
42
+ </TextField>
43
+ )
44
+ })
45
+
46
+ export default TrackTypeSelector
@@ -1,10 +1,6 @@
1
1
  import React, { useCallback, useMemo, useState, useRef, useEffect } from 'react'
2
2
  import { Fab, Menu, MenuItem } from '@mui/material'
3
3
  import { makeStyles } from 'tss-react/mui'
4
- // icons
5
- import AddIcon from '@mui/icons-material/Add'
6
- // other
7
- import AutoSizer from 'react-virtualized-auto-sizer'
8
4
  import {
9
5
  getSession,
10
6
  isSessionModelWithWidgets,
@@ -13,6 +9,10 @@ import {
13
9
  } from '@jbrowse/core/util'
14
10
  import { observer } from 'mobx-react'
15
11
  import { VariableSizeTree } from 'react-vtree'
12
+ import AutoSizer from 'react-virtualized-auto-sizer'
13
+
14
+ // icons
15
+ import AddIcon from '@mui/icons-material/Add'
16
16
 
17
17
  // locals
18
18
  import { TreeNode, HierarchicalTrackSelectorModel } from '../model'