🐛 Fix export presets not preserved in view mode inspect (#9972)

* 🐛 Fix export presets not preserved in view mode inspect

* 🐛 Changes after review
This commit is contained in:
Alonso Torres 2026-06-11 13:33:48 +02:00 committed by GitHub
parent 2d843da2cd
commit 5b6041624a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 97 additions and 20 deletions

View File

@ -0,0 +1,49 @@
import { test, expect } from "@playwright/test";
import { ViewerPage } from "../pages/ViewerPage";
test.beforeEach(async ({ page }) => {
await ViewerPage.init(page);
});
const multipleBoardsFileId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb0";
const multipleBoardsPageId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb3";
test("[View mode] Export presets are preserved when navigating between boards in inspect mode", async ({
page,
}) => {
const viewer = new ViewerPage(page);
await viewer.setupLoggedInUser();
await viewer.setupFileWithMultipleBoards();
await viewer.goToViewer({
fileId: multipleBoardsFileId,
pageId: multipleBoardsPageId,
});
// Enter inspect (code) mode
await viewer.showCode();
// Wait for the inspect panel to load
await page.waitForSelector(".main_ui_inspect_exports__add-export");
// Add an export preset via the "+" button in the Export section
const addExportButton = page.locator(".main_ui_inspect_exports__add-export");
await addExportButton.click();
// Verify the "Export 1 element" button appears, confirming the preset was added
const exportButton = page.getByRole("button", { name: "Export 1 element" });
await expect(exportButton).toBeVisible();
// Navigate to another board
const nextButton = page.getByRole("button", { name: "Next" });
await nextButton.click();
await expect(page).toHaveURL(/&index=1/);
// Navigate back to the first board
const prevButton = page.locator(".main_ui_viewer__viewer-go-prev");
await prevButton.click();
await expect(page).toHaveURL(/&index=0/);
// Export preset should still be visible after returning to the first board
await expect(exportButton).toBeVisible();
});

View File

@ -583,6 +583,13 @@
(update [_ state]
(d/dissoc-in state [:viewer-local :nav-scroll]))))
(defn update-exports-cache
[shapes-key exports]
(ptk/reify ::update-exports-cache
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:inspect-exports-cache shapes-key] exports))))
(defn complete-animation
[]
(ptk/reify ::complete-animation

View File

@ -9,6 +9,7 @@
(:require
[app.common.data :as d]
[app.main.data.exports.assets :as de]
[app.main.data.viewer :as dv]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.select :refer [select]]
@ -17,8 +18,12 @@
[app.util.dom :as dom]
[app.util.i18n :refer [tr c]]
[app.util.keyboard :as kbd]
[okulary.core :as l]
[rumext.v2 :as mf]))
(def ^:private exports-cache-ref
(l/derived :inspect-exports-cache st/state))
(mf/defc exports
{::mf/wrap [#(mf/memo % =)]}
[{:keys [shapes page-id file-id share-id type] :as props}]
@ -65,46 +70,58 @@
(de/request-export {:exports exports})
(de/export-shapes-event exports "viewer")))))
shapes-key
(mf/use-memo (mf/deps shapes) #(vec (sort (map :id shapes))))
add-export
(mf/use-callback
(mf/deps shapes)
(mf/deps shapes exports)
(fn []
(let [xspec {:type :png
:suffix ""
:scale 1}]
(swap! exports conj xspec))))
:scale 1}
new-exports (conj @exports xspec)]
(reset! exports new-exports)
(st/emit! (dv/update-exports-cache shapes-key new-exports)))))
delete-export
(mf/use-callback
(mf/deps shapes)
(mf/deps shapes exports)
(fn [index]
(swap! exports (fn [exports]
(let [[before after] (split-at index exports)]
(d/concat-vec before (rest after)))))))
(let [new-exports (let [[before after] (split-at index @exports)]
(d/concat-vec before (rest after)))]
(reset! exports new-exports)
(st/emit! (dv/update-exports-cache shapes-key new-exports)))))
on-scale-change
(mf/use-callback
(mf/deps shapes)
(mf/deps shapes exports)
(fn [index event]
(let [scale (d/parse-double event)]
(swap! exports assoc-in [index :scale] scale))))
(let [scale (d/parse-double event)
new-exports (assoc-in @exports [index :scale] scale)]
(reset! exports new-exports)
(st/emit! (dv/update-exports-cache shapes-key new-exports)))))
on-suffix-change
(mf/use-callback
(mf/deps shapes)
(mf/deps shapes exports)
(fn [event]
(let [value (dom/get-target-val event)
index (-> (dom/get-current-target event)
(dom/get-data "value")
(d/parse-integer))]
(swap! exports assoc-in [index :suffix] value))))
(d/parse-integer))
new-exports (assoc-in @exports [index :suffix] value)]
(reset! exports new-exports)
(st/emit! (dv/update-exports-cache shapes-key new-exports)))))
on-type-change
(mf/use-callback
(mf/deps shapes)
(mf/deps shapes exports)
(fn [index event]
(let [type (keyword event)]
(swap! exports assoc-in [index :type] type))))
(let [type (keyword event)
new-exports (assoc-in @exports [index :type] type)]
(reset! exports new-exports)
(st/emit! (dv/update-exports-cache shapes-key new-exports)))))
manage-key-down
(mf/use-callback
@ -130,10 +147,14 @@
(mf/use-effect
(mf/deps shapes)
(fn []
(reset! exports (-> (mapv #(:exports % []) shapes)
flatten
distinct
vec))))
(let [shapes-key (vec (sort (map :id shapes)))
cached (get @exports-cache-ref shapes-key)]
(if (some? cached)
(reset! exports cached)
(reset! exports (->> shapes
(mapcat #(:exports % []))
(distinct)
vec))))))
[:div {:class (stl/css :element-set)}
[:div {:class (stl/css :element-title)}
[:> title-bar* {:collapsable false