Skip to content

Commit 3326e7f

Browse files
authored
fix(vue-vanilla): preserve options' data types in enum controls
fixes #2450 Adapts the vue-vanilla enum controls to set values from the provided enum options instead of the stringified value of the HTML select element. Native HTML <select> elements inherently serialize all bound data into strings. The previous implementation relied on reading the DOM's target.value, which destroyed the original type information. Using the DOM's selectedIndex as a pointer to fetch the original value from the JSONForms state array bypasses this serialization completely. This provides a robust, universally type-safe solution for all schema types (numbers, booleans, etc.) without relying on brittle manual type-casting logic.
1 parent 7998a7d commit 3326e7f

4 files changed

Lines changed: 59 additions & 4 deletions

File tree

packages/vue-vanilla/src/controls/EnumControlRenderer.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,11 @@ const controlRenderer = defineComponent({
5252
...rendererProps<ControlElement>(),
5353
},
5454
setup(props: RendererProps<ControlElement>) {
55-
return useVanillaControl(useJsonFormsEnumControl(props), (target) =>
56-
target.selectedIndex === 0 ? undefined : target.value
55+
const input = useJsonFormsEnumControl(props);
56+
return useVanillaControl(input, (target) =>
57+
target.selectedIndex === 0
58+
? undefined
59+
: input.control.value.options[target.selectedIndex - 1].value
5760
);
5861
},
5962
});

packages/vue-vanilla/src/controls/EnumOneOfControlRenderer.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,11 @@ const controlRenderer = defineComponent({
5252
...rendererProps<ControlElement>(),
5353
},
5454
setup(props: RendererProps<ControlElement>) {
55-
return useVanillaControl(useJsonFormsOneOfEnumControl(props), (target) =>
56-
target.selectedIndex === 0 ? undefined : target.value
55+
const input = useJsonFormsOneOfEnumControl(props);
56+
return useVanillaControl(input, (target) =>
57+
target.selectedIndex === 0
58+
? undefined
59+
: input.control.value.options[target.selectedIndex - 1].value
5760
);
5861
},
5962
});

packages/vue-vanilla/tests/unit/controls/EnumControlRenderer.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,27 @@ describe('EnumControlRenderer.vue', () => {
2828
await select.setValue('b');
2929
expect(wrapper.vm.data).to.equal('b');
3030
});
31+
32+
it('emits undefined when empty option is selected', async () => {
33+
const wrapper = mountJsonForms('a', schema, uischema);
34+
const select = wrapper.find('select');
35+
await select.setValue('');
36+
expect(wrapper.vm.data).to.be.undefined;
37+
});
38+
});
39+
40+
const numberSchema = {
41+
type: 'integer',
42+
title: 'My Integer Enum',
43+
enum: [1, 2],
44+
};
45+
46+
describe('EnumControlRenderer.vue (integer)', () => {
47+
it('emits a data change with number type', async () => {
48+
const wrapper = mountJsonForms(1, numberSchema, uischema);
49+
const select = wrapper.find('select');
50+
await select.setValue('2');
51+
expect(wrapper.vm.data).to.be.a('number');
52+
expect(wrapper.vm.data).to.equal(2);
53+
});
3154
});

packages/vue-vanilla/tests/unit/controls/EnumOneOfControlRenderer.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,30 @@ describe('EnumOneOfControlRenderer.vue', () => {
3131
await select.setValue('b');
3232
expect(wrapper.vm.data).to.equal('b');
3333
});
34+
35+
it('emits undefined when empty option is selected', async () => {
36+
const wrapper = mountJsonForms('a', schema, uischema);
37+
const select = wrapper.find('select');
38+
await select.setValue('');
39+
expect(wrapper.vm.data).to.be.undefined;
40+
});
41+
});
42+
43+
const numberSchema = {
44+
type: 'integer',
45+
title: 'My Integer OneOf Enum',
46+
oneOf: [
47+
{ const: 1, title: 'One' },
48+
{ const: 2, title: 'Two' },
49+
],
50+
};
51+
52+
describe('EnumOneOfControlRenderer.vue (integer)', () => {
53+
it('emits a data change with number type', async () => {
54+
const wrapper = mountJsonForms(1, numberSchema, uischema);
55+
const select = wrapper.find('select');
56+
await select.setValue('2');
57+
expect(wrapper.vm.data).to.be.a('number');
58+
expect(wrapper.vm.data).to.equal(2);
59+
});
3460
});

0 commit comments

Comments
 (0)