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
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "fix: keep pivot row tree scroll position on toggle",
"type": "patch",
"packageName": "@visactor/vtable"
}
],
"packageName": "@visactor/vtable",
"email": "892739385@qq.com"
}
249 changes: 249 additions & 0 deletions packages/vtable/__tests__/pivotTable-tree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -659,4 +659,253 @@ describe('pivotTable grid-tree hierarchy scroll', () => {
pivotTable.release();
}
});

test('keeps scroll top after expanding a row tree node at scroll bottom', async () => {
const containerDom: HTMLElement = createDiv();
containerDom.style.position = 'relative';
containerDom.style.width = '800px';
containerDom.style.height = '400px';

const columnTree = Array.from({ length: 5 }, (_, index) => ({
value: `indicator-${index}`,
indicatorKey: `indicator-${index}`
}));
const indicators = columnTree.map(item => ({
indicatorKey: item.indicatorKey,
caption: item.value,
width: 100
}));

const pivotTable = new PivotTable({
container: containerDom,
records: [],
rowTree: [
...Array.from({ length: 30 }, (_, index) => ({
dimensionKey: 'category',
value: `category-${index}`
})),
{
dimensionKey: 'category',
value: 'Technology',
hierarchyState: 'collapse',
children: Array.from({ length: 20 }, (_, index) => ({
dimensionKey: 'subCategory',
value: `technology-${index}`
}))
}
],
columnTree,
rows: [
{
dimensionKey: 'category',
title: 'category',
width: 200
},
{
dimensionKey: 'subCategory',
title: 'subCategory',
width: 200
}
],
columns: [],
indicators,
defaultRowHeight: 40,
defaultHeaderRowHeight: 40,
rowHierarchyType: 'grid-tree',
columnHierarchyType: 'grid-tree',
widthMode: 'standard'
});

try {
const getMaxScrollTop = () => Math.max(0, pivotTable.getAllRowsHeight() - pivotTable.scenegraph.height);

pivotTable.setScrollTop(Number.MAX_SAFE_INTEGER);
const oldMaxScrollTop = getMaxScrollTop();
expect(pivotTable.scrollTop).toBe(oldMaxScrollTop);

const visibleRowRange = pivotTable.getBodyVisibleRowRange();
let targetRow = -1;
for (let row = visibleRowRange.rowStart; row <= visibleRowRange.rowEnd; row++) {
if (pivotTable.getCellValue(0, row) === 'Technology' && pivotTable.getHierarchyState(0, row) === 'collapse') {
targetRow = row;
break;
}
}
expect(targetRow).toBeGreaterThanOrEqual(0);
pivotTable.toggleHierarchyState(0, targetRow);
await new Promise(resolve => setTimeout(resolve, 0));

expect(getMaxScrollTop()).toBeGreaterThan(oldMaxScrollTop);
expect(pivotTable.scrollTop).toBe(oldMaxScrollTop);
} finally {
pivotTable.release();
}
});

test('returns to scroll bottom after expanding and collapsing a bottom row tree node', async () => {
const containerDom: HTMLElement = createDiv();
containerDom.style.position = 'relative';
containerDom.style.width = '800px';
containerDom.style.height = '400px';

const columnTree = Array.from({ length: 5 }, (_, index) => ({
value: `indicator-${index}`,
indicatorKey: `indicator-${index}`
}));
const indicators = columnTree.map(item => ({
indicatorKey: item.indicatorKey,
caption: item.value,
width: 100
}));

const pivotTable = new PivotTable({
container: containerDom,
records: [],
rowTree: [
...Array.from({ length: 30 }, (_, index) => ({
dimensionKey: 'category',
value: `category-${index}`
})),
{
dimensionKey: 'category',
value: 'Technology',
hierarchyState: 'collapse',
children: Array.from({ length: 20 }, (_, index) => ({
dimensionKey: 'subCategory',
value: `technology-${index}`
}))
}
],
columnTree,
rows: [
{
dimensionKey: 'category',
title: 'category',
width: 200
},
{
dimensionKey: 'subCategory',
title: 'subCategory',
width: 200
}
],
columns: [],
indicators,
defaultRowHeight: 40,
defaultHeaderRowHeight: 40,
rowHierarchyType: 'grid-tree',
columnHierarchyType: 'grid-tree',
widthMode: 'standard'
});

try {
const getMaxScrollTop = () => Math.max(0, pivotTable.getAllRowsHeight() - pivotTable.scenegraph.height);

pivotTable.setScrollTop(Number.MAX_SAFE_INTEGER);
const collapsedMaxScrollTop = getMaxScrollTop();
expect(pivotTable.scrollTop).toBe(collapsedMaxScrollTop);

const visibleRowRange = pivotTable.getBodyVisibleRowRange();
let targetRow = -1;
for (let row = visibleRowRange.rowStart; row <= visibleRowRange.rowEnd; row++) {
if (pivotTable.getCellValue(0, row) === 'Technology' && pivotTable.getHierarchyState(0, row) === 'collapse') {
targetRow = row;
break;
}
}
expect(targetRow).toBeGreaterThanOrEqual(0);

pivotTable.toggleHierarchyState(0, targetRow);
await new Promise(resolve => setTimeout(resolve, 0));
expect(getMaxScrollTop()).toBeGreaterThan(collapsedMaxScrollTop);
expect(pivotTable.scrollTop).toBe(collapsedMaxScrollTop);

pivotTable.toggleHierarchyState(0, targetRow);
await new Promise(resolve => setTimeout(resolve, 0));
expect(getMaxScrollTop()).toBe(collapsedMaxScrollTop);
expect(pivotTable.scrollTop).toBe(getMaxScrollTop());
} finally {
pivotTable.release();
}
});

