]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Refactor `isVisible` helper, fixing false positives from deep nesting or alternate...
authorRyan Berliner <22206986+RyanBerliner@users.noreply.github.com>
Thu, 20 May 2021 13:50:53 +0000 (09:50 -0400)
committerGitHub <noreply@github.com>
Thu, 20 May 2021 13:50:53 +0000 (16:50 +0300)
js/src/util/index.js
js/tests/unit/util/index.spec.js

index 6b38a05e94a53d17db11ceb7f39f6720df2f1218..77bdc072fc6e1487db7c14827e5be77f7b620cee 100644 (file)
@@ -159,20 +159,11 @@ const typeCheckConfig = (componentName, config, configTypes) => {
 }
 
 const isVisible = element => {
-  if (!element) {
+  if (!isElement(element) || element.getClientRects().length === 0) {
     return false
   }
 
-  if (element.style && element.parentNode && element.parentNode.style) {
-    const elementStyle = getComputedStyle(element)
-    const parentNodeStyle = getComputedStyle(element.parentNode)
-
-    return elementStyle.display !== 'none' &&
-      parentNodeStyle.display !== 'none' &&
-      elementStyle.visibility !== 'hidden'
-  }
-
-  return false
+  return getComputedStyle(element).getPropertyValue('visibility') === 'visible'
 }
 
 const isDisabled = element => {
index ca6430bee572eff993df07df98c662808670900e..774945d1f92a46453e978a5e63371a209a7adcaa 100644 (file)
@@ -326,10 +326,14 @@ describe('Util', () => {
       expect(Util.isVisible(div)).toEqual(false)
     })
 
-    it('should return false if the parent element is not visible', () => {
+    it('should return false if an ancestor element is display none', () => {
       fixtureEl.innerHTML = [
         '<div style="display: none;">',
-        '  <div class="content"></div>',
+        '  <div>',
+        '    <div>',
+        '      <div class="content"></div>',
+        '    </div>',
+        '  </div>',
         '</div>'
       ].join('')
 
@@ -338,6 +342,38 @@ describe('Util', () => {
       expect(Util.isVisible(div)).toEqual(false)
     })
 
+    it('should return false if an ancestor element is visibility hidden', () => {
+      fixtureEl.innerHTML = [
+        '<div style="visibility: hidden;">',
+        '  <div>',
+        '    <div>',
+        '      <div class="content"></div>',
+        '    </div>',
+        '  </div>',
+        '</div>'
+      ].join('')
+
+      const div = fixtureEl.querySelector('.content')
+
+      expect(Util.isVisible(div)).toEqual(false)
+    })
+
+    it('should return true if an ancestor element is visibility hidden, but reverted', () => {
+      fixtureEl.innerHTML = [
+        '<div style="visibility: hidden;">',
+        '  <div style="visibility: visible;">',
+        '    <div>',
+        '      <div class="content"></div>',
+        '    </div>',
+        '  </div>',
+        '</div>'
+      ].join('')
+
+      const div = fixtureEl.querySelector('.content')
+
+      expect(Util.isVisible(div)).toEqual(true)
+    })
+
     it('should return true if the element is visible', () => {
       fixtureEl.innerHTML = [
         '<div>',
@@ -349,6 +385,18 @@ describe('Util', () => {
 
       expect(Util.isVisible(div)).toEqual(true)
     })
+
+    it('should return false if the element is hidden, but not via display or visibility', () => {
+      fixtureEl.innerHTML = [
+        '<details>',
+        '  <div id="element"></div>',
+        '</details>'
+      ].join('')
+
+      const div = fixtureEl.querySelector('#element')
+
+      expect(Util.isVisible(div)).toEqual(false)
+    })
   })
 
   describe('isDisabled', () => {