Просмотр исходного кода

Merge branch 'master' into imprv/refactor-attachment

Yuki Takei 7 лет назад
Родитель
Сommit
b389d3bf75

+ 11 - 1
CHANGES.md

@@ -1,10 +1,20 @@
 CHANGES
 ========
 
-## 3.3.8-RC
+## 3.3.9
 
 * 
 
+## 3.3.8
+
+* Fix: Typeahead shows autocomplete wrongly
+* Fix: Move/Duplicate don't work
+    * Introduced by 3.3.7
+* Fix: Server doesn't respond when root page is restricted
+* Support: Upgrade libs
+    * react
+    * react-bootstrap-typeahead
+
 ## 3.3.7
 
 * Feature: Editor toolbar

+ 3 - 3
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "3.3.8-RC",
+  "version": "3.3.9-RC",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",
@@ -182,9 +182,9 @@
     "penpal": "^3.0.3",
     "plantuml-encoder": "^1.2.5",
     "postcss-loader": "^3.0.0",
-    "react": "^16.4.1",
+    "react": "^16.7.0",
     "react-bootstrap": "^0.32.1",
-    "react-bootstrap-typeahead": "^3.1.5",
+    "react-bootstrap-typeahead": "^3.3.2",
     "react-clipboard.js": "^2.0.0",
     "react-codemirror2": "^5.1.0",
     "react-dom": "^16.4.1",

+ 4 - 4
src/client/js/app.js

@@ -29,7 +29,7 @@ import SeenUserList     from './components/SeenUserList';
 import RevisionPath     from './components/Page/RevisionPath';
 import RevisionUrl      from './components/Page/RevisionUrl';
 import BookmarkButton   from './components/BookmarkButton';
-import NewPageNameInput from './components/NewPageNameInput';
+import PagePathAutoComplete from './components/PagePathAutoComplete';
 import RecentCreated from './components/RecentCreated/RecentCreated';
 
 import CustomCssEditor  from './components/Admin/CustomCssEditor';
@@ -281,9 +281,9 @@ const componentMappings = {
   'bookmark-button': <BookmarkButton pageId={pageId} crowi={crowi} />,
   'bookmark-button-lg': <BookmarkButton pageId={pageId} crowi={crowi} size="lg" />,
 
-  'create-page-name-input': <NewPageNameInput crowi={crowi} parentPageName={pagePath} />,
-  'rename-page-name-input': <NewPageNameInput crowi={crowi} parentPageName={pagePath} />,
-  'duplicate-page-name-input': <NewPageNameInput crowi={crowi} parentPageName={pagePath} />,
+  'create-page-name-input': <PagePathAutoComplete crowi={crowi} initializedPath={pagePath} addSlashToTheEnd={true} />,
+  'rename-page-name-input': <PagePathAutoComplete crowi={crowi} initializedPath={pagePath} />,
+  'duplicate-page-name-input': <PagePathAutoComplete crowi={crowi} initializedPath={pagePath} />,
 
 };
 // additional definitions if data exists

+ 0 - 82
src/client/js/components/NewPageNameInput.js

