diff --git a/frontend/playwright/ui/specs/tokens.spec.js b/frontend/playwright/ui/specs/tokens.spec.js index e717b77e3..8400e93b3 100644 --- a/frontend/playwright/ui/specs/tokens.spec.js +++ b/frontend/playwright/ui/specs/tokens.spec.js @@ -409,7 +409,7 @@ test.describe("Tokens: Tokens Tab", () => { // Clearing the input field should pick hex await valueField.fill(""); await expect( - tokensUpdateCreateModal.getByText("Resolved value: -"), + tokensUpdateCreateModal.getByText("Token value cannot be empty"), ).toBeVisible(); await valueSaturationSelector.click({ position: { x: 50, y: 50 } }); await expect(valueField).toHaveValue(/^#[A-Fa-f\d]+$/); diff --git a/frontend/src/app/main/data/workspace/tokens/errors.cljs b/frontend/src/app/main/data/workspace/tokens/errors.cljs index 025b7e791..f6ec3acd8 100644 --- a/frontend/src/app/main/data/workspace/tokens/errors.cljs +++ b/frontend/src/app/main/data/workspace/tokens/errors.cljs @@ -32,6 +32,10 @@ {:error/code :error.import/style-dictionary-reference-errors :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/code :error.token/direct-self-reference :error/fn #(tr "workspace.token.self-reference")} diff --git a/frontend/src/app/main/ui/ds/controls/input.stories.jsx b/frontend/src/app/main/ui/ds/controls/input.stories.jsx index f80a8a97b..c172c073b 100644 --- a/frontend/src/app/main/ui/ds/controls/input.stories.jsx +++ b/frontend/src/app/main/ui/ds/controls/input.stories.jsx @@ -40,7 +40,7 @@ export default { control: { type: "select" }, }, variant: { - options: ["dense", "comfortable"], + options: ["dense", "comfortable", "seamless"], control: { type: "select" }, }, disabled: { diff --git a/frontend/src/app/main/ui/ds/controls/utilities/input_field.scss b/frontend/src/app/main/ui/ds/controls/utilities/input_field.scss index c3ab208b7..64034c909 100644 --- a/frontend/src/app/main/ui/ds/controls/utilities/input_field.scss +++ b/frontend/src/app/main/ui/ds/controls/utilities/input_field.scss @@ -13,6 +13,7 @@ --input-bg-color: var(--color-background-tertiary); --input-fg-color: var(--color-foreground-primary); --input-icon-color: var(--color-foreground-secondary); + --input-text-indent: 0; --input-outline-color: none; --input-height: #{$sz-32}; --input-margin: unset; @@ -43,7 +44,8 @@ } } -.variant-dense { +.variant-dense, +.variant-seamless { @include use-typography("body-small"); } @@ -79,6 +81,7 @@ border: none; background: none; inline-size: 100%; + text-indent: var(--input-text-indent, 0); font-family: inherit; font-size: inherit; diff --git a/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens_value.cljs b/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens_value.cljs index 44e937958..105bf1e28 100644 --- a/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens_value.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens_value.cljs @@ -9,38 +9,37 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.main.constants :refer [max-input-length]] - [app.main.ui.ds.controls.input :refer [input*]] + [app.main.ui.ds.controls.utilities.input-field :refer [input-field*]] + [app.main.ui.ds.controls.utilities.label :refer [label*]] [rumext.v2 :as mf])) (def ^:private schema::input-tokens-value [:map [:label :string] [:placeholder {:optional true} :string] - [:default-value {:optional true} [:maybe :string]] + [:value {:optional true} [:maybe :string]] [:class {:optional true} :string] - [:max-length {:optional true} :int] - [:error {:optional true} :boolean] - [:value {:optional true} :string]]) + [:error {:optional true} :boolean]]) (mf/defc input-tokens-value* {::mf/props :obj ::mf/forward-ref true ::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) input-ref (mf/use-ref) props (mf/spread-props props {:id id :type "text" :class (stl/css :input) - :aria-invalid error - :max-length (d/nilv max-length max-input-length) - :value value + :placeholder placeholder + :value (d/nilv value "") + :variant "comfortable" + :hint-type (when error "error") :ref (or ref input-ref)})] [:div {:class (dm/str class " " (stl/css-case :wrapper true :input-error error))} - [:label {:for id :class (stl/css :label)} label] + [:> label* {:for id} label] [:div {:class (stl/css :input-wrapper)} (when (some? children) [:div {:class (stl/css :input-swatch)} children]) - [:> input* props]]])) + [:> input-field* props]]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens_value.scss b/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens_value.scss index ea4ad5106..c9ec7b2c6 100644 --- a/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens_value.scss +++ b/frontend/src/app/main/ui/workspace/tokens/components/controls/input_tokens_value.scss @@ -41,7 +41,7 @@ position: relative; & .input { - padding-inline-start: var(--sp-xxxl); + --input-text-indent: var(--sp-xxl); } } } diff --git a/frontend/src/app/main/ui/workspace/tokens/form.cljs b/frontend/src/app/main/ui/workspace/tokens/form.cljs index 8ca970138..2b97877b4 100644 --- a/frontend/src/app/main/ui/workspace/tokens/form.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/form.cljs @@ -28,7 +28,6 @@ [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.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.workspace.colorpicker :as colorpicker] [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)] (cond (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) (p/rejected {:errors [(wte/get-error-code :error.token/direct-self-reference)]}) @@ -217,25 +216,27 @@ :on-finish-drag on-finish-drag :on-change on-change'}]])) -(mf/defc token-value-or-errors - [{:keys [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)) +(mf/defc token-value-hint + [{:keys [result]}] + (let [{:keys [errors warnings resolved-value]} result + empty-message? (nil? result) 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" (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])) + :else (tr "workspace.token.resolved-value" (or resolved-value result))) + type (cond + empty-message? "hint" + errors "error" + warnings "warning" + :else "hint")] + [:> hint-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/wrap-props false} @@ -331,7 +332,7 @@ color-ramp-open* (mf/use-state false) color-ramp-open? (deref color-ramp-open*) 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 (deref token-resolve-result*) @@ -364,7 +365,7 @@ (dom/set-value! (mf/ref-val value-input-ref) hex) hex) value)] - (reset! value-ref value') + (mf/set-ref-val! value-ref value') (on-update-value-debounced value')))) on-update-color (mf/use-fn (mf/deps color on-update-value-debounced) @@ -389,7 +390,7 @@ color-value (-> (tinycolor/valid-color hex-value) (tinycolor/set-alpha (or alpha 1)) (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) (on-update-value-debounced color-value)))) @@ -443,7 +444,7 @@ ;; and press enter before the next validations could return. (let [final-name (finalize-name @token-name-ref) 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 valid-description?+ (some-> final-description validate-descripion schema-validation->promise)] (-> (p/all [valid-name?+ @@ -558,13 +559,12 @@ [:div {:class (stl/css :input-row)} [:> 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") - :max-length 256 - :default-value @value-ref + :value (mf/ref-val value-ref) :ref value-input-ref :on-change on-update-value + :error (not (nil? (:errors token-resolve-result))) :on-blur on-update-value} (when color? [:> input-token-color-bullet* @@ -573,10 +573,11 @@ (when color-ramp-open? [:> ramp* {:color (some-> color (tinycolor/valid-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)} [:> 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 :variant "comfortable" :default-value @description-ref diff --git a/frontend/src/app/main/ui/workspace/tokens/form.scss b/frontend/src/app/main/ui/workspace/tokens/form.scss index 53dc452f9..64a8d29a1 100644 --- a/frontend/src/app/main/ui/workspace/tokens/form.scss +++ b/frontend/src/app/main/ui/workspace/tokens/form.scss @@ -50,14 +50,7 @@ .resolved-value { --input-hint-color: var(--color-foreground-primary); - margin-bottom: 0; - padding: $s-4 $s-6; color: var(--input-hint-color); - white-space: pre-wrap; -} - -.resolved-value-placeholder { - --input-hint-color: var(--color-foreground-secondary); } .resolved-value-error { diff --git a/frontend/translations/en.po b/frontend/translations/en.po index fc5faebec..831b96e5a 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -6832,8 +6832,8 @@ msgid "workspace.token.edit-token" msgstr "Edit token" #: src/app/main/ui/workspace/tokens/form.cljs:544 -msgid "workspace.token.enter-token-description" -msgstr "Add a description (optional)" +msgid "workspace.token.token-description" +msgstr "Add a description" #: src/app/main/ui/workspace/tokens/form.cljs:498 msgid "workspace.token.enter-token-name" @@ -6869,6 +6869,10 @@ msgstr "Import Error:" msgid "workspace.token.import-tooltip" 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 msgid "workspace.token.invalid-color" msgstr "Invalid color value: %s" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 4cd9f93eb..952e11d31 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -6854,8 +6854,8 @@ msgid "workspace.token.edit-token" msgstr "Editar token" #: src/app/main/ui/workspace/tokens/form.cljs:544 -msgid "workspace.token.enter-token-description" -msgstr "Añade una Descripción (opcional)" +msgid "workspace.token.token-description" +msgstr "Añade una descripción" #: src/app/main/ui/workspace/tokens/form.cljs:498 msgid "workspace.token.enter-token-name" @@ -6887,9 +6887,13 @@ msgstr "Error al importar:" msgid "workspace.token.import-tooltip" 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 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 msgid "workspace.token.invalid-json"