diff --git a/package.json b/package.json
index d57166e8e..48f370d56 100644
--- a/package.json
+++ b/package.json
@@ -132,7 +132,7 @@
"react-shortcuts": "^2.0.0",
"react-sparklines": "1.6.0",
"react-test-renderer": "^15.6.2",
- "react-virtualized": "^8.0.8",
+ "react-virtualized": "^9.15.0",
"readable-stream": "^2.3.3",
"redux": "^3.7.2",
"redux-thunk": "^2.0.1",
diff --git a/src/common/base-component.js b/src/common/base-component.js
index 4405e84f2..9ee83aea9 100644
--- a/src/common/base-component.js
+++ b/src/common/base-component.js
@@ -10,7 +10,7 @@ import getEventValue from './get-event-value'
const VERBOSE = false
const get = (object, path, depth) => {
- if (depth >= path.length) {
+ if (object == null || depth >= path.length) {
return object
}
diff --git a/src/common/form/index.js b/src/common/form/index.js
index bbda123b1..e63dcf806 100644
--- a/src/common/form/index.js
+++ b/src/common/form/index.js
@@ -17,7 +17,6 @@ import propTypes from '../prop-types-decorator'
import { formatSizeRaw, parseSize } from '../utils'
export Select from './select'
-export SelectPlainObject from './select-plain-object'
// ===================================================================
diff --git a/src/common/form/select-plain-object.js b/src/common/form/select-plain-object.js
deleted file mode 100644
index b7b71c048..000000000
--- a/src/common/form/select-plain-object.js
+++ /dev/null
@@ -1,118 +0,0 @@
-import uncontrollableInput from 'uncontrollable-input'
-import Component from 'base-component'
-import find from 'lodash/find'
-import map from 'lodash/map'
-import React from 'react'
-
-import propTypes from '../prop-types-decorator'
-
-import Select from './select'
-
-@propTypes({
- autoFocus: propTypes.bool,
- disabled: propTypes.bool,
- optionRenderer: propTypes.func,
- multi: propTypes.bool,
- onChange: propTypes.func,
- options: propTypes.array,
- placeholder: propTypes.node,
- predicate: propTypes.func,
- required: propTypes.bool,
- value: propTypes.any,
-})
-@uncontrollableInput()
-export default class SelectPlainObject extends Component {
- componentDidMount () {
- const { options, value } = this.props
-
- this.setState({
- options: this._computeOptions(options),
- value: this._computeValue(value, this.props),
- })
- }
-
- componentWillReceiveProps (newProps) {
- if (newProps !== this.props) {
- this.setState({
- options: this._computeOptions(newProps.options),
- value: this._computeValue(newProps.value, newProps),
- })
- }
- }
-
- _computeValue (value, props = this.props) {
- let { optionKey } = props
- optionKey || (optionKey = 'id')
- const reduceValue = value =>
- value != null ? value[optionKey] || value : ''
- if (props.multi) {
- if (!Array.isArray(value)) {
- value = [value]
- }
- return map(value, reduceValue)
- }
-
- return reduceValue(value)
- }
-
- _computeOptions (options) {
- const { optionKey = 'id' } = this.props
- const { optionRenderer = o => o.label || o[optionKey] || o } = this.props
- return map(options, option => ({
- value: option[optionKey] || option,
- label: optionRenderer(option),
- }))
- }
-
- _getObject (value) {
- if (value == null) {
- return undefined
- }
-
- const { optionKey = 'id', options } = this.props
-
- const pickValue = value => {
- value = value.value || value
- return find(
- options,
- option => option[optionKey] === value || option === value
- )
- }
-
- if (this.props.multi) {
- return map(value, pickValue)
- }
-
- return pickValue(value)
- }
-
- _handleChange = value => {
- const { onChange } = this.props
-
- if (onChange) {
- onChange(this._getObject(value))
- }
- }
-
- _renderOption = option => option.label
-
- render () {
- const { props, state } = this
-
- return (
-
- )
- }
-}
diff --git a/src/common/form/select.js b/src/common/form/select.js
index f4f965f7b..119160231 100644
--- a/src/common/form/select.js
+++ b/src/common/form/select.js
@@ -1,138 +1,160 @@
-import map from 'lodash/map'
-import React, { Component } from 'react'
+import classNames from 'classnames'
+import isEmpty from 'lodash/isEmpty'
+import PropTypes from 'prop-types'
+import React from 'react'
import ReactSelect from 'react-select'
-import sum from 'lodash/sum'
-import { AutoSizer, CellMeasurer, List } from 'react-virtualized'
-
-import propTypes from '../prop-types-decorator'
-
-const SELECT_MENU_STYLE = {
- overflow: 'hidden',
-}
+import uncontrollableInput from 'uncontrollable-input'
+import {
+ AutoSizer,
+ CellMeasurer,
+ CellMeasurerCache,
+ List,
+} from 'react-virtualized'
const SELECT_STYLE = {
minWidth: '10em',
}
-
-const LIST_STYLE = {
- whiteSpace: 'normal',
+const MENU_STYLE = {
+ overflow: 'hidden',
}
-const MAX_OPTIONS = 5
-
-// See: https://github.com/bvaughn/react-virtualized-select/blob/master/source/VirtualizedSelect/VirtualizedSelect.js
-@propTypes({
- maxHeight: propTypes.number,
-})
-export default class Select extends Component {
+@uncontrollableInput()
+export default class Select extends React.PureComponent {
static defaultProps = {
maxHeight: 200,
- optionRenderer: (option, labelKey) => option[labelKey],
+
+ multi: ReactSelect.defaultProps.multi,
+ options: [],
+ required: ReactSelect.defaultProps.required,
+ valueKey: ReactSelect.defaultProps.valueKey,
}
- _renderMenu = ({ focusedOption, options, ...otherOptions }) => {
+ static propTypes = {
+ autoSelectSingleOption: PropTypes.bool, // default to props.required
+ maxHeight: PropTypes.number,
+ options: PropTypes.array.isRequired, // cannot be an object
+ }
+
+ _cellMeasurerCache = new CellMeasurerCache({
+ fixedWidth: true,
+ })
+
+ // https://github.com/JedWatson/react-select/blob/dd32c27d7ea338a93159da5e40bc06697d0d86f9/src/utils/defaultMenuRenderer.js#L4
+ _renderMenu (opts) {
+ const { focusOption, options, selectValue } = opts
+
+ const focusFromEvent = event =>
+ focusOption(options[event.currentTarget.dataset.index])
+ const selectFromEvent = event =>
+ selectValue(options[event.currentTarget.dataset.index])
+ const renderRow = opts2 =>
+ this._renderRow(opts, opts2, focusFromEvent, selectFromEvent)
+
+ let focusedOptionIndex = options.indexOf(opts.focusedOption)
+ if (focusedOptionIndex === -1) {
+ focusedOptionIndex = undefined
+ }
+
+ const { length } = options
const { maxHeight } = this.props
+ const { rowHeight } = this._cellMeasurerCache
- const focusedOptionIndex = options.indexOf(focusedOption)
- let height = options.length > MAX_OPTIONS && maxHeight
-
- const wrappedRowRenderer = ({ index, key, style }) =>
- this._optionRenderer({
- ...otherOptions,
- focusedOption,
- focusedOptionIndex,
- key,
- option: options[index],
- options,
- style,
- })
+ let height = 0
+ for (let i = 0; i < length; ++i) {
+ height += rowHeight({ index: i })
+ if (height > maxHeight) {
+ height = maxHeight
+ break
+ }
+ }
return (
- {({ width }) =>
- width ? (
-
- wrappedRowRenderer({ index: rowIndex })
- }
- columnCount={1}
- rowCount={options.length}
- // FIXME: 16 px: ugly workaround to take into account the scrollbar
- // during the offscreen render to measure the row height
- // See https://github.com/bvaughn/react-virtualized/issues/401
- width={width - 16}
- >
- {({ getRowHeight }) => {
- if (options.length <= MAX_OPTIONS) {
- height = sum(
- map(options, (_, index) => getRowHeight({ index }))
- )
- }
-
- return (
-
- )
- }}
-
- ) : null
- }
+ {({ width }) => (
+
+ )}
)
}
+ _renderMenu = this._renderMenu.bind(this)
- _optionRenderer = ({
- focusedOption,
- focusOption,
- key,
- labelKey,
- option,
- style,
- selectValue,
- }) => {
- let className = 'Select-option'
-
- if (option === focusedOption) {
- className += ' is-focused'
- }
-
+ _renderRow (
+ {
+ focusedOption,
+ focusOption,
+ inputValue,
+ optionClassName,
+ optionRenderer,
+ options,
+ selectValue,
+ },
+ { index, key, parent, style },
+ focusFromEvent,
+ selectFromEvent
+ ) {
+ const option = options[index]
const { disabled } = option
- if (disabled) {
- className += ' is-disabled'
- }
-
- const { props } = this
-
return (
-
selectValue(option)}
- onMouseOver={disabled ? undefined : () => focusOption(option)}
- style={style}
+
- {props.optionRenderer(option, labelKey)}
-
+
+ {optionRenderer(option, index, inputValue)}
+
+
)
}
+ componentDidMount () {
+ this.componentDidUpdate()
+ }
+
+ componentDidUpdate () {
+ const { props } = this
+ const { autoSelectSingleOption = props.required, options } = props
+ if (autoSelectSingleOption && options != null && options.length === 1) {
+ const value = options[0][props.valueKey]
+ props.onChange(props.multi ? [value] : value)
+ }
+ }
+
render () {
+ const { props } = this
+ const { multi } = props
return (
)
}
diff --git a/src/common/select-objects.js b/src/common/select-objects.js
index 09779c160..32be7d416 100644
--- a/src/common/select-objects.js
+++ b/src/common/select-objects.js
@@ -1,5 +1,5 @@
import React from 'react'
-import classNames from 'classnames'
+import PropTypes from 'prop-types'
import { parse as parseRemote } from 'xo-remote-parser'
import {
assign,
@@ -23,14 +23,12 @@ import {
import _ from './intl'
import Button from './button'
-import Component from './base-component'
import Icon from './icon'
-import propTypes from './prop-types-decorator'
import renderXoItem from './render-xo-item'
+import Select from './form/select'
import store from './store'
import Tooltip from './tooltip'
import uncontrollableInput from 'uncontrollable-input'
-import { Select } from './form'
import {
createCollectionWrapper,
createFilter,
@@ -112,23 +110,18 @@ const options = props => ({
* ]
* }
*/
-@propTypes({
- autoFocus: propTypes.bool,
- clearable: propTypes.bool,
- disabled: propTypes.bool,
- hasSelectAll: propTypes.bool,
- multi: propTypes.bool,
- onChange: propTypes.func,
- placeholder: propTypes.any.isRequired,
- required: propTypes.bool,
- value: propTypes.any,
- xoContainers: propTypes.array,
- xoObjects: propTypes.oneOfType([
- propTypes.array,
- propTypes.objectOf(propTypes.array),
- ]).isRequired,
-})
-export class GenericSelect extends Component {
+class GenericSelect extends React.Component {
+ static propTypes = {
+ hasSelectAll: PropTypes.bool,
+ multi: PropTypes.bool,
+ onChange: PropTypes.func.isRequired,
+ xoContainers: PropTypes.array,
+ xoObjects: PropTypes.oneOfType([
+ PropTypes.array,
+ PropTypes.objectOf(PropTypes.array),
+ ]).isRequired,
+ }
+
_getObjectsById = createSelector(
() => this.props.xoObjects,
objects =>
@@ -142,8 +135,8 @@ export class GenericSelect extends Component {
// createCollectionWrapper with a depth?
const { name } = this.constructor
- let options = []
- if (!containers) {
+ let options
+ if (containers === undefined) {
if (__DEV__ && !isArray(objects)) {
throw new Error(
`${name}: without xoContainers, xoObjects must be an array`
@@ -151,27 +144,29 @@ export class GenericSelect extends Component {
}
options = map(objects, getOption)
- } else if (__DEV__ && isArray(objects)) {
- throw new Error(
- `${name}: with xoContainers, xoObjects must be an object`
- )
+ } else {
+ if (__DEV__ && isArray(objects)) {
+ throw new Error(
+ `${name}: with xoContainers, xoObjects must be an object`
+ )
+ }
+
+ options = []
+ forEach(containers, container => {
+ options.push({
+ disabled: true,
+ xoItem: container,
+ })
+
+ forEach(objects[container.id], object => {
+ options.push(getOption(object, container))
+ })
+ })
}
- forEach(containers, container => {
- options.push({
- disabled: true,
- xoItem: container,
- })
-
- forEach(objects[container.id], object => {
- options.push(getOption(object, container))
- })
- })
-
- const values = this._getSelectValue()
const objectsById = this._getObjectsById()
const addIfMissing = val => {
- if (val && !objectsById[val]) {
+ if (val != null && !(val in objectsById)) {
options.push({
disabled: true,
id: val,
@@ -185,6 +180,7 @@ export class GenericSelect extends Component {
}
}
+ const values = this._getSelectedIds()
if (isArray(values)) {
forEach(values, addIfMissing)
} else {
@@ -195,27 +191,25 @@ export class GenericSelect extends Component {
}
)
- _getSelectValue = createSelector(
+ _getSelectedIds = createSelector(
() => this.props.value,
createCollectionWrapper(getIds)
)
- _getNewSelectedObjects = createSelector(
- this._getObjectsById,
- value => value,
- (objectsById, value) =>
- value == null
- ? value
- : isArray(value)
+ _getSelectedObjects = (() => {
+ const helper = createSelector(
+ this._getObjectsById,
+ value => value,
+ (objectsById, value) =>
+ isArray(value)
? map(value, value => objectsById[value.value])
: objectsById[value.value]
- )
+ )
+ return value => (value == null ? value : helper(value))
+ })()
_onChange = value => {
- const { onChange } = this.props
- if (onChange) {
- onChange(this._getNewSelectedObjects(value))
- }
+ this.props.onChange(this._getSelectedObjects(value))
}
_selectAll = () => {
@@ -225,46 +219,31 @@ export class GenericSelect extends Component {
// GroupBy: Display option with margin if not disabled and containers exists.
_renderOption = option => (
{renderXoItem(option.xoItem)}
)
render () {
- const {
- autoFocus,
- disabled,
- hasSelectAll,
- multi,
- placeholder,
- required,
-
- clearable = Boolean(multi || !required),
- } = this.props
+ const { hasSelectAll, xoContainers, xoObjects, ...props } = this.props
const select = (
)
- if (!multi || !hasSelectAll) {
+ if (!props.multi || !hasSelectAll) {
return select
}
@@ -295,36 +274,34 @@ const makeStoreSelect = (createSelectors, defaultProps) =>
const makeSubscriptionSelect = (subscribe, props) =>
uncontrollableInput(options)(
- class extends Component {
- constructor (props) {
- super(props)
+ class extends React.PureComponent {
+ state = {}
- this._getFilteredXoContainers = createFilter(
- () => this.state.xoContainers,
- () => this.props.containerPredicate
- )
+ _getFilteredXoContainers = createFilter(
+ () => this.state.xoContainers,
+ () => this.props.containerPredicate
+ )
- this._getFilteredXoObjects = createSelector(
- () => this.state.xoObjects,
- () => this.state.xoContainers && this._getFilteredXoContainers(),
- () => this.props.predicate,
- (xoObjects, xoContainers, predicate) => {
- if (xoContainers == null) {
- return filter(xoObjects, predicate)
- } else {
- // Filter xoObjects with `predicate`...
- const filteredObjects = mapValues(xoObjects, xoObjectsGroup =>
- filter(xoObjectsGroup, predicate)
- )
- // ...and keep only those whose xoContainer hasn't been filtered out
- return pick(
- filteredObjects,
- map(xoContainers, container => container.id)
- )
- }
+ _getFilteredXoObjects = createSelector(
+ () => this.state.xoObjects,
+ () => this.state.xoContainers && this._getFilteredXoContainers(),
+ () => this.props.predicate,
+ (xoObjects, xoContainers, predicate) => {
+ if (xoContainers == null) {
+ return filter(xoObjects, predicate)
+ } else {
+ // Filter xoObjects with `predicate`...
+ const filteredObjects = mapValues(xoObjects, xoObjectsGroup =>
+ filter(xoObjectsGroup, predicate)
+ )
+ // ...and keep only those whose xoContainer hasn't been filtered out
+ return pick(
+ filteredObjects,
+ map(xoContainers, container => container.id)
+ )
}
- )
- }
+ }
+ )
componentWillMount () {
this.componentWillUnmount = subscribe(::this.setState)
@@ -577,36 +554,35 @@ export const SelectHighLevelObject = makeStoreSelect(
// ===================================================================
-export const SelectVdi = propTypes({
- srPredicate: propTypes.func,
-})(
- makeStoreSelect(
- () => {
- const getSrs = createGetObjectsOfType('SR').filter(
- (_, props) => props.srPredicate
- )
- const getVdis = createGetObjectsOfType('VDI')
- .filter(
- createSelector(
- getSrs,
- getPredicate,
- (srs, predicate) =>
- predicate
- ? vdi => srs[vdi.$SR] && predicate(vdi)
- : vdi => srs[vdi.$SR]
- )
+export const SelectVdi = makeStoreSelect(
+ () => {
+ const getSrs = createGetObjectsOfType('SR').filter(
+ (_, props) => props.srPredicate
+ )
+ const getVdis = createGetObjectsOfType('VDI')
+ .filter(
+ createSelector(
+ getSrs,
+ getPredicate,
+ (srs, predicate) =>
+ predicate
+ ? vdi => srs[vdi.$SR] && predicate(vdi)
+ : vdi => srs[vdi.$SR]
)
- .sort()
- .groupBy('$SR')
+ )
+ .sort()
+ .groupBy('$SR')
- return {
- xoObjects: getVdis,
- xoContainers: getSrs.sort(),
- }
- },
- { placeholder: _('selectVdis') }
- )
+ return {
+ xoObjects: getVdis,
+ xoContainers: getSrs.sort(),
+ }
+ },
+ { placeholder: _('selectVdis') }
)
+SelectVdi.propTypes = {
+ srPredicate: PropTypes.func,
+}
// ===================================================================
@@ -747,7 +723,7 @@ export const SelectResourceSet = makeSubscriptionSelect(
// ===================================================================
-export class SelectResourceSetsVmTemplate extends Component {
+export class SelectResourceSetsVmTemplate extends React.PureComponent {
get value () {
return this.refs.select.value
}
@@ -782,7 +758,7 @@ export class SelectResourceSetsVmTemplate extends Component {
// ===================================================================
-export class SelectResourceSetsSr extends Component {
+export class SelectResourceSetsSr extends React.PureComponent {
get value () {
return this.refs.select.value
}
@@ -813,7 +789,7 @@ export class SelectResourceSetsSr extends Component {
// ===================================================================
-export class SelectResourceSetsVdi extends Component {
+export class SelectResourceSetsVdi extends React.PureComponent {
get value () {
return this.refs.select.value
}
@@ -853,7 +829,7 @@ export class SelectResourceSetsVdi extends Component {
// ===================================================================
-export class SelectResourceSetsNetwork extends Component {
+export class SelectResourceSetsNetwork extends React.PureComponent {
get value () {
return this.refs.select.value
}
@@ -894,12 +870,13 @@ export class SelectResourceSetsNetwork extends Component {
ipPools: subscribeIpPools,
resourceSets: subscribeResourceSets,
}))
-@propTypes({
- containerPredicate: propTypes.func,
- predicate: propTypes.func,
- resourceSetId: propTypes.string.isRequired,
-})
-export class SelectResourceSetIp extends Component {
+export class SelectResourceSetIp extends React.Component {
+ static propTypes = {
+ containerPredicate: PropTypes.func,
+ predicate: PropTypes.func,
+ resourceSetId: PropTypes.string.isRequired,
+ }
+
get value () {
return this.refs.select.value
}
@@ -958,7 +935,7 @@ export class SelectResourceSetIp extends Component {
// ===================================================================
-export class SelectSshKey extends Component {
+export class SelectSshKey extends React.PureComponent {
get value () {
return this.refs.select.value
}
diff --git a/src/xo-app/backup/file-restore/restore-file-modal.js b/src/xo-app/backup/file-restore/restore-file-modal.js
index 7fffaca2d..8fd6cd292 100644
--- a/src/xo-app/backup/file-restore/restore-file-modal.js
+++ b/src/xo-app/backup/file-restore/restore-file-modal.js
@@ -5,12 +5,12 @@ import endsWith from 'lodash/endsWith'
import Icon from 'icon'
import React from 'react'
import replace from 'lodash/replace'
+import Select from 'form/select'
import Tooltip from 'tooltip'
import { Container, Col, Row } from 'grid'
import { createSelector } from 'reselect'
import { formatSize } from 'utils'
import { FormattedDate } from 'react-intl'
-import { SelectPlainObject } from 'form'
import { filter, includes, isEmpty, map } from 'lodash'
import { scanDisk, scanFiles } from 'xo'
@@ -251,23 +251,25 @@ export default class RestoreFileModalBody extends Component {
return (
-
{backup && [
,
-
,
]}
{scanDiskError && (
@@ -279,13 +281,14 @@ export default class RestoreFileModalBody extends Component {
!scanDiskError &&
!noPartitions && [
,
-
,
]}
{(partition || (disk && !scanDiskError && noPartitions)) && [
@@ -311,13 +314,14 @@ export default class RestoreFileModalBody extends Component {
,
-
,
,
diff --git a/src/xo-app/backup/restore/index.js b/src/xo-app/backup/restore/index.js
index 23a65ddae..8615fba6e 100644
--- a/src/xo-app/backup/restore/index.js
+++ b/src/xo-app/backup/restore/index.js
@@ -22,7 +22,7 @@ import { addSubscriptions, noop } from 'utils'
import { Container, Row, Col } from 'grid'
import { FormattedDate, injectIntl } from 'react-intl'
import { info, error } from 'notification'
-import { SelectPlainObject, Toggle } from 'form'
+import { Select, Toggle } from 'form'
import {
importBackup,
@@ -210,14 +210,15 @@ class _ModalBody extends Component {
return (
-
{
const j = {}
for (const id in jobs) {
@@ -279,7 +270,7 @@ export default class Jobs extends Component {
params.value
),
},
- userId: owner,
+ userId: owner !== undefined ? owner : this.props.currentUser.id,
timeout: timeout ? timeout * 1e3 : undefined,
}
@@ -372,8 +363,8 @@ export default class Jobs extends Component {
type === 'user' && permission === 'admin'
render () {
- const { state } = this
- const { action, actions, job, jobs, owner } = state
+ const { props, state } = this
+ const { action, actions, job, jobs } = state
const { formatMessage } = this.props.intl
const isJobUserMissing = this._getIsJobUserMissing()
@@ -387,7 +378,7 @@ export default class Jobs extends Component {
placeholder={_('jobOwnerPlaceholder')}
predicate={this._subjectPredicate}
required
- value={owner}
+ value={defined(state.owner, () => props.currentUser.id)}
/>
-
-
diff --git a/src/xo-app/settings/acls/index.js b/src/xo-app/settings/acls/index.js
index d063e957f..f96a99f3a 100644
--- a/src/xo-app/settings/acls/index.js
+++ b/src/xo-app/settings/acls/index.js
@@ -166,7 +166,7 @@ export default class Acls extends Component {
constructor (props) {
super(props)
this.state = {
- action: '',
+ action: null,
objects: [],
subjects: [],
typeFilters: {},
diff --git a/yarn.lock b/yarn.lock
index 63b84d980..fac5b12f3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1270,7 +1270,7 @@ babel-runtime@^5.8.25:
dependencies:
core-js "^1.0.0"
-babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0:
+babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
@@ -7292,14 +7292,15 @@ react-transition-group@^2.2.0:
prop-types "^15.5.8"
warning "^3.0.0"
-react-virtualized@^8.0.8:
- version "8.11.4"
- resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-8.11.4.tgz#0bb94f1ecbd286d07145ce63983d0a11724522c0"
+react-virtualized@^9.15.0:
+ version "9.17.3"
+ resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.17.3.tgz#3854588f067235c00ae5cd134d96508956630033"
dependencies:
- babel-runtime "^6.11.6"
+ babel-runtime "^6.26.0"
classnames "^2.2.3"
dom-helpers "^2.4.0 || ^3.0.0"
loose-envify "^1.3.0"
+ prop-types "^15.5.4"
react@^15.4.1:
version "15.6.2"