Skip to content

Commit 99ec9e2

Browse files
asyncLizcopybara-github
authored andcommitted
fix(switch): pressing enter toggles the switch
PiperOrigin-RevId: 638508692
1 parent dc2ba2a commit 99ec9e2

File tree

2 files changed

+101
-6
lines changed

2 files changed

+101
-6
lines changed

switch/internal/switch.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import {property, query} from 'lit/decorators.js';
1212
import {ClassInfo, classMap} from 'lit/directives/class-map.js';
1313

1414
import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
15+
import {
16+
afterDispatch,
17+
setupDispatchHooks,
18+
} from '../../internal/events/dispatch-hooks.js';
1519
import {
1620
dispatchActivationClick,
1721
isActivationClick,
@@ -88,15 +92,33 @@ export class Switch extends switchBaseClass {
8892

8993
constructor() {
9094
super();
91-
if (!isServer) {
92-
this.addEventListener('click', (event: MouseEvent) => {
93-
if (!isActivationClick(event) || !this.input) {
95+
if (isServer) {
96+
return;
97+
}
98+
99+
// This click listener does not currently need dispatch hooks since it does
100+
// not check `event.defaultPrevented`.
101+
this.addEventListener('click', (event: MouseEvent) => {
102+
if (!isActivationClick(event) || !this.input) {
103+
return;
104+
}
105+
this.focus();
106+
dispatchActivationClick(this.input);
107+
});
108+
109+
// Add the aria keyboard interaction pattern for switch and the Enter key.
110+
// See https://www.w3.org/WAI/ARIA/apg/patterns/switch/.
111+
setupDispatchHooks(this, 'keydown');
112+
this.addEventListener('keydown', (event: KeyboardEvent) => {
113+
afterDispatch(event, () => {
114+
const ignoreEvent = event.defaultPrevented || event.key !== 'Enter';
115+
if (ignoreEvent || this.disabled || !this.input) {
94116
return;
95117
}
96-
this.focus();
97-
dispatchActivationClick(this.input);
118+
119+
this.input.click();
98120
});
99-
}
121+
});
100122
}
101123

102124
protected override render(): TemplateResult {

switch/switch_test.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88

99
import {html} from 'lit';
1010

11+
import {Environment} from '../testing/environment.js';
1112
import {createFormTests} from '../testing/forms.js';
1213
import {createTokenTests} from '../testing/tokens.js';
1314

15+
import {SwitchHarness} from './harness.js';
1416
import {MdSwitch} from './switch.js';
1517

1618
describe('<md-switch>', () => {
19+
const env = new Environment();
20+
1721
describe('.styles', () => {
1822
createTokenTests(MdSwitch.styles);
1923
});
@@ -117,4 +121,73 @@ describe('<md-switch>', () => {
117121
],
118122
});
119123
});
124+
125+
describe('enter key activation', () => {
126+
// Don't use harness.clickWithKeyboard() since it simulates a click event.
127+
// The underlying `<input type="checkbox">` will not dispatch click events
128+
// in response to the Enter key.
129+
130+
it('should toggle the switch on', async () => {
131+
// Arrange
132+
const root = env.render(html`<md-switch></md-switch>`);
133+
const harness = new SwitchHarness(root.querySelector('md-switch')!);
134+
135+
// Act
136+
await harness.keypress('Enter');
137+
await harness.element.updateComplete;
138+
139+
// Assert
140+
expect(harness.element.selected)
141+
.withContext('switch is selected after Enter')
142+
.toBeTrue();
143+
});
144+
145+
it('should toggle the switch off', async () => {
146+
// Arrange
147+
const root = env.render(html`<md-switch selected></md-switch>`);
148+
const harness = new SwitchHarness(root.querySelector('md-switch')!);
149+
150+
// Act
151+
await harness.keypress('Enter');
152+
await harness.element.updateComplete;
153+
154+
// Assert
155+
expect(harness.element.selected)
156+
.withContext('switch is unselected after Enter')
157+
.toBeFalse();
158+
});
159+
160+
it('should not toggle the switch when disabled', async () => {
161+
// Arrange
162+
const root = env.render(html`<md-switch disabled></md-switch>`);
163+
const harness = new SwitchHarness(root.querySelector('md-switch')!);
164+
165+
// Act
166+
await harness.keypress('Enter');
167+
await harness.element.updateComplete;
168+
169+
// Assert
170+
expect(harness.element.selected)
171+
.withContext('disabled switch is not selected after Enter')
172+
.toBeFalse();
173+
});
174+
175+
it('should not toggle the switch when keydown event is canceled', async () => {
176+
// Arrange
177+
const root = env.render(html`<md-switch></md-switch>`);
178+
const harness = new SwitchHarness(root.querySelector('md-switch')!);
179+
harness.element.addEventListener('keydown', (event) => {
180+
event.preventDefault();
181+
});
182+
183+
// Act
184+
await harness.keypress('Enter');
185+
await harness.element.updateComplete;
186+
187+
// Assert
188+
expect(harness.element.selected)
189+
.withContext('switch is not selected when Enter is canceled')
190+
.toBeFalse();
191+
});
192+
});
120193
});

0 commit comments

Comments
 (0)