I have created an <Input />
component with some base - helper components for better usability and minimizing the code inside the main component. However, I would like to create also a <textarea />
HTML element. I am using React.js with TypeScript. So, the props and most specifically the input event props overwrite each other, and the compiler complains. Is there anything that I can do, except for writing the same code inside the <Multiline />
component?
Note: I am wrapping the component with React.forwardRef
for forwarding and passing a ref
to the input component
UPDATE: Add code snippet
Input/Input.tsx
export default class Input extends React.Component<FProps, State> {
static displayName = "Input";
state: State = {
id: this.props.id || "",
value: this.props.value || this.props.defaultValue || "",
};
componentDidMount() {
if (!this.state.id) {
this.setState(prevState => ({ ...prevState, id: generateInputId() }));
}
}
handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newVal = e.target.value;
const { onChange } = this.props;
this.setState({ value: newVal });
if (onChange) {
onChange(e);
}
};
render() {
const { id, value } = this.state;
return (
<InputContextProvider value={{ ...this.props, value, id, onChange: this.handleChange }}>
<InputContainer>
<FieldContainer />
</InputContainer>
</InputContextProvider>
);
}
}
Input/helpers/FieldContainer.tsx
export const FieldContainer: React.FunctionComponent = () => {
const {
value: propsValue,
type,
label,
id,
defaultValue,
className,
state = "default",
placeholder,
floatingplaceholder,
prefix,
suffix,
characterLimit,
maxLength,
allowClear,
onChange,
forwardref,
...rest
} = useInputContext();
const isDisabled = useDisabled(rest, state);
const [value, setValue] = useState<string | number>(propsValue || defaultValue || "");
const [prefixWidth, setPrefixWidth] = useState<number>(0);
const [suffixWidth, setSuffixWidth] = useState<number>(0);
const prefixRef = useRef<HTMLDivElement>(null);
const suffixRef = useRef<HTMLDivElement>(null);
// TODO: Replace with <Icon /> component
if (allowClear) {
// suffix = <>
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newVal = e.target.value;
if (characterLimit && maxLength && String(newVal).length > maxLength) {
return;
}
setValue(newVal);
if (onChange) {
onChange(e);
}
};
useEffect(() => {
if (prefixRef && prefixRef.current) {
const prefixWidth = prefixRef.current.offsetWidth;
setPrefixWidth(prefixWidth);
}
if (suffixRef && suffixRef.current) {
const suffixWidth = suffixRef.current.offsetWidth;
setSuffixWidth(suffixWidth);
}
}, [prefix, suffix]);
return (
<OuterFieldContainer prefixWidth={prefixWidth}>
<FixGroup group={prefix} ref={prefixRef} position="left" />
<Base
id={id}
value={value}
className={className}
state={state}
placeholder={floatingplaceholder === false && placeholder ? placeholder : undefined}
disabled={isDisabled}
onChange={e => handleChange(e)}
ref={forwardref}
style={{
paddingLeft: prefix ? `${(prefixWidth + DEFAULT_PADDING) / EM_REM_MULTIPLIER}em` : "",
paddingRight: suffix ? `${(suffixWidth + DEFAULT_PADDING) / EM_REM_MULTIPLIER}em` : "",
}}
maxLength={maxLength}
{...rest}
/>
<FixGroup group={suffix} ref={suffixRef} position="right" />
</OuterFieldContainer>
);
};
Input/helpers/Base/Base.tsx
export const Base = React.forwardRef<HTMLInputElement>((componentProps, ref) => {
const {
id: propsId,
value,
placeholder,
type,
floatingplaceholder,
state = "default",
onChange,
style,
children: propsChildren,
characterLimit,
overrideOnChange,
allowClear,
...props
} = useInputContext();
const id = useInputId(propsId);
const [classNames, rest] = useClassnames("input", props, { stateToRemove: { state } });
const isDisabled = useDisabled(props, state);
// return type === "textarea" ? (
// <textarea
// id={id}
// className={classNames}
// value={value || ""}
// placeholder={placeholder}
// disabled={isDisabled}
// aria-disabled={isDisabled}
// aria-label={placeholder}
// ref={ref}
// // * Enable / disabled the Grammarly extension
// // data-gramm="false"
// cols={28}
// rows={5}
// {...rest}
// />
// ) : (
return (
<input
id={id}
type={type}
className={classNames}
value={value}
placeholder={!floatingplaceholder ? placeholder : undefined}
disabled={isDisabled}
aria-disabled={isDisabled}
aria-label={placeholder}
ref={ref}
data-hasfloatingplaceholder={floatingplaceholder}
data-testid="input"
onChange={e => onChange && onChange(e)}
style={style}
{...rest}
/>
);
});