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
-
+