16
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

MetapsAdvent Calendar 2023

Day 25

Amplify Studio と Figma を同期してReactコンポーネントを自動生成してみた

Last updated at Posted at 2023-12-24

はじめに

Metaps Advent Calendar 第25日目の記事です。
(弊社の場合、Advent Calendarの投稿日は参加してるエンジニア同士で早い者勝ちで決まるのですが、もたもたしてたら最終日のクリスマス当日枠になってしまいました🎄)

本題に入りますが、Figmaで作成されたデザインからReactコンポーネントを自動生成できれば便利ですよね。

今回は、ずっと気になっていた「Amplify Studio」と「Figma」を同期してReactコンポーネントを自動生成する方法を試してみました。FigmaのデザインからReactコンポーネントに変換してReactアプリに組み込む手順について主に書いていこうと思います。このハンズオンに沿って進めていきます。

事前準備

  • Githubアカウント
  • AWSアカウント
  • Figmaアカウント(無料版でOK)

Amplifyアプリケーションのデプロイ

バックエンドとフロントエンドを1つのGUI開発環境でビルドするために、まずはコチラからGitHubのサンプルリポジトリをフォークして、構成済みのバックエンドが含まれた新しいAmplifyアプリケーションをデプロイします。

スクリーンショット 2023-12-24 10.52.57.png

Githubに接続したら以下のような画面が表示されるので、好きな名前をつけていきます。
今回はamplify-homesというアプリ名で生成しました。
ここでサービスロールを作成する必要があるので、「新しいロールを作成」を押してIAMからロールを作成していきます。
スクリーンショット 2023-12-24 10.55.07.png

スクリーンショット 2023-12-24 17.38.53.png

ロール作成ができたら、そのロールを選択して「保存してデプロイ」をします。
スクリーンショット 2023-12-24 11.03.50.png

この画面にたどり着くのに5分以上はかかるので、次の手順などを読みながら待つのが良さそうです。
スクリーンショット 2023-12-24 11.18.34.png

デプロイが完了したら、Backend environmentsタブに移動して、「Studio を起動する」を押します。
スクリーンショット 2023-12-24 11.24.28.png

Amplify Studio Consoleでシードデータの作成

別タブで以下のAmplify Studioコンソールが起動します。ここからFigmaからインポートしたUI Libraryの設定やシードデータの生成などが操作できます。

スクリーンショット 2023-12-24 11.29.06.png

早速、サイドメニューのDataからData Modelingの画面をひらいてシードデータを作成していきます。今回は、ID, address, image_url, priceのフィールドを持つデータを定義していきます。

スクリーンショット 2023-12-24 17.37.50.png

Actions > Auto-generate dataからデータを作成すると、データをいくつ作成するかや、数字の範囲、値の設定などが選択できます。

addressフィールドには住所を入れたいので、「Street address」を設定します。
image_urlUnsplash's random photo generatorを使いましょうとのことなので、それに従って生成された画像のURLを入力していきます。

スクリーンショット 2023-12-24 11.36.17.png

そのほか、データ自動生成時に何かしらの制約を追加したい場合は Add constrainから追加が可能です。

スクリーンショット 2023-12-24 11.38.10.png

Generate dataを押してデータの生成が完了!!
ここで生成したデータをUI構築時に使用できるようになります。

スクリーンショット 2023-12-24 17.50.22.png

Figma でデザインテンプレートをインポート

自分でデザインを生成しても問題ないですが、せっかくなのでAWS Amplify UI Kitテンプレートを使ってみることにします。
このUI Kit でFigma を開いて、まずはREADMEに目を通してみます。

スクリーンショット 2023-12-24 18.03.06.png
Getting Startedの章でPrimitivesの中身は触るな!!と言われているので、このpageは眺めるだけにしましょう。
ここはAmplify UIでpre-buildされたprimitivesが含まれている箇所なので、このページでデザインに変更を加えたとしても、Amplify Studioに反映されないと言われてますね。(いじくる前にREADME見ていて良かった..)

