Skip to content

Commit 538f24b

Browse files
author
Hector Arce De Las Heras
committed
Enhance Handling of Input Values in useInput Hook
This commit improves the handling of input values in the useInput hook, particularly in the context of formatting, validation, and control. Changes include: The truncateValue function in useInput.ts now takes into account the locale or formatNumber.locale in addition to limitedValue and maxDecimals. This enhances the truncation of the value with maximum decimals. The handleChangeInternal function in useInput.ts has been refactored to use a new local variable eventValue instead of directly manipulating event.target.value. This improves code readability and maintainability. The condition to assign valueControlled to event.target.value in useInput.ts has been modified to exclude InputTypeType.NUMBER. This improves the handling of input values for different types. These changes enhance the functionality and readability of the useInput hook.
1 parent ee6fe3e commit 538f24b

File tree

3 files changed

+98
-22
lines changed

3 files changed

+98
-22
lines changed

src/hooks/useInput/__tests__/helpers.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { limitValue } from '../helpers/limitValue';
2+
import { modifyInputNumberValue } from '../helpers/modifyInputNumberValue';
23

34
describe('Input hook helpers', () => {
45
test('LimitValue should return same value', () => {
@@ -19,3 +20,25 @@ describe('Input hook helpers', () => {
1920
expect(limitValue(value, '1', '5', undefined)).toBe('1');
2021
});
2122
});
23+
24+
describe('modifyInputNumberValue', () => {
25+
it('should return true if the value is less than min', () => {
26+
const result = modifyInputNumberValue({ value: 5, min: 10 });
27+
expect(result).toBe(true);
28+
});
29+
30+
it('should return true if the value is greater than max', () => {
31+
const result = modifyInputNumberValue({ value: 15, max: 10 });
32+
expect(result).toBe(true);
33+
});
34+
35+
it('should return true if the length of the value is greater than maxLength', () => {
36+
const result = modifyInputNumberValue({ value: '12345', maxLength: 4 });
37+
expect(result).toBe(true);
38+
});
39+
40+
it('should return false if the value is within the min and max range and does not exceed maxLength', () => {
41+
const result = modifyInputNumberValue({ value: 7, min: 5, max: 10, maxLength: 5 });
42+
expect(result).toBe(false);
43+
});
44+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
type ModifyInputNumberValueProps = {
2+
value: string | number;
3+
min?: number;
4+
max?: number;
5+
maxLength?: number;
6+
};
7+
8+
export const modifyInputNumberValue = ({
9+
value,
10+
min,
11+
max,
12+
maxLength,
13+
}: ModifyInputNumberValueProps): boolean => {
14+
if (min && Number(value) < min) {
15+
return true;
16+
} else if (max && Number(value) > max) {
17+
return true;
18+
} else if (maxLength && String(value).length > maxLength) {
19+
return true;
20+
}
21+
return false;
22+
};

src/hooks/useInput/useInput.ts

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
import { matchInputValue } from '@/utils/maskUtility/mask.utility';
3636

3737
import { limitValue } from './helpers/limitValue';
38+
import { modifyInputNumberValue } from './helpers/modifyInputNumberValue';
3839
import { ParamsTypeInputHook, ReturnTypeInputHook } from './types/inputHook';
3940

4041
export const useInput = (props: ParamsTypeInputHook): ReturnTypeInputHook => {
@@ -128,17 +129,41 @@ export const useInput = (props: ParamsTypeInputHook): ReturnTypeInputHook => {
128129
}
129130
}, [value]);
130131

132+
const truncateFloatValue = value => {
133+
if (value === '') {
134+
return;
135+
}
136+
const hasMark = value.match(/[^a-zA-Z0-9]/g);
137+
138+
const isLastCharAMark =
139+
hasMark &&
140+
props.maxLength &&
141+
value.length >= props.maxLength &&
142+
value[props.maxLength - 1] === hasMark[0];
143+
144+
return isLastCharAMark && props.type === InputTypeType.NUMBER
145+
? value.replace(hasMark[0], '')
146+
: value;
147+
};
148+
131149
const controlValue = value => {
132150
// control character limit
133151
const limitedValue = value && limitValue(value, props.min, props.max, props.maxLength);
134152

135153
// truncate the value with maximun of decimals
136154
const truncateValue =
137155
props.truncate && props.maxDecimals !== null && props.maxDecimals !== undefined
138-
? truncatedValue(String(limitedValue), props.maxDecimals)
156+
? truncatedValue(
157+
String(limitedValue),
158+
props.maxDecimals,
159+
props.locale || props.formatNumber?.locale
160+
)
139161
: limitedValue;
140-
handleSetValue(truncateValue);
141-
return truncateValue;
162+
163+
const valueTruncated = truncateFloatValue(truncateValue);
164+
165+
handleSetValue(valueTruncated);
166+
return valueTruncated;
142167
};
143168

144169
// add thousand separator to the value
@@ -154,36 +179,42 @@ export const useInput = (props: ParamsTypeInputHook): ReturnTypeInputHook => {
154179
};
155180

156181
const handleChangeInternal: React.ChangeEventHandler<HTMLInputElement> = event => {
182+
let eventValue = event.target.value;
157183
// format value with the mask
158184
if (props.maskType) {
159-
let newMaskedValue = cleanInputValue(event.target.value, props.maskType);
185+
let newMaskedValue = cleanInputValue(eventValue, props.maskType);
160186
if (props.mask) {
161187
newMaskedValue = formatMask(newMaskedValue, props.mask);
162188
}
163-
event.target.value = newMaskedValue;
189+
eventValue = newMaskedValue;
164190
} else if (props.regex) {
165-
const newMaskedValue = matchInputValue(String(value), event.target.value, props.regex);
166-
event.target.value = newMaskedValue;
167-
}
168-
if (props.truncate && props.maxDecimals !== null && props.maxDecimals !== undefined) {
169-
event.target.value = truncatedValue(
170-
String(event.target.value),
171-
props.maxDecimals,
172-
props.locale || props.formatNumber?.locale
173-
);
191+
const newMaskedValue = matchInputValue(String(value), eventValue, props.regex);
192+
eventValue = newMaskedValue;
174193
}
175194

176195
// key validation
177196
if (props.errorExecution === ERROR_EXECUTION.ON_CHANGE && props.keyValidation) {
178-
props.onError?.(!validationValue(props.keyValidation, event.target.value));
197+
props.onError?.(!validationValue(props.keyValidation, eventValue));
179198
}
180199

181200
// limit or truncate the value
182-
const valueControlled = controlValue(event.target.value);
201+
const valueControlled = controlValue(eventValue);
183202

184203
// check internal validations
185204
if (props.type !== InputTypeType.DATE) {
186-
checkInternalValidations(event.target.value);
205+
checkInternalValidations(eventValue);
206+
}
207+
208+
if (
209+
modifyInputNumberValue({
210+
value: eventValue,
211+
min: props.min,
212+
max: props.max,
213+
maxLength: props.maxLength,
214+
}) ||
215+
props.type !== InputTypeType.NUMBER
216+
) {
217+
event.target.value = valueControlled;
187218
}
188219

189220
// value = previous value
@@ -192,10 +223,6 @@ export const useInput = (props: ParamsTypeInputHook): ReturnTypeInputHook => {
192223
return;
193224
}
194225

195-
if (props.type === InputTypeType.NUMBER) {
196-
event.target.value = valueControlled;
197-
}
198-
199226
props.onChange?.(event);
200227
};
201228

@@ -223,10 +250,14 @@ export const useInput = (props: ParamsTypeInputHook): ReturnTypeInputHook => {
223250
handleSetValue(event.target.value);
224251
}
225252
// transform the string value to a number format with dot as decimal separator to avoid errors
226-
event.target.value = convertDecimalSeparator(
253+
const decimalNumber = convertDecimalSeparator(
227254
event.target.value,
228255
getDecimalSeparator(props.locale || props.formatNumber?.locale)
229256
);
257+
258+
if (!isNaN(parseFloat(decimalNumber)) && isFinite(parseFloat(decimalNumber))) {
259+
event.target.value = decimalNumber;
260+
}
230261
props.onFocus?.(event);
231262
setFocus(true);
232263
};

0 commit comments

Comments
 (0)