+
@@ -141,6 +145,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 'docc-render/utils/scroll-lock';
const { PreviewState } = QuickNavigationPreview.constants;
@@ -174,6 +179,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 7b42f3e3f..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
@@ -79,12 +81,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(`[${SCROLL_LOCK_DISABLE_ATTR}]`) || 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/components/Navigator/QuickNavigationModal.spec.js b/tests/unit/components/Navigator/QuickNavigationModal.spec.js
index 22e98f930..25ab2c91d 100644
--- a/tests/unit/components/Navigator/QuickNavigationModal.spec.js
+++ b/tests/unit/components/Navigator/QuickNavigationModal.spec.js
@@ -16,6 +16,7 @@ import QuickNavigationHighlighter from '@/components/Navigator/QuickNavigationHi
import QuickNavigationModal from '@/components/Navigator/QuickNavigationModal.vue';
import QuickNavigationPreview from '@/components/Navigator/QuickNavigationPreview.vue';
import Reference from '@/components/ContentNode/Reference.vue';
+import { SCROLL_LOCK_DISABLE_ATTR } from '@/utils/scroll-lock';
import { flushPromises } from '../../../../test-utils';
jest.mock('@/utils/data');
@@ -165,6 +166,7 @@ describe('QuickNavigationModal', () => {
expect(wrapper.vm.debouncedInput).toBe(inputValue);
expect(wrapper.findAll('.quick-navigation__symbol-match').length).toBe(filteredSymbols.length);
expect(wrapper.find('.no-results').exists()).toBe(false);
+ expect(wrapper.find('.quick-navigation__refs').attributes(SCROLL_LOCK_DISABLE_ATTR)).toBeTruthy();
});
it('it renders the `no results found` string when no symbols are found given an input', () => {
@@ -375,6 +377,7 @@ describe('QuickNavigationModal', () => {
const preview = wrapper.find(QuickNavigationPreview);
expect(preview.exists()).toBe(true);
expect(preview.props('state')).toBe(PreviewState.loading);
+ expect(preview.attributes(SCROLL_LOCK_DISABLE_ATTR)).toBeTruthy();
});
it('renders with a successful state and data when data is loaded', async () => {
diff --git a/tests/unit/utils/scroll-lock.spec.js b/tests/unit/utils/scroll-lock.spec.js
index bdf15b481..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;
@@ -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(`[${SCROLL_LOCK_DISABLE_ATTR}]`);
// 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();