Skip to content

Commit

Permalink
Merge pull request #30611 from storybookjs/valentin/support-angular-19.2
Browse files Browse the repository at this point in the history
Angular: Support v19.2 when @angular/animations is not installed
  • Loading branch information
valentinpalkovic authored Feb 21, 2025
2 parents 0aa2e8c + 46eb952 commit f10850b
Show file tree
Hide file tree
Showing 13 changed files with 448 additions and 480 deletions.
3 changes: 2 additions & 1 deletion code/builders/builder-webpack5/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ import prettyTime from 'pretty-hrtime';
import sirv from 'sirv';
import { corePath } from 'storybook/core-path';
import type { Configuration, Stats, StatsOptions } from 'webpack';
import webpackDep, { DefinePlugin, ProgressPlugin } from 'webpack';
import webpackDep, { DefinePlugin, IgnorePlugin, ProgressPlugin } from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';

export * from './types';
export * from './preview/virtual-module-mapping';

export const WebpackDefinePlugin = DefinePlugin;
export const WebpackIgnorePlugin = IgnorePlugin;

export const printDuration = (startTime: [number, number]) =>
prettyTime(process.hrtime(startTime))
Expand Down
4 changes: 4 additions & 0 deletions code/frameworks/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"@angular-devkit/architect": ">=0.1500.0 < 0.2000.0",
"@angular-devkit/build-angular": ">=15.0.0 < 20.0.0",
"@angular-devkit/core": ">=15.0.0 < 20.0.0",
"@angular/animations": ">=15.0.0 < 20.0.0",
"@angular/cli": ">=15.0.0 < 20.0.0",
"@angular/common": ">=15.0.0 < 20.0.0",
"@angular/compiler": ">=15.0.0 < 20.0.0",
Expand All @@ -115,6 +116,9 @@
"zone.js": ">= 0.11.1 < 1.0.0"
},
"peerDependenciesMeta": {
"@angular/animations": {
"optional": true
},
"@angular/cli": {
"optional": true
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export abstract class AbstractRenderer {
this.initAngularRootElement(targetDOMNode, targetSelector);

const analyzedMetadata = new PropertyExtractor(storyFnAngular.moduleMetadata, component);
await analyzedMetadata.init();

const storyUid = this.generateStoryUIdFromRawStoryUid(
targetDOMNode.getAttribute(STORY_UID_ATTRIBUTE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ describe('StorybookModule', () => {
};

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { props },
Expand Down Expand Up @@ -98,6 +99,7 @@ describe('StorybookModule', () => {
};

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { props },
Expand Down Expand Up @@ -127,6 +129,7 @@ describe('StorybookModule', () => {
const storyProps$ = new BehaviorSubject<ICollection>(initialProps);

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { props: initialProps },
Expand Down Expand Up @@ -183,6 +186,7 @@ describe('StorybookModule', () => {
const storyProps$ = new BehaviorSubject<ICollection>(initialProps);

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { props: initialProps },
Expand Down Expand Up @@ -224,6 +228,7 @@ describe('StorybookModule', () => {
const storyProps$ = new BehaviorSubject<ICollection>(initialProps);

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: {
Expand Down Expand Up @@ -262,6 +267,7 @@ describe('StorybookModule', () => {
const storyProps$ = new BehaviorSubject<ICollection>(initialProps);

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { props: initialProps },
Expand Down Expand Up @@ -302,6 +308,8 @@ describe('StorybookModule', () => {
WithoutSelectorComponent
);

await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: {
props,
Expand Down Expand Up @@ -330,6 +338,7 @@ describe('StorybookModule', () => {
class FooComponent {}

const analyzedMetadata = new PropertyExtractor({}, FooComponent);
await analyzedMetadata.init();

const application = getApplication({
storyFnAngular: { template: '' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,89 +29,95 @@ const TestModuleWithImportsAndProviders = NgModule({
providers: [TestTokenProvider],
})(class {});

const analyzeMetadata = (metadata: NgModuleMetadata, component?: any) => {
return new PropertyExtractor(metadata, component);
const analyzeMetadata = async (metadata: NgModuleMetadata, component?: any) => {
const propertyExtractor = new PropertyExtractor(metadata, component);
await propertyExtractor.init();
return propertyExtractor;
};
const extractImports = (metadata: NgModuleMetadata, component?: any) => {
const { imports } = new PropertyExtractor(metadata, component);
return imports;
const extractImports = async (metadata: NgModuleMetadata, component?: any) => {
const propertyExtractor = new PropertyExtractor(metadata, component);
await propertyExtractor.init();
return propertyExtractor.imports;
};
const extractDeclarations = (metadata: NgModuleMetadata, component?: any) => {
const { declarations } = new PropertyExtractor(metadata, component);
return declarations;
const extractDeclarations = async (metadata: NgModuleMetadata, component?: any) => {
const propertyExtractor = new PropertyExtractor(metadata, component);
await propertyExtractor.init();
return propertyExtractor.declarations;
};
const extractProviders = (metadata: NgModuleMetadata, component?: any) => {
const { providers } = new PropertyExtractor(metadata, component);
return providers;
const extractProviders = async (metadata: NgModuleMetadata, component?: any) => {
const propertyExtractor = new PropertyExtractor(metadata, component);
await propertyExtractor.init();
return propertyExtractor.providers;
};
const extractApplicationProviders = (metadata: NgModuleMetadata, component?: any) => {
const { applicationProviders } = new PropertyExtractor(metadata, component);
return applicationProviders;
const extractApplicationProviders = async (metadata: NgModuleMetadata, component?: any) => {
const propertyExtractor = new PropertyExtractor(metadata, component);
await propertyExtractor.init();
return propertyExtractor.applicationProviders;
};

describe('PropertyExtractor', () => {
vi.spyOn(console, 'warn').mockImplementation(() => {});

describe('analyzeMetadata', () => {
it('should remove BrowserModule', () => {
it('should remove BrowserModule', async () => {
const metadata = {
imports: [BrowserModule],
};
const { imports, providers, applicationProviders } = analyzeMetadata(metadata);
const { imports, providers, applicationProviders } = await analyzeMetadata(metadata);
expect(imports.flat(Number.MAX_VALUE)).toEqual([CommonModule]);
expect(providers.flat(Number.MAX_VALUE)).toEqual([]);
expect(applicationProviders.flat(Number.MAX_VALUE)).toEqual([]);
});

it('should remove BrowserAnimationsModule and use its providers instead', () => {
it('should remove BrowserAnimationsModule and use its providers instead', async () => {
const metadata = {
imports: [BrowserAnimationsModule],
};
const { imports, providers, applicationProviders } = analyzeMetadata(metadata);
const { imports, providers, applicationProviders } = await analyzeMetadata(metadata);
expect(imports.flat(Number.MAX_VALUE)).toEqual([CommonModule]);
expect(providers.flat(Number.MAX_VALUE)).toEqual([]);
expect(applicationProviders.flat(Number.MAX_VALUE)).toEqual(provideAnimations());
});

it('should remove NoopAnimationsModule and use its providers instead', () => {
it('should remove NoopAnimationsModule and use its providers instead', async () => {
const metadata = {
imports: [NoopAnimationsModule],
};
const { imports, providers, applicationProviders } = analyzeMetadata(metadata);
const { imports, providers, applicationProviders } = await analyzeMetadata(metadata);
expect(imports.flat(Number.MAX_VALUE)).toEqual([CommonModule]);
expect(providers.flat(Number.MAX_VALUE)).toEqual([]);
expect(applicationProviders.flat(Number.MAX_VALUE)).toEqual(provideNoopAnimations());
});

it('should remove Browser/Animations modules recursively', () => {
it('should remove Browser/Animations modules recursively', async () => {
const metadata = {
imports: [BrowserAnimationsModule, BrowserModule],
};
const { imports, providers, applicationProviders } = analyzeMetadata(metadata);
const { imports, providers, applicationProviders } = await analyzeMetadata(metadata);
expect(imports.flat(Number.MAX_VALUE)).toEqual([CommonModule]);
expect(providers.flat(Number.MAX_VALUE)).toEqual([]);
expect(applicationProviders.flat(Number.MAX_VALUE)).toEqual(provideAnimations());
});

it('should not destructure Angular official module', () => {
it('should not destructure Angular official module', async () => {
const metadata = {
imports: [WithOfficialModule],
};
const { imports, providers, applicationProviders } = analyzeMetadata(metadata);
const { imports, providers, applicationProviders } = await analyzeMetadata(metadata);
expect(imports.flat(Number.MAX_VALUE)).toEqual([CommonModule, WithOfficialModule]);
expect(providers.flat(Number.MAX_VALUE)).toEqual([]);
expect(applicationProviders.flat(Number.MAX_VALUE)).toEqual([]);
});
});

describe('extractImports', () => {
it('should return Angular official modules', () => {
const imports = extractImports({ imports: [TestModuleWithImportsAndProviders] });
it('should return Angular official modules', async () => {
const imports = await extractImports({ imports: [TestModuleWithImportsAndProviders] });
expect(imports).toEqual([CommonModule, TestModuleWithImportsAndProviders]);
});

it('should return standalone components', () => {
const imports = extractImports(
it('should return standalone components', async () => {
const imports = await extractImports(
{
imports: [TestModuleWithImportsAndProviders],
},
Expand All @@ -124,8 +130,8 @@ describe('PropertyExtractor', () => {
]);
});

it('should return standalone directives', () => {
const imports = extractImports(
it('should return standalone directives', async () => {
const imports = await extractImports(
{
imports: [TestModuleWithImportsAndProviders],
},
Expand All @@ -140,8 +146,11 @@ describe('PropertyExtractor', () => {
});

describe('extractDeclarations', () => {
it('should return an array of declarations that contains `storyComponent`', () => {
const declarations = extractDeclarations({ declarations: [TestComponent1] }, TestComponent2);
it('should return an array of declarations that contains `storyComponent`', async () => {
const declarations = await extractDeclarations(
{ declarations: [TestComponent1] },
TestComponent2
);
expect(declarations).toEqual([TestComponent1, TestComponent2]);
});
});
Expand Down Expand Up @@ -174,15 +183,15 @@ describe('PropertyExtractor', () => {
});

describe('extractProviders', () => {
it('should return an array of providers', () => {
const providers = extractProviders({
it('should return an array of providers', async () => {
const providers = await extractProviders({
providers: [TestService],
});
expect(providers).toEqual([TestService]);
});

it('should return an array of singletons extracted', () => {
const singeltons = extractApplicationProviders({
it('should return an array of singletons extracted', async () => {
const singeltons = await extractApplicationProviders({
imports: [BrowserAnimationsModule],
});

Expand Down
Loading

0 comments on commit f10850b

Please sign in to comment.