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

fix(angular): support nested namespaces in event types #575

Merged
merged 3 commits into from
Dec 18, 2024
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
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ jobs:
- name: Checkout Code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- name: Use Node
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/dev-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ jobs:
- name: Checkout Code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: '20.10.0'
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ jobs:
- name: Checkout Code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- name: Use Node
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/prod-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: '20.10.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,22 @@ export class MyComponent {
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['myCustomEvent']);
proxyOutputs(this, this.el, ['myCustomEvent', 'myCustomNestedEvent']);
}
}


import type { IMyComponent as IMyComponentIMyComponent } from 'component-library';

export declare interface MyComponent extends Components.MyComponent {
/**
* Testing an event without value
*/
myCustomEvent: EventEmitter<CustomEvent<number>>;
myCustomEvent: EventEmitter<CustomEvent<IMyComponentIMyComponent.someVar>>;
/**
* Testing with nested namespaces
*/
myCustomNestedEvent: EventEmitter<CustomEvent<IMyComponentIMyComponent.SomeMoreComplexType.SubType>>;
}


Expand Down
12 changes: 9 additions & 3 deletions example-project/component-library-react/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import type { EventName, StencilReactComponent } from '@stencil/react-output-target/runtime';
import { createComponent } from '@stencil/react-output-target/runtime';
import { type CheckboxChangeEventDetail, type InputChangeEventDetail, type MyCheckboxCustomEvent, type MyInputCustomEvent, type MyPopoverCustomEvent, type MyRadioGroupCustomEvent, type MyRangeCustomEvent, type OverlayEventDetail, type RadioGroupChangeEventDetail, type RangeChangeEventDetail } from "component-library";
import { type CheckboxChangeEventDetail, type IMyComponent, type InputChangeEventDetail, type MyCheckboxCustomEvent, type MyComponentCustomEvent, type MyInputCustomEvent, type MyPopoverCustomEvent, type MyRadioGroupCustomEvent, type MyRangeCustomEvent, type OverlayEventDetail, type RadioGroupChangeEventDetail, type RangeChangeEventDetail } from "component-library";
import { MyButton as MyButtonElement, defineCustomElement as defineMyButton } from "component-library/components/my-button.js";
import { MyCheckbox as MyCheckboxElement, defineCustomElement as defineMyCheckbox } from "component-library/components/my-checkbox.js";
import { MyComponent as MyComponentElement, defineCustomElement as defineMyComponent } from "component-library/components/my-component.js";
Expand Down Expand Up @@ -56,14 +56,20 @@ export const MyCheckbox: StencilReactComponent<MyCheckboxElement, MyCheckboxEven
defineCustomElement: defineMyCheckbox
});

type MyComponentEvents = { onMyCustomEvent: EventName<CustomEvent<number>> };
type MyComponentEvents = {
onMyCustomEvent: EventName<MyComponentCustomEvent<IMyComponent.someVar>>,
onMyCustomNestedEvent: EventName<MyComponentCustomEvent<IMyComponent.SomeMoreComplexType.SubType>>
};

export const MyComponent: StencilReactComponent<MyComponentElement, MyComponentEvents> = /*@__PURE__*/ createComponent<MyComponentElement, MyComponentEvents>({
tagName: 'my-component',
elementClass: MyComponentElement,
// @ts-ignore - React type of Stencil Output Target may differ from the React version used in the Nuxt.js project, this can be ignored.
react: React,
events: { onMyCustomEvent: 'myCustomEvent' } as MyComponentEvents,
events: {
onMyCustomEvent: 'myCustomEvent',
onMyCustomNestedEvent: 'myCustomNestedEvent'
} as MyComponentEvents,
defineCustomElement: defineMyComponent
});

Expand Down
9 changes: 6 additions & 3 deletions example-project/component-library-vue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,11 @@ export const MyComponent = /*@__PURE__*/ globalThis.window ? defineContainer<JSX
'age',
'kidsNames',
'favoriteKidName',
'myCustomEvent'
'myCustomEvent',
'myCustomNestedEvent'
], [
'myCustomEvent'
'myCustomEvent',
'myCustomNestedEvent'
]) : defineStencilSSRComponent({
tagName: 'my-component',
hydrateModule: import('component-library/hydrate'),
Expand All @@ -111,7 +113,8 @@ export const MyComponent = /*@__PURE__*/ globalThis.window ? defineContainer<JSX
'last': [String, "last"],
'age': [Number, "age"],
'favoriteKidName': [String, "favorite-kid-name"],
'onMyCustomEvent': [Function]
'onMyCustomEvent': [Function],
'onMyCustomNestedEvent': [Function]
}
});

