Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/pos-remove-action-api-from-action-render.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@shopify/ui-extensions': major
'@shopify/ui-extensions-tester': major
---

Remove the POS Action API (`shopify.action.presentModal()`) from action render targets (`*.action.render`).

Action render targets are themselves the modal destination that `presentModal()` opens, so exposing the Action API there had no meaningful destination — calling `shopify.action.presentModal()` from `pos.cart.line-item-details.action.render` (the one action render target that still exposed it) pushed another instance of the same target onto the navigation stack, rendering a duplicate modal on top of the already-open one.

Action render targets now expose only `ActionTargetApi` plus their contextual APIs, matching every other `*.action.render` target. Extensions that need in-workflow navigation should use the Navigation API (`shopify.navigation`). This is a versioned breaking change: the surface is removed for API versions `2026-10` and later; older API versions keep their published types.
13 changes: 6 additions & 7 deletions packages/ui-extensions-tester/src/point-of-sale/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,13 +406,12 @@ function createStandardActionCartLineItemMock<T extends RenderExtensionTarget>(
};
}

// Group N: ActionTargetApi + ActionApi + CartApi + CartLineItemApi
function createActionTargetActionCartLineItemMock<
T extends RenderExtensionTarget,
>(target: T): ActionTargetApi<T> & ActionApi & CartApi & CartLineItemApi {
// Group N: ActionTargetApi + CartApi + CartLineItemApi
function createActionTargetCartLineItemMock<T extends RenderExtensionTarget>(
target: T,
): ActionTargetApi<T> & CartApi & CartLineItemApi {
return {
...createMockActionTargetApi(target),
...createMockActionApi(),
...createMockCartApi(),
...createMockCartLineItemApi(),
};
Expand Down Expand Up @@ -564,9 +563,9 @@ const posMockFactories: PosMockFactory = {
'pos.cart.line-item-details.action.menu-item.render':
createStandardActionCartLineItemMock,

// Group N: ActionTargetApi + ActionApi + CartApi + CartLineItemApi
// Group N: ActionTargetApi + CartApi + CartLineItemApi
'pos.cart.line-item-details.action.render':
createActionTargetActionCartLineItemMock,
createActionTargetCartLineItemMock,

// Group O: Receipt targets
'pos.receipt-footer.block.render': createReceiptMock,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {createMockPosTargetApi} from '../point-of-sale/factories';

// Regression guard: action render targets are the modal destination that
// `shopify.action.presentModal()` opens, so they must not expose the Action
// API. The mock for the one-time exception should mirror the published type.
describe('pos.cart.line-item-details.action.render', () => {
it('does not expose the Action API (modal destination target)', () => {
const api = createMockPosTargetApi(
'pos.cart.line-item-details.action.render',
);

expect(api.action).toBeUndefined();
expect(api).not.toHaveProperty('action');
});

it('exposes the actionable surface (cart + cartLineItem + standard APIs)', () => {
const api = createMockPosTargetApi(
'pos.cart.line-item-details.action.render',
);

expect(api.cart).toBeDefined();
expect(api.cartLineItem).toBeDefined();
expect(api.extensionPoint).toBe('pos.cart.line-item-details.action.render');
});
});

describe('pos.cart.line-item-details.action.menu-item.render', () => {
it('still exposes the Action API (menu items launch the modal)', () => {
const api = createMockPosTargetApi(
'pos.cart.line-item-details.action.menu-item.render',
);

expect(api.action).toBeDefined();
expect(typeof api.action.presentModal).toBe('function');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -309,11 +309,10 @@ export interface RenderExtensionTargets {
/**
* Renders a full-screen modal interface launched from cart line item menu items. Use this target for complex line item workflows that require forms, multi-step processes, or detailed information displays beyond what a simple button can provide.
*
* Extensions at this target have access to detailed line item data through the Cart Line Item API and support workflows with multiple screens, navigation, and interactive components.
* Extensions at this target have access to detailed line item data through the Cart Line Item API and support workflows with multiple screens, navigation, and interactive components. Action render targets are themselves the modal destination, so the Action API (`shopify.action.presentModal()`) is not available here; use the Navigation API for in-workflow navigation.
*/
'pos.cart.line-item-details.action.render': RenderExtension<
ActionTargetApi<'pos.cart.line-item-details.action.render'> &
ActionApi &
CartApi &
CartLineItemApi,
BasicComponents
Expand Down
Loading