Skip to content

Commit

Permalink
Merge pull request #33898 from dimagi/riese/search_group
Browse files Browse the repository at this point in the history
WebApps UI for grouped search fields
  • Loading branch information
MartinRiese committed Dec 19, 2023
2 parents 09fec58 + 2646960 commit 3328359
Show file tree
Hide file tree
Showing 13 changed files with 343 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ hqDefine("cloudcare/js/formplayer/menus/collections", function () {
'session_id',
],

queryProperties: [
'groupHeaders',
],

parse: function (response) {
_.extend(this, _.pick(response, this.commonProperties));

Expand Down Expand Up @@ -112,6 +116,7 @@ hqDefine("cloudcare/js/formplayer/menus/collections", function () {
return response.entities;
} else if (response.type === "query") {
addBreadcrumb(this, "query", sentryData);
_.extend(this, _.pick(response, this.queryProperties));
return response.displays;
} else if (response.details) {
addBreadcrumb(this, "details", sentryData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ hqDefine("cloudcare/js/formplayer/menus/controller", function () {
formplayerUtils = hqImport("cloudcare/js/formplayer/utils/utils"),
menusUtils = hqImport("cloudcare/js/formplayer/menus/utils"),
views = hqImport("cloudcare/js/formplayer/menus/views"),
QueryListView = hqImport("cloudcare/js/formplayer/menus/views/query"),
queryView = hqImport("cloudcare/js/formplayer/menus/views/query"),
initialPageData = hqImport("hqwebapp/js/initial_page_data").get,
Collection = hqImport("cloudcare/js/formplayer/menus/collections");
var selectMenu = function (options) {
Expand Down Expand Up @@ -128,24 +128,26 @@ hqDefine("cloudcare/js/formplayer/menus/controller", function () {
if (sidebarEnabled && menuResponse.type === constants.ENTITIES && queryResponse) {
var queryCollection = new Collection(queryResponse.displays);
FormplayerFrontend.regions.getRegion('sidebar').show(
QueryListView({
queryView.queryListView({
collection: queryCollection,
title: menuResponse.title,
description: menuResponse.description,
hasDynamicSearch: queryResponse.dynamicSearch,
sidebarEnabled: true,
disableDynamicSearch: !sessionStorage.submitPerformed,
groupHeaders: queryResponse.groupHeaders,
}).render()
);
} else if (sidebarEnabled && menuResponse.type === constants.QUERY) {
FormplayerFrontend.regions.getRegion('sidebar').show(
QueryListView({
queryView.queryListView({
collection: menuResponse,
title: menuResponse.title,
description: menuResponse.description,
hasDynamicSearch: menuResponse.dynamicSearch,
sidebarEnabled: true,
disableDynamicSearch: true,
groupHeaders: menuResponse.groupHeaders,
}).render()
);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ hqDefine("cloudcare/js/formplayer/menus/utils", function () {
var FormplayerFrontend = hqImport("cloudcare/js/formplayer/app"),
kissmetrics = hqImport("analytix/js/kissmetrix"),
ProgressBar = hqImport("cloudcare/js/formplayer/layout/views/progress_bar"),
QueryView = hqImport("cloudcare/js/formplayer/menus/views/query"),
view = hqImport("cloudcare/js/formplayer/menus/views/query"),
toggles = hqImport("hqwebapp/js/toggles"),
utils = hqImport("cloudcare/js/formplayer/utils/utils"),
views = hqImport("cloudcare/js/formplayer/menus/views"),
Expand Down Expand Up @@ -138,6 +138,7 @@ hqDefine("cloudcare/js/formplayer/menus/utils", function () {
multiSelectMaxSelectValue: menuResponse.maxSelectValue,
dynamicSearch: menuResponse.dynamicSearch,
endpointActions: menuResponse.endpointActions,
groupHeaders: menuResponse.groupHeaders,
};
};

Expand Down Expand Up @@ -186,7 +187,7 @@ hqDefine("cloudcare/js/formplayer/menus/utils", function () {
execute: false,
forceManualSearch: false,
});
return QueryView(menuData);
return view.queryListView(menuData);
} else if (menuResponse.type === constants.ENTITIES) {
var searchText = urlObject.search;
var event = "Viewed Case List";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*global DOMPurify, Marionette */
/*global Backbone, DOMPurify, Marionette */

hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
// 'hqwebapp/js/bootstrap3/hq.helpers' is a dependency. It needs to be added
Expand All @@ -11,7 +11,8 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
FormplayerFrontend = hqImport("cloudcare/js/formplayer/app"),
formplayerUtils = hqImport("cloudcare/js/formplayer/utils/utils"),
initialPageData = hqImport("hqwebapp/js/initial_page_data"),
toggles = hqImport("hqwebapp/js/toggles");
toggles = hqImport("hqwebapp/js/toggles"),
Collection = hqImport("cloudcare/js/formplayer/menus/collections");

var separator = " to ",
serverSeparator = "__",
Expand All @@ -26,6 +27,41 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
return cloudcareUtils.parseInputDate(dateString).format(dateFormat);
};

var getField = function (obj, fieldName) {
return typeof obj.get === 'function' ? obj.get(fieldName) : obj[fieldName];
};

var groupDisplays = function (displays, groupHeaders) {
const groupedDisplays = [];
let currentGroup = {
groupKey: null,
groupName: null,
displays: [],
required: false,
};

displays.forEach(display => {
const groupKey = getField(display, 'groupKey');
if (currentGroup.groupKey !== groupKey) {
if (currentGroup.groupKey) {
groupedDisplays.push(currentGroup);
}
currentGroup = {
groupKey: groupKey,
groupName: groupHeaders[groupKey],
displays: [display],
required: getField(display, 'required'),
};
} else {
currentGroup.displays.push(display);
currentGroup.required = currentGroup.required || getField(display, 'required');
}
});
groupedDisplays.push(currentGroup);

return groupedDisplays;
};

var encodeValue = function (model, searchForBlank) {
// transform value entered to that sent to CaseSearch API (and saved for sticky search)
var value = model.get('value');
Expand Down Expand Up @@ -460,10 +496,80 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {

});

var GroupedQueryView = Marionette.CollectionView.extend({
tagName: "tr",
template: _.template($("#query-view-group-template").html() || ""),
childView: QueryView,
childViewContainer: "#query-group-content",

childViewOptions: function () {
return {parentView: this.options.parentView};
},

ui: {
groupHeader: '.search-query-group-header',
},

events: {
'click @ui.groupHeader': 'updateArrow',
},

updateArrow: function (e) {
const arrow = $(e.currentTarget).children('i');
if (arrow.hasClass('fa-angle-double-down')) {
arrow.removeClass('fa-angle-double-down');
arrow.addClass('fa-angle-double-up');
} else {
arrow.removeClass('fa-angle-double-up');
arrow.addClass('fa-angle-double-down');
}
},

templateContext: function () {
let groupName = this.options.groupName === undefined ?
"" : markdown.render(this.options.groupName.trim());
return {
groupName: groupName,
groupKey: this.options.groupKey,
required: this.options.required,
named: groupName.length > 0,
};
},
});

var QueryListView = Marionette.CollectionView.extend({
tagName: "div",
template: _.template($("#query-view-list-template").html() || ""),
childView: QueryView,

childView(item) {
if (item.has("groupName")) {
return GroupedQueryView;
} else {
return QueryView;
}
},

buildChildView(child, ChildViewClass, childViewOptions) {
let options = {};

if (child.has("groupName")) {
const childList = new Backbone.Collection(child.get('displays'));
options = _.extend(
{
collection: childList,
groupName: child.get('groupName'),
groupKey: child.get('groupKey'),
required: child.get('required'),
},
childViewOptions
);
} else {
options = _.extend({model: child}, childViewOptions);
}

return new ChildViewClass(options);
},

childViewContainer: "#query-properties",
childViewOptions: function () { return {parentView: this}; },

Expand All @@ -478,6 +584,11 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {

this.dynamicSearchEnabled = !(options.disableDynamicSearch || this.smallScreenEnabled) &&
(toggles.toggleEnabled('DYNAMICALLY_UPDATE_SEARCH_RESULTS') && this.options.sidebarEnabled);

if (Object.keys(options.groupHeaders).length > 0) {
const groupedCollection = groupDisplays(options.collection, options.groupHeaders);
this.collection = new Collection(groupedCollection);
}
},

templateContext: function () {
Expand All @@ -487,6 +598,7 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
title: this.options.title.trim(),
description: DOMPurify.sanitize(description),
sidebarEnabled: this.options.sidebarEnabled,
grouped: Boolean(this.collection.find(c => c.has("groupKey"))),
};
},

Expand All @@ -512,9 +624,37 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
}
},

_getChildren: function () {
const children = [];
this.children.each(function (childView) {
if (childView.children) {
childView.children.each(function (grandChildView) {
children.push(grandChildView);
});
} else {
children.push(childView);
}
});
return children;
},

_getChildModels: function () {
return _.flatten(_.map(
Array.from(this.collection),
function (item) {
if (item.has("displays")) {
return item.get("displays");
} else {
return [item];
}
}
));
},

getAnswers: function () {
var answers = {};
this.children.each(function (childView) {
const children = this._getChildren();
children.forEach(function (childView) {
var encodedValue = childView.getEncodedValue();
if (encodedValue !== undefined) {
answers[childView.model.get('id')] = encodedValue;
Expand Down Expand Up @@ -544,13 +684,13 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
value = _.isEmpty(value) ? null : value[0];
}
}
self.collection.models[i].set({
self._getChildModels()[i].set({
value: value,
});

self.children.findByIndex(i)._setItemset(choices, response.models[i].get('itemsetChoicesKey'));

self.children.findByIndex(i)._render(); // re-render with new choice values
var childByIndex = self._getChildren()[i];
childByIndex._setItemset(choices, response.models[i].get('itemsetChoicesKey'));
childByIndex._render(); // re-render with new choice values
}
}
});
Expand All @@ -561,7 +701,7 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {

clearAction: function () {
var self = this;
this.children.each(function (childView) {
self._getChildren().forEach(function (childView) {
childView.clear();
});
self.setStickyQueryInputs();
Expand Down Expand Up @@ -595,7 +735,7 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
updateSearchResults: function () {
var self = this;
var invalidRequiredFields = [];
self.children.each(function (childView) {
self._getChildren().forEach(function (childView) {
if (childView.hasRequiredError()) {
invalidRequiredFields.push(childView.model.get('text'));
}
Expand All @@ -611,7 +751,7 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {

self._updateModelsForValidation().done(function (response) {
//Gather error messages
self.children.each(function (childView) {
self._getChildren().forEach(function (childView) {
//Filter out empty required fields and check for validity
if (!childView.hasRequiredError() || childView === changedChildView) { childView.isValid(); }
});
Expand All @@ -633,7 +773,7 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {

$.when(updatingModels).done(function (response) {
// Gather error messages
self.children.each(function (childView) {
self._getChildren().forEach(function (childView) {
if (!childView.isValid()) {
invalidFields.push(childView.model.get('text'));
}
Expand Down Expand Up @@ -674,15 +814,16 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {
// Update models based on response
if (response.queryResponse) {
_.each(response.queryResponse.displays, function (responseModel, i) {
self.collection.models[i].set({
self._getChildModels()[i].set({
error: responseModel.error,
required: responseModel.required,
required_msg: responseModel.required_msg,
});
});
} else {
_.each(response.models, function (responseModel, i) {
self.collection.models[i].set({
const childModels = self._getChildModels();
childModels[i].set({
error: responseModel.get('error'),
required: responseModel.get('required'),
required_msg: responseModel.get('required_msg'),
Expand Down Expand Up @@ -710,7 +851,7 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {

initGeocoders: function () {
var self = this;
_.each(self.collection.models, function (model, i) {
_.each(self._getChildModels(), function (model, i) {
var $field = $($(".query-field")[i]);

// Set geocoder receivers to subscribe
Expand All @@ -728,7 +869,10 @@ hqDefine("cloudcare/js/formplayer/menus/views/query", function () {

});

return function (data) {
return new QueryListView(data);
return {
queryListView: function (data) {
return new QueryListView(data);
},
groupDisplays: groupDisplays,
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ hqDefine("cloudcare/js/formplayer/spec/fake_formplayer", function () {
module.makeQueryResponse = function (options) {
AssertProperties.assertRequired(options, ["displays", "queryKey"]);
return _.defaults(makeResponse(options), {
type: "query",
"type": "query",
"groupHeaders": {},
});
};

Expand Down

0 comments on commit 3328359

Please sign in to comment.