Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SD-569] Moved data driven components feature into core #1406

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions examples/nuxt-app/layers/example-data-driven-component/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## About this layer

An example of setting up a data driven component for testing purposes

Data driven components can be registered in the Nuxt `app.config` file:

```ts
export default defineAppConfig({
ripple: {
dataDrivenComponents: {
// add key of field_data_driven_component and value of component name to render
// eg: find_a_council_map: 'VicCouncilLookup'
example_ddc: 'ExampleDDC'
}
}
})
```

Note that the vue component (e.g. ExampleDDC) must be declared globally for this to work, see https://nuxt.com/docs/guide/directory-structure/components.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default defineAppConfig({
ripple: {
featureFlags: {
contentCollectionSearchConnector: 'elasticsearch'
},
dataDrivenComponents: {
// add key of field_data_driven_component and value of component name to render
// eg: find_a_council_map: 'VicCouncilLookup'
example_ddc: 'ExampleDDC'
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<h3>{{ title }}</h3>
<div v-html="description" />
<p>{{ testCustomProp }}</p>
</template>

<script setup lang="ts">
interface Props {
title: string
description: string
testCustomProp: string
}

defineProps<Props>()
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { defineNuxtConfig } from 'nuxt/config'

export default defineNuxtConfig({})
3 changes: 2 additions & 1 deletion examples/nuxt-app/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export default defineNuxtConfig({
'./layers/example-components',
'./layers/fixture-api',
'./layers/map-features',
'./layers/ripple-ui-forms-ext'
'./layers/ripple-ui-forms-ext',
'./layers/example-data-driven-component'
],
// Nuxt devtools
sourcemap: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,9 @@ Feature: Home page
Then the dataLayer should include the following events
| event | element_id | element_text | label | file_name | file_extension | type | component |
| file_download | page-component-1951 | Download it | Complex image | medium.png | png | image | rpl-media-embed |

@mockserver
Scenario: Page component - Data driven component
Then a custom data driven component with ID "3553540" should exist with title "Test data driven title" and have the properties
| description | testCustomProp |
| Test data driven desc | testCustomValue |
12 changes: 12 additions & 0 deletions examples/nuxt-app/test/fixtures/landingpage/home.json
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,18 @@
"allowFullscreen": true,
"showTitle": true
}
},
{
"uuid": "0aa14648-604d-4aab-833a-bbce065433f4",
"component": "ExampleDDC",
"id": "3553540",
"props": {
"title": "Test data driven title",
"description": "<p>Test data driven desc</p>",
"field": "example_ddc",
"component": "ExampleDDC",
"testCustomProp": "testCustomValue"
}
}
],
"meta": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Then, DataTable } from '@badeball/cypress-cucumber-preprocessor'

Then(
'a custom data driven component with ID {string} should exist with title {string} and have the properties',
(id: string, title: string, dataTable: DataTable) => {
const data = dataTable.hashes()[0]

cy.get(`[data-component-id="${id}"]`).as('component')
cy.get('@component').should('exist')
cy.get(`@component`).should(
'have.attr',
'data-component-type',
'ExampleDDC'
)
cy.get('@component').within(() => {
cy.get(`h3`).should('have.text', title)
})

cy.get('@component').should('contain', data.description)
cy.get('@component').should('contain', data.testCustomProp)
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ import './category-grid'
import './content-collection'
import './custom-collection'
import './social-share'
import './data-driven-component'
4 changes: 4 additions & 0 deletions packages/ripple-tide-landing-page/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ export default defineAppConfig({
ripple: {
featureFlags: {
contentCollectionSearchConnector: 'elasticsearch'
},
dataDrivenComponents: {
// add key of field_data_driven_component and value of component name to render
// eg: find_a_council_map: 'VicCouncilLookup'
}
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<DevOnly>
<div class="rpl-callout callout-error">
<h3>Data driven component {{ component }} not found</h3>
<p>Check the component mapping for {{ field }} in app.config</p>
</div>
</DevOnly>
</template>

<script setup lang="ts">
interface Props {
component: string
field: string
}

defineProps<Props>()
</script>

<style>
.callout-error {
--rpl-clr-accent: var(--rpl-clr-error);
--rpl-clr-accent-alt: var(--rpl-clr-error-light);
}
</style>
4 changes: 3 additions & 1 deletion packages/ripple-tide-landing-page/mapping/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import complexImageMapping from './components/complex-image/complex-image-mappin
import dataTableMapping from './components/data-table/data-table-mapping'
import compactCardsMapping from './components/compact-cards/compact-cards-mapping'
import openFormsMapping from './components/openforms/openforms-mapping'
import dataDrivenComponentMapping from './components/data-driven-component/data-driven-component-mapping'

const mappings: {
[key: string]: {
Expand All @@ -45,7 +46,8 @@ const mappings: {
'paragraph--complex_image': complexImageMapping,
'paragraph--data_table': dataTableMapping,
'paragraph--compact_card_collection': compactCardsMapping,
'paragraph--form_embed_openforms': openFormsMapping
'paragraph--form_embed_openforms': openFormsMapping,
'paragraph--data_driven_component': dataDrivenComponentMapping
}

// Add reusable include on whitelisted paragraph types
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* eslint-disable no-prototype-builtins */
import type { TideDynamicPageComponent } from '@dpc-sdp/ripple-tide-api/types'
import { logger } from '@dpc-sdp/ripple-tide-api'
import { useAppConfig } from '#imports'

export interface ITideDataDrivenComponent {
component?: string
description: string
title: string
field?: string
data?: unknown
}

interface IDDCAppConfig {
ripple?: {
dataDrivenComponents?: Record<string, string>
}
}

export const dataDrivenComponentMapping = (
field: any
): TideDynamicPageComponent<ITideDataDrivenComponent> => {
const getComponent = (field: any) => {
const appConfig = useAppConfig() as IDDCAppConfig
if (
appConfig?.ripple?.dataDrivenComponents?.hasOwnProperty(
field.field_data_driven_component
)
) {
return appConfig.ripple.dataDrivenComponents[
field.field_data_driven_component
]
}
return 'TideLandingPageDataDrivenCmpError'
}
let dataProps = {}
try {
dataProps = JSON.parse(field.field_configuration)
} catch (error) {
logger.error(`Error parsing data driven component extra data`, error)
}

return {
component: getComponent(field),
id: `${field.drupal_internal__id}`,
props: {
title: field.field_paragraph_title,
description: field.field_paragraph_body?.processed,
field: field.field_data_driven_component,
component: getComponent(field),
...dataProps
}
}
}

export default {
includes: [],
mapping: dataDrivenComponentMapping,
contentTypes: ['landing_page']
}
Loading