Skip to content

Commit 33bef80

Browse files
authored
Merge pull request #15 from microcipcip/feature/useOrientation
feat(useOrientation): Adding useOrientation feature
2 parents 4788db8 + 10e2da0 commit 33bef80

File tree

14 files changed

+296
-13
lines changed

14 files changed

+296
-13
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ export default Vue.extend({
8686
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemouseelement--demo)
8787
- [`useMouseLeavePage`](./src/functions/useMouseLeavePage/stories/useMouseLeavePage.md) — tracks when mouse leaves page boundaries.
8888
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemouseleavepage--demo)
89+
- [`useOrientation`](./src/functions/useOrientation/stories/useOrientation.md) — tracks state of device's screen orientation.
90+
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-useorientation--demo)
8991
- [`useSearchParams`](./src/functions/useSearchParams/stories/useSearchParams.md) — tracks browser's location search params.
9092
[![Demo](https://img.shields.io/badge/demo-🚀-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usesearchparams--demo)
9193
- Animations

src/functions/useBeforeUnload/useBeforeUnload.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ describe('useBeforeUnload', () => {
4242
})
4343

4444
it('should show #isTracking when runOnMount is true', async () => {
45-
await checkElementExistenceOnMount(true, testComponent)
45+
await checkElementExistenceOnMount(true, testComponent(true))
4646
})
4747

4848
it('should not show #isTracking when runOnMount is false', async () => {
49-
await checkElementExistenceOnMount(false, testComponent)
49+
await checkElementExistenceOnMount(false, testComponent(false))
5050
})
5151
})

src/functions/useLocation/useLocation.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ describe('useLocation', () => {
5757
})
5858

5959
it('should show #isTracking when runOnMount is true', async () => {
60-
await checkElementExistenceOnMount(true, testComponent)
60+
await checkElementExistenceOnMount(true, testComponent(true))
6161
})
6262

6363
it('should not show #isTracking when runOnMount is false', async () => {
64-
await checkElementExistenceOnMount(false, testComponent)
64+
await checkElementExistenceOnMount(false, testComponent(false))
6565
})
6666

6767
it('should display the locationState object', async () => {

src/functions/useMediaDevices/useMediaDevices.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ describe('useMediaDevices', () => {
6262
})
6363

6464
it('should show #isTracking when runOnMount is true', async () => {
65-
await checkElementExistenceOnMount(true, testComponent)
65+
await checkElementExistenceOnMount(true, testComponent(true))
6666
})
6767

6868
it('should not show #isTracking when runOnMount is false', async () => {
69-
await checkElementExistenceOnMount(false, testComponent)
69+
await checkElementExistenceOnMount(false, testComponent(false))
7070
})
7171
})

src/functions/useMouseLeavePage/useMouseLeavePage.spec.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,18 @@ describe('useMouseLeavePage', () => {
4141
})
4242

4343
it('should show #isTracking when runOnMount is true', async () => {
44-
await checkElementExistenceOnMount(true, testComponent)
44+
await checkElementExistenceOnMount(true, testComponent(true))
4545
})
4646

4747
it('should not show #isTracking when runOnMount is false', async () => {
48-
await checkElementExistenceOnMount(false, testComponent)
48+
await checkElementExistenceOnMount(false, testComponent(false))
4949
})
5050

5151
it('should not show #hasLeftPage when runOnMount is false', async () => {
52-
await checkElementExistenceOnMount(false, testComponent, '#hasLeftPage')
52+
await checkElementExistenceOnMount(
53+
false,
54+
testComponent(false),
55+
'#hasLeftPage'
56+
)
5357
})
5458
})

