Преглед изворни кода

Merge branch 'master' into rc/3.0.0

# Conflicts:
#	lib/views/admin/customize.html
Yuki Takei пре 8 година
родитељ
комит
25cc2506c9

+ 11 - 1
CHANGES.md

@@ -1,11 +1,21 @@
 CHANGES
 ========
 
-## 2.4.4-RC
+## 2.4.5-RC
 
+* 
+
+## 2.4.4
+
+* Feature: Autoformat Markdown Table
+* Feature: highlight.js Theme Selector
+* Fix: The bug of updating numbering list by codemirror
 * Fix: Template LangProcessor doesn't work
     * Introduced by 2.4.0
 * Support: Apply ESLint
+* Support: Upgrade libs
+    * react, react-dom
+    * codemirror, react-codemirror2
 
 ## 2.4.3
 

+ 10 - 0
lib/form/admin/customhighlightJsStyle.js

@@ -0,0 +1,10 @@
+'use strict';
+
+var form = require('express-form')
+  , field = form.field
+  ;
+
+module.exports = form(
+  field('settingForm[customize:highlightJsStyle]'),
+  field('settingForm[customize:highlightJsStyleBorder]').trim().toBooleanStrict()
+);

+ 1 - 0
lib/form/index.js

@@ -26,6 +26,7 @@ module.exports = {
     custombehavior: require('./admin/custombehavior'),
     customlayout: require('./admin/customlayout'),
     customfeatures: require('./admin/customfeatures'),
+    customhighlightJsStyle: require('./admin/customhighlightJsStyle'),
     userInvite: require('./admin/userInvite'),
     slackIwhSetting: require('./admin/slackIwhSetting'),
     slackSetting: require('./admin/slackSetting'),

+ 39 - 42
lib/locales/en-US/translation.json

@@ -255,7 +255,7 @@
     "Disable": "Disable"
 
   },
- "security_setting": {
+  "security_setting": {
     "Basic authentication": "Basic authentication",
     "Security settings": "Security settings",
     "Guest users access": "Guest users access",
@@ -270,8 +270,7 @@
     "only_those":" Only those whose e-mail address including the company address can register.",
     "insert_single":"Please insert single e-mail address per line.",
     "Authentication mechanism settings":"Authentication mechanism settings"
-
- },
+  },
 
   "markdown_setting": {
     "markdown_rendering": "You can change Markdown rendering settings.",
@@ -280,47 +279,45 @@
     "validate_comment": "Validate Line Break in the comment section",
     "treat_comment": "Treat line breaking in the comment section as <code>&lt;br&gt;</code> in HTML",
     "TBD": "(TBD: Markdown function in the comment section has not been implemented yet)"
+  },
 