Expand Down
3 changes: 1 addition & 2 deletions example-project/component-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
"@stencil/core": "^4.22.3",
"@stencil/react-output-target": "workspace:*",
"@stencil/vue-output-target": "workspace:*",
"@types/puppeteer": "2.0.1",
"jest-cli": "26.0.1"
"@types/puppeteer": "2.0.1"
}
}
11 changes: 9 additions & 2 deletions example-project/component-library/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { AutocompleteTypes, Color, ComponentProps, ComponentRef, OverlayEventDetail, TextFieldTypes } from "./interfaces";
import { CheckboxChangeEventDetail, Color as Color1, StyleEventDetail } from "./components/element-interface";
import { IMyComponent } from "./components/helpers";
import { InputChangeEventDetail } from "./components/my-input/my-input";
import { RadioGroupChangeEventDetail } from "./components/my-radio-group/my-radio-group";
import { RangeChangeEventDetail, RangeValue } from "./components/my-range/my-range";
export { AutocompleteTypes, Color, ComponentProps, ComponentRef, OverlayEventDetail, TextFieldTypes } from "./interfaces";
export { CheckboxChangeEventDetail, Color as Color1, StyleEventDetail } from "./components/element-interface";
export { IMyComponent } from "./components/helpers";
export { InputChangeEventDetail } from "./components/my-input/my-input";
export { RadioGroupChangeEventDetail } from "./components/my-radio-group/my-radio-group";
export { RangeChangeEventDetail, RangeValue } from "./components/my-range/my-range";
Expand Down Expand Up @@ -471,7 +473,8 @@ declare global {
new (): HTMLMyCheckboxElement;
};
interface HTMLMyComponentElementEventMap {
"myCustomEvent": number;
"myCustomEvent": IMyComponent.someVar;
"myCustomNestedEvent": IMyComponent.SomeMoreComplexType.SubType;
}
interface HTMLMyComponentElement extends Components.MyComponent, HTMLStencilElement {
addEventListener<K extends keyof HTMLMyComponentElementEventMap>(type: K, listener: (this: HTMLMyComponentElement, ev: MyComponentCustomEvent<HTMLMyComponentElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
Expand Down Expand Up @@ -736,7 +739,11 @@ declare namespace LocalJSX {
/**
* Testing an event without value
*/
"onMyCustomEvent"?: (event: MyComponentCustomEvent<number>) => void;
"onMyCustomEvent"?: (event: MyComponentCustomEvent<IMyComponent.someVar>) => void;
/**
* Testing with nested namespaces
*/
"onMyCustomNestedEvent"?: (event: MyComponentCustomEvent<IMyComponent.SomeMoreComplexType.SubType>) => void;
}
interface MyInput {
/**
Expand Down
8 changes: 8 additions & 0 deletions example-project/component-library/src/components/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,11 @@ export const onceEvent = (element: HTMLElement, eventName: string, callback: (ev
};
element.addEventListener(eventName, handler);
};

export declare namespace IMyComponent {
/** Variables */
export type someVar = number;
export namespace SomeMoreComplexType {
export type SubType = string;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, Prop, h, Event, EventEmitter } from '@stencil/core';
import type { IMyComponent } from '../helpers';

@Component({
tag: 'my-component',
Expand Down Expand Up @@ -39,7 +40,12 @@ export class MyComponent {
/**
* Testing an event without value
*/
@Event() myCustomEvent: EventEmitter<number>;
@Event() myCustomEvent: EventEmitter<IMyComponent.someVar>;

/**
* Testing with nested namespaces
*/
@Event() myCustomNestedEvent: EventEmitter<IMyComponent.SomeMoreComplexType.SubType>;

emitCustomEvent() {
this.myCustomEvent.emit(5);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@

## Events

| Event | Description | Type |
| --------------- | ------------------------------ | --------------------- |
| `myCustomEvent` | Testing an event without value | `CustomEvent<number>` |
| Event | Description | Type |
| --------------------- | ------------------------------ | --------------------- |
| `myCustomEvent` | Testing an event without value | `CustomEvent<number>` |
| `myCustomNestedEvent` | Testing with nested namespaces | `CustomEvent<string>` |


----------------------------------------------
Expand Down
12 changes: 9 additions & 3 deletions example-project/next-app/src/app/components.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import type { EventName, StencilReactComponent } from '@stencil/react-output-target/runtime';
import { createComponent, createSSRComponent } from '@stencil/react-output-target/runtime';
import { type CheckboxChangeEventDetail, type InputChangeEventDetail, type MyCheckboxCustomEvent, type MyInputCustomEvent, type MyPopoverCustomEvent, type MyRadioGroupCustomEvent, type MyRangeCustomEvent, type OverlayEventDetail, type RadioGroupChangeEventDetail, type RangeChangeEventDetail } from "component-library";
import { type CheckboxChangeEventDetail, type IMyComponent, type InputChangeEventDetail, type MyCheckboxCustomEvent, type MyComponentCustomEvent, type MyInputCustomEvent, type MyPopoverCustomEvent, type MyRadioGroupCustomEvent, type MyRangeCustomEvent, type OverlayEventDetail, type RadioGroupChangeEventDetail, type RangeChangeEventDetail } from "component-library";
import { MyButton as MyButtonElement, defineCustomElement as defineMyButton } from "component-library/components/my-button.js";
import { MyCheckbox as MyCheckboxElement, defineCustomElement as defineMyCheckbox } from "component-library/components/my-checkbox.js";
import { MyComponent as MyComponentElement, defineCustomElement as defineMyComponent } from "component-library/components/my-component.js";
Expand Down Expand Up @@ -90,15 +90,21 @@ export const MyCheckbox: StencilReactComponent<MyCheckboxElement, MyCheckboxEven
hydrateModule: import('component-library/hydrate')
});

type MyComponentEvents = { onMyCustomEvent: EventName<CustomEvent<number>> };
type MyComponentEvents = {
onMyCustomEvent: EventName<MyComponentCustomEvent<IMyComponent.someVar>>,
onMyCustomNestedEvent: EventName<MyComponentCustomEvent<IMyComponent.SomeMoreComplexType.SubType>>
};

export const MyComponent: StencilReactComponent<MyComponentElement, MyComponentEvents> = typeof window !== 'undefined'
? /*@__PURE__*/ createComponent<MyComponentElement, MyComponentEvents>({
tagName: 'my-component',
elementClass: MyComponentElement,
// @ts-ignore - React type of Stencil Output Target may differ from the React version used in the Nuxt.js project, this can be ignored.
react: React,
events: { onMyCustomEvent: 'myCustomEvent' } as MyComponentEvents,
events: {
onMyCustomEvent: 'myCustomEvent',
onMyCustomNestedEvent: 'myCustomNestedEvent'
} as MyComponentEvents,
defineCustomElement: defineMyComponent
})
: /*@__PURE__*/ createSSRComponent<MyComponentElement, MyComponentEvents>({
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"prettier": "^3.3.3",
"rimraf": "^6.0.1",
"semver": "^5.5.0",
"ts-jest": "^26.2.0"
"vitest": "^2.1.8"
},
"prettier": "@ionic/prettier-config",
"engines": {
Expand All @@ -59,5 +59,6 @@
"patchedDependencies": {
"[email protected]": "patches/[email protected]"
}
}
},
"packageManager": "[email protected]+sha512.beb9e2a803db336c10c9af682b58ad7181ca0fbd0d4119f2b33d5f2582e96d6c0d93c85b23869295b765170fbdaa92890c0da6ada457415039769edf3c959efe"
}
2 changes: 1 addition & 1 deletion packages/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
},
"scripts": {
"prepublishOnly": "pnpm run build",
"prebuild": "rimraf ./dist && pnpm run test",
"prebuild": "rimraf ./dist",
"build": "tsc && pnpm run rollup",
"dev": "run-p dev:*",
"dev:build": "tsc --watch --preserveWatchOutput",
Expand Down
4 changes: 4 additions & 0 deletions packages/angular/src/generate-angular-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ const formatOutputType = (componentClassName: string, event: ComponentCompilerEv
}
return [p1, dst, p2].join('');
})
// Capture all instances that contain sub types, e.g. `IMyComponent.SomeMoreComplexType.SubType`.
.replace(new RegExp(`^${src}(\.\\w+)+$`, 'g'), (type: string) => {
return `I${componentClassName}${src}.${type.split('.').slice(1).join('.')}`;
})
);
},
event.complexType.original
Expand Down
14 changes: 0 additions & 14 deletions packages/angular/test/jest.preprocessor.js

This file was deleted.

58 changes: 58 additions & 0 deletions packages/angular/tests/generate-angular-component.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,46 @@ describe('createComponentTypeDefinition()', () => {
tags: [],
},
},
{
name: 'myCustomEvent',
method: 'myCustomEvent',
bubbles: true,
cancelable: true,
composed: true,
docs: { tags: [], text: 'Testing an event type with a dot signature' },
complexType: {
original: 'IMyComponent.someVar',
resolved: 'number',
references: {
IMyComponent: {
location: 'import',
path: '../helpers',
id: 'src/components/helpers.ts::IMyComponent'
}
}
},
internal: false
},
{
name: 'myCustomNestedEvent',
method: 'myCustomNestedEvent',
bubbles: true,
cancelable: true,
composed: true,
docs: { tags: [], text: 'Testing with nested namespaces' },
complexType: {
original: 'IMyComponent.SomeMoreComplexType.SubType',
resolved: 'string',
references: {
IMyComponent: {
location: 'import',
path: '../helpers',
id: 'src/components/helpers.ts::IMyComponent'
}
}
},
internal: false
}
];

