diff --git a/.changeset/perfect-tigers-eat.md b/.changeset/perfect-tigers-eat.md new file mode 100644 index 0000000000..1d438dbd8a --- /dev/null +++ b/.changeset/perfect-tigers-eat.md @@ -0,0 +1,5 @@ +--- +'@builder.io/mitosis': patch +--- + +React: Fix type error when emitting an event without data (onSomeEvent: () => void; type will now have correct binding in the parent without unnecesarry undefined event parameter) diff --git a/.changeset/six-ghosts-exercise.md b/.changeset/six-ghosts-exercise.md new file mode 100644 index 0000000000..d1d7783f97 --- /dev/null +++ b/.changeset/six-ghosts-exercise.md @@ -0,0 +1,5 @@ +--- +'@builder.io/mitosis': minor +--- + +Remove on prefix on angulare event emits to be aligned with Angular Style Rule 05-16: Don't Prefix Output Properties diff --git a/e2e/e2e-app/src/component-paths.ts b/e2e/e2e-app/src/component-paths.ts index 11a06ff467..6cc2e2862b 100644 --- a/e2e/e2e-app/src/component-paths.ts +++ b/e2e/e2e-app/src/component-paths.ts @@ -6,4 +6,5 @@ export const COMPONENT_PATHS = [ '/special-tags/', '/signals/', '/disabled-input/', + '/event-listener/', ]; diff --git a/e2e/e2e-app/src/components/events/event-child.lite.tsx b/e2e/e2e-app/src/components/events/event-child.lite.tsx new file mode 100644 index 0000000000..7c046375a0 --- /dev/null +++ b/e2e/e2e-app/src/components/events/event-child.lite.tsx @@ -0,0 +1,25 @@ +import { useStore } from '@builder.io/mitosis'; + +export interface EventChildProps { + onConfirm: (name: string) => void; + onCancel: () => void; +} + +export default function EventChild(props: EventChildProps) { + const state = useStore({ + _onCancel() { + props.onCancel(); + }, + + _onConfirm() { + props.onConfirm('Joe'); + }, + }); + + return ( + <> + + + + ); +} diff --git a/e2e/e2e-app/src/components/events/event-parent.lite.tsx b/e2e/e2e-app/src/components/events/event-parent.lite.tsx new file mode 100644 index 0000000000..71e5527314 --- /dev/null +++ b/e2e/e2e-app/src/components/events/event-parent.lite.tsx @@ -0,0 +1,28 @@ +import { useState, useStore } from '@builder.io/mitosis'; +import EventChild from './event-child.lite'; + +export default function EventParent() { + const [eventLog, setEventLog] = useState(''); + + const state = useStore({ + _onCancel() { + const newEventLog = eventLog + 'Cancel event called
'; + setEventLog(newEventLog); + }, + + _onConfirm(name: string) { + const newEventLog = eventLog + `Confirm event called with parameter: ${name}
`; + setEventLog(newEventLog); + }, + }); + + return ( + <> + state._onConfirm(name)} + onCancel={() => state._onCancel()} + > +

+ + ); +} diff --git a/e2e/e2e-app/src/homepage.lite.tsx b/e2e/e2e-app/src/homepage.lite.tsx index 854dc7669b..b4e3cd9a71 100644 --- a/e2e/e2e-app/src/homepage.lite.tsx +++ b/e2e/e2e-app/src/homepage.lite.tsx @@ -2,6 +2,7 @@ import { For, onMount, Show, useStore } from '@builder.io/mitosis'; import { COMPONENT_PATHS } from './component-paths'; import ComponentWithTypes from './components/component-with-types.lite'; import DisabledInput from './components/disabled-input/disabled-input.lite'; +import EventParent from './components/events/event-parent.lite'; import NestedParent from './components/nested/nested-parent.lite'; import OneComponent from './components/one-component.lite'; import ShowForComponent from './components/show-for-component.lite'; @@ -61,6 +62,10 @@ export default function Homepage(props: { pathname?: string }) { + + + + ); } diff --git a/e2e/e2e-app/tests/main.spec.ts b/e2e/e2e-app/tests/main.spec.ts index 4413323240..7a5df79e79 100644 --- a/e2e/e2e-app/tests/main.spec.ts +++ b/e2e/e2e-app/tests/main.spec.ts @@ -46,7 +46,6 @@ test.describe('e2e', () => { await expect(page.locator(`${textLocator}2`)).toBeVisible(); await expect(page.locator(`${textLocator}3`)).toBeVisible(); }); - test.describe('special HTML tags', () => { test('template tag', async ({ page, packageName }) => { await page.goto('/special-tags/'); @@ -101,4 +100,28 @@ test.describe('e2e', () => { await expect(nativeEnabled).toBeEditable(); }); }); + + test.describe('Event Listener', () => { + test('Single Event without parameter', async ({ page, packageName }) => { + await page.goto('/event-listener/'); + + const cancelButton = page.getByRole('button', { name: 'Cancel' }); + + await cancelButton.click(); + + await expect(page.getByTestId('event-log')).toHaveText('Cancel event called'); + }); + + test('Single Event with parameter', async ({ page, packageName }) => { + await page.goto('/event-listener/'); + + const confirmButton = page.getByRole('button', { name: 'Confirm' }); + + await confirmButton.click(); + + await expect(page.getByTestId('event-log')).toHaveText( + 'Confirm event called with parameter: Joe', + ); + }); + }); }); diff --git a/examples/basic/src/blocks/modal.lite.tsx b/examples/basic/src/blocks/modal.lite.tsx new file mode 100644 index 0000000000..fc02812d34 --- /dev/null +++ b/examples/basic/src/blocks/modal.lite.tsx @@ -0,0 +1,27 @@ +import {useStore} from '@builder.io/mitosis'; + +export interface ModalProps { + onConfirm: (name: string) => void; + onCancel: () => void; +} + +export default function Modal(props: ModalProps) { + + const state = useStore({ + _onCancel() { + props.onCancel() + }, + + _onConfirm() { + props.onConfirm('Joe') + }, + }); + + + return ( + <> + + + + ); +} diff --git a/examples/basic/src/index-helpers/blocks-exports.ts b/examples/basic/src/index-helpers/blocks-exports.ts index 617105ec9b..82d4faf4f0 100644 --- a/examples/basic/src/index-helpers/blocks-exports.ts +++ b/examples/basic/src/index-helpers/blocks-exports.ts @@ -1,2 +1,2 @@ export { default as Button } from '../blocks/button.lite'; -export { default as Image } from '../blocks/image.lite'; +export { default as Image } from '../blocks/image.lite'; \ No newline at end of file diff --git a/packages/core/src/__tests__/__snapshots__/angular.import.test.ts.snap b/packages/core/src/__tests__/__snapshots__/angular.import.test.ts.snap index fb6da252b3..46006dd0e6 100644 --- a/packages/core/src/__tests__/__snapshots__/angular.import.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/angular.import.test.ts.snap @@ -294,15 +294,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -338,15 +338,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -3694,7 +3694,7 @@ const defaultProps = { - @@ -3715,7 +3715,7 @@ export default class Button { @Input() text = defaultProps[\\"text\\"]; @Input() buttonText; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -3821,7 +3821,7 @@ const defaultProps = { - @@ -3841,7 +3841,7 @@ export default class Button { @Input() openLinkInNewTab = defaultProps[\\"openLinkInNewTab\\"]; @Input() text = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -7144,15 +7144,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -7188,15 +7188,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -10828,7 +10828,7 @@ const defaultProps = { - @@ -10850,7 +10850,7 @@ export default class Button { @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; @Input() buttonText!: ButtonProps[\\"buttonText\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; @@ -10964,7 +10964,7 @@ const defaultProps = { - @@ -10985,7 +10985,7 @@ export default class Button { defaultProps[\\"openLinkInNewTab\\"]; @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; diff --git a/packages/core/src/__tests__/__snapshots__/angular.mapper.test.ts.snap b/packages/core/src/__tests__/__snapshots__/angular.mapper.test.ts.snap index 4b8db0d072..f2e45ab9cb 100644 --- a/packages/core/src/__tests__/__snapshots__/angular.mapper.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/angular.mapper.test.ts.snap @@ -299,15 +299,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -344,15 +344,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -3748,7 +3748,7 @@ const defaultProps = { - @@ -3769,7 +3769,7 @@ export default class Button { @Input() text = defaultProps[\\"text\\"]; @Input() buttonText; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -3876,7 +3876,7 @@ const defaultProps = { - @@ -3896,7 +3896,7 @@ export default class Button { @Input() openLinkInNewTab = defaultProps[\\"openLinkInNewTab\\"]; @Input() text = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -7262,15 +7262,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -7307,15 +7307,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -10995,7 +10995,7 @@ const defaultProps = { - @@ -11017,7 +11017,7 @@ export default class Button { @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; @Input() buttonText!: ButtonProps[\\"buttonText\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; @@ -11132,7 +11132,7 @@ const defaultProps = { - @@ -11153,7 +11153,7 @@ export default class Button { defaultProps[\\"openLinkInNewTab\\"]; @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; diff --git a/packages/core/src/__tests__/__snapshots__/angular.state.test.ts.snap b/packages/core/src/__tests__/__snapshots__/angular.state.test.ts.snap index 164a92c9ef..d3afefc11e 100644 --- a/packages/core/src/__tests__/__snapshots__/angular.state.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/angular.state.test.ts.snap @@ -306,15 +306,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -350,15 +350,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -3797,7 +3797,7 @@ const defaultProps = { - @@ -3818,7 +3818,7 @@ export default class Button { @Input() text = defaultProps[\\"text\\"]; @Input() buttonText; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -3924,7 +3924,7 @@ const defaultProps = { - @@ -3944,7 +3944,7 @@ export default class Button { @Input() openLinkInNewTab = defaultProps[\\"openLinkInNewTab\\"]; @Input() text = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -7376,15 +7376,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -7420,15 +7420,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -11176,7 +11176,7 @@ const defaultProps = { - @@ -11198,7 +11198,7 @@ export default class Button { @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; @Input() buttonText!: ButtonProps[\\"buttonText\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; @@ -11312,7 +11312,7 @@ const defaultProps = { - @@ -11333,7 +11333,7 @@ export default class Button { defaultProps[\\"openLinkInNewTab\\"]; @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; diff --git a/packages/core/src/__tests__/__snapshots__/angular.styles.test.ts.snap b/packages/core/src/__tests__/__snapshots__/angular.styles.test.ts.snap index 41e4307ae4..c5e908172a 100644 --- a/packages/core/src/__tests__/__snapshots__/angular.styles.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/angular.styles.test.ts.snap @@ -260,15 +260,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -297,15 +297,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -3366,7 +3366,7 @@ const defaultProps = { - @@ -3380,7 +3380,7 @@ export default class Button { @Input() text = defaultProps[\\"text\\"]; @Input() buttonText; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -3486,7 +3486,7 @@ const defaultProps = { - @@ -3499,7 +3499,7 @@ export default class Button { @Input() openLinkInNewTab = defaultProps[\\"openLinkInNewTab\\"]; @Input() text = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -6393,15 +6393,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -6430,15 +6430,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -9783,7 +9783,7 @@ const defaultProps = { - @@ -9798,7 +9798,7 @@ export default class Button { @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; @Input() buttonText!: ButtonProps[\\"buttonText\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; @@ -9912,7 +9912,7 @@ const defaultProps = { - @@ -9926,7 +9926,7 @@ export default class Button { defaultProps[\\"openLinkInNewTab\\"]; @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; diff --git a/packages/core/src/__tests__/__snapshots__/angular.test.ts.snap b/packages/core/src/__tests__/__snapshots__/angular.test.ts.snap index 1f9225a34e..988c57ced1 100644 --- a/packages/core/src/__tests__/__snapshots__/angular.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/angular.test.ts.snap @@ -531,15 +531,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -575,15 +575,15 @@ import { CommonModule } from \\"@angular/common\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -612,15 +612,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -656,15 +656,15 @@ import { CommonModule } from \\"@angular/common\\"; export default class MyBasicOutputsComponent { @Input() message; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -6967,7 +6967,7 @@ const defaultProps = { - @@ -6988,7 +6988,7 @@ export default class Button { @Input() text = defaultProps[\\"text\\"]; @Input() buttonText; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -7094,7 +7094,7 @@ const defaultProps = { - @@ -7117,7 +7117,7 @@ export default class Button { @Input() text = defaultProps[\\"text\\"]; @Input() buttonText; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -7216,7 +7216,7 @@ const defaultProps = { - @@ -7236,7 +7236,7 @@ export default class Button { @Input() openLinkInNewTab = defaultProps[\\"openLinkInNewTab\\"]; @Input() text = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -7340,7 +7340,7 @@ const defaultProps = { - @@ -7362,7 +7362,7 @@ export default class Button { @Input() openLinkInNewTab = defaultProps[\\"openLinkInNewTab\\"]; @Input() text = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0; @ViewChild(\\"elRef1\\") elRef1; @@ -13410,15 +13410,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -13454,15 +13454,15 @@ import { CommonModule } from \\"@angular/common\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -13491,15 +13491,15 @@ import { Output, EventEmitter, Component, Input } from \\"@angular/core\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -13535,15 +13535,15 @@ import { CommonModule } from \\"@angular/common\\"; export default class MyBasicOutputsComponent { @Input() message!: any; - @Output() onMessage = new EventEmitter(); - @Output() onEvent = new EventEmitter(); + @Output() message = new EventEmitter(); + @Output() event = new EventEmitter(); name = \\"PatrickJS\\"; ngOnInit() { if (typeof window !== \\"undefined\\") { - this.onMessage.emit(this.name); - this.onEvent.emit(this.message); + this.message.emit(this.name); + this.event.emit(this.message); } } } @@ -20404,7 +20404,7 @@ const defaultProps = { - @@ -20426,7 +20426,7 @@ export default class Button { @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; @Input() buttonText!: ButtonProps[\\"buttonText\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; @@ -20542,7 +20542,7 @@ const defaultProps = { - @@ -20566,7 +20566,7 @@ export default class Button { @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; @Input() buttonText!: ButtonProps[\\"buttonText\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; @@ -20673,7 +20673,7 @@ const defaultProps = { - @@ -20694,7 +20694,7 @@ export default class Button { defaultProps[\\"openLinkInNewTab\\"]; @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; @@ -20806,7 +20806,7 @@ const defaultProps = { - @@ -20829,7 +20829,7 @@ export default class Button { defaultProps[\\"openLinkInNewTab\\"]; @Input() text!: ButtonProps[\\"text\\"] = defaultProps[\\"text\\"]; - @Output() onClick = new EventEmitter(); + @Output() click = new EventEmitter(); @ViewChild(\\"elRef0\\") elRef0!: ElementRef; @ViewChild(\\"elRef1\\") elRef1!: ElementRef; diff --git a/packages/core/src/generators/angular/index.ts b/packages/core/src/generators/angular/index.ts index 4e19b83b26..4ecf128f43 100644 --- a/packages/core/src/generators/angular/index.ts +++ b/packages/core/src/generators/angular/index.ts @@ -37,7 +37,7 @@ import { BaseHook, MitosisComponent } from '@/types/mitosis-component'; import { Binding, MitosisNode, checkIsForNode } from '@/types/mitosis-node'; import { TranspilerGenerator } from '@/types/transpiler'; import { flow, pipe } from 'fp-ts/lib/function'; -import { isString, kebabCase, uniq } from 'lodash'; +import { camelCase, isString, kebabCase, uniq } from 'lodash'; import traverse from 'neotraverse/legacy'; import { format } from 'prettier/standalone'; import isChildren from '../../helpers/is-children'; @@ -239,6 +239,22 @@ const stringifyBinding = } }; +export const stripOutputEmits = (code?: string, outputEventEmitters?: string[]): string => { + let newCode = code || ''; + + if (!outputEventEmitters?.length) { + return newCode; + } + + outputEventEmitters?.forEach((_var) => { + const regexp = '(^|\\s|;|\\()(props\\.?)' + _var + '\\('; + const replacer = '$1this.' + removeOnFromAngularOutputEvent(_var) + '.emit('; + newCode = newCode.replace(new RegExp(regexp, 'g'), replacer); + }); + + return newCode; +}; + const handleNgOutletBindings = (node: MitosisNode, options: ToAngularOptions) => { let allProps = ''; for (const key in node.properties) { @@ -558,9 +574,9 @@ const processAngularCode = DO_NOT_USE_VARS_TRANSFORMS(code, { contextVars, domRefs, - outputVars, stateVars, }), + (newCode) => stripOutputEmits(newCode, outputVars), (newCode) => stripStateAndPropsRefs(newCode, { replaceWith }), ); @@ -722,6 +738,10 @@ const classPropertiesPlugin = () => ({ }, }); +export function removeOnFromAngularOutputEvent(outputName: string): string { + return outputName.startsWith('on') ? camelCase(outputName.substring(2)) : camelCase(outputName); +} + // if any state "property" is trying to access state.* or props.* // then we need to move them to onInit where they can be accessed const transformState = (json: MitosisComponent) => { @@ -865,11 +885,12 @@ export const componentToAngular: TranspilerGenerator = props.delete(variableName); }); - const outputs = outputVars.map((variableName) => { + // Building the @Output() EventEmitters + const outputEvents = outputVars.map((outputName) => { if (options?.experimental?.outputs) { - return options?.experimental?.outputs(json, variableName); + return options?.experimental?.outputs(json, outputName); } - return `@Output() ${variableName} = new EventEmitter()`; + return `@Output() ${removeOnFromAngularOutputEvent(outputName)} = new EventEmitter()`; }); const domRefs = getRefs(json); @@ -992,7 +1013,7 @@ export const componentToAngular: TranspilerGenerator = Boolean(injectables.length) || dynamicComponents.size || refsForObjSpread.size; const angularCoreImports = [ - ...(outputs.length ? ['Output', 'EventEmitter'] : []), + ...(outputEvents.length ? ['Output', 'EventEmitter'] : []), ...(options?.experimental?.inject ? ['Inject', 'forwardRef'] : []), 'Component', ...(domRefs.size || dynamicComponents.size || refsForObjSpread.size @@ -1041,7 +1062,7 @@ export const componentToAngular: TranspilerGenerator = }) .join('\n')} - ${outputs.join('\n')} + ${outputEvents.join('\n')} ${Array.from(domRefs) .map( diff --git a/packages/core/src/generators/react/blocks.ts b/packages/core/src/generators/react/blocks.ts index e31205a0ac..9dd8471f85 100644 --- a/packages/core/src/generators/react/blocks.ts +++ b/packages/core/src/generators/react/blocks.ts @@ -290,7 +290,7 @@ export const blockToReact = ( if (json.bindings[key]?.type === 'spread') { str += ` {...(${value})} `; } else if (key.startsWith('on')) { - const { arguments: cusArgs = ['event'] } = json.bindings[key]!; + const { arguments: cusArgs = [] } = json.bindings[key]!; const eventName = options.type === 'native' ? NATIVE_EVENT_MAPPER[key] || key : key; str += ` ${eventName}={(${cusArgs.join(',')}) => ${updateStateSettersInCode( useBindingValue, diff --git a/packages/core/src/helpers/strip-state-and-props-refs.ts b/packages/core/src/helpers/strip-state-and-props-refs.ts index b93a804d86..f831ddce0b 100644 --- a/packages/core/src/helpers/strip-state-and-props-refs.ts +++ b/packages/core/src/helpers/strip-state-and-props-refs.ts @@ -49,7 +49,7 @@ export type DO_NOT_USE_ARGS = { * Do not use these anywhere. We are migrating to AST transforms and should avoid Regex String.replace() as they are * very brittle. * - * If you need to re-use a part of this, re-create it as an AST tranform first. + * If you need to re-use a part of this, re-create it as an AST transform first. */ export const DO_NOT_USE_VARS_TRANSFORMS = ( newCode: string, diff --git a/packages/core/src/parsers/angular.ts b/packages/core/src/parsers/angular.ts index 26229fa8e3..43d981319a 100644 --- a/packages/core/src/parsers/angular.ts +++ b/packages/core/src/parsers/angular.ts @@ -95,7 +95,7 @@ const angularTemplateNodeToMitosisNode = ( }); } for (const output of node.outputs) { - bindings['on' + capitalize(output.name)] = createSingleBinding({ + bindings[capitalize(output.name)] = createSingleBinding({ code: transformBinding( (output.handler as ASTWithSource) .source! // TODO: proper reference replace