Feature section

May 22, 2024

Adding the content type definition

Basically, here is an overview:

The mapping from the definition to the visual elements is similar to what we have seen in the hero section, except that we now have one element that contains multiple sub-elements.

This is done using the following line:

+ * (solidTemplate:feature)    

This is a child node definition that is restricted to a single content node type solidTemplate:feature.

The type definition is just below in the above illustration and is rather straightforward.

For the icon, we use a reference to an image that may be uploaded by content editors.

First we must add the definitions for both components in our definitions.cnd file:

[solidTemplate:featureSection] > jnt:content, solidTemplatemix:solidTemplateComponents
+ * (solidTemplate:feature)    

Then for the feature

[solidTemplate:feature] 
 - title (string) = 'Be Productive' i18n
 - icon (weakreference, picker[type='image'])
 - text (string) = 'Fermentum posuere urna nec tincidunt praesent semper feugiat nibh. A arcu cursus vitae congue mauris. Nam at lectus urna duis convallis. Mauris rhoncus aenean vel elit scelerisque mauris.' i18n    

Creating a feature view

The view for the solidTemplate:feature is created in the $NPM_TEMPLATE/src/server/views/feature/FeatureDefault.jsx file with the following content:

import React from 'react';
import {useServerContext, getNodeProps, buildUrl, defineJahiaComponent} from '@jahia/js-server-core';

export const FeatureDefault = () => {
    const {currentNode, renderContext, currentResource} = useServerContext();
    const props = getNodeProps(currentNode, ['icon', 'title', 'text']);
    return (
        <div className="feature text-center is-revealing">
            <div className="feature-inner">
                <div className="feature-icon">
                    {props.icon &&
                        <img srcSet={buildUrl({path:props.icon.getPath()}, renderContext, currentResource)} alt="Feature"/>
                    }
                </div>
                <h4 className="feature-title mt-24">{props.title}</h4>
                <p className="text-sm mb-0">{props.text}</p>
            </div>
        </div>
    )
}

FeatureDefault.jahiaComponent = defineJahiaComponent({
    nodeType: 'solidTemplate:feature',
    displayName: 'Feature',
    componentType: 'view'
})    

Again the HTML was copied from the static HTML template and modified to insert the content node properties.

Creating a feature section view

The parent feature section view is created in $NPM_TEMPLATE/src/server/views/featureSection/FeatureSectionDefault.jsx with the following content:

import React from 'react';
import {Render, AddContentButtons, useServerContext, getChildNodes, defineJahiaComponent} from '@jahia/js-server-core';

export const FeatureSectionDefault = () => {
    const {currentNode} = useServerContext();
    const allChildren = getChildNodes(currentNode, -1);
    return (
        <section className="features section">
            <div className="container">
                <div className="features-inner section-inner has-bottom-divider">
                    <div className="features-wrap">
                        {allChildren && allChildren.map(child => 
                            <Render path={child.getPath()} key={child.getIdentifier()} />
                        )}
                    </div>
                    <AddContentButtons />
                </div>
            </div>
        </section>
    )
}

FeatureSectionDefault.jahiaComponent = defineJahiaComponent({
    nodeType: 'solidTemplate:featureSection',
    displayName: 'Feature section',
    componentType: 'view'
})    

You can see here that we are using getChildNodes helper function to easily retrieve all the child nodes of the current node (The -1 count indicates we force the retrieval of all the nodes, which is something that should never be done in production views to avoid potential performance issues). To render them, we first check that we do have child nodes and then iterate over them using a map function and a Render component, which will then call the FeatureDefault.jsx view to render the child node. The <AddContentButtons /> is used to position the button to add new content entries.

Exporting the views

Then we need to export our two views, we first need to create a src/server/views/feature/index.js file with the following content:

export * from './FeatureDefault';    

And then create a src/server/views/featureSection/index.js file with the following content:

export * from './FeatureSectionDefault';    

And finally we must add the new types to the main src/server/views/index.js file with the following content:

export * from './featureSection';
export * from './feature';    

We can now get rid of the whole section from the $NPM_TEMPLATE/src/server/templates/page/PageHome.jsx template, so simply remove:

            <section className="features section"> 
            	... content removed for brievety...
	     </section>
    

Creating the content

Let’s add the feature section content now. Make sure you refresh your browser first and that everything is working. Click the New content button again to open up the content type selection dialog:

Select the featureSection and click CREATE

If you remember from the definition, the feature section has no self-defined properties, so we can simply click SAVE to create it and click the back arrow to come back to the Page Builder. We now see a feature button that appears in the new feature section.

Clicking the New feature button will directly open the content editing window, bypassing the content type selection since we already know which type we want to edit:

The feature content object is already filled out with the default values so we can simply leave them as they are. Let’s then click on Select an image for the icon:

We basically get an empty image selection screen, as our project never had any images added to it. Let’s quickly upload some images by clicking on the Upload file button. It is possible to upload more than one file at the same time, so here we navigate to the $HTML_TEMPLATE/dist/images directory and select all the feature-icon*.svg files:

Simply click Open once they are selected, this will trigger the upload process and the image selection screen will refresh to display the newly uploaded images. We can then select the feature-icon-01.svg as illustrated in this screenshot:

Finally click the SELECT button to return to the content editing screen:

Click the SAVE button.

You should now see your feature displayed. works normally.

You can then repeat this process to create as many features as you’d like.

Next step

Continue to finishing up your integration