-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
David Featherston
committed
Jun 23, 2023
1 parent
fde24a2
commit d2f1bb8
Showing
1 changed file
with
158 additions
and
3 deletions.
There are no files selected for viewing
161 changes: 158 additions & 3 deletions
161
docs/content/framework/3.guides/4.creating-content-types.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,166 @@ | ||
--- | ||
title: Creating content types | ||
description: A guide to creating new content templates. | ||
description: How to create new content types for the SDP platform | ||
draft: true | ||
layout: page | ||
--- | ||
|
||
This is how you do that | ||
> See [key concepts - content types](/framework/key-concepts/content-types) for an overview of what content types are and the core content types used within the SDP platform. | ||
## Anatomy | ||
|
||
Content types are comprised of three main parts: | ||
|
||
- [A mapping object](#the-mapping-object) - maps API data to pass to a Vue component | ||
- [A server plugin](#the-server-plugin) - registers the content type with `@dpc-sdp/ripple-tide-api` | ||
- [And Vue components](#the-vue-component) - takes the mapped data and renders the content type | ||
|
||
## Creating a content type | ||
|
||
The Nuxt Ripple CLI provides a simple way to add new content types, to get started just run the following command: | ||
|
||
``` | ||
npx @dpc-sdp/nuxt-ripple-cli add content-type [DIRECTORY] --name {NAME} --createTests --cypressPath {CYPRESSPATH} | ||
``` | ||
|
||
Where `[DIRECTORY]` is the location to output the content type scaffolding, `{NAME}` is the name of the new content type, and `{CYPRESSPATH}` is the relative path to be used for the cypress tests folder. | ||
|
||
> Using the CLI is the preferred way to create new content types as it will ensure the correct file structure and naming conventions are used. It will also scaffold the mapping object, server plugin, and Vue component for you. | ||
## The mapping object | ||
|
||
The mapping object is responsible for supplying the data mapping and includes. | ||
|
||
- **Data mapping**: the data mapping is responsible for mapping the Tide API response data (i.e. Drupal JSON) into a clean format that can be used within the Vue component. | ||
- **Includes**: the includes are an array of fields that need to be added to the API request so Drupal knows what referenced entities to include in the API response, for more on Drupal includes see [JSON API includes](https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/includes). | ||
|
||
Below is an example mapping object. | ||
|
||
```ts | ||
import type { IRplTideModuleMapping } from '@dpc-sdp/ripple-tide-api/types' | ||
import { tidePageBaseMapping, tidePageBaseIncludes } from '@dpc-sdp/nuxt-ripple/mapping' | ||
import { getField, getImageFromField } from '@dpc-sdp/ripple-tide-api' | ||
|
||
const tideMyConentTypeModule: IRplTideModuleMapping = { | ||
mapping: { | ||
// The base mapping is used to add common fields | ||
// See the tidePageBaseMapping function for details | ||
...tidePageBaseMapping({ withTopicTags: true }), | ||
|
||
content: 'field_my_content_type_content', | ||
|
||
// Can be nested as needed | ||
header: { | ||
intro: 'field_landing_page_intro_text', | ||
}, | ||
|
||
// Use a function to transform the data | ||
files: (src) => getField(src, 'field_node_files').map((file) => ({ | ||
name: file.name, | ||
url: file.field_media_file.url | ||
})), | ||
|
||
// Or when using helper functions | ||
image: (src) => getImageFromField(src, 'field_featured_image.field_media_image') | ||
}, | ||
includes: [ | ||
// The base includes are used to include common fields | ||
// See the tidePageBaseIncludes function for details | ||
...tidePageBaseIncludes({ withTopicTags: true }), | ||
|
||
'field_featured_image', | ||
'field_featured_image.field_media_image' | ||
] | ||
} | ||
|
||
export default tideMyConentTypeModule | ||
``` | ||
|
||
## The server plugin | ||
|
||
The server plugin is responsible for registering the new content type with `@dpc-sdp/ripple-tide-api`, this file needs to live within the `server/plugins` directory of the content type. | ||
|
||
The content type can then be registered by calling the `setContentType` method on the Tide page API. This method takes two arguments, the first is the name of the content type, and the second is the mapping object. If you have used the CLI to create the content type this will already be done for you. | ||
|
||
```ts | ||
import type { NitroApp } from 'nitropack' | ||
import { defineNitroPlugin } from 'nitropack/dist/runtime/plugin' | ||
import tideMyContentTypeModule from '../../mapping' | ||
|
||
export default defineNitroPlugin(async (nitroApp: NitroApp) => { | ||
nitroApp.tide?.pageApi.setContentType('my-content-type', tideMyContentTypeModule) | ||
}) | ||
``` | ||
|
||
## The Vue component | ||
|
||
A Vue component is needed to render the content type. This component will receive a `page` prop, it's this prop that contains the mapped data from the Tide API response. | ||
|
||
> **Important note**: The component needs to be registered globally, this can be done by adding the component to a `/components/global` folder within the content type. Because this is global the name needs to be unique, it must also be prefixed with `Tide` i.e. `TideMyContentType`. | ||
This component should use the `TideBaseLayout` component to render the shell of the site, from there slots are used to render the content types content in the right locations. If you're using the CLI it will take care of scaffolding a global component that uses `TideBaseLayout` for you. | ||
|
||
Below is an example component using `TideBaseLayout`. | ||
|
||
```vue | ||
<template> | ||
<TideBaseLayout | ||
:site="site" | ||
:page="page" | ||
:siteSection="page.siteSection" | ||
:pageTitle="page.title" | ||
:pageLanguage="page.lang" | ||
:updatedDate="page.changed || page.created" | ||
:showContentRating="page.showContentRating" | ||
> | ||
<template #aboveHeader> | ||
<slot name="aboveHeader"></slot> | ||
</template> | ||
<template #primaryNav> | ||
<slot name="primaryNav"></slot> | ||
</template> | ||
<template #breadcrumbs> | ||
<slot name="breadcrumbs"></slot> | ||
</template> | ||
<template #aboveBody> | ||
<slot name="aboveBody"></slot> | ||
</template> | ||
<template #body> | ||
<!-- Add content within the relevant slot --> | ||
<TideMyContentTypeContent :content="page.content" /> | ||
</template> | ||
<template #sidebar> | ||
<slot name="sidebar"></slot> | ||
</template> | ||
<template #footer> | ||
<slot name="footer"></slot> | ||
</template> | ||
</TideBaseLayout> | ||
</template> | ||
<script setup lang="ts"> | ||
import { TideSiteData } from '@dpc-sdp/ripple-tide-api/types' | ||
import type { TideMediaPage } from '../../types' | ||
interface Props { | ||
site: TideSiteData | ||
page: TideMediaPage | ||
} | ||
defineProps<Props>() | ||
</script> | ||
``` | ||
|
||
## Using the content type | ||
|
||
Content types are actually [Nuxt layers](https://nuxt.com/docs/getting-started/layers) so these are added in the same way as any other layer through the extends property of the main applications `nuxt.config.ts` file. | ||
|
||
```js | ||
Some code | ||
export default defineNuxtConfig({ | ||
extends: [ | ||
'@dpc-sdp/ripple-tide-news', // An npm installed package | ||
'github:dpc-sdp/ripple-tide-news#1.0.0', // A tagged git repository | ||
'./layers/tide-my-content-type', // A local layer | ||
] | ||
}) | ||
``` |