From 600781bb8096e814ad0671af28be94061453c3a8 Mon Sep 17 00:00:00 2001
From: Artem Baskal <a.baskal@adguard.com>
Date: Fri, 10 Jan 2020 19:47:49 +0300
Subject: [PATCH] - client: add wrapper for input normalization on blur

---
 .../src/components/Settings/Clients/Form.js   |  4 +-
 client/src/helpers/form.js                    | 75 +++++++++++--------
 client/src/helpers/helpers.js                 | 16 ++++
 3 files changed, 61 insertions(+), 34 deletions(-)

diff --git a/client/src/components/Settings/Clients/Form.js b/client/src/components/Settings/Clients/Form.js
index 1e33b0f2..2bf983ea 100644
--- a/client/src/components/Settings/Clients/Form.js
+++ b/client/src/components/Settings/Clients/Form.js
@@ -78,7 +78,7 @@ const renderFieldsWrapper = (placeholder, buttonTitle) =>
                             placeholder={placeholder}
                             isActionAvailable={index !== 0}
                             removeField={() => fields.remove(index)}
-                            normalize={data => data && data.trim()}
+                            normalizeOnBlur={data => data.trim()}
                         />
                     </div>
                 ))}
@@ -127,7 +127,7 @@ let Form = (props) => {
                             type="text"
                             className="form-control"
                             placeholder={t('form_client_name')}
-                            normalize={data => data && data.trim()}
+                            normalizeOnBlur={data => data.trim()}
                         />
                     </div>
 
diff --git a/client/src/helpers/form.js b/client/src/helpers/form.js
index 41483fba..2a09236c 100644
--- a/client/src/helpers/form.js
+++ b/client/src/helpers/form.js
@@ -2,13 +2,16 @@ import React, { Fragment } from 'react';
 import { Trans } from 'react-i18next';
 import PropTypes from 'prop-types';
 import { R_IPV4, R_MAC, R_HOST, R_IPV6, R_CIDR, UNSAFE_PORTS } from '../helpers/constants';
+import { createOnBlurHandler } from './helpers';
 
 export const renderField = (props, elementType) => {
     const {
-        input, id, className, placeholder, type, disabled,
+        input, id, className, placeholder, type, disabled, normalizeOnBlur,
         autoComplete, meta: { touched, error },
     } = props;
 
+    const onBlur = event => createOnBlurHandler(event, input, normalizeOnBlur);
+
     const element = React.createElement(elementType, {
         ...input,
         id,
@@ -17,6 +20,7 @@ export const renderField = (props, elementType) => {
         autoComplete,
         disabled,
         type,
+        onBlur,
     });
     return (
         <Fragment>
@@ -35,6 +39,7 @@ renderField.propTypes = {
     type: PropTypes.string,
     disabled: PropTypes.bool,
     autoComplete: PropTypes.bool,
+    normalizeOnBlur: PropTypes.func,
 };
 
 export const renderTextareaField = props => renderField(props, 'textarea');
@@ -52,37 +57,43 @@ export const renderGroupField = ({
     isActionAvailable,
     removeField,
     meta: { touched, error },
-}) => (
-    <Fragment>
-        <div className="input-group">
-            <input
-                {...input}
-                id={id}
-                placeholder={placeholder}
-                type={type}
-                className={className}
-                disabled={disabled}
-                autoComplete={autoComplete}
-            />
-            {isActionAvailable &&
-            <span className="input-group-append">
-                    <button
-                        type="button"
-                        className="btn btn-secondary btn-icon"
-                        onClick={removeField}
-                    >
-                        <svg className="icon icon--close">
-                            <use xlinkHref="#cross" />
-                        </svg>
-                    </button>
-                </span>
-            }
-        </div>
-        {!disabled &&
-        touched &&
-        (error && <span className="form__message form__message--error">{error}</span>)}
-    </Fragment>
-);
+    normalizeOnBlur,
+}) => {
+    const onBlur = event => createOnBlurHandler(event, input, normalizeOnBlur);
+
+    return (
+        <Fragment>
+            <div className="input-group">
+                <input
+                    {...input}
+                    id={id}
+                    placeholder={placeholder}
+                    type={type}
+                    className={className}
+                    disabled={disabled}
+                    autoComplete={autoComplete}
+                    onBlur={onBlur}
+                />
+                {isActionAvailable &&
+                <span className="input-group-append">
+                        <button
+                            type="button"
+                            className="btn btn-secondary btn-icon"
+                            onClick={removeField}
+                        >
+                            <svg className="icon icon--close">
+                                <use xlinkHref="#cross" />
+                            </svg>
+                        </button>
+                    </span>
+                }
+            </div>
+            {!disabled &&
+            touched &&
+            (error && <span className="form__message form__message--error">{error}</span>)}
+        </Fragment>
+    );
+};
 
 export const renderRadioField = ({
     input, placeholder, disabled, meta: { touched, error },
diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js
index 8f9556af..8fc9d36a 100644
--- a/client/src/helpers/helpers.js
+++ b/client/src/helpers/helpers.js
@@ -373,3 +373,19 @@ export const getParamsForClientsSearch = (data, param) => {
             return acc;
         }, {});
 };
+
+/**
+ * Creates onBlur handler that can normalize input if normalization function is specified
+ *
+ * @param {Object} event
+ * @param {Object} event.target
+ * @param {string} event.target.value
+ * @param {Object} input
+ * @param {function} input.onBlur
+ * @param {function} [normalizeOnBlur]
+ * @returns {function}
+ */
+export const createOnBlurHandler = (event, input, normalizeOnBlur) => (
+    normalizeOnBlur
+        ? input.onBlur(normalizeOnBlur(event.target.value))
+        : input.onBlur());