diff --git a/src/utility/selectOptions.ts b/src/utility/selectOptions.ts index 23d06eec..5e5b2c33 100644 --- a/src/utility/selectOptions.ts +++ b/src/utility/selectOptions.ts @@ -60,6 +60,11 @@ async function selectOptionsBase( if (isDisabled(select) || !selectedOptions.length) return const selectOption = (option: HTMLOptionElement) => { + // The browser does not fire `input` or `change` + // if the selectedness of the option does not change. + if (option.selected === newValue) { + return + } option.selected = newValue this.dispatchUIEvent(select, 'input', { bubbles: true, diff --git a/tests/utility/selectOptions/deselect.ts b/tests/utility/selectOptions/deselect.ts index ef385758..1cbea828 100644 --- a/tests/utility/selectOptions/deselect.ts +++ b/tests/utility/selectOptions/deselect.ts @@ -56,12 +56,22 @@ test('blurs previously focused element', async () => { select[name="select"][value=[]] - focusin: ← button option[value="1"][selected=false] - pointerup option[value="1"][selected=false] - mouseup: primary - select[name="select"][value=[]] - input - select[name="select"][value=[]] - change option[value="1"][selected=false] - click: primary `) }) +test('does not fire input/change when the option is not selected', async () => { + const {select, options, eventWasFired, user} = setupSelect({multiple: true}) + options[1].selected = true + + await user.deselectOptions(select, '1') + + expect(eventWasFired('click')).toBe(true) + expect(eventWasFired('input')).toBe(false) + expect(eventWasFired('change')).toBe(false) + expect(options.map(o => o.selected)).toEqual([false, true, false]) +}) + test('toggle options as expected', async () => { const {element, user} = setup(`