Skip to content

Commit d6e79e5

Browse files
authored
fix(AnalyticalTable): don't scroll to top if expandable row is collapsed (#4420)
Fixes #4409
1 parent 7db4ac8 commit d6e79e5

File tree

6 files changed

+112
-11
lines changed

6 files changed

+112
-11
lines changed

packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,67 @@ describe('AnalyticalTable', () => {
16311631
cy.get('[data-column-id="name"]').should('have.attr', 'aria-sort', 'descending').and('not.have.attr', 'aria-label');
16321632
});
16331633

1634+
it("Expandable: don't scroll when expanded/collapsed", () => {
1635+
cy.mount(<AnalyticalTable data={[...dataTree, ...dataTree]} columns={columns} isTreeTable visibleRows={5} />);
1636+
cy.findAllByText('Katy Bradshaw').eq(1).trigger('keydown', {
1637+
key: 'Enter'
1638+
});
1639+
cy.get('[data-component-name="AnalyticalTableBody"]').scrollTo('bottom');
1640+
cy.findByText('Carol Perez').trigger('keydown', {
1641+
key: 'Enter'
1642+
});
1643+
cy.get('[data-component-name="AnalyticalTableBody"]').should('not.equal', 0);
1644+
cy.findByText('Carol Perez').trigger('keydown', {
1645+
key: 'Enter'
1646+
});
1647+
cy.get('[data-component-name="AnalyticalTableBody"]').invoke('scrollTop').should('not.equal', 0);
1648+
1649+
cy.mount(<AnalyticalTable data={[...data, ...data]} columns={columns} visibleRows={5} groupable />);
1650+
cy.findByText('Name').click();
1651+
cy.findByText('Group').click();
1652+
cy.findByText('A (2)').trigger('keydown', {
1653+
key: 'Enter'
1654+
});
1655+
cy.findByText('B (2)').trigger('keydown', {
1656+
key: 'Enter'
1657+
});
1658+
cy.get('[data-component-name="AnalyticalTableBody"]').scrollTo('bottom');
1659+
cy.findByText('C (2)').trigger('keydown', {
1660+
key: 'Enter'
1661+
});
1662+
cy.get('[data-component-name="AnalyticalTableBody"]').invoke('scrollTop').should('not.equal', 0);
1663+
cy.get('[data-component-name="AnalyticalTableBody"]').scrollTo('bottom');
1664+
cy.findByText('C (2)').trigger('keydown', {
1665+
key: 'Enter'
1666+
});
1667+
cy.get('[data-component-name="AnalyticalTableBody"]').invoke('scrollTop').should('not.equal', 0);
1668+
1669+
const renderRowSubComponent = () => {
1670+
return (
1671+
<div style={{ height: '80px' }} title="subcomponent">
1672+
SubComponent
1673+
</div>
1674+
);
1675+
};
1676+
cy.mount(<AnalyticalTable data={data} columns={columns} renderRowSubComponent={renderRowSubComponent} />);
1677+
cy.findByText('A').trigger('keydown', {
1678+
key: 'Enter'
1679+
});
1680+
cy.findByText('B').trigger('keydown', {
1681+
key: 'Enter'
1682+
});
1683+
cy.get('[data-component-name="AnalyticalTableBody"]').scrollTo('bottom');
1684+
cy.findByText('X').trigger('keydown', {
1685+
key: 'Enter'
1686+
});
1687+
cy.get('[data-component-name="AnalyticalTableBody"]').invoke('scrollTop').should('not.equal', 0);
1688+
cy.get('[data-component-name="AnalyticalTableBody"]').scrollTo('bottom');
1689+
cy.findByText('X').trigger('keydown', {
1690+
key: 'Enter'
1691+
});
1692+
cy.get('[data-component-name="AnalyticalTableBody"]').invoke('scrollTop').should('not.equal', 0);
1693+
});
1694+
16341695
cypressPassThroughTestsFactory(AnalyticalTable, { data, columns });
16351696
});
16361697

