diff --git a/src/components/asset-panel/asset-panel.css b/src/components/asset-panel/asset-panel.css index 0cfb9a1c3598cf480e18a229eeb4a891497b2fb7..9a999057b57a29f12b8e288365605a814b5864f1 100644 --- a/src/components/asset-panel/asset-panel.css +++ b/src/components/asset-panel/asset-panel.css @@ -16,4 +16,5 @@ flex-grow: 1; flex-shrink: 1; border-left: 1px solid $ui-black-transparent; + overflow-y: auto; } 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.css b/src/components/gui/gui.css index 72670e031095acc3f02f9466cf892f13e1996710..2e4e7b846811c8fced93abbc6c004c19fde51ad8 100644 --- a/src/components/gui/gui.css +++ b/src/components/gui/gui.css @@ -132,7 +132,6 @@ .tabs { position: relative; flex-grow: 1; - flex-shrink: 0; display: flex; flex-direction: column; } @@ -140,7 +139,6 @@ .tab-panel { position: relative; flex-grow: 1; - flex-shrink: 0; display: none; } diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index 0f6cc8433169fcb181680c5f75f58b429e227c00..66a08f64f3d611c7d51a93a75df274710a7b0250 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,7 +46,7 @@ const GUIComponent = props => { const { activeTabIndex, basePath, - backpackVisible, + backpackOptions, blocksTabVisible, cardsVisible, children, @@ -205,8 +205,8 @@ const GUIComponent = props => { {soundsTabVisible ? <SoundTab vm={vm} /> : null} </TabPanel> </Tabs> - {backpackVisible ? ( - <Backpack /> + {backpackOptions.visible ? ( + <Backpack host={backpackOptions.host} /> ) : null} </Box> @@ -227,7 +227,10 @@ const GUIComponent = props => { }; GUIComponent.propTypes = { activeTabIndex: PropTypes.number, - backpackVisible: PropTypes.null, + backpackOptions: PropTypes.shape({ + host: PropTypes.string, + visible: PropTypes.bool + }), basePath: PropTypes.string, blocksTabVisible: PropTypes.bool, cardsVisible: PropTypes.bool, @@ -250,7 +253,10 @@ GUIComponent.propTypes = { vm: PropTypes.instanceOf(VM).isRequired }; GUIComponent.defaultProps = { - backpackVisible: false, + backpackOptions: { + host: null, + visible: false + }, basePath: './' }; export default injectIntl(GUIComponent); 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..9c0879f2c67f3ce92d1a1f3c60ef53ddbed1b7cd 100644 --- a/src/playground/index.jsx +++ b/src/playground/index.jsx @@ -24,4 +24,13 @@ 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; + +const backpackOptions = { + visible: true, + host: backpackHost +}; + +ReactDOM.render(<WrappedGui backpackOptions={backpackOptions} />, appTarget); diff --git a/test/integration/backpack.test.js b/test/integration/backpack.test.js new file mode 100644 index 0000000000000000000000000000000000000000..9694e67ba2cc4b3aca21d96a2c0145285ec0405b --- /dev/null +++ b/test/integration/backpack.test.js @@ -0,0 +1,47 @@ +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"]'); + + // Try activating the backpack from the costumes tab to make sure it isn't pushed off + await clickText('Costumes'); + + // 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([]); + }); +});