Skip to content

Commit 4ac711b

Browse files
authored
Refactor isVisible helper, fixing false positives from deep nesting or alternate means (#33960)
1 parent 79c3bf4 commit 4ac711b

File tree

2 files changed

+52
-13
lines changed

2 files changed

+52
-13
lines changed

js/src/util/index.js

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -159,20 +159,11 @@ const typeCheckConfig = (componentName, config, configTypes) => {
159159
}
160160

161161
const isVisible = element => {
162-
if (!element) {
162+
if (!isElement(element) || element.getClientRects().length === 0) {
163163
return false
164164
}
165165

166-
if (element.style && element.parentNode && element.parentNode.style) {
167-
const elementStyle = getComputedStyle(element)
168-
const parentNodeStyle = getComputedStyle(element.parentNode)
169-
170-
return elementStyle.display !== 'none' &&
171-
parentNodeStyle.display !== 'none' &&
172-
elementStyle.visibility !== 'hidden'
173-
}
174-
175-
return false
166+
return getComputedStyle(element).getPropertyValue('visibility') === 'visible'
176167
}
177168

178169
const isDisabled = element => {

js/tests/unit/util/index.spec.js

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,14 @@ describe('Util', () => {
326326
expect(Util.isVisible(div)).toEqual(false)
327327
})
328328

329-
it('should return false if the parent element is not visible', () => {
329+
it('should return false if an ancestor element is display none', () => {
330330
fixtureEl.innerHTML = [
331331
'<div style="display: none;">',
332-
' <div class="content"></div>',
332+
' <div>',
333+
' <div>',
334+
' <div class="content"></div>',
335+
' </div>',
336+
' </div>',
333337
'</div>'
334338
].join('')
335339

@@ -338,6 +342,38 @@ describe('Util', () => {
338342
expect(Util.isVisible(div)).toEqual(false)
339343
})
340344

345+
it('should return false if an ancestor element is visibility hidden', () => {
346+
fixtureEl.innerHTML = [
347+
'<div style="visibility: hidden;">',
348+
' <div>',
349+
' <div>',
350+
' <div class="content"></div>',
351+
' </div>',
352+
' </div>',
353+
'</div>'
354+
].join('')
355+
356+
const div = fixtureEl.querySelector('.content')
357+
358+
expect(Util.isVisible(div)).toEqual(false)
359+
})
360+
361+
it('should return true if an ancestor element is visibility hidden, but reverted', () => {
362+
fixtureEl.innerHTML = [
363+
'<div style="visibility: hidden;">',
364+
' <div style="visibility: visible;">',
365+
' <div>',
366+
' <div class="content"></div>',
367+
' </div>',
368+
' </div>',
369+
'</div>'
370+
].join('')
371+
372+
const div = fixtureEl.querySelector('.content')
373+
374+
expect(Util.isVisible(div)).toEqual(true)
375+
})
376+
341377
it('should return true if the element is visible', () => {
342378
fixtureEl.innerHTML = [
343379
'<div>',
@@ -349,6 +385,18 @@ describe('Util', () => {
349385

350386
expect(Util.isVisible(div)).toEqual(true)
351387
})
388+
389+
it('should return false if the element is hidden, but not via display or visibility', () => {
390+
fixtureEl.innerHTML = [
391+
'<details>',
392+
' <div id="element"></div>',
393+
'</details>'
394+
].join('')
395+
396+
const div = fixtureEl.querySelector('#element')
397+
398+
expect(Util.isVisible(div)).toEqual(false)
399+
})
352400
})
353401

354402
describe('isDisabled', () => {

0 commit comments

Comments
 (0)