You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by bb...@apache.org on 2022/11/17 20:10:47 UTC

[airflow] branch main updated: reset commits, clean submodules (#27560)

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 65bfea2a20 reset commits, clean submodules (#27560)
65bfea2a20 is described below

commit 65bfea2a20830baa10d2e1e8328c07a7a11bbb0c
Author: Brent Bovenzi <br...@astronomer.io>
AuthorDate: Thu Nov 17 14:10:35 2022 -0600

    reset commits, clean submodules (#27560)
---
 airflow/www/static/js/dag/Main.tsx                 |  18 +-
 airflow/www/static/js/dag/details/Dag.tsx          | 137 +++++++-------
 airflow/www/static/js/dag/details/dagRun/index.tsx | 169 +++++++++--------
 airflow/www/static/js/dag/details/index.tsx        |  10 +-
 .../js/dag/details/taskInstance/Logs/index.tsx     | 207 +++++++++++----------
 .../www/static/js/dag/details/taskInstance/Nav.tsx |  20 +-
 .../static/js/dag/details/taskInstance/index.tsx   |  27 ++-
 airflow/www/static/js/dag/grid/index.tsx           |  16 +-
 airflow/www/static/js/datasets/index.tsx           |   7 +-
 .../{useContentHeight.ts => useOffsetHeight.tsx}   |  36 ++--
 10 files changed, 359 insertions(+), 288 deletions(-)

diff --git a/airflow/www/static/js/dag/Main.tsx b/airflow/www/static/js/dag/Main.tsx
index e9822ea794..26f7e22346 100644
--- a/airflow/www/static/js/dag/Main.tsx
+++ b/airflow/www/static/js/dag/Main.tsx
@@ -34,7 +34,6 @@ 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';
@@ -44,11 +43,14 @@ import LegendRow from './nav/LegendRow';
 const detailsPanelKey = 'hideDetailsPanel';
 const minPanelWidth = 300;
 
+const gridWidthKey = 'grid-width';
+const saveWidth = debounce((w) => localStorage.setItem(gridWidthKey, w), hoverDelay);
+
 const Main = () => {
   const { data: { groups }, isLoading } = useGridData();
   const resizeRef = useRef<HTMLDivElement>(null);
   const gridRef = useRef<HTMLDivElement>(null);
-  const contentRef = useRef<HTMLDivElement>(null);
+  const detailsRef = useRef<HTMLDivElement>(null);
   const isPanelOpen = localStorage.getItem(detailsPanelKey) !== 'true';
   const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: isPanelOpen });
   const { clearSelection } = useSelection();
@@ -62,6 +64,8 @@ const Main = () => {
     onStatusHover.cancel();
   };
 
+  const gridWidth = localStorage.getItem(gridWidthKey) || undefined;
+
   const onPanelToggle = () => {
     if (!isOpen) {
       localStorage.setItem(detailsPanelKey, 'false');
@@ -72,12 +76,12 @@ const Main = () => {
     onToggle();
   };
 
-  useContentHeight(contentRef);
-
   const resize = useCallback((e: MouseEvent) => {
     const gridEl = gridRef.current;
     if (gridEl && e.x > minPanelWidth && e.x < window.innerWidth - minPanelWidth) {
-      gridEl.style.width = `${e.x}px`;
+      const width = `${e.x}px`;
+      gridEl.style.width = width;
+      saveWidth(width);
     }
   }, [gridRef]);
 
@@ -106,7 +110,7 @@ const Main = () => {
       <FilterBar />
       <LegendRow onStatusHover={onStatusHover} onStatusLeave={onStatusLeave} />
       <Divider mb={5} borderBottomWidth={2} />
-      <Flex ref={contentRef} overflow="hidden">
+      <Flex>
         {isLoading || isEmpty(groups)
           ? (<Spinner />)
           : (
@@ -116,6 +120,7 @@ const Main = () => {
                 flex={isOpen ? undefined : 1}
                 ref={gridRef}
                 height="100%"
+                width={gridWidth}
               >
                 <Grid
                   isPanelOpen={isOpen}
@@ -138,6 +143,7 @@ const Main = () => {
                     zIndex={1}
                     bg="white"
                     height="100%"
+                    ref={detailsRef}
                   >
                     <Details />
                   </Box>
diff --git a/airflow/www/static/js/dag/details/Dag.tsx b/airflow/www/static/js/dag/details/Dag.tsx
index 4973555f42..c45d48adb5 100644
--- a/airflow/www/static/js/dag/details/Dag.tsx
+++ b/airflow/www/static/js/dag/details/Dag.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import React, { ReactNode } from 'react';
+import React, { ReactNode, useRef } from 'react';
 import {
   Table,
   Tbody,
@@ -28,6 +28,7 @@ import {
   Flex,
   Heading,
   Text,
+  Box,
 } from '@chakra-ui/react';
 import { mean } from 'lodash';
 
@@ -35,6 +36,7 @@ import { getDuration, formatDuration } from 'src/datetime_utils';
 import { finalStatesMap, getMetaValue, getTaskSummary } from 'src/utils';
 import { useGridData } from 'src/api';
 import Time from 'src/components/Time';
+import useOffsetHeight from 'src/utils/useOffsetHeight';
 import type { TaskState } from 'src/types';
 
 import { SimpleStatus } from '../StatusBox';
@@ -43,6 +45,8 @@ const dagDetailsUrl = getMetaValue('dag_details_url');
 
 const Dag = () => {
   const { data: { dagRuns, groups } } = useGridData();
+  const detailsRef = useRef<HTMLDivElement>(null);
+  const offsetHeight = useOffsetHeight(detailsRef);
 
   const taskSummary = getTaskSummary({ task: groups });
   const numMap = finalStatesMap();
@@ -89,84 +93,91 @@ const Dag = () => {
       <Button as={Link} variant="ghost" colorScheme="blue" href={dagDetailsUrl}>
         DAG Details
       </Button>
-      <Table variant="striped">
-        <Tbody>
-          {durations.length > 0 && (
-          <>
-            <Tr borderBottomWidth={2} borderBottomColor="gray.300">
-              <Td><Heading size="sm">DAG Runs Summary</Heading></Td>
-              <Td />
-            </Tr>
-            <Tr>
-              <Td>Total Runs Displayed</Td>
-              <Td>
-                {durations.length}
-              </Td>
-            </Tr>
-            {stateSummary}
-            {firstStart && (
+      <Box
+        height="100%"
+        maxHeight={offsetHeight}
+        ref={detailsRef}
+        overflowY="auto"
+      >
+        <Table variant="striped">
+          <Tbody>
+            {durations.length > 0 && (
+            <>
+              <Tr borderBottomWidth={2} borderBottomColor="gray.300">
+                <Td><Heading size="sm">DAG Runs Summary</Heading></Td>
+                <Td />
+              </Tr>
               <Tr>
-                <Td>First Run Start</Td>
+                <Td>Total Runs Displayed</Td>
                 <Td>
-                  <Time dateTime={firstStart} />
+                  {durations.length}
+                </Td>
+              </Tr>
+              {stateSummary}
+              {firstStart && (
+                <Tr>
+                  <Td>First Run Start</Td>
+                  <Td>
+                    <Time dateTime={firstStart} />
+                  </Td>
+                </Tr>
+              )}
+              {lastStart && (
+                <Tr>
+                  <Td>Last Run Start</Td>
+                  <Td>
+                    <Time dateTime={lastStart} />
+                  </Td>
+                </Tr>
+              )}
+              <Tr>
+                <Td>Max Run Duration</Td>
+                <Td>
+                  {formatDuration(max)}
                 </Td>
               </Tr>
-            )}
-            {lastStart && (
               <Tr>
-                <Td>Last Run Start</Td>
+                <Td>Mean Run Duration</Td>
                 <Td>
-                  <Time dateTime={lastStart} />
+                  {formatDuration(avg)}
                 </Td>
               </Tr>
+              <Tr>
+                <Td>Min Run Duration</Td>
+                <Td>
+                  {formatDuration(min)}
+                </Td>
+              </Tr>
+            </>
             )}
-            <Tr>
-              <Td>Max Run Duration</Td>
+            <Tr borderBottomWidth={2} borderBottomColor="gray.300">
               <Td>
-                {formatDuration(max)}
+                <Heading size="sm">DAG Summary</Heading>
               </Td>
+              <Td />
             </Tr>
             <Tr>
-              <Td>Mean Run Duration</Td>
-              <Td>
-                {formatDuration(avg)}
-              </Td>
+              <Td>Total Tasks</Td>
+              <Td>{taskSummary.taskCount}</Td>
             </Tr>
+            {!!taskSummary.groupCount && (
             <Tr>
-              <Td>Min Run Duration</Td>
-              <Td>
-                {formatDuration(min)}
-              </Td>
+              <Td>Total Task Groups</Td>
+              <Td>{taskSummary.groupCount}</Td>
             </Tr>
-          </>
-          )}
-          <Tr borderBottomWidth={2} borderBottomColor="gray.300">
-            <Td>
-              <Heading size="sm">DAG Summary</Heading>
-            </Td>
-            <Td />
-          </Tr>
-          <Tr>
-            <Td>Total Tasks</Td>
-            <Td>{taskSummary.taskCount}</Td>
-          </Tr>
-          {!!taskSummary.groupCount && (
-          <Tr>
-            <Td>Total Task Groups</Td>
-            <Td>{taskSummary.groupCount}</Td>
-          </Tr>
-          )}
-          {Object.entries(taskSummary.operators).map(([key, value]) => (
-            <Tr key={key}>
-              <Td>
-                {key}
-                {value > 1 && 's'}
-              </Td>
-              <Td>{value}</Td>
-            </Tr>
-          ))}
-        </Tbody>
-      </Table>
+            )}
+            {Object.entries(taskSummary.operators).map(([key, value]) => (
+              <Tr key={key}>
+                <Td>
+                  {key}
+                  {value > 1 && 's'}
+                </Td>
+                <Td>{value}</Td>
+              </Tr>
+            ))}
+          </Tbody>
+        </Table>
+      </Box>
     </>
   );
 };
diff --git a/airflow/www/static/js/dag/details/dagRun/index.tsx b/airflow/www/static/js/dag/details/dagRun/index.tsx
index 728b8b8e07..ccfc0aa4c6 100644
--- a/airflow/www/static/js/dag/details/dagRun/index.tsx
+++ b/airflow/www/static/js/dag/details/dagRun/index.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React from 'react';
+import React, { useRef } from 'react';
 import {
   Flex,
   Text,
@@ -43,6 +43,7 @@ import { ClipboardText } from 'src/components/Clipboard';
 import { formatDuration, getDuration } from 'src/datetime_utils';
 import Time from 'src/components/Time';
 import RunTypeIcon from 'src/components/RunTypeIcon';
+import useOffsetHeight from 'src/utils/useOffsetHeight';
 
 import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
 import NotesAccordion from 'src/dag/details/NotesAccordion';
@@ -62,6 +63,8 @@ interface Props {
 
 const DagRun = ({ runId }: Props) => {
   const { data: { dagRuns } } = useGridData();
+  const detailsRef = useRef<HTMLDivElement>(null);
+  const offsetHeight = useOffsetHeight(detailsRef);
   const run = dagRuns.find((dr) => dr.runId === runId);
   const { onCopy, hasCopied } = useClipboard(run?.conf || '');
   if (!run) return null;
@@ -103,100 +106,107 @@ const DagRun = ({ runId }: Props) => {
         </Flex>
         <Divider my={3} />
       </Box>
-      <Box px={4}>
-        <NotesAccordion
-          dagId={dagId}
-          runId={runId}
-          initialValue={notes}
-          key={dagId + runId}
-        />
-      </Box>
-      <Divider my={0} />
-      <Table variant="striped">
-        <Tbody>
-          <Tr>
-            <Td>Status</Td>
-            <Td>
-              <Flex>
-                <SimpleStatus state={state} mx={2} />
-                {state || 'no status'}
-              </Flex>
-            </Td>
-          </Tr>
-          <Tr>
-            <Td>Run ID</Td>
-            <Td><ClipboardText value={runId} /></Td>
-          </Tr>
-          <Tr>
-            <Td>Run type</Td>
-            <Td>
-              <RunTypeIcon runType={runType} />
-              {runType}
-            </Td>
-          </Tr>
-          <Tr>
-            <Td>Run duration</Td>
-            <Td>
-              {formatDuration(getDuration(startDate, endDate))}
-            </Td>
-          </Tr>
-          {lastSchedulingDecision && (
+      <Box
+        height="100%"
+        maxHeight={offsetHeight}
+        ref={detailsRef}
+        overflowY="auto"
+        pb={4}
+      >
+        <Box px={4}>
+          <NotesAccordion
+            dagId={dagId}
+            runId={runId}
+            initialValue={notes}
+            key={dagId + runId}
+          />
+        </Box>
+        <Divider my={0} />
+        <Table variant="striped">
+          <Tbody>
             <Tr>
-              <Td>Last scheduling decision</Td>
+              <Td>Status</Td>
               <Td>
-                <Time dateTime={lastSchedulingDecision} />
+                <Flex>
+                  <SimpleStatus state={state} mx={2} />
+                  {state || 'no status'}
+                </Flex>
               </Td>
             </Tr>
-          )}
-          {queuedAt && (
             <Tr>
-              <Td>Queued at</Td>
-              <Td>
-                <Time dateTime={queuedAt} />
-              </Td>
+              <Td>Run ID</Td>
+              <Td><ClipboardText value={runId} /></Td>
             </Tr>
-          )}
-          {startDate && (
             <Tr>
-              <Td>Started</Td>
+              <Td>Run type</Td>
               <Td>
-                <Time dateTime={startDate} />
+                <RunTypeIcon runType={runType} />
+                {runType}
               </Td>
             </Tr>
-          )}
-          {endDate && (
             <Tr>
-              <Td>Ended</Td>
+              <Td>Run duration</Td>
               <Td>
-                <Time dateTime={endDate} />
+                {formatDuration(getDuration(startDate, endDate))}
               </Td>
             </Tr>
-          )}
-          {dataIntervalStart && dataIntervalEnd && (
-            <>
+            {lastSchedulingDecision && (
+              <Tr>
+                <Td>Last scheduling decision</Td>
+                <Td>
+                  <Time dateTime={lastSchedulingDecision} />
+                </Td>
+              </Tr>
+            )}
+            {queuedAt && (
+              <Tr>
+                <Td>Queued at</Td>
+                <Td>
+                  <Time dateTime={queuedAt} />
+                </Td>
+              </Tr>
+            )}
+            {startDate && (
               <Tr>
-                <Td>Data interval start</Td>
+                <Td>Started</Td>
                 <Td>
-                  <Time dateTime={dataIntervalStart} />
+                  <Time dateTime={startDate} />
                 </Td>
               </Tr>
+            )}
+            {endDate && (
               <Tr>
-                <Td>Data interval end</Td>
+                <Td>Ended</Td>
                 <Td>
-                  <Time dateTime={dataIntervalEnd} />
+                  <Time dateTime={endDate} />
                 </Td>
               </Tr>
-            </>
-          )}
-          <Tr>
-            <Td>Externally triggered</Td>
-            <Td>
-              {externalTrigger ? 'True' : 'False'}
-            </Td>
-          </Tr>
-          <Tr>
-            <Td>Run config</Td>
-            {
+            )}
+            {dataIntervalStart && dataIntervalEnd && (
+              <>
+                <Tr>
+                  <Td>Data interval start</Td>
+                  <Td>
+                    <Time dateTime={dataIntervalStart} />
+                  </Td>
+                </Tr>
+                <Tr>
+                  <Td>Data interval end</Td>
+                  <Td>
+                    <Time dateTime={dataIntervalEnd} />
+                  </Td>
+                </Tr>
+              </>
+            )}
+            <Tr>
+              <Td>Externally triggered</Td>
+              <Td>
+                {externalTrigger ? 'True' : 'False'}
+              </Td>
+            </Tr>
+            <Tr>
+              <Td>Run config</Td>
+              {
                 confIsJson
                   ? (
                     <Td>
@@ -218,12 +228,13 @@ const DagRun = ({ runId }: Props) => {
                   )
                   : <Td>{conf ?? 'None'}</Td>
               }
-          </Tr>
-        </Tbody>
-      </Table>
-      {runType === 'dataset_triggered' && (
-        <DatasetTriggerEvents runId={runId} />
-      )}
+            </Tr>
+          </Tbody>
+        </Table>
+        {runType === 'dataset_triggered' && (
+          <DatasetTriggerEvents runId={runId} />
+        )}
+      </Box>
     </>
   );
 };
diff --git a/airflow/www/static/js/dag/details/index.tsx b/airflow/www/static/js/dag/details/index.tsx
index b4eaac4db3..ee73378acd 100644
--- a/airflow/www/static/js/dag/details/index.tsx
+++ b/airflow/www/static/js/dag/details/index.tsx
@@ -33,11 +33,17 @@ import DagContent from './Dag';
 
 const Details = () => {
   const { selected: { runId, taskId, mapIndex }, onSelect } = useSelection();
+
   return (
-    <Flex flexDirection="column" pl={3} mr={3} height="100%">
+    <Flex
+      flexDirection="column"
+      pl={3}
+      mr={3}
+      height="100%"
+    >
       <Header />
       <Divider my={2} />
-      <Box overflowY="scroll">
+      <Box height="100%">
         {!runId && !taskId && <DagContent />}
         {runId && !taskId && (
           <DagRunContent runId={runId} />
diff --git a/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx b/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx
index ec6249cb27..308446678a 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Logs/index.tsx
@@ -36,8 +36,10 @@ import LinkButton from 'src/components/LinkButton';
 import { useTimezone } from 'src/context/timezone';
 import type { Dag, DagRun, TaskInstance } from 'src/types';
 import MultiSelect from 'src/components/MultiSelect';
+import useOffsetHeight from 'src/utils/useOffsetHeight';
 
 import URLSearchParamsWrapper from 'src/utils/URLSearchParamWrapper';
+
 import LogLink from './LogLink';
 import { LogLevel, logLevelColorMapping, parseLogs } from './utils';
 
@@ -106,6 +108,9 @@ const Logs = ({
   const [logLevelFilters, setLogLevelFilters] = useState<Array<LogLevelOption>>([]);
   const [fileSourceFilters, setFileSourceFilters] = useState<Array<FileSourceOption>>([]);
   const { timezone } = useTimezone();
+  const logBoxRef = useRef<HTMLPreElement>(null);
+
+  const offsetHeight = useOffsetHeight(logBoxRef);
 
   const taskTryNumber = selectedTryNumber || tryNumber || 1;
   const { data, isSuccess } = useTaskLog({
@@ -140,7 +145,7 @@ const Logs = ({
   const codeBlockBottomDiv = useRef<HTMLDivElement>(null);
 
   useEffect(() => {
-    if (codeBlockBottomDiv.current) {
+    if (codeBlockBottomDiv.current && parsedLogs) {
       codeBlockBottomDiv.current.scrollIntoView({ block: 'nearest', inline: 'nearest' });
     }
   }, [wrap, parsedLogs]);
@@ -165,109 +170,113 @@ const Logs = ({
     <>
       {tryNumber !== undefined && (
         <>
-          <Text as="span"> (by attempts)</Text>
-          <Flex my={1} justifyContent="space-between">
-            <Flex flexWrap="wrap">
-              {internalIndexes.map((index) => (
-                <Button
-                  key={index}
-                  variant={taskTryNumber === index ? 'solid' : 'ghost'}
-                  colorScheme="blue"
-                  onClick={() => setSelectedTryNumber(index)}
-                  data-testid={`log-attempt-select-button-${index}`}
-                >
-                  {index}
-                </Button>
-              ))}
+          <Box>
+            <Text as="span"> (by attempts)</Text>
+            <Flex my={1} justifyContent="space-between">
+              <Flex flexWrap="wrap">
+                {internalIndexes.map((index) => (
+                  <Button
+                    key={index}
+                    variant={taskTryNumber === index ? 'solid' : 'ghost'}
+                    colorScheme="blue"
+                    onClick={() => setSelectedTryNumber(index)}
+                    data-testid={`log-attempt-select-button-${index}`}
+                  >
+                    {index}
+                  </Button>
+                ))}
+              </Flex>
             </Flex>
-          </Flex>
-          <Flex my={1} justifyContent="space-between">
-            <Flex alignItems="center" flexGrow={1} mr={10}>
-              <Box width="100%" mr={2}>
-                <MultiSelect
-                  size="sm"
-                  isMulti
-                  options={logLevelOptions}
-                  placeholder="All Levels"
-                  value={logLevelFilters}
-                  onChange={(options) => setLogLevelFilters([...options])}
-                  chakraStyles={{
-                    multiValue: (provided, ...rest) => ({
-                      ...provided,
-                      backgroundColor: rest[0].data.color,
-                    }),
-                    option: (provided, ...rest) => ({
-                      ...provided,
-                      borderLeft: 'solid 4px black',
-                      borderColor: rest[0].data.color,
-                      mt: 2,
-                    }),
-                  }}
-                />
-              </Box>
-              <Box width="100%">
-                <MultiSelect
-                  size="sm"
-                  isMulti
-                  options={fileSources.map((fileSource) => ({
-                    label: fileSource,
-                    value: fileSource,
-                  }))}
-                  placeholder="All File Sources"
-                  value={fileSourceFilters}
-                  onChange={(options) => setFileSourceFilters([...options])}
+            <Flex my={1} justifyContent="space-between" flexWrap="wrap">
+              <Flex alignItems="center" flexGrow={1} mr={10}>
+                <Box width="100%" mr={2}>
+                  <MultiSelect
+                    size="sm"
+                    isMulti
+                    options={logLevelOptions}
+                    placeholder="All Levels"
+                    value={logLevelFilters}
+                    onChange={(options) => setLogLevelFilters([...options])}
+                    chakraStyles={{
+                      multiValue: (provided, ...rest) => ({
+                        ...provided,
+                        backgroundColor: rest[0].data.color,
+                      }),
+                      option: (provided, ...rest) => ({
+                        ...provided,
+                        borderLeft: 'solid 4px black',
+                        borderColor: rest[0].data.color,
+                        mt: 2,
+                      }),
+                    }}
+                  />
+                </Box>
+                <Box width="100%">
+                  <MultiSelect
+                    size="sm"
+                    isMulti
+                    options={fileSources.map((fileSource) => ({
+                      label: fileSource,
+                      value: fileSource,
+                    }))}
+                    placeholder="All File Sources"
+                    value={fileSourceFilters}
+                    onChange={(options) => setFileSourceFilters([...options])}
+                  />
+                </Box>
+              </Flex>
+              <Flex alignItems="center" flexWrap="wrap">
+                <Checkbox
+                  isChecked={wrap}
+                  onChange={() => setWrap((previousState) => !previousState)}
+                  px={4}
+                  data-testid="wrap-checkbox"
+                >
+                  <Text as="strong">Wrap</Text>
+                </Checkbox>
+                <Checkbox
+                  onChange={() => setShouldRequestFullContent((previousState) => !previousState)}
+                  px={4}
+                  data-testid="full-content-checkbox"
+                >
+                  <Text as="strong" whiteSpace="nowrap">Full Logs</Text>
+                </Checkbox>
+                <LogLink
+                  dagId={dagId}
+                  taskId={taskId}
+                  executionDate={executionDate}
+                  isInternal
+                  tryNumber={tryNumber}
+                  mapIndex={mapIndex}
                 />
-              </Box>
-            </Flex>
-            <Flex alignItems="center">
-              <Checkbox
-                isChecked={wrap}
-                onChange={() => setWrap((previousState) => !previousState)}
-                px={4}
-                data-testid="wrap-checkbox"
-              >
-                <Text as="strong">Wrap</Text>
-              </Checkbox>
-              <Checkbox
-                onChange={() => setShouldRequestFullContent((previousState) => !previousState)}
-                px={4}
-                data-testid="full-content-checkbox"
-              >
-                <Text as="strong" whiteSpace="nowrap">Full Logs</Text>
-              </Checkbox>
-              <LogLink
-                dagId={dagId}
-                taskId={taskId}
-                executionDate={executionDate}
-                isInternal
-                tryNumber={tryNumber}
-                mapIndex={mapIndex}
-              />
-              <LinkButton
-                href={`${logUrl}&${params.toString()}`}
-              >
-                See More
-              </LinkButton>
+                <LinkButton
+                  href={`${logUrl}&${params.toString()}`}
+                >
+                  See More
+                </LinkButton>
+              </Flex>
             </Flex>
-          </Flex>
-          {
-            isSuccess && (
-              <Code
-                height={350}
-                overflowY="scroll"
-                p={3}
-                pb={0}
-                display="block"
-                whiteSpace={wrap ? 'pre-wrap' : 'pre'}
-                border="1px solid"
-                borderRadius={3}
-                borderColor="blue.500"
-              >
+          </Box>
+          <Code
+            ref={logBoxRef}
+            height="100%"
+            maxHeight={offsetHeight}
+            overflowY="auto"
+            p={3}
+            pb={0}
+            display="block"
+            whiteSpace={wrap ? 'pre-wrap' : 'pre'}
+            border="1px solid"
+            borderRadius={3}
+            borderColor="blue.500"
+          >
+            {isSuccess && (
+              <>
                 {parsedLogs}
                 <div ref={codeBlockBottomDiv} />
-              </Code>
-            )
-          }
+              </>
+            )}
+          </Code>
         </>
       )}
       {externalLogName && externalIndexes.length > 0 && (
diff --git a/airflow/www/static/js/dag/details/taskInstance/Nav.tsx b/airflow/www/static/js/dag/details/taskInstance/Nav.tsx
index 833a7b3169..5f2695b96d 100644
--- a/airflow/www/static/js/dag/details/taskInstance/Nav.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/Nav.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import React from 'react';
+import React, { forwardRef } from 'react';
 import {
   Flex,
   Divider,
@@ -50,9 +50,17 @@ interface Props {
   mapIndex?: number;
 }
 
-const Nav = ({
-  runId, taskId, executionDate, operator, isMapped = false, mapIndex,
-}: Props) => {
+const Nav = forwardRef<HTMLDivElement, Props>((
+  {
+    runId,
+    taskId,
+    executionDate,
+    operator,
+    isMapped = false,
+    mapIndex,
+  },
+  ref,
+) => {
   if (!taskId) return null;
   const params = new URLSearchParamsWrapper({
     task_id: taskId,
@@ -92,7 +100,7 @@ const Nav = ({
 
   return (
     <>
-      <Flex flexWrap="wrap">
+      <Flex flexWrap="wrap" ref={ref}>
         {(!isMapped || mapIndex !== undefined) && (
         <>
           <LinkButton href={detailsLink}>Task Instance Details</LinkButton>
@@ -113,6 +121,6 @@ const Nav = ({
       <Divider mt={3} />
     </>
   );
-};
+});
 
 export default Nav;
diff --git a/airflow/www/static/js/dag/details/taskInstance/index.tsx b/airflow/www/static/js/dag/details/taskInstance/index.tsx
index 0fe53ec000..560ef28f49 100644
--- a/airflow/www/static/js/dag/details/taskInstance/index.tsx
+++ b/airflow/www/static/js/dag/details/taskInstance/index.tsx
@@ -19,7 +19,7 @@
 
 /* global localStorage */
 
-import React, { useState } from 'react';
+import React, { useRef, useState } from 'react';
 import {
   Box,
   Text,
@@ -32,8 +32,8 @@ import {
 
 import { useGridData, useTaskInstance } from 'src/api';
 import { getMetaValue, getTask } from 'src/utils';
+import useOffsetHeight from 'src/utils/useOffsetHeight';
 import type { DagRun, TaskInstance as TaskInstanceType } from 'src/types';
-
 import type { SelectionProps } from 'src/dag/useSelection';
 import NotesAccordion from 'src/dag/details/NotesAccordion';
 
@@ -62,6 +62,8 @@ const TaskInstance = ({
   const isMapIndexDefined = !(mapIndex === undefined);
   const actionsMapIndexes = isMapIndexDefined ? [mapIndex] : [];
   const { data: { dagRuns, groups } } = useGridData();
+  const detailsRef = useRef<HTMLDivElement>(null);
+  const offsetHeight = useOffsetHeight(detailsRef);
 
   const storageTabIndex = parseInt(localStorage.getItem(detailsPanelActiveTabIndex) || '0', 10);
   const [preferedTabIndex, setPreferedTabIndex] = useState(storageTabIndex);
@@ -115,7 +117,7 @@ const TaskInstance = ({
   }
 
   return (
-    <Box py="4px">
+    <Box py="4px" height="100%">
       {!isGroup && (
         <TaskNav
           taskId={taskId}
@@ -126,7 +128,13 @@ const TaskInstance = ({
           operator={operator}
         />
       )}
-      <Tabs size="lg" index={selectedTabIndex} onChange={handleTabsChange} isLazy>
+      <Tabs
+        size="lg"
+        index={selectedTabIndex}
+        onChange={handleTabsChange}
+        isLazy
+        height="100%"
+      >
         <TabList>
           <Tab>
             <Text as="strong">Details</Text>
@@ -149,9 +157,16 @@ const TaskInstance = ({
         />
 
         <TabPanels>
-
           {/* Details Tab */}
-          <TabPanel pt={isMapIndexDefined ? '0px' : undefined}>
+          <TabPanel
+            pt={isMapIndexDefined ? '0px' : undefined}
+            height="100%"
+            maxHeight={offsetHeight}
+            ref={detailsRef}
+            overflowY="auto"
+            py="4px"
+            pb={4}
+          >
             <Box py="4px">
               {!isGroupOrMappedTaskSummary && (
                 <NotesAccordion
diff --git a/airflow/www/static/js/dag/grid/index.tsx b/airflow/www/static/js/dag/grid/index.tsx
index b21e95bf8d..e2545125c1 100644
--- a/airflow/www/static/js/dag/grid/index.tsx
+++ b/airflow/www/static/js/dag/grid/index.tsx
@@ -27,7 +27,6 @@ import {
   Thead,
   Flex,
   IconButton,
-  useDimensions,
 } from '@chakra-ui/react';
 
 import { MdReadMore } from 'react-icons/md';
@@ -35,6 +34,7 @@ import { MdReadMore } from 'react-icons/md';
 import { useGridData } from 'src/api';
 import { getMetaValue } from 'src/utils';
 import AutoRefresh from 'src/components/AutoRefresh';
+import useOffsetHeight from 'src/utils/useOffsetHeight';
 
 import renderTaskRows from './renderTaskRows';
 import ResetRoot from './ResetRoot';
@@ -49,11 +49,14 @@ interface Props {
   hoveredTaskState?: string | null;
 }
 
-const Grid = ({ isPanelOpen = false, onPanelToggle, hoveredTaskState }: Props) => {
+const Grid = ({
+  isPanelOpen = false,
+  onPanelToggle,
+  hoveredTaskState,
+}: Props) => {
   const scrollRef = useRef<HTMLDivElement>(null);
   const tableRef = useRef<HTMLTableSectionElement>(null);
-  const buttonsRef = useRef<HTMLDivElement>(null);
-  const dimensions = useDimensions(buttonsRef);
+  const offsetHeight = useOffsetHeight(scrollRef);
 
   const { data: { groups, dagRuns } } = useGridData();
   const dagRunIds = dagRuns.map((dr) => dr.runId);
@@ -103,7 +106,6 @@ const Grid = ({ isPanelOpen = false, onPanelToggle, hoveredTaskState }: Props) =
         p={1}
         pb={2}
         backgroundColor="white"
-        ref={buttonsRef}
       >
         <Flex alignItems="center">
           <AutoRefresh />
@@ -125,11 +127,13 @@ const Grid = ({ isPanelOpen = false, onPanelToggle, hoveredTaskState }: Props) =
         />
       </Flex>
       <Box
-        height={`calc(100% - ${dimensions?.borderBox.height || 0}px)`}
+        height="100%"
+        maxHeight={offsetHeight}
         ref={scrollRef}
         overflow="auto"
         position="relative"
         pr={4}
+        pb={4}
       >
         <Table pr="10px">
           <Thead>
diff --git a/airflow/www/static/js/datasets/index.tsx b/airflow/www/static/js/datasets/index.tsx
index 9a993c0562..ba738a0d10 100644
--- a/airflow/www/static/js/datasets/index.tsx
+++ b/airflow/www/static/js/datasets/index.tsx
@@ -26,7 +26,6 @@ import { useSearchParams } from 'react-router-dom';
 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';
@@ -61,16 +60,14 @@ const Datasets = () => {
 
   const datasetUri = decodeURIComponent(searchParams.get(DATASET_URI) || '');
 
-  useContentHeight(contentRef);
-
   return (
     <Flex alignItems="flex-start" justifyContent="space-between" ref={contentRef}>
-      <Box minWidth="450px" height="100%" overflowY="scroll">
+      <Box minWidth="450px" height="100%" overflowY="auto">
         {datasetUri
           ? <DatasetDetails uri={datasetUri} onBack={onBack} />
           : <DatasetsList onSelect={onSelect} />}
       </Box>
-      <Box flex={1} ref={graphRef} height="100%" borderColor="gray.200" borderWidth={1}>
+      <Box flex={1} ref={graphRef} height="calc(100vh - 68px)" borderColor="gray.200" borderWidth={1}>
         <Graph
           selectedUri={datasetUri}
           onSelect={onSelect}
diff --git a/airflow/www/static/js/utils/useContentHeight.ts b/airflow/www/static/js/utils/useOffsetHeight.tsx
similarity index 59%
rename from airflow/www/static/js/utils/useContentHeight.ts
rename to airflow/www/static/js/utils/useOffsetHeight.tsx
index 9eb585899a..b0d39a89d5 100644
--- a/airflow/www/static/js/utils/useContentHeight.ts
+++ b/airflow/www/static/js/utils/useOffsetHeight.tsx
@@ -17,26 +17,28 @@
  * under the License.
  */
 
-/* global document */
+/* global document, window */
 
-import React, { useEffect } from 'react';
+import { debounce } from 'lodash';
+import React, { useEffect, useState } from 'react';
+
+const footerHeight = parseInt(getComputedStyle(document.getElementsByTagName('body')[0]).paddingBottom.replace('px', ''), 10) || 0;
+
+// For an html element, keep it within view height by calculating the top offset and footer height
+const useOffsetHeight = (
+  contentRef: React.RefObject<HTMLDivElement | HTMLPreElement>,
+  minHeight: number = 300,
+) => {
+  const [height, setHeight] = useState(0);
 
-const useContentHeight = (contentRef: React.RefObject<HTMLDivElement>) => {
   useEffect(() => {
-    const calculateHeight = () => {
+    const calculateHeight = debounce(() => {
       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';
-        }
+        const newHeight = window.innerHeight - (topOffset + footerHeight);
+        setHeight(newHeight > minHeight ? newHeight : minHeight);
       }
-    };
+    }, 25);
     // set height on load
     calculateHeight();
 
@@ -45,7 +47,9 @@ const useContentHeight = (contentRef: React.RefObject<HTMLDivElement>) => {
     return () => {
       window.removeEventListener('resize', calculateHeight);
     };
-  }, [contentRef]);
+  }, [contentRef, minHeight]);
+
+  return height;
 };
 
-export default useContentHeight;
+export default useOffsetHeight;