Skip to content
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
79 changes: 79 additions & 0 deletions packages/fiori/cypress/specs/NotificationList.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1131,3 +1131,82 @@ describe("Notification List Item Without a Group", () => {
});

});

describe("NotificationListItem semantic click event", () => {
it("fires click event when clicked", () => {
cy.mount(
<NotificationList id="nl1">
<NotificationListItem id="nli1">Item 1</NotificationListItem>
</NotificationList>
);

cy.get("#nli1").then(($item) => {
$item[0].addEventListener("click", cy.stub().as("clickStub"));
});

cy.get("#nli1").realClick();

cy.get("@clickStub").should("have.been.calledOnce");
cy.get("@clickStub").should((stub: any) => {
const event = stub.firstCall.args[0];
expect(event).to.be.instanceOf(CustomEvent);
expect(event.detail.item).to.exist;
expect(event.detail.originalEvent).to.be.instanceOf(MouseEvent);
});
});

it("fires click event when activated with Enter key", () => {
cy.mount(
<NotificationList>
<NotificationListItem id="nli1">Item 1</NotificationListItem>
</NotificationList>
);

cy.get("#nli1").then(($item) => {
$item[0].addEventListener("click", cy.stub().as("clickStub"));
});

cy.get("#nli1").realClick();
cy.realPress("Enter");

cy.get("@clickStub").should("have.been.calledTwice");
});

it("fires click event when activated with Space key", () => {
cy.mount(
<NotificationList>
<NotificationListItem id="nli1">Item 1</NotificationListItem>
</NotificationList>
);

cy.get("#nli1").then(($item) => {
$item[0].addEventListener("click", cy.stub().as("clickStub"));
});

cy.get("#nli1").realClick();
cy.realPress("Space");

cy.get("@clickStub").should("have.been.calledTwice");
});

it("fires both click on item and item-click on NotificationList", () => {
cy.mount(
<NotificationList id="nl1">
<NotificationListItem id="nli1">Item 1</NotificationListItem>
</NotificationList>
);

cy.get("#nli1").then(($item) => {
$item[0].addEventListener("click", cy.stub().as("itemClickStub"));
});

cy.get("#nl1").then(($list) => {
$list[0].addEventListener("ui5-item-click", cy.stub().as("listItemClickStub"));
});

cy.get("#nli1").realClick();

cy.get("@itemClickStub").should("have.been.calledOnce");
cy.get("@listItemClickStub").should("have.been.calledOnce");
});
});
27 changes: 24 additions & 3 deletions packages/fiori/src/NotificationListItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ type NotificationListItemPressEventDetail = {
item: NotificationListItem,
};

type NotificationListItemClickEventDetail = {
item: NotificationListItem,
originalEvent: Event,
};

type Footnote = Record<string, any>;

/**
Expand Down Expand Up @@ -141,6 +146,18 @@ const ICON_PER_STATUS_DESIGN = {
bubbles: true,
})

/**
* Fired when the component is activated either with a mouse/tap or by using the Enter or Space key.
*
* @since 2.22.0
* @public
* @param {NotificationListItem} item The activated item.
* @param {Event} originalEvent The original event from the user interaction.
*/
@event("click", {
bubbles: true,
})

