Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into orthogroup-tree-table
Browse files Browse the repository at this point in the history
  • Loading branch information
dmfalke committed Nov 10, 2024
2 parents 655c269 + cd82232 commit 4a83f7d
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ import {

import VariableTreeDropdown from '../variableSelectors/VariableTreeDropdown';
import { Toggle } from '@veupathdb/coreui';
import { makeEntityDisplayName } from '../../utils/study-metadata';
import {
findEntityAndVariable,
makeEntityDisplayName,
} from '../../utils/study-metadata';
import { useInputStyles } from './inputStyles';
import { Tooltip } from '@veupathdb/coreui';
import RadioButtonGroup from '@veupathdb/components/lib/components/widgets/RadioButtonGroup';
import { isEqual } from 'lodash';
import { red } from '@veupathdb/coreui/lib/definitions/colors';
import { CSSProperties } from '@material-ui/core/styles/withStyles';
import Banner from '@veupathdb/coreui/lib/components/banners/Banner';

export interface InputSpec {
name: string;
Expand Down Expand Up @@ -182,12 +186,27 @@ export function InputVariables(props: Props) {
onChange({ ...selectedVariables, [inputName]: selectedVariable });
};

const invalidInputs = inputs.filter((inputSpec) => {
const variableDescriptor = selectedVariables[inputSpec.name];
if (variableDescriptor == null) return false;
const entityAndVariable = findEntityAndVariable(
entities,
variableDescriptor
);
return (
entityAndVariable == null ||
entityAndVariable.variable.type === 'category'
);
});

// Find entities that are excluded for each variable, and union their variables
// with the disabled variables.
const disabledVariablesByInputName: Record<string, VariableDescriptor[]> =
useMemo(
() =>
inputs.reduce((map, input) => {
// ignore invalid inputs
if (invalidInputs.includes(input)) return map;
// For each input (ex. xAxisVariable), determine its constraints based on which patterns any other selected variables match.
const filteredConstraints =
constraints &&
Expand All @@ -210,6 +229,7 @@ export function InputVariables(props: Props) {
}, {} as Record<string, VariableDescriptor[]>),
[
inputs,
invalidInputs,
constraints,
selectedVariables,
entities,
Expand Down Expand Up @@ -270,7 +290,8 @@ export function InputVariables(props: Props) {
constraints &&
constraints.length &&
constraints[0][input.name]?.isRequired &&
!selectedVariables[input.name]
(!selectedVariables[input.name] ||
invalidInputs.includes(input))
? requiredInputLabelStyle
: input.role === 'stratification' &&
hasMultipleStratificationValues
Expand Down Expand Up @@ -351,8 +372,16 @@ export function InputVariables(props: Props) {
}
starredVariables={starredVariables}
toggleStarredVariable={toggleStarredVariable}
entityId={selectedVariables[input.name]?.entityId}
variableId={selectedVariables[input.name]?.variableId}
entityId={
invalidInputs.includes(input)
? undefined
: selectedVariables[input.name]?.entityId
}
variableId={
invalidInputs.includes(input)
? undefined
: selectedVariables[input.name]?.variableId
}
variableLinkConfig={{
type: 'button',
onClick: (variable) =>
Expand Down Expand Up @@ -396,6 +425,30 @@ export function InputVariables(props: Props) {
{content}
</div>
))}
{invalidInputs.length > 0 && (
<Banner
banner={{
type: 'error',
message: (
<div>
The following inputs reference a variable that no longer exists.
Use the dropdown to choose a new variable.
{
<ul>
{invalidInputs.map((inputSpec) => {
return (
<li>
<strong>{inputSpec.label}</strong>
</li>
);
})}
</ul>
}
</div>
),
}}
/>
)}
</div>
);
}
16 changes: 8 additions & 8 deletions packages/libs/eda/src/lib/core/utils/data-element-constraints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,13 @@ export function filterConstraints(
if (value == null) return true;
// Ignore constraints that are on this selectedVarReference
if (selectedVarReference === variableName) return true;
// {{ treat invalid selections as "empty" and return true
const entityAndVariable = findEntityAndVariable(entities, value);
if (entityAndVariable == null) return true;
const { variable } = entityAndVariable;
if (variable.type === 'category') return true;
// }}

// If a constraint does not declare shapes or types and it allows multivalued variables, then any value is allowed, thus the constraint is "in-play"
if (
isEmpty(constraint.allowedShapes) &&
Expand All @@ -296,15 +303,8 @@ export function filterConstraints(
constraint.allowMultiValued
)
return true;

// Check that the value's associated variable has compatible characteristics
const entityAndVariable = findEntityAndVariable(entities, value);
if (entityAndVariable == null)
throw new Error(
`Could not find selected entity and variable: entityId = ${value.entityId}; variableId = ${value.variableId}.`
);
const { variable } = entityAndVariable;
if (variable.type === 'category')
throw new Error('Categories are not allowed for variable constraints.');
const typeIsValid =
isEmpty(constraint.allowedTypes) ||
constraint.allowedTypes?.includes(variable.type);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React, { useMemo, useState, useCallback } from 'react';
import React, { useMemo, useCallback } from 'react';
import Select from 'react-select';
import { TypeAheadEnumParam } from '../../../Utils/WdkModel';
import { Props } from '../../../Views/Question/Params/Utils';
import { ValueType, InputActionMeta } from 'react-select/src/types';
import { ValueType } from 'react-select/src/types';
import { isMultiPick } from '../../../Views/Question/Params/EnumParamUtils';
import { safeHtml } from '../../../Utils/ComponentUtils';

Expand Down Expand Up @@ -33,24 +32,13 @@ export const TypeAheadEnumParamComponent = (props: TypeAheadParamProps) => {
[props.parameter.vocabulary]
);

const [searchTerm, setSearchTerm] = useState('');

const selection = useMemo(() => {
return props.selectedValues.map((value) => ({
value,
label: vocabularyByValue[value][1],
}));
}, [props.selectedValues, isMultiPick(props.parameter)]);

const onInputChange = useCallback(
(inputValue: string, { action }: InputActionMeta) => {
if (action === 'input-change') {
setSearchTerm(inputValue);
}
},
[]
);

const onChange = useCallback(
(newSelection: ValueType<Option, any>) => {
const newSelectionArray =
Expand All @@ -61,44 +49,25 @@ export const TypeAheadEnumParamComponent = (props: TypeAheadParamProps) => {
: [newSelection as Option];

props.onChange(newSelectionArray.map(({ value }) => value));
setSearchTerm('');
},
[props.onChange]
);

const filterOption = useCallback(
(option: Option, inputValue: string) =>
(inputValue.length >= 3 || option.label.length < 3) &&
option.label.toLowerCase().includes(inputValue.toLowerCase()),
[]
);

const noOptionsMessage = useCallback(
({ inputValue }: { inputValue: string }) =>
inputValue.length === 0
? 'Please input at least 3 characters'
: inputValue.length === 1
? 'Please input at least 2 more characters'
: inputValue.length === 2
? 'Please input at least 1 more character'
: 'No matches found',
[]
);

return (
<Select<Option, any>
isMulti={isMultiPick(props.parameter)}
isSearchable
options={options}
filterOption={filterOption}
noOptionsMessage={noOptionsMessage}
value={selection}
onChange={onChange}
inputValue={searchTerm}
onInputChange={onInputChange}
formatOptionLabel={(option) => safeHtml(option.label)}
formatOptionLabel={formatOptionLabel}
form="DO_NOT_SUBMIT_ON_ENTER"
/>
);
};

export default TypeAheadEnumParamComponent;

function formatOptionLabel(option: Option) {
return safeHtml(option.label);
}
6 changes: 3 additions & 3 deletions packages/libs/web-common/src/user-dataset-upload-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export const uploadTypeConfig: DatasetUploadTypeConfig<ImplementedUploadTypes> =
{
rnaseq: {
type: 'rnaseq',
displayName: 'RNA-Seq',
description: `Integrate your RNA-Seq data in ${projectId}.`,
uploadTitle: 'Upload My RNA-Seq Data Set',
displayName: 'Normalized RNA-Seq',
description: `Integrate your Normalized RNA-Seq data in ${projectId}.`,
uploadTitle: 'Upload My Normalized RNA-Seq Data Set',
formConfig: {
summary: {
inputProps: {
Expand Down

0 comments on commit 4a83f7d

Please sign in to comment.