From 8a57d74fb994bb410062693e908ed86144f0a067 Mon Sep 17 00:00:00 2001 From: Lucio Lelii Date: Wed, 9 Sep 2026 12:23:25 +0200 Subject: [PATCH] Say when a flow has no global input to take a field from "From global inputs" was offered on every bindable field, so on a flow that declares none it led to an empty picker and a form that could not be saved - a dead end with nothing explaining it. The choice stays visible, because it is what tells you the possibility exists, but it is disabled and says "(none declared)", and the value field it replaces is hidden while it is selected. Co-Authored-By: Claude Opus 5 (1M context) --- .../nodes/generic-node/generic-node.html | 16 +++++++- .../nodes/generic-node/generic-node.spec.ts | 38 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/app/shared/nodes/generic-node/generic-node.html b/src/app/shared/nodes/generic-node/generic-node.html index 2e61ea5..51dadb1 100644 --- a/src/app/shared/nodes/generic-node/generic-node.html +++ b/src/app/shared/nodes/generic-node/generic-node.html @@ -587,7 +587,14 @@ @if (canTakeGlobalInput()) { - + + } @if (localEditorUseInput) { @@ -620,6 +627,12 @@ } } + + @if (!localEditorFromGlobal) {
@@ -691,6 +704,7 @@ {{ localEditorTip }} }
+ }
diff --git a/src/app/shared/nodes/generic-node/generic-node.spec.ts b/src/app/shared/nodes/generic-node/generic-node.spec.ts index 3318cd5..a005a3c 100644 --- a/src/app/shared/nodes/generic-node/generic-node.spec.ts +++ b/src/app/shared/nodes/generic-node/generic-node.spec.ts @@ -674,6 +674,44 @@ describe('GenericNodeComponent', () => { expect(component.localEditorTip).toBe('Leave empty to take it from a node input.'); }); + it('knows the choice is unusable when the flow declares no global', async () => { + const component = fixture.componentInstance as any; + withGlobals([]); + await openModelEditor(component); + + // Selecting it used to lead to a mode nothing could complete: an empty picker, a hidden + // value, and a Save that stays disabled. The option is left in the list and disabled off + // this, so the capability stays discoverable. + expect(component.canTakeGlobalInput()).toBe(true); + expect(component.localEditorGlobalInputs).toEqual([]); + }); + + it('knows it is usable as soon as the flow has one', async () => { + const component = fixture.componentInstance as any; + withGlobals([{ name: 'modelName', type: 'TEXT', multiple: false }]); + await openModelEditor(component); + + expect(component.canTakeGlobalInput()).toBe(true); + expect(component.localEditorGlobalInputs.map((input: any) => input.name)).toEqual(['modelName']); + }); + + it('cannot be saved from global mode until a global is picked', async () => { + const component = fixture.componentInstance as any; + withGlobals([{ name: 'modelName', type: 'TEXT', multiple: false }]); + await openModelEditor(component); + + component.onLocalEditorSourceModeChange('global'); + // Entering the mode starts from nothing chosen, and the value control is hidden there: the + // reference the picker writes *is* the value, so there is nothing else to fill in. + expect(component.localEditorFromGlobal).toBe(true); + expect(component.localEditorValue).toBe(''); + expect(component.canSaveLocalEditor()).toBe(false); + + component.onLocalEditorGlobalInputChange('modelName'); + expect(component.localEditorValue).toBe('${{global.modelName}}'); + expect(component.canSaveLocalEditor()).toBe(true); + }); + it('labels a global with its type and multiplicity', () => { const component = fixture.componentInstance as any;