diff --git a/cypress.config.ts b/cypress.config.ts
index 269725cf424..57bd1c50878 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -1,5 +1,5 @@
-import { defineConfig } from 'cypress';
import codeCoverageTask from '@cypress/code-coverage/task';
+import { defineConfig } from 'cypress';
export default defineConfig({
component: {
@@ -16,5 +16,6 @@ export default defineConfig({
viewportWidth: 1920,
viewportHeight: 1080,
video: false,
- screenshotOnRunFailure: false
+ screenshotOnRunFailure: false,
+ scrollBehavior: false
});
diff --git a/package.json b/package.json
index 297e7c04f14..126cb85ea9e 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"license": "Apache-2.0",
"scripts": {
"start": "lerna run build:i18n && start-storybook -p 6006",
- "build:prepare": "lerna run build:i18n && node scripts/build-wrappers.js",
+ "build:prepare": "lerna run build:i18n && node scripts/build-wrappers.js && rimraf node_modules/@types/mocha",
"build:cleanup": "rimraf packages/main/tmp",
"build": "yarn build:prepare && tsc --build && yarn build:cleanup",
"build:storybook": "lerna run build:i18n && build-storybook -o .out",
diff --git a/packages/main/src/components/DynamicPage/DynamicPage.cy.tsx b/packages/main/src/components/DynamicPage/DynamicPage.cy.tsx
index f44e95705bb..116371964d7 100644
--- a/packages/main/src/components/DynamicPage/DynamicPage.cy.tsx
+++ b/packages/main/src/components/DynamicPage/DynamicPage.cy.tsx
@@ -1,4 +1,5 @@
-import { DynamicPage, DynamicPageHeader, DynamicPageTitle } from '../..';
+import { useState } from 'react';
+import { Button, DynamicPage, DynamicPageHeader, DynamicPagePropTypes, DynamicPageTitle } from '../..';
describe('DynamicPage', () => {
it('toggle header', () => {
@@ -35,4 +36,164 @@ describe('DynamicPage', () => {
cy.get('@toggleSpy').should('have.been.calledWith', true);
cy.get('@toggleSpy').should('have.callCount', 4);
});
+
+ it('pin header', () => {
+ const pin = cy.spy().as('onPinSpy');
+ cy.mount(
+ }
+ headerContent={DynamicPageHeader}
+ headerContentPinnable
+ showHideHeaderButton
+ onPinnedStateChange={pin}
+ data-testid="op"
+ >
+
+
+ );
+ cy.wait(50);
+ cy.findByTestId('op').scrollTo(0, 500);
+ cy.findByText('DynamicPageHeader').should('not.be.visible');
+
+ cy.findByTestId('op').scrollTo('top');
+
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').click();
+ cy.get('@onPinSpy').should('have.been.calledOnce');
+ cy.get('@onPinSpy').should('have.been.calledWith', true);
+
+ cy.findByTestId('op').scrollTo(0, 500);
+ cy.findByText('DynamicPageHeader').should('be.visible');
+
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').click();
+ cy.get('@onPinSpy').should('have.been.calledTwice');
+ cy.get('@onPinSpy').should('have.been.calledWith', false);
+ cy.findByTestId('op').scrollTo(0, 501);
+ cy.findByText('DynamicPageHeader').should('not.be.visible');
+ });
+
+ it('programmatically pin header (`alwaysShowContentHeader`)', () => {
+ const TestComp = ({ onPinnedStateChange }: DynamicPagePropTypes) => {
+ const [pinned, setPinned] = useState(false);
+ const handlePinChange = (pinned) => {
+ onPinnedStateChange(pinned);
+ setPinned(pinned);
+ };
+ return (
+ <>
+
+ }
+ headerContent={DynamicPageHeader}
+ headerContentPinnable
+ showHideHeaderButton
+ alwaysShowContentHeader={pinned}
+ onPinnedStateChange={handlePinChange}
+ data-testid="op"
+ >
+
+
+ >
+ );
+ };
+ const pin = cy.spy().as('onPinSpy');
+ cy.mount();
+ cy.wait(50);
+
+ cy.findByTestId('op').scrollTo(0, 500);
+ cy.findByText('DynamicPageHeader').should('not.be.visible');
+
+ cy.findByTestId('btn').click();
+ cy.get('@onPinSpy').should('have.been.calledOnce');
+ cy.get('@onPinSpy').should('have.been.calledWith', true);
+ cy.findByText('DynamicPageHeader').should('be.visible');
+
+ cy.findByTestId('op').scrollTo(0, 0);
+ cy.findByText('DynamicPageHeader').should('be.visible');
+
+ cy.findByTestId('op').scrollTo(0, 800);
+ cy.findByText('DynamicPageHeader').should('be.visible');
+
+ cy.findByTestId('btn').click();
+ cy.get('@onPinSpy').should('have.been.calledTwice');
+ cy.get('@onPinSpy').should('have.been.calledWith', false);
+ cy.findByText('DynamicPageHeader').should('be.visible');
+
+ cy.findByTestId('op').scrollTo(0, 801);
+ cy.findByText('DynamicPageHeader').should('not.be.visible');
+
+ cy.findByTestId('btn').click();
+ cy.findByTestId('op').scrollTo(0, 500);
+ cy.findByText('DynamicPageHeader').should('be.visible');
+
+ cy.findByTestId('btn').click();
+ cy.findByTestId('op').scrollTo(0, 501);
+ cy.findByText('DynamicPageHeader').should('not.be.visible');
+
+ cy.get('[data-component-name="DynamicPageAnchorBarExpandBtn"]').click();
+ cy.findByText('DynamicPageHeader').should('be.visible');
+
+ // wait for timeout of expand click
+ cy.wait(500);
+
+ cy.findByTestId('op').scrollTo(0, 502);
+ cy.findByText('DynamicPageHeader').should('not.be.visible');
+
+ cy.findByTestId('op').scrollTo(0, 30);
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').click();
+ cy.get('@onPinSpy').should('have.callCount', 5);
+ cy.findByTestId('btn').should('have.text', 'toggle false');
+ cy.findByText('DynamicPageHeader').should('be.visible');
+
+ cy.findByTestId('op').scrollTo(0, 500);
+ cy.findByTestId('btn').click();
+ cy.findByTestId('op').scrollTo(0, 501);
+ cy.findByText('DynamicPageHeader').should('not.be.visible');
+
+ cy.findByTestId('btn').click();
+ cy.findByText('DynamicPageHeader').should('be.visible');
+ cy.get('@onPinSpy').should('have.callCount', 7);
+ });
+
+ it('collapse header when partially visible', () => {
+ cy.viewport(1440, 1080);
+ cy.mount(
+ }
+ headerContent={
+
+ DynamicPageHeader
+
+ }
+ headerContentPinnable
+ showHideHeaderButton
+ data-testid="op"
+ >
+
+
+ );
+ cy.wait(50);
+
+ cy.findByTestId('op').scrollTo(0, 400);
+ cy.get('[data-component-name="DynamicPageAnchorBarExpandBtn"]').click();
+ // wait for timeout of expand click
+ cy.wait(500);
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').should('not.exist');
+
+ cy.findByTestId('op').scrollTo(0, 1);
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').should('not.exist');
+ cy.wait(50);
+ cy.findByTestId('op').scrollTo(0, 0);
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').should('be.visible');
+ cy.findByText('DynamicPageHeader').should('be.visible');
+ });
});
diff --git a/packages/main/src/components/DynamicPage/__snapshots__/DynamicPage.test.tsx.snap b/packages/main/src/components/DynamicPage/__snapshots__/DynamicPage.test.tsx.snap
index 547723e533b..400b10a243a 100644
--- a/packages/main/src/components/DynamicPage/__snapshots__/DynamicPage.test.tsx.snap
+++ b/packages/main/src/components/DynamicPage/__snapshots__/DynamicPage.test.tsx.snap
@@ -264,6 +264,7 @@ exports[`DynamicPage always show content header 1`] = `
{
* Fired when the `headerContent` is expanded or collapsed.
*/
onToggleHeaderContent?: (visible: boolean) => void;
+ /**
+ * Fired when the `headerContent` changes its `pinned` state.
+ */
+ onPinnedStateChange?: (pinned: boolean) => void;
}
/**
@@ -107,6 +111,7 @@ const DynamicPage = forwardRef((props, ref
footer,
a11yConfig,
onToggleHeaderContent,
+ onPinnedStateChange,
...rest
} = props;
const { onScroll: _1, ...propsWithoutOmitted } = rest;
@@ -117,6 +122,7 @@ const DynamicPage = forwardRef((props, ref
const [componentRefTopHeader, topHeaderRef] = useSyncRef((headerTitle as any)?.ref);
const [componentRefHeaderContent, headerContentRef] = useSyncRef((headerContent as any)?.ref);
+ const scrollTimeout = useRef(0);
const [headerState, setHeaderState] = useState(
alwaysShowContentHeader ? HEADER_STATES.VISIBLE_PINNED : HEADER_STATES.AUTO
@@ -134,7 +140,8 @@ const DynamicPage = forwardRef((props, ref
[headerCollapsedInternal, setHeaderCollapsedInternal],
{
noHeader: false,
- fixedHeader: headerState === HEADER_STATES.VISIBLE_PINNED || headerState === HEADER_STATES.HIDDEN_PINNED
+ fixedHeader: headerState === HEADER_STATES.VISIBLE_PINNED || headerState === HEADER_STATES.HIDDEN_PINNED,
+ scrollTimeout
}
);
@@ -168,19 +175,21 @@ const DynamicPage = forwardRef((props, ref
}, []);
useEffect(() => {
+ const dynamicPage = dynamicPageRef.current;
const oneTimeScrollHandler = () => {
setHeaderState(HEADER_STATES.AUTO);
setHeaderCollapsedInternal(true);
};
if (headerState === HEADER_STATES.VISIBLE || headerState === HEADER_STATES.HIDDEN) {
- dynamicPageRef.current?.addEventListener('scroll', oneTimeScrollHandler, { once: true });
+ dynamicPage?.addEventListener('scroll', oneTimeScrollHandler, { once: true });
}
return () => {
- dynamicPageRef.current?.removeEventListener('scroll', oneTimeScrollHandler);
+ dynamicPage?.removeEventListener('scroll', oneTimeScrollHandler);
};
}, [dynamicPageRef, headerState]);
const onToggleHeaderContentVisibility = (e) => {
+ scrollTimeout.current = performance.now() + 500;
const shouldHideHeader = !e.detail.visible;
setHeaderState((oldState) => {
if (oldState === HEADER_STATES.VISIBLE_PINNED || oldState === HEADER_STATES.HIDDEN_PINNED) {
@@ -222,10 +231,14 @@ const DynamicPage = forwardRef((props, ref
};
useEffect(() => {
- if (alwaysShowContentHeader) {
- setHeaderState(HEADER_STATES.VISIBLE_PINNED);
+ if (alwaysShowContentHeader !== undefined) {
+ if (alwaysShowContentHeader) {
+ setHeaderState(HEADER_STATES.VISIBLE_PINNED);
+ } else {
+ setHeaderState(HEADER_STATES.VISIBLE);
+ }
}
- }, [alwaysShowContentHeader, setHeaderState]);
+ }, [alwaysShowContentHeader]);
const responsivePaddingClass = useResponsiveContentPadding(dynamicPageRef.current);
@@ -297,11 +310,12 @@ const DynamicPage = forwardRef((props, ref
headerContentPinnable={headerContentPinnable}
showHideHeaderButton={showHideHeaderButton}
headerContentVisible={headerContent && headerCollapsed !== true}
- onToggleHeaderContentVisibility={onToggleHeaderContentInternal}
- setHeaderPinned={handleHeaderPinnedChange}
headerPinned={headerState === HEADER_STATES.VISIBLE_PINNED || headerState === HEADER_STATES.HIDDEN_PINNED}
- onHoverToggleButton={onHoverToggleButton}
a11yConfig={a11yConfig}
+ onHoverToggleButton={onHoverToggleButton}
+ onToggleHeaderContentVisibility={onToggleHeaderContentInternal}
+ onPinnedStateChange={onPinnedStateChange}
+ setHeaderPinned={handleHeaderPinnedChange}
/>
void;
}
/**
@@ -118,11 +122,12 @@ const DynamicPageAnchorBar = forwardRef
{
+ if (!isInitial.current && typeof onPinnedStateChange === 'function') {
+ onPinnedStateChange(headerPinned);
+ }
+ if (isInitial.current) {
+ isInitial.current = false;
+ }
+ }, [headerPinned]);
+
const anchorBarActionButtonClasses = clsx(classes.anchorBarActionButton, isRTL && classes.anchorBarActionButtonRtl);
const bothActionClasses = clsx(
@@ -172,6 +187,7 @@ const DynamicPageAnchorBar = forwardRef
)}
{shouldRenderHeaderPinnableButton && (
@@ -187,6 +203,7 @@ const DynamicPageAnchorBar = forwardRef
)}
diff --git a/packages/main/src/components/ObjectPage/ObjectPage.cy.tsx b/packages/main/src/components/ObjectPage/ObjectPage.cy.tsx
index 86a972c926e..3b5299309f7 100644
--- a/packages/main/src/components/ObjectPage/ObjectPage.cy.tsx
+++ b/packages/main/src/components/ObjectPage/ObjectPage.cy.tsx
@@ -1,4 +1,5 @@
-import { ObjectPage, DynamicPageHeader, DynamicPageTitle, ObjectPageSection } from '../..';
+import { useState } from 'react';
+import { Button, DynamicPageHeader, DynamicPageTitle, ObjectPage, ObjectPagePropTypes, ObjectPageSection } from '../..';
describe('ObjectPage', () => {
it('toggle header', () => {
@@ -38,4 +39,167 @@ describe('ObjectPage', () => {
cy.get('@toggleSpy').should('have.been.calledWith', true);
cy.get('@toggleSpy').should('have.callCount', 4);
});
+
+ it('pin header', () => {
+ const pin = cy.spy().as('onPinSpy');
+ cy.mount(
+ }
+ headerContent={ObjectPageHeader}
+ headerContentPinnable
+ showHideHeaderButton
+ onPinnedStateChange={pin}
+ data-testid="op"
+ >
+
+
+
+
+ );
+ cy.wait(50);
+
+ cy.findByTestId('op').scrollTo(0, 500);
+ cy.findByText('ObjectPageHeader').should('not.be.visible');
+
+ cy.findByTestId('op').scrollTo('top');
+
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').click();
+ cy.get('@onPinSpy').should('have.been.calledOnce');
+ cy.get('@onPinSpy').should('have.been.calledWith', true);
+
+ cy.findByTestId('op').scrollTo(0, 500);
+ cy.findByText('ObjectPageHeader').should('be.visible');
+
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').click();
+ cy.get('@onPinSpy').should('have.been.calledTwice');
+ cy.get('@onPinSpy').should('have.been.calledWith', false);
+ cy.findByText('ObjectPageHeader').should('not.be.visible');
+ });
+
+ it('programmatically pin header (`alwaysShowContentHeader`)', () => {
+ const TestComp = ({ onPinnedStateChange }: ObjectPagePropTypes) => {
+ const [pinned, setPinned] = useState(false);
+ const handlePinChange = (pinned) => {
+ onPinnedStateChange(pinned);
+ setPinned(pinned);
+ };
+ return (
+ <>
+
+ }
+ headerContent={ObjectPageHeader}
+ headerContentPinnable
+ showHideHeaderButton
+ alwaysShowContentHeader={pinned}
+ onPinnedStateChange={handlePinChange}
+ data-testid="op"
+ >
+
+
+
+
+ >
+ );
+ };
+ const pin = cy.spy().as('onPinSpy');
+ cy.mount();
+ cy.wait(50);
+
+ cy.findByTestId('op').scrollTo(0, 500);
+ cy.findByText('ObjectPageHeader').should('not.be.visible');
+
+ cy.findByTestId('btn').click();
+ cy.get('@onPinSpy').should('have.been.calledOnce');
+ cy.get('@onPinSpy').should('have.been.calledWith', true);
+ cy.findByText('ObjectPageHeader').should('be.visible');
+
+ cy.findByTestId('op').scrollTo(0, 0);
+ cy.findByText('ObjectPageHeader').should('be.visible');
+
+ cy.findByTestId('op').scrollTo(0, 800);
+ cy.findByText('ObjectPageHeader').should('be.visible');
+
+ cy.findByTestId('btn').click();
+ cy.get('@onPinSpy').should('have.been.calledTwice');
+ cy.get('@onPinSpy').should('have.been.calledWith', false);
+ cy.findByText('ObjectPageHeader').should('not.be.visible');
+
+ cy.findByTestId('btn').click();
+ cy.findByTestId('op').scrollTo(0, 500);
+ cy.findByText('ObjectPageHeader').should('be.visible');
+
+ cy.findByTestId('btn').click();
+ cy.findByText('ObjectPageHeader').should('not.be.visible');
+
+ cy.get('[data-component-name="DynamicPageAnchorBarExpandBtn"]').click();
+ cy.findByText('ObjectPageHeader').should('be.visible');
+
+ // wait for timeout of expand click
+ cy.wait(500);
+
+ cy.findByTestId('op').scrollTo(0, 501);
+ cy.findByText('ObjectPageHeader').should('not.be.visible');
+
+ cy.findByTestId('op').scrollTo(0, 30);
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').click();
+ cy.get('@onPinSpy').should('have.callCount', 5);
+ cy.findByTestId('btn').should('have.text', 'toggle false');
+ cy.findByText('ObjectPageHeader').should('be.visible');
+
+ cy.findByTestId('op').scrollTo(0, 500);
+ cy.findByTestId('btn').click();
+ cy.findByText('ObjectPageHeader').should('not.be.visible');
+
+ cy.findByTestId('btn').click();
+ cy.findByText('ObjectPageHeader').should('be.visible');
+ cy.get('@onPinSpy').should('have.callCount', 7);
+ });
+
+ it('collapse header when partially visible', () => {
+ cy.viewport(1440, 1080);
+ cy.mount(
+ }
+ headerContent={
+
+ ObjectPageHeader
+
+ }
+ headerContentPinnable
+ showHideHeaderButton
+ data-testid="op"
+ >
+
+
+
+
+ );
+ cy.wait(50);
+
+ cy.findByTestId('op').scrollTo(0, 400);
+ cy.get('[data-component-name="DynamicPageAnchorBarExpandBtn"]').click();
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').should('not.exist');
+ cy.findByText('ObjectPageHeader').should('not.be.visible');
+
+ // wait for timeout of expand click
+ cy.wait(500);
+
+ cy.findByTestId('op').scrollTo(0, 1);
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').should('not.exist');
+ cy.wait(50);
+ cy.findByTestId('op').scrollTo(0, 0);
+ cy.get('[data-component-name="DynamicPageAnchorBarPinBtn"]').should('be.visible');
+ cy.findByText('ObjectPageHeader').should('be.visible');
+ });
});
diff --git a/packages/main/src/components/ObjectPage/__snapshots__/ObjectPage.test.tsx.snap b/packages/main/src/components/ObjectPage/__snapshots__/ObjectPage.test.tsx.snap
index 5e9e084eaca..91bfd00e78b 100644
--- a/packages/main/src/components/ObjectPage/__snapshots__/ObjectPage.test.tsx.snap
+++ b/packages/main/src/components/ObjectPage/__snapshots__/ObjectPage.test.tsx.snap
@@ -1213,6 +1213,7 @@ exports[`ObjectPage with anchor-bar 1`] = `
{
* Defines the ID of the currently `ObjectPageSubSection` section.
*/
selectedSubSectionId?: string;
- /**
- * Fired when the selected section changes.
- */
- onSelectedSectionChange?: (
- event: CustomEvent<{ selectedSectionIndex: number; selectedSectionId: string; section: HTMLDivElement }>
- ) => void;
- /**
- * Fired when the `headerContent` is expanded or collapsed.
- */
- onToggleHeaderContent?: (visible: boolean) => void;
-
- // appearance
/**
* Defines whether the `headerContent` is hidden by scrolling down.
*/
@@ -131,6 +119,20 @@ export interface ObjectPagePropTypes extends Omit {
* __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;
+ /**
+ * Fired when the selected section changes.
+ */
+ onSelectedSectionChange?: (
+ event: CustomEvent<{ selectedSectionIndex: number; selectedSectionId: string; section: HTMLDivElement }>
+ ) => void;
+ /**
+ * Fired when the `headerContent` is expanded or collapsed.
+ */
+ onToggleHeaderContent?: (visible: boolean) => void;
+ /**
+ * Fired when the `headerContent` changes its pinned state.
+ */
+ onPinnedStateChange?: (pinned: boolean) => void;
}
const useStyles = createUseStyles(styles, { name: 'ObjectPage' });
@@ -161,6 +163,7 @@ const ObjectPage = forwardRef((props, ref)
placeholder,
onSelectedSectionChange,
onToggleHeaderContent,
+ onPinnedStateChange,
...rest
} = props;
@@ -180,9 +183,14 @@ const ObjectPage = forwardRef((props, ref)
//@ts-ignore
const [componentRefHeaderContent, headerContentRef] = useSyncRef(headerContent?.ref);
const anchorBarRef = useRef(null);
- const scrollTimeout = useRef(null);
+ const selectionScrollTimeout = useRef(null);
const [isAfterScroll, setIsAfterScroll] = useState(false);
const isToggledRef = useRef(false);
+ const isRTL = useIsRTL(objectPageRef);
+ const [responsivePaddingClass, responsiveRange] = useResponsiveContentPadding(objectPageRef.current, true);
+ const [headerCollapsedInternal, setHeaderCollapsedInternal] = useState(undefined);
+ const [scrolledHeaderExpanded, setScrolledHeaderExpanded] = useState(false);
+ const scrollTimeout = useRef(0);
const prevInternalSelectedSectionId = useRef(internalSelectedSectionId);
const fireOnSelectedChangedEvent = (targetEvent, index, id, section) => {
@@ -198,18 +206,13 @@ const ObjectPage = forwardRef((props, ref)
}
};
const debouncedOnSectionChange = useRef(debounce(fireOnSelectedChangedEvent, 500)).current;
-
useEffect(() => {
return () => {
debouncedOnSectionChange.cancel();
- clearTimeout(scrollTimeout.current);
+ clearTimeout(selectionScrollTimeout.current);
};
}, []);
- const isRTL = useIsRTL(objectPageRef);
- const [responsivePaddingClass, responsiveRange] = useResponsiveContentPadding(objectPageRef.current, true);
-
- const [headerCollapsedInternal, setHeaderCollapsedInternal] = useState(undefined);
// observe heights of header parts
const { topHeaderHeight, headerContentHeight, anchorBarHeight, totalHeaderHeight, headerCollapsed } =
useObserveHeights(
@@ -220,7 +223,8 @@ const ObjectPage = forwardRef((props, ref)
[headerCollapsedInternal, setHeaderCollapsedInternal],
{
noHeader: !headerTitle && !headerContent,
- fixedHeader: headerPinned
+ fixedHeader: headerPinned,
+ scrollTimeout
}
);
@@ -387,8 +391,24 @@ const ObjectPage = forwardRef((props, ref)
]);
useEffect(() => {
- setHeaderPinned(alwaysShowContentHeader);
- }, [setHeaderPinned, alwaysShowContentHeader]);
+ if (alwaysShowContentHeader !== undefined) {
+ setHeaderPinned(alwaysShowContentHeader);
+ }
+ if (alwaysShowContentHeader) {
+ onToggleHeaderContentVisibility({ detail: { visible: true } });
+ }
+ }, [alwaysShowContentHeader]);
+
+ const prevHeaderPinned = useRef(headerPinned);
+ useEffect(() => {
+ if (prevHeaderPinned.current && !headerPinned && objectPageRef.current.scrollTop > topHeaderHeight) {
+ onToggleHeaderContentVisibility({ detail: { visible: false } });
+ prevHeaderPinned.current = false;
+ }
+ if (!prevHeaderPinned.current && headerPinned) {
+ prevHeaderPinned.current = true;
+ }
+ }, [headerPinned, topHeaderHeight]);
useEffect(() => {
setSelectedSubSectionId(props.selectedSubSectionId);
@@ -453,6 +473,19 @@ const ObjectPage = forwardRef((props, ref)
};
}, [totalHeaderHeight, objectPageRef, children, mode, footer]);
+ const onToggleHeaderContentVisibility = useCallback((e) => {
+ isToggledRef.current = true;
+ scrollTimeout.current = performance.now() + 500;
+ if (!e.detail.visible) {
+ setHeaderCollapsedInternal(true);
+ objectPageRef.current?.classList.add(classes.headerCollapsed);
+ } else {
+ setHeaderCollapsedInternal(false);
+ setScrolledHeaderExpanded(true);
+ objectPageRef.current?.classList.remove(classes.headerCollapsed);
+ }
+ }, []);
+
const handleOnSubSectionSelected = useCallback(
(e) => {
isProgrammaticallyScrolled.current = true;
@@ -471,20 +504,6 @@ const ObjectPage = forwardRef((props, ref)
},
[mode, setInternalSelectedSectionId, setSelectedSubSectionId, isProgrammaticallyScrolled, children]
);
- const [scrolledHeaderExpanded, setScrolledHeaderExpanded] = useState(false);
- const scrollTimout = useRef(0);
- const onToggleHeaderContentVisibility = useCallback((e) => {
- isToggledRef.current = true;
- scrollTimout.current = performance.now() + 500;
- if (!e.detail.visible) {
- setHeaderCollapsedInternal(true);
- objectPageRef.current?.classList.add(classes.headerCollapsed);
- } else {
- setHeaderCollapsedInternal(false);
- setScrolledHeaderExpanded(true);
- objectPageRef.current?.classList.remove(classes.headerCollapsed);
- }
- }, []);
const objectPageClasses = clsx(
classes.objectPage,
@@ -659,7 +678,7 @@ const ObjectPage = forwardRef((props, ref)
if (!isToggledRef.current) {
isToggledRef.current = true;
}
- if (scrollTimout.current >= performance.now()) {
+ if (scrollTimeout.current >= performance.now()) {
return;
}
scrollEvent.current = e;
@@ -669,10 +688,10 @@ const ObjectPage = forwardRef((props, ref)
if (selectedSubSectionId) {
setSelectedSubSectionId(undefined);
}
- if (scrollTimeout.current) {
- clearTimeout(scrollTimeout.current);
+ if (selectionScrollTimeout.current) {
+ clearTimeout(selectionScrollTimeout.current);
}
- scrollTimeout.current = setTimeout(() => {
+ selectionScrollTimeout.current = setTimeout(() => {
setIsAfterScroll(true);
}, 100);
if (!headerPinned || e.target.scrollTop === 0) {
@@ -757,11 +776,12 @@ const ObjectPage = forwardRef((props, ref)
headerContentVisible={headerContent && headerCollapsed !== true}
headerContentPinnable={headerContentPinnable}
showHideHeaderButton={showHideHeaderButton}
+ headerPinned={headerPinned}
+ a11yConfig={a11yConfig}
onToggleHeaderContentVisibility={onToggleHeaderContentVisibility}
setHeaderPinned={setHeaderPinned}
- headerPinned={headerPinned}
onHoverToggleButton={onHoverToggleButton}
- a11yConfig={a11yConfig}
+ onPinnedStateChange={onPinnedStateChange}
/>
)}
diff --git a/packages/main/src/internal/useObserveHeights.ts b/packages/main/src/internal/useObserveHeights.ts
index 4f1410b7c0b..8d5f084c89b 100644
--- a/packages/main/src/internal/useObserveHeights.ts
+++ b/packages/main/src/internal/useObserveHeights.ts
@@ -8,7 +8,11 @@ export const useObserveHeights = (
headerContentRef,
anchorBarRef,
[headerCollapsed, setHeaderCollapsed]: [boolean, React.Dispatch>],
- { noHeader, fixedHeader = false }: { noHeader: boolean; fixedHeader?: boolean }
+ {
+ noHeader,
+ fixedHeader = false,
+ scrollTimeout = { current: 0 }
+ }: { noHeader: boolean; fixedHeader?: boolean; scrollTimeout?: React.MutableRefObject }
) => {
const [topHeaderHeight, setTopHeaderHeight] = useState(0);
const [headerContentHeight, setHeaderContentHeight] = useState(0);
@@ -19,6 +23,11 @@ export const useObserveHeights = (
(e) => {
const scrollDown = prevScrollTop.current <= e.target.scrollTop;
prevScrollTop.current = e.target.scrollTop;
+
+ if (scrollTimeout.current >= performance.now()) {
+ return;
+ }
+
if (scrollDown && e.target.scrollTop >= headerContentHeight && !headerCollapsed) {
setIsIntersecting(false);
setHeaderCollapsed(true);
@@ -31,11 +40,18 @@ export const useObserveHeights = (
);
useEffect(() => {
+ if (headerContentRef.current && headerCollapsed !== undefined) {
+ setHeaderContentHeight(headerContentRef.current.getBoundingClientRect().height);
+ }
+ }, [headerCollapsed]);
+
+ useEffect(() => {
+ const page = pageRef.current;
if (!fixedHeader) {
- pageRef.current.addEventListener('scroll', onScroll);
+ page.addEventListener('scroll', onScroll);
}
return () => {
- pageRef.current?.removeEventListener('scroll', onScroll);
+ page.removeEventListener('scroll', onScroll);
};
}, [onScroll, fixedHeader]);
diff --git a/tsconfig.spec.json b/tsconfig.spec.json
index 32394567d51..dc58d15912f 100644
--- a/tsconfig.spec.json
+++ b/tsconfig.spec.json
@@ -6,5 +6,5 @@
"moduleResolution": "Node",
"jsx": "react-jsx"
},
- "include": ["cypress/**/*.ts", "cypress/**/*.tsx", "**/*.cy.ts", "**/*.cy.tsx"]
+ "include": ["cypress/**/*.ts", "cypress/**/*.tsx", "**/*.cy.ts", "**/*.cy.tsx", "cypress.config.ts"]
}