♻️ Add minor refactor on tokens main form (#7690)

This commit is contained in:
Andrey Antukh
2025-11-05 10:37:38 +01:00
committed by GitHub
parent b2a9c55874
commit 132f7d6d3e
5 changed files with 203 additions and 215 deletions

View File

@@ -45,6 +45,7 @@
[common-tests.types.path-data-test] [common-tests.types.path-data-test]
[common-tests.types.shape-decode-encode-test] [common-tests.types.shape-decode-encode-test]
[common-tests.types.shape-interactions-test] [common-tests.types.shape-interactions-test]
[common-tests.types.token-test]
[common-tests.types.tokens-lib-test] [common-tests.types.tokens-lib-test]
[common-tests.uuid-test])) [common-tests.uuid-test]))
@@ -98,4 +99,5 @@
'common-tests.types.shape-decode-encode-test 'common-tests.types.shape-decode-encode-test
'common-tests.types.shape-interactions-test 'common-tests.types.shape-interactions-test
'common-tests.types.tokens-lib-test 'common-tests.types.tokens-lib-test
'common-tests.types.token-test
'common-tests.uuid-test)) 'common-tests.uuid-test))

View File

@@ -0,0 +1,27 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns common-tests.types.token-test
(:require
[app.common.schema :as sm]
[app.common.types.token :as cto]
[app.common.uuid :as uuid]
[clojure.test :as t]))
(t/deftest test-valid-token-name-schema
;; Allow regular namespace token names
(t/is (true? (sm/validate cto/token-name-ref "Foo")))
(t/is (true? (sm/validate cto/token-name-ref "foo")))
(t/is (true? (sm/validate cto/token-name-ref "FOO")))
(t/is (true? (sm/validate cto/token-name-ref "Foo.Bar.Baz")))
;; Disallow trailing tokens
(t/is (false? (sm/validate cto/token-name-ref "Foo.Bar.Baz....")))
;; Disallow multiple separator dots
(t/is (false? (sm/validate cto/token-name-ref "Foo..Bar.Baz")))
;; Disallow any special characters
(t/is (false? (sm/validate cto/token-name-ref "Hey Foo.Bar")))
(t/is (false? (sm/validate cto/token-name-ref "Hey😈Foo.Bar")))
(t/is (false? (sm/validate cto/token-name-ref "Hey%Foo.Bar"))))

View File

