From 51bf3a1e34b227f332935e0f48190568e8c3af0e Mon Sep 17 00:00:00 2001 From: mrholek Date: Mon, 14 Sep 2026 02:02:34 +0200 Subject: [PATCH] fix(COffcanvas): restore body scroll when unmounted while visible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The effect that locks body scroll had no cleanup, so unmounting an open offcanvas left `overflow: hidden` and the padding compensation on the body with nothing to undo them — the page stayed unscrollable. An SPA route change that tears the tree down while the offcanvas is open is the ordinary way to hit it. CModal next door already does this correctly; the effect now has the same shape, with the lock and release in one if/else and the release repeated in a cleanup. The early `return` in the visible branch is what made a cleanup impossible to add without restructuring: it skipped the return value in exactly the case the cleanup exists for. Test: an offcanvas rendered visible and then unmounted leaves no overflow or padding-right on the body. Verified red beforehand. --- .../src/components/offcanvas/COffcanvas.tsx | 12 ++++++++---- .../offcanvas/__tests__/COffcanvas.spec.tsx | 11 +++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/coreui-react/src/components/offcanvas/COffcanvas.tsx b/packages/coreui-react/src/components/offcanvas/COffcanvas.tsx index 12647cec..69f98aef 100644 --- a/packages/coreui-react/src/components/offcanvas/COffcanvas.tsx +++ b/packages/coreui-react/src/components/offcanvas/COffcanvas.tsx @@ -89,13 +89,17 @@ export const COffcanvas = forwardRef( if (_visible && !scroll) { document.body.style.overflow = 'hidden' document.body.style.paddingRight = '0px' - return - } - - if (!scroll) { + } else if (!scroll) { document.body.style.removeProperty('overflow') document.body.style.removeProperty('padding-right') } + + return () => { + if (!scroll) { + document.body.style.removeProperty('overflow') + document.body.style.removeProperty('padding-right') + } + } }, [_visible]) const handleDismiss = () => { diff --git a/packages/coreui-react/src/components/offcanvas/__tests__/COffcanvas.spec.tsx b/packages/coreui-react/src/components/offcanvas/__tests__/COffcanvas.spec.tsx index ea7c9082..a189d3fb 100644 --- a/packages/coreui-react/src/components/offcanvas/__tests__/COffcanvas.spec.tsx +++ b/packages/coreui-react/src/components/offcanvas/__tests__/COffcanvas.spec.tsx @@ -128,3 +128,14 @@ test('COffcanvas customize and event on keypress', async () => { vi.useRealTimers() }) + +test('COffcanvas restores body scroll when unmounted while visible', () => { + const { unmount } = render() + + expect(document.body.style.overflow).toBe('hidden') + + unmount() + + expect(document.body.style.overflow).toBe('') + expect(document.body.style.paddingRight).toBe('') +})