protvista-uniprot 2.13.2 → 3.0.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.
Files changed (124) hide show
  1. package/dist/es/__spec__/filter-config.spec.d.ts +1 -0
  2. package/dist/es/__spec__/filter-config.spec.js +123 -0
  3. package/dist/es/__spec__/filter-config.spec.js.map +1 -0
  4. package/dist/es/adapters/__tests__/__mocks__/uniprotkb-entry-data.d.ts +47 -0
  5. package/dist/es/adapters/__tests__/__mocks__/uniprotkb-entry-data.js +75 -0
  6. package/dist/es/adapters/__tests__/__mocks__/uniprotkb-entry-data.js.map +1 -0
  7. package/dist/es/adapters/__tests__/structure-adapter.spec.d.ts +1 -0
  8. package/dist/es/adapters/__tests__/structure-adapter.spec.js +14 -0
  9. package/dist/es/adapters/__tests__/structure-adapter.spec.js.map +1 -0
  10. package/dist/es/adapters/alphafold-confidence-adapter.d.ts +8 -0
  11. package/dist/es/{protvista-alphafold-confidence.js → adapters/alphafold-confidence-adapter.js} +4 -3
  12. package/dist/es/adapters/alphafold-confidence-adapter.js.map +1 -0
  13. package/dist/es/adapters/alphamissense-heatmap-adapter.d.ts +8 -0
  14. package/dist/es/{protvista-alphamissense-heatmap.js → adapters/alphamissense-heatmap-adapter.js} +4 -3
  15. package/dist/es/adapters/alphamissense-heatmap-adapter.js.map +1 -0
  16. package/dist/es/adapters/alphamissense-pathogenicity-adapter.d.ts +10 -0
  17. package/dist/es/{protvista-alphamissense-pathogenicity.js → adapters/alphamissense-pathogenicity-adapter.js} +3 -2
  18. package/dist/es/adapters/alphamissense-pathogenicity-adapter.js.map +1 -0
  19. package/dist/es/adapters/config/evidence.d.ts +14 -0
  20. package/dist/es/adapters/config/evidence.js +101 -0
  21. package/dist/es/adapters/config/evidence.js.map +1 -0
  22. package/dist/es/adapters/feature-adapter.d.ts +2 -0
  23. package/dist/es/adapters/feature-adapter.js +18 -0
  24. package/dist/es/adapters/feature-adapter.js.map +1 -0
  25. package/dist/es/adapters/interpro-adapter.d.ts +2 -0
  26. package/dist/es/adapters/interpro-adapter.js +60 -0
  27. package/dist/es/adapters/interpro-adapter.js.map +1 -0
  28. package/dist/es/adapters/proteomics-adapter.d.ts +2 -0
  29. package/dist/es/adapters/proteomics-adapter.js +66 -0
  30. package/dist/es/adapters/proteomics-adapter.js.map +1 -0
  31. package/dist/es/{protvista-ptm-exchange.d.ts → adapters/ptm-exchange-adapter.d.ts} +2 -2
  32. package/dist/es/{protvista-ptm-exchange.js → adapters/ptm-exchange-adapter.js} +3 -2
  33. package/dist/es/adapters/ptm-exchange-adapter.js.map +1 -0
  34. package/dist/es/adapters/structure-adapter.d.ts +4 -0
  35. package/dist/es/adapters/structure-adapter.js +89 -0
  36. package/dist/es/adapters/structure-adapter.js.map +1 -0
  37. package/dist/es/adapters/types/alphafold.js +2 -0
  38. package/dist/es/adapters/types/alphafold.js.map +1 -0
  39. package/dist/es/adapters/variation-adapter.d.ts +7 -0
  40. package/dist/es/adapters/variation-adapter.js +31 -0
  41. package/dist/es/adapters/variation-adapter.js.map +1 -0
  42. package/dist/es/{protvista-variation-graph-adapter.d.ts → adapters/variation-graph-adapter.d.ts} +2 -1
  43. package/dist/es/{protvista-variation-graph-adapter.js → adapters/variation-graph-adapter.js} +12 -5
  44. package/dist/es/adapters/variation-graph-adapter.js.map +1 -0
  45. package/dist/es/config.json +54 -54
  46. package/dist/es/filter-config.d.ts +24 -0
  47. package/dist/es/{filterConfig.js → filter-config.js} +1 -1
  48. package/dist/es/filter-config.js.map +1 -0
  49. package/dist/es/index.d.ts +6 -11
  50. package/dist/es/index.js +6 -16
  51. package/dist/es/index.js.map +1 -1
  52. package/dist/es/protvista-uniprot-structure.js +15 -9
  53. package/dist/es/protvista-uniprot-structure.js.map +1 -1
  54. package/dist/es/protvista-uniprot.d.ts +1 -100
  55. package/dist/es/protvista-uniprot.js +46 -60
  56. package/dist/es/protvista-uniprot.js.map +1 -1
  57. package/dist/es/styles/protvista-styles.js +2 -1
  58. package/dist/es/styles/protvista-styles.js.map +1 -1
  59. package/dist/es/tooltips/featureTooltip.d.ts +4 -0
  60. package/dist/es/tooltips/featureTooltip.js +160 -0
  61. package/dist/es/tooltips/featureTooltip.js.map +1 -0
  62. package/dist/es/tooltips/structureTooltip.d.ts +2 -0
  63. package/dist/es/tooltips/structureTooltip.js +15 -0
  64. package/dist/es/tooltips/structureTooltip.js.map +1 -0
  65. package/dist/es/tooltips/variationTooltip.d.ts +3 -0
  66. package/dist/es/tooltips/variationTooltip.js +82 -0
  67. package/dist/es/tooltips/variationTooltip.js.map +1 -0
  68. package/dist/es/utils.d.ts +3 -0
  69. package/dist/es/utils.js +28 -0
  70. package/dist/es/utils.js.map +1 -0
  71. package/dist/protvista-uniprot.js +24 -13
  72. package/dist/protvista-uniprot.js.map +1 -1
  73. package/package.json +25 -30
  74. package/src/__spec__/filter-config.spec.ts +143 -0
  75. package/src/adapters/__tests__/__mocks__/uniprotkb-entry-data.ts +76 -0
  76. package/src/adapters/__tests__/__snapshots__/structure-adapter.spec.ts.snap +157 -0
  77. package/src/adapters/__tests__/structure-adapter.spec.ts +19 -0
  78. package/src/{protvista-alphafold-confidence.ts → adapters/alphafold-confidence-adapter.ts} +5 -3
  79. package/src/{protvista-alphamissense-heatmap.ts → adapters/alphamissense-heatmap-adapter.ts} +8 -4
  80. package/src/{protvista-alphamissense-pathogenicity.ts → adapters/alphamissense-pathogenicity-adapter.ts} +4 -2
  81. package/src/adapters/config/evidence.ts +105 -0
  82. package/src/adapters/feature-adapter.ts +19 -0
  83. package/src/adapters/interpro-adapter.ts +71 -0
  84. package/src/adapters/proteomics-adapter.ts +76 -0
  85. package/src/{protvista-ptm-exchange.ts → adapters/ptm-exchange-adapter.ts} +3 -1
  86. package/src/adapters/structure-adapter.ts +105 -0
  87. package/src/adapters/variation-adapter.ts +44 -0
  88. package/src/{protvista-variation-graph-adapter.ts → adapters/variation-graph-adapter.ts} +25 -18
  89. package/src/config.json +54 -54
  90. package/src/{filterConfig.ts → filter-config.ts} +17 -12
  91. package/src/index.ts +14 -18
  92. package/src/protvista-uniprot-structure.ts +6 -14
  93. package/src/protvista-uniprot.ts +66 -96
  94. package/src/styles/protvista-styles.ts +2 -1
  95. package/src/tooltips/featureTooltip.ts +223 -0
  96. package/src/tooltips/structureTooltip.ts +20 -0
  97. package/src/tooltips/variationTooltip.ts +139 -0
  98. package/src/utils.ts +37 -0
  99. package/dist/es/commonTypes.js +0 -2
  100. package/dist/es/commonTypes.js.map +0 -1
  101. package/dist/es/filterConfig.d.ts +0 -20
  102. package/dist/es/filterConfig.js.map +0 -1
  103. package/dist/es/loadComponents.d.ts +0 -2
  104. package/dist/es/loadComponents.js +0 -7
  105. package/dist/es/loadComponents.js.map +0 -1
  106. package/dist/es/protvista-alphafold-confidence.d.ts +0 -9
  107. package/dist/es/protvista-alphafold-confidence.js.map +0 -1
  108. package/dist/es/protvista-alphamissense-heatmap.d.ts +0 -8
  109. package/dist/es/protvista-alphamissense-heatmap.js.map +0 -1
  110. package/dist/es/protvista-alphamissense-pathogenicity.d.ts +0 -10
  111. package/dist/es/protvista-alphamissense-pathogenicity.js.map +0 -1
  112. package/dist/es/protvista-ptm-exchange.js.map +0 -1
  113. package/dist/es/protvista-variation-graph-adapter.js.map +0 -1
  114. package/src/loadComponents.ts +0 -10
  115. package/src/types/nightingale-components.d.ts +0 -13
  116. package/src/types/nightingale-sequence-heatmap.d.ts +0 -1
  117. package/src/types/protvista-feature-adapter.d.ts +0 -1
  118. package/src/types/protvista-interpro-adapter.d.ts +0 -1
  119. package/src/types/protvista-proteomics-adapter.d.ts +0 -1
  120. package/src/types/protvista-structure-adapter.d.ts +0 -1
  121. package/src/types/protvista-structure.d.ts +0 -1
  122. package/src/types/protvista-variation.d.ts +0 -15
  123. /package/dist/es/{commonTypes.d.ts → adapters/types/alphafold.d.ts} +0 -0
  124. /package/src/{commonTypes.ts → adapters/types/alphafold.ts} +0 -0
