Kentico Page Builder Section: One to Rule Them All (Variable Section)

In this blog post, Ian explains how to utilize Kentico 13’s property visibility conditions to only show the properties that content admins need to see.

Iterating upon one of my previous blog posts about complex sections and widgets has put me on an unexpected journey to build one Page Builder section to rule them all. One section for almost every layout a content administrator could ask for. I’m utilizing Kentico 13’s property visibility conditions to only show the properties that content admins need to see.

Gold ring on fire

The Shire (What I Started With)

The Generic Column Section was a good starting point. One section that allows up to five columns with equal widths. An initial improvement I made to this section was adding an option to allow a sixth column.

I made another section to handle any percentage of two-column layouts. This section replaced the need for 30/70, 40/60, 50/50, or any other combination of two columns. As long as the content admin configures the column widths to be at or below 100%, then everything will look good.

In my head, I wanted to make a three-column adjustable section similar to the two-column one. With those three sections, I think most layouts would be covered.

The Fellowship (The Client Request)

Fellowship of the Ring group

After upgrading a client’s site to K13 they wanted to start utilizing page builder. Their first request was to make one section that handled the previously three mentioned sections and even more features all in one section. They wanted each column to have its own border styles, padding, and text color properties. Initially, I was hesitant to the request because the sheer amount of properties on this section would be overwhelming to content admins. I suggested breaking this into three sections, but they were adamant about keeping everything in one.

Forging the Section

melting metal

With the acceptance criteria defined, I set forth on my journey to forge them the section they desired. I began with defining all the properties in the VariableSectionProperties.cs class.

using System.Collections.Generic;
using Kentico.Forms.Web.Mvc;
using Kentico.PageBuilder.Web.Mvc;
using Client.Core.Constants.PageBuilder.Sections;
using Client.Web.Models.PageBuilder.Sections;
using Client.Web.Models.PageBuilder.VisibilityConditions;

[assembly: RegisterSection(
    “Vermeer.VariableSection”,
    “Variable Section”,
    typeof(VariableSectionProperties),
    customViewName: “PageBuilder/Sections/_VariableSection”,
    IconClass = “icon-layout”)]

Namespace Client.Web.Models.PageBuilder.Sections
{
    public class VariableSectionProperties : BaseSectionProperties, ISectionProperties
    {
        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 0, Label = "Number of Columns")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.NumberColumnOptions)]
        public string ColumnCount { get; set; } = "1";

        #region Column #1
        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 1, Label = "Column #1 Width (%)", Tooltip = VariableSection.AdjustableColumnTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 2)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 3)]
        public int? Column1Width { get; set; }

        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 2, Label = "Column #1 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        public string? Column1BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 3, Label = "Column #1 Padding (px)")]
        public int Column1Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 4, Label = "Column #1 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        public string? Column1TextColor { get; set; }
        #endregion

        #region Column #2
        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 5, Label = "Column #2 Width (%)", Tooltip = VariableSection.AdjustableColumnTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 2)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 3)]
        public int? Column2Width { get; set; }

        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 6, Label = "Column #2 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 2)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column2BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 7, Label = "Column #2 Padding (px)")]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 2)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public int Column2Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 8, Label = "Column #2 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 2)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column2TextColor { get; set; }
        #endregion

        #region Column #3
        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 9, Label = "Column #3 Width (%)", Tooltip = VariableSection.AdjustableColumnTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 3)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 3)]
        public int? Column3Width { get; set; }

        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 10, Label = "Column #3 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 3)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column3BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 11, Label = "Column #3 Padding (px)")]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 3)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public int Column3Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 12, Label = "Column #3 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 3)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column3TextColor { get; set; }
        #endregion

        #region Column #4
        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 13, Label = "Column #4 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 3)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column4BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 14, Label = "Column #4 Padding (px)")]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 4)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public int Column4Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 15, Label = "Column #4 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 4)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column4TextColor { get; set; }
        #endregion

        #region Column #5
        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 16, Label = "Column #5 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 5)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column5BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 17, Label = "Column #5 Padding (px)")]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 5)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public int Column5Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 18, Label = "Column #5 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 5)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column5TextColor { get; set; }
        #endregion

        #region Column #6
        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 19, Label = "Column #6 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 6)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column6BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 20, Label = "Column #6 Padding (px)")]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 6)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public int Column6Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 21, Label = "Column #6 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 6)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column6TextColor { get; set; }
        #endregion
    }
}

