Person list component

Contents

This chapter covers creation of the PersonList component, including a query for retrieving persons from the Enonic API.

Query

Add the following file to your project (note the new query folder). The component will use it to fetch data:

src/queries/PersonList.ts
const query = `query PersonListQuery {
    guillotine {
        queryDsl(
            first: 50
            query: {
                term: {
                    field: "type",
                    value: {
                        string: "com.enonic.app.intro:person"
                    }
                }
            }
            sort: {
                field: "modifiedTime",
                direction: DESC
            }
        ) {
            _id
            _name
            displayName
            type
            ... on com_enonic_app_intro_Person {
                data {
                    photos {
                        ... on media_Image {
                            imageUrl(type: absolute, scale: "square(500)")
                        }
                    }
                }
            }
        }
    }
}`;

export default query;

Styling

The component will require some styling, to make it look good! Add the stylesheet for the PersonList component (note the new styles folder):

src/styles/PersonList.sass
$gutter: 30px
$color: rgba(0, 0, 0, .5)

ul.person-list
  list-style-type: none
  padding: 30px 30px 0
  margin: 0
  background-color: #444

  li
    display: inline-block
    margin-bottom: $gutter
    padding-right: $gutter
    vertical-align: middle
    width: calc((100% / 3) - 20px)
    &:nth-child(3n)
      padding-right: 0

    a
      display: inline-block
      width: 100%
      position: relative // so div can be centered
      div
        bottom: 10px
        color: #fff
        left: 50%
        font-size: 1.5rem
        opacity: 0
        pointer-events: none
        position: absolute
        text-shadow: 1px 0 0 $color, 0 -1px 0 $color, 0 1px 0 $color, -1px 0 0 $color
        transform: translate(-50%)
        white-space: nowrap
      &:hover
        div
          opacity: 1
      img
        box-shadow: 0 0 10px rgba(0, 0, 0, .1)
        border-radius: 5%
        filter: grayscale(100%)
        height: auto
        margin: auto 0
        transition: all .2s ease-in-out
        width: 100%

        &:hover
          filter: grayscale(0%)
          transform: scale(1.1)

Component

Now, add the React component itself (note the new components folder):

src/components/PersonList.tsx
import {useEffect, useState,} from 'react';
import {Link} from 'react-router-dom';

import '../styles/PersonList.sass';

import PERSON_LIST_QUERY from '../queries/PersonList';

const forceArray = (data: any) => (Array.isArray(data) ? data : [data]);

export function PersonList() {
  const [data, setData] = useState<{
    _id: string
    _name: string
    data: {
      photos: {
        imageUrl: string
      }[]
    }
    displayName: string
  }[]>();

  useEffect(() => {
    fetch(import.meta.env.VITE_GUILLOTINE_URL as string, {
      body: JSON.stringify({
        query: PERSON_LIST_QUERY,
      }),
      headers: {
        'content-type': 'application/json',
      },
      method: 'POST',
    })
        .then(response => response.json())
        .then(json => setData(json.data.guillotine.queryDsl));
  }, []);

  return (
      <>
        {data && <ul className='person-list'>
          {data.map((person, i) => {

            const {
              _id,
              _name,
              data: {
                photos
              },
              displayName
            } = person;

            const imgProps = {
              alt: displayName,
              src: photos ? forceArray(photos)[0].imageUrl : undefined,
            };

            return <li key={i}>
              <Link to={`/p/${_name}/${_id}`}>
                <img {...imgProps}/>
                <div>{displayName}</div>
              </Link>
            </li>;
          })}
        </ul>}
      </>
  );
}

Routing

Finally, update the App.tsx file to include the PersonList component and display it on the root path /:

src/App.tsx
import './App.sass';
import {Route, BrowserRouter as Router, Routes} from 'react-router-dom';
import {PersonList} from './components/PersonList';

export default function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<PersonList />}/>
      </Routes>
    </Router>
  );
}

Result

With everything in place, you should now see a grid of person images at http://localhost:3000.

person list

Clicking any of the images will take you to the person’s detail page - which is yet to be implemented.

Next step

Moving forward, you’ll create a component and a matching routing to render Person details.


Contents

Contents

AI-powered search

Juke AI