Skip to content

Commit

Permalink
fix(addon-docs): Handle object-in-array class (Vue.js)
Browse files Browse the repository at this point in the history
https://vuejs.org/v2/guide/class-and-style.html#Array-Syntax

Add support for below.

```vue
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
```
  • Loading branch information
pocka committed Nov 29, 2020
1 parent fe2a84b commit 2e724f7
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 21 deletions.
16 changes: 8 additions & 8 deletions addons/docs/src/frameworks/vue/sourceDecorator.test.ts
Expand Up @@ -64,30 +64,30 @@ describe('vnodeToString', () => {
expect(
vnodeToString(
getVNode({
template: `<button :class="['foo', null, false, 0]">Button</button>`,
template: `<button :class="['foo', null, false, 0, {bar: true, baz: false}]">Button</button>`,
})
)
).toMatchInlineSnapshot(`<button class="foo">Button</button>`);
).toMatchInlineSnapshot(`<button class="foo bar">Button</button>`);
});

it('merge dynamic and static classes', () => {
it('object dynamic class', () => {
expect(
vnodeToString(
getVNode({
template: `<button class="foo" :class="{bar: null, baz: 1}">Button</button>`,
template: `<button :class="{foo: true, bar: false}">Button</button>`,
})
)
).toMatchInlineSnapshot(`<button class="foo baz">Button</button>`);
).toMatchInlineSnapshot(`<button class="foo">Button</button>`);
});

it('object dynamic class', () => {
it('merge dynamic and static classes', () => {
expect(
vnodeToString(
getVNode({
template: `<button :class="{foo: true, bar: false}">Button</button>`,
template: `<button class="foo" :class="{bar: null, baz: 1}">Button</button>`,
})
)
).toMatchInlineSnapshot(`<button class="foo">Button</button>`);
).toMatchInlineSnapshot(`<button class="foo baz">Button</button>`);
});

it('attributes', () => {
Expand Down
40 changes: 27 additions & 13 deletions addons/docs/src/frameworks/vue/sourceDecorator.ts
Expand Up @@ -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) : []),
]
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 2e724f7

Please sign in to comment.