From 25204eeff028dbf4f0af6ccc61f14a5c4c5b8669 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Wed, 22 Mar 2017 23:15:42 -0700 Subject: [PATCH] [fix] convert color values to CSS color Convert all hex and numeric colors to rgba. Assume non-hex strings are valid CSS colors. --- .../createReactDOMStyle-test.js.snap | 2 +- .../__tests__/resolveBoxShadow-test.js | 10 +++----- src/apis/StyleSheet/expandStyle.js | 21 ++++++++++++++-- src/apis/StyleSheet/processColor.js | 25 +++++++++++++++++++ src/apis/StyleSheet/resolveBoxShadow.js | 20 ++++++--------- src/apis/StyleSheet/resolveTextShadow.js | 7 ++++-- .../__snapshots__/index-test.js.snap | 16 ++++++------ 7 files changed, 69 insertions(+), 32 deletions(-) create mode 100644 src/apis/StyleSheet/processColor.js diff --git a/src/apis/StyleSheet/__tests__/__snapshots__/createReactDOMStyle-test.js.snap b/src/apis/StyleSheet/__tests__/__snapshots__/createReactDOMStyle-test.js.snap index 7425e7c4..5e222a84 100644 --- a/src/apis/StyleSheet/__tests__/__snapshots__/createReactDOMStyle-test.js.snap +++ b/src/apis/StyleSheet/__tests__/__snapshots__/createReactDOMStyle-test.js.snap @@ -8,7 +8,7 @@ Object { "borderTopWidth": "1px", "borderWidthLeft": "2px", "borderWidthRight": "3px", - "boxShadow": "1px 1px 1px 1px #000, 1px 2px 0px rgba(255,0,0,1)", + "boxShadow": "1px 1px 1px 1px #000, 1px 2px 0px red", "display": "flex", "flexShrink": 0, "marginBottom": "0px", diff --git a/src/apis/StyleSheet/__tests__/resolveBoxShadow-test.js b/src/apis/StyleSheet/__tests__/resolveBoxShadow-test.js index 51de4820..460fdc47 100644 --- a/src/apis/StyleSheet/__tests__/resolveBoxShadow-test.js +++ b/src/apis/StyleSheet/__tests__/resolveBoxShadow-test.js @@ -9,7 +9,7 @@ describe('apis/StyleSheet/resolveBoxShadow', () => { resolveBoxShadow(resolvedStyle, style); expect(resolvedStyle).toEqual({ - boxShadow: '0px 0px 0px rgba(255,0,0,1)' + boxShadow: '0px 0px 0px red' }); }); @@ -28,9 +28,7 @@ describe('apis/StyleSheet/resolveBoxShadow', () => { const style = { shadowOffset: { width: 1, height: 2 } }; resolveBoxShadow(resolvedStyle, style); - expect(resolvedStyle).toEqual({ - boxShadow: '1px 2px 0px rgba(0,0,0,0)' - }); + expect(resolvedStyle).toEqual({}); }); test('shadowRadius only', () => { @@ -38,9 +36,7 @@ describe('apis/StyleSheet/resolveBoxShadow', () => { const style = { shadowRadius: 5 }; resolveBoxShadow(resolvedStyle, style); - expect(resolvedStyle).toEqual({ - boxShadow: '0px 0px 5px rgba(0,0,0,0)' - }); + expect(resolvedStyle).toEqual({}); }); test('shadowOffset, shadowRadius, shadowColor', () => { diff --git a/src/apis/StyleSheet/expandStyle.js b/src/apis/StyleSheet/expandStyle.js index 234b6ee9..05f1bab5 100644 --- a/src/apis/StyleSheet/expandStyle.js +++ b/src/apis/StyleSheet/expandStyle.js @@ -10,6 +10,7 @@ */ import normalizeValue from './normalizeValue'; +import processColor from './processColor'; import resolveBoxShadow from './resolveBoxShadow'; import resolveTextShadow from './resolveTextShadow'; import resolveTransform from './resolveTransform'; @@ -36,6 +37,16 @@ const styleShortFormProperties = { writingDirection: ['direction'] }; +const colorProps = { + backgroundColor: true, + borderColor: true, + borderTopColor: true, + borderRightColor: true, + borderBottomColor: true, + borderLeftColor: true, + color: true +}; + const alphaSortProps = propsArray => propsArray.sort((a, b) => { if (a < b) { return -1; @@ -106,17 +117,23 @@ const createReducer = (style, styleProps) => { break; } default: { + // normalize color values + let finalValue = value; + if (colorProps[prop]) { + finalValue = processColor(value); + } + const longFormProperties = styleShortFormProperties[prop]; if (longFormProperties) { longFormProperties.forEach((longForm, i) => { // the value of any longform property in the original styles takes // precedence over the shortform's value if (styleProps.indexOf(longForm) === -1) { - resolvedStyle[longForm] = value; + resolvedStyle[longForm] = finalValue; } }); } else { - resolvedStyle[prop] = value; + resolvedStyle[prop] = finalValue; } } } diff --git a/src/apis/StyleSheet/processColor.js b/src/apis/StyleSheet/processColor.js new file mode 100644 index 00000000..bcf7f572 --- /dev/null +++ b/src/apis/StyleSheet/processColor.js @@ -0,0 +1,25 @@ +import normalizeColor from 'normalize-css-color'; + +const processColor = (color, opacity = 1) => { + if ( + color === undefined || + color === null || + (opacity === 1 && typeof color === 'string' && color.charAt(0) !== '#') + ) { + return color; + } + + // convert number and hex + const int32Color = normalizeColor(color); + if (int32Color === null) { + return undefined; + } + + // convert 0xrrggbbaa into rgba + const rgba = normalizeColor.rgba(int32Color); + rgba.a = rgba.a.toFixed(1); + const { r, g, b, a } = rgba; + return `rgba(${r},${g},${b},${a * opacity})`; +}; + +module.exports = processColor; diff --git a/src/apis/StyleSheet/resolveBoxShadow.js b/src/apis/StyleSheet/resolveBoxShadow.js index b4ac5295..c7001a34 100644 --- a/src/apis/StyleSheet/resolveBoxShadow.js +++ b/src/apis/StyleSheet/resolveBoxShadow.js @@ -1,26 +1,22 @@ -import normalizeColor from 'normalize-css-color'; import normalizeValue from './normalizeValue'; +import processColor from './processColor'; const defaultOffset = { height: 0, width: 0 }; -const applyOpacity = (color, opacity = 1) => { - const nullableColor = normalizeColor(color); - const colorInt = nullableColor === null ? 0x00000000 : nullableColor; - const { r, g, b, a } = normalizeColor.rgba(colorInt); - const alpha = a.toFixed(2); - return `rgba(${r},${g},${b},${alpha * opacity})`; -}; - // TODO: add inset and spread support const resolveBoxShadow = (resolvedStyle, style) => { const { height, width } = style.shadowOffset || defaultOffset; const offsetX = normalizeValue(null, width); const offsetY = normalizeValue(null, height); const blurRadius = normalizeValue(null, style.shadowRadius || 0); - const color = applyOpacity(style.shadowColor, style.shadowOpacity); + const color = processColor(style.shadowColor, style.shadowOpacity); - const boxShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`; - resolvedStyle.boxShadow = style.boxShadow ? `${style.boxShadow}, ${boxShadow}` : boxShadow; + if (color) { + const boxShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`; + resolvedStyle.boxShadow = style.boxShadow ? `${style.boxShadow}, ${boxShadow}` : boxShadow; + } else if (style.boxShadow) { + resolvedStyle.boxShadow = style.boxShadow; + } }; module.exports = resolveBoxShadow; diff --git a/src/apis/StyleSheet/resolveTextShadow.js b/src/apis/StyleSheet/resolveTextShadow.js index f6e19d19..99382f8f 100644 --- a/src/apis/StyleSheet/resolveTextShadow.js +++ b/src/apis/StyleSheet/resolveTextShadow.js @@ -1,4 +1,5 @@ import normalizeValue from './normalizeValue'; +import processColor from './processColor'; const defaultOffset = { height: 0, width: 0 }; @@ -7,9 +8,11 @@ const resolveTextShadow = (resolvedStyle, style) => { const offsetX = normalizeValue(null, width); const offsetY = normalizeValue(null, height); const blurRadius = normalizeValue(null, style.textShadowRadius || 0); - const color = style.textShadowColor || 'currentcolor'; + const color = processColor(style.textShadowColor); - resolvedStyle.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`; + if (color) { + resolvedStyle.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`; + } }; module.exports = resolveTextShadow; diff --git a/src/components/Switch/__tests__/__snapshots__/index-test.js.snap b/src/components/Switch/__tests__/__snapshots__/index-test.js.snap index af84e18d..27ca6369 100644 --- a/src/components/Switch/__tests__/__snapshots__/index-test.js.snap +++ b/src/components/Switch/__tests__/__snapshots__/index-test.js.snap @@ -14,7 +14,7 @@ exports[`components/Switch disabled when "false" a default checkbox is rendered className="rn-alignItems-1oszu61 rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-bottom-1p0dtai rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-height-1dernwh rn-left-1d2f490 rn-listStyle-1ebb2ja rn-marginTop-1t01tom rn-marginRight-lchren rn-marginBottom-1qahzrx rn-marginLeft-1jj8364 rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-u8s1d rn-right-zchlnj rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-top-ipm5af rn-transitionDuration-13tjlyg rn-width-e7q0ms" style={ Object { - "backgroundColor": "#939393", + "backgroundColor": "rgba(147,147,147,1)", "borderBottomLeftRadius": "10px", "borderBottomRightRadius": "10px", "borderTopLeftRadius": "10px", @@ -26,7 +26,7 @@ exports[`components/Switch disabled when "false" a default checkbox is rendered className="rn-alignItems-1oszu61 rn-alignSelf-k200y rn-borderTopLeftRadius-jt3ufn rn-borderTopRightRadius-1e868j9 rn-borderBottomRightRadius-ujv9e3 rn-borderBottomLeftRadius-1hakmuk rn-borderTopStyle-1efd50x rn-borderRightStyle-14skgim rn-borderBottomStyle-rull8r rn-borderLeftStyle-mm0ijv rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-boxShadow-1ewcgjf rn-boxSizing-deolkf rn-color-homxoj rn-display-6koalj rn-flexShrink-1qe8dj5 rn-flexBasis-1mlwlqe rn-flexDirection-eqz5dr rn-font-1lw9tu2 rn-left-1fe0xdi rn-listStyle-1ebb2ja rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw rn-minHeight-ifefl9 rn-minWidth-bcqeeo rn-paddingTop-wk8lta rn-paddingRight-9aemit rn-paddingBottom-1mdbw0j rn-paddingLeft-gy4na3 rn-position-bnwqim rn-textAlign-1ttztb7 rn-textDecoration-bauka4 rn-transform-emqnss rn-transitionDuration-13tjlyg" style={ Object { - "backgroundColor": "#FAFAFA", + "backgroundColor": "rgba(250,250,250,1)", "height": "20px", "width": "20px", } @@ -55,7 +55,7 @@ exports[`components/Switch disabled when "true" a disabled checkbox is rendered } >