Aller au contenu

Référence JavaScript (en anglais)

Cette page est générée depuis les types TypeScript du widget et reste en anglais, comme la référence de l’API : son contenu vient des identifiants et des commentaires du code. Une traduction serait une copie qui ment dès la première évolution du contrat.

Version de l’interface JavaScript : 2.8.0 (ServiceWidget.version).

Passed to ServiceWidget.create(). Everything except slug is optional.

NameSummary
options.slugRestaurant slug, e.g. "chez-marie".
options.modePresentation mode.
options.containerMount point for mode: "inline" — a CSS selector or an element.
options.localeInitial guest-facing locale — one of the 11 the widget ships.
options.positionAnchor for the floating trigger.
options.analyticsPush booking events onto the page’s window.dataLayer, for Google Tag Manager.
slug: string;

Restaurant slug, e.g. "chez-marie".

mode?: ServiceWidgetMode;

Presentation mode. Defaults to "inline".

container?: string | HTMLElement;

Mount point for mode: "inline" — a CSS selector or an element. Defaults to #service-widget. Ignored by popover / sticky, which mount into a host of their own at the end of <body>.

create() throws synchronously when an inline container cannot be resolved, so a typo surfaces at the call site rather than as a silent no-render.

locale?: string;

Initial guest-facing locale — one of the 11 the widget ships. Invalid or unsupported values fall back to fr. A locale the guest has already chosen for this restaurant (persisted in localStorage) wins over this.

position?: PopoverPosition;

Anchor for the floating trigger. mode: "sticky" only.

analytics?: boolean;

Push booking events onto the page’s window.dataLayer, for Google Tag Manager. Defaults to true.

The widget only pushes an object; it loads no analytics vendor, sets no cookie and sends no request of its own. Whether anything leaves the browser is decided by your GTM container and your consent platform. Set false to push nothing at all.

Returned by ServiceWidget.create(). Each instance is independent, and a page may carry several.

NameSummary
widget.slugThe restaurant this instance books for.
widget.modeThe mode it was created with.
widget.readyResolves once the instance has mounted.
widget.open()Show the widget.
widget.close()Hide the widget.
widget.isOpen()Whether the widget is currently visible.
widget.setContext()Pre-position the funnel — party size, date, time, seating, service, locale, guest request.
widget.setGuestToken()Hand over a signed guest token, so the funnel opens pre-identified.
widget.setLocks()Grey out fields the guest may not change in the widget.
widget.on()Subscribe to an event.
widget.destroy()Unmount this instance and remove every DOM node it owns — including its own portal host.
readonly slug: string;

The restaurant this instance books for.

readonly mode: ServiceWidgetMode;

The mode it was created with.

readonly ready: Promise<void>;

Resolves once the instance has mounted. Also resolves if the instance is destroyed before mounting, so await widget.ready can never hang.

open(): void;

Show the widget. Safe to call before ready — the open state lives outside React, so an early call is honoured by the first render rather than dropped. No-op in inline mode, which is always visible.

close(): void;

Hide the widget. The React tree is kept alive (display:none + inert), so anything the guest already typed survives a close/reopen. No-op in inline mode.

isOpen(): boolean;

Whether the widget is currently visible. Always true for a mounted inline instance.

setContext(hints: WidgetContextHints): void;

Pre-position the funnel — party size, date, time, seating, service, locale, guest request.

The most specific and most recent of the three transports, so it wins over the script tag’s data-* and over URL parameters. That ordering exists because context changes after load: the guest lands on the page, picks dates in the host’s own search box, and only then clicks Book. Merging is per field, so passing { partySize } leaves a date set earlier alone.

Safe to call before ready — the value is held outside React, so an early call is simply what the first render reads.

These are hints, not identity: forgeable by anyone with devtools, and harmless because the guest can change all of it anyway. Nothing here authenticates a guest, and no contact detail belongs in it.

setGuestToken(token: string | null): void;

Hand over a signed guest token, so the funnel opens pre-identified.

The other tier. setContext carries hints — forgeable, harmless, treated exactly as if the guest had typed them. This carries an assertion: your backend HMACs the guest’s contact details with your integration secret, and we skip the details step, attach to the right profile and honour the consent you already collected. Without a signature any page — or anyone with devtools — could assert any identity and read back a profile, so identity is signed or it is not asserted.

widget.setGuestToken(await myBackend.mintServiceToken(currentUser));
  • Mint per widget-open, not per session. The token lives 15 minutes: long enough to book, short enough that a leaked one is worthless. A session-scoped token would also be minted before your own flow knows what it is booking.
  • The guest can still edit anything it asserts. A prefilled field is a prefilled field. Edit one and it simply stops being asserted, for that field only.
  • Expiry is visible, never silently re-minted — the guest is told the session expired and continues anonymously.
  • Pass null to drop it, e.g. when your own user signs out.

Safe to call before ready, like setContext. Nothing here is trusted client-side: the token is verified server-side on every request that carries it, and this widget only ever renders what the server says it claims.

setLocks(locks: { fields: readonly ServiceWidgetLockableField[]; reason?: string }): void;

Grey out fields the guest may not change in the widget.

⚠️ Presentation only — the server does not enforce these. A booking that contradicts them is accepted. They travel on your page, so anything can change them, and the create endpoint cannot tell an unsigned lock from no lock at all.

Use it when the value came from your own page and you simply want the funnel to reflect it — the guest picked a date in your search box, and re-picking it inside the widget would be a second, confusing chance to disagree with themselves.

