Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 65 additions & 50 deletions packages/grid/src/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -488,12 +488,11 @@ class Grid extends PureComponent<GridProps, GridState> {
// apply on mount, so that it works with a static model
// https://github.com/deephaven/web-client-ui/issues/581
const { isStuckToBottom, isStuckToRight } = this.props;
if (isStuckToBottom) {
this.scrollToBottom();
}
if (isStuckToRight) {
this.scrollToRight();
}
const { top, left } = this.getStickyScrollPosition(
isStuckToBottom,
isStuckToRight
);
this.setState({ top, left });
}

componentDidUpdate(prevProps: GridProps, prevState: GridState): void {
Expand Down Expand Up @@ -576,7 +575,14 @@ class Grid extends PureComponent<GridProps, GridState> {

this.requestUpdateCanvas();

this.checkStickyScroll();
if (this.needToUpdateScroll()) {
const { top, left } = this.getStickyScrollPosition(
isStickyBottom,
isStickyRight
);
updatedState.top = top;
updatedState.left = left;
}

if (this.validateSelection()) {
this.checkSelectionChange(prevState);
Expand Down Expand Up @@ -741,29 +747,41 @@ class Grid extends PureComponent<GridProps, GridState> {
this.setState({ isStuckToBottom: false });
}

/**
* Scrolls to bottom, if not already at bottom
*/
scrollToBottom(): void {
if (!this.metrics) return;
const { bottomVisible, rowCount, top, lastTop } = this.metrics;
if ((bottomVisible < rowCount - 1 && bottomVisible > 0) || top > lastTop) {
this.setState({ top: lastTop });
}
}
getStickyScrollPosition(
isStickyBottom: boolean,
isStickyRight: boolean
): { top: VisibleIndex; left: VisibleIndex } {
// eslint-disable-next-line react/destructuring-assignment
if (!this.metrics) return { left: this.state.left, top: this.state.top };

/**
* Scrolls to right, if not already at right
*/
scrollToRight(): void {
if (!this.metrics) return;
const { rightVisible, columnCount, left, lastLeft } = this.metrics;
const {
bottomVisible,
rowCount,
top,
lastTop,
rightVisible,
columnCount,
left,
lastLeft,
} = this.metrics;

let newTop = top;
let newLeft = left;

if (
isStickyBottom &&
((bottomVisible < rowCount - 1 && bottomVisible > 0) || top > lastTop)
) {
newTop = lastTop;
}
if (
(rightVisible < columnCount - 1 && rightVisible > 0) ||
left > lastLeft
isStickyRight &&
((rightVisible < columnCount - 1 && rightVisible > 0) || left > lastLeft)
) {
this.setState({ left: lastLeft });
newLeft = lastLeft;
}

return { top: newTop, left: newLeft };
}

/**
Expand Down Expand Up @@ -888,35 +906,32 @@ class Grid extends PureComponent<GridProps, GridState> {
/**
* Compares the current metrics with the previous metrics to see if we need to scroll when it is stuck to the bottom or the right
*/
checkStickyScroll(): void {
if (!this.metrics) {
return;
needToUpdateScroll(): boolean {
if (!this.metrics || !this.prevMetrics) {
return false;
}

if (this.prevMetrics) {
const { rowCount, columnCount, height, width } = this.metrics;
const {
rowCount: prevRowCount,
columnCount: prevColumnCount,
height: prevHeight,
width: prevWidth,
} = this.prevMetrics;

if (prevRowCount !== rowCount || height !== prevHeight) {
const { isStuckToBottom } = this.state;
if (isStuckToBottom) {
this.scrollToBottom();
}
const { rowCount, columnCount, height, width } = this.metrics;
const {
rowCount: prevRowCount,
columnCount: prevColumnCount,
height: prevHeight,
width: prevWidth,
} = this.prevMetrics;

if (prevRowCount !== rowCount || height !== prevHeight) {
const { isStuckToBottom } = this.state;
if (isStuckToBottom) {
return true;
}

if (prevColumnCount !== columnCount || width !== prevWidth) {
const { isStuckToRight } = this.state;
if (isStuckToRight) {
this.scrollToRight();
}
}
if (prevColumnCount !== columnCount || width !== prevWidth) {
const { isStuckToRight } = this.state;
if (isStuckToRight) {
return true;
}
}
this.prevMetrics = this.metrics;
return false;
}

updateMetrics(state = this.state): GridMetrics {
Expand Down
26 changes: 26 additions & 0 deletions tests/docker-scripts/data/app.d/table_generators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from deephaven import empty_table, function_generated_table
from deephaven.execution_context import get_exec_ctx

# Creates a table that can be shrunk and grown in size
def create_shrink_grow_table():
ctx = get_exec_ctx()
i = 50

def make_table():
return empty_table(i).update(["X = i"])

def shrink_table():
nonlocal i
i = 30;

def grow_table():
nonlocal i
i = 70;

table = function_generated_table(
table_generator=make_table,
refresh_interval_ms=1000,
exec_ctx=ctx
)

return table, shrink_table, grow_table
1 change: 1 addition & 0 deletions tests/docker-scripts/data/app.d/test.app
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ name=Web UI Test Application
file_0=common_figures.py
file_1=common_tables.py
file_2=multiselect_tables.py
file_3=table_generators.py
98 changes: 98 additions & 0 deletions tests/table-scroll.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { test, expect } from '@playwright/test';
import {
waitForLoadingDone,
gotoPage,
pasteInMonaco,
generateId,
} from './utils';

test.beforeEach(async ({ page }) => {
await gotoPage(page, '');

// Fail quickly if console errors are detected
page.on('console', msg => {
if (msg.type() === 'error') {
throw new Error(msg.text());
}
});
page.on('pageerror', error => {
throw error;
});
});

test('stuck to bottom scroll growing table', async ({ page }) => {
// Need to generate a new shrink grow table for each test
const id = generateId();
const tableName = `_${id}_shrink_grow_table`;
const growFunction = `func_${id}`;
const consoleInput = page.locator('.console-input');

await pasteInMonaco(
consoleInput,
`${tableName}, _, ${growFunction} = create_shrink_grow_table()`
);
await page.keyboard.press('Enter');
await waitForLoadingDone(page);

// Open the newly created table
const openButton = page.getByRole('button', { name: tableName, exact: true });
await openButton.click();
await waitForLoadingDone(page);

const gridCanvas = page.locator('.iris-grid .grid-wrapper');
await gridCanvas.click({ position: { x: 10, y: 80 } });
// Scroll to end of table to get stuck at bottom
await page.mouse.wheel(0, 1000);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

// Add more rows to the table and take another screenshot
await pasteInMonaco(consoleInput, `${growFunction}()`);
await page.keyboard.press('Enter');

// No reliable way to know when the added rows have loaded, so just wait
await page.waitForTimeout(2000);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();
});

test('stuck to bottom scroll shrinking and growing table', async ({ page }) => {
// Need to generate a new shrink grow table for each test
const id = generateId();
const tableName = `_${id}_shrink_grow_table`;
const growFunction = `grow_${id}`;
const shrinkFunction = `shrink_${id}`;
const consoleInput = page.locator('.console-input');

await pasteInMonaco(
consoleInput,
`${tableName}, ${shrinkFunction}, ${growFunction} = create_shrink_grow_table()`
);
await page.keyboard.press('Enter');
await waitForLoadingDone(page);

// Open the newly created table
const openButton = page.getByRole('button', { name: tableName, exact: true });
await openButton.click();
await waitForLoadingDone(page);

const gridCanvas = page.locator('.iris-grid .grid-wrapper');
await gridCanvas.click({ position: { x: 10, y: 80 } });
// Scroll to end of table to get stuck at bottom
await page.mouse.wheel(0, 1000);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

// Shrink the table and take another screenshot
await pasteInMonaco(consoleInput, `${shrinkFunction}()`);
await page.keyboard.press('Enter');

// No reliable way to know when the removed rows have loaded, so just wait
await page.waitForTimeout(2000);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

// Add more rows to the table and take another screenshot
await pasteInMonaco(consoleInput, `${growFunction}()`);
await page.keyboard.press('Enter');

// No reliable way to know when the added rows have loaded, so just wait
await page.waitForTimeout(2000);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading