Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions addons/html_editor/static/src/main/media/image_crop.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
onWillDestroy,
markup,
useExternalListener,
status,
} from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
import { scrollTo, closestScrollableY } from "@web/core/utils/scrolling";
Expand Down Expand Up @@ -42,6 +43,7 @@ export class ImageCrop extends Component {
this.elRef = useRef("el");
this.cropperWrapper = useRef("cropperWrapper");
this.imageRef = useRef("imageRef");
this.cropperOpen = false;

// We use capture so that the handler is called before other editor handlers
// like save, such that we can restore the src before a save.
Expand All @@ -64,6 +66,9 @@ export class ImageCrop extends Component {
}

closeCropper() {
if (!this.cropperOpen) {
return;
}
this.cropper?.destroy?.();
this.media.setAttribute("src", this.initialSrc);
if (
Expand All @@ -73,6 +78,7 @@ export class ImageCrop extends Component {
this.media.classList.add("o_modified_image_to_save");
}
this.props?.onClose?.();
this.cropperOpen = false;
}

/**
Expand All @@ -90,6 +96,9 @@ export class ImageCrop extends Component {
}

async show() {
if (this.cropperOpen) {
return;
}
// key: ratio identifier, label: displayed to user, value: used by cropper lib
const src = this.media.getAttribute("src");
const data = { ...this.media.dataset };
Expand Down Expand Up @@ -134,6 +143,11 @@ export class ImageCrop extends Component {
await this.scrollToInvisibleImage();
// Replacing the src with the original's so that the layout is correct.
await loadImage(this.originalSrc, this.media);
if (status(this) !== "mounted") {
// Abort if the component has been destroyed in the meantime
// since `this.imageRef.el` is `null` when it is not mounted.
return;
}
const cropperImage = this.imageRef.el;
[cropperImage.style.width, cropperImage.style.height] = [
this.media.width + "px",
Expand Down Expand Up @@ -175,6 +189,7 @@ export class ImageCrop extends Component {
this.aspectRatios[this.aspectRatio].value,
this.media.dataset
);
this.cropperOpen = true;
}
/**
* Updates the DOM image with cropped data and associates required
Expand Down
53 changes: 52 additions & 1 deletion addons/html_editor/static/tests/toolbar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { withSequence } from "@html_editor/utils/resource";
import { describe, expect, test } from "@odoo/hoot";
import {
click,
delay,
getActiveElement,
keyDown,
keyUp,
Expand All @@ -16,7 +17,12 @@ import {
waitForNone,
} from "@odoo/hoot-dom";
import { advanceTime, animationFrame, tick } from "@odoo/hoot-mock";
import { contains, patchTranslations, patchWithCleanup } from "@web/../tests/web_test_helpers";
import {
contains,
onRpc,
patchTranslations,
patchWithCleanup,
} from "@web/../tests/web_test_helpers";
import { fontItems, fontSizeItems } from "../src/main/font/font_plugin";
import { Plugin } from "../src/plugin";
import { MAIN_PLUGINS } from "../src/plugin_sets";
Expand Down Expand Up @@ -762,6 +768,51 @@ test("close the toolbar if the selection contains any nodes (traverseNode = [],
expect(".o-we-toolbar").toHaveCount(0);
});

test.tags("desktop");
test("should not close image cropper while loading media", async () => {
onRpc("/html_editor/get_image_info", () => ({
original: {
image_src: "#",
},
}));
onRpc("/web/image/__odoo__unknown__src__/", async () => {
await delay(50);
return {};
});

await setupEditor(`<p>[<img src="#">]</p>`);
await waitFor(".o-we-toolbar");

await click('div[name="image_transform"] > .btn');
await animationFrame();

await click('.btn[name="image_crop"]');
await animationFrame();

await waitFor('.btn[title="Discard"]', { timeout: 1000 });
await click('.btn[title="Discard"]');
await animationFrame();

// cropper should not close as the cropper still loading the image.
expect('.btn[title="Discard"]').toHaveCount(1);

// once the image loaded we should be able to close
await waitFor('img[src^="blob:"]', { timeout: 2000 });
await click('.btn[title="Discard"]');
await waitForNone('.btn[title="Discard"]', { timeout: 1000 });

await click("img");
await waitFor(".o-we-toolbar", { timeout: 1000 });

await click('div[name="image_transform"] > .btn');
await animationFrame();

await waitFor('.btn[name="image_crop"]', { timeout: 1000 });
await click('.btn[name="image_crop"]');
await waitFor('.btn[title="Discard"]', { timeout: 1000 });
expect('.btn[title="Discard"]').toHaveCount(1);
});

describe.tags("desktop");
describe("toolbar open and close on user interaction", () => {
describe("mouse", () => {
Expand Down