From b604e37241f20c890d6de7e29d71b3f26b69694d Mon Sep 17 00:00:00 2001 From: rawagner Date: Thu, 6 Aug 2026 11:07:37 +0200 Subject: [PATCH] NO-ISSUE: Replace LabeledResourceRef with plain values in Select/MultiSelect fields SelectField and MultiSelectField stored { value, label } objects (LabeledResourceRef) in Formik state. This coupled display concerns into form values, complicated payloads, and required unwrapping everywhere. Simplify to store plain string/number values instead and derive display labels from the options list at render time. - Remove labeledResourceRef.ts and labeledResourceRefSchema.ts - Update SelectField/MultiSelectField to use primitive Formik values - Move review step rendering into per-adapter components (VmReviewStep, ClusterReviewStep, BareMetalReviewStep) that fetch display data directly, replacing the generic ReviewStep that relied on labeled refs - Simplify form schemas and payload builders to work with plain values - Add get handlers to mock transport for InstanceTypes, VirtualNetworks, and Subnets to support review step data fetching in tests Assisted-by: Claude Code --- libs/i18n/locales/en/translation.json | 12 +- .../src/components/Form/MultiSelectField.tsx | 16 +- .../src/components/Form/SelectField.test.tsx | 24 ++- .../src/components/Form/SelectField.tsx | 42 ++-- .../src/components/Form/labeledResourceRef.ts | 22 --- .../Form/labeledResourceRefSchema.ts | 20 -- .../CatalogProvisionWizard.tsx | 21 +- .../bareMetalInstance/BareMetalReviewStep.tsx | 52 +++++ .../adapters/bareMetalInstanceAdapter.ts | 27 +-- .../cluster/ClusterConfigurationStep.test.tsx | 2 +- .../cluster/ClusterNodeSetsArrayField.tsx | 4 +- .../adapters/cluster/ClusterReviewStep.tsx | 134 +++++++++++++ .../wizard/adapters/cluster/fields.ts | 6 +- .../wizard/adapters/cluster/payload.test.ts | 10 +- .../wizard/adapters/cluster/payload.ts | 2 +- .../wizard/adapters/cluster/schemas.test.ts | 6 +- .../wizard/adapters/cluster/schemas.ts | 7 +- .../wizard/adapters/clusterAdapter.ts | 88 +-------- .../computeInstance/VmNetworkingStep.tsx | 5 +- .../adapters/computeInstance/VmReviewStep.tsx | 180 ++++++++++++++++++ .../wizard/adapters/computeInstance/fields.ts | 13 +- .../computeInstance/generalFields.test.ts | 18 +- .../adapters/computeInstance/payload.ts | 16 +- .../adapters/computeInstance/schemas.test.ts | 9 +- .../adapters/computeInstance/schemas.ts | 20 +- .../wizard/adapters/computeInstanceAdapter.ts | 83 +------- .../catalogProvision/wizard/adapters/types.ts | 3 +- .../catalogProvision/wizard/catalogOverlay.ts | 10 - .../catalogProvision/wizard/stepIds.ts | 24 +-- .../wizard/steps/ReviewStep.tsx | 46 ----- .../wizard/steps/WizardSteps.tsx | 1 - .../networking/SecurityGroupCreateModal.tsx | 16 +- .../networking/SecurityGroupRuleForm.tsx | 15 +- .../networking/SecurityGroupRuleModal.tsx | 17 +- .../vm/DetailsPage/AttachExternalIpModal.tsx | 9 +- .../test-utils/createMockConnectTransport.ts | 9 + 36 files changed, 506 insertions(+), 483 deletions(-) delete mode 100644 libs/ui-components/src/components/Form/labeledResourceRef.ts delete mode 100644 libs/ui-components/src/components/Form/labeledResourceRefSchema.ts create mode 100644 libs/ui-components/src/components/catalogProvision/wizard/adapters/bareMetalInstance/BareMetalReviewStep.tsx create mode 100644 libs/ui-components/src/components/catalogProvision/wizard/adapters/cluster/ClusterReviewStep.tsx create mode 100644 libs/ui-components/src/components/catalogProvision/wizard/adapters/computeInstance/VmReviewStep.tsx delete mode 100644 libs/ui-components/src/components/catalogProvision/wizard/steps/ReviewStep.tsx diff --git a/libs/i18n/locales/en/translation.json b/libs/i18n/locales/en/translation.json index cefab47f..e88a2c4a 100644 --- a/libs/i18n/locales/en/translation.json +++ b/libs/i18n/locales/en/translation.json @@ -25,6 +25,7 @@ "Bare metal instances": "Bare metal instances", "Bare Metal Machines": "Bare Metal Machines", "Bare metal provisioning wizard": "Bare metal provisioning wizard", + "Boot disk": "Boot disk", "Break-glass credentials": "Break-glass credentials", "Browse catalog items and launch virtual machines, clusters, or bare metal machines from published offerings.": "Browse catalog items and launch virtual machines, clusters, or bare metal machines from published offerings.", "Cancel": "Cancel", @@ -54,11 +55,7 @@ "catalogProvision.instanceTypes.deprecatedSuffix": " (deprecated)", "catalogProvision.instanceTypes.loadError": "Could not load instance types", "catalogProvision.networking.loadError": "Could not load networking options", - "catalogProvision.review.catalogItem": "Catalog item", "catalogProvision.steps.catalog.title": "Catalog item", - "catalogProvision.steps.configuration.title": "Configuration", - "catalogProvision.steps.general.title": "General", - "catalogProvision.steps.networking.title": "Networking", "catalogProvision.steps.review.title": "Review", "catalogProvision.validation.bootDiskNumber": "Boot disk size must be a number", "catalogProvision.validation.catalogItemRequired": "Select a catalog item", @@ -194,7 +191,12 @@ "Failed to download kubeconfig": "Failed to download kubeconfig", "Failed to edit resource": "Failed to edit resource", "Failed to enable Identity provider": "Failed to enable Identity provider", + "Failed to fetch host types": "Failed to fetch host types", "Failed to fetch Identity provider": "Failed to fetch Identity provider", + "Failed to fetch instance type": "Failed to fetch instance type", + "Failed to fetch security groups": "Failed to fetch security groups", + "Failed to fetch subnet": "Failed to fetch subnet", + "Failed to fetch virtual network": "Failed to fetch virtual network", "Failed to load cluster password": "Failed to load cluster password", "Failed to load graphical console viewer": "Failed to load graphical console viewer", "Failed to load security groups": "Failed to load security groups", @@ -388,6 +390,7 @@ "Use IPv4 CIDR notation (for example 10.128.0.0/14).": "Use IPv4 CIDR notation (for example 10.128.0.0/14).", "Use IPv4 CIDR notation (for example 172.30.0.0/16).": "Use IPv4 CIDR notation (for example 172.30.0.0/16).", "User data": "User data", + "User Data": "User Data", "User Data is required": "User Data is required", "User data must not exceed 64 KB.": "User data must not exceed 64 KB.", "User info URL": "User info URL", @@ -402,6 +405,7 @@ "Virtual Network": "Virtual Network", "Virtual network is required": "Virtual network is required", "Virtual networks": "Virtual networks", + "VM image": "VM image", "Worker nodes": "Worker nodes", "You are not authorized to access this resource.": "You are not authorized to access this resource." } diff --git a/libs/ui-components/src/components/Form/MultiSelectField.tsx b/libs/ui-components/src/components/Form/MultiSelectField.tsx index 43c2e706..d61909c4 100644 --- a/libs/ui-components/src/components/Form/MultiSelectField.tsx +++ b/libs/ui-components/src/components/Form/MultiSelectField.tsx @@ -6,7 +6,6 @@ import { useField } from 'formik'; import { getVisibleFieldError } from './fieldError'; import { useShowFieldValidationErrors } from './FieldValidationContext'; import { FormFieldHelper } from './FormFieldHelper'; -import { type LabeledResourceRef } from './labeledResourceRef'; import type { SelectFieldOption } from './SelectField'; interface MultiSelectFieldProps { @@ -37,7 +36,7 @@ export const MultiSelectField = ({ noOptionsFoundMessage = (filter) => `No options found for "${filter}"`, autoSelectSingleOption = false, }: MultiSelectFieldProps) => { - const [field, meta, helpers] = useField(name); + const [field, meta, helpers] = useField<(string | number)[]>(name); const showValidationErrors = useShowFieldValidationErrors(); const error = getVisibleFieldError(meta, showValidationErrors); const validated = error ? 'error' : 'default'; @@ -59,25 +58,18 @@ export const MultiSelectField = ({ ) { return; } - void helpers.setValue([{ value: options[0].value, label: options[0].label }], false); + void helpers.setValue([options[0].value], false); }, [autoSelectSingleOption, helpers, isDisabled, isLoading, options, selectedValues.length]); const initialOptions = useMemo(() => { return options.map((option) => ({ content: option.label, value: option.value, - selected: selectedValues.some((value) => value.value === option.value), + selected: selectedValues.some((value) => value === option.value), isDisabled: option.isDisabled, })); }, [options, selectedValues]); - const toLabeledResourceRefs = (selections: (string | number)[]) => - selections.map((selection) => { - const value = String(selection); - const option = options.find((entry) => entry.value === value); - return option ? { value: option.value, label: option.label } : { value, label: value }; - }); - return ( { - void helpers.setValue(toLabeledResourceRefs(selections), true); + void helpers.setValue(selections, true); void helpers.setTouched(true); }} onToggle={(open) => { diff --git a/libs/ui-components/src/components/Form/SelectField.test.tsx b/libs/ui-components/src/components/Form/SelectField.test.tsx index 72472499..3645e762 100644 --- a/libs/ui-components/src/components/Form/SelectField.test.tsx +++ b/libs/ui-components/src/components/Form/SelectField.test.tsx @@ -4,26 +4,24 @@ import { Formik, type FormikErrors } from 'formik'; import { describe, expect, it } from 'vitest'; import * as yup from 'yup'; -import { EMPTY_LABELED_RESOURCE_REF, type LabeledResourceRef } from './labeledResourceRef'; -import { labeledResourceRefSchema } from './labeledResourceRefSchema'; import { SelectField } from './SelectField'; const renderSelect = ({ autoSelectSingleOption = false, - initialValue = EMPTY_LABELED_RESOURCE_REF, + initialValue = '', isLoading = false, }: { autoSelectSingleOption?: boolean; - initialValue?: LabeledResourceRef; + initialValue?: string; isLoading?: boolean; } = {}) => { - let latestErrors: FormikErrors<{ kind: LabeledResourceRef }> = {}; + let latestErrors: FormikErrors<{ kind: string }> = {}; render( undefined} > @@ -39,7 +37,7 @@ const renderSelect = ({ placeholder="Select a kind" options={[{ value: 'only-option', label: 'Only option label' }]} /> - {values.kind.value || '(empty)'} + {values.kind || '(empty)'}