[FIX] web_editor, website: fix onFocus/onBlur flow + add test

The flow has been broken multiple times across the years. Let's add a
test in all stable versions.

For the 14.0 forward-port, the test came with a fix which restores the
13.0 flow which we tried to change in 14.0... but only ended up broking
it. Indeed, with [1], the 13.0 was fixed. And, with [2], we tried to
simplify the flow... but the explanation given by the commit is just
wrong: the flow was just broken and the simplification that was to be
done in fact practically never occurs. The more the reason to add a
test, proving how the flow is working. The simplification may be done
someday but not sure it is really worth it.

For the 15.0 forward-port, another fix was needed because of [3] which
introduced a bug which makes the onBlur method be called multiple times
when hiding a whole editor hierarchy.

[1]: https://github.com/odoo/odoo/commit/076992bdf099f3a645c9a1ef6df2cdcccbf1c2b2
[2]: https://github.com/odoo/odoo/commit/5c007305c9998ade32c6677cac5552101c3a96af
[3]: https://github.com/odoo/odoo/commit/806a8db35b5e0e6a461422f5bba7c97180c3ef29

closes odoo/odoo#79734

X-original-commit: 0acc5e784b15d9c963660da3781763448503f33e
Signed-off-by: Romain Derie (rde) <rde@odoo.com>
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
qsm-odoo
2021-11-14 21:40:48 +00:00
parent 113038e1b1
commit 1ebc7c301b
4 changed files with 166 additions and 4 deletions
@@ -456,6 +456,10 @@ var SnippetEditor = Widget.extend({
// and would thus produce a deadlock. Also, this awaited
// 'activate_snippet' call would allow to remove the 'toggleOverlay' and
// 'toggleOptions' calls at the start of this function.
// TODO also to be checked: this not being awaited, the DOM is removed
// first, destroying the related editors and not calling onBlur... to
// check if this has always been like this or not and this should be
// unit tested.
const parent = this.$target[0].parentElement;
const nextSibling = this.$target[0].nextElementSibling;
const previousSibling = this.$target[0].previousElementSibling;
@@ -568,7 +572,7 @@ var SnippetEditor = Widget.extend({
}
if (this.areOptionsShown() === show) {
return this._customize$Elements;
return null;
}
// All onFocus before all ui updates as the onFocus of an option might
@@ -1785,7 +1789,8 @@ var SnippetsMenu = Widget.extend({
}
resolve(null);
}).then(async editorToEnable => {
if (ifInactiveOptions && this._enabledEditorHierarchy.includes(editorToEnable)) {
if (!previewMode && this._enabledEditorHierarchy[0] === editorToEnable
|| ifInactiveOptions && this._enabledEditorHierarchy.includes(editorToEnable)) {
return editorToEnable;
}
@@ -1802,8 +1807,14 @@ var SnippetsMenu = Widget.extend({
for (let i = this.snippetEditors.length; i--;) {
const editor = this.snippetEditors[i];
editor.toggleOverlay(false, previewMode);
if (!previewMode && !this._enabledEditorHierarchy.includes(editor)) {
await editor.toggleOptions(false);
if (!previewMode) {
const wasShown = !!await editor.toggleOptions(false);
if (wasShown) {
this._updateRightPanelContent({
content: [],
tab: this.tabs.BLOCKS,
});
}
}
}
// ... then enable the right editor or look if some have been
@@ -0,0 +1,19 @@
odoo.define('website.tour_focus_blur_snippets_options', function (require) {
'use strict';
const options = require('web_editor.snippets.options');
const FocusBlur = options.Class.extend({
onFocus() {
window.focusBlurSnippetsResult.push(`focus ${this.focusBlurName}`);
},
onBlur() {
window.focusBlurSnippetsResult.push(`blur ${this.focusBlurName}`);
},
});
options.registry.FocusBlurParent = FocusBlur.extend({focusBlurName: 'parent'});
options.registry.FocusBlurChild1 = FocusBlur.extend({focusBlurName: 'child1'});
options.registry.FocusBlurChild2 = FocusBlur.extend({focusBlurName: 'child2'});
});
@@ -0,0 +1,81 @@
odoo.define('website.tour.focus_blur_snippets', function (require) {
'use strict';
const ajax = require('web.ajax');
const tour = require('web_tour.tour');
const blockIDToData = {
parent: {
selector: '.s_focusblur',
name: 'section',
overlayIndex: 2,
},
child1: {
selector: '.s_focusblur_child1',
name: 'first child',
overlayIndex: 1,
},
child2: {
selector: '.s_focusblur_child2',
name: 'second child',
overlayIndex: 0,
},
};
function clickAndCheck(blockID, expected) {
const blockData = blockIDToData[blockID] || {};
return [{
content: blockID ? `Enable the ${blockData.name}` : 'Disable all blocks',
trigger: blockData.selector || '#wrapwrap',
}, {
content: 'Once the related overlays are enabled/disabled, check that the focus/blur calls have been correct.',
trigger: blockID
? `.oe_overlay.ui-draggable:eq(${blockData.overlayIndex}).oe_active`
: `#oe_manipulators:not(:has(.oe_active))`,
run: function (actions) {
const result = window.focusBlurSnippetsResult;
window.focusBlurSnippetsResult = [];
if (expected.length !== result.length
|| !expected.every((item, i) => item === result[i])) {
console.error(`
Expected: ${expected.toString()}
Result: ${result.toString()}
`);
}
},
}];
}
window.focusBlurSnippetsResult = [];
tour.register('focus_blur_snippets', {
url: '/?enable_editor=1',
}, [
{
content: 'First load our custom JS options',
trigger: 'body',
run: function () {
ajax.loadJS('/website/static/tests/tour_utils/focus_blur_snippets_options.js').then(function () {
$('body').addClass('focus_blur_snippets_options_loaded');
});
},
},
{
content: 'Drag the custom block into the page',
trigger: '#snippet_structure .oe_snippet:has(.oe_snippet_body.s_focusblur) .oe_snippet_thumbnail',
extra_trigger: 'body.focus_blur_snippets_options_loaded',
run: 'drag_and_drop #wrap',
},
...clickAndCheck('parent', ['focus parent']),
...clickAndCheck(null, ['blur parent']),
...clickAndCheck('child1', ['focus parent', 'focus child1']),
...clickAndCheck('child1', []),
...clickAndCheck(null, ['blur parent', 'blur child1']),
...clickAndCheck('parent', ['focus parent']),
...clickAndCheck('child1', ['blur parent', 'focus parent', 'focus child1']),
...clickAndCheck('child2', ['blur parent', 'blur child1', 'focus parent', 'focus child2']),
...clickAndCheck('parent', ['blur parent', 'blur child2', 'focus parent']),
]);
});
+51
View File
@@ -221,3 +221,54 @@ class TestUi(odoo.tests.HttpCase):
lang = self.env['res.lang']._activate_lang('nl_NL')
self.env['website'].browse(1).write({'language_ids': [(4, lang.id, 0)]})
self.start_tour("/nl/contactus", 'edit_translated_page_redirect', login='admin')
def test_13_editor_focus_blur_unit_test(self):
# TODO this should definitely not be a website python tour test but
# while waiting for a proper web_editor qunit JS test suite for the
# editor, it is better than no test at all as this was broken multiple
# times already.
self.env["ir.ui.view"].create([{
'name': 's_focusblur',
'key': 'website.s_focusblur',
'type': 'qweb',
'arch': """
<section class="s_focusblur bg-success py-5">
<div class="container">
<div class="row">
<div class="col-lg-6 s_focusblur_child1 bg-warning py-5"></div>
<div class="col-lg-6 s_focusblur_child2 bg-danger py-5"></div>
</div>
</div>
</section>
""",
}, {
'name': 's_focusblur_snippets',
'mode': 'extension',
'inherit_id': self.env.ref('website.snippets').id,
'key': 'website.s_focusblur_snippets',
'type': 'qweb',
'arch': """
<data>
<xpath expr="//*[@id='snippet_structure']//t[@t-snippet]" position="before">
<t t-snippet="website.s_focusblur"/>
</xpath>
</data>
""",
}, {
'name': 's_focusblur_options',
'mode': 'extension',
'inherit_id': self.env.ref('web_editor.snippet_options').id,
'key': 'website.s_focusblur_options',
'type': 'qweb',
'arch': """
<data>
<xpath expr=".">
<div data-js="FocusBlurParent" data-selector=".s_focusblur"/>
<div data-js="FocusBlurChild1" data-selector=".s_focusblur_child1"/>
<div data-js="FocusBlurChild2" data-selector=".s_focusblur_child2"/>
</xpath>
</data>
""",
}])
self.start_tour("/?enable_editor=1", "focus_blur_snippets", login="admin")