|

   Veteran Tools Platform - Developer Documentation

There are no notes for this item.

PropRequired?TypeDefaultDescription
errorMessage No string
field Yes {
  value: string,
  dirty: bool }
label Yes string
name No string
min No union
max No union
pattern No string
placeholder No string
required No bool
onValueChange Yes func handler for the value change with this prototype: (newValue)
additionalClass No string
<div id="reactMount" data-tpl="errorablenumberinput">
    <div class="usa-input-error"><label class="usa-input-error-label" for="errorable-number-input-38">Please pick a number</label><span class="usa-input-error-message" role="alert" id="errorable-number-input-38-error-message"><span class="sr-only">Error</span> Error message</span><input
            type="number" min="1" max="10" aria-describedby="errorable-number-input-38-error-message" id="errorable-number-input-38" name="Name" placeholder="Numbers are between 1 and 10" /></div>
</div>
<script>
    window.currentProps = {
        "package": {
            "name": "department-of-veteran-affairs/jean-pants",
            "version": "0.1.0"
        },
        "assetPath": "/design-system/",
        "isProduction": true,
        "componentSourcePath": "./ErrorableNumberInput.jsx",
        "errorMessage": "Error message",
        "label": "Please pick a number",
        "placeholder": "Numbers are between 1 and 10",
        "name": "Name",
        "min": 1,
        "max": 10,
        "field": {
            "dirty": null
        }
    }
</script>
import React from 'react';
import ErrorableNumberInput from './ErrorableNumberInput';

export default function ErrorableNumberInputExample(props) {
  return (
          <ErrorableNumberInput
            onValueChange={(e) => e}
            {...props}/>
  );
}
package:
  name: department-of-veteran-affairs/jean-pants
  version: 0.1.0
