From 2827c3f4399d4107250ef2587c250f8ad8b2e77c Mon Sep 17 00:00:00 2001 From: Rajdeep Chandra Date: Thu, 1 Feb 2024 17:18:49 +0530 Subject: [PATCH 01/25] chore(slider): added logic to double click reset value --- packages/slider/src/HandleController.ts | 75 ++++++++++++++++++------- packages/slider/src/Slider.ts | 7 ++- packages/slider/src/SliderHandle.ts | 1 + 3 files changed, 63 insertions(+), 20 deletions(-) diff --git a/packages/slider/src/HandleController.ts b/packages/slider/src/HandleController.ts index 279bf21042..607aa5119d 100644 --- a/packages/slider/src/HandleController.ts +++ b/packages/slider/src/HandleController.ts @@ -341,27 +341,46 @@ export class HandleController { private _activePointerEventData!: DataFromPointerEvent | undefined; - public handlePointerdown(event: PointerEvent): void { - const { resolvedInput, model } = this.extractDataFromEvent(event); - if (!model || this.host.disabled || event.button !== 0) { - event.preventDefault(); - return; - } - this.host.track.setPointerCapture(event.pointerId); - this.updateBoundingRect(); - if (event.pointerType === 'mouse') { - this.host.labelEl.click(); + /** + * @description check for defaultvalue(value) property in sp-slider and reset on double click on sliderHandle + * @param event + */ + public handleDoubleClick(event: PointerEvent): void { + const { input } = this.extractDataFromEvent(event); + if (input.model.handle?.defaultValue) { + input.model.handle.value = input.model.handle.defaultValue; + this.dispatchChangeEvent(input, input.model.handle); + this.requestUpdate(); } - this.draggingHandle = model.handle; - model.handle.dragging = true; - this.activateHandle(model.name); - if (resolvedInput) { - // When the input is resolved forward the pointer event to - // `handlePointermove` in order to update the value/UI becuase - // the pointer event was on the track not a handle - this.handlePointermove(event); + } + + private clickTimer: number | null = null; + + public handlePointerdown(event: PointerEvent): void { + if (this.checkForDoubleClick()) { + this.handleDoubleClick(event); + } else { + const { resolvedInput, model } = this.extractDataFromEvent(event); + if (!model || this.host.disabled || event.button !== 0) { + event.preventDefault(); + return; + } + this.host.track.setPointerCapture(event.pointerId); + this.updateBoundingRect(); + if (event.pointerType === 'mouse') { + this.host.labelEl.click(); + } + this.draggingHandle = model.handle; + model.handle.dragging = true; + this.activateHandle(model.name); + if (resolvedInput) { + // When the input is resolved forward the pointer event to + // `handlePointermove` in order to update the value/UI becuase + // the pointer event was on the track not a handle + this.handlePointermove(event); + } + this.requestUpdate(); } - this.requestUpdate(); } public handlePointerup(event: PointerEvent): void { @@ -438,6 +457,24 @@ export class HandleController { this.requestUpdate(); }; + /** + * @description method to check whether a click or double click on slider Handle + * @returns boolean + */ + private checkForDoubleClick = (): boolean => { + if (this.clickTimer !== null) { + clearTimeout(this.clickTimer); + this.clickTimer = null; + return true; + } + + this.clickTimer = setTimeout(() => { + this.clickTimer = null; + }, 300); + + return false; + }; + private dispatchChangeEvent( input: HTMLInputElement, handle: SliderHandle diff --git a/packages/slider/src/Slider.ts b/packages/slider/src/Slider.ts index 2c6cf8563f..ab16aba39a 100644 --- a/packages/slider/src/Slider.ts +++ b/packages/slider/src/Slider.ts @@ -362,7 +362,6 @@ export class Slider extends SizedMixin(ObserveSlotText(SliderHandle, ''), { * @description calculates the fill width * @param fillStartValue * @param currentValue - * @param cachedValue * @returns */ private getOffsetWidth( @@ -548,4 +547,10 @@ export class Slider extends SizedMixin(ObserveSlotText(SliderHandle, ''), { } } } + + protected override firstUpdated(changes: PropertyValues): void { + if (changes.has('value')) { + this.defaultValue = Number(this.value); + } + } } diff --git a/packages/slider/src/SliderHandle.ts b/packages/slider/src/SliderHandle.ts index d680e02c32..2bbc554568 100644 --- a/packages/slider/src/SliderHandle.ts +++ b/packages/slider/src/SliderHandle.ts @@ -73,6 +73,7 @@ const MaxConverter = { */ export class SliderHandle extends Focusable { public handleController?: HandleController; + public defaultValue: number | undefined; public get handleName(): string { return this.name; From 15ade1f48215d4f90f8a07edffba610c83a5e336 Mon Sep 17 00:00:00 2001 From: Rajdeep Chandra Date: Thu, 1 Feb 2024 19:21:22 +0530 Subject: [PATCH 02/25] chore(slider): correct type of timer --- packages/slider/src/HandleController.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/slider/src/HandleController.ts b/packages/slider/src/HandleController.ts index 607aa5119d..f7d5573800 100644 --- a/packages/slider/src/HandleController.ts +++ b/packages/slider/src/HandleController.ts @@ -354,8 +354,6 @@ export class HandleController { } } - private clickTimer: number | null = null; - public handlePointerdown(event: PointerEvent): void { if (this.checkForDoubleClick()) { this.handleDoubleClick(event); @@ -457,6 +455,7 @@ export class HandleController { this.requestUpdate(); }; + private clickTimer: number | NodeJS.Timeout | null = null; /** * @description method to check whether a click or double click on slider Handle * @returns boolean From 01eb2331989dfd4357e8c4f899d23d593646c4fc Mon Sep 17 00:00:00 2001 From: Rajdeep Chandra Date: Fri, 2 Feb 2024 14:12:46 +0530 Subject: [PATCH 03/25] chore(slider): updated tests --- packages/slider/src/HandleController.ts | 36 +++++++++++-------------- packages/slider/test/index.ts | 5 ++++ packages/slider/test/slider.test.ts | 10 ++++++- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/packages/slider/src/HandleController.ts b/packages/slider/src/HandleController.ts index f7d5573800..842dad93ae 100644 --- a/packages/slider/src/HandleController.ts +++ b/packages/slider/src/HandleController.ts @@ -88,6 +88,21 @@ export class HandleController { } return result; } + private clickTimer: number | NodeJS.Timeout | null = null; + + public get isDoubleClick(): boolean { + if (this.clickTimer !== null) { + clearTimeout(this.clickTimer); + this.clickTimer = null; + return true; + } + + this.clickTimer = setTimeout(() => { + this.clickTimer = null; + }, 300); + + return false; + } public get size(): number { return this.handles.size; @@ -355,7 +370,7 @@ export class HandleController { } public handlePointerdown(event: PointerEvent): void { - if (this.checkForDoubleClick()) { + if (this.isDoubleClick) { this.handleDoubleClick(event); } else { const { resolvedInput, model } = this.extractDataFromEvent(event); @@ -455,25 +470,6 @@ export class HandleController { this.requestUpdate(); }; - private clickTimer: number | NodeJS.Timeout | null = null; - /** - * @description method to check whether a click or double click on slider Handle - * @returns boolean - */ - private checkForDoubleClick = (): boolean => { - if (this.clickTimer !== null) { - clearTimeout(this.clickTimer); - this.clickTimer = null; - return true; - } - - this.clickTimer = setTimeout(() => { - this.clickTimer = null; - }, 300); - - return false; - }; - private dispatchChangeEvent( input: HTMLInputElement, handle: SliderHandle diff --git a/packages/slider/test/index.ts b/packages/slider/test/index.ts index 4a24bc80e6..4fd73ebfc9 100644 --- a/packages/slider/test/index.ts +++ b/packages/slider/test/index.ts @@ -295,6 +295,7 @@ export const testEditableSlider = (type: string): void => { pointerType: 'mouse', }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(el.dragging).to.be.false; @@ -310,6 +311,8 @@ export const testEditableSlider = (type: string): void => { pointerType: 'mouse', }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); + await elementUpdated(el); expect(el.dragging, 'it is dragging 1').to.be.true; @@ -340,6 +343,8 @@ export const testEditableSlider = (type: string): void => { pointerType: 'mouse', }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); + await elementUpdated(el); expect(el.dragging, 'it is dragging 2').to.be.true; diff --git a/packages/slider/test/slider.test.ts b/packages/slider/test/slider.test.ts index 68139c2248..d1665b0081 100644 --- a/packages/slider/test/slider.test.ts +++ b/packages/slider/test/slider.test.ts @@ -154,6 +154,7 @@ describe('Slider', () => { composed: true, }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(el.dragging).to.be.false; @@ -168,8 +169,8 @@ describe('Slider', () => { composed: true, }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); - expect(el.dragging, 'it is dragging 1').to.be.true; expect(pointerId, '2').to.equal(1); @@ -196,6 +197,7 @@ describe('Slider', () => { composed: true, }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(el.dragging, 'it is dragging 2').to.be.true; @@ -243,6 +245,7 @@ describe('Slider', () => { cancelable: true, }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(pointerId).to.equal(-1); @@ -259,6 +262,7 @@ describe('Slider', () => { cancelable: true, }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(pointerId).to.equal(4); @@ -320,6 +324,7 @@ describe('Slider', () => { cancelable: true, }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(el.dragging).to.be.false; @@ -337,6 +342,7 @@ describe('Slider', () => { cancelable: true, }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(pointerId).to.equal(-1); @@ -580,6 +586,7 @@ describe('Slider', () => { bubbles: true, }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); await nextFrame(); handle.dispatchEvent( @@ -633,6 +640,7 @@ describe('Slider', () => { bubbles: true, }) ); + await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); handle.dispatchEvent( new PointerEvent('pointermove', { From 620506223d0723e77ba57b11cf3881ba85455fa1 Mon Sep 17 00:00:00 2001 From: Rajdeep Chandra Date: Fri, 2 Feb 2024 14:55:49 +0530 Subject: [PATCH 04/25] chore(slider): added tests --- packages/slider/test/slider.test.ts | 67 +++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/packages/slider/test/slider.test.ts b/packages/slider/test/slider.test.ts index d1665b0081..17b2526e5e 100644 --- a/packages/slider/test/slider.test.ts +++ b/packages/slider/test/slider.test.ts @@ -1612,4 +1612,71 @@ 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 () => { + let pointerId = -1; + const el = await fixture( + html` + + ` + ); + await elementUpdated(el); + expect(el.value, 'initial').to.equal(0.7); + expect(pointerId).to.equal(-1); + const handle = el.shadowRoot.querySelector('.handle') as HTMLDivElement; + el.track.setPointerCapture = (id: number) => (pointerId = id); + el.track.releasePointerCapture = (id: number) => (pointerId = id); + + handle.dispatchEvent( + new PointerEvent('pointermove', { + clientX: 0, + cancelable: true, + composed: true, + bubbles: true, + }) + ); + + await elementUpdated(el); + await nextFrame(); + + handle.dispatchEvent( + new PointerEvent('pointerdown', { + clientX: 0, + cancelable: true, + button: 0, + composed: true, + bubbles: true, + }) + ); + + handle.dispatchEvent(new PointerEvent('pointerup')); + + await new Promise((resolve) => setTimeout(resolve, 300)); + + handle.dispatchEvent( + new PointerEvent('pointerdown', { + clientX: 0, + cancelable: true, + button: 0, + composed: true, + bubbles: true, + }) + ); + + handle.dispatchEvent(new PointerEvent('pointerup')); + + await elementUpdated(el); + await nextFrame(); + + expect( + el.value, + 'reset to default value on double click after moving pointer' + ).to.equal(0.7); + }); }); From 15d0170601a77cd20ad02d3ee9499289776d6a1d Mon Sep 17 00:00:00 2001 From: Rajdeep Chandra Date: Fri, 2 Feb 2024 16:01:35 +0530 Subject: [PATCH 05/25] chore(slider): updated firstupdated lifecycle --- packages/slider/src/Slider.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/slider/src/Slider.ts b/packages/slider/src/Slider.ts index ab16aba39a..b583e26519 100644 --- a/packages/slider/src/Slider.ts +++ b/packages/slider/src/Slider.ts @@ -548,7 +548,8 @@ export class Slider extends SizedMixin(ObserveSlotText(SliderHandle, ''), { } } - protected override firstUpdated(changes: PropertyValues): void { + protected override firstUpdated(changes: PropertyValues): void { + super.firstUpdated(changes); if (changes.has('value')) { this.defaultValue = Number(this.value); } From 745e5c003588d5e36b22bfec5906e884e04155b1 Mon Sep 17 00:00:00 2001 From: Rajdeep Chandra Date: Wed, 14 Feb 2024 15:36:01 +0530 Subject: [PATCH 06/25] chore(slider): updated slider handle position --- packages/slider/src/Slider.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/slider/src/Slider.ts b/packages/slider/src/Slider.ts index b583e26519..372b3b52c0 100644 --- a/packages/slider/src/Slider.ts +++ b/packages/slider/src/Slider.ts @@ -408,6 +408,14 @@ export class Slider extends SizedMixin(ObserveSlotText(SliderHandle, ''), { > `; } + private renderHandle(): TemplateResult { + if (this.variant === 'tick') { + return html``; + } + return html` + ${this.handleController.render()} + `; + } private renderHandle(): TemplateResult { return html` From 47ca064acfde0ff520b58184ffed1c5756f6069a Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Wed, 21 Feb 2024 16:50:16 +0530 Subject: [PATCH 07/25] feat(slider): reset default value on dblclick --- packages/slider/src/HandleController.ts | 57 +++++++++---------------- packages/slider/src/Slider.ts | 5 +++ 2 files changed, 24 insertions(+), 38 deletions(-) diff --git a/packages/slider/src/HandleController.ts b/packages/slider/src/HandleController.ts index 842dad93ae..03c9a99fcc 100644 --- a/packages/slider/src/HandleController.ts +++ b/packages/slider/src/HandleController.ts @@ -88,21 +88,6 @@ export class HandleController { } return result; } - private clickTimer: number | NodeJS.Timeout | null = null; - - public get isDoubleClick(): boolean { - if (this.clickTimer !== null) { - clearTimeout(this.clickTimer); - this.clickTimer = null; - return true; - } - - this.clickTimer = setTimeout(() => { - this.clickTimer = null; - }, 300); - - return false; - } public get size(): number { return this.handles.size; @@ -370,30 +355,26 @@ export class HandleController { } public handlePointerdown(event: PointerEvent): void { - if (this.isDoubleClick) { - this.handleDoubleClick(event); - } else { - const { resolvedInput, model } = this.extractDataFromEvent(event); - if (!model || this.host.disabled || event.button !== 0) { - event.preventDefault(); - return; - } - this.host.track.setPointerCapture(event.pointerId); - this.updateBoundingRect(); - if (event.pointerType === 'mouse') { - this.host.labelEl.click(); - } - this.draggingHandle = model.handle; - model.handle.dragging = true; - this.activateHandle(model.name); - if (resolvedInput) { - // When the input is resolved forward the pointer event to - // `handlePointermove` in order to update the value/UI becuase - // the pointer event was on the track not a handle - this.handlePointermove(event); - } - this.requestUpdate(); + const { resolvedInput, model } = this.extractDataFromEvent(event); + if (!model || this.host.disabled || event.button !== 0) { + event.preventDefault(); + return; + } + this.host.track.setPointerCapture(event.pointerId); + this.updateBoundingRect(); + if (event.pointerType === 'mouse') { + this.host.labelEl.click(); } + this.draggingHandle = model.handle; + model.handle.dragging = true; + this.activateHandle(model.name); + if (resolvedInput) { + // When the input is resolved forward the pointer event to + // `handlePointermove` in order to update the value/UI becuase + // the pointer event was on the track not a handle + this.handlePointermove(event); + } + this.requestUpdate(); } public handlePointerup(event: PointerEvent): void { diff --git a/packages/slider/src/Slider.ts b/packages/slider/src/Slider.ts index 372b3b52c0..6593288b44 100644 --- a/packages/slider/src/Slider.ts +++ b/packages/slider/src/Slider.ts @@ -449,6 +449,7 @@ export class Slider extends SizedMixin(ObserveSlotText(SliderHandle, ''), { ['pointerup', 'pointercancel', 'pointerleave'], this.handlePointerup, ], + streamOutside: ['dblclick', this.handleDoubleClick], })} >
@@ -482,6 +483,10 @@ export class Slider extends SizedMixin(ObserveSlotText(SliderHandle, ''), { `; } + protected handleDoubleClick(event: Event): void { + this.handleController.handleDoubleClick(event); + } + protected handlePointerdown(event: PointerEvent): void { this.handleController.handlePointerdown(event); } From 3ce3482794d3c7d5fcc82f6c0feefd6ddfa3bc73 Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Wed, 21 Feb 2024 17:11:32 +0530 Subject: [PATCH 08/25] chore: updated golden image hash --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index dc0bfa07ae..4c48747633 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ executors: parameters: current_golden_images_hash: type: string - default: 058e55ea2c05e5ab919bc7c9f9fc9ea2d5f6f816 + default: 47ca064acfde0ff520b58184ffed1c5756f6069a wireit_cache_name: type: string default: wireit From 26956b62b4e3ef01d640ff910088fbb1d796e18c Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Wed, 21 Feb 2024 17:12:09 +0530 Subject: [PATCH 09/25] chore(slider): removed repeated function in slider --- packages/slider/src/Slider.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/slider/src/Slider.ts b/packages/slider/src/Slider.ts index 6593288b44..8d03a00720 100644 --- a/packages/slider/src/Slider.ts +++ b/packages/slider/src/Slider.ts @@ -417,12 +417,6 @@ export class Slider extends SizedMixin(ObserveSlotText(SliderHandle, ''), { `; } - private renderHandle(): TemplateResult { - return html` - ${this.variant === 'tick' ? html`` : this.handleController.render()} - `; - } - private renderTrack(): TemplateResult { const segments = this.handleController.trackSegments(); const handleItems = [ @@ -483,7 +477,7 @@ export class Slider extends SizedMixin(ObserveSlotText(SliderHandle, ''), { `; } - protected handleDoubleClick(event: Event): void { + protected handleDoubleClick(event: PointerEvent): void { this.handleController.handleDoubleClick(event); } From 48231914f22af9f91c6a2dcf630278b3d09d007a Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Wed, 21 Feb 2024 20:36:24 +0530 Subject: [PATCH 10/25] chore(slider): updated tests for dbclick event --- packages/slider/test/slider.test.ts | 44 ++++++++++++++--------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/slider/test/slider.test.ts b/packages/slider/test/slider.test.ts index 17b2526e5e..1eefe216be 100644 --- a/packages/slider/test/slider.test.ts +++ b/packages/slider/test/slider.test.ts @@ -1616,51 +1616,54 @@ describe('Slider', () => { let pointerId = -1; const el = await fixture( html` - + ` ); await elementUpdated(el); - expect(el.value, 'initial').to.equal(0.7); + expect(el.value, 'initial').to.equal(50); expect(pointerId).to.equal(-1); const handle = el.shadowRoot.querySelector('.handle') as HTMLDivElement; el.track.setPointerCapture = (id: number) => (pointerId = id); el.track.releasePointerCapture = (id: number) => (pointerId = id); + await sendMouse({ + steps: [ + { + type: 'move', + position: [9, 30], + }, + { + type: 'down', + }, + ], + }); + await elementUpdated(el); + handle.dispatchEvent( new PointerEvent('pointermove', { clientX: 0, cancelable: true, - composed: true, bubbles: true, + composed: true, }) ); - await elementUpdated(el); - await nextFrame(); - handle.dispatchEvent( - new PointerEvent('pointerdown', { + new PointerEvent('pointerup', { clientX: 0, cancelable: true, - button: 0, composed: true, bubbles: true, }) ); - handle.dispatchEvent(new PointerEvent('pointerup')); + await elementUpdated(el); - await new Promise((resolve) => setTimeout(resolve, 300)); + // since we've moved the pointer, the new value should be 0 + expect(el.value).to.equal(0); handle.dispatchEvent( - new PointerEvent('pointerdown', { + new PointerEvent('dblclick', { clientX: 0, cancelable: true, button: 0, @@ -1669,14 +1672,11 @@ describe('Slider', () => { }) ); - handle.dispatchEvent(new PointerEvent('pointerup')); - await elementUpdated(el); - await nextFrame(); expect( el.value, 'reset to default value on double click after moving pointer' - ).to.equal(0.7); + ).to.equal(50); }); }); From 194bba55a6ca293b5172517d65f771c30f1d9e7e Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Wed, 21 Feb 2024 20:41:08 +0530 Subject: [PATCH 11/25] chore(slider): removed unnecessary delay from tests --- packages/slider/test/index.ts | 3 --- packages/slider/test/slider.test.ts | 9 --------- 2 files changed, 12 deletions(-) diff --git a/packages/slider/test/index.ts b/packages/slider/test/index.ts index 4fd73ebfc9..68c3ca0b2e 100644 --- a/packages/slider/test/index.ts +++ b/packages/slider/test/index.ts @@ -295,7 +295,6 @@ export const testEditableSlider = (type: string): void => { pointerType: 'mouse', }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(el.dragging).to.be.false; @@ -311,7 +310,6 @@ export const testEditableSlider = (type: string): void => { pointerType: 'mouse', }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); @@ -343,7 +341,6 @@ export const testEditableSlider = (type: string): void => { pointerType: 'mouse', }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); diff --git a/packages/slider/test/slider.test.ts b/packages/slider/test/slider.test.ts index 1eefe216be..c54bdc4fe5 100644 --- a/packages/slider/test/slider.test.ts +++ b/packages/slider/test/slider.test.ts @@ -154,7 +154,6 @@ describe('Slider', () => { composed: true, }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(el.dragging).to.be.false; @@ -169,7 +168,6 @@ describe('Slider', () => { composed: true, }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(el.dragging, 'it is dragging 1').to.be.true; expect(pointerId, '2').to.equal(1); @@ -197,7 +195,6 @@ describe('Slider', () => { composed: true, }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(el.dragging, 'it is dragging 2').to.be.true; @@ -245,7 +242,6 @@ describe('Slider', () => { cancelable: true, }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(pointerId).to.equal(-1); @@ -262,7 +258,6 @@ describe('Slider', () => { cancelable: true, }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(pointerId).to.equal(4); @@ -324,7 +319,6 @@ describe('Slider', () => { cancelable: true, }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(el.dragging).to.be.false; @@ -342,7 +336,6 @@ describe('Slider', () => { cancelable: true, }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); expect(pointerId).to.equal(-1); @@ -586,7 +579,6 @@ describe('Slider', () => { bubbles: true, }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); await nextFrame(); handle.dispatchEvent( @@ -640,7 +632,6 @@ describe('Slider', () => { bubbles: true, }) ); - await new Promise((resolve) => setTimeout(resolve, 300)); await elementUpdated(el); handle.dispatchEvent( new PointerEvent('pointermove', { From d13658069a4152f362edb2e05053bf820e8f3206 Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Thu, 22 Feb 2024 16:32:14 +0530 Subject: [PATCH 12/25] feat(slider): reset default value on pressing enter --- packages/slider/src/HandleController.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/slider/src/HandleController.ts b/packages/slider/src/HandleController.ts index 03c9a99fcc..f57ff63078 100644 --- a/packages/slider/src/HandleController.ts +++ b/packages/slider/src/HandleController.ts @@ -446,6 +446,14 @@ export class HandleController { }; private onInputKeydown = (event: Event): void => { + if (event.key == 'Enter') { + const input = event.target as InputWithModel; + if (input.model.handle?.defaultValue) { + input.model.handle.value = input.model.handle.defaultValue; + this.dispatchChangeEvent(input, input.model.handle); + this.requestUpdate(); + } + } const input = event.target as InputWithModel; input.model.handle.highlight = true; this.requestUpdate(); From 885367524c683f03aa4f7aa6f263b2e3bfa2c23d Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Thu, 22 Feb 2024 16:39:34 +0530 Subject: [PATCH 13/25] chore(slider): fixed event type --- packages/slider/src/HandleController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/slider/src/HandleController.ts b/packages/slider/src/HandleController.ts index f57ff63078..b47ce5fb24 100644 --- a/packages/slider/src/HandleController.ts +++ b/packages/slider/src/HandleController.ts @@ -445,7 +445,7 @@ export class HandleController { this.requestUpdate(); }; - private onInputKeydown = (event: Event): void => { + private onInputKeydown = (event: KeyboardEvent): void => { if (event.key == 'Enter') { const input = event.target as InputWithModel; if (input.model.handle?.defaultValue) { From 08d021e662c49ca00babbc669468b071f85ae3c7 Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Mon, 26 Feb 2024 14:13:44 +0530 Subject: [PATCH 14/25] feat(slider): added default-value attribute and updated docs --- packages/slider/README.md | 13 +++++++++++++ packages/slider/src/HandleController.ts | 16 +++++++++++++--- packages/slider/src/Slider.ts | 3 --- packages/slider/src/SliderHandle.ts | 8 +++++++- packages/slider/stories/slider.stories.ts | 20 ++++++++++++++++++++ 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/packages/slider/README.md b/packages/slider/README.md index 23381353e7..5eb9b2905c 100644 --- a/packages/slider/README.md +++ b/packages/slider/README.md @@ -232,6 +232,19 @@ An `` element can be paired with an `` element via t ``` +#### Default value + +Slider will reset to its `default-value` when the user double clicks on the slider handle or if the user presses the `escape` key when the slider handle is focused. + +```html + +``` + +Note: If a slider with `default-value` attribute is contained in a modal and the slider-handle is focused then the following interaction will occur on pressing the `escape` key: + +- If the slider value is different from the default value then the slider value will be reset to the default value and the modal will not be closed. +- If the slider value is equal to the default value then the modal will be closed. + #### Indeterminate The indeterminate attribute will be passed to the internal `` element and alter its visual delivery until a change has been made to the `` element at which point the `change` event that is dispatched can be understood as always removing the indeterminate attribute from the ``. diff --git a/packages/slider/src/HandleController.ts b/packages/slider/src/HandleController.ts index b47ce5fb24..152fd276a3 100644 --- a/packages/slider/src/HandleController.ts +++ b/packages/slider/src/HandleController.ts @@ -347,7 +347,7 @@ export class HandleController { */ public handleDoubleClick(event: PointerEvent): void { const { input } = this.extractDataFromEvent(event); - if (input.model.handle?.defaultValue) { + if (input.model?.handle.defaultValue) { input.model.handle.value = input.model.handle.defaultValue; this.dispatchChangeEvent(input, input.model.handle); this.requestUpdate(); @@ -446,13 +446,18 @@ export class HandleController { }; private onInputKeydown = (event: KeyboardEvent): void => { - if (event.key == 'Enter') { + if (event.key == 'Escape') { const input = event.target as InputWithModel; - if (input.model.handle?.defaultValue) { + if ( + input.model.handle?.defaultValue && + input.model.handle.value !== input.model.handle.defaultValue + ) { input.model.handle.value = input.model.handle.defaultValue; this.dispatchChangeEvent(input, input.model.handle); this.requestUpdate(); + event.preventDefault(); } + return; } const input = event.target as InputWithModel; input.model.handle.highlight = true; @@ -543,12 +548,17 @@ export class HandleController { aria-label=${ifDefined(model.ariaLabel)} aria-labelledby=${ariaLabelledBy} aria-valuetext=${this.formattedValueForHandle(model)} + aria-describedby="sliderDesc" @change=${this.onInputChange} @focus=${this.onInputFocus} @blur=${this.onInputBlur} @keydown=${this.onInputKeydown} .model=${model} /> +

+ Press escape or double click to reset the slider to its + default value. +

`; } diff --git a/packages/slider/src/Slider.ts b/packages/slider/src/Slider.ts index 8d03a00720..d5316aadc9 100644 --- a/packages/slider/src/Slider.ts +++ b/packages/slider/src/Slider.ts @@ -557,8 +557,5 @@ export class Slider extends SizedMixin(ObserveSlotText(SliderHandle, ''), { protected override firstUpdated(changes: PropertyValues): void { super.firstUpdated(changes); - if (changes.has('value')) { - this.defaultValue = Number(this.value); - } } } diff --git a/packages/slider/src/SliderHandle.ts b/packages/slider/src/SliderHandle.ts index 2bbc554568..100d567589 100644 --- a/packages/slider/src/SliderHandle.ts +++ b/packages/slider/src/SliderHandle.ts @@ -73,7 +73,6 @@ const MaxConverter = { */ export class SliderHandle extends Focusable { public handleController?: HandleController; - public defaultValue: number | undefined; public get handleName(): string { return this.name; @@ -93,6 +92,13 @@ export class SliderHandle extends Focusable { @property({ type: Number }) value!: number; + /** + * Set the default value of the handle. Setting this property will cause the + * handle to reset to the default value on double click or pressing the `escape` key. + */ + @property({ type: Number, attribute: 'default-value' }) + defaultValue!: number; + @property({ type: Boolean, reflect: true }) public dragging = false; diff --git a/packages/slider/stories/slider.stories.ts b/packages/slider/stories/slider.stories.ts index 76b9b76219..ed592f74fa 100644 --- a/packages/slider/stories/slider.stories.ts +++ b/packages/slider/stories/slider.stories.ts @@ -152,6 +152,26 @@ export const Filled = (args: StoryArgs = {}): TemplateResult => { `; }; +export const HasADefaultValue = (args: StoryArgs = {}): TemplateResult => { + return html` +
+ + double click or press escape key to reset + +
+ `; +}; + export const FillStart = (args: StoryArgs = {}): TemplateResult => { return html`
From a9e2eacdaaef2dbcfe08b5a167595edc7c7b7617 Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Mon, 26 Feb 2024 14:46:13 +0530 Subject: [PATCH 15/25] chore: updated golden image hash --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4c48747633..2f7fc21a33 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ executors: parameters: current_golden_images_hash: type: string - default: 47ca064acfde0ff520b58184ffed1c5756f6069a + default: 08d021e662c49ca00babbc669468b071f85ae3c7 wireit_cache_name: type: string default: wireit From 792484f790feb62cdff8dbbf3e68ed7dcac29e4e Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Mon, 26 Feb 2024 14:49:38 +0530 Subject: [PATCH 16/25] chore(slider): updated tests --- packages/slider/test/slider.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/slider/test/slider.test.ts b/packages/slider/test/slider.test.ts index c54bdc4fe5..faea8c07ec 100644 --- a/packages/slider/test/slider.test.ts +++ b/packages/slider/test/slider.test.ts @@ -1607,7 +1607,11 @@ describe('Slider', () => { let pointerId = -1; const el = await fixture( html` - + ` ); await elementUpdated(el); From 4f8cb68c02fbacf03387d542e94b5ffa0bdfdda5 Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Wed, 28 Feb 2024 11:00:02 +0530 Subject: [PATCH 17/25] chore: updated golden image hash --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2f7fc21a33..18d7947df5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ executors: parameters: current_golden_images_hash: type: string - default: 08d021e662c49ca00babbc669468b071f85ae3c7 + default: ad9dbf0742daf3f998e7324baff9cd047511f5ec wireit_cache_name: type: string default: wireit From 7f52af2c636d72dc3bbd0ea3b8b73a3ce1b9a8ef Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Thu, 29 Feb 2024 12:59:00 +0530 Subject: [PATCH 18/25] chore(slider): added more tests and updated stories --- packages/overlay/stories/overlay.stories.ts | 1 + packages/slider/src/HandleController.ts | 9 +- packages/slider/src/Slider.ts | 4 - packages/slider/stories/slider.stories.ts | 2 +- packages/slider/test/slider.test.ts | 108 +++++++++++++++----- 5 files changed, 89 insertions(+), 35 deletions(-) diff --git a/packages/overlay/stories/overlay.stories.ts b/packages/overlay/stories/overlay.stories.ts index f8a1fd764c..ee68f52f43 100644 --- a/packages/overlay/stories/overlay.stories.ts +++ b/packages/overlay/stories/overlay.stories.ts @@ -173,6 +173,7 @@ const template = ({ min="0" max="20" label="Awesomeness" + default-value="10" >
The background of this div should be blue diff --git a/packages/slider/src/HandleController.ts b/packages/slider/src/HandleController.ts index 152fd276a3..0e06c81fe7 100644 --- a/packages/slider/src/HandleController.ts +++ b/packages/slider/src/HandleController.ts @@ -347,7 +347,7 @@ export class HandleController { */ public handleDoubleClick(event: PointerEvent): void { const { input } = this.extractDataFromEvent(event); - if (input.model?.handle.defaultValue) { + if (input.model?.handle.defaultValue !== undefined) { input.model.handle.value = input.model.handle.defaultValue; this.dispatchChangeEvent(input, input.model.handle); this.requestUpdate(); @@ -449,13 +449,14 @@ export class HandleController { if (event.key == 'Escape') { const input = event.target as InputWithModel; if ( - input.model.handle?.defaultValue && + input.model.handle?.defaultValue !== undefined && input.model.handle.value !== input.model.handle.defaultValue ) { input.model.handle.value = input.model.handle.defaultValue; this.dispatchChangeEvent(input, input.model.handle); this.requestUpdate(); event.preventDefault(); + event.stopPropagation(); } return; } @@ -555,10 +556,10 @@ export class HandleController { @keydown=${this.onInputKeydown} .model=${model} /> -

+

+
`; } diff --git a/packages/slider/src/Slider.ts b/packages/slider/src/Slider.ts index d5316aadc9..817f428f99 100644 --- a/packages/slider/src/Slider.ts +++ b/packages/slider/src/Slider.ts @@ -554,8 +554,4 @@ export class Slider extends SizedMixin(ObserveSlotText(SliderHandle, ''), { } } } - - protected override firstUpdated(changes: PropertyValues): void { - super.firstUpdated(changes); - } } diff --git a/packages/slider/stories/slider.stories.ts b/packages/slider/stories/slider.stories.ts index ed592f74fa..fa3566c7d5 100644 --- a/packages/slider/stories/slider.stories.ts +++ b/packages/slider/stories/slider.stories.ts @@ -67,6 +67,7 @@ export default { variant: undefined, tickStep: 0.1, labelVisibility: undefined, + defaultValue: 0.3, }, }; @@ -160,7 +161,6 @@ export const HasADefaultValue = (args: StoryArgs = {}): TemplateResult => { min="0" value=".5" step="0.01" - default-value="0.2" @input=${handleEvent(args)} @change=${handleEvent(args)} .formatOptions=${{ style: 'percent' }} diff --git a/packages/slider/test/slider.test.ts b/packages/slider/test/slider.test.ts index faea8c07ec..4f742c0c3e 100644 --- a/packages/slider/test/slider.test.ts +++ b/packages/slider/test/slider.test.ts @@ -12,6 +12,9 @@ governing permissions and limitations under the License. import '@spectrum-web-components/slider/sp-slider.js'; import '@spectrum-web-components/slider/sp-slider-handle.js'; +import '@spectrum-web-components/button/sp-button.js'; +import '@spectrum-web-components/overlay/sp-overlay.js'; +import '@spectrum-web-components/popover/sp-popover.js'; import { Slider, SliderHandle } from '@spectrum-web-components/slider'; import { tick } from '../stories/slider.stories.js'; import { @@ -1604,7 +1607,6 @@ describe('Slider', () => { expect(el.values).to.deep.equal({ a: 10, b: 20, c: 29 }); }); it('resets to default value on double click after moving pointer', async () => { - let pointerId = -1; const el = await fixture( html` { ); await elementUpdated(el); expect(el.value, 'initial').to.equal(50); - expect(pointerId).to.equal(-1); - const handle = el.shadowRoot.querySelector('.handle') as HTMLDivElement; - el.track.setPointerCapture = (id: number) => (pointerId = id); - el.track.releasePointerCapture = (id: number) => (pointerId = id); + const handle = el.shadowRoot.querySelector('.handle') as HTMLDivElement; + const handleBoundingRect = handle.getBoundingClientRect(); + const position: [number, number] = [ + handleBoundingRect.x + handleBoundingRect.width / 2, + handleBoundingRect.y + handleBoundingRect.height / 2, + ]; await sendMouse({ steps: [ { type: 'move', - position: [9, 30], + position, }, { type: 'down', }, ], }); - await elementUpdated(el); - handle.dispatchEvent( - new PointerEvent('pointermove', { - clientX: 0, - cancelable: true, - bubbles: true, - composed: true, - }) - ); - - handle.dispatchEvent( - new PointerEvent('pointerup', { - clientX: 0, - cancelable: true, - composed: true, - bubbles: true, - }) - ); + await elementUpdated(el); + await sendMouse({ + steps: [ + { + type: 'move', + position: [ + 150, + handleBoundingRect.y + handleBoundingRect.height + 100, + ], + }, + { + type: 'up', + }, + ], + }); await elementUpdated(el); - // since we've moved the pointer, the new value should be 0 - expect(el.value).to.equal(0); + // since we've moved the pointer, the new value should be 100 + expect(el.value).to.equal(100); handle.dispatchEvent( new PointerEvent('dblclick', { @@ -1674,4 +1675,59 @@ describe('Slider', () => { 'reset to default value on double click after moving pointer' ).to.equal(50); }); + it('manages escape key interactions correctly in an overlaid context', async () => { + const el = await fixture( + html` +
+ Overlay Trigger + + + + + +
+ ` + ); + + await elementUpdated(el); + + // open the overlay + const trigger = el.querySelector('#trigger') as HTMLButtonElement; + trigger.click(); + + await elementUpdated(el); + + // current slider value should be 70 + const slider = el.querySelector('sp-slider') as Slider; + expect(slider.value).to.equal(70); + + slider.focus(); + // send escape key + await sendKeys({ + press: 'Escape', + }); + + await elementUpdated(el); + + // now the slider value should be 50 + expect(slider.value).to.equal(50); + + // and the overlay should be in open state + const overlay = el.querySelector('sp-overlay') as Overlay; + expect(overlay.open).to.be.true; + + // send escape key again + await sendKeys({ + press: 'Escape', + }); + + await elementUpdated(el); + + // now the overlay should be closed + expect(overlay.open).to.be.false; + }); }); From 949cc013de1f8357b76a0f1a5150d538c99e5b0b Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Thu, 29 Feb 2024 14:49:13 +0530 Subject: [PATCH 19/25] feat(slider): reset to default in editable sliders --- packages/slider/src/HandleController.ts | 5 +++- packages/slider/stories/slider.stories.ts | 33 ++++++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/slider/src/HandleController.ts b/packages/slider/src/HandleController.ts index 0e06c81fe7..4386383900 100644 --- a/packages/slider/src/HandleController.ts +++ b/packages/slider/src/HandleController.ts @@ -346,7 +346,10 @@ export class HandleController { * @param event */ public handleDoubleClick(event: PointerEvent): void { - const { input } = this.extractDataFromEvent(event); + const input = (event.target as Element).querySelector( + '.input' + ) as InputWithModel; + if (input.model?.handle.defaultValue !== undefined) { input.model.handle.value = input.model.handle.defaultValue; this.dispatchChangeEvent(input, input.model.handle); diff --git a/packages/slider/stories/slider.stories.ts b/packages/slider/stories/slider.stories.ts index fa3566c7d5..1629652b12 100644 --- a/packages/slider/stories/slider.stories.ts +++ b/packages/slider/stories/slider.stories.ts @@ -67,7 +67,6 @@ export default { variant: undefined, tickStep: 0.1, labelVisibility: undefined, - defaultValue: 0.3, }, }; @@ -161,6 +160,7 @@ export const HasADefaultValue = (args: StoryArgs = {}): TemplateResult => { min="0" value=".5" step="0.01" + default-value="0.2" @input=${handleEvent(args)} @change=${handleEvent(args)} .formatOptions=${{ style: 'percent' }} @@ -417,6 +417,37 @@ export const editable = (args: StoryArgs = {}): TemplateResult => { editable.decorators = [editableDecorator]; +export const editableWithDefaultValue = ( + args: StoryArgs = {} +): TemplateResult => { + return html` +
+ + Angle + +
+ `; +}; + +editableWithDefaultValue.swc_vrt = { + skip: true, +}; + export const editableDisabled = (args: StoryArgs = {}): TemplateResult => { return html`
From 2c97a76d7b2f5007ab4fd4efbdb183531921001a Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Thu, 29 Feb 2024 14:56:35 +0530 Subject: [PATCH 20/25] chore(slider): added missing type in slider tests --- packages/slider/test/slider.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/slider/test/slider.test.ts b/packages/slider/test/slider.test.ts index 4f742c0c3e..9cd81dadf1 100644 --- a/packages/slider/test/slider.test.ts +++ b/packages/slider/test/slider.test.ts @@ -15,6 +15,7 @@ import '@spectrum-web-components/slider/sp-slider-handle.js'; import '@spectrum-web-components/button/sp-button.js'; import '@spectrum-web-components/overlay/sp-overlay.js'; import '@spectrum-web-components/popover/sp-popover.js'; +import { Overlay } from '@spectrum-web-components/overlay'; import { Slider, SliderHandle } from '@spectrum-web-components/slider'; import { tick } from '../stories/slider.stories.js'; import { From fa57fcc84560978d648a7c30bdf22401d628033a Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Fri, 1 Mar 2024 17:38:46 +0530 Subject: [PATCH 21/25] chore: update golden image hash --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 18d7947df5..2f19fc0f13 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ executors: parameters: current_golden_images_hash: type: string - default: ad9dbf0742daf3f998e7324baff9cd047511f5ec + default: a63f4602db4236d25b3401ef7761f68d588e2b28 wireit_cache_name: type: string default: wireit From d1bb96778c20b1a9cac0c8c0b9f78cdfd90eeff9 Mon Sep 17 00:00:00 2001 From: TarunAdobe Date: Fri, 1 Mar 2024 20:07:57 +0530 Subject: [PATCH 22/25] chore(slider): migrate inline css to a separate file --- packages/slider/src/HandleController.ts | 4 ++-- packages/slider/src/slider.css | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/slider/src/HandleController.ts b/packages/slider/src/HandleController.ts index 4386383900..d8e05d0de5 100644 --- a/packages/slider/src/HandleController.ts +++ b/packages/slider/src/HandleController.ts @@ -552,14 +552,14 @@ export class HandleController { aria-label=${ifDefined(model.ariaLabel)} aria-labelledby=${ariaLabelledBy} aria-valuetext=${this.formattedValueForHandle(model)} - aria-describedby="sliderDesc" + aria-describedby="slider-description" @change=${this.onInputChange} @focus=${this.onInputFocus} @blur=${this.onInputBlur} @keydown=${this.onInputKeydown} .model=${model} /> -