Skip to content

Commit 6ad3386

Browse files
authored
refactor(ui5-shellbar): extend mobile support for search field (SAP#11419)
Improved the ShellBar component's search field behavior to align with UX specifications and support responsive, mobile interactions. **Summary of Changes** - **UX Compliance**: Prevented the search field from opening in full-width mode by default; initial showSearchField state is now set to false unless triggered. - **Event Handling:** Added support for ui5-open, ui5-close, and ui5-search events to dynamically manage `show-search-field` state. - **Testing Enhancements:** Added a dedicated mobile test suite (ShellBar.mobile.cy.tsx) and updated existing tests to reflect the component changes.
1 parent 62a4094 commit 6ad3386

File tree

5 files changed

+115
-32
lines changed

5 files changed

+115
-32
lines changed

packages/fiori/cypress/specs/ShellBar.cy.tsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import ToggleButton from "@ui5/webcomponents/dist/ToggleButton.js";
1111
import ListItemStandard from "@ui5/webcomponents/dist/ListItemStandard.js";
1212
import Avatar from "@ui5/webcomponents/dist/Avatar.js";
1313
import Switch from "@ui5/webcomponents/dist/Switch.js";
14-
import Search from "../../src/Search.js";
14+
import ShellBarSearch from "../../src/ShellBarSearch.js";
1515

1616
const RESIZE_THROTTLE_RATE = 300; // ms
1717

@@ -454,7 +454,7 @@ describe("Slots", () => {
454454
it("Test search button is not visible when a self-collapsible search field slot is empty", () => {
455455
cy.mount(
456456
<ShellBar id="shellbar">
457-
<Search slot="searchField"></Search>
457+
<ShellBarSearch slot="searchField"></ShellBarSearch>
458458
</ShellBar>
459459
);
460460
cy.get("#shellbar")
@@ -466,32 +466,36 @@ describe("Slots", () => {
466466
it("Test self-collapsible search is expanded and collapsed by the show-search-field property", () => {
467467
cy.mount(
468468
<ShellBar id="shellbar" showSearchField={true}>
469-
<Search id="search" slot="searchField"></Search>
469+
<ShellBarSearch id="search" slot="searchField"></ShellBarSearch>
470470
</ShellBar>
471471
);
472472
cy.get("#search").should("have.prop", "collapsed", false);
473473
cy.get("#shellbar").invoke("prop", "showSearchField", false);
474474
cy.get("#search").should("have.prop", "collapsed", true);
475475
});
476476

477-
478-
it("Test showSearchField property is true when using expanded search field", () => {
477+
it("Test showSearchField property is false when using collapsed search field", () => {
479478
cy.mount(
480479
<ShellBar id="shellbar">
481-
<Search id="search" slot="searchField"></Search>
480+
<ShellBarSearch id="search" slot="searchField" collapsed></ShellBarSearch>
482481
</ShellBar>
483482
);
484-
cy.get("#search").should("have.prop", "collapsed", false);
485-
cy.get("#shellbar").invoke("prop", "showSearchField").should("equal", true);
483+
cy.get("#search").should("have.prop", "collapsed", true);
484+
cy.get("#shellbar").invoke("prop", "showSearchField").should("equal", false);
486485
});
487486

488-
it("Test showSearchField property is false when using collapsed search field", () => {
487+
it("Test search field is collapsed initially instead of being displayed in full width mode", () => {
488+
cy.viewport(500, 1080);
489489
cy.mount(
490-
<ShellBar id="shellbar">
491-
<Search id="search" slot="searchField" collapsed></Search>
490+
// needs some content to trigger the full width mode
491+
<ShellBar id="shellbar" showSearchField={true} showNotifications={true} showProductSwitch={true}>
492+
<Button icon={navBack} slot="startButton"></Button>
493+
<img slot="logo" src="https://upload.wikimedia.org/wikipedia/commons/5/59/SAP_2011_logo.svg" />
494+
<Button slot="content">Start Button 1</Button>
495+
496+
<Input id="search" slot="searchField"></Input>
492497
</ShellBar>
493498
);
494-
cy.get("#search").should("have.prop", "collapsed", true);
495499
cy.get("#shellbar").invoke("prop", "showSearchField").should("equal", false);
496500
});
497501
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import ShellBar from "../../src/ShellBar.js";
2+
import ShellBarSearch from "../../src/ShellBarSearch.js";
3+
4+
describe("Mobile Behaviour", () => {
5+
beforeEach(() => {
6+
cy.ui5SimulateDevice();
7+
});
8+
9+
it("Test self-collapsible search is expanded and collapsed by the show-search-field property", () => {
10+
cy.mount(
11+
<ShellBar id="shellbar" showSearchField={true}>
12+
<ShellBarSearch id="search" slot="searchField"></ShellBarSearch>
13+
</ShellBar>
14+
);
15+
16+
cy.get("#search").should("have.prop", "open", true);
17+
});
18+
19+
it("Test shellbar should have show-search-field when search is open", () => {
20+
cy.mount(
21+
<ShellBar id="shellbar">
22+
<ShellBarSearch id="search" slot="searchField" open={true}></ShellBarSearch>
23+
</ShellBar>
24+
);
25+
26+
cy.get("#shellbar").should("have.prop", "showSearchField", true);
27+
});
28+
});

packages/fiori/src/ShellBar.ts

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import Icon from "@ui5/webcomponents/dist/Icon.js";
2525
import type Input from "@ui5/webcomponents/dist/Input.js";
2626
import type { IButton } from "@ui5/webcomponents/dist/Button.js";
2727
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
28-
import { isDesktop } from "@ui5/webcomponents-base/dist/Device.js";
28+
import { isDesktop, isPhone } from "@ui5/webcomponents-base/dist/Device.js";
2929
import search from "@ui5/webcomponents-icons/dist/search.js";
3030
import da from "@ui5/webcomponents-icons/dist/da.js";
3131
import bell from "@ui5/webcomponents-icons/dist/bell.js";
@@ -368,6 +368,16 @@ class ShellBar extends UI5Element {
368368
@property({ type: Boolean })
369369
showProductSwitch = false;
370370

371+
/**
372+
* Defines, if the Search Field would be displayed when there is a valid `searchField` slot.
373+
*
374+
* **Note:** By default the Search Field is not displayed.
375+
* @default false
376+
* @public
377+
*/
378+
@property({ type: Boolean })
379+
showSearchField = false;
380+
371381
/**
372382
* Defines additional accessibility attributes on different areas of the component.
373383
*
@@ -541,7 +551,9 @@ class ShellBar extends UI5Element {
541551
_autoRestoreSearchField = false;
542552

543553
_headerPress: () => void;
544-
_showSearchField = false;
554+
onSearchOpen: () => void;
555+
onSearchClose: () => void;
556+
onSearch: () => void;
545557

546558
static get FIORI_3_BREAKPOINTS() {
547559
return [
@@ -585,6 +597,24 @@ class ShellBar extends UI5Element {
585597
}
586598
};
587599

600+
this.onSearchOpen = () => {
601+
if (isPhone()) {
602+
this.setSearchState(true);
603+
}
604+
};
605+
606+
this.onSearchClose = () => {
607+
if (isPhone()) {
608+
this.setSearchState(false);
609+
}
610+
};
611+
612+
this.onSearch = () => {
613+
if (!isPhone() && !this.search?.value) {
614+
this.setSearchState(!this.showSearchField);
615+
}
616+
};
617+
588618
this._handleResize = throttle(() => {
589619
this.menuPopover = this._getMenuPopover();
590620
this.overflowPopover = this._getOverflowPopover();
@@ -764,35 +794,30 @@ class ShellBar extends UI5Element {
764794
});
765795

766796
this._observeContentItems();
767-
}
768797

769-
/**
770-
* Defines, if the Search Field would be displayed when there is a valid `searchField` slot.
771-
*
772-
* **Note:** By default the Search Field is not displayed.
773-
* @default false
774-
* @public
775-
*/
776-
@property({ type: Boolean })
777-
set showSearchField(value: boolean) {
778-
if (isSelfCollapsibleSearch(this.search)) {
779-
this.search.collapsed = !value;
798+
// search field shouldn't be expanded initially in full width mode
799+
if (this.showFullWidthSearch && this._isInitialRendering) {
800+
this.setSearchState(false);
801+
this._autoRestoreSearchField = true;
780802
}
781-
this._showSearchField = value;
782-
}
783803

784-
get showSearchField(): boolean {
785804
if (isSelfCollapsibleSearch(this.search)) {
786-
return !this.search.collapsed;
805+
if (isPhone()) {
806+
this.search.open = this.showSearchField;
807+
} else {
808+
this.search.collapsed = !this.showSearchField;
809+
}
787810
}
788-
return this._showSearchField;
789811
}
790812

791813
/**
792814
* Use this method to change the state of the search filed according to internal logic.
793815
* An event is fired to notify the change.
794816
*/
795817
async setSearchState(expanded: boolean) {
818+
if (expanded === this.showSearchField) {
819+
return;
820+
}
796821
this.showSearchField = expanded;
797822
await renderFinished();
798823
this.fireDecoratorEvent("search-field-toggle", { expanded });
@@ -907,6 +932,13 @@ class ShellBar extends UI5Element {
907932

908933
onEnterDOM() {
909934
ResizeHandler.register(this, this._handleResize);
935+
936+
if (isSelfCollapsibleSearch(this.search)) {
937+
this.search.addEventListener("ui5-open", this.onSearchOpen);
938+
this.search.addEventListener("ui5-close", this.onSearchClose);
939+
this.search.addEventListener("ui5-search", this.onSearch);
940+
}
941+
910942
if (isDesktop()) {
911943
this.setAttribute("desktop", "");
912944
}
@@ -916,6 +948,12 @@ class ShellBar extends UI5Element {
916948
this.contentItemsObserver.disconnect();
917949
this._observableContent = [];
918950
ResizeHandler.deregister(this, this._handleResize);
951+
952+
if (isSelfCollapsibleSearch(this.search)) {
953+
this.search.removeEventListener("ui5-open", this.onSearchOpen);
954+
this.search.removeEventListener("ui5-close", this.onSearchClose);
955+
this.search.removeEventListener("ui5-search", this.onSearch);
956+
}
919957
}
920958

921959
_handleSearchIconPress() {
@@ -1636,11 +1674,12 @@ class ShellBar extends UI5Element {
16361674

16371675
interface IShellBarSelfCollapsibleSearch {
16381676
collapsed: boolean;
1677+
open: boolean;
16391678
}
16401679

16411680
const isSelfCollapsibleSearch = (searchField: any): searchField is IShellBarSelfCollapsibleSearch => {
16421681
if (searchField) {
1643-
return "collapsed" in searchField;
1682+
return "collapsed" in searchField && "open" in searchField;
16441683
}
16451684
return false;
16461685
};

packages/fiori/src/themes/ShellBar.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,10 @@ slot[name="profile"] {
487487
width: auto;
488488
}
489489

490+
.ui5-shellbar-search-full-width-wrapper ::slotted([ui5-shellbar-search]) {
491+
max-width: unset;
492+
}
493+
490494
::slotted([ui5-input]) {
491495
background: var(--_ui5_shellbar_search_field_background);
492496
border: var(--_ui5_shellbar_search_field_border);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
:host(:not([hidden])) {
2+
width: 100%;
3+
}
4+
5+
:host [ui5-select] {
6+
width: inherit;
7+
}
8+

0 commit comments

Comments
 (0)