@availity/mui-textfield 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [1.1.2](https://github.com/Availity/element/compare/@availity/mui-textfield@1.1.1...@availity/mui-textfield@1.1.2) (2025-04-04)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **mui-textfield:** ensure helperText props are passed to helperText ([2d3de04](https://github.com/Availity/element/commit/2d3de04393bb0df18d5a8d73cad0ea688f90a0b2))
11
+
12
+ ## [1.1.1](https://github.com/Availity/element/compare/@availity/mui-textfield@1.1.0...@availity/mui-textfield@1.1.1) (2025-03-27)
13
+
5
14
  ## [1.1.0](https://github.com/Availity/element/compare/@availity/mui-textfield@1.0.1...@availity/mui-textfield@1.1.0) (2025-03-21)
6
15
 
7
16
  ### Dependency Updates
package/dist/index.d.mts CHANGED
@@ -11,7 +11,15 @@ type TextFieldProps = {
11
11
  fullWidth?: boolean;
12
12
  /** if `true`, the character counter will display. The maxLength is taken from the `inputProps.maxLength` prop. @default false */
13
13
  showCharacterCount?: boolean;
14
+ /** If `true`, the input maxLength can be exceeded. If validation is required, you'll have to do it manually. @default false */
15
+ displayOverflowMaxLength?: boolean;
14
16
  } & Pick<FormLabelProps, 'helpTopicId'> & Omit<TextFieldProps$1, 'fullWidth' | 'variant'>;
17
+ type TextFieldFormHelperTextProps = {
18
+ charCount: string;
19
+ helperText: string;
20
+ maxLength: string;
21
+ showCharacterCount: boolean;
22
+ } & FormHelperTextProps;
15
23
  declare const TextField: react.ForwardRefExoticComponent<Omit<TextFieldProps, "ref"> & react.RefAttributes<HTMLDivElement | HTMLInputElement>>;
16
24
 
17
- export { TextField, type TextFieldProps };
25
+ export { TextField, type TextFieldFormHelperTextProps, type TextFieldProps };
package/dist/index.d.ts CHANGED
@@ -11,7 +11,15 @@ type TextFieldProps = {
11
11
  fullWidth?: boolean;
12
12
  /** if `true`, the character counter will display. The maxLength is taken from the `inputProps.maxLength` prop. @default false */
13
13
  showCharacterCount?: boolean;
14
+ /** If `true`, the input maxLength can be exceeded. If validation is required, you'll have to do it manually. @default false */
15
+ displayOverflowMaxLength?: boolean;
14
16
  } & Pick<FormLabelProps, 'helpTopicId'> & Omit<TextFieldProps$1, 'fullWidth' | 'variant'>;
17
+ type TextFieldFormHelperTextProps = {
18
+ charCount: string;
19
+ helperText: string;
20
+ maxLength: string;
21
+ showCharacterCount: boolean;
22
+ } & FormHelperTextProps;
15
23
  declare const TextField: react.ForwardRefExoticComponent<Omit<TextFieldProps, "ref"> & react.RefAttributes<HTMLDivElement | HTMLInputElement>>;
16
24
 
17
- export { TextField, type TextFieldProps };
25
+ export { TextField, type TextFieldFormHelperTextProps, type TextFieldProps };
package/dist/index.js CHANGED
@@ -64,7 +64,7 @@ __export(index_exports, {
64
64
  module.exports = __toCommonJS(index_exports);
65
65
 
66
66
  // src/lib/TextField.tsx
67
- var import_react2 = require("react");
67
+ var import_react3 = require("react");
68
68
  var import_TextField = __toESM(require("@mui/material/TextField"));
69
69
  var import_mui_form_utils = require("@availity/mui-form-utils");
70
70
 
@@ -91,15 +91,50 @@ var Grid = (args) => {
91
91
  var import_Stack = __toESM(require("@mui/material/Stack"));
92
92
  var import_jsx_runtime4 = require("react/jsx-runtime");
93
93
 
94
+ // ../typography/src/lib/Typography.tsx
95
+ var import_react2 = require("react");
96
+ var import_Typography = __toESM(require("@mui/material/Typography"));
97
+ var import_jsx_runtime5 = require("react/jsx-runtime");
98
+ var Typography = (0, import_react2.forwardRef)(
99
+ (_a, ref) => {
100
+ var _b = _a, { children } = _b, rest = __objRest(_b, ["children"]);
101
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_Typography.default, __spreadProps(__spreadValues({}, rest), { ref, children }));
102
+ }
103
+ );
104
+
94
105
  // src/lib/TextField.tsx
95
106
  var import_styles = require("@mui/material/styles");
96
- var import_jsx_runtime5 = require("react/jsx-runtime");
107
+ var import_jsx_runtime6 = require("react/jsx-runtime");
97
108
  var SelectPlaceholder = (0, import_styles.styled)("span", {
98
109
  name: "MuiTextField",
99
110
  slot: "SelectPlaceholder",
100
111
  overridesResolver: (props, styles) => styles.avFilled
101
112
  })(({ theme }) => ({ opacity: 1, color: theme.palette.grey[400] }));
102
- var TextField = (0, import_react2.forwardRef)((props, ref) => {
113
+ var TextFieldFormHelperText = (_a) => {
114
+ var _b = _a, {
115
+ charCount,
116
+ helperText,
117
+ maxLength,
118
+ showCharacterCount
119
+ } = _b, FormHelperTextProps2 = __objRest(_b, [
120
+ "charCount",
121
+ "helperText",
122
+ "maxLength",
123
+ "showCharacterCount"
124
+ ]);
125
+ if (showCharacterCount) {
126
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Grid, { container: true, justifyContent: "space-between", flexWrap: "nowrap", children: [
127
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_mui_form_utils.FormHelperText, __spreadProps(__spreadValues({}, FormHelperTextProps2), { sx: { marginRight: "12px" }, children: helperText })),
128
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Typography, { variant: "caption", marginTop: "4px", lineHeight: "1.25rem", children: [
129
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Typography, { component: "span", variant: "inherit", color: charCount > maxLength ? "error" : "textPrimary", children: charCount || 0 }),
130
+ "/",
131
+ maxLength
132
+ ] })
133
+ ] });
134
+ }
135
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_mui_form_utils.FormHelperText, __spreadProps(__spreadValues({}, FormHelperTextProps2), { children: helperText }));
136
+ };
137
+ var TextField = (0, import_react3.forwardRef)((props, ref) => {
103
138
  var _b, _c, _d, _e, _f, _g, _h;
104
139
  const _a = props, {
105
140
  InputProps: InputProps2,
@@ -110,7 +145,8 @@ var TextField = (0, import_react2.forwardRef)((props, ref) => {
110
145
  SelectProps: SelectProps2,
111
146
  inputProps,
112
147
  helperText,
113
- showCharacterCount = false
148
+ showCharacterCount = false,
149
+ displayOverflowMaxLength = false
114
150
  } = _a, rest = __objRest(_a, [
115
151
  "InputProps",
116
152
  "helpTopicId",
@@ -120,34 +156,32 @@ var TextField = (0, import_react2.forwardRef)((props, ref) => {
120
156
  "SelectProps",
121
157
  "inputProps",
122
158
  "helperText",
123
- "showCharacterCount"
159
+ "showCharacterCount",
160
+ "displayOverflowMaxLength"
124
161
  ]);
125
- const [openDetected, setOpenDetected] = (0, import_react2.useState)(false);
126
- const [charCount, setCharCount] = (0, import_react2.useState)(0);
162
+ const [openDetected, setOpenDetected] = (0, import_react3.useState)(false);
163
+ const [charCount, setCharCount] = (0, import_react3.useState)(0);
164
+ const maxLength = (inputProps == null ? void 0 : inputProps.maxLength) || ((_c = (_b = rest.slotProps) == null ? void 0 : _b.htmlInput) == null ? void 0 : _c.maxLength);
127
165
  const resolvedProps = (props2) => !props2 || Object.keys(props2).length === 0 ? void 0 : props2;
128
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
166
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
129
167
  import_TextField.default,
130
168
  __spreadProps(__spreadValues({}, rest), {
131
169
  onChange: (event) => {
132
170
  setCharCount(event.target.value.length);
133
171
  if (rest.onChange) rest.onChange(event);
134
172
  },
135
- helperText: showCharacterCount ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Grid, { container: true, justifyContent: "space-between", children: [
136
- helperText,
137
- " ",
138
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { marginLeft: 4 }, children: [
139
- charCount || 0,
140
- "/",
141
- (inputProps == null ? void 0 : inputProps.maxLength) || ((_c = (_b = rest.slotProps) == null ? void 0 : _b.htmlInput) == null ? void 0 : _c.maxLength)
142
- ] })
143
- ] }) : helperText,
144
- slots: { formHelperText: import_mui_form_utils.FormHelperText },
173
+ helperText: helperText || /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, {}),
174
+ slots: { formHelperText: TextFieldFormHelperText },
145
175
  slotProps: {
146
176
  input: resolvedProps(__spreadValues(__spreadValues(__spreadValues({}, InputProps2), import_mui_form_utils.InputPropOverrides), (_d = rest.slotProps) == null ? void 0 : _d.input)),
147
- htmlInput: resolvedProps(__spreadValues(__spreadValues({ "aria-required": required }, inputProps), (_e = rest.slotProps) == null ? void 0 : _e.htmlInput)),
177
+ htmlInput: resolvedProps(__spreadProps(__spreadValues(__spreadValues({
178
+ "aria-required": required
179
+ }, inputProps), (_e = rest.slotProps) == null ? void 0 : _e.htmlInput), {
180
+ maxLength: !displayOverflowMaxLength ? maxLength : void 0
181
+ })),
148
182
  select: resolvedProps(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
149
183
  displayEmpty: !!rest.placeholder,
150
- renderValue: (value) => rest.placeholder && (!value || Array.isArray(value) && value.length === 0) ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SelectPlaceholder, { className: "MuiSelect-placeholder", children: rest.placeholder }) : value
184
+ renderValue: (value) => rest.placeholder && (!value || Array.isArray(value) && value.length === 0) ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SelectPlaceholder, { className: "MuiSelect-placeholder", children: rest.placeholder }) : value
151
185
  }, SelectProps2), import_mui_form_utils.SelectPropOverrides), (0, import_mui_form_utils.SelectAccessibilityOverrides)(openDetected, setOpenDetected, SelectProps2 == null ? void 0 : SelectProps2.open)), (_f = rest.slotProps) == null ? void 0 : _f.select)),
