リファクタ目的のVue + Storybook で Injectが便利

前回 Vue + Storybook + reg-suit で　VisualRegressionTestのフレームワークを構築しました。
https://qiita.com/kozayupapa/items/0099cea005b3ce0b6274

そのstoryを拡充していきたいと思います。

## 何がしたい？
前提としては、今回StorybookのStoryを追加したいのは、　リファクタリングが必要なくらいvueのcomponent,ページが巨大になってしまっているケースです。すべての変数がpage のdataとして定義されているような例です。

prop になっていれば、storyからのテストデータ設定も楽なのですが、
dataのなかみをtest story側から好きな値に書き換える方法がないかを検討していました。

## Vue Provide/Injectとは？
地道にvueのドキュメントを読み進んでいたら、良さそうなものがありました。
離れた階層からでも提供でき、　どこから挿入されたかは指揮せずに Inject されたあとのコードを受け側に追加すれば良い。ほしかったのはこれです！

https://v3.ja.vuejs.org/guide/component-provide-inject.html


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41631/6224ea2e-6b42-d688-fb5f-6d9a3349a3b9.png)

```javascript:provide/inject例
// 送り側
app.component('todo-list', {
  data() {
    return {
      todos: ['Feed a cat', 'Buy tickets']
    }
  },
  provide() {
    return {
      todoLength: this.todos.length
    }
  },
  template: `
    ...
  `
})

//受け側
app.component('todo-list-statistics', {
  inject: ['todoLength'],
  created() {
    console.log(`Injected property: ${this.todoLength}`) // > Injected property: 5
  }
})
```

ただし、　テスト時だけデータを受け渡したいので、通常時はいままで通りの初期値が設定されていてほしいです。
上記のページだけでは初期値の与え方がわからなかったので、さらに読みすすめると以下のAPIドキュメントに詳しくのっていました。

https://v3.ja.vuejs.org/api/options-composition.html#provide-inject

```javascript:初期値の与え方例
const Child = {
  inject: {
    foo: { default: 'foo' }
  }
}
```
すてき、なんて便利なの。
さらにInjectされた値をdata の初期値として渡す例も丁寧に書いてくれています。
```javascript
const Child = {
  inject: ['foo'],
  data() {
    return {
      bar: this.foo
    }
  }
}
```
しかし！！　
通常のvueでよく見かける単一ファイルコンポーネントのなかの書き方で
上記の書き方をするとundefinedになってしまい失敗します。

```javascript: NG 例
  export default {
    components: {
      hoge
    },
    data: () => ({
      foo:  this.fooInject,
    }),
    inject: { 
      fooInject :{default : false},
    },
    computed: {
     ...
 ```
こちらも　最初できないのかと思っていましたが　Documentをよく読んでみると
下記に解説がありました。　なるほどなるほど　componentのインスタンスを指してなかったんですね。

https://v3.ja.vuejs.org/api/options-data.html#data-2

```text
data プロパティでアロー関数を使う場合、this はコンポーネントのインスタンスになりませんが、
関数の第 1 引数としてそのインスタンスにアクセスできるということは忘れないでください。

data: vm => ({ a: vm.myProp })
```

上記の例を下記のように変更することで無事data プロパティの初期値に設定することも動作しました

```javascript: OK例
  export default {
    components: {
      hoge
    },
    data: (vm) => ({
      foo:  vm.fooInject,
    }),
    inject: { 
      fooInject :{default : false},
    },
    computed: {
     ...
 ```



## storybook storyの書き方
テスト対象（Injectされる側）の必要な変更は上記のように最小にしておきます。
テストコード（今回はstorybookのstory) の書き方は下記のようになります。

```javascript:SomePage.stories.js
import idea from '../../src/views/SomePage';

// More on default export: https://storybook.js.org/docs/vue/writing-stories/introduction#default-export
export default {
  title: 'somepage',
  component: some,
  excludeStories: /.*Data$/,
 };


/* story記述 */
// default
export const Default = () => ({    // 変数名がナビゲーションパネルでの表示名となる
    components: { some },  // 対象となるコンポーネントを指定する
    template: '<some />'  // レンダリングするhtmlを記述する
  });

// check foo
export const checkFoo = () => ({    // 変数名がナビゲーションパネルでの表示名となる
  components: { some },  // 対象となるコンポーネントを指定する
  provide(){
    return {fooInject:true}
  },
  template: '<some />'  // レンダリングするhtmlを記述する
});

// check baa
export const checkBaa = () => ({    // 変数名がナビゲーションパネルでの表示名となる
  components: { some },  // 対象となるコンポーネントを指定する
  provide(){
    return {
      baaInject:true,
      contentInject:'test baa from storybook scenario',
    }
  },
  template: '<some />'  // レンダリングするhtmlを記述する
});
```
これで　vueの data プロパティをすきに変えて　全画面遷移のVRTがかきやすくなります！