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}