diff --git a/__tests__/src/actions/workspace.test.js b/__tests__/src/actions/workspace.test.js index 270ca53322a36720e4e99c5b3502c5196d59d9f1..28158e00ded626dfa0e7d41eadec3432d8dbac6f 100644 --- a/__tests__/src/actions/workspace.test.js +++ b/__tests__/src/actions/workspace.test.js @@ -13,24 +13,6 @@ describe('workspace actions', () => { expect(actions.updateWorkspace(options)).toEqual(expectedAction); }); }); - describe('setWorkspaceFullscreen', () => { - it('should return correct action type if set to true', () => { - const receivedAction = actions.setWorkspaceFullscreen(true); - const expectedAction = { - isFullscreenEnabled: true, - type: ActionTypes.SET_WORKSPACE_FULLSCREEN, - }; - expect(receivedAction).toEqual(expectedAction); - }); - it('should return correct action type if set to false', () => { - const receivedAction = actions.setWorkspaceFullscreen(false); - const expectedAction = { - isFullscreenEnabled: false, - type: ActionTypes.SET_WORKSPACE_FULLSCREEN, - }; - expect(receivedAction).toEqual(expectedAction); - }); - }); describe('updateWorkspaceMosaicLayout', () => { it('should updates mosaic layout', () => { const options = { foo: 'bar' }; diff --git a/__tests__/src/components/AppProviders.test.js b/__tests__/src/components/AppProviders.test.js index 020a66b960d7ef2b3f1112dbda24ebf49783a156..8f8edbed97ce7013d695d3ad007877d1cc8c18ac 100644 --- a/__tests__/src/components/AppProviders.test.js +++ b/__tests__/src/components/AppProviders.test.js @@ -1,7 +1,6 @@ import React from 'react'; import { shallow } from 'enzyme'; import { ThemeProvider, StylesProvider } from '@material-ui/core/styles'; -import Fullscreen from 'react-full-screen'; import { DndContext, DndProvider } from 'react-dnd'; import { AppProviders } from '../../../src/components/AppProviders'; import settings from '../../../src/config/settings'; @@ -14,7 +13,6 @@ function createWrapper(props) { <AppProviders language="en" isFullscreenEnabled={false} - setWorkspaceFullscreen={() => {}} theme={settings.theme} translations={{}} t={k => k} @@ -28,7 +26,6 @@ describe('AppProviders', () => { const wrapper = createWrapper(); expect(wrapper.find(ThemeProvider).length).toBe(1); expect(wrapper.find(StylesProvider).length).toBe(1); - expect(wrapper.find(Fullscreen).length).toBe(1); }); it('sets up a theme based on the config passed in merged w/ MaterialUI', () => { @@ -44,23 +41,6 @@ describe('AppProviders', () => { expect(wrapper.instance().i18n.t('off')).toEqual('on'); }); - it('should pass setWorkspaceFullscreen to Fullscreen.onChange', () => { - const mockFn = jest.fn(); - const wrapper = createWrapper({ setWorkspaceFullscreen: mockFn }); - expect(wrapper.find(Fullscreen).first().prop('onChange')) - .toBe(mockFn); - }); - - it('should pass isFullscreenEnabled to Fullscreen.enabled', () => { - let wrapper = createWrapper({ isFullscreenEnabled: false }); - expect(wrapper.find(Fullscreen).first().prop('enabled')) - .toEqual(false); - - wrapper = createWrapper({ isFullscreenEnabled: true }); - expect(wrapper.find(Fullscreen).first().prop('enabled')) - .toEqual(true); - }); - describe('componentDidUpdate()', () => { it('changes the i18n language if the language prop has been updated', () => { const wrapper = createWrapper(); diff --git a/__tests__/src/components/FullScreenButton.test.js b/__tests__/src/components/FullScreenButton.test.js index 48701a823ad8859c11f73a1b14ee6bcfcc6c8d28..cd70231645a3abe0ca64db6f29cc391a904ac97f 100644 --- a/__tests__/src/components/FullScreenButton.test.js +++ b/__tests__/src/components/FullScreenButton.test.js @@ -4,18 +4,21 @@ import FullscreenIcon from '@material-ui/icons/FullscreenSharp'; import FullscreenExitIcon from '@material-ui/icons/FullscreenExitSharp'; import MiradorMenuButton from '../../../src/containers/MiradorMenuButton'; import { FullScreenButton } from '../../../src/components/FullScreenButton'; +import FullScreenContext from '../../../src/contexts/FullScreenContext'; /** */ -function createWrapper(props) { +function createWrapper(props, contextProps = { active: false }) { return shallow( <FullScreenButton classes={{}} className="xyz" - setWorkspaceFullscreen={() => {}} - isFullscreenEnabled={false} {...props} />, - ); + { + wrappingComponent: FullScreenContext.Provider, + wrappingComponentProps: { value: { enter: () => { }, exit: () => { }, ...contextProps } }, + }, + ).dive(); } describe('FullScreenButton', () => { @@ -30,10 +33,10 @@ describe('FullScreenButton', () => { }); describe('when not in fullscreen', () => { - let setWorkspaceFullscreen; + let enter; beforeAll(() => { - setWorkspaceFullscreen = jest.fn(); - wrapper = createWrapper({ setWorkspaceFullscreen }); + enter = jest.fn(); + wrapper = createWrapper({}, { enter }); menuButton = wrapper.find(MiradorMenuButton); }); @@ -45,17 +48,17 @@ describe('FullScreenButton', () => { expect(menuButton.props()['aria-label']).toEqual('workspaceFullScreen'); }); - it('triggers the setWorkspaceFullscreen prop with the appropriate boolean', () => { + it('triggers the handle enter with the appropriate boolean', () => { menuButton.props().onClick(); // Trigger the onClick prop - expect(setWorkspaceFullscreen).toHaveBeenCalledWith(true); + expect(enter).toHaveBeenCalled(); }); }); describe('when in fullscreen', () => { - let setWorkspaceFullscreen; + let exit; beforeAll(() => { - setWorkspaceFullscreen = jest.fn(); - wrapper = createWrapper({ isFullscreenEnabled: true, setWorkspaceFullscreen }); + exit = jest.fn(); + wrapper = createWrapper({}, { active: true, exit }); menuButton = wrapper.find(MiradorMenuButton); }); @@ -67,9 +70,9 @@ describe('FullScreenButton', () => { expect(menuButton.props()['aria-label']).toEqual('exitFullScreen'); }); - it('triggers the setWorkspaceFullscreen prop with the appropriate boolean', () => { + it('triggers the handle exit with the appropriate boolean', () => { menuButton.props().onClick(); // Trigger the onClick prop - expect(setWorkspaceFullscreen).toHaveBeenCalledWith(false); + expect(exit).toHaveBeenCalled(); }); }); }); diff --git a/package.json b/package.json index 179a809c5decd0500a50d9119a897429a9767ddc..5926f816211613ba7adbbb87f0b836861f940766 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "react-dnd-html5-backend": "^10.0.2", "react-dnd-multi-backend": "^5.0.0", "react-dnd-touch-backend": "^10.0.2", - "react-full-screen": "^0.2.4", + "react-full-screen": "^1.1.1", "react-i18next": "^11.7.0", "react-image": "^4.0.1", "react-mosaic-component": "^4.0.1", diff --git a/src/components/AppProviders.js b/src/components/AppProviders.js index aeeedc34b928f6622373be9005b843b6f3755451..25b6cb13b87a9bb61179efb8cbd50e9ea82e6452 100644 --- a/src/components/AppProviders.js +++ b/src/components/AppProviders.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import Fullscreen from 'react-full-screen'; +import { FullScreen, useFullScreenHandle } from 'react-full-screen'; import { I18nextProvider } from 'react-i18next'; import { LiveAnnouncer } from 'react-aria-live'; import { @@ -12,6 +12,7 @@ import HTML5toTouch from 'react-dnd-multi-backend/dist/cjs/HTML5toTouch'; import { create } from 'jss'; import rtl from 'jss-rtl'; import createI18nInstance from '../i18n'; +import FullScreenContext from '../contexts/FullScreenContext'; /** * Allow applications to opt-out of (or provide their own) drag and drop context @@ -46,6 +47,25 @@ MaybeDndProvider.propTypes = { ]).isRequired, }; +/** + * Shim to inject the full screen handle into a context + */ +const FullScreenShim = ({ children }) => { + const handle = useFullScreenHandle(); + + return ( + <FullScreen handle={handle}> + <FullScreenContext.Provider value={handle}> + {children} + </FullScreenContext.Provider> + </FullScreen> + ); +}; + +FullScreenShim.propTypes = { + children: PropTypes.node.isRequired, +}; + /** * This component adds viewer-specific providers. * @prop {Object} manifests @@ -81,8 +101,8 @@ export class AppProviders extends Component { /** */ render() { const { - children, createGenerateClassNameOptions, isFullscreenEnabled, - setWorkspaceFullscreen, theme, translations, + children, createGenerateClassNameOptions, + theme, translations, dndManager, } = this.props; @@ -93,10 +113,7 @@ export class AppProviders extends Component { }); return ( - <Fullscreen - enabled={isFullscreenEnabled} - onChange={setWorkspaceFullscreen} - > + <FullScreenShim> <I18nextProvider i18n={this.i18n}> <LiveAnnouncer> <ThemeProvider @@ -113,7 +130,7 @@ export class AppProviders extends Component { </ThemeProvider> </LiveAnnouncer> </I18nextProvider> - </Fullscreen> + </FullScreenShim> ); } } @@ -122,9 +139,7 @@ AppProviders.propTypes = { children: PropTypes.node, createGenerateClassNameOptions: PropTypes.object, // eslint-disable-line react/forbid-prop-types dndManager: PropTypes.object, // eslint-disable-line react/forbid-prop-types - isFullscreenEnabled: PropTypes.bool, language: PropTypes.string.isRequired, - setWorkspaceFullscreen: PropTypes.func.isRequired, theme: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types translations: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types }; @@ -133,5 +148,4 @@ AppProviders.defaultProps = { children: null, createGenerateClassNameOptions: {}, dndManager: undefined, - isFullscreenEnabled: false, }; diff --git a/src/components/FullScreenButton.js b/src/components/FullScreenButton.js index d51a4509c0be27d07e8916b9f1498f28a2b6c24c..40361176c5536575f35d0ae51546c91369e9a25f 100644 --- a/src/components/FullScreenButton.js +++ b/src/components/FullScreenButton.js @@ -3,6 +3,8 @@ import FullscreenIcon from '@material-ui/icons/FullscreenSharp'; import FullscreenExitIcon from '@material-ui/icons/FullscreenExitSharp'; import PropTypes from 'prop-types'; import MiradorMenuButton from '../containers/MiradorMenuButton'; +import FullScreenContext from '../contexts/FullScreenContext'; + /** */ export class FullScreenButton extends Component { @@ -12,29 +14,30 @@ export class FullScreenButton extends Component { */ render() { const { - className, isFullscreenEnabled, setWorkspaceFullscreen, t, + className, t, } = this.props; return ( - <MiradorMenuButton - className={className} - aria-label={isFullscreenEnabled ? t('exitFullScreen') : t('workspaceFullScreen')} - onClick={() => setWorkspaceFullscreen(!isFullscreenEnabled)} - > - {isFullscreenEnabled ? <FullscreenExitIcon /> : <FullscreenIcon />} - </MiradorMenuButton> + <FullScreenContext.Consumer> + { handle => ( + <MiradorMenuButton + className={className} + aria-label={handle.active ? t('exitFullScreen') : t('workspaceFullScreen')} + onClick={handle.active ? handle.exit : handle.enter} + > + {handle.active ? <FullscreenExitIcon /> : <FullscreenIcon />} + </MiradorMenuButton> + )} + </FullScreenContext.Consumer> ); } } FullScreenButton.propTypes = { className: PropTypes.string, - isFullscreenEnabled: PropTypes.bool, - setWorkspaceFullscreen: PropTypes.func.isRequired, t: PropTypes.func, }; FullScreenButton.defaultProps = { className: undefined, - isFullscreenEnabled: false, t: key => key, }; diff --git a/src/containers/AppProviders.js b/src/containers/AppProviders.js index 58c3731855565eabc6a1e0b5205beec5a0b8b086..22e8a3a06d4381880ecb305bea240a6df748ac1b 100644 --- a/src/containers/AppProviders.js +++ b/src/containers/AppProviders.js @@ -1,8 +1,7 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withPlugins } from '../extend/withPlugins'; -import * as actions from '../state/actions'; -import { getConfig, getTheme, getFullScreenEnabled } from '../state/selectors'; +import { getConfig, getTheme } from '../state/selectors'; import { AppProviders } from '../components/AppProviders'; /** @@ -13,24 +12,14 @@ import { AppProviders } from '../components/AppProviders'; const mapStateToProps = state => ( { createGenerateClassNameOptions: getConfig(state).createGenerateClassNameOptions, - isFullscreenEnabled: getFullScreenEnabled(state), language: getConfig(state).language, theme: getTheme(state), translations: getConfig(state).translations, } ); -/** - * mapDispatchToProps - used to hook up connect to action creators - * @memberof App - * @private - */ -const mapDispatchToProps = { - setWorkspaceFullscreen: actions.setWorkspaceFullscreen, -}; - const enhance = compose( - connect(mapStateToProps, mapDispatchToProps), + connect(mapStateToProps), withPlugins('AppProviders'), ); diff --git a/src/containers/FullScreenButton.js b/src/containers/FullScreenButton.js index cf5cf644d87b9255dc6ec5072c0c021c5218717b..529579d68411be5959abe60e461ddd24358511c8 100644 --- a/src/containers/FullScreenButton.js +++ b/src/containers/FullScreenButton.js @@ -2,8 +2,6 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; import { withPlugins } from '../extend/withPlugins'; -import * as actions from '../state/actions'; -import { getFullScreenEnabled } from '../state/selectors'; import { FullScreenButton } from '../components/FullScreenButton'; /** @@ -11,16 +9,14 @@ import { FullScreenButton } from '../components/FullScreenButton'; * @memberof FullScreenButton * @private */ -const mapStateToProps = state => ({ - isFullscreenEnabled: getFullScreenEnabled(state), -}); +const mapStateToProps = _state => ({}); /** * mapDispatchToProps - used to hook up connect to action creators * @memberof ManifestListItem * @private */ -const mapDispatchToProps = { setWorkspaceFullscreen: actions.setWorkspaceFullscreen }; +const mapDispatchToProps = {}; const enhance = compose( withTranslation(), diff --git a/src/contexts/FullScreenContext.js b/src/contexts/FullScreenContext.js new file mode 100644 index 0000000000000000000000000000000000000000..9c0f123d0a1f16a92720df747903cfc70a897a63 --- /dev/null +++ b/src/contexts/FullScreenContext.js @@ -0,0 +1,5 @@ +import { createContext } from 'react'; + +const FullScreenContext = createContext(); + +export default FullScreenContext; diff --git a/src/state/actions/workspace.js b/src/state/actions/workspace.js index 0d1d8241b80441b966526e70c0e8ed0eb7f5f4b3..5500e563bc82c79bb3975f48474e99c14771f4d5 100644 --- a/src/state/actions/workspace.js +++ b/src/state/actions/workspace.js @@ -9,16 +9,6 @@ export function updateWorkspace(config) { return { config, type: ActionTypes.UPDATE_WORKSPACE }; } -/** - * setWorkspaceFullscreen - action creator - * - * @param {Boolean} isFullscreenEnabled - * @memberof ActionCreators - */ -export function setWorkspaceFullscreen(isFullscreenEnabled) { - return { isFullscreenEnabled, type: ActionTypes.SET_WORKSPACE_FULLSCREEN }; -} - /** * toggleZoomControls - action creator * @param {Boolean} showZoomControls