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
67 changes: 64 additions & 3 deletions packages/main/src/components/ObjectPage/ObjectPage.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,14 @@ import {
Title,
Toolbar,
ToolbarButton,
Table,
TableCell,
TableHeaderCell,
TableHeaderRow,
TableRow,
} from '../..';
import { cypressPassThroughTestsFactory } from '@/cypress/support/utils';
import type { TabDomRef } from '../../webComponents/Tab/index.js';
import { cypressPassThroughTestsFactory } from '@/cypress/support/utils';

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

Expand Down Expand Up @@ -1651,6 +1656,22 @@ describe('ObjectPage', () => {
<Input data-testid="sub" />
</ObjectPageSubSection>
</ObjectPageSection>
<ObjectPageSection id={'7'} titleText={'Table'} aria-label="Table">
<Table data-testid="table">
<TableHeaderRow slot="headerRow">
<TableHeaderCell>Product</TableHeaderCell>
<TableHeaderCell>Supplier</TableHeaderCell>
<TableHeaderCell horizontalAlign="End">Price</TableHeaderCell>
</TableHeaderRow>
{new Array(20).fill(1337).map((_, i) => (
<TableRow key={i}>
<TableCell>Mac</TableCell>
<TableCell>Apple</TableCell>
<TableCell horizontalAlign="End">10.09</TableCell>
</TableRow>
))}
</Table>
</ObjectPageSection>
</ObjectPage>
</>,
);
Expand Down Expand Up @@ -1691,9 +1712,18 @@ describe('ObjectPage', () => {
// 6.2 input
cy.realPress('Tab');
cy.findByTestId('sub').should('be.focused');
// Table
cy.realPress('Tab');
cy.focused().should('have.attr', 'ui5-table-row');
//footer
cy.realPress('Tab');
cy.findByTestId('footer-accept-btn').should('be.focused');
// Table
cy.realPress(['Shift', 'Tab']);
cy.focused().should('have.attr', 'ui5-table-row');
// Table Section
cy.realPress(['Shift', 'Tab']);
cy.focused().should('have.attr', 'aria-label', 'Table').and('have.attr', 'tabindex', 0);
// 6.2 input
cy.realPress(['Shift', 'Tab']);
cy.findByTestId('sub').should('be.focused');
Expand Down Expand Up @@ -1730,10 +1760,12 @@ describe('ObjectPage', () => {
cy.get('[data-component-name="ObjectPageSubSection"]').should('have.attr', 'tabindex', -1);

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

// arrow section navigation
cy.log('arrow section navigation');
cy.realPress('ArrowUp');
cy.focused().should('have.attr', 'aria-label', 'Goals').and('have.attr', 'tabindex', 0);
cy.realPress('ArrowDown');
Expand All @@ -1747,9 +1779,25 @@ describe('ObjectPage', () => {
cy.realPress('ArrowDown');
cy.focused().should('have.attr', 'aria-label', 'SubSectionsInput').and('have.attr', 'tabindex', 0);
cy.realPress('ArrowDown');
cy.focused().should('have.attr', 'aria-label', 'SubSectionsInput').and('have.attr', 'tabindex', 0);
cy.focused()
.should('have.attr', 'aria-label', 'Table')
.and('have.attr', 'tabindex', 0)
.then(($el) => {
const rect = $el[0].getBoundingClientRect();
expect(rect.top).to.be.at.most(214);
});
cy.realPress('ArrowDown');
cy.focused()
.should('have.attr', 'aria-label', 'Table')
.and('have.attr', 'tabindex', 0)
.then(($el) => {
const rect = $el[0].getBoundingClientRect();
expect(rect.top).to.be.at.most(211);
});

// arrow subsection navigation
cy.log('arrow subsection navigation');
cy.realPress('ArrowUp');
cy.realPress('Tab');
cy.focused().should('have.attr', 'aria-label', '6.1').and('have.attr', 'tabindex', 0);
cy.realPress('ArrowUp');
Expand Down Expand Up @@ -1777,6 +1825,19 @@ describe('ObjectPage', () => {
cy.wrap(section).should('have.attr', 'tabindex', -1);
}
});

//Table row navigation (relatedTarget not present - scroll-padding fallback)
cy.log('Table row navigation');
cy.get('[ui5-tabcontainer]').findUi5TabByText('Table').click();
cy.realPress('Tab');
for (let i = 0; i < 15; i++) {
cy.realPress('ArrowDown');
}
cy.focused().should('be.visible').and('have.attr', 'ui5-table-row');
for (let i = 0; i < 13; i++) {
cy.realPress('ArrowUp');
}
cy.focused().should('be.visible').and('have.attr', 'ui5-table-row');
});
});

Expand Down
31 changes: 23 additions & 8 deletions packages/main/src/components/ObjectPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
useSyncRef,
} from '@ui5/webcomponents-react-base';
import { clsx } from 'clsx';
import type { CSSProperties, MouseEventHandler, ReactElement, UIEventHandler } from 'react';
import type { CSSProperties, FocusEventHandler, MouseEventHandler, ReactElement, UIEventHandler } from 'react';
import { cloneElement, forwardRef, isValidElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ObjectPageMode } from '../../enums/ObjectPageMode.js';
import { safeGetChildrenArray } from '../../internal/safeGetChildrenArray.js';
Expand Down Expand Up @@ -149,6 +149,7 @@ const ObjectPage = forwardRef<ObjectPageDomRef, ObjectPagePropTypes>((props, ref
scrollTimeout,
},
);
const scrollPaddingBlock = `${Math.ceil(12 + topHeaderHeight + TAB_CONTAINER_HEADER_HEIGHT + (!headerCollapsed && headerPinned ? headerContentHeight : 0))}px ${footerArea ? 'calc(var(--_ui5wcr-BarHeight) + 1.25rem)' : 0}`;

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

const handleContentBlur: FocusEventHandler<HTMLDivElement> = (e) => {
const opNode = objectPageRef.current;
if (!opNode) return;

if (e.relatedTarget && !e.currentTarget.contains(e.relatedTarget as Node)) {
opNode.style.scrollPaddingBlock = '0px';
// Fallback: Some (ui5-table) ui5wc components don't implement `relatedTarget` as expected.
} else if (!e.relatedTarget) {
const currentTarget = e.currentTarget;
opNode.style.scrollPaddingBlock = '0px';
requestAnimationFrame(() => {
if (currentTarget.contains(document.activeElement)) {
opNode.style.scrollPaddingBlock = scrollPaddingBlock;
document.activeElement.scrollIntoView({ block: 'nearest' });
}
});
}
};

return (
<ObjectPageContext.Provider value={mode}>
<div
Expand Down Expand Up @@ -780,15 +800,10 @@ const ObjectPage = forwardRef<ObjectPageDomRef, ObjectPagePropTypes>((props, ref
const opNode = objectPageRef.current;
if (opNode) {
// 12px or 0.75rem margin for ui5wc border and input margins
opNode.style.scrollPaddingBlock = `${Math.ceil(12 + topHeaderHeight + TAB_CONTAINER_HEADER_HEIGHT + (!headerCollapsed && headerPinned ? headerContentHeight : 0))}px ${footerArea ? 'calc(var(--_ui5wcr-BarHeight) + 1.25rem)' : 0}`;
}
}}
onBlur={(e) => {
const opNode = objectPageRef.current;
if (opNode && !e.currentTarget.contains(e.relatedTarget as Node)) {
opNode.style.scrollPaddingBlock = '0px';
opNode.style.scrollPaddingBlock = scrollPaddingBlock;
}
}}
onBlur={handleContentBlur}
>
<div
style={{
Expand Down
2 changes: 2 additions & 0 deletions packages/main/src/components/ObjectPageSection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ const ObjectPageSection = forwardRef<HTMLElement, ObjectPageSectionPropTypes>((p
navigateSections({ e, onKeyDown: props.onKeyDown, componentName: 'ObjectPageSection' });
const target = e.currentTarget as HTMLElement;
if (
target === e.target &&
(e.key === 'ArrowDown' || e.key === 'ArrowRight') &&
(target.nextElementSibling as HTMLElement).dataset.componentName === 'ObjectPageSection'
) {
Expand All @@ -219,6 +220,7 @@ const ObjectPageSection = forwardRef<HTMLElement, ObjectPageSectionPropTypes>((p
});
}
if (
target === e.target &&
(e.key === 'ArrowUp' || e.key === 'ArrowLeft') &&
(target.previousElementSibling as HTMLElement).dataset.componentName === 'ObjectPageSection'
) {
Expand Down
Loading