Skip to content

Commit

Permalink
Implement task duration page in react. (#35863)
Browse files Browse the repository at this point in the history
* Implement task duration page in react.

Update icon to match existing task duration page.

Update with queued duration.

PR comments about variables and using moment to convert duration.

Add task row selection support.

Implement task duration page in react.

Update icon to match existing task duration page.

Add task row selection support.

Sort as per DagRun order in grid and add logical date as x-axis label.

Rearrange task selection tabs

fix group/mapped details and select task node if no run

Fix tests and remove unneeded rebase changes

Fix TI ordering and add dbl click to expand/collapse groups

Fix header nav and no TIs

Refactor TaskName, hide null tooltips, and add fake tab link

* Fix task name selection

---------

Co-authored-by: Brent Bovenzi <brent.bovenzi@gmail.com>
  • Loading branch information
tirkarthi and bbovenzi committed Feb 20, 2024
1 parent d944eb0 commit 440f1c8
Show file tree
Hide file tree
Showing 11 changed files with 388 additions and 60 deletions.
10 changes: 9 additions & 1 deletion airflow/www/static/js/components/ReactECharts.tsx
Expand Up @@ -28,6 +28,7 @@ export interface ReactEChartsProps {
settings?: SetOptionOpts;
style?: CSSProperties;
theme?: "light" | "dark";
events?: { [key: string]: (params: any) => void };
}

const ReactECharts = ({
Expand All @@ -36,6 +37,7 @@ const ReactECharts = ({
settings,
style,
theme,
events,
}: ReactEChartsProps) => {
const ref = useRef<HTMLDivElement>(null);

Expand Down Expand Up @@ -78,9 +80,15 @@ const ReactECharts = ({
const chartInstance = getInstanceByDom(ref.current);
if (chartInstance) {
chartInstance.setOption(option, settings);

if (events) {
Object.keys(events).forEach((key) => {
chartInstance.on(key, events[key]);
});
}
}
}
}, [option, settings, theme]);
}, [option, settings, theme, events]);

return (
<div
Expand Down
21 changes: 9 additions & 12 deletions airflow/www/static/js/dag/TaskName.test.tsx
Expand Up @@ -28,28 +28,25 @@ import TaskName from "./TaskName";

describe("Test TaskName", () => {
test("Displays a normal task name", () => {
const { getByText } = render(
<TaskName label="test" id="test" onToggle={() => {}} />,
{ wrapper: ChakraWrapper }
);
const { getByText } = render(<TaskName label="test" id="test" />, {
wrapper: ChakraWrapper,
});

expect(getByText("test")).toBeDefined();
});

test("Displays a mapped task name", () => {
const { getByText } = render(
<TaskName label="test" id="test" isMapped onToggle={() => {}} />,
{ wrapper: ChakraWrapper }
);
const { getByText } = render(<TaskName label="test" id="test" isMapped />, {
wrapper: ChakraWrapper,
});

expect(getByText("test [ ]")).toBeDefined();
});

test("Displays a group task name", () => {
const { getByText } = render(
<TaskName label="test" id="test" isGroup onToggle={() => {}} />,
{ wrapper: ChakraWrapper }
);
const { getByText } = render(<TaskName label="test" id="test" isGroup />, {
wrapper: ChakraWrapper,
});

expect(getByText("test")).toBeDefined();
});
Expand Down
51 changes: 25 additions & 26 deletions airflow/www/static/js/dag/TaskName.tsx
Expand Up @@ -17,14 +17,13 @@
* under the License.
*/

import React, { MouseEventHandler, CSSProperties } from "react";
import { Text, TextProps, useTheme } from "@chakra-ui/react";
import React, { CSSProperties } from "react";
import { Text, TextProps, useTheme, chakra } from "@chakra-ui/react";
import { FiChevronUp, FiArrowUpRight, FiArrowDownRight } from "react-icons/fi";

interface Props extends TextProps {
isGroup?: boolean;
isMapped?: boolean;
onToggle: MouseEventHandler<HTMLDivElement>;
isOpen?: boolean;
label: string;
id?: string;
Expand All @@ -35,12 +34,12 @@ interface Props extends TextProps {
const TaskName = ({
isGroup = false,
isMapped = false,
onToggle,
isOpen = false,
label,
id,
setupTeardownType,
isZoomedOut,
onClick,
...rest
}: Props) => {
const { colors } = useTheme();
Expand All @@ -51,34 +50,34 @@ const TaskName = ({
};
return (
<Text
cursor={isGroup ? "pointer" : undefined}
onClick={onToggle}
cursor="pointer"
data-testid={id}
width="100%"
color={colors.gray[800]}
fontSize={isZoomedOut ? 24 : undefined}
textAlign="justify"
{...rest}
>
{label}
{isMapped && " [ ]"}
{isGroup && (
<FiChevronUp
size={isZoomedOut ? 24 : 15}
strokeWidth={3}
style={{
transition: "transform 0.5s",
transform: `rotate(${isOpen ? 0 : 180}deg)`,
...iconStyle,
}}
/>
)}
{setupTeardownType === "setup" && (
<FiArrowUpRight size={isZoomedOut ? 24 : 15} style={iconStyle} />
)}
{setupTeardownType === "teardown" && (
<FiArrowDownRight size={isZoomedOut ? 24 : 15} style={iconStyle} />
)}
<chakra.span onClick={onClick}>
{label}
{isMapped && " [ ]"}
{isGroup && (
<FiChevronUp
size={isZoomedOut ? 24 : 15}
strokeWidth={3}
style={{
transition: "transform 0.5s",
transform: `rotate(${isOpen ? 0 : 180}deg)`,
...iconStyle,
}}
/>
)}
{setupTeardownType === "setup" && (
<FiArrowUpRight size={isZoomedOut ? 24 : 15} style={iconStyle} />
)}
{setupTeardownType === "teardown" && (
<FiArrowDownRight size={isZoomedOut ? 24 : 15} style={iconStyle} />
)}
</chakra.span>
</Text>
);
};
Expand Down
23 changes: 15 additions & 8 deletions airflow/www/static/js/dag/details/Header.tsx
Expand Up @@ -47,13 +47,18 @@ const Header = () => {
} = useSelection();
const dagRun = dagRuns.find((r) => r.runId === runId);

// clearSelection if the current selected dagRun is
// filtered out.
const group = getTask({ taskId, task: groups });

// If runId and/or taskId can't be found remove the selection
useEffect(() => {
if (runId && !dagRun) {
if (runId && !dagRun && taskId && !group) {
clearSelection();
} else if (runId && !dagRun) {
onSelect({ taskId });
} else if (taskId && !group) {
onSelect({ runId });
}
}, [clearSelection, dagRun, runId]);
}, [dagRun, taskId, group, runId, onSelect, clearSelection]);

let runLabel;
if (dagRun && runId) {
Expand All @@ -75,15 +80,13 @@ const Header = () => {
);
}

const group = getTask({ taskId, task: groups });

const lastIndex = taskId ? taskId.lastIndexOf(".") : null;
const taskName =
taskId && lastIndex ? taskId.substring(lastIndex + 1) : taskId;

const isDagDetails = !runId && !taskId;
const isRunDetails = !!(runId && !taskId);
const isTaskDetails = runId && taskId && mapIndex === undefined;
const isTaskDetails = !runId && taskId;
const isMappedTaskDetails = runId && taskId && mapIndex !== undefined;

return (
Expand All @@ -109,7 +112,11 @@ const Header = () => {
{taskId && (
<BreadcrumbItem isCurrentPage mt={4}>
<BreadcrumbLink
onClick={() => onSelect({ runId, taskId })}
onClick={() =>
mapIndex !== undefined
? onSelect({ runId, taskId })
: onSelect({ taskId })
}
_hover={isTaskDetails ? { cursor: "default" } : undefined}
>
<BreadcrumbText
Expand Down
2 changes: 1 addition & 1 deletion airflow/www/static/js/dag/details/graph/Node.tsx
Expand Up @@ -144,7 +144,7 @@ export const BaseNode = ({
label={taskName}
isOpen={isOpen}
isGroup={!!childCount}
onToggle={(e) => {
onClick={(e) => {
e.stopPropagation();
onToggleCollapse();
}}
Expand Down
2 changes: 1 addition & 1 deletion airflow/www/static/js/dag/details/graph/utils.ts
Expand Up @@ -62,7 +62,7 @@ export const flattenNodes = ({
if (!node.id.endsWith("join_id") && selected.runId) {
instance = group?.instances.find((ti) => ti.runId === selected.runId);
}
const isSelected = node.id === selected.taskId && !!instance;
const isSelected = node.id === selected.taskId;
const isActive =
instance && hoveredTaskState !== undefined
? hoveredTaskState === instance.state
Expand Down
40 changes: 31 additions & 9 deletions airflow/www/static/js/dag/details/index.tsx
Expand Up @@ -27,6 +27,7 @@ import {
TabPanels,
Tab,
Text,
Button,
} from "@chakra-ui/react";
import { useSearchParams } from "react-router-dom";

Expand All @@ -40,6 +41,7 @@ import {
MdCode,
MdOutlineViewTimeline,
MdSyncAlt,
MdHourglassBottom,
} from "react-icons/md";
import { BiBracket } from "react-icons/bi";
import URLSearchParamsWrapper from "src/utils/URLSearchParamWrapper";
Expand All @@ -60,6 +62,7 @@ import MarkRunAs from "./dagRun/MarkRunAs";
import ClearInstance from "./taskInstance/taskActions/ClearInstance";
import MarkInstanceAs from "./taskInstance/taskActions/MarkInstanceAs";
import XcomCollection from "./taskInstance/Xcom";
import TaskDetails from "./task";

const dagId = getMetaValue("dag_id")!;

Expand Down Expand Up @@ -92,7 +95,6 @@ const tabToIndex = (tab?: string) => {

const indexToTab = (
index: number,
taskId: string | null,
isTaskInstance: boolean,
isMappedTaskSummary: boolean
) => {
Expand Down Expand Up @@ -153,6 +155,7 @@ const Details = ({
!isGroup &&
!isMappedTaskSummary
);
const showTaskDetails = !!taskId && !runId;

const [searchParams, setSearchParams] = useSearchParams();
const tab = searchParams.get(TAB_PARAM) || undefined;
Expand All @@ -161,24 +164,20 @@ const Details = ({
const onChangeTab = useCallback(
(index: number) => {
const params = new URLSearchParamsWrapper(searchParams);
const newTab = indexToTab(
index,
taskId,
isTaskInstance,
isMappedTaskSummary
);
const newTab = indexToTab(index, isTaskInstance, isMappedTaskSummary);
if (newTab) params.set(TAB_PARAM, newTab);
else params.delete(TAB_PARAM);
setSearchParams(params);
},
[setSearchParams, searchParams, isTaskInstance, isMappedTaskSummary, taskId]
[setSearchParams, searchParams, isTaskInstance, isMappedTaskSummary]
);

useEffect(() => {
// Default to graph tab when navigating from a task instance to a group/dag/dagrun
const tabCount = runId && taskId && !isGroup ? 5 : 4;
if (tabCount === 4 && tabIndex > 3) {
onChangeTab(1);
if (!runId && taskId) onChangeTab(0);
else onChangeTab(1);
}
}, [runId, taskId, tabIndex, isGroup, onChangeTab]);

Expand Down Expand Up @@ -300,6 +299,28 @@ const Details = ({
</Text>
</Tab>
)}
{/* Match the styling of a tab but its actually a button */}
{!!taskId && !!runId && (
<Button
variant="unstyled"
display="flex"
alignItems="center"
fontSize="lg"
py={3}
// need to split pl and pr instead of px
pl={4}
pr={4}
mt="4px"
onClick={() => {
onSelect({ taskId });
}}
>
<MdHourglassBottom size={16} />
<Text as="strong" ml={1}>
Task Duration
</Text>
</Button>
)}
</TabList>
<TabPanels height="100%">
<TabPanel height="100%">
Expand All @@ -318,6 +339,7 @@ const Details = ({
/>
</>
)}
{showTaskDetails && <TaskDetails />}
</TabPanel>
<TabPanel p={0} height="100%">
<Graph
Expand Down

0 comments on commit 440f1c8

Please sign in to comment.