@@ -1,4 +1,6 @@
1
1
  import { LitElement, html, svg } from 'lit';
2
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3
+ import { customElement } from 'lit/decorators.js';
2
4
  import { unsafeHTML } from 'lit/directives/unsafe-html.js';
3
5
  import { frame } from 'timing-functions';
4
6
 
@@ -14,68 +16,42 @@ import NightingaleLinegraphTrack from '@nightingale-elements/nightingale-linegra
14
16
  import NightingaleSequenceHeatmap from '@nightingale-elements/nightingale-sequence-heatmap';
15
17
  import NightingaleFilter from '@nightingale-elements/nightingale-filter';
16
18
 
17
- import { load } from 'data-loader';
18
19
  // adapters
19
- import { transformData as _transformDataFeatureAdapter } from 'protvista-feature-adapter';
20
- import { transformData as _transformDataProteomicsAdapter } from 'protvista-proteomics-adapter';
21
- import { transformData as _transformDataStructureAdapter } from 'protvista-structure-adapter';
22
- import {
23
- transformData as _transformDataVariationAdapter,
20
+ import featureAdapter from './adapters/feature-adapter';
21
+ import proteomicsAdapter from './adapters/proteomics-adapter';
22
+ import structureAdapter from './adapters/structure-adapter';
23
+ import variationAdapter, {
24
24
  TransformedVariant,
25
- } from 'protvista-variation-adapter';
26
- import { transformData as _transformDataVariationGraphAdapter } from './protvista-variation-graph-adapter';
27
- import { transformData as _transformDataInterproAdapter } from 'protvista-interpro-adapter';
28
- import { transformData as _transformDataProteomicsPTMApdapter } from './protvista-ptm-exchange';
29
- import { transformData as _transformDataAlphaFoldConfidenceAdapter } from './protvista-alphafold-confidence';
30
- import { transformData as _transformDataAlphaMissensePathogenicityAdapter } from './protvista-alphamissense-pathogenicity';
31
- import { transformData as _transformDataAlphaMissenseHeatmapAdapter } from './protvista-alphamissense-heatmap';
25
+ } from './adapters/variation-adapter';
26
+ import interproAdapter from './adapters/interpro-adapter';
27
+ import variationGraphAdapter from './adapters/variation-graph-adapter';
28
+ import proteomicsPTMApdapter from './adapters/ptm-exchange-adapter';
29
+ import alphaFoldConfidenceAdapter from './adapters/alphafold-confidence-adapter';
30
+ import alphaMissensePathogenicityAdapter from './adapters/alphamissense-pathogenicity-adapter';
31
+ import alphaMissenseHeatmapAdapter from './adapters/alphamissense-heatmap-adapter';
32
32
 
