Skip to content

Commit 8f3fa45

Browse files
authored
Merge pull request #18 from microcipcip/feature/useWindowSize
feat(useWindowSize): Adding useWindowSize feature
2 parents e1ad6d0 + 177029b commit 8f3fa45

12 files changed

+294
-27
lines changed

Diff for: README.md

+16-14
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ npm install @vue/composition-api
3333
## Setup
3434

3535
```js
36-
import Vue from 'vue';
37-
import VueCompositionAPI from '@vue/composition-api';
38-
Vue.use(VueCompositionAPI);
36+
import Vue from 'vue'
37+
import VueCompositionAPI from '@vue/composition-api'
38+
Vue.use(VueCompositionAPI)
3939
```
4040

4141
## Usage
@@ -46,17 +46,17 @@ Vue.use(VueCompositionAPI);
4646
</template>
4747

4848
<script lang="ts">
49-
import Vue from 'vue'
50-
import { useMedia } from 'vue-use-kit'
51-
52-
export default Vue.extend({
53-
name: 'UseMedia',
54-
setup() {
55-
const query = '(min-width: 1024px)'
56-
const isDesktop = useMedia(query)
57-
return { isDesktop }
58-
}
59-
})
49+
import Vue from 'vue'
50+
import { useMedia } from 'vue-use-kit'
51+
52+
export default Vue.extend({
53+
name: 'UseMedia',
54+
setup() {
55+
const query = '(min-width: 1024px)'
56+
const isDesktop = useMedia(query)
57+
return { isDesktop }
58+
}
59+
})
6060
</script>
6161
```
6262

@@ -94,6 +94,8 @@ export default Vue.extend({
9494
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usescroll--demo)
9595
- [`useSearchParams`](./src/functions/useSearchParams/stories/useSearchParams.md) &mdash; tracks browser's location search params.
9696
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usesearchparams--demo)
97+
- [`useWindowSize`](./src/functions/useWindowSize/stories/useWindowSize.md) &mdash; tracks `Window` scroll position.
98+
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usewindowsize--demo)
9799
- Animations
98100
- [`useInterval`](./src/functions/useInterval/stories/useInterval.md) &mdash; updates `counter` value repeatedly on a fixed time delay.
99101
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/animations-useinterval--demo)

Diff for: src/functions/useSize/stories/UseSizeDemo.vue

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
<template>
22
<div>
3-
<div class="box-actions">
4-
<button class="button is-primary" @click="start" v-if="!isTracking">
5-
Start tracking resize
6-
</button>
7-
<button class="button is-danger" @click="stop" v-else>
8-
Stop tracking resize
9-
</button>
10-
</div>
113
<div class="box" ref="elRef">
124
<div class="box__feedback">
135
{{ Math.ceil(width) }}px - {{ Math.ceil(height) }}px
@@ -16,6 +8,14 @@
168
<div class="box__message">Resize me!</div>
179
</div>
1810
</div>
11+
<div class="box-actions">
12+
<button class="button is-primary" @click="start" v-if="!isTracking">
13+
Start tracking resize
14+
</button>
15+
<button class="button is-danger" @click="stop" v-else>
16+
Stop tracking resize
17+
</button>
18+
</div>
1919
</div>
2020
</template>
2121

Diff for: src/functions/useSize/stories/useSize.story.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ const basicDemo = () => ({
1919
<template v-slot:title></template>
2020
<template v-slot:intro>
2121
<p>
22-
<strong>Try to resize the box below by dragging the handle</strong> in the bottom right hand corner.
22+
<strong>Try to resize the box below by dragging the handle</strong> in the bottom right hand corner
23+
and observe how the <strong>width</strong> and <strong>height</strong> values change on the fly.
2324
</p>
2425
</template>
2526
</story-title>

Diff for: src/functions/useSize/useSize.spec.ts

+45-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,53 @@
1-
// import { mount } from '@src/helpers/test'
2-
// import { useSize } from '@src/vue-use-kit'
1+
import { ref } from '@src/api'
2+
import { mount } from '@src/helpers/test'
3+
import { useSize } from '@src/vue-use-kit'
34

45
afterEach(() => {
56
jest.clearAllMocks()
67
})
78

9+
let observe: any
10+
let unobserve: any
11+
let disconnect: any
12+
beforeEach(() => {
13+
observe = jest.fn()
14+
unobserve = jest.fn()
15+
disconnect = jest.fn()
16+
;(window as any).ResizeObserver = jest.fn(() => ({
17+
observe,
18+
unobserve,
19+
disconnect
20+
}))
21+
})
22+
23+
const testComponent = (onMount = true) => ({
24+
template: `
25+
<div ref="elRef">
26+
<button id="start" @click="start"></button>
27+
<button id="stop" @click="stop"></button>
28+
</div>
29+
`,
30+
setup() {
31+
const elRef = ref(null)
32+
const { start, stop } = useSize(elRef, {}, onMount)
33+
return { start, stop, elRef }
34+
}
35+
})
36+
837
describe('useSize', () => {
9-
it('should do something', () => {
10-
// Add test here
38+
it('should call ResizeObserver on mounted', () => {
39+
expect(observe).toHaveBeenCalledTimes(0)
40+
mount(testComponent())
41+
expect(observe).toHaveBeenCalledTimes(1)
42+
})
43+
44+
it('should call ResizeObserver again when start is called', async () => {
45+
expect(observe).toHaveBeenCalledTimes(0)
46+
const wrapper = mount(testComponent())
47+
expect(observe).toHaveBeenCalledTimes(1)
48+
wrapper.find('#stop').trigger('click')
49+
wrapper.find('#start').trigger('click')
50+
await wrapper.vm.$nextTick()
51+
expect(observe).toHaveBeenCalledTimes(2)
1152
})
1253
})

Diff for: src/functions/useWindowSize/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './useWindowSize'
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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>width, height</td>
12+
<td>{{ width }}px - {{ height }}px</td>
13+
</tr>
14+
<tr>
15+
<td colspan="2">
16+
<button class="button is-primary" @click="start" v-if="!isTracking">
17+
Start tracking window resize
18+
</button>
19+
<button class="button is-danger" @click="stop" v-else>
20+
Stop tracking window resize
21+
</button>
22+
</td>
23+
</tr>
24+
</tbody>
25+
</table>
26+
</template>
27+
28+
<script lang="ts">
29+
import Vue from 'vue'
30+
import { useWindowSize } from '@src/vue-use-kit'
31+
32+
export default Vue.extend({
33+
name: 'UseWindowSizeDemo',
34+
setup() {
35+
const { width, height, isTracking, start, stop } = useWindowSize()
36+
return { width, height, isTracking, start, stop }
37+
}
38+
})
39+
</script>

Diff for: src/functions/useWindowSize/stories/useWindowSize.md

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# useWindowSize
2+
3+
Vue function that tracks `Window` scroll position.
4+
5+
## Reference
6+
7+
```typescript
8+
function useWindowSize(
9+
runOnMount?: boolean
10+
): {
11+
width: Ref<number>
12+
height: Ref<number>
13+
isTracking: Ref<boolean>
14+
start: () => void
15+
stop: () => void
16+
}
17+
```
18+
19+
### Parameters
20+
21+
- `runOnMount: boolean` whether to track the window resize event on mount, `true` by default
22+
23+
### Returns
24+
25+
- `width: Ref<number>` the current window's `width`
26+
- `height: Ref<number>` the current window's `height`
27+
- `isTracking: Ref<boolean>` whether this function observer is running or not
28+
- `start: Function` the function used for start tracking window's resize event
29+
- `stop: Function` the function used for stop tracking window's resize event
30+
31+
## Usage
32+
33+
```html
34+
<template>
35+
<div>
36+
<div>
37+
Window width and height: {{ width }}px {{ height }}px
38+
</div>
39+
<button @click="start" v-if="!isTracking">
40+
Start tracking window resize
41+
</button>
42+
<button @click="stop" v-else>Stop tracking window resize</button>
43+
</div>
44+
</template>
45+
46+
<script lang="ts">
47+
import Vue from 'vue'
48+
import { useWindowSize } from 'vue-use-kit'
49+
50+
export default Vue.extend({
51+
name: 'UseWindowSizeDemo',
52+
setup() {
53+
const { width, height, isTracking, start, stop } = useWindowSize()
54+
return { width, height, isTracking, start, stop }
55+
}
56+
})
57+
</script>
58+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { storiesOf } from '@storybook/vue'
2+
import path from 'path'
3+
import StoryTitle from '@src/helpers/StoryTitle.vue'
4+
import UseWindowSizeDemo from './UseWindowSizeDemo.vue'
5+
6+
const functionName = 'useWindowSize'
7+
const functionPath = path.resolve(__dirname, '..')
8+
const notes = require(`./${functionName}.md`).default
9+
10+
const basicDemo = () => ({
11+
components: { StoryTitle, demo: UseWindowSizeDemo },
12+
template: `
13+
<div class="container">
14+
<story-title
15+
function-path="${functionPath}"
16+
source-name="${functionName}"
17+
demo-name="UseWindowSizeDemo.vue"
18+
>
19+
<template v-slot:title></template>
20+
<template v-slot:intro>
21+
<p>
22+
<strong>Try to resize the window</strong> to see the <strong>width</strong> and <strong>height</strong> values change on the fly.
23+
</p>
24+
</template>
25+
</story-title>
26+
<demo />
27+
</div>`
28+
})
29+
30+
storiesOf('sensors|useWindowSize', module)
31+
.addParameters({ notes })
32+
.add('Demo', basicDemo)

