From b9a55d634bdb62f4bcc533340dd8ef2527acbc8d Mon Sep 17 00:00:00 2001 From: Milen Karmidzhanov Date: Thu, 4 Jun 2026 11:18:12 +0300 Subject: [PATCH 01/10] feat(ui5-comobobox, ui5-multi-combobox): add custom items --- packages/main/cypress/specs/ComboBox.cy.tsx | 217 +++++++++++++ .../main/cypress/specs/MultiComboBox.cy.tsx | 292 ++++++++++++++++++ packages/main/src/ComboBox.ts | 1 + packages/main/src/ComboBoxItemCustom.ts | 82 +++++ .../main/src/ComboBoxItemCustomTemplate.tsx | 10 + packages/main/src/MultiComboBox.ts | 1 + packages/main/src/MultiComboBoxItemCustom.ts | 89 ++++++ .../src/MultiComboBoxItemCustomTemplate.tsx | 22 ++ .../main/src/themes/ComboBoxItemCustom.css | 14 + .../src/themes/MultiComboBoxItemCustom.css | 19 ++ packages/main/test/pages/ComboBox.html | 53 ++++ .../main/ComboBox/CustomItems/CustomItems.md | 4 + .../main/ComboBox/CustomItems/main.js | 2 + .../main/ComboBox/CustomItems/sample.html | 34 ++ .../MultiComboBox/CustomItems/CustomItems.md | 4 + .../main/MultiComboBox/CustomItems/main.js | 2 + .../MultiComboBox/CustomItems/sample.html | 34 ++ 17 files changed, 880 insertions(+) create mode 100644 packages/main/src/ComboBoxItemCustom.ts create mode 100644 packages/main/src/ComboBoxItemCustomTemplate.tsx create mode 100644 packages/main/src/MultiComboBoxItemCustom.ts create mode 100644 packages/main/src/MultiComboBoxItemCustomTemplate.tsx create mode 100644 packages/main/src/themes/ComboBoxItemCustom.css create mode 100644 packages/main/src/themes/MultiComboBoxItemCustom.css create mode 100644 packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md create mode 100644 packages/website/docs/_samples/main/ComboBox/CustomItems/main.js create mode 100644 packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html create mode 100644 packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md create mode 100644 packages/website/docs/_samples/main/MultiComboBox/CustomItems/main.js create mode 100644 packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html diff --git a/packages/main/cypress/specs/ComboBox.cy.tsx b/packages/main/cypress/specs/ComboBox.cy.tsx index 0cd2803c939b..92f04e10217a 100644 --- a/packages/main/cypress/specs/ComboBox.cy.tsx +++ b/packages/main/cypress/specs/ComboBox.cy.tsx @@ -1,6 +1,7 @@ import ValueState from "@ui5/webcomponents-base/dist/types/ValueState.js"; import ComboBox from "../../src/ComboBox.js"; import ComboBoxItem from "../../src/ComboBoxItem.js"; +import ComboBoxItemCustom from "../../src/ComboBoxItemCustom.js"; import ComboBoxItemGroup from "../../src/ComboBoxItemGroup.js"; import ResponsivePopover from "../../src/ResponsivePopover.js"; import Link from "../../src/Link.js"; @@ -3992,3 +3993,219 @@ describe("Highlighting", () => { .should("contain.html", "AFR"); }); }); + +describe("ComboBoxItemCustom - Rendering", () => { + it("should render custom content correctly", () => { + cy.mount( + + + 🇩🇪 Germany + + + 🇫🇷 France + + + ); + + cy.get("[ui5-combobox]") + .as("combobox") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("[ui5-cbi-custom]").eq(0).shadow().find("li").should("contain.text", "🇩🇪 Germany"); + cy.get("[ui5-cbi-custom]").eq(1).shadow().find("li").should("contain.text", "🇫🇷 France"); + }); + + it("should mix regular and custom items", () => { + cy.mount( + + + + Custom Item + + + ); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("[ui5-cb-item]").should("have.length", 1); + cy.get("[ui5-cbi-custom]").should("have.length", 1); + }); + + it("should have role='option'", () => { + cy.mount( + + Test Item + + ); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("[ui5-cbi-custom]").shadow().find("li").should("have.attr", "role", "option"); + }); +}); + +describe("ComboBoxItemCustom - Filtering", () => { + it("should filter custom items by text property", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + 🇪🇸 Spain + + ); + + cy.get("[ui5-combobox]") + .as("combobox") + .realClick(); + + cy.get("@combobox").realPress("G"); + + cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "_isVisible", true); + cy.get("[ui5-cbi-custom]").eq(1).should("not.have.prop", "_isVisible", true); + cy.get("[ui5-cbi-custom]").eq(2).should("not.have.prop", "_isVisible", true); + }); + + it("should filter mixed regular and custom items", () => { + cy.mount( + + + 🇩🇪 Germany + + + ); + + cy.get("[ui5-combobox]") + .as("combobox") + .realClick(); + + cy.get("@combobox").realPress("G"); + + cy.get("[ui5-cb-item]").eq(0).should("not.have.prop", "_isVisible", true); + cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "_isVisible", true); + cy.get("[ui5-cb-item]").eq(1).should("not.have.prop", "_isVisible", true); + }); +}); + +describe("ComboBoxItemCustom - Selection", () => { + it("should select custom item on click", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + + ); + + cy.get("[ui5-combobox]") + .as("combobox") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("[ui5-cbi-custom]").eq(0).shadow().find("li").realClick(); + + cy.get("@combobox").should("have.prop", "value", "Germany"); + }); + + it("should select custom item with Enter key", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + + ); + + cy.get("[ui5-combobox]") + .as("combobox") + .realClick(); + + cy.get("@combobox").realPress("ArrowDown"); + cy.get("@combobox").realPress("Enter"); + + cy.get("@combobox").should("have.prop", "value", "Germany"); + }); + + it("should work with value property for programmatic selection", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + + ); + + cy.get("[ui5-combobox]").should("have.prop", "value", "France"); + cy.get("[ui5-combobox]").should("have.prop", "selectedValue", "FR"); + }); +}); + +describe("ComboBoxItemCustom - Navigation", () => { + it("should navigate through custom items with arrow keys", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + 🇪🇸 Spain + + ); + + cy.get("[ui5-combobox]") + .as("combobox") + .realClick(); + + cy.get("@combobox").realPress("ArrowDown"); + cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "focused", true); + + cy.get("@combobox").realPress("ArrowDown"); + cy.get("[ui5-cbi-custom]").eq(1).should("have.prop", "focused", true); + + cy.get("@combobox").realPress("ArrowUp"); + cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "focused", true); + }); + + it("should navigate through mixed items", () => { + cy.mount( + + + 🇩🇪 Germany + + + ); + + cy.get("[ui5-combobox]") + .as("combobox") + .realClick(); + + cy.get("@combobox").realPress("ArrowDown"); + cy.get("[ui5-cb-item]").eq(0).should("have.prop", "focused", true); + + cy.get("@combobox").realPress("ArrowDown"); + cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "focused", true); + + cy.get("@combobox").realPress("ArrowDown"); + cy.get("[ui5-cb-item]").eq(1).should("have.prop", "focused", true); + }); +}); + +describe("ComboBoxItemCustom - Accessibility", () => { + it("should have correct tabindex", () => { + cy.mount( + + Test Item + + ); + + cy.get("[ui5-combobox]") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("[ui5-cbi-custom]").shadow().find("li").should("not.have.attr", "tabindex", "0"); + }); +}); diff --git a/packages/main/cypress/specs/MultiComboBox.cy.tsx b/packages/main/cypress/specs/MultiComboBox.cy.tsx index 1ebf822ad04d..884ec158fbeb 100644 --- a/packages/main/cypress/specs/MultiComboBox.cy.tsx +++ b/packages/main/cypress/specs/MultiComboBox.cy.tsx @@ -1,5 +1,6 @@ import MultiComboBox from "../../src/MultiComboBox.js"; import MultiComboBoxItem from "../../src/MultiComboBoxItem.js"; +import MultiComboBoxItemCustom from "../../src/MultiComboBoxItemCustom.js"; import MultiComboBoxItemGroup from "../../src/MultiComboBoxItemGroup.js"; import ResponsivePopover from "../../src/ResponsivePopover.js"; import Button from "../../src/Button.js"; @@ -5143,3 +5144,294 @@ describe("Validation inside a form", () => { .should("have.been.calledOnce"); }); }); + +describe("MultiComboBoxItemCustom - Rendering", () => { + it("should render custom content correctly", () => { + cy.mount( + + + 🇩🇪 Germany + + + 🇫🇷 France + + + ); + + cy.get("[ui5-multi-combobox]") + .as("multiCombobox") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("[ui5-mcbi-custom]").eq(0).shadow().find(".ui5-li-content").should("contain.text", "🇩🇪 Germany"); + cy.get("[ui5-mcbi-custom]").eq(1).shadow().find(".ui5-li-content").should("contain.text", "🇫🇷 France"); + }); + + it("should render checkbox for custom items", () => { + cy.mount( + + 🇩🇪 Germany + + ); + + cy.get("[ui5-multi-combobox]") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("[ui5-mcbi-custom]").shadow().find("[ui5-checkbox]").should("exist"); + }); + + it("should mix regular and custom items", () => { + cy.mount( + + + + Custom Item + + + ); + + cy.get("[ui5-multi-combobox]") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("[ui5-mcb-item]").should("have.length", 1); + cy.get("[ui5-mcbi-custom]").should("have.length", 1); + }); +}); + +describe("MultiComboBoxItemCustom - Filtering", () => { + it("should filter custom items by text property", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + 🇪🇸 Spain + + ); + + cy.get("[ui5-multi-combobox]") + .as("multiCombobox") + .realClick(); + + cy.get("@multiCombobox").realPress("G"); + + cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "_isVisible", true); + cy.get("[ui5-mcbi-custom]").eq(1).should("not.have.prop", "_isVisible", true); + cy.get("[ui5-mcbi-custom]").eq(2).should("not.have.prop", "_isVisible", true); + }); + + it("should filter mixed regular and custom items", () => { + cy.mount( + + + 🇩🇪 Germany + + + ); + + cy.get("[ui5-multi-combobox]") + .as("multiCombobox") + .realClick(); + + cy.get("@multiCombobox").realPress("G"); + + cy.get("[ui5-mcb-item]").eq(0).should("not.have.prop", "_isVisible", true); + cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "_isVisible", true); + cy.get("[ui5-mcb-item]").eq(1).should("not.have.prop", "_isVisible", true); + }); +}); + +describe("MultiComboBoxItemCustom - Selection", () => { + it("should select custom item via checkbox click", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + + ); + + cy.get("[ui5-multi-combobox]") + .as("multiCombobox") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("[ui5-mcbi-custom]").eq(0).shadow().find("[ui5-checkbox]").realClick(); + + cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", true); + cy.get("@multiCombobox").shadow().find("[ui5-token]").should("have.length", 1); + }); + + it("should select multiple custom items", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + 🇪🇸 Spain + + ); + + cy.get("[ui5-multi-combobox]") + .as("multiCombobox") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("[ui5-mcbi-custom]").eq(0).shadow().find("[ui5-checkbox]").realClick(); + cy.get("[ui5-mcbi-custom]").eq(1).shadow().find("[ui5-checkbox]").realClick(); + + cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", true); + cy.get("[ui5-mcbi-custom]").eq(1).should("have.prop", "selected", true); + cy.get("@multiCombobox").shadow().find("[ui5-token]").should("have.length", 2); + }); + + it("should work with selectedValues property", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + 🇪🇸 Spain + + ); + + cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", true); + cy.get("[ui5-mcbi-custom]").eq(1).should("have.prop", "selected", true); + cy.get("[ui5-mcbi-custom]").eq(2).should("have.prop", "selected", false); + + cy.get("[ui5-multi-combobox]").shadow().find("[ui5-token]").should("have.length", 2); + }); +}); + +describe("MultiComboBoxItemCustom - Tokens", () => { + it("should display token with text property", () => { + cy.mount( + + 🇩🇪 Germany + + ); + + cy.get("[ui5-multi-combobox]") + .shadow() + .find("[ui5-token]") + .should("have.length", 1) + .and("have.prop", "text", "Germany"); + }); + + it("should display tokens for multiple selected items", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + + ); + + cy.get("[ui5-multi-combobox]") + .as("multiCombobox") + .shadow() + .find("[ui5-token]") + .should("have.length", 2); + + cy.get("@multiCombobox").shadow().find("[ui5-token]").eq(0).should("have.prop", "text", "Germany"); + cy.get("@multiCombobox").shadow().find("[ui5-token]").eq(1).should("have.prop", "text", "France"); + }); + + it("should remove selection when token is deleted", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + + ); + + cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", true); + + cy.get("[ui5-multi-combobox]") + .shadow() + .find("[ui5-token]") + .eq(0) + .shadow() + .find(".ui5-token-icon") + .realClick(); + + cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", false); + cy.get("[ui5-multi-combobox]").shadow().find("[ui5-token]").should("have.length", 0); + }); +}); + +describe("MultiComboBoxItemCustom - Navigation", () => { + it("should navigate through custom items with arrow keys", () => { + cy.mount( + + 🇩🇪 Germany + 🇫🇷 France + 🇪🇸 Spain + + ); + + cy.get("[ui5-multi-combobox]") + .as("multiCombobox") + .realClick(); + + cy.get("@multiCombobox").realPress("ArrowDown"); + cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "focused", true); + + cy.get("@multiCombobox").realPress("ArrowDown"); + cy.get("[ui5-mcbi-custom]").eq(1).should("have.prop", "focused", true); + + cy.get("@multiCombobox").realPress("ArrowUp"); + cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "focused", true); + }); + + it("should navigate through mixed items", () => { + cy.mount( + + + 🇩🇪 Germany + + + ); + + cy.get("[ui5-multi-combobox]") + .as("multiCombobox") + .realClick(); + + cy.get("@multiCombobox").realPress("ArrowDown"); + cy.get("[ui5-mcb-item]").eq(0).should("have.prop", "focused", true); + + cy.get("@multiCombobox").realPress("ArrowDown"); + cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "focused", true); + + cy.get("@multiCombobox").realPress("ArrowDown"); + cy.get("[ui5-mcb-item]").eq(1).should("have.prop", "focused", true); + }); +}); + +describe("MultiComboBoxItemCustom - Mixed Selection", () => { + it("should select both regular and custom items", () => { + cy.mount( + + + 🇩🇪 Germany + + + ); + + cy.get("[ui5-multi-combobox]") + .as("multiCombobox") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("[ui5-mcb-item]").eq(0).shadow().find("[ui5-checkbox]").realClick(); + cy.get("[ui5-mcbi-custom]").eq(0).shadow().find("[ui5-checkbox]").realClick(); + + cy.get("[ui5-mcb-item]").eq(0).should("have.prop", "selected", true); + cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", true); + + cy.get("@multiCombobox").shadow().find("[ui5-token]").should("have.length", 2); + }); +}); diff --git a/packages/main/src/ComboBox.ts b/packages/main/src/ComboBox.ts index c7bc268c62c0..f71adbedc173 100644 --- a/packages/main/src/ComboBox.ts +++ b/packages/main/src/ComboBox.ts @@ -79,6 +79,7 @@ import SuggestionsCss from "./generated/themes/Suggestions.css.js"; import "./ComboBoxItem.js"; import type ComboBoxItem from "./ComboBoxItem.js"; +import "./ComboBoxItemCustom.js"; import type Popover from "./Popover.js"; import type ResponsivePopover from "./ResponsivePopover.js"; import type List from "./List.js"; diff --git a/packages/main/src/ComboBoxItemCustom.ts b/packages/main/src/ComboBoxItemCustom.ts new file mode 100644 index 000000000000..ad1a5cd85f56 --- /dev/null +++ b/packages/main/src/ComboBoxItemCustom.ts @@ -0,0 +1,82 @@ +import property from "@ui5/webcomponents-base/dist/decorators/property.js"; +import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; +import slot from "@ui5/webcomponents-base/dist/decorators/slot-strict.js"; +import type { IComboBoxItem } from "./ComboBox.js"; +import ListItemBase from "./ListItemBase.js"; +import ComboBoxItemCustomTemplate from "./ComboBoxItemCustomTemplate.js"; +import styles from "./generated/themes/ComboBoxItemCustom.css.js"; +import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js"; + +/** + * @class + * The `ui5-cbi-custom` is type of combobox item, + * that can be used to place combobox items with custom content in the combobox. + * The text property is considered for filtering and autocomplete. + * + * @constructor + * @extends ListItemBase + * @implements {IComboBoxItem} + * @public + * @since 2.24.0 + */ +@customElement({ + tag: "ui5-cbi-custom", + template: ComboBoxItemCustomTemplate, + styles: [ + ListItemBase.styles, + styles, + ], +}) +class ComboBoxItemCustom extends ListItemBase implements IComboBoxItem { + eventDetails!: ListItemBase["eventDetails"]; + + /** + * Defines the text of the component. + * Used for filtering, autocomplete, and mobile rendering. + * @default undefined + * @public + * @since 2.24.0 + */ + @property() + text?: string; + + /** + * Defines the value of the component. + * Used for programmatic selection via selectedValue property. + * @default undefined + * @public + * @since 2.24.0 + */ + @property() + value?: string; + + /** + * Indicates whether the item is filtered. + * @private + */ + @property({ type: Boolean, noAttribute: true }) + _isVisible = false; + + /** + * Indicates whether the item is focused. + * @protected + */ + @property({ type: Boolean }) + focused = false; + + /** + * Defines the content of the component. + * @public + * @since 2.24.0 + */ + @slot({ type: Node, "default": true, invalidateOnChildChange: true }) + content!: DefaultSlot; + + get _effectiveTabIndex() { + return -1; + } +} + +ComboBoxItemCustom.define(); + +export default ComboBoxItemCustom; diff --git a/packages/main/src/ComboBoxItemCustomTemplate.tsx b/packages/main/src/ComboBoxItemCustomTemplate.tsx new file mode 100644 index 000000000000..5c8e2999889c --- /dev/null +++ b/packages/main/src/ComboBoxItemCustomTemplate.tsx @@ -0,0 +1,10 @@ +import ListItemBaseTemplate from "./ListItemBaseTemplate.js"; +import type ComboBoxItemCustom from "./ComboBoxItemCustom.js"; + +export default function ComboBoxItemCustomTemplate(this: ComboBoxItemCustom) { + return ListItemBaseTemplate.call(this, { listItemContent }, { role: "option" }); +} + +function listItemContent(this: ComboBoxItemCustom) { + return ; +} diff --git a/packages/main/src/MultiComboBox.ts b/packages/main/src/MultiComboBox.ts index 085142308936..e0e250a8421b 100644 --- a/packages/main/src/MultiComboBox.ts +++ b/packages/main/src/MultiComboBox.ts @@ -59,6 +59,7 @@ import arraysAreEqual from "@ui5/webcomponents-base/dist/util/arraysAreEqual.js" import { submitForm } from "@ui5/webcomponents-base/dist/features/InputElementsFormSupport.js"; import type { IFormInputElement } from "@ui5/webcomponents-base/dist/features/InputElementsFormSupport.js"; import MultiComboBoxItem, { isInstanceOfMultiComboBoxItem } from "./MultiComboBoxItem.js"; +import "./MultiComboBoxItemCustom.js"; import MultiComboBoxItemGroup, { isInstanceOfMultiComboBoxItemGroup } from "./MultiComboBoxItemGroup.js"; import ListItemGroup from "./ListItemGroup.js"; import Tokenizer, { getTokensCountText } from "./Tokenizer.js"; diff --git a/packages/main/src/MultiComboBoxItemCustom.ts b/packages/main/src/MultiComboBoxItemCustom.ts new file mode 100644 index 000000000000..1a5eee698dd0 --- /dev/null +++ b/packages/main/src/MultiComboBoxItemCustom.ts @@ -0,0 +1,89 @@ +import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js"; +import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; +import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js"; +import { + property, + eventStrict as event, +} from "@ui5/webcomponents-base/dist/decorators.js"; +import ComboBoxItemCustom from "./ComboBoxItemCustom.js"; +import CheckBox from "./CheckBox.js"; +import type { IMultiComboBoxItem } from "./MultiComboBox.js"; +import { + ARIA_LABEL_LIST_ITEM_CHECKBOX, +} from "./generated/i18n/i18n-defaults.js"; +import styles from "./generated/themes/MultiComboBoxItemCustom.css.js"; +import MultiComboBoxItemCustomTemplate from "./MultiComboBoxItemCustomTemplate.js"; +import type { SelectionRequestEventDetail } from "./ListItem.js"; +import type { AriaRole } from "@ui5/webcomponents-base"; + +/** + * @class + * The `ui5-mcbi-custom` is type of multi-combobox item, + * that can be used to place multi-combobox items with custom content. + * The text property is considered for filtering and token display. + * + * @constructor + * @extends ComboBoxItemCustom + * @implements {IMultiComboBoxItem} + * @public + * @since 2.24.0 + */ +@customElement({ + tag: "ui5-mcbi-custom", + template: MultiComboBoxItemCustomTemplate, + styles: [ComboBoxItemCustom.styles, styles], + dependencies: [...ComboBoxItemCustom.dependencies, CheckBox], +}) + +@event("selection-requested", { + bubbles: true, +}) +class MultiComboBoxItemCustom extends ComboBoxItemCustom implements IMultiComboBoxItem { + eventDetails!: ComboBoxItemCustom["eventDetails"] & { + "selection-requested": SelectionRequestEventDetail, + } + + /** + * Defines the selected state of the component. + * @default false + * @public + * @deprecated Set the `value` property on items and use the `selectedValues` property on the parent `ui5-multi-combobox` instead for programmatic selection. + */ + @property({ type: Boolean }) + selected = false; + + /** + * @private + */ + @property({ type: Boolean, noAttribute: true }) + _readonly = false; + + @i18n("@ui5/webcomponents") + static i18nBundle: I18nBundle; + + get isMultiComboBoxItem() { + return true; + } + + _onclick(e: MouseEvent) { + if ((e.target as HTMLElement)?.hasAttribute("ui5-checkbox")) { + return this.fireDecoratorEvent("selection-requested", { item: this, selected: (e.target as CheckBox).checked, selectionComponentPressed: true }); + } + + super._onclick(e); + } + + get _accessibleName() { + return MultiComboBoxItemCustom.i18nBundle.getText(ARIA_LABEL_LIST_ITEM_CHECKBOX); + } + + get checkBoxAccInfo() { + return { + role: "presentation" as AriaRole, + }; + } +} + +MultiComboBoxItemCustom.define(); + +export default MultiComboBoxItemCustom; diff --git a/packages/main/src/MultiComboBoxItemCustomTemplate.tsx b/packages/main/src/MultiComboBoxItemCustomTemplate.tsx new file mode 100644 index 000000000000..f6acf5bd381c --- /dev/null +++ b/packages/main/src/MultiComboBoxItemCustomTemplate.tsx @@ -0,0 +1,22 @@ +import CheckBox from "./CheckBox.js"; +import ListItemBaseTemplate from "./ListItemBaseTemplate.js"; +import type MultiComboBoxItemCustom from "./MultiComboBoxItemCustom.js"; + +export default function MultiComboBoxItemCustomTemplate(this: MultiComboBoxItemCustom) { + return ListItemBaseTemplate.call(this, { listItemContent }, { role: "option" }); +} + +function listItemContent(this: MultiComboBoxItemCustom) { + return ( + <> + +
+ +
+ + ); +} diff --git a/packages/main/src/themes/ComboBoxItemCustom.css b/packages/main/src/themes/ComboBoxItemCustom.css new file mode 100644 index 000000000000..06466771c5e8 --- /dev/null +++ b/packages/main/src/themes/ComboBoxItemCustom.css @@ -0,0 +1,14 @@ +:host([ui5-cbi-custom]) { + height: auto; + min-height: var(--_ui5_list_item_base_height); +} + +:host([ui5-cbi-custom]) .ui5-li-root { + min-height: var(--_ui5_list_item_base_height); +} + +:host([ui5-cbi-custom]) .ui5-li-content { + padding-bottom: .5rem; + padding-top: .5rem; + box-sizing: border-box; +} diff --git a/packages/main/src/themes/MultiComboBoxItemCustom.css b/packages/main/src/themes/MultiComboBoxItemCustom.css new file mode 100644 index 000000000000..7e6eb383c5ad --- /dev/null +++ b/packages/main/src/themes/MultiComboBoxItemCustom.css @@ -0,0 +1,19 @@ +:host([ui5-mcbi-custom]) { + height: auto; + min-height: var(--_ui5_list_item_base_height); +} + +:host([ui5-mcbi-custom]) .ui5-li-root { + padding-inline-start: 0; + min-height: var(--_ui5_list_item_base_height); +} + +:host([ui5-mcbi-custom]) .ui5-li-content { + padding-bottom: .5rem; + padding-top: .5rem; + box-sizing: border-box; +} + +:host([ui5-mcbi-custom]) [ui5-checkbox] { + overflow: visible; +} diff --git a/packages/main/test/pages/ComboBox.html b/packages/main/test/pages/ComboBox.html index 0d2e49ee54cf..e655361591a8 100644 --- a/packages/main/test/pages/ComboBox.html +++ b/packages/main/test/pages/ComboBox.html @@ -671,6 +671,59 @@

