@apollo-annotation/jbrowse-plugin-apollo 0.3.8 → 0.3.9

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 (48) hide show
  1. package/dist/index.esm.js +10932 -10932
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +10845 -10846
  4. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  5. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  6. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  7. package/dist/jbrowse-plugin-apollo.umd.development.js +18619 -21342
  8. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  9. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  10. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  11. package/package.json +7 -7
  12. package/src/ApolloInternetAccount/model.ts +81 -63
  13. package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +4 -4
  14. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +9 -7
  15. package/src/BackendDrivers/CollaborationServerDriver.ts +49 -18
  16. package/src/BackendDrivers/DesktopFileDriver.ts +2 -2
  17. package/src/ChangeManager.ts +3 -1
  18. package/src/FeatureDetailsWidget/BasicInformation.tsx +6 -4
  19. package/src/FeatureDetailsWidget/NumberTextField.tsx +5 -2
  20. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +39 -203
  21. package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +92 -0
  22. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +6 -102
  23. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +31 -230
  24. package/src/LinearApolloDisplay/glyphs/util.ts +19 -0
  25. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceOverlay.ts +181 -0
  26. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceTrack.ts +218 -0
  27. package/src/LinearApolloReferenceSequenceDisplay/stateModel/rendering.ts +62 -386
  28. package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +6 -0
  29. package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +122 -70
  30. package/src/components/AddAssembly.tsx +33 -37
  31. package/src/components/AddFeature.tsx +21 -18
  32. package/src/components/AddRefSeqAliases.tsx +56 -42
  33. package/src/components/CopyFeature.tsx +1 -1
  34. package/src/components/CreateApolloAnnotation.tsx +22 -10
  35. package/src/components/DeleteAssembly.tsx +2 -9
  36. package/src/components/DownloadGFF3.tsx +2 -2
  37. package/src/components/ManageChecks.tsx +2 -9
  38. package/src/components/ManageUsers.tsx +23 -22
  39. package/src/components/OntologyTermAutocomplete.tsx +1 -8
  40. package/src/components/ViewChangeLog.tsx +25 -50
  41. package/src/components/ViewCheckResults.tsx +1 -7
  42. package/src/config.ts +3 -3
  43. package/src/index.ts +17 -16
  44. package/src/makeDisplayComponent.tsx +9 -13
  45. package/src/session/ClientDataStore.ts +32 -14
  46. package/src/session/session.ts +19 -27
  47. package/src/util/glyphUtils.ts +178 -1
  48. package/src/util/loadAssemblyIntoClient.ts +3 -2
@@ -149,66 +149,18 @@ export const TranscriptWidgetEditLocation = observer(
149
149
  cdsMax = sortedCDSLocations[sortedCDSLocations.length - 1].max
150
150
  }
151
151
 
