[Ionic][Vue3][Storybook]IonicでStorybookを使う（モック的な）

# ギリギリでいつも生きていたいから
去年アシナガバチに人生2回目を刺された時「あっ、これ死ぬヤツやん」ってなった、どうも　え～すけさん　です。
※残念ながら何事もなく生きてます。きっとスズメバチじゃないから大丈夫だったんだね!!

# やる気が少しでも有るうちに。。。
[コレ](https://qiita.com/AsilHatake/items/5750bf532718846a22d1)の続き的な記事になります。

# 今回やること
とりあえず企業のポータル的なアプリを作ろう！
でもログインとかから作るのめんどいなぁ、とりあえず社内報の一覧的なのを作ってみるかぁ。
で、今後ログインが出来た時に、イチイチログインしないと見れないんじゃ作ってもしょうが無いから、Storybook経由で画面だけは見れるようにしとくか。。。
みたいな、とりあえず無計画に社内ポータルに有りがちな何か作ってみよう的なノリで作ってます。

こんなのが出せるようになるのが今回のゴール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/996378/55e4c2bf-ceca-4a62-8836-a0cbfc462315.png)

# やったこと（実装）
## とりあえず普通に画面を作る
viewsフォルダーに社内報用のフォルダと一覧画面のVueファイルを作る。
※とりあえず表示データはダミーデータをsetupでゴリゴリに作ってます
※デザインとかは一旦そっとしておいてくれ。。。
```vue:src/views/company-newsletter/NewsList.vue
<template>
  <ion-page>
    <ion-header :translucent="true">
      <ion-toolbar>
        <ion-buttons slot="start">
          <ion-menu-button color="primary"></ion-menu-button>
        </ion-buttons>
        <ion-title>社内報</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      <ion-list>
        <template v-for="item in listItems" :key="item.year">
          <ion-list-header @click="item.isOpen = !item.isOpen">
            <ion-label>{{ item.title }}</ion-label>
            <ion-button>{{ item.isOpen ? "閉じる" : "全て表示" }}</ion-button>
          </ion-list-header>
          <template v-if="item.isOpen">
            <ion-item v-for="subItem in item.subItems" :key="subItem.id">
              <ion-label>{{ subItem.title }}</ion-label>
            </ion-item>
          </template>
        </template>
      </ion-list>
    </ion-content>
  </ion-page>
</template>

<script setup lang="ts">
import {
  IonContent,
  IonItem,
  IonLabel,
  IonList,
  IonPage,
  IonListHeader,
} from "@ionic/vue";
import { ref } from "vue";
interface ListItem {
  year: number;
  title: string;
  isOpen: boolean;
  subItems: SubItem[];
}
interface SubItem {
  id: string;
  title: string;
}

const listItems = ref<ListItem[]>([]);

for (let i = 0; i < 5; i++) {
  const subItems: SubItem[] = [];
  for (let j = 1; j <= 12; j++) {
    subItems.push({
      id: j,
      title: j + "月号",
    });
  }
  listItems.value = [
    ...listItems.value,
    {
      year: i,
      title: "〇〇年度",
      isOpen: i == 0,
      subItems,
    },
  ];
}
</script>

<style scoped></style>
```

## stories.tsの作成
対象のコンポーネントにはパラメータとかなにもないので、特にStoryも糞もあったもんじゃないけど、とりあえず画面が見たいということで作ってみた。
```typescript:src\stories\views\company-newsletter\NewsList.stories.ts
import type { Meta, StoryObj } from "@storybook/vue3";
import NewsList from "../../../views/company-newsletter/NewsList.vue";

const meta = {
  title: "views/company-newsletter/NewsList",
  component: NewsList,
  tags: ["autodocs"],
} satisfies Meta<typeof NewsList>;

export default meta;
type Story = StoryObj<typeof meta>;

export const 画面イメージ: Story = {};
```

## コレでも十分見れるものにはなってはいるが、、、
この状態でのStorybookだと`<ion-app>`にラップされていない画面になってしまうため、preview.tsを少しいじる
※追加した分だけを記載
```typescript:.storybook/preview.ts
import type { Decorator, Preview } from "@storybook/vue3";

// 省略

/* Storybook Bug CSS */
import "./storybook.css";

export const decorators: Decorator[] = [
  () => {
    return {
      template:
        '<ion-app><story /></ion-app>',
    };
  },
];

export default preview;
```

あとしれっと書いているけど、`stories.ts`のmetaに`tags: ["autodocs"],`を付けた際に出来るDocs画面ですが、表示エリアがスクロールしない（高さが出ないのとscroll:noneとかで定義されている）ため、暫定対応のCSSを適応してます。

```css:.storybook/storybook.css
#storybook-docs {
  height: 100vh;
  overflow: auto;
}

#storybook-docs div[scale]{
  min-height: 50vh;
}
```

## 完成
とりあえず`npm run storybook`で立ち上げて、普通に画面が見ることを確認出来ればおｋ

# 作ってみて思った。。。
自分がまだStorybookバブだからかもしれませんが、Prop/Emitが無いようなView/Page的なものを表示するのにコレを使うのはあまり向いていないような気がしている。。。

あくまで、コンポーネント（画面に表示するパーツ）限定で考えて作るのが本来のコンセプトとかにもあってるのかなと。

今回で言うならば、`<ion-list>`部分を別コンポーネントとかにすれば、0件/1件/2件以上の表示だったり、見た目とかイベントのStoryを色々と作れたのかなって思うので、ちょっと別コンポーネントにしてみた版も今後作ってみようかなと。
※コンポーネント化するのはいいんだけど、その画面でしか使わないようなコンポーネントとかだと汎用性があまり効かない無駄リソースを増やすだけになりそうなので、世の中のフロント周りのエライ開発者達はどうやってるんだろうね。。。