Table Transformation Snippets
Table transformation snippets take content entered by editors on a page and use XSL to transform that content into complex graphic elements. The content editor experience is that of placing and filling out a snippet, built as tables; design changes happen via the XSL files when the page is rendered.
Tables provide an easily-editable and -understandable format for content editors that also ensures a specific content structure in the same way that forms do. The WYSIWYG editor can also easily render and handle a table, while it could struggle with more complex structures. By handling the complex elements in the XSL instead, we remove both the risk of the WYSIWYG interfering with the design code and simplify the editing process.
If you are not already familiar with XSL, please read through our introduction to XSL and XML before continuing with this topic.
StructureLink to this section
Technically, no specific syntax for table transformations is required, as they are built off of your XSL and CSS. Therefore, they can be built however you want, with the caveat that the table itself must be plain HTML so it can render appropriately in the WYSIWYG editor. The following structure and examples are based off of the syntax our professional services team uses when creating table transformations.
The standard structural elements of a table transformation snippet are as follows:
- An identifier for the table, typically a class, unique to that table transformation snippet. The standard implementation convention is to start each identifier class with
- A heading to label the table cells, so that editors know what content to enter.
- One or more open table cells for editors to enter content into. These can be as structured as necessary, depending on what the output needs to be.
The structure of the table itself is determined by what content or data points you want editors to input, as opposed to what the XSL handles. For example, if you want editors to input an image to be used, that might be one cell in the table row, and if a caption for the image is also desired, then that could be another cell. Using table transformations also allows for repeatable elements, as more rows can be added to the table with the same structure as preceding rows.
Once you have the code for your snippet defined (not necessarily in Omni CMS), write an XSL template, matched specifically to that table transformation. You should already have a
/_resources/xsl/_shared/snippets.xsl file in your implementation which you can add on to, or you can create your own and make sure your
common.xsl imports it. This template match targets the identifier class you set on the table.
Within the template match, define the output and call in the various pieces of the table using XPath. To call in table data, you can either use an array-type notation (using [square brackets], starting at 1) to identify rows, columns, or cells, or you can add specific classes or IDs to the rows, etc. that the XSL then targets.
Additionally, the XSL of any page that you want the table transformation snippet to potentially be added to must use
apply-templates, targeting the node of the editable region or regions. Using
apply-templates rather than
copy-of is important, as
copy-of will not function properly because it does a static copy of the block and does not send the node to be matched for the transformation.
snippets.xsl (or whatever XSL you define your table transformation snippets within) should be imported by
common.xsl so that all pages using
common.xsl automatically can use any snippet defined in
snippets.xsl (or wherever you choose to define your snippets).
It is also important to ensure you have Identity Matches using a shallow copy defined (or using a recursive copy template) so that each node within the editable region is also matched and transformed. This is standard in OU implementations, either within the OU global XSL library or in
template-matches.xsl. Here is an example of this code:
<!-- Identity Matches --> <!-- The following mode will match all items except processing instructions, copies them, then processes any children. --> <xsl:mode on-no-match="shallow-copy" /> <!-- match the pcf-sheet to make the editable region function --> <xsl:template match="processing-instruction('pcf-stylesheet')" mode="#all" />
The recursive copy template that was used in older implementations will look like the following code excerpt.
<xsl:template match="attribute()|element()|text()|comment()" mode="copy"> <xsl:copy> <xsl:apply-templates select="attribute()|node()" mode="copy"/> </xsl:copy> </xsl:template>
If you have either of the above Identity Matches defined in your XSL, you DO NOT need to add the other in.
Step-by-Step ExampleLink to this section
When building a table transformation, always start with an existing HTML design element and work backwards to determine what the table snippet should be. Create the table structure first, potentially in a text editor, then modify the XSL to match. Finally, create the snippet in Omni CMS when you are ready for editors to use it.
For this example, we're going to start with the accordion snippet below:
The source code of the accordion on the live page looks like this:
<ul class="accordion" data-accordion="data-accordion" data-allow-all-closed="true" data-multi-expand="true" role="tablist"> <li class="accordion-item" data-accordion-item="data-accordion-item"> <a href="#" class="accordion-bar" aria-controls="9f8ou8-accordion" role="tab" id="9f8ou8-accordion-label" aria-expanded="false" aria-selected="false">Heading 1</a> <div class="accordion-content" data-tab-content="data-tab-content" role="tabpanel" aria-labelledby="9f8ou8-accordion-label" aria-hidden="true" id="9f8ou8-accordion">Content for Region 1 Goes Here</div> </li> <li class="accordion-item" data-accordion-item="data-accordion-item"> <a href="#" class="accordion-bar" aria-controls="7bltf1-accordion" role="tab" id="7bltf1-accordion-label" aria-expanded="false" aria-selected="false">Heading 2</a> <div class="accordion-content" data-tab-content="data-tab-content" role="tabpanel" aria-labelledby="7bltf1-accordion-label" aria-hidden="true" id="7bltf1-accordion">Content for Region 2 Goes Here</div> </li> <li class="accordion-item" data-accordion-item="data-accordion-item"> <a href="#" class="accordion-bar" aria-controls="wvazsm-accordion" role="tab" id="wvazsm-accordion-label" aria-expanded="false" aria-selected="false">Heading 3</a> <div class="accordion-content" data-tab-content="data-tab-content" role="tabpanel" aria-labelledby="wvazsm-accordion-label" aria-hidden="true" id="wvazsm-accordion">Content for Region 3 Goes Here</div> </li> </ul>
The first step is determining how to translate the accordion design into an editable table. In this case, we've opted to make each expandable region of the accordion its own row, with one cell to determine the heading and another cell for the content, like so:
Content for Region 1 Goes Here
|Heading 2||Content for Region 2 Goes Here|
|Heading 3||Content for Region 3 Goes Here|
Which in code form, looks like the following:
<table> <tbody> <tr> <td>Heading 1</td> <td> <p>Content for Region 1 Goes Here</p> </td> </tr> <tr> <td>Heading 2</td> <td>Content for Region 2 Goes Here</td> </tr> <tr> <td>Heading 3</td> <td>Content for Region 3 Goes Here</td> </tr> </tbody> </table>
Next, we're going to add the following identifier class to the start of the code:
<table class="ou-accordion"><colgroup> <col/><col/></colgroup><caption>Accordion</caption>
We're also going to add a header to the table. This will label the table so editors know what to put in each cell.
<thead> <tr> <th>Accordion Heading</th> <th>Content</th> </tr> </thead>
Combined, this creates the code that will be the source code of our snippet. But first, we have to go to the XSL.
The following code first matches the identifier class previously added to the start of the table code with the
xsl:template. Everything within this template match determines the final output of the snippet. Typically, the raw output from the table is placed within this match initially, and then the repeatable elements are placed in an
xsl:for-each loop. Then, use the correct XPath to access the data from the table and render it in the desired location.
<!-- Accordion --> <xsl:template match="table[@class='ou-accordion']"> <div class="clearfix"> <ul class="accordion" data-accordion="data-accordion" data-allow-all-closed="true" data-multi-expand="true"> <xsl:for-each select="tbody/tr"> <li class="accordion-item" data-accordion-item="data-accordion-item"> <a href="#" class="accordion-bar"><xsl:value-of select="td"/></a> <div class="accordion-content" data-tab-content="data-tab-content"> <xsl:apply-templates select="td/node()"/> </div> </li> </xsl:for-each> </ul> </div> </xsl:template>
The final result will display the transformed table into the desired output. Any CSS or JS that is required to support the element should also be added within your main CSS file, or can be conditionally added to the desired location using XSL through an
xsl:if statement that should look something like this:
<xsl:if test="descendant::table[@class='ou-accordion']"> <link rel="stylesheet" type="text/css" href="accordion-stylesheet.css"> </xsl:if>
Now that your site resources and styling have been updated, it's time to make the snippet itself. Take the table code you created earlier and use it as the source code for a snippet, configuring the other settings however you want. When you place it on a page, it should look something along the lines of this:
Which will then render as the accordion example presented at the beginning of this section.