Commit 6c62eda6 authored by Imran Salam's avatar Imran Salam
Browse files

use API data to display browser bar and track panel info

parent 26ba957e
......@@ -21,6 +21,7 @@ import {
} from './browserState';
import {
changeBrowserLocation,
fetchObjectData,
toggleDrawer,
updateChrLocation,
updateBrowserNavStates,
......@@ -31,7 +32,8 @@ import {
getDrawerOpened,
getBrowserNavOpened,
getChrLocation,
getGenomeSelectorActive
getGenomeSelectorActive,
getExampleObjects
} from './browserSelectors';
import styles from './Browser.scss';
......@@ -44,6 +46,7 @@ type StateProps = {
browserOpenState: BrowserOpenState;
chrLocation: ChrLocation;
drawerOpened: boolean;
exampleObjects: {};
genomeSelectorActive: boolean;
};
......@@ -52,6 +55,7 @@ type DispatchProps = {
chrLocation: ChrLocation,
browserEl: HTMLDivElement
) => void;
fetchObjectData: (objSymbol: string) => void;
toggleDrawer: (drawerOpened: boolean) => void;
updateBrowserActivated: (browserActivated: boolean) => void;
updateBrowserNavStates: (browserNavStates: BrowserNavStates) => void;
......@@ -83,10 +87,20 @@ export const Browser: FunctionComponent<BrowserProps> = (
};
useEffect(() => {
const { location } = props.match.params;
const { location, objSymbol } = props.match.params;
const chrLocation = getChrLocationFromStr(location);
dispatchBrowserLocation(chrLocation);
let objectStableId = '';
Object.values(props.exampleObjects).forEach((exampleObject: any) => {
if (exampleObject.display_name === objSymbol) {
objectStableId = exampleObject.stable_id;
}
});
props.fetchObjectData(objectStableId);
}, []);
useEffect(() => {
......@@ -142,11 +156,13 @@ const mapStateToProps = (state: RootState): StateProps => ({
browserOpenState: getBrowserOpenState(state),
chrLocation: getChrLocation(state),
drawerOpened: getDrawerOpened(state),
exampleObjects: getExampleObjects(state),
genomeSelectorActive: getGenomeSelectorActive(state)
});
const mapDispatchToProps: DispatchProps = {
changeBrowserLocation,
fetchObjectData,
toggleDrawer,
updateBrowserActivated,
updateBrowserNavStates,
......
......@@ -18,7 +18,8 @@ import {
getDefaultChrLocation,
getGenomeSelectorActive,
getDrawerOpened,
getSelectedBrowserTab
getSelectedBrowserTab,
getObjectInfo
} from '../browserSelectors';
import { RootState } from 'src/rootReducer';
......@@ -35,6 +36,7 @@ type StateProps = {
defaultChrLocation: ChrLocation;
drawerOpened: boolean;
genomeSelectorActive: boolean;
objectInfo: any;
selectedBrowserTab: TrackType;
};
......@@ -55,6 +57,7 @@ export const BrowserBar: FunctionComponent<BrowserBarProps> = (
props: BrowserBarProps
) => {
const { navigator, reset } = browserInfoConfig;
const { objectInfo } = props;
const getBrowserInfoClasses = () => {
let classNames = styles.browserInfo;
......@@ -99,22 +102,24 @@ export const BrowserBar: FunctionComponent<BrowserBarProps> = (
<Fragment>
<dd className={styles.geneSymbol}>
<label>Gene</label>
<span className={styles.value}>BRAC2</span>
<span className={styles.value}>{objectInfo.obj_symbol}</span>
</dd>
<dd>
<label>Stable ID</label>
<span className={styles.value}>ENSG00000139618</span>
<span className={styles.value}>{objectInfo.stable_id}</span>
</dd>
<dd className="show-for-large">
<label>Spliced mRNA length</label>
<span className={styles.value}>84,793</span>
<span className={styles.value}>
{objectInfo.spliced_length}
</span>
<label>bp</label>
</dd>
<dd className={`show-for-large ${styles.nonLabelValue}`}>
protein coding
{objectInfo.bio_type}
</dd>
<dd className={`show-for-large ${styles.nonLabelValue}`}>
forward strand
{objectInfo.strand} strand
</dd>
</Fragment>
)}
......@@ -155,6 +160,7 @@ const mapStateToProps = (state: RootState): StateProps => ({
defaultChrLocation: getDefaultChrLocation(state),
drawerOpened: getDrawerOpened(state),
genomeSelectorActive: getGenomeSelectorActive(state),
objectInfo: getObjectInfo(state),
selectedBrowserTab: getSelectedBrowserTab(state)
});
......
......@@ -82,24 +82,43 @@ export const changeBrowserLocation = (
};
};
export const fetchObjectInfo = createAsyncAction(
'FETCH_OBJECT_INFO_REQUEST',
'FETCH_OBJECT_INFO_SUCCESS',
'FETCH_OBJECT_INFO_FAILURE'
export const fetchObject = createAsyncAction(
'FETCH_OBJECT_REQUEST',
'FETCH_OBJECT_SUCCESS',
'FETCH_OBJECT_FAILURE'
)<string, {}, Error>();
export function getObjectInfo(objectId: string) {
export const fetchObjectData = (objectId: string) => {
return (dispatch: Dispatch) => {
dispatch(fetchObjectInfo.request(objectId));
dispatch(fetchObject.request(objectId));
return fetch(`http://127.0.0.1:4000/browser/get_object_info/${objectId}`)
.then(
(response) => response.json(),
(error) => console.log('An error occurred.', error)
(error) => dispatch(fetchObject.failure(error))
)
.then((json) => dispatch(fetchObjectInfo.success(json)));
.then((json) => dispatch(fetchObject.success(json)));
};
}
};
export const fetchExampleObjects = createAsyncAction(
'FETCH_EXAMPLE_OBJECTS_REQUEST',
'FETCH_EXAMPLE_OBJECTS_SUCCESS',
'FETCH_EXAMPLE_OBJECTS_FAILURE'
)<null, {}, Error>();
export const fetchExampleObjectsData = () => {
return (dispatch: Dispatch) => {
dispatch(fetchExampleObjects.request(null));
return fetch('http://127.0.0.1:4000/browser/example_objects')
.then(
(response) => response.json(),
(error) => dispatch(fetchExampleObjects.failure(error))
)
.then((json) => dispatch(fetchExampleObjects.success(json)));
};
};
export const openTrackPanelModal = createAction(
'browser/open-track-panel-modal',
......
......@@ -7,8 +7,10 @@ import {
defaultBrowserState,
trackPanelState,
drawerState,
ObjectInfoState,
defaultObjectInfoState
ExampleObjects,
defaultExampleObjects,
ObjectState,
defaultObjectState
} from './browserState';
function browserInfo(
......@@ -60,31 +62,62 @@ function browserInfo(
}
}
function objectInfo(
state: ObjectInfoState = defaultObjectInfoState,
function exampleObjects(
state: ExampleObjects = defaultExampleObjects,
action: ActionType<typeof browserActions>
): ObjectInfoState {
): ExampleObjects {
switch (action.type) {
case getType(browserActions.fetchObjectInfo.failure):
return { ...state, browserInfoFetchFailed: true };
case getType(browserActions.fetchObjectInfo.request):
case getType(browserActions.fetchExampleObjects.failure):
return { ...state, exampleObjectsFetchFailed: true };
case getType(browserActions.fetchExampleObjects.request):
return {
...state,
browserInfoFetchFailed: false,
browserInfoFetching: true
exampleObjectsFetchFailed: false,
exampleObjectsFetching: true
};
case getType(browserActions.fetchObjectInfo.success):
type BrowserInfo = {
case getType(browserActions.fetchExampleObjects.success):
type Payload = {
examples: {};
};
const json = action.payload as Payload;
return {
...state,
exampleObjectsFetchFailed: false,
exampleObjectsFetching: false,
examples: json.examples
};
default:
return state;
}
}
function object(
state: ObjectState = defaultObjectState,
action: ActionType<typeof browserActions>
): ObjectState {
switch (action.type) {
case getType(browserActions.fetchObject.failure):
return { ...state, objectFetchFailed: true };
case getType(browserActions.fetchObject.request):
return {
...state,
objectFetchFailed: false,
objectFetching: true
};
case getType(browserActions.fetchObject.success):
type Payload = {
object_info: {};
track_categories: [];
};
const json = action.payload as BrowserInfo;
const json = action.payload as Payload;
return {
...state,
browserInfoFetchFailed: false,
browserInfoFetching: false,
objectFetchFailed: false,
objectFetching: false,
objectInfo: json.object_info,
trackCategories: json.track_categories
};
......@@ -95,5 +128,6 @@ function objectInfo(
export default combineReducers({
browserInfo,
objectInfo
exampleObjects,
object
});
......@@ -44,3 +44,18 @@ export const getGenomeSelectorActive = (state: RootState): boolean =>
export const getSelectedBrowserTab = (state: RootState): TrackType =>
state.browser.browserInfo.selectedBrowserTab;
export const getObjectFetchFailed = (state: RootState) =>
state.browser.object.objectFetchFailed;
export const getObjectFetching = (state: RootState) =>
state.browser.object.objectFetching;
export const getObjectInfo = (state: RootState) =>
state.browser.object.objectInfo;
export const getTrackCategories = (state: RootState): [] =>
state.browser.object.trackCategories;
export const getExampleObjects = (state: RootState) =>
state.browser.exampleObjects.examples;
......@@ -50,16 +50,30 @@ export const defaultBrowserState: BrowserState = {
trackPanelOpened: true
};
export type ObjectInfoState = Readonly<{
browserInfoFetchFailed: boolean;
browserInfoFetching: boolean;
export type ExampleObjects = Readonly<{
exampleObjectsFetchFailed: boolean;
exampleObjectsFetching: boolean;
examples: {
[key: string]: {};
};
}>;
export const defaultExampleObjects: ExampleObjects = {
exampleObjectsFetchFailed: false,
exampleObjectsFetching: false,
examples: {}
};
export type ObjectState = Readonly<{
objectFetchFailed: boolean;
objectFetching: boolean;
objectInfo: object;
trackCategories: [];
}>;
export const defaultObjectInfoState: ObjectInfoState = {
browserInfoFetchFailed: false,
browserInfoFetching: false,
export const defaultObjectState: ObjectState = {
objectFetchFailed: false,
objectFetching: false,
objectInfo: {},
trackCategories: []
};
......
......@@ -27,7 +27,9 @@ import {
getBrowserActivated,
getTrackPanelModalOpened,
getTrackPanelModalView,
getSelectedBrowserTab
getSelectedBrowserTab,
getTrackCategories,
getObjectInfo
} from '../browserSelectors';
import { getLaunchbarExpanded } from 'src/header/headerSelectors';
......@@ -43,7 +45,9 @@ type StateProps = {
drawerView: string;
breakpointWidth: BreakpointWidth;
launchbarExpanded: boolean;
objectInfo: any;
selectedBrowserTab: TrackType;
trackCategories: [];
trackPanelModalOpened: boolean;
trackPanelModalView: string;
trackPanelOpened: boolean;
......@@ -94,8 +98,10 @@ const TrackPanel: FunctionComponent<TrackPanelProps> = (
browserRef={props.browserRef}
drawerView={props.drawerView}
launchbarExpanded={props.launchbarExpanded}
objectInfo={props.objectInfo}
selectedBrowserTab={props.selectedBrowserTab}
toggleDrawer={props.toggleDrawer}
trackCategories={props.trackCategories}
updateDrawerView={props.changeDrawerView}
/>
{props.trackPanelModalOpened ? (
......@@ -119,7 +125,9 @@ const mapStateToProps = (state: RootState): StateProps => ({
drawerOpened: getDrawerOpened(state),
drawerView: getDrawerView(state),
launchbarExpanded: getLaunchbarExpanded(state),
objectInfo: getObjectInfo(state),
selectedBrowserTab: getSelectedBrowserTab(state),
trackCategories: getTrackCategories(state),
trackPanelModalOpened: getTrackPanelModalOpened(state),
trackPanelModalView: getTrackPanelModalView(state),
trackPanelOpened: getTrackPanelOpened(state)
......
......@@ -8,6 +8,7 @@ import React, {
import TrackPanelListItem from './TrackPanelListItem';
import {
TrackItemColour,
TrackPanelCategory,
TrackPanelItem,
trackPanelConfig,
......@@ -20,25 +21,27 @@ type TrackPanelListProps = {
browserRef: RefObject<HTMLDivElement>;
drawerView: string;
launchbarExpanded: boolean;
objectInfo: any;
selectedBrowserTab: TrackType;
toggleDrawer: (drawerOpened: boolean) => void;
trackCategories: [];
updateDrawerView: (drawerView: string) => void;
};
const TrackPanelList: FunctionComponent<TrackPanelListProps> = (
props: TrackPanelListProps
) => {
const [trackCategories, setTrackCategories] = useState(
trackPanelConfig.categories
);
const [currentTrackCategories, setCurrentTrackCategories] = useState([]);
useEffect(() => {
setTrackCategories(
trackPanelConfig.categories.filter(
(category: TrackPanelCategory) =>
category.types.indexOf(props.selectedBrowserTab) > -1
)
);
if (props.trackCategories.length > 0) {
setCurrentTrackCategories(
props.trackCategories.filter(
(category: TrackPanelCategory) =>
category.types.indexOf(props.selectedBrowserTab) > -1
)
);
}
}, [props.selectedBrowserTab]);
const changeDrawerView = useCallback(
......@@ -62,6 +65,35 @@ const TrackPanelList: FunctionComponent<TrackPanelListProps> = (
return `${styles.trackPanelList} ${heightClass}`;
};
const getMainTracks = () => {
const { objectInfo } = props;
let geneLabel = objectInfo.obj_symbol;
let transcriptLabel = objectInfo.associated_object.stable_id;
if (objectInfo.obj_type === 'transcript') {
geneLabel = objectInfo.associated_object.obj_symbol;
transcriptLabel = objectInfo.stable_id;
}
return {
additionalInfo: objectInfo.bio_type,
childTrackList: [
{
additionalInfo: objectInfo.bio_type,
color: 'BLUE',
id: 0.1,
label: transcriptLabel,
name: 'transcript',
selectedInfo: objectInfo.associated_object.selected_info
}
],
id: 0,
label: geneLabel,
name: 'gene'
};
};
const getTrackListItem = (track: TrackPanelItem) => (
<TrackPanelListItem
browserRef={props.browserRef}
......@@ -80,9 +112,9 @@ const TrackPanelList: FunctionComponent<TrackPanelListProps> = (
return (
<div className={getTrackPanelListClasses()}>
<section>
<dl>{getTrackListItem(trackPanelConfig.main)}</dl>
<dl>{getTrackListItem(getMainTracks())}</dl>
</section>
{trackCategories.map((category: TrackPanelCategory) => (
{currentTrackCategories.map((category: TrackPanelCategory) => (
<section key={category.name}>
<h4>{category.name}</h4>
<dl>
......
......@@ -6,7 +6,11 @@ import React, {
useState,
useCallback
} from 'react';
import { TrackPanelItem, trackPanelIconConfig } from '../trackPanelConfig';
import {
TrackItemColour,
TrackPanelItem,
trackPanelIconConfig
} from '../trackPanelConfig';
import chevronDownIcon from 'static/img/shared/chevron-down.svg';
import chevronUpIcon from 'static/img/shared/chevron-up.svg';
......@@ -27,7 +31,7 @@ const trackPrefix = '';
const TrackPanelListItem: FunctionComponent<TrackPanelListItemProps> = (
props: TrackPanelListItemProps
) => {
const [expanded, setExpanded] = useState(false);
const [expanded, setExpanded] = useState(true);
const [trackStatus, setTrackStatus] = useState('on');
const { browserRef, drawerView, track } = props;
......@@ -47,6 +51,17 @@ const TrackPanelListItem: FunctionComponent<TrackPanelListItemProps> = (
return classNames;
}, [drawerView]);
const getBoxClasses = (colour: any) => {
let classNames = styles.box;
if (colour) {
const colourValue = TrackItemColour[colour];
classNames += ` ${styles[colourValue]}`;
}
return classNames;
};
const changeDrawerViewHandler = () => {
props.updateDrawerView(props.track.name);
};
......@@ -76,9 +91,7 @@ const TrackPanelListItem: FunctionComponent<TrackPanelListItemProps> = (
<Fragment>
<dd className={getListItemClasses()}>
<label>
{track.color && (
<span className={`${styles.box} ${styles[track.color]}`} />
)}
{track.color && <span className={getBoxClasses(track.color)} />}
<span className={styles.mainText}>{track.label}</span>
{track.selectedInfo && (
<span className={styles.selectedInfo}>{track.selectedInfo}</span>
......
......@@ -4,7 +4,7 @@ import eyeOffIcon from 'static/img/track-panel/eye-off.svg';
import ellipsisOnIcon from 'static/img/track-panel/ellipsis-on.svg';
import ellipsisOffIcon from 'static/img/track-panel/ellipsis-off.svg';
enum TrackItemColour {
export enum TrackItemColour {
BLUE = 'blue',
DARK_GREY = 'darkGrey',
GREY = 'grey',
......
import React, { FunctionComponent } from 'react';
import React, { FunctionComponent, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import styles from './Home.scss';
import { fetchExampleObjectsData } from '../app/browser/browserActions';
import { getExampleObjects } from '../app/browser/browserSelectors';
import { RootState } from 'src/rootReducer';
type HomeProps = {};
type StateProps = {
exampleObjects: {};
};
type DispatchProps = {
fetchExampleObjectsData: () => void;
};
type OwnProps = {};
type HomeProps = StateProps & DispatchProps & OwnProps;
const Home: FunctionComponent<HomeProps> = (props: HomeProps) => {
useEffect(() => {
props.fetchExampleObjectsData();
}, []);
const exampleObjectsTotal = Object.keys(props.exampleObjects).length;
const getExampleObjectNode = (exampleObject: any) => {
const {
assembly,
chromosome,
display_name,
location,
object_type,
species,
stable_id
} = exampleObject;
const assemblyStr = `${assembly.name}_demo`;
const regionStr = `${chromosome}:${location.start}-${location.end}`;
const path = `/app/browser/${assemblyStr}/${display_name}/${regionStr}`;
return (
<dd key={stable_id}>
<Link to={path}>
{species} {object_type} {display_name}
</Link>
</dd>
);
};
return (
<div className={styles.home}>