From 922993485e878921a0b119bacf4bfb04f8b21245 Mon Sep 17 00:00:00 2001 From: Wolfgang Ziegler // fago Date: Mon, 20 Jan 2025 22:01:17 -0600 Subject: [PATCH 1/3] update code --- src/runtime/composables/useDrupalCe/index.ts | 32 ++++++++++++-------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/runtime/composables/useDrupalCe/index.ts b/src/runtime/composables/useDrupalCe/index.ts index 32ceb01..6270c8e 100644 --- a/src/runtime/composables/useDrupalCe/index.ts +++ b/src/runtime/composables/useDrupalCe/index.ts @@ -1,7 +1,7 @@ import { defu } from 'defu' import { appendResponseHeader } from 'h3' import type { $Fetch, NitroFetchRequest } from 'nitropack' -import type { Ref, ComputedRef } from 'vue' +import type { Ref, ComputedRef, Component } from 'vue' import { getDrupalBaseUrl, getMenuBaseUrl } from './server' import type { UseFetchOptions } from '#app' import { callWithNuxt } from '#app' @@ -271,21 +271,22 @@ export const useDrupalCe = () => { * * @param customElements - Custom element data that can be: * - null/undefined (returns null, skipping render) - * - string (auto-creates component rendering string as HTML if it contains markup) + * - string (rendered inside a wrapping div element) * - single custom element object with {element: string, ...props} - * - array of custom element objects - * @returns Vue component definition, null for skipped render, or HTML-capable component for strings. - * Result can be used directly with Vue's dynamic component: + * - array of strings or custom element objects (rendered inside a wrapping div element) + * @returns Component | null - A Vue component that can be used with . + * Returns null for skipped render, otherwise returns a Vue component + * (either a custom element component or a wrapping div component for strings/arrays). */ const renderCustomElements = ( - customElements: null | undefined | string | Record | Array, - ) => { + customElements: null | undefined | string | Record | Array, + ): Component | null => { // Handle null/undefined case if (customElements == null) { return null } - // Handle string case by creating a component that can render HTML + // Handle string case by creating a component with wrapping div if (typeof customElements === 'string') { return defineComponent({ setup() { @@ -297,14 +298,21 @@ export const useDrupalCe = () => { } // Handle empty object case - if (typeof customElements === 'object' && Object.keys(customElements).length === 0) { + if (Object.keys(customElements).length === 0) { return null } - // Handle array of custom elements + // Handle array case by creating a wrapper div component that renders all children if (Array.isArray(customElements)) { - return customElements.map((customElement) => { - return renderCustomElements(customElement) + return defineComponent({ + setup() { + return () => h('div', {}, + customElements.map(element => { + const rendered = renderCustomElements(element) + return rendered ? h(rendered) : null + }) + ) + } }) } From 8bcc0bd6258ac7280eb0092949906f983d49fb4e Mon Sep 17 00:00:00 2001 From: Wolfgang Ziegler // fago Date: Mon, 20 Jan 2025 22:10:48 -0600 Subject: [PATCH 2/3] update test case --- test/unit/renderCustomElements.test.ts | 80 +++++++++++--------------- 1 file changed, 33 insertions(+), 47 deletions(-) diff --git a/test/unit/renderCustomElements.test.ts b/test/unit/renderCustomElements.test.ts index 8fca9d0..4a440dc 100644 --- a/test/unit/renderCustomElements.test.ts +++ b/test/unit/renderCustomElements.test.ts @@ -23,13 +23,6 @@ describe('renderCustomElements', () => { }) describe('basic input handling', () => { - const NullRenderer = defineComponent({ - setup() { - return { component: renderCustomElements(null) } - }, - template: '' - }) - it('should return null for empty inputs', () => { expect(renderCustomElements(null)).toBe(null) expect(renderCustomElements(undefined)).toBe(null) @@ -37,32 +30,35 @@ describe('renderCustomElements', () => { }) it('should render nothing when component is null', async () => { - const wrapper = await mountSuspended(NullRenderer) + const wrapper = await mountSuspended(defineComponent({ + setup() { + return { component: renderCustomElements(null) } + }, + template: '' + })) expect(wrapper.html()).toBe('') }) }) describe('string rendering', () => { it('should render plain text', async () => { - const TextRenderer = defineComponent({ + const wrapper = await mountSuspended(defineComponent({ setup() { return { component: renderCustomElements('Hello World') } }, template: '' - }) - const wrapper = await mountSuspended(TextRenderer) + })) expect(wrapper.text()).toBe('Hello World') }) it('should render HTML string preserving markup', async () => { const htmlString = '

Hello World

' - const HtmlRenderer = defineComponent({ + const wrapper = await mountSuspended(defineComponent({ setup() { return { component: renderCustomElements(htmlString) } }, template: '' - }) - const wrapper = await mountSuspended(HtmlRenderer) + })) expect(wrapper.html()).toContain(htmlString) expect(wrapper.text()).toBe('Hello World') }) @@ -70,7 +66,7 @@ describe('renderCustomElements', () => { describe('custom element rendering', () => { it('should render a single custom element', async () => { - const ComponentRenderer = defineComponent({ + const wrapper = await mountSuspended(defineComponent({ components: { TestComponent }, setup() { return { component: renderCustomElements({ @@ -79,44 +75,37 @@ describe('renderCustomElements', () => { })} }, template: '' - }) - const wrapper = await mountSuspended(ComponentRenderer) + })) expect(wrapper.text()).toBe('Test Component: bar') }) }) describe('array handling', () => { - it('should render array of strings', async () => { - const StringArrayRenderer = defineComponent({ + it('should render array of strings in a wrapper div', async () => { + const wrapper = await mountSuspended(defineComponent({ setup() { - const content = ['Text 1', '

