TranscriptsListItemInfo.tsx 7.06 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/**
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

17
import React from 'react';
18
import { useParams, Link } from 'react-router-dom';
19
import classNames from 'classnames';
20
import { connect } from 'react-redux';
21
import { Pick2, Pick3, Pick4 } from 'ts-multipick';
22 23 24 25

import { getCommaSeparatedNumber } from 'src/shared/helpers/formatters/numberFormatter';
import { getFormattedLocation } from 'src/shared/helpers/formatters/regionFormatter';
import {
26
  isProteinCodingTranscript,
27
  getFeatureCoordinates,
28
  getRegionName,
Imran Salam's avatar
Imran Salam committed
29
  getNumberOfCodingExons,
30 31
  getSplicedRNALength,
  getProductAminoAcidLength
32
} from 'src/content/app/entity-viewer/shared/helpers/entity-helpers';
33 34
import * as urlFor from 'src/shared/helpers/urlHelper';
import { buildFocusIdForUrl } from 'src/shared/state/ens-object/ensObjectHelpers';
35

36
import { InstantDownloadTranscript } from 'src/shared/components/instant-download';
37
import ViewInApp from 'src/shared/components/view-in-app/ViewInApp';
38

39
import { toggleTranscriptDownload } from 'src/content/app/entity-viewer/state/gene-view/transcripts/geneViewTranscriptsSlice';
40
import { clearExpandedProteins } from 'src/content/app/entity-viewer/state/gene-view/proteins/geneViewProteinsSlice';
41

42 43 44 45
import { FullGene } from 'src/shared/types/thoas/gene';
import { FullTranscript } from 'src/shared/types/thoas/transcript';
import { SplicedExon, PhasedExon } from 'src/shared/types/thoas/exon';
import { FullProductGeneratingContext } from 'src/shared/types/thoas/productGeneratingContext';
46
import { View } from 'src/content/app/entity-viewer/state/gene-view/view/geneViewViewSlice';
47 48 49 50

import transcriptsListStyles from '../DefaultTranscriptsList.scss';
import styles from './TranscriptsListItemInfo.scss';

Jyothish's avatar
Jyothish committed
51 52
import { ReactComponent as ChevronDown } from 'static/img/shared/chevron-down.svg';

Imran Salam's avatar
Imran Salam committed
53
type Gene = Pick<FullGene, 'unversioned_stable_id' | 'stable_id'>;
54 55
type Transcript = Pick<
  FullTranscript,
56
  'stable_id' | 'unversioned_stable_id' | 'so_term'
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
> &
  Pick2<FullTranscript, 'slice', 'location'> &
  Pick3<FullTranscript, 'slice', 'region', 'name'> & {
    spliced_exons: Array<
      Pick2<SplicedExon, 'exon', 'stable_id'> &
        Pick4<SplicedExon, 'exon', 'slice', 'location', 'length'>
    >;
  } & {
    product_generating_contexts: Array<
      Pick<FullProductGeneratingContext, 'product_type'> &
        Pick2<
          FullProductGeneratingContext,
          'product',
          'length' | 'stable_id'
        > & {
          phased_exons: Array<
            Pick<PhasedExon, 'start_phase' | 'end_phase'> &
              Pick2<PhasedExon, 'exon', 'stable_id'>
          >;
        }
    >;
  };

80
export type TranscriptsListItemInfoProps = {
81
  gene: Gene;
82
  transcript: Transcript;
83 84
  expandDownload: boolean;
  toggleTranscriptDownload: (id: string) => void;
85
  onProteinLinkClick: () => void;
86 87
};

88 89 90
export const TranscriptsListItemInfo = (
  props: TranscriptsListItemInfoProps
) => {
91
  const { transcript } = props;
92
  const params: { [key: string]: string } = useParams();
93
  const { genomeId, entityId } = params;
94 95 96 97 98 99 100 101 102 103 104 105

  const getTranscriptLocation = () => {
    const { start, end } = getFeatureCoordinates(transcript);
    const chromosome = getRegionName(transcript);

    return getFormattedLocation({
      chromosome,
      start,
      end
    });
  };

Imran Salam's avatar
Imran Salam committed
106 107 108 109
  const splicedRNALength = getCommaSeparatedNumber(
    getSplicedRNALength(transcript)
  );

Jyothish's avatar
Jyothish committed
110 111 112 113
  const aminoAcidLength = getCommaSeparatedNumber(
    getProductAminoAcidLength(transcript)
  );

114 115 116
  const mainStyles = classNames(transcriptsListStyles.row, styles.listItemInfo);
  const midStyles = classNames(transcriptsListStyles.middle, styles.middle);

117 118
  const focusIdForUrl = buildFocusIdForUrl({
    type: 'gene',
119
    objectId: props.gene.unversioned_stable_id
120 121
  });

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
  const getLinkToProteinView = (proteinStableId: string) => {
    const proteinViewUrl = urlFor.entityViewer({
      genomeId,
      entityId,
      view: View.PROTEIN,
      proteinId: proteinStableId
    });

    return (
      <Link onClick={() => props.onProteinLinkClick()} to={proteinViewUrl}>
        {proteinStableId}
      </Link>
    );
  };

137 138 139 140
  const getBrowserLink = () => {
    const { genomeId } = params;
    return urlFor.browser({ genomeId: genomeId, focus: focusIdForUrl });
  };
Jyothish's avatar
Jyothish committed
141 142 143 144 145

  const chevronClassForDownload = classNames(styles.chevron, {
    [styles.chevronUp]: props.expandDownload
  });

146 147
  return (
    <div className={mainStyles}>
148
      <div className={transcriptsListStyles.left}></div>
149
      <div className={midStyles}>
150
        <div className={styles.topLeft}>
151
          <div>
152
            <strong>{transcript.so_term}</strong>
153 154 155
          </div>
          <div>{getTranscriptLocation()}</div>
        </div>
156
        <div className={styles.topMiddle}>
157
          {isProteinCodingTranscript(transcript) && (
158
            <>
159
              <div>
Jyothish's avatar
Jyothish committed
160
                <strong>{aminoAcidLength} aa</strong>
161
              </div>
162 163 164
              {getLinkToProteinView(
                transcript.product_generating_contexts[0]?.product.stable_id
              )}
165 166 167
            </>
          )}
        </div>
168
        <div className={styles.topRight}>
169
          <div>
170
            Combined exon length <strong>{splicedRNALength}</strong> bp
171 172
          </div>
          <div>
173
            Coding exons <strong>{getNumberOfCodingExons(transcript)}</strong>{' '}
174
            of {transcript.spliced_exons.length}
175 176
          </div>
        </div>
Jyothish's avatar
Jyothish committed
177 178 179 180 181 182 183 184 185

        <div className={styles.moreInformation}>More information</div>

        <div
          className={styles.downloadLink}
          onClick={() => props.toggleTranscriptDownload(transcript.stable_id)}
        >
          Download
          <ChevronDown className={chevronClassForDownload} />
186
        </div>
187
        {props.expandDownload && renderInstantDownload({ ...props, genomeId })}
188
      </div>
189
      <div className={transcriptsListStyles.right}>
190
        <div className={styles.viewInApp}>
191
          <ViewInApp links={{ genomeBrowser: { url: getBrowserLink() } }} />
192 193
        </div>
      </div>
194 195 196 197
    </div>
  );
};

198
const renderInstantDownload = ({
199
  transcript,
200
  gene,
201 202 203 204
  genomeId
}: TranscriptsListItemInfoProps & {
  genomeId: string;
}) => {
205 206
  return (
    <div className={styles.download}>
207
      <InstantDownloadTranscript
208
        genomeId={genomeId}
209
        transcript={{
Imran Salam's avatar
Imran Salam committed
210
          id: transcript.stable_id,
211 212
          so_term: transcript.so_term
        }}
Imran Salam's avatar
Imran Salam committed
213
        gene={{ id: gene.stable_id }}
214
      />
215 216 217 218
    </div>
  );
};

219
const mapDispatchToProps = {
220 221
  toggleTranscriptDownload,
  onProteinLinkClick: clearExpandedProteins
222 223 224
};

export default connect(null, mapDispatchToProps)(TranscriptsListItemInfo);