Implement form validation

feature/gui
Alinson S. Xavier 4 years ago
parent 524299a3c2
commit af2a8b67be
No known key found for this signature in database
GPG Key ID: DCA0DAD4D2F58624

@ -1,11 +1,11 @@
import { useState } from 'react';
import form_styles from './Form.module.css'; import form_styles from './Form.module.css';
import Button from './Button'; import Button from './Button';
import { validate } from './Form';
const DictInputRow = (props) => { const DictInputRow = (props) => {
const dict = { ...props.value }; const dict = { ...props.value };
if (!props.disableKeys) { if (!props.disableKeys) {
dict[""] = ""; dict[""] = "0";
} }
let unit = ""; let unit = "";
@ -36,6 +36,15 @@ const DictInputRow = (props) => {
if (index > 0) { if (index > 0) {
label = ""; label = "";
} }
let isValid = true;
if (props.validate !== undefined) {
isValid = validate(props.validate, dict[key]);
}
let className = "";
if (!isValid) className = form_styles.invalid;
form.push( form.push(
<div className={form_styles.FormRow} key={index}> <div className={form_styles.FormRow} key={index}>
<label>{label}</label> <label>{label}</label>
@ -52,6 +61,7 @@ const DictInputRow = (props) => {
data-index={index} data-index={index}
value={dict[key]} value={dict[key]}
placeholder={props.valuePlaceholder} placeholder={props.valuePlaceholder}
className={className}
onChange={e => onChangeValue(key, e.target.value)} onChange={e => onChangeValue(key, e.target.value)}
/> />
{tooltip} {tooltip}

@ -2,7 +2,6 @@ import form_styles from './Form.module.css';
import Button from './Button'; import Button from './Button';
const FileInputRow = (props) => { const FileInputRow = (props) => {
let tooltip = ""; let tooltip = "";
if (props.tooltip != undefined) { if (props.tooltip != undefined) {
tooltip = <Button label="?" kind="inline" tooltip={props.tooltip} />; tooltip = <Button label="?" kind="inline" tooltip={props.tooltip} />;
@ -10,7 +9,7 @@ const FileInputRow = (props) => {
return <div className={form_styles.FormRow}> return <div className={form_styles.FormRow}>
<label>{props.label}</label> <label>{props.label}</label>
<input type="text" disabled="disabled" /> <input type="text" value={props.value} disabled="disabled" />
<Button label="Upload" kind="inline" /> <Button label="Upload" kind="inline" />
<Button label="Clear" kind="inline" /> <Button label="Clear" kind="inline" />
<Button label="Template" kind="inline" /> <Button label="Template" kind="inline" />

@ -1,3 +1,17 @@
const VALIDATION_REGEX = {
int: new RegExp('^[0-9]+$'),
intList: new RegExp('^\[[0-9]+(,[0-9]+)*\]$'),
float: new RegExp('^[0-9]*\\.?[0-9]*$'),
};
export const validate = (kind, value) => {
if (value.length == 0) return false;
if (!VALIDATION_REGEX[kind].test(value)) {
return false;
}
return true;
};
const Form = (props) => { const Form = (props) => {
return <>{props.children}</>; return <>{props.children}</>;
}; };

@ -21,3 +21,8 @@
.FormRow_unit { .FormRow_unit {
color: rgba(0, 0, 0, 0.4); color: rgba(0, 0, 0, 0.4);
} }
.invalid {
border: 2px solid #faa !important;
background-color: rgba(255, 0, 0, 0.05);
}

@ -12,7 +12,7 @@ const defaultData = {
parameters: { parameters: {
"time horizon (years)": "1", "time horizon (years)": "1",
"building period (years)": "[1]", "building period (years)": "[1]",
"annual inflation rate (%)": "0.0", "annual inflation rate (%)": "0",
}, },
products: { products: {
}, },
@ -21,15 +21,13 @@ const defaultData = {
}; };
const defaultProduct = { const defaultProduct = {
"acquisition cost ($/tonne)": "0.00", "initial amounts": {},
"disposal cost ($/tonne)": "0.00", "acquisition cost ($/tonne)": "0",
"disposal cost ($/tonne)": "0",
"disposal limit (tonne)": "0", "disposal limit (tonne)": "0",
"transportation cost ($/km/tonne)": "0.00", "transportation cost ($/km/tonne)": "0",
"transportation energy (J/km/tonne)": "0", "transportation energy (J/km/tonne)": "0",
"transportation emissions (J/km/tonne)": { "transportation emissions (J/km/tonne)": {}
"CO2": 0,
"NH2": 0,
}
}; };
const randomPosition = () => { const randomPosition = () => {

@ -19,6 +19,7 @@ const ParametersBlock = (props) => {
tooltip="Number of years in the simulation." tooltip="Number of years in the simulation."
value={props.value["time horizon (years)"]} value={props.value["time horizon (years)"]}
onChange={v => onChangeField("time horizon (years)", v)} onChange={v => onChangeField("time horizon (years)", v)}
validate="int"
/> />
<TextInputRow <TextInputRow
label="Building period" label="Building period"
@ -26,6 +27,7 @@ const ParametersBlock = (props) => {
tooltip="List of years in which we are allowed to open new plants. For example, if this parameter is set to [1,2,3], we can only open plants during the first three years. By default, this equals [1]; that is, plants can only be opened during the first year." tooltip="List of years in which we are allowed to open new plants. For example, if this parameter is set to [1,2,3], we can only open plants during the first three years. By default, this equals [1]; that is, plants can only be opened during the first year."
value={props.value["building period (years)"]} value={props.value["building period (years)"]}
onChange={v => onChangeField("building period (years)", v)} onChange={v => onChangeField("building period (years)", v)}
validate="intList"
/> />
<TextInputRow <TextInputRow
label="Annual inflation rate" label="Annual inflation rate"
@ -33,6 +35,7 @@ const ParametersBlock = (props) => {
tooltip="Rate of inflation applied to all costs." tooltip="Rate of inflation applied to all costs."
value={props.value["annual inflation rate (%)"]} value={props.value["annual inflation rate (%)"]}
onChange={v => onChangeField("annual inflation rate (%)", v)} onChange={v => onChangeField("annual inflation rate (%)", v)}
validate="float"
/> />
</Form> </Form>
</Card> </Card>

@ -13,6 +13,8 @@ const ProductBlock = (props) => {
props.onChange(newProduct); props.onChange(newProduct);
}; };
const nCenters = Object.keys(props.value["initial amounts"]).length;
return ( return (
<> <>
<Section title={props.name} /> <Section title={props.name} />
@ -20,6 +22,7 @@ const ProductBlock = (props) => {
<Form> <Form>
<h1>General information</h1> <h1>General information</h1>
<FileInputRow <FileInputRow
value={`${nCenters} collection centers`}
label="Initial amounts" label="Initial amounts"
tooltip="A dictionary mapping the name of each location to its description (see below). If this product is not initially available, this key may be omitted." tooltip="A dictionary mapping the name of each location to its description (see below). If this product is not initially available, this key may be omitted."
/> />
@ -29,6 +32,7 @@ const ProductBlock = (props) => {
tooltip="The cost to acquire one tonne of this product from collection centers. Does not apply to plant outputs." tooltip="The cost to acquire one tonne of this product from collection centers. Does not apply to plant outputs."
value={props.value["acquisition cost ($/tonne)"]} value={props.value["acquisition cost ($/tonne)"]}
onChange={v => onChange("acquisition cost ($/tonne)", v)} onChange={v => onChange("acquisition cost ($/tonne)", v)}
validate="float"
/> />
<h1>Disposal</h1> <h1>Disposal</h1>
@ -38,6 +42,7 @@ const ProductBlock = (props) => {
tooltip="The cost to dispose of one tonne of this product at a collection center, without further processing. Does not apply to plant outputs." tooltip="The cost to dispose of one tonne of this product at a collection center, without further processing. Does not apply to plant outputs."
value={props.value["disposal cost ($/tonne)"]} value={props.value["disposal cost ($/tonne)"]}
onChange={v => onChange("disposal cost ($/tonne)", v)} onChange={v => onChange("disposal cost ($/tonne)", v)}
validate="float"
/> />
<TextInputRow <TextInputRow
label="Disposal limit" label="Disposal limit"
@ -45,6 +50,7 @@ const ProductBlock = (props) => {
tooltip="The maximum amount of this product that can be disposed of across all collection centers, without further processing." tooltip="The maximum amount of this product that can be disposed of across all collection centers, without further processing."
value={props.value["disposal limit (tonne)"]} value={props.value["disposal limit (tonne)"]}
onChange={v => onChange("disposal limit (tonne)", v)} onChange={v => onChange("disposal limit (tonne)", v)}
validate="float"
/> />
<h1>Transportation</h1> <h1>Transportation</h1>
@ -54,6 +60,7 @@ const ProductBlock = (props) => {
tooltip="The cost to transport this product." tooltip="The cost to transport this product."
value={props.value["transportation cost ($/km/tonne)"]} value={props.value["transportation cost ($/km/tonne)"]}
onChange={v => onChange("transportation cost ($/km/tonne)", v)} onChange={v => onChange("transportation cost ($/km/tonne)", v)}
validate="float"
/> />
<TextInputRow <TextInputRow
label="Transportation energy" label="Transportation energy"
@ -61,15 +68,16 @@ const ProductBlock = (props) => {
tooltip="The energy required to transport this product." tooltip="The energy required to transport this product."
value={props.value["transportation energy (J/km/tonne)"]} value={props.value["transportation energy (J/km/tonne)"]}
onChange={v => onChange("transportation energy (J/km/tonne)", v)} onChange={v => onChange("transportation energy (J/km/tonne)", v)}
validate="float"
/> />
<DictInputRow <DictInputRow
label="Transportation emissions" label="Transportation emissions"
unit="J/km/tonne" unit="J/km/tonne"
tooltip="A dictionary mapping the name of each greenhouse gas, produced to transport one tonne of this product along one kilometer, to the amount of gas produced (in tonnes)." tooltip="A dictionary mapping the name of each greenhouse gas, produced to transport one tonne of this product along one kilometer, to the amount of gas produced (in tonnes)."
keyPlaceholder="Emission name" keyPlaceholder="Emission name"
valuePlaceholder="0"
value={props.value["transportation emissions (J/km/tonne)"]} value={props.value["transportation emissions (J/km/tonne)"]}
onChange={v => onChange("transportation emissions (J/km/tonne)", v)} onChange={v => onChange("transportation emissions (J/km/tonne)", v)}
validate="float"
/> />
</Form> </Form>
</Card> </Card>

@ -1,5 +1,6 @@
import form_styles from './Form.module.css'; import form_styles from './Form.module.css';
import Button from './Button'; import Button from './Button';
import { validate } from './Form';
const TextInputRow = (props) => { const TextInputRow = (props) => {
let unit = ""; let unit = "";
@ -12,6 +13,14 @@ const TextInputRow = (props) => {
tooltip = <Button label="?" kind="inline" tooltip={props.tooltip} />; tooltip = <Button label="?" kind="inline" tooltip={props.tooltip} />;
} }
let isValid = true;
if (props.validate !== undefined) {
isValid = validate(props.validate, props.value);
}
let className = "";
if (!isValid) className = form_styles.invalid;
return <div className={form_styles.FormRow}> return <div className={form_styles.FormRow}>
<label> <label>
{props.label} {unit} {props.label} {unit}
@ -21,6 +30,7 @@ const TextInputRow = (props) => {
placeholder={props.default} placeholder={props.default}
disabled={props.disabled} disabled={props.disabled}
value={props.value} value={props.value}
className={className}
onChange={e => props.onChange(e.target.value)} onChange={e => props.onChange(e.target.value)}
/> />
{tooltip} {tooltip}

Loading…
Cancel
Save