@availity/mui-textfield 1.0.1 → 1.1.1
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 +13 -0
- package/dist/index.d.mts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +118 -11
- package/dist/index.mjs +117 -10
- package/package.json +2 -2
- package/src/lib/TextField.stories.tsx +47 -37
- package/src/lib/TextField.test.tsx +57 -1
- package/src/lib/TextField.tsx +100 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [1.1.1](https://github.com/Availity/element/compare/@availity/mui-textfield@1.1.0...@availity/mui-textfield@1.1.1) (2025-03-27)
|
|
6
|
+
|
|
7
|
+
## [1.1.0](https://github.com/Availity/element/compare/@availity/mui-textfield@1.0.1...@availity/mui-textfield@1.1.0) (2025-03-21)
|
|
8
|
+
|
|
9
|
+
### Dependency Updates
|
|
10
|
+
|
|
11
|
+
* `mui-form-utils` updated to version `1.0.1`
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **mui-textfield, mui-form-utils:** add placeholder support to select ([3a6b1ef](https://github.com/Availity/element/commit/3a6b1ef2e5ceedca632bd1916734e56402371ffb))
|
|
16
|
+
* **mui-textfield:** add optional character count to textfield ([1c3f0dc](https://github.com/Availity/element/commit/1c3f0dc886e153c79afc81c98b3326ed8990b305))
|
|
17
|
+
|
|
5
18
|
## [1.0.1](https://github.com/Availity/element/compare/@availity/mui-textfield@1.0.0...@availity/mui-textfield@1.0.1) (2025-03-07)
|
|
6
19
|
|
|
7
20
|
### Dependency Updates
|
package/dist/index.d.mts
CHANGED
|
@@ -9,7 +9,17 @@ type TextFieldProps = {
|
|
|
9
9
|
SelectProps?: SelectProps;
|
|
10
10
|
/** If `true`, the input will take up the full width of its container. @default true */
|
|
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
|
+
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;
|
|
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
|
+
};
|
|
13
23
|
declare const TextField: react.ForwardRefExoticComponent<Omit<TextFieldProps, "ref"> & react.RefAttributes<HTMLDivElement | HTMLInputElement>>;
|
|
14
24
|
|
|
15
|
-
export { TextField, type TextFieldProps };
|
|
25
|
+
export { TextField, type TextFieldFormHelperTextProps, type TextFieldProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,17 @@ type TextFieldProps = {
|
|
|
9
9
|
SelectProps?: SelectProps;
|
|
10
10
|
/** If `true`, the input will take up the full width of its container. @default true */
|
|
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
|
+
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;
|
|
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
|
+
};
|
|
13
23
|
declare const TextField: react.ForwardRefExoticComponent<Omit<TextFieldProps, "ref"> & react.RefAttributes<HTMLDivElement | HTMLInputElement>>;
|
|
14
24
|
|
|
15
|
-
export { TextField, type TextFieldProps };
|
|
25
|
+
export { TextField, type TextFieldFormHelperTextProps, type TextFieldProps };
|
package/dist/index.js
CHANGED
|
@@ -64,27 +64,134 @@ __export(index_exports, {
|
|
|
64
64
|
module.exports = __toCommonJS(index_exports);
|
|
65
65
|
|
|
66
66
|
// src/lib/TextField.tsx
|
|
67
|
-
var
|
|
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
|
+
|
|
71
|
+
// ../layout/src/lib/Box.tsx
|
|
72
|
+
var import_react = require("react");
|
|
73
|
+
var import_Box = __toESM(require("@mui/material/Box"));
|
|
70
74
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
71
|
-
var
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
var Box = (0, import_react.forwardRef)((props, ref) => {
|
|
76
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Box.default, __spreadProps(__spreadValues({}, props), { ref }));
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// ../layout/src/lib/Container.tsx
|
|
80
|
+
var import_Container = __toESM(require("@mui/material/Container"));
|
|
81
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
82
|
+
|
|
83
|
+
// ../layout/src/lib/Grid.tsx
|
|
84
|
+
var import_Grid2 = __toESM(require("@mui/material/Grid2"));
|
|
85
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
86
|
+
var Grid = (args) => {
|
|
87
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_Grid2.default, __spreadValues({}, args));
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// ../layout/src/lib/Stack.tsx
|
|
91
|
+
var import_Stack = __toESM(require("@mui/material/Stack"));
|
|
92
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
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
|
+
|
|
105
|
+
// src/lib/TextField.tsx
|
|
106
|
+
var import_styles = require("@mui/material/styles");
|
|
107
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
108
|
+
var SelectPlaceholder = (0, import_styles.styled)("span", {
|
|
109
|
+
name: "MuiTextField",
|
|
110
|
+
slot: "SelectPlaceholder",
|
|
111
|
+
overridesResolver: (props, styles) => styles.avFilled
|
|
112
|
+
})(({ theme }) => ({ opacity: 1, color: theme.palette.grey[400] }));
|
|
113
|
+
var TextFieldFormHelperText = ({
|
|
114
|
+
charCount,
|
|
115
|
+
helperText,
|
|
116
|
+
maxLength,
|
|
117
|
+
showCharacterCount
|
|
118
|
+
}) => {
|
|
119
|
+
if (showCharacterCount) {
|
|
120
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Grid, { container: true, justifyContent: "space-between", flexWrap: "nowrap", children: [
|
|
121
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_mui_form_utils.FormHelperText, { sx: { marginRight: "12px" }, children: helperText }),
|
|
122
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Typography, { variant: "caption", marginTop: "4px", lineHeight: "1.25rem", children: [
|
|
123
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Typography, { component: "span", variant: "inherit", color: charCount > maxLength ? "error" : "textPrimary", children: charCount || 0 }),
|
|
124
|
+
"/",
|
|
125
|
+
maxLength
|
|
126
|
+
] })
|
|
127
|
+
] });
|
|
128
|
+
}
|
|
129
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_mui_form_utils.FormHelperText, { children: helperText });
|
|
130
|
+
};
|
|
131
|
+
var TextField = (0, import_react3.forwardRef)((props, ref) => {
|
|
132
|
+
var _b, _c, _d, _e, _f, _g, _h;
|
|
133
|
+
const _a = props, {
|
|
134
|
+
InputProps: InputProps2,
|
|
135
|
+
helpTopicId,
|
|
136
|
+
InputLabelProps,
|
|
137
|
+
FormHelperTextProps: FormHelperTextProps2,
|
|
138
|
+
required,
|
|
139
|
+
SelectProps: SelectProps2,
|
|
140
|
+
inputProps,
|
|
141
|
+
helperText,
|
|
142
|
+
showCharacterCount = false,
|
|
143
|
+
displayOverflowMaxLength = false
|
|
144
|
+
} = _a, rest = __objRest(_a, [
|
|
145
|
+
"InputProps",
|
|
146
|
+
"helpTopicId",
|
|
147
|
+
"InputLabelProps",
|
|
148
|
+
"FormHelperTextProps",
|
|
149
|
+
"required",
|
|
150
|
+
"SelectProps",
|
|
151
|
+
"inputProps",
|
|
152
|
+
"helperText",
|
|
153
|
+
"showCharacterCount",
|
|
154
|
+
"displayOverflowMaxLength"
|
|
155
|
+
]);
|
|
156
|
+
const [openDetected, setOpenDetected] = (0, import_react3.useState)(false);
|
|
157
|
+
const [charCount, setCharCount] = (0, import_react3.useState)(0);
|
|
158
|
+
const maxLength = (inputProps == null ? void 0 : inputProps.maxLength) || ((_c = (_b = rest.slotProps) == null ? void 0 : _b.htmlInput) == null ? void 0 : _c.maxLength);
|
|
159
|
+
const resolvedProps = (props2) => !props2 || Object.keys(props2).length === 0 ? void 0 : props2;
|
|
160
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
75
161
|
import_TextField.default,
|
|
76
162
|
__spreadProps(__spreadValues({}, rest), {
|
|
163
|
+
onChange: (event) => {
|
|
164
|
+
setCharCount(event.target.value.length);
|
|
165
|
+
if (rest.onChange) rest.onChange(event);
|
|
166
|
+
},
|
|
167
|
+
helperText: helperText || /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, {}),
|
|
168
|
+
slots: { formHelperText: TextFieldFormHelperText },
|
|
77
169
|
slotProps: {
|
|
78
|
-
input: __spreadValues(__spreadValues({}, InputProps2), import_mui_form_utils.InputPropOverrides),
|
|
79
|
-
htmlInput: __spreadValues({
|
|
80
|
-
|
|
81
|
-
|
|
170
|
+
input: resolvedProps(__spreadValues(__spreadValues(__spreadValues({}, InputProps2), import_mui_form_utils.InputPropOverrides), (_d = rest.slotProps) == null ? void 0 : _d.input)),
|
|
171
|
+
htmlInput: resolvedProps(__spreadProps(__spreadValues(__spreadValues({
|
|
172
|
+
"aria-required": required
|
|
173
|
+
}, inputProps), (_e = rest.slotProps) == null ? void 0 : _e.htmlInput), {
|
|
174
|
+
maxLength: !displayOverflowMaxLength ? maxLength : void 0
|
|
175
|
+
})),
|
|
176
|
+
select: resolvedProps(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
|
|
177
|
+
displayEmpty: !!rest.placeholder,
|
|
178
|
+
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
|
|
179
|
+
}, 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)),
|
|
180
|
+
inputLabel: resolvedProps(__spreadValues(__spreadValues({
|
|
82
181
|
component: import_mui_form_utils.FormLabel,
|
|
83
182
|
helpTopicId,
|
|
84
183
|
required,
|
|
85
184
|
shrink: true
|
|
86
|
-
}, InputLabelProps),
|
|
87
|
-
formHelperText: __spreadValues({
|
|
185
|
+
}, InputLabelProps), (_g = rest.slotProps) == null ? void 0 : _g.inputLabel)),
|
|
186
|
+
formHelperText: resolvedProps(__spreadProps(__spreadValues(__spreadValues({
|
|
187
|
+
component: "div"
|
|
188
|
+
}, FormHelperTextProps2), (_h = rest.slotProps) == null ? void 0 : _h.formHelperText), {
|
|
189
|
+
charCount,
|
|
190
|
+
helperText,
|
|
191
|
+
maxLength,
|
|
192
|
+
displayOverflowMaxLength,
|
|
193
|
+
showCharacterCount
|
|
194
|
+
}))
|
|
88
195
|
},
|
|
89
196
|
ref
|
|
90
197
|
})
|
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, 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,
|
|
@@ -40,24 +40,131 @@ import {
|
|
|
40
40
|
SelectAccessibilityOverrides,
|
|
41
41
|
SelectPropOverrides
|
|
42
42
|
} from "@availity/mui-form-utils";
|
|
43
|
+
|
|
44
|
+
// ../layout/src/lib/Box.tsx
|
|
45
|
+
import { forwardRef } from "react";
|
|
46
|
+
import MuiBox from "@mui/material/Box";
|
|
43
47
|
import { jsx } from "react/jsx-runtime";
|
|
44
|
-
var
|
|
45
|
-
|
|
48
|
+
var Box = forwardRef((props, ref) => {
|
|
49
|
+
return /* @__PURE__ */ jsx(MuiBox, __spreadProps(__spreadValues({}, props), { ref }));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ../layout/src/lib/Container.tsx
|
|
53
|
+
import MuiContainer from "@mui/material/Container";
|
|
54
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
55
|
+
|
|
56
|
+
// ../layout/src/lib/Grid.tsx
|
|
57
|
+
import MuiGrid2 from "@mui/material/Grid2";
|
|
58
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
59
|
+
var Grid = (args) => {
|
|
60
|
+
return /* @__PURE__ */ jsx3(MuiGrid2, __spreadValues({}, args));
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// ../layout/src/lib/Stack.tsx
|
|
64
|
+
import MuiStack from "@mui/material/Stack";
|
|
65
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
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
|
+
|
|
78
|
+
// src/lib/TextField.tsx
|
|
79
|
+
import { styled } from "@mui/material/styles";
|
|
80
|
+
import { Fragment, jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
81
|
+
var SelectPlaceholder = styled("span", {
|
|
82
|
+
name: "MuiTextField",
|
|
83
|
+
slot: "SelectPlaceholder",
|
|
84
|
+
overridesResolver: (props, styles) => styles.avFilled
|
|
85
|
+
})(({ theme }) => ({ opacity: 1, color: theme.palette.grey[400] }));
|
|
86
|
+
var TextFieldFormHelperText = ({
|
|
87
|
+
charCount,
|
|
88
|
+
helperText,
|
|
89
|
+
maxLength,
|
|
90
|
+
showCharacterCount
|
|
91
|
+
}) => {
|
|
92
|
+
if (showCharacterCount) {
|
|
93
|
+
return /* @__PURE__ */ jsxs(Grid, { container: true, justifyContent: "space-between", flexWrap: "nowrap", children: [
|
|
94
|
+
/* @__PURE__ */ jsx6(FormHelperText, { sx: { marginRight: "12px" }, children: helperText }),
|
|
95
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "caption", marginTop: "4px", lineHeight: "1.25rem", children: [
|
|
96
|
+
/* @__PURE__ */ jsx6(Typography, { component: "span", variant: "inherit", color: charCount > maxLength ? "error" : "textPrimary", children: charCount || 0 }),
|
|
97
|
+
"/",
|
|
98
|
+
maxLength
|
|
99
|
+
] })
|
|
100
|
+
] });
|
|
101
|
+
}
|
|
102
|
+
return /* @__PURE__ */ jsx6(FormHelperText, { children: helperText });
|
|
103
|
+
};
|
|
104
|
+
var TextField = forwardRef3((props, ref) => {
|
|
105
|
+
var _b, _c, _d, _e, _f, _g, _h;
|
|
106
|
+
const _a = props, {
|
|
107
|
+
InputProps: InputProps2,
|
|
108
|
+
helpTopicId,
|
|
109
|
+
InputLabelProps,
|
|
110
|
+
FormHelperTextProps: FormHelperTextProps2,
|
|
111
|
+
required,
|
|
112
|
+
SelectProps: SelectProps2,
|
|
113
|
+
inputProps,
|
|
114
|
+
helperText,
|
|
115
|
+
showCharacterCount = false,
|
|
116
|
+
displayOverflowMaxLength = false
|
|
117
|
+
} = _a, rest = __objRest(_a, [
|
|
118
|
+
"InputProps",
|
|
119
|
+
"helpTopicId",
|
|
120
|
+
"InputLabelProps",
|
|
121
|
+
"FormHelperTextProps",
|
|
122
|
+
"required",
|
|
123
|
+
"SelectProps",
|
|
124
|
+
"inputProps",
|
|
125
|
+
"helperText",
|
|
126
|
+
"showCharacterCount",
|
|
127
|
+
"displayOverflowMaxLength"
|
|
128
|
+
]);
|
|
46
129
|
const [openDetected, setOpenDetected] = useState(false);
|
|
47
|
-
|
|
130
|
+
const [charCount, setCharCount] = useState(0);
|
|
131
|
+
const maxLength = (inputProps == null ? void 0 : inputProps.maxLength) || ((_c = (_b = rest.slotProps) == null ? void 0 : _b.htmlInput) == null ? void 0 : _c.maxLength);
|
|
132
|
+
const resolvedProps = (props2) => !props2 || Object.keys(props2).length === 0 ? void 0 : props2;
|
|
133
|
+
return /* @__PURE__ */ jsx6(
|
|
48
134
|
MuiTextField,
|
|
49
135
|
__spreadProps(__spreadValues({}, rest), {
|
|
136
|
+
onChange: (event) => {
|
|
137
|
+
setCharCount(event.target.value.length);
|
|
138
|
+
if (rest.onChange) rest.onChange(event);
|
|
139
|
+
},
|
|
140
|
+
helperText: helperText || /* @__PURE__ */ jsx6(Fragment, {}),
|
|
141
|
+
slots: { formHelperText: TextFieldFormHelperText },
|
|
50
142
|
slotProps: {
|
|
51
|
-
input: __spreadValues(__spreadValues({}, InputProps2), InputPropOverrides),
|
|
52
|
-
htmlInput: __spreadValues({
|
|
53
|
-
|
|
54
|
-
|
|
143
|
+
input: resolvedProps(__spreadValues(__spreadValues(__spreadValues({}, InputProps2), InputPropOverrides), (_d = rest.slotProps) == null ? void 0 : _d.input)),
|
|
144
|
+
htmlInput: resolvedProps(__spreadProps(__spreadValues(__spreadValues({
|
|
145
|
+
"aria-required": required
|
|
146
|
+
}, inputProps), (_e = rest.slotProps) == null ? void 0 : _e.htmlInput), {
|
|
147
|
+
maxLength: !displayOverflowMaxLength ? maxLength : void 0
|
|
148
|
+
})),
|
|
149
|
+
select: resolvedProps(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
|
|
150
|
+
displayEmpty: !!rest.placeholder,
|
|
151
|
+
renderValue: (value) => rest.placeholder && (!value || Array.isArray(value) && value.length === 0) ? /* @__PURE__ */ jsx6(SelectPlaceholder, { className: "MuiSelect-placeholder", children: rest.placeholder }) : value
|
|
152
|
+
}, SelectProps2), SelectPropOverrides), SelectAccessibilityOverrides(openDetected, setOpenDetected, SelectProps2 == null ? void 0 : SelectProps2.open)), (_f = rest.slotProps) == null ? void 0 : _f.select)),
|
|
153
|
+
inputLabel: resolvedProps(__spreadValues(__spreadValues({
|
|
55
154
|
component: FormLabel,
|
|
56
155
|
helpTopicId,
|
|
57
156
|
required,
|
|
58
157
|
shrink: true
|
|
59
|
-
}, InputLabelProps),
|
|
60
|
-
formHelperText: __spreadValues({
|
|
158
|
+
}, InputLabelProps), (_g = rest.slotProps) == null ? void 0 : _g.inputLabel)),
|
|
159
|
+
formHelperText: resolvedProps(__spreadProps(__spreadValues(__spreadValues({
|
|
160
|
+
component: "div"
|
|
161
|
+
}, FormHelperTextProps2), (_h = rest.slotProps) == null ? void 0 : _h.formHelperText), {
|
|
162
|
+
charCount,
|
|
163
|
+
helperText,
|
|
164
|
+
maxLength,
|
|
165
|
+
displayOverflowMaxLength,
|
|
166
|
+
showCharacterCount
|
|
167
|
+
}))
|
|
61
168
|
},
|
|
62
169
|
ref
|
|
63
170
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@availity/mui-textfield",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Availity MUI Textfield Component - part of the @availity/element design system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"publish:canary": "yarn npm publish --access public --tag canary"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@availity/mui-form-utils": "1.0
|
|
43
|
+
"@availity/mui-form-utils": "1.1.0",
|
|
44
44
|
"@availity/mui-icon": "1.0.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
@@ -17,25 +17,37 @@ const meta: Meta<typeof TextField> = {
|
|
|
17
17
|
title: 'Form Components/TextField/TextField',
|
|
18
18
|
component: TextField,
|
|
19
19
|
tags: ['autodocs'],
|
|
20
|
-
args: {
|
|
21
|
-
|
|
22
|
-
fullWidth: false,
|
|
23
|
-
},
|
|
24
|
-
argTypes: {
|
|
25
|
-
helperText: {
|
|
26
|
-
type: 'string',
|
|
27
|
-
},
|
|
28
|
-
},
|
|
20
|
+
args: { helperText: 'Helper Text', fullWidth: false },
|
|
21
|
+
argTypes: { helperText: { type: 'string' } },
|
|
29
22
|
};
|
|
30
23
|
|
|
31
24
|
export default meta;
|
|
32
25
|
|
|
33
26
|
export const _TextField: StoryObj<typeof TextField> = {
|
|
34
27
|
render: (args: TextFieldProps) => <TextField {...args} />,
|
|
28
|
+
args: { label: 'Field Label', id: 'test', helpTopicId: '123' },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const _TextFieldCharacterCount: StoryObj<typeof TextField> = {
|
|
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} />,
|
|
35
44
|
args: {
|
|
36
45
|
label: 'Field Label',
|
|
37
46
|
id: 'test',
|
|
38
47
|
helpTopicId: '123',
|
|
48
|
+
showCharacterCount: true,
|
|
49
|
+
displayOverflowMaxLength: true,
|
|
50
|
+
slotProps: { htmlInput: { maxLength: 10 } },
|
|
39
51
|
},
|
|
40
52
|
};
|
|
41
53
|
|
|
@@ -48,9 +60,7 @@ export const _States: StoryObj<typeof TextField> = {
|
|
|
48
60
|
<TextField label="Disabled" id="disabled" disabled {...args} />
|
|
49
61
|
</Stack>
|
|
50
62
|
),
|
|
51
|
-
args: {
|
|
52
|
-
margin: 'normal',
|
|
53
|
-
},
|
|
63
|
+
args: { margin: 'normal' },
|
|
54
64
|
};
|
|
55
65
|
|
|
56
66
|
export const _Sizes: StoryObj<typeof TextField> = {
|
|
@@ -60,9 +70,7 @@ export const _Sizes: StoryObj<typeof TextField> = {
|
|
|
60
70
|
<TextField label="Medium" id="medium" size="medium" {...args} />
|
|
61
71
|
</Stack>
|
|
62
72
|
),
|
|
63
|
-
args: {
|
|
64
|
-
margin: 'normal',
|
|
65
|
-
},
|
|
73
|
+
args: { margin: 'normal' },
|
|
66
74
|
};
|
|
67
75
|
|
|
68
76
|
export const _WithIcon: StoryObj<typeof TextField> = {
|
|
@@ -158,9 +166,7 @@ const TextMaskCustom = forwardRef<HTMLInputElement, CustomProps>(function TextMa
|
|
|
158
166
|
<IMaskInput
|
|
159
167
|
{...other}
|
|
160
168
|
mask="(#00) 000-0000"
|
|
161
|
-
definitions={{
|
|
162
|
-
'#': /[1-9]/,
|
|
163
|
-
}}
|
|
169
|
+
definitions={{ '#': /[1-9]/ }}
|
|
164
170
|
inputRef={ref}
|
|
165
171
|
onAccept={(value: any) => onChange({ target: { name: props.name, value } })}
|
|
166
172
|
overwrite
|
|
@@ -176,12 +182,7 @@ const NumericFormatCustom = forwardRef<NumericFormatProps, CustomProps>(function
|
|
|
176
182
|
{...other}
|
|
177
183
|
getInputRef={ref}
|
|
178
184
|
onValueChange={(values) => {
|
|
179
|
-
onChange({
|
|
180
|
-
target: {
|
|
181
|
-
name: props.name,
|
|
182
|
-
value: values.value,
|
|
183
|
-
},
|
|
184
|
-
});
|
|
185
|
+
onChange({ target: { name: props.name, value: values.value } });
|
|
185
186
|
}}
|
|
186
187
|
thousandSeparator
|
|
187
188
|
valueIsNumericString
|
|
@@ -251,16 +252,10 @@ export const _InputMasking: StoryObj<typeof TextField> = {
|
|
|
251
252
|
|
|
252
253
|
// ---------------------------------------
|
|
253
254
|
|
|
254
|
-
const [values, setValues] = useState({
|
|
255
|
-
textmask: '(100) 000-0000',
|
|
256
|
-
numberformat: '1320',
|
|
257
|
-
});
|
|
255
|
+
const [values, setValues] = useState({ textmask: '(100) 000-0000', numberformat: '1320' });
|
|
258
256
|
|
|
259
257
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
260
|
-
setValues({
|
|
261
|
-
...values,
|
|
262
|
-
[event.target.name]: event.target.value,
|
|
263
|
-
});
|
|
258
|
+
setValues({ ...values, [event.target.name]: event.target.value });
|
|
264
259
|
};
|
|
265
260
|
|
|
266
261
|
return (
|
|
@@ -306,9 +301,26 @@ export const _Select: StoryObj<typeof TextField> = {
|
|
|
306
301
|
</TextField>
|
|
307
302
|
);
|
|
308
303
|
},
|
|
309
|
-
args: {
|
|
310
|
-
|
|
304
|
+
args: { label: 'Select' },
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
export const _SelectPlaceholder: StoryObj<typeof TextField> = {
|
|
308
|
+
render: (args: TextFieldProps) => {
|
|
309
|
+
const [count, setCount] = useState('');
|
|
310
|
+
|
|
311
|
+
const handleChange = (event: SelectChangeEvent) => {
|
|
312
|
+
setCount(event.target.value as string);
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
<TextField value={count} select SelectProps={{ onChange: handleChange }} {...args}>
|
|
317
|
+
<MenuItem value={10}>10</MenuItem>
|
|
318
|
+
<MenuItem value={20}>20</MenuItem>
|
|
319
|
+
<MenuItem value={30}>30</MenuItem>
|
|
320
|
+
</TextField>
|
|
321
|
+
);
|
|
311
322
|
},
|
|
323
|
+
args: { label: 'Select', placeholder: 'Select...' },
|
|
312
324
|
};
|
|
313
325
|
|
|
314
326
|
export const _MultiSelect: StoryObj<typeof TextField> = {
|
|
@@ -351,7 +363,5 @@ export const _MultiSelect: StoryObj<typeof TextField> = {
|
|
|
351
363
|
</TextField>
|
|
352
364
|
);
|
|
353
365
|
},
|
|
354
|
-
args: {
|
|
355
|
-
label: 'MultiSelect',
|
|
356
|
-
},
|
|
366
|
+
args: { label: 'MultiSelect' },
|
|
357
367
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { render } from '@testing-library/react';
|
|
1
|
+
import { render, fireEvent } from '@testing-library/react';
|
|
2
|
+
import { MenuItem } from '@availity/mui-menu';
|
|
2
3
|
import { TextField } from './TextField';
|
|
3
4
|
|
|
4
5
|
describe('TextField', () => {
|
|
@@ -6,4 +7,59 @@ describe('TextField', () => {
|
|
|
6
7
|
const { getByLabelText } = render(<TextField label="Test" />);
|
|
7
8
|
expect(getByLabelText('Test')).toBeTruthy();
|
|
8
9
|
});
|
|
10
|
+
|
|
11
|
+
describe('TextField character counter', () => {
|
|
12
|
+
test('should render character counter successfully via inputProps', () => {
|
|
13
|
+
const { getByText, getByTestId, getByTitle } = render(
|
|
14
|
+
<TextField label="Test" showCharacterCount inputProps={{ 'data-testid': 'testTextField', maxLength: 20 }} />
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
expect(getByText('0')).toBeTruthy();
|
|
18
|
+
expect(getByText('/20')).toBeTruthy();
|
|
19
|
+
|
|
20
|
+
const input = getByTestId('testTextField');
|
|
21
|
+
fireEvent.change(input, { target: { value: 'Some Text' } });
|
|
22
|
+
|
|
23
|
+
expect(getByText('9')).toBeTruthy();
|
|
24
|
+
expect(getByText('/20')).toBeTruthy();
|
|
25
|
+
|
|
26
|
+
fireEvent.change(input, { target: { value: "Some More Text that doesn't fit" } });
|
|
27
|
+
|
|
28
|
+
expect(getByTitle('Error')).toBeTruthy();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('should render character counter successfully via slotProps', () => {
|
|
32
|
+
const { getByText, getByTestId, getByTitle } = render(
|
|
33
|
+
<TextField
|
|
34
|
+
label="Test"
|
|
35
|
+
showCharacterCount
|
|
36
|
+
slotProps={{ htmlInput: { 'data-testid': 'testTextField', maxLength: 20 } }}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
expect(getByText('0')).toBeTruthy();
|
|
40
|
+
expect(getByText('/20')).toBeTruthy();
|
|
41
|
+
|
|
42
|
+
const input = getByTestId('testTextField');
|
|
43
|
+
fireEvent.change(input, { target: { value: 'Some Text' } });
|
|
44
|
+
|
|
45
|
+
expect(getByText('9')).toBeTruthy();
|
|
46
|
+
expect(getByText('/20')).toBeTruthy();
|
|
47
|
+
|
|
48
|
+
fireEvent.change(input, { target: { value: "Some More Text that doesn't fit" } });
|
|
49
|
+
|
|
50
|
+
expect(getByTitle('Error')).toBeTruthy();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('TextField select placeholder', () => {
|
|
54
|
+
test('should render select placeholder successfully', () => {
|
|
55
|
+
const { getByText } = render(
|
|
56
|
+
<TextField label="Test" select placeholder="Select...">
|
|
57
|
+
<MenuItem value={10}>10</MenuItem>
|
|
58
|
+
<MenuItem value={20}>20</MenuItem>
|
|
59
|
+
<MenuItem value={30}>30</MenuItem>
|
|
60
|
+
</TextField>
|
|
61
|
+
);
|
|
62
|
+
expect(getByText('Select...')).toBeTruthy();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
9
65
|
});
|
package/src/lib/TextField.tsx
CHANGED
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
SelectPropOverrides,
|
|
12
12
|
SelectProps,
|
|
13
13
|
} from '@availity/mui-form-utils';
|
|
14
|
+
import { Grid } from '@availity/mui-layout';
|
|
15
|
+
import { Typography } from '@availity/mui-typography';
|
|
16
|
+
import { styled } from '@mui/material/styles';
|
|
14
17
|
|
|
15
18
|
export type TextFieldProps = {
|
|
16
19
|
FormHelperTextProps?: FormHelperTextProps;
|
|
@@ -19,33 +22,120 @@ export type TextFieldProps = {
|
|
|
19
22
|
SelectProps?: SelectProps;
|
|
20
23
|
/** If `true`, the input will take up the full width of its container. @default true */
|
|
21
24
|
fullWidth?: boolean;
|
|
25
|
+
/** if `true`, the character counter will display. The maxLength is taken from the `inputProps.maxLength` prop. @default false */
|
|
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;
|
|
22
29
|
} & Pick<FormLabelProps, 'helpTopicId'> &
|
|
23
|
-
Omit<MuiTextFieldProps, 'fullWidth' | 'variant'
|
|
30
|
+
Omit<MuiTextFieldProps, 'fullWidth' | 'variant'>;
|
|
31
|
+
|
|
32
|
+
const SelectPlaceholder = styled('span', {
|
|
33
|
+
name: 'MuiTextField',
|
|
34
|
+
slot: 'SelectPlaceholder',
|
|
35
|
+
overridesResolver: (props, styles) => styles.avFilled,
|
|
36
|
+
})(({ theme }) => ({ opacity: 1, color: theme.palette.grey[400] }));
|
|
37
|
+
|
|
38
|
+
export type TextFieldFormHelperTextProps = {
|
|
39
|
+
charCount: string;
|
|
40
|
+
helperText: string;
|
|
41
|
+
maxLength: string;
|
|
42
|
+
showCharacterCount: boolean;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const TextFieldFormHelperText = ({
|
|
46
|
+
charCount,
|
|
47
|
+
helperText,
|
|
48
|
+
maxLength,
|
|
49
|
+
showCharacterCount,
|
|
50
|
+
}: TextFieldFormHelperTextProps) => {
|
|
51
|
+
if (showCharacterCount) {
|
|
52
|
+
return (
|
|
53
|
+
<Grid container justifyContent="space-between" flexWrap="nowrap">
|
|
54
|
+
<FormHelperText sx={{ marginRight: '12px' }}>{helperText}</FormHelperText>
|
|
55
|
+
<Typography variant="caption" marginTop="4px" lineHeight="1.25rem">
|
|
56
|
+
<Typography component="span" variant="inherit" color={charCount > maxLength ? 'error' : 'textPrimary'}>
|
|
57
|
+
{charCount || 0}
|
|
58
|
+
</Typography>
|
|
59
|
+
/{maxLength}
|
|
60
|
+
</Typography>
|
|
61
|
+
</Grid>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return <FormHelperText>{helperText}</FormHelperText>;
|
|
66
|
+
};
|
|
24
67
|
|
|
25
68
|
export const TextField = forwardRef<HTMLDivElement | HTMLInputElement, TextFieldProps>((props, ref) => {
|
|
26
|
-
const {
|
|
27
|
-
|
|
69
|
+
const {
|
|
70
|
+
InputProps,
|
|
71
|
+
helpTopicId,
|
|
72
|
+
InputLabelProps,
|
|
73
|
+
FormHelperTextProps,
|
|
74
|
+
required,
|
|
75
|
+
SelectProps,
|
|
76
|
+
inputProps,
|
|
77
|
+
helperText,
|
|
78
|
+
showCharacterCount = false,
|
|
79
|
+
displayOverflowMaxLength = false,
|
|
80
|
+
...rest
|
|
81
|
+
} = props;
|
|
28
82
|
const [openDetected, setOpenDetected] = useState(false);
|
|
83
|
+
const [charCount, setCharCount] = useState(0);
|
|
84
|
+
|
|
85
|
+
// @ts-expect-error I'm not sure why maxLength is undefined in htmlInput, but it works. There's something weird with the type.
|
|
86
|
+
const maxLength = inputProps?.maxLength || rest.slotProps?.htmlInput?.maxLength;
|
|
87
|
+
|
|
88
|
+
const resolvedProps = (props: Record<string, unknown>) =>
|
|
89
|
+
!props || Object.keys(props).length === 0 ? undefined : props;
|
|
29
90
|
|
|
30
91
|
return (
|
|
31
92
|
<MuiTextField
|
|
32
93
|
{...rest}
|
|
94
|
+
onChange={(event) => {
|
|
95
|
+
setCharCount(event.target.value.length);
|
|
96
|
+
if (rest.onChange) rest.onChange(event);
|
|
97
|
+
}}
|
|
98
|
+
helperText={helperText || <></>}
|
|
99
|
+
slots={{ formHelperText: TextFieldFormHelperText }}
|
|
33
100
|
slotProps={{
|
|
34
|
-
input: { ...InputProps, ...InputPropOverrides },
|
|
35
|
-
htmlInput: {
|
|
36
|
-
|
|
101
|
+
input: resolvedProps({ ...InputProps, ...InputPropOverrides, ...rest.slotProps?.input }),
|
|
102
|
+
htmlInput: resolvedProps({
|
|
103
|
+
'aria-required': required,
|
|
104
|
+
...inputProps,
|
|
105
|
+
...rest.slotProps?.htmlInput,
|
|
106
|
+
maxLength: !displayOverflowMaxLength ? maxLength : undefined,
|
|
107
|
+
}),
|
|
108
|
+
select: resolvedProps({
|
|
109
|
+
displayEmpty: !!rest.placeholder,
|
|
110
|
+
renderValue: (value: unknown) =>
|
|
111
|
+
rest.placeholder && (!value || (Array.isArray(value) && value.length === 0)) ? (
|
|
112
|
+
<SelectPlaceholder className="MuiSelect-placeholder">{rest.placeholder}</SelectPlaceholder>
|
|
113
|
+
) : (
|
|
114
|
+
value
|
|
115
|
+
),
|
|
37
116
|
...SelectProps,
|
|
38
117
|
...SelectPropOverrides,
|
|
39
118
|
...SelectAccessibilityOverrides(openDetected, setOpenDetected, SelectProps?.open),
|
|
40
|
-
|
|
41
|
-
|
|
119
|
+
...rest.slotProps?.select,
|
|
120
|
+
}),
|
|
121
|
+
inputLabel: resolvedProps({
|
|
42
122
|
component: FormLabel,
|
|
43
123
|
helpTopicId: helpTopicId,
|
|
44
124
|
required,
|
|
45
125
|
shrink: true,
|
|
46
126
|
...InputLabelProps,
|
|
47
|
-
|
|
48
|
-
|
|
127
|
+
...rest.slotProps?.inputLabel,
|
|
128
|
+
}),
|
|
129
|
+
formHelperText: resolvedProps({
|
|
130
|
+
component: 'div',
|
|
131
|
+
...FormHelperTextProps,
|
|
132
|
+
...rest.slotProps?.formHelperText,
|
|
133
|
+
charCount,
|
|
134
|
+
helperText,
|
|
135
|
+
maxLength,
|
|
136
|
+
displayOverflowMaxLength,
|
|
137
|
+
showCharacterCount,
|
|
138
|
+
}),
|
|
49
139
|
}}
|
|
50
140
|
ref={ref}
|
|
51
141
|
/>
|