From 2f8096019a300ad65611aaa10328f235ffaed162 Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Wed, 26 Feb 2025 16:26:05 +0100 Subject: [PATCH 1/2] fix(ObjectPage): scroll to section when programmatically selected --- .../components/ObjectPage/ObjectPage.cy.tsx | 85 ++++++++++++++++++- .../main/src/components/ObjectPage/index.tsx | 77 +++++++++-------- 2 files changed, 124 insertions(+), 38 deletions(-) diff --git a/packages/main/src/components/ObjectPage/ObjectPage.cy.tsx b/packages/main/src/components/ObjectPage/ObjectPage.cy.tsx index 2e97a0a2c1b..42392743087 100644 --- a/packages/main/src/components/ObjectPage/ObjectPage.cy.tsx +++ b/packages/main/src/components/ObjectPage/ObjectPage.cy.tsx @@ -905,6 +905,75 @@ describe('ObjectPage', () => { cy.get('@cb').should('not.been.called'); }); + it('programmatic prop selection', () => { + const TestComp = (props: ObjectPagePropTypes) => { + const [selectedSection, setSelectedSection] = useState(props.selectedSectionId); + const [selectedSubSection, setSelectedSubSection] = useState(props.selectedSubSectionId); + + return ( + <> + + + + {[ + ...OPContent.slice(0, 1), + +
+ test-content +
+
, + ...OPContent.slice(1) + ]} +
+ + ); + }; + + [ + { headerTitle: DPTitle, headerContent: DPContent }, + { headerTitle: DPTitle }, + { headerContent: DPContent }, + {} + ].forEach((props: ObjectPagePropTypes) => { + cy.mount(); + cy.findByText('employment-job-relationship-content').should('be.visible'); + cy.findByText('Job Information').should('not.be.visible'); + cy.get('[ui5-tabcontainer]').findUi5TabByText('Employment').should('have.attr', 'aria-selected', 'true'); + + cy.mount(); + cy.findByText('personal-connect-content').should('be.visible'); + cy.findByText('test-content').should('not.be.visible'); + cy.get('[ui5-tabcontainer]').findUi5TabByText('Personal').should('have.attr', 'aria-selected', 'true'); + + cy.findByText('Select Goals').click(); + cy.findByText('goals-content').should('be.visible'); + cy.findByText('personal-connect-content').should('not.be.visible'); + cy.get('[ui5-tabcontainer]').findUi5TabByText('Goals').should('have.attr', 'aria-selected', 'true'); + + cy.findByText('Select Payment Information').click(); + cy.findByText('personal-payment-information-content').should('be.visible'); + cy.findByText('personal-connect-content').should('not.be.visible'); + cy.get('[ui5-tabcontainer]').findUi5TabByText('Personal').should('have.attr', 'aria-selected', 'true'); + }); + }); + cypressPassThroughTestsFactory(ObjectPage); }); @@ -952,7 +1021,9 @@ const DPContent = ( const OPContent = [ -
+
+ goals-content +
,
@@ -972,14 +1043,18 @@ const OPContent = [ } > -
+
+ personal-connect-content +
-
+
+ personal-payment-information-content +
, /?|♥`} aria-label="Employment"> @@ -990,7 +1065,9 @@ const OPContent = [
-
+
+ employment-job-relationship-content +
]; diff --git a/packages/main/src/components/ObjectPage/index.tsx b/packages/main/src/components/ObjectPage/index.tsx index 74b5a0fc5bc..2262a6d1218 100644 --- a/packages/main/src/components/ObjectPage/index.tsx +++ b/packages/main/src/components/ObjectPage/index.tsx @@ -224,6 +224,7 @@ const ObjectPage = forwardRef((props, ref) const [sectionSpacer, setSectionSpacer] = useState(0); const [currentTabModeSection, setCurrentTabModeSection] = useState(null); const sections = mode === ObjectPageMode.IconTabBar ? currentTabModeSection : children; + const isScrolling = useRef(null); const deprecationNoticeDisplayed = useRef(false); useEffect(() => { @@ -336,7 +337,6 @@ const ObjectPage = forwardRef((props, ref) } else { scrollToSectionById(sectionId); } - isProgrammaticallyScrolled.current = false; }; const programmaticallySetSection = () => { @@ -388,6 +388,7 @@ const ObjectPage = forwardRef((props, ref) } return newSelectionSectionId; }); + prevSelectedSectionId.current = newSelectionSectionId; scrollEvent.current = targetEvent; fireOnSelectedChangedEvent(targetEvent, index, newSelectionSectionId, section); }; @@ -448,6 +449,7 @@ const ObjectPage = forwardRef((props, ref) }); if (sectionId) { setInternalSelectedSectionId(sectionId); + prevSelectedSectionId.current = sectionId; } } } @@ -504,6 +506,7 @@ const ObjectPage = forwardRef((props, ref) if (mode === ObjectPageMode.IconTabBar) { const sectionId = e.detail.sectionId; setInternalSelectedSectionId(sectionId); + prevSelectedSectionId.current = sectionId; const sectionNodes = objectPageRef.current?.querySelectorAll( 'section[data-component-name="ObjectPageSection"]' ); @@ -552,11 +555,11 @@ const ObjectPage = forwardRef((props, ref) } return visibleSectionIds.current.has(section.id); }); - - if (sortedVisibleSections.length > 0) { + if (sortedVisibleSections.length > 0 && !isProgrammaticallyScrolled.current) { const section = sortedVisibleSections[0]; const id = sortedVisibleSections[0].id.slice(18); setInternalSelectedSectionId(id); + prevSelectedSectionId.current = id; debouncedOnSectionChange(scrollEvent.current, currentIndex, id, section); } }); @@ -707,40 +710,46 @@ const ObjectPage = forwardRef((props, ref) }; const prevScrollTop = useRef(); - const onObjectPageScroll = useCallback( - (e) => { - if (!isToggledRef.current) { - isToggledRef.current = true; - } - if (scrollTimeout.current >= performance.now()) { + const onObjectPageScroll = (e) => { + if (!isToggledRef.current) { + isToggledRef.current = true; + } + if (isScrolling.current) { + clearTimeout(isScrolling.current); + } + + isScrolling.current = setTimeout(() => { + console.log('end scroll'); + isProgrammaticallyScrolled.current = false; + }, 300); + + if (scrollTimeout.current >= performance.now()) { + return; + } + scrollEvent.current = e; + if (typeof props.onScroll === 'function') { + props.onScroll(e); + } + if (selectedSubSectionId) { + setSelectedSubSectionId(undefined); + } + if (selectionScrollTimeout.current) { + clearTimeout(selectionScrollTimeout.current); + } + if (!headerPinned || e.target.scrollTop === 0) { + objectPageRef.current?.classList.remove(classNames.headerCollapsed); + } + if (scrolledHeaderExpanded && e.target.scrollTop !== prevScrollTop.current) { + if (e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight) { return; } - scrollEvent.current = e; - if (typeof props.onScroll === 'function') { - props.onScroll(e); - } - if (selectedSubSectionId) { - setSelectedSubSectionId(undefined); + prevScrollTop.current = e.target.scrollTop; + if (!headerPinned) { + setHeaderCollapsedInternal(true); } - if (selectionScrollTimeout.current) { - clearTimeout(selectionScrollTimeout.current); - } - if (!headerPinned || e.target.scrollTop === 0) { - objectPageRef.current?.classList.remove(classNames.headerCollapsed); - } - if (scrolledHeaderExpanded && e.target.scrollTop !== prevScrollTop.current) { - if (e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight) { - return; - } - prevScrollTop.current = e.target.scrollTop; - if (!headerPinned) { - setHeaderCollapsedInternal(true); - } - setScrolledHeaderExpanded(false); - } - }, - [topHeaderHeight, headerPinned, props.onScroll, scrolledHeaderExpanded, selectedSubSectionId] - ); + setScrolledHeaderExpanded(false); + } + }; const onHoverToggleButton = useCallback( (e) => { From 9434172b7df96c8ab3a0fb41575c33a005fb226d Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Wed, 26 Feb 2025 16:26:47 +0100 Subject: [PATCH 2/2] Update index.tsx --- packages/main/src/components/ObjectPage/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/components/ObjectPage/index.tsx b/packages/main/src/components/ObjectPage/index.tsx index 2262a6d1218..a73f12ff081 100644 --- a/packages/main/src/components/ObjectPage/index.tsx +++ b/packages/main/src/components/ObjectPage/index.tsx @@ -224,7 +224,7 @@ const ObjectPage = forwardRef((props, ref) const [sectionSpacer, setSectionSpacer] = useState(0); const [currentTabModeSection, setCurrentTabModeSection] = useState(null); const sections = mode === ObjectPageMode.IconTabBar ? currentTabModeSection : children; - const isScrolling = useRef(null); + const isScrolling = useRef | null>(null); const deprecationNoticeDisplayed = useRef(false); useEffect(() => {