Diff for: src/functions/useWindowSize/useWindowSize.spec.ts

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { useWindowSize } from '@src/vue-use-kit'
2+
import {
3+
checkElementExistenceOnMount,
4+
checkOnMountAndUnmountEvents,
5+
checkOnStartEvents,
6+
checkOnStopEvents,
7+
mount
8+
} from '@src/helpers/test'
9+
10+
afterEach(() => {
11+
jest.clearAllMocks()
12+
})
13+
14+
const testComponent = (onMount = true) => ({
15+
template: `
16+
<div>
17+
<div id="isTracking" v-if="isTracking"></div>
18+
<div id="width" v-if="width">{{width}}</div>
19+
<div id="height" v-if="height">{{height}}</div>
20+
<button id="start" @click="start"></button>
21+
<button id="stop" @click="stop"></button>
22+
</div>
23+
`,
24+
setup() {
25+
const { width, height, isTracking, start, stop } = useWindowSize(onMount)
26+
return { width, height, isTracking, start, stop }
27+
}
28+
})
29+
30+
describe('useWindowSize', () => {
31+
const events = ['resize']
32+
33+
it('should add events on mounted and remove them on unmounted', async () => {
34+
await checkOnMountAndUnmountEvents(window, events, testComponent)
35+
})
36+
37+
it('should add events again when start is called', async () => {
38+
await checkOnStartEvents(window, events, testComponent)
39+
})
40+
41+
it('should remove events when stop is called', async () => {
42+
await checkOnStopEvents(window, events, testComponent)
43+
})
44+
45+
it('should show #isTracking when runOnMount is true', async () => {
46+
await checkElementExistenceOnMount(true, testComponent(true))
47+
})
48+
49+
it('should not show #isTracking when runOnMount is false', async () => {
50+
await checkElementExistenceOnMount(false, testComponent(false))
51+
})
52+
53+
it('should display the width and height values', async () => {
54+
const wrapper = mount(testComponent(true))
55+
await wrapper.vm.$nextTick()
56+
expect(wrapper.find('#width').text()).toEqual(`${window.innerWidth}`)
57+
expect(wrapper.find('#height').text()).toEqual(`${window.innerHeight}`)
58+
})
59+
})

