Skip to content
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(slider): double click on slider handle to reset slider position #3991

Merged
merged 29 commits into from Mar 5, 2024

Conversation

Rajdeepc
Copy link
Contributor

@Rajdeepc Rajdeepc commented Feb 1, 2024

Description

Double click on slider handle to reset slider position

Related issue(s)

Motivation and context

How has this been tested?

  • Test case 1
    1. Go here
    2. Do this
  • Test case 2
    1. Go here
    2. Do this

Screenshots (if appropriate)

sliderdefault.mov

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Chore (minor updates related to the tooling or maintenance of the repository, does not impact compiled assets)

Checklist

  • I have signed the Adobe Open Source CLA.
  • My code follows the code style of this project.
  • If my change required a change to the documentation, I have updated the documentation in this pull request.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • I have reviewed at the Accessibility Practices for this feature, see: Aria Practices

Best practices

This repository uses conventional commit syntax for each commit message; note that the GitHub UI does not use this by default so be cautious when accepting suggested changes. Avoid the "Update branch" button on the pull request and opt instead for rebasing your branch against main.

@Rajdeepc Rajdeepc linked an issue Feb 1, 2024 that may be closed by this pull request
1 task
Copy link

github-actions bot commented Feb 1, 2024

Tachometer results

Chrome

action-bar permalink

Version Bytes Avg Time vs remote vs branch
npm latest 489 kB 68.17ms - 70.76ms - unsure 🔍
-3% - +3%
-1.98ms - +2.15ms
branch 474 kB 67.78ms - 70.99ms unsure 🔍
-3% - +3%
-2.15ms - +1.98ms
-

action-menu permalink

Version Bytes Avg Time vs remote vs branch
npm latest 655 kB 159.95ms - 163.34ms - unsure 🔍
-1% - +2%
-2.13ms - +2.44ms
branch 635 kB 159.96ms - 163.03ms unsure 🔍
-2% - +1%
-2.44ms - +2.13ms
-

combobox permalink

Version Bytes Avg Time vs remote vs branch
npm latest 706 kB 37.21ms - 37.57ms - unsure 🔍
-0% - +1%
-0.16ms - +0.40ms
branch 695 kB 37.05ms - 37.48ms unsure 🔍
-1% - +0%
-0.40ms - +0.16ms
-

menu permalink

Version Bytes Avg Time vs remote vs branch
npm latest 480 kB 206.64ms - 210.76ms - unsure 🔍
-0% - +2%
-0.97ms - +4.66ms
branch 465 kB 204.93ms - 208.78ms unsure 🔍
-2% - +0%
-4.66ms - +0.97ms
-

overlay permalink

Version Bytes Avg Time vs remote vs branch
npm latest 494 kB 53.76ms - 54.82ms - unsure 🔍
-0% - +2%
-0.25ms - +1.08ms
branch 475 kB 53.47ms - 54.28ms unsure 🔍
-2% - +0%
-1.08ms - +0.25ms
-

picker permalink

Version Bytes Avg Time vs remote vs branch
npm latest 518 kB 545.63ms - 552.87ms - faster ✔
0% - 2%
0.73ms - 13.64ms
branch 501 kB 551.10ms - 561.78ms slower ❌
0% - 2%
0.73ms - 13.64ms
-

popover permalink

Version Bytes Avg Time vs remote vs branch
npm latest 386 kB 21.24ms - 21.37ms - slower ❌
1% - 2%
0.16ms - 0.33ms
branch 373 kB 21.00ms - 21.12ms faster ✔
1% - 2%
0.16ms - 0.33ms
-

slider permalink

Version Bytes Avg Time vs remote vs branch
npm latest 481 kB 101.89ms - 103.94ms - unsure 🔍
-1% - +1%
-1.47ms - +1.19ms
branch 469 kB 102.21ms - 103.90ms unsure 🔍
-1% - +1%
-1.19ms - +1.47ms
-

split-button permalink

Version Bytes Avg Time vs remote vs branch
npm latest 722 kB 1860.85ms - 1863.21ms - unsure 🔍
-0% - -0%
-4.76ms - -1.63ms
branch 708 kB 1864.19ms - 1866.25ms unsure 🔍
+0% - +0%
+1.63ms - +4.76ms
-

tooltip permalink

