Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regulatory activity viewer: epigenome selection #1188

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -19,42 +19,74 @@ import noop from 'lodash/noop';
import { useAppSelector } from 'src/store';

import { getActiveGenomeId } from 'src/content/app/regulatory-activity-viewer/state/general/generalSelectors';
import { getMainContentBottomView } from 'src/content/app/regulatory-activity-viewer/state/ui/uiSelectors';

import usePreselectedEpigenomes from './hooks/usePreselectedEpigenomes';

import { StandardAppLayout } from 'src/shared/components/layout';
import RegionOverview from './components/region-overview/RegionOverview';
import RegionActivitySection from './components/region-activity-section/RegionActivitySection';
import ActivityViewerSidebar from './components/activity-viewer-sidebar/ActivityViewerSidebar';
import SidebarNavigation from './components/activity-viewer-sidebar/sidebar-navigation/SidebarNavigation';
import MainContentBottomViewControls from './components/main-content-bottom-view-controls/MainContentBottomViewControls';
import EpigenomeSelectionModal from './components/epigenome-selection-modal/EpigenomeSelectionModal';
import SelectedEpigenomes from './components/selected-epigenomes/SelectedEpigenomes';

const ActivityViewer = () => {
const activeGenomeId = useAppSelector(getActiveGenomeId);
usePreselectedEpigenomes();

return (
<StandardAppLayout
mainContent={<MainContent />}
sidebarContent={<ActivityViewerSidebar />}
mainContent={<MainContent genomeId={activeGenomeId} />}
sidebarContent={<ActivityViewerSidebar genomeId={activeGenomeId} />}
isSidebarOpen={true}
topbarContent={null}
sidebarNavigation={null}
topbarContent={<div />}
sidebarNavigation={<SidebarNavigation genomeId={activeGenomeId} />}
onSidebarToggle={noop}
viewportWidth={1800}
/>
);
};