src/functions/useOrientation/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './useOrientation'
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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>orientation</td>
12+
<td>
13+
<pre>{{ JSON.stringify(orientation, null, 2) }}</pre>
14+
</td>
15+
</tr>
16+
<tr>
17+
<td colspan="2">
18+
<button class="button is-primary" @click="start" v-if="!isTracking">
19+
Enable orientation tracking
20+
</button>
21+
<button class="button is-danger" @click="stop" v-else>
22+
Disable orientation tracking
23+
</button>
24+
</td>
25+
</tr>
26+
</tbody>
27+
</table>
28+
</template>
29+
30+
<script lang="ts">
31+
import Vue from 'vue'
32+
import { useOrientation } from '@src/vue-use-kit'
33+
34+
export default Vue.extend({
35+
name: 'UseOrientationDemo',
36+
setup() {
37+
const { orientation, isTracking, start, stop } = useOrientation()
38+
return { orientation, isTracking, start, stop }
39+
}
40+
})
41+
</script>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# useOrientation
2+
3+
Vue function that tracks state of device's screen orientation.
4+
5+
## Reference
6+
7+
```typescript
8+
interface UseOrientationState {
9+
angle: number;
10+
type: string;
11+
}
12+
```
13+
14+
```typescript
15+
function useOrientation(
16+
initialState?: UseOrientationState,
17+
runOnMount?: boolean
18+
): {
19+
orientation: Ref<{
20+
angle: number;
21+
type: string;
22+
}>;
23+
isTracking: Ref<boolean>;
24+
start: () => void;
25+
stop: () => void;
26+
};
27+
```
28+
29+
### Parameters
30+
31+
- `initialState: UseOrientationState` the initial state to use before the event listener is fired
32+
- `runOnMount: boolean` whether to track orientation on mount, `true` by default
33+
34+
### Returns
35+
36+
- `orientation: Ref<UseOrientationState>`
37+
- `angle: number`: the possible values for the window.orientation angle are: -90, 0, 90, 180.
38+
- `type: string`: the type can be `landscape-primary` or `portrait-primary`
39+
- `isTracking: Ref<boolean>` whether this function events are running or not
40+
- `start: Function` the function used to start tracking the device's screen orientation
41+
- `stop: Function` the function used to stop tracking the device's screen orientation
42+
43+
## Usage
44+
45+
```html
46+
<template>
47+
<div>
48+
<p>
49+
orientation:
50+
<pre>{{ JSON.stringify(orientation, null, 2) }}</pre>
51+
</p>
52+
<button @click="start" v-if="!isTracking">Start tracking</button>
53+
<button @click="stop" v-else>Stop tracking</button>
54+
</div>
55+
</template>
56+
57+
<script lang="ts">
58+
import Vue from 'vue'
59+
import { useOrientation } from 'vue-use-kit'
60+
61+
export default Vue.extend({
62+
name: 'UseOrientationDemo',
63+
setup() {
64+
const { orientation, isTracking, start, stop } = useOrientation()
65+
return { orientation, isTracking, start, stop }
66+
}
67+
})
68+
</script>
69+
```
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { storiesOf } from '@storybook/vue'
2+
import path from 'path'
3+
import StoryTitle from '@src/helpers/StoryTitle.vue'
4+
import UseOrientationDemo from './UseOrientationDemo.vue'
5+
6+
const functionName = 'useOrientation'
7+
const functionPath = path.resolve(__dirname, '..')
8+
const notes = require(`./${functionName}.md`).default
9+
10+
const basicDemo = () => ({
11+
components: { StoryTitle, demo: UseOrientationDemo },
12+
template: `
13+
<div class="container">
14+
<story-title
15+
function-path="${functionPath}"
16+
source-name="${functionName}"
17+
demo-name="UseOrientationDemo.vue"
18+
>
19+
<template v-slot:title></template>
20+
<template v-slot:intro></template>
21+
</story-title>
22+
<demo />
23+
</div>`
24+
})
25+
26+
storiesOf('sensors|useOrientation', module)
27+
.addParameters({ notes })
28+
.add('Demo', basicDemo)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {
2+
checkElementExistenceOnMount,
3+
checkOnMountAndUnmountEvents,
4+
checkOnStartEvents,
5+
checkOnStopEvents,
6+
mount
7+
} from '@src/helpers/test'
8+
import { useOrientation } from '@src/vue-use-kit'
9+
10+
const screenOrientationMock = () => ({
11+
angle: 0,
12+
type: 'landscape-primary'
13+
})
14+
15+
beforeEach(() => {
16+
;(window as any).screen = {
17+
orientation: screenOrientationMock()
18+
}
19+
})
20+
21+
afterEach(() => {
22+
jest.clearAllMocks()
23+
})
24+
25+
const defaultState = {
26+
angle: 0,
27+
type: 'landscape-primary'
28+
}
29+
30+
const testComponent = (state = defaultState, onMount = true) => ({
31+
template: `
32+
<div>
33+
<div id="isTracking" v-if="isTracking"></div>
34+
<div id="orientation">{{JSON.stringify(orientation)}}</div>
35+
<button id="start" @click="start"></button>
36+
<button id="stop" @click="stop"></button>
37+
</div>
38+
`,
39+
setup() {
40+
const { orientation, isTracking, start, stop } = useOrientation(
41+
state,
42+
onMount
43+
)
44+
return { orientation, isTracking, start, stop }
45+
}
46+
})
47+
48+
describe('useOrientation', () => {
49+
const events = ['orientationchange']
50+
const orientationKeys = Object.keys(defaultState)
51+
const orientationValues = Object.values(defaultState)
52+
53+
it('should add events on mounted and remove them on unmounted', async () => {
54+
await checkOnMountAndUnmountEvents(window, events, testComponent)
55+
})
56+
57+
it('should add events again when start is called', async () => {
58+
await checkOnStartEvents(window, events, testComponent)
59+
})
60+
61+
it('should remove events when stop is called', async () => {
62+
await checkOnStopEvents(window, events, testComponent)
63+
})
64+
65+
it('should show #isTracking when runOnMount is true', async () => {
66+
await checkElementExistenceOnMount(true, testComponent(defaultState, true))
67+
})
68+
69+
it('should not show #isTracking when runOnMount is false', async () => {
70+
await checkElementExistenceOnMount(
71+
false,
72+
testComponent(defaultState, false)
73+
)
74+
})
75+
76+
it('should display the orientation object keys and values', async () => {
77+
const wrapper = mount(testComponent())
78+
await wrapper.vm.$nextTick()
79+
orientationKeys.forEach(orientationKey => {
80+
expect(wrapper.text().includes(orientationKey)).toBe(true)
81+
})
82+
orientationValues.forEach(orientationValue => {
83+
expect(wrapper.text().includes(`${orientationValue}`)).toBe(true)
84+
})
85+
})
86+
})
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { ref, onMounted, onUnmounted, Ref } from '@src/api'
2+
3+
export interface UseOrientationState {
4+
angle: number
5+
type: string
6+
}
7+
8+
const defaultState: UseOrientationState = {
9+
angle: 0,
10+
type: 'landscape-primary'
11+
}
12+
13+
export function useOrientation(
14+
initialState: UseOrientationState = defaultState,
15+
runOnMount = true
16+
) {
17+
const isTracking = ref(false)
18+
const orientation = ref(initialState)
19+
20+
const handleOrientationChange = () => {
21+
if (screen.orientation) {
22+
const { angle, type } = screen.orientation
23+
orientation.value = { angle, type }
24+
} else if (window.orientation) {
25+
orientation.value = {
26+
angle: typeof window.orientation === 'number' ? window.orientation : 0,
27+
type: ''
28+
}
29+
} else {
30+
orientation.value = initialState
31+
}
32+
}
33+
34+
const start = () => {
35+
if (isTracking.value) return
36+
window.addEventListener('orientationchange', handleOrientationChange)
37+
handleOrientationChange()
38+
isTracking.value = true
39+
}
40+
41+
const stop = () => {
42+
if (!isTracking.value) return
43+
window.removeEventListener('orientationchange', handleOrientationChange)
44+
isTracking.value = false
45+
}
46+
47+
onMounted(() => runOnMount && start())
48+
onUnmounted(stop)
49+
50+
return { orientation, isTracking, start, stop }
51+
}

