mirror of
https://github.com/penpot/penpot.git
synced 2025-05-25 18:36:12 +02:00
💄 Fix errors UI on input token for value
This commit is contained in:
parent
dbb9971482
commit
c0eaa75232
10 changed files with 61 additions and 53 deletions
|
@ -409,7 +409,7 @@ test.describe("Tokens: Tokens Tab", () => {
|
||||||
// Clearing the input field should pick hex
|
// Clearing the input field should pick hex
|
||||||
await valueField.fill("");
|
await valueField.fill("");
|
||||||
await expect(
|
await expect(
|
||||||
tokensUpdateCreateModal.getByText("Resolved value: -"),
|
tokensUpdateCreateModal.getByText("Token value cannot be empty"),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await valueSaturationSelector.click({ position: { x: 50, y: 50 } });
|
await valueSaturationSelector.click({ position: { x: 50, y: 50 } });
|
||||||
await expect(valueField).toHaveValue(/^#[A-Fa-f\d]+$/);
|
await expect(valueField).toHaveValue(/^#[A-Fa-f\d]+$/);
|
||||||
|
|
|
@ -32,6 +32,10 @@
|
||||||
{:error/code :error.import/style-dictionary-reference-errors
|
{:error/code :error.import/style-dictionary-reference-errors
|
||||||
:error/fn #(tr "workspace.token.import-error")}
|
:error/fn #(tr "workspace.token.import-error")}
|
||||||
|
|
||||||
|
:error.token/empty-input
|
||||||
|
{:error/code :error.token/empty-input
|
||||||
|
:error/fn #(tr "workspace.token.empty-input")}
|
||||||
|
|
||||||
:error.token/direct-self-reference
|
:error.token/direct-self-reference
|
||||||
{:error/code :error.token/direct-self-reference
|
{:error/code :error.token/direct-self-reference
|
||||||
:error/fn #(tr "workspace.token.self-reference")}
|
:error/fn #(tr "workspace.token.self-reference")}
|
||||||
|
|
|
@ -40,7 +40,7 @@ export default {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
options: ["dense", "comfortable"],
|
options: ["dense", "comfortable", "seamless"],
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
--input-bg-color: var(--color-background-tertiary);
|
--input-bg-color: var(--color-background-tertiary);
|
||||||
--input-fg-color: var(--color-foreground-primary);
|
--input-fg-color: var(--color-foreground-primary);
|
||||||
--input-icon-color: var(--color-foreground-secondary);
|
--input-icon-color: var(--color-foreground-secondary);
|
||||||
|
--input-text-indent: 0;
|
||||||
--input-outline-color: none;
|
--input-outline-color: none;
|
||||||
--input-height: #{$sz-32};
|
--input-height: #{$sz-32};
|
||||||
--input-margin: unset;
|
--input-margin: unset;
|
||||||
|
@ -43,7 +44,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.variant-dense {
|
.variant-dense,
|
||||||
|
.variant-seamless {
|
||||||
@include use-typography("body-small");
|
@include use-typography("body-small");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +81,7 @@
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
inline-size: 100%;
|
inline-size: 100%;
|
||||||
|
text-indent: var(--input-text-indent, 0);
|
||||||
|
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
|
|
|
@ -9,38 +9,37 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.main.constants :refer [max-input-length]]
|
[app.main.ui.ds.controls.utilities.input-field :refer [input-field*]]
|
||||||
[app.main.ui.ds.controls.input :refer [input*]]
|
[app.main.ui.ds.controls.utilities.label :refer [label*]]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(def ^:private schema::input-tokens-value
|
(def ^:private schema::input-tokens-value
|
||||||
[:map
|
[:map
|
||||||
[:label :string]
|
[:label :string]
|
||||||
[:placeholder {:optional true} :string]
|
[:placeholder {:optional true} :string]
|
||||||
[:default-value {:optional true} [:maybe :string]]
|
[:value {:optional true} [:maybe :string]]
|
||||||
[:class {:optional true} :string]
|
[:class {:optional true} :string]
|
||||||
[:max-length {:optional true} :int]
|
[:error {:optional true} :boolean]])
|
||||||
[:error {:optional true} :boolean]
|
|
||||||
[:value {:optional true} :string]])
|
|
||||||
|
|
||||||
(mf/defc input-tokens-value*
|
(mf/defc input-tokens-value*
|
||||||
{::mf/props :obj
|
{::mf/props :obj
|
||||||
::mf/forward-ref true
|
::mf/forward-ref true
|
||||||
::mf/schema schema::input-tokens-value}
|
::mf/schema schema::input-tokens-value}
|
||||||
[{:keys [class label max-length error value children] :rest props} ref]
|
[{:keys [class label placeholder error value children] :rest props} ref]
|
||||||
(let [id (mf/use-id)
|
(let [id (mf/use-id)
|
||||||
input-ref (mf/use-ref)
|
input-ref (mf/use-ref)
|
||||||
props (mf/spread-props props {:id id
|
props (mf/spread-props props {:id id
|
||||||
:type "text"
|
:type "text"
|
||||||
:class (stl/css :input)
|
:class (stl/css :input)
|
||||||
:aria-invalid error
|
:placeholder placeholder
|
||||||
:max-length (d/nilv max-length max-input-length)
|
:value (d/nilv value "")
|
||||||
:value value
|
:variant "comfortable"
|
||||||
|
:hint-type (when error "error")
|
||||||
:ref (or ref input-ref)})]
|
:ref (or ref input-ref)})]
|
||||||
[:div {:class (dm/str class " " (stl/css-case :wrapper true
|
[:div {:class (dm/str class " " (stl/css-case :wrapper true
|
||||||
:input-error error))}
|
:input-error error))}
|
||||||
[:label {:for id :class (stl/css :label)} label]
|
[:> label* {:for id} label]
|
||||||
[:div {:class (stl/css :input-wrapper)}
|
[:div {:class (stl/css :input-wrapper)}
|
||||||
(when (some? children)
|
(when (some? children)
|
||||||
[:div {:class (stl/css :input-swatch)} children])
|
[:div {:class (stl/css :input-swatch)} children])
|
||||||
[:> input* props]]]))
|
[:> input-field* props]]]))
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
& .input {
|
& .input {
|
||||||
padding-inline-start: var(--sp-xxxl);
|
--input-text-indent: var(--sp-xxl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
[app.main.ui.ds.controls.utilities.hint-message :refer [hint-message*]]
|
[app.main.ui.ds.controls.utilities.hint-message :refer [hint-message*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
|
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
|
||||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
|
||||||
[app.main.ui.ds.notifications.context-notification :refer [context-notification*]]
|
[app.main.ui.ds.notifications.context-notification :refer [context-notification*]]
|
||||||
[app.main.ui.workspace.colorpicker :as colorpicker]
|
[app.main.ui.workspace.colorpicker :as colorpicker]
|
||||||
[app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector*]]
|
[app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector*]]
|
||||||
|
@ -112,7 +111,7 @@
|
||||||
token-name (if (str/empty? name-value) "__TOKEN_STUDIO_SYSTEM.TEMP" name-value)]
|
token-name (if (str/empty? name-value) "__TOKEN_STUDIO_SYSTEM.TEMP" name-value)]
|
||||||
(cond
|
(cond
|
||||||
(empty? (str/trim value))
|
(empty? (str/trim value))
|
||||||
(p/rejected {:errors [{:error/code :error/empty-input}]})
|
(p/rejected {:errors [(wte/get-error-code :error.token/empty-input)]})
|
||||||
|
|
||||||
(ctob/token-value-self-reference? token-name value)
|
(ctob/token-value-self-reference? token-name value)
|
||||||
(p/rejected {:errors [(wte/get-error-code :error.token/direct-self-reference)]})
|
(p/rejected {:errors [(wte/get-error-code :error.token/direct-self-reference)]})
|
||||||
|
@ -217,25 +216,27 @@
|
||||||
:on-finish-drag on-finish-drag
|
:on-finish-drag on-finish-drag
|
||||||
:on-change on-change'}]]))
|
:on-change on-change'}]]))
|
||||||
|
|
||||||
(mf/defc token-value-or-errors
|
(mf/defc token-value-hint
|
||||||
[{:keys [result-or-errors]}]
|
[{:keys [result]}]
|
||||||
(let [{:keys [errors warnings resolved-value]} result-or-errors
|
(let [{:keys [errors warnings resolved-value]} result
|
||||||
empty-message? (or (nil? result-or-errors)
|
empty-message? (nil? result)
|
||||||
(wte/has-error-code? :error/empty-input errors))
|
|
||||||
|
|
||||||
message (cond
|
message (cond
|
||||||
empty-message? (tr "workspace.token.resolved-value" "-")
|
empty-message? (tr "workspace.token.resolved-value" "-")
|
||||||
warnings (wtw/humanize-warnings warnings)
|
warnings (wtw/humanize-warnings warnings)
|
||||||
errors (->> (wte/humanize-errors errors)
|
errors (->> (wte/humanize-errors errors)
|
||||||
(str/join "\n"))
|
(str/join "\n"))
|
||||||
:else (tr "workspace.token.resolved-value" (or resolved-value result-or-errors)))]
|
:else (tr "workspace.token.resolved-value" (or resolved-value result)))
|
||||||
[:> text* {:as "p"
|
type (cond
|
||||||
:typography "body-small"
|
empty-message? "hint"
|
||||||
:class (stl/css-case :resolved-value true
|
errors "error"
|
||||||
:resolved-value-placeholder empty-message?
|
warnings "warning"
|
||||||
:resolved-value-warning (seq warnings)
|
:else "hint")]
|
||||||
:resolved-value-error (seq errors))}
|
[:> hint-message*
|
||||||
message]))
|
{:id "token-value-hint"
|
||||||
|
:message message
|
||||||
|
:class (stl/css-case :resolved-value (not (or empty-message? (seq warnings) (seq errors))))
|
||||||
|
:type type}]))
|
||||||
|
|
||||||
(mf/defc form
|
(mf/defc form
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
|
@ -331,7 +332,7 @@
|
||||||
color-ramp-open* (mf/use-state false)
|
color-ramp-open* (mf/use-state false)
|
||||||
color-ramp-open? (deref color-ramp-open*)
|
color-ramp-open? (deref color-ramp-open*)
|
||||||
value-input-ref (mf/use-ref nil)
|
value-input-ref (mf/use-ref nil)
|
||||||
value-ref (mf/use-var (: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* (mf/use-state (get resolved-tokens (cft/token-identifier token)))
|
||||||
token-resolve-result (deref token-resolve-result*)
|
token-resolve-result (deref token-resolve-result*)
|
||||||
|
@ -364,7 +365,7 @@
|
||||||
(dom/set-value! (mf/ref-val value-input-ref) hex)
|
(dom/set-value! (mf/ref-val value-input-ref) hex)
|
||||||
hex)
|
hex)
|
||||||
value)]
|
value)]
|
||||||
(reset! value-ref value')
|
(mf/set-ref-val! value-ref value')
|
||||||
(on-update-value-debounced value'))))
|
(on-update-value-debounced value'))))
|
||||||
on-update-color (mf/use-fn
|
on-update-color (mf/use-fn
|
||||||
(mf/deps color on-update-value-debounced)
|
(mf/deps color on-update-value-debounced)
|
||||||
|
@ -389,7 +390,7 @@
|
||||||
color-value (-> (tinycolor/valid-color hex-value)
|
color-value (-> (tinycolor/valid-color hex-value)
|
||||||
(tinycolor/set-alpha (or alpha 1))
|
(tinycolor/set-alpha (or alpha 1))
|
||||||
(tinycolor/->string format))]
|
(tinycolor/->string format))]
|
||||||
(reset! value-ref color-value)
|
(mf/set-ref-val! value-ref color-value)
|
||||||
(dom/set-value! (mf/ref-val value-input-ref) color-value)
|
(dom/set-value! (mf/ref-val value-input-ref) color-value)
|
||||||
(on-update-value-debounced color-value))))
|
(on-update-value-debounced color-value))))
|
||||||
|
|
||||||
|
@ -443,7 +444,7 @@
|
||||||
;; 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)
|
(let [final-name (finalize-name @token-name-ref)
|
||||||
valid-name?+ (-> (validate-name final-name) schema-validation->promise)
|
valid-name?+ (-> (validate-name final-name) schema-validation->promise)
|
||||||
final-value (finalize-value @value-ref)
|
final-value (finalize-value (mf/ref-val value-ref))
|
||||||
final-description @description-ref
|
final-description @description-ref
|
||||||
valid-description?+ (some-> final-description validate-descripion schema-validation->promise)]
|
valid-description?+ (some-> final-description validate-descripion schema-validation->promise)]
|
||||||
(-> (p/all [valid-name?+
|
(-> (p/all [valid-name?+
|
||||||
|
@ -558,13 +559,12 @@
|
||||||
|
|
||||||
[:div {:class (stl/css :input-row)}
|
[:div {:class (stl/css :input-row)}
|
||||||
[:> input-tokens-value*
|
[:> input-tokens-value*
|
||||||
{:id "token-value"
|
{:placeholder (tr "workspace.token.token-value-enter")
|
||||||
:placeholder (tr "workspace.token.token-value-enter")
|
|
||||||
:label (tr "workspace.token.token-value")
|
:label (tr "workspace.token.token-value")
|
||||||
:max-length 256
|
:value (mf/ref-val value-ref)
|
||||||
:default-value @value-ref
|
|
||||||
:ref value-input-ref
|
:ref value-input-ref
|
||||||
:on-change on-update-value
|
:on-change on-update-value
|
||||||
|
:error (not (nil? (:errors token-resolve-result)))
|
||||||
:on-blur on-update-value}
|
:on-blur on-update-value}
|
||||||
(when color?
|
(when color?
|
||||||
[:> input-token-color-bullet*
|
[:> input-token-color-bullet*
|
||||||
|
@ -573,10 +573,11 @@
|
||||||
(when color-ramp-open?
|
(when color-ramp-open?
|
||||||
[:> ramp* {:color (some-> color (tinycolor/valid-color))
|
[:> ramp* {:color (some-> color (tinycolor/valid-color))
|
||||||
:on-change on-update-color}])
|
:on-change on-update-color}])
|
||||||
[:& token-value-or-errors {:result-or-errors token-resolve-result}]]
|
[:& token-value-hint {:result token-resolve-result}]]
|
||||||
[:div {:class (stl/css :input-row)}
|
[:div {:class (stl/css :input-row)}
|
||||||
[:> input* {:label (tr "workspace.token.token-description")
|
[:> input* {:label (tr "workspace.token.token-description")
|
||||||
:placeholder (tr "workspace.token.enter-token-description")
|
:placeholder (tr "workspace.token.token-description")
|
||||||
|
:is-optional true
|
||||||
:max-length max-input-length
|
:max-length max-input-length
|
||||||
:variant "comfortable"
|
:variant "comfortable"
|
||||||
:default-value @description-ref
|
:default-value @description-ref
|
||||||
|
|
|
@ -50,14 +50,7 @@
|
||||||
|
|
||||||
.resolved-value {
|
.resolved-value {
|
||||||
--input-hint-color: var(--color-foreground-primary);
|
--input-hint-color: var(--color-foreground-primary);
|
||||||
margin-bottom: 0;
|
|
||||||
padding: $s-4 $s-6;
|
|
||||||
color: var(--input-hint-color);
|
color: var(--input-hint-color);
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.resolved-value-placeholder {
|
|
||||||
--input-hint-color: var(--color-foreground-secondary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.resolved-value-error {
|
.resolved-value-error {
|
||||||
|
|
|
@ -6832,8 +6832,8 @@ msgid "workspace.token.edit-token"
|
||||||
msgstr "Edit token"
|
msgstr "Edit token"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/form.cljs:544
|
#: src/app/main/ui/workspace/tokens/form.cljs:544
|
||||||
msgid "workspace.token.enter-token-description"
|
msgid "workspace.token.token-description"
|
||||||
msgstr "Add a description (optional)"
|
msgstr "Add a description"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/form.cljs:498
|
#: src/app/main/ui/workspace/tokens/form.cljs:498
|
||||||
msgid "workspace.token.enter-token-name"
|
msgid "workspace.token.enter-token-name"
|
||||||
|
@ -6869,6 +6869,10 @@ msgstr "Import Error:"
|
||||||
msgid "workspace.token.import-tooltip"
|
msgid "workspace.token.import-tooltip"
|
||||||
msgstr "Importing a JSON file will override all your current tokens, sets and themes"
|
msgstr "Importing a JSON file will override all your current tokens, sets and themes"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/errors.cljs:29
|
||||||
|
msgid "workspace.token.empty-input"
|
||||||
|
msgstr "Token value cannot be empty"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/errors.cljs:29
|
#: src/app/main/ui/workspace/tokens/errors.cljs:29
|
||||||
msgid "workspace.token.invalid-color"
|
msgid "workspace.token.invalid-color"
|
||||||
msgstr "Invalid color value: %s"
|
msgstr "Invalid color value: %s"
|
||||||
|
|
|
@ -6854,8 +6854,8 @@ msgid "workspace.token.edit-token"
|
||||||
msgstr "Editar token"
|
msgstr "Editar token"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/form.cljs:544
|
#: src/app/main/ui/workspace/tokens/form.cljs:544
|
||||||
msgid "workspace.token.enter-token-description"
|
msgid "workspace.token.token-description"
|
||||||
msgstr "Añade una Descripción (opcional)"
|
msgstr "Añade una descripción"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/form.cljs:498
|
#: src/app/main/ui/workspace/tokens/form.cljs:498
|
||||||
msgid "workspace.token.enter-token-name"
|
msgid "workspace.token.enter-token-name"
|
||||||
|
@ -6887,9 +6887,13 @@ msgstr "Error al importar:"
|
||||||
msgid "workspace.token.import-tooltip"
|
msgid "workspace.token.import-tooltip"
|
||||||
msgstr "Al importar un fichero JSON sobreescribirás todos tus tokens, sets y themes"
|
msgstr "Al importar un fichero JSON sobreescribirás todos tus tokens, sets y themes"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/errors.cljs:29
|
||||||
|
msgid "workspace.token.empty-input"
|
||||||
|
msgstr "El valor del token no puede estar vacío"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/errors.cljs:29
|
#: src/app/main/ui/workspace/tokens/errors.cljs:29
|
||||||
msgid "workspace.token.invalid-color"
|
msgid "workspace.token.invalid-color"
|
||||||
msgstr "Valor de color no válido: %s"
|
msgstr "Valor de color inválido: %s"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/errors.cljs:13
|
#: src/app/main/ui/workspace/tokens/errors.cljs:13
|
||||||
msgid "workspace.token.invalid-json"
|
msgid "workspace.token.invalid-json"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue