Skip to content

Commit 188d231

Browse files
authored
feat: enable form support and name attribute for inputs (#337)
1 parent 2e2439a commit 188d231

19 files changed

+353
-9
lines changed

packages/main/.eslintrc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ module.exports = {
4646
// "consistent-return": 1, // removed for UI5 WebComponents
4747
"curly": [2, "all"],
4848
// "default-case": 1, // removed for UI5 WebComponents
49-
"import/extensions": ["error", "ignorePackages"], // override for UI5 WebComponents
49+
"import/extensions": ["error", "always"], // override for UI5 WebComponents
5050
"no-alert": 2,
5151
"no-caller": 2,
5252
"no-div-regex": 2,

packages/main/bundle.esm.js

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ import StandardListItem from "./dist/StandardListItem.js";
4949
import CustomListItem from "./dist/CustomListItem.js";
5050
import GroupHeaderListItem from "./dist/GroupHeaderListItem.js";
5151

52+
// used for forms
53+
import InputElementsFormSupport from "./dist/InputElementsFormSupport.js";
54+
5255
// used in test pages
5356
import RenderScheduler from "@ui5/webcomponents-base/src/RenderScheduler.js";
5457
window.RenderScheduler = RenderScheduler;

packages/main/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
"test:jest": "jest",
7676
"test": "npm-run-all --sequential test:jest test:wdio",
7777
"watch:src": "copy-and-watch --watch \"src/**/*.js\" \"src/**/i18n/*.json\" dist/",
78-
"watch:bundle": "rollup -c -w --environment ES5_BUILD,DEV",
78+
"watch:bundle": "wait-on dist/i18n/messagebundle.json && rollup -c -w --environment ES5_BUILD,DEV",
7979
"watch:templates": "npm-watch build:templates",
8080
"watch:samples": "npm-watch build:samples",
8181
"watch:pages": "npm-watch build:pages",

packages/main/src/Button.js

+21
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,20 @@ const metadata = {
8181
*/
8282
activeIcon: { type: URI, defaultValue: null },
8383

84+
/**
85+
* When set to <code>true</code>, the <code>ui5-button</code> will
86+
* automatically submit the nearest form element upon <code>press</code>.
87+
*
88+
* <b>Important:</b> For the <code>submits</code> property to have effect, you must add the following import to your project:
89+
* <code>import InputElementsFormSupport from "@ui5/webcomponents/dist/InputElementsFormSupport";</code>
90+
*
91+
* @type {boolean}
92+
* @public
93+
*/
94+
submits: {
95+
type: Boolean,
96+
},
97+
8498
/**
8599
* Used to switch the active state (pressed or not) of the <code>ui5-button</code>.
86100
*/
@@ -174,6 +188,10 @@ class Button extends UI5Element {
174188
} else {
175189
this._iconSettings = null;
176190
}
191+
192+
if (this.submits && !Button.FormSupport) {
193+
console.warn(`In order for the "submits" property to have effect, you should also: import InputElementsFormSupport from "@ui5/webcomponents/dist/InputElementsFormSupport";`); // eslint-disable-line
194+
}
177195
}
178196

179197
onEnterDOM() {
@@ -188,6 +206,9 @@ class Button extends UI5Element {
188206
event.isMarked = "button";
189207
if (!this.disabled) {
190208
this.fireEvent("press", {});
209+
if (Button.FormSupport) {
210+
Button.FormSupport.triggerFormSubmit(this);
211+
}
191212
}
192213
}
193214

packages/main/src/CheckBox.hbs

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@
1515
?wrap="{{ctr._label.wrap}}"
1616
>{{ctr._label.text}}</ui5-label>
1717
{{/if}}
18-
</div>
18+
19+
<slot name="formSupport"></slot>
20+
</div>

packages/main/src/CheckBox.js

+26
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,23 @@ const metadata = {
9292
type: Boolean,
9393
},
9494

95+
/**
96+
* Determines the name with which the <code>ui5-checkbox</code> will be submitted in an HTML form.
97+
*
98+
* <b>Important:</b> For the <code>name</code> property to have effect, you must add the following import to your project:
99+
* <code>import InputElementsFormSupport from "@ui5/webcomponents/dist/InputElementsFormSupport";</code>
100+
*
101+
* <b>Note:</b> When set, a native <code>input</code> HTML element
102+
* will be created inside the <code>ui5-checkbox</code> so that it can be submitted as
103+
* part of an HTML form. Do not use this property unless you need to submit a form.
104+
*
105+
* @type {String}
106+
* @public
107+
*/
108+
name: {
109+
type: String,
110+
},
111+
95112
_label: {
96113
type: Object,
97114
},
@@ -165,6 +182,15 @@ class CheckBox extends UI5Element {
165182

166183
onBeforeRendering() {
167184
this.syncLabel();
185+
186+
if (CheckBox.FormSupport) {
187+
CheckBox.FormSupport.syncNativeHiddenInput(this, (element, nativeInput) => {
188+
nativeInput.disabled = element.disabled || !element.checked;
189+
nativeInput.value = element.checked ? "on" : "";
190+
});
191+
} else if (this.name) {
192+
console.warn(`In order for the "name" property to have effect, you should also: import InputElementsFormSupport from "@ui5/webcomponents/dist/InputElementsFormSupport";`); // eslint-disable-line
193+
}
168194
}
169195

170196
syncLabel() {

packages/main/src/DatePicker.hbs

+3-1
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,6 @@
4848
@selectedDatesChange="{{ctr._calendar.onSelectedDatesChange}}"
4949
></ui5-calendar>
5050
</ui5-popover>
51-
</div>
51+
52+
<slot name="formSupport"></slot>
53+
</div>

packages/main/src/DatePicker.js

+23
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,23 @@ const metadata = {
113113
type: String,
114114
},
115115

116+
/**
117+
* Determines the name with which the <code>ui5-datepicker</code> will be submitted in an HTML form.
118+
*
119+
* <b>Important:</b> For the <code>name</code> property to have effect, you must add the following import to your project:
120+
* <code>import InputElementsFormSupport from "@ui5/webcomponents/dist/InputElementsFormSupport";</code>
121+
*
122+
* <b>Note:</b> When set, a native <code>input</code> HTML element
123+
* will be created inside the <code>ui5-datepicker</code> so that it can be submitted as
124+
* part of an HTML form. Do not use this property unless you need to submit a form.
125+
*
126+
* @type {String}
127+
* @public
128+
*/
129+
name: {
130+
type: String,
131+
},
132+
116133
_isPickerOpen: {
117134
defaultValue: false,
118135
type: Boolean,
@@ -283,6 +300,12 @@ class DatePicker extends UI5Element {
283300
} else {
284301
this._calendar.selectedDates = [];
285302
}
303+
304+
if (DatePicker.FormSupport) {
305+
DatePicker.FormSupport.syncNativeHiddenInput(this);
306+
} else if (this.name) {
307+
console.warn(`In order for the "name" property to have effect, you should also: import InputElementsFormSupport from "@ui5/webcomponents/dist/InputElementsFormSupport";`); // eslint-disable-line
308+
}
286309
}
287310

288311
onclick(event) {

packages/main/src/Input.hbs

+3-1
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,6 @@
3838
</ui5-list>
3939
</ui5-popover>
4040
{{/if}}
41-
</div>
41+
42+
<slot name="formSupport"></slot>
43+
</div>

packages/main/src/Input.js

+23
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,23 @@ const metadata = {
145145
defaultValue: ValueState.None,
146146
},
147147

148+
/**
149+
* Determines the name with which the <code>ui5-input</code> will be submitted in an HTML form.
150+
*
151+
* <b>Important:</b> For the <code>name</code> property to have effect, you must add the following import to your project:
152+
* <code>import InputElementsFormSupport from "@ui5/webcomponents/dist/InputElementsFormSupport";</code>
153+
*
154+
* <b>Note:</b> When set, a native <code>input</code> HTML element
155+
* will be created inside the <code>ui5-input</code> so that it can be submitted as
156+
* part of an HTML form. Do not use this property unless you need to submit a form.
157+
*
158+
* @type {String}
159+
* @public
160+
*/
161+
name: {
162+
type: String,
163+
},
164+
148165
/**
149166
* Defines whether the <code>ui5-input</code> should show suggestions, if such are present.
150167
*
@@ -302,6 +319,12 @@ class Input extends UI5Element {
302319
if (this.showSuggestions) {
303320
this.enableSuggestions();
304321
}
322+
323+
if (Input.FormSupport) {
324+
Input.FormSupport.syncNativeHiddenInput(this);
325+
} else if (this.name) {
326+
console.warn(`In order for the "name" property to have effect, you should also: import InputElementsFormSupport from "@ui5/webcomponents/dist/InputElementsFormSupport";`); // eslint-disable-line
327+
}
305328
}
306329

307330
onAfterRendering() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Input from "./Input.js";
2+
import TextArea from "./TextArea.js";
3+
import DatePicker from "./DatePicker.js";
4+
import CheckBox from "./CheckBox.js";
5+
import Button from "./Button.js";
6+
7+
import FormSupport from "./util/FormSupport.js";
8+
9+
Input.FormSupport = FormSupport;
10+
TextArea.FormSupport = FormSupport;
11+
DatePicker.FormSupport = FormSupport;
12+
CheckBox.FormSupport = FormSupport;
13+
Button.FormSupport = FormSupport;
14+
15+
export default {};

packages/main/src/Panel.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import UI5Element from "@ui5/webcomponents-base/src/UI5Element";
1+
import UI5Element from "@ui5/webcomponents-base/src/UI5Element.js";
22
import Bootstrap from "@ui5/webcomponents-base/src/Bootstrap.js";
33
import { getIconURI } from "@ui5/webcomponents-base/src/IconPool.js";
44
import slideDown from "@ui5/webcomponents-base/src/animations/slideDown.js";

packages/main/src/TextArea.hbs

+3-1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,6 @@
3434
{{#if ctr.showExceededText}}
3535
<span class="{{classes.exceededText}}">{{exceededText}}</span>
3636
{{/if}}
37-
</div>
37+
38+
<slot name="formSupport"></slot>
39+
</div>

packages/main/src/TextArea.js

+23
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,23 @@ const metadata = {
139139
defaultValue: 0,
140140
},
141141

142+
/**
143+
* Determines the name with which the <code>ui5-textarea</code> will be submitted in an HTML form.
144+
*
145+
* <b>Important:</b> For the <code>name</code> property to have effect, you must add the following import to your project:
146+
* <code>import InputElementsFormSupport from "@ui5/webcomponents/dist/InputElementsFormSupport";</code>
147+
*
148+
* <b>Note:</b> When set, a native <code>input</code> HTML element
149+
* will be created inside the <code>ui5-textarea</code> so that it can be submitted as
150+
* part of an HTML form. Do not use this property unless you need to submit a form.
151+
*
152+
* @type {String}
153+
* @public
154+
*/
155+
name: {
156+
type: String,
157+
},
158+
142159
_height: {
143160
type: CSSSize,
144161
defaultValue: null,
@@ -236,6 +253,12 @@ class TextArea extends UI5Element {
236253
// this should be complex calc between line height and paddings - TODO: make it stable
237254
this._maxHeight = `${this.growingMaxLines * 1.4 * 14 + 9}px`;
238255
}
256+
257+
if (TextArea.FormSupport) {
258+
TextArea.FormSupport.syncNativeHiddenInput(this);
259+
} else if (this.name) {
260+
console.warn(`In order for the "name" property to have effect, you should also: import InputElementsFormSupport from "@ui5/webcomponents/dist/InputElementsFormSupport";`); // eslint-disable-line
261+
}
239262
}
240263

241264
getInputDomRef() {

packages/main/src/util/FormSupport.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
class FormSupport {
2+
/**
3+
*
4+
* @param element - the WebComponent that needs form support
5+
* @param nativeInputUpdateCallback - determines how the native input's disabled and value properties are calculated
6+
*/
7+
static syncNativeHiddenInput(element, nativeInputUpdateCallback) {
8+
const needsNativeInput = !!element.name;
9+
let nativeInput = element.querySelector("input[type=hidden][data-ui5-webcomponents-form-support]");
10+
if (needsNativeInput && !nativeInput) {
11+
nativeInput = document.createElement("input");
12+
nativeInput.type = "hidden";
13+
nativeInput.setAttribute("data-ui5-webcomponents-form-support", "");
14+
nativeInput.slot = "formSupport"; // Needed for IE - otherwise input elements are not part of the real DOM tree and are not detected by forms
15+
element.appendChild(nativeInput);
16+
}
17+
if (!needsNativeInput && nativeInput) {
18+
element.removeChild(nativeInput);
19+
}
20+
21+
if (needsNativeInput) {
22+
nativeInput.name = element.name;
23+
(nativeInputUpdateCallback || copyDefaultProperties)(element, nativeInput);
24+
}
25+
}
26+
27+
static triggerFormSubmit(element) {
28+
if (!element.submits) {
29+
return;
30+
}
31+
let parentElement;
32+
do {
33+
parentElement = element.parentElement;
34+
} while (parentElement && parentElement.tagName.toLowerCase() !== "form");
35+
if (parentElement) {
36+
parentElement.submit();
37+
}
38+
}
39+
}
40+
41+
42+
const copyDefaultProperties = (element, nativeInput) => {
43+
nativeInput.disabled = element.disabled;
44+
nativeInput.value = element.value;
45+
};
46+
47+
export default FormSupport;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
5+
<meta charset="utf-8">
6+
<title>Form support</title>
7+
<script src="../../../../../../webcomponentsjs/webcomponents-loader.js"></script>
8+
<script src="../../../../../../resources/sap/ui/webcomponents/main/bundle.esm.js" type="module"></script>
9+
<script nomodule src="../../../../../../resources/sap/ui/webcomponents/main/bundle.es5.js"></script>
10+
</head>
11+
12+
<body>
13+
14+
<form method="get">
15+
16+
<ui5-input name="input" value="ok"></ui5-input>
17+
<ui5-input name="input_disabled" disabled value="ok"></ui5-input>
18+
19+
<ui5-textarea name="ta" value="ok"></ui5-textarea>
20+
<ui5-textarea name="ta_disabled" disabled value="ok"></ui5-textarea>
21+
22+
<ui5-datepicker name="dp" value="Apr 10, 2019"></ui5-datepicker>
23+
<ui5-datepicker name="dp_disabled" disabled value="Apr 10, 2019"></ui5-datepicker>
24+
25+
<ui5-checkbox name="cb" checked></ui5-checkbox>
26+
<ui5-checkbox name="cb_disabled" disabled checked></ui5-checkbox>
27+
28+
<ui5-button id="b1">Does not submit forms</ui5-button>
29+
30+
<ui5-button id="b2" submits>Submits forms</ui5-button>
31+
32+
</form>
33+
34+
35+
</body>
36+
</html>

0 commit comments

Comments
 (0)