UNPKG

@financial-times/o-forms

Version:

This component provides responsive styling for form fields and inputs. It provides validation and error handling for forms, as well.

132 lines (121 loc) 3.81 kB
class State { /** * Class constructor. * * @param {RadioNodeList} [inputs] - A NodeList of radio input elements * @param {boolean | object} opts - an object of options * @param {string} opts.iconOnly [null] - when true display an icon only, hiding the status label */ constructor(inputs, opts) { const radioInputs = inputs instanceof RadioNodeList; if (radioInputs) { this.inputs = inputs; this.parent = this.inputs[0].closest('.o-forms-input'); } else { throw new Error('State can only be applied to `radio` type inputs.'); } this._verify(); this.opts = Object.assign({ iconOnly: false }, opts); this.className = { saving: 'o-forms-input--saving', saved: 'o-forms-input--saved' }; } /** * Create state element * * @access private */ _generateStateEl() { this.stateEl = document.createElement('span'); const classNames = this.opts.iconOnly ? ['o-forms-input__state', 'o-forms-input__state--icon-only'] : ['o-forms-input__state']; this.stateEl.classList.add(...classNames); this.parent.append(this.stateEl); } /** * State setter * * @param {string} state type of state to display * @param {string} label customise the label of the state, e.g. the saved state defaults to "Saving" but could be "Sent" */ set(state, label) { if (!this.stateEl) { this._generateStateEl(); } if (state === 'saving') { this._saving(label); } else if (state === 'saved') { this._saved(label); } else if (state === 'none') { this._remove(); } else { throw new Error(`${state} is not a recognised state, the options are 'saving', 'saved' or 'none'.`); } } /** * Saving state * * @param {string} label - the copy when saving * @access private */ _saving(label) { // Remove other state classes. this.parent.classList.remove(this.className.saved); // Add saving state class. this.parent.classList.add(this.className.saving); // Add custom state label if given. // Default label copy is added via the CSS `content` attribute. this.stateEl.classList.toggle('o-forms-input__state--custom', Boolean(label)); this.stateEl.textContent = label && !this.opts.iconOnly ? label : ''; // When icon-only is set there is no copy when given a custom label so // add an aria label. this.stateEl.setAttribute('aria-label', label || 'Saving'); this.stateEl.setAttribute('role', 'status'); } /** * Saved state * * @param {string} label - the copy when saved * @access private */ _saved(label) { // Remove other state classes. this.parent.classList.remove(this.className.saving); // Add saved state class. this.parent.classList.add(this.className.saved); // Add custom state label if given. // Default label copy is added via the CSS `content` attribute. this.stateEl.classList.toggle('o-forms-input__state--custom', Boolean(label)); this.stateEl.textContent = label && !this.opts.iconOnly ? label : ''; // When icon-only is set there is no copy when given a custom label so // add an aria label. this.stateEl.setAttribute('aria-label', label || 'Saved'); this.stateEl.setAttribute('role', 'status'); } /** * Remove state * * @access private */ _remove() { this.parent.classList.remove(this.className.saving); this.parent.classList.remove(this.className.saved); this.parent.removeChild(this.stateEl); this.stateEl = null; } /** * Verify input parent * * @access private */ _verify() { if (!this.parent.classList.contains('o-forms-input--radio-box')) { throw new Error('State can only be set on radio inputs with a box style (o-forms-input--radio-box).'); } else if (this.parent.classList.contains('.o-forms--input-invalid')) { throw new Error('State cannot be set on an invalid input field.'); } } } export default State;