@@ -37,8 +37,7 @@
[app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector*]] [app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector*]]
[app.main.ui.workspace.sidebar.options.menus.typography :refer [font-selector*]] [app.main.ui.workspace.sidebar.options.menus.typography :refer [font-selector*]]
[app.main.ui.workspace.tokens.management.create.input-token-color-bullet :refer [input-token-color-bullet*]] [app.main.ui.workspace.tokens.management.create.input-token-color-bullet :refer [input-token-color-bullet*]]
[app.main.ui.workspace.tokens.management.create.input-tokens-value :refer [input-token* [app.main.ui.workspace.tokens.management.create.input-tokens-value :refer [input-token* token-value-hint*]]
token-value-hint*]]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.functions :as uf] [app.util.functions :as uf]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
@@ -46,72 +45,37 @@
[app.util.object :as obj] [app.util.object :as obj]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[cuerdas.core :as str] [cuerdas.core :as str]
[malli.core :as m]
[malli.error :as me]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
;; Helpers --------------------------------------------------------------------- ;; Helpers ---------------------------------------------------------------------
(defn finalize-name [name] (defn- clean-name [name]
(-> (str/trim name) (-> (str/trim name)
;; Remove trailing dots ;; Remove trailing dots
(str/replace #"\.+$" ""))) (str/replace #"\.+$" "")))
(defn valid-name? [name] (defn- valid-name? [name]
(seq (finalize-name (str name)))) (seq (clean-name (str name))))
(defn finalize-value [value]
(-> (str value)
(str/trim)))
(defn valid-value? [value]
(seq (finalize-value value)))
;; Schemas --------------------------------------------------------------------- ;; Schemas ---------------------------------------------------------------------
(def ^:private well-formed-token-name-regexp (defn- make-token-name-schema
"Only allow letters and digits for token names. "Generate a dynamic schema validation to check if a token path derived
Also allow one `.` for a namespace separator. from the name already exists at `tokens-tree`."
[tokens-tree]
[:and
[:string {:min 1 :max 255 :error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}]
(sm/update-properties cto/token-name-ref assoc :error/fn #(str (:value %) (tr "workspace.tokens.token-name-validation-error")))
[:fn {:error/fn #(tr "workspace.tokens.token-name-duplication-validation-error" (:value %))}
#(not (cft/token-name-path-exists? % tokens-tree))]])
Caution: This will allow a trailing dot like `token-name.`, (def ^:private schema:token-description
But we will trim that in the `finalize-name`, [:string {:max 2048 :error/fn #(tr "errors.field-max-length" 2048)}])
to not throw too many errors while the user is editing."
#"(?!\$)([a-zA-Z0-9-$_]+\.?)*")
(def ^:private well-formed-token-name-schema (def ^:private validate-token-description
(m/-simple-schema (let [explainer (sm/lazy-explainer schema:token-description)]
{:type :token/invalid-token-name (fn [description]
:pred #(re-matches well-formed-token-name-regexp %) (-> description explainer sm/simplify not-empty))))
:type-properties {:error/fn #(str (:value %) (tr "workspace.tokens.token-name-validation-error"))}}))
(defn- token-name-schema
"Generate a dynamic schema validation to check if a token path derived from the name already exists at `tokens-tree`."
[{:keys [tokens-tree]}]
(let [path-exists-schema
(m/-simple-schema
{:type :token/name-exists
:pred #(not (cft/token-name-path-exists? % tokens-tree))
:type-properties {:error/fn #(tr "workspace.tokens.token-name-duplication-validation-error" (:value %))}})]
(m/schema
[:and
[:string {:min 1 :max 255 :error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}]
well-formed-token-name-schema
path-exists-schema])))
(defn- validate-token-name
[tokens-tree name]
(let [schema (token-name-schema {:tokens-tree tokens-tree})
validation (m/explain schema (finalize-name name))]
(me/humanize validation)))
(def ^:private token-description-schema
(m/schema
[:string {:max 2048 :error/fn #(tr "errors.field-max-length" 2048)}]))
(defn- validate-token-description
[description]
(let [validation (m/explain token-description-schema description)]
(me/humanize validation)))
;; Value Validation ------------------------------------------------------------- ;; Value Validation -------------------------------------------------------------
@@ -153,15 +117,15 @@
:else (rx/throw {:errors (or (seq errors) :else (rx/throw {:errors (or (seq errors)
[(wte/get-error-code :error/unknown-error)])})))))))) [(wte/get-error-code :error/unknown-error)])}))))))))
(defn validate-token-with [token validators] (defn- validate-token-with [token validators]
(if-let [error (some (fn [validate] (validate token)) validators)] (if-let [error (some (fn [validate] (validate token)) validators)]
(rx/throw {:errors [error]}) (rx/throw {:errors [error]})
(rx/of token))) (rx/of token)))
(def default-validators (def ^:private default-validators
[check-token-empty-value check-self-reference]) [check-token-empty-value check-self-reference])
(defn default-validate-token (defn- default-validate-token
"Validates a token by confirming a list of `validator` predicates and resolving the token using `tokens` with StyleDictionary. "Validates a token by confirming a list of `validator` predicates and resolving the token using `tokens` with StyleDictionary.
Returns rx stream of either a valid resolved token or an errors map. Returns rx stream of either a valid resolved token or an errors map.
@@ -189,16 +153,16 @@
;; Resolving token via StyleDictionary ;; Resolving token via StyleDictionary
(rx/mapcat #(validate-resolve-token % prev-token tokens))))) (rx/mapcat #(validate-resolve-token % prev-token tokens)))))
(defn check-coll-self-reference (defn- check-coll-self-reference
"Invalidate a collection of `token-vals` for a self-refernce against `token-name`.," "Invalidate a collection of `token-vals` for a self-refernce against `token-name`.,"
[token-name token-vals] [token-name token-vals]
(when (some #(cto/token-value-self-reference? token-name %) token-vals) (when (some #(cto/token-value-self-reference? token-name %) token-vals)
(wte/get-error-code :error.token/direct-self-reference))) (wte/get-error-code :error.token/direct-self-reference)))
(defn check-font-family-token-self-reference [token] (defn- check-font-family-token-self-reference [token]
(check-coll-self-reference (:name token) (:value token))) (check-coll-self-reference (:name token) (:value token)))
(defn validate-font-family-token (defn- validate-font-family-token
[props] [props]
(-> props (-> props
(update :token-value cto/split-font-family) (update :token-value cto/split-font-family)
@@ -208,7 +172,7 @@
check-font-family-token-self-reference]) check-font-family-token-self-reference])
(default-validate-token))) (default-validate-token)))
(defn check-typography-token-self-reference (defn- check-typography-token-self-reference
"Check token when any of the attributes in token value have a self-reference." "Check token when any of the attributes in token value have a self-reference."
[token] [token]
(let [token-name (:name token) (let [token-name (:name token)
@@ -220,11 +184,11 @@
(assoc err :typography-key k))) (assoc err :typography-key k)))
token-values))) token-values)))
(defn check-empty-typography-token [token] (defn- check-empty-typography-token [token]
(when (empty? (:value token)) (when (empty? (:value token))
(wte/get-error-code :error.token/empty-input))) (wte/get-error-code :error.token/empty-input)))
(defn check-shadow-token-self-reference (defn- check-shadow-token-self-reference
"Check token when any of the attributes in a shadow's value have a self-reference." "Check token when any of the attributes in a shadow's value have a self-reference."
[token] [token]
(let [token-name (:name token) (let [token-name (:name token)
@@ -236,13 +200,13 @@
shadow-map)) shadow-map))
(d/enumerate shadow-values)))) (d/enumerate shadow-values))))
(defn check-empty-shadow-token [token] (defn- check-empty-shadow-token [token]
(when (or (empty? (:value token)) (when (or (empty? (:value token))
(some (fn [shadow] (not-every? #(contains? shadow %) [:offsetX :offsetY :blur :spread :color])) (some (fn [shadow] (not-every? #(contains? shadow %) [:offsetX :offsetY :blur :spread :color]))
(:value token))) (:value token)))
(wte/get-error-code :error.token/empty-input))) (wte/get-error-code :error.token/empty-input)))
(defn validate-typography-token (defn- validate-typography-token
[{:keys [token-value] :as props}] [{:keys [token-value] :as props}]
(cond (cond
;; Entering form without a value - show no error just resolve nil ;; Entering form without a value - show no error just resolve nil
@@ -260,7 +224,7 @@
check-typography-token-self-reference]) check-typography-token-self-reference])
(default-validate-token)))) (default-validate-token))))
(defn validate-shadow-token (defn- validate-shadow-token
[{:keys [token-value] :as props}] [{:keys [token-value] :as props}]
(cond (cond
;; Entering form without a value - show no error just resolve nil ;; Entering form without a value - show no error just resolve nil
@@ -281,32 +245,30 @@
check-shadow-token-self-reference]) check-shadow-token-self-reference])
(default-validate-token)))) (default-validate-token))))
(defn use-debonced-resolve-callback (defn- use-debonced-resolve-callback
"Resolves a token values using `StyleDictionary`. "Resolves a token values using `StyleDictionary`.
This function is debounced as the resolving might be an expensive calculation. This function is debounced as the resolving might be an expensive calculation.
Uses a custom debouncing logic, as the resolve function is async." Uses a custom debouncing logic, as the resolve function is async."
[{:keys [timeout name-ref token tokens callback validate-token] [{:keys [timeout name-ref token tokens callback validate-token]
:or {timeout 160}}] :or {timeout 160}}]
(let [timeout-id-ref (mf/use-ref nil) (let [timeout-id-ref (mf/use-ref nil)]
debounced-resolver-callback (mf/use-fn
(mf/use-fn (mf/deps token callback tokens)
(mf/deps token callback tokens) (fn [value]
(fn [value] (let [timeout-id (js/Symbol)
(let [timeout-id (js/Symbol) ;; Dont execute callback when the timout-id-ref is outdated because this function got called again
;; Dont execute callback when the timout-id-ref is outdated because this function got called again timeout-outdated-cb? #(not= (mf/ref-val timeout-id-ref) timeout-id)]
timeout-outdated-cb? #(not= (mf/ref-val timeout-id-ref) timeout-id)] (mf/set-ref-val! timeout-id-ref timeout-id)
(mf/set-ref-val! timeout-id-ref timeout-id) (js/setTimeout
(js/setTimeout (fn []
(fn [] (when (not (timeout-outdated-cb?))
(when (not (timeout-outdated-cb?)) (->> (validate-token {:token-value value
(->> (validate-token {:token-value value :token-name (mf/ref-val name-ref)
:token-name @name-ref :prev-token token
:prev-token token :tokens tokens})
:tokens tokens}) (rx/filter #(not (timeout-outdated-cb?)))
(rx/filter #(not (timeout-outdated-cb?))) (rx/subs! callback callback))))
(rx/subs! callback callback)))) timeout))))))
timeout))))]
debounced-resolver-callback))
(defonce form-token-cache-atom (atom nil)) (defonce form-token-cache-atom (atom nil))
@@ -342,64 +304,87 @@
custom-input-token-value custom-input-token-value
custom-input-token-value-props] custom-input-token-value-props]
:or {validate-token default-validate-token}}] :or {validate-token default-validate-token}}]
(let [token (or token {:type token-type})
token-properties (dwta/get-token-properties token)
tokens-in-selected-set (mf/deref refs/workspace-all-tokens-in-selected-set)
active-theme-tokens (cond-> (mf/deref refs/workspace-active-theme-sets-tokens) (let [token
;; Ensure that the resolved value uses the currently editing token (mf/with-memo [token]
;; even if the name has been overriden by a token with the same name (or token {:type token-type}))
;; in another set below.
(and (:name token) (:value token))
(assoc (:name token) token)
;; Style dictionary resolver needs font families to be an array of strings token-name (get token :name)
(= :font-family (or (:type token) token-type)) token-description (get token :description)
(update-in [(:name token) :value] cto/split-font-family) token-name-ref (mf/use-ref token-name)
(= :typography (or (:type token) token-type)) name-ref (mf/use-ref nil)
(d/update-in-when [(:name token) :font-family :value] cto/split-font-family))
resolved-tokens (sd/use-resolved-tokens active-theme-tokens {:cache-atom form-token-cache-atom name-errors* (mf/use-state nil)
:interactive? true}) name-errors (deref name-errors*)
token-path (mf/use-memo
(mf/deps (:name token))
#(cft/token-name->path (:name token)))
tokens-tree-in-selected-set (mf/use-memo touched-name* (mf/use-state false)
(mf/deps token-path tokens-in-selected-set) touched-name? (deref touched-name*)
(fn []
(-> (ctob/tokens-tree tokens-in-selected-set) warning-name-change*
;; Allow setting editing token to it's own path (mf/use-state false)
(d/dissoc-in token-path))))
;; Name warning-name-change?
touched-name* (mf/use-state false) (deref warning-name-change*)
touched-name? (deref touched-name*)
warning-name-change* (mf/use-state false) token-properties
warning-name-change? (deref warning-name-change*) (dwta/get-token-properties token)
token-name-ref (mf/use-var (:name token))
name-ref (mf/use-ref nil) tokens-in-selected-set
name-errors* (mf/use-state nil) (mf/deref refs/workspace-all-tokens-in-selected-set)
name-errors (deref name-errors*)
active-theme-tokens
(cond-> (mf/deref refs/workspace-active-theme-sets-tokens)
;; Ensure that the resolved value uses the currently editing token
;; even if the name has been overriden by a token with the same name
;; in another set below.
(and (:name token) (:value token))
(assoc (:name token) token)
;; Style dictionary resolver needs font families to be an array of strings
(= :font-family (or (:type token) token-type))
(update-in [(:name token) :value] cto/split-font-family)
(= :typography (or (:type token) token-type))
(d/update-in-when [(:name token) :font-family :value] cto/split-font-family))
resolved-tokens
(sd/use-resolved-tokens active-theme-tokens
{:cache-atom form-token-cache-atom
:interactive? true})
token-path
(mf/with-memo [token-name]
(cft/token-name->path token-name))
tokens-tree-in-selected-set
(mf/with-memo [token-path tokens-in-selected-set]
(-> (ctob/tokens-tree tokens-in-selected-set)
;; Allow setting editing token to it's own path
(d/dissoc-in token-path)))
validate-token-name
(mf/with-memo [tokens-tree-in-selected-set]
(let [schema (make-token-name-schema tokens-tree-in-selected-set)
explainer (sm/explainer schema)]
(fn [name]
(-> name explainer sm/simplify not-empty))))
on-blur-name on-blur-name
(mf/use-fn (mf/use-fn
(mf/deps touched-name? warning-name-change? tokens-tree-in-selected-set) (mf/deps touched-name? validate-token-name)
(fn [e] (fn [e]
(let [value (dom/get-target-val e) (let [value (dom/get-target-val e)
errors (validate-token-name tokens-tree-in-selected-set value)] errors (validate-token-name value)]
(when touched-name? (when touched-name? (reset! warning-name-change* true))
(reset! warning-name-change* true))
(reset! name-errors* errors)))) (reset! name-errors* errors))))
on-update-name-debounced on-update-name-debounced
(mf/use-fn (mf/with-memo [touched-name? validate-token-name]
(mf/deps touched-name? tokens-tree-in-selected-set) (uf/debounce (fn [token-name]
(uf/debounce (fn [token-name] (when touched-name?
(let [errors (validate-token-name tokens-tree-in-selected-set token-name)] (reset! name-errors* (validate-token-name token-name))))
(when touched-name? 300))
(reset! name-errors* errors))))
300))
on-update-name on-update-name
(mf/use-fn (mf/use-fn
@@ -408,19 +393,24 @@
(let [ref (mf/ref-val name-ref) (let [ref (mf/ref-val name-ref)
token-name (dom/get-value ref)] token-name (dom/get-value ref)]
(reset! touched-name* true) (reset! touched-name* true)
(reset! token-name-ref token-name)
(mf/set-ref-val! token-name-ref token-name)
(on-update-name-debounced token-name)))) (on-update-name-debounced token-name))))
valid-name-field? (and valid-name-field?
(not name-errors) (and
(valid-name? @token-name-ref)) (not name-errors)
(valid-name? (mf/ref-val token-name-ref)))
;; Value ;; Value
value-input-ref (mf/use-ref nil) value-input-ref (mf/use-ref nil)
value-ref (mf/use-ref (:value token)) value-ref (mf/use-ref (:value token))
token-resolve-result* (mf/use-state (get resolved-tokens (cft/token-identifier token))) token-resolve-result*
token-resolve-result (deref token-resolve-result*) (mf/use-state #(get resolved-tokens (cft/token-identifier token)))
token-resolve-result
(deref token-resolve-result*)
clear-resolve-value clear-resolve-value
(mf/use-fn (mf/use-fn
@@ -464,73 +454,73 @@
(mf/set-ref-val! value-ref next-value) (mf/set-ref-val! value-ref next-value)
(on-update-value-debounced next-value))) (on-update-value-debounced next-value)))
value-error? (seq (:errors token-resolve-result)) value-error? (seq (:errors token-resolve-result))
valid-value-field? (and token-resolve-result (not value-error?)) valid-value-field? (and token-resolve-result (not value-error?))
;; Description ;; Description
description-ref (mf/use-var (:description token)) description-ref (mf/use-ref token-description)
description-errors* (mf/use-state nil) description-errors* (mf/use-state nil)
description-errors (deref description-errors*) description-errors (deref description-errors*)
on-update-description-debounced on-update-description-debounced
(mf/use-fn (mf/with-memo []
(uf/debounce (fn [e] (uf/debounce (fn [e]
(let [value (dom/get-target-val e) (let [value (dom/get-target-val e)
errors (validate-token-description value)] errors (validate-token-description value)]
(reset! description-errors* errors))))) (reset! description-errors* errors)))))
on-update-description on-update-description
(mf/use-fn (mf/use-fn
(mf/deps on-update-description-debounced) (mf/deps on-update-description-debounced)
(fn [e] (fn [e]
(reset! description-ref (dom/get-target-val e)) (mf/set-ref-val! description-ref (dom/get-target-val e))
(on-update-description-debounced e))) (on-update-description-debounced e)))
valid-description-field? (empty? description-errors)
valid-description-field?
(empty? description-errors)
;; Form ;; Form
disabled? (or (not valid-name-field?) disabled?
(not valid-value-field?) (or (not valid-name-field?)
(not valid-description-field?)) (not valid-value-field?)
(not valid-description-field?))
on-submit on-submit
(mf/use-fn (mf/use-fn
(mf/deps is-create tokens-tree-in-selected-set token active-theme-tokens validate-token) (mf/deps is-create token active-theme-tokens validate-token validate-token-name validate-token-description)
(fn [e] (fn [e]
(dom/prevent-default e) (dom/prevent-default e)
;; We have to re-validate the current form values before submitting ;; We have to re-validate the current form values before submitting
;; because the validation is asynchronous/debounced ;; because the validation is asynchronous/debounced
;; and the user might have edited a valid form to make it invalid, ;; and the user might have edited a valid form to make it invalid,
;; and press enter before the next validations could return. ;; and press enter before the next validations could return.
(let [final-name (finalize-name @token-name-ref)
valid-name? (try (let [clean-name (clean-name (mf/ref-val token-name-ref))
(empty? (:errors (validate-token-name tokens-tree-in-selected-set final-name))) valid-name? (empty? (validate-token-name clean-name))
(catch js/Error _ nil))
value (mf/ref-val value-ref) value (mf/ref-val value-ref)
final-description @description-ref clean-description (mf/ref-val description-ref)
valid-description? (if final-description valid-description? (or (some-> clean-description validate-token-description empty?) true)]
(try
(empty? (:errors (validate-token-description final-description)))
(catch js/Error _ nil))
true)]
(when (and valid-name? valid-description?) (when (and valid-name? valid-description?)
(->> (validate-token {:token-value value (->> (validate-token {:token-value value
:token-name final-name :token-name clean-name
:token-description final-description :token-description clean-description
:prev-token token :prev-token token
:tokens active-theme-tokens}) :tokens active-theme-tokens})
(rx/subs! (rx/subs!
(fn [valid-token] (fn [valid-token]
(st/emit! (st/emit!
(if is-create (if is-create
(dwtl/create-token {:name final-name (dwtl/create-token {:name clean-name
:type token-type :type token-type
:value (:value valid-token) :value (:value valid-token)
:description final-description}) :description clean-description})
(dwtl/update-token (:id token) (dwtl/update-token (:id token)
{:name final-name {:name clean-name
:value (:value valid-token) :value (:value valid-token)
:description final-description})) :description clean-description}))
(dwtp/propagate-workspace-tokens) (dwtp/propagate-workspace-tokens)
(modal/hide))))))))) (modal/hide)))))))))
@@ -570,21 +560,18 @@
(on-submit e))))] (on-submit e))))]
;; Clear form token cache on unmount ;; Clear form token cache on unmount
(mf/use-effect (mf/with-effect []
(fn [] #(reset! form-token-cache-atom nil))
#(reset! form-token-cache-atom nil)))
;; Update the value when editing an existing token ;; Update the value when editing an existing token
;; so the user doesn't have to interact with the form to validate the token ;; so the user doesn't have to interact with the form to validate the token
(mf/use-effect (mf/with-effect [is-create token resolved-tokens token-resolve-result set-resolve-value]
(mf/deps is-create token resolved-tokens token-resolve-result set-resolve-value) (when (and (not is-create)
(fn [] (:value token) ;; Don't retrigger this effect when switching tabs on composite tokens
(when (and (not is-create) (not token-resolve-result)
(:value token) ;; Don't retrigger this effect when switching tabs on composite tokens resolved-tokens)
(not token-resolve-result) (-> (get resolved-tokens (mf/ref-val token-name-ref))
resolved-tokens) (set-resolve-value))))
(-> (get resolved-tokens @token-name-ref)
(set-resolve-value)))))
[:form {:class (stl/css :form-wrapper) [:form {:class (stl/css :form-wrapper)
:on-submit on-submit} :on-submit on-submit}
@@ -602,7 +589,7 @@
:max-length max-input-length :max-length max-input-length
:variant "comfortable" :variant "comfortable"
:auto-focus true :auto-focus true
:default-value @token-name-ref :default-value (mf/ref-val token-name-ref)
:hint-type (when-not (empty? name-errors) "error") :hint-type (when-not (empty? name-errors) "error")
:hint-message (first name-errors) :hint-message (first name-errors)
:ref name-ref :ref name-ref
@@ -644,7 +631,7 @@
:is-optional true :is-optional true
:max-length max-input-length :max-length max-input-length
:variant "comfortable" :variant "comfortable"
:default-value @description-ref :default-value (mf/ref-val description-ref)
:hint-type (when-not (empty? description-errors) "error") :hint-type (when-not (empty? description-errors) "error")
:hint-message (first description-errors) :hint-message (first description-errors)
:on-blur on-update-description :on-blur on-update-description

View File

@@ -15,7 +15,6 @@
[frontend-tests.tokens.logic.token-actions-test] [frontend-tests.tokens.logic.token-actions-test]
[frontend-tests.tokens.logic.token-data-test] [frontend-tests.tokens.logic.token-data-test]
[frontend-tests.tokens.style-dictionary-test] [frontend-tests.tokens.style-dictionary-test]
[frontend-tests.tokens.token-form-test]
[frontend-tests.util-range-tree-test] [frontend-tests.util-range-tree-test]
[frontend-tests.util-simple-math-test] [frontend-tests.util-simple-math-test]
[frontend-tests.worker-snap-test])) [frontend-tests.worker-snap-test]))
@@ -44,7 +43,6 @@
'frontend-tests.tokens.logic.token-actions-test 'frontend-tests.tokens.logic.token-actions-test
'frontend-tests.tokens.logic.token-data-test 'frontend-tests.tokens.logic.token-data-test
'frontend-tests.tokens.style-dictionary-test 'frontend-tests.tokens.style-dictionary-test
'frontend-tests.tokens.token-form-test
'frontend-tests.util-range-tree-test 'frontend-tests.util-range-tree-test
'frontend-tests.util-simple-math-test 'frontend-tests.util-simple-math-test
'frontend-tests.worker-snap-test)) 'frontend-tests.worker-snap-test))

