Skip to content

Commit

Permalink
Fix array input validation
Browse files Browse the repository at this point in the history
  • Loading branch information
djhi committed Apr 12, 2021
1 parent 34dfc0c commit 815767b
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 13 deletions.
55 changes: 54 additions & 1 deletion packages/ra-ui-materialui/src/input/ArrayInput.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { Form } from 'react-final-form';
import arrayMutators from 'final-form-arrays';

import ArrayInput from './ArrayInput';
import NumberInput from './NumberInput';
import TextInput from './TextInput';
import SimpleFormIterator from '../form/SimpleFormIterator';
import { minLength, required } from 'ra-core';

describe('<ArrayInput />', () => {
const onSubmit = jest.fn();
Expand Down Expand Up @@ -126,4 +127,56 @@ describe('<ArrayInput />', () => {
)
).toEqual(['bar', 'baz']);
});

it('should apply validation to both itself and its inner inputs', async () => {
const { getByText, getAllByLabelText, queryByText } = render(
<FinalForm
render={() => (
<form>
<ArrayInput
resource="bar"
source="arr"
validate={[minLength(2, 'array_min_length')]}
>
<SimpleFormIterator>
<TextInput
source="id"
validate={[required('id_required')]}
/>
<TextInput
source="foo"
validate={[required('foo_required')]}
/>
</SimpleFormIterator>
</ArrayInput>
</form>
)}
/>
);

fireEvent.click(getByText('ra.action.add'));
expect(queryByText('array_min_length')).not.toBeNull();
fireEvent.click(getByText('ra.action.add'));
const firstId = getAllByLabelText('resources.bar.fields.id *')[0];
fireEvent.change(firstId, {
target: { value: 'aaa' },
});
fireEvent.change(firstId, {
target: { value: '' },
});
fireEvent.blur(firstId);
const firstFoo = getAllByLabelText('resources.bar.fields.foo *')[0];
fireEvent.change(firstFoo, {
target: { value: 'aaa' },
});
fireEvent.change(firstFoo, {
target: { value: '' },
});
fireEvent.blur(firstFoo);
expect(queryByText('array_min_length')).toBeNull();
await waitFor(() => {
expect(queryByText('id_required')).not.toBeNull();
expect(queryByText('foo_required')).not.toBeNull();
});
});
});
32 changes: 20 additions & 12 deletions packages/ra-ui-materialui/src/input/ArrayInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,21 @@ const ArrayInput: FC<ArrayInputProps> = ({
);
}

const { error, submitError, touched } = fieldProps.meta;
const { error, submitError, touched, dirty } = fieldProps.meta;
const arrayInputError = getArrayInputError(error || submitError);

return (
<FormControl
fullWidth
margin="normal"
className={className}
error={touched && !!(error || submitError)}
error={(touched || dirty) && !!arrayInputError}
{...sanitizeInputRestProps(rest)}
>
<InputLabel
htmlFor={source}
shrink
error={touched && !!(error || submitError)}
error={(touched || dirty) && !!arrayInputError}
>
<FieldTitle
label={label}
Expand All @@ -118,15 +119,6 @@ const ArrayInput: FC<ArrayInputProps> = ({
isRequired={isRequired(validate)}
/>
</InputLabel>
{!!(touched && (error || submitError)) || helperText ? (
<FormHelperText error={touched && !!(error || submitError)}>
<InputHelperText
touched={touched}
error={error || submitError}
helperText={helperText}
/>
</FormHelperText>
) : null}
{cloneElement(Children.only(children), {
...fieldProps,
record,
Expand All @@ -136,6 +128,15 @@ const ArrayInput: FC<ArrayInputProps> = ({
margin,
disabled,
})}
{!!((touched || dirty) && arrayInputError) || helperText ? (
<FormHelperText error={(touched || dirty) && !!arrayInputError}>
<InputHelperText
touched={touched || dirty}
error={arrayInputError}
helperText={helperText}
/>
</FormHelperText>
) : null}
</FormControl>
);
};
Expand Down Expand Up @@ -163,6 +164,13 @@ ArrayInput.defaultProps = {
fullWidth: true,
};

export const getArrayInputError = error => {
if (Array.isArray(error)) {
return undefined;
}
return error;
};

export interface ArrayInputProps extends InputProps {
children: ReactElement;
disabled?: boolean;
Expand Down

0 comments on commit 815767b

Please sign in to comment.