-     },
-
-     "customize_page": {
-       "Behavior": "Behavior",
-       "Layout": "Layout",
-        "Function": "Function",
-        "function_choose": "You can choose Valid/Invalid of the function",
-        "Timeline function": "Timeline function",
-        "subpage_display": "You can show the timeline of the subpages.",
-        "performance_decrease": "If there are many subpages, performance decreases while page loading.",
-        "list_page_display": "You can speed up list page display by invalidating.",
-        "tab_switch": "Save tab-switching in the browser",
-        "save_edit": "Save edit tab and history tab switching in the browser and make it object for forward/back command of the browser.",
-        "by_invalidating": "By invalidating, you can make page transition as the only object for forward/back command of the browser.",
-        "Custom CSS": "Custom CSS",
-        "write_CSS": "You can write CSS that is applied to whole system.",
-        "reflect_change": "You need to reload the page to reflect the change.",
-        "ctrl_space": "Ctrl+Space to Autocomplete",
-        "Custom script": "Custom script",
-        "write_java": "You can write Javascript that is applied to whole system."
-
-
- },
+  "customize_page": {
+    "Behavior": "Behavior",
+    "Layout": "Layout",
+    "Function": "Function",
+    "function_choose": "You can choose Valid/Invalid of the function",
+    "Timeline function": "Timeline function",
+    "Code Highlight": "Code Highlight",
+    "Theme": "Theme",
+    "subpage_display": "You can show the timeline of the subpages.",
+    "performance_decrease": "If there are many subpages, performance decreases while page loading.",
+    "list_page_display": "You can speed up list page display by invalidating.",
+    "tab_switch": "Save tab-switching in the browser",
+    "save_edit": "Save edit tab and history tab switching in the browser and make it object for forward/back command of the browser.",
+    "by_invalidating": "By invalidating, you can make page transition as the only object for forward/back command of the browser.",
+    "Custom CSS": "Custom CSS",
+    "write_CSS": "You can write CSS that is applied to whole system.",
+    "reflect_change": "You need to reload the page to reflect the change.",
+    "ctrl_space": "Ctrl+Space to Autocomplete",
+    "Custom script": "Custom script",
+    "write_java": "You can write Javascript that is applied to whole system."
+  },
 
   "user_management": {
-        "User management": "User management",
-        "invite_users": "Invite new users",
-        "external_account": "External account management",
-        "user_list": "List of users",
-        "Date created": "Date created",
-        "Last login": "Last login",
-        "Manage": "Manage",
-        "Edit menu": "Edit menu",
-        "Reissue password": "Reissue password",
-        "Status":"Status",
-        "Deactivate account":"Deactivate account",
-        "your_own":"You cannot deactivate your own account",
-        "Administrator menu":"Administrator menu",
-        "cannot_remove":"You cannot remove yourself from administrator"
-
+    "User management": "User management",
+    "invite_users": "Invite new users",
+    "external_account": "External account management",
+    "user_list": "List of users",
+    "Date created": "Date created",
+    "Last login": "Last login",
+    "Manage": "Manage",
+    "Edit menu": "Edit menu",
+    "Reissue password": "Reissue password",
+    "Status":"Status",
+    "Deactivate account":"Deactivate account",
+    "your_own":"You cannot deactivate your own account",
+    "Administrator menu":"Administrator menu",
+    "cannot_remove":"You cannot remove yourself from administrator"
+  }
 
-      }
 }

+ 37 - 41
lib/locales/ja/translation.json

@@ -287,7 +287,6 @@
     "insert_single":"1行に1メールアドレス入力してください。",
     "Authentication mechanism settings":"認証機構設定"
   },
-
   "markdown_setting": {
     "markdown_rendering": "Markdownレンダリングの設定を変更できます。",
     "validate Line Break": "Line Break を有効にする",
@@ -298,48 +297,45 @@
 
   },
 
