From 1054503b4ec8402ffe93a28712a8bf026027fa7f Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:47:27 +0000 Subject: [PATCH] Add support for values factory function in widget select combo (#8775) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Adds support for values factory functions, e.g. ``` this.addWidget( "combo", "Dynamic", "", (e) => { }, { values: () => { return getSomeValuesHere() } } ) ``` Specifically for fixing KJNodes get/set ## Changes - **What**: Check if object is a function, if so, calls it. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8775-Add-support-for-values-factory-function-in-widget-select-combo-3036d73d3650819bb4e4f9181445cb1d) by [Unito](https://www.unito.io) --- .../components/WidgetSelectDefault.test.ts | 108 ++++++++++++++++++ .../components/WidgetSelectDefault.vue | 27 +++-- 2 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.test.ts diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.test.ts b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.test.ts new file mode 100644 index 0000000000..52121b8164 --- /dev/null +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.test.ts @@ -0,0 +1,108 @@ +import { mount } from '@vue/test-utils' +import { nextTick } from 'vue' +import PrimeVue from 'primevue/config' +import { describe, expect, it } from 'vitest' + +import SelectPlus from '@/components/primevueOverride/SelectPlus.vue' +import type { SimplifiedWidget } from '@/types/simplifiedWidget' + +import WidgetSelectDefault from './WidgetSelectDefault.vue' + +describe('WidgetSelectDefault', () => { + const createWidget = ( + values: unknown + ): SimplifiedWidget => ({ + name: 'test_combo', + type: 'combo', + value: undefined, + options: { values } as SimplifiedWidget['options'] + }) + + const mountComponent = (widget: SimplifiedWidget) => + mount(WidgetSelectDefault, { + props: { widget }, + global: { + plugins: [PrimeVue], + components: { SelectPlus } + } + }) + + describe('array-valued options', () => { + it('resolves options from a plain array', () => { + const widget = createWidget(['a', 'b', 'c']) + const wrapper = mountComponent(widget) + + expect(wrapper.findComponent(SelectPlus).props('options')).toEqual([ + 'a', + 'b', + 'c' + ]) + }) + + it('reactively updates when widget prop changes', async () => { + const widget = createWidget(['x', 'y']) + const wrapper = mountComponent(widget) + + await wrapper.setProps({ widget: createWidget(['x', 'y', 'z']) }) + + expect(wrapper.findComponent(SelectPlus).props('options')).toEqual([ + 'x', + 'y', + 'z' + ]) + }) + }) + + describe('undefined/empty options', () => { + it('returns empty array when values is undefined', () => { + const widget = createWidget(undefined) + const wrapper = mountComponent(widget) + + expect(wrapper.findComponent(SelectPlus).props('options')).toEqual([]) + }) + }) + + describe('function-valued options', () => { + it('resolves options from a function', () => { + const widget = createWidget(() => ['a', 'b', 'c']) + const wrapper = mountComponent(widget) + + expect(wrapper.findComponent(SelectPlus).props('options')).toEqual([ + 'a', + 'b', + 'c' + ]) + }) + + it('re-evaluates function on show event', async () => { + let items = ['x', 'y'] + const widget = createWidget(() => items) + const wrapper = mountComponent(widget) + + items = ['x', 'y', 'z'] + wrapper.findComponent(SelectPlus).vm.$emit('show') + await nextTick() + + expect(wrapper.findComponent(SelectPlus).props('options')).toEqual([ + 'x', + 'y', + 'z' + ]) + }) + + it('re-evaluates function on filter event', async () => { + let items = ['a'] + const widget = createWidget(() => items) + const wrapper = mountComponent(widget) + + items = ['a', 'b'] + wrapper.findComponent(SelectPlus).vm.$emit('filter') + await nextTick() + + expect(wrapper.findComponent(SelectPlus).props('options')).toEqual([ + 'a', + 'b' + ]) + }) + }) +}) diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.vue index 2803b57708..2fdf328cd2 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetSelectDefault.vue @@ -17,6 +17,8 @@ overlay: 'w-fit min-w-full' }" data-capture-wheel="true" + @show="refreshOptions" + @filter="refreshOptions" >