Skip to content

Commit 190c2f7

Browse files
committed
Customize control order in DefaultPlayer
1 parent 2cfb410 commit 190c2f7

File tree

3 files changed

+155
-27
lines changed

3 files changed

+155
-27
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,21 @@ View the [demo](http://mderrick.github.io/react-html5video/).
2323

2424
### Simple Usage
2525

26-
The simplest way to use this component is to use the default player that is provided.
26+
The simplest way to use this component is to use the default player that is provided. It works the same way as a normal HTML5 video by taking all the supported [HTML5 video attributes](https://developer.mozilla.org/en/docs/Web/HTML/Element/video) except for `controls`. This is now "controlled" and can be an array of supported component names in any order as below:
2727

2828
```js
2929
import { DefaultPlayer as Video } from 'react-html5video';
3030
import 'react-html5video/dist/styles.css';
3131
render() {
3232
return (
33-
<Video controls autoPlay loop muted
33+
<Video autoPlay loop muted
34+
controls={['PlayPause', 'Seek', 'Time', 'Volume', 'Fullscreen']}
3435
poster="http://sourceposter.jpg"
3536
onCanPlayThrough={() => {
3637
// Do stuff
3738
}}>
3839
<source src="http://sourcefile.webm" type="video/webm" />
40+
<track label="English" kind="subtitles" srcLang="en" src="http://source.vtt" default />
3941
</Video>
4042
);
4143
}
@@ -48,10 +50,10 @@ If you want to get creative and create your own video player then you will need
4850
```js
4951
import videoConnect from 'react-html5video';
5052

51-
const MyVideoPlayer = ({ video, videoEl, ...restProps }) => (
53+
const MyVideoPlayer = ({ video, videoEl, children, ...restProps }) => (
5254
<div>
5355
<video {...restProps}>
54-
<source src="http://sourcefile.webm" type="video/webm" />
56+
{ children }
5557
</video>
5658
<p>
5759
Here are the video properties for the above HTML5 video:

src/DefaultPlayer/DefaultPlayer.js

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { PropTypes } from 'react';
22
import videoConnect from './../video/video';
33
import {
44
setVolume,
@@ -10,16 +10,17 @@ import {
1010
getPercentageBuffered
1111
} from './../video/api';
1212
import styles from './DefaultPlayer.css';
13-
import PlayPause from './PlayPause/PlayPause';
13+
import Time from './Time/Time';
1414
import Seek from './Seek/Seek';
1515
import Volume from './Volume/Volume';
16-
import ErrorMessage from './ErrorMessage/ErrorMessage';
17-
import Time from './Time/Time';
16+
import PlayPause from './PlayPause/PlayPause';
1817
import Fullscreen from './Fullscreen/Fullscreen';
18+
import ErrorMessage from './ErrorMessage/ErrorMessage';
1919

20-
const DefaultPlayer = ({
20+
export const DefaultPlayer = ({
2121
video,
2222
style,
23+
controls,
2324
children,
2425
className,
2526
onSeekChange,
@@ -35,7 +36,7 @@ const DefaultPlayer = ({
3536
className
3637
].join(' ')}
3738
style={style}>
38-
{ video.error
39+
{ video && video.error
3940
? <ErrorMessage
4041
className={styles.error}
4142
{...video} />
@@ -45,27 +46,56 @@ const DefaultPlayer = ({
4546
{...restProps}>
4647
{ children }
4748
</video>
48-
<div className={styles.controls}>
49-
<PlayPause
50-
onClick={onPlayPauseClick}
51-
{...video} />
52-
<Seek
53-
className={styles.seek}
54-
onChange={onSeekChange}
55-
{...video} />
56-
<Time {...video} />
57-
<Volume
58-
onChange={onVolumeChange}
59-
onClick={onVolumeClick}
60-
{...video} />
61-
<Fullscreen
62-
onClick={onFullscreenClick}
63-
{...video} />
64-
</div>
49+
{ controls && controls.length
50+
? <div className={styles.controls}>
51+
{ controls.map((control, i) => {
52+
switch (control) {
53+
case 'Seek':
54+
return <Seek
55+
key={i}
56+
className={styles.seek}
57+
onChange={onSeekChange}
58+
{...video} />;
59+
case 'PlayPause':
60+
return <PlayPause
61+
key={i}
62+
onClick={onPlayPauseClick}
63+
{...video} />;
64+
case 'Fullscreen':
65+
return <Fullscreen
66+
key={i}
67+
onClick={onFullscreenClick}
68+
{...video} />;
69+
case 'Time':
70+
return <Time
71+
key={i}
72+
{...video} />;
73+
case 'Volume':
74+
return <Volume
75+
key={i}
76+
onChange={onVolumeChange}
77+
onClick={onVolumeClick}
78+
{...video} />;
79+
default:
80+
return null;
81+
}
82+
}) }
83+
</div>
84+
: null }
6585
</div>
6686
);
6787
};
6888

89+
const controls = ['PlayPause', 'Seek', 'Time', 'Volume', 'Fullscreen'];
90+
91+
DefaultPlayer.defaultProps = {
92+
controls
93+
};
94+
95+
DefaultPlayer.propTypes = {
96+
controls: PropTypes.arrayOf(PropTypes.oneOf(controls))
97+
};
98+
6999
export default videoConnect(
70100
DefaultPlayer,
71101
({ networkState, error, ...restState }) => ({
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import React from 'react';
2+
import { shallow } from 'enzyme';
3+
import { DefaultPlayer } from './DefaultPlayer';
4+
import styles from './DefaultPlayer.css';
5+
import Time from './Time/Time';
6+
import Seek from './Seek/Seek';
7+
import Volume from './Volume/Volume';
8+
import PlayPause from './PlayPause/PlayPause';
9+
import Fullscreen from './Fullscreen/Fullscreen';
10+
import ErrorMessage from './ErrorMessage/ErrorMessage';
11+
12+
describe('DefaultPlayer', () => {
13+
let component;
14+
15+
beforeAll(() => {
16+
component = shallow(<DefaultPlayer />);
17+
});
18+
19+
it('should accept a className prop and append it to the components class', () => {
20+
const newClassNameString = 'a new className';
21+
expect(component.prop('className'))
22+
.toContain(styles.component);
23+
component.setProps({
24+
className: newClassNameString
25+
});
26+
expect(component.prop('className'))
27+
.toContain(styles.component);
28+
expect(component.prop('className'))
29+
.toContain(newClassNameString);
30+
});
31+
32+
it('applies `style` prop if provided', () => {
33+
component.setProps({
34+
style: { color: 'red' }
35+
});
36+
expect(component.prop('style'))
37+
.toEqual({ color: 'red' });
38+
});
39+
40+
it('spreads all parent props on the video element', () => {
41+
component.setProps({
42+
autoPlay: true
43+
});
44+
expect(component.find('video').prop('autoPlay'))
45+
.toEqual(true);
46+
});
47+
48+
it('shows the error component when required', () => {
49+
expect(component.find(ErrorMessage).exists())
50+
.toBeFalsy();
51+
component.setProps({
52+
video: {
53+
error: true
54+
}
55+
});
56+
expect(component.find(ErrorMessage).exists())
57+
.toBeTruthy();
58+
});
59+
60+
it('renders some default controls in a default order', () => {
61+
const controlsComponent = component.find(`.${styles.controls}`);
62+
expect(controlsComponent.childAt(0).is(PlayPause))
63+
.toBeTruthy();
64+
expect(controlsComponent.childAt(1).is(Seek))
65+
.toBeTruthy();
66+
expect(controlsComponent.childAt(2).is(Time))
67+
.toBeTruthy();
68+
expect(controlsComponent.childAt(3).is(Volume))
69+
.toBeTruthy();
70+
expect(controlsComponent.childAt(4).is(Fullscreen))
71+
.toBeTruthy();
72+
});
73+
74+
it('renders controls in a given custom order', () => {
75+
component.setProps({
76+
controls: ['Fullscreen', 'Seek', 'Time']
77+
});
78+
const controlsComponent = component.find(`.${styles.controls}`);
79+
expect(controlsComponent.childAt(0).is(Fullscreen))
80+
.toBeTruthy();
81+
expect(controlsComponent.childAt(1).is(Seek))
82+
.toBeTruthy();
83+
expect(controlsComponent.childAt(2).is(Time))
84+
.toBeTruthy();
85+
});
86+
87+
it('renders no controls when given an empty array', () => {
88+
expect(component.find(`.${styles.controls}`).exists())
89+
.toBeTruthy();
90+
component.setProps({
91+
controls: []
92+
});
93+
expect(component.find(`.${styles.controls}`).exists())
94+
.toBeFalsy();
95+
});
96+
});

0 commit comments

Comments
 (0)