mirror of
https://github.com/penpot/penpot.git
synced 2026-09-23 04:16:13 +00:00
* ✨ Auto link tokens when adding external libraries (provisional) * 🔧 Refactor tokens-lib initialization * 🔧 Add separated TokenStatus to store status apart of TokensLib * 🔧 Make all status operations use the new data structure * 🔧 Normalize status helper functions and access token sets by id * 🔧 Rename :tokens-file to :tokens-source * 🎉 Allow the user to choose the tokens-source of a file * 🎉 Make tokens library readonly when it's in an external file * 🎉 Show tokens in library summaries * 🎉 Show source info in sidebar * 🔧 Fix integration tests * 🐛 Propagate changes of token values in external library * 🎉 Layout updates * 🔧 Refactor tokens source calculations * 🔧 Add harder checks for nil or empty values in everything * 🐛 Fix some integration tests * 🔧 Add integration tests for tokens in external libs * 🔧 Validate and repair missing tokens status * 🎉 Make ui changes optional with config flag * 🐛 Propagate tokens after synchronizing components in ext library * 🐛 Propagate tokens after creating new instances * 🐛 Propagate tokens after synchronizing tokens in ext library * 🐛 Add a tokens source icon to libraries section (#11439) * 🐛 Add a tokens source icon to libraries section * 🐛 Fix ellipsis on library names * ♻️ Remove code under flag on legacy component * 🐛 Fix token theme name on inspect tab * 🎉 Add changes notification (#11476) * 🎉 Add changes notification * ♻️ Change fn names * 🐛 Fix tokens source label truncation and missing translations (#11533) * 🐛 Fix tokens source label truncation and missing translations The tokens source file name always showed, even for the current file, and long names wrapped onto a second line instead of truncating because the header used flex-wrap and overflow-wrap: break-word instead of single-line ellipsis. Show the source row unconditionally (it now displays "This file" when the source is the current file, matching the connected-library case), truncate the file name to one line with an ellipsis, and only attach a tooltip with the full name when the text is actually truncated. Replace the hardcoded UI strings with translated ones and add their English and Spanish entries. AI-assisted-by: claude-sonnet-5 * 🐛 Remove redundant effect dependency in tokens source file-name-truncated? was listed as a dependency of the with-effect that checks and observes label truncation, even though it isn't read inside the effect body. Since the effect itself flips that state via check-file-name-truncated, including it as a dependency caused the ResizeObserver to be needlessly disconnected and reconnected on every truncation change. AI-assisted-by: claude-sonnet-5 * 🐛 Fix small visual error * 🐛 Fix problem with plugins * 🐛 Fix playwright tests --------- Co-authored-by: Eva Marco <evamarcod@gmail.com> Co-authored-by: Eva Marco <eva.marco@kaleidos.net> Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
202 lines
7.5 KiB
JavaScript
202 lines
7.5 KiB
JavaScript
import { test, expect } from "@playwright/test";
|
|
import { WorkspacePage } from "../pages/WorkspacePage";
|
|
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// The "Create typography style" button (workspace.options.convert-to-typography)
|
|
// in the text options sidebar is only shown when ALL of these hold for the
|
|
// selected text shape(s) (src/app/main/ui/workspace/sidebar/options/menus/text.cljs):
|
|
// (and (some? font) (not typography) (not multiple?) (not applied-token-name))
|
|
// Each test below isolates one condition that must independently hide it:
|
|
// - font missing (font-id not registered in app.main.fonts/fontsdb)
|
|
// - a typography asset is applied (typography-ref-id set)
|
|
// - multiple shapes are selected with differing attributes
|
|
// - a typography design token is applied (applied-tokens :typography)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function convertToTypographyButton(workspace) {
|
|
return workspace.rightSidebar.getByRole("button", {
|
|
name: "Create typography style",
|
|
});
|
|
}
|
|
|
|
test.describe("font missing", () => {
|
|
// Fixture render-wasm/get-file-text-custom-fonts.json has a text shape
|
|
// ("Penpot & Dragons") using a custom team font-id and no typography/token
|
|
// applied - otherwise exactly the state that reveals the button once its
|
|
// font resolves. Toggling the get-font-variants mock between "the team owns
|
|
// this font" and "empty" simulates the font being present vs. missing.
|
|
const FILE = {
|
|
id: "434b0541-fa2f-802f-8006-59827d964a9b",
|
|
pageId: "434b0541-fa2f-802f-8006-59827d964a9c",
|
|
};
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await WorkspacePage.init(page);
|
|
});
|
|
|
|
test("Create typography style button is hidden when the shape font is missing", async ({
|
|
page,
|
|
}) => {
|
|
const workspace = new WorkspacePage(page);
|
|
await workspace.setupEmptyFile();
|
|
await workspace.mockRPC(
|
|
/get\-file\?/,
|
|
"render-wasm/get-file-text-custom-fonts.json",
|
|
);
|
|
// The team does not own the shape's custom font, so it can't be resolved.
|
|
await workspace.mockRPC(
|
|
"get-font-variants?team-id=*",
|
|
"workspace/get-font-variants-empty.json",
|
|
);
|
|
await workspace.goToWorkspace({ fileId: FILE.id, pageId: FILE.pageId });
|
|
|
|
await workspace.clickLeafLayer("Penpot & Dragons");
|
|
|
|
await expect(convertToTypographyButton(workspace)).not.toBeVisible();
|
|
});
|
|
|
|
test("Create typography style button is visible once the shape font resolves", async ({
|
|
page,
|
|
}) => {
|
|
const workspace = new WorkspacePage(page);
|
|
await workspace.setupEmptyFile();
|
|
await workspace.mockRPC(
|
|
/get\-file\?/,
|
|
"render-wasm/get-file-text-custom-fonts.json",
|
|
);
|
|
// The team owns the shape's custom font, so it resolves normally.
|
|
await workspace.mockRPC(
|
|
"get-font-variants?team-id=*",
|
|
"render-wasm/get-font-variants-custom-fonts.json",
|
|
);
|
|
await workspace.goToWorkspace({ fileId: FILE.id, pageId: FILE.pageId });
|
|
|
|
await workspace.clickLeafLayer("Penpot & Dragons");
|
|
|
|
await expect(convertToTypographyButton(workspace)).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe("typography asset applied", () => {
|
|
// multiselection-typography.json: "Text with typography asset one" has a
|
|
// typography-ref-id pointing at an in-file typography asset (font
|
|
// gfont-agdasima, a built-in Google font that resolves with no extra
|
|
// mocking), and is not multi-selected or token-applied.
|
|
const FILE = {
|
|
id: "c7ce0794-0992-8105-8004-38f280443849",
|
|
pageId: "1062e0a0-8fe0-80ae-8007-e70b4993f5f0",
|
|
};
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await WorkspacePage.init(page);
|
|
});
|
|
|
|
test("Create typography style button is hidden when a typography asset is applied", async ({
|
|
page,
|
|
}) => {
|
|
const workspace = new WorkspacePage(page);
|
|
await workspace.setupEmptyFile();
|
|
await workspace.mockRPC(
|
|
/get\-file\?/,
|
|
"workspace/multiselection-typography.json",
|
|
);
|
|
await workspace.goToWorkspace({ fileId: FILE.id, pageId: FILE.pageId });
|
|
|
|
await workspace.clickLeafLayer("Text with typography asset one");
|
|
|
|
// Sanity check: the text options panel did render for this shape - the
|
|
// button is specifically hidden by the applied typography, not because
|
|
// the whole panel failed to show up.
|
|
await expect(
|
|
workspace.rightSidebar.getByRole("region", { name: "Text section" }),
|
|
).toBeVisible();
|
|
await expect(convertToTypographyButton(workspace)).not.toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe("multiple selection", () => {
|
|
// get-file-text-multiple-selection.json has two text shapes sharing the
|
|
// same (resolvable, built-in) font-id but differing font-size, with no
|
|
// typography or token applied - so selecting both together isolates
|
|
// `multiple?` becoming true without also making the font unresolved.
|
|
const FILE = {
|
|
id: "434b0541-fa2f-802f-8006-6a827d964a9b",
|
|
pageId: "434b0541-fa2f-802f-8006-6a827d964a9c",
|
|
};
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await WorkspacePage.init(page);
|
|
});
|
|
|
|
test("Create typography style button is hidden when multiple shapes with different values are selected", async ({
|
|
page,
|
|
}) => {
|
|
const workspace = new WorkspacePage(page);
|
|
await workspace.setupEmptyFile();
|
|
await workspace.mockRPC(
|
|
/get\-file\?/,
|
|
"workspace/get-file-text-multiple-selection.json",
|
|
);
|
|
await workspace.goToWorkspace({ fileId: FILE.id, pageId: FILE.pageId });
|
|
|
|
await workspace.clickLeafLayer("Text multiple selection one");
|
|
await expect(convertToTypographyButton(workspace)).toBeVisible();
|
|
|
|
await workspace.clickLeafLayer("Text multiple selection two", {
|
|
modifiers: ["Shift"],
|
|
});
|
|
|
|
await expect(convertToTypographyButton(workspace)).not.toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe("typography token applied", () => {
|
|
// get-file-token-tooltip.json: "Text with token" has a typography design
|
|
// token applied (applied-tokens :typography) using font gfont-arizonia (a
|
|
// built-in Google font that resolves with no extra mocking).
|
|
test.beforeEach(async ({ page }) => {
|
|
await WasmWorkspacePage.init(page);
|
|
await WasmWorkspacePage.mockRPC(page, "get-teams", "get-teams-tokens.json");
|
|
});
|
|
|
|
test("Create typography style button is hidden when a typography token is applied", async ({
|
|
page,
|
|
}) => {
|
|
const workspace = new WasmWorkspacePage(page);
|
|
await workspace.mockConfigFlags(["enable-feature-token-input"]);
|
|
await workspace.setupEmptyFile();
|
|
await workspace.mockRPC("get-team?id=*", "workspace/get-team-tokens.json");
|
|
await workspace.mockRPC(
|
|
/get\-file\?/,
|
|
"workspace/get-file-token-tooltip.json",
|
|
);
|
|
await workspace.mockRPC(
|
|
/get\-file\-fragment\?/,
|
|
"workspace/get-file-fragment-tokens.json",
|
|
);
|
|
await workspace.mockRPC(
|
|
"update-file?id=*",
|
|
"workspace/update-file-create-rect.json",
|
|
);
|
|
await workspace.goToWorkspace({
|
|
fileId: "c7ce0794-0992-8105-8004-38f280443849",
|
|
pageId: "4530574a-7a0a-807b-8008-0107b2c4628e",
|
|
});
|
|
|
|
await page.getByRole("tab", { name: "Layers" }).click();
|
|
await workspace.layers
|
|
.getByTestId("layer-row")
|
|
.filter({ hasText: "Text with token" })
|
|
.click();
|
|
|
|
// Sanity check: the text options panel did render for this shape - the
|
|
// button is specifically hidden by the applied token, not because the
|
|
// whole panel failed to show up.
|
|
await expect(
|
|
workspace.rightSidebar.getByRole("region", { name: "Text section" }),
|
|
).toBeVisible();
|
|
await expect(convertToTypographyButton(workspace)).not.toBeVisible();
|
|
});
|
|
});
|