Skip to content

Commit

Permalink
Merge b56de00 into d7ebf2c
Browse files Browse the repository at this point in the history
  • Loading branch information
duckonomy committed Sep 7, 2022
2 parents d7ebf2c + b56de00 commit 92de361
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 25 deletions.
85 changes: 64 additions & 21 deletions client/src/components/Applications/AppForm/AppFormSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ const FormSchema = (app) => {
schema: { inputs: {}, parameters: {} },
};

Yup.addMethod(Yup.array, 'oneOfSchemas', function (schemas) {
return this.test(
'one-of-schemas',
'Not all items in ${path} match one of the allowed schemas',
(items) =>
items.every((item) => {
return schemas.some((schema) =>
schema.isValidSync(item, { strict: true })
);
})
);
});

(app.definition.parameters || []).forEach((parameter) => {
const param = parameter;
if (!param.value.visible || param.id.startsWith('_')) {
Expand All @@ -25,46 +38,72 @@ const FormSchema = (app) => {
required: param.value.required,
};

field.multiinput = false;
if (param.semantics.maxCardinality > 1) {
field.multiinput = true;
field.maxitems = param.semantics.maxCardinality;
field.minitems = param.semantics.minCardinality;
}

switch (param.value.type) {
case 'bool':
case 'flag':
field.type = 'checkbox';
field.checked = param.value.default || false;
appFields.schema.parameters[param.id] = Yup.boolean();
appFields.schema.parameters[param.id] = field.multiinput
? Yup.array().of(Yup.boolean())
: Yup.boolean();
break;

case 'enumeration':
field.type = 'select';
field.options = param.value.enum_values;
appFields.schema.parameters[param.id] = Yup.string().oneOf(
field.options.map((enumVal) => {
if (typeof enumVal === 'string') {
return enumVal;
}
return Object.keys(enumVal)[0];
})
);
appFields.schema.parameters[param.id] = field.multiinput
? Yup.array().of(
Yup.string().oneOf(
field.options.map((enumVal) => {
if (typeof enumVal === 'string') {
return enumVal;
}
return Object.keys(enumVal)[0];
})
)
)
: Yup.string().oneOf(
field.options.map((enumVal) => {
if (typeof enumVal === 'string') {
return enumVal;
}
return Object.keys(enumVal)[0];
})
);
break;

case 'number':
appFields.schema.parameters[param.id] = Yup.number();
appFields.schema.parameters[param.id] = field.multiinput
? Yup.array().of(Yup.number())
: Yup.number();
field.type = 'number';
break;

case 'string':
field.agaveFile = param.semantics.ontology.includes('agaveFile');
if (param.semantics.ontology.includes('email')) {
field.type = 'email';
appFields.schema.parameters[param.id] = Yup.string().email(
'Must be a valid email.'
);
appFields.schema.parameters[param.id] = field.multiinput
? Yup.array().of(Yup.string().email('Must be a valid email.\n'))
: Yup.string().email('Must be a valid email.');
} else {
field.type = 'text';
appFields.schema.parameters[param.id] = Yup.string();
appFields.schema.parameters[param.id] = field.multiinput
? Yup.array().of(Yup.string())
: Yup.string();
}
break;
default:
appFields.schema.parameters[param.id] = Yup.string();
appFields.schema.parameters[param.id] = field.multiinput
? Yup.array().of(Yup.string())
: Yup.string();
field.type = 'text';
}

Expand Down Expand Up @@ -99,12 +138,8 @@ const FormSchema = (app) => {
description: input.details.description,
required: input.value.required,
};
if (input.semantics.maxCardinality === 1) {
field.type = 'text';
} else {
field.type = 'array';
field.maxItems = input.semantics.maxCardinality;
}

field.type = 'text';
appFields.schema.inputs[input.id] = Yup.string();
if (input.value.required) {
appFields.schema.inputs[input.id] =
Expand All @@ -127,6 +162,14 @@ const FormSchema = (app) => {
input.value.default === null || typeof input.value.default === 'undefined'
? ''
: input.value.default;

field.multiinput = false;
if (input.semantics.maxCardinality > 1) {
field.multiinput = true;
field.maxitems = input.semantics.maxCardinality;
field.minitems = input.semantics.minCardinality;
appFields.schema.inputs[input.id] = Yup.array().of(Yup.string());
}
});
return appFields;
};
Expand Down
123 changes: 119 additions & 4 deletions client/src/components/_common/Form/FormField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from 'reactstrap';
import { Button } from '_common';

import { useField } from 'formik';
import { useField, Field, FieldArray } from 'formik';
import PropTypes from 'prop-types';
import './FormField.scss';

Expand Down Expand Up @@ -63,7 +63,8 @@ const FormField = ({
// which we can spread on <input> and also replace ErrorMessage entirely.
const [field, meta, helpers] = useField(props);
const [openAgaveFileModal, setOpenAgaveFileModal] = useState(false);
const { id, name } = props;
const [openMultiAgaveFileModal, setOpenMultiAgaveFileModal] = useState([]);
const { id, name, multiinput, maxitems, minitems } = props;
const hasAddon = addon !== undefined;
const wrapperType = hasAddon ? 'InputGroup' : '';

Expand All @@ -85,7 +86,12 @@ const FormField = ({
const FieldNote = () => (
<>
{meta.touched && meta.error ? (
<div className="form-field__validation-error">{meta.error}</div>
<div
style={{ whiteSpace: 'pre-wrap' }}
className="form-field__validation-error"
>
{meta.error}
</div>
) : (
description && (
<FormText className="form-field__help" color="muted">
Expand All @@ -111,7 +117,116 @@ const FormField = ({
{label && hasAddon ? <FieldLabel /> : null}
<FormFieldWrapper type={wrapperType}>
{label && !hasAddon ? <FieldLabel /> : null}
{agaveFile ? (
{multiinput ? (
<FieldArray
name={name}
render={(arrayHelpers) => (
<div>
{field.value &&
Array.isArray(field.value) &&
field.value.length > 0 ? (
field.value.map((_, index) => (
<div key={index}>
{agaveFile ? (
<>
<SelectModal
isOpen={openMultiAgaveFileModal.includes(index)}
toggle={() => {
setOpenMultiAgaveFileModal([]);
}}
onSelect={(system, path) => {
arrayHelpers.replace(
index,
`agave://${system}${path}`
);
}}
/>

<InputGroup className="multi-form-field">
<InputGroupAddon addonType="prepend">
<Button
size="sm"
color="secondary"
type="button"
onClick={() =>
setOpenMultiAgaveFileModal([index])
}
>
Select
</Button>
</InputGroupAddon>

<Input
tag={Field}
name={`${name}.${index}`}
type={props.type}
placeholder={props.placeholder}
component="input"
bsSize="sm"
/>

<InputGroupAddon addonType="append">
<Button
size="sm"
color="secondary"
type="button"
onClick={() => arrayHelpers.remove(index)}
>
X
</Button>
</InputGroupAddon>
</InputGroup>
</>
) : (
<>
{hasAddon && addonType === 'prepend' ? addon : null}
<InputGroup className="multi-form-field">
{props.type === 'select' ? (
<Input
{...props}
tag={Field}
name={`${name}.${index}`}
component={props.type}
bsSize="sm"
/>
) : (
<Input
{...props}
tag={Field}
name={`${name}.${index}`}
bsSize="sm"
/>
)}
<InputGroupAddon addonType="append">
<Button
size="sm"
color="secondary"
type="button"
onClick={() => arrayHelpers.remove(index)}
>
X
</Button>
</InputGroupAddon>
</InputGroup>
{hasAddon && addonType === 'append' ? addon : null}
</>
)}
</div>
))
) : (
<div>No Inputs</div>
)}
<Button
style={{ display: 'flex', justifyContent: 'right' }}
color="link"
onClick={() => arrayHelpers.push('')}
>
<b>+ Add Input</b>
</Button>
</div>
)}
/>
) : agaveFile ? (
<>
<SelectModal
isOpen={openAgaveFileModal}
Expand Down
4 changes: 4 additions & 0 deletions client/src/components/_common/Form/FormField.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@
color: #eb6e6e;
padding-top: 5px;
}

.multi-form-field {
padding-bottom: 10px;
}

0 comments on commit 92de361

Please sign in to comment.