From 94a52afcee1ca7f1d2c03e9ded5f5b931e9f9af2 Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Mon, 5 May 2025 14:17:42 +0200 Subject: [PATCH 1/3] fix(AnalyticalTable): restore focus correctly when ungrouping a column --- .../AnalyticalTable/AnalyticalTable.cy.tsx | 14 ++++++++++++++ .../AnalyticalTable/ColumnHeader/index.tsx | 13 ++++++++++++- .../defaults/Column/ColumnHeaderModal.tsx | 11 +++++++---- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx index 86a53f920ef..a7a9e638f21 100644 --- a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx +++ b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx @@ -874,6 +874,12 @@ describe('AnalyticalTable', () => { cy.findByText('Friend Name').click(); cy.findByText('Group').realClick(); + cy.get('[data-component-name="ATHeaderContainer-friend.name"]').should('have.attr', 'data-prev-opener'); + cy.focused() + .should('have.attr', 'data-row-index', '0') + .and('have.attr', 'data-column-index', '2') + .and('have.text', 'Friend Name'); + cy.get('[aria-rowindex="7"] > [aria-colindex="3"] > [title="Expand Node"] > [ui5-icon]').click(); cy.findByText('25').click(); @@ -887,6 +893,14 @@ describe('AnalyticalTable', () => { cy.findByTestId('selectedFlatRowsLength').should('have.text', '1'); cy.findByTestId('selectedRowIds').should('have.text', '{"2":true}'); cy.findByTestId('isSelected').should('have.text', 'false'); + + cy.findByText('Friend Name').click(); + cy.findByText('Ungroup').realClick(); + cy.focused() + .should('have.attr', 'data-row-index', '0') + .and('have.attr', 'data-column-index', '3') + .and('have.text', 'Friend Name'); + cy.get('[data-component-name="ATHeaderContainer-friend.name"]').should('not.have.attr', 'data-prev-opener'); }); it('useIndeterminateRowSelection - select subRows', () => { diff --git a/packages/main/src/components/AnalyticalTable/ColumnHeader/index.tsx b/packages/main/src/components/AnalyticalTable/ColumnHeader/index.tsx index ab54964c9a0..62a0ce77b76 100644 --- a/packages/main/src/components/AnalyticalTable/ColumnHeader/index.tsx +++ b/packages/main/src/components/AnalyticalTable/ColumnHeader/index.tsx @@ -5,7 +5,7 @@ import iconFilter from '@ui5/webcomponents-icons/dist/filter.js'; import iconGroup from '@ui5/webcomponents-icons/dist/group-2.js'; import iconSortAscending from '@ui5/webcomponents-icons/dist/sort-ascending.js'; import iconSortDescending from '@ui5/webcomponents-icons/dist/sort-descending.js'; -import { ThemingParameters } from '@ui5/webcomponents-react-base'; +import { ThemingParameters, useIsomorphicLayoutEffect } from '@ui5/webcomponents-react-base'; import { clsx } from 'clsx'; import type { AriaAttributes, @@ -163,6 +163,16 @@ export const ColumnHeader = (props: ColumnHeaderProps) => { } }; + // restore focus to an ungrouped header column + // ungrouping causes reordering thus unmounting the header cell -> losing focus + useIsomorphicLayoutEffect(() => { + const prevOpener = columnHeaderRef.current; + if (column.canGroupBy && prevOpener?.dataset.prevOpener === columnId) { + (prevOpener.children[0] as HTMLDivElement).focus(); + } + prevOpener?.removeAttribute('data-prev-opener'); + }, []); + if (!column) return null; return (
{ width: `${virtualColumn.size}px`, ...directionStyles }} + data-component-name={`ATHeaderContainer-${columnId}`} >
{ const isSortedAscending = column.isSorted && column.isSortedDesc === false; const isSortedDescending = column.isSorted && column.isSortedDesc === true; - const onAfterClose = (e) => { + const onAfterClose: PopoverPropTypes['onClose'] = (e) => { + if (column.isGrouped) { + (e.currentTarget.opener as HTMLDivElement).setAttribute('data-prev-opener', column.id); + } stopPropagation(e); setOpen(false); }; @@ -167,13 +170,13 @@ export const ColumnHeaderModal = (instance: TableInstanceWithPopoverProps) => { }; useEffect(() => { - if (open && ref.current && openerRef.current) { + if (ref.current && openerRef.current) { void customElements.whenDefined(getUi5TagWithSuffix('ui5-popover')).then(() => { ref.current.opener = openerRef.current; ref.current.open = true; }); } - }, [open]); + }, []); return ( Date: Mon, 5 May 2025 15:57:28 +0200 Subject: [PATCH 2/3] remove flaky part of test (is redundant) --- .../main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx index a7a9e638f21..2c1f29649d4 100644 --- a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx +++ b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx @@ -874,7 +874,6 @@ describe('AnalyticalTable', () => { cy.findByText('Friend Name').click(); cy.findByText('Group').realClick(); - cy.get('[data-component-name="ATHeaderContainer-friend.name"]').should('have.attr', 'data-prev-opener'); cy.focused() .should('have.attr', 'data-row-index', '0') .and('have.attr', 'data-column-index', '2') From 58db62ed146cd734d9d4c763bdfd0e3eefec4e47 Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Tue, 6 May 2025 09:44:38 +0200 Subject: [PATCH 3/3] support React18 --- .../components/AnalyticalTable/AnalyticalTable.cy.tsx | 1 - .../components/AnalyticalTable/ColumnHeader/index.tsx | 10 +++++----- .../defaults/Column/ColumnHeaderModal.tsx | 3 --- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx index 2c1f29649d4..d9bd6c38a9f 100644 --- a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx +++ b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx @@ -899,7 +899,6 @@ describe('AnalyticalTable', () => { .should('have.attr', 'data-row-index', '0') .and('have.attr', 'data-column-index', '3') .and('have.text', 'Friend Name'); - cy.get('[data-component-name="ATHeaderContainer-friend.name"]').should('not.have.attr', 'data-prev-opener'); }); it('useIndeterminateRowSelection - select subRows', () => { diff --git a/packages/main/src/components/AnalyticalTable/ColumnHeader/index.tsx b/packages/main/src/components/AnalyticalTable/ColumnHeader/index.tsx index 62a0ce77b76..67eb419943f 100644 --- a/packages/main/src/components/AnalyticalTable/ColumnHeader/index.tsx +++ b/packages/main/src/components/AnalyticalTable/ColumnHeader/index.tsx @@ -163,15 +163,15 @@ export const ColumnHeader = (props: ColumnHeaderProps) => { } }; - // restore focus to an ungrouped header column - // ungrouping causes reordering thus unmounting the header cell -> losing focus + // restore focus after grouping to header cell + const prevIsGrouped = useRef(null); useIsomorphicLayoutEffect(() => { const prevOpener = columnHeaderRef.current; - if (column.canGroupBy && prevOpener?.dataset.prevOpener === columnId) { + if (column.canGroupBy && prevIsGrouped.current && !popoverOpen && prevOpener) { (prevOpener.children[0] as HTMLDivElement).focus(); } - prevOpener?.removeAttribute('data-prev-opener'); - }, []); + prevIsGrouped.current = column.isGrouped; + }, [popoverOpen]); if (!column) return null; return ( diff --git a/packages/main/src/components/AnalyticalTable/defaults/Column/ColumnHeaderModal.tsx b/packages/main/src/components/AnalyticalTable/defaults/Column/ColumnHeaderModal.tsx index bb9bbbb3894..b3d88be56d8 100644 --- a/packages/main/src/components/AnalyticalTable/defaults/Column/ColumnHeaderModal.tsx +++ b/packages/main/src/components/AnalyticalTable/defaults/Column/ColumnHeaderModal.tsx @@ -129,9 +129,6 @@ export const ColumnHeaderModal = (instance: TableInstanceWithPopoverProps) => { const isSortedDescending = column.isSorted && column.isSortedDesc === true; const onAfterClose: PopoverPropTypes['onClose'] = (e) => { - if (column.isGrouped) { - (e.currentTarget.opener as HTMLDivElement).setAttribute('data-prev-opener', column.id); - } stopPropagation(e); setOpen(false); };