This is an automated email from the ASF dual-hosted git repository.
jedcunningham 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 7ea7abc75d Adjust graph node layout (#37207)
7ea7abc75d is described below
commit 7ea7abc75d9bb8349e95fc330b43657950837d30
Author: Brent Bovenzi <[email protected]>
AuthorDate: Tue Feb 6 17:24:49 2024 -0500
Adjust graph node layout (#37207)
---
airflow/www/static/js/components/Graph/Edge.tsx | 6 +-
.../www/static/js/dag/details/graph/Node.test.tsx | 1 +
airflow/www/static/js/dag/details/graph/Node.tsx | 79 ++++++++++++++--------
airflow/www/static/js/dag/details/graph/index.tsx | 14 ++++
airflow/www/static/js/dag/details/graph/utils.ts | 8 +++
airflow/www/static/js/types/index.ts | 1 +
6 files changed, 80 insertions(+), 29 deletions(-)
diff --git a/airflow/www/static/js/components/Graph/Edge.tsx
b/airflow/www/static/js/components/Graph/Edge.tsx
index 3d9e144795..99b6259055 100644
--- a/airflow/www/static/js/components/Graph/Edge.tsx
+++ b/airflow/www/static/js/components/Graph/Edge.tsx
@@ -32,6 +32,10 @@ const CustomEdge = ({ data }: EdgeProps) => {
const { colors } = useTheme();
if (!data) return null;
const { rest } = data;
+ let strokeWidth = 2;
+ if (rest.isSelected) strokeWidth = 3;
+ if (rest.isZoomedOut) strokeWidth = 5;
+ if (rest.isZoomedOut && rest.isSelected) strokeWidth = 7;
return (
<>
{rest?.labels?.map(({ id, x, y, text, width, height }) => {
@@ -48,7 +52,7 @@ const CustomEdge = ({ data }: EdgeProps) => {
<LinePath
key={s.id}
stroke={rest.isSelected ? colors.blue[400] : colors.gray[400]}
- strokeWidth={rest.isSelected ? 3 : 2}
+ strokeWidth={strokeWidth}
x={(d) => d.x || 0}
y={(d) => d.y || 0}
data={[s.startPoint, ...(s.bendPoints || []), s.endPoint]}
diff --git a/airflow/www/static/js/dag/details/graph/Node.test.tsx
b/airflow/www/static/js/dag/details/graph/Node.test.tsx
index 6e1a4bca13..251f3c6aeb 100644
--- a/airflow/www/static/js/dag/details/graph/Node.test.tsx
+++ b/airflow/www/static/js/dag/details/graph/Node.test.tsx
@@ -52,6 +52,7 @@ const mockNode: NodeProps<CustomNodeProps> = {
latestDagRunId: "run_id",
onToggleCollapse: () => {},
isActive: true,
+ isZoomedOut: false,
},
selected: false,
zIndex: 0,
diff --git a/airflow/www/static/js/dag/details/graph/Node.tsx
b/airflow/www/static/js/dag/details/graph/Node.tsx
index 6c72f041ac..7be5ae7b88 100644
--- a/airflow/www/static/js/dag/details/graph/Node.tsx
+++ b/airflow/www/static/js/dag/details/graph/Node.tsx
@@ -46,6 +46,7 @@ export interface CustomNodeProps {
setupTeardownType?: "setup" | "teardown";
labelStyle?: string;
style?: string;
+ isZoomedOut: boolean;
}
export const BaseNode = ({
@@ -65,6 +66,7 @@ export const BaseNode = ({
setupTeardownType,
labelStyle,
style,
+ isZoomedOut,
},
}: NodeProps<CustomNodeProps>) => {
const { colors } = useTheme();
@@ -92,6 +94,8 @@ export const BaseNode = ({
if (labelStyle) {
[, operatorTextColor] = labelStyle.split(":");
}
+ if (!operatorTextColor || operatorTextColor === "#000;")
+ operatorTextColor = "gray.500";
const nodeBorderColor =
instance?.state && stateColors[instance.state]
@@ -111,10 +115,15 @@ export const BaseNode = ({
openDelay={hoverDelay}
>
<Box
- borderRadius={5}
- borderWidth={isSelected ? 2.5 : 1.5}
+ borderRadius={isZoomedOut ? 10 : 5}
+ borderWidth={(isSelected ? 4 : 2) * (isZoomedOut ? 3 : 1)}
borderColor={nodeBorderColor}
- bg={isSelected ? "blue.50" : bg}
+ bg={
+ !task.children?.length && operatorBG
+ ? // Fade the operator color to clash less with the task instance
status
+ `color-mix(in srgb, ${operatorBG.replace(";", "")} 80%, white)`
+ : bg
+ }
height={`${height}px`}
width={`${width}px`}
cursor={latestDagRunId ? "cursor" : "default"}
@@ -134,13 +143,16 @@ export const BaseNode = ({
justifyContent="space-between"
width={width}
p={2}
- flexWrap="wrap"
+ flexWrap={isZoomedOut ? "nowrap" : "wrap"}
+ flexDirection={isZoomedOut ? "row" : "column"}
>
<Flex flexDirection="column" width="100%">
<Flex
justifyContent="space-between"
alignItems="center"
width="100%"
+ fontSize={isZoomedOut ? 25 : undefined}
+ fontWeight="bold"
>
<Text noOfLines={1} maxWidth={`calc(${width}px - 8px)`}>
{taskName}
@@ -152,37 +164,48 @@ export const BaseNode = ({
<ImArrowDownRight2 size={15} color={colors.gray[800]} />
)}
</Flex>
- {!!instance && instance.state && (
- <Flex alignItems="center">
- <SimpleStatus state={instance.state} />
- <Text ml={2} color="gray.500" fontSize="lg">
- {instance.state}
- </Text>
- </Flex>
- )}
- {task?.operator && (
- <Text
- noOfLines={1}
- maxWidth={`calc(${width}px - 12px)`}
- fontWeight={400}
- fontSize="md"
- width="fit-content"
- borderRadius={5}
- bg={operatorBG}
- color={operatorTextColor || "gray.500"}
- px={1}
- >
- {task.operator}
- </Text>
+ {!isZoomedOut && (
+ <>
+ {!!instance && instance.state && (
+ <Flex alignItems="center">
+ <SimpleStatus state={instance.state} />
+ <Text
+ ml={2}
+ color="gray.500"
+ fontWeight={400}
+ fontSize="md"
+ >
+ {instance.state}
+ </Text>
+ </Flex>
+ )}
+ {task?.operator && (
+ <Text
+ noOfLines={1}
+ maxWidth={`calc(${width}px - 12px)`}
+ fontWeight={400}
+ fontSize="md"
+ width="fit-content"
+ color={operatorTextColor}
+ px={1}
+ >
+ {task.operator}
+ </Text>
+ )}
+ </>
)}
</Flex>
{!!childCount && (
<Text
+ noOfLines={1}
+ fontSize={isZoomedOut ? 25 : undefined}
color="blue.600"
cursor="pointer"
+ width="150px"
+ fontWeight="bold"
// Increase the target area to expand/collapse a group
- p={3}
- m={-3}
+ p={2}
+ m={-2}
onClick={(e) => {
e.stopPropagation();
onToggleCollapse();
diff --git a/airflow/www/static/js/dag/details/graph/index.tsx
b/airflow/www/static/js/dag/details/graph/index.tsx
index 29e07d65aa..4fb3d21f6c 100644
--- a/airflow/www/static/js/dag/details/graph/index.tsx
+++ b/airflow/www/static/js/dag/details/graph/index.tsx
@@ -26,6 +26,8 @@ import ReactFlow, {
MiniMap,
useReactFlow,
Panel,
+ useOnViewportChange,
+ Viewport,
} from "reactflow";
import { useGraphData, useGridData } from "src/api";
@@ -51,6 +53,7 @@ const Graph = ({ openGroupIds, onToggleGroups,
hoveredTaskState }: Props) => {
const { data } = useGraphData();
const [arrange, setArrange] = useState(data?.arrange || "LR");
const [hasRendered, setHasRendered] = useState(false);
+ const [isZoomedOut, setIsZoomedOut] = useState(false);
useEffect(() => {
setArrange(data?.arrange || "LR");
@@ -71,6 +74,13 @@ const Graph = ({ openGroupIds, onToggleGroups,
hoveredTaskState }: Props) => {
const latestDagRunId = dagRuns[dagRuns.length - 1]?.runId;
const offsetTop = useOffsetTop(graphRef);
+ useOnViewportChange({
+ onEnd: (viewport: Viewport) => {
+ if (viewport.zoom < 0.5 && !isZoomedOut) setIsZoomedOut(true);
+ if (viewport.zoom >= 0.5 && isZoomedOut) setIsZoomedOut(false);
+ },
+ });
+
const { nodes, edges: nodeEdges } = useMemo(
() =>
flattenNodes({
@@ -81,6 +91,7 @@ const Graph = ({ openGroupIds, onToggleGroups,
hoveredTaskState }: Props) => {
latestDagRunId,
groups,
hoveredTaskState,
+ isZoomedOut,
}),
[
graphData?.children,
@@ -90,6 +101,7 @@ const Graph = ({ openGroupIds, onToggleGroups,
hoveredTaskState }: Props) => {
latestDagRunId,
groups,
hoveredTaskState,
+ isZoomedOut,
]
);
@@ -97,6 +109,7 @@ const Graph = ({ openGroupIds, onToggleGroups,
hoveredTaskState }: Props) => {
useEffect(() => {
if (hasRendered) {
const zoom = getZoom();
+ if (zoom < 0.5) setIsZoomedOut(true);
fitView({
duration: 750,
nodes: selected.taskId ? [{ id: selected.taskId }] : undefined,
@@ -116,6 +129,7 @@ const Graph = ({ openGroupIds, onToggleGroups,
hoveredTaskState }: Props) => {
edges: flatEdges,
nodes,
selectedTaskId: selected.taskId,
+ isZoomedOut,
});
return (
diff --git a/airflow/www/static/js/dag/details/graph/utils.ts
b/airflow/www/static/js/dag/details/graph/utils.ts
index ac940d46c7..901b47ab18 100644
--- a/airflow/www/static/js/dag/details/graph/utils.ts
+++ b/airflow/www/static/js/dag/details/graph/utils.ts
@@ -36,6 +36,7 @@ interface FlattenNodesProps {
openGroupIds: string[];
onToggleGroups: (groupIds: string[]) => void;
hoveredTaskState?: string | null;
+ isZoomedOut: boolean;
}
// Generate a flattened list of nodes for react-flow to render
@@ -48,6 +49,7 @@ export const flattenNodes = ({
openGroupIds,
parent,
hoveredTaskState,
+ isZoomedOut,
}: FlattenNodesProps) => {
let nodes: ReactFlowNode<CustomNodeProps>[] = [];
let edges: ElkExtendedEdge[] = [];
@@ -76,6 +78,7 @@ export const flattenNodes = ({
isSelected,
latestDagRunId,
isActive,
+ isZoomedOut,
onToggleCollapse: () => {
let newGroupIds = [];
if (!node.value.isOpen) {
@@ -115,6 +118,7 @@ export const flattenNodes = ({
openGroupIds,
parent: newNode,
hoveredTaskState,
+ isZoomedOut,
});
nodes = [...nodes, ...childNodes];
edges = [...edges, ...childEdges];
@@ -153,6 +157,7 @@ interface BuildEdgesProps {
edges?: Edge[];
nodes: ReactFlowNode<CustomNodeProps>[];
selectedTaskId?: string | null;
+ isZoomedOut?: boolean;
}
// Format edge data to what react-flow needs to render successfully
@@ -160,6 +165,7 @@ export const buildEdges = ({
edges = [],
nodes,
selectedTaskId,
+ isZoomedOut,
}: BuildEdgesProps) =>
edges
.map((edge) => ({
@@ -184,6 +190,7 @@ export const buildEdges = ({
...e,
data: {
rest: {
+ isZoomedOut,
...e.data.rest,
labels: e.data.rest.labels?.map((l) =>
l.x && l.y ? { ...l, x: l.x + parentX, y: l.y + parentY } : l
@@ -215,6 +222,7 @@ export const buildEdges = ({
rest: {
...e.data.rest,
isSelected,
+ isZoomedOut,
},
},
};
diff --git a/airflow/www/static/js/types/index.ts
b/airflow/www/static/js/types/index.ts
index a75c1ace23..b9ada90370 100644
--- a/airflow/www/static/js/types/index.ts
+++ b/airflow/www/static/js/types/index.ts
@@ -165,6 +165,7 @@ export interface EdgeData {
layoutOptions?: LayoutOptions;
isSetupTeardown?: boolean;
parentNode?: string;
+ isZoomedOut?: boolean;
};
}