You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ju...@apache.org on 2026/08/05 17:41:46 UTC

(superset) branch master updated: perf(TimeTable): skip rendering TableView while the window is resizing (#42097)

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

justinpark pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 657e2d2267a perf(TimeTable): skip rendering TableView while the window is resizing (#42097)
657e2d2267a is described below

commit 657e2d2267ac34a4a39ef6c052a0a1bfa8b17972
Author: JUST.in DO IT <ju...@airbnb.com>
AuthorDate: Wed Aug 5 10:41:39 2026 -0700

    perf(TimeTable): skip rendering TableView while the window is resizing (#42097)
---
 .../visualizations/TimeTable/TimeTable.test.tsx    | 51 ++++++++++++++++++++-
 .../src/visualizations/TimeTable/TimeTable.tsx     | 53 +++++++++++++++++-----
 2 files changed, 92 insertions(+), 12 deletions(-)

diff --git a/superset-frontend/src/visualizations/TimeTable/TimeTable.test.tsx b/superset-frontend/src/visualizations/TimeTable/TimeTable.test.tsx
index 979aa12a2ea..0e38ae1464d 100644
--- a/superset-frontend/src/visualizations/TimeTable/TimeTable.test.tsx
+++ b/superset-frontend/src/visualizations/TimeTable/TimeTable.test.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { render, screen, within } from '@superset-ui/core/spec';
+import { act, fireEvent, render, screen, within } from '@superset-ui/core/spec';
 import TimeTable from './TimeTable';
 
 const mockData = {
@@ -185,6 +185,55 @@ test('should handle column type sparkline correctly', () => {
   });
 });
 
+test('should hide the table while the window is resizing and reveal it again after the debounce delay', () => {
+  jest.useFakeTimers();
+
+  const { container } = render(<TimeTable {...defaultProps} />);
+  const timeTable = container.querySelector('[data-test="time-table"]');
+
+  expect(timeTable).not.toHaveStyle('display: none');
+
+  act(() => {
+    fireEvent(window, new Event('resize'));
+  });
+  expect(timeTable).toHaveStyle('display: none');
+
+  act(() => {
+    jest.advanceTimersByTime(499);
+  });
+  expect(timeTable).toHaveStyle('display: none');
+
+  act(() => {
+    jest.advanceTimersByTime(1);
+  });
+  expect(timeTable).not.toHaveStyle('display: none');
+
+  jest.clearAllTimers();
+  jest.useRealTimers();
+});
+
+test('should clear the resize listener and pending timer on unmount', () => {
+  jest.useFakeTimers();
+  const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener');
+
+  const { unmount } = render(<TimeTable {...defaultProps} />);
+
+  act(() => {
+    fireEvent(window, new Event('resize'));
+  });
+
+  unmount();
+
+  expect(removeEventListenerSpy).toHaveBeenCalledWith(
+    'resize',
+    expect.any(Function),
+  );
+
+  removeEventListenerSpy.mockRestore();
+  jest.clearAllTimers();
+  jest.useRealTimers();
+});
+
 test('should not render empty table due to missing column id property', () => {
   render(<TimeTable {...defaultProps} />);
 
diff --git a/superset-frontend/src/visualizations/TimeTable/TimeTable.tsx b/superset-frontend/src/visualizations/TimeTable/TimeTable.tsx
index 9d840ecb717..f83f37f52d0 100644
--- a/superset-frontend/src/visualizations/TimeTable/TimeTable.tsx
+++ b/superset-frontend/src/visualizations/TimeTable/TimeTable.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { useMemo, ReactNode } from 'react';
+import { useMemo, useRef, useState, useEffect, ReactNode } from 'react';
 import { InfoTooltip, TableView } from '@superset-ui/core/components';
 import { t } from '@apache-superset/core/translation';
 import { styled } from '@apache-superset/core/theme';
@@ -28,8 +28,11 @@ import {
 import { ValueCell, LeftCell, Sparkline } from './components';
 import type { TimeTableProps } from './types';
 
+const RESIZE_DEBOUNCE_MS = 500;
+
 // @z-index-above-dashboard-charts + 1 = 11
-const TimeTableStyles = styled.div<{ height?: number }>`
+const TimeTableStyles = styled.div<{ height?: number; hideTable?: boolean }>`
+  ${props => props.hideTable && 'display: none;'}
   height: ${props => props.height}px;
   overflow: auto;
 
@@ -47,6 +50,30 @@ const TimeTable = ({
   rows,
   url = '',
 }: TimeTableProps) => {
+  // Hide TableView as soon as the window starts resizing, and only show it
+  // again once resizing has stopped for RESIZE_DEBOUNCE_MS.
+  const resizeTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(
+    undefined,
+  );
+  const [isSizeStable, setIsSizeStable] = useState(true);
+
+  useEffect(() => {
+    const handleResize = () => {
+      setIsSizeStable(false);
+      clearTimeout(resizeTimerRef.current);
+      resizeTimerRef.current = setTimeout(() => {
+        setIsSizeStable(true);
+      }, RESIZE_DEBOUNCE_MS);
+    };
+
+    window.addEventListener('resize', handleResize);
+
+    return () => {
+      window.removeEventListener('resize', handleResize);
+      clearTimeout(resizeTimerRef.current);
+    };
+  }, []);
+
   const memoizedColumns = useMemo(
     () => [
       {
@@ -125,21 +152,25 @@ const TimeTable = ({
     });
   }, [columnConfigs, data, rowType, rows, url]);
 
-  const defaultSort =
-    rowType === 'column' && columnConfigs.length
-      ? [
-          {
-            id: columnConfigs[0].key,
-            desc: true,
-          },
-        ]
-      : [];
+  const defaultSort = useMemo(
+    () =>
+      rowType === 'column' && columnConfigs.length
+        ? [
+            {
+              id: columnConfigs[0].key,
+              desc: true,
+            },
+          ]
+        : [],
+    [rowType, columnConfigs],
+  );
 
   return (
     <TimeTableStyles
       data-test="time-table"
       className={className}
       height={height}
+      hideTable={!isSizeStable}
     >
       <TableView
         className="table-no-hover"