diff --git a/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.d.ts b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.d.ts new file mode 100644 index 00000000000..d72563feeb0 --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.d.ts @@ -0,0 +1,10 @@ +import { SFC, HTMLProps, ReactNode } from 'react'; +import { Omit } from '../../typeUtils' + +export interface InputGroupProps extends Omit, 'children'> { + children: ReactNode; +} + +declare const InputGroup: SFC; + +export default InputGroup; diff --git a/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.docs.js b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.docs.js new file mode 100644 index 00000000000..38e5754cb08 --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.docs.js @@ -0,0 +1,16 @@ +import { InputGroup, InputGroupText } from '@patternfly/react-core'; +import SimpleInputGroups from './examples/SimpleInputGroups'; + +export default { + title: 'InputGroup', + components: { + InputGroup, + InputGroupText + }, + examples: [ + { + component: SimpleInputGroups, + title: 'Buttons and TextArea' + } + ] +}; diff --git a/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.js b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.js new file mode 100644 index 00000000000..2f85f0dc1ad --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.js @@ -0,0 +1,38 @@ +import React from 'react'; +import styles from '@patternfly/patternfly/components/InputGroup/input-group.css'; +import { css } from '@patternfly/react-styles'; +import PropTypes from 'prop-types'; +import { FormSelect } from '../FormSelect'; +import { TextArea } from '../TextArea'; +import { TextInput } from '../TextInput'; + +const InputGroup = ({ className, children, ...props }) => { + const formCtrls = [FormSelect, TextArea, TextInput].map(comp => comp.toString()); + const idItem = React.Children.toArray(children).find( + child => !formCtrls.includes(child.type.toString()) && child.props.id + ); + return ( +
+ {idItem + ? React.Children.map(children, child => { + return formCtrls.includes(child.type.toString()) + ? React.cloneElement(child, { 'aria-describedby': idItem.props.id }) + : child; + }) + : children} +
+ ); +}; + +InputGroup.propTypes = { + /** Additional classes added to the input group. */ + className: PropTypes.string, + /** Content rendered inside the input group. */ + children: PropTypes.node.isRequired +}; + +InputGroup.defaultProps = { + className: '' +}; + +export default InputGroup; diff --git a/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.test.js b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.test.js new file mode 100644 index 00000000000..8133bfa1869 --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroup.test.js @@ -0,0 +1,46 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import InputGroup from './InputGroup'; +import InputGroupText from './InputGroupText'; +import { Button } from '../Button'; +import { TextInput } from '../TextInput'; + +test('InputGroupText', () => { + const view = shallow( + + @ + + ); + expect(view.find('span')).toHaveLength(1); + const spanProps = view.find('span').props(); + expect(spanProps.className).toEqual(expect.stringContaining('inpt-grp-text')); + expect(spanProps.id).toBe('email-npt-grp'); + expect(view.text()).toBe('@'); +}); + +test('InputGroup', () => { + const view = shallow( + + + + ); + + expect(view.find('div')).toHaveLength(1); + const divProps = view.find('div').props(); + expect(divProps.className).toEqual(expect.stringContaining('text-verify-cls')); + expect(divProps.id).toBe('text-1'); +}); + +test('add aria-describedby to form-control if one of the non form-controls has id', () => { + // In this test, TextInput is a form-control component and Button is not. + // If Button has an id props, this should be used in aria-describedby. + const view = shallow( + + + + + ); + expect(view.find(TextInput).props()['aria-describedby']).toBe('button-id'); +}); diff --git a/packages/patternfly-4/react-core/src/components/InputGroup/InputGroupText.d.ts b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroupText.d.ts new file mode 100644 index 00000000000..7b1fb75dfae --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroupText.d.ts @@ -0,0 +1,11 @@ +import { SFC, HTMLProps, ReactNode } from 'react'; +import { Omit } from '../../typeUtils' + +export interface InputGroupTextProps extends Omit, 'children'> { + children: ReactNode; + component?: ReactType | ReactType; +} + +declare const InputGroupText: SFC; + +export default InputGroupText; diff --git a/packages/patternfly-4/react-core/src/components/InputGroup/InputGroupText.js b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroupText.js new file mode 100644 index 00000000000..61f5c660455 --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/InputGroup/InputGroupText.js @@ -0,0 +1,29 @@ +import React from 'react'; +import styles from '@patternfly/patternfly/components/InputGroup/input-group.css'; +import { css } from '@patternfly/react-styles'; +import PropTypes from 'prop-types'; +import { componentShape } from '../../helpers/componentShape'; + +const InputGroupText = ({ component: Component, className, children, ...props }) => { + return ( + + {children} + + ); +}; + +InputGroupText.propTypes = { + /** Additional classes added to the input group text. */ + className: PropTypes.string, + /** Content rendered inside the input group text. */ + children: PropTypes.node.isRequired, + /** Component that wraps the input group text. */ + component: componentShape, +}; + +InputGroupText.defaultProps = { + className: '', + component: 'span', +}; + +export default InputGroupText; diff --git a/packages/patternfly-4/react-core/src/components/InputGroup/examples/SimpleInputGroups.js b/packages/patternfly-4/react-core/src/components/InputGroup/examples/SimpleInputGroups.js new file mode 100644 index 00000000000..fd9ef1bc111 --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/InputGroup/examples/SimpleInputGroups.js @@ -0,0 +1,106 @@ +import React from 'react'; +import { DollarSignIcon, AtIcon, CalendarAltIcon, SearchIcon, QuestionCircleIcon } from '@patternfly/react-icons'; +import { + Button, + TextArea, + InputGroup, + InputGroupText, + TextInput, + Dropdown, + DropdownToggle, + Popover +} from '@patternfly/react-core'; + +class SimpleInputGroups extends React.Component { + render() { + return ( + + + +