🚀

ノンデスクワーカー向けプロジェクト管理 SaaS の Material-UI を v4 から v5 にアップデートした話

2023/07/27に公開

こんにちは!
KANNA の開発のお手伝いをしております、フリーランスエンジニアの len_prog です。

アルダグラムは、ノンデスクワーカー向けプロジェクト管理 SaaS「KANNA」を提供しています。
https://lp.kanna4u.com

KANNA の Web フロントエンドは、UI ライブラリとして Material-UI を使用しています。

今回、2ヶ月ほどの期間で KANNA の Material-UI を v4 から v5 にアップデートしましたので、事前にやっておくと良いことや、アップデートで注意すべき点についてご紹介します。

事前にやっておくと良いこと

公式のマイグレーションガイドを読み込む

Material-UI のマイグレーションガイドは丁寧に書かれており、基本的にはこちらの記載どおりに進めれば大丈夫なようになっておりますので、最初に一通り目を通すことを強く推奨します。

Visual Regression Test を用意する

KANNA の Web フロントエンドでは、ページ全体や各種コンポーネントのカタログを Storybook に用意してあり、CI 上で reg-suitを使用した Visual Regression Test が動くようになっております。

今回は UI ライブラリのメジャーアップデートだったこともあり、非常に大きく見た目が崩れたのですが、どこがどう崩れているかを把握するのに Visual Regression Test が非常に役立ちました。
恐らくこれが無ければ追加で1ヶ月は時間が掛かっていたのではと思うほど、アップデート工数の削減に寄与してくれました。

Visual Regression Test の様子

アップデートで注意するべき点

基本的には Material-UI v5 へのマイグレーションガイドに必要な情報は載っていますが、一部ドキュメンテーションされていない破壊的変更が存在します。
以下に、私がマイグレーションをしていた際に引っかかった点について記載します。

Fade コンポーネントの children を div など fragment 以外に変更する必要がある

v4までは、Fade コンポーネント直下の要素が fragment でも動いていましたが、2023年7月18日現在、v5では、divなど fragment 以外の要素に置き換えないとランタイムエラー(Cannot read property scrollTop of null)が発生するバグが存在します。
(関連するissue: https://github.com/mui/material-ui/issues/27154#issuecomment-1117386458)

そのため、Material-UI 側で上記のバグが修正されるまでは、以下のように、Fade コンポーネントの children の最上位要素には div などを使用することをおすすめします。

diff --git a/src/Timeline.tsx b/src/Timeline.tsx
index a875e6057..616415315 100644
+++ b/src/Timeline.tsx
--- a/src/Timeline.tsx
@@ -207,7 +207,7 @@ const Timeline: FC<Timeline> = ({
   return (
     <div className={classes.container}>
       <Fade in={!shouldScrollToBar} timeout={400}>
-        <div>
+        <>
           <Button
             ref={timelineBarRef}
             key={`timeline_bar_observe_${observeCount}`}
@@ -335,7 +335,7 @@ const Timeline: FC<Timeline> = ({
               </IconButton>
             </Zoom>
           </Box>
-        </div>
+        </>
       </Fade>
       <>
         <Popover

InputLabel Component で shrink=true を指定した要素のラベル位置がズレることがある

v4 では shrink=true を指定した場合、入力欄の真上にラベルが配置されていましたが、現状、v5では位置が大きくズレてしまうことがあります。


Material-UI v4


Material-UI v5

こうなってしまった場合、以下のように transform で位置を調整することで、v4と同じ表示に戻すことができます。

<InputLabel className={classes.searchTypeLabel} shrink={true}>
  フリーワード検索
</InputLabel>

const useStyles = makeStyles<StyleProps>()((theme) => {
  return {
    searchTypeLabel: {
      transform: 'translate(0, 1.5px) scale(0.75)'
    }
  }
}

sx props で px 単位で大きさなどを指定する場合には、明示的に単位を書く

v4 で Material-UI のコンポーネントに sx props で width や margin などを px 指定する際には、単位を省略した場合に暗黙的に px として値が解釈されていました。

しかし、v5 では同じ指定の仕方をしてしまうと入力値の10倍の大きさになってしまいます。
そのため、基本的には px など単位を明示的に指定することを推奨します。

なお、theme の設定を弄れば元に戻せそうではあったのですが、明示的に単位を指定した方が将来の v6 へのマイグレーション時にデグレが起きづらいはずと思い、KANNA ではあえて theme を弄らずに明示的に単位を指定しています。

// Material-UI v4
<Box sx={{ width: 10 }}>[v4:単位指定なし]width: 10pxで表示されます</Box>

// Material-UI v5
<Box sx={{ width: 10 }}>[v5:単位指定なし]width: 100pxで表示されます</Box>
<Box sx={{ width: '10px' }}>[v5:単位指定あり]width: 10pxで表示されます</Box>

Tooltip Component のマウスホバー時の挙動を v4 と揃えるには、disableInteractive prop を渡す必要がある

disableInteractive prop を渡さないと、ホバーが外れたときにすぐにTooltip が消えず、UX が悪くなってしまう可能性があります。
そのため、基本的には渡すことを推奨します。(詳しくはドキュメントをご覧ください)

  • disableInteractive prop を渡さない場合(ホバーが外れたときにすぐに Tooltip が閉じない)

  • disableInteractive prop を渡した場合(v4 と同じく、ホバーが外れたときにすぐに Tooltip が閉じる)

DatePicker の value の型が不安定なので、常に DayJs オブジェクトか null が返るようにハンドリングする必要がある

KANNA では日付を扱うライブラリとして DayJs を使用しており、DatePicker と DayJs を一緒に使うために AdapterDayjs を使用しています。
しかし、LocalizationProvider の dateAdapter に AdapterDayjs を渡しても、なぜか value が文字列で返ってくることがあり、そのまま動かそうとすると空文字列に対して isUTC メソッド(DayJs オブジェクトが持っているもの)を呼ぼうとして実行時エラーになります。

そのため、以下のように必ず DayJs のオブジェクトか null が返るようにハンドリングをする必要があります。

import { LocalizationProvider } from '@mui/x-date-pickers'
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
<>
  <DatePicker
    value={
      dayjs.isDayjs(value) || value === null // 変換不要
      ? value
      : value === undefined || value === '' // フォームの初期表示時
      ? dayjs()
      : dayjs(value) // 文字列が入っている場合
    }
  >
  </DatePicker>
</>

レスポンシブデザインをする際のサイズ指定(theme.breakpoints.down)において、v4より1段階大きなブレークポイントを指定する必要がある

例えば、いままで theme.breakpoints.down('sm')で指定していた箇所は theme.breakpoints.down('md')にする必要があります。

これは一応ドキュメントにも載っている話ではあるのですが、少々探しにくい位置にあったので、導線としてリンクを貼っておきます。詳しくは以下のドキュメントをご覧ください。
https://mui.com/material-ui/migration/v5-component-changes/#✅-breakpoint-behavior

まとめ

この記事では、Material-UI を v4 から v5 にアップデートする際にやっておくと良いことや、注意点について紹介しました。
プロダクトの進化のスピードを遅くしないためには定期的なライブラリのアップデートが必要なので、今後も定期的に取り組んでいきます!

GitHubで編集を提案
アルダグラム Tech Blog

Discussion