Skip to content

Commit 1d4c4fe

Browse files
authored
fix(ObjectPage): fire onSelectedSectionChange correctly in IconTabBar mode (#7240)
Fixes #7235
1 parent e049394 commit 1d4c4fe

File tree

2 files changed

+75
-15
lines changed

2 files changed

+75
-15
lines changed

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

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,31 +1284,75 @@ describe('ObjectPage', () => {
12841284
</>
12851285
);
12861286
};
1287-
1287+
const changeSpy = cy.spy().as('change');
1288+
let callCount = 1;
12881289
[{ titleArea: DPTitle, headerArea: DPContent }, { titleArea: DPTitle }, { headerArea: DPContent }, {}].forEach(
12891290
(props: ObjectPagePropTypes) => {
1290-
cy.mount(<TestComp {...props} mode={mode} selectedSubSectionId={`employment-job-relationship`} />);
1291+
cy.mount(
1292+
<TestComp
1293+
{...props}
1294+
mode={mode}
1295+
selectedSubSectionId={`employment-job-relationship`}
1296+
onSelectedSectionChange={changeSpy}
1297+
/>
1298+
);
12911299

12921300
cy.findByText('employment-job-relationship-content').should('be.visible');
12931301
cy.findByText('Job Information').should('not.be.visible');
12941302
cy.get('[ui5-tabcontainer]').findUi5TabByText('Employment').should('have.attr', 'aria-selected', 'true');
12951303

1296-
cy.mount(<TestComp {...props} selectedSectionId={`personal`} />);
1304+
cy.mount(
1305+
<TestComp {...props} mode={mode} selectedSectionId={`personal`} onSelectedSectionChange={changeSpy} />
1306+
);
12971307
cy.findByText('personal-connect-content').should('be.visible');
1298-
cy.findByText('test-content').should('not.be.visible');
1308+
1309+
if (mode === 'IconTabBar') {
1310+
cy.findByText('test-content').should('not.exist');
1311+
} else {
1312+
cy.findByText('test-content').should('not.be.visible');
1313+
}
12991314
cy.get('[ui5-tabcontainer]').findUi5TabByText('Personal').should('have.attr', 'aria-selected', 'true');
13001315

13011316
cy.wait(100);
13021317
cy.findByText('Select Goals').click();
13031318
cy.findByText('goals-content').should('be.visible');
1319+
cy.get('@change').should('have.callCount', callCount);
1320+
callCount++;
1321+
if (mode === 'IconTabBar') {
1322+
cy.findByText('personal-connect-content').should('not.exist');
1323+
} else {
1324+
cy.findByText('personal-connect-content').should('not.be.visible');
1325+
}
1326+
cy.get('[ui5-tabcontainer]').findUi5TabByText('Goals').should('have.attr', 'aria-selected', 'true');
13041327

1305-
cy.findByText('personal-connect-content').should('not.be.visible');
1328+
cy.get('[ui5-tabcontainer]').findUi5TabByText('Personal').click();
1329+
if (mode === 'IconTabBar') {
1330+
cy.findByText('test-content').should('not.exist');
1331+
} else {
1332+
cy.findByText('test-content').should('not.be.visible');
1333+
}
1334+
cy.findByText('personal-connect-content').should('be.visible');
1335+
cy.get('[ui5-tabcontainer]').findUi5TabByText('Personal').should('have.attr', 'aria-selected', 'true');
1336+
cy.get('@change').should('have.callCount', callCount);
1337+
callCount++;
1338+
1339+
cy.get('[ui5-tabcontainer]').findUi5TabByText('Goals').click();
1340+
if (mode === 'IconTabBar') {
1341+
cy.findByText('personal-connect-content').should('not.exist');
1342+
} else {
1343+
cy.findByText('personal-connect-content').should('not.be.visible');
1344+
}
1345+
cy.findByText('goals-content').should('be.visible');
13061346
cy.get('[ui5-tabcontainer]').findUi5TabByText('Goals').should('have.attr', 'aria-selected', 'true');
1347+
cy.get('@change').should('have.callCount', callCount);
1348+
callCount++;
13071349

13081350
cy.findByText('Select Payment Information').click();
13091351
cy.findByText('personal-payment-information-content').should('be.visible');
13101352
cy.findByText('personal-connect-content').should('not.be.visible');
13111353
cy.get('[ui5-tabcontainer]').findUi5TabByText('Personal').should('have.attr', 'aria-selected', 'true');
1354+
cy.get('@change').should('have.callCount', callCount);
1355+
callCount++;
13121356
}
13131357
);
13141358
});

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

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
debounce,
66
enrichEventWithDetails,
77
ThemingParameters,
8+
useIsomorphicLayoutEffect,
89
useStylesheet,
910
useSyncRef
1011
} from '@ui5/webcomponents-react-base';
@@ -249,18 +250,23 @@ const ObjectPage = forwardRef<ObjectPageDomRef, ObjectPagePropTypes>((props, ref
249250
fireOnSelectedChangedEvent(targetEvent, index, newSelectionSectionId, section);
250251
};
251252

252-
useEffect(() => {
253+
useIsomorphicLayoutEffect(() => {
253254
if (selectedSectionId) {
255+
const fireSelectEvent = () => {
256+
const selectedSection = getSectionElementById(objectPageRef.current, false, selectedSectionId);
257+
if (selectedSection) {
258+
const selectedSectionIndex = Array.from(
259+
selectedSection.parentElement.querySelectorAll(':scope > [data-component-name="ObjectPageSection"]')
260+
).indexOf(selectedSection);
261+
handleOnSectionSelected({}, selectedSectionId, selectedSectionIndex, selectedSection);
262+
}
263+
};
254264
if (mode === ObjectPageMode.IconTabBar) {
255265
setInternalSelectedSectionId(selectedSectionId);
256-
return;
257-
}
258-
const selectedSection = getSectionElementById(objectPageRef.current, false, selectedSectionId);
259-
if (selectedSection) {
260-
const selectedSectionIndex = Array.from(
261-
selectedSection.parentElement.querySelectorAll(':scope > [data-component-name="ObjectPageSection"]')
262-
).indexOf(selectedSection);
263-
handleOnSectionSelected({}, selectedSectionId, selectedSectionIndex, selectedSection);
266+
// In TabBar mode the section is only rendered when selected, therefore delay firing the event until the section is available in the DOM
267+
setTimeout(fireSelectEvent);
268+
} else {
269+
fireSelectEvent();
264270
}
265271
}
266272
}, [selectedSectionId, mode]);
@@ -300,6 +306,7 @@ const ObjectPage = forwardRef<ObjectPageDomRef, ObjectPagePropTypes>((props, ref
300306
}
301307
}, [headerPinned, topHeaderHeight]);
302308

309+
const isInitialTabBarMode = useRef(true);
303310
useEffect(() => {
304311
if (!isMounted) {
305312
requestAnimationFrame(() => setIsMounted(true));
@@ -311,24 +318,33 @@ const ObjectPage = forwardRef<ObjectPageDomRef, ObjectPagePropTypes>((props, ref
311318
isProgrammaticallyScrolled.current = true;
312319
if (mode === ObjectPageMode.IconTabBar) {
313320
let sectionId: string;
314-
childrenArray.forEach((section) => {
321+
let curSection: ReactElement;
322+
let sectionIndex: number = -1;
323+
childrenArray.forEach((section, index) => {
315324
if (isValidElement(section) && section.props && section.props.children) {
316325
safeGetChildrenArray(section.props.children).forEach((subSection) => {
317326
if (
318327
isValidElement(subSection) &&
319328
subSection.props &&
320329
(subSection as ReactElement<ObjectPageSubSectionPropTypes>).props.id === props.selectedSubSectionId
321330
) {
331+
curSection = section;
322332
sectionId = section.props?.id;
333+
sectionIndex = index;
323334
}
324335
});
325336
}
326337
});
327338
if (sectionId) {
339+
if (!isInitialTabBarMode.current) {
340+
//In TabBar mode the section is often not scrolled when subsection changes, thus the onSelectedSectionChange isn't fired
341+
debouncedOnSectionChange({}, sectionIndex, sectionId, curSection);
342+
}
328343
setInternalSelectedSectionId(sectionId);
329344
}
330345
}
331346
}
347+
isInitialTabBarMode.current = false;
332348
}, [props.selectedSubSectionId, isMounted]);
333349

334350
const tabContainerContainerRef = useRef(null);

0 commit comments

Comments
 (0)