diff --git a/packages/react-native-web/src/exports/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap b/packages/react-native-web/src/exports/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap index 938453b9..13e3773a 100644 --- a/packages/react-native-web/src/exports/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap +++ b/packages/react-native-web/src/exports/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap @@ -37,6 +37,8 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit } @keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}} @keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}} +.rn-pointerEvents-12vffkv > *{pointer-events:auto} +.rn-pointerEvents-12vffkv{pointer-events:none !important} .rn-alignItems-1oszu61{-ms-flex-align:stretch;-webkit-align-items:stretch;-webkit-box-align:stretch;align-items:stretch} .rn-borderTopStyle-1efd50x{border-top-style:solid} .rn-borderRightStyle-14skgim{border-right-style:solid} @@ -70,7 +72,5 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit .rn-left-1d2f490{left:0px} .rn-position-u8s1d{position:absolute} .rn-right-zchlnj{right:0px} -.rn-top-ipm5af{top:0px} -.rn-pointerEvents-12vffkv > *{pointer-events:auto} -.rn-pointerEvents-12vffkv{pointer-events:none !important}" +.rn-top-ipm5af{top:0px}" `; diff --git a/packages/react-native-web/src/exports/StyleSheet/ReactNativeStyleResolver.js b/packages/react-native-web/src/exports/StyleSheet/ReactNativeStyleResolver.js index 4990c151..c891a1a7 100644 --- a/packages/react-native-web/src/exports/StyleSheet/ReactNativeStyleResolver.js +++ b/packages/react-native-web/src/exports/StyleSheet/ReactNativeStyleResolver.js @@ -158,13 +158,13 @@ export default class ReactNativeStyleResolver { props.classList.push(className); } else { // Certain properties and values are not transformed by 'createReactDOMStyle' as they - // require more complex transforms into multiple CSS rules. Here we make sure the styles - // can be represented by a className and don't end up as invalid inline-styles. - if (styleProp === 'pointerEvents') { + // require more complex transforms into multiple CSS rules. Here we assume that StyleManager + // can bind these styles to a className, and prevent them becoming invalid inline-styles. + if (styleProp === 'pointerEvents' || styleProp === 'placeholderTextColor') { const className = this.styleSheetManager.injectDeclaration(styleProp, value); - props.classList.push(className); - // } else if (styleProp ==='placeholderTextColor') { - // } else if (styleProp ==='animationName') { + if (className) { + props.classList.push(className); + } } else { if (!props.style) { props.style = {}; diff --git a/packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/createAtomicRules.js.snap b/packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/createAtomicRules.js.snap new file mode 100644 index 00000000..06bca7c9 --- /dev/null +++ b/packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/createAtomicRules.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`StyleSheet/createAtomicRules transforms custom placeholderTextColor declaration 1`] = ` +Array [ + "@media all { +.test::-webkit-input-placeholder{color:gray;opacity:1} +.test::-moz-placeholder{color:gray;opacity:1} +.test:-ms-input-placeholder{color:gray;opacity:1} +.test::placeholder{color:gray;opacity:1} +}", +] +`; + +exports[`StyleSheet/createAtomicRules transforms custom pointerEvents declaration 1`] = ` +Array [ + ".test > *{pointer-events:none}", + ".test{pointer-events:auto !important}", +] +`; + +exports[`StyleSheet/createAtomicRules transforms standard declarations to a single rule 1`] = ` +Array [ + ".test{margin:0px}", +] +`; diff --git a/packages/react-native-web/src/exports/StyleSheet/__tests__/createAtomicRules.js b/packages/react-native-web/src/exports/StyleSheet/__tests__/createAtomicRules.js new file mode 100644 index 00000000..9f381c7e --- /dev/null +++ b/packages/react-native-web/src/exports/StyleSheet/__tests__/createAtomicRules.js @@ -0,0 +1,17 @@ +/* eslint-env jasmine, jest */ + +import createAtomicRules from '../createAtomicRules'; + +describe('StyleSheet/createAtomicRules', () => { + test('transforms standard declarations to a single rule', () => { + expect(createAtomicRules('.test', 'margin', 0)).toMatchSnapshot(); + }); + + test('transforms custom pointerEvents declaration', () => { + expect(createAtomicRules('.test', 'pointerEvents', 'box-only')).toMatchSnapshot(); + }); + + test('transforms custom placeholderTextColor declaration', () => { + expect(createAtomicRules('.test', 'placeholderTextColor', 'gray')).toMatchSnapshot(); + }); +}); diff --git a/packages/react-native-web/src/exports/StyleSheet/createAtomicRules.js b/packages/react-native-web/src/exports/StyleSheet/createAtomicRules.js index 21bd3f49..781d73ce 100644 --- a/packages/react-native-web/src/exports/StyleSheet/createAtomicRules.js +++ b/packages/react-native-web/src/exports/StyleSheet/createAtomicRules.js @@ -2,29 +2,47 @@ import createRuleBlock from './createRuleBlock'; const createAtomicRules = (selector, prop, value) => { const rules = []; - let val = value; - // pointerEvents is a special case that requires custom values and additional rules - // See #513 - if (prop === 'pointerEvents') { - if (value === 'auto' || value === 'box-only') { - val = 'auto !important'; - if (value === 'box-only') { - const block = createRuleBlock({ [prop]: 'none' }); - rules.push(`${selector} > *{${block}}`); - } - } else if (value === 'none' || value === 'box-none') { - val = 'none !important'; - if (value === 'box-none') { - const block = createRuleBlock({ [prop]: 'auto' }); - rules.push(`${selector} > *{${block}}`); + switch (prop) { + // pointerEvents is a special case that requires custom values and additional rules + // See #513 + case 'pointerEvents': { + let val = value; + if (value === 'auto' || value === 'box-only') { + val = 'auto !important'; + if (value === 'box-only') { + const block = createRuleBlock({ [prop]: 'none' }); + rules.push(`${selector} > *{${block}}`); + } + } else if (value === 'none' || value === 'box-none') { + val = 'none !important'; + if (value === 'box-none') { + const block = createRuleBlock({ [prop]: 'auto' }); + rules.push(`${selector} > *{${block}}`); + } } + const block = createRuleBlock({ [prop]: val }); + rules.push(`${selector}{${block}}`); + break; + } + + case 'placeholderTextColor': { + const block = createRuleBlock({ color: value, opacity: 1 }); + rules.push(`@media all { +${selector}::-webkit-input-placeholder{${block}} +${selector}::-moz-placeholder{${block}} +${selector}:-ms-input-placeholder{${block}} +${selector}::placeholder{${block}} +}`); + break; + } + + default: { + const block = createRuleBlock({ [prop]: value }); + rules.push(`${selector}{${block}}`); } } - const block = createRuleBlock({ [prop]: val }); - rules.push(`${selector}{${block}}`); - return rules; }; diff --git a/packages/react-native-web/src/exports/TextInput/index.js b/packages/react-native-web/src/exports/TextInput/index.js index 1ef27726..d22c5e0f 100644 --- a/packages/react-native-web/src/exports/TextInput/index.js +++ b/packages/react-native-web/src/exports/TextInput/index.js @@ -104,6 +104,7 @@ class TextInput extends Component<*> { onSelectionChange: func, onSubmitEditing: func, placeholder: string, + placeholderTextColor: ColorPropType, secureTextEntry: bool, selectTextOnFocus: bool, selection: shape({ @@ -126,7 +127,6 @@ class TextInput extends Component<*> { onContentSizeChange: func, onEndEditing: func, onScroll: func, - placeholderTextColor: ColorPropType, returnKeyLabel: string, returnKeyType: string, selectionColor: ColorPropType, @@ -198,7 +198,6 @@ class TextInput extends Component<*> { onContentSizeChange, onEndEditing, onScroll, - placeholderTextColor, returnKeyLabel, returnKeyType, selectionColor, diff --git a/packages/react-native-web/src/modules/createDOMProps/index.js b/packages/react-native-web/src/modules/createDOMProps/index.js index a18f0239..22b83ae9 100644 --- a/packages/react-native-web/src/modules/createDOMProps/index.js +++ b/packages/react-native-web/src/modules/createDOMProps/index.js @@ -68,6 +68,7 @@ const createDOMProps = (component, props, styleResolver) => { accessibilityLabel, accessibilityLiveRegion, importantForAccessibility, + placeholderTextColor, pointerEvents, style: providedStyle, testID, @@ -89,8 +90,9 @@ const createDOMProps = (component, props, styleResolver) => { role === 'heading' && resetStyles.heading, component === 'ul' && resetStyles.list, role === 'button' && !isDisabled && resetStyles.ariaButton, + pointerEvents && pointerEventsStyles[pointerEvents], providedStyle, - pointerEvents && pointerEventsStyles[pointerEvents] + placeholderTextColor && { placeholderTextColor } ]; const { className, style } = styleResolver(reactNativeStyle); diff --git a/website/storybook/1-components/TextInput/TextInputScreen.js b/website/storybook/1-components/TextInput/TextInputScreen.js index f8d0d6eb..9910b3cf 100644 --- a/website/storybook/1-components/TextInput/TextInputScreen.js +++ b/website/storybook/1-components/TextInput/TextInputScreen.js @@ -14,6 +14,7 @@ import PropMultiline from './examples/PropMultiline'; import PropNumberOfLines from './examples/PropNumberOfLines'; import PropOnSelectionChange from './examples/PropOnSelectionChange'; import PropPlaceholder from './examples/PropPlaceholder'; +import PropPlaceholderTextColor from './examples/PropPlaceholderTextColor'; import PropSecureTextEntry from './examples/PropSecureTextEntry'; import PropSelectTextOnFocus from './examples/PropSelectTextOnFocus'; import TextInputEvents from './examples/TextInputEvents'; @@ -262,6 +263,15 @@ nativeEvent: { key: keyValue } }`}{' '} }} /> + + }} + /> + ( + + + + +); + +export default TextInputPlaceholderTextColorExample;