Skip to content

Commit 09d8d19

Browse files
authored
fix(ObjectPage): handle scrollPaddingBlock when relatedTarget is null (#7631)
Fixes #7570
1 parent 18af555 commit 09d8d19

File tree

3 files changed

+89
-11
lines changed

3 files changed

+89
-11
lines changed

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

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,14 @@ import {
4343
Title,
4444
Toolbar,
4545
ToolbarButton,
46+
Table,
47+
TableCell,
48+
TableHeaderCell,
49+
TableHeaderRow,
50+
TableRow,
4651
} from '../..';
47-
import { cypressPassThroughTestsFactory } from '@/cypress/support/utils';
4852
import type { TabDomRef } from '../../webComponents/Tab/index.js';
53+
import { cypressPassThroughTestsFactory } from '@/cypress/support/utils';
4954

5055
const arbitraryCharsId = `~\`!1@#$%^&*()-_+={}[]:;"'z,<.>/?|♥`;
5156

@@ -1651,6 +1656,22 @@ describe('ObjectPage', () => {
16511656
<Input data-testid="sub" />
16521657
</ObjectPageSubSection>
16531658
</ObjectPageSection>
1659+
<ObjectPageSection id={'7'} titleText={'Table'} aria-label="Table">
1660+
<Table data-testid="table">
1661+
<TableHeaderRow slot="headerRow">
1662+
<TableHeaderCell>Product</TableHeaderCell>
1663+
<TableHeaderCell>Supplier</TableHeaderCell>
1664+
<TableHeaderCell horizontalAlign="End">Price</TableHeaderCell>
1665+
</TableHeaderRow>
1666+
{new Array(20).fill(1337).map((_, i) => (
1667+
<TableRow key={i}>
1668+
<TableCell>Mac</TableCell>
1669+
<TableCell>Apple</TableCell>
1670+
<TableCell horizontalAlign="End">10.09</TableCell>
1671+
</TableRow>
1672+
))}
1673+
</Table>
1674+
</ObjectPageSection>
16541675
</ObjectPage>
16551676
</>,
16561677
);
@@ -1691,9 +1712,18 @@ describe('ObjectPage', () => {
16911712
// 6.2 input
16921713
cy.realPress('Tab');
16931714
cy.findByTestId('sub').should('be.focused');
1715+
// Table
1716+
cy.realPress('Tab');
1717+
cy.focused().should('have.attr', 'ui5-table-row');
16941718
//footer
16951719
cy.realPress('Tab');
16961720
cy.findByTestId('footer-accept-btn').should('be.focused');
1721+
// Table
1722+
cy.realPress(['Shift', 'Tab']);
1723+
cy.focused().should('have.attr', 'ui5-table-row');
1724+
// Table Section
1725+
cy.realPress(['Shift', 'Tab']);
1726+
cy.focused().should('have.attr', 'aria-label', 'Table').and('have.attr', 'tabindex', 0);
16971727
// 6.2 input
16981728
cy.realPress(['Shift', 'Tab']);
16991729
cy.findByTestId('sub').should('be.focused');
@@ -1730,10 +1760,12 @@ describe('ObjectPage', () => {
17301760
cy.get('[data-component-name="ObjectPageSubSection"]').should('have.attr', 'tabindex', -1);
17311761

17321762
// click first Tab
1733-
cy.focused().realClick();
1763+
cy.log('click first Tab');
1764+
cy.get('[ui5-tabcontainer]').findUi5TabByText('Goals').click();
17341765
cy.focused().should('have.attr', 'aria-label', 'Goals').and('have.attr', 'tabindex', 0);
17351766

17361767
// arrow section navigation
1768+
cy.log('arrow section navigation');
17371769
cy.realPress('ArrowUp');
17381770
cy.focused().should('have.attr', 'aria-label', 'Goals').and('have.attr', 'tabindex', 0);
17391771
cy.realPress('ArrowDown');
@@ -1747,9 +1779,25 @@ describe('ObjectPage', () => {
17471779
cy.realPress('ArrowDown');
17481780
cy.focused().should('have.attr', 'aria-label', 'SubSectionsInput').and('have.attr', 'tabindex', 0);
17491781
cy.realPress('ArrowDown');
1750-
cy.focused().should('have.attr', 'aria-label', 'SubSectionsInput').and('have.attr', 'tabindex', 0);
1782+
cy.focused()
1783+
.should('have.attr', 'aria-label', 'Table')
1784+
.and('have.attr', 'tabindex', 0)
1785+
.then(($el) => {
1786+
const rect = $el[0].getBoundingClientRect();
1787+
expect(rect.top).to.be.at.most(214);
1788+
});
1789+
cy.realPress('ArrowDown');
1790+
cy.focused()
1791+
.should('have.attr', 'aria-label', 'Table')
1792+
.and('have.attr', 'tabindex', 0)
1793+
.then(($el) => {
1794+
const rect = $el[0].getBoundingClientRect();
1795+
expect(rect.top).to.be.at.most(211);
1796+
});
17511797

17521798
// arrow subsection navigation
1799+
cy.log('arrow subsection navigation');
1800+
cy.realPress('ArrowUp');
17531801
cy.realPress('Tab');
17541802
cy.focused().should('have.attr', 'aria-label', '6.1').and('have.attr', 'tabindex', 0);
17551803
cy.realPress('ArrowUp');
@@ -1777,6 +1825,19 @@ describe('ObjectPage', () => {
17771825
cy.wrap(section).should('have.attr', 'tabindex', -1);
17781826
}
17791827
});
1828+
1829+
//Table row navigation (relatedTarget not present - scroll-padding fallback)
1830+
cy.log('Table row navigation');
1831+
cy.get('[ui5-tabcontainer]').findUi5TabByText('Table').click();
1832+
cy.realPress('Tab');
1833+
for (let i = 0; i < 15; i++) {
1834+
cy.realPress('ArrowDown');
1835+
}
1836+
cy.focused().should('be.visible').and('have.attr', 'ui5-table-row');
1837+
for (let i = 0; i < 13; i++) {
1838+
cy.realPress('ArrowUp');
1839+
}
1840+
cy.focused().should('be.visible').and('have.attr', 'ui5-table-row');
17801841
});
17811842
});
17821843

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

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
useSyncRef,
1111
} from '@ui5/webcomponents-react-base';
1212
import { clsx } from 'clsx';
13-
import type { CSSProperties, MouseEventHandler, ReactElement, UIEventHandler } from 'react';
13+
import type { CSSProperties, FocusEventHandler, MouseEventHandler, ReactElement, UIEventHandler } from 'react';
1414
import { cloneElement, forwardRef, isValidElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
1515
import { ObjectPageMode } from '../../enums/ObjectPageMode.js';
1616
import { safeGetChildrenArray } from '../../internal/safeGetChildrenArray.js';
@@ -149,6 +149,7 @@ const ObjectPage = forwardRef<ObjectPageDomRef, ObjectPagePropTypes>((props, ref
149149
scrollTimeout,
150150
},
151151
);
152+
const scrollPaddingBlock = `${Math.ceil(12 + topHeaderHeight + TAB_CONTAINER_HEADER_HEIGHT + (!headerCollapsed && headerPinned ? headerContentHeight : 0))}px ${footerArea ? 'calc(var(--_ui5wcr-BarHeight) + 1.25rem)' : 0}`;
152153

153154
useEffect(() => {
154155
if (typeof onToggleHeaderArea === 'function' && isToggledRef.current) {
@@ -625,6 +626,25 @@ const ObjectPage = forwardRef<ObjectPageDomRef, ObjectPagePropTypes>((props, ref
625626
}
626627
}, [isMounted, children, mode]);
627628

629+
const handleContentBlur: FocusEventHandler<HTMLDivElement> = (e) => {
630+
const opNode = objectPageRef.current;
631+
if (!opNode) return;
632+
633+
if (e.relatedTarget && !e.currentTarget.contains(e.relatedTarget as Node)) {
634+
opNode.style.scrollPaddingBlock = '0px';
635+
// Fallback: Some (ui5-table) ui5wc components don't implement `relatedTarget` as expected.
636+
} else if (!e.relatedTarget) {
637+
const currentTarget = e.currentTarget;
638+
opNode.style.scrollPaddingBlock = '0px';
639+
requestAnimationFrame(() => {
640+
if (currentTarget.contains(document.activeElement)) {
641+
opNode.style.scrollPaddingBlock = scrollPaddingBlock;
642+
document.activeElement.scrollIntoView({ block: 'nearest' });
643+
}
644+
});
645+
}
646+
};
647+
628648
return (
629649
<ObjectPageContext.Provider value={mode}>
630650
<div
@@ -780,15 +800,10 @@ const ObjectPage = forwardRef<ObjectPageDomRef, ObjectPagePropTypes>((props, ref
780800
const opNode = objectPageRef.current;
781801
if (opNode) {
782802
// 12px or 0.75rem margin for ui5wc border and input margins
783-
opNode.style.scrollPaddingBlock = `${Math.ceil(12 + topHeaderHeight + TAB_CONTAINER_HEADER_HEIGHT + (!headerCollapsed && headerPinned ? headerContentHeight : 0))}px ${footerArea ? 'calc(var(--_ui5wcr-BarHeight) + 1.25rem)' : 0}`;
784-
}
785-
}}
786-
onBlur={(e) => {
787-
const opNode = objectPageRef.current;
788-
if (opNode && !e.currentTarget.contains(e.relatedTarget as Node)) {
789-
opNode.style.scrollPaddingBlock = '0px';
803+
opNode.style.scrollPaddingBlock = scrollPaddingBlock;
790804
}
791805
}}
806+
onBlur={handleContentBlur}
792807
>
793808
<div
794809
style={{

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ const ObjectPageSection = forwardRef<HTMLElement, ObjectPageSectionPropTypes>((p
201201
navigateSections({ e, onKeyDown: props.onKeyDown, componentName: 'ObjectPageSection' });
202202
const target = e.currentTarget as HTMLElement;
203203
if (
204+
target === e.target &&
204205
(e.key === 'ArrowDown' || e.key === 'ArrowRight') &&
205206
(target.nextElementSibling as HTMLElement).dataset.componentName === 'ObjectPageSection'
206207
) {
@@ -219,6 +220,7 @@ const ObjectPageSection = forwardRef<HTMLElement, ObjectPageSectionPropTypes>((p
219220
});
220221
}
221222
if (
223+
target === e.target &&
222224
(e.key === 'ArrowUp' || e.key === 'ArrowLeft') &&
223225
(target.previousElementSibling as HTMLElement).dataset.componentName === 'ObjectPageSection'
224226
) {

0 commit comments

Comments
 (0)