Version Bytes Avg Time vs remote vs branch
npm latest 560 kB 43.19ms - 43.95ms - unsure 🔍
-0% - +2%
-0.18ms - +0.84ms
branch 539 kB 42.91ms - 43.58ms unsure 🔍
-2% - +0%
-0.84ms - +0.18ms
-
Firefox

action-bar permalink

Version Bytes Avg Time vs remote vs branch
npm latest 489 kB 147.43ms - 154.01ms - unsure 🔍
-2% - +4%
-2.63ms - +6.63ms
branch 474 kB 145.47ms - 151.97ms unsure 🔍
-4% - +2%
-6.63ms - +2.63ms
-

action-menu permalink

Version Bytes Avg Time vs remote vs branch
npm latest 655 kB 320.04ms - 333.32ms - unsure 🔍
-1% - +4%
-2.20ms - +12.80ms
branch 635 kB 317.90ms - 324.86ms unsure 🔍
-4% - +1%
-12.80ms - +2.20ms
-

combobox permalink

Version Bytes Avg Time vs remote vs branch
npm latest 706 kB 67.77ms - 75.15ms - slower ❌
7% - 19%
4.55ms - 12.17ms
branch 695 kB 62.14ms - 64.06ms faster ✔
7% - 16%
4.55ms - 12.17ms
-

menu permalink

Version Bytes Avg Time vs remote vs branch
npm latest 480 kB 435.39ms - 447.05ms - unsure 🔍
-2% - +2%
-6.96ms - +9.56ms
branch 465 kB 434.07ms - 445.77ms unsure 🔍
-2% - +2%
-9.56ms - +6.96ms
-

overlay permalink

Version Bytes Avg Time vs remote vs branch
npm latest 589 kB 114.23ms - 119.97ms - unsure 🔍
-1% - +5%
-1.57ms - +5.29ms
branch 579 kB 113.36ms - 117.12ms unsure 🔍
-4% - +1%
-5.29ms - +1.57ms
-

picker permalink

Version Bytes Avg Time vs remote vs branch
npm latest 518 kB 1014.82ms - 1044.70ms - unsure 🔍
-0% - +3%
-3.71ms - +27.71ms
branch 501 kB 1012.88ms - 1022.64ms unsure 🔍
-3% - +0%
-27.71ms - +3.71ms
-

popover permalink

Version Bytes Avg Time vs remote vs branch
npm latest 386 kB 42.42ms - 46.10ms - unsure 🔍
-5% - +7%
-2.22ms - +3.26ms
branch 373 kB 41.71ms - 45.77ms unsure 🔍
-7% - +5%
-3.26ms - +2.22ms
-

slider permalink

Version Bytes Avg Time vs remote vs branch
npm latest 481 kB 200.14ms - 207.38ms - unsure 🔍
-3% - +2%
-5.30ms - +4.98ms
branch 469 kB 200.26ms - 207.58ms unsure 🔍
-2% - +3%
-4.98ms - +5.30ms
-

split-button permalink

Version Bytes Avg Time vs remote vs branch
npm latest 722 kB 1635.23ms - 1643.29ms - faster ✔
0% - 1%
3.32ms - 15.88ms
branch 708 kB 1644.04ms - 1653.68ms slower ❌
0% - 1%
3.32ms - 15.88ms
-

tooltip permalink

Version Bytes Avg Time vs remote vs branch
npm latest 654 kB 91.45ms - 96.59ms - slower ❌
2% - 8%
1.70ms - 7.42ms
branch 641 kB 88.19ms - 90.73ms faster ✔
2% - 8%
1.70ms - 7.42ms
-

@Rajdeepc Rajdeepc marked this pull request as ready for review February 2, 2024 11:38
@Rajdeepc Rajdeepc self-assigned this Feb 2, 2024
@Westbrook
Copy link
Collaborator

Please add tests cases for the various input modalities covered in this change to the PR description.

@TarunAdobe
Copy link
Contributor

screen-capture.webm
Tested on simulator

@Rajdeepc Rajdeepc changed the title chore(slider): double click on slider handle to reset slider position chore(slider): double click on slider handle to reset slider position + update sp-slider handle to S1 design Feb 14, 2024
@Rajdeepc
Copy link
Contributor Author

