Skip to content

Commit 02a79a6

Browse files
committed
Seek control
1 parent 77c9ca1 commit 02a79a6

File tree

6 files changed

+168
-13
lines changed

6 files changed

+168
-13
lines changed

src/DefaultPlayer/DefaultPlayer.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import {
44
setVolume,
55
toggleMute,
66
togglePause,
7-
setCurrentTime
7+
setCurrentTime,
8+
getPercentagePlayed,
9+
getPercentageBuffered
810
} from './../video/api';
911
import styles from './DefaultPlayer.css';
1012
import PlayPause from './PlayPause/PlayPause';
@@ -44,6 +46,7 @@ const DefaultPlayer = ({
4446
togglePause={togglePause}
4547
{...video} />
4648
<Seek
49+
className={styles.seek}
4750
setCurrentTime={setCurrentTime}
4851
{...video} />
4952
<Volume
@@ -57,9 +60,11 @@ const DefaultPlayer = ({
5760

5861
export default video(
5962
DefaultPlayer,
60-
({ networkState, error, ...restState }) => ({
63+
({ networkState, error, ...restState }) => ({
6164
video: {
6265
error: error || networkState === 3,
66+
percentagePlayed: getPercentagePlayed(restState),
67+
percentageBuffered: getPercentageBuffered(restState),
6368
...restState
6469
}
6570
}),

src/DefaultPlayer/Seek/Seek.css

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,36 @@
1-
.component {}
1+
.component {
2+
position: relative;
3+
}
4+
5+
.track {
6+
position: absolute;
7+
top: 50%;
8+
left: 5px;
9+
right: 5px;
10+
height: 4px;
11+
transform: translateY(-50%);
12+
background-color: #3e3e3e;
13+
}
14+
15+
.buffer,
16+
.fill,
17+
.input {
18+
position: absolute;
19+
top: 0;
20+
left: 0;
21+
height: 100%;
22+
}
23+
24+
.buffer {
25+
background-color: #5a5a5a;
26+
}
27+
28+
.fill {
29+
background: #fff;
30+
}
31+
32+
.input {
33+
width: 100%;
34+
opacity: 0;
35+
cursor: pointer;
36+
}

src/DefaultPlayer/Seek/Seek.js

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import styles from './Seek.css';
33

4-
export default ({ setCurrentTime, duration, currentTime, className }) => {
4+
export default ({ setCurrentTime, duration, percentagePlayed, percentageBuffered, className }) => {
55
const change = (e) => {
66
setCurrentTime(e.target.value * duration / 100);
77
};
@@ -10,14 +10,27 @@ export default ({ setCurrentTime, duration, currentTime, className }) => {
1010
styles.component,
1111
className
1212
].join(' ')}>
13-
<input
14-
min="0"
15-
step={1}
16-
max="100"
17-
type="range"
18-
orient="horizontal"
19-
onChange={change}
20-
value={currentTime / duration * 100} />
13+
<div className={styles.track}>
14+
<div
15+
className={styles.buffer}
16+
style={{
17+
width: `${percentageBuffered || 0}%`
18+
}} />
19+
<div
20+
className={styles.fill}
21+
style={{
22+
width: `${percentagePlayed || 0}%`
23+
}} />
24+
<input
25+
min="0"
26+
step={1}
27+
max="100"
28+
type="range"
29+
orient="horizontal"
30+
onChange={change}
31+
className={styles.input}
32+
value={percentagePlayed} />
33+
</div>
2134
</div>
2235
);
2336
};

src/DefaultPlayer/Seek/Seek.test.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React from 'react';
2+
import { shallow } from 'enzyme';
3+
import Seek from './Seek';
4+
import styles from './Seek.css';
5+
6+
describe('Seek', () => {
7+
let component;
8+
9+
beforeAll(() => {
10+
component = shallow(<Seek />);
11+
});
12+
13+
it('should accept a className prop and append it to the components class', () => {
14+
const newClassNameString = 'a new className';
15+
expect(component.prop('className'))
16+
.toContain(styles.component);
17+
component.setProps({
18+
className: newClassNameString
19+
});
20+
expect(component.prop('className'))
21+
.toContain(styles.component);
22+
expect(component.prop('className'))
23+
.toContain(newClassNameString);
24+
});
25+
26+
it('has a range input with correct ranges and percentagePlayed value', () => {
27+
component.setProps({
28+
percentagePlayed: 10
29+
});
30+
const rangeInput = component.find(`.${styles.input}`);
31+
expect(rangeInput.prop('type'))
32+
.toEqual('range');
33+
expect(rangeInput.prop('orient'))
34+
.toEqual('horizontal');
35+
expect(rangeInput.prop('step'))
36+
.toEqual(1);
37+
expect(rangeInput.prop('min'))
38+
.toEqual('0');
39+
expect(rangeInput.prop('max'))
40+
.toEqual('100');
41+
expect(rangeInput.prop('value'))
42+
.toEqual(10);
43+
});
44+
45+
it('changes the played fill bar width', () => {
46+
component.setProps({
47+
percentagePlayed: 0
48+
});
49+
expect(component.find(`.${styles.fill}`).prop('style').width)
50+
.toEqual('0%');
51+
component.setProps({
52+
percentagePlayed: 11
53+
});
54+
expect(component.find(`.${styles.fill}`).prop('style').width)
55+
.toEqual('11%');
56+
});
57+
58+
it('changes the buffer bar width', () => {
59+
component.setProps({
60+
percentageBuffered: 0
61+
});
62+
expect(component.find(`.${styles.buffer}`).prop('style').width)
63+
.toEqual('0%');
64+
component.setProps({
65+
percentageBuffered: 11
66+
});
67+
expect(component.find(`.${styles.buffer}`).prop('style').width)
68+
.toEqual('11%');
69+
});
70+
});

src/video/api.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,14 @@ export const toggleMute = (videoEl, { volume, muted }) => {
3939
videoEl.muted = true;
4040
}
4141
};
42+
43+
/**
44+
* Custom getter methods that are commonly used
45+
* across video layouts. To be primarily used in
46+
* `mapStateToProps`
47+
*/
48+
export const getPercentageBuffered = ({ buffered, duration }) =>
49+
buffered && buffered.length && buffered.end(buffered.length - 1) / duration * 100 || 0;
50+
51+
export const getPercentagePlayed = ({ currentTime, duration }) =>
52+
currentTime / duration * 100;

src/video/api.test.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import {
44
setVolume,
55
toggleMute,
66
togglePause,
7-
setCurrentTime
7+
setCurrentTime,
8+
getPercentagePlayed,
9+
getPercentageBuffered
810
} from './api';
911

1012
describe('api', () => {
@@ -102,4 +104,23 @@ describe('api', () => {
102104
expect(videoElMock.muted).toBe(false);
103105
});
104106
});
107+
108+
describe('getPercentagePlayed', () => {
109+
it('returns correct percentage played', () => {
110+
expect(getPercentagePlayed({
111+
currentTime: 10,
112+
duration: 100
113+
})).toBe(10);
114+
115+
expect(getPercentagePlayed({
116+
currentTime: 1,
117+
duration: 10
118+
})).toBe(10);
119+
120+
expect(getPercentagePlayed({
121+
currentTime: 5,
122+
duration: 20
123+
})).toBe(25);
124+
});
125+
});
105126
});

0 commit comments

Comments
 (0)