From 7a0a2eab8ca743b0fafe389bd074a59be6f69eea Mon Sep 17 00:00:00 2001 From: Smit Makadia Date: Mon, 27 Apr 2026 18:03:11 +0530 Subject: [PATCH 1/9] Add direction functionality in asset graph view. --- .../airflow/ui/src/pages/Asset/AssetGraph.tsx | 8 +- .../ui/src/pages/Asset/AssetPanelButtons.tsx | 122 +++++++++++++++--- 2 files changed, 109 insertions(+), 21 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx b/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx index 5251117ccb74f..9b551e0299013 100644 --- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx @@ -25,10 +25,12 @@ import type { AssetResponse } from "openapi/requests/types.gen"; import { DownloadButton } from "src/components/Graph/DownloadButton"; import { edgeTypes, nodeTypes } from "src/components/Graph/graphTypes"; import type { CustomNodeProps } from "src/components/Graph/reactflowUtils"; -import { useGraphLayout } from "src/components/Graph/useGraphLayout"; +import { Direction, useGraphLayout } from "src/components/Graph/useGraphLayout"; import { useColorMode } from "src/context/colorMode"; import { useDependencyGraph } from "src/queries/useDependencyGraph"; import { getReactFlowThemeStyle } from "src/theme"; +import { useLocalStorage } from "usehooks-ts"; +import { directionKey } from "src/constants/localStorage"; export const AssetGraph = ({ asset, @@ -44,10 +46,12 @@ export const AssetGraph = ({ const { data: graphData = { edges: [], nodes: [] } } = useDependencyGraph(`asset:${assetId}`, { dependencyType, }); + + const [direction] = useLocalStorage(directionKey(assetId ?? ""),"RIGHT"); const { data: layoutData } = useGraphLayout({ ...graphData, - direction: "RIGHT", + direction, openGroupIds: [], }); diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx b/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx index c8b3178fa2f31..439aacfb22062 100644 --- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx @@ -16,8 +16,23 @@ * specific language governing permissions and limitations * under the License. */ -import { Box, Button, ButtonGroup } from "@chakra-ui/react"; +import { + Box, + Button, + ButtonGroup, + Flex, + IconButton, + Popover, + Portal, + Select, + type SelectValueChangeDetails, +} from "@chakra-ui/react"; import { useTranslation } from "react-i18next"; +import { directionOptions, type Direction } from "src/components/Graph/useGraphLayout"; +import { MdSettings } from "react-icons/md"; +import { useParams } from "react-router-dom"; +import { useLocalStorage } from "usehooks-ts"; +import { directionKey } from "src/constants/localStorage"; type Props = { readonly dependencyType: "data" | "scheduling"; @@ -26,27 +41,96 @@ type Props = { export const AssetPanelButtons = ({ dependencyType, setDependencyType }: Props) => { const { t: translate } = useTranslation(["assets"]); + const { assetId } = useParams(); + const [direction, setDirection] = useLocalStorage(directionKey(assetId ?? ""), "RIGHT"); + + const handleDirectionUpdate = ( + event: SelectValueChangeDetails<{ label: string; value: Array }>, + ) => { + if (event.value[0] !== undefined) { + setDirection(event.value[0] as Direction); + } + }; return ( - - - - + + + + + + + + + + + + + + + + + + + {translate("dag:panel.graphDirection.label")} + + + + + + + + + + + + {directionOptions(translate).items.map((option) => ( + + {option.label} + + ))} + + + + + + + + + ); }; From 9c52dc7263da0d60fedd2b933ab25a5f36a904eb Mon Sep 17 00:00:00 2001 From: Smit Makadia Date: Tue, 28 Apr 2026 13:16:21 +0530 Subject: [PATCH 2/9] Extract the direction dropdown to its own shared component for asset and dag graphs. --- .../components/Graph/DirectionDropdown.tsx | 81 +++++++++++++++++++ .../ui/src/components/Graph/elkGraphUtils.ts | 2 +- .../ui/src/components/Graph/useGraphLayout.ts | 14 +--- .../ui/src/layouts/Details/Graph/Graph.tsx | 3 +- .../ui/src/layouts/Details/PanelButtons.tsx | 42 +--------- .../airflow/ui/src/pages/Asset/AssetGraph.tsx | 3 +- .../ui/src/pages/Asset/AssetPanelButtons.tsx | 44 +--------- airflow-core/src/airflow/ui/testsSetup.ts | 1 - 8 files changed, 92 insertions(+), 98 deletions(-) create mode 100644 airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx diff --git a/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx b/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx new file mode 100644 index 0000000000000..6b441f6a4cb04 --- /dev/null +++ b/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx @@ -0,0 +1,81 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { createListCollection, Select, type SelectValueChangeDetails } from "@chakra-ui/react"; +import { useLocalStorage } from "usehooks-ts"; +import { directionKey } from "src/constants/localStorage"; +import type { TFunction } from "i18next"; +import { useTranslation } from "react-i18next"; + + +export type Direction = "DOWN" | "LEFT" | "RIGHT" | "UP"; + +export const DirectionDropdown = ({ graphId }: { readonly graphId: string }) => { + const { t: translate } = useTranslation(); + + const [direction, setDirection] = useLocalStorage(directionKey(graphId ?? ""), "RIGHT"); + + const directionOptions = (translate: TFunction) => + createListCollection({ + items: [ + { label: translate("graph.directionRight"), value: "RIGHT" as Direction }, + { label: translate("graph.directionLeft"), value: "LEFT" as Direction }, + { label: translate("graph.directionUp"), value: "UP" as Direction }, + { label: translate("graph.directionDown"), value: "DOWN" as Direction }, + ], + }); + + const handleDirectionUpdate = ( + event: SelectValueChangeDetails<{ label: string; value: Array }>, + ) => { + if (event.value[0] !== undefined) { + setDirection(event.value[0] as Direction); + } + }; + + return ( + + + {translate("dag:panel.graphDirection.label")} + + + + + + + + + + + + {directionOptions(translate).items.map((option) => ( + + {option.label} + + ))} + + + + ); +}; diff --git a/airflow-core/src/airflow/ui/src/components/Graph/elkGraphUtils.ts b/airflow-core/src/airflow/ui/src/components/Graph/elkGraphUtils.ts index 0ab685cf37e84..b307482ba75dc 100644 --- a/airflow-core/src/airflow/ui/src/components/Graph/elkGraphUtils.ts +++ b/airflow-core/src/airflow/ui/src/components/Graph/elkGraphUtils.ts @@ -20,7 +20,7 @@ import type { ElkNode, ElkExtendedEdge, ElkShape } from "elkjs"; import type { EdgeResponse, NodeResponse } from "openapi/requests/types.gen"; -import type { Direction } from "./useGraphLayout"; +import type { Direction } from "./DirectionDropdown"; // --------------------------------------------------------------------------- // Types diff --git a/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts b/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts index 9ffeb0fb673fb..d5336db2393e1 100644 --- a/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts +++ b/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import { createListCollection } from "@chakra-ui/react"; import { useQuery } from "@tanstack/react-query"; import ELK, { type ElkNode } from "elkjs"; // ?raw imports the file content as a plain string without any transformation. @@ -26,12 +25,12 @@ import ELK, { type ElkNode } from "elkjs"; // URL-based worker approaches (?worker, ?worker&inline, new URL()) resolve to // the Vite origin which browsers reject for Workers. import ElkWorkerSource from "elkjs/lib/elk-worker.min.js?raw"; -import type { TFunction } from "i18next"; import type { NodeResponse, StructureDataResponse } from "openapi/requests/types.gen"; import { generateElkGraph } from "./elkGraphUtils"; import { flattenGraph, formatFlowEdges } from "./reactflowUtils"; +import { type Direction } from "./DirectionDropdown"; // Blob URL created once at module load. `type: "classic"` preserves the // original CJS environment detection in elk-worker: as a classic script @@ -43,17 +42,6 @@ const elk = new ELK({ workerFactory: () => new Worker(elkWorkerBlobUrl, { type: "classic" }), }); -export type Direction = "DOWN" | "LEFT" | "RIGHT" | "UP"; -export const directionOptions = (translate: TFunction) => - createListCollection({ - items: [ - { label: translate("graph.directionRight"), value: "RIGHT" as Direction }, - { label: translate("graph.directionLeft"), value: "LEFT" as Direction }, - { label: translate("graph.directionUp"), value: "UP" as Direction }, - { label: translate("graph.directionDown"), value: "DOWN" as Direction }, - ], - }); - export type LayoutNode = ElkNode & NodeResponse; type LayoutProps = { diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx b/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx index 6ceeb9bacdfde..74a82534b48ca 100644 --- a/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx +++ b/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx @@ -37,7 +37,7 @@ import { useDagRunServiceGetDagRun, useStructureServiceStructureData } from "ope import { DownloadButton } from "src/components/Graph/DownloadButton"; import { edgeTypes, nodeTypes } from "src/components/Graph/graphTypes"; import type { CustomNodeProps } from "src/components/Graph/reactflowUtils"; -import { type Direction, useGraphLayout } from "src/components/Graph/useGraphLayout"; +import { useGraphLayout } from "src/components/Graph/useGraphLayout"; import { dependenciesKey, directionKey } from "src/constants/localStorage"; import { useColorMode } from "src/context/colorMode"; import { useOpenGroups } from "src/context/openGroups"; @@ -46,6 +46,7 @@ import { flattenGraphNodes } from "src/layouts/Details/Grid/utils.ts"; import { useDependencyGraph } from "src/queries/useDependencyGraph"; import { useGridTiSummariesStream } from "src/queries/useGridTISummaries.ts"; import { getReactFlowThemeStyle } from "src/theme"; +import { type Direction } from "src/components/Graph/DirectionDropdown"; // Hoisted to module scope so ReactFlow receives a stable reference and skips // its internal shallow-equality check on every render. diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx b/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx index 6a26cab3764e1..7e0536de4d05c 100644 --- a/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx +++ b/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx @@ -42,11 +42,10 @@ import { useParams } from "react-router-dom"; import { useLocalStorage } from "usehooks-ts"; import { DagVersionSelect } from "src/components/DagVersionSelect"; -import { directionOptions, type Direction } from "src/components/Graph/useGraphLayout"; import { Tooltip } from "src/components/ui"; import { type ButtonGroupOption, ButtonGroupToggle } from "src/components/ui/ButtonGroupToggle"; import type { DagView } from "src/constants/dagView"; -import { dependenciesKey, directionKey } from "src/constants/localStorage"; +import { dependenciesKey } from "src/constants/localStorage"; import type { VersionIndicatorOptions } from "src/constants/showVersionIndicatorOptions"; import { useContainerWidth } from "src/utils/useContainerWidth"; @@ -56,6 +55,7 @@ import { GridFilters } from "./GridFilters"; import { TaskStreamFilter } from "./TaskStreamFilter"; import { ToggleGroups } from "./ToggleGroups"; import { VersionIndicatorSelect } from "./VersionIndicatorSelect"; +import { DirectionDropdown } from "src/components/Graph/DirectionDropdown"; type Props = { readonly dagView: DagView; @@ -117,7 +117,6 @@ export const PanelButtons = ({ dependenciesKey(dagId), "tasks", ); - const [direction, setDirection] = useLocalStorage(directionKey(dagId), "RIGHT"); const containerRef = useRef(null); const containerWidth = useContainerWidth(containerRef); const handleLimitChange = (event: SelectValueChangeDetails<{ label: string; value: Array }>) => { @@ -147,14 +146,6 @@ export const PanelButtons = ({ } }; - const handleDirectionUpdate = ( - event: SelectValueChangeDetails<{ label: string; value: Array }>, - ) => { - if (event.value[0] !== undefined) { - setDirection(event.value[0] as Direction); - } - }; - const handleFocus = (view: string) => { if (panelGroupRef.current) { const newLayout = view === "graph" ? [70, 30] : [30, 70]; @@ -277,34 +268,7 @@ export const PanelButtons = ({ - - - {translate("dag:panel.graphDirection.label")} - - - - - - - - - - - - {directionOptions(translate).items.map((option) => ( - - {option.label} - - ))} - - - + ) : ( <> diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx b/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx index 9b551e0299013..291e729251fe3 100644 --- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx @@ -25,12 +25,13 @@ import type { AssetResponse } from "openapi/requests/types.gen"; import { DownloadButton } from "src/components/Graph/DownloadButton"; import { edgeTypes, nodeTypes } from "src/components/Graph/graphTypes"; import type { CustomNodeProps } from "src/components/Graph/reactflowUtils"; -import { Direction, useGraphLayout } from "src/components/Graph/useGraphLayout"; +import { useGraphLayout } from "src/components/Graph/useGraphLayout"; import { useColorMode } from "src/context/colorMode"; import { useDependencyGraph } from "src/queries/useDependencyGraph"; import { getReactFlowThemeStyle } from "src/theme"; import { useLocalStorage } from "usehooks-ts"; import { directionKey } from "src/constants/localStorage"; +import { type Direction } from "src/components/Graph/DirectionDropdown"; export const AssetGraph = ({ asset, diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx b/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx index 439aacfb22062..42d930cb3aa80 100644 --- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx @@ -24,15 +24,11 @@ import { IconButton, Popover, Portal, - Select, - type SelectValueChangeDetails, } from "@chakra-ui/react"; import { useTranslation } from "react-i18next"; -import { directionOptions, type Direction } from "src/components/Graph/useGraphLayout"; import { MdSettings } from "react-icons/md"; import { useParams } from "react-router-dom"; -import { useLocalStorage } from "usehooks-ts"; -import { directionKey } from "src/constants/localStorage"; +import { DirectionDropdown } from "src/components/Graph/DirectionDropdown"; type Props = { readonly dependencyType: "data" | "scheduling"; @@ -42,15 +38,6 @@ type Props = { export const AssetPanelButtons = ({ dependencyType, setDependencyType }: Props) => { const { t: translate } = useTranslation(["assets"]); const { assetId } = useParams(); - const [direction, setDirection] = useLocalStorage(directionKey(assetId ?? ""), "RIGHT"); - - const handleDirectionUpdate = ( - event: SelectValueChangeDetails<{ label: string; value: Array }>, - ) => { - if (event.value[0] !== undefined) { - setDirection(event.value[0] as Direction); - } - }; return ( @@ -97,34 +84,7 @@ export const AssetPanelButtons = ({ dependencyType, setDependencyType }: Props) overflowY="auto" p={2} > - - - {translate("dag:panel.graphDirection.label")} - - - - - - - - - - - - {directionOptions(translate).items.map((option) => ( - - {option.label} - - ))} - - - + diff --git a/airflow-core/src/airflow/ui/testsSetup.ts b/airflow-core/src/airflow/ui/testsSetup.ts index 1bb26b7d5ba7a..bbb226c7c91c4 100644 --- a/airflow-core/src/airflow/ui/testsSetup.ts +++ b/airflow-core/src/airflow/ui/testsSetup.ts @@ -28,7 +28,6 @@ import { handlers } from "src/mocks/handlers"; // happy-dom. Mock useGraphLayout so the Worker is never constructed. Any // test that specifically exercises graph layout should override this mock. vi.mock("src/components/Graph/useGraphLayout", () => ({ - directionOptions: () => ({ items: [] }), useGraphLayout: vi.fn().mockReturnValue({ data: undefined, isPending: false }), })); From 81999406812c0a3292dcb4bac29c38d62d6d4278 Mon Sep 17 00:00:00 2001 From: Smit Makadia Date: Thu, 30 Apr 2026 10:32:44 +0530 Subject: [PATCH 3/9] Add namespaces to use for translations. --- .../src/airflow/ui/src/components/Graph/DirectionDropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx b/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx index 6b441f6a4cb04..d2bb55c1a8fda 100644 --- a/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx +++ b/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx @@ -26,7 +26,7 @@ import { useTranslation } from "react-i18next"; export type Direction = "DOWN" | "LEFT" | "RIGHT" | "UP"; export const DirectionDropdown = ({ graphId }: { readonly graphId: string }) => { - const { t: translate } = useTranslation(); + const { t: translate } = useTranslation(["components", "dag"]); const [direction, setDirection] = useLocalStorage(directionKey(graphId ?? ""), "RIGHT"); From 562f8ea201bc1e79b55f05622ed9cfeef4f29459 Mon Sep 17 00:00:00 2001 From: Smit Makadia Date: Thu, 30 Apr 2026 21:18:08 +0530 Subject: [PATCH 4/9] Formatting changes. --- .../components/Graph/DirectionDropdown.tsx | 22 +++++++++---------- .../ui/src/components/Graph/useGraphLayout.ts | 2 +- .../ui/src/layouts/Details/Graph/Graph.tsx | 2 +- .../ui/src/layouts/Details/PanelButtons.tsx | 2 +- .../airflow/ui/src/pages/Asset/AssetGraph.tsx | 10 ++++----- .../ui/src/pages/Asset/AssetPanelButtons.tsx | 11 ++-------- 6 files changed, 20 insertions(+), 29 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx b/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx index d2bb55c1a8fda..eb791917bf885 100644 --- a/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx +++ b/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx @@ -17,11 +17,11 @@ * under the License. */ import { createListCollection, Select, type SelectValueChangeDetails } from "@chakra-ui/react"; -import { useLocalStorage } from "usehooks-ts"; -import { directionKey } from "src/constants/localStorage"; import type { TFunction } from "i18next"; import { useTranslation } from "react-i18next"; +import { useLocalStorage } from "usehooks-ts"; +import { directionKey } from "src/constants/localStorage"; export type Direction = "DOWN" | "LEFT" | "RIGHT" | "UP"; @@ -39,14 +39,14 @@ export const DirectionDropdown = ({ graphId }: { readonly graphId: string }) => { label: translate("graph.directionDown"), value: "DOWN" as Direction }, ], }); - + const handleDirectionUpdate = ( - event: SelectValueChangeDetails<{ label: string; value: Array }>, - ) => { - if (event.value[0] !== undefined) { - setDirection(event.value[0] as Direction); - } - }; + event: SelectValueChangeDetails<{ label: string; value: Array }>, + ) => { + if (event.value[0] !== undefined) { + setDirection(event.value[0] as Direction); + } + }; return ( size="sm" value={[direction]} > - - {translate("dag:panel.graphDirection.label")} - + {translate("dag:panel.graphDirection.label")} diff --git a/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts b/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts index d5336db2393e1..212453d3fc593 100644 --- a/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts +++ b/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts @@ -28,9 +28,9 @@ import ElkWorkerSource from "elkjs/lib/elk-worker.min.js?raw"; import type { NodeResponse, StructureDataResponse } from "openapi/requests/types.gen"; +import { type Direction } from "./DirectionDropdown"; import { generateElkGraph } from "./elkGraphUtils"; import { flattenGraph, formatFlowEdges } from "./reactflowUtils"; -import { type Direction } from "./DirectionDropdown"; // Blob URL created once at module load. `type: "classic"` preserves the // original CJS environment detection in elk-worker: as a classic script diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx b/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx index 4709c7efe7dfa..f3f6649ebe041 100644 --- a/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx +++ b/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx @@ -24,6 +24,7 @@ import { useParams } from "react-router-dom"; import { useLocalStorage } from "usehooks-ts"; import { useDagRunServiceGetDagRun, useStructureServiceStructureData } from "openapi/queries"; +import { type Direction } from "src/components/Graph/DirectionDropdown"; import { DownloadButton } from "src/components/Graph/DownloadButton"; import { edgeTypes, nodeTypes } from "src/components/Graph/graphTypes"; import type { CustomNodeProps } from "src/components/Graph/reactflowUtils"; @@ -36,7 +37,6 @@ import { flattenGraphNodes } from "src/layouts/Details/Grid/utils.ts"; import { useDependencyGraph } from "src/queries/useDependencyGraph"; import { useGridTiSummariesStream } from "src/queries/useGridTISummaries.ts"; import { getReactFlowThemeStyle } from "src/theme"; -import { type Direction } from "src/components/Graph/DirectionDropdown"; import { FitViewOnLayout } from "./components/FitViewOnLayout"; import { GraphControls } from "./components/GraphControls"; diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx b/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx index 957a150703aaa..9bc1f7beeac2a 100644 --- a/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx +++ b/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx @@ -42,6 +42,7 @@ import { useParams } from "react-router-dom"; import { useLocalStorage } from "usehooks-ts"; import { DagVersionSelect } from "src/components/DagVersionSelect"; +import { DirectionDropdown } from "src/components/Graph/DirectionDropdown"; import { GraphTaskFilters } from "src/components/GraphTaskFilters"; import { Tooltip } from "src/components/ui"; import { type ButtonGroupOption, ButtonGroupToggle } from "src/components/ui/ButtonGroupToggle"; @@ -56,7 +57,6 @@ import { GridFilters } from "./GridFilters"; import { TaskStreamFilter } from "./TaskStreamFilter"; import { ToggleGroups } from "./ToggleGroups"; import { VersionIndicatorSelect } from "./VersionIndicatorSelect"; -import { DirectionDropdown } from "src/components/Graph/DirectionDropdown"; type Props = { readonly dagView: DagView; diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx b/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx index 291e729251fe3..b812a574ace6d 100644 --- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx @@ -20,18 +20,18 @@ import { useToken } from "@chakra-ui/react"; import { ReactFlow, Controls, Background, MiniMap, type Node as ReactFlowNode } from "@xyflow/react"; import "@xyflow/react/dist/style.css"; import { useParams } from "react-router-dom"; +import { useLocalStorage } from "usehooks-ts"; import type { AssetResponse } from "openapi/requests/types.gen"; +import { type Direction } from "src/components/Graph/DirectionDropdown"; import { DownloadButton } from "src/components/Graph/DownloadButton"; import { edgeTypes, nodeTypes } from "src/components/Graph/graphTypes"; import type { CustomNodeProps } from "src/components/Graph/reactflowUtils"; import { useGraphLayout } from "src/components/Graph/useGraphLayout"; +import { directionKey } from "src/constants/localStorage"; import { useColorMode } from "src/context/colorMode"; import { useDependencyGraph } from "src/queries/useDependencyGraph"; import { getReactFlowThemeStyle } from "src/theme"; -import { useLocalStorage } from "usehooks-ts"; -import { directionKey } from "src/constants/localStorage"; -import { type Direction } from "src/components/Graph/DirectionDropdown"; export const AssetGraph = ({ asset, @@ -47,8 +47,8 @@ export const AssetGraph = ({ const { data: graphData = { edges: [], nodes: [] } } = useDependencyGraph(`asset:${assetId}`, { dependencyType, }); - - const [direction] = useLocalStorage(directionKey(assetId ?? ""),"RIGHT"); + + const [direction] = useLocalStorage(directionKey(assetId ?? ""), "RIGHT"); const { data: layoutData } = useGraphLayout({ ...graphData, diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx b/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx index 42d930cb3aa80..7235d162b4a45 100644 --- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx @@ -16,18 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -import { - Box, - Button, - ButtonGroup, - Flex, - IconButton, - Popover, - Portal, -} from "@chakra-ui/react"; +import { Box, Button, ButtonGroup, Flex, IconButton, Popover, Portal } from "@chakra-ui/react"; import { useTranslation } from "react-i18next"; import { MdSettings } from "react-icons/md"; import { useParams } from "react-router-dom"; + import { DirectionDropdown } from "src/components/Graph/DirectionDropdown"; type Props = { From 703a76971a718fa46e81d94574a7d3f8a9afc9c8 Mon Sep 17 00:00:00 2001 From: Brent Bovenzi Date: Thu, 30 Apr 2026 14:39:43 -0400 Subject: [PATCH 5/9] Update airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx --- airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx b/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx index b812a574ace6d..c0f5218e5c605 100644 --- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetGraph.tsx @@ -23,7 +23,7 @@ import { useParams } from "react-router-dom"; import { useLocalStorage } from "usehooks-ts"; import type { AssetResponse } from "openapi/requests/types.gen"; -import { type Direction } from "src/components/Graph/DirectionDropdown"; +import type { Direction } from "src/components/Graph/DirectionDropdown"; import { DownloadButton } from "src/components/Graph/DownloadButton"; import { edgeTypes, nodeTypes } from "src/components/Graph/graphTypes"; import type { CustomNodeProps } from "src/components/Graph/reactflowUtils"; From 4ec5bf3834961535499ce51041e217b711e15607 Mon Sep 17 00:00:00 2001 From: Brent Bovenzi Date: Thu, 30 Apr 2026 14:39:51 -0400 Subject: [PATCH 6/9] Update airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts --- .../src/airflow/ui/src/components/Graph/useGraphLayout.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts b/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts index 212453d3fc593..e2da074034fc9 100644 --- a/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts +++ b/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts @@ -28,7 +28,7 @@ import ElkWorkerSource from "elkjs/lib/elk-worker.min.js?raw"; import type { NodeResponse, StructureDataResponse } from "openapi/requests/types.gen"; -import { type Direction } from "./DirectionDropdown"; +import type { Direction } from "./DirectionDropdown"; import { generateElkGraph } from "./elkGraphUtils"; import { flattenGraph, formatFlowEdges } from "./reactflowUtils"; From fad1a1a5c82eb06ac6bd3f5a9693d12cba903d46 Mon Sep 17 00:00:00 2001 From: Brent Bovenzi Date: Thu, 30 Apr 2026 14:40:02 -0400 Subject: [PATCH 7/9] Update airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx --- airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx b/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx index f3f6649ebe041..8c2ff6574530d 100644 --- a/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx +++ b/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx @@ -24,7 +24,7 @@ import { useParams } from "react-router-dom"; import { useLocalStorage } from "usehooks-ts"; import { useDagRunServiceGetDagRun, useStructureServiceStructureData } from "openapi/queries"; -import { type Direction } from "src/components/Graph/DirectionDropdown"; +import type { Direction } from "src/components/Graph/DirectionDropdown"; import { DownloadButton } from "src/components/Graph/DownloadButton"; import { edgeTypes, nodeTypes } from "src/components/Graph/graphTypes"; import type { CustomNodeProps } from "src/components/Graph/reactflowUtils"; From 5350c7a4e3608e5a2b690ff7266b9d35b1cf3e45 Mon Sep 17 00:00:00 2001 From: Smit Makadia Date: Fri, 1 May 2026 11:44:18 +0530 Subject: [PATCH 8/9] Fix compile, format, and lint issues found in checks. --- .../ui/src/components/Graph/DirectionDropdown.tsx | 9 ++++----- .../src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx b/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx index eb791917bf885..b1f608a445ddb 100644 --- a/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx +++ b/airflow-core/src/airflow/ui/src/components/Graph/DirectionDropdown.tsx @@ -17,7 +17,6 @@ * under the License. */ import { createListCollection, Select, type SelectValueChangeDetails } from "@chakra-ui/react"; -import type { TFunction } from "i18next"; import { useTranslation } from "react-i18next"; import { useLocalStorage } from "usehooks-ts"; @@ -28,9 +27,9 @@ export type Direction = "DOWN" | "LEFT" | "RIGHT" | "UP"; export const DirectionDropdown = ({ graphId }: { readonly graphId: string }) => { const { t: translate } = useTranslation(["components", "dag"]); - const [direction, setDirection] = useLocalStorage(directionKey(graphId ?? ""), "RIGHT"); + const [direction, setDirection] = useLocalStorage(directionKey(graphId), "RIGHT"); - const directionOptions = (translate: TFunction) => + const directionOptions = () => createListCollection({ items: [ { label: translate("graph.directionRight"), value: "RIGHT" as Direction }, @@ -51,7 +50,7 @@ export const DirectionDropdown = ({ graphId }: { readonly graphId: string }) => return ( - {directionOptions(translate).items.map((option) => ( + {directionOptions().items.map((option) => ( {option.label} diff --git a/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx b/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx index 7235d162b4a45..c63a1dcafb2de 100644 --- a/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Asset/AssetPanelButtons.tsx @@ -53,7 +53,7 @@ export const AssetPanelButtons = ({ dependencyType, setDependencyType }: Props) {translate("assets:taskDependencies")} - + Date: Tue, 5 May 2026 16:34:55 +0530 Subject: [PATCH 9/9] Remove an extra white space. --- .../src/airflow/ui/src/components/Graph/useGraphLayout.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts b/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts index e2da074034fc9..6d989e4f606c9 100644 --- a/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts +++ b/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts @@ -28,7 +28,7 @@ import ElkWorkerSource from "elkjs/lib/elk-worker.min.js?raw"; import type { NodeResponse, StructureDataResponse } from "openapi/requests/types.gen"; -import type { Direction } from "./DirectionDropdown"; +import type { Direction } from "./DirectionDropdown"; import { generateElkGraph } from "./elkGraphUtils"; import { flattenGraph, formatFlowEdges } from "./reactflowUtils";