diff --git a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx index b8a8b7c616c..0123b4abb35 100644 --- a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx +++ b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx @@ -3742,10 +3742,51 @@ describe('AnalyticalTable', () => { cy.realPress('ArrowLeft'); cy.focused().should('have.attr', 'data-row-index', '0').should('have.attr', 'data-column-index', '0'); + // End/Home + const manyColumns = Array.from({ length: 30 }, (_, i) => ({ + Header: `Col ${i}`, + accessor: `col${i}`, + })); + const manyColumnsData = Array.from({ length: 10 }, (_, rowIdx) => { + const row: Record = {}; + for (let c = 0; c < 30; c++) { + row[`col${c}`] = `R${rowIdx}C${c}`; + } + return row; + }); + + cy.mount( + , + ); + + cy.findByText('R0C0').should('be.visible'); + cy.window().focus(); + cy.realPress('Tab'); + cy.focused().should('have.attr', 'data-column-index', '0'); + cy.realPress('End'); - cy.focused().should('have.attr', 'data-row-index', '0').should('have.attr', 'data-column-index', '3'); + cy.focused().should('have.attr', 'data-column-index', '29').should('have.attr', 'data-row-index', '0'); cy.realPress('Home'); - cy.focused().should('have.attr', 'data-row-index', '0').should('have.attr', 'data-column-index', '0'); + cy.focused().should('have.attr', 'data-column-index', '0').should('have.attr', 'data-row-index', '0'); + + cy.realPress('ArrowDown'); + cy.focused().should('have.attr', 'data-row-index', '1'); + cy.realPress('End'); + cy.focused().should('have.attr', 'data-column-index', '29').should('have.attr', 'data-row-index', '1'); + cy.realPress('Home'); + cy.focused().should('have.attr', 'data-column-index', '0').should('have.attr', 'data-row-index', '1'); + + // PageDown/PageUp + cy.mount(); + cy.findByText('Name-0').should('be.visible'); + cy.window().focus(); + cy.realPress('Tab'); cy.realPress('PageDown'); cy.focused().should('have.attr', 'data-row-index', '1').should('have.attr', 'data-column-index', '0'); @@ -3905,6 +3946,30 @@ describe('AnalyticalTable', () => { cy.window().focus(); cy.realPress('Tab'); cy.focused().should('have.attr', 'data-row-index', '0').should('have.attr', 'data-column-index', '2'); + + // column resize with Shift+Arrow + cy.mount(); + cy.findByText('Name').should('be.visible'); + cy.window().focus(); + cy.realPress('Tab'); + cy.focused().should('have.attr', 'data-row-index', '0').should('have.attr', 'data-column-index', '0'); + + cy.get('[data-column-id="name"]') + .invoke('outerWidth') + .then((initialWidth) => { + cy.realPress(['Shift', 'ArrowRight']); + cy.get('[data-column-id="name"]') + .invoke('outerWidth') + .should(($width: number) => { + expect($width).to.equal(initialWidth + 16); + }); + cy.realPress(['Shift', 'ArrowLeft']); + cy.get('[data-column-id="name"]') + .invoke('outerWidth') + .should(($width: number) => { + expect($width).to.equal(initialWidth); + }); + }); }); it('controlled bodyHeight', () => { diff --git a/packages/main/src/components/AnalyticalTable/hooks/useKeyboardNavigation.ts b/packages/main/src/components/AnalyticalTable/hooks/useKeyboardNavigation.ts index 37ea1bd54a3..4af7b2f1316 100644 --- a/packages/main/src/components/AnalyticalTable/hooks/useKeyboardNavigation.ts +++ b/packages/main/src/components/AnalyticalTable/hooks/useKeyboardNavigation.ts @@ -63,9 +63,24 @@ const navigateFromActiveSubCompItem = (currentlyFocusedCell: MutableRefObject, + scrollLeft: number, + targetSelector: string, + currentlyFocusedCell: MutableRefObject, +) => { + tableRef.current.scrollLeft = scrollLeft; + requestAnimationFrame(() => { + const el: HTMLElement | null = tableRef.current.querySelector(targetSelector); + if (el) { + setFocus(currentlyFocusedCell, el); + } + }); +}; + const useGetTableProps = ( tableProps, - { instance: { webComponentsReactProperties, data, columns, state } }: { instance: TableInstance }, + { instance: { webComponentsReactProperties, data, columns, state, visibleColumns } }: { instance: TableInstance }, ) => { const { showOverlay, tableRef } = webComponentsReactProperties; const { isRtl } = state; @@ -182,32 +197,34 @@ const useGetTableProps = ( switch (e.key) { case 'End': { - const visibleColumns = tableRef.current.querySelector( - `div[data-component-name="AnalyticalTableHeaderRow"]`, - ).children; - - const lastVisibleColumn = Array.from(visibleColumns) - .slice(0) - .reduceRight((_, cur, index, arr) => { - const columnIndex = parseInt((cur.children?.[0] as HTMLDivElement)?.dataset.columnIndex, 10); - if (!isNaN(columnIndex)) { - arr.length = 0; - return columnIndex; - } - return 0; - }, 0); - - const newElement: HTMLElement | null = tableRef.current.querySelector( - `div[data-visible-column-index="${lastVisibleColumn}"][data-row-index="${rowIndex}"]`, - ); - setFocus(currentlyFocusedCell, newElement); + const lastColumnIndex = visibleColumns.length - 1; + const targetSelector = `div[data-column-index="${lastColumnIndex}"][data-row-index="${rowIndex}"]`; + const newElement: HTMLElement | null = tableRef.current.querySelector(targetSelector); + if (newElement) { + setFocus(currentlyFocusedCell, newElement); + } else { + scrollToHorizontalEdgeAndFocus( + tableRef, + isRtl ? 0 : tableRef.current.scrollWidth, + targetSelector, + currentlyFocusedCell, + ); + } break; } case 'Home': { - const newElement: HTMLElement | null = tableRef.current.querySelector( - `div[data-visible-column-index="0"][data-row-index="${rowIndex}"]`, - ); - setFocus(currentlyFocusedCell, newElement); + const targetSelector = `div[data-column-index="0"][data-row-index="${rowIndex}"]`; + const newElement: HTMLElement | null = tableRef.current.querySelector(targetSelector); + if (newElement) { + setFocus(currentlyFocusedCell, newElement); + } else { + scrollToHorizontalEdgeAndFocus( + tableRef, + isRtl ? tableRef.current.scrollWidth : 0, + targetSelector, + currentlyFocusedCell, + ); + } break; } case 'PageDown': { @@ -326,7 +343,7 @@ const useGetTableProps = ( } } }, - [isRtl, tableRef], + [isRtl, tableRef, visibleColumns], ); if (showOverlay) { return tableProps; @@ -357,7 +374,7 @@ function getPayload(e: KeyboardEvent, column: ColumnType) { const columnId = column.id; const columnWidth = column.totalWidth; const headersToResize = getLeafHeaders(column); - const headerIdWidths = headersToResize.map((d) => [d.id, d.totalWidth, d.minWidth]); + const headerIdWidths = headersToResize.map((d) => [d.id, d.totalWidth, d.minWidth, d.maxWidth]); return { clientX, columnId, columnWidth, headerIdWidths }; }