-
Notifications
You must be signed in to change notification settings - Fork 0
/
ValidationFeedback.tsx
101 lines (87 loc) · 2.98 KB
/
ValidationFeedback.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import React, { PropsWithChildren, useContext, useEffect, useState } from 'react'
import { useDebouncedCallback } from 'use-debounce'
import {
ASYNC_VALIDATION_PENDING,
ASYNC_VALIDATION_DEBOUNCE_PENDING,
INVALID_NO_FEEDBACK,
ValidatorOutput,
} from '@interface-technologies/iti-react-core'
import { ItiReactContext } from '../ItiReactContext'
/**
* Used to show a loading indicator when async validation is progress. Uses debouncing
* so the loading indicator is never displayed if the async validation completes quickly.
*/
export function useDebouncedAsyncValidationPending(propsPending: boolean): boolean {
const [pending, setPending] = useState(false)
const setPendingDebounced = useDebouncedCallback(setPending, 1000)
useEffect(() => {
if (propsPending) {
setPendingDebounced(true)
} else {
setPendingDebounced.cancel()
setPending(false)
}
}, [propsPending, setPendingDebounced])
return pending
}
export type ValidationFeedbackProps = PropsWithChildren<{
validatorOutput: ValidatorOutput
showValidation: boolean
renderLoadingIndicator?: () => React.ReactNode
className?: string
}>
/**
* Displays validation feedback below an input. Used by `ValidatedInput`, .etc.
*
* You usually won't use this directly unless creating your own input component.
*/
export function ValidationFeedback({
validatorOutput,
showValidation,
children,
renderLoadingIndicator,
className,
}: ValidationFeedbackProps): JSX.Element {
const contextRenderLoadingIndicator =
useContext(ItiReactContext).renderLoadingIndicator
renderLoadingIndicator = renderLoadingIndicator ?? contextRenderLoadingIndicator
const debouncedAsyncValidationPending = useDebouncedAsyncValidationPending(
validatorOutput === ASYNC_VALIDATION_PENDING
)
let feedback: React.ReactNode
if (showValidation) {
if (validatorOutput === ASYNC_VALIDATION_PENDING) {
if (debouncedAsyncValidationPending) {
feedback = (
<div className="pending-feedback">
{renderLoadingIndicator()} Validating...
</div>
)
}
} else if (
validatorOutput &&
validatorOutput !== INVALID_NO_FEEDBACK &&
validatorOutput !== ASYNC_VALIDATION_DEBOUNCE_PENDING
) {
feedback = <div className="invalid-feedback">{validatorOutput}</div>
}
}
const classes = ['validated-input']
if (className) classes.push(className)
return (
<div className={classes.join(' ')}>
{children}
{feedback}
</div>
)
}
/**
* Returns a Bootstrap validation class depending on `valid` and `showValidation`.
*/
export function getValidationClass(valid: boolean, showValidation: boolean): string {
if (showValidation) {
if (valid) return 'is-valid'
return 'is-invalid'
}
return ''
}