こんにちは、mashabow です。LINE で CRM を実現する Shopify アプリ「ソーシャルPLUS」の、フロントエンド開発を担当しています。
アプリに機能が増えてくると、埋め込みアプリ(マーチャントの方に触っていただく設定画面)にメニューをつけたくなりますよね? 項目をクリックしたら画面が切り替わる、タブのようなものです。

今回は、このメニューを埋め込みアプリにつける方法をご紹介します。
実装方法は3種類
結論から言うと、B. の NavigatonMenu
を使う方法がおすすめですが、A、B、C とそれぞれ順番に説明していきます。
A. 埋め込みアプリの設定画面からメニューを設定する
設定方法
参考リンク
Shopify Partners から、アプリ設定 > (自分のアプリ名) > アプリ設定 のページを開いて、「埋め込み式アプリ」の項目の [管理する] をクリックします。

埋め込み式アプリの設定ページが開くので、メニューの [設定する] をクリック。

すると、メニューバーの設定ページが開くので、ここで設定していきます。

ストアの管理画面からアプリを開いてみると、アプリ名の下にメニューが表示されていることがわかります。(↓の画像)
なお、ここで設定したメニューは、埋め込みアプリ側(iframe の中)ではなく、Shopify Admin 側(iframe の外)に表示されることに注意してください。埋め込みアプリの内容を下にスクロールしても、メニューの位置は固定されたままになります。

日本語メニューが追加できない問題
…が、困ったことに、この画面からは日本語のメニュー項目は追加することができません。

Shopify のフォーラムでも、この件で困っている方を見かけました。
埋め込みアプリのメニュー項目名の設定を日本語でできない。 – Shopify Community
追加できないといっても、どうやらクライアントサイドのバリデーションで引っかかっているだけのようです。ということで、[保存] ボタンをクリックしたときのリクエストを元に、自分で API を叩けば保存することができます。
res = await fetch('https://partners.shopify.com/1111111/api/graphql', {
method: 'POST',
headers: {
'content-type': 'application/json',
'X-CSRF-Token': '...',
},
body: JSON.stringify({
operationName: 'EditExtension',
variables: {
input: {
type: 'APP_NAV_ITEM',
appId: '2222222',
context: 'primary_navigation',
config: JSON.stringify({
children: [
// ここにメニューが入る
{ label: 'ほげほげ', path: 'hogehoge' },
{ label: 'ふがふが', path: 'fugafuga' },
{ label: 'ぴよぴよ', path: 'piyopiyo' },
],
}),
id: '3333333',
},
},
query: 'mutation EditExtension($input: UpdateExtensionInput!) {\n extensionUpdate(input: $input) {\n extension {\n id\n __typename\n }\n userErrors {\n field\n message\n __typename\n }\n __typename\n }\n}\n',
}),
});
実行後、ページをリロードするとこんな感じになります。

ストアの管理画面からアプリを開いてみても正しく表示されています。

この方法の問題点
設定自体はかんたんにできるのですが、以下の問題点が気になります。
- 動的にメニューを変更する方法がない。したがって、多言語化もできない
- アプリの設定画面から手動で設定する必要があるので、コード管理ができない
- hack しないと日本語でメニューを設定できない
B. App Bridge の NavigationMenu を使う
App Bridge の NavigationMenu
を使うと、埋め込みアプリから動的にメニューを設定することができます 。
以下は、Next.js 製の埋め込みアプリからメニューを設定するカスタムフックの例です。
import { useAppBridge } from '@shopify/app-bridge-react';
import { NavigationMenu, AppLink } from '@shopify/app-bridge/actions';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
export const useNavigationMenu = (): void => {
const app = useAppBridge();
const router = useRouter();
useEffect(() => {
const items = [
{ label: 'ほげほげ', destination: 'hogehoge' },
{ label: 'ふがふが', destination: 'fugafuga' },
{ label: 'ぴよぴよ', destination: 'piyopiyo' },
].map((item) => AppLink.create(app, item));
// 複数回 create() しても問題なく動く様子
NavigationMenu.create(app, {
items,
// 現在のページのパスに完全一致する項目を active にする
// このあたりのロジックはお好みで
active: items.find(({ destination }) => destination === router.pathname),
});
}, [app, router.pathname]);
};
表示の位置やスタイル、クリック時の挙動は、A. で設定したメニューとまったく同じです。

もし、A. のメニューが設定済みの場合、この NavigationMenu
で上書きされます。items
を空配列 []
にすると、メニューの領域は確保されず、何も表示されません。
というわけで、この NavigationMenu
を使うと、A. の問題点がすべて解決することになります!
C. 自作のメニューを埋め込みアプリ内に追加する
メニューのコンポーネントを自作し、埋め込みアプリ内(iframe の中)に表示させる方法です。ちなみに、B. の方法を知らなかった開発段階では、Polaris の Tabs
コンポーネント を使ってメニューを自作していました。

しかし Polaris の Tabs
コンポーネントの問題なのか、たまにレイアウトが崩れたり、Cannot read property 'panelID' of undefined
というエラーでクラッシュしたりと、不安定な挙動に悩まされていました。

ちなみに Polaris の Tabs
コンポーネントは、B. の NavigationMenu
(下図)と比較すると、若干スタイルが異なります。

自作メニューによる実装は、他のアプリでもよく見かけます。B. の NavigationMenu
の知名度がいまいち低いのが、要因なのかもしれません。
まとめ
以上、埋め込みアプリのメニューについて説明しました。これから開発される方には B. の NavigationMenu
がおすすめです。
ちなみに App Bridge には、NavigationMenu
以外にもいろいろな機能があります。9月に参加させていただいた「Shopify 開発者向け配信」の中でもご紹介しましたので、アプリ開発に興味がある方はぜひご覧ください!