Skip to content

Commit

Permalink
feat(controls): Base React controls for Dash (#1390)
Browse files Browse the repository at this point in the history
  • Loading branch information
Conrad Chan authored May 26, 2021
1 parent 09fadfa commit 6954be0
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 39 deletions.
34 changes: 34 additions & 0 deletions src/lib/viewers/controls/media/_styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,37 @@ $bp-MediaControl-width: 36px;
@mixin bp-MediaButton {
@include bp-Control($width: $bp-MediaControl-width, $height: $bp-MediaControl-height);
}

@mixin bp-VideoControls {
display: flex;
flex-direction: column;
width: 100%;
background-image: linear-gradient(to top, rgba($black, .6) 0%, rgba($black, 0) 100%);

.bp-FullscreenToggle {
width: $bp-MediaControl-width;
height: $bp-MediaControl-height;
}
}

@mixin bp-VideoControls-bar {
display: flex;
justify-content: space-between;
padding: 0 10px 5px;
}

@mixin bp-VideoControls-group {
display: flex;
align-items: center;
}

@mixin bp-VideoControls-settings {
.bp-SettingsFlyout {
right: 15px;
}

.bp-SettingsToggle {
width: $bp-MediaControl-width;
height: $bp-MediaControl-height;
}
}
17 changes: 17 additions & 0 deletions src/lib/viewers/media/DashControls.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@import '../controls//media/styles';

.bp-DashControls {
@include bp-VideoControls;
}

.bp-DashControls-bar {
@include bp-VideoControls-bar;
}

.bp-DashControls-group {
@include bp-VideoControls-group;
}

.bp-DashControls-settings {
@include bp-VideoControls-settings;
}
63 changes: 63 additions & 0 deletions src/lib/viewers/media/DashControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import DurationLabels, { Props as DurationLabelsProps } from '../controls/media/DurationLabels';
import MediaFullscreenToggle, { Props as MediaFullscreenToggleProps } from '../controls/media/MediaFullscreenToggle';
import MediaSettings, { Props as MediaSettingsProps } from '../controls/media/MediaSettings';
import PlayPauseToggle, { Props as PlayControlsProps } from '../controls/media/PlayPauseToggle';
import TimeControls, { Props as TimeControlsProps } from '../controls/media/TimeControls';
import VolumeControls, { Props as VolumeControlsProps } from '../controls/media/VolumeControls';
import './DashControls.scss';

export type Props = DurationLabelsProps &
MediaFullscreenToggleProps &
MediaSettingsProps &
PlayControlsProps &
TimeControlsProps &
VolumeControlsProps;

export default function DashControls({
autoplay,
bufferedRange,
currentTime,
durationTime,
isPlaying,
onAutoplayChange,
onFullscreenToggle,
onMuteChange,
onPlayPause,
onRateChange,
onTimeChange,
onVolumeChange,
rate,
volume,
}: Props): JSX.Element {
return (
<div className="bp-DashControls">
<TimeControls
bufferedRange={bufferedRange}
currentTime={currentTime}
durationTime={durationTime}
onTimeChange={onTimeChange}
/>

<div className="bp-DashControls-bar">
<div className="bp-DashControls-group">
<PlayPauseToggle isPlaying={isPlaying} onPlayPause={onPlayPause} />
<VolumeControls onMuteChange={onMuteChange} onVolumeChange={onVolumeChange} volume={volume} />
<DurationLabels currentTime={currentTime} durationTime={durationTime} />
</div>

<div className="bp-DashControls-group">
{/* CC Toggle */}
<MediaSettings
autoplay={autoplay}
className="bp-DashControls-settings"
onAutoplayChange={onAutoplayChange}
onRateChange={onRateChange}
rate={rate}
/>
<MediaFullscreenToggle onFullscreenToggle={onFullscreenToggle} />
</div>
</div>
</div>
);
}
58 changes: 49 additions & 9 deletions src/lib/viewers/media/DashViewer.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import VideoBaseViewer from './VideoBaseViewer';
import PreviewError from '../../PreviewError';
import React from 'react';
import DashControls from './DashControls';
import fullscreen from '../../Fullscreen';
import getLanguageName from '../../lang';
import PreviewError from '../../PreviewError';
import Timer from '../../Timer';
import VideoBaseViewer from './VideoBaseViewer';
import { appendQueryParams, getProp } from '../../util';
import { ERROR_CODE, VIEWER_EVENT, MEDIA_METRIC, MEDIA_METRIC_EVENTS } from '../../events';
import { getRepresentation } from '../../file';
import { MEDIA_STATIC_ASSETS_VERSION } from '../../constants';
import getLanguageName from '../../lang';
import { ERROR_CODE, VIEWER_EVENT, MEDIA_METRIC, MEDIA_METRIC_EVENTS } from '../../events';
import './Dash.scss';

const CSS_CLASS_DASH = 'bp-media-dash';
Expand Down Expand Up @@ -685,26 +687,36 @@ class DashViewer extends VideoBaseViewer {
}

this.calculateVideoDimensions();
this.loadUI();
if (this.getViewerOption('useReactControls')) {
this.loadUIReact();
} else {
this.loadUI();
}

if (this.isAutoplayEnabled()) {
this.autoplay();
}

this.loadFilmStrip();
if (!this.getViewerOption('useReactControls')) {
this.loadFilmStrip();
}
this.resize();
this.handleVolume();
this.startBandwidthTracking();
this.loadSubtitles();
this.loadAlternateAudio();
if (!this.getViewerOption('useReactControls')) {
this.loadSubtitles();
this.loadAlternateAudio();
}
this.showPlayButton();

this.loaded = true;
this.emit(VIEWER_EVENT.load);

// Make media element visible after resize
this.showMedia();
this.mediaControls.show();
if (!this.getViewerOption('useReactControls')) {
this.mediaControls.show();
}

if (this.options.autoFocus) {
this.mediaContainerEl.focus();
Expand Down Expand Up @@ -911,6 +923,34 @@ class DashViewer extends VideoBaseViewer {

return super.onKeydown(key);
}

/**
* @inheritdoc
*/
renderUI() {
if (!this.controls) {
return;
}

this.controls.render(
<DashControls
autoplay={this.isAutoplayEnabled()}
bufferedRange={this.mediaEl.buffered}
currentTime={this.mediaEl.currentTime}
durationTime={this.mediaEl.duration}
isPlaying={!this.mediaEl.paused}
onAutoplayChange={this.setAutoplay}
onFullscreenToggle={this.toggleFullscreen}
onMuteChange={this.toggleMute}
onPlayPause={this.togglePlay}
onRateChange={this.setRate}
onTimeChange={this.handleTimeupdateFromMediaControls}
onVolumeChange={this.setVolume}
rate={this.getRate()}
volume={this.mediaEl.volume}
/>,
);
}
}

export default DashViewer;
8 changes: 0 additions & 8 deletions src/lib/viewers/media/MP4.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,4 @@
opacity: 1;
}
}

