April 11, 2020
Estimated Post Reading Time ~

Building an Accordion Component in Adobe CQ5

Over the past year, I have been developing websites with Adobe CQ5 as the CMS (Content Management System), including building components, so that content writers can populate the web pages. In some cases, the logical method of creating components did not work; with any CMS, there are limitations or bugs, and part of the 'fun' (if you could call it that) is discovering an alternative solution. This article describes and explains the accordion component development. This component was developed nearly one year ago now, so some of the particulars are not as fresh in my mind.

What The Heck is An Accordion in Web Terms?
For those who do not know, an 'accordion' in web terminology is a design element that can show or hide a panel of content (to save screen space or provide a consistent grouping of similar data), and the user can click onto the panel to expand the minimized view, or to minimize an expanded panel. The screenshot below shows an example of the component, which was the solution I implemented. Obviously, the styling (CSS) and Javascript required is beyond the scope of this. (For those who are interested, the functionality of the interaction is completed using the JQuery Javascript library.)


For setting this component up in CQ5 CMS for the content editors to use, I ideally wanted the content writer to be able to add a panel for each 'accordion' entry, which would consist of a title/subject and body text. The body text needed the ability to be styled, so the custom CQ5 rich text editor needed to be used. This would have been using a compositeField/MultiField component, but a bug was discovered while creating this, and the values did not save correctly. Unfortunately, Adobe CQ5 CMS cannot handle certain data types, even though it was meant to be supported. (Adobe CQ5 CMS does tend to have a lot of "TODO" comments in its code.)

Obviously, I needed to come up with an alternative and user-friendly solution. I read in forums online about using the column component and adapting it, but I wanted it to be more intuitive to the content writers. I'm mainly writing about my experience here as there seem to be many wanting to complete the same task, but the information does not exist.

My Accordion CQ5 Solution Explained
The alternative solution was to develop the accordion and accordion panel components separately, as a parsys, while providing a user-friendly interface to give the content writers direction. Simply, the content writer will drag and drop an accordion component from the Sidekick onto the screen. The accordion component will have a placeholder message to instruct the user to add accordion entries. The user will double-click the accordion component on the screen to add accordion entries. (The accordion entries are simply another component.) The accordion entries component is the only type of component that they will be able to add inside the accordion component parsys; no other components will be allowed to be placed.

This tutorial shows some of the steps to create an accordion component in Adobe CQ5 CMS. First of all, I will explain the structure of the set-up for the component. A visual representation of the structure is displayed below. As you can see, the accordion is organized into the 'accordion' and 'accordion-entry' (one panel, consisting of a heading and rich text) components.


The 'accordion' ComponentThe 'Accordion' component is simply the placeholder that encompasses the accordion entries.

_cq_editConfig.xml:
To ensure that the screen updates so that the editor can see the output on the screen, the page in author mode in the CMS needs to be refreshed. The file demonstrates refreshing after creation, after deletion, after insertion, and after moving.

<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    cq:dialogMode="floating"
    jcr:primaryType="cq:EditConfig">
    <cq:listeners
        jcr:primaryType="cq:EditListenersConfig"
        aftercreate="REFRESH_PAGE"
        afterdelete="REFRESH_PAGE"
        afterinsert="REFRESH_PAGE"
        aftermove="REFRESH_PAGE"/>
</jcr:root>


.content.xml
The 'Accordions' .content.xml file specifies that the sling:resourceSuperType is the standard parbase component. This will allow us to drag and drop other components into it. However, we need to ensure that only a specific type of component can be put into that place - the 'accordion-entry'. The settings allowedChildren is set to be an 'accordion-entry' component type, and allowedParents is the parsys.

<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    cq:isContainer="{Boolean}true"
    jcr:primaryType="cq:Component"
    jcr:title="Accordion"
    sling:resourceSuperType="foundation/components/parbase"
    allowedChildren="[*/accordion-entry]"
    allowedParents="[*/parsys]"
    componentGroup=".hidden"/>


dialog.xml
When the user drags and drops the 'Accordion' component into place, they have the option to enter a unique ID for the accordion. See the snippet of code below.

<items jcr:primaryType="cq:WidgetCollection">
        <title
            jcr:primaryType="cq:Widget"
            fieldDescription="Leave empty to use the page title."
            fieldLabel="Title"
            name="./jcr:title"
            xtype="textfield"/>
        <id
            jcr:primaryType="cq:Widget"
            fieldDescription="Enter a unique ID for the accordion"
            fieldLabel="ID"
            name="./id"
            xtype="textfield"/>
    </items>


accordion.jsp
The following shows the contents of the Accordion JSP and how it is rendered on the page. The resourceType is a parsys since the content editor will be dragging and dropping the 'according-entries' components into place here.

