Skip to content

Commit 5e8dad8

Browse files
authored
fix(ssr): avoid tree missmatches by either using domProps or children (closes #5453, #5557) (#5723)
* fix(ssr): avoid tree mismetaches by either using `domProps` or `children` * Update README.md
1 parent f54e427 commit 5e8dad8

File tree

5 files changed

+32
-27
lines changed

5 files changed

+32
-27
lines changed

src/components/dropdown/dropdown.js

+23-17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Vue from '../../utils/vue'
22
import { arrayIncludes } from '../../utils/array'
33
import { getComponentConfig } from '../../utils/config'
44
import { htmlOrText } from '../../utils/html'
5+
import { toString } from '../../utils/string'
56
import dropdownMixin from '../../mixins/dropdown'
67
import idMixin from '../../mixins/id'
78
import normalizeSlotMixin from '../../mixins/normalize-slot'
@@ -15,14 +16,12 @@ const NAME = 'BDropdown'
1516

1617
export const props = {
1718
text: {
18-
// Button label
19-
type: String,
20-
default: ''
19+
type: String
20+
// default: null
2121
},
2222
html: {
23-
// Button label
2423
type: String
25-
// default: undefined
24+
// default: null
2625
},
2726
variant: {
2827
type: String,
@@ -141,11 +140,12 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
141140
}
142141
},
143142
render(h) {
144-
const { variant, size, block, disabled, split, role } = this
143+
const { visible, variant, size, block, disabled, split, role, hide, toggle } = this
145144
const commonProps = { variant, size, block, disabled }
146145

147-
const $buttonContent = this.normalizeSlot('button-content')
148-
const buttonContentProps = this.hasNormalizedSlot('button-content')
146+
const buttonContentSlotName = 'button-content'
147+
let $buttonChildren = this.normalizeSlot(buttonContentSlotName)
148+
let buttonContentDomProps = this.hasNormalizedSlot(buttonContentSlotName)
149149
? {}
150150
: htmlOrText(this.html, this.text)
151151

@@ -154,8 +154,9 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
154154
const { splitTo, splitHref, splitButtonType } = this
155155
const btnProps = {
156156
...commonProps,
157-
variant: this.splitVariant || this.variant
157+
variant: this.splitVariant || variant
158158
}
159+
159160
// We add these as needed due to <router-link> issues with
160161
// defined property with `undefined`/`null` values
161162
if (splitTo) {
@@ -165,18 +166,23 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
165166
} else if (splitButtonType) {
166167
btnProps.type = splitButtonType
167168
}
169+
168170
$split = h(
169171
BButton,
170172
{
171173
class: this.splitClass,
172174
attrs: { id: this.safeId('_BV_button_') },
173175
props: btnProps,
174-
domProps: buttonContentProps,
176+
domProps: buttonContentDomProps,
175177
on: { click: this.onSplitClick },
176178
ref: 'button'
177179
},
178-
[$buttonContent]
180+
$buttonChildren
179181
)
182+
183+
// Overwrite button content for the toggle when in `split` mode
184+
$buttonChildren = [h('span', { class: ['sr-only'] }, [this.toggleText])]
185+
buttonContentDomProps = {}
180186
}
181187

182188
const $toggle = h(
@@ -187,22 +193,22 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
187193
attrs: {
188194
id: this.safeId('_BV_toggle_'),
189195
'aria-haspopup': 'true',
190-
'aria-expanded': this.visible ? 'true' : 'false'
196+
'aria-expanded': toString(visible)
191197
},
192198
props: {
193199
...commonProps,
194200
tag: this.toggleTag,
195201
block: block && !split
196202
},
197-
domProps: split ? {} : buttonContentProps,
203+
domProps: buttonContentDomProps,
198204
on: {
199205
mousedown: this.onMousedown,
200-
click: this.toggle,
201-
keydown: this.toggle // Handle ENTER, SPACE and DOWN
206+
click: toggle,
207+
keydown: toggle // Handle ENTER, SPACE and DOWN
202208
},
203209
ref: 'toggle'
204210
},
205-
[split ? h('span', { class: ['sr-only'] }, [this.toggleText]) : $buttonContent]
211+
$buttonChildren
206212
)
207213

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

226232
return h(

src/components/modal/modal.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,7 @@ export const BModal = /*#__PURE__*/ Vue.extend({
900900
: htmlOrText(this.titleHtml, this.title)
901901
},
902902
// TODO: Rename slot to `title` and deprecate `modal-title`
903-
[this.normalizeSlot('modal-title', this.slotScope)]
903+
this.normalizeSlot('modal-title', this.slotScope)
904904
),
905905
$closeButton
906906
]

src/components/progress/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ Need more control over the label? Provide your own label by using the default sl
9696
<h5>Custom label via default slot</h5>
9797
<b-progress :max="max" height="2rem">
9898
<b-progress-bar :value="value">
99-
Progress: <strong>{{ value.toFixed(2) }} / {{ max }}</strong>
99+
<span>Progress: <strong>{{ value.toFixed(2) }} / {{ max }}</strong></span>
100100
</b-progress-bar>
101101
</b-progress>
102102

src/components/progress/progress-bar.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -124,16 +124,16 @@ export const BProgressBar = /*#__PURE__*/ Vue.extend({
124124
render(h) {
125125
const { label, labelHtml, computedValue, computedPrecision } = this
126126

127-
let $content = h()
127+
let $children
128128
let domProps = {}
129129
if (this.hasNormalizedSlot('default')) {
130-
$content = this.normalizeSlot('default')
130+
$children = this.normalizeSlot('default')
131131
} else if (label || labelHtml) {
132132
domProps = htmlOrText(labelHtml, label)
133133
} else if (this.computedShowProgress) {
134-
$content = this.computedProgress
134+
$children = this.computedProgress
135135
} else if (this.computedShowValue) {
136-
$content = toFixed(computedValue, computedPrecision)
136+
$children = toFixed(computedValue, computedPrecision)
137137
}
138138

139139
return h(
@@ -150,7 +150,7 @@ export const BProgressBar = /*#__PURE__*/ Vue.extend({
150150
},
151151
domProps
152152
},
153-
[$content]
153+
$children
154154
)
155155
}
156156
})

src/utils/html.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,5 @@ const RX_HTML_TAGS = /(<([^>]+)>)/gi
44
export const stripTags = (text = '') => String(text).replace(RX_HTML_TAGS, '')
55

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

0 commit comments

Comments
 (0)