From 08fa5cabe58319dbb3eaa800d65298b56e6d43eb Mon Sep 17 00:00:00 2001 From: Avi Sharvit Date: Sun, 17 Dec 2017 17:06:57 +0200 Subject: [PATCH] feat(Form): Add form related components re #118 #117 #116 --- src/components/Form/Checkbox.js | 1 + src/components/Form/ControlLabel.js | 1 + src/components/Form/Form.js | 1 + src/components/Form/Form.stories.js | 254 ++++++++++++++++++ src/components/Form/FormControl.js | 1 + src/components/Form/FormGroup.js | 1 + src/components/Form/HelpBlock.js | 1 + src/components/Form/InputGroup.js | 1 + src/components/Form/Radio.js | 1 + src/components/Form/Stories/BasicForm.js | 94 +++++++ .../Form/Stories/HorizontalFormField.js | 35 +++ src/components/Form/Stories/InlineForm.js | 35 +++ .../Form/Stories/InlineFormField.js | 28 ++ .../Form/Stories/InputGroupsForm.js | 142 ++++++++++ .../Form/Stories/SupportedControlsForm.js | 120 +++++++++ .../Form/Stories/VerticalFormField.js | 30 +++ src/components/Form/index.js | 8 + src/index.js | 1 + 18 files changed, 755 insertions(+) create mode 100644 src/components/Form/Checkbox.js create mode 100644 src/components/Form/ControlLabel.js create mode 100644 src/components/Form/Form.js create mode 100644 src/components/Form/Form.stories.js create mode 100644 src/components/Form/FormControl.js create mode 100644 src/components/Form/FormGroup.js create mode 100644 src/components/Form/HelpBlock.js create mode 100644 src/components/Form/InputGroup.js create mode 100644 src/components/Form/Radio.js create mode 100644 src/components/Form/Stories/BasicForm.js create mode 100644 src/components/Form/Stories/HorizontalFormField.js create mode 100644 src/components/Form/Stories/InlineForm.js create mode 100644 src/components/Form/Stories/InlineFormField.js create mode 100644 src/components/Form/Stories/InputGroupsForm.js create mode 100644 src/components/Form/Stories/SupportedControlsForm.js create mode 100644 src/components/Form/Stories/VerticalFormField.js create mode 100644 src/components/Form/index.js diff --git a/src/components/Form/Checkbox.js b/src/components/Form/Checkbox.js new file mode 100644 index 00000000000..a534135f21b --- /dev/null +++ b/src/components/Form/Checkbox.js @@ -0,0 +1 @@ +export { Checkbox as default } from 'react-bootstrap'; diff --git a/src/components/Form/ControlLabel.js b/src/components/Form/ControlLabel.js new file mode 100644 index 00000000000..eca5ae64beb --- /dev/null +++ b/src/components/Form/ControlLabel.js @@ -0,0 +1 @@ +export { ControlLabel as default } from 'react-bootstrap'; diff --git a/src/components/Form/Form.js b/src/components/Form/Form.js new file mode 100644 index 00000000000..b4275639eec --- /dev/null +++ b/src/components/Form/Form.js @@ -0,0 +1 @@ +export { Form as default } from 'react-bootstrap'; diff --git a/src/components/Form/Form.stories.js b/src/components/Form/Form.stories.js new file mode 100644 index 00000000000..245dee24a9a --- /dev/null +++ b/src/components/Form/Form.stories.js @@ -0,0 +1,254 @@ +/* eslint react/prop-types: 0 */ + +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import { withKnobs, boolean } from '@storybook/addon-knobs'; +import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; +import { Icon } from '../Icon'; +import { Col, Row, Grid } from '../Grid'; +import { Button } from '../Button'; +import { Modal } from '../Modal'; +import { Form } from './index'; + +import { + InlineFormFields, + InlineFormButtons, + getInlineFormKnobs +} from './Stories/InlineForm'; +import { + BasicFormFields, + BasicFormButtons, + BasicFormSpinner, + getBasicFormKnobs +} from './Stories/BasicForm'; +import { + SupportedControlsFormFields, + getSupportedControlsFormKnobs +} from './Stories/SupportedControlsForm'; +import { + InputGroupsFormFields, + getInputGroupsFormKnobs +} from './Stories/InputGroupsForm'; +import { InlineFormField } from './Stories/InlineFormField'; +import { HorizontalFormField } from './Stories/HorizontalFormField'; +import { VerticalFormField } from './Stories/VerticalFormField'; + +const stories = storiesOf('Forms', module); + +stories.addDecorator(withKnobs); + +const description = ( +