test('keeps scroll top after expanding a visible row tree node', async () => {
const containerDom: HTMLElement = createDiv();
containerDom.style.position = 'relative';
containerDom.style.width = '800px';
containerDom.style.height = '400px';

const columnTree = Array.from({ length: 5 }, (_, index) => ({
value: `indicator-${index}`,
indicatorKey: `indicator-${index}`
}));
const indicators = columnTree.map(item => ({
indicatorKey: item.indicatorKey,
caption: item.value,
width: 100
}));

const pivotTable = new PivotTable({
container: containerDom,
records: [],
rowTree: [
...Array.from({ length: 10 }, (_, index) => ({
dimensionKey: 'region',
value: `region-${index}`,
hierarchyState: 'collapse',
children: Array.from({ length: 4 }, (_, childIndex) => ({
dimensionKey: 'province',
value: `province-${index}-${childIndex}`
}))
})),
{
dimensionKey: 'region',
value: 'grand-total'
}
],
columnTree,
rows: [
{
dimensionKey: 'region',
title: 'region',
width: 200
},
{
dimensionKey: 'province',
title: 'province',
width: 200
}
],
columns: [],
indicators,
defaultRowHeight: 40,
defaultHeaderRowHeight: 40,
rowHierarchyType: 'grid-tree',
columnHierarchyType: 'grid-tree',
widthMode: 'standard'
});

try {
const scrollTop = 90;
pivotTable.setScrollTop(scrollTop);
const oldScrollTop = pivotTable.scrollTop;

const visibleRowRange = pivotTable.getBodyVisibleRowRange();
let targetRow = -1;
for (let row = visibleRowRange.rowStart; row <= visibleRowRange.rowEnd; row++) {
if (pivotTable.getHierarchyState(0, row) === 'collapse') {
targetRow = row;
break;
}
}
expect(targetRow).toBeGreaterThanOrEqual(0);

pivotTable.toggleHierarchyState(0, targetRow);
await new Promise(resolve => setTimeout(resolve, 0));

expect(pivotTable.scrollTop).toBe(oldScrollTop);
} finally {
pivotTable.release();
}
});
});
18 changes: 16 additions & 2 deletions packages/vtable/src/PivotTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1571,6 +1571,7 @@ export class PivotTable extends BaseTable implements PivotTableAPI {
Math.max(0, this.getAllRowsHeight() - (this.scenegraph?.height ?? this.tableNoFrameHeight) - sizeTolerance);
const oldMaxScrollTop = getMaxScrollTop();
const isScrollToBottom = oldMaxScrollTop > 0 && this.scrollTop >= oldMaxScrollTop - 1;
const oldScrollTop = this.scrollTop;
const visibleStartRow = this.getBodyVisibleRowRange().rowStart;
this.internalProps._oldRowCount = this.rowCount;
this.internalProps._oldColCount = this.colCount;
Expand All @@ -1588,6 +1589,9 @@ export class PivotTable extends BaseTable implements PivotTableAPI {
}
}
const isChangeRowTree = this.internalProps.layoutMap.isRowHeader(col, row);
const oldHierarchyState = isChangeRowTree ? this.getHierarchyState(col, row) : undefined;
const shouldKeepBottomAfterToggle = isScrollToBottom && oldHierarchyState === 'expand';
const shouldKeepScrollAfterToggle = isChangeRowTree && !shouldKeepBottomAfterToggle;
const result: {
addCellPositionsRowDirection?: CellAddress[];
removeCellPositionsRowDirection?: CellAddress[];
Expand Down Expand Up @@ -1645,17 +1649,27 @@ export class PivotTable extends BaseTable implements PivotTableAPI {
this.clearCellStyleCache();
this.scenegraph.createSceneGraph();
this.scrollToRow(visibleStartRow);
if (isScrollToBottom) {
if (shouldKeepBottomAfterToggle) {
this.clearCorrectTimer();
this.setScrollTop(Number.MAX_SAFE_INTEGER);
} else if (shouldKeepScrollAfterToggle) {
this.clearCorrectTimer();
this.setScrollTop(oldScrollTop);
}
// this.renderWithRecreateCells();
}
this.reactCustomLayout?.updateAllCustomCell();

if (isScrollToBottom) {
if (shouldKeepBottomAfterToggle) {
this.clearCorrectTimer();
this.setScrollTop(Number.MAX_SAFE_INTEGER);
} else if (
shouldKeepScrollAfterToggle &&
this.rowHierarchyType !== 'grid-tree' &&
this.columnHierarchyType !== 'grid-tree'
) {
this.clearCorrectTimer();
this.setScrollTop(oldScrollTop);
}

if (checkHasChart) {
Expand Down
Loading