mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 10:59:53 +00:00
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 <448862+DrJKL@users.noreply.github.com> Co-authored-by: Alexander Brown <drjkl@comfy.org> Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
@@ -0,0 +1,208 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import PrimeVue from 'primevue/config'
|
||||
import InputNumber from 'primevue/inputnumber'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
||||
|
||||
import WidgetInputNumberInput from './WidgetInputNumberInput.vue'
|
||||
|
||||
function createMockWidget(
|
||||
value: number = 0,
|
||||
type: 'int' | 'float' = 'int',
|
||||
options: SimplifiedWidget['options'] = {},
|
||||
callback?: (value: number) => void
|
||||
): SimplifiedWidget<number> {
|
||||
return {
|
||||
name: 'test_input_number',
|
||||
type,
|
||||
value,
|
||||
options,
|
||||
callback
|
||||
}
|
||||
}
|
||||
|
||||
function mountComponent(
|
||||
widget: SimplifiedWidget<number>,
|
||||
modelValue: number,
|
||||
readonly = false
|
||||
) {
|
||||
return mount(WidgetInputNumberInput, {
|
||||
global: {
|
||||
plugins: [PrimeVue],
|
||||
components: { InputNumber }
|
||||
},
|
||||
props: {
|
||||
widget,
|
||||
modelValue,
|
||||
readonly
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getNumberInput(wrapper: ReturnType<typeof mount>) {
|
||||
const input = wrapper.get<HTMLInputElement>('input[inputmode="numeric"]')
|
||||
return input.element
|
||||
}
|
||||
|
||||
describe('WidgetInputNumberInput Value Binding', () => {
|
||||
it('displays initial value in input field', () => {
|
||||
const widget = createMockWidget(42, 'int')
|
||||
const wrapper = mountComponent(widget, 42)
|
||||
|
||||
const input = getNumberInput(wrapper)
|
||||
expect(input.value).toBe('42')
|
||||
})
|
||||
|
||||
it('emits update:modelValue when value changes', async () => {
|
||||
const widget = createMockWidget(10, 'int')
|
||||
const wrapper = mountComponent(widget, 10)
|
||||
|
||||
const inputNumber = wrapper.findComponent(InputNumber)
|
||||
await inputNumber.vm.$emit('update:modelValue', 20)
|
||||
|
||||
const emitted = wrapper.emitted('update:modelValue')
|
||||
expect(emitted).toBeDefined()
|
||||
expect(emitted![0]).toContain(20)
|
||||
})
|
||||
|
||||
it('handles negative values', () => {
|
||||
const widget = createMockWidget(-5, 'int')
|
||||
const wrapper = mountComponent(widget, -5)
|
||||
|
||||
const input = getNumberInput(wrapper)
|
||||
expect(input.value).toBe('-5')
|
||||
})
|
||||
|
||||
it('handles decimal values for float type', () => {
|
||||
const widget = createMockWidget(3.14, 'float')
|
||||
const wrapper = mountComponent(widget, 3.14)
|
||||
|
||||
const input = getNumberInput(wrapper)
|
||||
expect(input.value).toBe('3.14')
|
||||
})
|
||||
})
|
||||
|
||||
describe('WidgetInputNumberInput Component Rendering', () => {
|
||||
it('renders InputNumber component with show-buttons', () => {
|
||||
const widget = createMockWidget(5, 'int')
|
||||
const wrapper = mountComponent(widget, 5)
|
||||
|
||||
const inputNumber = wrapper.findComponent(InputNumber)
|
||||
expect(inputNumber.exists()).toBe(true)
|
||||
expect(inputNumber.props('showButtons')).toBe(true)
|
||||
})
|
||||
|
||||
it('disables input when readonly', () => {
|
||||
const widget = createMockWidget(5, 'int', {}, undefined)
|
||||
const wrapper = mountComponent(widget, 5, true)
|
||||
|
||||
const inputNumber = wrapper.findComponent(InputNumber)
|
||||
expect(inputNumber.props('disabled')).toBe(true)
|
||||
})
|
||||
|
||||
it('sets button layout to horizontal', () => {
|
||||
const widget = createMockWidget(5, 'int')
|
||||
const wrapper = mountComponent(widget, 5)
|
||||
|
||||
const inputNumber = wrapper.findComponent(InputNumber)
|
||||
expect(inputNumber.props('buttonLayout')).toBe('horizontal')
|
||||
})
|
||||
|
||||
it('sets size to small', () => {
|
||||
const widget = createMockWidget(5, 'int')
|
||||
const wrapper = mountComponent(widget, 5)
|
||||
|
||||
const inputNumber = wrapper.findComponent(InputNumber)
|
||||
expect(inputNumber.props('size')).toBe('small')
|
||||
})
|
||||
})
|
||||
|
||||
describe('WidgetInputNumberInput Step Value', () => {
|
||||
it('defaults to 0 for unrestricted stepping', () => {
|
||||
const widget = createMockWidget(5, 'int')
|
||||
const wrapper = mountComponent(widget, 5)
|
||||
|
||||
const inputNumber = wrapper.findComponent(InputNumber)
|
||||
expect(inputNumber.props('step')).toBe(0)
|
||||
})
|
||||
|
||||
it('uses step2 value when provided', () => {
|
||||
const widget = createMockWidget(5, 'int', { step2: 0.5 })
|
||||
const wrapper = mountComponent(widget, 5)
|
||||
|
||||
const inputNumber = wrapper.findComponent(InputNumber)
|
||||
expect(inputNumber.props('step')).toBe(0.5)
|
||||
})
|
||||
|
||||
it('calculates step from precision for precision 0', () => {
|
||||
const widget = createMockWidget(5, 'int', { precision: 0 })
|
||||
const wrapper = mountComponent(widget, 5)
|
||||
|
||||
const inputNumber = wrapper.findComponent(InputNumber)
|
||||
expect(inputNumber.props('step')).toBe(1)
|
||||
})
|
||||
|
||||
it('calculates step from precision for precision 1', () => {
|
||||
const widget = createMockWidget(5, 'float', { precision: 1 })
|
||||
const wrapper = mountComponent(widget, 5)
|
||||
|
||||
const inputNumber = wrapper.findComponent(InputNumber)
|
||||
expect(inputNumber.props('step')).toBe(0.1)
|
||||
})
|
||||
|
||||
it('calculates step from precision for precision 2', () => {
|
||||
const widget = createMockWidget(5, 'float', { precision: 2 })
|
||||
const wrapper = mountComponent(widget, 5)
|
||||
|
||||
const inputNumber = wrapper.findComponent(InputNumber)
|
||||
expect(inputNumber.props('step')).toBe(0.01)
|
||||
})
|
||||
})
|
||||
|
||||
describe('WidgetInputNumberInput Grouping Behavior', () => {
|
||||
it('displays numbers without commas by default for int widgets', () => {
|
||||
const widget = createMockWidget(1000, 'int')
|
||||
const wrapper = mountComponent(widget, 1000)
|
||||
|
||||
const input = getNumberInput(wrapper)
|
||||
expect(input.value).toBe('1000')
|
||||
expect(input.value).not.toContain(',')
|
||||
})
|
||||
|
||||
it('displays numbers without commas by default for float widgets', () => {
|
||||
const widget = createMockWidget(1000.5, 'float')
|
||||
const wrapper = mountComponent(widget, 1000.5)
|
||||
|
||||
const input = getNumberInput(wrapper)
|
||||
expect(input.value).toBe('1000.5')
|
||||
expect(input.value).not.toContain(',')
|
||||
})
|
||||
|
||||
it('displays numbers with commas when grouping enabled', () => {
|
||||
const widget = createMockWidget(1000, 'int', { useGrouping: true })
|
||||
const wrapper = mountComponent(widget, 1000)
|
||||
|
||||
const input = getNumberInput(wrapper)
|
||||
expect(input.value).toBe('1,000')
|
||||
expect(input.value).toContain(',')
|
||||
})
|
||||
|
||||
it('displays numbers without commas when grouping explicitly disabled', () => {
|
||||
const widget = createMockWidget(1000, 'int', { useGrouping: false })
|
||||
const wrapper = mountComponent(widget, 1000)
|
||||
|
||||
const input = getNumberInput(wrapper)
|
||||
expect(input.value).toBe('1000')
|
||||
expect(input.value).not.toContain(',')
|
||||
})
|
||||
|
||||
it('displays numbers without commas when useGrouping option is undefined', () => {
|
||||
const widget = createMockWidget(1000, 'int', { useGrouping: undefined })
|
||||
const wrapper = mountComponent(widget, 1000)
|
||||
|
||||
const input = getNumberInput(wrapper)
|
||||
expect(input.value).toBe('1000')
|
||||
expect(input.value).not.toContain(',')
|
||||
})
|
||||
})
|
||||
@@ -48,6 +48,11 @@ const stepValue = computed(() => {
|
||||
// Default to 'any' for unrestricted stepping
|
||||
return 0
|
||||
})
|
||||
|
||||
// Disable grouping separators by default unless explicitly enabled by the node author
|
||||
const useGrouping = computed(() => {
|
||||
return props.widget.options?.useGrouping === true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -60,6 +65,7 @@ const stepValue = computed(() => {
|
||||
size="small"
|
||||
:disabled="readonly"
|
||||
:step="stepValue"
|
||||
:use-grouping="useGrouping"
|
||||
:class="cn(WidgetInputBaseClass, 'w-full text-xs')"
|
||||
:pt="{
|
||||
incrementButton:
|
||||
|
||||
Reference in New Issue
Block a user