packages/main/src/components/AnalyticalTable/AnalyticalTable.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ export const Subcomponents: Story = {
413413
};
414414
return (
415415
<AnalyticalTable
416+
{...args}
416417
data={args.data}
417418
columns={args.columns}
418419
renderRowSubComponent={renderRowSubComponent}

packages/main/src/components/AnalyticalTable/TableBody/VirtualTableBodyContainer.tsx

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
11
import { enrichEventWithDetails } from '@ui5/webcomponents-react-base';
22
import { clsx } from 'clsx';
3-
import React, { useCallback, useEffect, useRef, useState } from 'react';
3+
import React, { MutableRefObject, useCallback, useEffect, useRef, useState } from 'react';
4+
import { AnalyticalTablePropTypes } from '../index';
45

5-
export const VirtualTableBodyContainer = (props) => {
6+
interface VirtualTableBodyContainerProps {
7+
tableBodyHeight: number;
8+
totalColumnsWidth: number;
9+
children: any;
10+
parentRef: MutableRefObject<HTMLDivElement>;
11+
classes: Record<string, string>;
12+
infiniteScroll?: AnalyticalTablePropTypes['infiniteScroll'];
13+
infiniteScrollThreshold?: AnalyticalTablePropTypes['infiniteScrollThreshold'];
14+
onLoadMore: AnalyticalTablePropTypes['onLoadMore'];
15+
rows: Record<string, any>[];
16+
internalRowHeight: number;
17+
handleExternalScroll: AnalyticalTablePropTypes['onTableScroll'];
18+
visibleRows: number;
19+
popInRowHeight: number;
20+
rowCollapsedFlag?: boolean;
21+
dispatch: (e: { type: string; payload?: any }) => void;
22+
}
23+
24+
export const VirtualTableBodyContainer = (props: VirtualTableBodyContainerProps) => {
625
const {
726
tableBodyHeight,
827
totalColumnsWidth,
@@ -16,7 +35,9 @@ export const VirtualTableBodyContainer = (props) => {
1635
internalRowHeight,
1736
handleExternalScroll,
1837
visibleRows,
19-
popInRowHeight
38+
popInRowHeight,
39+
rowCollapsedFlag,
40+
dispatch
2041
} = props;
2142
const [isMounted, setIsMounted] = useState(false);
2243

@@ -35,12 +56,19 @@ export const VirtualTableBodyContainer = (props) => {
3556

3657
useEffect(() => {
3758
if (prevDataLength.current > dataLength) {
38-
firedInfiniteLoadEvents.current.clear();
39-
parentRef.current.scrollTop = 0;
40-
lastScrollTop.current = 0;
59+
if (rowCollapsedFlag) {
60+
dispatch({
61+
type: 'ROW_COLLAPSED_FLAG',
62+
payload: false
63+
});
64+
} else {
65+
firedInfiniteLoadEvents.current.clear();
66+
parentRef.current.scrollTop = 0;
67+
lastScrollTop.current = 0;
68+
}
4169
}
4270
prevDataLength.current = dataLength;
43-
}, [dataLength]);
71+
}, [dataLength, rowCollapsedFlag]);
4472

4573
const onScroll = useCallback(
4674
(event) => {
@@ -55,12 +83,12 @@ export const VirtualTableBodyContainer = (props) => {
5583
if (rows.length - currentLastRow < infiniteScrollThreshold) {
5684
if (!firedInfiniteLoadEvents.current.has(rows.length)) {
5785
const rootNodes = rows.filter((row) => row.depth === 0);
58-
onLoadMore({
59-
detail: {
86+
onLoadMore(
87+
enrichEventWithDetails(event, {
6088
rowCount: rootNodes.length,
6189
totalRowCount: rows.length
62-
}
63-
});
90+
})
91+
);
6492
}
6593
firedInfiniteLoadEvents.current.add(rows.length);
6694
}

packages/main/src/components/AnalyticalTable/hooks/useToggleRowExpand.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { enrichEventWithDetails } from '@ui5/webcomponents-react-base';
22

33
const getToggleRowExpandedProps = (rowProps, { row, instance }) => {
4+
const { dispatch } = instance;
45
const { onRowExpandChange, isTreeTable, renderRowSubComponent } = instance.webComponentsReactProperties;
56
const onClick = (e, noPropagation = true) => {
67
if (noPropagation) {
@@ -13,6 +14,12 @@ const getToggleRowExpandedProps = (rowProps, { row, instance }) => {
1314
column = row.cells.find((cell) => cell.column.id === row.groupByID).column;
1415
}
1516

17+
if (row.isExpanded) {
18+
dispatch({
19+
type: 'ROW_COLLAPSED_FLAG',
20+
payload: true
21+
});
22+
}
1623
onRowExpandChange(enrichEventWithDetails(e, { row, column }));
1724
};
1825
const onKeyDown = (e) => {

packages/main/src/components/AnalyticalTable/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,8 @@ const AnalyticalTable = forwardRef<AnalyticalTableDomRef, AnalyticalTablePropTyp
10971097
)}
10981098
{rawData?.length > 0 && tableRef.current && (
10991099
<VirtualTableBodyContainer
1100+
rowCollapsedFlag={tableState.rowCollapsed}
1101+
dispatch={dispatch}
11001102
tableBodyHeight={tableBodyHeight}
11011103
totalColumnsWidth={totalColumnsWidth}
11021104
parentRef={parentRef}

packages/main/src/components/AnalyticalTable/tableReducer/stateReducer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ export const stateReducer = (prevState, action) => {
4949
return { ...prevState, tableColResized: payload };
5050
case 'SELECT_ROW_CB':
5151
return { ...prevState, selectedRowPayload: payload };
52+
case 'ROW_COLLAPSED_FLAG':
53+
return { ...prevState, rowCollapsed: payload };
5254
default:
5355
return prevState;
5456
}

0 commit comments

Comments
 (0)