From 6ee7f0be778240a8cf3b7315d6d4674d9005c7af Mon Sep 17 00:00:00 2001 From: Thierry Deo Date: Tue, 23 Jul 2019 09:20:06 +0200 Subject: [PATCH 1/2] Add emptyText prop to SelectInput --- docs/Inputs.md | 11 +++-- .../ra-ui-materialui/src/input/SelectInput.js | 18 +++++++- .../src/input/SelectInput.spec.js | 44 +++++++++++++++++++ 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/docs/Inputs.md b/docs/Inputs.md index 99f2fbed991..776d516e904 100644 --- a/docs/Inputs.md +++ b/docs/Inputs.md @@ -174,7 +174,7 @@ By default the component matches choices with the current input searchText: if i If you want to limit the initial choices shown to the current value only, you can set the `limitChoicesToValue` prop. -When dealing with a large amount of `choices` you may need to limit the number of suggestions that are rendered in order to maintain usable performance. The `shouldRenderSuggestions` is an optional prop that allows you to set conditions on when to render suggestions. An easy way to improve performance would be to skip rendering until the user has entered 2 or 3 characters in the search box. This lowers the result set significantly, and might be all you need (depending on your data set). +When dealing with a large amount of `choices` you may need to limit the number of suggestions that are rendered in order to maintain usable performance. The `shouldRenderSuggestions` is an optional prop that allows you to set conditions on when to render suggestions. An easy way to improve performance would be to skip rendering until the user has entered 2 or 3 characters in the search box. This lowers the result set significantly, and might be all you need (depending on your data set). Ex. ` { return val.trim().length > 2 }} />` would not render any suggestions until the 3rd character was entered. This prop is passed to the underlying `react-autosuggest` component and is documented [here](https://github.com/moroshko/react-autosuggest#should-render-suggestions-prop). `` renders a material-ui `` component. Use the `options` attribute to override any of the `` attributes: @@ -289,7 +289,7 @@ By default the component matches choices with the current input searchText. For If you want to limit the initial choices shown to the current value only, you can set the `limitChoicesToValue` prop. -When dealing with a large amount of `choices` you may need to limit the number of suggestions that are rendered in order to maintain usable performance. The `shouldRenderSuggestions` is an optional prop that allows you to set conditions on when to render suggestions. An easy way to improve performance would be to skip rendering until the user has entered 2 or 3 characters in the search box. This lowers the result set significantly, and might be all you need (depending on your data set). +When dealing with a large amount of `choices` you may need to limit the number of suggestions that are rendered in order to maintain usable performance. The `shouldRenderSuggestions` is an optional prop that allows you to set conditions on when to render suggestions. An easy way to improve performance would be to skip rendering until the user has entered 2 or 3 characters in the search box. This lowers the result set significantly, and might be all you need (depending on your data set). Ex. ` { return val.trim().length > 2 }} />` would not render any suggestions until the 3rd character was entered. This prop is passed to the underlying `react-autosuggest` component and is documented [here](https://github.com/moroshko/react-autosuggest#should-render-suggestions-prop). Lastly, `` renders a [material-ui-chip-input](https://github.com/TeamWertarbyte/material-ui-chip-input) component. Use the `options` attribute to override any of the `` attributes: @@ -1008,8 +1008,7 @@ const FullNameField = ({ record }) => {record.first_name} {record.last_nam }/> ``` -Enabling the `allowEmpty` props adds an empty choice (with a default `null` value, which you can overwrite with the `emptyValue` prop) on top of the options, and makes the value nullable: - +Enabling the `allowEmpty` props adds an empty choice (with a default `null` value, which you can overwrite with the `emptyValue` prop) on top of the options, and makes the value nullable. You can furthermore customize the `MenuItem` for the empty choice by using the `emptyText` prop, which can receive either a string or a React Element, which doesn't receive any props. ```jsx ``` -You can use a custom field name by setting `disableValue` prop: +You can use a custom field name by setting `disableValue` prop: ```jsx const choices = [ diff --git a/packages/ra-ui-materialui/src/input/SelectInput.js b/packages/ra-ui-materialui/src/input/SelectInput.js index 75d23396986..d73ab35b814 100644 --- a/packages/ra-ui-materialui/src/input/SelectInput.js +++ b/packages/ra-ui-materialui/src/input/SelectInput.js @@ -137,6 +137,7 @@ export const SelectInput = ({ classes, className, disableValue, + emptyText, emptyValue, helperText, input, @@ -176,6 +177,15 @@ export const SelectInput = ({ [input, setValue] ); + const renderEmptyItemOption = useCallback( + emptyText => { + return React.isValidElement(emptyText) + ? React.cloneElement(emptyText) + : translate(emptyText, { _: emptyText }); + }, + [emptyText, translate] + ); + const renderMenuItemOption = useCallback( choice => { if (React.isValidElement(optionText)) { @@ -231,7 +241,11 @@ export const SelectInput = ({ {...sanitizeRestProps(rest)} onChange={handleChange} > - {allowEmpty ? : null} + {allowEmpty ? ( + + {renderEmptyItemOption(emptyText)} + + ) : null} {choices.map(choice => ( ', () => { assert.equal(options[0].getAttribute('data-value'), emptyValue); }); + it('should add an empty menu with proper text when emptyText is a string', () => { + const emptyText = 'Default'; + + const { getByRole, getByText, queryAllByRole } = render( + + ); + const TextFieldElement = getByRole('button'); + fireEvent.click(TextFieldElement); + + const options = queryAllByRole('option'); + assert.equal(options.length, 1); + + assert.ok(getByText('Default')); + }); + + it('should add an empty menu with proper text when emptyText is a React element', () => { + const emptyText = ( +
+ Empty choice +
+ ); + + const { getByRole, getByText, queryAllByRole } = render( + + ); + const TextFieldElement = getByRole('button'); + fireEvent.click(TextFieldElement); + + const options = queryAllByRole('option'); + assert.equal(options.length, 1); + + assert.ok(getByText('Empty choice')); + }); + it('should not add a falsy (null or false) element when allowEmpty is false', () => { const { getByRole, queryAllByRole } = render( Date: Thu, 1 Aug 2019 15:26:07 +0200 Subject: [PATCH 2/2] Apply suggestions from code review Fix poor copy-pasting Co-Authored-By: Gildas Garcia --- packages/ra-ui-materialui/src/input/SelectInput.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/SelectInput.spec.js b/packages/ra-ui-materialui/src/input/SelectInput.spec.js index 852e6abf592..2bb6fd16643 100644 --- a/packages/ra-ui-materialui/src/input/SelectInput.spec.js +++ b/packages/ra-ui-materialui/src/input/SelectInput.spec.js @@ -128,8 +128,8 @@ describe('', () => { choices={[]} /> ); - const TextFieldElement = getByRole('button'); - fireEvent.click(TextFieldElement); + const EmptyMenuElement = getByRole('button'); + fireEvent.click(EmptyMenuElement); const options = queryAllByRole('option'); assert.equal(options.length, 1); @@ -152,8 +152,8 @@ describe('', () => { choices={[]} /> ); - const TextFieldElement = getByRole('button'); - fireEvent.click(TextFieldElement); + const EmptyMenuElement = getByRole('button'); + fireEvent.click(EmptyMenuElement); const options = queryAllByRole('option'); assert.equal(options.length, 1);