WordPress 6.2からブロックエディタの中身が壊れる件

How to use the MUI DataGrid in Block Editor?
Why CheckBox dose not work?

ブロックエディタにMUIのDataGridを配置してました。
以前は表示されてたのに、いつの間にか表示が崩れてしまってます。

機能はしているのでおそらくスタイルの問題かとおもって調べていると、
見つかりました、犯人・・・。

iframe・・・

どうやらWP 6.2からブロックエディタがiframe内に配置されるようになったようです。

試しにトップレベルにあるスタイルシートをiframeのヘッダにぶっこんでみると

const css = [...document.styleSheets].map(ss => [...ss.cssRules]).flat().map(r => r.cssText).join("\n")

(() => {
    const win = jQuery('iframe').get(0).contentWindow;
    const elm = win.document.createElement('style');
    elm.textContent = css;
    win.document.head.appendChild(elm)
})()

ちゃんと表示されるので原因は特定です。

えーと、何てことやってくれてんねん・・・(心の声)

問題は、これ、どうやって解決する・・・?

DataGridで探すから情報少なくなるのでMUIとiframeでググってようやく発見。
ちなみにMUIはスタイルシートにemotionを使っていて、そっちのほうで解決できるらしい。

https://github.com/mui/material-ui/issues/30675

なーるほど。

import React, { useState } from "react"
import Box from '@mui/material/Box';
import { Spinner } from "@wordpress/components";
import { Suspense, useMemo } from "@wordpress/element";
import { createHigherOrderComponent } from "@wordpress/compose";
import { CacheProvider } from "@emotion/react";
import createCache from "@emotion/cache";

const initEmotionCache = () =>
{
    let cache: any = null;

    const getCache = () => {
        const iframe = window?.document?.getElementsByTagName('iframe')?.[0];
        const head = iframe?.contentWindow?.document?.head

        if(head)
        {
            return createCache({
                key: 'css',
                container: head,
                prepend: true
            })          
        }
    }

    return {
        get: () => {
            return cache || (cache = getCache());
        }
    }
}
const defaultEmotionCache = initEmotionCache();

const createComponentLoader = (callback) =>
{
    let components: any = null;
    const promise = (async () => { components = await callback() })();

    return {
        get: () => 
        {
            if(components)
            {
                return components;
            }
            throw promise;
        }
    }
}
const dataGridLoader = createComponentLoader(() => import("@mui/x-data-grid"));

const withSuspense = createHigherOrderComponent(Edit => props =>
{
    const cache = defaultEmotionCache.get();

    return (
        <CacheProvider value={cache}>
            <Suspense fallback={ <Spinner /> }>
                <Edit {...props} />
            </Suspense>
        </CacheProvider>
    )
}, '');

export default withSuspense(({  }) =>
{

        const { DataGrid } = dataGridLoader.get();

        const [ model, setModel ] = useState({ page: 0, pageSize: 10 });
        const options = [10, 20, 30, 50, 100];

        const { items, itemsCount } = useServerData( model.page, model.pageSize );

        const columns = [...Object.keys(items?.[0] ?? {})].map(k => ({ field: k, headerName: k}))

        console.log(items)

        return (
            <Box sx={{ height: 400, width: "100%" }}>
                <DataGrid
                    paginationMode="server"
                    paginationModel={model}
                    onPaginationModelChange={setModel}
                    pageSizeOptions={options}

                    columns={columns}
                    rows={items}
                    rowCount={itemsCount}

                    />           
            </Box>
        )

});

const useServerData = ( page, length ) =>
{

    const items = useMemo(() => {
        const v = [...Array(100)].map((v, k) => ({ id: k + 1, name: `no${k + 1}`, message: `Hello! I am No ${k + 1}!` }))
        return v;
    }, [])

    return { items: items.slice(page * length, page * length + length), itemsCount: items.length };
}

1.シングルトンでインラインフレームを取得。
2.そのwindowからdocument.headを取得。
3.それを使ってcreateCacheでキャッシュを作成
4.そのキャッシュをCacheProviderに渡す

とりあえずは解決。

と思ったら早速問題発生!

ブロックエディタに配置したMUI DataGridのチェックボックスが効かない!

クリックしても全く反応なし。
最初はMUI側のバグだと思ってました。

チェックボックスをクリックしてもチェックされない。
あっちこっち乱れうちでチェックボックスをクリックするとたまたまチェックされる瞬間がありました。
どうやら一部の範囲に限ってイベントが有効なようです。

DOMを見るとチェックボックスが実際の見た目より小さいです。
そこでチェックボックスのスタイルを見てみます。

チェックボックスの縦幅、横幅ともに1remに設定してある。
チェックボックスのクラス設定ではそれぞれ100%に設定されているがかき消されている。

これが原因だろう・・・。
では1remはどこで設定してあるのか・・・。

/wp-admin/css/forms.css

ということでWordPressレベルでチェックボックスの幅が設定してありました。
DOMの構造を見るとforms.cssのほうが先に読み込まれている。
クラスと属性セレクタ優先順位は同と書いてあったので読み込みの順番を変えてみよう。

emotionのキャッシュでは順番を変更できるオプションがあったのを思い出した。

return createCache({
    key: 'css',
    container: head,
    prepend: false
})  

prependをfalseにしてみる。

再びDOMを見ると読み込み準は反転できた。

しかしなにもおこらなかった・・・。

ん・・・。
有無を言わず属性セレクタのほうが優先されとる・・・orz

仕方なく属性セレクタで幅を上書き。

    return (
        <Box sx={{ height: 400, width: '100%' }}>
            <style>
                {`
                    input[type="checkbox"]
                    {
                        width: 100%;
                        height: 100%;
                    }
                `}
            </style>
            <DataGrid
                pageSizeOptions={pageSizeOptions}
                columns={columns2}
                rows={items}
                rowCount={totalItems}
                loading={isLoading}

                checkboxSelection
                disableRowSelectionOnClick

                rowSelectionModel={selectionModel}
                onRowSelectionModelChange={setSelectionModel}

                slots={{
                    toolbar: GridToolbar
                }}
            />
        </Box>
    )

とりあえずクリックできるようになったものの、
今後WP本体のCSSのせいで結構邪魔されそう・・・。

BlockEditor certificate css DataGrid Docker Gutenberg Hyper-V iframe MUI openssl PHP React ReduxToolkit REST ubuntu WordPress オレオレ認証局 フレームワーク