
Defining the storefront assets for Commerce Composer layout templates
You must define the storefront assets for a layout template before your custom template can be used on the storefront. The storefront components for the template define the container for the widget and the configurable slots within the container. The storefront components consist of the definition of the template container and placement of the template slots, the environment setup file.
For more information about the overall creation of a layout template, see Creating Commerce Composer layout templates.
Procedure
-
In the Enterprise Explorer view within HCL Commerce Developer, create a directory to
contain the code assets for your custom layout templates.
- Go to the WCDE_installdir\workspace\crs-web\WebContent\Aurora directory.
-
If the directory does not exist within the Aurora directory, create the
Container-vendor directory. Where vendor is the name
of your company.
For example, the name for your directory can be Container-MyCompany
-
Create the container JSP file for your custom layout template. The container JSP file defines
the template, which is an empty widget that is divided into configurable slots. The container
definition is similar to the definition of a widget, but does not have any functionality or content
that is defined for use on the storefront. The container identifies the configurable slots where a
widget can be added and marks these slots with the
wcpgl:widgetImport
tag. Each slot is defined by an internal slot ID, which must be unique within the container. The container JSP file also identifies the environment setup file.- Go to the WCDE_installdir\workspace\crs-web\WebContent\Aurora\Container directory.
- Copy the StaticContentPageDisplayContainer.jsp file. This file is the container JSP file for the Any page, single slot layout template that is provided by default with HCL Commerce.
- Go to your WCDE_installdir\workspace\crs-web\WebContent\Aurora\Container-vendor directory and add the copied JSP file into this directory.
-
Rename the JSP file and open the file for editing.
For example, you can rename the file MyCustomLayoutTemplate.jsp.By default, the contents of the container JSP file resembles the following code.
Where1<%@include file="../Common/EnvironmentSetup.jspf" %> <%@taglib uri="http://commerce.ibm.com/pagelayout" prefix="wcpgl"%> 2<div class="rowContainer" id="container_${pageDesign.layoutID}"> 3<div class="row"> 4<div class="col12" data-slot-id="1"><wcpgl:widgetImport slotId="1"/></div> </div> </div>
- 1 The environment setup file. This configuration file is used at run time. This file retrieves and prepares the JSP file path and resource bundle for the container. The configuration file is used to identifies the values of the environment configurations for the Commerce Composer framework. The file identifies the values for any prefix variables, common context file paths, page encoding setting, and the page to display as an error page. This file is called by many JSP files in a store and is shared by multiple containers. By default, the WCDE_installdir\workspace\crs-web\WebContent/Aurora/Common/EnvironmentSetup.jspf file is specified.
- 2 Container division element. This element is the beginning of the
code that defines the overall layout template container. Include the definitions for all rows and
slots within this
rowContainer
element. For theid
attribute, the value for all layout templates is"container_${pageDesign.layoutID}"
. The value for the ID is generated when the layout template is used to create a layout. The value of the new layout ID is retrieved as the${pageDesign.layoutID}
portion of the value for thisid
attribute. For theclass
attribute, include"rowContainer"
as part of the attribute value to help you identify the division within the corresponding CSS file. For example, you can rename the class value to be"MyCustomTemplate_tab_content rowContainer"
. The value for this attribute must map to an entry within a corresponding CSS file to define the rendering of the template on the storefront. - 3 The row division element. Include more
<div class="row">
elements to design your layout template to include multiple rows of slots. The value for theclass
attribute must match an entry within a corresponding CSS file. - 4 The column division element. This element is used to define a slot within a row. By
default the Any page, single slot layout template includes a single slot
within one row. To define the slot, you must define the
class
anddata-slot-id
attributes.The
class
attribute identifies the CSS entry that defines display information for the template slot. The values for theclass
attribute in the following steps specify CSS entries that are defined by default for template columns. The CSS file that includes the CSS definitions for layout templates is defined in the WCDE_installdir\workspace\crs-web\WebContent\Aurora\css\base.css file. By default, the CSS for a layout template uses a 12-column fluid grid system that divides a page into rows and columns. The class value"col12"
entry, defines a slot to have a width that equals 100% of the page width, or as wide as all 12 columns.TheFor example, the following code snippet defines the first three slots within a template. All of the slots are included within a single row.data-slot-id
attribute defines theslotId
for the slot within your layout template. This ID displays in Management Center for the template slot wireframe to help users identify and select the slots within a template to include widgets within a layout. The value for thedata-slot-id
must use the following format
Where the specified numerical value is the ID of the slot that the column division element is defining. For each additional slot that you include within a template, it is recommended that you increment the slot ID value in sequential order.data-slot-id="1"><wcpgl:widgetImport slotId="1"/>
The class value<div class="row"> <div class="col4 acol12" data-slot-id="1"><wcpgl:widgetImport slotId="1"/></div> <div class="col4 acol12" data-slot-id="2"><wcpgl:widgetImport slotId="2"/></div> <div class="col4 acol12" data-slot-id="3"><wcpgl:widgetImport slotId="3"/></div> </div>
"col4 acol12"
entry, maps to thecol4
andacol12
entries in the base.css file. Thecol4
entry defines a slot width to be equal to a third of the total page width when the page is viewed in a browser with a page range greater than 600 pixels. Theacol12
entry defines the slot width to be equal to 100% of the page width when the page is viewed at a page range fewer than 600 pixels. When this fewer 600 pixel breakpoint is encountered, the slots within the layout template rearrange on the storefront to stack vertically so that the slots can show at the defined width. This behavior ensures that the layout template is responsive. For more information about the CSS framework for the fluid grid system that makes up a layout template, see Responsive Web Design (RWD) framework.
-
Update the container JSP file for your layout to divide you layout into more rows and
columns.
To divide your template into more rows, include more row division elements. The following code snippet divides the MyCustomLayoutTemplate.jsp container JSP into three rows. The first row is divided into two columns and, the second row into four columns. The third row includes a single column.
<%@include file="../Common/EnvironmentSetup.jspf" %> <%@taglib uri="http://commerce.ibm.com/pagelayout" prefix="wcpgl"%> <div class="rowContainer" id="container_${pageDesign.layoutID}"> <div class="row"> <div class="col6 acol12" data-slot-id="1"><wcpgl:widgetImport slotId="1"/></div> <div class="col6 acol12" data-slot-id="2"><wcpgl:widgetImport slotId="2"/></div> </div> <div class="row"> <div class="col3 acol12" data-slot-id="3"><wcpgl:widgetImport slotId="3"/></div> <div class="col3 acol12" data-slot-id="4"><wcpgl:widgetImport slotId="4"/></div> <div class="col3 acol12" data-slot-id="5"><wcpgl:widgetImport slotId="5"/></div> <div class="col3 acol12" data-slot-id="6"><wcpgl:widgetImport slotId="6"/></div> </div> <div class="row"> <div class="col12" data-slot-id="7"><wcpgl:widgetImport slotId="7"/></div> </div> </div>
-
Update the container JSP to include any tabbed slots that you want within your layout
template.
-
To include tabbed slots within your template, you must include the
ProductTab.js JavaScript file within your container JSP file.
This file defines the interactions that a shopper can perform to switch between tabs and defines how the tabbed slots respond. To include this file, add the following code after the include statements for the environment setup file and the tag library
<script type="text/javascript" src="${jsAssetsDir}javascript/Widgets/ProductTab/ProductTab.js"></script>
-
Update the definition for the row that you want to include tabbed slots to include the logic to
identify the number of tabbed slots and to retrieve the slot ID values for the slots.
Within the row definition, replace the division elements for any columns that you want to be tabbed slots with the following code. The following code defines the number of tabbed slots and the logic that is used to retrieve the ID values for the tabbed slots.
Where<wcf:useBean var="tabSlotIds" classname="java.util.ArrayList"/> <%-- Double loop to get the slots into the array list in proper order. The service does not return the child widgets in any predictable order. --%> <c:set var="tabSlotCount" value="0"/> 1 <c:forEach var="slotNumber" begin="3" end="6"> <c:set var="foundCurrentSlot" value="false"/> <c:forEach var="childWidget" items="${pageDesign.widget.childWidget}"> <c:if test="${childWidget.slot.internalSlotId == slotNumber && !foundCurrentSlot}"> <wcf:set target="${tabSlotIds}" value="${slotNumber}" /> <c:set var="foundCurrentSlot" value="true"/> <c:set var="tabSlotCount" value="${tabSlotCount+1}"/> </c:if> </c:forEach> </c:forEach>
- 1 Defines the beginning and end of the tabbed slots to set the
number of tabbed slots. The preceding code snippet defines the row of tabbed slots to include the
slots with the slot ID values
3
,4
,5
, and6
.
Add the code to define the titles for the tabbed slots. Append the following code after the code for defining the number of tabbed slots.
Where<div class="tabButtonContainer" role="tablist"> <div class="tab_header tab_header_double"> <c:forEach var="tabSlotId" items="${tabSlotIds}" varStatus="status"> 1<c:set var="tabSlotName" value="Title${tabSlotId}"/> 2<c:forEach var="childWidget" items="${pageDesign.widget.childWidget}"> <c:if test="${childWidget.slot.internalSlotId == tabSlotName}"> <c:set var="tabWidgetDefIdentifier" value="${childWidget.widgetDefinitionIdentifier.uniqueID}"/> <c:set var="tabWidgetIdentifier" value="${childWidget.widgetIdentifier.uniqueID}"/> </c:if> </c:forEach> 3<c:choose> <c:when test="${status.first}"> <c:set var="tabClass" value="tab_container active_tab" /> <c:set var="tabIndex" value="0" /> </c:when> <c:otherwise> <c:set var="tabClass" value="tab_container inactive_tab" /> <c:set var="tabIndex" value="-1" /> </c:otherwise> </c:choose> <c:set var="tabNumber" value="${status.index+1}" scope="request"/> 4<div id="tab${status.count}" tabindex="${tabIndex}" class="tab_container ${tabClass}" aria-labelledby="contentRecommendationWidget_${tabSlotName}_${tabWidgetDefIdentifier}_${tabWidgetIdentifier}" aria-controls="tab${status.count}Widget" onfocus="ProductTabJS.focusTab('tab${status.count}');" onblur="ProductTabJS.blurTab('tab${status.count}');" role="tab" aria-setsize="${tabSlotCount}" aria-posinset="${status.count}" aria-selected="${status.first == true ? 'true' : 'false'}" onclick="ProductTabJS.selectTab('tab${status.count}');" onkeydown="ProductTabJS.selectTabWithKeyboard('${status.count}','${tabSlotCount}', event);"> 5<wcpgl:widgetImport slotId="${tabSlotName}"/> </div> <c:remove var="tabNumber"/> </c:forEach> </div> </div>
- 1 Sets the title for the tab slot as the
tabSlotName
- 2 The code also defines the logic for returning the widgets within a tabbed slot in the correct order.
- 3 Sets which tab to display when a shopper initially views a page that uses this layout template
- 4 Calls the JavaScript functions defined within the ProductTab.js file. These functions define how the page layout changes between tabs.
- 5 The code to import the Text Editor widget to use as the value for
the
tabSlotName
that displays in the storefront as the title for the tabbed slot.
Add the code to define the retrieval of content for display in the tabbed slots. Append the following code after the code to define the tabbed slot titles.<c:forEach var="tabSlotId" items="${tabSlotIds}" varStatus="status"> <c:set var="tabStyle" value=""/> <c:if test="${!status.first}"> <c:set var="tabStyle" value="style='display:none'" /> </c:if> <div role="tabpanel" class="tab left" data-slot-id="${tabSlotId}" id="tab${status.count}Widget" aria-labelledby="tab${status.count}" ${tabStyle}> <div class="content"> <wcpgl:widgetImport slotId="${tabSlotId}"/> </div> </div> <c:remove var="tabStyle"/> </c:forEach>
- 1 Defines the beginning and end of the tabbed slots to set the
number of tabbed slots. The preceding code snippet defines the row of tabbed slots to include the
slots with the slot ID values
-
To include tabbed slots within your template, you must include the
ProductTab.js JavaScript file within your container JSP file.
-
Save your container JSP file.
Your updated JSP file can resemble the following code, which defines the Any page, seven slots, tabs layout template.
<%@include file="../Common/EnvironmentSetup.jspf" %> <%@taglib uri="http://commerce.ibm.com/pagelayout" prefix="wcpgl"%> <script type="text/javascript" src="${jsAssetsDir}javascript/Widgets/ProductTab/ProductTab.js"></script> <div class="rowContainer" id="container_${pageDesign.layoutID}"> <div class="row"> <div class="col6 acol12" data-slot-id="1"><wcpgl:widgetImport slotId="1"/></div> <div class="col6 acol12" data-slot-id="2"><wcpgl:widgetImport slotId="2"/></div> </div> <div class="row margin-true"> <div class="col12 acol12 ccol12 right"> <wcf:useBean var="tabSlotIds" classname="java.util.ArrayList"/> <%-- Double loop to get the slots into the array list in proper order. The service does not return the child widgets in any predictable order. --%> <c:set var="tabSlotCount" value="0"/> <c:forEach var="slotNumber" begin="3" end="6"> <c:set var="foundCurrentSlot" value="false"/> <c:forEach var="childWidget" items="${pageDesign.widget.childWidget}"> <c:if test="${childWidget.slot.internalSlotId == slotNumber && !foundCurrentSlot}"> <wcf:set target="${tabSlotIds}" value="${slotNumber}" /> <c:set var="foundCurrentSlot" value="true"/> <c:set var="tabSlotCount" value="${tabSlotCount+1}"/> </c:if> </c:forEach> </c:forEach> <div class="tabButtonContainer" role="tablist"> <div class="tab_header tab_header_double"> <c:forEach var="tabSlotId" items="${tabSlotIds}" varStatus="status"> <c:set var="tabSlotName" value="Title${tabSlotId}"/> <c:forEach var="childWidget" items="${pageDesign.widget.childWidget}"> <c:if test="${childWidget.slot.internalSlotId == tabSlotName}"> <c:set var="tabWidgetDefIdentifier" value="${childWidget.widgetDefinitionIdentifier.uniqueID}"/> <c:set var="tabWidgetIdentifier" value="${childWidget.widgetIdentifier.uniqueID}"/> </c:if> </c:forEach> <c:choose> <c:when test="${status.first}"> <c:set var="tabClass" value="tab_container active_tab" /> <c:set var="tabIndex" value="0" /> </c:when> <c:otherwise> <c:set var="tabClass" value="tab_container inactive_tab" /> <c:set var="tabIndex" value="-1" /> </c:otherwise> </c:choose> <c:set var="tabNumber" value="${status.index+1}" scope="request"/> <div id="tab${status.count}" tabindex="${tabIndex}" class="tab_container ${tabClass}" aria-labelledby="contentRecommendationWidget_${tabSlotName}_${tabWidgetDefIdentifier}_${tabWidgetIdentifier}" aria-controls="tab${status.count}Widget" onfocus="ProductTabJS.focusTab('tab${status.count}');" onblur="ProductTabJS.blurTab('tab${status.count}');" role="tab" aria-setsize="${tabSlotCount}" aria-posinset="${status.count}" aria-selected="${status.first == true ? 'true' : 'false'}" onclick="ProductTabJS.selectTab('tab${status.count}');" onkeydown="ProductTabJS.selectTabWithKeyboard('${status.count}','${tabSlotCount}', event);"> <wcpgl:widgetImport slotId="${tabSlotName}"/> </div> <c:remove var="tabNumber"/> </c:forEach> </div> </div> <c:forEach var="tabSlotId" items="${tabSlotIds}" varStatus="status"> <c:set var="tabStyle" value=""/> <c:if test="${!status.first}"> <c:set var="tabStyle" value="style='display:none'" /> </c:if> <div role="tabpanel" class="tab left" data-slot-id="${tabSlotId}" id="tab${status.count}Widget" aria-labelledby="tab${status.count}" ${tabStyle}> <div class="content"> <wcpgl:widgetImport slotId="${tabSlotId}"/> </div> </div> <c:remove var="tabStyle"/> </c:forEach> </div> </div> <div class="row"> <div class="col12" data-slot-id="7"><wcpgl:widgetImport slotId="7"/></div> </div> </div>
- Optional:
Define the CSS to render your custom layout template in the storefront.
- Go to the WCDE_installdir\workspace\crs-web\WebContent\Aurora\css directory.
- Open the base.css file for review. This file defines the CSS for all layout templates that are provided by HCL Commerce.
-
Search for the comment
/*** Grid system ***/
within the file. The CSS in the section defines the display for the layout template fluid grid system. By default, this CSS file defines various display options. If the base.css does not include the definition that you require, create a base-vendor.css file to include your custom CSS definition. Create this file within the existing css directory. Ensure that the values for the attributes areclass
unique and map to your new CSS file.
For more information about defining the CSS for a responsive layout template, see Creating responsive layout templates.