Skip to content

Commit a089cb1

Browse files
loivseniOvergaard
andauthored
feat: Form elements submits the form on enter (#288)
* submit form on enter * ops. console log remove * binding formElement to this in onkeypress method * submit on enter * submit on enter for checkbox * radio and slider submit on enter * remove old eventlistener * replace submit with requestSubmit to mimic normal form submit behaviour * add addEventListener to all instances regardless of type to allow for dynamic type checking * add tests to check if uui-input correctly submits the form * add tests to see if relevant form elements submits correctly when pressing the 'Enter' key Co-authored-by: Jacob Overgaard <[email protected]>
1 parent 5e5b73e commit a089cb1

File tree

10 files changed

+133
-25
lines changed

10 files changed

+133
-25
lines changed

packages/uui-base/lib/mixins/FormControlMixin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export declare abstract class FormControlMixinInterface extends LitElement {
1818
get validationMessage(): string;
1919
get validity(): ValidityState;
2020
public setCustomValidity(error: string): void;
21+
public submit(): void;
2122
protected _value: FormDataEntryValue | FormData;
2223
protected _internals: any;
2324
protected abstract getFormElement(): HTMLElement | undefined;
@@ -325,6 +326,10 @@ export const FormControlMixin = <T extends Constructor<LitElement>>(
325326
this.pristine = false;
326327
};
327328

329+
public submit() {
330+
this._form?.requestSubmit();
331+
}
332+
328333
public formAssociatedCallback() {
329334
this._removeFormListeners();
330335
this._form = this._internals.form;

packages/uui-boolean-input/lib/uui-boolean-input.element.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,19 @@ export abstract class UUIBooleanInputElement extends FormControlMixin(
134134
this._value = 'on';
135135
}
136136
this.inputRole = inputRole;
137+
this.addEventListener('keypress', this._onKeypress);
137138
}
138139

139140
protected getFormElement(): HTMLElement {
140141
return this._input;
141142
}
142143

144+
private _onKeypress(e: KeyboardEvent): void {
145+
if (e.key == 'Enter') {
146+
this.submit();
147+
}
148+
}
149+
143150
public hasValue(): boolean {
144151
return this.checked;
145152
}

packages/uui-boolean-input/lib/uui-boolean-input.test.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
expect,
99
fixture,
1010
html,
11+
oneEvent,
1112
unsafeStatic,
1213
} from '@open-wc/testing';
1314
import { html as litHTMLLiteral } from 'lit';
@@ -27,6 +28,10 @@ const tagName = defineCE(
2728

2829
const tag = unsafeStatic(tagName);
2930

31+
const preventSubmit = (e: SubmitEvent) => {
32+
e.preventDefault();
33+
};
34+
3035
describe('UUIBooleanInputElement', () => {
3136
let element: any;
3237
let label: HTMLLabelElement;
@@ -85,7 +90,7 @@ describe('BooleanInputBaseElement in a Form', () => {
8590
let element: any;
8691
beforeEach(async () => {
8792
formElement = await fixture(
88-
html`<form><${tag} name="test" value="testValue"
93+
html`<form @submit=${preventSubmit}><${tag} name="test" value="testValue"
8994
label="test label"></${tag}></form>`
9095
);
9196
element = formElement.firstChild;
@@ -119,6 +124,18 @@ describe('BooleanInputBaseElement in a Form', () => {
119124
expect(formData.get(`test`)).to.equal('on');
120125
});
121126

127+
describe('submit', () => {
128+
it('should submit when pressing enter', async () => {
129+
const listener = oneEvent(formElement, 'submit');
130+
element.dispatchEvent(new KeyboardEvent('keypress', { key: 'Enter' }));
131+
132+
const event = await listener;
133+
expect(event).to.exist;
134+
expect(event.type).to.equal('submit');
135+
expect(event!.target).to.equal(formElement);
136+
});
137+
});
138+
122139
describe('validation', () => {
123140
let formElement: HTMLFormElement;
124141
let element: any;

packages/uui-form/lib/uui-form.element.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ export class UUIFormElement extends LitElement {
1717
if (this._formElement) {
1818
this._formElement.removeEventListener('submit', this._onSubmit);
1919
this._formElement.removeEventListener('reset', this._onReset);
20-
this._formElement.removeEventListener('keypress', this._onKeypress);
2120
}
2221

2322
const formElements = (event.target as HTMLSlotElement)
@@ -29,10 +28,6 @@ export class UUIFormElement extends LitElement {
2928
this._formElement.setAttribute('novalidate', '');
3029
this._formElement.addEventListener('submit', this._onSubmit);
3130
this._formElement.addEventListener('reset', this._onReset);
32-
this._formElement.addEventListener(
33-
'keypress',
34-
this._onKeypress.bind(this)
35-
);
3631
}
3732
}
3833

@@ -58,14 +53,6 @@ export class UUIFormElement extends LitElement {
5853
(event.target as HTMLFormElement).removeAttribute('submit-invalid');
5954
}
6055

61-
private _onKeypress(event: KeyboardEvent) {
62-
if (event.key === 'Enter') {
63-
if (this._formElement) {
64-
this._formElement.dispatchEvent(new SubmitEvent('submit'));
65-
}
66-
}
67-
}
68-
6956
render() {
7057
return html`<slot @slotchange=${this._onSlotChanged}></slot>`;
7158
}

packages/uui-input/lib/uui-input.element.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ export class UUIInputElement extends FormControlMixin(LitElement) {
249249
this.addEventListener('blur', () => {
250250
this.style.setProperty('--uui-show-focus-outline', '');
251251
});
252+
this.addEventListener('keypress', this._onKeypress);
252253

253254
this.addValidator(
254255
'tooShort',
@@ -269,6 +270,12 @@ export class UUIInputElement extends FormControlMixin(LitElement) {
269270
this.addFormControlElement(this._input);
270271
}
271272

273+
private _onKeypress(e: KeyboardEvent): void {
274+
if (this.type !== 'color' && e.key == 'Enter') {
275+
this.submit();
276+
}
277+
}
278+
272279
/**
273280
* This method enables <label for="..."> to focus the input
274281
*/

packages/uui-input/lib/uui-input.test.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ import {
99
import { UUIInputElement } from './uui-input.element';
1010
import { UUIInputEvent } from './UUIInputEvent';
1111

12+
const preventSubmit = (e: SubmitEvent) => {
13+
e.preventDefault();
14+
};
15+
16+
function sleep(ms: number) {
17+
return new Promise(resolve => setTimeout(resolve, ms));
18+
}
19+
1220
describe('UuiInputElement', () => {
1321
let element: UUIInputElement;
1422
let input: HTMLInputElement;
@@ -136,7 +144,7 @@ describe('UuiInput in Form', () => {
136144
beforeEach(async () => {
137145
formElement = await fixture(
138146
html`
139-
<form>
147+
<form @submit=${preventSubmit}>
140148
<uui-input label="a input label" name="input" value="Hello uui-input">
141149
</uui-input>
142150
</form>
@@ -160,6 +168,35 @@ describe('UuiInput in Form', () => {
160168
await expect(formData.get('input')).to.be.equal('anotherValue');
161169
});
162170

171+
describe('submit', () => {
172+
it('should submit when pressing enter', async () => {
173+
const listener = oneEvent(formElement, 'submit');
174+
element.dispatchEvent(new KeyboardEvent('keypress', { key: 'Enter' }));
175+
176+
const event = await listener;
177+
expect(event).to.exist;
178+
expect(event.type).to.equal('submit');
179+
expect(event!.target).to.equal(formElement);
180+
});
181+
182+
it('should not submit if type=color', async () => {
183+
element.type = 'color';
184+
await elementUpdated(element);
185+
186+
let isFulfilled = false;
187+
188+
const listener = oneEvent(formElement, 'submit');
189+
190+
listener.then(() => (isFulfilled = true));
191+
192+
element.dispatchEvent(new KeyboardEvent('keypress', { key: 'Enter' }));
193+
194+
await sleep(100);
195+
196+
expect(isFulfilled).to.be.false;
197+
});
198+
});
199+
163200
describe('validation', () => {
164201
describe('required', () => {
165202
beforeEach(async () => {

packages/uui-radio/lib/uui-radio-group.element.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export class UUIRadioGroupElement extends FormControlMixin(LitElement) {
7474
constructor() {
7575
super();
7676
this.addEventListener('keydown', this._onKeydown);
77+
this.addEventListener('keypress', this._onKeypress);
7778
}
7879

7980
/**
@@ -247,6 +248,12 @@ export class UUIRadioGroupElement extends FormControlMixin(LitElement) {
247248
}
248249
}
249250

251+
private _onKeypress(e: KeyboardEvent): void {
252+
if (e.key == 'Enter') {
253+
this.submit();
254+
}
255+
}
256+
250257
private _fireChangeEvent() {
251258
this.pristine = false;
252259
this.dispatchEvent(new UUIRadioGroupEvent(UUIRadioGroupEvent.CHANGE));

packages/uui-radio/lib/uui-radio-group.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import { UUIRadioGroupElement } from './uui-radio-group.element';
44
import { UUIRadioElement } from './uui-radio.element';
55
import { UUIRadioGroupEvent } from './UUIRadioGroupEvent';
66

7+
const preventSubmit = (e: SubmitEvent) => {
8+
e.preventDefault();
9+
};
10+
711
describe('UuiRadio', () => {
812
let element: UUIRadioGroupElement;
913
let radios: UUIRadioElement[];
@@ -127,7 +131,7 @@ describe('UuiRadioGroup in a Form', () => {
127131
let radios: UUIRadioElement[];
128132
beforeEach(async () => {
129133
formElement = await fixture(
130-
html` <form action="">
134+
html` <form @submit=${preventSubmit} action="">
131135
<uui-radio-group name="Test">
132136
<uui-radio value="Value 1" label="Option 1">Option 1</uui-radio>
133137
<uui-radio value="Value 2" label="Option 2"></uui-radio>
@@ -161,6 +165,18 @@ describe('UuiRadioGroup in a Form', () => {
161165
const formData = new FormData(formElement);
162166
expect(formData.get(`${element.name}`)).to.be.equal('');
163167
});
168+
169+
describe('submit', () => {
170+
it('should submit when pressing enter', async () => {
171+
const listener = oneEvent(formElement, 'submit');
172+
element.dispatchEvent(new KeyboardEvent('keypress', { key: 'Enter' }));
173+
174+
const event = await listener;
175+
expect(event).to.exist;
176+
expect(event.type).to.equal('submit');
177+
expect(event!.target).to.equal(formElement);
178+
});
179+
});
164180
});
165181

166182
describe('UuiRadioGroup when multiple radio childs are checked', () => {

packages/uui-slider/lib/uui-slider.element.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { LitElement, html, css, svg, nothing } from 'lit';
1+
import { UUIHorizontalPulseKeyframes } from '@umbraco-ui/uui-base/lib/animations';
2+
import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins';
23
import { defineElement } from '@umbraco-ui/uui-base/lib/registration';
4+
import { css, html, LitElement, nothing, svg } from 'lit';
35
import { property, query, state } from 'lit/decorators.js';
4-
56
import { styleMap } from 'lit/directives/style-map.js';
7+
68
import { nativeInputStyles } from './native-input.styles';
7-
import { UUIHorizontalPulseKeyframes } from '@umbraco-ui/uui-base/lib/animations';
89
import { UUISliderEvent } from './UUISliderEvents';
9-
import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins';
1010

1111
const TRACK_PADDING = 12;
1212
const STEP_MIN_WIDTH = 24;
@@ -309,6 +309,7 @@ export class UUISliderElement extends FormControlMixin(LitElement) {
309309
this.addEventListener('blur', () => {
310310
this.style.setProperty('--uui-show-focus-outline', '');
311311
});
312+
this.addEventListener('keypress', this._onKeypress);
312313
}
313314

314315
/**
@@ -372,6 +373,12 @@ export class UUISliderElement extends FormControlMixin(LitElement) {
372373
this._stepWidth = this._calculateStepWidth();
373374
};
374375

376+
private _onKeypress(e: KeyboardEvent): void {
377+
if (e.key == 'Enter') {
378+
this.submit();
379+
}
380+
}
381+
375382
@state()
376383
protected _steps: number[] = [];
377384

packages/uui-slider/lib/uui-slider.test.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1+
import '.';
2+
13
import {
2-
html,
3-
fixture,
4-
expect,
54
elementUpdated,
5+
expect,
6+
fixture,
7+
html,
68
oneEvent,
79
} from '@open-wc/testing';
10+
811
import { UUISliderElement } from './uui-slider.element';
9-
import './index';
1012
import { UUISliderEvent } from './UUISliderEvents';
1113

14+
const preventSubmit = (e: SubmitEvent) => {
15+
e.preventDefault();
16+
};
17+
1218
describe('UuiSlider', () => {
1319
let element: UUISliderElement;
1420
let input: HTMLInputElement;
@@ -116,7 +122,7 @@ describe('UuiSlider in Form', () => {
116122
let element: UUISliderElement;
117123
beforeEach(async () => {
118124
formElement = await fixture(
119-
html` <form>
125+
html` <form @submit=${preventSubmit}>
120126
<uui-slider
121127
label="a slideruui-slider label"
122128
name="slider"
@@ -141,4 +147,16 @@ describe('UuiSlider in Form', () => {
141147
const formData = new FormData(formElement);
142148
await expect(formData.get('slider')).to.be.equal('90');
143149
});
150+
151+
describe('submit', () => {
152+
it('should submit when pressing enter', async () => {
153+
const listener = oneEvent(formElement, 'submit');
154+
element.dispatchEvent(new KeyboardEvent('keypress', { key: 'Enter' }));
155+
156+
const event = await listener;
157+
expect(event).to.exist;
158+
expect(event.type).to.equal('submit');
159+
expect(event!.target).to.equal(formElement);
160+
});
161+
});
144162
});

0 commit comments

Comments
 (0)