diff --git a/CHANGELOG.md b/CHANGELOG.md index ad4c1c3f6a..07dac50167 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,10 +21,14 @@ should change the heading of the (upcoming) version to include a major version b ## @rjsf/core - Support allowing raising errors from within a custom Widget [#2718](https://github.com/rjsf-team/react-jsonschema-form/issues/2718) +- Updated `ArrayField`, `BooleanField` and `StringField` to call `optionsList()` with the additional `UiSchema` parameter, fixing [#4215](https://github.com/rjsf-team/react-jsonschema-form/issues/4215) and [#4260](https://github.com/rjsf-team/react-jsonschema-form/issues/4260) ## @rjsf/utils - Updated the `WidgetProps` type to add `es?: ErrorSchema, id?: string` to the params of the `onChange` handler function +- Updated `UIOptionsBaseType` to add the new `enumNames` prop to support an alternate way to provide labels for `enum`s in a schema, fixing [#4215](https://github.com/rjsf-team/react-jsonschema-form/issues/4215) +- Updated `optionsList()` to take an optional `uiSchema` that is used to extract alternate labels for `enum`s or `oneOf`/`anyOf` in a schema, fixing [#4215](https://github.com/rjsf-team/react-jsonschema-form/issues/4215) and [#4260](https://github.com/rjsf-team/react-jsonschema-form/issues/4260) + - NOTE: The generics for `optionsList()` were expanded from `` to `` to support the `UiSchema`. ## Dev / docs / playground @@ -41,7 +45,7 @@ should change the heading of the (upcoming) version to include a major version b - Updated the `ValidatorType` interface to add an optional `reset?: () => void` prop that can be implemented to reset a validator back to initial constructed state - Updated the `ParserValidator` to provide a `reset()` function that clears the schema map -- Also updated the default translatable string to use `Markdown` rather than HTML tags since we now render them with `Markdown` +- Also updated the default translatable string to use `Markdown` rather than HTML tags since we now render them with `Markdown` ## @rjsf/validator-ajv8 diff --git a/packages/core/src/components/fields/ArrayField.tsx b/packages/core/src/components/fields/ArrayField.tsx index e94a61f9b3..5bae4bba10 100644 --- a/packages/core/src/components/fields/ArrayField.tsx +++ b/packages/core/src/components/fields/ArrayField.tsx @@ -612,7 +612,7 @@ class ArrayField(itemsSchema, uiSchema); const { widget = 'select', title: uiTitle, ...options } = getUiOptions(uiSchema, globalUiOptions); const Widget = getWidget(schema, widget, widgets); const label = uiTitle ?? schema.title ?? name; diff --git a/packages/core/src/components/fields/BooleanField.tsx b/packages/core/src/components/fields/BooleanField.tsx index 9880d47e54..38b95d83b2 100644 --- a/packages/core/src/components/fields/BooleanField.tsx +++ b/packages/core/src/components/fields/BooleanField.tsx @@ -52,19 +52,22 @@ function BooleanField[] | undefined; const label = uiTitle ?? schemaTitle ?? title ?? name; if (Array.isArray(schema.oneOf)) { - enumOptions = optionsList({ - oneOf: schema.oneOf - .map((option) => { - if (isObject(option)) { - return { - ...option, - title: option.title || (option.const === true ? yes : no), - }; - } - return undefined; - }) - .filter((o: any) => o) as S[], // cast away the error that typescript can't grok is fixed - } as unknown as S); + enumOptions = optionsList( + { + oneOf: schema.oneOf + .map((option) => { + if (isObject(option)) { + return { + ...option, + title: option.title || (option.const === true ? yes : no), + }; + } + return undefined; + }) + .filter((o: any) => o) as S[], // cast away the error that typescript can't grok is fixed + } as unknown as S, + uiSchema + ); } else { // We deprecated enumNames in v5. It's intentionally omitted from RSJFSchema type, so we need to cast here. const schemaWithEnumNames = schema as S & { enumNames?: string[] }; @@ -81,11 +84,14 @@ function BooleanField({ - enum: enums, - // NOTE: enumNames is deprecated, but still supported for now. - enumNames: schemaWithEnumNames.enumNames, - } as unknown as S); + enumOptions = optionsList( + { + enum: enums, + // NOTE: enumNames is deprecated, but still supported for now. + enumNames: schemaWithEnumNames.enumNames, + } as unknown as S, + uiSchema + ); } } diff --git a/packages/core/src/components/fields/StringField.tsx b/packages/core/src/components/fields/StringField.tsx index da861071d8..d825fd2c5c 100644 --- a/packages/core/src/components/fields/StringField.tsx +++ b/packages/core/src/components/fields/StringField.tsx @@ -35,7 +35,7 @@ function StringField(schema, uiSchema) : undefined; let defaultWidget = enumOptions ? 'select' : 'text'; if (format && hasWidget(schema, format, widgets)) { defaultWidget = format; diff --git a/packages/core/test/BooleanField.test.jsx b/packages/core/test/BooleanField.test.jsx index 5a6817b474..3a2e991dc6 100644 --- a/packages/core/test/BooleanField.test.jsx +++ b/packages/core/test/BooleanField.test.jsx @@ -387,7 +387,7 @@ describe('BooleanField', () => { const labels = [].map.call(node.querySelectorAll('.field-radio-group label'), (label) => label.textContent); expect(labels).eql(['Yes', 'No']); - expect(console.warn.calledWithMatch(/The enumNames property is deprecated/)).to.be.true; + expect(console.warn.calledWithMatch(/The "enumNames" property in the schema is deprecated/)).to.be.true; }); it('should support oneOf titles for radio widgets', () => { @@ -413,6 +413,29 @@ describe('BooleanField', () => { expect(labels).eql(['Yes', 'No']); }); + it('should support oneOf titles for radio widgets, overrides in uiSchema', () => { + const { node } = createFormComponent({ + schema: { + type: 'boolean', + oneOf: [ + { + const: true, + title: 'Yes', + }, + { + const: false, + title: 'No', + }, + ], + }, + formData: true, + uiSchema: { 'ui:widget': 'radio', oneOf: [{ 'ui:title': 'Si!' }, { 'ui:title': 'No!' }] }, + }); + + const labels = [].map.call(node.querySelectorAll('.field-radio-group label'), (label) => label.textContent); + expect(labels).eql(['Si!', 'No!']); + }); + it('should preserve oneOf option ordering for radio widgets', () => { const { node } = createFormComponent({ schema: { @@ -495,19 +518,19 @@ describe('BooleanField', () => { expect(onBlur.calledWith(element.id, false)).to.be.true; }); - it('should support enumNames for select', () => { + it('should support enumNames for select, with overrides in uiSchema', () => { const { node } = createFormComponent({ schema: { type: 'boolean', enumNames: ['Yes', 'No'], }, formData: true, - uiSchema: { 'ui:widget': 'select' }, + uiSchema: { 'ui:widget': 'select', 'ui:enumNames': ['Si!', 'No!'] }, }); const labels = [].map.call(node.querySelectorAll('.field option'), (label) => label.textContent); - expect(labels).eql(['', 'Yes', 'No']); - expect(console.warn.calledWithMatch(/The enumNames property is deprecated/)).to.be.true; + expect(labels).eql(['', 'Si!', 'No!']); + expect(console.warn.calledWithMatch(/TThe "enumNames" property in the schema is deprecated/)).to.be.false; }); it('should handle a focus event with checkbox', () => { diff --git a/packages/docs/docs/api-reference/uiSchema.md b/packages/docs/docs/api-reference/uiSchema.md index f34488c3ef..d4eaaaa208 100644 --- a/packages/docs/docs/api-reference/uiSchema.md +++ b/packages/docs/docs/api-reference/uiSchema.md @@ -228,6 +228,22 @@ const uiSchema: UiSchema = { }; ``` +### enumNames + +Allows a user to provide a list of labels for enum values in the schema. + +```tsx +import { RJSFSchema, UiSchema } from '@rjsf/utils'; + +const schema: RJSFSchema = { + type: 'number', + enum: [1, 2, 3], +}; +const uiSchema: UiSchema = { + 'ui:enumNames': ['one', 'two', 'three'], +}; +``` + ### filePreview The `FileWidget` can be configured to show a preview of an image or a download link for non-images using this flag. diff --git a/packages/docs/docs/api-reference/utility-functions.md b/packages/docs/docs/api-reference/utility-functions.md index c1b61a4b88..1e6a9812ff 100644 --- a/packages/docs/docs/api-reference/utility-functions.md +++ b/packages/docs/docs/api-reference/utility-functions.md @@ -621,17 +621,21 @@ Return a consistent `id` for the `optionIndex`s of a `Radio` or `Checkboxes` wid - string: An id for the option index based on the parent `id` -### optionsList<S extends StrictRJSFSchema = RJSFSchema>() +### optionsList<S extends StrictRJSFSchema = RJSFSchema, T = any, F extends FormContextType = any>() -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 extracted from the non-standard `enumNames` if it exists otherwise will be the same as the `value`. -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` or the value. +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 extracted from the non-standard, RJSF-deprecated `enumNames` if it exists, otherwise +the label will be the same as the `value`. 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` or the value. If a `uiSchema` is provided +and it has the `ui:enumNames` matched with `enum` or it has an associated `oneOf` or `anyOf` with a list of objects +containing `ui:title` then the UI schema values will replace the values from the schema. -NOTE: `enumNames` is deprecated and may be removed in a future major version of RJSF. +NOTE: `enumNames` is deprecated and will be removed in a future major version of RJSF. Use the "ui:enumNames" property in the uiSchema instead. #### Parameters - schema: S - The schema from which to extract the options list +- uiSchema: UiSchema - The optional uiSchema from which to get alternate labels for the options #### Returns diff --git a/packages/docs/docs/json-schema/single.md b/packages/docs/docs/json-schema/single.md index 381692d842..c3bf7fffbb 100644 --- a/packages/docs/docs/json-schema/single.md +++ b/packages/docs/docs/json-schema/single.md @@ -107,7 +107,7 @@ const schema: RJSFSchema = { render(
, document.getElementById('app')); ``` -In your JSON Schema, you may also specify `enumNames`, a non-standard field which RJSF can use to label an enumeration. **This behavior is deprecated and may be removed in a future major release of RJSF.** +In your JSON Schema, you may also specify `enumNames`, a non-standard field which RJSF can use to label an enumeration. **This behavior is deprecated and will be removed in a future major release of RJSF. Use the "ui:enumNames" property in the uiSchema instead.** ```tsx import { RJSFSchema } from '@rjsf/utils'; @@ -121,6 +121,22 @@ const schema: RJSFSchema = { render(, document.getElementById('app')); ``` +Same example using the `uiSchema`'s `ui:enumNames` instead. + +```tsx +import { RJSFSchema, UiSchema } from '@rjsf/utils'; +import validator from '@rjsf/validator-ajv8'; + +const schema: RJSFSchema = { + type: 'number', + enum: [1, 2, 3], +}; +const uiSchema: UiSchema = { + 'ui:enumNames': ['one', 'two', 'three'], +}; +render(, document.getElementById('app')); +``` + ### Disabled attribute for `enum` fields To disable an option, use the `ui:enumDisabled` property in the uiSchema. diff --git a/packages/docs/docs/migration-guides/v5.x upgrade guide.md b/packages/docs/docs/migration-guides/v5.x upgrade guide.md index 77ffde6f74..edc19c46e8 100644 --- a/packages/docs/docs/migration-guides/v5.x upgrade guide.md +++ b/packages/docs/docs/migration-guides/v5.x upgrade guide.md @@ -437,7 +437,7 @@ The utility function `getMatchingOption()` was deprecated in favor of the more a `enumNames` is a non-standard JSON Schema field that was deprecated in version 5. `enumNames` could be included in the schema to apply labels that differed from an enumeration value. -This behavior can still be accomplished with `oneOf` or `anyOf` containing `const` values, so `enumNames` support may be removed from a future major version of RJSF. +This behavior can still be accomplished with `oneOf` or `anyOf` containing `const` values, so `enumNames` support will be removed from a future major version of RJSF. For more information, see [#532](https://github.com/rjsf-team/react-jsonschema-form/issues/532). ##### uiSchema.classNames diff --git a/packages/utils/src/optionsList.ts b/packages/utils/src/optionsList.ts index 4d449ae4c2..2ba4eade59 100644 --- a/packages/utils/src/optionsList.ts +++ b/packages/utils/src/optionsList.ts @@ -1,36 +1,61 @@ import toConstant from './toConstant'; -import { RJSFSchema, EnumOptionsType, StrictRJSFSchema } from './types'; +import { RJSFSchema, EnumOptionsType, StrictRJSFSchema, FormContextType, UiSchema } from './types'; +import getUiOptions from './getUiOptions'; -/** Gets the list of options from the schema. If the schema has an enum list, then those enum values are returned. The +/** 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 extracted from the non-standard, RJSF-deprecated `enumNames` if it exists, otherwise * the label will be the same as the `value`. 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` or the value. + * `const` values from the schema and the label is either the `schema.title` or the value. If a `uiSchema` is provided + * and it has the `ui:enumNames` matched with `enum` or it has an associated `oneOf` or `anyOf` with a list of objects + * containing `ui:title` then the UI schema values will replace the values from the schema. * * @param schema - The schema from which to extract the options list + * @param [uiSchema] - The optional uiSchema from which to get alternate labels for the options * @returns - The list of options from the schema */ -export default function optionsList( - schema: S +export default function optionsList( + schema: S, + uiSchema?: UiSchema ): EnumOptionsType[] | undefined { - // enumNames was deprecated in v5 and is intentionally omitted from the RJSFSchema type. - // Cast the type to include enumNames so the feature still works. + // TODO flip generics to move T first in v6 const schemaWithEnumNames = schema as S & { enumNames?: string[] }; - if (schemaWithEnumNames.enumNames && process.env.NODE_ENV !== 'production') { - console.warn('The enumNames property is deprecated and may be removed in a future major release.'); - } if (schema.enum) { + let enumNames: string[] | undefined; + if (uiSchema) { + const { enumNames: uiEnumNames } = getUiOptions(uiSchema); + enumNames = uiEnumNames; + } + if (!enumNames && schemaWithEnumNames.enumNames) { + // enumNames was deprecated in v5 and is intentionally omitted from the RJSFSchema type. + // Cast the type to include enumNames so the feature still works. + if (process.env.NODE_ENV !== 'production') { + console.warn( + 'The "enumNames" property in the schema is deprecated and will be removed in a future major release. Use the "ui:enumNames" property in the uiSchema instead.' + ); + } + enumNames = schemaWithEnumNames.enumNames; + } return schema.enum.map((value, i) => { - const label = (schemaWithEnumNames.enumNames && schemaWithEnumNames.enumNames[i]) || String(value); + const label = enumNames?.[i] || String(value); return { label, value }; }); } - const altSchemas = schema.oneOf || schema.anyOf; + let altSchemas: S['anyOf'] | S['oneOf'] = undefined; + let altUiSchemas: UiSchema | undefined = undefined; + if (schema.anyOf) { + altSchemas = schema.anyOf; + altUiSchemas = uiSchema?.anyOf; + } else if (schema.oneOf) { + altSchemas = schema.oneOf; + altUiSchemas = uiSchema?.oneOf; + } return ( altSchemas && - altSchemas.map((aSchemaDef) => { + altSchemas.map((aSchemaDef, index) => { + const { title } = getUiOptions(altUiSchemas?.[index]); const aSchema = aSchemaDef as S; const value = toConstant(aSchema); - const label = aSchema.title || String(value); + const label = title || aSchema.title || String(value); return { schema: aSchema, label, diff --git a/packages/utils/src/types.ts b/packages/utils/src/types.ts index 5e4b848808..6c0fbb0170 100644 --- a/packages/utils/src/types.ts +++ b/packages/utils/src/types.ts @@ -890,6 +890,8 @@ type UIOptionsBaseType | string; + /** Allows a user to provide a list of labels for enum values in the schema */ + enumNames?: string[]; }; /** The type that represents the Options potentially provided by `ui:options` */ diff --git a/packages/utils/test/optionsList.test.ts b/packages/utils/test/optionsList.test.ts index 5a1c804f88..84a0c676b4 100644 --- a/packages/utils/test/optionsList.test.ts +++ b/packages/utils/test/optionsList.test.ts @@ -1,103 +1,280 @@ -import { RJSFSchema, optionsList } from '../src'; +import { RJSFSchema, UiSchema, optionsList } from '../src'; describe('optionsList()', () => { let consoleWarnSpy: jest.SpyInstance; + let oldProcessEnv: string | undefined; beforeAll(() => { + oldProcessEnv = process.env.NODE_ENV; consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); }); afterAll(() => { consoleWarnSpy.mockRestore(); }); afterEach(() => { + process.env.NODE_ENV = oldProcessEnv; consoleWarnSpy.mockClear(); }); + it('returns undefined when schema does not have any options', () => { + expect(optionsList({})).toBeUndefined(); + }); + describe('enums', () => { + it('should generate options for an enum schema', () => { + const enumSchema: RJSFSchema = { + type: 'string', + enum: ['Opt1', 'Opt2', 'Opt3'], + }; - it('should generate options for an enum schema', () => { - const enumSchema: RJSFSchema = { - type: 'string', - enum: ['Opt1', 'Opt2', 'Opt3'], - }; + expect(optionsList(enumSchema)).toEqual(enumSchema.enum!.map((opt) => ({ label: opt, value: opt }))); + }); + it('should generate options for an enum schema and uiSchema enumNames', () => { + const enumSchema: RJSFSchema = { + type: 'string', + enum: ['Opt1', 'Opt2', 'Opt3'], + }; + const uiSchema: UiSchema = { + 'ui:enumNames': ['Option1', 'Option2', 'Option3'], + }; - expect(optionsList(enumSchema)).toEqual(enumSchema.enum!.map((opt) => ({ label: opt, value: opt }))); - }); + expect(optionsList(enumSchema, uiSchema)).toEqual( + enumSchema.enum!.map((opt, index) => { + const label: string = uiSchema['ui:enumNames']![index] ?? opt; + return { label, value: opt }; + }) + ); + }); + it('generates options and favors uiSchema if schema.enumNames is present', () => { + const enumSchema: RJSFSchema = { + type: 'string', + enum: ['Opt1', 'Opt2', 'Opt3'], + }; + const uiSchema: UiSchema = { + 'ui:enumNames': ['Option1', 'Option2', 'Option3'], + }; - it('generates options and emits a deprecation warning for a schema with enumNames', () => { - const enumSchema: RJSFSchema = { - type: 'string', - enum: ['Opt1', 'Opt2', 'Opt3'], - }; + const enumNameSchema = { + ...enumSchema, + enumNames: ['Option One', 'Option Two', 'Option Three'], + }; - const enumNameSchema = { - ...enumSchema, - enumNames: ['Option1', 'Option2', 'Option3'], - }; + expect(optionsList(enumNameSchema, uiSchema)).toEqual( + enumNameSchema.enum!.map((opt, index) => { + const label: string = uiSchema['ui:enumNames']![index] ?? opt; + return { label, value: opt }; + }) + ); + expect(console.warn).not.toHaveBeenCalled(); + }); + it('generates options and does not emit a deprecation warning for a schema with enumNames in production', () => { + process.env.NODE_ENV = 'production'; + const enumSchema: RJSFSchema = { + type: 'string', + enum: ['Opt1', 'Opt2', 'Opt3'], + }; - expect(optionsList(enumNameSchema)).toEqual( - enumNameSchema.enum!.map((opt, index) => { - const label = enumNameSchema.enumNames[index] || opt; - return { label: label, value: opt }; - }) - ); - expect(console.warn).toHaveBeenCalledWith(expect.stringMatching(/The enumNames property is deprecated/)); - }); + const enumNameSchema = { + ...enumSchema, + enumNames: ['Option1', 'Option2', 'Option3'], + }; - it('should generate options for a oneOf|anyOf schema', () => { - const oneOfSchema = { - title: 'string', - oneOf: [ - { - const: 'Option1', - title: 'Option1 title', - description: 'Option1 description', - }, - { - const: 'Option2', - title: 'Option2 title', - description: 'Option2 description', - }, - { - const: 'Option3', - title: 'Option3 title', - description: 'Option3 description', - }, - ], - }; - const anyofSchema = { - ...oneOfSchema, - oneOf: undefined, - anyOf: oneOfSchema.oneOf, - }; - expect(optionsList(oneOfSchema)).toEqual( - oneOfSchema.oneOf.map((schema) => ({ - schema, - label: schema.title, - value: schema.const, - })) - ); - expect(optionsList(anyofSchema)).toEqual( - anyofSchema.anyOf.map((schema) => ({ - schema, - label: schema.title, - value: schema.const, - })) - ); + expect(optionsList(enumNameSchema)).toEqual( + enumNameSchema.enum!.map((opt, index) => { + const label = enumNameSchema.enumNames[index] || opt; + return { label, value: opt }; + }) + ); + expect(console.warn).not.toHaveBeenCalled(); + }); + it('generates options and emits a deprecation warning for a schema with enumNames', () => { + const enumSchema: RJSFSchema = { + type: 'string', + enum: ['Opt1', 'Opt2', 'Opt3'], + }; + + const enumNameSchema = { + ...enumSchema, + enumNames: ['Option1', 'Option2', 'Option3'], + }; + + expect(optionsList(enumNameSchema)).toEqual( + enumNameSchema.enum!.map((opt, index) => { + const label = enumNameSchema.enumNames[index] || opt; + return { label, value: opt }; + }) + ); + expect(console.warn).toHaveBeenCalledWith( + expect.stringMatching(/The "enumNames" property in the schema is deprecated/) + ); + }); + }); + describe('anyOf', () => { + it('should generate options for a anyOf schema', () => { + const anyOfSchema = { + title: 'string', + anyOf: [ + { + const: 'Option1', + title: 'Option1 title', + description: 'Option1 description', + }, + { + const: 'Option2', + title: 'Option2 title', + description: 'Option2 description', + }, + { + const: 'Option3', + title: 'Option3 title', + description: 'Option3 description', + }, + ], + }; + const anyofSchema = { + ...anyOfSchema, + anyOf: anyOfSchema.anyOf, + }; + expect(optionsList(anyOfSchema)).toEqual( + anyOfSchema.anyOf.map((schema) => ({ + schema, + label: schema.title, + value: schema.const, + })) + ); + expect(optionsList(anyofSchema)).toEqual( + anyofSchema.anyOf.map((schema) => ({ + schema, + label: schema.title, + value: schema.const, + })) + ); + }); + it('should generate options for a anyOf schema and uiSchema', () => { + const anyOfSchema = { + title: 'string', + anyOf: [ + { + const: 'Option', + description: 'Option description', + }, + ], + }; + const anyOfUiSchema: UiSchema = { + anyOf: [ + { + 'ui:title': 'Alternate', + }, + ], + }; + expect(optionsList(anyOfSchema, anyOfUiSchema)).toEqual( + anyOfSchema.anyOf.map((schema, index) => ({ + schema, + label: anyOfUiSchema.anyOf[index]['ui:title'], + value: schema.const, + })) + ); + }); + it('should generate options for a anyOf schema uses value as fallback title', () => { + const anyOfSchema = { + title: 'string', + anyOf: [ + { + const: 'Option', + description: 'Option description', + }, + ], + }; + expect(optionsList(anyOfSchema)).toEqual( + anyOfSchema.anyOf.map((schema) => ({ + schema, + label: schema.const, + value: schema.const, + })) + ); + }); }); - it('should generate options for a oneOf schema uses value as fallback title', () => { - const oneOfSchema = { - title: 'string', - oneOf: [ - { - const: 'Option', - description: 'Option description', - }, - ], - }; - expect(optionsList(oneOfSchema)).toEqual( - oneOfSchema.oneOf.map((schema) => ({ - schema, - label: schema.const, - value: schema.const, - })) - ); + describe('oneOf', () => { + it('should generate options for a oneOf schema', () => { + const oneOfSchema = { + title: 'string', + oneOf: [ + { + const: 'Option1', + title: 'Option1 title', + description: 'Option1 description', + }, + { + const: 'Option2', + title: 'Option2 title', + description: 'Option2 description', + }, + { + const: 'Option3', + title: 'Option3 title', + description: 'Option3 description', + }, + ], + }; + const anyofSchema = { + ...oneOfSchema, + oneOf: undefined, + anyOf: oneOfSchema.oneOf, + }; + expect(optionsList(oneOfSchema)).toEqual( + oneOfSchema.oneOf.map((schema) => ({ + schema, + label: schema.title, + value: schema.const, + })) + ); + expect(optionsList(anyofSchema)).toEqual( + anyofSchema.anyOf.map((schema) => ({ + schema, + label: schema.title, + value: schema.const, + })) + ); + }); + it('should generate options for a oneOf schema and uiSchema', () => { + const oneOfSchema = { + title: 'string', + oneOf: [ + { + const: 'Option', + description: 'Option description', + }, + ], + }; + const oneOfUiSchema: UiSchema = { + oneOf: [ + { + 'ui:title': 'Alternate', + }, + ], + }; + expect(optionsList(oneOfSchema, oneOfUiSchema)).toEqual( + oneOfSchema.oneOf.map((schema, index) => ({ + schema, + label: oneOfUiSchema.oneOf[index]['ui:title'], + value: schema.const, + })) + ); + }); + it('should generate options for a oneOf schema uses value as fallback title', () => { + const oneOfSchema = { + title: 'string', + oneOf: [ + { + const: 'Option', + description: 'Option description', + }, + ], + }; + expect(optionsList(oneOfSchema)).toEqual( + oneOfSchema.oneOf.map((schema) => ({ + schema, + label: schema.const, + value: schema.const, + })) + ); + }); }); });