-     "customize_page": {
-       "Behavior": "挙動",
-       "Layout": "レイアウト",
-       "Function": "機能",
-       "function_choose": "機能の有効/無効を選択できます。",
-       "Timeline function": "タイムライン機能",
-       "subpage_display": "配下ページのタイムラインを表示できます。",
-       "performance_decrease": "配下ページが多い場合はページロード時のパフォーマンスが落ちます。",
-       "list_page_display": "無効化することでリストページの表示を高速化できます。",
-       "tab_switch":"タブ変更をブラウザ履歴に保存",
-       "save_edit": "編集タブやヒストリータブ等の切り替えをブラウザ履歴に保存し、ブラウザの戻る/進む操作の対象にします。",
-       "by_invalidating": "無効化することで、ページ遷移のみを戻る/進む操作の対象にすることができます。",
-       "Custom CSS": "カスタム CSS",
-        "write_CSS": " システム全体に適用されるCSSを記述できます。",
-        "reflect_change": "変更の反映はページの更新が必要です。",
-        "ctrl_space": "Ctrl+Space でコード補完",
-        "Custom script": "カスタムスクリプト",
-        "write_java": "システム全体に適用されるJavaScriptを記述できます。"
-
- },
-
-     "user_management": {
-        "User management": "ユーザー管理",
-        "invite_users": "新規ユーザーの招待",
-        "external_account": "外部アカウントの管理",
-        "user_list": "ユーザー一覧",
-        "Date created": "作成日",
-        "Last login": "最終ログイン",
-        "Manage": "操作",
-        "Edit menu": "編集メニュー",
-        "Reissue password": "パスワードの再発行",
-        "Status":"ステータス",
-        "Deactivate account":"アカウント停止",
-        "your_own":"自分自身のアカウントを停止することはできません",
-        "Administrator menu":"管理者メニュー",
-        "cannot_remove":"自分自身を管理者から外すことはできません"
-
-
-
+  "customize_page": {
+    "Behavior": "挙動",
+    "Layout": "レイアウト",
+    "Function": "機能",
+    "function_choose": "機能の有効/無効を選択できます。",
+    "Timeline function": "タイムライン機能",
+    "Code Highlight": "コードハイライト",
+    "Theme": "テーマ",
+    "subpage_display": "配下ページのタイムラインを表示できます。",
+    "performance_decrease": "配下ページが多い場合はページロード時のパフォーマンスが落ちます。",
+    "list_page_display": "無効化することでリストページの表示を高速化できます。",
+    "tab_switch": "タブ変更をブラウザ履歴に保存",
+    "save_edit": "編集タブやヒストリータブ等の切り替えをブラウザ履歴に保存し、ブラウザの戻る/進む操作の対象にします。",
+    "by_invalidating": "無効化することで、ページ遷移のみを戻る/進む操作の対象にすることができます。",
+    "Custom CSS": "カスタム CSS",
+    "write_CSS": " システム全体に適用されるCSSを記述できます。",
+    "reflect_change": "変更の反映はページの更新が必要です。",
+    "ctrl_space": "Ctrl+Space でコード補完",
+    "Custom script": "カスタムスクリプト",
+    "write_java": "システム全体に適用されるJavaScriptを記述できます。"
 
+  },
 
+  "user_management": {
+    "User management": "ユーザー管理",
+    "invite_users": "新規ユーザーの招待",
+    "external_account": "外部アカウントの管理",
+    "user_list": "ユーザー一覧",
+    "Date created": "作成日",
+    "Last login": "最終ログイン",
+    "Manage": "操作",
+    "Edit menu": "編集メニュー",
+    "Reissue password": "パスワードの再発行",
+    "Status": "ステータス",
+    "Deactivate account": "アカウント停止",
+    "your_own": "自分自身のアカウントを停止することはできません",
+    "Administrator menu": "管理者メニュー",
+    "cannot_remove": "自分自身を管理者から外すことはできません"
+  }
 
-      }
 }
 

+ 14 - 0
lib/models/config.js

