Skip to content

Commit

Permalink
added tabs for top 10 genes and message when no mutations are present…
Browse files Browse the repository at this point in the history
… for selected gene, fixed gene tab and selector inconsistency issues, removed gene alerts (#4415)

Co-authored-by: Bryan Lai <laib1@mskcc.org>
  • Loading branch information
gblaih and Bryan Lai authored Nov 15, 2022
1 parent 276f385 commit 68fd100
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 72 deletions.
4 changes: 4 additions & 0 deletions src/globalStyles/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ th.reactable-header-sort-asc:after {
}
}

.comparisonMutationMapperTabs {
height: 0;
}

.mainTabs {
> .nav {
@extend .portalWidth;
Expand Down
77 changes: 73 additions & 4 deletions src/pages/groupComparison/GroupComparisonMutationsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ErrorMessage from 'shared/components/ErrorMessage';
import { LollipopGeneSelector } from './LollipopGeneSelector';
import GroupComparisonMutationsTabPlot from './GroupComparisonMutationsTabPlot';
import OverlapExclusionIndicator from './OverlapExclusionIndicator';
import { MSKTab, MSKTabs } from 'shared/components/MSKTabs/MSKTabs';

interface IGroupComparisonMutationsTabProps {
store: GroupComparisonStore;
Expand All @@ -24,8 +25,39 @@ export default class GroupComparisonMutationsTab extends React.Component<
IGroupComparisonMutationsTabProps,
{}
> {
@observable public geneTab: string | undefined;
constructor(props: IGroupComparisonMutationsTabProps) {
super(props);
makeObservable(this);
}

@action.bound
protected handleGeneChange(id: string | undefined) {
this.geneTab = id;
this.props.store.setSelectedMutationMapperGene(id);
}

@computed get tabs() {
return this.props.store.genesWithMaxFrequency.map(g => (
<MSKTab
key={g.hugoGeneSymbol}
id={g.hugoGeneSymbol}
linkText={g.hugoGeneSymbol}
/>
));
}

@computed get activeTabId(): string | undefined {
let activeTabId;
if (this.geneTab) {
activeTabId = this.geneTab;
} else if (this.props.store.userSelectedMutationMapperGene) {
activeTabId = this.props.store.userSelectedMutationMapperGene;
} else {
activeTabId = this.props.store.activeMutationMapperGene!
.hugoGeneSymbol;
}
return activeTabId;
}

readonly tabUI = MakeMobxView({
Expand All @@ -45,22 +77,59 @@ export default class GroupComparisonMutationsTab extends React.Component<
</div>
);
}

return (
<>
{!this.props.store.userSelectedMutationMapperGene && (
{/* {!this.props.store.userSelectedMutationMapperGene ? (
<div className="alert alert-info">
<div>
Gene with highest frequency is displayed by
default. Gene can be changed in the dropdown
below.
</div>
<div>
The top 10 genes with highest frequency are
shown below the dropdown and can be selected by
clicking on their respective tabs.
</div>
</div>
) : (
<div className="alert alert-info">
Gene with highest frequency is displayed by default.
Gene can be changed in the dropdown below.
The top 10 genes with highest frequency are shown
below the dropdown and can be selected by clicking
on their respective tabs.
</div>
)}
)} */}
<OverlapExclusionIndicator
store={this.props.store}
only="sample"
/>
<LollipopGeneSelector
store={this.props.store}
genes={this.props.store.availableGenes.result!}
handleGeneChange={this.handleGeneChange}
key={
'comparisonLollipopGene' +
this.props.store.activeMutationMapperGene!
.hugoGeneSymbol
}
/>
<div style={{ display: 'flex' }}>
<div style={{ paddingRight: '5px' }}>
Highest Frequency:
</div>
<MSKTabs
activeTabId={this.activeTabId}
onTabClick={(id: string) =>
this.handleGeneChange(id)
}
className="pillTabs comparisonMutationMapperTabs"
tabButtonStyle="pills"
defaultTabId={false}
>
{this.tabs}
</MSKTabs>
</div>
<GroupComparisonMutationsTabPlot
store={this.props.store}
mutations={_(this.props.store.mutationsByGroup.result!)
Expand Down
29 changes: 25 additions & 4 deletions src/pages/groupComparison/GroupComparisonMutationsTabPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export default class GroupComparisonMutationsTabPlot extends React.Component<
this.props.store.mutationsByGroup,
this.props.store.profiledSamplesCount,
this.mutationMapperToolStore.mutationMapperStores,
this.props.store.coverageInformation,
],
render: () => {
if (
Expand All @@ -92,9 +93,12 @@ export default class GroupComparisonMutationsTabPlot extends React.Component<
return (
<>
<h3>
{_(this.props.store.mutationsByGroup.result!)
.keys()
.join(' vs ')}
{this.props.store.activeMutationMapperGene
?.hugoGeneSymbol +
' mutations: ' +
_(this.props.store.mutationsByGroup.result!)
.keys()
.join(' vs ')}
</h3>
<GroupComparisonMutationMapper
{...convertToMutationMapperProps({
Expand All @@ -115,7 +119,24 @@ export default class GroupComparisonMutationsTabPlot extends React.Component<
</>
);
} else {
return null;
if (
Object.values(
this.props.store.coverageInformation.result!.samples
).some(s => !_.isEmpty(s.allGenes) || !_.isEmpty(s.byGene))
) {
return (
<div style={{ marginTop: '20px' }}>
Selected gene has no mutations for profiled samples.
</div>
);
} else {
return (
<div style={{ marginTop: '20px' }}>
Selected gene has no mutations due to no profiled
samples.
</div>
);
}
}
},
renderPending: () => (
Expand Down
11 changes: 3 additions & 8 deletions src/pages/groupComparison/GroupComparisonStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import { FeatureFlagEnum } from 'shared/featureFlags';

export default class GroupComparisonStore extends ComparisonStore {
@observable private sessionId: string;
@observable private _userSelectedMutationMapperGene: string;
@observable private _userSelectedMutationMapperGene: string | undefined;

constructor(
sessionId: string,
Expand Down Expand Up @@ -452,13 +452,8 @@ export default class GroupComparisonStore extends ComparisonStore {
}

@action.bound
public setSelectedMutationMapperGene(gene: Gene) {
this._userSelectedMutationMapperGene = gene.hugoGeneSymbol;
}

@action.bound
public clearSelectedMutationMapperGene() {
this._userSelectedMutationMapperGene = '';
public setSelectedMutationMapperGene(gene: string | undefined) {
this._userSelectedMutationMapperGene = gene;
}

@autobind
Expand Down
103 changes: 52 additions & 51 deletions src/pages/groupComparison/LollipopGeneSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,59 @@ import GroupComparisonStore from './GroupComparisonStore';
interface ILollipopGeneSelectorProps {
store: GroupComparisonStore;
genes: Gene[];
handleGeneChange: (id?: string) => void;
}

export const LollipopGeneSelector: React.FC<ILollipopGeneSelectorProps> = ({
store,
genes,
}: ILollipopGeneSelectorProps) => {
const loadOptions = (inputText: string, callback: any) => {
if (!inputText) {
callback([]);
}
const stringCompare = (item: any) =>
item.hugoGeneSymbol.startsWith(inputText.toUpperCase());
const options = genes;
callback(
options
.filter(stringCompare)
.slice(0, 200)
.map(g => ({
label: g.hugoGeneSymbol,
value: g,
}))
);
};
export const LollipopGeneSelector: React.FC<ILollipopGeneSelectorProps> = observer(
({ store, genes, handleGeneChange }: ILollipopGeneSelectorProps) => {
const loadOptions = (inputText: string, callback: any) => {
if (!inputText) {
callback([]);
}
const stringCompare = (item: any) =>
item.hugoGeneSymbol.startsWith(inputText.toUpperCase());
const options = genes;
callback(
options
.filter(stringCompare)
.slice(0, 200)
.map(g => ({
label: g.hugoGeneSymbol,
value: g,
}))
);
};

return (
<div style={{ width: 200, paddingBottom: 10 }}>
<AsyncSelect
name="Select gene"
onChange={(option: any | null) => {
if (option) {
store.setSelectedMutationMapperGene(option.value);
} else {
store.clearSelectedMutationMapperGene();
return (
<div style={{ width: 200, paddingBottom: 10 }}>
<AsyncSelect
name="Select gene"
onChange={(option: any | null) => {
if (option) {
handleGeneChange(option.value.hugoGeneSymbol);
} else {
handleGeneChange(undefined);
}
}}
isClearable={true}
isSearchable={true}
defaultOptions={genes.slice(0, 200).map(gene => ({
label: gene.hugoGeneSymbol,
value: gene,
}))}
value={
store.userSelectedMutationMapperGene
? {
label: store.userSelectedMutationMapperGene,
value: store.activeMutationMapperGene,
}
: null
}
}}
isClearable={true}
isSearchable={true}
defaultOptions={genes.slice(0, 200).map(gene => ({
label: gene.hugoGeneSymbol,
value: gene,
}))}
value={{
label: store.activeMutationMapperGene!.hugoGeneSymbol,
value: store.activeMutationMapperGene,
}}
placeholder={
store.activeMutationMapperGene!.hugoGeneSymbol ||
'Select a gene'
}
loadOptions={loadOptions}
cacheOptions={true}
/>
</div>
);
};
placeholder={'Search genes'}
loadOptions={loadOptions}
cacheOptions={true}
/>
</div>
);
}
);
13 changes: 8 additions & 5 deletions src/shared/components/MSKTabs/MSKTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ interface IMSKTabsProps {
contentWindowExtra?: JSX.Element;
hrefRoot?: string;
onMount?: () => void;
defaultTabId?: string | Boolean;
}

@observer
Expand Down Expand Up @@ -218,9 +219,11 @@ export class MSKTabs extends React.Component<IMSKTabsProps> {
) {
return this.props.activeTabId;
} else {
return (toArrayedChildren[0] as React.ReactElement<
IMSKTabProps
>).props.id;
return this.props.defaultTabId === false
? undefined
: (toArrayedChildren[0] as React.ReactElement<
IMSKTabProps
>).props.id;
}
})();

Expand Down Expand Up @@ -287,7 +290,7 @@ export class MSKTabs extends React.Component<IMSKTabsProps> {

protected navTabs(
children: React.ReactElement<IMSKTabProps>[],
effectiveActiveTab: string
effectiveActiveTab: string | undefined
) {
// restart the tab refs before each tab rendering
this.tabRefs = [];
Expand Down Expand Up @@ -349,7 +352,7 @@ export class MSKTabs extends React.Component<IMSKTabsProps> {

protected tabPages(
children: React.ReactElement<IMSKTabProps>[],
effectiveActiveTab: string
effectiveActiveTab: string | undefined
): JSX.Element[][] {
const pages: JSX.Element[][] = [[]];
let currentPage = 1;
Expand Down

0 comments on commit 68fd100

Please sign in to comment.