Skip to content

Commit

Permalink
Adjust graph node layout (apache#37207)
Browse files Browse the repository at this point in the history
  • Loading branch information
bbovenzi authored and abhishekbhakat committed Mar 5, 2024
1 parent 2b31847 commit 492a2db
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 29 deletions.
6 changes: 5 additions & 1 deletion airflow/www/static/js/components/Graph/Edge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand All @@ -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]}
Expand Down
1 change: 1 addition & 0 deletions airflow/www/static/js/dag/details/graph/Node.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const mockNode: NodeProps<CustomNodeProps> = {
latestDagRunId: "run_id",
onToggleCollapse: () => {},
isActive: true,
isZoomedOut: false,
},
selected: false,
zIndex: 0,
Expand Down
79 changes: 51 additions & 28 deletions airflow/www/static/js/dag/details/graph/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface CustomNodeProps {
setupTeardownType?: "setup" | "teardown";
labelStyle?: string;
style?: string;
isZoomedOut: boolean;
}

export const BaseNode = ({
Expand All @@ -65,6 +66,7 @@ export const BaseNode = ({
setupTeardownType,
labelStyle,
style,
isZoomedOut,
},
}: NodeProps<CustomNodeProps>) => {
const { colors } = useTheme();
Expand Down Expand Up @@ -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]
Expand All @@ -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"}
Expand All @@ -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}
Expand All @@ -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();
Expand Down
14 changes: 14 additions & 0 deletions airflow/www/static/js/dag/details/graph/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import ReactFlow, {
MiniMap,
useReactFlow,
Panel,
useOnViewportChange,
Viewport,
} from "reactflow";

import { useGraphData, useGridData } from "src/api";
Expand All @@ -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");
Expand All @@ -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({
Expand All @@ -81,6 +91,7 @@ const Graph = ({ openGroupIds, onToggleGroups, hoveredTaskState }: Props) => {
latestDagRunId,
groups,
hoveredTaskState,
isZoomedOut,
}),
[
graphData?.children,
Expand All @@ -90,13 +101,15 @@ const Graph = ({ openGroupIds, onToggleGroups, hoveredTaskState }: Props) => {
latestDagRunId,
groups,
hoveredTaskState,
isZoomedOut,
]
);

// Zoom to/from nodes when changing selection, maintain zoom level when changing task selection
useEffect(() => {
if (hasRendered) {
const zoom = getZoom();
if (zoom < 0.5) setIsZoomedOut(true);
fitView({
duration: 750,
nodes: selected.taskId ? [{ id: selected.taskId }] : undefined,
Expand All @@ -116,6 +129,7 @@ const Graph = ({ openGroupIds, onToggleGroups, hoveredTaskState }: Props) => {
edges: flatEdges,
nodes,
selectedTaskId: selected.taskId,
isZoomedOut,
});

return (
Expand Down
8 changes: 8 additions & 0 deletions airflow/www/static/js/dag/details/graph/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -48,6 +49,7 @@ export const flattenNodes = ({
openGroupIds,
parent,
hoveredTaskState,
isZoomedOut,
}: FlattenNodesProps) => {
let nodes: ReactFlowNode<CustomNodeProps>[] = [];
let edges: ElkExtendedEdge[] = [];
Expand Down Expand Up @@ -76,6 +78,7 @@ export const flattenNodes = ({
isSelected,
latestDagRunId,
isActive,
isZoomedOut,
onToggleCollapse: () => {
let newGroupIds = [];
if (!node.value.isOpen) {
Expand Down Expand Up @@ -115,6 +118,7 @@ export const flattenNodes = ({
openGroupIds,
parent: newNode,
hoveredTaskState,
isZoomedOut,
});
nodes = [...nodes, ...childNodes];
edges = [...edges, ...childEdges];
Expand Down Expand Up @@ -153,13 +157,15 @@ interface BuildEdgesProps {
edges?: Edge[];
nodes: ReactFlowNode<CustomNodeProps>[];
selectedTaskId?: string | null;
isZoomedOut?: boolean;
}

// Format edge data to what react-flow needs to render successfully
export const buildEdges = ({
edges = [],
nodes,
selectedTaskId,
isZoomedOut,
}: BuildEdgesProps) =>
edges
.map((edge) => ({
Expand All @@ -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
Expand Down Expand Up @@ -215,6 +222,7 @@ export const buildEdges = ({
rest: {
...e.data.rest,
isSelected,
isZoomedOut,
},
},
};
Expand Down
1 change: 1 addition & 0 deletions airflow/www/static/js/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export interface EdgeData {
layoutOptions?: LayoutOptions;
isSetupTeardown?: boolean;
parentNode?: string;
isZoomedOut?: boolean;
};
}

Expand Down

0 comments on commit 492a2db

Please sign in to comment.