S-modal not opening when triggered from s-menu > s-button (Shopify App Bridge / UI Extensions)

Hi @shopify
I’m running into an issue where an s-modal does not open when the trigger button is placed inside an s-menu.

Here is the component structure:

<s-button icon="caret-down" commandFor="actions-menu">
  {isExportingStoreCreditReport ? <s-spinner size="small" /> : t("Single.Actions")}
</s-button>

<s-menu id="actions-menu" accessibilityLabel="Product actions menu">
  {actionDropDown().map((item, index) => (
    <s-button
      key={index}
      icon={item.icon}
      commandFor={item.commandFor}
      onClick={item.onAction}
    >
      {item.content}
    </s-button>
  ))}
</s-menu>

My dropdown items are generated like this:

{
  content: isExporting ? <s-spinner size="small" /> : t("Customers.ExportFile"),
  icon: isExporting ? "" : "export",
  commandFor: "export-2",
  onAction: () => { setExportindex() },
},

And the modal:

const ExportModal = ({ index }) => (
  <s-modal id={`export-${index}`} heading="Details">
    <s-paragraph>Displaying more details here.</s-paragraph>

    <s-button slot="secondary-actions" commandFor={`export-${index}`} command="--hide">
      Close
    </s-button>

    <s-button slot="primary-action" variant="primary" commandFor={`export-${index}`} command="--hide">
      Save
    </s-button>
  </s-modal>
);

Issue:

When I click the button inside the s-menu, s-modal does not open.
However, if I place the same button outside the s-menu, the modal opens normally.

Question:

  • Is there a known limitation where s-button inside s-menu cannot trigger a modal using commandFor?

  • Do I need a different approach (e.g., manually calling UI modal APIs)?

  • How can I correctly open an s-modal from an item inside an s-menu?

Any help or guidance is greatly appreciated!

1 Like

Hi @webplanex_info

I believe s-menu supports s-button children with command/commandFor, and those buttons can open s-modal. You rely on default command (“--auto”) plus a state change in onClick. The menu closes and the item re-renders before the command fires, so the modal never receives it.

You’ll want to make the menu item purely declarative by setting an explicit command and avoiding state changes in that click. It should look something like:

<s-button commandFor={`export-${index}`} command="--show">

{isExporting ? <s-spinner size="small" /> : t("Customers.ExportFile")}

</s-button>

@Liam-Shopify Hello, I am doing exactly as you said, but it still isn’t working. Is there any way to solve this?

Here is my buttons

<>
  <s-button
    icon="delete"
    variant="tertiary"
    accessibilityLabel="Delete product"
    commandFor="customer-menu"
  />
  <s-menu
    id="customer-menu"
    accessibilityLabel="Customer actions"
  >
    <s-button
      icon="delete"
      variant="tertiary"
      disabled={isDeletePending}
      loading={isDeletePending}
      commandFor="delete-product-confirmation"
      command="--show"
    >
      Delete
    </s-button>
  </s-menu>
  <ModalProductDeleteConfirm
    modalId="delete-product-confirmation"
    heading="Delete Product Confirmation"
    message="Are you sure you want to delete this product? This action cannot be undone."
    onDelete={handleDeleteDefault}
    onShopifyDelete={handleSetDeleteOnShopify}
  />
</>

Here is my modal.tsx

export default function ModalProductDeleteConfirm({
  modalId,
  heading,
  message,
  onDelete,
  onShopifyDelete,
}: {
  modalId: string;
  heading: string;
  message: string;
  onDelete?: () => void;
  onShopifyDelete?: () => void;
}) {
  return (
    <s-modal id={modalId} heading={heading}>
      <s-paragraph>{message}</s-paragraph>

      <s-button
        slot="secondary-actions"
        commandFor={modalId}
        command="--hide"
        onClick={onShopifyDelete}
      >
        Delete On Shopify also
      </s-button>
      <s-button
        slot="primary-action"
        variant="primary"
        commandFor={modalId}
        command="--hide"
        onClick={onDelete}
      >
        Delete
      </s-button>
    </s-modal>
  );
}

Thanks for help?