ComponentBlocksで入力したコンテンツ用コンポーネントとStorybook

2年前にこのブログの運用を開始した時から気になっていた「Storybook」。某国立大学法人の提案依頼書内で想定成果物に記載されるほどメジャーとなったUIカタログ作成ツールですが、React/Vueを使わずにHTML/CSSを設計しMTMLでテンプレート化している昔ながらのプロジェクト環境にいることもあってなかなか試すことができずにいました。そのような中、ふと『PowerCMS X version 3.5から提供が開始された「ComponentBlocks」とReact(Next.js)を組み合わせて利用するとどうなるだろう?』と思いつき試してみました。

ComponentBlocksで編集したデータはJSON形式でデータベースに格納されており、PowerCMS XのAPIでもアクセスすることが可能です。サンプルのJSON(一部抜粋)は以下の通りです。

{
    "id": 593,
    "title": "ComponentBlocksテスト",
    "text": "",
    "text_format": "richtext",
    "block_edit": [
        {
            "id": "99vs84oe",
            "type": "Heading",
            "text": "サンプル見出し",
            "level": 2
        },
        {
            "id": "bkzejr4i",
            "type": "Text",
            "text": "狸は口の話床手から集りへ待ち構え胸たた。するとどうばかたたというかっこうでた。だめましまし方ましもなまたぱちんととめておじぎをの生意気めのところをもどうしても上手たたて、あれでもホールの鳴っせんなう。云いすぎきみは先生から高くたながら毎日のあかしの赤団が云っ第二専門げのマッチへ入れとくださいたた。鳥も午前来ながらくれた。",
            "align": "left"
        }
    ],
}

1ブロック1オブジェクトで配列の中に格納されていますので、これをループして取り出してブロックに応じたコンポーネントを適用します。

import { Fragment } from 'react';
import Heading from './atoms/Heading';
import Text from './atoms/Text';
import styles from '../styles/BlogEntry.module.scss';

function getBlockComponent(block) {
  switch (block.type) {
    case 'Heading':
      return <Heading text={block.text} level={block.level} />;
    case 'Text':
      return <Text text={block.text} align={block.align} />;
    default:
      return null;
  }
}

export default function EntryBlockEditorBody({ blocks }: { blocks: Array<any> }) {
  return(
    <div className={styles['article-body'] + ' js-links'}>
      {blocks.map((block, index) => (
        <Fragment key={index}>{getBlockComponent(block)}</Fragment>
      ))}
    </div>
  );
}

テキストブロックのコンテンツを表示するためのTextコンポーネントは以下のように試作してみました。

import styles from './text.module.scss';

type TextProps = {
  text: string;
  align?: 'left' | 'center' | 'right';
}

export default function Text({ text, align }: TextProps) {
  let alignStyle = '';

  if (align === 'center') {
    alignStyle = styles['text-center'];
  } else if (align === 'right') {
    alignStyle = styles['text-right'];
  }

  return (
    <p className={alignStyle}>{text}</p>
  );
}

このTextコンポーネントに対するストーリーを書くと、以下のように左寄せ・中央揃え・右寄せの状態も反映されたコンポーネントの表示がされました。
文章を表示する「Text」コンポーネントをStorybookで可視化した画面のキャプチャ

UIのバリエーションも含めてカタログ化しプロジェクト内で共有できること、文字を打ち替えるなど状態を変化させて表示を確認できること、などに大きな魅力を感じます。Jamstack導入のメリットに「開発者体験の向上」もあるとずっと考えていましたが、その一端を今回のStorybook試作で感じることができました。