@Westbrook Slider Handle UI is updated according to S1 designs
Screenshot 2024-02-14 at 3 48 28 PM
Screenshot 2024-02-14 at 3 48 34 PM

Copy link
Collaborator

@Westbrook Westbrook left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is currently no way of knowing that this interaction exists. Further, it's not clear how to trigger it when interacting with a keyboard or with a screen reader.

packages/slider/src/HandleController.ts Outdated Show resolved Hide resolved
@Rajdeepc Rajdeepc changed the title chore(slider): double click on slider handle to reset slider position + update sp-slider handle to S1 design chore(slider): double click on slider handle to reset slider position Feb 15, 2024
@spdev3000
Copy link
Collaborator

Did you try listen to dblclick event to avoid timers here?

@Rajdeepc Rajdeepc assigned TarunAdobe and unassigned Rajdeepc Feb 15, 2024
Copy link

github-actions bot commented Feb 21, 2024

Lighthouse scores

Category Latest (report) Main (report) Branch (report)
Performance 0.97 0.97 0.98
Accessibility 1 1 1
Best Practices 1 1 1
SEO 1 0.92 0.92
PWA 1 1 1
What is this?

Lighthouse scores comparing the documentation site built from the PR ("Branch") to that of the production documentation site ("Latest") and the build currently on main ("Main"). Higher scores are better, but note that the SEO scores on Netlify URLs are artifically constrained to 0.92.

Transfer Size

Category Latest Main Branch
Total 242.248 kB 228.573 kB 227.304 kB 🏆
Scripts 59.321 kB 54.409 kB 54.258 kB 🏆
Stylesheet 50.471 kB 42.302 kB 41.195 kB 🏆
Document 5.744 kB 5.15 kB 5.139 kB 🏆
Third Party 126.712 kB 126.712 kB 126.712 kB

Request Count

Category Latest Main Branch
Total 42 🏆 43 43
Scripts 34 🏆 35 35
Stylesheet 5 5 5
Document 1 1 1
Third Party 2 2 2

@Rajdeepc
Copy link
Contributor Author

Did you try listen to dblclick event to avoid timers here?

Yes thats the way to go forward to include on all the input modalities. The code should now listen to a dblClick event instead of handling it with a timer function. Thanks for pointing it out.

@spdev3000
Copy link
Collaborator

looks good to me so far - one question:
If the slider is part of an overlaid popover or modal dialog and we want to leverage a default-value:
If the user clicks and drags the slider handle and then presses Escape key to close/dismiss the outer popover overlay or the modal dialog - the slider would first reset its value and then the overlay would close, correct?
If so: any idea how to let the user only close the overlay (containing the slider) and not reset the default value?
Should we add another property to control/disable that (Escape=reset) behavior for such use cases?

@TarunAdobe
Copy link
Contributor

Valid question @spdev3000. You are right if the user drags the slider that is contained inside of an overlaid content and then presses escape the slider value will reset instead of closing the modal... This interaction pattern was suggested by someone from the accessibility team.
And for your second question: I don't think it's possible to avoid reseting the value of the slider in case the slider has a default-value attribute and the user presses escape key. As for having a separate attribute/property to control this behaviour, It's possible to have it but we need a few more eyes on this... Maybe @Westbrook and @Rajdeepc can help us decide the correct path forward.

