Creating Volto Views#

Full View#

In this chapter we are going to create a new type of view for displaying contents in a folder. We will call this view full view. In Plone there is a view called All content with the view id full_view that we will reuse. We start by creating a file called: components/FullView/FullView.jsx.

/**
 * Full view component.
 * @module components/theme/View/FullView
 */

import React from 'react';
import PropTypes from 'prop-types';
import { UniversalLink } from '@plone/volto/components';
import { Container, Image } from 'semantic-ui-react';
import { FormattedMessage } from 'react-intl';

/**
 * Full view component class.
 * @function FullView
 * @param {Object} content Content object.
 * @returns {string} Markup of the component.
 */
const FullView = ({ content }) => (
  <Container className="view-wrapper">
    <article id="content">
      <header>
        <h1 className="documentFirstHeading">{content.title}</h1>
        {content.description && (
          <p className="documentDescription">{content.description}</p>
        )}
      </header>
      <section id="content-core">
        {content.items.map((item) => (
          <article key={item.url}>
            <h2>
              <UniversalLink href={item.url} title={item['@type']}>
                {item.title}
              </UniversalLink>
            </h2>
            {item.image && (
              <Image
                clearing
                floated="right"
                alt={item.image_caption ? item.image_caption : item.title}
                src={item.image.scales.thumb.download}
              />
            )}
            {item.description && <p>{item.description}</p>}
            {item.text && item.text.data && (
              <p dangerouslySetInnerHTML={{ __html: item.text.data }} />
            )}
          </article>
        ))}
      </section>
    </article>
  </Container>
);

/**
 * Property types.
 * @property {Object} propTypes Property types.
 * @static
 */
FullView.propTypes = {
  /**
   * Content of the object
   */
  content: PropTypes.shape({
    /**
     * Title of the object
     */
    title: PropTypes.string,
    /**
     * Description of the object
     */
    description: PropTypes.string,
    /**
     * Child items of the object
     */
    items: PropTypes.arrayOf(
      PropTypes.shape({
        /**
         * Title of the item
         */
        title: PropTypes.string,
        /**
         * Description of the item
         */
        description: PropTypes.string,
        /**
         * Url of the item
         */
        url: PropTypes.string,
        /**
         * Image of the item
         */
        image: PropTypes.object,
        /**
         * Image caption of the item
         */
        image_caption: PropTypes.string,
        /**
         * Type of the item
         */
        '@type': PropTypes.string,
      }),
    ),
  }).isRequired,
};

export default FullView;

Next we will add the view to the app components. We can do this by adding the following lines to components/index.js.

import FullView from './FullView/FullView';

export { FullView };

Registering The View#

To register the view we will edit the config.js file. The views configuration options contains all the views. This object contains an object called layoutViews which registers all the layout views. We will add the full_view to this object.

import { FullView } from './components';

export default function applyConfig(config) {
  const defaultViews = config.views
  // Add here your project's configuration here by modifying `config` accordingly
  config.views = {
    ...defaultViews,
    layoutViews: {
      ...defaultViewslayoutViews,
      full_view: FullView,
    },
  };
  return config;
}

Registering a new view called Album View#

Create the Album View that shows the images in a grid. You can use the Card class from semantic-ui, components/AlbumView/AlbumView.jsx:

/**
 * Album view component.
 * @module components/theme/View/AlbumView
 */

import React from 'react';
import PropTypes from 'prop-types';
import { UniversalLink } from '@plone/volto/components';
import { Card, Container, Image } from 'semantic-ui-react';
import { FormattedMessage } from 'react-intl';

/**
 * Album view component class.
 * @function AlbumView
 * @param {Object} content Content object.
 * @returns {string} Markup of the component.
 */
const AlbumView = ({ content }) => (
  <Container className="view-wrapper">
    <article id="content">
      <header>
        <h1 className="documentFirstHeading">{content.title}</h1>
        {content.description && (
          <p className="documentDescription">{content.description}</p>
        )}
      </header>
      <section id="content-core">
        <Card.Group>
          {content.items.map((item) => (
            <Card key={item.url}>
              {item.image && (
                <Image
                  alt={item.image_caption ? item.image_caption : item.title}
                  src={item.image.scales.thumb.download}
                />
              )}
              <Card.Content>
                <Card.Header>
                  <UniversalLink href={item.url} title={item['@type']}>
                    {item.title}
                  </UniversalLink>
                </Card.Header>
              </Card.Content>
            </Card>
          ))}
        </Card.Group>
      </section>
    </article>
  </Container>
);

/**
 * Property types.
 * @property {Object} propTypes Property types.
 * @static
 */
AlbumView.propTypes = {
  /**
   * Content of the object
   */
  content: PropTypes.shape({
    /**
     * Title of the object
     */
    title: PropTypes.string,
    /**
     * Description of the object
     */
    description: PropTypes.string,
    /**
     * Child items of the object
     */
    items: PropTypes.arrayOf(
      PropTypes.shape({
        /**
         * Title of the item
         */
        title: PropTypes.string,
        /**
         * Description of the item
         */
        description: PropTypes.string,
        /**
         * Url of the item
         */
        url: PropTypes.string,
        /**
         * Image of the item
         */
        image: PropTypes.object,
        /**
         * Image caption of the item
         */
        image_caption: PropTypes.string,
        /**
         * Type of the item
         */
        '@type': PropTypes.string,
      }),
    ),
  }).isRequired,
};

export default AlbumView;

Then in components/index.js:

/**
 * Add your components here.
 * @module components
 * @example
 * import Footer from './Footer/Footer';
 *
 * export {
 *   Footer,
 * };
 */

import AlbumView from './AlbumView/AlbumView';
import FullView from './FullView/FullView';

export { AlbumView, FullView };

And in config.js:

/**
 * Add your config changes here.
 * @module config
 * @example
 * export default function applyConfig(config) {
 *   config.settings = {
 *     ...config.settings,
 *     port: 4300,
 *     listBlockTypes: {
 *       ...config.settings.listBlockTypes,
 *       'my-list-item',
 *    }
 * }
 */

// All your imports required for the config here BEFORE this line
import '@plone/volto/config';
import { AlbumView, FullView } from './components';

export default function applyConfig(config) {
  const defaultViews = config.views
  // Add here your project's configuration here by modifying `config` accordingly
  config.views = {
    ...defaultViews,
    layoutViews: {
      ...defaultViews.layoutViews,
      album_view: AlbumView,
      full_view: FullView,
    },
  };
  return config;
}