This is an automated email from the ASF dual-hosted git repository.

pierrejeambrun pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-1-test by this push:
     new add3c55ef6b [v3-1-test] Popup is getting automatically closed when 
there is a DAG… (#58538)
add3c55ef6b is described below

commit add3c55ef6bc68d1ed75fd60bfc06632d16224f9
Author: Brent Bovenzi <[email protected]>
AuthorDate: Fri Nov 21 11:21:16 2025 +0100

    [v3-1-test] Popup is getting automatically closed when there is a DAG… 
(#58538)
    
    * [v3-1-test] Popup is getting automatically closed when there is a DAG 
running (#57568)
    
    * Popup is getting automatically closed when there is a DAG running
    
    * data refresh affects only the table rows, not the dialog boxes.
    
    * use useMemo to memorize columns
    
    * modify hope the dialog state will not be lost due to auto-refresh.
    
    * undo simple auth changes
    
    * Update simple_auth_manager_passwords.json
---
 .../components/ActionAccordion/ActionAccordion.tsx   |  5 ++++-
 .../Clear/TaskInstance/ClearTaskInstanceButton.tsx   | 20 +++++++++++++++-----
 .../airflow/ui/src/pages/AssetsList/AssetsList.tsx   |  5 ++++-
 .../airflow/ui/src/pages/Dag/Backfills/Backfills.tsx |  5 ++++-
 airflow-core/src/airflow/ui/src/pages/DagRuns.tsx    |  5 +++--
 .../src/airflow/ui/src/pages/Events/Events.tsx       |  8 +++++++-
 .../pages/HITLTaskInstances/HITLTaskInstances.tsx    | 20 +++++++++++++-------
 .../src/airflow/ui/src/pages/TaskInstance/Header.tsx | 12 ++++++++++++
 .../ui/src/pages/TaskInstances/TaskInstances.tsx     | 20 +++++++++++++-------
 airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx  |  5 ++++-
 10 files changed, 79 insertions(+), 26 deletions(-)

diff --git 
a/airflow-core/src/airflow/ui/src/components/ActionAccordion/ActionAccordion.tsx
 
b/airflow-core/src/airflow/ui/src/components/ActionAccordion/ActionAccordion.tsx
index f9c6c42e706..a08b9744b51 100644
--- 
a/airflow-core/src/airflow/ui/src/components/ActionAccordion/ActionAccordion.tsx
+++ 
b/airflow-core/src/airflow/ui/src/components/ActionAccordion/ActionAccordion.tsx
@@ -18,6 +18,7 @@
  */
 import { Box, Editable, Text, VStack } from "@chakra-ui/react";
 import type { ChangeEvent } from "react";
+import { useMemo } from "react";
 import { useTranslation } from "react-i18next";
 
 import type { DAGRunResponse, TaskInstanceCollectionResponse } from 
"openapi/requests/types.gen";
@@ -39,6 +40,8 @@ const ActionAccordion = ({ affectedTasks, note, setNote }: 
Props) => {
   const showTaskSection = affectedTasks !== undefined;
   const { t: translate } = useTranslation();
 
+  const columns = useMemo(() => getColumns(translate), [translate]);
+
   return (
     <Accordion.Root
       collapsible
@@ -58,7 +61,7 @@ const ActionAccordion = ({ affectedTasks, note, setNote }: 
Props) => {
           <Accordion.ItemContent>
             <Box maxH="400px" overflowY="scroll">
               <DataTable
-                columns={getColumns(translate)}
+                columns={columns}
                 data={affectedTasks.task_instances}
                 displayMode="table"
                 modelName={translate("common:taskInstance_other")}
diff --git 
a/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceButton.tsx
 
b/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceButton.tsx
index 5e8e27ac757..ac1b795630f 100644
--- 
a/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceButton.tsx
+++ 
b/airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceButton.tsx
@@ -31,6 +31,8 @@ import ClearTaskInstanceDialog from 
"./ClearTaskInstanceDialog";
 type Props = {
   readonly groupTaskInstance?: LightGridTaskInstanceSummary;
   readonly isHotkeyEnabled?: boolean;
+  // Optional: allow parent to handle opening a stable, page-level dialog
+  readonly onOpen?: (ti: LightGridTaskInstanceSummary | TaskInstanceResponse) 
=> void;
   readonly taskInstance?: TaskInstanceResponse;
   readonly withText?: boolean;
 };
@@ -38,17 +40,25 @@ type Props = {
 const ClearTaskInstanceButton = ({
   groupTaskInstance,
   isHotkeyEnabled = false,
+  onOpen,
   taskInstance,
   withText = true,
 }: Props) => {
-  const { onClose, onOpen, open } = useDisclosure();
+  const { onClose, onOpen: onOpenInternal, open } = useDisclosure();
   const { t: translate } = useTranslation();
   const isGroup = groupTaskInstance && !taskInstance;
+  const useInternalDialog = !Boolean(onOpen);
+
+  const selectedInstance = taskInstance ?? groupTaskInstance;
 
   useHotkeys(
     "shift+c",
     () => {
-      onOpen();
+      if (onOpen && selectedInstance) {
+        onOpen(selectedInstance);
+      } else {
+        onOpenInternal();
+      }
     },
     { enabled: isHotkeyEnabled },
   );
@@ -66,18 +76,18 @@ const ClearTaskInstanceButton = ({
             type: translate("taskInstance_one"),
           })}
           icon={<CgRedo />}
-          onClick={onOpen}
+          onClick={() => (onOpen && selectedInstance ? 
onOpen(selectedInstance) : onOpenInternal())}
           text={translate("dags:runAndTaskActions.clear.button", {
             type: translate(isGroup ? "taskGroup" : "taskInstance_one"),
           })}
           withText={withText}
         />
 
-        {open && isGroup ? (
+        {useInternalDialog && open && isGroup ? (
           <ClearGroupTaskInstanceDialog onClose={onClose} open={open} 
taskInstance={groupTaskInstance} />
         ) : undefined}
 
-        {open && !isGroup && taskInstance ? (
+        {useInternalDialog && open && !isGroup && taskInstance ? (
           <ClearTaskInstanceDialog onClose={onClose} open={open} 
taskInstance={taskInstance} />
         ) : undefined}
       </Box>
diff --git a/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx 
b/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx
index 9aa92b6ce0b..1a7c94d6427 100644
--- a/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx
@@ -18,6 +18,7 @@
  */
 import { Box, Heading, Link, VStack } from "@chakra-ui/react";
 import type { ColumnDef } from "@tanstack/react-table";
+import { useMemo } from "react";
 import { useTranslation } from "react-i18next";
 import { useSearchParams, Link as RouterLink } from "react-router-dom";
 
@@ -111,6 +112,8 @@ export const AssetsList = () => {
     orderBy,
   });
 
+  const columns = useMemo(() => createColumns(translate), [translate]);
+
   const handleSearchChange = (value: string) => {
     setTableURLState({
       pagination: { ...pagination, pageIndex: 0 },
@@ -140,7 +143,7 @@ export const AssetsList = () => {
       </VStack>
       <Box overflow="auto">
         <DataTable
-          columns={createColumns(translate)}
+          columns={columns}
           data={data?.assets ?? []}
           errorMessage={<ErrorAlert error={error} />}
           initialState={tableURLState}
diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/Backfills/Backfills.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Backfills/Backfills.tsx
index 62bfdaf4b32..983daf46c4f 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Backfills/Backfills.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Backfills/Backfills.tsx
@@ -18,6 +18,7 @@
  */
 import { Box, Heading, Text } from "@chakra-ui/react";
 import type { ColumnDef } from "@tanstack/react-table";
+import { useMemo } from "react";
 import { useTranslation } from "react-i18next";
 import { useParams } from "react-router-dom";
 
@@ -117,6 +118,8 @@ export const Backfills = () => {
     offset: pagination.pageIndex * pagination.pageSize,
   });
 
+  const columns = useMemo(() => getColumns(translate), [translate]);
+
   return (
     <Box>
       <ErrorAlert error={error} />
@@ -124,7 +127,7 @@ export const Backfills = () => {
         {translate("backfill", { count: data ? data.total_entries : 0 })}
       </Heading>
       <DataTable
-        columns={getColumns(translate)}
+        columns={columns}
         data={data ? data.backfills : []}
         isFetching={isFetching}
         isLoading={isLoading}
diff --git a/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx 
b/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx
index 0ad7f63f343..c0487ab49c7 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx
@@ -21,7 +21,7 @@
 import { Flex, HStack, Link, type SelectValueChangeDetails, Text, Box } from 
"@chakra-ui/react";
 import type { ColumnDef } from "@tanstack/react-table";
 import type { TFunction } from "i18next";
-import { useCallback } from "react";
+import { useCallback, useMemo } from "react";
 import { useTranslation } from "react-i18next";
 import { Link as RouterLink, useParams, useSearchParams } from 
"react-router-dom";
 
@@ -287,6 +287,7 @@ export const DagRuns = () => {
     },
     [pagination, searchParams, setSearchParams, setTableURLState, sorting],
   );
+  const columns = useMemo(() => runColumns(translate, dagId), [translate, 
dagId]);
 
   return (
     <>
@@ -378,7 +379,7 @@ export const DagRuns = () => {
         </Select.Root>
       </HStack>
       <DataTable
-        columns={runColumns(translate, dagId)}
+        columns={columns}
         data={data?.dag_runs ?? []}
         errorMessage={<ErrorAlert error={error} />}
         initialState={tableURLState}
diff --git a/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx 
b/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx
index 2a3bbefbb3a..e94bf74bdb2 100644
--- a/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Events/Events.tsx
@@ -19,6 +19,7 @@
 import { Code, Flex, Heading, useDisclosure, VStack } from "@chakra-ui/react";
 import type { ColumnDef } from "@tanstack/react-table";
 import dayjs from "dayjs";
+import { useMemo } from "react";
 import { useTranslation } from "react-i18next";
 import { useParams, useSearchParams } from "react-router-dom";
 
@@ -207,6 +208,11 @@ export const Events = () => {
     undefined,
   );
 
+  const columns = useMemo(
+    () => eventsColumn({ dagId, open, runId, taskId }, translate),
+    [dagId, open, runId, taskId, translate],
+  );
+
   return (
     <VStack alignItems="stretch">
       {dagId === undefined && runId === undefined && taskId === undefined ? (
@@ -224,7 +230,7 @@ export const Events = () => {
 
       <ErrorAlert error={error} />
       <DataTable
-        columns={eventsColumn({ dagId, open, runId, taskId }, translate)}
+        columns={columns}
         data={data?.event_logs ?? []}
         displayMode="table"
         initialState={tableURLState}
diff --git 
a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx 
b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx
index 40d6e7d2eb1..36151ca155f 100644
--- 
a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx
+++ 
b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLTaskInstances.tsx
@@ -19,7 +19,7 @@
 import { Heading, Link, VStack } from "@chakra-ui/react";
 import type { ColumnDef } from "@tanstack/react-table";
 import type { TFunction } from "i18next";
-import { useCallback } from "react";
+import { useCallback, useMemo } from "react";
 import { useTranslation } from "react-i18next";
 import { Link as RouterLink, useParams, useSearchParams } from 
"react-router-dom";
 
@@ -179,6 +179,17 @@ export const HITLTaskInstances = () => {
     setSearchParams(searchParams);
   }, [pagination, searchParams, setSearchParams, setTableURLState, sorting]);
 
+  const columns = useMemo(
+    () =>
+      taskInstanceColumns({
+        dagId,
+        runId,
+        taskId,
+        translate,
+      }),
+    [dagId, runId, taskId, translate],
+  );
+
   return (
     <VStack align="start">
       {!Boolean(dagId) && !Boolean(runId) && !Boolean(taskId) ? (
@@ -188,12 +199,7 @@ export const HITLTaskInstances = () => {
       ) : undefined}
       <HITLFilters onResponseChange={handleResponseChange} />
       <DataTable
-        columns={taskInstanceColumns({
-          dagId,
-          runId,
-          taskId,
-          translate,
-        })}
+        columns={columns}
         data={data?.hitl_details ?? []}
         errorMessage={<ErrorAlert error={error} />}
         initialState={tableURLState}
diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx
index 0698baeaddc..2238a33029b 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx
@@ -23,6 +23,7 @@ import { MdOutlineTask } from "react-icons/md";
 
 import type { TaskInstanceResponse } from "openapi/requests/types.gen";
 import { ClearTaskInstanceButton } from "src/components/Clear";
+import ClearTaskInstanceDialog from 
"src/components/Clear/TaskInstance/ClearTaskInstanceDialog";
 import { DagVersion } from "src/components/DagVersion";
 import EditableMarkdownButton from "src/components/EditableMarkdownButton";
 import { HeaderCard } from "src/components/HeaderCard";
@@ -93,6 +94,9 @@ export const Header = ({
     setNote(taskInstance.note ?? "");
   };
 
+  // Stable dialog state at header/page level
+  const [clearOpen, setClearOpen] = useState(false);
+
   return (
     <Box ref={containerRef}>
       <HeaderCard
@@ -111,6 +115,7 @@ export const Header = ({
             />
             <ClearTaskInstanceButton
               isHotkeyEnabled
+              onOpen={() => setClearOpen(true)}
               taskInstance={taskInstance}
               withText={containerWidth > 700}
             />
@@ -127,6 +132,13 @@ export const Header = ({
         stats={stats}
         title={`${taskInstance.task_display_name}${taskInstance.map_index > -1 
? ` [${taskInstance.rendered_map_index ?? taskInstance.map_index}]` : ""}`}
       />
+      {clearOpen ? (
+        <ClearTaskInstanceDialog
+          onClose={() => setClearOpen(false)}
+          open={clearOpen}
+          taskInstance={taskInstance}
+        />
+      ) : undefined}
     </Box>
   );
 };
diff --git 
a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx 
b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx
index 446c490322a..c51430b9935 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx
@@ -21,7 +21,7 @@
 import { Flex, Link } from "@chakra-ui/react";
 import type { ColumnDef } from "@tanstack/react-table";
 import type { TFunction } from "i18next";
-import { useState } from "react";
+import { useMemo, useState } from "react";
 import { useTranslation } from "react-i18next";
 import { Link as RouterLink, useParams, useSearchParams } from 
"react-router-dom";
 
@@ -261,6 +261,17 @@ export const TaskInstances = () => {
     },
   );
 
+  const columns = useMemo(
+    () =>
+      taskInstanceColumns({
+        dagId,
+        runId,
+        taskId: Boolean(groupId) ? undefined : taskId,
+        translate,
+      }),
+    [dagId, runId, groupId, taskId, translate],
+  );
+
   return (
     <>
       <TaskInstancesFilter
@@ -268,12 +279,7 @@ export const TaskInstances = () => {
         taskDisplayNamePattern={taskDisplayNamePattern}
       />
       <DataTable
-        columns={taskInstanceColumns({
-          dagId,
-          runId,
-          taskId: Boolean(groupId) ? undefined : taskId,
-          translate,
-        })}
+        columns={columns}
         data={data?.task_instances ?? []}
         errorMessage={<ErrorAlert error={error} />}
         initialState={tableURLState}
diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx 
b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx
index 9a1fc1a22a1..fc0e8045b3f 100644
--- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx
@@ -18,6 +18,7 @@
  */
 import { Box, Heading, Link, Flex, useDisclosure } from "@chakra-ui/react";
 import type { ColumnDef } from "@tanstack/react-table";
+import { useMemo } from "react";
 import { useTranslation } from "react-i18next";
 import { Link as RouterLink, useParams, useSearchParams } from 
"react-router-dom";
 
@@ -161,6 +162,8 @@ export const XCom = () => {
 
   const { data, error, isFetching, isLoading } = 
useXcomServiceGetXcomEntries(apiParams, undefined);
 
+  const memoizedColumns = useMemo(() => columns(translate, open), [translate, 
open]);
+
   return (
     <Box>
       {dagId === "~" && runId === "~" && taskId === "~" ? (
@@ -179,7 +182,7 @@ export const XCom = () => {
 
       <ErrorAlert error={error} />
       <DataTable
-        columns={columns(translate, open)}
+        columns={memoizedColumns}
         data={data ? data.xcom_entries : []}
         displayMode="table"
         initialState={tableURLState}

Reply via email to