Dialog Header Title from Label

+
+

ComboBox with Custom Items

+ Custom items with icons/emojis: +
+
+ + + 🇩🇪Germany + + + 🇫🇷France + + + 🇪🇸Spain + + + 🇮🇹Italy + + + 🇬🇧United Kingdom + + +
+ Selected value: +
+ Selected selectedValue: + + +
+ +
+

ComboBox with Mixed Items

+ Regular items mixed with custom items: +
+
+ + + + ⭐ Custom Item with Icon + + + + 🔷 Another Custom Item + + +
+ diff --git a/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md b/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md new file mode 100644 index 000000000000..17798ecc59ab --- /dev/null +++ b/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.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/ComboBox/CustomItems/main.js b/packages/website/docs/_samples/main/ComboBox/CustomItems/main.js new file mode 100644 index 000000000000..77f0d920c858 --- /dev/null +++ b/packages/website/docs/_samples/main/ComboBox/CustomItems/main.js @@ -0,0 +1,2 @@ +import "@ui5/webcomponents/dist/ComboBox.js"; +import "@ui5/webcomponents/dist/ComboBoxItemCustom.js"; diff --git a/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html b/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html new file mode 100644 index 000000000000..eaa41f1a5b37 --- /dev/null +++ b/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html @@ -0,0 +1,34 @@ + + + + + + + ComboBox Custom Items + + + + + + + 🇩🇪Germany + + + 🇫🇷France + + + 🇪🇸Spain + + + 🇮🇹Italy + + + 🇬🇧United Kingdom + + + + + + + + diff --git a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md new file mode 100644 index 000000000000..17798ecc59ab --- /dev/null +++ b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.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/CustomItems/main.js b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/main.js new file mode 100644 index 000000000000..318a11a8cff2 --- /dev/null +++ b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/main.js @@ -0,0 +1,2 @@ +import "@ui5/webcomponents/dist/MultiComboBox.js"; +import "@ui5/webcomponents/dist/MultiComboBoxItemCustom.js"; diff --git a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html new file mode 100644 index 000000000000..453020238700 --- /dev/null +++ b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html @@ -0,0 +1,34 @@ + + + + + + + MultiComboBox Custom Items + + + + + + + 🇩🇪Germany + + + 🇫🇷France + + + 🇪🇸Spain + + + 🇮🇹Italy + + + 🇬🇧United Kingdom + + + + + + + + From 983f1f5c7ca44d3f1d8412a7369ab259d180712e Mon Sep 17 00:00:00 2001 From: Milen Karmidzhanov Date: Thu, 4 Jun 2026 12:00:23 +0300 Subject: [PATCH 02/10] feat(ui5-comobobox, ui5-multi-combobox): add custom items --- packages/main/cypress/specs/ComboBox.cy.tsx | 20 +++--- .../main/cypress/specs/MultiComboBox.cy.tsx | 56 +++++++++++---- .../main/ComboBox/CustomItems/sample.tsx | 63 +++++++++++++++++ .../main/MultiComboBox/CustomItems/sample.tsx | 68 +++++++++++++++++++ 4 files changed, 184 insertions(+), 23 deletions(-) create mode 100644 packages/website/docs/_samples/main/ComboBox/CustomItems/sample.tsx create mode 100644 packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.tsx diff --git a/packages/main/cypress/specs/ComboBox.cy.tsx b/packages/main/cypress/specs/ComboBox.cy.tsx index 92f04e10217a..0aaf83f56278 100644 --- a/packages/main/cypress/specs/ComboBox.cy.tsx +++ b/packages/main/cypress/specs/ComboBox.cy.tsx @@ -4013,8 +4013,8 @@ describe("ComboBoxItemCustom - Rendering", () => { .find("[ui5-icon]") .realClick(); - cy.get("[ui5-cbi-custom]").eq(0).shadow().find("li").should("contain.text", "🇩🇪 Germany"); - cy.get("[ui5-cbi-custom]").eq(1).shadow().find("li").should("contain.text", "🇫🇷 France"); + cy.get("[ui5-cbi-custom]").eq(0).should("contain.text", "🇩🇪 Germany"); + cy.get("[ui5-cbi-custom]").eq(1).should("contain.text", "🇫🇷 France"); }); it("should mix regular and custom items", () => { @@ -4157,15 +4157,17 @@ describe("ComboBoxItemCustom - Navigation", () => { cy.get("[ui5-combobox]") .as("combobox") + .shadow() + .find("[ui5-icon]") .realClick(); - cy.get("@combobox").realPress("ArrowDown"); + cy.get("@combobox").shadow().find("input").realPress("ArrowDown"); cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "focused", true); - cy.get("@combobox").realPress("ArrowDown"); + cy.get("@combobox").shadow().find("input").realPress("ArrowDown"); cy.get("[ui5-cbi-custom]").eq(1).should("have.prop", "focused", true); - cy.get("@combobox").realPress("ArrowUp"); + cy.get("@combobox").shadow().find("input").realPress("ArrowUp"); cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "focused", true); }); @@ -4180,15 +4182,17 @@ describe("ComboBoxItemCustom - Navigation", () => { cy.get("[ui5-combobox]") .as("combobox") + .shadow() + .find("[ui5-icon]") .realClick(); - cy.get("@combobox").realPress("ArrowDown"); + cy.get("@combobox").shadow().find("input").realPress("ArrowDown"); cy.get("[ui5-cb-item]").eq(0).should("have.prop", "focused", true); - cy.get("@combobox").realPress("ArrowDown"); + cy.get("@combobox").shadow().find("input").realPress("ArrowDown"); cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "focused", true); - cy.get("@combobox").realPress("ArrowDown"); + cy.get("@combobox").shadow().find("input").realPress("ArrowDown"); cy.get("[ui5-cb-item]").eq(1).should("have.prop", "focused", true); }); }); diff --git a/packages/main/cypress/specs/MultiComboBox.cy.tsx b/packages/main/cypress/specs/MultiComboBox.cy.tsx index 9097e95d3763..45952ea505e0 100644 --- a/packages/main/cypress/specs/MultiComboBox.cy.tsx +++ b/packages/main/cypress/specs/MultiComboBox.cy.tsx @@ -5181,8 +5181,8 @@ describe("MultiComboBoxItemCustom - Rendering", () => { .find("[ui5-icon]") .realClick(); - cy.get("[ui5-mcbi-custom]").eq(0).shadow().find(".ui5-li-content").should("contain.text", "🇩🇪 Germany"); - cy.get("[ui5-mcbi-custom]").eq(1).shadow().find(".ui5-li-content").should("contain.text", "🇫🇷 France"); + cy.get("[ui5-mcbi-custom]").eq(0).should("contain.text", "🇩🇪 Germany"); + cy.get("[ui5-mcbi-custom]").eq(1).should("contain.text", "🇫🇷 France"); }); it("should render checkbox for custom items", () => { @@ -5371,7 +5371,7 @@ describe("MultiComboBoxItemCustom - Tokens", () => { .find("[ui5-token]") .eq(0) .shadow() - .find(".ui5-token-icon") + .find("[ui5-icon]") .realClick(); cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", false); @@ -5393,14 +5393,27 @@ describe("MultiComboBoxItemCustom - Navigation", () => { .as("multiCombobox") .realClick(); - cy.get("@multiCombobox").realPress("ArrowDown"); - cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "focused", true); + cy.get("@multiCombobox") + .should("be.focused"); + + cy.get("@multiCombobox") + .shadow() + .find("[ui5-icon]") + .realClick(); + + cy.get("@multiCombobox") + .shadow() + .find("ui5-responsive-popover") + .ui5ResponsivePopoverOpened(); - cy.get("@multiCombobox").realPress("ArrowDown"); - cy.get("[ui5-mcbi-custom]").eq(1).should("have.prop", "focused", true); + cy.realPress(["Meta", "ArrowDown"]); + cy.get("[ui5-mcbi-custom]").eq(0).should("be.focused"); - cy.get("@multiCombobox").realPress("ArrowUp"); - cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "focused", true); + cy.realPress(["Meta", "ArrowDown"]); + cy.get("[ui5-mcbi-custom]").eq(1).should("be.focused"); + + cy.realPress(["Meta", "ArrowUp"]); + cy.get("[ui5-mcbi-custom]").eq(0).should("be.focused"); }); it("should navigate through mixed items", () => { @@ -5416,14 +5429,27 @@ describe("MultiComboBoxItemCustom - Navigation", () => { .as("multiCombobox") .realClick(); - cy.get("@multiCombobox").realPress("ArrowDown"); - cy.get("[ui5-mcb-item]").eq(0).should("have.prop", "focused", true); + cy.get("@multiCombobox") + .should("be.focused"); - cy.get("@multiCombobox").realPress("ArrowDown"); - cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "focused", true); + cy.get("@multiCombobox") + .shadow() + .find("[ui5-icon]") + .realClick(); - cy.get("@multiCombobox").realPress("ArrowDown"); - cy.get("[ui5-mcb-item]").eq(1).should("have.prop", "focused", true); + cy.get("@multiCombobox") + .shadow() + .find("ui5-responsive-popover") + .ui5ResponsivePopoverOpened(); + + cy.realPress(["Meta", "ArrowDown"]); + cy.get("[ui5-mcb-item]").eq(0).should("be.focused"); + + cy.realPress(["Meta", "ArrowDown"]); + cy.get("[ui5-mcbi-custom]").eq(0).should("be.focused"); + + cy.realPress(["Meta", "ArrowDown"]); + cy.get("[ui5-mcb-item]").eq(1).should("be.focused"); }); }); diff --git a/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.tsx b/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.tsx new file mode 100644 index 000000000000..1c8e6a2fac33 --- /dev/null +++ b/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.tsx @@ -0,0 +1,63 @@ +import { useState } from "react"; +import createReactComponent from "@ui5/webcomponents-base/dist/createReactComponent.js"; +import { type UI5CustomEvent } from "@ui5/webcomponents-base"; +import ComboBoxClass from "@ui5/webcomponents/dist/ComboBox.js"; +import ComboBoxItemCustomClass from "@ui5/webcomponents/dist/ComboBoxItemCustom.js"; + +const ComboBox = createReactComponent(ComboBoxClass); +const ComboBoxItemCustom = createReactComponent(ComboBoxItemCustomClass); + +const countries = [ + { text: "Germany", value: "DE", flag: "🇩🇪" }, + { text: "France", value: "FR", flag: "🇫🇷" }, + { text: "Spain", value: "ES", flag: "🇪🇸" }, + { text: "Italy", value: "IT", flag: "🇮🇹" }, + { text: "United Kingdom", value: "GB", flag: "🇬🇧" }, +]; + +function App() { + const [selectedValue, setSelectedValue] = useState(""); + + const handleChange = (e: UI5CustomEvent) => { + setSelectedValue(e.currentTarget.value); + }; + + return ( + <> + + + {countries.map((country) => ( + + + {country.flag} + + {country.text} + + ))} + + {selectedValue && ( +
+ Selected: {selectedValue} +
+ )} + + ); +} + +export default App; diff --git a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.tsx b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.tsx new file mode 100644 index 000000000000..ebbf003244b7 --- /dev/null +++ b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.tsx @@ -0,0 +1,68 @@ +import { useState } from "react"; +import createReactComponent from "@ui5/webcomponents-base/dist/createReactComponent.js"; +import { type UI5CustomEvent } from "@ui5/webcomponents-base"; +import MultiComboBoxClass from "@ui5/webcomponents/dist/MultiComboBox.js"; +import MultiComboBoxItemCustomClass from "@ui5/webcomponents/dist/MultiComboBoxItemCustom.js"; + +const MultiComboBox = createReactComponent(MultiComboBoxClass); +const MultiComboBoxItemCustom = createReactComponent(MultiComboBoxItemCustomClass); + +const countries = [ + { text: "Germany", value: "DE", flag: "🇩🇪" }, + { text: "France", value: "FR", flag: "🇫🇷" }, + { text: "Spain", value: "ES", flag: "🇪🇸" }, + { text: "Italy", value: "IT", flag: "🇮🇹" }, + { text: "United Kingdom", value: "GB", flag: "🇬🇧" }, +]; + +function App() { + const [selectedValues, setSelectedValues] = useState([]); + + const handleSelectionChange = (e: UI5CustomEvent) => { + const items = e.currentTarget.items; + setSelectedValues( + items + .filter((item: any) => item.selected) + .map((item: any) => item.text) + ); + }; + + return ( + <> + + + {countries.map((country) => ( + + + {country.flag} + + {country.text} + + ))} + + {selectedValues.length > 0 && ( +
+ Selected: {selectedValues.join(", ")} +
+ )} + + ); +} + +export default App; From 02fb8ce8b54ff1ac87f20899859cfc28cf98f74c Mon Sep 17 00:00:00 2001 From: Milen Karmidzhanov Date: Thu, 4 Jun 2026 13:05:37 +0300 Subject: [PATCH 03/10] feat(ui5-comobobox, ui5-multi-combobox): add custom items --- packages/main/src/bundle.esm.ts | 2 ++ .../_components_pages/main/ComboBox/ComboBoxItemCustom.mdx | 7 +++++++ .../main/MultiComboBox/MultiComboBoxItemCustom.mdx | 7 +++++++ 3 files changed, 16 insertions(+) create mode 100644 packages/website/docs/_components_pages/main/ComboBox/ComboBoxItemCustom.mdx create mode 100644 packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBoxItemCustom.mdx diff --git a/packages/main/src/bundle.esm.ts b/packages/main/src/bundle.esm.ts index f770e818a792..d916b641dfac 100644 --- a/packages/main/src/bundle.esm.ts +++ b/packages/main/src/bundle.esm.ts @@ -47,6 +47,7 @@ import ColorPaletteItem from "./ColorPaletteItem.js"; import ColorPalettePopover from "./ColorPalettePopover.js"; import ColorPicker from "./ColorPicker.js"; import ComboBox from "./ComboBox.js"; +import ComboBoxItemCustom from "./ComboBoxItemCustom.js"; import DatePicker from "./DatePicker.js"; import DateRangePicker from "./DateRangePicker.js"; import DateTimePicker from "./DateTimePicker.js"; @@ -109,6 +110,7 @@ import RangeSlider from "./RangeSlider.js"; import Switch from "./Switch.js"; import MessageStrip from "./MessageStrip.js"; import MultiComboBox from "./MultiComboBox.js"; +import MultiComboBoxItemCustom from "./MultiComboBoxItemCustom.js"; import ProgressIndicator from "./ProgressIndicator.js"; import RatingIndicator from "./RatingIndicator.js"; import Tag from "./Tag.js"; diff --git a/packages/website/docs/_components_pages/main/ComboBox/ComboBoxItemCustom.mdx b/packages/website/docs/_components_pages/main/ComboBox/ComboBoxItemCustom.mdx new file mode 100644 index 000000000000..c5e477222e81 --- /dev/null +++ b/packages/website/docs/_components_pages/main/ComboBox/ComboBoxItemCustom.mdx @@ -0,0 +1,7 @@ +--- +slug: ../../ComboBoxItemCustom +--- + +<%COMPONENT_OVERVIEW%> + +<%COMPONENT_METADATA%> diff --git a/packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBoxItemCustom.mdx b/packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBoxItemCustom.mdx new file mode 100644 index 000000000000..848e8db046f4 --- /dev/null +++ b/packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBoxItemCustom.mdx @@ -0,0 +1,7 @@ +--- +slug: ../../MultiComboBoxItemCustom +--- + +<%COMPONENT_OVERVIEW%> + +<%COMPONENT_METADATA%> From c129bc83e672f9d241ac2b1498c9d782d264ae88 Mon Sep 17 00:00:00 2001 From: Milen Karmidzhanov Date: Thu, 4 Jun 2026 13:10:30 +0300 Subject: [PATCH 04/10] feat(ui5-comobobox, ui5-multi-combobox): add custom items --- packages/main/src/ComboBoxItemCustom.ts | 1 + packages/main/src/MultiComboBoxItemCustom.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/main/src/ComboBoxItemCustom.ts b/packages/main/src/ComboBoxItemCustom.ts index ad1a5cd85f56..b85e2cec72f9 100644 --- a/packages/main/src/ComboBoxItemCustom.ts +++ b/packages/main/src/ComboBoxItemCustom.ts @@ -12,6 +12,7 @@ import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js"; * The `ui5-cbi-custom` is type of combobox item, * that can be used to place combobox items with custom content in the combobox. * The text property is considered for filtering and autocomplete. + * In case the user needs highlighting functionality, check "@ui5/webcomponents-base/dist/util/generateHighlightedMarkup.js" * * @constructor * @extends ListItemBase diff --git a/packages/main/src/MultiComboBoxItemCustom.ts b/packages/main/src/MultiComboBoxItemCustom.ts index 1a5eee698dd0..eced507096f6 100644 --- a/packages/main/src/MultiComboBoxItemCustom.ts +++ b/packages/main/src/MultiComboBoxItemCustom.ts @@ -21,6 +21,7 @@ import type { AriaRole } from "@ui5/webcomponents-base"; * The `ui5-mcbi-custom` is type of multi-combobox item, * that can be used to place multi-combobox items with custom content. * The text property is considered for filtering and token display. + * In case the user needs highlighting functionality, check "@ui5/webcomponents-base/dist/util/generateHighlightedMarkup.js" * * @constructor * @extends ComboBoxItemCustom From e677faef0c04919a920eefaa414f82d0202abf7a Mon Sep 17 00:00:00 2001 From: Milen Karmidzhanov Date: Thu, 4 Jun 2026 13:50:44 +0300 Subject: [PATCH 05/10] feat(ui5-comobobox, ui5-multi-combobox): add custom items --- packages/main/src/ComboBoxItemCustom.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/main/src/ComboBoxItemCustom.ts b/packages/main/src/ComboBoxItemCustom.ts index b85e2cec72f9..8b1c9e8d7818 100644 --- a/packages/main/src/ComboBoxItemCustom.ts +++ b/packages/main/src/ComboBoxItemCustom.ts @@ -36,7 +36,6 @@ class ComboBoxItemCustom extends ListItemBase implements IComboBoxItem { * Used for filtering, autocomplete, and mobile rendering. * @default undefined * @public - * @since 2.24.0 */ @property() text?: string; @@ -46,7 +45,6 @@ class ComboBoxItemCustom extends ListItemBase implements IComboBoxItem { * Used for programmatic selection via selectedValue property. * @default undefined * @public - * @since 2.24.0 */ @property() value?: string; @@ -68,7 +66,6 @@ class ComboBoxItemCustom extends ListItemBase implements IComboBoxItem { /** * Defines the content of the component. * @public - * @since 2.24.0 */ @slot({ type: Node, "default": true, invalidateOnChildChange: true }) content!: DefaultSlot; From e51aaf272f0d34149fc6879f06a38b3d2c4d615c Mon Sep 17 00:00:00 2001 From: Milen Karmidzhanov Date: Tue, 9 Jun 2026 18:28:05 +0300 Subject: [PATCH 06/10] feat(ui5-comobobox, ui5-multi-combobox): add custom items --- .../cypress/specs/MultiComboBox.mobile.cy.tsx | 184 ++++++++++++++++++ packages/main/src/MultiComboBox.ts | 27 ++- packages/main/src/MultiComboBoxItemCustom.ts | 5 +- packages/main/test/pages/ComboBox.html | 55 ++++-- packages/main/test/pages/MultiComboBox.html | 94 +++++++++ .../main/ComboBox/CustomItems/sample.html | 53 +++-- .../MultiComboBox/CustomItems/sample.html | 53 +++-- 7 files changed, 428 insertions(+), 43 deletions(-) diff --git a/packages/main/cypress/specs/MultiComboBox.mobile.cy.tsx b/packages/main/cypress/specs/MultiComboBox.mobile.cy.tsx index f5b465c2a9f9..600262b137ee 100644 --- a/packages/main/cypress/specs/MultiComboBox.mobile.cy.tsx +++ b/packages/main/cypress/specs/MultiComboBox.mobile.cy.tsx @@ -962,4 +962,188 @@ describe("Dialog header title", () => { .find("[ui5-responsive-popover] .ui5-responsive-popover-header-text") .should("have.text", "Country"); }); +}); + +describe("Custom Items", () => { + beforeEach(() => { + cy.ui5SimulateDevice("phone"); + }); + + it("Should select custom items via checkbox click and OK button", () => { + cy.mount( + + +
+ 🇺🇸 + New York, USA + ✈️ +
+
+ +
+ 🇬🇧 + London, UK + ✈️ +
+
+
+ ); + + cy.get("[ui5-multi-combobox]") + .shadow() + .find("input") + .realClick(); + + // Click custom item checkboxes + cy.get("[ui5-multi-combobox]") + .find("[ui5-mcbi-custom]") + .eq(0) + .shadow() + .find("[ui5-checkbox]") + .realClick(); + + cy.get("[ui5-multi-combobox]") + .find("[ui5-mcbi-custom]") + .eq(1) + .shadow() + .find("[ui5-checkbox]") + .realClick(); + + // Press OK button to confirm + cy.get("[ui5-multi-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .find(".ui5-responsive-popover-footer") + .find("[ui5-button]") + .realClick(); + + // Verify tokens were created + cy.get("[ui5-multi-combobox]") + .shadow() + .find("[ui5-tokenizer]") + .find("[ui5-token]") + .should("have.length", 2); + + // Verify token texts + cy.get("[ui5-multi-combobox]") + .shadow() + .find("[ui5-tokenizer]") + .find("[ui5-token]") + .eq(0) + .shadow() + .find(".ui5-token--text") + .should("have.text", "New York, USA"); + + cy.get("[ui5-multi-combobox]") + .shadow() + .find("[ui5-tokenizer]") + .find("[ui5-token]") + .eq(1) + .shadow() + .find(".ui5-token--text") + .should("have.text", "London, UK"); + }); + + it("Should maintain custom item checkbox state when reopening dialog", () => { + cy.mount( + + +
+ 🇫🇷 + Paris, France + ✈️ +
+
+ +
+ 🇯🇵 + Tokyo, Japan + ✈️ +
+
+
+ ); + + cy.get("[ui5-multi-combobox]") + .shadow() + .find("input") + .realClick(); + + // Select first custom item and confirm with OK + cy.get("[ui5-multi-combobox]") + .find("[ui5-mcbi-custom]") + .eq(0) + .shadow() + .find("[ui5-checkbox]") + .realClick(); + + cy.get("[ui5-multi-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .find(".ui5-responsive-popover-footer") + .find("[ui5-button]") + .realClick(); + + // Reopen the dialog + cy.get("[ui5-multi-combobox]") + .shadow() + .find("input") + .realClick(); + + // Verify checkbox states are maintained + cy.get("[ui5-multi-combobox]") + .find("[ui5-mcbi-custom]") + .eq(0) + .shadow() + .find("[ui5-checkbox]") + .should("have.attr", "checked"); + + cy.get("[ui5-multi-combobox]") + .find("[ui5-mcbi-custom]") + .eq(1) + .shadow() + .find("[ui5-checkbox]") + .should("not.have.attr", "checked"); + }); + + it("Should not create token when custom item is selected but Cancel is pressed", () => { + cy.mount( + + +
+ 🇩🇪 + Berlin, Germany + ✈️ +
+
+
+ ); + + cy.get("[ui5-multi-combobox]") + .shadow() + .find("input") + .realClick(); + + // Click custom item checkbox + cy.get("[ui5-multi-combobox]") + .find("[ui5-mcbi-custom]") + .eq(0) + .shadow() + .find("[ui5-checkbox]") + .realClick(); + + // Press Cancel button + cy.get("[ui5-multi-combobox]") + .shadow() + .find("[ui5-responsive-popover]") + .find(".ui5-responsive-popover-close-btn") + .realClick(); + + // Verify no token was created + cy.get("[ui5-multi-combobox]") + .shadow() + .find("[ui5-tokenizer]") + .find("[ui5-token]") + .should("have.length", 0); + }); }); \ No newline at end of file diff --git a/packages/main/src/MultiComboBox.ts b/packages/main/src/MultiComboBox.ts index e0e250a8421b..534eeb34c311 100644 --- a/packages/main/src/MultiComboBox.ts +++ b/packages/main/src/MultiComboBox.ts @@ -1623,6 +1623,13 @@ class MultiComboBox extends UI5Element implements IFormInputElement { return this._getItems().filter(item => item.selected) as Array; } + _getSelectedValues(): Array { + return this._getItems() + .filter((i): i is MultiComboBoxItem => isInstanceOfMultiComboBoxItem(i) && i.selected) + .map(i => i.value) + .filter((v): v is string => !!v); + } + _listSelectionChange(e: CustomEvent) { let changePrevented; @@ -1635,16 +1642,15 @@ class MultiComboBox extends UI5Element implements IFormInputElement { this._previouslySelectedItems = e.detail.previouslySelectedItems; } + // Update selectedValues for both desktop and mobile + // On mobile, this provides visual feedback (checkbox state) + // On desktop, this happens before firing the selection-change event + if (this.selectedValues) { + this.selectedValues = this._getSelectedValues(); + } + // 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) { @@ -1960,6 +1966,11 @@ class MultiComboBox extends UI5Element implements IFormInputElement { } }); + // Revert selectedValues to match the restored selection state + if (this.selectedValues) { + this.selectedValues = this._getSelectedValues(); + } + this._toggleTokenizerPopover(); this.value = this._valueBeforeOpen; diff --git a/packages/main/src/MultiComboBoxItemCustom.ts b/packages/main/src/MultiComboBoxItemCustom.ts index eced507096f6..74483aff831e 100644 --- a/packages/main/src/MultiComboBoxItemCustom.ts +++ b/packages/main/src/MultiComboBoxItemCustom.ts @@ -68,7 +68,10 @@ class MultiComboBoxItemCustom extends ComboBoxItemCustom implements IMultiComboB _onclick(e: MouseEvent) { if ((e.target as HTMLElement)?.hasAttribute("ui5-checkbox")) { - return this.fireDecoratorEvent("selection-requested", { item: this, selected: (e.target as CheckBox).checked, selectionComponentPressed: true }); + const checkboxCheckedState = (e.target as CheckBox).checked; + + // The checkbox has already toggled itself, so use its current state + return this.fireDecoratorEvent("selection-requested", { item: this, selected: checkboxCheckedState, selectionComponentPressed: true }); } super._onclick(e); diff --git a/packages/main/test/pages/ComboBox.html b/packages/main/test/pages/ComboBox.html index e655361591a8..8d46b9ace410 100644 --- a/packages/main/test/pages/ComboBox.html +++ b/packages/main/test/pages/ComboBox.html @@ -673,24 +673,55 @@

