|

   Veteran Tools Platform - Developer Documentation

There are no notes for this item.

PropRequired?TypeDefaultDescription
errorMessage No string display error message for input that indicates a validation error
label Yes any label for input field
placeholder No string text displayed when input has no user provided value
name No string input name attribute
autocomplete No string input autocomplete attribute
required No bool render marker indicating field is required
field Yes {
  value: string,
  dirty: bool }
value of the input field and if its dirty status
additionalClass No string extra attribute for use by CSS selector, specifically by tests
charMax No number maximum permitted input length
onValueChange Yes func called when input value is changed
type No string 'text' type attribute for input field
<div id="reactMount" data-tpl="errorabletextinput">
    <div class="usa-input-error"><label class="usa-input-error-label" for="errorable-text-input-41">Label</label><span class="usa-input-error-message" role="alert" id="errorable-text-input-41-error-message"><span class="sr-only">Error</span> Error message</span><input type="text"
            aria-describedby="errorable-text-input-41-error-message" id="errorable-text-input-41" placeholder="Placeholder" name="Name" maxlength="255" /></div>
</div>
<script>
    window.currentProps = {
        "package": {
            "name": "department-of-veteran-affairs/jean-pants",
            "version": "0.1.0"
        },
        "assetPath": "/design-system/",
        "isProduction": true,
        "componentSourcePath": "./ErrorableTextInput.jsx",
        "errorMessage": "Error message",
        "label": "Label",
        "placeholder": "Placeholder",
        "name": "Name",
        "field": {
            "dirty": null
        },
        "charMax": 255
    }
</script>
import React from 'react';
import ErrorableTextInput from './ErrorableTextInput';

export default function ErrorableTextInputExample(props) {
  return (
          <ErrorableTextInput
            onValueChange={(e) => e}
            {...props}/>
  );
}
package:
  name: department-of-veteran-affairs/jean-pants
  version: 0.1.0
assetPath: /design-system/
isProduction: true
componentSourcePath: ./ErrorableTextInput.jsx
errorMessage: Error message
label: Label
placeholder: Placeholder
name: Name
field:
  dirty: null