I leveraged the Builder Visibility Conditions to ensure only the required properties display based on the ColumnCount dropdown. For example, when the user selects 2-3 columns, those columns will have a width property displaying, but when a user selects 4 or above, the custom visibility conditions tell those width properties they shall not pass, hiding them. 4-6 columns are all equal-width columns based on the client’s request.

The BaseSectionProperties is where I put properties that are shared across any section I build. Properties like margin, padding, IsFullBleed, and background color are in this file.

using System.Collections.Generic;
using Kentico.Forms.Web.Mvc;
using Kentico.PageBuilder.Web.Mvc;
using Vermeer.Core.Constants.PageBuilder.Sections;
using Vermeer.Web.Models.PageBuilder.Sections;
using Vermeer.Web.Models.PageBuilder.VisibilityConditions;

[assembly: RegisterSection(
    "Vermeer.VariableSection",
    "Variable Section",
    typeof(VariableSectionProperties),
    customViewName: "PageBuilder/Sections/_VariableSection",
    IconClass = "icon-layout")]

namespace Vermeer.Web.Models.PageBuilder.Sections
{
    public class VariableSectionProperties : BaseSectionProperties, ISectionProperties
    {
        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 0, Label = "Number of Columns")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.NumberColumnOptions)]
        public string ColumnCount { get; set; } = "1";

        #region Column #1
        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 1, Label = "Column #1 Width (%)", Tooltip = VariableSection.AdjustableColumnTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 2)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 3)]
        public int? Column1Width { get; set; }

        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 2, Label = "Column #1 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        public string? Column1BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 3, Label = "Column #1 Padding (px)")]
        public int Column1Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 4, Label = "Column #1 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        public string? Column1TextColor { get; set; }
        #endregion

        #region Column #2
        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 5, Label = "Column #2 Width (%)", Tooltip = VariableSection.AdjustableColumnTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 2)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 3)]
        public int? Column2Width { get; set; }

        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 6, Label = "Column #2 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 2)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column2BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 7, Label = "Column #2 Padding (px)")]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 2)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public int Column2Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 8, Label = "Column #2 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 2)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column2TextColor { get; set; }
        #endregion

        #region Column #3
        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 9, Label = "Column #3 Width (%)", Tooltip = VariableSection.AdjustableColumnTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 3)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 3)]
        public int? Column3Width { get; set; }

        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 10, Label = "Column #3 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 3)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column3BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 11, Label = "Column #3 Padding (px)")]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 3)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public int Column3Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 12, Label = "Column #3 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 3)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column3TextColor { get; set; }
        #endregion

        #region Column #4
        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 13, Label = "Column #4 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 3)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column4BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 14, Label = "Column #4 Padding (px)")]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 4)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public int Column4Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 15, Label = "Column #4 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 4)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column4TextColor { get; set; }
        #endregion

        #region Column #5
        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 16, Label = "Column #5 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 5)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column5BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 17, Label = "Column #5 Padding (px)")]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 5)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public int Column5Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 18, Label = "Column #5 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 5)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column5TextColor { get; set; }
        #endregion

        #region Column #6
        [EditingComponent(TextInputComponent.IDENTIFIER, Order = 19, Label = "Column #6 Border Style", Tooltip = VariableSection.BorderStyleTooltip)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 6)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column6BorderStyle { get; set; }

        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 20, Label = "Column #6 Padding (px)")]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 6)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public int Column6Padding { get; set; }

        [EditingComponent(DropDownComponent.IDENTIFIER, Order = 21, Label = "Column #6 Text Color")]
        [EditingComponentProperty(nameof(DropDownProperties.DataSource), VariableSection.TextColorOptions)]
        [VisibilityCondition(nameof(ColumnCount), typeof(VariableSectionColumnVisibilityCondition))]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.LowRange), 6)]
        [VisibilityConditionProperty(nameof(VariableSectionColumnVisibilityCondition.HighRange), 6)]
        public string? Column6TextColor { get; set; }
        #endregion

        public int ColumnCountParsed => int.Parse(ColumnCount);

        public string ColClass => ColumnCountParsed switch
        {
            1 => "one-column",
            2 => "two-column",
            3 => "three-column",
            4 => "four-column",
            5 => "five-column",
            _ => "six-column"
        };

        public string SetColumnStyles(int colNumber) => colNumber switch
        {
            1 => FormatColumnStyles(Column1Width, Column1BorderStyle, Column1Padding, Column1TextColor),
            2 => FormatColumnStyles(Column2Width, Column2BorderStyle, Column2Padding, Column2TextColor),
            3 => FormatColumnStyles(Column3Width, Column3BorderStyle, Column3Padding, Column3TextColor),
            4 => FormatColumnStyles(null, Column4BorderStyle, Column4Padding, Column4TextColor),
            5 => FormatColumnStyles(null, Column5BorderStyle, Column5Padding, Column5TextColor),
            6 => FormatColumnStyles(null, Column6BorderStyle, Column6Padding, Column6TextColor),
            _ => string.Empty
        };

        private static string FormatColumnStyles(int? width, string? border, int? padding, string? textColor)
        {
            var widthStyle = width != null ? $"flex-basis: {width}%;" : string.Empty;
            var borderStyle = !string.IsNullOrWhiteSpace(border) ? $"border: {border};" : string.Empty;
            var paddingStyle = padding != null ? $"padding: {padding}px;" : string.Empty;
            var textColorStyle = !string.IsNullOrWhiteSpace(textColor) ? $"color: {textColor};" : string.Empty;

            return $"{widthStyle}{borderStyle}{paddingStyle}{textColorStyle}";
        }
    }
}

