From a162f72655505e5da28e4033b2ef1d49cfcf067f Mon Sep 17 00:00:00 2001 From: StefanT Date: Tue, 22 Dec 2015 19:29:01 -0800 Subject: [PATCH] Added ColorPropType Summary: Problem: https://github.com/facebook/react-native/issues/4708 Solution: Added a ColorPropType that validates the color used by the dev Notes: 1) I'm working a Win8.1 machine and couldn't build the react-native using the github repo. As soon as I figure that out, I'll probably figure how to run the tests and how to add some for this feature. 2) It's my first pull request. Be gentle :) Closes https://github.com/facebook/react-native/pull/4866 Reviewed By: bestander, svcscm Differential Revision: D2783672 Pulled By: nicklockwood fb-gh-sync-id: ca22aa3c0999188075681b5d20fff0631496e238 --- Libraries/Components/MapView/MapView.js | 16 +++------ .../ProgressBarAndroid.android.js | 3 +- Libraries/Components/Switch/Switch.js | 9 ++--- .../Components/SwitchIOS/SwitchIOS.ios.js | 7 ++-- .../Components/TabBarIOS/TabBarIOS.ios.js | 5 +-- .../ToolbarAndroid/ToolbarAndroid.android.js | 5 +-- .../Touchable/TouchableHighlight.js | 3 +- .../Components/View/ViewStylePropTypes.js | 15 ++++---- Libraries/Image/ImageStylePropTypes.js | 7 ++-- .../PullToRefreshViewAndroid.android.js | 7 ++-- Libraries/StyleSheet/ColorPropType.js | 34 +++++++++++++++++++ Libraries/Text/TextStylePropTypes.js | 5 +-- Libraries/react-native/react-native.js | 1 + Libraries/react-native/react-native.js.flow | 1 + 14 files changed, 78 insertions(+), 40 deletions(-) create mode 100644 Libraries/StyleSheet/ColorPropType.js diff --git a/Libraries/Components/MapView/MapView.js b/Libraries/Components/MapView/MapView.js index e10efa32b..29257d1db 100644 --- a/Libraries/Components/MapView/MapView.js +++ b/Libraries/Components/MapView/MapView.js @@ -11,6 +11,7 @@ */ 'use strict'; +const ColorPropType = require('ColorPropType'); const EdgeInsetsPropType = require('EdgeInsetsPropType'); const Image = require('Image'); const NativeMethodsMixin = require('NativeMethodsMixin'); @@ -166,10 +167,7 @@ const MapView = React.createClass({ * are supported for regular pins. For custom pin images, any tintColor * value is supported on all iOS versions. */ - tintColor: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.number - ]), + tintColor: ColorPropType, /** * Custom pin image. This must be a static image resource inside the app. @@ -213,14 +211,8 @@ const MapView = React.createClass({ * Line attributes */ lineWidth: React.PropTypes.number, - strokeColor: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.number - ]), - fillColor: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.number - ]), + strokeColor: ColorPropType, + fillColor: ColorPropType, /** * Overlay id diff --git a/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js b/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js index fb2175b99..974eaf97d 100644 --- a/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js +++ b/Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js @@ -15,6 +15,7 @@ var React = require('React'); var ReactPropTypes = require('ReactPropTypes'); var ReactNativeViewAttributes = require('ReactNativeViewAttributes'); var View = require('View'); +var ColorPropType = require('ColorPropType'); var requireNativeComponent = require('requireNativeComponent'); @@ -88,7 +89,7 @@ var ProgressBarAndroid = React.createClass({ /** * Color of the progress bar. */ - color: ReactPropTypes.string, + color: ColorPropType, /** * Used to locate this view in end-to-end tests. */ diff --git a/Libraries/Components/Switch/Switch.js b/Libraries/Components/Switch/Switch.js index ceab1f0ba..31ec91ef5 100644 --- a/Libraries/Components/Switch/Switch.js +++ b/Libraries/Components/Switch/Switch.js @@ -6,6 +6,7 @@ */ 'use strict'; +var ColorPropType = require('ColorPropType'); var NativeMethodsMixin = require('NativeMethodsMixin'); var Platform = require('Platform'); var React = require('React'); @@ -43,22 +44,22 @@ var Switch = React.createClass({ * Used to locate this view in end-to-end tests. */ testID: React.PropTypes.string, - + /** * Background color when the switch is turned off. * @platform ios */ - tintColor: React.PropTypes.string, + tintColor: ColorPropType, /** * Background color when the switch is turned on. * @platform ios */ - onTintColor: React.PropTypes.string, + onTintColor: ColorPropType, /** * Color of the foreground switch grip. * @platform ios */ - thumbTintColor: React.PropTypes.string, + thumbTintColor: ColorPropType, }, getDefaultProps: function(): DefaultProps { diff --git a/Libraries/Components/SwitchIOS/SwitchIOS.ios.js b/Libraries/Components/SwitchIOS/SwitchIOS.ios.js index 35514f66f..798529c58 100644 --- a/Libraries/Components/SwitchIOS/SwitchIOS.ios.js +++ b/Libraries/Components/SwitchIOS/SwitchIOS.ios.js @@ -13,6 +13,7 @@ */ 'use strict'; +var ColorPropType = require('ColorPropType'); var NativeMethodsMixin = require('NativeMethodsMixin'); var PropTypes = require('ReactPropTypes'); var React = require('React'); @@ -62,17 +63,17 @@ var SwitchIOS = React.createClass({ /** * Background color when the switch is turned on. */ - onTintColor: PropTypes.string, + onTintColor: ColorPropType, /** * Background color for the switch round button. */ - thumbTintColor: PropTypes.string, + thumbTintColor: ColorPropType, /** * Background color when the switch is turned off. */ - tintColor: PropTypes.string, + tintColor: ColorPropType, }, getDefaultProps: function(): DefaultProps { diff --git a/Libraries/Components/TabBarIOS/TabBarIOS.ios.js b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js index 660c8c62a..3d168509f 100644 --- a/Libraries/Components/TabBarIOS/TabBarIOS.ios.js +++ b/Libraries/Components/TabBarIOS/TabBarIOS.ios.js @@ -11,6 +11,7 @@ */ 'use strict'; +var ColorPropType = require('ColorPropType'); var React = require('React'); var StyleSheet = require('StyleSheet'); var TabBarItemIOS = require('TabBarItemIOS'); @@ -29,11 +30,11 @@ var TabBarIOS = React.createClass({ /** * Color of the currently selected tab icon */ - tintColor: React.PropTypes.string, + tintColor: ColorPropType, /** * Background color of the tab bar */ - barTintColor: React.PropTypes.string, + barTintColor: ColorPropType, /** * A Boolean value that indicates whether the tab bar is translucent */ diff --git a/Libraries/Components/ToolbarAndroid/ToolbarAndroid.android.js b/Libraries/Components/ToolbarAndroid/ToolbarAndroid.android.js index ba5541e8a..060c6a1d0 100644 --- a/Libraries/Components/ToolbarAndroid/ToolbarAndroid.android.js +++ b/Libraries/Components/ToolbarAndroid/ToolbarAndroid.android.js @@ -18,6 +18,7 @@ var ReactNativeViewAttributes = require('ReactNativeViewAttributes'); var ReactPropTypes = require('ReactPropTypes'); var UIManager = require('UIManager'); var View = require('View'); +var ColorPropType = require('ColorPropType'); var requireNativeComponent = require('requireNativeComponent'); var resolveAssetSource = require('resolveAssetSource'); @@ -116,7 +117,7 @@ var ToolbarAndroid = React.createClass({ /** * Sets the toolbar subtitle color. */ - subtitleColor: ReactPropTypes.string, + subtitleColor: ColorPropType, /** * Sets the toolbar title. */ @@ -124,7 +125,7 @@ var ToolbarAndroid = React.createClass({ /** * Sets the toolbar title color. */ - titleColor: ReactPropTypes.string, + titleColor: ColorPropType, /** * Sets the content inset for the toolbar starting edge. * diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 67c1e55a0..929c43dfc 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -13,6 +13,7 @@ // Note (avik): add @flow when Flow supports spread properties in propTypes +var ColorPropType = require('ColorPropType'); var NativeMethodsMixin = require('NativeMethodsMixin'); var React = require('React'); var ReactNativeViewAttributes = require('ReactNativeViewAttributes'); @@ -76,7 +77,7 @@ var TouchableHighlight = React.createClass({ * The color of the underlay that will show through when the touch is * active. */ - underlayColor: React.PropTypes.string, + underlayColor: ColorPropType, style: View.propTypes.style, /** * Called immediately after the underlay is shown diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index 957679705..cb193be1e 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -13,6 +13,7 @@ var LayoutPropTypes = require('LayoutPropTypes'); var ReactPropTypes = require('ReactPropTypes'); +var ColorPropType = require('ColorPropType'); var TransformPropTypes = require('TransformPropTypes'); /** @@ -22,12 +23,12 @@ var ViewStylePropTypes = { ...LayoutPropTypes, ...TransformPropTypes, backfaceVisibility: ReactPropTypes.oneOf(['visible', 'hidden']), - backgroundColor: ReactPropTypes.string, - borderColor: ReactPropTypes.string, - borderTopColor: ReactPropTypes.string, - borderRightColor: ReactPropTypes.string, - borderBottomColor: ReactPropTypes.string, - borderLeftColor: ReactPropTypes.string, + backgroundColor: ColorPropType, + borderColor: ColorPropType, + borderTopColor: ColorPropType, + borderRightColor: ColorPropType, + borderBottomColor: ColorPropType, + borderLeftColor: ColorPropType, borderRadius: ReactPropTypes.number, borderTopLeftRadius: ReactPropTypes.number, borderTopRightRadius: ReactPropTypes.number, @@ -41,7 +42,7 @@ var ViewStylePropTypes = { borderLeftWidth: ReactPropTypes.number, opacity: ReactPropTypes.number, overflow: ReactPropTypes.oneOf(['visible', 'hidden']), - shadowColor: ReactPropTypes.string, + shadowColor: ColorPropType, shadowOffset: ReactPropTypes.shape( {width: ReactPropTypes.number, height: ReactPropTypes.number} ), diff --git a/Libraries/Image/ImageStylePropTypes.js b/Libraries/Image/ImageStylePropTypes.js index 6974bdc55..ec806b1b8 100644 --- a/Libraries/Image/ImageStylePropTypes.js +++ b/Libraries/Image/ImageStylePropTypes.js @@ -14,6 +14,7 @@ var ImageResizeMode = require('ImageResizeMode'); var LayoutPropTypes = require('LayoutPropTypes'); var ReactPropTypes = require('ReactPropTypes'); +var ColorPropType = require('ColorPropType'); var TransformPropTypes = require('TransformPropTypes'); var ImageStylePropTypes = { @@ -21,15 +22,15 @@ var ImageStylePropTypes = { ...TransformPropTypes, resizeMode: ReactPropTypes.oneOf(Object.keys(ImageResizeMode)), backfaceVisibility: ReactPropTypes.oneOf(['visible', 'hidden']), - backgroundColor: ReactPropTypes.string, - borderColor: ReactPropTypes.string, + backgroundColor: ColorPropType, + borderColor: ColorPropType, borderWidth: ReactPropTypes.number, borderRadius: ReactPropTypes.number, overflow: ReactPropTypes.oneOf(['visible', 'hidden']), // iOS-Specific style to "tint" an image. // It changes the color of all the non-transparent pixels to the tintColor - tintColor: ReactPropTypes.string, + tintColor: ColorPropType, opacity: ReactPropTypes.number, }; diff --git a/Libraries/PullToRefresh/PullToRefreshViewAndroid.android.js b/Libraries/PullToRefresh/PullToRefreshViewAndroid.android.js index 68d6de95d..1c37d2ec9 100644 --- a/Libraries/PullToRefresh/PullToRefreshViewAndroid.android.js +++ b/Libraries/PullToRefresh/PullToRefreshViewAndroid.android.js @@ -10,8 +10,9 @@ */ 'use strict'; +var ColorPropType = require('ColorPropType'); var React = require('React'); -var RefreshLayoutConsts = require('NativeModules').UIManager.AndroidSwipeRefreshLayout.Constants; +var RefreshLayoutConsts = require('UIManager').AndroidSwipeRefreshLayout.Constants; var View = require('View'); var onlyChild = require('onlyChild'); @@ -41,11 +42,11 @@ var PullToRefreshViewAndroid = React.createClass({ /** * The colors (at least one) that will be used to draw the refresh indicator */ - colors: React.PropTypes.arrayOf(React.PropTypes.string), + colors: React.PropTypes.arrayOf(ColorPropType), /** * The background color of the refresh indicator */ - progressBackgroundColor: React.PropTypes.string, + progressBackgroundColor: ColorPropType, /** * Whether the view should be indicating an active refresh */ diff --git a/Libraries/StyleSheet/ColorPropType.js b/Libraries/StyleSheet/ColorPropType.js new file mode 100644 index 000000000..753b65c2d --- /dev/null +++ b/Libraries/StyleSheet/ColorPropType.js @@ -0,0 +1,34 @@ + /** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ColorPropType + */ +'use strict'; +var ReactPropTypes = require('ReactPropTypes'); +var tinycolor = require('tinycolor'); + +var colorValidator = function (props, propName) { + var selectedColor = props[propName]; + if (selectedColor === null || selectedColor === undefined || selectedColor.toString().trim() === '') { + return new Error( + `Invalid argument supplied to ${propName}.Expected a string like #123ADF or 'red'.` + ); + } + + if (tinycolor(selectedColor.toString().trim()).isValid()) { + return null; + } + + return new Error( + `Invalid argument supplied to ${propName}.Expected a string like #123ADF or 'red'.` + ); +}; + +var ColorPropType = ReactPropTypes.oneOfType([colorValidator, ReactPropTypes.number]); + +module.exports = ColorPropType; diff --git a/Libraries/Text/TextStylePropTypes.js b/Libraries/Text/TextStylePropTypes.js index cbd624936..ce1044277 100644 --- a/Libraries/Text/TextStylePropTypes.js +++ b/Libraries/Text/TextStylePropTypes.js @@ -12,11 +12,12 @@ 'use strict'; var ReactPropTypes = require('ReactPropTypes'); +var ColorPropType = require('ColorPropType'); var ViewStylePropTypes = require('ViewStylePropTypes'); // TODO: use spread instead of Object.assign/create after #6560135 is fixed var TextStylePropTypes = Object.assign(Object.create(ViewStylePropTypes), { - color: ReactPropTypes.string, + color: ColorPropType, fontFamily: ReactPropTypes.string, fontSize: ReactPropTypes.number, fontStyle: ReactPropTypes.oneOf(['normal', 'italic']), @@ -55,7 +56,7 @@ var TextStylePropTypes = Object.assign(Object.create(ViewStylePropTypes), { /** * @platform ios */ - textDecorationColor: ReactPropTypes.string, + textDecorationColor: ColorPropType, /** * @platform ios */ diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js index f2313318b..5b33bd363 100644 --- a/Libraries/react-native/react-native.js +++ b/Libraries/react-native/react-native.js @@ -89,6 +89,7 @@ var ReactNative = { get requireNativeComponent() { return require('requireNativeComponent'); }, // Prop Types + get ColorPropType() { return require('ColorPropType'); }, get EdgeInsetsPropType() { return require('EdgeInsetsPropType'); }, get PointPropType() { return require('PointPropType'); }, diff --git a/Libraries/react-native/react-native.js.flow b/Libraries/react-native/react-native.js.flow index bc7d05513..a250e57b0 100644 --- a/Libraries/react-native/react-native.js.flow +++ b/Libraries/react-native/react-native.js.flow @@ -100,6 +100,7 @@ var ReactNative = Object.assign(Object.create(require('React')), { requireNativeComponent: require('requireNativeComponent'), // Prop Types + ColorPropType: require('ColorPropType'), EdgeInsetsPropType: require('EdgeInsetsPropType'), PointPropType: require('PointPropType'),