Skip to content

Commit 951e726

Browse files
feat(ui5-multi-combobox/ui5-combobox): introduce open property (#11044)
* feat(ui5-multi-combobox/ui5-combobox): introduce open property * feat(ui5-multi-combobox/ui5-combobox): fix failing tests * feat(ui5-multi-combobox/ui5-combobox): add close event
1 parent 41c23eb commit 951e726

File tree

6 files changed

+192
-32
lines changed

6 files changed

+192
-32
lines changed

packages/main/cypress/specs/ComboBox.cy.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,59 @@ describe("Security", () => {
1919
.should("have.text", "Albania<button onClick='alert(1)'>alert</button>");
2020
});
2121
});
22+
23+
describe("Event firing", () => {
24+
it("tests if open and close events are fired correctly", () => {
25+
cy.mount(
26+
<ComboBox>
27+
<ComboBoxItem text="Algeria"></ComboBoxItem>
28+
<ComboBoxItem text="Bulgaria"></ComboBoxItem>
29+
<ComboBoxItem text="England"></ComboBoxItem>
30+
</ComboBox>
31+
);
32+
33+
cy.get("ui5-combobox")
34+
.as("combo");
35+
36+
cy.get("@combo").then($combo => {
37+
$combo[0].addEventListener("focusin", () => {
38+
$combo[0].setAttribute("open", "true");
39+
});
40+
});
41+
42+
cy.get("@combo").then($combo => {
43+
$combo[0].addEventListener("ui5-open", cy.stub().as("comboOpened"));
44+
});
45+
46+
cy.get("@combo").then($combo => {
47+
$combo[0].addEventListener("ui5-close", cy.stub().as("comboClosed"));
48+
});
49+
50+
cy.get("@combo")
51+
.shadow()
52+
.find("input")
53+
.focus();
54+
55+
cy.get("@combo")
56+
.shadow()
57+
.find("ui5-icon")
58+
.as("icon");
59+
60+
cy.get("@icon")
61+
.click();
62+
63+
cy.get("@icon")
64+
.click();
65+
66+
cy.get("@combo")
67+
.shadow()
68+
.find("ui5-responsive-popover")
69+
.should("have.attr", "open");
70+
71+
cy.get("@comboClosed")
72+
.should("have.been.calledOnce");
73+
74+
cy.get("@comboOpened")
75+
.should("have.been.calledTwice");
76+
});
77+
});

packages/main/cypress/specs/MultiComboBox.cy.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,71 @@ describe("Value State", () => {
5656
.should("not.exist");
5757
});
5858
});
59+
60+
describe("Event firing", () => {
61+
it("tests if open and close events are fired correctly", () => {
62+
cy.mount(
63+
<MultiComboBox>
64+
<MultiComboBoxItem text="Algeria"></MultiComboBoxItem>
65+
<MultiComboBoxItem text="Bulgaria"></MultiComboBoxItem>
66+
<MultiComboBoxItem text="England"></MultiComboBoxItem>
67+
</MultiComboBox>
68+
);
69+
70+
cy.get("ui5-multi-combobox")
71+
.as("multiComboBox");
72+
73+
cy.get("@multiComboBox")
74+
.then($mcb => {
75+
$mcb[0].addEventListener("focusin", () => {
76+
$mcb[0].setAttribute("open", "true");
77+
});
78+
});
79+
80+
cy.get("@multiComboBox")
81+
.then($mcb => {
82+
$mcb[0].addEventListener("ui5-open", cy.stub().as("mcbOpened"));
83+
});
84+
85+
cy.get("@multiComboBox")
86+
.then($mcb => {
87+
$mcb[0].addEventListener("ui5-close", cy.stub().as("mcbClosed"));
88+
});
89+
90+
cy.get("@multiComboBox")
91+
.shadow()
92+
.find("input")
93+
.as("input");
94+
95+
cy.get("@input")
96+
.click();
97+
98+
cy.get("@multiComboBox")
99+
.shadow()
100+
.find("ui5-responsive-popover")
101+
.as("respPopover");
102+
103+
cy.get("@respPopover")
104+
.should("have.attr", "open");
105+
106+
cy.get("@mcbOpened")
107+
.should("have.been.calledOnce");
108+
109+
cy.get("@multiComboBox")
110+
.shadow()
111+
.find("ui5-icon")
112+
.as("icon");
113+
114+
cy.get("@icon")
115+
.click();
116+
117+
cy.get("@icon")
118+
.click();
119+
120+
cy.get("@mcbClosed")
121+
.should("have.been.calledOnce");
122+
123+
cy.get("@mcbOpened")
124+
.should("have.been.calledTwice");
125+
});
126+
});

