diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 8524b2ba36cdf19ebe818ece46fa79021042dc64..9846eed7566356ed1e5395def9b59388bb870958 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -105,7 +105,6 @@ const GUIComponent = props => {
         onRequestCloseTelemetryModal,
         onSeeCommunity,
         onShare,
-        onStartSelectingFileUpload,
         onTelemetryModalCancel,
         onTelemetryModalOptIn,
         onTelemetryModalOptOut,
@@ -227,7 +226,6 @@ const GUIComponent = props => {
                     onProjectTelemetryEvent={onProjectTelemetryEvent}
                     onSeeCommunity={onSeeCommunity}
                     onShare={onShare}
-                    onStartSelectingFileUpload={onStartSelectingFileUpload}
                     onToggleLoginOpen={onToggleLoginOpen}
                 />
                 <Box className={styles.bodyWrapper}>
@@ -401,7 +399,6 @@ GUIComponent.propTypes = {
     onRequestCloseTelemetryModal: PropTypes.func,
     onSeeCommunity: PropTypes.func,
     onShare: PropTypes.func,
-    onStartSelectingFileUpload: PropTypes.func,
     onTabSelect: PropTypes.func,
     onTelemetryModalCancel: PropTypes.func,
     onTelemetryModalOptIn: PropTypes.func,
diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx
index 05a5294bc30eadfe41a723f7712bc4b3fcb0e837..98bc128a92f11fe272d674513930e53f897d58bb 100644
--- a/src/components/menu-bar/menu-bar.jsx
+++ b/src/components/menu-bar/menu-bar.jsx
@@ -17,6 +17,7 @@ import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx';
 import Divider from '../divider/divider.jsx';
 import LanguageSelector from '../../containers/language-selector.jsx';
 import SaveStatus from './save-status.jsx';
+import SBFileUploader from '../../containers/sb-file-uploader.jsx';
 import ProjectWatcher from '../../containers/project-watcher.jsx';
 import MenuBarMenu from './menu-bar-menu.jsx';
 import {MenuItem, MenuSection} from '../menu/menu.jsx';
@@ -391,11 +392,22 @@ class MenuBar extends React.Component {
                                         </MenuSection>
                                     )}
                                     <MenuSection>
-                                        <MenuItem
-                                            onClick={this.props.onStartSelectingFileUpload}
+                                        <SBFileUploader
+                                            canSave={this.props.canSave}
+                                            userOwnsProject={this.props.userOwnsProject}
                                         >
-                                            {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)}
-                                        </MenuItem>
+                                            {(className, renderFileInput, handleLoadProject) => (
+                                                <MenuItem
+                                                    className={className}
+                                                    onClick={handleLoadProject}
+                                                >
+                                                    {/* eslint-disable max-len */}
+                                                    {this.props.intl.formatMessage(sharedMessages.loadFromComputerTitle)}
+                                                    {/* eslint-enable max-len */}
+                                                    {renderFileInput()}
+                                                </MenuItem>
+                                            )}
+                                        </SBFileUploader>
                                         <SB3Downloader>{(className, downloadProjectCallback) => (
                                             <MenuItem
                                                 className={className}
@@ -731,7 +743,6 @@ MenuBar.propTypes = {
     onRequestCloseLogin: PropTypes.func,
     onSeeCommunity: PropTypes.func,
     onShare: PropTypes.func,
-    onStartSelectingFileUpload: PropTypes.func,
     onToggleLoginOpen: PropTypes.func,
     projectTitle: PropTypes.string,
     renderLogin: PropTypes.func,
diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index c10df0fb3e9bf73c3ca60d342f8d4f155a6e61c0..314c38893518b0a8e5a8bce6a847dc008a6ac5b6 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -27,7 +27,6 @@ import {
 
 import FontLoaderHOC from '../lib/font-loader-hoc.jsx';
 import LocalizationHOC from '../lib/localization-hoc.jsx';
-import SBFileUploaderHOC from '../lib/sb-file-uploader-hoc.jsx';
 import ProjectFetcherHOC from '../lib/project-fetcher-hoc.jsx';
 import TitledHOC from '../lib/titled-hoc.jsx';
 import ProjectSaverHOC from '../lib/project-saver-hoc.jsx';
@@ -182,7 +181,6 @@ const WrappedGui = compose(
     ProjectSaverHOC,
     vmListenerHOC,
     vmManagerHOC,
-    SBFileUploaderHOC,
     cloudManagerHOC
 )(ConnectedGUI);
 
diff --git a/src/containers/sb-file-uploader.jsx b/src/containers/sb-file-uploader.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..2019f719e978a9c485b540488f873cb8046644ee
--- /dev/null
+++ b/src/containers/sb-file-uploader.jsx
@@ -0,0 +1,226 @@
+import bindAll from 'lodash.bindall';
+import PropTypes from 'prop-types';
+import React from 'react';
+import {connect} from 'react-redux';
+import {defineMessages, injectIntl, intlShape} from 'react-intl';
+import {setProjectTitle} from '../reducers/project-title';
+
+import log from '../lib/log';
+import sharedMessages from '../lib/shared-messages';
+
+import {
+    LoadingStates,
+    getIsLoadingUpload,
+    getIsShowingWithoutId,
+    onLoadedProject,
+    requestProjectUpload
+} from '../reducers/project-state';
+
+import {
+    openLoadingProject,
+    closeLoadingProject
+} from '../reducers/modals';
+import {
+    closeFileMenu
+} from '../reducers/menus';
+
+/**
+ * SBFileUploader component passes a file input, load handler and props to its child.
+ * It expects this child to be a function with the signature
+ *     function (renderFileInput, handleLoadProject) {}
+ * The component can then be used to attach project loading functionality
+ * to any other component:
+ *
+ * <SBFileUploader>{(className, renderFileInput, handleLoadProject) => (
+ *     <MyCoolComponent
+ *         className={className}
+ *         onClick={handleLoadProject}
+ *     >
+ *         {renderFileInput()}
+ *     </MyCoolComponent>
+ * )}</SBFileUploader>
+ */
+
+const messages = defineMessages({
+    loadError: {
+        id: 'gui.projectLoader.loadError',
+        defaultMessage: 'The project file that was selected failed to load.',
+        description: 'An error that displays when a local project file fails to load.'
+    }
+});
+
+class SBFileUploader extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'getProjectTitleFromFilename',
+            'renderFileInput',
+            'setFileInput',
+            'handleChange',
+            'handleClick',
+            'onload',
+            'resetFileInput'
+        ]);
+    }
+    componentWillMount () {
+        this.reader = new FileReader();
+        this.reader.onload = this.onload;
+        this.resetFileInput();
+    }
+    componentDidUpdate (prevProps) {
+        if (this.props.isLoadingUpload && !prevProps.isLoadingUpload && this.fileToUpload && this.reader) {
+            this.reader.readAsArrayBuffer(this.fileToUpload);
+        }
+    }
+    componentWillUnmount () {
+        this.reader = null;
+        this.resetFileInput();
+    }
+    resetFileInput () {
+        this.fileToUpload = null;
+        if (this.fileInput) {
+            this.fileInput.value = null;
+        }
+    }
+    getProjectTitleFromFilename (fileInputFilename) {
+        if (!fileInputFilename) return '';
+        // only parse title with valid scratch project extensions
+        // (.sb, .sb2, and .sb3)
+        const matches = fileInputFilename.match(/^(.*)\.sb[23]?$/);
+        if (!matches) return '';
+        return matches[1].substring(0, 100); // truncate project title to max 100 chars
+    }
+    // called when user has finished selecting a file to upload
+    handleChange (e) {
+        const {
+            intl,
+            isShowingWithoutId,
+            loadingState,
+            projectChanged,
+            userOwnsProject
+        } = this.props;
+
+        const thisFileInput = e.target;
+        if (thisFileInput.files) { // Don't attempt to load if no file was selected
+            this.fileToUpload = thisFileInput.files[0];
+
+            // If user owns the project, or user has changed the project,
+            // we must confirm with the user that they really intend to replace it.
+            // (If they don't own the project and haven't changed it, no need to confirm.)
+            let uploadAllowed = true;
+            if (userOwnsProject || (projectChanged && isShowingWithoutId)) {
+                uploadAllowed = confirm( // eslint-disable-line no-alert
+                    intl.formatMessage(sharedMessages.replaceProjectWarning)
+                );
+            }
+            if (uploadAllowed) {
+                this.props.requestProjectUpload(loadingState);
+            } else {
+                this.props.closeFileMenu();
+            }
+        }
+    }
+    // called when file upload raw data is available in the reader
+    onload () {
+        if (this.reader) {
+            this.props.onLoadingStarted();
+            const filename = this.fileToUpload && this.fileToUpload.name;
+            this.props.vm.loadProject(this.reader.result)
+                .then(() => {
+                    this.props.onLoadingFinished(this.props.loadingState, true);
+                    // Reset the file input after project is loaded
+                    // This is necessary in case the user wants to reload a project
+                    if (filename) {
+                        const uploadedProjectTitle = this.getProjectTitleFromFilename(filename);
+                        this.props.onReceivedProjectTitle(uploadedProjectTitle);
+                    }
+                    this.resetFileInput();
+                })
+                .catch(error => {
+                    log.warn(error);
+                    alert(this.props.intl.formatMessage(messages.loadError)); // eslint-disable-line no-alert
+                    this.props.onLoadingFinished(this.props.loadingState, false);
+                    // Reset the file input after project is loaded
+                    // This is necessary in case the user wants to reload a project
+                    this.resetFileInput();
+                });
+        }
+    }
+    handleClick () {
+        // open filesystem browsing window
+        this.fileInput.click();
+    }
+    setFileInput (input) {
+        this.fileInput = input;
+    }
+    renderFileInput () {
+        return (
+            <input
+                accept=".sb,.sb2,.sb3"
+                ref={this.setFileInput}
+                style={{display: 'none'}}
+                type="file"
+                onChange={this.handleChange}
+            />
+        );
+    }
+    render () {
+        return this.props.children(this.props.className, this.renderFileInput, this.handleClick);
+    }
+}
+
+SBFileUploader.propTypes = {
+    canSave: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
+    children: PropTypes.func,
+    className: PropTypes.string,
+    closeFileMenu: PropTypes.func,
+    intl: intlShape.isRequired,
+    isLoadingUpload: PropTypes.bool,
+    isShowingWithoutId: PropTypes.bool,
+    loadingState: PropTypes.oneOf(LoadingStates),
+    onLoadingFinished: PropTypes.func,
+    onLoadingStarted: PropTypes.func,
+    projectChanged: PropTypes.bool,
+    requestProjectUpload: PropTypes.func,
+    onReceivedProjectTitle: PropTypes.func,
+    userOwnsProject: PropTypes.bool,
+    vm: PropTypes.shape({
+        loadProject: PropTypes.func
+    })
+};
+SBFileUploader.defaultProps = {
+    className: ''
+};
+const mapStateToProps = state => {
+    const loadingState = state.scratchGui.projectState.loadingState;
+    return {
+        isLoadingUpload: getIsLoadingUpload(loadingState),
+        isShowingWithoutId: getIsShowingWithoutId(loadingState),
+        loadingState: loadingState,
+        projectChanged: state.scratchGui.projectChanged,
+        vm: state.scratchGui.vm
+    };
+};
+
+const mapDispatchToProps = (dispatch, ownProps) => ({
+    closeFileMenu: () => dispatch(closeFileMenu()),
+    onLoadingFinished: (loadingState, success) => {
+        dispatch(onLoadedProject(loadingState, ownProps.canSave, success));
+        dispatch(closeLoadingProject());
+        dispatch(closeFileMenu());
+    },
+    requestProjectUpload: loadingState => dispatch(requestProjectUpload(loadingState)),
+    onLoadingStarted: () => dispatch(openLoadingProject()),
+    onReceivedProjectTitle: title => dispatch(setProjectTitle(title))
+});
+
+// Allow incoming props to override redux-provided props. Used to mock in tests.
+const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
+    {}, stateProps, dispatchProps, ownProps
+);
+
+export default connect(
+    mapStateToProps,
+    mapDispatchToProps,
+    mergeProps
+)(injectIntl(SBFileUploader));
diff --git a/src/lib/sb-file-uploader-hoc.jsx b/src/lib/sb-file-uploader-hoc.jsx
deleted file mode 100644
index 9e34b57684ef9390d6d9517d47e1ea3c1c8863ee..0000000000000000000000000000000000000000
--- a/src/lib/sb-file-uploader-hoc.jsx
+++ /dev/null
@@ -1,269 +0,0 @@
-import bindAll from 'lodash.bindall';
-import React from 'react';
-import PropTypes from 'prop-types';
-import {defineMessages, intlShape, injectIntl} from 'react-intl';
-import {connect} from 'react-redux';
-import log from '../lib/log';
-import sharedMessages from './shared-messages';
-
-import {
-    LoadingStates,
-    getIsLoadingUpload,
-    getIsShowingWithoutId,
-    onLoadedProject,
-    requestProjectUpload
-} from '../reducers/project-state';
-import {
-    openLoadingProject,
-    closeLoadingProject
-} from '../reducers/modals';
-import {
-    closeFileMenu
-} from '../reducers/menus';
-
-const messages = defineMessages({
-    loadError: {
-        id: 'gui.projectLoader.loadError',
-        defaultMessage: 'The project file that was selected failed to load.',
-        description: 'An error that displays when a local project file fails to load.'
-    }
-});
-
-/**
- * Higher Order Component to provide behavior for loading local project files into editor.
- * @param {React.Component} WrappedComponent the component to add project file loading functionality to
- * @returns {React.Component} WrappedComponent with project file loading functionality added
- *
- * <SBFileUploaderHOC>
- *     <WrappedComponent />
- * </SBFileUploaderHOC>
- */
-const SBFileUploaderHOC = function (WrappedComponent) {
-    class SBFileUploaderComponent extends React.Component {
-        constructor (props) {
-            super(props);
-            bindAll(this, [
-                'createFileObjects',
-                'getProjectTitleFromFilename',
-                'handleFinishedLoadingUpload',
-                'handleStartSelectingFileUpload',
-                'handleChange',
-                'onload',
-                'removeFileObjects'
-            ]);
-        }
-        componentDidUpdate (prevProps) {
-            if (this.props.isLoadingUpload && !prevProps.isLoadingUpload) {
-                this.handleFinishedLoadingUpload(); // cue step 5 below
-            }
-        }
-        componentWillUnmount () {
-            this.removeFileObjects();
-        }
-        // step 1: this is where the upload process begins
-        handleStartSelectingFileUpload () {
-            this.createFileObjects(); // go to step 2
-        }
-        // step 2: create a FileReader and an <input> element, and issue a
-        // pseudo-click to it. That will open the file chooser dialog.
-        createFileObjects () {
-            // redo step 7, in case it got skipped last time and its objects are
-            // still in memory
-            this.removeFileObjects();
-            // create fileReader
-            this.fileReader = new FileReader();
-            this.fileReader.onload = this.onload;
-            // create <input> element and add it to DOM
-            this.inputElement = document.createElement('input');
-            this.inputElement.accept = '.sb,.sb2,.sb3';
-            this.inputElement.style = 'display: none;';
-            this.inputElement.type = 'file';
-            this.inputElement.onchange = this.handleChange; // connects to step 3
-            document.body.appendChild(this.inputElement);
-            // simulate a click to open file chooser dialog
-            this.inputElement.click();
-        }
-        // step 3: user has picked a file using the file chooser dialog.
-        // We don't actually load the file here, we only decide whether to do so.
-        handleChange (e) {
-            const {
-                intl,
-                isShowingWithoutId,
-                loadingState,
-                projectChanged,
-                userOwnsProject
-            } = this.props;
-            const thisFileInput = e.target;
-            if (thisFileInput.files) { // Don't attempt to load if no file was selected
-                this.fileToUpload = thisFileInput.files[0];
-
-                // If user owns the project, or user has changed the project,
-                // we must confirm with the user that they really intend to
-                // replace it. (If they don't own the project and haven't
-                // changed it, no need to confirm.)
-                let uploadAllowed = true;
-                if (userOwnsProject || (projectChanged && isShowingWithoutId)) {
-                    uploadAllowed = confirm( // eslint-disable-line no-alert
-                        intl.formatMessage(sharedMessages.replaceProjectWarning)
-                    );
-                }
-                if (uploadAllowed) {
-                    // cues step 4
-                    this.props.requestProjectUpload(loadingState);
-                } else {
-                    // skips ahead to step 7
-                    this.removeFileObjects();
-                }
-                this.props.closeFileMenu();
-            }
-        }
-        // step 4 is below, in mapDispatchToProps
-
-        // step 5: called from componentDidUpdate when project state shows
-        // that project data has finished "uploading" into the browser
-        handleFinishedLoadingUpload () {
-            if (this.fileToUpload && this.fileReader) {
-                // begin to read data from the file. When finished,
-                // cues step 6 using the reader's onload callback
-                this.fileReader.readAsArrayBuffer(this.fileToUpload);
-            } else {
-                this.props.cancelFileUpload(this.props.loadingState);
-                // skip ahead to step 7
-                this.removeFileObjects();
-            }
-        }
-        // used in step 6 below
-        getProjectTitleFromFilename (fileInputFilename) {
-            if (!fileInputFilename) return '';
-            // only parse title with valid scratch project extensions
-            // (.sb, .sb2, and .sb3)
-            const matches = fileInputFilename.match(/^(.*)\.sb[23]?$/);
-            if (!matches) return '';
-            return matches[1].substring(0, 100); // truncate project title to max 100 chars
-        }
-        // step 6: attached as a handler on our FileReader object; called when
-        // file upload raw data is available in the reader
-        onload () {
-            if (this.fileReader) {
-                this.props.onLoadingStarted();
-                const filename = this.fileToUpload && this.fileToUpload.name;
-                this.props.vm.loadProject(this.fileReader.result)
-                    .then(() => {
-                        this.props.onLoadingFinished(this.props.loadingState, true);
-                        if (filename) {
-                            const uploadedProjectTitle = this.getProjectTitleFromFilename(filename);
-                            this.props.onUpdateProjectTitle(uploadedProjectTitle);
-                        }
-                    })
-                    .catch(error => {
-                        log.warn(error);
-                        this.props.intl.formatMessage(messages.loadError);
-                        this.props.onLoadingFinished(this.props.loadingState, false);
-                    })
-                    .then(() => {
-                        // go back to step 7: whether project loading succeeded
-                        // or failed, reset file objects
-                        this.removeFileObjects();
-                    });
-            }
-        }
-        // step 7: remove the <input> element from the DOM and clear reader and
-        // fileToUpload reference, so those objects can be garbage collected
-        removeFileObjects () {
-            if (this.inputElement) {
-                this.inputElement.value = null;
-                document.body.removeChild(this.inputElement);
-            }
-            this.inputElement = null;
-            this.fileReader = null;
-            this.fileToUpload = null;
-        }
-        render () {
-            const {
-                /* eslint-disable no-unused-vars */
-                cancelFileUpload,
-                closeFileMenu: closeFileMenuProp,
-                isLoadingUpload,
-                isShowingWithoutId,
-                loadingState,
-                onLoadingFinished,
-                onLoadingStarted,
-                projectChanged,
-                requestProjectUpload: requestProjectUploadProp,
-                userOwnsProject,
-                /* eslint-enable no-unused-vars */
-                ...componentProps
-            } = this.props;
-            return (
-                <React.Fragment>
-                    <WrappedComponent
-                        onStartSelectingFileUpload={this.handleStartSelectingFileUpload}
-                        {...componentProps}
-                    />
-                </React.Fragment>
-            );
-        }
-    }
-
-    SBFileUploaderComponent.propTypes = {
-        canSave: PropTypes.bool,
-        cancelFileUpload: PropTypes.func,
-        closeFileMenu: PropTypes.func,
-        intl: intlShape.isRequired,
-        isLoadingUpload: PropTypes.bool,
-        isShowingWithoutId: PropTypes.bool,
-        loadingState: PropTypes.oneOf(LoadingStates),
-        onLoadingFinished: PropTypes.func,
-        onLoadingStarted: PropTypes.func,
-        onUpdateProjectTitle: PropTypes.func,
-        projectChanged: PropTypes.bool,
-        requestProjectUpload: PropTypes.func,
-        userOwnsProject: PropTypes.bool,
-        vm: PropTypes.shape({
-            loadProject: PropTypes.func
-        })
-    };
-    const mapStateToProps = (state, ownProps) => {
-        const loadingState = state.scratchGui.projectState.loadingState;
-        const user = state.session && state.session.session && state.session.session.user;
-        return {
-            isLoadingUpload: getIsLoadingUpload(loadingState),
-            isShowingWithoutId: getIsShowingWithoutId(loadingState),
-            loadingState: loadingState,
-            projectChanged: state.scratchGui.projectChanged,
-            userOwnsProject: ownProps.authorUsername && user &&
-                (ownProps.authorUsername === user.username),
-            vm: state.scratchGui.vm
-        };
-    };
-    const mapDispatchToProps = (dispatch, ownProps) => ({
-        cancelFileUpload: loadingState => dispatch(onLoadedProject(loadingState, false, false)),
-        closeFileMenu: () => dispatch(closeFileMenu()),
-        // transition project state from loading to regular, and close
-        // loading screen and file menu
-        onLoadingFinished: (loadingState, success) => {
-            dispatch(onLoadedProject(loadingState, ownProps.canSave, success));
-            dispatch(closeLoadingProject());
-            dispatch(closeFileMenu());
-        },
-        // show project loading screen
-        onLoadingStarted: () => dispatch(openLoadingProject()),
-        // step 4: transition the project state so we're ready to handle the new
-        // project data. When this is done, the project state transition will be
-        // noticed by componentDidUpdate()
-        requestProjectUpload: loadingState => dispatch(requestProjectUpload(loadingState))
-    });
-    // Allow incoming props to override redux-provided props. Used to mock in tests.
-    const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
-        {}, stateProps, dispatchProps, ownProps
-    );
-    return injectIntl(connect(
-        mapStateToProps,
-        mapDispatchToProps,
-        mergeProps
-    )(SBFileUploaderComponent));
-};
-
-export {
-    SBFileUploaderHOC as default
-};
diff --git a/test/integration/menu-bar.test.js b/test/integration/menu-bar.test.js
index c83f1022d214a69f4bfa7cf019971334f4c4e153..3f6457e5208ae171685e62f70327a3f3463a5f4c 100644
--- a/test/integration/menu-bar.test.js
+++ b/test/integration/menu-bar.test.js
@@ -75,7 +75,6 @@ describe('Menu bar settings', () => {
     test('User is not warned before uploading project file over a fresh project', async () => {
         await loadUri(uri);
         await clickText('File');
-        await clickText('Load from your computer');
         const input = await findByXpath('//input[@accept=".sb,.sb2,.sb3"]');
         await input.sendKeys(path.resolve(__dirname, '../fixtures/project1.sb3'));
         // No replace alert since no changes were made
@@ -90,7 +89,6 @@ describe('Menu bar settings', () => {
         await clickText('delete', scope.spriteTile);
 
         await clickText('File');
-        await clickText('Load from your computer');
         const input = await findByXpath('//input[@accept=".sb,.sb2,.sb3"]');
         await input.sendKeys(path.resolve(__dirname, '../fixtures/project1.sb3'));
         await driver.switchTo().alert()
diff --git a/test/unit/containers/sb-file-uploader.test.jsx b/test/unit/containers/sb-file-uploader.test.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..c757114b4ca7e387c78e7477931cf31a04c91a9a
--- /dev/null
+++ b/test/unit/containers/sb-file-uploader.test.jsx
@@ -0,0 +1,84 @@
+import React from 'react';
+
+import {shallowWithIntl} from '../../helpers/intl-helpers.jsx';
+import configureStore from 'redux-mock-store';
+import SBFileUploader from '../../../src/containers/sb-file-uploader';
+import {LoadingState} from '../../../src/reducers/project-state';
+
+jest.mock('react-ga'); // must mock this entire library, or lib/analytics causes error
+
+describe('SBFileUploader Container', () => {
+    const mockStore = configureStore();
+    let onLoadingFinished;
+    let onLoadingStarted;
+    let store;
+
+    // Wrap this in a function so it gets test specific states and can be reused.
+    const getContainer = function () {
+        return (
+            <SBFileUploader
+                onLoadingFinished={onLoadingFinished}
+                onLoadingStarted={onLoadingStarted}
+            >
+                {(renderFileInput, loadProject) => (
+                    <div
+                        onClick={loadProject}
+                    />
+                )}
+            </SBFileUploader>
+        );
+    };
+
+    beforeEach(() => {
+        store = mockStore({
+            scratchGui: {
+                projectState: {
+                    loadingState: LoadingState.SHOWING_WITH_ID
+                },
+                vm: {}
+            }
+        });
+        onLoadingFinished = jest.fn();
+        onLoadingStarted = jest.fn();
+    });
+
+    test('correctly sets title with .sb3 filename', () => {
+        const wrapper = shallowWithIntl(getContainer(), {context: {store}});
+        const instance = wrapper
+            .dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
+            .dive() // unwrap InjectIntl(SBFileUploader)
+            .instance(); // SBFileUploader
+        const projectName = instance.getProjectTitleFromFilename('my project is great.sb3');
+        expect(projectName).toBe('my project is great');
+    });
+
+    test('correctly sets title with .sb2 filename', () => {
+        const wrapper = shallowWithIntl(getContainer(), {context: {store}});
+        const instance = wrapper
+            .dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
+            .dive() // unwrap InjectIntl(SBFileUploader)
+            .instance(); // SBFileUploader
+        const projectName = instance.getProjectTitleFromFilename('my project is great.sb2');
+        expect(projectName).toBe('my project is great');
+    });
+
+    test('correctly sets title with .sb filename', () => {
+        const wrapper = shallowWithIntl(getContainer(), {context: {store}});
+        const instance = wrapper
+            .dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
+            .dive() // unwrap InjectIntl(SBFileUploader)
+            .instance(); // SBFileUploader
+        const projectName = instance.getProjectTitleFromFilename('my project is great.sb');
+        expect(projectName).toBe('my project is great');
+    });
+
+    test('sets blank title with filename with no extension', () => {
+        const wrapper = shallowWithIntl(getContainer(), {context: {store}});
+        const instance = wrapper
+            .dive() // unwrap redux Connect(InjectIntl(SBFileUploader))
+            .dive() // unwrap InjectIntl(SBFileUploader)
+            .instance(); // SBFileUploader
+        const projectName = instance.getProjectTitleFromFilename('my project is great');
+        expect(projectName).toBe('');
+    });
+});
diff --git a/test/unit/util/sb-file-uploader-hoc.test.jsx b/test/unit/util/sb-file-uploader-hoc.test.jsx
deleted file mode 100644
index c200157c984d5afaf1ff384da292a0b417985210..0000000000000000000000000000000000000000
--- a/test/unit/util/sb-file-uploader-hoc.test.jsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import 'web-audio-test-api';
-
-import React from 'react';
-import configureStore from 'redux-mock-store';
-import {mountWithIntl, shallowWithIntl} from '../../helpers/intl-helpers.jsx';
-import {LoadingState} from '../../../src/reducers/project-state';
-import VM from 'scratch-vm';
-
-import SBFileUploaderHOC from '../../../src/lib/sb-file-uploader-hoc.jsx';
-
-describe('SBFileUploaderHOC', () => {
-    const mockStore = configureStore();
-    let store;
-    let vm;
-
-    // Wrap this in a function so it gets test specific states and can be reused.
-    const getContainer = function () {
-        const Component = () => <div />;
-        return SBFileUploaderHOC(Component);
-    };
-
-    const shallowMountWithContext = component => (
-        shallowWithIntl(component, {context: {store}})
-    );
-
-    const unwrappedInstance = () => {
-        const WrappedComponent = getContainer();
-        // default starting state: looking at a project you created, not logged in
-        const wrapper = shallowMountWithContext(
-            <WrappedComponent
-                projectChanged
-                canSave={false}
-                cancelFileUpload={jest.fn()}
-                closeFileMenu={jest.fn()}
-                requestProjectUpload={jest.fn()}
-                userOwnsProject={false}
-                vm={vm}
-                onLoadingFinished={jest.fn()}
-                onLoadingStarted={jest.fn()}
-                onUpdateProjectTitle={jest.fn()}
-            />
-        );
-        return wrapper
-            .dive() // unwrap intl
-            .dive() // unwrap redux Connect(SBFileUploaderComponent)
-            .instance(); // SBFileUploaderComponent
-    };
-
-    beforeEach(() => {
-        vm = new VM();
-        store = mockStore({
-            scratchGui: {
-                projectState: {
-                    loadingState: LoadingState.SHOWING_WITHOUT_ID
-                },
-                vm: {}
-            },
-            locales: {
-                locale: 'en'
-            }
-        });
-    });
-
-    test('correctly sets title with .sb3 filename', () => {
-        const projectName = unwrappedInstance().getProjectTitleFromFilename('my project is great.sb3');
-        expect(projectName).toBe('my project is great');
-    });
-
-    test('correctly sets title with .sb2 filename', () => {
-        const projectName = unwrappedInstance().getProjectTitleFromFilename('my project is great.sb2');
-        expect(projectName).toBe('my project is great');
-    });
-
-    test('correctly sets title with .sb filename', () => {
-        const projectName = unwrappedInstance().getProjectTitleFromFilename('my project is great.sb');
-        expect(projectName).toBe('my project is great');
-    });
-
-    test('sets blank title with filename with no extension', () => {
-        const projectName = unwrappedInstance().getProjectTitleFromFilename('my project is great');
-        expect(projectName).toBe('');
-    });
-
-    test('if isLoadingUpload becomes true, without fileToUpload set, will call cancelFileUpload', () => {
-        const mockedCancelFileUpload = jest.fn();
-        const WrappedComponent = getContainer();
-        const mounted = mountWithIntl(
-            <WrappedComponent
-                projectChanged
-                canSave={false}
-                cancelFileUpload={mockedCancelFileUpload}
-                closeFileMenu={jest.fn()}
-                isLoadingUpload={false}
-                requestProjectUpload={jest.fn()}
-                store={store}
-                userOwnsProject={false}
-                vm={vm}
-                onLoadingFinished={jest.fn()}
-                onLoadingStarted={jest.fn()}
-                onUpdateProjectTitle={jest.fn()}
-            />
-        );
-        mounted.setProps({
-            isLoadingUpload: true
-        });
-        expect(mockedCancelFileUpload).toHaveBeenCalled();
-    });
-});