0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-23 15:26:29 -05:00

Add validation to token opacity (#5802)

This commit is contained in:
Eva Marco 2025-02-10 15:04:22 +01:00 committed by GitHub
parent 8b7a102927
commit 6692f8dce2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 92 additions and 9 deletions

View file

@ -35,6 +35,11 @@
{:error/code :error.style-dictionary/invalid-token-value
:error/fn #(str "Invalid token value: " %)}
:error.style-dictionary/invalid-token-value-opacity
{:error/code :error.style-dictionary/invalid-token-value-opacity
:error/fn #(str/join "\n" [(str "Invalid token value: " % ".") "Opacity must be between 0 and 100% or 0 and 1 (e.g. 50% or 0.5)"])}
:error/unknown
{:error/code :error/unknown
:error/message "Unknown error"}})

View file

@ -29,6 +29,7 @@
[app.main.ui.workspace.tokens.token :as wtt]
[app.main.ui.workspace.tokens.token-types :as wtty]
[app.main.ui.workspace.tokens.update :as wtu]
[app.main.ui.workspace.tokens.warnings :as wtw]
[app.util.dom :as dom]
[app.util.functions :as uf]
[app.util.i18n :refer [tr]]
@ -198,18 +199,21 @@
(mf/defc token-value-or-errors
[{:keys [result-or-errors]}]
(let [{:keys [errors]} result-or-errors
(let [{:keys [errors warnings resolved-value]} result-or-errors
empty-message? (or (nil? result-or-errors)
(wte/has-error-code? :error/empty-input errors))
message (cond
empty-message? (tr "workspace.token.resolved-value" "-")
warnings (wtw/humanize-warnings warnings)
errors (->> (wte/humanize-errors errors)
(str/join "\n"))
:else (tr "workspace.token.resolved-value" result-or-errors))]
:else (tr "workspace.token.resolved-value" (or resolved-value result-or-errors)))]
[:> text* {:as "p"
:typography "body-small"
:class (stl/css-case :resolved-value true
:resolved-value-placeholder empty-message?
:resolved-value-warning (seq warnings)
:resolved-value-error (seq errors))}
message]))
@ -227,6 +231,7 @@
token-path (mf/use-memo
(mf/deps (:name token))
#(wtt/token-name->path (:name token)))
selected-set-tokens-tree (mf/use-memo
(mf/deps token-path selected-set-tokens)
(fn []
@ -294,14 +299,20 @@
color-ramp-open? (deref color-ramp-open*)
value-input-ref (mf/use-ref nil)
value-ref (mf/use-var (:value token))
token-resolve-result* (mf/use-state (get-in resolved-tokens [(wtt/token-identifier token) :resolved-value]))
token-resolve-result* (mf/use-state (get resolved-tokens (wtt/token-identifier token)))
token-resolve-result (deref token-resolve-result*)
set-resolve-value
(mf/use-fn
(fn [token-or-err]
(let [error? (:errors token-or-err)
v (if error?
warnings? (:warnings token-or-err)
v (cond
error?
token-or-err
warnings?
(:warnings {:warnings token-or-err})
:else
(:resolved-value token-or-err))]
(when color? (reset! color (if error? nil v)))
(reset! token-resolve-result* v))))
@ -333,6 +344,7 @@
(on-display-colorpicker open?))))
value-error? (seq (:errors token-resolve-result))
valid-value-field? (and
(not value-error?)
(valid-value? token-resolve-result))

View file

@ -53,6 +53,7 @@
margin-bottom: 0;
padding: $s-4 $s-6;
color: var(--input-hint-color);
white-space: pre-wrap;
}
.resolved-value-placeholder {
@ -63,6 +64,10 @@
--input-hint-color: var(--status-color-error-500);
}
.resolved-value-warning {
--input-hint-color: var(--status-color-warning-500);
}
.form-modal-title {
color: var(--color-foreground-primary);
}

View file

@ -9,6 +9,7 @@
[app.main.ui.workspace.tokens.errors :as wte]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[app.main.ui.workspace.tokens.token :as wtt]
[app.main.ui.workspace.tokens.warnings :as wtw]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[promesa.core :as p]
@ -59,6 +60,31 @@
:references references}
{:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value value)]})))
(defn parse-sd-token-opacity-value
"Parses `value` of a dimensions `sd-token` into a map like `{:value 1 :unit \"px\"}`.
If the `value` is not parseable and/or has missing references returns a map with `:errors`.
If the `value` is parseable but is out of range returns a map with `warnings`."
[value has-references?]
(let [parsed-value (wtt/parse-token-value value)
out-of-scope (< 100 (:value parsed-value))
references (seq (ctob/find-token-value-references value))]
(cond
(and parsed-value (not out-of-scope))
parsed-value
references
{:errors [(wte/error-with-value :error.style-dictionary/missing-reference references)]
:references references}
(and (not has-references?) out-of-scope)
{:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value-opacity value)]}
(and has-references? out-of-scope)
(assoc parsed-value :warnings [(wtw/warning-with-value :warning.style-dictionary/invalid-referenced-token-value value)])
:else {:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value value)]})))
(defn process-sd-tokens
"Converts a StyleDictionary dictionary with resolved tokens (aka `sd-tokens`) back to clojure.
The `get-origin-token` argument should be a function that takes an
@ -95,14 +121,22 @@
(fn [acc ^js sd-token]
(let [origin-token (get-origin-token sd-token)
value (.-value sd-token)
has-references? (str/includes? (:value origin-token) "{")
parsed-token-value (case (:type origin-token)
:color (parse-sd-token-color-value value)
:opacity (parse-sd-token-opacity-value value has-references?)
(parse-sd-token-dimensions-value value))
output-token (if (:errors parsed-token-value)
(merge origin-token parsed-token-value)
(assoc origin-token
:resolved-value (:value parsed-token-value)
:unit (:unit parsed-token-value)))]
output-token (cond (:errors parsed-token-value)
(merge origin-token parsed-token-value)
(:warnings parsed-token-value)
(assoc origin-token
:resolved-value (:value parsed-token-value)
:warnings (:warnings parsed-token-value)
:unit (:unit parsed-token-value))
:else
(assoc origin-token
:resolved-value (:value parsed-token-value)
:unit (:unit parsed-token-value)))]
(assoc acc (:name output-token) output-token)))
{} sd-tokens))

View file

@ -0,0 +1,27 @@
(ns app.main.ui.workspace.tokens.warnings
(:require
[cuerdas.core :as str]))
(def warning-codes
{:warning.style-dictionary/invalid-referenced-token-value
{:warning/code :warning.style-dictionary/invalid-referenced-token-value
:warning/fn (fn [value] (str/join "\n" [(str "Resolved value " value ".") "Opacity must be between 0 and 100% or 0 and 1 (e.g. 50% or 0.5)"]))}
:warning/unknown
{:warning/code :warning/unknown
:warning/message "Unknown warning"}})
(defn get-warning-code [warning-key]
(get warning-codes warning-key (:warning/unknown warning-codes)))
(defn warning-with-value [warning-key warning-value]
(-> (get-warning-code warning-key)
(assoc :warning/value warning-value)))
(defn humanize-warnings [warnings]
(->> warnings
(map (fn [warn]
(cond
(:warning/fn warn) ((:warning/fn warn) (:warning/value warn))
(:warning/message warn) (:warning/message warn)
:else warn)))))