From a45cff4763f9d0133c4d2d2e3608c3a62565f49a Mon Sep 17 00:00:00 2001 From: Suzanne Rozier Date: Fri, 13 Sep 2019 16:18:50 -0400 Subject: [PATCH 1/7] Add new text input in Name --- src/components/Form/Name/Name.jsx | 25 +++++++ src/components/Inputs/FormField/FormField.jsx | 33 +++++++++ src/components/Inputs/InputConnector.jsx | 57 +++++++++++++++ src/components/Inputs/TextInput/TextInput.jsx | 71 +++++++++++++++++++ src/components/Inputs/connectedInputs.js | 9 +++ 5 files changed, 195 insertions(+) create mode 100644 src/components/Inputs/FormField/FormField.jsx create mode 100644 src/components/Inputs/InputConnector.jsx create mode 100644 src/components/Inputs/TextInput/TextInput.jsx create mode 100644 src/components/Inputs/connectedInputs.js diff --git a/src/components/Form/Name/Name.jsx b/src/components/Form/Name/Name.jsx index 6de49ee58..71bb92c90 100644 --- a/src/components/Form/Name/Name.jsx +++ b/src/components/Form/Name/Name.jsx @@ -1,5 +1,9 @@ import React from 'react' import { i18n } from 'config' + +import FormField from 'components/Inputs/FormField/FormField' +import { ConnectedTextInput } from 'components/Inputs/connectedInputs' + import ValidationElement from '../ValidationElement' import Field from '../Field' import Show from '../Show' @@ -140,6 +144,27 @@ export default class Name extends ValidationElement { return (
{this.props.title &&

{this.props.title}

} + + + { + this.update({ + first: values.value, + }) + }} + onFocus={this.props.onFocus} + onBlur={this.props.onBlur} + required={this.props.required} + disabled={this.props.disabled} + /> + + + {children} +
+ ) + } +} + +export default FormField diff --git a/src/components/Inputs/InputConnector.jsx b/src/components/Inputs/InputConnector.jsx new file mode 100644 index 000000000..167467982 --- /dev/null +++ b/src/components/Inputs/InputConnector.jsx @@ -0,0 +1,57 @@ +import React from 'react' +import PropTypes from 'prop-types' + +const connectInput = (Component) => { + class ConnectedInput extends React.Component { + constructor(props) { + super(props) + + this.uid = '' + + this.state = { + focus: false, + } + } + + handleChange = (event) => { + const { name, onChange } = this.props + const { value } = event.target + onChange({ value, name }) + } + + handleFocus = () => { + this.setState({ focus: true }) + } + + handleBlur = () => { + this.setState({ focus: false }) + } + + render() { + return ( + + ) + } + } + + ConnectedInput.propTypes = { + name: PropTypes.string.isRequired, + onChange: PropTypes.func, + } + + ConnectedInput.defaultProps = { + onChange: () => {}, + } + + return ConnectedInput +} + +export default connectInput diff --git a/src/components/Inputs/TextInput/TextInput.jsx b/src/components/Inputs/TextInput/TextInput.jsx new file mode 100644 index 000000000..5155b1d76 --- /dev/null +++ b/src/components/Inputs/TextInput/TextInput.jsx @@ -0,0 +1,71 @@ +import React from 'react' +import PropTypes from 'prop-types' +import classnames from 'classnames' + +import { autotab } from 'components/Form/Generic' + +const TextInput = (props) => { + const { + label, uid, name, type, placeholder, ariaLabel, disabled, error, maxlength, + pattern, readonly, autocapitalize, autocorrect, autocomplete, spellcheck, + value, clipboard, className, focus, valid, onChange, onFocus, onBlur, + tabBack, tabNext, + } = props + + const divClass = classnames( + 'hide-for-print', + className, + { 'usa-input-error': !disabled && error } + ) + + const labelClass = classnames({ + disabled, + 'usa-input-error-label': !disabled && error, + }) + + const inputClass = classnames({ + 'usa-input-focus': !disabled && focus, + 'usa-input-success': !disabled && valid, + }) + + const handleKeyDown = (e) => { + autotab(e, maxlength, tabBack, tabNext) + } + + return ( +
+ {label && ( + + )} + +
{value}
+
+ ) +} + +TextInput.propTypes = { + +} + +export default TextInput diff --git a/src/components/Inputs/connectedInputs.js b/src/components/Inputs/connectedInputs.js new file mode 100644 index 000000000..9e0faea40 --- /dev/null +++ b/src/components/Inputs/connectedInputs.js @@ -0,0 +1,9 @@ +import connectInput from 'components/Inputs/InputConnector' + +import TextInput from 'components/Inputs/TextInput/TextInput' + +const ConnectedTextInput = connectInput(TextInput) + +export { + ConnectedTextInput, +} From 5b7e047ae7deb65cf750aa2b2c9f8aa97f4bc3fb Mon Sep 17 00:00:00 2001 From: Suzanne Rozier Date: Tue, 17 Sep 2019 12:47:51 -0400 Subject: [PATCH 2/7] Add new input components from old spike --- src/components/Form/Name/Name.jsx | 38 +++--- .../Inputs/CheckboxInput/CheckboxInput.jsx | 97 ++++++++++++++ src/components/Inputs/FormField/FormField.jsx | 122 +++++++++++++++--- .../Inputs/FormField/FormField.module.scss | 5 + .../Inputs/FormField/FormField.stories.jsx | 21 +++ src/components/Inputs/InputConnector.jsx | 41 +++++- .../Inputs/SelectInput/SelectInput.jsx | 78 +++++++++++ src/components/Inputs/TextInput/TextInput.jsx | 56 +++++++- .../Inputs/TextInput/TextInput.stories.jsx | 55 ++++++++ 9 files changed, 471 insertions(+), 42 deletions(-) create mode 100644 src/components/Inputs/CheckboxInput/CheckboxInput.jsx create mode 100644 src/components/Inputs/FormField/FormField.module.scss create mode 100644 src/components/Inputs/FormField/FormField.stories.jsx create mode 100644 src/components/Inputs/SelectInput/SelectInput.jsx create mode 100644 src/components/Inputs/TextInput/TextInput.stories.jsx diff --git a/src/components/Form/Name/Name.jsx b/src/components/Form/Name/Name.jsx index 71bb92c90..5bceaa634 100644 --- a/src/components/Form/Name/Name.jsx +++ b/src/components/Form/Name/Name.jsx @@ -145,25 +145,25 @@ export default class Name extends ValidationElement {
{this.props.title &&

{this.props.title}

} - - { - this.update({ - first: values.value, - }) - }} - onFocus={this.props.onFocus} - onBlur={this.props.onBlur} - required={this.props.required} - disabled={this.props.disabled} - /> - + { + this.update({ + first: values.value, + }) + }} + onFocus={this.props.onFocus} + onBlur={this.props.onBlur} + required={this.props.required} + disabled={this.props.disabled} + /> { + const { + label, uid, name, ariaLabel, disabled, error, readonly, value, className, + valid, onChange, onFocus, onBlur, children, + } = props + + // eslint-disable-next-line eqeqeq + const checked = value == true + + const showError = !disabled && error + + const divClass = classnames( + className, + 'block', + { + disabled, + 'usa-input-error': showError, + } + ) + + const labelClass = classnames( + 'checkbox', + { + disabled, + 'usa-input-error-label': showError, + checked, + } + ) + + const inputClass = classnames({ + 'usa-input-success': !disabled && valid, + }) + + return ( +
+ + +
+ ) +} + +CheckboxInput.propTypes = { + name: PropTypes.string.isRequired, + uid: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + ariaLabel: PropTypes.string, + children: PropTypes.node, + value: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]), + error: PropTypes.bool, + valid: PropTypes.bool, + disabled: PropTypes.bool, + className: PropTypes.string, + readonly: PropTypes.bool, + onChange: PropTypes.func, + onFocus: PropTypes.func, + onBlur: PropTypes.func, +} + +CheckboxInput.defaultProps = { + ariaLabel: null, + children: null, + value: '', + error: false, + valid: false, + disabled: false, + className: '', + readonly: false, + onChange: () => {}, + onFocus: () => {}, + onBlur: () => {}, +} + +export default CheckboxInput diff --git a/src/components/Inputs/FormField/FormField.jsx b/src/components/Inputs/FormField/FormField.jsx index dff410834..0c3ac9187 100644 --- a/src/components/Inputs/FormField/FormField.jsx +++ b/src/components/Inputs/FormField/FormField.jsx @@ -1,33 +1,117 @@ +/** + * Field component requirements: + * - Wraps a form input + * - Renders inside of .usa-form-control + * - Renders an anchor ID so it can be linked to + * - renders a label + * - renders help text - displayed as internal state, icon toggle + * - renders an error message + * - renders the form input (children?) + * - render modifiers + * + * TODO: + * - data test ID + */ + import React from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' -class FormField extends React.Component { - constructor(props) { - super(props) +import i18n from 'util/i18n' + +const FormField = ({ + title, required, inputId, children, errors, helptext, toggleHelp, showHelp, + className, +}) => { + const classes = classnames( + 'field', + 'usa-form-control', + { required }, + className, + ) - this.state = { - helpActive: false, - } - } + const iconClasses = classnames( + 'toggle', + { active: showHelp }, + ) - render() { - const { children, optional, className, dataTestId } = this.props + return ( +
+ {title && ( +

+ {title} + {!required && (Optional)} +

+ )} - const required = !optional + {helptext && toggleHelp && ( + + )} - const classes = classnames( - 'field', - { required }, - className, - ) + {helptext && showHelp && ( +
+
+
+ {helptext} + {toggleHelp && ( + + )} +
+
+
+ )} - return ( -
+ {errors && errors.length > 0 && ( +
+ {errors.map((e, i) => ( +
+
{e}
+
+ ))} +
+ )} + +
{children}
- ) - } +
+ ) +} + +FormField.propTypes = { + name: PropTypes.string.isRequired, + children: PropTypes.node.isRequired, + errors: PropTypes.array, + helptext: PropTypes.node, +} + +FormField.defaultProps = { + errors: [], + helptext: null, } export default FormField diff --git a/src/components/Inputs/FormField/FormField.module.scss b/src/components/Inputs/FormField/FormField.module.scss new file mode 100644 index 000000000..6b531dc9c --- /dev/null +++ b/src/components/Inputs/FormField/FormField.module.scss @@ -0,0 +1,5 @@ +.modifiers { + display: flex; + justify-content: flex-end; + align-items: center; +} \ No newline at end of file diff --git a/src/components/Inputs/FormField/FormField.stories.jsx b/src/components/Inputs/FormField/FormField.stories.jsx new file mode 100644 index 000000000..a3800f844 --- /dev/null +++ b/src/components/Inputs/FormField/FormField.stories.jsx @@ -0,0 +1,21 @@ +/* eslint import/no-extraneous-dependencies: 0 */ + +import React from 'react' +import { storiesOf } from '@storybook/react' + +import FormField from './FormField' + +storiesOf('FormField', module) + .add('default', () => ( + + )) + .add('with a value', () => ( + + )) diff --git a/src/components/Inputs/InputConnector.jsx b/src/components/Inputs/InputConnector.jsx index 167467982..d14f7059b 100644 --- a/src/components/Inputs/InputConnector.jsx +++ b/src/components/Inputs/InputConnector.jsx @@ -1,7 +1,14 @@ +/** + * HOC that connects input event handlers and optionally renders inside of a + * FormField component + */ import React from 'react' import PropTypes from 'prop-types' -const connectInput = (Component) => { +import FormField from './FormField/FormField' +import styles from './FormField/FormField.module.scss' + +const connectInput = (Component, renderFormField = true) => { class ConnectedInput extends React.Component { constructor(props) { super(props) @@ -10,9 +17,15 @@ const connectInput = (Component) => { this.state = { focus: false, + showHelp: false, } } + toggleHelp = () => { + const { showHelp } = this.state + this.setState({ showHelp: !showHelp }) + } + handleChange = (event) => { const { name, onChange } = this.props const { value } = event.target @@ -28,7 +41,7 @@ const connectInput = (Component) => { } render() { - return ( + const inputComponent = ( { onChange={this.handleChange} onFocus={this.handleFocus} onBlur={this.handleBlur} - /> ) + + if (renderFormField) { + const { modifiers } = this.props + const { showHelp } = this.state + return ( + + {inputComponent} + {modifiers && ( +
{modifiers}
+ )} +
+ ) + } + + return inputComponent } } ConnectedInput.propTypes = { name: PropTypes.string.isRequired, onChange: PropTypes.func, + modifiers: PropTypes.array, } ConnectedInput.defaultProps = { + modifiers: [], onChange: () => {}, } diff --git a/src/components/Inputs/SelectInput/SelectInput.jsx b/src/components/Inputs/SelectInput/SelectInput.jsx new file mode 100644 index 000000000..91c47d3d4 --- /dev/null +++ b/src/components/Inputs/SelectInput/SelectInput.jsx @@ -0,0 +1,78 @@ +import React from 'react' +import PropTypes from 'prop-types' +import classnames from 'classnames' + +const SelectInput = (props) => { + const { + uid, name, value, error, valid, disabled, className, + label, options, optional, onChange, + } = props + + const wrapperClasses = classnames( + className, + { 'usa-input-error': error } + ) + + const selectClasses = classnames( + { 'usa-input-success': valid } + ) + + return ( +
+ + + +
+ ) +} + +SelectInput.propTypes = { + name: PropTypes.string.isRequired, + uid: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + options: PropTypes.array.isRequired, + value: PropTypes.string, + error: PropTypes.bool, + valid: PropTypes.bool, + disabled: PropTypes.bool, + className: PropTypes.string, + optional: PropTypes.bool, + onChange: PropTypes.func, +} + +SelectInput.defaultProps = { + value: '', + error: false, + valid: false, + disabled: false, + className: '', + optional: false, + onChange: () => {}, +} + +export default SelectInput diff --git a/src/components/Inputs/TextInput/TextInput.jsx b/src/components/Inputs/TextInput/TextInput.jsx index 5155b1d76..0548406bb 100644 --- a/src/components/Inputs/TextInput/TextInput.jsx +++ b/src/components/Inputs/TextInput/TextInput.jsx @@ -1,9 +1,16 @@ +/** + * Input requirements + * Generic text input (text, email, number, password) +*/ import React from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' import { autotab } from 'components/Form/Generic' +// TODO +// import { getValidationPropsFromModel } from './modelValidations' + const TextInput = (props) => { const { label, uid, name, type, placeholder, ariaLabel, disabled, error, maxlength, @@ -26,12 +33,16 @@ const TextInput = (props) => { const inputClass = classnames({ 'usa-input-focus': !disabled && focus, 'usa-input-success': !disabled && valid, - }) + }, className) const handleKeyDown = (e) => { autotab(e, maxlength, tabBack, tabNext) } + const allowClipboard = (e) => { + if (!clipboard) e.preventDefault() + } + return (
{label && ( @@ -58,14 +69,57 @@ const TextInput = (props) => { onFocus={onFocus} onBlur={onBlur} onKeyDown={handleKeyDown} + onCopy={allowClipboard} + onCut={allowClipboard} + onPaste={allowClipboard} /> + {/* for print CSS */}
{value}
) } TextInput.propTypes = { + name: PropTypes.string.isRequired, + uid: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + ariaLabel: PropTypes.string, + type: PropTypes.oneOf(['text', 'email', 'number', 'password']), + value: PropTypes.string, + placeholder: PropTypes.string, + error: PropTypes.bool, + valid: PropTypes.bool, + disabled: PropTypes.bool, + className: PropTypes.string, + clipboard: PropTypes.bool, + spellcheck: PropTypes.bool, + autocapitalize: PropTypes.bool, + autocorrect: PropTypes.bool, + autocomplete: PropTypes.bool, + readonly: PropTypes.bool, + onChange: PropTypes.func, + onFocus: PropTypes.func, + onBlur: PropTypes.func, +} +TextInput.defaultProps = { + ariaLabel: null, + type: 'text', + value: '', + placeholder: '', + error: false, + valid: false, + disabled: false, + className: '', + clipboard: true, + spellcheck: true, + autocapitalize: true, + autocorrect: true, + autocomplete: true, + readonly: false, + onChange: () => {}, + onFocus: () => {}, + onBlur: () => {}, } export default TextInput diff --git a/src/components/Inputs/TextInput/TextInput.stories.jsx b/src/components/Inputs/TextInput/TextInput.stories.jsx new file mode 100644 index 000000000..c3293b5d8 --- /dev/null +++ b/src/components/Inputs/TextInput/TextInput.stories.jsx @@ -0,0 +1,55 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import React from 'react' +import { storiesOf } from '@storybook/react' + +import TextInput from './TextInput' +import { ConnectedTextInput } from '../connectedInputs' + +storiesOf('Input', module) + .add('default', () => ( + + )) + .add('with a value', () => ( + + )) + .add('with FormField', () => ( + + )) + .add('with FormField and errors', () => ( + + )) + .add('with FormField and help text', () => ( + + )) + .add('with FormField and errors and help text', () => ( + + )) From 267be84d81122748bc3a34cc0e7f7e8eea1107dd Mon Sep 17 00:00:00 2001 From: Suzanne Rozier Date: Tue, 17 Sep 2019 13:20:04 -0400 Subject: [PATCH 3/7] Hook up NameFieldset to ApplicantName --- .../Fieldsets/NameFieldset/NameFieldset.jsx | 175 ++++++++++++++++++ .../NameFieldset/NameFieldset.stories.jsx | 34 ++++ src/components/Form/Name/Name.jsx | 23 --- .../Inputs/CheckboxInput/CheckboxInput.jsx | 9 +- src/components/Inputs/InputConnector.jsx | 5 +- .../Inputs/SelectInput/SelectInput.jsx | 6 +- src/components/Inputs/TextInput/TextInput.jsx | 6 +- src/components/Inputs/connectedInputs.js | 15 +- .../ApplicantName/ApplicantName.jsx | 13 ++ src/helpers/validation.js | 61 ++++++ 10 files changed, 316 insertions(+), 31 deletions(-) create mode 100644 src/components/Fieldsets/NameFieldset/NameFieldset.jsx create mode 100644 src/components/Fieldsets/NameFieldset/NameFieldset.stories.jsx diff --git a/src/components/Fieldsets/NameFieldset/NameFieldset.jsx b/src/components/Fieldsets/NameFieldset/NameFieldset.jsx new file mode 100644 index 000000000..184016eb3 --- /dev/null +++ b/src/components/Fieldsets/NameFieldset/NameFieldset.jsx @@ -0,0 +1,175 @@ +import React from 'react' +import PropTypes from 'prop-types' +import classnames from 'classnames' + +import i18n from 'util/i18n' + +import { + ConnectedTextFormField, ConnectedSelectFormField, ConnectedCheckboxInput, +} from 'components/Inputs/connectedInputs' + +import nameModel from 'models/shared/name' +import { getEffectiveModel } from 'helpers/validation' + +const NameFieldset = (props) => { + const { + value, title, className, disabled, required, prefix, onUpdate, + hideMiddleName, errors, + } = props + + const classes = classnames('name', className, { disabled }) + + const suffixOptions = [ + { label: '', value: '' }, + { label: i18n.t(`${prefix}.label.jr`), value: 'Jr' }, + { label: i18n.t(`${prefix}.label.sr`), value: 'Sr' }, + { label: i18n.t(`${prefix}.label.i`), value: 'I' }, + { label: i18n.t(`${prefix}.label.ii`), value: 'II' }, + { label: i18n.t(`${prefix}.label.iii`), value: 'III' }, + { label: i18n.t(`${prefix}.label.iv`), value: 'IV' }, + { label: i18n.t(`${prefix}.label.v`), value: 'V' }, + { label: i18n.t(`${prefix}.label.vi`), value: 'VI' }, + { label: i18n.t(`${prefix}.label.vii`), value: 'VII' }, + { label: i18n.t(`${prefix}.label.viii`), value: 'VIII' }, + { label: i18n.t(`${prefix}.label.ix`), value: 'IX' }, + { label: i18n.t(`${prefix}.label.x`), value: 'X' }, + { label: i18n.t(`${prefix}.label.other`), value: 'Other' }, + ] + + const onChange = (name, newValue) => { + console.log('on change', name, newValue) + onUpdate({ + ...value, + [`${name}`]: newValue, + }) + } + + const { + first, middle, last, firstInitialOnly, middleInitialOnly, noMiddleName, + suffix, suffixOther, + } = value + + const inputProps = { + disabled, + required, + onChange, + } + + const effectiveNameModel = getEffectiveModel(nameModel, value) + + const firstInitialOnlyCheckbox = ( +
+ +
+ ) + + const middleInitialOnlyCheckbox = ( +
+ +
+ ) + + const noMiddleNameCheckbox = ( +
+ +
+ ) + + return ( +
+ {title && {title}} + + e.indexOf('first') > -1)} + /> + + {!hideMiddleName && ( + e.indexOf('middle') > -1)} + disabled={noMiddleName} + modifiers={( + + {noMiddleNameCheckbox} + {middleInitialOnlyCheckbox} + + )} + /> + )} + + e.indexOf('last') > -1)} + /> + + + + {suffix === 'Other' && ( + + )} + +
+ ) +} + +NameFieldset.propTypes = { + value: PropTypes.object, + prefix: PropTypes.string, + onUpdate: PropTypes.func, + errors: PropTypes.array, +} + +NameFieldset.defaultProps = { + value: {}, + prefix: 'name', + onUpdate: () => {}, + errors: [], +} + +export default NameFieldset diff --git a/src/components/Fieldsets/NameFieldset/NameFieldset.stories.jsx b/src/components/Fieldsets/NameFieldset/NameFieldset.stories.jsx new file mode 100644 index 000000000..745720dda --- /dev/null +++ b/src/components/Fieldsets/NameFieldset/NameFieldset.stories.jsx @@ -0,0 +1,34 @@ +/* eslint import/no-extraneous-dependencies: 0 */ + +import React from 'react' +import { storiesOf } from '@storybook/react' + +import NameFieldset from './NameFieldset' + +storiesOf('NameFieldset', module) + .add('default', () => ( + + )) + .add('with a value', () => ( + + )) + .add('with a title', () => ( + + )) diff --git a/src/components/Form/Name/Name.jsx b/src/components/Form/Name/Name.jsx index 5bceaa634..b43bb8f4f 100644 --- a/src/components/Form/Name/Name.jsx +++ b/src/components/Form/Name/Name.jsx @@ -1,9 +1,6 @@ import React from 'react' import { i18n } from 'config' -import FormField from 'components/Inputs/FormField/FormField' -import { ConnectedTextInput } from 'components/Inputs/connectedInputs' - import ValidationElement from '../ValidationElement' import Field from '../Field' import Show from '../Show' @@ -145,26 +142,6 @@ export default class Name extends ValidationElement {
{this.props.title &&

{this.props.title}

} - { - this.update({ - first: values.value, - }) - }} - onFocus={this.props.onFocus} - onBlur={this.props.onBlur} - required={this.props.required} - disabled={this.props.disabled} - /> - { ) const inputClass = classnames({ - 'usa-input-success': !disabled && valid, + 'usa-input-success': valid, }) + const handleChange = (e) => { + console.log('checkbox handle change', e) + onChange(e.target.checked) + } + return (
{ aria-label={ariaLabel || label} disabled={disabled} readOnly={readonly} - onChange={onChange} + onChange={handleChange} onFocus={onFocus} onBlur={onBlur} /> diff --git a/src/components/Inputs/InputConnector.jsx b/src/components/Inputs/InputConnector.jsx index d14f7059b..4e42eedb7 100644 --- a/src/components/Inputs/InputConnector.jsx +++ b/src/components/Inputs/InputConnector.jsx @@ -26,10 +26,9 @@ const connectInput = (Component, renderFormField = true) => { this.setState({ showHelp: !showHelp }) } - handleChange = (event) => { + handleChange = (value) => { const { name, onChange } = this.props - const { value } = event.target - onChange({ value, name }) + onChange(name, value) } handleFocus = () => { diff --git a/src/components/Inputs/SelectInput/SelectInput.jsx b/src/components/Inputs/SelectInput/SelectInput.jsx index 91c47d3d4..6b9a08500 100644 --- a/src/components/Inputs/SelectInput/SelectInput.jsx +++ b/src/components/Inputs/SelectInput/SelectInput.jsx @@ -17,6 +17,10 @@ const SelectInput = (props) => { { 'usa-input-success': valid } ) + const handleChange = (e) => { + onChange(e.target.value) + } + return (