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: add strict event type checking #10235

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
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
5 changes: 4 additions & 1 deletion packages/ai/src/Button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import { renderFinished } from "@ui5/webcomponents-base/dist/Render.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import event from "@ui5/webcomponents-base/dist/decorators/event.js";
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import MainButton from "@ui5/webcomponents/dist/Button.js";
Expand Down Expand Up @@ -63,6 +63,9 @@ import ButtonCss from "./generated/themes/Button.css.js";
bubbles: true,
})
class Button extends UI5Element {
eventDetails!: {
click: void,
}
/**
* Defines the component design.
* @default "Default"
Expand Down
7 changes: 6 additions & 1 deletion packages/ai/src/PromptInput.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import event from "@ui5/webcomponents-base/dist/decorators/event.js";
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
Expand Down Expand Up @@ -87,6 +87,11 @@ import PromptInputCss from "./generated/themes/PromptInput.css.js";
bubbles: true,
})
class PromptInput extends UI5Element {
eventDetails!: {
submit: void;
input: void;
change: void;
}
/**
* Defines the value of the component.
*
Expand Down
9 changes: 5 additions & 4 deletions packages/base/src/UI5Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ function getPropertyDescriptor(proto: any, name: PropertyKey): PropertyDescripto
* @public
*/
abstract class UI5Element extends HTMLElement {
eventDetails!: object;
__id?: string;
_suppressInvalidation: boolean;
_changedState: Array<ChangeInfo>;
Expand Down Expand Up @@ -970,13 +971,13 @@ abstract class UI5Element extends HTMLElement {
* @param data - additional data for the event
* @returns false, if the event was cancelled (preventDefault called), true otherwise
*/
fireDecoratorEvent<T>(name: string, data?: T): boolean {
const eventData = this.getEventData(name);
fireDecoratorEvent<N extends keyof this["eventDetails"]>(name: N, data?: this["eventDetails"][N] | undefined): boolean {
const eventData = this.getEventData(name as string);
const cancellable = eventData ? eventData.cancelable : false;
const bubbles = eventData ? eventData.bubbles : false;

const eventResult = this._fireEvent(name, data, cancellable, bubbles);
const pascalCaseEventName = kebabToPascalCase(name);
const eventResult = this._fireEvent(name as string, data, cancellable, bubbles);
const pascalCaseEventName = kebabToPascalCase(name as string);

// pascal events are more convinient for native react usage
// live-change:
Expand Down
2 changes: 1 addition & 1 deletion packages/base/src/UI5ElementMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type Property = {

type PropertyValue = boolean | number | string | object | undefined | null;

type EventData = Record<string, { detail: Record<string, object>, cancelable: boolean, bubbles: boolean }>;
type EventData = Record<string, { detail?: Record<string, object>, cancelable?: boolean, bubbles?: boolean }>;

type I18nBundleAccessorValue = {
bundleName: string,
Expand Down
2 changes: 2 additions & 0 deletions packages/base/src/decorators.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import customElement from "./decorators/customElement.js";
import event from "./decorators/event.js";
import eventStrict from "./decorators/event-strict.js";
import property from "./decorators/property.js";
import slot from "./decorators/slot.js";

export {
customElement,
event,
eventStrict,
property,
slot,
};
30 changes: 30 additions & 0 deletions packages/base/src/decorators/event-strict.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type UI5Element from "../UI5Element.js";

/**
* Returns an event class decorator.
*
* @param { string } name the event name
* @param { EventData } data the event data
* @returns { ClassDecorator }
*/
const event = <T extends typeof UI5Element, N extends keyof InstanceType<T>["eventDetails"]>(name: N, data: { detail?: Record<keyof InstanceType<T>["eventDetails"][N], { type: any}>, bubbles?: boolean, cancelable?: boolean } = {}): (target: T) => T | void => {
return (target: T) => {
if (!Object.prototype.hasOwnProperty.call(target, "metadata")) {
target.metadata = {};
}

const metadata = target.metadata;
if (!metadata.events) {
metadata.events = {};
}

const eventsMetadata = metadata.events;
if (!eventsMetadata[name as string]) {
data.bubbles = !!data.bubbles;
data.cancelable = !!data.cancelable;
eventsMetadata[name as string] = data;
}
};
};

export default event;
1 change: 1 addition & 0 deletions packages/base/src/decorators/event.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* Returns an event class decorator.
*
* @deprecated Use `@ui5/webcomponents-base/dist/decorators/event-strict.js` instead.
* @param { string } name the event name
* @param { EventData } data the event data
* @returns { ClassDecorator }
Expand Down
26 changes: 16 additions & 10 deletions packages/compat/src/Table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import type { ChangeInfo } from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import event from "@ui5/webcomponents-base/dist/decorators/event.js";
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
Expand Down Expand Up @@ -182,7 +182,7 @@ enum TableFocusTargetElement {
* @param {HTMLElement} row the activated row.
* @public
*/
@event<TableRowClickEventDetail>("row-click", {
@event("row-click", {
detail: {
/**
* @public
Expand All @@ -198,7 +198,7 @@ enum TableFocusTargetElement {
* @since 2.0.0
* @public
*/
@event<TablePopinChangeEventDetail>("popin-change", {
@event("popin-change", {
detail: {
/**
* @public
Expand Down Expand Up @@ -229,7 +229,7 @@ enum TableFocusTargetElement {
* @public
* @since 2.0.0
*/
@event<TableSelectionChangeEventDetail>("selection-change", {
@event("selection-change", {
detail: {
/**
* @public
Expand All @@ -243,6 +243,12 @@ enum TableFocusTargetElement {
bubbles: true,
})
class Table extends UI5Element {
eventDetails!: {
"row-click": TableRowClickEventDetail,
"popin-change": TablePopinChangeEventDetail,
"load-more": void,
"selection-change": TableSelectionChangeEventDetail,
}
/**
* Defines the text that will be displayed when there is no data and `hideNoData` is not present.
* @default undefined
Expand Down Expand Up @@ -673,7 +679,7 @@ class Table extends UI5Element {

const selectedRows = this.selectedRows;

this.fireDecoratorEvent<TableSelectionChangeEventDetail>("selection-change", {
this.fireDecoratorEvent("selection-change", {
selectedRows,
previouslySelectedRows,
});
Expand Down Expand Up @@ -705,7 +711,7 @@ class Table extends UI5Element {

const selectedRows: Array<ITableRow> = this.selectedRows;

this.fireDecoratorEvent<TableSelectionChangeEventDetail>("selection-change", {
this.fireDecoratorEvent("selection-change", {
selectedRows,
previouslySelectedRows,
});
Expand Down Expand Up @@ -967,7 +973,7 @@ class Table extends UI5Element {
}
});
row.selected = true;
this.fireDecoratorEvent<TableSelectionChangeEventDetail>("selection-change", {
this.fireDecoratorEvent("selection-change", {
selectedRows: [row],
previouslySelectedRows,
});
Expand All @@ -992,7 +998,7 @@ class Table extends UI5Element {
this._allRowsSelected = false;
}

this.fireDecoratorEvent<TableSelectionChangeEventDetail>("selection-change", {
this.fireDecoratorEvent("selection-change", {
selectedRows,
previouslySelectedRows,
});
Expand Down Expand Up @@ -1021,7 +1027,7 @@ class Table extends UI5Element {

const selectedRows = bAllSelected ? this.rows : [];

this.fireDecoratorEvent<TableSelectionChangeEventDetail>("selection-change", {
this.fireDecoratorEvent("selection-change", {
selectedRows,
previouslySelectedRows,
});
Expand Down Expand Up @@ -1096,7 +1102,7 @@ class Table extends UI5Element {
// invalidate if hidden columns count has changed or columns are shown
if (hiddenColumnsChange || shownColumnsChange) {
this._hiddenColumns = hiddenColumns;
this.fireDecoratorEvent<TablePopinChangeEventDetail>("popin-change", {
this.fireDecoratorEvent("popin-change", {
poppedColumns: this._hiddenColumns,
});
}
Expand Down
5 changes: 4 additions & 1 deletion packages/compat/src/TableGroupRow.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import event from "@ui5/webcomponents-base/dist/decorators/event.js";
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
Expand Down Expand Up @@ -47,6 +47,9 @@ import tableGroupRowStyles from "./generated/themes/TableGroupRow.css.js";
bubbles: true,
})
class TableGroupRow extends UI5Element implements ITableRow {
eventDetails!: {
_focused: FocusEvent,
}
/**
* Defines the mode of the row
* @default "None"
Expand Down
28 changes: 18 additions & 10 deletions packages/compat/src/TableRow.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js";
import property from "@ui5/webcomponents-base/dist/decorators/property.js";
import event from "@ui5/webcomponents-base/dist/decorators/event.js";
import event from "@ui5/webcomponents-base/dist/decorators/event-strict.js";
import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
Expand Down Expand Up @@ -89,7 +89,7 @@ type TableRowF7PressEventDetail = {
/**
* @private
*/
@event<TableRowForwardBeforeEventDetail>("_forward-before", {
@event("_forward-before", {
detail: {
target: {
type: HTMLElement,
Expand All @@ -100,7 +100,7 @@ type TableRowF7PressEventDetail = {
/**
* @private
*/
@event<TableRowForwardAfterEventDetail>("_forward-after", {
@event("_forward-after", {
detail: {
target: {
type: HTMLElement,
Expand All @@ -125,6 +125,14 @@ type TableRowF7PressEventDetail = {
bubbles: true,
})
class TableRow extends UI5Element implements ITableRow {
eventDetails!: {
"row-click": TableRowClickEventDetail,
"_focused": FocusEvent,
"_forward-before": TableRowForwardBeforeEventDetail,
"_forward-after": TableRowForwardAfterEventDetail,
"selection-requested": TableRowSelectionRequestedEventDetail,
"f7-pressed": TableRowF7PressEventDetail,
}
/**
* Defines the visual indication and behavior of the component.
*
Expand Down Expand Up @@ -236,11 +244,11 @@ class TableRow extends UI5Element implements ITableRow {
const lastFocusableElement = elements.pop();

if (isTabNext(e) && activeElement === (lastFocusableElement || this.root)) {
this.fireDecoratorEvent<TableRowForwardAfterEventDetail>("_forward-after", { target: activeElement });
this.fireDecoratorEvent("_forward-after", { target: activeElement });
}

if (isTabPrevious(e) && activeElement === this.root) {
this.fireDecoratorEvent<TableRowForwardBeforeEventDetail>("_forward-before", { target: activeElement });
this.fireDecoratorEvent("_forward-before", { target: activeElement });
}

if (isSpace(e) && target.tagName.toLowerCase() === "tr") {
Expand All @@ -249,11 +257,11 @@ class TableRow extends UI5Element implements ITableRow {

if (isRowFocused && !checkboxPressed) {
if ((isSpace(e) && itemSelectable) || (isEnter(e) && isSingleSelect)) {
this.fireDecoratorEvent<TableRowSelectionRequestedEventDetail>("selection-requested", { row: this });
this.fireDecoratorEvent("selection-requested", { row: this });
}

if (isEnter(e) && itemActive) {
this.fireDecoratorEvent<TableRowClickEventDetail>("row-click", { row: this });
this.fireDecoratorEvent("row-click", { row: this });
if (!isSingleSelect) {
this.activate();
}
Expand All @@ -262,7 +270,7 @@ class TableRow extends UI5Element implements ITableRow {

if (isF7(e)) {
e.preventDefault();
this.fireDecoratorEvent<TableRowF7PressEventDetail>("f7-pressed", { row: this });
this.fireDecoratorEvent("f7-pressed", { row: this });
}
}

Expand Down Expand Up @@ -315,13 +323,13 @@ class TableRow extends UI5Element implements ITableRow {
}

if (this.type === TableRowType.Active && !checkboxPressed) {
this.fireDecoratorEvent<TableRowClickEventDetail>("row-click", { row: this });
this.fireDecoratorEvent("row-click", { row: this });
}
}
}

_handleSelection() {
this.fireDecoratorEvent<TableRowSelectionRequestedEventDetail>("selection-requested", { row: this });
this.fireDecoratorEvent("selection-requested", { row: this });
}

_activeElementHasAttribute(attr: string): boolean {
Expand Down
Loading
Loading