33
+ import ProtvistaUniprotStructure from './protvista-uniprot-structure';
34
+
35
+ import { fetchAll, loadComponent } from './utils';
36
+
37
+ import filterConfig, { colorConfig } from './filter-config';
33
38
  import defaultConfig from './config.json';
34
- import _ProtvistaUniprotStructure from './protvista-uniprot-structure';
35
- import { loadComponent } from './loadComponents';
36
- import _filterConfig, { colorConfig as _colorConfig } from './filterConfig';
37
- import { NightingaleEvent } from './types/nightingale-components';
38
39
 
39
40
  import loaderIcon from './icons/spinner.svg';
40
41
  import protvistaStyles from './styles/protvista-styles';
41
42
  import loaderStyles from './styles/loader-styles';
42
43
 
43
- export const transformDataFeatureAdapter = _transformDataFeatureAdapter;
44
- export const transformDataProteomicsAdapter = _transformDataProteomicsAdapter;
45
- export const transformDataStructureAdapter = _transformDataStructureAdapter;
46
- export const transformDataVariationAdapter = _transformDataVariationAdapter;
47
- export const transformDataVariationGraphAdapter =
48
- _transformDataVariationGraphAdapter;
49
- export const transformDataInterproAdapter = _transformDataInterproAdapter;
50
- export const transformDataProteomicsPTMApdapter =
51
- _transformDataProteomicsPTMApdapter;
52
- export const transformDataAlphaFoldConfidenceAdapter =
53
- _transformDataAlphaFoldConfidenceAdapter;
54
-
55
- export const transformDataAlphaMissensePathogenicityAdapter =
56
- _transformDataAlphaMissensePathogenicityAdapter;
57
-
58
- export const transformDataAlphaMissenseHeatmapAdapter =
59
- _transformDataAlphaMissenseHeatmapAdapter;
60
-
61
- export const filterConfig = _filterConfig;
62
- export const colorConfig = _colorConfig;
63
- export const ProtvistaUniprotStructure = _ProtvistaUniprotStructure;
64
-
65
44
  const adapters = {
66
- 'protvista-feature-adapter': transformDataFeatureAdapter,
67
- 'protvista-interpro-adapter': transformDataInterproAdapter,
68
- 'protvista-proteomics-adapter': transformDataProteomicsAdapter,
69
- 'protvista-structure-adapter': transformDataStructureAdapter,
70
- 'protvista-variation-adapter': transformDataVariationAdapter,
71
- 'protvista-variation-graph-adapter': transformDataVariationGraphAdapter,
72
- 'protvista-proteomics-ptm-adapter': transformDataProteomicsPTMApdapter,
73
- 'protvista-alphafold-confidence-adapter':
74
- transformDataAlphaFoldConfidenceAdapter,
75
- 'protvista-alphamissense-pathogenicity-adapter':
76
- transformDataAlphaMissensePathogenicityAdapter,
77
- 'protvista-alphamissense-heatmap-adapter':
78
- transformDataAlphaMissenseHeatmapAdapter,
45
+ 'feature-adapter': featureAdapter,
46
+ 'interpro-adapter': interproAdapter,
47
+ 'proteomics-adapter': proteomicsAdapter,
48
+ 'structure-adapter': structureAdapter,
49
+ 'variation-adapter': variationAdapter,
50
+ 'variation-graph-adapter': variationGraphAdapter,
51
+ 'proteomics-ptm-adapter': proteomicsPTMApdapter,
52
+ 'alphafold-confidence-adapter': alphaFoldConfidenceAdapter,
53
+ 'alphamissense-pathogenicity-adapter': alphaMissensePathogenicityAdapter,
54
+ 'alphamissense-heatmap-adapter': alphaMissenseHeatmapAdapter,
79
55
  };
