+
+ {this.props.additionalControls && (
+ {this.props.additionalControls}
+ )}
+
{this.getHorizontalAxisMenu}
@@ -5761,6 +5859,12 @@ export default class PlotsTab extends React.Component
{
this.onClickLegendItem
)}
legendTitle={this.legendTitle}
+ highlightedSamples={
+ this.props.highlightedSamples
+ }
+ customSamplePointComponent={
+ this.props.customSamplePointComponent
+ }
/>
);
break;
@@ -5908,6 +6012,12 @@ export default class PlotsTab extends React.Component {
samplesForPatients={
this.samplesForEachPatient
}
+ highlightedSamples={
+ this.props.highlightedSamples
+ }
+ customSamplePointComponent={
+ this.props.customSamplePointComponent
+ }
/>
);
break;
diff --git a/src/shared/components/plots/PlotsTabUtils.tsx b/src/shared/components/plots/PlotsTabUtils.tsx
index d22df6f94c3..992cfbced61 100644
--- a/src/shared/components/plots/PlotsTabUtils.tsx
+++ b/src/shared/components/plots/PlotsTabUtils.tsx
@@ -3299,7 +3299,9 @@ export function getCacheQueries(utilitiesSelection: ColoringMenuSelection) {
export function showWaterfallPlot(
horzSelection: AxisMenuSelection,
- vertSelection: AxisMenuSelection
+ vertSelection: AxisMenuSelection,
+ horzAxisData?: IAxisData,
+ vertAxisData?: IAxisData
): boolean {
return (
(vertSelection.dataType !== undefined &&
@@ -3311,7 +3313,13 @@ export function showWaterfallPlot(
isGenericAssaySelected(horzSelection) &&
horzSelection.genericAssayDataType ===
DataTypeConstants.LIMITVALUE &&
- vertSelection.dataType === NONE_SELECTED_OPTION_STRING_VALUE)
+ vertSelection.dataType === NONE_SELECTED_OPTION_STRING_VALUE) ||
+ (horzAxisData !== undefined &&
+ isNumberData(horzAxisData) &&
+ vertSelection.dataType === NONE_SELECTED_OPTION_STRING_VALUE) ||
+ (vertAxisData !== undefined &&
+ isNumberData(vertAxisData) &&
+ horzSelection.dataType === NONE_SELECTED_OPTION_STRING_VALUE)
);
}
diff --git a/src/shared/components/plots/ScatterPlot.tsx b/src/shared/components/plots/ScatterPlot.tsx
index e86837ece64..480ecc524d0 100644
--- a/src/shared/components/plots/ScatterPlot.tsx
+++ b/src/shared/components/plots/ScatterPlot.tsx
@@ -21,6 +21,7 @@ import {
getBottomLegendHeight,
getMaxLegendLabelWidth,
getLegendItemsPerRow,
+ separateScatterData,
} from './PlotUtils';
import { toConditionalPrecision } from '../../lib/NumberUtils';
import {
@@ -44,6 +45,7 @@ import autobind from 'autobind-decorator';
export interface IBaseScatterPlotData {
x: number;
y: number;
+ sampleId: string;
}
export interface IScatterPlotProps {
@@ -79,6 +81,11 @@ export interface IScatterPlotProps {
axisLabelY?: string;
fontFamily?: string;
legendTitle?: string | string[];
+ highlightedSamples?: string[];
+ customSamplePointComponent?: (
+ sampleId: string,
+ mouseEvents: any
+ ) => JSX.Element;
}
// constants related to the gutter
const GUTTER_TEXT_STYLE = {
@@ -596,8 +603,22 @@ export default class ScatterPlot<
}
@computed get data() {
- return separateScatterDataByAppearance(
+ const [highlightedData, unHighlightedData] = _.partition(
this.props.data,
+ d => this.props.highlightedSamples?.includes(d.sampleId)
+ );
+ const highlightedDataBuckets = separateScatterData(
+ highlightedData,
+ ifNotDefined(this.props.fill, '0x000000'),
+ ifNotDefined(this.props.stroke, '0x000000'),
+ ifNotDefined(this.props.strokeWidth, 0),
+ ifNotDefined(this.props.strokeOpacity, 1),
+ ifNotDefined(this.props.fillOpacity, 1),
+ ifNotDefined(this.props.symbol, 'circle'),
+ this.props.zIndexSortBy
+ );
+ const unHighlightedDataBuckets = separateScatterDataByAppearance(
+ unHighlightedData,
ifNotDefined(this.props.fill, '0x000000'),
ifNotDefined(this.props.stroke, '0x000000'),
ifNotDefined(this.props.strokeWidth, 0),
@@ -606,6 +627,8 @@ export default class ScatterPlot<
ifNotDefined(this.props.symbol, 'circle'),
this.props.zIndexSortBy
);
+ // highlighted data points should appear in front of the other data points
+ return [...unHighlightedDataBuckets, ...highlightedDataBuckets];
}
@computed private get regressionLineComputations() {
@@ -713,29 +736,47 @@ export default class ScatterPlot<
axisLabelComponent={}
label={this.axisLabelY}
/>
- {this.data.map(dataWithAppearance => (
-
- ))}
+ {this.data.map(dataWithAppearance => {
+ const useCustomDataComponent =
+ this.props.customSamplePointComponent &&
+ this.props.highlightedSamples?.includes(
+ dataWithAppearance.data[0].sampleId
+ );
+ return (
+
+ );
+ })}
{this.regressionLine}
{this.correlationInfo}
diff --git a/src/shared/components/plots/styles.scss b/src/shared/components/plots/styles.scss
index 1bcccbe739c..d523a4bcf6b 100644
--- a/src/shared/components/plots/styles.scss
+++ b/src/shared/components/plots/styles.scss
@@ -17,6 +17,10 @@
width: 240px;
}
+ .cohort-select-div .Select {
+ width: 100%;
+ }
+
.Select.is-open.is-searchable {
.Select-value-label {
color: #aaaaaa !important;
diff --git a/src/shared/components/sampleLabel/SampleLabel.tsx b/src/shared/components/sampleLabel/SampleLabel.tsx
index eab5dae9557..4d70da5cd77 100644
--- a/src/shared/components/sampleLabel/SampleLabel.tsx
+++ b/src/shared/components/sampleLabel/SampleLabel.tsx
@@ -84,3 +84,42 @@ interface ISampleLabelHTMLProps {
color: string;
fillOpacity: number;
}
+
+export class SamplePointLabel extends React.Component<
+ ISamplePointLabelProps,
+ {}
+> {
+ public render() {
+ const { x = 6, y = 6, events = {}, label, ...restProps } = this.props;
+ const { onMouseOver, onMouseOut } = events;
+ return (
+ <>
+
+
+
+
+
+ {label}
+
+
+ >
+ );
+ }
+}
+
+interface ISamplePointLabelProps {
+ label: string;
+ events: any;
+ x?: number;
+ y?: number;
+}