Skip to content

Commit ac93a6b

Browse files
christian-byrneDrJKLgithub-actions
authored
Disable number grouping (thousands comma separators) by default in Vue node number widgets (#5776)
## Summary Makes the [useGrouping](https://primevue.org/inputnumber/#api.inputnumber.props.useGrouping) prop for number widgets disabled by default, aligning with the old UI (also requested via design). Node authors can still enable if they want by setting prop explicitly. ## Changes - **What**: Modified [WidgetInputNumberInput](https://primevue.org/inputnumber/) to disable `useGrouping` by default, requiring explicit opt-in via widget options - **Testing**: Added component tests covering value binding, component rendering, step calculations, and grouping behavior ## Review Focus UX impact on existing nodes that may have relied on default grouping behavior and test coverage for edge cases with precision calculations. ## Screenshots (if applicable) *Before*: <img width="1685" height="879" alt="Screenshot from 2025-09-25 11-34-34" src="https://github.com/user-attachments/assets/432097ab-203d-4f86-8ca0-721b27ee33de" /> *After*: <img width="1951" height="1175" alt="Screenshot from 2025-09-25 11-35-27" src="https://github.com/user-attachments/assets/74d35b62-612e-4dbf-b6e2-0ac17af03ea1" /> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5776-Disable-number-grouping-thousands-comma-separators-by-default-in-Vue-node-number-widget-2796d73d365081369ca6c155335d0d57) by [Unito](https://www.unito.io) --------- Co-authored-by: DrJKL <[email protected]> Co-authored-by: Alexander Brown <[email protected]> Co-authored-by: github-actions <[email protected]>
1 parent 961af87 commit ac93a6b

File tree

4 files changed

+214
-0
lines changed

4 files changed

+214
-0
lines changed
137 Bytes
Loading
123 Bytes
Loading
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import { mount } from '@vue/test-utils'
2+
import PrimeVue from 'primevue/config'
3+
import InputNumber from 'primevue/inputnumber'
4+
import { describe, expect, it } from 'vitest'
5+
6+
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
7+
8+
import WidgetInputNumberInput from './WidgetInputNumberInput.vue'
9+
10+
function createMockWidget(
11+
value: number = 0,
12+
type: 'int' | 'float' = 'int',
13+
options: SimplifiedWidget['options'] = {},
14+
callback?: (value: number) => void
15+
): SimplifiedWidget<number> {
16+
return {
17+
name: 'test_input_number',
18+
type,
19+
value,
20+
options,
21+
callback
22+
}
23+
}
24+
25+
function mountComponent(
26+
widget: SimplifiedWidget<number>,
27+
modelValue: number,
28+
readonly = false
29+
) {
30+
return mount(WidgetInputNumberInput, {
31+
global: {
32+
plugins: [PrimeVue],
33+
components: { InputNumber }
34+
},
35+
props: {
36+
widget,
37+
modelValue,
38+
readonly
39+
}
40+
})
41+
}
42+
43+
function getNumberInput(wrapper: ReturnType<typeof mount>) {
44+
const input = wrapper.get<HTMLInputElement>('input[inputmode="numeric"]')
45+
return input.element
46+
}
47+
48+
describe('WidgetInputNumberInput Value Binding', () => {
49+
it('displays initial value in input field', () => {
50+
const widget = createMockWidget(42, 'int')
51+
const wrapper = mountComponent(widget, 42)
52+
53+
const input = getNumberInput(wrapper)
54+
expect(input.value).toBe('42')
55+
})
56+
57+
it('emits update:modelValue when value changes', async () => {
58+
const widget = createMockWidget(10, 'int')
59+
const wrapper = mountComponent(widget, 10)
60+
61+
const inputNumber = wrapper.findComponent(InputNumber)
62+
await inputNumber.vm.$emit('update:modelValue', 20)
63+
64+
const emitted = wrapper.emitted('update:modelValue')
65+
expect(emitted).toBeDefined()
66+
expect(emitted![0]).toContain(20)
67+
})
68+
69+
it('handles negative values', () => {
70+
const widget = createMockWidget(-5, 'int')
71+
const wrapper = mountComponent(widget, -5)
72+
73+
const input = getNumberInput(wrapper)
74+
expect(input.value).toBe('-5')
75+
})
76+
77+
it('handles decimal values for float type', () => {
78+
const widget = createMockWidget(3.14, 'float')
79+
const wrapper = mountComponent(widget, 3.14)
80+
81+
const input = getNumberInput(wrapper)
82+
expect(input.value).toBe('3.14')
83+
})
84+
})
85+
86+
describe('WidgetInputNumberInput Component Rendering', () => {
87+
it('renders InputNumber component with show-buttons', () => {
88+
const widget = createMockWidget(5, 'int')
89+
const wrapper = mountComponent(widget, 5)
90+
91+
const inputNumber = wrapper.findComponent(InputNumber)
92+
expect(inputNumber.exists()).toBe(true)
93+
expect(inputNumber.props('showButtons')).toBe(true)
94+
})
95+
96+
it('disables input when readonly', () => {
97+
const widget = createMockWidget(5, 'int', {}, undefined)
98+
const wrapper = mountComponent(widget, 5, true)
99+
100+
const inputNumber = wrapper.findComponent(InputNumber)
101+
expect(inputNumber.props('disabled')).toBe(true)
102+
})
103+
104+
it('sets button layout to horizontal', () => {
105+
const widget = createMockWidget(5, 'int')
106+
const wrapper = mountComponent(widget, 5)
107+
108+
const inputNumber = wrapper.findComponent(InputNumber)
109+
expect(inputNumber.props('buttonLayout')).toBe('horizontal')
110+
})
111+
112+
it('sets size to small', () => {
113+
const widget = createMockWidget(5, 'int')
114+
const wrapper = mountComponent(widget, 5)
115+
116+
const inputNumber = wrapper.findComponent(InputNumber)
117+
expect(inputNumber.props('size')).toBe('small')
118+
})
119+
})
120+
121+
describe('WidgetInputNumberInput Step Value', () => {
122+
it('defaults to 0 for unrestricted stepping', () => {
123+
const widget = createMockWidget(5, 'int')
124+
const wrapper = mountComponent(widget, 5)
125+
126+
const inputNumber = wrapper.findComponent(InputNumber)
127+
expect(inputNumber.props('step')).toBe(0)
128+
})
129+
130+
it('uses step2 value when provided', () => {
131+
const widget = createMockWidget(5, 'int', { step2: 0.5 })
132+
const wrapper = mountComponent(widget, 5)
133+
134+
const inputNumber = wrapper.findComponent(InputNumber)
135+
expect(inputNumber.props('step')).toBe(0.5)
136+
})
137+
138+
it('calculates step from precision for precision 0', () => {
139+
const widget = createMockWidget(5, 'int', { precision: 0 })
140+
const wrapper = mountComponent(widget, 5)
141+
142+
const inputNumber = wrapper.findComponent(InputNumber)
143+
expect(inputNumber.props('step')).toBe(1)
144+
})
145+
146+
it('calculates step from precision for precision 1', () => {
147+
const widget = createMockWidget(5, 'float', { precision: 1 })
148+
const wrapper = mountComponent(widget, 5)
149+
150+
const inputNumber = wrapper.findComponent(InputNumber)
151+
expect(inputNumber.props('step')).toBe(0.1)
152+
})
153+
154+
it('calculates step from precision for precision 2', () => {
155+
const widget = createMockWidget(5, 'float', { precision: 2 })
156+
const wrapper = mountComponent(widget, 5)
157+
158+
const inputNumber = wrapper.findComponent(InputNumber)
159+
expect(inputNumber.props('step')).toBe(0.01)
160+
})
161+
})
162+
163+
describe('WidgetInputNumberInput Grouping Behavior', () => {
164+
it('displays numbers without commas by default for int widgets', () => {
165+
const widget = createMockWidget(1000, 'int')
166+
const wrapper = mountComponent(widget, 1000)
167+
168+
const input = getNumberInput(wrapper)
169+
expect(input.value).toBe('1000')
170+
expect(input.value).not.toContain(',')
171+
})
172+
173+
it('displays numbers without commas by default for float widgets', () => {
174+
const widget = createMockWidget(1000.5, 'float')
175+
const wrapper = mountComponent(widget, 1000.5)
176+
177+
const input = getNumberInput(wrapper)
178+
expect(input.value).toBe('1000.5')
179+
expect(input.value).not.toContain(',')
180+
})
181+
182+
it('displays numbers with commas when grouping enabled', () => {
183+
const widget = createMockWidget(1000, 'int', { useGrouping: true })
184+
const wrapper = mountComponent(widget, 1000)
185+
186+
const input = getNumberInput(wrapper)
187+
expect(input.value).toBe('1,000')
188+
expect(input.value).toContain(',')
189+
})
190+
191+
it('displays numbers without commas when grouping explicitly disabled', () => {
192+
const widget = createMockWidget(1000, 'int', { useGrouping: false })
193+
const wrapper = mountComponent(widget, 1000)
194+
195+
const input = getNumberInput(wrapper)
196+
expect(input.value).toBe('1000')
197+
expect(input.value).not.toContain(',')
198+
})
199+
200+
it('displays numbers without commas when useGrouping option is undefined', () => {
201+
const widget = createMockWidget(1000, 'int', { useGrouping: undefined })
202+
const wrapper = mountComponent(widget, 1000)
203+
204+
const input = getNumberInput(wrapper)
205+
expect(input.value).toBe('1000')
206+
expect(input.value).not.toContain(',')
207+
})
208+
})

src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ const stepValue = computed(() => {
4848
// Default to 'any' for unrestricted stepping
4949
return 0
5050
})
51+
52+
// Disable grouping separators by default unless explicitly enabled by the node author
53+
const useGrouping = computed(() => {
54+
return props.widget.options?.useGrouping === true
55+
})
5156
</script>
5257

5358
<template>
@@ -60,6 +65,7 @@ const stepValue = computed(() => {
6065
size="small"
6166
:disabled="readonly"
6267
:step="stepValue"
68+
:use-grouping="useGrouping"
6369
:class="cn(WidgetInputBaseClass, 'w-full text-xs')"
6470
:pt="{
6571
incrementButton:

0 commit comments

Comments
 (0)