80
56
 
81
57
  type TrackType =
@@ -95,15 +71,15 @@ type ProtvistaTrackConfig = {
95
71
  data: {
96
72
  url: string | string[];
97
73
  adapter?:
98
- | 'protvista-feature-adapter'
99
- | 'protvista-structure-adapter'
100
- | 'protvista-proteomics-adapter'
101
- | 'protvista-variation-adapter'
102
- | 'protvista-variation-graph-adapter'
103
- | 'protvista-interpro-adapter'
104
- | 'protvista-alphafold-confidence-adapter'
105
- | 'protvista-alphamissense-pathogenicity-adapter'
106
- | 'protvista-alphamissense-heatmap-adapter';
74
+ | 'feature-adapter'
75
+ | 'structure-adapter'
76
+ | 'proteomics-adapter'
77
+ | 'variation-adapter'
78
+ | 'variation-graph-adapter'
79
+ | 'interpro-adapter'
80
+ | 'alphafold-confidence-adapter'
81
+ | 'alphamissense-pathogenicity-adapter'
82
+ | 'alphamissense-heatmap-adapter';
107
83
  }[];
108
84
  tooltip: string;
109
85
  color?: string;
@@ -124,16 +100,21 @@ type ProtvistaCategory = {
124
100
  'color-range'?: string;
125
101
  };
126
102
 
127
- export type DownloadConfig = {
128
- type: string;
129
- url: string;
130
- }[];
131
-
132
103
  type ProtvistaConfig = {
133
104
  categories: ProtvistaCategory[];
134
- download: DownloadConfig;
135
105
  };
136
106
 
107
+ type NightingaleEvent = Event & {
108
+ detail?: {
109
+ displaystart?: number;
110
+ displayend?: number;
111
+ eventType?: 'click' | 'mouseover' | 'mouseout' | 'reset';
112
+ feature?: any;
113
+ coords?: [number, number];
114
+ };
115
+ };
116
+
117
+ @customElement('protvista-uniprot')
137
118
  class ProtvistaUniprot extends LitElement {
138
119
  private openCategories: string[];
139
120
  private nostructure: boolean;
@@ -196,7 +177,7 @@ class ProtvistaUniprot extends LitElement {
196
177
  loadComponent('nightingale-linegraph-track', NightingaleLinegraphTrack);
197
178
  loadComponent('nightingale-filter', NightingaleFilter);
198
179
  loadComponent('nightingale-manager', NightingaleManager);
199
- loadComponent('protvista-uniprot-structure', _ProtvistaUniprotStructure);
180
+ loadComponent('protvista-uniprot-structure', ProtvistaUniprotStructure);
200
181
  loadComponent('nightingale-sequence-heatmap', NightingaleSequenceHeatmap);
201
182
  }
202
183
 
@@ -207,28 +188,17 @@ class ProtvistaUniprot extends LitElement {
207
188
  const urls = this.config.categories.flatMap(({ tracks }) =>
208
189
  tracks.flatMap(({ data }) => data[0].url)
209
190
  );
210
- const uniqueUrls = [...new Set(urls)];
191
+
211
192
  // Get the data for all urls and store it
212
- await Promise.all(
213
- uniqueUrls.map((url: string) =>
214
- load(url.replace('{accession}', accession))
215
- .then(
216
- (data) => {
217
- this.rawData[url] = data.payload;
218
- // Some endpoints return empty arrays, while most fail 🙄
219
- if (!this.hasData && data.payload?.features?.length)
220
- this.hasData = true;
221
- },
222
- // TODO handle this better based on error code
223
- // Fail silently for now
224
- (error) => console.warn(error)
225
- )
226
- .catch((e) => {
227
- console.log(e);
228
- })
229
- )
193
+ this.rawData = await fetchAll([...new Set(urls)], (url) =>
194
+ url.replace('{accession}', accession)
230
195
  );
231
196
 
197
+ // Some endpoints return empty arrays, while most fail 🙄
198
+ this.hasData =
199
+ this.hasData ||
200
+ Object.values(this.rawData).some((d) => !!d?.features?.length);
201
+
232
202
  // Now iterate over tracks and categories, transforming the data
233
203
  // and assigning it as adequate
234
204
  for (const { name: categoryName, tracks, trackType } of this.config
@@ -242,18 +212,17 @@ class ProtvistaUniprot extends LitElement {
242
212
 
243
213
  if (
244
214
  !trackData ||
245
- (adapter === 'protvista-variation-adapter' &&
246
- trackData[0].length === 0)
215
+ (adapter === 'variation-adapter' && trackData[0].length === 0)
247
216
  ) {
248
217
  return;
249
218
  }
250
219
 
251
220
  // 1. Convert data
252
221
  let transformedData = adapter
253
- ? await adapters[adapter](...trackData)
222
+ ? await adapters[adapter].apply(null, trackData)
254
223
  : trackData;
255
224
 
256
- if (adapter === 'protvista-interpro-adapter') {
225
+ if (adapter === 'interpro-adapter') {
257
226
  const representativeDomains = [];
258
227
  transformedData?.forEach((feature) => {
259
228
  feature.locations?.forEach((location) => {
@@ -322,7 +291,7 @@ class ProtvistaUniprot extends LitElement {
322
291
  currentCategory.tracks &&
323
292
  data &&
324
293
  // Check there's data and special case for variants
325
- // NOTE: should refactor protvista-variation-adapter
294
+ // NOTE: should refactor variation-adapter
326
295
  // to return a list of variants and set the sequence
327
296
  // on protvista-variation separately
328
297
  (data.length > 0 || data.variants?.length)
@@ -350,9 +319,10 @@ class ProtvistaUniprot extends LitElement {
350
319
  ) {
351
320
  for (const track of currentCategory.tracks) {
352
321
  if (track.trackType === 'nightingale-sequence-heatmap') {
353
- const heatmapComponent = this.querySelector<
354
- typeof NightingaleSequenceHeatmap
355
- >('nightingale-sequence-heatmap');
322
+ const heatmapComponent =
323
+ this.querySelector<NightingaleSequenceHeatmap>(
324
+ 'nightingale-sequence-heatmap'
325
+ );
356
326
  if (heatmapComponent) {
357
327
  const heatmapData = this.data[`${id}-${track.name}`];
358
328
  const xDomain = Array.from(
@@ -361,7 +331,7 @@ class ProtvistaUniprot extends LitElement {
361
331
  );
362
332
  const yDomain = [
363
333
  ...new Set(heatmapData.map((hotMapItem) => hotMapItem.yValue)),
364
- ];
334
+ ] as string[];
365
335
  heatmapComponent.setHeatmapData(xDomain, yDomain, heatmapData);
366
336
  }
367
337
  }
@@ -25,7 +25,8 @@ export default css`
25
25
  .track-label,
26
26
  .nav-track-label,
27
27
  .credits {
28
- width: 20vw;
28
+ min-width: 20vw;
29
+ max-width: 20vw;
29
30
  padding: 0.5em;
30
31
  line-height: normal;
31
32
  }
@@ -0,0 +1,223 @@
1
+ import ecoMap from '../adapters/config/evidence';
2
+
3
+ const formatSource = (source) => {
4
+ if (source.name?.toLowerCase() === 'PubMed'.toLowerCase()) {
5
+ return `${source.id}&nbsp;(<a href='${source.url}' style="color:#FFF" target='_blank'>${source.name}</a>&nbsp;<a href='${source.alternativeUrl}' style="color:#FFF" target='_blank'>EuropePMC</a>)`;
6
+ }
7
+ const sourceLink = `&nbsp;<a href='${source.url}' style="color:#FFF" target='_blank'>${source.id}</a>`;
8
+ if (source.name) {
9
+ // Temporary until we get the expected value as 'PeptideAtlas' instead of 'HppPeptideAtlas'
10
+ if (source.name.startsWith('Hpp')) {
11
+ return `${sourceLink}&nbsp;(${source.name.slice(3)})`;
12
+ }
13
+ return `${sourceLink}&nbsp;(${source.name})`;
14
+ }
15
+ return sourceLink;
16
+ };
17
+
18
+ export const getEvidenceFromCodes = (evidenceList) => {
19
+ if (!evidenceList) return ``;
20
+ return `
21
+ <ul>${evidenceList
22
+ .map((ev) => {
23
+ const ecoMatch = ecoMap.find((eco) => eco.name === ev.code);
24
+ if (!ecoMatch) return ``;
25
+ return `<li title='${
26
+ ecoMatch.description
27
+ }' style="padding: .25rem 0">${ecoMatch.shortDescription}:&nbsp;${
28
+ ev.source ? formatSource(ev.source) : ''
29
+ }</li>`;
30
+ })
31
+ .join('')}</ul>
32
+ `;
33
+ };
34
+
35
+ export const formatXrefs = (xrefs) => {
36
+ return `<ul>${xrefs
37
+ .map(
38
+ (xref) =>
39
+ `<li style="padding: .25rem 0">${xref.name} ${
40
+ xref.url
41
+ ? `<a href="${xref.url}" style="color:#FFF" target="_blank">${xref.id}</a>`
42
+ : `${xref.name} ${xref.id}`
43
+ }</li>`
44
+ )
45
+ .join('')}</ul>`;
46
+ };
47
+
48
+ const getPTMEvidence = (ptms) => {
49
+ if (!ptms) return ``;
50
+ const ids = ptms.flatMap(({ dbReferences }) =>
51
+ dbReferences.map((ref) => ref.id)
52
+ );
53
+ const uniqueIds = [...new Set(ids.flat())];
54
+ // Urls in the payload are not relevant. For 'Glue project' dataset, Dataset ID and publication reference is hardcoded. Need to be checked in 2024 if it still exists in the payload
55
+ const proteomexchange =
56
+ 'https://proteomecentral.proteomexchange.org/dataset/';
57
+ return `
58
+ <ul>${uniqueIds
59
+ .map((id) => {
60
+ const datasetID = id === 'Glue project' ? 'PXD012174' : id;
61
+ return `<li title='${datasetID}' style="padding: .25rem 0">${datasetID}&nbsp;(<a href="${proteomexchange}${datasetID}" style="color:#FFF" target="_blank">ProteomeXchange</a>${
62
+ id === 'Glue project'
63
+ ? `)</li><li title="publication" style="padding: .25rem 0">Publication:&nbsp;31819260&nbsp;(<a href="https://pubmed.ncbi.nlm.nih.gov/31819260" style="color:#FFF" target="_blank">PubMed</a>)</li>`
64
+ : `&nbsp;<a href="http://www.peptideatlas.org/builds/rice/phospho/" style="color:#FFF" target="_blank">PeptideAtlas</a>)</li>`
65
+ }`;
66
+ })
67
+ .join('')}</ul>
68
+ `;
69
+ };
70
+
71
+ const formatPTMPeptidoform = (peptide, ptms) => {
72
+ if (!ptms) return ``;
73
+ const modificationValues = ptms.map((ptm) => ({
74
+ name: ptm.name,
75
+ position: ptm.position,
76
+ }));
77
+ let peptidoform = '';
78
+ let lastModPosition = 0;
79
+ modificationValues.forEach((p) => {
80
+ peptidoform = `${peptidoform}${peptide.slice(
81
+ lastModPosition,
82
+ p.position
83
+ )}[${p.name}]`;
84
+ lastModPosition = p.position;
85
+ });
86
+ // Add last remaining part of the peptide if any
87
+ peptidoform = `${peptidoform}${peptide.slice(lastModPosition)}`;
88
+ return `<p>${peptidoform}</p>`;
89
+ };
90
+
91
+ // At the moment, there is only phospho data. In future we may have more, the below AA sites have to be updated to accomodate more.
92
+ const AAPhosphoSites = {
93
+ A: 'alanine',
94
+ S: 'serine',
95
+ T: 'threonine',
96
+ Y: 'tyrosine',
97
+ };
98
+
99
+ const findModifiedResidueName = (feature, ptm) => {
100
+ const { peptide, begin: peptideStart } = feature;
101
+ const proteinLocation = Number(peptideStart) + ptm.position - 1;
102
+ const modifiedResidue = peptide.charAt(ptm.position - 1); // CharAt index starts from 0
103
+ return `${proteinLocation} phospho${AAPhosphoSites[modifiedResidue]}`;
104
+ };
105
+
106
+ const formatTooltip = (feature) => {
107
+ const evidenceHTML =
108
+ feature.type === 'PROTEOMICS_PTM'
109
+ ? getPTMEvidence(feature.ptms)
110
+ : getEvidenceFromCodes(feature.evidences);
111
+ const ptms =
112
+ feature.type === 'PROTEOMICS_PTM' &&
113
+ feature.ptms.map((ptm) => findModifiedResidueName(feature, ptm));
114
+
115
+ const dataset =
116
+ feature.type === 'PROTEOMICS_PTM' &&
117
+ feature.ptms.flatMap(({ dbReferences }) =>
118
+ dbReferences.map((ref) => ref.id)
119
+ );
120
+
121
+ let { description } = feature;
122
+
123
+ if (feature.type === 'BINDING' || feature.type === 'Binding site') {
124
+ let bindingDescription = '';
125
+ if (feature.ligandPart) {
126
+ bindingDescription += `${feature.ligandPart.name} of `;
127
+ }
128
+ if (feature.ligand) {
129
+ bindingDescription += feature.ligand.name;
130
+ }
131
+ if (feature.description) {
132
+ bindingDescription += `; ${feature.description}`;
133
+ }
134
+ description = bindingDescription;
135
+ }
136
+
137
+ try {
138
+ return `
139
+ ${description ? `<h5>Description</h5><p>${description}</p>` : ``}
140
+ ${
141
+ feature.matchScore
142
+ ? `<h5>Match score</h5><p>${feature.matchScore}%</p>`
143
+ : ``
144
+ }
145
+ ${feature.ftId ? `<h5>Feature ID</h5><p>${feature.ftId}</p>` : ``}
146
+ ${
147
+ feature.alternativeSequence
148
+ ? `<h5>Alternative sequence</h5><p>${feature.alternativeSequence}</p>`
149
+ : ``
150
+ }
151
+ ${
152
+ ptms
153
+ ? `<h5 data-article-id="ptm_processing_section">PTMs</h5><ul>${ptms
154
+ .map((item) => `<li>${item}</li>`)
155
+ .join('')}</ul>
156
+ `
157
+ : ''
158
+ }
159
+ ${
160
+ feature.peptide && feature.type === 'PROTEOMICS_PTM'
161
+ ? `<h5 data-article-id="mod_res_large_scale#what-is-the-goldsilverbronze-criterion">Peptidoform</h5><p>${formatPTMPeptidoform(
162
+ feature.peptide,
163
+ feature.ptms
164
+ )}</p>`
165
+ : ``
166
+ }
167
+ ${
168
+ feature.peptide && feature.type !== 'PROTEOMICS_PTM'
169
+ ? `<h5>Peptide</h5><p>${feature.peptide}</p>`
170
+ : ``
171
+ }
172
+ ${
173
+ feature.xrefs
174
+ ? `<h5>Cross-references</h5>${formatXrefs(feature.xrefs)}`
175
+ : ``
176
+ }
177
+ ${evidenceHTML ? `<h5>Evidence</h5>${evidenceHTML}` : ``}
178
+ ${
179
+ feature.ptms && dataset && !dataset.includes('Glue project')
180
+ ? `<hr /><h5 data-article-id="mod_res_large_scale#what-is-the-goldsilverbronze-criterion">PTM statistical attributes</h5><ul>${feature.ptms
181
+ .map((ptm) =>
182
+ ptm.dbReferences
183
+ .map(
184
+ (ref) =>
185
+ `<li><b>${ref.id}</b></li>
186
+ <li style="text-indent: 1em"><b>${findModifiedResidueName(
187
+ feature,
188
+ ptm
189
+ )}</b></li>
190
+ <li style="text-indent: 2em">PubMed ID: <a href="https://europepmc.org/article/MED/${
191
+ ref.properties['Pubmed ID']
192
+ }" style="color:#FFF" target="_blank">
193
+ ${ref.properties['Pubmed ID']}</a>
194
+ </li>
195
+ <li style="text-indent: 2em"><span data-article-id="mod_res_large_scale#confidence-score">Confidence score</span>: ${
196
+ ref.properties['Confidence score']
197
+ }</li>
198
+ <li style="text-indent: 2em">Universal Spectrum Id:
199
+ <a href="http://proteomecentral.proteomexchange.org/usi/?usi=${
200
+ ref.properties['Universal Spectrum Id']
201
+ }" style="color:#FFF" target="_blank">View on ProteomeXchange</a>
202
+ </li>
203
+ <li style="text-indent: 2em">PSM Count (0.05 gFLR): ${
204
+ ref.properties['PSM Count (0.05 gFLR)']
205
+ }</li>
206
+ <li style="text-indent: 2em">Final site probability: ${
207
+ ref.properties['Final site probability']
208
+ }</li>
209
+ `
210
+ )
211
+ .join('')
212
+ )
213
+ .join('')}</ul>`
214
+ : ''
215
+ }
216
+ `;
217
+ } catch (error) {
218
+ console.error(error);
219
+ return '';
220
+ }
221
+ };
222
+
223
+ export default formatTooltip;
@@ -0,0 +1,20 @@
1
+ const getStructuresHTML = (structureList) => {
2
+ return `<ul>
3
+ ${structureList
4
+ .map(
5
+ (
6
+ structure
7
+ ) => `<li style="margin: 0.25rem 0"><a style="color:#FFF" href='${structure.source.url}' target='_blank'>
8
+ ${structure.source.id}
9
+ </a> (${structure.start}-${structure.end})</li>`
10
+ )
11
+ .join('')}
12
+ </ul>`;
13
+ };
14
+
15
+ const formatTooltip = (feature) => {
16
+ const structuresHTML = getStructuresHTML(feature.structures);
17
+ return `${structuresHTML ? `<h5>Structures</h5>${structuresHTML}` : ``}`;
18
+ };
19
+
20
+ export default formatTooltip;
@@ -0,0 +1,139 @@
1
+ import {
2
+ Association,
3
+ Description,
4
+ PopulationFrequency,
5
+ Variant,
6
+ Prediction,
7
+ } from '@nightingale-elements/nightingale-variation';
8
+ import groupBy from 'lodash-es/groupBy';
9
+
10
+ import { formatXrefs, getEvidenceFromCodes } from './featureTooltip';
11
+
12
+ const getDiseaseAssociations = (associations: Association[]): string =>
13
+ associations
14
+ ?.map(
15
+ (association) => `
16
+ <h4>Disease association</h4><p>${association.name}</p>
17
+ ${
18
+ association.description
19
+ ? `<>${association.description}</p>`
20
+ : ''
21
+ }
22
+ ${
23
+ association.dbReferences
24
+ ? `<h5>Cross-references</h5>${formatXrefs(
25
+ association.dbReferences
26
+ )}`
27
+ : ''
28
+ }
29
+ ${getEvidenceFromCodes(association.evidences)}
30
+ `
31
+ )
32
+ .join('');
33
+
34
+ const getDescriptions = (descriptions: Description[]): string =>
35
+ `<hr/><h5>Description</h5>${descriptions
36
+ .map((description) => `<p>${description.value}</p>`)
37
+ .join('')}
38
+ `;
39
+
40
+ const getPopulationFrequencies = (
41
+ popFrequencies: PopulationFrequency[]
42
+ ): string =>
43
+ `<hr/><h5>Population frequencies</h5>${popFrequencies
44
+ .map(
45
+ (freq) =>
46
+ `<p>${freq.frequency} - ${freq.populationName} (${freq.source})</p>`
47
+ )
48
+ .join('')}`;
49
+
50
+ const getEnsemblCovidLinks = (variant: Variant): string => {
51
+ const shouldGenerateLink = variant.locations.some(
52
+ (location) => location.source === 'EnsemblViruses'
53
+ );
54
+ if (shouldGenerateLink) {
55
+ const xref = variant.xrefs.find((xref) => xref.name === 'ENA');
56
+ return xref.id
57
+ ? `<h5>Ensembl COVID-19</h5>
58
+ <p>
59
+ <a href="https://covid-19.ensembl.org/Sars_cov_2/Variation/Explore?v=${xref.id}" target="_blank" rel="noopener noreferrer">${xref.id}</a>
60
+ </p>`
61
+ : '';
62
+ }
63
+ return '';
64
+ };
65
+
66
+ const getPredictions = (predictions: Prediction[]): string => {
67
+ const groupedPredictions = groupBy(predictions, 'predAlgorithmNameType');
68
+ const counts = Object.keys(groupedPredictions).map((key) => {
69
+ const valueGroups = groupBy(groupedPredictions[key], 'predictionValType');
70
+ return {
71
+ algorithm: key,
72
+ values: Object.keys(valueGroups).map((valKey) => ({
73
+ name: valKey,
74
+ count: valueGroups[valKey].length,
75
+ })),
76
+ };
77
+ });
78
+ return counts
79
+ .map(
80
+ (countItem) =>
81
+ `<h6>${countItem.algorithm}</h6><ul class="no-bullet">${countItem.values
82
+ .map((countValue) => `<li>${countValue.name}</li>`)
83
+ .join('')}</ul>`
84
+ )
85
+ .join('');
86
+ };
87
+
88
+ const formatTooltip = (variant: Variant): string =>
89
+ `
90
+ <h5>Variant</h5><p>${variant.wildType} > ${
91
+ variant.alternativeSequence || ''
92
+ }</p>
93
+ ${
94
+ variant.populationFrequencies
95
+ ? getPopulationFrequencies(variant.populationFrequencies)
96
+ : ''
97
+ }
98
+ ${
99
+ variant.consequenceType
100
+ ? `<h5>Consequence</h5><p>${variant.consequenceType}</p>`
101
+ : ``
102
+ }
103
+ ${
104
+ variant.somaticStatus
105
+ ? `<h5>Somatic</h5><p>${
106
+ variant.somaticStatus === 0 ? 'No' : 'Yes'
107
+ }</p>`
108
+ : ``
109
+ }
110
+ ${
111
+ variant.genomicLocation?.length
112
+ ? `<h5>Location</h5><p>${variant.genomicLocation.join(
113
+ ', '
114
+ )}</p>`
115
+ : ``
116
+ }
117
+ ${
118
+ variant.ftId
119
+ ? `<h5>Feature ID</h5><p>${variant.ftId}</p>`
120
+ : ``
121
+ }
122
+ ${
123
+ variant.descriptions
124
+ ? getDescriptions(variant.descriptions)
125
+ : ''
126
+ }
127
+ ${
128
+ variant.association
129
+ ? getDiseaseAssociations(variant.association)
130
+ : ''
131
+ }
132
+ ${
133
+ variant.predictions ? getPredictions(variant.predictions) : ''
134
+ }
135
+ ${getEnsemblCovidLinks(variant)}
136
+
137
+ `;
138
+
139
+ export default formatTooltip;