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">

AutoCollapsedRefiners.zip