Diff for: src/functions/useWindowSize/useWindowSize.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { ref, onMounted, onUnmounted, Ref } from '@src/api'
2+
import { isClient } from '@src/shared/utils'
3+
4+
export function useWindowSize(runOnMount = true) {
5+
const width = ref(isClient ? window.innerWidth : Infinity)
6+
const height = ref(isClient ? window.innerHeight : Infinity)
7+
const isTracking = ref(false)
8+
9+
const handleWindowResize = () => {
10+
width.value = window.innerWidth
11+
height.value = window.innerHeight
12+
}
13+
14+
const start = () => {
15+
if (isTracking.value) return
16+
window.addEventListener('resize', handleWindowResize)
17+
handleWindowResize()
18+
isTracking.value = true
19+
}
20+
21+
const stop = () => {
22+
if (!isTracking.value) return
23+
window.removeEventListener('resize', handleWindowResize)
24+
isTracking.value = false
25+
}
26+
27+
onMounted(() => runOnMount && start())
28+
onUnmounted(stop)
29+
30+
return { width, height, isTracking, start, stop }
31+
}

Diff for: src/shared/utils.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export const isClient = typeof window === 'object'
2+
13
const checkType = (typeToCheck: any) =>
24
Object.prototype.toString.call(typeToCheck)
35

Diff for: src/vue-use-kit.ts

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export * from './functions/useOrientation'
1818
export * from './functions/useSize'
1919
export * from './functions/useSearchParams'
2020
export * from './functions/useScroll'
21+
export * from './functions/useWindowSize'
2122
// Animations
2223
export * from './functions/useIntervalFn'
2324
export * from './functions/useInterval'

0 commit comments

Comments
 (0)