UNPKG

storm-validate

Version:

[![npm version](https://badge.fury.io/js/storm-validate.svg)](https://badge.fury.io/js/storm-validate)

167 lines (151 loc) 5.41 kB
import { DOTNET_CLASSNAMES } from '../constants'; //Track error message DOM nodes in local scope => ;_; // let errorNodes = {}; /** * Hypertext DOM factory function * * @param nodeName [String] * @param attributes [Object] * @param text [String] The innerText of the new node * * @returns node [DOM node] * */ export const h = (nodeName, attributes, text) => { let node = document.createElement(nodeName); for(let prop in attributes) { node.setAttribute(prop, attributes[prop]); } if(text !== undefined && text.length) node.appendChild(document.createTextNode(text)); return node; }; /** * Creates and appends a text node error message to a error container DOM node for a group * * @param group [Object, vaidation group] * @param msg [String] The error message * * @returns node [Text node] * */ export const createErrorTextNode = (group, msg) => { let node = document.createTextNode(msg); group.serverErrorNode.classList.remove(DOTNET_CLASSNAMES.VALID); group.serverErrorNode.classList.add(DOTNET_CLASSNAMES.ERROR); return group.serverErrorNode.appendChild(node); }; /** * Removes the error message DOM node, updates .NET MVC error span classNames and deletes the * error from local errorNodes tracking object * * Signature () => groupName => state => {} * (Curried groupName for ease of use as eventListener and in whole form iteration) * * @param groupName [String, vaidation group] * @param state [Object, validation state] * */ export const clearError = groupName => state => { state.errorNodes[groupName].parentNode.removeChild(state.errorNodes[groupName]); // errorNodes[groupName].parentNode.removeChild(errorNodes[groupName]); if(state.groups[groupName].serverErrorNode) { state.groups[groupName].serverErrorNode.classList.remove(DOTNET_CLASSNAMES.ERROR); state.groups[groupName].serverErrorNode.classList.add(DOTNET_CLASSNAMES.VALID); } state.groups[groupName].fields.forEach(field => { field.parentNode.classList.remove('is--invalid'); field.removeAttribute('aria-invalid'); }); delete state.errorNodes[groupName];//shouldn't be doing this here... }; /** * Iterates over all errorNodes in local scope to remove each error prior to re-validation * * @param state [Object, validation state] * */ export const clearErrors = state => { state.errorNodes && Object.keys(state.errorNodes).forEach(name => { clearError(name)(state); }); }; /** * Iterates over all groups to render each error post-vaidation * * @param state [Object, validation state] * */ export const renderErrors = state => { Object.keys(state.groups).forEach(groupName => { if(!state.groups[groupName].valid) renderError(groupName)(state); }) }; /** * Adds an error message to the DOM and saves it to local scope * * If .NET MVC error span is present, it is used with a appended textNode, * if not a new DOM node is created * * Signature () => groupName => state => {} * (Curried groupName for ease of use as eventListener and in whole form iteration) * * @param groupName [String, validation group] * @param state [Object, validation state] * */ export const renderError = groupName => state => { if(state.errorNodes[groupName]) clearError(groupName)(state); state.errorNodes[groupName] = state.groups[groupName].serverErrorNode ? createErrorTextNode(state.groups[groupName], state.groups[groupName].errorMessages[0]) : state.groups[groupName] .fields[state.groups[groupName].fields.length-1] .parentNode .appendChild( h('span', { class: DOTNET_CLASSNAMES.ERROR }, state.groups[groupName].errorMessages[0]), state.groups[groupName].fields[state.groups[groupName].fields.length-1] ); state.groups[groupName].fields.forEach(field => { field.parentNode.classList.add('is--invalid'); field.setAttribute('aria-invalid', 'true'); }); }; /** * Set focus on first invalid field after form-level validate() * * We can assume that there is a group in an invalid state, * and that the group has at least one field * * @param groups [Object, validation group slice of state] * */ export const focusFirstInvalidField = state => { const firstInvalid = Object.keys(state.groups).reduce((acc, curr) => { if(!acc && !state.groups[curr].valid) acc = state.groups[curr].fields[0]; return acc; }, false); firstInvalid && firstInvalid.focus(); }; /** * Creates a hidden field duplicate of a given field, for conferring submit button values * * @param source [Node] A submit input/button * @param form [Node] A form node * */ export const createButtonValueNode = (source, form) => { const node = document.createElement('input'); node.setAttribute('type', 'hidden'); node.setAttribute('name', source.getAttribute('name')); node.setAttribute('value', source.getAttribute('value')); return form.appendChild(node); }; /** * Removes the node added in createButtonValueNode * * @param node [Node] A hidden input * */ export const cleanupButtonValueNode = node => { node.parentNode.removeChild(node); }