Skip to content

Commit

Permalink
Merge pull request #3444 from tdeo/tdeo/SelectInputEmptyText
Browse files Browse the repository at this point in the history
Add emptyText prop to SelectInput
  • Loading branch information
djhi authored Aug 5, 2019
2 parents 7706ead + 9dddb66 commit cabecb1
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 7 deletions.
11 changes: 5 additions & 6 deletions docs/Inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. `<AutocompleteInput shouldRenderSuggestions={(val) => { 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).

`<AutocompleteInput>` renders a material-ui `<TextField>` component. Use the `options` attribute to override any of the `<TextField>` attributes:
Expand Down Expand Up @@ -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. `<AutocompleteArrayInput shouldRenderSuggestions={(val) => { 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, `<AutocompleteArrayInput>` renders a [material-ui-chip-input](https://github.com/TeamWertarbyte/material-ui-chip-input) component. Use the `options` attribute to override any of the `<ChipInput>` attributes:
Expand Down Expand Up @@ -1008,8 +1008,7 @@ const FullNameField = ({ record }) => <span>{record.first_name} {record.last_nam
<SelectInput source="gender" choices={choices} optionText={<FullNameField />}/>
```
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
<SelectInput source="category" allowEmpty emptyValue="" choices={[
{ id: 'programming', name: 'Programming' },
Expand Down Expand Up @@ -1063,7 +1062,7 @@ You can make the `SelectInput` component resettable using the `resettable` prop.
![resettable SelectInput](./img/resettable-select-input.png)
You can set disabled values by setting the `disabled` property of one item:
You can set disabled values by setting the `disabled` property of one item:
```jsx
const choices = [
Expand All @@ -1074,7 +1073,7 @@ const choices = [
<SelectInput source="author_id" choices={choices} optionText="full_name" optionValue="_id" />
```
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 = [
Expand Down
18 changes: 17 additions & 1 deletion packages/ra-ui-materialui/src/input/SelectInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export const SelectInput = ({
classes,
className,
disableValue,
emptyText,
emptyValue,
helperText,
input,
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -231,7 +241,11 @@ export const SelectInput = ({
{...sanitizeRestProps(rest)}
onChange={handleChange}
>
{allowEmpty ? <MenuItem value={emptyValue} key="null" /> : null}
{allowEmpty ? (
<MenuItem value={emptyValue} key="null">
{renderEmptyItemOption(emptyText)}
</MenuItem>
) : null}
{choices.map(choice => (
<MenuItem
key={get(choice, optionValue)}
Expand All @@ -247,6 +261,7 @@ export const SelectInput = ({

SelectInput.propTypes = {
allowEmpty: PropTypes.bool.isRequired,
emptyText: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
emptyValue: PropTypes.any,
choices: PropTypes.arrayOf(PropTypes.object),
classes: PropTypes.object,
Expand All @@ -271,6 +286,7 @@ SelectInput.propTypes = {

SelectInput.defaultProps = {
allowEmpty: false,
emptyText: '',
emptyValue: '',
classes: {},
choices: [],
Expand Down
44 changes: 44 additions & 0 deletions packages/ra-ui-materialui/src/input/SelectInput.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,50 @@ describe('<SelectInput />', () => {
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(
<SelectInput
allowEmpty
emptyText={emptyText}
{...defaultProps}
choices={[]}
/>
);
const EmptyMenuElement = getByRole('button');
fireEvent.click(EmptyMenuElement);

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 = (
<div>
<em>Empty choice</em>
</div>
);

const { getByRole, getByText, queryAllByRole } = render(
<SelectInput
allowEmpty
emptyText={emptyText}
{...defaultProps}
choices={[]}
/>
);
const EmptyMenuElement = getByRole('button');
fireEvent.click(EmptyMenuElement);

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(
<SelectInput
Expand Down

0 comments on commit cabecb1

Please sign in to comment.