From 26e2454f2371967e5148ec448be7821c573d62b4 Mon Sep 17 00:00:00 2001 From: darius-lesch <134329447+darius-lesch@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:30:55 +0000 Subject: [PATCH] fix: decouple precision from step for NumberControlRenderer Decouple `precision` property in `NumberControlRenderer.vue` from `step` value. Default it to pass `null` to `v-number-input` to allow infinite decimal places instead of locking it to 1 decimal place. Enforce limits only if explicitly provided in `options.precision` via the UI Schema. Includes unit tests for these fixes. --- .../src/controls/NumberControlRenderer.vue | 18 +++++------ .../controls/NumberControlRenderer.spec.ts | 32 +++++++++++++++++++ .../NumberControlRenderer.spec.ts.snap | 2 +- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/packages/vue-vuetify/src/controls/NumberControlRenderer.vue b/packages/vue-vuetify/src/controls/NumberControlRenderer.vue index 872a522e3f..9239b346c3 100644 --- a/packages/vue-vuetify/src/controls/NumberControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/NumberControlRenderer.vue @@ -75,16 +75,16 @@ const controlRenderer = defineComponent({ const options: any = this.appliedOptions; return options.step ?? 0.1; }, - precision(): number | undefined { - if (!this.step || Number.isInteger(this.step)) return undefined; - // Handle scientific notation and float imprecision - const stepStr = this.step.toString(); - if (stepStr.indexOf('e-') > -1) { - // Handle cases like 1e-3 - return parseInt(stepStr.split('e-')[1], 10); + precision(): number | null { + const options: any = this.appliedOptions; + + // If precision is explicitly configured in the UI Schema options, respect it + if (options.precision !== undefined && options.precision !== null) { + return Number(options.precision); } - const fraction = stepStr.split('.')[1]; - return fraction ? fraction.length : undefined; + + // Return null (not undefined) to allow Vuetify to accept any precision by default + return null; }, value(): number | null | undefined { if ( diff --git a/packages/vue-vuetify/tests/unit/controls/NumberControlRenderer.spec.ts b/packages/vue-vuetify/tests/unit/controls/NumberControlRenderer.spec.ts index 0f76ffd832..3d2493a1bd 100644 --- a/packages/vue-vuetify/tests/unit/controls/NumberControlRenderer.spec.ts +++ b/packages/vue-vuetify/tests/unit/controls/NumberControlRenderer.spec.ts @@ -57,3 +57,35 @@ describe('NumberControlRenderer.vue', () => { expect(wrapper.html()).toMatchSnapshot(); }); }); + +import { VNumberInput } from 'vuetify/components'; + +describe('NumberControlRenderer precision logic', () => { + const renderers = [numberControlRendererEntry]; + const schema = { type: 'number' }; + + beforeEach(() => { + clearAllIds(); + }); + + it('allows unlimited precision by default (passes null to v-number-input)', () => { + const uischema = { type: 'Control', scope: '#' }; + const wrapper = mountJsonForms(1.23456, schema, renderers, uischema); + + const numberInput = wrapper.findComponent(VNumberInput); + // Vuetify requires null to remove precision limits + expect(numberInput.props('precision')).toBeNull(); + }); + + it('respects explicitly configured precision via UI schema options', () => { + const uischema = { + type: 'Control', + scope: '#', + options: { precision: 3 } + }; + const wrapper = mountJsonForms(1.23456, schema, renderers, uischema); + + const numberInput = wrapper.findComponent(VNumberInput); + expect(numberInput.props('precision')).toBe(3); + }); +}); diff --git a/packages/vue-vuetify/tests/unit/controls/__snapshots__/NumberControlRenderer.spec.ts.snap b/packages/vue-vuetify/tests/unit/controls/__snapshots__/NumberControlRenderer.spec.ts.snap index 0094a50bca..7e6bd28595 100644 --- a/packages/vue-vuetify/tests/unit/controls/__snapshots__/NumberControlRenderer.spec.ts.snap +++ b/packages/vue-vuetify/tests/unit/controls/__snapshots__/NumberControlRenderer.spec.ts.snap @@ -29,7 +29,7 @@ exports[`NumberControlRenderer.vue > should render component and match snapshot - +