Unverified Commit 2b1b6fc1 authored by Andrey Azov's avatar Andrey Azov Committed by GitHub
Browse files

Use RTK-query to get gene data for track panel (#563)

parent c5089410
Pipeline #191401 passed with stages
in 4 minutes and 44 seconds
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
"express": "4.17.1", "express": "4.17.1",
"filesize": "7.0.0", "filesize": "7.0.0",
"graphql": "15.5.1", "graphql": "15.5.1",
"graphql-request": "3.5.0",
"http-proxy-middleware": "2.0.1", "http-proxy-middleware": "2.0.1",
"lodash": "4.17.21", "lodash": "4.17.21",
"query-string": "7.0.1", "query-string": "7.0.1",
...@@ -13297,8 +13298,7 @@ ...@@ -13297,8 +13298,7 @@
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
"dev": true
}, },
"node_modules/at-least-node": { "node_modules/at-least-node": {
"version": "1.0.0", "version": "1.0.0",
...@@ -15922,7 +15922,6 @@ ...@@ -15922,7 +15922,6 @@
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"dependencies": { "dependencies": {
"delayed-stream": "~1.0.0" "delayed-stream": "~1.0.0"
}, },
...@@ -18198,7 +18197,6 @@ ...@@ -18198,7 +18197,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true,
"engines": { "engines": {
"node": ">=0.4.0" "node": ">=0.4.0"
} }
...@@ -20298,6 +20296,17 @@ ...@@ -20298,6 +20296,17 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/extract-files": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz",
"integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==",
"engines": {
"node": "^10.17.0 || ^12.0.0 || >= 13.7.0"
},
"funding": {
"url": "https://github.com/sponsors/jaydenseric"
}
},
"node_modules/faker": { "node_modules/faker": {
"version": "5.5.3", "version": "5.5.3",
"resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz",
...@@ -20978,7 +20987,6 @@ ...@@ -20978,7 +20987,6 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"dev": true,
"dependencies": { "dependencies": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "^1.0.8", "combined-stream": "^1.0.8",
...@@ -21883,6 +21891,19 @@ ...@@ -21883,6 +21891,19 @@
"node": ">= 10.x" "node": ">= 10.x"
} }
}, },
"node_modules/graphql-request": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-3.5.0.tgz",
"integrity": "sha512-Io89QpfU4rqiMbqM/KwMBzKaDLOppi8FU8sEccCE4JqCgz95W9Q8bvxQ4NfPALLSMvg9nafgg8AkYRmgKSlukA==",
"dependencies": {
"cross-fetch": "^3.0.6",
"extract-files": "^9.0.0",
"form-data": "^3.0.0"
},
"peerDependencies": {
"graphql": "14.x || 15.x"
}
},
"node_modules/graphql-tag": { "node_modules/graphql-tag": {
"version": "2.12.4", "version": "2.12.4",
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.4.tgz", "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.4.tgz",
...@@ -52754,8 +52775,7 @@ ...@@ -52754,8 +52775,7 @@
"asynckit": { "asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
"dev": true
}, },
"at-least-node": { "at-least-node": {
"version": "1.0.0", "version": "1.0.0",
...@@ -54848,7 +54868,6 @@ ...@@ -54848,7 +54868,6 @@
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"requires": { "requires": {
"delayed-stream": "~1.0.0" "delayed-stream": "~1.0.0"
} }
...@@ -56671,8 +56690,7 @@ ...@@ -56671,8 +56690,7 @@
"delayed-stream": { "delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
"dev": true
}, },
"delegates": { "delegates": {
"version": "1.0.0", "version": "1.0.0",
...@@ -58342,6 +58360,11 @@ ...@@ -58342,6 +58360,11 @@
} }
} }
}, },
"extract-files": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz",
"integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ=="
},
"faker": { "faker": {
"version": "5.5.3", "version": "5.5.3",
"resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz",
...@@ -58867,7 +58890,6 @@ ...@@ -58867,7 +58890,6 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"dev": true,
"requires": { "requires": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "^1.0.8", "combined-stream": "^1.0.8",
...@@ -59560,6 +59582,16 @@ ...@@ -59560,6 +59582,16 @@
"resolved": "https://registry.npmjs.org/graphql/-/graphql-15.5.1.tgz", "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.5.1.tgz",
"integrity": "sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw==" "integrity": "sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw=="
}, },
"graphql-request": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-3.5.0.tgz",
"integrity": "sha512-Io89QpfU4rqiMbqM/KwMBzKaDLOppi8FU8sEccCE4JqCgz95W9Q8bvxQ4NfPALLSMvg9nafgg8AkYRmgKSlukA==",
"requires": {
"cross-fetch": "^3.0.6",
"extract-files": "^9.0.0",
"form-data": "^3.0.0"
}
},
"graphql-tag": { "graphql-tag": {
"version": "2.12.4", "version": "2.12.4",
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.4.tgz", "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.4.tgz",
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
"express": "4.17.1", "express": "4.17.1",
"filesize": "7.0.0", "filesize": "7.0.0",
"graphql": "15.5.1", "graphql": "15.5.1",
"graphql-request": "3.5.0",
"http-proxy-middleware": "2.0.1", "http-proxy-middleware": "2.0.1",
"lodash": "4.17.21", "lodash": "4.17.21",
"query-string": "7.0.1", "query-string": "7.0.1",
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
.value { .value {
grid-column: value; grid-column: value;
// width: calc(100vw - 320px - 45px - 46px - 220px); // FIXME: decide whether we want to explicitly set the max-width
& > span + span { & > span + span {
margin-left: 10px; margin-left: 10px;
...@@ -47,19 +48,24 @@ ...@@ -47,19 +48,24 @@
} }
.value .featureDetails { .transcriptQuality {
margin-left: 10px;
font-weight: $light;
}
.featureDetails {
display: flex; display: flex;
flex-wrap: wrap;
align-items: center; align-items: center;
white-space: nowrap; gap: 10px;
flex-wrap: nowrap; }
overflow: hidden;
& > span:not(:nth-child(1)) { .featureDetail {
margin-left: 40px; flex: 0 0 auto;
} display: flex;
& > span:nth-child(2) { align-items: center;
margin-left: 10px; flex-wrap: nowrap;
} padding-right: 30px;
} }
.featureSymbol { .featureSymbol {
...@@ -87,7 +93,7 @@ ...@@ -87,7 +93,7 @@
.downloadWrapper{ .downloadWrapper{
display: inline-block; display: inline-block;
margin-left: 40px; margin-left: 40px;
min-width: 600px; min-width: 300px;
position: relative; position: relative;
} }
......
...@@ -25,7 +25,6 @@ import { ...@@ -25,7 +25,6 @@ import {
getDisplayStableId, getDisplayStableId,
buildFocusIdForUrl buildFocusIdForUrl
} from 'src/shared/state/ens-object/ensObjectHelpers'; } from 'src/shared/state/ens-object/ensObjectHelpers';
import { getBrowserActiveEnsObject } from 'src/content/app/browser/browserSelectors';
import { getCommaSeparatedNumber } from 'src/shared/helpers/formatters/numberFormatter'; import { getCommaSeparatedNumber } from 'src/shared/helpers/formatters/numberFormatter';
import { getGeneName } from 'src/shared/helpers/formatters/geneFormatter'; import { getGeneName } from 'src/shared/helpers/formatters/geneFormatter';
...@@ -35,6 +34,9 @@ import { ...@@ -35,6 +34,9 @@ import {
getSplicedRNALength getSplicedRNALength
} from 'src/content/app/entity-viewer/shared/helpers/entity-helpers'; } from 'src/content/app/entity-viewer/shared/helpers/entity-helpers';
import { useGetTrackPanelGeneQuery } from 'src/content/app/browser/state/genomeBrowserApiSlice';
import { getBrowserActiveEnsObject } from 'src/content/app/browser/browserSelectors';
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 ExternalReference from 'src/shared/components/external-reference/ExternalReference'; import ExternalReference from 'src/shared/components/external-reference/ExternalReference';
...@@ -156,10 +158,16 @@ const GENE_AND_TRANSCRIPT_QUERY = gql` ...@@ -156,10 +158,16 @@ const GENE_AND_TRANSCRIPT_QUERY = gql`
const TranscriptSummary = () => { const TranscriptSummary = () => {
const ensObjectGene = useSelector(getBrowserActiveEnsObject) as EnsObjectGene; const ensObjectGene = useSelector(getBrowserActiveEnsObject) as EnsObjectGene;
const [shouldShowDownload, showDownload] = useState(false);
const transcriptTrack = ensObjectGene?.track?.child_tracks?.[0]; const { data: geneData } = useGetTrackPanelGeneQuery({
genomeId: ensObjectGene.genome_id,
geneId: ensObjectGene.stable_id
});
const [shouldShowDownload, showDownload] = useState(false); const canonicalTranscript = geneData?.gene.transcripts.find(
(transcript) => transcript.metadata.canonical
);
const { data, loading } = useQuery<{ const { data, loading } = useQuery<{
gene: Gene; gene: Gene;
...@@ -167,10 +175,10 @@ const TranscriptSummary = () => { ...@@ -167,10 +175,10 @@ const TranscriptSummary = () => {
}>(GENE_AND_TRANSCRIPT_QUERY, { }>(GENE_AND_TRANSCRIPT_QUERY, {
variables: { variables: {
geneId: ensObjectGene.stable_id, geneId: ensObjectGene.stable_id,
transcriptId: transcriptTrack?.stable_id, transcriptId: canonicalTranscript?.stable_id,
genomeId: ensObjectGene.genome_id genomeId: ensObjectGene.genome_id
}, },
skip: !transcriptTrack?.stable_id skip: !canonicalTranscript?.stable_id
}); });
if (loading) { if (loading) {
...@@ -224,22 +232,31 @@ const TranscriptSummary = () => { ...@@ -224,22 +232,31 @@ const TranscriptSummary = () => {
<div className={styles.label}>Transcript</div> <div className={styles.label}>Transcript</div>
<div className={styles.value}> <div className={styles.value}>
<div className={styles.featureDetails}> <div className={styles.featureDetails}>
<span className={styles.featureSymbol}>{stableId}</span> <div className={styles.featureDetail}>
<span className={styles.label}> <span className={styles.featureSymbol}>{stableId}</span>
<TranscriptQualityLabel metadata={metadata} /> <div className={styles.transcriptQuality}>
</span> <TranscriptQualityLabel metadata={metadata} />
</div>
</div>
{metadata.biotype && ( {metadata.biotype && (
<> <div className={styles.featureDetail}>
<span>{metadata.biotype.label}</span> <span>{metadata.biotype.label}</span>
<div className={styles.questionButton}> <div className={styles.questionButton}>
<QuestionButton helpText={metadata.biotype.definition} /> <QuestionButton helpText={metadata.biotype.definition} />
</div> </div>
</> </div>
)} )}
{transcript.slice.strand.code && ( {transcript.slice.strand.code && (
<span>{getStrandDisplayName(transcript.slice.strand.code)}</span> <div className={styles.featureDetail}>
<span>
{getStrandDisplayName(transcript.slice.strand.code)}
</span>
</div>
)} )}
<span>{getFormattedLocation(ensObjectGene.location)}</span> <div className={styles.featureDetail}>
<span>{getFormattedLocation(ensObjectGene.location)}</span>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -322,6 +339,7 @@ const TranscriptSummary = () => { ...@@ -322,6 +339,7 @@ const TranscriptSummary = () => {
}} }}
gene={{ id: gene.unversioned_stable_id }} gene={{ id: gene.unversioned_stable_id }}
theme="light" theme="light"
layout="vertical"
/> />
<CloseButton <CloseButton
className={styles.closeButton} className={styles.closeButton}
......
/**
* 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.
*/
import { createApi } from '@reduxjs/toolkit/query/react';
import { request, ClientError } from 'graphql-request';
import trackPanelGeneQuery from './queries/trackPanelGeneQuery';
import type { TrackPanelGene } from './types/track-panel-gene';
// FIXME: move some place more generic
const graphqlBaseQuery =
({ baseUrl }: { baseUrl: string }) =>
async ({ body }: { body: string }) => {
try {
const result = await request(baseUrl, body);
return { data: result };
} catch (error) {
if (error instanceof ClientError) {
return { error: { status: error.response.status, data: error } };
}
return { error: { status: 500, data: error } };
}
};
type GeneQueryParams = { genomeId: string; geneId: string };
export const genomeBrowserApiSlice = createApi({
reducerPath: 'genomeBrowserApi',
baseQuery: graphqlBaseQuery({
baseUrl: '/api/thoas'
}),
endpoints: (builder) => ({
getTrackPanelGene: builder.query<{ gene: TrackPanelGene }, GeneQueryParams>(
{
query: (params) => ({
body: trackPanelGeneQuery(params)
})
}
)
})
});
export const { getTrackPanelGene } = genomeBrowserApiSlice.endpoints;
export const { useGetTrackPanelGeneQuery } = genomeBrowserApiSlice;
/**
* 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.
*/
import { gql } from 'graphql-request';
type Params = {
genomeId: string;
geneId: string;
};
const trackPanelGeneQuery = (params: Params) => gql`
query TrackPanelGene {
gene(byId: { genome_id: "${params.genomeId}", stable_id: "${params.geneId}" }) {
stable_id
unversioned_stable_id
symbol
slice {
region {
name
}
location {
start
end
}
strand {
code
}
}
metadata {
biotype {
label
}
}
transcripts {
stable_id
slice {
location {
length
}
}
product_generating_contexts {
product_type
}
metadata {
biotype {
label
}
canonical {
value
label
}
mane {
value
label
ncbi_transcript {
id
url
}
}
}
}
}
}
`;
export default trackPanelGeneQuery;
/**
* 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.
*/
import { Pick2, Pick3 } from 'ts-multipick';
import type { FullGene } from 'src/shared/types/thoas/gene';
import type { FullTranscript } from 'src/shared/types/thoas/transcript';
type GeneFields = Pick<
FullGene,
'stable_id' | 'unversioned_stable_id' | 'symbol'
>;
type GeneMetadata = Pick3<FullGene, 'metadata', 'biotype', 'label'>;
type GeneSlice = Pick3<FullGene, 'slice', 'region', 'name'> &
Pick3<FullGene, 'slice', 'location', 'start' | 'end'> &
Pick3<FullGene, 'slice', 'strand', 'code'>;
type TranscriptFields = Pick<FullTranscript, 'stable_id'>;
type TranscriptSlice = Pick3<FullTranscript, 'slice', 'location', 'length'>;
type TranscriptPGCs = Pick3<
FullTranscript,
'product_generating_contexts',