From 9ea5ee2fabb146359b7f8c697787f073df859da3 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Thu, 2 Jun 2022 17:02:39 -0400 Subject: [PATCH] Flatten Fragments when rendering --- .../@headlessui-vue/src/utils/render.test.ts | 40 +++++++++++++++++++ packages/@headlessui-vue/src/utils/render.ts | 31 +++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/packages/@headlessui-vue/src/utils/render.test.ts b/packages/@headlessui-vue/src/utils/render.test.ts index ddec4c01ef..47408315ea 100644 --- a/packages/@headlessui-vue/src/utils/render.test.ts +++ b/packages/@headlessui-vue/src/utils/render.test.ts @@ -79,4 +79,44 @@ describe('Validation', () => { expect(document.getElementById('result')).toHaveClass('abc') }) + + it('should allow use of as children', () => { + renderTemplate({ + template: html` + +
Some Content
+
+ `, + + components: { + ExampleOuter: defineComponent({ + template: html` + + + + `, + + components: { + ExampleInner: defineComponent({ + components: { Dummy }, + + template: html` + + + + + + `, + }), + }, + }), + }, + }) + + expect(document.getElementById('result')).toHaveClass('foo') + expect(document.getElementById('result')).toHaveClass('bar') + + // TODO: Is this the expected behavior? Should it actually be `345`? + expect(document.getElementById('result')).toHaveAttribute('data-test', '123') + }) }) diff --git a/packages/@headlessui-vue/src/utils/render.ts b/packages/@headlessui-vue/src/utils/render.ts index 10c742655b..172bd0d8ba 100644 --- a/packages/@headlessui-vue/src/utils/render.ts +++ b/packages/@headlessui-vue/src/utils/render.ts @@ -1,4 +1,4 @@ -import { h, cloneVNode, Slots } from 'vue' +import { h, cloneVNode, Slots, Fragment, VNode } from 'vue' import { match } from './match' export enum Features { @@ -102,6 +102,8 @@ function _render({ } if (as === 'template') { + children = flattenFragments(children as VNode[]) + if (Object.keys(incomingProps).length > 0 || Object.keys(attrs).length > 0) { let [firstChild, ...other] = children ?? [] @@ -144,6 +146,33 @@ function _render({ return h(as, Object.assign({}, incomingProps, dataAttributes), children) } +/** + * When passed a structure like this: + * something + * + * And Example is defined as: + * + * + * We need to turn the fragment that represents into the slot. + * Luckily by this point it's already rendered into an array of VNodes + * for us so we can just flatten it directly. + * + * We have to do this recursively because there could be multiple + * levels of Component nesting all with elements interspersed + * + * @param children + * @returns + */ +function flattenFragments(children: VNode[]): VNode[] { + return children.flatMap((child) => { + if (child.type === Fragment) { + return flattenFragments(child.children as VNode[]) + } + + return [child] + }) +} + export function compact>(object: T) { let clone = Object.assign({}, object) for (let key in clone) {