-
Notifications
You must be signed in to change notification settings - Fork 8.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
[Controls] Range slider a11y and performance improvements #159271
[Controls] Range slider a11y and performance improvements #159271
Conversation
46908fb
to
1ab47c1
Compare
08d548f
to
aefe1d9
Compare
aefe1d9
to
d859706
Compare
a11y
6bf0aff
to
02acc60
Compare
7fffa6f
to
cbce8a0
Compare
18275fd
to
8a339a6
Compare
8a339a6
to
15f3495
Compare
<EuiFieldNumber | ||
controlOnly | ||
fullWidth | ||
value={value[0] === String(min) ? '' : value[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.
this.subscriptions.add( | ||
this.getInput$() | ||
.pipe( | ||
debounceTime(400), | ||
distinctUntilChanged((a, b) => isEqual(a.value, b.value)), | ||
distinctUntilChanged((a, b) => isEqual(a.value ?? ['', ''], b.value ?? ['', ''])), |
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 moved the debounce to the component itself - this limits the amount of input changes that are fired, which makes the dragging feel a lot smoother (although it's still not perfect).
const onClick = useCallback( | ||
(event) => { | ||
// the popover should remain open if the click/focus target is one of the number inputs | ||
if (isPopoverOpen && event.target instanceof HTMLInputElement) { | ||
return; | ||
} | ||
setIsPopoverOpen(true); | ||
}, | ||
[isPopoverOpen, setIsPopoverOpen] | ||
); |
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 is meant to replicate the behaviour of the EuiDualRange
component:
EuiDualRange |
Before | After |
---|---|---|
Note that this doesn't currently respond to focus/blur events as it should, which means keyboard interactivity is compromised.
Basically, the disableFocusTrap
on the EuiInputPopover
prevents the popover from being actually focusable/interactive via keyboard input. We could remove the disableFocusTrap
prop, but this means that someone using the keyboard controls can only make changes to their selected range via the up/down arrow keys and they cannot manually type in a given number - this is much worse UX IMO!
Because of the disableFocusTrap
prop, this also means that someone using keyboard controls cannot access the "clear selections" button - this is another reason why moving this to a hover action should be a priority.
We will be able to clean all this up + improve the keyboard accessibility once we are officially able to make the transition to the EuiDualRange
component rather than building our own :)
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 keyboard interactivity downside is totally fine for the time being, especially since this is still better than it was, and we may not even release in this intermediate state. Nice work!
Pinging @elastic/kibana-accessibility (Project:Accessibility) |
Pinging @elastic/kibana-presentation (Team:Presentation) |
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.
Looked through the code, and ran this locally!
The code all looks good, but I was able to run into some strange states with a control group with a options list -> range slider -> options list
. I'll keep trying to see if I can reproduce this, or if it's a problem in main.
Anyway, I added a few comments as well.
[RANGE_SLIDER_CONTROL]: { | ||
getPanelIsEqual: (initialInput, newInput) => { | ||
if (!deepEqual(omit(initialInput, 'explicitInput'), omit(newInput, 'explicitInput'))) { | ||
return false; | ||
} | ||
|
||
const { value: valueA = ['', ''] }: Partial<RangeSliderEmbeddableInput> = | ||
initialInput.explicitInput; | ||
const { value: valueB = ['', ''] }: Partial<RangeSliderEmbeddableInput> = | ||
newInput.explicitInput; | ||
return isEqual(valueA, valueB); | ||
}, | ||
}, |
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 to see these stability improvements!
const onClick = useCallback( | ||
(event) => { | ||
// the popover should remain open if the click/focus target is one of the number inputs | ||
if (isPopoverOpen && event.target instanceof HTMLInputElement) { | ||
return; | ||
} | ||
setIsPopoverOpen(true); | ||
}, | ||
[isPopoverOpen, setIsPopoverOpen] | ||
); |
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 keyboard interactivity downside is totally fine for the time being, especially since this is still better than it was, and we may not even release in this intermediate state. Nice work!
const hasUpperBoundSelection = value[1] !== ''; | ||
const debouncedOnChange = useMemo( | ||
() => | ||
debounce((newRange: RangeValue) => { |
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 usage of the lodash debounce to keep some of this in the react component rather than putting it into the embeddable class. Also great to see the debounce added!
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.
Update: I can reproduce the above issue in main, so it isn't a problem caused by this PR.
Additionally, ideally this will be solved when we move to EuiDualRange.
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.
Design changes LGTM. Great improvements!
💚 Build Succeeded
Metrics [docs]Module Count
Async chunks
Page load bundle
Unknown metric groupsESLint disabled line counts
Total ESLint disabled count
History
To update your PR or re-run it, just comment with: cc @Heenawter |
Closes #159395 Closes #153383 ## Summary This PR moves the "clear selections" button for all controls (options list, range slider, and time slider) from inside their respective popovers to a general hover action - this not only saves users a click for this common interaction (which has actually been brought in user feedback up as a downside of the current controls compared to the legacy controls), it also allows us to fully move forward with migrating the range slider control to the `EuiDualRange` component. This will be done in a follow up PR, which should both (1) clean up our range slider code significantly and (2) fix the [bug discussed here](#159271 (review)). The related issue can be tracked [here](#159724), since we might not be able to get to it right away. This "clear selections" action is available in both view and edit mode, like so: | | Edit mode | View mode | |--------|--------|--------| | **Range slider** | ![image](https://github.com/elastic/kibana/assets/8698078/83cb1e1a-0b20-43aa-a37b-14484b5f4945) | ![image](https://github.com/elastic/kibana/assets/8698078/0d28ce03-5242-4f3a-8a05-d447bca50ddb) | | **Options list** | ![image](https://github.com/elastic/kibana/assets/8698078/066257f6-c0ce-4e33-a193-5bbc62e341a6) | ![image](https://github.com/elastic/kibana/assets/8698078/d1ec124c-f5ee-4137-9eb9-33e06d522435) | | **Time slider** | ![image](https://github.com/elastic/kibana/assets/8698078/33b8bb80-fa0c-4281-ae81-f1e1b44086f3) | ![image](https://github.com/elastic/kibana/assets/8698078/bd7c41ae-706c-45f3-8b49-9bd4d259e5cf) | You may notice in the above screenshots that the "delete" action is now represented with a red trash icon rather than a red cross, and the tooltip text was also changed to use the word "Delete" rather than the word "Remove" - these changes were both made to be more consistent with the "Delete panel" action available on dashboards: | Delete control - Before | Delete control - After | Delete panel | |--------|--------|--------| | ![Screenshot 2023-06-13 at 5 32 22 PM](https://github.com/elastic/kibana/assets/8698078/2600b197-653b-43ea-a043-a50be7e6a796) | ![image](https://github.com/elastic/kibana/assets/8698078/5ef80380-2575-45fc-ba11-c59f3f252ac3) | <img src="https://github.com/elastic/kibana/assets/8698078/a7f65777-45cf-44f2-96a7-f1042cb25e02"/> | Beyond these changes, I also made a few quick changes to the time slider control, including: 1. Fixing the appearance so that the background is once again white, as described [here](#159526 (comment)) 2. Adding comparison logic so that clearing selections no longer causes unsaved changes unnecessarily, as described [here](#159526 (comment)) ### Videos **Before** https://github.com/elastic/kibana/assets/8698078/96365c85-748e-4fd7-ae5d-589aa11a23ef **After** https://github.com/elastic/kibana/assets/8698078/68352559-e71b-4b5e-8709-587016f0b35a ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: kibanamachine <[email protected]>
Closes #135466
Summary
The main goal of this PR is to fix the serious "Buttons must have discernible text" a11y failure - this is accomplished by switching from building our own range slider button using
EuiFlexGroup
to instead usingEuiFormControlLayoutDelimited
, which both resolves these a11y issues and also fixes a rendering regression:As part of this, I also took some time to clean up some of the range slider code, which hasn't really been touched in awhile - this includes...
input$
subscription to the component itself, as described here.onClick
behaviour (with some notable limitations), as described here.As a follow up, we need to move the "clear selections" button to a hover action, which will enable us to then fully move forward with our transition to the
EuiDualRange
component.Checklist
Any UI touched in this PR is usable by keyboard only (learn more about keyboard accessibility)For maintainers