Skip to content

Commit 5ebaf8f

Browse files
authored
feat(ObjectPageTitle): introduce snappedHeader & snappedSubHeaderprop (#7497)
Closes #7490
1 parent ea8e7a0 commit 5ebaf8f

File tree

4 files changed

+92
-8
lines changed

4 files changed

+92
-8
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import '@ui5/webcomponents-fiori/dist/illustrations/UnableToLoad.js';
22
import SampleImage from '@sb/demoImages/Person.png';
33
import type { Meta, StoryObj } from '@storybook/react';
4+
import { fn } from '@storybook/test';
45
import BarDesign from '@ui5/webcomponents/dist/types/BarDesign.js';
56
import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js';
67
import ValueState from '@ui5/webcomponents-base/dist/types/ValueState.js';
@@ -59,6 +60,7 @@ const meta = {
5960
imageShapeCircle: true,
6061
image: SampleImage,
6162
style: { height: '700px', maxHeight: '90vh' },
63+
onToggleHeaderArea: fn(),
6264
footerArea: (
6365
<Bar
6466
design={BarDesign.FloatingFooter}
@@ -73,7 +75,9 @@ const meta = {
7375
titleArea: (
7476
<ObjectPageTitle
7577
header={<Title level="H2">Denise Smith</Title>}
78+
snappedHeader={<Title level="H2">Denise Smith (snapped header)</Title>}
7679
subHeader="Senior UI Developer"
80+
snappedSubHeader={'Senior UI Developer (snapped header)'}
7781
actionsBar={
7882
<Toolbar design="Transparent">
7983
<ToolbarButton design={ButtonDesign.Emphasized} text="Primary Action" />

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,62 @@ describe('ObjectPageTitle', () => {
129129
cy.findByTestId('expandedContent').should('not.exist');
130130
});
131131
});
132+
133+
[
134+
{ snappedHeader: 'Snapped Header', header: 'Header' },
135+
{ snappedHeader: undefined, header: 'Header' },
136+
{ snappedHeader: undefined, header: undefined },
137+
{ snappedHeader: 'Snapped Header', header: undefined },
138+
].forEach(({ snappedHeader, header }) => {
139+
const titleParts = [];
140+
if (snappedHeader) {
141+
titleParts.push('snappedHeader');
142+
}
143+
if (header) {
144+
titleParts.push('header');
145+
}
146+
const title = titleParts.length ? titleParts.join(' & ') : 'no headers';
147+
148+
it(`renders with ${title}`, () => {
149+
cy.mount(
150+
<PageComponent
151+
childrenScrollable
152+
pageProps={{
153+
headerArea: (
154+
<ObjectPageHeader key="headerContent" style={{ height: '100px', background: 'lightblue' }}>
155+
Header Section
156+
</ObjectPageHeader>
157+
),
158+
style: { height: '800px' },
159+
}}
160+
titleProps={{
161+
snappedHeader,
162+
header,
163+
}}
164+
/>,
165+
);
166+
167+
// not snapped - always show header
168+
cy.findByText('Snapped Header').should('not.exist');
169+
if (header) {
170+
cy.findByText('Header').should('exist');
171+
} else {
172+
cy.findByText('Header').should('not.exist');
173+
}
174+
175+
cy.findByTestId('page').scrollTo('bottom');
176+
177+
// snapped - show snapped header if defined otherwise show header
178+
if (snappedHeader) {
179+
cy.findByText('Snapped Header').should('exist');
180+
cy.findByText('Header').should('not.exist');
181+
} else if (header) {
182+
cy.findByText('Snapped Header').should('not.exist');
183+
cy.findByText('Header').should('exist');
184+
} else {
185+
cy.findByText('Snapped Header').should('not.exist');
186+
cy.findByText('Header').should('not.exist');
187+
}
188+
});
189+
});
132190
});

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const ObjectPageTitle = forwardRef<HTMLDivElement, ObjectPageTitlePropTypes>((pr
2828
onToggleHeaderContentVisibility,
2929
expandedContent,
3030
snappedContent,
31+
snappedHeader,
32+
snappedSubHeader,
3133
_snappedAvatar,
3234
...rest
3335
} = props as InternalProps;
@@ -41,6 +43,8 @@ const ObjectPageTitle = forwardRef<HTMLDivElement, ObjectPageTitlePropTypes>((pr
4143
);
4244
const containerClasses = clsx(classNames.container, isPhone && classNames.phone, className);
4345
const toolbarContainerRef = useRef<HTMLDivElement>(null);
46+
const _header = !props?.['data-header-content-visible'] && snappedHeader ? snappedHeader : header;
47+
const _subHeader = !props?.['data-header-content-visible'] && snappedSubHeader ? snappedSubHeader : subHeader;
4448

4549
useEffect(() => {
4650
isMounted.current = true;
@@ -157,9 +161,9 @@ const ObjectPageTitle = forwardRef<HTMLDivElement, ObjectPageTitlePropTypes>((pr
157161
data-component-name="ObjectPageTitleMiddleSection"
158162
>
159163
<FlexBox className={classNames.titleMainSection} onClick={onHeaderClick}>
160-
{header && (
164+
{_header && (
161165
<div className={classNames.title} data-component-name="ObjectPageTitleHeader">
162-
{header}
166+
{_header}
163167
</div>
164168
)}
165169
{children && (
@@ -182,13 +186,13 @@ const ObjectPageTitle = forwardRef<HTMLDivElement, ObjectPageTitlePropTypes>((pr
182186
</div>
183187
)}
184188
</FlexBox>
185-
{subHeader && (
189+
{_subHeader && (
186190
<FlexBox id="sub">
187191
<div
188192
className={clsx(classNames.subTitle, classNames.subTitleBottom)}
189193
data-component-name="ObjectPageTitleSubHeader"
190194
>
191-
{subHeader}
195+
{_subHeader}
192196
</div>
193197
</FlexBox>
194198
)}

packages/main/src/components/ObjectPageTitle/types/index.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,33 @@ export interface ObjectPageTitlePropTypes extends CommonProps {
2323
children?: ReactNode | ReactNode[];
2424

2525
/**
26-
* The `header` is positioned in the `ObjectPageTitle` left area.
27-
* Use this prop to display a `Title` (or any other component that serves as a heading).
26+
* The `header` is displayed in the left area of the `ObjectPageTitle`.
27+
* Use this prop to render a `Title` or any other component that serves as a heading.
28+
*
29+
* __Note:__ If the header is snapped (collapsed), the `snappedHeader` prop is used instead (if defined).
2830
*/
2931
header?: ReactNode;
3032
/**
31-
* The `subHeader` is positioned in the `ObjectPageTitle` left area below the `header`.
32-
* Use this aggregation to display a component like `Label` or any other component that serves as sub header.
33+
* The `snappedHeader` is displayed in the left area of the `ObjectPageTitle` when the header is snapped (collapsed).
34+
* Use this prop to render a `Title` or any other component that serves as a heading in the snapped view.
35+
*
36+
* @since 2.12.0
37+
*/
38+
snappedHeader?: ReactNode;
39+
/**
40+
* The `subHeader` is displayed in the left area of the `ObjectPageTitle`, below the `header`.
41+
* Use this prop to render a `Label` or any other component that serves as a sub-header.
42+
*
43+
* __Note:__ If the header is snapped (collapsed), the `snappedSubHeader` prop is used instead (if defined).
3344
*/
3445
subHeader?: ReactNode;
46+
/**
47+
* The `snappedSubHeader` is displayed in the left area of the `ObjectPageTitle` when the header is snapped (collapsed).
48+
* Use this prop to render a `Label` or any other component that serves as a sub-header in the snapped view.
49+
*
50+
* @since 2.12.0
51+
*/
52+
snappedSubHeader?: ReactNode;
3553
/**
3654
* Defines navigation-actions bar of the `ObjectPageTitle`.
3755
*

0 commit comments

Comments
 (0)