From a7ed5228d3e63ee684a0790bcce84d7719201316 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Fri, 14 Feb 2025 12:51:14 +0100 Subject: [PATCH] :bug: Fix lost translation strings (#5846) * :bug: Fix lost translation strings * :bug: Fix form error management internal issues and inconsistencies * :paperclip: Add better validation conditons for ::sm/text schema * :bug: Add better touched detection mechanism for input and textarea --------- Co-authored-by: Andrey Antukh --- common/src/app/common/schema.cljc | 14 ++- frontend/src/app/main/ui/auth/register.cljs | 2 +- .../src/app/main/ui/components/forms.cljs | 21 ++++- .../app/main/ui/settings/change_email.cljs | 2 +- .../src/app/main/ui/settings/password.cljs | 4 +- frontend/src/app/util/forms.cljs | 88 ++++++++++++------- frontend/translations/en.po | 12 +++ frontend/translations/es.po | 12 +++ 8 files changed, 111 insertions(+), 44 deletions(-) diff --git a/common/src/app/common/schema.cljc b/common/src/app/common/schema.cljc index eaa4fffbd..e7329e621 100644 --- a/common/src/app/common/schema.cljc +++ b/common/src/app/common/schema.cljc @@ -963,7 +963,6 @@ {:title "string" :description "not whitespace string" :gen/gen (sg/word-string) - :error/code "errors.invalid-text" :error/fn (fn [{:keys [value schema]}] (let [{:keys [max min] :as props} (properties schema)] @@ -971,16 +970,23 @@ (and (string? value) (number? max) (> (count value) max)) - ["errors.field-max-length" max] + {:code ["errors.field-max-length" max]} (and (string? value) (number? min) (< (count value) min)) - ["errors.field-min-length" min] + {:code ["errors.field-min-length" min]} + + (and (string? value) + (str/empty? value)) + {:code "errors.field-missing"} (and (string? value) (str/blank? value)) - "errors.field-not-all-whitespace")))}}) + {:code "errors.field-not-all-whitespace"} + + :else + {:code "errors.invalid-text"})))}}) (register! {:type ::password diff --git a/frontend/src/app/main/ui/auth/register.cljs b/frontend/src/app/main/ui/auth/register.cljs index 7b6bc2f99..ea75d0605 100644 --- a/frontend/src/app/main/ui/auth/register.cljs +++ b/frontend/src/app/main/ui/auth/register.cljs @@ -61,7 +61,7 @@ [:validation :email-as-password] (swap! form assoc-in [:errors :password] - {:code "errors.email-as-password"}) + {:message (tr "errors.email-as-password")}) (st/emit! (ntf/error (tr "errors.generic"))))))) diff --git a/frontend/src/app/main/ui/components/forms.cljs b/frontend/src/app/main/ui/components/forms.cljs index 3ed28eca0..ce5ea9996 100644 --- a/frontend/src/app/main/ui/components/forms.cljs +++ b/frontend/src/app/main/ui/components/forms.cljs @@ -33,6 +33,8 @@ more-classes (get props :class) auto-focus? (get props :auto-focus? false) + data-testid (d/nilv data-testid input-name) + form (or form (mf/use-ctx form-ctx)) type' (mf/use-state input-type) @@ -45,7 +47,9 @@ (= @type' "email")) placeholder (when is-text? (or placeholder label)) - touched? (get-in @form [:touched input-name]) + touched? (and (contains? (:data @form) input-name) + (get-in @form [:touched input-name])) + error (get-in @form [:errors input-name]) value (get-in @form [:data input-name] "") @@ -153,6 +157,14 @@ children]) (cond + (and touched? (:message error) show-error) + (let [message (:message error)] + [:div {:id (dm/str "error-" input-name) + :class (stl/css :error) + :data-testid (dm/str data-testid "-error")} + message]) + + ;; FIXME: DEPRECATED (and touched? (:code error) show-error) (let [code (:code error)] [:div {:id (dm/str "error-" input-name) @@ -173,7 +185,9 @@ focus? (mf/use-state false) - touched? (get-in @form [:touched input-name]) + touched? (and (contains? (:data @form) input-name) + (get-in @form [:touched input-name])) + error (get-in @form [:errors input-name]) value (get-in @form [:data input-name] "") @@ -211,6 +225,9 @@ [:label {:class (stl/css :textarea-label)} label] [:> :textarea props] (cond + (and touched? (:message error)) + [:span {:class (stl/css :error)} (:message error)] + (and touched? (:code error)) [:span {:class (stl/css :error)} (tr (:code error))] diff --git a/frontend/src/app/main/ui/settings/change_email.cljs b/frontend/src/app/main/ui/settings/change_email.cljs index 5535c08c3..e11aadc95 100644 --- a/frontend/src/app/main/ui/settings/change_email.cljs +++ b/frontend/src/app/main/ui/settings/change_email.cljs @@ -59,7 +59,7 @@ [:map {:title "EmailChangeForm"} [:email-1 ::sm/email] [:email-2 ::sm/email]] - [:fn {:error/code "errors.invalid-email-confirmation" + [:fn {:error/fn #(tr "errors.invalid-email-confirmation") :error/field :email-2} (fn [data] (let [email-1 (:email-1 data) diff --git a/frontend/src/app/main/ui/settings/password.cljs b/frontend/src/app/main/ui/settings/password.cljs index 5de6d7b65..5de1e2796 100644 --- a/frontend/src/app/main/ui/settings/password.cljs +++ b/frontend/src/app/main/ui/settings/password.cljs @@ -21,10 +21,10 @@ (case (:code (ex-data error)) :old-password-not-match (swap! form assoc-in [:errors :password-old] - {:code "errors.wrong-old-password"}) + {:message (tr "errors.wrong-old-password")}) :email-as-password (swap! form assoc-in [:errors :password-1] - {:code "errors.email-as-password"}) + {:message (tr "errors.email-as-password")}) (let [msg (tr "generic.error")] (st/emit! (ntf/error msg))))) diff --git a/frontend/src/app/util/forms.cljs b/frontend/src/app/util/forms.cljs index f628b06b2..5e818a7a3 100644 --- a/frontend/src/app/util/forms.cljs +++ b/frontend/src/app/util/forms.cljs @@ -10,43 +10,78 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.schema :as sm] - [app.util.i18n :refer [tr]] + [app.util.i18n :as i18n :refer [tr]] [cuerdas.core :as str] [malli.core :as m] [rumext.v2 :as mf])) ;; --- Handlers Helpers +(defn- translate-code + [code] + (if (vector? code) + (tr (nth code 0) (i18n/c (nth code 1))) + (tr code))) + +(defn- handle-error-fn + [props problem] + (let [v-fn (:error/fn props) + result (v-fn problem)] + (if (string? result) + {:message result} + {:message (or (some-> (get result :code) + (translate-code)) + (get result :message) + (tr "errors.invalid-data"))}))) + +(defn- handle-error-message + [props] + {:message (get props :error/message)}) + +(defn- handle-error-code + [props] + (let [code (get props :error/code)] + {:message (translate-code code)})) + (defn- interpret-schema-problem - [acc {:keys [schema in value] :as problem}] - (let [props (merge (m/type-properties schema) - (m/properties schema)) - field (or (first in) (:error/field props))] + [acc {:keys [schema in value type] :as problem}] + (let [props (m/properties schema) + tprops (m/type-properties schema) + field (or (first in) + (:error/field props))] (if (contains? acc field) acc (cond - (nil? value) - (assoc acc field {:code "errors.field-missing"}) + (nil? field) + acc - (contains? props :error/code) - (assoc acc field {:code (:error/code props)}) + (or (= type :malli.core/missing-key) + (nil? value)) + (assoc acc field {:message (tr "errors.field-missing")}) + + ;; --- CHECK on schema props + (contains? props :error/fn) + (assoc acc field (handle-error-fn props problem)) (contains? props :error/message) - (assoc acc field {:code (:error/message props)}) + (assoc acc field (handle-error-message props)) - (contains? props :error/fn) - (let [v-fn (:error/fn props) - code (v-fn problem)] - (assoc acc field {:code code})) + (contains? props :error/code) + (assoc acc field (handle-error-code props)) - (contains? props :error/validators) - (let [validators (:error/validators props) - props (reduce #(%2 %1 value) props validators)] - (assoc acc field {:code (d/nilv (:error/code props) "errors.invalid-data")})) + ;; --- CHECK on type props + (contains? tprops :error/fn) + (assoc acc field (handle-error-fn tprops problem)) + + (contains? tprops :error/message) + (assoc acc field (handle-error-message tprops)) + + (contains? tprops :error/code) + (assoc acc field (handle-error-code tprops)) :else - (assoc acc field {:code "errors.invalid-data"}))))) + (assoc acc field {:message (tr "errors.invalid-data")}))))) (defn- use-rerender-fn [] @@ -177,21 +212,6 @@ ;; --- Helper Components -(mf/defc field-error - [{:keys [form field type] - :as props}] - (let [{:keys [message] :as error} (dm/get-in form [:errors field]) - touched? (dm/get-in form [:touched field]) - show? (and touched? error message - (cond - (nil? type) true - (keyword? type) (= (:type error) type) - (ifn? type) (type (:type error)) - :else false))] - (when show? - [:ul - [:li {:key (:code error)} (tr (:message error))]]))) - (defn error-class [form field] (when (and (dm/get-in form [:errors field]) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index c185ea192..3aae323d4 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -1230,6 +1230,18 @@ msgstr "Something wrong has happened." msgid "errors.invalid-color" msgstr "Invalid color" +#: src/app/util/forms.cljs +msgid "errors.invalid-data" +msgstr "Invalid data" + +#: src/app/util/forms.cljs +msgid "errors.field-missing" +msgstr "Empty field" + +#: src/app/util/forms.cljs +msgid "errors.invalid-text" +msgstr "Invalid text" + #: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/login.cljs, src/app/main/ui/auth/recovery_request.cljs #, unused msgid "errors.invalid-email" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 9ffd59215..2dd27b74a 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -1231,6 +1231,18 @@ msgstr "Ha ocurrido algún error." msgid "errors.invalid-color" msgstr "Color no válido" +#: src/app/util/forms.cljs +msgid "errors.invalid-data" +msgstr "Datos no válidos" + +#: src/app/util/forms.cljs +msgid "errors.field-missing" +msgstr "Campo vacio" + +#: src/app/util/forms.cljs +msgid "errors.invalid-text" +msgstr "Texto no válido" + #: src/app/main/ui/auth/register.cljs, src/app/main/ui/auth/login.cljs, src/app/main/ui/auth/recovery_request.cljs #, unused msgid "errors.invalid-email"