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

Lollipop plots in group comparison page shows mutations for two or more groups #4392

Merged
merged 11 commits into from
Nov 8, 2022
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
"@types/react-overlays": "^0.6.9",
"@types/react-router": "3.0.2",
"@types/react-router-dom": "^5.1.6",
"@types/react-select": "^5.0.1",
"@types/react-sortable-hoc": "^0.6.5",
"@types/react-spinkit": "^1.1.35",
"@types/react-table": "^6.8.7",
Expand Down
23 changes: 21 additions & 2 deletions packages/cbioportal-frontend-commons/src/components/SVGAxis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,24 @@ export default class SVGAxis extends React.Component<SVGAxisProps, {}> {
: horizontalPadding);
transform = '';
}
let wrappedLabel;
if (this.props.label.endsWith('Mutations')) {
wrappedLabel = (
alisman marked this conversation as resolved.
Show resolved Hide resolved
<>
<tspan x={x} dy="1.2em">
{this.props.label.slice(
0,
this.props.label.lastIndexOf('Mutations')
)}
</tspan>
<tspan x={x} dy="1.2em">
{'Mutations'}
</tspan>
</>
);
} else {
wrappedLabel = <>{this.props.label}</>;
}
return (
<text
textAnchor="middle"
Expand All @@ -157,9 +175,10 @@ export default class SVGAxis extends React.Component<SVGAxisProps, {}> {
fill="#2E3436"
x={x}
y={y}
transform={transform}
transform={transform + ' translate(0, -30)'}
>
{this.props.label}
{wrappedLabel}
{/* {this.props.label} */}
alisman marked this conversation as resolved.
Show resolved Hide resolved
</text>
);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import classNames from 'classnames';
import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ButtonGroup } from 'react-bootstrap';

interface IAxisScaleSwitchProps {
onChange: (selectedScale: AxisScale) => void;
selectedScale: AxisScale;
}

export enum AxisScale {
PERCENT = '%',
COUNT = '#',
}