const MainContent = () => {
const activeGenomeId = useAppSelector(getActiveGenomeId);

if (!activeGenomeId) {
const MainContent = ({ genomeId }: { genomeId: string | null }) => {
if (!genomeId) {
// this will be an interstitial in the future
return null;
}

return (
<div>
Hello activity viewer
<RegionOverview activeGenomeId={activeGenomeId} />
<div style={{ margin: '3rem 0' }} />
<RegionActivitySection activeGenomeId={activeGenomeId} />
<RegionOverview activeGenomeId={genomeId} />
{/* The spacer divs below are temporary */}
<div style={{ margin: '0.6rem 0' }} />
<MainContentBottomViewControls genomeId={genomeId} />
<div style={{ margin: '0.6rem 0' }} />
<MainContentBottom genomeId={genomeId} />
<div style={{ margin: '4rem 0' }} />
</div>
);
};

const MainContentBottom = ({ genomeId }: { genomeId: string }) => {
const activeView = useAppSelector((state) =>
getMainContentBottomView(state, genomeId)
);

return (
<>
{['epigenomes-list', 'epigenomes-selection'].includes(activeView) && (
<SelectedEpigenomes genomeId={genomeId} />
)}
{activeView === 'epigenomes-selection' && (
<EpigenomeSelectionModal genomeId={genomeId} />
)}
{activeView === 'dataviz' && (
<RegionActivitySection activeGenomeId={genomeId} />
)}
</>
);
};

export default ActivityViewer;
Original file line number Diff line number Diff line change
Expand Up @@ -14,59 +14,31 @@
* limitations under the License.
*/

import { useRegionOverviewQuery } from 'src/content/app/regulatory-activity-viewer/state/api/activityViewerApiSlice';
import { useAppSelector } from 'src/store';

import Sidebar from 'src/shared/components/layout/sidebar/Sidebar';
import RegulatoryFeatureLegend from './regulatory-feature-legend/RegulatoryFeatureLegend';

import type { OverviewRegion } from 'src/content/app/regulatory-activity-viewer/types/regionOverview';
import { getSidebarView } from 'src/content/app/regulatory-activity-viewer/state/ui/uiSelectors';

const ActivityViewerSidebar = () => {
const { currentData } = useRegionOverviewQuery();

if (!currentData) {
return null;
}
import Sidebar from 'src/shared/components/layout/sidebar/Sidebar';
import SidebarDefaultView from './sidebar-default-view/SidebarDefaultView';
import EpigenomeFiltersView from './epigenome-filters-view/EpigenomeFiltersView';

return (
<Sidebar>
<div>
<Genes genes={currentData.genes} />
<RegulatoryFeatureLegendSection
featureTypes={currentData.regulatory_features.feature_types}
/>
</div>
</Sidebar>
);
type Props = {
genomeId: string | null;
};

const Genes = (props: { genes: OverviewRegion['genes'] }) => {
const genes = props.genes.map((gene) => (
<div key={gene.stable_id}>
{gene.symbol}
{' '}
{gene.stable_id}
</div>
));

// TODO: change this into an accordion
return (
<div>
<div style={{ fontWeight: 'bold' }}>Genes</div>
{genes}
</div>
const ActivityViewerSidebar = (props: Props) => {
const { genomeId } = props;
const sidebarView = useAppSelector((state) =>
getSidebarView(state, genomeId ?? '')
);
};

const RegulatoryFeatureLegendSection = (props: {
featureTypes: OverviewRegion['regulatory_features']['feature_types'];
}) => {
// TODO: change this into an accordion
return (
<div style={{ marginTop: '1.5rem' }}>
<div style={{ fontWeight: 'bold' }}>Regulatory features</div>
<RegulatoryFeatureLegend featureTypes={props.featureTypes} />
</div>
<Sidebar>
{sidebarView === 'default' && <SidebarDefaultView />}
{sidebarView === 'epigenome-filters' && (
<EpigenomeFiltersView genomeId={genomeId} />
)}
</Sidebar>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.epigenomeFilter {
display: grid;
grid-template-columns: 1fr auto;
align-items: start;
padding: 0 20px;
}

.epigenomeFilter + .epigenomeFilter {
margin-top: 10px;
}

.epigenomeFilterRight {
display: flex;
align-items: end;
column-gap: 10px;
}

.epigenomeFilterRight button {
margin-top: 2px;
}

.epigenomesCount {
font-size: 12px;
font-weight: var(--font-weight-light);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/**
* 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 { useCallback } from 'react';

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

import { getEpigenomeSelectionCriteria } from 'src/content/app/regulatory-activity-viewer/state/epigenome-selection/epigenomeSelectionSelectors';

import {
useEpigenomeMetadataDimensionsQuery,
useBaseEpigenomesQuery
} from 'src/content/app/regulatory-activity-viewer/state/api/activityViewerApiSlice';
import { removeSelectionCriterion } from 'src/content/app/regulatory-activity-viewer/state/epigenome-selection/epigenomeSelectionSlice';

import { getMetadataItems } from 'src/content/app/regulatory-activity-viewer/components/epigenome-selection-panel/getEpigenomeCounts';

import {
CollapsibleSection,
CollapsibleSectionHead,
CollapsibleSectionBody
} from 'src/shared/components/collapsible-section/CollapsibleSection';
import DeleteButton from 'src/shared/components/delete-button/DeleteButton';

import type { Epigenome } from 'src/content/app/regulatory-activity-viewer/types/epigenome';
import type { EpigenomeMetadataDimensions } from 'src/content/app/regulatory-activity-viewer/types/epigenomeMetadataDimensions';

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

type Props = {
genomeId: string | null;
};

const EpigenomeFiltersView = (props: Props) => {
const { genomeId } = props;
const { currentData: epigenomeMetadataDimensionsResponse } =
useEpigenomeMetadataDimensionsQuery();
const { currentData: baseEpigenomes } = useBaseEpigenomesQuery();
const epigenomeSelectionCriteria = useAppSelector((state) =>
getEpigenomeSelectionCriteria(state, genomeId ?? '')
);
const dispatch = useAppDispatch();

const onFilterRemove = useCallback(
(payload: { dimensionName: string; value: string }) => {
dispatch(
removeSelectionCriterion({
...payload,
genomeId: genomeId as string
})
);
},
[]
);

if (!epigenomeMetadataDimensionsResponse || !baseEpigenomes) {
return null;
}

const metadataDimensions = epigenomeMetadataDimensionsResponse.dimensions;
const metadataDimensionNames =
epigenomeMetadataDimensionsResponse.filter_layout.flat();

return (
<div>
{metadataDimensionNames.map((dimensionName) => (
<FiltersSection
key={dimensionName}
dimensionName={dimensionName}
metadataDimensions={metadataDimensions}
allFilters={epigenomeSelectionCriteria}
epigenomes={baseEpigenomes}
onFilterRemove={onFilterRemove}
/>
))}
</div>
);
};

const FiltersSection = ({
dimensionName,
allFilters,
metadataDimensions,
epigenomes,
onFilterRemove
}: {
dimensionName: string;
allFilters: Record<string, Set<string>>;
metadataDimensions: EpigenomeMetadataDimensions;
onFilterRemove: (params: { dimensionName: string; value: string }) => void;
epigenomes: Epigenome[];
}) => {
const filtersForSection = allFilters[dimensionName];

if (!filtersForSection) {
return null;
}

const { metadataItems, counts: metadataCounts } = getMetadataItems({
epigenomes: epigenomes,
dimensionName: dimensionName,
selectionCriteria: allFilters,
metadataItems: metadataDimensions[dimensionName]
});

return (
<CollapsibleSection>
<CollapsibleSectionHead>{metadataItems.name}</CollapsibleSectionHead>
<CollapsibleSectionBody>
{[...filtersForSection].map((filterName) => (
<EpigenomeFilter
key={filterName}
dimensionName={dimensionName}
filterValue={filterName}
epigenomesCount={metadataCounts[filterName]}
onRemove={onFilterRemove}
/>
))}
</CollapsibleSectionBody>
</CollapsibleSection>
);
};

const EpigenomeFilter = ({
dimensionName,
filterValue,
epigenomesCount,
onRemove
}: {
dimensionName: string;
filterValue: string;
epigenomesCount: number;
onRemove: (params: { dimensionName: string; value: string }) => void;
}) => {
const onDeleteClick = () => {
onRemove({ dimensionName, value: filterValue });
};

return (
<div className={styles.epigenomeFilter}>
{filterValue}
<div className={styles.epigenomeFilterRight}>
<span className={styles.epigenomesCount}>{epigenomesCount ?? 0}</span>
<DeleteButton onClick={onDeleteClick} />
</div>
</div>
);
};

export default EpigenomeFiltersView;
Loading