Replies: 6 comments 5 replies
-
Maybe this helps? You can manually provide a route for the route array. You could also use provideLocationMocks in the application config decorator. You can apply this decorator in preview, meta or story level. |
Beta Was this translation helpful? Give feedback.
-
You can set template as
and then it depends if you already have routing initialised or you need to provide forRoot routing specify routing
you may need to change location strategy to HashLocationStrategy in decorators:
|
Beta Was this translation helpful? Give feedback.
-
My comment on the discussion the @smlimasu linked is probably what you want, but I didn't fill it out with a full example. So, I will provide one for what I think you are trying to do. I tried to make everything small and simple, so I could add it to one file and hopefully still be readable. Here is a Stackblitz: https://stackblitz.com/edit/github-bazgks?file=src%2Fapp%2Fexample.stories.ts&preset=node import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { provideRouter, RouterModule, withHashLocation } from '@angular/router';
import { applicationConfig, Meta, StoryObj } from '@storybook/angular';
@Component({
template: `
<div>
<a routerLink="/content-1">Content 1</a>
<a routerLink="/content-2">Content 2</a>
<a routerLink="/content-3">Content 3</a>
</div>
<div>
<router-outlet></router-outlet>
</div>
`,
standalone: true,
imports: [CommonModule, RouterModule],
})
class MyPageComponent {}
@Component({
template: `Content Page 1`,
standalone: true,
})
class ContentOneComponent {}
@Component({
template: `Content Page 2`,
standalone: true,
})
class ContentTwoComponent {}
@Component({
template: `Content Page 3`,
standalone: true,
})
class ContentThreeComponent {}
@Component({
template: `Dashboard`,
standalone: true,
})
class DashboardComponent {}
const meta: Meta<MyPageComponent> = {
title: 'MyPageComponent',
component: MyPageComponent,
decorators: [
applicationConfig({
providers: [
// The router is most likely going to interfere with your Storybook application/iframe
// route, but hash routes should be fine, so I also add `withHashLocation`.
provideRouter(
[
{ path: '', component: DashboardComponent },
{ path: 'content-1', component: ContentOneComponent },
{ path: 'content-2', component: ContentTwoComponent },
{ path: 'content-3', component: ContentThreeComponent },
],
withHashLocation()
),
],
}),
],
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
layout: 'fullscreen',
},
};
export default meta;
type Story = StoryObj<MyPageComponent>;
export const Basic: Story = {}; One issue is that changing stories will not reset the route, since it is using the actual router. I have a sort of complicated solution that I may provide. It is mixed in with some old code that I no longer use, so I need to pull it out and clean it up. It basically adds providers that are used to navigate to a specified route after I have seen solutions that use |
Beta Was this translation helpful? Give feedback.
-
Here is what I had to do. I had to use two helper components and a helper service to test the actual component of interest. Storybook sees the outer helper component and passes arguments to its input properties. The outer component writes the arguments received to a helper service. Router-outlet renders the inner helper component which reads arguments out of the helper service and sets them on props of the real component being tested. The inner helper component renders the real test subject. When the args changed they are passed along this chain. In my dirty implementation of this, I have two arguments being passed down to the test subject. In my work Storybook, many many args have to be passed as the test subject can be rendered many different ways. I should have probably used a single Signal for a bag of all properties as each argument is defined multiple times. I can't think of another way to do this. As a hack at work, the Layout component that contains a
|
Beta Was this translation helpful? Give feedback.
-
In my last solution, I mentioned a provider that I use to set an initial url. While updating it, I added some improvements then decided to just publish it as a package. https://github.com/Marklb/storybook-angular-initial-url The package adds a provider that is just a simple Angular provider and adds a decorator to set the url in the story args. Here is the story file from the above Stackblitz: import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { provideRouter, RouterModule } from '@angular/router';
import { applicationConfig, Meta, StoryObj } from '@storybook/angular';
import { provideLocationMocks } from '@angular/common/testing';
import {
provideStoryInitialUrl,
initialUrlFromArgs,
} from '@marklb/storybook-angular-initial-url';
@Component({
template: `
<div>
<a routerLink="/content-1">Content 1</a>
<a routerLink="/content-2">Content 2</a>
<a routerLink="/content-3">Content 3</a>
<a routerLink="../other">Content 3</a>
</div>
<div>
<router-outlet></router-outlet>
</div>
`,
standalone: true,
imports: [CommonModule, RouterModule],
})
class MyPageComponent {}
@Component({
template: `Content Page 1`,
standalone: true,
})
class ContentOneComponent {}
@Component({
template: `Content Page 2`,
standalone: true,
})
class ContentTwoComponent {}
@Component({
template: `Content Page 3`,
standalone: true,
})
class ContentThreeComponent {}
@Component({
template: `Dashboard`,
standalone: true,
})
class DashboardComponent {}
interface ExtraArgs {
initialUrl?: string
}
type StoryComponent = MyPageComponent & ExtraArgs
const meta: Meta<StoryComponent> = {
title: 'MyPageComponent',
component: MyPageComponent,
tags: ['autodocs'],
decorators: [
applicationConfig({
providers: [
provideLocationMocks(),
provideRouter([
{ path: '', component: DashboardComponent },
{ path: 'content-1', component: ContentOneComponent },
{ path: 'content-2', component: ContentTwoComponent },
{ path: 'content-3', component: ContentThreeComponent },
]),
// provideStoryInitialUrl('content-1'),
],
}),
],
parameters: {
layout: 'fullscreen',
},
};
export default meta;
type Story = StoryObj<StoryComponent>;
export const Basic: Story = {};
export const WithInitialUrl: Story = {
decorators: [
applicationConfig({
providers: [provideStoryInitialUrl('content-2')],
}),
],
};
export const WithInitialUrlArg: Story = {
argTypes: {
initialUrl: {
control: {
type: 'select',
},
options: ['/content-1', '/content-2', '/content-3'],
},
},
args: {
initialUrl: '/content-3',
},
decorators: [initialUrlFromArgs()],
};
export const ArgChangeRerender: Story = {
argTypes: {
...WithInitialUrlArg.argTypes,
},
args: {
...WithInitialUrlArg.args,
},
decorators: [initialUrlFromArgs({ rerender: true })],
}; Also, I switched from using hash routes to just providing |
Beta Was this translation helpful? Give feedback.
-
Hmm. I think for our tests, the Page component that the router would render
inside Layout is being skipped for expediency and we're setting grid inputs
in the test that probably would be done by a Page in the real app using
data obtained from the router and other services.
When I wrote that code, I wasn't sure how to pass args to the grid or if it
was even possible.
You can't just add an argument to the story and expect to use in the
template of a story. It won't compile. There seems to be two ways to solve
this. One is add inputs to the helper component. The other is to extend the
story interface. I've seen one example of it. Something like
Layout & { extraProp1: string }
(Less than, greater than not available on my phone for some reason).
I can't remember the exact syntax.
The next problem I hit was to do with importing components for use.
I kept getting an error saying can't set prop on such a comp; make sure you
include it
My helper was standalone and despite including the missing comp in the
helpers imports, I had to include it in the module metadata.
I found resolving this to be insanely stressful, and due to time
constraints, I just had to clobber the problem until the import error went
away.
I haven't been able to test carefully enough to establish what the rules of
thumb are so this is all smooth in future.
…On Mon, Aug 5, 2024, 7:22 AM Mark Berry ***@***.***> wrote:
In my last solution, I mentioned a provider that I use to set an initial
url. While updating it, I added some improvements then decided to just
publish it as a package.
https://github.com/Marklb/storybook-angular-initial-url
Updated Stackblitz: https://stackblitz.com/edit/github-bazgks-55nmcv
The package adds a provider that is just a simple Angular provider and
adds a decorator to set the url in the story args.
Here is the story file from the above Stackblitz:
import { CommonModule } from ***@***.***/common';import { Component } from ***@***.***/core';import { provideRouter, RouterModule } from ***@***.***/router';import { applicationConfig, Meta, StoryObj } from ***@***.***/angular';import { provideLocationMocks } from ***@***.***/common/testing';
import {
provideStoryInitialUrl,
initialUrlFromArgs,} from ***@***.***/storybook-angular-initial-url';
@component({
template: ` <div> <a routerLink="/content-1">Content 1</a> <a routerLink="/content-2">Content 2</a> <a routerLink="/content-3">Content 3</a> <a routerLink="../other">Content 3</a> </div> <div> <router-outlet></router-outlet> </div> `,
standalone: true,
imports: [CommonModule, RouterModule],})class MyPageComponent {}
@component({
template: `Content Page 1`,
standalone: true,})class ContentOneComponent {}
@component({
template: `Content Page 2`,
standalone: true,})class ContentTwoComponent {}
@component({
template: `Content Page 3`,
standalone: true,})class ContentThreeComponent {}
@component({
template: `Dashboard`,
standalone: true,})class DashboardComponent {}
const meta: Meta<MyPageComponent> = {
title: 'MyPageComponent',
component: MyPageComponent,
tags: ['autodocs'],
decorators: [
applicationConfig({
providers: [
provideLocationMocks(),
provideRouter([
{ path: '', component: DashboardComponent },
{ path: 'content-1', component: ContentOneComponent },
{ path: 'content-2', component: ContentTwoComponent },
{ path: 'content-3', component: ContentThreeComponent },
]),
// provideStoryInitialUrl('content-1'),
],
}),
],
parameters: {
layout: 'fullscreen',
},};
export default meta;type Story = StoryObj<MyPageComponent>;
export const Basic: Story = {};
export const WithInitialUrl: Story = {
decorators: [
applicationConfig({
providers: [provideStoryInitialUrl('content-2')],
}),
],};
export const WithInitialUrlArg: Story = {
argTypes: {
initialUrl: {
control: {
type: 'select',
},
options: ['/content-1', '/content-2', '/content-3'],
},
},
args: {
initialUrl: '/content-3',
},
decorators: [initialUrlFromArgs()],};
export const ArgChangeRerender: Story = {
argTypes: {
...WithInitialUrlArg.argTypes,
},
args: {
...WithInitialUrlArg.args,
},
decorators: [initialUrlFromArgs({ rerender: true })],};
Also, I switched from using hash routes to just providing
provideLocationMocks(), which seems to work fine for hash and non-hash
routes.
—
Reply to this email directly, view it on GitHub
<#28724 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAMYL7ZQS6TRYBRRA22ZJA3ZP4K4DAVCNFSM6AAAAABLRWSVYOVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTAMRTHE2TMMI>
.
You are receiving this because you authored the thread.Message ID:
***@***.***
com>
|
Beta Was this translation helpful? Give feedback.
-
Summary
I'd like to write a story that shows a complete screen. My layout component contains a header, sidebar and footer component, and a placeholder for main content using Angular's router-outlet directive. To render a particular component inside the router-outlet, I have to configure routes and I guess, either the RouterTestingModule or RouterModule. All my components are standalone. I just can't figure out how to get this configured. I'd like a simple example I can use as a base for my story.
Additional information
Storybook 8.1 / Angular 17
Create a reproduction
No response
Beta Was this translation helpful? Give feedback.
All reactions