Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions dotcom-rendering/src/components/TableOfContents.importable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
SvgChevronDownSingle,
SvgChevronUpSingle,
} from '@guardian/source/react-components';
import { useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { ArticleDisplay, type ArticleFormat } from '../lib/articleFormat';
import { getZIndex } from '../lib/getZIndex';
import type { TableOfContentsItem } from '../model/enhanceTableOfContents';
Expand Down Expand Up @@ -75,7 +75,7 @@ const detailsStyles = css`
`;
const stickyStyles = css`
position: sticky;
top: 0;
top: -1px;
background: ${palette('--article-background')};
z-index: ${getZIndex('tableOfContents')};
summary {
Expand Down Expand Up @@ -137,14 +137,39 @@ const verticalStyle = css`

export const TableOfContents = ({ tableOfContents, format }: Props) => {
const [open, setOpen] = useState(tableOfContents.length < 5);
const tocRef = useRef<HTMLDetailsElement>(null);

// Automatically collapse the ToC when it becomes sticky (i.e. when it reaches the top of the viewport). This
// approach is inspired by:
// https://css-tricks.com/how-to-detect-when-a-sticky-element-gets-pinned/
useEffect(() => {
const tocElement = tocRef.current;

if (!tocElement) return;

const observer = new IntersectionObserver(
Comment thread
arelra marked this conversation as resolved.
([e]) => {
// Verify whether the ToC is at the top of the viewport or the bottom. It should only collapse when it
// reaches the top.
if (e && e.boundingClientRect.top < 0) {
setOpen(false);
}
},
{ threshold: [1] },
);

observer.observe(tocElement);

return () => {
observer.disconnect();
};
}, []);

return (
<details
ref={tocRef}
open={open}
css={[
detailsStyles,
tableOfContents.length > 5 ? stickyStyles : undefined,
]}
css={[detailsStyles, stickyStyles]}
data-component="table-of-contents"
>
<summary
Expand Down