mirror of https://github.com/penpot/penpot.git
♻️ Adapt token tests to tree structure
This commit is contained in:
parent
9cf356b3ce
commit
77b6fe9e19
|
|
@ -40,6 +40,7 @@ const setupEmptyTokensFile = async (page, options = {}) => {
|
|||
tokensUpdateCreateModal: workspacePage.tokensUpdateCreateModal,
|
||||
tokenThemesSetsSidebar: workspacePage.tokenThemesSetsSidebar,
|
||||
tokenSetItems: workspacePage.tokenSetItems,
|
||||
tokensSidebar: workspacePage.tokensSidebar,
|
||||
tokenSetGroupItems: workspacePage.tokenSetGroupItems,
|
||||
tokenContextMenuForSet: workspacePage.tokenContextMenuForSet,
|
||||
};
|
||||
|
|
@ -110,15 +111,12 @@ const checkInputFieldWithError = async (
|
|||
).toBeVisible();
|
||||
};
|
||||
|
||||
const checkInputFieldWithoutError = async (
|
||||
tokenThemeUpdateCreateModal,
|
||||
inputLocator,
|
||||
) => {
|
||||
const checkInputFieldWithoutError = async (inputLocator) => {
|
||||
expect(await inputLocator.getAttribute("aria-invalid")).toBeNull();
|
||||
expect(await inputLocator.getAttribute("aria-describedby")).toBeNull();
|
||||
};
|
||||
|
||||
async function testTokenCreationFlow(
|
||||
const testTokenCreationFlow = async (
|
||||
page,
|
||||
{
|
||||
tokenLabel,
|
||||
|
|
@ -132,7 +130,7 @@ async function testTokenCreationFlow(
|
|||
resolvedValueText,
|
||||
secondResolvedValueText,
|
||||
},
|
||||
) {
|
||||
) => {
|
||||
const invalidValueError = "Invalid token value";
|
||||
const emptyNameError = "Name should be at least 1 character";
|
||||
const selfReferenceError = "Token has self reference";
|
||||
|
|
@ -242,7 +240,46 @@ async function testTokenCreationFlow(
|
|||
await expect(
|
||||
tokensTabPanel.getByRole("button", { name: "my-token-2" }),
|
||||
).toBeEnabled();
|
||||
}
|
||||
};
|
||||
|
||||
const unfoldTokenTree = async (tokensTabPanel, type, tokenName) => {
|
||||
const tokenSegments = tokenName.split(".");
|
||||
const tokenFolderTree = tokenSegments.slice(0, -1);
|
||||
const tokenLeafName = tokenSegments.pop();
|
||||
|
||||
const typeParentWrapper = tokensTabPanel.getByTestId(`section-${type}`);
|
||||
const typeSectionButton = typeParentWrapper
|
||||
.getByRole("button", {
|
||||
name: type,
|
||||
})
|
||||
.first();
|
||||
|
||||
console.log(await typeSectionButton.getAttribute("aria-expanded"));
|
||||
const isSectionExpanded =
|
||||
await typeSectionButton.getAttribute("aria-expanded");
|
||||
|
||||
if (isSectionExpanded === "false") {
|
||||
await typeSectionButton.click();
|
||||
}
|
||||
|
||||
for (const segment of tokenFolderTree) {
|
||||
const segmentButton = typeParentWrapper
|
||||
.getByRole("listitem")
|
||||
.getByRole("button", { name: segment })
|
||||
.first();
|
||||
|
||||
const isExpanded = await segmentButton.getAttribute("aria-expanded");
|
||||
if (isExpanded === "false") {
|
||||
await segmentButton.click();
|
||||
}
|
||||
}
|
||||
|
||||
await expect(
|
||||
typeParentWrapper.getByRole("button", {
|
||||
name: tokenLeafName,
|
||||
}),
|
||||
).toBeEnabled();
|
||||
};
|
||||
|
||||
test.describe("Tokens: Tokens Tab", () => {
|
||||
test("Clicking tokens tab button opens tokens sidebar tab", async ({
|
||||
|
|
@ -398,15 +435,12 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
const emptyNameError = "Name should be at least 1 character";
|
||||
const selfReferenceError = "Token has self reference";
|
||||
const missingReferenceError = "Missing token references";
|
||||
const { tokensUpdateCreateModal, tokenThemesSetsSidebar } =
|
||||
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
|
||||
await setupEmptyTokensFile(page);
|
||||
|
||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||
const addTokenButton = tokensTabPanel.getByRole("button", {
|
||||
name: `Add Token: Color`,
|
||||
});
|
||||
|
||||
await addTokenButton.click();
|
||||
await tokensSidebar
|
||||
.getByRole("button", { name: "Add Token: Color" })
|
||||
.click();
|
||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||
|
||||
// Placeholder checks
|
||||
|
|
@ -471,38 +505,34 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
await expect(submitButton).toBeEnabled();
|
||||
await submitButton.click();
|
||||
|
||||
await expect(
|
||||
tokensTabPanel.getByRole("button", {
|
||||
name: "color.primary",
|
||||
}),
|
||||
).toBeEnabled();
|
||||
await unfoldTokenTree(tokensSidebar, "color", "color.primary");
|
||||
|
||||
// Create token referencing the previous one with keyboard
|
||||
|
||||
await tokensTabPanel
|
||||
await tokensSidebar
|
||||
.getByRole("button", { name: "Add Token: Color" })
|
||||
.click();
|
||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||
|
||||
await nameField.click();
|
||||
await nameField.fill("color.secondary");
|
||||
await nameField.fill("secondary");
|
||||
await nameField.press("Tab");
|
||||
|
||||
await valueField.click();
|
||||
await valueField.fill("{color.primary}");
|
||||
|
||||
await expect(submitButton).toBeEnabled();
|
||||
await nameField.press("Enter");
|
||||
await submitButton.press("Enter");
|
||||
|
||||
await expect(
|
||||
tokensTabPanel.getByRole("button", {
|
||||
name: "color.secondary",
|
||||
tokensSidebar.getByRole("button", {
|
||||
name: "secondary",
|
||||
}),
|
||||
).toBeEnabled();
|
||||
|
||||
// Tokens tab panel should have two tokens with the color red / #ff0000
|
||||
await expect(
|
||||
tokensTabPanel.getByRole("button", { name: "#ff0000" }),
|
||||
tokensSidebar.getByRole("button", { name: "#ff0000" }),
|
||||
).toHaveCount(2);
|
||||
|
||||
// Global set has been auto created and is active
|
||||
|
|
@ -518,7 +548,7 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
).toHaveAttribute("aria-checked", "true");
|
||||
|
||||
// Check color picker
|
||||
await tokensTabPanel
|
||||
await tokensSidebar
|
||||
.getByRole("button", { name: "Add Token: Color" })
|
||||
.click();
|
||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||
|
|
@ -1079,7 +1109,7 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
const emptyNameError = "Name should be at least 1 character";
|
||||
|
||||
const { tokensUpdateCreateModal, tokenThemesSetsSidebar } =
|
||||
await setupEmptyTokensFile(page, {flags: ["enable-token-shadow"]});
|
||||
await setupEmptyTokensFile(page, { flags: ["enable-token-shadow"] });
|
||||
|
||||
// Open modal
|
||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||
|
|
@ -1507,24 +1537,15 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
test("User edits token and auto created set show up in the sidebar", async ({
|
||||
page,
|
||||
}) => {
|
||||
const {
|
||||
workspacePage,
|
||||
tokensUpdateCreateModal,
|
||||
tokenThemesSetsSidebar,
|
||||
tokensSidebar,
|
||||
tokenContextMenuForToken,
|
||||
} = await setupTokensFile(page);
|
||||
const { tokensUpdateCreateModal, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFile(page);
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
const tokensColorGroup = tokensSidebar.getByRole("button", {
|
||||
name: "Color 92",
|
||||
});
|
||||
await expect(tokensColorGroup).toBeVisible();
|
||||
await tokensColorGroup.click();
|
||||
await unfoldTokenTree(tokensSidebar, "color", "colors.blue.100");
|
||||
|
||||
const colorToken = tokensSidebar.getByRole("button", {
|
||||
name: "colors.blue.100",
|
||||
name: "100",
|
||||
});
|
||||
await expect(colorToken).toBeVisible();
|
||||
await colorToken.click({ button: "right" });
|
||||
|
|
@ -1541,8 +1562,10 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
|
||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
||||
|
||||
await unfoldTokenTree(tokensSidebar, "color", "colors.blue.100.changed");
|
||||
|
||||
const colorTokenChanged = tokensSidebar.getByRole("button", {
|
||||
name: "colors.blue.100.changed",
|
||||
name: "changed",
|
||||
});
|
||||
await expect(colorTokenChanged).toBeVisible();
|
||||
});
|
||||
|
|
@ -1633,11 +1656,10 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
});
|
||||
|
||||
test("User creates grouped color token", async ({ page }) => {
|
||||
const { workspacePage, tokensUpdateCreateModal, tokenThemesSetsSidebar } =
|
||||
const { workspacePage, tokensUpdateCreateModal, tokensSidebar } =
|
||||
await setupEmptyTokensFile(page);
|
||||
|
||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||
await tokensTabPanel
|
||||
await tokensSidebar
|
||||
.getByRole("button", { name: "Add Token: Color" })
|
||||
.click();
|
||||
|
||||
|
|
@ -1649,7 +1671,7 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
const valueField = tokensUpdateCreateModal.getByLabel("Value");
|
||||
|
||||
await nameField.click();
|
||||
await nameField.fill("color.dark.primary");
|
||||
await nameField.fill("dark.primary");
|
||||
|
||||
await valueField.click();
|
||||
await valueField.fill("red");
|
||||
|
|
@ -1660,7 +1682,9 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
await expect(submitButton).toBeEnabled();
|
||||
await submitButton.click();
|
||||
|
||||
await expect(tokensTabPanel.getByLabel("color.dark.primary")).toBeEnabled();
|
||||
await unfoldTokenTree(tokensSidebar, "color", "dark.primary");
|
||||
|
||||
await expect(tokensSidebar.getByLabel("primary")).toBeEnabled();
|
||||
});
|
||||
|
||||
test("User cant create regular token with value missing", async ({
|
||||
|
|
@ -1676,7 +1700,6 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||
|
||||
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||
const valueField = tokensUpdateCreateModal.getByLabel("Value");
|
||||
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
|
|
@ -1686,7 +1709,7 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
|
||||
// Fill in name but leave value empty
|
||||
await nameField.click();
|
||||
await nameField.fill("color.primary");
|
||||
await nameField.fill("primary");
|
||||
|
||||
// Submit button should remain disabled when value is empty
|
||||
await expect(submitButton).toBeDisabled();
|
||||
|
|
@ -1704,7 +1727,6 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
.click();
|
||||
|
||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||
const valueField = tokensUpdateCreateModal.getByLabel("Value");
|
||||
|
||||
await valueField.click();
|
||||
|
|
@ -1754,15 +1776,10 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
const tokensColorGroup = tokensSidebar.getByRole("button", {
|
||||
name: "Color 92",
|
||||
});
|
||||
|
||||
await expect(tokensColorGroup).toBeVisible();
|
||||
await tokensColorGroup.click();
|
||||
unfoldTokenTree(tokensSidebar, "color", "colors.blue.100");
|
||||
|
||||
const colorToken = tokensSidebar.getByRole("button", {
|
||||
name: "colors.blue.100",
|
||||
name: "100",
|
||||
});
|
||||
|
||||
await colorToken.click({ button: "right" });
|
||||
|
|
@ -1782,15 +1799,10 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
const tokensColorGroup = tokensSidebar.getByRole("button", {
|
||||
name: "Color 92",
|
||||
});
|
||||
await expect(tokensColorGroup).toBeVisible();
|
||||
|
||||
await tokensColorGroup.click();
|
||||
unfoldTokenTree(tokensSidebar, "color", "colors.blue.100");
|
||||
|
||||
const colorToken = tokensSidebar.getByRole("button", {
|
||||
name: "colors.blue.100",
|
||||
name: "100",
|
||||
});
|
||||
await expect(colorToken).toBeVisible();
|
||||
await colorToken.click({ button: "right" });
|
||||
|
|
@ -1803,8 +1815,7 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
});
|
||||
|
||||
test("User fold/unfold color tokens", async ({ page }) => {
|
||||
const { tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFile(page);
|
||||
const { tokensSidebar } = await setupTokensFile(page);
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
|
|
@ -1814,8 +1825,10 @@ test.describe("Tokens: Tokens Tab", () => {
|
|||
await expect(tokensColorGroup).toBeVisible();
|
||||
await tokensColorGroup.click();
|
||||
|
||||
unfoldTokenTree(tokensSidebar, "color", "colors.blue.100");
|
||||
|
||||
const colorToken = tokensSidebar.getByRole("button", {
|
||||
name: "colors.blue.100",
|
||||
name: "100",
|
||||
});
|
||||
await expect(colorToken).toBeVisible();
|
||||
await tokensColorGroup.click();
|
||||
|
|
@ -2218,13 +2231,10 @@ test.describe("Tokens: Apply token", () => {
|
|||
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
|
||||
await tokensTabButton.click();
|
||||
|
||||
await tokensSidebar
|
||||
.getByRole("button")
|
||||
.filter({ hasText: "Color" })
|
||||
.click();
|
||||
unfoldTokenTree(tokensSidebar, "color", "colors.black");
|
||||
|
||||
await tokensSidebar
|
||||
.getByRole("button", { name: "colors.black" })
|
||||
.getByRole("button", { name: "black" })
|
||||
.click({ button: "right" });
|
||||
await tokenContextMenuForToken.getByText("Fill").click();
|
||||
|
||||
|
|
@ -2462,7 +2472,7 @@ test.describe("Tokens: Apply token", () => {
|
|||
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||
|
||||
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||
await nameField.fill("shadow.primary");
|
||||
await nameField.fill("primary");
|
||||
|
||||
// User adds first shadow with a color from the color ramp
|
||||
const firstShadowFields = tokensUpdateCreateModal.getByTestId(
|
||||
|
|
@ -2709,9 +2719,11 @@ test.describe("Tokens: Apply token", () => {
|
|||
await submitButton.click();
|
||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
||||
|
||||
unfoldTokenTree(tokensSidebar, "shadow", "primary");
|
||||
|
||||
// Verify token appears in sidebar
|
||||
const shadowToken = tokensSidebar.getByRole("button", {
|
||||
name: "shadow.primary",
|
||||
name: "primary",
|
||||
});
|
||||
await expect(shadowToken).toBeEnabled();
|
||||
|
||||
|
|
|
|||
|
|
@ -23,25 +23,27 @@
|
|||
|
||||
(mf/defc layer-button*
|
||||
{::mf/schema schema:layer-button}
|
||||
[{:keys [label description class is-expandable expanded icon on-toggle-expand children]}]
|
||||
[:div {:class (stl/css :layer-button-wrapper)}
|
||||
[:button {:class [class (stl/css-case :layer-button true
|
||||
:layer-button--expandable is-expandable
|
||||
:layer-button--expanded expanded)]
|
||||
:type "button"
|
||||
:on-click on-toggle-expand}
|
||||
[:div {:class (stl/css :layer-button-content)}
|
||||
(when is-expandable
|
||||
(if expanded
|
||||
[:> icon* {:icon-id i/arrow-down :class (stl/css :folder-node-icon)}]
|
||||
[:> icon* {:icon-id i/arrow-right :class (stl/css :folder-node-icon)}]))
|
||||
(when icon
|
||||
[:> icon* {:icon-id icon :class (stl/css :layer-button-icon)}])
|
||||
[:span {:class (stl/css :layer-button-name)}
|
||||
label]
|
||||
(when description
|
||||
[:span {:class (stl/css :layer-button-description)}
|
||||
description])
|
||||
[:span {:class (stl/css :layer-button-quantity)}]]]
|
||||
[:div {:class (stl/css :layer-button-actions)}
|
||||
children]])
|
||||
[{:keys [label description class is-expandable expanded icon on-toggle-expand children] :rest props}]
|
||||
(let [button-props (mf/spread-props props
|
||||
{:class [class (stl/css-case :layer-button true
|
||||
:layer-button--expandable is-expandable
|
||||
:layer-button--expanded expanded)]
|
||||
:type "button"
|
||||
:on-click on-toggle-expand})]
|
||||
[:div {:class (stl/css :layer-button-wrapper)}
|
||||
[:> "button" button-props
|
||||
[:div {:class (stl/css :layer-button-content)}
|
||||
(when is-expandable
|
||||
(if expanded
|
||||
[:> icon* {:icon-id i/arrow-down :class (stl/css :folder-node-icon)}]
|
||||
[:> icon* {:icon-id i/arrow-right :class (stl/css :folder-node-icon)}]))
|
||||
(when icon
|
||||
[:> icon* {:icon-id icon :class (stl/css :layer-button-icon)}])
|
||||
[:span {:class (stl/css :layer-button-name)}
|
||||
label]
|
||||
(when description
|
||||
[:span {:class (stl/css :layer-button-description)}
|
||||
description])
|
||||
[:span {:class (stl/css :layer-button-quantity)}]]]
|
||||
[:div {:class (stl/css :layer-button-actions)}
|
||||
children]]))
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@
|
|||
(let [tokens (get tokens-by-type type)]
|
||||
[:> token-group* {:key (name type)
|
||||
:tokens tokens
|
||||
:is-open (get open-status type false)
|
||||
:is-expanded (get open-status type false)
|
||||
:type type
|
||||
:selected-ids selected
|
||||
:selected-shapes selected-shapes
|
||||
|
|
|
|||
|
|
@ -64,13 +64,13 @@
|
|||
|
||||
(mf/defc token-group*
|
||||
{::mf/schema schema:token-group}
|
||||
[{:keys [type tokens selected-shapes is-selected-inside-layout active-theme-tokens selected-token-set-id tokens-lib is-open selected-ids]}]
|
||||
[{:keys [type tokens selected-shapes is-selected-inside-layout active-theme-tokens selected-token-set-id tokens-lib is-expanded selected-ids]}]
|
||||
(let [{:keys [modal title]}
|
||||
(get dwta/token-properties type)
|
||||
editing-ref (mf/deref refs/workspace-editor-state)
|
||||
not-editing? (empty? editing-ref)
|
||||
|
||||
is-open (d/nilv is-open false)
|
||||
is-expanded (d/nilv is-expanded false)
|
||||
|
||||
can-edit?
|
||||
(mf/use-ctx ctx/can-edit?)
|
||||
|
|
@ -95,8 +95,8 @@
|
|||
|
||||
on-toggle-open-click
|
||||
(mf/use-fn
|
||||
(mf/deps is-open type)
|
||||
#(st/emit! (dwtl/set-token-type-section-open type (not is-open))))
|
||||
(mf/deps is-expanded type)
|
||||
#(st/emit! (dwtl/set-token-type-section-open type (not is-expanded))))
|
||||
|
||||
on-popover-open-click
|
||||
(mf/use-fn
|
||||
|
|
@ -124,11 +124,14 @@
|
|||
(st/emit! (dwta/toggle-token {:token token
|
||||
:shape-ids selected-ids}))))))]
|
||||
|
||||
[:div {:class (stl/css :token-section-wrapper)}
|
||||
[:div {:class (stl/css :token-section-wrapper)
|
||||
:data-testid (dm/str "section-" (name type))}
|
||||
[:> layer-button* {:label title
|
||||
:expanded is-open
|
||||
:expanded is-expanded
|
||||
:description (when expandable? (dm/str (count tokens)))
|
||||
:is-expandable expandable?
|
||||
:aria-expanded is-expanded
|
||||
:aria-controls (dm/str "token-tree-" (name type))
|
||||
:on-toggle-expand on-toggle-open-click
|
||||
:icon (token-section-icon type)}
|
||||
(when can-edit?
|
||||
|
|
@ -138,8 +141,10 @@
|
|||
:variant "ghost"
|
||||
:on-click on-popover-open-click
|
||||
:class (stl/css :token-section-icon)}])]
|
||||
(when is-open
|
||||
(when is-expanded
|
||||
[:> token-tree* {:tokens tokens
|
||||
:id (dm/str "token-tree-" (name type))
|
||||
:type type
|
||||
:tokens-lib tokens-lib
|
||||
:selected-shapes selected-shapes
|
||||
:active-theme-tokens active-theme-tokens
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
[app.main.ui.ds.utilities.swatch :refer [swatch*]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[cljs.pprint :as pp]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
|
@ -221,8 +220,7 @@
|
|||
|
||||
color
|
||||
(when (cft/color-token? token)
|
||||
(let [theme-token (get active-theme-tokens name)
|
||||
_ (pp/pprint {:theme-token active-theme-tokens :name name})]
|
||||
(let [theme-token (get active-theme-tokens name)]
|
||||
(or (dwtc/resolved-token-bullet-color theme-token)
|
||||
(dwtc/resolved-token-bullet-color token))))
|
||||
|
||||
|
|
|
|||
|
|
@ -36,11 +36,14 @@
|
|||
[:li {:class (stl/css :folder-node)}
|
||||
[:> layer-button* {:label (:name node)
|
||||
:expanded expanded
|
||||
:aria-expanded expanded
|
||||
:aria-controls (str "folder-children-" (:path node))
|
||||
:is-expandable (:has-children node)
|
||||
:on-toggle-expand swap-folder-expanded}]
|
||||
(when expanded
|
||||
(let [children-fn (:children-fn node)]
|
||||
[:div {:class (stl/css :folder-children-wrapper)}
|
||||
[:div {:class (stl/css :folder-children-wrapper)
|
||||
:id (str "folder-children-" (:path node))}
|
||||
(when children-fn
|
||||
(let [children (children-fn)]
|
||||
(for [child children]
|
||||
|
|
@ -69,6 +72,7 @@
|
|||
(def ^:private schema:token-tree
|
||||
[:map
|
||||
[:tokens :any]
|
||||
[:type :keyword]
|
||||
[:selected-shapes :any]
|
||||
[:is-selected-inside-layout {:optional true} :boolean]
|
||||
[:active-theme-tokens {:optional true} :any]
|
||||
|
|
|
|||
Loading…
Reference in New Issue