Dialog Header Title from Label

ComboBox with Custom Items

- Custom items with icons/emojis: + Custom items with multiple icons/emojis:

- - - 🇩🇪Germany + + +
+ 🇺🇸 + New York, USA + ✈️ + +
- - 🇫🇷France + +
+ 🇬🇧 + London, UK + ✈️ + +
- - 🇪🇸Spain + +
+ 🇯🇵 + Tokyo, Japan + ✈️ + +
- - 🇮🇹Italy + +
+ 🇫🇷 + Paris, France + ✈️ +
- - 🇬🇧United Kingdom + +
+ 🇩🇪 + Berlin, Germany + 🚆 +
+
+ +
+ 🇦🇺 + Sydney, Australia + 🏖️ + +

diff --git a/packages/main/test/pages/MultiComboBox.html b/packages/main/test/pages/MultiComboBox.html index 771510ff0411..35b675cebfe5 100644 --- a/packages/main/test/pages/MultiComboBox.html +++ b/packages/main/test/pages/MultiComboBox.html @@ -903,6 +903,100 @@

Dialog Header Title from Label

+
+

MultiComboBox with Custom Items

+ Custom items with multiple icons/emojis: +
+
+ + +
+ 🇺🇸 + New York, USA + ✈️ + +
+
+ +
+ 🇬🇧 + London, UK + ✈️ + +
+
+ +
+ 🇯🇵 + Tokyo, Japan + ✈️ + +
+
+ +
+ 🇫🇷 + Paris, France + ✈️ +
+
+ +
+ 🇩🇪 + Berlin, Germany + 🚆 +
+
+ +
+ 🇦🇺 + Sydney, Australia + 🏖️ + +
+
+
+
+ Selected values: +
+ Selected selectedValues: + + +
+ +
+

