はじめに
Dropdownコンポーネント実装のときにFloating UIのDropdownのようにトリガーボタンとfloating要素の構成にしましたが、そうすると親要素のStyleの影響を受けてfloating要素部分の表示が見切れてしまう等といった問題がありました。
その問題を解消するために、floatingする要素をコンポーネント外のbody直下などに配置して表示するように改修しました。
ただ挙動自体は正しく動作するようになりましたが、DOMの配置方法を変更したことでStorybookのインタラクションテストやChromaticのVisual Regression Testing(以下VRT)に支障が出たため、その内容と対処方法を共有します。
詰まったところと対処方法
1. インタラクションテストでfloating要素が見つからない
Storybookでインタラクションテストを行う際にplay関数を使用します。play関数は引数としてcanvasElementを受け取りますが、このcanvasElementにはstoryに描画するHTMLElementが入っています。
// 例
export const SampleStory: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole('button'));
...
},
};
そのため今回のDropdownのfloating要素のように、body直下に配置した要素はcanvasElemetの中には含まれていません。
なので、以下のようにownerDcumentを使用してbody要素を取得し、そこからfloating要素を見つける必要があります。
export const SampleStory: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// body要素取得
const body = canvasElement.ownerDocument.body;
// #TODO body要素以降も書いておく
},
};
またその他に@storybook/testing-library
が提供するscreen
を使用しても、body直下の要素を取得することはできるようですが、以下のように警告が出ていたため、この方法は採用しませんでした。
You are using Testing Library's screen' object. Use ' within(canvasElement) instead.
More info:https://storybook.js.org/docs/react/essentials/interactions
2. フローティング要素がsnapshotされない
ChromaticでDropdownのVRTを行った際に、Dropdownコンポーネント内の要素(トリガーボタンなど)はsnapshotされましたが、フローティング要素が表示されているはずなのに写っていませんでした。
Chromaticの公式ドキュメントの以下のところに、フローティング要素が写っていない理由が書いてありました。
Why are components that render in a portal (tooltip, modal, popover, menu) getting cut off?
Chromaticがコンポーネントをsnapshotする際に、その写る領域は#storybook-root
のdiv要素(=canvasElement)の高さまでのようです。
なのでドキュメントに書いてあるとおり、storyのdecoratorでフローティング要素を含めた高さを指定しておくことで、見切れず撮影することができました。
export default {
component: Dropdown,
title: "Dropdown VRT",
decorators: [
(Story) => (
// 高さを指定
<div style={{ height: "300px" }}>
<DropdownStory />
</div>
),
],
};
まとめ
- body直下に要素を置いた場合の各テストの対応
- Storybookのインタラクションテストでは
canvasElement.ownerDocument.body
でbody要素にアクセスできる - ChromaticのVRTでは、
#storybook-root
のdiv要素分の高さまでしかsnaphotされないため、その他の要素も写したい場合はdecoratorで高さを指定する
- Storybookのインタラクションテストでは
参考