From e19d287438cb11d30fae5439412bec9daf64f92f Mon Sep 17 00:00:00 2001 From: Gildas Garcia Date: Fri, 9 Aug 2019 15:11:40 +0200 Subject: [PATCH 1/5] Export useInput from ra-core --- packages/ra-core/src/form/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/ra-core/src/form/index.ts b/packages/ra-core/src/form/index.ts index 1b33a101c4c..018e496b6fa 100644 --- a/packages/ra-core/src/form/index.ts +++ b/packages/ra-core/src/form/index.ts @@ -1,9 +1,10 @@ import addField from './addField'; import FormDataConsumer from './FormDataConsumer'; import FormField from './FormField'; +import useInput from './useInput'; import ValidationError from './ValidationError'; -export { addField, FormDataConsumer, FormField, ValidationError }; +export { addField, FormDataConsumer, FormField, useInput, ValidationError }; export { isRequired } from './FormField'; export * from './validate'; export * from './constants'; From ee1c92c8e5ecb128fec9d67497667c3306544fca Mon Sep 17 00:00:00 2001 From: Gildas Garcia Date: Fri, 9 Aug 2019 15:12:22 +0200 Subject: [PATCH 2/5] Introduce InputPropTypes --- .../ra-ui-materialui/src/input/InputPropTypes.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 packages/ra-ui-materialui/src/input/InputPropTypes.ts diff --git a/packages/ra-ui-materialui/src/input/InputPropTypes.ts b/packages/ra-ui-materialui/src/input/InputPropTypes.ts new file mode 100644 index 00000000000..08de065ab64 --- /dev/null +++ b/packages/ra-ui-materialui/src/input/InputPropTypes.ts @@ -0,0 +1,12 @@ +import PropTypes from 'prop-types'; + +/** + * Common PropTypes for all react-admin inputs + */ +const InputPropTypes = { + label: PropTypes.string, + resource: PropTypes.string, + source: PropTypes.string, +}; + +export default InputPropTypes; From 458c263a620819a10aabde3888a5477d7eadea05 Mon Sep 17 00:00:00 2001 From: Gildas Garcia Date: Fri, 9 Aug 2019 15:12:37 +0200 Subject: [PATCH 3/5] Convert BooleanInput to use useInput --- .../src/input/BooleanInput.js | 144 +++++++++--------- .../src/input/BooleanInput.spec.js | 74 ++++++--- 2 files changed, 127 insertions(+), 91 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/BooleanInput.js b/packages/ra-ui-materialui/src/input/BooleanInput.js index d5f53da29d7..322dad9dc49 100644 --- a/packages/ra-ui-materialui/src/input/BooleanInput.js +++ b/packages/ra-ui-materialui/src/input/BooleanInput.js @@ -1,88 +1,94 @@ -import React, { Component } from 'react'; +import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import FormHelperText from '@material-ui/core/FormHelperText'; import FormGroup from '@material-ui/core/FormGroup'; import Switch from '@material-ui/core/Switch'; -import { addField, FieldTitle } from 'ra-core'; +import { FieldTitle, useInput } from 'ra-core'; import sanitizeRestProps from './sanitizeRestProps'; import InputHelperText from './InputHelperText'; +import InputPropTypes from './InputPropTypes'; -export class BooleanInput extends Component { - handleChange = (event, value) => { - this.props.input.onChange(value); - }; +const BooleanInput = ({ + label, + fullWidth, + helperText, + onBlur, + onChange, + onFocus, + options, + resource, + source, + validate, + ...rest +}) => { + const { + id, + input: { onChange: finalFormOnChange, value, ...inputProps }, + isRequired, + meta: { error, touched }, + } = useInput({ + onBlur, + onChange, + onFocus, + resource, + source, + type: 'checkbox', + validate, + }); - render() { - const { - className, - id, - input, - isRequired, - label, - source, - resource, - options, - fullWidth, - meta, - helperText, - ...rest - } = this.props; + const handleChange = useCallback( + (event, value) => { + finalFormOnChange(value); + }, + [finalFormOnChange] + ); - const { value, ...inputProps } = input; - const { touched, error } = meta; - - return ( - - - } - label={ - - } - /> - {helperText || (touched && !!error) ? ( - - - - ) : null} - - ); - } -} + return ( + + + } + label={ + + } + /> + {helperText || (touched && !!error) ? ( + + + + ) : null} + + ); +}; BooleanInput.propTypes = { - className: PropTypes.string, - id: PropTypes.string, - input: PropTypes.object, - isRequired: PropTypes.bool, - label: PropTypes.string, - resource: PropTypes.string, - source: PropTypes.string, - options: PropTypes.object, + ...InputPropTypes, + ...FormGroup.propTypes, + options: PropTypes.shape(Switch.propTypes), }; BooleanInput.defaultProps = { options: {}, }; -export default addField(BooleanInput); +export default BooleanInput; diff --git a/packages/ra-ui-materialui/src/input/BooleanInput.spec.js b/packages/ra-ui-materialui/src/input/BooleanInput.spec.js index e022cbc91e5..bfa9cb88ea3 100644 --- a/packages/ra-ui-materialui/src/input/BooleanInput.spec.js +++ b/packages/ra-ui-materialui/src/input/BooleanInput.spec.js @@ -1,56 +1,86 @@ import React from 'react'; -import { render, cleanup } from '@testing-library/react'; +import { render, cleanup, fireEvent } from '@testing-library/react'; +import { Form } from 'react-final-form'; -import { BooleanInput } from './BooleanInput'; +import BooleanInput from './BooleanInput'; describe('', () => { afterEach(cleanup); const defaultProps = { - id: 'bar', - resource: 'foo', - source: 'bar', - input: {}, - meta: {}, + resource: 'posts', + source: 'isPublished', }; it('should render as a checkbox', () => { - const { getByLabelText } = render(); - expect(getByLabelText('resources.foo.fields.bar').type).toBe( + const { getByLabelText } = render( +
} + /> + ); + expect(getByLabelText('resources.posts.fields.isPublished').type).toBe( 'checkbox' ); }); it('should be checked if the value is true', () => { const { getByLabelText } = render( - + } + /> ); - expect(getByLabelText('resources.foo.fields.bar').checked).toBe(true); + expect( + getByLabelText('resources.posts.fields.isPublished').checked + ).toBe(true); }); it('should not be checked if the value is false', () => { const { getByLabelText } = render( - + } + /> ); - expect(getByLabelText('resources.foo.fields.bar').checked).toBe(false); + expect( + getByLabelText('resources.posts.fields.isPublished').checked + ).toBe(false); }); it('should not be checked if the value is undefined', () => { const { getByLabelText } = render( - + } + /> ); - expect(getByLabelText('resources.foo.fields.bar').checked).toBe(false); + expect( + getByLabelText('resources.posts.fields.isPublished').checked + ).toBe(false); }); it('should displays errors', () => { - const { queryAllByText } = render( - 'ra.validation.error'; + + const { getByLabelText, queryAllByText } = render( + ( + + )} /> ); - expect(queryAllByText('foobar')).toHaveLength(1); + const input = getByLabelText('resources.posts.fields.isPublished'); + fireEvent.click(input); + expect(input.checked).toBe(false); + + fireEvent.blur(input); + expect(queryAllByText('ra.validation.error')).toHaveLength(1); }); }); From 62d1fa686ce9f9b8e98defccdb9a546a13b7ff75 Mon Sep 17 00:00:00 2001 From: Gildas Garcia Date: Fri, 9 Aug 2019 15:31:43 +0200 Subject: [PATCH 4/5] Fix e2e tests --- cypress/integration/create.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/integration/create.js b/cypress/integration/create.js index ac788d379a6..f61c6b38e38 100644 --- a/cypress/integration/create.js +++ b/cypress/integration/create.js @@ -164,7 +164,7 @@ describe('Create Page', () => { value: 'Test teaser', }, { - type: 'input', + type: 'checkbox', name: 'commentable', value: 'false', }, From dffd84b8ccfe04519acbdf645a83ea61a5e2d360 Mon Sep 17 00:00:00 2001 From: Gildas Garcia Date: Fri, 9 Aug 2019 16:01:25 +0200 Subject: [PATCH 5/5] Export InputPropTypes --- packages/ra-ui-materialui/src/input/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/ra-ui-materialui/src/input/index.js b/packages/ra-ui-materialui/src/input/index.js index df4ba324a94..e94777d7d40 100644 --- a/packages/ra-ui-materialui/src/input/index.js +++ b/packages/ra-ui-materialui/src/input/index.js @@ -9,6 +9,7 @@ import DisabledInput from './DisabledInput'; import FileInput from './FileInput'; import ImageInput from './ImageInput'; import InputHelperText from './InputHelperText'; +import InputPropTypes from './InputPropTypes'; import Labeled from './Labeled'; import LongTextInput from './LongTextInput'; import NullableBooleanInput from './NullableBooleanInput'; @@ -34,6 +35,7 @@ export { FileInput, ImageInput, InputHelperText, + InputPropTypes, Labeled, LongTextInput, NullableBooleanInput,