この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは!DA(データアナリティクス)事業本部 サービスソリューション部の大高です。
最近コードベースのデザインツール「UXPin Merge」を触る機会をいただいたので、実際に試したことをまとめたいと思います。
UXPin Mergeとは?
UXPin Mergeはコードベースのデザインツールです。
一般的なデザインツールでは描画したものはベクター、ラスターのイメージデータとして扱われますが、UXPinで描画したものはコード化が行われ実際に動作するコンポーネントとしてレンダリングされます。
また、UXPin Mergeでは作成したReactコンポーネントをインポートしてデザインに利用することができます。
今回試してみたこと
UXPin Mergeでは「React」に対応していますが、今回は「Next.js」のプロジェクトでコンポーネントを作成し、作成したものをUXPinに取り込んで利用できるか試してみます。
コンポーネントとしては「簡単なボタンのコンポーネント」を作成して、試してみたいと思います。
ドキュメントは以下のドキュメントを参考にしてすすめてみます。
前提
今回実施する環境は、NodeとYarnが設定済みの環境となっています。
% node -v
v16.13.2
% yarn -v
1.22.17
プロジェクトのセットアップ
まずはNext.jsのプロジェクトを作成します。今回はhello-uxpin-merge-sample
という名前にしました。
$ yarn create v1.22.17
[1/4] ? Resolving packages...
[2/4] ? Fetching packages...
[3/4] ? Linking dependencies...
[4/4] ? Building fresh packages...
success Installed "create-next-app@12.1.0" with binaries:
- create-next-app
? What is your project named? › hello-uxpin-merge-sample
(...snip...)
プロジェクトを作成したら、UXPin MergeのCLIをインストールします。
$ cd hello-uxpin-merge-sample
$ yarn add -D @uxpin/merge-cli
インストール後に以下のコマンドを実行してUXPin Merge関連の初期化をしておきます。
$ npx uxpin-merge init
You are using @uxpin/merge-cli version: 2.7.10
✅ Successfully created /misc/hello-uxpin-merge-sample/uxpin.config.js
✅ Successfully created /misc/hello-uxpin-merge-sample/uxpin.webpack.config.js
✅ Successfully created /misc/hello-uxpin-merge-sample/src/components/UXPinWrapper/UXPinWrapper.js
✅ Successfully created example component in /misc/hello-uxpin-merge-sample/src/components/Button
初期化をすることで、サンプルのButtonコンポーネントButton.js
と設定ファイルuxpin.config.js
、uxpin-webpack.config.js
などが自動生成されます。
なお、今回はこのサンプルのButton.js
コンポーネントは利用しません。
TypeScript関連のセットアップ
今回は、TypeScriptのNext.jsプロジェクトなのでTypeScript関連のセットアップを行います。
UXPin MergeではWebpackを利用しているので、Loaderの追加インストールや設定ファイルの変更などを行っていきます。
ts-loader
ts-loaderをインストールします。注意点として、merge-cliでは「Webpack 4」を利用しているようなので、バージョンの指定でWebpack 4をサポートしている~8.2.0
を指定するようにします。
yarn add -D ts-loader@~8.2.0
なお、バージョン指定をせずに最新バージョンを利用すると以下のようなエラーが発生します。
loaderContext.getOptions is not a function
uxpin.webpack.config.js
Webpackの設定ファイルuxpin.webpack.config.js
は初期状態ではTypeScript向けにはなっていないので、いくつか設定していきます。
uxpin.webpack.config.js
const path = require("path");
const webpack = require("webpack");
module.exports = {
output: {
path: path.resolve(__dirname, "build"),
filename: "bundle.js",
publicPath: "/"
},
resolve: {
modules: [__dirname, "node_modules"],
extensions: ["*", ".js", ".jsx", ".ts", ".tsx"],
},
devtool: "source-map",
module: {
rules: [
{
test: /\.ts$|\.tsx$/,
use: [
{
loader: require.resolve("babel-loader", {
paths: ["./node_modules/@uxpin/merge-cli"],
}),
options: {
presets: [
require.resolve("@babel/preset-react", {
paths: ["./node_modules/@uxpin/merge-cli"],
}),
],
},
},
{
loader: "ts-loader",
},
],
exclude: /node_modules/,
},
{
test: /\.(s*)css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
]
},
{
loader: require.resolve('babel-loader', { paths: ['./node_modules/@uxpin/merge-cli'] }),
test: /\.js?$/,
exclude: /node_modules/,
options: {
presets: [
require.resolve('@babel/preset-env', { paths: ['./node_modules/@uxpin/merge-cli'] }),
require.resolve('@babel/preset-react', { paths: ['./node_modules/@uxpin/merge-cli'] })
],
}
},
]
}
}
resolve
の拡張子に.ts
と.tsx
を加えているのと、module.rules
に各loaderの設定を加えています。
ts-loader
は先程追加インストールしたものを利用しており、それ以外はmerge-cli
に含まれているので、そちらを利用しています。注意点としてloaderは下から順番に評価されるのでts-loader
の設定を下にしています。
Loaders can be chained. Each loader in the chain applies transformations to the processed resource. A chain is executed in reverse order.
tsconfig.json
Next.jsのデフォルトではnoEmit
がtrue
となっているのですが、こちらはUXPin Merge用にfalse
に設定します。
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": false,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
起動用の設定
あとは package.json
を修正して、uxpin関連のコマンドが実行できるようにしておきます。
package.json
{
"name": "hello-uxpin-merge-sample",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"uxpin": "uxpin-merge --disable-tunneling",
"push": "uxpin-merge push --token \"SampleToken12345678901234567890123456789\" --branch main"
},
"dependencies": {
"next": "12.1.0",
"react": "17.0.2",
"react-dom": "17.0.2"
},
"devDependencies": {
"@types/node": "17.0.21",
"@types/react": "17.0.39",
"@uxpin/merge-cli": "^2.7.10",
"eslint": "8.9.0",
"eslint-config-next": "12.1.0",
"ts-loader": "~8.2.0",
"typescript": "4.5.5"
}
}
コンポーネントの動作確認実行用にuxpin
、サーバーへのコードデプロイ用にpush
を追加で定義しました。
uxpin-merge
こちらのコマンドで--disable-tunneling
オプションを付加して起動すると、UXPinのExperimental Modeが起動して自作コンポーネントの確認ができます。
なお、確認時にはUXPinにログインする必要があるので、事前にUXPinのサイトからログインしておくと確認がスムーズです。
uxpin-merge push
このコマンドは自作コンポーネントをサーバー側のライブラリにPushするコマンドです。2つオプションを指定しています。
--token
このトークンにはUXPin側で設定したライブラリに設定したトークンを指定します。
まずはMerge用のライブラリを以下のページの「Add new library」から作成していきます。
「Import React Components」を選択して「Next」
「Library name」に適宜名前をつけて、権限設定を選択したら「Create library」で作成します。
作成したらライブラリを開きます。
開いた画面にトークン情報が表示されるのでこちらをトークンとして利用します。
--branch
こちらには自分のgitブランチ名を指定します。指定しない場合にはmaster
が利用されるようです。
私は今回ローカル環境で特に意識していませんでしたが、gitのブランチをgit status
コマンドで確認するとmain
ブランチだったので明示的に指定をしています。
コンポーネントを作成する
では、肝心のコンポーネントを作成していきます。
今回はuxpin-merge init
でサンプル作成されたコンポーネントと同様の階層に自作コンポーネントSampleButton
を作っていきます。
ファイル構成は以下のようになります。
$ cd src/components/
$ tree
.
├── Button
│ ├── Button.js
│ └── presets
│ └── 0-default.jsx
├── SampleButton
│ ├── SampleButton.tsx
│ └── presets
│ └── 0-default.jsx
└── UXPinWrapper
└── UXPinWrapper.js
まずはコンポーネント自身のコードです。こちらはただのシンプルなボタンになっています。
src/components/SampleButton/SampleButton.tsx
import React from "react";
type Props = {
disabled: boolean;
label: string;
};
const SampleButton = ({ ...props }: Props) => {
return (
<div>
<button type="button" disabled={props.disabled}>
{props.label}
</button>
</div>
);
};
export default SampleButton;
次にちょっと特殊な0-default.jsx
です。これは、UXPin側でコンポーネントを表示させる場合のデフォルト値を設定するためのものです。フォルダ名とファイル名はこの名前が規定のようなのでそのまま指定しています。
src/components/SampleButton/presets/0-default.jsx
import * as React from "react";
import SampleButton from "../SampleButton";
export default (
<SampleButton
uxpId="SampleButton"
disabled={false}
label="I'm Sample"
></SampleButton>
);
コンポーネントが作成できたら、Next.js側でも動作を確認してみます。
ちょっと見るだけなので、pages/index.tsx
に埋め込んで確認してみました。
pages/index.tsx
import type { NextPage } from 'next'
import Head from 'next/head'
import Image from 'next/image'
import SampleButton from '../src/components/SampleButton/SampleButton'
import styles from '../styles/Home.module.css'
const Home: NextPage = () => {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<SampleButton disabled={false} label={'in Next.js'}></SampleButton>
(...snip...)
動かしてみましょう。
$ yarn dev
問題なさそうですね。ボタンのラベルも指定したもので表示されています。
UXPin MergeのExperimental Modeで確認する
さて、コンポーネントが作成できたのでUXPin MergeのExperimental Modeで確認したいと思います。
まずはコンポーネントを設定ファイルに登録する必要があるので、uxpin.config.js
を以下のように編集します。
uxpin.config.js
module.exports = {
components: {
categories: [
{
name: 'General',
include: [
'src/components/Button/Button.js',
'src/components/SampleButton/SampleButton.tsx',
],
},
],
wrapper: 'src/components/UXPinWrapper/UXPinWrapper.js',
webpackConfig: 'uxpin.webpack.config.js',
},
name: 'Example Design System'
};
コンポーネントの定義に、作成したSampleButton
コンポーネントのパスを指定して追加しました。
指定が済んだら以下のコマンドで起動させて、表示されたURLにアクセスします。
$ yarn uxpin
yarn run v1.22.17
$ uxpin-merge --disable-tunneling
You are using @uxpin/merge-cli version: 2.7.10
┌─────────┐
│ │
│ UXPin │ {V}erge - Experimental Mode
│ │
└─────────┘
?? Open the following URL in your browser to enter the experimental mode:
https://app.uxpin.com/experiment/foobar?port=8877&name=Example Design System
表示されました!デフォルトのサンプルボタンButton
と、自作コンポーネントのSampleButton
が表示されています。
ラベルにもpresets/0-default.jsx
に設定したデフォルト値I'm Sample
が入っていますね。
サーバーにPushする
最後に作成したライブラリをサーバーにPushしてみましょう。
$ git add .
$ git commit -m "[add]first commit"
$ yarn push
yarn run v1.22.17
$ uxpin-merge push --token "SampleToken12345678901234567890123456789" --branch main
You are using @uxpin/merge-cli version: 2.7.10
✅ Library bundle uploaded successfully!
✅ Library metadata uploaded successfully!
? Projects using this Design System have been updated to branch [main]
✨ Done in 12.79s.
pushが完了したら、ブラウザでUXPin側にログインして先程トークンを取得した際に作成した自作ライブラリを開いてみましょう。
ちゃんとライブラリに登録されましたね。
あとは、デザイン画面上でも同様に参照可能なので、適宜画面に配置してデザインを行うことができます。
まとめ
以上、コードベースでのデザイン作成「UXPin Merge」を試してみました。
このツールの素敵なところは、ともかくローカル環境で作成したコンポーネントがそのままデザイン画面で利用できるところにあると思っています。また、Gitでコード管理が出来るのも、UXPin側へCLIコマンドでPushして連携できるのも素敵ですね。
今回は試していませんが、Circle CIやGitHub Actionsをうまく使ってCIを回すこともできるそうです。
Connecting code to UXPin (CI & push) | Merge
また以下には日本語のドキュメントも用意されていますので、こちらも参考になるかと思います。
どなたかのお役に立てば幸いです。それでは!
参考
以下は、今回特に参考にさせていただいたサイトです。