# Content Pipeline UI

A library that provides a set of React components that can be used to render content received from the `cp-content-pipeline-api`, including [Content Tree](https://github.com/Financial-Times/content-tree) components.

The components can be used independently or alongside the [cp-content-pipeline-client](../client/README.md), which supplies content data from CAPI and other sources.

## Table of Contents

- [Usage guide](#usage-guide)
- [Components](#components)
- [Rendering](#rendering)
- [Storybook](#storybook)

## Usage Guide

1. Install the `cp-content-pipeline-ui` components

```bash
npm install @financial-times/cp-content-pipeline-ui
```

2. Import and render them in your app's server code

```js
import { Topper, ArticleInfo, Body } from '@financial-times/cp-content-pipeline-ui'
const articleData = // obtain data by use of cp-content-pipeline-api directly or via cp-content-pipeline-client

<Topper content={articleData} />
<ArticleInfo content={articleData} />
<Body content={articleData} />
```

## Components Explained

Each Component in this package contains a function which returns a JSX object.

Components can be combined to form content views which can be used on, ft.com, the apps or the edit.

Components may be nested. For example the `<Body>` component pulls in other components to form a complete article body.

For nested components, the default component mapping used can be overridden by use of a `components` argument. It is an object that maps component names to JSX.

```js
import { Body } from '@financial-times/cp-content-pipeline-ui'
const removeOutboundLinks = (props) => <span>{...props.children}</span>
const articleData = // obtain data by use of cp-content-pipeline-api directly or via cp-content-pipeline-client

const componentOverrides = {
	Link: removeOutboundLinks,
};
<Body content={articleData} richTextComponents={componentOverrides}>
```

### Top-Level Components

These are components that do not directly invoke [content-tree](https://github.com/Financial-Times/content-tree). They take a simple object data structure from the resolvers, eg. `<Body>` or `<Topper>` and invoke other components.

### RichText Structured Components

These are components that use the [content-tree](https://github.com/Financial-Times/content-tree). They require data in the form of an abstract syntax tree. Please refer to [this documention](../schema/README.md#content-resolution) for a detailed explanation about how we use the `content-tree` spec to generate a `tree` and `references`.

#### The RichText Component

The `<RichText>` component renders a `content-tree`

It accepts:
- A structured `content-tree` data object
- An optional list of component overrides

This component recursively walks the tree and renders components for each `tag` in that dataset. The supported components are defined in a [componentMap](./src/components/RichText/index.tsx#L83) and they refer to the set of components which can be rendered.

Any component override must be a valid [JSX functional or class constructor](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/24f1d0c82da2d898acd03fbb3e692eba3c431f82/types/react/index.d.ts#L80).

- ✅ `{ paragraph: (props) => (<p>{props.children}</p>) }`
- ✅ `{ paragraph: MyParagraphComponent }`
- ✅ `{ paragraph: () => null }`
- ❌ `{ paragraph: 'p' }`


##### Fallback
The `Fallback` component is a special instance. This component is used whenever the renderer encounters a tag that it doesn't have a component mapping for. The default behaviour is to render any known child components. This `Fallback` component can also be overridden in the same way others can.

##### References

The `<RichText>` component will match the `references` (an array of extra external content required to render a component no provided by `content-tree`) for a given node and pass those as a property to the child component.

##### Example usage

```jsx
  const structuredContent = {
    tree: contentTree,
    references: [{
      type: 'recommended',
      ...
    }]
  }

  const components = {
    paragraph: ParagraphComponent,
    link: LinkComponent,
    'image-set': ImageSetComponent
  }

  <RichText structuredContent={structuredContent} components={components} />
```

### Content body components

The `Body` component can render different components for different types of content. As well as the fallback `Article` renderer, which just renders the body content with a wrapper element, there are built-in renderers for these content types:

- `Audio`, renders an audio player before the actual body content
- `LiveBlogPackage`, renders the package's live blog posts
- `ContentPackage`, renders the package's articles

To provide a custom renderer, you can pass in an object of `bodyComponents` to `Body`, which maps content types to React components:

```js
<Body bodyComponents={{ Audio: (props) => <CustomPodcastPlayer {...props} /> }} content={...} />
```

If no default or override component is provided for a particular content type, the `fallback` body component is rendered. By default, this is the renderer used for `Article` content.

```js
<Body bodyComponents={{ fallback: (props) => <CustomFallback {...props} /> }} content={...} />
```

Any additional properties passed to `Body` are forwarded to your renderer.

## Storybook

You can visually test the components on Storybook by running the following command:

```
npm run storybook
```
