UNPKG

gatsby-drupal-webform

Version:
149 lines 6.52 kB
import React, { useState, useMemo } from 'react'; import axios from 'axios'; import { getAttributeValue, formToJSON } from './utils'; import { WebformDebug, WebformInput, WebformSelect, WebformTextarea, WebformCheckbox, WebformCheckboxGroup, WebformButton, WebformText } from './components'; export const DEFAULT_SUBMIT_LABEL = 'Submit'; export class WebformError extends Error { constructor(response) { super(); this.response = response; } } /** * Render single webform element. */ export function renderWebformElement(customComponents, Button, element, error) { const customComponentAPI = { error }; // Render using custom component if provided: if (customComponents[element.type]) { const CustomComponent = customComponents[element.type]; return <CustomComponent element={element} {...customComponentAPI}/>; } // Othervise select renderer based on element type: switch (element.type) { case 'textfield': return <WebformInput element={{ ...element, type: 'text' }} {...customComponentAPI}/>; case 'textarea': return <WebformTextarea element={element} {...customComponentAPI}/>; case 'tel': case 'number': case 'email': case 'hidden': return <WebformInput element={element} {...customComponentAPI}/>; case 'checkbox': case 'radio': /** Render single checkbox or radio element. */ return <WebformCheckbox element={element} {...customComponentAPI}/>; case 'checkboxes': return <WebformCheckboxGroup element={{ ...element, type: 'checkbox' }} {...customComponentAPI}/>; case 'radios': return <WebformCheckboxGroup element={{ ...element, type: 'radio' }} {...customComponentAPI}/>; case 'select': return <WebformSelect element={element} {...customComponentAPI}/>; case 'webform_markup': case 'processed_text': return <WebformText element={element} {...customComponentAPI}/>; // Submit button case 'webform_actions': return (<div className="form-group"> <Button type="submit">{getAttributeValue('#submit__label', element) || DEFAULT_SUBMIT_LABEL}</Button> </div>); // Unknown element type -> render as json string default: return <WebformDebug element={element} error={error}/>; } } /** * Drupal webform react component. */ const Webform = ({ webform, customComponents, buttonComponent, ...props }) => { const [errors, setErrors] = useState({}); const submitHandler = async (event) => { event.preventDefault(); const target = event.currentTarget; // Clear errors from previous submit. setErrors({}); // Remove lingering css classes from previous submits. target.classList.remove('form-submitting', 'form-error', 'form-submitted'); if ((!props.onValidate || props.onValidate(event)) && target.checkValidity()) { // Let css know that this form was validated and is being submitted. target.classList.add('was-validated', 'form-submitting'); // Serialize form data. const data = formToJSON(target.elements); // Post process serialized data: // Some webform elements require specialized data formatting. for (const element of webform.elements) { if (data[element.name]) { switch (element.type) { case 'checkbox': data[element.name] = data[element.name][0]; break; } } } try { // If onSubmit returns false skip submitting to API. if (props.onSubmit && (await props.onSubmit(data)) === false) { target.classList.remove('form-submitting'); target.classList.add('form-submitted'); return; } // Submit form to API. const response = await axios.post(props.endpoint, { ...props.extraData, ...data, webform_id: webform.drupal_internal__id }); if (response.data.error) { throw new WebformError(response); } // Convey current form state. target.classList.remove('form-submitting'); target.classList.add('form-submitted'); props.onSuccess && props.onSuccess(response.data, data); } catch (err) { // API should return error structure if validation fails. // We use that to render error messages to the form. if (err.response && err.response.data.error) { setErrors(err.response.data.error); } // Convey current form state. target.classList.remove('form-submitting'); target.classList.add('form-error'); props.onError && props.onError(err, data); } } else { // Let css know this form was validated. target.classList.add('was-validated', 'form-error'); } }; /** * Build and memonize webform elements * * Webform object should rarely change. */ const elements = useMemo(() => { const Button = buttonComponent; const ret = webform.elements.map((element) => (<React.Fragment key={element.name}>{renderWebformElement(customComponents, Button, element, errors[element.name])}</React.Fragment>)); // Render default submit button if it is not defined in elements array. if (webform.elements.find((element) => element.type === 'webform_actions') === undefined) { ret.push(<Button key="webform_actions__default_button" type="submit"> {DEFAULT_SUBMIT_LABEL} </Button>); } return ret; }, [webform, customComponents, buttonComponent, errors]); return (<form onSubmit={submitHandler} id={props.id} className={props.className} style={props.style} noValidate={props.noValidate} data-webform-id={webform.drupal_internal__id}> {elements} </form>); }; Webform.defaultProps = { customComponents: {}, buttonComponent: WebformButton }; export default Webform; //# sourceMappingURL=Webform.jsx.map