gramene-search 2.35.1 → 2.36.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.
package/dist/index.js CHANGED
@@ -761,6 +761,69 @@ const $9d9aeaf9299e61a1$var$grameneParalogs = {
761
761
  },
762
762
  selectGrameneParalogs: (state)=>state.grameneParalogs
763
763
  };
764
+ // The GXA studies each gene is expressed in (Solr's expressed_in_gxa_attr_ss,
765
+ // e.g. ['E-MTAB-3839', 'JGI-SB-2']), keyed by gene id. The search results do
766
+ // not carry the field (it is not among /search's default fields), so the
767
+ // Expression tab asks for it gene by gene. Each entry is {pending: true} while
768
+ // the request is out, then {studies: [...]} ([] for a gene expressed in no
769
+ // study), or {studies: null} when the request failed (asked again next time).
770
+ const $9d9aeaf9299e61a1$var$grameneGeneStudies = {
771
+ name: 'grameneGeneStudies',
772
+ getReducer: ()=>(state = {}, { type: type, payload: payload })=>{
773
+ switch(type){
774
+ case 'GRAMENE_GENE_STUDIES_REQUESTED':
775
+ return {
776
+ ...state,
777
+ [payload]: {
778
+ pending: true
779
+ }
780
+ };
781
+ case 'GRAMENE_GENE_STUDIES_RECEIVED':
782
+ return {
783
+ ...state,
784
+ [payload.geneId]: {
785
+ studies: payload.studies
786
+ }
787
+ };
788
+ case 'GRAMENE_GENE_STUDIES_FAILED':
789
+ return {
790
+ ...state,
791
+ [payload]: {
792
+ studies: null
793
+ }
794
+ };
795
+ default:
796
+ return state;
797
+ }
798
+ },
799
+ doRequestGeneStudies: (geneId)=>({ dispatch: dispatch, store: store })=>{
800
+ const known = store.selectGrameneGeneStudies()[geneId];
801
+ if (known && (known.pending || known.studies)) return;
802
+ dispatch({
803
+ type: 'GRAMENE_GENE_STUDIES_REQUESTED',
804
+ payload: geneId
805
+ });
806
+ const q = encodeURIComponent(`id:"${geneId}"`);
807
+ fetch(`${store.selectGrameneAPI()}/search?q=${q}&fl=id,expressed_in_gxa_attr_ss&rows=1`).then((res)=>{
808
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
809
+ return res.json();
810
+ }).then((res)=>{
811
+ const doc = res.response.docs.find((d)=>d.id === geneId);
812
+ const studies = doc && doc.expressed_in_gxa_attr_ss || [];
813
+ dispatch({
814
+ type: 'GRAMENE_GENE_STUDIES_RECEIVED',
815
+ payload: {
816
+ geneId: geneId,
817
+ studies: studies
818
+ }
819
+ });
820
+ }).catch(()=>dispatch({
821
+ type: 'GRAMENE_GENE_STUDIES_FAILED',
822
+ payload: geneId
823
+ }));
824
+ },
825
+ selectGrameneGeneStudies: (state)=>state.grameneGeneStudies
826
+ };
764
827
  var // function selectFacetIDs(store, field) {
765
828
  // const path = `grameneGenes.data.facet_counts.facet_fields.${field}`;
766
829
  // if (_.has(store,path)) {
