Rendering based on Content Type

Contents

In this chapter you will create a new component that renders persons

Person content type

The Headless Movie DB contains sample content, and content types. One of these is Person or more specifically com.enonic.app.hmdb:person.

From Content Studio, you may easily find and list persons. By selecting the JSON preview, you can also study the properties of a person:

Sample JSON for Person
{
    "_id": "a8b374a2-c532-45eb-9aa1-73d1c37cd681",
    "_name": "lea-seydoux",
    "_path": "/hmdb/persons/lea-seydoux",
    "creator": "user:system:su",
    "modifier": "user:system:su",
    "createdTime": "2021-11-23T10:01:16.711Z",
    "modifiedTime": "2021-11-23T13:06:38.493Z",
    "owner": "user:system:su",
    "type": "com.enonic.app.hmdb:person",
    "displayName": "Léa Seydoux",
    "hasChildren": true,
    "language": "en",
    "valid": true,
    "childOrder": "modifiedtime DESC",
    "data": {
        "photos": "09b3af0e-6da3-4bcf-88d9-11cbe9c41283",
        "bio": "French actress Léa Seydoux was born in 1985 in Paris, France, to Valérie Schlumberger, a philanthropist, and Henri Seydoux, a businessman.",
        "dateofbirth": "1985-07-01"
    },
    "x": {},
    "page": {},
    "attachments": {},
    "publish": {
        "from": "2024-11-13T13:32:39.427Z",
        "first": "2024-11-13T13:32:39.427Z"
    },
    "workflow": {
        "state": "READY",
        "checks": {}
    }
}

The Person component

To implement the rendering we need to create a new component, and accompanying data processor:

  1. Create a processor:

    /src/main/resources/react4xp/components/content/PersonProcessor.ts
    import {get as getContentByKey} from '/lib/xp/content';
    import {imageUrl} from '/lib/xp/portal';
    import {toArray} from "/react4xp/utils/arrayUtils";
    import {PageDescriptor} from '@enonic-types/core';
    import type {Content} from '@enonic-types/lib-content';
    import type {ComponentProcessorFunction} from '@enonic-types/lib-react4xp/DataFetcher';
    
    function fetchAdditionalPhotos(photosIds) {
        return photosIds.map(photoId => {
            if (photoId) {
                const photoContent = getContentByKey<Content>({key: photoId});
                return {
                    _id: photoContent._id,
                    title: photoContent.displayName,
                    imageUrl: imageUrl({id: photoContent._id, scale: 'block(175, 175)'})
                };
            }
        });
    }
    
    export const personProcessor: ComponentProcessorFunction<PageDescriptor>
        = (params) => {
        const photos: string[] = toArray<string>(params.content.data.photos as string | string[])
        const firstPhotoId = photos[0] || '';
        const remainingPhotoIds = photos.slice(1);
    
        let firstPhoto = null;
        if (firstPhotoId) {
            const {_id, displayName} = getContentByKey<Content>({key: firstPhotoId});
            firstPhoto = {
                _id,
                title: displayName,
                imageUrl: imageUrl({id: _id, scale: 'block(1200, 675)'})
            }
        }
    
        const extraPhotos = fetchAdditionalPhotos(remainingPhotoIds);
    
        return {
            displayName: `${params.content.displayName}`,
            photo: firstPhoto,
            birthDate: params.content.data.dateofbirth,
            restPhotos: extraPhotos
        };
    };
  2. Add it to the dataFetcher, with the following two lines:

    dataFetcher
    import {personProcessor} from './components/content/PersonProcessor';
    
    dataFetcher.addContentType('com.enonic.app.hmdb:person', {processor: personProcessor});
  3. Create the Person component, it consists of two files:

    /src/main/resources/react4xp/components/content/Person.tsx (React component)
    import React from 'react'
    import styles from './Person.module.css';
    
    export const Person = (props) => {
        const {displayName, photo, restPhotos, bioHtml, birthDate} = props as any;
        return (
            <div className={styles.person}>
                <h1>{displayName}</h1>
                <p>{birthDate}</p>
                <div>
                    {
                        photo ? (
                            <>
                                <div className={styles.photos}>
                                    <img src={photo.imageUrl}
                                         title={photo.title}
                                         alt={photo.title}
                                         height={675}
                                         width={1200}
                                         loading="eager"
                                    />
                                </div>
                            </>
                        ) : (
                            <p>No photo available</p>
                        )
                    }
                    {restPhotos && restPhotos.length > 0 && (
                        <>
                            <h2>Photos</h2>
                            <div className={styles.photoContainer}>
                                <div className={styles.photoGrid}>
                                    <div className={styles.photoScroll}>
                                        {restPhotos.map((photo, index) => (
                                            <img key={index}
                                                 src={photo.imageUrl}
                                                 title={photo.title}
                                                 alt={photo.title}
                                                 height={175}
                                                 width={175}
                                            />
                                        ))}
                                    </div>
                                </div>
                            </div>
                        </>
                    )}
                </div>
            </div>
        )
    }
    /src/main/resources/react4xp/components/content/Person.module.css (CSS module)
    .person .bio {
      font-family: sans-serif;
    }
    
    .person {
      display: flex;
      flex-direction: column;
      margin-inline: auto;
      min-height: 1600px;
    }
    
    .person h1 {
      margin-bottom: 0;
    }
    
    .person p {
      margin-top: 0;
    }
    
    .flexBlock {
      display: flex;
      justify-content: space-between;
    }
    
    .photoContainer {
      position: relative;
      display: flex;
    
      ::-webkit-scrollbar {
        -webkit-appearance: none;
        height: 6px;
      }
    
      ::-webkit-scrollbar-thumb {
        border-radius: 8px;
        background-color: ghostwhite;
      }
    }
    
    .photoGrid {
      overflow-x: auto;
    }
    
    .photoScroll {
      display: flex;
      gap: 25px;
      white-space: nowrap;
      padding: 0;
      margin: .5rem 0 .5rem 0;
    }
    
    .photoScroll img {
      border-radius: 5px;
    }
    
    .photos {
      margin-block: 1rem;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      grid-gap: 50px;
      gap: 50px;
    }
    
    .restPhotosContainer {
      padding: 1rem 0;
      max-width: calc(90vw - 1rem);
    }
    
    .restPhotos {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(15%, 1fr));
      gap: 5%;
      margin-block: 1rem;
    }
    
    .photos img {
      width: 100%;
      height: auto;
      border-radius: 8px;
    }
    
    .restImg {
      width: 100%;
      aspect-ratio: 1 / 1;
      object-fit: cover;
      object-position: 50% 50%;
      border-radius: 5px;
    }
    
    
    .richText {
      font-size: 24px;
    }
    
    
    .richText figure img {
      border-radius: 5px;
    }
    
    .photosHeader {
      margin-top: 0;
      color: #EF82F0;
    }
  4. Plaec it in the component to the registry: by adding the following two lines:

componentRegistry
import {Person} from './components/content/Person';

componentRegistry.addContentType('com.enonic.app.hmdb:person', {View: Person}); (1)

+ <1> By using addContentType() we are instructnig the componentRegistry to render this component when the content is of type 'com.enonic.app.hmdb:person'.

Back in Content Studio, select a random person to see what you have made:

lea seydoux

Next

You are on a roll, next - lets have a look at Rich text handling.


Contents

Contents

AI-powered search

Juke AI