Provides easy way to register display options used to customize look and feel of the blocks inside your Optimizely content area (+ many more advanced features and whistles).
For Optimizely CMS v12 support please use master
branch.
- Getting Started
- Setup (Configuration)
- Available Built-in Display Options
- Display Option Fallbacks
- Advanced Features
- Forms Support
You would need to install package from Optimizely's NuGet feed to start using Optimizely Advanced ContentArea renderer:
> dotnet add package TechFellow.Optimizely.AdvancedContentArea
Next you would need to configure renderer by adding it to the application and specifying display options:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAdvancedContentArea(o =>
{
o.DisplayOptions = DisplayOptions.Default;
});
}
}
Or you can add your own diplsay options:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAdvancedContentArea(o =>
{
o.DisplayOptions = new List<DisplayModeFallback>
{
new()
{
Id = "three-fifth",
Name = "Three fifth (3/5)",
Tag = "displaymode-three-fifth",
ExtraExtraLargeScreenWidth = 7,
ExtraExtraLargeScreenCssClassPattern = "col-three-fifth-xxl-{0}",
ExtraLargeScreenWidth = 7,
ExtraLargeScreenCssClassPattern = "col-three-fifth-xl-{0}",
LargeScreenWidth = 7,
LargeScreenCssClassPattern = "col-three-fifth-lg-{0}",
MediumScreenWidth = 12,
MediumScreenCssClassPattern = "col-three-fifth-md-{0}",
SmallScreenWidth = 12,
SmallScreenCssClassPattern = "col-three-fifth-sm-{0}",
ExtraSmallScreenWidth = 12,
ExtraSmallScreenCssClassPattern = "col-three-fifth-xs-{0}",
Icon = "epi-icon__layout--three-fifth"
}
};
});
}
}
Following configuration options are available:
Name | Default | Description |
---|---|---|
DisplayOptions |
Empty list | Specify list of display options available for the renderer. Later editors will be able to choose any of these options while creating content and specifying dispay option for blocks. |
RowSupportEnabled |
false |
Configure if advanced content area renderer should support row option. |
AutoAddRow |
false |
Configure renderer to add automatically row CSS class to each new row div element. |
ItemStartRenderCallback |
null |
Callback to use to modify start tag for the content area items. |
Following display options are available by default (via DisplayOptions.Default
):
- "Full width (1/1)" (
displaymode-full
). - "Half width (1/2)" (
displaymode-half
). - "One-third width (1/3)" (
displaymode-one-third
). - "Two-thirds width (2/3)" (
displaymode-two-thirds
). - "One-quarter width (1/4)" (
displaymode-one-quarter
). - "Three-quarter width (3/4)" (
displaymode-three-quarters
).
For every display option there are 6 fallback width for various screen sizes based on Bootstrap grid system. According to Bootstrap specification following screen sizes are defined:
- Extra extra large screen (>= 1400px,
-xxl-
) - Extra large screen (>= 1200px,
-xl-
) - Large screen (>= 992px,
-lg-
) - Medium devices (>= 768px,
-md-
) - Small devices (>= 576px,
-sm-
) - Extra small devices (< 576px, None)
These numbers are added at the end of Bootstrap grid system class (for instance 12 for Large screen -> 'col-lg-12'
)
Display Mode Name | Extra small devices (xs) | Small devices (sm) | Medium devices (md) | Large screen (lg) | Extra large screen (xl) | Extra extra large screen (xxl) |
---|---|---|---|---|---|---|
Full width | 12 | 12 | 12 | 12 | 12 | 12 |
Half width | 12 | 12 | 6 | 6 | 6 | 6 |
One third | 12 | 12 | 6 | 4 | 4 | 4 |
Two thirds | 12 | 12 | 6 | 8 | 8 | 8 |
One quarter | 12 | 12 | 6 | 3 | 3 | 3 |
Three quarters | 12 | 12 | 6 | 9 | 9 | 9 |
Eventually if you choose Half-width (1/2)
display option for a block of type EditorialBlockWithHeader
following markup will be generated:
<div class="block editorialblockwithheader col-lg-6 col-md-6 col-sm-12 col-xs-12 displaymode-half">
...
</div>
Breakdown of added classes:
block
: generic class added to identify a block{block-name}
: name of the block type is added (in this caseEditorialBlockWithHeader
)col-xs-12
: block will occupy whole width of the screen on extra small devicescol-sm-12
: block will occupy whole width of the screen on small devicescol-md-6
: block will occupy one half of the screen on medium devicescol-lg-6
: block will occupy one half of the screen on desktopdisplaymode-half
: chosen display optiontag
is added
Let's take a look at One quarter width
block.
This is a block layout in Optimizely content area on-page edit mode (desktop view - large screen col-lg-3
):
This is a block layout in Optimizely content area on medium devices - col-md-6
:
This is a block layout in Optimizely content area on small and extra small devices - col-sm-12
and col-xs-12
:
If you need to support Boostrap row elements in Content Area, you can just render that area with "rowsupport"
parameter:
@Html.PropertyFor(m => m.MainContentArea, new { rowsupport = true })
For every collection of elements that fill up 12 columns - additional element (<div>
) will be wrapped around with class="row"
.
If you need to add custom Css class to your row
element, it's possible via ViewData
object. Pass in rowcssclass
parameter with desired class name:
@Html.PropertyFor(x => x.CurrentPage.MainContentArea,
new
{
rowsupport = true,
rowcssclass = "special-row"
})
Thanks to Jon Jones for copyright! If you have Content Area with single row and want to validate item count inside to match single Bootstrap row (12 columns), you just need to add [BootstrapRowValidation]
attribute:
public class StartPage : SitePageData
{
...
[BootstrapRowValidation]
public virtual ContentArea MainContentArea { get; set; }
Example: you add 2 blocks to the content area with 1/2
and 2/3
. In total it's 7/6
of the width - which exceeds full width columns 12/12
.
You can specify which display option to use if block is dropped inside content area and editor did not specify display option explicitly:
using TechFellow.Optimizely.AdvancedContentArea;
public static Class ContentAreaTags
{
public const string HalfWidth = "half-width";
}
[DefaultDisplayOption(ContentAreaTags.HalfWidth)]
public class SomeBlock : BlockData
{
...
}
This attribute will make sure that if block is dropped inside content area - display option registered with tag half-width
is used.
Also "tagged" blocks are supported:
using TechFellow.Optimizely.AdvancedContentArea;
[DefaultDisplayOptionForTag("ca-tag", ContentAreaTags.OneThirdWidth)]
public class SomeBlock : BlockData
{
...
}
The same attribute can be used in ContentArea property definition:
using TechFellow.Optimizely.AdvancedContentArea;
[ContentType(DisplayName...]
public class StandardPage : PageData
{
[DefaultDisplayOption(ContentAreaTags.HalfWidth)]
public virtual ContentArea MainContentArea { get; set; }
...
}
If you need to get index of the current block in the ContentArea, you are able to write just following line:
<div>
Index: @Html.BlockIndex()
</div>
Sometimes you would like to set display option that does nothing - none of the CSS classes would be added that could mess up your site design.
For this reason there is a new built-in display option - None
.
You can find it in :
public void ConfigureServices(IServiceCollection services)
{
services.AddAdvancedContentArea(o =>
{
o.DisplayOptions = new List<DisplayModeFallback>(DisplayOptions.Default)
{
DisplayModeFallback.None
};
});
}
If you set this display option on the block (in this example "Teaser Block"
in Alloy sample site) only following classes will be added to the container element:
<div class="block teaserblock displaymode-none">
<!-- block content -->
</div>
Similar to Optimizely AlloyTech sample site it's possible to define custom styles for block. You have to implement EPiBootstrapArea.ICustomCssInContentArea
interface.
using TechFellow.Optimizely.AdvancedContentArea;
[ContentType(GUID = "EED33EA7-D118-4D3D-BD7F-88C012DFA1F8", GroupName = SystemTabNames.Content)]
public class Divider : BaseBlockData, ICustomCssInContentArea
{
...
public string ContentAreaCssClass => "block-with-round-borders";
}
You will need to add few localization resource entries in order to get localized DisplayOptions. Following entry has to be added to get localized names for default display options:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<languages>
<language name="English" id="en">
<displayoptions>
<displaymode-full>Full (1/1)</displaymode-full>
<displaymode-half>Half (1/2)</displaymode-half>
<displaymode-one-third>One third (1/3)</displaymode-one-third>
<displaymode-two-thirds>Two thirds (2/3)</displaymode-two-thirds>
<displaymode-one-quarter>One quarter (1/4)</displaymode-one-quarter>
<displaymode-three-quarters>Three quarters (3/4)</displaymode-three-quarters>
</displayoptions>
</language>
</languages>
If there is a requirement to modify start element tag for the block (i.e. add id
attribute to element as shown in this blog post) you can do so by providing element's start tag modification callback:
public void ConfigureServices(IServiceCollection services)
{
services.AddAdvancedContentArea(o =>
{
o.ItemStartRenderCallback = (node, item, content) =>
{
// modify start element (eg. add id attribute or some dynamic CSS classes)
};
});
}
This will make sure that your registered AnotherBootstrapAwareContentAreaRenderer
renderer will be used instead of built-in one. And you will have chance to modify start element of the block before it's sent to the output writer.'
NB! You have to use Intercept
method to overwrite renderer (just registering new implementation for ContentAreaRenderer
did not do the trick).
By default Optimizely will generate wrapping element around content area (div
tag name is actually controllable as well, more info here):
@Html.PropertyFor(m => m.PageHeaderArea)
Resulting in:
<div> <!-- CA wrapper element -->
<div ...> <!-- Block element -->
<...> <!-- Actual content of the block -->
</div>
</div>
Optimizely gives you an option to skip wrapper element generation - resulting only in set of blocks added to the content area.
@Html.PropertyFor(m => m.PageHeaderArea, new { HasContainer = false })
Resulting in:
<div ...> <!-- Block element -->
<...> <!-- Actual content of the block -->
</div>
However, we still see that wrapping <div>
element is not desired in <head>
area.
Looking for the best place to add feature to skip even further - not to generate block wrapping element, but just content of the block.. Content area renderer is perfect candidate for this functionality.
You can now write like this:
@Html.PropertyFor(m => m.PageHeaderArea,
new
{
HasContainer = false,
HasItemContainer = false
})
Resulting in:
<...> <!-- Actual content of the block -->
If you use this approach to render elements for instance in head section, you might run into problems ending with invalid markup and Optimizely is adding edit container if property is rendered inside Edit Mode. To avoid this, you need to include additional parameter - HasEditContainer = false
@Html.PropertyFor(m => m.PageHeaderArea,
new
{
HasContainer = false,
HasItemContainer = false,
HasEditContainer = false
})
More info here: https://tech-fellow.eu/2023/02/11/optimizely-forms-advanced-contentarea-renderer-is-back/