This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new e79b034173b Add shortcut to clear and mark state for taskinstance and
dagrun. (#50885)
e79b034173b is described below
commit e79b034173b4fed704ce61ea30ab8c52c3579bbb
Author: Karthikeyan Singaravelan <[email protected]>
AuthorDate: Mon May 26 14:47:36 2025 +0530
Add shortcut to clear and mark state for taskinstance and dagrun. (#50885)
* Add shortcut to clear and mark state for taskinstance and dagrun.
* Rename hasHotKey to isHotkeyEnabled.
---
.../ui/src/components/Clear/Run/ClearRunButton.tsx | 37 +++++++----
.../Clear/TaskInstance/ClearTaskInstanceButton.tsx | 41 ++++++++----
.../src/components/MarkAs/Run/MarkRunAsButton.tsx | 75 ++++++++++++++++------
.../TaskInstance/MarkTaskInstanceAsButton.tsx | 75 ++++++++++++++++------
.../src/airflow/ui/src/pages/Run/Header.tsx | 4 +-
.../airflow/ui/src/pages/TaskInstance/Header.tsx | 12 +++-
6 files changed, 174 insertions(+), 70 deletions(-)
diff --git
a/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunButton.tsx
b/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunButton.tsx
index 0395812cd9d..ba9019ba12b 100644
--- a/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunButton.tsx
+++ b/airflow-core/src/airflow/ui/src/components/Clear/Run/ClearRunButton.tsx
@@ -17,33 +17,46 @@
* under the License.
*/
import { Box, useDisclosure } from "@chakra-ui/react";
+import { useHotkeys } from "react-hotkeys-hook";
import { CgRedo } from "react-icons/cg";
import type { DAGRunResponse } from "openapi/requests/types.gen";
+import { Tooltip } from "src/components/ui";
import ActionButton from "src/components/ui/ActionButton";
import ClearRunDialog from "./ClearRunDialog";
type Props = {
readonly dagRun: DAGRunResponse;
+ readonly isHotkeyEnabled?: boolean;
readonly withText?: boolean;
};
-const ClearRunButton = ({ dagRun, withText = true }: Props) => {
+const ClearRunButton = ({ dagRun, isHotkeyEnabled = false, withText = true }:
Props) => {
const { onClose, onOpen, open } = useDisclosure();
+ useHotkeys(
+ "shift+c",
+ () => {
+ onOpen();
+ },
+ { enabled: isHotkeyEnabled },
+ );
+
return (
- <Box>
- <ActionButton
- actionName="Clear Dag Run"
- icon={<CgRedo />}
- onClick={onOpen}
- text="Clear Run"
- withText={withText}
- />
-
- {open ? <ClearRunDialog dagRun={dagRun} onClose={onClose} open={open} />
: undefined}
- </Box>
+ <Tooltip closeDelay={100} content="Press shift+c to clear"
disabled={!isHotkeyEnabled} openDelay={100}>
+ <Box>
+ <ActionButton
+ actionName="Clear Dag Run"
+ icon={<CgRedo />}
+ onClick={onOpen}
+ text="Clear Run"
+ withText={withText}
+ />
+
+ {open ? <ClearRunDialog dagRun={dagRun} onClose={onClose} open={open}
/> : undefined}
+ </Box>
+ </Tooltip>
);
};
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 68750c2c949..0badf52acba 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
@@ -17,35 +17,48 @@
* under the License.
*/
import { Box, useDisclosure } from "@chakra-ui/react";
+import { useHotkeys } from "react-hotkeys-hook";
import { CgRedo } from "react-icons/cg";
import type { TaskInstanceResponse } from "openapi/requests/types.gen";
+import { Tooltip } from "src/components/ui";
import ActionButton from "src/components/ui/ActionButton";
import ClearTaskInstanceDialog from "./ClearTaskInstanceDialog";
type Props = {
+ readonly isHotkeyEnabled?: boolean;
readonly taskInstance: TaskInstanceResponse;
readonly withText?: boolean;
};
-const ClearTaskInstanceButton = ({ taskInstance, withText = true }: Props) => {
+const ClearTaskInstanceButton = ({ isHotkeyEnabled = false, taskInstance,
withText = true }: Props) => {
const { onClose, onOpen, open } = useDisclosure();
+ useHotkeys(
+ "shift+c",
+ () => {
+ onOpen();
+ },
+ { enabled: isHotkeyEnabled },
+ );
+
return (
- <Box>
- <ActionButton
- actionName="Clear Task Instance"
- icon={<CgRedo />}
- onClick={onOpen}
- text="Clear Task Instance"
- withText={withText}
- />
-
- {open ? (
- <ClearTaskInstanceDialog onClose={onClose} open={open}
taskInstance={taskInstance} />
- ) : undefined}
- </Box>
+ <Tooltip closeDelay={100} content="Press shift+c to clear"
disabled={!isHotkeyEnabled} openDelay={100}>
+ <Box>
+ <ActionButton
+ actionName="Clear Task Instance"
+ icon={<CgRedo />}
+ onClick={onOpen}
+ text="Clear Task Instance"
+ withText={withText}
+ />
+
+ {open ? (
+ <ClearTaskInstanceDialog onClose={onClose} open={open}
taskInstance={taskInstance} />
+ ) : undefined}
+ </Box>
+ </Tooltip>
);
};
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 1ed24c51185..ad6fa2ef537 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
@@ -18,11 +18,12 @@
*/
import { Box, useDisclosure } from "@chakra-ui/react";
import { useState } from "react";
+import { useHotkeys } from "react-hotkeys-hook";
import { MdArrowDropDown } from "react-icons/md";
import type { DAGRunPatchStates, DAGRunResponse } from
"openapi/requests/types.gen";
import { StateBadge } from "src/components/StateBadge";
-import { Menu } from "src/components/ui";
+import { Menu, Tooltip } from "src/components/ui";
import ActionButton from "src/components/ui/ActionButton";
import { allowedStates } from "../utils";
@@ -30,13 +31,32 @@ import MarkRunAsDialog from "./MarkRunAsDialog";
type Props = {
readonly dagRun: DAGRunResponse;
+ readonly isHotkeyEnabled?: boolean;
readonly withText?: boolean;
};
-const MarkRunAsButton = ({ dagRun, withText = true }: Props) => {
+const MarkRunAsButton = ({ dagRun, isHotkeyEnabled = false, withText = true }:
Props) => {
const { onClose, onOpen, open } = useDisclosure();
const [state, setState] = useState<DAGRunPatchStates>("success");
+ useHotkeys(
+ "shift+f",
+ () => {
+ setState("failed");
+ onOpen();
+ },
+ { enabled: isHotkeyEnabled && dagRun.state !== "failed" },
+ );
+
+ useHotkeys(
+ "shift+s",
+ () => {
+ setState("success");
+ onOpen();
+ },
+ { enabled: isHotkeyEnabled && dagRun.state !== "success" },
+ );
+
return (
<Box>
<Menu.Root positioning={{ gutter: 0, placement: "bottom" }}>
@@ -50,24 +70,39 @@ const MarkRunAsButton = ({ dagRun, withText = true }:
Props) => {
/>
</Menu.Trigger>
<Menu.Content>
- {allowedStates.map((menuState) => (
- <Menu.Item
- asChild
- disabled={dagRun.state === menuState}
- key={menuState}
- onClick={() => {
- if (dagRun.state !== menuState) {
- setState(menuState);
- onOpen();
- }
- }}
- value={menuState}
- >
- <StateBadge my={1} state={menuState}>
- {menuState}
- </StateBadge>
- </Menu.Item>
- ))}
+ {allowedStates.map((menuState) => {
+ const content =
+ menuState === "success"
+ ? "Press shift+s to mark as success"
+ : "Press shift+f to mark as failed";
+
+ return (
+ <Tooltip
+ closeDelay={100}
+ content={content}
+ disabled={!isHotkeyEnabled || dagRun.state === menuState}
+ key={menuState}
+ openDelay={100}
+ >
+ <Menu.Item
+ asChild
+ disabled={dagRun.state === menuState}
+ key={menuState}
+ onClick={() => {
+ if (dagRun.state !== menuState) {
+ setState(menuState);
+ onOpen();
+ }
+ }}
+ value={menuState}
+ >
+ <StateBadge my={1} state={menuState}>
+ {menuState}
+ </StateBadge>
+ </Menu.Item>
+ </Tooltip>
+ );
+ })}
</Menu.Content>
</Menu.Root>
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 395dfba27b3..e8ffcddd49a 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
@@ -18,26 +18,46 @@
*/
import { Box, useDisclosure } from "@chakra-ui/react";
import { useState } from "react";
+import { useHotkeys } from "react-hotkeys-hook";
import { MdArrowDropDown } from "react-icons/md";
import type { TaskInstanceResponse, TaskInstanceState } from
"openapi/requests/types.gen";
import { StateBadge } from "src/components/StateBadge";
-import { Menu } from "src/components/ui";
+import { Menu, Tooltip } from "src/components/ui";
import ActionButton from "src/components/ui/ActionButton";
import { allowedStates } from "../utils";
import MarkTaskInstanceAsDialog from "./MarkTaskInstanceAsDialog";
type Props = {
+ readonly isHotkeyEnabled?: boolean;
readonly taskInstance: TaskInstanceResponse;
readonly withText?: boolean;
};
-const MarkTaskInstanceAsButton = ({ taskInstance, withText = true }: Props) =>
{
+const MarkTaskInstanceAsButton = ({ isHotkeyEnabled = false, taskInstance,
withText = true }: Props) => {
const { onClose, onOpen, open } = useDisclosure();
const [state, setState] = useState<TaskInstanceState>("success");
+ useHotkeys(
+ "shift+f",
+ () => {
+ setState("failed");
+ onOpen();
+ },
+ { enabled: isHotkeyEnabled && taskInstance.state !== "failed" },
+ );
+
+ useHotkeys(
+ "shift+s",
+ () => {
+ setState("success");
+ onOpen();
+ },
+ { enabled: isHotkeyEnabled && taskInstance.state !== "success" },
+ );
+
return (
<Box>
<Menu.Root positioning={{ gutter: 0, placement: "bottom" }}>
@@ -51,24 +71,39 @@ const MarkTaskInstanceAsButton = ({ taskInstance, withText
= true }: Props) => {
/>
</Menu.Trigger>
<Menu.Content>
- {allowedStates.map((menuState) => (
- <Menu.Item
- asChild
- disabled={taskInstance.state === menuState}
- key={menuState}
- onClick={() => {
- if (taskInstance.state !== menuState) {
- setState(menuState);
- onOpen();
- }
- }}
- value={menuState}
- >
- <StateBadge my={1} state={menuState}>
- {menuState}
- </StateBadge>
- </Menu.Item>
- ))}
+ {allowedStates.map((menuState) => {
+ const content =
+ menuState === "success"
+ ? "Press shift+s to mark as success"
+ : "Press shift+f to mark as failed";
+
+ return (
+ <Tooltip
+ closeDelay={100}
+ content={content}
+ disabled={!isHotkeyEnabled || taskInstance.state === menuState}
+ key={menuState}
+ openDelay={100}
+ >
+ <Menu.Item
+ asChild
+ disabled={taskInstance.state === menuState}
+ key={menuState}
+ onClick={() => {
+ if (taskInstance.state !== menuState) {
+ setState(menuState);
+ onOpen();
+ }
+ }}
+ value={menuState}
+ >
+ <StateBadge my={1} state={menuState}>
+ {menuState}
+ </StateBadge>
+ </Menu.Item>
+ </Tooltip>
+ );
+ })}
</Menu.Content>
</Menu.Root>
diff --git a/airflow-core/src/airflow/ui/src/pages/Run/Header.tsx
b/airflow-core/src/airflow/ui/src/pages/Run/Header.tsx
index d5bd8d7a655..c412da36b9d 100644
--- a/airflow-core/src/airflow/ui/src/pages/Run/Header.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Run/Header.tsx
@@ -77,8 +77,8 @@ export const Header = ({
text={Boolean(dagRun.note) ? "Note" : "Add a note"}
withText={containerWidth > 700}
/>
- <ClearRunButton dagRun={dagRun} withText={containerWidth > 700} />
- <MarkRunAsButton dagRun={dagRun} withText={containerWidth > 700} />
+ <ClearRunButton dagRun={dagRun} isHotkeyEnabled
withText={containerWidth > 700} />
+ <MarkRunAsButton dagRun={dagRun} isHotkeyEnabled
withText={containerWidth > 700} />
</>
}
icon={<FiBarChart />}
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 40c35319b8f..702060ae285 100644
--- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx
@@ -98,8 +98,16 @@ export const Header = ({
text={Boolean(taskInstance.note) ? "Note" : "Add a note"}
withText={containerWidth > 700}
/>
- <ClearTaskInstanceButton taskInstance={taskInstance}
withText={containerWidth > 700} />
- <MarkTaskInstanceAsButton taskInstance={taskInstance}
withText={containerWidth > 700} />
+ <ClearTaskInstanceButton
+ isHotkeyEnabled
+ taskInstance={taskInstance}
+ withText={containerWidth > 700}
+ />
+ <MarkTaskInstanceAsButton
+ isHotkeyEnabled
+ taskInstance={taskInstance}
+ withText={containerWidth > 700}
+ />
</>
}
icon={<MdOutlineTask />}