Skip to content

Commit

Permalink
Add an initial, skeleton page for Variant Entity Viewer, with appropr…
Browse files Browse the repository at this point in the history
…iate routing (#1073)

- Added a new, so far mostly empty, view for the Entity Viewer that will evolve into a view for variants
- Updated server-side fetch code for EntityViewer pages — split it into gene data fetching
   vs variant data fetching
- Picking an allele (first alternative allele) and adding it as a url parameter
  • Loading branch information
azangru authored Jan 15, 2024
1 parent a48edaf commit daaea53
Show file tree
Hide file tree
Showing 12 changed files with 804 additions and 196 deletions.
101 changes: 21 additions & 80 deletions src/content/app/entity-viewer/EntityViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,35 +21,21 @@ import * as urlFor from 'src/shared/helpers/urlHelper';

import { useAppSelector, useAppDispatch } from 'src/store';
import useEntityViewerIds from 'src/content/app/entity-viewer/hooks/useEntityViewerIds';
import useEntityViewerUrlCheck from 'src/content/app/entity-viewer/hooks/useEntityViewerUrlChecks';

import { getBreakpointWidth } from 'src/global/globalSelectors';
import { getGenomeById } from 'src/shared/state/genome/genomeSelectors';
import {
isEntityViewerSidebarOpen,
getEntityViewerSidebarModalView
} from 'src/content/app/entity-viewer/state/sidebar/entityViewerSidebarSelectors';

import {
setActiveIds,
deleteActiveEntityIdAndSave
} from 'src/content/app/entity-viewer/state/general/entityViewerGeneralSlice';
import {
toggleSidebar,
initializeSidebar
} from 'src/content/app/entity-viewer/state/sidebar/entityViewerSidebarSlice';
import { initializeSidebar } from 'src/content/app/entity-viewer/state/sidebar/entityViewerSidebarSlice';

import { StandardAppLayout } from 'src/shared/components/layout';
import EntityViewerAppBar from './shared/components/entity-viewer-app-bar/EntityViewerAppBar';
import EntityViewerSidebarToolstrip from './shared/components/entity-viewer-sidebar/entity-viewer-sidebar-toolstrip/EntityViewerSidebarToolstrip';
import EntityViewerSidebarModal from 'src/content/app/entity-viewer/shared/components/entity-viewer-sidebar/entity-viewer-sidebar-modal/EntityViewerSidebarModal';
import EntityViewerTopbar from './shared/components/entity-viewer-topbar/EntityViewerTopbar';
import EntityViewerInterstitial from './interstitial/EntityViewerInterstitial';
import GeneView from './gene-view/GeneView';
import GeneViewSidebar from './gene-view/components/gene-view-sidebar/GeneViewSideBar';
import GeneViewSidebarTabs from './gene-view/components/gene-view-sidebar-tabs/GeneViewSidebarTabs';
import MissingGenomeError from 'src/shared/components/error-screen/url-errors/MissingGenomeError';
import MissingFeatureError from 'src/shared/components/error-screen/url-errors/MissingFeatureError';
import EntityViewerForGene from './EntityViewerForGene';
import EntityViewerForVariant from './EntityViewerForVariant';

import styles from './EntityViewer.module.css';

Expand All @@ -58,6 +44,7 @@ const EntityViewer = () => {
activeGenomeId,
genomeIdInUrl,
entityIdInUrl,
parsedEntityId,
isMissingGenomeId,
isMalformedEntityId
} = useEntityViewerIds();
Expand All @@ -80,13 +67,7 @@ const EntityViewer = () => {
navigate(urlFor.entityViewer({ genomeId: genomeIdInUrl }));
};

const renderEntityViewerRoutes = () => (
<Routes>
<Route index element={<EntityViewerInterstitial />} />
<Route path="/:genomeId" element={<EntityViewerInterstitial />} />
<Route path="/:genomeId/:entityId" element={<EntityViewerForGene />} />
</Routes>
);
const entityType = parsedEntityId?.type ?? 'unknown';

return (
<div className={styles.entityViewer}>
Expand All @@ -101,70 +82,30 @@ const EntityViewer = () => {
onContinue={openEntityViewerInterstitial}
/>
) : (
renderEntityViewerRoutes()
<Routes>
<Route index element={<EntityViewerInterstitial />} />
<Route path="/:genomeId" element={<EntityViewerInterstitial />} />
<Route
path="/:genomeId/:entityId"
element={<EntityViewerController entityType={entityType} />}
/>
</Routes>
)}
</div>
);
};

