From 7993b43df1f9d052b04ed6f2eb784cc535eafda7 Mon Sep 17 00:00:00 2001 From: darius-lesch <134329447+darius-lesch@users.noreply.github.com> Date: Wed, 22 Jul 2026 10:54:22 +0000 Subject: [PATCH] fix(vue-vanilla): fix type coercion for Enum and OneOfEnum controls This commit fixes an issue where selecting an integer value in the `EnumControlRenderer` or `EnumOneOfControlRenderer` would incorrectly write a stringified version of the integer to the JSONForms data state, causing validation errors. The components were updated to look up the correct value from `options` using `selectedIndex - 1` (accounting for the hardcoded empty option at index 0) instead of relying on `target.value`. Additional tests were added to explicitly verify this behavior with an `integer` based schema. --- .../src/controls/EnumControlRenderer.vue | 7 +++-- .../src/controls/EnumOneOfControlRenderer.vue | 7 +++-- .../unit/controls/EnumControlRenderer.spec.ts | 23 ++++++++++++++++ .../controls/EnumOneOfControlRenderer.spec.ts | 26 +++++++++++++++++++ 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/packages/vue-vanilla/src/controls/EnumControlRenderer.vue b/packages/vue-vanilla/src/controls/EnumControlRenderer.vue index e2bf0ce6c2..0de37499aa 100644 --- a/packages/vue-vanilla/src/controls/EnumControlRenderer.vue +++ b/packages/vue-vanilla/src/controls/EnumControlRenderer.vue @@ -52,8 +52,11 @@ const controlRenderer = defineComponent({ ...rendererProps(), }, setup(props: RendererProps) { - return useVanillaControl(useJsonFormsEnumControl(props), (target) => - target.selectedIndex === 0 ? undefined : target.value + const input = useJsonFormsEnumControl(props); + return useVanillaControl(input, (target) => + target.selectedIndex === 0 + ? undefined + : input.control.value.options[target.selectedIndex - 1].value ); }, }); diff --git a/packages/vue-vanilla/src/controls/EnumOneOfControlRenderer.vue b/packages/vue-vanilla/src/controls/EnumOneOfControlRenderer.vue index 0b520279d9..3c46193385 100644 --- a/packages/vue-vanilla/src/controls/EnumOneOfControlRenderer.vue +++ b/packages/vue-vanilla/src/controls/EnumOneOfControlRenderer.vue @@ -52,8 +52,11 @@ const controlRenderer = defineComponent({ ...rendererProps(), }, setup(props: RendererProps) { - return useVanillaControl(useJsonFormsOneOfEnumControl(props), (target) => - target.selectedIndex === 0 ? undefined : target.value + const input = useJsonFormsOneOfEnumControl(props); + return useVanillaControl(input, (target) => + target.selectedIndex === 0 + ? undefined + : input.control.value.options[target.selectedIndex - 1].value ); }, }); diff --git a/packages/vue-vanilla/tests/unit/controls/EnumControlRenderer.spec.ts b/packages/vue-vanilla/tests/unit/controls/EnumControlRenderer.spec.ts index 58cb1c0e6d..85f3902076 100644 --- a/packages/vue-vanilla/tests/unit/controls/EnumControlRenderer.spec.ts +++ b/packages/vue-vanilla/tests/unit/controls/EnumControlRenderer.spec.ts @@ -28,4 +28,27 @@ describe('EnumControlRenderer.vue', () => { await select.setValue('b'); expect(wrapper.vm.data).to.equal('b'); }); + + it('emits undefined when empty option is selected', async () => { + const wrapper = mountJsonForms('a', schema, uischema); + const select = wrapper.find('select'); + await select.setValue(''); + expect(wrapper.vm.data).to.be.undefined; + }); +}); + +const numberSchema = { + type: 'integer', + title: 'My Integer Enum', + enum: [1, 2], +}; + +describe('EnumControlRenderer.vue (integer)', () => { + it('emits a data change with number type', async () => { + const wrapper = mountJsonForms(1, numberSchema, uischema); + const select = wrapper.find('select'); + await select.setValue('2'); + expect(wrapper.vm.data).to.be.a('number'); + expect(wrapper.vm.data).to.equal(2); + }); }); diff --git a/packages/vue-vanilla/tests/unit/controls/EnumOneOfControlRenderer.spec.ts b/packages/vue-vanilla/tests/unit/controls/EnumOneOfControlRenderer.spec.ts index 371b55458b..964f51a27b 100644 --- a/packages/vue-vanilla/tests/unit/controls/EnumOneOfControlRenderer.spec.ts +++ b/packages/vue-vanilla/tests/unit/controls/EnumOneOfControlRenderer.spec.ts @@ -31,4 +31,30 @@ describe('EnumOneOfControlRenderer.vue', () => { await select.setValue('b'); expect(wrapper.vm.data).to.equal('b'); }); + + it('emits undefined when empty option is selected', async () => { + const wrapper = mountJsonForms('a', schema, uischema); + const select = wrapper.find('select'); + await select.setValue(''); + expect(wrapper.vm.data).to.be.undefined; + }); +}); + +const numberSchema = { + type: 'integer', + title: 'My Integer OneOf Enum', + oneOf: [ + { const: 1, title: 'One' }, + { const: 2, title: 'Two' }, + ], +}; + +describe('EnumOneOfControlRenderer.vue (integer)', () => { + it('emits a data change with number type', async () => { + const wrapper = mountJsonForms(1, numberSchema, uischema); + const select = wrapper.find('select'); + await select.setValue('2'); + expect(wrapper.vm.data).to.be.a('number'); + expect(wrapper.vm.data).to.equal(2); + }); });