Skip to content
Draft
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
7 changes: 7 additions & 0 deletions .changeset/pos-component-contract-docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@shopify/ui-extensions': patch
---

Correct POS date and time component callback types to accept null, preserve inherited component declarations during generation, and update component contract documentation and examples.

Align POS API descriptions with version-specific cart mutation outcomes, storage errors, session and localization behavior, hardware requests, and navigation.
8 changes: 7 additions & 1 deletion packages/ui-extensions/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ export interface I18nTranslate {
/**
* Returns a translated string matching a key in a locale file. Use this to display localized text in your extension based on the merchant's language preferences. Supports interpolation with replacement values and pluralization with the `count` option. Returns a string when replacements are primitives, or an array when replacements include UI components.
*
* In POS extensions, translation lookup doesn't expose a separate success or error result. A missing key, a missing required plural form, or a missing pluralization count can produce diagnostic text as the return value, along with a console warning. A missing interpolation value leaves the unresolved placeholder in the returned text and emits a warning. Check the development console and test each locale, plural category, and interpolation key before displaying translated content. Don't rely on diagnostic message wording as a stable API contract.
*
* In POS extensions, pass the `count` option as a number for a pluralized translation key. Zero is a valid count. A numeric string doesn't select a plural form, and omitting the count or passing `undefined` produces a missing-count diagnostic for a pluralized key.
*
* @param key - The translation key from your locale file (for example, "banner.title")
* @param options - Optional replacement values for interpolation or the special `count` property for pluralization
*
Expand Down Expand Up @@ -38,7 +42,9 @@ export interface I18n {
/**
* Returns a localized currency value formatted according to the user's locale and currency conventions. Use this to display prices, totals, or financial amounts in the appropriate format for the merchant's region. This function behaves like the standard `Intl.NumberFormat()` with a style of `currency` applied. Uses the current user's locale by default.
*
* @param number - The currency amount to format
* In POS extensions, pass an amount in major currency units and provide `options.currency` as an [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code. For example, `formatCurrency(10, {currency: 'CAD'})` formats ten Canadian dollars. The function doesn't convert currencies or divide an integer minor-unit amount. Omitting the currency while using the currency style throws synchronously. Formatting errors from invalid Intl options are synchronous exceptions, not rejected promises.
*
* @param number - The currency amount to format, in major currency units
* @param options.inExtensionLocale - If true, use the extension's default locale instead of the user's locale
* @param options - Additional Intl.NumberFormatOptions for customizing the currency format, such as the currency code
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/ui-extensions/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,8 @@ export interface ReadonlySignalLike<T> {
readonly value: T;
/**
* Subscribes to value changes and calls the provided function whenever the value updates. Returns an unsubscribe function to clean up the subscription. Use to automatically react to changes in the signal's value.
*
* Read `value` when you need the initial snapshot. A subscription callback can also receive the current value during subscription setup, particularly when you use the `@shopify/ui-extensions/preact` integration, so a callback firing isn't proof that a new host event occurred. Save the returned unsubscribe function and call it during component cleanup.
*/
subscribe(fn: (value: T) => void): () => void;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
export interface ActionApiContent {
/**
* Presents the corresponding action (modal) target on top of the current view as a full-screen modal. For example, calling this method from `pos.purchase.post.action.menu-item.render` presents `pos.purchase.post.action.render`. Use to launch detailed workflows, complex forms, or multi-step processes that require more screen space than simple components provide.
* Presents the corresponding action (modal) target on top of the current view as a full-screen modal. The companion modal target must be registered in the same extension. Calling this method from a menu item or block target presents the companion `.action.render` target for that target group; for example, calling it from `pos.purchase.post.action.menu-item.render` presents `pos.purchase.post.action.render`. Calling it from a tile target presents the companion `.modal.render` target; for example, calling it from `pos.home.tile.render` presents `pos.home.modal.render`. Use to launch detailed workflows, complex forms, or multi-step processes that require more screen space than simple components provide.
*/
presentModal(): void;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ export interface CameraMediaOptions {
*/
facingMode?: 'user' | 'environment';
/**
* The maximum width (1 to 1920) of the image in pixels. Resizes the image to this width if it is larger.
* The requested maximum width of the captured image in pixels. Resizes the image to this width if it's larger. Values below 1 are clamped to 1, and values above 1920 are clamped to 1920 before capture. The `width` on the returned `CameraMediaResponse` is the actual captured image width.
* @defaultValue 1080
*/
maxWidth?: number;
/**
* The maximum height (1 to 1920) of the image in pixels. Resizes the image to this height if it is larger.
* The requested maximum height of the captured image in pixels. Resizes the image to this height if it's larger. Values below 1 are clamped to 1, and values above 1920 are clamped to 1920 before capture. The `height` on the returned `CameraMediaResponse` is the actual captured image height.
* @defaultValue 1080
*/
maxHeight?: number;
/**
* The quality of the image returned.
* Percentile value between 0 (lowest quality/highest compression) and 1 (highest quality/lowest compression).
* A value between 0 (lowest quality, highest compression) and 1 (highest quality, lowest compression). `takePhoto` rejects values outside this range.
* @defaultValue 0.9
*/
quality?: number;
Expand Down Expand Up @@ -59,6 +59,7 @@ export interface CameraApiContent {
* @throws {Error} when the user cancels the photo capture
* @throws {Error} when the device has no available camera
* @throws {Error} when the camera API is already in use
* @throws {Error} when `options` are invalid, such as a `quality` outside the 0 to 1 range
*/
takePhoto: (options?: CameraMediaOptions) => Promise<CameraMediaResponse>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,16 @@ export type LineItemDiscountType = 'Percentage' | 'FixedAmount';
export interface ReadonlyCartApiContent {
/**
* Provides read-only access to the current cart state and allows subscribing to cart changes. The `value` property provides the current cart state, and `subscribe` allows listening to changes with improved performance and memory management.
*
* While POS is in a return, exchange, or refund flow, this signal exposes an empty, non-editable cart (`editable: false`). The lines being returned or exchanged aren't exposed through this signal.
*/
current: ReadonlySignalLike<Cart>;
}

/**
* Cart and line item write operations.
*
* Each mutation returns a promise that resolves once POS has processed the operation. On every API version, a mutation rejects the promise when the cart isn't editable (during a return, exchange, or refund flow) or when the underlying POS operation fails. On API version 2026-10 and later, most mutations also reject when POS refuses the write or can't confirm that it succeeded; on earlier API versions, the promise can resolve in that case. Some methods resolve without confirming the write on every API version: `removeCartDiscount`, `clearCart`, `addCartProperties`, `removeCartProperties`, `setAttributedStaff`, and `setAttributedStaffToLineItem`. After a mutation, read `shopify.cart.current` to confirm the cart state changed.
* @publicDocs
*/
export interface MutableCartApiContent {
Expand All @@ -65,11 +69,13 @@ export interface MutableCartApiContent {
bulkCartUpdate(cartState: CartUpdateInput): Promise<Cart>;

/**
* Apply a cart-level discount with the specified type (`'Percentage'`, `'FixedAmount'`, or `'Code'`), title, and optional amount. For discount codes, omit the `amount` parameter. Enhanced validation ensures proper discount application.
* Apply a cart-level discount with the specified type (`'Percentage'`, `'FixedAmount'`, or `'Code'`), title, and optional amount.
*
* For type `'Code'`, pass the discount code itself as `title` (it must be non-empty) and omit `amount`. For validated code redemption with availability checks, use `addCartCodeDiscount` instead. For `'Percentage'` and `'FixedAmount'`, `amount` is required; omitting it submits a zero-value discount.
*
* @param type the type of discount applied (example: 'Percentage')
* @param title the title attributed with the discount
* @param amount the percentage or fixed monetary amount deducted with the discount. Pass in `undefined` if using discount codes.
* @param title the title attributed with the discount. For type `'Code'`, the discount code to redeem
* @param amount the percentage or fixed monetary amount deducted with the discount. Required for `'Percentage'` and `'FixedAmount'`; pass `undefined` for discount codes
*/
applyCartDiscount(
type: CartDiscountType,
Expand All @@ -78,7 +84,9 @@ export interface MutableCartApiContent {
): Promise<void>;

/**
* Apply a discount code to the cart. The system will validate the code and apply the appropriate discount if the code is valid and applicable to the current cart contents with improved error messaging.
* Apply a discount code to the cart. POS validates the code against the shop's discount rules and applies the appropriate discount if the code is valid and applicable to the current cart contents.
*
* On API version 2026-10 and later, the returned promise rejects if the code is invalid or can't be applied. On earlier API versions, an invalid or inapplicable code can fail without rejecting the promise; check `shopify.cart.current` to confirm the discount was applied.
*
* @param code the code for the discount to add to the cart
*/
Expand Down Expand Up @@ -122,14 +130,14 @@ export interface MutableCartApiContent {
addCustomSale(customSale: CustomSale): Promise<string>;

/**
* Add a product variant to the cart by its numeric `ID` with the specified quantity. Returns the `UUID` of the newly added line item, or an empty string if the user dismissed an oversell guard modal. Throws an error if POS fails to add the line item due to validation or system errors.
* Add a product variant to the cart by its numeric `ID` with the specified quantity. Returns the `UUID` of the new line item, or the `UUID` of the existing line item if POS merged the quantity into a matching line. Throws an error if the variant can't be found or POS fails to add the line item.
*
* Pass `options` to attach line-item properties in the same operation, instead of following up with a separate `addLineItemProperties` call.
* Pass `options` to attach line-item properties in the same operation, instead of following up with a separate `addLineItemProperties` call. The `'RFID Tag'` property key is reserved and isn't applied to the line item.
*
* @param variantId the product variant's numeric ID to add to the cart
* @param quantity the number of this variant to add to the cart
* @param options optional line-item properties to apply to the new line item in the same operation
* @returns {string} the UUID of the line item added, or the empty string if the user dismissed an oversell guard modal
* @returns {string} the UUID of the line item added or merged into
* @throws {Error} if POS fails to add the line item
*/
addLineItem(
Expand Down Expand Up @@ -176,7 +184,7 @@ export interface MutableCartApiContent {
/**
* Add custom properties to a specific line item using its `UUID`. Properties are merged with existing line item properties for metadata storage and tracking with enhanced validation.
*
* @param uuid the uuid of the line item to which the properties should be stringd
* @param uuid the uuid of the line item to which the properties should be added
* @param properties the custom key to value object to attribute to the line item
*/
addLineItemProperties(
Expand Down Expand Up @@ -204,10 +212,12 @@ export interface MutableCartApiContent {
/**
* Apply a discount to a specific line item using its `UUID`. Specify the discount type (`'Percentage'` or `'FixedAmount'`), title, and amount value with improved discount allocation tracking. `FixedAmount` discounts use per-unit amounts. For example, passing `'5.00'` on a line item with quantity 2 results in a $10.00 total discount.
*
* On API versions earlier than 2026-07, `FixedAmount` amounts apply to the whole line instead: POS divides the total across the line's quantity, which can introduce a one-cent rounding difference (for example, `'2.00'` on a quantity of 3 becomes `'0.67'` per unit).
*
* @param uuid the uuid of the line item that should receive a discount
* @param type the type of discount applied (example: 'Percentage')
* @param title the title attributed with the discount
* @param amount the percentage or fixed monetary amount deducted with the discout
* @param amount the percentage or fixed monetary amount deducted with the discount
*/
setLineItemDiscount(
uuid: string,
Expand All @@ -219,14 +229,16 @@ export interface MutableCartApiContent {
/**
* Apply discounts to multiple line items simultaneously. Each input specifies the line item `UUID` and discount details for efficient bulk discount operations with enhanced validation and allocation tracking. `FixedAmount` discounts use per-unit amounts. For example, passing `'5.00'` on a line item with quantity 2 results in a $10.00 total discount.
*
* @param lineItemDiscounts a map of discounts to add. They key is the uuid of the line item you want to add the discount to. The value is the discount input.
* On API versions earlier than 2026-07, `FixedAmount` amounts apply to the whole line instead: POS divides the total across the line's quantity, which can introduce a one-cent rounding difference (for example, `'2.00'` on a quantity of 3 becomes `'0.67'` per unit).
*
* @param lineItemDiscounts an array of discount inputs to apply. Each entry pairs the target line item's `UUID` (`lineItemUuid`) with the discount to apply to it (`lineItemDiscount`).
*/
bulkSetLineItemDiscounts(
lineItemDiscounts: SetLineItemDiscountInput[],
): Promise<void>;

/**
* Set the attributed staff member for all line items in the cart using the staff `ID`. Pass `undefined` to clear staff attribution from all line items with enhanced staff validation and tracking.
* Set the attributed staff member for all line items in the cart using the staff `ID`. Gift card line items are excluded from staff attribution and keep their current attribution. Pass `undefined` to clear staff attribution from all line items.
*
* @param staffId the ID of the staff. Providing undefined will clear the attributed staff from all line items.
*/
Expand Down Expand Up @@ -272,10 +284,9 @@ export interface MutableCartApiContent {
updateDefaultAddress(addressId: number): Promise<void>;

/**
* Add a selling plan to a line item in the cart using the line item `UUID`, selling plan `ID`, and selling plan name. Optionally provide delivery interval and interval count for improved performance, otherwise POS will fetch them after syncing the cart.
* Add a selling plan to a line item in the cart.
*
* @param uuid the uuid of the line item that should receive the selling plan
* @param sellingPlanId the ID of the selling plan to add to the line item
* @param input the selling plan assignment, containing the target line item's `UUID` (`lineItemUuid`), the selling plan `ID` (`sellingPlanId`), and an optional display name (`sellingPlanName`)
*/
addLineItemSellingPlan(input: SetLineItemSellingPlanInput): Promise<void>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export interface CashDrawerApiContent {
/**
* Opens the connected cash drawer device. The drawer will automatically open if a compatible cash drawer is connected to the POS device. Use for manual cash drawer operations, implementing custom payment workflows, or providing explicit cash drawer access in register management interfaces.
*
* The returned promise resolves once the open command has been passed to the native printer bridge. It doesn't confirm that the drawer physically opened, and later hardware failures (such as a jammed drawer) aren't reported. Errors while sending the command reject the promise.
*
* @returns Void
*/
open(): Promise<void>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type ConnectivityStateSeverity = 'Connected' | 'Disconnected';
*/
export interface ConnectivityState {
/**
* The Internet connection status of the POS device.
* The Internet connection status of the POS device. The state defaults to `'Connected'` until POS has confirmed connectivity, and a confirmed outage is required before `'Disconnected'` is reported.
*/
internetConnected: ConnectivityStateSeverity;
}
Expand All @@ -22,7 +22,7 @@ export interface ConnectivityState {
*/
export interface ConnectivityApiContent {
/**
* Provides read-only access to the current connectivity state and allows subscribing to connectivity changes. Use for implementing connectivity-aware functionality and reactive connectivity handling.
* Provides read-only access to the current connectivity state and allows subscribing to connectivity changes. Use for implementing connectivity-aware functionality and reactive connectivity handling. The state defaults to `Connected` until POS has confirmed connectivity, so an initial `Connected` value doesn't guarantee that the device is online.
*/
current: ReadonlySignalLike<ConnectivityState>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
export interface DeviceApiContent {
/**
* The name of the device as configured by the merchant or system. Use for displaying device information in interfaces, logging, or support contexts where device identification is helpful.
* The operating system device name (for example, the iPad name set in system settings). For the merchant-assigned register name configured in POS settings, use `registerName`. Use for displaying device information in interfaces, logging, or support contexts where device identification is helpful.
*/
name: string;
/**
Expand All @@ -13,6 +13,9 @@ export interface DeviceApiContent {
registerName: string;
/**
* Retrieves the unique string identifier for the device. Returns a promise that resolves to the device ID. Use for device-specific data storage, analytics tracking, or implementing device-based permissions and configurations.
*
* This identifier differs from the numeric device ID available as `shopify.session.deviceId` and as `device.deviceId` on event data.
*
* Note: While Shopify POS attempts to maintain a stable identifier, it is not guaranteed to be permanent and may change.
*/
getDeviceId(): Promise<string>;
Expand Down
Loading
Loading