Add Media.tsx
This commit is contained in:
parent
6fd6a4edf3
commit
22380ea381
81
src/client/components/Media.test.tsx
Normal file
81
src/client/components/Media.test.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import TestUtils from 'react-dom/test-utils'
|
||||
import { Provider } from 'react-redux'
|
||||
import { createStore, Store } from '../store'
|
||||
import { Media } from './Media'
|
||||
import { MEDIA_ENUMERATE } from '../constants'
|
||||
|
||||
describe('Media', () => {
|
||||
|
||||
const onSave = jest.fn()
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks()
|
||||
store = createStore()
|
||||
store.dispatch({
|
||||
type: MEDIA_ENUMERATE,
|
||||
status: 'resolved',
|
||||
payload: [{
|
||||
id: '123',
|
||||
name: 'Audio Input',
|
||||
type: 'audioinput',
|
||||
}, {
|
||||
id: '456',
|
||||
label: 'Video Input',
|
||||
name: 'videoinput',
|
||||
}],
|
||||
})
|
||||
})
|
||||
|
||||
let store: Store
|
||||
async function render() {
|
||||
const div = document.createElement('div')
|
||||
const node = await new Promise<HTMLDivElement>(resolve => {
|
||||
ReactDOM.render(
|
||||
<div ref={div => resolve(div!)}>
|
||||
<Provider store={store}>
|
||||
<Media onSave={onSave} />
|
||||
</Provider>
|
||||
</div>,
|
||||
div,
|
||||
)
|
||||
})
|
||||
return node.children[0]
|
||||
}
|
||||
|
||||
describe('submit', () => {
|
||||
it('calls onSave', async () => {
|
||||
const node = await render()
|
||||
expect(node.tagName).toBe('FORM')
|
||||
TestUtils.Simulate.submit(node)
|
||||
expect(onSave.mock.calls.length).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('onVideoChange', () => {
|
||||
it('calls onSetVideoConstraint', async () => {
|
||||
const node = await render()
|
||||
const select = node.querySelector('select.media-video')!
|
||||
TestUtils.Simulate.change(select, {
|
||||
target: {
|
||||
value: '{"deviceId":123}',
|
||||
} as any,
|
||||
})
|
||||
expect(store.getState().media.video).toEqual({ deviceId: 123 })
|
||||
})
|
||||
})
|
||||
|
||||
describe('onAudioChange', () => {
|
||||
it('calls onSetAudioConstraint', async () => {
|
||||
const node = await render()
|
||||
const select = node.querySelector('select.media-audio')!
|
||||
TestUtils.Simulate.change(select, {
|
||||
target: {
|
||||
value: '{"deviceId":456}',
|
||||
} as any,
|
||||
})
|
||||
expect(store.getState().media.audio).toEqual({ deviceId: 456 })
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
100
src/client/components/Media.tsx
Normal file
100
src/client/components/Media.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
import React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { AudioConstraint, MediaDevice, setAudioConstraint, setVideoConstraint, VideoConstraint } from '../actions/MediaActions'
|
||||
import { MediaState } from '../reducers/media'
|
||||
import { State } from '../store'
|
||||
|
||||
export type MediaProps = MediaState & {
|
||||
onSetVideoConstraint: typeof setVideoConstraint
|
||||
onSetAudioConstraint: typeof setAudioConstraint
|
||||
onSave: () => void
|
||||
}
|
||||
|
||||
function getId(constraint: VideoConstraint | AudioConstraint) {
|
||||
return typeof constraint === 'object' && 'deviceId' in constraint
|
||||
? constraint.deviceId
|
||||
: ''
|
||||
}
|
||||
|
||||
function mapStateToProps(state: State) {
|
||||
return {
|
||||
...state.media,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
onSetVideoConstraint: setVideoConstraint,
|
||||
onSetAudioConstraint: setAudioConstraint,
|
||||
}
|
||||
|
||||
const c = connect(mapStateToProps, mapDispatchToProps)
|
||||
|
||||
export const Media = c(React.memo(function Media(props: MediaProps) {
|
||||
|
||||
function onSave(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault()
|
||||
props.onSave()
|
||||
}
|
||||
|
||||
function onVideoChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||
const constraint: VideoConstraint = JSON.parse(event.target.value)
|
||||
props.onSetVideoConstraint(constraint)
|
||||
}
|
||||
|
||||
function onAudioChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||
const constraint: AudioConstraint = JSON.parse(event.target.value)
|
||||
props.onSetAudioConstraint(constraint)
|
||||
}
|
||||
|
||||
const videoId = getId(props.video)
|
||||
const audioId = getId(props.audio)
|
||||
|
||||
return (
|
||||
<form className='media' onSubmit={onSave}>
|
||||
<select className='media-video' onChange={onVideoChange} value={videoId}>
|
||||
<Options
|
||||
devices={props.devices}
|
||||
default='{"facingMode":"user"}'
|
||||
type='videoinput'
|
||||
/>
|
||||
</select>
|
||||
|
||||
<select className='media-audio' onChange={onAudioChange} value={audioId}>
|
||||
<Options
|
||||
devices={props.devices}
|
||||
default='true'
|
||||
type='audioinput'
|
||||
/>
|
||||
</select>
|
||||
|
||||
<button type='submit'>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}))
|
||||
|
||||
interface OptionsProps {
|
||||
devices: MediaDevice[]
|
||||
type: 'audioinput' | 'videoinput'
|
||||
default: string
|
||||
}
|
||||
|
||||
function Options(props: OptionsProps) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<option value='false'>Disabled</option>
|
||||
<option value={props.default}>Default</option>
|
||||
{
|
||||
props.devices
|
||||
.filter(device => device.type === props.type)
|
||||
.map(device =>
|
||||
<option
|
||||
key={device.id}
|
||||
value={JSON.stringify({deviceId: device.id})}>{device.name}
|
||||
</option>,
|
||||
)
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user