.bp-VideoControlsRoot {
position: absolute;
right: 0;
bottom: -1px;
left: 0;
width: 100%;
}
}
26 changes: 4 additions & 22 deletions src/lib/viewers/media/MP4Controls.scss
Original file line number Diff line number Diff line change
@@ -1,35 +1,17 @@
@import '../controls/media/styles';

.bp-MP4Controls {
display: flex;
flex-direction: column;
width: 100%;
background-image: linear-gradient(to top, rgba($black, .6) 0%, rgba($black, 0) 100%);

.bp-FullscreenToggle {
width: $bp-MediaControl-width;
height: $bp-MediaControl-height;
}
@include bp-VideoControls;
}

.bp-MP4Controls-bar {
display: flex;
justify-content: space-between;
padding: 0 10px 5px;
@include bp-VideoControls-bar;
}

.bp-MP4Controls-group {
display: flex;
align-items: center;
@include bp-VideoControls-group;
}

.bp-MP4Controls-settings {
.bp-SettingsFlyout {
right: 15px;
}

.bp-SettingsToggle {
width: $bp-MediaControl-width;
height: $bp-MediaControl-height;
}
@include bp-VideoControls-settings;
}
44 changes: 44 additions & 0 deletions src/lib/viewers/media/__tests__/DashControls-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import { shallow } from 'enzyme';
import DashControls from '../DashControls';
import MediaFullscreenToggle from '../../controls/media/MediaFullscreenToggle';
import MediaSettings from '../../controls/media/MediaSettings';
import PlayPauseToggle from '../../controls/media/PlayPauseToggle';
import TimeControls from '../../controls/media/TimeControls';
import VolumeControls from '../../controls/media/VolumeControls';

