Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 36 additions & 6 deletions src/utils/isElementVisible.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,48 @@ function isStyleVisible<T extends Element>(element: T) {
)
}

function isAttributeVisible<T extends Element>(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<T extends Element>(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<T extends Element>(element: T) {
Comment thread
cexbrayat marked this conversation as resolved.
const summary = element.closest('summary')
let parent = element.parentElement

while (parent) {
if (parent.nodeName === 'DETAILS' && !(parent as HTMLDetailsElement).open) {
// If no <summary> ancestor exists, or the <summary> is not a direct child of this <details>,
// then the content is hidden
if (!summary || summary.parentElement !== parent) {
return true
}
}
parent = parent.parentElement
}

return false
}

export function isElementVisible<T extends Element>(element: T): boolean {
return (
element.nodeName !== '#comment' &&
isStyleVisible(element) &&
isAttributeVisible(element) &&
isElementVisibleByAttribute(element) &&
!isHiddenByClosedDetails(element) &&
(!element.parentElement || isElementVisible(element.parentElement))
)
}
86 changes: 86 additions & 0 deletions tests/isVisible.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '<div id="my-div" style="display: none;"><details><summary>Summary</summary></details></div>'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use defineComponent to declare the component.

})

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', () => {
Comment thread
cexbrayat marked this conversation as resolved.
const DetailContent = defineComponent({
template: `<details><summary>Summary</summary><div>Content</div></details>`
})

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)
})
Comment thread
cexbrayat marked this conversation as resolved.
it ('DetailContent shouild be visible when summarys child is visible', () => {
const childContent = defineComponent({
template: `<details><summary><span>Summary</span></summary></details>`
})
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: `
<details>
<summary>Main summary</summary>
<div>
<details>
<summary>Nested summary</summary>
</details>
</div>
</details>
`
})
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: `
<details open>
<summary>Main summary</summary>
<div>
<details open>
<summary>Nested summary</summary>
</details>
</div>
</details>
`
})
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: `
<details>
<summary>Main summary</summary>
<div style="display: none;">
<details>
<summary>Nested summary</summary>
</details>
</div>
</details>
`
})
const wrapper = mount(NestedSummaryInOpenDetails)
const summaries = wrapper.findAll('summary')

expect(summaries[0].isVisible()).toBe(true)
expect(summaries[1].isVisible()).toBe(false)
})
})
})
})