-
Notifications
You must be signed in to change notification settings - Fork 399
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix toBeVisible visibility #428
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,33 +1,63 @@ | ||||||
import {checkHtmlElement} from './utils' | ||||||
|
||||||
function isStyleVisible(element) { | ||||||
function getElementVisibilityStyle(element) { | ||||||
if (!element) return 'visible' | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bit odd. If the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm, good point. This clarifies your point about bailing out quickly. I'll make the change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, for This was relevant in earlier versions of JSOM where |
||||||
const {getComputedStyle} = element.ownerDocument.defaultView | ||||||
const {visibility} = getComputedStyle(element) | ||||||
return visibility || getElementVisibilityStyle(element.parentElement) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you walk up the tree here? |
||||||
} | ||||||
|
||||||
const {display, visibility, opacity} = getComputedStyle(element) | ||||||
return ( | ||||||
display !== 'none' && | ||||||
visibility !== 'hidden' && | ||||||
visibility !== 'collapse' && | ||||||
opacity !== '0' && | ||||||
opacity !== 0 | ||||||
) | ||||||
function isVisibleSummaryDetails(element, previousElement) { | ||||||
return element.nodeName === 'DETAILS' && | ||||||
previousElement.nodeName !== 'SUMMARY' | ||||||
? element.hasAttribute('open') | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question not directly related to this PR: Is the attribute or property relevant i.e. when I set There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm honestly not sure. I did not even implement the support for summary/detail. It may be worth checking. |
||||||
: true | ||||||
} | ||||||
|
||||||
function isAttributeVisible(element, previousElement) { | ||||||
function isElementTreeVisible(element, previousElement = undefined) { | ||||||
const {getComputedStyle} = element.ownerDocument.defaultView | ||||||
const {display, opacity} = getComputedStyle(element) | ||||||
return ( | ||||||
display !== 'none' && | ||||||
opacity !== '0' && | ||||||
!element.hasAttribute('hidden') && | ||||||
(element.nodeName === 'DETAILS' && previousElement.nodeName !== 'SUMMARY' | ||||||
? element.hasAttribute('open') | ||||||
isVisibleSummaryDetails(element, previousElement) && | ||||||
(element.parentElement | ||||||
? isElementTreeVisible(element.parentElement, element) | ||||||
: true) | ||||||
) | ||||||
} | ||||||
|
||||||
function isElementVisible(element, previousElement) { | ||||||
return ( | ||||||
isStyleVisible(element) && | ||||||
isAttributeVisible(element, previousElement) && | ||||||
(!element.parentElement || isElementVisible(element.parentElement, element)) | ||||||
) | ||||||
function isElementVisibilityVisible(element) { | ||||||
const visibility = getElementVisibilityStyle(element) | ||||||
return visibility !== 'hidden' && visibility !== 'collapse' | ||||||
} | ||||||
|
||||||
/** | ||||||
* Computes the boolean value that determines if an element is considered visible from the | ||||||
* `toBeVisible` custom matcher point of view. | ||||||
* | ||||||
* Visibility is controlled via two different sets of properties and styles. | ||||||
* | ||||||
* 1. One set of properties allow parent elements to fully controls its sub-tree visibility. This | ||||||
* means that if higher up in the tree some element is not visible by this criteria, it makes the | ||||||
* entire sub-tree not visible too, and there's nothing that child elements can do to revert it. | ||||||
* This includes `display: none`, `opacity: 0`, the presence of the `hidden` attribute`, and the | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that you can have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't include There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, but that's a separate discussion. Right now this is not new functionality, and I'm merely maintaining backward compatibility. I agree that I'll open an issue to discuss this. But I will not remove in this PR, as it will be a breaking change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. I missed, that you already included opacity. |
||||||
* open state of a details/summary elements pair. | ||||||
* | ||||||
* 2. The other aspect influencing if an element is visible is the CSS `visibility` style. This one | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit:
Suggested change
That's how these are named officially i.e. in the spec. |
||||||
* is also inherited. But unlike the previous case, this one can be reverted by child elements. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is "also inherited" in this context? Neither There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe "inherited" is not the best word. What I mean is that setting I'll reword to not use the word inherit to avoid confusion. |
||||||
* A parent element can set its visibiilty to `hidden` or `collapse`, but a child element setting | ||||||
* its own styles to `visibility: visible` can rever that, and it makes itself visible. Hence, | ||||||
* this criteria needs to be checked independently of the other one. | ||||||
* | ||||||
* Hence, the apprach taken by this function is two-fold: it first gets the first set of criteria | ||||||
* out of the way, analyzing the target element and up its tree. If this branch yields that the | ||||||
* element is not visible, there's nothing the element could be doing to revert that, so it returns | ||||||
* false. Only if the first check is true, if proceeds to analyze the `visibility` CSS. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's an interesting trade-off. You're basically assuming that checking How was this trade-off motivated? In the other Testing Library implementations we're checking function isElementVisible(element) {
- return isElementTreeVisible(element) && isElementVisibilityVisible(element)
+ return isElementVisibilityVisible(element) && isElementTreeVisible(element)
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Clarified below: #428 (comment) |
||||||
*/ | ||||||
function isElementVisible(element) { | ||||||
return isElementTreeVisible(element) && isElementVisibilityVisible(element) | ||||||
} | ||||||
|
||||||
export function toBeVisible(element) { | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.