@@ -85,6 +85,8 @@ module.exports = function(crowi) {
       'customize:css' : '',
       'customize:script' : '',
       'customize:header' : '',
+      'customize:highlightJsStyle' : 'github',
+      'customize:highlightJsStyleBorder' : false,
       'customize:behavior' : 'crowi',
       'customize:layout' : 'crowi',
       'customize:isEnabledTimeline' : true,
@@ -370,6 +372,18 @@ module.exports = function(crowi) {
     return getValueForCrowiNS(config, key);
   }
 
+  configSchema.statics.highlightJsStyle = function(config)
+  {
+    const key = 'customize:highlightJsStyle';
+    return getValueForCrowiNS(config, key);
+  }
+
+  configSchema.statics.highlightJsStyleBorder = function(config)
+  {
+    const key = 'customize:highlightJsStyleBorder';
+    return getValueForCrowiNS(config, key);
+  }
+
   configSchema.statics.isEnabledTimeline = function(config)
   {
     const key = 'customize:isEnabledTimeline';

+ 14 - 0
lib/routes/admin.js

@@ -130,8 +130,22 @@ module.exports = function(crowi, app) {
     var settingForm;
     settingForm = Config.setupCofigFormData('crowi', req.config);
 
+    const highlightJsCssSelectorOptions = {
+      "github":           { name: '[Light] Github',         border: false },
+      "github-gist":      { name: '[Light] Github Gist',    border: true },
+      "atom-one-light":   { name: '[Light] Atom One Light', border: true },
+      "xcode":            { name: '[Light] Xcode',          border: true },
+      "vs":               { name: '[Light] Vs',             border: true },
+      "atom-one-dark":    { name: '[Dark] Atom One Dark',   border: false },
+      "hybrid":           { name: '[Dark] Hybrid',          border: false },
+      "monokai":          { name: '[Dark] Monokai',         border: false },
+      "tomorrow-night":   { name: '[Dark] Tomorrow Night',  border: false },
+      "vs2015":           { name: '[Dark] Vs 2015',         border: false },
+    }
+
     return res.render('admin/customize', {
       settingForm: settingForm,
+      highlightJsCssSelectorOptions: highlightJsCssSelectorOptions
     });
   };
 

+ 1 - 0
lib/routes/index.js

@@ -77,6 +77,7 @@ module.exports = function(crowi, app) {
   app.post('/_api/admin/customize/behavior' , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.custombehavior, admin.api.customizeSetting);
   app.post('/_api/admin/customize/layout'   , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customlayout, admin.api.customizeSetting);
   app.post('/_api/admin/customize/features' , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customfeatures, admin.api.customizeSetting);
+  app.post('/_api/admin/customize/highlightJsStyle' , loginRequired(crowi, app) , middleware.adminRequired() , csrf, form.admin.customhighlightJsStyle, admin.api.customizeSetting);
 
   // search admin
   app.get('/admin/search'              , loginRequired(crowi, app) , middleware.adminRequired() , admin.search.index);

+ 10 - 0
lib/util/swigFunctions.js

@@ -119,6 +119,16 @@ module.exports = function(crowi, app, req, locals) {
     return Config.layoutType(config);
   }
 
+  locals.highlightJsStyle = function() {
+    var config = crowi.getConfig()
+    return Config.highlightJsStyle(config);
+  }
+
+  locals.highlightJsStyleBorder = function() {
+    var config = crowi.getConfig()
+    return Config.highlightJsStyleBorder(config);
+  }
+
   locals.isEnabledTimeline = function() {
     var config = crowi.getConfig()
     return Config.isEnabledTimeline(config);

+ 76 - 5
lib/views/admin/customize.html

@@ -13,7 +13,6 @@
   </style>
 {% endblock %}
 
-
 {% block content_header %}
 <div class="header-wrap">
   <header id="page-header">
@@ -142,7 +141,6 @@
       </fieldset>
       </form>
 
-
       <form action="/_api/admin/customize/features" method="post" class="form-horizontal" id="customfeaturesSettingForm" role="form">
       <fieldset>
       <legend>{{ t('customize_page.Function') }}</legend>
@@ -203,6 +201,67 @@
       </fieldset>
       </form>
 
+      <form action="/_api/admin/customize/highlightJsStyle" method="post" class="form-horizontal" id="cutomhighlightJsStyleSettingForm" role="form">
+        <fieldset>
+          <legend>{{ t('customize_page.Code Highlight') }}</legend>
+          <div class="form-group">
+            <label for="settingForm[customize:highlightJsStyle]" class="col-xs-3 control-label">{{ t('customize_page.Theme') }}</label>
+            <div class="col-xs-9">
+              <select class="form-control selectpicker" name="settingForm[customize:highlightJsStyle]" onChange="selectHighlightJsStyle(event)">
+                {% for key in Object.keys(highlightJsCssSelectorOptions) %}
+                  <option value={{key}} {% if key == highlightJsStyle() %} selected {% endif %}>{{highlightJsCssSelectorOptions[key].name}}</option>
+                {% endfor %}
+              </select>
+            </div>
+          </div>
+
+          <div class="form-group">
+            <label for="settingForm[customize:highlightJsStyleBorder]" class="col-xs-3 control-label">(TBD) Border</label>
+            <div class="col-xs-9">
+              <div class="btn-group btn-toggle" data-toggle="buttons">
+                <label class="btn btn-default btn-rounded btn-outline {% if settingForm['customize:highlightJsStyleBorder'] %}active{% endif %}" data-active-class="primary">
+                  <input name="settingForm[customize:highlightJsStyleBorder]" value="true" type="radio"
+                      {% if true === settingForm['customize:highlightJsStyleBorder'] %}checked{% endif %}> ON
+                </label>
+                <label class="btn btn-default btn-rounded btn-outline {% if !settingForm['customize:highlightJsStyleBorder'] %}active{% endif %}" data-active-class="default">
+                  <input name="settingForm[customize:highlightJsStyleBorder]" value="false" type="radio"
+                      {% if !settingForm['customize:highlightJsStyleBorder'] %}checked{% endif %}> OFF
+                </label>
+              </div>
+            </div>
+          </div>
+
+          <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@9.12.0/styles/{{ highlightJsStyle() }}.css" class="highlightJsCss">
+
+          <p class="help-block">
+            Examples:
+            <pre class="hljs"><code class="highlightjs-demo">function $initHighlight(block, cls) {
+  try {
+    if (cls.search(/\bno\-highlight\b/) != -1)
+      return process(block, true, 0x0F) +
+              ` class="${cls}"`;
+  } catch (e) {
+    /* handle exception */
+  }
+  for (var i = 0 / 2; i < classes.length; i++) {
+    if (checkCondition(classes[i]) === undefined)
+      console.log('undefined');
+  }
+}
+
+export  $initHighlight;</code></pre>
+          </p>
+
+          <div class="form-group">
+            <div class="col-xs-offset-5 col-xs-6">
+              <input type="hidden" name="_csrf" value="{{ csrf() }}">
+              <button type="submit" class="btn btn-primary">{{ t('Update') }}</button>
+            </div>
+          </div>
+
+        </fieldset>
+      </form>
+
       <form action="/_api/admin/customize/header" method="post" class="form-horizontal" id="cutomheaderSettingForm" role="form">
       <fieldset>
         <legend>カスタムヘッダーHTML</legend>
@@ -214,7 +273,7 @@
 
         <p class="help-block">
           Examples:
-          <pre><code>&lt;script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.12.0/build/languages/yaml.min.js" defer&gt;&lt;/script&gt;</code></pre>
+          <pre class="hljs"><code>&lt;script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.12.0/build/languages/yaml.min.js" defer&gt;&lt;/script&gt;</code></pre>
         </p>
 
         <div class="form-group">
@@ -300,7 +359,7 @@
         </p>
         <p class="help-block">
           Examples:
-<pre><code>console.log($('.main-container'));
+<pre class="hljs"><code>console.log($('.main-container'));
 
 window.addEventListener('load', (event) => {
   console.log('config: ', crowi.config);
@@ -334,7 +393,7 @@ window.addEventListener('load', (event) => {
   </div>
 
   <script>
-    $('#cutomcssSettingForm, #cutomscriptSettingForm, #cutomlayoutSettingForm, #cutombehaviorSettingForm, #customfeaturesSettingForm, #cutomheaderSettingForm').each(function() {
+    $('#cutomcssSettingForm, #cutomscriptSettingForm, #cutomlayoutSettingForm, #cutombehaviorSettingForm, #customfeaturesSettingForm, #cutomheaderSettingForm, #cutomhighlightJsStyleSettingForm').each(function() {
       $(this).submit(function()
       {
         function showMessage(formId, msg, status) {
@@ -382,6 +441,18 @@ window.addEventListener('load', (event) => {
       });
     });
 
+    // init highlight.js
+    hljs.initHighlightingOnLoad()
+
+    function selectHighlightJsStyle(event) {
+      var highlightJsCssDOM = $(".highlightJsCss")[0]
+      // selected value
+      var val = event.target.value
+      // replace css url
+      // see https://regex101.com/r/gBNZYu/4
+      highlightJsCssDOM.href = highlightJsCssDOM.href.replace(/[^/]+\.css$/, `${val}.css`);
+    }
+
   </script>
 
 </div>

+ 1 - 1
lib/views/layout/layout.html

@@ -92,7 +92,7 @@ gh/highlightjs/cdn-release@9.12.0/build/languages/yaml.min.js
   <!-- emojione -->
   <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/emojione@3.1.2/extras/css/emojione.min.css">
   <!-- highlight.js -->
-  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@9.12.0/styles/github.css">
+  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@9.12.0/styles/{{ highlightJsStyle() }}.css">
 
   {% block html_additional_headers %}{% endblock %}
 

+ 0 - 2
lib/views/page_presentation.html

@@ -49,8 +49,6 @@ gh/highlightjs/cdn-release@9.12.0/build/languages/yaml.min.js
 
     <!-- Google Fonts -->
     <link href='https://fonts.googleapis.com/css?family=Lato:400,700' rel='stylesheet' type='text/css'>
-    <!-- highlight.js -->
-    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@9.12.0/styles/github.css">
 
     <style>
       {{ customCss() }}

+ 5 - 5
package.json

@@ -1,6 +1,6 @@
 {
   "name": "crowi-plus",
-  "version": "2.4.4-RC",
+  "version": "2.4.5-RC",
   "description": "Enhanced Crowi",
   "tags": [
     "wiki",
@@ -62,7 +62,7 @@
     "bootstrap-sass": "~3.3.6",
     "bootstrap-select": "^1.12.4",
     "check-node-version": "^3.1.1",
-    "codemirror": "^5.33.0",
+    "codemirror": "^5.36.0",
     "connect-flash": "~0.1.1",
     "connect-redis": "^3.3.0",
     "cookie-parser": "^1.4.3",
@@ -122,12 +122,12 @@
     "passport-local": "^1.0.0",
     "pino-clf": "^1.0.2",
     "plantuml-encoder": "^1.2.5",
-    "react": "^16.0.0",
+    "react": "^16.2.0",
     "react-bootstrap": "^0.32.0",
     "react-bootstrap-typeahead": "^2.0.2",
     "react-clipboard.js": "^1.1.2",
-    "react-codemirror2": "^4.0.0",
-    "react-dom": "^16.0.0",
+    "react-codemirror2": "^4.2.1",
+    "react-dom": "^16.2.0",
     "react-dropzone": "^4.2.7",
     "redis": "^2.7.1",
     "reveal.js": "^3.5.0",

+ 0 - 2
resource/js/components/PageEditor/MarkdownListInterceptor.js

@@ -1,6 +1,4 @@
 import { BasicInterceptor } from 'crowi-pluginkit';
-import * as codemirror from 'codemirror';
-
 import mlu from './MarkdownListUtil';
 
 export default class MarkdownListInterceptor extends BasicInterceptor {

+ 2 - 0
resource/js/components/PageEditor/MarkdownListUtil.js

@@ -1,3 +1,5 @@
+import * as codemirror from 'codemirror';
+
 /**
  * Utility for markdown list
  */

+ 8 - 8
yarn.lock

@@ -1539,9 +1539,9 @@ code-point-at@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
 
-codemirror@^5.33.0:
-  version "5.33.0"
-  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.33.0.tgz#462ad9a6fe8d38b541a9536a3997e1ef93b40c6a"
+codemirror@^5.36.0:
+  version "5.36.0"
+  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.36.0.tgz#1172ad9dc298056c06e0b34e5ccd23825ca15b40"
 
 color-convert@^1.3.0, color-convert@^1.9.0:
   version "1.9.1"
@@ -5570,11 +5570,11 @@ react-clipboard.js@^1.1.2:
     clipboard "^1.6.1"
     prop-types "^15.5.0"
 
-react-codemirror2@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-4.0.0.tgz#88a30bb082cb87755a80e057d4c7b577456c38f0"
+react-codemirror2@^4.2.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-4.2.1.tgz#4ad3c5c60ebbcb34880f961721b51527324ec021"
 
-react-dom@^16.0.0:
+react-dom@^16.2.0:
   version "16.2.0"
   resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044"
   dependencies:
@@ -5628,7 +5628,7 @@ react-transition-group@^2.0.0, react-transition-group@^2.2.0:
     prop-types "^15.5.8"
     warning "^3.0.0"
 
-react@^16.0.0:
+react@^16.2.0:
   version "16.2.0"
   resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba"
   dependencies: