メモ。
ちょっと前から Lightning Design System(LDS)の React 実装が公式から出てます。
Lightning Design System for React
で、ようやく入れてみようとドキュメントを頼りにやってみたところ色々手こずったので、手順をまとめておきます。
はじめに
design-system-react の設定に関するドキュメントは、以下のように点在しています。
- Getting Started
- リポジトリの README#Usage
- リポジトリの docs/webpack.md (Usage with Webpack)
1 が一番参考にしたドキュメント。webpack.config.js の設定例なども載っています。基本はここを見ながらやりました。
(ここに書かれてる内容は private repo で管理してるらしい)
2 は 1 と被る部分もあり、ただ Icon の設定に関してはここにしか記載されてません。
また 1, 2 は design-system-react のインストール手順であり、これだけだと LDS はインストールされず、スタイルがあたりません。
LDS を webpack から使えるようにするための情報が 3 に載っています。
以下では、設定手順を大きく「1. design-system-react の設定」と「2. LDS の設定」に分けて記載します。
サンプルコード
Redux & Express でのアプリのひな形リポジトリに導入したときの PR です。
クライアント側のファイルは public
というディレクトリに配置するような構成になっています。
- public
- assets
- lds --- LDS はここに展開
- bundle.js --- ビルド後のファイル
- components --- React & Redux の
- containers
- ...
- index.js --- エントリーポイント
- index.html
- package.json
- webpack.config.base.babel.js
- webpack.config.dev.babel.js
- webpack.config.prod.babel.js
1. design-system-react の設定
まずは design-system-react のインストールから設定までの手順。主に先ほどの 1. Getting Started を参考にします。
インストール
npm でインストールできます。
$ npm install @salesforce-ux/design-system @salesforce/design-system-react
webpack の設定
Getting Started のページに webpack.config.js の設定例があるので、それを参考に設定していきます。
// webpack.config.babel.js (抜粋)
export default {
context: path.resolve(path.join(__dirname, 'public')),
entry: [
'./index',
],
+ resolve: {
+ extensions: ['.js', '.jsx'],
+ },
output: {
filename: 'bundle.js',
path: path.resolve(path.join(__dirname, 'public', 'assets')),
publicPath: '/assets/',
},
module: {
rules: [
{
- test: /\.js$/,
- exclude: /node_modules/,
+ test: /\.jsx?$/,
+ include: [
+ path.resolve(path.join(__dirname, 'public')),
+ path.resolve(path.join(__dirname, 'node_modules/@salesforce/design-system-react')),
+ ],
use: 'babel-loader',
},
{
...
(ファイル全体は こちら を参照)
ポイントとしては
- design-system-react のファイルは
.jsx
なので、 .jsx
もビルドできるように resolve.extensions
および babel-loader を適用する正規表現を修正
- 元々 node_modules 以下はすべて babe-loader の対象外としていたが、design-system-react は対象に含めるよう修正
です。
また、この記事を書いた時点ではドキュメントの方では
path.join(__dirname, 'node_modules/design-system-react')
というように @salesforce
が入ってなかったんですが、入れるのが正しいです。
Issue で報告したのでそのうち直るはず。
Babel の設定
.babelrc
を修正します。
これには @salesforce/babel-preset-design-system-react という preset を使えとドキュメントにはありますが、中身
https://github.com/salesforce/design-system-react/blob/master/preset/index.js
を見てみると
- babel-preset-env
- babel-preset-react
- babel-plugin-transform-object-rest-spread
- babel-plugin-transform-class-properties
- babel-plugin-transform-export-extensions
を指定しているだけなので、既に設定されている .babelrc
の内容に合わせて必要な preset/plugin を個別に追加するという対応でもいいのかもしれません。
(実際自分のリポジトリでも babel-plugin-transform-export-extensions 以外は使っていた)
今回は素直にこの preset を使うようにします。
$ npm install -D @salesforce/babel-preset-design-system-react
{
"presets": ["@salesforce/babel-preset-design-system-react"]
}
使う
このような形で import して使います。
import React, { Component } from 'react';
import Button from '@salesforce/design-system-react/components/button';
export default class App extends Component {
render() {
return (
<Button
iconName="download"
iconPosition="left"
label="Neutral Icon"
/>
);
}
}
2. LDS の設定
さて、今度は LDS を読み込むための設定をします。
これは docs/webpack.md がある程度参考になります。
インストール
$ npm install @salesforce-ux/design-system style-loader css-loader file-loader
必要になる loader も一緒にインストールしときます。
webpack の設定
// webpack.config.babel.js
...
export default {
...
module: {
rules: [
...
+ {
+ test: /\.min\.css$/,
+ use: ['style-loader', 'css-loader'],
+ },
+ {
+ test: /\.(eot|svg|ttf|woff|woff2)$/,
+ loader: 'file-loader',
+ options: {
+ name: '[name].[ext]',
+ outputPath: 'lds/',
+ },
+ },
...
postinstall 時の処理
次に、npm install 後に LDS を静的リソース配信用のディレクトリ(ここでは public/assets/lds/
)にコピーしておきます。
ドキュメントでは postinstall.js スクリプトを作ってシンボリックリンクを貼る処理をやっているようですが、単にコピーするだけなら package.json 内に記述しても十分だと思います。
// package.json
{
"scripts": {
+ "postinstall": "rm -rf public/assets/lds && cp -r node_modules/@salesforce-ux/design-system/assets public/assets/lds",
...
}
読み込み
アプリのエントリーポイントで
// public/index.js
import { render } from 'react-dom';
import { AppContainer } from 'react-hot-loader';
+import '@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.min.css';
+
import configureStore from './store/configureStore';
import App from './containers/App';
...
とすれば OK です。
3. Icon パスの設定
最後にもう一点だけ。Icon を使う場合、アプリのルートで <IconSettings>
コンポーネントを使い、パスを正しく設定しておく必要があります。
README#icon-usage や SLDS React | Icon Settings あたりに記載があります。
// public/index.js
import { AppContainer } from 'react-hot-loader';
import '@salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.min.css';
+import IconSettings from '@salesforce/design-system-react/components/icon-settings';
import configureStore from './store/configureStore';
import App from './containers/App';
import './styles/index.scss';
const store = configureStore();
const rootEl = document.getElementById('root');
+const LDS_ROOT = 'assets/lds';
render(
<AppContainer>
<Provider store={store}>
- <App />
+ <IconSettings
+ standardSprite={`${LDS_ROOT}/icons/standard-sprite/svg/symbols.svg`}
+ utilitySprite={`${LDS_ROOT}/icons/utility-sprite/svg/symbols.svg`}
+ actionSprite={`${LDS_ROOT}/icons/action-sprite/svg/symbols.svg`}
+ doctypeSprite={`${LDS_ROOT}/icons/doctype-sprite/svg/symbols.svg`}
+ customSprite={`${LDS_ROOT}/icons/custom-sprite/svg/symbols.svg`}
+ >
+ <App />
+ </IconSettings>
</Provider>
</AppContainer>,
rootEl,
);
ここに関してはよくわかってないことが 2 点あって、
1) <IconSettings>
には iconPath
を指定する方法と、xxxSprite
というアイコンの種類ごとのパスを指定する方法とあるんですが、webpack を使ってると後者を推奨してるようです。
Individual sprites If you are using webpack it is advised to use the sprite properties {actionSprite, standardSprite...} to specify the individual sprite paths so that webpack can easily re-write the paths.
この PR で追加されたようで、そこでも webpack のためにということが書かれていますが詳しいことはよくわからず。
2) xxxSprite
の指定のしかたとして、Example にあるように
import actionSprite from '@salesforce-ux/design-system/assets/icons/action-sprite/svg/symbols.svg';
のようにすればいいのかなと思ったんですが、なぜかうまくいかず。。。
結局 postinstall でコピーした方のパスを指定してうまくいったのでそうしています。
結果
<Button>
だけですが、無事に LDS のスタイルがあたった状態でコンポーネントが描画できていることが確認できました。アイコンも表示されています。
おわりに
書いてみるとなんてことないんですが、 @salesforce
がちょいちょい抜けてたりとドキュメントの整備が追いついてないみたいで結構ハマりました。。。
とりあえず使えるところまでできてよかった。
遭遇したエラー達。
ERROR in ./node_modules/@salesforce/design-system-react/components/button/index.jsx
Module parse failed: Unexpected token (228:3)
You may need an appropriate loader to handle this file type.
ERROR in ./public/components/App.js
Module not found: Error: Can't resolve '@salesforce/design-system-react/components/button' in '/Users/yamazaki/workspace/react/redux-express-template/public/components'
→ webpack の設定不備。 .jsx
がビルド対象になっていない、 node_modules/@salesforce/design-system-react
が include されていない、などなど。
/Users/yamazaki/workspace/react/redux-express-template/node_modules/webpack/lib/webpack.js:19
throw new WebpackOptionsValidationError(webpackOptionsValidationErrors);
^
WebpackOptionsValidationError: Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
- configuration.resolve.extensions[0] should not be empty.
-> A non-empty string
at webpack (/Users/yamazaki/workspace/react/redux-express-template/node_modules/webpack/lib/webpack.js:19:9)
at Object.<anonymous> (/Users/yamazaki/workspace/react/redux-express-template/app.js:13:20)
at Module._compile (module.js:643:30)
at babelWatchLoader (/Users/yamazaki/workspace/react/redux-express-template/node_modules/babel-watch/runner.js:50:13)
at Object.require.extensions.(anonymous function) [as .js] (/Users/yamazaki/workspace/react/redux-express-template/node_modules/babel-watch/runner.js:61:7)
at Module.load (module.js:556:32)
at tryModuleLoad (module.js:499:12)
at Function.Module._load (module.js:491:3)
at Function.Module.runMain (module.js:684:10)
at process.on (/Users/yamazaki/workspace/react/redux-express-template/node_modules/babel-watch/runner.js:108:21)
→ webpack の resolve.exntensions
に空文字 ['', '.js', '.jsx']
は webpack 2 系以降 NG になったぽい。使うなら'*'
。