JBrowse 2 · Build Your Own examples

Coloring and grouping by a field

Two menus set the color and the row facet from any attribute, with a Legend.

In grammar-of-graphics terms, Color by is a color scale and Group rows by is a facet, each over a field. setColorScale and setFacet take the same { field, domain }. The color scale assigns the palette in domain order, and the facet stacks the rows for those values first. Grouping and lane order covers the facet on every display.

View source — 123 lines
import {
  EmbedProvider,
  Legend,
  Toolbar,
  TrackStack,
} from '@jbrowse/display-ui/embed'
import { useCreateViewState } from '@jbrowse/react-linear-genome-view2'
import { observer } from 'mobx-react'

import type { LinearBasicDisplayModel } from '@jbrowse/plugin-canvas'
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'

const fields = ['gene_biotype', 'strand', 'source']

const domains: Record<string, string[]> = {
  gene_biotype: ['protein_coding', 'lncRNA', 'pseudogene'],
}

const FieldSelect = observer(function FieldSelect({
  label,
  value,
  onChange,
}: {
  label: string
  value?: string
  onChange: (field?: string) => void
}) {
  return (
    <label>
      {label}{' '}
      <select
        value={value ?? ''}
        onChange={event => {
          onChange(event.target.value || undefined)
        }}
      >
        <option value="">nothing</option>
        {fields.map(field => (
          <option key={field} value={field}>
            {field}
          </option>
        ))}
      </select>
    </label>
  )
})

const Channels = observer(function Channels({
  view,
}: {
  view: LinearGenomeViewModel
}) {
  const display = view.getTrack('genes')?.activeDisplay as
    | LinearBasicDisplayModel
    | undefined
  if (!display) {
    return null
  }
  return (
    <Toolbar>
      <FieldSelect
        label="Color by"
        value={display.colorField?.field}
        onChange={field => {
          display.setColorScale(
            field ? { field, domain: domains[field] } : undefined,
          )
        }}
      />
      <FieldSelect
        label="Group rows by"
        value={display.facet?.field}
        onChange={field => {
          display.setFacet(
            field ? { field, domain: domains[field] } : undefined,
          )
        }}
      />
      <Legend view={view} trackId="genes" />
    </Toolbar>
  )
})

const ColorAndGroupByAField = observer(function ColorAndGroupByAField() {
  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: 'genes',
        name: 'NCBI RefSeq genes',
        uri: 'https://jbrowse.org/genomes/GRCh38/ncbi_refseq/GCA_000001405.15_GRCh38_full_analysis_set.refseq_annotation.sorted.gff.gz',
        displayDefaults: {
          height: 220,
          geneGlyphMode: 'longestCoding',
          color: { field: 'gene_biotype', domain: domains.gene_biotype },
          showLegend: false,
        },
      },
    ],
    view: {
      loc: 'chr17:42,900,000..43,400,000',
      tracks: ['genes'],
    },
  })
  if (!state) {
    return null
  }
  const { session } = state
  return (
    <EmbedProvider session={session}>
      <Channels view={session.view} />
      <TrackStack view={session.view} />
    </EmbedProvider>
  )
})

export default ColorAndGroupByAField