From 31c67fa22d792e6d9cf14d852418f989ad254c55 Mon Sep 17 00:00:00 2001 From: Brent Bovenzi Date: Tue, 28 Jun 2022 13:07:42 -0400 Subject: [PATCH 1/3] Add delay / debounce to not always highlight tasks --- airflow/www/static/js/grid/Grid.tsx | 3 +-- ...{LegendRow.test.jsx => LegendRow.test.tsx} | 15 ++++++----- .../js/grid/{LegendRow.jsx => LegendRow.tsx} | 27 ++++++++++++++----- airflow/www/static/js/grid/Main.tsx | 14 +++++++--- airflow/www/static/js/grid/renderTaskRows.tsx | 4 +-- 5 files changed, 43 insertions(+), 20 deletions(-) rename airflow/www/static/js/grid/{LegendRow.test.jsx => LegendRow.test.tsx} (76%) rename airflow/www/static/js/grid/{LegendRow.jsx => LegendRow.tsx} (71%) diff --git a/airflow/www/static/js/grid/Grid.tsx b/airflow/www/static/js/grid/Grid.tsx index c32d8230ce548..6de3d0ecc2812 100644 --- a/airflow/www/static/js/grid/Grid.tsx +++ b/airflow/www/static/js/grid/Grid.tsx @@ -43,7 +43,7 @@ const dagId = getMetaValue('dag_id'); interface Props { isPanelOpen?: boolean; onPanelToggle: () => void; - hoveredTaskState?: string; + hoveredTaskState?: string | null; } const Grid = ({ isPanelOpen = false, onPanelToggle, hoveredTaskState }: Props) => { @@ -129,7 +129,6 @@ const Grid = ({ isPanelOpen = false, onPanelToggle, hoveredTaskState }: Props) = - {/* TODO: remove hardcoded values. 665px is roughly the total heade+footer height */} {renderTaskRows({ task: groups, dagRunIds, openGroupIds, onToggleGroups, hoveredTaskState, diff --git a/airflow/www/static/js/grid/LegendRow.test.jsx b/airflow/www/static/js/grid/LegendRow.test.tsx similarity index 76% rename from airflow/www/static/js/grid/LegendRow.test.jsx rename to airflow/www/static/js/grid/LegendRow.test.tsx index bd1c64d0c3bd9..7a3ba185932fc 100644 --- a/airflow/www/static/js/grid/LegendRow.test.jsx +++ b/airflow/www/static/js/grid/LegendRow.test.tsx @@ -20,14 +20,16 @@ /* global describe, test, expect, stateColors, jest */ import React from 'react'; -import { render, fireEvent } from '@testing-library/react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; import LegendRow from './LegendRow'; describe('Test LegendRow', () => { test('Render displays correctly the different task states', () => { + const onStatusHover = jest.fn(); + const onStatusLeave = jest.fn(); const { getByText } = render( - , + , ); Object.keys(stateColors).forEach((taskState) => { @@ -44,15 +46,16 @@ describe('Test LegendRow', () => { ])( 'Hovering $state badge should trigger setHoverdTaskState function with $expectedSetValue', async ({ state, expectedSetValue }) => { - const setHoveredTaskState = jest.fn(); + const onStatusHover = jest.fn(); + const onStatusLeave = jest.fn(); const { getByText } = render( - , + , ); const successElement = getByText(state); fireEvent.mouseEnter(successElement); - expect(setHoveredTaskState).toHaveBeenCalledWith(expectedSetValue); + await waitFor(() => expect(onStatusHover).toHaveBeenCalledWith(expectedSetValue)); fireEvent.mouseLeave(successElement); - expect(setHoveredTaskState).toHaveBeenLastCalledWith(); + await waitFor(() => expect(onStatusLeave).toHaveBeenLastCalledWith()); }, ); }); diff --git a/airflow/www/static/js/grid/LegendRow.jsx b/airflow/www/static/js/grid/LegendRow.tsx similarity index 71% rename from airflow/www/static/js/grid/LegendRow.jsx rename to airflow/www/static/js/grid/LegendRow.tsx index eff503403c768..ade94b059250f 100644 --- a/airflow/www/static/js/grid/LegendRow.jsx +++ b/airflow/www/static/js/grid/LegendRow.tsx @@ -26,23 +26,34 @@ import { } from '@chakra-ui/react'; import React from 'react'; +interface LegendProps { + onStatusHover: (status: string | null) => void; + onStatusLeave: () => void; +} + +interface BadgeProps extends LegendProps { + state: string | null; + stateColor: string; + displayValue?: string; +} + const StatusBadge = ({ - state, stateColor, setHoveredTaskState, displayValue, -}) => ( + state, stateColor, onStatusHover, onStatusLeave, displayValue, +}: BadgeProps) => ( setHoveredTaskState(state)} - onMouseLeave={() => setHoveredTaskState()} + onMouseEnter={() => onStatusHover(state)} + onMouseLeave={() => onStatusLeave()} > {displayValue || state } ); -const LegendRow = ({ setHoveredTaskState }) => ( +const LegendRow = ({ onStatusHover, onStatusLeave }: LegendProps) => ( { @@ -51,7 +62,8 @@ const LegendRow = ({ setHoveredTaskState }) => ( key={state} state={state} stateColor={stateColor} - setHoveredTaskState={setHoveredTaskState} + onStatusHover={onStatusHover} + onStatusLeave={onStatusLeave} /> )) } @@ -60,7 +72,8 @@ const LegendRow = ({ setHoveredTaskState }) => ( displayValue="no_status" state={null} stateColor="white" - setHoveredTaskState={setHoveredTaskState} + onStatusHover={onStatusHover} + onStatusLeave={onStatusLeave} /> diff --git a/airflow/www/static/js/grid/Main.tsx b/airflow/www/static/js/grid/Main.tsx index 8779b547ec16b..e5d9bbbe42c44 100644 --- a/airflow/www/static/js/grid/Main.tsx +++ b/airflow/www/static/js/grid/Main.tsx @@ -27,7 +27,7 @@ import { Divider, Spinner, } from '@chakra-ui/react'; -import { isEmpty } from 'lodash'; +import { isEmpty, debounce } from 'lodash'; import Details from './details'; import useSelection from './utils/useSelection'; @@ -43,7 +43,15 @@ const Main = () => { const isPanelOpen = localStorage.getItem(detailsPanelKey) !== 'true'; const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: isPanelOpen }); const { clearSelection } = useSelection(); - const [hoveredTaskState, setHoveredTaskState] = useState(); + const [hoveredTaskState, setHoveredTaskState] = useState(); + + // Add a debounced delay to not constantly trigger highlighting certain task states + const onStatusHover = debounce((state) => setHoveredTaskState(state), 400) + + const onStatusLeave = () => { + setHoveredTaskState(undefined); + onStatusHover.cancel() + } const onPanelToggle = () => { if (!isOpen) { @@ -58,7 +66,7 @@ const Main = () => { return ( - + {isLoading || isEmpty(groups) diff --git a/airflow/www/static/js/grid/renderTaskRows.tsx b/airflow/www/static/js/grid/renderTaskRows.tsx index cfbae119da443..f35d1a2c9e17e 100644 --- a/airflow/www/static/js/grid/renderTaskRows.tsx +++ b/airflow/www/static/js/grid/renderTaskRows.tsx @@ -43,7 +43,7 @@ interface RowProps { openParentCount?: number; openGroupIds?: string[]; onToggleGroups?: (groupIds: string[]) => void; - hoveredTaskState?: string; + hoveredTaskState?: string | null; } const renderTaskRows = ({ @@ -67,7 +67,7 @@ interface TaskInstancesProps { dagRunIds: string[]; selectedRunId?: string | null; onSelect: (selection: SelectionProps) => void; - hoveredTaskState?: string; + hoveredTaskState?: string | null; } const TaskInstances = ({ From 4f49007d9d75f714e492b0354b1f8a7f308937e0 Mon Sep 17 00:00:00 2001 From: Brent Bovenzi Date: Tue, 28 Jun 2022 13:46:14 -0400 Subject: [PATCH 2/3] fix linting --- airflow/www/static/js/grid/Main.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/airflow/www/static/js/grid/Main.tsx b/airflow/www/static/js/grid/Main.tsx index e5d9bbbe42c44..9f3f1600ef044 100644 --- a/airflow/www/static/js/grid/Main.tsx +++ b/airflow/www/static/js/grid/Main.tsx @@ -46,12 +46,12 @@ const Main = () => { const [hoveredTaskState, setHoveredTaskState] = useState(); // Add a debounced delay to not constantly trigger highlighting certain task states - const onStatusHover = debounce((state) => setHoveredTaskState(state), 400) + const onStatusHover = debounce((state) => setHoveredTaskState(state), 400); const onStatusLeave = () => { setHoveredTaskState(undefined); - onStatusHover.cancel() - } + onStatusHover.cancel(); + }; const onPanelToggle = () => { if (!isOpen) { From 0e89dc1ba9eb6662f55fb815b1f3d78032746bc5 Mon Sep 17 00:00:00 2001 From: Brent Bovenzi Date: Tue, 28 Jun 2022 14:11:32 -0400 Subject: [PATCH 3/3] single delay variable at 200ms --- airflow/www/static/js/grid/Main.tsx | 3 ++- .../static/js/grid/components/Clipboard.jsx | 1 - .../static/js/grid/components/StatusBox.tsx | 3 ++- airflow/www/static/js/grid/dagRuns/Bar.tsx | 3 ++- airflow/www/static/js/grid/utils/index.ts | 26 +++++++++++++++++++ 5 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 airflow/www/static/js/grid/utils/index.ts diff --git a/airflow/www/static/js/grid/Main.tsx b/airflow/www/static/js/grid/Main.tsx index 9f3f1600ef044..bde36c2984fcf 100644 --- a/airflow/www/static/js/grid/Main.tsx +++ b/airflow/www/static/js/grid/Main.tsx @@ -35,6 +35,7 @@ import Grid from './Grid'; import FilterBar from './FilterBar'; import LegendRow from './LegendRow'; import { useGridData } from './api'; +import { hoverDelay } from './utils'; const detailsPanelKey = 'hideDetailsPanel'; @@ -46,7 +47,7 @@ const Main = () => { const [hoveredTaskState, setHoveredTaskState] = useState(); // Add a debounced delay to not constantly trigger highlighting certain task states - const onStatusHover = debounce((state) => setHoveredTaskState(state), 400); + const onStatusHover = debounce((state) => setHoveredTaskState(state), hoverDelay); const onStatusLeave = () => { setHoveredTaskState(undefined); diff --git a/airflow/www/static/js/grid/components/Clipboard.jsx b/airflow/www/static/js/grid/components/Clipboard.jsx index 794e363fa0b0e..a4e0acca2884c 100644 --- a/airflow/www/static/js/grid/components/Clipboard.jsx +++ b/airflow/www/static/js/grid/components/Clipboard.jsx @@ -60,7 +60,6 @@ export const ClipboardButton = forwardRef( label="Copied" isOpen={hasCopied} isDisabled={!hasCopied} - closeDelay={500} placement="top" portalProps={{ containerRef }} > diff --git a/airflow/www/static/js/grid/components/StatusBox.tsx b/airflow/www/static/js/grid/components/StatusBox.tsx index f9acf57402ee1..dd8dfabfb14b1 100644 --- a/airflow/www/static/js/grid/components/StatusBox.tsx +++ b/airflow/www/static/js/grid/components/StatusBox.tsx @@ -30,6 +30,7 @@ import InstanceTooltip from './InstanceTooltip'; import { useContainerRef } from '../context/containerRef'; import type { Task, TaskInstance, TaskState } from '../types'; import type { SelectionProps } from '../utils/useSelection'; +import { hoverDelay } from '../utils'; export const boxSize = 10; export const boxSizePx = `${boxSize}px`; @@ -92,7 +93,7 @@ const StatusBox = ({ portalProps={{ containerRef }} hasArrow placement="top" - openDelay={400} + openDelay={hoverDelay} >