自分でデザインを作成する場合は、My Components page なら大丈夫そうですね!
primitives page を少し覗いてみると、色んなボタンはもちろんSearchFieldとかSliderFieldなども用意されてました。

スクリーンショット 2023-12-24 18.25.21.png

スクリーンショット 2023-12-24 18.25.30.png

Figma でデザインを作成

Figma でデザインの作成をしたことないので、見よう見真似でチュートリアルで紹介されていたデザインを作ってみることにします。 READMEでコンポーネントを作成するにはYOU HAVE TO USE FRAMES と記載されているので、frameをまずは作成していきます。

※ Figma上で自分のアカウントの権限がRead OnlyになっていてFrame作成のボタンが表示されなかったので注意

スクリーンショット 2023-12-24 18.32.10.png

HomeCardという名前でコンポーネント化し、その中にさらにframeを入れて、TitleDescriptionを作成していきます。

Figmaの基本的な操作方法を調べながらできたデザインがコチラ。

スクリーンショット 2023-12-24 13.14.04.png

Figma と Amplify Studio を同期

Figmaで作成したコンポーネントをAmplify Studioに取り込むために同期の設定をしていきます。まずはFigmaの右上のShareボタンを押して、URLをコピーします。

スクリーンショット 2023-12-24 12.11.35.png

Amplify Studio に戻り、サイドメニューのUI Libraryから右上のSync with Figmaを押し、コピーしたFigmaのURLをAmplify Studio に貼り付けて同期させます。

スクリーンショット 2023-12-24 12.13.40.png

スクリーンショット 2023-12-24 12.13.51.png

コピーしたURLを貼り付けると、AmplifyがFigmaへのアクセス権を求めるウィンドウが立ち上がるので、許可をします。
スクリーンショット 2023-12-24 12.14.28.png

Amplify Studio のUI LibraryにFigmaのデザインがComponentsとして作成されていれば成功です!自作したHomeCardコンポーネントもAmplify Studioに取り込まれてるのが下記の画像から確認できますね。

UI コンポーネントをデータと紐づける

UI LibraryにFigmaでインポートしたデザインがComponentsとして表示されるので、今回自分で作成したHomeCardコンポーネントを選択してConfigureを押します。

スクリーンショット 2023-12-24 19.06.09.png

ここで開いたUIコンポーネントエディターで、コンポーネントのプロパティと、それに紐づくコンポーネントのUI要素を紐づけていきます。

Component propertiesにはAdd propから入力フィールドを追加し、Data Modeling画面で作成したHomeTypeに指定します。 Child propertiesにはUIコンポーネントの各要素に紐付けたいデータを指定していきます。

Elements treeで操作したい要素を選択して、どのデータを紐づけるかを直感的に操作することができました。

image要素
スクリーンショット 2023-12-24 13.28.30.png

description要素
スクリーンショット 2023-12-24 13.32.55.png

今回は、要素のTitleにはhome.addressDescriptionにはhome.priceを文字列の中で変数を展開して表示するように割り当てます。 また、onClick actionも割り当てができるので、画像を押したら画像URLに遷移するような動作も実現することができそうです。

これでコンポーネントにデータの紐付けは完了です!

Collectionのコンポーネントを作成する

実際にUIを構築するときには、このコンポーネントを利用して複数のHomeCardを表示させたいので、コレクションを作成してみます。上記キャプチャの右上のCreate collectionからコレクション名を入力して生成されたコンポーネントが以下になります。 これでシードデータを使用したHomeCardCollectionコンポーネントの作成ができました。画面左側のLayoutで確認できますが、Grid表示でのカラム数の指定、X-aligin, Y-align やHomeCardを並べるときの余白もpxで指定することが可能です。

スクリーンショット 2023-12-24 13.36.55.png

Reactアプリにコンポーネントを取り込む

上記のComponent Editor 画面で</>Get component codeを押すと、Reactアプリに取り込む手順が表示されるので、それに従えばコンポーネントを取り込むことができます。

スクリーンショット 2023-12-24 20.01.16.png

今回はReactアプリをまだ作成してないので、以下のコマンドを実行してamplify-homesという名前でReactアプリを作成します。

npx create-react-app amplify-homes	

