diff --git a/src/utils/isElementVisible.ts b/src/utils/isElementVisible.ts index 27aa0cfd4..e9b38fdc2 100644 --- a/src/utils/isElementVisible.ts +++ b/src/utils/isElementVisible.ts @@ -19,18 +19,48 @@ function isStyleVisible(element: T) { ) } -function isAttributeVisible(element: T) { - return ( - !element.hasAttribute('hidden') && - (element.nodeName === 'DETAILS' ? element.hasAttribute('open') : true) - ) +/** + * checks if an element is visible by checking its hidden attribute and parent visibility. + * @param element + * @returns boolean + */ +function isElementVisibleByAttribute(element: T) { + if (element instanceof HTMLElement && element.hidden) { + return false + } + + return true +} + +/** + * checks if an element is hidden by a closed details element. + * @param element + * @returns boolean + */ +function isHiddenByClosedDetails(element: T) { + const summary = element.closest('summary') + let parent = element.parentElement + + while (parent) { + if (parent.nodeName === 'DETAILS' && !(parent as HTMLDetailsElement).open) { + // If no ancestor exists, or the is not a direct child of this
, + // then the content is hidden + if (!summary || summary.parentElement !== parent) { + return true + } + } + parent = parent.parentElement + } + + return false } export function isElementVisible(element: T): boolean { return ( element.nodeName !== '#comment' && isStyleVisible(element) && - isAttributeVisible(element) && + isElementVisibleByAttribute(element) && + !isHiddenByClosedDetails(element) && (!element.parentElement || isElementVisible(element.parentElement)) ) } diff --git a/tests/isVisible.spec.ts b/tests/isVisible.spec.ts index 2de407a61..dbf0712b0 100644 --- a/tests/isVisible.spec.ts +++ b/tests/isVisible.spec.ts @@ -229,5 +229,91 @@ describe('isVisible', () => { expect(wrapper.isVisible()).toBe(false) }) }) + + describe('details and summary elements', () => { + it('DetailContent should be invisible when display:none is applied by style attribute', () => { + const wrapper = defineComponent({ + template: '' + }) + + expect(wrapper.get('#my-div').isVisible()).toBe(false) + expect(wrapper.find('summary').isVisible()).toBe(false) + expect(wrapper.find('details').isVisible()).toBe(false) + }) + it('DetailContent should be visible when summary is visible', () => { + const DetailContent = defineComponent({ + template: `
Summary
Content
` + }) + + const wrapper = mount(DetailContent) + expect(wrapper.find('details').isVisible()).toBe(true) + expect(wrapper.find('summary').isVisible()).toBe(true) + expect(wrapper.find('div').isVisible()).toBe(false) + }) + it ('DetailContent shouild be visible when summarys child is visible', () => { + const childContent = defineComponent({ + template: `
Summary
` + }) + const wrapper = mount(childContent) + expect(wrapper.find('summary span').isVisible()).toBe(true); + }) + it('should consider a summary as hidden when nested inside closed details content', () => { + const NestedSummaryInClosedDetails = defineComponent({ + template: ` +
+ Main summary +
+
+ Nested summary +
+
+
+ ` + }) + const wrapper = mount(NestedSummaryInClosedDetails) + const summaries = wrapper.findAll('summary') + + expect(summaries[0].isVisible()).toBe(true) + expect(summaries[1].isVisible()).toBe(false) + }) + it('should consider a summary as visible when nested inside open details content', () => { + const NestedSummaryInOpenDetails = defineComponent({ + template: ` +
+ Main summary +
+
+ Nested summary +
+
+
+ ` + }) + const wrapper = mount(NestedSummaryInOpenDetails) + const summaries = wrapper.findAll('summary') + + expect(summaries[0].isVisible()).toBe(true) + expect(summaries[1].isVisible()).toBe(true) + }) + it('should consider a 1st summary as visible and 2nd as hidden when nested inside closed details content which is applied display: none', () => { + const NestedSummaryInOpenDetails = defineComponent({ + template: ` +
+ Main summary +
+
+ Nested summary +
+
+
+ ` + }) + const wrapper = mount(NestedSummaryInOpenDetails) + const summaries = wrapper.findAll('summary') + + expect(summaries[0].isVisible()).toBe(true) + expect(summaries[1].isVisible()).toBe(false) + }) + }) }) })