diff --git a/addons/docs/src/frameworks/vue/sourceDecorator.test.ts b/addons/docs/src/frameworks/vue/sourceDecorator.test.ts
index c4aa381a3a4c..54695c1ea84f 100644
--- a/addons/docs/src/frameworks/vue/sourceDecorator.test.ts
+++ b/addons/docs/src/frameworks/vue/sourceDecorator.test.ts
@@ -64,30 +64,30 @@ describe('vnodeToString', () => {
expect(
vnodeToString(
getVNode({
- template: ``,
+ template: ``,
})
)
- ).toMatchInlineSnapshot(``);
+ ).toMatchInlineSnapshot(``);
});
- it('merge dynamic and static classes', () => {
+ it('object dynamic class', () => {
expect(
vnodeToString(
getVNode({
- template: ``,
+ template: ``,
})
)
- ).toMatchInlineSnapshot(``);
+ ).toMatchInlineSnapshot(``);
});
- it('object dynamic class', () => {
+ it('merge dynamic and static classes', () => {
expect(
vnodeToString(
getVNode({
- template: ``,
+ template: ``,
})
)
- ).toMatchInlineSnapshot(``);
+ ).toMatchInlineSnapshot(``);
});
it('attributes', () => {
diff --git a/addons/docs/src/frameworks/vue/sourceDecorator.ts b/addons/docs/src/frameworks/vue/sourceDecorator.ts
index 6178b60dc66f..40831a68f70c 100644
--- a/addons/docs/src/frameworks/vue/sourceDecorator.ts
+++ b/addons/docs/src/frameworks/vue/sourceDecorator.ts
@@ -76,7 +76,7 @@ export const sourceDecorator = (storyFn: any, context: StoryContext) => {
export function vnodeToString(vnode: Vue.VNode): string {
const attrString = [
...(vnode.data?.slot ? ([['slot', vnode.data.slot]] as [string, any][]) : []),
- ['class', normalizeClassAttribute(vnode)],
+ ['class', stringifyClassAttribute(vnode)],
...(vnode.componentOptions?.propsData ? Object.entries(vnode.componentOptions.propsData) : []),
...(vnode.data?.attrs ? Object.entries(vnode.data.attrs) : []),
]
@@ -123,27 +123,41 @@ export function vnodeToString(vnode: Vue.VNode): string {
.join('')}${tag}>`;
}
-function normalizeClassAttribute(vnode: Vue.VNode): string | undefined {
+function stringifyClassAttribute(vnode: Vue.VNode): string | undefined {
if (!vnode.data || (!vnode.data.staticClass && !vnode.data.class)) {
return undefined;
}
- let dynamicClass: readonly string[] = [];
+ return (
+ [...(vnode.data.staticClass?.split(' ') ?? []), ...normalizeClassBinding(vnode.data.class)]
+ .filter(Boolean)
+ .join(' ') || undefined
+ );
+}
- if (typeof vnode.data.class === 'string') {
- dynamicClass = [vnode.data.class];
- } else if (vnode.data.class instanceof Array) {
- dynamicClass = vnode.data.class;
- } else if (typeof vnode.data.class === 'object') {
- dynamicClass = Object.entries(vnode.data.class)
+// https://vuejs.org/v2/guide/class-and-style.html#Binding-HTML-Classes
+function normalizeClassBinding(binding: unknown): readonly string[] {
+ if (!binding) {
+ return [];
+ }
+
+ if (typeof binding === 'string') {
+ return [binding];
+ }
+
+ if (binding instanceof Array) {
+ // To handle an object-in-array binding smartly, we use recursion
+ return binding.map(normalizeClassBinding).reduce((a, b) => [...a, ...b], []);
+ }
+
+ if (typeof binding === 'object') {
+ return Object.entries(binding)
.filter(([, active]) => !!active)
.map(([className]) => className);
}
- return (
- [...(vnode.data.staticClass?.split(' ') ?? []), ...dynamicClass].filter(Boolean).join(' ') ||
- undefined
- );
+ // Unknown class binding
+ return [];
}
function stringifyAttr(attrName: string, value?: any): string | null {