152
- function handleCDSLocationChange(
152
+ const updateCDSLocation = (
153
153
  oldLocation: number,
154
154
  newLocation: number,
155
155
  feature: AnnotationFeature,
156
156
  isMin: boolean,
157
- ) {
157
+ onComplete?: () => void,
158
+ ): boolean => {
158
159
  if (!feature.children) {
159
160
  throw new Error('Transcript should have child features')
160
161
  }
161
-
162
- const overlappingExon = getOverlappingExonForCDS(
163
- feature,
164
- featureTypeOntology,
165
- oldLocation,
166
- isMin,
167
- )
168
- if (!overlappingExon) {
169
- notify('No matching exon found', 'error')
170
- return
171
- }
172
- const oldExonLocation = isMin ? overlappingExon.min : overlappingExon.max
173
- const { prevExon, nextExon } = getNeighboringExonParts(
174
- feature,
175
- featureTypeOntology,
176
- oldExonLocation,
177
- isMin,
178
- )
179
-
180
- // Start location should be less than end location
181
- if (isMin && newLocation >= overlappingExon.max) {
182
- notify(
183
- 'Start location should be less than overlapping exon end location',
184
- 'error',
185
- )
186
- return
187
- }
188
-
189
- // End location should be greater than start location
190
- if (!isMin && newLocation <= overlappingExon.min) {
191
- notify(
192
- 'End location should be greater than overlapping exon start location',
193
- 'error',
194
- )
195
- return
196
- }
197
- // Changed location should be greater than end location of previous exon - give 2bp buffer
198
- if (prevExon && prevExon.max + 2 > newLocation) {
199
- notify(
200
- 'Start location should be greater than previous exon end location',
201
- 'error',
202
- )
203
- return
204
- }
205
- // Changed location should be less than start location of next exon
206
- if (nextExon && nextExon.min - 2 < newLocation) {
207
- notify(
208
- 'End location should be less than next exon start location',
209
- 'error',
210
- )
211
- return
162
+ if (oldLocation === newLocation) {
163
+ return true
212
164
  }
213
165
 
214
166
  const cdsFeature = getMatchingCDSFeature(
@@ -217,10 +169,14 @@ export const TranscriptWidgetEditLocation = observer(
217
169
  oldLocation,
218
170
  isMin,
219
171
  )
220
-
221
172
  if (!cdsFeature) {
222
173
  notify('No matching CDS feature found', 'error')
223
- return
174
+ return false
175
+ }
176
+
177
+ if (isMin && newLocation >= cdsFeature.max) {
178
+ notify('Start location should be less than CDS end location', 'error')
179
+ return false
224
180
  }
225
181
 
226
182
  if (!isMin && newLocation <= cdsFeature.min) {
@@ -228,145 +184,23 @@ export const TranscriptWidgetEditLocation = observer(
228
184
  'End location should be greater than CDS start location',
229
185
  'error',
230
186
  )
231
- return
232
- }
233
- if (isMin && newLocation >= cdsFeature.max) {
234
- notify('Start location should be less than CDS end location', 'error')
235
- return
236
- }
237
-
238
- const overlappingExonFeature = getExonFeature(
239
- feature,
240
- overlappingExon.min,
241
- overlappingExon.max,
242
- featureTypeOntology,
243
- )
244
-
245
- if (!overlappingExonFeature) {
246
- notify('No matching exon feature found', 'error')
247
- return
187
+ return false
248
188
  }
249
189
 
250
- if (isMin && newLocation !== cdsFeature.min) {
251
- const startChange: LocationStartChange = new LocationStartChange({
252
- typeName: 'LocationStartChange',
253
- changedIds: [],
254
- changes: [],
255
- assembly,
256
- })
257
-
258
- if (newLocation < overlappingExon.min) {
259
- if (prevExon) {
260
- // update exon start location
261
- appendStartLocationChange(
262
- overlappingExonFeature,
263
- startChange,
264
- newLocation,
265
- )
266
- // update CDS start location
267
- appendStartLocationChange(cdsFeature, startChange, newLocation)
268
- } else {
269
- const transcriptStart = feature.min
270
- const gene = feature.parent
271
- if (newLocation < transcriptStart) {
272
- if (gene && newLocation < gene.min) {
273
- // update gene start location
274
- appendStartLocationChange(gene, startChange, newLocation)
275
- }
276
- // update transcript start location
277
- appendStartLocationChange(feature, startChange, newLocation)
278
- // update exon start location
279
- appendStartLocationChange(
280
- overlappingExonFeature,
281
- startChange,
282
- newLocation,
283
- )
284
- // update CDS start location
285
- appendStartLocationChange(cdsFeature, startChange, newLocation)
286
- }
287
- }
288
- } else {
289
- // update CDS start location
290
- appendStartLocationChange(cdsFeature, startChange, newLocation)
291
- }
292
-
293
- void changeManager.submit(startChange).catch(() => {
294
- notify('Error updating feature CDS start position', 'error')
295
- })
296
- }
297
-
298
- if (!isMin && newLocation !== cdsFeature.max) {
299
- const endChange: LocationEndChange = new LocationEndChange({
300
- typeName: 'LocationEndChange',
301
- changedIds: [],
302
- changes: [],
303
- assembly,
304
- })
305
-
306
- if (newLocation > overlappingExon.max) {
307
- if (nextExon) {
308
- // update exon end location
309
- appendEndLocationChange(
310
- overlappingExonFeature,
311
- endChange,
312
- newLocation,
313
- )
314
- // update CDS end location
315
- appendEndLocationChange(cdsFeature, endChange, newLocation)
316
- } else {
317
- const transcriptEnd = feature.max
318
- const gene = feature.parent
319
- if (newLocation > transcriptEnd) {
320
- if (gene && newLocation > gene.max) {
321
- // update gene end location
322
- appendEndLocationChange(gene, endChange, newLocation)
323
- }
324
- // update transcript end location
325
- appendEndLocationChange(feature, endChange, newLocation)
326
- // update exon end location
327
- appendEndLocationChange(
328
- overlappingExonFeature,
329
- endChange,
330
- newLocation,
331
- )
332
- // update CDS end location
333
- appendEndLocationChange(cdsFeature, endChange, newLocation)
334
- }
335
- }
336
- } else {
337
- // update CDS end location
338
- appendEndLocationChange(cdsFeature, endChange, newLocation)
339
- }
340
-
341
- void changeManager.submit(endChange).catch(() => {
342
- notify('Error updating feature CDS end position', 'error')
343
- })
344
- }
345
- }
346
-
347
- const updateCDSLocation = (
348
- oldLocation: number,
349
- newLocation: number,
350
- feature: AnnotationFeature,
351
- isMin: boolean,
352
- onComplete?: () => void,
353
- ) => {
354
- if (!feature.children) {
355
- throw new Error('Transcript should have child features')
356
- }
357
- if (oldLocation === newLocation) {
358
- return
359
- }
360
-
361
- const cdsFeature = getMatchingCDSFeature(
190
+ // overlapping exon of new CDS location
191
+ const overlappingExon = getOverlappingExonForCDS(
362
192
  feature,
363
193
  featureTypeOntology,
364
- oldLocation,
194
+ newLocation,
365
195
  isMin,
366
196
  )
367
- if (!cdsFeature) {
368
- notify('No matching CDS feature found', 'error')
369
- return
197
+
198
+ if (!overlappingExon) {
199
+ notify(
200
+ 'There should be an overlapping exon for the new CDS location',
201
+ 'error',
202
+ )
203
+ return false
370
204
  }
371
205
 
372
206
  const change = isMin
@@ -397,6 +231,7 @@ export const TranscriptWidgetEditLocation = observer(
397
231
  .catch(() => {
398
232
  notify('Error updating feature CDS position', 'error')
399
233
  })
234
+ return true
400
235
  }
401
236
 
402
237
  function handleExonLocationChange(
@@ -404,7 +239,7 @@ export const TranscriptWidgetEditLocation = observer(
404
239
  newLocation: number,
405
240
  feature: AnnotationFeature,
406
241
  isMin: boolean,
407
- ) {
242
+ ): boolean {
408
243
  if (!feature.children) {
409
244
  throw new Error('Transcript should have child features')
410
245
  }
@@ -417,28 +252,28 @@ export const TranscriptWidgetEditLocation = observer(
417
252
 
418
253
  if (!matchingExon) {
419
254
  notify('No matching exon found', 'error')
420
- return
255
+ return false
421
256
  }
422
257
 
423
258
  // Start location should be less than end location
424
259
  if (isMin && newLocation >= matchingExon.max) {
425
260
  notify(`Start location should be less than end location`, 'error')
426
- return
261
+ return false
427
262
  }
428
263
  // End location should be greater than start location
429
264
  if (!isMin && newLocation <= matchingExon.min) {
430
265
  notify(`End location should be greater than start location`, 'error')
431
- return
266
+ return false
432
267
  }
433
268
  // Changed location should be greater than end location of previous exon - give 2bp buffer
434
269
  if (prevExon && prevExon.max + 2 > newLocation) {
435
270
  notify(`Error while changing start location`, 'error')
436
- return
271
+ return false
437
272
  }
438
273
  // Changed location should be less than start location of next exon - give 2bp buffer
439
274
  if (nextExon && nextExon.min - 2 < newLocation) {
440
275
  notify(`Error while changing end location`, 'error')
441
- return
276
+ return false
442
277
  }
443
278
 
444
279
  const exonFeature = getExonFeature(
@@ -449,7 +284,7 @@ export const TranscriptWidgetEditLocation = observer(
449
284
  )
450
285
  if (!exonFeature) {
451
286
  notify('No matching exon feature found', 'error')
452
- return
287
+ return false
453
288
  }
454
289
 
455
290
  const cdsFeature = getFirstCDSFeature(feature, featureTypeOntology)
@@ -577,6 +412,7 @@ export const TranscriptWidgetEditLocation = observer(
577
412
  notify('Error updating feature exon end position', 'error')
578
413
  })
579
414
  }
415
+ return true
580
416
  }
581
417
 
582
418
  const appendEndLocationChange = (
@@ -1170,7 +1006,7 @@ export const TranscriptWidgetEditLocation = observer(
1170
1006
  variant="outlined"
1171
1007
  value={cdsMin + 1}
1172
1008
  onChangeCommitted={(newLocation: number) => {
1173
- handleCDSLocationChange(
1009
+ return updateCDSLocation(
1174
1010
  cdsMin,
1175
1011
  newLocation - 1,
1176
1012
  feature,
@@ -1187,7 +1023,7 @@ export const TranscriptWidgetEditLocation = observer(
1187
1023
  variant="outlined"
1188
1024
  value={cdsMax}
1189
1025
  onChangeCommitted={(newLocation: number) => {
1190
- handleCDSLocationChange(
1026
+ return updateCDSLocation(
1191
1027
  cdsMax,
1192
1028
  newLocation,
1193
1029
  feature,
@@ -1208,7 +1044,7 @@ export const TranscriptWidgetEditLocation = observer(
1208
1044
  variant="outlined"
1209
1045
  value={cdsMax}
1210
1046
  onChangeCommitted={(newLocation: number) => {
1211
- handleCDSLocationChange(
1047
+ return updateCDSLocation(
1212
1048
  cdsMax,
1213
1049
  newLocation,
1214
1050
  feature,
@@ -1225,7 +1061,7 @@ export const TranscriptWidgetEditLocation = observer(
1225
1061
  variant="outlined"
1226
1062
  value={cdsMin + 1}
1227
1063
  onChangeCommitted={(newLocation: number) => {
1228
- handleCDSLocationChange(
1064
+ return updateCDSLocation(
1229
1065
  cdsMin,
1230
1066
  newLocation - 1,
1231
1067
  feature,
@@ -1270,7 +1106,7 @@ export const TranscriptWidgetEditLocation = observer(
1270
1106
  variant="outlined"
1271
1107
  value={loc.min + 1}
1272
1108
  onChangeCommitted={(newLocation: number) => {
1273
- handleExonLocationChange(
1109
+ return handleExonLocationChange(
1274
1110
  loc.min,
1275
1111
  newLocation - 1,
1276
1112
  feature,
@@ -1286,7 +1122,7 @@ export const TranscriptWidgetEditLocation = observer(
1286
1122
  variant="outlined"
1287
1123
  value={loc.max}
1288
1124
  onChangeCommitted={(newLocation: number) => {
1289
- handleExonLocationChange(
1125
+ return handleExonLocationChange(
1290
1126
  loc.max,
1291
1127
  newLocation,
1292
1128
  feature,
@@ -1306,7 +1142,7 @@ export const TranscriptWidgetEditLocation = observer(
1306
1142
  variant="outlined"
1307
1143
  value={loc.max}
1308
1144
  onChangeCommitted={(newLocation: number) => {
1309
- handleExonLocationChange(
1145
+ return handleExonLocationChange(
1310
1146
  loc.max,
1311
1147
  newLocation,
1312
1148
  feature,
@@ -1322,7 +1158,7 @@ export const TranscriptWidgetEditLocation = observer(
1322
1158
  variant="outlined"
1323
1159
  value={loc.min + 1}
1324
1160
  onChangeCommitted={(newLocation: number) => {
1325
- handleExonLocationChange(
1161
+ return handleExonLocationChange(
1326
1162
  loc.min,
1327
1163
  newLocation - 1,
1328
1164
  feature,
@@ -0,0 +1,92 @@
1
+ import { type CheckResultI } from '@apollo-annotation/mst'
2
+ import { type AbstractSessionModel, doesIntersect2 } from '@jbrowse/core/util'
3
+ import ErrorIcon from '@mui/icons-material/Error'
4
+ import { Avatar, Badge, Box, Tooltip } from '@mui/material'
5
+ import { observer } from 'mobx-react'
6
+ import React from 'react'
7
+
8
+ import { clusterResultByMessage, useStyles } from '../../util/displayUtils'
9
+ import { getLeftPx } from '../glyphs/util'
10
+ import { type LinearApolloDisplay as LinearApolloDisplayI } from '../stateModel'
11
+
12
+ export const CheckResultWarnings = observer(function CheckResultWarnings({
13
+ display,
14
+ }: {
15
+ display: LinearApolloDisplayI
16
+ }) {
17
+ const { classes } = useStyles()
18
+ const { apolloDragging, apolloRowHeight, lgv, session, showCheckResults } =
19
+ display
20
+ const { assemblyManager } = session as unknown as AbstractSessionModel
21
+ if (!showCheckResults) {
22
+ return null
23
+ }
24
+ return lgv.dynamicBlocks.contentBlocks.map((block) => {
25
+ const widthBp = lgv.bpPerPx * apolloRowHeight
26
+ const assembly = assemblyManager.get(block.assemblyName)
27
+ if (!assembly) {
28
+ return null
29
+ }
30
+ const filteredCheckResults = [
31
+ ...session.apolloDataStore.checkResults.values(),
32
+ ].filter(
33
+ (checkResult) =>
34
+ assembly.isValidRefName(checkResult.refSeq) &&
35
+ assembly.getCanonicalRefName(checkResult.refSeq) === block.refName &&
36
+ doesIntersect2(
37
+ block.start,
38
+ block.end,
39
+ checkResult.start,
40
+ checkResult.end,
41
+ ),
42
+ )
43
+ const checkResults = clusterResultByMessage<CheckResultI>(
44
+ filteredCheckResults,
45
+ widthBp,
46
+ true,
47
+ )
48
+ return checkResults.map((checkResult) => {
49
+ const left = Math.round(getLeftPx(display, checkResult.range, block))
50
+
51
+ const [feature] = checkResult.featureIds
52
+ if (!feature) {
53
+ return null
54
+ }
55
+ let row = 0
56
+ const featureLayout = display.getFeatureLayoutPosition(feature)
57
+ if (featureLayout) {
58
+ row = featureLayout.layoutRow + featureLayout.featureRow
59
+ }
60
+ const top = row * apolloRowHeight
61
+ const height = apolloRowHeight
62
+ return (
63
+ <Tooltip key={checkResult._id} title={checkResult.message}>
64
+ <Box
65
+ className={classes.box}
66
+ style={{
67
+ top,
68
+ left,
69
+ height,
70
+ width: height,
71
+ pointerEvents: apolloDragging ? 'none' : 'auto',
72
+ }}
73
+ >
74
+ <Badge
75
+ className={classes.badge}
76
+ badgeContent={checkResult.count}
77
+ color="primary"
78
+ overlap="circular"
79
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
80
+ invisible={checkResult.count <= 1}
81
+ >
82
+ <Avatar className={classes.avatar}>
83
+ <ErrorIcon data-testid={`ErrorIcon-${checkResult.start}`} />
84
+ </Avatar>
85
+ </Badge>
86
+ </Box>
87
+ </Tooltip>
88
+ )
89
+ })
90
+ return null
91
+ })
92
+ })
@@ -1,35 +1,19 @@
1
1
  /* eslint-disable @typescript-eslint/unbound-method */
2
2
  /* eslint-disable @typescript-eslint/no-misused-promises */
3
3
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
4
- import { type CheckResultI } from '@apollo-annotation/mst'
5
4
  import { Menu, type MenuItem } from '@jbrowse/core/ui'
6
- import {
7
- type AbstractSessionModel,
8
- doesIntersect2,
9
- getContainingView,
10
- } from '@jbrowse/core/util'
5
+ import { getContainingView } from '@jbrowse/core/util'
11
6
  import { type LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
12
- import ErrorIcon from '@mui/icons-material/Error'
13
7
  import LockIcon from '@mui/icons-material/Lock'
14
- import {
15
- Alert,
16
- Avatar,
17
- Badge,
18
- Box,
19
- CircularProgress,
20
- Tooltip,
21
- useTheme,
22
- } from '@mui/material'
8
+ import { Alert, CircularProgress, Tooltip, useTheme } from '@mui/material'
23
9
  import { observer } from 'mobx-react'
24
10
  import React, { useEffect, useState } from 'react'
25
11
 
26
- import {
27
- type Coord,
28
- clusterResultByMessage,
29
- useStyles,
30
- } from '../../util/displayUtils'
12
+ import { type Coord, useStyles } from '../../util/displayUtils'
31
13
  import { type LinearApolloDisplay as LinearApolloDisplayI } from '../stateModel'
32
14
 
15
+ import { CheckResultWarnings } from './CheckResultWarnings'
16
+
33
17
  interface LinearApolloDisplayProps {
34
18
  model: LinearApolloDisplayI
35
19
  }
@@ -43,8 +27,6 @@ export const LinearApolloDisplay = observer(function LinearApolloDisplay(
43
27
  const { model } = props
44
28
  const {
45
29
  loading,
46
- apolloDragging,
47
- apolloRowHeight,
48
30
  contextMenuItems: getContextMenuItems,
49
31
  cursor,
50
32
  featuresHeight,
@@ -59,7 +41,6 @@ export const LinearApolloDisplay = observer(function LinearApolloDisplay(
59
41
  setCollaboratorCanvas,
60
42
  setOverlayCanvas,
61
43
  setTheme,
62
- showCheckResults,
63
44
  } = model
64
45
  const { classes } = useStyles()
65
46
  const lgv = getContainingView(model) as unknown as LinearGenomeViewModel
@@ -73,7 +54,6 @@ export const LinearApolloDisplay = observer(function LinearApolloDisplay(
73
54
  if (!isShown) {
74
55
  return null
75
56
  }
76
- const { assemblyManager } = session as unknown as AbstractSessionModel
77
57
  return (
78
58
  <>
79
59
  <div
@@ -153,83 +133,7 @@ export const LinearApolloDisplay = observer(function LinearApolloDisplay(
153
133
  style={{ cursor: cursor ?? 'default' }}
154
134
  data-testid="overlayCanvas"
155
135
  />
156
- {lgv.displayedRegions.flatMap((region, idx) => {
157
- const widthBp = lgv.bpPerPx * apolloRowHeight
158
- const assembly = assemblyManager.get(region.assemblyName)
159
- if (showCheckResults) {
160
- const filteredCheckResults = [
161
- ...session.apolloDataStore.checkResults.values(),
162
- ].filter(
163
- (checkResult) =>
164
- assembly?.isValidRefName(checkResult.refSeq) &&
165
- assembly.getCanonicalRefName(checkResult.refSeq) ===
166
- region.refName &&
167
- doesIntersect2(
168
- region.start,
169
- region.end,
170
- checkResult.start,
171
- checkResult.end,
172
- ),
173
- )
174
- const checkResults = clusterResultByMessage<CheckResultI>(
175
- filteredCheckResults,
176
- widthBp,
177
- true,
178
- )
179
- return checkResults.map((checkResult) => {
180
- const left =
181
- (lgv.bpToPx({
182
- refName: region.refName,
183
- coord: checkResult.start,
184
- regionNumber: idx,
185
- })?.offsetPx ?? 0) - lgv.offsetPx
186
- const [feature] = checkResult.featureIds
187
- if (!feature) {
188
- return null
189
- }
190
- let row = 0
191
- const featureLayout = model.getFeatureLayoutPosition(feature)
192
- if (featureLayout) {
193
- row = featureLayout.layoutRow + featureLayout.featureRow
194
- }
195
- const top = row * apolloRowHeight
196
- const height = apolloRowHeight
197
- return (
198
- <Tooltip key={checkResult._id} title={checkResult.message}>
199
- <Box
200
- className={classes.box}
201
- style={{
202
- top,
203
- left,
204
- height,
205
- width: height,
206
- pointerEvents: apolloDragging ? 'none' : 'auto',
207
- }}
208
- >
209
- <Badge
210
- className={classes.badge}
211
- badgeContent={checkResult.count}
212
- color="primary"
213
- overlap="circular"
214
- anchorOrigin={{
215
- vertical: 'bottom',
216
- horizontal: 'right',
217
- }}
218
- invisible={checkResult.count <= 1}
219
- >
220
- <Avatar className={classes.avatar}>
221
- <ErrorIcon
222
- data-testid={`ErrorIcon-${checkResult.start}`}
223
- />
224
- </Avatar>
225
- </Badge>
226
- </Box>
227
- </Tooltip>
228
- )
229
- })
230
- }
231
- return null
232
- })}
136
+ <CheckResultWarnings display={model} />
233
137
  <Menu
234
138
  open={contextMenuItems.length > 0}
235
139
  onMenuItemClick={(_, callback) => {