diff --git a/src/components/backpack/backpack.css b/src/components/backpack/backpack.css index be60ca9779ae504487bbef3cbb5e520573495dcc..fc50063869622ab5934f947dcbeace7e1c456c34 100644 --- a/src/components/backpack/backpack.css +++ b/src/components/backpack/backpack.css @@ -17,4 +17,21 @@ color: $text-primary; transition: 0.2s; cursor: pointer; + user-select: none; +} + +.backpack-list { + position: relative; + display: flex; + flex-direction: row; + align-items: center; + border-right: 1px solid $ui-black-transparent; + min-height: 6rem; +} + +.empty-message { + width: 100%; + text-align: center; + font-size: 0.85rem; + color: $text-primary; } diff --git a/src/components/backpack/backpack.jsx b/src/components/backpack/backpack.jsx index 6720faaf57d850fda52d29523d96ec72ac04816f..74798b8a7a07d038e5d62dcac2fb2e0a2f2df703 100644 --- a/src/components/backpack/backpack.jsx +++ b/src/components/backpack/backpack.jsx @@ -1,20 +1,57 @@ import React from 'react'; +import PropTypes from 'prop-types'; import {FormattedMessage} from 'react-intl'; import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; + import styles from './backpack.css'; -const Backpack = () => ( +const Backpack = ({expanded, onToggle}) => ( <div className={styles.backpackContainer}> - <div className={styles.backpackHeader}> - <ComingSoonTooltip place="top"> + <div + className={styles.backpackHeader} + onClick={onToggle} + > + {onToggle ? ( <FormattedMessage defaultMessage="Backpack" description="Button to open the backpack" id="gui.backpack.header" /> - </ComingSoonTooltip> + ) : ( + <ComingSoonTooltip + place="top" + tooltipId="backpack-tooltip" + > + <FormattedMessage + defaultMessage="Backpack" + description="Button to open the backpack" + id="gui.backpack.header" + /> + </ComingSoonTooltip> + )} </div> + {expanded ? ( + <div className={styles.backpackList}> + <div className={styles.emptyMessage}> + <FormattedMessage + defaultMessage="Backpack is empty" + description="Empty backpack message" + id="gui.backpack.emptyBackpack" + /> + </div> + </div> + ) : null} </div> ); +Backpack.propTypes = { + expanded: PropTypes.bool, + onToggle: PropTypes.func +}; + +Backpack.defaultProps = { + expanded: false, + onToggle: null +}; + export default Backpack; diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index 0f6cc8433169fcb181680c5f75f58b429e227c00..38c5551b3de6a337c88ffad4fbc743294cf6783c 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -15,8 +15,8 @@ import StageWrapper from '../../containers/stage-wrapper.jsx'; import Loader from '../loader/loader.jsx'; import Box from '../box/box.jsx'; import MenuBar from '../menu-bar/menu-bar.jsx'; -import Backpack from '../backpack/backpack.jsx'; +import Backpack from '../../containers/backpack.jsx'; import PreviewModal from '../../containers/preview-modal.jsx'; import ImportModal from '../../containers/import-modal.jsx'; import WebGlModal from '../../containers/webgl-modal.jsx'; @@ -46,6 +46,7 @@ const GUIComponent = props => { const { activeTabIndex, basePath, + backpackHost, backpackVisible, blocksTabVisible, cardsVisible, @@ -206,7 +207,7 @@ const GUIComponent = props => { </TabPanel> </Tabs> {backpackVisible ? ( - <Backpack /> + <Backpack host={backpackHost} /> ) : null} </Box> @@ -227,6 +228,7 @@ const GUIComponent = props => { }; GUIComponent.propTypes = { activeTabIndex: PropTypes.number, + backpackHost: PropTypes.string, backpackVisible: PropTypes.null, basePath: PropTypes.string, blocksTabVisible: PropTypes.bool, @@ -250,6 +252,7 @@ GUIComponent.propTypes = { vm: PropTypes.instanceOf(VM).isRequired }; GUIComponent.defaultProps = { + backpackHost: null, backpackVisible: false, basePath: './' }; diff --git a/src/containers/backpack.jsx b/src/containers/backpack.jsx new file mode 100644 index 0000000000000000000000000000000000000000..3af28f9e13325a764012bb4b100f401cbffc7655 --- /dev/null +++ b/src/containers/backpack.jsx @@ -0,0 +1,35 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import bindAll from 'lodash.bindall'; +import BackpackComponent from '../components/backpack/backpack.jsx'; + +class Backpack extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'handleToggle' + ]); + this.state = { + expanded: false, + contents: [] + }; + } + handleToggle () { + this.setState({expanded: !this.state.expanded}); + } + render () { + return ( + <BackpackComponent + contents={this.state.contents} + expanded={this.state.expanded} + onToggle={this.props.host ? this.handleToggle : null} + /> + ); + } +} + +Backpack.propTypes = { + host: PropTypes.string +}; + +export default Backpack; diff --git a/src/playground/index.jsx b/src/playground/index.jsx index 7bcadca5b4e4fb1f286294e1ff1c17616c71ec76..b6637425f8e57d6e7e35047f086823d53c9d2883 100644 --- a/src/playground/index.jsx +++ b/src/playground/index.jsx @@ -24,4 +24,11 @@ document.body.appendChild(appTarget); GUI.setAppElement(appTarget); const WrappedGui = HashParserHOC(AppStateHOC(GUI)); -ReactDOM.render(<WrappedGui backpackVisible />, appTarget); +// TODO a hack for testing the backpack, allow backpack host to be set by url param +const backpackHostMatches = window.location.href.match(/[?&]backpack_host=(.*)&?/); +const backpackHost = backpackHostMatches ? backpackHostMatches[1] : null; + +ReactDOM.render(<WrappedGui + backpackVisible + backpackHost={backpackHost} +/>, appTarget); diff --git a/test/integration/backpack.test.js b/test/integration/backpack.test.js new file mode 100644 index 0000000000000000000000000000000000000000..9eb5e5f9e5b0a1b518b68035e93f067e462539ef --- /dev/null +++ b/test/integration/backpack.test.js @@ -0,0 +1,43 @@ +import path from 'path'; +import SeleniumHelper from '../helpers/selenium-helper'; + +const { + clickText, + clickXpath, + getDriver, + getLogs, + loadUri +} = new SeleniumHelper(); + +const uri = path.resolve(__dirname, '../../build/index.html'); + +let driver; + +describe('Working with the how-to library', () => { + beforeAll(() => { + driver = getDriver(); + }); + + afterAll(async () => { + await driver.quit(); + }); + + test('Backpack is "Coming Soon" without backpack host param', async () => { + await loadUri(uri); + await clickXpath('//button[@title="tryit"]'); + // Check that the backpack header is visible and wrapped in a coming soon tooltip + await clickText('Backpack', '*[@data-for="backpack-tooltip"]'); + const logs = await getLogs(); + await expect(logs).toEqual([]); + }); + + test('Backpack can be expanded with backpack host param', async () => { + await loadUri(`${uri}?backpack_host=some-value`); + await clickXpath('//button[@title="tryit"]'); + // Check that the backpack header is visible and wrapped in a coming soon tooltip + await clickText('Backpack'); // Not wrapped in tooltip + await clickText('Backpack is empty'); // Make sure it can expand, is empty + const logs = await getLogs(); + await expect(logs).toEqual([]); + }); +});