Vue.js@3.x + Storybook@6.x 環境に bootstrap-vue-next を導入する

<details>
<summary>変更履歴</summary>

* 2023/02/02 Bootstrap-vue-3 廃止に伴い、Bootstrap-vue-next に記載を変更
* 2022/07/27 `src/plugins/bootstrap-vue/index.js`の不具合を修正

</details>

# 環境
* Vue.js - v3.2.13
* Storybook - v6.4.22
* ~~bootstrap-vue-3 - 0.1.13~~
* bootstrap-vue-next - v0.7.1

# ~~なぜ`bootstrap-vue`ではなく`bootstrap-vue-3`なのか？~~

~~`bootstrap-vue`は残念ながら、しばらく upgrade の見込みがないので、代替えパッケージ `bootstrap-vue-3`を使う。恨むべきは国際情勢。~~

https://qiita.com/ryo2020/items/1b3c759502a21be3a596

但し、`bootstrap-vue`の更新が復調する可能性も考慮した導入方法を検討したい。

※2023/02/02 現在 Bootstrap-vue-3は廃止され、Bootstrap-vue-nextに移行された模様

# 0. 前提

* Vue.js - v3.x のプロジェクトが作成ずみ
* Vue.js プロジェクトにStorybook が導入済み

https://qiita.com/ryo2020/items/3118aca9a649ce208d3d

# 1. `bootstrap-vue-next`を導入する

## 1.1 `bootstrap-vue-next`のインストール

[公式ページ](https://bootstrap-vue.github.io/bootstrap-vue-next/getting-started/#installation-vue-js)を参考。

```bash:gitbash
$ npm i bootstrap bootstrap-vue-next @popperjs/core

$ npm i unplugin-vue-components -D
```
## 1.2 `Vue.js`に導入

`bootstrap-vue`と同様に`import { BootstrapVueNext } from "bootstrap-vue-next";`としてしまえば導入できますが、それでは`bootstrap-vue`が復調した場合が考慮できて無いので、若干手を加えます。

## 1.2.1 bootstrap-vue-next の import 用にラッパーモジュールを作成する

`src`ディレクトリ以下に`plugins`ディレクトリを作成し、更にその配下に`bootstrap-vue`ディレクトリを作成。

```bash:gitbash
# ディレクトリを移動
$ cd src

# 配下にディレクトリを作成
$ mkdir -p plugins/bootstrap-vue
```

次に、`plugins/bootstrap-vue`配下に`index.js`と`css.js`を作成。

```bash:gitbash
# ディレクトリを移動
$ cd plugins/bootstrap-vue

# 配下にフィアルを作成
$ touch index.js

# 配下にフィアルを作成
$ touch css.js
```

次に、それぞれのファイルをスクリプトエディタ等で以下の用に編集。

```javascript:src/plugins/bootstrap-vue/index.js
// [index.js]
// Wrapper module for treating bootstrap-vue-next as bootstrap-vue.
// Export all bootstrap-vue-next content.
// @see https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/export#syntax
// export * from "bootstrap-vue-next";
export { BootstrapVueNext as BootstrapVue } from "bootstrap-vue-next";
```

```javascript:src/plugins/bootstrap-vue/css.js
// [css.js]
// Wrapper module for importing only bootstrap + bootstrap-vue-3 CSS module
// @see https://bootstrap-vue.org/docs#vue-cli-3

// Optional, since every component import their Bootstrap functionality
// the following line is not necessary
// import 'bootstrap'

import "bootstrap/dist/css/bootstrap.css";
import "bootstrap-vue-next/dist/bootstrap-vue-next.css";
```

最後にVue.jsのエントリポイントである`main.js`を以下の様に変更

```diff_javascript:src/main.js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";

+ import { BootstrapVue } from "@/plugins/bootstrap-vue";
+ import "@/plugins/bootstrap-vue/css";
+ 
- createApp(App).use(store).use(router).mount("#app");
+ createApp(App)
+   .use(store)
+   .use(router)
+   .use(BootstrapVue)
+   .mount("#app");
```

## 1.2.2 Vueテンプレートで`import`する

`import { BButton } from "@/plugins/bootstrap-vue";`で`import`可能。

以下は例。

```vue:WrapBButton.vue
<template>
  <b-button :variant="variant">{{ label }}</b-button>
</template>

<script>
import { BButton } from "@/plugins/bootstrap-vue";

export default {
  name: "wrap-b-button",
  props: {
    label: {
      type: String,
      required: true,
    },
    variant: {
      type: String,
    },
  },
  components: { BButton },
  setup() {},
};
</script>

```

# 1.3 `Storybook`に導入

## 1.3.1 `bootstrap-vue`ラッパーモジュールを import する

`.storybook\preview.js`を以下の様に変更する。

```diff_javascript:.storybook\preview.js
+ import { app } from "@storybook/vue3";
+ import { BootstrapVue } from "@/plugins/bootstrap-vue";
+ import "@/plugins/bootstrap-vue/css";
+ 
+ app.use(BootstrapVue);
+ 
export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
};
```

## 1.3.2 Stories で利用する

変更は特になし。Stories 内で Vue.js テンプレートを import すれば良い。

以下は例。

```javascript:WrapBButton.stories.js
import WrapBButton from "@/components/atoms/WrapBButton";

export default {
  title: "Atoms/WrapBButton",
  component: WrapBButton,
};

const Template = (args) => ({
  components: { WrapBButton },
  setup() {
    return { args };
  },
  template: '<wrap-b-button v-bind="args" />',
});

export const Primary = Template.bind({});
Primary.args = {
  label: "Primary",
  variant: "primary",
};

export const Secondary = Template.bind({});
Secondary.args = {
  label: "Secondary",
  variant: "secondary",
};

export const Success = Template.bind({});
Success.args = {
  label: "Success",
  variant: "success",
};
```

## 1.3.3 Storybook を起動して確認

特に変更なし。`npm run storybook`すれば良い。

```bash:gitbash
$ npm run storybook
```

こんな感じで表示されれば OK. Good!

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682590/d0d42d4f-7ecf-9b31-9866-b72c56e16239.png)

# Extra. `bootstrap-vue-3` パッケージを`bootstrap-vue`パッケージと差し替える

もし今後、`bootstrap-vue`が更新されたり、他に互換性を保ったパッケージが出現した場合でも、ラッパーモジュールの２つを変更するだけで対応可能になる

:::note warn
警告
**パッケージに互換性がある事** が前提であり、全てのパッケージに対応はできないので注意が必要。
:::

```diff_javascript:src/plugins/bootstrap-vue/index.js
// [index.js]
// Wrapper module for treating bootstrap-vue-3 as bootstrap-vue.
// Export all bootstrap-vue-3 content.
// @see https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/export#syntax
- export * from "bootstrap-vue-next";
+ export * from "bootstrap-vue";  // この行を変更
```

```diff_javascript:src/plugins/bootstrap-vue/css.js
// [css.js]
// Wrapper module for importing only bootstrap + bootstrap-vue-3 CSS module
// @see https://bootstrap-vue.org/docs#vue-cli-3

// Optional, since every component import their Bootstrap functionality
// the following line is not necessary
// import 'bootstrap'

import "bootstrap/dist/css/bootstrap.css";
- import "bootstrap-vue-next/dist/bootstrap-vue-next.css";
+ import "bootstrap-vue/dist/bootstrap-vue.css"; // この行を変更
```

了