make sure relayNativeEvent is synchronous

This commit is contained in:
konnorrogers
2025-06-12 19:00:31 -04:00
parent b99d7771dc
commit 45ea974ca6
10 changed files with 129 additions and 55 deletions

View File

@@ -132,7 +132,9 @@ export default class WaCheckbox extends WebAwesomeFormAssociatedElement {
this.hasInteracted = true;
this.checked = !this.checked;
this.indeterminate = false;
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
@watch('defaultChecked')

View File

@@ -300,6 +300,7 @@ describe('<wa-color-picker>', () => {
await sendKeys({ type: 'fc0' }); // type in a color
input.blur(); // commit changes by blurring the field
await el.updateComplete;
await aTimeout(1)
expect(changeHandler).to.have.been.calledOnce;
expect(inputHandler).to.have.been.calledOnce;

View File

@@ -275,8 +275,11 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
const nextIndex = (formats.indexOf(this.format) + 1) % formats.length;
this.format = formats[nextIndex] as 'hex' | 'rgb' | 'hsl' | 'hsv';
this.setColor(this.value || '');
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.dispatchEvent(new InputEvent('input'));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
})
}
private handleAlphaDrag(event: PointerEvent) {
@@ -296,13 +299,18 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
if (this.value !== currentValue) {
currentValue = this.value;
this.dispatchEvent(new InputEvent('input'));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
})
}
},
onStop: () => {
if (this.value !== initialValue) {
initialValue = this.value;
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
},
initialEvent: event,
@@ -326,13 +334,17 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
if (this.value !== currentValue) {
currentValue = this.value;
this.dispatchEvent(new InputEvent('input'));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input'));
})
}
},
onStop: () => {
if (this.value !== initialValue) {
initialValue = this.value;
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
},
initialEvent: event,
@@ -359,14 +371,18 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
if (this.value !== currentValue) {
currentValue = this.value;
this.dispatchEvent(new InputEvent('input'));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
})
}
},
onStop: () => {
this.isDraggingGridHandle = false;
if (this.value !== initialValue) {
initialValue = this.value;
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
},
initialEvent: event,
@@ -402,8 +418,10 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
}
@@ -436,8 +454,10 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
}
@@ -470,8 +490,10 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
}
@@ -490,8 +512,10 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
}
@@ -511,8 +535,10 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
this.input.value = this.value;
if (this.value !== oldValue) {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
setTimeout(() => this.input.select());
@@ -696,8 +722,10 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
this.setColor(colorSelectionResult.sRGBHex);
if (this.value !== oldValue) {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
})
.catch(() => {
@@ -712,8 +740,10 @@ export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
this.setColor(color);
if (this.value !== oldValue) {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
}
}

View File

@@ -223,8 +223,9 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
@property({ attribute: 'with-hint', type: Boolean }) withHint = false;
private handleChange(event: Event) {
this.relayNativeEvent(event, { bubbles: true, composed: true });
this.value = this.input.value;
this.relayNativeEvent(event, { bubbles: true, composed: true });
}
private handleClearClick(event: MouseEvent) {
@@ -232,9 +233,12 @@ export default class WaInput extends WebAwesomeFormAssociatedElement {
if (this.value !== '') {
this.value = '';
this.dispatchEvent(new WaClearEvent());
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new WaClearEvent());
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
this.input.focus();

View File

@@ -171,8 +171,10 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
};
@@ -274,8 +276,10 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
}
if (this.value !== oldValue) {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
event.preventDefault();

View File

@@ -101,7 +101,9 @@ export default class WaRating extends WebAwesomeElement {
}
this.setValue(this.getValueFromMousePosition(event));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
private setValue(newValue: number) {
@@ -145,7 +147,9 @@ export default class WaRating extends WebAwesomeElement {
}
if (this.value !== oldValue) {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
}
@@ -178,7 +182,9 @@ export default class WaRating extends WebAwesomeElement {
private handleTouchEnd(event: TouchEvent) {
this.isHovering = false;
this.setValue(this.hoverValue);
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
// Prevent click on mobile devices
event.preventDefault();

View File

@@ -381,7 +381,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
// Emit after updating
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
});
@@ -511,7 +511,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
// Emit after update
this.updateComplete.then(() => {
this.dispatchEvent(new WaClearEvent());
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
});
}
@@ -542,7 +542,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
if (this.value !== oldValue) {
// Emit after updating
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
});
}
@@ -600,7 +600,7 @@ export default class WaSelect extends WebAwesomeFormAssociatedElement {
// Emit after updating
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input'));
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
});
}

