Skip to content

Commit bb65b93

Browse files
authored
Add sanitizeField method (#24)
* Add sanitizeField method * Bump version number
1 parent edd66ff commit bb65b93

File tree

4 files changed

+86
-8
lines changed

4 files changed

+86
-8
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ const {
9494
setFieldError,
9595
focusField,
9696
resetField,
97+
sanitizeField,
9798
validateField,
9899
listenFields,
99100
resetForm,
@@ -259,6 +260,14 @@ type resetField = (
259260
) => void;
260261
```
261262

263+
#### sanitizeField
264+
265+
Sanitize the field value.
266+
267+
```tsx
268+
type sanitizeField = (name: FieldName) => void;
269+
```
270+
262271
#### validateField
263272

264273
Once you manually call validation, the field automatically switches to _talkative_ state.

__tests__/sanitization.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { fireEvent, render, screen } from "@testing-library/react";
2+
import * as React from "react";
3+
import { test } from "vitest";
4+
import { useForm } from "../src";
5+
6+
test("input sanitization is perform onBlur", async () => {
7+
const Test = () => {
8+
const { Field, sanitizeField } = useForm({
9+
firstName: {
10+
initialValue: "",
11+
sanitize: (value) => value.trim(),
12+
},
13+
});
14+
15+
return (
16+
<form onSubmit={(e) => e.preventDefault()}>
17+
<Field name="firstName">
18+
{({ ref, onBlur, onChange, value }) => (
19+
<>
20+
<label htmlFor="firstName">First name</label>
21+
22+
<input
23+
ref={ref}
24+
type="text"
25+
id="firstName"
26+
value={value}
27+
onBlur={() => {
28+
sanitizeField("firstName");
29+
onBlur();
30+
}}
31+
onChange={(e) => {
32+
e.preventDefault();
33+
onChange(e.target.value);
34+
}}
35+
/>
36+
37+
<span>Sanitized value: "{value}"</span>
38+
</>
39+
)}
40+
</Field>
41+
</form>
42+
);
43+
};
44+
45+
render(<Test />);
46+
47+
const input = await screen.findByLabelText("First name");
48+
49+
fireEvent.focus(input);
50+
fireEvent.input(input, { target: { value: " Nicolas " } });
51+
fireEvent.blur(input);
52+
53+
await screen.findByText('Sanitized value: "Nicolas"');
54+
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-ux-form",
3-
"version": "1.3.0",
3+
"version": "1.4.0",
44
"license": "MIT",
55
"description": "A simple, fast and opinionated form library for React & React Native focusing on UX.",
66
"author": "Mathieu Acthernoene <[email protected]>",

src/index.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export type Form<Values extends AnyRecord, ErrorMessage = string> = {
9595

9696
focusField: (name: keyof Values) => void;
9797
resetField: (name: keyof Values, options?: { feedbackOnly?: boolean }) => void;
98+
sanitizeField: (name: keyof Values) => void;
9899
validateField: (name: keyof Values) => Promise<ErrorMessage | void>;
99100

100101
listenFields: <N extends keyof Values>(
@@ -268,7 +269,7 @@ export const useForm = <Values extends AnyRecord, ErrorMessage = string>(
268269
return debounced;
269270
};
270271

271-
const runCallbacks = (name: Name): void => {
272+
const runRenderCallbacks = (name: Name): void => {
272273
callbacks.current[name].forEach((callback) => callback());
273274
};
274275

@@ -335,14 +336,14 @@ export const useForm = <Values extends AnyRecord, ErrorMessage = string>(
335336
}
336337

337338
setError(name, error);
338-
runCallbacks(name);
339+
runRenderCallbacks(name);
339340

340341
return error;
341342
}
342343

343344
if (!debounced) {
344345
setValidating(name);
345-
runCallbacks(name);
346+
runRenderCallbacks(name);
346347
}
347348

348349
return promiseOrError
@@ -358,7 +359,7 @@ export const useForm = <Values extends AnyRecord, ErrorMessage = string>(
358359
}
359360

360361
setError(name, error);
361-
runCallbacks(name);
362+
runRenderCallbacks(name);
362363

363364
return error;
364365
})
@@ -390,7 +391,7 @@ export const useForm = <Values extends AnyRecord, ErrorMessage = string>(
390391
const setFieldError: Contract["setFieldError"] = (name, error) => {
391392
setError(name, error);
392393
setTalkative(name);
393-
runCallbacks(name);
394+
runRenderCallbacks(name);
394395
};
395396

396397
const focusField: Contract["focusField"] = (name) => {
@@ -410,7 +411,19 @@ export const useForm = <Values extends AnyRecord, ErrorMessage = string>(
410411
validity: { tag: "unknown" },
411412
}));
412413

413-
runCallbacks(name);
414+
runRenderCallbacks(name);
415+
};
416+
417+
const sanitizeField: Contract["sanitizeField"] = (name) => {
418+
const sanitize = getSanitize(name);
419+
420+
setState(name, ({ talkative, value, validity }) => ({
421+
value: sanitize(value),
422+
talkative,
423+
validity,
424+
}));
425+
426+
runRenderCallbacks(name);
414427
};
415428

416429
const validateField: Contract["validateField"] = (name) => {
@@ -468,7 +481,7 @@ export const useForm = <Values extends AnyRecord, ErrorMessage = string>(
468481
}
469482

470483
setValidating(name);
471-
runCallbacks(name);
484+
runRenderCallbacks(name);
472485

473486
timeouts.current[name] = setTimeout(() => {
474487
if (isMounted(name)) {
@@ -618,6 +631,7 @@ export const useForm = <Values extends AnyRecord, ErrorMessage = string>(
618631
setFieldError,
619632
focusField,
620633
resetField,
634+
sanitizeField,
621635
validateField,
622636
listenFields,
623637

@@ -750,6 +764,7 @@ export const useForm = <Values extends AnyRecord, ErrorMessage = string>(
750764
setFieldError: api.setFieldError,
751765
focusField: api.focusField,
752766
resetField: api.resetField,
767+
sanitizeField: api.sanitizeField,
753768
validateField: api.validateField,
754769
listenFields: api.listenFields,
755770

0 commit comments

Comments
 (0)