diff --git a/projects/forms/src/internal/controllers/state-active.controller.test.ts b/projects/forms/src/internal/controllers/state-active.controller.test.ts index be441da6f..7d3dd92bd 100644 --- a/projects/forms/src/internal/controllers/state-active.controller.test.ts +++ b/projects/forms/src/internal/controllers/state-active.controller.test.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { userEvent } from 'vitest/browser'; import { html } from 'lit'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { createFixture, removeFixture } from '@internals/testing'; @@ -57,13 +58,13 @@ describe('StateActiveController', () => { element.dispatchEvent(new MouseEvent('mouseup')); expect(element.matches(':state(active)')).toBe(false); - element.dispatchEvent(new KeyboardEvent('keypress', { code: 'Space' })); + element.dispatchEvent(new KeyboardEvent('keydown', { code: 'Space' })); expect(element.matches(':state(active)')).toBe(true); element.dispatchEvent(new KeyboardEvent('keyup')); expect(element.matches(':state(active)')).toBe(false); - element.dispatchEvent(new KeyboardEvent('keypress', { code: 'Enter' })); + element.dispatchEvent(new KeyboardEvent('keydown', { code: 'Enter' })); expect(element.matches(':state(active)')).toBe(true); element.dispatchEvent(new FocusEvent('blur')); @@ -76,13 +77,13 @@ describe('StateActiveController', () => { expect(element.matches(':state(active)')).toBe(false); element.disabled = false; - element.dispatchEvent(new KeyboardEvent('keypress', { code: 'KeyA' })); + element.dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyA' })); expect(element.matches(':state(active)')).toBe(false); }); - it('should prevent default space keypress scrolling on the host', () => { - const space = new KeyboardEvent('keypress', { code: 'Space', cancelable: true }); - const enter = new KeyboardEvent('keypress', { code: 'Enter', cancelable: true }); + it('should prevent default on space keydown activation', () => { + const space = new KeyboardEvent('keydown', { code: 'Space', cancelable: true }); + const enter = new KeyboardEvent('keydown', { code: 'Enter', cancelable: true }); element.dispatchEvent(space); element.dispatchEvent(enter); @@ -91,6 +92,21 @@ describe('StateActiveController', () => { expect(enter.defaultPrevented).toBe(false); }); + it('should hold the active state for a real space or enter key', async () => { + element.tabIndex = 0; + element.focus(); + + await userEvent.keyboard('{Space>}'); + expect(element.matches(':state(active)')).toBe(true); + await userEvent.keyboard('{/Space}'); + expect(element.matches(':state(active)')).toBe(false); + + await userEvent.keyboard('{Enter>}'); + expect(element.matches(':state(active)')).toBe(true); + await userEvent.keyboard('{/Enter}'); + expect(element.matches(':state(active)')).toBe(false); + }); + it('should remove active listeners on disconnect', () => { element.remove(); element.dispatchEvent(new MouseEvent('mousedown')); diff --git a/projects/forms/src/internal/controllers/state-active.controller.ts b/projects/forms/src/internal/controllers/state-active.controller.ts index d14c89003..c7c428994 100644 --- a/projects/forms/src/internal/controllers/state-active.controller.ts +++ b/projects/forms/src/internal/controllers/state-active.controller.ts @@ -13,7 +13,7 @@ export class StateActiveController implements ReactiveControll hostConnected() { attachInternals(this.host); - this.host.addEventListener('keypress', this.#onActive as EventListener); + this.host.addEventListener('keydown', this.#onActive as EventListener); this.host.addEventListener('mousedown', this.#onActive as EventListener); this.host.addEventListener('keyup', this.#onInactive); this.host.addEventListener('blur', this.#onInactive); @@ -21,7 +21,7 @@ export class StateActiveController implements ReactiveControll } hostDisconnected() { - this.host.removeEventListener('keypress', this.#onActive as EventListener); + this.host.removeEventListener('keydown', this.#onActive as EventListener); this.host.removeEventListener('mousedown', this.#onActive as EventListener); this.host.removeEventListener('keyup', this.#onInactive); this.host.removeEventListener('blur', this.#onInactive); diff --git a/projects/forms/src/mixins/button.test.ts b/projects/forms/src/mixins/button.test.ts index 4b603def3..55fba41a6 100644 --- a/projects/forms/src/mixins/button.test.ts +++ b/projects/forms/src/mixins/button.test.ts @@ -91,24 +91,24 @@ describe('ButtonFormControlMixin', () => { button.dispatchEvent(new MouseEvent('mouseup')); expect(button.matches(':state(active)')).toBe(false); - button.dispatchEvent(new KeyboardEvent('keypress', { code: 'Space' })); + button.dispatchEvent(new KeyboardEvent('keydown', { code: 'Space' })); expect(button.matches(':state(active)')).toBe(true); }); it('should clear active state on blur and ignore non-activation keys', () => { - button.dispatchEvent(new KeyboardEvent('keypress', { code: 'KeyA' })); + button.dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyA' })); expect(button.matches(':state(active)')).toBe(false); - button.dispatchEvent(new KeyboardEvent('keypress', { code: 'Enter' })); + button.dispatchEvent(new KeyboardEvent('keydown', { code: 'Enter' })); expect(button.matches(':state(active)')).toBe(true); button.dispatchEvent(new FocusEvent('blur')); expect(button.matches(':state(active)')).toBe(false); }); - it('should prevent default on space keypress activation', () => { - const space = new KeyboardEvent('keypress', { code: 'Space', cancelable: true }); - const enter = new KeyboardEvent('keypress', { code: 'Enter', cancelable: true }); + it('should prevent default on space keydown activation', () => { + const space = new KeyboardEvent('keydown', { code: 'Space', cancelable: true }); + const enter = new KeyboardEvent('keydown', { code: 'Enter', cancelable: true }); button.dispatchEvent(space); button.dispatchEvent(enter);