Skip to content

Commit

Permalink
enforce/display required for Select and DateTime;
Browse files Browse the repository at this point in the history
fix implicit date timezone (#127)
  • Loading branch information
sheppard committed Mar 19, 2021
1 parent c5e6bc2 commit 5002ac9
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 8 deletions.
4 changes: 2 additions & 2 deletions packages/material/src/inputs/DateTime.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function DateTime({ type, hint, ...rest }) {
type = type.toLowerCase();

const Picker = pickers[type],
[, { value }, { setValue }] = useField(rest.name);
[, { value, error, touched }, { setValue }] = useField(rest.name);

useEffect(() => {
if (value instanceof Date) {
Expand All @@ -55,7 +55,7 @@ export default function DateTime({ type, hint, ...rest }) {
fullWidth
margin="dense"
component={Picker}
helperText={hint}
helperText={!!error && touched ? error : hint}
{...rest}
/>
</MuiPickersUtilsProvider>
Expand Down
20 changes: 18 additions & 2 deletions packages/material/src/inputs/Select.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@ ContextCheckbox.propTypes = {
field: PropTypes.string
};

export default function Select({ choices, label, renderValue, ...rest }) {
export default function Select({
choices,
label,
required,
renderValue,
...rest
}) {
const { name: fieldName, type, hint } = rest,
{ errors, touched } = useFormikContext(),
showError = !!getIn(errors, fieldName) && getIn(touched, fieldName),
multiple = type === 'select';

if (multiple && !renderValue) {
Expand All @@ -39,10 +47,17 @@ export default function Select({ choices, label, renderValue, ...rest }) {

return (
<FormControl fullWidth margin="dense">
<InputLabel htmlFor={fieldName}>{label}</InputLabel>
<InputLabel
htmlFor={fieldName}
required={required}
error={showError}
>
{label}
</InputLabel>
<Field
component={FMuiSelect}
multiple={multiple}
required={required}
renderValue={renderValue}
{...rest}
>
Expand All @@ -68,5 +83,6 @@ export default function Select({ choices, label, renderValue, ...rest }) {
Select.propTypes = {
choices: PropTypes.arrayOf(PropTypes.object),
label: PropTypes.string,
required: PropTypes.bool,
renderValue: PropTypes.func
};
7 changes: 4 additions & 3 deletions packages/material/src/inputs/date-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import formatISO9075 from 'date-fns/formatISO9075';
import parseISO from 'date-fns/parseISO';

export const format = {
date: value => formatISO9075(value, { representation: 'date' }),
Expand All @@ -7,7 +8,7 @@ export const format = {
};

export const parse = {
date: value => (value ? new Date(value) : null),
time: value => (value ? new Date('9999-01-01 ' + value) : null),
datetime: value => (value ? new Date(value) : null)
date: value => (value ? parseISO(value) : null),
time: value => (value ? parseISO('9999-01-01 ' + value) : null),
datetime: value => (value ? parseISO(value) : null)
};
31 changes: 30 additions & 1 deletion packages/outbox/src/outbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ class Outbox {
this.app
.callPlugins('validate', [data, modelConf])
.forEach(pluginErrors => {
Object.assign(errors, pluginErrors);
mergeErrors(errors, pluginErrors);
});
return errors;
}
Expand Down Expand Up @@ -1131,6 +1131,35 @@ class Outbox {
}
}

function mergeErrors(errors, newErrors) {
if (
!newErrors ||
typeof newErrors !== 'object' ||
!Object.keys(newErrors).length
) {
return errors;
}
Object.entries(newErrors).forEach(([key, newError]) => {
const error = errors[key];
if (
!error ||
typeof error !== typeof newError ||
Array.isArray(error) !== Array.isArray(newError)
) {
errors[key] = newError;
} else if (typeof error === 'string') {
errors[key] = error + ' • ' + newError;
} else if (Array.isArray(error)) {
errors[key] = error
.map((err, i) => mergeErrors(err, newError[i]))
.concat(newError.slice(error.length));
} else if (typeof error === 'object') {
errors[key] = mergeErrors(error, newError);
}
});
return errors;
}

var outbox = new Outbox(ds);

function getOutbox(store) {
Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/components/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export default function Form({
initialErrors={errors}
initialTouched={errors}
validate={values => validate(values, modelConf)}
validateOnBlur={true}
validateOnChange={false}
onSubmit={handleSubmit}
enableReinitialize={true}
>
Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as inputs from './inputs/index';
import * as icons from './icons';
import * as views from './views/index';
import { init, start, unmount } from './init';
import validate from './validate';

export default {
name: 'react',
Expand Down Expand Up @@ -66,6 +67,7 @@ export default {
},

start,
validate,

createInstance(component, root, app) {
if (!app) {
Expand Down
45 changes: 45 additions & 0 deletions packages/react/src/validate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export default function validate(values, modelConf) {
return validateRequired(values, modelConf && modelConf.form) || {};
}

function validateRequired(values, form) {
if (!values || !form) {
return null;
}

const errors = {};

form.forEach(field => {
const name = field['wq:ForeignKey'] ? `${field.name}_id` : field.name,
value = values[name];
if (isMissing(value, field)) {
errors[name] = 'This field is required.';
} else if (value && field.type === 'repeat') {
const nestedErrors = value.map(row =>
validateRequired(row, field.children)
);
if (nestedErrors.some(row => row)) {
errors[name] = nestedErrors;
}
} else if (field.type === 'group') {
if (name === '') {
Object.assign(errors, validateRequired(values, field.children));
} else if (value) {
const nestedErrors = validateRequired(value, field.children);
if (nestedErrors) {
errors[name] = nestedErrors;
}
}
}
});

return Object.keys(errors).length > 0 ? errors : null;
}

function isMissing(value, field) {
if (field.required || (field.bind && field.bind.required)) {
return value === undefined || value === null || value === '';
} else {
return false;
}
}

0 comments on commit 5002ac9

Please sign in to comment.