assetPath: /design-system/
isProduction: true
componentSourcePath: ./ErrorableNumberInput.jsx
errorMessage: Error message
label: Please pick a number
placeholder: Numbers are between 1 and 10
name: Name
min: 1
max: 10
field:
  dirty: null
  • Content:
    import PropTypes from 'prop-types';
    import React from 'react';
    import _ from 'lodash';
    
    import { makeField } from '../../../../helpers/fields';
    
    /*
     * A form input with a label that can display error messages.
     *
     */
    
    class ErrorableNumberInput extends React.Component {
      constructor() {
        super();
        this.handleChange = this.handleChange.bind(this);
        this.handleBlur = this.handleBlur.bind(this);
      }
    
      componentWillMount() {
        this.inputId = _.uniqueId('errorable-number-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 errorSpanId = undefined;
    
        // TODO: Look into an alternate way of adding error styling not based on presence of errorMessage:
        // There could be cases where there is an error but we don’t want a message to appear, and this
        // is not clear right now
        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>
          );
        }
    
        // Calculate required.
        let requiredSpan = undefined;
        if (this.props.required) {
          requiredSpan = <span className="form-required-span">*</span>;
        }
    
        return (
          <div className={this.props.errorMessage ? 'usa-input-error' : undefined}>
            <label
              className={this.props.errorMessage !== undefined ? 'usa-input-error-label' : undefined}
              htmlFor={this.inputId}>
              {this.props.label}
              {requiredSpan}
            </label>
            {errorSpan}
            <input
              autoComplete={this.props.autocomplete}
              className={this.props.additionalClass}
              aria-describedby={errorSpanId}
              id={this.inputId}
              name={this.props.name}
              max={this.props.max}
              min={this.props.min}
              pattern={this.props.pattern}
              placeholder={this.props.placeholder}
              type="number"
              value={this.props.field.value}
              onChange={this.handleChange}
              onBlur={this.handleBlur}/>
          </div>
        );
      }
    }
    
    ErrorableNumberInput.propTypes = {
      /* Error string to display in the component. When defined, indicates input has a validation error. */
      errorMessage: PropTypes.string,
      field: PropTypes.shape({
        value: PropTypes.string,
        dirty: PropTypes.bool
      }).isRequired,
      /* `label` - String for the input field label. */
      label: PropTypes.string.isRequired,
      /* `name` - String for name attribute. */
      name: PropTypes.string,
      /* minimum number value and maximum of same */
      min: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number
      ]),
      max: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number
      ]),
      /* String specifying the pattern for the input. */
      pattern: PropTypes.string,
      /* placeholder string for input field. */
      placeholder: PropTypes.string,
      /* Render marker indicating field is required. */
      required: PropTypes.bool,
      /**
       * handler for the value change with this prototype: (newValue)
       */
      onValueChange: PropTypes.func.isRequired,
      additionalClass: PropTypes.string
    };
    
    export default ErrorableNumberInput;
    
  • URL: /components/raw/errorablenumberinput/ErrorableNumberInput.jsx
  • Filesystem Path: src/components/form/controls/ErrorableNumberInput/ErrorableNumberInput.jsx
  • Size: 3.4 KB
  • Content:
    import React from 'react';
    import { shallow } from 'enzyme';
    import { axeCheck } from '../../../../../lib/testing/helpers';
    import chaiAsPromised from 'chai-as-promised';
    import chai, { expect } from 'chai';
    
    import { makeField } from '../../../../helpers/fields.js';
    
    import ReactTestUtils from 'react-dom/test-utils';
    import ErrorableNumberInput from './ErrorableNumberInput';
    
    chai.use(chaiAsPromised);
    
    describe('<ErrorableNumberInput>', () => {
      const testValue = makeField('');
    
      it('ensure value changes propagate', () => {
        let errorableInput;
    
        const updatePromise = new Promise((resolve, _reject) => {
          errorableInput = ReactTestUtils.renderIntoDocument(
            <ErrorableNumberInput field={testValue} label="test" onValueChange={(update) => { resolve(update); }}/>
          );
        });
    
        const input = ReactTestUtils.findRenderedDOMComponentWithTag(errorableInput, 'input');
        input.value = '1';
        ReactTestUtils.Simulate.change(input);
    
        return expect(updatePromise).to.eventually.eql(makeField('1', false));
      });
    
      it('ensure blur makes field dirty', () => {
        let errorableInput;
    
        const updatePromise = new Promise((resolve, _reject) => {
          errorableInput = ReactTestUtils.renderIntoDocument(
            <ErrorableNumberInput field={testValue} label="test" onValueChange={(update) => { resolve(update); }}/>
          );
        });
    
        const input = ReactTestUtils.findRenderedDOMComponentWithTag(errorableInput, 'input');
        ReactTestUtils.Simulate.blur(input);
    
        return expect(updatePromise).to.eventually.eql(makeField('', true));
      });
    
      it('no error styles when errorMessage undefined', () => {
        const tree = shallow(
          <ErrorableNumberInput field={testValue} label="my label" onValueChange={(_update) => {}}/>);
    
        // No error classes.
        expect(tree.find('.usa-input-error')).to.have.lengthOf(0);
        expect(tree.find('.usa-input-error-label')).to.have.lengthOf(0);
        expect(tree.find('.usa-input-error-message')).to.have.lengthOf(0);
    
        // Ensure no unnecessary class names on label w/o error..
        const labels = tree.find('label');
        expect(labels).to.have.lengthOf(1);
        expect(labels.hasClass('foo')).to.be.false;
    
        // No error means no aria-describedby to not confuse screen readers.
        const inputs = tree.find('input');
        expect(inputs).to.have.lengthOf(1);
        expect(inputs.prop('aria-describedby')).to.be.undefined;
      });
    
      it('has error styles when errorMessage is set', () => {
        const tree = shallow(
          <ErrorableNumberInput field={testValue} label="my label" errorMessage="error message" onValueChange={(_update) => {}}/>);
    
        // Ensure all error classes set.
        expect(tree.find('.usa-input-error')).to.have.lengthOf(1);
    
        const labels = tree.find('.usa-input-error-label');
        expect(labels).to.have.lengthOf(1);
        expect(labels.text()).to.equal('my label');
    
        const errorMessages = tree.find('.usa-input-error-message');
        expect(errorMessages).to.have.lengthOf(1);
        expect(errorMessages.text()).to.equal('Error error message');
    
        // No error means no aria-describedby to not confuse screen readers.
        const inputs = tree.find('input');
        expect(inputs).to.have.lengthOf(1);
        expect(inputs.prop('aria-describedby')).to.not.be.undefined;
        expect(inputs.prop('aria-describedby')).to.equal(errorMessages.prop('id'));
      });
    
      it('required=false does not have required asterisk', () => {
        const tree = shallow(
          <ErrorableNumberInput field={testValue} label="my label" onValueChange={(_update) => {}}/>);
        expect(tree.find('label').text()).to.equal('my label');
      });
    
      it('required=true has required asterisk', () => {
        const tree = shallow(
          <ErrorableNumberInput field={testValue} label="my label" required onValueChange={(_update) => {}}/>);
    
        const label = tree.find('label');
        expect(label.text()).to.equal('my label*');
      });
    
      it('label attribute propagates', () => {
        const tree = shallow(
          <ErrorableNumberInput field={testValue} label="my label" onValueChange={(_update) => {}}/>);
    
        // Ensure label text is correct.
        const labels = tree.find('label');
        expect(labels).to.have.lengthOf(1);
        expect(labels.text()).to.equal('my label');
    
        // Ensure label htmlFor is attached to input id.
        const inputs = tree.find('input');
        expect(inputs).to.have.lengthOf(1);
        expect(inputs.prop('id')).to.not.be.undefined;
        expect(inputs.prop('id')).to.equal(labels.prop('htmlFor'));
      });
    
      it('passes aXe check', () => {
        const check = axeCheck(<ErrorableNumberInput field={testValue} label="my label" onValueChange={(_update) => {}}/>);
        return check;
      });
    });
    
  • URL: /components/raw/errorablenumberinput/ErrorableNumberInput.unit.spec.jsx
  • Filesystem Path: src/components/form/controls/ErrorableNumberInput/ErrorableNumberInput.unit.spec.jsx
  • Size: 4.7 KB
  • Handle: @errorablenumberinput
  • Preview:
  • Filesystem Path: src/components/form/controls/ErrorableNumberInput/ErrorableNumberInput.njk