次にnpmコマンドを実行します。

npm install -g @aws-amplify/cli
npm install aws-amplify @aws-amplify/ui-react

aws-amplify/ui-reactは、AWS AmplifyのReact用UIコンポーネントライブラリです。このライブラリには、認証コンポーネント(ログイン、サインアップフォームなど)や、様々なUI要素が含まれており、Reactアプリケーションでの使用に最適化されているようです。

次に以下のコマンドを実行します。

amplify pull --appId {YOUR_APP_ID} --envName devh

実行すると、Amplifyにログインされるかを聞かれ、その後、FigmaのコンポーネントをReactのコンポーネントに変換するためにいくつか質問が表示されるので、回答していきます。

スクリーンショット 2023-12-24 14.57.36.png

スクリーンショット 2023-12-24 14.58.08.png

すると、コンポーネントが自動生成されました!!:sunglasses:

amplify-homesのプロジェクトを確認すると、amplify, /src/ui-componentsディレクトリが追加されてます。そして、ui-componentsの配下には、Amplify Studio から実際にPullしてきたコンポーネントの一覧が格納されてます。Amplify Studio のUI Library > Componentsに存在するコンポーネント名と一致するファイルが生成されてることが確認できます。フロント実装時には、ここからコンポーネントをインポートして使用する形になります。

スクリーンショット 2023-12-24 20.43.28.png

次にindex.jsを修正します。

index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";

+ import { ThemeProvider } from "@aws-amplify/ui-react";
+ import { Amplify } from "aws-amplify";
+ import awsconfig from "./aws-exports";

+ import "@aws-amplify/ui-react/styles.css";
+ import { studioTheme } from "./ui-components";

+ Amplify.configure(awsconfig);

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
+     <ThemeProvider theme={studioTheme}>
      <App />
+     </ThemeProvider>
  </React.StrictMode>
);

reportWebVitals();

App.jsで出力されたコンポーネントをインポートします。テンプレートに存在していたNavBarHeader2もインポートしてみました。Amplify UI の「Flex」コンポーネントで使用できるプロパティを使用できるようです。 この例では試しに、NavBarHeader2width={"100vw"}を指定してみました。

App.js
import "./App.css";
import { HomeCardCollection, NavBarHeader2 } from "./ui-components";

function App() {
  return (
    <div className="App">
      <NavBarHeader2 width={"100vw"} />
      <HomeCardCollection />
    </div>
  );
}

export default App;

HomeCardCollectionisPaginatedプロパティとitemsPerPage={3}などを指定することで、ページネーションを表示できたり1ページに表示させるHomeCardの数などを指定することが可能です。

<HomeCardCollection isPaginated itemsPerPage={3}/>

アプリ起動!!

npm start

スクリーンショット 2023-12-24 16.00.47.png

FigmaでデザインしたものにAmplify Studio で生成したデータが紐づいた形でUIが構築できてますね! イイ感じです!

まとめ

今回、特に詰まった点はなく、GUIからぽちぽちするだけで、Reactコンポーネントを自動生成できる点は非常に便利と感じました。デザインからコンポーネントを作る作業が爆速になりそうな予感がしました!!ただ実際にFigmaでデザインしてReactの自動生成をしてみた結果、Figmaの知識が少し必要かも、、という所感です。プロジェクトに導入するなら、Figmaでデザイン生成時点でデザイナーと開発者の間でしっかり決め事を決めておかないと想定外のことも起きそうな気がしました。ネックになりそうなポイントを洗い出して検討する必要はありそうかもという感じですね。

最後に出力されたコードを貼り付けて貼り付けておわりにしたいと思います。

Figmaから自動生成されたコンポーネントのjsxファイルのコード

HomeCard.jsx
/***************************************************************************
 * The contents of this file were generated with Amplify Studio.           *
 * Please refrain from making any modifications to this file.              *
 * Any changes to this file will be overwritten when running amplify pull. *
 **************************************************************************/