View File

@@ -1,26 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns frontend-tests.tokens.token-form-test
(:require
[app.main.ui.workspace.tokens.management.create.form :as wtf]
[cljs.test :as t :include-macros true]
[malli.core :as m]))
(t/deftest test-valid-token-name-schema
;; Allow regular namespace token names
(t/is (some? (m/validate wtf/well-formed-token-name-schema "Foo")))
(t/is (some? (m/validate wtf/well-formed-token-name-schema "foo")))
(t/is (some? (m/validate wtf/well-formed-token-name-schema "FOO")))
(t/is (some? (m/validate wtf/well-formed-token-name-schema "Foo.Bar.Baz")))
;; Allow trailing tokens
(t/is (nil? (m/validate wtf/well-formed-token-name-schema "Foo.Bar.Baz....")))
;; Disallow multiple separator dots
(t/is (nil? (m/validate wtf/well-formed-token-name-schema "Foo..Bar.Baz")))
;; Disallow any special characters
(t/is (nil? (m/validate wtf/well-formed-token-name-schema "Hey Foo.Bar")))
(t/is (nil? (m/validate wtf/well-formed-token-name-schema "Hey😈Foo.Bar")))
(t/is (nil? (m/validate wtf/well-formed-token-name-schema "Hey%Foo.Bar"))))