Skip to content

Commit 515992a

Browse files
Add stickyTop prop to PageLayout.Pane
1 parent 4badb57 commit 515992a

File tree

4 files changed

+119
-8
lines changed

4 files changed

+119
-8
lines changed

docs/content/PageLayout.mdx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,36 @@ See [storybook](https://primer.style/react/storybook?path=/story/layout-pagelayo
168168
</Box>
169169
```
170170

171+
### With a stickyTop sticky pane
172+
173+
```jsx live
174+
<Box sx={{height: '360px', overflowY: 'scroll', border: '1px solid', borderColor: 'border.default'}}>
175+
<Box
176+
height="100px"
177+
width="100%"
178+
borderBottom="1px solid"
179+
borderColor="border.default"
180+
sx={{position: 'sticky', top: '0', backgroundColor: 'canvas.subtle'}}
181+
display="flex"
182+
alignItems="center"
183+
justifyContent="center"
184+
>
185+
<Heading>Sticky top content</Heading>
186+
</Box>
187+
<PageLayout>
188+
<PageLayout.Content>
189+
<Placeholder label="Content" height={240} />
190+
</PageLayout.Content>
191+
<PageLayout.Pane stickyTop={100} position="start" sticky>
192+
<Placeholder label="Pane" height={240} />
193+
</PageLayout.Pane>
194+
<PageLayout.Footer>
195+
<Placeholder label="Footer" height={64} />
196+
</PageLayout.Footer>
197+
</PageLayout>
198+
</Box>
199+
```
200+
171201
## Props
172202

173203
### PageLayout

src/PageLayout/PageLayout.stories.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,4 +659,72 @@ NestedScrollContainer.argTypes = {
659659
}
660660
}
661661

662+
export const StickyPaneWithStickyTop: Story = args => (
663+
// a box to create a sticky top element that will be on the consumer side and outside of the PageLayout component
664+
<Box>
665+
<Box
666+
height={args.stickyTop}
667+
width="100%"
668+
border="1px solid"
669+
borderColor="border.default"
670+
sx={{position: 'sticky', top: '0', backgroundColor: 'canvas.subtle'}}
671+
display="flex"
672+
alignItems="center"
673+
justifyContent="center"
674+
>
675+
<Heading>Sticky top content</Heading>
676+
</Box>
677+
<PageLayout rowGap="none" columnGap="none" padding="none" containerWidth="full">
678+
<PageLayout.Content padding="normal" width="large">
679+
<Box sx={{display: 'grid', gap: 3}}>
680+
{Array.from({length: args.numParagraphsInContent}).map((_, i) => (
681+
<Box key={i} as="p" sx={{margin: 0}}>
682+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam at enim id lorem tempus egestas a non ipsum.
683+
Maecenas imperdiet ante quam, at varius lorem molestie vel. Sed at eros consequat, varius tellus et,
684+
auctor felis. Donec pulvinar lacinia urna nec commodo. Phasellus at imperdiet risus. Donec sit amet massa
685+
purus. Nunc sem lectus, bibendum a sapien nec, tristique tempus felis. Ut porttitor auctor tellus in
686+
imperdiet. Ut blandit tincidunt augue, quis fringilla nunc tincidunt sed. Vestibulum auctor euismod nisi.
687+
Nullam tincidunt est in mi tincidunt dictum. Sed consectetur aliquet velit ut ornare.
688+
</Box>
689+
))}
690+
</Box>
691+
</PageLayout.Content>
692+
<PageLayout.Pane position="start" padding="normal" divider="line" stickyTop={100} sticky>
693+
<Box sx={{display: 'grid', gap: 3}}>
694+
{Array.from({length: args.numParagraphsInPane}).map((_, i) => (
695+
<Box key={i} as="p" sx={{margin: 0}}>
696+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam at enim id lorem tempus egestas a non ipsum.
697+
Maecenas imperdiet ante quam, at varius lorem molestie vel. Sed at eros consequat, varius tellus et,
698+
auctor felis. Donec pulvinar lacinia urna nec commodo. Phasellus at imperdiet risus. Donec sit amet massa
699+
purus.
700+
</Box>
701+
))}
702+
</Box>
703+
</PageLayout.Pane>
704+
<PageLayout.Footer padding="normal" divider="line">
705+
<Placeholder label="Footer" height={64} />
706+
</PageLayout.Footer>
707+
</PageLayout>
708+
</Box>
709+
)
710+
711+
StickyPaneWithStickyTop.argTypes = {
712+
sticky: {
713+
type: 'boolean',
714+
defaultValue: true
715+
},
716+
stickyTop: {
717+
type: 'number',
718+
defaultValue: 100
719+
},
720+
numParagraphsInPane: {
721+
type: 'number',
722+
defaultValue: 10
723+
},
724+
numParagraphsInContent: {
725+
type: 'number',
726+
defaultValue: 30
727+
}
728+
}
729+
662730
export default meta

src/PageLayout/PageLayout.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const PageLayoutContext = React.createContext<{
2222
padding: keyof typeof SPACING_MAP
2323
rowGap: keyof typeof SPACING_MAP
2424
columnGap: keyof typeof SPACING_MAP
25-
enableStickyPane?: () => void
25+
enableStickyPane?: (stickyTopHeight: number) => void
2626
disableStickyPane?: () => void
2727
contentTopRef?: (node?: Element | null | undefined) => void
2828
contentBottomRef?: (node?: Element | null | undefined) => void
@@ -362,6 +362,7 @@ export type PageLayoutPaneProps = {
362362
*/
363363
dividerWhenNarrow?: 'inherit' | 'none' | 'line' | 'filled'
364364
sticky?: boolean
365+
stickyTop?: number // the height of the sticky top element
365366
hidden?: boolean | ResponsiveValue<boolean>
366367
} & SxProp
367368

@@ -384,6 +385,7 @@ const Pane: React.FC<React.PropsWithChildren<PageLayoutPaneProps>> = ({
384385
divider: responsiveDivider = 'none',
385386
dividerWhenNarrow = 'inherit',
386387
sticky = false,
388+
stickyTop = 0,
387389
hidden: responsiveHidden = false,
388390
children,
389391
sx = {}
@@ -410,11 +412,11 @@ const Pane: React.FC<React.PropsWithChildren<PageLayoutPaneProps>> = ({
410412

411413
React.useEffect(() => {
412414
if (sticky) {
413-
enableStickyPane?.()
415+
enableStickyPane?.(stickyTop)
414416
} else {
415417
disableStickyPane?.()
416418
}
417-
}, [sticky, enableStickyPane, disableStickyPane])
419+
}, [sticky, enableStickyPane, disableStickyPane, stickyTop])
418420

419421
return (
420422
<Box
@@ -439,7 +441,9 @@ const Pane: React.FC<React.PropsWithChildren<PageLayoutPaneProps>> = ({
439441
...(sticky
440442
? {
441443
position: 'sticky',
442-
top: 0,
444+
// If stickyTop has value, it will stick the pane to the position where the sticky top ends
445+
// else top will be 0 as the default value of stickyTop
446+
top: stickyTop,
443447
overflow: 'hidden',
444448
maxHeight: 'var(--sticky-pane-height)'
445449
}

src/PageLayout/useStickyPaneHeight.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export function useStickyPaneHeight() {
1111

1212
// Default the height to the viewport height
1313
const [height, setHeight] = React.useState('100vh')
14+
// stickyTop state
15+
const [stickyTopHeight, setStickyTopHeight] = React.useState(0)
1416

1517
// Create intersection observers to track the top and bottom of the content region
1618
const [contentTopRef, contentTopInView, contentTopEntry] = useInView()
@@ -44,11 +46,11 @@ export function useStickyPaneHeight() {
4446
// We need to account for this when calculating the offset.
4547
const overflowScroll = Math.max(window.scrollY + window.innerHeight - document.body.scrollHeight, 0)
4648

47-
calculatedHeight = `calc(100vh - ${topOffset + bottomOffset - overflowScroll}px)`
49+
calculatedHeight = `calc(100vh - ${Math.max(topOffset, stickyTopHeight) + bottomOffset - overflowScroll}px)`
4850
}
4951

5052
setHeight(calculatedHeight)
51-
}, [contentTopEntry, contentBottomEntry])
53+
}, [contentTopEntry, contentBottomEntry, stickyTopHeight])
5254

5355
// We only want to add scroll and resize listeners if the pane is sticky.
5456
// Since hooks can't be called conditionally, we need to use state to track
@@ -88,10 +90,17 @@ export function useStickyPaneHeight() {
8890
}
8991
}, [isEnabled, contentTopInView, contentBottomInView, calculateHeight])
9092

93+
const enableStickyPane = (stickyTop: number) => {
94+
setIsEnabled(true)
95+
setStickyTopHeight(stickyTop)
96+
}
97+
98+
const disableStickyPane = () => setIsEnabled(false)
99+
91100
return {
92101
rootRef,
93-
enableStickyPane: () => setIsEnabled(true),
94-
disableStickyPane: () => setIsEnabled(false),
102+
enableStickyPane,
103+
disableStickyPane,
95104
contentTopRef,
96105
contentBottomRef,
97106
stickyPaneHeight: height

0 commit comments

Comments
 (0)