diff --git a/src/components/menu-bar/confirm-replace-hoc.jsx b/src/components/menu-bar/confirm-replace-hoc.jsx
deleted file mode 100644
index 5631b800b..000000000
--- a/src/components/menu-bar/confirm-replace-hoc.jsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import {connect} from 'react-redux';
-import PropTypes from 'prop-types';
-import bindAll from 'lodash.bindall';
-import React from 'react';
-
-const ConfirmReplaceHOC = function (WrappedComponent) {
- class ConfirmReplaceProject extends React.PureComponent {
- constructor (props) {
- super(props);
-
- bindAll(this, [
- 'confirmReadyToReplaceProject'
- ]);
- }
-
- confirmReadyToReplaceProject (message) {
- let readyToReplaceProject = true;
- if (this.props.projectChanged && !this.props.canCreateNew) {
- readyToReplaceProject = confirm(message); // eslint-disable-line no-alert
- }
- return readyToReplaceProject;
- }
-
- render () {
- const {
- /* eslint-disable no-unused-vars */
- projectChanged,
- /* eslint-enable no-unused-vars */
- ...props
- } = this.props;
- return ();
- }
- }
-
- ConfirmReplaceProject.propTypes = {
- canCreateNew: PropTypes.bool,
- projectChanged: PropTypes.bool
- };
-
- const _mapStateToProps = state => ({
- projectChanged: state.scratchGui.projectChanged
- });
-
- return connect(_mapStateToProps)(ConfirmReplaceProject);
-};
-
-export default ConfirmReplaceHOC;
diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx
index 5c957525c..22cd2d919 100644
--- a/src/components/menu-bar/menu-bar.jsx
+++ b/src/components/menu-bar/menu-bar.jsx
@@ -28,7 +28,7 @@ import LoginDropdown from './login-dropdown.jsx';
import SB3Downloader from '../../containers/sb3-downloader.jsx';
import DeletionRestorer from '../../containers/deletion-restorer.jsx';
import TurboMode from '../../containers/turbo-mode.jsx';
-import ConfirmReplaceHOC from './confirm-replace-hoc.jsx';
+import MenuBarHOC from '../../containers/menu-bar-hoc.jsx';
import {openTipsLibrary} from '../../reducers/modals';
import {setPlayer} from '../../reducers/mode';
@@ -192,8 +192,8 @@ class MenuBar extends React.Component {
this.props.onRequestCloseFile();
}
handleClickSeeCommunity (waitForUpdate) {
- if (this.props.canSave && this.props.projectChanged) { // save before transitioning to project page
- this.props.autoUpdateProject();
+ if (this.props.shouldSaveBeforeTransition()) {
+ this.props.autoUpdateProject(); // save before transitioning to project page
waitForUpdate(true); // queue the transition to project page
} else {
waitForUpdate(false); // immediately transition to project page
@@ -705,6 +705,7 @@ MenuBar.propTypes = {
canSave: PropTypes.bool,
canShare: PropTypes.bool,
className: PropTypes.string,
+ confirmReadyToReplaceProject: PropTypes.func,
editMenuOpen: PropTypes.bool,
enableCommunity: PropTypes.bool,
fileMenuOpen: PropTypes.bool,
@@ -741,6 +742,7 @@ MenuBar.propTypes = {
projectTitle: PropTypes.string,
renderLogin: PropTypes.func,
sessionExists: PropTypes.bool,
+ shouldSaveBeforeTransition: PropTypes.func,
showComingSoon: PropTypes.bool,
username: PropTypes.string,
vm: PropTypes.instanceOf(VM).isRequired
@@ -794,7 +796,7 @@ const mapDispatchToProps = dispatch => ({
export default compose(
injectIntl,
- ConfirmReplaceHOC,
+ MenuBarHOC,
connect(
mapStateToProps,
mapDispatchToProps
diff --git a/src/containers/menu-bar-hoc.jsx b/src/containers/menu-bar-hoc.jsx
new file mode 100644
index 000000000..71e56fa89
--- /dev/null
+++ b/src/containers/menu-bar-hoc.jsx
@@ -0,0 +1,66 @@
+import {connect} from 'react-redux';
+import PropTypes from 'prop-types';
+import bindAll from 'lodash.bindall';
+import React from 'react';
+
+const MenuBarHOC = function (WrappedComponent) {
+ class MenuBarContainer extends React.PureComponent {
+ constructor (props) {
+ super(props);
+
+ bindAll(this, [
+ 'confirmReadyToReplaceProject',
+ 'shouldSaveBeforeTransition'
+ ]);
+ }
+ confirmReadyToReplaceProject (message) {
+ let readyToReplaceProject = true;
+ if (this.props.projectChanged && !this.props.canCreateNew) {
+ readyToReplaceProject = this.props.confirmWithMessage(message);
+ }
+ return readyToReplaceProject;
+ }
+ shouldSaveBeforeTransition () {
+ return (this.props.canSave && this.props.projectChanged);
+ }
+ render () {
+ const {
+ /* eslint-disable no-unused-vars */
+ projectChanged,
+ /* eslint-enable no-unused-vars */
+ ...props
+ } = this.props;
+ return ();
+ }
+ }
+
+ MenuBarContainer.propTypes = {
+ canCreateNew: PropTypes.bool,
+ canSave: PropTypes.bool,
+ confirmWithMessage: PropTypes.func,
+ projectChanged: PropTypes.bool
+ };
+ MenuBarContainer.defaultProps = {
+ // default to using standard js confirm
+ confirmWithMessage: message => (confirm(message)) // eslint-disable-line no-alert
+ };
+ const mapStateToProps = state => ({
+ projectChanged: state.scratchGui.projectChanged
+ });
+ const mapDispatchToProps = () => ({});
+ // Allow incoming props to override redux-provided props. Used to mock in tests.
+ const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
+ {}, stateProps, dispatchProps, ownProps
+ );
+ return connect(
+ mapStateToProps,
+ mapDispatchToProps,
+ mergeProps
+ )(MenuBarContainer);
+};
+
+export default MenuBarHOC;
diff --git a/test/unit/containers/menu-bar-hoc.test.jsx b/test/unit/containers/menu-bar-hoc.test.jsx
new file mode 100644
index 000000000..9099c4f10
--- /dev/null
+++ b/test/unit/containers/menu-bar-hoc.test.jsx
@@ -0,0 +1,164 @@
+import React from 'react';
+import {mount} from 'enzyme';
+import configureStore from 'redux-mock-store';
+import MenuBarHOC from '../../../src/containers/menu-bar-hoc.jsx';
+
+describe('Menu Bar HOC', () => {
+ const mockStore = configureStore();
+ let store;
+
+ beforeEach(() => {
+ store = mockStore({
+ scratchGui: {
+ projectChanged: true
+ }
+ });
+ });
+
+ test('Logged in user who IS owner and HAS changed project will NOT be prompted to save', () => {
+ const Component = () => (
);
+ const WrappedComponent = MenuBarHOC(Component);
+ const wrapper = mount(
+ (false)} // eslint-disable-line react/jsx-no-bind
+ store={store}
+ />
+ );
+ const child = wrapper.find(Component);
+ expect(child.props().projectChanged).toBeUndefined();
+ expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
+ });
+
+ test('Logged in user who IS owner and has NOT changed project will NOT be prompted to save', () => {
+ const Component = () => ();
+ const WrappedComponent = MenuBarHOC(Component);
+ const wrapper = mount(
+ (false)} // eslint-disable-line react/jsx-no-bind
+ projectChanged={false}
+ store={store}
+ />
+ );
+ const child = wrapper.find(Component);
+ expect(child.props().projectChanged).toBeUndefined();
+ expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
+ });
+
+ test('Logged in user who is NOT owner and HAS changed project will NOT be prompted to save', () => {
+ const Component = () => ();
+ const WrappedComponent = MenuBarHOC(Component);
+ const wrapper = mount(
+ (false)} // eslint-disable-line react/jsx-no-bind
+ store={store}
+ />
+ );
+ const child = wrapper.find(Component);
+ expect(child.props().projectChanged).toBeUndefined();
+ expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
+ });
+
+ test('Logged OUT user who HAS changed project WILL be prompted to save', () => {
+ const Component = () => ();
+ const WrappedComponent = MenuBarHOC(Component);
+ const wrapper = mount(
+ (false)} // eslint-disable-line react/jsx-no-bind
+ store={store}
+ />
+ );
+ const child = wrapper.find(Component);
+ expect(child.props().projectChanged).toBeUndefined();
+ expect(child.props().confirmReadyToReplaceProject('message')).toBe(false);
+ });
+
+ test('Logged OUT user who has NOT changed project WILL NOT be prompted to save', () => {
+ const Component = () => ();
+ const WrappedComponent = MenuBarHOC(Component);
+ const wrapper = mount(
+ (false)} // eslint-disable-line react/jsx-no-bind
+ projectChanged={false}
+ store={store}
+ />
+ );
+ const child = wrapper.find(Component);
+ expect(child.props().projectChanged).toBeUndefined();
+ expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
+ });
+
+ test('Logged in user who IS owner and HAS changed project SHOULD save before transition to project page', () => {
+ const Component = () => ();
+ const WrappedComponent = MenuBarHOC(Component);
+ const wrapper = mount(
+
+ );
+ const child = wrapper.find(Component);
+ expect(child.props().projectChanged).toBeUndefined();
+ expect(child.props().shouldSaveBeforeTransition()).toBe(true);
+ });
+
+ test('Logged in user who IS owner and has NOT changed project should NOT save before transition', () => {
+ const Component = () => ();
+ const WrappedComponent = MenuBarHOC(Component);
+ const wrapper = mount(
+
+ );
+ const child = wrapper.find(Component);
+ expect(child.props().projectChanged).toBeUndefined();
+ expect(child.props().shouldSaveBeforeTransition()).toBe(false);
+ });
+
+ test('Logged in user who is NOT owner and HAS changed project should NOT save before transition', () => {
+ const Component = () => ();
+ const WrappedComponent = MenuBarHOC(Component);
+ const wrapper = mount(
+
+ );
+ const child = wrapper.find(Component);
+ expect(child.props().projectChanged).toBeUndefined();
+ expect(child.props().shouldSaveBeforeTransition()).toBe(false);
+ });
+
+ test('Logged in user who is NOT owner and has NOT changed project should NOT save before transition', () => {
+ const Component = () => ();
+ const WrappedComponent = MenuBarHOC(Component);
+ const wrapper = mount(
+
+ );
+ const child = wrapper.find(Component);
+ expect(child.props().projectChanged).toBeUndefined();
+ expect(child.props().shouldSaveBeforeTransition()).toBe(false);
+ });
+
+});