describe('www build', () => {
Expand All @@ -350,6 +390,7 @@ import type { MyOtherEvent as IMyComponentMyOtherEvent } from '@ionic/core';
import type { MyDoclessEvent as IMyComponentMyDoclessEvent } from '@ionic/core';
import type { MyKebabEvent as IMyComponentMyKebabEvent } from '@ionic/core';
import type { MySlashEvent as IMyComponentMySlashEvent } from '@ionic/core';
import type { IMyComponent as IMyComponentIMyComponent } from '@ionic/core';

export declare interface MyComponent extends Components.MyComponent {
/**
Expand All @@ -366,6 +407,14 @@ export declare interface MyComponent extends Components.MyComponent {
'my-kebab-event': EventEmitter<CustomEvent<IMyComponentMyKebabEvent>>;

'my/slash/event': EventEmitter<CustomEvent<IMyComponentMySlashEvent>>;
/**
* Testing an event type with a dot signature
*/
myCustomEvent: EventEmitter<CustomEvent<IMyComponentIMyComponent.someVar>>;
/**
* Testing with nested namespaces
*/
myCustomNestedEvent: EventEmitter<CustomEvent<IMyComponentIMyComponent.SomeMoreComplexType.SubType>>;
}`
);
});
Expand Down Expand Up @@ -465,6 +514,7 @@ import type { MyOtherEvent as IMyComponentMyOtherEvent } from '@ionic/core/custo
import type { MyDoclessEvent as IMyComponentMyDoclessEvent } from '@ionic/core/custom-elements';
import type { MyKebabEvent as IMyComponentMyKebabEvent } from '@ionic/core/custom-elements';
import type { MySlashEvent as IMyComponentMySlashEvent } from '@ionic/core/custom-elements';
import type { IMyComponent as IMyComponentIMyComponent } from '@ionic/core/custom-elements';

export declare interface MyComponent extends Components.MyComponent {
/**
Expand All @@ -481,6 +531,14 @@ export declare interface MyComponent extends Components.MyComponent {
'my-kebab-event': EventEmitter<CustomEvent<IMyComponentMyKebabEvent>>;

'my/slash/event': EventEmitter<CustomEvent<IMyComponentMySlashEvent>>;
/**
* Testing an event type with a dot signature
*/
myCustomEvent: EventEmitter<CustomEvent<IMyComponentIMyComponent.someVar>>;
/**
* Testing with nested namespaces
*/
myCustomNestedEvent: EventEmitter<CustomEvent<IMyComponentIMyComponent.SomeMoreComplexType.SubType>>;
}`
);
});
Expand Down
2 changes: 1 addition & 1 deletion packages/angular/tests/output-angular.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('generateProxies', () => {

const finalText = generateProxies(components, pkgData, outputTarget, rootDir);
expect(finalText).not.toContain(`import { Components } from 'component-library';`);
expect(finalText.includes(`import { Components } from '../../angular/dist/types/components';`)).toBeTruthy();
expect(finalText).toContain(`import { Components } from '../../angular/dist/types/components';`);
});

it('should include output related imports when there is component with not internal event', () => {
Expand Down
Loading
Loading