describe('DashControls', () => {
describe('render', () => {
test('should return a valid wrapper', () => {
const onAutoplayChange = jest.fn();
const onFullscreenToggle = jest.fn();
const onMuteChange = jest.fn();
const onRateChange = jest.fn();
const onPlayPause = jest.fn();
const onTimeChange = jest.fn();
const onVolumeChange = jest.fn();
const wrapper = shallow(
<DashControls
autoplay={false}
onAutoplayChange={onAutoplayChange}
onFullscreenToggle={onFullscreenToggle}
onMuteChange={onMuteChange}
onPlayPause={onPlayPause}
onRateChange={onRateChange}
onTimeChange={onTimeChange}
onVolumeChange={onVolumeChange}
rate="1.0"
/>,
);

expect(wrapper.hasClass('bp-DashControls')).toBe(true);
expect(wrapper.find(MediaFullscreenToggle).prop('onFullscreenToggle')).toEqual(onFullscreenToggle);
expect(wrapper.find(MediaSettings).prop('onAutoplayChange')).toEqual(onAutoplayChange);
expect(wrapper.find(MediaSettings).prop('onRateChange')).toEqual(onRateChange);
expect(wrapper.find(PlayPauseToggle).prop('onPlayPause')).toEqual(onPlayPause);
expect(wrapper.find(TimeControls).prop('onTimeChange')).toEqual(onTimeChange);
expect(wrapper.find(VolumeControls).prop('onMuteChange')).toEqual(onMuteChange);
expect(wrapper.find(VolumeControls).prop('onVolumeChange')).toEqual(onVolumeChange);
});
});
});
54 changes: 54 additions & 0 deletions src/lib/viewers/media/__tests__/DashViewer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,29 @@ describe('lib/viewers/media/DashViewer', () => {
expect(dash.mediaControls.show).toBeCalled();
expect(dash.loadUI).toBeCalled();
});

describe('With react controls', () => {
beforeEach(() => {
jest.spyOn(dash, 'calculateVideoDimensions').mockImplementation();
jest.spyOn(dash, 'getViewerOption').mockImplementation(() => true);
jest.spyOn(dash, 'loadAlternateAudio').mockImplementation();
jest.spyOn(dash, 'loadFilmStrip').mockImplementation();
jest.spyOn(dash, 'loadSubtitles').mockImplementation();
jest.spyOn(dash, 'loadUIReact').mockImplementation();
jest.spyOn(dash, 'loadUI').mockImplementation();
jest.spyOn(dash, 'resize').mockImplementation();
});

test('should call loadUIReact', () => {
dash.loadeddataHandler();

expect(dash.loadUIReact).toBeCalled();
expect(dash.loadUI).not.toBeCalled();
expect(dash.loadFilmStrip).not.toBeCalled();
expect(dash.loadSubtitles).not.toBeCalled();
expect(dash.loadAlternateAudio).not.toBeCalled();
});
});
});

describe('loadUI()', () => {
Expand Down Expand Up @@ -1442,4 +1465,35 @@ describe('lib/viewers/media/DashViewer', () => {
expect(dash.determineWatchLength()).toBe(10000);
});
});

describe('renderUI()', () => {
const getProps = instance => instance.controls.render.mock.calls[0][0].props;

beforeEach(() => {
jest.spyOn(dash, 'getViewerOption').mockImplementation(() => true);
dash.controls = {
destroy: jest.fn(),
render: jest.fn(),
};
});

test('should render react controls with the correct props', () => {
dash.renderUI();

expect(getProps(dash)).toMatchObject({
autoplay: false,
currentTime: expect.any(Number),
isPlaying: expect.any(Boolean),
onAutoplayChange: dash.setAutoplay,
onFullscreenToggle: dash.toggleFullscreen,
onMuteChange: dash.toggleMute,
onPlayPause: dash.togglePlay,
onRateChange: dash.setRate,
onTimeChange: dash.handleTimeupdateFromMediaControls,
onVolumeChange: dash.setVolume,
rate: '1.0',
volume: expect.any(Number),
});
});
});
});
8 changes: 8 additions & 0 deletions src/lib/viewers/media/_mediaBase.scss
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,11 @@
display: none;
}
}

.bp-VideoControlsRoot {
position: absolute;
right: 0;
bottom: -1px;
left: 0;
width: 100%;
}

0 comments on commit 6954be0

Please sign in to comment.