Coveo for Sitecore has a method for customizing your UI and UX that doesn't involve changing the renderings, making custom components, or changing hive at all.
It can even be customized per language and answers the question "Where do I bind my events now?" that comes up when upgrading from older UI setups.
Where Is It?
It's the field called Initialization File Name on the search interface item for your search page. The Hive example search page puts this item in $searchpage/example/example-search-interfaces.
The the actual file location is in $instanceDirectory/Coveo/Hive/Init.
What Do We Do With It?
Here's the original file's javascript:
<script type="text/javascript">
document.addEventListener("CoveoSearchEndpointInitialized", function() {
@*
setTimeout(function() { }, 0) defers the initialization just enough to execute other DOMContentLoaded or CoveoSearchEndpointInitialized events in the page.
This is required by some components to allow them to register before initialization.
*@
setTimeout(function() {
var searchInterface = document.getElementById("@Model.Id");
@if (Html.Coveo().IsEditingInPageEditor()) {
@: if (typeof(CoveoForSitecore) !== "undefined" && typeof(Sitecore) !== "undefined") {
@: CoveoForSitecore.initializeSearchInterfaceForExperienceEditor(searchInterface);
@: }
} else {
@: if (typeof(CoveoForSitecore) !== "undefined") {
@: CoveoForSitecore.initSearchInterface(searchInterface);
@: }
}
}, 0);
});
</script>
This part is really basic; it just starts the Coveo interface, though notice it has a different start for Experience Editor.
Let's start with something simple. Say you have a lot of facets, and you want the UI to load up nice and clean. It'll be simpler for your users. Maybe you just want the page to be shorter, you've already limited the number of results to four because you're doing a graphic-heavy gallery presentation.
So let's collapse all the facets, and show only the headings.
First, we move the line defining the search interface to the start of the script element, and then bind a function to the afterComponentsInitilization event.
This makes sure the facets are ready to accept the collapse command.
var searchInterface = document.getElementById("@Model.Id");
Coveo.$$(searchInterface).on(Coveo.InitializationEvents.afterComponentsInitialization, function() {
var facets = [...searchInterface.querySelectorAll(".CoveoDynamicFacet")]; //old style facets use .CoveoFacet, if you have those instead
facets.forEach(facet => {
var myFacetElement = Coveo.get(facet);
myFacetElement.collapse();
});
});
You could also use this to preselect the facets, like this: Interacting with facets.
What's Next?
There are a whole ton of events available now. Coveo has a big list here with a nice flowchart: Search Framework Events
For now, let's do two more examples, then wrap up. The first example is query filtering. One on project, users were pasting in dollar signs from text on the website, and that's special Coveo syntax. So we saw an error, and it doesn't actually affect the results. So we'll strip it out.
Building on the previous example, we'll add this just before the SetTimeout call.
Coveo.$$(searchInterface).on('doneBuildingQuery',
function(e, args) {
var q = args.queryBuilder.expression.parts[0];
if(q && q.indexOf("$") !== -1){
args.queryBuilder.expression.parts[0] = q.replace("$", "");
}
});
Here, q is the query text entered by the user, and we have some guard code in case it is an empty search. If you really wanted to, you can use this same code to inject search terms if you were on a customized search page.
We bind to the doneBuildingQuery event to make sure Coveo is done setting everything up for the query, and any other custom code you have is finished.
This is a place where per-language customization would be useful, too. That you do just by creating different files for each language and assigning the file name in the language version of the search interface.
Customizing Results
Let's say you have some analytics data about your user and want to customize the presentation of the results based on that. Maybe you add a border or a "look here" icon to certain results on your catalog search page.
Which event do you want? Well, there are two that are good for this. newResultDisplayed, which fires on rendering each new result, and newResultsDisplayed, which fires at the end of displaying all of the new results.
This one also goes right before the SetTimeout call. Always register your event handlers before the CoveoForSitecore.initSearchInterface call, otherwise, you can end up with race conditions, or if you are unlucky, it won't fire at all. This example also assumes your analytics data is already handy.
Coveo.$$(searchInterface).on('newResultDisplayed',
function(e, args) {
if(userData.raw.jobtitle.toLowerCase() == "developer" && args.result.raw.category.toLowerCase == "computer"){
var iconLocation = args.item.QuerySelector(".icon-container");
iconLocation.classList.add("special-interest");ฦ
}
});
This example uses hardcoded English strings for simplicity, but you could easily use Sitecore's dictionary strings here, or take advantage of the multi-language setting for the init files. All of the HTML of the item template is available here, fully filled in with data, so you can make any changes you need. If you need to change tabindex or aria-* settings for accessibility reasons, this is also the place to do it, after Coveo's default code has run.
Wrap up
That's it! Now you know how to add any customizations that depend on the state of all sorts of Sitecore data, make any styling that couldn't be done with pure CSS and handle different things for different languages.