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"