From c340993170c9df63e5b315dc2c6015158d02c746 Mon Sep 17 00:00:00 2001 From: internetcoffeephone Date: Mon, 11 May 2026 17:48:53 +0300 Subject: [PATCH] UI: Fix "Mark state as..." buttons grayed out when task/DAGRun already in target state (#66198) * UI: Fix "Mark state as..." buttons grayed out when task/DAGRun already in target state Menu items for marking a task instance, task group, or DAG run as success/failed were disabled whenever the item's current state matched the target state. This blocked users from re-applying the same state (e.g. marking an already-succeeded DAG run as success to also flip all its tasks). The same bug existed in Airflow 2.x (#36219) and regressed in the Airflow 3.x UI redesign. Remove the state-equality guards from: - the `disabled` prop on Menu.Item - the `onClick` early-return - the hotkey `enabled` conditions - the Tooltip `disabled` condition Add regression tests for MarkRunAsButton to prevent future recurrence. Fixes #66197 * Replace unit test with inline comments explaining why state-match is not disabled Per review feedback: comments in the source communicate the intent more reliably than a separate test file that could be deleted without context. https://claude.ai/code/session_012oET1NyiNZe44zWbr9GZnn --------- Co-authored-by: Claude (cherry picked from commit 505924feaa4de521c364f24d7562a35d951561f0) --- .../src/components/MarkAs/Run/MarkRunAsButton.tsx | 14 ++++++-------- .../TaskInstance/MarkTaskInstanceAsButton.tsx | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsButton.tsx b/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsButton.tsx index a71fc8c282163..47025709a5fd0 100644 --- a/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsButton.tsx +++ b/airflow-core/src/airflow/ui/src/components/MarkAs/Run/MarkRunAsButton.tsx @@ -46,7 +46,7 @@ const MarkRunAsButton = ({ dagRun, isHotkeyEnabled = false }: Props) => { setState("failed"); onOpen(); }, - { enabled: isHotkeyEnabled && dagRun.state !== "failed" }, + { enabled: isHotkeyEnabled }, ); useHotkeys( @@ -55,7 +55,7 @@ const MarkRunAsButton = ({ dagRun, isHotkeyEnabled = false }: Props) => { setState("success"); onOpen(); }, - { enabled: isHotkeyEnabled && dagRun.state !== "success" }, + { enabled: isHotkeyEnabled }, ); return ( @@ -94,20 +94,18 @@ const MarkRunAsButton = ({ dagRun, isHotkeyEnabled = false }: Props) => { + {/* Not disabled when state matches: re-applying lets users also flip upstream/downstream tasks */} { - if (dagRun.state !== menuState) { - setState(menuState); - onOpen(); - } + setState(menuState); + onOpen(); }} value={menuState} > diff --git a/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsButton.tsx b/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsButton.tsx index 8ae616232b90a..19d1e8c75c547 100644 --- a/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsButton.tsx +++ b/airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsButton.tsx @@ -47,7 +47,7 @@ const MarkTaskInstanceAsButton = ({ isHotkeyEnabled = false, taskInstance }: Pro setState("failed"); onOpen(); }, - { enabled: isHotkeyEnabled && taskInstance.state !== "failed" }, + { enabled: isHotkeyEnabled }, ); useHotkeys( @@ -56,7 +56,7 @@ const MarkTaskInstanceAsButton = ({ isHotkeyEnabled = false, taskInstance }: Pro setState("success"); onOpen(); }, - { enabled: isHotkeyEnabled && taskInstance.state !== "success" }, + { enabled: isHotkeyEnabled }, ); return ( @@ -96,19 +96,17 @@ const MarkTaskInstanceAsButton = ({ isHotkeyEnabled = false, taskInstance }: Pro + {/* Not disabled when state matches: re-applying lets users also flip upstream/downstream tasks */} { - if (taskInstance.state !== menuState) { - setState(menuState); - onOpen(); - } + setState(menuState); + onOpen(); }} value={menuState} >