Compare commits

...

1 Commits

Author SHA1 Message Date
bymyself
660ccb9236 fix: promoted textarea widgets in subgraphs no longer permanently read-only
Skip disabled override for promoted widgets whose internal slot is
linked to SubgraphInput. Extract isPromotedOnOwningNode variable
and reuse it for the existing borderStyle check.

Amp-Thread-ID: https://ampcode.com/threads/T-019c88b8-a377-754b-b624-db394d59f5b5
2026-02-23 04:19:57 +00:00
2 changed files with 41 additions and 18 deletions

View File

@@ -192,19 +192,23 @@ const processedWidgets = computed((): ProcessedWidget[] => {
// Get value from store (falls back to undefined if not registered) // Get value from store (falls back to undefined if not registered)
const value = widgetState?.value as WidgetValue const value = widgetState?.value as WidgetValue
// Build options from store state, with slot-linked override for disabled // Build options from store state, with slot-linked override for disabled.
// Promoted widgets inside a subgraph are always linked to SubgraphInput,
// but should remain interactive — skip the disabled override for them.
const storeOptions = widgetState?.options ?? {} const storeOptions = widgetState?.options ?? {}
const widgetOptions = slotMetadata?.linked const isPromotedOnOwningNode =
? { ...storeOptions, disabled: true } widgetState?.promoted && String(widgetState?.nodeId) === String(nodeId)
: storeOptions const widgetOptions =
slotMetadata?.linked && !isPromotedOnOwningNode
? { ...storeOptions, disabled: true }
: storeOptions
// Derive border style from store metadata // Derive border style from store metadata
const borderStyle = const borderStyle = isPromotedOnOwningNode
widgetState?.promoted && String(widgetState?.nodeId) === String(nodeId) ? 'ring ring-component-node-widget-promoted'
? 'ring ring-component-node-widget-promoted' : widget.options?.advanced
: widget.options?.advanced ? 'ring ring-component-node-widget-advanced'
? 'ring ring-component-node-widget-advanced' : undefined
: undefined
const simplified: SimplifiedWidget = { const simplified: SimplifiedWidget = {
name: widget.name, name: widget.name,

View File

@@ -22,14 +22,12 @@ function createMockWidget(
function mountComponent( function mountComponent(
widget: SimplifiedWidget<string>, widget: SimplifiedWidget<string>,
modelValue: string, modelValue: string,
readonly = false,
placeholder?: string placeholder?: string
) { ) {
return mount(WidgetTextarea, { return mount(WidgetTextarea, {
props: { props: {
widget, widget,
modelValue, modelValue,
readonly,
placeholder placeholder
} }
}) })
@@ -179,18 +177,39 @@ describe('WidgetTextarea Value Binding', () => {
it('uses provided placeholder when specified', () => { it('uses provided placeholder when specified', () => {
const widget = createMockWidget('test') const widget = createMockWidget('test')
const wrapper = mountComponent( const wrapper = mountComponent(widget, 'test', 'Custom placeholder')
widget,
'test',
false,
'Custom placeholder'
)
const textarea = wrapper.find('textarea') const textarea = wrapper.find('textarea')
expect(textarea.attributes('placeholder')).toBe('Custom placeholder') expect(textarea.attributes('placeholder')).toBe('Custom placeholder')
}) })
}) })
describe('Read-Only Behavior', () => {
it('is readonly when options.read_only is true', () => {
const widget = createMockWidget('test', { read_only: true })
const wrapper = mountComponent(widget, 'test')
expect(wrapper.find('textarea').attributes('readonly')).toBeDefined()
})
it('is readonly when options.disabled is true', () => {
const widget = createMockWidget('test', { disabled: true })
const wrapper = mountComponent(widget, 'test')
expect(wrapper.find('textarea').attributes('readonly')).toBeDefined()
})
it('is editable when neither read_only nor disabled is set', () => {
const widget = createMockWidget('test', {})
const wrapper = mountComponent(widget, 'test')
expect(wrapper.find('textarea').attributes('readonly')).toBeUndefined()
})
it('is editable when disabled is explicitly false', () => {
const widget = createMockWidget('test', { disabled: false })
const wrapper = mountComponent(widget, 'test')
expect(wrapper.find('textarea').attributes('readonly')).toBeUndefined()
})
})
describe('Edge Cases', () => { describe('Edge Cases', () => {
it('handles very long text', async () => { it('handles very long text', async () => {
const widget = createMockWidget('short') const widget = createMockWidget('short')