Skip to content
Permalink
Browse files
fix(ssr): avoid tree missmatches by either using domProps or `child…
…ren` (closes #5453, #5557) (#5723)

* fix(ssr): avoid tree mismetaches by either using `domProps` or `children`

* Update README.md
  • Loading branch information
jacobmllr95 committed Sep 4, 2020
1 parent f54e427 commit 5e8dad84c094ff1f7810f69293418b81e676af26
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 27 deletions.
@@ -2,6 +2,7 @@ import Vue from '../../utils/vue'
import { arrayIncludes } from '../../utils/array'
import { getComponentConfig } from '../../utils/config'
import { htmlOrText } from '../../utils/html'
import { toString } from '../../utils/string'
import dropdownMixin from '../../mixins/dropdown'
import idMixin from '../../mixins/id'
import normalizeSlotMixin from '../../mixins/normalize-slot'
@@ -15,14 +16,12 @@ const NAME = 'BDropdown'

export const props = {
text: {
// Button label
type: String,
default: ''
type: String
// default: null
},
html: {
// Button label
type: String
// default: undefined
// default: null
},
variant: {
type: String,
@@ -141,11 +140,12 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
}
},
render(h) {
const { variant, size, block, disabled, split, role } = this
const { visible, variant, size, block, disabled, split, role, hide, toggle } = this
const commonProps = { variant, size, block, disabled }

const $buttonContent = this.normalizeSlot('button-content')
const buttonContentProps = this.hasNormalizedSlot('button-content')
const buttonContentSlotName = 'button-content'
let $buttonChildren = this.normalizeSlot(buttonContentSlotName)
let buttonContentDomProps = this.hasNormalizedSlot(buttonContentSlotName)
? {}
: htmlOrText(this.html, this.text)

@@ -154,8 +154,9 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
const { splitTo, splitHref, splitButtonType } = this
const btnProps = {
...commonProps,
variant: this.splitVariant || this.variant
variant: this.splitVariant || variant
}

// We add these as needed due to <router-link> issues with
// defined property with `undefined`/`null` values
if (splitTo) {
@@ -165,18 +166,23 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
} else if (splitButtonType) {
btnProps.type = splitButtonType
}

$split = h(
BButton,
{
class: this.splitClass,
attrs: { id: this.safeId('_BV_button_') },
props: btnProps,
domProps: buttonContentProps,
domProps: buttonContentDomProps,
on: { click: this.onSplitClick },
ref: 'button'
},
[$buttonContent]
$buttonChildren
)

// Overwrite button content for the toggle when in `split` mode
$buttonChildren = [h('span', { class: ['sr-only'] }, [this.toggleText])]
buttonContentDomProps = {}
}

const $toggle = h(
@@ -187,22 +193,22 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
attrs: {
id: this.safeId('_BV_toggle_'),
'aria-haspopup': 'true',
'aria-expanded': this.visible ? 'true' : 'false'
'aria-expanded': toString(visible)
},
props: {
...commonProps,
tag: this.toggleTag,
block: block && !split
},
domProps: split ? {} : buttonContentProps,
domProps: buttonContentDomProps,
on: {
mousedown: this.onMousedown,
click: this.toggle,
keydown: this.toggle // Handle ENTER, SPACE and DOWN
click: toggle,
keydown: toggle // Handle ENTER, SPACE and DOWN
},
ref: 'toggle'
},
[split ? h('span', { class: ['sr-only'] }, [this.toggleText]) : $buttonContent]
$buttonChildren
)

const $menu = h(
@@ -220,7 +226,7 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
},
ref: 'menu'
},
!this.lazy || this.visible ? this.normalizeSlot('default', { hide: this.hide }) : [h()]
[!this.lazy || visible ? this.normalizeSlot('default', { hide }) : h()]
)

return h(
@@ -900,7 +900,7 @@ export const BModal = /*#__PURE__*/ Vue.extend({
: htmlOrText(this.titleHtml, this.title)
},
// TODO: Rename slot to `title` and deprecate `modal-title`
[this.normalizeSlot('modal-title', this.slotScope)]
this.normalizeSlot('modal-title', this.slotScope)
),
$closeButton
]
@@ -96,7 +96,7 @@ Need more control over the label? Provide your own label by using the default sl
<h5>Custom label via default slot</h5>
<b-progress :max="max" height="2rem">
<b-progress-bar :value="value">
Progress: <strong>{{ value.toFixed(2) }} / {{ max }}</strong>
<span>Progress: <strong>{{ value.toFixed(2) }} / {{ max }}</strong></span>
</b-progress-bar>
</b-progress>

@@ -124,16 +124,16 @@ export const BProgressBar = /*#__PURE__*/ Vue.extend({
render(h) {
const { label, labelHtml, computedValue, computedPrecision } = this

let $content = h()
let $children
let domProps = {}
if (this.hasNormalizedSlot('default')) {
$content = this.normalizeSlot('default')
$children = this.normalizeSlot('default')
} else if (label || labelHtml) {
domProps = htmlOrText(labelHtml, label)
} else if (this.computedShowProgress) {
$content = this.computedProgress
$children = this.computedProgress
} else if (this.computedShowValue) {
$content = toFixed(computedValue, computedPrecision)
$children = toFixed(computedValue, computedPrecision)
}

return h(
@@ -150,7 +150,7 @@ export const BProgressBar = /*#__PURE__*/ Vue.extend({
},
domProps
},
[$content]
$children
)
}
})
@@ -4,6 +4,5 @@ const RX_HTML_TAGS = /(<([^>]+)>)/gi
export const stripTags = (text = '') => String(text).replace(RX_HTML_TAGS, '')

// Generate a `domProps` object for either `innerHTML`, `textContent` or an empty object
export const htmlOrText = (innerHTML, textContent) => {
return innerHTML ? { innerHTML } : textContent ? { textContent } : {}
}
export const htmlOrText = (innerHTML, textContent) =>
innerHTML ? { innerHTML } : textContent ? { textContent } : {}

0 comments on commit 5e8dad8

Please sign in to comment.