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

Refactor TrackPanelModal and TrackPanelBar (#726)

parent f3315958
Pipeline #273480 passed with stages
in 4 minutes and 44 seconds
......@@ -26,6 +26,8 @@ import Browser from './Browser';
import { createMockBrowserState } from 'tests/fixtures/browser';
import { BrowserSidebarModalView } from './state/browser-sidebar-modal/browserSidebarModalSlice';
jest.mock('./hooks/useBrowserRouting', () => () => ({
changeGenomeId: jest.fn()
}));
......@@ -41,9 +43,6 @@ jest.mock('./components/browser-image/BrowserImage', () => () => (
jest.mock('./components/browser-nav/BrowserNavBar', () => () => (
<div className="browserNavBar">BrowserNavBar</div>
));
jest.mock('./components/track-panel/TrackPanel', () => () => (
<div className="trackPanel">TrackPanel</div>
));
jest.mock('./components/browser-app-bar/BrowserAppBar', () => () => (
<div className="browserAppBar">BrowserAppBar</div>
));
......@@ -51,9 +50,13 @@ jest.mock('./components/interstitial/BrowserInterstitial', () => () => (
<div className="browserInterstitial">BrowserInterstitial</div>
));
jest.mock(
'./components/track-panel/components/track-panel-bar/TrackPanelBar',
() => () => <div className="trackPanelBar">TrackPanelBar</div>
'./components/browser-sidebar-toolstrip/BrowserSidebarToolstrip',
() => () =>
<div className="browserSidebarToolstrip">BrowserSidebarToolstrip</div>
);
jest.mock('./components/track-panel/TrackPanel', () => () => (
<div className="trackPanel">TrackPanel</div>
));
jest.mock(
'./components/track-panel/components/track-panel-tabs/TrackPanelTabs',
() => () => <div className="trackPanelTabs">TrackPanelTabs</div>
......@@ -61,7 +64,9 @@ jest.mock(
jest.mock('./components/drawer/Drawer', () => () => (
<div className="drawer">Drawer</div>
));
jest.mock('src/shared/components/in-app-search/InAppSearch', () => () => (
<div className="inAppSearch">Genome browser search</div>
));
jest.mock('src/gql-client', () => ({ client: jest.fn() }));
const mockState = createMockBrowserState();
......@@ -113,7 +118,11 @@ describe('<Browser />', () => {
expect(container.querySelectorAll('.trackPanel')).toHaveLength(0);
container = renderComponent({
state: mockState,
state: set(
`browser.browserSidebarModal.${activeGenomeId}.browserSidebarModalView`,
null,
mockState
),
url: '/genome-browser?focus=foo'
}).container;
......@@ -121,7 +130,37 @@ describe('<Browser />', () => {
expect(container.querySelectorAll('.trackPanel')).toHaveLength(1);
});
describe('BrowserNavBar', () => {
it('renders the browser sidebar modal when a modal is selected', () => {
const stateWithModalClosed = set(
`browser.browserSidebarModal.${activeGenomeId}.browserSidebarModalView`,
null,
mockState
);
let { container } = renderComponent({
state: stateWithModalClosed,
url: '/genome-browser?focus=foo'
});
expect(container.querySelector('.title')).toBeNull();
const stateWithModalOpened = set(
`browser.browserSidebarModal.${activeGenomeId}.browserSidebarModalView`,
BrowserSidebarModalView.SHARE,
mockState
);
container = renderComponent({
state: stateWithModalOpened,
url: '/genome-browser?focus=foo'
}).container;
expect(container.querySelector('.title')?.innerHTML).toEqual(
BrowserSidebarModalView.SHARE
);
});
describe('<BrowserNavBar />', () => {
const stateWithBrowserNavOpen = set(
`browser.browserNav.browserNavOpenState.${activeGenomeId}`,
true,
......@@ -133,6 +172,7 @@ describe('<Browser />', () => {
state: mockState,
url: '/genome-browser?focus=foo'
});
expect(container.querySelectorAll('.browserNavBar')).toHaveLength(0);
container = renderComponent({
......
......@@ -30,20 +30,18 @@ import { closeDrawer } from 'src/content/app/genome-browser/state/drawer/drawerS
import { getBrowserNavOpenState } from 'src/content/app/genome-browser/state/browser-nav/browserNavSelectors';
import { getBrowserActiveGenomeId } from './state/browser-general/browserGeneralSelectors';
import {
getIsTrackPanelModalOpened,
getIsTrackPanelOpened
} from 'src/content/app/genome-browser/state/track-panel/trackPanelSelectors';
import { getIsTrackPanelOpened } from 'src/content/app/genome-browser/state/track-panel/trackPanelSelectors';
import { getIsBrowserSidebarModalOpened } from './state/browser-sidebar-modal/browserSidebarModalSelectors';
import { getIsDrawerOpened } from 'src/content/app/genome-browser/state/drawer/drawerSelectors';
import { getBreakpointWidth } from 'src/global/globalSelectors';
import BrowserBar from './components/browser-bar/BrowserBar';
import BrowserImage from './components/browser-image/BrowserImage';
import BrowserNavBar from './components/browser-nav/BrowserNavBar';
import BrowserSidebarToolstrip from './components/browser-sidebar-toolstrip/BrowserSidebarToolstrip';
import BrowserSidebarModal from './components/browser-sidebar-modal/BrowserSidebarModal';
import TrackPanel from './components/track-panel/TrackPanel';
import TrackPanelBar from './components/track-panel/components/track-panel-bar/TrackPanelBar';
import TrackPanelTabs from './components/track-panel/components/track-panel-tabs/TrackPanelTabs';
import TrackPanelModal from './components/track-panel/components/track-panel-modal/TrackPanelModal';
import BrowserAppBar from './components/browser-app-bar/BrowserAppBar';
import Drawer from './components/drawer/Drawer';
import { StandardAppLayout } from 'src/shared/components/layout';
......@@ -58,7 +56,9 @@ export const Browser = () => {
const browserNavOpenState = useSelector(getBrowserNavOpenState);
const isDrawerOpened = useSelector(getIsDrawerOpened);
const isTrackPanelOpened = useSelector(getIsTrackPanelOpened);
const isTrackPanelModalOpened = useSelector(getIsTrackPanelModalOpened);
const isBrowserSidebarModalOpened = useSelector(
getIsBrowserSidebarModalOpened
);
const viewportWidth = useSelector(getBreakpointWidth);
const { search } = useLocation(); // from document.location provided by the router
......@@ -95,8 +95,8 @@ export const Browser = () => {
</>
);
const SideBarContent = isTrackPanelModalOpened ? (
<TrackPanelModal />
const SideBarContent = isBrowserSidebarModalOpened ? (
<BrowserSidebarModal />
) : (
<TrackPanel />
);
......@@ -109,7 +109,7 @@ export const Browser = () => {
mainContent={mainContent}
sidebarContent={SideBarContent}
sidebarNavigation={<TrackPanelTabs />}
sidebarToolstripContent={<TrackPanelBar />}
sidebarToolstripContent={<BrowserSidebarToolstrip />}
onSidebarToggle={onSidebarToggle}
topbarContent={<BrowserBar />}
isSidebarOpen={isTrackPanelOpened}
......
......@@ -26,7 +26,7 @@ import { ZmenuController } from 'src/content/app/genome-browser/components/zmenu
import { CircleLoader } from 'src/shared/components/loader';
import Overlay from 'src/shared/components/overlay/Overlay';
import { BROWSER_CONTAINER_ID } from 'src/content/app/genome-browser/constants/browser-constants';
import { BROWSER_CONTAINER_ID } from 'src/content/app/genome-browser/constants/browserConstants';
import {
getRegionEditorActive,
......
......@@ -23,18 +23,21 @@ import thunk from 'redux-thunk';
import set from 'lodash/fp/set';
import { createMockBrowserState } from 'tests/fixtures/browser';
import * as trackPanelActions from 'src/content/app/genome-browser/state/track-panel/trackPanelSlice';
import * as browserSidebarModalActions from 'src/content/app/genome-browser/state/browser-sidebar-modal/browserSidebarModalSlice';
import { TrackPanelModal, trackPanelModalTitles } from './TrackPanelModal';
import {
BrowserSidebarModal,
browserSidebarModalTitles
} from './BrowserSidebarModal';
import { TrackPanelModalView } from 'src/content/app/genome-browser/state/track-panel/trackPanelSlice';
import { BrowserSidebarModalView } from 'src/content/app/genome-browser/state/browser-sidebar-modal/browserSidebarModalSlice';
jest.mock('./modal-views/TrackPanelSearch', () => () => (
<div className="trackPanelSearch" />
jest.mock('./modal-views/SearchModal', () => () => (
<div className="searchModal" />
));
jest.mock('./modal-views/TrackPanelDownloads', () => () => (
<div className="trackPanelDownloads" />
jest.mock('./modal-views/DownloadsModal', () => () => (
<div className="downloadsModal" />
));
jest.mock(
......@@ -53,7 +56,7 @@ const renderComponent = (state: typeof mockState = mockState) => {
store = mockStore(state);
return render(
<Provider store={store}>
<TrackPanelModal />
<BrowserSidebarModal />
</Provider>
);
};
......@@ -65,38 +68,54 @@ describe('<TrackPanelModal />', () => {
describe('rendering', () => {
it('displays track pane modal view for search', () => {
const { container } = renderComponent();
const { activeGenomeId } = mockState.browser.browserGeneral;
const { container } = renderComponent(
set(
`browser.browserSidebarModal.${activeGenomeId}.browserSidebarModalView`,
BrowserSidebarModalView.SEARCH,
mockState
)
);
expect(container.querySelector('.title')?.innerHTML).toBe(
trackPanelModalTitles[TrackPanelModalView.SEARCH]
browserSidebarModalTitles[BrowserSidebarModalView.SEARCH]
);
});
it('displays track panel modal view for downloads', () => {
const { activeGenomeId } = mockState.browser.browserGeneral;
const { container } = renderComponent(
set(
`browser.trackPanel.${activeGenomeId}.trackPanelModalView`,
TrackPanelModalView.DOWNLOADS,
`browser.browserSidebarModal.${activeGenomeId}.browserSidebarModalView`,
BrowserSidebarModalView.DOWNLOADS,
mockState
)
);
expect(container.querySelector('.title')?.innerHTML).toBe(
trackPanelModalTitles[TrackPanelModalView.DOWNLOADS]
browserSidebarModalTitles[BrowserSidebarModalView.DOWNLOADS]
);
});
});
describe('behaviour', () => {
it('closes modal when close button is clicked', () => {
const { container } = renderComponent();
const { activeGenomeId } = mockState.browser.browserGeneral;
const { container } = renderComponent(
set(
`browser.browserSidebarModal.${activeGenomeId}.browserSidebarModalView`,
BrowserSidebarModalView.SHARE,
mockState
)
);
const closeButton = container.querySelector('button.closeButton');
jest.spyOn(trackPanelActions, 'closeTrackPanelModal');
jest.spyOn(browserSidebarModalActions, 'closeBrowserSidebarModal');
userEvent.click(closeButton as HTMLElement);
expect(trackPanelActions.closeTrackPanelModal).toHaveBeenCalledTimes(1);
expect(
browserSidebarModalActions.closeBrowserSidebarModal
).toHaveBeenCalledTimes(1);
});
});
});
......@@ -17,61 +17,62 @@
import React, { lazy, Suspense, LazyExoticComponent } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { getTrackPanelModalView } from 'src/content/app/genome-browser/state/track-panel/trackPanelSelectors';
import { closeTrackPanelModal } from 'src/content/app/genome-browser/state/track-panel/trackPanelSlice';
import { getBrowserSidebarModalView } from 'src/content/app/genome-browser/state/browser-sidebar-modal/browserSidebarModalSelectors';
import {
BrowserSidebarModalView,
closeBrowserSidebarModal
} from 'src/content/app/genome-browser/state/browser-sidebar-modal/browserSidebarModalSlice';
import { closeDrawer } from 'src/content/app/genome-browser/state/drawer/drawerSlice';
import SidebarModal from 'src/shared/components/layout/sidebar-modal/SidebarModal';
import { TrackPanelModalView } from 'src/content/app/genome-browser/state/track-panel/trackPanelSlice';
const trackPanelSidebarModals: Record<
const browserSidebarModals: Record<
string,
LazyExoticComponent<() => JSX.Element | null>
> = {
[TrackPanelModalView.SEARCH]: lazy(
() => import('./modal-views/TrackPanelSearch')
[BrowserSidebarModalView.SEARCH]: lazy(
() => import('./modal-views/SearchModal')
),
[TrackPanelModalView.TRACKS_MANAGER]: lazy(
() => import('./modal-views/TracksManager')
[BrowserSidebarModalView.TRACKS_MANAGER]: lazy(
() => import('./modal-views/TracksManagerModal')
),
[TrackPanelModalView.BOOKMARKS]: lazy(
() => import('./modal-views/TrackPanelBookmarks')
[BrowserSidebarModalView.BOOKMARKS]: lazy(
() => import('./modal-views/BookmarksModal')
),
[TrackPanelModalView.PERSONAL_DATA]: lazy(
() => import('./modal-views/PersonalData')
[BrowserSidebarModalView.PERSONAL_DATA]: lazy(
() => import('./modal-views/PersonalDataModal')
),
[TrackPanelModalView.SHARE]: lazy(
() => import('./modal-views/TrackPanelShare')
[BrowserSidebarModalView.SHARE]: lazy(
() => import('./modal-views/ShareModal')
),
[TrackPanelModalView.DOWNLOADS]: lazy(
() => import('./modal-views/TrackPanelDownloads')
[BrowserSidebarModalView.DOWNLOADS]: lazy(
() => import('./modal-views/DownloadsModal')
)
};
export const trackPanelModalTitles: { [key: string]: string } = {
[TrackPanelModalView.SEARCH]: 'Search',
[TrackPanelModalView.TRACKS_MANAGER]: 'Tracks manager',
[TrackPanelModalView.BOOKMARKS]: 'Previously viewed',
[TrackPanelModalView.PERSONAL_DATA]: 'Personal data',
[TrackPanelModalView.SHARE]: 'Share',
[TrackPanelModalView.DOWNLOADS]: 'Downloads'
export const browserSidebarModalTitles: { [key: string]: string } = {
[BrowserSidebarModalView.SEARCH]: 'Search',
[BrowserSidebarModalView.TRACKS_MANAGER]: 'Tracks manager',
[BrowserSidebarModalView.BOOKMARKS]: 'Previously viewed',
[BrowserSidebarModalView.PERSONAL_DATA]: 'Personal data',
[BrowserSidebarModalView.SHARE]: 'Share',
[BrowserSidebarModalView.DOWNLOADS]: 'Downloads'
};
export const TrackPanelModal = () => {
const trackPanelModalView = useSelector(getTrackPanelModalView);
export const BrowserSidebarModal = () => {
const browserSidebarModalView = useSelector(getBrowserSidebarModalView);
const dispatch = useDispatch();
if (!trackPanelModalView) {
if (!browserSidebarModalView) {
return null;
}
const ModalView = trackPanelSidebarModals[trackPanelModalView];
const modalViewTitle = trackPanelModalTitles[trackPanelModalView];
const ModalView = browserSidebarModals[browserSidebarModalView];
const modalViewTitle = browserSidebarModalTitles[browserSidebarModalView];
const onClose = () => {
dispatch(closeDrawer());
dispatch(closeTrackPanelModal());
dispatch(closeBrowserSidebarModal());
};
return (
......@@ -83,4 +84,4 @@ export const TrackPanelModal = () => {
);
};
export default TrackPanelModal;
export default BrowserSidebarModal;
@import 'src/styles/common';
.trackPanelBookmarks {
.bookmarksModal {
margin-left: 10px;
}
......@@ -31,7 +31,8 @@
.more {
margin-top: 25px;
span { /* stylelint-disable-line no-descending-specificity */
span {
/* stylelint-disable-line no-descending-specificity */
font-size: 12px;
cursor: pointer;
color: $blue;
......
......@@ -23,12 +23,16 @@ import userEvent from '@testing-library/user-event';
import faker from '@faker-js/faker';
import times from 'lodash/times';
import set from 'lodash/fp/set';
import merge from 'lodash/fp/merge';
import { changeDrawerViewForGenome } from 'src/content/app/genome-browser/state/drawer/drawerSlice';
import { TrackPanelBookmarks } from './TrackPanelBookmarks';
import { createMockBrowserState } from 'tests/fixtures/browser';
import { PreviouslyViewedObject } from 'src/content/app/genome-browser/state/track-panel/trackPanelSlice';
import { BookmarksModal } from './BookmarksModal';
import { PreviouslyViewedObject } from 'src/content/app/genome-browser/state/browser-bookmarks/browserBookmarksSlice';
import { BrowserSidebarModalView } from 'src/content/app/genome-browser/state/browser-sidebar-modal/browserSidebarModalSlice';
jest.mock('react-router-dom', () => ({
Link: (props: any) => (
......@@ -38,13 +42,6 @@ jest.mock('react-router-dom', () => ({
)
}));
const genomeId = 'triticum_aestivum_GCA_900519105_1';
const geneId = 'TraesCS3D02G273600';
const versionedStableId = 'TraesCS3D02G273600.1';
const region = '3D:2585940-2634711';
const geneObjectId = `${genomeId}:gene:${geneId}`;
const regionObjectId = `${genomeId}:region:${region}`;
const createRandomPreviouslyViewedObject = (): PreviouslyViewedObject => ({
genome_id: faker.random.word(),
object_id: `${faker.random.word()}:gene:${faker.datatype.uuid()}`,
......@@ -52,164 +49,116 @@ const createRandomPreviouslyViewedObject = (): PreviouslyViewedObject => ({
label: [faker.random.word(), faker.random.word()]
});
const previouslyViewedObjects = [
{
genome_id: genomeId,
object_id: geneObjectId,
type: 'gene',
label: [geneId, versionedStableId]
},
{
genome_id: genomeId,
object_id: regionObjectId,
type: 'region',
label: [region]
}
];
const example_objects = [
{
id: geneId,
type: 'gene'
},
{
id: region,
type: 'region'
}
];
const mockState = {
browser: {
browserGeneral: {
activeGenomeId: genomeId,
activeFocusObjectIds: {
[genomeId]: geneObjectId
}
},
trackPanel: {
[genomeId]: {
isTrackPanelModalOpened: true,
trackPanelModalView: '',
previouslyViewedObjects
}
}
},
drawer: {
[genomeId]: {
isDrawerOpened: false,
drawerView: null
}
},
focusObjects: {
[geneObjectId]: {
data: {
description: 'Heat shock protein 101',
genome_id: genomeId,
label: geneId,
location: {
chromosome: '3D',
end: 379539827,
start: 379535906
},
stable_id: geneId,
type: 'gene',
object_id: geneObjectId
}
},
[regionObjectId]: {
data: {
genome_id: genomeId,
label: region,
location: {
chromosome: '3D',
start: 2585940,
end: 2634711
},
type: 'region',
object_id: regionObjectId
}
}
},
genome: {
genomeInfo: {
genomeInfoData: {
[genomeId]: {
example_objects,
genome_id: genomeId
}
}
}
}
};
const mockState = createMockBrowserState();
const mockStore = configureMockStore([thunk]);
let store: ReturnType<typeof mockStore>;
const { activeGenomeId } = mockState.browser.browserGeneral;
const renderComponent = (state: typeof mockState = mockState) => {
store = mockStore(state);
return render(
<Provider store={store}>
<TrackPanelBookmarks />
<BookmarksModal />
</Provider>
);
};
describe('<TrackPanelBookmarks />', () => {
describe('<BookmarksModal />', () => {
beforeEach(() => {
jest.resetAllMocks();
});
it('renders previously viewed links', () => {
renderComponent();
const geneId = 'TraesCS3D02G273600';
const region = '3D:2585940-2634711';
const geneObjectId = `${activeGenomeId}:gene:${geneId}`;
const regionObjectId = `${activeGenomeId}:region:${region}`;
const nonRandomPreviouslyViewedObjects = [
{
genome_id: activeGenomeId,
object_id: geneObjectId,
type: 'gene',
label: [geneId, faker.random.word()]
},
{
genome_id: activeGenomeId,
object_id: regionObjectId,
type: 'region',
label: [region]
}
];
const newMockState = merge(mockState, {
browser: {