Auto collapse multi-valued refiners in the SharePoint 2013 search refinement panel
My client asked if it was possible to auto-collapse the refinement panel and I thought to myself... that should be a setting right? Probably something that I had just never used or needed, it's got to be there.
No.
So I found this great post that gives some good instruction on how to modify Filter_Default.html to allow single valued refiners to appear collapsed when the page is initially rendered. Awesome, 1 down.
But this didn't work for multi valued refiners because they use a different template, well... actually 2 templates, Filter_MultiValue.html and Filter_MultiValue_Body.html. Ok, simple enough, we just have to make some mods to both files. First, we edit Filter_MultiValue.html to add a line to set its defaulted state to collapsed.
var hasNoListData = ($isEmptyArray(listData));
var propertyName = ctx.RefinementControl.propertyName;
var displayTitle = Srch.Refinement.getRefinementTitle(ctx.RefinementControl);
var isExpanded = Srch.Refinement.getExpanded(ctx.RefinementControl.propertyName);
becomes
var hasNoListData = ($isEmptyArray(listData));
var propertyName = ctx.RefinementControl.propertyName;
var displayTitle = Srch.Refinement.getRefinementTitle(ctx.RefinementControl);
Srch.Refinement.setExpanded(ctx.RefinementControl.propertyName, false);
var isExpanded = Srch.Refinement.getExpanded(ctx.RefinementControl.propertyName);
Now on to Filter_MultiValue_Body.html, and before I go much further, I’ve found what I consider a bug but generally, it would never be noticed because the default state for any refiner is expanded.
Notice in Filter_MultiValue.html the line
var isExpanded = Srch.Refinement.getExpanded(ctx.RefinementControl.propertyName);
Now you would think that this function would return a boolean type variable, but it doesn’t, it’s a string.
Srch.Refinement.getExpanded = function (f) {
var e = "refinementExpandCookieName_" + f,
a = document.cookie;
if (!Srch.U.e(a)) {
var b = a.indexOf(e + "=");
if (b !== -1) {
b = b + e.length + 1;
var d = a.indexOf(";", b);
if (d === -1) d = a.length;
var c = a.substring(b, d);
if (!Srch.U.n(c)) c = unescape(c);
return c
}
}
return"true"
};
Now take a look at the bottom of the file where our isExpanded string variable is saved for use in Filter_MultiValue_Body.html, the value of isExpanded is a string value of “false”.
ctx.RefinementControl["csr_isExpanded"] = isExpanded;
Now let’s take a look at Filter_MultiValue_Body.html, notice the line
var isExpanded = Boolean(ctx.RefinementControl["csr_isExpanded"]);
The JavaScript “Boolean” function when given a single string as an argument will return false only if the string is blank, this means that a string with a value of “false” will return true. So, to be clear, any string with 1 or more characters will return true from the “Boolean” function. So you can see the problem, upon rendering, the isExpanded variable in Filter_MultiValue_Body.html is always true.
I ran into another issue that was really just unexpected behavior, when you would select a few items to refine on, then click the "Apply" button, the entire refiner would hide itself again. Duh, that’s what I told it to do. I had to add some code to determine if there were some refiners selected and override the collapsed state.
Anyway, here’s the code in Filter_MultiValue_Body.html.
<!--#_
var propertyName = ctx.RefinementControl["csr_propertyName"];
var displayTitle = ctx.RefinementControl["csr_displayTitle"];
var filters = ctx.RefinementControl["csr_filters"];
var isExpanded = Boolean(ctx.RefinementControl["csr_isExpanded"]);
var renderEmptyContainer = Boolean(ctx.RefinementControl["csr_renderEmptyContainer"]);
var useContains = Boolean(ctx.RefinementControl["csr_useContains"]);
var useKQL = Boolean(ctx.RefinementControl["csr_useKQL"]);
var showCounts = Boolean(ctx.RefinementControl["csr_showCounts"]);
if($isEmptyString(propertyName) || (!$isNull(renderEmptyContainer) && renderEmptyContainer))
{
_#-->
<divid="EmptyContainer"></div>
<!--#_
}
else if(!$isNull(filters) && Srch.U.isArray(filters) && !$isEmptyArray(filters))
{
var expandedStatus = !$isNull(isExpanded) ? isExpanded : true;
var iconClass = "ms-core-listMenu-item ";
iconClass += expandedStatus ? "ms-ref-uparrow" : "ms-ref-downarrow";
_#-->
<divid="Container">
_#= Srch.U.collapsibleRefinerTitle(propertyName, ctx.ClientControl.get_id(), displayTitle, iconClass) =#_
<divclass="ms-ref-unselSec" id="UnselectedSection">
<divid="unselShortList" class="ms-ref-unsel-shortList">
Becomes
<!--#_
var propertyName = ctx.RefinementControl["csr_propertyName"];
var displayTitle = ctx.RefinementControl["csr_displayTitle"];
var filters = ctx.RefinementControl["csr_filters"];
var isExpanded = ctx.RefinementControl["csr_isExpanded"];
var renderEmptyContainer = Boolean(ctx.RefinementControl["csr_renderEmptyContainer"]);
var useContains = Boolean(ctx.RefinementControl["csr_useContains"]);
var useKQL = Boolean(ctx.RefinementControl["csr_useKQL"]);
var showCounts = Boolean(ctx.RefinementControl["csr_showCounts"]);
if($isEmptyString(propertyName) || (!$isNull(renderEmptyContainer) && renderEmptyContainer))
{
_#-->
<divid="EmptyContainer"></div>
<!--#_
}
else if(!$isNull(filters) && Srch.U.isArray(filters) && !$isEmptyArray(filters))
{
var expandedStatus = !$isNull(isExpanded) ? (isExpanded == "true" ? true : false) : true;
if (!expandedStatus) {
for (var i = 0; i < filters.length; i++) {
var filter = filters[i];
if(!$isNull(filter)) {
if (Boolean(filter.IsSelected) == true)
{
expandedStatus = true;
break;
};
}
}
}
var iconClass = "ms-core-listMenu-item ";
iconClass += expandedStatus ? "ms-ref-uparrow" : "ms-ref-downarrow";
var displayStyle = expandedStatus ? "" : "none";
_#-->
<divid="Container">
_#= Srch.U.collapsibleRefinerTitle(propertyName, ctx.ClientControl.get_id(), displayTitle, iconClass) =#_
<divclass="ms-ref-unselSec" id="UnselectedSection"style='display:_#=$htmlEncode(displayStyle)=#_'>
<divid="unselShortList" class="ms-ref-unsel-shortList">