From f3a561d207ad4066c405f7093dfbf9b3fcbc87d1 Mon Sep 17 00:00:00 2001 From: Pedro Guerrato Date: Thu, 10 Feb 2022 09:55:40 -0300 Subject: [PATCH 1/9] Solve custom function validation bug and add autocomplete --- .../components/inputs/basic/Input.stories.tsx | 29 +++++++- src/lib/components/inputs/basic/Input.tsx | 6 +- .../inputs/basic/password/Password.tsx | 10 +-- .../components/inputs/basic/search/Search.tsx | 1 + .../components/inputs/basic/shared/styles.tsx | 2 +- .../components/inputs/basic/shared/types.ts | 73 ++++++++++++++++--- src/lib/components/inputs/basic/text/Text.tsx | 2 + 7 files changed, 101 insertions(+), 22 deletions(-) diff --git a/src/lib/components/inputs/basic/Input.stories.tsx b/src/lib/components/inputs/basic/Input.stories.tsx index efd6e843..8b06d749 100644 --- a/src/lib/components/inputs/basic/Input.stories.tsx +++ b/src/lib/components/inputs/basic/Input.stories.tsx @@ -1,5 +1,5 @@ import { Story } from '@storybook/react' -import React from 'react' +import React, { FormEvent, FormEventHandler, SyntheticEvent } from 'react' import { Input } from './Input' import { InputProps } from './shared' @@ -8,7 +8,7 @@ export default { component: Input } -const Template = (args: InputProps) => ( +const Template: Story = (args: InputProps) => (
@@ -174,3 +174,28 @@ Search.args = { required: true, placeholder: 'Type your search' } + +const handleSubmit: FormEventHandler = (e: SyntheticEvent) => { + e.preventDefault() + + const formElements = e.currentTarget.elements as typeof e.currentTarget.elements & { + emailInput: { value: string } + } + + console.log(formElements) +} + +export const AutoComplete: Story = (args: InputProps) => ( +
+ + +
+) +AutoComplete.args = { + type: 'email', + id: 'emailInput', + label: 'E-mail', + required: true, + placeholder: 'Type your search', + autoComplete: 'email' +} diff --git a/src/lib/components/inputs/basic/Input.tsx b/src/lib/components/inputs/basic/Input.tsx index 0d90ec07..112324ca 100644 --- a/src/lib/components/inputs/basic/Input.tsx +++ b/src/lib/components/inputs/basic/Input.tsx @@ -86,7 +86,11 @@ export const Input = (props: InputProps) => { return ( - {!isEmpty(props.label) && {props.label}} + {!isEmpty(props.label) && ( + + {props.label} + + )} ) diff --git a/src/lib/components/inputs/basic/password/Password.tsx b/src/lib/components/inputs/basic/password/Password.tsx index 3018c097..c206b057 100644 --- a/src/lib/components/inputs/basic/password/Password.tsx +++ b/src/lib/components/inputs/basic/password/Password.tsx @@ -3,12 +3,7 @@ import { useInputValidation } from '../../../../hooks/useInputValidation' import { InputProps, InputType } from '../shared' import { ValidationInfo } from '../shared/ValidationInfo' -import { - ActionArea, - InputGroup, - StyledInput, - VerificationWithToggle -} from '../shared' +import { ActionArea, InputGroup, StyledInput, VerificationWithToggle } from '../shared' import { Icon } from '../../../icons' export const Password = (props: InputProps) => { @@ -47,7 +42,8 @@ export const Password = (props: InputProps) => { toggleType()} - {...props.inputStyles}> + {...props.inputStyles} + > {} diff --git a/src/lib/components/inputs/basic/search/Search.tsx b/src/lib/components/inputs/basic/search/Search.tsx index ff3b8496..6742cda4 100644 --- a/src/lib/components/inputs/basic/search/Search.tsx +++ b/src/lib/components/inputs/basic/search/Search.tsx @@ -25,6 +25,7 @@ export const Search = (props: InputProps) => { placeholder={props.placeholder} readOnly={props.readOnly} disabled={props.disabled} + autoComplete={props.autoComplete} {...props.inputStyles} /> diff --git a/src/lib/components/inputs/basic/shared/styles.tsx b/src/lib/components/inputs/basic/shared/styles.tsx index 5d6131c0..ca5eb4c6 100644 --- a/src/lib/components/inputs/basic/shared/styles.tsx +++ b/src/lib/components/inputs/basic/shared/styles.tsx @@ -56,7 +56,7 @@ export const StyledLabel = styled.label` export const GlobalInput = styled.input.attrs((props) => ({ type: props.type, - autocomplete: 'new-password' + autoComplete: props.autoComplete }))` box-sizing: border-box; cursor: ${(props) => (props.disabled ? 'not-allowed' : props.cursor)}; diff --git a/src/lib/components/inputs/basic/shared/types.ts b/src/lib/components/inputs/basic/shared/types.ts index da012064..6fb05723 100644 --- a/src/lib/components/inputs/basic/shared/types.ts +++ b/src/lib/components/inputs/basic/shared/types.ts @@ -8,6 +8,62 @@ export type InputType = Extract< 'checkbox' | 'email' | 'password' | 'radio' | 'search' | 'text' > +export type AutocompleteValues = + | 'off' + | 'on' + | 'name' + | 'honorific-prefix' + | 'given-name' + | 'additional-name' + | 'family-name' + | 'honorific-suffix' + | 'nickname' + | 'email' + | 'username' + | 'new-password' + | 'current-password' + | 'one-time-code' + | 'organization-title' + | 'organization' + | 'street-address' + | 'address-line1' + | 'address-line2' + | 'address-line3' + | 'address-level4' + | 'address-level3' + | 'address-level2' + | 'address-level1' + | 'country' + | 'country-name' + | 'postal-code' + | 'cc-name' + | 'cc-given-name' + | 'cc-additional-name' + | 'cc-family-name' + | 'cc-number' + | 'cc-exp' + | 'cc-exp-month' + | 'cc-exp-year' + | 'cc-csc' + | 'cc-type' + | 'transaction-currency' + | 'transaction-amount' + | 'language' + | 'bday' + | 'bday-day' + | 'bday-month' + | 'bday-year' + | 'sex' + | 'tel' + | 'tel-country-code' + | 'tel-national' + | 'tel-area-code' + | 'tel-local' + | 'tel-extension' + | 'impp' + | 'url' + | 'photo' + export type InputProps = { type: InputType name?: string @@ -22,13 +78,12 @@ export type InputProps = { onChange?: ChangeEventHandler inputStyles?: SharedProps readOnly?: boolean + autoComplete?: AutocompleteValues } & React.HTMLProps export type InputResponseType = 'error' | 'warning' | 'success' | 'none' -export type CriteriaType = 'min' | 'max' | 'email' | 'required' | 'regex' | 'validation' - -export type ValidationFunction = (value: string) => boolean +export type CriteriaType = 'min' | 'max' | 'email' | 'required' | 'regex' | 'custom' export interface InputValidation { invalidMessage?: string @@ -37,7 +92,7 @@ export interface InputValidation { validResponseType?: InputResponseType condition: { type: CriteriaType - rule?: number | RegExp | ValidationFunction + rule?: number | RegExp | Function } } @@ -48,11 +103,7 @@ export type InputCriteriaResponse = { isValid: boolean } -export const criteriaRule = ( - type: CriteriaType, - value: any, - rule?: number | RegExp | ValidationFunction -) => { +export const criteriaRule = (type: CriteriaType, value: any, rule?: number | RegExp | Function) => { switch (type) { case 'required': return !isEmpty(value as string) @@ -66,8 +117,8 @@ export const criteriaRule = ( ).test(value as string) case 'regex': return (rule as RegExp).test(value as string) - case 'validation': - return (rule as ValidationFunction)?.(value) + case 'custom': + return (rule as Function)?.(value) default: throw new Error('Unknown criteria type') } diff --git a/src/lib/components/inputs/basic/text/Text.tsx b/src/lib/components/inputs/basic/text/Text.tsx index 174614ba..85f3239e 100644 --- a/src/lib/components/inputs/basic/text/Text.tsx +++ b/src/lib/components/inputs/basic/text/Text.tsx @@ -19,11 +19,13 @@ export const Text = (props: InputProps) => { From 185026f20852798a49ffbc0d116eaca5ca083924 Mon Sep 17 00:00:00 2001 From: Pedro Guerrato Date: Thu, 10 Feb 2022 12:46:55 -0300 Subject: [PATCH 2/9] Add form id and type prop --- src/lib/components/buttons/Button/Button.styles.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/components/buttons/Button/Button.styles.tsx b/src/lib/components/buttons/Button/Button.styles.tsx index 0760773d..aa02d22b 100644 --- a/src/lib/components/buttons/Button/Button.styles.tsx +++ b/src/lib/components/buttons/Button/Button.styles.tsx @@ -5,6 +5,8 @@ import { ButtonVariant } from '..' import { IconName } from '../../icons' export type StyledButtonProps = { + type?: 'button' | 'reset' | 'submit' + form?: string borderRadius?: string backgroundColor?: string foregroundColor?: string @@ -22,7 +24,9 @@ export type StyledButtonProps = { } export const StyledButton = styled.button.attrs((props) => ({ - disabled: props.disabled + disabled: props.disabled, + type: props.type, + form: props.form }))` border-radius: ${(props) => props.borderRadius}; background-color: ${(props) => props.backgroundColor}; From 7e10d88121580f1481926ff775d9cc482771b057 Mon Sep 17 00:00:00 2001 From: Pedro Guerrato Date: Fri, 11 Feb 2022 11:46:53 -0300 Subject: [PATCH 3/9] Set valid message to empty instead of invalid by default --- src/lib/hooks/useInputValidation.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/hooks/useInputValidation.ts b/src/lib/hooks/useInputValidation.ts index e3657e26..6b04bce0 100644 --- a/src/lib/hooks/useInputValidation.ts +++ b/src/lib/hooks/useInputValidation.ts @@ -27,11 +27,12 @@ const handleResponse = ( type: InputResponseType, isValid: boolean ) => { - let message = invalidMessage + let message = invalidMessage ?? '' - if (isValid && !isEmpty(validMessage)) { - message = validMessage + if (isValid) { + message = !isEmpty(validMessage) ? validMessage : '' } + const icon = getValidationIcon(type) return { From ecfc3aae1f9164439d2097028982b3167d81138d Mon Sep 17 00:00:00 2001 From: Pedro Guerrato Date: Thu, 10 Feb 2022 09:55:40 -0300 Subject: [PATCH 4/9] Solve custom function validation bug and add autocomplete --- .../components/inputs/basic/Input.stories.tsx | 29 ++++++- src/lib/components/inputs/basic/Input.tsx | 6 +- .../components/inputs/basic/search/Search.tsx | 1 + .../components/inputs/basic/shared/styles.tsx | 2 +- .../components/inputs/basic/shared/types.ts | 82 ++++++++++++++----- src/lib/components/inputs/basic/text/Text.tsx | 2 + 6 files changed, 98 insertions(+), 24 deletions(-) diff --git a/src/lib/components/inputs/basic/Input.stories.tsx b/src/lib/components/inputs/basic/Input.stories.tsx index efd6e843..8b06d749 100644 --- a/src/lib/components/inputs/basic/Input.stories.tsx +++ b/src/lib/components/inputs/basic/Input.stories.tsx @@ -1,5 +1,5 @@ import { Story } from '@storybook/react' -import React from 'react' +import React, { FormEvent, FormEventHandler, SyntheticEvent } from 'react' import { Input } from './Input' import { InputProps } from './shared' @@ -8,7 +8,7 @@ export default { component: Input } -const Template = (args: InputProps) => ( +const Template: Story = (args: InputProps) => (
@@ -174,3 +174,28 @@ Search.args = { required: true, placeholder: 'Type your search' } + +const handleSubmit: FormEventHandler = (e: SyntheticEvent) => { + e.preventDefault() + + const formElements = e.currentTarget.elements as typeof e.currentTarget.elements & { + emailInput: { value: string } + } + + console.log(formElements) +} + +export const AutoComplete: Story = (args: InputProps) => ( +
+ + +
+) +AutoComplete.args = { + type: 'email', + id: 'emailInput', + label: 'E-mail', + required: true, + placeholder: 'Type your search', + autoComplete: 'email' +} diff --git a/src/lib/components/inputs/basic/Input.tsx b/src/lib/components/inputs/basic/Input.tsx index 0d90ec07..112324ca 100644 --- a/src/lib/components/inputs/basic/Input.tsx +++ b/src/lib/components/inputs/basic/Input.tsx @@ -86,7 +86,11 @@ export const Input = (props: InputProps) => { return ( - {!isEmpty(props.label) && {props.label}} + {!isEmpty(props.label) && ( + + {props.label} + + )} ) diff --git a/src/lib/components/inputs/basic/search/Search.tsx b/src/lib/components/inputs/basic/search/Search.tsx index ff3b8496..6742cda4 100644 --- a/src/lib/components/inputs/basic/search/Search.tsx +++ b/src/lib/components/inputs/basic/search/Search.tsx @@ -25,6 +25,7 @@ export const Search = (props: InputProps) => { placeholder={props.placeholder} readOnly={props.readOnly} disabled={props.disabled} + autoComplete={props.autoComplete} {...props.inputStyles} /> diff --git a/src/lib/components/inputs/basic/shared/styles.tsx b/src/lib/components/inputs/basic/shared/styles.tsx index 75f266c5..40fff537 100644 --- a/src/lib/components/inputs/basic/shared/styles.tsx +++ b/src/lib/components/inputs/basic/shared/styles.tsx @@ -55,7 +55,7 @@ export const StyledLabel = styled.label` export const GlobalInput = styled.input.attrs((props) => ({ type: props.type, - autocomplete: 'new-password' + autoComplete: props.autoComplete }))` box-sizing: border-box; cursor: ${(props) => (props.disabled ? 'not-allowed' : props.cursor)}; diff --git a/src/lib/components/inputs/basic/shared/types.ts b/src/lib/components/inputs/basic/shared/types.ts index 495f4f6e..6fb05723 100644 --- a/src/lib/components/inputs/basic/shared/types.ts +++ b/src/lib/components/inputs/basic/shared/types.ts @@ -8,6 +8,62 @@ export type InputType = Extract< 'checkbox' | 'email' | 'password' | 'radio' | 'search' | 'text' > +export type AutocompleteValues = + | 'off' + | 'on' + | 'name' + | 'honorific-prefix' + | 'given-name' + | 'additional-name' + | 'family-name' + | 'honorific-suffix' + | 'nickname' + | 'email' + | 'username' + | 'new-password' + | 'current-password' + | 'one-time-code' + | 'organization-title' + | 'organization' + | 'street-address' + | 'address-line1' + | 'address-line2' + | 'address-line3' + | 'address-level4' + | 'address-level3' + | 'address-level2' + | 'address-level1' + | 'country' + | 'country-name' + | 'postal-code' + | 'cc-name' + | 'cc-given-name' + | 'cc-additional-name' + | 'cc-family-name' + | 'cc-number' + | 'cc-exp' + | 'cc-exp-month' + | 'cc-exp-year' + | 'cc-csc' + | 'cc-type' + | 'transaction-currency' + | 'transaction-amount' + | 'language' + | 'bday' + | 'bday-day' + | 'bday-month' + | 'bday-year' + | 'sex' + | 'tel' + | 'tel-country-code' + | 'tel-national' + | 'tel-area-code' + | 'tel-local' + | 'tel-extension' + | 'impp' + | 'url' + | 'photo' + export type InputProps = { type: InputType name?: string @@ -22,20 +78,12 @@ export type InputProps = { onChange?: ChangeEventHandler inputStyles?: SharedProps readOnly?: boolean + autoComplete?: AutocompleteValues } & React.HTMLProps export type InputResponseType = 'error' | 'warning' | 'success' | 'none' -export type CriteriaType = - | 'min' - | 'max' - | 'email' - | 'required' - | 'regex' - | 'validation' - | 'condition' - -export type ValidationFunction = (value: string) => boolean +export type CriteriaType = 'min' | 'max' | 'email' | 'required' | 'regex' | 'custom' export interface InputValidation { invalidMessage?: string @@ -44,7 +92,7 @@ export interface InputValidation { validResponseType?: InputResponseType condition: { type: CriteriaType - rule?: number | RegExp | ValidationFunction | boolean + rule?: number | RegExp | Function } } @@ -55,11 +103,7 @@ export type InputCriteriaResponse = { isValid: boolean } -export const criteriaRule = ( - type: CriteriaType, - value: any, - rule?: number | RegExp | ValidationFunction | boolean -): boolean => { +export const criteriaRule = (type: CriteriaType, value: any, rule?: number | RegExp | Function) => { switch (type) { case 'required': return !isEmpty(value as string) @@ -73,10 +117,8 @@ export const criteriaRule = ( ).test(value as string) case 'regex': return (rule as RegExp).test(value as string) - case 'validation': - return (rule as ValidationFunction)?.(value) - case 'condition': - return value + case 'custom': + return (rule as Function)?.(value) default: throw new Error('Unknown criteria type') } diff --git a/src/lib/components/inputs/basic/text/Text.tsx b/src/lib/components/inputs/basic/text/Text.tsx index 174614ba..85f3239e 100644 --- a/src/lib/components/inputs/basic/text/Text.tsx +++ b/src/lib/components/inputs/basic/text/Text.tsx @@ -19,11 +19,13 @@ export const Text = (props: InputProps) => { From 1a3fc12718739376fb9e874e8d7aa03941fb7629 Mon Sep 17 00:00:00 2001 From: Pedro Guerrato Date: Thu, 10 Feb 2022 12:46:55 -0300 Subject: [PATCH 5/9] Add form id and type prop --- src/lib/components/buttons/Button/Button.styles.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/components/buttons/Button/Button.styles.tsx b/src/lib/components/buttons/Button/Button.styles.tsx index 0760773d..aa02d22b 100644 --- a/src/lib/components/buttons/Button/Button.styles.tsx +++ b/src/lib/components/buttons/Button/Button.styles.tsx @@ -5,6 +5,8 @@ import { ButtonVariant } from '..' import { IconName } from '../../icons' export type StyledButtonProps = { + type?: 'button' | 'reset' | 'submit' + form?: string borderRadius?: string backgroundColor?: string foregroundColor?: string @@ -22,7 +24,9 @@ export type StyledButtonProps = { } export const StyledButton = styled.button.attrs((props) => ({ - disabled: props.disabled + disabled: props.disabled, + type: props.type, + form: props.form }))` border-radius: ${(props) => props.borderRadius}; background-color: ${(props) => props.backgroundColor}; From 512fb359509df7eced0bb74d9a86b7aaab405041 Mon Sep 17 00:00:00 2001 From: Pedro Guerrato Date: Fri, 11 Feb 2022 11:46:53 -0300 Subject: [PATCH 6/9] Set valid message to empty instead of invalid by default --- src/lib/hooks/useInputValidation.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/hooks/useInputValidation.ts b/src/lib/hooks/useInputValidation.ts index cd31a0bf..3020fee0 100644 --- a/src/lib/hooks/useInputValidation.ts +++ b/src/lib/hooks/useInputValidation.ts @@ -27,11 +27,12 @@ const handleResponse = ( type: InputResponseType, isValid: boolean ) => { - let message = invalidMessage + let message = invalidMessage ?? '' - if (isValid && !isEmpty(validMessage)) { - message = validMessage + if (isValid) { + message = !isEmpty(validMessage) ? validMessage : '' } + const icon = getValidationIcon(type) return { From d0dd43489abb70d22d3908325d8bb7a9331d945a Mon Sep 17 00:00:00 2001 From: Pedro Guerrato Date: Fri, 11 Feb 2022 15:37:29 -0300 Subject: [PATCH 7/9] Adjust validation to the new business requirement --- .../components/inputs/basic/shared/types.ts | 1 + src/lib/components/inputs/basic/text/Text.tsx | 6 ++++- src/lib/hooks/useInputValidation.ts | 23 +++++++++++-------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/lib/components/inputs/basic/shared/types.ts b/src/lib/components/inputs/basic/shared/types.ts index 6fb05723..53389af5 100644 --- a/src/lib/components/inputs/basic/shared/types.ts +++ b/src/lib/components/inputs/basic/shared/types.ts @@ -76,6 +76,7 @@ export type InputProps = { multipleCriteriaInfo?: boolean criteria?: InputValidation | InputValidation[] onChange?: ChangeEventHandler + onValidate?: (isValid: boolean) => void inputStyles?: SharedProps readOnly?: boolean autoComplete?: AutocompleteValues diff --git a/src/lib/components/inputs/basic/text/Text.tsx b/src/lib/components/inputs/basic/text/Text.tsx index 85f3239e..92a3dfae 100644 --- a/src/lib/components/inputs/basic/text/Text.tsx +++ b/src/lib/components/inputs/basic/text/Text.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useEffect } from 'react' import { Icon } from '../../../icons' import { useInputValidation } from '../../../../hooks/useInputValidation' import { InputProps } from '../shared' @@ -15,6 +15,10 @@ export const Text = (props: InputProps) => { } } + useEffect(() => { + // console.log(validationResponse) + }, [validationResponse]) + return ( diff --git a/src/lib/hooks/useInputValidation.ts b/src/lib/hooks/useInputValidation.ts index 3020fee0..7fe5efd6 100644 --- a/src/lib/hooks/useInputValidation.ts +++ b/src/lib/hooks/useInputValidation.ts @@ -25,14 +25,14 @@ const handleResponse = ( invalidMessage: string, validMessage: string, type: InputResponseType, - isValid: boolean + isValid: boolean, + fromMultiple: boolean ) => { - let message = invalidMessage ?? '' + let message = invalidMessage - if (isValid) { + if (isValid && !fromMultiple) { message = !isEmpty(validMessage) ? validMessage : '' } - const icon = getValidationIcon(type) return { @@ -63,7 +63,6 @@ const getCriteriaSet = (props: InputProps) => { { invalidMessage: 'Please fill with a valid e-mail address', invalidResponseType: 'warning', - validMessage: '', validResponseType: 'success', condition: { type: 'email' } }, @@ -80,7 +79,6 @@ const getCriteriaSet = (props: InputProps) => { invalidMessage: 'Please fill in this field', invalidResponseType: 'error', validResponseType: 'success', - validMessage: '', condition: { type: 'required' } }, ...criteriaSet @@ -107,13 +105,20 @@ export const useInputValidation = (props: InputProps) => { crit.invalidMessage, crit.validMessage, crit.invalidResponseType, - isValid + isValid, + false ) ) break } else { setResponse( - handleResponse(crit.invalidMessage, crit.validMessage, crit.validResponseType, isValid) + handleResponse( + crit.invalidMessage, + crit.validMessage, + crit.validResponseType, + isValid, + false + ) ) } } @@ -126,7 +131,7 @@ export const useInputValidation = (props: InputProps) => { validations = [ ...validations, - handleResponse(crit.invalidMessage, crit.validMessage, respType, isValid) + handleResponse(crit.invalidMessage, crit.validMessage, respType, isValid, true) ] } From bb7c3e2742ed6cd492a5e1979e091689f7892fa7 Mon Sep 17 00:00:00 2001 From: Pedro Guerrato Date: Fri, 11 Feb 2022 15:37:29 -0300 Subject: [PATCH 8/9] Adjust validation to the new business requirement --- .../components/inputs/basic/shared/types.ts | 1 + src/lib/components/inputs/basic/text/Text.tsx | 6 ++++- src/lib/hooks/useInputValidation.ts | 23 +++++++++++-------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/lib/components/inputs/basic/shared/types.ts b/src/lib/components/inputs/basic/shared/types.ts index 6fb05723..53389af5 100644 --- a/src/lib/components/inputs/basic/shared/types.ts +++ b/src/lib/components/inputs/basic/shared/types.ts @@ -76,6 +76,7 @@ export type InputProps = { multipleCriteriaInfo?: boolean criteria?: InputValidation | InputValidation[] onChange?: ChangeEventHandler + onValidate?: (isValid: boolean) => void inputStyles?: SharedProps readOnly?: boolean autoComplete?: AutocompleteValues diff --git a/src/lib/components/inputs/basic/text/Text.tsx b/src/lib/components/inputs/basic/text/Text.tsx index 85f3239e..92a3dfae 100644 --- a/src/lib/components/inputs/basic/text/Text.tsx +++ b/src/lib/components/inputs/basic/text/Text.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useEffect } from 'react' import { Icon } from '../../../icons' import { useInputValidation } from '../../../../hooks/useInputValidation' import { InputProps } from '../shared' @@ -15,6 +15,10 @@ export const Text = (props: InputProps) => { } } + useEffect(() => { + // console.log(validationResponse) + }, [validationResponse]) + return ( diff --git a/src/lib/hooks/useInputValidation.ts b/src/lib/hooks/useInputValidation.ts index 3020fee0..7fe5efd6 100644 --- a/src/lib/hooks/useInputValidation.ts +++ b/src/lib/hooks/useInputValidation.ts @@ -25,14 +25,14 @@ const handleResponse = ( invalidMessage: string, validMessage: string, type: InputResponseType, - isValid: boolean + isValid: boolean, + fromMultiple: boolean ) => { - let message = invalidMessage ?? '' + let message = invalidMessage - if (isValid) { + if (isValid && !fromMultiple) { message = !isEmpty(validMessage) ? validMessage : '' } - const icon = getValidationIcon(type) return { @@ -63,7 +63,6 @@ const getCriteriaSet = (props: InputProps) => { { invalidMessage: 'Please fill with a valid e-mail address', invalidResponseType: 'warning', - validMessage: '', validResponseType: 'success', condition: { type: 'email' } }, @@ -80,7 +79,6 @@ const getCriteriaSet = (props: InputProps) => { invalidMessage: 'Please fill in this field', invalidResponseType: 'error', validResponseType: 'success', - validMessage: '', condition: { type: 'required' } }, ...criteriaSet @@ -107,13 +105,20 @@ export const useInputValidation = (props: InputProps) => { crit.invalidMessage, crit.validMessage, crit.invalidResponseType, - isValid + isValid, + false ) ) break } else { setResponse( - handleResponse(crit.invalidMessage, crit.validMessage, crit.validResponseType, isValid) + handleResponse( + crit.invalidMessage, + crit.validMessage, + crit.validResponseType, + isValid, + false + ) ) } } @@ -126,7 +131,7 @@ export const useInputValidation = (props: InputProps) => { validations = [ ...validations, - handleResponse(crit.invalidMessage, crit.validMessage, respType, isValid) + handleResponse(crit.invalidMessage, crit.validMessage, respType, isValid, true) ] } From 0ebf73a0d9582b8bbee731bf8131b0b0a526c463 Mon Sep 17 00:00:00 2001 From: Pedro Guerrato Date: Mon, 14 Feb 2022 07:56:30 -0300 Subject: [PATCH 9/9] Improve input with onValid response --- .../inputs/basic/password/Password.tsx | 8 ++++++-- .../components/inputs/basic/search/Search.tsx | 8 ++++++-- .../inputs/basic/shared/ValidationInfo.tsx | 11 +++++++---- src/lib/components/inputs/basic/shared/types.ts | 17 ++++++++++++++++- src/lib/components/inputs/basic/text/Text.tsx | 4 ++-- src/lib/hooks/useInputValidation.ts | 12 +++++++----- 6 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/lib/components/inputs/basic/password/Password.tsx b/src/lib/components/inputs/basic/password/Password.tsx index c206b057..5e545544 100644 --- a/src/lib/components/inputs/basic/password/Password.tsx +++ b/src/lib/components/inputs/basic/password/Password.tsx @@ -1,6 +1,6 @@ -import React, { useState } from 'react' +import React, { useState, useEffect } from 'react' import { useInputValidation } from '../../../../hooks/useInputValidation' -import { InputProps, InputType } from '../shared' +import { getResultantValidationResponse, InputProps, InputType } from '../shared' import { ValidationInfo } from '../shared/ValidationInfo' import { ActionArea, InputGroup, StyledInput, VerificationWithToggle } from '../shared' @@ -23,6 +23,10 @@ export const Password = (props: InputProps) => { } } + useEffect(() => { + props.onValidate?.call(null, getResultantValidationResponse(validationResponse)) + }, [validationResponse]) + return ( diff --git a/src/lib/components/inputs/basic/search/Search.tsx b/src/lib/components/inputs/basic/search/Search.tsx index 6742cda4..ac5ce664 100644 --- a/src/lib/components/inputs/basic/search/Search.tsx +++ b/src/lib/components/inputs/basic/search/Search.tsx @@ -1,7 +1,7 @@ -import React from 'react' +import React, { useEffect } from 'react' import { Icon } from '../../../icons' import { useInputValidation } from '../../../../hooks/useInputValidation' -import { InputProps } from '../shared' +import { getResultantValidationResponse, InputProps } from '../shared' import { ActionArea, InputGroup, VerificationWithToggle } from '../shared' import { ValidationInfo } from '../shared/ValidationInfo' import { SearchInput } from './Search.styles' @@ -16,6 +16,10 @@ export const Search = (props: InputProps) => { } } + useEffect(() => { + props.onValidate?.call(null, getResultantValidationResponse(validationResponse)) + }, [validationResponse]) + return ( diff --git a/src/lib/components/inputs/basic/shared/ValidationInfo.tsx b/src/lib/components/inputs/basic/shared/ValidationInfo.tsx index 7136fea1..d17a5afa 100644 --- a/src/lib/components/inputs/basic/shared/ValidationInfo.tsx +++ b/src/lib/components/inputs/basic/shared/ValidationInfo.tsx @@ -7,18 +7,21 @@ export const ValidationInfo = ({ validationResponse }: { props: InputProps - validationResponse: any + validationResponse: InputCriteriaResponse | InputCriteriaResponse[] }) => { return ( {!props.multipleCriteriaInfo && ( - - {validationResponse?.message} + + {(validationResponse as InputCriteriaResponse)?.message} )} {props.multipleCriteriaInfo && validationResponse ? (
    - {(validationResponse as Array).map((crit, idx) => ( + {(validationResponse as InputCriteriaResponse[]).map((crit, idx) => ( {crit.message} diff --git a/src/lib/components/inputs/basic/shared/types.ts b/src/lib/components/inputs/basic/shared/types.ts index 53389af5..7ca669ac 100644 --- a/src/lib/components/inputs/basic/shared/types.ts +++ b/src/lib/components/inputs/basic/shared/types.ts @@ -100,7 +100,7 @@ export interface InputValidation { export type InputCriteriaResponse = { message: string type: InputResponseType - icon: IconName + icon: IconName | undefined isValid: boolean } @@ -124,3 +124,18 @@ export const criteriaRule = (type: CriteriaType, value: any, rule?: number | Reg throw new Error('Unknown criteria type') } } + +export const getResultantValidationResponse = ( + validationResponse: InputCriteriaResponse | InputCriteriaResponse[] | undefined +) => { + if (!validationResponse) { + return validationResponse + } + + if (Array.isArray(validationResponse)) { + const allResp = validationResponse.map((v) => v.isValid) + return allResp.includes(false) ? false : true + } + + return validationResponse.isValid +} diff --git a/src/lib/components/inputs/basic/text/Text.tsx b/src/lib/components/inputs/basic/text/Text.tsx index 92a3dfae..281b2ee6 100644 --- a/src/lib/components/inputs/basic/text/Text.tsx +++ b/src/lib/components/inputs/basic/text/Text.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react' import { Icon } from '../../../icons' import { useInputValidation } from '../../../../hooks/useInputValidation' -import { InputProps } from '../shared' +import { getResultantValidationResponse, InputProps } from '../shared' import { InputGroup, GlobalInput, VerificationIcon } from '../shared' import { ValidationInfo } from '../shared/ValidationInfo' @@ -16,7 +16,7 @@ export const Text = (props: InputProps) => { } useEffect(() => { - // console.log(validationResponse) + props.onValidate?.call(null, getResultantValidationResponse(validationResponse)) }, [validationResponse]) return ( diff --git a/src/lib/hooks/useInputValidation.ts b/src/lib/hooks/useInputValidation.ts index 7fe5efd6..a6d15a6a 100644 --- a/src/lib/hooks/useInputValidation.ts +++ b/src/lib/hooks/useInputValidation.ts @@ -1,21 +1,23 @@ import { useCallback, useState } from 'react' import { criteriaRule, + InputCriteriaResponse, InputProps, InputResponseType, InputValidation } from '../components/inputs/basic/shared' import { isEmpty } from 'lodash' +import { IconName } from '../components' const getValidationIcon = (errorType: InputResponseType) => { switch (errorType) { case 'error': - return 'close_filled' + return 'close_filled' as IconName case 'warning': - return 'warning_filled' + return 'warning_filled' as IconName case 'success': - return 'check_filled' + return 'check_filled' as IconName default: return undefined } @@ -27,7 +29,7 @@ const handleResponse = ( type: InputResponseType, isValid: boolean, fromMultiple: boolean -) => { +): InputCriteriaResponse => { let message = invalidMessage if (isValid && !fromMultiple) { @@ -123,7 +125,7 @@ export const useInputValidation = (props: InputProps) => { } } } else { - let validations = [] + let validations: InputCriteriaResponse[] = [] for (const key in criteriaSet) { const crit = criteriaSet[key] const isValid = criteriaRule(crit.condition.type, value, crit.condition?.rule)