/**
* Fired when the `Close` button is pressed.
* @param {HTMLElement} item the closed item.
Expand All @@ -160,6 +177,7 @@ const ICON_PER_STATUS_DESIGN = {
class NotificationListItem extends NotificationListItemBase {
eventDetails!: NotificationListItemBase["eventDetails"] & {
_press: NotificationListItemPressEventDetail,
click: NotificationListItemClickEventDetail,
close: NotificationListItemCloseEventDetail,
_close: NotificationListItemCloseEventDetail,
}
Expand Down Expand Up @@ -478,8 +496,9 @@ class NotificationListItem extends NotificationListItemBase {
/**
* Event handlers
*/
_onclick() {
this.fireItemPress();
_onclick(e: MouseEvent) {
e.stopPropagation();
this.fireItemPress(e);
}

_onShowMoreClick(e: UI5CustomEvent<Link, "click">) {
Expand Down Expand Up @@ -549,14 +568,15 @@ class NotificationListItem extends NotificationListItemBase {
/**
* Private
*/
fireItemPress() {
fireItemPress(e: Event) {
if (this.getFocusDomRef()!.matches(":has(:focus-within)")) {
return;
}

// NotificationListItem will never be assigned to a variable of type ListItemBase
// typescipt complains here, if that is the case, the parameter to the _press event handler could be a ListItemBase item,
// but this is never the case, all components are used by their class and never assigned to a variable with a type of ListItemBase
this.fireDecoratorEvent("click", { item: this, originalEvent: e });
this.fireDecoratorEvent("_press", { item: this });
}

Expand Down Expand Up @@ -591,5 +611,6 @@ NotificationListItem.define();
export default NotificationListItem;
export type {
NotificationListItemPressEventDetail,
NotificationListItemClickEventDetail,
NotificationListItemCloseEventDetail,
};
2 changes: 2 additions & 0 deletions packages/fiori/src/NotificationListItemBase.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isSpace, isF2 } from "@ui5/webcomponents-base/dist/Keys.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
Expand All @@ -21,6 +22,7 @@ import {
* @since 1.0.0-rc.8
* @public
*/
@customElement({})
class NotificationListItemBase extends ListItemBase {
eventDetails!: ListItemBase["eventDetails"];
/**
Expand Down
2 changes: 2 additions & 0 deletions packages/fiori/src/Search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ import type Input from "@ui5/webcomponents/dist/Input.js";
import type { PopupBeforeCloseEventDetail } from "@ui5/webcomponents/dist/Popup.js";
import type Select from "@ui5/webcomponents/dist/Select.js";
import type { Slot, DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.js";
import type { ListItemBaseClickEventDetail } from "@ui5/webcomponents/dist/ListItemBase.js";

interface ISearchSuggestionItem extends UI5Element {
selected: boolean;
text: string;
items?: ISearchSuggestionItem[];
eventDetails: { click?: ListItemBaseClickEventDetail };
}

type SearchEventDetails = {
Expand Down
5 changes: 3 additions & 2 deletions packages/fiori/src/SearchItemShowMore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import customElement from "@ui5/webcomponents-base/dist/decorators/customElement
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
import ListItemBase from "@ui5/webcomponents/dist/ListItemBase.js";
import type { ListItemBaseClickEventDetail } from "@ui5/webcomponents/dist/ListItemBase.js";
import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js";
import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
Expand All @@ -11,7 +12,7 @@ import SearchItemShowMoreCss from "./generated/themes/SearchItemShowMore.css.js"
import { SEARCH_ITEM_SHOW_MORE_COUNT, SEARCH_ITEM_SHOW_MORE_NO_COUNT } from "./generated/i18n/i18n-defaults.js";
import { isEnter, isSpace } from "@ui5/webcomponents-base/dist/Keys.js";

type ShowMoreItemClickEventDetail = {
type ShowMoreItemClickEventDetail = ListItemBaseClickEventDetail & {
fromKeyboard: boolean;
}

Expand Down Expand Up @@ -100,7 +101,7 @@ If a number is provided, it displays "Show more (N)", where N is that number.

_onclick(e: MouseEvent | KeyboardEvent, fromKeyboard = false) {
e.stopImmediatePropagation();
this.fireDecoratorEvent("click", { fromKeyboard });
this.fireDecoratorEvent("click", { item: this, originalEvent: e, fromKeyboard });
}

_onkeydown(e: KeyboardEvent) {
Expand Down
100 changes: 100 additions & 0 deletions packages/main/cypress/specs/TabContainer.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1287,3 +1287,103 @@ describe("TabContainer popover", () => {
cy.get("@list").find(".ui5-tab-overflow-itemContent-wrapper").eq(3).should("have.css", "padding-left", "24px");
});
});

describe("Tab semantic click event", () => {
it("fires click event on tab when clicked", () => {
cy.mount(
<TabContainer id="tc1" collapsed>
<Tab id="tab1" text="Products"></Tab>
<Tab text="Laptops" selected></Tab>
</TabContainer>
);

cy.get("#tab1").then(($tab) => {
$tab[0].addEventListener("click", cy.stub().as("clickStub"));
});

cy.get("#tc1").shadow().find(".ui5-tab-strip-item:nth-child(1)").realClick();

cy.get("@clickStub").should("have.been.calledOnce");
cy.get("@clickStub").should((stub: any) => {
const event = stub.firstCall.args[0];
expect(event).to.be.instanceOf(CustomEvent);
expect(event.detail.tab).to.exist;
expect(event.detail.originalEvent).to.be.instanceOf(MouseEvent);
});
});

it("fires click event on tab when activated with Enter key", () => {
cy.mount(
<TabContainer id="tc1" collapsed>
<Tab id="tab1" text="Products"></Tab>
<Tab text="Laptops" selected></Tab>
</TabContainer>
);

cy.get("#tab1").then(($tab) => {
$tab[0].addEventListener("click", cy.stub().as("clickStub"));
});

cy.get("#tc1").shadow().find(".ui5-tab-strip-item:nth-child(1)").realClick();
cy.realPress("Enter");

cy.get("@clickStub").should("have.been.calledTwice");
});

it("fires click event on tab when activated with Space key", () => {
cy.mount(
<TabContainer id="tc1" collapsed>
<Tab id="tab1" text="Products"></Tab>
<Tab text="Laptops" selected></Tab>
</TabContainer>
);

cy.get("#tab1").then(($tab) => {
$tab[0].addEventListener("click", cy.stub().as("clickStub"));
});

cy.get("#tc1").shadow().find(".ui5-tab-strip-item:nth-child(1)").realClick();
cy.realPress("Space");

cy.get("@clickStub").should("have.been.calledTwice");
});

it("does not fire click event on disabled tab", () => {
cy.mount(
<TabContainer id="tc1" collapsed>
<Tab id="tab1" text="Products" disabled></Tab>
<Tab text="Laptops" selected></Tab>
</TabContainer>
);

cy.get("#tab1").then(($tab) => {
$tab[0].addEventListener("click", cy.stub().as("clickStub"));
});

cy.get("#tc1").shadow().find(".ui5-tab-strip-item:nth-child(1)").click({ force: true });

cy.get("@clickStub").should("not.have.been.called");
});

it("fires both click on Tab and tab-select on TabContainer", () => {
cy.mount(
<TabContainer id="tc1" collapsed>
<Tab id="tab1" text="Products"></Tab>
<Tab text="Laptops" selected></Tab>
</TabContainer>
);

cy.get("#tab1").then(($tab) => {
$tab[0].addEventListener("click", cy.stub().as("tabClickStub"));
});

cy.get("#tc1").then(($tc) => {
$tc[0].addEventListener("ui5-tab-select", cy.stub().as("tabSelectStub"));
});

cy.get("#tc1").shadow().find(".ui5-tab-strip-item:nth-child(1)").realClick();

cy.get("@tabClickStub").should("have.been.calledOnce");
cy.get("@tabSelectStub").should("have.been.calledOnce");
});
});
4 changes: 3 additions & 1 deletion packages/main/src/ComboBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import { isInstanceOfComboBoxItemGroup } from "./ComboBoxItemGroup.js";
import type ComboBoxFilter from "./types/ComboBoxFilter.js";
import type Input from "./Input.js";
import type { InputEventDetail } from "./Input.js";
import type { ListItemBaseClickEventDetail } from "./ListItemBase.js";
import type InputComposition from "./features/InputComposition.js";

const SKIP_ITEMS_SIZE = 10;
Expand All @@ -107,7 +108,8 @@ interface IComboBoxItem extends UI5Element {
selected?: boolean,
additionalText?: string,
_isVisible?: boolean,
items?: Array<IComboBoxItem>
items?: Array<IComboBoxItem>,
eventDetails: { click?: ListItemBaseClickEventDetail },
}

type ValueStateAnnouncement = Record<Exclude<ValueState, ValueState.None>, string>;
Expand Down
2 changes: 2 additions & 0 deletions packages/main/src/Input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ import ResponsivePopoverCommonCss from "./generated/themes/ResponsivePopoverComm
import ValueStateMessageCss from "./generated/themes/ValueStateMessage.css.js";
import SuggestionsCss from "./generated/themes/Suggestions.css.js";
import type { ListItemClickEventDetail, ListSelectionChangeEventDetail } from "./List.js";
import type { ListItemBaseClickEventDetail } from "./ListItemBase.js";
import type ResponsivePopover from "./ResponsivePopover.js";
import type InputKeyHint from "./types/InputKeyHint.js";
import type InputComposition from "./features/InputComposition.js";
Expand All @@ -110,6 +111,7 @@ interface IInputSuggestionItem extends UI5Element {
focused: boolean;
additionalText?: string;
items?: IInputSuggestionItem[];
eventDetails: { click?: ListItemBaseClickEventDetail };
}

interface IInputSuggestionItemSelectable extends IInputSuggestionItem {
Expand Down
Loading
Loading