diff --git a/src/lib/viewers/controls/media/MediaSettings.tsx b/src/lib/viewers/controls/media/MediaSettings.tsx index 88dd84a6c..07a82bb5d 100644 --- a/src/lib/viewers/controls/media/MediaSettings.tsx +++ b/src/lib/viewers/controls/media/MediaSettings.tsx @@ -15,7 +15,7 @@ export type Props = Partial & Partial & Partial & AutoplayProps & - RateProps & { className?: string }; + RateProps & { className?: string; isHDSupported?: boolean }; export default function MediaSettings({ audioTrack, @@ -23,6 +23,7 @@ export default function MediaSettings({ autoplay, badge, className, + isHDSupported, onAudioTrackChange = noop, onAutoplayChange, onQualityChange, @@ -61,6 +62,7 @@ export default function MediaSettings({ {quality && ( { const wrapper = getWrapper({ quality: 'auto', onQualityChange: jest.fn() }); expect(wrapper.exists(MediaSettingsMenuQuality)).toBe(true); }); + + test('should render with isDisabled based on isHDSupported prop', () => { + const wrapper = getWrapper({ isHDSupported: false, quality: 'auto', onQualityChange: jest.fn() }); + expect(wrapper.find({ target: 'quality' }).prop('isDisabled')).toBe(true); + }); }); describe('subtitles menu', () => { diff --git a/src/lib/viewers/controls/settings/SettingsMenuItem.scss b/src/lib/viewers/controls/settings/SettingsMenuItem.scss index 74d43488d..1ec9a0b5c 100644 --- a/src/lib/viewers/controls/settings/SettingsMenuItem.scss +++ b/src/lib/viewers/controls/settings/SettingsMenuItem.scss @@ -3,6 +3,10 @@ .bp-SettingsMenuItem { @include bp-SettingsRow; + &[aria-disabled='true'] { + cursor: default; + } + &:hover { background-color: $hover-blue-background; diff --git a/src/lib/viewers/controls/settings/SettingsMenuItem.tsx b/src/lib/viewers/controls/settings/SettingsMenuItem.tsx index 84a2d2d64..ba076adcd 100644 --- a/src/lib/viewers/controls/settings/SettingsMenuItem.tsx +++ b/src/lib/viewers/controls/settings/SettingsMenuItem.tsx @@ -8,23 +8,28 @@ import './SettingsMenuItem.scss'; export type Props = React.RefAttributes & { className?: string; label: string; + isDisabled?: boolean; target: Menu; value: string; }; export type Ref = HTMLDivElement; function SettingsMenuItem(props: Props, ref: React.Ref): JSX.Element { - const { className, label, target, value, ...rest } = props; + const { className, label, isDisabled = false, target, value, ...rest } = props; const { setActiveMenu } = React.useContext(SettingsContext); const handleClick = (): void => { + if (isDisabled) { + return; + } + setActiveMenu(target); }; const handleKeydown = (event: React.KeyboardEvent): void => { const key = decodeKeydown(event); - if (key !== 'ArrowRight' && key !== 'Enter' && key !== 'Space') { + if (isDisabled || (key !== 'ArrowRight' && key !== 'Enter' && key !== 'Space')) { return; } @@ -34,6 +39,7 @@ function SettingsMenuItem(props: Props, ref: React.Ref): JSX.Element { return (
): JSX.Element {
{value}
- + {!isDisabled && }
); diff --git a/src/lib/viewers/controls/settings/__tests__/SettingsMenuItem-test.tsx b/src/lib/viewers/controls/settings/__tests__/SettingsMenuItem-test.tsx index 8c362f042..fafb8d459 100644 --- a/src/lib/viewers/controls/settings/__tests__/SettingsMenuItem-test.tsx +++ b/src/lib/viewers/controls/settings/__tests__/SettingsMenuItem-test.tsx @@ -36,6 +36,29 @@ describe('SettingsMenuItem', () => { expect(context.setActiveMenu).toBeCalledTimes(calledTimes); }); + + test('should not set the active menu when clicked while disabled', () => { + const context = getContext(); + const wrapper = getWrapper({ isDisabled: true }, context); + + wrapper.simulate('click'); + + expect(context.setActiveMenu).not.toBeCalled(); + }); + + test.each` + key + ${'ArrowRight'} + ${'Enter'} + ${'Space'} + `('should not set the active menu when $key is pressed while disabled', ({ key }) => { + const context = getContext(); + const wrapper = getWrapper({ isDisabled: true }, context); + + wrapper.simulate('keydown', { key }); + + expect(context.setActiveMenu).not.toBeCalled(); + }); }); describe('render', () => { @@ -43,9 +66,17 @@ describe('SettingsMenuItem', () => { const wrapper = getWrapper(); expect(wrapper.getDOMNode()).toHaveClass('bp-SettingsMenuItem'); + expect(wrapper.getDOMNode().getAttribute('aria-disabled')).toBe('false'); expect(wrapper.find('.bp-SettingsMenuItem-label').contains('Speed')).toBe(true); expect(wrapper.find('.bp-SettingsMenuItem-value').contains('Normal')).toBe(true); expect(wrapper.exists(IconArrowRight24)).toBe(true); }); + + test('should render as disabled when isDisabled is true', () => { + const wrapper = getWrapper({ isDisabled: true }); + + expect(wrapper.getDOMNode().getAttribute('aria-disabled')).toBe('true'); + expect(wrapper.exists(IconArrowRight24)).toBe(false); + }); }); }); diff --git a/src/lib/viewers/media/DashControls.tsx b/src/lib/viewers/media/DashControls.tsx index d47e3ce3c..ac3739467 100644 --- a/src/lib/viewers/media/DashControls.tsx +++ b/src/lib/viewers/media/DashControls.tsx @@ -26,6 +26,7 @@ export default function DashControls({ currentTime, durationTime, isAutoGeneratedSubtitles, + isHDSupported, isPlaying, isPlayingHD, onAudioTrackChange, @@ -74,6 +75,7 @@ export default function DashControls({ autoplay={autoplay} badge={isPlayingHD ? : undefined} className="bp-DashControls-settings" + isHDSupported={isHDSupported} onAudioTrackChange={onAudioTrackChange} onAutoplayChange={onAutoplayChange} onQualityChange={onQualityChange} diff --git a/src/lib/viewers/media/DashViewer.js b/src/lib/viewers/media/DashViewer.js index 83a07e429..cc8733185 100644 --- a/src/lib/viewers/media/DashViewer.js +++ b/src/lib/viewers/media/DashViewer.js @@ -835,7 +835,7 @@ class DashViewer extends VideoBaseViewer { const isHDSupported = this.hdVideoId !== -1; this.selectedQuality = isHDSupported ? this.cache.get('media-quality') || 'auto' : 'sd'; - this.setQuality(this.selectedQuality); + this.setQuality(this.selectedQuality, false); } /** @@ -1045,12 +1045,13 @@ class DashViewer extends VideoBaseViewer { /** * Updates the selected quality and updates the player accordingly * @param {string} quality - 'sd', 'hd', or 'auto' + * @param {boolean} [saveToCache] - Whether to save this value to the cache, defaults to true * @emits qualitychange * @return {void} */ - setQuality(quality) { + setQuality(quality, saveToCache = true) { const newQuality = quality !== 'sd' && quality !== 'hd' ? 'auto' : quality; - this.cache.set('media-quality', newQuality, true); + this.cache.set('media-quality', newQuality, saveToCache); this.selectedQuality = newQuality; switch (newQuality) { @@ -1147,6 +1148,7 @@ class DashViewer extends VideoBaseViewer { currentTime={this.mediaEl.currentTime} durationTime={this.mediaEl.duration} isAutoGeneratedSubtitles={!!this.autoCaptionDisplayer} + isHDSupported={this.hdVideoId !== -1} isPlaying={!this.mediaEl.paused} isPlayingHD={this.isPlayingHD()} onAudioTrackChange={this.setAudioTrack} diff --git a/src/lib/viewers/media/__tests__/DashControls-test.tsx b/src/lib/viewers/media/__tests__/DashControls-test.tsx index 0a5da8e4a..0ec57dd54 100644 --- a/src/lib/viewers/media/__tests__/DashControls-test.tsx +++ b/src/lib/viewers/media/__tests__/DashControls-test.tsx @@ -79,6 +79,11 @@ describe('DashControls', () => { expect(wrapper.find(VolumeControls).prop('onVolumeChange')).toEqual(onVolumeChange); }); + test.each([true, false])('should set isHDSupported prop on MediaSettings as %s', isHDSupported => { + const wrapper = getWrapper({ isHDSupported }); + expect(wrapper.find(MediaSettings).prop('isHDSupported')).toBe(isHDSupported); + }); + test('should not pass along badge if not playing HD', () => { const wrapper = getWrapper({ badge: }); expect(wrapper.find(MediaSettings).prop('badge')).toBeUndefined(); diff --git a/src/lib/viewers/media/__tests__/DashViewer-test.js b/src/lib/viewers/media/__tests__/DashViewer-test.js index 118f6a7a2..1a4cfb949 100644 --- a/src/lib/viewers/media/__tests__/DashViewer-test.js +++ b/src/lib/viewers/media/__tests__/DashViewer-test.js @@ -752,14 +752,14 @@ describe('lib/viewers/media/DashViewer', () => { dash.loadUIReact(); expect(dash.selectedQuality).toBe('sd'); - expect(dash.setQuality).toBeCalledWith('sd'); + expect(dash.setQuality).toBeCalledWith('sd', false); }); test('should set quality to auto if HD is supported and cache has no entry', () => { dash.loadUIReact(); expect(dash.selectedQuality).toBe('auto'); - expect(dash.setQuality).toBeCalledWith('auto'); + expect(dash.setQuality).toBeCalledWith('auto', false); }); test('should set quality to cache value if HD is supported and cache has an entry', () => { @@ -768,7 +768,7 @@ describe('lib/viewers/media/DashViewer', () => { dash.loadUIReact(); expect(dash.selectedQuality).toBe('hd'); - expect(dash.setQuality).toBeCalledWith('hd'); + expect(dash.setQuality).toBeCalledWith('hd', false); }); }); @@ -1638,6 +1638,11 @@ describe('lib/viewers/media/DashViewer', () => { expect(dash.emit).toBeCalledWith('qualitychange', 'auto'); expect(dash.renderUI).toBeCalled(); }); + + test('should not save to cache if saveToCache is false', () => { + dash.setQuality('auto', false); + expect(dash.cache.set).toBeCalledWith('media-quality', 'auto', false); + }); }); describe('initSubtitles()', () => { diff --git a/test/integration/media/DashViewer.e2e.test.js b/test/integration/media/DashViewer.e2e.test.js index 696dc484b..b6d44a639 100644 --- a/test/integration/media/DashViewer.e2e.test.js +++ b/test/integration/media/DashViewer.e2e.test.js @@ -1,6 +1,7 @@ import { runAudioTracksTests, runBaseMediaSettingsTests, + runLowQualityMenuTests, runQualityMenuTests, runSubtitlesTests, } from '../../support/mediaSettingsTests'; @@ -8,50 +9,61 @@ import { describe('Dash Viewer', () => { const token = Cypress.env('ACCESS_TOKEN'); const fileIdVideo = Cypress.env('FILE_ID_VIDEO_SUBTITLES_TRACKS'); + const fileIdVideoSmall = Cypress.env('FILE_ID_VIDEO_SMALL'); - describe('Media Settings Controls', () => { - describe('Without react controls', () => { - beforeEach(() => { - cy.visit('/'); - cy.showPreview(token, fileIdVideo, { - viewers: { Dash: { useReactControls: false } }, - }); + const setupTest = (fileId, useReactControls) => { + cy.visit('/'); + cy.showPreview(token, fileId, { + viewers: { Dash: { useReactControls } }, + }); - cy.showMediaControls(); + cy.showMediaControls(); - // Open the menu - cy.getByTitle('Settings').click(); - }); + // Open the menu + cy.getByTitle('Settings').click(); + }; - runBaseMediaSettingsTests(); + describe('HD Video with Subtitles', () => { + describe('Media Settings Controls', () => { + describe('Without react controls', () => { + beforeEach(() => setupTest(fileIdVideo, false)); - runQualityMenuTests(); + runBaseMediaSettingsTests(); - runAudioTracksTests(); + runQualityMenuTests(); - runSubtitlesTests(); - }); + runAudioTracksTests(); + + runSubtitlesTests(); + }); + + describe('With react controls', () => { + beforeEach(() => setupTest(fileIdVideo, true)); - describe('With react controls', () => { - beforeEach(() => { - cy.visit('/'); - cy.showPreview(token, fileIdVideo, { - viewers: { Dash: { useReactControls: true } }, - }); + runBaseMediaSettingsTests(); - cy.showMediaControls(); + runQualityMenuTests(); - // Open the menu - cy.getByTitle('Settings').click(); + runAudioTracksTests(); + + runSubtitlesTests(); }); + }); + }); - runBaseMediaSettingsTests(); + describe('Non HD Video', () => { + describe('Media Settings Controls', () => { + describe('Without react controls', () => { + beforeEach(() => setupTest(fileIdVideoSmall, false)); - runQualityMenuTests(); + runLowQualityMenuTests(); + }); - runAudioTracksTests(); + describe('With react controls', () => { + beforeEach(() => setupTest(fileIdVideoSmall, true)); - runSubtitlesTests(); + runLowQualityMenuTests(); + }); }); }); }); diff --git a/test/support/mediaSettingsTests.js b/test/support/mediaSettingsTests.js index f3cc2d426..1c094939a 100644 --- a/test/support/mediaSettingsTests.js +++ b/test/support/mediaSettingsTests.js @@ -76,6 +76,18 @@ export function runQualityMenuTests() { }); } +export function runLowQualityMenuTests() { + describe('Non HD Video', () => { + it('Should not have the Quality settings menu enabled', () => { + cy.getByTestId('bp-media-settings-quality') + .contains('480p') + .click({ force: true }); + + cy.getByTestId('bp-media-controls-hd').should('not.be.visible'); + }); + }); +} + export function runAudioTracksTests() { describe('Audiotracks Menu', () => { it('Should be able to change the Audiotrack setting', () => {