diff --git a/packages/base/cypress/specs/UI5ElementPropsAndAttrs.cy.tsx b/packages/base/cypress/specs/UI5ElementPropsAndAttrs.cy.tsx index 84f71fc52f65..6cb954d48d39 100644 --- a/packages/base/cypress/specs/UI5ElementPropsAndAttrs.cy.tsx +++ b/packages/base/cypress/specs/UI5ElementPropsAndAttrs.cy.tsx @@ -78,7 +78,7 @@ describe("Properties and attributes convert to each other", () => { .should("not.have.attr", "object-prop"); }); - it("Tests that array properties have no attributes", () => { + it("Tests that array properties have attributes", () => { cy.mount(); cy.get("[ui5-test-generic]") @@ -88,7 +88,7 @@ describe("Properties and attributes convert to each other", () => { .invoke("prop", "multiProp", ["a", "b"]); cy.get("@testGeneric") - .should("not.have.attr", "multi-prop"); + .should("have.attr", "multi-prop", '["a","b"]'); }); it("Tests that noAttribute properties have no attributes", () => { diff --git a/packages/base/src/UI5Element.ts b/packages/base/src/UI5Element.ts index 2f103fe8a863..11e758444670 100644 --- a/packages/base/src/UI5Element.ts +++ b/packages/base/src/UI5Element.ts @@ -80,6 +80,15 @@ const defaultConverter = { if (type === Number) { return value === null ? undefined : parseFloat(value); } + + if (type === Object || type === Array) { + try { + return JSON.parse(value as string) as object | Array; + } catch { + return value; + } + } + return value; }, toAttribute(value: unknown, type: unknown) { @@ -89,7 +98,7 @@ const defaultConverter = { // don't set attributes for arrays and objects if (type === Object || type === Array) { - return null; + return JSON.stringify(value); } // object, array, other diff --git a/packages/base/src/UI5ElementMetadata.ts b/packages/base/src/UI5ElementMetadata.ts index b2c240e340ed..7230e8bdf2d6 100644 --- a/packages/base/src/UI5ElementMetadata.ts +++ b/packages/base/src/UI5ElementMetadata.ts @@ -133,7 +133,7 @@ class UI5ElementMetadata { */ hasAttribute(propName: string): boolean { const propData = this.getProperties()[propName]; - return propData.type !== Object && propData.type !== Array && !propData.noAttribute; + return propData.type !== Object && !propData.noAttribute; } /** diff --git a/packages/compat/src/Table.ts b/packages/compat/src/Table.ts index f2406ca98167..7f7def495ae8 100644 --- a/packages/compat/src/Table.ts +++ b/packages/compat/src/Table.ts @@ -359,7 +359,7 @@ class Table extends UI5Element { @property() accessibleNameRef?: string; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _hiddenColumns?: Array; @property({ type: Boolean }) diff --git a/packages/compat/src/TableGroupRow.ts b/packages/compat/src/TableGroupRow.ts index 7e7713383d3a..34350f52f426 100644 --- a/packages/compat/src/TableGroupRow.ts +++ b/packages/compat/src/TableGroupRow.ts @@ -56,7 +56,7 @@ class TableGroupRow extends UI5Element implements ITableRow { @property() mode: `${TableMode}` = "None"; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _columnsInfo?: Array; @property() diff --git a/packages/compat/src/TableRow.ts b/packages/compat/src/TableRow.ts index 78ed73f98f40..c4916ff26cc9 100644 --- a/packages/compat/src/TableRow.ts +++ b/packages/compat/src/TableRow.ts @@ -171,7 +171,7 @@ class TableRow extends UI5Element implements ITableRow { @property({ type: Boolean }) active = false; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _columnsInfo?: Array; @property() diff --git a/packages/fiori/src/FlexibleColumnLayout.ts b/packages/fiori/src/FlexibleColumnLayout.ts index 365c48f9c11c..0c47db39d1e2 100644 --- a/packages/fiori/src/FlexibleColumnLayout.ts +++ b/packages/fiori/src/FlexibleColumnLayout.ts @@ -308,7 +308,7 @@ class FlexibleColumnLayout extends UI5Element { * @default undefined * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _columnLayout?: FlexibleColumnLayoutColumnLayout; /** diff --git a/packages/fiori/src/Wizard.ts b/packages/fiori/src/Wizard.ts index be4d7aa9dc73..d26f4914547b 100644 --- a/packages/fiori/src/Wizard.ts +++ b/packages/fiori/src/Wizard.ts @@ -245,7 +245,7 @@ class Wizard extends UI5Element { * Stores references to the grouped steps. * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _groupedTabs: Array = []; /** diff --git a/packages/main/cypress/specs/MultiComboBox.cy.tsx b/packages/main/cypress/specs/MultiComboBox.cy.tsx index c64b42ceb8aa..e557a58e4ef8 100644 --- a/packages/main/cypress/specs/MultiComboBox.cy.tsx +++ b/packages/main/cypress/specs/MultiComboBox.cy.tsx @@ -815,6 +815,242 @@ describe("General", () => { .should("have.text", resourceBundle.getText(MULTIINPUT_SHOW_MORE_TOKENS.defaultText, 1)); }) }); + + it("preselects items based on selectedValues property", () => { + cy.mount( + + + + + + ); + + cy.get("ui5-multi-combobox") + .should("have.attr", "selected-values",'["al","en"]'); + + cy.get("[ui5-mcb-item]") + .eq(0) + .should("be.selected"); + + cy.get("[ui5-mcb-item]") + .eq(2) + .should("be.selected"); + + cy.get("[ui5-multi-combobox]") + .as("mcb") + .shadow() + .find("[ui5-tokenizer]") + .as("tokenizer"); + + cy.get("@tokenizer") + .find("[ui5-token]") + .should("have.length", "2"); + }); + + it("updates selectedValues when a token is deleted", () => { + cy.mount( + + + + + + ); + + cy.get("[ui5-mcb-item]") + .eq(1) + .should("be.selected"); + + cy.get("[ui5-mcb-item]") + .eq(2) + .should("be.selected"); + + cy.get("[ui5-multi-combobox]") + .as("mcb") + .shadow() + .find("[ui5-tokenizer]") + .as("tokenizer"); + + cy.get("@tokenizer") + .find("[ui5-token]") + .eq(1) + .realClick(); + + cy.realPress("Backspace"); + + cy.get("@tokenizer") + .find("[ui5-token]") + .should("have.length", "1"); + + cy.get("[ui5-multi-combobox]") + .should("have.attr", "selected-values", '["dk"]'); + }); + + it("updates selectedValues when selecting items via checkbox", () => { + cy.mount( + + + + + + + ); + + cy.get("[ui5-multi-combobox]") + .as("mcb") + .should("have.attr", "selected-values", '[]'); + + // Open the dropdown + cy.get("@mcb") + .shadow() + .find("[ui5-icon][name='slim-arrow-down']") + .realClick(); + + // Select first item via checkbox + cy.get("[ui5-mcb-item]") + .eq(0) + .shadow() + .find("[ui5-checkbox]") + .realClick(); + + cy.get("@mcb") + .should("have.attr", "selected-values", '["DE"]'); + + // Select second item via checkbox + cy.get("[ui5-mcb-item]") + .eq(1) + .shadow() + .find("[ui5-checkbox]") + .realClick(); + + cy.get("@mcb") + .should("have.attr", "selected-values", '["DE","FR"]'); + + // Select third and fourth items + cy.get("[ui5-mcb-item]") + .eq(2) + .shadow() + .find("[ui5-checkbox]") + .realClick(); + + cy.get("[ui5-mcb-item]") + .eq(3) + .shadow() + .find("[ui5-checkbox]") + .realClick(); + + cy.get("@mcb") + .should("have.attr", "selected-values", '["DE","FR","IT","US"]'); + }); + + it("selects correct items when selectedValues is set before items are added", () => { + // First mount with selectedValues but no items + cy.mount( + + ); + + cy.get("[ui5-multi-combobox]") + .as("mcb") + .should("have.attr", "selected-values", '["FR","US"]'); + + // No tokens yet since no items + cy.get("@mcb") + .shadow() + .find("[ui5-tokenizer]") + .find("[ui5-token]") + .should("have.length", 0); + + // Now add items dynamically + cy.get("@mcb").then($mcb => { + const mcb = $mcb[0]; + + const items = [ + { text: "Germany", value: "DE" }, + { text: "France", value: "FR" }, + { text: "Italy", value: "IT" }, + { text: "United States", value: "US" }, + ]; + + items.forEach(item => { + const mcbItem = document.createElement("ui5-mcb-item"); + mcbItem.setAttribute("text", item.text); + mcbItem.setAttribute("value", item.value); + mcb.appendChild(mcbItem); + }); + }); + + // Verify items with matching values are now selected + cy.get("[ui5-mcb-item]") + .eq(1) // France + .should("have.attr", "selected"); + + cy.get("[ui5-mcb-item]") + .eq(3) // United States + .should("have.attr", "selected"); + + // Verify non-matching items are not selected + cy.get("[ui5-mcb-item]") + .eq(0) // Germany + .should("not.have.attr", "selected"); + + cy.get("[ui5-mcb-item]") + .eq(2) // Italy + .should("not.have.attr", "selected"); + + // Verify tokens are created + cy.get("@mcb") + .shadow() + .find("[ui5-tokenizer]") + .find("[ui5-token]") + .should("have.length", 2); + }); + + it("updates selectedValues when selecting item via Enter key (typeahead)", () => { + cy.mount( + + + + + + + ); + + cy.get("[ui5-multi-combobox]") + .as("mcb") + .should("have.attr", "selected-values", "[]"); + + // Type "Ca" to trigger typeahead for Canada + cy.get("@mcb") + .shadow() + .find("input") + .realClick() + .realType("Ca"); + + // Press Enter to select the autocompleted item + cy.realPress("Enter"); + + // Verify selectedValues is updated + cy.get("@mcb") + .should("have.attr", "selected-values", '["CA"]'); + + // Verify token is created + cy.get("@mcb") + .shadow() + .find("[ui5-tokenizer]") + .find("[ui5-token]") + .should("have.length", 1); + + // Type "Ja" to select Japan + cy.get("@mcb") + .shadow() + .find("input") + .realType("Ja"); + + cy.realPress("Enter"); + + // Verify selectedValues now has both values + cy.get("@mcb") + .should("have.attr", "selected-values", '["CA","JP"]'); + }); }); describe("MultiComboBox Truncated Tokens", () => { @@ -2225,6 +2461,48 @@ describe("Event firing", () => { cy.get("@valueStateChangeEvent") .should("have.been.calledTwice"); }); + + it("fires selection-change and updates selectedValues on token deletion", () => { + const selectionChangeSpy = cy.stub().as("selectionChangeSpy"); + cy.mount( + + + + + + ); + + cy.get("[ui5-multi-combobox]") + .as("mcb") + .shadow() + .find("[ui5-tokenizer]") + .as("tokenizer"); + + cy.get("@tokenizer") + .find("[ui5-token]") + .eq(0) + .realClick(); + + cy.realPress("ArrowRight"); + cy.get("@tokenizer") + .find("[ui5-token]") + .eq(1) + .should("be.focused"); + + cy.realPress("Space"); + cy.realPress("Backspace"); + + cy.get("@tokenizer") + .should("be.empty"); + + cy.get("@selectionChangeSpy") + .should("have.been.calledOnce"); + cy.get("@selectionChangeSpy").should('have.been.calledWithMatch', Cypress.sinon.match(event => { + return event.detail.item === undefined; + })); + cy.get("[ui5-multi-combobox]") + .should("have.attr", "selected-values", '[]'); + }); }); describe("MultiComboBox RTL/LTR Arrow Navigation", () => { diff --git a/packages/main/src/ComboBox.ts b/packages/main/src/ComboBox.ts index a5c814721e51..7dc7dac43931 100644 --- a/packages/main/src/ComboBox.ts +++ b/packages/main/src/ComboBox.ts @@ -366,7 +366,7 @@ class ComboBox extends UI5Element implements IFormInputElement { @property({ type: Boolean, noAttribute: true }) _iconPressed = false; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _filteredItems: Array = []; @property({ type: Number, noAttribute: true }) @@ -403,7 +403,7 @@ class ComboBox extends UI5Element implements IFormInputElement { /** * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _linksListenersArray: Array<(args: any) => void> = []; /** diff --git a/packages/main/src/DayPicker.ts b/packages/main/src/DayPicker.ts index 6b39305e959a..6fad0fa4e087 100644 --- a/packages/main/src/DayPicker.ts +++ b/packages/main/src/DayPicker.ts @@ -141,7 +141,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker { * An array of UTC timestamps representing the selected date or dates depending on the capabilities of the picker component. * @default [] */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) selectedDates: Array = []; /** @@ -170,10 +170,10 @@ class DayPicker extends CalendarPart implements ICalendarPicker { /** * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _weeks: Array = []; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _dayNames: Array = []; /** @@ -187,14 +187,14 @@ class DayPicker extends CalendarPart implements ICalendarPicker { * When selectionMode="Range" and the first day in the range is selected, this is the currently hovered (when using mouse) or focused (when using keyboard) day by the user * @private */ - @property({ type: Number }) + @property({ type: Number, noAttribute: true }) _secondTimestamp?: number; /** * Array of special calendar dates (if such are passed) from the calendar. * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) specialCalendarDates: Array = []; /** @@ -202,7 +202,7 @@ class DayPicker extends CalendarPart implements ICalendarPicker { * Each range can have a start and/or end date value. * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) disabledDates: Array = []; @query("[data-sap-focus-ref]") diff --git a/packages/main/src/Icon.ts b/packages/main/src/Icon.ts index 9c997af0e1ee..b4bad67ba381 100644 --- a/packages/main/src/Icon.ts +++ b/packages/main/src/Icon.ts @@ -197,7 +197,7 @@ class Icon extends UI5Element implements IIcon { /** * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) pathData: Array = []; /** diff --git a/packages/main/src/Input.ts b/packages/main/src/Input.ts index 9eac8282caa2..335175c39cba 100644 --- a/packages/main/src/Input.ts +++ b/packages/main/src/Input.ts @@ -569,7 +569,7 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement /** * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _linksListenersArray: Array<(args: any) => void> = []; /** diff --git a/packages/main/src/MonthPicker.ts b/packages/main/src/MonthPicker.ts index 812f4540badd..881ab8d9bdc9 100644 --- a/packages/main/src/MonthPicker.ts +++ b/packages/main/src/MonthPicker.ts @@ -101,7 +101,7 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { * or dates depending on the capabilities of the picker component. * @default [] */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) selectedDates: Array = []; /** @@ -118,7 +118,7 @@ class MonthPicker extends CalendarPart implements ICalendarPicker { @property() selectionMode: `${CalendarSelectionMode}` = "Single"; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _monthsInterval: MonthInterval = []; @property({ type: Boolean, noAttribute: true }) diff --git a/packages/main/src/MultiComboBox.ts b/packages/main/src/MultiComboBox.ts index e4713b23a486..d49fe4c57ca2 100644 --- a/packages/main/src/MultiComboBox.ts +++ b/packages/main/src/MultiComboBox.ts @@ -121,6 +121,7 @@ import type InputComposition from "./features/InputComposition.js"; */ interface IMultiComboBoxItem extends UI5Element { text?: string, + value?: string, additionalText?: string, headerText?: string, selected: boolean, @@ -293,6 +294,15 @@ class MultiComboBox extends UI5Element implements IFormInputElement { @property() value = ""; + /** + * Defines the values of the selected items. + * @default [] + * @public + * @since 2.19.0 + */ + @property({ type: Array }) + selectedValues:Array = []; + /** * Determines the name by which the component will be identified upon submission in an HTML form. * @@ -448,10 +458,10 @@ class MultiComboBox extends UI5Element implements IFormInputElement { @property() _valueBeforeOpen = this.value; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _filteredItems!: Array; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _previouslySelectedItems!: Array; @property({ type: Boolean }) @@ -503,7 +513,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { /** * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _linksListenersArray: Array<(args: any) => void> = []; /** @@ -548,7 +558,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { @slot() valueStateMessage!: Slot; - selectedValues: Array; + // selectedValues: Array; _inputLastValue: string; _deleting: boolean; _validationTimeout: Timeout | null; @@ -612,7 +622,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { this._filteredItems = []; this.selectedItems = []; this._previouslySelectedItems = []; - this.selectedValues = []; + // this.selectedValues = []; this._itemsBeforeOpen = []; this._inputLastValue = ""; this._deleting = false; @@ -807,6 +817,11 @@ class MultiComboBox extends UI5Element implements IFormInputElement { item.selected = false; }); + if (this.selectedValues) { + const valuesToDelete = deletingItems.map(item => item.value); + this.selectedValues = this.selectedValues.filter(value => !valuesToDelete.includes(value)); + } + this._deleting = true; this._preventTokenizerToggle = true; @@ -1429,6 +1444,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { }); } else { this._previouslySelectedItems = this._getSelectedItems(); + const previousSelectedValues = [...this.selectedValues]; matchingItem.selected = true; this.value = ""; // during composition prevent _inputLiveChange for proper input clearing @@ -1436,9 +1452,14 @@ class MultiComboBox extends UI5Element implements IFormInputElement { this._suppressNextLiveChange = true; } + if (this.selectedValues && matchingItem.value) { + this.selectedValues = [...this.selectedValues, matchingItem.value]; + } + const changePrevented = this.fireSelectionChange(); if (changePrevented) { + this.selectedValues = previousSelectedValues; this._revertSelection(); } } @@ -1573,8 +1594,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { _getSelectedItems(): Array { // Angular 2 way data binding - this.selectedValues = this._getItems().filter(item => item.selected); - return this.selectedValues as Array; + return this._getItems().filter(item => item.selected) as Array; } _listSelectionChange(e: CustomEvent) { @@ -1591,6 +1611,14 @@ class MultiComboBox extends UI5Element implements IFormInputElement { // don't call selection change right after selection as user can cancel it on phone if (!isPhone()) { + if (this.selectedValues) { + // Get values from all selected items (not just filtered ones) + this.selectedValues = this._getItems() + .filter((i): i is MultiComboBoxItem => isInstanceOfMultiComboBoxItem(i) && i.selected) + .map(i => i.value) + .filter((v): v is string => !!v); + } + changePrevented = this.fireSelectionChange(); if (changePrevented) { @@ -1782,6 +1810,15 @@ class MultiComboBox extends UI5Element implements IFormInputElement { }); } + _syncSelection() { + // set selected property of the items based on the selection value + this._getItems().forEach(item => { + if (isInstanceOfMultiComboBoxItem(item) && item.value) { + item.selected = this.selectedValues.includes(item.value); + } + }); + } + onBeforeRendering() { const input = this._innerInput; const autoCompletedChars = input && (input.selectionEnd || 0) - (input.selectionStart || 0); @@ -1800,6 +1837,10 @@ class MultiComboBox extends UI5Element implements IFormInputElement { this._filteredItems = this._getItems(); } + if (this.selectedValues) { + this._syncSelection(); + } + this.tokenizerAvailable = this._getSelectedItems().length > 0; this.style.setProperty("--_ui5-input-icons-count", `${this.iconsCount}`); @@ -2152,7 +2193,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { return; } - return getTokensCountText(this.selectedValues.length); + return getTokensCountText(this._getSelectedItems().length); } get _tokensCountTextId() { diff --git a/packages/main/src/MultiComboBoxItem.ts b/packages/main/src/MultiComboBoxItem.ts index 2f671bc48acf..869585e8f7d8 100644 --- a/packages/main/src/MultiComboBoxItem.ts +++ b/packages/main/src/MultiComboBoxItem.ts @@ -48,6 +48,19 @@ class MultiComboBoxItem extends ComboBoxItem implements IMultiComboBoxItem { @property({ type: Boolean }) declare selected: boolean; + /** + * Defines the value of the component. + * + * Use this property to associate a unique identifier with the item, + * separate from the display text. This enables selecting items + * programmatically via `selectedValues` on the parent MultiComboBox. + * @default undefined + * @public + * @since 2.19.0 + */ + @property() + value?: string; + /** * Defines whether the item is filtered * @private diff --git a/packages/main/src/RatingIndicator.ts b/packages/main/src/RatingIndicator.ts index 976dee370ed9..ff7cf099268b 100644 --- a/packages/main/src/RatingIndicator.ts +++ b/packages/main/src/RatingIndicator.ts @@ -199,7 +199,7 @@ class RatingIndicator extends UI5Element { /** * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _stars: Array = []; /** diff --git a/packages/main/src/TabContainer.ts b/packages/main/src/TabContainer.ts index 58805fae48ee..1804260e55f5 100644 --- a/packages/main/src/TabContainer.ts +++ b/packages/main/src/TabContainer.ts @@ -306,7 +306,7 @@ class TabContainer extends UI5Element { @property({ noAttribute: true }) _endOverflowText = "More"; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _popoverItemsFlat: Array = []; @property({ type: Number, noAttribute: true }) diff --git a/packages/main/src/TextArea.ts b/packages/main/src/TextArea.ts index 9de830fe48f8..31502fa2842c 100644 --- a/packages/main/src/TextArea.ts +++ b/packages/main/src/TextArea.ts @@ -311,7 +311,7 @@ class TextArea extends UI5Element implements IFormInputElement { /** * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _mirrorText: IndexedTokenizedText = []; /** diff --git a/packages/main/src/TimePickerClock.ts b/packages/main/src/TimePickerClock.ts index 92c4e5ef1de3..993ac587e648 100644 --- a/packages/main/src/TimePickerClock.ts +++ b/packages/main/src/TimePickerClock.ts @@ -179,7 +179,7 @@ class TimePickerClock extends UI5Element { /** * Defines the currently available Time Picker Clock items depending on Clock setup. */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _items: Array = []; /** diff --git a/packages/main/src/TimePickerInternals.ts b/packages/main/src/TimePickerInternals.ts index 2b928bb65bd4..fc69240521d9 100644 --- a/packages/main/src/TimePickerInternals.ts +++ b/packages/main/src/TimePickerInternals.ts @@ -134,7 +134,7 @@ class TimePickerInternals extends UI5Element { * Contains currently available Time Picker components depending on time format. * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _entities: Array = []; /** @@ -148,7 +148,7 @@ class TimePickerInternals extends UI5Element { * Contains currently available Button components depending on time format. * @private */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _periods: Array = []; /** diff --git a/packages/main/src/YearPicker.ts b/packages/main/src/YearPicker.ts index e837ae2e7232..c487eb23daf6 100644 --- a/packages/main/src/YearPicker.ts +++ b/packages/main/src/YearPicker.ts @@ -97,7 +97,7 @@ class YearPicker extends CalendarPart implements ICalendarPicker { * or dates depending on the capabilities of the picker component. * @default [] */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) selectedDates: Array = []; /** @@ -114,7 +114,7 @@ class YearPicker extends CalendarPart implements ICalendarPicker { @property() selectionMode: `${CalendarSelectionMode}` = "Single"; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _yearsInterval: YearInterval = []; @property({ type: Boolean, noAttribute: true }) diff --git a/packages/main/src/YearRangePicker.ts b/packages/main/src/YearRangePicker.ts index 07b6086cca6e..6494deb8a3ca 100644 --- a/packages/main/src/YearRangePicker.ts +++ b/packages/main/src/YearRangePicker.ts @@ -97,7 +97,7 @@ class YearRangePicker extends CalendarPart implements ICalendarPicker { * or dates depending on the capabilities of the picker component. * @default [] */ - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) selectedDates: Array = []; /** @@ -117,7 +117,7 @@ class YearRangePicker extends CalendarPart implements ICalendarPicker { @property({ type: Number }) _secondTimestamp?: number; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _yearRanges: YearRanges = []; @property({ type: Boolean, noAttribute: true }) diff --git a/packages/main/test/pages/MultiComboBox.html b/packages/main/test/pages/MultiComboBox.html index 082c29af7a2a..c4cccf21bfb1 100644 --- a/packages/main/test/pages/MultiComboBox.html +++ b/packages/main/test/pages/MultiComboBox.html @@ -630,6 +630,63 @@

MultiComboBox in Compact

+
+ MultiInput with selectedValues + + + + + + + +
+ +
+ Dynamic selectedValues test +
+ Select All + Clear All + Select Europe +
+ + + + + + + + +
Selected values: []
+ +
+
diff --git a/packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBox.mdx b/packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBox.mdx index 94d59434f27f..6061fde837e9 100644 --- a/packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBox.mdx +++ b/packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBox.mdx @@ -8,6 +8,8 @@ import MultiComboBoxCustomValue from "../../../_samples/main/MultiComboBox/Multi import MultiComboBoxGrouping from "../../../_samples/main/MultiComboBox/MultiComboBoxGrouping/MultiComboBoxGrouping.md"; import MultiComboBoxSelectAll from "../../../_samples/main/MultiComboBox/MultiComboBoxSelectAll/MultiComboBoxSelectAll.md"; import SuggestionsWrapping from "../../../_samples/main/MultiComboBox/SuggestionsWrapping/SuggestionsWrapping.md"; +import SelectedValues from "../../../_samples/main/MultiComboBox/SelectedValues/SelectedValues.md"; +import SelectedValuesDynamic from "../../../_samples/main/MultiComboBox/SelectedValuesDynamic/SelectedValuesDynamic.md"; <%COMPONENT_OVERVIEW%> @@ -18,6 +20,17 @@ import SuggestionsWrapping from "../../../_samples/main/MultiComboBox/Suggestion ## More Samples +### Programmatic Selection with selectedValues +Use the selectedValues property to pre-select items by their value attribute. +Each item must have a corresponding value attribute that matches the values in the array. + + + +### Dynamic Selection Manipulation +The selectedValues property can be updated programmatically to change the selection. + + + ### Clear Icon The MultiComboBox can show a clear icon, visble when there is a value, typed by the user. When pressed, the value gets cleared. diff --git a/packages/website/docs/_samples/main/MultiComboBox/SelectedValues/SelectedValues.md b/packages/website/docs/_samples/main/MultiComboBox/SelectedValues/SelectedValues.md new file mode 100644 index 000000000000..17798ecc59ab --- /dev/null +++ b/packages/website/docs/_samples/main/MultiComboBox/SelectedValues/SelectedValues.md @@ -0,0 +1,4 @@ +import html from '!!raw-loader!./sample.html'; +import js from '!!raw-loader!./main.js'; + + diff --git a/packages/website/docs/_samples/main/MultiComboBox/SelectedValues/main.js b/packages/website/docs/_samples/main/MultiComboBox/SelectedValues/main.js new file mode 100644 index 000000000000..7013db256e76 --- /dev/null +++ b/packages/website/docs/_samples/main/MultiComboBox/SelectedValues/main.js @@ -0,0 +1,2 @@ +import "@ui5/webcomponents/dist/MultiComboBox.js"; +import "@ui5/webcomponents/dist/MultiComboBoxItem.js"; diff --git a/packages/website/docs/_samples/main/MultiComboBox/SelectedValues/sample.html b/packages/website/docs/_samples/main/MultiComboBox/SelectedValues/sample.html new file mode 100644 index 000000000000..1f84a2e949a6 --- /dev/null +++ b/packages/website/docs/_samples/main/MultiComboBox/SelectedValues/sample.html @@ -0,0 +1,26 @@ + + + + + + + + Sample + + + + + + + + + + + + + + + + + + diff --git a/packages/website/docs/_samples/main/MultiComboBox/SelectedValuesDynamic/SelectedValuesDynamic.md b/packages/website/docs/_samples/main/MultiComboBox/SelectedValuesDynamic/SelectedValuesDynamic.md new file mode 100644 index 000000000000..17798ecc59ab --- /dev/null +++ b/packages/website/docs/_samples/main/MultiComboBox/SelectedValuesDynamic/SelectedValuesDynamic.md @@ -0,0 +1,4 @@ +import html from '!!raw-loader!./sample.html'; +import js from '!!raw-loader!./main.js'; + + diff --git a/packages/website/docs/_samples/main/MultiComboBox/SelectedValuesDynamic/main.js b/packages/website/docs/_samples/main/MultiComboBox/SelectedValuesDynamic/main.js new file mode 100644 index 000000000000..1fd03cf981f6 --- /dev/null +++ b/packages/website/docs/_samples/main/MultiComboBox/SelectedValuesDynamic/main.js @@ -0,0 +1,31 @@ +import "@ui5/webcomponents/dist/MultiComboBox.js"; +import "@ui5/webcomponents/dist/MultiComboBoxItem.js"; +import "@ui5/webcomponents/dist/Button.js"; +import "@ui5/webcomponents/dist/Label.js"; + +const mcb = document.getElementById("mcb"); +const output = document.getElementById("output"); + +const europeanCountries = ["DE", "FR", "IT"]; +const allCountries = ["DE", "FR", "IT", "US", "CA", "JP"]; + +function updateOutput() { + output.textContent = `Selected values: ${JSON.stringify(mcb.selectedValues)}`; +} + +mcb.addEventListener("selection-change", updateOutput); + +document.getElementById("btnSelectAll").addEventListener("click", () => { + mcb.selectedValues = [...allCountries]; + updateOutput(); +}); + +document.getElementById("btnClearAll").addEventListener("click", () => { + mcb.selectedValues = []; + updateOutput(); +}); + +document.getElementById("btnSelectEurope").addEventListener("click", () => { + mcb.selectedValues = [...europeanCountries]; + updateOutput(); +}); diff --git a/packages/website/docs/_samples/main/MultiComboBox/SelectedValuesDynamic/sample.html b/packages/website/docs/_samples/main/MultiComboBox/SelectedValuesDynamic/sample.html new file mode 100644 index 000000000000..334fda755bbb --- /dev/null +++ b/packages/website/docs/_samples/main/MultiComboBox/SelectedValuesDynamic/sample.html @@ -0,0 +1,36 @@ + + + + + + + + Sample + + + + + +
+ Select All + Clear All + Select Europe +
+ + + + + + + + + + + Selected values: [] + + + + + + +