Skip to content

Commit ccf10db

Browse files
authored
Merge pull request #10821 from WiXSL/disable-radiobuttongroupitem-next
Add disable support for `RadioButtonGroupInput` and `CheckboxGroupInput` choices
2 parents 62d7937 + a0904e3 commit ccf10db

16 files changed

+287
-10
lines changed

docs/CheckboxGroupInput.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ The form value for the source must be an array of the selected values, e.g.
6666
| `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value |
6767
| `row` | Optional | `boolean` | `true` | Display group of elements in a compact row. |
6868
| `translateChoice` | Optional | `boolean` | `true` | Whether the choices should be translated |
69+
| `disableValue` | Optional | `string` | `disabled` | The custom field name used in `choices` to disable some choices |
6970

7071
`<CheckboxGroupInput>` also accepts the [common input props](./Inputs.md#common-input-props).
7172

@@ -93,6 +94,17 @@ You can also use an array of objects with different properties for the label and
9394
]} optionValue="_id" optionText="label" />
9495
```
9596

97+
You can render some options as disabled by setting the `disabled` field in some choices:
98+
99+
```jsx
100+
const choices = [
101+
{ id: 'tech', name: 'Tech' },
102+
{ id: 'lifestyle', name: 'Lifestyle' },
103+
{ id: 'people', name: 'People', disabled: true },
104+
];
105+
<RadioButtonGroupInput source="category" choices={choices} />
106+
```
107+
96108
The choices are translated by default, so you can use translation identifiers as choices:
97109

98110
```jsx
@@ -256,6 +268,30 @@ However, in some cases (e.g. inside a `<ReferenceArrayInput>`), you may not want
256268
<CheckboxGroupInput source="roles" choices={choices} translateChoice={false}/>
257269
```
258270

271+
## `disableValue`
272+
273+
By default, `<CheckboxGroupInput>` renders the choices with the field `disabled: true` as disabled.
274+
275+
```jsx
276+
const choices = [
277+
{ id: 'tech', name: 'Tech' },
278+
{ id: 'lifestyle', name: 'Lifestyle' },
279+
{ id: 'people', name: 'People', disabled: true },
280+
];
281+
<CheckboxGroupInput source="category" choices={choices} />
282+
```
283+
284+
If you want to use another field to denote disabled options, set the `disableValue` prop.
285+
286+
```jsx
287+
const choices = [
288+
{ id: 'tech', name: 'Tech' },
289+
{ id: 'lifestyle', name: 'Lifestyle' },
290+
{ id: 'people', name: 'People', not_available: true },
291+
];
292+
<CheckboxGroupInput source="category" choices={choices} disableValue="not_available" />
293+
```
294+
259295
## Fetching Choices
260296

261297
If you want to populate the `choices` attribute with a list of related records, you should decorate `<CheckboxGroupInput>` with [`<ReferenceArrayInput>`](./ReferenceArrayInput.md), and leave the `choices` empty:

docs/RadioButtonGroupInput.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ The form value for the source must be the selected value, e.g.
6363
| `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value |
6464
| `row` | Optional | `boolean` | `true` | Display options in a compact row. |
6565
| `translateChoice` | Optional | `boolean` | `true` | Whether the choices should be translated |
66+
| `disableValue` | Optional | `string` | `disabled` | The custom field name used in `choices` to disable some choices |
6667

6768
`<RadioButtonGroupInput>` also accepts the [common input props](./Inputs.md#common-input-props).
6869

@@ -95,6 +96,17 @@ const choices = [
9596
/>
9697
```
9798

99+
You can render some options as disabled by setting the `disabled` field in some choices:
100+
101+
```jsx
102+
const choices = [
103+
{ id: 'tech', name: 'Tech' },
104+
{ id: 'lifestyle', name: 'Lifestyle' },
105+
{ id: 'people', name: 'People', disabled: true },
106+
];
107+
<RadioButtonGroupInput source="category" choices={choices} />
108+
```
109+
98110
The choices are translated by default, so you can use translation identifiers as choices:
99111

