From 4b44c8c68dfbc5604f4050cf4375c9366cd93014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9ril?= Date: Mon, 21 Jan 2019 18:18:06 +0000 Subject: [PATCH] feat(input): allow refs for input components (#235) --- .eslintrc | 2 +- .flowconfig | 2 +- package.json | 12 +- src/Atoms/Inputs/Checkbox.js | 21 +- src/Atoms/Inputs/Input.js | 14 +- src/Atoms/Inputs/Input.spec.js | 26 +- src/Atoms/Inputs/Password.js | 11 +- src/Atoms/Inputs/Radio.js | 11 +- src/Atoms/Inputs/TextArea.js | 10 +- .../Inputs/__snapshots__/Input.spec.js.snap | 2757 +++++----- .../__snapshots__/Password.spec.js.snap | 4716 +++++++++-------- .../__snapshots__/TextArea.spec.js.snap | 1942 +++---- src/Molecules/Dropdowns/SingleDropdown.js | 9 +- .../__snapshots__/SingleDropdown.spec.js.snap | 3551 +++++++------ .../__snapshots__/SearchBar.spec.js.snap | 1369 ++--- .../SearchBarUnderlined.spec.js.snap | 1369 ++--- 16 files changed, 8302 insertions(+), 7520 deletions(-) diff --git a/.eslintrc b/.eslintrc index df36a972..3cab041a 100644 --- a/.eslintrc +++ b/.eslintrc @@ -25,7 +25,7 @@ settings: onlyFilesWithFlowAnnotation: true react: version: 16.7.0 - flowVersion: 0.90.0 + flowVersion: 0.91.0 rules: react/sort-comp: diff --git a/.flowconfig b/.flowconfig index 4aa57f90..1df6272e 100644 --- a/.flowconfig +++ b/.flowconfig @@ -27,4 +27,4 @@ sketchy-null sketchy-number [version] -^0.90.0 \ No newline at end of file +^0.91.0 \ No newline at end of file diff --git a/package.json b/package.json index 7fbedd8f..6c0697cb 100644 --- a/package.json +++ b/package.json @@ -76,8 +76,8 @@ "enzyme": "3.8.0", "enzyme-adapter-react-16": "1.7.1", "enzyme-to-json": "3.3.5", - "eslint": "5.12.0", - "eslint-config-prettier": "3.4.0", + "eslint": "5.12.1", + "eslint-config-prettier": "3.6.0", "eslint-config-standard": "12.0.0", "eslint-loader": "2.1.1", "eslint-plugin-babel": "5.3.0", @@ -86,23 +86,23 @@ "eslint-plugin-node": "8.0.1", "eslint-plugin-prettier": "3.0.1", "eslint-plugin-promise": "4.0.1", - "eslint-plugin-react": "7.12.3", + "eslint-plugin-react": "7.12.4", "eslint-plugin-standard": "4.0.0", - "flow-bin": "0.90.0", + "flow-bin": "0.91.0", "flow-copy-source": "2.0.2", "flow-coverage-report": "0.6.1", "husky": "1.3.1", "jest": "23.6.0", "jest-styled-components": "6.3.1", "lint-staged": "8.1.0", - "prettier": "1.15.3", + "prettier": "1.16.0", "prop-types": "15.6.2", "react": "16.7.0", "react-dom": "16.7.0", "rimraf": "2.6.3", "standard": "12.0.1", "styled-components": "4.1.3", - "stylelint": "9.10.0", + "stylelint": "9.10.1", "stylelint-config-recommended": "2.1.0", "stylelint-config-standard": "18.2.0", "stylelint-config-styled-components": "0.1.1", diff --git a/src/Atoms/Inputs/Checkbox.js b/src/Atoms/Inputs/Checkbox.js index d87beace..07eff448 100644 --- a/src/Atoms/Inputs/Checkbox.js +++ b/src/Atoms/Inputs/Checkbox.js @@ -89,11 +89,26 @@ export const CheckboxLabel = styled.label` } ` -const Checkbox = ({ className, input, ...rest }: PropsType) => ( +const Checkbox = ({ + className, + forwardRef: ref, + input, + ...rest +}: PropsType) => ( - + {rest.label} ) -export default Checkbox +type RefPropsType = { ...PropsType } + +const CheckboxWithRef = (props, ref) => + +export default React.forwardRef(CheckboxWithRef) diff --git a/src/Atoms/Inputs/Input.js b/src/Atoms/Inputs/Input.js index db35020b..c2280dc4 100644 --- a/src/Atoms/Inputs/Input.js +++ b/src/Atoms/Inputs/Input.js @@ -27,6 +27,7 @@ export type PropsType = { +disabled?: boolean, +e2e?: string, +error?: string, + +forwardRef?: React.ElementRef<*>, +input?: {}, +label?: string, +name?: string, @@ -109,8 +110,9 @@ export const InputLabel = styled.label` props.disabled === true ? 'cursor: not-allowed' : ''}; ` -const Input = ({ +export const Input = ({ className = '', + forwardRef: ref, input, type, renderSuffix, @@ -141,7 +143,7 @@ const Input = ({ hasLabel && hasError ? ' - ' : '' }${rest.error || ''}`} - + {renderSuffix && renderSuffix(rest.disabled)} ) @@ -149,7 +151,7 @@ const Input = ({ return ( - + {renderSuffix && renderSuffix(rest.disabled)} ) @@ -184,4 +186,8 @@ Input.defaultProps = { width: '100%', } -export default Input +type RefPropsType = { ...PropsType } + +const InputWithRef = (props, ref) => + +export default React.forwardRef(InputWithRef) diff --git a/src/Atoms/Inputs/Input.spec.js b/src/Atoms/Inputs/Input.spec.js index d32e494a..f7506eca 100644 --- a/src/Atoms/Inputs/Input.spec.js +++ b/src/Atoms/Inputs/Input.spec.js @@ -1,6 +1,7 @@ +import { mount } from 'enzyme' import React from 'react' -import { mountWithTheme } from '../../Utils/testHelper' +import { mountWithTheme } from '../../Utils/testHelper' import Input from './Input' describe('Input', () => { @@ -63,4 +64,27 @@ describe('Input', () => { expect(tree.find(Input)).toMatchSnapshot() }) + + it('should accept ref', () => { + class MyComponent extends React.Component { + constructor(props) { + super(props) + this.myRef = React.createRef() + this.focus = this.focus.bind(this) + } + + focus() { + this.myRef.current.focus() + } + + render() { + return + } + } + const wrapper = mount() + + expect(document.activeElement.type).toBeUndefined() + wrapper.instance().focus() + expect(document.activeElement.id).toEqual('TestRef') + }) }) diff --git a/src/Atoms/Inputs/Password.js b/src/Atoms/Inputs/Password.js index b497e109..6ddf39c1 100644 --- a/src/Atoms/Inputs/Password.js +++ b/src/Atoms/Inputs/Password.js @@ -3,7 +3,7 @@ import * as React from 'react' import styled from 'styled-components' import { fontSize, theme } from '../../Tools/interpolation' -import Input, { type PropsType, type HtmlInputType } from './Input' +import { Input, type PropsType, type HtmlInputType } from './Input' import { FarEye, FarEyeSlash } from '../Icons' const Switch = styled.span` @@ -59,11 +59,12 @@ class Password extends React.Component { render() { const { type } = this.state - const { renderSuffix } = this.props + const { forwardRef: ref, renderSuffix } = this.props return ( @@ -71,4 +72,8 @@ class Password extends React.Component { } } -export default Password +type RefPropsType = { ...PropsType } + +const PasswordWithRef = (props, ref) => + +export default React.forwardRef(PasswordWithRef) diff --git a/src/Atoms/Inputs/Radio.js b/src/Atoms/Inputs/Radio.js index 8a55bfe4..9453a796 100644 --- a/src/Atoms/Inputs/Radio.js +++ b/src/Atoms/Inputs/Radio.js @@ -34,6 +34,7 @@ const Input = styled.input.attrs(injectE2E)` visibility: visible; white-space: nowrap; width: 1px; + user-select: none; &:focus ~ label ${RadioButton} { box-shadow: 0 0 0 2px ${theme('inputActiveColor')}; @@ -69,9 +70,9 @@ export const RadioLabel = styled.label` font-size: ${fontSize('md')}; ` -const Radio = ({ className, input, ...rest }: PropsType) => ( +const Radio = ({ className, forwardRef: ref, input, ...rest }: PropsType) => ( - + {rest.label} @@ -79,4 +80,8 @@ const Radio = ({ className, input, ...rest }: PropsType) => ( ) -export default Radio +type RefPropsType = { ...PropsType } + +const RadioWithRef = (props, ref) => + +export default React.forwardRef(RadioWithRef) diff --git a/src/Atoms/Inputs/TextArea.js b/src/Atoms/Inputs/TextArea.js index 53df83cc..72efe9d6 100644 --- a/src/Atoms/Inputs/TextArea.js +++ b/src/Atoms/Inputs/TextArea.js @@ -62,7 +62,7 @@ const Wrapper = styled.textarea.attrs(injectE2E)` } ` -const TextArea = ({ input, ...rest }: PropsType) => { +const TextArea = ({ forwardRef: ref, input, ...rest }: PropsType) => { const hasLabel = rest.label != null const hasError = rest.error != null @@ -74,7 +74,7 @@ const TextArea = ({ input, ...rest }: PropsType) => { hasLabel && hasError ? ' - ' : '' }${rest.error || ''}`} - + ) } @@ -99,4 +99,8 @@ TextArea.defaultProps = { width: '100%', } -export default TextArea +type RefPropsType = { ...PropsType } + +const TextAreaWithRef = (props, ref) => + > + + `; exports[`TextArea should render correctly with disabled 1`] = ` @@ -233,62 +236,66 @@ exports[`TextArea should render correctly with disabled 1`] = ` font-size: 0.875rem; } - + > + + `; exports[`TextArea should render correctly with e2e 1`] = ` @@ -409,62 +417,66 @@ exports[`TextArea should render correctly with e2e 1`] = ` font-size: 0.875rem; } - + > + + `; exports[`TextArea should render correctly with error="error" 1`] = ` @@ -603,157 +616,161 @@ exports[`TextArea should render correctly with error="error" 1`] = ` font-size: 0.875rem; } - + > + + `; exports[`TextArea should render correctly with height 1`] = ` @@ -877,62 +895,66 @@ exports[`TextArea should render correctly with height 1`] = ` font-size: 0.875rem; } - + > + + `; exports[`TextArea should render correctly with label="label" 1`] = ` @@ -1071,156 +1094,160 @@ exports[`TextArea should render correctly with label="label" 1`] = ` font-size: 0.875rem; } - + > + + `; exports[`TextArea should render correctly with label="label" and error="error" 1`] = ` @@ -1364,159 +1392,164 @@ exports[`TextArea should render correctly with label="label" and error="error" 1 font-size: 0.875rem; } - + > + + `; exports[`TextArea should render correctly with name 1`] = ` @@ -1642,64 +1676,68 @@ exports[`TextArea should render correctly with name 1`] = ` font-size: 0.875rem; } - + > + + `; exports[`TextArea should render correctly with value 1`] = ` @@ -1822,66 +1861,71 @@ exports[`TextArea should render correctly with value 1`] = ` font-size: 0.875rem; } - + > + + `; exports[`TextArea should render correctly with width 1`] = ` @@ -2006,62 +2051,66 @@ exports[`TextArea should render correctly with width 1`] = ` font-size: 0.875rem; } - + > + + `; diff --git a/src/Molecules/Dropdowns/SingleDropdown.js b/src/Molecules/Dropdowns/SingleDropdown.js index 997da78f..6ec72f85 100644 --- a/src/Molecules/Dropdowns/SingleDropdown.js +++ b/src/Molecules/Dropdowns/SingleDropdown.js @@ -16,6 +16,7 @@ export type PropsType = { +className?: string, +disabled?: boolean, +error?: string, + +forwardRef?: React.ElementRef<*>, +input?: {}, +italic?: boolean, +items: Array<{ +text: string, +value: string | number }>, @@ -105,6 +106,7 @@ const Select = styled.select` const SingleDropdown = ({ className, + forwardRef: ref, input, items, placeholder, @@ -128,6 +130,7 @@ const SingleDropdown = ({ defaultValue: placeholder != null ? '' : undefined, ...input, ...rest, + ref, }} > {placeholder != null && ( @@ -166,4 +169,8 @@ SingleDropdown.defaultProps = { width: '100%', } -export default SingleDropdown +const SingleDropdownWithRef = (props, ref) => ( + +) + +export default React.forwardRef(SingleDropdownWithRef) diff --git a/src/Molecules/Dropdowns/__snapshots__/SingleDropdown.spec.js.snap b/src/Molecules/Dropdowns/__snapshots__/SingleDropdown.spec.js.snap index ca4762d6..9626c9f6 100644 --- a/src/Molecules/Dropdowns/__snapshots__/SingleDropdown.spec.js.snap +++ b/src/Molecules/Dropdowns/__snapshots__/SingleDropdown.spec.js.snap @@ -76,9 +76,7 @@ exports[`SingleDropdown should render correctly 1`] = ` box-shadow: 0 0 0 1px #10ADE4; } - - - + -
- - + - - - -
-
-
-
+ + + + + + +
+ `; exports[`SingleDropdown should render correctly with capitalize 1`] = ` @@ -431,9 +476,8 @@ exports[`SingleDropdown should render correctly with capitalize 1`] = ` box-shadow: 0 0 0 1px #10ADE4; } - - - + -
- - + - - - -
-
-
-
+ + + + + + + + `; exports[`SingleDropdown should render correctly with error="error" 1`] = ` @@ -793,10 +884,8 @@ exports[`SingleDropdown should render correctly with error="error" 1`] = ` box-shadow: 0 0 0 1px transparent; } - - - + -
- - + - - - - - + error + + + + + - - - -
-
-
-
+ + + + + + + + `; exports[`SingleDropdown should render correctly with italic 1`] = ` @@ -1197,8 +1333,7 @@ exports[`SingleDropdown should render correctly with italic 1`] = ` box-shadow: 0 0 0 1px #10ADE4; } - - - + -
- - + - - - -
-
-
-
+ + + + + + + + `; exports[`SingleDropdown should render correctly with label="label" 1`] = ` @@ -1559,9 +1741,7 @@ exports[`SingleDropdown should render correctly with label="label" 1`] = ` box-shadow: 0 0 0 1px #10ADE4; } - - - + -
- - + - - - - - + label + + + + + - - - -
-
-
-
+ + + + + + + + `; exports[`SingleDropdown should render correctly with label="label" and error="error" 1`] = ` @@ -1971,10 +2198,8 @@ exports[`SingleDropdown should render correctly with label="label" and error="er box-shadow: 0 0 0 1px transparent; } - - - + -
- - + - - - - - + label - error + + + + + - - - -
-
-
-
+ + + + + + + + `; exports[`SingleDropdown should render correctly with placeholder 1`] = ` @@ -2379,9 +2652,7 @@ exports[`SingleDropdown should render correctly with placeholder 1`] = ` box-shadow: 0 0 0 1px #10ADE4; } - - - + -
- - + - - - -
-
-
-
+ + + + + + + + `; exports[`SingleDropdown should render correctly with uppercase 1`] = ` @@ -2744,9 +3063,7 @@ exports[`SingleDropdown should render correctly with uppercase 1`] = ` box-shadow: 0 0 0 1px #10ADE4; } - - - + -
- - + - - - -
-
-
-
+ + + + + + + + `; exports[`SingleDropdown should render correctly with weight 1`] = ` @@ -3100,9 +3465,7 @@ exports[`SingleDropdown should render correctly with weight 1`] = ` box-shadow: 0 0 0 1px #10ADE4; } - - - + -
- - + - - - -
-
-
-
+ + + + + + + + `; diff --git a/src/Molecules/SearchBars/__snapshots__/SearchBar.spec.js.snap b/src/Molecules/SearchBars/__snapshots__/SearchBar.spec.js.snap index f95a493a..d7a23822 100644 --- a/src/Molecules/SearchBars/__snapshots__/SearchBar.spec.js.snap +++ b/src/Molecules/SearchBars/__snapshots__/SearchBar.spec.js.snap @@ -401,7 +401,7 @@ exports[`SearchBar should render correctly 1`] = ` - - - - + -
- - + - - - -
-
-
- + > + +
+ + + + + + -
+ @@ -1087,7 +1095,7 @@ exports[`SearchBar should render correctly with e2e 1`] = ` - - - - + -
- - + - - - -
-
-
- + > + +
+ + + + + + -
+ @@ -1774,7 +1790,7 @@ exports[`SearchBar should render correctly with onChange 1`] = ` - @@ -1804,119 +1820,127 @@ exports[`SearchBar should render correctly with onChange 1`] = ` ", ], }, - "displayName": "Styled(Input)", + "displayName": "Styled(Component)", "foldedComponentIds": Array [], "render": [Function], "styledComponentId": "sc-SpTIZ", - "target": [Function], + "target": Object { + "$$typeof": Symbol(react.forward_ref), + "render": [Function], + }, "toString": [Function], "warnTooManyClasses": [Function], "withComponent": [Function], - Symbol(Symbol.hasInstance): [Function], } } forwardedRef={null} onChange={[MockFunction]} > - - - + -
- - + - - - -
-
-
- + > + + + + + + + + -
+ @@ -2467,7 +2492,7 @@ exports[`SearchBar should render correctly with onClick 1`] = ` - - - - + -
- - + - - - -
-
-
- + > + +
+ + + + + + -
+ - - - - + -
- - + - - - -
-
-
- + > + + + + + + + + -
+ @@ -3858,7 +3901,7 @@ exports[`SearchBar should render correctly with width 1`] = ` - - - - + -
- - + - - - -
-
-
- + > + + + + + + + + -
+ diff --git a/src/Molecules/SearchBars/__snapshots__/SearchBarUnderlined.spec.js.snap b/src/Molecules/SearchBars/__snapshots__/SearchBarUnderlined.spec.js.snap index 0b367141..bd866bab 100644 --- a/src/Molecules/SearchBars/__snapshots__/SearchBarUnderlined.spec.js.snap +++ b/src/Molecules/SearchBars/__snapshots__/SearchBarUnderlined.spec.js.snap @@ -180,7 +180,7 @@ exports[`SearchBarUnderlined should render correctly 1`] = ` className="c0" width="100%" > - - - - + -
- - + - - - -
-
-
- + > + +
+ + + +
+ + - + @@ -690,7 +698,7 @@ exports[`SearchBarUnderlined should render correctly with e2e 1`] = ` className="c0" width="100%" > - - - - + -
- - + - - - -
-
-
- + > + +
+ + + +
+ + - + @@ -1201,7 +1217,7 @@ exports[`SearchBarUnderlined should render correctly with onChange 1`] = ` className="c0" width="100%" > - @@ -1229,119 +1245,127 @@ exports[`SearchBarUnderlined should render correctly with onChange 1`] = ` ", ], }, - "displayName": "Styled(Input)", + "displayName": "Styled(Component)", "foldedComponentIds": Array [], "render": [Function], "styledComponentId": "sc-irJFYO", - "target": [Function], + "target": Object { + "$$typeof": Symbol(react.forward_ref), + "render": [Function], + }, "toString": [Function], "warnTooManyClasses": [Function], "withComponent": [Function], - Symbol(Symbol.hasInstance): [Function], } } forwardedRef={null} onChange={[MockFunction]} > - - - + -
- - + - - - -
-
-
- + > + + + + + +
+ + - + @@ -1718,7 +1743,7 @@ exports[`SearchBarUnderlined should render correctly with onClick 1`] = ` className="c0" width="100%" > - - - - + -
- - + - - - -
-
-
- + > + +
+ + + +
+ + - + - - - - + -
- - + - - - -
-
-
- + > + + + + + +
+ + - + @@ -2757,7 +2800,7 @@ exports[`SearchBarUnderlined should render correctly with width 1`] = ` className="c0" width="200px" > - - - - + -
- - + - - - -
-
-
- + > + + + + + +
+ + - +