diff --git a/changelog/8234-policy-match-values-chips.yaml b/changelog/8234-policy-match-values-chips.yaml new file mode 100644 index 00000000000..86b80151985 --- /dev/null +++ b/changelog/8234-policy-match-values-chips.yaml @@ -0,0 +1,4 @@ +type: Changed +description: Improved UI/UX for selecting many taxonomy values in a policy match block +pr: 8234 +labels: [] diff --git a/clients/admin-ui/src/features/access-policies/AccessPolicyEditor.tsx b/clients/admin-ui/src/features/access-policies/AccessPolicyEditor.tsx index 625755b1970..d4e08451c9e 100644 --- a/clients/admin-ui/src/features/access-policies/AccessPolicyEditor.tsx +++ b/clients/admin-ui/src/features/access-policies/AccessPolicyEditor.tsx @@ -623,11 +623,20 @@ const PolicyCanvasPanel = (props: PolicyCanvasPanelProps) => { const nodeSizes = useMemo(() => { const sizes: Record = {}; nodes.forEach((n) => { - if (n.type === "conditionNode") { - sizes[n.id] = { width: 320, height: 310 }; - } else if (n.type === "constraintNode") { - sizes[n.id] = { width: 320, height: 380 }; + if (n.type !== "conditionNode" && n.type !== "constraintNode") { + return; } + // Once React Flow has measured the node, use the real DOM height so + // condition nodes that grow vertically (e.g. a chip list of many + // selected values) don't overlap the next match node below. The + // fallback covers the first paint before measurement is available. + const measuredHeight = (n as Node & { measured?: { height?: number } }) + .measured?.height; + const fallbackHeight = n.type === "conditionNode" ? 310 : 380; + sizes[n.id] = { + width: 320, + height: measuredHeight ?? fallbackHeight, + }; }); return sizes; }, [nodes]); diff --git a/clients/admin-ui/src/features/access-policies/ConditionValuesField.tsx b/clients/admin-ui/src/features/access-policies/ConditionValuesField.tsx new file mode 100644 index 00000000000..a3a517f15dd --- /dev/null +++ b/clients/admin-ui/src/features/access-policies/ConditionValuesField.tsx @@ -0,0 +1,194 @@ +import { Flex, Select, SelectProps, Tag, Text } from "fidesui"; +import { ReactNode, useState } from "react"; + +import { useAppSelector } from "~/app/hooks"; +import DataCategorySelect from "~/features/common/dropdown/DataCategorySelect"; +import DataSubjectSelect from "~/features/common/dropdown/DataSubjectSelect"; +import DataUseSelect from "~/features/common/dropdown/DataUseSelect"; +import SystemGroupSelect from "~/features/common/dropdown/SystemGroupSelect"; +import useTaxonomies from "~/features/common/hooks/useTaxonomies"; +import { + selectSystemGroupsAsTaxonomyEntities, + useGetAllSystemGroupsQuery, +} from "~/features/system/system-groups.slice"; +import CustomTaxonomySelect from "~/features/taxonomy/components/CustomTaxonomySelect"; +import { useGetTaxonomyQuery } from "~/features/taxonomy/taxonomy.slice"; + +import { BUILT_IN_TAXONOMY_KEYS, ConditionProperty } from "./types"; + +interface ConditionValuesFieldProps { + property: string | undefined; + values: string[] | undefined; + onChange: (values: string[]) => void; +} + +const ConditionValuesField = ({ + property, + values, + onChange, +}: ConditionValuesFieldProps) => { + const [isEditing, setIsEditing] = useState(false); + const { + getDataUseDisplayName, + getDataCategoryDisplayName, + getDataSubjectDisplayName, + } = useTaxonomies(); + + useGetAllSystemGroupsQuery(); + const systemGroups = useAppSelector(selectSystemGroupsAsTaxonomyEntities); + + const isCustomTaxonomy = + !!property && + !BUILT_IN_TAXONOMY_KEYS.includes(property as ConditionProperty); + const { data: customTaxonomyItems = [] } = useGetTaxonomyQuery( + property ?? "", + { skip: !isCustomTaxonomy }, + ); + + if (!property) { + return ( + - ); - } - - const commonProps = { - selectedTaxonomies: values, - value: values, - onChange: (v: unknown) => onChange(v as string[]), - mode: "multiple" as const, - maxTagCount: 1 as const, - }; - - switch (property) { - case ConditionProperty.DATA_USE: - return ( - - ); - case ConditionProperty.DATA_CATEGORIES: - return ( - - ); - case ConditionProperty.DATA_SUBJECTS: - return ( - - ); - case ConditionProperty.SYSTEM_GROUP: - return ( - - ); - default: - return ( - - ); - } -}; - const ConditionNode = ({ data }: NodeProps) => { const { options, labelByKey } = usePolicyTaxonomyOptions(); @@ -200,9 +125,11 @@ const ConditionNode = ({ data }: NodeProps) => { /> - {renderValuesSelect(data.property, data.values, (values) => - data.onValuesChange?.(values), - )} + data.onValuesChange?.(values)} + />