import * as React from 'react'; import { Validation, ValidationState } from '../../models/Validation'; import { getConfigByNames } from '../../utils/helper'; import { SecondaryButtonProps } from '../secondary-button/SecondaryButton'; import { SubmitButtonProps } from '../submit-button/SubmitButton'; import { ConfirmButtonProps } from '../confirm-button/ConfirmButton'; import { Application } from '../../models/application'; import { ServerErrors } from '../../models/serverErrors'; import { Colors } from '../../models/colors'; import SubmitButtonOne from '../../components/submit-buttons/submit-button-one/SubmitButtonOne'; import { Query } from '../../models/query'; export interface TemplateProps { name: string; moreButtons?: Array>; submitButton?: React.ReactElement; translator?: Function; checkIfTranslateExist?: Function; } export type TemplateContainer = TemplateProps & { query?: Query; onSubmit?: any; application?: Application; submitButton?: React.ReactElement; moreButtons?: Array>; palette?: Colors; onChange?: (name: string, value: any) => void; serverErrors?: ServerErrors; config?: { [key: string]: any }; render?: ( props: { elements: JSX.Element[]; palette: Colors; onSubmit: any; submitButton: JSX.Element; } ) => JSX.Element; }; export interface TemplateState { validations: { [name: string]: Validation | any }; } class Template extends React.PureComponent { constructor(props: TemplateContainer) { super(props); this.state = { validations: {} }; document.addEventListener( 'invalid', (() => { return (e: any) => { e.preventDefault(); const firstInvalid = document.querySelector('input:invalid') as HTMLInputElement; window.scroll({ top: firstInvalid.getBoundingClientRect().top - document.body.getBoundingClientRect().top - 300, behavior: 'smooth' }); setTimeout(() => firstInvalid.focus(), 400); }; })(), true ); } onSubmit = (props: any) => { if (this.checkValidity()) { if (props.preventDefault) props.preventDefault(); this.props.onSubmit(props); } return false; }; checkValidity = () => { let valid = true; let validations = Object.assign({}, this.state.validations); for (let key in this.state.validations) { if (validations[key].state) { if ( validations.hasOwnProperty(key) && validations[key].state === ValidationState.Required ) { validations[key] = Object.assign({}, validations[key], { state: ValidationState.Invalid }); valid = false; } if (validations.hasOwnProperty(key) && validations[key].state === ValidationState.Invalid) { valid = false; } } else { let innerObj: { [key: string]: Validation } = {}; for (let innerKey in this.state.validations[key]) { if ( validations[key].hasOwnProperty(innerKey) && validations[key][innerKey].state === ValidationState.Required ) { innerObj[innerKey] = Object.assign( innerObj[innerKey] || {}, validations[key][innerKey], { state: ValidationState.Invalid } ); valid = false; } if ( validations[key].hasOwnProperty(innerKey) && validations[key][innerKey].state === ValidationState.Invalid ) { valid = false; } validations[key] = Object.assign({}, validations[key], innerObj); } } } this.setState(prevState => { return { validations: Object.assign({}, prevState.validations, validations) }; }); return valid; }; notifyValidity = (name: string, state: ValidationState, errorMessage: string) => { this.setState(prevState => { if (name.lastIndexOf('.') > 0) { let prefix = name.substring(0, name.lastIndexOf('.')); let suffix = name.substring(name.lastIndexOf('.') + 1); let validationObject = Object.assign({}, prevState.validations[prefix], { [suffix]: { state, errorMessage } }); let validations = Object.assign({}, prevState.validations, { [prefix]: validationObject }); return { validations }; } let validations = Object.assign({}, prevState.validations, { [name]: { state, errorMessage } }); return { validations }; }); }; getConfigByNames = (name: string | string[]) => { return getConfigByNames(name, this.props.config); }; render() { const { children, palette } = this.props; const childrenWithProps = React.Children.map(children, (child: any) => { let props = { notifyValidity: this.notifyValidity, getConfig: this.getConfigByNames, validationState: this.state.validations[child.props.name], validations: this.state.validations, palette, onChange: this.props.onChange, value: this.props.application[child.props.name], application: this.props.application, serverErrors: (this.props.serverErrors || {})[child.props.name], templateName: this.props.name, checkIfTranslateExist: this.props.checkIfTranslateExist, translator: this.props.translator, query: this.props.query }; return React.cloneElement(child, props); }); const submitButton = React.cloneElement(this.props.submitButton || , { onSubmit: this.onSubmit, palette, checkIfTranslateExist: this.props.checkIfTranslateExist, translator: this.props.translator }); return this.props.render({ elements: childrenWithProps, palette: this.props.palette, onSubmit: this.onSubmit, submitButton }); } } export default Template;