diff --git a/docs/pages/resources/changelog.md b/docs/pages/resources/changelog.md index a92ea1a06b..e1b8bc871d 100644 --- a/docs/pages/resources/changelog.md +++ b/docs/pages/resources/changelog.md @@ -12,6 +12,11 @@ Components with the Experimental bad New versions of Shoelace are released as-needed and generally occur when a critical mass of changes have accumulated. At any time, you can see what's coming in the next release by visiting [next.shoelace.style](https://next.shoelace.style). +## Next + +- Fixed a bug with bundled components using CDN builds not having translations on initial connect [#1696] +- Fixed a bug where the `"sl-change"` event would always fire simultaneously with `"sl-input"` event in ``. The `` event now only fires when a user stops dragging a slider or stops dragging on the color canvas. [#1689] + ## 2.11.2 - Fixed a bug in `` component that caused an error to be thrown when rendered with Lit [#1684] diff --git a/src/components/color-picker/color-picker.component.ts b/src/components/color-picker/color-picker.component.ts index 96b5184cfc..3ed5f1c471 100644 --- a/src/components/color-picker/color-picker.component.ts +++ b/src/components/color-picker/color-picker.component.ts @@ -243,7 +243,8 @@ export default class SlColorPicker extends ShoelaceElement implements ShoelaceFo const container = this.shadowRoot!.querySelector('.color-picker__slider.color-picker__alpha')!; const handle = container.querySelector('.color-picker__slider-handle')!; const { width } = container.getBoundingClientRect(); - let oldValue = this.value; + let initialValue = this.value; + let currentValue = this.value; handle.focus(); event.preventDefault(); @@ -253,12 +254,17 @@ export default class SlColorPicker extends ShoelaceElement implements ShoelaceFo this.alpha = clamp((x / width) * 100, 0, 100); this.syncValues(); - if (this.value !== oldValue) { - oldValue = this.value; - this.emit('sl-change'); + if (this.value !== currentValue) { + currentValue = this.value; this.emit('sl-input'); } }, + onStop: () => { + if (this.value !== initialValue) { + initialValue = this.value; + this.emit('sl-change'); + } + }, initialEvent: event }); } @@ -267,7 +273,8 @@ export default class SlColorPicker extends ShoelaceElement implements ShoelaceFo const container = this.shadowRoot!.querySelector('.color-picker__slider.color-picker__hue')!; const handle = container.querySelector('.color-picker__slider-handle')!; const { width } = container.getBoundingClientRect(); - let oldValue = this.value; + let initialValue = this.value; + let currentValue = this.value; handle.focus(); event.preventDefault(); @@ -277,12 +284,17 @@ export default class SlColorPicker extends ShoelaceElement implements ShoelaceFo this.hue = clamp((x / width) * 360, 0, 360); this.syncValues(); - if (this.value !== oldValue) { - oldValue = this.value; - this.emit('sl-change'); + if (this.value !== currentValue) { + currentValue = this.value; this.emit('sl-input'); } }, + onStop: () => { + if (this.value !== initialValue) { + initialValue = this.value; + this.emit('sl-change'); + } + }, initialEvent: event }); } @@ -291,7 +303,8 @@ export default class SlColorPicker extends ShoelaceElement implements ShoelaceFo const grid = this.shadowRoot!.querySelector('.color-picker__grid')!; const handle = grid.querySelector('.color-picker__grid-handle')!; const { width, height } = grid.getBoundingClientRect(); - let oldValue = this.value; + let initialValue = this.value; + let currentValue = this.value; handle.focus(); event.preventDefault(); @@ -304,13 +317,18 @@ export default class SlColorPicker extends ShoelaceElement implements ShoelaceFo this.brightness = clamp(100 - (y / height) * 100, 0, 100); this.syncValues(); - if (this.value !== oldValue) { - oldValue = this.value; - this.emit('sl-change'); + if (this.value !== currentValue) { + currentValue = this.value; this.emit('sl-input'); } }, - onStop: () => (this.isDraggingGridHandle = false), + onStop: () => { + this.isDraggingGridHandle = false; + if (this.value !== initialValue) { + initialValue = this.value; + this.emit('sl-change'); + } + }, initialEvent: event }); } diff --git a/src/components/color-picker/color-picker.test.ts b/src/components/color-picker/color-picker.test.ts index a05f7f137d..92249c96c4 100644 --- a/src/components/color-picker/color-picker.test.ts +++ b/src/components/color-picker/color-picker.test.ts @@ -1,6 +1,6 @@ import '../../../dist/shoelace.js'; import { aTimeout, expect, fixture, html, oneEvent } from '@open-wc/testing'; -import { clickOnElement } from '../../internal/test.js'; +import { clickOnElement, dragElement } from '../../internal/test.js'; import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests.js'; import { sendKeys } from '@web/test-runner-commands'; import { serialize } from '../../utilities/form.js'; @@ -31,11 +31,22 @@ describe('', () => { await clickOnElement(trigger); // open the dropdown await aTimeout(200); // wait for the dropdown to open - await clickOnElement(grid); // click on the grid + + // Simulate a drag event. "sl-change" should not fire until we stop dragging. + await dragElement(grid, 2, 0, { + afterMouseDown: () => { + expect(changeHandler).to.have.not.been.called; + expect(inputHandler).to.have.been.calledOnce; + }, + afterMouseMove: () => { + expect(inputHandler).to.have.been.calledTwice; + } + }); + await el.updateComplete; expect(changeHandler).to.have.been.calledOnce; - expect(inputHandler).to.have.been.calledOnce; + expect(inputHandler).to.have.been.calledTwice; }); it('should emit sl-change and sl-input when the hue slider is moved', async () => { @@ -50,10 +61,22 @@ describe('', () => { await clickOnElement(trigger); // open the dropdown await aTimeout(200); // wait for the dropdown to open - await clickOnElement(slider); // click on the hue slider + // Simulate a drag event. "sl-change" should not fire until we stop dragging. + await dragElement(slider, 20, 0, { + afterMouseDown: () => { + expect(changeHandler).to.have.not.been.called; + expect(inputHandler).to.have.been.calledOnce; + }, + afterMouseMove: () => { + // It's not twice because you can't change the hue of white! + expect(inputHandler).to.have.been.calledOnce; + } + }); + await el.updateComplete; expect(changeHandler).to.have.been.calledOnce; + // It's not twice because you can't change the hue of white! expect(inputHandler).to.have.been.calledOnce; }); @@ -69,11 +92,22 @@ describe('', () => { await clickOnElement(trigger); // open the dropdown await aTimeout(200); // wait for the dropdown to open - await clickOnElement(slider); // click on the opacity slider + + // Simulate a drag event. "sl-change" should not fire until we stop dragging. + await dragElement(slider, 2, 0, { + afterMouseDown: () => { + expect(changeHandler).to.have.not.been.called; + expect(inputHandler).to.have.been.calledOnce; + }, + afterMouseMove: () => { + expect(inputHandler).to.have.been.calledTwice; + } + }); + await el.updateComplete; expect(changeHandler).to.have.been.calledOnce; - expect(inputHandler).to.have.been.calledOnce; + expect(inputHandler).to.have.been.calledTwice; }); it('should emit sl-change and sl-input when toggling the format', async () => { diff --git a/src/internal/test.ts b/src/internal/test.ts index 607753fcdb..050f5efdef 100644 --- a/src/internal/test.ts +++ b/src/internal/test.ts @@ -73,11 +73,21 @@ export async function dragElement( /** The horizontal distance to drag in pixels */ deltaX = 0, /** The vertical distance to drag in pixels */ - deltaY = 0 + deltaY = 0, + callbacks: { + afterMouseDown?: () => void | Promise; + afterMouseMove?: () => void | Promise; + } = {} ): Promise { await moveMouseOnElement(el); await sendMouse({ type: 'down' }); + + await callbacks.afterMouseDown?.(); + const { clickX, clickY } = determineMousePosition(el, 'center', deltaX, deltaY); await sendMouse({ type: 'move', position: [clickX, clickY] }); + + await callbacks.afterMouseMove?.(); + await sendMouse({ type: 'up' }); }