Skip to content

Commit 006bd59

Browse files
authored
Merge pull request #5311 from gavacho/nullable-boolean-labels
Allow the option labels of NullableBooleanInput to be customized
2 parents 2ad0138 + 4f97ed5 commit 006bd59

File tree

3 files changed

+101
-35
lines changed

3 files changed

+101
-35
lines changed

docs/Inputs.md

+17-13
Original file line numberDiff line numberDiff line change
@@ -450,30 +450,34 @@ import { NullableBooleanInput } from 'react-admin';
450450

451451
![NullableBooleanInput](./img/nullable-boolean-input.png)
452452

453-
`<NullableBooleanInput />` doesn't display the empty option by default. If you want to customize its label and display it, you can use the `displayNull` prop.
454-
455-
```jsx
456-
import { NullableBooleanInput } from 'react-admin';
457-
458-
<NullableBooleanInput
459-
label="Commentable"
460-
source="commentable"
461-
displayNull
462-
/>
463-
```
464-
465-
Also you need to provide your own label for null value.
453+
The labels of the options can be customized for the entire application by overriding the translation.
466454

467455
```jsx
468456
import polyglotI18nProvider from 'ra-i18n-polyglot';
469457
import englishMessages from 'ra-language-english';
470458

471459
englishMessages.ra.boolean.null = 'Null label';
460+
englishMessages.ra.boolean.false = 'False label';
461+
englishMessages.ra.boolean.true = 'True label';
472462
const i18nProvider = polyglotI18nProvider(() => englishMessages, 'en');
473463

474464
<Admin i18nProvider={i18nProvider}></Admin>
475465
```
476466

467+
Additionally, individual instances of `NullableBooleanInput` may be customized by setting the `nullLabel`, `falseLabel` and `trueLabel` properties. Values specified for those properties will be translated by react-admin.
468+
469+
```jsx
470+
import { NullableBooleanInput } from 'react-admin';
471+
472+
<NullableBooleanInput
473+
label="Commentable"
474+
source="commentable"
475+
nullLabel="Either"
476+
falseLabel="No"
477+
trueLabel="Yes"
478+
/>
479+
```
480+
477481
![NullableBooleanInput](./img/nullable-boolean-input-null-label.png)
478482

479483
`<BooleanInput>` and `<NullableBooleanInput>` also accept the [common input props](./Inputs.md#common-input-props).

packages/ra-ui-materialui/src/input/NullableBooleanInput.spec.tsx

+66
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,70 @@ describe('<NullableBooleanInput />', () => {
138138
'true'
139139
);
140140
});
141+
142+
it('should allow to customize the label of the null option', () => {
143+
const { getByRole, getByText, container } = render(
144+
<Form
145+
onSubmit={jest.fn}
146+
initialValues={{
147+
isPublished: null,
148+
}}
149+
render={() => (
150+
<NullableBooleanInput
151+
source="isPublished"
152+
resource="posts"
153+
nullLabel="example null label"
154+
/>
155+
)}
156+
/>
157+
);
158+
expect(container.querySelector('input').getAttribute('value')).toBe('');
159+
const select = getByRole('button');
160+
fireEvent.mouseDown(select);
161+
expect(getByText('example null label')).not.toBeNull();
162+
});
163+
164+
it('should allow to customize the label of the false option', () => {
165+
const { getByRole, getByText, container } = render(
166+
<Form
167+
onSubmit={jest.fn}
168+
initialValues={{
169+
isPublished: null,
170+
}}
171+
render={() => (
172+
<NullableBooleanInput
173+
source="isPublished"
174+
resource="posts"
175+
falseLabel="example false label"
176+
/>
177+
)}
178+
/>
179+
);
180+
expect(container.querySelector('input').getAttribute('value')).toBe('');
181+
const select = getByRole('button');
182+
fireEvent.mouseDown(select);
183+
expect(getByText('example false label')).not.toBeNull();
184+
});
185+
186+
it('should allow to customize the label of the true option', () => {
187+
const { getByRole, getByText, container } = render(
188+
<Form
189+
onSubmit={jest.fn}
190+
initialValues={{
191+
isPublished: null,
192+
}}
193+
render={() => (
194+
<NullableBooleanInput
195+
source="isPublished"
196+
resource="posts"
197+
trueLabel="example true label"
198+
/>
199+
)}
200+
/>
201+
);
202+
expect(container.querySelector('input').getAttribute('value')).toBe('');
203+
const select = getByRole('button');
204+
fireEvent.mouseDown(select);
205+
expect(getByText('example true label')).not.toBeNull();
206+
});
141207
});

