Skip to content
Merged
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
81 changes: 81 additions & 0 deletions packages/fiori/cypress/specs/ShellBar.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,87 @@ describe("Events", () => {
cy.get("@logoClickSmall")
.should("have.been.calledOnce");
});

it("Test search field clear event default behavior", () => {
cy.mount(
<ShellBar showSearchField={true}>
<ShellBarSearch id="search" slot="searchField" value="test search text"></ShellBarSearch>
</ShellBar>
);

cy.get("[ui5-shellbar]")
.as("shellbar");

// Set up event listener without preventing default
cy.get("@shellbar")
.then(shellbar => {
shellbar.get(0).addEventListener("ui5-search-field-clear", cy.stub().as("searchFieldClear"));
});

// Trigger full width search mode by reducing viewport
cy.viewport(400, 800);

// Manually call the cancel button handler
cy.get<ShellBar>("@shellbar").then(shellbar => {
const shellbarInstance = shellbar.get(0);
// Call the private method directly to simulate cancel button press
shellbarInstance._handleCancelButtonPress();
});

// Verify the event was fired
cy.get("@searchFieldClear")
.should("have.been.calledOnce");

// Verify search field value is cleared (default behavior)
cy.get("#search")
.should("have.value", "");

// Verify search is closed
cy.get("@shellbar")
.should("have.prop", "showSearchField", false);
});

it("Test search field clear event can be prevented", () => {
cy.mount(
<ShellBar showSearchField={true}>
<ShellBarSearch id="search" slot="searchField" value="test search text"></ShellBarSearch>
</ShellBar>
);

cy.get("[ui5-shellbar]")
.as("shellbar");

// Set up event listener that prevents default
cy.get("@shellbar")
.then(shellbar => {
shellbar.get(0).addEventListener("ui5-search-field-clear", (event) => {
event.preventDefault();
});
shellbar.get(0).addEventListener("ui5-search-field-clear", cy.stub().as("searchFieldClear"));
});

// Trigger full width search mode by reducing viewport
cy.viewport(400, 800);

// Manually call the cancel button handler
cy.get<ShellBar>("@shellbar").then(shellbar => {
const shellbarInstance = shellbar.get(0);
// Call the private method directly to simulate cancel button press
shellbarInstance._handleCancelButtonPress();
});

// Verify the event was fired
cy.get("@searchFieldClear")
.should("have.been.calledOnce");

// Verify search field value is preserved (due to preventDefault)
cy.get("#search")
.should("have.value", "test search text");

// Verify search is closed
cy.get("@shellbar")
.should("have.prop", "showSearchField", false);
});
});

describe("ButtonBadge in ShellBar", () => {
Expand Down
46 changes: 44 additions & 2 deletions packages/fiori/src/ShellBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ type ShellBarSearchFieldToggleEventDetail = {
expanded: boolean;
};

type ShellBarSearchFieldClearEventDetail = {
targetRef: HTMLElement;
};

interface IShellBarSearchField extends HTMLElement {
focused: boolean;
value: string;
Expand Down Expand Up @@ -291,6 +295,20 @@ const PREDEFINED_PLACE_ACTIONS = ["feedback", "sys-help"];
bubbles: true,
})

/**
* Fired, when the search cancel button is activated.
*
* **Note:** You can prevent the default behavior (clearing the search field value) by calling `event.preventDefault()`. The search will still be closed.
* **Note:** The `search-field-clear` event is in an experimental state and is a subject to change.
* @param {HTMLElement} targetRef dom ref of the cancel button element
* @since 2.14.0
* @public
*/
@event("search-field-clear", {
cancelable: true,
bubbles: true,
})

/**
* Fired, when an item from the content slot is hidden or shown.
* **Note:** The `content-item-visibility-change` event is in an experimental state and is a subject to change.
Expand All @@ -312,6 +330,7 @@ class ShellBar extends UI5Element {
"menu-item-click": ShellBarMenuItemClickEventDetail,
"search-button-click": ShellBarSearchButtonEventDetail,
"search-field-toggle": ShellBarSearchFieldToggleEventDetail,
"search-field-clear": ShellBarSearchFieldClearEventDetail,
"content-item-visibility-change": ShellBarContentItemVisibilityChangeEventDetail
}

Expand Down Expand Up @@ -661,9 +680,16 @@ class ShellBar extends UI5Element {
this._detachSearchFieldListeners(e.target as HTMLElement);
return;
}
if (!isPhone() && !this.search?.value) {
this.setSearchState(!this.showSearchField);

// Decide when to toggle the search field:
// - On mobile, the search opens on its own (we don’t interfere).
// - If there’s already a value, onSearch is responsible for triggering the search (we don’t interfere)
// - If the field is closed, we must open it regardless.
if (isPhone() || (this.search?.value && this.showSearchField)) {
return;
}

this.setSearchState(!this.showSearchField);
}

_updateSearchFieldState() {
Expand Down Expand Up @@ -1104,8 +1130,17 @@ class ShellBar extends UI5Element {
}

_handleCancelButtonPress() {
const cancelButtonRef = this.shadowRoot!.querySelector<Button>(".ui5-shellbar-cancel-button")!;
const clearDefaultPrevented = !this.fireDecoratorEvent("search-field-clear", {
targetRef: cancelButtonRef,
});

this.showFullWidthSearch = false;
this.setSearchState(false);

if (!clearDefaultPrevented) {
this._clearSearchFieldValue();
}
}

_handleProductSwitchPress(e: UI5CustomEvent<Button, "click">) {
Expand All @@ -1117,6 +1152,12 @@ class ShellBar extends UI5Element {
});
}

_clearSearchFieldValue() {
if (this.search) {
this.search.value = "";
}
}

/**
* Returns the `logo` DOM ref.
* @public
Expand Down Expand Up @@ -1773,6 +1814,7 @@ export type {
ShellBarAccessibilityAttributes,
ShellBarSearchButtonEventDetail,
ShellBarSearchFieldToggleEventDetail,
ShellBarSearchFieldClearEventDetail,
IShellBarSelfCollapsibleSearch,
IShellBarSearchField,
};
Loading