@@ -1,82 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import SearchTypeahead from './SearchTypeahead';
-
-export default class NewPageNameInput extends React.Component {
-
-  constructor(props) {
-
-    super(props);
-
-    this.state = {
-      searchError: null,
-    };
-    this.crowi = this.props.crowi;
-
-    this.onSearchError = this.onSearchError.bind(this);
-    this.onSubmit = this.onSubmit.bind(this);
-    this.getParentPageName = this.getParentPageName.bind(this);
-  }
-
-  componentDidMount() {
-  }
-
-  componentWillUnmount() {
-  }
-
-  onSearchError(err) {
-    this.setState({
-      searchError: err,
-    });
-  }
-
-  onSubmit(query) {
-    // get the closest form element
-    const elem = this.refs.rootDom;
-    const form = elem.closest('form');
-    // submit with jQuery
-    $(form).submit();
-  }
-
-  getParentPageName(path) {
-    if (path == '/') {
-      return path;
-    }
-
-    if (path.match(/.+\/$/)) {
-      return path;
-    }
-
-    return path + '/';
-  }
-
-  render() {
-    const emptyLabel = (this.state.searchError !== null)
-      ? 'Error on searching.'
-      : 'No matches found on title...';
-
-    return (
-      <div ref='rootDom'>
-        <SearchTypeahead
-          ref={this.searchTypeaheadDom}
-          crowi={this.crowi}
-          onSearchError={this.onSearchError}
-          onSubmit={this.onSubmit}
-          emptyLabel={emptyLabel}
-          placeholder="Input page name"
-          keywordOnInit={this.getParentPageName(this.props.parentPageName)}
-        />
-      </div>
-    );
-  }
-}
-
-NewPageNameInput.propTypes = {
-  crowi:          PropTypes.object.isRequired,
-  parentPageName: PropTypes.string,
-};
-
-NewPageNameInput.defaultProps = {
-  parentPageName: '',
-};

+ 67 - 0
src/client/js/components/PagePathAutoComplete.jsx

@@ -0,0 +1,67 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import * as pagePathUtils from '@commons/util/page-path-utils';
+import SearchTypeahead from './SearchTypeahead';
+
+export default class PagePathAutoComplete extends React.Component {
+
+  constructor(props) {
+
+    super(props);
+
+    this.state = {
+      searchError: null,
+    };
+    this.crowi = this.props.crowi;
+
+    this.onSubmit = this.onSubmit.bind(this);
+    this.getKeywordOnInit = this.getKeywordOnInit.bind(this);
+  }
+
+  componentDidMount() {
+  }
+
+  componentWillUnmount() {
+  }
+
+  onSubmit(query) {
+    // get the closest form element
+    const elem = this.refs.rootDom;
+    const form = elem.closest('form');
+    // submit with jQuery
+    $(form).submit();
+  }
+
+  getKeywordOnInit(path) {
+    return this.props.addSlashToTheEnd
+      ? pagePathUtils.addSlashToTheEnd(path)
+      : pagePathUtils.removeLastSlash(path);
+  }
+
+  render() {
+    return (
+      <div ref='rootDom'>
+        <SearchTypeahead
+          ref={this.searchTypeaheadDom}
+          crowi={this.crowi}
+          onSubmit={this.onSubmit}
+          inputName='new_path'
+          emptyLabelExceptError={null}
+          placeholder="Input page path"
+          keywordOnInit={this.getKeywordOnInit(this.props.initializedPath)}
+        />
+      </div>
+    );
+  }
+}
+
+PagePathAutoComplete.propTypes = {
+  crowi:            PropTypes.object.isRequired,
+  initializedPath:  PropTypes.string,
+  addSlashToTheEnd: PropTypes.bool,
+};
+
+PagePathAutoComplete.defaultProps = {
+  initializedPath: '/',
+};

+ 51 - 25
src/client/js/components/SearchTypeahead.js

@@ -23,15 +23,15 @@ export default class SearchTypeahead extends React.Component {
       searchError: null,
     };
     this.crowi = this.props.crowi;
-    this.emptyLabel = props.emptyLabel;
 
+    this.restoreInitialData = this.restoreInitialData.bind(this);
     this.search = this.search.bind(this);
     this.onInputChange = this.onInputChange.bind(this);
     this.onKeyDown = this.onKeyDown.bind(this);
     this.dispatchSubmit = this.dispatchSubmit.bind(this);
+    this.getEmptyLabel = this.getEmptyLabel.bind(this);
     this.getRestoreFormButton = this.getRestoreFormButton.bind(this);
     this.renderMenuItemChildren = this.renderMenuItemChildren.bind(this);
-    this.restoreInitialData = this.restoreInitialData.bind(this);
     this.getTypeahead = this.getTypeahead.bind(this);
   }
 
