Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(ui5-combobox/multi-combobox): physical items #10051

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions packages/main/cypress/specs/LitKeyFunction.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,11 @@ describe("Lit HTML key function for #each", () => {
cy.realPress("a");

cy.get("@mcb")
.shadow()
.find(".ui5-multi-combobox-all-items-responsive-popover")
.as("popover");

cy.get("@popover")
.find(".ui5-multi-combobox-all-items-list > ui5-li")
.find("[ui5-mcb-item]")
.as("items");

cy.get("@items")
.eq(0)
.eq(3)
.realClick();

cy.get("@mcb")
Expand All @@ -39,12 +34,12 @@ describe("Lit HTML key function for #each", () => {

cy.get("@items")
.eq(0)
.should("contain.text", "<empty>")
.invoke("attr", "text", "<empty>")
.should("not.have.attr", "selected");

cy.get("@items")
.eq(3)
.should("contain.text", "USA")
.invoke("attr", "text", "USA")
.should("have.attr", "selected");
});
});
3 changes: 1 addition & 2 deletions packages/main/cypress/specs/base/Events.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,7 @@ describe("Event bubbling", () => {
.realClick();

cy.get("@multiCombobox")
.shadow()
.find("[ui5-responsive-popover]")
.find("[ui5-mcb-item]")
.should("be.visible");

// act - close the MultiComboBox
Expand Down
29 changes: 10 additions & 19 deletions packages/main/src/ComboBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,13 @@ interface IComboBoxItem extends UI5Element {
isGroupItem?: boolean,
selected?: boolean,
additionalText?: string,
stableDomRef: string,
_isVisible?: boolean,
items?: Array<IComboBoxItem>
}

type ValueStateAnnouncement = Record<Exclude<ValueState, ValueState.None>, string>;
type ValueStateTypeAnnouncement = Record<Exclude<ValueState, ValueState.None>, string>;

type ComboBoxListItem = ListItemStandard & {
mappedItem: ComboBoxItem
};

enum ValueStateIconMapping {
Negative = "error",
Critical = "alert",
Expand Down Expand Up @@ -398,7 +393,12 @@ class ComboBox extends UI5Element implements IFormInputElement {
* Defines the component items.
* @public
*/
@slot({ type: HTMLElement, "default": true, invalidateOnChildChange: true })
@slot({
type: HTMLElement,
"default": true,
individualSlots: true,
invalidateOnChildChange: true,
})
items!: Array<IComboBoxItem>;

/**
Expand Down Expand Up @@ -504,10 +504,6 @@ class ComboBox extends UI5Element implements IFormInputElement {
}

this.storeResponsivePopoverWidth();

this.items.forEach(item => {
item._getRealDomRef = () => this._getPicker().querySelector(`*[data-ui5-stable=${item.stableDomRef}]`)!;
});
}

_focusin(e: FocusEvent) {
Expand Down Expand Up @@ -1129,9 +1125,9 @@ class ComboBox extends UI5Element implements IFormInputElement {
}

_selectItem(e: CustomEvent<ListItemClickEventDetail>) {
const listItem = e.detail.item as ComboBoxListItem;
const item = e.detail.item as ComboBoxItem;

this._selectedItemText = listItem.mappedItem.text || "";
this._selectedItemText = item.text || "";
this._selectionPerformed = true;

const sameItemSelected = this.value === this._selectedItemText;
Expand All @@ -1144,17 +1140,12 @@ class ComboBox extends UI5Element implements IFormInputElement {

this.value = this._selectedItemText;

if (!listItem.mappedItem.selected) {
if (!item.selected) {
this.fireDecoratorEvent<ComboBoxSelectionChangeEventDetail>("selection-change", {
item: listItem.mappedItem,
item,
});
}

this._filteredItems.map(item => {
item.selected = (item === listItem.mappedItem && !item.isGroupItem);
return item;
});

this._fireChangeEvent();
this._closeRespPopover();

Expand Down
18 changes: 18 additions & 0 deletions packages/main/src/ComboBoxItem.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{{>include "./ListItemBase.hbs"}}

{{#*inline "listItemContent"}}
<div part="content" id="content" class="ui5-li-content">
<div class="ui5-li-text-wrapper">
<span part="title" class="ui5-li-title">
{{{text}}}
</span>
{{#if additionalText}}
<span part="additional-text" class="ui5-li-additional-text">{{additionalText}}</span>
{{/if}}
</div>
</div>
{{/inline}}

{{#*inline "listItemAttributes"}}
role="option"
{{/inline}}
27 changes: 19 additions & 8 deletions packages/main/src/ComboBoxItem.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import type { IComboBoxItem } from "./ComboBox.js";
import ListItemBase from "./ListItemBase.js";

import ComboBoxItemTemplate from "./generated/templates/ComboBoxItemTemplate.lit.js";
import ComboboxItemCss from "./generated/themes/ComboBoxItem.css.js";
/**
* @class
* The `ui5-cb-item` represents the item for a `ui5-combobox`.
* @constructor
* @extends UI5Element
* @abstract
* @extends ListItemBase
* @implements {IComboBoxItem}
* @public
*/
@customElement("ui5-cb-item")
class ComboBoxItem extends UI5Element implements IComboBoxItem {
@customElement({
tag: "ui5-cb-item",
template: ComboBoxItemTemplate,
styles: [ListItemBase.styles, ComboboxItemCss],
})
class ComboBoxItem extends ListItemBase implements IComboBoxItem {
/**
* Defines the text of the component.
* @default undefined
Expand Down Expand Up @@ -52,9 +57,15 @@ class ComboBoxItem extends UI5Element implements IComboBoxItem {
@property({ type: Boolean })
selected = false;

get stableDomRef() {
return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`;
}
/**
* Defines the markup text that will be displayed as suggestion.
* Used for highlighting the matching parts of the text.
*
* @since 2.4.0
* @private
*/
@property()
markupText = "";
}

ComboBoxItem.define();
Expand Down
9 changes: 9 additions & 0 deletions packages/main/src/ComboBoxItemGroup.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{>include "./ListItemGroup.hbs"}}

{{#*inline "items"}}
{{#each items}}
{{#if _isVisible}}
<slot name="{{this._individualSlot}}"></slot>
{{/if}}
{{/each}}
{{/inline}}
74 changes: 28 additions & 46 deletions packages/main/src/ComboBoxItemGroup.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,45 @@
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import type { IComboBoxItem } from "./ComboBox.js";
import ListItemGroup from "./ListItemGroup.js";
import type ComboBoxItem from "./ComboBoxItem.js";
import ComboBoxItemGroupTemplate from "./generated/templates/ComboBoxItemGroupTemplate.lit.js";

/**
* @class
* The `ui5-cb-group-item` is type of suggestion item,
* that can be used to split the `ui5-combobox` suggestions into groups.
* @constructor
* @extends UI5Element
* @extends ListItemGroup
* @abstract
* @public
* @implements {IComboBoxItem}
* @since 1.0.0-rc.15
*/
@customElement("ui5-cb-item-group")
class ComboBoxItemGroup extends UI5Element implements IComboBoxItem {
/**
* Defines the text of the component.
* @default undefined
* @public
*/
@property()
headerText?: string;

/**
* Indicates whether the item is focused
* @protected
*/
@property({ type: Boolean })
focused = false

/**
* Defines the items of the <code>ui5-cb-item-group</code>.
* @public
*/
@slot({
"default": true,
invalidateOnChildChange: true,
type: HTMLElement,
})
items!: Array<IComboBoxItem>;

/**
* Used to avoid tag name checks
* @protected
*/
get isGroupItem(): boolean {
return true;
}

get stableDomRef() {
return this.getAttribute("stable-dom-ref") || `${this._id}-stable-dom-ref`;
}

get _isVisible() {
return this.items.some(item => item._isVisible);
}
@customElement({
tag: "ui5-cb-item-group",
template: ComboBoxItemGroupTemplate,
})
class ComboBoxItemGroup extends ListItemGroup implements IComboBoxItem {
/**
* Defines the items of the <code>ui5-cb-item-group</code>.
* @public
*/
@slot({
"default": true,
invalidateOnChildChange: true,
individualSlots: true,
type: HTMLElement,
})
items!: Array<ComboBoxItem>;

get isGroupItem(): boolean {
return true;
}

get _isVisible() {
return this.items.some(item => item._isVisible);
}
}

ComboBoxItemGroup.define();
Expand Down
33 changes: 2 additions & 31 deletions packages/main/src/ComboBoxPopover.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,7 @@
selection-mode="Single"
>
{{#each _filteredItems}}
{{#if isGroupItem}}
{{#if _isVisible}}
<ui5-li-group header-text="{{this.headerText}}" ?focused={{this.focused}}>
{{#each this.items}}
{{#if _isVisible}}
{{> listItem}}
{{/if}}
{{/each}}
</ui5-li-group>
{{/if}}
{{else}}
{{> listItem}}
{{/if}}

<slot name="{{this._individualSlot}}"></slot>
{{/each}}
</ui5-list>

Expand Down Expand Up @@ -140,20 +127,4 @@
{{else}}
<slot name="valueStateMessage"></slot>
{{/if}}
{{/inline}}

{{#*inline "listItem"}}
<ui5-li
accessible-role="Option"
type="Active"
wrapping-type="Normal"
additional-text={{this.additionalText}}
group-name={{this.groupName}}
.mappedItem={{this}}
?selected={{this.selected}}
?focused={{this.focused}}
data-ui5-stable="{{this.stableDomRef}}"
>
{{this.text}}
</ui5-li>
{{/inline}}
{{/inline}}
4 changes: 2 additions & 2 deletions packages/main/src/List.ts
Original file line number Diff line number Diff line change
Expand Up @@ -873,10 +873,10 @@ class List extends UI5Element {

slottedItems.forEach(item => {
if (isInstanceOfListItemGroup(item)) {
const groupItems = [item.groupHeaderItem, ...item.items].filter(Boolean);
const groupItems = [item.groupHeaderItem, ...item.items.filter(listItem => listItem.assignedSlot)].filter(Boolean);
items.push(...groupItems);
} else {
items.push(item);
item.assignedSlot && items.push(item);
}
});

Expand Down
15 changes: 11 additions & 4 deletions packages/main/src/ListItemGroup.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,25 @@
@dragover="{{_ondragover}}"
@drop="{{_ondrop}}"
@dragleave="{{_ondragleave}}">

{{#if hasHeader}}
<ui5-li-group-header ?focused="{{focused}}" part="header">
{{#if hasFormattedHeader}}
<slot name="header"></slot>
<slot name="header"></slot>
{{else}}
{{headerText}}
{{headerText}}
{{/if}}
</ui5-li-group-header>
{{/if}}
<slot></slot>

{{> items}}
<ui5-drop-indicator
orientation="Horizontal"
.ownerReference="{{this}}"
></ui5-drop-indicator>
</ul>
</ul>


{{#*inline "items"}}
<slot></slot>
{{/inline}}
Loading
Loading