Skip to content

Commit afa9d76

Browse files
authored
feat(ObjectPage): hide tabbar when only one section is available (#8116)
#8087 needs to be merged first. Closes #8106
1 parent 3d0250e commit afa9d76

6 files changed

Lines changed: 235 additions & 93 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,7 @@ export interface AnalyticalTablePropTypes extends Omit<CommonProps, 'title'> {
10301030
nativeDetail: number;
10311031
}>,
10321032
) => void;
1033+
//todo: add cursor pointer when this prop is active in next major version update.
10331034
/**
10341035
* Fired when a row is clicked
10351036
*/

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

Lines changed: 91 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -662,29 +662,52 @@ describe('ObjectPage', () => {
662662
cy.findByTestId('footer').should('be.visible');
663663
});
664664

665-
it('single section - Default mode', () => {
666-
document.body.style.margin = '0px';
667-
const TestComp = ({
668-
mode,
669-
height,
670-
withFooter,
671-
}: {
672-
height: CSSProperties['height'];
673-
withFooter?: boolean;
674-
mode: ObjectPageMode;
675-
}) => {
676-
const ref = useRef(null);
677-
const [showCurrentHeights, setShowCurrentHeights] = useState({ offset: null, scroll: null });
678-
return (
679-
<ObjectPage
680-
style={{ height: '100vh' }}
681-
titleArea={DPTitle}
682-
headerArea={DPContent}
683-
data-testid="op"
684-
ref={ref}
685-
footerArea={withFooter && Footer}
686-
mode={mode}
687-
>
665+
const TestSingleSectionComp = ({
666+
mode,
667+
height,
668+
withFooter,
669+
withSubSections,
670+
}: {
671+
height: CSSProperties['height'];
672+
withFooter?: boolean;
673+
mode: ObjectPageMode;
674+
withSubSections?: boolean;
675+
}) => {
676+
const ref = useRef(null);
677+
const [showCurrentHeights, setShowCurrentHeights] = useState({ offset: null, scroll: null });
678+
return (
679+
<ObjectPage
680+
style={{ height: '100vh' }}
681+
titleArea={DPTitle}
682+
headerArea={DPContent}
683+
data-testid="op"
684+
ref={ref}
685+
footerArea={withFooter && Footer}
686+
mode={mode}
687+
>
688+
{withSubSections ? (
689+
<ObjectPageSection key="0" titleText="Goals" id="goals" aria-label="Goals">
690+
<ObjectPageSubSection id="0.0" titleText="0.0">
691+
<div data-testid="section 1" style={{ height, width: '100%', background: 'lightblue' }}>
692+
<Button
693+
onClick={() => {
694+
setShowCurrentHeights({
695+
// rounding offset/scroll-height differs from browser to browser and maybe even from headless tests
696+
offset: Math.floor(ref.current?.offsetHeight / 10) * 10,
697+
scroll: Math.floor(ref.current?.scrollHeight / 10) * 10,
698+
});
699+
}}
700+
>
701+
Update Heights
702+
</Button>
703+
{JSON.stringify(showCurrentHeights)}
704+
</div>
705+
</ObjectPageSubSection>
706+
<ObjectPageSubSection id="0.1" titleText="0.0">
707+
<div data-testid="section 2" style={{ height: '400px', width: '100%', background: 'lightgreen' }}></div>
708+
</ObjectPageSubSection>
709+
</ObjectPageSection>
710+
) : (
688711
<ObjectPageSection key="0" titleText="Goals" id="goals" aria-label="Goals">
689712
<div data-testid="section 1" style={{ height, width: '100%', background: 'lightblue' }}>
690713
<Button
@@ -701,26 +724,35 @@ describe('ObjectPage', () => {
701724
{JSON.stringify(showCurrentHeights)}
702725
</div>
703726
</ObjectPageSection>
704-
</ObjectPage>
705-
);
706-
};
707-
cy.mount(<TestComp height="2000px" mode={ObjectPageMode.Default} />);
727+
)}
728+
</ObjectPage>
729+
);
730+
};
731+
732+
it('single section - Default mode', () => {
733+
document.body.style.margin = '0px';
734+
735+
cy.mount(<TestSingleSectionComp height="2000px" mode={ObjectPageMode.Default} />);
736+
737+
cy.get('[data-component-name="ObjectPageTabContainerPlaceholder"]').should('exist');
738+
cy.get('[data-component-name="ObjectPageTabContainer"]').should('not.exist');
739+
708740
cy.findByText('Update Heights').click();
709-
cy.findByText('{"offset":1080,"scroll":2330}').should('exist');
741+
cy.findByText('{"offset":1080,"scroll":2280}').should('exist');
710742

711743
cy.findByTestId('op').scrollTo('bottom');
712744
cy.findByText('Update Heights').click({ force: true });
713-
cy.findByText('{"offset":1080,"scroll":2330}').should('exist');
745+
cy.findByText('{"offset":1080,"scroll":2280}').should('exist');
714746

715-
cy.mount(<TestComp height="2000px" withFooter mode={ObjectPageMode.Default} />);
747+
cy.mount(<TestSingleSectionComp height="2000px" withFooter mode={ObjectPageMode.Default} />);
716748
cy.findByText('Update Heights').click();
717-
cy.findByText('{"offset":1080,"scroll":2370}').should('exist');
749+
cy.findByText('{"offset":1080,"scroll":2320}').should('exist');
718750

719751
cy.findByTestId('op').scrollTo('bottom');
720752
cy.findByText('Update Heights').click({ force: true });
721-
cy.findByText('{"offset":1080,"scroll":2370}').should('exist');
753+
cy.findByText('{"offset":1080,"scroll":2320}').should('exist');
722754

723-
cy.mount(<TestComp height="400px" mode={ObjectPageMode.Default} />);
755+
cy.mount(<TestSingleSectionComp height="400px" mode={ObjectPageMode.Default} />);
724756
cy.findByText('Update Heights').click();
725757
cy.findByText('{"offset":1080,"scroll":1080}').should('exist');
726758

@@ -732,7 +764,7 @@ describe('ObjectPage', () => {
732764
cy.findByText('Update Heights').click({ force: true });
733765
cy.findByText('{"offset":1080,"scroll":1080}').should('exist');
734766

735-
cy.mount(<TestComp height="400px" withFooter mode={ObjectPageMode.Default} />);
767+
cy.mount(<TestSingleSectionComp height="400px" withFooter mode={ObjectPageMode.Default} />);
736768
cy.findByText('Update Heights').click();
737769
cy.findByText('{"offset":1080,"scroll":1080}').should('exist');
738770

@@ -744,7 +776,7 @@ describe('ObjectPage', () => {
744776
cy.findByText('Update Heights').click({ force: true });
745777
cy.findByText('{"offset":1080,"scroll":1080}').should('exist');
746778

747-
cy.mount(<TestComp height="925px" mode={ObjectPageMode.Default} />);
779+
cy.mount(<TestSingleSectionComp height="925px" mode={ObjectPageMode.Default} />);
748780
cy.findByText('https://github.com/UI5/webcomponents-react').should('be.visible');
749781

750782
cy.wait(50);
@@ -757,67 +789,36 @@ describe('ObjectPage', () => {
757789

758790
cy.get('[data-component-name="ObjectPageAnchorBarExpandBtn"]').click();
759791
cy.findByText('https://github.com/UI5/webcomponents-react').should('not.be.visible');
792+
793+
cy.log('with subsections');
794+
cy.mount(<TestSingleSectionComp height="2000px" withSubSections mode={ObjectPageMode.Default} />);
795+
cy.get('[data-component-name="ObjectPageTabContainerPlaceholder"]').should('exist');
796+
cy.get('[data-component-name="ObjectPageTabContainer"]').should('not.exist');
760797
});
761798

762799
it('single section - Tab mode', () => {
763800
document.body.style.margin = '0px';
764-
const TestComp = ({
765-
mode,
766-
height,
767-
withFooter,
768-
}: {
769-
height: CSSProperties['height'];
770-
withFooter?: boolean;
771-
mode: ObjectPageMode;
772-
}) => {
773-
const ref = useRef(null);
774-
const [showCurrentHeights, setShowCurrentHeights] = useState({ offset: null, scroll: null });
775-
return (
776-
<ObjectPage
777-
style={{ height: '100vh' }}
778-
titleArea={DPTitle}
779-
headerArea={DPContent}
780-
data-testid="op"
781-
ref={ref}
782-
footerArea={withFooter && Footer}
783-
mode={mode}
784-
>
785-
<ObjectPageSection key="0" titleText="Goals" id="goals" aria-label="Goals">
786-
<div data-testid="section 1" style={{ height, width: '100%', background: 'lightblue' }}>
787-
<Button
788-
onClick={() => {
789-
setShowCurrentHeights({
790-
// rounding offset/scroll-height differs from browser to browser and maybe even from headless tests
791-
offset: Math.floor(ref.current?.offsetHeight / 10) * 10,
792-
scroll: Math.floor(ref.current?.scrollHeight / 10) * 10,
793-
});
794-
}}
795-
>
796-
Update Heights
797-
</Button>
798-
{JSON.stringify(showCurrentHeights)}
799-
</div>
800-
</ObjectPageSection>
801-
</ObjectPage>
802-
);
803-
};
804-
cy.mount(<TestComp height="2000px" mode={ObjectPageMode.IconTabBar} />);
801+
cy.mount(<TestSingleSectionComp height="2000px" mode={ObjectPageMode.IconTabBar} />);
802+
803+
cy.get('[data-component-name="ObjectPageTabContainerPlaceholder"]').should('exist');
804+
cy.get('[data-component-name="ObjectPageTabContainer"]').should('not.exist');
805+
805806
cy.findByText('Update Heights').click();
806-
cy.findByText('{"offset":1080,"scroll":2290}').should('exist');
807+
cy.findByText('{"offset":1080,"scroll":2240}').should('exist');
807808

808809
cy.findByTestId('op').scrollTo('bottom');
809810
cy.findByText('Update Heights').click({ force: true });
810-
cy.findByText('{"offset":1080,"scroll":2290}').should('exist');
811+
cy.findByText('{"offset":1080,"scroll":2240}').should('exist');
811812

812-
cy.mount(<TestComp height="2000px" withFooter mode={ObjectPageMode.IconTabBar} />);
813+
cy.mount(<TestSingleSectionComp height="2000px" withFooter mode={ObjectPageMode.IconTabBar} />);
813814
cy.findByText('Update Heights').click();
814-
cy.findByText('{"offset":1080,"scroll":2350}').should('exist');
815+
cy.findByText('{"offset":1080,"scroll":2300}').should('exist');
815816

816817
cy.findByTestId('op').scrollTo('bottom');
817818
cy.findByText('Update Heights').click({ force: true });
818-
cy.findByText('{"offset":1080,"scroll":2350}').should('exist');
819+
cy.findByText('{"offset":1080,"scroll":2300}').should('exist');
819820

820-
cy.mount(<TestComp height="400px" mode={ObjectPageMode.IconTabBar} />);
821+
cy.mount(<TestSingleSectionComp height="400px" mode={ObjectPageMode.IconTabBar} />);
821822
cy.findByText('Update Heights').click();
822823
cy.findByText('{"offset":1080,"scroll":1080}').should('exist');
823824

@@ -829,7 +830,7 @@ describe('ObjectPage', () => {
829830
cy.findByText('Update Heights').click({ force: true });
830831
cy.findByText('{"offset":1080,"scroll":1080}').should('exist');
831832

832-
cy.mount(<TestComp height="400px" withFooter mode={ObjectPageMode.IconTabBar} />);
833+
cy.mount(<TestSingleSectionComp height="400px" withFooter mode={ObjectPageMode.IconTabBar} />);
833834
cy.findByText('Update Heights').click();
834835
cy.findByText('{"offset":1080,"scroll":1080}').should('exist');
835836

@@ -841,7 +842,7 @@ describe('ObjectPage', () => {
841842
cy.findByText('Update Heights').click({ force: true });
842843
cy.findByText('{"offset":1080,"scroll":1080}').should('exist');
843844

844-
cy.mount(<TestComp height="925px" mode={ObjectPageMode.IconTabBar} />);
845+
cy.mount(<TestSingleSectionComp height="925px" mode={ObjectPageMode.IconTabBar} />);
845846
cy.findByText('https://github.com/UI5/webcomponents-react').should('be.visible');
846847

847848
cy.wait(50);
@@ -854,7 +855,13 @@ describe('ObjectPage', () => {
854855

855856
cy.get('[data-component-name="ObjectPageAnchorBarExpandBtn"]').click();
856857
cy.findByText('https://github.com/UI5/webcomponents-react').should('not.be.visible');
858+
859+
cy.log('with subsections');
860+
cy.mount(<TestSingleSectionComp height="2000px" withSubSections mode={ObjectPageMode.Default} />);
861+
cy.get('[data-component-name="ObjectPageTabContainerPlaceholder"]').should('exist');
862+
cy.get('[data-component-name="ObjectPageTabContainer"]').should('not.exist');
857863
});
864+
858865
[ObjectPageMode.Default, ObjectPageMode.IconTabBar].forEach((mode) => {
859866
it(`ObjectPageSection/SubSection: Custom header & hideTitleText (mode: ${mode})`, () => {
860867
document.body.style.margin = '0px';

packages/main/src/components/ObjectPage/ObjectPage.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ To render a single section in fullscreen mode, set its height to `100%`.
8888
</ObjectPageSection>
8989
```
9090

91+
## ObjectPage with single section
92+
93+
When only a single section is available, the tabbar is hidden.
94+
95+
<Canvas of={ComponentStories.SingleSection} />
96+
9197
## Opening popover components by pressing an action
9298

9399
Please see the [Docs](?path=/docs/layouts-floorplans-toolbar--docs#open-popovers-with-toolbarbutton) of the `Toolbar` component.

packages/main/src/components/ObjectPage/ObjectPage.module.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@
104104
background: var(--sapObjectHeader_Background);
105105
}
106106

107+
.tabContainerPlaceholder {
108+
composes: tabContainer;
109+
box-shadow: var(--sapContent_HeaderShadow);
110+
height: 1px;
111+
flex-shrink: 0;
112+
}
113+
107114
.tabContainerComponent {
108115
&::part(content) {
109116
display: none;

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

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ const meta = {
5656
},
5757
args: {
5858
mode: ObjectPageMode.Default,
59-
selectedSectionId: 'goals',
6059
imageShapeCircle: true,
6160
image: SampleImage,
6261
style: { height: '700px', maxHeight: '90vh' },
@@ -451,6 +450,93 @@ export const FullScreenSingleSection: Story = {
451450
},
452451
};
453452

453+
export const SingleSection: Story = {
454+
name: 'with single section',
455+
render(args) {
456+
return (
457+
<ObjectPage {...args}>
458+
<ObjectPageSection titleText="Employment" id="employment" aria-label="Employment">
459+
<ObjectPageSubSection
460+
titleText="Job Information"
461+
id="employment-job-information"
462+
aria-label="Job Information"
463+
>
464+
<Form>
465+
<FormItem labelContent={<Label showColon>Job Classification</Label>}>
466+
<FlexBox direction={FlexBoxDirection.Column}>
467+
<Text>Senior UI Developer</Text>
468+
<Label>(UIDEV-SR)</Label>
469+
</FlexBox>
470+
</FormItem>
471+
<FormItem labelContent={<Label showColon>Job Title</Label>}>
472+
<Text>Developer</Text>
473+
</FormItem>
474+
<FormItem labelContent={<Label showColon>Employee Class</Label>}>
475+
<Text>Employee</Text>
476+
</FormItem>
477+
<FormItem labelContent={<Label showColon>Manager</Label>}>
478+
<FlexBox direction={FlexBoxDirection.Column}>
479+
<Text>Dan Smith</Text>
480+
<Label>Development Manager</Label>
481+
</FlexBox>
482+
</FormItem>
483+
<FormItem labelContent={<Label showColon>Pay Grade</Label>}>
484+
<Text>Salary Grade 18 (GR-14)</Text>
485+
</FormItem>
486+
<FormItem labelContent={<Label showColon>FTE</Label>}>
487+
<Text>1</Text>
488+
</FormItem>
489+
</Form>
490+
</ObjectPageSubSection>
491+
<ObjectPageSubSection
492+
titleText="Employee Details"
493+
id="employment-employee-details"
494+
aria-label="Employee Details"
495+
>
496+
<Form>
497+
<FormItem labelContent={<Label showColon>Start Date</Label>}>
498+
<Text>Jan 01, 2018</Text>
499+
</FormItem>
500+
<FormItem labelContent={<Label showColon>End Date</Label>}>
501+
<Text>Dec 31, 9999</Text>
502+
</FormItem>
503+
<FormItem labelContent={<Label showColon>Payroll Start Date</Label>}>
504+
<Text>Jan 01, 2018</Text>
505+
</FormItem>
506+
<FormItem labelContent={<Label showColon>Benefits Start Date</Label>}>
507+
<Text>Jul 01, 2018</Text>
508+
</FormItem>
509+
<FormItem labelContent={<Label showColon>Company Car Eligibility</Label>}>
510+
<Text>Jan 01, 2021</Text>
511+
</FormItem>
512+
<FormItem labelContent={<Label showColon>Equity Start Date</Label>}>
513+
<Text>Jul 01, 2018</Text>
514+
</FormItem>
515+
</Form>
516+
</ObjectPageSubSection>
517+
<ObjectPageSubSection
518+
titleText="Job Relationship"
519+
id="employment-job-relationship"
520+
aria-label="Job Relationship"
521+
>
522+
<Form>
523+
<FormItem labelContent={<Label showColon>Manager</Label>}>
524+
<Text>John Doe</Text>
525+
</FormItem>
526+
<FormItem labelContent={<Label showColon>Scrum Master</Label>}>
527+
<Text>Michael Adams</Text>
528+
</FormItem>
529+
<FormItem labelContent={<Label showColon>Product Owner</Label>}>
530+
<Text>John Miller</Text>
531+
</FormItem>
532+
</Form>
533+
</ObjectPageSubSection>
534+
</ObjectPageSection>
535+
</ObjectPage>
536+
);
537+
},
538+
};
539+
454540
export const LegacyToolbarSupport: Story = {
455541
render(args) {
456542
const objectPageRef = useRef<ObjectPageDomRef>(null);

0 commit comments

Comments
 (0)