-
Notifications
You must be signed in to change notification settings - Fork 843
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 EuiComboBox focus trap #866
Conversation
icon: PropTypes.string, | ||
icon: PropTypes.oneOfType([ | ||
PropTypes.string, | ||
PropTypes.shape({ |
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.
Thoughts on this pattern? I was inspired a bit by https://www.youtube.com/watch?v=Yy7gFgETp0o&t=1079s.
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 like it
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 this is a smarter pattern. One thing I think we probably should do is improve how we'll handle this kind of layout in our autodocs though. While importing the array values was never great and had similar problems, at least you could see the prop names themselves (which is probably the most important part). Right now it will be super vague and blackboxy. Most of the time when we do this kind of abstraction we actually only expose subsets of the original proplist (for example, color is not coming over from icon) so it will be harder to say "consult EuiIcon for props...etc).
Ultimately up to y'all, so I'll stay out of the decision here, but it'd be nice to have some sort of plan should we go down this route (if we are planning to adopt / migrate this pattern in more places where we nest components, which is what I'm imagining you two are thinking). Obviously this is a mostly internal component so the breaking change is less damaging than most, but if we started changing things like EuiButton
with this pattern, I think we'd need to really consider the impact to benefit we're gaining.
Do we know what browser/OS combinations do or don't allow tabbing out of a native |
On Mac:
This might be another case where we get to make up the rules a bit. |
This approach does look a lot cleaner to me than #845 |
agreed. I will close 845 in favor of this |
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.
needs a changelog but otherwise LGTM
791009f
to
2bc2cd5
Compare
componentDidMount() { | ||
this._isMounted = true; | ||
document.addEventListener('click', this.onDocumentFocusChange); | ||
document.addEventListener('focusin', this.onDocumentFocusChange); |
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.
By leaving these event listeners active while the component is mounted, we can open the list when the user tabs backwards to give the clear button focus.
2bc2cd5
to
fecf0be
Compare
@chandlerprall @nreese I updated the tests, made some minor tweaks, and updated the CHANGELOG. Could you take another look please? |
} | ||
|
||
// Otherwise tab to the next adjacent item. | ||
tabbableItems[searchInputIndex + amount].focus(); |
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.
test if this exceeds tabbableItems.length
?
|
||
// Wrap to last tabbable if tabbing backwards. | ||
if (amount < 0) { | ||
if (searchInputIndex === 0) { |
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.
amount
may be less that -1
, test if searchInputIndex + amount < 0
- Set focus on searchInput when you click the caret to open the combo box. - Once open, clicking it again is a no-op.
- Redesign EuiFormControlLayout props to use icon and clear configuration objects.
9de6431
to
3f799e4
Compare
@chandlerprall Thanks for the review! I addressed your comments by adding a check that the |
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 change
@@ -137,26 +137,42 @@ export class EuiComboBox extends Component { | |||
}; | |||
|
|||
tabAway = amount => { | |||
if (![-1, 1].includes(amount)) { |
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.
IE doesn't support includes
. What do you think of if (Math.abs(amount) !== 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.
Oh interesting! We're using includes
in other places in EUI. Maybe we should just add documentation stating that we expect consumers to polyfill ES2015 features, e.g. with babel-polyfill. I think that's fair, since EUI is intended for usage within Elastic and I'd be surprised if we couldn't meet that requirements.
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.
Sounds like the right thing since the methods are already being used
This breaks the functionality of being able to close the list by clicking the caret. I think that is a nice feature to have is used in kibana functional tests. |
9f5c26c
to
d622d46
Compare
8fecab5
to
26065e8
Compare
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.
Padding stuff I'd consider a blocker, rest is just asking questions. Code quality is sharp as always.
// Loading spinner needs adjustment if clear also exists | ||
~ .euiFormControlLayout__loading { | ||
right: $euiFormControlPadding*3; | ||
> * + * { |
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 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.
Looking at this again, it's only a problem in the generic examples and not the real form controls themselves. So ignore the above. But we might want to provide a comment about the issue if someone wants to use this stuff.
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.
^^ Well, kinda. See the comment below about the number field.
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 idea, I'll add a note to the docs.
icon: PropTypes.string, | ||
icon: PropTypes.oneOfType([ | ||
PropTypes.string, | ||
PropTypes.shape({ |
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 this is a smarter pattern. One thing I think we probably should do is improve how we'll handle this kind of layout in our autodocs though. While importing the array values was never great and had similar problems, at least you could see the prop names themselves (which is probably the most important part). Right now it will be super vague and blackboxy. Most of the time when we do this kind of abstraction we actually only expose subsets of the original proplist (for example, color is not coming over from icon) so it will be harder to say "consult EuiIcon for props...etc).
Ultimately up to y'all, so I'll stay out of the decision here, but it'd be nice to have some sort of plan should we go down this route (if we are planning to adopt / migrate this pattern in more places where we nest components, which is what I'm imagining you two are thinking). Obviously this is a mostly internal component so the breaking change is less damaging than most, but if we started changing things like EuiButton
with this pattern, I think we'd need to really consider the impact to benefit we're gaining.
@@ -81,7 +82,14 @@ EuiFieldNumber.propTypes = { | |||
max: PropTypes.number, | |||
step: PropTypes.number, | |||
value: numberOrEmptyString, | |||
icon: PropTypes.string, | |||
icon: PropTypes.oneOfType([ |
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 lot of these components purposefully limited prop declarations. Is there a reason to add iconSide and onClick to the number field? I kind of like giving people less options here which is why I only exposed the props in some of the compoents. Generally, when we've given people the power to use things, they start using them. I'm just a little worried about seeing random action icons on the right side of inputs that lack context. Down arrow, clear? Makes sense. Random logstash icon with some hidden meaning and an onClick action has me worried 😄
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 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.
Oh great catch! This was an oversight on my part.
… docs with note about input padding.
@snide Thanks for your feedback! I've addressed your points. About the pattern of an |
works great |
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
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.
Cool. Thanks.
Thanks for pushing forward on this! LGTM |
Alternative approach to #845. Fixes #788 and #787. Also fixes a bug in which clicking the caret doesn't set focus to the search input.
This also seems to fix a bug in master, in which opening the list, using the down arrow to navigate to an item, and then hitting tab ends up setting focus to limbo. In this PR, you can't escape the list by tabbing. You have to hit Escape to return focus to the search input, and then you can tab away.
@nreese @chandlerprall What do you think of this approach? If you think this has promise then I need to update the CHANGELOG with the many breaking changes, update other instances in which
EuiFormControlLayout
is consumed, and fix and flesh out the tests.