[REF] *: BS5: migrate popovers/Tooltip
* data('bs.popover') is not a jQuery data anymore
-> fetch data from the instance instead.
* use the new class name 'popover-arrow' instead of 'arrow'
* website_forum: offset is not a number anymore
-> Convert to string instead.
* BS5 event listener: 'focus' -> 'focusin'
* use `mouseover` event to show and `mouseout` to hide.
* disable at some point the animation to avoid remaining listener
in the DOM.
Ref:
https://getbootstrap.com/docs/5.1/migration/#popovers
Task ID: 2766483
Part-of: odoo/odoo#95450
This commit is contained in:
@@ -39,7 +39,7 @@ QUnit.module('ModelFieldSelector', {
|
||||
var $popoverButton = form.$('a.fa.fa-info-circle.text-danger');
|
||||
assert.strictEqual($popoverButton.length, 1, "Should have a popover icon/button in red");
|
||||
assert.strictEqual($popoverButton.prop('special_click'), true, "Special click properpy should be activated");
|
||||
await testUtils.dom.triggerEvents($popoverButton, ['focus']);
|
||||
await testUtils.dom.triggerEvent($popoverButton, 'focusin');
|
||||
$popover = $('div.popover');
|
||||
assert.strictEqual($popover.length, 1, "Should have a popover container in DOM");
|
||||
assert.strictEqual($popover.html().includes("var that = self // why not?"), true, "The message should be in DOM");
|
||||
|
||||
@@ -13,7 +13,17 @@ export class Popover extends Component {
|
||||
}
|
||||
onPositioned(el, { direction, variant }) {
|
||||
const position = `${direction[0]}${variant[0]}`;
|
||||
el.classList.add(`bs-popover-${direction}`, `o-popover--${position}`);
|
||||
const directionMap = {
|
||||
top: "top",
|
||||
bottom: "bottom",
|
||||
left: "start",
|
||||
right: "end",
|
||||
};
|
||||
el.classList.add(
|
||||
`bs-popover-${directionMap[direction]}`,
|
||||
`o-popover-${direction}`,
|
||||
`o-popover--${position}`
|
||||
);
|
||||
const arrowEl = el.firstElementChild;
|
||||
switch (position) {
|
||||
case "tm": // top-middle
|
||||
|
||||
@@ -35,19 +35,35 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.o-popover--ts .arrow, &.o-popover--bs .arrow {
|
||||
&.o-popover-top, &.o-popover-auto[x-placement^="top"] {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
&.o-popover-right, &.o-popover-auto[x-placement^="right"] {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
&.o-popover-bottom, &.o-popover-auto[x-placement^="bottom"] {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
&.o-popover-left, &.o-popover-auto[x-placement^="left"] {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
&.o-popover--ts .popover-arrow, &.o-popover--bs .popover-arrow {
|
||||
left: $popover-border-radius;
|
||||
}
|
||||
|
||||
&.o-popover--te .arrow, &.o-popover--be .arrow {
|
||||
&.o-popover--te .popover-arrow, &.o-popover--be .popover-arrow {
|
||||
right: $popover-border-radius;
|
||||
}
|
||||
|
||||
&.o-popover--ls .arrow, &.o-popover--rs .arrow {
|
||||
&.o-popover--ls .popover-arrow, &.o-popover--rs .popover-arrow {
|
||||
top: $popover-border-radius;
|
||||
}
|
||||
|
||||
&.o-popover--le .arrow, &.o-popover--re .arrow {
|
||||
&.o-popover--le .popover-arrow, &.o-popover--re .popover-arrow {
|
||||
bottom: $popover-border-radius;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
t-att-class="props.popoverClass"
|
||||
t-ref="popper"
|
||||
>
|
||||
<div class="arrow"/>
|
||||
<div class="popover-arrow"/>
|
||||
<t t-slot="default" />
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -664,7 +664,7 @@ return AbstractRenderer.extend({
|
||||
* @private
|
||||
*/
|
||||
_onPopoverShown: function ($popoverElement, calendarPopover) {
|
||||
var $popover = $($popoverElement.data('bs.popover').tip);
|
||||
const $popover = $(Popover.getInstance($popoverElement[0]).tip);
|
||||
$popover.find('.o_cw_popover_close').on('click', this._unselectEvent.bind(this));
|
||||
$popover.find('.o_cw_body').replaceWith(calendarPopover.$el);
|
||||
},
|
||||
|
||||
@@ -43,7 +43,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.arrow {
|
||||
.tooltip-arrow {
|
||||
&::before, &::after {
|
||||
border: $arrow-size solid transparent;
|
||||
}
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@@ -51,64 +54,54 @@
|
||||
}
|
||||
|
||||
&.bs-tooltip-bottom {
|
||||
margin-top: $tooltip-arrow-height;
|
||||
.arrow {
|
||||
.tooltip-arrow {
|
||||
top: $tooltip-arrow-height;
|
||||
&::before {
|
||||
border: $arrow-size solid transparent;
|
||||
border-bottom-color: $o-tooltip-color;
|
||||
bottom: 0;
|
||||
}
|
||||
&::after {
|
||||
bottom: -1px;
|
||||
border: $arrow-size solid transparent;
|
||||
border-bottom-color: $o-tooltip-title-background-color;
|
||||
bottom: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.bs-tooltip-top {
|
||||
margin-bottom: $tooltip-arrow-height;
|
||||
.arrow {
|
||||
.tooltip-arrow {
|
||||
bottom: $tooltip-arrow-height;
|
||||
&::before {
|
||||
border: $arrow-size solid transparent;
|
||||
border-top-color: $o-tooltip-color;
|
||||
top: 0;
|
||||
}
|
||||
&::after {
|
||||
border: $arrow-size solid transparent;
|
||||
border-top-color: $o-tooltip-arrow-color;
|
||||
margin-top: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.bs-tooltip-left {
|
||||
margin-right: $tooltip-arrow-height;
|
||||
.arrow {
|
||||
&.bs-tooltip-start {
|
||||
.tooltip-arrow {
|
||||
right: $tooltip-arrow-height;
|
||||
&::before {
|
||||
border: $arrow-size solid transparent;
|
||||
border-left-color: $o-tooltip-color;
|
||||
left: 0;
|
||||
}
|
||||
&::after {
|
||||
border: $arrow-size solid transparent;
|
||||
border-left-color: $o-tooltip-arrow-color;
|
||||
margin-left: -1px;
|
||||
left: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.bs-tooltip-right {
|
||||
margin-left: $tooltip-arrow-height;
|
||||
.arrow {
|
||||
&.bs-tooltip-end {
|
||||
.tooltip-arrow {
|
||||
left: $tooltip-arrow-height;
|
||||
&::before,
|
||||
&::after {
|
||||
border: $arrow-size solid transparent;
|
||||
&::before {
|
||||
border-right-color: $o-tooltip-color;
|
||||
right: 0;
|
||||
}
|
||||
&::after {
|
||||
border: $arrow-size solid transparent;
|
||||
border-right-color: $o-tooltip-arrow-color;
|
||||
margin-right: -1px;
|
||||
right: 0px;
|
||||
right: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<t t-name="CalendarView.event.popover.placeholder">
|
||||
<div t-attf-class="o_cw_popover popover card shadow #{typeof color === 'number' ? _.str.sprintf('o_calendar_color_%s', color) : ''}" role="tooltip">
|
||||
<div class="arrow"/>
|
||||
<div class="popover-arrow"/>
|
||||
<div class="card-header d-flex justify-content-between py-2 pr-2">
|
||||
<h4 class="p-0 pt-1">
|
||||
<span class="popover-header border-0"/><span t-if="displayLock" class="ml-1"><i class="fa fa-lock"/></span>
|
||||
@@ -170,7 +170,7 @@
|
||||
|
||||
<t t-name="CalendarView.yearEvent.popover.placeholder">
|
||||
<div class="popover o_cw_popover" style="position: relative">
|
||||
<div class="arrow"></div>
|
||||
<div class="popover-arrow"></div>
|
||||
<div class="popover-header"></div>
|
||||
<div style="position: absolute; top: 0; right: 0.5rem;">
|
||||
<span class="o_cw_popover_close"><i class="fa fa-close small"></i></span>
|
||||
|
||||
@@ -2135,7 +2135,7 @@ QUnit.module('Legacy relational_fields', {
|
||||
assert.strictEqual(list.$('.o_data_row:eq(3) .o_field_many2manytags .o_m2m_avatar_empty').text().trim(), "+9",
|
||||
"should have +9 in o_m2m_avatar_empty");
|
||||
|
||||
list.$('.o_data_row:eq(1) .o_field_many2manytags .o_m2m_avatar_empty').trigger($.Event('mouseenter'));
|
||||
list.$('.o_data_row:eq(1) .o_field_many2manytags .o_m2m_avatar_empty')[0].dispatchEvent(new Event('mouseover'));
|
||||
await testUtils.nextTick();
|
||||
assert.containsOnce(list, '.popover',
|
||||
"should open a popover hover on o_m2m_avatar_empty");
|
||||
@@ -2241,7 +2241,7 @@ QUnit.module('Legacy relational_fields', {
|
||||
assert.strictEqual(kanban.$('.o_kanban_record:eq(3) .o_field_many2manytags .o_m2m_avatar_empty').text().trim(), "9+",
|
||||
"should have 9+ in o_m2m_avatar_empty");
|
||||
|
||||
kanban.$('.o_kanban_record:eq(2) .o_field_many2manytags .o_m2m_avatar_empty').trigger($.Event('mouseenter'));
|
||||
kanban.$('.o_kanban_record:eq(2) .o_field_many2manytags .o_m2m_avatar_empty')[0].dispatchEvent(new Event('mouseover'));
|
||||
await testUtils.nextTick();
|
||||
assert.containsOnce(kanban, '.popover',
|
||||
"should open a popover hover on o_m2m_avatar_empty");
|
||||
|
||||
@@ -41,6 +41,9 @@ let serverData;
|
||||
let target;
|
||||
QUnit.module('LegacyViews', {
|
||||
beforeEach: function () {
|
||||
// Avoid animation to not have to wait until the tooltip is removed
|
||||
this.initialTooltipDefaultAnimation = Tooltip.Default.animation;
|
||||
Tooltip.Default.animation = false;
|
||||
target = getFixture();
|
||||
|
||||
registry.category("services").add("scroller", scrollerService);
|
||||
@@ -188,6 +191,9 @@ QUnit.module('LegacyViews', {
|
||||
models: this.data,
|
||||
};
|
||||
},
|
||||
afterEach: async function () {
|
||||
Tooltip.Default.animation = this.initialTooltipDefaultAnimation;
|
||||
},
|
||||
}, function () {
|
||||
|
||||
QUnit.module('FormView (legacy)');
|
||||
@@ -1597,27 +1603,27 @@ QUnit.module('LegacyViews', {
|
||||
|
||||
const $fooLabel1 = form.$('.o_form_label:nth(0)');
|
||||
$fooLabel1.tooltip('show', false);
|
||||
$fooLabel1.trigger($.Event('mouseenter'));
|
||||
$fooLabel1[0].dispatchEvent(new Event('mouseover'));
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_help').text().trim(), "foo tooltip");
|
||||
$fooLabel1.trigger($.Event('mouseleave'));
|
||||
$fooLabel1[0].dispatchEvent(new Event('mouseout'));
|
||||
|
||||
const $fooLabel2 = form.$('.o_form_label:nth(2)');
|
||||
$fooLabel2.tooltip('show', false);
|
||||
$fooLabel2.trigger($.Event('mouseenter'));
|
||||
$fooLabel2[0].dispatchEvent(new Event('mouseover'));
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_help').text().trim(), "foo tooltip");
|
||||
$fooLabel2.trigger($.Event('mouseleave'));
|
||||
$fooLabel2[0].dispatchEvent(new Event('mouseout'));
|
||||
|
||||
const $barLabel1 = form.$('.o_form_label:nth(1)');
|
||||
$barLabel1.tooltip('show', false);
|
||||
$barLabel1.trigger($.Event('mouseenter'));
|
||||
$barLabel1[0].dispatchEvent(new Event('mouseover'));
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_help').text().trim(), "bar tooltip");
|
||||
$barLabel1.trigger($.Event('mouseleave'));
|
||||
$barLabel1[0].dispatchEvent(new Event('mouseout'));
|
||||
|
||||
const $barLabel2 = form.$('.o_form_label:nth(3)');
|
||||
$barLabel2.tooltip('show', false);
|
||||
$barLabel2.trigger($.Event('mouseenter'));
|
||||
$barLabel2[0].dispatchEvent(new Event('mouseover'));
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_help').text().trim(), "bar tooltip");
|
||||
$barLabel2.trigger($.Event('mouseleave'));
|
||||
$barLabel2[0].dispatchEvent(new Event('mouseout'));
|
||||
|
||||
odoo.debug = initialDebugMode;
|
||||
form.destroy();
|
||||
@@ -8209,6 +8215,7 @@ QUnit.module('LegacyViews', {
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_string').length, 1,
|
||||
"should have rendered a tooltip");
|
||||
$secondButton.trigger($.Event('mouseleave'));
|
||||
$secondButton.tooltip('hide');
|
||||
|
||||
odoo.debug = initialDebugMode;
|
||||
form.destroy();
|
||||
@@ -8599,6 +8606,7 @@ QUnit.module('LegacyViews', {
|
||||
assert.strictEqual($('.oe_tooltip_technical>li[data-item="widget"]')[0].lastChild.wholeText.trim(),
|
||||
'Many2one (many2one)', "widget description should be correct");
|
||||
|
||||
$field.tooltip('hide');
|
||||
odoo.debug = initialDebugMode;
|
||||
form.destroy();
|
||||
});
|
||||
@@ -9874,7 +9882,7 @@ QUnit.module('LegacyViews', {
|
||||
form.$buttons.find('.o_form_buttons_edit').tooltip('show',false);
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_string').length, 1,
|
||||
"should have rendered a tooltip");
|
||||
await testUtils.nextTick();
|
||||
form.$buttons.find('.o_form_buttons_edit').tooltip('hide');
|
||||
form.destroy();
|
||||
});
|
||||
QUnit.test('if the focus is on the save button, hitting ENTER should save', async function (assert) {
|
||||
@@ -10160,17 +10168,17 @@ QUnit.module('LegacyViews', {
|
||||
|
||||
const $productLabel = form.$('.o_form_label:eq(1)');
|
||||
$productLabel.tooltip('show', false);
|
||||
await testUtils.dom.triggerMouseEvent($productLabel, 'mouseenter');
|
||||
await testUtils.dom.triggerMouseEvent($productLabel, 'mouseover');
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_help').text().trim(),
|
||||
"this is a tooltip\n\nValues set here are company-specific.");
|
||||
await testUtils.dom.triggerMouseEvent($productLabel, 'mouseleave');
|
||||
await testUtils.dom.triggerMouseEvent($productLabel, 'mouseout');
|
||||
|
||||
const $fooLabel = form.$('.o_form_label:first');
|
||||
$fooLabel.tooltip('show', false);
|
||||
await testUtils.dom.triggerMouseEvent($fooLabel, 'mouseenter');
|
||||
await testUtils.dom.triggerMouseEvent($fooLabel, 'mouseover');
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_help').text().trim(),
|
||||
"Values set here are company-specific.");
|
||||
await testUtils.dom.triggerMouseEvent($fooLabel, 'mouseleave');
|
||||
await testUtils.dom.triggerMouseEvent($fooLabel, 'mouseout');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
@@ -10199,9 +10207,9 @@ QUnit.module('LegacyViews', {
|
||||
const $productLabel = form.$('.o_form_label');
|
||||
|
||||
$productLabel.tooltip('show', false);
|
||||
await testUtils.dom.triggerMouseEvent($productLabel, 'mouseenter');
|
||||
await testUtils.dom.triggerMouseEvent($productLabel, 'mouseover');
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_help').text().trim(), "this is a tooltip");
|
||||
await testUtils.dom.triggerMouseEvent($productLabel, 'mouseleave');
|
||||
await testUtils.dom.triggerMouseEvent($productLabel, 'mouseout');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
@@ -40,6 +40,9 @@ let serverData;
|
||||
let target;
|
||||
QUnit.module('LegacyViews', {
|
||||
beforeEach: function () {
|
||||
// Avoid animation to not have to wait until the tooltip is removed
|
||||
this.initialTooltipDefaultAnimation = Tooltip.Default.animation;
|
||||
Tooltip.Default.animation = false;
|
||||
registry.category("views").remove("list"); // remove new list from registry
|
||||
registry.category("views").remove("kanban"); // remove new kanban from registry
|
||||
registry.category("views").remove("form"); // remove new form from registry
|
||||
@@ -149,7 +152,10 @@ QUnit.module('LegacyViews', {
|
||||
|
||||
serverData = { models: this.data };
|
||||
target = getFixture();
|
||||
}
|
||||
},
|
||||
afterEach: async function () {
|
||||
Tooltip.Default.animation = this.initialTooltipDefaultAnimation;
|
||||
},
|
||||
}, function () {
|
||||
|
||||
QUnit.module('ListView (legacy)');
|
||||
@@ -4430,24 +4436,25 @@ QUnit.module('LegacyViews', {
|
||||
// 1000 ms. not totally academic, but a short test suite is easier to sell :(
|
||||
list.$('th[data-name=foo]').tooltip('show', false);
|
||||
|
||||
list.$('th[data-name=foo]').trigger($.Event('mouseenter'));
|
||||
list.$('th[data-name=foo]')[0].dispatchEvent(new Event('mouseover'));
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_string').length, 0, "should not have rendered a tooltip");
|
||||
|
||||
odoo.debug = true;
|
||||
// it is necessary to rerender the list so tooltips can be properly created
|
||||
await list.reload();
|
||||
list.$('th[data-name=foo]').tooltip('show', false);
|
||||
list.$('th[data-name=foo]').trigger($.Event('mouseenter'));
|
||||
list.$('th[data-name=foo]')[0].dispatchEvent(new Event('mouseover'));
|
||||
assert.strictEqual($('.tooltip .oe_tooltip_string').length, 1, "should have rendered a tooltip");
|
||||
|
||||
await list.reload();
|
||||
list.$('th[data-name=bar]').tooltip('show', false);
|
||||
list.$('th[data-name=bar]').trigger($.Event('mouseenter'));
|
||||
list.$('th[data-name=bar]')[0].dispatchEvent(new Event('mouseover'));
|
||||
assert.containsOnce($, '.oe_tooltip_technical>li[data-item="widget"]',
|
||||
'widget should be present for this field');
|
||||
assert.strictEqual($('.oe_tooltip_technical>li[data-item="widget"]')[0].lastChild.wholeText.trim(),
|
||||
'Button (toggle_button)', "widget description should be correct");
|
||||
|
||||
list.$('th[data-name=bar]')[0].dispatchEvent(new Event('mouseout'));
|
||||
odoo.debug = initialDebugMode;
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
@@ -685,12 +685,18 @@ QUnit.module("ActionManager", (hooks) => {
|
||||
assert.containsN(target, ".o_form_buttons_view button:not([disabled])", 2);
|
||||
const actionButton = target.querySelector("button[name=object]");
|
||||
const tooltipProm = new Promise((resolve) => {
|
||||
$(document.body).one("shown.bs.tooltip", () => {
|
||||
$(actionButton).mouseleave();
|
||||
resolve();
|
||||
});
|
||||
document.body.addEventListener(
|
||||
"shown.bs.tooltip",
|
||||
() => {
|
||||
actionButton.dispatchEvent(new Event("mouseout"));
|
||||
resolve();
|
||||
},
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
$(actionButton).mouseenter();
|
||||
actionButton.dispatchEvent(new Event("mouseover"));
|
||||
await tooltipProm;
|
||||
assert.containsOnce(document.body, ".tooltip");
|
||||
await click(actionButton);
|
||||
|
||||
@@ -155,10 +155,11 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({
|
||||
_.each(this.$('.o_wforum_bio_popover'), authorBox => {
|
||||
$(authorBox).popover({
|
||||
trigger: 'hover',
|
||||
offset: 10,
|
||||
offset: '10',
|
||||
animation: false,
|
||||
html: true,
|
||||
}).popover('hide').data('bs.popover').tip.classList.add('o_wforum_bio_popover_container');
|
||||
customClass: 'o_wforum_bio_popover_container',
|
||||
});
|
||||
});
|
||||
|
||||
this.$('#post_reply').on('shown.bs.collapse', function (e) {
|
||||
|
||||
Reference in New Issue
Block a user