Skip to content

Commit

Permalink
feat: merge mixins options
Browse files Browse the repository at this point in the history
Closes #261
  • Loading branch information
adrienbaron committed Sep 6, 2018
1 parent 33a7aa4 commit ad4d61c
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 19 deletions.
27 changes: 18 additions & 9 deletions src/shared/getComponentOption.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import deepmerge from 'deepmerge'
import isArray from './isArray'

/**
* Returns the `opts.option` $option value of the given `opts.component`.
Expand All @@ -24,16 +25,10 @@ export default function getComponentOption (opts, result = {}) {
if (typeof $options[option] !== 'undefined' && $options[option] !== null) {
let data = $options[option]

// if option is a function, replace it with it's result
if (typeof data === 'function') {
data = data.call(component)
}

if (typeof data === 'object') {
// merge with existing options
result = deepmerge(result, data, { arrayMerge })
if (isArray(data)) {
result = data.reduce((result, dataItem) => mergeDataInResult(dataItem, result, component, arrayMerge), result)
} else {
result = data
result = mergeDataInResult(data, result, component, arrayMerge)
}
}

Expand Down Expand Up @@ -67,3 +62,17 @@ export default function getComponentOption (opts, result = {}) {
}
return result
}

function mergeDataInResult (data, result, component, arrayMerge) {
// if option is a function, replace it with it's result
if (typeof data === 'function') {
data = data.call(component)
}

if (typeof data === 'object') {
// merge with existing options
return deepmerge(result, data, { arrayMerge })
} else {
return data
}
}
3 changes: 3 additions & 0 deletions src/shared/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export default function VueMeta (Vue, options = {}) {
// bind the $meta method to this component instance
Vue.prototype.$meta = $meta(options)

// define optionMergeStrategies for the keyName
Vue.config.optionMergeStrategies[options.keyName] = Vue.config.optionMergeStrategies.created

// store an id to keep track of DOM updates
let batchID = null

Expand Down
79 changes: 69 additions & 10 deletions test/getComponentOption.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,77 @@ describe('getComponentOption', () => {
expect(mergedOption).to.eql({ bar: 'baz', fizz: 'buzz' })
})

it('allows for a custom array merge strategy', () => {
it('allows for a custom array merge strategy in object literal', () => {
Vue.component('array-child', {
template: '<div></div>',
foo: [
foo: {
flowers: [
{ name: 'flower', content: 'rose' }
]
}
})

component = new Vue({
render: (h) => h('div', null, [h('array-child')]),
foo: {
flowers: [
{ name: 'flower', content: 'tulip' }
]
},
el: container
})

const mergedOption = getComponentOption({
component,
option: 'foo',
deep: true,
arrayMerge (target, source) {
return target.concat(source)
}
})

expect(mergedOption).to.eql({
flowers: [
{ name: 'flower', content: 'tulip' },
{ name: 'flower', content: 'rose' }
]
})
})

it('merges arrays of objects literal options', () => {
component = new Vue({ someOption: [{ foo: 'hello' }, { bar: 'there' }] })

const mergedOption = getComponentOption({ component, option: 'someOption' })
expect(mergedOption).to.eql({ foo: 'hello', bar: 'there' })
})

it('merges arrays of mixed object literals and functions', () => {
component = new Vue({
render: (h) => h('div', null, [h('array-child')]),
cake: 'good',
desserts: [
{ yogurt: 'meh' },
function someFunction () {
return { cake: this.$options.cake }
},
function someOtherFunction () {
return { pineapple: 'not bad' }
}
]
})

const mergedOption = getComponentOption({ component, option: 'desserts' })
expect(mergedOption).to.eql({ yogurt: 'meh', cake: 'good', pineapple: 'not bad' })
})

it('uses custom array merge strategy when merging arrays in arrays of options', () => {
component = new Vue({
template: '<div></div>',
foo: [
{ name: 'flower', content: 'tulip' }
],
el: container
{ cars: [{ brand: 'renault', color: 'red' }] },
function someFunction () {
return { cars: [{ brand: 'peugeot', color: 'blue' }] }
}
]
})

const mergedOption = getComponentOption({
Expand All @@ -68,9 +125,11 @@ describe('getComponentOption', () => {
}
})

expect(mergedOption).to.eql([
{ name: 'flower', content: 'tulip' },
{ name: 'flower', content: 'rose' }
])
expect(mergedOption).to.eql({
cars: [
{ brand: 'renault', color: 'red' },
{ brand: 'peugeot', color: 'blue' }
]
})
})
})
91 changes: 91 additions & 0 deletions test/getMetaInfo.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const defaultOptions = {

const getMetaInfo = _getMetaInfo(defaultOptions)

// define optionMergeStrategies for the keyName
Vue.config.optionMergeStrategies[VUE_META_KEY_NAME] = Vue.config.optionMergeStrategies.created

describe('getMetaInfo', () => {
// const container = document.createElement('div')
let component
Expand Down Expand Up @@ -530,4 +533,92 @@ describe('getMetaInfo', () => {
__dangerouslyDisableSanitizersByTagID: {}
})
})

it('properly merges mixins options', () => {
const mixin1 = {
metaInfo: function () {
return {
title: 'This title will be overridden',
meta: [
{
vmid: 'og:title',
property: 'og:title',
content: 'This title will be overridden'
},
{
vmid: 'og:fromMixin1',
property: 'og:fromMixin1',
content: 'This is from mixin1'
}
]
}
}
}
const mixin2 = {
metaInfo: {
meta: [
{
vmid: 'og:fromMixin2',
property: 'og:fromMixin2',
content: 'This is from mixin2'
}
]
}
}
const component = new Vue({
mixins: [mixin1, mixin2],
metaInfo: {
title: 'New Title',
meta: [
{
vmid: 'og:title',
property: 'og:title',
content: 'New Title! - My page'
},
{
vmid: 'og:description',
property: 'og:description',
content: 'Some Description'
}
]
}
})
expect(getMetaInfo(component)).to.eql({
title: 'New Title',
titleChunk: 'New Title',
titleTemplate: '%s',
htmlAttrs: {},
headAttrs: {},
bodyAttrs: {},
meta: [
{
vmid: 'og:fromMixin1',
property: 'og:fromMixin1',
content: 'This is from mixin1'
},
{
vmid: 'og:fromMixin2',
property: 'og:fromMixin2',
content: 'This is from mixin2'
},
{
vmid: 'og:title',
property: 'og:title',
content: 'New Title! - My page'
},
{
vmid: 'og:description',
property: 'og:description',
content: 'Some Description'
}
],
base: [],
link: [],
style: [],
script: [],
noscript: [],
__dangerouslyDisableSanitizers: [],
__dangerouslyDisableSanitizersByTagID: {}
})
})
})
5 changes: 5 additions & 0 deletions test/plugin.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,9 @@ describe('plugin', () => {
const vm = new Vue(Component).$mount()
expect(vm._hasMetaInfo).to.equal(true)
})

it('setup optionMergeStrategies for the keyName', () => {
const strats = Vue.config.optionMergeStrategies
expect(strats[VUE_META_KEY_NAME]).to.equal(strats.created)
})
})

0 comments on commit ad4d61c

Please sign in to comment.