From 6de892c92bad455960314c54fca632518f75a418 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Wed, 20 Dec 2017 14:51:44 +0000 Subject: [PATCH] [add] CheckBox component Implements the CheckBox component and adds a web-only 'color' prop to allow the color of the checkbox to be customized. --- .../1-components/CheckBox/CheckBoxScreen.js | 93 ++++++++++ .../CheckBox/examples/CustomSize.js | 20 +++ .../CheckBox/examples/PropColor.js | 20 +++ .../CheckBox/examples/PropDisabled.js | 20 +++ .../CheckBox/examples/PropOnValueChange.js | 59 +++++++ .../CheckBox/examples/PropValue.js | 20 +++ .../1-components/CheckBox/examples/styles.js | 23 +++ .../1-components/CheckBox/helpers.js | 36 ++++ .../renderApplication-test.js.snap | 30 +++- .../__snapshots__/index-test.js.snap | 30 +++- .../CheckBox/__tests__/index-test.js | 60 +++++++ src/components/CheckBox/index.js | 162 ++++++++++++++++++ src/components/Switch/index.js | 6 + src/module.js | 3 + 14 files changed, 578 insertions(+), 4 deletions(-) create mode 100644 docs/storybook/1-components/CheckBox/CheckBoxScreen.js create mode 100644 docs/storybook/1-components/CheckBox/examples/CustomSize.js create mode 100644 docs/storybook/1-components/CheckBox/examples/PropColor.js create mode 100755 docs/storybook/1-components/CheckBox/examples/PropDisabled.js create mode 100755 docs/storybook/1-components/CheckBox/examples/PropOnValueChange.js create mode 100755 docs/storybook/1-components/CheckBox/examples/PropValue.js create mode 100755 docs/storybook/1-components/CheckBox/examples/styles.js create mode 100644 docs/storybook/1-components/CheckBox/helpers.js create mode 100644 src/components/CheckBox/__tests__/index-test.js create mode 100644 src/components/CheckBox/index.js diff --git a/docs/storybook/1-components/CheckBox/CheckBoxScreen.js b/docs/storybook/1-components/CheckBox/CheckBoxScreen.js new file mode 100644 index 000000000..b1fe55ce9 --- /dev/null +++ b/docs/storybook/1-components/CheckBox/CheckBoxScreen.js @@ -0,0 +1,93 @@ +/* eslint-disable react/jsx-sort-props */ + +/** + * @flow + */ + +import CustomSize from './examples/CustomSize'; +import PropColor from './examples/PropColor'; +import PropDisabled from './examples/PropDisabled'; +import PropOnValueChange from './examples/PropOnValueChange'; +import PropValue from './examples/PropValue'; +import React from 'react'; +import UIExplorer, { + AppText, + Code, + Description, + DocItem, + Section, + storiesOf +} from '../../ui-explorer'; + +const CheckBoxScreen = () => ( + + + + This is a controlled component that requires an onValueChange callback that + updates the value prop in order for the component to reflect user actions. If the{' '} + value prop is not updated, the component will continue to render the supplied{' '} + value prop instead of the expected result of any user actions. + + + +
+ + + + }} + label="web" + name="color" + typeInfo="?color" + /> + + + }} + name="disabled" + typeInfo="?boolean = false" + /> + + + + + }} + name="onValueChange" + typeInfo="?function" + /> + + + }} + name="value" + typeInfo="?boolean = false" + /> +
+ +
+ ', + render: () => + }} + name="Custom size" + /> +
+
+); + +storiesOf('Components', module).add('CheckBox', CheckBoxScreen); diff --git a/docs/storybook/1-components/CheckBox/examples/CustomSize.js b/docs/storybook/1-components/CheckBox/examples/CustomSize.js new file mode 100644 index 000000000..8c0807ad0 --- /dev/null +++ b/docs/storybook/1-components/CheckBox/examples/CustomSize.js @@ -0,0 +1,20 @@ +/** + * @flow + */ + +import React from 'react'; +import styles from './styles'; +import { CheckBox, View } from 'react-native'; + +const CustomSizeExample = () => ( + + + + + + + + +); + +export default CustomSizeExample; diff --git a/docs/storybook/1-components/CheckBox/examples/PropColor.js b/docs/storybook/1-components/CheckBox/examples/PropColor.js new file mode 100644 index 000000000..aaf530a90 --- /dev/null +++ b/docs/storybook/1-components/CheckBox/examples/PropColor.js @@ -0,0 +1,20 @@ +/** + * @flow + */ + +import React from 'react'; +import styles from './styles'; +import { CheckBox, View } from 'react-native'; + +const CheckBoxColorExample = () => ( + + + + + + + + +); + +export default CheckBoxColorExample; diff --git a/docs/storybook/1-components/CheckBox/examples/PropDisabled.js b/docs/storybook/1-components/CheckBox/examples/PropDisabled.js new file mode 100755 index 000000000..86aa0a774 --- /dev/null +++ b/docs/storybook/1-components/CheckBox/examples/PropDisabled.js @@ -0,0 +1,20 @@ +/** + * @flow + */ + +import React from 'react'; +import styles from './styles'; +import { CheckBox, View } from 'react-native'; + +const CheckBoxDisabledExample = () => ( + + + + + + + + +); + +export default CheckBoxDisabledExample; diff --git a/docs/storybook/1-components/CheckBox/examples/PropOnValueChange.js b/docs/storybook/1-components/CheckBox/examples/PropOnValueChange.js new file mode 100755 index 000000000..aeb106205 --- /dev/null +++ b/docs/storybook/1-components/CheckBox/examples/PropOnValueChange.js @@ -0,0 +1,59 @@ +/** + * @flow + */ + +import styles from './styles'; +import React, { PureComponent } from 'react'; +import { CheckBox, Text, View } from 'react-native'; + +class CheckBoxOnValueChangeExample extends PureComponent { + state = { + eventSwitchIsOn: false, + eventSwitchRegressionIsOn: true + }; + + render() { + const { eventSwitchIsOn, eventSwitchRegressionIsOn } = this.state; + + return ( + + + + + {eventSwitchIsOn ? 'On' : 'Off'} + + + + + {eventSwitchRegressionIsOn ? 'On' : 'Off'} + + + ); + } + + _handleEventSwitch = value => { + this.setState({ eventSwitchIsOn: value }); + }; + + _handleEventSwitchRegression = value => { + this.setState({ eventSwitchRegressionIsOn: value }); + }; +} + +export default CheckBoxOnValueChangeExample; diff --git a/docs/storybook/1-components/CheckBox/examples/PropValue.js b/docs/storybook/1-components/CheckBox/examples/PropValue.js new file mode 100755 index 000000000..7f368f1c2 --- /dev/null +++ b/docs/storybook/1-components/CheckBox/examples/PropValue.js @@ -0,0 +1,20 @@ +/** + * @flow + */ + +import React from 'react'; +import styles from './styles'; +import { CheckBox, View } from 'react-native'; + +const CheckBoxValueExample = () => ( + + + + + + + + +); + +export default CheckBoxValueExample; diff --git a/docs/storybook/1-components/CheckBox/examples/styles.js b/docs/storybook/1-components/CheckBox/examples/styles.js new file mode 100755 index 000000000..e0a2f08f6 --- /dev/null +++ b/docs/storybook/1-components/CheckBox/examples/styles.js @@ -0,0 +1,23 @@ +/** + * @flow + */ + +import { StyleSheet } from 'react-native'; + +const styles = StyleSheet.create({ + row: { + flexDirection: 'row', + flexWrap: 'wrap' + }, + marginRight: { + marginRight: 10 + }, + marginBottom: { + marginBottom: 10 + }, + alignCenter: { + alignItems: 'center' + } +}); + +export default styles; diff --git a/docs/storybook/1-components/CheckBox/helpers.js b/docs/storybook/1-components/CheckBox/helpers.js new file mode 100644 index 000000000..7b87cf0e9 --- /dev/null +++ b/docs/storybook/1-components/CheckBox/helpers.js @@ -0,0 +1,36 @@ +/** + * @flow + */ + +import React from 'react'; +import { StyleSheet, View } from 'react-native'; + +const DividerHorizontal = () => ; +const DividerVertical = () => ; + +export const styles = StyleSheet.create({ + horizontalDivider: { + width: '0.6rem' + }, + verticalDivider: { + height: '1.3125rem' + }, + row: { + flexDirection: 'row', + flexWrap: 'wrap' + }, + marginRight: { + marginRight: 10 + }, + marginBottom: { + marginBottom: 10 + }, + marginVertical: { + marginVertical: 5 + }, + alignCenter: { + alignItems: 'center' + } +}); + +export { DividerHorizontal, DividerVertical }; diff --git a/src/apis/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap b/src/apis/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap index f4b615971..b8d66b988 100644 --- a/src/apis/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap +++ b/src/apis/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap @@ -39,6 +39,9 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = ` .rn-backgroundColor-wib322{background-color:transparent} .rn-backgroundColor-8ndhhv{background-color:rgba(33,150,243,1)} .rn-backgroundColor-15al3ab{background-color:rgba(223,223,223,1)} +.rn-backgroundColor-44z8sh{background-color:rgba(255,255,255,1)} +.rn-backgroundColor-5itogg{background-color:rgba(0,150,136,1)} +.rn-backgroundColor-1v82r4u{background-color:rgba(170,184,194,1)} .rn-backgroundColor-1hj8efq{background-color:rgba(213,213,213,1)} .rn-backgroundColor-1bgzomc{background-color:rgba(189,189,189,1)} .rn-color-homxoj{color:inherit} @@ -56,9 +59,13 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = ` .rn-borderBottomStyle-rull8r{border-bottom-style:solid} .rn-borderLeftStyle-mm0ijv{border-left-style:solid} .rn-borderTopWidth-13yce4e{border-top-width:0px} +.rn-borderTopWidth-1jxfwug{border-top-width:2px} .rn-borderRightWidth-fnigne{border-right-width:0px} +.rn-borderRightWidth-18p6if4{border-right-width:2px} .rn-borderBottomWidth-ndvcnb{border-bottom-width:0px} +.rn-borderBottomWidth-wgabs5{border-bottom-width:2px} .rn-borderLeftWidth-gxnn5r{border-left-width:0px} +.rn-borderLeftWidth-dwliz8{border-left-width:2px} .rn-boxSizing-deolkf{box-sizing:border-box} .rn-display-6koalj{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex} .rn-display-xoduu5{display:-webkit-inline-box;display:-moz-inline-box;display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex} @@ -98,6 +105,7 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = ` .rn-height-1pi2tsx{height:100%} .rn-height-z80fyv{height:20px} .rn-height-1r8g8re{height:36px} +.rn-height-10ptun7{height:16px} .rn-height-4v7adb{height:5px} .rn-height-1dernwh{height:70%} .rn-opacity-1272l3b{opacity:0} @@ -105,6 +113,7 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = ` .rn-width-13qz1uu{width:100%} .rn-width-19wmn03{width:20px} .rn-width-1acpoxo{width:36px} +.rn-width-1janqcz{width:16px} .rn-touchAction-19z077z{-ms-touch-action:none;touch-action:none} .rn-touchAction-1gvxusu{-ms-touch-action:manipulate;touch-action:manipulate} .rn-WebkitOverflowScrolling-150rngu{-webkit-overflow-scrolling:touch} @@ -152,11 +161,28 @@ exports[`apis/AppRegistry/renderApplication getApplication 3`] = ` .rn-borderBottomLeftRadius-pm2fo{border-bottom-left-radius:0px} .rn-fontWeight-majxgm{font-weight:500} .rn-textTransform-tsynxw{text-transform:uppercase} -.rn-alignSelf-k200y{-ms-flex-item-align:start;-webkit-align-self:flex-start;align-self:flex-start} -.rn-boxShadow-1ewcgjf{box-shadow:0px 1px 3px rgba(0,0,0,0.5)} +.rn-borderTopColor-j4x2lb{border-top-color:rgba(101,119,134,1)} +.rn-borderTopColor-gj2eto{border-top-color:rgba(0,150,136,1)} +.rn-borderTopColor-1j7vz2b{border-top-color:rgba(204,214,221,1)} +.rn-borderTopColor-2dclza{border-top-color:rgba(170,184,194,1)} .rn-borderTopColor-kqr9px{border-top-color:black} +.rn-borderRightColor-12i18q1{border-right-color:rgba(101,119,134,1)} +.rn-borderRightColor-31ud7z{border-right-color:rgba(0,150,136,1)} +.rn-borderRightColor-10fg1ub{border-right-color:rgba(204,214,221,1)} +.rn-borderRightColor-8jf312{border-right-color:rgba(170,184,194,1)} .rn-borderRightColor-q0dj5p{border-right-color:black} +.rn-borderBottomColor-mg3rfb{border-bottom-color:rgba(101,119,134,1)} +.rn-borderBottomColor-1bgnb8i{border-bottom-color:rgba(0,150,136,1)} +.rn-borderBottomColor-zxuuv6{border-bottom-color:rgba(204,214,221,1)} +.rn-borderBottomColor-1yeakrt{border-bottom-color:rgba(170,184,194,1)} .rn-borderBottomColor-1ah7hsa{border-bottom-color:black} +.rn-borderLeftColor-vnhemr{border-left-color:rgba(101,119,134,1)} +.rn-borderLeftColor-tbzcuz{border-left-color:rgba(0,150,136,1)} +.rn-borderLeftColor-1p34dw6{border-left-color:rgba(204,214,221,1)} +.rn-borderLeftColor-bluj2i{border-left-color:rgba(170,184,194,1)} .rn-borderLeftColor-137uh4u{border-left-color:black} +.rn-backgroundImage-rs94m5{background-image:url(\\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\\")} +.rn-alignSelf-k200y{-ms-flex-item-align:start;-webkit-align-self:flex-start;align-self:flex-start} +.rn-boxShadow-1ewcgjf{box-shadow:0px 1px 3px rgba(0,0,0,0.5)} .rn-resize-1dz5y72{resize:none}" `; diff --git a/src/apis/StyleSheet/__tests__/__snapshots__/index-test.js.snap b/src/apis/StyleSheet/__tests__/__snapshots__/index-test.js.snap index 6faaa0f7a..acc3f418c 100644 --- a/src/apis/StyleSheet/__tests__/__snapshots__/index-test.js.snap +++ b/src/apis/StyleSheet/__tests__/__snapshots__/index-test.js.snap @@ -34,6 +34,9 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit .rn-backgroundColor-wib322{background-color:transparent} .rn-backgroundColor-8ndhhv{background-color:rgba(33,150,243,1)} .rn-backgroundColor-15al3ab{background-color:rgba(223,223,223,1)} +.rn-backgroundColor-44z8sh{background-color:rgba(255,255,255,1)} +.rn-backgroundColor-5itogg{background-color:rgba(0,150,136,1)} +.rn-backgroundColor-1v82r4u{background-color:rgba(170,184,194,1)} .rn-backgroundColor-1hj8efq{background-color:rgba(213,213,213,1)} .rn-backgroundColor-1bgzomc{background-color:rgba(189,189,189,1)} .rn-color-homxoj{color:inherit} @@ -51,9 +54,13 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit .rn-borderBottomStyle-rull8r{border-bottom-style:solid} .rn-borderLeftStyle-mm0ijv{border-left-style:solid} .rn-borderTopWidth-13yce4e{border-top-width:0px} +.rn-borderTopWidth-1jxfwug{border-top-width:2px} .rn-borderRightWidth-fnigne{border-right-width:0px} +.rn-borderRightWidth-18p6if4{border-right-width:2px} .rn-borderBottomWidth-ndvcnb{border-bottom-width:0px} +.rn-borderBottomWidth-wgabs5{border-bottom-width:2px} .rn-borderLeftWidth-gxnn5r{border-left-width:0px} +.rn-borderLeftWidth-dwliz8{border-left-width:2px} .rn-boxSizing-deolkf{box-sizing:border-box} .rn-display-6koalj{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex} .rn-display-xoduu5{display:-webkit-inline-box;display:-moz-inline-box;display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex} @@ -93,6 +100,7 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit .rn-height-1pi2tsx{height:100%} .rn-height-z80fyv{height:20px} .rn-height-1r8g8re{height:36px} +.rn-height-10ptun7{height:16px} .rn-height-4v7adb{height:5px} .rn-height-1dernwh{height:70%} .rn-opacity-1272l3b{opacity:0} @@ -100,6 +108,7 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit .rn-width-13qz1uu{width:100%} .rn-width-19wmn03{width:20px} .rn-width-1acpoxo{width:36px} +.rn-width-1janqcz{width:16px} .rn-touchAction-19z077z{-ms-touch-action:none;touch-action:none} .rn-touchAction-1gvxusu{-ms-touch-action:manipulate;touch-action:manipulate} .rn-WebkitOverflowScrolling-150rngu{-webkit-overflow-scrolling:touch} @@ -147,12 +156,29 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit .rn-borderBottomLeftRadius-pm2fo{border-bottom-left-radius:0px} .rn-fontWeight-majxgm{font-weight:500} .rn-textTransform-tsynxw{text-transform:uppercase} -.rn-alignSelf-k200y{-ms-flex-item-align:start;-webkit-align-self:flex-start;align-self:flex-start} -.rn-boxShadow-1ewcgjf{box-shadow:0px 1px 3px rgba(0,0,0,0.5)} +.rn-borderTopColor-j4x2lb{border-top-color:rgba(101,119,134,1)} +.rn-borderTopColor-gj2eto{border-top-color:rgba(0,150,136,1)} +.rn-borderTopColor-1j7vz2b{border-top-color:rgba(204,214,221,1)} +.rn-borderTopColor-2dclza{border-top-color:rgba(170,184,194,1)} .rn-borderTopColor-kqr9px{border-top-color:black} +.rn-borderRightColor-12i18q1{border-right-color:rgba(101,119,134,1)} +.rn-borderRightColor-31ud7z{border-right-color:rgba(0,150,136,1)} +.rn-borderRightColor-10fg1ub{border-right-color:rgba(204,214,221,1)} +.rn-borderRightColor-8jf312{border-right-color:rgba(170,184,194,1)} .rn-borderRightColor-q0dj5p{border-right-color:black} +.rn-borderBottomColor-mg3rfb{border-bottom-color:rgba(101,119,134,1)} +.rn-borderBottomColor-1bgnb8i{border-bottom-color:rgba(0,150,136,1)} +.rn-borderBottomColor-zxuuv6{border-bottom-color:rgba(204,214,221,1)} +.rn-borderBottomColor-1yeakrt{border-bottom-color:rgba(170,184,194,1)} .rn-borderBottomColor-1ah7hsa{border-bottom-color:black} +.rn-borderLeftColor-vnhemr{border-left-color:rgba(101,119,134,1)} +.rn-borderLeftColor-tbzcuz{border-left-color:rgba(0,150,136,1)} +.rn-borderLeftColor-1p34dw6{border-left-color:rgba(204,214,221,1)} +.rn-borderLeftColor-bluj2i{border-left-color:rgba(170,184,194,1)} .rn-borderLeftColor-137uh4u{border-left-color:black} +.rn-backgroundImage-rs94m5{background-image:url(\\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K\\")} +.rn-alignSelf-k200y{-ms-flex-item-align:start;-webkit-align-self:flex-start;align-self:flex-start} +.rn-boxShadow-1ewcgjf{box-shadow:0px 1px 3px rgba(0,0,0,0.5)} .rn-resize-1dz5y72{resize:none}", }, ] diff --git a/src/components/CheckBox/__tests__/index-test.js b/src/components/CheckBox/__tests__/index-test.js new file mode 100644 index 000000000..7f4437627 --- /dev/null +++ b/src/components/CheckBox/__tests__/index-test.js @@ -0,0 +1,60 @@ +/* eslint-env jest */ + +import CheckBox from '../'; +import React from 'react'; +import { shallow } from 'enzyme'; + +const checkboxSelector = 'input[type="checkbox"]'; + +describe('CheckBox', () => { + describe('disabled', () => { + test('when "false" a default checkbox is rendered', () => { + const component = shallow(); + expect(component.find(checkboxSelector).prop('disabled')).toBe(false); + }); + + test('when "true" a disabled checkbox is rendered', () => { + const component = shallow(); + expect(component.find(checkboxSelector).prop('disabled')).toBe(true); + }); + }); + + describe('onChange', () => { + test('is called with the event object', () => { + const onChange = jest.fn(); + const component = shallow(); + component.find('input').simulate('change', { nativeEvent: { target: { checked: true } } }); + expect(onChange).toHaveBeenCalledWith({ + nativeEvent: { target: { checked: true }, value: true } + }); + }); + }); + + describe('onValueChange', () => { + test('when value is "false" it receives "true"', () => { + const onValueChange = jest.fn(); + const component = shallow(); + component.find('input').simulate('change', { nativeEvent: { target: { checked: true } } }); + expect(onValueChange).toHaveBeenCalledWith(true); + }); + + test('when value is "true" it receives "false"', () => { + const onValueChange = jest.fn(); + const component = shallow(); + component.find('input').simulate('change', { nativeEvent: { target: { checked: false } } }); + expect(onValueChange).toHaveBeenCalledWith(false); + }); + }); + + describe('value', () => { + test('when "false" an unchecked checkbox is rendered', () => { + const component = shallow(); + expect(component.find(checkboxSelector).prop('checked')).toBe(false); + }); + + test('when "true" a checked checkbox is rendered', () => { + const component = shallow(); + expect(component.find(checkboxSelector).prop('checked')).toBe(true); + }); + }); +}); diff --git a/src/components/CheckBox/index.js b/src/components/CheckBox/index.js new file mode 100644 index 000000000..81aed8263 --- /dev/null +++ b/src/components/CheckBox/index.js @@ -0,0 +1,162 @@ +/** + * Copyright (c) 2017-present, Nicolas Gallagher. + * Copyright (c) 2017-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. + * + * @providesModule CheckBox + * @flow + */ + +import applyNativeMethods from '../../modules/applyNativeMethods'; +import ColorPropType from '../../propTypes/ColorPropType'; +import createElement from '../../modules/createElement'; +import StyleSheet from '../../apis/StyleSheet'; +import UIManager from '../../apis/UIManager'; +import View from '../View'; +import ViewPropTypes, { type ViewProps } from '../View/ViewPropTypes'; +import React, { Component } from 'react'; +import { bool, func } from 'prop-types'; + +type Props = ViewProps & { + color?: ColorPropType, + disabled?: boolean, + onChange?: Function, + onValueChange?: Function, + value?: boolean +}; + +class CheckBox extends Component { + _checkboxElement: HTMLInputElement; + + static displayName = 'CheckBox'; + + static propTypes = { + ...ViewPropTypes, + color: ColorPropType, + disabled: bool, + onChange: func, + onValueChange: func, + value: bool + }; + + static defaultProps = { + disabled: false, + value: false + }; + + blur() { + UIManager.blur(this._checkboxElement); + } + + focus() { + UIManager.focus(this._checkboxElement); + } + + render() { + const { + color, + disabled, + /* eslint-disable */ + onChange, + onValueChange, + /* eslint-enable */ + style, + value, + ...other + } = this.props; + + const fakeControl = ( + + ); + + const nativeControl = createElement('input', { + checked: value, + disabled: disabled, + onChange: this._handleChange, + ref: this._setCheckboxRef, + style: [styles.nativeControl, styles.cursorInherit], + type: 'checkbox' + }); + + return ( + + {fakeControl} + {nativeControl} + + ); + } + + _handleChange = (event: Object) => { + const { onChange, onValueChange } = this.props; + const value = event.nativeEvent.target.checked; + event.nativeEvent.value = value; + onChange && onChange(event); + onValueChange && onValueChange(value); + }; + + _setCheckboxRef = element => { + this._checkboxElement = element; + }; +} + +const styles = StyleSheet.create({ + root: { + cursor: 'pointer', + height: 16, + userSelect: 'none', + width: 16 + }, + cursorDefault: { + cursor: 'default' + }, + cursorInherit: { + cursor: 'inherit' + }, + fakeControl: { + alignItems: 'center', + backgroundColor: '#fff', + borderColor: '#657786', + borderRadius: 2, + borderStyle: 'solid', + borderWidth: 2, + height: '100%', + justifyContent: 'center', + width: '100%' + }, + fakeControlChecked: { + backgroundColor: '#009688', + backgroundImage: + 'url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgdmVyc2lvbj0iMS4xIgogICB2aWV3Qm94PSIwIDAgMSAxIgogICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWluWU1pbiBtZWV0Ij4KICA8cGF0aAogICAgIGQ9Ik0gMC4wNDAzODA1OSwwLjYyNjc3NjcgMC4xNDY0NDY2MSwwLjUyMDcxMDY4IDAuNDI5Mjg5MzIsMC44MDM1NTMzOSAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IE0gMC4yMTcxNTcyOSwwLjgwMzU1MzM5IDAuODUzNTUzMzksMC4xNjcxNTcyOSAwLjk1OTYxOTQxLDAuMjczMjIzMyAwLjMyMzIyMzMsMC45MDk2MTk0MSB6IgogICAgIGlkPSJyZWN0Mzc4MCIKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lIiAvPgo8L3N2Zz4K")', + backgroundRepeat: 'no-repeat', + borderColor: '#009688' + }, + fakeControlDisabled: { + borderColor: '#CCD6DD' + }, + fakeControlCheckedAndDisabled: { + backgroundColor: '#AAB8C2', + borderColor: '#AAB8C2' + }, + nativeControl: { + ...StyleSheet.absoluteFillObject, + height: '100%', + margin: 0, + opacity: 0, + padding: 0, + width: '100%' + } +}); + +export default applyNativeMethods(CheckBox); diff --git a/src/components/Switch/index.js b/src/components/Switch/index.js index 999138c61..5f304b555 100644 --- a/src/components/Switch/index.js +++ b/src/components/Switch/index.js @@ -1,4 +1,10 @@ /** + * Copyright (c) 2016-present, Nicolas Gallagher. + * 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. + * * @providesModule Switch * @flow */ diff --git a/src/module.js b/src/module.js index 5b927a66d..f0ac07fac 100644 --- a/src/module.js +++ b/src/module.js @@ -30,6 +30,7 @@ import Vibration from './apis/Vibration'; import ActivityIndicator from './components/ActivityIndicator'; import ART from './components/ART'; import Button from './components/Button'; +import CheckBox from './components/CheckBox'; import FlatList from './components/FlatList'; import Image from './components/Image'; import ImageBackground from './components/Image/ImageBackground'; @@ -95,6 +96,7 @@ export { ActivityIndicator, ART, Button, + CheckBox, FlatList, Image, ImageBackground, @@ -163,6 +165,7 @@ const ReactNative = { ActivityIndicator, ART, Button, + CheckBox, FlatList, Image, ImageBackground,