View File

@@ -228,7 +228,9 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
},
stop: () => {
if (this.minValue !== this.valueWhenDraggingStarted) {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
this.hasInteracted = true;
}
this.hideRangeTooltips();
@@ -251,7 +253,9 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
},
stop: () => {
if (this.maxValue !== this.valueWhenDraggingStarted) {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
this.hasInteracted = true;
}
this.hideRangeTooltips();
@@ -321,7 +325,9 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
if (this.activeThumb) {
const currentValue = this.activeThumb === 'min' ? this.minValue : this.maxValue;
if (currentValue !== this.valueWhenDraggingStarted) {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
this.hasInteracted = true;
}
}
@@ -346,7 +352,10 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
},
stop: () => {
if (this.value !== this.valueWhenDraggingStarted) {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
this.hasInteracted = true;
}
this.hideTooltip();
@@ -476,7 +485,9 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
this.hideTooltip();
}
this.customStates.set('focused', false);
this.dispatchEvent(new FocusEvent('blur', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new FocusEvent('blur', { bubbles: true, composed: true }));
})
}
private handleFocus(event: FocusEvent) {
@@ -495,7 +506,9 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
}
this.customStates.set('focused', true);
this.dispatchEvent(new FocusEvent('focus', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new FocusEvent('focus', { bubbles: true, composed: true }));
})
}
private handleKeyDown(event: KeyboardEvent) {
@@ -602,8 +615,10 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
}
// Dispatch events
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
this.hasInteracted = true;
}
@@ -625,7 +640,9 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
// Dispatch input events when the value changes by dragging
if (this.value !== oldValue) {
this.dispatchEvent(new InputEvent('input'));
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
})
}
}
@@ -658,8 +675,10 @@ export default class WaSlider extends WebAwesomeFormAssociatedElement {
// Dispatch input events
if (oldValue !== (thumb === 'min' ? this.minValue : this.maxValue)) {
this.dispatchEvent(new InputEvent('input'));
this.updateFormValue();
this.updateComplete.then(() => {
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
})
}
}

View File

@@ -117,22 +117,29 @@ export default class WaSwitch extends WebAwesomeFormAssociatedElement {
private handleClick() {
this.hasInteracted = true;
this.checked = !this.checked;
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
})
}
private handleKeyDown(event: KeyboardEvent) {
if (event.key === 'ArrowLeft') {
event.preventDefault();
this.checked = false;
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.dispatchEvent(new InputEvent('input'));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
})
}
if (event.key === 'ArrowRight') {
event.preventDefault();
this.checked = true;
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.dispatchEvent(new InputEvent('input'));
this.updateComplete.then(() => {
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
this.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }));
})
}
}

View File

@@ -206,13 +206,14 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
this.valueHasChanged = true;
this.value = this.input.value;
this.setTextareaDimensions();
this.relayNativeEvent(event, { bubbles: true, composed: true });
this.checkValidity();
this.relayNativeEvent(event, { bubbles: true, composed: true });
}
private handleInput() {
private handleInput(event: InputEvent) {
this.valueHasChanged = true;
this.value = this.input.value;
this.relayNativeEvent(event, { bubbles: true, composed: true });
}
private setTextareaDimensions() {