@jbrowse/plugin-linear-genome-view 1.3.4 → 1.5.0
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/BaseLinearDisplay/components/BaseLinearDisplay.d.ts +3 -2
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +20 -10
- package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +2 -2
- package/dist/LinearBareDisplay/model.d.ts +10 -9
- package/dist/LinearBasicDisplay/model.d.ts +13 -9
- package/dist/LinearGenomeView/components/Header.d.ts +2 -4
- package/dist/LinearGenomeView/components/RefNameAutocomplete.d.ts +1 -11
- package/dist/LinearGenomeView/components/ScaleBar.d.ts +46 -14
- package/dist/LinearGenomeView/components/util.d.ts +2 -0
- package/dist/LinearGenomeView/index.d.ts +13 -2
- package/dist/index.d.ts +47 -56
- package/dist/plugin-linear-genome-view.cjs.development.js +642 -423
- package/dist/plugin-linear-genome-view.cjs.development.js.map +1 -1
- package/dist/plugin-linear-genome-view.cjs.production.min.js +1 -1
- package/dist/plugin-linear-genome-view.cjs.production.min.js.map +1 -1
- package/dist/plugin-linear-genome-view.esm.js +652 -433
- package/dist/plugin-linear-genome-view.esm.js.map +1 -1
- package/package.json +4 -2
- package/src/BaseLinearDisplay/components/BaseLinearDisplay.tsx +100 -21
- package/src/BaseLinearDisplay/models/BaseLinearDisplayModel.tsx +10 -10
- package/src/BaseLinearDisplay/models/serverSideRenderedBlock.ts +15 -13
- package/src/LinearBasicDisplay/model.ts +25 -3
- package/src/LinearGenomeView/components/ExportSvgDialog.tsx +17 -8
- package/src/LinearGenomeView/components/Header.tsx +101 -104
- package/src/LinearGenomeView/components/ImportForm.tsx +146 -113
- package/src/LinearGenomeView/components/LinearGenomeView.test.js +6 -6
- package/src/LinearGenomeView/components/OverviewScaleBar.tsx +4 -1
- package/src/LinearGenomeView/components/RefNameAutocomplete.tsx +196 -169
- package/src/LinearGenomeView/components/SearchResultsDialog.tsx +1 -16
- package/src/LinearGenomeView/components/SequenceDialog.tsx +59 -58
- package/src/LinearGenomeView/components/__snapshots__/LinearGenomeView.test.js.snap +5 -177
- package/src/LinearGenomeView/components/util.ts +8 -0
- package/src/LinearGenomeView/index.tsx +39 -28
- package/src/index.ts +3 -1
|
@@ -1,23 +1,27 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
1
3
|
import { getSession } from '@jbrowse/core/util'
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
FormGroup,
|
|
7
|
+
Typography,
|
|
8
|
+
makeStyles,
|
|
9
|
+
useTheme,
|
|
10
|
+
alpha,
|
|
11
|
+
} from '@material-ui/core'
|
|
2
12
|
import BaseResult from '@jbrowse/core/TextSearch/BaseResults'
|
|
3
|
-
import Button from '@material-ui/core/Button'
|
|
4
|
-
import { makeStyles, useTheme } from '@material-ui/core/styles'
|
|
5
|
-
import { alpha } from '@material-ui/core/styles'
|
|
6
|
-
import FormGroup from '@material-ui/core/FormGroup'
|
|
7
|
-
import Typography from '@material-ui/core/Typography'
|
|
8
|
-
import { observer } from 'mobx-react'
|
|
9
|
-
import { Instance, getEnv } from 'mobx-state-tree'
|
|
10
|
-
import React from 'react'
|
|
11
13
|
|
|
14
|
+
// icons
|
|
12
15
|
import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons'
|
|
13
16
|
import ArrowForwardIcon from '@material-ui/icons/ArrowForward'
|
|
14
17
|
import ArrowBackIcon from '@material-ui/icons/ArrowBack'
|
|
15
|
-
|
|
18
|
+
|
|
19
|
+
// locals
|
|
20
|
+
import { LinearGenomeViewModel, HEADER_BAR_HEIGHT } from '..'
|
|
16
21
|
import RefNameAutocomplete from './RefNameAutocomplete'
|
|
17
22
|
import OverviewScaleBar from './OverviewScaleBar'
|
|
18
23
|
import ZoomControls from './ZoomControls'
|
|
19
|
-
|
|
20
|
-
type LGV = Instance<LinearGenomeViewStateModel>
|
|
24
|
+
import { dedupe } from './util'
|
|
21
25
|
|
|
22
26
|
const WIDGET_HEIGHT = 32
|
|
23
27
|
const SPACING = 7
|
|
@@ -58,7 +62,7 @@ const useStyles = makeStyles(theme => ({
|
|
|
58
62
|
},
|
|
59
63
|
}))
|
|
60
64
|
|
|
61
|
-
const Controls = observer(({ model }: { model:
|
|
65
|
+
const Controls = observer(({ model }: { model: LinearGenomeViewModel }) => {
|
|
62
66
|
const classes = useStyles()
|
|
63
67
|
return (
|
|
64
68
|
<Button
|
|
@@ -73,7 +77,7 @@ const Controls = observer(({ model }: { model: LGV }) => {
|
|
|
73
77
|
)
|
|
74
78
|
})
|
|
75
79
|
|
|
76
|
-
function PanControls({ model }: { model:
|
|
80
|
+
function PanControls({ model }: { model: LinearGenomeViewModel }) {
|
|
77
81
|
const classes = useStyles()
|
|
78
82
|
return (
|
|
79
83
|
<>
|
|
@@ -95,7 +99,7 @@ function PanControls({ model }: { model: LGV }) {
|
|
|
95
99
|
)
|
|
96
100
|
}
|
|
97
101
|
|
|
98
|
-
const RegionWidth = observer(({ model }: { model:
|
|
102
|
+
const RegionWidth = observer(({ model }: { model: LinearGenomeViewModel }) => {
|
|
99
103
|
const classes = useStyles()
|
|
100
104
|
const { coarseTotalBp } = model
|
|
101
105
|
return (
|
|
@@ -105,104 +109,97 @@ const RegionWidth = observer(({ model }: { model: LGV }) => {
|
|
|
105
109
|
)
|
|
106
110
|
})
|
|
107
111
|
|
|
108
|
-
const LinearGenomeViewHeader = observer(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
112
|
+
const LinearGenomeViewHeader = observer(
|
|
113
|
+
({ model }: { model: LinearGenomeViewModel }) => {
|
|
114
|
+
const classes = useStyles()
|
|
115
|
+
const theme = useTheme()
|
|
116
|
+
const session = getSession(model)
|
|
117
|
+
|
|
118
|
+
const { textSearchManager, assemblyManager } = session
|
|
119
|
+
const { assemblyNames, rankSearchResults } = model
|
|
120
|
+
const assemblyName = assemblyNames[0]
|
|
121
|
+
const assembly = assemblyManager.get(assemblyName)
|
|
122
|
+
const searchScope = model.searchScope(assemblyName)
|
|
123
|
+
|
|
124
|
+
async function fetchResults(queryString: string) {
|
|
125
|
+
if (!textSearchManager) {
|
|
126
|
+
console.warn('No text search manager')
|
|
127
|
+
}
|
|
128
|
+
const results = await textSearchManager?.search(
|
|
129
|
+
{
|
|
130
|
+
queryString: queryString.toLowerCase(),
|
|
131
|
+
searchType: 'exact',
|
|
132
|
+
},
|
|
133
|
+
searchScope,
|
|
134
|
+
rankSearchResults,
|
|
130
135
|
)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
searchType: 'exact',
|
|
142
|
-
},
|
|
143
|
-
searchScope,
|
|
144
|
-
rankSearchResults,
|
|
145
|
-
)) || []
|
|
146
|
-
// distinguishes between locstrings and search strings
|
|
147
|
-
if (results.length > 0) {
|
|
148
|
-
model.setSearchResults(results, newRegionValue.toLocaleLowerCase())
|
|
136
|
+
return dedupe(results)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function handleSelectedRegion(option: BaseResult) {
|
|
140
|
+
let trackId = option.getTrackId()
|
|
141
|
+
let location = option.getLocation()
|
|
142
|
+
const label = option.getLabel()
|
|
143
|
+
try {
|
|
144
|
+
if (assembly?.refNames?.includes(location)) {
|
|
145
|
+
model.navToLocString(location)
|
|
149
146
|
} else {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
session.notify(`${e}`, 'warning')
|
|
163
|
-
}
|
|
147
|
+
const results = await fetchResults(label)
|
|
148
|
+
if (results && results.length > 1) {
|
|
149
|
+
model.setSearchResults(results, label.toLowerCase())
|
|
150
|
+
return
|
|
151
|
+
} else if (results?.length === 1) {
|
|
152
|
+
location = results[0].getLocation()
|
|
153
|
+
trackId = results[0].getTrackId()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
model.navToLocString(location, assemblyName)
|
|
157
|
+
if (trackId) {
|
|
158
|
+
model.showTrack(trackId)
|
|
164
159
|
}
|
|
165
160
|
}
|
|
161
|
+
} catch (e) {
|
|
162
|
+
console.error(e)
|
|
163
|
+
session.notify(`${e}`, 'warning')
|
|
166
164
|
}
|
|
167
165
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
background: alpha(theme.palette.background.paper, 0.8),
|
|
166
|
+
|
|
167
|
+
const controls = (
|
|
168
|
+
<div className={classes.headerBar}>
|
|
169
|
+
<Controls model={model} />
|
|
170
|
+
<div className={classes.spacer} />
|
|
171
|
+
<FormGroup row className={classes.headerForm}>
|
|
172
|
+
<PanControls model={model} />
|
|
173
|
+
<RefNameAutocomplete
|
|
174
|
+
onSelect={handleSelectedRegion}
|
|
175
|
+
assemblyName={assemblyName}
|
|
176
|
+
model={model}
|
|
177
|
+
TextFieldProps={{
|
|
178
|
+
variant: 'outlined',
|
|
179
|
+
className: classes.headerRefName,
|
|
180
|
+
style: { margin: SPACING, minWidth: '175px' },
|
|
181
|
+
InputProps: {
|
|
182
|
+
style: {
|
|
183
|
+
padding: 0,
|
|
184
|
+
height: WIDGET_HEIGHT,
|
|
185
|
+
background: alpha(theme.palette.background.paper, 0.8),
|
|
186
|
+
},
|
|
190
187
|
},
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
)
|
|
188
|
+
}}
|
|
189
|
+
/>
|
|
190
|
+
</FormGroup>
|
|
191
|
+
<RegionWidth model={model} />
|
|
192
|
+
<ZoomControls model={model} />
|
|
193
|
+
<div className={classes.spacer} />
|
|
194
|
+
</div>
|
|
195
|
+
)
|
|
200
196
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
if (model.hideHeaderOverview) {
|
|
198
|
+
return controls
|
|
199
|
+
}
|
|
204
200
|
|
|
205
|
-
|
|
206
|
-
}
|
|
201
|
+
return <OverviewScaleBar model={model}>{controls}</OverviewScaleBar>
|
|
202
|
+
},
|
|
203
|
+
)
|
|
207
204
|
|
|
208
205
|
export default LinearGenomeViewHeader
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
|
-
import { getEnv } from 'mobx-state-tree'
|
|
4
3
|
import { getSession } from '@jbrowse/core/util'
|
|
4
|
+
import BaseResult, {
|
|
5
|
+
RefSequenceResult,
|
|
6
|
+
} from '@jbrowse/core/TextSearch/BaseResults'
|
|
5
7
|
import AssemblySelector from '@jbrowse/core/ui/AssemblySelector'
|
|
6
8
|
import {
|
|
7
9
|
Button,
|
|
@@ -15,14 +17,12 @@ import {
|
|
|
15
17
|
import RefNameAutocomplete from './RefNameAutocomplete'
|
|
16
18
|
import SearchResultsDialog from './SearchResultsDialog'
|
|
17
19
|
import { LinearGenomeViewModel } from '..'
|
|
20
|
+
import { dedupe } from './util'
|
|
18
21
|
|
|
19
22
|
const useStyles = makeStyles(theme => ({
|
|
20
23
|
importFormContainer: {
|
|
21
24
|
padding: theme.spacing(2),
|
|
22
25
|
},
|
|
23
|
-
importFormEntry: {
|
|
24
|
-
minWidth: 180,
|
|
25
|
-
},
|
|
26
26
|
button: {
|
|
27
27
|
margin: theme.spacing(2),
|
|
28
28
|
},
|
|
@@ -41,16 +41,14 @@ const ErrorDisplay = observer(({ error }: { error?: Error | string }) => {
|
|
|
41
41
|
const ImportForm = observer(({ model }: { model: LGV }) => {
|
|
42
42
|
const classes = useStyles()
|
|
43
43
|
const session = getSession(model)
|
|
44
|
-
const { assemblyNames, assemblyManager } = session
|
|
45
|
-
const { pluginManager } = getEnv(session)
|
|
46
|
-
const { textSearchManager } = pluginManager.rootModel
|
|
44
|
+
const { assemblyNames, assemblyManager, textSearchManager } = session
|
|
47
45
|
const {
|
|
48
46
|
rankSearchResults,
|
|
49
47
|
isSearchDialogDisplayed,
|
|
50
48
|
error: modelError,
|
|
51
49
|
} = model
|
|
52
|
-
const [selectedAsm, setSelectedAsm] = useState
|
|
53
|
-
const [error, setError] = useState<
|
|
50
|
+
const [selectedAsm, setSelectedAsm] = useState(assemblyNames[0])
|
|
51
|
+
const [error, setError] = useState<typeof modelError | undefined>(modelError)
|
|
54
52
|
const message = !assemblyNames.length ? 'No configured assemblies' : ''
|
|
55
53
|
const searchScope = model.searchScope(selectedAsm)
|
|
56
54
|
|
|
@@ -60,123 +58,158 @@ const ImportForm = observer(({ model }: { model: LGV }) => {
|
|
|
60
58
|
: 'No configured assemblies'
|
|
61
59
|
const regions = assembly?.regions || []
|
|
62
60
|
const err = assemblyError || error
|
|
63
|
-
const [mySelectedRegion, setSelectedRegion] = useState<string>()
|
|
64
|
-
const selectedRegion = mySelectedRegion || regions[0]?.refName
|
|
65
61
|
|
|
62
|
+
const [myOption, setOption] = useState<BaseResult | undefined>()
|
|
63
|
+
|
|
64
|
+
// use this instead of useState initializer because the useState initializer
|
|
65
|
+
// won't update in response to an observable
|
|
66
|
+
const option =
|
|
67
|
+
myOption ||
|
|
68
|
+
new RefSequenceResult({
|
|
69
|
+
refName: regions[0]?.refName,
|
|
70
|
+
label: regions[0]?.refName,
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const selectedRegion = option?.getLocation()
|
|
74
|
+
|
|
75
|
+
async function fetchResults(queryString: string) {
|
|
76
|
+
if (!textSearchManager) {
|
|
77
|
+
console.warn('No text search manager')
|
|
78
|
+
}
|
|
79
|
+
const results = await textSearchManager?.search(
|
|
80
|
+
{
|
|
81
|
+
queryString: queryString.toLowerCase(),
|
|
82
|
+
searchType: 'exact',
|
|
83
|
+
},
|
|
84
|
+
searchScope,
|
|
85
|
+
rankSearchResults,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return dedupe(results)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* gets a string as input, or use stored option results from previous query,
|
|
93
|
+
* then re-query and
|
|
94
|
+
* 1) if it has multiple results: pop a dialog
|
|
95
|
+
* 2) if it's a single result navigate to it
|
|
96
|
+
* 3) else assume it's a locstring and navigate to it
|
|
97
|
+
*/
|
|
66
98
|
async function handleSelectedRegion(input: string) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
{
|
|
76
|
-
queryString: input.toLocaleLowerCase(),
|
|
77
|
-
searchType: 'exact',
|
|
78
|
-
},
|
|
79
|
-
searchScope,
|
|
80
|
-
rankSearchResults,
|
|
81
|
-
)
|
|
82
|
-
if (results?.length > 0) {
|
|
83
|
-
model.setSearchResults(results, input.toLocaleLowerCase())
|
|
99
|
+
if (!option) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
let trackId = option.getTrackId()
|
|
103
|
+
let location = input || option.getLocation() || ''
|
|
104
|
+
try {
|
|
105
|
+
if (assembly?.refNames?.includes(location)) {
|
|
106
|
+
model.navToLocString(location, selectedAsm)
|
|
84
107
|
} else {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
const results = await fetchResults(input)
|
|
109
|
+
if (results && results.length > 1) {
|
|
110
|
+
model.setSearchResults(results, input.toLowerCase())
|
|
111
|
+
return
|
|
112
|
+
} else if (results?.length === 1) {
|
|
113
|
+
location = results[0].getLocation()
|
|
114
|
+
trackId = results[0].getTrackId()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
model.navToLocString(location, selectedAsm)
|
|
118
|
+
if (trackId) {
|
|
119
|
+
model.showTrack(trackId)
|
|
94
120
|
}
|
|
95
121
|
}
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.error(e)
|
|
124
|
+
session.notify(`${e}`, 'warning')
|
|
96
125
|
}
|
|
97
126
|
}
|
|
98
127
|
|
|
128
|
+
// implementation notes:
|
|
129
|
+
// having this wrapped in a form allows intuitive use of enter key to submit
|
|
99
130
|
return (
|
|
100
131
|
<div>
|
|
101
132
|
{err ? <ErrorDisplay error={err} /> : null}
|
|
102
|
-
|
|
103
133
|
<Container className={classes.importFormContainer}>
|
|
104
|
-
<
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
134
|
+
<form
|
|
135
|
+
onSubmit={event => {
|
|
136
|
+
event.preventDefault()
|
|
137
|
+
}}
|
|
138
|
+
>
|
|
139
|
+
<Grid
|
|
140
|
+
container
|
|
141
|
+
spacing={1}
|
|
142
|
+
justifyContent="center"
|
|
143
|
+
alignItems="center"
|
|
144
|
+
>
|
|
145
|
+
<Grid item>
|
|
146
|
+
<AssemblySelector
|
|
147
|
+
onChange={val => {
|
|
148
|
+
setError(undefined)
|
|
149
|
+
setSelectedAsm(val)
|
|
150
|
+
}}
|
|
151
|
+
session={session}
|
|
152
|
+
selected={selectedAsm}
|
|
153
|
+
/>
|
|
154
|
+
</Grid>
|
|
155
|
+
<Grid item>
|
|
156
|
+
{selectedAsm ? (
|
|
157
|
+
err ? (
|
|
158
|
+
<Typography color="error">X</Typography>
|
|
159
|
+
) : selectedRegion && model.volatileWidth ? (
|
|
160
|
+
<RefNameAutocomplete
|
|
161
|
+
model={model}
|
|
162
|
+
assemblyName={message ? undefined : selectedAsm}
|
|
163
|
+
value={selectedRegion}
|
|
164
|
+
onSelect={option => {
|
|
165
|
+
setOption(option)
|
|
166
|
+
}}
|
|
167
|
+
TextFieldProps={{
|
|
168
|
+
margin: 'normal',
|
|
169
|
+
variant: 'outlined',
|
|
170
|
+
helperText: 'Enter a sequence or location',
|
|
171
|
+
}}
|
|
172
|
+
/>
|
|
173
|
+
) : (
|
|
174
|
+
<CircularProgress
|
|
175
|
+
role="progressbar"
|
|
176
|
+
size={20}
|
|
177
|
+
disableShrink
|
|
178
|
+
/>
|
|
179
|
+
)
|
|
180
|
+
) : null}
|
|
181
|
+
</Grid>
|
|
182
|
+
<Grid item>
|
|
183
|
+
<Button
|
|
184
|
+
type="submit"
|
|
185
|
+
disabled={!selectedRegion}
|
|
186
|
+
className={classes.button}
|
|
187
|
+
onClick={() => {
|
|
188
|
+
model.setError(undefined)
|
|
189
|
+
if (selectedRegion) {
|
|
190
|
+
handleSelectedRegion(selectedRegion)
|
|
191
|
+
}
|
|
192
|
+
}}
|
|
193
|
+
variant="contained"
|
|
194
|
+
color="primary"
|
|
195
|
+
>
|
|
196
|
+
Open
|
|
197
|
+
</Button>
|
|
198
|
+
<Button
|
|
199
|
+
disabled={!selectedRegion}
|
|
200
|
+
className={classes.button}
|
|
201
|
+
onClick={() => {
|
|
202
|
+
model.setError(undefined)
|
|
203
|
+
model.showAllRegionsInAssembly(selectedAsm)
|
|
204
|
+
}}
|
|
205
|
+
variant="contained"
|
|
206
|
+
color="secondary"
|
|
207
|
+
>
|
|
208
|
+
Show all regions in assembly
|
|
209
|
+
</Button>
|
|
210
|
+
</Grid>
|
|
178
211
|
</Grid>
|
|
179
|
-
</
|
|
212
|
+
</form>
|
|
180
213
|
</Container>
|
|
181
214
|
{isSearchDialogDisplayed ? (
|
|
182
215
|
<SearchResultsDialog
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import { render } from '@testing-library/react'
|
|
2
|
+
import { fireEvent, render, waitFor } from '@testing-library/react'
|
|
3
3
|
import { createTestSession } from '@jbrowse/web/src/rootModel'
|
|
4
4
|
import sizeMe from 'react-sizeme'
|
|
5
5
|
import 'requestidlecallback-polyfill'
|
|
@@ -34,12 +34,12 @@ describe('<LinearGenomeView />', () => {
|
|
|
34
34
|
session.addView('LinearGenomeView', { id: 'lgv' })
|
|
35
35
|
const model = session.views[0]
|
|
36
36
|
model.setWidth(800)
|
|
37
|
-
const {
|
|
38
|
-
const openButton = await findByText('Open')
|
|
39
|
-
expect(container.firstChild).toMatchSnapshot()
|
|
37
|
+
const { findByText } = render(<LinearGenomeView model={model} />)
|
|
40
38
|
expect(model.displayedRegions.length).toEqual(0)
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
fireEvent.click(await findByText('Open'))
|
|
40
|
+
await waitFor(() => {
|
|
41
|
+
expect(model.displayedRegions.length).toEqual(1)
|
|
42
|
+
})
|
|
43
43
|
})
|
|
44
44
|
it('renders one track, one region', async () => {
|
|
45
45
|
const session = createTestSession()
|