const EntityViewerForGene = () => {
const { activeGenomeId, activeEntityId } = useEntityViewerIds();
const {
genomeIdInUrl,
entityIdInUrl,
isFetching: isVerifyingUrl,
isMissingEntity
} = useEntityViewerUrlCheck();
const genome = useAppSelector((state) =>
getGenomeById(state, activeGenomeId ?? '')
);
const isSidebarOpen = useAppSelector(isEntityViewerSidebarOpen);
const isSidebarModalOpen = Boolean(
useAppSelector(getEntityViewerSidebarModalView)
);
const viewportWidth = useAppSelector(getBreakpointWidth);
const dispatch = useAppDispatch();
const navigate = useNavigate();
const EntityViewerController = (props: { entityType: string }) => {
const { entityType } = props;

const openEntityViewerInterstitial = () => {
dispatch(deleteActiveEntityIdAndSave());
navigate(urlFor.entityViewer({ genomeId: genomeIdInUrl }));
};

if (!activeGenomeId || !activeEntityId || isVerifyingUrl) {
if (entityType === 'gene') {
return <EntityViewerForGene />;
} else if (entityType === 'variant') {
return <EntityViewerForVariant />;
} else {
// this shouldn't happen
return null;
}

if (isMissingEntity) {
return (
<MissingFeatureError
featureId={entityIdInUrl as string}
genome={genome}
showTopBar={true}
onContinue={openEntityViewerInterstitial}
/>
);
}

const SideBarContent = isSidebarModalOpen ? (
<EntityViewerSidebarModal />
) : (
<GeneViewSidebar />
);

return (
<StandardAppLayout
mainContent={<GeneView />}
topbarContent={<EntityViewerTopbar />}
sidebarContent={SideBarContent}
sidebarNavigation={<GeneViewSidebarTabs />}
sidebarToolstripContent={<EntityViewerSidebarToolstrip />}
isSidebarOpen={isSidebarOpen}
onSidebarToggle={() => dispatch(toggleSidebar())}
isDrawerOpen={false}
viewportWidth={viewportWidth}
/>
);
};

const useEntityViewerRouting = () => {
Expand Down
109 changes: 109 additions & 0 deletions src/content/app/entity-viewer/EntityViewerForGene.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React from 'react';
import { useNavigate } from 'react-router-dom';

import * as urlFor from 'src/shared/helpers/urlHelper';

import { useAppSelector, useAppDispatch } from 'src/store';
import useEntityViewerIds from 'src/content/app/entity-viewer/hooks/useEntityViewerIds';
import useEntityViewerUrlCheck from 'src/content/app/entity-viewer/hooks/useEntityViewerUrlChecks';

import { getBreakpointWidth } from 'src/global/globalSelectors';
import { getGenomeById } from 'src/shared/state/genome/genomeSelectors';
import {
isEntityViewerSidebarOpen,
getEntityViewerSidebarModalView
} from 'src/content/app/entity-viewer/state/sidebar/entityViewerSidebarSelectors';

import { deleteActiveEntityIdAndSave } from 'src/content/app/entity-viewer/state/general/entityViewerGeneralSlice';
import { toggleSidebar } from 'src/content/app/entity-viewer/state/sidebar/entityViewerSidebarSlice';

import { StandardAppLayout } from 'src/shared/components/layout';
import EntityViewerSidebarToolstrip from './shared/components/entity-viewer-sidebar/entity-viewer-sidebar-toolstrip/EntityViewerSidebarToolstrip';
import EntityViewerSidebarModal from 'src/content/app/entity-viewer/shared/components/entity-viewer-sidebar/entity-viewer-sidebar-modal/EntityViewerSidebarModal';
import EntityViewerTopbar from './shared/components/entity-viewer-topbar/EntityViewerTopbar';
import GeneView from './gene-view/GeneView';
import GeneViewSidebar from './gene-view/components/gene-view-sidebar/GeneViewSideBar';
import GeneViewSidebarTabs from './gene-view/components/gene-view-sidebar-tabs/GeneViewSidebarTabs';
import MissingFeatureError from 'src/shared/components/error-screen/url-errors/MissingFeatureError';

const EntityViewerForGene = () => {
const { activeGenomeId, activeEntityId } = useEntityViewerIds();
const {
genomeIdInUrl,
entityIdInUrl,
isFetching: isVerifyingUrl,
isMissingEntity
} = useEntityViewerUrlCheck();
const genome = useAppSelector((state) =>
getGenomeById(state, activeGenomeId ?? '')
);
const isSidebarOpen = useAppSelector(isEntityViewerSidebarOpen);
const isSidebarModalOpen = Boolean(
useAppSelector(getEntityViewerSidebarModalView)
);
const viewportWidth = useAppSelector(getBreakpointWidth);
const dispatch = useAppDispatch();
const navigate = useNavigate();

const openEntityViewerInterstitial = () => {
dispatch(deleteActiveEntityIdAndSave());
navigate(urlFor.entityViewer({ genomeId: genomeIdInUrl }));
};

if (!activeGenomeId || !activeEntityId || isVerifyingUrl) {
return null;
}

if (isMissingEntity) {
return (
<MissingFeatureError
featureId={entityIdInUrl as string}
genome={genome}
showTopBar={true}
onContinue={openEntityViewerInterstitial}
/>
);
}

return (
<StandardAppLayout
mainContent={<GeneView />}
topbarContent={<EntityViewerTopbar />}
sidebarContent={
<SidebarContent isSidebarModalOpen={isSidebarModalOpen} />
}
sidebarNavigation={<GeneViewSidebarTabs />}
sidebarToolstripContent={<EntityViewerSidebarToolstrip />}
isSidebarOpen={isSidebarOpen}
onSidebarToggle={() => dispatch(toggleSidebar())}
isDrawerOpen={false}
viewportWidth={viewportWidth}
/>
);
};

const SidebarContent = (props: { isSidebarModalOpen: boolean }) => {
return props.isSidebarModalOpen ? (
<EntityViewerSidebarModal />
) : (
<GeneViewSidebar />
);
};

export default EntityViewerForGene;
68 changes: 68 additions & 0 deletions src/content/app/entity-viewer/EntityViewerForVariant.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React from 'react';

import { useAppSelector, useAppDispatch } from 'src/store';

import { getBreakpointWidth } from 'src/global/globalSelectors';
import {
isEntityViewerSidebarOpen,
getEntityViewerSidebarModalView
} from 'src/content/app/entity-viewer/state/sidebar/entityViewerSidebarSelectors';

import { toggleSidebar } from 'src/content/app/entity-viewer/state/sidebar/entityViewerSidebarSlice';

import { StandardAppLayout } from 'src/shared/components/layout';
import EntityViewerSidebarToolstrip from './shared/components/entity-viewer-sidebar/entity-viewer-sidebar-toolstrip/EntityViewerSidebarToolstrip';
import EntityViewerSidebarModal from 'src/content/app/entity-viewer/shared/components/entity-viewer-sidebar/entity-viewer-sidebar-modal/EntityViewerSidebarModal';
import VariantView from './variant-view/VariantView';

const EntityViewerForVariant = () => {
const isSidebarOpen = useAppSelector(isEntityViewerSidebarOpen);
const isSidebarModalOpen = Boolean(
useAppSelector(getEntityViewerSidebarModalView)
);

const viewportWidth = useAppSelector(getBreakpointWidth);
const dispatch = useAppDispatch();

return (
<StandardAppLayout
mainContent={<VariantView />}
topbarContent={<div>Variant summary for top bar goes here</div>}
sidebarContent={
<SidebarContent isSidebarModalOpen={isSidebarModalOpen} />
}
sidebarNavigation={null}
sidebarToolstripContent={<EntityViewerSidebarToolstrip />}
isSidebarOpen={isSidebarOpen}
onSidebarToggle={() => dispatch(toggleSidebar())}
isDrawerOpen={false}
viewportWidth={viewportWidth}
/>
);
};

const SidebarContent = (props: { isSidebarModalOpen: boolean }) => {
return props.isSidebarModalOpen ? (
<EntityViewerSidebarModal />
) : (
<div>Variant information for sidebar goes here</div>
);
};

export default EntityViewerForVariant;
Loading

0 comments on commit daaea53

Please sign in to comment.