Passed
Push — feature/skill-step-functional ( 0739b3 )
by Tristan
06:05
created

resources/assets/js/components/TextArea.tsx   A

Complexity

Total Complexity 2
Complexity/F 0

Size

Lines of Code 134
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 2
eloc 102
mnd 2
bc 2
fnc 0
dl 0
loc 134
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import React, { ReactElement } from "react";
2
import { FormattedMessage, useIntl, defineMessages } from "react-intl";
3
import { inputMessages } from "./Form/Messages";
4
import WordCounter from "./WordCounter/WordCounter";
5
import SimpleWordCounter from "./WordCounter/SimpleWordCounter";
6
7
export interface TextAreaProps {
8
  /** HTML id of the input element */
9
  id: string;
10
  /** HTML name of the input element */
11
  name: string;
12
  /** Holds text for label associated with input element */
13
  label: string;
14
  /** Custom class for the wrapper div */
15
  className?: string;
16
  /** boolean indicating if input must have a value, or not */
17
  required?: boolean;
18
  /** Holds message for right hand side, after required warning */
19
  rightMessage?: string | ReactElement;
20
  /** Boolean that sets the select input to invalid */
21
  invalid?: boolean | null;
22
  /** Let's you specify example text that appears in input element when empty */
23
  placeholder?: string;
24
  /** Minimum length of characters the text value can be */
25
  minLength?: number;
26
  /** Maximum length of characters the text value can be */
27
  maxLength?: number;
28
  /** The value of the input */
29
  value?: string | number | string[];
30
  /** Error text that appers underneath if error occurs (eg. required) */
31
  errorText?: string;
32
  /** data-clone-grid-item value: https://designwithclone.ca/#flexbox-grid */
33
  grid?: string;
34
  /** Event listener which fires when a change event occurs (varies on input type) */
35
  onChange: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
36
  /** Event listener which fires when a input loses focus */
37
  onBlur?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
38
  /** The maximum word count. If set, adds a word counter. */
39
  wordLimit?: number;
40
}
41
42
const messages = defineMessages({
43
  underLimit: {
44
    id: "textArea.wordsUnderLimit",
45
    defaultMessage: "words left",
46
    description:
47
      "Message displayed on word counter when user is under/matching the limit.",
48
  },
49
  overLimit: {
50
    id: "textArea.wordsOverLimit",
51
    defaultMessage: "words over limit",
52
    description:
53
      "Message displayed on word counter when user passes the limit.",
54
  },
55
});
56
57
const TextArea: React.FunctionComponent<TextAreaProps> = ({
58
  id,
59
  name,
60
  label,
61
  className,
62
  required,
63
  rightMessage,
64
  invalid,
65
  placeholder,
66
  value,
67
  errorText,
68
  grid,
69
  minLength,
70
  maxLength,
71
  onChange,
72
  onBlur,
73
  wordLimit,
74
}): React.ReactElement => {
75
  const intl = useIntl();
76
  const valueString = typeof value === "string" ? value : "";
77
  const wordCounter = wordLimit ? (
78
    <span data-c-color="black">
79
      <SimpleWordCounter
80
        wordLimit={wordLimit}
81
        value={valueString}
82
        absoluteValue
83
        beforeText="( "
84
        underMaxMessage={`${intl.formatMessage(messages.underLimit)} )`}
85
        overMaxMessage={`${intl.formatMessage(messages.overLimit)} )`}
86
      />
87
    </span>
88
  ) : null;
89
90
  return (
91
    <div
92
      className={className}
93
      data-c-grid-item={grid}
94
      data-c-input="textarea"
95
      data-c-required={required || null}
96
      data-c-invalid={invalid || null}
97
    >
98
      <label htmlFor={id}>{label}</label>
99
      {required && (
100
        <span>
101
          <FormattedMessage {...inputMessages.required} /> {rightMessage}
102
          {wordCounter}
103
        </span>
104
      )}
105
      {/** rightMessage and wordCounter are repeated because if this input is not required,
106
       *   the previous span will not appear - and it is required, they must be part of the
107
       *   previous span to appear in the right place.
108
       */}
109
      {!required && (
110
        <span style={{ display: "block" }}>
111
          {rightMessage}
112
          {wordCounter}
113
        </span>
114
      )}
115
      <div>
116
        <textarea
117
          id={id}
118
          name={name}
119
          required={required}
120
          placeholder={placeholder}
121
          minLength={minLength}
122
          maxLength={maxLength}
123
          onChange={onChange}
124
          onBlur={onBlur}
125
          value={value}
126
        />
127
      </div>
128
      <span>{errorText || <FormattedMessage {...inputMessages.error} />}</span>
129
    </div>
130
  );
131
};
132
133
export default TextArea;
134