[REF] web: adaptations to use simple tap to add an event on mobile

Before this commit, the only way to add an event was using a long tap.

After this commit, a short tap will be added.

So to summarize on Mobile there is two methods to add an event using the
calendar: short tap and long tap.

For month/year views when a user tap on a "Free Zone" (no event)
the view is changed in the "day view" for this clicked day.

Task ID: 1891957

closes odoo/odoo#64615

Related: odoo/enterprise#15785
Signed-off-by: Adrien Dieudonné (adr) <adr@odoo.com>
This commit is contained in:
Odoo's Mergebot
2021-01-28 16:27:19 +01:00
committed by GitHub
5 changed files with 127 additions and 20 deletions
@@ -236,6 +236,9 @@ var CalendarController = AbstractController.extend({
// When clicking on a random day of a random other week, switch to week view
this.model.setScale('week');
}
if (event.data.scale) {
this.model.setScale(event.data.scale);
}
this.model.setDate(event.data.date);
this.reload();
},
@@ -338,6 +338,7 @@ return AbstractRenderer.extend({
r_end: fc4Event.extendedProps.r_end && moment(fc4Event.extendedProps.r_end).utcOffset(0, true),
record: fc4Event.extendedProps.record,
attendees: fc4Event.extendedProps.attendees,
disableQuickCreate: fc4Event.extendedProps.disableQuickCreate,
});
}
}
@@ -375,6 +376,20 @@ return AbstractRenderer.extend({
return field_utils.format[field.type](record[fieldName], field, {forceString: true});
}
},
_preOpenCreate: function (data) {
if (this.$('.o_cw_popover').length) {
this._unselectEvent();
}
if (this.state.context.default_name) {
data.title = this.state.context.default_name;
}
this.trigger_up('openCreate', this._convertEventToFC3Event(data));
if (this.state.scale === 'year') {
this.calendar.view.unselect();
} else {
this.calendar.unselect();
}
},
/**
* Return the Object options for FullCalendar
*
@@ -405,7 +420,7 @@ return AbstractRenderer.extend({
eventClickInfo.jsEvent.stopPropagation();
var eventData = eventClickInfo.event;
self._unselectEvent();
$(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', eventData.id)).addClass('o_cw_custom_highlight');
$(self.calendarElement).find(`[data-event-id=${eventData.id}]`).addClass('o_cw_custom_highlight');
self._renderEventPopover(eventData, $(eventClickInfo.el));
},
yearDateClick: function (info) {
@@ -427,20 +442,8 @@ return AbstractRenderer.extend({
}
},
select: function (selectionInfo) {
// Clicking on the view, dispose any visible popover. Otherwise create a new event.
if (self.$('.o_cw_popover').length) {
self._unselectEvent();
}
var data = {start: selectionInfo.start, end: selectionInfo.end, allDay: selectionInfo.allDay};
if (self.state.context.default_name) {
data.title = self.state.context.default_name;
}
self.trigger_up('openCreate', self._convertEventToFC3Event(data));
if (self.state.scale === 'year') {
self.calendar.view.unselect();
} else {
self.calendar.unselect();
}
self._preOpenCreate(data);
},
eventRender: function (info) {
var event = info.event;
@@ -503,20 +506,20 @@ return AbstractRenderer.extend({
// The css ":hover" selector can't be used because these events
// are rendered using multiple elements.
eventMouseEnter: function (mouseEnterInfo) {
$(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseEnterInfo.event.id)).addClass('o_cw_custom_hover');
$(self.calendarElement).find(`[data-event-id=${mouseEnterInfo.event.id}]`).addClass('o_cw_custom_hover');
},
eventMouseLeave: function (mouseLeaveInfo) {
if (!mouseLeaveInfo.event.id) {
return;
}
$(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseLeaveInfo.event.id)).removeClass('o_cw_custom_hover');
$(self.calendarElement).find(`[data-event-id=${mouseLeaveInfo.event.id}]`).removeClass('o_cw_custom_hover');
},
eventDragStart: function (mouseDragInfo) {
$(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseDragInfo.event.id)).addClass('o_cw_custom_hover');
$(self.calendarElement).find(`[data-event-id=${mouseDragInfo.event.id}]`).addClass('o_cw_custom_hover');
self._unselectEvent();
},
eventResizeStart: function (mouseResizeInfo) {
$(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseResizeInfo.event.id)).addClass('o_cw_custom_hover');
$(self.calendarElement).find(`[data-event-id=${mouseResizeInfo.event.id}]`).addClass('o_cw_custom_hover');
self._unselectEvent();
},
eventLimitClick: function () {
@@ -709,7 +712,7 @@ return AbstractRenderer.extend({
if (options.avatar_field) {
_.each(options.filters, function (filter) {
if (!['all', false].includes(filter.value)) {
var selector = _.str.sprintf('.o_calendar_filter_item[data-value=%s]', filter.value);
var selector = `.o_calendar_filter_item[data-value=${filter.value}]`;
sidebarFilter.$el.find(selector).popover({
animation: false,
trigger: 'hover',
@@ -719,7 +722,7 @@ return AbstractRenderer.extend({
delay: {show: 300, hide: 0},
content: function () {
return $('<img>', {
src: _.str.sprintf('/web/image/%s/%s/%s', options.avatar_model, filter.value, options.avatar_field),
src: `/web/image/${options.avatar_model}/${filter.value}/${options.avatar_field}}`,
class: 'mx-auto',
});
},
@@ -197,6 +197,7 @@ odoo.define('web.test_utils', async function (require) {
triggerKeypressEvent: testUtilsDom.triggerKeypressEvent,
triggerMouseEvent: testUtilsDom.triggerMouseEvent,
triggerPositionalMouseEvent: testUtilsDom.triggerPositionalMouseEvent,
triggerPositionalTapEvents: testUtilsDom.triggerPositionalTapEvents,
dragAndDrop: testUtilsDom.dragAndDrop,
find: testUtilsDom.findItem,
getNode: testUtilsDom.getNode,
@@ -33,6 +33,17 @@ odoo.define('web.test_utils_dom', function (require) {
clientY: args ? args.pageY : undefined,
view: window,
});
const touchEventMapping = args => Object.assign({}, args, {
cancelable: true,
bubbles: true,
composed: true,
view: window,
rotation: 0.0,
zoom: 1.0,
});
const touchEventCancelMapping = args => Object.assign({}, touchEventMapping(args), {
cancelable: false,
});
const noBubble = args => Object.assign({}, args, { bubbles: false });
const onlyBubble = args => Object.assign({}, args, { bubbles: true });
// TriggerEvent constructor/args processor mapping
@@ -70,6 +81,11 @@ odoo.define('web.test_utils_dom', function (require) {
dragover: { constructor: DragEvent, processParameters: onlyBubble },
drop: { constructor: DragEvent, processParameters: onlyBubble },
touchstart: { constructor: TouchEvent, processParameters: touchEventMapping },
touchend: { constructor: TouchEvent, processParameters: touchEventMapping },
touchmove: { constructor: TouchEvent, processParameters: touchEventMapping },
touchcancel: { constructor: TouchEvent, processParameters: touchEventCancelMapping },
input: { constructor: InputEvent, processParameters: onlyBubble },
compositionstart: { constructor: CompositionEvent, processParameters: onlyBubble },
@@ -533,6 +549,39 @@ odoo.define('web.test_utils_dom', function (require) {
return el;
}
/**
* Simulate a "TAP" (touch) event with a custom position x and y.
*
* @param {number} x
* @param {number} y
* @returns {HTMLElement}
*/
async function triggerPositionalTapEvents(x, y) {
const element = document.elementFromPoint(x, y);
const touch = new Touch({
identifier: 0,
target: element,
clientX: x,
clientY: y,
pageX: x,
pageY: y,
});
await triggerEvent(element, 'touchstart', {
touches: [touch],
targetTouches: [touch],
changedTouches: [touch],
});
await triggerEvent(element, 'touchmove', {
touches: [touch],
targetTouches: [touch],
changedTouches: [touch],
});
await triggerEvent(element, 'touchend', {
changedTouches: [touch],
});
return element;
}
return {
click,
clickFirst,
@@ -547,5 +596,6 @@ odoo.define('web.test_utils_dom', function (require) {
triggerKeypressEvent,
triggerMouseEvent,
triggerPositionalMouseEvent,
triggerPositionalTapEvents,
};
});
@@ -3938,6 +3938,56 @@ QUnit.module('Views', {
calendar.destroy();
});
QUnit.test('calendar: disableQuickCreate in data event', async function (assert) {
assert.expect(3);
testUtils.mock.patch(CalendarRenderer, {
_preOpenCreate: function (data) {
data = Object.assign({}, data, {
extendedProps: {disableQuickCreate: true},
});
return this._super.call(this, data);
},
});
let calendar = await testUtils.createCalendarView({
View: CalendarView,
model: 'event',
data: this.data,
arch: `<calendar mode="day" date_start="start" date_stop="stop"></calendar>`,
viewOptions: {
initialDate: initialDate,
},
intercepts: {
do_action(ev) {
assert.step('do_action');
assert.deepEqual(ev.data.action.context, {
default_start: "2016-12-12 06:30:00",
default_stop: "2016-12-12 11:00:00",
}, "should send the correct data to create events");
},
},
}, {positionalClicks: true});
// Create event
var $initCell = calendar.$('.fc-time-grid .fc-minor[data-time="06:30:00"] .fc-widget-content:last-child');
var $endCell = calendar.$('.fc-time-grid .fc-minor[data-time="10:30:00"] .fc-widget-content:last-child');
var left = $initCell.offset().left;
var top = $initCell.offset().top;
testUtils.dom.triggerPositionalMouseEvent(left, top, "mousedown");
top = $endCell.offset().top;
testUtils.dom.triggerPositionalMouseEvent(left, top, "mousemove");
testUtils.dom.triggerPositionalMouseEvent(left, top, "mouseup");
await testUtils.nextTick();
assert.verifySteps(['do_action']);
calendar.destroy();
testUtils.mock.unpatch(CalendarRenderer);
});
});
});