*/
public handleDoubleClick(event: PointerEvent): void {
const { input } = this.extractDataFromEvent(event);
if (input.model?.handle.defaultValue) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the default value in 0 then this test will not work appropriately.

min="0"
value=".5"
step="0.01"
default-value="0.2"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be useful to put default-value into the args, but not a blocker.

@@ -1604,4 +1603,75 @@ describe('Slider', () => {
await elementUpdated(el);
expect(el.values).to.deep.equal({ a: 10, b: 20, c: 29 });
});
it('resets to default value on double click after moving pointer', async () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a test where the Slider is in an Overlay to confirm it does not close the Overlay.

We should also add default-value to one or some of the Stories in Overlay to confirm this functionality there, as well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added the test (line 1679) as well as the default-value for stories in overlay.

Comment on lines 1620 to 1622
const handle = el.shadowRoot.querySelector('.handle') as HTMLDivElement;
el.track.setPointerCapture = (id: number) => (pointerId = id);
el.track.releasePointerCapture = (id: number) => (pointerId = id);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use sendMouse() all the way through the test here, instead of synthetically dispatching events. Then we should be able to exclude this part of the test all together. Here we are mimicking the default pointerCapture functionality, which the actual pointer interactions in sendMouse() do not need.

Comment on lines 557 to 560

protected override firstUpdated(changes: PropertyValues<this>): void {
super.firstUpdated(changes);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're not actually doing work here than empty additions to the call stack are just costing us tiny amounts of performance.

@@ -442,6 +443,7 @@ export class Slider extends SizedMixin(ObserveSlotText(SliderHandle, ''), {
['pointerup', 'pointercancel', 'pointerleave'],
this.handlePointerup,
],
streamOutside: ['dblclick', this.handleDoubleClick],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Half surprised this works, but it does make a nice even API for this.

private renderHandle(): TemplateResult {
if (this.variant === 'tick') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼

@change=${this.onInputChange}
@focus=${this.onInputFocus}
@blur=${this.onInputBlur}
@keydown=${this.onInputKeydown}
.model=${model}
/>
<p id="sliderDesc" class="visually-hidden">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be OK as just display: none as it is describedby content. Otherwise you get multiple stops of the screen reader cursor. This should be confirmed manually after the change, preferably in assistivlabs across more than one screen reader, as also in a unit test using accessibility snapshotting.

Probably OK to make it a span over a p as it's not visible, too.

if (event.key == 'Escape') {
const input = event.target as InputWithModel;
if (
input.model.handle?.defaultValue &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will also fail on 0.

@change=${this.onInputChange}
@focus=${this.onInputFocus}
@blur=${this.onInputBlur}
@keydown=${this.onInputKeydown}
.model=${model}
/>
<p id="sliderDesc" class="visually-hidden">
Press escape or double click to reset the slider to its
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add an entry for this content to #1975

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we display: none in the CSS rather than inline?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done 👍

@spdev3000
Copy link
Collaborator

As another requirement: the double click to default-value use case should also work for editable sliders, as most of our use cases have editable versions and need that feature working there as well.

@Westbrook Westbrook changed the title chore(slider): double click on slider handle to reset slider position fix(slider): double click on slider handle to reset slider position Mar 1, 2024
Copy link
Collaborator

@Westbrook Westbrook left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating the demos and adding the test, this is looking much closer to being ready to ship. A couple of small nuances and then we're ready to go!

Comment on lines 1699 to 1701
// open the overlay
const trigger = el.querySelector('#trigger') as HTMLButtonElement;
trigger.click();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overlay open action should follow the pattern:

const opened = oneEvent(el, 'sp-opened');
trigger.click(); // or similiar
await opened;

Otherwise there's no guarantee the content actually opened and was promoted to the top layer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it!

Comment on lines +1724 to +1727
// send escape key again
await sendKeys({
press: 'Escape',
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to reverse of the above, e.g. const closed = oneEvent(...);


if (input.model?.handle.defaultValue !== undefined) {
input.model.handle.value = input.model.handle.defaultValue;
this.dispatchChangeEvent(input, input.model.handle);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirm whether this dispatches an input event with the same data. See the result of clicking a new value in an <input type="range"> as an example: https://codepen.io/Westbrook/pen/ExJjVRZ?editors=1111

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added dispatchInputEvent on both double-click and escape key interactions

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's a good test to confirm that this happens before we close this up?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make sense if I extend the dispatches input of the animation frame test (line 388) or should I make a separate one?

input.model.handle.value = input.model.handle.defaultValue;
this.dispatchChangeEvent(input, input.model.handle);
this.requestUpdate();
event.preventDefault();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to dispatch an input event with the same data. See the result of clicking a new value in an as an example: https://codepen.io/Westbrook/pen/ExJjVRZ?editors=1111

Copy link
Collaborator

@Westbrook Westbrook left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Catching this up to main and if CI stays green, I'll merge this in a bit.

@Westbrook Westbrook merged commit 64c594a into main Mar 5, 2024
48 checks passed
@Westbrook Westbrook deleted the feature/slider-default branch March 5, 2024 16:57
@Rajdeepc Rajdeepc self-assigned this Mar 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feat]: Default-values for slider / sider-handle
4 participants