[FIX] web: autocompletion in the new search view

A lot of new features have been implemented in search view
but autocompletion was forgotten. This commit fixes many
small problems that were introduced:

	- synchronization of facets created through
		autocompletion with filters and groupby menus
	- filters created through autocompletion can be saved
This commit is contained in:
Mathieu Duckerts-Antoine
2018-08-14 12:37:44 +02:00
parent 89931d1749
commit ad0a758b75
6 changed files with 168 additions and 81 deletions
@@ -14,6 +14,8 @@ var _t = core._t;
var PERIOD_OPTIONS = TimeRangeMenuOptions.PeriodOptions;
var DEFAULT_PERIOD = 'this_month';
var FiltersMenu = DropdownMenu.extend({
custom_events: {
remove_proposition: '_onRemoveProposition',
@@ -54,6 +56,7 @@ var FiltersMenu = DropdownMenu.extend({
this.isMobile = config.device.isMobile;
// determines list of options used by filter of type 'date'
this.periodOptions = PERIOD_OPTIONS;
this.defaultOptionId = DEFAULT_PERIOD;
// determines when the 'Add custom filter' submenu is open
this.generatorMenuIsOpen = false;
this.propositions = [];
@@ -146,9 +149,6 @@ var FiltersMenu = DropdownMenu.extend({
if (item.isPeriod) {
item.options = this.periodOptions;
}
// super has to be called here because we need to add options to groupby
// before to call it since some keys in a groupby are initialized using
// the keys 'options' and 'defaultOptionId'
this._super.apply(this, arguments);
},
/**
@@ -54,6 +54,7 @@ var GroupByMenu = DropdownMenu.extend({
{description: 'Quarter', optionId: 'quarter', groupId: 1},
{description: 'Year', optionId: 'year', groupId: 1},
];
this.defaultOptionId = DEFAULT_INTERVAL;
this.groupableFields = [];
_.each(fields, function (field, name) {
if (field.sortable && _.contains(GROUPABLE_TYPES, field.type)) {
@@ -131,6 +132,7 @@ var GroupByMenu = DropdownMenu.extend({
var eventData = _.clone(groupby);
this._prepareItem(groupby);
if (groupby.hasOptions) {
groupby.defaultOptionId = DEFAULT_INTERVAL;
groupby.currentOptionId = DEFAULT_INTERVAL;
groupby.isDate = true;
eventData.optionId = groupby.currentOptionId;
@@ -128,7 +128,7 @@ var Field = Input.extend( /** @lends instance.web.search.Field# */ {
__contexts: contexts
});
},
get_groupby: function () { },
get_groupby: function () {},
/**
* Function creating the returned domain for the field, override this
* methods in children if you only need to customize the field's domain
@@ -143,7 +143,7 @@ var Field = Input.extend( /** @lends instance.web.search.Field# */ {
make_domain: function (name, operator, facet) {
return [[name, operator, this.value_from(facet)]];
},
get_domain: function (facet) {
get_domain: function (facet, noDomainEvaluation) {
if (!facet.values.length) { return; }
var value_to_domain;
@@ -166,10 +166,15 @@ var Field = Input.extend( /** @lends instance.web.search.Field# */ {
};
}
var domains = facet.values.map(value_to_domain);
// here 'noDomainEvaluation' means 'string representation'
if (noDomainEvaluation) {
domains = domains.map(Domain.prototype.arrayToString);
return pyUtils.assembleDomains(domains, 'OR');
}
if (domains.length === 1) {
return domains[0];
}
_.each(domains, Domain.prototype.normalizeArray);
var ors = _.times(domains.length - 1, _.constant("|"));
return ors.concat.apply(ors, domains);
@@ -380,7 +380,7 @@ var SearchView = Widget.extend({
break;
}
}
this.selectedGroupIds = {
this.activeItemIds = {
groupByCategory: [],
filterCategory: []
};
@@ -627,9 +627,10 @@ var SearchView = Widget.extend({
* @param {Object} ui.item selected completion item
*/
select_completion: function (e, ui) {
var facet = ui.item.facet;
e.preventDefault();
if(ui.item.facet && ui.item.facet.values && ui.item.facet.values.length && String(ui.item.facet.values[0].value).trim() !== "") {
this.query.add(ui.item.facet);
if(facet && facet.values && facet.values.length && String(facet.values[0].value).trim() !== "") {
this.query.add(facet);
} else {
this.query.trigger('add');
}
@@ -666,7 +667,7 @@ var SearchView = Widget.extend({
_.invoke(this.input_subviews, 'destroy');
this.input_subviews = [];
var selectedGroupIds = {
var activeItemIds = {
groupByCategory: [],
filterCategory: [],
};
@@ -674,29 +675,31 @@ var SearchView = Widget.extend({
this.query.each(function (facet) {
var values = facet.get('values');
if (facet.attributes.cat === "groupByCategory") {
selectedGroupIds.groupByCategory = _.uniq(
values.reduce(
function (acc, value) {
var groupby = value.value;
var description = _.findWhere(self.groupbysMapping, {groupby: groupby});
if (description) {
acc.push(description.groupId);
}
return acc;
},
[]
activeItemIds.groupByCategory = activeItemIds.groupByCategory.concat(
_.uniq(
values.reduce(
function (acc, value) {
var groupby = value.value;
var description = _.findWhere(self.groupbysMapping, {groupby: groupby});
if (description) {
acc.push(description.groupbyId);
}
return acc;
},
[]
)
)
);
}
if (facet.attributes.cat === "filterCategory") {
selectedGroupIds.filterCategory = selectedGroupIds.filterCategory.concat(
activeItemIds.filterCategory = activeItemIds.filterCategory.concat(
_.uniq(
values.reduce(
function (acc, value) {
var filter = value.value;
var description = _.findWhere(self.filtersMapping, {filter: filter});
if (description) {
acc.push(description.groupId);
acc.push(description.filterId);
}
return acc;
},
@@ -729,11 +732,10 @@ var SearchView = Widget.extend({
_.last(self.input_subviews).$el.focus();
}
if (self.groupby_menu) {
self._unsetUnusedGroupbys(selectedGroupIds.groupByCategory);
self.groupby_menu.updateItemsStatus(activeItemIds.groupByCategory);
}
if (self.filters_menu) {
self._unsetUnusedFilters(selectedGroupIds.filterCategory);
}
self.filters_menu.updateItemsStatus(activeItemIds.filterCategory); }
if (self.timeRangeMenu && !timeRangeMenuIsActive) {
self.timeRangeMenu.deactivate();
}
@@ -903,6 +905,7 @@ var SearchView = Widget.extend({
var filterId = _.uniqueId('__filter__');
var isPeriod = filter.attrs.isPeriod;
var defaultPeriod = filter.attrs.default_period;
var isActive = !self.hasFavorites && !!self.search_defaults[filter.attrs.name];
filters.push({
itemId: filterId,
description: filter.attrs.string || filter.attrs.help ||
@@ -910,8 +913,8 @@ var SearchView = Widget.extend({
domain: filter.attrs.domain,
fieldName: filter.attrs.date,
isPeriod: isPeriod,
currentOptionId: defaultPeriod,
isActive: !self.hasFavorites && !!self.search_defaults[filter.attrs.name],
defaultOptionId: defaultPeriod,
isActive: isActive,
groupId: groupId,
});
self.filtersMapping.push({filterId: filterId, filter: filter, groupId: groupId});
@@ -945,14 +948,15 @@ var SearchView = Widget.extend({
var fieldName = groupby.attrs.fieldName;
var isDate = groupby.attrs.isDate;
var defaultInterval = groupby.attrs.defaultInterval || DEFAULT_INTERVAL;
var isActive = !self.hasFavorites && !!self.search_defaults[groupby.attrs.name];
groupbys.push({
itemId: groupbyId,
description: groupby.attrs.string || groupby.attrs.help || groupby.attrs.name
|| groupby.attrs.fieldName || 'Ω',
isDate: isDate,
fieldName: fieldName,
currentOptionId: defaultInterval,
isActive: !self.hasFavorites && !!self.search_defaults[groupby.attrs.name],
defaultOptionId: defaultInterval,
isActive: isActive,
groupId: groupId,
});
if (isDate) {
@@ -990,6 +994,9 @@ var SearchView = Widget.extend({
fv.arch = utils.xml_to_json(doc, true);
return fv;
},
/**
* @returns {Deferred}
*/
_searchDefaultTimeRange: function () {
if (this.timeRanges) {
var timeRange = "[]";
@@ -1065,50 +1072,6 @@ var SearchView = Widget.extend({
}
},
/**
* This function ask the groupby menu to deactive all groupbys if no
* groupby is used.
*
* @param {string[]]} selectedGroupIds list of group ids to deactivate
*/
_unsetUnusedGroupbys: function (selectedGroupIds) {
var groupIds = this.groupsMapping.reduce(
function (acc, triple) {
if (triple.category === 'Group By') {
acc.push(triple.groupId);
}
return acc;
},
[]
);
this.selectedGroupIds.groupByCategory = selectedGroupIds;
if (selectedGroupIds.length === 0) {
this.groupby_menu.unsetGroups(groupIds);
}
},
/**
* This function ask the filters menu to deactive all filters with
* corresponding groupId in the list selectedGroupIds.
*
* @private
* @param {string[]]} selectedGroupIds
*/
_unsetUnusedFilters: function (selectedGroupIds) {
var groupIds = this.groupsMapping.reduce(
function (acc, triple) {
if (triple.category === 'Filters') {
if (!_.contains(selectedGroupIds, triple.groupId)) {
acc.push(triple.groupId);
}
}
return acc;
},
[]
);
this.selectedGroupIds.filterCategory = selectedGroupIds;
this.filters_menu.unsetGroups(groupIds);
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
@@ -66,14 +66,18 @@ var DropdownMenu = Widget.extend({
//--------------------------------------------------------------------------
/**
* @param {Number[]} groupIds
* @param {string[]} activeItemIds
*/
unsetGroups: function (groupIds) {
var self = this;
_.each(self.items, function (item) {
if (_.contains(groupIds, item.groupId)) {
updateItemsStatus: function (activeItemIds) {
_.each(this.items, function (item) {
if (!_.contains(activeItemIds, item.itemId)) {
item.isActive = false;
item.currentOptionId = false;
} else {
item.isActive = true;
item.currentOptionId = item.hasOptions ?
(item.currentOptionId || item.defaultOptionId) :
false;
}
});
this._renderMenuItems();
@@ -122,7 +126,8 @@ var DropdownMenu = Widget.extend({
if (item.options && item.options.length !== 0) {
item.options = _.sortBy(item.options, 'groupId');
item.hasOptions = true;
item.currentOptionId = item.isActive && item.currentOptionId ? item.currentOptionId : false;
item.defaultOptionId = item.defaultOptionId || this.defaultOptionId;
item.currentOptionId = item.isActive && item.defaultOptionId ? item.defaultOptionId : false;
}
},
/**
@@ -90,6 +90,20 @@ QUnit.module('Search View', {
type: 'ir.actions.act_window',
views: [[2, 'list']],
search_view_id: [7, 'search'],
}, {
id: 9,
name: 'Partners Action 9',
res_model: 'partner',
type: 'ir.actions.act_window',
views: [[false, 'pivot']],
search_view_id: [5, 'search'],
}, {
id: 10,
name: 'Partners Action 10',
res_model: 'partner',
type: 'ir.actions.act_window',
views: [[false, 'pivot']],
search_view_id: [8, 'search'],
}
];
@@ -109,6 +123,12 @@ QUnit.module('Search View', {
'<field name="float_field" type="measure"/>' +
'</graph>',
// pivot views
'partner,false,pivot': '<pivot>' +
'<field name="date_field" type="row" interval="day"/>' +
'<field name="float_field" type="measure"/>' +
'</pivot>',
// search views
'partner,false,search': '<search>'+
'<filter string="candle" name="itsName" context="{\'group_by\': \'foo\'}"/>' +
@@ -154,6 +174,15 @@ QUnit.module('Search View', {
'<separator/>' +
'<filter string="11" name="coolName11" domain="[]"/>' +
'</search>',
'partner,8,search': '<search>'+
'<field name="foo"/>' +
'<field name="date_field"/>' +
'<field name="birthday"/>' +
'<field name="bar"/>' +
'<field name="float_field"/>' +
'<filter string="Date Field Filter" name="positive" date="date_field"/>' +
'<filter string="Date Field Groupby" name="coolName" context="{\'group_by\': \'date_field:day\'}"/>' +
'</search>',
};
},
}, function () {
@@ -319,7 +348,7 @@ QUnit.module('Search View', {
assert.strictEqual($('.o_content tr.o_group_header').length, 4);
actionManager.destroy();
});
QUnit.test('a separator in groupbys does not cause problems', function (assert) {
assert.expect(6);
@@ -523,5 +552,88 @@ QUnit.module('Search View', {
}
actionManager.destroy();
});
QUnit.test('selection via autocompletion modifies appropriately submenus', function (assert) {
assert.expect(4);
var actionManager = createActionManager({
actions: this.actions,
archs: this.archs,
data: this.data,
});
actionManager.doAction(9);
$('.o_searchview_input').trigger($.Event('keypress', {
which: 97,
}));
$('.o_searchview_input').trigger($.Event('keyup', {
which: $.ui.keyCode.ENTER,
keyCode: $.ui.keyCode.ENTER,
}));
$('.o_searchview_input').trigger($.Event('keypress', {
which: 103,
}));
$('.o_searchview_input').trigger($.Event('keyup', {
which: $.ui.keyCode.ENTER,
keyCode: $.ui.keyCode.ENTER,
}));
assert.strictEqual($('.o_searchview_input_container .o_facet_values').eq(0).text().trim(),
"Date Field Filter: This Month",
"There should be a filter facet with label 'Date Field Filter: This Month'");
assert.strictEqual($('.o_searchview_input_container .o_facet_values').eq(1).text().trim(),
"Date Field Groupby: Day",
"There should be a filter facet with label 'Date Field Groupby: Day'");
$('button .fa-filter').click();
$('.o_filters_menu .o_menu_item').eq(0).click();
assert.strictEqual($('.o_filters_menu .o_item_option a.selected').text().trim(), "This Month",
"The item 'This Month' should be selected in the filters menu");
$('button .fa-bars').click();
$('.o_group_by_menu .o_menu_item').eq(0).click();
assert.strictEqual($('.o_group_by_menu .o_item_option a.selected').text().trim(), "Day",
"The item 'Day' should be selected in the groupby menu");
actionManager.destroy();
});
QUnit.test('save filters created via autocompletion works', function (assert) {
assert.expect(2);
var actionManager = createActionManager({
actions: this.actions,
archs: this.archs,
data: this.data,
intercepts: {
create_filter: function (ev) {
assert.ok(ev.data.filter.domain === "[['foo', 'ilike', 'a']]");
},
},
});
actionManager.doAction(10);
$('.o_searchview_input').trigger($.Event('keypress', {
which: 97,
}));
$('.o_searchview_input').trigger($.Event('keyup', {
which: $.ui.keyCode.ENTER,
keyCode: $.ui.keyCode.ENTER,
}));
assert.strictEqual($('.o_searchview_input_container .o_facet_values span').text().trim(), "a");
$('button .fa-star').click();
$('.o_favorites_menu a.o_save_search').click();
$('.o_favorites_menu div.o_save_name button').click();
actionManager.destroy();
});
});
});