-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(useScroll): Adding useScroll feature
- Loading branch information
1 parent
0c142fa
commit cf14283
Showing
10 changed files
with
334 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './useScroll' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<template> | ||
<div> | ||
<table class="table is-fullwidth"> | ||
<thead> | ||
<tr> | ||
<th>Prop</th> | ||
<th>Value</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>isScrolling</td> | ||
<td>{{ isScrolling }}</td> | ||
</tr> | ||
<tr> | ||
<td>x, y</td> | ||
<td>{{ x }}px - {{ y }}px</td> | ||
</tr> | ||
<tr> | ||
<td colspan="2"> | ||
<button class="button is-primary" @click="start" v-if="!isTracking"> | ||
Enable scroll tracking | ||
</button> | ||
<button class="button is-danger" @click="stop" v-else> | ||
Disable scroll tracking | ||
</button> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
<div class="scrollme" ref="scrollRef"> | ||
<span class="scrollme__msg">Scroll me!</span> | ||
<div class="scrollme__height"></div> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import Vue from 'vue' | ||
import { useScroll } from '@src/vue-use-kit' | ||
import { ref } from '@src/api' | ||
export default Vue.extend({ | ||
name: 'UseScrollDemo', | ||
setup() { | ||
const scrollRef = ref(null) | ||
const { x, y, isScrolling, isTracking, start, stop } = useScroll(scrollRef) | ||
return { scrollRef, x, y, isScrolling, isTracking, start, stop } | ||
} | ||
}) | ||
</script> | ||
|
||
<style scoped> | ||
.scrollme { | ||
position: relative; | ||
overflow-y: scroll; | ||
height: 200px; | ||
background: #f1f1f1; | ||
} | ||
.scrollme__msg { | ||
padding: 10px; | ||
} | ||
.scrollme__height { | ||
width: 2000px; | ||
height: 40000px; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# useScroll | ||
|
||
Vue function that tracks an HTML element's scroll position. | ||
|
||
## Reference | ||
|
||
```typescript | ||
function useScroll( | ||
elRef: Ref<null | HTMLElement | Window>, | ||
ms?: number, | ||
runOnMount?: boolean | ||
): { | ||
x: Ref<number> | ||
y: Ref<number> | ||
isTracking: Ref<boolean> | ||
isScrolling: Ref<boolean> | ||
start: () => void | ||
stop: () => void | ||
} | ||
``` | ||
|
||
### Parameters | ||
|
||
- `elRef: Ref<null | HTMLElement | Window>` target element used for tracking the `x` and `y` scroll position | ||
- `ms: number` how many milliseconds of delay before `isScrolling` goes back to false (basically when user is idle), `150` by default | ||
- `runOnMount: boolean` whether to run the scroll tracking on mount, `true` by default | ||
|
||
### Returns | ||
|
||
- `x: Ref<number>` the `x` scroll position relative to the elRef | ||
- `y: Ref<number>` the `y` scroll position relative to the elRef | ||
- `isScrolling: Ref<boolean>` whether the element is currently being scrolled or not | ||
- `isTracking: Ref<boolean>` whether this function events are running or not | ||
- `start: Function` the function used for starting the scroll tracking | ||
- `stop: Function` the function used for stopping the scroll tracking | ||
|
||
## Usage | ||
|
||
```html | ||
<template> | ||
<div> | ||
<div>x, y: {{ x }}px - {{ y }}px</div> | ||
<div>isScrolling: {{ isScrolling }}</div> | ||
<button @click="start" v-if="!isTracking">Start tracking</button> | ||
<button @click="stop" v-else>Stop tracking</button> | ||
<div ref="scrollRef" class="scrollme"> | ||
<div class="scrollme__height"></div> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import Vue from 'vue' | ||
import { useScroll } from 'vue-use-kit' | ||
import { ref } from '@src/api' | ||
export default Vue.extend({ | ||
name: 'useScrollDemo', | ||
setup() { | ||
const scrollRef = ref(null) | ||
const { x, y, isScrolling, isTracking, start, stop } = useScroll(scrollRef) | ||
return { scrollRef, x, y, isScrolling, isTracking, start, stop } | ||
} | ||
}) | ||
</script> | ||
|
||
<style scoped> | ||
.scrollme { | ||
position: relative; | ||
overflow-y: scroll; | ||
height: 200px; | ||
background: #f1f1f1; | ||
} | ||
.scrollme__height { | ||
width: 2000px; | ||
height: 40000px; | ||
} | ||
</style> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { storiesOf } from '@storybook/vue' | ||
import path from 'path' | ||
import StoryTitle from '@src/helpers/StoryTitle.vue' | ||
import UseScrollDemo from './UseScrollDemo.vue' | ||
|
||
const functionName = 'useScroll' | ||
const functionPath = path.resolve(__dirname, '..') | ||
const notes = require(`./${functionName}.md`).default | ||
|
||
const basicDemo = () => ({ | ||
components: { StoryTitle, demo: UseScrollDemo }, | ||
template: ` | ||
<div class="container"> | ||
<story-title | ||
function-path="${functionPath}" | ||
source-name="${functionName}" | ||
demo-name="UseScrollDemo.vue" | ||
> | ||
<template v-slot:title></template> | ||
<template v-slot:intro></template> | ||
</story-title> | ||
<demo /> | ||
</div>` | ||
}) | ||
|
||
storiesOf('sensors|useScroll', module) | ||
.addParameters({ notes }) | ||
.add('Demo', basicDemo) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { | ||
checkElementExistenceOnMount, | ||
checkOnStartEvents, | ||
checkOnStopEvents, | ||
mount | ||
} from '@src/helpers/test' | ||
import { ref } from '@src/api' | ||
import { useScroll } from '@src/vue-use-kit' | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks() | ||
}) | ||
|
||
const testComponent = (onMount = true) => ({ | ||
template: ` | ||
<div> | ||
<div id="isTracking" v-if="isTracking"></div> | ||
<div id="isScrolling" v-if="isScrolling"></div> | ||
<div id="x" v-if="x === 0">{{ x }}px</div> | ||
<div id="y" v-if="y === 0">{{ y }}px</div> | ||
<button id="start" @click="start"></button> | ||
<button id="stop" @click="stop"></button> | ||
</div> | ||
`, | ||
setup() { | ||
const { x, y, isScrolling, isTracking, start, stop } = useScroll( | ||
ref(window), | ||
150, | ||
onMount | ||
) | ||
return { x, y, isScrolling, isTracking, start, stop } | ||
} | ||
}) | ||
|
||
describe('useScroll', () => { | ||
const events = ['scroll'] | ||
|
||
it('should add events on mounted and remove them on unmounted', async () => { | ||
const addEventListenerSpy = jest.spyOn(window, 'addEventListener') | ||
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener') | ||
const wrapper = mount(testComponent()) | ||
await wrapper.vm.$nextTick() | ||
expect(addEventListenerSpy).toHaveBeenCalledTimes(events.length) | ||
events.forEach(event => { | ||
expect(addEventListenerSpy).toBeCalledWith( | ||
event, | ||
expect.any(Function), | ||
expect.any(Object) | ||
) | ||
}) | ||
|
||
// Destroy instance to check if the remove event listener is being called | ||
wrapper.destroy() | ||
expect(removeEventListenerSpy).toHaveBeenCalledTimes(events.length) | ||
events.forEach(event => { | ||
expect(removeEventListenerSpy).toBeCalledWith(event, expect.any(Function)) | ||
}) | ||
}) | ||
|
||
it('should add events again when start is called', async () => { | ||
await checkOnStartEvents(window, events, testComponent) | ||
}) | ||
|
||
it('should remove events when stop is called', async () => { | ||
await checkOnStopEvents(window, events, testComponent) | ||
}) | ||
|
||
it('should show #isTracking when runOnMount is true', async () => { | ||
await checkElementExistenceOnMount(true, testComponent(true)) | ||
}) | ||
|
||
it('should not show #isTracking when runOnMount is false', async () => { | ||
await checkElementExistenceOnMount(false, testComponent(false)) | ||
}) | ||
|
||
it('should show #x and #y elements and not show #isScrolling', async () => { | ||
const wrapper = mount(testComponent()) | ||
await wrapper.vm.$nextTick() | ||
expect(wrapper.find('#x').exists()).toBe(true) | ||
expect(wrapper.find('#y').exists()).toBe(true) | ||
expect(wrapper.find('#isScrolling').exists()).toBe(false) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { ref, onMounted, onUnmounted, Ref } from '@src/api' | ||
|
||
export function useScroll( | ||
elRef: Ref<null | HTMLElement | Window>, | ||
ms = 150, | ||
runOnMount = true | ||
) { | ||
const isTracking = ref(false) | ||
const isScrolling = ref(false) | ||
|
||
const x = ref(0) | ||
const y = ref(0) | ||
|
||
let scrollingTimeout: any = null | ||
const updateScrollStatus = () => { | ||
isScrolling.value = true | ||
clearTimeout(scrollingTimeout) | ||
scrollingTimeout = setTimeout(() => (isScrolling.value = false), ms) | ||
} | ||
|
||
const updateWindowElement = () => { | ||
x.value = window.pageXOffset | ||
y.value = window.pageYOffset | ||
} | ||
|
||
const updateHTMLElement = () => { | ||
x.value = (elRef.value as HTMLElement).scrollLeft | ||
y.value = (elRef.value as HTMLElement).scrollTop | ||
} | ||
|
||
const updateElScrollPos = () => { | ||
if (!elRef.value) return | ||
elRef.value === window ? updateWindowElement() : updateHTMLElement() | ||
} | ||
|
||
const handleScroll = () => { | ||
updateElScrollPos() | ||
updateScrollStatus() | ||
} | ||
|
||
const start = () => { | ||
if (isTracking.value) return | ||
if (elRef.value) { | ||
elRef.value.addEventListener('scroll', handleScroll, { | ||
capture: false, | ||
passive: true | ||
}) | ||
updateElScrollPos() | ||
} | ||
isTracking.value = true | ||
} | ||
|
||
const stop = () => { | ||
if (!isTracking.value) return | ||
if (elRef.value) elRef.value.removeEventListener('scroll', handleScroll) | ||
isTracking.value = false | ||
} | ||
|
||
onMounted(() => runOnMount && start()) | ||
onUnmounted(stop) | ||
|
||
return { x, y, isTracking, isScrolling, start, stop } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters