mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 05:11:46 +00:00
fix(atlas): constrain bucket list width to prevent panel overflow
With 30+ bucket list entries the panel expanded to near-full viewport width, elongating the Stats tab, hiding overflow entries, and covering the Leaflet zoom controls. Measure the stats content width via ResizeObserver and use it as maxWidth on the horizontal bucket row so scroll activates exactly when entries exceed the stats panel width. Also fixes the ResizeObserver test mock to use a class (matching the IntersectionObserver pattern) so the instance methods are accessible. Closes #787
This commit is contained in:
@@ -1240,6 +1240,15 @@ interface SidebarContentProps {
|
||||
|
||||
function SidebarContent({ data, stats, countries, selectedCountry, countryDetail, resolveName, onTripClick, onUnmarkCountry, bucketList, bucketTab, setBucketTab, showBucketAdd, setShowBucketAdd, bucketForm, setBucketForm, onAddBucket, onDeleteBucket, onSearchBucket, onSelectBucketPoi, bucketSearchResults, setBucketSearchResults, bucketPoiMonth, setBucketPoiMonth, bucketPoiYear, setBucketPoiYear, bucketSearching, bucketSearch, setBucketSearch, t, dark }: SidebarContentProps): React.ReactElement {
|
||||
const { language } = useTranslation()
|
||||
const statsContentRef = useRef<HTMLDivElement>(null)
|
||||
const [statsWidth, setStatsWidth] = useState<number | undefined>(undefined)
|
||||
useEffect(() => {
|
||||
const el = statsContentRef.current
|
||||
if (!el || typeof ResizeObserver === 'undefined') return
|
||||
const ro = new ResizeObserver(() => setStatsWidth(el.offsetWidth))
|
||||
ro.observe(el)
|
||||
return () => ro.disconnect()
|
||||
}, [])
|
||||
const bg = (o) => dark ? `rgba(255,255,255,${o})` : `rgba(0,0,0,${o})`
|
||||
const tp = dark ? '#f1f5f9' : '#0f172a'
|
||||
const tm = dark ? '#94a3b8' : '#64748b'
|
||||
@@ -1290,7 +1299,7 @@ function SidebarContent({ data, stats, countries, selectedCountry, countryDetail
|
||||
// Bucket list content
|
||||
const bucketContent = (
|
||||
<>
|
||||
<div className="flex items-stretch" style={{ overflowX: 'auto', padding: '0 8px' }}>
|
||||
<div className="flex items-stretch" style={{ overflowX: 'auto', padding: '0 8px', maxWidth: statsWidth, width: '100%' }}>
|
||||
{bucketList.map(item => (
|
||||
<div key={item.id} className="group flex flex-col items-center justify-center shrink-0" style={{ padding: '8px 14px', position: 'relative', minWidth: 80 }}>
|
||||
{(() => {
|
||||
@@ -1400,7 +1409,7 @@ function SidebarContent({ data, stats, countries, selectedCountry, countryDetail
|
||||
{/* Both tabs always rendered so the wider one sets the panel width */}
|
||||
<div style={{ display: 'grid' }}>
|
||||
<div style={bucketTab === 'bucket' ? { visibility: 'hidden' as const, gridArea: '1/1' } : { gridArea: '1/1' }}>
|
||||
<div className="flex items-stretch justify-center">
|
||||
<div ref={statsContentRef} className="flex items-stretch justify-center">
|
||||
|
||||
{/* ═══ SECTION 1: Numbers ═══ */}
|
||||
{/* Countries hero */}
|
||||
|
||||
@@ -64,11 +64,13 @@ class _MockIntersectionObserver {
|
||||
globalThis.IntersectionObserver = _MockIntersectionObserver as unknown as typeof IntersectionObserver;
|
||||
|
||||
// ResizeObserver — used by resizable panels
|
||||
globalThis.ResizeObserver = vi.fn().mockImplementation(() => ({
|
||||
observe: vi.fn(),
|
||||
unobserve: vi.fn(),
|
||||
disconnect: vi.fn(),
|
||||
})) as unknown as typeof ResizeObserver;
|
||||
class _MockResizeObserver {
|
||||
observe = vi.fn()
|
||||
unobserve = vi.fn()
|
||||
disconnect = vi.fn()
|
||||
constructor(_callback: ResizeObserverCallback) {}
|
||||
}
|
||||
globalThis.ResizeObserver = _MockResizeObserver as unknown as typeof ResizeObserver;
|
||||
|
||||
// URL.createObjectURL / revokeObjectURL — Node 22 URL.createObjectURL requires
|
||||
// a native node:buffer Blob; passing a jsdom Blob throws ERR_INVALID_ARG_TYPE.
|
||||
|
||||
Reference in New Issue
Block a user