packages/main/src/ComboBox.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,22 @@ type ComboBoxSelectionChangeEventDetail = {
172172
bubbles: true,
173173
})
174174

175+
/**
176+
* Fired when the dropdown is opened.
177+
* @since 2.9.0
178+
* @public
179+
*/
180+
@event("open", {
181+
bubbles: true,
182+
})
183+
184+
/**
185+
* Fired when the dropdown is closed.
186+
* @since 2.9.0
187+
* @public
188+
*/
189+
@event("close")
190+
175191
/**
176192
* Fired when typing in input or clear icon is pressed.
177193
*
@@ -194,6 +210,8 @@ class ComboBox extends UI5Element implements IFormInputElement {
194210
eventDetails!: {
195211
"change": void,
196212
"input": void,
213+
"open": void,
214+
"close": void,
197215
"selection-change": ComboBoxSelectionChangeEventDetail,
198216
}
199217
/**
@@ -363,10 +381,10 @@ class ComboBox extends UI5Element implements IFormInputElement {
363381

364382
/**
365383
* Indicates whether the items picker is open.
366-
* @private
367-
* @since 2.0.0
384+
* @public
385+
* @since 2.9.0
368386
*/
369-
@property({ type: Boolean, noAttribute: true })
387+
@property({ type: Boolean })
370388
open = false;
371389

372390
/**
@@ -526,6 +544,7 @@ class ComboBox extends UI5Element implements IFormInputElement {
526544
_afterOpenPopover() {
527545
this._iconPressed = true;
528546
this.inner.focus();
547+
this.fireDecoratorEvent("open");
529548
}
530549

531550
_afterClosePopover() {
@@ -545,6 +564,7 @@ class ComboBox extends UI5Element implements IFormInputElement {
545564
}
546565

547566
this.open = false;
567+
this.fireDecoratorEvent("close");
548568
}
549569

550570
_toggleRespPopover() {

packages/main/src/MultiComboBox.ts

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -401,10 +401,11 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
401401

402402
/**
403403
* Indicates whether the items picker is open.
404-
* @private
404+
* @public
405+
* @since 2.9.0
405406
*/
406-
@property({ type: Boolean, noAttribute: true })
407-
_open = false;
407+
@property({ type: Boolean })
408+
open = false;
408409

409410
@property()
410411
_valueBeforeOpen = this.value;
@@ -614,19 +615,19 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
614615
if (!changePrevented) {
615616
matchingItem.selected = !initiallySelected;
616617
this._getResponsivePopover().preventFocusRestore = false;
617-
this._open = false;
618+
this.open = false;
618619
this.value = "";
619620
}
620621
}
621622

622623
_toggleTokenizerPopover() {
623624
this.tokenizerOpen = false;
624-
this._open = !this.open;
625+
this.open = !this.open;
625626
}
626627

627628
togglePopoverByDropdownIcon() {
628629
this._shouldFilterItems = false;
629-
this._open = !this.open;
630+
this.open = !this.open;
630631
this.tokenizerOpen = false;
631632
}
632633

@@ -649,8 +650,8 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
649650
* @default false
650651
* @public
651652
*/
652-
get open(): boolean {
653-
return this._open;
653+
get isOpen(): boolean {
654+
return this.open;
654655
}
655656

656657
get _showAllItemsButtonPressed(): boolean {
@@ -704,9 +705,9 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
704705

705706
if (!isPhone()) {
706707
if (filteredItems.length === 0) {
707-
this._open = false;
708+
this.open = false;
708709
} else {
709-
this._open = true;
710+
this.open = true;
710711
}
711712
}
712713

@@ -753,7 +754,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
753754

754755
_onPopoverFocusOut() {
755756
if (!isPhone()) {
756-
this._tokenizer.expanded = this._open;
757+
this._tokenizer.expanded = this.open;
757758
}
758759
}
759760

@@ -962,7 +963,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
962963
this.value = this.valueBeforeAutoComplete;
963964
}
964965

965-
if (!this.noValidation || (!this._open && this.noValidation)) {
966+
if (!this.noValidation || (!this.open && this.noValidation)) {
966967
this.value = this._lastValue;
967968
}
968969
}
@@ -988,7 +989,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
988989
}
989990

