JBrowse 2 · Build Your Own examples

Loading and error states

The view's loading, error and no-location states, and its notifications.

Loading, failing, and waiting for a location

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

import { SessionPaletteProvider } from '@jbrowse/core/ui/PaletteContext'
import { DisplayUIProvider } from '@jbrowse/display-ui'
import { TrackStack } 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'

const brca1 = 'chr17:43,044,295..43,125,364'

const scenarios = {
  'hg38 (2bit), which loads': {
    assembly: {
      name: 'hg38',
      uri: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/hg38.2bit',
    },
    loc: brca1,
  },
  'GRCh38 (bgzip FASTA), four files to fetch first': {
    assembly: {
      name: 'GRCh38',
      uri: 'https://jbrowse.org/genomes/GRCh38/fasta/hg38.prefix.fa.gz',
      refNameAliases: {
        uri: 'https://jbrowse.org/genomes/GRCh38/hg38_aliases.txt',
      },
    },
    loc: 'chr17:1..2,000,000',
  },
  'a sequence file behind a 404': {
    assembly: {
      name: 'hg38',
      uri: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/does-not-exist.2bit',
    },
    loc: brca1,
  },
  'a view with no location yet': {
    assembly: {
      name: 'hg38',
      uri: 'https://hgdownload.soe.ucsc.edu/goldenPath/hg38/bigZips/hg38.2bit',
    },
    loc: undefined,
  },
}

type Scenario = keyof typeof scenarios

const Notifications = observer(function Notifications({
  session,
}: {
  session: ViewModel['session']
}) {
  const latest = session.snackbarMessages.at(-1)
  return latest ? (
    <div
      role={latest.level === 'error' ? 'alert' : 'status'}
      style={{
        display: 'flex',
        gap: 8,
        marginTop: 8,
        padding: '4px 8px',
        fontSize: '0.8rem',
        background: 'color-mix(in srgb, CanvasText 8%, Canvas)',
      }}
    >
      <span style={{ flex: 1 }}>{latest.message}</span>
      <button
        type="button"
        aria-label="Dismiss"
        onClick={() => {
          session.popSnackbarMessage()
        }}
      >

      </button>
    </div>
  ) : null
})

const Browser = observer(function Browser({
  scenario,
}: {
  scenario: Scenario
}) {
  const { assembly, loc } = scenarios[scenario]
  const tracks = ['hg38_phylop']
  const state = useCreateViewState({
    assembly,
    tracks: [
      {
        trackId: 'hg38_phylop',
        name: 'phyloP 100-way conservation',
        uri: 'https://jbrowse.org/demos/phylop/hg38.phyloP100way.brca1.bw',
        displayDefaults: { height: 100, color: '#3a7ca5' },
      },
    ],
    init: loc ? { loc, tracks } : undefined,
  })
  if (!state) {
    return null
  }
  const { session } = state
  const { view } = session
  return (
    <SessionPaletteProvider session={session}>
      <DisplayUIProvider>
        <button
          type="button"
          style={{ marginBottom: 8 }}
          onClick={() => {
            void view.launchTrack('a_track_that_is_not_in_the_config')
          }}
        >
          Show a track that isn't in the config
        </button>
        {view.status.type === 'noRegions' ? (
          <div role="status" style={{ padding: '10px 12px' }}>
            Nothing has told this view where to look yet.{' '}
            <button
              type="button"
              onClick={() => {
                view.setLaunch({ assembly: assembly.name, loc: brca1, tracks })
              }}
            >
              Show {brca1}
            </button>
          </div>
        ) : (
          <TrackStack view={view} />
        )}
        <Notifications session={session} />
      </DisplayUIProvider>
    </SessionPaletteProvider>
  )
})

const LoadingAndErrors = observer(function LoadingAndErrors() {
  const [scenario, setScenario] = useState<Scenario>('hg38 (2bit), which loads')
  return (
    <div>
      <div
        style={{
          display: 'flex',
          flexWrap: 'wrap',
          gap: 14,
          paddingBottom: 8,
          fontSize: '0.85rem',
        }}
      >
        Load
        {Object.keys(scenarios).map(name => (
          <label key={name}>
            <input
              type="radio"
              name="scenario"
              checked={scenario === name}
              onChange={() => {
                setScenario(name as Scenario)
              }}
            />
            {name}
          </label>
        ))}
      </div>
      <Browser key={scenario} scenario={scenario} />
    </div>
  )
})

export default LoadingAndErrors