JBrowse 2 · Build Your Own examples

A track selector sidebar

A sidebar groups session.tracks by category, with a checkbox per track.

JBrowseLinearGenomeView opens the track selector JBrowse ships in a drawer: Widgets in a side drawer.

View source — 181 lines
import { useState } from 'react'

import { allSessionTracks } from '@jbrowse/core/util/tracks'
import {
  EmbedProvider,
  TrackStack,
  TrackToggle,
} from '@jbrowse/display-ui/embed'
import { useCreateViewState } from '@jbrowse/react-linear-genome-view2'
import { observer } from 'mobx-react'

import type { ViewModel } from '@jbrowse/react-linear-genome-view2'

type Session = ViewModel['session']

const variantTrack = {
  trackId: 'thousand_genomes_sv',
  name: 'Structural variants',
  category: ['Variants'],
  assemblyNames: ['hg38'],
  uri: 'https://ftp.1000genomes.ebi.ac.uk/vol1/ftp/data_collections/1000G_2504_high_coverage/working/20210124.SV_Illumina_Integration/1KGP_3202.gatksv_svtools_novelins.freeze_V3.wAF.vcf.gz',
  displayDefaults: { height: 90 },
}

function listTracks(session: Session) {
  return allSessionTracks(session).map(conf => {
    const trackId: string = conf.trackId
    const [category = 'Uncategorized'] =
      (conf.category as string[] | undefined) ?? []
    return {
      trackId,
      category,
      name: (conf.name as string | undefined) || trackId,
    }
  })
}

const Sidebar = observer(function Sidebar({ session }: { session: Session }) {
  const [filter, setFilter] = useState('')
  const { view } = session
  const needle = filter.trim().toLowerCase()
  const groups = Map.groupBy(
    listTracks(session).filter(e =>
      `${e.name} ${e.category}`.toLowerCase().includes(needle),
    ),
    e => e.category,
  )
  return (
    <div
      style={{
        width: 220,
        flex: 'none',
        display: 'flex',
        flexDirection: 'column',
        gap: 8,
        padding: 10,
        fontSize: '0.8rem',
      }}
    >
      <input
        aria-label="Filter tracks"
        placeholder="Filter tracks"
        value={filter}
        onChange={event => {
          setFilter(event.target.value)
        }}
      />
      {[...groups].map(([category, entries]) => (
        <div key={category}>
          <strong>{category}</strong>
          {entries.map(({ trackId, name }) => (
            <TrackToggle
              key={trackId}
              view={view}
              trackId={trackId}
              style={{ display: 'block' }}
            >
              {name}
            </TrackToggle>
          ))}
        </div>
      ))}
      <button
        type="button"
        disabled={!!session.getTrackById(variantTrack.trackId)}
        style={{ marginTop: 'auto' }}
        onClick={() => {
          session.addSessionTrackConf(variantTrack)
        }}
      >
        Add a variant track
      </button>
    </div>
  )
})

const TrackSelector = observer(function TrackSelector() {
  const state = useCreateViewState({
    assembly: {
      name: 'hg38',
      uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz',
      refNameAliases: {
        uri: 'https://jbrowse.org/genomes/GRCh38/hg38_aliases.txt',
      },
    },
    tracks: [
      {
        trackId: 'hg38_phylop',
        name: 'phyloP conservation',
        category: ['Signal'],
        uri: 'https://jbrowse.org/demos/phylop/hg38.phyloP100way.brca1.bw',
        displayDefaults: { height: 80, color: '#3a7ca5' },
      },
      {
        trackId: 'hg38_gnomad_genome_coverage',
        name: 'gnomAD genome coverage',
        category: ['Signal'],
        uri: 'https://hgdownload.soe.ucsc.edu/gbdb/hg38/gnomAD/coverage/v3-genome/gnomad.coverage.mean.bw',
        displayDefaults: { height: 80 },
      },
      {
        trackId: 'hg38_gnomad_exome_coverage',
        name: 'gnomAD exome coverage',
        category: ['Signal'],
        uri: 'https://hgdownload.soe.ucsc.edu/gbdb/hg38/gnomAD/coverage/v4-exome/gnomad.coverage.mean.bw',
        displayDefaults: { height: 80 },
      },
      {
        trackId: 'hg38_genes',
        name: 'RefSeq curated genes',
        category: ['Annotation'],
        uri: 'https://jbrowse.org/ucsc/hg38/ncbiRefSeqCurated.gff.gz',
        index: 'https://jbrowse.org/ucsc/hg38/ncbiRefSeqCurated.gff.gz.csi',
        displayDefaults: { height: 120 },
      },
      {
        trackId: 'hg38_segmental_dups',
        name: 'Segmental duplications',
        category: ['Annotation'],
        uri: 'https://jbrowse.org/ucsc/hg38/genomicSuperDups.bed.gz',
        index: 'https://jbrowse.org/ucsc/hg38/genomicSuperDups.bed.gz.csi',
        displayDefaults: { height: 100 },
      },
      {
        trackId: 'na12878_exome',
        name: 'NA12878 exome reads',
        category: ['Alignments'],
        uri: 'https://jbrowse.org/genomes/GRCh38/alignments/NA12878/NA12878.alt_bwamem_GRCh38DH.20150826.CEU.exome.cram',
        displayDefaults: { height: 150 },
      },
      {
        trackId: 'thousand_genomes_snvindels',
        name: '1000 Genomes variants',
        uri: 'https://jbrowse.org/genomes/GRCh38/variants/ALL.wgs.shapeit2_integrated_snvindels_v2a.GRCh38.27022019.sites.vcf.gz',
        displayDefaults: { height: 80 },
      },
    ],
    view: {
      loc: 'chr17:43,044,295..43,125,364',
      tracks: ['hg38_phylop', 'hg38_genes'],
    },
  })
  if (!state) {
    return null
  }
  const { session } = state
  return (
    <EmbedProvider session={session}>
      <div style={{ display: 'flex', minHeight: 330 }}>
        <Sidebar session={session} />
        <TrackStack
          view={session.view}
          trackIds={listTracks(session).map(e => e.trackId)}
          style={{ flex: 1, minWidth: 0 }}
        />
      </div>
    </EmbedProvider>
  )
})

export default TrackSelector