From 739849025ee57fdc2095817291c085d1772c52fa Mon Sep 17 00:00:00 2001 From: djhi Date: Tue, 13 Aug 2019 18:33:45 +0200 Subject: [PATCH 1/2] [RFR] Migrate NullableBooleanInput to use useInput --- .../src/input/NullableBooleanInput.js | 178 ++++++++---------- .../src/input/NullableBooleanInput.spec.js | 47 ++--- 2 files changed, 106 insertions(+), 119 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/NullableBooleanInput.js b/packages/ra-ui-materialui/src/input/NullableBooleanInput.js index 473c6ed406a..547cac1dc13 100644 --- a/packages/ra-ui-materialui/src/input/NullableBooleanInput.js +++ b/packages/ra-ui-materialui/src/input/NullableBooleanInput.js @@ -1,118 +1,100 @@ -import React, { Component } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import TextField from '@material-ui/core/TextField'; import MenuItem from '@material-ui/core/MenuItem'; -import { withStyles, createStyles } from '@material-ui/core/styles'; -import compose from 'recompose/compose'; +import { makeStyles } from '@material-ui/core/styles'; import classnames from 'classnames'; -import { addField, translate, FieldTitle } from 'ra-core'; +import { useInput, useTranslate, FieldTitle } from 'ra-core'; import sanitizeRestProps from './sanitizeRestProps'; import InputHelperText from './InputHelperText'; -const styles = theme => - createStyles({ - input: { width: theme.spacing(16) }, - }); - -export class NullableBooleanInput extends Component { - state = { - value: this.props.input.value, - }; +const useStyles = makeStyles(theme => ({ + input: { width: theme.spacing(16) }, +})); - componentWillReceiveProps(nextProps) { - if (nextProps.input.value !== this.props.input.value) { - this.setState({ value: nextProps.input.value }); - } - } - - handleChange = event => { - this.props.input.onChange( - this.getBooleanFromString(event.target.value) - ); - this.setState({ value: event.target.value }); - }; +const getBooleanFromString = value => { + if (value === 'true') return true; + if (value === 'false') return false; + return null; +}; - getBooleanFromString = value => { - if (value === 'true') return true; - if (value === 'false') return false; - return null; - }; +const getStringFromBoolean = value => { + if (value === true) return 'true'; + if (value === false) return 'false'; + return ''; +}; - getStringFromBoolean = value => { - if (value === true) return 'true'; - if (value === false) return 'false'; - return ''; - }; +const NullableBooleanInput = ({ + className, + helperText, + label, + onBlur, + onChange, + onFocus, + options, + resource, + source, + validate, + ...rest +}) => { + const classes = useStyles(); + const translate = useTranslate(); - render() { - const { - classes, - className, - isRequired, - label, - meta, - options, - resource, - source, - translate, - helperText, - ...rest - } = this.props; - const { touched, error } = meta; - return ( - - } - error={!!(touched && error)} - helperText={ - - } - className={classnames(classes.input, className)} - {...options} - {...sanitizeRestProps(rest)} - onChange={this.handleChange} - > - - - {translate('ra.boolean.false')} - - {translate('ra.boolean.true')} - - ); - } -} + const { + id, + input, + isRequired, + meta: { error, touched }, + } = useInput({ + format: getStringFromBoolean, + onBlur, + onChange, + onFocus, + parse: getBooleanFromString, + resource, + source, + type: 'checkbox', + validate, + }); + return ( + + } + error={!!(touched && error)} + helperText={ + + } + className={classnames(classes.input, className)} + {...options} + {...sanitizeRestProps(rest)} + > + + {translate('ra.boolean.false')} + {translate('ra.boolean.true')} + + ); +}; NullableBooleanInput.propTypes = { - classes: PropTypes.object, - className: PropTypes.string, - input: PropTypes.object, - isRequired: PropTypes.bool, label: PropTypes.string, - meta: PropTypes.object, options: PropTypes.object, resource: PropTypes.string, source: PropTypes.string, - translate: PropTypes.func.isRequired, }; -const enhance = compose( - addField, - translate, - withStyles(styles) -); - -export default enhance(NullableBooleanInput); +export default NullableBooleanInput; diff --git a/packages/ra-ui-materialui/src/input/NullableBooleanInput.spec.js b/packages/ra-ui-materialui/src/input/NullableBooleanInput.spec.js index 8b4bb441a84..41687a2f3e4 100644 --- a/packages/ra-ui-materialui/src/input/NullableBooleanInput.spec.js +++ b/packages/ra-ui-materialui/src/input/NullableBooleanInput.spec.js @@ -1,35 +1,40 @@ -import { shallow } from 'enzyme'; import React from 'react'; +import { fireEvent, render } from '@testing-library/react'; +import { Form } from 'react-final-form'; -import { NullableBooleanInput } from './NullableBooleanInput'; +import NullableBooleanInput from './NullableBooleanInput'; describe('', () => { const defaultProps = { - input: {}, - meta: {}, - classes: {}, - translate: x => x, + source: 'isPublished', + resource: 'posts', }; it('should give three different choices for true, false or unknown', () => { - const wrapper = shallow( - + let formApi; + const { getByText, getByRole, getAllByRole } = render( +
{ + formApi = form; + return ; + }} + /> ); - const MenuItemElements = wrapper.find( - 'WithStyles(ForwardRef(MenuItem))' - ); - expect(MenuItemElements.length).toEqual(3); + const select = getByRole('button'); + fireEvent.click(select); + const options = getAllByRole('option'); + expect(options.length).toEqual(3); - const MenuItemElement1 = MenuItemElements.at(0); - expect(MenuItemElement1.prop('value')).toEqual(''); - expect(MenuItemElement1.children().length).toEqual(0); + fireEvent.click(options[0]); + expect(formApi.getState().values.isPublished).toBeNull(); - const MenuItemElement2 = MenuItemElements.at(1); - expect(MenuItemElement2.prop('value')).toEqual('false'); - expect(MenuItemElement2.childAt(0).text()).toEqual('ra.boolean.false'); + fireEvent.click(select); + fireEvent.click(getByText('ra.boolean.false')); + expect(formApi.getState().values.isPublished).toEqual(false); - const MenuItemElement3 = MenuItemElements.at(2); - expect(MenuItemElement3.prop('value')).toEqual('true'); - expect(MenuItemElement3.childAt(0).text()).toEqual('ra.boolean.true'); + fireEvent.click(select); + fireEvent.click(getByText('ra.boolean.true')); + expect(formApi.getState().values.isPublished).toEqual(true); }); }); From f938d9301ef78eae27be1bc4b8f49cd8c30aa86e Mon Sep 17 00:00:00 2001 From: djhi Date: Tue, 13 Aug 2019 18:36:47 +0200 Subject: [PATCH 2/2] Fix accessibility --- packages/ra-ui-materialui/src/input/NullableBooleanInput.js | 2 +- .../ra-ui-materialui/src/input/NullableBooleanInput.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/NullableBooleanInput.js b/packages/ra-ui-materialui/src/input/NullableBooleanInput.js index 547cac1dc13..7266e11738f 100644 --- a/packages/ra-ui-materialui/src/input/NullableBooleanInput.js +++ b/packages/ra-ui-materialui/src/input/NullableBooleanInput.js @@ -83,7 +83,7 @@ const NullableBooleanInput = ({ {...options} {...sanitizeRestProps(rest)} > - + {translate('ra.boolean.null')} {translate('ra.boolean.false')} {translate('ra.boolean.true')} diff --git a/packages/ra-ui-materialui/src/input/NullableBooleanInput.spec.js b/packages/ra-ui-materialui/src/input/NullableBooleanInput.spec.js index 41687a2f3e4..6b7b1e9202c 100644 --- a/packages/ra-ui-materialui/src/input/NullableBooleanInput.spec.js +++ b/packages/ra-ui-materialui/src/input/NullableBooleanInput.spec.js @@ -26,7 +26,7 @@ describe('', () => { const options = getAllByRole('option'); expect(options.length).toEqual(3); - fireEvent.click(options[0]); + fireEvent.click(getByText('ra.boolean.null')); expect(formApi.getState().values.isPublished).toBeNull(); fireEvent.click(select);