100112
```jsx
@@ -283,6 +295,30 @@ However, in some cases, you may not want the choice to be translated. In that ca
283295

284296
Note that `translateChoice` is set to `false` when `<RadioButtonGroupInput>` is a child of `<ReferenceInput>`.
285297

298+
## `disableValue`
299+
300+
By default, `<RadioButtonGroupInput>` renders the choices with the field `disabled: true` as disabled.
301+
302+
```jsx
303+
const choices = [
304+
{ id: 'tech', name: 'Tech' },
305+
{ id: 'lifestyle', name: 'Lifestyle' },
306+
{ id: 'people', name: 'People', disabled: true },
307+
];
308+
<RadioButtonGroupInput source="category" choices={choices} />
309+
```
310+
311+
If you want to use another field to denote disabled options, set the `disableValue` prop.
312+
313+
```jsx
314+
const choices = [
315+
{ id: 'tech', name: 'Tech' },
316+
{ id: 'lifestyle', name: 'Lifestyle' },
317+
{ id: 'people', name: 'People', not_available: true },
318+
];
319+
<RadioButtonGroupInput source="category" choices={choices} disableValue="not_available" />
320+
```
321+
286322
## Fetching Choices
287323

288324
You can use [`useGetList`](./useGetList.md) to fetch choices. For example, to fetch a list of countries for a user profile:
@@ -391,4 +427,4 @@ const CompanyInput = () => (
391427

392428
This is the recommended approach for using `<RadioButtonGroupInput>` to select a foreign key. This not only signifies that the input is a `<RadioButtonGroupInput>` but also highlights its function in fetching choices from another resource, ultimately enhancing the code's readability.
393429

394-
**Tip**: `<ReferenceInput>` is much more powerful than the initial snippet. It optimizes and caches API calls, enables refetching of both API calls with a single command, and stores supplementary data in the `<ChoicesContext>`. `<ReferenceInput>` can provide choices to `<RadioButtonGroupInput>`, but also to [`<AutocompleteInput>`](./AutocompleteInput.md) and [`<SelectInput>`](./SelectInput.md). For further information, refer to [the `<ReferenceInput>` documentation](./ReferenceInput.md).
430+
**Tip**: `<ReferenceInput>` is much more powerful than the initial snippet. It optimizes and caches API calls, enables refetching of both API calls with a single command, and stores supplementary data in the `<ChoicesContext>`. `<ReferenceInput>` can provide choices to `<RadioButtonGroupInput>`, but also to [`<AutocompleteInput>`](./AutocompleteInput.md) and [`<SelectInput>`](./SelectInput.md). For further information, refer to [the `<ReferenceInput>` documentation](./ReferenceInput.md).

packages/ra-core/src/form/choices/useChoices.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface ChoicesProps {
2020
optionValue?: string;
2121
optionText?: OptionText;
2222
translateChoice?: boolean;
23+
disableValue?: string;
2324
}
2425

2526
export interface UseChoicesOptions {

packages/ra-ui-materialui/src/field/SelectField.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export const SelectField = genericMemo(SelectFieldImpl);
138138

139139
export interface SelectFieldProps<
140140
RecordType extends Record<string, any> = Record<string, any>,
141-
> extends ChoicesProps,
141+
> extends Omit<ChoicesProps, 'disableValue'>,
142142
FieldProps<RecordType>,
143143
Omit<TypographyProps, 'textAlign'> {}
144144

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ export interface AutocompleteInputProps<
780780
DisableClearable extends boolean | undefined = false,
781781
SupportCreate extends boolean | undefined = false,
782782
> extends Omit<CommonInputProps, 'source' | 'onChange'>,
783-
ChoicesProps,
783+
Omit<ChoicesProps, 'disableValue'>,
784784
UseSuggestionsOptions,
785785
Omit<SupportCreateSuggestionOptions, 'handleChange' | 'optionText'>,
786786
Omit<

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,4 +426,65 @@ describe('<CheckboxGroupInput />', () => {
426426
await screen.findByText('Option 1 (This is option 1)');
427427
});
428428
});
429+
430+
it('should render disabled choices marked as so', () => {
431+
const choices = [
432+
{ id: 'ang', name: 'Angular' },
433+
{ id: 'rct', name: 'React', disabled: true },
434+
];
435+
render(
436+
<AdminContext dataProvider={testDataProvider()}>
437+
<ResourceContextProvider value="posts">
438+
<SimpleForm onSubmit={jest.fn()}>
439+
<CheckboxGroupInput
440+
{...defaultProps}
441+
choices={choices}
442+
/>
443+
</SimpleForm>
444+
</ResourceContextProvider>
445+
</AdminContext>
446+
);
447+
fireEvent.mouseDown(screen.getByLabelText('React'));
448+
449+
const enabledInput = screen.getByLabelText(
450+
'Angular'
451+
) as HTMLInputElement;
452+
expect(enabledInput.disabled).toBe(false);
453+
454+
const disabledInput = screen.getByLabelText(
455+
'React'
456+
) as HTMLInputElement;
457+
expect(disabledInput.disabled).toBe(true);
458+
});
459+
460+
it('should render disabled choices marked as so by disableValue prop', () => {
461+
const choices = [
462+
{ id: 'ang', name: 'Angular' },
463+
{ id: 'rct', name: 'React', not_available: true },
464+
];
465+
render(
466+
<AdminContext dataProvider={testDataProvider()}>
467+
<ResourceContextProvider value="posts">
468+
<SimpleForm onSubmit={jest.fn()}>
469+
<CheckboxGroupInput
470+
{...defaultProps}
471+
choices={choices}
472+
disableValue="not_available"
473+
/>
474+
</SimpleForm>
475+
</ResourceContextProvider>
476+
</AdminContext>
477+
);
478+
fireEvent.mouseDown(screen.getByLabelText('React'));
479+
480+
const enabledInput = screen.getByLabelText(
481+
'Angular'
482+
) as HTMLInputElement;
483+
expect(enabledInput.disabled).toBe(false);
484+
485+
const disabledInput = screen.getByLabelText(
486+
'React'
487+
) as HTMLInputElement;
488+
expect(disabledInput.disabled).toBe(true);
489+
});
429490
});

packages/ra-ui-materialui/src/input/CheckboxGroupInput.stories.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,17 @@ export const SetFocus = () => (
322322
<SetFocusButton source="roles" />
323323
</Wrapper>
324324
);
325+
326+
export const DisabledChoice = () => (
327+
<Wrapper>
328+
<CheckboxGroupInput
329+
source="roles"
330+
choices={[
331+
{ id: 'admin', name: 'Admin' },
332+
{ id: 'u001', name: 'Editor' },
333+
{ id: 'u002', name: 'Moderator' },
334+
{ id: 'u003', name: 'Reviewer', disabled: true },
335+
]}
336+
/>
337+
</Wrapper>
338+
);

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,23 @@ import { LinearProgress } from '../layout';
9595
* <CheckboxGroupInput source="tags" choices={choices} translateChoice={false}/>
9696
*
9797
* The object passed as `options` props is passed to the Material UI <Checkbox> components
98+
*
99+
* You can disable some choices by providing a `disableValue` field which name is `disabled` by default
100+
* @example
101+
* const choices = [
102+
* { id: 'programming', name: 'myroot.category.programming' },
103+
* { id: 'lifestyle', name: 'myroot.category.lifestyle' },
104+
* { id: 'photography', name: 'myroot.category.photography', disabled: true },
105+
* ];
106+
*
107+
* @example
108+
* const choices = [
109+
* { id: 'programming', name: 'myroot.category.programming' },
110+
* { id: 'lifestyle', name: 'myroot.category.lifestyle' },
111+
* { id: 'photography', name: 'myroot.category.photography', not_available: true },
112+
* ];
113+
* <CheckboxGroupInput source="tags" choices={choices} disableValue="not_available" />
114+
*
98115
*/
99116
export const CheckboxGroupInput = (inProps: CheckboxGroupInputProps) => {
100117
const props = useThemeProps({
@@ -124,6 +141,7 @@ export const CheckboxGroupInput = (inProps: CheckboxGroupInputProps) => {
124141
source: sourceProp,
125142
translateChoice,
126143
validate,
144+
disableValue = 'disabled',
127145
disabled,
128146
readOnly,
129147
...rest
@@ -262,6 +280,7 @@ export const CheckboxGroupInput = (inProps: CheckboxGroupInputProps) => {
262280
value={value}
263281
labelPlacement={labelPlacement}
264282
inputRef={index === 0 ? ref : undefined}
283+
disableValue={disableValue}
265284
disabled={disabled || readOnly}
266285
readOnly={readOnly}
267286
{...sanitizeRestProps(rest)}

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,19 @@ export const CheckboxGroupInputItem = (
3232
value,
3333
labelPlacement,
3434
inputRef,
35+
disableValue = 'disabled',
3536
...rest
3637
} = props;
3738

38-
const { getChoiceText, getChoiceValue } = useChoices({
39+
const { getChoiceText, getChoiceValue, getDisableValue } = useChoices({
3940
optionText,
4041
optionValue,
4142
translateChoice,
43+
disableValue,
4244
});
4345

4446
const choiceName = getChoiceText(choice);
47+
const disabled = getDisableValue(choice);
4548

4649
return (
4750
<StyledFormControlLabel
@@ -62,6 +65,7 @@ export const CheckboxGroupInputItem = (
6265
: false
6366
}
6467
value={String(getChoiceValue(choice))}
68+
disabled={disabled}
6569
{...options}
6670
/>
6771
}
@@ -74,7 +78,10 @@ export const CheckboxGroupInputItem = (
7478

7579
export interface CheckboxGroupInputItemProps
7680
extends Omit<FormControlLabelProps, 'control' | 'label'>,
77-
Pick<ChoicesProps, 'optionValue' | 'optionText' | 'translateChoice'> {
81+
Pick<
82+
ChoicesProps,
83+
'optionValue' | 'optionText' | 'translateChoice' | 'disableValue'
84+
> {
7885
choice: any;
7986
value: any;
8087
fullWidth?: boolean;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export type DatagridInputProps = Omit<
177177
CommonInputProps,
178178
'source' | 'readOnly' | 'disabled'
179179
> &
180-
ChoicesProps &
180+
Omit<ChoicesProps, 'disableValue'> &
181181
Omit<SupportCreateSuggestionOptions, 'handleChange'> &
182182
DatagridProps & {
183183
children?: ReactNode;

0 commit comments

Comments
 (0)