<c:set var="accordionFlag" scope="request" value="yes"/>
<c:set var="accordionID" scope="request" value="1"/>
<div><cq:include path="entries" resourceType="myproject/components/my-accordion/components/parsys"/></div>


clientlibs folder
This folder holds the Javascript for the accordion. There are some configurations here, such as defining the client libraries, such as the Javascript/jQuery file that will handle how the accordion should behave. The Javascript file is placed into the 'source' folder underneath the 'clientlibs' folder. Note that the name must match the name in the js.txt file.
  • .content.xml:
    <jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
        jcr:primaryType="cq:ClientLibraryFolder"
        categories="[my.accordion-component]"/>
  • js.txt:
    #base=source
    jaccordion.congif.js

The 'accordion-entry' ComponentThe 'Accordion-entry' component is an actual entry or panel that sits inside the 'Accordion' component. This is a child of the 'Accordion' component.

.content.xml:
The 'accordion-entry' will only be allowed to be used if it is a child of the 'Accordion' component, so we set the allowedParents accordingly.

<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:Component"
    jcr:title="Accordion Entry"
    sling:resourceSuperType="foundation/components/parbase"
    allowedParents="[*/my-accordion/components/*parsys]"
    componentGroup=".hidden"/>

dialog.xml:
The 'accordion-entry' dialog allows the user to enter a title for the accordion entry panel as well as rich text for the body text of the accordion.

<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:Dialog"
    helpPath="en/cq/current/wcm/default_components.html#Text"
    title="Text"
    xtype="tabpanel">
    <items jcr:primaryType="cq:WidgetCollection">
        <tab1
            jcr:primaryType="cq:Widget"
            anchor="100%"
            title="Text"
            xtype="panel">
            <items jcr:primaryType="cq:WidgetCollection">
                <title
                    jcr:primaryType="cq:Widget"
                    defaultValue="enter a title"
                    fieldLabel="Title"
                    name="./title"
                    xtype="textfield"/>
                <isRichTextFlag
                    jcr:primaryType="cq:Widget"
                    ignoreData="{Boolean}true"
                    name="./textIsRich"
                    value="true"
                    xtype="hidden"/>
                <text
                    jcr:primaryType="cq:Widget"
                    defaultValue="Please enter some text"
                    fieldDescription="Text displayed in panel"
                    fieldLabel="Text"
                    hideLabel="{Boolean}true"
                    name="./text"
                    xtype="richtext"/>
            </items>
        </tab1>
    </items>
</jcr:root>

accordion-entry.jsp:
The JSP renders the accordion-entry's HTML. I'm not going to include all of the file's contents, but note that I use the lines below in order to obtain the title and text, set by the content editor by using the contents of the dialog.xml file above.

<%
final String title = properties.get("title", "");
final String text = properties.get("text","");
%>

The 'parsys'The parsys is used for ensuring that the component has the same properties to behave like the foundation parsys component. This component is a copy of the component in the foundation library, with a change to a few of the files for the accordion. The changed files are mentioned below.
.content.xml:
The parsys component superResourceType is the foundation's parsys component, and the allowedChildren is 'accordion-entry'.
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    cq:isContainer="{Boolean}true"
    jcr:primaryType="cq:Component"
    jcr:title="Accordian Composite 1"
    sling:resourceSuperType="foundation/components/parsys"
    allowedChildren="*/*accordion-entry"
    componentGroup=".hidden"/>


.content.xml (in the 'new' folder)
The resourceType needs to point to the accordion's copy of the parsys file.

<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:Component"
    jcr:title="New Paragraph"
    sling:resourceType="myprojectx/components/my-accordion/parsys/new"
    componentGroup=".hidden"/>


_cq_editConfig.xml (in the 'new' folder) 
Simply, we will tell the user to drag and drop accordion entries to this place. (So, we change the emptyText parameter here.)

<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
    cq:actions="[_clear,insert]"
    cq:emptyText="Drop Accordian entries here"
    jcr:primaryType="cq:EditConfig"/>


Conclusion
Hopefully, I hope I have not missed anything important out from these steps. (It was a while ago since this was developed, so it's not very fresh in my memory.) I hope that this will allow you to think of alternative solutions where some of the components in CQ5 do not work as expected or as documented. The most important point is to develop the CMS website customization so that the user can intuitively add their content and be prevented from making changes that could 'break' the website.

One more tip: I also think it's important to note that you can switch off Javascript or JQuery from running in EditMode, and this was done so that the user can view and edit all panels in the accordion by clicking onto any panel (without the behaviors applied to expand and contract).


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.