Text 2

'] - return { - components: content.map(item => renderCustomElements(item)) - } + return { component: renderCustomElements(['Text 1', '

Text 2

']) } }, - template: '
' - }) - const wrapper = await mountSuspended(StringArrayRenderer) + template: '' + })) + expect(wrapper.html()).toContain('
') expect(wrapper.text()).toContain('Text 1') expect(wrapper.text()).toContain('Text 2') expect(wrapper.html()).toContain('

Text 2

') }) - it('should render array of custom elements', async () => { - const ElementArrayRenderer = defineComponent({ + it('should render array of custom elements in a wrapper div', async () => { + const wrapper = await mountSuspended(defineComponent({ components: { TestComponent, AnotherComponent }, setup() { - const content = [ - { element: 'test-component', foo: 'one' }, - { element: 'another-component', bar: 'two' } - ] - return { - components: content.map(item => renderCustomElements(item)) - } + return { component: renderCustomElements([ + { element: 'test-component', foo: 'one' }, + { element: 'another-component', bar: 'two' } + ]) } }, - template: '
' - }) - const wrapper = await mountSuspended(ElementArrayRenderer) + template: '' + })) + expect(wrapper.html()).toContain('
') expect(wrapper.text()).toContain('Test Component: one') expect(wrapper.text()).toContain('Another Component: two') }) @@ -124,19 +113,18 @@ describe('renderCustomElements', () => { describe('edge cases', () => { it('should handle malformed element objects', async () => { - const MalformedRenderer = defineComponent({ + const wrapper = await mountSuspended(defineComponent({ components: { TestComponent }, setup() { return { component: renderCustomElements({ element: 'test-component' })} }, template: '' - }) - const wrapper = await mountSuspended(MalformedRenderer) + })) expect(wrapper.text()).toBe('Test Component:') }) it('should handle nonexistent components', async () => { - const NonexistentRenderer = defineComponent({ + const wrapper = await mountSuspended(defineComponent({ setup() { return { component: renderCustomElements({ element: 'nonexistent-component', @@ -144,19 +132,17 @@ describe('renderCustomElements', () => { })} }, template: '' - }) - const wrapper = await mountSuspended(NonexistentRenderer) + })) expect(wrapper.html()).toBe('') }) it('should handle empty arrays', async () => { - const EmptyArrayRenderer = defineComponent({ + const wrapper = await mountSuspended(defineComponent({ setup() { return { component: renderCustomElements([]) } }, template: '' - }) - const wrapper = await mountSuspended(EmptyArrayRenderer) + })) expect(wrapper.html()).toBe('') }) }) From b2aa078e48c93e363575102d71f52f56ce11e28f Mon Sep 17 00:00:00 2001 From: Wolfgang Ziegler // fago Date: Mon, 20 Jan 2025 22:18:19 -0600 Subject: [PATCH 3/3] add test --- test/unit/renderCustomElements.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/unit/renderCustomElements.test.ts b/test/unit/renderCustomElements.test.ts index 4a440dc..3208935 100644 --- a/test/unit/renderCustomElements.test.ts +++ b/test/unit/renderCustomElements.test.ts @@ -3,12 +3,14 @@ import { describe, it, expect } from 'vitest' import { mountSuspended } from '@nuxt/test-utils/runtime' import { defineComponent } from 'vue' import { useDrupalCe } from '../../src/runtime/composables/useDrupalCe' +import {useNuxtApp} from "#imports"; describe('renderCustomElements', () => { const { renderCustomElements } = useDrupalCe() // Define reusable test components const TestComponent = defineComponent({ + name: 'TestComponent', props: { foo: String }, @@ -16,11 +18,15 @@ describe('renderCustomElements', () => { }) const AnotherComponent = defineComponent({ + name: 'AnotherComponent', props: { bar: String }, template: '
Another Component: {{ bar }}
' }) + const app = useNuxtApp() + app.vueApp.component('TestComponent', TestComponent) + app.vueApp.component('AnotherComponent', AnotherComponent) describe('basic input handling', () => { it('should return null for empty inputs', () => { @@ -67,7 +73,6 @@ describe('renderCustomElements', () => { describe('custom element rendering', () => { it('should render a single custom element', async () => { const wrapper = await mountSuspended(defineComponent({ - components: { TestComponent }, setup() { return { component: renderCustomElements({ element: 'test-component', @@ -96,7 +101,6 @@ describe('renderCustomElements', () => { it('should render array of custom elements in a wrapper div', async () => { const wrapper = await mountSuspended(defineComponent({ - components: { TestComponent, AnotherComponent }, setup() { return { component: renderCustomElements([ { element: 'test-component', foo: 'one' }, @@ -114,7 +118,6 @@ describe('renderCustomElements', () => { describe('edge cases', () => { it('should handle malformed element objects', async () => { const wrapper = await mountSuspended(defineComponent({ - components: { TestComponent }, setup() { return { component: renderCustomElements({ element: 'test-component' })} },