mirror of
https://github.com/penpot/penpot.git
synced 2026-06-17 21:02:05 +00:00
🐛 Fix replace text by ref when dropdown is opened by click (#10174)
* 🐛 Fix replace text by ref when dropdown is opened by click * 🎉 Add test
This commit is contained in:
parent
0a54533240
commit
8b20a3da15
@ -721,7 +721,7 @@
|
||||
(or (nil? last-space-left) (> (dm/number open-pos) (dm/number last-space-left)))
|
||||
(or (nil? first-space-right) (< (dm/number close-pos) (dm/number first-space-right)))))))
|
||||
|
||||
(defn- build-result
|
||||
(defn build-result
|
||||
"Builds the result map for `insert-ref` by replacing the substring of `value`
|
||||
between `prefix-end` and `suffix-start` with a formatted reference `{name}`.
|
||||
Returns a map with:
|
||||
|
||||
@ -113,6 +113,80 @@ test.describe("Tokens - creation", () => {
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("User creates a token referencing another via combobox, replacing existing text", async ({
|
||||
page,
|
||||
}) => {
|
||||
const { tokensUpdateCreateModal } = await setupEmptyTokensFileRender(page, {
|
||||
flags: ["enable-token-combobox", "enable-feature-token-input"],
|
||||
});
|
||||
|
||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||
|
||||
const addTokenButton = tokensTabPanel.getByRole("button", {
|
||||
name: "Add Token: Border Radius",
|
||||
});
|
||||
|
||||
await addTokenButton.click();
|
||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||
|
||||
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||
const valueField = tokensUpdateCreateModal.getByRole("combobox", {
|
||||
name: "Value",
|
||||
});
|
||||
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
|
||||
// Create first token
|
||||
await nameField.fill("my-token");
|
||||
await valueField.fill("1 + 2");
|
||||
await expect(
|
||||
tokensUpdateCreateModal.getByText("Resolved value: 3"),
|
||||
).toBeVisible();
|
||||
await expect(submitButton).toBeEnabled();
|
||||
await submitButton.click();
|
||||
|
||||
await unfoldTokenType(tokensTabPanel, "border radius");
|
||||
await expect(
|
||||
tokensTabPanel.getByRole("button", { name: "my-token" }),
|
||||
).toBeEnabled();
|
||||
|
||||
// Create second token — type text first, then replace it via combobox
|
||||
await addTokenButton.click();
|
||||
|
||||
await nameField.fill("my-token-2");
|
||||
await valueField.fill("4 + 4");
|
||||
await expect(
|
||||
tokensUpdateCreateModal.getByText("Resolved value: 8"),
|
||||
).toBeVisible();
|
||||
|
||||
const toggleDropdownButton = tokensUpdateCreateModal.getByRole("button", {
|
||||
name: "Open token list",
|
||||
});
|
||||
await toggleDropdownButton.click();
|
||||
|
||||
const option = page.getByRole("option", { name: "my-token" });
|
||||
await expect(option).toBeVisible();
|
||||
await option.click();
|
||||
|
||||
// The typed text should have been replaced by {my-token}
|
||||
await expect(valueField).toHaveValue("{my-token}");
|
||||
await expect(
|
||||
tokensUpdateCreateModal.getByText("Resolved value: 3"),
|
||||
).toBeVisible();
|
||||
|
||||
// Original text must no longer be present
|
||||
await expect(valueField).not.toHaveValue("some text to replace");
|
||||
|
||||
await expect(submitButton).toBeEnabled();
|
||||
await submitButton.click();
|
||||
|
||||
await unfoldTokenType(tokensTabPanel, "border radius");
|
||||
await expect(
|
||||
tokensTabPanel.getByRole("button", { name: "my-token-2" }),
|
||||
).toBeEnabled();
|
||||
});
|
||||
|
||||
test("User creates dimensions token", async ({ page }) => {
|
||||
await testTokenCreationFlow(page, {
|
||||
tokenLabel: "Dimensions",
|
||||
|
||||
@ -147,12 +147,14 @@
|
||||
toggle-dropdown
|
||||
(mf/use-fn
|
||||
(mf/deps is-open)
|
||||
(fn [event]
|
||||
(fn [event & [select-text?]]
|
||||
(dom/prevent-default event)
|
||||
(swap! is-open* not)
|
||||
(reset! selected-id* (get-selected-id))
|
||||
(let [input-node (mf/ref-val ref)]
|
||||
(dom/focus! input-node))))
|
||||
(when select-text?
|
||||
(let [input-node (mf/ref-val ref)]
|
||||
(dom/select-text! input-node)
|
||||
(dom/focus! input-node)))))
|
||||
|
||||
resolve-stream
|
||||
(mf/with-memo [token]
|
||||
@ -264,7 +266,7 @@
|
||||
:tab-index "-1"
|
||||
:aria-label (tr "ds.inputs.numeric-input.open-token-list-dropdown")
|
||||
:on-mouse-down dom/prevent-default
|
||||
:on-click toggle-dropdown}]))})
|
||||
:on-click #(toggle-dropdown % true)}]))})
|
||||
props
|
||||
(if (or extra-error (and error touched?))
|
||||
(mf/spread-props props {:hint-type "error"
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
;; Dropdown closed with options: open and focus first
|
||||
(seq focusables)
|
||||
(do
|
||||
(toggle-dropdown event)
|
||||
(toggle-dropdown event false)
|
||||
(when get-selected-id
|
||||
(get-selected-id))
|
||||
(reset! focused-id* (first-focusable-id focusables)))
|
||||
|
||||
@ -50,9 +50,12 @@
|
||||
(defn select-option-by-id
|
||||
[id options-ref input-node value]
|
||||
(let [cursor (dom/selection-start input-node)
|
||||
sel-end (dom/selection-end input-node)
|
||||
options (mf/ref-val options-ref)
|
||||
options (if (delay? options) @options options)
|
||||
|
||||
option (get-option options id)
|
||||
name (:name option)]
|
||||
(cto/insert-ref value cursor name)))
|
||||
(if (= cursor sel-end)
|
||||
(cto/insert-ref value cursor name)
|
||||
(cto/build-result value cursor sel-end name))))
|
||||
@ -299,6 +299,11 @@
|
||||
(when (some? node)
|
||||
(.-selectionStart node)))
|
||||
|
||||
(defn selection-end
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(.-selectionEnd node)))
|
||||
|
||||
(defn set-selection-range!
|
||||
[^js node start end]
|
||||
(when (some? node)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user