Skip to content

Commit

Permalink
Angular: Add support for Angular's output signals
Browse files Browse the repository at this point in the history
Support the new signal-based output function in Angular 17.3 and upwards in Storybook's helper types as StoryObj or StoryFn. I also ensured that TypeScript doesn't complain in Angular versions that don't support this feature.

Please be aware, that controls might not reflect the proper types when Signals are used. For component and property analysis, we are relying on Compodoc. Compodoc doesn't support the new output signal yet.
  • Loading branch information
valentinpalkovic committed Mar 18, 2024
1 parent 607e2bf commit 7a0a1c7
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 11 deletions.
4 changes: 3 additions & 1 deletion code/frameworks/angular/scripts/postbuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ const path = require('path');

const filePath = path.join(__dirname, '../dist/client/public-types.d.ts');
const fileContent = fs.readFileSync(filePath, 'utf8');
const newContent = fileContent.replaceAll(/(type AngularInputSignal)/g, '// @ts-ignore\n$1');
const newContent = fileContent
.replaceAll(/(type AngularInputSignal)/g, '// @ts-ignore\n$1')
.replaceAll(/(type AngularOutputEmitterRef)/g, '// @ts-ignore\n$1');
fs.writeFileSync(filePath, newContent, 'utf8');
17 changes: 13 additions & 4 deletions code/frameworks/angular/src/client/public-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,30 @@ export type Preview = ProjectAnnotations<AngularRenderer>;
/**
* Utility type that transforms InputSignal and EventEmitter types
*/
type TransformComponentType<T> = TransformInputSignalType<TransformEventType<T>>
type TransformComponentType<T> = TransformInputSignalType<TransformOutputSignalType<TransformEventType<T>>>

// @ts-ignore Angular < 17.2 doesn't export InputSignal
type AngularInputSignal<T> = AngularCore.InputSignal<T>
// @ts-ignore Angular < 17.2 doesn't export InputSignalWithTransform
type AngularInputSignalWithTransform<T, U> = AngularCore.InputSignalWithTransform<T, U>
// @ts-ignore Angular < 17.3 doesn't export AngularOutputEmitterRef
type AngularOutputEmitterRef<T> = AngularCore.OutputEmitterRef<T>

type AngularHasSignal = typeof AngularCore extends { input: infer U } ? true : false;
type InputSignal<T> = AngularHasSignal extends true ? AngularInputSignal<T> : never;
type InputSignalWithTransform<T, U> = AngularHasSignal extends true ? AngularInputSignalWithTransform<T, U> : never;
type AngularHasInputSignal = typeof AngularCore extends { input: infer U } ? true : false;
type AngularHasOutputSignal = typeof AngularCore extends { output: infer U } ? true : false;

type InputSignal<T> = AngularHasInputSignal extends true ? AngularInputSignal<T> : never;
type InputSignalWithTransform<T, U> = AngularHasInputSignal extends true ? AngularInputSignalWithTransform<T, U> : never;
type OutputEmitterRef<T> = AngularHasOutputSignal extends true ? AngularOutputEmitterRef<T> : never;

type TransformInputSignalType<T> = {
[K in keyof T]: T[K] extends InputSignal<infer E> ? E : T[K] extends InputSignalWithTransform<any, infer U> ? U : T[K];
};

type TransformOutputSignalType<T> = {
[K in keyof T]: T[K] extends OutputEmitterRef<infer E> ? (e: E) => void : T[K];
};

type TransformEventType<T> = {
[K in keyof T]: T[K] extends AngularCore.EventEmitter<infer E> ? (e: E) => void : T[K];
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Input, Output, EventEmitter, input } from '@angular/core';
import { Component, Input, input, output } from '@angular/core';

@Component({
// Needs to be a different name to the CLI template button
Expand Down Expand Up @@ -40,8 +40,7 @@ export default class SignalButtonComponent {
/**
* Optional click handler
*/
@Output()
onClick = new EventEmitter<Event>();
onClick = output<Event>();

public get classes(): string[] {
const mode = this.primary() ? 'storybook-button--primary' : 'storybook-button--secondary';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Input, Output, EventEmitter, input } from '@angular/core';
import { Component, Input, input, output } from '@angular/core';

@Component({
// Needs to be a different name to the CLI template button
Expand Down Expand Up @@ -40,8 +40,7 @@ export default class SignalButtonComponent {
/**
* Optional click handler
*/
@Output()
onClick = new EventEmitter<Event>();
onClick = output<Event>();

public get classes(): string[] {
const mode = this.primary() ? 'storybook-button--primary' : 'storybook-button--secondary';
Expand Down

0 comments on commit 7a0a1c7

Please sign in to comment.