-
Notifications
You must be signed in to change notification settings - Fork 377
Dropdown interaction issues #1014
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
Dropdown interaction issues #1014
Conversation
|
PatternFly-React preview: https://1014-pr-patternfly-react-patternfly.surge.sh |
mcarrano
left a comment
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.
Looks great. Thanks for making these changes @kmcfaul !
|
Thanks for making these updates! I tested across different devices.
|
| <Component | ||
| {...additionalProps} | ||
| className={css(isDisabled && styles.modifiers.disabled, isHovered && styles.modifiers.hover, className)} | ||
| onClick={onSelect} |
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.
There is a bit of an issue with always setting onClick to onSelect here. If the dropdown has multiple button items, this will set all of their onClick handlers to onSelect (so all buttons will fire the same action).
One possible solution would be to add a handleOnClick handler to DropdownItem and do something like the following
// ...
return (
<li>
<Component
{...additionalProps}
className={css(isDisabled && styles.modifiers.disabled, isHovered && styles.modifiers.hover, className)}
onClick={event => handleOnClick && handleOnClick(event, onSelect)}
>
{children}
</Component>
</li>
);Then the consuming app could do something like
<DropdownItem
// ...
component="button"
handleOnClick={(event, onSelect) => {
// fire off the particular action for this button
onSelect();
}}
>
// ...
</DropdownItem>But this would mean that all DropdownItems (even the "link" ones) will require an explicit handleOnClick handler to ensure that onSelect is fired.
| }; | ||
|
|
||
| const DropdownItem = ({ className, children, isHovered, component: Component, isDisabled, ...props }) => { | ||
| const DropdownItem = ({ className, children, isHovered, onSelect, component: Component, isDisabled, ...props }) => { |
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! onSelect needs to be added to the propTypes
Pull Request Test Coverage Report for Build 3880
💛 - Coveralls |
15046f2 to
6644e80
Compare
|
Added onSelect to prop types, updated the onClick to also call the button's onClick method in addition to the onSelect, updated snapshots |
| {...additionalProps} | ||
| className={css(isDisabled && styles.modifiers.disabled, isHovered && styles.modifiers.hover, className)} | ||
| onClick={() => { | ||
| Component === 'button' ? (props.onClick && props.onClick(), onSelect()) : onSelect(); |
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.
Nice, def cleaner than my suggestion! Just a couple more things :)
-
Now that we're explicitly setting
onClickhandler using a prop,onClickshould be added to the propTypes, and can be destructured along with the other props that we exclude from...props -
We should probably pass the onClick
eventobject to the callbacks, just in case the consumers need it -
Seeing an eslint error here
[eslint] Expected an assignment or function call and instead saw an expression. [no-unused-expressions]
We can do something like the following to get rid of it
onClick={event => { if (Component === 'button') { onClick && onClick(event); onSelect(event); } else { onSelect(event); } }}
That being said, I'm not sure why Travis isn't running
prettier, or maybe this particular rule has been removed. Are we not standardizing aroundprettierandeslint? @jschuler
| position={position} | ||
| aria-labelledby={id} | ||
| onClick={event => onSelect && onSelect(event)} | ||
| onSelect={event => onSelect && onSelect(event)} |
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.
One last thing, I'd like to discuss the semantics of using onSelect as a handler for click events.
The native select event is fired when some text is being selected, whereas, the native change event is fired for select elements when a change to the element's value is committed by the user.
Since this is a custom implementation, and we're actually hooking into the click event, technically, it does not matter what we call our event handler. In fact, just to make sure, I went through and changed all the onSelects to onSomeEvent, and things seem to work just fine.
However, since this is going to become part of <Dropdown />'s API, I figured it was worth bringing up
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.
Actually, now that I think about it again, I realize that <Dropdown /> isn't actually a select... it's a menu. So onChange doesn't necessarily make sense either.
This is sort of a weird use case here... What do you think we should do?
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 may be more of a case of bad nomenclature, as this onSelect isn't a property of the DropdownMenu but a prop that gets passed down to the DropdownItem itself where it is set to the onClick function.
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.
| onSelect={event => onSelect && onSelect(event)} | |
| onSelect={onSelect} |
| className={css(isDisabled && styles.modifiers.disabled, isHovered && styles.modifiers.hover, className)} | ||
| onClick={event => { | ||
| if (Component === 'button') { | ||
| onClick && onClick(event); |
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.
Why both onClick and onSelect?
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 see, onSelect is added in the Menu. Maybe remove from propTypes so it is only documented that user can add onClick to the item?
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.
Actually, nevermind, better would be to use context i think
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 think it would be best to create a context in Dropdown, and then pass the onSelect through that instead of props. Additionally, when you return the onSelect to the user, apart from the event maybe also return the index
cd03a63 to
fd5f478
Compare
| } else if (Component === 'button') { | ||
| additionalProps.disabled = isDisabled; | ||
| } | ||
| class DropdownItem extends React.Component { |
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.
Out of curiosity, what is the reasoning behind switching to a class based component? In any case, if we decide to keep this as a class, we can switch the context stuff over to the contextType syntax
EDIT: hmm, maybe not on the contextType. Trying to get it working locally and it's being weird.
| if (Component === 'button') onClick && onClick(event); | ||
| onSelect && onSelect(event); | ||
| } else { | ||
| Function.prototype; |
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.
Getting an eslint error here
Expected an assignment or function call and instead saw an expression.eslint(no-unused-expressions)
I think, if we want the handler to do nothing in case isDisabled is true, we can just omit this else block
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.
Yeah I think you are right, I will remove the block
| isDisabled: false, | ||
| href: '#' | ||
| href: '#', | ||
| onSelect: Function.prototype, |
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.
Missing from propTypes :)
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.
Oops, I'm wrong, we need to do the opposite. I think we need to remove onSelect from defaultProps
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.
Must've missed removing it when I took out the proptypes, will remove
fd5f478 to
1857284
Compare
Select event should not fire when disabled actions in a menu are selected.
disabled items may be enabled by application code, so should still have the onSelect attached.
Ensures that if the component is a button, the unique onClick is still fired in addition to the onSelect
1857284 to
8640789
Compare
tlabaj
left a comment
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.
LGTM
jgiardino
left a comment
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.
Same here, since the issue I noted about touch is captured in a separate issue.
| '': PropTypes.any | ||
| '': PropTypes.any, | ||
| /** Callback for click event */ | ||
| onClick: PropTypes.func |
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.
Also need to update the props in the DropdownItem.d.ts file?
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 will add the onClick to the ts file
Issue reference: #984
What: Fix focus trap disabling the dropdown toggle function. Pipe onSelect event to DropdownItem, allowing its disabled property to control if the event is fired. In our example docs, this means that clicking a disabled link will not close the dropdown.