-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Support Property Selector #70
Conversation
@@ -128,3 +136,46 @@ export function AND(fns) { | |||
return true; | |||
}; | |||
} | |||
|
|||
export const attributePropMap = { |
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.
curious is there an npm module we can extract these from?
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.
I did find one. The only potential problem is that react supports some non-standard attributes, which aren't covered in the package I found, and likely wouldn't be covered by other packages.
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.
how about take it from react itself? You can import HTMLDOMPropertyConfig and SVGDOMPropertyConfig from renderers/dom/shared. I think it's better, because when react team add new property it starts working here without any changes.
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.
This will be going away entirely actually.
- attr syntax(`[foo="bar"]`, `[foo]`, etc.); | ||
|
||
A note on attribute selectors: For compatibilty, you can use either the | ||
react prop name, or the native html selector. For example, the following |
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.
I still see this as a horribly bad API decision. []
means "HTML attributes", not "React component props" - the two aren't the same thing. Wouldn't the code be a lot simpler to use a different syntax instead of smashing two things into one existing syntax?
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.
In addition, this is a decision that will be almost impossible to unmake - whereas introducing a different syntax now won't in any way prevent bloating the attribute selector syntax later (and just using the same code) if that's what we decide we want.
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.
I'm open for changing either way.
@ljharb what about the context of different rendering layers. Think ReactNative, ReactHardware. In those contexts, do we just say the []
isn't for them? I think leland had a good point that the []
selector was built for XML, not explicitly the DOM. so in a ReactNative/Hardware standpoint, the Props are the attributes of the "rendered" xml
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.
as I understand it (@lelandrichardson can confirm) enzyme was originally built to replicate much of jQuery's API, which is why the querySelectorAll syntax is used for the selector string. If we're going to be deviating from it, for React Native or otherwise, that's a bigger discussion - does React Native itself use selector syntax?
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.
@ljharb I can understand your hesitation with the API decision. We solved something similar in the testing library skin-deep with tree.subTreeLike('*', {foo: 'bar'})
.
We wanted to query for specific components using props, so we could use specific testing properties like data-testref='foo'
that didn't interfere with our ui teams use of classnames.
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.
I started on an implementation to use seperate syntax ({}
for props, []
for html attr). It seems feasible. In shallow renders, I'm basically faking it by mapping the html attr selector to the matching react prop since I don't get an actual render. I'm just waiting on word from @lelandrichardson if that is the way we want this togo.
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.
I just pushed up the changes that @ljharb requested. Hopefully this can help the conversation move along.
looking for feedback from @lelandrichardson and @ljharb
return inst => instHasId(inst, selector.substr(1)); | ||
case SELECTOR.ATTR_TYPE: | ||
const attrKey = selector.split(/\[([a-z\-\:]*?)(=|\])/)[1]; |
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.
are we sure attributes can't have capitals?
if not I would expect some useful error message
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.
Good catch. That is actually a bug now that I think about it. svg introduced a lot of camelCase attributes, like preserveAspectRatio
wow, thanks for all this work! I'm going to try and take a closer look tonight after we finish up this hackathon. Looks great! |
Hey @blainekasten , I hate to give you more work to do... but i was hoping you could change this back to the original To recap, the syntax we would like is:
|
No worries. I want to see this land. I can do that. I agree with that API also honestly. Glad to have it sorted out. I'll finish it tomorrow 👍 |
wow, good work on the tests! Istanbul is becoming a bit of a pain since we are using babel... I need to figure out how to get istanbul working with the original source lines so that it makes more sense. just ignore for now if the percentage goes down. I'm curious of a couple of things:
const wrapper = shallow(<div><Foo bar={2} /></div>);
expect(wrapper.find('[bar="2"]')).to.have.length(1); Does the above pass? (I'm not necessarily saying it should) |
@@ -116,7 +116,26 @@ export function selectorError(selector) { | |||
); | |||
} | |||
|
|||
export const isCompoundSelector = /[a-z]\.[a-z]/i; | |||
export const isCompoundSelector = /([a-z]\.[a-z]|[a-z]\[.*\])/i; |
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.
will this always work? what about [foo][bar]
?
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.
It does, I tested just tested that exact scenario, but also this test effectively proves it also, https://github.com/airbnb/enzyme/pull/70/files#diff-e08e795cdfa46e03ea97df132b57d75aR167
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.
My testing shows that it doesnt? div[foo][bar]
matches but [foo][bar]
does not
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.
Maybe test again? I literally just tested both those ideas and they both matched.
Also, what is the expected behavior for falsy but present props?
|
const nodeProps = propsOfNode(node); | ||
|
||
if (propValue) { | ||
return nodeProps[propKey] === propValue; |
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.
i guess my questions boil down to: should this be ==
or ===
?
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.
That doesn't work for situations like 'false'
or 'true'
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.
yah... so we may need some special case logic put in here. let's try and figure out what the corner cases are and what the desired behavior is...
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.
===
. A prop of 3
and a prop of '3'
are different and shouldn't be selected in the same group.
Ahh good catch. I didn't think about non-string values.. I'll have to work on this a bit more. Empty string currently works fine:
I guess in the situation of non string prop values, I would think dropping the quotes would be the way to do it. So |
@ljharb what do you think? How should empty values be handled? should I'm trying to think pragmatically, and it seems to me that if i look for |
If we want to stick to Some new code that I have you could do this.
|
That's a very good question - since this is a string-based API, it will already only work for a subset of prop values (and won't work for |
Alternatively, I'd be totally fine with saying that |
I think I like That just leaves the non-string-based values... in this case we could say that things have to be |
agreed. I think non-string values is for |
Let's also add two tests describing its behavior for |
Feeling a little lost by the string of comments. Can you drop me a few examples of what tests you'd want to pass/fail? |
I don't think adding support for |
Agreed. I don't want to support it... I just want to add a test that asserts that it's not supported so we know that that doesn't change at some point |
@blainekasten thanks for bearing with us for this whole conversation! |
No prob! It's a pretty big api addition. Always gotta make sure it serves the right direction. |
@lelandrichardson This is ready to go now! Thanks |
@blainekasten one last request: can you squash your commits before I merge? |
@blainekasten a rebase would be preferred over a merge |
Yeah. I'm trying to rebase. I always struggle through the squashing process... |
sadly github doesn't make it easy with a rebase button. on your branch, |
Yeah.. it is too hard. I am not following exactly for that suggestion. I tried |
Alternatively, you could make a brand new branch locally, recreate it manually by checking things out or cherry picking from the original branch, and then, force push the new local branch to the original remote branch, which will update the PR. |
on your branch, figure out the total # of commits After that, run |
Are you guys happy enough with 2 squashedish commits? haha. I can fix it if you want. I just get so worried when I do this that I'll break something. |
Oh boom. I got this down now. Alright, I think we're good to :) |
|
||
it('returns a boolean if passed a stringified bool', () => { | ||
expect(coercePropValue('true')).to.be.true; | ||
expect(coercePropValue('false')).to.be.false; |
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.
whoops. something got merged into master that prevents these types of tests. You need to change these to .to.equal(true)
instead. Our linter prevents it the use of these now. You can run npm run lint
to ensure everything is fixed.
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.
when I run the linter, those aren't reported. But I changed them, we'll see if this gets it passing
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.
@blainekasten you may need to fetch and rebase again - there's been some merges today.
This should be ready to go now. Thanks |
hey @blainekasten it looks like it has some merge conflicts. Some stuff got merged in that must be conflicting. Can you rebase once more and resolve whatever conflicts show up? Thanks! |
cleanup mounted attr api Add documentation for attr api the rest of the attribute mapping and test for colons react 13/14 compatibility clean up code by utilizing utils method fix lint error Change syntax for react property lookups fix lint errors Fix key lookup bug, and simplify regex Change back to [] syntax for props only fix documentation split out test coverage more test coverage fix lint issue support non-string values for prop querying Add tests for not finding ref or key string based selector only deny undefined values fix lint issues Revert to 80b14d3 support multiple literal types for property lookups WIP: Early push for discussion cleanup Add support for prop selector clean up code by utilizing utils method fix lint error Change syntax for react property lookups Change back to [] syntax for props only split out test coverage fix lint issue support non-string values for prop querying string based selector only fix lint issues Revert to 80b14d3 support multiple literal types for property lookups Revert "support multiple literal types for property lookups" This reverts commit 72872fa. undo revert Trigger new build add support for prop selector fix lint errors
Alright. Should be good again. Hopefully it can get merged before something else conflicts again :) |
Support Property Selector
Awesome work. Thank you! |
Thanks for merging! No problem. I'm excited about this project. Thanks for starting it |
Per discussion in #4 and continued discussion here. This PR adds support for querying react properties, and not html attributes. The following syntax works:
component.find('input[type="text"][value="1"])
component.find('label[htmlFor="button"]')
component.find('div[data-wrapper]')