990991
_handleTab() {
991-
this._open = false;
992+
this.open = false;
992993
}
993994

994995
_handleSelectAll() {
@@ -1144,7 +1145,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
11441145
_onItemTab() {
11451146
this._getResponsivePopover().preventFocusRestore = true;
11461147
this._inputDom.focus();
1147-
this._open = false;
1148+
this.open = false;
11481149
this._tokenizer.expanded = false;
11491150
}
11501151

@@ -1175,7 +1176,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
11751176
this._handleArrowDown();
11761177
}
11771178

1178-
if (!isArrowDown && !this._open && !this.readonly) {
1179+
if (!isArrowDown && !this.open && !this.readonly) {
11791180
this._navigateToPrevItem();
11801181
}
11811182
}
@@ -1315,7 +1316,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
13151316
}
13161317

13171318
innerInput.setSelectionRange(matchingItem.text!.length, matchingItem.text!.length);
1318-
this._open = false;
1319+
this.open = false;
13191320
} else if (this._internals?.form) {
13201321
submitForm(this);
13211322
}
@@ -1399,7 +1400,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
13991400
}
14001401

14011402
_afterOpen() {
1402-
const action = this._open ? "open" : "close";
1403+
const action = this.open ? "open" : "close";
14031404

14041405
if (!isPhone() && !this._isOpenedByKeyboard) {
14051406
this._innerInput.focus();
@@ -1472,7 +1473,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
14721473
}
14731474

14741475
if (!e.detail.selectionComponentPressed && !isSpace(castedEvent) && !isSpaceCtrl(castedEvent)) {
1475-
this._open = false;
1476+
this.open = false;
14761477
this.value = "";
14771478

14781479
// if the item (not checkbox) is clicked, call the selection change
@@ -1506,7 +1507,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
15061507

15071508
_click() {
15081509
if (isPhone() && !this.readonly && !this._showMorePressed && !this._deleting) {
1509-
this._open = true;
1510+
this.open = true;
15101511
}
15111512

15121513
this._showMorePressed = false;
@@ -1524,11 +1525,11 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
15241525
}
15251526

15261527
_beforeClose() {
1527-
this._open = false;
1528+
this.open = false;
15281529
}
15291530

15301531
_afterClose() {
1531-
const action = this._open ? "open" : "close";
1532+
const action = this.open ? "open" : "close";
15321533

15331534
// close device's keyboard and prevent further typing
15341535
if (isPhone()) {
@@ -1544,7 +1545,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
15441545
}
15451546

15461547
_beforeOpen() {
1547-
this._open = true;
1548+
this.open = true;
15481549
this._itemsBeforeOpen = this._getItems().map(item => {
15491550
return {
15501551
ref: item,
@@ -1694,7 +1695,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
16941695
}
16951696

16961697
storeResponsivePopoverWidth() {
1697-
if (this._open && !this._listWidth) {
1698+
if (this.open && !this._listWidth) {
16981699
this._listWidth = this.list!.offsetWidth;
16991700
}
17001701
}
@@ -1978,7 +1979,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement {
19781979
}
19791980

19801981
const isCurrentlyExpanded = this._tokenizer?.expanded;
1981-
const shouldBeExpanded = this.focused || this._open || isCurrentlyExpanded;
1982+
const shouldBeExpanded = this.focused || this.open || isCurrentlyExpanded;
19821983

19831984
return shouldBeExpanded;
19841985
}

packages/main/src/MultiComboBoxPopoverTemplate.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default function MultiComboBoxPopoverTemplate(this: MultiComboBox) {
2828
onOpen={this._afterOpen}
2929
onFocusOut={this._onPopoverFocusOut}
3030
accessibleName={this._popupLabel}
31-
open={this._open}
31+
open={this.open}
3232
opener={this}
3333
>
3434
{this._isPhone && <>

0 commit comments

Comments
 (0)