Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit752661a

Browse files
committed
fix(b-tooltip): Updated tooltip to work under shadowDOM
Added to dom utility methods- isConnectedToDOM() checks if a target element is in the DOM and will check both Shadow and Regular DOM- getShadowRootOrRoot() will return the target's root either the Shadow Root or DOCUMENT.bodyUpdated isVisibile() dom util to use new isConnectedToDOM() functionUpdated the dom.spec.js unit tests for the two new dom utilitiesFixed the dom.spec.js to get the select() and selectAll() tests working
1 parentfab4161 commit752661a

File tree

3 files changed

+56
-72
lines changed

3 files changed

+56
-72
lines changed

‎src/components/tooltip/helpers/bv-tooltip.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ import {
2929
contains,
3030
getAttr,
3131
getById,
32+
getShadowRootOrRoot,
3233
hasAttr,
3334
hasClass,
35+
isConnectedToDOM,
3436
isDisabled,
3537
isElement,
3638
isVisible,
@@ -262,7 +264,7 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({
262264

263265
this.$nextTick(()=>{
264266
consttarget=this.getTarget()
265-
if(target&&contains(document.body,target)){
267+
if(target&&(target.isConnected||isConnectedToDOM(target))){
266268
// Copy the parent's scoped style attribute
267269
this.scopeId=getScopeId(this.$parent)
268270
// Set up all trigger handlers and listeners
@@ -420,7 +422,7 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({
420422
consttarget=this.getTarget()
421423
if(
422424
!target||
423-
!contains(document.body,target)||
425+
!isConnectedToDOM(target)||
424426
!isVisible(target)||
425427
this.dropdownOpen()||
426428
((isUndefinedOrNull(this.title)||this.title==='')&&
@@ -567,8 +569,9 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({
567569
getContainer(){
568570
// Handle case where container may be a component ref
569571
constcontainer=this.container ?this.container.$el||this.container :false
570-
constbody=document.body
571572
consttarget=this.getTarget()
573+
constbody=getShadowRootOrRoot(target)
574+
572575
// If we are in a modal, we append to the modal, If we
573576
// are in a sidebar, we append to the sidebar, else append
574577
// to body, unless a container is specified

‎src/utils/dom.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export const isActiveElement = el => isElement(el) && el === getActiveElement()
8383

8484
// Determine if an HTML element is visible - Faster than CSS check
8585
exportconstisVisible=el=>{
86-
if(!isElement(el)||!el.parentNode||!contains(DOCUMENT.body,el)){
86+
if(!isElement(el)||!el.parentNode||!isConnectedToDOM(el)){
8787
// Note this can fail for shadow dom elements since they
8888
// are not a direct descendant of document.body
8989
returnfalse
@@ -100,6 +100,23 @@ export const isVisible = el => {
100100
return!!(bcr&&bcr.height>0&&bcr.width>0)
101101
}
102102

103+
// used to grab either the shadow root in a web component or the main document body
104+
exportconstgetShadowRootOrRoot=el=>{
105+
if(el.getRootNode==null){
106+
returnDOCUMENT.body
107+
}
108+
constroot=el.getRootNode()
109+
if(root.nodeType===9){
110+
returnroot.body
111+
}
112+
returnroot
113+
}
114+
115+
exportconstisConnectedToDOM=el=>{
116+
// If node.isConnected undefined then fallback to IE11 compliant check
117+
returnel.isConnected==null ?contains(DOCUMENT.body,el) :el.isConnected
118+
}
119+
103120
// Determine if an element is disabled
104121
exportconstisDisabled=el=>
105122
!isElement(el)||el.disabled||hasAttr(el,'disabled')||hasClass(el,'disabled')

‎src/utils/dom.spec.js

Lines changed: 32 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import {
33
closest,
44
contains,
55
getAttr,
6+
getShadowRootOrRoot,
67
getStyle,
78
hasAttr,
89
hasClass,
10+
isConnectedToDOM,
911
isDisabled,
1012
isElement,
1113
matches,
@@ -25,28 +27,30 @@ const template = `
2527
</div>
2628
</div>
2729
`
28-
constApp={ template}
30+
letApp
31+
letwrapper
2932

3033
describe('utils/dom',()=>{
31-
it('isElement() works',async()=>{
32-
constwrapper=mount(App,{
34+
beforeEach(()=>{
35+
App={ template}
36+
wrapper=mount(App,{
3337
attachTo:document.body
3438
})
39+
})
40+
41+
afterEach(()=>{
42+
wrapper.destroy()
43+
})
3544

45+
it('isElement() works',async()=>{
3646
expect(wrapper).toBeDefined()
3747
expect(wrapper.find('div.foo').exists()).toBe(true)
3848
expect(isElement(wrapper.element)).toBe(true)
3949
expect(isElement(null)).toBe(false)
4050
expect(isElement(App)).toBe(false)
41-
42-
wrapper.destroy()
4351
})
4452

4553
it('isDisabled() works',async()=>{
46-
constwrapper=mount(App,{
47-
attachTo:document.body
48-
})
49-
5054
expect(wrapper).toBeDefined()
5155

5256
const$btns=wrapper.findAll('div.baz > button')
@@ -55,15 +59,28 @@ describe('utils/dom', () => {
5559
expect(isDisabled($btns.at(0).element)).toBe(false)
5660
expect(isDisabled($btns.at(1).element)).toBe(false)
5761
expect(isDisabled($btns.at(2).element)).toBe(true)
62+
})
5863

59-
wrapper.destroy()
64+
// NOTE: Need to figure out how to test against shadowDOM
65+
it('isConnectedToDOM() Regular DOM',async()=>{
66+
expect(wrapper).toBeDefined()
67+
68+
const$barspan=wrapper.findAll('span.barspan')
69+
expect($barspan).toBeDefined()
70+
expect($barspan.length).toBe(1)
71+
expect(isConnectedToDOM($barspan.at(0).element)).toBe(true)
6072
})
6173

62-
it('hasClass() works',async()=>{
63-
constwrapper=mount(App,{
64-
attachTo:document.body
65-
})
74+
it('getShadowRootOrRoot() Regular DOM',async()=>{
75+
expect(wrapper).toBeDefined()
76+
77+
const$baz=wrapper.find('div.baz')
78+
const$documentBody=getShadowRootOrRoot($baz.element)
79+
expect($documentBody).toBeDefined()
80+
expect($documentBody.toString()).toBe('[object HTMLBodyElement]')
81+
})
6682

83+
it('hasClass() works',async()=>{
6784
expect(wrapper).toBeDefined()
6885

6986
const$span=wrapper.find('span.barspan')
@@ -73,15 +90,9 @@ describe('utils/dom', () => {
7390
expect(hasClass($span.element,'foobar')).toBe(true)
7491
expect(hasClass($span.element,'fizzle-rocks')).toBe(false)
7592
expect(hasClass(null,'foobar')).toBe(false)
76-
77-
wrapper.destroy()
7893
})
7994

8095
it('contains() works',async()=>{
81-
constwrapper=mount(App,{
82-
attachTo:document.body
83-
})
84-
8596
expect(wrapper).toBeDefined()
8697

8798
const$span=wrapper.find('span.barspan')
@@ -95,15 +106,9 @@ describe('utils/dom', () => {
95106
expect(contains(wrapper.element,$btn1.element)).toBe(true)
96107
expect(contains($span.element,$btn1.element)).toBe(false)
97108
expect(contains(null,$btn1.element)).toBe(false)
98-
99-
wrapper.destroy()
100109
})
101110

102111
it('closest() works',async()=>{
103-
constwrapper=mount(App,{
104-
attachTo:document.body
105-
})
106-
107112
expect(wrapper).toBeDefined()
108113

109114
const$btns=wrapper.findAll('div.baz > button')
@@ -122,15 +127,9 @@ describe('utils/dom', () => {
122127
expect(closest('div.not-here',$btns.at(0).element)).toBe(null)
123128
expect(closest('div.baz',$baz.element)).toBe(null)
124129
expect(closest('div.baz',$baz.element,true)).toBe($baz.element)
125-
126-
wrapper.destroy()
127130
})
128131

129132
it('matches() works',async()=>{
130-
constwrapper=mount(App,{
131-
attachTo:document.body
132-
})
133-
134133
expect(wrapper).toBeDefined()
135134

136135
const$btns=wrapper.findAll('div.baz > button')
@@ -148,15 +147,9 @@ describe('utils/dom', () => {
148147
expect(matches($btns.at(0).element,'div.bar > button')).toBe(false)
149148
expect(matches($btns.at(0).element,'button#button1')).toBe(true)
150149
expect(matches(null,'div.foo')).toBe(false)
151-
152-
wrapper.destroy()
153150
})
154151

155152
it('hasAttr() works',async()=>{
156-
constwrapper=mount(App,{
157-
attachTo:document.body
158-
})
159-
160153
expect(wrapper).toBeDefined()
161154

162155
const$btns=wrapper.findAll('div.baz > button')
@@ -169,15 +162,9 @@ describe('utils/dom', () => {
169162
expect(hasAttr($btns.at(2).element,'disabled')).toBe(true)
170163
expect(hasAttr($btns.at(2).element,'role')).toBe(false)
171164
expect(hasAttr(null,'role')).toBe(null)
172-
173-
wrapper.destroy()
174165
})
175166

176167
it('getAttr() works',async()=>{
177-
constwrapper=mount(App,{
178-
attachTo:document.body
179-
})
180-
181168
expect(wrapper).toBeDefined()
182169

183170
const$btns=wrapper.findAll('div.baz > button')
@@ -193,15 +180,9 @@ describe('utils/dom', () => {
193180
expect(getAttr(null,'role')).toBe(null)
194181
expect(getAttr($btns.at(0).element,'')).toBe(null)
195182
expect(getAttr($btns.at(0).element,undefined)).toBe(null)
196-
197-
wrapper.destroy()
198183
})
199184

200185
it('getStyle() works',async()=>{
201-
constwrapper=mount(App,{
202-
attachTo:document.body
203-
})
204-
205186
expect(wrapper).toBeDefined()
206187

207188
const$span=wrapper.find('span.barspan')
@@ -210,15 +191,9 @@ describe('utils/dom', () => {
210191
expect(getStyle($span.element,'color')).toBe('red')
211192
expect(getStyle($span.element,'width')).toBe(null)
212193
expect(getStyle(null,'color')).toBe(null)
213-
214-
wrapper.destroy()
215194
})
216195

217196
it('select() works',async()=>{
218-
constwrapper=mount(App,{
219-
attachTo:document.body
220-
})
221-
222197
expect(wrapper).toBeDefined()
223198

224199
const$btns=wrapper.findAll('div.baz > button')
@@ -230,22 +205,15 @@ describe('utils/dom', () => {
230205
expect(select('button#button3',wrapper.element)).toBe($btns.at(2).element)
231206
expect(select('span.not-here',wrapper.element)).toBe(null)
232207

233-
// Note: It appears that `vue-test-utils` is not detaching previous
234-
// app instances and elements once the test is complete!
208+
// Without root element specified
235209
expect(select('button')).not.toBe(null)
236210
expect(select('button')).toBe($btns.at(0).element)
237211
expect(select('button#button3')).not.toBe(null)
238212
expect(select('button#button3')).toBe($btns.at(2).element)
239213
expect(select('span.not-here')).toBe(null)
240-
241-
wrapper.destroy()
242214
})
243215

244216
it('selectAll() works',async()=>{
245-
constwrapper=mount(App,{
246-
attachTo:document.body
247-
})
248-
249217
expect(wrapper).toBeDefined()
250218

251219
const$btns=wrapper.findAll('div.baz > button')
@@ -268,8 +236,6 @@ describe('utils/dom', () => {
268236
expect(selectAll('div.baz button',wrapper.element)[2]).toBe($btns.at(2).element)
269237

270238
// Without root element specified (assumes document as root)
271-
// Note: It appears that `vue-test-utils` is not detaching previous
272-
// app instances and elements once the test is complete!
273239
expect(Array.isArray(selectAll('button'))).toBe(true)
274240
expect(selectAll('button')).not.toEqual([])
275241
expect(selectAll('button').length).toBe(3)
@@ -285,7 +251,5 @@ describe('utils/dom', () => {
285251
expect(selectAll('div.baz button')[0]).toBe($btns.at(0).element)
286252
expect(selectAll('div.baz button')[1]).toBe($btns.at(1).element)
287253
expect(selectAll('div.baz button')[2]).toBe($btns.at(2).element)
288-
289-
wrapper.destroy()
290254
})
291255
})

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp