This is an automated email from the ASF dual-hosted git repository.

bbovenzi 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 1d20bd9f23 Fix dimensions for datasets page (#27355)
1d20bd9f23 is described below

commit 1d20bd9f23e3feef1d4bd3b690c1cec9169eb584
Author: Brent Bovenzi <[email protected]>
AuthorDate: Wed Nov 2 18:07:21 2022 -0600

    Fix dimensions for datasets page (#27355)
---
 airflow/www/static/js/dag/Main.tsx              |   9 +-
 airflow/www/static/js/dag/grid/index.tsx        |   4 +-
 airflow/www/static/js/datasets/Graph/Legend.tsx |   4 +-
 airflow/www/static/js/datasets/Graph/index.tsx  | 135 ++++++++++--------------
 airflow/www/static/js/datasets/index.tsx        |  23 +++-
 airflow/www/static/js/utils/useContentHeight.ts |  51 +++++++++
 6 files changed, 133 insertions(+), 93 deletions(-)

diff --git a/airflow/www/static/js/dag/Main.tsx 
b/airflow/www/static/js/dag/Main.tsx
index 78e457d845..e9822ea794 100644
--- a/airflow/www/static/js/dag/Main.tsx
+++ b/airflow/www/static/js/dag/Main.tsx
@@ -34,6 +34,7 @@ import { isEmpty, debounce } from 'lodash';
 import useSelection from 'src/dag/useSelection';
 import { useGridData } from 'src/api';
 import { hoverDelay } from 'src/utils';
+import useContentHeight from 'src/utils/useContentHeight';
 
 import Details from './details';
 import Grid from './grid';
@@ -71,13 +72,7 @@ const Main = () => {
     onToggle();
   };
 
-  useEffect(() => {
-    if (contentRef.current) {
-      const topOffset = contentRef.current.offsetTop;
-      const footerHeight = 
parseInt(getComputedStyle(document.getElementsByTagName('body')[0]).paddingBottom.replace('px',
 ''), 10) || 0;
-      contentRef.current.style.height = `${window.innerHeight - topOffset - 
footerHeight}px`;
-    }
-  }, []);
+  useContentHeight(contentRef);
 
   const resize = useCallback((e: MouseEvent) => {
     const gridEl = gridRef.current;
diff --git a/airflow/www/static/js/dag/grid/index.tsx 
b/airflow/www/static/js/dag/grid/index.tsx
index a87a0955b2..b21e95bf8d 100644
--- a/airflow/www/static/js/dag/grid/index.tsx
+++ b/airflow/www/static/js/dag/grid/index.tsx
@@ -100,8 +100,8 @@ const Grid = ({ isPanelOpen = false, onPanelToggle, 
hoveredTaskState }: Props) =
       <Flex
         alignItems="center"
         justifyContent="space-between"
-        mb={2}
         p={1}
+        pb={2}
         backgroundColor="white"
         ref={buttonsRef}
       >
@@ -125,7 +125,7 @@ const Grid = ({ isPanelOpen = false, onPanelToggle, 
hoveredTaskState }: Props) =
         />
       </Flex>
       <Box
-        height={`calc(100% - ${dimensions?.borderBox.height}px)`}
+        height={`calc(100% - ${dimensions?.borderBox.height || 0}px)`}
         ref={scrollRef}
         overflow="auto"
         position="relative"
diff --git a/airflow/www/static/js/datasets/Graph/Legend.tsx 
b/airflow/www/static/js/datasets/Graph/Legend.tsx
index 1884a6aa4c..a787827e88 100644
--- a/airflow/www/static/js/datasets/Graph/Legend.tsx
+++ b/airflow/www/static/js/datasets/Graph/Legend.tsx
@@ -32,7 +32,7 @@ interface Props {
 }
 
 const Legend = ({ zoom, center }: Props) => (
-  <Flex justifyContent="space-between" alignItems="center">
+  <Flex height="100%" flexDirection="column" justifyContent="space-between">
     <Box>
       <IconButton
         onClick={zoom.reset}
@@ -55,7 +55,7 @@ const Legend = ({ zoom, center }: Props) => (
       backgroundColor="white"
       p={2}
       borderColor="gray.200"
-      borderLeftWidth={1}
+      borderRightWidth={1}
       borderTopWidth={1}
     >
       <Text>Legend</Text>
diff --git a/airflow/www/static/js/datasets/Graph/index.tsx 
b/airflow/www/static/js/datasets/Graph/index.tsx
index c49c902081..947be32959 100644
--- a/airflow/www/static/js/datasets/Graph/index.tsx
+++ b/airflow/www/static/js/datasets/Graph/index.tsx
@@ -17,11 +17,10 @@
  * under the License.
  */
 
-import React, { useState, useEffect, RefObject } from 'react';
+import React, { RefObject } from 'react';
 import { Box, Spinner } from '@chakra-ui/react';
 import { Zoom } from '@visx/zoom';
 import { Group } from '@visx/group';
-import { debounce } from 'lodash';
 
 import { useDatasetDependencies } from 'src/api';
 
@@ -32,27 +31,14 @@ import Legend from './Legend';
 interface Props {
   onSelect: (datasetId: string) => void;
   selectedUri: string | null;
+  height: number;
+  width: number;
 }
 
-const Graph = ({ onSelect, selectedUri }: Props) => {
+const Graph = ({
+  onSelect, selectedUri, height, width,
+}: Props) => {
   const { data, isLoading } = useDatasetDependencies();
-  const [dimensions, setDimensions] = useState({
-    height: window.innerHeight,
-    width: window.innerWidth,
-  });
-
-  // Reset the graph div when the window size changes
-  useEffect(() => {
-    const handleResize = debounce(() => {
-      setDimensions({
-        height: window.innerHeight,
-        width: window.innerWidth,
-      });
-    }, 200);
-
-    window.addEventListener('resize', handleResize);
-    return () => window.removeEventListener('resize', handleResize);
-  });
 
   if (isLoading && !data) return <Spinner />;
   if (!data) return null;
@@ -75,65 +61,60 @@ const Graph = ({ onSelect, selectedUri }: Props) => {
       selectedEdges.some(({ sources, targets }) => (
         sources[0] === n.id || targets[0] === n.id))));
 
-  const width = dimensions.width - 600 || 200;
-  const height = dimensions.height - 125 || 200;
-
   return (
-    <Box position="relative" alignSelf="center" borderColor="gray.200" 
borderWidth={1}>
-      <Zoom
-        width={width}
-        height={height}
-        scaleXMin={1 / 4}
-        scaleXMax={1}
-        scaleYMin={1 / 4}
-        scaleYMax={1}
-        initialTransformMatrix={initialTransform}
-      >
-        {(zoom) => (
-          <Box>
-            <svg
-              id="GRAPH"
-              width={width}
-              height={height}
-              ref={zoom.containerRef as RefObject<SVGSVGElement>}
-              style={{ cursor: zoom.isDragging ? 'grabbing' : 'grab', 
touchAction: 'none' }}
-            >
-              <g transform={zoom.toString()}>
-                <g height={data.height} width={data.width}>
-                  {data.edges.map((edge) => (
-                    <Edge
-                      key={edge.id}
-                      edge={edge}
-                      isSelected={selectedEdges.some((e) => e.id === edge.id)}
-                    />
-                  ))}
-                  {data.children.map((node) => (
-                    <Node
-                      key={node.id}
-                      node={node}
-                      onSelect={onSelect}
-                      isSelected={node.id === `dataset:${selectedUri}`}
-                      isHighlighted={highlightedNodes.some((n) => n.id === 
node.id)}
-                    />
-                  ))}
-                </g>
-              </g>
-              <Group top={height - 50} left={0} height={50} width={width}>
-                <foreignObject width={width} height={50}>
-                  <Legend
-                    zoom={zoom}
-                    center={() => zoom.translateTo({
-                      x: (width - (data.width ?? 0)) / 2,
-                      y: (height - (data.height ?? 0)) / 2,
-                    })}
+    <Zoom
+      width={width}
+      height={height}
+      scaleXMin={1 / 4}
+      scaleXMax={1}
+      scaleYMin={1 / 4}
+      scaleYMax={1}
+      initialTransformMatrix={initialTransform}
+    >
+      {(zoom) => (
+        <Box>
+          <svg
+            id="GRAPH"
+            width={width}
+            height={height}
+            ref={zoom.containerRef as RefObject<SVGSVGElement>}
+            style={{ cursor: zoom.isDragging ? 'grabbing' : 'grab', 
touchAction: 'none' }}
+          >
+            <g transform={zoom.toString()}>
+              <g height={data.height} width={data.width}>
+                {data.edges.map((edge) => (
+                  <Edge
+                    key={edge.id}
+                    edge={edge}
+                    isSelected={selectedEdges.some((e) => e.id === edge.id)}
+                  />
+                ))}
+                {data.children.map((node) => (
+                  <Node
+                    key={node.id}
+                    node={node}
+                    onSelect={onSelect}
+                    isSelected={node.id === `dataset:${selectedUri}`}
+                    isHighlighted={highlightedNodes.some((n) => n.id === 
node.id)}
                   />
-                </foreignObject>
-              </Group>
-            </svg>
-          </Box>
-        )}
-      </Zoom>
-    </Box>
+                ))}
+              </g>
+            </g>
+            <Group top={height - 100} left={0} height={100} width={width}>
+              <foreignObject width={150} height={100}>
+                <Legend
+                  zoom={zoom}
+                  center={() => zoom.translateTo({
+                    x: (width - (data.width ?? 0)) / 2,
+                    y: (height - (data.height ?? 0)) / 2,
+                  })}
+                />
+              </foreignObject>
+            </Group>
+          </svg>
+        </Box>
+      )}
+    </Zoom>
   );
 };
 
diff --git a/airflow/www/static/js/datasets/index.tsx 
b/airflow/www/static/js/datasets/index.tsx
index 6e32995e92..9a993c0562 100644
--- a/airflow/www/static/js/datasets/index.tsx
+++ b/airflow/www/static/js/datasets/index.tsx
@@ -19,13 +19,14 @@
 
 /* global document */
 
-import React from 'react';
+import React, { useRef } from 'react';
 import { createRoot } from 'react-dom/client';
 import createCache from '@emotion/cache';
 import { useSearchParams } from 'react-router-dom';
-import { Flex, Box } from '@chakra-ui/react';
+import { Flex, Box, useDimensions } from '@chakra-ui/react';
 
 import App from 'src/App';
+import useContentHeight from 'src/utils/useContentHeight';
 
 import DatasetsList from './List';
 import DatasetDetails from './Details';
@@ -44,6 +45,9 @@ const DATASET_URI = 'uri';
 
 const Datasets = () => {
   const [searchParams, setSearchParams] = useSearchParams();
+  const contentRef = useRef<HTMLDivElement>(null);
+  const graphRef = useRef<HTMLDivElement>(null);
+  const dimensions = useDimensions(graphRef, true);
 
   const onBack = () => {
     searchParams.delete(DATASET_URI);
@@ -57,14 +61,23 @@ const Datasets = () => {
 
   const datasetUri = decodeURIComponent(searchParams.get(DATASET_URI) || '');
 
+  useContentHeight(contentRef);
+
   return (
-    <Flex alignItems="flex-start" justifyContent="space-between">
-      <Box width="600px" height="calc(100vh - 125px)" overflowY="scroll">
+    <Flex alignItems="flex-start" justifyContent="space-between" 
ref={contentRef}>
+      <Box minWidth="450px" height="100%" overflowY="scroll">
         {datasetUri
           ? <DatasetDetails uri={datasetUri} onBack={onBack} />
           : <DatasetsList onSelect={onSelect} />}
       </Box>
-      <Graph selectedUri={datasetUri} onSelect={onSelect} />
+      <Box flex={1} ref={graphRef} height="100%" borderColor="gray.200" 
borderWidth={1}>
+        <Graph
+          selectedUri={datasetUri}
+          onSelect={onSelect}
+          height={dimensions?.contentBox.height || 0}
+          width={dimensions?.contentBox.width || 0}
+        />
+      </Box>
     </Flex>
   );
 };
diff --git a/airflow/www/static/js/utils/useContentHeight.ts 
b/airflow/www/static/js/utils/useContentHeight.ts
new file mode 100644
index 0000000000..9eb585899a
--- /dev/null
+++ b/airflow/www/static/js/utils/useContentHeight.ts
@@ -0,0 +1,51 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* global document */
+
+import React, { useEffect } from 'react';
+
+const useContentHeight = (contentRef: React.RefObject<HTMLDivElement>) => {
+  useEffect(() => {
+    const calculateHeight = () => {
+      if (contentRef.current) {
+        const topOffset = contentRef.current.offsetTop;
+        const footerHeight = 
parseInt(getComputedStyle(document.getElementsByTagName('body')[0]).paddingBottom.replace('px',
 ''), 10) || 0;
+        const newHeight = window.innerHeight - topOffset - footerHeight;
+        const newHeightPx = `${newHeight}px`;
+
+        // only set a new height if it has changed
+        if (newHeightPx !== contentRef.current.style.height) {
+          // keep a minimum usable height of 300px
+          contentRef.current.style.height = newHeight > 300 ? newHeightPx : 
'300px';
+        }
+      }
+    };
+    // set height on load
+    calculateHeight();
+
+    // set height on window resize
+    window.addEventListener('resize', calculateHeight);
+    return () => {
+      window.removeEventListener('resize', calculateHeight);
+    };
+  }, [contentRef]);
+};
+
+export default useContentHeight;

Reply via email to