Skip to content

Commit cb1b232

Browse files
committed
re-introducing uui-form
1 parent 9bed1b2 commit cb1b232

File tree

5 files changed

+206
-27
lines changed

5 files changed

+206
-27
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ export const FormControlMixin = <T extends Constructor<LitElement>>(
222222
this._removeFormListeners();
223223
this._form = this._internals.form;
224224
if (this._form) {
225-
// TODO: How can we ensure that we can know about this state without a Form Component?
225+
// This relies on the form begin a 'uui-form':
226226
if (this._form.hasAttribute('invalid-submit')) {
227227
this.pristine = false;
228228
}

packages/uui-form/lib/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { UUIFormElement } from './uui-form.element';
22
import { defineElement } from '@umbraco-ui/uui-base/lib/registration';
33

4-
defineElement('uui-form', UUIFormElement);
4+
defineElement('uui-form', UUIFormElement, { extends: 'form' });
55

66
export * from './uui-form.element';
Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
1-
import { LitElement, html, css } from 'lit';
2-
31
/**
42
* @element uui-form
53
*/
6-
export class UUIFormElement extends LitElement {
7-
static styles = [
8-
css`
9-
:host {
10-
/* Styles goes here */
11-
}
12-
`,
13-
];
4+
export class UUIFormElement extends HTMLFormElement {
5+
constructor() {
6+
super();
7+
this.setAttribute('novalidate', '');
8+
this.addEventListener('submit', this._onSubmit);
9+
this.addEventListener('reset', this._onReset);
10+
}
11+
12+
private _onSubmit(event: Event) {
13+
event.preventDefault();
14+
15+
const isValid = this.checkValidity();
16+
17+
if (!isValid) {
18+
this.setAttribute('submit-invalid', '');
19+
return;
20+
}
21+
this.removeAttribute('submit-invalid');
22+
23+
const formData = new FormData(this);
24+
25+
for (const value of formData.values()) {
26+
console.log(value);
27+
}
28+
}
1429

15-
render() {
16-
return html` Markup goes here `;
30+
private _onReset() {
31+
this.removeAttribute('submit-invalid');
1732
}
1833
}
Lines changed: 99 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,107 @@
1-
import '.';
2-
31
import { Story } from '@storybook/web-components';
42
import { html } from 'lit-html';
3+
import '@umbraco-ui/uui-form/lib/index';
4+
import '@umbraco-ui/uui-checkbox/lib';
5+
import '@umbraco-ui/uui-slider/lib';
6+
import '@umbraco-ui/uui-radio/lib';
7+
import '@umbraco-ui/uui-toggle/lib';
58

69
export default {
710
id: 'uui-form',
8-
title: 'Form',
11+
title: 'Inputs/Form',
912
component: 'uui-form',
10-
parameters: {
11-
docs: {
12-
source: {
13-
code: `<uui-form></uui-form>`,
14-
},
15-
},
16-
},
1713
};
1814

19-
export const Overview: Story = () => html`<uui-form></uui-form>`;
15+
export const Overview: Story = () => html` <form is="uui-form">
16+
<div style="margin-bottom: 15px;">
17+
<uui-checkbox
18+
name="checkbox"
19+
value="Bike"
20+
label="This is my checked checkbox"
21+
checked
22+
required>
23+
This is my checked checkbox
24+
</uui-checkbox>
25+
</div>
26+
27+
<div style="margin-bottom: 15px;">
28+
<uui-toggle name="toggle" label="This is my toggle" required>
29+
This is my toggle
30+
</uui-toggle>
31+
</div>
32+
33+
<div style="margin-bottom: 15px;">
34+
<uui-radio-group name="radio" label="This is my radio" required>
35+
<uui-radio value="radio1" label="radio1" name="radio1">Label</uui-radio>
36+
<uui-radio value="radio2" label="radio2" name="radio2">Label</uui-radio>
37+
<uui-radio value="radio3" label="radio3" name="radio3">Label</uui-radio>
38+
</uui-radio-group>
39+
</div>
40+
41+
<div style="margin-bottom: 15px;">
42+
<uui-input name="email" type="text" label="Email" required> </uui-input>
43+
</div>
44+
45+
<div style="margin-bottom: 15px;">
46+
<uui-input
47+
type="password"
48+
name="password"
49+
value="MyPassword"
50+
label="Password"
51+
required>
52+
</uui-input>
53+
</div>
54+
55+
<div style="margin-bottom: 15px;">
56+
<uui-slider
57+
label="Slider"
58+
name="slider"
59+
value="5.5"
60+
min="0"
61+
max="10"
62+
step="1"
63+
required>
64+
</uui-slider>
65+
</div>
66+
67+
<div style="margin-bottom: 15px;">
68+
<input
69+
name="nativeCheckbox"
70+
label="Native input text"
71+
type="checkbox"
72+
value="NativeCheckboxValue"
73+
placeholder="native text input"
74+
checked
75+
required />
76+
</div>
77+
78+
<div style="margin-bottom: 15px;">
79+
<input
80+
name="nativeInput"
81+
label="Native input text"
82+
type="text"
83+
default-value="default test value"
84+
value="test value"
85+
placeholder="native text input"
86+
required />
87+
</div>
88+
89+
<div style="margin-bottom: 15px;">
90+
<input
91+
name="nativeInputNumber"
92+
label="Native input number"
93+
type="number"
94+
value=""
95+
placeholder="native number input"
96+
min="0"
97+
max="10"
98+
required />
99+
</div>
100+
<div>
101+
<uui-button type="submit" label="Submit" look="positive">
102+
Submit
103+
</uui-button>
104+
105+
<uui-button type="reset" label="Reset" look="secondary"> Reset </uui-button>
106+
</div>
107+
</form>`;
Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,91 @@
1-
import { html, fixture, expect } from '@open-wc/testing';
1+
import { html, fixture, expect, oneEvent } from '@open-wc/testing';
22
import { UUIFormElement } from './uui-form.element';
33
import '.';
44

55
describe('UUIFormElement', () => {
66
let element: UUIFormElement;
77

88
beforeEach(async () => {
9-
element = await fixture(html` <uui-form></uui-form> `);
9+
element = await fixture(html` <form is="uui-form"></form> `);
1010
});
1111

1212
it('passes the a11y audit', async () => {
1313
await expect(element).shadowDom.to.be.accessible();
1414
});
15+
16+
it('set novalidate attribute on form-element', async () => {
17+
await expect(element.hasAttribute('novalidate')).to.be.true;
18+
await expect(element.getAttribute('novalidate')).to.not.be.null;
19+
await expect(element.getAttribute('novalidate')).to.be.empty;
20+
});
21+
22+
describe('events', () => {
23+
describe('submit', () => {
24+
it('emits a submit event when submitted', async () => {
25+
const listener = oneEvent(element, 'submit');
26+
27+
element.requestSubmit();
28+
29+
const event = await listener;
30+
expect(event).to.exist;
31+
expect(event.type).to.equal('submit');
32+
expect(event!.target).to.equal(element);
33+
});
34+
});
35+
});
36+
37+
describe('Validation', () => {
38+
let element: UUIFormElement;
39+
let input: HTMLInputElement;
40+
41+
beforeEach(async () => {
42+
element = await fixture(html`
43+
<form is="uui-form">
44+
<input type="text" name="my_required_input" required />
45+
</form>
46+
`);
47+
input = element.querySelector('input') as HTMLInputElement;
48+
});
49+
50+
it('does not have "submit-invalid" attribute before submission.', async () => {
51+
await expect(element.hasAttribute('submit-invalid')).to.be.false;
52+
});
53+
54+
it('has "submit-invalid" attribute if Form Control was invalid at submission.', async () => {
55+
const listener = oneEvent(element, 'submit');
56+
57+
element.requestSubmit();
58+
59+
await listener;
60+
await expect(element.hasAttribute('submit-invalid')).to.be.true;
61+
await expect(element.getAttribute('submit-invalid')).to.not.be.null;
62+
await expect(element.getAttribute('submit-invalid')).to.be.empty;
63+
});
64+
65+
it('only has "submit-invalid" attribute if Form Control was invalid at submission.', async () => {
66+
const listener = oneEvent(element, 'submit');
67+
68+
input.value = 'something';
69+
element.requestSubmit();
70+
71+
await listener;
72+
await expect(element.hasAttribute('submit-invalid')).to.be.false;
73+
});
74+
75+
it('"submit-invalid" attribute is removed when form is re-validated and submitted.', async () => {
76+
const listener = oneEvent(element, 'submit');
77+
78+
element.requestSubmit();
79+
80+
await listener;
81+
await expect(element.hasAttribute('submit-invalid')).to.be.true;
82+
83+
const listener2 = oneEvent(element, 'submit');
84+
input.value = 'something';
85+
element.requestSubmit();
86+
87+
await listener2;
88+
await expect(element.hasAttribute('submit-invalid')).to.be.false;
89+
});
90+
});
1591
});

0 commit comments

Comments
 (0)