From db2d6715479b64fa6c2e84ed38f9190948bae86e Mon Sep 17 00:00:00 2001 From: Dobromir Hristov Date: Thu, 4 May 2023 14:49:30 +0300 Subject: [PATCH 1/3] feat: allow disabling scroll-lock for elements inside a scrolllock container --- .../Navigator/QuickNavigationModal.vue | 2 ++ src/utils/scroll-lock.js | 6 ++++-- tests/unit/utils/scroll-lock.spec.js | 20 +++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/components/Navigator/QuickNavigationModal.vue b/src/components/Navigator/QuickNavigationModal.vue index f447bf3f1..b68868088 100644 --- a/src/components/Navigator/QuickNavigationModal.vue +++ b/src/components/Navigator/QuickNavigationModal.vue @@ -50,6 +50,7 @@
diff --git a/src/utils/scroll-lock.js b/src/utils/scroll-lock.js index 7b42f3e3f..83354686a 100644 --- a/src/utils/scroll-lock.js +++ b/src/utils/scroll-lock.js @@ -79,12 +79,14 @@ function advancedUnlock(targetElement) { */ function handleScroll(event, targetElement) { const clientY = event.targetTouches[0].clientY - initialClientY; - if (targetElement.scrollTop === 0 && clientY > 0) { + // check if any parent has a scroll-lock disable, if not use the targetElement + const target = event.target.closest('[data-scroll-lock-disable]') || targetElement; + if (target.scrollTop === 0 && clientY > 0) { // element is at the top of its scroll. return preventDefault(event); } - if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) { + if (isTargetElementTotallyScrolled(target) && clientY < 0) { // element is at the bottom of its scroll. return preventDefault(event); } diff --git a/tests/unit/utils/scroll-lock.spec.js b/tests/unit/utils/scroll-lock.spec.js index bdf15b481..d979e7b2d 100644 --- a/tests/unit/utils/scroll-lock.spec.js +++ b/tests/unit/utils/scroll-lock.spec.js @@ -56,6 +56,9 @@ describe('scroll-lock', () => { preventDefault, stopPropagation, touches: [1], + target: { + closest: jest.fn(), + }, }; // init the scroll lock scrollLock.lockScroll(container); @@ -67,6 +70,8 @@ describe('scroll-lock', () => { container.ontouchmove(touchMoveEvent); expect(preventDefault).toHaveBeenCalledTimes(1); expect(stopPropagation).toHaveBeenCalledTimes(0); + expect(touchMoveEvent.target.closest).toHaveBeenCalledTimes(1); + expect(touchMoveEvent.target.closest).toHaveBeenCalledWith('[data-scroll-lock-disable]'); // simulate scroll middle // simulate we have enough to scroll @@ -81,6 +86,21 @@ describe('scroll-lock', () => { expect(preventDefault).toHaveBeenCalledTimes(2); expect(stopPropagation).toHaveBeenCalledTimes(1); + // simulate there is a scroll-lock-disable target + container.ontouchmove({ + ...touchMoveEvent, + targetTouches: [{ clientY: -10 }], + target: { + closest: jest.fn().mockReturnValue({ + ...container, + clientHeight: 150, + }), + }, + }); + // assert scrolling was allowed + expect(preventDefault).toHaveBeenCalledTimes(2); + expect(stopPropagation).toHaveBeenCalledTimes(2); + scrollLock.unlockScroll(container); expect(container.ontouchmove).toBeFalsy(); expect(container.ontouchstart).toBeFalsy(); From 7b0918f8d77dcd39439b4ab0953aff149c82f947 Mon Sep 17 00:00:00 2001 From: Dobromir Hristov Date: Thu, 4 May 2023 18:15:25 +0300 Subject: [PATCH 2/3] refactor: extract attr into a constant --- src/components/Navigator/QuickNavigationModal.vue | 6 ++++-- src/utils/scroll-lock.js | 4 +++- tests/unit/utils/scroll-lock.spec.js | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/Navigator/QuickNavigationModal.vue b/src/components/Navigator/QuickNavigationModal.vue index b68868088..d7e6a10db 100644 --- a/src/components/Navigator/QuickNavigationModal.vue +++ b/src/components/Navigator/QuickNavigationModal.vue @@ -50,7 +50,7 @@
@@ -143,6 +143,7 @@ import keyboardNavigation from 'docc-render/mixins/keyboardNavigation'; import LRUMap from 'docc-render/utils/lru-map'; import { convertChildrenArrayToObject, getParents } from 'docc-render/utils/navigatorData'; import { fetchDataForPreview } from 'docc-render/utils/data'; +import { SCROLL_LOCK_DISABLE_ATTR } from '@/utils/scroll-lock'; const { PreviewState } = QuickNavigationPreview.constants; @@ -176,6 +177,7 @@ export default { focusedInput: false, cachedSymbolResults: {}, previewIsLoadingSlowly: false, + SCROLL_LOCK_DISABLE_ATTR, }; }, props: { diff --git a/src/utils/scroll-lock.js b/src/utils/scroll-lock.js index 83354686a..a9ac31acd 100644 --- a/src/utils/scroll-lock.js +++ b/src/utils/scroll-lock.js @@ -11,6 +11,8 @@ let isLocked = false; let initialClientY = -1; let scrolledClientY = 0; +// Adds this attribute to an inner scrollable element to allow it to scroll +export const SCROLL_LOCK_DISABLE_ATTR = 'data-scroll-lock-disable'; const isIosDevice = () => window.navigator && window.navigator.platform @@ -80,7 +82,7 @@ function advancedUnlock(targetElement) { function handleScroll(event, targetElement) { const clientY = event.targetTouches[0].clientY - initialClientY; // check if any parent has a scroll-lock disable, if not use the targetElement - const target = event.target.closest('[data-scroll-lock-disable]') || targetElement; + const target = event.target.closest(`[${SCROLL_LOCK_DISABLE_ATTR}]`) || targetElement; if (target.scrollTop === 0 && clientY > 0) { // element is at the top of its scroll. return preventDefault(event); diff --git a/tests/unit/utils/scroll-lock.spec.js b/tests/unit/utils/scroll-lock.spec.js index d979e7b2d..3a67eede6 100644 --- a/tests/unit/utils/scroll-lock.spec.js +++ b/tests/unit/utils/scroll-lock.spec.js @@ -8,7 +8,7 @@ * See https://swift.org/CONTRIBUTORS.txt for Swift project authors */ -import scrollLock from 'docc-render/utils/scroll-lock'; +import scrollLock, { SCROLL_LOCK_DISABLE_ATTR } from 'docc-render/utils/scroll-lock'; import { createEvent, parseHTMLString } from '../../../test-utils'; const { platform } = window.navigator; @@ -71,7 +71,7 @@ describe('scroll-lock', () => { expect(preventDefault).toHaveBeenCalledTimes(1); expect(stopPropagation).toHaveBeenCalledTimes(0); expect(touchMoveEvent.target.closest).toHaveBeenCalledTimes(1); - expect(touchMoveEvent.target.closest).toHaveBeenCalledWith('[data-scroll-lock-disable]'); + expect(touchMoveEvent.target.closest).toHaveBeenCalledWith(`[${SCROLL_LOCK_DISABLE_ATTR}]`); // simulate scroll middle // simulate we have enough to scroll From b55cf3258dd1c846e32ee51a43ccce7fff5f6cc5 Mon Sep 17 00:00:00 2001 From: Dobromir Hristov Date: Fri, 5 May 2023 17:59:42 +0300 Subject: [PATCH 3/3] fix: merge conflict --- src/components/Navigator/QuickNavigationModal.vue | 6 ++++-- .../unit/components/Navigator/QuickNavigationModal.spec.js | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/Navigator/QuickNavigationModal.vue b/src/components/Navigator/QuickNavigationModal.vue index 8ee76406c..735bc30df 100644 --- a/src/components/Navigator/QuickNavigationModal.vue +++ b/src/components/Navigator/QuickNavigationModal.vue @@ -50,7 +50,6 @@