152
186
  inputLabel: resolvedProps(__spreadValues(__spreadValues({
153
187
  component: import_mui_form_utils.FormLabel,
@@ -155,7 +189,15 @@ var TextField = (0, import_react2.forwardRef)((props, ref) => {
155
189
  required,
156
190
  shrink: true
157
191
  }, InputLabelProps), (_g = rest.slotProps) == null ? void 0 : _g.inputLabel)),
158
- formHelperText: resolvedProps(__spreadValues(__spreadValues({ component: "div" }, FormHelperTextProps2), (_h = rest.slotProps) == null ? void 0 : _h.formHelperText))
192
+ formHelperText: resolvedProps(__spreadProps(__spreadValues(__spreadValues({
193
+ component: "div"
194
+ }, FormHelperTextProps2), (_h = rest.slotProps) == null ? void 0 : _h.formHelperText), {
195
+ charCount,
196
+ helperText,
197
+ maxLength,
198
+ displayOverflowMaxLength,
199
+ showCharacterCount
200
+ }))
159
201
  },
160
202
  ref
161
203
  })
package/dist/index.mjs CHANGED
@@ -31,7 +31,7 @@ var __objRest = (source, exclude) => {
31
31
  };
32
32
 
33
33
  // src/lib/TextField.tsx
34
- import { forwardRef as forwardRef2, useState } from "react";
34
+ import { forwardRef as forwardRef3, useState } from "react";
35
35
  import MuiTextField from "@mui/material/TextField";
