Skip to content

Commit 983a9d1

Browse files
committed
[FIX] html_editor: update font size selector on set tag in toolbar
In website builder: 1. Select text 2. Change heading type in the toolbar -> the font size selector in the toolbar is blank. This is because the font size input is in an iframe, and a chain of events triggered by changing the heading type (`setTag`) leads to the iframe's document to be re-rendered. This only happens in the website builder because of a call to `updateContainers` as a step_added handler that triggers the `change_current_options_containers_listeners`. These listeners are triggered because `setTag` replaced the block which was the target of the `BuilderOptionsPlugin` with another. The target is not connected anymore so it was assumed we would always need to reset the containers, but the target didn't really change, it was simply replaced to change its tag. So this allows going through the checks to see if updating the containers is needed before triggering the listeners. Since in this case it isn't, they won't be and the iframe will remain intact.
1 parent 8478118 commit 983a9d1

File tree

2 files changed

+61
-5
lines changed

2 files changed

+61
-5
lines changed

addons/html_builder/static/src/core/builder_options_plugin.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,16 @@ export class BuilderOptionsPlugin extends Plugin {
115115
if (!this.target || !this.target.isConnected) {
116116
this.lastContainers = this.lastContainers.filter((c) => c.element.isConnected);
117117
this.target = this.lastContainers.at(-1)?.element;
118-
this.dependencies.history.setStepExtra("optionSelection", this.target);
119-
this.dispatchTo("change_current_options_containers_listeners", this.lastContainers);
120-
return;
121118
}
122119

123120
const newContainers = this.computeContainers(this.target);
124121
// Do not update the containers if they did not change or not forced to update.
125-
if (newContainers.length === this.lastContainers.length && !force) {
122+
if (
123+
this.target &&
124+
this.target.isConnected &&
125+
newContainers.length === this.lastContainers.length &&
126+
!force
127+
) {
126128
const previousIds = this.lastContainers.map((c) => c.id);
127129
const newIds = newContainers.map((c) => c.id);
128130
const areSameElements = newIds.every((id, i) => id === previousIds[i]);

addons/website/static/tests/builder/editor.test.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { expect, test, describe } from "@odoo/hoot";
33
import { animationFrame } from "@odoo/hoot-mock";
44
import { contains, patchWithCleanup } from "@web/../tests/web_test_helpers";
55
import { defineWebsiteModels, setupWebsiteBuilder } from "./website_helpers";
6-
import { click, manuallyDispatchProgrammaticEvent, waitFor } from "@odoo/hoot-dom";
6+
import { click, manuallyDispatchProgrammaticEvent, waitFor, queryOne } from "@odoo/hoot-dom";
77
import { isTextNode } from "@html_editor/utils/dom_info";
88
import { parseHTML } from "@html_editor/utils/html";
99
import { setSelection } from "@html_editor/../tests/_helpers/selection";
@@ -81,6 +81,60 @@ test("should set contenteditable to false on .o_not_editable elements", async ()
8181
expect(snippet).toHaveAttribute("contenteditable", "false");
8282
});
8383

84+
test("should preserve iframe in the toolbar's font size input", async () => {
85+
const { getEditor } = await setupWebsiteBuilder(`
86+
<section class="s_text_block pt40 pb40 o_colored_level" data-snippet="s_text_block" data-name="Text">
87+
<div class="container s_allow_columns">
88+
<p>Some text.</p>
89+
<p>Some more text.</p>
90+
</div>
91+
</section>
92+
`);
93+
const editor = getEditor();
94+
const p = editor.editable.querySelector("p")
95+
const p2 = p.nextElementSibling;
96+
// Activate the text block snippet.
97+
click(p);
98+
99+
// Select the word "more".
100+
editor.shared.selection.setSelection({
101+
anchorNode: p2.firstChild,
102+
anchorOffset: 5,
103+
focusNode: p2.firstChild,
104+
focusOffset: 9,
105+
});
106+
await waitFor(".o-we-toolbar");
107+
// Get the font size selector input.
108+
let iframeEl = queryOne(".o-we-toolbar [name='font_size_selector'] iframe");
109+
let inputEl = iframeEl.contentWindow.document?.querySelector("input");
110+
// Change the font style from paragraph to paragraph.
111+
await contains(".o-we-toolbar .btn[name='font'].dropdown-toggle").click();
112+
await waitFor(".btn[name='font'].dropdown-toggle.show");
113+
await contains(".dropdown-menu [name='p']").click();
114+
iframeEl = queryOne(".o-we-toolbar [name='font_size_selector'] iframe");
115+
let newInputEl = iframeEl.contentWindow.document?.querySelector("input");
116+
expect(newInputEl).toBe(inputEl); // The input shouldn't have been changed.
117+
118+
// Select the first word "text".
119+
editor.shared.selection.setSelection({
120+
anchorNode: p.firstChild,
121+
anchorOffset: 5,
122+
focusNode: p.firstChild,
123+
focusOffset: 9,
124+
});
125+
await waitFor(".o-we-toolbar");
126+
// Get the font size selector input.
127+
iframeEl = queryOne(".o-we-toolbar [name='font_size_selector'] iframe");
128+
inputEl = iframeEl.contentWindow.document?.querySelector("input");
129+
// Change the font style from paragraph to header 1.
130+
await contains(".o-we-toolbar .btn[name='font'].dropdown-toggle").click();
131+
await waitFor(".btn[name='font'].dropdown-toggle.show");
132+
await contains(".dropdown-menu [name='h2']").click();
133+
iframeEl = queryOne(".o-we-toolbar [name='font_size_selector'] iframe");
134+
newInputEl = iframeEl.contentWindow.document?.querySelector("input");
135+
expect(newInputEl).toBe(inputEl); // The input shouldn't have been changed.
136+
});
137+
84138
describe("toolbar dropdowns", () => {
85139
const setup = async () => {
86140
const { getEditor } = await setupWebsiteBuilder(`<p>abc</p>`);

0 commit comments

Comments
 (0)