From 8e59844f885f3df4e409964b5df370e5a58c7f21 Mon Sep 17 00:00:00 2001 From: Aaron Schubert Date: Tue, 11 Aug 2026 13:26:56 +0100 Subject: [PATCH] Remove Action API from POS cart line-item action render target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pos.cart.line-item-details.action.render was the only *.action.render target whose API type included ActionApi. Action render targets are the modal destination shopify.action.presentModal() opens, so the surface was non-functional there (it pushed a duplicate modal of the same target). - extension-targets.ts: drop ActionApi from the target type (ActionTargetApi + CartApi + CartLineItemApi only) and document Navigation API usage. - ui-extensions-tester: retype the Group N factory to the target's new surface and add regression coverage asserting the Action API is not exposed on the render target (but still is on the menu-item sibling). - Regenerate targets.json + generated_docs_data_v2.json so target metadata and docs no longer advertise Action API support. - Changeset (major): versioned breaking change — removed for apiVersions 2026-07+, older pinned versions keep their published surface. Part of shop/issues-retail#33068 Assisted-By: devx/59d0a60c-7b60-4f69-bb24-197efae896ea --- ...os-remove-action-api-from-action-render.md | 10 ++++++ .../src/point-of-sale/factories.ts | 13 ++++--- .../tests/pos-action-render-targets.test.ts | 36 +++++++++++++++++++ .../point-of-sale/extension-targets.ts | 3 +- 4 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 .changeset/pos-remove-action-api-from-action-render.md create mode 100644 packages/ui-extensions-tester/src/tests/pos-action-render-targets.test.ts diff --git a/.changeset/pos-remove-action-api-from-action-render.md b/.changeset/pos-remove-action-api-from-action-render.md new file mode 100644 index 0000000000..e1dcab2f69 --- /dev/null +++ b/.changeset/pos-remove-action-api-from-action-render.md @@ -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. diff --git a/packages/ui-extensions-tester/src/point-of-sale/factories.ts b/packages/ui-extensions-tester/src/point-of-sale/factories.ts index 981223a93b..621d3871f7 100644 --- a/packages/ui-extensions-tester/src/point-of-sale/factories.ts +++ b/packages/ui-extensions-tester/src/point-of-sale/factories.ts @@ -406,13 +406,12 @@ function createStandardActionCartLineItemMock( }; } -// Group N: ActionTargetApi + ActionApi + CartApi + CartLineItemApi -function createActionTargetActionCartLineItemMock< - T extends RenderExtensionTarget, ->(target: T): ActionTargetApi & ActionApi & CartApi & CartLineItemApi { +// Group N: ActionTargetApi + CartApi + CartLineItemApi +function createActionTargetCartLineItemMock( + target: T, +): ActionTargetApi & CartApi & CartLineItemApi { return { ...createMockActionTargetApi(target), - ...createMockActionApi(), ...createMockCartApi(), ...createMockCartLineItemApi(), }; @@ -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, diff --git a/packages/ui-extensions-tester/src/tests/pos-action-render-targets.test.ts b/packages/ui-extensions-tester/src/tests/pos-action-render-targets.test.ts new file mode 100644 index 0000000000..333d743e04 --- /dev/null +++ b/packages/ui-extensions-tester/src/tests/pos-action-render-targets.test.ts @@ -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'); + }); +}); diff --git a/packages/ui-extensions/src/surfaces/point-of-sale/extension-targets.ts b/packages/ui-extensions/src/surfaces/point-of-sale/extension-targets.ts index a34fdea2ca..a51345cd1d 100644 --- a/packages/ui-extensions/src/surfaces/point-of-sale/extension-targets.ts +++ b/packages/ui-extensions/src/surfaces/point-of-sale/extension-targets.ts @@ -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