Skip to content

Commit

Permalink
Merge branch 'main' into 426-donut-marker-config-panel
Browse files Browse the repository at this point in the history
  • Loading branch information
moontrip committed Sep 13, 2023
2 parents 2610930 + 07a2389 commit 1338056
Show file tree
Hide file tree
Showing 14 changed files with 197 additions and 67 deletions.
4 changes: 4 additions & 0 deletions packages/libs/coreui/src/components/inputs/SelectList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export interface SelectListProps<T> extends CheckboxListProps<T> {
/** A button's content if/when no values are currently selected */
defaultButtonDisplayContent: ReactNode;
isDisabled?: boolean;
/** Are contents loading? */
isLoading?: boolean;
}

export default function SelectList<T>({
Expand All @@ -18,6 +20,7 @@ export default function SelectList<T>({
children,
defaultButtonDisplayContent,
isDisabled = false,
isLoading = false,
...props
}: SelectListProps<T>) {
const [selected, setSelected] = useState<SelectListProps<T>['value']>(value);
Expand Down Expand Up @@ -67,6 +70,7 @@ export default function SelectList<T>({
margin: '0.5em',
}}
>
{isLoading && <div css={{ height: '20px' }}>Loading...</div>}
<CheckboxList
name={name}
items={items}
Expand Down
11 changes: 11 additions & 0 deletions packages/libs/eda/src/lib/core/api/DataClient/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,17 @@ export const BinRange = type({
binLabel: string,
});

export type LabeledRange = TypeOf<typeof LabeledRange>;
export const LabeledRange = intersection([
type({
label: string,
}),
partial({
max: string,
min: string,
}),
]);

export type ContinousVariableMetadataResponse = TypeOf<
typeof ContinousVariableMetadataResponse
>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { useCollectionVariables, useStudyMetadata } from '../../..';
import {
ContinuousVariableDataShape,
LabeledRange,
useCollectionVariables,
usePromise,
useStudyMetadata,
} from '../../..';
import { VariableDescriptor } from '../../../types/variable';
import { volcanoPlotVisualization } from '../../visualizations/implementations/VolcanoPlotVisualization';
import { ComputationConfigProps, ComputationPlugin } from '../Types';
Expand All @@ -7,8 +13,11 @@ import { useConfigChangeHandler, assertComputationWithConfig } from '../Utils';
import * as t from 'io-ts';
import { Computation } from '../../../types/visualization';
import SingleSelect from '@veupathdb/coreui/lib/components/inputs/SingleSelect';
import { useFindEntityAndVariable } from '../../../hooks/workspace';
import { useMemo } from 'react';
import {
useDataClient,
useFindEntityAndVariable,
} from '../../../hooks/workspace';
import { useCallback, useMemo } from 'react';
import { ComputationStepContainer } from '../ComputationStepContainer';
import VariableTreeDropdown from '../../variableTrees/VariableTreeDropdown';
import { ValuePicker } from '../../visualizations/implementations/ValuePicker';
Expand All @@ -19,6 +28,10 @@ import { SwapHorizOutlined } from '@material-ui/icons';
import './Plugins.scss';
import { makeClassNameHelper } from '@veupathdb/wdk-client/lib/Utils/ComponentUtils';
import { Tooltip } from '@material-ui/core';
import {
GetBinRangesProps,
getBinRanges,
} from '../../../../map/analysis/utils/defaultOverlayConfig';

const cx = makeClassNameHelper('AppStepConfigurationContainer');

Expand All @@ -44,8 +57,8 @@ export type DifferentialAbundanceConfig = t.TypeOf<

const Comparator = t.intersection([
t.partial({
groupA: t.array(t.string),
groupB: t.array(t.string),
groupA: t.array(LabeledRange),
groupB: t.array(LabeledRange),
}),
t.type({
variable: VariableDescriptor,
Expand Down Expand Up @@ -111,6 +124,7 @@ function DifferentialAbundanceConfigDescriptionComponent({
collectionVariable
)
);

return (
<div className="ConfigDescriptionContainer">
<h4>
Expand Down Expand Up @@ -150,6 +164,7 @@ export function DifferentialAbundanceConfiguration(
const configuration = computation.descriptor
.configuration as DifferentialAbundanceConfig;
const studyMetadata = useStudyMetadata();
const dataClient = useDataClient();
const toggleStarredVariable = useToggleStarredVariable(props.analysisState);
const filters = analysisState.analysis?.descriptor.subset.descriptor;
const findEntityAndVariable = useFindEntityAndVariable(filters);
Expand Down Expand Up @@ -215,10 +230,56 @@ export function DifferentialAbundanceConfiguration(
}
}, [configuration, findEntityAndVariable]);

// If the variable is continuous, ask the backend for a list of bins
const continuousVariableBins = usePromise(
useCallback(async () => {
if (
!ContinuousVariableDataShape.is(
selectedComparatorVariable?.variable.dataShape
)
)
return;

const binRangeProps: GetBinRangesProps = {
studyId: studyMetadata.id,
...configuration.comparator?.variable,
filters: filters ?? [],
dataClient,
binningMethod: 'quantile',
};
const bins = await getBinRanges(binRangeProps);
return bins;
}, [
dataClient,
configuration?.comparator?.variable,
filters,
selectedComparatorVariable,
studyMetadata.id,
])
);

const disableSwapGroupValuesButton =
!configuration?.comparator?.groupA && !configuration?.comparator?.groupB;
const disableGroupValueSelectors = !configuration?.comparator?.variable;

// Create the options for groupA and groupB. Organizing into the LabeledRange[] format
// here in order to keep the later code clean.
const groupValueOptions = continuousVariableBins.value
? continuousVariableBins.value.map((bin): LabeledRange => {
return {
min: bin.binStart,
max: bin.binEnd,
label: bin.binLabel,
};
})
: selectedComparatorVariable?.variable.vocabulary?.map(
(value): LabeledRange => {
return {
label: value,
};
}
);

return (
<ComputationStepContainer
computationStepInfo={{
Expand Down Expand Up @@ -290,21 +351,32 @@ export function DifferentialAbundanceConfiguration(
<span>Group A</span>
<ValuePicker
allowedValues={
selectedComparatorVariable?.variable.vocabulary
!continuousVariableBins.pending
? groupValueOptions?.map((option) => option.label)
: undefined
}
selectedValues={configuration?.comparator?.groupA}
disabledValues={configuration?.comparator?.groupB}
onSelectedValuesChange={(newValues) =>
selectedValues={configuration?.comparator?.groupA?.map(
(entry) => entry.label
)}
disabledValues={configuration?.comparator?.groupB?.map(
(entry) => entry.label
)}
onSelectedValuesChange={(newValues) => {
changeConfigHandler('comparator', {
variable:
configuration?.comparator?.variable ?? undefined,
groupA: newValues.length ? newValues : undefined,
groupA: newValues.length
? groupValueOptions?.filter((option) =>
newValues.includes(option.label)
)
: undefined,
groupB: configuration?.comparator?.groupB ?? undefined,
})
}
});
}}
disabledCheckboxTooltipContent="Values cannot overlap between groups"
showClearSelectionButton={false}
disableInput={disableGroupValueSelectors}
isLoading={continuousVariableBins.pending}
/>
<FloatingButton
icon={SwapHorizOutlined}
Expand Down Expand Up @@ -338,21 +410,32 @@ export function DifferentialAbundanceConfiguration(
<span>Group B</span>
<ValuePicker
allowedValues={
selectedComparatorVariable?.variable.vocabulary
!continuousVariableBins.pending
? groupValueOptions?.map((option) => option.label)
: undefined
}
selectedValues={configuration?.comparator?.groupB}
disabledValues={configuration?.comparator?.groupA}
selectedValues={configuration?.comparator?.groupB?.map(
(entry) => entry.label
)}
disabledValues={configuration?.comparator?.groupA?.map(
(entry) => entry.label
)}
onSelectedValuesChange={(newValues) =>
changeConfigHandler('comparator', {
variable:
configuration?.comparator?.variable ?? undefined,
groupA: configuration?.comparator?.groupA ?? undefined,
groupB: newValues.length ? newValues : undefined,
groupB: newValues.length
? groupValueOptions?.filter((option) =>
newValues.includes(option.label)
)
: undefined,
})
}
disabledCheckboxTooltipContent="Values cannot overlap between groups"
showClearSelectionButton={false}
disableInput={disableGroupValueSelectors}
isLoading={continuousVariableBins.pending}
/>
</div>
</Tooltip>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export type ValuePickerProps = {
disabledCheckboxTooltipContent?: ReactNode;
disableInput?: boolean;
showClearSelectionButton?: boolean;
/** Show loading spinner */
isLoading?: boolean;
};

const EMPTY_ALLOWED_VALUES_ARRAY: string[] = [];
Expand All @@ -25,6 +27,7 @@ export function ValuePicker({
disabledCheckboxTooltipContent,
disableInput = false,
showClearSelectionButton = true,
isLoading = false,
}: ValuePickerProps) {
const items = allowedValues.map((value) => ({
display: <span>{value}</span>,
Expand All @@ -41,6 +44,7 @@ export function ValuePicker({
value={selectedValues}
disabledCheckboxTooltipContent={disabledCheckboxTooltipContent}
isDisabled={disableInput}
isLoading={isLoading}
/>
{showClearSelectionButton && (
<ClearSelectionButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ function VolcanoPlotViz(props: VisualizationProps<Options>) {
// Standard volcano plots have -log10(raw p value) as the y axis
const yAxisMin = -Math.log10(dataYMax);
const yAxisMax = -Math.log10(dataYMin);

// Add a little padding to prevent clipping the glyph representing the extreme points
return {
min: Math.floor(yAxisMin - (yAxisMax - yAxisMin) * AXIS_PADDING_FACTOR),
Expand Down Expand Up @@ -382,8 +383,14 @@ function VolcanoPlotViz(props: VisualizationProps<Options>) {
computationConfiguration.comparator?.groupA &&
computationConfiguration.comparator?.groupB
? [
'Up in ' + computationConfiguration.comparator.groupA.join(', '),
'Up in ' + computationConfiguration.comparator.groupB.join(', '),
'Up in ' +
computationConfiguration.comparator.groupA
.map((entry) => entry.label)
.join(','),
'Up in ' +
computationConfiguration.comparator.groupB
.map((entry) => entry.label)
.join(','),
]
: [];

Expand Down Expand Up @@ -562,17 +569,17 @@ function VolcanoPlotViz(props: VisualizationProps<Options>) {
markerColor: significanceColors['inconclusive'],
},
{
label: `Up regulated in ${computationConfiguration.comparator.groupB?.join(
', '
)} (${countsData[significanceColors['high']]})`,
label: `Up regulated in ${computationConfiguration.comparator.groupB
?.map((entry) => entry.label)
.join(',')} (${countsData[significanceColors['high']]})`,
marker: 'circle',
hasData: true,
markerColor: significanceColors['high'],
},
{
label: `Up regulated in ${computationConfiguration.comparator.groupA?.join(
', '
)} (${countsData[significanceColors['low']]})`,
label: `Up regulated in ${computationConfiguration.comparator.groupA
?.map((entry) => entry.label)
.join(',')} (${countsData[significanceColors['low']]})`,
marker: 'circle',
hasData: true,
markerColor: significanceColors['low'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ReactNode } from 'react';
import { OverlayConfig } from '../../../api/DataClient';
import { Filter } from '../../../types/filter';
import { VariableDescriptor } from '../../../types/variable';
Expand All @@ -11,7 +12,7 @@ export interface OverlayOptions {
getOverlayVariable?: (
computeConfig: unknown
) => VariableDescriptor | undefined;
getOverlayVariableHelp?: () => string;
getOverlayVariableHelp?: () => ReactNode;
getOverlayType?: () => OverlayConfig['overlayType'] | undefined;
getOverlayVocabulary?: () => string[] | undefined;
getCheckedLegendItems?: (computeConfig: unknown) => string[] | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default function DraggableVisualization({
panelTitle={activeVizOverview?.displayName || ''}
defaultPosition={{
x: 535,
y: 142,
y: 220,
}}
onPanelDismiss={() => setActiveVisualizationId(undefined)}
>
Expand Down
9 changes: 8 additions & 1 deletion packages/libs/eda/src/lib/map/analysis/MapAnalysis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,14 @@ function MapAnalysisImpl(props: ImplProps) {
);

const plugins = useStandaloneVizPlugins({
selectedOverlayConfig: activeOverlayConfig.value,
selectedOverlayConfig:
activeMarkerConfigurationType === 'bubble'
? undefined
: activeOverlayConfig.value,
overlayHelp:
activeMarkerConfigurationType === 'bubble'
? 'Overlay variables are not available for this map type'
: undefined,
});

const subsetVariableAndEntity = useMemo(() => {
Expand Down
7 changes: 3 additions & 4 deletions packages/libs/eda/src/lib/map/analysis/MapHeader.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,16 @@
align-items: flex-start;
flex-direction: column;

&__SaveableTextEditorContainer {
cursor: text;
font-size: 19px;
.wdk-SaveableTextEditor {
font-style: italic;
}

&__StudyName {
cursor: default;
margin-right: 1em;
}

&__AnalysisTitle {
font-style: italic;
padding: 0;
font-size: 19px;
}
Expand Down
Loading

0 comments on commit 1338056

Please sign in to comment.