+ Those components are based on React Bootstrap Form components. See{' '} + + React Bootstrap Docs + {' '} + for complete Form components documentation. +

+); + +stories.addDecorator( + defaultTemplate({ + title: 'Forms', + documentationLink: + 'http://www.patternfly.org/pattern-library/widgets/#forms', + description: description + }) +); + +stories.addWithInfo('Inline Form', '', () => { + const formFieldsKnobs = getInlineFormKnobs(); + const { bsSize, disabled } = formFieldsKnobs; + const buttonsProps = {}; + + if (bsSize) buttonsProps.bsSize = bsSize; + if (disabled) buttonsProps.disabled = disabled; + + const formFields = InlineFormFields.map(formField => + InlineFormField({ ...formField, ...formFieldsKnobs }) + ).reduce((result = [], element) => { + return [...result, element, ' ']; // create spacing betwwen elements + }, []); + + const formButtons = InlineFormButtons.map(({ text, ...props }) => ( + + )); + + return ( + +
+ {formFields} {formButtons} +
+
+ ); +}); + +stories.addWithInfo('Horizontal Form', '', () => { + const formFieldsKnobs = getBasicFormKnobs(); + const { bsSize, disabled } = formFieldsKnobs; + const buttonsProps = {}; + + if (bsSize) buttonsProps.bsSize = bsSize; + if (disabled) buttonsProps.disabled = disabled; + + const showLoading = boolean('Show Loading', false); + + const formFields = BasicFormFields.map(formField => + HorizontalFormField({ ...formField, ...formFieldsKnobs }) + ); + + const formButtons = BasicFormButtons.map(({ text, ...props }) => ( + + {' '} + + )); + + return ( + +
+ {formFields} + + + {formButtons} + + + {showLoading && ( + + + {[...BasicFormSpinner]} + + + )} +
+
+ ); +}); + +stories.addWithInfo('Vertical Form', '', () => { + const formFieldsKnobs = getBasicFormKnobs(); + const { bsSize, disabled } = formFieldsKnobs; + const buttonsProps = {}; + + if (bsSize) buttonsProps.bsSize = bsSize; + if (disabled) buttonsProps.disabled = disabled; + + const showLoading = boolean('Show Loading', false); + + const formFields = BasicFormFields.map(formField => + VerticalFormField({ ...formField, ...formFieldsKnobs }) + ); + + const formButtons = BasicFormButtons.map(({ text, ...props }) => ( + + {' '} + + )); + + return ( + +
+ + {formFields} + + + {formButtons} + + {showLoading && ( + + {BasicFormSpinner} + + )} +
+
+ ); +}); + +stories.addWithInfo('Modal Form', '', () => { + const formFieldsKnobs = getBasicFormKnobs(); + const { bsSize, disabled } = formFieldsKnobs; + const buttonsProps = {}; + + if (bsSize) buttonsProps.bsSize = bsSize; + if (disabled) buttonsProps.disabled = disabled; + + const showModal = boolean('Show Modal', true); + const showLoading = boolean('Show Loading', false); + + const formFields = BasicFormFields.map(formField => + HorizontalFormField({ ...formField, ...formFieldsKnobs }) + ); + + const formButtons = BasicFormButtons.map(({ text, ...props }) => ( + + )).reverse(); + + const formSpinner = ( +
+ {[...BasicFormSpinner].reverse()} +
+ ); + + return ( + + + + Basic Settings + + +
{formFields}
+
+ + {formButtons} + {showLoading && formSpinner} + +
+ ); +}); + +stories.addWithInfo('Supported Controls', '', () => { + const formFieldsKnobs = getSupportedControlsFormKnobs(); + + const formFields = SupportedControlsFormFields.map(formField => + VerticalFormField({ ...formField, ...formFieldsKnobs }) + ); + + return ( + +
{formFields}
+
+ ); +}); + +stories.addWithInfo('Input Groups', '', () => { + const formFieldsKnobs = getInputGroupsFormKnobs(); + + const formFields = InputGroupsFormFields.map(formField => + VerticalFormField({ ...formField, ...formFieldsKnobs }) + ); + + return ( + +
+ + {formFields} + +
+
+ ); +}); diff --git a/src/components/Form/FormControl.js b/src/components/Form/FormControl.js new file mode 100644 index 00000000000..b0090ba0649 --- /dev/null +++ b/src/components/Form/FormControl.js @@ -0,0 +1 @@ +export { FormControl as default } from 'react-bootstrap'; diff --git a/src/components/Form/FormGroup.js b/src/components/Form/FormGroup.js new file mode 100644 index 00000000000..2b2414bd415 --- /dev/null +++ b/src/components/Form/FormGroup.js @@ -0,0 +1 @@ +export { FormGroup as default } from 'react-bootstrap'; diff --git a/src/components/Form/HelpBlock.js b/src/components/Form/HelpBlock.js new file mode 100644 index 00000000000..7ac25b75970 --- /dev/null +++ b/src/components/Form/HelpBlock.js @@ -0,0 +1 @@ +export { HelpBlock as default } from 'react-bootstrap'; diff --git a/src/components/Form/InputGroup.js b/src/components/Form/InputGroup.js new file mode 100644 index 00000000000..89263986f60 --- /dev/null +++ b/src/components/Form/InputGroup.js @@ -0,0 +1 @@ +export { InputGroup as default } from 'react-bootstrap'; diff --git a/src/components/Form/Radio.js b/src/components/Form/Radio.js new file mode 100644 index 00000000000..0a1d6699ad0 --- /dev/null +++ b/src/components/Form/Radio.js @@ -0,0 +1 @@ +export { Radio as default } from 'react-bootstrap'; diff --git a/src/components/Form/Stories/BasicForm.js b/src/components/Form/Stories/BasicForm.js new file mode 100644 index 00000000000..1fcc20d3f3a --- /dev/null +++ b/src/components/Form/Stories/BasicForm.js @@ -0,0 +1,94 @@ +/* eslint react/prop-types: 0 */ + +import React from 'react'; +import { action } from '@storybook/addon-actions'; +import { select, boolean } from '@storybook/addon-knobs'; +import { Spinner } from '../../Spinner'; +import { Button } from '../../Button'; +import { FormControl, InputGroup } from '../index'; + +export const BasicFormFields = [ + { + controlId: 'name', + label: 'Name', + help: 'Enter your name', + formControl: ({ validationState, ...props }) => ( + + ) + }, + { + controlId: 'address', + label: 'Address', + help: 'Enter your address', + formControl: ({ validationState, ...props }) => ( + + ) + }, + { + controlId: 'city', + label: 'City', + help: 'Enter your city', + formControl: ({ validationState, ...props }) => ( + + ) + }, + { + controlId: 'email', + label: 'Email', + help: 'Enter a valid email address', + formControl: ({ validationState, ...props }) => ( + + ) + }, + { + controlId: 'url', + label: 'My meeting URL', + help: 'Enter a valid URL', + formControl: ({ validationState, disabled, ...props }) => { + const controlProps = { disabled }; + return ( + + + + + + + ); + } + } +]; + +export const BasicFormButtons = [ + { + text: 'Save', + bsStyle: 'primary', + onClick: action('Save') + }, + { + text: 'Cancel', + bsStyle: 'default', + onClick: action('Cancel') + } +]; + +export const BasicFormSpinner = [ + , + ' ', + + Do not refresh this page. This request may take a minute... + +]; + +export const getBasicFormKnobs = () => ({ + validationState: select('Validation State', [ + null, + 'success', + 'warning', + 'error' + ]), + bsSize: select('Size', [null, 'small', 'large']), + showHelp: boolean('Show Help', true), + disabled: boolean('Disabled', false) +}); diff --git a/src/components/Form/Stories/HorizontalFormField.js b/src/components/Form/Stories/HorizontalFormField.js new file mode 100644 index 00000000000..1aba6664980 --- /dev/null +++ b/src/components/Form/Stories/HorizontalFormField.js @@ -0,0 +1,35 @@ +/* eslint react/prop-types: 0 */ + +import React from 'react'; +import { Col } from '../../Grid'; +import { FormGroup, ControlLabel, HelpBlock } from '../index'; + +export const HorizontalFormField = ({ + controlId, + label, + help, + formControl, + validationState, + bsSize, + showHelp, + ...props +}) => { + const controlProps = { ...props }; + + if (bsSize) controlProps.bsSize = bsSize; + if (validationState) controlProps.validationState = validationState; + + const formGroupProps = { key: controlId, controlId, ...controlProps }; + + return ( + + + {label} + + + {formControl(controlProps)} + {showHelp && help && {help}} + + + ); +}; diff --git a/src/components/Form/Stories/InlineForm.js b/src/components/Form/Stories/InlineForm.js new file mode 100644 index 00000000000..beb2b2624dd --- /dev/null +++ b/src/components/Form/Stories/InlineForm.js @@ -0,0 +1,35 @@ +/* eslint react/prop-types: 0 */ + +import React from 'react'; +import { action } from '@storybook/addon-actions'; +import { select, boolean } from '@storybook/addon-knobs'; +import { FormControl } from '../index'; + +export const InlineFormFields = [ + { + controlId: 'email', + label: 'Email', + placeholder: 'Email', + formControl: props => + }, + { + controlId: 'password', + label: 'Password', + placeholder: 'Password', + formControl: props => + } +]; + +export const InlineFormButtons = [ + { + text: 'Log In', + bsStyle: 'primary', + onClick: action('Login') + } +]; + +export const getInlineFormKnobs = () => ({ + bsSize: select('Size', [null, 'small', 'large']), + disabled: boolean('Disabled', false), + showLabel: boolean('Show Labels', false) +}); diff --git a/src/components/Form/Stories/InlineFormField.js b/src/components/Form/Stories/InlineFormField.js new file mode 100644 index 00000000000..cd17f2f73cf --- /dev/null +++ b/src/components/Form/Stories/InlineFormField.js @@ -0,0 +1,28 @@ +/* eslint react/prop-types: 0 */ + +import React from 'react'; +import { FormGroup, ControlLabel } from '../index'; + +export const InlineFormField = ({ + controlId, + label, + formControl, + validationState, + bsSize, + showLabel, + ...props +}) => { + const controlProps = { ...props }; + + if (bsSize) controlProps.bsSize = bsSize; + if (validationState) controlProps.validationState = validationState; + + const formGroupProps = { key: controlId, controlId, ...controlProps }; + + return ( + + {showLabel && label && {label}}{' '} + {formControl(controlProps)} + + ); +}; diff --git a/src/components/Form/Stories/InputGroupsForm.js b/src/components/Form/Stories/InputGroupsForm.js new file mode 100644 index 00000000000..bc4de0babb3 --- /dev/null +++ b/src/components/Form/Stories/InputGroupsForm.js @@ -0,0 +1,142 @@ +/* eslint react/prop-types: 0 */ + +import React from 'react'; +import { select, boolean } from '@storybook/addon-knobs'; +import { Button, DropdownButton } from '../../Button'; +import { Icon } from '../../Icon'; +import { MenuItem } from '../../MenuItem'; +import { FormControl, InputGroup } from '../index'; + +export const InputGroupsFormFields = [ + { + controlId: 'control-1', + formControl: ({ + validationState = 'default', + disabled = false, + ...props + }) => ( + + @ + + + ) + }, + { + controlId: 'control-2', + formControl: ({ + validationState = 'default', + disabled = false, + ...props + }) => ( + + + .00 + + ) + }, + { + controlId: 'control-3', + formControl: ({ + validationState = 'default', + disabled = false, + ...props + }) => ( + + $ + + .00 + + ) + }, + { + controlId: 'control-4', + formControl: ({ + validationState = 'default', + disabled = false, + ...props + }) => ( + + + + + + + ) + }, + { + controlId: 'control-5', + formControl: ({ + validationState = 'default', + disabled = false, + ...props + }) => ( + + + + + + + ) + }, + { + controlId: 'control-6', + formControl: ({ + validationState = 'default', + disabled = false, + ...props + }) => ( + + + + Item + + + ) + }, + { + controlId: 'control-7', + formControl: ({ + validationState = 'default', + disabled = false, + ...props + }) => ( + + + + + + + ) + }, + { + controlId: 'control-8', + formControl: ({ + validationState = 'default', + disabled = false, + ...props + }) => ( + + + + + + + ) + } +]; + +export const getInputGroupsFormKnobs = () => ({ + validationState: select('Validation State', [ + null, + 'success', + 'warning', + 'error' + ]), + bsSize: select('Size', [null, 'small', 'large']), + disabled: boolean('Disabled', false) +}); diff --git a/src/components/Form/Stories/SupportedControlsForm.js b/src/components/Form/Stories/SupportedControlsForm.js new file mode 100644 index 00000000000..9b122824004 --- /dev/null +++ b/src/components/Form/Stories/SupportedControlsForm.js @@ -0,0 +1,120 @@ +/* eslint react/prop-types: 0 */ + +import React from 'react'; +import { select, boolean } from '@storybook/addon-knobs'; +import { FormControl, Checkbox, Radio } from '../index'; + +export const SupportedControlsFormFields = [ + { + controlId: 'text', + label: 'Text', + help: 'Help text', + formControl: ({ validationState, ...props }) => ( + + ) + }, + { + controlId: 'email', + label: 'Email', + help: 'Help text', + formControl: ({ validationState, ...props }) => ( + + ) + }, + { + controlId: 'password', + label: 'Password', + help: 'Help text', + formControl: ({ validationState, ...props }) => ( + + ) + }, + { + controlId: 'file', + label: 'File', + help: 'Help text', + formControl: ({ validationState, bsSize, ...props }) => ( + + ) + }, + { + controlId: 'checkbox', + label: 'Checkbox', + help: 'Help text', + formControl: ({ validationState, ...props }) => ( + Checkbox + ) + }, + { + controlId: 'radio', + label: 'Radio', + help: 'Help text', + formControl: ({ validationState, ...props }) => ( + Radio + ) + }, + { + controlId: 'checkboxGroup', + label: 'Checkbox Group', + help: 'Help text', + formControl: ({ validationState, ...props }) => ( +
+ + 1 + + + 2 + + + 3 + +
+ ) + }, + { + controlId: 'radioGroup', + label: 'Radio Group', + help: 'Help text', + formControl: ({ validationState, ...props }) => ( +
+ + 1 + + + 2 + + + 3 + +
+ ) + }, + { + controlId: 'textarea', + label: 'Textarea', + help: 'Help text', + formControl: ({ validationState, ...props }) => ( + + ) + }, + { + controlId: 'static', + label: 'Static', + help: 'Help text', + formControl: ({ validationState, ...props }) => ( + email@example.com + ) + } +]; + +export const getSupportedControlsFormKnobs = () => ({ + validationState: select('Validation State', [ + null, + 'success', + 'warning', + 'error' + ]), + bsSize: select('Size', [null, 'small', 'large']), + showHelp: boolean('Show Help', true), + disabled: boolean('Disabled', false) +}); diff --git a/src/components/Form/Stories/VerticalFormField.js b/src/components/Form/Stories/VerticalFormField.js new file mode 100644 index 00000000000..22a80b704e9 --- /dev/null +++ b/src/components/Form/Stories/VerticalFormField.js @@ -0,0 +1,30 @@ +/* eslint react/prop-types: 0 */ + +import React from 'react'; +import { FormGroup, ControlLabel, HelpBlock } from '../index'; + +export const VerticalFormField = ({ + controlId, + label, + help, + formControl, + validationState, + bsSize, + showHelp, + ...props +}) => { + const controlProps = { ...props }; + + if (bsSize) controlProps.bsSize = bsSize; + if (validationState) controlProps.validationState = validationState; + + const formGroupProps = { key: controlId, controlId, ...controlProps }; + + return ( + + {label && {label}} + {formControl(controlProps)} + {showHelp && help && {help}} + + ); +}; diff --git a/src/components/Form/index.js b/src/components/Form/index.js new file mode 100644 index 00000000000..8a7fb24615b --- /dev/null +++ b/src/components/Form/index.js @@ -0,0 +1,8 @@ +export { default as Checkbox } from './Checkbox'; +export { default as ControlLabel } from './ControlLabel'; +export { default as Form } from './Form'; +export { default as FormControl } from './FormControl'; +export { default as FormGroup } from './FormGroup'; +export { default as HelpBlock } from './HelpBlock'; +export { default as InputGroup } from './InputGroup'; +export { default as Radio } from './Radio'; diff --git a/src/index.js b/src/index.js index 384fa52de45..89a527b8fb1 100644 --- a/src/index.js +++ b/src/index.js @@ -5,6 +5,7 @@ export * from './components/Button'; export * from './components/Dropdown'; export * from './components/DropdownKebab'; export * from './components/Filter'; +export * from './components/Form'; export * from './components/Grid'; export * from './components/Icon'; export * from './components/Chart';