From 31ae2bb826a5a6d2afe7944fedc2fcb1f8e25eed Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 2 Mar 2026 22:18:53 +0500 Subject: [PATCH 1/3] feat(dashboard): allow clearing instanceType field in VMInstance form Update openapi-k8s-toolkit to release/1.4.0 (d6b9e4ad). The previous value-binding layout refactor is already included upstream, so drop the formlistinput-value-binding.diff patch. Add formlistinput-allow-empty.diff patch which introduces two props to the listInput component: - allowEmpty: when set, auto-persists the field so BFF sends an empty value instead of falling back to the schema default - persistType: controls the type of empty value ('str' | 'number' | 'arr' | 'obj'), allowing the feature to work correctly for any field type Set allowEmpty: true on the VMInstance instanceType field so users can explicitly clear the selection and override the default instance type. Co-Authored-By: Claude Signed-off-by: Kirill Ilin (cherry picked from commit a3ccb4f87d9b89bbbf791183720f7507ee72e276) --- .../patches/formlistinput-allow-empty.diff | 37 ++++++++++++++ .../patches/formlistinput-value-binding.diff | 49 ------------------- 2 files changed, 37 insertions(+), 49 deletions(-) create mode 100644 packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-allow-empty.diff delete mode 100644 packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-value-binding.diff diff --git a/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-allow-empty.diff b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-allow-empty.diff new file mode 100644 index 00000000..1c83df7c --- /dev/null +++ b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-allow-empty.diff @@ -0,0 +1,37 @@ +diff --git a/src/localTypes/formExtensions.ts b/src/localTypes/formExtensions.ts +--- a/src/localTypes/formExtensions.ts ++++ b/src/localTypes/formExtensions.ts +@@ -59,2 +59,4 @@ + relatedValuePath?: string ++ allowEmpty?: boolean ++ persistType?: 'str' | 'number' | 'arr' | 'obj' + } +diff --git a/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx b/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx +--- a/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx ++++ b/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx +@@ -149,3 +149,10 @@ + }, [relatedPath, form, arrName, fixedName, relatedFieldValue, onValuesChangeCallBack, isTouchedPeristed]) + ++ // When allowEmpty is set, auto-persist the field so the BFF preserves empty values ++ useEffect(() => { ++ if (customProps.allowEmpty) { ++ persistedControls.onPersistMark(persistName || name, customProps.persistType ?? 'str') ++ } ++ }, [customProps.allowEmpty, customProps.persistType, persistedControls, persistName, name]) ++ + const uri = prepareTemplate({ +@@ -267,5 +274,14 @@ + validateTrigger="onBlur" + hasFeedback={designNewLayout ? { icons: feedbackIcons } : true} + style={{ flex: 1 }} ++ normalize={(value: unknown) => { ++ if (customProps.allowEmpty && (value === undefined || value === null)) { ++ if (customProps.persistType === 'number') return 0 ++ if (customProps.persistType === 'arr') return [] ++ if (customProps.persistType === 'obj') return {} ++ return '' ++ } ++ return value ++ }} + > + = ({ - showSearch - style={{ width: '100%' }} - /> -- {relatedValueTooltip && ( -- -- -- -- )} -- -- -+ -+ {relatedValueTooltip && ( -+ -+ -+ -+ )} -+ - - ) - } From 7cea11e57bcbf67f39006e1282dd93e388f34097 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 2 Mar 2026 22:19:54 +0500 Subject: [PATCH 2/3] feat(dashboard): set allowEmpty on instanceType and update openapi-ui toolkit Update openapi-k8s-toolkit commit to d6b9e4ad (release/1.4.0) which includes the FormListInput layout refactor, making formlistinput-value-binding.diff obsolete. Set allowEmpty: true on the VMInstance instanceType field so users can explicitly clear the selection and override the default instance type. Co-Authored-By: Claude Signed-off-by: Kirill Ilin (cherry picked from commit 6e8ce65e495fadd7dd260fa076acc4727672d0e1) --- internal/controller/dashboard/customformsoverride.go | 1 + internal/controller/dashboard/customformsoverride_test.go | 4 ++++ packages/system/dashboard/images/openapi-ui/Dockerfile | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/controller/dashboard/customformsoverride.go b/internal/controller/dashboard/customformsoverride.go index 67b131cf..cfacb2fa 100644 --- a/internal/controller/dashboard/customformsoverride.go +++ b/internal/controller/dashboard/customformsoverride.go @@ -195,6 +195,7 @@ func applyListInputOverrides(schema map[string]any, kind string, openAPIProps ma "valueUri": "/api/clusters/{cluster}/k8s/apis/instancetype.kubevirt.io/v1beta1/virtualmachineclusterinstancetypes", "keysToValue": []any{"metadata", "name"}, "keysToLabel": []any{"metadata", "name"}, + "allowEmpty": true, }, } if prop, _ := openAPIProps["instanceType"].(map[string]any); prop != nil { diff --git a/internal/controller/dashboard/customformsoverride_test.go b/internal/controller/dashboard/customformsoverride_test.go index 37da1244..76fc4f83 100644 --- a/internal/controller/dashboard/customformsoverride_test.go +++ b/internal/controller/dashboard/customformsoverride_test.go @@ -202,6 +202,10 @@ func TestApplyListInputOverrides_VMInstance(t *testing.T) { t.Errorf("expected valueUri %s, got %v", expectedURI, customProps["valueUri"]) } + if customProps["allowEmpty"] != true { + t.Errorf("expected allowEmpty true, got %v", customProps["allowEmpty"]) + } + // Check disks[].name is a listInput disks, ok := specProps["disks"].(map[string]any) if !ok { diff --git a/packages/system/dashboard/images/openapi-ui/Dockerfile b/packages/system/dashboard/images/openapi-ui/Dockerfile index 4b80c960..82cc6db0 100644 --- a/packages/system/dashboard/images/openapi-ui/Dockerfile +++ b/packages/system/dashboard/images/openapi-ui/Dockerfile @@ -6,7 +6,7 @@ FROM node:${NODE_VERSION}-alpine AS openapi-k8s-toolkit-builder RUN apk add git WORKDIR /src # release/1.4.0 -ARG COMMIT=c67029cc7b7495c65ee0406033576e773a73bb01 +ARG COMMIT=d6b9e4ad0d1eb9d3730f7f0c664792c8dda3214d RUN wget -O- https://github.com/PRO-Robotech/openapi-k8s-toolkit/archive/${COMMIT}.tar.gz | tar -xzvf- --strip-components=1 COPY openapi-k8s-toolkit/patches /patches From 0fefaa246f5a369acbf608b27057768e5bc6cd25 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 2 Mar 2026 22:36:01 +0500 Subject: [PATCH 3/3] fix(dashboard): preserve newlines when copying secrets with CMD+C Add onCopy handler to SecretBase64Plain inputs to intercept native browser copy events and explicitly write the full decoded text (including newlines) to the clipboard. Without this, input[type=text] strips newlines on copy. Upstream PR: https://github.com/PRO-Robotech/openapi-k8s-toolkit/pull/339 Co-Authored-By: Claude Signed-off-by: Kirill Ilin (cherry picked from commit 99ee0e34bf71024e041d2f96e8bf0d0b6223508e) --- .../secret-copy-preserve-newlines.diff | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/secret-copy-preserve-newlines.diff diff --git a/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/secret-copy-preserve-newlines.diff b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/secret-copy-preserve-newlines.diff new file mode 100644 index 00000000..7bce6c25 --- /dev/null +++ b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/secret-copy-preserve-newlines.diff @@ -0,0 +1,29 @@ +diff --git a/src/components/organisms/DynamicComponents/molecules/SecretBase64Plain/SecretBase64Plain.tsx b/src/components/organisms/DynamicComponents/molecules/SecretBase64Plain/SecretBase64Plain.tsx +--- a/src/components/organisms/DynamicComponents/molecules/SecretBase64Plain/SecretBase64Plain.tsx ++++ b/src/components/organisms/DynamicComponents/molecules/SecretBase64Plain/SecretBase64Plain.tsx +@@ -145,6 +145,12 @@ + handleInputClick(e, effectiveHidden, value)} ++ onCopy={e => { ++ if (!effectiveHidden) { ++ e.preventDefault() ++ e.clipboardData?.setData('text/plain', value) ++ } ++ }} + value={shownValue} + readOnly + /> +@@ -161,6 +167,12 @@ + handleInputClick(e, effectiveHidden, value)} ++ onCopy={e => { ++ if (!effectiveHidden) { ++ e.preventDefault() ++ e.clipboardData?.setData('text/plain', value) ++ } ++ }} + value={shownValue} + readOnly + />