@@ -48,6 +48,17 @@ export default class SearchTypeahead extends React.Component {
   componentWillUnmount() {
   }
 
+  /**
+   * Initialize keyword
+   */
+  restoreInitialData() {
+    // see https://github.com/ericgio/react-bootstrap-typeahead/issues/266#issuecomment-414987723
+    const text = this.props.keywordOnInit;
+    const instance = this.refs.typeahead.getInstance();
+    instance.clear();
+    instance.setState({ text });
+  }
+
   search(keyword) {
 
     if (keyword === '') {
@@ -110,23 +121,20 @@ export default class SearchTypeahead extends React.Component {
     }
   }
 
-  renderMenuItemChildren(option, props, index) {
-    const page = option;
-    return (
-      <span>
-      <UserPicture user={page.lastUpdateUser} size="sm" />
-      <PagePath page={page} />
-      <PageListMeta page={page} />
-      </span>
-    );
-  }
+  getEmptyLabel() {
+    // use props.emptyLabel as is if defined
+    if (this.props.emptyLabel !== undefined) {
+      return this.props.emptyLabel;
+    }
 
-  /**
-   * Initialize keyword
-   */
-  restoreInitialData() {
-    this.refs.typeahead.getInstance().clear();
-    this.refs.typeahead.getInstance()._updateText(this.props.keywordOnInit);
+    let emptyLabelExceptError = 'No matches found on title...';
+    if (this.props.emptyLabelExceptError !== undefined) {
+      emptyLabelExceptError = this.props.emptyLabelExceptError;
+    }
+
+    return (this.state.searchError !== null)
+      ? 'Error on searching.'
+      : emptyLabelExceptError;
   }
 
   /**
@@ -142,26 +150,43 @@ export default class SearchTypeahead extends React.Component {
     );
   }
 
+  renderMenuItemChildren(option, props, index) {
+    const page = option;
+    return (
+      <span>
+      <UserPicture user={page.lastUpdateUser} size="sm" />
+      <PagePath page={page} />
+      <PageListMeta page={page} />
+      </span>
+    );
+  }
+
   render() {
-    const emptyLabel = (this.state.searchError !== null)
-      ? 'Error on searching.'
-      : 'No matches found on title...';
-    const restoreFormButton = this.getRestoreFormButton();
     const defaultSelected = (this.props.keywordOnInit != '')
       ? [{path: this.props.keywordOnInit}]
       : [];
+    const inputProps = { autoComplete: 'off' };
+    if (this.props.inputName != null) {
+      inputProps.name = this.props.inputName;
+    }
+
+    const restoreFormButton = this.getRestoreFormButton();
 
     return (
       <div className="search-typeahead">
         <AsyncTypeahead
           {...this.props}
           ref="typeahead"
-          inputProps={{autoComplete: 'off'}}
+          inputProps={inputProps}
           isLoading={this.state.isLoading}
           labelKey="path"
           minLength={0}
           options={this.state.pages} // Search result (Some page names)
-          emptyLabel={this.emptyLabel ? this.emptyLabel : emptyLabel}
+          emptyLabel={this.getEmptyLabel()}
+          searchText={(this.state.isLoading ? 'Searching...' : this.getEmptyLabel())}
+              // DIRTY HACK
+              //  note: The default searchText string has been shown wrongly even if isLoading is false
+              //        since upgrade react-bootstrap-typeahead to v3.3.2 -- 2019.02.05 Yuki Takei
           align='left'
           submitFormOnEnter={true}
           onSearch={this.search}
@@ -188,7 +213,9 @@ SearchTypeahead.propTypes = {
   onChange:        PropTypes.func,
   onSubmit:        PropTypes.func,
   onInputChange:   PropTypes.func,
+  inputName:       PropTypes.string,
   emptyLabel:      PropTypes.string,
+  emptyLabelExceptError: PropTypes.string,
   placeholder:     PropTypes.string,
   keywordOnInit:   PropTypes.string,
   promptText:      PropTypes.object,
@@ -201,7 +228,6 @@ SearchTypeahead.defaultProps = {
   onSearchSuccess: noop,
   onSearchError:   noop,
   onChange:        noop,
-  emptyLabel:      null,
   placeholder:     '',
   keywordOnInit:   '',
   onInputChange: () => {},

+ 6 - 1
src/client/styles/scss/_search.scss

@@ -20,7 +20,7 @@
   position: relative;
   .search-clear {
     position: absolute;
-    z-index: 2;
+    z-index: 3;
     top: 4px;
     right: 4px;
     width: 24px;
@@ -100,6 +100,11 @@
       width: 300px;
     }
   }
+  .rbt-menu {
+    margin-top: 33px;   // DIRTY HACK
+                        //   note: 'transform: translate3d(0px, XXpx, 0px)' calculation has failed on .search-top
+                        //         since upgrade react-bootstrap-typeahead to v3.3.2 -- 2019.02.05 Yuki Takei
+  }
 }
 .search-sidebar {
   .search-form, .form-group, .rbt-input.form-control, .input-group {

+ 35 - 2
src/lib/util/page-path-utils.js

@@ -18,7 +18,40 @@ function encodePagePath(path) {
   return paths.join('/');
 }
 
+function matchEndWithSlash(path) {
+  // https://regex101.com/r/Z21fEd/1
+  return path.match(/(.+?)(\/)?$/);
+}
+
+function isEndWithSlash(path) {
+  const match = matchEndWithSlash(path);
+  return (match[2] != null);
+}
+
+function addSlashToTheEnd(path) {
+  if (path === '/') {
+    return path;
+  }
+
+  if (!isEndWithSlash(path)) {
+    return `${path}/`;
+  }
+  return path;
+}
+
+function removeLastSlash(path) {
+  if (path === '/') {
+    return path;
+  }
+
+  const match = matchEndWithSlash(path);
+  return match[1];
+}
+
 module.exports = {
-  encodePagePath: encodePagePath,
-  encodePagesPath: encodePagesPath
+  encodePagePath,
+  encodePagesPath,
+  isEndWithSlash,
+  addSlashToTheEnd,
+  removeLastSlash,
 };

+ 9 - 8
src/server/routes/page.js

@@ -192,12 +192,13 @@ module.exports = function(crowi, app) {
     // check whether this page has portal page
     const portalPageStatus = await getPortalPageState(path, req.user);
 
+    let view = 'customlayout-selector/page_list';
     const renderVars = { path };
 
     if (portalPageStatus === PORTAL_STATUS_FORBIDDEN) {
       // inject to req
       req.isForbidden = true;
-      return next();
+      view = 'customlayout-selector/forbidden';
     }
     else if (portalPageStatus === PORTAL_STATUS_EXISTS) {
       let portalPage = await Page.findByPathAndViewer(path, req.user);
@@ -216,7 +217,7 @@ module.exports = function(crowi, app) {
     await addRenderVarsForDescendants(renderVars, path, req.user, offset, limit);
 
     await interceptorManager.process('beforeRenderPage', req, res, renderVars);
-    return res.render('customlayout-selector/page_list', renderVars);
+    return res.render(view, renderVars);
   }
 
   async function showPageForGrowiBehavior(req, res, next) {
@@ -623,8 +624,8 @@ module.exports = function(crowi, app) {
       options.grantUserGroupId = grantUserGroupId;
     }
 
-      const Revision = crowi.model('Revision');
-      const previousRevision = await Revision.findById(revisionId);
+    const Revision = crowi.model('Revision');
+    const previousRevision = await Revision.findById(revisionId);
     try {
       page = await Page.updatePage(page, pageBody, previousRevision.body, req.user, options);
     }
@@ -953,13 +954,13 @@ module.exports = function(crowi, app) {
    * @apiParam {String} page_id Page Id.
    * @apiParam {String} path
    * @apiParam {String} revision_id
-   * @apiParam {String} q New path name.
+   * @apiParam {String} new_path New path name.
    * @apiParam {Bool} create_redirect
    */
   api.rename = async function(req, res) {
     const pageId = req.body.page_id;
     const previousRevision = req.body.revision_id || null;
-    const newPagePath = Page.normalizePath(req.body.q);
+    const newPagePath = Page.normalizePath(req.body.new_path);
     const options = {
       createRedirectPage: req.body.create_redirect || 0,
       moveUnderTrees: req.body.move_trees || 0,
@@ -1019,11 +1020,11 @@ module.exports = function(crowi, app) {
    * @apiGroup Page
    *
    * @apiParam {String} page_id Page Id.
-   * @apiParam {String} q New path name.
+   * @apiParam {String} new_path New path name.
    */
   api.duplicate = async function(req, res) {
     const pageId = req.body.page_id;
-    const newPagePath = Page.normalizePath(req.body.q);
+    const newPagePath = Page.normalizePath(req.body.new_path);
 
     const page = await Page.findByIdAndViewer(pageId, req.user);
 

+ 1 - 1
src/server/views/layout-growi/forbidden.html

@@ -2,7 +2,7 @@
 
 
 {% block content_header %}
-  {% include 'widget/header.html' %}
+  {% include 'widget/header.html' with {forbidden: true} %}
 {% endblock %}
 
 

+ 1 - 1
src/server/views/layout-growi/widget/header.html

@@ -39,7 +39,7 @@
       </ul>
       {% endif %}
 
-      {% if not page and ('/' === path or 'crowi' === behaviorType()) and not isUserPageList(path) and !isTrashPage() %}
+      {% if not page and not forbidden and ('/' === path or 'crowi' === behaviorType()) and not isUserPageList(path) and !isTrashPage() %}
         {% if '/' === path.slice(-1) %}
           {% include '../../widget/create_portal.html' %}
         {% endif %}

+ 1 - 1
src/server/views/modal/duplicate.html

@@ -20,7 +20,7 @@
                 {% if searchConfigured() %}
                 <div id="duplicate-page-name-input" class="page-name-input"></div>
                 {% else %}
-                <input type="text" class="form-control" name="q" id="duplicatePageName" value="{{ page.path }}">
+                <input type="text" class="form-control" name="new_path" id="duplicatePageName" value="{{ page.path }}">
                 {% endif %}
               </div>
             </div>

+ 1 - 1
src/server/views/modal/rename.html

@@ -20,7 +20,7 @@
               {% if searchConfigured() %}
               <div id="rename-page-name-input" class="page-name-input"></div>
               {% else %}
-              <input type="text" class="form-control" name="q" id="newPageName" value="{{ page.path }}">
+              <input type="text" class="form-control" name="new_path" id="newPageName" value="{{ page.path }}">
               {% endif %}
             </div>
           </div>

+ 97 - 22
yarn.lock

@@ -11,6 +11,13 @@
     loader-utils "^1.1.0"
     lodash "^4.17.10"
 
+"@babel/runtime@^7.1.2":
+  version "7.3.1"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.1.tgz#574b03e8e8a9898eaf4a872a92ea20b7846f6f2a"
+  integrity sha512-7jGW8ppV0ant637pIqAcFfQDDH1orEPGJb8aXfUozuCU3QqX7rX4DA8iwrbPrR1hcH0FTTHz47yQnk+bl5xHQA==
+  dependencies:
+    regenerator-runtime "^0.12.0"
+
 "@browser-bunyan/console-formatted-stream@^1.3.0":
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/@browser-bunyan/console-formatted-stream/-/console-formatted-stream-1.3.0.tgz#3dc059aa5c1b2a7a1f26e2706e2bdeb9a09bbe57"
@@ -2341,6 +2348,22 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4, create-hmac@^1.1.6:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
+create-react-context@<=0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.2.tgz#9836542f9aaa22868cd7d4a6f82667df38019dca"
+  integrity sha512-KkpaLARMhsTsgp0d2NA/R94F/eDLbhXERdIq3LvX2biCAXcDvHYoOqHfWCHf1+OLj+HKBotLG3KqaOOf+C1C+A==
+  dependencies:
+    fbjs "^0.8.0"
+    gud "^1.0.0"
+
+create-react-context@^0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3"
+  integrity sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==
+  dependencies:
+    fbjs "^0.8.0"
+    gud "^1.0.0"
+
 cross-env@^5.0.5:
   version "5.1.3"
   resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.1.3.tgz#f8ae18faac87692b0a8b4d2f7000d4ec3a85dfd7"
@@ -3528,6 +3551,19 @@ fastparse@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
 
+fbjs@^0.8.0:
+  version "0.8.17"
+  resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
+  integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
+  dependencies:
+    core-js "^1.0.0"
+    isomorphic-fetch "^2.1.1"
+    loose-envify "^1.0.0"
+    object-assign "^4.1.0"
+    promise "^7.1.1"
+    setimmediate "^1.0.5"
+    ua-parser-js "^0.7.18"
+
 fbjs@^0.8.16:
   version "0.8.16"
   resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
@@ -4102,6 +4138,11 @@ gtoken@^2.3.0:
     mime "^2.2.0"
     pify "^3.0.0"
 
+gud@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
+  integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==
+
 gzip-size@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.0.0.tgz#a55ecd99222f4c48fd8c01c625ce3b349d0a0e80"
@@ -6936,9 +6977,10 @@ pluralize@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
 
-popper.js@^1.14.1:
-  version "1.14.3"
-  resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.3.tgz#1438f98d046acf7b4d78cd502bf418ac64d4f095"
+popper.js@^1.14.4:
+  version "1.14.7"
+  resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.7.tgz#e31ec06cfac6a97a53280c3e55e4e0c860e7738e"
+  integrity sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ==
 
 portscanner@2.1.1:
   version "2.1.1"
@@ -7482,20 +7524,21 @@ rc@^1.1.7:
     minimist "^1.2.0"
     strip-json-comments "~2.0.1"
 
-react-bootstrap-typeahead@^3.1.5:
-  version "3.1.5"
-  resolved "https://registry.yarnpkg.com/react-bootstrap-typeahead/-/react-bootstrap-typeahead-3.1.5.tgz#4761300571334ca447d7c7e9b85864205fccd8c0"
+react-bootstrap-typeahead@^3.3.2:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/react-bootstrap-typeahead/-/react-bootstrap-typeahead-3.3.2.tgz#ffe0193b6fc4af585d1724b1447d8bf2c871f75a"
+  integrity sha512-BCaFXrN3MefKj2jn8HqeSHu4hWwv1f8WtYWTNOhkz+KN4QtNLwbm7ttxOVuaG8CWk89HYGvF6QEjN5XIFE47hg==
   dependencies:
     classnames "^2.2.0"
+    create-react-context "^0.2.3"
     escape-string-regexp "^1.0.5"
     invariant "^2.2.1"
     lodash "^4.17.2"
     prop-types "^15.5.8"
     prop-types-extra "^1.0.1"
-    react-onclickoutside "^6.1.1"
     react-overlays "^0.8.1"
-    react-popper "^0.10.4"
-    warning "^3.0.0"
+    react-popper "^1.0.0"
+    warning "^4.0.1"
 
 react-bootstrap@^0.32.1:
   version "0.32.1"
@@ -7557,10 +7600,6 @@ react-is@^16.6.3:
   version "16.6.3"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0"
 
-react-onclickoutside@^6.1.1:
-  version "6.7.0"
-  resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.7.0.tgz#997a4d533114c9a0a104913638aa26afc084f75c"
-
 react-overlays@^0.8.0, react-overlays@^0.8.1:
   version "0.8.3"
   resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-0.8.3.tgz#fad65eea5b24301cca192a169f5dddb0b20d3ac5"
@@ -7572,12 +7611,17 @@ react-overlays@^0.8.0, react-overlays@^0.8.1:
     react-transition-group "^2.2.0"
     warning "^3.0.0"
 
-react-popper@^0.10.4:
-  version "0.10.4"
-  resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-0.10.4.tgz#af2a415ea22291edd504678d7afda8a6ee3295aa"
+react-popper@^1.0.0:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.3.tgz#2c6cef7515a991256b4f0536cd4bdcb58a7b6af6"
+  integrity sha512-ynMZBPkXONPc5K4P5yFWgZx5JGAUIP3pGGLNs58cfAPgK67olx7fmLp+AdpZ0+GoQ+ieFDa/z4cdV6u7sioH6w==
   dependencies:
-    popper.js "^1.14.1"
+    "@babel/runtime" "^7.1.2"
+    create-react-context "<=0.2.2"
+    popper.js "^1.14.4"
     prop-types "^15.6.1"
+    typed-styles "^0.0.7"
+    warning "^4.0.2"
 
 react-prop-types@^0.4.0:
   version "0.4.0"
@@ -7604,14 +7648,15 @@ react-waypoint@^8.1.0:
     prop-types "^15.0.0"
     react-is "^16.6.3"
 
-react@^16.4.1:
-  version "16.4.1"
-  resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32"
+react@^16.7.0:
+  version "16.7.0"
+  resolved "https://registry.yarnpkg.com/react/-/react-16.7.0.tgz#b674ec396b0a5715873b350446f7ea0802ab6381"
+  integrity sha512-StCz3QY8lxTb5cl2HJxjwLFOXPIFQp+p+hxQfc8WE0QiLfCtIlKj8/+5tjjKm8uSTlAW+fCPaavGFS06V9Ar3A==
   dependencies:
-    fbjs "^0.8.16"
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
-    prop-types "^15.6.0"
+    prop-types "^15.6.2"
+    scheduler "^0.12.0"
 
 read-pkg-up@^1.0.1:
   version "1.0.1"
@@ -7755,6 +7800,11 @@ regenerator-runtime@^0.11.0:
   version "0.11.1"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
 
+regenerator-runtime@^0.12.0:
+  version "0.12.1"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
+  integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
+
 regenerator-transform@^0.10.0:
   version "0.10.1"
   resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
@@ -8173,6 +8223,14 @@ sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
 
+scheduler@^0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.12.0.tgz#8ab17699939c0aedc5a196a657743c496538647b"
+  integrity sha512-t7MBR28Akcp4Jm+QoR63XgAi9YgCUmgvDHqf5otgAj4QvdoBE4ImCX0ffehefePPG+aitiYHp0g/mW6s4Tp+dw==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+
 schema-utils@^0.4.4, schema-utils@^0.4.5:
   version "0.4.5"
   resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e"
@@ -9149,6 +9207,11 @@ type-is@~1.6.16:
     media-typer "0.3.0"
     mime-types "~2.1.18"
 
+typed-styles@^0.0.7:
+  version "0.0.7"
+  resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9"
+  integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==
+
 typedarray@^0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -9157,6 +9220,11 @@ ua-parser-js@0.7.12:
   version "0.7.12"
   resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"
 
+ua-parser-js@^0.7.18:
+  version "0.7.19"
+  resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
+  integrity sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==
+
 ua-parser-js@^0.7.9:
   version "0.7.17"
   resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
@@ -9472,6 +9540,13 @@ warning@^3.0.0:
   dependencies:
     loose-envify "^1.0.0"
 
+warning@^4.0.1, warning@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.2.tgz#aa6876480872116fa3e11d434b0d0d8d91e44607"
+  integrity sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==
+  dependencies:
+    loose-envify "^1.0.0"
+
 watchpack@^1.5.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"