Skip to content

Commit eb16fe1

Browse files
authored
Remove enumNames (#3031)
1 parent 13e451e commit eb16fe1

File tree

12 files changed

+96
-91
lines changed

12 files changed

+96
-91
lines changed

docs/5.x upgrade guide.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ There are three new packages added in RJSF version 5:
2828

2929
### `@rjsf/core` BREAKING CHANGES
3030

31+
#### Support dropped for non-standard `enumNames` property
32+
33+
`enumNames` was a non-standard JSON Schema field that RJSF supported prior to version 5 to support labels that differed from an enumeration value. This behavior can still be accomplished with `oneOf` or `anyOf` containing `const` values. For more information, see [#532](https://github.com/rjsf-team/react-jsonschema-form/issues/532).
34+
3135
#### Types
3236
In version 4, RJSF exported all its types directly from `@rjsf/core`.
3337
In version 5, only the types for the `Form` component and the `withTheme()` HOC are exported directly from `@rjsf/core`.

docs/api-reference/utility-functions.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,8 @@ The difference between mergeSchemas and mergeObjects is that mergeSchemas only c
250250
- GenericObjectType: The merged schema object
251251

252252
### optionsList()
253-
Gets the list of options from the schema. If the schema has an enum list, then those enum values are returned.
254-
The labels for the options will be extracted from the non-standard `enumNames` if it exists othewise will be the same as the `value`.
255-
If the schema has a `oneOf` or `anyOf`, then the value is the list of `constant` values from the schema and the label is either the `schema.title` or the value.
253+
Gets the list of options from the schema. If the schema has an enum list, then those enum values are returned. The labels for the options will be the same as the `value`.
254+
If the schema has a `oneOf` or `anyOf`, then the value is the list of `const` values from the schema and the label is either the `schema.title`, if it exists, or the value.
256255

257256
#### Parameters
258257
- schema: RJSFSchema - The schema from which to extract the options list

docs/usage/single.md

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,26 +63,7 @@ render((
6363

6464
### Custom labels for `enum` fields
6565

66-
This library supports a custom [`enumNames`](https://github.com/rjsf-team/react-jsonschema-form/issues/57) property for `enum` fields, which, however is not JSON-Schema compliant (see below for a compliant approach).
67-
The `enumNames` property allows defining custom labels for each option of an `enum`:
68-
69-
```jsx
70-
import validator from "@rjsf/validator-ajv6";
71-
72-
const schema = {
73-
type: "number",
74-
enum: [1, 2, 3],
75-
enumNames: ["one", "two", "three"]
76-
};
77-
78-
render((
79-
<Form schema={schema} validator={validator} />
80-
), document.getElementById("app"));
81-
```
82-
83-
#### Alternative JSON-Schema compliant approach
84-
85-
JSON Schema has an alternative approach to enumerations using `anyOf`; react-jsonschema-form supports it as well.
66+
JSON Schema supports the following approaches to enumerations using `oneOf`/`anyOf`; react-jsonschema-form supports it as well.
8667

8768
```jsx
8869
import validator from "@rjsf/validator-ajv6";
@@ -119,6 +100,21 @@ render((
119100
), document.getElementById("app"));
120101
```
121102

103+
```jsx
104+
const schema = {
105+
"type": "number",
106+
"oneOf": [
107+
{"const": 1, "title": "one"},
108+
{"const": 2, "title": "two"},
109+
{"const": 3, "title": "three"}
110+
]
111+
};
112+
113+
render((
114+
<Form schema={schema} validator={validator} />
115+
), document.getElementById("app"));
116+
```
117+
122118
### Disabled attribute for `enum` fields
123119

124120
To disable an option, use the `ui:enumDisabled` property in the uiSchema.

docs/usage/widgets.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,33 @@ Here's a list of supported alternative widgets for different JSON Schema data ty
3535
* `select`: a select box with `true` and `false` as options;
3636
* by default, a checkbox is used
3737

38-
> Note: To set the labels for a boolean field, instead of using `true` and `false` you can set `enumNames` in your schema. Note that `enumNames` belongs in your `schema`, not the `uiSchema`, and the order is always `[true, false]`.
38+
> Note: To set the labels for a boolean field, instead of using `true` and `false`, your schema can use `oneOf` with `const` values for both true and false, where you can specify the custom label in the `title` field. You will also need to specify a widget in your `uiSchema`. See the following example:
39+
40+
schema:
41+
42+
```json
43+
{
44+
"properties": {
45+
"booleanWithCustomLabels": {
46+
"type": "boolean",
47+
"oneOf": [
48+
{"const": true, "title": "Custom label for true"},
49+
{"const": false, "title": "Custom label for false"}
50+
]
51+
}
52+
}
53+
}
54+
```
55+
56+
uiSchema:
3957

58+
```json
59+
{
60+
"booleanWithCustomLabels": {
61+
"ui:widget": "radio" // or "select"
62+
}
63+
}
64+
```
4065
## For `string` fields
4166

4267
* `textarea`: a `textarea` element is used;

packages/core/src/components/fields/BooleanField.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
optionsList,
66
FieldProps,
77
RJSFSchemaDefinition,
8+
EnumOptionsType,
89
} from "@rjsf/utils";
910
import isObject from "lodash/isObject";
1011

@@ -35,7 +36,7 @@ function BooleanField<T = any, F = any>(props: FieldProps<T, F>) {
3536
const { widget = "checkbox", ...options } = getUiOptions<T, F>(uiSchema);
3637
const Widget = getWidget(schema, widget, widgets);
3738

38-
let enumOptions;
39+
let enumOptions: EnumOptionsType[] | undefined;
3940

4041
if (Array.isArray(schema.oneOf)) {
4142
enumOptions = optionsList({
@@ -52,14 +53,21 @@ function BooleanField<T = any, F = any>(props: FieldProps<T, F>) {
5253
.filter((o) => o) as RJSFSchemaDefinition[], // cast away the error that typescript can't grok is fixed
5354
});
5455
} else {
55-
enumOptions = optionsList({
56-
enum: schema.enum || [true, false],
57-
enumNames:
58-
schema.enumNames ||
59-
(schema.enum && schema.enum[0] === false
60-
? ["No", "Yes"]
61-
: ["Yes", "No"]),
62-
});
56+
schema.enum = schema.enum ?? [true, false];
57+
if (
58+
schema.enum &&
59+
schema.enum.length === 2 &&
60+
schema.enum.every((v) => typeof v === "boolean")
61+
) {
62+
enumOptions = [
63+
{ value: schema.enum[0], label: schema.enum[0] ? "Yes" : "No" },
64+
{ value: schema.enum[1], label: schema.enum[1] ? "Yes" : "No" },
65+
];
66+
} else {
67+
enumOptions = optionsList({
68+
enum: schema.enum,
69+
});
70+
}
6371
}
6472

6573
return (

packages/core/test/BooleanField_test.js

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -324,23 +324,6 @@ describe("BooleanField", () => {
324324
expect(labels).eql(["No", "Yes"]);
325325
});
326326

327-
it("should support enumNames for radio widgets", () => {
328-
const { node } = createFormComponent({
329-
schema: {
330-
type: "boolean",
331-
enumNames: ["Yes", "No"],
332-
},
333-
formData: true,
334-
uiSchema: { "ui:widget": "radio" },
335-
});
336-
337-
const labels = [].map.call(
338-
node.querySelectorAll(".field-radio-group label"),
339-
(label) => label.textContent
340-
);
341-
expect(labels).eql(["Yes", "No"]);
342-
});
343-
344327
it("should support oneOf titles for radio widgets", () => {
345328
const { node } = createFormComponent({
346329
schema: {
@@ -452,23 +435,6 @@ describe("BooleanField", () => {
452435
expect(onBlur.calledWith(element.id, false)).to.be.true;
453436
});
454437

455-
it("should support enumNames for select", () => {
456-
const { node } = createFormComponent({
457-
schema: {
458-
type: "boolean",
459-
enumNames: ["Yes", "No"],
460-
},
461-
formData: true,
462-
uiSchema: { "ui:widget": "select" },
463-
});
464-
465-
const labels = [].map.call(
466-
node.querySelectorAll(".field option"),
467-
(label) => label.textContent
468-
);
469-
expect(labels).eql(["", "Yes", "No"]);
470-
});
471-
472438
it("should handle a focus event with checkbox", () => {
473439
const onFocus = sandbox.spy();
474440
const { node } = createFormComponent({

packages/core/test/FormContext_test.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,12 @@ describe("FormContext", () => {
137137
type: "array",
138138
items: {
139139
type: "string",
140-
enum: ["foo"],
141-
enumNames: ["bar"],
140+
oneOf: [
141+
{
142+
const: "foo",
143+
title: "bar",
144+
},
145+
],
142146
},
143147
uniqueItems: true,
144148
},

packages/playground/src/samples/alternatives.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,11 @@ module.exports = {
6767
blendMode: {
6868
title: "Blend mode",
6969
type: "string",
70-
enum: ["screen", "multiply", "overlay"],
71-
enumNames: ["Screen", "Multiply", "Overlay"],
70+
oneOf: [
71+
{ const: "screen", title: "Screen" },
72+
{ const: "multiply", title: "Multiply" },
73+
{ const: "overlay", title: "Overlay" },
74+
],
7275
},
7376
},
7477
},

packages/playground/src/samples/widgets.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,20 @@ export default {
9191
title: "Custom select widget with options",
9292
type: "string",
9393
enum: ["foo", "bar"],
94-
enumNames: ["Foo", "Bar"],
94+
},
95+
selectWidgetOptions2: {
96+
title: "Custom select widget with options, overriding the enum titles.",
97+
type: "string",
98+
oneOf: [
99+
{
100+
const: "foo",
101+
title: "Foo",
102+
},
103+
{
104+
const: "bar",
105+
title: "Bar",
106+
},
107+
],
95108
},
96109
},
97110
},

packages/utils/src/optionsList.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ import toConstant from "./toConstant";
22
import { RJSFSchema, EnumOptionsType } from "./types";
33

44
/** Gets the list of options from the schema. If the schema has an enum list, then those enum values are returned. The
5-
* labels for the options will be extracted from the non-standard `enumNames` if it exists othewise will be the same as
6-
* the `value`. If the schema has a `oneOf` or `anyOf`, then the value is the list of `constant` values from the schema
7-
* and the label is either the `schema.title` or the value.
5+
* labels for the options will be the same as the `value`. If the schema has a `oneOf` or `anyOf`, then the value is the
6+
* list of `const` values from the schema and the label is either the `schema.title` or the value.
87
*
98
* @param schema - The schema from which to extract the options list
109
* @returns - The list of options from the schema
@@ -13,8 +12,8 @@ export default function optionsList(
1312
schema: RJSFSchema
1413
): EnumOptionsType[] | undefined {
1514
if (schema.enum) {
16-
return schema.enum.map((value, i) => {
17-
const label = (schema.enumNames && schema.enumNames[i]) || String(value);
15+
return schema.enum.map((value) => {
16+
const label = String(value);
1817
return { label, value };
1918
});
2019
}

packages/utils/src/types.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ export type GenericObjectType = {
99
};
1010

1111
/** Map the JSONSchema7 to our own type so that we can easily bump to JSONSchema8 at some future date and only have to
12-
* update this one type. Also, because we are supporting the non-standard `enumNames` prop in the code, define it here.
12+
* update this one type.
1313
*/
14-
export type RJSFSchema = JSONSchema7 & {
15-
enumNames?: string[];
16-
};
14+
export type RJSFSchema = JSONSchema7;
1715

1816
/** Map the JSONSchema7Definition to our own type so that we can easily bump to JSONSchema8Definition at some future
1917
* date and only have to update this one type.

packages/utils/test/optionsList.test.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,9 @@ describe("optionsList()", () => {
77
enum: ["Opt1", "Opt2", "Opt3"],
88
};
99

10-
const enumNameSchema = {
11-
...enumSchema,
12-
enumNames: ["Option1", "Option2", "Option3"],
13-
};
1410
expect(optionsList(enumSchema)).toEqual(
1511
enumSchema.enum!.map((opt) => ({ label: opt, value: opt }))
1612
);
17-
expect(optionsList(enumNameSchema)).toEqual(
18-
enumNameSchema.enum!.map((opt, index) => {
19-
const label = enumNameSchema.enumNames[index] || opt;
20-
return { label: label, value: opt };
21-
})
22-
);
2313
});
2414
it("should generate options for a oneOf|anyOf schema", () => {
2515
const oneOfSchema = {

0 commit comments

Comments
 (0)