diff --git a/__tests__/integration/mirador/plugins/add.html b/__tests__/integration/mirador/plugins/add.html index 651a54edac5defda98a17bf7404dd8ffa7864f59..d50ca8046d8881c31f8962580a8613104e4b3781 100644 --- a/__tests__/integration/mirador/plugins/add.html +++ b/__tests__/integration/mirador/plugins/add.html @@ -14,7 +14,7 @@ <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script type="text/babel"> - const config = { id: 'mirador' }; + const config = { id: 'mirador', windows: [{manifestId: 'https://purl.stanford.edu/hg676jb4964/iiif/manifest'}] }; const AddPluginComponentA = (props) => ( <div id="add-plugin-component-a"> @@ -34,6 +34,27 @@ </div> ); + const CustomIcon = () => ( + <svg className="umbrella" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" aria-labelledby="title"> + <title id="title">Umbrella Icon</title> + <path d="M27 14h5c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2v0zM27 14c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2v0 14c0 1.112-0.895 2-2 2-1.112 0-2-0.896-2-2.001v-1.494c0-0.291 0.224-0.505 0.5-0.505 0.268 0 0.5 0.226 0.5 0.505v1.505c0 0.547 0.444 0.991 1 0.991 0.552 0 1-0.451 1-0.991v-14.009c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-1.105-1.119-2-2.5-2s-2.5 0.895-2.5 2c0-5.415 6.671-9.825 15-9.995v-1.506c0-0.283 0.224-0.499 0.5-0.499 0.268 0 0.5 0.224 0.5 0.499v1.506c8.329 0.17 15 4.58 15 9.995h-5z"/> + </svg> + ); + + const WrapPluginComponent = (plugin) => { + return ( + <div id='wrapped-plugin-with-adds'> + <plugin.TargetComponent menuIcon={<CustomIcon/>} {...plugin.targetProps} {...plugin} /> + </div> + ) + } + + const AddPluginComponentE = (props) => ( + <div id="add-plugin-component-e"> + <input value="hello world"/> + </div> + ); + const addPluginA = { target: 'WorkspaceControlPanelButtons', mode: 'add', @@ -51,8 +72,20 @@ mode: 'add', component: AddPluginComponentC, }; + + const wrapPluginD = { + target: 'WindowTopBarPluginMenu', + mode: 'wrap', + component: WrapPluginComponent, + }; + + const addPluginE = { + target: 'WindowTopBarPluginArea', + mode: 'add', + component: AddPluginComponentE, + }; - const miradorInstance = Mirador.viewer(config, [addPluginA, addPluginB, addPluginC]); + const miradorInstance = Mirador.viewer(config, [addPluginA, addPluginB, addPluginC, wrapPluginD, addPluginE]); </script> </body> diff --git a/__tests__/integration/mirador/plugins/add.test.js b/__tests__/integration/mirador/plugins/add.test.js index 3c3550c5f31bd3bd20fdd365070763eec714c7a8..03889a9586fe3b340397dfd7d6e69b97c04be3b5 100644 --- a/__tests__/integration/mirador/plugins/add.test.js +++ b/__tests__/integration/mirador/plugins/add.test.js @@ -10,4 +10,10 @@ describe('add two plugins to <WorkspaceControlPanelButtons>', () => { await expect(page).toMatchElement('#add-plugin-component-a'); await expect(page).toMatchElement('#add-plugin-component-b'); }); + + it('wrapped and added plugins will be added to <WindowTopBarPluginMenu>', async () => { + await expect(page).toClick('#wrapped-plugin-with-adds button'); + await expect(page).toMatchElement('#add-plugin-component-c'); + await expect(page).toMatchElement('#wrapped-plugin-with-adds'); + }); }); diff --git a/__tests__/integration/mirador/plugins/priority.test.js b/__tests__/integration/mirador/plugins/priority.test.js index ecd275051bb007c51d74fbfbf0a0b86ae6cb0209..3f069fa72dc1c7699e311664430fc020b80216c6 100644 --- a/__tests__/integration/mirador/plugins/priority.test.js +++ b/__tests__/integration/mirador/plugins/priority.test.js @@ -9,7 +9,7 @@ describe('try to apply 2 add plugins and 2 wrap plugins to <WorkspaceControlPane it('only apply the first wrap plugin', async () => { await expect(page).toMatchElement('#wrap-plugin-component-a'); await expect(page).not.toMatchElement('#wrap-plugin-component-b'); - await expect(page).not.toMatchElement('#add-plugin-component-a'); - await expect(page).not.toMatchElement('#add-plugin-component-b'); + await expect(page).toMatchElement('#add-plugin-component-a'); + await expect(page).toMatchElement('#add-plugin-component-b'); }); }); diff --git a/__tests__/src/components/WindowTopBar.test.js b/__tests__/src/components/WindowTopBar.test.js index 425f1a012ed72689eff687145baf312b21226ec4..92c832beb115b1e2d9d0ee7c22157b2ee555cfa6 100644 --- a/__tests__/src/components/WindowTopBar.test.js +++ b/__tests__/src/components/WindowTopBar.test.js @@ -5,6 +5,7 @@ import Toolbar from '@material-ui/core/Toolbar'; import AppBar from '@material-ui/core/AppBar'; import WindowTopMenuButton from '../../../src/containers/WindowTopMenuButton'; +import WindowTopBarPluginArea from '../../../src/containers/WindowTopBarPluginArea'; import WindowTopBarPluginMenu from '../../../src/containers/WindowTopBarPluginMenu'; import WindowTopBarTitle from '../../../src/containers/WindowTopBarTitle'; import MiradorMenuButton from '../../../src/containers/MiradorMenuButton'; @@ -35,6 +36,7 @@ describe('WindowTopBar', () => { expect(wrapper.find(Toolbar).length).toBe(1); expect(wrapper.find(MiradorMenuButton).length).toBe(3); expect(wrapper.find(WindowTopBarTitle).length).toBe(1); + expect(wrapper.find(WindowTopBarPluginArea).length).toBe(1); expect(wrapper.find(WindowTopBarPluginMenu).length).toBe(1); expect(wrapper.find(WindowTopMenuButton).length).toBe(1); expect(wrapper.find(FullScreenButton).length).toBe(0); diff --git a/__tests__/src/components/WindowTopBarPluginArea.test.js b/__tests__/src/components/WindowTopBarPluginArea.test.js new file mode 100644 index 0000000000000000000000000000000000000000..31e9e2d33f9ef9721752db628df21d57e53d419d --- /dev/null +++ b/__tests__/src/components/WindowTopBarPluginArea.test.js @@ -0,0 +1,9 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { WindowTopBarPluginArea } from '../../../src/components/WindowTopBarPluginArea'; +import { PluginHook } from '../../../src/components/PluginHook'; + +it('renders the component', () => { + const wrapper = shallow(<WindowTopBarPluginArea />); + expect(wrapper.find(PluginHook).length).toBe(1); +}); diff --git a/__tests__/src/extend/withPlugins.test.js b/__tests__/src/extend/withPlugins.test.js index 23e5bdbcee643c0c60253ea3bab0e98ce75ab826..2e3ecf2cdc8de9b6b775ec00684992756b27e17e 100644 --- a/__tests__/src/extend/withPlugins.test.js +++ b/__tests__/src/extend/withPlugins.test.js @@ -85,7 +85,7 @@ describe('PluginHoc: if add plugins exist but no wrap plugin', () => { }); describe('PluginHoc: if wrap plugins AND add plugins exist for target', () => { - it('renders the first wrap plugin, ignores add plugins', () => { + it('renders the first wrap plugin, ignores add plugins if props are not passed through', () => { /** */ const WrapPluginComponentA = props => <div>look i am a plugin</div>; /** */ const WrapPluginComponentB = props => <div>look i am a plugin</div>; /** */ const AddPluginComponentA = props => <div>look i am a plugin</div>; @@ -106,4 +106,27 @@ describe('PluginHoc: if wrap plugins AND add plugins exist for target', () => { expect(hoc.find(WrapPluginComponentA).length).toBe(1); expect(hoc.find(Target).length).toBe(0); }); + it('renders the first wrap plugin, renders add plugins if plugin/props are passed through', () => { + /** */ const WrapPluginComponentA = plugin => ( + <plugin.TargetComponent {...plugin.targetProps} {...plugin} /> + ); + /** */ const WrapPluginComponentB = props => <div>look i am a plugin</div>; + /** */ const AddPluginComponentA = props => <div>look i am a plugin</div>; + /** */ const AddPluginComponentB = props => <div>look i am a plugin</div>; + const plugins = { + Target: { + add: [ + { component: AddPluginComponentA, mode: 'add', target: 'Target' }, + { component: AddPluginComponentB, mode: 'add', target: 'Target' }, + ], + wrap: [ + { component: WrapPluginComponentA, mode: 'wrap', target: 'Target' }, + { component: WrapPluginComponentB, mode: 'wrap', target: 'Target' }, + ], + }, + }; + const hoc = createPluginHoc(plugins); + expect(hoc.find(WrapPluginComponentA).length).toBe(1); + expect(hoc.find(Target).length).toBe(1); + }); }); diff --git a/src/components/WindowTopBar.js b/src/components/WindowTopBar.js index 45aeaf3ffb8ada9b0de147c235b89e845c4aade3..940017674495c9d4fd74ccb94214e0eccb8f4381 100644 --- a/src/components/WindowTopBar.js +++ b/src/components/WindowTopBar.js @@ -6,6 +6,7 @@ import Toolbar from '@material-ui/core/Toolbar'; import AppBar from '@material-ui/core/AppBar'; import classNames from 'classnames'; import WindowTopMenuButton from '../containers/WindowTopMenuButton'; +import WindowTopBarPluginArea from '../containers/WindowTopBarPluginArea'; import WindowTopBarPluginMenu from '../containers/WindowTopBarPluginMenu'; import WindowTopBarTitle from '../containers/WindowTopBarTitle'; import MiradorMenuButton from '../containers/MiradorMenuButton'; @@ -58,6 +59,7 @@ export class WindowTopBar extends Component { {allowTopMenuButton && ( <WindowTopMenuButton className={ns('window-menu-btn')} windowId={windowId} /> )} + <WindowTopBarPluginArea windowId={windowId} /> <WindowTopBarPluginMenu windowId={windowId} /> {allowMaximize && ( <MiradorMenuButton diff --git a/src/components/WindowTopBarPluginArea.js b/src/components/WindowTopBarPluginArea.js new file mode 100644 index 0000000000000000000000000000000000000000..ce5ec626f4317974bf6fd0dbf143fe6c41c219f0 --- /dev/null +++ b/src/components/WindowTopBarPluginArea.js @@ -0,0 +1,16 @@ +import React, { Component } from 'react'; +import { PluginHook } from './PluginHook'; + +/** + * + */ +export class WindowTopBarPluginArea extends Component { + /** */ + render() { + return ( + <> + <PluginHook {...this.props} /> + </> + ); + } +} diff --git a/src/components/WindowTopBarPluginMenu.js b/src/components/WindowTopBarPluginMenu.js index f9b26d3031396b8c60d22500766d9fe4279bd2a9..f5704153993cd11f0404661147b86ff8ac50419d 100644 --- a/src/components/WindowTopBarPluginMenu.js +++ b/src/components/WindowTopBarPluginMenu.js @@ -45,7 +45,7 @@ export class WindowTopBarPluginMenu extends Component { */ render() { const { - classes, containerId, PluginComponents, t, windowId, + classes, containerId, PluginComponents, t, windowId, menuIcon, } = this.props; const { anchorEl } = this.state; @@ -60,7 +60,7 @@ export class WindowTopBarPluginMenu extends Component { className={anchorEl ? classes.ctrlBtnSelected : null} onClick={this.handleMenuClick} > - <MoreVertIcon /> + {menuIcon} </MiradorMenuButton> <Menu @@ -91,6 +91,7 @@ WindowTopBarPluginMenu.propTypes = { ctrlBtnSelected: PropTypes.string, }), containerId: PropTypes.string.isRequired, + menuIcon: PropTypes.element, PluginComponents: PropTypes.arrayOf( PropTypes.node, ), @@ -101,5 +102,6 @@ WindowTopBarPluginMenu.propTypes = { WindowTopBarPluginMenu.defaultProps = { classes: {}, + menuIcon: <MoreVertIcon />, PluginComponents: [], }; diff --git a/src/containers/WindowTopBarPluginArea.js b/src/containers/WindowTopBarPluginArea.js new file mode 100644 index 0000000000000000000000000000000000000000..4bcdc88f5ae95da56b721b52d856012ded348c0a --- /dev/null +++ b/src/containers/WindowTopBarPluginArea.js @@ -0,0 +1,19 @@ +import { compose } from 'redux'; +import { connect } from 'react-redux'; +import { withTranslation } from 'react-i18next'; +import { withStyles } from '@material-ui/core'; +import { withPlugins } from '../extend/withPlugins'; +import { WindowTopBarPluginArea } from '../components/WindowTopBarPluginArea'; + +/** + */ +const styles = {}; + +const enhance = compose( + withTranslation(), + withStyles(styles), + connect(null, null), + withPlugins('WindowTopBarPluginArea'), +); + +export default enhance(WindowTopBarPluginArea); diff --git a/src/extend/withPlugins.js b/src/extend/withPlugins.js index 59e92997967a57f777971b1df6b18dd29fd61a39..f7033bac4fe2addafe701172313255074806f80a 100644 --- a/src/extend/withPlugins.js +++ b/src/extend/withPlugins.js @@ -26,6 +26,19 @@ function _withPlugins(targetName, TargetComponent) { // eslint-disable-line no-u return <TargetComponent {...passDownProps} />; } + if (!isEmpty(plugins.wrap) && !isEmpty(plugins.add)) { + const WrapPluginComponent = plugins.wrap[0].component; + const AddPluginComponents = plugins.add.map(plugin => plugin.component); + return ( + <WrapPluginComponent + targetProps={passDownProps} + {...passDownProps} + PluginComponents={AddPluginComponents} + TargetComponent={TargetComponent} + /> + ); + } + if (!isEmpty(plugins.wrap)) { const PluginComponent = plugins.wrap[0].component; return <PluginComponent targetProps={passDownProps} TargetComponent={TargetComponent} />;