src/functions/useSearchParams/useSearchParams.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ describe('useSearchParams', () => {
5151
})
5252

5353
it('should show #isTracking when runOnMount is true', async () => {
54-
await checkElementExistenceOnMount(true, testComponent)
54+
await checkElementExistenceOnMount(true, testComponent(true))
5555
})
5656

5757
it('should not show #isTracking when runOnMount is false', async () => {
58-
await checkElementExistenceOnMount(false, testComponent)
58+
await checkElementExistenceOnMount(false, testComponent(false))
5959
})
6060

6161
it('should display the searchParams object with all parameter keys', async () => {

src/helpers/test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ export const checkOnStopEvents = async (
7575

7676
export const checkElementExistenceOnMount = async (
7777
mountType: boolean,
78-
testComponent: Function,
78+
testComponent: ComponentOptions<Vue>,
7979
elementName = '#isTracking'
8080
) => {
81-
const wrapper = mount(testComponent(mountType))
81+
const wrapper = mount(testComponent)
8282
await wrapper.vm.$nextTick()
8383
expect(wrapper.find(elementName).exists()).toBe(mountType)
8484
}

src/vue-use-kit.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export * from './functions/useMediaDevices'
1414
export * from './functions/useMouse'
1515
export * from './functions/useMouseElement'
1616
export * from './functions/useMouseLeavePage'
17+
export * from './functions/useOrientation'
1718
export * from './functions/useSearchParams'
1819
// Animations
1920
export * from './functions/useIntervalFn'

0 commit comments

Comments
 (0)