@de-formed/react-validations
Advanced tools
| import { renderHook, act } from '@testing-library/react-hooks'; | ||
| import { useValidation } from '../'; | ||
| import { ValidationSchema, ValidationState } from '@de-formed/base'; | ||
| type TestSchema = { | ||
| name: string; | ||
| age: number; | ||
| dingo: boolean; | ||
| agreement: boolean; | ||
| }; | ||
| const schema: ValidationSchema<TestSchema> = { | ||
| name: [ | ||
| { | ||
| error: 'Name is required.', | ||
| validation: (state: TestSchema) => state.name.length > 0, | ||
| }, | ||
| { | ||
| error: 'Cannot be bob.', | ||
| validation: (state: TestSchema) => state.name !== 'bob', | ||
| }, | ||
| { | ||
| error: 'Must be dingo.', | ||
| validation: (state: TestSchema) => { | ||
| return state.dingo ? state.name === 'dingo' : true; | ||
| }, | ||
| }, | ||
| ], | ||
| age: [ | ||
| { | ||
| error: 'Must be 18', | ||
| validation: (state: TestSchema) => state.age >= 18, | ||
| }, | ||
| ], | ||
| agreement: [ | ||
| { | ||
| error: 'Must accept terms.', | ||
| validation: ({ agreement }) => Boolean(agreement), | ||
| }, | ||
| ], | ||
| }; | ||
| const mockValidationState: ValidationState = { | ||
| name: { | ||
| isValid: true, | ||
| errors: [], | ||
| }, | ||
| age: { | ||
| isValid: true, | ||
| errors: [], | ||
| }, | ||
| agreement: { | ||
| isValid: true, | ||
| errors: [], | ||
| }, | ||
| }; | ||
| const defaultState = { | ||
| name: 'jack', | ||
| dingo: false, | ||
| age: 42, | ||
| agreement: true, | ||
| }; | ||
| const failingState = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| age: 15, | ||
| }; | ||
| describe('useValidation tests', () => { | ||
| it('should be defined', () => { | ||
| expect(useValidation).toBeDefined(); | ||
| }); | ||
| it('renders the hook correctly and checks types', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| expect(typeof result.current.getError).toBe('function'); | ||
| expect(typeof result.current.getFieldValid).toBe('function'); | ||
| expect(typeof result.current.isValid).toBe('boolean'); | ||
| expect(typeof result.current.validate).toBe('function'); | ||
| expect(typeof result.current.validateAll).toBe('function'); | ||
| expect(typeof result.current.validateAllIfTrue).toBe('function'); | ||
| expect(typeof result.current.validateIfTrue).toBe('function'); | ||
| expect(typeof result.current.validateOnBlur).toBe('function'); | ||
| expect(typeof result.current.validateOnChange).toBe('function'); | ||
| expect(typeof result.current.resetValidationState).toBe('function'); | ||
| expect(typeof result.current.setValidationState).toBe('function'); | ||
| expect(Array.isArray(result.current.validationErrors)).toBe(true); | ||
| expect(typeof result.current.validationState).toBe('object'); | ||
| }); | ||
| it('returns all functions and read-only objects defined by hook', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| expect(result.current.validationState).toStrictEqual(mockValidationState); | ||
| expect(Object.keys(result.current)).toStrictEqual([ | ||
| 'getAllErrors', | ||
| 'getError', | ||
| 'getFieldValid', | ||
| 'isValid', | ||
| 'resetValidationState', | ||
| 'setValidationState', | ||
| 'validate', | ||
| 'validateAll', | ||
| 'validateAllIfTrue', | ||
| 'validateIfTrue', | ||
| 'validateOnBlur', | ||
| 'validateOnChange', | ||
| 'validationErrors', | ||
| 'validationState', | ||
| ]); | ||
| }); | ||
| describe('createValidationState', () => { | ||
| it('crates a validation state when given a schema', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| expect(result.current.validationState).toStrictEqual(mockValidationState); | ||
| }); | ||
| it('defaults to an empty object if undefined is provided', () => { | ||
| const { result } = renderHook(() => useValidation(undefined as any)); | ||
| expect(result.current.validationState).toStrictEqual({}); | ||
| }); | ||
| }); | ||
| describe('getError', () => { | ||
| it('returns empty string by default', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getError('name'); | ||
| expect(output).toBe(''); | ||
| }); | ||
| it('returns empty string if the property does not exist', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getError('balls' as keyof TestSchema); | ||
| expect(output).toBe(''); | ||
| }); | ||
| it('retrieves an error message', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| result.current.validate('name', { name: '' } as any); | ||
| }); | ||
| const output = result.current.getError('name'); | ||
| expect(output).toBe('Name is required.'); | ||
| }); | ||
| }); | ||
| describe('getAllErrors', () => { | ||
| it('returns empty array by default', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getAllErrors('name'); | ||
| expect(output).toStrictEqual([]); | ||
| }); | ||
| it('returns empty array if the property does not exist', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getAllErrors('balls' as keyof TestSchema); | ||
| expect(output).toStrictEqual([]); | ||
| }); | ||
| it('retrieves array of all error messages', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: '', | ||
| }; | ||
| act(() => { | ||
| result.current.validate(name, state); | ||
| }); | ||
| const output = result.current.getAllErrors('name'); | ||
| expect(output).toStrictEqual(['Name is required.']); | ||
| }); | ||
| }); | ||
| describe('getFieldValid', () => { | ||
| it('returns true by default', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getFieldValid('name'); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('returns true if the property does not exist', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getFieldValid('balls' as keyof TestSchema); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('retrieves an invalid state', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: '', | ||
| }; | ||
| act(() => { | ||
| result.current.validate(name, state); | ||
| }); | ||
| const output = result.current.getFieldValid('name'); | ||
| expect(output).toBe(false); | ||
| }); | ||
| }); | ||
| describe('isValid', () => { | ||
| it('returns true by default', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.isValid; | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('changes to false after a validation fails', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| result.current.validate(name, state); | ||
| }); | ||
| const output = result.current.isValid; | ||
| expect(output).toBe(false); | ||
| }); | ||
| it('changes to true after a failed validation passes', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| const state2 = { | ||
| ...defaultState, | ||
| name: 'bob ross', | ||
| }; | ||
| act(() => { | ||
| result.current.validate(name, state); | ||
| result.current.validate(name, state2); | ||
| }); | ||
| const output = result.current.isValid; | ||
| expect(output).toBe(true); | ||
| }); | ||
| }); | ||
| describe('validate', () => { | ||
| it('returns a boolean if key exists', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| output = result.current.validate(name, state); | ||
| }); | ||
| expect(typeof output).toBe('boolean'); | ||
| }); | ||
| it('returns true if key does not exist', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'balls' as keyof TestSchema; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validate(name, state); | ||
| }); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('updates the validationState when validation fails', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const validationState = { | ||
| ...mockValidationState, | ||
| name: { | ||
| isValid: false, | ||
| errors: ['Must be dingo.'], | ||
| }, | ||
| }; | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'chuck', | ||
| dingo: true, | ||
| }; | ||
| act(() => { | ||
| result.current.validate(name, state); | ||
| }); | ||
| expect(result.current.isValid).toBe(false); | ||
| expect(result.current.validationState).toStrictEqual(validationState); | ||
| }); | ||
| }); | ||
| describe('validateAll', () => { | ||
| it('returns a boolean', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validateAll(defaultState); | ||
| }); | ||
| expect(typeof output).toBe('boolean'); | ||
| }); | ||
| it('returns true if validations pass', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validateAll(defaultState); | ||
| }); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('returns false if any validation fails', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| const output = result.current.validateAll(failingState); | ||
| expect(output).toBe(false); | ||
| }); | ||
| }); | ||
| it('returns all failing validations', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| result.current.validateAll(failingState); | ||
| }); | ||
| expect(result.current.validationState).toStrictEqual({ | ||
| name: { | ||
| errors: ['Cannot be bob.'], | ||
| isValid: false, | ||
| }, | ||
| age: { | ||
| errors: ['Must be 18'], | ||
| isValid: false, | ||
| }, | ||
| agreement: { | ||
| errors: [], | ||
| isValid: true, | ||
| }, | ||
| }); | ||
| }); | ||
| it('handles nested validation reductions', () => { | ||
| const data = [defaultState, defaultState, defaultState]; | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: boolean[]; | ||
| act(() => { | ||
| output = data.map((s) => result.current.validateAll(s)); | ||
| expect(output).toStrictEqual([true, true, true]); | ||
| }); | ||
| }); | ||
| it('validates a subsection of keys', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| result.current.validateAll(failingState); | ||
| }); | ||
| expect(result.current.getError('age')).toBe('Must be 18'); | ||
| act(() => { | ||
| result.current.validateAll(failingState, ['name']); | ||
| }); | ||
| expect(result.current.getError('age')).toBe('Must be 18'); | ||
| }); | ||
| it('handles missing properties', () => { | ||
| const wonkySchema = { | ||
| ...schema, | ||
| canSave: [ | ||
| { | ||
| error: 'you cannot save', | ||
| validation: (state: any) => !!state.name, | ||
| }, | ||
| ], | ||
| }; | ||
| const { result } = renderHook(() => useValidation(wonkySchema)); | ||
| act(() => { | ||
| result.current.validateAll(failingState); | ||
| }); | ||
| expect(result.current.getError('canSave')).toBe(''); | ||
| }); | ||
| }); | ||
| describe('validateAllIfTrue', () => { | ||
| it('returns a boolean', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validateAllIfTrue(defaultState); | ||
| }); | ||
| expect(typeof output).toBe('boolean'); | ||
| }); | ||
| it('returns true if validations pass', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validateAllIfTrue(defaultState); | ||
| }); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('ignores failing validations', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| const output = result.current.validateAllIfTrue(failingState); | ||
| expect(output).toBe(true); | ||
| }); | ||
| }); | ||
| it('handles nested validation reductions', () => { | ||
| const data = [defaultState, defaultState, defaultState]; | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: boolean[]; | ||
| act(() => { | ||
| output = data.map((s) => result.current.validateAllIfTrue(s)); | ||
| expect(output).toStrictEqual([true, true, true]); | ||
| }); | ||
| }); | ||
| it('validates a subsection of keys', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| result.current.validateAllIfTrue(failingState); | ||
| }); | ||
| expect(result.current.getError('age')).toBe(''); | ||
| act(() => { | ||
| result.current.validateAllIfTrue(failingState, ['name']); | ||
| }); | ||
| expect(result.current.getError('age')).toBe(''); | ||
| }); | ||
| it('handles missing properties', () => { | ||
| const wonkySchema = { | ||
| ...schema, | ||
| canSave: [ | ||
| { | ||
| error: 'you cannot save', | ||
| validation: (state: any) => !!state.name, | ||
| }, | ||
| ], | ||
| }; | ||
| const { result } = renderHook(() => useValidation(wonkySchema)); | ||
| act(() => { | ||
| result.current.validateAllIfTrue(failingState); | ||
| }); | ||
| expect(result.current.getError('canSave')).toBe(''); | ||
| }); | ||
| }); | ||
| describe('validateIfTrue', () => { | ||
| it('returns a boolean if key exists', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| output = result.current.validateIfTrue(name, state); | ||
| }); | ||
| expect(typeof output).toBe('boolean'); | ||
| }); | ||
| it('returns true if key does not exist', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'balls' as keyof TestSchema; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validateIfTrue(name, state); | ||
| }); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('updates the validationState when validation fails', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const validationState = { | ||
| ...mockValidationState, | ||
| }; | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'chuck', | ||
| dingo: true, | ||
| }; | ||
| act(() => { | ||
| result.current.validateIfTrue(name, state); | ||
| }); | ||
| expect(result.current.isValid).toBe(true); | ||
| expect(result.current.validationState).toStrictEqual(validationState); | ||
| }); | ||
| it('updates the validationState when an invalid validation succeeds', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| const state2 = { | ||
| ...defaultState, | ||
| name: 'jack', | ||
| }; | ||
| const validationState = { | ||
| ...mockValidationState, | ||
| }; | ||
| act(() => { | ||
| result.current.validate('name', state); | ||
| }); | ||
| expect(result.current.isValid).toBe(false); | ||
| act(() => { | ||
| result.current.validateIfTrue('name', state2); | ||
| }); | ||
| expect(result.current.isValid).toBe(true); | ||
| expect(result.current.validationState).toStrictEqual(validationState); | ||
| }); | ||
| }); | ||
| describe('validateOnBlur', () => { | ||
| it('returns a new function', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = defaultState; | ||
| const handleBlur = result.current.validateOnBlur(state); | ||
| expect(typeof handleBlur).toBe('function'); | ||
| }); | ||
| it('updates the valdiation state when called', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = defaultState; | ||
| const handleBlur = result.current.validateOnBlur(state); | ||
| const event = { | ||
| target: { | ||
| name: 'name', | ||
| value: 'bob', | ||
| }, | ||
| }; | ||
| act(() => { | ||
| handleBlur(event as any); | ||
| }); | ||
| expect(result.current.isValid).toBe(false); | ||
| }); | ||
| }); | ||
| describe('validateOnChange', () => { | ||
| it('returns a new function', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = defaultState; | ||
| const onChange = (_event: any) => 'bob ross'; | ||
| const handleChange = result.current.validateOnChange(onChange, state); | ||
| expect(typeof handleChange).toBe('function'); | ||
| }); | ||
| it('updates the valdiation state if true and returns event', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| result.current.validate('name', state); | ||
| }); | ||
| expect(result.current.isValid).toBe(false); | ||
| const onChange = () => 'bob ross'; | ||
| const handleChange = result.current.validateOnChange(onChange, state); | ||
| const event = { | ||
| target: { | ||
| name: 'name', | ||
| value: 'jack', | ||
| }, | ||
| }; | ||
| let output: any; | ||
| act(() => { | ||
| output = handleChange(event as any); | ||
| }); | ||
| expect(result.current.isValid).toBe(true); | ||
| expect(output).toBe('bob ross'); | ||
| }); | ||
| it('updates checked properties if true and returns event', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| agreement: false, | ||
| }; | ||
| act(() => { | ||
| result.current.validate('agreement', state); | ||
| }); | ||
| expect(result.current.isValid).toBe(false); | ||
| const onChange = () => true; | ||
| const handleChange = result.current.validateOnChange(onChange, state); | ||
| const event = { | ||
| target: { | ||
| name: 'agreement', | ||
| checked: true, | ||
| type: 'checkbox', | ||
| }, | ||
| }; | ||
| let output: any; | ||
| act(() => { | ||
| output = handleChange(event as any); | ||
| }); | ||
| expect(result.current.isValid).toBe(true); | ||
| expect(output).toBe(true); | ||
| }); | ||
| }); | ||
| describe('resetValidationState', () => { | ||
| it('resets the validation state', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| result.current.validate('name', state); | ||
| result.current.resetValidationState(); | ||
| }); | ||
| expect(result.current.isValid).toBe(true); | ||
| }); | ||
| }); | ||
| describe('validationErrors', () => { | ||
| it('starts as an empty array', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| expect(result.current.validationErrors).toStrictEqual([]); | ||
| }); | ||
| it('adds validation errors when validation state is invalid', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| result.current.validate('name', state); | ||
| }); | ||
| expect(result.current.validationErrors).toStrictEqual(['Cannot be bob.']); | ||
| }); | ||
| it('removes validation errors when validation state is valid', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| const state2 = { | ||
| ...defaultState, | ||
| name: 'jack', | ||
| }; | ||
| act(() => { | ||
| result.current.validate('name', state); | ||
| result.current.validate('name', state2); | ||
| }); | ||
| expect(result.current.validationErrors).toStrictEqual([]); | ||
| }); | ||
| }); | ||
| describe('forceValidationState', () => { | ||
| it('overrides the existing validation state with a new one', () => { | ||
| const { result: v1 } = renderHook(() => useValidation(schema)); | ||
| const { result: v2 } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| v1.current.validateAll(failingState); | ||
| }); | ||
| act(() => { | ||
| v2.current.setValidationState(v1.current.validationState); | ||
| }); | ||
| expect(v1.current.validationState).toStrictEqual( | ||
| v2.current.validationState, | ||
| ); | ||
| }); | ||
| }); | ||
| }); |
| # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node | ||
| # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions | ||
| name: Main | ||
| on: | ||
| push: | ||
| branches: [ main ] | ||
| pull_request: | ||
| branches: [ main ] | ||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| matrix: | ||
| node-version: [12.x, 14.x, 16.x] | ||
| # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ | ||
| steps: | ||
| - uses: actions/checkout@v2 | ||
| - name: Use Node.js ${{ matrix.node-version }} | ||
| uses: actions/setup-node@v2 | ||
| with: | ||
| node-version: ${{ matrix.node-version }} | ||
| - run: npm install -g yarn | ||
| - name: yarn install, build, and test | ||
| run: | | ||
| yarn | ||
| yarn test --coverage | ||
| - name: Codecov | ||
| uses: codecov/codecov-action@v2 | ||
| with: | ||
| # token: ${{ secrets.CODECOV_TOKEN }} | ||
| # flags: unittests | ||
| name: codecov-umbrella # optional | ||
| # fail_ci_if_error: true # optional (default = false) | ||
| verbose: true # optional (default = false) |
+4
-5
@@ -1,4 +0,3 @@ | ||
| /// <reference types="react" /> | ||
| import { GetAllErrors, GetError, GetFieldValid, ResetValidationState, Validate, ValidateAll, ValidateAllIfTrue, ValidateIfTrue, ValidateOnBlur, ValidateOnChange, ValidationSchema } from './types'; | ||
| export * from './types'; | ||
| import React from 'react'; | ||
| import { GetAllErrors, GetError, GetFieldValid, ResetValidationState, Validate, ValidateAll, ValidateAllIfTrue, ValidateIfTrue, ValidateOnBlur, ValidateOnChange, ValidationSchema, ValidationState } from '@de-formed/base'; | ||
| export declare const useValidation: <S>(validationSchema: ValidationSchema<S>) => { | ||
@@ -10,3 +9,3 @@ getAllErrors: GetAllErrors<S>; | ||
| resetValidationState: ResetValidationState; | ||
| setValidationState: import("react").Dispatch<import("react").SetStateAction<import("@de-formed/base").ValidationState>>; | ||
| setValidationState: React.Dispatch<React.SetStateAction<ValidationState>>; | ||
| validate: Validate<S>; | ||
@@ -19,3 +18,3 @@ validateAll: ValidateAll<S>; | ||
| validationErrors: string[]; | ||
| validationState: import("@de-formed/base").ValidationState; | ||
| validationState: ValidationState; | ||
| }; |
+8
-17
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
| for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
| var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.useValidation = void 0; | ||
| const react_1 = require("react"); | ||
| const react_1 = __importDefault(require("react")); | ||
| const base_1 = require("@de-formed/base"); | ||
| __exportStar(require("./types"), exports); | ||
| const useValidation = (validationSchema) => { | ||
| const [validationState, setValidationState] = (0, react_1.useState)(() => (0, base_1.createValidationState)(validationSchema)); | ||
| const [validationErrors, setValidationErros] = (0, react_1.useState)([]); | ||
| const [isValid, setIsValid] = (0, react_1.useState)(true); | ||
| const [validationState, setValidationState] = react_1.default.useState(() => (0, base_1.createValidationState)(validationSchema)); | ||
| const [validationErrors, setValidationErrors] = react_1.default.useState([]); | ||
| const [isValid, setIsValid] = react_1.default.useState(true); | ||
| const resetValidationState = () => setValidationState((0, base_1.createValidationState)(validationSchema)); | ||
@@ -31,5 +23,4 @@ const validate = (0, base_1.createValidate)(validationSchema, validationState, setValidationState); | ||
| const getFieldValid = (0, base_1.createGetFieldValid)(validationState); | ||
| const generateValidationErrors = (state = validationState) => (0, base_1.gatherValidationErrors)(state); | ||
| (0, react_1.useEffect)(() => { | ||
| setValidationErros(generateValidationErrors(validationState)); | ||
| react_1.default.useEffect(() => { | ||
| setValidationErrors((0, base_1.gatherValidationErrors)(validationState)); | ||
| setIsValid((0, base_1.calculateIsValid)(validationState)); | ||
@@ -36,0 +27,0 @@ }, [validationState]); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,iCAA4C;AAC5C,0CAayB;AAezB,0CAAwB;AAQjB,MAAM,aAAa,GAAG,CAAI,gBAAqC,EAAE,EAAE;IAExE,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,IAAA,gBAAQ,EAAC,GAAG,EAAE,CAC1D,IAAA,4BAAqB,EAAC,gBAAgB,CAAC,CACxC,CAAC;IACF,MAAM,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,GAAG,IAAA,gBAAQ,EAAW,EAAE,CAAC,CAAC;IACtE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,IAAA,gBAAQ,EAAC,IAAI,CAAC,CAAC;IAK7C,MAAM,oBAAoB,GAAyB,GAAG,EAAE,CACtD,kBAAkB,CAAC,IAAA,4BAAqB,EAAC,gBAAgB,CAAC,CAAC,CAAC;IAG9D,MAAM,QAAQ,GAAgB,IAAA,qBAAc,EAC1C,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAGF,MAAM,cAAc,GAAsB,IAAA,2BAAoB,EAC5D,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAGF,MAAM,WAAW,GAAmB,IAAA,wBAAiB,EACnD,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAGF,MAAM,iBAAiB,GAAyB,IAAA,8BAAuB,EACrE,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAGF,MAAM,cAAc,GAAsB,IAAA,2BAAoB,EAC5D,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAGF,MAAM,gBAAgB,GAAwB,IAAA,6BAAsB,EAClE,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAGF,MAAM,YAAY,GAAoB,IAAA,yBAAkB,EAAC,eAAe,CAAC,CAAC;IAG1E,MAAM,QAAQ,GAAgB,IAAA,qBAAc,EAAC,eAAe,CAAC,CAAC;IAG9D,MAAM,aAAa,GAAqB,IAAA,0BAAmB,EAAC,eAAe,CAAC,CAAC;IAG7E,MAAM,wBAAwB,GAAG,CAAC,KAAK,GAAG,eAAe,EAAE,EAAE,CAC3D,IAAA,6BAAsB,EAAC,KAAK,CAAC,CAAC;IAGhC,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,kBAAkB,CAAC,wBAAwB,CAAC,eAAe,CAAQ,CAAC,CAAC;QACrE,UAAU,CAAC,IAAA,uBAAgB,EAAC,eAAe,CAAC,CAAC,CAAC;IAChD,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IAEtB,MAAM,gBAAgB,GAAG;QACvB,YAAY;QACZ,QAAQ;QACR,aAAa;QACb,OAAO;QACP,oBAAoB;QACpB,kBAAkB;QAClB,QAAQ;QACR,WAAW;QACX,iBAAiB;QACjB,cAAc;QACd,cAAc;QACd,gBAAgB;QAChB,gBAAgB;QAChB,eAAe;KAChB,CAAC;IAEF,OAAO,gBAAgB,CAAC;AAC1B,CAAC,CAAC;AA7FW,QAAA,aAAa,iBA6FxB"} | ||
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA0B;AAC1B,0CAyByB;AAQlB,MAAM,aAAa,GAAG,CAAI,gBAAqC,EAAE,EAAE;IAExE,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,eAAK,CAAC,QAAQ,CAC1D,GAAG,EAAE,CAAC,IAAA,4BAAqB,EAAC,gBAAgB,CAAC,CAC9C,CAAC;IACF,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,eAAK,CAAC,QAAQ,CAAW,EAAE,CAAC,CAAC;IAC7E,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,eAAK,CAAC,QAAQ,CAAU,IAAI,CAAC,CAAC;IAI5D,MAAM,oBAAoB,GAAyB,GAAG,EAAE,CACtD,kBAAkB,CAAC,IAAA,4BAAqB,EAAC,gBAAgB,CAAC,CAAC,CAAC;IAE9D,MAAM,QAAQ,GAAgB,IAAA,qBAAc,EAC1C,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAEF,MAAM,cAAc,GAAsB,IAAA,2BAAoB,EAC5D,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAEF,MAAM,WAAW,GAAmB,IAAA,wBAAiB,EACnD,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAEF,MAAM,iBAAiB,GAAyB,IAAA,8BAAuB,EACrE,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAEF,MAAM,cAAc,GAAsB,IAAA,2BAAoB,EAC5D,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAEF,MAAM,gBAAgB,GAAwB,IAAA,6BAAsB,EAClE,gBAAgB,EAChB,eAAe,EACf,kBAAkB,CACnB,CAAC;IAEF,MAAM,YAAY,GAAoB,IAAA,yBAAkB,EAAC,eAAe,CAAC,CAAC;IAE1E,MAAM,QAAQ,GAAgB,IAAA,qBAAc,EAAC,eAAe,CAAC,CAAC;IAE9D,MAAM,aAAa,GAAqB,IAAA,0BAAmB,EAAC,eAAe,CAAC,CAAC;IAG7E,eAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,mBAAmB,CAAC,IAAA,6BAAsB,EAAC,eAAe,CAAC,CAAC,CAAC;QAC7D,UAAU,CAAC,IAAA,uBAAgB,EAAC,eAAe,CAAC,CAAC,CAAC;IAChD,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IAEtB,MAAM,gBAAgB,GAAG;QACvB,YAAY;QACZ,QAAQ;QACR,aAAa;QACb,OAAO;QACP,oBAAoB;QACpB,kBAAkB;QAClB,QAAQ;QACR,WAAW;QACX,iBAAiB;QACjB,cAAc;QACd,cAAc;QACd,gBAAgB;QAChB,gBAAgB;QAChB,eAAe;KAChB,CAAC;IAEF,OAAO,gBAAgB,CAAC;AAC1B,CAAC,CAAC;AA/EW,QAAA,aAAa,iBA+ExB"} |
+1
-1
| module.exports = { | ||
| roots: ['<rootDir>/src'], | ||
| roots: ['<rootDir>/__tests__'], | ||
| transform: { | ||
@@ -4,0 +4,0 @@ '^.+\\.tsx?$': 'ts-jest', |
+4
-13
| { | ||
| "name": "@de-formed/react-validations", | ||
| "version": "2.0.4", | ||
| "version": "3.0.0", | ||
| "description": "Modular, Function-driven Validations", | ||
@@ -22,3 +22,3 @@ "main": "dist/index", | ||
| "dependencies": { | ||
| "@de-formed/base": "1.0.1" | ||
| "@de-formed/base": "3.0.0" | ||
| }, | ||
@@ -34,5 +34,7 @@ "peerDependencies": { | ||
| "@types/jest": "^27.0.1", | ||
| "@types/react": "^17.0.20", | ||
| "@types/testing-library__dom": "^7.5.0", | ||
| "jest": "^27.1.1", | ||
| "prettier": "^2.4.0", | ||
| "react": "^17.0.2", | ||
| "react-test-renderer": "^17.0.2", | ||
@@ -44,13 +46,2 @@ "ts-jest": "^27.0.5", | ||
| }, | ||
| "jest": { | ||
| "transform": { | ||
| ".(ts|tsx)": "ts-jest" | ||
| }, | ||
| "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", | ||
| "moduleFileExtensions": [ | ||
| "ts", | ||
| "tsx", | ||
| "js" | ||
| ] | ||
| }, | ||
| "keywords": [ | ||
@@ -57,0 +48,0 @@ "composable", |
+15
-2
@@ -1,3 +0,11 @@ | ||
| # @De-Formed / React Validations | ||
| <p align="center"> | ||
| <img src="https://user-images.githubusercontent.com/35798153/157611790-96f35e8b-ee4f-44e4-b3c9-1864900a02f2.png" /> | ||
| </p> | ||
| [](https://badge.fury.io/js/@de-formed%2Fbase) | ||
| [](https://snyk.io/test/github/prescottbreeden/de-formed) | ||
|  | ||
| [](https://codecov.io/gh/prescottbreeden/de-formed) | ||
|  | ||
| @De-Formed Validations offers a robust and unopinionated API to customize form and data validations. With only a handful of properties to learn, @de-formed maintains its own internal state with simple function calls so that you can design your architecture the way you want to. | ||
@@ -13,5 +21,7 @@ | ||
| ## Install | ||
| ``` | ||
| yarn add @de-formed/react-validations | ||
| ``` | ||
| ``` | ||
@@ -24,2 +34,3 @@ npm i @de-formed/react-validations | ||
| ### Step 1: Create a file to define your validations. | ||
| ```ts | ||
@@ -62,2 +73,3 @@ // usePersonValidation.ts | ||
| ### Step 2: Use the hook anywhere you need it. | ||
| ```tsx | ||
@@ -109,4 +121,5 @@ // PersonForm.component.tsx | ||
| ``` | ||
| *** | ||
| --- | ||
| ## Documentation | ||
@@ -113,0 +126,0 @@ |
+12
-29
@@ -1,2 +0,2 @@ | ||
| import { useState, useEffect } from 'react'; | ||
| import React from 'react'; | ||
| import { | ||
@@ -15,4 +15,2 @@ calculateIsValid, | ||
| gatherValidationErrors, | ||
| } from '@de-formed/base'; | ||
| import { | ||
| GetAllErrors, | ||
@@ -29,6 +27,5 @@ GetError, | ||
| ValidationSchema, | ||
| } from './types'; | ||
| ValidationState, | ||
| } from '@de-formed/base'; | ||
| export * from './types'; | ||
| /** | ||
@@ -42,15 +39,13 @@ * A hook that can be used to generate an object containing functions and | ||
| // --[ local states ]-------------------------------------------------------- | ||
| const [validationState, setValidationState] = useState(() => | ||
| createValidationState(validationSchema), | ||
| const [validationState, setValidationState] = React.useState<ValidationState>( | ||
| () => createValidationState(validationSchema), | ||
| ); | ||
| const [validationErrors, setValidationErros] = useState<string[]>([]); | ||
| const [isValid, setIsValid] = useState(true); | ||
| const [validationErrors, setValidationErrors] = React.useState<string[]>([]); | ||
| const [isValid, setIsValid] = React.useState<boolean>(true); | ||
| // --[ validation logic ] --------------------------------------------------- | ||
| // resetValidationState :: () -> void | ||
| const resetValidationState: ResetValidationState = () => | ||
| setValidationState(createValidationState(validationSchema)); | ||
| // validate :: string -> value -> boolean | ||
| const validate: Validate<S> = createValidate( | ||
@@ -62,3 +57,2 @@ validationSchema, | ||
| // validateIfTrue :: string -> value -> boolean | ||
| const validateIfTrue: ValidateIfTrue<S> = createValidateIfTrue( | ||
@@ -70,3 +64,2 @@ validationSchema, | ||
| // validationAllIfTrue :: (x, [string]) -> boolean | ||
| const validateAll: ValidateAll<S> = createValidateAll( | ||
@@ -78,3 +71,2 @@ validationSchema, | ||
| // validationAllIfTrue :: (x, [string]) -> boolean | ||
| const validateAllIfTrue: ValidateAllIfTrue<S> = createValidateAllIfTrue( | ||
@@ -86,3 +78,2 @@ validationSchema, | ||
| // validateOnBlur :: state -> (event -> any) | ||
| const validateOnBlur: ValidateOnBlur<S> = createValidateOnBlur( | ||
@@ -94,3 +85,2 @@ validationSchema, | ||
| // validateOnChange :: (onChange, state) -> (event -> any) | ||
| const validateOnChange: ValidateOnChange<S> = createValidateOnChange( | ||
@@ -102,20 +92,13 @@ validationSchema, | ||
| // getAllErrors :: (string, ValidationState) -> [string] | ||
| const getAllErrors: GetAllErrors<S> = createGetAllErrors(validationState); | ||
| // getError :: (string, ValidationState) -> string | ||
| const getError: GetError<S> = createGetError(validationState); | ||
| // getFieldValid :: (string, ValidationState) -> boolean | ||
| const getFieldValid: GetFieldValid<S> = createGetFieldValid(validationState); | ||
| // generateValidationErrors :: ValidationState -> [string] | ||
| const generateValidationErrors = (state = validationState) => | ||
| gatherValidationErrors(state); | ||
| // -- update validation error array when validation state changes | ||
| useEffect(() => { | ||
| setValidationErros(generateValidationErrors(validationState) as any); | ||
| // -- update validation error array and isValid when validation state changes | ||
| React.useEffect(() => { | ||
| setValidationErrors(gatherValidationErrors(validationState)); | ||
| setIsValid(calculateIsValid(validationState)); | ||
| }, [validationState]); // eslint-disable-line | ||
| }, [validationState]); | ||
@@ -140,2 +123,2 @@ const validationObject = { | ||
| return validationObject; | ||
| }; | ||
| }; |
Sorry, the diff of this file is not supported yet
| export declare class Maybe { | ||
| $value: any; | ||
| get isNothing(): boolean; | ||
| get isJust(): boolean; | ||
| constructor(x: any); | ||
| static of(x: any): Maybe; | ||
| map(fn: any): Maybe; | ||
| chain(fn: any): any; | ||
| join(): any; | ||
| } | ||
| export declare const maybe: (x: any) => Maybe; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.maybe = exports.Maybe = void 0; | ||
| class Maybe { | ||
| constructor(x) { | ||
| this.$value = x; | ||
| } | ||
| get isNothing() { | ||
| return this.$value === null || this.$value === undefined; | ||
| } | ||
| get isJust() { | ||
| return !this.isNothing; | ||
| } | ||
| static of(x) { | ||
| return new Maybe(x); | ||
| } | ||
| map(fn) { | ||
| return this.isNothing ? this : Maybe.of(fn(this.$value)); | ||
| } | ||
| chain(fn) { | ||
| return this.map(fn).join(); | ||
| } | ||
| join() { | ||
| return this.isNothing ? this : this.$value; | ||
| } | ||
| } | ||
| exports.Maybe = Maybe; | ||
| exports.maybe = (x) => Maybe.of(x); | ||
| //# sourceMappingURL=maybe.js.map |
| {"version":3,"file":"maybe.js","sourceRoot":"","sources":["../src/maybe.ts"],"names":[],"mappings":";;;AAAA,MAAa,KAAK;IAWhB,YAAY,CAAM;QAChB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAClB,CAAC;IAVD,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;IAC3D,CAAC;IAED,IAAI,MAAM;QACR,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;IACzB,CAAC;IAOD,MAAM,CAAC,EAAE,CAAC,CAAM;QACd,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IAGD,GAAG,CAAC,EAAO;QACT,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3D,CAAC;IAQD,KAAK,CAAC,EAAO;QACX,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;IAC7C,CAAC;CAUF;AA/CD,sBA+CC;AACY,QAAA,KAAK,GAAG,CAAC,CAAM,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC"} |
| export declare type GetAllErrors<S> = (property: keyof S) => string[]; | ||
| export declare type GetError<S> = (property: keyof S) => string; | ||
| export declare type GetFieldValid<S> = (property: keyof S) => boolean; | ||
| export declare type IsValid = boolean; | ||
| export declare type ResetValidationState = () => void; | ||
| export declare type SetValidationState = (validationState: ValidationState) => void; | ||
| export declare type Validate<S> = (property: keyof S, value: S) => boolean; | ||
| export declare type ValidateAll<S> = (value: S, keys?: (keyof S)[]) => boolean; | ||
| export declare type ValidateAllIfTrue<S> = (value: S, keys?: (keyof S)[]) => boolean; | ||
| export declare type ValidateIfTrue<S> = (property: keyof S, value: S) => boolean; | ||
| export declare type ValidateOnBlur<S> = (value: S) => (event: any) => any; | ||
| export declare type ValidateOnChange<S> = (onChange: (event: any) => any, value: S) => (event: any) => any; | ||
| export declare type ValidationFunction<S> = (value: S) => boolean; | ||
| export interface ValidationObject<S> { | ||
| getAllErrors: GetAllErrors<S>; | ||
| getError: GetError<S>; | ||
| getFieldValid: GetFieldValid<S>; | ||
| isValid: boolean; | ||
| resetValidationState: ResetValidationState; | ||
| setValidationState: SetValidationState; | ||
| validate: Validate<S>; | ||
| validateAll: ValidateAll<S>; | ||
| validateAllIfTrue: ValidateAllIfTrue<S>; | ||
| validateIfTrue: ValidateIfTrue<S>; | ||
| validateOnBlur: ValidateOnBlur<S>; | ||
| validateOnChange: ValidateOnChange<S>; | ||
| validationErrors: string[]; | ||
| validationState: ValidationState; | ||
| } | ||
| export interface ValidationProps<S> { | ||
| error: string; | ||
| validation: ValidationFunction<S>; | ||
| } | ||
| export interface ValidationSchema<S> { | ||
| [key: string]: ValidationProps<S>[]; | ||
| } | ||
| export interface ValidationState { | ||
| [key: string]: { | ||
| isValid: boolean; | ||
| errors: string[]; | ||
| }; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| //# sourceMappingURL=types.js.map |
| {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""} |
| export declare const compose: (...fns: any[]) => (...args: any[]) => any; | ||
| export declare const prop: (...args: any[]) => any; | ||
| export declare const isPropertyValid: <S>(property: keyof S) => any; | ||
| export declare const executeSideEffect: (...args: any[]) => any; | ||
| export declare const stringIsNotEmpty: (...args: any[]) => any; | ||
| export declare const stringIsLessThan: (...args: any[]) => any; | ||
| export declare const stringIsMoreThan: (...args: any[]) => any; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.stringIsMoreThan = exports.stringIsLessThan = exports.stringIsNotEmpty = exports.executeSideEffect = exports.isPropertyValid = exports.prop = exports.compose = void 0; | ||
| const R = __importStar(require("ramda")); | ||
| function curry(fn) { | ||
| const arity = fn.length; | ||
| return function $curry(...args) { | ||
| if (args.length < arity) { | ||
| return $curry.bind(null, ...args); | ||
| } | ||
| return fn.call(null, ...args); | ||
| }; | ||
| } | ||
| exports.compose = (...fns) => (...args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0]; | ||
| exports.prop = curry((p, obj) => (obj ? obj[p] : undefined)); | ||
| exports.isPropertyValid = (property) => exports.compose(R.defaultTo(true), R.path([property, 'isValid'])); | ||
| exports.executeSideEffect = curry((f, x) => f(x) || x); | ||
| exports.stringIsNotEmpty = exports.compose(R.gt(R.__, 0), R.length, R.trim); | ||
| exports.stringIsLessThan = curry((num, str) => { | ||
| return exports.compose(R.lt(R.__, num), R.length, R.trim)(str); | ||
| }); | ||
| exports.stringIsMoreThan = curry((num, str) => { | ||
| return exports.compose(R.gt(R.__, num), R.length, R.trim)(str); | ||
| }); | ||
| //# sourceMappingURL=utilities.js.map |
| {"version":3,"file":"utilities.js","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,yCAA2B;AAG3B,SAAS,KAAK,CAAC,EAAO;IACpB,MAAM,KAAK,GAAG,EAAE,CAAC,MAAM,CAAC;IAExB,OAAO,SAAS,MAAM,CAAC,GAAG,IAAW;QACnC,IAAI,IAAI,CAAC,MAAM,GAAG,KAAK,EAAE;YACvB,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;SACnC;QAED,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;IAChC,CAAC,CAAC;AACJ,CAAC;AAGY,QAAA,OAAO,GAAG,CAAC,GAAG,GAAU,EAAE,EAAE,CAAC,CAAC,GAAG,IAAW,EAAE,EAAE,CAC3D,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAGpD,QAAA,IAAI,GAAG,KAAK,CAAC,CAAC,CAAS,EAAE,GAAQ,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;AAElE,QAAA,eAAe,GAAG,CAAI,QAAiB,EAAO,EAAE,CAAC,eAAO,CACnE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,EACjB,CAAC,CAAC,IAAI,CAAC,CAAC,QAAe,EAAE,SAAS,CAAC,CAAC,CACrC,CAAC;AAEW,QAAA,iBAAiB,GAAG,KAAK,CAAC,CAAC,CAAM,EAAE,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAIzD,QAAA,gBAAgB,GAAG,eAAO,CACrC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EACb,CAAC,CAAC,MAAM,EACR,CAAC,CAAC,IAAI,CACP,CAAC;AAGW,QAAA,gBAAgB,GAAG,KAAK,CAAC,CAAC,GAAW,EAAE,GAAW,EAAE,EAAE;IACjE,OAAO,eAAO,CACZ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,EACf,CAAC,CAAC,MAAM,EACR,CAAC,CAAC,IAAI,CACP,CAAC,GAAG,CAAC,CAAC;AACT,CAAC,CAAC,CAAC;AAGU,QAAA,gBAAgB,GAAG,KAAK,CAAC,CAAC,GAAW,EAAE,GAAW,EAAE,EAAE;IACjE,OAAO,eAAO,CACZ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,EACf,CAAC,CAAC,MAAM,EACR,CAAC,CAAC,IAAI,CACP,CAAC,GAAG,CAAC,CAAC;AACT,CAAC,CAAC,CAAC"} |
| export declare type ValidationFunction<S> = (state: S) => boolean; | ||
| export declare type ForceValidationState = (validationState: ValidationState) => void; | ||
| export declare type GetAllErrors<S> = (property: keyof S) => string[]; | ||
| export declare type GetError<S> = (property: keyof S) => string; | ||
| export declare type GetFieldValid<S> = (property: keyof S) => boolean; | ||
| export declare type ResetValidationState = () => void; | ||
| export declare type Validate<S> = (property: keyof S, state: S) => boolean; | ||
| export declare type ValidateAll<S> = (state: S, keys?: (keyof S)[]) => boolean; | ||
| export declare type ValidateCustom = (vals: CustomValidation[]) => boolean; | ||
| export declare type ValidateIfTrue<S> = (property: keyof S, state: S) => boolean; | ||
| export declare type ValidateOnBlur<S> = (state: S) => (event: any) => any; | ||
| export declare type ValidateOnChange<S> = (onChange: (event: any) => any, state: S) => (event: any) => any; | ||
| export interface ValidationObject<S> { | ||
| forceValidationState: ForceValidationState; | ||
| getError: GetError<S>; | ||
| getAllErrors: GetAllErrors<S>; | ||
| getFieldValid: GetFieldValid<S>; | ||
| isValid: boolean; | ||
| resetValidationState: ResetValidationState; | ||
| validate: Validate<S>; | ||
| validateAll: ValidateAll<S>; | ||
| validateCustom: ValidateCustom; | ||
| validateIfTrue: ValidateIfTrue<S>; | ||
| validateOnBlur: ValidateOnBlur<S>; | ||
| validateOnChange: ValidateOnChange<S>; | ||
| validationErrors: string[]; | ||
| validationState: ValidationState; | ||
| } | ||
| export interface ValidationProps<S> { | ||
| errorMessage: string; | ||
| validation: ValidationFunction<S>; | ||
| } | ||
| export interface ValidationSchema<S> { | ||
| [key: string]: ValidationProps<S>[]; | ||
| } | ||
| export interface ValidationState { | ||
| [key: string]: { | ||
| isValid: boolean; | ||
| errors: string[]; | ||
| }; | ||
| } | ||
| export interface CustomValidation { | ||
| key: string; | ||
| value: any; | ||
| state?: any; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| //# sourceMappingURL=types.js.map |
| {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/validations/types.ts"],"names":[],"mappings":""} |
| export {}; |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| //# sourceMappingURL=useValidation-old.js.map |
| {"version":3,"file":"useValidation-old.js","sourceRoot":"","sources":["../../src/validations/useValidation-old.ts"],"names":[],"mappings":""} |
| /// <reference types="react" /> | ||
| import { ValidateOnBlur, ValidateOnChange, ValidationSchema } from "../types"; | ||
| export declare const useValidation: <S>(validationSchema: ValidationSchema<S>) => { | ||
| getAllErrors: (property: keyof S, vState?: any) => any; | ||
| getError: (property: keyof S, vState?: any) => any; | ||
| getFieldValid: (property: keyof S, vState?: any) => any; | ||
| isValid: (state?: any) => boolean; | ||
| resetValidationState: () => void; | ||
| setValidationState: import("react").Dispatch<any>; | ||
| validate: (property: keyof S, value: any) => any; | ||
| validateIfTrue: (property: keyof S, value: any) => any; | ||
| validateAll: (value: any, props?: string[]) => any; | ||
| validateAllIfTrue: (value: any, props?: string[]) => any; | ||
| validateOnBlur: ValidateOnBlur<S>; | ||
| validateOnChange: ValidateOnChange<S>; | ||
| validationErrors: never[]; | ||
| validationState: any; | ||
| }; |
| "use strict"; | ||
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
| }) : (function(o, m, k, k2) { | ||
| if (k2 === undefined) k2 = k; | ||
| o[k2] = m[k]; | ||
| })); | ||
| var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| }) : function(o, v) { | ||
| o["default"] = v; | ||
| }); | ||
| var __importStar = (this && this.__importStar) || function (mod) { | ||
| if (mod && mod.__esModule) return mod; | ||
| var result = {}; | ||
| if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
| __setModuleDefault(result, mod); | ||
| return result; | ||
| }; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.useValidation = void 0; | ||
| const react_1 = require("react"); | ||
| const R = __importStar(require("ramda")); | ||
| const maybe_1 = require("../maybe"); | ||
| const utilities_1 = require("../utilities"); | ||
| exports.useValidation = (validationSchema) => { | ||
| const createValidationsState = (schema) => { | ||
| const buildState = (acc, key) => ({ | ||
| ...acc, | ||
| [key]: { isValid: true, errors: [] }, | ||
| }); | ||
| const state = maybe_1.maybe(schema) | ||
| .map(R.keys) | ||
| .map(R.reduce(buildState, {})); | ||
| return state.isJust ? state.join() : {}; | ||
| }; | ||
| const [validationState, setValidationState] = react_1.useState(createValidationsState(validationSchema)); | ||
| const [validationErrors, setValidationErros] = react_1.useState([]); | ||
| const updateValidationState = utilities_1.executeSideEffect(setValidationState); | ||
| const resetValidationState = () => R.pipe(createValidationsState, setValidationState)(validationSchema); | ||
| const updatePropertyOnState = R.curry((property, value) => { | ||
| const valueIsValid = R.pipe(utilities_1.prop("validation"), R.applyTo(value)); | ||
| const getErrorOrNone = R.ifElse(valueIsValid, R.always(''), R.prop('error')); | ||
| const state = maybe_1.maybe(validationSchema) | ||
| .map(utilities_1.prop(property)) | ||
| .map(R.values) | ||
| .map(R.map(getErrorOrNone)) | ||
| .map(R.filter((x) => x.length > 0)) | ||
| .map((errors) => ({ errors, isValid: !errors.length })) | ||
| .map(R.assoc(property, R.__, validationState)); | ||
| return state.isJust ? state.join() : validationState; | ||
| }); | ||
| const validate = (property, value) => maybe_1.maybe(value) | ||
| .map(updatePropertyOnState(property)) | ||
| .map(R.mergeRight(validationState)) | ||
| .map(updateValidationState) | ||
| .map(utilities_1.isPropertyValid(property)) | ||
| .chain(R.defaultTo(true)); | ||
| const validateIfTrue = (property, value) => maybe_1.maybe(value) | ||
| .map(updatePropertyOnState(property)) | ||
| .map(R.mergeRight(validationState)) | ||
| .map(R.ifElse(utilities_1.isPropertyValid(property), updateValidationState, R.always(null))) | ||
| .chain(R.defaultTo(true)); | ||
| const validateAll = (value, props = Object.keys(validationSchema)) => { | ||
| const reduceStateUpdates = (acc, property) => ({ | ||
| ...acc, | ||
| ...updatePropertyOnState(property, value), | ||
| }); | ||
| return maybe_1.maybe(props) | ||
| .map(R.reduce(reduceStateUpdates, {})) | ||
| .map(R.mergeRight(validationState)) | ||
| .map(updateValidationState) | ||
| .map(isValid) | ||
| .chain(R.defaultTo(true)); | ||
| }; | ||
| const validateAllIfTrue = (value, props = Object.keys(validationSchema)) => { | ||
| const reduceValids = (acc, property) => { | ||
| const updated = updatePropertyOnState(property, value); | ||
| return updated[property].isValid | ||
| ? { ...acc, ...updated } | ||
| : { ...acc, ...validationState[property] }; | ||
| }; | ||
| return maybe_1.maybe(props) | ||
| .map(R.reduce(reduceValids, {})) | ||
| .map(R.mergeRight(validationState)) | ||
| .map(isValid) | ||
| .chain(R.defaultTo(true)); | ||
| }; | ||
| const validateOnBlur = (value) => (event) => { | ||
| const { name } = event.target; | ||
| validate(name, value); | ||
| }; | ||
| const validateOnChange = (onChange, value) => (event) => { | ||
| const { name } = event.target; | ||
| validateIfTrue(name, value); | ||
| return onChange(event); | ||
| }; | ||
| const getAllErrors = (property, vState = validationState) => { | ||
| const errors = maybe_1.maybe(vState) | ||
| .map(utilities_1.prop(property)) | ||
| .map(utilities_1.prop("errors")); | ||
| return errors.isJust ? errors.join() : []; | ||
| }; | ||
| const getError = (property, vState = validationState) => { | ||
| const error = maybe_1.maybe(vState) | ||
| .map(utilities_1.prop(property)) | ||
| .map(utilities_1.prop("errors")) | ||
| .map(R.head); | ||
| return error.isJust ? error.join() : ""; | ||
| }; | ||
| const getFieldValid = (property, vState = validationState) => { | ||
| const valid = maybe_1.maybe(vState) | ||
| .map(utilities_1.prop(property)) | ||
| .map(utilities_1.prop("isValid")); | ||
| return valid.isJust ? valid.join() : true; | ||
| }; | ||
| const isValid = (state = validationState) => { | ||
| return R.reduce((acc, curr) => acc ? utilities_1.isPropertyValid(curr)(state) : acc, true, Object.keys(state)); | ||
| }; | ||
| const generateValidationErrors = (state) => { | ||
| return R.reduce((acc, curr) => getError(curr) ? [...acc, getError(curr)] : acc, [], Object.keys(state)); | ||
| }; | ||
| react_1.useEffect(() => { | ||
| setValidationErros(generateValidationErrors(validationState)); | ||
| }, [validationState]); | ||
| return { | ||
| getAllErrors, | ||
| getError, | ||
| getFieldValid, | ||
| isValid, | ||
| resetValidationState, | ||
| setValidationState, | ||
| validate, | ||
| validateIfTrue, | ||
| validateAll, | ||
| validateAllIfTrue, | ||
| validateOnBlur, | ||
| validateOnChange, | ||
| validationErrors, | ||
| validationState, | ||
| }; | ||
| }; | ||
| //# sourceMappingURL=useValidation.js.map |
| {"version":3,"file":"useValidation.js","sourceRoot":"","sources":["../../src/validations/useValidation.ts"],"names":[],"mappings":"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"} |
| import { ForceValidationState, GetAllErrors, GetError, GetFieldValid, ResetValidationState, Validate, ValidateAll, ValidateCustom, ValidateIfTrue, ValidateOnBlur, ValidateOnChange, ValidationSchema, ValidationState } from './types'; | ||
| export declare class Validation<S> { | ||
| private _validationSchema; | ||
| private _validationState; | ||
| get isValid(): boolean; | ||
| get validationErrors(): string[]; | ||
| get validationState(): ValidationState; | ||
| constructor(props: ValidationSchema<S>); | ||
| private createValidationsState; | ||
| resetValidationState: ResetValidationState; | ||
| forceValidationState: ForceValidationState; | ||
| private allValid; | ||
| private runAllValidators; | ||
| getError: GetError<S>; | ||
| getAllErrors: GetAllErrors<S>; | ||
| getFieldValid: GetFieldValid<S>; | ||
| validate: Validate<S>; | ||
| validateAll: ValidateAll<S>; | ||
| validateCustom: ValidateCustom; | ||
| validateIfTrue: ValidateIfTrue<S>; | ||
| validateOnBlur: ValidateOnBlur<S>; | ||
| validateOnChange: ValidateOnChange<S>; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.Validation = void 0; | ||
| const utilities_1 = require("../utilities"); | ||
| const ramda_1 = require("ramda"); | ||
| class Validation { | ||
| constructor(props) { | ||
| this.createValidationsState = (schema) => { | ||
| return ramda_1.reduce((acc, item) => ({ | ||
| ...acc, | ||
| [item]: { | ||
| isValid: true, | ||
| errors: [], | ||
| }, | ||
| }), {}, Object.keys(schema)); | ||
| }; | ||
| this.resetValidationState = () => { | ||
| this._validationState = this.createValidationsState(this._validationSchema); | ||
| }; | ||
| this.forceValidationState = (newValidationState) => { | ||
| this._validationState = newValidationState; | ||
| }; | ||
| this.allValid = (state) => { | ||
| const keys = Object.keys(state); | ||
| const valid = ramda_1.reduce((acc, current) => { | ||
| return acc ? utilities_1.isPropertyValid(current, this._validationState) : acc; | ||
| }, true, keys); | ||
| return valid; | ||
| }; | ||
| this.runAllValidators = (property, state) => { | ||
| const runValidator = utilities_1.compose((func) => func(state), utilities_1.prop('validation')); | ||
| const bools = ramda_1.map(runValidator, utilities_1.prop(property, this._validationSchema)); | ||
| const allValidationsValid = utilities_1.all(bools); | ||
| const errors = bools.reduce((acc, curr, idx) => { | ||
| const errorOf = utilities_1.compose(utilities_1.prop('errorMessage'), utilities_1.prop(idx), utilities_1.prop(property)); | ||
| return curr ? acc : [...acc, errorOf(this._validationSchema)]; | ||
| }, []); | ||
| return { | ||
| [property]: { | ||
| isValid: allValidationsValid, | ||
| errors: allValidationsValid ? [] : errors, | ||
| }, | ||
| }; | ||
| }; | ||
| this.getError = (property) => { | ||
| if (property in this._validationSchema) { | ||
| const val = utilities_1.compose(ramda_1.head, utilities_1.prop('errors'), utilities_1.prop(property)); | ||
| return val(this._validationState) ? val(this._validationState) : ''; | ||
| } | ||
| return ''; | ||
| }; | ||
| this.getAllErrors = (property) => { | ||
| if (property in this._validationSchema) { | ||
| const val = utilities_1.compose(utilities_1.prop('errors'), utilities_1.prop(property)); | ||
| return val(this._validationState); | ||
| } | ||
| return []; | ||
| }; | ||
| this.getFieldValid = (property, vState = this._validationState) => { | ||
| if (property in this._validationSchema) { | ||
| return utilities_1.isPropertyValid(property, vState); | ||
| } | ||
| return true; | ||
| }; | ||
| this.validate = (property, state) => { | ||
| if (property in this._validationSchema) { | ||
| const validations = this.runAllValidators(property, state); | ||
| this._validationState = { | ||
| ...this._validationState, | ||
| ...validations, | ||
| }; | ||
| return utilities_1.isPropertyValid(property, validations); | ||
| } | ||
| return true; | ||
| }; | ||
| this.validateAll = (state, props) => { | ||
| const newState = ramda_1.reduce((acc, property) => { | ||
| return { ...acc, ...this.runAllValidators(property, state) }; | ||
| }, {}, props !== null && props !== void 0 ? props : Object.keys(this._validationSchema)); | ||
| this._validationState = newState; | ||
| return this.allValid(newState); | ||
| }; | ||
| this.validateCustom = (customValidations) => { | ||
| const zip = ramda_1.converge(this.runAllValidators, [ | ||
| utilities_1.prop('key'), | ||
| utilities_1.prop('value'), | ||
| utilities_1.prop('state'), | ||
| ]); | ||
| const state = ramda_1.reduce((acc, current) => { | ||
| return { | ||
| ...acc, | ||
| ...zip(current), | ||
| }; | ||
| }, {}, customValidations); | ||
| this._validationState = state; | ||
| return this.allValid(state); | ||
| }; | ||
| this.validateIfTrue = (property, state) => { | ||
| if (property in this._validationSchema) { | ||
| const validations = this.runAllValidators(property, state); | ||
| if (utilities_1.isPropertyValid(property, validations)) { | ||
| const updated = { ...this._validationState, ...validations }; | ||
| this._validationState = updated; | ||
| } | ||
| return utilities_1.isPropertyValid(property, validations); | ||
| } | ||
| return true; | ||
| }; | ||
| this.validateOnBlur = (state) => { | ||
| return (event) => { | ||
| const { value, name } = event.target; | ||
| this.validate(name, { ...state, [name]: value }); | ||
| }; | ||
| }; | ||
| this.validateOnChange = (onChange, state) => { | ||
| return (event) => { | ||
| const { value, name } = event.target; | ||
| this.validateIfTrue(name, { ...state, [name]: value }); | ||
| return onChange(event); | ||
| }; | ||
| }; | ||
| this._validationSchema = props; | ||
| this._validationState = this.createValidationsState(props); | ||
| } | ||
| get isValid() { | ||
| return this.allValid(this._validationState); | ||
| } | ||
| get validationErrors() { | ||
| const props = Object.keys(this._validationState); | ||
| const errors = ramda_1.reduce((acc, curr) => { | ||
| const err = this.getError(curr); | ||
| return err ? [...acc, err] : acc; | ||
| }, [], props); | ||
| return errors; | ||
| } | ||
| get validationState() { | ||
| return this._validationState; | ||
| } | ||
| } | ||
| exports.Validation = Validation; | ||
| //# sourceMappingURL=validation.js.map |
| {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/validations/validation.ts"],"names":[],"mappings":"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"} |
| import { renderHook, act } from '@testing-library/react-hooks'; | ||
| import { useValidation } from '../'; | ||
| import { ValidationSchema, ValidationState } from '../types'; | ||
| type TestSchema = { | ||
| name: string; | ||
| age: number; | ||
| dingo: boolean; | ||
| agreement: boolean; | ||
| }; | ||
| const schema: ValidationSchema<TestSchema> = { | ||
| name: [ | ||
| { | ||
| error: 'Name is required.', | ||
| validation: (state: TestSchema) => state.name.length > 0, | ||
| }, | ||
| { | ||
| error: 'Cannot be bob.', | ||
| validation: (state: TestSchema) => state.name !== 'bob', | ||
| }, | ||
| { | ||
| error: 'Must be dingo.', | ||
| validation: (state: TestSchema) => { | ||
| return state.dingo ? state.name === 'dingo' : true; | ||
| }, | ||
| }, | ||
| ], | ||
| age: [ | ||
| { | ||
| error: 'Must be 18', | ||
| validation: (state: TestSchema) => state.age >= 18, | ||
| }, | ||
| ], | ||
| agreement: [ | ||
| { | ||
| error: 'Must accept terms.', | ||
| validation: ({ agreement }) => Boolean(agreement), | ||
| }, | ||
| ], | ||
| }; | ||
| const mockValidationState: ValidationState = { | ||
| name: { | ||
| isValid: true, | ||
| errors: [], | ||
| }, | ||
| age: { | ||
| isValid: true, | ||
| errors: [], | ||
| }, | ||
| agreement: { | ||
| isValid: true, | ||
| errors: [], | ||
| }, | ||
| }; | ||
| const defaultState = { | ||
| name: 'jack', | ||
| dingo: false, | ||
| age: 42, | ||
| agreement: true, | ||
| }; | ||
| const failingState = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| age: 15, | ||
| }; | ||
| describe('useValidation tests', () => { | ||
| it('should be defined', () => { | ||
| expect(useValidation).toBeDefined(); | ||
| }); | ||
| it('renders the hook correctly and checks types', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| expect(typeof result.current.getError).toBe('function'); | ||
| expect(typeof result.current.getFieldValid).toBe('function'); | ||
| expect(typeof result.current.isValid).toBe('boolean'); | ||
| expect(typeof result.current.validate).toBe('function'); | ||
| expect(typeof result.current.validateAll).toBe('function'); | ||
| expect(typeof result.current.validateAllIfTrue).toBe('function'); | ||
| expect(typeof result.current.validateIfTrue).toBe('function'); | ||
| expect(typeof result.current.validateOnBlur).toBe('function'); | ||
| expect(typeof result.current.validateOnChange).toBe('function'); | ||
| expect(typeof result.current.resetValidationState).toBe('function'); | ||
| expect(typeof result.current.setValidationState).toBe('function'); | ||
| expect(Array.isArray(result.current.validationErrors)).toBe(true); | ||
| expect(typeof result.current.validationState).toBe('object'); | ||
| }); | ||
| it('returns all functions and read-only objects defined by hook', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| expect(result.current.validationState).toStrictEqual(mockValidationState); | ||
| expect(Object.keys(result.current)).toStrictEqual([ | ||
| 'getAllErrors', | ||
| 'getError', | ||
| 'getFieldValid', | ||
| 'isValid', | ||
| 'resetValidationState', | ||
| 'setValidationState', | ||
| 'validate', | ||
| 'validateAll', | ||
| 'validateAllIfTrue', | ||
| 'validateIfTrue', | ||
| 'validateOnBlur', | ||
| 'validateOnChange', | ||
| 'validationErrors', | ||
| 'validationState', | ||
| ]); | ||
| }); | ||
| describe('createValidationState', () => { | ||
| it('crates a validation state when given a schema', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| expect(result.current.validationState).toStrictEqual(mockValidationState); | ||
| }); | ||
| it('defaults to an empty object if null or undefined is provided', () => { | ||
| const { result } = renderHook(() => useValidation(null as any)); | ||
| expect(result.current.validationState).toStrictEqual({}); | ||
| }); | ||
| }); | ||
| describe('getError', () => { | ||
| it('returns empty string by default', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getError('name'); | ||
| expect(output).toBe(''); | ||
| }); | ||
| it('returns empty string if the property does not exist', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getError('balls' as keyof TestSchema); | ||
| expect(output).toBe(''); | ||
| }); | ||
| it('retrieves an error message', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| result.current.validate('name', { name: '' } as any); | ||
| }); | ||
| const output = result.current.getError('name'); | ||
| expect(output).toBe('Name is required.'); | ||
| }); | ||
| }); | ||
| describe('getAllErrors', () => { | ||
| it('returns empty array by default', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getAllErrors('name'); | ||
| expect(output).toStrictEqual([]); | ||
| }); | ||
| it('returns empty array if the property does not exist', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getAllErrors('balls' as keyof TestSchema); | ||
| expect(output).toStrictEqual([]); | ||
| }); | ||
| it('retrieves array of all error messages', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: '', | ||
| }; | ||
| act(() => { | ||
| result.current.validate(name, state); | ||
| }); | ||
| const output = result.current.getAllErrors('name'); | ||
| expect(output).toStrictEqual(['Name is required.']); | ||
| }); | ||
| }); | ||
| describe('getFieldValid', () => { | ||
| it('returns true by default', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getFieldValid('name'); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('returns true if the property does not exist', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.getFieldValid('balls' as keyof TestSchema); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('retrieves an invalid state', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: '', | ||
| }; | ||
| act(() => { | ||
| result.current.validate(name, state); | ||
| }); | ||
| const output = result.current.getFieldValid('name'); | ||
| expect(output).toBe(false); | ||
| }); | ||
| }); | ||
| describe('isValid', () => { | ||
| it('returns true by default', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const output = result.current.isValid; | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('changes to false after a validation fails', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| result.current.validate(name, state); | ||
| }); | ||
| const output = result.current.isValid; | ||
| expect(output).toBe(false); | ||
| }); | ||
| it('changes to true after a failed validation passes', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| const state2 = { | ||
| ...defaultState, | ||
| name: 'bob ross', | ||
| }; | ||
| act(() => { | ||
| result.current.validate(name, state); | ||
| result.current.validate(name, state2); | ||
| }); | ||
| const output = result.current.isValid; | ||
| expect(output).toBe(true); | ||
| }); | ||
| }); | ||
| describe('validate', () => { | ||
| it('returns a boolean if key exists', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| output = result.current.validate(name, state); | ||
| }); | ||
| expect(typeof output).toBe('boolean'); | ||
| }); | ||
| it('returns true if key does not exist', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'balls' as keyof TestSchema; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validate(name, state); | ||
| }); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('updates the validationState when validation fails', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const validationState = { | ||
| ...mockValidationState, | ||
| name: { | ||
| isValid: false, | ||
| errors: ['Must be dingo.'], | ||
| }, | ||
| }; | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'chuck', | ||
| dingo: true, | ||
| }; | ||
| act(() => { | ||
| result.current.validate(name, state); | ||
| }); | ||
| expect(result.current.isValid).toBe(false); | ||
| expect(result.current.validationState).toStrictEqual(validationState); | ||
| }); | ||
| }); | ||
| describe('validateAll', () => { | ||
| it('returns a boolean', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validateAll(defaultState); | ||
| }); | ||
| expect(typeof output).toBe('boolean'); | ||
| }); | ||
| it('returns true if validations pass', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validateAll(defaultState); | ||
| }); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('returns false if any validation fails', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| const output = result.current.validateAll(failingState); | ||
| expect(output).toBe(false); | ||
| }); | ||
| }); | ||
| it('returns all failing validations', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| result.current.validateAll(failingState); | ||
| }); | ||
| expect(result.current.validationState).toStrictEqual({ | ||
| name: { | ||
| errors: ['Cannot be bob.'], | ||
| isValid: false, | ||
| }, | ||
| age: { | ||
| errors: ['Must be 18'], | ||
| isValid: false, | ||
| }, | ||
| agreement: { | ||
| errors: [], | ||
| isValid: true, | ||
| }, | ||
| }); | ||
| }); | ||
| it('handles nested validation reductions', () => { | ||
| const data = [defaultState, defaultState, defaultState]; | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: boolean[]; | ||
| act(() => { | ||
| output = data.map((s) => result.current.validateAll(s)); | ||
| expect(output).toStrictEqual([true, true, true]); | ||
| }); | ||
| }); | ||
| it('validates a subsection of keys', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| result.current.validateAll(failingState); | ||
| }); | ||
| expect(result.current.getError('age')).toBe('Must be 18'); | ||
| act(() => { | ||
| result.current.validateAll(failingState, ['name']); | ||
| }); | ||
| expect(result.current.getError('age')).toBe('Must be 18'); | ||
| }); | ||
| it('handles missing properties', () => { | ||
| const wonkySchema = { | ||
| ...schema, | ||
| canSave: [ | ||
| { | ||
| error: 'you cannot save', | ||
| validation: (state: any) => !!state.name, | ||
| }, | ||
| ], | ||
| }; | ||
| const { result } = renderHook(() => useValidation(wonkySchema)); | ||
| act(() => { | ||
| result.current.validateAll(failingState); | ||
| }); | ||
| expect(result.current.getError('canSave')).toBe(''); | ||
| }); | ||
| }); | ||
| describe('validateAllIfTrue', () => { | ||
| it('returns a boolean', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validateAllIfTrue(defaultState); | ||
| }); | ||
| expect(typeof output).toBe('boolean'); | ||
| }); | ||
| it('returns true if validations pass', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validateAllIfTrue(defaultState); | ||
| }); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('ignores failing validations', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| const output = result.current.validateAllIfTrue(failingState); | ||
| expect(output).toBe(true); | ||
| }); | ||
| }); | ||
| it('handles nested validation reductions', () => { | ||
| const data = [defaultState, defaultState, defaultState]; | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: boolean[]; | ||
| act(() => { | ||
| output = data.map((s) => result.current.validateAllIfTrue(s)); | ||
| expect(output).toStrictEqual([true, true, true]); | ||
| }); | ||
| }); | ||
| it('validates a subsection of keys', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| result.current.validateAllIfTrue(failingState); | ||
| }); | ||
| expect(result.current.getError('age')).toBe(''); | ||
| act(() => { | ||
| result.current.validateAllIfTrue(failingState, ['name']); | ||
| }); | ||
| expect(result.current.getError('age')).toBe(''); | ||
| }); | ||
| it('handles missing properties', () => { | ||
| const wonkySchema = { | ||
| ...schema, | ||
| canSave: [ | ||
| { | ||
| error: 'you cannot save', | ||
| validation: (state: any) => !!state.name, | ||
| }, | ||
| ], | ||
| }; | ||
| const { result } = renderHook(() => useValidation(wonkySchema)); | ||
| act(() => { | ||
| result.current.validateAllIfTrue(failingState); | ||
| }); | ||
| expect(result.current.getError('canSave')).toBe(''); | ||
| }); | ||
| }); | ||
| describe('validateIfTrue', () => { | ||
| it('returns a boolean if key exists', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| let output: any; | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| output = result.current.validateIfTrue(name, state); | ||
| }); | ||
| expect(typeof output).toBe('boolean'); | ||
| }); | ||
| it('returns true if key does not exist', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const name = 'balls' as keyof TestSchema; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| let output: any; | ||
| act(() => { | ||
| output = result.current.validateIfTrue(name, state); | ||
| }); | ||
| expect(output).toBe(true); | ||
| }); | ||
| it('updates the validationState when validation fails', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const validationState = { | ||
| ...mockValidationState, | ||
| }; | ||
| const name = 'name'; | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'chuck', | ||
| dingo: true, | ||
| }; | ||
| act(() => { | ||
| result.current.validateIfTrue(name, state); | ||
| }); | ||
| expect(result.current.isValid).toBe(true); | ||
| expect(result.current.validationState).toStrictEqual(validationState); | ||
| }); | ||
| it('updates the validationState when an invalid validation succeeds', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| const state2 = { | ||
| ...defaultState, | ||
| name: 'jack', | ||
| }; | ||
| const validationState = { | ||
| ...mockValidationState, | ||
| }; | ||
| act(() => { | ||
| result.current.validate('name', state); | ||
| }); | ||
| expect(result.current.isValid).toBe(false); | ||
| act(() => { | ||
| result.current.validateIfTrue('name', state2); | ||
| }); | ||
| expect(result.current.isValid).toBe(true); | ||
| expect(result.current.validationState).toStrictEqual(validationState); | ||
| }); | ||
| }); | ||
| describe('validateOnBlur', () => { | ||
| it('returns a new function', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = defaultState; | ||
| const handleBlur = result.current.validateOnBlur(state); | ||
| expect(typeof handleBlur).toBe('function'); | ||
| }); | ||
| it('updates the valdiation state when called', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = defaultState; | ||
| const handleBlur = result.current.validateOnBlur(state); | ||
| const event = { | ||
| target: { | ||
| name: 'name', | ||
| value: 'bob', | ||
| dispatchEvent: new Event('blur'), | ||
| }, | ||
| }; | ||
| act(() => { | ||
| handleBlur(event as any); | ||
| }); | ||
| expect(result.current.isValid).toBe(false); | ||
| }); | ||
| }); | ||
| describe('validateOnChange', () => { | ||
| it('returns a new function', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = defaultState; | ||
| const onChange = (event: any) => 'bob ross'; | ||
| const handleChange = result.current.validateOnChange(onChange, state); | ||
| expect(typeof handleChange).toBe('function'); | ||
| }); | ||
| it('updates the valdiation state if true and returns event', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| result.current.validate('name', state); | ||
| }); | ||
| expect(result.current.isValid).toBe(false); | ||
| const onChange = () => 'bob ross'; | ||
| const handleChange = result.current.validateOnChange(onChange, state); | ||
| const event = { | ||
| target: { | ||
| name: 'name', | ||
| value: 'jack', | ||
| dispatchEvent: new Event('change'), | ||
| }, | ||
| }; | ||
| let output: any; | ||
| act(() => { | ||
| output = handleChange(event as any); | ||
| }); | ||
| expect(result.current.isValid).toBe(true); | ||
| expect(output).toBe('bob ross'); | ||
| }); | ||
| it('updates checked properties if true and returns event', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| agreement: false, | ||
| }; | ||
| act(() => { | ||
| result.current.validate('agreement', state); | ||
| }); | ||
| expect(result.current.isValid).toBe(false); | ||
| const onChange = () => true; | ||
| const handleChange = result.current.validateOnChange(onChange, state); | ||
| const event = { | ||
| target: { | ||
| name: 'agreement', | ||
| checked: true, | ||
| dispatchEvent: new Event('change'), | ||
| type: 'checkbox', | ||
| }, | ||
| }; | ||
| let output: any; | ||
| act(() => { | ||
| output = handleChange(event as any); | ||
| }); | ||
| expect(result.current.isValid).toBe(true); | ||
| expect(output).toBe(true); | ||
| }); | ||
| }); | ||
| describe('resetValidationState', () => { | ||
| it('resets the validation state', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| result.current.validate('name', state); | ||
| result.current.resetValidationState(); | ||
| }); | ||
| expect(result.current.isValid).toBe(true); | ||
| }); | ||
| }); | ||
| describe('validationErrors', () => { | ||
| it('starts as an empty array', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| expect(result.current.validationErrors).toStrictEqual([]); | ||
| }); | ||
| it('adds validation errors when validation state is invalid', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| act(() => { | ||
| result.current.validate('name', state); | ||
| }); | ||
| expect(result.current.validationErrors).toStrictEqual(['Cannot be bob.']); | ||
| }); | ||
| it('removes validation errors when validation state is valid', () => { | ||
| const { result } = renderHook(() => useValidation(schema)); | ||
| const state = { | ||
| ...defaultState, | ||
| name: 'bob', | ||
| }; | ||
| const state2 = { | ||
| ...defaultState, | ||
| name: 'jack', | ||
| }; | ||
| act(() => { | ||
| result.current.validate('name', state); | ||
| result.current.validate('name', state2); | ||
| }); | ||
| expect(result.current.validationErrors).toStrictEqual([]); | ||
| }); | ||
| }); | ||
| describe('forceValidationState', () => { | ||
| it('overrides the existing validation state with a new one', () => { | ||
| const { result: v1 } = renderHook(() => useValidation(schema)); | ||
| const { result: v2 } = renderHook(() => useValidation(schema)); | ||
| act(() => { | ||
| v1.current.validateAll(failingState); | ||
| }); | ||
| act(() => { | ||
| v2.current.setValidationState(v1.current.validationState); | ||
| }); | ||
| expect(v1.current.validationState).toStrictEqual( | ||
| v2.current.validationState, | ||
| ); | ||
| }); | ||
| }); | ||
| }); |
-49
| export type GetAllErrors<S> = (property: keyof S) => string[]; | ||
| export type GetError<S> = (property: keyof S) => string; | ||
| export type GetFieldValid<S> = (property: keyof S) => boolean; | ||
| export type IsValid = boolean; | ||
| export type ResetValidationState = () => void; | ||
| export type SetValidationState = (validationState: ValidationState) => void; | ||
| export type Validate<S> = (property: keyof S, value: S) => boolean; | ||
| export type ValidateAll<S> = (value: S, keys?: (keyof S)[]) => boolean; | ||
| export type ValidateAllIfTrue<S> = (value: S, keys?: (keyof S)[]) => boolean; | ||
| export type ValidateIfTrue<S> = (property: keyof S, value: S) => boolean; | ||
| export type ValidateOnBlur<S> = (value: S) => (event: any) => any; | ||
| export type ValidateOnChange<S> = ( | ||
| onChange: (event: any) => any, | ||
| value: S, | ||
| ) => (event: any) => any; | ||
| export type ValidationFunction<S> = (value: S) => boolean; | ||
| export interface ValidationObject<S> { | ||
| getAllErrors: GetAllErrors<S>; | ||
| getError: GetError<S>; | ||
| getFieldValid: GetFieldValid<S>; | ||
| isValid: boolean; | ||
| resetValidationState: ResetValidationState; | ||
| setValidationState: SetValidationState; | ||
| validate: Validate<S>; | ||
| validateAll: ValidateAll<S>; | ||
| validateAllIfTrue: ValidateAllIfTrue<S>; | ||
| validateIfTrue: ValidateIfTrue<S>; | ||
| validateOnBlur: ValidateOnBlur<S>; | ||
| validateOnChange: ValidateOnChange<S>; | ||
| validationErrors: string[]; | ||
| validationState: ValidationState; | ||
| } | ||
| export interface ValidationProps<S> { | ||
| error: string; | ||
| validation: ValidationFunction<S>; | ||
| } | ||
| export interface ValidationSchema<S> { | ||
| [key: string]: ValidationProps<S>[]; | ||
| } | ||
| export interface ValidationState { | ||
| [key: string]: { | ||
| isValid: boolean; | ||
| errors: string[]; | ||
| }; | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
132
10.92%36918
-64.63%15
15.38%14
-61.11%829
-41.12%1
Infinity%+ Added
- Removed
- Removed
- Removed
- Removed
Updated