Skip to content

Commit

Permalink
feat(useOrientation): Adding useOrientation feature
Browse files Browse the repository at this point in the history
  • Loading branch information
microcipcip committed Mar 18, 2020
1 parent 4788db8 commit 10e2da0
Show file tree
Hide file tree
Showing 14 changed files with 296 additions and 13 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ export default Vue.extend({
[![Demo](https://img.shields.io/badge/demo-馃殌-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemouseelement--demo)
- [`useMouseLeavePage`](./src/functions/useMouseLeavePage/stories/useMouseLeavePage.md) — tracks when mouse leaves page boundaries.
[![Demo](https://img.shields.io/badge/demo-馃殌-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usemouseleavepage--demo)
- [`useOrientation`](./src/functions/useOrientation/stories/useOrientation.md) — tracks state of device's screen orientation.
[![Demo](https://img.shields.io/badge/demo-馃殌-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-useorientation--demo)
- [`useSearchParams`](./src/functions/useSearchParams/stories/useSearchParams.md) — tracks browser's location search params.
[![Demo](https://img.shields.io/badge/demo-馃殌-yellow.svg)](https://microcipcip.github.io/vue-use-kit/?path=/story/sensors-usesearchparams--demo)
- Animations
Expand Down
4 changes: 2 additions & 2 deletions src/functions/useBeforeUnload/useBeforeUnload.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ describe('useBeforeUnload', () => {
})

it('should show #isTracking when runOnMount is true', async () => {
await checkElementExistenceOnMount(true, testComponent)
await checkElementExistenceOnMount(true, testComponent(true))
})

it('should not show #isTracking when runOnMount is false', async () => {
await checkElementExistenceOnMount(false, testComponent)
await checkElementExistenceOnMount(false, testComponent(false))
})
})
4 changes: 2 additions & 2 deletions src/functions/useLocation/useLocation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ describe('useLocation', () => {
})

it('should show #isTracking when runOnMount is true', async () => {
await checkElementExistenceOnMount(true, testComponent)
await checkElementExistenceOnMount(true, testComponent(true))
})

it('should not show #isTracking when runOnMount is false', async () => {
await checkElementExistenceOnMount(false, testComponent)
await checkElementExistenceOnMount(false, testComponent(false))
})

it('should display the locationState object', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/functions/useMediaDevices/useMediaDevices.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ describe('useMediaDevices', () => {
})

it('should show #isTracking when runOnMount is true', async () => {
await checkElementExistenceOnMount(true, testComponent)
await checkElementExistenceOnMount(true, testComponent(true))
})

it('should not show #isTracking when runOnMount is false', async () => {
await checkElementExistenceOnMount(false, testComponent)
await checkElementExistenceOnMount(false, testComponent(false))
})
})
10 changes: 7 additions & 3 deletions src/functions/useMouseLeavePage/useMouseLeavePage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,18 @@ describe('useMouseLeavePage', () => {
})

it('should show #isTracking when runOnMount is true', async () => {
await checkElementExistenceOnMount(true, testComponent)
await checkElementExistenceOnMount(true, testComponent(true))
})

it('should not show #isTracking when runOnMount is false', async () => {
await checkElementExistenceOnMount(false, testComponent)
await checkElementExistenceOnMount(false, testComponent(false))
})

it('should not show #hasLeftPage when runOnMount is false', async () => {
await checkElementExistenceOnMount(false, testComponent, '#hasLeftPage')
await checkElementExistenceOnMount(
false,
testComponent(false),
'#hasLeftPage'
)
})
})
1 change: 1 addition & 0 deletions src/functions/useOrientation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useOrientation'
41 changes: 41 additions & 0 deletions src/functions/useOrientation/stories/UseOrientationDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<template>
<table class="table is-fullwidth">
<thead>
<tr>
<th>Prop</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>orientation</td>
<td>
<pre>{{ JSON.stringify(orientation, null, 2) }}</pre>
</td>
</tr>
<tr>
<td colspan="2">
<button class="button is-primary" @click="start" v-if="!isTracking">
Enable orientation tracking
</button>
<button class="button is-danger" @click="stop" v-else>
Disable orientation tracking
</button>
</td>
</tr>
</tbody>
</table>
</template>

<script lang="ts">
import Vue from 'vue'
import { useOrientation } from '@src/vue-use-kit'
export default Vue.extend({
name: 'UseOrientationDemo',
setup() {
const { orientation, isTracking, start, stop } = useOrientation()
return { orientation, isTracking, start, stop }
}
})
</script>
69 changes: 69 additions & 0 deletions src/functions/useOrientation/stories/useOrientation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# useOrientation

Vue function that tracks state of device's screen orientation.

## Reference

```typescript
interface UseOrientationState {
angle: number;
type: string;
}
```

```typescript
function useOrientation(
initialState?: UseOrientationState,
runOnMount?: boolean
): {
orientation: Ref<{
angle: number;
type: string;
}>;
isTracking: Ref<boolean>;
start: () => void;
stop: () => void;
};
```

### Parameters

- `initialState: UseOrientationState` the initial state to use before the event listener is fired
- `runOnMount: boolean` whether to track orientation on mount, `true` by default

### Returns

- `orientation: Ref<UseOrientationState>`
- `angle: number`: the possible values for the window.orientation angle are: -90, 0, 90, 180.
- `type: string`: the type can be `landscape-primary` or `portrait-primary`
- `isTracking: Ref<boolean>` whether this function events are running or not
- `start: Function` the function used to start tracking the device's screen orientation
- `stop: Function` the function used to stop tracking the device's screen orientation

## Usage

```html
<template>
<div>
<p>
orientation:
<pre>{{ JSON.stringify(orientation, null, 2) }}</pre>
</p>
<button @click="start" v-if="!isTracking">Start tracking</button>
<button @click="stop" v-else>Stop tracking</button>
</div>
</template>

<script lang="ts">
import Vue from 'vue'
import { useOrientation } from 'vue-use-kit'
export default Vue.extend({
name: 'UseOrientationDemo',
setup() {
const { orientation, isTracking, start, stop } = useOrientation()
return { orientation, isTracking, start, stop }
}
})
</script>
```
28 changes: 28 additions & 0 deletions src/functions/useOrientation/stories/useOrientation.story.ts
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 UseOrientationDemo from './UseOrientationDemo.vue'

const functionName = 'useOrientation'
const functionPath = path.resolve(__dirname, '..')
const notes = require(`./${functionName}.md`).default

const basicDemo = () => ({
components: { StoryTitle, demo: UseOrientationDemo },
template: `
<div class="container">
<story-title
function-path="${functionPath}"
source-name="${functionName}"
demo-name="UseOrientationDemo.vue"
>
<template v-slot:title></template>
<template v-slot:intro></template>
</story-title>
<demo />
</div>`
})

storiesOf('sensors|useOrientation', module)
.addParameters({ notes })
.add('Demo', basicDemo)
86 changes: 86 additions & 0 deletions src/functions/useOrientation/useOrientation.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
checkElementExistenceOnMount,
checkOnMountAndUnmountEvents,
checkOnStartEvents,
checkOnStopEvents,
mount
} from '@src/helpers/test'
import { useOrientation } from '@src/vue-use-kit'

const screenOrientationMock = () => ({
angle: 0,
type: 'landscape-primary'
})

beforeEach(() => {
;(window as any).screen = {
orientation: screenOrientationMock()
}
})

afterEach(() => {
jest.clearAllMocks()
})

const defaultState = {
angle: 0,
type: 'landscape-primary'
}

const testComponent = (state = defaultState, onMount = true) => ({
template: `
<div>
<div id="isTracking" v-if="isTracking"></div>
<div id="orientation">{{JSON.stringify(orientation)}}</div>
<button id="start" @click="start"></button>
<button id="stop" @click="stop"></button>
</div>
`,
setup() {
const { orientation, isTracking, start, stop } = useOrientation(
state,
onMount
)
return { orientation, isTracking, start, stop }
}
})

describe('useOrientation', () => {
const events = ['orientationchange']
const orientationKeys = Object.keys(defaultState)
const orientationValues = Object.values(defaultState)

it('should add events on mounted and remove them on unmounted', async () => {
await checkOnMountAndUnmountEvents(window, events, testComponent)
})

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(defaultState, true))
})

it('should not show #isTracking when runOnMount is false', async () => {
await checkElementExistenceOnMount(
false,
testComponent(defaultState, false)
)
})

it('should display the orientation object keys and values', async () => {
const wrapper = mount(testComponent())
await wrapper.vm.$nextTick()
orientationKeys.forEach(orientationKey => {
expect(wrapper.text().includes(orientationKey)).toBe(true)
})
orientationValues.forEach(orientationValue => {
expect(wrapper.text().includes(`${orientationValue}`)).toBe(true)
})
})
})
51 changes: 51 additions & 0 deletions src/functions/useOrientation/useOrientation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { ref, onMounted, onUnmounted, Ref } from '@src/api'

export interface UseOrientationState {
angle: number
type: string
}

const defaultState: UseOrientationState = {
angle: 0,
type: 'landscape-primary'
}

export function useOrientation(
initialState: UseOrientationState = defaultState,
runOnMount = true
) {
const isTracking = ref(false)
const orientation = ref(initialState)

const handleOrientationChange = () => {
if (screen.orientation) {
const { angle, type } = screen.orientation
orientation.value = { angle, type }
} else if (window.orientation) {
orientation.value = {
angle: typeof window.orientation === 'number' ? window.orientation : 0,
type: ''
}
} else {
orientation.value = initialState
}
}

const start = () => {
if (isTracking.value) return
window.addEventListener('orientationchange', handleOrientationChange)
handleOrientationChange()
isTracking.value = true
}

const stop = () => {
if (!isTracking.value) return
window.removeEventListener('orientationchange', handleOrientationChange)
isTracking.value = false
}

onMounted(() => runOnMount && start())
onUnmounted(stop)

return { orientation, isTracking, start, stop }
}
4 changes: 2 additions & 2 deletions src/functions/useSearchParams/useSearchParams.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ describe('useSearchParams', () => {
})

it('should show #isTracking when runOnMount is true', async () => {
await checkElementExistenceOnMount(true, testComponent)
await checkElementExistenceOnMount(true, testComponent(true))
})

it('should not show #isTracking when runOnMount is false', async () => {
await checkElementExistenceOnMount(false, testComponent)
await checkElementExistenceOnMount(false, testComponent(false))
})

it('should display the searchParams object with all parameter keys', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/helpers/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ export const checkOnStopEvents = async (

export const checkElementExistenceOnMount = async (
mountType: boolean,
testComponent: Function,
testComponent: ComponentOptions<Vue>,
elementName = '#isTracking'
) => {
const wrapper = mount(testComponent(mountType))
const wrapper = mount(testComponent)
await wrapper.vm.$nextTick()
expect(wrapper.find(elementName).exists()).toBe(mountType)
}
1 change: 1 addition & 0 deletions src/vue-use-kit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export * from './functions/useMediaDevices'
export * from './functions/useMouse'
export * from './functions/useMouseElement'
export * from './functions/useMouseLeavePage'
export * from './functions/useOrientation'
export * from './functions/useSearchParams'
// Animations
export * from './functions/useIntervalFn'
Expand Down

0 comments on commit 10e2da0

Please sign in to comment.