WikiEducationFoundation/WikiEduDashboard

View on GitHub
app/assets/javascripts/components/common/text_input.jsx

Summary

Maintainability
A
2 hrs
Test Coverage
A
95%
import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import InputHOC from '../high_order/input_hoc.jsx';
import Conditional from '../high_order/conditional.jsx';

const TextInput = ({
  value,
  value_key,
  editable,
  label,
  placeholder,
  spacer,
  valueClass,
  p_tag_classname,
  inline,
  type,
  max,
  maxLength,
  focus,
  onBlur,
  onClick,
  append,
  onKeyDown,
  _value,
  invalid,
  onChange,
  onFocus,
  id,
  children
}) => {
  const inputRef = useRef(null);

  const onKeyDownHandler = (e) => {
    if (!onKeyDown) return;
    onKeyDown(e.keyCode, inputRef.current);
  };

  const labelContent = label ? `${label}${spacer || ': '}` : undefined;

  const usedValueClass = `text-input-component__value ${valueClass ?? ''}`;

  if (editable) {
    const labelClass = invalid ? 'red' : '';
    const inputClass = `${inline ? 'inline' : ''} ${invalid ? 'invalid' : ''}`;

    let title;
    if (type === 'number') {
      title = I18n.t('accessibility.number_field');
    }

    const className = `${inputClass} ${value_key}`;

    // The default maximum length of 75 ensures that the slug field
    // of a course, which combines three TextInput values, will not exceed
    // the maximum string size of 255.
    const usedMaxLength = maxLength || '75';
    const inputElement = (
      <input
        id={id}
        className={className}
        value={_value ?? (value || '')}
        onChange={onChange}
        autoFocus={focus}
        onFocus={onFocus}
        onBlur={onBlur}
        onKeyDown={onKeyDownHandler}
        type={type || 'text'}
        max={max}
        maxLength={usedMaxLength}
        placeholder={placeholder}
        title={title}
        min={0}
        ref={inputRef}
        aria-labelledby={`${id}-label`}
      />
    );

    return (
      <div className="form-group">
        <label id={`${id}-label`} htmlFor={id} className={labelClass}>
          {labelContent}
        </label>
        {inputElement}
        {children}
      </div>
    );
  } else if (label) {
    return (
      <p className={p_tag_classname}>
        <span className="text-input-component__label">
          <strong>{labelContent}</strong>
        </span>
        <span onBlur={onBlur} onClick={onClick} className={usedValueClass}>
          {value}
        </span>
        {append}
      </p>
    );
  }
  return <span>{value}</span>;
};

TextInput.propTypes = {
  value: PropTypes.any,
  value_key: PropTypes.string,
  editable: PropTypes.bool,
  label: PropTypes.string,
  placeholder: PropTypes.string,
  spacer: PropTypes.string,
  valueClass: PropTypes.string,
  p_tag_classname: PropTypes.string,
  inline: PropTypes.bool,
  type: PropTypes.string,
  max: PropTypes.string,
  maxLength: PropTypes.string,
  focus: PropTypes.func,
  onBlur: PropTypes.func,
  onClick: PropTypes.func,
  append: PropTypes.node,
  onKeyDown: PropTypes.func,
  _value: PropTypes.any,
  // validation: Regex used by Conditional
  // required: bool used by Conditional
};

export default Conditional(InputHOC(TextInput));