charMax: 255
  • Content:
    import PropTypes from 'prop-types';
    import React from 'react';
    import _ from 'lodash';
    import ToolTip from '../../../Tooltip/Tooltip';
    import { makeField } from '../../../../helpers/fields';
    
    /**
     * A form input with a label that can display error messages.
     *
     * Props:
     * `errorMessage` - Error string to display in the component.
     *                  When defined, indicates input has a validation error.
     * `label` - String for the input field label.
     * `name` - String for the input name attribute.
     * `toolTipText` - String with help text for user.
     * `tabIndex` - Number for keyboard tab order.
     * `autocomplete` - String for the input autocomplete attribute.
     * `placeholder` - placeholder string for input field.
     * `required` - boolean. Render marker indicating field is required.
     * `field` - string. Value of the input field.
     * `additionalClass` - Extra attribute for use by CSS selector, specifically
     *                     by tests
     * `onValueChange` - a function with this prototype: (newValue)
     */
    class ErrorableTextInput extends React.Component {
      constructor() {
        super();
        this.handleChange = this.handleChange.bind(this);
        this.handleBlur = this.handleBlur.bind(this);
      }
    
      componentWillMount() {
        this.inputId = _.uniqueId('errorable-text-input-');
      }
    
      handleChange(domEvent) {
        this.props.onValueChange(
          makeField(domEvent.target.value, this.props.field.dirty)
        );
      }
    
      handleBlur() {
        this.props.onValueChange(makeField(this.props.field.value, true));
      }
    
      render() {
        // Calculate error state.
        let errorSpan = '';
        let maxCharacters;
        let errorSpanId = undefined;
        let inputErrorClass = undefined;
        let labelErrorClass = undefined;
        if (this.props.errorMessage) {
          errorSpanId = `${this.inputId}-error-message`;
          errorSpan = (
            <span className="usa-input-error-message" role="alert" id={errorSpanId}>
              <span className="sr-only">Error</span> {this.props.errorMessage}
            </span>
          );
          inputErrorClass = 'usa-input-error';
          labelErrorClass = 'usa-input-error-label';
        }
    
        // Addes ToolTip if text is provided.
        let toolTip;
        if (this.props.toolTipText) {
          toolTip = (
            <ToolTip
              tabIndex={this.props.tabIndex}
              toolTipText={this.props.toolTipText}/>
          );
        }
    
        // Calculate max characters and display '(Max. XX characters)' when max is hit.
        if (this.props.field.value) {
          if (this.props.charMax === this.props.field.value.length) {
            maxCharacters = <small>(Max. {this.props.charMax} characters)</small>;
          }
        }
    
        // Calculate required.
        let requiredSpan = undefined;
        if (this.props.required) {
          requiredSpan = <span className="form-required-span">*</span>;
        }
    
        return (
          <div className={inputErrorClass}>
            <label className={labelErrorClass} htmlFor={this.inputId}>
              {this.props.label}
              {requiredSpan}
            </label>
            {errorSpan}
            <input
              className={this.props.additionalClass}
              aria-describedby={errorSpanId}
              id={this.inputId}
              placeholder={this.props.placeholder}
              name={this.props.name}
              tabIndex={this.props.tabIndex}
              autoComplete={this.props.autocomplete}
              type={this.props.type}
              maxLength={this.props.charMax}
              value={this.props.field.value}
              onChange={this.handleChange}
              onBlur={this.handleBlur}/>
            {maxCharacters}
            {toolTip}
          </div>
        );
      }
    }
    
    ErrorableTextInput.propTypes = {
      /**
       * display error message for input that indicates a validation error
       */
      errorMessage: PropTypes.string,
      /**
       * label for input field
       */
      label: PropTypes.any.isRequired,
      /**
       * text displayed when input has no user provided value
       */
      placeholder: PropTypes.string,
      /**
       * input name attribute
       */
      name: PropTypes.string,
      /**
       * input autocomplete attribute
       */
      autocomplete: PropTypes.string,
      /**
       * render marker indicating field is required
       */
      required: PropTypes.bool,
      /**
       * value of the input field and if its dirty status
       */
      field: PropTypes.shape({
        value: PropTypes.string,
        dirty: PropTypes.bool
      }).isRequired,
      /**
       * extra attribute for use by CSS selector, specifically by tests
       */
      additionalClass: PropTypes.string,
      /**
       * maximum permitted input length
       */
      charMax: PropTypes.number,
      /**
       * called when input value is changed
       */
      onValueChange: PropTypes.func.isRequired,
      /**
       * type attribute for input field
       */
      type: PropTypes.string
    };
    
    ErrorableTextInput.defaultProps = {
      type: 'text'
    };
    
    export default ErrorableTextInput;
    
  • URL: /components/raw/errorabletextinput/ErrorableTextInput.jsx
  • Filesystem Path: src/components/form/controls/ErrorableTextInput/ErrorableTextInput.jsx
  • Size: 4.7 KB
  • Content:
    import React from 'react';
    import {
      shallow,
      mount
    } from 'enzyme';
    import { axeCheck } from '../../../../../lib/testing/helpers';
    import { expect } from 'chai';
    import ErrorableTextInput from './ErrorableTextInput.jsx';
    import { makeField } from '../../../../helpers/fields.js';
    
    describe('<ErrorableTextInput>', () => {
      it('calls onValueChange with input value and dirty state', () => {
        let valueChanged;
        // shallowly render component with callback that alters valueChanged with passed argument
        const wrapper = mount(<ErrorableTextInput
          field={makeField('')}
          label="test"
          onValueChange={(value) => { valueChanged = value; }}/>);
    
        wrapper.find('input').first().simulate('change', { target: { value: 'hello' } });
        expect(valueChanged.value).to.eql('hello');
        expect(valueChanged.dirty).to.eql(false);
      });
    
      it('calls onValueChange with dirty state on blur', () => {
        let valueChanged;
        // shallowly render component with callback that alters valueChanged with passed argument
        const wrapper = mount(<ErrorableTextInput
          field={makeField('')}
          label="test"
          onValueChange={(value) => { valueChanged = value; }}/>);
    
        wrapper.find('input').first().simulate('blur');
        expect(valueChanged.dirty).to.eql(true);
      });
    
      it('renders a label and a placeholder', () => {
        const wrapper = shallow(<ErrorableTextInput
          field={makeField('')}
          label="test"
          placeholder="Placeholder"
          onValueChange={(value) => value}/>);
    
        const label = wrapper.find('label');
        const input = wrapper.find('input');
    
        expect(label.first().text()).to.eql('test');
        expect(input.first().prop('placeholder')).to.eql('Placeholder');
    
      });
    
      it('renders error styling when errorMessage attribute is present', () => {
        const wrapper = shallow(<ErrorableTextInput
          field={makeField('')}
          label="test"
          errorMessage="errorMessage"
          onValueChange={(value) => value}/>);
    
        const errorStyles = [
          '.usa-input-error-label',
          '.usa-input-error',
          '.usa-input-error-message'
        ];
        // assert that each error style corresponds to one component
        errorStyles.forEach((style) =>
          expect(wrapper.find(style)).to.have.lengthOf(1));
      });
    
      it('renders aria-describedby attribute when errorMessage attribute is present', () => {
        const wrapper = shallow(<ErrorableTextInput
          field={makeField('')}
          label="test"
          errorMessage="errorMessage"
          onValueChange={(value) => value}/>);
    
        const input = wrapper.find('input');
        const errorMessageId = wrapper.find('.usa-input-error-message').first().prop('id');
        expect(input.prop('aria-describedby')).to.eql(errorMessageId);
      });
    
      it('renders an error message when errorMessage attribute is present', () => {
        const wrapper = shallow(<ErrorableTextInput
          field={makeField('')}
          label="test"
          errorMessage="errorMessage"
          onValueChange={(value) => value}/>);
    
        const errorMessage = wrapper.find('.usa-input-error-message');
        expect(errorMessage).to.have.lengthOf(1);
        expect(errorMessage.text()).to.eql('Error errorMessage');
      });
    
      it('renders no error styling when errorMessage attribute is not present', () => {
        const wrapper = shallow(<ErrorableTextInput
          field={makeField('')}
          label="test"
          onValueChange={(value) => value}/>);
    
        const errorStyles = [
          '.usa-input-error-label',
          '.usa-input-error',
          '.usa-input-error-message'
        ];
        // assert that each error style corresponds to one component
        errorStyles.forEach((style) =>
          expect(wrapper.find(style)).to.have.lengthOf(0));
      });
    
      it('renders no aria-describedby attribute when errorMessage attribute is not present', () => {
        const wrapper = shallow(<ErrorableTextInput
          field={makeField('')}
          label="test"
          onValueChange={(value) => value}/>);
    
        expect(wrapper.find('input').prop('aria-describedby')).to.not.exist;
      });
    
      it('renders no error message when errorMessage attribute is not present', () => {
        const wrapper = shallow(<ErrorableTextInput
          field={makeField('')}
          label="test"
          onValueChange={(value) => value}/>);
    
        expect(wrapper.find('.usa-input-error-message')).to.have.lengthOf(0);
      });
    
      it('renders a required asterick when required is true', () => {
        const wrapper = shallow(<ErrorableTextInput
          field={makeField('')}
          label="test"
          required
          onValueChange={(value) => value}/>);
    
        expect(wrapper.find('label').text()).to.eql('test*');
      });
    
      it('renders no required asterick when required is false', () => {
        const wrapper = shallow(<ErrorableTextInput
          field={makeField('')}
          label="test"
          onValueChange={(value) => value}/>);
    
        expect(wrapper.find('label').text()).to.eql('test');
      });
    
      it('renders the input id as label\'s for attribute value', () => {
        const wrapper = shallow(<ErrorableTextInput
          field={makeField('')}
          label="test"
          onValueChange={(value) => value}/>);
    
        const inputId = wrapper.find('input').first().prop('id');
        const labelFor = wrapper.find('label').first().prop('htmlFor');
        expect(inputId).to.eql(labelFor);
      });
    
    
      it('passes aXe check when no error present', () => {
        const check = axeCheck(<ErrorableTextInput
          field={makeField('')}
          label="test"
          placeholder="Placeholder"
          onValueChange={(value) => value}/>);
    
        return check;
      });
    
      it('passes aXe check when error present', () => {
        const check = axeCheck(<ErrorableTextInput
          field={makeField('')}
          label="test"
          placeholder="Placeholder"
          errorMessage="error"
          onValueChange={(value) => value}/>);
    
        return check;
      });
    });
    
  • URL: /components/raw/errorabletextinput/ErrorableTextInput.unit.spec.jsx
  • Filesystem Path: src/components/form/controls/ErrorableTextInput/ErrorableTextInput.unit.spec.jsx
  • Size: 5.8 KB
  • Handle: @errorabletextinput
  • Preview:
  • Filesystem Path: src/components/form/controls/ErrorableTextInput/ErrorableTextInput.njk