Skip to content

Commit

Permalink
feat(ObjectPage): add onBeforeNavigate event (#5557)
Browse files Browse the repository at this point in the history
Closes #5342
  • Loading branch information
Lukas742 committed Feb 29, 2024
1 parent d82b44b commit 3c8c18f
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 5 deletions.
39 changes: 39 additions & 0 deletions packages/main/src/components/ObjectPage/ObjectPage.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
TitleLevel,
ValueState
} from '../..';
import { cypressPassThroughTestsFactory } from '@/cypress/support/utils';

describe('ObjectPage', () => {
it('toggle header', () => {
Expand Down Expand Up @@ -837,6 +838,44 @@ describe('ObjectPage', () => {
cy.get('[ui5-tabcontainer]').should('not.exist');
cy.get('[data-component-name="ObjectPageAnchorBar"]').should('not.be.visible');
});

it('onBeforeNavigate', () => {
const beforeNavigateHandlerDefaultPrevented = (e) => {
// deleted as not relevant for the test
delete e.detail.tab;
delete e.detail.tabIndex;
e.preventDefault();
};
const beforeNavigate = cy.spy(beforeNavigateHandlerDefaultPrevented).as('beforeNavigateSpy');
const sectionChange = cy.spy().as('sectionChangeSpy');
cy.mount(
<ObjectPage
data-testid="op"
headerTitle={DPTitle}
headerContent={DPContent}
onBeforeNavigate={beforeNavigate}
onSelectedSectionChange={sectionChange}
>
{OPContent}
</ObjectPage>
);
cy.get('[ui5-tabcontainer]').findUi5TabByText('Personal').click();
cy.get('@beforeNavigateSpy')
.should('have.been.calledOnce')
.its('firstCall.args[0].detail')
.should('deep.equal', { sectionIndex: 2, sectionId: 'personal', subSectionId: undefined });
cy.get('@sectionChangeSpy').should('not.have.been.called');

cy.get('[ui5-tabcontainer]').findUi5TabOpenPopoverButtonByText('Employment').click();
cy.realPress('Enter');
cy.get('@beforeNavigateSpy')
.should('have.been.calledTwice')
.its('secondCall.args[0].detail')
.should('deep.equal', { sectionIndex: 3, sectionId: 'employment', subSectionId: 'employment-job-information' });
cy.get('@sectionChangeSpy').should('not.have.been.called');
});

cypressPassThroughTestsFactory(ObjectPage);
});

const DPTitle = (
Expand Down
46 changes: 41 additions & 5 deletions packages/main/src/components/ObjectPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import type { TabContainerTabSelectEventDetail } from '@ui5/webcomponents/dist/TabContainer.js';
import { debounce, enrichEventWithDetails, ThemingParameters, useSyncRef } from '@ui5/webcomponents-react-base';
import { clsx } from 'clsx';
import type { CSSProperties, ReactElement, ReactNode } from 'react';
Expand All @@ -18,8 +19,8 @@ import { AvatarSize, GlobalStyleClasses, ObjectPageMode } from '../../enums/inde
import { addCustomCSSWithScoping } from '../../internal/addCustomCSSWithScoping.js';
import { safeGetChildrenArray } from '../../internal/safeGetChildrenArray.js';
import { useObserveHeights } from '../../internal/useObserveHeights.js';
import type { CommonProps } from '../../types/index.js';
import type { AvatarPropTypes } from '../../webComponents/index.js';
import type { CommonProps, Ui5CustomEvent } from '../../types/index.js';
import type { AvatarPropTypes, TabContainerDomRef } from '../../webComponents/index.js';
import { Tab, TabContainer } from '../../webComponents/index.js';
import { DynamicPageCssVariables } from '../DynamicPage/DynamicPage.jss.js';
import { DynamicPageAnchorBar } from '../DynamicPageAnchorBar/index.js';
Expand All @@ -43,6 +44,14 @@ const TAB_CONTAINER_HEADER_HEIGHT = 48;

type ObjectPageSectionType = ReactElement<ObjectPageSectionPropTypes> | boolean;

interface BeforeNavigateDetail {
sectionIndex: number;
sectionId: string;
subSectionId: string | undefined;
}

type ObjectPageTabSelectEventDetail = TabContainerTabSelectEventDetail & BeforeNavigateDetail;

export interface ObjectPagePropTypes extends Omit<CommonProps, 'placeholder'> {
/**
* Defines the upper, always static, title section of the `ObjectPage`.
Expand Down Expand Up @@ -79,11 +88,13 @@ export interface ObjectPagePropTypes extends Omit<CommonProps, 'placeholder'> {
*/
children?: ObjectPageSectionType | ObjectPageSectionType[];
/**
* Defines the ID of the currently `ObjectPageSection` section.
* Sets the current selected `ObjectPageSection` by `id`.
*
* __Note:__ If a valid `selectedSubSectionId` is set, this prop has no effect.
*/
selectedSectionId?: string;
/**
* Defines the ID of the currently `ObjectPageSubSection` section.
* Sets the current selected `ObjectPageSubSection` by `id`.
*/
selectedSubSectionId?: string;
/**
Expand Down Expand Up @@ -132,6 +143,12 @@ export interface ObjectPagePropTypes extends Omit<CommonProps, 'placeholder'> {
* __Note:__ Although this prop accepts all HTML Elements, it is strongly recommended that you only use placeholder components like the `IllustratedMessage` or custom skeletons pages in order to preserve the intended design.
*/
placeholder?: ReactNode;
/**
* The event is fired before the selected section is changed using the navigation. It can be aborted by the application with `preventDefault()`, which means that there will be no navigation.
*
* __Note:__ This event is only fired when navigating via tab-bar.
*/
onBeforeNavigate?: (event: Ui5CustomEvent<TabContainerDomRef, ObjectPageTabSelectEventDetail>) => void;
/**
* Fired when the selected section changes.
*/
Expand Down Expand Up @@ -177,6 +194,7 @@ const ObjectPage = forwardRef<HTMLDivElement, ObjectPagePropTypes>((props, ref)
onSelectedSectionChange,
onToggleHeaderContent,
onPinnedStateChange,
onBeforeNavigate,
...rest
} = props;

Expand Down Expand Up @@ -584,6 +602,7 @@ const ObjectPage = forwardRef<HTMLDivElement, ObjectPagePropTypes>((props, ref)

const snappedHeaderInObjPage = headerTitle && headerTitle.props.snappedContent && headerCollapsed === true && !!image;

const hasHeaderContent = !!headerContent;
const renderTitleSection = useCallback(
(inHeader = false) => {
const titleInHeaderClass = inHeader ? classes.titleInHeader : undefined;
Expand All @@ -606,7 +625,7 @@ const ObjectPage = forwardRef<HTMLDivElement, ObjectPagePropTypes>((props, ref)
'data-is-snapped-rendered-outside': snappedHeaderInObjPage
});
},
[headerTitle, titleHeaderNotClickable, onTitleClick, headerCollapsed, snappedHeaderInObjPage, !!headerContent]
[headerTitle, titleHeaderNotClickable, onTitleClick, headerCollapsed, snappedHeaderInObjPage, hasHeaderContent]
);

const isInitial = useRef(true);
Expand Down Expand Up @@ -668,6 +687,22 @@ const ObjectPage = forwardRef<HTMLDivElement, ObjectPagePropTypes>((props, ref)
]);

const onTabItemSelect = (event) => {
if (typeof onBeforeNavigate === 'function') {
const selectedTabDataset = event.detail.tab.dataset;
const sectionIndex = parseInt(selectedTabDataset.index, 10);
const sectionId = selectedTabDataset.parentId ?? selectedTabDataset.sectionId;
const subSectionId = selectedTabDataset.isSubTab ? selectedTabDataset.sectionId : undefined;
onBeforeNavigate(
enrichEventWithDetails(event, {
sectionIndex,
sectionId,
subSectionId
})
);
if (event.defaultPrevented) {
return;
}
}
event.preventDefault();
const { sectionId, index, isSubTab, parentId } = event.detail.tab.dataset;
if (isSubTab) {
Expand Down Expand Up @@ -845,6 +880,7 @@ const ObjectPage = forwardRef<HTMLDivElement, ObjectPagePropTypes>((props, ref)
data-section-id={item.props.id}
text={item.props.titleText}
selected={item.props.id === selectedSubSectionId || undefined}
data-index={index}
>
{/*ToDo: workaround for nested tab selection*/}
<span style={{ display: 'none' }} />
Expand Down

0 comments on commit 3c8c18f

Please sign in to comment.