packages/ra-ui-materialui/src/input/NullableBooleanInput.tsx

+18-22
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,14 @@ const getStringFromBoolean = (value?: boolean | null): string => {
2929
return '';
3030
};
3131

32-
const NullableBooleanInput: FunctionComponent<
33-
InputProps<TextFieldProps> & Omit<TextFieldProps, 'label' | 'helperText'>
34-
> = props => {
32+
export type NullableBooleanInputProps = InputProps<TextFieldProps> &
33+
Omit<TextFieldProps, 'label' | 'helperText'> & {
34+
nullLabel?: string;
35+
falseLabel?: string;
36+
trueLabel?: string;
37+
};
38+
39+
const NullableBooleanInput: FunctionComponent<NullableBooleanInputProps> = props => {
3540
const {
3641
className,
3742
classes: classesOverride,
@@ -43,12 +48,14 @@ const NullableBooleanInput: FunctionComponent<
4348
onChange,
4449
onFocus,
4550
options,
46-
displayNull,
4751
parse = getBooleanFromString,
4852
resource,
4953
source,
5054
validate,
5155
variant = 'filled',
56+
nullLabel = 'ra.boolean.null',
57+
falseLabel = 'ra.boolean.false',
58+
trueLabel = 'ra.boolean.true',
5259
...rest
5360
} = props;
5461
const classes = useStyles(props);
@@ -70,20 +77,6 @@ const NullableBooleanInput: FunctionComponent<
7077
validate,
7178
});
7279

73-
const enhancedOptions = displayNull
74-
? {
75-
...options,
76-
SelectProps: {
77-
displayEmpty: true,
78-
...(options && options.SelectProps),
79-
},
80-
InputLabelProps: {
81-
shrink: true,
82-
...(options && options.InputLabelProps),
83-
},
84-
}
85-
: options;
86-
8780
return (
8881
<TextField
8982
id={id}
@@ -108,12 +101,12 @@ const NullableBooleanInput: FunctionComponent<
108101
}
109102
className={classnames(classes.input, className)}
110103
variant={variant}
111-
{...enhancedOptions}
104+
{...options}
112105
{...sanitizeRestProps(rest)}
113106
>
114-
<MenuItem value="">{translate('ra.boolean.null')}</MenuItem>
115-
<MenuItem value="false">{translate('ra.boolean.false')}</MenuItem>
116-
<MenuItem value="true">{translate('ra.boolean.true')}</MenuItem>
107+
<MenuItem value="">{translate(nullLabel)}</MenuItem>
108+
<MenuItem value="false">{translate(falseLabel)}</MenuItem>
109+
<MenuItem value="true">{translate(trueLabel)}</MenuItem>
117110
</TextField>
118111
);
119112
};
@@ -123,6 +116,9 @@ NullableBooleanInput.propTypes = {
123116
options: PropTypes.object,
124117
resource: PropTypes.string,
125118
source: PropTypes.string,
119+
nullLabel: PropTypes.string,
120+
falseLabel: PropTypes.string,
121+
trueLabel: PropTypes.string,
126122
};
127123

128124
export default NullableBooleanInput;

0 commit comments

Comments
 (0)