Skip to content

Commit bd0e547

Browse files
committed
Add useOverflow hook, fix broken story missing aria-label.
1 parent 90883a5 commit bd0e547

File tree

3 files changed

+31
-13
lines changed

3 files changed

+31
-13
lines changed

src/PageLayout/PageLayout.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ export const ScrollContainerWithinPageLayoutPane: Story = () => (
736736
<Box sx={{overflow: 'auto'}}>
737737
<Placeholder label="Above inner scroll container" height={120} />
738738
<PageLayout rowGap="none" columnGap="none" padding="none" containerWidth="full">
739-
<PageLayout.Pane position="start" padding="normal" divider="line" sticky>
739+
<PageLayout.Pane position="start" padding="normal" divider="line" sticky aria-label="Sticky pane">
740740
<Box sx={{overflow: 'auto'}}>
741741
<PageLayout.Pane padding="normal">
742742
<Placeholder label="Inner scroll container" height={800} />

src/PageLayout/PageLayout.tsx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {useSlots} from '../hooks/useSlots'
88
import {BetterSystemStyleObject, merge, SxProp} from '../sx'
99
import {Theme} from '../ThemeProvider'
1010
import {canUseDOM} from '../utils/environment'
11+
import {invariant} from '../utils/invariant'
12+
import {useOverflow} from '../utils/useOverflow'
1113
import VisuallyHidden from '../_VisuallyHidden'
1214
import {useStickyPaneHeight} from './useStickyPaneHeight'
1315

@@ -604,7 +606,7 @@ const Pane = React.forwardRef<HTMLDivElement, React.PropsWithChildren<PageLayout
604606
const MIN_PANE_WIDTH = 256 // 256px, related to `--pane-min-width CSS var.
605607
const [minPercent, setMinPercent] = React.useState(0)
606608
const [maxPercent, setMaxPercent] = React.useState(0)
607-
const [overflow, setOverflow] = React.useState(false)
609+
const hasOverflow = useOverflow(paneRef)
608610

609611
const measuredRef = React.useCallback(() => {
610612
if (paneRef.current !== null) {
@@ -624,11 +626,6 @@ const Pane = React.forwardRef<HTMLDivElement, React.PropsWithChildren<PageLayout
624626

625627
const widthPercent = Math.round((100 * paneWidth) / viewportWidth)
626628
setWidthPercent(widthPercent.toString())
627-
628-
const hasOverflow =
629-
paneRef.current.scrollHeight > paneRef.current.offsetHeight ||
630-
paneRef.current.scrollWidth > paneRef.current.offsetWidth
631-
setOverflow(hasOverflow)
632629
}
633630
}, [paneRef])
634631

@@ -656,16 +653,13 @@ const Pane = React.forwardRef<HTMLDivElement, React.PropsWithChildren<PageLayout
656653
const paneId = useId(id)
657654

658655
let labelProp = undefined
659-
if (overflow) {
656+
if (hasOverflow) {
657+
invariant(label !== undefined || labelledBy !== undefined)
660658
if (labelledBy) {
661659
labelProp = {'aria-labelledby': labelledBy}
662660
} else {
663661
labelProp = {'aria-label': label}
664662
}
665-
666-
if (labelProp['aria-label'] === undefined) {
667-
throw new Error('PageLayout.Pane must have either `aria-labelledby` or `aria-label` when overflow is active.')
668-
}
669663
}
670664

671665
return (
@@ -755,7 +749,7 @@ const Pane = React.forwardRef<HTMLDivElement, React.PropsWithChildren<PageLayout
755749
'--pane-max-width-diff': '959px',
756750
},
757751
})}
758-
{...(overflow && {tabIndex: 0, role: 'region'})}
752+
{...(hasOverflow && {tabIndex: 0, role: 'region'})}
759753
{...labelProp}
760754
{...(id && {id: paneId})}
761755
>

src/utils/useOverflow.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {useEffect, useState} from 'react'
2+
3+
export function useOverflow<T extends HTMLElement>(ref: React.RefObject<T>) {
4+
const [hasOverflow, setHasOverflow] = useState(false)
5+
6+
useEffect(() => {
7+
if (ref.current === null) return
8+
9+
const observer = new ResizeObserver(entries => {
10+
for (const entry of entries) {
11+
setHasOverflow(
12+
entry.target.scrollHeight > entry.target.clientHeight || entry.target.scrollWidth > entry.target.clientWidth,
13+
)
14+
}
15+
})
16+
17+
observer.observe(ref.current)
18+
return () => {
19+
observer.disconnect()
20+
}
21+
}, [ref])
22+
23+
return hasOverflow
24+
}

0 commit comments

Comments
 (0)