Skip to content

Support multiple BurgerEditor engines in one document - #933

Merged
YusukeHirao merged 6 commits into
devfrom
worktree-concurrent-purring-spark
Sep 9, 2026
Merged

Support multiple BurgerEditor engines in one document#933
YusukeHirao merged 6 commits into
devfrom
worktree-concurrent-purring-spark

Conversation

@YusukeHirao

Copy link
Copy Markdown
Member

背景

同一 HTML document 内に BurgerEditor エンジンを複数マウントすると、document スコープの固定 ID が衝突し、片方のエンジンの操作がもう片方に誤って作用していた。

  • コマンドバスの受信要素 ID が bge-command-bus 固定(core/src/command/command-bus.ts)。親 document 側のボタン(ブロックカタログ・下書き切替)の commandfor が先勝ちで別エンジンに配送される
  • EditorDialog<dialog id> / <form id>name 固定。command="close"<button form> が別エンジンのダイアログ・フォームに作用する
  • a11y 用のハードコード ID(BlockOptions の radiogroup、ImageEditor のフィールドセット・入力欄)が aria-* / htmlFor の参照先を混線させる

CSS / CSS カスタムプロパティの共有は対象外(意図的な仕様)。

変更内容

  • CommandBus にインスタンスごとに一意な receiverIdbge-command-bus-<n>)を追加。commandfor を出す全コンポーネントを engine.commandBus.receiverId 経由に変更。固定 ID の COMMAND_BUS_ID@deprecated として export は維持
  • BurgerEditorEngineOptions.rootHTMLElement を直接受け付けるように拡張(同一セレクタでの要素衝突を回避する逃げ道)。getElement() は null/undefined が渡った場合に ElementNotFoundError を投げるようにし、意味の掴めない TypeError を防ぐ
  • EditorDialog のダイアログ・フォーム ID を useId() ベースに変更(data-bge-component は種別フックとして name ベースのまま維持)
  • BlockOptions / ImageEditor のハードコード a11y ID を useId()
  • 複数エンジン間で共有される前提のままにした箇所(意図的な設計判断、JSDoc / ARCHITECTURE.md に明記):
    • <bge-wysiwyg-editor>classList / experimental.itemOptions.wysiwyg.enableTextOnlyMode(static プロパティ、document 単位)
    • Google Maps の script タグ注入(先勝ち)
    • ブロッククリップボード(sessionStoragestorageKey オプションで分離可能)

テスト

  • core: 同一 document に複数エンジンを生成し、receiverId の一意性・実クリックでの配送先分離・dispose 後の受信要素残存・root への HTMLElement 直接渡しを検証する新規テスト
  • core: getElement() の null/undefined ガード、非 HTMLElement 一致時のエラーを検証する新規テスト
  • client: EditorDialog の ID 一意性、commandfor/form の自己参照を検証する新規テスト
  • client: BlockMenuView / BlockMenu / DraftSwitcher / BlockCatalog(新規スペック追加)の各ボタンで commandfor が実際に engine.commandBus.receiverId を指すことを検証

影響範囲

@burger-editor/core / client / custom-element / blocks / storybook@burger-editor/local(1ページ=1ファイル前提の開発用 CMS)はスコープ外。exports の変更なし。

検証

  • yarn lint: エラー 0
  • NX_WORKSPACE_ROOT_PATH=<worktree> yarn build: 16 プロジェクト全成功
  • yarn test:unit(ホスト実行): 非 VR 全 1488 件通過。VR 24 件は変更前から存在する既知のホスト/Docker フォントレンダリング差異で失敗(CSS は無変更)
  • 本セッションの環境では Docker が利用できず、yarn test(Docker 経由の VR 込みフルスイート)は未実行。CI での確認が必要

Multiple BurgerEditorEngine instances can now share one document:
- CommandBus.receiverId gives each bus a unique receiver element id
  instead of the fixed COMMAND_BUS_ID (deprecated but kept exported)
- BurgerEditorEngineOptions.root accepts an HTMLElement directly, so two
  engines don't need to share (and collide on) one selector
- getElement() throws ElementNotFoundError instead of silently passing
  through null/undefined when the root reference resolves to nothing
Wire every commandfor and dialog/form id through the instance that owns
it, so two engines can share one document without their buttons and
dialogs cross-wiring:
- Components that hold an engine reference (BlockCatalog, DraftSwitcher,
  BlockMenu) address commandfor={engine.commandBus.receiverId} instead of
  the removed shared COMMAND_BUS_ID
- Presentational components without an engine reference (BlockMenuView,
  InitialInsertionButton) take a required commandBusId prop instead
- EditorDialog derives its dialog/form ids with useId() instead of the
  fixed `${name}-dialog` (data-bge-component keeps the name-based value,
  since it identifies the dialog's kind, not one specific instance)
- BlockOptions' radiogroup/description ids move to useId() for the same
  reason (previously fixed literals)

Adds tests asserting the commandfor attribute actually resolves to the
owning engine's receiverId, matching the wiring these components claim
to provide.
ImageEditor's tabpanel/fieldset/range-input/description ids move to
useId() instead of fixed literals, so two image item editors open at
once don't share htmlFor/aria-* references. Updates the pipeline spec
that located the item editor's form by a now-removed fixed id, using
HTMLInputElement.form instead.
@YusukeHirao
YusukeHirao merged commit 9577344 into dev Sep 9, 2026
1 check passed
@YusukeHirao
YusukeHirao deleted the worktree-concurrent-purring-spark branch September 9, 2026 03:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant