Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/components/Navigator/QuickNavigationModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@
</p>
</div>
<template v-else>
<div class="quick-navigation__refs">
<div
v-bind="{[SCROLL_LOCK_DISABLE_ATTR]: true}"
class="quick-navigation__refs"
>
<Reference
v-for="(symbol, index) in filteredSymbols"
class="quick-navigation__reference"
Expand Down Expand Up @@ -120,6 +123,7 @@
class="quick-navigation__preview"
:json="previewJSON"
:state="previewState"
v-bind="{[SCROLL_LOCK_DISABLE_ATTR]: true}"
/>
</template>
</div>
Expand All @@ -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;

Expand Down Expand Up @@ -174,6 +179,7 @@ export default {
focusedInput: false,
cachedSymbolResults: {},
previewIsLoadingSlowly: false,
SCROLL_LOCK_DISABLE_ATTR,
};
},
props: {
Expand Down
8 changes: 6 additions & 2 deletions src/utils/scroll-lock.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down
3 changes: 3 additions & 0 deletions tests/unit/components/Navigator/QuickNavigationModal.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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 () => {
Expand Down
22 changes: 21 additions & 1 deletion tests/unit/utils/scroll-lock.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -56,6 +56,9 @@ describe('scroll-lock', () => {
preventDefault,
stopPropagation,
touches: [1],
target: {
closest: jest.fn(),
},
};
// init the scroll lock
scrollLock.lockScroll(container);
Expand All @@ -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
Expand All @@ -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();
Expand Down