The custom visibility condition uses a range value to display properties. The DependeePropertyValue is the ColumnCount property value. As content admins update the ColumnCount the properties will update dynamically.

using Kentico.Forms.Web.Mvc;

namespace Client.Web.Models.PageBuilder.VisibilityConditions
{
    public class VariableSectionColumnVisibilityCondition : AnotherPropertyVisibilityCondition<string>
    {
        public int? LowRange { get; set; }
        public int? HighRange { get; set; }

        public override bool IsVisible()
        {
            int.TryParse(DependeePropertyValue, out var dependeePropertyValue);

            if (dependeePropertyValue >= LowRange && dependeePropertyValue <= HighRange)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

Rendering the section is about as easy as Frodo’s quest would’ve been if Gandalf called upon the Eagles from the beginning. I removed bootstrap from the Generic Column Section I started with. Now we’re using flexbox with custom classes to be able to override the default column widths easier.

@model ComponentViewModel<VariableSectionProperties>

<section class="variable-section @Model.Properties.BackgroundColor"
         style="@Model.Properties.SectionStyles">
    <div class="@Model.Properties.ContainerClass">
        <div class="@(Model.Properties.IsFullBleed ? "full-bleed-row" : "row")">
            @for ( int i = 0; i < @Model.Properties.ColumnCountParsed; i++ )
            {
                <div class="@Model.Properties.ColClass" style="@Model.Properties.SetColumnStyles(i + 1)">
                    @await Html.Kentico().WidgetZoneAsync()
                </div>
            }
        </div>
    </div>
</section>

In the stylesheet for the variable section, we’re utilizing media queries to set an override for mobile styling. The 1-3 column(s) are stacking and having their width set to 100%, and the 4-6 columns are stacking in a two-column layout.

.variable-section {
  &.black-background {
    background-color: $bg-black-transparent;
  }

  &.gray-background {
    background-color: $bg-gray-transparent;
  }

  &.primary-background {
    background-color: $bg-primary-transparent;
  }

  &.secondary-background {
    background-color: $bg-secondary-transparent;
  }

  .full-bleed-row,
  .row {
    display: flex;
    justify-content: space-between;

    @include tablet-max {
      margin: 0;

      .one-column,
      .two-column,
      .three-column {
        flex-basis: 100% !important;
      }

      .four-column,
      .five-column,
      .six-column {
        flex-basis: 49% !important;
      }
    }

    @include desktop-narrow() {
      .one-column {
        flex-basis: 100%;
      }

      .two-column {
        flex-basis: 49%;
      }

      .three-column {
        flex-basis: 32%;
      }

      .four-column {
        flex-basis: 24%;
      }

      .five-column {
        flex-basis: 19%;
      }

      .six-column {
        flex-basis: 15%;
      }
    }
  }
}

The Future of the Variable Section

I think this variable section is a great foundation to keep iterating on for years to come. I have a few thoughts on how to expand the functionality of this section. One of the features I would like to add is a IsAccordion boolean to the section and each column in the section.

I also plan to add this section to the Kentico Marketplace and GitHub. I’ll probably look into doing this after adding the additional features.

About the Author

Ian TeGrootenhuis

Ian was raised in Allendale, Michigan, and has lived there his whole life. He loves the location; being a few minutes from Lake Michigan and downtown Grand Rapids in a small town is perfect. From an early age, he fell in love with video games and wanted to develop them. In pursuit of following his dream, he found Bizstream Academy and signed up to gain some coding experience. Ian loved the Academy so much that his passion changed to web development. In his spare time, Ian still plays video games.

Subscribe to Our Blog

Stay up to date on what BizStream is doing and keep in the loop on the latest in marketing & technology.