Skip to content

Commit ab180cf

Browse files
authored
Merge pull request #7 from microcipcip/feature/useLocation
Feature/use location
2 parents 4a21698 + ac33118 commit ab180cf

File tree

13 files changed

+399
-5
lines changed

13 files changed

+399
-5
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ Vue.use(VueCompositionAPI);
6868
- [`useIntersection`](./src/components/useIntersection/stories/useIntersection.md) — tracks intersection of target element with an ancestor element.
6969
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-useintersection--demo)
7070
[![Demo](https://img.shields.io/badge/advanced_demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-useintersection--advanced-demo)
71+
- [`useLocation`](./src/components/useLocation/stories/useLocation.md) — tracks bar navigation location state.
72+
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-uselocation--demo)
7173
- [`useMedia`](./src/components/useMedia/stories/useMedia.md) — tracks state of a CSS media query.
7274
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemedia--demo)
7375
[![Demo](https://img.shields.io/badge/advanced_demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemedia--advanced-demo)

src/components/useClickAway/useClickAway.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ const testComponent = () => ({
2828
describe('useClickAway', () => {
2929
it('should call document.addEventListener', async () => {
3030
const addEventListenerSpy = jest.spyOn(document, 'addEventListener')
31-
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener')
3231
expect(addEventListenerSpy).not.toHaveBeenCalled()
32+
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener')
3333
const wrapper = mount(testComponent())
3434
await wrapper.vm.$nextTick()
3535
expect(addEventListenerSpy).toHaveBeenCalled()

src/components/useGeolocation/stories/useGeolocation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ function useGeolocation(
3838

3939
### Returns
4040

41-
- `isTracking: Ref<boolean>` whether the function is tracking the user's location or not
4241
- `geo: Ref<UseGeolocation>` the geolocation object
42+
- `isTracking: Ref<boolean>` whether the function is tracking the user's location or not
4343
- `start: Function` the function used for starting the geolocation tracking
4444
- `stop: Function` the function used for stopping the geolocation tracking
4545

src/components/useIdle/useIdle.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ describe('useIdle', () => {
2525

2626
it('should call document.addEventListener', async () => {
2727
const addEventListenerSpy = jest.spyOn(document, 'addEventListener')
28-
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener')
2928
expect(addEventListenerSpy).not.toHaveBeenCalled()
29+
const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener')
3030
const wrapper = mount(testComponent())
3131
await wrapper.vm.$nextTick()
3232
expect(addEventListenerSpy).toHaveBeenCalledTimes(totEvents)

src/components/useLocation/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './useLocation'
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<template>
2+
<table class="table is-fullwidth">
3+
<thead>
4+
<tr>
5+
<th>Prop</th>
6+
<th>Value</th>
7+
</tr>
8+
</thead>
9+
<tbody>
10+
<tr>
11+
<td>locationState</td>
12+
<td>
13+
<pre>{{ JSON.stringify(locationState, null, 2) }}</pre>
14+
</td>
15+
</tr>
16+
<tr>
17+
<td colspan="2">
18+
<button class="button is-primary" @click="push">
19+
Fire push event
20+
</button>
21+
<button class="button is-info" @click="replace">
22+
Fire replace event
23+
</button>
24+
</td>
25+
</tr>
26+
<tr>
27+
<td colspan="2">
28+
<button class="button is-primary" @click="start" v-if="!isTracking">
29+
Start tracking location
30+
</button>
31+
<button class="button is-danger" @click="stop" v-else>
32+
Stop tracking location
33+
</button>
34+
</td>
35+
</tr>
36+
</tbody>
37+
</table>
38+
</template>
39+
40+
<script lang="ts">
41+
import Vue from 'vue'
42+
import { useLocation } from '@src/vue-use-kit'
43+
44+
export default Vue.extend({
45+
name: 'UseLocationDemo',
46+
setup() {
47+
let count = 0
48+
const url = location.origin + location.pathname + location.search
49+
const push = () => {
50+
count++
51+
history.pushState({ page: count }, '', `${url}&page=${count}`)
52+
}
53+
const replace = () => {
54+
count--
55+
history.replaceState({ page: count }, '', `${url}&page=${count}`)
56+
}
57+
58+
const { locationState, isTracking, start, stop } = useLocation()
59+
return { locationState, isTracking, start, stop, push, replace }
60+
}
61+
})
62+
</script>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# useLocation
2+
3+
Vue function that tracks bar navigation location state.
4+
5+
## Reference
6+
7+
```typescript
8+
interface UseLocationState {
9+
trigger: string
10+
state: any
11+
length: number
12+
hash: string
13+
host: string
14+
hostname: string
15+
href: string
16+
origin: string
17+
pathname: string
18+
port: string
19+
protocol: string
20+
search: string
21+
}
22+
```
23+
24+
```typescript
25+
function useLocation(
26+
runOnMount?: boolean
27+
): {
28+
locationState: Ref<UseLocationState>
29+
isTracking: Ref<boolean>
30+
start: () => void
31+
stop: () => void
32+
}
33+
```
34+
35+
### Parameters
36+
37+
- `runOnMount: boolean` whether to start tracking the bar navigation location state on mount, `true` by default
38+
39+
### Returns
40+
41+
- `locationState: Ref<object>` the location object
42+
- `isTracking: Ref<UseLocationState>` whether the location state is being currently tracked or not
43+
- `start: Function` the function used for starting the location tracking
44+
- `stop: Function` the function used for stopping the location tracking
45+
46+
## Usage
47+
48+
```html
49+
<template>
50+
<div>
51+
<div>
52+
locationState:
53+
<pre>{{ JSON.stringify(locationState, null, 2) }}</pre>
54+
</div>
55+
<div>
56+
<button @click="push">Fire push event</button>
57+
<button @click="replace">Fire replace event</button>
58+
</div>
59+
<div>
60+
<button @click="start" v-if="!isTracking">Start tracking location</button>
61+
<button @click="stop" v-else>Stop tracking location</button>
62+
</div>
63+
</div>
64+
</template>
65+
66+
<script lang="ts">
67+
import Vue from 'vue'
68+
import { useLocation } from 'vue-use-kit'
69+
70+
export default Vue.extend({
71+
name: 'UseLocationDemo',
72+
setup() {
73+
let count = 0
74+
const url = location.origin + location.pathname + location.search
75+
const push = () => {
76+
count++
77+
history.pushState({ page: count }, '', `${url}&page=${count}`)
78+
}
79+
const replace = () => {
80+
count--
81+
history.replaceState({ page: count }, '', `${url}&page=${count}`)
82+
}
83+
const { locationState, isTracking, start, stop } = useLocation()
84+
return { locationState, isTracking, start, stop, push, replace }
85+
}
86+
})
87+
</script>
88+
```
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { storiesOf } from '@storybook/vue'
2+
import path from 'path'
3+
import StoryTitle from '@src/helpers/StoryTitle.vue'
4+
import UseLocationDemo from './UseLocationDemo.vue'
5+
6+
const functionName = 'useLocation'
7+
const functionPath = path.resolve(__dirname, '..')
8+
const notes = require(`./${functionName}.md`).default
9+
10+
const basicDemo = () => ({
11+
components: { StoryTitle, demo: UseLocationDemo },
12+
template: `
13+
<div class="container">
14+
<story-title
15+
function-path="${functionPath}"
16+
source-name="${functionName}"
17+
demo-name="UseLocationDemo.vue"
18+
>
19+
<template v-slot:title></template>
20+
<template v-slot:intro>
21+
<p>
22+
<strong>Try pressing the 'Fire push event' and 'Fire replace event' buttons</strong> to see the state change
23+
on the fly.
24+
</p>
25+
<p>
26+
<strong>Please note that in this demo you won't see the browser's bar navigation change</strong>
27+
because this example is within an iframe. Click the "Open canvas in new tab" button on the
28+
top right hand corner to see the demo without iframe.
29+
</p>
30+
</template>
31+
</story-title>
32+
<demo />
33+
</div>`
34+
})
35+
36+
storiesOf('sensors|useLocation', module)
37+
.addParameters({ notes })
38+
.add('Demo', basicDemo)
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { mount } from '@src/helpers/test'
2+
import { useLocation } from '@src/vue-use-kit'
3+
4+
afterEach(() => {
5+
jest.clearAllMocks()
6+
})
7+
8+
const testComponent = (onMount = true) => ({
9+
template: `
10+
<div>
11+
<div id="isTracking" v-if="isTracking"></div>
12+
<div id="locationState">{{JSON.stringify(locationState)}}</div>
13+
<button id="start" @click="start"></button>
14+
<button id="stop" @click="stop"></button>
15+
</div>
16+
`,
17+
setup() {
18+
const { locationState, isTracking, start, stop } = useLocation(onMount)
19+
return { locationState, isTracking, start, stop }
20+
}
21+
})
22+
23+
describe('useLocation', () => {
24+
const locationStateKeys = [
25+
'trigger',
26+
'state',
27+
'length',
28+
'hash',
29+
'host',
30+
'hostname',
31+
'href',
32+
'origin',
33+
'pathname',
34+
'port',
35+
'protocol',
36+
'search'
37+
]
38+
const events = ['popstate', 'pushstate', 'replacestate']
39+
40+
it('should call popstate, pushstate and replacestate onMounted', async () => {
41+
const addEventListenerSpy = jest.spyOn(window, 'addEventListener')
42+
expect(addEventListenerSpy).not.toHaveBeenCalled()
43+
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener')
44+
const wrapper = mount(testComponent())
45+
await wrapper.vm.$nextTick()
46+
expect(addEventListenerSpy).toHaveBeenCalledTimes(events.length)
47+
events.forEach(event => {
48+
expect(addEventListenerSpy).toBeCalledWith(event, expect.any(Function))
49+
})
50+
51+
// Destroy instance to check if the remove event listener is being called
52+
wrapper.destroy()
53+
expect(removeEventListenerSpy).toHaveBeenCalledTimes(events.length)
54+
events.forEach(event => {
55+
expect(removeEventListenerSpy).toBeCalledWith(event, expect.any(Function))
56+
})
57+
})
58+
59+
it('should call document.addEventListener again when start is called', async () => {
60+
const addEventListenerSpy = jest.spyOn(window, 'addEventListener')
61+
const wrapper = mount(testComponent())
62+
expect(addEventListenerSpy).toHaveBeenCalledTimes(events.length)
63+
wrapper.find('#stop').trigger('click')
64+
65+
// Wait for Vue to append #start in the DOM
66+
await wrapper.vm.$nextTick()
67+
wrapper.find('#start').trigger('click')
68+
expect(addEventListenerSpy).toHaveBeenCalledTimes(events.length * 2)
69+
})
70+
71+
it('should call document.removeEventListener when stop is called', async () => {
72+
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener')
73+
const wrapper = mount(testComponent())
74+
wrapper.find('#stop').trigger('click')
75+
76+
// Wait for Vue to append #start in the DOM
77+
await wrapper.vm.$nextTick()
78+
expect(removeEventListenerSpy).toHaveBeenCalledTimes(events.length)
79+
})
80+
81+
it('should show #isTracking when onMount is true', async () => {
82+
const wrapper = mount(testComponent(true))
83+
await wrapper.vm.$nextTick()
84+
expect(wrapper.find('#isTracking').exists()).toBe(true)
85+
})
86+
87+
it('should not show #isTracking when onMount is false', async () => {
88+
const wrapper = mount(testComponent(false))
89+
await wrapper.vm.$nextTick()
90+
expect(wrapper.find('#isTracking').exists()).toBe(false)
91+
})
92+
93+
it('should display the locationState object', async () => {
94+
const wrapper = mount(testComponent(true))
95+
await wrapper.vm.$nextTick()
96+
locationStateKeys.forEach(locationKey => {
97+
expect(wrapper.text().includes(locationKey)).toBe(true)
98+
})
99+
})
100+
})

0 commit comments

Comments
 (0)