@observer
export class AxisScaleSwitch extends React.Component<
IAxisScaleSwitchProps,
{}
> {
public static defaultProps: Partial<IAxisScaleSwitchProps> = {
selectedScale: AxisScale.COUNT,
};

@observable
private selectedScale: AxisScale;

constructor(props: IAxisScaleSwitchProps) {
super(props);
this.selectedScale = props.selectedScale;
makeObservable(this);
}

public toggleButton(scale: AxisScale, onClick: () => void) {
return (
<button
className={classNames(
{
'btn-secondary': this.props.selectedScale === scale,
'btn-outline-secondary':
this.props.selectedScale !== scale,
},
'btn',
'btn-sm',
'btn-axis-switch'
)}
style={{
lineHeight: 1,
cursor:
this.props.selectedScale === scale
? 'default'
: 'pointer',
fontWeight:
this.props.selectedScale === scale
? 'bolder'
: 'normal',
}}
onClick={onClick}
>
{scale}
</button>
);
}

public render() {
return (
<ButtonGroup aria-label="">
{this.toggleButton(AxisScale.PERCENT, this.handlePercentClick)}
{this.toggleButton(AxisScale.COUNT, this.handleCountClick)}
</ButtonGroup>
);
}

@action.bound
private handlePercentClick() {
this.selectedScale = AxisScale.PERCENT;
this.props.onChange(this.selectedScale);
}

@action.bound
private handleCountClick() {
this.selectedScale = AxisScale.COUNT;
this.props.onChange(this.selectedScale);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import TrackPanel from '../track/TrackPanel';

import './lollipopMutationPlot.scss';
import DomainTooltip from '../lollipopPlot/DomainTooltip';
import { AxisScale } from './AxisScaleSwitch';

const DEFAULT_PROTEIN_LENGTH = 10;

Expand Down Expand Up @@ -98,8 +99,11 @@ export type LollipopMutationPlotProps<T extends Mutation> = {
yAxisLabelPadding?: number;
lollipopTooltipCountInfo?: (
count: number,
mutations?: Partial<T>[]
mutations?: Partial<T>[],
axisMode?: AxisScale
) => JSX.Element;
yAxisLabelFormatter?: (symbol?: string, groupName?: string) => string;
axisMode?: AxisScale;
customControls?: JSX.Element;
onXAxisOffset?: (offset: number) => void;
geneWidth: number;
Expand Down Expand Up @@ -161,7 +165,11 @@ export default class LollipopMutationPlot<
const codon = mutationsAtPosition[0].proteinPosStart;
const count = countsByPosition[codon];
const countInfo = this.props.lollipopTooltipCountInfo ? (
this.props.lollipopTooltipCountInfo(count, mutationsAtPosition)
this.props.lollipopTooltipCountInfo(
count,
mutationsAtPosition,
this.props.axisMode
)
) : (
<strong>
{count} mutation{`${count !== 1 ? 's' : ''}`}
Expand Down Expand Up @@ -892,6 +900,7 @@ export default class LollipopMutationPlot<
topYAxisSymbol={this.props.topYAxisSymbol}
bottomYAxisSymbol={this.props.bottomYAxisSymbol}
groups={this.groups}
yAxisLabelFormatter={this.props.yAxisLabelFormatter}
/>
<TrackPanel
store={this.props.store}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AxisScale, AxisScaleSwitch } from './AxisScaleSwitch';

interface IPercentToggleProps {
axisMode?: AxisScale;
onScaleToggle?: (selectedScale: AxisScale) => void;
}

export class PercentToggle extends React.Component<IPercentToggleProps, {}> {
constructor(props: IPercentToggleProps) {
super(props);
}

public render() {
return (
<div
className="small"
style={{ display: 'flex', alignItems: 'center' }}
>
<span style={{ marginLeft: 10, marginRight: 10 }}>
Y-Axis:{' '}
</span>
<AxisScaleSwitch
selectedScale={this.props.axisMode}
onChange={this.props.onScaleToggle}
/>
</div>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export type LollipopPlotProps = {
hugoGeneSymbol?: string;
dataStore?: DataStore;
onXAxisOffset?: (offset: number) => void;
yAxisLabelFormatter?: (symbol?: string, groupName?: string) => string;
};

@observer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -689,11 +689,12 @@ export default class LollipopPlotNoTooltip extends React.Component<
groupName?: string,
symbol: string = '#'
) {
const label = groupName
const label = this.props.yAxisLabelFormatter
? this.props.yAxisLabelFormatter(symbol, groupName)
: groupName
onursumer marked this conversation as resolved.
Show resolved Hide resolved
? `${symbol} ${this.props.hugoGeneSymbol ||
''} ${groupName} Mutations`
: `${symbol} ${this.props.hugoGeneSymbol || ''} Mutations`;

const placeOnBottom = placement === LollipopPlacement.BOTTOM;

return (
Expand Down
2 changes: 2 additions & 0 deletions packages/react-mutation-mapper/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ export { default as Domain } from './component/lollipopPlot/Domain';
export { HotspotInfo } from './component/hotspot/HotspotInfo';
export { default as Lollipop } from './component/lollipopPlot/Lollipop';
export { default as LollipopMutationPlot } from './component/lollipopMutationPlot/LollipopMutationPlot';
export * from './component/lollipopMutationPlot/PercentToggle';
export * from './component/lollipopMutationPlot/AxisScaleSwitch';
export { default as LollipopPlot } from './component/lollipopPlot/LollipopPlot';
export { default as LollipopPlotNoTooltip } from './component/lollipopPlot/LollipopPlotNoTooltip';
export { default as Sequence } from './component/lollipopPlot/LollipopPlot';
Expand Down
4 changes: 4 additions & 0 deletions src/globalStyles/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -752,3 +752,7 @@ h2.divider {
}
}
}

#mutationMapperToolTabs .nav-pills {
display: none;
}
onursumer marked this conversation as resolved.
Show resolved Hide resolved
59 changes: 59 additions & 0 deletions src/pages/groupComparison/GroupComparisonMutationMapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from 'react';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import {
IMutationMapperProps,
default as MutationMapper,
} from 'shared/components/mutationMapper/MutationMapper';
import { PercentToggle, AxisScale } from 'react-mutation-mapper';

interface IGroupComparisonMutationMapperProps extends IMutationMapperProps {
onInit?: (mutationMapper: GroupComparisonMutationMapper) => void;
onScaleToggle?: (selectedScale: AxisScale) => void;
axisMode?: AxisScale;
}

@observer
export default class GroupComparisonMutationMapper extends MutationMapper<
IGroupComparisonMutationMapperProps
> {
constructor(props: IGroupComparisonMutationMapperProps) {
super(props);
}

protected get mutationTableComponent() {
return null;
}

protected get plotTopYAxisSymbol() {
return this.props.axisMode;
}

protected get plotBottomYAxisSymbol() {
return this.props.axisMode;
}

protected get plotTopYAxisDefaultMax() {
return this.props.axisMode === AxisScale.PERCENT ? 0 : 5;
}

protected get plotBottomYAxisDefaultMax() {
return this.props.axisMode === AxisScale.PERCENT ? 0 : 5;
}

protected get plotYMaxLabelPostfix() {
return this.props.axisMode === AxisScale.PERCENT ? '%' : '';
}

/**
* Override the parent method to get custom controls.
*/
protected get customControls(): JSX.Element | undefined {
return (
<PercentToggle
axisMode={this.props.axisMode}
onScaleToggle={this.props.onScaleToggle}
/>
);
}
}
Loading