@availity/mui-textfield 1.0.1 → 1.1.0
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 +11 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +82 -11
- package/dist/index.mjs +81 -10
- package/package.json +2 -2
- package/src/lib/TextField.stories.tsx +35 -37
- package/src/lib/TextField.test.tsx +53 -1
- package/src/lib/TextField.tsx +61 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [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
|
+
|
|
7
|
+
### Dependency Updates
|
|
8
|
+
|
|
9
|
+
* `mui-form-utils` updated to version `1.0.1`
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **mui-textfield, mui-form-utils:** add placeholder support to select ([3a6b1ef](https://github.com/Availity/element/commit/3a6b1ef2e5ceedca632bd1916734e56402371ffb))
|
|
14
|
+
* **mui-textfield:** add optional character count to textfield ([1c3f0dc](https://github.com/Availity/element/commit/1c3f0dc886e153c79afc81c98b3326ed8990b305))
|
|
15
|
+
|
|
5
16
|
## [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
17
|
|
|
7
18
|
### Dependency Updates
|
package/dist/index.d.mts
CHANGED
|
@@ -9,7 +9,9 @@ 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
|
+
} & Pick<FormLabelProps, 'helpTopicId'> & Omit<TextFieldProps$1, 'fullWidth' | 'variant'>;
|
|
13
15
|
declare const TextField: react.ForwardRefExoticComponent<Omit<TextFieldProps, "ref"> & react.RefAttributes<HTMLDivElement | HTMLInputElement>>;
|
|
14
16
|
|
|
15
17
|
export { TextField, type TextFieldProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,9 @@ 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
|
+
} & Pick<FormLabelProps, 'helpTopicId'> & Omit<TextFieldProps$1, 'fullWidth' | 'variant'>;
|
|
13
15
|
declare const TextField: react.ForwardRefExoticComponent<Omit<TextFieldProps, "ref"> & react.RefAttributes<HTMLDivElement | HTMLInputElement>>;
|
|
14
16
|
|
|
15
17
|
export { TextField, type TextFieldProps };
|
package/dist/index.js
CHANGED
|
@@ -64,27 +64,98 @@ __export(index_exports, {
|
|
|
64
64
|
module.exports = __toCommonJS(index_exports);
|
|
65
65
|
|
|
66
66
|
// src/lib/TextField.tsx
|
|
67
|
-
var
|
|
67
|
+
var import_react2 = 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
|
+
// src/lib/TextField.tsx
|
|
95
|
+
var import_styles = require("@mui/material/styles");
|
|
96
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
97
|
+
var SelectPlaceholder = (0, import_styles.styled)("span", {
|
|
98
|
+
name: "MuiTextField",
|
|
99
|
+
slot: "SelectPlaceholder",
|
|
100
|
+
overridesResolver: (props, styles) => styles.avFilled
|
|
101
|
+
})(({ theme }) => ({ opacity: 1, color: theme.palette.grey[400] }));
|
|
102
|
+
var TextField = (0, import_react2.forwardRef)((props, ref) => {
|
|
103
|
+
var _b, _c, _d, _e, _f, _g, _h;
|
|
104
|
+
const _a = props, {
|
|
105
|
+
InputProps: InputProps2,
|
|
106
|
+
helpTopicId,
|
|
107
|
+
InputLabelProps,
|
|
108
|
+
FormHelperTextProps: FormHelperTextProps2,
|
|
109
|
+
required,
|
|
110
|
+
SelectProps: SelectProps2,
|
|
111
|
+
inputProps,
|
|
112
|
+
helperText,
|
|
113
|
+
showCharacterCount = false
|
|
114
|
+
} = _a, rest = __objRest(_a, [
|
|
115
|
+
"InputProps",
|
|
116
|
+
"helpTopicId",
|
|
117
|
+
"InputLabelProps",
|
|
118
|
+
"FormHelperTextProps",
|
|
119
|
+
"required",
|
|
120
|
+
"SelectProps",
|
|
121
|
+
"inputProps",
|
|
122
|
+
"helperText",
|
|
123
|
+
"showCharacterCount"
|
|
124
|
+
]);
|
|
125
|
+
const [openDetected, setOpenDetected] = (0, import_react2.useState)(false);
|
|
126
|
+
const [charCount, setCharCount] = (0, import_react2.useState)(0);
|
|
127
|
+
const resolvedProps = (props2) => !props2 || Object.keys(props2).length === 0 ? void 0 : props2;
|
|
128
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
75
129
|
import_TextField.default,
|
|
76
130
|
__spreadProps(__spreadValues({}, rest), {
|
|
131
|
+
onChange: (event) => {
|
|
132
|
+
setCharCount(event.target.value.length);
|
|
133
|
+
if (rest.onChange) rest.onChange(event);
|
|
134
|
+
},
|
|
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 },
|
|
77
145
|
slotProps: {
|
|
78
|
-
input: __spreadValues(__spreadValues({}, InputProps2), import_mui_form_utils.InputPropOverrides),
|
|
79
|
-
htmlInput: __spreadValues({ "aria-required": required }, inputProps),
|
|
80
|
-
select: __spreadValues(__spreadValues(__spreadValues({
|
|
81
|
-
|
|
146
|
+
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)),
|
|
148
|
+
select: resolvedProps(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
|
|
149
|
+
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
|
|
151
|
+
}, 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
|
+
inputLabel: resolvedProps(__spreadValues(__spreadValues({
|
|
82
153
|
component: import_mui_form_utils.FormLabel,
|
|
83
154
|
helpTopicId,
|
|
84
155
|
required,
|
|
85
156
|
shrink: true
|
|
86
|
-
}, InputLabelProps),
|
|
87
|
-
formHelperText: __spreadValues({ component:
|
|
157
|
+
}, 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))
|
|
88
159
|
},
|
|
89
160
|
ref
|
|
90
161
|
})
|
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 forwardRef2, useState } from "react";
|
|
35
35
|
import MuiTextField from "@mui/material/TextField";
|
|
36
36
|
import {
|
|
37
37
|
FormHelperText,
|
|
@@ -40,24 +40,95 @@ 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
|
+
// src/lib/TextField.tsx
|
|
68
|
+
import { styled } from "@mui/material/styles";
|
|
69
|
+
import { jsx as jsx5, jsxs } from "react/jsx-runtime";
|
|
70
|
+
var SelectPlaceholder = styled("span", {
|
|
71
|
+
name: "MuiTextField",
|
|
72
|
+
slot: "SelectPlaceholder",
|
|
73
|
+
overridesResolver: (props, styles) => styles.avFilled
|
|
74
|
+
})(({ theme }) => ({ opacity: 1, color: theme.palette.grey[400] }));
|
|
75
|
+
var TextField = forwardRef2((props, ref) => {
|
|
76
|
+
var _b, _c, _d, _e, _f, _g, _h;
|
|
77
|
+
const _a = props, {
|
|
78
|
+
InputProps: InputProps2,
|
|
79
|
+
helpTopicId,
|
|
80
|
+
InputLabelProps,
|
|
81
|
+
FormHelperTextProps: FormHelperTextProps2,
|
|
82
|
+
required,
|
|
83
|
+
SelectProps: SelectProps2,
|
|
84
|
+
inputProps,
|
|
85
|
+
helperText,
|
|
86
|
+
showCharacterCount = false
|
|
87
|
+
} = _a, rest = __objRest(_a, [
|
|
88
|
+
"InputProps",
|
|
89
|
+
"helpTopicId",
|
|
90
|
+
"InputLabelProps",
|
|
91
|
+
"FormHelperTextProps",
|
|
92
|
+
"required",
|
|
93
|
+
"SelectProps",
|
|
94
|
+
"inputProps",
|
|
95
|
+
"helperText",
|
|
96
|
+
"showCharacterCount"
|
|
97
|
+
]);
|
|
46
98
|
const [openDetected, setOpenDetected] = useState(false);
|
|
47
|
-
|
|
99
|
+
const [charCount, setCharCount] = useState(0);
|
|
100
|
+
const resolvedProps = (props2) => !props2 || Object.keys(props2).length === 0 ? void 0 : props2;
|
|
101
|
+
return /* @__PURE__ */ jsx5(
|
|
48
102
|
MuiTextField,
|
|
49
103
|
__spreadProps(__spreadValues({}, rest), {
|
|
104
|
+
onChange: (event) => {
|
|
105
|
+
setCharCount(event.target.value.length);
|
|
106
|
+
if (rest.onChange) rest.onChange(event);
|
|
107
|
+
},
|
|
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 },
|
|
50
118
|
slotProps: {
|
|
51
|
-
input: __spreadValues(__spreadValues({}, InputProps2), InputPropOverrides),
|
|
52
|
-
htmlInput: __spreadValues({ "aria-required": required }, inputProps),
|
|
53
|
-
select: __spreadValues(__spreadValues(__spreadValues({
|
|
54
|
-
|
|
119
|
+
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)),
|
|
121
|
+
select: resolvedProps(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
|
|
122
|
+
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
|
|
124
|
+
}, SelectProps2), SelectPropOverrides), SelectAccessibilityOverrides(openDetected, setOpenDetected, SelectProps2 == null ? void 0 : SelectProps2.open)), (_f = rest.slotProps) == null ? void 0 : _f.select)),
|
|
125
|
+
inputLabel: resolvedProps(__spreadValues(__spreadValues({
|
|
55
126
|
component: FormLabel,
|
|
56
127
|
helpTopicId,
|
|
57
128
|
required,
|
|
58
129
|
shrink: true
|
|
59
|
-
}, InputLabelProps),
|
|
60
|
-
formHelperText: __spreadValues({ component:
|
|
130
|
+
}, 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))
|
|
61
132
|
},
|
|
62
133
|
ref
|
|
63
134
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@availity/mui-textfield",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
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,25 @@ 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> = {
|
|
27
|
+
render: (args: TextFieldProps) => <TextField {...args} />,
|
|
28
|
+
args: { label: 'Field Label', id: 'test', helpTopicId: '123' },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const _TextFieldCharacterCount: StoryObj<typeof TextField> = {
|
|
34
32
|
render: (args: TextFieldProps) => <TextField {...args} />,
|
|
35
33
|
args: {
|
|
36
34
|
label: 'Field Label',
|
|
37
35
|
id: 'test',
|
|
38
36
|
helpTopicId: '123',
|
|
37
|
+
showCharacterCount: true,
|
|
38
|
+
slotProps: { htmlInput: { maxLength: 10 } },
|
|
39
39
|
},
|
|
40
40
|
};
|
|
41
41
|
|
|
@@ -48,9 +48,7 @@ export const _States: StoryObj<typeof TextField> = {
|
|
|
48
48
|
<TextField label="Disabled" id="disabled" disabled {...args} />
|
|
49
49
|
</Stack>
|
|
50
50
|
),
|
|
51
|
-
args: {
|
|
52
|
-
margin: 'normal',
|
|
53
|
-
},
|
|
51
|
+
args: { margin: 'normal' },
|
|
54
52
|
};
|
|
55
53
|
|
|
56
54
|
export const _Sizes: StoryObj<typeof TextField> = {
|
|
@@ -60,9 +58,7 @@ export const _Sizes: StoryObj<typeof TextField> = {
|
|
|
60
58
|
<TextField label="Medium" id="medium" size="medium" {...args} />
|
|
61
59
|
</Stack>
|
|
62
60
|
),
|
|
63
|
-
args: {
|
|
64
|
-
margin: 'normal',
|
|
65
|
-
},
|
|
61
|
+
args: { margin: 'normal' },
|
|
66
62
|
};
|
|
67
63
|
|
|
68
64
|
export const _WithIcon: StoryObj<typeof TextField> = {
|
|
@@ -158,9 +154,7 @@ const TextMaskCustom = forwardRef<HTMLInputElement, CustomProps>(function TextMa
|
|
|
158
154
|
<IMaskInput
|
|
159
155
|
{...other}
|
|
160
156
|
mask="(#00) 000-0000"
|
|
161
|
-
definitions={{
|
|
162
|
-
'#': /[1-9]/,
|
|
163
|
-
}}
|
|
157
|
+
definitions={{ '#': /[1-9]/ }}
|
|
164
158
|
inputRef={ref}
|
|
165
159
|
onAccept={(value: any) => onChange({ target: { name: props.name, value } })}
|
|
166
160
|
overwrite
|
|
@@ -176,12 +170,7 @@ const NumericFormatCustom = forwardRef<NumericFormatProps, CustomProps>(function
|
|
|
176
170
|
{...other}
|
|
177
171
|
getInputRef={ref}
|
|
178
172
|
onValueChange={(values) => {
|
|
179
|
-
onChange({
|
|
180
|
-
target: {
|
|
181
|
-
name: props.name,
|
|
182
|
-
value: values.value,
|
|
183
|
-
},
|
|
184
|
-
});
|
|
173
|
+
onChange({ target: { name: props.name, value: values.value } });
|
|
185
174
|
}}
|
|
186
175
|
thousandSeparator
|
|
187
176
|
valueIsNumericString
|
|
@@ -251,16 +240,10 @@ export const _InputMasking: StoryObj<typeof TextField> = {
|
|
|
251
240
|
|
|
252
241
|
// ---------------------------------------
|
|
253
242
|
|
|
254
|
-
const [values, setValues] = useState({
|
|
255
|
-
textmask: '(100) 000-0000',
|
|
256
|
-
numberformat: '1320',
|
|
257
|
-
});
|
|
243
|
+
const [values, setValues] = useState({ textmask: '(100) 000-0000', numberformat: '1320' });
|
|
258
244
|
|
|
259
245
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
260
|
-
setValues({
|
|
261
|
-
...values,
|
|
262
|
-
[event.target.name]: event.target.value,
|
|
263
|
-
});
|
|
246
|
+
setValues({ ...values, [event.target.name]: event.target.value });
|
|
264
247
|
};
|
|
265
248
|
|
|
266
249
|
return (
|
|
@@ -306,9 +289,26 @@ export const _Select: StoryObj<typeof TextField> = {
|
|
|
306
289
|
</TextField>
|
|
307
290
|
);
|
|
308
291
|
},
|
|
309
|
-
args: {
|
|
310
|
-
|
|
292
|
+
args: { label: 'Select' },
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export const _SelectPlaceholder: StoryObj<typeof TextField> = {
|
|
296
|
+
render: (args: TextFieldProps) => {
|
|
297
|
+
const [count, setCount] = useState('');
|
|
298
|
+
|
|
299
|
+
const handleChange = (event: SelectChangeEvent) => {
|
|
300
|
+
setCount(event.target.value as string);
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
return (
|
|
304
|
+
<TextField value={count} select SelectProps={{ onChange: handleChange }} {...args}>
|
|
305
|
+
<MenuItem value={10}>10</MenuItem>
|
|
306
|
+
<MenuItem value={20}>20</MenuItem>
|
|
307
|
+
<MenuItem value={30}>30</MenuItem>
|
|
308
|
+
</TextField>
|
|
309
|
+
);
|
|
311
310
|
},
|
|
311
|
+
args: { label: 'Select', placeholder: 'Select...' },
|
|
312
312
|
};
|
|
313
313
|
|
|
314
314
|
export const _MultiSelect: StoryObj<typeof TextField> = {
|
|
@@ -351,7 +351,5 @@ export const _MultiSelect: StoryObj<typeof TextField> = {
|
|
|
351
351
|
</TextField>
|
|
352
352
|
);
|
|
353
353
|
},
|
|
354
|
-
args: {
|
|
355
|
-
label: 'MultiSelect',
|
|
356
|
-
},
|
|
354
|
+
args: { label: 'MultiSelect' },
|
|
357
355
|
};
|
|
@@ -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,55 @@ 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/20')).toBeTruthy();
|
|
18
|
+
|
|
19
|
+
const input = getByTestId('testTextField');
|
|
20
|
+
fireEvent.change(input, { target: { value: 'Some Text' } });
|
|
21
|
+
|
|
22
|
+
expect(getByText('9/20')).toBeTruthy();
|
|
23
|
+
|
|
24
|
+
fireEvent.change(input, { target: { value: "Some More Text that doesn't fit" } });
|
|
25
|
+
|
|
26
|
+
expect(getByTitle('Error')).toBeTruthy();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('should render character counter successfully via slotProps', () => {
|
|
30
|
+
const { getByText, getByTestId, getByTitle } = render(
|
|
31
|
+
<TextField
|
|
32
|
+
label="Test"
|
|
33
|
+
showCharacterCount
|
|
34
|
+
slotProps={{ htmlInput: { 'data-testid': 'testTextField', maxLength: 20 } }}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
expect(getByText('0/20')).toBeTruthy();
|
|
38
|
+
|
|
39
|
+
const input = getByTestId('testTextField');
|
|
40
|
+
fireEvent.change(input, { target: { value: 'Some Text' } });
|
|
41
|
+
|
|
42
|
+
expect(getByText('9/20')).toBeTruthy();
|
|
43
|
+
|
|
44
|
+
fireEvent.change(input, { target: { value: "Some More Text that doesn't fit" } });
|
|
45
|
+
|
|
46
|
+
expect(getByTitle('Error')).toBeTruthy();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('TextField select placeholder', () => {
|
|
50
|
+
test('should render select placeholder successfully', () => {
|
|
51
|
+
const { getByText } = render(
|
|
52
|
+
<TextField label="Test" select placeholder="Select...">
|
|
53
|
+
<MenuItem value={10}>10</MenuItem>
|
|
54
|
+
<MenuItem value={20}>20</MenuItem>
|
|
55
|
+
<MenuItem value={30}>30</MenuItem>
|
|
56
|
+
</TextField>
|
|
57
|
+
);
|
|
58
|
+
expect(getByText('Select...')).toBeTruthy();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
9
61
|
});
|
package/src/lib/TextField.tsx
CHANGED
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
SelectPropOverrides,
|
|
12
12
|
SelectProps,
|
|
13
13
|
} from '@availity/mui-form-utils';
|
|
14
|
+
import { Grid } from '@availity/mui-layout';
|
|
15
|
+
import { styled } from '@mui/material/styles';
|
|
14
16
|
|
|
15
17
|
export type TextFieldProps = {
|
|
16
18
|
FormHelperTextProps?: FormHelperTextProps;
|
|
@@ -19,33 +21,82 @@ export type TextFieldProps = {
|
|
|
19
21
|
SelectProps?: SelectProps;
|
|
20
22
|
/** If `true`, the input will take up the full width of its container. @default true */
|
|
21
23
|
fullWidth?: boolean;
|
|
24
|
+
/** if `true`, the character counter will display. The maxLength is taken from the `inputProps.maxLength` prop. @default false */
|
|
25
|
+
showCharacterCount?: boolean;
|
|
22
26
|
} & Pick<FormLabelProps, 'helpTopicId'> &
|
|
23
|
-
Omit<MuiTextFieldProps, 'fullWidth' | 'variant'
|
|
27
|
+
Omit<MuiTextFieldProps, 'fullWidth' | 'variant'>;
|
|
28
|
+
|
|
29
|
+
const SelectPlaceholder = styled('span', {
|
|
30
|
+
name: 'MuiTextField',
|
|
31
|
+
slot: 'SelectPlaceholder',
|
|
32
|
+
overridesResolver: (props, styles) => styles.avFilled,
|
|
33
|
+
})(({ theme }) => ({ opacity: 1, color: theme.palette.grey[400] }));
|
|
24
34
|
|
|
25
35
|
export const TextField = forwardRef<HTMLDivElement | HTMLInputElement, TextFieldProps>((props, ref) => {
|
|
26
|
-
const {
|
|
27
|
-
|
|
36
|
+
const {
|
|
37
|
+
InputProps,
|
|
38
|
+
helpTopicId,
|
|
39
|
+
InputLabelProps,
|
|
40
|
+
FormHelperTextProps,
|
|
41
|
+
required,
|
|
42
|
+
SelectProps,
|
|
43
|
+
inputProps,
|
|
44
|
+
helperText,
|
|
45
|
+
showCharacterCount = false,
|
|
46
|
+
...rest
|
|
47
|
+
} = props;
|
|
28
48
|
const [openDetected, setOpenDetected] = useState(false);
|
|
49
|
+
const [charCount, setCharCount] = useState(0);
|
|
50
|
+
|
|
51
|
+
const resolvedProps = (props: Record<string, unknown>) =>
|
|
52
|
+
!props || Object.keys(props).length === 0 ? undefined : props;
|
|
29
53
|
|
|
30
54
|
return (
|
|
31
55
|
<MuiTextField
|
|
32
56
|
{...rest}
|
|
57
|
+
onChange={(event) => {
|
|
58
|
+
setCharCount(event.target.value.length);
|
|
59
|
+
if (rest.onChange) rest.onChange(event);
|
|
60
|
+
}}
|
|
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 }}
|
|
33
75
|
slotProps={{
|
|
34
|
-
input: { ...InputProps, ...InputPropOverrides },
|
|
35
|
-
htmlInput: { 'aria-required': required, ...inputProps },
|
|
36
|
-
select: {
|
|
76
|
+
input: resolvedProps({ ...InputProps, ...InputPropOverrides, ...rest.slotProps?.input }),
|
|
77
|
+
htmlInput: resolvedProps({ 'aria-required': required, ...inputProps, ...rest.slotProps?.htmlInput }),
|
|
78
|
+
select: resolvedProps({
|
|
79
|
+
displayEmpty: !!rest.placeholder,
|
|
80
|
+
renderValue: (value: unknown) =>
|
|
81
|
+
rest.placeholder && (!value || (Array.isArray(value) && value.length === 0)) ? (
|
|
82
|
+
<SelectPlaceholder className="MuiSelect-placeholder">{rest.placeholder}</SelectPlaceholder>
|
|
83
|
+
) : (
|
|
84
|
+
value
|
|
85
|
+
),
|
|
37
86
|
...SelectProps,
|
|
38
87
|
...SelectPropOverrides,
|
|
39
88
|
...SelectAccessibilityOverrides(openDetected, setOpenDetected, SelectProps?.open),
|
|
40
|
-
|
|
41
|
-
|
|
89
|
+
...rest.slotProps?.select,
|
|
90
|
+
}),
|
|
91
|
+
inputLabel: resolvedProps({
|
|
42
92
|
component: FormLabel,
|
|
43
93
|
helpTopicId: helpTopicId,
|
|
44
94
|
required,
|
|
45
95
|
shrink: true,
|
|
46
96
|
...InputLabelProps,
|
|
47
|
-
|
|
48
|
-
|
|
97
|
+
...rest.slotProps?.inputLabel,
|
|
98
|
+
}),
|
|
99
|
+
formHelperText: resolvedProps({ component: 'div', ...FormHelperTextProps, ...rest.slotProps?.formHelperText }),
|
|
49
100
|
}}
|
|
50
101
|
ref={ref}
|
|
51
102
|
/>
|