When the value must actually hold — a voucher valid on one date only, a prepaid slot — put the field in locked inside the signed token instead. That tier the server enforces: it refuses a create that contradicts it. The two can be combined; the signed set always wins.

reason is shown beside each locked field. Supply the why (« Bon cadeau BC-4471 »), not a whole sentence — the widget adds its own localized ”· date fixée” per field.

on<K extends ServiceWidgetEventName>(event: K, handler: (payload: ServiceWidgetEventMap[K]) => void): ServiceWidgetUnsubscribe;

Subscribe to an event. Returns the unsubscribe function.

destroy(): void;

Unmount this instance and remove every DOM node it owns — including its own portal host. Other instances on the page are untouched. Idempotent.

Subscribe with widget.on(name, handler).

NameSummary
readyThe instance has mounted; its methods are live.
openedThe popover became visible.
closedThe popover was hidden.
step:changedThe guest moved to another step of the funnel.
reservation:createdA reservation now exists server-side — including non-final outcomes (pending approval, awaiting_guarantee holds).
reservation:confirmedThe booking is genuinely final.
widget.on("ready", () => {});

The instance has mounted; its methods are live. Mirrors widget.ready, including for a late subscriber: subscribing after the widget has already mounted calls the handler once, immediately, the way ready.then() would.

widget.on("opened", () => {});

The popover became visible. popover / sticky modes only.

widget.on("closed", () => {});

The popover was hidden. popover / sticky modes only.

widget.on("step:changed", (payload: { step: ServiceWidgetStep }) => {});

The guest moved to another step of the funnel.

Transitions only — there is no event for the step the funnel opens on. The widget mounts with the page, not with the guest’s attention: a popover or sticky instance builds its whole booking tree on load and merely hides it, so an event at mount would count page views rather than funnel entries, and every conversion rate computed from it would be wrong by the ratio between the two.

Take the entry from the event that already means it on your integration:

  • popover / stickyopened, which fires for every path that opens the widget: your open() call, the sticky button, a declarative trigger.
  • inlineready, since an inline widget is visible from the moment it mounts.

Re-entries do fire: “make another reservation” from the confirmation, and the restart after a payment hold expires, are both real transitions back to availability, and a funnel that hid them would under-count second bookings.

The step is one of four the guest can reach. Internal states — a booking disabled mid-session, for instance — are not funnel steps and are not reported here.

widget.on("reservation:created", (payload: { reservation: ServiceWidgetReservation }) => {});

A reservation now exists server-side — including non-final outcomes (pending approval, awaiting_guarantee holds). Never treat this as “booked”: listen for reservation:confirmed for that.

widget.on("reservation:confirmed", (payload: { reservation: ServiceWidgetReservation }) => {});

The booking is genuinely final.

Carried by reservation:created and reservation:confirmed.

NameSummary
reservation.publicIdStable public identifier — the join key for webhooks and the read API.
reservation.statusReservation status as of the event, e.g. confirmed, pending.
reservation.dateService date, YYYY-MM-DD.
reservation.startTimeStart time, HH:MM.
reservation.partySizeNumber of covers.
reservation.sectionKeySeating-group key, or null for “any”.
publicId: string;

Stable public identifier — the join key for webhooks and the read API.

status: string;

Reservation status as of the event, e.g. confirmed, pending.

date: string;

Service date, YYYY-MM-DD.

startTime: string;

Start time, HH:MM.

partySize: number;

Number of covers.

sectionKey: string | null;

Seating-group key, or null for “any”.

Installed by the script tag. create() is the entry point; the rest is the original one-widget-per-page API, kept working because restaurants have that snippet live today.

NameSummary
ServiceWidget.create()Create a widget instance.
ServiceWidget.open()Open the widget the script tag mounted.
ServiceWidget.close()Close the widget the script tag mounted.
ServiceWidget.isOpen()Whether the widget the script tag mounted is currently visible.
ServiceWidget.destroy()Destroy every instance on the page, whatever created them, and disarm the declarative [data-service-widget-open] triggers so a later click cannot resurrect the widget.
ServiceWidget.versionBundle version.
create(options: ServiceWidgetCreateOptions): ServiceWidgetInstance;

Create a widget instance. The modern entry point.

open(): void;

Open the widget the script tag mounted.

Takes no arguments because one script tag drives one restaurant: there is never a second widget it could have meant. Called before the page has finished loading, it is remembered and honoured the moment the widget mounts, so it is safe from any script position.

Does nothing when the script tag mounted an inline widget — that one is on the page already — or when no script tag mounted anything, in which case use create() and call open() on the instance it returns.

close(): void;

Close the widget the script tag mounted.

isOpen(): boolean;

Whether the widget the script tag mounted is currently visible.

destroy(): void;

Destroy every instance on the page, whatever created them, and disarm the declarative [data-service-widget-open] triggers so a later click cannot resurrect the widget.

version: string;

Bundle version.

Referenced by the signatures above. Both are closed sets — a value outside them cannot arrive.

export type ServiceWidgetMode = "inline" | "popover" | "sticky";

How a widget instance is presented on the host page.

  • inline — rendered in flow inside the host’s own container.
  • popover — no trigger UI; the host page opens it with widget.open().
  • sticky — the widget renders its own floating “Book a table” button.
export type ServiceWidgetStep = "availability" | "details" | "payment" | "confirmation";

A step of the booking funnel, as reported by step:changed.

  • availability — choosing covers, seating, day and time.
  • details — entering name and contact details.
  • payment — capturing a card. Only where the booking requires a guarantee, so a funnel built on these has to treat it as optional.
  • confirmation — the receipt.