鍋綿ブログ

C#・SharePoint・SharePoint Framework・Office365を中心に扱うブログです。

SharePoint FrameworkでMaterial UIを使う方法と注意点

SharePoint Framework (SPFx)では標準機能との見た目の統一感を考えてOffice UI Fabric Reactを使うことが多いのですが、個人的にはもっとオシャレにしたいとか楽に開発したいとかの理由でMaterial UIも割と利用しています。つい先日Material UI関連でハマってしまったのでその備忘と、ついでに導入方法も書いておきます。Reactを前提としています。

 

SPFxにMaterial UIを導入する方法

プロジェクト作成

まずはSPFxのプロジェクトを作ります。はじめての方はこちら
マニフェストとかフォルダ構成とかはお好み次第で変えてください。
ベストプラクティスをまとめていますので参考にどうぞ。

Material UIをインストール

SPFx以外で利用する時と一緒です。npmで持ってきます。

npm i @material-ui/core @material-ui/icons typeface-roboto

テーマ作成

Material UIのテーマを作り、それを全体に展開するためにwithRoot関数を作成します。テーマなんか要らないよっていう人は飛ばしてください。

配置先はどこでも良いのですが、私はsrcフォルダ直下にwithRoot.tsxファイルを作成しています。コードはこんな感じ。

import * as React from 'react';
import { MuiThemeProvidercreateMuiThemeTheme
StylesProvidercreateGenerateClassName } from '@material-ui/core/styles';
import primaryColor from '@material-ui/core/colors/indigo';
import secondaryColor from '@material-ui/core/colors/grey';
import CssBaseline from '@material-ui/core/CssBaseline';

/** アプリケーション全体に適用するテーマ */
function theme() : Theme {
  return createMuiTheme({
    palette : {
      primary :  primaryColor,
      secondary : secondaryColor,
      text: {
        primary : '#2c2c2c',
        secondary : '#6c6c6c'
      }
    },
    typography: {
      fontSize: 11,
      fontFamily: 'inherit',
      h1: {
        fontSize: 18
      },
      h2: {
        fontSize: 16
      }
    }
  });
}

/** テーマとスタイルを適用したコンポーネントを返却 */
function withRoot<P>(ComponentReact.ComponentType<P>) {
  function WithRoot(propsP) {
    const generateClassName = createGenerateClassName({
      productionPrefix: 'myWebPart_'
    });
    return (
      <MuiThemeProvider theme={theme()}>
        <CssBaseline />
        <StylesProvider generateClassName={generateClassName} >
          <Component {...props} />
        </StylesProvider>
      </MuiThemeProvider>
    );
  }

  return WithRoot;
}

/** テーマとスタイルを適用したコンポーネントを返却 */
export default withRoot;

コンポーネントにMaterial UIを導入

Webパーツを開発中であれば、React.Componentを継承するクラス(コンポーネント)が既定で1つありますのでそこにMaterial UIを盛り込みます。アプリケーションカスタマイザーなど既定でコンポーネントが無い場合は新しく作ります。以下サンプルです。

import * as React from 'react';
import msTheme from './MaterialUiSample.module.scss';
import styles from './MaterialUiSampleStyles';
import withRoot from '../../../withRoot';
import { Typography } from '@material-ui/core';
import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles';

/** プロパティ定義 */
export interface IProps extends WithStyles<typeof styles> {
}

/** Material UI サンプル Webパーツ */
class MaterialUiSample extends React.Component<IProps, {}> {
  public render(): React.ReactElement<IProps> {
    return (
      <div className={this.props.classes.Sample}>
        <Typography>これはサンプルです。</Typography>
      </div>
    );
  }
}

/** テーマとスタイルをプロパティに含めて返却 */
export default withRoot(withStyles(styles, { withTheme: true })(MaterialUiSample));

 

 

ここで読み込んでいる「MaterialUiSampleStyles」はスタイルを記述するためのファイルです。(私の好みでファイルを分けています。)こんな感じで作ります。

import { Theme } from '@material-ui/core/styles/createMuiTheme';
import createStyles from '@material-ui/core/styles/createStyles';

const styles = (themeTheme=>
    createStyles({
        Sample: {
            color: 'red'
        }
    });

export default styles;

 

これでフォルダ構成はこうなりました。

f:id:micknabewata:20200128172737p:plain

srcフォルダ配下の構成

コンポーネントを呼び出す

 

WebパーツならBaseClientSideWebPartを、アプリケーションカスタマイザーならBaseApplicationCustomizerをそれぞれ継承するクラスでコンポーネントを呼び出すようにします。以下、Webパーツ開発時のサンプルです。

import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import { IPropertyPaneConfiguration } from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'MaterialUiSampleWebPartStrings';
import MaterialUiSample from './components/MaterialUiSample';

/** マニフェストで定義したプロパティの型定義 */
export interface IProps {
  descriptionstring;
}

/** Material UI サンプル Webパーツ */
export default class MaterialUiSampleWebPart
 extends BaseClientSideWebPart <IProps> {

  /** レンダリング */
  public render(): void {
    const elementReact.ReactElement = React.createElement(
      MaterialUiSample,
      {
      }
    );

    ReactDom.render(elementthis.domElement);
  }
  
  /** 破棄イベント */
  protected onDispose(): void {
    ReactDom.unmountComponentAtNode(this.domElement);
  }

  /** バージョン取得 */
  protected get dataVersion(): Version {
    return Version.parse('1.0');
  }

  /** プロパティウィンドウ定義 */
  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: []
    };
  }
}

 

以上です。gulp serveして動作を確認してみてください。

f:id:micknabewata:20200128173918p:plain

サンプルコードの実行結果

SPFxにMaterial UIを導入する際の注意点

他のSPFxパッケージとのクラス名被り

上記サンプルで行っているようにproductionPrefixを指定しておくのが無難です。こうしておかないとSharePointサイトにMaterial UIを使う複数のアプリをインストールした時にcreateStylesで作成したクラスの名前が被ります。(productionPrefixを指定しないと「jss + 連番」がクラス名になるため)

 

以上、参考になれば幸いです。