diff --git a/changelog/8197-multi-select-taxonomy-custom-fields.yaml b/changelog/8197-multi-select-taxonomy-custom-fields.yaml new file mode 100644 index 00000000000..25f8b12f80b --- /dev/null +++ b/changelog/8197-multi-select-taxonomy-custom-fields.yaml @@ -0,0 +1,4 @@ +type: Added +description: Support multi-select taxonomy custom fields via the "taxonomy_key[]" field_type convention — frontend now renders a multi-select dropdown for [] fields on taxonomy detail pages +pr: 8197 +labels: [] diff --git a/clients/admin-ui/src/features/common/custom-fields/hooks.ts b/clients/admin-ui/src/features/common/custom-fields/hooks.ts index 617b1f1ed1b..39ded10dacf 100644 --- a/clients/admin-ui/src/features/common/custom-fields/hooks.ts +++ b/clients/admin-ui/src/features/common/custom-fields/hooks.ts @@ -110,7 +110,8 @@ export const useCustomFields = ({ activeCustomFieldDefinition.forEach((value) => { const customField = definitionIdToCustomField.get(value.id || ""); if (customField) { - if (!!value.allow_list_id && value.field_type === "string[]") { + // Preserve array values for any multi-value field_type (legacy "string[]" or taxonomy "risk[]") + if (value.field_type.endsWith("[]")) { values[customField.custom_field_definition_id] = customField.value; } else { values[customField.custom_field_definition_id] = diff --git a/clients/admin-ui/src/features/custom-fields/CustomFieldForm.tsx b/clients/admin-ui/src/features/custom-fields/CustomFieldForm.tsx index 9b252ace0fb..d265c003f04 100644 --- a/clients/admin-ui/src/features/custom-fields/CustomFieldForm.tsx +++ b/clients/admin-ui/src/features/custom-fields/CustomFieldForm.tsx @@ -79,6 +79,8 @@ const CustomFieldForm = ({ const [form] = Form.useForm(); const valueType = Form.useWatch("value_type", form); const selectedTemplate = Form.useWatch("template", form); + const isTaxonomyTemplate = + !!selectedTemplate && selectedTemplate !== CUSTOM_TEMPLATE_VALUE; const router = useRouter(); const { resource_type: queryResourceType } = router.query; @@ -185,17 +187,26 @@ const CustomFieldForm = ({ return undefined; } const fieldType = getCustomFieldType(field); - const template = + const isCustomTemplate = fieldType === FieldTypes.OPEN_TEXT || fieldType === FieldTypes.SINGLE_SELECT || - fieldType === FieldTypes.MULTIPLE_SELECT - ? CUSTOM_TEMPLATE_VALUE - : undefined; + fieldType === FieldTypes.MULTIPLE_SELECT; + + // Detect taxonomy multi-select: field_type ends with "[]" and is not a standard type + const isTaxonomyMulti = + !isCustomTemplate && field.field_type.endsWith("[]"); + const taxonomyBaseKey = isTaxonomyMulti + ? field.field_type.slice(0, -2) + : field.field_type; + + const template = isCustomTemplate ? CUSTOM_TEMPLATE_VALUE : taxonomyBaseKey; + return { ...field, - value_type: field.field_type, + value_type: taxonomyBaseKey, template, field_type: fieldType, + selection_mode: isTaxonomyMulti ? "multiple" : "single", resource_type: parseResourceType(field.resource_type), options: allowList?.allowed_values ?? [], }; @@ -205,10 +216,11 @@ const CustomFieldForm = ({ const initialValues = queryResourceType ? { + selection_mode: "single" as const, ...defaultInitialValues, resource_type: `taxonomy:${queryResourceType}`, } - : defaultInitialValues; + : { selection_mode: "single" as const, ...defaultInitialValues }; if (isLoading || isAllowListLoading || isLocationsLoading) { return ; @@ -256,8 +268,10 @@ const CustomFieldForm = ({ onChange={(value) => { if (value === CUSTOM_TEMPLATE_VALUE) { form.setFieldValue("value_type", undefined); + form.setFieldValue("selection_mode", undefined); } else { form.setFieldValue("value_type", value); + form.setFieldValue("selection_mode", "single"); } }} data-testid="select-template" @@ -277,6 +291,27 @@ const CustomFieldForm = ({ /> + {isTaxonomyTemplate && ( + +