@apollo-annotation/jbrowse-plugin-apollo 0.3.4 → 0.3.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/index.esm.js +1513 -1074
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +1510 -1065
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.development.js +4681 -2097
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
- package/package.json +4 -4
- package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +13 -0
- package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +88 -17
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +144 -22
- package/src/FeatureDetailsWidget/Attributes.tsx +93 -43
- package/src/FeatureDetailsWidget/BasicInformation.tsx +2 -4
- package/src/FeatureDetailsWidget/FeatureDetailsNavigation.tsx +6 -4
- package/src/FeatureDetailsWidget/Sequence.tsx +16 -33
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +137 -92
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +600 -0
- package/src/FeatureDetailsWidget/TranscriptWidgetSummary.tsx +54 -0
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +13 -6
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +6 -27
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +35 -13
- package/src/LinearApolloDisplay/stateModel/layouts.ts +7 -2
- package/src/LinearApolloDisplay/stateModel/rendering.ts +4 -0
- package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +10 -1
- package/src/OntologyManager/OntologyStore/index.test.ts +1 -0
- package/src/OntologyManager/index.ts +2 -0
- package/src/SixFrameFeatureDisplay/stateModel.ts +5 -1
- package/src/TabularEditor/HybridGrid/Feature.tsx +0 -1
- package/src/TabularEditor/HybridGrid/NumberCell.tsx +8 -1
- package/src/TabularEditor/HybridGrid/ToolBar.tsx +14 -12
- package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +3 -27
- package/src/components/AddAssembly.tsx +608 -291
- package/src/components/CreateApolloAnnotation.tsx +144 -37
- package/src/components/OntologyTermMultiSelect.tsx +3 -0
- package/src/components/index.ts +0 -1
- package/src/extensions/annotationFromJBrowseFeature.ts +3 -2
- package/src/makeDisplayComponent.tsx +3 -4
- package/src/util/annotationFeatureUtils.ts +53 -0
- package/src/util/index.ts +1 -0
- package/src/FeatureDetailsWidget/TranscriptBasic.tsx +0 -200
- package/src/components/ModifyFeatureAttribute.tsx +0 -460
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unsafe-enum-comparison */
|
|
2
1
|
/* eslint-disable @typescript-eslint/unbound-method */
|
|
3
2
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
|
4
3
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
@@ -11,31 +10,35 @@ import {
|
|
|
11
10
|
} from '@apollo-annotation/shared'
|
|
12
11
|
import { readConfObject } from '@jbrowse/core/configuration'
|
|
13
12
|
import { AbstractSessionModel } from '@jbrowse/core/util'
|
|
14
|
-
import LinkIcon from '@mui/icons-material/Link'
|
|
15
13
|
import {
|
|
16
|
-
Box,
|
|
17
14
|
Button,
|
|
18
|
-
Checkbox,
|
|
19
15
|
DialogActions,
|
|
20
16
|
DialogContent,
|
|
21
17
|
DialogContentText,
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
Accordion,
|
|
19
|
+
AccordionSummary,
|
|
20
|
+
AccordionDetails,
|
|
21
|
+
Typography,
|
|
22
|
+
Box,
|
|
24
23
|
FormGroup,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
RadioGroup,
|
|
29
|
-
Select,
|
|
30
|
-
SelectChangeEvent,
|
|
24
|
+
FormControlLabel,
|
|
25
|
+
Checkbox,
|
|
26
|
+
LinearProgress,
|
|
31
27
|
TextField,
|
|
32
|
-
|
|
28
|
+
Tooltip,
|
|
29
|
+
IconButton,
|
|
30
|
+
Table,
|
|
31
|
+
TableCell,
|
|
32
|
+
TableBody,
|
|
33
|
+
InputAdornment,
|
|
34
|
+
TableRow,
|
|
33
35
|
} from '@mui/material'
|
|
34
|
-
|
|
35
|
-
import
|
|
36
|
+
|
|
37
|
+
import { makeStyles } from 'tss-react/mui'
|
|
38
|
+
|
|
36
39
|
import ObjectID from 'bson-objectid'
|
|
37
40
|
import { getRoot } from 'mobx-state-tree'
|
|
38
|
-
import React, { useState } from 'react'
|
|
41
|
+
import React, { useEffect, useState } from 'react'
|
|
39
42
|
|
|
40
43
|
import { ApolloInternetAccountModel } from '../ApolloInternetAccount/model'
|
|
41
44
|
import { ChangeManager } from '../ChangeManager'
|
|
@@ -44,6 +47,12 @@ import { ApolloRootModel } from '../types'
|
|
|
44
47
|
import { createFetchErrorMessage } from '../util'
|
|
45
48
|
import { Dialog } from './Dialog'
|
|
46
49
|
|
|
50
|
+
import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked'
|
|
51
|
+
import RadioButtonCheckedIcon from '@mui/icons-material/RadioButtonChecked'
|
|
52
|
+
|
|
53
|
+
import InfoIcon from '@mui/icons-material/Info'
|
|
54
|
+
import LinkIcon from '@mui/icons-material/Link'
|
|
55
|
+
|
|
47
56
|
interface AddAssemblyProps {
|
|
48
57
|
session: ApolloSessionModel
|
|
49
58
|
handleClose(): void
|
|
@@ -53,14 +62,71 @@ interface AddAssemblyProps {
|
|
|
53
62
|
enum FileType {
|
|
54
63
|
GFF3 = 'text/x-gff3',
|
|
55
64
|
FASTA = 'text/x-fasta',
|
|
65
|
+
BGZIP_FASTA = 'application/x-bgzip-fasta',
|
|
66
|
+
FAI = 'text/x-fai',
|
|
67
|
+
GZI = 'application/x-gzi',
|
|
56
68
|
EXTERNAL = 'text/x-external',
|
|
57
69
|
}
|
|
58
70
|
|
|
71
|
+
const useStyles = makeStyles()((theme) => ({
|
|
72
|
+
accordion: {
|
|
73
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
74
|
+
'&:not(:last-child)': {
|
|
75
|
+
borderBottom: 0,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
accordionSummary: {
|
|
79
|
+
flexDirection: 'row-reverse',
|
|
80
|
+
},
|
|
81
|
+
accordionDetails: {
|
|
82
|
+
padding: theme.spacing(2),
|
|
83
|
+
borderTop: '1px solid rgba(0, 0, 0, .125)',
|
|
84
|
+
},
|
|
85
|
+
radioIcon: {
|
|
86
|
+
color: theme?.palette?.tertiary?.contrastText,
|
|
87
|
+
},
|
|
88
|
+
dialog: {
|
|
89
|
+
// minHeight: 500,
|
|
90
|
+
minWidth: 550,
|
|
91
|
+
maxWidth: 800,
|
|
92
|
+
},
|
|
93
|
+
}))
|
|
94
|
+
|
|
95
|
+
function checkSumbission(
|
|
96
|
+
validAsm: boolean,
|
|
97
|
+
sequenceIsEditable: boolean,
|
|
98
|
+
fileType: FileType,
|
|
99
|
+
fastaFile: File | null,
|
|
100
|
+
fastaIndexFile: File | null,
|
|
101
|
+
fastaGziIndexFile: File | null,
|
|
102
|
+
validFastaUrl: boolean,
|
|
103
|
+
validFastaIndexUrl: boolean,
|
|
104
|
+
validFastaGziIndexUrl: boolean,
|
|
105
|
+
) {
|
|
106
|
+
if (!validAsm) {
|
|
107
|
+
return false
|
|
108
|
+
}
|
|
109
|
+
if (sequenceIsEditable && fastaFile) {
|
|
110
|
+
return true
|
|
111
|
+
}
|
|
112
|
+
if (fileType === FileType.GFF3 && fastaFile) {
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
if (fastaFile && fastaIndexFile && fastaGziIndexFile) {
|
|
116
|
+
return true
|
|
117
|
+
}
|
|
118
|
+
if (validFastaUrl && validFastaIndexUrl && validFastaGziIndexUrl) {
|
|
119
|
+
return true
|
|
120
|
+
}
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
|
|
59
124
|
export function AddAssembly({
|
|
60
125
|
changeManager,
|
|
61
126
|
handleClose,
|
|
62
127
|
session,
|
|
63
128
|
}: AddAssemblyProps) {
|
|
129
|
+
const { classes } = useStyles()
|
|
64
130
|
const { internetAccounts } = getRoot<ApolloRootModel>(session)
|
|
65
131
|
const { notify } = session as unknown as AbstractSessionModel
|
|
66
132
|
const apolloInternetAccounts = internetAccounts.filter(
|
|
@@ -72,62 +138,39 @@ export function AddAssembly({
|
|
|
72
138
|
const [assemblyName, setAssemblyName] = useState('')
|
|
73
139
|
const [errorMessage, setErrorMessage] = useState('')
|
|
74
140
|
const [validAsm, setValidAsm] = useState(false)
|
|
75
|
-
const [
|
|
76
|
-
const [fileType, setFileType] = useState(FileType.GFF3)
|
|
141
|
+
const [fileType, setFileType] = useState(FileType.BGZIP_FASTA)
|
|
77
142
|
const [importFeatures, setImportFeatures] = useState(true)
|
|
143
|
+
const [sequenceIsEditable, setSequenceIsEditable] = useState(false)
|
|
78
144
|
const [submitted, setSubmitted] = useState(false)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
)
|
|
82
|
-
const [
|
|
83
|
-
|
|
84
|
-
const [
|
|
145
|
+
|
|
146
|
+
const [fastaFile, setFastaFile] = useState<File | null>(null)
|
|
147
|
+
const [fastaIndexFile, setFastaIndexFile] = useState<File | null>(null)
|
|
148
|
+
const [fastaGziIndexFile, setFastaGziIndexFile] = useState<File | null>(null)
|
|
149
|
+
|
|
150
|
+
const [fastaUrl, setFastaUrl] = useState<string>('')
|
|
151
|
+
const [fastaIndexUrl, setFastaIndexUrl] = useState<string>('')
|
|
152
|
+
const [fastaGziIndexUrl, setFastaGziIndexUrl] = useState<string>('')
|
|
153
|
+
|
|
85
154
|
const [loading, setLoading] = useState(false)
|
|
155
|
+
const [isGzip, setIsGzip] = useState<boolean>(false)
|
|
86
156
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
(ia) => ia.internetAccountId === e.target.value,
|
|
91
|
-
)
|
|
92
|
-
if (!newlySelectedInternetAccount) {
|
|
93
|
-
throw new Error(
|
|
94
|
-
`Could not find internetAccount with ID "${e.target.value}"`,
|
|
95
|
-
)
|
|
96
|
-
}
|
|
97
|
-
setSelectedInternetAccount(newlySelectedInternetAccount)
|
|
98
|
-
}
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
setFastaIndexUrl(fastaUrl ? `${fastaUrl}.fai` : '')
|
|
159
|
+
}, [fastaUrl])
|
|
99
160
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
const selectedFile = e.target.files.item(0)
|
|
105
|
-
setFile(selectedFile)
|
|
106
|
-
if (!selectedFile) {
|
|
107
|
-
return
|
|
108
|
-
}
|
|
109
|
-
const fileNameLower = selectedFile.name.toLowerCase()
|
|
110
|
-
if (
|
|
111
|
-
fileNameLower.endsWith('.fasta') ||
|
|
112
|
-
fileNameLower.endsWith('.fna') ||
|
|
113
|
-
fileNameLower.endsWith('.fa')
|
|
114
|
-
) {
|
|
115
|
-
setFileType(FileType.FASTA)
|
|
116
|
-
} else if (
|
|
117
|
-
fileNameLower.endsWith('.gff3') ||
|
|
118
|
-
fileNameLower.endsWith('.gff')
|
|
119
|
-
) {
|
|
120
|
-
setFileType(FileType.GFF3)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
setFastaGziIndexUrl(fastaUrl ? `${fastaUrl}.gzi` : '')
|
|
163
|
+
}, [fastaUrl])
|
|
123
164
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
if (sequenceIsEditable || fileType === FileType.GFF3) {
|
|
167
|
+
setIsGzip(
|
|
168
|
+
fastaFile?.name.toLocaleLowerCase().endsWith('.gz') ? true : false,
|
|
169
|
+
)
|
|
170
|
+
} else {
|
|
171
|
+
setIsGzip(true)
|
|
172
|
+
}
|
|
173
|
+
}, [fastaFile, sequenceIsEditable, fileType])
|
|
131
174
|
|
|
132
175
|
function checkAssemblyName(assembly: string) {
|
|
133
176
|
const { assemblies } = session as unknown as AbstractSessionModel
|
|
@@ -143,6 +186,70 @@ export function AddAssembly({
|
|
|
143
186
|
}
|
|
144
187
|
}
|
|
145
188
|
|
|
189
|
+
async function uploadFile(file: File, fileType: FileType): Promise<string> {
|
|
190
|
+
const { jobsManager } = session
|
|
191
|
+
const controller = new AbortController()
|
|
192
|
+
|
|
193
|
+
const [{ baseURL, getFetcher }] = apolloInternetAccounts
|
|
194
|
+
const url = new URL('files', baseURL)
|
|
195
|
+
|
|
196
|
+
url.searchParams.set('type', fileType)
|
|
197
|
+
const uri = url.href
|
|
198
|
+
const formData = new FormData()
|
|
199
|
+
let filename = file.name
|
|
200
|
+
|
|
201
|
+
if (fileType === FileType.FAI || fileType === FileType.GZI) {
|
|
202
|
+
filename = `${filename}.txt`
|
|
203
|
+
} else if (isGzip && !file.name.toLocaleLowerCase().endsWith('.gz')) {
|
|
204
|
+
filename = `${filename}.gz`
|
|
205
|
+
} else if (!isGzip && file.name.toLocaleLowerCase().endsWith('.gz')) {
|
|
206
|
+
filename = `${filename}.txt`
|
|
207
|
+
}
|
|
208
|
+
formData.append('file', file, filename)
|
|
209
|
+
formData.append('type', fileType)
|
|
210
|
+
const apolloFetchFile = getFetcher({
|
|
211
|
+
locationType: 'UriLocation',
|
|
212
|
+
uri,
|
|
213
|
+
})
|
|
214
|
+
if (apolloFetchFile) {
|
|
215
|
+
const job = {
|
|
216
|
+
name: `UploadAssemblyFile for ${assemblyName}`,
|
|
217
|
+
statusMessage: 'Pre-validating',
|
|
218
|
+
progressPct: 0,
|
|
219
|
+
cancelCallback: () => {
|
|
220
|
+
controller.abort()
|
|
221
|
+
jobsManager.abortJob(job.name)
|
|
222
|
+
},
|
|
223
|
+
}
|
|
224
|
+
jobsManager.runJob(job)
|
|
225
|
+
jobsManager.update(
|
|
226
|
+
job.name,
|
|
227
|
+
`Uploading ${file.name}, this may take awhile`,
|
|
228
|
+
)
|
|
229
|
+
const { signal } = controller
|
|
230
|
+
|
|
231
|
+
const response = await apolloFetchFile(uri, {
|
|
232
|
+
method: 'POST',
|
|
233
|
+
body: formData,
|
|
234
|
+
signal,
|
|
235
|
+
})
|
|
236
|
+
if (!response.ok) {
|
|
237
|
+
const newErrorMessage = await createFetchErrorMessage(
|
|
238
|
+
response,
|
|
239
|
+
'Error when inserting new assembly (while uploading file)',
|
|
240
|
+
)
|
|
241
|
+
jobsManager.abortJob(job.name, newErrorMessage)
|
|
242
|
+
setErrorMessage(newErrorMessage)
|
|
243
|
+
return ''
|
|
244
|
+
}
|
|
245
|
+
const result = await response.json()
|
|
246
|
+
const fileId = result._id as string
|
|
247
|
+
jobsManager.done(job)
|
|
248
|
+
return fileId
|
|
249
|
+
}
|
|
250
|
+
throw new Error('Failed to fetch')
|
|
251
|
+
}
|
|
252
|
+
|
|
146
253
|
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
147
254
|
event.preventDefault()
|
|
148
255
|
setErrorMessage('')
|
|
@@ -153,158 +260,137 @@ export function AddAssembly({
|
|
|
153
260
|
handleClose()
|
|
154
261
|
event.preventDefault()
|
|
155
262
|
|
|
156
|
-
const { jobsManager } = session
|
|
157
|
-
const controller = new AbortController()
|
|
158
|
-
|
|
159
|
-
const job = {
|
|
160
|
-
name: `UploadAssemblyFile for ${assemblyName}`,
|
|
161
|
-
statusMessage: 'Pre-validating',
|
|
162
|
-
progressPct: 0,
|
|
163
|
-
cancelCallback: () => {
|
|
164
|
-
controller.abort()
|
|
165
|
-
jobsManager.abortJob(job.name)
|
|
166
|
-
},
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
jobsManager.runJob(job)
|
|
170
|
-
|
|
171
|
-
let fileId = ''
|
|
172
|
-
const { baseURL, getFetcher, internetAccountId } = selectedInternetAccount
|
|
173
|
-
if (fileType !== FileType.EXTERNAL && file) {
|
|
174
|
-
// First upload file
|
|
175
|
-
const url = new URL('files', baseURL)
|
|
176
|
-
url.searchParams.set('type', fileType)
|
|
177
|
-
const uri = url.href
|
|
178
|
-
const formData = new FormData()
|
|
179
|
-
formData.append('file', file)
|
|
180
|
-
formData.append('fileName', file.name)
|
|
181
|
-
formData.append('type', fileType)
|
|
182
|
-
const apolloFetchFile = getFetcher({
|
|
183
|
-
locationType: 'UriLocation',
|
|
184
|
-
uri,
|
|
185
|
-
})
|
|
186
|
-
if (apolloFetchFile) {
|
|
187
|
-
jobsManager.update(job.name, 'Uploading file, this may take awhile')
|
|
188
|
-
const { signal } = controller
|
|
189
|
-
const response = await apolloFetchFile(uri, {
|
|
190
|
-
method: 'POST',
|
|
191
|
-
body: formData,
|
|
192
|
-
signal,
|
|
193
|
-
})
|
|
194
|
-
if (!response.ok) {
|
|
195
|
-
const newErrorMessage = await createFetchErrorMessage(
|
|
196
|
-
response,
|
|
197
|
-
'Error when inserting new assembly (while uploading file)',
|
|
198
|
-
)
|
|
199
|
-
jobsManager.abortJob(job.name, newErrorMessage)
|
|
200
|
-
setErrorMessage(newErrorMessage)
|
|
201
|
-
return
|
|
202
|
-
}
|
|
203
|
-
const result = await response.json()
|
|
204
|
-
fileId = result._id
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
263
|
let change:
|
|
209
264
|
| AddAssemblyFromExternalChange
|
|
210
265
|
| AddAssemblyAndFeaturesFromFileChange
|
|
211
266
|
| AddAssemblyFromFileChange
|
|
267
|
+
|
|
212
268
|
if (fileType === FileType.EXTERNAL) {
|
|
213
269
|
change = new AddAssemblyFromExternalChange({
|
|
214
270
|
typeName: 'AddAssemblyFromExternalChange',
|
|
215
|
-
|
|
216
271
|
assembly: new ObjectID().toHexString(),
|
|
217
272
|
assemblyName,
|
|
218
273
|
externalLocation: {
|
|
219
|
-
fa:
|
|
220
|
-
fai:
|
|
221
|
-
|
|
274
|
+
fa: fastaUrl,
|
|
275
|
+
fai: fastaIndexUrl,
|
|
276
|
+
gzi: fastaGziIndexUrl,
|
|
222
277
|
},
|
|
223
278
|
})
|
|
224
279
|
} else {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
assemblyName,
|
|
228
|
-
fileIds: { fa: fileId },
|
|
280
|
+
if (!fastaFile) {
|
|
281
|
+
throw new Error('Missing fasta file')
|
|
229
282
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
283
|
+
if (fileType === FileType.GFF3 && importFeatures) {
|
|
284
|
+
const faId = await uploadFile(fastaFile, FileType.GFF3)
|
|
285
|
+
change = new AddAssemblyAndFeaturesFromFileChange({
|
|
286
|
+
typeName: 'AddAssemblyAndFeaturesFromFileChange',
|
|
287
|
+
assembly: new ObjectID().toHexString(),
|
|
288
|
+
assemblyName,
|
|
289
|
+
fileIds: { fa: faId },
|
|
290
|
+
})
|
|
291
|
+
} else if (fileType === FileType.GFF3) {
|
|
292
|
+
const faId = await uploadFile(fastaFile, FileType.GFF3)
|
|
293
|
+
change = new AddAssemblyFromFileChange({
|
|
294
|
+
typeName: 'AddAssemblyFromFileChange',
|
|
295
|
+
assembly: new ObjectID().toHexString(),
|
|
296
|
+
assemblyName,
|
|
297
|
+
fileIds: {
|
|
298
|
+
fa: faId,
|
|
299
|
+
},
|
|
300
|
+
})
|
|
301
|
+
} else if (sequenceIsEditable) {
|
|
302
|
+
const faId = await uploadFile(fastaFile, FileType.FASTA)
|
|
303
|
+
change = new AddAssemblyFromFileChange({
|
|
304
|
+
typeName: 'AddAssemblyFromFileChange',
|
|
305
|
+
assembly: new ObjectID().toHexString(),
|
|
306
|
+
assemblyName,
|
|
307
|
+
fileIds: {
|
|
308
|
+
fa: faId,
|
|
309
|
+
},
|
|
310
|
+
})
|
|
311
|
+
} else {
|
|
312
|
+
if (!fastaIndexFile || !fastaGziIndexFile) {
|
|
313
|
+
throw new Error('Missing fasta index files')
|
|
314
|
+
}
|
|
315
|
+
const faId = await uploadFile(fastaFile, FileType.BGZIP_FASTA)
|
|
316
|
+
const faiId = await uploadFile(fastaIndexFile, FileType.FAI)
|
|
317
|
+
const gziId = await uploadFile(fastaGziIndexFile, FileType.GZI)
|
|
241
318
|
|
|
242
|
-
|
|
319
|
+
change = new AddAssemblyFromFileChange({
|
|
320
|
+
typeName: 'AddAssemblyFromFileChange',
|
|
321
|
+
assembly: new ObjectID().toHexString(),
|
|
322
|
+
assemblyName,
|
|
323
|
+
fileIds: {
|
|
324
|
+
fa: faId,
|
|
325
|
+
fai: faiId,
|
|
326
|
+
gzi: gziId,
|
|
327
|
+
},
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
}
|
|
243
331
|
|
|
332
|
+
const [{ internetAccountId }] = apolloInternetAccounts
|
|
244
333
|
await changeManager.submit(change, {
|
|
245
334
|
internetAccountId,
|
|
246
335
|
updateJobsManager: true,
|
|
247
336
|
})
|
|
248
|
-
|
|
249
337
|
setSubmitted(false)
|
|
250
338
|
setLoading(false)
|
|
251
339
|
}
|
|
252
340
|
|
|
253
|
-
let
|
|
341
|
+
let validFastaUrl = false
|
|
254
342
|
try {
|
|
255
|
-
const url = new URL(
|
|
343
|
+
const url = new URL(fastaUrl)
|
|
256
344
|
if (url.protocol === 'http:' || url.protocol === 'https:') {
|
|
257
|
-
|
|
345
|
+
validFastaUrl = true
|
|
258
346
|
}
|
|
259
347
|
} catch {
|
|
260
348
|
// pass
|
|
261
349
|
}
|
|
262
|
-
let
|
|
350
|
+
let validFastaIndexUrl = false
|
|
263
351
|
try {
|
|
264
|
-
const url = new URL(
|
|
352
|
+
const url = new URL(fastaIndexUrl)
|
|
265
353
|
if (url.protocol === 'http:' || url.protocol === 'https:') {
|
|
266
|
-
|
|
354
|
+
validFastaIndexUrl = true
|
|
267
355
|
}
|
|
268
356
|
} catch {
|
|
269
357
|
// pass
|
|
270
358
|
}
|
|
271
|
-
let
|
|
359
|
+
let validFastaGziIndexUrl = false
|
|
272
360
|
try {
|
|
273
|
-
const url = new URL(
|
|
361
|
+
const url = new URL(fastaGziIndexUrl)
|
|
274
362
|
if (url.protocol === 'http:' || url.protocol === 'https:') {
|
|
275
|
-
|
|
363
|
+
validFastaGziIndexUrl = true
|
|
276
364
|
}
|
|
277
365
|
} catch {
|
|
278
366
|
// pass
|
|
279
367
|
}
|
|
280
368
|
|
|
369
|
+
const [expanded, setExpanded] = React.useState<string>('panelFastaInput')
|
|
370
|
+
|
|
371
|
+
const handleAccordionChange =
|
|
372
|
+
(panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
|
|
373
|
+
if (newExpanded) {
|
|
374
|
+
setExpanded(panel)
|
|
375
|
+
}
|
|
376
|
+
if (panel === 'panelGffInput') {
|
|
377
|
+
setIsGzip(false)
|
|
378
|
+
} else {
|
|
379
|
+
setIsGzip(true)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
281
383
|
return (
|
|
282
384
|
<Dialog
|
|
283
|
-
open
|
|
284
|
-
|
|
385
|
+
open={true}
|
|
386
|
+
handleClose={handleClose}
|
|
285
387
|
data-testid="add-assembly-dialog"
|
|
286
388
|
title="Add new assembly"
|
|
287
|
-
|
|
389
|
+
maxWidth={false}
|
|
288
390
|
>
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
{apolloInternetAccounts.length > 1 ? (
|
|
293
|
-
<>
|
|
294
|
-
<DialogContentText>Select account</DialogContentText>
|
|
295
|
-
<Select
|
|
296
|
-
value={selectedInternetAccount.internetAccountId}
|
|
297
|
-
onChange={handleChangeInternetAccount}
|
|
298
|
-
disabled={submitted && !errorMessage}
|
|
299
|
-
>
|
|
300
|
-
{internetAccounts.map((option) => (
|
|
301
|
-
<MenuItem key={option.id} value={option.internetAccountId}>
|
|
302
|
-
{option.name}
|
|
303
|
-
</MenuItem>
|
|
304
|
-
))}
|
|
305
|
-
</Select>
|
|
306
|
-
</>
|
|
307
|
-
) : null}
|
|
391
|
+
<form onSubmit={onSubmit} data-testid="submit-form">
|
|
392
|
+
<DialogContent className={classes.dialog}>
|
|
393
|
+
{loading ? <LinearProgress /> : null}
|
|
308
394
|
<TextField
|
|
309
395
|
margin="dense"
|
|
310
396
|
id="name"
|
|
@@ -319,147 +405,378 @@ export function AddAssembly({
|
|
|
319
405
|
}}
|
|
320
406
|
disabled={submitted && !errorMessage}
|
|
321
407
|
/>
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
408
|
+
|
|
409
|
+
<Accordion
|
|
410
|
+
disableGutters
|
|
411
|
+
elevation={0}
|
|
412
|
+
square
|
|
413
|
+
className={classes.accordion}
|
|
414
|
+
expanded={expanded === 'panelFastaInput'}
|
|
415
|
+
onChange={handleAccordionChange('panelFastaInput')}
|
|
416
|
+
>
|
|
417
|
+
<AccordionSummary
|
|
418
|
+
className={classes.accordionSummary}
|
|
419
|
+
expandIcon={
|
|
420
|
+
expanded === 'panelFastaInput' ? (
|
|
421
|
+
<RadioButtonCheckedIcon
|
|
422
|
+
className={classes.radioIcon}
|
|
423
|
+
sx={{ fontSize: '1.2rem', ml: 5 }}
|
|
424
|
+
/>
|
|
425
|
+
) : (
|
|
426
|
+
<RadioButtonUncheckedIcon
|
|
427
|
+
className={classes.radioIcon}
|
|
428
|
+
sx={{ fontSize: '1.2rem', mr: 5 }}
|
|
429
|
+
/>
|
|
430
|
+
)
|
|
431
|
+
}
|
|
432
|
+
aria-controls="panelFastaInputd-content"
|
|
433
|
+
id="panelFastaInputd-header"
|
|
330
434
|
>
|
|
331
|
-
<
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
label="GFF3"
|
|
335
|
-
disabled={submitted && !errorMessage}
|
|
336
|
-
/>
|
|
337
|
-
<FormControlLabel
|
|
338
|
-
value={FileType.FASTA}
|
|
339
|
-
control={<Radio />}
|
|
340
|
-
label="FASTA"
|
|
341
|
-
disabled={submitted && !errorMessage}
|
|
342
|
-
/>
|
|
343
|
-
<FormControlLabel
|
|
344
|
-
value={FileType.EXTERNAL}
|
|
345
|
-
control={<Radio />}
|
|
346
|
-
label="External"
|
|
347
|
-
disabled={submitted && !errorMessage}
|
|
348
|
-
/>
|
|
349
|
-
</RadioGroup>
|
|
350
|
-
</FormControl>
|
|
351
|
-
{fileType === FileType.EXTERNAL ? (
|
|
352
|
-
<Box style={{ marginTop: 20 }}>
|
|
353
|
-
<Typography variant="caption">
|
|
354
|
-
Enter FASTA and FASTA index(es) URL
|
|
355
|
-
</Typography>
|
|
356
|
-
<TextField
|
|
357
|
-
margin="dense"
|
|
358
|
-
helperText="Can be bgz-compressed"
|
|
359
|
-
id="fasta"
|
|
360
|
-
label="FASTA"
|
|
361
|
-
type="TextField"
|
|
362
|
-
fullWidth
|
|
363
|
-
variant="outlined"
|
|
364
|
-
error={!validFastaFile}
|
|
365
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
366
|
-
setFastaFile(e.target.value)
|
|
367
|
-
}}
|
|
368
|
-
disabled={submitted && !errorMessage}
|
|
369
|
-
InputProps={{
|
|
370
|
-
startAdornment: (
|
|
371
|
-
<InputAdornment position="start">
|
|
372
|
-
<LinkIcon />
|
|
373
|
-
</InputAdornment>
|
|
374
|
-
),
|
|
375
|
-
}}
|
|
376
|
-
/>
|
|
377
|
-
<TextField
|
|
378
|
-
margin="dense"
|
|
379
|
-
id="fasta-index"
|
|
380
|
-
label="FASTA Index"
|
|
381
|
-
helperText=".fai or .gz.fai"
|
|
382
|
-
type="TextField"
|
|
383
|
-
fullWidth
|
|
384
|
-
variant="outlined"
|
|
385
|
-
error={!validFastaIndexFile}
|
|
386
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
387
|
-
setFastaIndexFile(e.target.value)
|
|
388
|
-
}}
|
|
389
|
-
disabled={submitted && !errorMessage}
|
|
390
|
-
InputProps={{
|
|
391
|
-
startAdornment: (
|
|
392
|
-
<InputAdornment position="start">
|
|
393
|
-
<LinkIcon />
|
|
394
|
-
</InputAdornment>
|
|
395
|
-
),
|
|
396
|
-
}}
|
|
397
|
-
/>
|
|
398
|
-
<TextField
|
|
399
|
-
margin="dense"
|
|
400
|
-
id="fasta-gzi-index"
|
|
401
|
-
label="FASTA GZI Index"
|
|
402
|
-
helperText="Only for bgz-compressed FASTA, .gz.gzi"
|
|
403
|
-
type="TextField"
|
|
404
|
-
fullWidth
|
|
405
|
-
variant="outlined"
|
|
406
|
-
error={Boolean(fastaGziIndexFile) && !validFastaGziIndexFile}
|
|
407
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
408
|
-
setFastaGziIndexFile(e.target.value)
|
|
409
|
-
}}
|
|
410
|
-
disabled={submitted && !errorMessage}
|
|
411
|
-
InputProps={{
|
|
412
|
-
startAdornment: (
|
|
413
|
-
<InputAdornment position="start">
|
|
414
|
-
<LinkIcon />
|
|
415
|
-
</InputAdornment>
|
|
416
|
-
),
|
|
417
|
-
}}
|
|
418
|
-
/>
|
|
419
|
-
</Box>
|
|
420
|
-
) : (
|
|
421
|
-
<Box style={{ marginTop: 20 }}>
|
|
422
|
-
<input
|
|
423
|
-
type="file"
|
|
424
|
-
onChange={handleChangeFile}
|
|
425
|
-
disabled={submitted && !errorMessage}
|
|
426
|
-
/>
|
|
435
|
+
<Typography component="span">FASTA input</Typography>
|
|
436
|
+
</AccordionSummary>
|
|
437
|
+
<AccordionDetails className={classes.accordionDetails}>
|
|
427
438
|
<FormGroup>
|
|
428
439
|
<FormControlLabel
|
|
440
|
+
data-testid="files-on-url-checkbox"
|
|
429
441
|
control={
|
|
430
442
|
<Checkbox
|
|
431
|
-
checked={fileType === FileType.GFF3 && importFeatures}
|
|
432
443
|
onChange={() => {
|
|
433
|
-
|
|
444
|
+
setFileType(
|
|
445
|
+
fileType === FileType.EXTERNAL
|
|
446
|
+
? FileType.BGZIP_FASTA
|
|
447
|
+
: FileType.EXTERNAL,
|
|
448
|
+
)
|
|
449
|
+
if (fileType === FileType.EXTERNAL) {
|
|
450
|
+
setSequenceIsEditable(false)
|
|
451
|
+
}
|
|
434
452
|
}}
|
|
453
|
+
checked={fileType === FileType.EXTERNAL}
|
|
435
454
|
disabled={
|
|
436
|
-
fileType !== FileType.GFF3
|
|
437
|
-
(submitted && !errorMessage)
|
|
455
|
+
sequenceIsEditable && fileType !== FileType.GFF3
|
|
438
456
|
}
|
|
439
457
|
/>
|
|
440
458
|
}
|
|
441
|
-
label=
|
|
459
|
+
label={
|
|
460
|
+
<Box display="flex" alignItems="center">
|
|
461
|
+
Use external URLs
|
|
462
|
+
<Tooltip
|
|
463
|
+
title="Use external URLs to provide FASTA and index files. Does not copy the files to the Apollo collaboration server, so ensure the URLs are stable."
|
|
464
|
+
placement="top-start"
|
|
465
|
+
>
|
|
466
|
+
<IconButton size="small">
|
|
467
|
+
<InfoIcon sx={{ fontSize: 18 }} />
|
|
468
|
+
</IconButton>
|
|
469
|
+
</Tooltip>
|
|
470
|
+
</Box>
|
|
471
|
+
}
|
|
442
472
|
/>
|
|
473
|
+
|
|
474
|
+
<FormControlLabel
|
|
475
|
+
data-testid="sequence-is-editable-checkbox"
|
|
476
|
+
control={
|
|
477
|
+
<Checkbox
|
|
478
|
+
onChange={() => {
|
|
479
|
+
setSequenceIsEditable(!sequenceIsEditable)
|
|
480
|
+
}}
|
|
481
|
+
/>
|
|
482
|
+
}
|
|
483
|
+
checked={sequenceIsEditable}
|
|
484
|
+
disabled={fileType === FileType.EXTERNAL}
|
|
485
|
+
label={
|
|
486
|
+
<Box display="flex" alignItems="center">
|
|
487
|
+
Store sequence in database
|
|
488
|
+
<Tooltip
|
|
489
|
+
title="Enables users to edit the genomic sequence, but comes with performance impacts. Use with care."
|
|
490
|
+
placement="top-start"
|
|
491
|
+
>
|
|
492
|
+
<IconButton size="small">
|
|
493
|
+
<InfoIcon sx={{ fontSize: 18 }} />
|
|
494
|
+
</IconButton>
|
|
495
|
+
</Tooltip>
|
|
496
|
+
</Box>
|
|
497
|
+
}
|
|
498
|
+
/>
|
|
499
|
+
<FormControlLabel
|
|
500
|
+
data-testid="fasta-is-gzip-checkbox"
|
|
501
|
+
control={
|
|
502
|
+
<Checkbox
|
|
503
|
+
checked={isGzip}
|
|
504
|
+
onChange={() => {
|
|
505
|
+
if (sequenceIsEditable) {
|
|
506
|
+
setIsGzip(!isGzip)
|
|
507
|
+
} else {
|
|
508
|
+
setIsGzip(true)
|
|
509
|
+
}
|
|
510
|
+
}}
|
|
511
|
+
disabled={!sequenceIsEditable}
|
|
512
|
+
/>
|
|
513
|
+
}
|
|
514
|
+
label="FASTA is gzip compressed"
|
|
515
|
+
/>
|
|
516
|
+
|
|
517
|
+
{fileType === FileType.BGZIP_FASTA ||
|
|
518
|
+
fileType === FileType.GFF3 ? (
|
|
519
|
+
<Table size="small" sx={{ mt: 2 }}>
|
|
520
|
+
<TableBody>
|
|
521
|
+
<TableRow />
|
|
522
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
523
|
+
<Box display="flex" alignItems="center">
|
|
524
|
+
<span>FASTA</span>
|
|
525
|
+
<Tooltip title='Unless "Store sequence in database" enabled, FASTA input must be compressed with bgzip and indexed with samtools faidx (or equivalent). Compression is optional for sequences stored in the database.'>
|
|
526
|
+
<IconButton size="small">
|
|
527
|
+
<InfoIcon sx={{ fontSize: 18 }} />
|
|
528
|
+
</IconButton>
|
|
529
|
+
</Tooltip>
|
|
530
|
+
</Box>
|
|
531
|
+
</TableCell>
|
|
532
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
533
|
+
<input
|
|
534
|
+
data-testid="fasta-input-file"
|
|
535
|
+
type="file"
|
|
536
|
+
onChange={(
|
|
537
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
538
|
+
) => {
|
|
539
|
+
setFastaFile(e.target.files?.item(0) ?? null)
|
|
540
|
+
}}
|
|
541
|
+
disabled={submitted && !errorMessage}
|
|
542
|
+
/>
|
|
543
|
+
</TableCell>
|
|
544
|
+
|
|
545
|
+
<TableRow />
|
|
546
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
547
|
+
FASTA index (.fai)
|
|
548
|
+
</TableCell>
|
|
549
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
550
|
+
<input
|
|
551
|
+
data-testid="fai-input-file"
|
|
552
|
+
type="file"
|
|
553
|
+
onChange={(
|
|
554
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
555
|
+
) => {
|
|
556
|
+
setFastaIndexFile(e.target.files?.item(0) ?? null)
|
|
557
|
+
}}
|
|
558
|
+
disabled={
|
|
559
|
+
(submitted && !errorMessage) || sequenceIsEditable
|
|
560
|
+
}
|
|
561
|
+
/>
|
|
562
|
+
</TableCell>
|
|
563
|
+
|
|
564
|
+
<TableRow />
|
|
565
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
566
|
+
FASTA binary index (.gzi)
|
|
567
|
+
</TableCell>
|
|
568
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
569
|
+
<input
|
|
570
|
+
data-testid="gzi-input-file"
|
|
571
|
+
type="file"
|
|
572
|
+
onChange={(
|
|
573
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
574
|
+
) => {
|
|
575
|
+
setFastaGziIndexFile(
|
|
576
|
+
e.target.files?.item(0) ?? null,
|
|
577
|
+
)
|
|
578
|
+
}}
|
|
579
|
+
disabled={
|
|
580
|
+
(submitted && !errorMessage) || sequenceIsEditable
|
|
581
|
+
}
|
|
582
|
+
/>
|
|
583
|
+
</TableCell>
|
|
584
|
+
</TableBody>
|
|
585
|
+
</Table>
|
|
586
|
+
) : (
|
|
587
|
+
<Table size="small" sx={{ mt: 2 }}>
|
|
588
|
+
<TableBody>
|
|
589
|
+
<TableRow />
|
|
590
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
591
|
+
<Box display="flex" alignItems="center">
|
|
592
|
+
<span>FASTA</span>
|
|
593
|
+
<Tooltip title="Remote FASTA input must be compressed with bgzip and indexed with samtools faidx (or equivalent)">
|
|
594
|
+
<IconButton size="small">
|
|
595
|
+
<InfoIcon sx={{ fontSize: 18 }} />
|
|
596
|
+
</IconButton>
|
|
597
|
+
</Tooltip>
|
|
598
|
+
</Box>
|
|
599
|
+
</TableCell>
|
|
600
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
601
|
+
<TextField
|
|
602
|
+
data-testid="fasta-input-url"
|
|
603
|
+
variant="outlined"
|
|
604
|
+
value={fastaUrl}
|
|
605
|
+
error={!validFastaUrl}
|
|
606
|
+
onChange={(
|
|
607
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
608
|
+
) => {
|
|
609
|
+
setFastaUrl(e.target.value)
|
|
610
|
+
}}
|
|
611
|
+
disabled={submitted && !errorMessage}
|
|
612
|
+
slotProps={{
|
|
613
|
+
input: {
|
|
614
|
+
startAdornment: (
|
|
615
|
+
<InputAdornment position="start">
|
|
616
|
+
<LinkIcon />
|
|
617
|
+
</InputAdornment>
|
|
618
|
+
),
|
|
619
|
+
},
|
|
620
|
+
}}
|
|
621
|
+
/>
|
|
622
|
+
</TableCell>
|
|
623
|
+
|
|
624
|
+
<TableRow />
|
|
625
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
626
|
+
FASTA index (.fai)
|
|
627
|
+
</TableCell>
|
|
628
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
629
|
+
<TextField
|
|
630
|
+
data-testid="fai-input-url"
|
|
631
|
+
variant="outlined"
|
|
632
|
+
value={fastaIndexUrl}
|
|
633
|
+
error={!validFastaIndexUrl}
|
|
634
|
+
onChange={(
|
|
635
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
636
|
+
) => {
|
|
637
|
+
setFastaIndexUrl(e.target.value)
|
|
638
|
+
}}
|
|
639
|
+
disabled={submitted && !errorMessage}
|
|
640
|
+
slotProps={{
|
|
641
|
+
input: {
|
|
642
|
+
startAdornment: (
|
|
643
|
+
<InputAdornment position="start">
|
|
644
|
+
<LinkIcon />
|
|
645
|
+
</InputAdornment>
|
|
646
|
+
),
|
|
647
|
+
},
|
|
648
|
+
}}
|
|
649
|
+
/>
|
|
650
|
+
</TableCell>
|
|
651
|
+
|
|
652
|
+
<TableRow />
|
|
653
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
654
|
+
FASTA binary index (.gzi)
|
|
655
|
+
</TableCell>
|
|
656
|
+
<TableCell style={{ borderBottomWidth: 0 }}>
|
|
657
|
+
<TextField
|
|
658
|
+
data-testid="gzi-input-url"
|
|
659
|
+
variant="outlined"
|
|
660
|
+
value={fastaGziIndexUrl}
|
|
661
|
+
error={!validFastaGziIndexUrl}
|
|
662
|
+
onChange={(
|
|
663
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
664
|
+
) => {
|
|
665
|
+
setFastaGziIndexUrl(e.target.value)
|
|
666
|
+
}}
|
|
667
|
+
disabled={submitted && !errorMessage}
|
|
668
|
+
slotProps={{
|
|
669
|
+
input: {
|
|
670
|
+
startAdornment: (
|
|
671
|
+
<InputAdornment position="start">
|
|
672
|
+
<LinkIcon />
|
|
673
|
+
</InputAdornment>
|
|
674
|
+
),
|
|
675
|
+
},
|
|
676
|
+
}}
|
|
677
|
+
/>
|
|
678
|
+
</TableCell>
|
|
679
|
+
</TableBody>
|
|
680
|
+
</Table>
|
|
681
|
+
)}
|
|
443
682
|
</FormGroup>
|
|
444
|
-
</
|
|
445
|
-
|
|
683
|
+
</AccordionDetails>
|
|
684
|
+
</Accordion>
|
|
685
|
+
<Accordion
|
|
686
|
+
disableGutters
|
|
687
|
+
elevation={0}
|
|
688
|
+
square
|
|
689
|
+
className={classes.accordion}
|
|
690
|
+
expanded={expanded === 'panelGffInput'}
|
|
691
|
+
onChange={handleAccordionChange('panelGffInput')}
|
|
692
|
+
>
|
|
693
|
+
<AccordionSummary
|
|
694
|
+
className={classes.accordionSummary}
|
|
695
|
+
expandIcon={
|
|
696
|
+
expanded === 'panelGffInput' ? (
|
|
697
|
+
<RadioButtonCheckedIcon
|
|
698
|
+
className={classes.radioIcon}
|
|
699
|
+
sx={{ fontSize: '1.2rem', ml: 5 }}
|
|
700
|
+
/>
|
|
701
|
+
) : (
|
|
702
|
+
<RadioButtonUncheckedIcon
|
|
703
|
+
className={classes.radioIcon}
|
|
704
|
+
sx={{ fontSize: '1.2rem', mr: 5 }}
|
|
705
|
+
/>
|
|
706
|
+
)
|
|
707
|
+
}
|
|
708
|
+
aria-controls="panelGffInputd-content"
|
|
709
|
+
>
|
|
710
|
+
<Typography component="span">
|
|
711
|
+
GFF3 input
|
|
712
|
+
<Tooltip title="GFF3 must includes FASTA sequences. File can be gzip compressed.">
|
|
713
|
+
<InfoIcon
|
|
714
|
+
className={classes.radioIcon}
|
|
715
|
+
sx={{ fontSize: 18 }}
|
|
716
|
+
/>
|
|
717
|
+
</Tooltip>
|
|
718
|
+
</Typography>
|
|
719
|
+
</AccordionSummary>
|
|
720
|
+
<AccordionDetails className={classes.accordionDetails}>
|
|
721
|
+
<Box style={{ marginTop: 20 }}>
|
|
722
|
+
<input
|
|
723
|
+
data-testid="gff3-input-file"
|
|
724
|
+
type="file"
|
|
725
|
+
disabled={submitted && !errorMessage}
|
|
726
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
727
|
+
setFastaFile(e.target.files?.item(0) ?? null)
|
|
728
|
+
setFileType(FileType.GFF3)
|
|
729
|
+
}}
|
|
730
|
+
/>
|
|
731
|
+
<FormGroup style={{ display: 'grid' }}>
|
|
732
|
+
<FormControlLabel
|
|
733
|
+
control={
|
|
734
|
+
<Checkbox
|
|
735
|
+
checked={importFeatures}
|
|
736
|
+
onChange={() => {
|
|
737
|
+
setImportFeatures(!importFeatures)
|
|
738
|
+
}}
|
|
739
|
+
disabled={submitted && !errorMessage}
|
|
740
|
+
/>
|
|
741
|
+
}
|
|
742
|
+
label="Load features from GFF3 file"
|
|
743
|
+
/>
|
|
744
|
+
<FormControlLabel
|
|
745
|
+
data-testid="gff3-is-gzip-checkbox"
|
|
746
|
+
control={
|
|
747
|
+
<Checkbox
|
|
748
|
+
checked={isGzip}
|
|
749
|
+
onChange={() => {
|
|
750
|
+
setIsGzip(!isGzip)
|
|
751
|
+
}}
|
|
752
|
+
disabled={submitted && !errorMessage}
|
|
753
|
+
/>
|
|
754
|
+
}
|
|
755
|
+
label="GFF3 is gzip compressed"
|
|
756
|
+
/>
|
|
757
|
+
</FormGroup>
|
|
758
|
+
</Box>
|
|
759
|
+
</AccordionDetails>
|
|
760
|
+
</Accordion>
|
|
446
761
|
</DialogContent>
|
|
447
762
|
<DialogActions>
|
|
448
763
|
<Button
|
|
449
764
|
disabled={
|
|
450
|
-
!
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
765
|
+
!checkSumbission(
|
|
766
|
+
validAsm,
|
|
767
|
+
sequenceIsEditable,
|
|
768
|
+
fileType,
|
|
769
|
+
fastaFile,
|
|
770
|
+
fastaIndexFile,
|
|
771
|
+
fastaGziIndexFile,
|
|
772
|
+
validFastaUrl,
|
|
773
|
+
validFastaIndexUrl,
|
|
774
|
+
validFastaGziIndexUrl,
|
|
775
|
+
) || submitted
|
|
460
776
|
}
|
|
461
777
|
variant="contained"
|
|
462
778
|
type="submit"
|
|
779
|
+
data-testid="submit-button"
|
|
463
780
|
>
|
|
464
781
|
{submitted ? 'Submitting...' : 'Submit'}
|
|
465
782
|
</Button>
|