@@ -822,6 +885,7 @@ $9d9aeaf9299e61a1$export$2e2bcd8739ae039 = [
822
885
  $9d9aeaf9299e61a1$var$grameneTaxonomy,
823
886
  $9d9aeaf9299e61a1$var$grameneTaxDist,
824
887
  $9d9aeaf9299e61a1$var$grameneParalogs,
888
+ $9d9aeaf9299e61a1$var$grameneGeneStudies,
825
889
  $9d9aeaf9299e61a1$var$grameneGermplasm,
826
890
  $9d9aeaf9299e61a1$var$expressionSamples,
827
891
  $9d9aeaf9299e61a1$var$expressionStudies
@@ -6279,9 +6343,12 @@ var $d365d8c287ab0c94$export$2e2bcd8739ae039 = $d365d8c287ab0c94$var$ontologyEnr
6279
6343
  // controlled mode from this so a saved view restores the chosen sub-tab and
6280
6344
  // isoform.
6281
6345
  // - expression: the Expression detail's internal state — activeTab
6282
- // ('gene'|'paralogs'|'eFP'), atlasExperiment (selected GXA experiment id),
6283
- // barStudy (selected eFP/BAR study). Driven in controlled mode from this so a
6284
- // saved view restores the chosen sub-tab and study.
6346
+ // ('gene' (EBI Studies)|'jgi'|'paralogs'|'eFP'), atlasExperiment (selected
6347
+ // GXA experiment id for Paralogs), jgiExperiment (selected JGI study id),
6348
+ // jgiAxes ({[jgiExperiment]: {rowFactor, columnFactor}}, the factor grid's
6349
+ // axes per JGI study), barStudy (selected eFP/BAR study). Driven in
6350
+ // controlled mode from this so a saved view restores the chosen sub-tab,
6351
+ // studies and grid axes.
6285
6352
  // - primers: the Primers detail's PrimerDesignerState (gramene-primers), a
6286
6353
  // serializable {v: 1, mode, transcriptId, flankUp, flankDown, region,
6287
6354
  // target, included, excluded, junctionSpanning, avoidRepeats, preset,
@@ -6412,7 +6479,8 @@ const $7f865ea0feda21af$var$uiViewState = {
6412
6479
  return $7f865ea0feda21af$var$setSequences(state, payload.geneId, payload.patch);
6413
6480
  case 'UI_EXPRESSION_SET':
6414
6481
  // payload: { geneId, patch } -- merges into the expression slice
6415
- // (e.g. { activeTab }, { atlasExperiment }, { barStudy })
6482
+ // (e.g. { activeTab }, { atlasExperiment }, { jgiExperiment },
6483
+ // { jgiAxes } (the whole per-study map), { barStudy })
6416
6484
  return $7f865ea0feda21af$var$setExpression(state, payload.geneId, payload.patch);
6417
6485
  case 'UI_PRIMERS_SET':
6418
6486
  // payload: { geneId, state } -- replaces the primers slice with the
@@ -6593,7 +6661,9 @@ var $6048209b532f201d$export$2e2bcd8739ae039 = $6048209b532f201d$var$taxTreeView
6593
6661
  // fullscreen: boolean,
6594
6662
  // homology: { viewer, height, tbrowse: <ViewState> } | undefined,
6595
6663
  // sequences: { tab, tid, upstream, downstream } | undefined,
6596
- // expression: { activeTab, atlasExperiment, barStudy } | undefined,
6664
+ // expression: { activeTab, atlasExperiment, jgiExperiment,
6665
+ // jgiAxes: { [jgiExperiment]: { rowFactor, columnFactor } },
6666
+ // barStudy } | undefined,
6597
6667
  // primers: <gramene-primers PrimerDesignerState, {v: 1, ...}, without
6598
6668
  // the pasted `sequence`> | undefined
6599
6669
  // }, ...
@@ -8058,14 +8128,23 @@ function $59a241f3912e631e$export$1bc0f3596ef2efe9(gene) {
8058
8128
  }
8059
8129
 
8060
8130
 
8061
- // "All Studies" and "Paralogs" draw Expression Atlas heatmaps in the page with
8131
+ // "EBI Studies" and "Paralogs" draw Expression Atlas heatmaps in the page with
8062
8132
  // gramene-atlas-heatmap (warelab's React 18 fork of EBI's atlas-heatmap; the
8063
- // anatomogram comes from gramene-anatomogram). configuration.atlasUrl names the
8064
- // gramene-swagger /gxa/ instance to query; sites that leave it unset get the
8065
- // instance the old dev.gramene.org iframe widget fell back to.
8133
+ // anatomogram comes from gramene-anatomogram), and "JGI Studies" draws one JGI
8134
+ // study at a time as a grid of its factors (ExpressionFactorGrid from the same
8135
+ // package). configuration.atlasUrl names the gramene-swagger /gxa/ instance to
8136
+ // query; sites that leave it unset get the instance the old dev.gramene.org
8137
+ // iframe widget fell back to.
8066
8138
  const $9e29a4f60318db7a$var$DEFAULT_ATLAS_URL = 'https://data.sorghumbase.org/auth_testing/gxa/';
8067
8139
  const $9e29a4f60318db7a$var$EBI_GXA = 'https://www.ebi.ac.uk/gxa/';
8068
- const $9e29a4f60318db7a$var$genesOf = (query)=>query && query.gene ? query.gene.split(' ') : [];
8140
+ // A link's context names the genes in its query ({gene: 'A B'} or 'A B'), or
8141
+ // in gene (ExpressionFactorGrid shows one gene).
8142
+ const $9e29a4f60318db7a$var$genesOf = (context)=>{
8143
+ const query = context.query;
8144
+ const genes = (typeof query === 'string' ? query : query && query.gene) || context.gene;
8145
+ return genes ? genes.split(' ') : [];
8146
+ };
8147
+ const $9e29a4f60318db7a$var$isEbiUrl = (url)=>/^https?:\/\/www\.ebi\.ac\.uk\//i.test(url);
8069
8148
  const $9e29a4f60318db7a$var$editSearch = (url, edit)=>{
8070
8149
  try {
8071
8150
  const u = new URL(url);
@@ -8078,12 +8157,15 @@ const $9e29a4f60318db7a$var$editSearch = (url, edit)=>{
8078
8157
  // gramene-swagger echoes geneQuery back as [null,...], returns relative row uris
8079
8158
  // (genes/<id>) that only some instances redirect to EBI, and atlasUrl itself is
8080
8159
  // not a browsable page, so point the heatmap's links at EBI's Expression Atlas.
8081
- // undefined keeps the heatmap's own URL.
8160
+ // JGI studies link to Phytozome, which takes no geneQuery. undefined keeps the
8161
+ // heatmap's own URL.
8082
8162
  const $9e29a4f60318db7a$var$resolveUrl = (kind, url, context)=>{
8083
- const genes = $9e29a4f60318db7a$var$genesOf(context.query);
8084
- const withGenes = (params)=>params.set('geneQuery', JSON.stringify(genes.map((value)=>({
8163
+ const genes = $9e29a4f60318db7a$var$genesOf(context);
8164
+ const withGenes = (params)=>{
8165
+ if (genes.length) params.set('geneQuery', JSON.stringify(genes.map((value)=>({
8085
8166
  value: value
8086
8167
  }))));
8168
+ };
8087
8169
  switch(kind){
8088
8170
  case 'row':
8089
8171
  {
@@ -8093,9 +8175,10 @@ const $9e29a4f60318db7a$var$resolveUrl = (kind, url, context)=>{
8093
8175
  case 'atlas':
8094
8176
  return $9e29a4f60318db7a$var$EBI_GXA;
8095
8177
  case 'experiment':
8096
- return $9e29a4f60318db7a$var$editSearch(url, withGenes);
8178
+ return $9e29a4f60318db7a$var$isEbiUrl(url) ? $9e29a4f60318db7a$var$editSearch(url, withGenes) : undefined;
8097
8179
  case 'moreInformation':
8098
- return context.experiment ? $9e29a4f60318db7a$var$editSearch(url, withGenes) : `${$9e29a4f60318db7a$var$EBI_GXA}genes/${encodeURIComponent(genes[0])}`;
8180
+ if (context.experiment) return $9e29a4f60318db7a$var$isEbiUrl(url) ? $9e29a4f60318db7a$var$editSearch(url, withGenes) : undefined;
8181
+ return genes.length ? `${$9e29a4f60318db7a$var$EBI_GXA}genes/${encodeURIComponent(genes[0])}` : undefined;
8099
8182
  case 'download':
8100
8183
  return $9e29a4f60318db7a$var$editSearch(url, (params)=>params.delete('geneQuery'));
8101
8184
  default:
@@ -8110,14 +8193,26 @@ const $9e29a4f60318db7a$var$HEATMAP_OPTIONS = {
8110
8193
  resolveUrl: $9e29a4f60318db7a$var$resolveUrl
8111
8194
  };
8112
8195
  const $9e29a4f60318db7a$var$studyLabel = (e)=>`${e.type}:${e.description || e._id}`;
8196
+ const $9e29a4f60318db7a$var$jgiStudyLabel = (e)=>e.description ? `${e.name || e._id}: ${e.description}` : e.name || e._id;
8197
+ // The JGI studies (source 'JGI', e.g. the Mullet lab's JGI-SB-1..4 in sorghum)
8198
+ // have their own sub-tab, so the EBI Studies heatmap drops their rows. The GXA
8199
+ // API names a JGI row by the study's accession, by its name, or — for a study
8200
+ // split by a second factor — '<study name> - <value>'.
8201
+ const $9e29a4f60318db7a$var$isJgiStudy = (e)=>e.source === 'JGI';
8202
+ const $9e29a4f60318db7a$var$isDifferential = (e)=>/differential/i.test(e.type || '');
8203
+ const $9e29a4f60318db7a$var$makeIsJgiRow = (jgiStudies)=>{
8204
+ const accessions = new Set(jgiStudies.map((e)=>e._id));
8205
+ const names = jgiStudies.map((e)=>e.name).filter(Boolean);
8206
+ const isJgiName = (s)=>typeof s === 'string' && names.some((n)=>s === n || s.startsWith(`${n} - `));
8207
+ return (row)=>accessions.has(row.id) || isJgiName(row.id) || isJgiName(row.name);
8208
+ };
8113
8209
  const $9e29a4f60318db7a$var$Detail = (props)=>{
8114
8210
  const geneId = props.searchResult.id;
8115
8211
  const gene = props.geneDocs[geneId];
8116
- // User-selected view state (active sub-tab, chosen GXA experiment, chosen eFP
8117
- // study) lives in the uiViewState bundle keyed by geneId, so the shareable-
8118
- // views snapshot can round-trip it.
8212
+ // User-selected view state (active sub-tab, chosen GXA experiment, chosen JGI
8213
+ // study and its grid axes, chosen eFP study) lives in the uiViewState bundle
8214
+ // keyed by geneId, so the shareable-views snapshot can round-trip it.
8119
8215
  const expr = props.uiViewState && props.uiViewState.byGene[geneId] && props.uiViewState.byGene[geneId].expression || {};
8120
- const activeTab = expr.activeTab || 'gene';
8121
8216
  const atlasExperiment = expr.atlasExperiment || null;
8122
8217
  const setExpression = (patch)=>props.doSetExpressionState({
8123
8218
  geneId: geneId,
@@ -8125,10 +8220,11 @@ const $9e29a4f60318db7a$var$Detail = (props)=>{
8125
8220
  });
8126
8221
  // The expressionStudies resource is otherwise fetched only when a top-level
8127
8222
  // expression view (exprViz/expression/export) is on — but this per-gene
8128
- // Expression detail also needs it (the Paralogs sub-tab's experiment list and
8129
- // the atlasExperiment selection both derive from it). Fetch it here when it is
8130
- // missing or stale: it is persisted, and a browser that cached the list before
8131
- // studies were added to the release would otherwise keep offering the old list.
8223
+ // Expression detail also needs it (the Paralogs sub-tab's experiment list, the
8224
+ // atlasExperiment selection, the JGI Studies tab and the EBI Studies row
8225
+ // filter all derive from it). Fetch it here when it is missing or stale: it is
8226
+ // persisted, and a browser that cached the list before studies were added to
8227
+ // the release would otherwise keep offering the old list.
8132
8228
  (0, $gXNCa$react.useEffect)(()=>{
8133
8229
  if (props.expressionStudiesShouldUpdate && props.doFetchExpressionStudies) props.doFetchExpressionStudies();
8134
8230
  }, [
@@ -8136,6 +8232,18 @@ const $9e29a4f60318db7a$var$Detail = (props)=>{
8136
8232
  ]);
8137
8233
  const studies = props.expressionStudies && props.expressionStudies[Math.floor(gene.taxon_id / 1000)];
8138
8234
  const inGxa = props.searchResult.expressed_in_gxa_attr_ss;
8235
+ // The studies the gene is expressed in, which decide the JGI Studies tab and
8236
+ // the default sub-tab. The search results leave expressed_in_gxa_attr_ss out,
8237
+ // so unless the host's results carry it, it is asked for per gene: undefined
8238
+ // until it is in, null if the request failed.
8239
+ const geneStudiesEntry = props.grameneGeneStudies && props.grameneGeneStudies[geneId];
8240
+ const geneStudyIds = Array.isArray(inGxa) ? inGxa : geneStudiesEntry && !geneStudiesEntry.pending ? geneStudiesEntry.studies : undefined;
8241
+ (0, $gXNCa$react.useEffect)(()=>{
8242
+ if (!Array.isArray(inGxa)) props.doRequestGeneStudies(geneId);
8243
+ }, [
8244
+ geneId,
8245
+ Array.isArray(inGxa)
8246
+ ]);
8139
8247
  const experiments = (0, $gXNCa$react.useMemo)(()=>{
8140
8248
  if (!studies) return [];
8141
8249
  const inGxaSet = inGxa && new Set(inGxa);
@@ -8180,8 +8288,71 @@ const $9e29a4f60318db7a$var$Detail = (props)=>{
8180
8288
  paralogQuery
8181
8289
  ]);
8182
8290
  const knownExperiment = experiments.some((e)=>e._id === atlasExperiment);
8183
- // Only the active sub-tab's heatmap is mounted; the keys give each query its
8184
- // own fetch, zoom and filters.
8291
+ // The Paralogs selector keeps offering every study (as before: the search
8292
+ // results' expressed_in_gxa_attr_ss, which they do not carry, would narrow
8293
+ // it); the JGI Studies tab lists the JGI studies the gene is expressed in
8294
+ // (every JGI study of the taxon if that cannot be looked up). The EBI Studies
8295
+ // rows are filtered on every JGI study of the taxon, in case the gene's list
8296
+ // is incomplete. The filter is keyed on the studies' ids and names so that its
8297
+ // identity (and the heatmap's filtering) only changes when they do.
8298
+ const allJgiStudies = studies ? studies.filter($9e29a4f60318db7a$var$isJgiStudy) : [];
8299
+ const jgiStudies = (0, $gXNCa$react.useMemo)(()=>{
8300
+ if (!studies || geneStudyIds === undefined) return [];
8301
+ const mine = geneStudyIds && new Set(geneStudyIds);
8302
+ return studies.filter((e)=>$9e29a4f60318db7a$var$isJgiStudy(e) && (!mine || mine.has(e._id))).sort((a, b)=>$9e29a4f60318db7a$var$jgiStudyLabel(a) < $9e29a4f60318db7a$var$jgiStudyLabel(b) ? -1 : 1);
8303
+ }, [
8304
+ studies,
8305
+ geneStudyIds
8306
+ ]);
8307
+ // Whether the EBI Studies heatmap has anything to show: a study the gene is
8308
+ // expressed in that is neither a JGI study nor a differential one (that tab
8309
+ // draws baseline experiments).
8310
+ const hasEbiBaseline = (0, $gXNCa$react.useMemo)(()=>{
8311
+ if (!geneStudyIds || !studies) return true;
8312
+ const notEbiBaseline = new Set(studies.filter((e)=>$9e29a4f60318db7a$var$isJgiStudy(e) || $9e29a4f60318db7a$var$isDifferential(e)).map((e)=>e._id));
8313
+ return geneStudyIds.some((id)=>!notEbiBaseline.has(id));
8314
+ }, [
8315
+ studies,
8316
+ geneStudyIds
8317
+ ]);
8318
+ const allJgiKey = JSON.stringify(allJgiStudies.map((e)=>[
8319
+ e._id,
8320
+ e.name
8321
+ ]));
8322
+ const filterRows = (0, $gXNCa$react.useMemo)(()=>{
8323
+ if (allJgiStudies.length === 0) return undefined;
8324
+ const isJgiRow = $9e29a4f60318db7a$var$makeIsJgiRow(allJgiStudies);
8325
+ return (row)=>!isJgiRow(row);
8326
+ }, [
8327
+ allJgiKey
8328
+ ]);
8329
+ // The EBI Studies heatmap waits for the studies list, which names the JGI
8330
+ // studies its filter drops: drawn before the list is in (a first visit; the
8331
+ // list is persisted), it would show the JGI rows and then redraw without
8332
+ // them. If the list cannot be fetched, the heatmap is drawn unfiltered. It
8333
+ // also waits for the gene's studies, which pick the default sub-tab.
8334
+ const studiesSettled = (!!props.expressionStudies || !!props.expressionStudiesLastError) && geneStudyIds !== undefined;
8335
+ // With no saved sub-tab, a gene expressed in JGI studies only opens on JGI
8336
+ // Studies (its EBI Studies heatmap would be all below cutoff). A saved JGI
8337
+ // Studies tab falls back to EBI Studies once the studies are in and the gene
8338
+ // has none; until then the tab shows that it is loading.
8339
+ const defaultTab = studiesSettled && jgiStudies.length > 0 && !hasEbiBaseline ? 'jgi' : 'gene';
8340
+ const activeTab = expr.activeTab === 'jgi' && studiesSettled && jgiStudies.length === 0 ? 'gene' : expr.activeTab || defaultTab;
8341
+ const jgiExperiment = expr.jgiExperiment || (jgiStudies.length > 0 ? jgiStudies[0]._id : null);
8342
+ const knownJgiExperiment = jgiStudies.some((e)=>e._id === jgiExperiment);
8343
+ const jgiAxes = expr.jgiAxes || {};
8344
+ const jgiAxesOfStudy = jgiExperiment && jgiAxes[jgiExperiment] || {};
8345
+ const setJgiAxes = ({ rowFactor: rowFactor, columnFactor: columnFactor })=>setExpression({
8346
+ jgiAxes: {
8347
+ ...jgiAxes,
8348
+ [jgiExperiment]: {
8349
+ rowFactor: rowFactor,
8350
+ columnFactor: columnFactor
8351
+ }
8352
+ }
8353
+ });
8354
+ // Only the active sub-tab's heatmap (or factor grid) is mounted; the keys give
8355
+ // each query its own fetch, zoom and filters.
8185
8356
  return /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Tabs), {
8186
8357
  activeKey: activeTab,
8187
8358
  onSelect: (k)=>setExpression({
@@ -8223,17 +8394,70 @@ const $9e29a4f60318db7a$var$Detail = (props)=>{
8223
8394
  }, `${atlasUrl} ${atlasExperiment} ${paralogQuery}`)
8224
8395
  ]
8225
8396
  }, "gxaparalogs"),
8226
- /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tab), {
8397
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Tab), {
8227
8398
  tabClassName: "gxa",
8228
8399
  eventKey: "gene",
8229
- title: "All Studies",
8230
- children: activeTab === "gene" && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$grameneatlasheatmap.ExpressionAtlasHeatmap), {
8231
- ...$9e29a4f60318db7a$var$HEATMAP_OPTIONS,
8232
- atlasUrl: atlasUrl,
8233
- query: allStudiesQuery,
8234
- experiment: false
8235
- }, `${atlasUrl} all ${geneQuery}`)
8400
+ title: "EBI Studies",
8401
+ children: [
8402
+ activeTab === "gene" && !studiesSettled && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
8403
+ className: "text-muted small",
8404
+ role: "status",
8405
+ children: "Loading expression studies\u2026"
8406
+ }),
8407
+ activeTab === "gene" && studiesSettled && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$grameneatlasheatmap.ExpressionAtlasHeatmap), {
8408
+ ...$9e29a4f60318db7a$var$HEATMAP_OPTIONS,
8409
+ atlasUrl: atlasUrl,
8410
+ query: allStudiesQuery,
8411
+ experiment: false,
8412
+ filterRows: filterRows
8413
+ }, `${atlasUrl} all ${geneQuery}`)
8414
+ ]
8236
8415
  }, "gxa"),
8416
+ jgiStudies.length === 0 && activeTab === "jgi" && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tab), {
8417
+ tabClassName: "jgi",
8418
+ eventKey: "jgi",
8419
+ title: "JGI Studies",
8420
+ children: /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("div", {
8421
+ className: "text-muted small",
8422
+ role: "status",
8423
+ children: "Loading expression studies\u2026"
8424
+ })
8425
+ }, "jgi"),
8426
+ jgiStudies.length > 0 && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Tab), {
8427
+ tabClassName: "jgi",
8428
+ eventKey: "jgi",
8429
+ title: "JGI Studies",
8430
+ children: [
8431
+ /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsxs)((0, $gXNCa$reactbootstrap.Form).Select, {
8432
+ className: "mb-2",
8433
+ "aria-label": "JGI study selector",
8434
+ value: jgiExperiment,
8435
+ onChange: (e)=>setExpression({
8436
+ jgiExperiment: e.target.value
8437
+ }),
8438
+ children: [
8439
+ !knownJgiExperiment && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("option", {
8440
+ value: jgiExperiment,
8441
+ children: jgiExperiment
8442
+ }),
8443
+ jgiStudies.map((e)=>/*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)("option", {
8444
+ value: e._id,
8445
+ children: $9e29a4f60318db7a$var$jgiStudyLabel(e)
8446
+ }, e._id))
8447
+ ]
8448
+ }),
8449
+ activeTab === "jgi" && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$grameneatlasheatmap.ExpressionFactorGrid), {
8450
+ atlasUrl: atlasUrl,
8451
+ experiment: jgiExperiment,
8452
+ gene: geneQuery,
8453
+ rowFactor: jgiAxesOfStudy.rowFactor,
8454
+ columnFactor: jgiAxesOfStudy.columnFactor,
8455
+ onChangeFactors: setJgiAxes,
8456
+ linkTarget: "_blank",
8457
+ resolveUrl: $9e29a4f60318db7a$var$resolveUrl
8458
+ }, `${atlasUrl} ${jgiExperiment} ${geneQuery}`)
8459
+ ]
8460
+ }, "jgi"),
8237
8461
  (0, $59a241f3912e631e$export$1bc0f3596ef2efe9)(gene) && /*#__PURE__*/ (0, $gXNCa$reactjsxruntime.jsx)((0, $gXNCa$reactbootstrap.Tab), {
8238
8462
  tabClassName: "eFP",
8239
8463
  eventKey: "eFP",
@@ -8249,7 +8473,7 @@ const $9e29a4f60318db7a$var$Detail = (props)=>{
8249
8473
  ]
8250
8474
  });
8251
8475
  };
8252
- var $9e29a4f60318db7a$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectConfiguration', 'selectGrameneParalogs', 'selectExpressionStudies', 'selectExpressionStudiesShouldUpdate', 'selectUiViewState', 'doRequestParalogs', 'doFetchExpressionStudies', 'doSetExpressionState', $9e29a4f60318db7a$var$Detail);
8476
+ var $9e29a4f60318db7a$export$2e2bcd8739ae039 = (0, $gXNCa$reduxbundlerreact.connect)('selectConfiguration', 'selectGrameneParalogs', 'selectGrameneGeneStudies', 'selectExpressionStudies', 'selectExpressionStudiesShouldUpdate', 'selectExpressionStudiesLastError', 'selectUiViewState', 'doRequestParalogs', 'doRequestGeneStudies', 'doFetchExpressionStudies', 'doSetExpressionState', $9e29a4f60318db7a$var$Detail);
8253
8477
 
8254
8478
 
8255
8479