@vtex/faststore 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/dist/actions.json +181 -0
- package/dist/catalog.json +1595 -481
- package/dist/client.d.ts +2 -1
- package/dist/client.js +2 -1
- package/dist/components.d.ts +31 -7
- package/dist/components.js +35 -13
- package/dist/content-types.json +73 -0
- package/dist/docs/access-code-sign-in.md +1 -1
- package/dist/docs/account-access-gate.md +153 -0
- package/dist/docs/account-entry-point.md +1 -2
- package/dist/docs/account-menu.md +164 -0
- package/dist/docs/address-summary.md +24 -3
- package/dist/docs/alert-dialog.md +204 -0
- package/dist/docs/billing-address.md +284 -0
- package/dist/docs/buy-button.md +1 -1
- package/dist/docs/card-entry.md +250 -0
- package/dist/docs/cardholder-document.md +142 -0
- package/dist/docs/cart.md +1 -1
- package/dist/docs/client-preferences-fieldset.md +148 -0
- package/dist/docs/client-preferences.md +2 -1
- package/dist/docs/client-profile-fieldset.md +184 -0
- package/dist/docs/client-profile.md +1 -1
- package/dist/docs/document-field.md +58 -4
- package/dist/docs/field.md +317 -0
- package/dist/docs/form.md +307 -0
- package/dist/docs/identity-edit.md +6 -6
- package/dist/docs/identity-summary.md +5 -5
- package/dist/docs/installments.md +248 -0
- package/dist/docs/my-account-profile.md +1 -0
- package/dist/docs/order-placed-payment-summary.md +138 -0
- package/dist/docs/payment-method-badges.md +232 -0
- package/dist/docs/payment-methods.md +1 -0
- package/dist/docs/payment.md +218 -0
- package/dist/docs/phone-field.md +58 -4
- package/dist/docs/place-order.md +157 -0
- package/dist/docs/save-card.md +186 -0
- package/dist/docs/saved-cards.md +218 -0
- package/dist/docs/select.md +465 -0
- package/dist/docs/session.md +1 -1
- package/dist/docs/shipping-address.md +110 -258
- package/dist/docs/sign-in-redirect.md +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/internal/client.d.ts +3 -1
- package/dist/internal/client.js +3 -1
- package/dist/matter/access-code-sign-in/access-code-sign-in.js +9 -8
- package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
- package/dist/matter/account-access-gate/account-access-gate.d.ts +86 -0
- package/dist/matter/account-access-gate/account-access-gate.js +283 -0
- package/dist/matter/account-access-gate/account-access-gate.js.map +1 -0
- package/dist/matter/account-access-gate/sign-out-intent.d.ts +17 -0
- package/dist/matter/account-access-gate/sign-out-intent.js +62 -0
- package/dist/matter/account-access-gate/sign-out-intent.js.map +1 -0
- package/dist/matter/account-entry-point/account-entry-point.js +2 -2
- package/dist/matter/account-entry-point/account-entry-point.js.map +1 -1
- package/dist/matter/account-menu/account-menu.d.ts +88 -0
- package/dist/matter/account-menu/account-menu.js +246 -0
- package/dist/matter/account-menu/account-menu.js.map +1 -0
- package/dist/matter/billing-address/billing-address.d.ts +165 -0
- package/dist/matter/billing-address/billing-address.js +408 -0
- package/dist/matter/billing-address/billing-address.js.map +1 -0
- package/dist/matter/card-entry/card-capture.d.ts +25 -0
- package/dist/matter/card-entry/card-capture.js +25 -0
- package/dist/matter/card-entry/card-capture.js.map +1 -0
- package/dist/matter/card-entry/card-entry.d.ts +120 -0
- package/dist/matter/card-entry/card-entry.js +832 -0
- package/dist/matter/card-entry/card-entry.js.map +1 -0
- package/dist/matter/cardholder-document/cardholder-document.d.ts +45 -0
- package/dist/matter/cardholder-document/cardholder-document.js +70 -0
- package/dist/matter/cardholder-document/cardholder-document.js.map +1 -0
- package/dist/matter/cart/cart.d.ts +1 -1
- package/dist/matter/cart/cart.js +2 -2
- package/dist/matter/cart/cart.js.map +1 -1
- package/dist/matter/client-preferences/client-preferences-fieldset.d.ts +77 -0
- package/dist/matter/client-preferences/client-preferences-fieldset.js +299 -0
- package/dist/matter/client-preferences/client-preferences-fieldset.js.map +1 -0
- package/dist/matter/client-preferences/client-preferences.d.ts +3 -5
- package/dist/matter/client-preferences/client-preferences.js +6 -6
- package/dist/matter/client-preferences/client-preferences.js.map +1 -1
- package/dist/matter/client-profile/client-profile.d.ts +4 -6
- package/dist/matter/client-profile/client-profile.js +9 -6
- package/dist/matter/client-profile/client-profile.js.map +1 -1
- package/dist/matter/client-profile-form/client-profile-fieldset.d.ts +119 -0
- package/dist/matter/client-profile-form/client-profile-fieldset.js +488 -0
- package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -0
- package/dist/matter/delivery-options/delivery-options.js.map +1 -1
- package/dist/matter/identity-edit/identity-edit.js +2 -2
- package/dist/matter/identity-edit/identity-edit.js.map +1 -1
- package/dist/matter/identity-summary/identity-summary.js +2 -2
- package/dist/matter/identity-summary/identity-summary.js.map +1 -1
- package/dist/matter/installments/installments-select.d.ts +42 -0
- package/dist/matter/installments/installments-select.js +167 -0
- package/dist/matter/installments/installments-select.js.map +1 -0
- package/dist/matter/installments/installments.d.ts +58 -0
- package/dist/matter/installments/installments.js +176 -0
- package/dist/matter/installments/installments.js.map +1 -0
- package/dist/matter/installments/use-installments-selection.d.ts +29 -0
- package/dist/matter/installments/use-installments-selection.js +95 -0
- package/dist/matter/installments/use-installments-selection.js.map +1 -0
- package/dist/matter/my-account-profile/my-account-profile.js +5 -5
- package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +63 -0
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +305 -0
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -0
- package/dist/matter/payment/payment-card-capture.d.ts +19 -0
- package/dist/matter/payment/payment-card-capture.js +129 -0
- package/dist/matter/payment/payment-card-capture.js.map +1 -0
- package/dist/matter/payment/payment-controller.d.ts +24 -0
- package/dist/matter/payment/payment-controller.js +107 -0
- package/dist/matter/payment/payment-controller.js.map +1 -0
- package/dist/matter/payment/payment-method-badges.d.ts +57 -0
- package/dist/matter/payment/payment-method-badges.js +192 -0
- package/dist/matter/payment/payment-method-badges.js.map +1 -0
- package/dist/matter/payment/payment.d.ts +89 -0
- package/dist/matter/payment/payment.js +263 -0
- package/dist/matter/payment/payment.js.map +1 -0
- package/dist/matter/payment/use-payment-method-selection.d.ts +302 -0
- package/dist/matter/payment/use-payment-method-selection.js +458 -0
- package/dist/matter/payment/use-payment-method-selection.js.map +1 -0
- package/dist/matter/payment/use-payment-submit-gate.d.ts +111 -0
- package/dist/matter/payment/use-payment-submit-gate.js +144 -0
- package/dist/matter/payment/use-payment-submit-gate.js.map +1 -0
- package/dist/matter/place-order/place-order.d.ts +74 -0
- package/dist/matter/place-order/place-order.js +379 -0
- package/dist/matter/place-order/place-order.js.map +1 -0
- package/dist/matter/product-list/product-list.d.ts +1 -0
- package/dist/matter/product-list/product-list.js.map +1 -1
- package/dist/matter/product-shelf/product-shelf.d.ts +1 -0
- package/dist/matter/product-shelf/product-shelf.js +2 -0
- package/dist/matter/product-shelf/product-shelf.js.map +1 -1
- package/dist/matter/region/region.js +1 -1
- package/dist/matter/region/region.js.map +1 -1
- package/dist/matter/save-card/save-card.d.ts +49 -0
- package/dist/matter/save-card/save-card.js +188 -0
- package/dist/matter/save-card/save-card.js.map +1 -0
- package/dist/matter/saved-cards/saved-cards.d.ts +79 -0
- package/dist/matter/saved-cards/saved-cards.js +387 -0
- package/dist/matter/saved-cards/saved-cards.js.map +1 -0
- package/dist/matter/shipping-address/country-descriptors.js +20 -10
- package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
- package/dist/matter/shipping-address/shipping-address.d.ts +113 -174
- package/dist/matter/shipping-address/shipping-address.js +481 -792
- package/dist/matter/shipping-address/shipping-address.js.map +1 -1
- package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
- package/dist/matter/variant-selector/variant-selector.js +1 -1
- package/dist/particles/address-summary/address-summary.d.ts +24 -0
- package/dist/particles/address-summary/address-summary.js +83 -2
- package/dist/particles/address-summary/address-summary.js.map +1 -1
- package/dist/particles/alert-dialog/alert-dialog.d.ts +68 -0
- package/dist/particles/alert-dialog/alert-dialog.js +187 -0
- package/dist/particles/alert-dialog/alert-dialog.js.map +1 -0
- package/dist/particles/checkbox/checkbox.d.ts +7 -3
- package/dist/particles/checkbox/checkbox.js +4 -1
- package/dist/particles/checkbox/checkbox.js.map +1 -1
- package/dist/particles/document-field/document-field.d.ts +48 -5
- package/dist/particles/document-field/document-field.js +189 -32
- package/dist/particles/document-field/document-field.js.map +1 -1
- package/dist/particles/field/bound-field-registry.js +52 -0
- package/dist/particles/field/bound-field-registry.js.map +1 -0
- package/dist/particles/field/field.d.ts +116 -0
- package/dist/particles/field/field.js +497 -6
- package/dist/particles/field/field.js.map +1 -1
- package/dist/particles/fieldset/fieldset.js +2 -2
- package/dist/particles/fieldset/fieldset.js.map +1 -1
- package/dist/particles/footer-social/footer-social.js +1 -1
- package/dist/particles/form/form.d.ts +104 -0
- package/dist/particles/form/form.js +485 -0
- package/dist/particles/form/form.js.map +1 -0
- package/dist/particles/incentives/incentives.js +1 -1
- package/dist/particles/phone-field/phone-field.d.ts +38 -10
- package/dist/particles/phone-field/phone-field.js +183 -39
- package/dist/particles/phone-field/phone-field.js.map +1 -1
- package/dist/particles/postal-code-field/postal-code-field.js +2 -2
- package/dist/particles/postal-code-field/postal-code-field.js.map +1 -1
- package/dist/particles/select/field-select.d.ts +43 -0
- package/dist/particles/select/field-select.js +150 -0
- package/dist/particles/select/field-select.js.map +1 -0
- package/dist/particles/select/select.d.ts +2 -0
- package/dist/particles/select/select.js.map +1 -1
- package/dist/particles/shared/compose-refs.js +21 -0
- package/dist/particles/shared/compose-refs.js.map +1 -0
- package/dist/schema.graphql +701 -2
- package/dist/sdk/actions/analytics-action-provider.d.ts +15 -0
- package/dist/sdk/actions/analytics-action-provider.js +19 -0
- package/dist/sdk/actions/analytics-action-provider.js.map +1 -0
- package/dist/sdk/actions/cart-action-provider.d.ts +1 -1
- package/dist/sdk/actions/cart-action-provider.js +1 -1
- package/dist/sdk/actions/cart-action-provider.js.map +1 -1
- package/dist/sdk/actions/index.d.ts +3 -1
- package/dist/sdk/actions/index.js +3 -1
- package/dist/sdk/actions/payment-action-provider.d.ts +22 -0
- package/dist/sdk/actions/payment-action-provider.js +26 -0
- package/dist/sdk/actions/payment-action-provider.js.map +1 -0
- package/dist/sdk/actions/session-action-provider.d.ts +1 -1
- package/dist/sdk/actions/session-action-provider.js +1 -1
- package/dist/sdk/actions/session-action-provider.js.map +1 -1
- package/dist/sdk/actions/shipping-action-provider.d.ts +1 -1
- package/dist/sdk/actions/shipping-action-provider.js +1 -1
- package/dist/sdk/actions/shipping-action-provider.js.map +1 -1
- package/dist/sdk/actions/types.d.ts +6 -2
- package/dist/sdk/analytics/analytics-destination.d.ts +43 -0
- package/dist/sdk/analytics/analytics-destination.js +90 -0
- package/dist/sdk/analytics/analytics-destination.js.map +1 -0
- package/dist/sdk/analytics/use-analytics-actions.d.ts +57 -0
- package/dist/sdk/analytics/use-analytics-actions.js +108 -0
- package/dist/sdk/analytics/use-analytics-actions.js.map +1 -0
- package/dist/sdk/cache/storefront-cache.js +1 -1
- package/dist/sdk/cache/storefront-cache.js.map +1 -1
- package/dist/sdk/cart/operations.d.ts +4 -4
- package/dist/sdk/cart/operations.js +1 -1
- package/dist/sdk/cart/operations.js.map +1 -1
- package/dist/sdk/cart/optimistic.js +1 -1
- package/dist/sdk/cart/optimistic.js.map +1 -1
- package/dist/sdk/cart/use-cart-actions.d.ts +1 -1
- package/dist/sdk/cart/use-cart-actions.js +4 -4
- package/dist/sdk/cart/use-cart-actions.js.map +1 -1
- package/dist/sdk/cart/use-cart-validate-on-open.js +1 -1
- package/dist/sdk/cart/use-cart-validate-on-open.js.map +1 -1
- package/dist/sdk/cart/write-gate.js +1 -1
- package/dist/sdk/cart/write-gate.js.map +1 -1
- package/dist/sdk/data/entity-resolution-context.js +31 -0
- package/dist/sdk/data/entity-resolution-context.js.map +1 -0
- package/dist/sdk/data/entity-subscriber.js +18 -8
- package/dist/sdk/data/entity-subscriber.js.map +1 -1
- package/dist/sdk/data/index.js +1 -0
- package/dist/sdk/data/page-reactive-data-root.d.ts +38 -2
- package/dist/sdk/data/page-reactive-data-root.js +4 -2
- package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
- package/dist/sdk/data/reactive-data-provider.d.ts +1 -1
- package/dist/sdk/data/reactive-data-provider.js +8 -4
- package/dist/sdk/data/reactive-data-provider.js.map +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.d.ts +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.js +13 -5
- package/dist/sdk/data/reactive-list-item-data-provider.js.map +1 -1
- package/dist/sdk/data/use-reactive-data-settled.js +39 -0
- package/dist/sdk/data/use-reactive-data-settled.js.map +1 -0
- package/dist/sdk/data/use-reactive-entity.js +6 -2
- package/dist/sdk/data/use-reactive-entity.js.map +1 -1
- package/dist/sdk/generated/fragment-types.js +2 -0
- package/dist/sdk/generated/fragment-types.js.map +1 -1
- package/dist/sdk/generated/gql.js +2 -1
- package/dist/sdk/generated/gql.js.map +1 -1
- package/dist/sdk/generated/graphql.js +85 -1
- package/dist/sdk/generated/graphql.js.map +1 -1
- package/dist/sdk/i18n/messages/en-US.js +3 -0
- package/dist/sdk/i18n/messages/en-US.json +3 -0
- package/dist/sdk/i18n/messages/en-US.json.d.ts +3 -0
- package/dist/sdk/i18n/messages/es-ES.js +3 -0
- package/dist/sdk/i18n/messages/es-ES.json +3 -0
- package/dist/sdk/i18n/messages/pt-BR.js +3 -0
- package/dist/sdk/i18n/messages/pt-BR.json +3 -0
- package/dist/sdk/identity/operations.js.map +1 -1
- package/dist/sdk/identity/use-identity-actions.d.ts +1 -1
- package/dist/sdk/identity/use-identity-actions.js +3 -2
- package/dist/sdk/identity/use-identity-actions.js.map +1 -1
- package/dist/sdk/payment/gateway-origin.js +35 -0
- package/dist/sdk/payment/gateway-origin.js.map +1 -0
- package/dist/sdk/payment/gateway.d.ts +85 -0
- package/dist/sdk/payment/gateway.js +183 -0
- package/dist/sdk/payment/gateway.js.map +1 -0
- package/dist/sdk/payment/operations.d.ts +52 -0
- package/dist/sdk/payment/operations.js +223 -0
- package/dist/sdk/payment/operations.js.map +1 -0
- package/dist/sdk/payment/security-code-mask.js +71 -0
- package/dist/sdk/payment/security-code-mask.js.map +1 -0
- package/dist/sdk/payment/submission-registry.d.ts +33 -0
- package/dist/sdk/payment/submission-registry.js +58 -0
- package/dist/sdk/payment/submission-registry.js.map +1 -0
- package/dist/sdk/payment/use-payment-actions.d.ts +78 -0
- package/dist/sdk/payment/use-payment-actions.js +276 -0
- package/dist/sdk/payment/use-payment-actions.js.map +1 -0
- package/dist/sdk/payment/use-place-order.d.ts +65 -0
- package/dist/sdk/payment/use-place-order.js +640 -0
- package/dist/sdk/payment/use-place-order.js.map +1 -0
- package/dist/sdk/session/auth-operations.d.ts +1 -1
- package/dist/sdk/session/auth-operations.js.map +1 -1
- package/dist/sdk/session/use-session-actions.d.ts +1 -1
- package/dist/sdk/session/use-session-actions.js.map +1 -1
- package/dist/sdk/session/use-session-revalidation.js +65 -0
- package/dist/sdk/session/use-session-revalidation.js.map +1 -0
- package/dist/sdk/shipping/option-select-operations.d.ts +1 -1
- package/dist/sdk/shipping/option-select-operations.js.map +1 -1
- package/dist/sdk/shipping/request-timeout.js +1 -1
- package/dist/sdk/shipping/request-timeout.js.map +1 -1
- package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
- package/dist/sdk/shipping/shipping-operations.js.map +1 -1
- package/dist/sdk/shipping/use-shipping-actions.d.ts +2 -2
- package/dist/sdk/shipping/use-shipping-actions.js +1 -1
- package/dist/sdk/shipping/use-shipping-actions.js.map +1 -1
- package/dist/sdk/state/cart.d.ts +3 -4
- package/dist/sdk/state/cart.js +6 -7
- package/dist/sdk/state/cart.js.map +1 -1
- package/dist/utils/loggable-error-name.js +49 -0
- package/dist/utils/loggable-error-name.js.map +1 -0
- package/dist/utils/resolve-authored-copy.js +18 -0
- package/dist/utils/resolve-authored-copy.js.map +1 -0
- package/dist/utils/safe-href.js +3 -3
- package/dist/utils/safe-href.js.map +1 -1
- package/dist/utils/substitute-template.js +33 -0
- package/dist/utils/substitute-template.js.map +1 -0
- package/package.json +2 -1
- package/src/styles/tailwind.css +34 -0
- package/dist/docs/client-profile-form.md +0 -192
- package/dist/matter/client-profile-form/client-profile-form.d.ts +0 -126
- package/dist/matter/client-profile-form/client-profile-form.js +0 -662
- package/dist/matter/client-profile-form/client-profile-form.js.map +0 -1
|
@@ -20,7 +20,7 @@ const IdentityEditControllerDefinition = defineComponent({
|
|
|
20
20
|
category: "commerce"
|
|
21
21
|
},
|
|
22
22
|
schema: {},
|
|
23
|
-
guideline: "Owns the identity section's summary/form toggle: starts on the summary, opens on IdentityEditButton and returns on IdentityEditCancelButton. Saving does not move it, so the form
|
|
23
|
+
guideline: "Owns the identity section's summary/form toggle: starts on the summary, opens on IdentityEditButton and returns on IdentityEditCancelButton. Saving does not move it, so the form stays open and no descendant needs to know about a section around it. Self-contained: collapsing tells nothing else, so no other section opens as a result. Compose an IdentitySummaryView holding the summary parts and an IdentityEditForm holding a FormRoot over ClientProfileFieldset, plus an IdentityEditButton in the heading and an IdentityEditCancelButton next to the form. No state is named anywhere: which state a part belongs to is which component it is. Renders no DOM of its own."
|
|
24
24
|
});
|
|
25
25
|
const IdentitySummaryViewDefinition = defineComponent({
|
|
26
26
|
name: "IdentitySummaryView",
|
|
@@ -44,7 +44,7 @@ const IdentityEditFormDefinition = defineComponent({
|
|
|
44
44
|
category: "commerce"
|
|
45
45
|
},
|
|
46
46
|
schema: {},
|
|
47
|
-
guideline: "Renders its children only while the identity section shows the form, and nothing while it shows the summary. Compose
|
|
47
|
+
guideline: "Renders its children only while the identity section shows the form, and nothing while it shows the summary. Compose a FormRoot holding ClientProfileFieldset and its controls inside, plus an IdentityEditCancelButton. Must be inside an IdentityEditController."
|
|
48
48
|
});
|
|
49
49
|
const IdentityEditButtonDefinition = defineComponent({
|
|
50
50
|
name: "IdentityEditButton",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-edit.js","names":[],"sources":["../../../src/matter/identity-edit/identity-edit.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport { createContext, type ReactNode, useContext, useMemo, useState } from 'react';\n\n// ---------------------------------------------------------------------------\n// Identity view/edit toggle.\n//\n// The identity section shows a summary of who the shopper is until they ask to change it, and returns to\n// the summary when they say so — this component owns the mode rather than a page authoring it\n// (ADR-0006). Saving does not move it; whether it should is a sequencing question left to the\n// flow-of-steps RFC (#611). The sibling of the delivery section's `ShippingAddressEditController`, and\n// its own component because the two differ in the parts that are domain knowledge. See identity-edit.md.\n// ---------------------------------------------------------------------------\n\n/** Internal: the section's two states never reach the schema, because each has its own component. */\ntype IdentityEditMode = 'view' | 'edit';\n\ntype IdentityEditContextValue = {\n mode: IdentityEditMode;\n setMode: (mode: IdentityEditMode) => void;\n};\n\nconst IdentityEditContext = createContext<IdentityEditContextValue | undefined>(undefined);\n\nfunction useIdentityEditContext(): IdentityEditContextValue {\n const ctx = useContext(IdentityEditContext);\n if (!ctx) {\n throw new Error(\n 'IdentitySummaryView, IdentityEditForm and the edit buttons must be used inside an IdentityEditController.',\n );\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport type IdentityEditControllerProps = {\n children?: ReactNode;\n};\n\nexport type IdentityEditPanelProps = {\n children?: ReactNode;\n};\n\nexport type IdentityEditButtonProps = {\n /** Text shown when no children are composed. */\n label?: string;\n className?: string;\n /** Compose custom button content (e.g. an Icon plus text); falls back to `label`. */\n children?: ReactNode;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const IdentityEditControllerDefinition = defineComponent<IdentityEditControllerProps>({\n name: 'IdentityEditController',\n importPath: '@vtex/faststore/components',\n component: IdentityEditController,\n providesContext: 'identity-edit',\n editor: { label: 'Identity Edit Controller', category: 'commerce' },\n schema: {},\n guideline:\n \"Owns the identity section's summary/form toggle: starts on the summary, opens on IdentityEditButton and returns on IdentityEditCancelButton. Saving does not move it, so the form's confirmation stays readable and ClientProfileForm never needs to know about a section around it. Self-contained: collapsing tells nothing else, so no other section opens as a result. Compose an IdentitySummaryView holding the summary parts and an IdentityEditForm holding a ClientProfileForm, plus an IdentityEditButton in the heading and an IdentityEditCancelButton next to the form. No state is named anywhere: which state a part belongs to is which component it is. Renders no DOM of its own.\",\n});\n\nexport const IdentitySummaryViewDefinition = defineComponent<IdentityEditPanelProps>({\n name: 'IdentitySummaryView',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryView,\n requiresContext: 'identity-edit',\n editor: { label: 'Identity Summary View', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders its children only while the identity section shows the summary, and nothing while it shows the form. Takes no props: which state it gates is which component it is. Must be inside an IdentityEditController.',\n});\n\nexport const IdentityEditFormDefinition = defineComponent<IdentityEditPanelProps>({\n name: 'IdentityEditForm',\n importPath: '@vtex/faststore/components',\n component: IdentityEditForm,\n requiresContext: 'identity-edit',\n editor: { label: 'Identity Edit Form', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders its children only while the identity section shows the form, and nothing while it shows the summary. Compose ClientProfileFormRoot and its parts inside, plus an IdentityEditCancelButton. Must be inside an IdentityEditController.',\n});\n\nexport const IdentityEditButtonDefinition = defineComponent<IdentityEditButtonProps>({\n name: 'IdentityEditButton',\n importPath: '@vtex/faststore/components',\n component: IdentityEditButton,\n requiresContext: 'identity-edit',\n editor: { label: 'Identity Edit Button', category: 'action' },\n schema: {\n label: { type: 'string', default: 'editar', label: 'Button label' },\n },\n guideline:\n 'Opens the identity form. Place it in the section heading, inside an IdentitySummaryView so it disappears while the form is open. Compose an Icon plus text as children, or rely on `label`. Must be inside an IdentityEditController.',\n});\n\nexport const IdentityEditCancelButtonDefinition = defineComponent<IdentityEditButtonProps>({\n name: 'IdentityEditCancelButton',\n importPath: '@vtex/faststore/components',\n component: IdentityEditCancelButton,\n requiresContext: 'identity-edit',\n editor: { label: 'Identity Edit Cancel Button', category: 'action' },\n schema: {\n label: { type: 'string', default: 'cancelar', label: 'Button label' },\n },\n guideline:\n 'Returns the identity section to its summary without saving. Place it inside an IdentityEditForm. Must be inside an IdentityEditController.',\n});\n\n// ---------------------------------------------------------------------------\n// Components\n// ---------------------------------------------------------------------------\n\n/**\n * Owns the identity section's mode and provides it to descendants. Renders no DOM of its own.\n *\n * The mode moves only when the shopper presses one of this section's own buttons. Saving the form does\n * not close the section — the confirmation stays readable, and whether finishing a form should collapse\n * anything is a sequencing question this component deliberately has no opinion on.\n */\nexport function IdentityEditController({ children }: IdentityEditControllerProps) {\n const [mode, setMode] = useState<IdentityEditMode>('view');\n\n const value = useMemo<IdentityEditContextValue>(() => ({ mode, setMode }), [mode]);\n\n return <IdentityEditContext.Provider value={value}>{children}</IdentityEditContext.Provider>;\n}\n\n/** Renders its children only while the section shows the summary. */\nexport function IdentitySummaryView({ children }: IdentityEditPanelProps) {\n const { mode } = useIdentityEditContext();\n if (mode !== 'view') return null;\n return <>{children}</>;\n}\n\n/** Renders its children only while the section shows the form. */\nexport function IdentityEditForm({ children }: IdentityEditPanelProps) {\n const { mode } = useIdentityEditContext();\n if (mode !== 'edit') return null;\n return <>{children}</>;\n}\n\n/** Opens the form. Renders `children` or the `label` fallback. */\nexport function IdentityEditButton({ label = 'editar', className, children }: IdentityEditButtonProps) {\n const { setMode } = useIdentityEditContext();\n return (\n <button type=\"button\" data-fs-identity-edit-button=\"\" className={className} onClick={() => setMode('edit')}>\n {children ?? label}\n </button>\n );\n}\n\n/** Returns to the summary without saving. Renders `children` or the `label` fallback. */\nexport function IdentityEditCancelButton({ label = 'cancelar', className, children }: IdentityEditButtonProps) {\n const { setMode } = useIdentityEditContext();\n return (\n <button type=\"button\" data-fs-identity-edit-cancel-button=\"\" className={className} onClick={() => setMode('view')}>\n {children ?? label}\n </button>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace Export\n// ---------------------------------------------------------------------------\n\nexport const IdentityEdit = {\n Controller: IdentityEditController,\n ControllerDefinition: IdentityEditControllerDefinition,\n SummaryView: IdentitySummaryView,\n SummaryViewDefinition: IdentitySummaryViewDefinition,\n Form: IdentityEditForm,\n FormDefinition: IdentityEditFormDefinition,\n EditButton: IdentityEditButton,\n EditButtonDefinition: IdentityEditButtonDefinition,\n CancelButton: IdentityEditCancelButton,\n CancelButtonDefinition: IdentityEditCancelButtonDefinition,\n};\n"],"mappings":";;;;;;AAuBA,MAAM,sBAAsB,cAAoD,KAAA,CAAS;AAEzF,SAAS,yBAAmD;CAC1D,MAAM,MAAM,WAAW,mBAAmB;CAC1C,IAAI,CAAC,KACH,MAAM,IAAI,MACR,2GACF;CAEF,OAAO;AACT;AA0BA,MAAa,mCAAmC,gBAA6C;CAC3F,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAA4B,UAAU;CAAW;CAClE,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,gCAAgC,gBAAwC;CACnF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAyB,UAAU;CAAW;CAC/D,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,6BAA6B,gBAAwC;CAChF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAsB,UAAU;CAAW;CAC5D,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,+BAA+B,gBAAyC;CACnF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAwB,UAAU;CAAS;CAC5D,QAAQ,EACN,OAAO;EAAE,MAAM;EAAU,SAAS;EAAU,OAAO;CAAe,EACpE;CACA,WACE;AACJ,CAAC;AAED,MAAa,qCAAqC,gBAAyC;CACzF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAA+B,UAAU;CAAS;CACnE,QAAQ,EACN,OAAO;EAAE,MAAM;EAAU,SAAS;EAAY,OAAO;CAAe,EACtE;CACA,WACE;AACJ,CAAC;;;;;;;;AAaD,SAAgB,uBAAuB,EAAE,YAAyC;CAChF,MAAM,CAAC,MAAM,WAAW,SAA2B,MAAM;CAEzD,MAAM,QAAQ,eAAyC;EAAE;EAAM;CAAQ,IAAI,CAAC,IAAI,CAAC;CAEjF,OAAO,oBAAC,oBAAoB,UAArB;EAAqC;EAAQ;CAAuC,CAAA;AAC7F;;AAGA,SAAgB,oBAAoB,EAAE,YAAoC;CACxE,MAAM,EAAE,SAAS,uBAAuB;CACxC,IAAI,SAAS,QAAQ,OAAO;CAC5B,OAAO,oBAAA,UAAA,EAAG,SAAW,CAAA;AACvB;;AAGA,SAAgB,iBAAiB,EAAE,YAAoC;CACrE,MAAM,EAAE,SAAS,uBAAuB;CACxC,IAAI,SAAS,QAAQ,OAAO;CAC5B,OAAO,oBAAA,UAAA,EAAG,SAAW,CAAA;AACvB;;AAGA,SAAgB,mBAAmB,EAAE,QAAQ,UAAU,WAAW,YAAqC;CACrG,MAAM,EAAE,YAAY,uBAAuB;CAC3C,OACE,oBAAC,UAAD;EAAQ,MAAK;EAAS,gCAA6B;EAAc;EAAW,eAAe,QAAQ,MAAM;EACtG,UAAA,YAAY;CACP,CAAA;AAEZ;;AAGA,SAAgB,yBAAyB,EAAE,QAAQ,YAAY,WAAW,YAAqC;CAC7G,MAAM,EAAE,YAAY,uBAAuB;CAC3C,OACE,oBAAC,UAAD;EAAQ,MAAK;EAAS,uCAAoC;EAAc;EAAW,eAAe,QAAQ,MAAM;EAC7G,UAAA,YAAY;CACP,CAAA;AAEZ;AAMA,MAAa,eAAe;CAC1B,YAAY;CACZ,sBAAsB;CACtB,aAAa;CACb,uBAAuB;CACvB,MAAM;CACN,gBAAgB;CAChB,YAAY;CACZ,sBAAsB;CACtB,cAAc;CACd,wBAAwB;AAC1B"}
|
|
1
|
+
{"version":3,"file":"identity-edit.js","names":[],"sources":["../../../src/matter/identity-edit/identity-edit.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport { createContext, type ReactNode, useContext, useMemo, useState } from 'react';\n\n// ---------------------------------------------------------------------------\n// Identity view/edit toggle.\n//\n// The identity section shows a summary of who the shopper is until they ask to change it, and returns to\n// the summary when they say so — this component owns the mode rather than a page authoring it\n// (ADR-0006). Saving does not move it; whether it should is a sequencing question left to the\n// flow-of-steps RFC (#611). The delivery section owns no such mode of its own — its view/edit toggle\n// was removed with no replacement, so ShippingAddressFieldset always renders once resolved. See\n// identity-edit.md.\n// ---------------------------------------------------------------------------\n\n/** Internal: the section's two states never reach the schema, because each has its own component. */\ntype IdentityEditMode = 'view' | 'edit';\n\ntype IdentityEditContextValue = {\n mode: IdentityEditMode;\n setMode: (mode: IdentityEditMode) => void;\n};\n\nconst IdentityEditContext = createContext<IdentityEditContextValue | undefined>(undefined);\n\nfunction useIdentityEditContext(): IdentityEditContextValue {\n const ctx = useContext(IdentityEditContext);\n if (!ctx) {\n throw new Error(\n 'IdentitySummaryView, IdentityEditForm and the edit buttons must be used inside an IdentityEditController.',\n );\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport type IdentityEditControllerProps = {\n children?: ReactNode;\n};\n\nexport type IdentityEditPanelProps = {\n children?: ReactNode;\n};\n\nexport type IdentityEditButtonProps = {\n /** Text shown when no children are composed. */\n label?: string;\n className?: string;\n /** Compose custom button content (e.g. an Icon plus text); falls back to `label`. */\n children?: ReactNode;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const IdentityEditControllerDefinition = defineComponent<IdentityEditControllerProps>({\n name: 'IdentityEditController',\n importPath: '@vtex/faststore/components',\n component: IdentityEditController,\n providesContext: 'identity-edit',\n editor: { label: 'Identity Edit Controller', category: 'commerce' },\n schema: {},\n guideline:\n \"Owns the identity section's summary/form toggle: starts on the summary, opens on IdentityEditButton and returns on IdentityEditCancelButton. Saving does not move it, so the form stays open and no descendant needs to know about a section around it. Self-contained: collapsing tells nothing else, so no other section opens as a result. Compose an IdentitySummaryView holding the summary parts and an IdentityEditForm holding a FormRoot over ClientProfileFieldset, plus an IdentityEditButton in the heading and an IdentityEditCancelButton next to the form. No state is named anywhere: which state a part belongs to is which component it is. Renders no DOM of its own.\",\n});\n\nexport const IdentitySummaryViewDefinition = defineComponent<IdentityEditPanelProps>({\n name: 'IdentitySummaryView',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryView,\n requiresContext: 'identity-edit',\n editor: { label: 'Identity Summary View', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders its children only while the identity section shows the summary, and nothing while it shows the form. Takes no props: which state it gates is which component it is. Must be inside an IdentityEditController.',\n});\n\nexport const IdentityEditFormDefinition = defineComponent<IdentityEditPanelProps>({\n name: 'IdentityEditForm',\n importPath: '@vtex/faststore/components',\n component: IdentityEditForm,\n requiresContext: 'identity-edit',\n editor: { label: 'Identity Edit Form', category: 'commerce' },\n schema: {},\n guideline:\n 'Renders its children only while the identity section shows the form, and nothing while it shows the summary. Compose a FormRoot holding ClientProfileFieldset and its controls inside, plus an IdentityEditCancelButton. Must be inside an IdentityEditController.',\n});\n\nexport const IdentityEditButtonDefinition = defineComponent<IdentityEditButtonProps>({\n name: 'IdentityEditButton',\n importPath: '@vtex/faststore/components',\n component: IdentityEditButton,\n requiresContext: 'identity-edit',\n editor: { label: 'Identity Edit Button', category: 'action' },\n schema: {\n label: { type: 'string', default: 'editar', label: 'Button label' },\n },\n guideline:\n 'Opens the identity form. Place it in the section heading, inside an IdentitySummaryView so it disappears while the form is open. Compose an Icon plus text as children, or rely on `label`. Must be inside an IdentityEditController.',\n});\n\nexport const IdentityEditCancelButtonDefinition = defineComponent<IdentityEditButtonProps>({\n name: 'IdentityEditCancelButton',\n importPath: '@vtex/faststore/components',\n component: IdentityEditCancelButton,\n requiresContext: 'identity-edit',\n editor: { label: 'Identity Edit Cancel Button', category: 'action' },\n schema: {\n label: { type: 'string', default: 'cancelar', label: 'Button label' },\n },\n guideline:\n 'Returns the identity section to its summary without saving. Place it inside an IdentityEditForm. Must be inside an IdentityEditController.',\n});\n\n// ---------------------------------------------------------------------------\n// Components\n// ---------------------------------------------------------------------------\n\n/**\n * Owns the identity section's mode and provides it to descendants. Renders no DOM of its own.\n *\n * The mode moves only when the shopper presses one of this section's own buttons. Saving the form does\n * not close the section — the confirmation stays readable, and whether finishing a form should collapse\n * anything is a sequencing question this component deliberately has no opinion on.\n */\nexport function IdentityEditController({ children }: IdentityEditControllerProps) {\n const [mode, setMode] = useState<IdentityEditMode>('view');\n\n const value = useMemo<IdentityEditContextValue>(() => ({ mode, setMode }), [mode]);\n\n return <IdentityEditContext.Provider value={value}>{children}</IdentityEditContext.Provider>;\n}\n\n/** Renders its children only while the section shows the summary. */\nexport function IdentitySummaryView({ children }: IdentityEditPanelProps) {\n const { mode } = useIdentityEditContext();\n if (mode !== 'view') return null;\n return <>{children}</>;\n}\n\n/** Renders its children only while the section shows the form. */\nexport function IdentityEditForm({ children }: IdentityEditPanelProps) {\n const { mode } = useIdentityEditContext();\n if (mode !== 'edit') return null;\n return <>{children}</>;\n}\n\n/** Opens the form. Renders `children` or the `label` fallback. */\nexport function IdentityEditButton({ label = 'editar', className, children }: IdentityEditButtonProps) {\n const { setMode } = useIdentityEditContext();\n return (\n <button type=\"button\" data-fs-identity-edit-button=\"\" className={className} onClick={() => setMode('edit')}>\n {children ?? label}\n </button>\n );\n}\n\n/** Returns to the summary without saving. Renders `children` or the `label` fallback. */\nexport function IdentityEditCancelButton({ label = 'cancelar', className, children }: IdentityEditButtonProps) {\n const { setMode } = useIdentityEditContext();\n return (\n <button type=\"button\" data-fs-identity-edit-cancel-button=\"\" className={className} onClick={() => setMode('view')}>\n {children ?? label}\n </button>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace Export\n// ---------------------------------------------------------------------------\n\nexport const IdentityEdit = {\n Controller: IdentityEditController,\n ControllerDefinition: IdentityEditControllerDefinition,\n SummaryView: IdentitySummaryView,\n SummaryViewDefinition: IdentitySummaryViewDefinition,\n Form: IdentityEditForm,\n FormDefinition: IdentityEditFormDefinition,\n EditButton: IdentityEditButton,\n EditButtonDefinition: IdentityEditButtonDefinition,\n CancelButton: IdentityEditCancelButton,\n CancelButtonDefinition: IdentityEditCancelButtonDefinition,\n};\n"],"mappings":";;;;;;AAwBA,MAAM,sBAAsB,cAAoD,KAAA,CAAS;AAEzF,SAAS,yBAAmD;CAC1D,MAAM,MAAM,WAAW,mBAAmB;CAC1C,IAAI,CAAC,KACH,MAAM,IAAI,MACR,2GACF;CAEF,OAAO;AACT;AA0BA,MAAa,mCAAmC,gBAA6C;CAC3F,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAA4B,UAAU;CAAW;CAClE,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,gCAAgC,gBAAwC;CACnF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAyB,UAAU;CAAW;CAC/D,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,6BAA6B,gBAAwC;CAChF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAsB,UAAU;CAAW;CAC5D,QAAQ,CAAC;CACT,WACE;AACJ,CAAC;AAED,MAAa,+BAA+B,gBAAyC;CACnF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAAwB,UAAU;CAAS;CAC5D,QAAQ,EACN,OAAO;EAAE,MAAM;EAAU,SAAS;EAAU,OAAO;CAAe,EACpE;CACA,WACE;AACJ,CAAC;AAED,MAAa,qCAAqC,gBAAyC;CACzF,MAAM;CACN,YAAY;CACZ,WAAW;CACX,iBAAiB;CACjB,QAAQ;EAAE,OAAO;EAA+B,UAAU;CAAS;CACnE,QAAQ,EACN,OAAO;EAAE,MAAM;EAAU,SAAS;EAAY,OAAO;CAAe,EACtE;CACA,WACE;AACJ,CAAC;;;;;;;;AAaD,SAAgB,uBAAuB,EAAE,YAAyC;CAChF,MAAM,CAAC,MAAM,WAAW,SAA2B,MAAM;CAEzD,MAAM,QAAQ,eAAyC;EAAE;EAAM;CAAQ,IAAI,CAAC,IAAI,CAAC;CAEjF,OAAO,oBAAC,oBAAoB,UAArB;EAAqC;EAAQ;CAAuC,CAAA;AAC7F;;AAGA,SAAgB,oBAAoB,EAAE,YAAoC;CACxE,MAAM,EAAE,SAAS,uBAAuB;CACxC,IAAI,SAAS,QAAQ,OAAO;CAC5B,OAAO,oBAAA,UAAA,EAAG,SAAW,CAAA;AACvB;;AAGA,SAAgB,iBAAiB,EAAE,YAAoC;CACrE,MAAM,EAAE,SAAS,uBAAuB;CACxC,IAAI,SAAS,QAAQ,OAAO;CAC5B,OAAO,oBAAA,UAAA,EAAG,SAAW,CAAA;AACvB;;AAGA,SAAgB,mBAAmB,EAAE,QAAQ,UAAU,WAAW,YAAqC;CACrG,MAAM,EAAE,YAAY,uBAAuB;CAC3C,OACE,oBAAC,UAAD;EAAQ,MAAK;EAAS,gCAA6B;EAAc;EAAW,eAAe,QAAQ,MAAM;EACtG,UAAA,YAAY;CACP,CAAA;AAEZ;;AAGA,SAAgB,yBAAyB,EAAE,QAAQ,YAAY,WAAW,YAAqC;CAC7G,MAAM,EAAE,YAAY,uBAAuB;CAC3C,OACE,oBAAC,UAAD;EAAQ,MAAK;EAAS,uCAAoC;EAAc;EAAW,eAAe,QAAQ,MAAM;EAC7G,UAAA,YAAY;CACP,CAAA;AAEZ;AAMA,MAAa,eAAe;CAC1B,YAAY;CACZ,sBAAsB;CACtB,aAAa;CACb,uBAAuB;CACvB,MAAM;CACN,gBAAgB;CAChB,YAAY;CACZ,sBAAsB;CACtB,cAAc;CACd,wBAAwB;AAC1B"}
|
|
@@ -47,7 +47,7 @@ const IdentitySummaryRootDefinition = defineComponent({
|
|
|
47
47
|
default: "Unable to sign out. Please try again.",
|
|
48
48
|
label: "Sign-out error message"
|
|
49
49
|
} },
|
|
50
|
-
guideline: "Read-only identity summary data root: reads the shopper profile, provides it plus a sign-out action to descendants, and self-gates (renders nothing in the anonymous state). Must be inside SessionRoot. Wrap the parts in an IdentityEditController for the summary/form swap: an IdentitySummaryView holding these parts, an IdentityEditForm holding a
|
|
50
|
+
guideline: "Read-only identity summary data root: reads the shopper profile, provides it plus a sign-out action to descendants, and self-gates (renders nothing in the anonymous state). Must be inside SessionRoot. Wrap the parts in an IdentityEditController for the summary/form swap: an IdentitySummaryView holding these parts, an IdentityEditForm holding a FormRoot over ClientProfileFieldset. See identity-summary.md."
|
|
51
51
|
});
|
|
52
52
|
const IdentitySummarySelectedSummaryDefinition = defineComponent({
|
|
53
53
|
name: "IdentitySummarySelectedSummary",
|
|
@@ -182,7 +182,7 @@ function IdentitySummaryRoot({ signOutErrorMessage = "Unable to sign out. Please
|
|
|
182
182
|
const errors = await $actions?.session?.logout?.() ?? [];
|
|
183
183
|
if (errors.length > 0) setSignOutError(errors[0]?.message ?? signOutErrorMessage);
|
|
184
184
|
} catch (error) {
|
|
185
|
-
console.error("[IdentitySummary] logout failed:", error);
|
|
185
|
+
console.error("[IdentitySummary] logout failed:", error instanceof Error ? error.name : "UnknownError");
|
|
186
186
|
setSignOutError(signOutErrorMessage);
|
|
187
187
|
} finally {
|
|
188
188
|
setSignOutPending(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-summary.js","names":[],"sources":["../../../src/matter/identity-summary/identity-summary.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport { createContext, type ReactNode, useContext, useState } from 'react';\nimport { Badge } from '../../particles/badge/badge.js';\nimport { applyPhoneMask, type PhoneCountry } from '../../particles/phone-field/phone-field.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport type { SessionUserError } from '../../sdk/session/auth-operations.js';\nimport { type SessionState, useSession } from '../session/session.js';\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — colocated self-contained data root (ADR-0011).\n// Reads only `clientProfile` (the fields this read-only summary displays). The\n// edit surface is a separately-composed `ClientProfileForm`, which owns its own\n// fragment and identity actions.\n//\n// Single top-level entity → the compiler delivers `$data` UNWRAPPED, i.e. the\n// `ClientProfile` value directly, not `{ clientProfile: ... }` (ADR-0015 arity-1).\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment IdentitySummaryData on Query {\n clientProfile {\n __typename\n id\n email\n firstName\n lastName\n phone\n document\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * The `$data` slice forwarded by the reactive runtime — the unwrapped `ClientProfile` entity.\n * The fragment selects a single top-level field, so the compiler hands the entity value directly\n * (not a `{ clientProfile: ... }` wrapper); ADR-0015.\n */\nexport type IdentitySummaryData = {\n email?: string | null;\n firstName?: string | null;\n lastName?: string | null;\n phone?: string | null;\n document?: string | null;\n} | null;\n\n// ---------------------------------------------------------------------------\n// Context — data + session state + sign-out, provided by IdentitySummaryRoot.\n// The summary/form swap is owned by `IdentityEditController`, which moves only on its own buttons\n// (ADR-0006) — this matter owns only the identity data and the sign-out action, never a bespoke toggle.\n// ---------------------------------------------------------------------------\n\ntype IdentitySummaryContextValue = {\n state: SessionState;\n firstName: string;\n lastName: string;\n email: string;\n phone: string;\n document: string;\n handleSignOut: () => void;\n signOutPending: boolean;\n signOutError: string | null;\n};\n\nconst IDENTITY_SUMMARY_CONTEXT_UNSET = Symbol('IdentitySummaryContextUnset');\n\nconst IdentitySummaryContext = createContext<IdentitySummaryContextValue | typeof IDENTITY_SUMMARY_CONTEXT_UNSET>(\n IDENTITY_SUMMARY_CONTEXT_UNSET,\n);\n\nfunction useIdentitySummaryContext(): IdentitySummaryContextValue {\n const ctx = useContext(IdentitySummaryContext);\n if (ctx === IDENTITY_SUMMARY_CONTEXT_UNSET) {\n throw new Error('IdentitySummary components must be used inside IdentitySummaryRoot.');\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport type IdentitySummaryRootProps = {\n /** Error message shown when the sign-out mutation returns user errors. */\n signOutErrorMessage?: string;\n className?: string;\n children?: ReactNode;\n /** `IdentitySummaryData` slice forwarded by the reactive runtime. */\n $data?: IdentitySummaryData;\n /** Compiler-injected action surface; carries `session.logout`. */\n $actions?: ComponentActions;\n};\n\nexport type IdentitySummarySelectedSummaryProps = {\n /** Phone display country; selects the mask applied to the stored E.164 value. */\n country?: PhoneCountry;\n /** Badge label in the `identified` session state. */\n identifiedBadgeLabel?: string;\n /** Badge label in the `loggedIn` session state. */\n loggedInBadgeLabel?: string;\n className?: string;\n /** Compose custom read-only content; falls back to the default badge + details when omitted. */\n children?: ReactNode;\n};\n\nexport type IdentitySummaryBadgeProps = {\n /** Badge label in the `identified` session state. */\n identifiedLabel?: string;\n /** Badge label in the `loggedIn` session state. */\n loggedInLabel?: string;\n className?: string;\n};\n\nexport type IdentitySummaryFieldProps = {\n className?: string;\n};\n\nexport type IdentitySummaryPhoneProps = {\n /** Phone display country; selects the mask applied to the stored E.164 value. */\n country?: PhoneCountry;\n className?: string;\n};\n\nexport type IdentitySummarySignOutButtonProps = {\n label?: string;\n className?: string;\n children?: ReactNode;\n};\n\nexport type IdentitySummaryErrorProps = {\n className?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const IdentitySummaryRootDefinition = defineComponent<IdentitySummaryRootProps>({\n name: 'IdentitySummaryRoot',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryRoot,\n fragment,\n fetchStrategy: 'reactive',\n requiresContext: 'session',\n providesContext: 'identity-summary',\n actions: { session: ['logout'] },\n editor: { label: 'Identity Summary Root', category: 'identity' },\n schema: {\n signOutErrorMessage: {\n type: 'string',\n default: 'Unable to sign out. Please try again.',\n label: 'Sign-out error message',\n },\n },\n guideline:\n 'Read-only identity summary data root: reads the shopper profile, provides it plus a sign-out action to descendants, and self-gates (renders nothing in the anonymous state). Must be inside SessionRoot. Wrap the parts in an IdentityEditController for the summary/form swap: an IdentitySummaryView holding these parts, an IdentityEditForm holding a ClientProfileForm. See identity-summary.md.',\n});\n\nexport const IdentitySummarySelectedSummaryDefinition = defineComponent<IdentitySummarySelectedSummaryProps>({\n name: 'IdentitySummarySelectedSummary',\n importPath: '@vtex/faststore/components',\n component: IdentitySummarySelectedSummary,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Selected Summary', category: 'identity' },\n schema: {},\n guideline:\n 'Default composition of the identity summary: badge, name, e-mail, phone, document, in that order. It is a shortcut, not the only way — to change the order, drop a field, or interleave anything else, compose IdentitySummaryBadge / Name / Email / Phone / Document directly instead. It offers nothing to the schema: its country and badge-label props configure the default composition only, and are withheld from the schema because a page that set one here and then composed children would watch it vanish. A composed summary configures the leaf instead — the phone mask country on IdentitySummaryPhone, the labels on IdentitySummaryBadge. Must be a descendant of IdentitySummaryRoot.',\n});\n\nexport const IdentitySummaryBadgeDefinition = defineComponent<IdentitySummaryBadgeProps>({\n name: 'IdentitySummaryBadge',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryBadge,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Badge', category: 'identity' },\n schema: {\n identifiedLabel: { type: 'string', default: 'Guest', label: 'Identified badge label' },\n loggedInLabel: { type: 'string', default: 'Signed in', label: 'Logged-in badge label' },\n },\n guideline:\n 'Session-state badge for the identity summary. Place it wherever the summary should carry it; order is composition, not a prop. Must be a descendant of IdentitySummaryRoot.',\n});\n\nexport const IdentitySummaryNameDefinition = defineComponent<IdentitySummaryFieldProps>({\n name: 'IdentitySummaryName',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryName,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Name', category: 'identity' },\n schema: {},\n guideline:\n \"The shopper's full name, or nothing when the profile has neither first nor last name. Place it in whatever order the summary should read. Must be a descendant of IdentitySummaryRoot.\",\n});\n\nexport const IdentitySummaryEmailDefinition = defineComponent<IdentitySummaryFieldProps>({\n name: 'IdentitySummaryEmail',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryEmail,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Email', category: 'identity' },\n schema: {},\n guideline: \"The shopper's e-mail, or nothing when absent. Must be a descendant of IdentitySummaryRoot.\",\n});\n\nexport const IdentitySummaryPhoneDefinition = defineComponent<IdentitySummaryPhoneProps>({\n name: 'IdentitySummaryPhone',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryPhone,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Phone', category: 'identity' },\n schema: {\n country: { type: 'string', label: 'Phone country (US or BR)' },\n },\n guideline:\n \"The shopper's phone, or nothing when absent. Masked for `country` when one is set; rendered as stored otherwise, since each mask truncates to its own country's length. Must be a descendant of IdentitySummaryRoot.\",\n});\n\nexport const IdentitySummaryDocumentDefinition = defineComponent<IdentitySummaryFieldProps>({\n name: 'IdentitySummaryDocument',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryDocument,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Document', category: 'identity' },\n schema: {},\n guideline: \"The shopper's tax document, or nothing when absent. Must be a descendant of IdentitySummaryRoot.\",\n});\n\nexport const IdentitySummarySignOutButtonDefinition = defineComponent<IdentitySummarySignOutButtonProps>({\n name: 'IdentitySummarySignOutButton',\n importPath: '@vtex/faststore/components',\n component: IdentitySummarySignOutButton,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Sign Out Button', category: 'action' },\n schema: {\n label: { type: 'string', default: 'Sign out', label: 'Button label' },\n },\n guideline:\n 'Dispatches $actions.session.logout (async/pessimistic): carries data-fs-identity-summary-pending while in flight and disables itself. Compose an Icon + text as children, or rely on `label`. Must be a descendant of IdentitySummaryRoot.',\n});\n\nexport const IdentitySummaryErrorDefinition = defineComponent<IdentitySummaryErrorProps>({\n name: 'IdentitySummaryError',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryError,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Error', category: 'content' },\n schema: {},\n guideline:\n 'Renders the inline sign-out error (role=\"alert\") when the logout mutation returns a user error, else nothing. Must be a descendant of IdentitySummaryRoot.',\n});\n\n// ---------------------------------------------------------------------------\n// Root\n// ---------------------------------------------------------------------------\n\n/**\n * Data root for the identity summary. Reads the shopper profile from its colocated fragment,\n * enforces the SessionRoot ancestor via `useSession()`, and self-gates to null in the anonymous\n * state. Owns the sign-out mutation state (pending/error) and broadcasts everything the read-only\n * parts and the sign-out button need through `IdentitySummaryContext`.\n */\nexport function IdentitySummaryRoot({\n signOutErrorMessage = 'Unable to sign out. Please try again.',\n className,\n children,\n $data: $dataProp,\n $actions: $actionsProp,\n}: Readonly<IdentitySummaryRootProps>) {\n const $data = useDataOverride($dataProp);\n const $actions = useActionsOverride($actionsProp);\n // Enforces the SessionRoot ancestor: useSession() throws when rendered outside one.\n const { state } = useSession();\n\n const [signOutPending, setSignOutPending] = useState(false);\n const [signOutError, setSignOutError] = useState<string | null>(null);\n\n async function handleSignOut() {\n setSignOutPending(true);\n setSignOutError(null);\n try {\n const errors: readonly SessionUserError[] = (await $actions?.session?.logout?.()) ?? [];\n if (errors.length > 0) setSignOutError(errors[0]?.message ?? signOutErrorMessage);\n } catch (error) {\n // A logout that throws rather than resolving userErrors would otherwise leave the shopper still\n // signed in with nothing on screen, and escape as an unhandled rejection.\n console.error('[IdentitySummary] logout failed:', error);\n setSignOutError(signOutErrorMessage);\n } finally {\n setSignOutPending(false);\n }\n }\n\n // Self-gate: nothing to show for an anonymous or unresolved session.\n if (state === 'anonymous') return null;\n\n const contextValue: IdentitySummaryContextValue = {\n state,\n firstName: $data?.firstName ?? '',\n lastName: $data?.lastName ?? '',\n email: $data?.email ?? '',\n phone: $data?.phone ?? '',\n document: $data?.document ?? '',\n handleSignOut,\n signOutPending,\n signOutError,\n };\n\n return (\n <IdentitySummaryContext.Provider value={contextValue}>\n <div\n className={className}\n data-fs-identity-summary=\"\"\n {...(signOutPending ? { 'data-fs-identity-summary-pending': '' } : {})}\n >\n {children}\n </div>\n </IdentitySummaryContext.Provider>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Read-only summary\n// ---------------------------------------------------------------------------\n\n/**\n * Read-only identity display: a session-state badge plus name, email, phone, and document.\n *\n * The props below configure the default composition only — composing children replaces it wholesale,\n * and a leaf placed there takes its own props (`IdentitySummaryPhone` its `country`,\n * `IdentitySummaryBadge` its labels). They are deliberately absent from the schema, so a page cannot\n * set one here and watch it disappear behind composed children.\n */\nexport function IdentitySummarySelectedSummary({\n country,\n identifiedBadgeLabel = 'Guest',\n loggedInBadgeLabel = 'Signed in',\n className,\n children,\n}: IdentitySummarySelectedSummaryProps) {\n return (\n <div className={className} data-fs-identity-summary-summary=\"\">\n {children ?? (\n <>\n <IdentitySummaryBadge identifiedLabel={identifiedBadgeLabel} loggedInLabel={loggedInBadgeLabel} />\n <IdentitySummaryName />\n <IdentitySummaryEmail />\n <IdentitySummaryPhone country={country} />\n <IdentitySummaryDocument />\n </>\n )}\n </div>\n );\n}\n\n/** Session-state badge. Renders `identifiedLabel` for a guest and `loggedInLabel` once authenticated. */\nexport function IdentitySummaryBadge({\n identifiedLabel = 'Guest',\n loggedInLabel = 'Signed in',\n className,\n}: IdentitySummaryBadgeProps) {\n const { state } = useIdentitySummaryContext();\n return (\n <Badge className={className} data-fs-identity-summary-badge=\"\" data-fs-identity-summary-state={state}>\n {state === 'loggedIn' ? loggedInLabel : identifiedLabel}\n </Badge>\n );\n}\n\n/** The shopper's full name. Renders nothing when the profile carries neither name. */\nexport function IdentitySummaryName({ className }: IdentitySummaryFieldProps) {\n const { firstName, lastName } = useIdentitySummaryContext();\n const fullName = [firstName, lastName].filter(Boolean).join(' ');\n if (!fullName) return null;\n return (\n <p className={className} data-fs-identity-summary-name=\"\">\n {fullName}\n </p>\n );\n}\n\n/** The shopper's e-mail. Renders nothing when absent. */\nexport function IdentitySummaryEmail({ className }: IdentitySummaryFieldProps) {\n const { email } = useIdentitySummaryContext();\n if (!email) return null;\n return (\n <p className={className} data-fs-identity-summary-email=\"\">\n {email}\n </p>\n );\n}\n\n/** E.164 prefixes for the countries the masks understand. */\nconst COUNTRY_PREFIX: Record<PhoneCountry, string> = { BR: '+55', US: '+1' };\n\n/**\n * The shopper's phone, masked for `country`. Renders nothing when absent, and renders the stored value\n * as-is whenever the mask does not apply to it — no country given, or a stored E.164 number belonging to\n * a different one. Each mask truncates to its own country's length, so applying the wrong one prints a\n * number that is not the shopper's, with nothing to signal that digits were dropped.\n */\nexport function IdentitySummaryPhone({ country, className }: IdentitySummaryPhoneProps) {\n const { phone } = useIdentitySummaryContext();\n if (!phone) return null;\n const maskApplies = country !== undefined && (!phone.startsWith('+') || phone.startsWith(COUNTRY_PREFIX[country]));\n return (\n <p className={className} data-fs-identity-summary-phone=\"\">\n {maskApplies && country ? applyPhoneMask(phone, country) : phone}\n </p>\n );\n}\n\n/** The shopper's tax document. Renders nothing when absent. */\nexport function IdentitySummaryDocument({ className }: IdentitySummaryFieldProps) {\n const { document } = useIdentitySummaryContext();\n if (!document) return null;\n return (\n <p className={className} data-fs-identity-summary-document=\"\">\n {document}\n </p>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Sign-out button + error (domain-coupled; the summary/form swap is IdentityEditController's)\n// ---------------------------------------------------------------------------\n\n/** Dispatches the sign-out mutation (async/pessimistic). */\nexport function IdentitySummarySignOutButton({\n label = 'Sign out',\n className,\n children,\n}: IdentitySummarySignOutButtonProps) {\n const { handleSignOut, signOutPending } = useIdentitySummaryContext();\n return (\n <button\n type=\"button\"\n data-fs-identity-summary-sign-out=\"\"\n className={className}\n disabled={signOutPending}\n {...(signOutPending ? { 'data-fs-identity-summary-pending': '' } : {})}\n onClick={handleSignOut}\n >\n {children ?? label}\n </button>\n );\n}\n\n/** Renders the inline sign-out user error, or nothing when there is none. */\nexport function IdentitySummaryError({ className }: IdentitySummaryErrorProps) {\n const { signOutError } = useIdentitySummaryContext();\n if (!signOutError) return null;\n return (\n <div role=\"alert\" className={className} data-fs-identity-summary-error=\"\">\n {signOutError}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace Export\n// ---------------------------------------------------------------------------\n\nexport const IdentitySummary = {\n Badge: IdentitySummaryBadge,\n BadgeDefinition: IdentitySummaryBadgeDefinition,\n Name: IdentitySummaryName,\n NameDefinition: IdentitySummaryNameDefinition,\n Email: IdentitySummaryEmail,\n EmailDefinition: IdentitySummaryEmailDefinition,\n Phone: IdentitySummaryPhone,\n PhoneDefinition: IdentitySummaryPhoneDefinition,\n Document: IdentitySummaryDocument,\n DocumentDefinition: IdentitySummaryDocumentDefinition,\n Root: IdentitySummaryRoot,\n RootDefinition: IdentitySummaryRootDefinition,\n SelectedSummary: IdentitySummarySelectedSummary,\n SelectedSummaryDefinition: IdentitySummarySelectedSummaryDefinition,\n SignOutButton: IdentitySummarySignOutButton,\n SignOutButtonDefinition: IdentitySummarySignOutButtonDefinition,\n Error: IdentitySummaryError,\n ErrorDefinition: IdentitySummaryErrorDefinition,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"identity-summary.js","names":[],"sources":["../../../src/matter/identity-summary/identity-summary.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport { createContext, type ReactNode, useContext, useState } from 'react';\nimport { Badge } from '../../particles/badge/badge.js';\nimport { applyPhoneMask, type PhoneCountry } from '../../particles/phone-field/phone-field.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport { useDataOverride } from '../../sdk/data/data-override-context.js';\nimport type { SessionUserError } from '../../sdk/session/auth-operations.js';\nimport { type SessionState, useSession } from '../session/session.js';\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — colocated self-contained data root (ADR-0011).\n// Reads only `clientProfile` (the fields this read-only summary displays). The\n// edit surface is a separately-composed FormRoot over `ClientProfileFieldset`, which owns its own\n// fragment and identity actions.\n//\n// Single top-level entity → the compiler delivers `$data` UNWRAPPED, i.e. the\n// `ClientProfile` value directly, not `{ clientProfile: ... }` (ADR-0015 arity-1).\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment IdentitySummaryData on Query {\n clientProfile {\n __typename\n id\n email\n firstName\n lastName\n phone\n document\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * The `$data` slice forwarded by the reactive runtime — the unwrapped `ClientProfile` entity.\n * The fragment selects a single top-level field, so the compiler hands the entity value directly\n * (not a `{ clientProfile: ... }` wrapper); ADR-0015.\n */\nexport type IdentitySummaryData = {\n email?: string | null;\n firstName?: string | null;\n lastName?: string | null;\n phone?: string | null;\n document?: string | null;\n} | null;\n\n// ---------------------------------------------------------------------------\n// Context — data + session state + sign-out, provided by IdentitySummaryRoot.\n// The summary/form swap is owned by `IdentityEditController`, which moves only on its own buttons\n// (ADR-0006) — this matter owns only the identity data and the sign-out action, never a bespoke toggle.\n// ---------------------------------------------------------------------------\n\ntype IdentitySummaryContextValue = {\n state: SessionState;\n firstName: string;\n lastName: string;\n email: string;\n phone: string;\n document: string;\n handleSignOut: () => void;\n signOutPending: boolean;\n signOutError: string | null;\n};\n\nconst IDENTITY_SUMMARY_CONTEXT_UNSET = Symbol('IdentitySummaryContextUnset');\n\nconst IdentitySummaryContext = createContext<IdentitySummaryContextValue | typeof IDENTITY_SUMMARY_CONTEXT_UNSET>(\n IDENTITY_SUMMARY_CONTEXT_UNSET,\n);\n\nfunction useIdentitySummaryContext(): IdentitySummaryContextValue {\n const ctx = useContext(IdentitySummaryContext);\n if (ctx === IDENTITY_SUMMARY_CONTEXT_UNSET) {\n throw new Error('IdentitySummary components must be used inside IdentitySummaryRoot.');\n }\n return ctx;\n}\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport type IdentitySummaryRootProps = {\n /** Error message shown when the sign-out mutation returns user errors. */\n signOutErrorMessage?: string;\n className?: string;\n children?: ReactNode;\n /** `IdentitySummaryData` slice forwarded by the reactive runtime. */\n $data?: IdentitySummaryData;\n /** Compiler-injected action surface; carries `session.logout`. */\n $actions?: ComponentActions;\n};\n\nexport type IdentitySummarySelectedSummaryProps = {\n /** Phone display country; selects the mask applied to the stored E.164 value. */\n country?: PhoneCountry;\n /** Badge label in the `identified` session state. */\n identifiedBadgeLabel?: string;\n /** Badge label in the `loggedIn` session state. */\n loggedInBadgeLabel?: string;\n className?: string;\n /** Compose custom read-only content; falls back to the default badge + details when omitted. */\n children?: ReactNode;\n};\n\nexport type IdentitySummaryBadgeProps = {\n /** Badge label in the `identified` session state. */\n identifiedLabel?: string;\n /** Badge label in the `loggedIn` session state. */\n loggedInLabel?: string;\n className?: string;\n};\n\nexport type IdentitySummaryFieldProps = {\n className?: string;\n};\n\nexport type IdentitySummaryPhoneProps = {\n /** Phone display country; selects the mask applied to the stored E.164 value. */\n country?: PhoneCountry;\n className?: string;\n};\n\nexport type IdentitySummarySignOutButtonProps = {\n label?: string;\n className?: string;\n children?: ReactNode;\n};\n\nexport type IdentitySummaryErrorProps = {\n className?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const IdentitySummaryRootDefinition = defineComponent<IdentitySummaryRootProps>({\n name: 'IdentitySummaryRoot',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryRoot,\n fragment,\n fetchStrategy: 'reactive',\n requiresContext: 'session',\n providesContext: 'identity-summary',\n actions: { session: ['logout'] },\n editor: { label: 'Identity Summary Root', category: 'identity' },\n schema: {\n signOutErrorMessage: {\n type: 'string',\n default: 'Unable to sign out. Please try again.',\n label: 'Sign-out error message',\n },\n },\n guideline:\n 'Read-only identity summary data root: reads the shopper profile, provides it plus a sign-out action to descendants, and self-gates (renders nothing in the anonymous state). Must be inside SessionRoot. Wrap the parts in an IdentityEditController for the summary/form swap: an IdentitySummaryView holding these parts, an IdentityEditForm holding a FormRoot over ClientProfileFieldset. See identity-summary.md.',\n});\n\nexport const IdentitySummarySelectedSummaryDefinition = defineComponent<IdentitySummarySelectedSummaryProps>({\n name: 'IdentitySummarySelectedSummary',\n importPath: '@vtex/faststore/components',\n component: IdentitySummarySelectedSummary,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Selected Summary', category: 'identity' },\n schema: {},\n guideline:\n 'Default composition of the identity summary: badge, name, e-mail, phone, document, in that order. It is a shortcut, not the only way — to change the order, drop a field, or interleave anything else, compose IdentitySummaryBadge / Name / Email / Phone / Document directly instead. It offers nothing to the schema: its country and badge-label props configure the default composition only, and are withheld from the schema because a page that set one here and then composed children would watch it vanish. A composed summary configures the leaf instead — the phone mask country on IdentitySummaryPhone, the labels on IdentitySummaryBadge. Must be a descendant of IdentitySummaryRoot.',\n});\n\nexport const IdentitySummaryBadgeDefinition = defineComponent<IdentitySummaryBadgeProps>({\n name: 'IdentitySummaryBadge',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryBadge,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Badge', category: 'identity' },\n schema: {\n identifiedLabel: { type: 'string', default: 'Guest', label: 'Identified badge label' },\n loggedInLabel: { type: 'string', default: 'Signed in', label: 'Logged-in badge label' },\n },\n guideline:\n 'Session-state badge for the identity summary. Place it wherever the summary should carry it; order is composition, not a prop. Must be a descendant of IdentitySummaryRoot.',\n});\n\nexport const IdentitySummaryNameDefinition = defineComponent<IdentitySummaryFieldProps>({\n name: 'IdentitySummaryName',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryName,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Name', category: 'identity' },\n schema: {},\n guideline:\n \"The shopper's full name, or nothing when the profile has neither first nor last name. Place it in whatever order the summary should read. Must be a descendant of IdentitySummaryRoot.\",\n});\n\nexport const IdentitySummaryEmailDefinition = defineComponent<IdentitySummaryFieldProps>({\n name: 'IdentitySummaryEmail',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryEmail,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Email', category: 'identity' },\n schema: {},\n guideline: \"The shopper's e-mail, or nothing when absent. Must be a descendant of IdentitySummaryRoot.\",\n});\n\nexport const IdentitySummaryPhoneDefinition = defineComponent<IdentitySummaryPhoneProps>({\n name: 'IdentitySummaryPhone',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryPhone,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Phone', category: 'identity' },\n schema: {\n country: { type: 'string', label: 'Phone country (US or BR)' },\n },\n guideline:\n \"The shopper's phone, or nothing when absent. Masked for `country` when one is set; rendered as stored otherwise, since each mask truncates to its own country's length. Must be a descendant of IdentitySummaryRoot.\",\n});\n\nexport const IdentitySummaryDocumentDefinition = defineComponent<IdentitySummaryFieldProps>({\n name: 'IdentitySummaryDocument',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryDocument,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Document', category: 'identity' },\n schema: {},\n guideline: \"The shopper's tax document, or nothing when absent. Must be a descendant of IdentitySummaryRoot.\",\n});\n\nexport const IdentitySummarySignOutButtonDefinition = defineComponent<IdentitySummarySignOutButtonProps>({\n name: 'IdentitySummarySignOutButton',\n importPath: '@vtex/faststore/components',\n component: IdentitySummarySignOutButton,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Sign Out Button', category: 'action' },\n schema: {\n label: { type: 'string', default: 'Sign out', label: 'Button label' },\n },\n guideline:\n 'Dispatches $actions.session.logout (async/pessimistic): carries data-fs-identity-summary-pending while in flight and disables itself. Compose an Icon + text as children, or rely on `label`. Must be a descendant of IdentitySummaryRoot.',\n});\n\nexport const IdentitySummaryErrorDefinition = defineComponent<IdentitySummaryErrorProps>({\n name: 'IdentitySummaryError',\n importPath: '@vtex/faststore/components',\n component: IdentitySummaryError,\n requiresContext: 'identity-summary',\n editor: { label: 'Identity Summary Error', category: 'content' },\n schema: {},\n guideline:\n 'Renders the inline sign-out error (role=\"alert\") when the logout mutation returns a user error, else nothing. Must be a descendant of IdentitySummaryRoot.',\n});\n\n// ---------------------------------------------------------------------------\n// Root\n// ---------------------------------------------------------------------------\n\n/**\n * Data root for the identity summary. Reads the shopper profile from its colocated fragment,\n * enforces the SessionRoot ancestor via `useSession()`, and self-gates to null in the anonymous\n * state. Owns the sign-out mutation state (pending/error) and broadcasts everything the read-only\n * parts and the sign-out button need through `IdentitySummaryContext`.\n */\nexport function IdentitySummaryRoot({\n signOutErrorMessage = 'Unable to sign out. Please try again.',\n className,\n children,\n $data: $dataProp,\n $actions: $actionsProp,\n}: Readonly<IdentitySummaryRootProps>) {\n const $data = useDataOverride($dataProp);\n const $actions = useActionsOverride($actionsProp);\n // Enforces the SessionRoot ancestor: useSession() throws when rendered outside one.\n const { state } = useSession();\n\n const [signOutPending, setSignOutPending] = useState(false);\n const [signOutError, setSignOutError] = useState<string | null>(null);\n\n async function handleSignOut() {\n setSignOutPending(true);\n setSignOutError(null);\n try {\n const errors: readonly SessionUserError[] = (await $actions?.session?.logout?.()) ?? [];\n if (errors.length > 0) setSignOutError(errors[0]?.message ?? signOutErrorMessage);\n } catch (error) {\n // A logout that throws rather than resolving userErrors would otherwise leave the shopper still\n // signed in with nothing on screen, and escape as an unhandled rejection.\n console.error('[IdentitySummary] logout failed:', error instanceof Error ? error.name : 'UnknownError');\n setSignOutError(signOutErrorMessage);\n } finally {\n setSignOutPending(false);\n }\n }\n\n // Self-gate: nothing to show for an anonymous or unresolved session.\n if (state === 'anonymous') return null;\n\n const contextValue: IdentitySummaryContextValue = {\n state,\n firstName: $data?.firstName ?? '',\n lastName: $data?.lastName ?? '',\n email: $data?.email ?? '',\n phone: $data?.phone ?? '',\n document: $data?.document ?? '',\n handleSignOut,\n signOutPending,\n signOutError,\n };\n\n return (\n <IdentitySummaryContext.Provider value={contextValue}>\n <div\n className={className}\n data-fs-identity-summary=\"\"\n {...(signOutPending ? { 'data-fs-identity-summary-pending': '' } : {})}\n >\n {children}\n </div>\n </IdentitySummaryContext.Provider>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Read-only summary\n// ---------------------------------------------------------------------------\n\n/**\n * Read-only identity display: a session-state badge plus name, email, phone, and document.\n *\n * The props below configure the default composition only — composing children replaces it wholesale,\n * and a leaf placed there takes its own props (`IdentitySummaryPhone` its `country`,\n * `IdentitySummaryBadge` its labels). They are deliberately absent from the schema, so a page cannot\n * set one here and watch it disappear behind composed children.\n */\nexport function IdentitySummarySelectedSummary({\n country,\n identifiedBadgeLabel = 'Guest',\n loggedInBadgeLabel = 'Signed in',\n className,\n children,\n}: IdentitySummarySelectedSummaryProps) {\n return (\n <div className={className} data-fs-identity-summary-summary=\"\">\n {children ?? (\n <>\n <IdentitySummaryBadge identifiedLabel={identifiedBadgeLabel} loggedInLabel={loggedInBadgeLabel} />\n <IdentitySummaryName />\n <IdentitySummaryEmail />\n <IdentitySummaryPhone country={country} />\n <IdentitySummaryDocument />\n </>\n )}\n </div>\n );\n}\n\n/** Session-state badge. Renders `identifiedLabel` for a guest and `loggedInLabel` once authenticated. */\nexport function IdentitySummaryBadge({\n identifiedLabel = 'Guest',\n loggedInLabel = 'Signed in',\n className,\n}: IdentitySummaryBadgeProps) {\n const { state } = useIdentitySummaryContext();\n return (\n <Badge className={className} data-fs-identity-summary-badge=\"\" data-fs-identity-summary-state={state}>\n {state === 'loggedIn' ? loggedInLabel : identifiedLabel}\n </Badge>\n );\n}\n\n/** The shopper's full name. Renders nothing when the profile carries neither name. */\nexport function IdentitySummaryName({ className }: IdentitySummaryFieldProps) {\n const { firstName, lastName } = useIdentitySummaryContext();\n const fullName = [firstName, lastName].filter(Boolean).join(' ');\n if (!fullName) return null;\n return (\n <p className={className} data-fs-identity-summary-name=\"\">\n {fullName}\n </p>\n );\n}\n\n/** The shopper's e-mail. Renders nothing when absent. */\nexport function IdentitySummaryEmail({ className }: IdentitySummaryFieldProps) {\n const { email } = useIdentitySummaryContext();\n if (!email) return null;\n return (\n <p className={className} data-fs-identity-summary-email=\"\">\n {email}\n </p>\n );\n}\n\n/** E.164 prefixes for the countries the masks understand. */\nconst COUNTRY_PREFIX: Record<PhoneCountry, string> = { BR: '+55', US: '+1' };\n\n/**\n * The shopper's phone, masked for `country`. Renders nothing when absent, and renders the stored value\n * as-is whenever the mask does not apply to it — no country given, or a stored E.164 number belonging to\n * a different one. Each mask truncates to its own country's length, so applying the wrong one prints a\n * number that is not the shopper's, with nothing to signal that digits were dropped.\n */\nexport function IdentitySummaryPhone({ country, className }: IdentitySummaryPhoneProps) {\n const { phone } = useIdentitySummaryContext();\n if (!phone) return null;\n const maskApplies = country !== undefined && (!phone.startsWith('+') || phone.startsWith(COUNTRY_PREFIX[country]));\n return (\n <p className={className} data-fs-identity-summary-phone=\"\">\n {maskApplies && country ? applyPhoneMask(phone, country) : phone}\n </p>\n );\n}\n\n/** The shopper's tax document. Renders nothing when absent. */\nexport function IdentitySummaryDocument({ className }: IdentitySummaryFieldProps) {\n const { document } = useIdentitySummaryContext();\n if (!document) return null;\n return (\n <p className={className} data-fs-identity-summary-document=\"\">\n {document}\n </p>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Sign-out button + error (domain-coupled; the summary/form swap is IdentityEditController's)\n// ---------------------------------------------------------------------------\n\n/** Dispatches the sign-out mutation (async/pessimistic). */\nexport function IdentitySummarySignOutButton({\n label = 'Sign out',\n className,\n children,\n}: IdentitySummarySignOutButtonProps) {\n const { handleSignOut, signOutPending } = useIdentitySummaryContext();\n return (\n <button\n type=\"button\"\n data-fs-identity-summary-sign-out=\"\"\n className={className}\n disabled={signOutPending}\n {...(signOutPending ? { 'data-fs-identity-summary-pending': '' } : {})}\n onClick={handleSignOut}\n >\n {children ?? label}\n </button>\n );\n}\n\n/** Renders the inline sign-out user error, or nothing when there is none. */\nexport function IdentitySummaryError({ className }: IdentitySummaryErrorProps) {\n const { signOutError } = useIdentitySummaryContext();\n if (!signOutError) return null;\n return (\n <div role=\"alert\" className={className} data-fs-identity-summary-error=\"\">\n {signOutError}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace Export\n// ---------------------------------------------------------------------------\n\nexport const IdentitySummary = {\n Badge: IdentitySummaryBadge,\n BadgeDefinition: IdentitySummaryBadgeDefinition,\n Name: IdentitySummaryName,\n NameDefinition: IdentitySummaryNameDefinition,\n Email: IdentitySummaryEmail,\n EmailDefinition: IdentitySummaryEmailDefinition,\n Phone: IdentitySummaryPhone,\n PhoneDefinition: IdentitySummaryPhoneDefinition,\n Document: IdentitySummaryDocument,\n DocumentDefinition: IdentitySummaryDocumentDefinition,\n Root: IdentitySummaryRoot,\n RootDefinition: IdentitySummaryRootDefinition,\n SelectedSummary: IdentitySummarySelectedSummary,\n SelectedSummaryDefinition: IdentitySummarySelectedSummaryDefinition,\n SignOutButton: IdentitySummarySignOutButton,\n SignOutButtonDefinition: IdentitySummarySignOutButtonDefinition,\n Error: IdentitySummaryError,\n ErrorDefinition: IdentitySummaryErrorDefinition,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ComponentDefinition } from "../../core/dist/define-component.js";
|
|
2
|
+
import "../../core/dist/index.js";
|
|
3
|
+
import { ComponentActions } from "../../sdk/actions/types.js";
|
|
4
|
+
import { InstallmentOption } from "./installments.js";
|
|
5
|
+
import { ComponentPropsWithRef } from "react";
|
|
6
|
+
//#region src/matter/installments/installments-select.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* `$data` forwarded by the reactive runtime — the unwrapped value of the `payment` top-level field on
|
|
9
|
+
* `InstallmentsSelectData on Query` (arity-1 fragment, ADR-0015), not a `{ payment: … }` wrapper.
|
|
10
|
+
* Every field optional, mirroring `InstallmentsData`: `$data` arrives partial while the entity
|
|
11
|
+
* resolves.
|
|
12
|
+
*/
|
|
13
|
+
export type InstallmentsSelectData = {
|
|
14
|
+
installmentOptions?: readonly InstallmentOption[];
|
|
15
|
+
selectedInstallment?: {
|
|
16
|
+
count: number;
|
|
17
|
+
} | null;
|
|
18
|
+
} | null;
|
|
19
|
+
export type InstallmentsSelectProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {
|
|
20
|
+
/** Placeholders `{count}` / `{value}`, matching `Installments`'/`PaymentSummary`'s convention. */
|
|
21
|
+
installmentTemplate?: string;
|
|
22
|
+
/** Appended when a tier's `interestRate` is `0`. */
|
|
23
|
+
interestFreeLabel?: string;
|
|
24
|
+
/** Placeholder `{rate}`; appended instead of `interestFreeLabel` when a tier's `interestRate` is above `0`. */
|
|
25
|
+
interestTemplate?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Accessible name for the control, rendered as `Select.Label` (Decision 13, always renders).
|
|
28
|
+
* Resolved through `resolveAuthoredCopy` (T38): an authored empty or whitespace-only string falls
|
|
29
|
+
* back to the default rather than rendering a nameless combobox.
|
|
30
|
+
*/
|
|
31
|
+
label?: string;
|
|
32
|
+
/** Shown on the closed control when the bound count matches no rendered tier (Decision 6/FR-8). */
|
|
33
|
+
placeholder?: string;
|
|
34
|
+
/** `InstallmentsSelectData` slice forwarded by the reactive runtime. */
|
|
35
|
+
$data?: InstallmentsSelectData;
|
|
36
|
+
/** Compiler-injected action surface; carries `payment.setInstallments`. */
|
|
37
|
+
$actions?: ComponentActions;
|
|
38
|
+
};
|
|
39
|
+
export declare const InstallmentsSelectDefinition: ComponentDefinition<InstallmentsSelectProps>;
|
|
40
|
+
export declare function InstallmentsSelect({ installmentTemplate, interestFreeLabel, interestTemplate, label, placeholder, $data, $actions: $actionsProp, className, ...props }: InstallmentsSelectProps): import("react/jsx-runtime").JSX.Element | null;
|
|
41
|
+
//#endregion
|
|
42
|
+
//# sourceMappingURL=installments-select.d.ts.map
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { defineComponent } from "../../core/dist/define-component.js";
|
|
3
|
+
import "../../core/dist/index.js";
|
|
4
|
+
import { useStorefrontContext } from "../../sdk/context/storefront-context.js";
|
|
5
|
+
import { useActionsOverride } from "../../sdk/actions/actions-override-context.js";
|
|
6
|
+
import { resolveAuthoredCopy } from "../../utils/resolve-authored-copy.js";
|
|
7
|
+
import { Select } from "../../particles/select/select.js";
|
|
8
|
+
import { useInstallmentsSelection } from "./use-installments-selection.js";
|
|
9
|
+
import { installmentLabel } from "./installments.js";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
const InstallmentsSelectDefinition = defineComponent({
|
|
12
|
+
name: "InstallmentsSelect",
|
|
13
|
+
importPath: "@vtex/faststore/components",
|
|
14
|
+
component: InstallmentsSelect,
|
|
15
|
+
fragment: `
|
|
16
|
+
fragment InstallmentsSelectData on Query {
|
|
17
|
+
payment {
|
|
18
|
+
__typename
|
|
19
|
+
id
|
|
20
|
+
installmentOptions {
|
|
21
|
+
__typename
|
|
22
|
+
methodId
|
|
23
|
+
bin
|
|
24
|
+
installments {
|
|
25
|
+
__typename
|
|
26
|
+
count
|
|
27
|
+
value {
|
|
28
|
+
amount
|
|
29
|
+
currencyCode
|
|
30
|
+
}
|
|
31
|
+
interestRate
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
selectedInstallment {
|
|
35
|
+
__typename
|
|
36
|
+
count
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
`,
|
|
41
|
+
fetchStrategy: "reactive",
|
|
42
|
+
requiresContext: "payment",
|
|
43
|
+
actions: { payment: ["setInstallments"] },
|
|
44
|
+
docPath: "./installments.md",
|
|
45
|
+
editor: {
|
|
46
|
+
label: "Installments Select",
|
|
47
|
+
category: "commerce"
|
|
48
|
+
},
|
|
49
|
+
schema: {
|
|
50
|
+
installmentTemplate: {
|
|
51
|
+
type: "string",
|
|
52
|
+
default: "{count}x of {value}",
|
|
53
|
+
label: "Installment template"
|
|
54
|
+
},
|
|
55
|
+
interestFreeLabel: {
|
|
56
|
+
type: "string",
|
|
57
|
+
default: "interest-free",
|
|
58
|
+
label: "Interest-free label"
|
|
59
|
+
},
|
|
60
|
+
interestTemplate: {
|
|
61
|
+
type: "string",
|
|
62
|
+
default: "{rate}% interest",
|
|
63
|
+
label: "Interest template"
|
|
64
|
+
},
|
|
65
|
+
label: {
|
|
66
|
+
type: "string",
|
|
67
|
+
default: "Installments",
|
|
68
|
+
label: "Accessible label"
|
|
69
|
+
},
|
|
70
|
+
placeholder: {
|
|
71
|
+
type: "string",
|
|
72
|
+
default: "Select an installment plan",
|
|
73
|
+
label: "Placeholder"
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
guideline: "Collapsed installment picker for checkout: a single closed control (Base UI Select) showing the persisted selection's label, expanding into one option per tier the matched option carries — count, per-installment value, and interest, formatted identically to Installments (the API guarantees a 1x tier is present in every option). Reads the same Query.payment data and dispatches the same $actions.payment.setInstallments as Installments — the two are alternative renderings of one engine, so pick whichever fits the layout: this one for a narrow column, a drawer, or a mobile checkout; Installments when stacked rows fit. Must be inside a PaymentSectionRoot or PaymentSectionController — place it as a sibling of CardEntry and SavedCards, never nested inside either. Renders its own options from real data — no children. The popup is portalled to <body>: the label, trigger, value, icon, and inline error stay reachable from a store's style array, but the option list does not — see installments.md."
|
|
77
|
+
});
|
|
78
|
+
/**
|
|
79
|
+
* Resolves `Select.Root`'s `onValueChange` value into a dispatch, or a no-op. Exported so a test can
|
|
80
|
+
* invoke it directly (Decision 7): `SelectRootProps.onValueChange` is `(value: string | null) => void`,
|
|
81
|
+
* unlike `RadioGroupProps`', and `Number(null)` is `0` — `paymentInstallmentSelect` would reject count
|
|
82
|
+
* `0` as `INVALID_INSTALLMENT_COUNT`. Base UI's `Select.Root` never actually calls `onValueChange` with
|
|
83
|
+
* `null` through a user-reachable path once every `Select.Item` carries a non-null `value` (mirrors
|
|
84
|
+
* `billing-address.tsx`'s own `handleCountryChange` reasoning), so this guard has no interaction that
|
|
85
|
+
* could exercise it end-to-end — the direct-invocation test is the only way to cover it.
|
|
86
|
+
*/
|
|
87
|
+
function handleInstallmentSelectValueChange(value, selectInstallment) {
|
|
88
|
+
if (value === null) return;
|
|
89
|
+
selectInstallment(Number(value));
|
|
90
|
+
}
|
|
91
|
+
function InstallmentsSelect({ installmentTemplate = "{count}x of {value}", interestFreeLabel = "interest-free", interestTemplate = "{rate}% interest", label, placeholder = "Select an installment plan", $data, $actions: $actionsProp, className, ...props }) {
|
|
92
|
+
const $actions = useActionsOverride($actionsProp);
|
|
93
|
+
const { locale } = useStorefrontContext();
|
|
94
|
+
const { tiers, selectedCount, state, isPending, selectionError, selectInstallment, placing, cardPaymentCollapsed } = useInstallmentsSelection({
|
|
95
|
+
$data,
|
|
96
|
+
$actions
|
|
97
|
+
});
|
|
98
|
+
if (cardPaymentCollapsed) return null;
|
|
99
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
100
|
+
...props,
|
|
101
|
+
className,
|
|
102
|
+
inert: placing,
|
|
103
|
+
"aria-disabled": placing || void 0,
|
|
104
|
+
"data-fs-installments": "",
|
|
105
|
+
"data-fs-installments-control": "select",
|
|
106
|
+
"data-fs-installments-state": state,
|
|
107
|
+
"aria-busy": isPending || void 0,
|
|
108
|
+
...placing ? { "data-fs-installments-placing": "" } : {},
|
|
109
|
+
children: [selectionError ? /* @__PURE__ */ jsx("div", {
|
|
110
|
+
role: "alert",
|
|
111
|
+
"data-fs-installments-error": "",
|
|
112
|
+
children: selectionError
|
|
113
|
+
}) : null, tiers.length > 0 ? /* @__PURE__ */ jsxs(Select.Root, {
|
|
114
|
+
value: String(selectedCount),
|
|
115
|
+
onValueChange: (value) => handleInstallmentSelectValueChange(value, selectInstallment),
|
|
116
|
+
disabled: isPending,
|
|
117
|
+
children: [
|
|
118
|
+
/* @__PURE__ */ jsx(Select.Label, {
|
|
119
|
+
"data-fs-installments-label": "",
|
|
120
|
+
children: resolveAuthoredCopy(label, "Installments")
|
|
121
|
+
}),
|
|
122
|
+
/* @__PURE__ */ jsxs(Select.Trigger, {
|
|
123
|
+
"data-fs-installments-trigger": "",
|
|
124
|
+
disabled: isPending,
|
|
125
|
+
children: [/* @__PURE__ */ jsx(Select.Value, {
|
|
126
|
+
"data-fs-installments-value": "",
|
|
127
|
+
children: (value) => {
|
|
128
|
+
const tier = tiers.find((candidate) => String(candidate.count) === value);
|
|
129
|
+
return tier ? installmentLabel({
|
|
130
|
+
tier,
|
|
131
|
+
locale,
|
|
132
|
+
installmentTemplate,
|
|
133
|
+
interestFreeLabel,
|
|
134
|
+
interestTemplate
|
|
135
|
+
}) : placeholder;
|
|
136
|
+
}
|
|
137
|
+
}), /* @__PURE__ */ jsx(Select.Icon, {
|
|
138
|
+
"data-fs-installments-icon": "",
|
|
139
|
+
children: "▾"
|
|
140
|
+
})]
|
|
141
|
+
}),
|
|
142
|
+
/* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsx(Select.Positioner, { children: /* @__PURE__ */ jsx(Select.Popup, { children: /* @__PURE__ */ jsx(Select.List, { children: tiers.map((tier) => {
|
|
143
|
+
const text = installmentLabel({
|
|
144
|
+
tier,
|
|
145
|
+
locale,
|
|
146
|
+
installmentTemplate,
|
|
147
|
+
interestFreeLabel,
|
|
148
|
+
interestTemplate
|
|
149
|
+
});
|
|
150
|
+
return /* @__PURE__ */ jsx(Select.Item, {
|
|
151
|
+
value: String(tier.count),
|
|
152
|
+
label: text,
|
|
153
|
+
"data-fs-installments-option": "",
|
|
154
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
155
|
+
"data-fs-installments-option-label": "",
|
|
156
|
+
children: text
|
|
157
|
+
})
|
|
158
|
+
}, tier.count);
|
|
159
|
+
}) }) }) }) })
|
|
160
|
+
]
|
|
161
|
+
}) : null]
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
//#endregion
|
|
165
|
+
export { InstallmentsSelect, InstallmentsSelectDefinition, handleInstallmentSelectValueChange };
|
|
166
|
+
|
|
167
|
+
//# sourceMappingURL=installments-select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installments-select.js","names":[],"sources":["../../../src/matter/installments/installments-select.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { Select } from '../../particles/select/select.js';\nimport { useActionsOverride } from '../../sdk/actions/actions-override-context.js';\nimport type { ComponentActions } from '../../sdk/actions/types.js';\nimport { useStorefrontContext } from '../../sdk/context/storefront-context.js';\nimport { resolveAuthoredCopy } from '../../utils/resolve-authored-copy.js';\nimport { type InstallmentOption, installmentLabel } from './installments.js';\nimport { useInstallmentsSelection } from './use-installments-selection.js';\n\n/**\n * `InstallmentsSelect` matter (T90, specs/render-installments-as-a-radio-group.md;\n * docs/features/credit-card/rfc.md § D4, \"a radio/select\"). The collapsed sibling of `Installments`:\n * the same installment tiers, the same server-bound selection, and the same write path, rendered as a\n * single closed Base UI Select control instead of a stacked `RadioGroup` — for a narrow payment\n * column, a drawer, or a mobile checkout where twelve stacked rows do not fit (Decision 1: a second\n * registered component, never a rendering prop on `Installments`).\n *\n * Reads and dispatches through `useInstallmentsSelection()` — the same engine `Installments` uses —\n * so matching, tier resolution, state derivation, the BIN-confirmation pending gate, and the dispatch\n * itself cannot drift between the two surfaces (Decision 3). Formats each option with the same\n * `installmentLabel` function the radio surface uses (FR-5), driven by the same four authorable copy\n * props with the same shipped pt-BR defaults, so a store moving between surfaces carries its copy\n * across unchanged.\n *\n * Declares its own `fragment InstallmentsSelectData on Query`, selection-set-identical to\n * `InstallmentsData` under a distinct name (Decision 4) — two registered components must not share one\n * fragment *name*, which would collide once both sit on a page.\n *\n * The popup is composed inside `Select.Portal`, teleported to `<body>` (Decision 5, reversing this\n * spec's own first draft): both shipped `Select` consumers (`billing-address.tsx`, `shipping-address.tsx`)\n * do the same, and the payment controller composes this leaf inside overlay popups where the portal's\n * clipping protection matters. The accepted cost — a store's descendant-selector styling and compound\n * swatches cannot reach the popup's internals — is recorded there, not solved here.\n *\n * The closed control's text is resolved locally from this component's own `tiers` (Decision 6), never\n * from Base UI's automatic item-label lookup, so it is correct before the popup has ever been opened;\n * a bound count matching no rendered tier shows the authorable `placeholder` instead of a fabricated\n * label (FR-8). `Select.Label` always renders (Decision 13), giving the control a programmatic\n * accessible name (FR-14) with no mode prop — the FPM forbids a `showX` switch inside one component. No\n * `modal` prop is authored (Decision 14): the particle's own default (`true`) is inherited unchanged,\n * matching both shipped consumers.\n */\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment — raw template literal, identical selection set to `InstallmentsData`\n// (`installments.tsx`) apart from the name (Decision 4; INV-8 asserts the two stay identical).\n// `total` stays deselected — neither surface renders or computes against it. `payment { id }` is\n// mandatory: it is what lets the action's echoed `payment` re-normalize the entity these fragments\n// read.\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment InstallmentsSelectData on Query {\n payment {\n __typename\n id\n installmentOptions {\n __typename\n methodId\n bin\n installments {\n __typename\n count\n value {\n amount\n currencyCode\n }\n interestRate\n }\n }\n selectedInstallment {\n __typename\n count\n }\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * `$data` forwarded by the reactive runtime — the unwrapped value of the `payment` top-level field on\n * `InstallmentsSelectData on Query` (arity-1 fragment, ADR-0015), not a `{ payment: … }` wrapper.\n * Every field optional, mirroring `InstallmentsData`: `$data` arrives partial while the entity\n * resolves.\n */\nexport type InstallmentsSelectData = {\n installmentOptions?: readonly InstallmentOption[];\n selectedInstallment?: { count: number } | null;\n} | null;\n\nexport type InstallmentsSelectProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {\n /** Placeholders `{count}` / `{value}`, matching `Installments`'/`PaymentSummary`'s convention. */\n installmentTemplate?: string;\n /** Appended when a tier's `interestRate` is `0`. */\n interestFreeLabel?: string;\n /** Placeholder `{rate}`; appended instead of `interestFreeLabel` when a tier's `interestRate` is above `0`. */\n interestTemplate?: string;\n /**\n * Accessible name for the control, rendered as `Select.Label` (Decision 13, always renders).\n * Resolved through `resolveAuthoredCopy` (T38): an authored empty or whitespace-only string falls\n * back to the default rather than rendering a nameless combobox.\n */\n label?: string;\n /** Shown on the closed control when the bound count matches no rendered tier (Decision 6/FR-8). */\n placeholder?: string;\n /** `InstallmentsSelectData` slice forwarded by the reactive runtime. */\n $data?: InstallmentsSelectData;\n /** Compiler-injected action surface; carries `payment.setInstallments`. */\n $actions?: ComponentActions;\n};\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const InstallmentsSelectDefinition = defineComponent<InstallmentsSelectProps>({\n name: 'InstallmentsSelect',\n importPath: '@vtex/faststore/components',\n component: InstallmentsSelect,\n fragment,\n fetchStrategy: 'reactive',\n requiresContext: 'payment',\n actions: { payment: ['setInstallments'] },\n // Required: the catalog builder resolves a doc by the `<basename>.md` convention\n // (`installments-select.md`, which does not exist) and silently drops the key without an explicit\n // docPath — mirrors `PaymentSectionController`'s identical need (`payment-controller.tsx:113`).\n docPath: './installments.md',\n editor: { label: 'Installments Select', category: 'commerce' },\n schema: {\n installmentTemplate: { type: 'string', default: '{count}x of {value}', label: 'Installment template' },\n interestFreeLabel: { type: 'string', default: 'interest-free', label: 'Interest-free label' },\n interestTemplate: { type: 'string', default: '{rate}% interest', label: 'Interest template' },\n label: { type: 'string', default: 'Installments', label: 'Accessible label' },\n placeholder: { type: 'string', default: 'Select an installment plan', label: 'Placeholder' },\n },\n guideline:\n \"Collapsed installment picker for checkout: a single closed control (Base UI Select) showing the persisted selection's label, expanding into one option per tier the matched option carries — count, per-installment value, and interest, formatted identically to Installments (the API guarantees a 1x tier is present in every option). Reads the same Query.payment data and dispatches the same $actions.payment.setInstallments as Installments — the two are alternative renderings of one engine, so pick whichever fits the layout: this one for a narrow column, a drawer, or a mobile checkout; Installments when stacked rows fit. Must be inside a PaymentSectionRoot or PaymentSectionController — place it as a sibling of CardEntry and SavedCards, never nested inside either. Renders its own options from real data — no children. The popup is portalled to <body>: the label, trigger, value, icon, and inline error stay reachable from a store's style array, but the option list does not — see installments.md.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Resolves `Select.Root`'s `onValueChange` value into a dispatch, or a no-op. Exported so a test can\n * invoke it directly (Decision 7): `SelectRootProps.onValueChange` is `(value: string | null) => void`,\n * unlike `RadioGroupProps`', and `Number(null)` is `0` — `paymentInstallmentSelect` would reject count\n * `0` as `INVALID_INSTALLMENT_COUNT`. Base UI's `Select.Root` never actually calls `onValueChange` with\n * `null` through a user-reachable path once every `Select.Item` carries a non-null `value` (mirrors\n * `billing-address.tsx`'s own `handleCountryChange` reasoning), so this guard has no interaction that\n * could exercise it end-to-end — the direct-invocation test is the only way to cover it.\n */\nexport function handleInstallmentSelectValueChange(\n value: string | null,\n selectInstallment: (count: number) => void,\n): void {\n if (value === null) return;\n selectInstallment(Number(value));\n}\n\nexport function InstallmentsSelect({\n installmentTemplate = '{count}x of {value}',\n interestFreeLabel = 'interest-free',\n interestTemplate = '{rate}% interest',\n label,\n placeholder = 'Select an installment plan',\n $data,\n $actions: $actionsProp,\n className,\n ...props\n}: InstallmentsSelectProps) {\n const $actions = useActionsOverride($actionsProp);\n // T192: the formatting locale is the storefront's, never an authored prop (RFC localization § 10).\n const { locale } = useStorefrontContext();\n const { tiers, selectedCount, state, isPending, selectionError, selectInstallment, placing, cardPaymentCollapsed } =\n useInstallmentsSelection({\n $data,\n $actions,\n });\n\n // T191 (FR-7): nothing renders while the card payment is collapsed. Returned after the last hook, so\n // this instance stays mounted and keeps its local state.\n if (cardPaymentCollapsed) return null;\n\n return (\n <div\n {...props}\n className={className}\n inert={placing}\n aria-disabled={placing || undefined}\n data-fs-installments=\"\"\n data-fs-installments-control=\"select\"\n data-fs-installments-state={state}\n aria-busy={isPending || undefined}\n {...(placing ? { 'data-fs-installments-placing': '' } : {})}\n >\n {selectionError ? (\n <div role=\"alert\" data-fs-installments-error=\"\">\n {selectionError}\n </div>\n ) : null}\n {tiers.length > 0 ? (\n <Select.Root\n value={String(selectedCount)}\n onValueChange={(value) => handleInstallmentSelectValueChange(value, selectInstallment)}\n disabled={isPending}\n >\n <Select.Label data-fs-installments-label=\"\">{resolveAuthoredCopy(label, 'Installments')}</Select.Label>\n <Select.Trigger data-fs-installments-trigger=\"\" disabled={isPending}>\n <Select.Value data-fs-installments-value=\"\">\n {(value) => {\n const tier = tiers.find((candidate) => String(candidate.count) === value);\n return tier\n ? installmentLabel({ tier, locale, installmentTemplate, interestFreeLabel, interestTemplate })\n : placeholder;\n }}\n </Select.Value>\n <Select.Icon data-fs-installments-icon=\"\">▾</Select.Icon>\n </Select.Trigger>\n <Select.Portal>\n <Select.Positioner>\n <Select.Popup>\n <Select.List>\n {tiers.map((tier) => {\n const text = installmentLabel({\n tier,\n locale,\n installmentTemplate,\n interestFreeLabel,\n interestTemplate,\n });\n return (\n <Select.Item\n key={tier.count}\n value={String(tier.count)}\n label={text}\n data-fs-installments-option=\"\"\n >\n <span data-fs-installments-option-label=\"\">{text}</span>\n </Select.Item>\n );\n })}\n </Select.List>\n </Select.Popup>\n </Select.Positioner>\n </Select.Portal>\n </Select.Root>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ComponentDefinition } from "../../core/dist/define-component.js";
|
|
2
|
+
import "../../core/dist/index.js";
|
|
3
|
+
import { ComponentActions } from "../../sdk/actions/types.js";
|
|
4
|
+
import { findInstallmentOption, resolveInstallmentTiers } from "./use-installments-selection.js";
|
|
5
|
+
import { ComponentPropsWithRef } from "react";
|
|
6
|
+
//#region src/matter/installments/installments.d.ts
|
|
7
|
+
export type InstallmentMoney = Readonly<{
|
|
8
|
+
amount: number;
|
|
9
|
+
currencyCode: string;
|
|
10
|
+
}>;
|
|
11
|
+
export type InstallmentTier = Readonly<{
|
|
12
|
+
count: number;
|
|
13
|
+
value: InstallmentMoney;
|
|
14
|
+
interestRate: number;
|
|
15
|
+
}>;
|
|
16
|
+
export type InstallmentOption = Readonly<{
|
|
17
|
+
methodId: string;
|
|
18
|
+
bin: string | null;
|
|
19
|
+
installments: readonly InstallmentTier[];
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* `$data` forwarded by the reactive runtime — the unwrapped value of the `payment` top-level field
|
|
23
|
+
* on `InstallmentsData on Query` (arity-1 fragment, ADR-0015), not a `{ payment: … }` wrapper.
|
|
24
|
+
* Every field optional, mirroring `PaymentSummaryData` / `PaymentSectionData`: `$data` arrives
|
|
25
|
+
* partial while the entity resolves.
|
|
26
|
+
*/
|
|
27
|
+
export type InstallmentsData = {
|
|
28
|
+
installmentOptions?: readonly InstallmentOption[];
|
|
29
|
+
selectedInstallment?: {
|
|
30
|
+
count: number;
|
|
31
|
+
} | null;
|
|
32
|
+
} | null;
|
|
33
|
+
export type InstallmentsProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {
|
|
34
|
+
/** Placeholders `{count}` / `{value}`, matching `PaymentSummary`'s / `PricingInstallment`'s convention. */
|
|
35
|
+
installmentTemplate?: string;
|
|
36
|
+
/** Appended when a tier's `interestRate` is `0`. */
|
|
37
|
+
interestFreeLabel?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Placeholder `{rate}`; appended instead of `interestFreeLabel` when a tier's `interestRate` is above `0`.
|
|
40
|
+
* `{rate}` is the order form's `interestRate` (hundredths of a percent) divided by 100: `100` renders `1`.
|
|
41
|
+
*/
|
|
42
|
+
interestTemplate?: string;
|
|
43
|
+
/**
|
|
44
|
+
* The radio group's accessible name, rendered as a visible `Fieldset.Legend` (T38, Decision 1).
|
|
45
|
+
* Resolved through `resolveAuthoredCopy`: an authored empty or whitespace-only string falls back to
|
|
46
|
+
* the default rather than rendering a nameless group.
|
|
47
|
+
*/
|
|
48
|
+
legend?: string;
|
|
49
|
+
/** `InstallmentsData` slice forwarded by the reactive runtime. */
|
|
50
|
+
$data?: InstallmentsData;
|
|
51
|
+
/** Compiler-injected action surface; carries `payment.setInstallments`. */
|
|
52
|
+
$actions?: ComponentActions;
|
|
53
|
+
};
|
|
54
|
+
export declare const InstallmentsDefinition: ComponentDefinition<InstallmentsProps>;
|
|
55
|
+
export declare function Installments({ installmentTemplate, interestFreeLabel, interestTemplate, legend, $data, $actions: $actionsProp, className, ...props }: InstallmentsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { findInstallmentOption, resolveInstallmentTiers };
|
|
58
|
+
//# sourceMappingURL=installments.d.ts.map
|