diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetColorPicker.test.ts b/src/renderer/extensions/vueNodes/widgets/components/WidgetColorPicker.test.ts index dacc21f08..70f7e240d 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetColorPicker.test.ts +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetColorPicker.test.ts @@ -17,7 +17,7 @@ describe('WidgetColorPicker Value Binding', () => { callback?: (value: string) => void ): SimplifiedWidget => { const valueRef = ref(value) - if (callback) watch(valueRef, callback) + if (callback) watch(valueRef, (v) => callback(v)) return { name: 'test_color_picker', type: 'color', @@ -53,80 +53,61 @@ describe('WidgetColorPicker Value Binding', () => { ) => { const colorPicker = wrapper.findComponent({ name: 'ColorPicker' }) await colorPicker.setValue(value) - return wrapper.emitted('update:modelValue') } - describe('Vue Event Emission', () => { - it('emits Vue event when color changes', async () => { - const widget = createMockWidget('#ff0000') + describe('Value Binding', () => { + it('triggers callback when color changes', async () => { + const callback = vi.fn() + const widget = createMockWidget('#ff0000', {}, callback) const wrapper = mountComponent(widget, '#ff0000') - const emitted = await setColorPickerValue(wrapper, '#00ff00') - - expect(emitted).toBeDefined() - expect(emitted![0]).toContain('#00ff00') + await setColorPickerValue(wrapper, '#00ff00') + expect(callback).toHaveBeenCalledExactlyOnceWith('#00ff00') }) it('handles different color formats', async () => { - const widget = createMockWidget('#ffffff') + const callback = vi.fn() + const widget = createMockWidget('#ffffff', {}, callback) const wrapper = mountComponent(widget, '#ffffff') - const emitted = await setColorPickerValue(wrapper, '#123abc') - - expect(emitted).toBeDefined() - expect(emitted![0]).toContain('#123abc') + await setColorPickerValue(wrapper, '#123abc') + expect(callback).toHaveBeenCalledExactlyOnceWith('#123abc') }) - it('handles missing callback gracefully', async () => { - const widget = createMockWidget('#000000', {}, undefined) - const wrapper = mountComponent(widget, '#000000') - - const emitted = await setColorPickerValue(wrapper, '#ff00ff') - - // Should still emit Vue event - expect(emitted).toBeDefined() - expect(emitted![0]).toContain('#ff00ff') - }) - - it('normalizes bare hex without # to #hex on emit', async () => { - const widget = createMockWidget('ff0000') + it('normalizes bare hex without # to #hex', async () => { + const callback = vi.fn() + const widget = createMockWidget('ff0000', {}, callback) const wrapper = mountComponent(widget, 'ff0000') - const emitted = await setColorPickerValue(wrapper, '00ff00') - expect(emitted).toBeDefined() - expect(emitted![0]).toContain('#00ff00') + await setColorPickerValue(wrapper, '00ff00') + expect(callback).toHaveBeenCalledExactlyOnceWith('#00ff00') }) - it('normalizes rgb() strings to #hex on emit', async (context) => { - context.skip('needs diagnosis') - const widget = createMockWidget('#000000') + it('normalizes rgb() strings to #hex', async () => { + const callback = vi.fn() + const widget = createMockWidget('#000000', { format: 'rgb' }, callback) const wrapper = mountComponent(widget, '#000000') - const emitted = await setColorPickerValue(wrapper, 'rgb(255, 0, 0)') - expect(emitted).toBeDefined() - expect(emitted![0]).toContain('#ff0000') + await setColorPickerValue(wrapper, 'rgb(255, 0, 0)') + expect(callback).toHaveBeenCalledExactlyOnceWith('#ff0000') }) - it('normalizes hsb() strings to #hex on emit', async () => { - const widget = createMockWidget('#000000', { format: 'hsb' }) + it('normalizes hsb() strings to #hex', async () => { + const callback = vi.fn() + const widget = createMockWidget('#000000', { format: 'hsb' }, callback) const wrapper = mountComponent(widget, '#000000') - const emitted = await setColorPickerValue(wrapper, 'hsb(120, 100, 100)') - expect(emitted).toBeDefined() - expect(emitted![0]).toContain('#00ff00') + await setColorPickerValue(wrapper, 'hsb(120, 100, 100)') + expect(callback).toHaveBeenCalledExactlyOnceWith('#00ff00') }) - it('normalizes HSB object values to #hex on emit', async () => { - const widget = createMockWidget('#000000', { format: 'hsb' }) + it('normalizes HSB object values to #hex', async () => { + const callback = vi.fn() + const widget = createMockWidget('#000000', { format: 'hsb' }, callback) const wrapper = mountComponent(widget, '#000000') - const emitted = await setColorPickerValue(wrapper, { - h: 240, - s: 100, - b: 100 - }) - expect(emitted).toBeDefined() - expect(emitted![0]).toContain('#0000ff') + await setColorPickerValue(wrapper, { h: 240, s: 100, b: 100 }) + expect(callback).toHaveBeenCalledExactlyOnceWith('#0000ff') }) }) @@ -269,15 +250,15 @@ describe('WidgetColorPicker Value Binding', () => { }) it('handles invalid color formats gracefully', async () => { - const widget = createMockWidget('invalid-color') + const callback = vi.fn() + const widget = createMockWidget('invalid-color', {}, callback) const wrapper = mountComponent(widget, 'invalid-color') const colorText = wrapper.find('[data-testid="widget-color-text"]') expect(colorText.text()).toBe('#000000') - const emitted = await setColorPickerValue(wrapper, 'invalid-color') - expect(emitted).toBeDefined() - expect(emitted![0]).toContain('#000000') + await setColorPickerValue(wrapper, 'invalid-color') + expect(callback).toHaveBeenCalledExactlyOnceWith('#000000') }) it('handles widget with no options', () => { diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetColorPicker.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetColorPicker.vue index b87f6b616..bf932c1f5 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetColorPicker.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetColorPicker.vue @@ -14,7 +14,6 @@ :pt="{ preview: '!w-full !h-full !border-none' }" - @update:model-value="onPickerUpdate" /> import ColorPicker from 'primevue/colorpicker' -import { computed, ref, watch } from 'vue' +import { computed } from 'vue' import type { SimplifiedWidget } from '@/types/simplifiedWidget' import { isColorFormat, toHexFromFormat } from '@/utils/colorUtil' -import type { ColorFormat, HSB } from '@/utils/colorUtil' +import type { ColorFormat } from '@/utils/colorUtil' import { cn } from '@/utils/tailwindUtil' import { PANEL_EXCLUDED_PROPS, @@ -45,39 +44,23 @@ type WidgetOptions = { format?: ColorFormat } & Record const props = defineProps<{ widget: SimplifiedWidget - modelValue: string }>() -const emit = defineEmits<{ - 'update:modelValue': [value: string] -}>() +const modelValue = props.widget.value() const format = computed(() => { const optionFormat = props.widget.options?.format return isColorFormat(optionFormat) ? optionFormat : 'hex' }) -type PickerValue = string | HSB -const localValue = ref( - toHexFromFormat( - props.modelValue || '#000000', - isColorFormat(props.widget.options?.format) - ? props.widget.options.format - : 'hex' - ) -) - -watch( - () => props.modelValue, - (newVal) => { - localValue.value = toHexFromFormat(newVal || '#000000', format.value) +const localValue = computed({ + get() { + return toHexFromFormat(modelValue.value || '#000000', format.value) + }, + set(v) { + modelValue.value = toHexFromFormat(v, format.value) } -) - -function onPickerUpdate(val: unknown) { - localValue.value = val as PickerValue - emit('update:modelValue', toHexFromFormat(val, format.value)) -} +}) // ColorPicker specific excluded props include panel/overlay classes const COLOR_PICKER_EXCLUDED_PROPS = [...PANEL_EXCLUDED_PROPS] as const diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.ts b/src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.ts index f730efd35..c06575665 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.ts +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberInput.test.ts @@ -51,7 +51,7 @@ describe('WidgetInputNumberInput Value Binding', () => { expect(input.value).toBe('42') }) - it('emits update:modelValue when value changes', async () => { + it('triggers callback when value changes', async () => { const callback = vi.fn() const widget = createMockWidget(10, 'int', {}, callback) const wrapper = mountComponent(widget, 10) diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetMarkdown.test.ts b/src/renderer/extensions/vueNodes/widgets/components/WidgetMarkdown.test.ts index 1bac6b678..aa3aad4f0 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetMarkdown.test.ts +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetMarkdown.test.ts @@ -212,7 +212,7 @@ describe('WidgetMarkdown Dual Mode Display', () => { }) describe('Value Updates', () => { - it('emits update:modelValue when textarea content changes', async () => { + it('triggers callback when textarea content changes', async () => { const callback = vi.fn() const widget = createMockWidget('# Original', {}, callback) const wrapper = mountComponent(widget, '# Original') diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.test.ts b/src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.test.ts index 9ff644fa3..92ede8880 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.test.ts +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetTextarea.test.ts @@ -61,7 +61,7 @@ async function setTextareaValueAndTrigger( describe('WidgetTextarea Value Binding', () => { describe('Widget Value Callbacks', () => { - it('emits Vue event when textarea value changes on input', async () => { + it('triggers callback when textarea value changes on input', async () => { const callback = vi.fn() const widget = createMockWidget('initial', {}, callback) const wrapper = mountComponent(widget, 'initial')