MultiComboBox with Mixed Items

+ Regular items mixed with custom items: +
+
+ + + +
+ + Custom Item with Icons + 🔥 +
+
+ + +
+ 🔷 + Another Custom Item + +
+
+
+
+ diff --git a/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html b/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html index eaa41f1a5b37..11a3f87f91cc 100644 --- a/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html +++ b/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html @@ -9,21 +9,52 @@ - - - 🇩🇪Germany + + +
+ 🇺🇸 + New York, USA + ✈️ + +
- - 🇫🇷France + +
+ 🇬🇧 + London, UK + ✈️ + +
- - 🇪🇸Spain + +
+ 🇯🇵 + Tokyo, Japan + ✈️ + +
- - 🇮🇹Italy + +
+ 🇫🇷 + Paris, France + ✈️ +
- - 🇬🇧United Kingdom + +
+ 🇩🇪 + Berlin, Germany + 🚆 +
+
+ +
+ 🇦🇺 + Sydney, Australia + 🏖️ + +
diff --git a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html index 453020238700..5008d89ee94d 100644 --- a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html +++ b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html @@ -9,21 +9,52 @@ - - - 🇩🇪Germany + + +
+ 🇺🇸 + New York, USA + ✈️ + +
- - 🇫🇷France + +
+ 🇬🇧 + London, UK + ✈️ + +
- - 🇪🇸Spain + +
+ 🇯🇵 + Tokyo, Japan + ✈️ + +
- - 🇮🇹Italy + +
+ 🇫🇷 + Paris, France + ✈️ +
- - 🇬🇧United Kingdom + +
+ 🇩🇪 + Berlin, Germany + 🚆 +
+
+ +
+ 🇦🇺 + Sydney, Australia + 🏖️ + +
From bdffd79d22abb2fc9f24255a6b4fcaa247070492 Mon Sep 17 00:00:00 2001 From: Milen Karmidzhanov Date: Thu, 11 Jun 2026 16:44:20 +0300 Subject: [PATCH 07/10] feat(ui5-comobobox, ui5-multi-combobox): add custom items --- packages/main/cypress/specs/ComboBox.cy.tsx | 28 +++++------ .../main/cypress/specs/MultiComboBox.cy.tsx | 50 +++++++++---------- .../cypress/specs/MultiComboBox.mobile.cy.tsx | 32 ++++++------ packages/main/src/ComboBoxItemCustom.ts | 4 +- packages/main/src/MultiComboBoxItemCustom.ts | 4 +- .../main/src/themes/ComboBoxItemCustom.css | 6 +-- .../src/themes/MultiComboBoxItemCustom.css | 8 +-- packages/main/test/pages/ComboBox.html | 32 ++++++------ packages/main/test/pages/MultiComboBox.html | 32 ++++++------ .../main/ComboBox/ComboBox.mdx | 2 +- .../main/MultiComboBox/MultiComboBox.mdx | 2 +- .../main/ComboBox/CustomItems/sample.html | 24 ++++----- .../MultiComboBox/CustomItems/sample.html | 24 ++++----- 13 files changed, 124 insertions(+), 124 deletions(-) diff --git a/packages/main/cypress/specs/ComboBox.cy.tsx b/packages/main/cypress/specs/ComboBox.cy.tsx index 0aaf83f56278..c1cab959a21d 100644 --- a/packages/main/cypress/specs/ComboBox.cy.tsx +++ b/packages/main/cypress/specs/ComboBox.cy.tsx @@ -4013,8 +4013,8 @@ describe("ComboBoxItemCustom - Rendering", () => { .find("[ui5-icon]") .realClick(); - cy.get("[ui5-cbi-custom]").eq(0).should("contain.text", "🇩🇪 Germany"); - cy.get("[ui5-cbi-custom]").eq(1).should("contain.text", "🇫🇷 France"); + cy.get("[ui5-cb-item-custom]").eq(0).should("contain.text", "🇩🇪 Germany"); + cy.get("[ui5-cb-item-custom]").eq(1).should("contain.text", "🇫🇷 France"); }); it("should mix regular and custom items", () => { @@ -4033,7 +4033,7 @@ describe("ComboBoxItemCustom - Rendering", () => { .realClick(); cy.get("[ui5-cb-item]").should("have.length", 1); - cy.get("[ui5-cbi-custom]").should("have.length", 1); + cy.get("[ui5-cb-item-custom]").should("have.length", 1); }); it("should have role='option'", () => { @@ -4048,7 +4048,7 @@ describe("ComboBoxItemCustom - Rendering", () => { .find("[ui5-icon]") .realClick(); - cy.get("[ui5-cbi-custom]").shadow().find("li").should("have.attr", "role", "option"); + cy.get("[ui5-cb-item-custom]").shadow().find("li").should("have.attr", "role", "option"); }); }); @@ -4068,9 +4068,9 @@ describe("ComboBoxItemCustom - Filtering", () => { cy.get("@combobox").realPress("G"); - cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "_isVisible", true); - cy.get("[ui5-cbi-custom]").eq(1).should("not.have.prop", "_isVisible", true); - cy.get("[ui5-cbi-custom]").eq(2).should("not.have.prop", "_isVisible", true); + cy.get("[ui5-cb-item-custom]").eq(0).should("have.prop", "_isVisible", true); + cy.get("[ui5-cb-item-custom]").eq(1).should("not.have.prop", "_isVisible", true); + cy.get("[ui5-cb-item-custom]").eq(2).should("not.have.prop", "_isVisible", true); }); it("should filter mixed regular and custom items", () => { @@ -4089,7 +4089,7 @@ describe("ComboBoxItemCustom - Filtering", () => { cy.get("@combobox").realPress("G"); cy.get("[ui5-cb-item]").eq(0).should("not.have.prop", "_isVisible", true); - cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "_isVisible", true); + cy.get("[ui5-cb-item-custom]").eq(0).should("have.prop", "_isVisible", true); cy.get("[ui5-cb-item]").eq(1).should("not.have.prop", "_isVisible", true); }); }); @@ -4109,7 +4109,7 @@ describe("ComboBoxItemCustom - Selection", () => { .find("[ui5-icon]") .realClick(); - cy.get("[ui5-cbi-custom]").eq(0).shadow().find("li").realClick(); + cy.get("[ui5-cb-item-custom]").eq(0).shadow().find("li").realClick(); cy.get("@combobox").should("have.prop", "value", "Germany"); }); @@ -4162,13 +4162,13 @@ describe("ComboBoxItemCustom - Navigation", () => { .realClick(); cy.get("@combobox").shadow().find("input").realPress("ArrowDown"); - cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "focused", true); + cy.get("[ui5-cb-item-custom]").eq(0).should("have.prop", "focused", true); cy.get("@combobox").shadow().find("input").realPress("ArrowDown"); - cy.get("[ui5-cbi-custom]").eq(1).should("have.prop", "focused", true); + cy.get("[ui5-cb-item-custom]").eq(1).should("have.prop", "focused", true); cy.get("@combobox").shadow().find("input").realPress("ArrowUp"); - cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "focused", true); + cy.get("[ui5-cb-item-custom]").eq(0).should("have.prop", "focused", true); }); it("should navigate through mixed items", () => { @@ -4190,7 +4190,7 @@ describe("ComboBoxItemCustom - Navigation", () => { cy.get("[ui5-cb-item]").eq(0).should("have.prop", "focused", true); cy.get("@combobox").shadow().find("input").realPress("ArrowDown"); - cy.get("[ui5-cbi-custom]").eq(0).should("have.prop", "focused", true); + cy.get("[ui5-cb-item-custom]").eq(0).should("have.prop", "focused", true); cy.get("@combobox").shadow().find("input").realPress("ArrowDown"); cy.get("[ui5-cb-item]").eq(1).should("have.prop", "focused", true); @@ -4210,6 +4210,6 @@ describe("ComboBoxItemCustom - Accessibility", () => { .find("[ui5-icon]") .realClick(); - cy.get("[ui5-cbi-custom]").shadow().find("li").should("not.have.attr", "tabindex", "0"); + cy.get("[ui5-cb-item-custom]").shadow().find("li").should("not.have.attr", "tabindex", "0"); }); }); diff --git a/packages/main/cypress/specs/MultiComboBox.cy.tsx b/packages/main/cypress/specs/MultiComboBox.cy.tsx index 9f7ee4ccfa8e..7d5add89a3fd 100644 --- a/packages/main/cypress/specs/MultiComboBox.cy.tsx +++ b/packages/main/cypress/specs/MultiComboBox.cy.tsx @@ -5181,8 +5181,8 @@ describe("MultiComboBoxItemCustom - Rendering", () => { .find("[ui5-icon]") .realClick(); - cy.get("[ui5-mcbi-custom]").eq(0).should("contain.text", "🇩🇪 Germany"); - cy.get("[ui5-mcbi-custom]").eq(1).should("contain.text", "🇫🇷 France"); + cy.get("[ui5-mcb-item-custom]").eq(0).should("contain.text", "🇩🇪 Germany"); + cy.get("[ui5-mcb-item-custom]").eq(1).should("contain.text", "🇫🇷 France"); }); it("should render checkbox for custom items", () => { @@ -5197,7 +5197,7 @@ describe("MultiComboBoxItemCustom - Rendering", () => { .find("[ui5-icon]") .realClick(); - cy.get("[ui5-mcbi-custom]").shadow().find("[ui5-checkbox]").should("exist"); + cy.get("[ui5-mcb-item-custom]").shadow().find("[ui5-checkbox]").should("exist"); }); it("should mix regular and custom items", () => { @@ -5216,7 +5216,7 @@ describe("MultiComboBoxItemCustom - Rendering", () => { .realClick(); cy.get("[ui5-mcb-item]").should("have.length", 1); - cy.get("[ui5-mcbi-custom]").should("have.length", 1); + cy.get("[ui5-mcb-item-custom]").should("have.length", 1); }); }); @@ -5236,9 +5236,9 @@ describe("MultiComboBoxItemCustom - Filtering", () => { cy.get("@multiCombobox").realPress("G"); - cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "_isVisible", true); - cy.get("[ui5-mcbi-custom]").eq(1).should("not.have.prop", "_isVisible", true); - cy.get("[ui5-mcbi-custom]").eq(2).should("not.have.prop", "_isVisible", true); + cy.get("[ui5-mcb-item-custom]").eq(0).should("have.prop", "_isVisible", true); + cy.get("[ui5-mcb-item-custom]").eq(1).should("not.have.prop", "_isVisible", true); + cy.get("[ui5-mcb-item-custom]").eq(2).should("not.have.prop", "_isVisible", true); }); it("should filter mixed regular and custom items", () => { @@ -5257,7 +5257,7 @@ describe("MultiComboBoxItemCustom - Filtering", () => { cy.get("@multiCombobox").realPress("G"); cy.get("[ui5-mcb-item]").eq(0).should("not.have.prop", "_isVisible", true); - cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "_isVisible", true); + cy.get("[ui5-mcb-item-custom]").eq(0).should("have.prop", "_isVisible", true); cy.get("[ui5-mcb-item]").eq(1).should("not.have.prop", "_isVisible", true); }); }); @@ -5277,9 +5277,9 @@ describe("MultiComboBoxItemCustom - Selection", () => { .find("[ui5-icon]") .realClick(); - cy.get("[ui5-mcbi-custom]").eq(0).shadow().find("[ui5-checkbox]").realClick(); + cy.get("[ui5-mcb-item-custom]").eq(0).shadow().find("[ui5-checkbox]").realClick(); - cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", true); + cy.get("[ui5-mcb-item-custom]").eq(0).should("have.prop", "selected", true); cy.get("@multiCombobox").shadow().find("[ui5-token]").should("have.length", 1); }); @@ -5298,11 +5298,11 @@ describe("MultiComboBoxItemCustom - Selection", () => { .find("[ui5-icon]") .realClick(); - cy.get("[ui5-mcbi-custom]").eq(0).shadow().find("[ui5-checkbox]").realClick(); - cy.get("[ui5-mcbi-custom]").eq(1).shadow().find("[ui5-checkbox]").realClick(); + cy.get("[ui5-mcb-item-custom]").eq(0).shadow().find("[ui5-checkbox]").realClick(); + cy.get("[ui5-mcb-item-custom]").eq(1).shadow().find("[ui5-checkbox]").realClick(); - cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", true); - cy.get("[ui5-mcbi-custom]").eq(1).should("have.prop", "selected", true); + cy.get("[ui5-mcb-item-custom]").eq(0).should("have.prop", "selected", true); + cy.get("[ui5-mcb-item-custom]").eq(1).should("have.prop", "selected", true); cy.get("@multiCombobox").shadow().find("[ui5-token]").should("have.length", 2); }); @@ -5315,9 +5315,9 @@ describe("MultiComboBoxItemCustom - Selection", () => { ); - cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", true); - cy.get("[ui5-mcbi-custom]").eq(1).should("have.prop", "selected", true); - cy.get("[ui5-mcbi-custom]").eq(2).should("have.prop", "selected", false); + cy.get("[ui5-mcb-item-custom]").eq(0).should("have.prop", "selected", true); + cy.get("[ui5-mcb-item-custom]").eq(1).should("have.prop", "selected", true); + cy.get("[ui5-mcb-item-custom]").eq(2).should("have.prop", "selected", false); cy.get("[ui5-multi-combobox]").shadow().find("[ui5-token]").should("have.length", 2); }); @@ -5364,7 +5364,7 @@ describe("MultiComboBoxItemCustom - Tokens", () => { ); - cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", true); + cy.get("[ui5-mcb-item-custom]").eq(0).should("have.prop", "selected", true); cy.get("[ui5-multi-combobox]") .shadow() @@ -5374,7 +5374,7 @@ describe("MultiComboBoxItemCustom - Tokens", () => { .find("[ui5-icon]") .realClick(); - cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", false); + cy.get("[ui5-mcb-item-custom]").eq(0).should("have.prop", "selected", false); cy.get("[ui5-multi-combobox]").shadow().find("[ui5-token]").should("have.length", 0); }); }); @@ -5407,13 +5407,13 @@ describe("MultiComboBoxItemCustom - Navigation", () => { .ui5ResponsivePopoverOpened(); cy.realPress(["Meta", "ArrowDown"]); - cy.get("[ui5-mcbi-custom]").eq(0).should("be.focused"); + cy.get("[ui5-mcb-item-custom]").eq(0).should("be.focused"); cy.realPress(["Meta", "ArrowDown"]); - cy.get("[ui5-mcbi-custom]").eq(1).should("be.focused"); + cy.get("[ui5-mcb-item-custom]").eq(1).should("be.focused"); cy.realPress(["Meta", "ArrowUp"]); - cy.get("[ui5-mcbi-custom]").eq(0).should("be.focused"); + cy.get("[ui5-mcb-item-custom]").eq(0).should("be.focused"); }); it("should navigate through mixed items", () => { @@ -5446,7 +5446,7 @@ describe("MultiComboBoxItemCustom - Navigation", () => { cy.get("[ui5-mcb-item]").eq(0).should("be.focused"); cy.realPress(["Meta", "ArrowDown"]); - cy.get("[ui5-mcbi-custom]").eq(0).should("be.focused"); + cy.get("[ui5-mcb-item-custom]").eq(0).should("be.focused"); cy.realPress(["Meta", "ArrowDown"]); cy.get("[ui5-mcb-item]").eq(1).should("be.focused"); @@ -5470,10 +5470,10 @@ describe("MultiComboBoxItemCustom - Mixed Selection", () => { .realClick(); cy.get("[ui5-mcb-item]").eq(0).shadow().find("[ui5-checkbox]").realClick(); - cy.get("[ui5-mcbi-custom]").eq(0).shadow().find("[ui5-checkbox]").realClick(); + cy.get("[ui5-mcb-item-custom]").eq(0).shadow().find("[ui5-checkbox]").realClick(); cy.get("[ui5-mcb-item]").eq(0).should("have.prop", "selected", true); - cy.get("[ui5-mcbi-custom]").eq(0).should("have.prop", "selected", true); + cy.get("[ui5-mcb-item-custom]").eq(0).should("have.prop", "selected", true); cy.get("@multiCombobox").shadow().find("[ui5-token]").should("have.length", 2); }); diff --git a/packages/main/cypress/specs/MultiComboBox.mobile.cy.tsx b/packages/main/cypress/specs/MultiComboBox.mobile.cy.tsx index 600262b137ee..71b644389eeb 100644 --- a/packages/main/cypress/specs/MultiComboBox.mobile.cy.tsx +++ b/packages/main/cypress/specs/MultiComboBox.mobile.cy.tsx @@ -972,20 +972,20 @@ describe("Custom Items", () => { it("Should select custom items via checkbox click and OK button", () => { cy.mount( - +
🇺🇸 New York, USA ✈️
-
- + +
🇬🇧 London, UK ✈️
-
+
); @@ -996,14 +996,14 @@ describe("Custom Items", () => { // Click custom item checkboxes cy.get("[ui5-multi-combobox]") - .find("[ui5-mcbi-custom]") + .find("[ui5-mcb-item-custom]") .eq(0) .shadow() .find("[ui5-checkbox]") .realClick(); cy.get("[ui5-multi-combobox]") - .find("[ui5-mcbi-custom]") + .find("[ui5-mcb-item-custom]") .eq(1) .shadow() .find("[ui5-checkbox]") @@ -1047,20 +1047,20 @@ describe("Custom Items", () => { it("Should maintain custom item checkbox state when reopening dialog", () => { cy.mount( - +
🇫🇷 Paris, France ✈️
-
- + +
🇯🇵 Tokyo, Japan ✈️
-
+
); @@ -1071,7 +1071,7 @@ describe("Custom Items", () => { // Select first custom item and confirm with OK cy.get("[ui5-multi-combobox]") - .find("[ui5-mcbi-custom]") + .find("[ui5-mcb-item-custom]") .eq(0) .shadow() .find("[ui5-checkbox]") @@ -1092,14 +1092,14 @@ describe("Custom Items", () => { // Verify checkbox states are maintained cy.get("[ui5-multi-combobox]") - .find("[ui5-mcbi-custom]") + .find("[ui5-mcb-item-custom]") .eq(0) .shadow() .find("[ui5-checkbox]") .should("have.attr", "checked"); cy.get("[ui5-multi-combobox]") - .find("[ui5-mcbi-custom]") + .find("[ui5-mcb-item-custom]") .eq(1) .shadow() .find("[ui5-checkbox]") @@ -1109,13 +1109,13 @@ describe("Custom Items", () => { it("Should not create token when custom item is selected but Cancel is pressed", () => { cy.mount( - +
🇩🇪 Berlin, Germany ✈️
-
+
); @@ -1126,7 +1126,7 @@ describe("Custom Items", () => { // Click custom item checkbox cy.get("[ui5-multi-combobox]") - .find("[ui5-mcbi-custom]") + .find("[ui5-mcb-item-custom]") .eq(0) .shadow() .find("[ui5-checkbox]") diff --git a/packages/main/src/ComboBoxItemCustom.ts b/packages/main/src/ComboBoxItemCustom.ts index 8b1c9e8d7818..ba9a0bd1c9f3 100644 --- a/packages/main/src/ComboBoxItemCustom.ts +++ b/packages/main/src/ComboBoxItemCustom.ts @@ -9,7 +9,7 @@ import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js"; /** * @class - * The `ui5-cbi-custom` is type of combobox item, + * The `ui5-cb-item-custom` is type of combobox item, * that can be used to place combobox items with custom content in the combobox. * The text property is considered for filtering and autocomplete. * In case the user needs highlighting functionality, check "@ui5/webcomponents-base/dist/util/generateHighlightedMarkup.js" @@ -21,7 +21,7 @@ import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js"; * @since 2.24.0 */ @customElement({ - tag: "ui5-cbi-custom", + tag: "ui5-cb-item-custom", template: ComboBoxItemCustomTemplate, styles: [ ListItemBase.styles, diff --git a/packages/main/src/MultiComboBoxItemCustom.ts b/packages/main/src/MultiComboBoxItemCustom.ts index 74483aff831e..68182e737485 100644 --- a/packages/main/src/MultiComboBoxItemCustom.ts +++ b/packages/main/src/MultiComboBoxItemCustom.ts @@ -18,7 +18,7 @@ import type { AriaRole } from "@ui5/webcomponents-base"; /** * @class - * The `ui5-mcbi-custom` is type of multi-combobox item, + * The `ui5-mcb-item-custom` is type of multi-combobox item, * that can be used to place multi-combobox items with custom content. * The text property is considered for filtering and token display. * In case the user needs highlighting functionality, check "@ui5/webcomponents-base/dist/util/generateHighlightedMarkup.js" @@ -30,7 +30,7 @@ import type { AriaRole } from "@ui5/webcomponents-base"; * @since 2.24.0 */ @customElement({ - tag: "ui5-mcbi-custom", + tag: "ui5-mcb-item-custom", template: MultiComboBoxItemCustomTemplate, styles: [ComboBoxItemCustom.styles, styles], dependencies: [...ComboBoxItemCustom.dependencies, CheckBox], diff --git a/packages/main/src/themes/ComboBoxItemCustom.css b/packages/main/src/themes/ComboBoxItemCustom.css index 06466771c5e8..e2ff7ec05922 100644 --- a/packages/main/src/themes/ComboBoxItemCustom.css +++ b/packages/main/src/themes/ComboBoxItemCustom.css @@ -1,13 +1,13 @@ -:host([ui5-cbi-custom]) { +:host([ui5-cb-item-custom]) { height: auto; min-height: var(--_ui5_list_item_base_height); } -:host([ui5-cbi-custom]) .ui5-li-root { +:host([ui5-cb-item-custom]) .ui5-li-root { min-height: var(--_ui5_list_item_base_height); } -:host([ui5-cbi-custom]) .ui5-li-content { +:host([ui5-cb-item-custom]) .ui5-li-content { padding-bottom: .5rem; padding-top: .5rem; box-sizing: border-box; diff --git a/packages/main/src/themes/MultiComboBoxItemCustom.css b/packages/main/src/themes/MultiComboBoxItemCustom.css index 7e6eb383c5ad..392e254e3902 100644 --- a/packages/main/src/themes/MultiComboBoxItemCustom.css +++ b/packages/main/src/themes/MultiComboBoxItemCustom.css @@ -1,19 +1,19 @@ -:host([ui5-mcbi-custom]) { +:host([ui5-mcb-item-custom]) { height: auto; min-height: var(--_ui5_list_item_base_height); } -:host([ui5-mcbi-custom]) .ui5-li-root { +:host([ui5-mcb-item-custom]) .ui5-li-root { padding-inline-start: 0; min-height: var(--_ui5_list_item_base_height); } -:host([ui5-mcbi-custom]) .ui5-li-content { +:host([ui5-mcb-item-custom]) .ui5-li-content { padding-bottom: .5rem; padding-top: .5rem; box-sizing: border-box; } -:host([ui5-mcbi-custom]) [ui5-checkbox] { +:host([ui5-mcb-item-custom]) [ui5-checkbox] { overflow: visible; } diff --git a/packages/main/test/pages/ComboBox.html b/packages/main/test/pages/ComboBox.html index 8d46b9ace410..0693262cd50b 100644 --- a/packages/main/test/pages/ComboBox.html +++ b/packages/main/test/pages/ComboBox.html @@ -677,52 +677,52 @@

ComboBox with Custom Items



- +
🇺🇸 New York, USA ✈️
-
- + +
🇬🇧 London, UK ✈️
-
- + +
🇯🇵 Tokyo, Japan ✈️
-
- + +
🇫🇷 Paris, France ✈️
-
- + +
🇩🇪 Berlin, Germany 🚆
-
- + +
🇦🇺 Sydney, Australia 🏖️
-
+

Selected value: @@ -745,13 +745,13 @@

ComboBox with Mixed Items


- + ⭐ Custom Item with Icon - + - + 🔷 Another Custom Item - +
diff --git a/packages/main/test/pages/MultiComboBox.html b/packages/main/test/pages/MultiComboBox.html index 35b675cebfe5..b9484c2c49e1 100644 --- a/packages/main/test/pages/MultiComboBox.html +++ b/packages/main/test/pages/MultiComboBox.html @@ -909,52 +909,52 @@

MultiComboBox with Custom Items



- +
🇺🇸 New York, USA ✈️
-
- + +
🇬🇧 London, UK ✈️
-
- + +
🇯🇵 Tokyo, Japan ✈️
-
- + +
🇫🇷 Paris, France ✈️
-
- + +
🇩🇪 Berlin, Germany 🚆
-
- + +
🇦🇺 Sydney, Australia 🏖️
-
+

Selected values: @@ -979,21 +979,21 @@

MultiComboBox with Mixed Items


- +
Custom Item with Icons 🔥
-
+ - +
🔷 Another Custom Item
-
+
diff --git a/packages/website/docs/_components_pages/main/ComboBox/ComboBox.mdx b/packages/website/docs/_components_pages/main/ComboBox/ComboBox.mdx index f0c837b8c1d8..afea7b4c16fd 100644 --- a/packages/website/docs/_components_pages/main/ComboBox/ComboBox.mdx +++ b/packages/website/docs/_components_pages/main/ComboBox/ComboBox.mdx @@ -60,7 +60,7 @@ The `additional-text` property helps users distinguish between items visually. ### Custom Items with Rich Content -Use `ui5-cbi-custom` to create custom item templates with complex layouts, multiple icons, images, or any HTML content. +Use `ui5-cb-item-custom` to create custom item templates with complex layouts, multiple icons, images, or any HTML content. This allows you to build rich, visually appealing dropdown items beyond the standard text and additional-text format. \ No newline at end of file diff --git a/packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBox.mdx b/packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBox.mdx index 6ca5c208f330..b5e32e32cbab 100644 --- a/packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBox.mdx +++ b/packages/website/docs/_components_pages/main/MultiComboBox/MultiComboBox.mdx @@ -61,7 +61,7 @@ The sample demonstrates how the text of the items wrap when too long. ### Custom Items with Rich Content -Use `ui5-mcbi-custom` to create custom item templates with complex layouts, multiple icons, images, or any HTML content. +Use `ui5-mcb-item-custom` to create custom item templates with complex layouts, multiple icons, images, or any HTML content. This allows you to build rich, visually appealing dropdown items that can display multiple pieces of information at once. diff --git a/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html b/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html index 11a3f87f91cc..49312edc5ae2 100644 --- a/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html +++ b/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html @@ -10,52 +10,52 @@ - +
🇺🇸 New York, USA ✈️
-
- + +
🇬🇧 London, UK ✈️
-
- + +
🇯🇵 Tokyo, Japan ✈️
-
- + +
🇫🇷 Paris, France ✈️
-
- + +
🇩🇪 Berlin, Germany 🚆
-
- + +
🇦🇺 Sydney, Australia 🏖️
-
+
diff --git a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html index 5008d89ee94d..1addf119a640 100644 --- a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html +++ b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html @@ -10,52 +10,52 @@ - +
🇺🇸 New York, USA ✈️
-
- + +
🇬🇧 London, UK ✈️
-
- + +
🇯🇵 Tokyo, Japan ✈️
-
- + +
🇫🇷 Paris, France ✈️
-
- + +
🇩🇪 Berlin, Germany 🚆
-
- + +
🇦🇺 Sydney, Australia 🏖️
-
+
From 74681b00201e8d88d72fb7f37ab3e33131a09e9e Mon Sep 17 00:00:00 2001 From: Milen Karmidzhanov Date: Mon, 15 Jun 2026 17:12:01 +0300 Subject: [PATCH 08/10] feat(ui5-comobobox, ui5-multi-combobox): add custom items --- packages/main/src/ComboBox.ts | 12 ++++- packages/main/src/ComboBoxItemCustom.ts | 6 +-- packages/main/src/ComboBoxPopoverTemplate.tsx | 4 +- packages/main/src/MultiComboBoxItemCustom.ts | 29 ++++++------ packages/main/test/pages/ComboBox.html | 45 +++++++++++++++++-- packages/main/test/pages/MultiComboBox.html | 34 +++++++++++++- .../main/ComboBox/CustomItems/CustomItems.md | 3 +- .../main/ComboBox/CustomItems/main.css | 3 ++ .../MultiComboBox/CustomItems/CustomItems.md | 3 +- .../main/MultiComboBox/CustomItems/main.css | 3 ++ 10 files changed, 114 insertions(+), 28 deletions(-) create mode 100644 packages/website/docs/_samples/main/ComboBox/CustomItems/main.css create mode 100644 packages/website/docs/_samples/main/MultiComboBox/CustomItems/main.css diff --git a/packages/main/src/ComboBox.ts b/packages/main/src/ComboBox.ts index f71adbedc173..0343c8217d6c 100644 --- a/packages/main/src/ComboBox.ts +++ b/packages/main/src/ComboBox.ts @@ -1319,8 +1319,16 @@ class ComboBox extends UI5Element implements IFormInputElement { } } else { if (this._useSelectedValue) { - itemToBeSelected = this.items.find(i => i.value === valueToMatch && (this.value === "" || i.text?.toLowerCase() === this.value.toLowerCase())); - return; + // During initial render, match by value even if text is empty + if (this._initialRendering && this.value === "") { + itemToBeSelected = this._filteredItems.find(i => !i.isGroupItem && i.value === valueToMatch); + } else if (this.value !== "") { + // When user is typing, require exact text match in addition to value match + itemToBeSelected = this._filteredItems.find(i => !i.isGroupItem && i.value === valueToMatch && i.text?.toLowerCase() === this.value.toLowerCase()); + } + if (itemToBeSelected) { + return; + } } itemToBeSelected = item.text?.toLowerCase() === this.value.toLowerCase() ? item : undefined; } diff --git a/packages/main/src/ComboBoxItemCustom.ts b/packages/main/src/ComboBoxItemCustom.ts index ba9a0bd1c9f3..7adc213f6f4d 100644 --- a/packages/main/src/ComboBoxItemCustom.ts +++ b/packages/main/src/ComboBoxItemCustom.ts @@ -9,9 +9,9 @@ import type { DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js"; /** * @class - * The `ui5-cb-item-custom` is type of combobox item, - * that can be used to place combobox items with custom content in the combobox. - * The text property is considered for filtering and autocomplete. + * The `ui5-cb-item-custom` is a combobox item component + * that can be used to place custom content in the combobox item. + * The text property is used for filtering and auto-complete. * In case the user needs highlighting functionality, check "@ui5/webcomponents-base/dist/util/generateHighlightedMarkup.js" * * @constructor diff --git a/packages/main/src/ComboBoxPopoverTemplate.tsx b/packages/main/src/ComboBoxPopoverTemplate.tsx index b4dd0ebcc93d..8373f4ced8b5 100644 --- a/packages/main/src/ComboBoxPopoverTemplate.tsx +++ b/packages/main/src/ComboBoxPopoverTemplate.tsx @@ -67,11 +67,11 @@ export default function ComboBoxPopoverTemplate(this: ComboBox) { return item.items .filter(nestedItem => !!nestedItem) .map(nestedItem => - + ); } // For regular items - return ; + return ; })} diff --git a/packages/main/src/MultiComboBoxItemCustom.ts b/packages/main/src/MultiComboBoxItemCustom.ts index 68182e737485..c117ff46fb26 100644 --- a/packages/main/src/MultiComboBoxItemCustom.ts +++ b/packages/main/src/MultiComboBoxItemCustom.ts @@ -6,7 +6,7 @@ import { eventStrict as event, } from "@ui5/webcomponents-base/dist/decorators.js"; import ComboBoxItemCustom from "./ComboBoxItemCustom.js"; -import CheckBox from "./CheckBox.js"; +import type CheckBox from "./CheckBox.js"; import type { IMultiComboBoxItem } from "./MultiComboBox.js"; import { ARIA_LABEL_LIST_ITEM_CHECKBOX, @@ -18,9 +18,9 @@ import type { AriaRole } from "@ui5/webcomponents-base"; /** * @class - * The `ui5-mcb-item-custom` is type of multi-combobox item, - * that can be used to place multi-combobox items with custom content. - * The text property is considered for filtering and token display. + * The `ui5-mcb-item-custom` is a multi-combobox item component + * that can be used to place custom content in the multi-combobox item. + * The text property is used for filtering and token display. * In case the user needs highlighting functionality, check "@ui5/webcomponents-base/dist/util/generateHighlightedMarkup.js" * * @constructor @@ -33,7 +33,6 @@ import type { AriaRole } from "@ui5/webcomponents-base"; tag: "ui5-mcb-item-custom", template: MultiComboBoxItemCustomTemplate, styles: [ComboBoxItemCustom.styles, styles], - dependencies: [...ComboBoxItemCustom.dependencies, CheckBox], }) @event("selection-requested", { @@ -44,15 +43,6 @@ class MultiComboBoxItemCustom extends ComboBoxItemCustom implements IMultiComboB "selection-requested": SelectionRequestEventDetail, } - /** - * Defines the selected state of the component. - * @default false - * @public - * @deprecated Set the `value` property on items and use the `selectedValues` property on the parent `ui5-multi-combobox` instead for programmatic selection. - */ - @property({ type: Boolean }) - selected = false; - /** * @private */ @@ -62,6 +52,17 @@ class MultiComboBoxItemCustom extends ComboBoxItemCustom implements IMultiComboB @i18n("@ui5/webcomponents") static i18nBundle: I18nBundle; + onBeforeRendering(): void { + // Synchronize selected state from parent's selectedValues + // This ensures the checkbox reflects the correct state + if (this.value) { + const parent = this.closest("[ui5-multi-combobox]"); + if (parent) { + this.selected = (parent as any).selectedValues?.includes(this.value) ?? false; + } + } + } + get isMultiComboBoxItem() { return true; } diff --git a/packages/main/test/pages/ComboBox.html b/packages/main/test/pages/ComboBox.html index 0693262cd50b..25c65629c592 100644 --- a/packages/main/test/pages/ComboBox.html +++ b/packages/main/test/pages/ComboBox.html @@ -725,16 +725,55 @@

ComboBox with Custom Items


- Selected value: + Selected option text:
- Selected selectedValue: + selectedValue: +
+
+ Select "New York, USA" + Select "London, UK" + Clear Selection diff --git a/packages/main/test/pages/MultiComboBox.html b/packages/main/test/pages/MultiComboBox.html index b9484c2c49e1..226b7ec7f48e 100644 --- a/packages/main/test/pages/MultiComboBox.html +++ b/packages/main/test/pages/MultiComboBox.html @@ -960,14 +960,44 @@

MultiComboBox with Custom Items

Selected values:
Selected selectedValues: +
+
+ Select NYC & London + Select All + Clear All diff --git a/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md b/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md index 17798ecc59ab..ef4652628b4d 100644 --- a/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md +++ b/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md @@ -1,4 +1,5 @@ import html from '!!raw-loader!./sample.html'; import js from '!!raw-loader!./main.js'; +import css from '!!raw-loader!./main.css'; - + diff --git a/packages/website/docs/_samples/main/ComboBox/CustomItems/main.css b/packages/website/docs/_samples/main/ComboBox/CustomItems/main.css new file mode 100644 index 000000000000..0abde2adf6bb --- /dev/null +++ b/packages/website/docs/_samples/main/ComboBox/CustomItems/main.css @@ -0,0 +1,3 @@ +body { + min-height: 500px; +} diff --git a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md index 17798ecc59ab..ef4652628b4d 100644 --- a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md +++ b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md @@ -1,4 +1,5 @@ import html from '!!raw-loader!./sample.html'; import js from '!!raw-loader!./main.js'; +import css from '!!raw-loader!./main.css'; - + diff --git a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/main.css b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/main.css new file mode 100644 index 000000000000..0abde2adf6bb --- /dev/null +++ b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/main.css @@ -0,0 +1,3 @@ +body { + min-height: 500px; +} From ec88d68368357162ff3ec29a9a12b5962df863eb Mon Sep 17 00:00:00 2001 From: Milen Karmidzhanov Date: Mon, 15 Jun 2026 17:18:31 +0300 Subject: [PATCH 09/10] feat(ui5-comobobox, ui5-multi-combobox): add custom items --- .../docs/_samples/main/ComboBox/CustomItems/CustomItems.md | 2 +- .../website/docs/_samples/main/ComboBox/CustomItems/main.css | 3 --- .../docs/_samples/main/ComboBox/CustomItems/sample.html | 2 +- .../_samples/main/MultiComboBox/CustomItems/CustomItems.md | 2 +- .../docs/_samples/main/MultiComboBox/CustomItems/main.css | 3 --- .../docs/_samples/main/MultiComboBox/CustomItems/sample.html | 2 +- 6 files changed, 4 insertions(+), 10 deletions(-) delete mode 100644 packages/website/docs/_samples/main/ComboBox/CustomItems/main.css delete mode 100644 packages/website/docs/_samples/main/MultiComboBox/CustomItems/main.css diff --git a/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md b/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md index ef4652628b4d..16e68e702b24 100644 --- a/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md +++ b/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md @@ -2,4 +2,4 @@ import html from '!!raw-loader!./sample.html'; import js from '!!raw-loader!./main.js'; import css from '!!raw-loader!./main.css'; - + diff --git a/packages/website/docs/_samples/main/ComboBox/CustomItems/main.css b/packages/website/docs/_samples/main/ComboBox/CustomItems/main.css deleted file mode 100644 index 0abde2adf6bb..000000000000 --- a/packages/website/docs/_samples/main/ComboBox/CustomItems/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - min-height: 500px; -} diff --git a/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html b/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html index 49312edc5ae2..4999cf3533b9 100644 --- a/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html +++ b/packages/website/docs/_samples/main/ComboBox/CustomItems/sample.html @@ -6,7 +6,7 @@ ComboBox Custom Items - + diff --git a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md index ef4652628b4d..16e68e702b24 100644 --- a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md +++ b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md @@ -2,4 +2,4 @@ import html from '!!raw-loader!./sample.html'; import js from '!!raw-loader!./main.js'; import css from '!!raw-loader!./main.css'; - + diff --git a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/main.css b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/main.css deleted file mode 100644 index 0abde2adf6bb..000000000000 --- a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/main.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - min-height: 500px; -} diff --git a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html index 1addf119a640..4f8ae63087ae 100644 --- a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html +++ b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/sample.html @@ -6,7 +6,7 @@ MultiComboBox Custom Items - + From 77c71afda4e13ab928250eb61e15b7a6793811a1 Mon Sep 17 00:00:00 2001 From: Milen Karmidzhanov Date: Mon, 15 Jun 2026 17:27:25 +0300 Subject: [PATCH 10/10] feat(ui5-comobobox, ui5-multi-combobox): add custom items --- .../docs/_samples/main/ComboBox/CustomItems/CustomItems.md | 1 - .../docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md b/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md index 16e68e702b24..17798ecc59ab 100644 --- a/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md +++ b/packages/website/docs/_samples/main/ComboBox/CustomItems/CustomItems.md @@ -1,5 +1,4 @@ import html from '!!raw-loader!./sample.html'; import js from '!!raw-loader!./main.js'; -import css from '!!raw-loader!./main.css'; diff --git a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md index 16e68e702b24..17798ecc59ab 100644 --- a/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md +++ b/packages/website/docs/_samples/main/MultiComboBox/CustomItems/CustomItems.md @@ -1,5 +1,4 @@ import html from '!!raw-loader!./sample.html'; import js from '!!raw-loader!./main.js'; -import css from '!!raw-loader!./main.css';