Angular/Ionic + StorybookでNavControllerを使い画面遷移する

4 min読了の目安(約3600字TECH技術記事

画面遷移時のインタラクションを確認するために、Storybook上でNavControllerを使いました。やり方が分からずハマったのでメモ

まず画面遷移ができる状態に

この記事の「サンプル削除」が終わったところから。

まず、

ionic g page hello

で遷移先の画面を作り、homeとhelloで行ったり来たりできるようにします。

  • app-routing.module.ts
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {
    path: 'home',
    loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)
  },
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full'
  },
  {
    path: 'hello',
    loadChildren: () => import('./hello/hello.module').then( m => m.HelloPageModule)
  },
];
  • home.module.ts

Storybookで使えるようにexportしています。

import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { IonicModule } from "@ionic/angular";
import { FormsModule } from "@angular/forms";
import { HomePage } from "./home.page";

import { HomePageRoutingModule } from "./home-routing.module";

@NgModule({
  imports: [CommonModule, FormsModule, IonicModule, HomePageRoutingModule],
  declarations: [HomePage],
  exports: [HomePage],
})
export class HomePageModule {}
  • home.page.html
<ion-header>
  <ion-toolbar>
    <ion-title>home</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-button (click)="toHello()">home</ion-button>
</ion-content>
  • home.page.ts
import { Component, OnInit } from "@angular/core";
import { NavController } from "@ionic/angular";

@Component({
  selector: "app-hello",
  templateUrl: "./hello.page.html",
  styleUrls: ["./hello.page.scss"],
})
export class HelloPage implements OnInit {
  constructor(private nav: NavController) {}

  ngOnInit() {}

  public toHome(): void {
    this.nav.navigateForward("home");
  }
}

helloページの方は、htmlの文言とnavigateForwardnavigateBackが違うだけなので割愛。

ionic serve

で画面を立ち上げると、ボタンを押して行ったり来たりできるようになっています。

これをStorybookで

useFactoryでRouterをDIしてみたり色々試したのですが、結論だけ書くと以下のコードで動きます。

import { APP_BASE_HREF, CommonModule } from "@angular/common";
import {
  DefaultUrlSerializer,
  RouterModule,
  Routes,
  UrlSerializer,
} from "@angular/router";
import { IonicModule } from "@ionic/angular";
import { moduleMetadata } from "@storybook/angular";
import { HomePageModule } from "./home.module";
import { HomePage } from "./home.page";

const routes: Routes = [
  {
    path: "home",
    loadChildren: () => import("./home.module").then((m) => m.HomePageModule),
  },
  {
    path: "",
    redirectTo: "home",
    pathMatch: "full",
  },
  {
    path: "hello",
    loadChildren: () =>
      import("../hello/hello.module").then((m) => m.HelloPageModule),
  },
];

//#region モジュール定義
const imports = [
  CommonModule,
  IonicModule.forRoot(),
  RouterModule.forRoot(routes, {
    useHash: true,
  }),
  HomePageModule,
];

const providers = [{ provide: APP_BASE_HREF, useValue: "/" }];

export const data = {
  imports,
  providers,
};

export default {
  title: "pages/HomePage",
  excludeStories: /.*[(data)]$/,
  decorators: [moduleMetadata(data)],
  component: HomePage,
};
//#endregion

const Template = (args: HomePage) => ({
  component: HomePage,
  props: args,
  template: `
    <ion-app>
      <ion-router-outlet></ion-router-outlet>
    </ion-app>
  `,
});

export const 画面遷移のテスト = Template.bind({});

ポイントは

  • Storybookのiframe内で遷移するのでuseHash:trueが必要
  • index.htmlにある<base>の代わりになる{ provide: APP_BASE_HREF, useValue: "/" },が必要
  • templateにはapp-homeではなくion-router-outletを使用

といったところでしょうか。インタラクションの確認が必要なときには参考にしてみてください。