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;