Creating content types
How to create new content types for the SDP platform
See 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 - maps API data to pass to a Vue component
- A server plugin - registers the content type with
@dpc-sdp/ripple-tide-api
- And Vue components - 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.
Below is an example mapping object.
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.
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 withTide
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
.
<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 so these are added in the same way as any other layer through the extends property of the main applications nuxt.config.ts
file.
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
]
})
Propose a change to this page on GitHub.