Unverified Commit 07975116 authored by Imran Salam's avatar Imran Salam Committed by GitHub
Browse files

Make track panel state of species indepedent (#136)

* fix track panel state not remembered for each species

* disable track panel bar icon click handler when track panel is closed

* change track panel state shape to be more natural

* make further improvements to the track panel state code

* converting track panel dumb components to connected components

* remove redundant comments

* convert component using redux hooks to a connected component

* remove unused browser open state

* refactor track panel state based on PR suggestions

* removed redundant FunctionComponent and OwnProps from track related components

* add null check to active track panel selector

* rename TrackType to TrackSet to be more meaningful

* rename everything browser tab to track panel tab

* remove premature optimisation hooks
parent c6109d2c
Pipeline #29495 passed with stages
in 4 minutes and 22 seconds
import React, { FunctionComponent, useEffect } from 'react';
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import browserMessagingService from 'src/content/app/browser/browser-messaging-service';
......@@ -19,31 +19,22 @@ import {
import styles from './BrowserCogList.scss';
type StateProps = {
type BrowserCogListProps = {
browserActivated: boolean;
browserCogList: number;
browserCogTrackList: CogList;
selectedCog: any;
};
type DispatchProps = {
updateCogList: (cogList: number) => void;
updateCogTrackList: (track_y: CogList) => void;
updateSelectedCog: (index: string) => void;
};
type OwnProps = {};
type BrowserCogListProps = StateProps & DispatchProps & OwnProps;
type BpaneScrollPayload = {
delta_y?: number;
track_y?: CogList;
};
const BrowserCogList: FunctionComponent<BrowserCogListProps> = (
props: BrowserCogListProps
) => {
const BrowserCogList = (props: BrowserCogListProps) => {
const { browserCogTrackList } = props;
const listenBpaneScroll = (payload: BpaneScrollPayload) => {
const { delta_y, track_y } = payload;
......@@ -93,14 +84,14 @@ const BrowserCogList: FunctionComponent<BrowserCogListProps> = (
) : null;
};
const mapStateToProps = (state: RootState): StateProps => ({
const mapStateToProps = (state: RootState) => ({
browserActivated: getBrowserActivated(state),
browserCogList: getBrowserCogList(state),
browserCogTrackList: getBrowserCogTrackList(state),
selectedCog: getBrowserSelectedCog(state)
});
const mapDispatchToProps: DispatchProps = {
const mapDispatchToProps = {
updateCogList,
updateCogTrackList,
updateSelectedCog
......
......@@ -12,10 +12,10 @@ import { BreakpointWidth } from 'src/global/globalConfig';
import BrowserReset from 'src/content/app/browser/browser-reset/BrowserReset';
import BrowserGenomeSelector from 'src/content/app/browser/browser-genome-selector/BrowserGenomeSelector';
import BrowserTabs from 'src/content/app/browser/browser-tabs/BrowserTabs';
import TrackPanelTabs from '../track-panel/track-panel-tabs/TrackPanelTabs';
import { ChrLocation } from '../browserState';
import { TrackType } from '../track-panel/trackPanelConfig';
import { TrackSet } from '../track-panel/trackPanelConfig';
import { createEnsObject } from 'tests/fixtures/ens-object';
......@@ -26,13 +26,13 @@ jest.mock(
'src/content/app/browser/browser-genome-selector/BrowserGenomeSelector',
() => () => <div>BrowserGenomeSelector</div>
);
jest.mock('src/content/app/browser/browser-tabs/BrowserTabs', () => () => (
jest.mock('../track-panel/track-panel-tabs/TrackPanelTabs', () => () => (
<div>BrowserReset</div>
));
describe('<BrowserBar />', () => {
const dispatchBrowserLocation: any = jest.fn();
const selectBrowserTab: any = jest.fn();
const selectTrackPanelTab: any = jest.fn();
const toggleBrowserNav: any = jest.fn();
const toggleDrawer: any = jest.fn();
const toggleGenomeSelector: any = jest.fn();
......@@ -48,11 +48,11 @@ describe('<BrowserBar />', () => {
drawerOpened: false,
genomeSelectorActive: false,
ensObject: createEnsObject(),
selectedBrowserTab: TrackType.GENOMIC,
selectedTrackPanelTab: TrackSet.GENOMIC,
trackPanelModalOpened: false,
trackPanelOpened: false,
dispatchBrowserLocation,
selectBrowserTab,
selectTrackPanelTab,
toggleBrowserNav,
toggleDrawer,
toggleGenomeSelector,
......@@ -60,7 +60,7 @@ describe('<BrowserBar />', () => {
isTrackPanelModalOpened: false,
isTrackPanelOpened: false,
closeDrawer: jest.fn(),
selectBrowserTabAndSave: jest.fn(),
selectTrackPanelTabAndSave: jest.fn(),
toggleTrackPanel: jest.fn()
};
......@@ -133,23 +133,23 @@ describe('<BrowserBar />', () => {
expect(renderedBrowserBar.find(BrowserNavigatorButton).length).toBe(0);
});
test('shows BrowserTabs if TrackPanel is open', () => {
test('shows TrackPanelTabs if TrackPanel is open', () => {
const renderedBrowserBar = mount(
renderBrowserBar({ isTrackPanelOpened: true })
);
expect(renderedBrowserBar.find(BrowserTabs).length).toBe(1);
expect(renderedBrowserBar.find(TrackPanelTabs).length).toBe(1);
});
test('shows BrowserTabs on a wide display even if TrackPanel is closed', () => {
test('shows TrackPanelTabs on a wide display even if TrackPanel is closed', () => {
const renderedBrowserBar = mount(renderBrowserBar());
expect(renderedBrowserBar.find(BrowserTabs).length).toBe(1);
expect(renderedBrowserBar.find(TrackPanelTabs).length).toBe(1);
});
test('hides BrowserTabs on small if TrackPanel is closed', () => {
test('hides TrackPanelTabs on small if TrackPanel is closed', () => {
const renderedBrowserBar = mount(
renderBrowserBar({ breakpointWidth: BreakpointWidth.MEDIUM })
);
expect(renderedBrowserBar.find(BrowserTabs).length).toBe(0);
expect(renderedBrowserBar.find(TrackPanelTabs).length).toBe(0);
});
});
});
......@@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import classNames from 'classnames';
import { browserInfoConfig, BrowserInfoItem } from '../browserConfig';
import { TrackType } from '../track-panel/trackPanelConfig';
import { TrackSet } from '../track-panel/trackPanelConfig';
import { getDisplayStableId } from 'src/ens-object/ensObjectHelpers';
import { getFormattedLocation } from 'src/shared/helpers/regionFormatter';
......@@ -22,12 +22,12 @@ import {
} from '../browserSelectors';
import { getIsDrawerOpened } from '../drawer/drawerSelectors';
import {
getSelectedBrowserTab,
getSelectedTrackPanelTab,
getIsTrackPanelModalOpened,
getIsTrackPanelOpened
} from '../track-panel/trackPanelSelectors';
import {
selectBrowserTabAndSave,
selectTrackPanelTabAndSave,
toggleTrackPanel
} from '../track-panel/trackPanelActions';
import { closeDrawer } from '../drawer/drawerActions';
......@@ -36,7 +36,7 @@ import { EnsObject } from 'src/ens-object/ensObjectTypes';
import BrowserReset from '../browser-reset/BrowserReset';
import BrowserGenomeSelector from '../browser-genome-selector/BrowserGenomeSelector';
import BrowserTabs from '../browser-tabs/BrowserTabs';
import TrackPanelTabs from '../track-panel/track-panel-tabs/TrackPanelTabs';
import { getBreakpointWidth } from 'src/global/globalSelectors';
import { BreakpointWidth } from 'src/global/globalConfig';
......@@ -56,12 +56,12 @@ type StateProps = {
isTrackPanelOpened: boolean;
genomeSelectorActive: boolean;
ensObject: EnsObject | null;
selectedBrowserTab: TrackType;
selectedTrackPanelTab: TrackSet;
};
type DispatchProps = {
closeDrawer: () => void;
selectBrowserTabAndSave: (selectedBrowserTab: TrackType) => void;
selectTrackPanelTabAndSave: (selectedTrackPanelTab: TrackSet) => void;
toggleBrowserNav: () => void;
toggleGenomeSelector: (genomeSelectorActive: boolean) => void;
toggleTrackPanel: (isTrackPanelOpened: boolean) => void;
......@@ -129,7 +129,7 @@ export const BrowserBar: FunctionComponent<BrowserBarProps> = (
props.toggleBrowserNav();
};
const shouldShowBrowserTabs =
const shouldShowTrackPanelTabs =
props.activeGenomeId &&
(props.isTrackPanelOpened ||
props.breakpointWidth === BreakpointWidth.LARGE);
......@@ -175,14 +175,14 @@ export const BrowserBar: FunctionComponent<BrowserBarProps> = (
)}
</dl>
</div>
{shouldShowBrowserTabs && (
<BrowserTabs
{shouldShowTrackPanelTabs && (
<TrackPanelTabs
closeDrawer={props.closeDrawer}
ensObject={props.ensObject}
isDrawerOpened={props.isDrawerOpened}
genomeSelectorActive={props.genomeSelectorActive}
selectBrowserTabAndSave={props.selectBrowserTabAndSave}
selectedBrowserTab={props.selectedBrowserTab}
selectTrackPanelTabAndSave={props.selectTrackPanelTabAndSave}
selectedTrackPanelTab={props.selectedTrackPanelTab}
toggleTrackPanel={props.toggleTrackPanel}
isTrackPanelModalOpened={props.isTrackPanelModalOpened}
isTrackPanelOpened={props.isTrackPanelOpened}
......@@ -252,12 +252,12 @@ const mapStateToProps = (state: RootState): StateProps => ({
isDrawerOpened: getIsDrawerOpened(state),
isTrackPanelModalOpened: getIsTrackPanelModalOpened(state),
isTrackPanelOpened: getIsTrackPanelOpened(state),
selectedBrowserTab: getSelectedBrowserTab(state)
selectedTrackPanelTab: getSelectedTrackPanelTab(state)
});
const mapDispatchToProps: DispatchProps = {
closeDrawer,
selectBrowserTabAndSave,
selectTrackPanelTabAndSave,
toggleBrowserNav,
toggleGenomeSelector,
toggleTrackPanel
......
import { BrowserStorageService, StorageKeys } from './browser-storage-service';
import { ImageButtonStatus } from 'src/shared/image-button/ImageButton';
import { TrackType } from './track-panel/trackPanelConfig';
import { TrackSet } from './track-panel/trackPanelConfig';
const mockStorageService = {
get: jest.fn(),
......@@ -23,7 +23,7 @@ const trackListToggleStates = {
'gene-feat': false
};
const SELECTED_BROWSER_TAB = TrackType.VARIATION;
const SELECTED_TRACK_PANEL_TAB = TrackSet.VARIATION;
describe('BrowserStorageService', () => {
afterEach(() => {
......@@ -138,22 +138,22 @@ describe('BrowserStorageService', () => {
});
});
describe('.getSelectedBrowserTab()', () => {
describe('.getSelectedTrackPanelTab()', () => {
it('gets saved selected browser tab from storage service', () => {
jest
.spyOn(mockStorageService, 'get')
.mockImplementation(() => SELECTED_BROWSER_TAB);
.mockImplementation(() => SELECTED_TRACK_PANEL_TAB);
const browserStorageService = new BrowserStorageService(
mockStorageService
);
const result = browserStorageService.getSelectedBrowserTab();
const result = browserStorageService.getSelectedTrackPanelTab();
expect(mockStorageService.get).toHaveBeenCalledWith(
StorageKeys.SELECTED_BROWSER_TAB
StorageKeys.SELECTED_TRACK_PANEL_TAB
);
expect(result).toEqual(SELECTED_BROWSER_TAB);
expect(result).toEqual(SELECTED_TRACK_PANEL_TAB);
mockStorageService.get.mockRestore();
});
......@@ -164,7 +164,7 @@ describe('BrowserStorageService', () => {
const browserStorageService = new BrowserStorageService(
mockStorageService
);
const result = browserStorageService.getSelectedBrowserTab();
const result = browserStorageService.getSelectedTrackPanelTab();
expect(result).toEqual({});
......@@ -172,20 +172,20 @@ describe('BrowserStorageService', () => {
});
});
describe('.updateSelectedBrowserTab()', () => {
describe('.updateSelectedTrackPanelTab()', () => {
it('updates selected browser tab via storage service', () => {
const browserStorageService = new BrowserStorageService(
mockStorageService
);
browserStorageService.updateSelectedBrowserTab({
homo_sapiens38: TrackType.EXPRESSION
browserStorageService.updateSelectedTrackPanelTab({
homo_sapiens38: TrackSet.EXPRESSION
});
expect(mockStorageService.update).toHaveBeenCalledWith(
StorageKeys.SELECTED_BROWSER_TAB,
StorageKeys.SELECTED_TRACK_PANEL_TAB,
{
homo_sapiens38: TrackType.EXPRESSION
homo_sapiens38: TrackSet.EXPRESSION
}
);
});
......
......@@ -3,7 +3,7 @@ import storageService, {
} from 'src/services/storage-service';
import {
TrackStates,
TrackType,
TrackSet,
TrackToggleStates
} from './track-panel/trackPanelConfig';
import { ChrLocations } from './browserState';
......@@ -15,7 +15,7 @@ export enum StorageKeys {
DEFAULT_CHR_LOCATION = 'browser.defaultChrLocation',
TRACK_STATES = 'browser.trackStates',
TRACK_LIST_TOGGLE_STATES = 'browser.trackListToggleStates',
SELECTED_BROWSER_TAB = 'browser.selectedBrowserTab'
SELECTED_TRACK_PANEL_TAB = 'browser.selectedTrackPanelTab'
}
export class BrowserStorageService {
......@@ -75,16 +75,16 @@ export class BrowserStorageService {
);
}
public getSelectedBrowserTab() {
return this.storageService.get(StorageKeys.SELECTED_BROWSER_TAB) || {};
public getSelectedTrackPanelTab(): { [genomeId: string]: TrackSet } {
return this.storageService.get(StorageKeys.SELECTED_TRACK_PANEL_TAB) || {};
}
public updateSelectedBrowserTab(selectedBrowserTabForGenome: {
[genomeId: string]: TrackType;
public updateSelectedTrackPanelTab(selectedTrackPanelTabForGenome: {
[genomeId: string]: TrackSet;
}) {
this.storageService.update(
StorageKeys.SELECTED_BROWSER_TAB,
selectedBrowserTabForGenome
StorageKeys.SELECTED_TRACK_PANEL_TAB,
selectedTrackPanelTabForGenome
);
}
}
......
......@@ -5,12 +5,9 @@ import pickBy from 'lodash/pickBy';
import { RootAction } from 'src/objects';
import * as browserActions from './browserActions';
import * as drawerActions from './drawer/drawerActions';
import * as trackPanelActions from './track-panel/trackPanelActions';
import {
BrowserState,
defaultBrowserState,
BrowserOpenState,
BrowserLocationState,
defaultBrowserLocationState,
BrowserNavState,
......@@ -28,20 +25,6 @@ export function browserInfo(
switch (action.type) {
case getType(browserActions.updateBrowserActivated):
return { ...state, browserActivated: action.payload };
case getType(trackPanelActions.toggleTrackPanel):
return {
...state,
browserOpenState: action.payload
? BrowserOpenState.SEMI_EXPANDED
: BrowserOpenState.EXPANDED
};
case getType(drawerActions.toggleDrawerForGenome):
return {
...state,
browserOpenState: action.payload
? BrowserOpenState.COLLAPSED
: BrowserOpenState.SEMI_EXPANDED
};
default:
return state;
}
......
import { RootState } from 'src/store';
import {
BrowserOpenState,
BrowserNavStates,
CogList,
ChrLocation,
......@@ -13,9 +12,6 @@ import { getEnsObjectById } from 'src/ens-object/ensObjectSelectors';
export const getBrowserActivated = (state: RootState): boolean =>
state.browser.browserInfo.browserActivated;
export const getBrowserOpenState = (state: RootState): BrowserOpenState =>
state.browser.browserInfo.browserOpenState;
export const getBrowserActiveGenomeId = (state: RootState) =>
state.browser.browserEntity.activeGenomeId;
......
......@@ -7,12 +7,6 @@ const activeEnsObjectIds = browserStorageService.getActiveEnsObjectIds();
const trackStates = browserStorageService.getTrackStates();
const chrLocations = browserStorageService.getChrLocation();
export enum BrowserOpenState {
EXPANDED = 'expanded',
SEMI_EXPANDED = 'semiExpanded',
COLLAPSED = 'collapsed'
}
// states are top, right, bottom, left (TRBL) and minus (zoom out) and plus (zoom in)
export type BrowserNavStates = [
boolean,
......@@ -33,12 +27,10 @@ export type CogList = {
export type BrowserState = Readonly<{
browserActivated: boolean;
browserOpenState: BrowserOpenState;
}>;
export const defaultBrowserState: BrowserState = {
browserActivated: false,
browserOpenState: BrowserOpenState.SEMI_EXPANDED
browserActivated: false
};
export type BrowserEntityState = Readonly<{
......
import React, { FunctionComponent } from 'react';
import React from 'react';
import { connect } from 'react-redux';
import { RootState } from 'src/store';
......@@ -20,20 +20,13 @@ import SnpIndels from './drawer-views/SnpIndels';
import { EnsObject } from 'src/ens-object/ensObjectTypes';
type StateProps = {
type DrawerProps = {
drawerView: string;
ensObject: EnsObject | null;
};
type DispatchProps = {
closeDrawer: () => void;
};
type OwnProps = {};
type DrawerProps = StateProps & DispatchProps & OwnProps;
const Drawer: FunctionComponent<DrawerProps> = (props: DrawerProps) => {
const Drawer = (props: DrawerProps) => {
const { ensObject, drawerView } = props;
if (!ensObject) {
......@@ -73,7 +66,7 @@ const Drawer: FunctionComponent<DrawerProps> = (props: DrawerProps) => {
);
};
const mapStateToProps = (state: RootState): StateProps => ({
const mapStateToProps = (state: RootState) => ({
drawerView: getDrawerView(state),
ensObject: getBrowserActiveEnsObject(state)
});
......
import React, { FunctionComponent, useEffect } from 'react';
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { useSpring, animated } from 'react-spring';
......@@ -8,37 +8,22 @@ import TrackPanelModal from './track-panel-modal/TrackPanelModal';
import Drawer from '../drawer/Drawer';
import { RootState } from 'src/store';
import {
updateTrackStatesAndSave,
UpdateTrackStatesPayload
} from 'src/content/app/browser/browserActions';
import {
toggleTrackPanel,
closeTrackPanelModal,
openTrackPanelModal
} from './trackPanelActions';
import {
toggleDrawer,
changeDrawerView,
closeDrawer
} from '../drawer/drawerActions';
import { toggleTrackPanel } from './trackPanelActions';
import {
getIsTrackPanelOpened,
getIsTrackPanelModalOpened,
getTrackPanelModalView,
getSelectedBrowserTab
getSelectedTrackPanelTab
} from './trackPanelSelectors';
import { getDrawerView, getIsDrawerOpened } from '../drawer/drawerSelectors';
import { getIsDrawerOpened } from '../drawer/drawerSelectors';
import {
getBrowserActivated,
getBrowserActiveGenomeId,
getBrowserActiveEnsObject,
getBrowserTrackStates
} from '../browserSelectors';
import { getLaunchbarExpanded } from 'src/header/headerSelectors';
import { getBreakpointWidth } from 'src/global/globalSelectors';
import { BreakpointWidth } from 'src/global/globalConfig';
import { TrackType, TrackStates } from './trackPanelConfig';
import { TrackSet, TrackStates } from './trackPanelConfig';
import { GenomeTrackCategory } from 'src/genome/genomeTypes';
import { getGenomeTrackCategoriesById } from 'src/genome/genomeSelectors';
......@@ -46,39 +31,21 @@ import { EnsObject } from 'src/ens-object/ensObjectTypes';
import styles from './TrackPanel.scss';
type StateProps = {
type TrackPanelProps = {
activeGenomeId: string | null;
breakpointWidth: BreakpointWidth;
browserActivated: boolean;
isDrawerOpened: boolean;
drawerView: string;
ensObject: EnsObject | null;
activeEnsObject: EnsObject | null;
isTrackPanelModalOpened: boolean;
isTrackPanelOpened: boolean;
launchbarExpanded: boolean;
selectedBrowserTab: TrackType;
selectedTrackPanelTab: TrackSet;
genomeTrackCategories: GenomeTrackCategory[];
trackPanelModalView: string;
trackStates: TrackStates;
};
type DispatchProps = {
changeDrawerView: (drawerView: string) => void;
closeDrawer: () => void;
closeTrackPanelModal: () => void;
openTrackPanelModal: (trackPanelModalView: string) => void;
toggleDrawer: (isDrawerOpened: boolean) => void;
toggleTrackPanel: (isTrackPanelOpened?: boolean) => void;
updateTrackStates: (payload: UpdateTrackStatesPayload) => void;
};
type OwnProps = {};
type TrackPanelProps = StateProps & DispatchProps & OwnProps;
const TrackPanel: FunctionComponent<TrackPanelProps> = (
props: TrackPanelProps
) => {
const TrackPanel = (props: TrackPanelProps) => {
const { isDrawerOpened } = props;
useEffect(() => {
......@@ -114,41 +81,11 @@ const TrackPanel: FunctionComponent<TrackPanelProps> = (
return props.activeGenomeId ? (
<animated.div style={trackAnimation}>
{props.browserActivated && props.ensObject ? (
{props.browserActivated && props.activeEnsObject ? (
<div className={styles.trackPanel}>
<TrackPanelBar
activeGenomeId={props.activeGenomeId}
closeDrawer={props.closeDrawer}
closeTrackPanelModal={props.closeTrackPanelModal}
isDrawerOpened={props.isDrawerOpened}
launchbarExpanded={props.launchbarExpanded}
openTrackPanelModal={props.openTrackPanelModal}
toggleTrackPanel={props.toggleTrackPanel}
isTrackPanelModalOpened={props.isTrackPanelModalOpened}
trackPanelModalView={props.trackPanelModalView}
isTrackPanelOpened={props.isTrackPanelOpened}
/>
<TrackPanelList
activeGenomeId={props.activeGenomeId}
isDrawerOpened={props.isDrawerOpened}
drawerView={props.drawerView}
launchbarExpanded={props.launchbarExpanded}
ensObject={props.ensObject}
selectedBrowserTab={props.selectedBrowserTab}
toggleDrawer={props.toggleDrawer}
trackStates={props.trackStates}