mirror of
https://github.com/penpot/penpot.git
synced 2026-11-05 09:30:38 +00:00
♻️ Extract the token pill state and tooltip from its component (#12691)
The token pill worked out inside the component whether its token is applied to the selection, whether it can apply to it at all and why the token is invalid, so no test could reach that logic. Move it into `pill-state`, and split the tooltip into `tooltip-info`, which decides what it says, and `render-tooltip`, which renders it. Both are pure and get unit tests for the applied, partial, disabled and error states and each tooltip case. The two chained `on-click` handlers become one. Nothing else changes in behavior. Signed-off-by: Florian Schroedl <flo.schroedl@gmail.com>
This commit is contained in:
parent
a089f7a5a1
commit
e6153b61a5
@ -109,61 +109,59 @@
|
||||
(str/join ", " (map attribute-dictionary values)) ".")))
|
||||
grouped-values)))
|
||||
|
||||
(defn- generate-tooltip
|
||||
"Generates a tooltip for a given token"
|
||||
[is-viewer shape theme-token token half-applied no-valid-value ref-not-in-active-set error-message]
|
||||
(let [{:keys [name type resolved-value value]} token
|
||||
resolved-value-theme (:resolved-value theme-token)
|
||||
resolved-value (or resolved-value-theme resolved-value)
|
||||
{:keys [title] :as token-props} (dwta/get-token-properties theme-token)
|
||||
(defn- tooltip-info
|
||||
"For an invalid token the tooltip is only its error message. Otherwise
|
||||
it shows the token's name and values and, for viewers, the attributes
|
||||
of `shape` the token is applied to."
|
||||
[token resolved-token shape state is-viewer]
|
||||
(let [{:keys [name type value]} token
|
||||
{:keys [is-half-applied has-missing-reference has-invalid-value
|
||||
error-message]} state
|
||||
resolved-value (or (:resolved-value resolved-token) (:resolved-value token))
|
||||
{:keys [title] :as token-props} (dwta/get-token-properties resolved-token)
|
||||
applied-tokens (:applied-tokens shape)
|
||||
app-token-vals (set (vals applied-tokens))
|
||||
app-token-keys (keys applied-tokens)
|
||||
is-applied? (contains? app-token-vals name)
|
||||
|
||||
|
||||
applied-to (if half-applied
|
||||
(partially-applied-attr app-token-keys is-applied? token-props)
|
||||
(tr "labels.all"))
|
||||
grouped-values (group-by dimensions-dictionary app-token-keys)
|
||||
|
||||
name-line
|
||||
(mf/html [:* (dm/str (tr "workspace.tokens.token-name") ": ")
|
||||
[:span {:class (stl/css :token-pill-tooltip-name)} name]])
|
||||
|
||||
rest-of-title (dm/str (tr "workspace.tokens.original-value" (dwtf/format-token-value value)) "\n"
|
||||
(tr "workspace.tokens.resolved-value" (dwtf/format-token-value resolved-value))
|
||||
(when (= (:type token) :number)
|
||||
(dm/str "\n" (tr "workspace.tokens.more-options"))))]
|
||||
is-applied (contains? (set (vals applied-tokens)) name)]
|
||||
|
||||
(cond
|
||||
;; If there are errors, show the appropriate message
|
||||
ref-not-in-active-set
|
||||
(tr "workspace.tokens.ref-not-valid" name)
|
||||
has-missing-reference
|
||||
{:message (tr "workspace.tokens.ref-not-valid" name)}
|
||||
|
||||
error-message
|
||||
error-message
|
||||
{:message error-message}
|
||||
|
||||
no-valid-value
|
||||
(tr "workspace.tokens.value-not-valid")
|
||||
has-invalid-value
|
||||
{:message (tr "workspace.tokens.value-not-valid")}
|
||||
|
||||
;; If the token is applied and the user is a is-viewer, show the details
|
||||
(and is-applied? is-viewer)
|
||||
(mf/html
|
||||
[:*
|
||||
name-line
|
||||
[:div rest-of-title]
|
||||
[:div (tr "workspace.tokens.applied-to")]
|
||||
[:div (if (= :dimensions type)
|
||||
(translate-and-format grouped-values)
|
||||
(str "- " title ": " applied-to))]])
|
||||
|
||||
;; Otherwise only show the base title
|
||||
:else
|
||||
{:name name
|
||||
:value (dwtf/format-token-value value)
|
||||
:resolved-value (dwtf/format-token-value resolved-value)
|
||||
:has-more-options (= type :number)
|
||||
:applied-to
|
||||
(when (and is-applied is-viewer)
|
||||
(if (= :dimensions type)
|
||||
(translate-and-format (group-by dimensions-dictionary app-token-keys))
|
||||
(str "- " title ": "
|
||||
(if is-half-applied
|
||||
(partially-applied-attr app-token-keys is-applied token-props)
|
||||
(tr "labels.all")))))})))
|
||||
|
||||
(defn- render-tooltip
|
||||
[{:keys [message name value resolved-value has-more-options applied-to]}]
|
||||
(or message
|
||||
(mf/html
|
||||
[:*
|
||||
name-line
|
||||
[:div rest-of-title]]))))
|
||||
(dm/str (tr "workspace.tokens.token-name") ": ")
|
||||
[:span {:class (stl/css :token-pill-tooltip-name)} name]
|
||||
[:div (dm/str (tr "workspace.tokens.original-value" value) "\n"
|
||||
(tr "workspace.tokens.resolved-value" resolved-value)
|
||||
(when has-more-options
|
||||
(dm/str "\n" (tr "workspace.tokens.more-options"))))]
|
||||
(when applied-to
|
||||
[:*
|
||||
[:div (tr "workspace.tokens.applied-to")]
|
||||
[:div applied-to]])])))
|
||||
|
||||
;; FIXME: the token thould already have precalculated references, so
|
||||
;; we don't need to perform this regex operation on each rerender
|
||||
@ -195,12 +193,69 @@
|
||||
(def token-types-with-status-icon
|
||||
#{:color :border-radius :rotation :sizing :dimensions :opacity :spacing :stroke-width})
|
||||
|
||||
(defn- pill-state
|
||||
"Works out whether the token is applied to the selection, partially
|
||||
applied, disabled or invalid. Without a selection it counts as fully
|
||||
applied."
|
||||
[token active-theme-tokens selected-shapes is-selected-inside-layout]
|
||||
(let [{:keys [name value type]} token
|
||||
errors (:errors (get active-theme-tokens name))
|
||||
has-selected (seq selected-shapes)
|
||||
tokenscript (contains? cf/flags :tokenscript)
|
||||
|
||||
attributes
|
||||
(as-> (get dwta/token-properties type) $
|
||||
(d/nilv (:all-attributes $) (:attributes $)))
|
||||
|
||||
full-applied
|
||||
(or (not has-selected)
|
||||
(applied-all-attributes? token selected-shapes attributes))
|
||||
|
||||
applied
|
||||
(boolean (and has-selected
|
||||
(cfo/shapes-token-applied? token selected-shapes attributes)))
|
||||
|
||||
half-applied (and applied (not full-applied))
|
||||
|
||||
missing-reference
|
||||
(boolean
|
||||
(if tokenscript
|
||||
(wte/has-error-code? :error.style-dictionary/missing-reference errors)
|
||||
(and (cfo/is-reference? token)
|
||||
(not (contains-reference-value? value active-theme-tokens)))))
|
||||
|
||||
name-collision
|
||||
(= :error.token/name-collision (:error/code (first errors)))
|
||||
|
||||
invalid-value (boolean (seq errors))]
|
||||
|
||||
{:is-applied applied
|
||||
:is-half-applied half-applied
|
||||
:is-disabled
|
||||
(boolean
|
||||
(and has-selected
|
||||
(not applied)
|
||||
(not (attributes-match-selection?
|
||||
selected-shapes attributes
|
||||
{:selected-inside-layout? is-selected-inside-layout}))))
|
||||
:has-missing-reference missing-reference
|
||||
:has-invalid-value invalid-value
|
||||
;; Tokenscript errors carry their own message, as in the edit form.
|
||||
:error-message
|
||||
(when (and invalid-value (or tokenscript name-collision))
|
||||
(wte/resolve-error-message (first errors)))
|
||||
:has-errors (or missing-reference invalid-value)
|
||||
:status-icon-id
|
||||
(cond
|
||||
half-applied "token-status-partial"
|
||||
full-applied "token-status-full"
|
||||
:else "token-status-non-applied")}))
|
||||
|
||||
(mf/defc token-pill*
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [on-click token on-context-menu selected-shapes is-selected-inside-layout active-theme-tokens can-edit]}]
|
||||
(let [{:keys [name value type]} token
|
||||
resolved-token (get active-theme-tokens (:name token))
|
||||
errors (:errors resolved-token)
|
||||
(let [{:keys [name type]} token
|
||||
resolved-token (get active-theme-tokens name)
|
||||
|
||||
pill-ref (mf/use-ref nil)
|
||||
pill-id (mf/use-id)
|
||||
@ -208,59 +263,14 @@
|
||||
tooltip-content* (mf/use-state "")
|
||||
tooltip-content (deref tooltip-content*)
|
||||
|
||||
has-selected? (pos? (count selected-shapes))
|
||||
is-reference? (cfo/is-reference? token)
|
||||
contains-path? (str/includes? name ".")
|
||||
is-viewer? (not can-edit)
|
||||
|
||||
attributes (as-> (get dwta/token-properties type) $
|
||||
(d/nilv (:all-attributes $) (:attributes $)))
|
||||
|
||||
full-applied?
|
||||
(if has-selected?
|
||||
(applied-all-attributes? token selected-shapes attributes)
|
||||
true)
|
||||
|
||||
applied?
|
||||
(if has-selected?
|
||||
(cfo/shapes-token-applied? token selected-shapes attributes)
|
||||
false)
|
||||
|
||||
half-applied?
|
||||
(and applied? (not full-applied?))
|
||||
|
||||
disabled? (and
|
||||
has-selected?
|
||||
(not applied?)
|
||||
(not half-applied?)
|
||||
(not (attributes-match-selection? selected-shapes attributes {:selected-inside-layout? is-selected-inside-layout})))
|
||||
|
||||
|
||||
is-viewer? (not can-edit)
|
||||
|
||||
tokenscript? (contains? cf/flags :tokenscript)
|
||||
|
||||
ref-not-in-active-set
|
||||
(if tokenscript?
|
||||
(wte/has-error-code? :error.style-dictionary/missing-reference errors)
|
||||
(and is-reference?
|
||||
(not (contains-reference-value? value active-theme-tokens))))
|
||||
|
||||
name-collision (->> errors
|
||||
(first)
|
||||
(:error/code)
|
||||
(= :error.token/name-collision))
|
||||
|
||||
no-valid-value (seq errors)
|
||||
|
||||
;; Tokenscript errors carry their own message, as in the edit form.
|
||||
error-message
|
||||
(when (and no-valid-value (or tokenscript? name-collision))
|
||||
(wte/resolve-error-message (first errors)))
|
||||
|
||||
errors?
|
||||
(or ref-not-in-active-set
|
||||
no-valid-value
|
||||
name-collision)
|
||||
{:keys [is-applied is-disabled has-invalid-value error-message
|
||||
has-errors status-icon-id]
|
||||
:as state}
|
||||
(mf/with-memo [token active-theme-tokens selected-shapes is-selected-inside-layout]
|
||||
(pill-state token active-theme-tokens selected-shapes is-selected-inside-layout))
|
||||
|
||||
color
|
||||
(when (cfo/color-token? token)
|
||||
@ -269,23 +279,6 @@
|
||||
|
||||
status-icon? (contains? token-types-with-status-icon type)
|
||||
|
||||
on-click
|
||||
(mf/use-fn
|
||||
(mf/deps errors? on-click token)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(when (and (not (seq errors)) on-click)
|
||||
(on-click event token))))
|
||||
|
||||
token-status-id
|
||||
(cond
|
||||
half-applied?
|
||||
"token-status-partial"
|
||||
full-applied?
|
||||
"token-status-full"
|
||||
:else
|
||||
"token-status-non-applied")
|
||||
|
||||
on-context-menu
|
||||
(mf/use-fn
|
||||
(mf/deps can-edit on-context-menu token)
|
||||
@ -296,20 +289,19 @@
|
||||
|
||||
on-click
|
||||
(mf/use-fn
|
||||
(mf/deps errors? on-click can-edit)
|
||||
(mf/deps can-edit has-invalid-value on-click token)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(when (and can-edit (not (seq errors)) on-click)
|
||||
(on-click event))))
|
||||
(when (and can-edit (not has-invalid-value) on-click)
|
||||
(on-click event token))))
|
||||
|
||||
on-hover
|
||||
(mf/use-fn
|
||||
(mf/deps selected-shapes is-viewer? active-theme-tokens token half-applied? no-valid-value ref-not-in-active-set error-message)
|
||||
(mf/deps token resolved-token selected-shapes state is-viewer?)
|
||||
(fn [_]
|
||||
(let [theme-token (get active-theme-tokens name)
|
||||
title (generate-tooltip is-viewer? (first selected-shapes) theme-token token
|
||||
half-applied? no-valid-value ref-not-in-active-set error-message)]
|
||||
(reset! tooltip-content* title))))
|
||||
(->> (tooltip-info token resolved-token (first selected-shapes) state is-viewer?)
|
||||
(render-tooltip)
|
||||
(reset! tooltip-content*))))
|
||||
|
||||
;; `tooltip-content` may hold rich (hiccup) content, so it's wrapped
|
||||
;; in a function to satisfy the tooltip's `:content` schema; this
|
||||
@ -326,20 +318,20 @@
|
||||
[:button {:ref pill-ref
|
||||
:class (stl/css-case
|
||||
:token-pill true
|
||||
:token-pill-no-icon (and (not status-icon?) (not errors?))
|
||||
:token-pill-no-icon (and (not status-icon?) (not has-errors))
|
||||
:token-pill-default can-edit
|
||||
:token-pill-disabled disabled?
|
||||
:token-pill-applied (and can-edit applied?)
|
||||
:token-pill-invalid (and can-edit errors?)
|
||||
:token-pill-invalid-applied (and applied? errors? can-edit)
|
||||
:token-pill-disabled is-disabled
|
||||
:token-pill-applied (and can-edit is-applied)
|
||||
:token-pill-invalid (and can-edit has-errors)
|
||||
:token-pill-invalid-applied (and is-applied has-errors can-edit)
|
||||
|
||||
:token-pill-viewer is-viewer?
|
||||
:token-pill-applied-viewer (and is-viewer?
|
||||
applied?)
|
||||
is-applied)
|
||||
:token-pill-invalid-viewer (and is-viewer?
|
||||
errors?)
|
||||
has-errors)
|
||||
:token-pill-invalid-applied-viewer (and is-viewer?
|
||||
applied?))
|
||||
is-applied))
|
||||
:id (str "token-pill-" (:id token))
|
||||
:type "button"
|
||||
:on-focus on-hover
|
||||
@ -349,7 +341,7 @@
|
||||
|
||||
:on-context-menu on-context-menu}
|
||||
(cond
|
||||
errors?
|
||||
has-errors
|
||||
[:> icon*
|
||||
{:icon-id i/broken-link
|
||||
:class (stl/css :token-pill-icon)
|
||||
@ -362,7 +354,7 @@
|
||||
|
||||
status-icon?
|
||||
[:> token-status-icon*
|
||||
{:icon-id token-status-id
|
||||
{:icon-id status-icon-id
|
||||
:class (stl/css :token-pill-icon)}])
|
||||
|
||||
(if contains-path?
|
||||
|
||||
@ -115,6 +115,7 @@
|
||||
[frontend-tests.tokens.style-dictionary-test]
|
||||
[frontend-tests.tokens.token-errors-test]
|
||||
[frontend-tests.tokens.token-path-test]
|
||||
[frontend-tests.tokens.token-pill-test]
|
||||
[frontend-tests.tokens.tokenscript-test]
|
||||
[frontend-tests.tokens.workspace-tokens-remap-test]
|
||||
[frontend-tests.ui.check-updates-test]
|
||||
@ -287,6 +288,7 @@
|
||||
'frontend-tests.tokens.style-dictionary-test
|
||||
'frontend-tests.tokens.token-errors-test
|
||||
'frontend-tests.tokens.token-path-test
|
||||
'frontend-tests.tokens.token-pill-test
|
||||
'frontend-tests.tokens.tokenscript-test
|
||||
'frontend-tests.tokens.logic.tokens-source-test
|
||||
'frontend-tests.tokens.logic.tokens-status-test
|
||||
|
||||
220
frontend/test/frontend_tests/tokens/token_pill_test.cljs
Normal file
220
frontend/test/frontend_tests/tokens/token_pill_test.cljs
Normal file
@ -0,0 +1,220 @@
|
||||
;; 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 SUBSIDIARY SL
|
||||
|
||||
(ns frontend-tests.tokens.token-pill-test
|
||||
(:require
|
||||
[app.common.test-helpers.ids-map :as thi]
|
||||
[app.common.test-helpers.shapes :as ths]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.config :as cf]
|
||||
[app.main.ui.workspace.tokens.management.token-pill :as wtp]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[cljs.test :as t :include-macros true]))
|
||||
|
||||
(t/use-fixtures :each thi/test-fixture)
|
||||
|
||||
(def ^:private pill-state @#'wtp/pill-state)
|
||||
(def ^:private tooltip-info @#'wtp/tooltip-info)
|
||||
|
||||
(def ^:private radius
|
||||
(ctob/make-token :name "radius.md" :type :border-radius :value "8"))
|
||||
|
||||
(def ^:private spacing
|
||||
(ctob/make-token :name "spacing.md" :type :spacing :value "16"))
|
||||
|
||||
(def ^:private width
|
||||
(ctob/make-token :name "width.md" :type :dimensions :value "100"))
|
||||
|
||||
(def ^:private active-theme-tokens
|
||||
{"radius.md" (assoc radius :resolved-value 8)
|
||||
"spacing.md" (assoc spacing :resolved-value 16)
|
||||
"width.md" (assoc width :resolved-value 100)})
|
||||
|
||||
(defn- with-token
|
||||
[shape token attributes]
|
||||
(cto/apply-token-to-shape {:shape shape :token token :attributes attributes}))
|
||||
|
||||
(defn- state
|
||||
[token selected-shapes & {:keys [tokens is-selected-inside-layout]
|
||||
:or {tokens active-theme-tokens}}]
|
||||
(pill-state token tokens selected-shapes is-selected-inside-layout))
|
||||
|
||||
(defn- with-flags
|
||||
[flags f]
|
||||
(with-redefs [cf/flags flags]
|
||||
(f)))
|
||||
|
||||
(def ^:private no-tokenscript-flags (disj cf/flags :tokenscript))
|
||||
(def ^:private tokenscript-flags (conj cf/flags :tokenscript))
|
||||
|
||||
(t/deftest applied-status
|
||||
(t/testing "without a selection the token counts as fully applied"
|
||||
(t/is (= {:is-applied false
|
||||
:is-half-applied false
|
||||
:is-disabled false
|
||||
:status-icon-id "token-status-full"}
|
||||
(select-keys (state radius [])
|
||||
[:is-applied :is-half-applied :is-disabled :status-icon-id]))))
|
||||
|
||||
(t/testing "applied to every attribute of every selected shape"
|
||||
(let [result (state radius [(with-token (ths/sample-shape :rect1) radius cto/border-radius-keys)
|
||||
(with-token (ths/sample-shape :rect2) radius cto/border-radius-keys)])]
|
||||
(t/is (true? (:is-applied result)))
|
||||
(t/is (false? (:is-half-applied result)))
|
||||
(t/is (= "token-status-full" (:status-icon-id result)))))
|
||||
|
||||
(t/testing "applied to only some of the attributes"
|
||||
(let [result (state radius [(with-token (ths/sample-shape :rect1) radius #{:r1})])]
|
||||
(t/is (true? (:is-applied result)))
|
||||
(t/is (true? (:is-half-applied result)))
|
||||
(t/is (= "token-status-partial" (:status-icon-id result)))))
|
||||
|
||||
(t/testing "applied to only some of the selected shapes"
|
||||
(let [result (state radius [(with-token (ths/sample-shape :rect1) radius cto/border-radius-keys)
|
||||
(ths/sample-shape :rect2)])]
|
||||
(t/is (true? (:is-half-applied result)))
|
||||
(t/is (= "token-status-partial" (:status-icon-id result)))))
|
||||
|
||||
(t/testing "another token on the same attributes does not count"
|
||||
(let [other (ctob/make-token :name "radius.lg" :type :border-radius :value "16")
|
||||
result (state radius [(with-token (ths/sample-shape :rect1) other #{:r1})])]
|
||||
(t/is (false? (:is-applied result)))
|
||||
(t/is (= "token-status-non-applied" (:status-icon-id result)))))
|
||||
|
||||
(t/testing "color tokens count all their attributes, not only the fill"
|
||||
(let [color (ctob/make-token :name "brand" :type :color :value "#ff0000")]
|
||||
(t/is (true? (:is-half-applied
|
||||
(state color [(with-token (ths/sample-shape :rect1) color #{:fill})]
|
||||
:tokens {"brand" color}))))
|
||||
(t/is (false? (:is-half-applied
|
||||
(state color [(with-token (ths/sample-shape :rect1) color #{:fill :stroke-color})]
|
||||
:tokens {"brand" color})))))))
|
||||
|
||||
(t/deftest disabled-status
|
||||
(t/testing "a token that applies to none of the selected shapes is disabled"
|
||||
(t/is (true? (:is-disabled (state radius [(ths/sample-shape :circle1 :type :circle)]))))
|
||||
(t/is (true? (:is-disabled (state spacing [(ths/sample-shape :rect1)])))))
|
||||
|
||||
(t/testing "one shape the token applies to is enough"
|
||||
(t/is (false? (:is-disabled (state radius [(ths/sample-shape :circle1 :type :circle)
|
||||
(ths/sample-shape :rect1)])))))
|
||||
|
||||
(t/testing "spacing applies to boards with a layout"
|
||||
(t/is (false? (:is-disabled (state spacing [(ths/sample-shape :board1 :type :frame :layout :flex)])))))
|
||||
|
||||
(t/testing "spacing applies as margin to shapes inside a layout"
|
||||
(t/is (false? (:is-disabled (state spacing [(ths/sample-shape :rect1)]
|
||||
:is-selected-inside-layout true)))))
|
||||
|
||||
(t/testing "an applied token is never disabled"
|
||||
(t/is (false? (:is-disabled (state radius [(with-token (ths/sample-shape :circle1 :type :circle)
|
||||
radius cto/border-radius-keys)]))))))
|
||||
|
||||
(t/deftest error-status
|
||||
(t/testing "a valid token has no errors"
|
||||
(let [result (state radius [])]
|
||||
(t/is (false? (:has-errors result)))
|
||||
(t/is (nil? (:error-message result)))))
|
||||
|
||||
(t/testing "a reference to a token outside the active sets"
|
||||
(with-flags no-tokenscript-flags
|
||||
#(let [token (ctob/make-token :name "radius.ref" :type :border-radius :value "{radius.missing}")
|
||||
result (state token [])]
|
||||
(t/is (true? (:has-missing-reference result)))
|
||||
(t/is (false? (:has-invalid-value result)))
|
||||
(t/is (true? (:has-errors result)))
|
||||
(t/is (nil? (:error-message result)))))
|
||||
|
||||
(with-flags no-tokenscript-flags
|
||||
#(let [token (ctob/make-token :name "radius.ref" :type :border-radius :value "{radius.md}")]
|
||||
(t/is (false? (:has-errors (state token []))))))
|
||||
|
||||
(with-flags tokenscript-flags
|
||||
#(let [error {:error/code :error.style-dictionary/missing-reference
|
||||
:message "Missing reference"}
|
||||
token (ctob/make-token :name "radius.ref" :type :border-radius :value "{radius.missing}")
|
||||
result (state token [] :tokens {"radius.ref" (assoc token :errors [error])})]
|
||||
(t/is (true? (:has-missing-reference result)))
|
||||
(t/is (true? (:has-invalid-value result)))
|
||||
(t/is (= "Missing reference" (:error-message result))))))
|
||||
|
||||
(t/testing "an invalid value only shows its own message with tokenscript"
|
||||
(let [error {:error/code :error.style-dictionary/invalid-token-value
|
||||
:message "Invalid value"}
|
||||
tokens {"radius.md" (assoc radius :errors [error])}]
|
||||
(with-flags no-tokenscript-flags
|
||||
#(let [result (state radius [] :tokens tokens)]
|
||||
(t/is (true? (:has-invalid-value result)))
|
||||
(t/is (true? (:has-errors result)))
|
||||
(t/is (nil? (:error-message result)))))
|
||||
(with-flags tokenscript-flags
|
||||
#(t/is (= "Invalid value" (:error-message (state radius [] :tokens tokens)))))))
|
||||
|
||||
(t/testing "a name collision shows its message"
|
||||
(with-flags no-tokenscript-flags
|
||||
#(let [error {:error/code :error.token/name-collision
|
||||
:error/value "radius.md"
|
||||
:error/fn (fn [value] (str "Collides: " value))}
|
||||
result (state radius [] :tokens {"radius.md" (assoc radius :errors [error])})]
|
||||
(t/is (true? (:has-errors result)))
|
||||
(t/is (= "Collides: radius.md" (:error-message result)))))))
|
||||
|
||||
(defn- tooltip
|
||||
[token shapes & {:keys [tokens is-viewer] :or {tokens active-theme-tokens}}]
|
||||
(tooltip-info token (get tokens (:name token)) (first shapes)
|
||||
(pill-state token tokens shapes false)
|
||||
is-viewer))
|
||||
|
||||
(t/deftest tooltip-messages
|
||||
(t/testing "errors replace the token details"
|
||||
(with-flags no-tokenscript-flags
|
||||
#(let [token (ctob/make-token :name "radius.ref" :type :border-radius :value "{radius.missing}")]
|
||||
(t/is (= {:message (tr "workspace.tokens.ref-not-valid" "radius.ref")}
|
||||
(tooltip token [])))))
|
||||
|
||||
(with-flags no-tokenscript-flags
|
||||
#(let [error {:error/code :error.style-dictionary/invalid-token-value}]
|
||||
(t/is (= {:message (tr "workspace.tokens.value-not-valid")}
|
||||
(tooltip radius [] :tokens {"radius.md" (assoc radius :errors [error])}))))))
|
||||
|
||||
(t/testing "a valid token shows its value and resolved value"
|
||||
(t/is (= {:name "radius.md"
|
||||
:value "8"
|
||||
:resolved-value "8"
|
||||
:has-more-options false
|
||||
:applied-to nil}
|
||||
(tooltip radius []))))
|
||||
|
||||
(t/testing "the resolved value comes from the active themes"
|
||||
(let [token (ctob/make-token :name "radius.ref" :type :border-radius :value "{radius.md}")]
|
||||
(t/is (= "8" (:resolved-value
|
||||
(tooltip token [] :tokens (assoc active-theme-tokens
|
||||
"radius.ref" (assoc token :resolved-value 8)))))))))
|
||||
|
||||
(t/deftest tooltip-applied-to
|
||||
(t/testing "editors don't see where the token is applied"
|
||||
(t/is (nil? (:applied-to (tooltip radius [(with-token (ths/sample-shape :rect1) radius cto/border-radius-keys)])))))
|
||||
|
||||
(t/testing "viewers see the attributes of the first selected shape"
|
||||
(t/is (= (str "- Border Radius: " (tr "labels.all"))
|
||||
(:applied-to (tooltip radius [(with-token (ths/sample-shape :rect1) radius cto/border-radius-keys)]
|
||||
:is-viewer true))))
|
||||
|
||||
(t/is (= "- Border Radius: Top Left"
|
||||
(:applied-to (tooltip radius [(with-token (ths/sample-shape :rect1) radius #{:r1})]
|
||||
:is-viewer true)))))
|
||||
|
||||
(t/testing "dimension tokens list their attributes by category"
|
||||
(t/is (= "- Sizing: Width."
|
||||
(:applied-to (tooltip width [(with-token (ths/sample-shape :rect1) width #{:width})]
|
||||
:is-viewer true)))))
|
||||
|
||||
(t/testing "nothing is listed when the token isn't applied to the first shape"
|
||||
(t/is (nil? (:applied-to (tooltip radius [(ths/sample-shape :rect1)
|
||||
(with-token (ths/sample-shape :rect2)
|
||||
radius cto/border-radius-keys)]
|
||||
:is-viewer true))))))
|
||||
Loading…
x
Reference in New Issue
Block a user