Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add form associated custom elements #7909

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/main/src/CheckBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,32 @@
@event("change")

class CheckBox extends UI5Element implements IFormElement {
static formAssociated = true;
get form() {
return this.#internals.form;
}

formStateRestoreCallback(value: string) {
this.checked = Boolean(value);
this._setFormValue();
}

formAssociatedCallback() {
this._setFormValue();
}

_setFormValue() {
this.#internals.setFormValue(this.checked.toString());
}

connectedCallback(): Promise<void> {
return super.connectedCallback();
}

onEnterDOM(): void {
console.log();

Check failure on line 134 in packages/main/src/CheckBox.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected console statement

Check failure on line 134 in packages/main/src/CheckBox.ts

View workflow job for this annotation

GitHub Actions / check (base)

Unexpected console statement

Check failure on line 134 in packages/main/src/CheckBox.ts

View workflow job for this annotation

GitHub Actions / check (main:suite-1)

Unexpected console statement

Check failure on line 134 in packages/main/src/CheckBox.ts

View workflow job for this annotation

GitHub Actions / check (main:suite-2)

Unexpected console statement

Check failure on line 134 in packages/main/src/CheckBox.ts

View workflow job for this annotation

GitHub Actions / check (fiori)

Unexpected console statement
}

/**
* Receives id(or many ids) of the elements that label the component
* @type {string}
Expand Down Expand Up @@ -281,10 +307,12 @@

static i18nBundle: I18nBundle;
_deactivate: () => void;
#internals: ElementInternals;

constructor() {
super();

this.#internals = this.attachInternals();
this._deactivate = () => {
if (activeCb) {
activeCb.active = false;
Expand Down Expand Up @@ -368,13 +396,15 @@
this.checked = !this.checked;
}

this._setFormValue();
const changePrevented = !this.fireEvent("change", null, true);
// Angular two way data binding
const valueChagnePrevented = !this.fireEvent("value-changed", null, true);

if (changePrevented || valueChagnePrevented) {
this.checked = lastState.checked;
this.indeterminate = lastState.indeterminate;
this._setFormValue();
}
}
return this;
Expand Down
15 changes: 15 additions & 0 deletions packages/main/src/Input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,13 @@ type InputSuggestionScrollEventDetail = {
},
})
class Input extends UI5Element implements SuggestionComponent, IFormElement {
static formAssociated = true;
get form() {
return this._internals.form;
}
formStateRestoreCallback(value: string) {
this.value = value;
}
/**
* Defines whether the component is in disabled state.
* <br><br>
Expand Down Expand Up @@ -642,9 +649,12 @@ class Input extends UI5Element implements SuggestionComponent, IFormElement {
_performTextSelection?: boolean;
_previewItem?: SuggestionListItem;
static i18nBundle: I18nBundle;
_internals: ElementInternals;

constructor() {
super();
this._internals = this.attachInternals();

// Indicates if there is selected suggestionItem.
this.hasSuggestionItemSelected = false;

Expand Down Expand Up @@ -1365,12 +1375,17 @@ class Input extends UI5Element implements SuggestionComponent, IFormElement {
this.valueBeforeItemPreview = inputValue;

if (isUserInput) { // input
this._setFormValue();
this.fireEvent<InputEventDetail>(INPUT_EVENTS.INPUT, { inputType: e.inputType });
// Angular two way data binding
this.fireEvent("value-changed");
}
}

_setFormValue() {
this._internals.setFormValue(this.value);
}

async getInputValue() {
const domRef = this.getDomRef();

Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/Menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ class Menu extends UI5Element {
}
if (this.open) {
const rootNode = this.getRootNode() as Document;
const opener = this.opener instanceof HTMLElement ? this.opener : rootNode && rootNode.getElementById(this.opener);
const opener = this.opener instanceof HTMLElement ? this.opener : rootNode && typeof rootNode.getElementById === "function" && rootNode.getElementById(this.opener);

if (opener) {
this.showAt(opener);
Expand Down
58 changes: 58 additions & 0 deletions packages/main/src/MultiComboBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,31 @@
})

class MultiComboBox extends UI5Element {
static formAssociated = true;
private _internals: ElementInternals;
formStateValue?: FormData;

get form() {
return this._internals.form;
}


Check failure on line 251 in packages/main/src/MultiComboBox.ts

View workflow job for this annotation

GitHub Actions / check

More than 1 blank line not allowed

Check failure on line 251 in packages/main/src/MultiComboBox.ts

View workflow job for this annotation

GitHub Actions / check (base)

More than 1 blank line not allowed

Check failure on line 251 in packages/main/src/MultiComboBox.ts

View workflow job for this annotation

GitHub Actions / check (main:suite-1)

More than 1 blank line not allowed

Check failure on line 251 in packages/main/src/MultiComboBox.ts

View workflow job for this annotation

GitHub Actions / check (main:suite-2)

More than 1 blank line not allowed

Check failure on line 251 in packages/main/src/MultiComboBox.ts

View workflow job for this annotation

GitHub Actions / check (fiori)

More than 1 blank line not allowed
formStateRestoreCallback(formValue: FormData) {
this.formStateValue = formValue;
const [value, ...tokens] = formValue.getAll(this.getAttribute("name")!) as string[];
this.items.forEach(item => {
const idx = tokens.indexOf(item.text);
if (idx !== -1) {
item.selected = true;
tokens.splice(idx, 1);
} else {
item.selected = false;
}
});

this.value = value;
}

/**
* Defines the value of the component.
* <br><br>
Expand Down Expand Up @@ -490,6 +515,7 @@
constructor() {
super();

this._internals = this.attachInternals();
this._filteredItems = [];
this._previouslySelectedItems = [];
this.selectedValues = [];
Expand All @@ -506,6 +532,9 @@
}

onEnterDOM() {
if (this.formStateValue) {
this.formStateRestoreCallback(this.formStateValue);
}
ResizeHandler.register(this, this._handleResizeBound);
}

Expand Down Expand Up @@ -592,9 +621,34 @@
}
}

this._internals.setFormValue(this.buildValue());
this.fireEvent("input");
}

buildValue() {
const fd = new FormData();

// value is always present and always first
fd.append(this.getAttribute("name")!, this.value);
// fd.set("value", this.value);

// tokens if any
this._getSelectedItems().forEach(token => {
// fd.append("tokens", token.text);
// eslint-disable-next-line prefer-template
fd.append(this.getAttribute("name")!, token.text);
});

// const obj = { value: this.value, tokens: this._getSelectedItems().map(t => t.text) };
// const blob = new Blob([JSON.stringify(obj, null, 2)], {
// type: "application/json",
// });
// // return blob;
// fd.set(this.getAttribute("name")!, blob);

return fd;
}

_tokenDelete(e: CustomEvent<TokenizerTokenDeleteEventDetail>) {
this._previouslySelectedItems = this._getSelectedItems();
const token: Token = e.detail.ref;
Expand Down Expand Up @@ -777,6 +831,7 @@
});
} else {
this.value = pastedText;
this._internals.setFormValue(this.buildValue());
this.fireEvent("input");
}
}
Expand Down Expand Up @@ -1297,6 +1352,7 @@
}
}

this._internals.setFormValue(this.buildValue());
this.fireEvent("input");
}

Expand All @@ -1314,6 +1370,7 @@
}

fireSelectionChange() {
this._internals.setFormValue(this.buildValue());
const changePrevented = !this.fireEvent<MultiComboBoxSelectionChangeEventDetail>("selection-change", {
items: this._getSelectedItems(),
}, true);
Expand Down Expand Up @@ -1419,6 +1476,7 @@
this._filteredItems.forEach(item => {
item.selected = this._previouslySelectedItems.includes(item);
});
this._internals.setFormValue(this.buildValue());
}

onBeforeRendering() {
Expand Down
2 changes: 2 additions & 0 deletions packages/main/src/MultiInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ type MultiInputTokenDeleteEventDetail = {
})

class MultiInput extends Input {
static formAssociated = true;

/**
* Determines whether a value help icon will be visualized in the end of the input.
* Pressing the icon will fire <code>value-help-trigger</code> event.
Expand Down
28 changes: 12 additions & 16 deletions packages/main/src/RadioButton.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isDesktop } from "@ui5/webcomponents-base/dist/Device.js";
import { getFeature } from "@ui5/webcomponents-base/dist/FeaturesRegistry.js";

Check warning on line 2 in packages/main/src/RadioButton.ts

View workflow job for this annotation

GitHub Actions / check

'getFeature' is defined but never used

Check warning on line 2 in packages/main/src/RadioButton.ts

View workflow job for this annotation

GitHub Actions / check (base)

'getFeature' is defined but never used

Check warning on line 2 in packages/main/src/RadioButton.ts

View workflow job for this annotation

GitHub Actions / check (main:suite-1)

'getFeature' is defined but never used

Check warning on line 2 in packages/main/src/RadioButton.ts

View workflow job for this annotation

GitHub Actions / check (main:suite-2)

'getFeature' is defined but never used

Check warning on line 2 in packages/main/src/RadioButton.ts

View workflow job for this annotation

GitHub Actions / check (fiori)

'getFeature' is defined but never used
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
Expand All @@ -22,7 +22,7 @@
import RadioButtonGroup from "./RadioButtonGroup.js";
import WrappingType from "./types/WrappingType.js";
import type { IFormElement } from "./features/InputElementsFormSupport.js";
import type FormSupport from "./features/InputElementsFormSupport.js";

Check warning on line 25 in packages/main/src/RadioButton.ts

View workflow job for this annotation

GitHub Actions / check

'FormSupport' is defined but never used

Check warning on line 25 in packages/main/src/RadioButton.ts

View workflow job for this annotation

GitHub Actions / check (base)

'FormSupport' is defined but never used

Check warning on line 25 in packages/main/src/RadioButton.ts

View workflow job for this annotation

GitHub Actions / check (main:suite-1)

'FormSupport' is defined but never used

Check warning on line 25 in packages/main/src/RadioButton.ts

View workflow job for this annotation

GitHub Actions / check (main:suite-2)

'FormSupport' is defined but never used

Check warning on line 25 in packages/main/src/RadioButton.ts

View workflow job for this annotation

GitHub Actions / check (fiori)

'FormSupport' is defined but never used

// Template
import RadioButtonTemplate from "./generated/templates/RadioButtonTemplate.lit.js";
Expand Down Expand Up @@ -93,6 +93,17 @@
@event("change")

class RadioButton extends UI5Element implements IFormElement {
static formAssociated = true;

formStateRestoreCallback(value: string) {
this.checked = (this.value === value);
this._setFormValue();
}

formAssociatedCallback() {
this._setFormValue();
}

/**
* Defines whether the component is disabled.
* <br><br>
Expand Down Expand Up @@ -279,10 +290,6 @@
_checked!: boolean;
_internals: ElementInternals;

static get formAssociated() {
return true;
}

static i18nBundle: I18nBundle;

constructor() {
Expand All @@ -308,8 +315,6 @@

onBeforeRendering() {
this.syncGroup();

this._enableFormSupport();
}

onExitDOM() {
Expand Down Expand Up @@ -348,16 +353,6 @@
this._checked = this.checked;
}

_enableFormSupport() {
const formSupport = getFeature<typeof FormSupport>("FormSupport");

if (formSupport) {
this._setFormValue();
} else if (this.value) {
console.warn(`In order for the "value" property to have effect, you should also: import "@ui5/webcomponents/dist/features/InputElementsFormSupport.js";`); // eslint-disable-line
}
}

_setFormValue() {
this._internals.setFormValue(this.checked ? this.value : null);
}
Expand Down Expand Up @@ -450,6 +445,7 @@

if (!this.name) {
this.checked = !this.checked;
this._setFormValue();
this.fireEvent("change");
return this;
}
Expand Down
2 changes: 2 additions & 0 deletions packages/main/src/RadioButtonGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ class RadioButtonGroup {
static _deselectRadio(radioBtn: RadioButton) {
if (radioBtn) {
radioBtn.checked = false;
radioBtn._setFormValue();
}
}

Expand All @@ -173,6 +174,7 @@ class RadioButtonGroup {
radioBtn.focus();
radioBtn.checked = true;
radioBtn._checked = true;
radioBtn._setFormValue();
radioBtn.fireEvent("change");
}
}
Expand Down
21 changes: 21 additions & 0 deletions packages/main/src/Select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,17 @@
*/
@event("close")
class Select extends UI5Element implements IFormElement {
static formAssociated = true;
formStateValue?: string;
get form() {
return this.#internals.form;
}
formStateRestoreCallback(value: string) {
this.formStateValue = value;
this.options.forEach(option => {
option.selected = option.value === value;
});
}
static i18nBundle: I18nBundle;

/**
Expand Down Expand Up @@ -436,10 +447,12 @@
_onMenuChange: (e: CustomEvent<SelectMenuChange>) => void;
_attachMenuListeners: (menu: HTMLElement) => void;
_detachMenuListeners: (menu: HTMLElement) => void;
#internals: ElementInternals;

constructor() {
super();

this.#internals = this.attachInternals();
this._syncedOptions = [];
this._selectedIndexBeforeOpen = -1;
this._escapePressed = false;
Expand Down Expand Up @@ -789,6 +802,7 @@
this.selectOptions[this._selectedIndex].selected = false;

if (this._selectedIndex !== index) {
this.#internals.setFormValue(this.selectOptions[index].value);
this.fireEvent<SelectLiveChangeEventDetail>("live-change", { selectedOption: this.selectOptions[index] });
}

Expand Down Expand Up @@ -1126,6 +1140,13 @@
}
}

onEnterDOM(): void {
if (this.formStateValue) {
this.formStateRestoreCallback(this.formStateValue);
// TODO remove

Check warning on line 1146 in packages/main/src/Select.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected 'todo' comment: 'TODO remove'

Check warning on line 1146 in packages/main/src/Select.ts

View workflow job for this annotation

GitHub Actions / check (base)

Unexpected 'todo' comment: 'TODO remove'

Check warning on line 1146 in packages/main/src/Select.ts

View workflow job for this annotation

GitHub Actions / check (main:suite-1)

Unexpected 'todo' comment: 'TODO remove'

Check warning on line 1146 in packages/main/src/Select.ts

View workflow job for this annotation

GitHub Actions / check (main:suite-2)

Unexpected 'todo' comment: 'TODO remove'

Check warning on line 1146 in packages/main/src/Select.ts

View workflow job for this annotation

GitHub Actions / check (fiori)

Unexpected 'todo' comment: 'TODO remove'
}
}

get selectedOptionIcon() {
return this.selectedOption && this.selectedOption.icon;
}
Expand Down
Loading
Loading