Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust graph node layout #37207

Merged
merged 2 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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