Storybook: How to override Angular component method?

Created on 27 Sep 2019  路  4Comments  路  Source: storybookjs/storybook

Let's say I have an Angular component with a getDataFromBackend method.

In my storybook I want getDataFromBackend to always return a mock.

Is there a way to override this method?

angular question / support

All 4 comments

Mh I don't think overriding is possible. What you are trying to achieve is kind of the same as trying to mock things for unit tests. Without seeing your code I'd try something like this

@Injectable()
export class DataBackendService {
  getDataFromBackend() {
    // do the network request here
  }
}

@Component({
  ...
})
export class SmartComponent {
  constructor(private dataBackendService: DataBackendService) {}

  getData() {
    return this.dataBackendService.getDataFromBackend();
  }
}

In your story you can now do this

class StubService {
  getDataFromBackend() {
    return { /* static test data here */ }
  }
}

storiesOf('YourStory', module).addDecorator(
    moduleMetadata({
      provides: [
        { provide: DataBackendService, useClass: StubService }
      ]
      declarations: [WithStoreComponent],
    })

What happens here? Well Angular magic! You can replace a service with another implementation
See https://angular.io/guide/dependency-injection-providers

Here's a stackblitz example https://stackblitz.com/edit/angular-6xhma8ng-dep-providers?file=src/app/app.component.ts

Unfortunately the service is not injected in in this case. I have no control of the component.

Right now I am using a proxy approach similar to this: https://github.com/storybookjs/storybook/issues/208#issuecomment-472359026

But even with the proxy approach, it would be better to be able to do proxy per story instead of per the whole project.

In Jasmine, we can use spyOn to override specific methods when testing. It would be nice to have similar mechanism in storybook.

Storybook just wraps an Angular host and puts a story inside this wrapper. You are of course free to instantiate your component using the angular ComponentFactory or wrap it inside a demo component. Jasmine tests also instantiate the component in order to give you an instance that you can spyOn

Maybe this helps?

@Component({
  template: `<your-actual-component></your-actual-component>`
})
class DemoWrapperComponent {

  @ViewChild(YourActualComponent, { static: false }) componentRef: YourActualComponent

  ngOnInit() {
    componentRef['methodYouWantToOverride'] = () => { return "do crazy stuff" };
  }
}

Haven't tested it, not sure if it works!

@kroeder That works! Thanks so much for your help!!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wahengchang picture wahengchang  路  3Comments

dnlsandiego picture dnlsandiego  路  3Comments

sakulstra picture sakulstra  路  3Comments

levithomason picture levithomason  路  3Comments

shilman picture shilman  路  3Comments