36
36
  import {
37
37
  FormHelperText,
@@ -64,15 +64,50 @@ var Grid = (args) => {
64
64
  import MuiStack from "@mui/material/Stack";
65
65
  import { jsx as jsx4 } from "react/jsx-runtime";
66
66
 
67
+ // ../typography/src/lib/Typography.tsx
68
+ import { forwardRef as forwardRef2 } from "react";
69
+ import { default as MuiTypography } from "@mui/material/Typography";
70
+ import { jsx as jsx5 } from "react/jsx-runtime";
71
+ var Typography = forwardRef2(
72
+ (_a, ref) => {
73
+ var _b = _a, { children } = _b, rest = __objRest(_b, ["children"]);
74
+ return /* @__PURE__ */ jsx5(MuiTypography, __spreadProps(__spreadValues({}, rest), { ref, children }));
75
+ }
76
+ );
77
+
67
78
  // src/lib/TextField.tsx
68
79
  import { styled } from "@mui/material/styles";
69
- import { jsx as jsx5, jsxs } from "react/jsx-runtime";
80
+ import { Fragment, jsx as jsx6, jsxs } from "react/jsx-runtime";
70
81
  var SelectPlaceholder = styled("span", {
71
82
  name: "MuiTextField",
72
83
  slot: "SelectPlaceholder",
73
84
  overridesResolver: (props, styles) => styles.avFilled
74
85
  })(({ theme }) => ({ opacity: 1, color: theme.palette.grey[400] }));
75
- var TextField = forwardRef2((props, ref) => {
86
+ var TextFieldFormHelperText = (_a) => {
87
+ var _b = _a, {
88
+ charCount,
89
+ helperText,
90
+ maxLength,
91
+ showCharacterCount
92
+ } = _b, FormHelperTextProps2 = __objRest(_b, [
93
+ "charCount",
94
+ "helperText",
95
+ "maxLength",
96
+ "showCharacterCount"
97
+ ]);
98
+ if (showCharacterCount) {
99
+ return /* @__PURE__ */ jsxs(Grid, { container: true, justifyContent: "space-between", flexWrap: "nowrap", children: [
100
+ /* @__PURE__ */ jsx6(FormHelperText, __spreadProps(__spreadValues({}, FormHelperTextProps2), { sx: { marginRight: "12px" }, children: helperText })),
101
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", marginTop: "4px", lineHeight: "1.25rem", children: [
102
+ /* @__PURE__ */ jsx6(Typography, { component: "span", variant: "inherit", color: charCount > maxLength ? "error" : "textPrimary", children: charCount || 0 }),
103
+ "/",
104
+ maxLength
105
+ ] })
106
+ ] });
107
+ }
108
+ return /* @__PURE__ */ jsx6(FormHelperText, __spreadProps(__spreadValues({}, FormHelperTextProps2), { children: helperText }));
109
+ };
110
+ var TextField = forwardRef3((props, ref) => {
76
111
  var _b, _c, _d, _e, _f, _g, _h;
77
112
  const _a = props, {
78
113
  InputProps: InputProps2,
@@ -83,7 +118,8 @@ var TextField = forwardRef2((props, ref) => {
83
118
  SelectProps: SelectProps2,
84
119
  inputProps,
85
120
  helperText,
86
- showCharacterCount = false
121
+ showCharacterCount = false,
122
+ displayOverflowMaxLength = false
87
123
  } = _a, rest = __objRest(_a, [
88
124
  "InputProps",
89
125
  "helpTopicId",
@@ -93,34 +129,32 @@ var TextField = forwardRef2((props, ref) => {
93
129
  "SelectProps",
94
130
  "inputProps",
95
131
  "helperText",
96
- "showCharacterCount"
132
+ "showCharacterCount",
133
+ "displayOverflowMaxLength"
97
134
  ]);
98
135
  const [openDetected, setOpenDetected] = useState(false);
99
136
  const [charCount, setCharCount] = useState(0);
137
+ const maxLength = (inputProps == null ? void 0 : inputProps.maxLength) || ((_c = (_b = rest.slotProps) == null ? void 0 : _b.htmlInput) == null ? void 0 : _c.maxLength);
100
138
  const resolvedProps = (props2) => !props2 || Object.keys(props2).length === 0 ? void 0 : props2;
101
- return /* @__PURE__ */ jsx5(
139
+ return /* @__PURE__ */ jsx6(
102
140
  MuiTextField,
103
141
  __spreadProps(__spreadValues({}, rest), {
104
142
  onChange: (event) => {
105
143
  setCharCount(event.target.value.length);
106
144
  if (rest.onChange) rest.onChange(event);
107
145
  },
108
- helperText: showCharacterCount ? /* @__PURE__ */ jsxs(Grid, { container: true, justifyContent: "space-between", children: [
109
- helperText,
110
- " ",
111
- /* @__PURE__ */ jsxs("span", { style: { marginLeft: 4 }, children: [
112
- charCount || 0,
113
- "/",
114
- (inputProps == null ? void 0 : inputProps.maxLength) || ((_c = (_b = rest.slotProps) == null ? void 0 : _b.htmlInput) == null ? void 0 : _c.maxLength)
115
- ] })
116
- ] }) : helperText,
117
- slots: { formHelperText: FormHelperText },
146
+ helperText: helperText || /* @__PURE__ */ jsx6(Fragment, {}),
147
+ slots: { formHelperText: TextFieldFormHelperText },
118
148
  slotProps: {
119
149
  input: resolvedProps(__spreadValues(__spreadValues(__spreadValues({}, InputProps2), InputPropOverrides), (_d = rest.slotProps) == null ? void 0 : _d.input)),
120
- htmlInput: resolvedProps(__spreadValues(__spreadValues({ "aria-required": required }, inputProps), (_e = rest.slotProps) == null ? void 0 : _e.htmlInput)),
150
+ htmlInput: resolvedProps(__spreadProps(__spreadValues(__spreadValues({
151
+ "aria-required": required
152
+ }, inputProps), (_e = rest.slotProps) == null ? void 0 : _e.htmlInput), {
153
+ maxLength: !displayOverflowMaxLength ? maxLength : void 0
154
+ })),
121
155
  select: resolvedProps(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
122
156
  displayEmpty: !!rest.placeholder,
123
- renderValue: (value) => rest.placeholder && (!value || Array.isArray(value) && value.length === 0) ? /* @__PURE__ */ jsx5(SelectPlaceholder, { className: "MuiSelect-placeholder", children: rest.placeholder }) : value
157
+ renderValue: (value) => rest.placeholder && (!value || Array.isArray(value) && value.length === 0) ? /* @__PURE__ */ jsx6(SelectPlaceholder, { className: "MuiSelect-placeholder", children: rest.placeholder }) : value
124
158
  }, SelectProps2), SelectPropOverrides), SelectAccessibilityOverrides(openDetected, setOpenDetected, SelectProps2 == null ? void 0 : SelectProps2.open)), (_f = rest.slotProps) == null ? void 0 : _f.select)),
125
159
  inputLabel: resolvedProps(__spreadValues(__spreadValues({
126
160
  component: FormLabel,
@@ -128,7 +162,15 @@ var TextField = forwardRef2((props, ref) => {
128
162
  required,
129
163
  shrink: true
130
164
  }, InputLabelProps), (_g = rest.slotProps) == null ? void 0 : _g.inputLabel)),
131
- formHelperText: resolvedProps(__spreadValues(__spreadValues({ component: "div" }, FormHelperTextProps2), (_h = rest.slotProps) == null ? void 0 : _h.formHelperText))
165
+ formHelperText: resolvedProps(__spreadProps(__spreadValues(__spreadValues({
166
+ component: "div"
167
+ }, FormHelperTextProps2), (_h = rest.slotProps) == null ? void 0 : _h.formHelperText), {
168
+ charCount,
169
+ helperText,
170
+ maxLength,
171
+ displayOverflowMaxLength,
172
+ showCharacterCount
173
+ }))
132
174
  },
133
175
  ref
134
176
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@availity/mui-textfield",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Availity MUI Textfield Component - part of the @availity/element design system",
5
5
  "keywords": [
6
6
  "react",
@@ -29,12 +29,24 @@ export const _TextField: StoryObj<typeof TextField> = {
29
29
  };
30
30
 
31
31
  export const _TextFieldCharacterCount: StoryObj<typeof TextField> = {
32
- render: (args: TextFieldProps) => <TextField {...args} />,
32
+ render: (args: TextFieldProps) => <TextField sx={{ width: 'min-content' }} {...args} />,
33
+ args: {
34
+ label: 'Field Label',
35
+ id: 'test',
36
+ helpTopicId: '123',
37
+ showCharacterCount: true,
38
+ slotProps: { htmlInput: { maxLength: 10 } },
39
+ },
40
+ };
41
+
42
+ export const _TextFieldCharacterCountOverflow: StoryObj<typeof TextField> = {
43
+ render: (args: TextFieldProps) => <TextField sx={{ width: 'min-content' }} {...args} />,
33
44
  args: {
34
45
  label: 'Field Label',
35
46
  id: 'test',
36
47
  helpTopicId: '123',
37
48
  showCharacterCount: true,
49
+ displayOverflowMaxLength: true,
38
50
  slotProps: { htmlInput: { maxLength: 10 } },
39
51
  },
40
52
  };
@@ -14,12 +14,14 @@ describe('TextField', () => {
14
14
  <TextField label="Test" showCharacterCount inputProps={{ 'data-testid': 'testTextField', maxLength: 20 }} />
15
15
  );
16
16
 
17
- expect(getByText('0/20')).toBeTruthy();
17
+ expect(getByText('0')).toBeTruthy();
18
+ expect(getByText('/20')).toBeTruthy();
18
19
 
19
20
  const input = getByTestId('testTextField');
20
21
  fireEvent.change(input, { target: { value: 'Some Text' } });
21
22
 
22
- expect(getByText('9/20')).toBeTruthy();
23
+ expect(getByText('9')).toBeTruthy();
24
+ expect(getByText('/20')).toBeTruthy();
23
25
 
24
26
  fireEvent.change(input, { target: { value: "Some More Text that doesn't fit" } });
25
27
 
@@ -34,12 +36,14 @@ describe('TextField', () => {
34
36
  slotProps={{ htmlInput: { 'data-testid': 'testTextField', maxLength: 20 } }}
35
37
  />
36
38
  );
37
- expect(getByText('0/20')).toBeTruthy();
39
+ expect(getByText('0')).toBeTruthy();
40
+ expect(getByText('/20')).toBeTruthy();
38
41
 
39
42
  const input = getByTestId('testTextField');
40
43
  fireEvent.change(input, { target: { value: 'Some Text' } });
41
44
 
42
- expect(getByText('9/20')).toBeTruthy();
45
+ expect(getByText('9')).toBeTruthy();
46
+ expect(getByText('/20')).toBeTruthy();
43
47
 
44
48
  fireEvent.change(input, { target: { value: "Some More Text that doesn't fit" } });
45
49
 
@@ -12,6 +12,7 @@ import {
12
12
  SelectProps,
13
13
  } from '@availity/mui-form-utils';
14
14
  import { Grid } from '@availity/mui-layout';
15
+ import { Typography } from '@availity/mui-typography';
15
16
  import { styled } from '@mui/material/styles';
16
17
 
17
18
  export type TextFieldProps = {
@@ -23,6 +24,8 @@ export type TextFieldProps = {
23
24
  fullWidth?: boolean;
24
25
  /** if `true`, the character counter will display. The maxLength is taken from the `inputProps.maxLength` prop. @default false */
25
26
  showCharacterCount?: boolean;
27
+ /** If `true`, the input maxLength can be exceeded. If validation is required, you'll have to do it manually. @default false */
28
+ displayOverflowMaxLength?: boolean;
26
29
  } & Pick<FormLabelProps, 'helpTopicId'> &
27
30
  Omit<MuiTextFieldProps, 'fullWidth' | 'variant'>;
28
31
 
@@ -32,6 +35,39 @@ const SelectPlaceholder = styled('span', {
32
35
  overridesResolver: (props, styles) => styles.avFilled,
33
36
  })(({ theme }) => ({ opacity: 1, color: theme.palette.grey[400] }));
34
37
 
38
+ export type TextFieldFormHelperTextProps = {
39
+ charCount: string;
40
+ helperText: string;
41
+ maxLength: string;
42
+ showCharacterCount: boolean;
43
+ } & FormHelperTextProps;
44
+
45
+ const TextFieldFormHelperText = ({
46
+ charCount,
47
+ helperText,
48
+ maxLength,
49
+ showCharacterCount,
50
+ ...FormHelperTextProps
51
+ }: TextFieldFormHelperTextProps) => {
52
+ if (showCharacterCount) {
53
+ return (
54
+ <Grid container justifyContent="space-between" flexWrap="nowrap">
55
+ <FormHelperText {...FormHelperTextProps} sx={{ marginRight: '12px' }}>
56
+ {helperText}
57
+ </FormHelperText>
58
+ <Typography variant="caption" marginTop="4px" lineHeight="1.25rem">
59
+ <Typography component="span" variant="inherit" color={charCount > maxLength ? 'error' : 'textPrimary'}>
60
+ {charCount || 0}
61
+ </Typography>
62
+ /{maxLength}
63
+ </Typography>
64
+ </Grid>
65
+ );
66
+ }
67
+
68
+ return <FormHelperText {...FormHelperTextProps}>{helperText}</FormHelperText>;
69
+ };
70
+
35
71
  export const TextField = forwardRef<HTMLDivElement | HTMLInputElement, TextFieldProps>((props, ref) => {
36
72
  const {
37
73
  InputProps,
@@ -43,11 +79,15 @@ export const TextField = forwardRef<HTMLDivElement | HTMLInputElement, TextField
43
79
  inputProps,
44
80
  helperText,
45
81
  showCharacterCount = false,
82
+ displayOverflowMaxLength = false,
46
83
  ...rest
47
84
  } = props;
48
85
  const [openDetected, setOpenDetected] = useState(false);
49
86
  const [charCount, setCharCount] = useState(0);
50
87
 
88
+ // @ts-expect-error I'm not sure why maxLength is undefined in htmlInput, but it works. There's something weird with the type.
89
+ const maxLength = inputProps?.maxLength || rest.slotProps?.htmlInput?.maxLength;
90
+
51
91
  const resolvedProps = (props: Record<string, unknown>) =>
52
92
  !props || Object.keys(props).length === 0 ? undefined : props;
53
93
 
@@ -58,23 +98,16 @@ export const TextField = forwardRef<HTMLDivElement | HTMLInputElement, TextField
58
98
  setCharCount(event.target.value.length);
59
99
  if (rest.onChange) rest.onChange(event);
60
100
  }}
61
- helperText={
62
- showCharacterCount ? (
63
- <Grid container justifyContent="space-between">
64
- {helperText}{' '}
65
- <span style={{ marginLeft: 4 }}>
66
- {/* @ts-expect-error I'm not sure why maxLength is undefined here, but it works. */}
67
- {charCount || 0}/{inputProps?.maxLength || rest.slotProps?.htmlInput?.maxLength}
68
- </span>
69
- </Grid>
70
- ) : (
71
- helperText
72
- )
73
- }
74
- slots={{ formHelperText: FormHelperText }}
101
+ helperText={helperText || <></>}
102
+ slots={{ formHelperText: TextFieldFormHelperText }}
75
103
  slotProps={{
76
104
  input: resolvedProps({ ...InputProps, ...InputPropOverrides, ...rest.slotProps?.input }),
77
- htmlInput: resolvedProps({ 'aria-required': required, ...inputProps, ...rest.slotProps?.htmlInput }),
105
+ htmlInput: resolvedProps({
106
+ 'aria-required': required,
107
+ ...inputProps,
108
+ ...rest.slotProps?.htmlInput,
109
+ maxLength: !displayOverflowMaxLength ? maxLength : undefined,
110
+ }),
78
111
  select: resolvedProps({
79
112
  displayEmpty: !!rest.placeholder,
80
113
  renderValue: (value: unknown) =>
@@ -96,7 +129,16 @@ export const TextField = forwardRef<HTMLDivElement | HTMLInputElement, TextField
96
129
  ...InputLabelProps,
97
130
  ...rest.slotProps?.inputLabel,
98
131
  }),
99
- formHelperText: resolvedProps({ component: 'div', ...FormHelperTextProps, ...rest.slotProps?.formHelperText }),
132
+ formHelperText: resolvedProps({
133
+ component: 'div',
134
+ ...FormHelperTextProps,
135
+ ...rest.slotProps?.formHelperText,
136
+ charCount,
137
+ helperText,
138
+ maxLength,
139
+ displayOverflowMaxLength,
140
+ showCharacterCount,
141
+ }),
100
142
  }}
101
143
  ref={ref}
102
144
  />