mirror of
https://github.com/penpot/penpot.git
synced 2026-06-01 21:20:18 +00:00
🐛 Fix errors when token name conflicts with group name
This commit is contained in:
parent
8dbbd49c0e
commit
429103d076
@ -0,0 +1,439 @@
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"fdata/path-data",
|
||||
"plugins/runtime",
|
||||
"design-tokens/v1",
|
||||
"variants/v1",
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/objects-map",
|
||||
"tokens/numeric-input",
|
||||
"render-wasm/v1",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:team-id": "~uc6b102e2-5aaa-809c-8007-dcd1eab2135d",
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "New File 11",
|
||||
"~:revn": 3,
|
||||
"~:modified-at": "~m1779204621124",
|
||||
"~:vern": 0,
|
||||
"~:id": "~u9fb430ed-e1e9-81bc-8008-0b7ae978d9c4",
|
||||
"~:is-shared": false,
|
||||
"~:migrations": {
|
||||
"~#ordered-set": [
|
||||
"legacy-2",
|
||||
"legacy-3",
|
||||
"legacy-5",
|
||||
"legacy-6",
|
||||
"legacy-7",
|
||||
"legacy-8",
|
||||
"legacy-9",
|
||||
"legacy-10",
|
||||
"legacy-11",
|
||||
"legacy-12",
|
||||
"legacy-13",
|
||||
"legacy-14",
|
||||
"legacy-16",
|
||||
"legacy-17",
|
||||
"legacy-18",
|
||||
"legacy-19",
|
||||
"legacy-25",
|
||||
"legacy-26",
|
||||
"legacy-27",
|
||||
"legacy-28",
|
||||
"legacy-29",
|
||||
"legacy-31",
|
||||
"legacy-32",
|
||||
"legacy-33",
|
||||
"legacy-34",
|
||||
"legacy-36",
|
||||
"legacy-37",
|
||||
"legacy-38",
|
||||
"legacy-39",
|
||||
"legacy-40",
|
||||
"legacy-41",
|
||||
"legacy-42",
|
||||
"legacy-43",
|
||||
"legacy-44",
|
||||
"legacy-45",
|
||||
"legacy-46",
|
||||
"legacy-47",
|
||||
"legacy-48",
|
||||
"legacy-49",
|
||||
"legacy-50",
|
||||
"legacy-51",
|
||||
"legacy-52",
|
||||
"legacy-53",
|
||||
"legacy-54",
|
||||
"legacy-55",
|
||||
"legacy-56",
|
||||
"legacy-57",
|
||||
"legacy-59",
|
||||
"legacy-62",
|
||||
"legacy-65",
|
||||
"legacy-66",
|
||||
"legacy-67",
|
||||
"0001-remove-tokens-from-groups",
|
||||
"0002-normalize-bool-content-v2",
|
||||
"0002-clean-shape-interactions",
|
||||
"0003-fix-root-shape",
|
||||
"0003-convert-path-content-v2",
|
||||
"0005-deprecate-image-type",
|
||||
"0006-fix-old-texts-fills",
|
||||
"0008-fix-library-colors-v4",
|
||||
"0009-clean-library-colors",
|
||||
"0009-add-partial-text-touched-flags",
|
||||
"0010-fix-swap-slots-pointing-non-existent-shapes",
|
||||
"0011-fix-invalid-text-touched-flags",
|
||||
"0012-fix-position-data",
|
||||
"0013-fix-component-path",
|
||||
"0013-clear-invalid-strokes-and-fills",
|
||||
"0014-fix-tokens-lib-duplicate-ids",
|
||||
"0014-clear-components-nil-objects",
|
||||
"0015-fix-text-attrs-blank-strings",
|
||||
"0015-clean-shadow-color",
|
||||
"0016-copy-fills-from-position-data-to-text-node",
|
||||
"0017-fix-layout-flex-dir",
|
||||
"0018-remove-unneeded-objects-from-components",
|
||||
"0019-fix-missing-swap-slots",
|
||||
"0020-sync-component-id-with-near-main"
|
||||
]
|
||||
},
|
||||
"~:version": 67,
|
||||
"~:project-id": "~u4cdd76d8-0e6d-8168-8008-0118118e1a1a",
|
||||
"~:created-at": "~m1779204571619",
|
||||
"~:backend": "legacy-db",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~u9fb430ed-e1e9-81bc-8008-0b7ae978d9c5"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~u9fb430ed-e1e9-81bc-8008-0b7ae978d9c5": {
|
||||
"~:objects": {
|
||||
"~#penpot/objects-map/v2": {
|
||||
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^H\",0.01,\"~:flip-y\",null,\"~:shapes\",[]]]"
|
||||
}
|
||||
},
|
||||
"~:id": "~u9fb430ed-e1e9-81bc-8008-0b7ae978d9c5",
|
||||
"~:name": "Page 1"
|
||||
}
|
||||
},
|
||||
"~:id": "~u9fb430ed-e1e9-81bc-8008-0b7ae978d9c4",
|
||||
"~:options": {
|
||||
"~:components-v2": true,
|
||||
"~:base-font-size": "16px"
|
||||
},
|
||||
"~:tokens-lib": {
|
||||
"~#penpot/tokens-lib": {
|
||||
"~:sets": {
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"S-Global",
|
||||
{
|
||||
"~#penpot/token-set": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cbd",
|
||||
"~:name": "Global",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204621126",
|
||||
"~:tokens": {
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"str1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cad",
|
||||
"~:name": "str1",
|
||||
"~:type": "~:stroke-width",
|
||||
"~:value": "1",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"typ1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cae",
|
||||
"~:name": "typ1",
|
||||
"~:type": "~:typography",
|
||||
"~:value": {
|
||||
"~:font-family": [
|
||||
"ABeeZee"
|
||||
],
|
||||
"~:font-size": "{fsiz1}",
|
||||
"~:font-weight": "{wei1}",
|
||||
"~:letter-spacing": "{lspa1}",
|
||||
"~:line-height": "1",
|
||||
"~:text-case": "{cas1}",
|
||||
"~:text-decoration": "{dec1}"
|
||||
},
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"siz1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180caf",
|
||||
"~:name": "siz1",
|
||||
"~:type": "~:sizing",
|
||||
"~:value": "1",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"num1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cb0",
|
||||
"~:name": "num1",
|
||||
"~:type": "~:number",
|
||||
"~:value": "1",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"lspa1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cb1",
|
||||
"~:name": "lspa1",
|
||||
"~:type": "~:letter-spacing",
|
||||
"~:value": "1",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"opa1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cb2",
|
||||
"~:name": "opa1",
|
||||
"~:type": "~:opacity",
|
||||
"~:value": "1",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"dec1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cb3",
|
||||
"~:name": "dec1",
|
||||
"~:type": "~:text-decoration",
|
||||
"~:value": "none",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"fsiz1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cb4",
|
||||
"~:name": "fsiz1",
|
||||
"~:type": "~:font-size",
|
||||
"~:value": "1",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"sha1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cb5",
|
||||
"~:name": "sha1",
|
||||
"~:type": "~:shadow",
|
||||
"~:value": [
|
||||
{
|
||||
"~:offset-x": "4",
|
||||
"~:offset-y": "4",
|
||||
"~:blur": "4",
|
||||
"~:spread": "0",
|
||||
"~:color": "grey",
|
||||
"~:inset": false
|
||||
}
|
||||
],
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"rad1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cb6",
|
||||
"~:name": "rad1",
|
||||
"~:type": "~:border-radius",
|
||||
"~:value": "1",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"cas1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cb7",
|
||||
"~:name": "cas1",
|
||||
"~:type": "~:text-case",
|
||||
"~:value": "none",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"spa1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cb8",
|
||||
"~:name": "spa1",
|
||||
"~:type": "~:spacing",
|
||||
"~:value": "1",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"rot1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cb9",
|
||||
"~:name": "rot1",
|
||||
"~:type": "~:rotation",
|
||||
"~:value": "1",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"wei1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cba",
|
||||
"~:name": "wei1",
|
||||
"~:type": "~:font-weight",
|
||||
"~:value": "regular",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"col1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cbb",
|
||||
"~:name": "col1",
|
||||
"~:type": "~:color",
|
||||
"~:value": "red",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"dim1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7af8180cbc",
|
||||
"~:name": "dim1",
|
||||
"~:type": "~:dimensions",
|
||||
"~:value": "1",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204586592"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"fam1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1dc5cafc-6d57-808d-8008-0b7b16dca3ad",
|
||||
"~:name": "fam1",
|
||||
"~:type": "~:font-family",
|
||||
"~:value": [
|
||||
"Aboreto"
|
||||
],
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1779204618098"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"~:themes": {
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"",
|
||||
{
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"__PENPOT__HIDDEN__TOKEN__THEME__",
|
||||
{
|
||||
"~#penpot/token-theme": {
|
||||
"~:id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:name": "__PENPOT__HIDDEN__TOKEN__THEME__",
|
||||
"~:group": "",
|
||||
"~:description": "",
|
||||
"~:is-source": false,
|
||||
"~:external-id": "",
|
||||
"~:modified-at": "~m1779204586593",
|
||||
"~:sets": {
|
||||
"~#set": [
|
||||
"Global"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"~:active-themes": {
|
||||
"~#set": [
|
||||
"/__PENPOT__HIDDEN__TOKEN__THEME__"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1103,7 +1103,7 @@ test.describe("Tokens - creation", () => {
|
||||
).toBeEnabled();
|
||||
});
|
||||
|
||||
test("User cant submit empty typography token or reference", async ({
|
||||
test("User can't submit empty typography token or reference", async ({
|
||||
page,
|
||||
}) => {
|
||||
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
|
||||
@ -1661,7 +1661,7 @@ test.describe("Tokens - creation", () => {
|
||||
await expect(tokensSidebar.getByLabel("primary")).toBeEnabled();
|
||||
});
|
||||
|
||||
test("User cant create regular token with value missing", async ({
|
||||
test("User can't create regular token with value missing", async ({
|
||||
page,
|
||||
}) => {
|
||||
const { tokensUpdateCreateModal } = await setupEmptyTokensFileRender(page);
|
||||
@ -1743,7 +1743,7 @@ test("User creates grouped color token", async ({ page }) => {
|
||||
await expect(tokensSidebar.getByLabel("primary")).toBeEnabled();
|
||||
});
|
||||
|
||||
test("User cant create regular token with value missing", async ({ page }) => {
|
||||
test("User can't create regular token with value missing", async ({ page }) => {
|
||||
const { tokensUpdateCreateModal } = await setupEmptyTokensFileRender(page);
|
||||
|
||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||
@ -1817,6 +1817,356 @@ test("User disables the current set but token still have resolved values shown i
|
||||
);
|
||||
});
|
||||
|
||||
test.describe("User can't create groups that clash with token names", () => {
|
||||
const unknownError = "Unknown error";
|
||||
|
||||
const changeSetInput = async (sidebar, setName, finalKey = "Enter") => {
|
||||
const setInput = sidebar.locator("input:focus");
|
||||
await expect(setInput).toBeVisible();
|
||||
await setInput.fill(setName);
|
||||
await setInput.press(finalKey);
|
||||
};
|
||||
|
||||
const createSet = async (sidebar, setName, finalKey = "Enter") => {
|
||||
const tokensTabButton = sidebar
|
||||
.getByRole("button", { name: "Add set" })
|
||||
.click();
|
||||
|
||||
await changeSetInput(sidebar, setName, (finalKey = "Enter"));
|
||||
};
|
||||
|
||||
const createBadToken = async (page, type, name, textFieldName, value) => {
|
||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||
|
||||
const { tokensUpdateCreateModal } = await setupTokensFileRender(page, {
|
||||
flags: ["enable-token-shadow"],
|
||||
});
|
||||
|
||||
// Add a token of the given type
|
||||
await tokensTabPanel
|
||||
.getByRole("button", { name: `Add Token: ${type}` })
|
||||
.click();
|
||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||
|
||||
// Fill the bad name
|
||||
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||
await nameField.fill(name);
|
||||
|
||||
// Fill the value
|
||||
const valueField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: textFieldName,
|
||||
});
|
||||
await valueField.fill(value);
|
||||
|
||||
// Check that the value has an error
|
||||
const errorNode =
|
||||
tokensUpdateCreateModal.getByText(unknownError);
|
||||
|
||||
await expect(errorNode).toBeVisible();
|
||||
|
||||
// Check that the form cannot be saved
|
||||
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
await expect(submitButton).toBeDisabled();
|
||||
};
|
||||
|
||||
test("User can't create Border Radius token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Border Radius", "rad1.bad", "Value", "10");
|
||||
});
|
||||
|
||||
test("User can't create Color token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Color", "col1.bad", "Value", "red");
|
||||
});
|
||||
|
||||
test("User can't create Dimensions token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Dimensions", "dim1.bad", "Value", "100");
|
||||
});
|
||||
|
||||
test("User can't create Font Size token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Font Size", "fsiz1.bad", "Value", "16");
|
||||
});
|
||||
|
||||
test("User can't create Font Weight token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Font Weight", "wei1.bad", "Value", "400");
|
||||
});
|
||||
|
||||
test("User can't create Letter Spacing token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Letter Spacing", "lspa1.bad", "Value", "1");
|
||||
});
|
||||
|
||||
test("User can't create Number token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Number", "num1.bad", "Value", "10");
|
||||
});
|
||||
|
||||
test("User can't create Rotation token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Rotation", "rot1.bad", "Value", "90");
|
||||
});
|
||||
|
||||
test("User can't create Sizing token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Sizing", "siz1.bad", "Value", "100");
|
||||
});
|
||||
|
||||
test("User can't create Spacing token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Spacing", "spa1.bad", "Value", "10");
|
||||
});
|
||||
|
||||
test("User can't create Stroke Width token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Stroke Width", "str1.bad", "Value", "2");
|
||||
});
|
||||
|
||||
test("User can't create Text Case token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Text Case", "cas1.bad", "Value", "uppercase");
|
||||
});
|
||||
|
||||
test("User can't create Text Decoration token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
await createBadToken(page, "Text Decoration", "td1.bad", "Value", "underline");
|
||||
});
|
||||
|
||||
test("User can't create Typography token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||
|
||||
const { tokensUpdateCreateModal } = await setupTokensFileRender(page, {
|
||||
flags: ["enable-token-shadow"],
|
||||
});
|
||||
|
||||
await tokensTabPanel
|
||||
.getByRole("button", { name: `Add Token: Typography` })
|
||||
.click();
|
||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||
|
||||
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||
await nameField.fill("typ1.bad");
|
||||
|
||||
const fontFamilyField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Font family",
|
||||
});
|
||||
await fontFamilyField.fill("Arial");
|
||||
|
||||
const fontSizeField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Font size",
|
||||
});
|
||||
await fontSizeField.fill("16");
|
||||
|
||||
const fontWeightField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Font weight",
|
||||
});
|
||||
await fontWeightField.fill("400");
|
||||
|
||||
const lineHeightField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Line height",
|
||||
});
|
||||
await lineHeightField.fill("1.5");
|
||||
|
||||
const letterSpacingField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Letter spacing",
|
||||
});
|
||||
await letterSpacingField.fill("0");
|
||||
|
||||
const textCaseField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Text case",
|
||||
});
|
||||
await textCaseField.fill("none");
|
||||
|
||||
const textDecorationField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Text decoration",
|
||||
});
|
||||
await textDecorationField.fill("none");
|
||||
|
||||
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
|
||||
const errorNode = tokensUpdateCreateModal.getByText(unknownError);
|
||||
await expect(errorNode).toHaveCount(6);
|
||||
await expect(submitButton).toBeDisabled();
|
||||
});
|
||||
|
||||
test("User can't create Shadow token with group name that clashes with existing token", async ({ page }) => {
|
||||
const { tokenThemesSetsSidebar, tokensSidebar } =
|
||||
await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-tokens-all-types.json"
|
||||
});
|
||||
|
||||
await expect(tokensSidebar).toBeVisible();
|
||||
|
||||
await createSet(tokenThemesSetsSidebar, "Second set");
|
||||
|
||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||
|
||||
const { tokensUpdateCreateModal } = await setupTokensFileRender(page, {
|
||||
flags: ["enable-token-shadow"],
|
||||
});
|
||||
|
||||
await tokensTabPanel
|
||||
.getByRole("button", { name: `Add Token: Shadow` })
|
||||
.click();
|
||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||
|
||||
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||
await nameField.fill("sha1.bad");
|
||||
|
||||
const colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Color",
|
||||
});
|
||||
await colorField.fill("red");
|
||||
|
||||
const offsetXField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "X",
|
||||
});
|
||||
await offsetXField.fill("7");
|
||||
|
||||
const offsetYField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Y",
|
||||
});
|
||||
await offsetYField.fill("8");
|
||||
|
||||
const blurField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Blur",
|
||||
});
|
||||
await blurField.fill("5");
|
||||
|
||||
const spreadField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Spread",
|
||||
});
|
||||
await spreadField.fill("1");
|
||||
|
||||
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||
name: "Save",
|
||||
});
|
||||
|
||||
const errorNode = tokensUpdateCreateModal.getByText(unknownError);
|
||||
await expect(errorNode).toHaveCount(4);
|
||||
await expect(submitButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Tokens tab - edition", () => {
|
||||
test("User edits typography token and all fields are valid", async ({
|
||||
page,
|
||||
|
||||
@ -348,10 +348,10 @@ const createToken = async (page, type, name, textFieldName, value) => {
|
||||
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||
await nameField.fill(name);
|
||||
|
||||
const colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
const valueField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: textFieldName,
|
||||
});
|
||||
await colorField.fill(value);
|
||||
await valueField.fill(value);
|
||||
|
||||
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||
name: "Save",
|
||||
|
||||
@ -141,9 +141,11 @@
|
||||
:error/value to produce the message. Falls back to :message for
|
||||
errors that originate from schema-validation (which have no :error/fn)."
|
||||
[error]
|
||||
(if-let [f (:error/fn error)]
|
||||
(f (:error/value error))
|
||||
(:message error)))
|
||||
(if error
|
||||
(if-let [f (:error/fn error)]
|
||||
(f (:error/value error))
|
||||
(:message error))
|
||||
(tr "labels.unknown-error")))
|
||||
|
||||
(defn resolve-error-assoc-message
|
||||
"Returns the error map with a :message key set to the resolved human-
|
||||
@ -151,9 +153,11 @@
|
||||
is called with :error/value; otherwise the map is returned unchanged
|
||||
(it is expected to already carry a :message from schema-validation)."
|
||||
[error]
|
||||
(if-let [f (:error/fn error)]
|
||||
(assoc error :message (f (:error/value error)))
|
||||
error))
|
||||
(if error
|
||||
(if-let [f (:error/fn error)]
|
||||
(assoc error :message (f (:error/value error)))
|
||||
error)
|
||||
(assoc error :message (tr "labels.unknown-error"))))
|
||||
|
||||
(defn humanize-errors [errors]
|
||||
(->> errors
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
[app.config :as cf]
|
||||
[app.main.data.helpers :as dh]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.style-dictionary :as sd]
|
||||
[app.main.data.workspace.tokens.application :as dwta]
|
||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||
[app.main.data.workspace.tokens.propagation :as dwtp]
|
||||
@ -112,9 +111,6 @@
|
||||
(mf/with-memo [active-tokens selected-token-set-tokens]
|
||||
(merge active-tokens selected-token-set-tokens))
|
||||
|
||||
tokens
|
||||
(sd/use-resolved-tokens* tokens)
|
||||
|
||||
;; Group tokens by their type
|
||||
tokens-by-type
|
||||
(mf/with-memo [tokens selected-token-set-tokens]
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
[app.config :as cf]
|
||||
[app.main.data.style-dictionary :as sd]
|
||||
[app.main.data.tokenscript :as ts]
|
||||
[app.main.data.workspace.tokens.errors :as wte]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.controls.input :as ds]
|
||||
@ -60,7 +61,9 @@
|
||||
resolved-value)]
|
||||
(if resolved-value
|
||||
(rx/of {:value resolved-value})
|
||||
(rx/of {:error (first errors)}))))))))
|
||||
(rx/of {:error (if errors
|
||||
(first errors)
|
||||
(wte/error-with-value :error/unknown value))}))))))))
|
||||
|
||||
(mf/defc value-combobox*
|
||||
[{:keys [name tokens token token-type empty-to-end ref] :rest props}]
|
||||
|
||||
@ -78,7 +78,9 @@
|
||||
resolved-value)]
|
||||
(if resolved-value
|
||||
(rx/of {:value resolved-value})
|
||||
(rx/of {:error (first errors)}))))))))
|
||||
(rx/of {:error (if errors
|
||||
(first errors)
|
||||
(wte/error-with-value :error/unknown value))}))))))))
|
||||
|
||||
(mf/defc fonts-combobox*
|
||||
[{:keys [token tokens name] :rest props}]
|
||||
|
||||
@ -181,7 +181,9 @@
|
||||
resolved-value)]
|
||||
(if resolved-value
|
||||
(rx/of {:value resolved-value})
|
||||
(rx/of {:error (first errors)}))))))))
|
||||
(rx/of {:error (if errors
|
||||
(first errors)
|
||||
(wte/error-with-value :error/unknown value))}))))))))
|
||||
|
||||
(mf/defc input*
|
||||
[{:keys [name tokens token] :rest props}]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user