diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index 7f5e6e83a..f791eaee1 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -604,18 +604,23 @@ (defn remove-layout-item-data [shape] - (dissoc shape - :layout-item-margin - :layout-item-margin-type - :layout-item-h-sizing - :layout-item-v-sizing - :layout-item-max-h - :layout-item-min-h - :layout-item-max-w - :layout-item-min-w - :layout-item-align-self - :layout-item-absolute - :layout-item-z-index)) + (-> shape + (dissoc :layout-item-margin + :layout-item-margin-type + :layout-item-max-h + :layout-item-min-h + :layout-item-max-w + :layout-item-min-w + :layout-item-align-self + :layout-item-absolute + :layout-item-z-index) + (cond-> (or (not (any-layout? shape)) + (= :fill (:layout-item-h-sizing shape))) + (dissoc :layout-item-h-sizing) + + (or (not (any-layout? shape)) + (= :fill (:layout-item-v-sizing shape))) + (dissoc :layout-item-v-sizing)))) (defn update-flex-scale [shape scale] diff --git a/frontend/playwright/data/workspace/get-file-7760.json b/frontend/playwright/data/workspace/get-file-7760.json new file mode 100644 index 000000000..ff33a7a94 --- /dev/null +++ b/frontend/playwright/data/workspace/get-file-7760.json @@ -0,0 +1,49 @@ +{ + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~: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 6", + "~:revn": 5, + "~:modified-at": "~m1718094617219", + "~:id": "~ucd90e028-326a-80b4-8004-7cdec16ffad5", + "~:is-shared": false, + "~:version": 48, + "~:project-id": "~u128636f9-5e78-812b-8004-350dd1a8869a", + "~:created-at": "~m1718094569923", + "~:data": { + "~:pages": [ + "~ucd90e028-326a-80b4-8004-7cdec16ffad6" + ], + "~:pages-index": { + "~ucd90e028-326a-80b4-8004-7cdec16ffad6": { + "~#penpot/pointer": [ + "~ucd90e028-326a-80b4-8004-7cdeefa23ece", + { + "~:created-at": "~m1718094617224" + } + ] + } + }, + "~:id": "~ucd90e028-326a-80b4-8004-7cdec16ffad5", + "~:options": { + "~:components-v2": true + } + } +} diff --git a/frontend/playwright/data/workspace/get-file-fragment-7760.json b/frontend/playwright/data/workspace/get-file-fragment-7760.json new file mode 100644 index 000000000..0c8011553 --- /dev/null +++ b/frontend/playwright/data/workspace/get-file-fragment-7760.json @@ -0,0 +1,383 @@ +{ + "~:id": "~ucd90e028-326a-80b4-8004-7cdeefa23ece", + "~:file-id": "~ucd90e028-326a-80b4-8004-7cdec16ffad5", + "~:created-at": "~m1718094617214", + "~:content": { + "~:options": {}, + "~:objects": { + "~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, + "~:y": 0 + } + }, + { + "~#point": { + "~:x": 0.01, + "~:y": 0 + } + }, + { + "~#point": { + "~:x": 0.01, + "~:y": 0.01 + } + }, + { + "~#point": { + "~:x": 0, + "~:y": 0.01 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.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, + "~:selrect": { + "~#rect": { + "~:x": 0, + "~:y": 0, + "~:width": 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, + "~:height": 0.01, + "~:flip-y": null, + "~:shapes": [ + "~u86087f92-9a17-8067-8004-7cdec45bee43", + "~u86087f92-9a17-8067-8004-7cded1cbe70e" + ] + } + }, + "~u86087f92-9a17-8067-8004-7cdec45bee43": { + "~#shape": { + "~:y": 341, + "~:hide-fill-on-export": false, + "~:layout-gap-type": "~:multiple", + "~:layout-padding": { + "~:p1": 34, + "~:p2": 36, + "~:p3": 34, + "~:p4": 36 + }, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:layout-wrap-type": "~:nowrap", + "~:grow-type": "~:fixed", + "~:layout": "~:flex", + "~:hide-in-viewer": false, + "~:name": "Flex Board", + "~:layout-align-items": "~:start", + "~:width": 176, + "~:layout-padding-type": "~:simple", + "~:type": "~:frame", + "~:points": [ + { + "~#point": { + "~:x": 217, + "~:y": 341 + } + }, + { + "~#point": { + "~:x": 393, + "~:y": 341 + } + }, + { + "~#point": { + "~:x": 393, + "~:y": 511 + } + }, + { + "~#point": { + "~:x": 217, + "~:y": 511 + } + } + ], + "~:layout-item-h-sizing": "~:auto", + "~:proportion-lock": false, + "~:layout-gap": { + "~:row-gap": 0, + "~:column-gap": 0 + }, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:layout-item-v-sizing": "~:auto", + "~:layout-justify-content": "~:start", + "~:id": "~u86087f92-9a17-8067-8004-7cdec45bee43", + "~:parent-id": "~u00000000-0000-0000-0000-000000000000", + "~:layout-flex-dir": "~:row", + "~:layout-align-content": "~:stretch", + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 217, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 217, + "~:y": 341, + "~:width": 176, + "~:height": 170, + "~:x1": 217, + "~:y1": 341, + "~:x2": 393, + "~:y2": 511 + } + }, + "~:fills": [ + { + "~:fill-color": "#FFFFFF", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:height": 170, + "~:flip-y": null, + "~:shapes": [ + "~u86087f92-9a17-8067-8004-7cdec98dfa7f" + ] + } + }, + "~u86087f92-9a17-8067-8004-7cdec98dfa7f": { + "~#shape": { + "~:y": 375, + "~:rx": 0, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Rectangle", + "~:width": 104, + "~:type": "~:rect", + "~:points": [ + { + "~#point": { + "~:x": 253, + "~:y": 375 + } + }, + { + "~#point": { + "~:x": 357, + "~:y": 375 + } + }, + { + "~#point": { + "~:x": 357, + "~:y": 477 + } + }, + { + "~#point": { + "~:x": 253, + "~:y": 477 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:id": "~u86087f92-9a17-8067-8004-7cdec98dfa7f", + "~:parent-id": "~u86087f92-9a17-8067-8004-7cdec45bee43", + "~:frame-id": "~u86087f92-9a17-8067-8004-7cdec45bee43", + "~:strokes": [], + "~:x": 253, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 253, + "~:y": 375, + "~:width": 104, + "~:height": 102, + "~:x1": 253, + "~:y1": 375, + "~:x2": 357, + "~:y2": 477 + } + }, + "~:fills": [ + { + "~:fill-color": "#B1B2B5", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:ry": 0, + "~:height": 102, + "~:flip-y": null + } + }, + "~u86087f92-9a17-8067-8004-7cded1cbe70e": { + "~#shape": { + "~:y": 300, + "~: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, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Container Board", + "~:width": 434, + "~:type": "~:frame", + "~:points": [ + { + "~#point": { + "~:x": 689, + "~:y": 300 + } + }, + { + "~#point": { + "~:x": 1123, + "~:y": 300 + } + }, + { + "~#point": { + "~:x": 1123, + "~:y": 741 + } + }, + { + "~#point": { + "~:x": 689, + "~:y": 741 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:id": "~u86087f92-9a17-8067-8004-7cded1cbe70e", + "~:parent-id": "~u00000000-0000-0000-0000-000000000000", + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 689, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 689, + "~:y": 300, + "~:width": 434, + "~:height": 441, + "~:x1": 689, + "~:y1": 300, + "~:x2": 1123, + "~:y2": 741 + } + }, + "~:fills": [ + { + "~:fill-color": "#FFFFFF", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:height": 441, + "~:flip-y": null, + "~:shapes": [] + } + } + }, + "~:id": "~ucd90e028-326a-80b4-8004-7cdec16ffad6", + "~:name": "Page 1" + } +} diff --git a/frontend/playwright/ui/pages/WorkspacePage.js b/frontend/playwright/ui/pages/WorkspacePage.js index 3baa3ece1..c8f0c49cc 100644 --- a/frontend/playwright/ui/pages/WorkspacePage.js +++ b/frontend/playwright/ui/pages/WorkspacePage.js @@ -50,9 +50,9 @@ export class WorkspacePage extends BaseWebSocketPage { this.layers = page.getByTestId("layer-tree"); this.palette = page.getByTestId("palette"); this.sidebar = page.getByTestId("left-sidebar"); - this.librariesModal = page.getByTestId("libraries-modal"); this.selectionRect = page.getByTestId("workspace-selection-rect"); this.horizontalScrollbar = page.getByTestId("horizontal-scrollbar"); + this.librariesModal = page.getByTestId("libraries-modal"); } async goToWorkspace({ fileId = WorkspacePage.anyFileId, pageId = WorkspacePage.anyPageId } = {}) { @@ -117,6 +117,13 @@ export class WorkspacePage extends BaseWebSocketPage { await pagesToggle.click(); } + async moveSelectionToShape(name) { + await this.page.locator('rect.viewport-selrect').hover(); + await this.page.mouse.down(); + await this.viewport.getByTestId(name).first().hover({ force: true }); + await this.page.mouse.up(); + } + async clickLeafLayer(name, clickOptions = {}) { const layer = this.layers.getByText(name); await layer.click(clickOptions); @@ -160,4 +167,10 @@ export class WorkspacePage extends BaseWebSocketPage { async clickColorPalette(clickOptions = {}) { await this.palette.getByRole("button", { name: "Color Palette (Alt+P)" }).click(clickOptions); } + + async clickColorPalette(clickOptions = {}) { + await this.palette + .getByRole("button", { name: "Color Palette (Alt+P)" }) + .click(clickOptions); + } } diff --git a/frontend/playwright/ui/specs/design-tab.spec.js b/frontend/playwright/ui/specs/design-tab.spec.js index 0c890c07e..7a43aa166 100644 --- a/frontend/playwright/ui/specs/design-tab.spec.js +++ b/frontend/playwright/ui/specs/design-tab.spec.js @@ -92,3 +92,35 @@ test.describe("Multiple shapes attributes", () => { await expect(workspace.page.getByTestId("add-blur")).toBeHidden(); }); }); + +test("BUG 7760 - Layout losing properties when changing parents", async ({ page }) => { + const workspacePage = new WorkspacePage(page); + await workspacePage.setupEmptyFile(); + await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-7760.json"); + await workspacePage.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-fragment-7760.json", + ); + await workspacePage.mockRPC("update-file?id=*", "workspace/update-file-create-rect.json"); + + await workspacePage.goToWorkspace({ + fileId: "cd90e028-326a-80b4-8004-7cdec16ffad5", + pageId: "cd90e028-326a-80b4-8004-7cdec16ffad6", + }); + + // Select the flex board and drag it into the other container board + await workspacePage.clickLeafLayer("Flex Board"); + + // Move the first board into the second + const hAuto = await workspacePage.page.getByTitle("Fit content (Horizontal)"); + const vAuto = await workspacePage.page.getByTitle("Fit content (Vertical)"); + + await expect(vAuto.locator("input")).toBeChecked(); + await expect(hAuto.locator("input")).toBeChecked(); + + await workspacePage.moveSelectionToShape("Container Board"); + + // The first board properties should still be auto width/height + await expect(vAuto.locator("input")).toBeChecked(); + await expect(hAuto.locator("input")).toBeChecked(); +}); diff --git a/frontend/playwright/ui/specs/workspace.spec.js b/frontend/playwright/ui/specs/workspace.spec.js index 2800163b2..3682ecf68 100644 --- a/frontend/playwright/ui/specs/workspace.spec.js +++ b/frontend/playwright/ui/specs/workspace.spec.js @@ -92,3 +92,31 @@ test("Bug 7525 - User moves a scrollbar and no selciont rectangle appears", asyn await expect(workspacePage.selectionRect).not.toBeInViewport(); }); + +test("User adds a library and its automatically selected in the color palette", async ({ page }) => { + const workspacePage = new WorkspacePage(page); + await workspacePage.setupEmptyFile(); + await workspacePage.mockRPC("link-file-to-library", "workspace/link-file-to-library.json"); + await workspacePage.mockRPC("unlink-file-from-library", "workspace/unlink-file-from-library.json"); + await workspacePage.mockRPC("get-team-shared-files?team-id=*", "workspace/get-team-shared-libraries-non-empty.json"); + + await workspacePage.goToWorkspace(); + + // Add Testing library 1 + await workspacePage.clickColorPalette(); + await workspacePage.clickAssets(); + // Now the get-file call should return a library + await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-library.json"); + await workspacePage.openLibrariesModal(); + await workspacePage.clickLibrary("Testing library 1") + await workspacePage.closeLibrariesModal(); + + await expect(workspacePage.palette.getByRole("button", { name: "test-color-187cd5" })).toBeVisible(); + + // Remove Testing library 1 + await workspacePage.openLibrariesModal(); + await workspacePage.clickLibrary("Testing library 1") + await workspacePage.closeLibrariesModal(); + + await expect(workspacePage.palette.getByText('There are no color styles in your library yet')).toBeVisible(); +}); diff --git a/frontend/src/app/main/ui/components/radio_buttons.cljs b/frontend/src/app/main/ui/components/radio_buttons.cljs index 0d7cce294..dbaffebb5 100644 --- a/frontend/src/app/main/ui/components/radio_buttons.cljs +++ b/frontend/src/app/main/ui/components/radio_buttons.cljs @@ -54,7 +54,7 @@ :name name :disabled disabled :value value - :checked checked?}]])) + :default-checked checked?}]])) (mf/defc radio-buttons {::mf/props :obj} diff --git a/frontend/src/app/main/ui/components/tab_container.cljs b/frontend/src/app/main/ui/components/tab_container.cljs index 20c79a417..1e3b99079 100644 --- a/frontend/src/app/main/ui/components/tab_container.cljs +++ b/frontend/src/app/main/ui/components/tab_container.cljs @@ -59,6 +59,7 @@ [:div {:key (str/concat "tab-" sid) :title tooltip :data-id sid + :data-testid sid :on-click on-click :class (stl/css-case :tab-container-tab-title true diff --git a/frontend/src/app/main/ui/shapes/shape.cljs b/frontend/src/app/main/ui/shapes/shape.cljs index e4082b89c..c344ec857 100644 --- a/frontend/src/app/main/ui/shapes/shape.cljs +++ b/frontend/src/app/main/ui/shapes/shape.cljs @@ -96,6 +96,7 @@ (obj/unset! "disable-shadows?") (obj/set! "ref" ref) (obj/set! "id" (dm/fmt "shape-%" shape-id)) + (obj/set! "data-testid" (:name shape)) ;; TODO: This is added for backward compatibility. (cond-> (and (cfh/text-shape? shape) (empty? (:position-data shape))) diff --git a/frontend/src/app/main/ui/workspace/libraries.cljs b/frontend/src/app/main/ui/workspace/libraries.cljs index 6d5813c0d..5e354e423 100644 --- a/frontend/src/app/main/ui/workspace/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/libraries.cljs @@ -519,7 +519,8 @@ [:div {:class (stl/css :modal-dialog)} [:button {:class (stl/css :close-btn) :on-click close-dialog - :aria-label (tr "labels.close")} + :aria-label (tr "labels.close") + :data-testid "close-libraries"} close-icon] [:div {:class (stl/css :modal-title)} (tr "workspace.libraries.libraries")] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs index 5092d025b..681e879f3 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs @@ -250,7 +250,7 @@ [:& radio-button {:value "auto" :icon i/hug-content - :title "Fit content" + :title "Fit content (Horizontal)" :id "behaviour-h-auto"}])]]) (mf/defc element-behaviour-vertical @@ -288,7 +288,7 @@ {:value "auto" :icon i/hug-content :icon-class (stl/css :rotated) - :title "Fit content" + :title "Fit content (Vertical)" :id "behaviour-v-auto"}])]]) (mf/defc align-self-row