diff --git a/clients/admin-ui/src/features/taxonomy/components/TaxonomyInteractiveTree.tsx b/clients/admin-ui/src/features/taxonomy/components/TaxonomyInteractiveTree.tsx index b9aeeedf79..68ac9447b2 100644 --- a/clients/admin-ui/src/features/taxonomy/components/TaxonomyInteractiveTree.tsx +++ b/clients/admin-ui/src/features/taxonomy/components/TaxonomyInteractiveTree.tsx @@ -31,6 +31,8 @@ interface TaxonomyInteractiveTreeProps { taxonomyType: CoreTaxonomiesEnum; taxonomyItems: TaxonomyEntity[]; draftNewItem: Partial | null; + lastCreatedItemKey: string | null; + resetLastCreatedItemKey: () => void; onTaxonomyItemClick: (taxonomyItem: TaxonomyEntity) => void; onAddButtonClick: (taxonomyItem: TaxonomyEntity | undefined) => void; onCancelDraftItem: () => void; @@ -41,6 +43,8 @@ const TaxonomyInteractiveTree = ({ taxonomyType, taxonomyItems, draftNewItem, + lastCreatedItemKey, + resetLastCreatedItemKey, onTaxonomyItemClick, onAddButtonClick, onCancelDraftItem, @@ -87,6 +91,8 @@ const TaxonomyInteractiveTree = ({ onTaxonomyItemClick, onAddButtonClick, hasChildren: false, + isLastCreatedItem: lastCreatedItemKey === taxonomyItem.fides_key, + resetLastCreatedItemKey, }, type: "taxonomyTreeNode", }; diff --git a/clients/admin-ui/src/features/taxonomy/components/TaxonomyTextInputNode.tsx b/clients/admin-ui/src/features/taxonomy/components/TaxonomyTextInputNode.tsx index c05d7655a2..db45929af1 100644 --- a/clients/admin-ui/src/features/taxonomy/components/TaxonomyTextInputNode.tsx +++ b/clients/admin-ui/src/features/taxonomy/components/TaxonomyTextInputNode.tsx @@ -1,7 +1,8 @@ -import { Node, NodeProps, useReactFlow } from "@xyflow/react"; +import { Node, NodeProps } from "@xyflow/react"; import { AntInput, InputRef } from "fidesui"; import { useEffect, useRef, useState } from "react"; +import useCenterScreenOnNode from "../hooks/useCenterScreenOnNode"; import TaxonomyTreeNodeHandle from "./TaxonomyTreeNodeHandle"; export type TextInputNodeType = Node< @@ -23,29 +24,29 @@ const TaxonomyTextInputNode = ({ const [value, setValue] = useState(""); const inputWidth = 200; - const { setCenter, getZoom } = useReactFlow(); + const { centerScreenOnNode } = useCenterScreenOnNode({ + positionAbsoluteX, + positionAbsoluteY, + nodeWidth: inputWidth, + }); // Reset state and autofocus / center screen around the node when it is mounted // or when the parent key changes (it's being added to somewhere else in the tree) useEffect(() => { setValue(""); const focusOnInput = () => - inputRef.current!.focus({ + inputRef.current?.focus({ cursor: "start", preventScroll: true, }); - const centerScreenOnNode = () => - setCenter(positionAbsoluteX + inputWidth / 2, positionAbsoluteY, { - duration: 500, - zoom: getZoom(), - }); + const centerAndFocus = async () => { await centerScreenOnNode(); focusOnInput(); }; centerAndFocus(); - }, [parentKey, getZoom, positionAbsoluteX, positionAbsoluteY, setCenter]); + }, [parentKey, centerScreenOnNode, positionAbsoluteX, positionAbsoluteY]); return (
diff --git a/clients/admin-ui/src/features/taxonomy/components/TaxonomyTreeNode.tsx b/clients/admin-ui/src/features/taxonomy/components/TaxonomyTreeNode.tsx index dd2ee54591..47209a55be 100644 --- a/clients/admin-ui/src/features/taxonomy/components/TaxonomyTreeNode.tsx +++ b/clients/admin-ui/src/features/taxonomy/components/TaxonomyTreeNode.tsx @@ -1,12 +1,13 @@ import { Node, NodeProps } from "@xyflow/react"; import { AntButton, AntTypography, Icons } from "fidesui"; -import { useCallback, useContext } from "react"; +import { useCallback, useContext, useEffect } from "react"; import { TAXONOMY_ROOT_NODE_ID } from "../constants"; import { TaxonomyTreeHoverContext, TreeNodeHoverStatus, } from "../context/TaxonomyTreeHoverContext"; +import useCenterScreenOnNode from "../hooks/useCenterScreenOnNode"; import { TaxonomyEntity } from "../types"; import styles from "./TaxonomyTreeNode.module.scss"; import TaxonomyTreeNodeHandle from "./TaxonomyTreeNodeHandle"; @@ -18,11 +19,17 @@ export type TaxonomyTreeNodeType = Node< onTaxonomyItemClick: (taxonomyItem: TaxonomyEntity) => void | null; onAddButtonClick: (taxonomyItem: TaxonomyEntity | undefined) => void; hasChildren: boolean; + isLastCreatedItem: boolean; + resetLastCreatedItemKey: () => void; }, "taxonomyTreeNode" >; -const TaxonomyTreeNode = ({ data }: NodeProps) => { +const TaxonomyTreeNode = ({ + data, + positionAbsoluteX, + positionAbsoluteY, +}: NodeProps) => { const { onMouseEnter, onMouseLeave, getNodeHoverStatus } = useContext( TaxonomyTreeHoverContext, ); @@ -32,8 +39,34 @@ const TaxonomyTreeNode = ({ data }: NodeProps) => { onTaxonomyItemClick, label, hasChildren, + isLastCreatedItem, + resetLastCreatedItemKey, } = data; + const { centerScreenOnNode } = useCenterScreenOnNode({ + positionAbsoluteX, + positionAbsoluteY, + nodeWidth: 200, + }); + + useEffect(() => { + const centerScreenAndSimulateHover = async () => { + await centerScreenOnNode(); + onMouseEnter(taxonomyItem?.fides_key!); + }; + + if (isLastCreatedItem) { + centerScreenAndSimulateHover(); + resetLastCreatedItemKey(); + } + }, [ + isLastCreatedItem, + onMouseEnter, + taxonomyItem?.fides_key, + resetLastCreatedItemKey, + centerScreenOnNode, + ]); + const nodeHoverStatus = getNodeHoverStatus(taxonomyItem?.fides_key!); const getNodeHoverStatusClass = useCallback(() => { switch (nodeHoverStatus) { diff --git a/clients/admin-ui/src/features/taxonomy/hooks/useCenterScreenOnNode.ts b/clients/admin-ui/src/features/taxonomy/hooks/useCenterScreenOnNode.ts new file mode 100644 index 0000000000..b8a7829076 --- /dev/null +++ b/clients/admin-ui/src/features/taxonomy/hooks/useCenterScreenOnNode.ts @@ -0,0 +1,28 @@ +import { useReactFlow } from "@xyflow/react"; +import { useCallback } from "react"; + +interface UseCenterScreenOnNodeParams { + positionAbsoluteX: number; + positionAbsoluteY: number; + nodeWidth: number; +} + +const useCenterScreenOnNode = ({ + positionAbsoluteX, + positionAbsoluteY, + nodeWidth, +}: UseCenterScreenOnNodeParams) => { + const { setCenter, getZoom } = useReactFlow(); + + const centerScreenOnNode = useCallback( + () => + setCenter(positionAbsoluteX + nodeWidth / 2, positionAbsoluteY, { + duration: 500, + zoom: getZoom(), + }), + [getZoom, positionAbsoluteX, positionAbsoluteY, nodeWidth, setCenter], + ); + + return { centerScreenOnNode }; +}; +export default useCenterScreenOnNode; diff --git a/clients/admin-ui/src/pages/taxonomy/index.tsx b/clients/admin-ui/src/pages/taxonomy/index.tsx index 210d43c364..608c61e7c4 100644 --- a/clients/admin-ui/src/pages/taxonomy/index.tsx +++ b/clients/admin-ui/src/pages/taxonomy/index.tsx @@ -42,9 +42,14 @@ const TaxonomyPage: NextPage = () => { const [draftNewItem, setDraftNewItem] = useState | null>(null); + const [lastCreatedItemKey, setLastCreatedItemKey] = useState( + null, + ); + // reset state when changing taxonomy type useEffect(() => { setDraftNewItem(null); + setLastCreatedItemKey(null); setTaxonomyItemToEdit(null); }, [taxonomyType]); @@ -65,6 +70,7 @@ const TaxonomyPage: NextPage = () => { toast(errorToastParams(getErrorMessage(result.error))); return; } + setLastCreatedItemKey(result.data.fides_key); toast(successToastParams("New label successfully created")); setTimeout(() => setDraftNewItem(null)); }, @@ -120,6 +126,8 @@ const TaxonomyPage: NextPage = () => { showDisabledItems ? taxonomyItems : activeTaxonomyItems } draftNewItem={draftNewItem} + lastCreatedItemKey={lastCreatedItemKey} + resetLastCreatedItemKey={() => setLastCreatedItemKey(null)} onTaxonomyItemClick={(taxonomyItem) => { setTaxonomyItemToEdit(taxonomyItem); }}