diff --git a/mt-static/plugins/MTBlockEditor/src/Block/MTImage-AssetUploader.test.ts b/mt-static/plugins/MTBlockEditor/src/Block/MTImage-AssetUploader.test.ts new file mode 100644 index 0000000..9310a2b --- /dev/null +++ b/mt-static/plugins/MTBlockEditor/src/Block/MTImage-AssetUploader.test.ts @@ -0,0 +1,161 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + addEditUpdateBlock: vi.fn(), + assetUploaderOpen: vi.fn(), + editor: {}, +})); + +vi.mock("mt-block-editor-block/React", () => ({ + default: window.MTBlockEditor.React, + useRef: (initialValue) => ({ current: initialValue }), + useState: (initialValue) => [initialValue, vi.fn()], +})); + +vi.mock("mt-block-editor-block/decorator", () => ({ + blockProperty: (component) => component, +})); + +vi.mock("mt-block-editor-block/Context", () => ({ + useEditorContext: () => ({ editor: mocks.editor }), +})); + +vi.mock("mt-block-editor-block/Hook", () => ({ + useCommands: vi.fn(), +})); + +vi.mock("./edit", () => ({ + addEditUpdateBlock: mocks.addEditUpdateBlock, +})); + +import MTImage from "./MTImage"; + +interface AssetUploaderOptions { + initialSelectedData: Record[]; + insert: (selectedData: Record[]) => void; +} + +const openAssetUploader = (block: MTImage): AssetUploaderOptions => { + block.showModal = true; + + const element = block.editor({ focus: true }); + const Editor = element.type as (props: Record) => unknown; + Editor(element.props); + + expect(mocks.assetUploaderOpen).toHaveBeenCalledOnce(); + return mocks.assetUploaderOpen.mock.calls[0][0]; +}; + +describe("MTImage - AssetUploader", () => { + beforeEach(() => { + mocks.addEditUpdateBlock.mockReset(); + mocks.assetUploaderOpen.mockReset(); + window.trans = (message) => message; + window.MT = { + AssetUploader: { + open: mocks.assetUploaderOpen, + }, + } as unknown as Window["MT"]; + }); + + it.each([ + ["the original asset", "https://example.com/image.jpg", true], + ["an external URL", "https://example.com/page.html", false], + ])("passes whether the image links to %s", (_description, linkUrl, expected) => { + const block = new MTImage({ + assetId: "123", + assetUrl: "https://example.com/image.jpg", + linkUrl, + imageWidth: "640", + alternativeText: "Alternative text", + caption: "Caption", + alignment: "center", + }); + + const options = openAssetUploader(block); + + expect(options.initialSelectedData).toEqual([ + { + id: "123", + imageWidth: "640", + alternativeText: "Alternative text", + caption: "Caption", + align: "center", + linkToOriginal: expected, + }, + ]); + }); + + it("sets the link URL to the selected original asset", () => { + const block = new MTImage({ + assetUrl: "https://example.com/old.jpg", + linkUrl: "https://example.com/page.html", + }); + const options = openAssetUploader(block); + + options.insert([ + { + assetId: "456", + assetUrl: "https://example.com/new.jpg", + assetThumbnailUrl: "https://example.com/new-thumbnail.jpg", + assetThumbnailWidth: "320", + assetThumbnailHeight: "240", + align: "none", + alternativeText: "New alternative text", + caption: "New caption", + linkToOriginal: true, + }, + ]); + + expect(mocks.addEditUpdateBlock).toHaveBeenCalledWith( + mocks.editor, + block, + expect.objectContaining({ + assetUrl: "https://example.com/new.jpg", + linkUrl: "https://example.com/new.jpg", + }) + ); + expect(block.linkUrl).toBe("https://example.com/new.jpg"); + }); + + it("clears a previous link to the original asset when it is unchecked", () => { + const block = new MTImage({ + assetUrl: "https://example.com/old.jpg", + linkUrl: "https://example.com/old.jpg", + }); + const options = openAssetUploader(block); + + options.insert([ + { + assetUrl: "https://example.com/new.jpg", + linkToOriginal: false, + }, + ]); + + expect(mocks.addEditUpdateBlock).toHaveBeenCalledWith( + mocks.editor, + block, + expect.objectContaining({ linkUrl: "" }) + ); + expect(block.linkUrl).toBe(""); + }); + + it("preserves an external link when link to original is unchecked", () => { + const block = new MTImage({ + assetUrl: "https://example.com/old.jpg", + linkUrl: "https://example.com/page.html", + }); + const options = openAssetUploader(block); + + options.insert([ + { + assetUrl: "https://example.com/new.jpg", + linkToOriginal: false, + }, + ]); + + const newData = mocks.addEditUpdateBlock.mock.calls[0][2]; + expect(newData).not.toHaveProperty("linkUrl"); + expect(block.linkUrl).toBe("https://example.com/page.html"); + }); +}); diff --git a/mt-static/plugins/MTBlockEditor/src/Block/MTImage.tsx b/mt-static/plugins/MTBlockEditor/src/Block/MTImage.tsx index e5026f1..6e1dd8d 100644 --- a/mt-static/plugins/MTBlockEditor/src/Block/MTImage.tsx +++ b/mt-static/plugins/MTBlockEditor/src/Block/MTImage.tsx @@ -93,6 +93,7 @@ const Editor: React.FC = blockProperty(({ focus, block }) => { alternativeText: block.alternativeText, caption: block.caption, align: block.alignment, + linkToOriginal: block.assetUrl === block.linkUrl, }, ], insert: ([data]) => { @@ -106,7 +107,13 @@ const Editor: React.FC = blockProperty(({ focus, block }) => { alternativeText: data.alternativeText, caption: data.caption, hasCaption: (data.caption || "") !== "", - }; + } as Partial; + if (data.linkToOriginal) { + newData.linkUrl = data.assetUrl; + } else if (block.linkUrl === block.assetUrl) { + // If the link was previously set to the original asset, but the user has unchecked "Link to original", clear the link URL. + newData.linkUrl = ""; + } addEditUpdateBlock(editor, block, newData); diff --git a/vite.config.ts b/vite.config.ts index 60140c1..083d133 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,7 +10,7 @@ export default defineConfig(() => { environment: "jsdom", setupFiles: [ "./test/setup.ts", - "./mt-static/plugins/MTBlockEditor/dist/mt-block-editor/1.1.56/mt-block-editor.js", + "./mt-static/plugins/MTBlockEditor/dist/mt-block-editor/1.2.8/mt-block-editor.js", ], }, resolve: {