Skip to content

Commit 810d580

Browse files
jvigliottadavetsay
andauthored
[Telemetry Tables] Make sure tables auto scroll correctly on first load (#7720)
* run scroll method to scroll to top after initial load of historical data * clarifying comment * added e2e test to make sure tables auto scroll on mount * adding descriptive comments * adding in ascending check as well * added new appAction for navigating to/in realtime, using it in table scroll test * lint --------- Co-authored-by: David Tsay <[email protected]>
1 parent 977792f commit 810d580

File tree

3 files changed

+98
-4
lines changed

3 files changed

+98
-4
lines changed

e2e/appActions.js

+12
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,17 @@ async function navigateToObjectWithFixedTimeBounds(page, url, start, end) {
275275
);
276276
}
277277

278+
/**
279+
* Navigates directly to a given object url, in real-time mode.
280+
* @param {import('@playwright/test').Page} page
281+
* @param {string} url The url to the domainObject
282+
*/
283+
async function navigateToObjectWithRealTime(page, url, start = '1800000', end = '30000') {
284+
await page.goto(
285+
`${url}?tc.mode=local&tc.startDelta=${start}&tc.endDelta=${end}&tc.timeSystem=utc`
286+
);
287+
}
288+
278289
/**
279290
* Open the given `domainObject`'s context menu from the object tree.
280291
* Expands the path to the object and scrolls to it if necessary.
@@ -656,6 +667,7 @@ export {
656667
getFocusedObjectUuid,
657668
getHashUrlToDomainObject,
658669
navigateToObjectWithFixedTimeBounds,
670+
navigateToObjectWithRealTime,
659671
openObjectTreeContextMenu,
660672
renameObjectFromContextMenu,
661673
setEndOffset,

e2e/tests/functional/plugins/telemetryTable/telemetryTable.e2e.spec.js

+83-4
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222

2323
import {
2424
createDomainObjectWithDefaults,
25-
setTimeConductorBounds,
26-
setTimeConductorMode
25+
navigateToObjectWithRealTime,
26+
setTimeConductorBounds
2727
} from '../../../../appActions.js';
2828
import { expect, test } from '../../../../pluginFixtures.js';
2929

@@ -39,12 +39,52 @@ test.describe('Telemetry Table', () => {
3939
type: 'Sine Wave Generator',
4040
parent: table.uuid
4141
});
42-
await page.goto(table.url);
43-
await setTimeConductorMode(page, false);
42+
await navigateToObjectWithRealTime(page, table.url);
4443
const rows = page.getByLabel('table content').getByLabel('Table Row');
4544
await expect(rows).toHaveCount(50);
4645
});
4746

47+
test('on load, auto scrolls to top for descending, and to bottom for ascending', async ({
48+
page
49+
}) => {
50+
const sineWaveGenerator = await createDomainObjectWithDefaults(page, {
51+
type: 'Sine Wave Generator',
52+
parent: table.uuid
53+
});
54+
55+
// verify in telemetry table object view
56+
await navigateToObjectWithRealTime(page, table.url);
57+
58+
expect(await getScrollPosition(page)).toBe(0);
59+
60+
// verify in telemetry table view
61+
await page.goto(sineWaveGenerator.url);
62+
await page.getByLabel('Open the View Switcher Menu').click();
63+
await page.getByText('Telemetry Table', { exact: true }).click();
64+
65+
expect(await getScrollPosition(page)).toBe(0);
66+
67+
// navigate back to table
68+
await page.goto(table.url);
69+
70+
// go into edit mode
71+
await page.getByLabel('Edit Object').click();
72+
73+
// change sort direction
74+
await page.locator('thead div').filter({ hasText: 'Time' }).click();
75+
76+
// save view
77+
await page.getByRole('button', { name: 'Save' }).click();
78+
await page.getByRole('listitem', { name: 'Save and Finish Editing' }).click();
79+
80+
// navigate away and back
81+
await page.goto(sineWaveGenerator.url);
82+
await page.goto(table.url);
83+
84+
// verify scroll position
85+
expect(await getScrollPosition(page, false)).toBeLessThan(1);
86+
});
87+
4888
test('unpauses and filters data when paused by button and user changes bounds', async ({
4989
page
5090
}) => {
@@ -183,3 +223,42 @@ test.describe('Telemetry Table', () => {
183223
await page.click('button[title="Pause"]');
184224
});
185225
});
226+
227+
async function getScrollPosition(page, top = true) {
228+
const tableBody = page.locator('.c-table__body-w');
229+
230+
// Wait for the scrollbar to appear
231+
await tableBody.evaluate((node) => {
232+
return new Promise((resolve) => {
233+
function checkScroll() {
234+
if (node.scrollHeight > node.clientHeight) {
235+
resolve();
236+
} else {
237+
setTimeout(checkScroll, 100);
238+
}
239+
}
240+
checkScroll();
241+
});
242+
});
243+
244+
// make sure there are rows
245+
const rows = page.getByLabel('table content').getByLabel('Table Row');
246+
await rows.first().waitFor();
247+
248+
// Using this to allow for rows to come and go, so we can truly test the scroll position
249+
// eslint-disable-next-line playwright/no-wait-for-timeout
250+
await page.waitForTimeout(1000);
251+
252+
const { scrollTop, clientHeight, scrollHeight } = await tableBody.evaluate((node) => ({
253+
scrollTop: node.scrollTop,
254+
clientHeight: node.clientHeight,
255+
scrollHeight: node.scrollHeight
256+
}));
257+
258+
// eslint-disable-next-line playwright/no-conditional-in-test
259+
if (top) {
260+
return scrollTop;
261+
} else {
262+
return Math.abs(scrollHeight - (scrollTop + clientHeight));
263+
}
264+
}

src/plugins/telemetryTable/components/TableComponent.vue

+3
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,9 @@ export default {
561561

562562
this.table.initialize();
563563
this.rescaleToContainer();
564+
565+
// Scroll to the top of the table after loading
566+
this.addToAfterLoadActions(this.scroll);
564567
},
565568
beforeUnmount() {
566569
this.table.off('object-added', this.addObject);

0 commit comments

Comments
 (0)