Unverified Commit 3ac7d6a3 authored by Ridwan Amode's avatar Ridwan Amode Committed by GitHub
Browse files

ENSWBSITES-1110: Display transcript metadata in transcript accordian (#526)

* ENSWBSITES-1110: Display transcript metadata in transcript accordian
- retrieve metadata from thoas (gencode_basic, tsl, appris, ccds)
- update component to display metadata
- Modify transcript object by setting each key instead of overwriting
- rename git-column-gap to column-gap since new CSS standard
parent 07aed325
Pipeline #176725 passed with stages
in 4 minutes and 39 seconds
...@@ -154,6 +154,15 @@ const QUERY = gql` ...@@ -154,6 +154,15 @@ const QUERY = gql`
} }
} }
} }
external_references {
accession_id
name
url
source {
id
name
}
}
metadata { metadata {
canonical { canonical {
value value
...@@ -165,6 +174,15 @@ const QUERY = gql` ...@@ -165,6 +174,15 @@ const QUERY = gql`
label label
definition definition
} }
gencode_basic {
label
}
appris {
label
}
tsl {
label
}
} }
} }
} }
......
...@@ -26,6 +26,7 @@ import { filterTranscriptsBySOTerm } from 'src/content/app/entity-viewer/shared/ ...@@ -26,6 +26,7 @@ import { filterTranscriptsBySOTerm } from 'src/content/app/entity-viewer/shared/
import { import {
getExpandedTranscriptIds, getExpandedTranscriptIds,
getExpandedTranscriptDownloadIds, getExpandedTranscriptDownloadIds,
getExpandedTranscriptMoreInfoIds,
getFilters, getFilters,
getSortingRule getSortingRule
} from 'src/content/app/entity-viewer/state/gene-view/transcripts/geneViewTranscriptsSelectors'; } from 'src/content/app/entity-viewer/state/gene-view/transcripts/geneViewTranscriptsSelectors';
...@@ -72,6 +73,9 @@ const DefaultTranscriptslist = (props: Props) => { ...@@ -72,6 +73,9 @@ const DefaultTranscriptslist = (props: Props) => {
const expandedTranscriptDownloadIds = useSelector( const expandedTranscriptDownloadIds = useSelector(
getExpandedTranscriptDownloadIds getExpandedTranscriptDownloadIds
); );
const expandedTranscriptMoreInfoIds = useSelector(
getExpandedTranscriptMoreInfoIds
);
const sortingRule = useSelector(getSortingRule); const sortingRule = useSelector(getSortingRule);
const filters = useSelector(getFilters); const filters = useSelector(getFilters);
const dispatch = useDispatch(); const dispatch = useDispatch();
...@@ -109,6 +113,9 @@ const DefaultTranscriptslist = (props: Props) => { ...@@ -109,6 +113,9 @@ const DefaultTranscriptslist = (props: Props) => {
const expandDownload = expandedTranscriptDownloadIds.includes( const expandDownload = expandedTranscriptDownloadIds.includes(
transcript.stable_id transcript.stable_id
); );
const expandMoreInfo = expandedTranscriptMoreInfoIds.includes(
transcript.stable_id
);
return ( return (
<DefaultTranscriptsListItem <DefaultTranscriptsListItem
...@@ -118,6 +125,7 @@ const DefaultTranscriptslist = (props: Props) => { ...@@ -118,6 +125,7 @@ const DefaultTranscriptslist = (props: Props) => {
rulerTicks={props.rulerTicks} rulerTicks={props.rulerTicks}
expandTranscript={expandTranscript} expandTranscript={expandTranscript}
expandDownload={expandDownload} expandDownload={expandDownload}
expandMoreInfo={expandMoreInfo}
/> />
); );
})} })}
......
...@@ -51,6 +51,7 @@ describe('<DefaultTranscriptListItem />', () => { ...@@ -51,6 +51,7 @@ describe('<DefaultTranscriptListItem />', () => {
rulerTicks: createRulerTicks(), rulerTicks: createRulerTicks(),
expandTranscript: false, expandTranscript: false,
expandDownload: false, expandDownload: false,
expandMoreInfo: false,
toggleTranscriptInfo: toggleTranscriptInfo toggleTranscriptInfo: toggleTranscriptInfo
}; };
......
...@@ -47,6 +47,7 @@ export type DefaultTranscriptListItemProps = { ...@@ -47,6 +47,7 @@ export type DefaultTranscriptListItemProps = {
rulerTicks: TicksAndScale; rulerTicks: TicksAndScale;
expandTranscript: boolean; expandTranscript: boolean;
expandDownload: boolean; expandDownload: boolean;
expandMoreInfo: boolean;
toggleTranscriptInfo: (id: string) => void; toggleTranscriptInfo: (id: string) => void;
}; };
...@@ -101,6 +102,7 @@ export const DefaultTranscriptListItem = ( ...@@ -101,6 +102,7 @@ export const DefaultTranscriptListItem = (
gene={props.gene} gene={props.gene}
transcript={props.transcript} transcript={props.transcript}
expandDownload={props.expandDownload} expandDownload={props.expandDownload}
expandMoreInfo={props.expandMoreInfo}
/> />
) : null} ) : null}
</div> </div>
......
...@@ -9,10 +9,11 @@ ...@@ -9,10 +9,11 @@
display: grid; display: grid;
grid-template-areas: grid-template-areas:
'left middle right' 'left middle right'
'moreinformation . .' 'moreinformationLink . .'
'downloadLink . .' 'moreInformation moreInformation moreInformation'
'downloadLink . .'
'download download download'; 'download download download';
grid-column-gap: 10px; column-gap: 10px;
background: $soft-black; background: $soft-black;
padding: 15px 30px; padding: 15px 30px;
...@@ -30,6 +31,7 @@ ...@@ -30,6 +31,7 @@
.topLeft { .topLeft {
grid-area: left; grid-area: left;
width: 190px; width: 190px;
margin-bottom: 12px;
div { div {
padding-bottom: 6px; padding-bottom: 6px;
} }
...@@ -51,8 +53,31 @@ ...@@ -51,8 +53,31 @@
} }
} }
.moreInformationLink {
padding-top: 6px;
color: $blue;
cursor: pointer;
}
.moreInformation { .moreInformation {
padding-top: 16px; grid-area: moreInformation;
display: grid;
grid-template-columns: 190px auto;
column-gap: 20px;
padding-top: 6px;
padding-left: 12px;
margin-bottom: 6px;
}
.moreInfoColumn {
width: 190px;
div {
padding-bottom: 6px;
}
}
.normalText {
font-weight: $normal;
} }
.downloadLink { .downloadLink {
......
...@@ -42,12 +42,15 @@ jest.mock('src/shared/components/instant-download', () => ({ ...@@ -42,12 +42,15 @@ jest.mock('src/shared/components/instant-download', () => ({
const transcript = createTranscript(); const transcript = createTranscript();
const gene = createGene({ transcripts: [transcript] }); const gene = createGene({ transcripts: [transcript] });
const expandDownload = false; const expandDownload = false;
const expandMoreInfo = false;
const defaultProps = { const defaultProps = {
gene, gene,
transcript, transcript,
expandDownload, expandDownload,
expandMoreInfo,
toggleTranscriptDownload: jest.fn(), toggleTranscriptDownload: jest.fn(),
toggleTranscriptMoreInfo: jest.fn(),
onProteinLinkClick: jest.fn() onProteinLinkClick: jest.fn()
}; };
......
...@@ -36,8 +36,12 @@ import { buildFocusIdForUrl } from 'src/shared/state/ens-object/ensObjectHelpers ...@@ -36,8 +36,12 @@ import { buildFocusIdForUrl } from 'src/shared/state/ens-object/ensObjectHelpers
import { InstantDownloadTranscript } from 'src/shared/components/instant-download'; import { InstantDownloadTranscript } from 'src/shared/components/instant-download';
import ViewInApp from 'src/shared/components/view-in-app/ViewInApp'; import ViewInApp from 'src/shared/components/view-in-app/ViewInApp';
import ShowHide from 'src/shared/components/show-hide/ShowHide'; import ShowHide from 'src/shared/components/show-hide/ShowHide';
import ExternalReference from 'src/shared/components/external-reference/ExternalReference';
import { toggleTranscriptDownload } from 'src/content/app/entity-viewer/state/gene-view/transcripts/geneViewTranscriptsSlice'; import {
toggleTranscriptDownload,
toggleTranscriptMoreInfo
} from 'src/content/app/entity-viewer/state/gene-view/transcripts/geneViewTranscriptsSlice';
import { clearExpandedProteins } from 'src/content/app/entity-viewer/state/gene-view/proteins/geneViewProteinsSlice'; import { clearExpandedProteins } from 'src/content/app/entity-viewer/state/gene-view/proteins/geneViewProteinsSlice';
import { FullGene } from 'src/shared/types/thoas/gene'; import { FullGene } from 'src/shared/types/thoas/gene';
...@@ -52,9 +56,14 @@ import styles from './TranscriptsListItemInfo.scss'; ...@@ -52,9 +56,14 @@ import styles from './TranscriptsListItemInfo.scss';
type Gene = Pick<FullGene, 'unversioned_stable_id' | 'stable_id'>; type Gene = Pick<FullGene, 'unversioned_stable_id' | 'stable_id'>;
type Transcript = Pick< type Transcript = Pick<
FullTranscript, FullTranscript,
'stable_id' | 'unversioned_stable_id' | 'so_term' 'stable_id' | 'unversioned_stable_id' | 'so_term' | 'external_references'
> & > &
Pick2<FullTranscript, 'slice', 'location'> & Pick2<FullTranscript, 'slice', 'location'> &
Pick2<
FullTranscript,
'metadata',
'canonical' | 'mane' | 'gencode_basic' | 'appris' | 'tsl'
> &
Pick3<FullTranscript, 'slice', 'region', 'name'> & { Pick3<FullTranscript, 'slice', 'region', 'name'> & {
spliced_exons: Array< spliced_exons: Array<
Pick2<SplicedExon, 'exon', 'stable_id'> & Pick2<SplicedExon, 'exon', 'stable_id'> &
...@@ -80,7 +89,9 @@ export type TranscriptsListItemInfoProps = { ...@@ -80,7 +89,9 @@ export type TranscriptsListItemInfoProps = {
gene: Gene; gene: Gene;
transcript: Transcript; transcript: Transcript;
expandDownload: boolean; expandDownload: boolean;
expandMoreInfo: boolean;
toggleTranscriptDownload: (id: string) => void; toggleTranscriptDownload: (id: string) => void;
toggleTranscriptMoreInfo: (id: string) => void;
onProteinLinkClick: () => void; onProteinLinkClick: () => void;
}; };
...@@ -112,6 +123,13 @@ export const TranscriptsListItemInfo = ( ...@@ -112,6 +123,13 @@ export const TranscriptsListItemInfo = (
const mainStyles = classNames(transcriptsListStyles.row, styles.listItemInfo); const mainStyles = classNames(transcriptsListStyles.row, styles.listItemInfo);
const midStyles = classNames(transcriptsListStyles.middle, styles.middle); const midStyles = classNames(transcriptsListStyles.middle, styles.middle);
const transcriptCCDS = transcript.external_references.find(
(xref) => xref.source.name === 'CCDS'
);
const hasRelevantMetadata = (
['gencode_basic', 'tsl', 'appris'] as const
).some((key) => transcript.metadata[key]);
const focusIdForUrl = buildFocusIdForUrl({ const focusIdForUrl = buildFocusIdForUrl({
type: 'gene', type: 'gene',
...@@ -138,6 +156,36 @@ export const TranscriptsListItemInfo = ( ...@@ -138,6 +156,36 @@ export const TranscriptsListItemInfo = (
return urlFor.browser({ genomeId: genomeId, focus: focusIdForUrl }); return urlFor.browser({ genomeId: genomeId, focus: focusIdForUrl });
}; };
const moreInfoContent = () => {
return (
<>
{hasRelevantMetadata && (
<div className={styles.moreInfoColumn}>
{transcript.metadata.gencode_basic?.label && (
<div>{transcript.metadata.gencode_basic?.label}</div>
)}
{transcript.metadata?.tsl && (
<div>{transcript.metadata.tsl?.label}</div>
)}
{transcript.metadata?.appris && (
<div>{transcript.metadata.appris?.label}</div>
)}
</div>
)}
{!!transcriptCCDS && (
<div className={styles.moreInfoColumn}>
<ExternalReference
classNames={{ label: styles.normalText }}
label={'CCDS'}
to={transcriptCCDS.url}
linkText={transcriptCCDS.accession_id}
/>
</div>
)}
</>
);
};
return ( return (
<div className={mainStyles}> <div className={mainStyles}>
<div className={transcriptsListStyles.left}></div> <div className={transcriptsListStyles.left}></div>
...@@ -170,7 +218,18 @@ export const TranscriptsListItemInfo = ( ...@@ -170,7 +218,18 @@ export const TranscriptsListItemInfo = (
</div> </div>
</div> </div>
<div className={styles.moreInformation}>More information</div> {(hasRelevantMetadata || !!transcriptCCDS) && (
<ShowHide
onClick={() => props.toggleTranscriptMoreInfo(transcript.stable_id)}
label="More information"
isExpanded={props.expandMoreInfo}
classNames={{ wrapper: styles.moreInformationLink }}
/>
)}
{props.expandMoreInfo && (
<div className={styles.moreInformation}>{moreInfoContent()}</div>
)}
<ShowHide <ShowHide
onClick={() => props.toggleTranscriptDownload(transcript.stable_id)} onClick={() => props.toggleTranscriptDownload(transcript.stable_id)}
...@@ -212,6 +271,7 @@ const renderInstantDownload = ({ ...@@ -212,6 +271,7 @@ const renderInstantDownload = ({
const mapDispatchToProps = { const mapDispatchToProps = {
toggleTranscriptDownload, toggleTranscriptDownload,
toggleTranscriptMoreInfo,
onProteinLinkClick: clearExpandedProteins onProteinLinkClick: clearExpandedProteins
}; };
......
...@@ -109,30 +109,25 @@ const createTranscriptWithSmallestExons = () => { ...@@ -109,30 +109,25 @@ const createTranscriptWithSmallestExons = () => {
const createMANETranscript = () => { const createMANETranscript = () => {
const transcript = createTranscript(); const transcript = createTranscript();
transcript.metadata = { transcript.metadata.canonical = {
canonical: { label: 'Ensembl canonical',
label: 'Ensembl canonical', value: true,
value: true, definition: faker.lorem.sentence()
definition: faker.lorem.sentence() };
}, transcript.metadata.mane = {
mane: { label: 'MANE Select',
label: 'MANE Select', value: 'select',
value: 'select', definition: faker.lorem.sentence()
definition: faker.lorem.sentence()
}
}; };
return transcript; return transcript;
}; };
const createOtherMANETranscript = () => { const createOtherMANETranscript = () => {
const transcript = createTranscript(); const transcript = createTranscript();
transcript.metadata = { transcript.metadata.mane = {
canonical: null, label: 'MANE Plus Clinical',
mane: { value: 'plus_clinical',
label: 'MANE Plus Clinical', definition: faker.lorem.sentence()
value: 'plus_clinical',
definition: faker.lorem.sentence()
}
}; };
return transcript; return transcript;
}; };
......
...@@ -51,6 +51,13 @@ export const getExpandedTranscriptDownloadIds = ( ...@@ -51,6 +51,13 @@ export const getExpandedTranscriptDownloadIds = (
return transcriptsSlice?.expandedDownloadIds ?? []; return transcriptsSlice?.expandedDownloadIds ?? [];
}; };
export const getExpandedTranscriptMoreInfoIds = (
state: RootState
): string[] => {
const transcriptsSlice = getSliceForGene(state);
return transcriptsSlice?.expandedMoreInfoIds ?? [];
};
export const getFilters = (state: RootState): Filters => { export const getFilters = (state: RootState): Filters => {
const transcriptsSlice = getSliceForGene(state); const transcriptsSlice = getSliceForGene(state);
return transcriptsSlice?.filters ?? {}; return transcriptsSlice?.filters ?? {};
......
...@@ -26,7 +26,8 @@ import { ...@@ -26,7 +26,8 @@ import {
import { import {
getExpandedTranscriptIds, getExpandedTranscriptIds,
getExpandedTranscriptDownloadIds getExpandedTranscriptDownloadIds,
getExpandedTranscriptMoreInfoIds
} from './geneViewTranscriptsSelectors'; } from './geneViewTranscriptsSelectors';
import { RootState } from 'src/store'; import { RootState } from 'src/store';
...@@ -42,6 +43,7 @@ export enum SortingRule { ...@@ -42,6 +43,7 @@ export enum SortingRule {
export type TranscriptsStatePerGene = { export type TranscriptsStatePerGene = {
expandedIds: string[]; expandedIds: string[];
expandedDownloadIds: string[]; expandedDownloadIds: string[];
expandedMoreInfoIds: string[];
filters: Filters; filters: Filters;
sortingRule: SortingRule; sortingRule: SortingRule;
}; };
...@@ -57,110 +59,129 @@ export type Filters = { [filter: string]: boolean }; ...@@ -57,110 +59,129 @@ export type Filters = { [filter: string]: boolean };
const defaultStatePerGene: TranscriptsStatePerGene = { const defaultStatePerGene: TranscriptsStatePerGene = {
expandedIds: [], expandedIds: [],
expandedDownloadIds: [], expandedDownloadIds: [],
expandedMoreInfoIds: [],
filters: {}, filters: {},
sortingRule: SortingRule.DEFAULT sortingRule: SortingRule.DEFAULT
}; };
export const setFilters = ( export const setFilters =
filters: Filters (filters: Filters): ThunkAction<void, any, null, Action<string>> =>
): ThunkAction<void, any, null, Action<string>> => ( (dispatch, getState: () => RootState) => {
dispatch, const state = getState();
getState: () => RootState const activeGenomeId = getEntityViewerActiveGenomeId(state);
) => { const activeEntityId = getEntityViewerActiveEntityId(state);
const state = getState(); if (!activeGenomeId || !activeEntityId) {
const activeGenomeId = getEntityViewerActiveGenomeId(state); return;
const activeEntityId = getEntityViewerActiveEntityId(state); }
if (!activeGenomeId || !activeEntityId) { dispatch(
return; transcriptsSlice.actions.updateFilters({
} activeGenomeId,
dispatch( activeEntityId,
transcriptsSlice.actions.updateFilters({ filters
activeGenomeId, })
activeEntityId, );
filters };
})
);
};
export const setSortingRule = ( export const setSortingRule =
sortingRule: SortingRule (sortingRule: SortingRule): ThunkAction<void, any, null, Action<string>> =>
): ThunkAction<void, any, null, Action<string>> => (