/* eslint-disable */
import * as React from "react";
import { getOverrideProps } from "./utils";
import { Image, Text, View } from "@aws-amplify/ui-react";
export default function HomeCard(props) {
  const { home, overrides, ...rest } = props;
  return (
    <View
      width="320px"
      height="235px"
      display="block"
      gap="unset"
      alignItems="unset"
      justifyContent="unset"
      overflow="hidden"
      position="relative"
      padding="0px 0px 0px 0px"
      backgroundColor="rgba(255,255,255,1)"
      {...getOverrideProps(overrides, "HomeCard")}
      {...rest}
    >
      <Image
        width="320px"
        height="160px"
        display="block"
        gap="unset"
        alignItems="unset"
        justifyContent="unset"
        position="absolute"
        top="0px"
        left="0px"
        padding="0px 0px 0px 0px"
        objectFit="cover"
        src={home?.image_url}
        {...getOverrideProps(overrides, "image")}
      ></Image>
      <View
        width="320px"
        height="67px"
        display="block"
        gap="unset"
        alignItems="unset"
        justifyContent="unset"
        overflow="hidden"
        position="absolute"
        top="168px"
        left="0px"
        padding="0px 0px 0px 0px"
        backgroundColor="rgba(255,255,255,1)"
        {...getOverrideProps(overrides, "Frame 437")}
      >
        <Text
          fontFamily="Inter"
          fontSize="16px"
          fontWeight="700"
          color="rgba(0,0,0,1)"
          lineHeight="24px"
          textAlign="left"
          display="block"
          direction="column"
          justifyContent="unset"
          width="320px"
          height="25px"
          gap="unset"
          alignItems="unset"
          position="absolute"
          top="0px"
          left="0px"
          padding="0px 0px 0px 0px"
          whiteSpace="pre-wrap"
          children={home?.address}
          {...getOverrideProps(overrides, "Title")}
        ></Text>
        <Text
          fontFamily="Inter"
          fontSize="12px"
          fontWeight="400"
          color="rgba(0,0,0,1)"
          lineHeight="24px"
          textAlign="left"
          display="block"
          direction="column"
          justifyContent="unset"
          width="320px"
          height="25px"
          gap="unset"
          alignItems="unset"
          position="absolute"
          top="33px"
          left="0px"
          padding="0px 0px 0px 0px"
          whiteSpace="pre-wrap"
          children={`${"Price: $"}${home?.price}${"/night"}`}
          {...getOverrideProps(overrides, "Description")}
        ></Text>
      </View>
    </View>
  );
}
HomeCardCollection.jsx
/***************************************************************************
 * The contents of this file were generated with Amplify Studio.           *
 * Please refrain from making any modifications to this file.              *
 * Any changes to this file will be overwritten when running amplify pull. *
 **************************************************************************/

/* eslint-disable */
import * as React from "react";
import { Home } from "../models";
import { SortDirection } from "@aws-amplify/datastore";
import { getOverrideProps, useDataStoreBinding } from "./utils";
import HomeCard from "./HomeCard";
import { Collection } from "@aws-amplify/ui-react";
export default function HomeCardCollection(props) {
  const { items: itemsProp, overrideItems, overrides, ...rest } = props;
  const itemsPagination = {
    sort: (s) => s.createdAt(SortDirection.DESCENDING),
  };
  const [items, setItems] = React.useState(undefined);
  const itemsDataStore = useDataStoreBinding({
    type: "collection",
    model: Home,
    pagination: itemsPagination,
  }).items;
  React.useEffect(() => {
    if (itemsProp !== undefined) {
      setItems(itemsProp);
      return;
    }
    setItems(itemsDataStore);
  }, [itemsProp, itemsDataStore]);
  return (
    <Collection
      type="grid"
      searchPlaceholder="Search..."
      templateColumns="1fr 1fr 1fr"
      autoFlow="row"
      alignItems="stretch"
      justifyContent="stretch"
      items={items || []}
      {...getOverrideProps(overrides, "HomeCardCollection")}
      {...rest}
    >
      {(item, index) => (
        <HomeCard
          home={item}
          key={item.id}
          {...(overrideItems && overrideItems({ item, index }))}
        ></HomeCard>
      )}
    </Collection>
  );
}

16
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?