Skip to content

Commit

Permalink
Improves typings in Luigi container (#4030)
Browse files Browse the repository at this point in the history
  • Loading branch information
walmazacn authored Nov 25, 2024
1 parent 89e0fea commit ab4bfce
Show file tree
Hide file tree
Showing 18 changed files with 749 additions and 615 deletions.
2 changes: 1 addition & 1 deletion container/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@types/jest": "^29.5.12",
"@typescript-eslint/eslint-plugin": "^8.13.0",
"@typescript-eslint/parser": "^8.13.0",
"@typescript-eslint/typescript-estree": "^8.13.0",
"chokidar-cli": "^3.0.0",
"cli-color": "^2.0.4",
"concurrently": "^7.6.0",
Expand All @@ -65,7 +66,6 @@
"svelte-preprocess": "5.0.4",
"tslib": "2.6.1",
"typescript": "5.1.6",
"@typescript-eslint/typescript-estree": "^8.13.0",
"typescript-eslint": "^8.13.0"
},
"engines": {
Expand Down
19 changes: 12 additions & 7 deletions container/src/LuigiCompoundContainer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,13 @@

<script lang="ts">
import { onMount } from 'svelte';
import { Events } from './constants/communication';
import type { ContainerElement } from './constants/container.model';
import { ContainerService } from './services/container.service';
import { WebComponentService } from './services/webcomponents.service';
import { Events } from './constants/communication';
import { GenericHelperFunctions } from './utilities/helpers';
/* eslint-disable */
export let activeFeatureToggleList: string[];
export let anchor: string;
export let clientPermissions: any;
Expand All @@ -86,10 +88,11 @@
export let userSettings: any;
export let viewurl: string;
export let webcomponent: any;
/* eslint-enable */
let containerInitialized = false;
let mainComponent: HTMLElement;
let eventBusElement: HTMLElement;
let mainComponent: ContainerElement;
let eventBusElement: ContainerElement;
const containerService = new ContainerService();
const webcomponentService = new WebComponentService();
Expand All @@ -114,11 +117,12 @@
);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const initialize = (thisComponent: any) => {
if (!compoundConfig || containerInitialized) {
return;
}
thisComponent.updateContext = (contextObj: any, internal?: any) => {
thisComponent.updateContext = (contextObj: object, internal?: object) => {
const rootElement = thisComponent.getNoShadow() ? thisComponent : mainComponent;
rootElement._luigi_mfe_webcomponent.context = contextObj;
Expand Down Expand Up @@ -149,14 +153,14 @@
}
webcomponentService
.renderWebComponentCompound(node, thisComponent.getNoShadow() ? thisComponent : mainComponent, ctx)
.then((compCnt) => {
eventBusElement = compCnt as HTMLElement;
.then((compCnt: ContainerElement) => {
eventBusElement = compCnt;
if (skipInitCheck || !node.viewUrl) {
thisComponent.initialized = true;
setTimeout(() => {
webcomponentService.dispatchLuigiEvent(Events.INITIALIZED, {});
});
} else if ((eventBusElement as any).LuigiClient && !(eventBusElement as any).deferLuigiClientWCInit) {
} else if (eventBusElement.LuigiClient && !eventBusElement.deferLuigiClientWCInit) {
thisComponent.initialized = true;
webcomponentService.dispatchLuigiEvent(Events.INITIALIZED, {});
}
Expand All @@ -166,6 +170,7 @@
};
onMount(async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const thisComponent: any =
mainComponent.getRootNode() === document
? mainComponent.parentNode
Expand Down
24 changes: 14 additions & 10 deletions container/src/LuigiContainer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@

<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { containerService } from './services/container.service';
import { WebComponentService } from './services/webcomponents.service';
import { ContainerAPI } from './api/container-api';
import { Events } from './constants/communication';
import { GenericHelperFunctions } from './utilities/helpers';
import type { IframeHandle, ContainerElement } from './constants/container.model';
import { containerService } from './services/container.service';
import { getAllowRules } from './services/iframe-helpers';
import { WebComponentService } from './services/webcomponents.service';
import { GenericHelperFunctions } from './utilities/helpers';
/* eslint-disable */
export let activeFeatureToggleList: string[];
export let allowRules: string[];
export let anchor: string;
Expand All @@ -88,9 +90,10 @@
export let userSettings: any;
export let viewurl: string;
export let webcomponent: any;
/* eslint-enable */
const iframeHandle: { iframe: HTMLIFrameElement } | any = {};
let mainComponent: HTMLElement;
const iframeHandle: IframeHandle = {};
let mainComponent: ContainerElement;
let containerInitialized = false;
const webcomponentService = new WebComponentService();
Expand Down Expand Up @@ -119,9 +122,10 @@
);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const initialize = (thisComponent: any) => {
if (!containerInitialized) {
thisComponent.sendCustomMessage = (id: string, data?: any) => {
thisComponent.sendCustomMessage = (id: string, data?: object) => {
ContainerAPI.sendCustomMessage(
id,
thisComponent.getNoShadow() ? thisComponent : mainComponent,
Expand All @@ -131,15 +135,15 @@
);
};
thisComponent.updateContext = (contextObj: any, internal?: any) => {
thisComponent.updateContext = (contextObj: object, internal?: object) => {
if (webcomponent) {
(thisComponent.getNoShadow() ? thisComponent : mainComponent)._luigi_mfe_webcomponent.context = contextObj;
} else {
ContainerAPI.updateContext(contextObj, internal, iframeHandle);
}
};
thisComponent.closeAlert = (id: any, dismissKey: any) => {
thisComponent.closeAlert = (id: string, dismissKey: string) => {
ContainerAPI.closeAlert(id, dismissKey, iframeHandle);
};
Expand Down Expand Up @@ -179,8 +183,7 @@
} else if (webcomponent) {
(thisComponent.getNoShadow() ? thisComponent : mainComponent).addEventListener('wc_ready', () => {
if (
!(thisComponent.getNoShadow() ? thisComponent : (mainComponent as any))._luigi_mfe_webcomponent
?.deferLuigiClientWCInit
!(thisComponent.getNoShadow() ? thisComponent : mainComponent)._luigi_mfe_webcomponent?.deferLuigiClientWCInit
) {
thisComponent.initialized = true;
webcomponentService.dispatchLuigiEvent(Events.INITIALIZED, {});
Expand All @@ -193,6 +196,7 @@
};
onMount(async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const thisComponent: any = mainComponent.parentNode;
thisComponent.iframeHandle = iframeHandle;
thisComponent.init = () => {
Expand Down
23 changes: 15 additions & 8 deletions container/src/api/container-api.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { IframeHandle, ContainerElement } from '../constants/container.model';
import { LuigiInternalMessageID } from '../constants/internal-communication';
import { containerService } from '../services/container.service';

Expand All @@ -8,7 +9,7 @@ export class ContainerAPIFunctions {
* @param internal internal luigi legacy data
* @param iframeHandle a reference to the iframe that is needed to send a message to it internally
*/
updateContext = (contextObj: any, internal?: any, iframeHandle?: any) => {
updateContext = (contextObj: object, internal?: object, iframeHandle?: IframeHandle) => {
if (iframeHandle) {
const internalParameter = internal || {};
containerService.sendCustomMessageToIframe(
Expand All @@ -31,7 +32,7 @@ export class ContainerAPIFunctions {
* @param iframeHandle a reference to the iframe that is needed to send a message to it internally
* @param authData the authData object being sent to the microfrontend
*/
updateAuthData = (iframeHandle: any, authData: any) => {
updateAuthData = (iframeHandle: IframeHandle, authData: object) => {
if (iframeHandle && authData) {
containerService.sendCustomMessageToIframe(iframeHandle, { authData }, LuigiInternalMessageID.AUTH_SET_TOKEN);
} else {
Expand All @@ -47,15 +48,21 @@ export class ContainerAPIFunctions {
* @param iframeHandle a reference to the iframe to be affected
* @param data data to be sent alongside the custom message
*/
sendCustomMessage = (id: string, mainComponent: any, isWebcomponent: boolean, iframeHandle: any, data?: any) => {
if (isWebcomponent && (mainComponent as any)._luigi_mfe_webcomponent) {
containerService.dispatch(id, (mainComponent as any)._luigi_mfe_webcomponent, data);
sendCustomMessage = (
id: string,
mainComponent: ContainerElement,
isWebcomponent: boolean,
iframeHandle: IframeHandle,
data?: object
) => {
if (isWebcomponent && mainComponent._luigi_mfe_webcomponent) {
containerService.dispatch(id, mainComponent._luigi_mfe_webcomponent, data);
} else {
const msg = { ...data };
if (msg.id) {
if (msg['id']) {
console.warn('Property "id" is reserved and can not be used in custom message data');
}
msg.id = id;
msg['id'] = id;
containerService.sendCustomMessageToIframe(iframeHandle, msg);
}
};
Expand All @@ -66,7 +73,7 @@ export class ContainerAPIFunctions {
* @param dismissKey the dismiss key being sent if any
* @param iframeHandle the handle of the iframe to send the message to
*/
closeAlert(id: any, dismissKey: any, iframeHandle: any) {
closeAlert(id: string, dismissKey: string, iframeHandle: IframeHandle) {
containerService.sendCustomMessageToIframe(iframeHandle, { id, dismissKey }, LuigiInternalMessageID.ALERT_CLOSED);
}
}
Expand Down
58 changes: 58 additions & 0 deletions container/src/constants/container.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
export interface IframeHandle {
data?: string;
iframe: HTMLIFrameElement;
}

export interface ContainerElement extends HTMLElement {
iframeHandle?: IframeHandle;
[key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}

export interface LayoutConfig {
column?: string;
row?: string;
slot?: string;
}

export interface RendererLayout {
columns?: string;
gap?: number;
maxWidth?: number;
minHeight?: number;
minWidth?: number;
rows?: string;
}

export interface RendererConfig extends RendererLayout {
layouts?: RendererLayout[];
}

export interface RendererUseProps {
attachCompoundItem?: (compoundCnt, compoundItemCnt, renderer) => void;
createCompoundContainer?: (config, renderer) => HTMLDivElement;
createCompoundItemContainer?: (layoutConfig, config?, renderer?) => HTMLDivElement;
extends?: string;
}

export interface RendererObject {
config?: RendererConfig;
use?: RendererUseProps | string;
}

export interface WebComponentNode {
compound?: {
renderer?: RendererObject;
[key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
};
webcomponent?: {
selfRegistered?: boolean;
tagName?: string;
[key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
};
viewUrl?: string;
}

export interface WebComponentRenderer {
createCompoundContainer?: () => HTMLDivElement;
viewUrl?: string;
}
10 changes: 7 additions & 3 deletions container/src/constants/event-type.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
// TODO: Add and extend event to inclide custom typings/interface to make it easier to use on the listener parameter
export interface ParamsEvent extends Event {}
/**
* ParamsEvent interface is used to make the handling of listener parameter easier
*/
export interface ParamsEvent extends Event {
[key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}

/**
* PathExistsEvent interface is used to make it easier to use the linkManager().pathExists() promise based function
* on the core application side.
* It enforces the use of the callback function, since the latter is hardcoded to be 'callback'.
* This allows to send back the boolean value if the path exists or not.
* Example Usage:
* addEventListener('my-event-id' event: PathExistsEvent => {
* addEventListener('my-event-id', event: PathExistsEvent => {
* event.callback(true);
* }
* };
Expand Down
5 changes: 3 additions & 2 deletions container/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ComponentType } from 'svelte';
import LuigiContainer from './LuigiContainer.svelte';
import LuigiCompoundContainer from './LuigiCompoundContainer.svelte';
import { Events } from './constants/communication';
Expand All @@ -8,9 +9,9 @@ export type { PathExistsEvent } from './constants/event-type';
export default Events;

if (!customElements.get('luigi-container')) {
customElements.define('luigi-container', (LuigiContainer as any).element);
customElements.define('luigi-container', (LuigiContainer as ComponentType).element);
}

if (!customElements.get('luigi-compound-container')) {
customElements.define('luigi-compound-container', (LuigiCompoundContainer as any).element);
customElements.define('luigi-compound-container', (LuigiCompoundContainer as ComponentType).element);
}
21 changes: 14 additions & 7 deletions container/src/services/container.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Events } from '../constants/communication';
import type { IframeHandle, ContainerElement } from '../constants/container.model';
import { LuigiInternalMessageID } from '../constants/internal-communication';
import { GenericHelperFunctions } from '../utilities/helpers';

Expand All @@ -20,7 +21,7 @@ export class ContainerService {
* @param msg the message to be sent
* @param msgName the optional message name
*/
sendCustomMessageToIframe(iframeHandle: any, msg: any, msgName?: string) {
sendCustomMessageToIframe(iframeHandle: IframeHandle, msg: object, msgName?: string) {
const messageName = msgName || 'custom';

if (iframeHandle.iframe.contentWindow) {
Expand All @@ -38,16 +39,22 @@ export class ContainerService {
/**
* Dispatch an event to the given target container
* @param {string} msg the event message
* @param {HTMLElement} targetCnt the targeted HTML element onto which the event is dispatched
* @param {any} data custom data added to the event to be dispatched
* @param {ContainerElement} targetCnt the targeted HTML element onto which the event is dispatched
* @param {Object} data custom data added to the event to be dispatched
* @param {Function} callback
* @param {string} callbackName
*/
dispatch(msg: string, targetCnt: HTMLElement, data: any, callback?: (any) => void, callbackName?: string): void {
dispatch(
msg: string,
targetCnt: ContainerElement,
data: object,
callback?: (arg?) => void,
callbackName?: string
): void {
const customEvent = new CustomEvent(msg, { detail: data });

if (callback && GenericHelperFunctions.isFunction(callback) && callbackName) {
(customEvent as any)[callbackName] = (data) => {
customEvent[callbackName] = (data) => {
callback(data);
};
}
Expand All @@ -59,9 +66,9 @@ export class ContainerService {
* Retrieves the target container based on the event source.
*
* @param event The event object representing the source of the container.
* @returns {Object| undefined} The target container object or undefined if not found.
* @returns {ContainerElement | undefined} The target container object or undefined if not found.
*/
getTargetContainer(event) {
getTargetContainer(event): ContainerElement | undefined {
let cnt;

globalThis.__luigi_container_manager.container.forEach((element) => {
Expand Down
2 changes: 1 addition & 1 deletion container/src/services/iframe-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const getAllowRules = (allowRules: string[]) => {
const rules = allowRules;
rules.forEach((rule, index) => {
rules[index] = rule + (rule.indexOf(';') != -1 ? '' : ';');
rules[index] = (allowRules[index] as any).replaceAll('"', "'");
rules[index] = allowRules[index].replaceAll('"', "'");
});
return rules.join(' ');
};
Loading

0 comments on commit ab4bfce

Please sign in to comment.