-
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(useMediaDevices): Adding the function useMediaDevices
- Loading branch information
1 parent
9185917
commit 1717c6a
Showing
8 changed files
with
315 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './useMediaDevices' |
51 changes: 51 additions & 0 deletions
51
src/components/useMediaDevices/stories/UseMediaDevicesDemo.vue
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,51 @@ | ||
<template> | ||
<table class="table is-fullwidth"> | ||
<thead> | ||
<tr> | ||
<th>Prop</th> | ||
<th>Value</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>devicesState</td> | ||
<td> | ||
<pre>{{ JSON.stringify(devicesState, null, 2) }}</pre> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>isTracked</td> | ||
<td>{{ isTracked }}</td> | ||
</tr> | ||
<tr> | ||
<td colspan="2"> | ||
<button class="button is-primary" @click="start" v-if="!isTracking"> | ||
Start tracking media devices | ||
</button> | ||
<button class="button is-danger" @click="stop" v-else> | ||
Stop tracking media devices | ||
</button> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import Vue from 'vue' | ||
import { useMediaDevices } from '@src/vue-use-kit' | ||
export default Vue.extend({ | ||
name: 'UseMediaDevicesDemo', | ||
setup() { | ||
const { | ||
devicesState, | ||
isTracking, | ||
isTracked, | ||
start, | ||
stop | ||
} = useMediaDevices(false) | ||
return { devicesState, isTracking, isTracked, start, stop } | ||
} | ||
}) | ||
</script> |
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,71 @@ | ||
# useMediaDevices | ||
|
||
Vue function that tracks connected hardware devices. | ||
|
||
## Reference | ||
|
||
```typescript | ||
interface UseMediaDevicesState { | ||
deviceId: string | ||
groupId: string | ||
kind: string | ||
label: string | ||
} | ||
|
||
function useMediaDevices( | ||
runOnMount?: boolean | ||
): { | ||
devicesState: Ref<UseMediaDevicesState[]> | ||
isTracking: Ref<boolean> | ||
isTracked: Ref<boolean> | ||
start: () => void | ||
stop: () => void | ||
} | ||
``` | ||
|
||
### Parameters | ||
|
||
- `runOnMount: boolean` whether to run the connected media devices tracking on mount, `true` by default | ||
|
||
### Returns | ||
|
||
- `devicesState: Ref<UseMediaDevicesState[]>` the list of connected media devices | ||
- `isTracking: Ref<boolean>` whether the function is tracking the connected media devices or not | ||
- `isTracked: Ref<boolean>` whether the connected devices have been successfully tracked | ||
- `start: Function` the function used to start tracking the connected media devices | ||
- `stop: Function` the function used to stop tracking the connected media devices | ||
|
||
## Usage | ||
|
||
```html | ||
<template> | ||
<div> | ||
<div> | ||
devicesState: | ||
<pre>{{ JSON.stringify(devicesState, null, 2) }}</pre> | ||
</div> | ||
<div> | ||
isTracked: {{ isTracked }} | ||
</div> | ||
<div> | ||
<button @click="start" v-if="!isTracking"> | ||
Start tracking media devices | ||
</button> | ||
<button @click="stop" v-else>Stop tracking media devices</button> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import Vue from 'vue' | ||
import { useMediaDevices } from 'vue-use-kit' | ||
export default Vue.extend({ | ||
name: 'UseMediaDevicesDemo', | ||
setup() { | ||
const { devicesState, isTracked, start, stop } = useMediaDevices(false) | ||
return { devicesState, isTracked, start, stop } | ||
} | ||
}) | ||
</script> | ||
``` |
28 changes: 28 additions & 0 deletions
28
src/components/useMediaDevices/stories/useMediaDevices.story.ts
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 UseMediaDevicesDemo from './UseMediaDevicesDemo.vue' | ||
|
||
const functionName = 'useMediaDevices' | ||
const functionPath = path.resolve(__dirname, '..') | ||
const notes = require(`./${functionName}.md`).default | ||
|
||
const basicDemo = () => ({ | ||
components: { StoryTitle, demo: UseMediaDevicesDemo }, | ||
template: ` | ||
<div class="container"> | ||
<story-title | ||
function-path="${functionPath}" | ||
source-name="${functionName}" | ||
demo-name="UseMediaDevicesDemo.vue" | ||
> | ||
<template v-slot:title></template> | ||
<template v-slot:intro></template> | ||
</story-title> | ||
<demo /> | ||
</div>` | ||
}) | ||
|
||
storiesOf('sensors|useMediaDevices', 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,101 @@ | ||
import { mount } from '@src/helpers/test' | ||
import { useMediaDevices } from '@src/vue-use-kit' | ||
|
||
const mediaDeviceInfo = { | ||
deviceId: 'string', | ||
groupId: 'string', | ||
kind: 'string', | ||
label: 'string' | ||
} | ||
const mediaDevices = [mediaDeviceInfo, mediaDeviceInfo] | ||
let enumerateDevices: any | ||
beforeEach(() => { | ||
enumerateDevices = () => Promise.resolve(mediaDevices) | ||
;(navigator as any).mediaDevices = { | ||
enumerateDevices, | ||
addEventListener: jest.fn(), | ||
removeEventListener: jest.fn() | ||
} | ||
}) | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks() | ||
}) | ||
|
||
const testComponent = (onMount = true) => ({ | ||
template: ` | ||
<div> | ||
<div id="isTracking" v-if="isTracking"></div> | ||
<div id="devicesState">{{JSON.stringify(devicesState)}}</div> | ||
<button id="start" @click="start"></button> | ||
<button id="stop" @click="stop"></button> | ||
</div> | ||
`, | ||
setup() { | ||
const { devicesState, isTracking, start, stop } = useMediaDevices(onMount) | ||
return { devicesState, isTracking, start, stop } | ||
} | ||
}) | ||
|
||
describe('useMediaDevices', () => { | ||
const event = 'devicechange' | ||
it('should call devicechange onMounted', async () => { | ||
const addEventListenerSpy = jest.spyOn( | ||
navigator.mediaDevices, | ||
'addEventListener' | ||
) | ||
const removeEventListenerSpy = jest.spyOn( | ||
navigator.mediaDevices, | ||
'removeEventListener' | ||
) | ||
const wrapper = mount(testComponent()) | ||
await wrapper.vm.$nextTick() | ||
expect(addEventListenerSpy).toHaveBeenCalledTimes(1) | ||
expect(addEventListenerSpy).toBeCalledWith(event, expect.any(Function)) | ||
|
||
// Destroy instance to check if the remove event listener is being called | ||
wrapper.destroy() | ||
expect(removeEventListenerSpy).toHaveBeenCalledTimes(1) | ||
expect(removeEventListenerSpy).toBeCalledWith(event, expect.any(Function)) | ||
}) | ||
|
||
it('should call document.addEventListener again when start is called', async () => { | ||
const addEventListenerSpy = jest.spyOn( | ||
navigator.mediaDevices, | ||
'addEventListener' | ||
) | ||
const wrapper = mount(testComponent()) | ||
expect(addEventListenerSpy).toHaveBeenCalledTimes(1) | ||
wrapper.find('#stop').trigger('click') | ||
|
||
// Wait for Vue to append #start in the DOM | ||
await wrapper.vm.$nextTick() | ||
wrapper.find('#start').trigger('click') | ||
expect(addEventListenerSpy).toHaveBeenCalledTimes(1 * 2) | ||
}) | ||
|
||
it('should call document.removeEventListener when stop is called', async () => { | ||
const removeEventListenerSpy = jest.spyOn( | ||
navigator.mediaDevices, | ||
'removeEventListener' | ||
) | ||
const wrapper = mount(testComponent()) | ||
wrapper.find('#stop').trigger('click') | ||
|
||
// Wait for Vue to append #start in the DOM | ||
await wrapper.vm.$nextTick() | ||
expect(removeEventListenerSpy).toHaveBeenCalledTimes(1) | ||
}) | ||
|
||
it('should show #isTracking when onMount is true', async () => { | ||
const wrapper = mount(testComponent(true)) | ||
await wrapper.vm.$nextTick() | ||
expect(wrapper.find('#isTracking').exists()).toBe(true) | ||
}) | ||
|
||
it('should not show #isTracking when onMount is false', async () => { | ||
const wrapper = mount(testComponent(false)) | ||
await wrapper.vm.$nextTick() | ||
expect(wrapper.find('#isTracking').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,60 @@ | ||
import { ref, onMounted, onUnmounted, Ref } from '@vue/composition-api' | ||
|
||
export interface UseMediaDevicesState { | ||
deviceId: string | ||
groupId: string | ||
kind: string | ||
label: string | ||
} | ||
|
||
export function useMediaDevices(runOnMount = true) { | ||
const devicesState: Ref<UseMediaDevicesState[]> = ref([]) | ||
const isTracking = ref(false) | ||
const isTracked = ref(false) | ||
|
||
const deviceMap = ({ | ||
deviceId, | ||
groupId, | ||
kind, | ||
label | ||
}: UseMediaDevicesState) => ({ | ||
deviceId, | ||
groupId, | ||
kind, | ||
label | ||
}) | ||
|
||
const handleDeviceChange = () => { | ||
navigator.mediaDevices | ||
.enumerateDevices() | ||
.then(deviceList => { | ||
if (!isTracking.value) return | ||
isTracked.value = true | ||
devicesState.value = deviceList.map(deviceMap) | ||
}) | ||
.catch(() => { | ||
isTracked.value = false | ||
}) | ||
} | ||
|
||
const start = () => { | ||
if (isTracking.value) return | ||
handleDeviceChange() | ||
navigator.mediaDevices.addEventListener('devicechange', handleDeviceChange) | ||
isTracking.value = true | ||
} | ||
|
||
const stop = () => { | ||
if (!isTracking.value) return | ||
navigator.mediaDevices.removeEventListener( | ||
'devicechange', | ||
handleDeviceChange | ||
) | ||
isTracking.value = false | ||
} | ||
|
||
onMounted(() => runOnMount && start()) | ||
onUnmounted(stop) | ||
|
||
return { devicesState, isTracking, isTracked, 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