diff --git a/addons/calendar/static/src/xml/base_calendar.xml b/addons/calendar/static/src/xml/base_calendar.xml index 6c8a3c0a332..2630988340c 100644 --- a/addons/calendar/static/src/xml/base_calendar.xml +++ b/addons/calendar/static/src/xml/base_calendar.xml @@ -69,7 +69,7 @@ -
+
diff --git a/addons/web/static/src/legacy/js/views/calendar/calendar_controller.js b/addons/web/static/src/legacy/js/views/calendar/calendar_controller.js index 9f9f348f263..1bd80d1e07f 100644 --- a/addons/web/static/src/legacy/js/views/calendar/calendar_controller.js +++ b/addons/web/static/src/legacy/js/views/calendar/calendar_controller.js @@ -248,9 +248,12 @@ var CalendarController = AbstractController.extend({ * @param {OdooEvent} event */ _onChangeFilter: async function (event) { - if (event.data.value !== "all" && event.target.filter_field) { - const domain = [['user_id', '=', this.getSession().uid], [event.target.write_field, '=', parseInt(event.data.value)]]; - const contact = await this._rpc({ + if (event.data.value !== 'all' && event.target.filter_field) { + const domain = [['user_id', '=', this.getSession().uid]]; + if (event.data.value !== 'check_all') { + domain.push([event.target.write_field, '=', parseInt(event.data.value)]); + } + const existingFilter = await this._rpc({ model: event.target.write_model, method: 'search', args: [domain], @@ -261,7 +264,7 @@ var CalendarController = AbstractController.extend({ await this._rpc({ model: event.target.write_model, method: 'write', - args: [contact, val], + args: [existingFilter, val], }); } if (this.model.changeFilter(event.data) && !event.data.no_reload) { @@ -275,34 +278,32 @@ var CalendarController = AbstractController.extend({ _onDeleteRecord: async function (event) { var self = this; if (event.data.event.record.recurrency) { - const recurrenceUpdate = await this._askRecurrenceUpdatePolicy(); - event.data = _.extend({}, event.data, { - 'recurrenceUpdate': recurrenceUpdate, - }); - if (recurrenceUpdate === 'self_only') { - self.model.deleteRecords([event.data.id], self.modelName).then(function () { - self.reload(); - }); - } else { - return this._rpc({ - model: self.modelName, - method: 'action_mass_deletion', - args: [[event.data.id], recurrenceUpdate], - }).then( function () { - self.reload(); - }); - } - } else { - Dialog.confirm(this, _t("Are you sure you want to delete this record ?"), { - confirm_callback: function () { + const recurrenceUpdate = await this._askRecurrenceUpdatePolicy(); + event.data = Object.assign({}, event.data, { + 'recurrenceUpdate': recurrenceUpdate, + }); + if (recurrenceUpdate === 'self_only') { self.model.deleteRecords([event.data.id], self.modelName).then(function () { + self.reload(); + }); + } else { + return this._rpc({ + model: self.modelName, + method: 'action_mass_deletion', + args: [[event.data.id], recurrenceUpdate], + }).then( function () { self.reload(); }); } - }); + } else { + Dialog.confirm(this, _t("Are you sure you want to delete this record ?"), { + confirm_callback: function () { + self.model.deleteRecords([event.data.id], self.modelName).then(function () { + self.reload(); + }); + } + }); } - - }, /** * @private @@ -517,14 +518,12 @@ var CalendarController = AbstractController.extend({ */ _onAttendeeStatus: async function(event) { const self = this; - let recurrenceUpdate; + let recurrenceUpdate = false; if (event.data.record.recurrency) { recurrenceUpdate = await this._askRecurrenceUpdatePolicy(); - event.data = _.extend({}, event.data, { + event.data = Object.assign({}, event.data, { 'recurrenceUpdate': recurrenceUpdate, }); - } else { - recurrenceUpdate = false; } return this._rpc({ model: self.modelName, diff --git a/addons/web/static/src/legacy/js/views/calendar/calendar_model.js b/addons/web/static/src/legacy/js/views/calendar/calendar_model.js index 34e504d2fc3..a87de95e481 100644 --- a/addons/web/static/src/legacy/js/views/calendar/calendar_model.js +++ b/addons/web/static/src/legacy/js/views/calendar/calendar_model.js @@ -24,6 +24,7 @@ return AbstractModel.extend({ // calendar uses index 0 for Sunday but Odoo stores it as 7 this.week_start = week_start !== undefined && week_start !== false ? week_start % 7 : moment().startOf('week').day(); this.week_stop = this.week_start + 6; + this.filter_check_all = {}; }, //-------------------------------------------------------------------------- @@ -115,13 +116,30 @@ return AbstractModel.extend({ if (filter.value === 'all') { Filter.all = filter.active; } + else if (filter.value === 'check_all') { + // Select all numeric filters and update the checkboxes + this.filter_check_all[filter.fieldName] = filter.active; + // Deactivate the All selection + Filter.all = false; + if (filter.active) { + // Check every displayed filters except the 'all' filter + Filter.filters.filter(el => el.value !== 'all').forEach(element => element.active = true ); + return true; + + } else { + Filter.filters.forEach(element => element.active = false); + return true; + } + } var f = _.find(Filter.filters, function (f) { return f.value === filter.value; }); if (f) { if (f.active !== filter.active) { + // Update the value according to the checkbox state f.active = filter.active; - } else { + } else if (filter.active) { + // if the filter is NOT active, we remove it by returning true [triggered by _onFilterRemove] return false; } } else if (filter.active) { @@ -150,8 +168,6 @@ return AbstractModel.extend({ }); }, /** - * @todo I think this is dead code - * * @param {any} ids * @param {any} model * @returns @@ -384,6 +400,8 @@ return AbstractModel.extend({ _.each(this.data.filters, function (filter) { // Skip 'all' filters because they do not affect the domain if (filter.all) return; + // all visible filters have been activated + if (filter.visible) return; // Loop over subfilters to complete authorizedValues _.each(filter.filters, function (f) { @@ -518,18 +536,9 @@ return AbstractModel.extend({ _loadColors: function (element, events) { if (this.fieldColor) { var fieldName = this.fieldColor; - var self = this; _.each(events, function (event) { var value = event.record[fieldName]; - var filter = self.loadParams.filters[fieldName]; - var color = filter && _.map(filter.filters, f => f.value) || value; - var everyoneFilter = filter && (_.find(filter.filters, f => f.value === "all") || {}).active || false; - if (!everyoneFilter) { - color = [event.attendee_id || value] - } else { - color = value.includes(self.getSession().partner_id) ? self.getSession().partner_id : value - } - event.color_index = _.isArray(color) ? self._getColorIndex(value, color[0]) : color; + event.color_index = _.isArray(value) ? value[0] : value; }); this.model_color = this.fields[fieldName].relation || element.model; } @@ -539,7 +548,7 @@ return AbstractModel.extend({ * Get the color index used to render the event and the filter. * Since we have a maximum of 30 colors, we have to adapt the color * index in case the id exceeds the number of colors available. - * + * * - actual_color_list contains the list of color already used in the filter * - color_index is the color we try to apply for the event * @@ -550,7 +559,7 @@ return AbstractModel.extend({ */ _getColorIndex: function(actual_color_list, color_index) { var actual_color_list = actual_color_list || [0]; - var new_color_index = color_index % 30; + var new_color_index = color_index % 50; if (new_color_index !== color_index && actual_color_list.includes(new_color_index)) { new_color_index = this._getColorIndex(actual_color_list, new_color_index + 3); } diff --git a/addons/web/static/src/legacy/js/views/calendar/calendar_popover.js b/addons/web/static/src/legacy/js/views/calendar/calendar_popover.js index f7e2fa7a53f..adec67eea64 100644 --- a/addons/web/static/src/legacy/js/views/calendar/calendar_popover.js +++ b/addons/web/static/src/legacy/js/views/calendar/calendar_popover.js @@ -205,9 +205,26 @@ var CalendarPopover = Widget.extend(WidgetAdapterMixin, StandaloneFieldManagerMi const fieldModifier = record.evalModifiers(_.pick(fieldWidget.attrs.modifiers, 'invisible')); fieldClass += fieldModifier.invisible ? ' o_invisible_modifier' : ''; } + if (fieldWidget.attrs) { + if (fieldWidget.attrs.options && fieldWidget.attrs.options.icon) { + field.icon = fieldWidget.attrs.options.icon; + } + if (fieldWidget.attrs.class) { + fieldClass += ' ' + fieldWidget.attrs.class; + } + if (fieldWidget.attrs.modifiers) { + const fieldModifier = record.evalModifiers(fieldWidget.attrs.modifiers); + fieldClass += fieldModifier.invisible ? ' o_invisible_modifier' : ''; + } + } var $field = $('
  • ', {class: fieldClass}); - var $fieldLabel = $('', {class: 'mr-2', text: _.str.sprintf('%s : ', field.string)}); + let $fieldLabel; + if (field.icon) { + $fieldLabel = $('', {class: 'mr-2', html: _.str.sprintf("", field.icon)}); + } else { + $fieldLabel = $('', {class: 'mr-2', text: _.str.sprintf('%s : ', field.string)}); + } $fieldLabel.appendTo($field); var $fieldContainer = $('
    ', {class: 'flex-grow-1'}); $fieldContainer.appendTo($field); diff --git a/addons/web/static/src/legacy/js/views/calendar/calendar_quick_create.js b/addons/web/static/src/legacy/js/views/calendar/calendar_quick_create.js index 503b483340f..fba58a5e08d 100644 --- a/addons/web/static/src/legacy/js/views/calendar/calendar_quick_create.js +++ b/addons/web/static/src/legacy/js/views/calendar/calendar_quick_create.js @@ -40,6 +40,8 @@ var QuickCreate = Dialog.extend({ var self = this; this._super(parent, { title: options.title, + // Close the dialog when clicking outside of it + backdrop: true, size: 'small', buttons: this._buttons ? [ {text: _t("Create"), classes: 'btn-primary', click: function () { diff --git a/addons/web/static/src/legacy/js/views/calendar/calendar_renderer.js b/addons/web/static/src/legacy/js/views/calendar/calendar_renderer.js index d733ae00b82..325563b2b6d 100644 --- a/addons/web/static/src/legacy/js/views/calendar/calendar_renderer.js +++ b/addons/web/static/src/legacy/js/views/calendar/calendar_renderer.js @@ -53,6 +53,7 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, { this.filters = options.filters; this.label = options.label; this.getColor = options.getColor; + this.filter_check_all = options.check_all; }, /** * @override @@ -95,6 +96,7 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, { } this.$el.on('click', '.o_remove', this._onFilterRemove.bind(this)); this.$el.on('click', '.o_calendar_filter_items input', this._onFilterActive.bind(this)); + this.$el.on('click', '.o_calendar_filter_items_checkall input', this._onFilterCheckAll.bind(this)); }, //-------------------------------------------------------------------------- @@ -130,6 +132,7 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, { */ _onFilterActive: function (e) { var $input = $(e.currentTarget); + this.filter_check_all[this.fieldName] = false; this.trigger_up('changeFilter', { 'fieldName': this.fieldName, 'value': $input.closest('.o_calendar_filter_item').data('value'), @@ -143,6 +146,7 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, { _onFilterRemove: function (e) { var self = this; var $filter = $(e.currentTarget).closest('.o_calendar_filter_item'); + this.filter_check_all[this.fieldName] = false; self._rpc({ model: self.write_model, method: 'unlink', @@ -157,6 +161,23 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, { }); }); }, + /** + * @private + * When the user check the box near the filter title: all existing filters + * are active, excepts the 'all' filter that removes the domain on active filters. + * @param {MouseEvent} e + */ + _onFilterCheckAll: function (e) { + let $input = $(e.currentTarget); + let $filter = $(e.currentTarget).closest('.o_calendar_filter_item'); + this.filter_check_all[this.fieldName] = $input.prop('checked'); + this.trigger_up('changeFilter', { + 'fieldName': this.fieldName, + 'value': 'check_all', + 'active': $input.prop('checked'), + }); + + }, }); return AbstractRenderer.extend({ @@ -381,6 +402,10 @@ return AbstractRenderer.extend({ _preOpenCreate: function (data) { if (this.$('.o_cw_popover').length) { this._unselectEvent(); + // Don't take into account the selection if the user closed a popover by doing a "selection" + this.$('.fc-event').not(".o_event").remove(); + // Clicking anywhere in the interface will only close the popover but we don't want the quickCreate + return; } if (this.state.context.default_name) { data.title = this.state.context.default_name; @@ -546,6 +571,8 @@ return AbstractRenderer.extend({ }, height: 'parent', unselectAuto: false, + // prevent too small events + timeGridEventMinHeight: 15, dir: _t.database.parameters.direction, events: (info, successCB) => { successCB(self.state.data); @@ -612,6 +639,7 @@ return AbstractRenderer.extend({ */ _initSidebar: function () { this.$sidebar = this.$('.o_calendar_sidebar'); + this.$calendarSyncContainer = this.$('#calendar_sync'); this.$sidebar_container = this.$(".o_calendar_sidebar_container"); this._initCalendarMini(); }, @@ -857,6 +885,12 @@ return AbstractRenderer.extend({ * @param {Object} eventData */ _getPopoverParams: function (eventData) { + // Display a small lock for private events that we manage + const displayLock = eventData.extendedProps.record.privacy === 'private' && eventData.extendedProps.record.partner_ids.includes(session.partner_id); + let title = eventData.extendedProps.record.display_name; + if (title.length > 30) { + title = title.substring(0, 30) + "…"; + } return { animation: false, delay: { @@ -865,8 +899,9 @@ return AbstractRenderer.extend({ }, trigger: 'manual', html: true, - title: eventData.extendedProps.record.display_name, - template: qweb.render('CalendarView.event.popover.placeholder', {color: this.getColor(eventData.extendedProps.color_index)}), + title: title, + template: qweb.render('CalendarView.event.popover.placeholder', + {color: this.getColor(eventData.extendedProps.color_index), displayLock: displayLock}), container: eventData.allDay ? '.fc-view' : '.fc-scroller', } }, diff --git a/addons/web/static/src/legacy/js/views/calendar/calendar_view.js b/addons/web/static/src/legacy/js/views/calendar/calendar_view.js index 9e69842a989..582471e1ecf 100644 --- a/addons/web/static/src/legacy/js/views/calendar/calendar_view.js +++ b/addons/web/static/src/legacy/js/views/calendar/calendar_view.js @@ -102,6 +102,8 @@ var CalendarView = AbstractView.extend({ 'title': fields[fieldName].string, 'fieldName': fieldName, 'filters': [], + 'check_all': {}, + 'filters': [], }; filters[fieldName].avatar_field = child.attrs.avatar_field; filters[fieldName].avatar_model = fields[fieldName].relation; @@ -111,6 +113,7 @@ var CalendarView = AbstractView.extend({ 'title': fields[fieldName].string, 'fieldName': fieldName, 'filters': [], + 'check_all': {}, }; filters[fieldName].write_model = child.attrs.write_model; filters[fieldName].write_field = child.attrs.write_field; // can't use a x2many fields @@ -122,6 +125,7 @@ var CalendarView = AbstractView.extend({ filters[fieldName] = filters[fieldName] || { 'title': fields[fieldName].string, 'fieldName': fieldName, + 'check_all': {}, 'filters': [], }; if (child.attrs.color) { diff --git a/addons/web/static/src/legacy/scss/web_calendar.scss b/addons/web/static/src/legacy/scss/web_calendar.scss index 28636d85b8b..62a62a4ec8f 100644 --- a/addons/web/static/src/legacy/scss/web_calendar.scss +++ b/addons/web/static/src/legacy/scss/web_calendar.scss @@ -1,5 +1,4 @@ // Variables -$o-cw-color-today: nth($o-colors, 3); $o-cw-color-today-accent: #FC3D39; $o-cw-popup-avatar-size: 16px; $o-cw-filter-avatar-size: 20px; @@ -140,10 +139,10 @@ $o-cw-filter-avatar-size: 20px; padding-left: $o-horizontal-padding; } - // Hide unnecessary borders - table td, div.fc-row.fc-week.fc-widget-content { - border-left-color: transparent; - border-right-color: transparent; + .fc-widget-header.fc-today{ + border-radius: 25px; + background: $o-brand-odoo; + color: white; } // Reinfornce default border color @@ -178,6 +177,8 @@ $o-cw-filter-avatar-size: 20px; div.fc-day-grid { background-color: $o-view-background-color; box-shadow: 0 6px 12px -6px rgba(black, 0.16); + border-left-color: rgba($o-brand-odoo, 0.3); + border-right-color: rgba($o-brand-odoo, 0.3); + hr.fc-widget-header { padding: 1px 0 0; @@ -196,9 +197,9 @@ $o-cw-filter-avatar-size: 20px; } .fc-day.fc-widget-content.fc-today { - border-left-color: rgba($o-cw-color-today, 0.3); - border-right-color: rgba($o-cw-color-today, 0.3); - background: rgba($o-cw-color-today, 0.05 ); + border-left-color: rgba($o-brand-odoo, 0.3); + border-right-color: rgba($o-brand-odoo, 0.3); + background: rgba($o-brand-odoo, .05); } .fc-event { @@ -272,6 +273,7 @@ $o-cw-filter-avatar-size: 20px; .fc-day-top { text-align: center; padding-top: 3px; + padding-bottom: 3px; .fc-day-number { float: none !important; } @@ -557,6 +559,10 @@ $o-cw-filter-avatar-size: 20px; @include media-breakpoint-up('xl') { width: 250px; } + // sync buttons are only displayed on calendar.event views + .o_calendar_sync { + padding-top: 0.5em; + } } .ui-datepicker { @@ -670,7 +676,7 @@ $o-cw-filter-avatar-size: 20px; opacity: 1; } - .o_calendar_filter_item { + .o_calendar_filter_items_checkall, .o_calendar_filter_item { cursor: pointer; overflow: hidden; @@ -775,7 +781,7 @@ $o-cw-filter-avatar-size: 20px; .o_cw_popover_fields_secondary { max-height: 170px; // Fallback for old browsers - max-height: 75vh; + max-height: 25vh; overflow: auto; padding-bottom: 1px; // prevents the scrollbar to show when not needed @@ -802,9 +808,17 @@ $o-cw-filter-avatar-size: 20px; } } +// Generated with http://phrogz.net/css/distinct-colors.html +$o-filter_colors: #ff1e00, #bf9600, #00ff84, #00aeff, #0003b3, #ff006a, #cc3300, #ffea00, #00cc85, #007ab3, + #6900cc, #ff0048, #ff6200, #c2cc00, #00ffc8, #008cff, #a600ff, #bf0036, #b24400, #d0ff00, + #00ffea, #0062b3, #ea00ff, #ff0026, #ff8400, #8bcc00, #00bfaf, #006aff, #af00bf, #bf001d, + #bf6300, #8cff00, #00f2ff, #004ab3, #ff00d0, #ffa600, #3acc00, #00b6bf, #0048ff, #d90094, + #bf7c00, #04ff00, #00d0ff, #0036bf, #ff008c, #ffc800, #00bf49, #0092b3, #0004ff, #b20062, + #649173 !default; + // =============== Generate color classes =============== -@for $i from 1 through length($o-colors-complete) { - $color: nth($o-colors-complete, $i); +@for $i from 1 through length($o-filter_colors) { + $color: nth($o-filter_colors, $i); .o_calendar_view .fc-view { .o_calendar_hatched_#{$i - 1} { @@ -918,6 +932,7 @@ $o-cw-filter-avatar-size: 20px; } .card-header { + padding-left: 2px; border-color: mix($card-border-color, mix(white, $color)); } } diff --git a/addons/web/static/src/legacy/xml/web_calendar.xml b/addons/web/static/src/legacy/xml/web_calendar.xml index 789fce74c0d..0dceb264b39 100644 --- a/addons/web/static/src/legacy/xml/web_calendar.xml +++ b/addons/web/static/src/legacy/xml/web_calendar.xml @@ -7,6 +7,7 @@
    +
    @@ -23,8 +24,10 @@
    -

    - +

    + +

    +

    @@ -55,12 +58,25 @@
    -
    - -
    - -
    - +
    +
    + + +
    +
    + + +
    +
    +
    diff --git a/addons/web/static/tests/legacy/views/calendar_tests.js b/addons/web/static/tests/legacy/views/calendar_tests.js index 0f560f3d7e9..a2c60b1693d 100644 --- a/addons/web/static/tests/legacy/views/calendar_tests.js +++ b/addons/web/static/tests/legacy/views/calendar_tests.js @@ -237,7 +237,7 @@ QUnit.module('Views', { // test filters assert.containsN($sidebar, '.o_calendar_filter', 2, "should display 2 filters"); - var $typeFilter = $sidebar.find('.o_calendar_filter:has(h5:contains(user))'); + var $typeFilter = $sidebar.find('.o_calendar_filter:has(span:contains(user))'); assert.ok($typeFilter.length, "should display 'user' filter"); assert.containsN($typeFilter, '.o_calendar_filter_item', 3, "should display 3 filter items for 'user'"); @@ -246,27 +246,27 @@ QUnit.module('Views', { assert.strictEqual($typeFilter.find('.o_calendar_filter_item:last .o_cw_filter_title').text(), "Undefined", "filters having false value should display 'Undefined' string"); assert.strictEqual($typeFilter.find('.o_calendar_filter_item:last label img').length, 0, "filters having false value should not have any user image"); - var $attendeesFilter = $sidebar.find('.o_calendar_filter:has(h5:contains(attendees))'); + var $attendeesFilter = $sidebar.find('.o_calendar_filter:has(span:contains(attendees))'); assert.ok($attendeesFilter.length, "should display 'attendees' filter"); - assert.containsN($attendeesFilter, '.o_calendar_filter_item', 3, "should display 3 filter items for 'attendees' who use write_model (2 saved + Everything)"); + assert.containsN($attendeesFilter, '.o_calendar_filter_item', 3, "should display 3 filter items for 'attendees' who use write_model (checkall + 2 saved + Everything)"); assert.ok($attendeesFilter.find('.o_field_many2one').length, "should display one2many search bar for 'attendees' filter"); assert.containsN(calendar, '.fc-event', 7, "should display 7 events ('event 5' counts for 2 because it spans two weeks and thus generate two fc-event elements)"); - await testUtils.dom.click(calendar.$('.o_calendar_filter input[type="checkbox"]').first()); + await testUtils.dom.click(calendar.$('.o_calendar_filter input[type="checkbox"]').eq(1)); // click on partner 2 assert.containsN(calendar, '.fc-event', 4, "should now only display 4 event"); - await testUtils.dom.click(calendar.$('.o_calendar_filter input[type="checkbox"]').eq(1)); + await testUtils.dom.click(calendar.$('.o_calendar_filter input[type="checkbox"]').eq(2)); assert.containsNone(calendar, '.fc-event', "should not display any event anymore"); // test search bar in filter await testUtils.dom.click($sidebar.find('input[type="text"]')); - assert.strictEqual($('ul.ui-autocomplete li:not(.o_m2o_dropdown_option)').length, 2,"should display 2 choices in one2many autocomplete"); // TODO: remove :not(.o_m2o_dropdown_option) because can't have "create & edit" choice + assert.strictEqual($('ul.ui-autocomplete li').length, 2,"should display 2 choices in one2many autocomplete"); await testUtils.dom.click($('ul.ui-autocomplete li:first')); - assert.containsN($sidebar, '.o_calendar_filter:has(h5:contains(attendees)) .o_calendar_filter_item', 4, "should display 4 filter items for 'attendees'"); + assert.containsN($sidebar, '.o_calendar_filter:has(span:contains(attendees)) .o_calendar_filter_item', 4, "should display 4 filter items for 'attendees'"); await testUtils.dom.click($sidebar.find('input[type="text"]')); - assert.strictEqual($('ul.ui-autocomplete li:not(.o_m2o_dropdown_option)').text(), "partner 4", "should display the last choice in one2many autocomplete"); // TODO: remove :not(.o_m2o_dropdown_option) because can't have "create & edit" choice - await testUtils.dom.click($sidebar.find('.o_calendar_filter_item .o_remove').first(), {allowInvisible: true}); - assert.containsN($sidebar, '.o_calendar_filter:has(h5:contains(attendees)) .o_calendar_filter_item', 3, "click on remove then should display 3 filter items for 'attendees'"); + assert.strictEqual($('ul.ui-autocomplete li').text(), "partner 4", "should display the last choice in one2many autocomplete"); + await testUtils.dom.click($sidebar.find('.o_calendar_filter_item .o_remove').eq(1), {allowInvisible: true}); + assert.containsN($sidebar, '.o_calendar_filter:has(span:contains(attendees)) .o_calendar_filter_item', 3, "click on remove then should display 3 filter items for 'attendees'"); calendar.destroy(); }); @@ -2279,7 +2279,7 @@ QUnit.module('Views', { assert.containsN(calendar, '.o_calendar_filter', 2, "should display 2 filters"); - var $typeFilter = calendar.$('.o_calendar_filter:has(h5:contains(Event Type))'); + var $typeFilter = calendar.$('.o_calendar_filter:has(span:contains(Event Type))'); assert.ok($typeFilter.length, "should display 'Event Type' filter"); assert.containsOnce($typeFilter, '#o_cw_filter_collapse_EventType', "Id should be equals to o_cw_filter_collapse_EventType for 'Event Type'"); assert.containsN($typeFilter, '.o_calendar_filter_item', 3, "should display 3 filter items for 'Event Type'"); @@ -2450,7 +2450,7 @@ QUnit.module('Views', { }); QUnit.test('Update event with filters', async function (assert) { - assert.expect(6); + assert.expect(12); var records = this.data.user.records; records.push({ @@ -2516,6 +2516,19 @@ QUnit.module('Views', { assert.containsN(calendar, '.o_calendar_filter_item', 6, "should add the missing filter (active)"); assert.containsN(calendar, '.fc-event', 3, "should display the updated item"); + // test the behavior of the 'select all' input checkbox + assert.containsN(calendar, '.o_calendar_filter_item input:checked', 3, "should display 3 true checkbox"); + assert.containsN(calendar, '.o_calendar_filter_item input:not(:checked)', 3, "should display 3 false checkbox"); + // Click to select all users + await testUtils.dom.click(calendar.$('.o_calendar_filter_items_checkall:eq(1) input')); + // should contains 4 events + assert.containsN(calendar, '.fc-event', 4, "should display the updated events"); + // Should have 4 checked boxes + assert.containsN(calendar, '.o_calendar_filter_item input:checked', 4, "should display 4 true checkbox"); + // unselect all user + await testUtils.dom.click(calendar.$('.o_calendar_filter_items_checkall:eq(1) input')); + assert.containsN(calendar, '.fc-event', 0, "should not display any event"); + assert.containsN(calendar, '.o_calendar_filter_item input:checked', 1, "should display 1 true checkbox"); calendar.destroy(); });