on master help sign in
feat: implement hasbeentouched for inputs and integrate into error display
e471cdbMatt Condon (shrugs)
src/indigo-react/components/CheckboxInput.js+3−2
@@ -19,6 +19,7 @@ export default function CheckboxInput({
19 pass,
19 pass,
20 visiblyPassed,
20 visiblyPassed,
21 error,
21 error,
22+ hintError,
22 data,
23 data,
23 bind,
24 bind,
24 autoFocus,
25 autoFocus,
@@ -59,8 +60,8 @@ export default function CheckboxInput({
59 {
60 {
60 'b-green3': visiblyPassed,
61 'b-green3': visiblyPassed,
61 'b-black': focused && !visiblyPassed,
62 'b-black': focused && !visiblyPassed,
62− 'b-yellow3': !focused && error,
63+ 'b-yellow3': !focused && hintError,
63− 'b-gray3': !focused && !error && !visiblyPassed,
64+ 'b-gray3': !focused && !hintError && !visiblyPassed,
64 }
65 }
65 )}
66 )}
66 id={name}
67 id={name}
src/indigo-react/components/Input.js+3−2
@@ -21,6 +21,7 @@ export default function Input({
21 pass,
21 pass,
22 visiblyPassed,
22 visiblyPassed,
23 error,
23 error,
24+ hintError,
24 data,
25 data,
25 bind,
26 bind,
26 autoFocus,
27 autoFocus,
@@ -84,8 +85,8 @@ export default function Input({
84 {
85 {
85 'b-green3': visiblyPassed,
86 'b-green3': visiblyPassed,
86 'b-black': focused && !visiblyPassed,
87 'b-black': focused && !visiblyPassed,
87− 'b-yellow3': !focused && error,
88+ 'b-yellow3': !focused && hintError,
88− 'b-gray3': !focused && !error && !visiblyPassed,
89+ 'b-gray3': !focused && !hintError && !visiblyPassed,
89 }
90 }
90 // TODO: inputClassName ?
91 // TODO: inputClassName ?
91 )}
92 )}
src/indigo-react/components/SelectInput.js+3−2
@@ -25,6 +25,7 @@ export default function SelectInput({
25 pass,
25 pass,
26 visiblyPassed,
26 visiblyPassed,
27 error,
27 error,
28+ hintError,
28 data,
29 data,
29 bind,
30 bind,
30 autoFocus,
31 autoFocus,
@@ -92,8 +93,8 @@ export default function SelectInput({
92 {
93 {
93 'b-green3': visiblyPassed,
94 'b-green3': visiblyPassed,
94 'b-black': focused && !visiblyPassed,
95 'b-black': focused && !visiblyPassed,
95− 'b-yellow3': !focused && error,
96+ 'b-yellow3': !focused && hintError,
96− 'b-gray3': !focused && !error && !visiblyPassed,
97+ 'b-gray3': !focused && !hintError && !visiblyPassed,
97 }
98 }
98 )}
99 )}
99 id={name}
100 id={name}
src/indigo-react/lib/useForm.js+39−13
@@ -61,6 +61,9 @@ export default function useForm(inputConfigs = []) {
61 // track whether or not input has been focused
61 // track whether or not input has been focused
62 const [hasBeenFocused, setHasBeenFocused] = useSetState({});
62 const [hasBeenFocused, setHasBeenFocused] = useSetState({});
63
63
64+ // track whether or not the input has been touched
65+ const [hasBeenTouched, setHasBeenTouched] = useSetState({});
66+
64 // build fn that transforms a value by input name
67 // build fn that transforms a value by input name
65 const transform = useCallback(
68 const transform = useCallback(
66 (name, value) => compose(...get(byName, [name, 'transformers'], []))(value),
69 (name, value) => compose(...get(byName, [name, 'transformers'], []))(value),
@@ -97,8 +100,11 @@ export default function useForm(inputConfigs = []) {
97
100
98 // on change, transform and set value
101 // on change, transform and set value
99 const onChange = useCallback(
102 const onChange = useCallback(
103+ name => e => {
100− name => e => setValue(name, getValue(name, e)), //
104+ setValue(name, getValue(name, e));
105+ setHasBeenTouched({ [name]: true });
106+ }, //
101− [setValue, getValue]
107+ [setValue, getValue, setHasBeenTouched]
102 );
108 );
103
109
104 // on focus, update focus
110 // on focus, update focus
@@ -126,27 +132,45 @@ export default function useForm(inputConfigs = []) {
126 const datas = useMemo(() => validations.map(v => v.data), [validations]);
132 const datas = useMemo(() => validations.map(v => v.data), [validations]);
127
133
128 // the input has errored if it
134 // the input has errored if it
135+ // 1) did not pass validation and has an error text
136+ // 2) or has a specific error from parent state
137+ // and then we store that in errorTexts[i]
138+ // <string | falsy>[]
139+ const errorTexts = useMemo(
140+ () =>
141+ configs.map((config, i) => validations[i].error || config.error || false),
142+ [configs, validations]
143+ );
144+
145+ // we should hint at an error if it
129 // 1) has been focused before
146 // 1) has been focused before
130− // 2)
131− // a) did not pass validation and has an error text
147+ // 2) and has an error
132− // b) or has a specific error from parent state
148+ // boolean[]
133− // (and we implicitly return the error text at the end)
134− const errors = useMemo(
149+ const hintErrors = useMemo(
135 () =>
150 () =>
136 configs.map(
151 configs.map(
137− (config, i) =>
138− hasBeenFocused[config.name] && //
152+ (config, i) => hasBeenFocused[config.name] && !!errorTexts[i]
139− (validations[i].error || config.error)
140 ),
153 ),
141− [configs, validations, hasBeenFocused]
154+ [configs, errorTexts, hasBeenFocused]
155+ );
156+
157+ // we should block on error (with potential reflow) if it
158+ // 1) has been touched
159+ // 2) and has an error
160+ // consumers should use this value to show error text and block on operations
161+ // <string | falsy>[]
162+ const errors = useMemo(
163+ () =>
164+ configs.map((config, i) => hasBeenTouched[config.name] && errorTexts[i]),
165+ [configs, errorTexts, hasBeenTouched]
142 );
166 );
143
167
144 // this input has passed if
168 // this input has passed if
145 // 1) it has passed local validation
169 // 1) it has passed local validation
146 // 2) there are no errors
170 // 2) there are no errors
147 const passes = useMemo(
171 const passes = useMemo(
148− () => validations.map((v, i) => v.pass && !errors[i]),
172+ () => validations.map((v, i) => v.pass && !errorTexts[i]),
149− [validations, errors]
173+ [validations, errorTexts]
150 );
174 );
151
175
152 // visibly tell the user that their input has passed if
176 // visibly tell the user that their input has passed if
@@ -176,6 +200,7 @@ export default function useForm(inputConfigs = []) {
176 pass: passes[i],
200 pass: passes[i],
177 visiblyPassed: visiblePasses[i],
201 visiblyPassed: visiblePasses[i],
178 error: errors[i],
202 error: errors[i],
203+ hintError: hintErrors[i],
179 focused: focuses[i],
204 focused: focuses[i],
180 autoFocus: autoFocus && !disabled,
205 autoFocus: autoFocus && !disabled,
181 disabled,
206 disabled,
@@ -198,6 +223,7 @@ export default function useForm(inputConfigs = []) {
198 visiblePasses,
223 visiblePasses,
199 passes,
224 passes,
200 errors,
225 errors,
226+ hintErrors,
201 focuses,
227 focuses,
202 values,
228 values,
203 onBlur,
229 onBlur,