Skip to content

TEBEX-1618 - Add async launch callback#33

Merged
Chilledson merged 13 commits intotebexio:mainfrom
Chilledson:add-async-launch-callback
Feb 27, 2026
Merged

TEBEX-1618 - Add async launch callback#33
Chilledson merged 13 commits intotebexio:mainfrom
Chilledson:add-async-launch-callback

Conversation

@Chilledson
Copy link
Contributor

This pull request introduces support for launching the checkout with an asynchronous callback, allowing the basket identifier (ident) to be resolved at launch time rather than during initialization. It also adds a configurable timeout for this callback, changes the loading spinner to appear while the callback is running.

Support for async basket resolution and improved loading experience:

  • Added an optional async callback parameter to Tebex.checkout.launch() that allows the basket ident to be fetched at launch time, with a configurable timeout (launchTimeout). If the callback is provided, the ident is resolved before the checkout iframe is created. [1] [2] [3] [4] [5] [6] [7] [8] [9]
  • Refactored mobile popup logic to support async ident resolution, ensuring the popup window is opened synchronously to avoid browser blocking, and displaying a spinner while waiting for the callback. [1] [2] [3]
  • Improved the loading spinner experience: spinner is shown during lightbox opening and hidden once the checkout iframe is ready, including new CSS for spinner animation. [1] [2] [3] [4]

API and type changes:

  • Changed ident in CheckoutOptions to be optional, allowing it to be resolved at launch time.
  • Added launchTimeout to CheckoutOptions and implemented validation/warning logic for its value. [1] [2] [3]

Utility and testing improvements:

  • Added a generic withTimeout utility for promise timeout handling. [1] [2]
  • Updated tests to cover new behavior for optional ident, launchTimeout defaults, and validation. [1] [2]

Error handling improvements:

  • Added logError utility for improved error logging.

Example usage update:

  • Updated example/index.js to demonstrate fetching the basket ident asynchronously at checkout launch, and removed pre-fetch logic on page load. [1] [2]

@Chilledson Chilledson changed the title Add async launch callback TEBEX-1618 - Add async launch callback Feb 26, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds support for asynchronously resolving the basket identifier (ident) at checkout launch time via an optional callback parameter to Tebex.checkout.launch(). The implementation ensures the spinner is displayed during the callback execution, handles popup blocking gracefully for mobile flows, and includes comprehensive test coverage for the new functionality.

Changes:

  • Made ident optional in CheckoutOptions and added async callback parameter to launch() method for resolving ident at runtime
  • Added launchTimeout configuration option (default 10 seconds) with validation to control callback timeout duration
  • Implemented loading spinner display during lightbox opening and callback execution, with proper cleanup on iframe ready

Reviewed changes

Copilot reviewed 11 out of 23 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/checkout.ts Core implementation of async callback support in launch(), timeout handling, and mobile popup pre-opening logic
src/components/lightbox.tsx Added showSpinner() and hideSpinner() methods with lifecycle management
src/components/lightbox.css CSS for spinner animation with keyframe rotation
src/utils/timeout.ts New generic withTimeout() utility for promise timeout handling
src/utils/err.ts Enhanced err() with configurable prefix and added unused logError() utility
src/utils/index.ts Export withTimeout utility
src/types/zoid.d.ts Updated ZoidComponent type signature to accept optional window parameter
tests/checkout.test.ts Comprehensive test coverage for callback scenarios, timeout handling, and error cases
tests/index.test-d.ts Updated type tests to reflect optional ident parameter
example/index.js Updated example to demonstrate async basket fetching at launch time
example/dist/index.js Compiled example code
package.json Version bump to 1.11.0
package-lock.json Added Node.js engine constraint >= 20.0.0
dist/* Generated distribution files with new functionality

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

src/utils/err.ts Outdated
Comment on lines 13 to 15
export const logError = (msg = "") => {
console.error("Tebex.js error" + (msg ? ": " : "") + msg.trim());
};
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logError utility function is defined but never used in the codebase. Consider removing it unless it's intended for future use. If it's meant for external consumption, it should be exported from the main module index.

Copilot uses AI. Check for mistakes.
Comment on lines +263 to +281
async #openMobilePopupWithCallback(callback: () => Promise<string>) {
// Must be opened synchronously — browsers block window.open after an async operation.
const preOpenedWin = window.open('', '_blank');

if (preOpenedWin) {
const spinner = spinnerRender({ props: {} });
preOpenedWin.document.open();
preOpenedWin.document.write(spinner.outerHTML);
preOpenedWin.document.close();
}

try {
await this.#resolveIdentFromCallback(callback);
} catch (error) {
preOpenedWin?.close();
throw error;
}

await this.#createComponentInstance(document.body, true, preOpenedWin ?? undefined);
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If window.open() fails and returns null (which can happen if popups are blocked), preOpenedWin will be null. The code then passes preOpenedWin ?? undefined to #createComponentInstance. However, the check on line 267 if (preOpenedWin) only writes the spinner HTML if the window was successfully opened. This means if the popup was blocked, the user won't see any feedback, and the code will proceed to call #createComponentInstance with undefined, which may cause zoid to attempt opening a new popup anyway. Consider handling the popup-blocked case more explicitly, perhaps by showing an error message to the user or falling back to a different behavior.

Copilot uses AI. Check for mistakes.
display: block !important;
}

.tebex-js-lightbox__spinner {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way this could reuse the Zoid spinner? Or at least the styles in spinner.css?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will refactor to use the same, I had avoided it because the spinner was a bit awkward to implement at first.

@Chilledson Chilledson merged commit eba32f1 into tebexio:main Feb 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants