@vtex/faststore 0.0.10 → 0.0.12

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.
Files changed (509) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +0 -1
  3. package/dist/_virtual/_rolldown/runtime.js +4 -0
  4. package/dist/actions.json +6 -1
  5. package/dist/catalog.json +789 -1701
  6. package/dist/cli/index.d.ts +66 -28
  7. package/dist/cli/index.js +7371 -512
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/cli/server-runtime.js +41 -60
  10. package/dist/cli/server-runtime.js.map +1 -1
  11. package/dist/client.d.ts +3 -3
  12. package/dist/client.js +2 -2
  13. package/dist/components.d.ts +63 -57
  14. package/dist/components.js +60 -54
  15. package/dist/content-types.json +5785 -754
  16. package/dist/core/dist/define-component.d.ts +1 -1
  17. package/dist/core/dist/define-content-type.d.ts +50 -2
  18. package/dist/core/dist/define-content-type.js +107 -3
  19. package/dist/core/dist/define-content-type.js.map +1 -1
  20. package/dist/core/dist/index.d.ts +2 -2
  21. package/dist/docs/access-code-field.md +1 -1
  22. package/dist/docs/access-code-sign-in.md +126 -77
  23. package/dist/docs/account-address-book.md +210 -95
  24. package/dist/docs/account-menu.md +14 -15
  25. package/dist/docs/account-saved-cards.md +44 -39
  26. package/dist/docs/alert-dialog.md +3 -1
  27. package/dist/docs/avatar.md +4 -2
  28. package/dist/docs/billing-address.md +99 -30
  29. package/dist/docs/binding-switcher.md +5 -2
  30. package/dist/docs/buy-button.md +7 -8
  31. package/dist/docs/card-entry.md +39 -26
  32. package/dist/docs/card-summary.md +18 -8
  33. package/dist/docs/cardholder-document.md +5 -3
  34. package/dist/docs/carousel.md +5 -4
  35. package/dist/docs/cart-destination.md +39 -26
  36. package/dist/docs/cart-item.md +29 -14
  37. package/dist/docs/cart-totalizer.md +7 -5
  38. package/dist/docs/cart.md +135 -41
  39. package/dist/docs/challenge-return.md +135 -0
  40. package/dist/docs/client-preferences-fieldset.md +10 -5
  41. package/dist/docs/client-preferences.md +31 -8
  42. package/dist/docs/client-profile-fieldset.md +20 -6
  43. package/dist/docs/client-profile.md +12 -3
  44. package/dist/docs/copy-text.md +9 -7
  45. package/dist/docs/document-field.md +1 -1
  46. package/dist/docs/email-field.md +2 -1
  47. package/dist/docs/email-identification.md +12 -2
  48. package/dist/docs/footer-copyright.md +4 -2
  49. package/dist/docs/form.md +54 -18
  50. package/dist/docs/free-shipping-progress.md +6 -5
  51. package/dist/docs/identity-edit.md +8 -2
  52. package/dist/docs/identity-summary.md +18 -5
  53. package/dist/docs/installments.md +46 -32
  54. package/dist/docs/my-account-profile.md +16 -11
  55. package/dist/docs/navigation-link.md +5 -3
  56. package/dist/docs/order-cancel.md +3 -1
  57. package/dist/docs/order-delivery-address.md +7 -5
  58. package/dist/docs/order-delivery-group.md +1 -3
  59. package/dist/docs/order-details-back.md +1 -1
  60. package/dist/docs/order-details-header.md +1 -4
  61. package/dist/docs/order-invoice-download.md +60 -0
  62. package/dist/docs/order-list-row.md +1 -1
  63. package/dist/docs/order-list-search.md +13 -20
  64. package/dist/docs/order-list-views.md +11 -17
  65. package/dist/docs/order-list.md +12 -5
  66. package/dist/docs/order-payment.md +9 -7
  67. package/dist/docs/order-placed-date.md +1 -1
  68. package/dist/docs/order-placed-payment-summary.md +28 -15
  69. package/dist/docs/order-placed.md +49 -14
  70. package/dist/docs/order-progress.md +1 -1
  71. package/dist/docs/order-status-badge.md +20 -9
  72. package/dist/docs/order-summary.md +2 -2
  73. package/dist/docs/order-tracking-link.md +58 -0
  74. package/dist/docs/payment-method-badges.md +30 -4
  75. package/dist/docs/payment-methods.md +1 -1
  76. package/dist/docs/payment.md +36 -17
  77. package/dist/docs/phone-field.md +4 -4
  78. package/dist/docs/place-order.md +84 -36
  79. package/dist/docs/postal-code-field.md +3 -1
  80. package/dist/docs/pricing.md +21 -23
  81. package/dist/docs/product-gallery.md +19 -20
  82. package/dist/docs/product-information.md +13 -9
  83. package/dist/docs/product-list.md +7 -6
  84. package/dist/docs/product-shelf.md +0 -1
  85. package/dist/docs/promotion.md +4 -2
  86. package/dist/docs/region.md +23 -15
  87. package/dist/docs/save-card.md +7 -6
  88. package/dist/docs/saved-cards.md +65 -36
  89. package/dist/docs/shipping-address.md +57 -21
  90. package/dist/docs/shipping-simulator.md +53 -15
  91. package/dist/docs/sign-in-disclosure.md +176 -0
  92. package/dist/docs/sign-in-form.md +78 -47
  93. package/dist/docs/sign-in-redirect.md +4 -2
  94. package/dist/docs/sign-in.md +173 -0
  95. package/dist/docs/toast.md +15 -4
  96. package/dist/docs/variant-selector.md +15 -11
  97. package/dist/domain-logic/dist/checkout/rules.js +33 -0
  98. package/dist/domain-logic/dist/checkout/rules.js.map +1 -0
  99. package/dist/graphql/types.d.ts +4 -14
  100. package/dist/index.d.ts +2 -2
  101. package/dist/internal/client.d.ts +1 -3
  102. package/dist/internal/client.js +1 -1
  103. package/dist/internal/server.d.ts +7 -4
  104. package/dist/internal/server.js +7 -4
  105. package/dist/matter/access-code-sign-in/access-code-sign-in.d.ts +250 -66
  106. package/dist/matter/access-code-sign-in/access-code-sign-in.js +459 -188
  107. package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
  108. package/dist/matter/account-address-book/account-address-book-delete.d.ts +78 -40
  109. package/dist/matter/account-address-book/account-address-book-delete.js +86 -50
  110. package/dist/matter/account-address-book/account-address-book-delete.js.map +1 -1
  111. package/dist/matter/account-address-book/account-address-book-deliverability.d.ts +21 -4
  112. package/dist/matter/account-address-book/account-address-book-deliverability.js +45 -17
  113. package/dist/matter/account-address-book/account-address-book-deliverability.js.map +1 -1
  114. package/dist/matter/account-address-book/account-address-book-form-parts.d.ts +74 -19
  115. package/dist/matter/account-address-book/account-address-book-form-parts.js +77 -26
  116. package/dist/matter/account-address-book/account-address-book-form-parts.js.map +1 -1
  117. package/dist/matter/account-address-book/account-address-book-profile-notice.d.ts +27 -6
  118. package/dist/matter/account-address-book/account-address-book-profile-notice.js +55 -28
  119. package/dist/matter/account-address-book/account-address-book-profile-notice.js.map +1 -1
  120. package/dist/matter/account-address-book/account-address-book.d.ts +67 -14
  121. package/dist/matter/account-address-book/account-address-book.js +74 -33
  122. package/dist/matter/account-address-book/account-address-book.js.map +1 -1
  123. package/dist/matter/account-address-book/account-address-country-switch.js +3 -3
  124. package/dist/matter/account-address-book/account-address-country-switch.js.map +1 -1
  125. package/dist/matter/account-address-book/account-address-country.d.ts +66 -20
  126. package/dist/matter/account-address-book/account-address-country.js +83 -49
  127. package/dist/matter/account-address-book/account-address-country.js.map +1 -1
  128. package/dist/matter/account-address-book/account-address-form-context.d.ts +8 -4
  129. package/dist/matter/account-address-book/account-address-form-context.js.map +1 -1
  130. package/dist/matter/account-address-book/account-address-form.d.ts +468 -45
  131. package/dist/matter/account-address-book/account-address-form.js +518 -155
  132. package/dist/matter/account-address-book/account-address-form.js.map +1 -1
  133. package/dist/matter/account-address-book/account-address-record.d.ts +27 -6
  134. package/dist/matter/account-address-book/account-address-record.js +32 -17
  135. package/dist/matter/account-address-book/account-address-record.js.map +1 -1
  136. package/dist/matter/account-menu/account-menu.d.ts +34 -17
  137. package/dist/matter/account-menu/account-menu.js +46 -54
  138. package/dist/matter/account-menu/account-menu.js.map +1 -1
  139. package/dist/matter/account-menu-identity/account-menu-identity.js +1 -1
  140. package/dist/matter/account-saved-cards/account-saved-cards.d.ts +108 -28
  141. package/dist/matter/account-saved-cards/account-saved-cards.js +177 -122
  142. package/dist/matter/account-saved-cards/account-saved-cards.js.map +1 -1
  143. package/dist/matter/billing-address/billing-address.d.ts +156 -74
  144. package/dist/matter/billing-address/billing-address.js +227 -156
  145. package/dist/matter/billing-address/billing-address.js.map +1 -1
  146. package/dist/matter/breadcrumb/breadcrumb.d.ts +21 -3
  147. package/dist/matter/breadcrumb/breadcrumb.js +24 -9
  148. package/dist/matter/breadcrumb/breadcrumb.js.map +1 -1
  149. package/dist/matter/buy-button/buy-button.d.ts +8 -11
  150. package/dist/matter/buy-button/buy-button.js +10 -19
  151. package/dist/matter/buy-button/buy-button.js.map +1 -1
  152. package/dist/matter/card-entry/card-capture.js +12 -4
  153. package/dist/matter/card-entry/card-capture.js.map +1 -1
  154. package/dist/matter/card-entry/card-entry.d.ts +123 -32
  155. package/dist/matter/card-entry/card-entry.js +348 -221
  156. package/dist/matter/card-entry/card-entry.js.map +1 -1
  157. package/dist/matter/cart/cart-namespace.d.ts +7 -1
  158. package/dist/matter/cart/cart-namespace.js +7 -1
  159. package/dist/matter/cart/cart-namespace.js.map +1 -1
  160. package/dist/matter/cart/cart.d.ts +402 -61
  161. package/dist/matter/cart/cart.js +624 -153
  162. package/dist/matter/cart/cart.js.map +1 -1
  163. package/dist/matter/cart-destination/cart-destination.d.ts +30 -19
  164. package/dist/matter/cart-destination/cart-destination.js +160 -70
  165. package/dist/matter/cart-destination/cart-destination.js.map +1 -1
  166. package/dist/matter/cart-item/cart-item.d.ts +144 -17
  167. package/dist/matter/cart-item/cart-item.js +166 -49
  168. package/dist/matter/cart-item/cart-item.js.map +1 -1
  169. package/dist/matter/cart-item-compositions/cart-item-compositions.js +1 -1
  170. package/dist/matter/cart-item-compositions/cart-item-compositions.js.map +1 -1
  171. package/dist/matter/cart-item-customizations/cart-item-customizations.js +1 -1
  172. package/dist/matter/cart-item-customizations/cart-item-customizations.js.map +1 -1
  173. package/dist/matter/cart-item-discounts/cart-item-discounts.js +1 -1
  174. package/dist/matter/cart-item-discounts/cart-item-discounts.js.map +1 -1
  175. package/dist/matter/cart-item-seller-info/cart-item-seller-info.js +1 -1
  176. package/dist/matter/cart-item-seller-info/cart-item-seller-info.js.map +1 -1
  177. package/dist/matter/cart-item-variations/cart-item-variations.js +1 -1
  178. package/dist/matter/cart-item-variations/cart-item-variations.js.map +1 -1
  179. package/dist/matter/cart-totalizer/cart-totalizer.d.ts +43 -9
  180. package/dist/matter/cart-totalizer/cart-totalizer.js +46 -17
  181. package/dist/matter/cart-totalizer/cart-totalizer.js.map +1 -1
  182. package/dist/matter/challenge-return/challenge-return.d.ts +62 -0
  183. package/dist/matter/challenge-return/challenge-return.js +241 -0
  184. package/dist/matter/challenge-return/challenge-return.js.map +1 -0
  185. package/dist/matter/client-preferences/client-preferences-fieldset.d.ts +86 -17
  186. package/dist/matter/client-preferences/client-preferences-fieldset.js +126 -54
  187. package/dist/matter/client-preferences/client-preferences-fieldset.js.map +1 -1
  188. package/dist/matter/client-preferences/client-preferences.d.ts +50 -7
  189. package/dist/matter/client-preferences/client-preferences.js +84 -15
  190. package/dist/matter/client-preferences/client-preferences.js.map +1 -1
  191. package/dist/matter/client-profile/client-profile.d.ts +44 -9
  192. package/dist/matter/client-profile/client-profile.js +64 -24
  193. package/dist/matter/client-profile/client-profile.js.map +1 -1
  194. package/dist/matter/client-profile-form/client-profile-fieldset.d.ts +119 -22
  195. package/dist/matter/client-profile-form/client-profile-fieldset.js +161 -73
  196. package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -1
  197. package/dist/matter/delivery-options/delivery-options-accordion.d.ts +49 -4
  198. package/dist/matter/delivery-options/delivery-options-accordion.js +67 -14
  199. package/dist/matter/delivery-options/delivery-options-accordion.js.map +1 -1
  200. package/dist/matter/delivery-options/delivery-options-controller.d.ts +69 -9
  201. package/dist/matter/delivery-options/delivery-options-controller.js +80 -14
  202. package/dist/matter/delivery-options/delivery-options-controller.js.map +1 -1
  203. package/dist/matter/delivery-options/delivery-options.d.ts +37 -2
  204. package/dist/matter/delivery-options/delivery-options.js +75 -12
  205. package/dist/matter/delivery-options/delivery-options.js.map +1 -1
  206. package/dist/matter/delivery-options/use-delivery-option-selection.d.ts +1 -0
  207. package/dist/matter/delivery-options/use-delivery-option-selection.js +1 -1
  208. package/dist/matter/delivery-options/use-delivery-option-selection.js.map +1 -1
  209. package/dist/matter/email-identification/email-identification.d.ts +25 -3
  210. package/dist/matter/email-identification/email-identification.js +49 -12
  211. package/dist/matter/email-identification/email-identification.js.map +1 -1
  212. package/dist/matter/free-shipping-progress/free-shipping-progress.d.ts +79 -7
  213. package/dist/matter/free-shipping-progress/free-shipping-progress.js +77 -20
  214. package/dist/matter/free-shipping-progress/free-shipping-progress.js.map +1 -1
  215. package/dist/matter/identity-edit/identity-edit.d.ts +52 -10
  216. package/dist/matter/identity-edit/identity-edit.js +47 -19
  217. package/dist/matter/identity-edit/identity-edit.js.map +1 -1
  218. package/dist/matter/identity-summary/identity-summary.d.ts +76 -22
  219. package/dist/matter/identity-summary/identity-summary.js +95 -45
  220. package/dist/matter/identity-summary/identity-summary.js.map +1 -1
  221. package/dist/matter/installments/installments-select.d.ts +98 -16
  222. package/dist/matter/installments/installments-select.js +156 -48
  223. package/dist/matter/installments/installments-select.js.map +1 -1
  224. package/dist/matter/installments/installments.d.ts +96 -16
  225. package/dist/matter/installments/installments.js +115 -40
  226. package/dist/matter/installments/installments.js.map +1 -1
  227. package/dist/matter/installments/use-installments-selection.d.ts +1 -0
  228. package/dist/matter/installments/use-installments-selection.js +8 -3
  229. package/dist/matter/installments/use-installments-selection.js.map +1 -1
  230. package/dist/matter/my-account-profile/my-account-profile.d.ts +358 -103
  231. package/dist/matter/my-account-profile/my-account-profile.js +370 -174
  232. package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
  233. package/dist/matter/order-cancel/order-cancel.d.ts +65 -19
  234. package/dist/matter/order-cancel/order-cancel.js +118 -51
  235. package/dist/matter/order-cancel/order-cancel.js.map +1 -1
  236. package/dist/matter/order-delivery-address/order-delivery-address.d.ts +8 -18
  237. package/dist/matter/order-delivery-address/order-delivery-address.js +14 -23
  238. package/dist/matter/order-delivery-address/order-delivery-address.js.map +1 -1
  239. package/dist/matter/order-delivery-group/order-delivery-group.d.ts +40 -24
  240. package/dist/matter/order-delivery-group/order-delivery-group.js +64 -74
  241. package/dist/matter/order-delivery-group/order-delivery-group.js.map +1 -1
  242. package/dist/matter/order-details-header/order-details-header.d.ts +32 -15
  243. package/dist/matter/order-details-header/order-details-header.js +66 -58
  244. package/dist/matter/order-details-header/order-details-header.js.map +1 -1
  245. package/dist/matter/order-invoice-download/order-invoice-download.d.ts +78 -0
  246. package/dist/matter/order-invoice-download/order-invoice-download.js +173 -0
  247. package/dist/matter/order-invoice-download/order-invoice-download.js.map +1 -0
  248. package/dist/matter/order-list/order-list.d.ts +164 -19
  249. package/dist/matter/order-list/order-list.js +229 -87
  250. package/dist/matter/order-list/order-list.js.map +1 -1
  251. package/dist/matter/order-list-row/order-list-row.d.ts +6 -1
  252. package/dist/matter/order-list-row/order-list-row.js +3 -2
  253. package/dist/matter/order-list-row/order-list-row.js.map +1 -1
  254. package/dist/matter/order-list-search/order-list-search.d.ts +18 -26
  255. package/dist/matter/order-list-search/order-list-search.js +30 -69
  256. package/dist/matter/order-list-search/order-list-search.js.map +1 -1
  257. package/dist/matter/order-list-views/order-list-views.d.ts +15 -22
  258. package/dist/matter/order-list-views/order-list-views.js +22 -68
  259. package/dist/matter/order-list-views/order-list-views.js.map +1 -1
  260. package/dist/matter/order-payment/order-payment.d.ts +33 -10
  261. package/dist/matter/order-payment/order-payment.js +44 -39
  262. package/dist/matter/order-payment/order-payment.js.map +1 -1
  263. package/dist/matter/order-placed/order-placed-deliveries.d.ts +144 -12
  264. package/dist/matter/order-placed/order-placed-deliveries.js +115 -40
  265. package/dist/matter/order-placed/order-placed-deliveries.js.map +1 -1
  266. package/dist/matter/order-placed/order-placed.d.ts +198 -31
  267. package/dist/matter/order-placed/order-placed.js +230 -139
  268. package/dist/matter/order-placed/order-placed.js.map +1 -1
  269. package/dist/matter/order-placed-date/order-placed-date.d.ts +5 -8
  270. package/dist/matter/order-placed-date/order-placed-date.js +7 -8
  271. package/dist/matter/order-placed-date/order-placed-date.js.map +1 -1
  272. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +90 -17
  273. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +93 -117
  274. package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -1
  275. package/dist/matter/order-progress/order-progress.d.ts +19 -8
  276. package/dist/matter/order-progress/order-progress.js +37 -45
  277. package/dist/matter/order-progress/order-progress.js.map +1 -1
  278. package/dist/matter/order-summary/order-summary.d.ts +19 -9
  279. package/dist/matter/order-summary/order-summary.js +38 -44
  280. package/dist/matter/order-summary/order-summary.js.map +1 -1
  281. package/dist/matter/order-tracking-link/order-tracking-link.d.ts +43 -0
  282. package/dist/matter/order-tracking-link/order-tracking-link.js +107 -0
  283. package/dist/matter/order-tracking-link/order-tracking-link.js.map +1 -0
  284. package/dist/matter/payment/payment-card-capture.js +17 -10
  285. package/dist/matter/payment/payment-card-capture.js.map +1 -1
  286. package/dist/matter/payment/payment-controller.d.ts +4 -2
  287. package/dist/matter/payment/payment-controller.js +30 -9
  288. package/dist/matter/payment/payment-controller.js.map +1 -1
  289. package/dist/matter/payment/payment-method-badges.js +1 -1
  290. package/dist/matter/payment/payment.d.ts +69 -19
  291. package/dist/matter/payment/payment.js +101 -35
  292. package/dist/matter/payment/payment.js.map +1 -1
  293. package/dist/matter/payment/use-payment-method-selection.d.ts +25 -4
  294. package/dist/matter/payment/use-payment-method-selection.js +94 -32
  295. package/dist/matter/payment/use-payment-method-selection.js.map +1 -1
  296. package/dist/matter/payment/use-payment-submit-gate.d.ts +14 -3
  297. package/dist/matter/payment/use-payment-submit-gate.js +47 -8
  298. package/dist/matter/payment/use-payment-submit-gate.js.map +1 -1
  299. package/dist/matter/place-order/place-order.d.ts +171 -57
  300. package/dist/matter/place-order/place-order.js +376 -185
  301. package/dist/matter/place-order/place-order.js.map +1 -1
  302. package/dist/matter/pricing/pricing.d.ts +152 -13
  303. package/dist/matter/pricing/pricing.js +153 -52
  304. package/dist/matter/pricing/pricing.js.map +1 -1
  305. package/dist/matter/product-information/product-information.d.ts +27 -4
  306. package/dist/matter/product-information/product-information.js +32 -15
  307. package/dist/matter/product-information/product-information.js.map +1 -1
  308. package/dist/matter/product-list/product-list.d.ts +24 -6
  309. package/dist/matter/product-list/product-list.js +32 -26
  310. package/dist/matter/product-list/product-list.js.map +1 -1
  311. package/dist/matter/product-shelf/product-shelf.d.ts +1 -3
  312. package/dist/matter/product-shelf/product-shelf.js +2 -15
  313. package/dist/matter/product-shelf/product-shelf.js.map +1 -1
  314. package/dist/matter/region/region.d.ts +33 -16
  315. package/dist/matter/region/region.js +59 -52
  316. package/dist/matter/region/region.js.map +1 -1
  317. package/dist/matter/save-card/save-card.d.ts +43 -8
  318. package/dist/matter/save-card/save-card.js +61 -23
  319. package/dist/matter/save-card/save-card.js.map +1 -1
  320. package/dist/matter/saved-cards/saved-cards.d.ts +113 -21
  321. package/dist/matter/saved-cards/saved-cards.js +349 -77
  322. package/dist/matter/saved-cards/saved-cards.js.map +1 -1
  323. package/dist/matter/shipping-address/address-payload.js +25 -0
  324. package/dist/matter/shipping-address/address-payload.js.map +1 -0
  325. package/dist/matter/shipping-address/country-descriptors.js +71 -71
  326. package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
  327. package/dist/matter/shipping-address/library-country-descriptors.js +136 -0
  328. package/dist/matter/shipping-address/library-country-descriptors.js.map +1 -0
  329. package/dist/matter/shipping-address/shipping-address.d.ts +312 -45
  330. package/dist/matter/shipping-address/shipping-address.js +443 -188
  331. package/dist/matter/shipping-address/shipping-address.js.map +1 -1
  332. package/dist/matter/shipping-simulator/shipping-simulator.d.ts +250 -24
  333. package/dist/matter/shipping-simulator/shipping-simulator.js +257 -51
  334. package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
  335. package/dist/matter/sign-in/sign-in.d.ts +108 -0
  336. package/dist/matter/sign-in/sign-in.js +285 -0
  337. package/dist/matter/sign-in/sign-in.js.map +1 -0
  338. package/dist/matter/sign-in-disclosure/sign-in-disclosure.d.ts +81 -0
  339. package/dist/matter/sign-in-disclosure/sign-in-disclosure.js +232 -0
  340. package/dist/matter/sign-in-disclosure/sign-in-disclosure.js.map +1 -0
  341. package/dist/matter/sign-in-form/sign-in-form.d.ts +93 -62
  342. package/dist/matter/sign-in-form/sign-in-form.js +134 -144
  343. package/dist/matter/sign-in-form/sign-in-form.js.map +1 -1
  344. package/dist/particles/binding-switcher/binding-switcher.d.ts +29 -8
  345. package/dist/particles/binding-switcher/binding-switcher.js +37 -26
  346. package/dist/particles/binding-switcher/binding-switcher.js.map +1 -1
  347. package/dist/particles/card-summary/card-summary.d.ts +38 -8
  348. package/dist/particles/card-summary/card-summary.js +55 -22
  349. package/dist/particles/card-summary/card-summary.js.map +1 -1
  350. package/dist/particles/carousel/carousel.d.ts +99 -11
  351. package/dist/particles/carousel/carousel.js +103 -35
  352. package/dist/particles/carousel/carousel.js.map +1 -1
  353. package/dist/particles/copy-text/copy-text.js +5 -15
  354. package/dist/particles/copy-text/copy-text.js.map +1 -1
  355. package/dist/particles/dialog/dialog.d.ts +14 -6
  356. package/dist/particles/dialog/dialog.js +6 -3
  357. package/dist/particles/dialog/dialog.js.map +1 -1
  358. package/dist/particles/drawer/drawer.js +1 -1
  359. package/dist/particles/drawer/drawer.js.map +1 -1
  360. package/dist/particles/email-field/email-field.d.ts +2 -1
  361. package/dist/particles/email-field/email-field.js +2 -1
  362. package/dist/particles/email-field/email-field.js.map +1 -1
  363. package/dist/particles/footer-link-group/footer-link-group.js +1 -0
  364. package/dist/particles/footer-link-group/footer-link-group.js.map +1 -1
  365. package/dist/particles/form/form.d.ts +85 -13
  366. package/dist/particles/form/form.js +91 -42
  367. package/dist/particles/form/form.js.map +1 -1
  368. package/dist/particles/gallery/gallery.d.ts +129 -12
  369. package/dist/particles/gallery/gallery.js +121 -34
  370. package/dist/particles/gallery/gallery.js.map +1 -1
  371. package/dist/particles/order-details-back/order-details-back.d.ts +5 -3
  372. package/dist/particles/order-details-back/order-details-back.js +8 -10
  373. package/dist/particles/order-details-back/order-details-back.js.map +1 -1
  374. package/dist/particles/order-status-badge/order-status-badge.d.ts +3 -3
  375. package/dist/particles/order-status-badge/order-status-badge.js +4 -20
  376. package/dist/particles/order-status-badge/order-status-badge.js.map +1 -1
  377. package/dist/particles/password-field/password-field.d.ts +1 -1
  378. package/dist/particles/password-field/password-field.js +2 -4
  379. package/dist/particles/password-field/password-field.js.map +1 -1
  380. package/dist/particles/phone-field/phone-field.d.ts +4 -4
  381. package/dist/particles/phone-field/phone-field.js +8 -11
  382. package/dist/particles/phone-field/phone-field.js.map +1 -1
  383. package/dist/particles/spinner/spinner.d.ts +24 -4
  384. package/dist/particles/spinner/spinner.js +25 -9
  385. package/dist/particles/spinner/spinner.js.map +1 -1
  386. package/dist/particles/switch/switch.d.ts +4 -1
  387. package/dist/particles/switch/switch.js +3 -1
  388. package/dist/particles/switch/switch.js.map +1 -1
  389. package/dist/particles/toast/toast.d.ts +29 -3
  390. package/dist/particles/toast/toast.js +34 -10
  391. package/dist/particles/toast/toast.js.map +1 -1
  392. package/dist/schema.graphql +164 -56
  393. package/dist/sdk/address/address-fields.js +4 -4
  394. package/dist/sdk/address/address-fields.js.map +1 -1
  395. package/dist/sdk/address/constants.js +1 -6
  396. package/dist/sdk/address/constants.js.map +1 -1
  397. package/dist/sdk/address/messages.js +44 -44
  398. package/dist/sdk/address/messages.js.map +1 -1
  399. package/dist/sdk/address/postal-code.js +15 -2
  400. package/dist/sdk/address/postal-code.js.map +1 -1
  401. package/dist/sdk/address/types.d.ts +5 -3
  402. package/dist/sdk/address/validate-address.js +3 -3
  403. package/dist/sdk/address/validate-address.js.map +1 -1
  404. package/dist/sdk/analytics/use-analytics-actions.d.ts +1 -1
  405. package/dist/sdk/analytics/use-analytics-actions.js.map +1 -1
  406. package/dist/sdk/cart/use-cart-actions.js.map +1 -1
  407. package/dist/sdk/data/index.js +2 -2
  408. package/dist/sdk/data/reactive-list-item-data-provider.js +2 -2
  409. package/dist/sdk/generated/gql.js +1 -1
  410. package/dist/sdk/generated/gql.js.map +1 -1
  411. package/dist/sdk/generated/graphql.d.ts +20 -5
  412. package/dist/sdk/generated/graphql.js +6 -0
  413. package/dist/sdk/generated/graphql.js.map +1 -1
  414. package/dist/sdk/i18n/format-error-code.js +23 -0
  415. package/dist/sdk/i18n/format-error-code.js.map +1 -0
  416. package/dist/sdk/i18n/message-formatter.d.ts +22 -0
  417. package/dist/sdk/i18n/message-formatter.js +140 -0
  418. package/dist/sdk/i18n/message-formatter.js.map +1 -0
  419. package/dist/sdk/i18n/use-message-formatter.d.ts +8 -0
  420. package/dist/sdk/i18n/use-message-formatter.js +18 -0
  421. package/dist/sdk/i18n/use-message-formatter.js.map +1 -0
  422. package/dist/sdk/identity/use-identity-actions.js.map +1 -1
  423. package/dist/sdk/navigation/redirect.d.ts +2 -0
  424. package/dist/sdk/navigation/redirect.js +19 -1
  425. package/dist/sdk/navigation/redirect.js.map +1 -1
  426. package/dist/sdk/navigation/types.d.ts +13 -0
  427. package/dist/sdk/payment/bin-format.js +21 -0
  428. package/dist/sdk/payment/bin-format.js.map +1 -0
  429. package/dist/sdk/payment/challenge-return.js +37 -0
  430. package/dist/sdk/payment/challenge-return.js.map +1 -0
  431. package/dist/sdk/payment/confirmation-last-four.js +39 -0
  432. package/dist/sdk/payment/confirmation-last-four.js.map +1 -0
  433. package/dist/sdk/payment/gateway.d.ts +1 -1
  434. package/dist/sdk/payment/gateway.js +8 -2
  435. package/dist/sdk/payment/gateway.js.map +1 -1
  436. package/dist/sdk/payment/operations.d.ts +1 -0
  437. package/dist/sdk/payment/operations.js +28 -6
  438. package/dist/sdk/payment/operations.js.map +1 -1
  439. package/dist/sdk/payment/resumed-purchase.d.ts +1 -0
  440. package/dist/sdk/payment/resumed-purchase.js +119 -0
  441. package/dist/sdk/payment/resumed-purchase.js.map +1 -0
  442. package/dist/sdk/payment/security-code-mask.js +16 -23
  443. package/dist/sdk/payment/security-code-mask.js.map +1 -1
  444. package/dist/sdk/payment/submission-registry.js +6 -2
  445. package/dist/sdk/payment/submission-registry.js.map +1 -1
  446. package/dist/sdk/payment/use-payment-actions.d.ts +18 -3
  447. package/dist/sdk/payment/use-payment-actions.js +23 -4
  448. package/dist/sdk/payment/use-payment-actions.js.map +1 -1
  449. package/dist/sdk/payment/use-place-order.d.ts +10 -2
  450. package/dist/sdk/payment/use-place-order.js +138 -76
  451. package/dist/sdk/payment/use-place-order.js.map +1 -1
  452. package/dist/sdk/provider/storefront-provider.d.ts +1 -7
  453. package/dist/sdk/provider/storefront-provider.js +4 -11
  454. package/dist/sdk/provider/storefront-provider.js.map +1 -1
  455. package/dist/sdk/seo/entity-landing.d.ts +24 -0
  456. package/dist/sdk/seo/entity-landing.js +30 -0
  457. package/dist/sdk/seo/entity-landing.js.map +1 -0
  458. package/dist/sdk/seo/page-alternates.d.ts +3 -2
  459. package/dist/sdk/seo/page-alternates.js +4 -2
  460. package/dist/sdk/seo/page-alternates.js.map +1 -1
  461. package/dist/sdk/seo/search-landing.d.ts +42 -0
  462. package/dist/sdk/seo/search-landing.js +85 -0
  463. package/dist/sdk/seo/search-landing.js.map +1 -0
  464. package/dist/sdk/session/mock-session.d.ts +9 -0
  465. package/dist/sdk/session/mock-session.js +31 -0
  466. package/dist/sdk/session/mock-session.js.map +1 -0
  467. package/dist/sdk/session/read-server-session.d.ts +3 -0
  468. package/dist/sdk/session/read-server-session.js +5 -0
  469. package/dist/sdk/session/read-server-session.js.map +1 -1
  470. package/dist/sdk/session/return-path-segment.d.ts +10 -0
  471. package/dist/sdk/session/return-path-segment.js +15 -0
  472. package/dist/sdk/session/return-path-segment.js.map +1 -0
  473. package/dist/sdk/session/use-session-actions.js.map +1 -1
  474. package/dist/sdk/shipping/use-cart-shipping-simulation.js.map +1 -1
  475. package/dist/sdk/shipping/use-shipping-actions.js.map +1 -1
  476. package/dist/sdk/shipping/use-shipping-simulation.js.map +1 -1
  477. package/dist/utils/safe-href.d.ts +2 -0
  478. package/package.json +4 -3
  479. package/skills/storefront-asset/SKILL.md +2 -2
  480. package/skills/storefront-asset/references/api-side.md +27 -14
  481. package/skills/storefront-asset/references/findings.md +5 -2
  482. package/src/styles/tailwind.css +8 -0
  483. package/dist/matter/shipping-address/country-descriptors.d.ts +0 -1
  484. package/dist/message-locales.json +0 -7
  485. package/dist/sdk/i18n/client.d.ts +0 -4
  486. package/dist/sdk/i18n/client.js +0 -4
  487. package/dist/sdk/i18n/locales.d.ts +0 -8
  488. package/dist/sdk/i18n/locales.js +0 -57
  489. package/dist/sdk/i18n/locales.js.map +0 -1
  490. package/dist/sdk/i18n/messages/en-US.js +0 -183
  491. package/dist/sdk/i18n/messages/en-US.js.map +0 -1
  492. package/dist/sdk/i18n/messages/en-US.json +0 -178
  493. package/dist/sdk/i18n/messages/en-US.json.d.ts +0 -182
  494. package/dist/sdk/i18n/messages/es-ES.js +0 -183
  495. package/dist/sdk/i18n/messages/es-ES.js.map +0 -1
  496. package/dist/sdk/i18n/messages/es-ES.json +0 -178
  497. package/dist/sdk/i18n/messages/pt-BR.js +0 -183
  498. package/dist/sdk/i18n/messages/pt-BR.js.map +0 -1
  499. package/dist/sdk/i18n/messages/pt-BR.json +0 -178
  500. package/dist/sdk/i18n/provider.d.ts +0 -1
  501. package/dist/sdk/i18n/provider.js +0 -30
  502. package/dist/sdk/i18n/provider.js.map +0 -1
  503. package/dist/sdk/i18n/server.d.ts +0 -28
  504. package/dist/sdk/i18n/server.js +0 -59
  505. package/dist/sdk/i18n/server.js.map +0 -1
  506. package/dist/utils/resolve-authored-copy.js +0 -18
  507. package/dist/utils/resolve-authored-copy.js.map +0 -1
  508. package/dist/utils/substitute-template.js +0 -33
  509. package/dist/utils/substitute-template.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"billing-address.js","names":[],"sources":["../../../src/matter/billing-address/billing-address.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { useEffect, useId, useMemo, useState } from 'react';\nimport { Checkbox } from '../../particles/checkbox/checkbox.js';\nimport { Field } from '../../particles/field/field.js';\nimport { Icon } from '../../particles/icon/icon.js';\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 { resolveAuthoredCopy } from '../../utils/resolve-authored-copy.js';\nimport { usePayment } from '../payment/payment.js';\nimport {\n type CountryDescriptor,\n type FieldDescriptor,\n getAvailableCountries,\n getCountryDescriptor,\n satisfiesFieldPattern,\n} from '../shipping-address/country-descriptors.js';\n\n/**\n * The `BillingAddress` matter (specs/billing-address.md, T11 of the Credit Card epic;\n * docs/features/credit-card/rfc.md § D6). A \"same as delivery\" toggle (default on) plus, when off, a\n * country-specific address form that persists via `$actions.payment.setBillingAddress`\n * (already shipped by T4, zero consumers before this task).\n *\n * The shared `Address` composition RFC D6 names (shipping and billing as two control matters over\n * one set of reusable particles) is DEFERRED — an implementation-time call the engineer resolved\n * narrow (specs/billing-address.md Decision 1). This component builds its own controls and imports\n * `countryDescriptors` from `../shipping-address/country-descriptors.js`, the one place a country's field\n * set, labels, copy and format rules live.\n *\n * There is NO read-back path for a persisted billing address (specs/billing-address.md Decision 3):\n * `type Payment` carries no `billingAddress` field, and `setBillingAddress` itself resolves only\n * `userErrors`, discarding the mutation's own echo. This component declares no fragment and renders\n * nothing below the toggle while \"same as delivery\" is checked, not even the delivery address\n * (specs/stop-re-rendering-the-full-delivery.md, T175). The custom-address form always starts blank,\n * and its own local state is the sole source of truth for what was typed.\n *\n * Only a boolean rides the shared `draft` (`billingAddressDifferent`, widened onto `PaymentDraft` by\n * this task under sole ownership of `use-payment-method-selection.ts` — specs/billing-address.md\n * Decision 5); the address itself is persisted server-side by this component's own action call, never\n * passed through the draft. `setBillingAddress` is dispatched at exactly two points (Decision 9): the\n * toggle's transition back to \"same as delivery\", and the custom form's explicit submit control —\n * never on mount, never per keystroke.\n *\n * **`draft.billingAddressDifferent` only ever becomes `true` once `setBillingAddress` has actually\n * persisted a different address (PR #544 N1).** Unchecking \"same as delivery\" opens the form\n * (`formOpen`, local-only state below) but does not touch the draft — `gateway.ts`'s own doc comment\n * (\"`setBillingAddress` already persisted it … so the gateway reads it from there\") is the contract\n * this must not break: publishing `true` before anything is persisted would point the gateway at an\n * `invoiceData` address nothing ever wrote. The engineer's own resolution for this (session of\n * 2026-07-31): the new-address fields become required whenever \"different\" is selected, so this\n * component's own submit blocks on an incomplete address (`isBillingAddressFormValid`) — but that\n * alone cannot stop a shopper from declining \"same as delivery\" and clicking Place Order without ever\n * opening the form's own submit control, which is why the draft write itself is gated on success, not\n * merely the submit button.\n *\n * **The native `required` attribute below is cosmetic here, not an enforcement mechanism (PR #544\n * N7).** This file renders no `aria-required` at all — only `required` (native HTML). There is no\n * `<form>` in this render tree and the submit control is `type=\"button\"`, so the browser's own\n * constraint validation never runs — nothing in this file or its tests may claim this attribute itself\n * prevents a shopper from placing an order with an unsaved address; the actual submit gate is\n * `isBillingAddressFormValid`'s verdict (`areRenderableFieldsValid`), read at the submit control's own\n * `disabled` below. Since T38\n * (specs/harden-and-verify-accessibility-across-the.md FR-6) each rendered field does carry a\n * `Field.Error`, but it is driven by a local `touched` set (a field joins it on blur, or when a refused\n * submit moves focus to it) and `getFieldIssue`'s verdict (`required` for a blank field, `format` for a\n * non-blank value that fails the country's postal pattern), never by `required`'s own native\n * `ValidityState` — so this paragraph's point is unchanged: `required` itself still enforces nothing\n * here. What this component owes, and does: make the gap **visible** — an inline `role=\"status\"`\n * notice while the form is open and nothing has been persisted yet — and publish that same fact\n * (`setDraftBillingAddressUnsaved`) onto the shared draft, from the identical expression the notice\n * renders from (T24, specs/cardholder-document-submit-gate.md FR-6). **The actual hard block lives\n * elsewhere**: T24's `usePaymentSubmitGate()` reads that published fact and refuses Place Order\n * client-side — this component's own job stops at publishing the fact and showing the notice, never at\n * gating the CTA itself. See `specs/billing-address.md`'s Documentation Gaps #1 for the deferral this closes.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Local-only values for the \"different\" address form, keyed by the country descriptor's own\n * canonical field names — a plain string index signature, not a closed set of properties, so a future\n * country descriptor introducing a new field name needs no type change here (mirrors\n * `FieldDescriptor.name: string`'s own deliberately untyped choice; specs/billing-address.md\n * Decision 7). `postalCode` and `country` are named explicitly because this component's own logic\n * reads both directly. Never carries a `recipientName` key: `PaymentBillingAddressUpdateInput` has no\n * such field (Decision 4) — the render loop filters it out of whatever the descriptor lists.\n */\nexport type BillingAddressFormDraft = Readonly<{\n postalCode: string;\n country: string;\n [fieldName: string]: string;\n}>;\n\nexport type BillingAddressProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {\n /** The \"same as delivery\" toggle's own accessible label. */\n sameAsDeliveryLabel?: string;\n /** ISO 3166-1 alpha-3 default country for the custom-address form's country selector. */\n defaultCountry?: string;\n /** Label for the country selector inside the custom-address form. */\n countryLabel?: string;\n /** Label for the control that persists a custom billing address. */\n submitLabel?: string;\n /** Label on the submit control while the mutation is in flight. */\n submitPendingLabel?: string;\n /** Shown when the selected method's validator marks a billing address required. */\n requiredNoticeLabel?: string;\n /** Label for the recipient-name descriptor field. Not rendered by the custom-address form today\n * (`OMITTED_FORM_FIELD_NAMES` — the write contract has no matching input slot, Decision 4); kept\n * for parity with the other seven field labels this same map covers. */\n recipientNameLabel?: string;\n /** Label for the street descriptor field. */\n streetLabel?: string;\n /** Label for the street-number descriptor field. */\n numberLabel?: string;\n /** Label for the complement descriptor field. Its \"(optional)\" suffix is the only signal that the\n * field is not required — `OPTIONAL_FORM_FIELD_NAMES` drives the actual `required` attribute\n * independently, so authoring this without the suffix loses the optionality cue, not the behavior\n * (R7). */\n complementLabel?: string;\n /** Label for the neighborhood descriptor field. Unused when the active country's descriptor has no\n * `neighborhood` field (e.g. USA). */\n neighborhoodLabel?: string;\n /** Label for the city descriptor field. */\n cityLabel?: string;\n /** Label for the state descriptor field. */\n stateLabel?: string;\n /** Label for the postal-code descriptor field. */\n postalCodeLabel?: string;\n /** Shown while the form is open and no address has been persisted yet (PR #544 N7) — the shopper\n * declined \"same as delivery\" but has not (or not yet again) had a submit succeed, so Place Order\n * will bill the delivery address unless they save one first. Rendered `role=\"status\"`, not\n * `role=\"alert\"`: it is a persistent, advisory condition of the form being open, not a one-off\n * announcement reacting to a rejected action (see this file's own existing `-error` alert for that\n * shape) — see `specs/billing-address.md`'s Accessibility Contract for the site count this choice is\n * measured against. */\n unsavedAddressNoticeLabel?: string;\n /** Always-on billing-match disclaimer (requirement 12.20): tells the shopper their billing address\n * must match the one their card issuer or bank statement holds, so an AVS mismatch does not get the\n * order declined with no explanation on screen. Gated on this prop and `billingMatchNoticeVisible`\n * only — reads no `usePayment()` value, no local state, no `$data` (FR-4), unlike the two notices\n * above. */\n billingMatchNoticeLabel?: string;\n /** Switches the billing-match disclaimer above on or off. Defaults to `true` (Decision 5 — the\n * requirement's own intent); subordinate to `billingMatchNoticeLabel.trim()`, which alone can still\n * suppress an empty notice (FR-3) regardless of this prop's value. */\n billingMatchNoticeVisible?: boolean;\n /**\n * Shown as a `Field.Error` under a required, blank, touched field (T38, FR-6/FR-7) — the same\n * non-blank rule `isBillingAddressFormValid` already applies, except `complement`\n * (`OPTIONAL_FORM_FIELD_NAMES`), which is never marked invalid. One prop, not one per field: every\n * rendered field shares the same rule, so seven props would be seven ways to say the same sentence.\n * Resolved through `resolveAuthoredCopy`.\n */\n fieldRequiredMessage?: string;\n /**\n * Shown as a `Field.Error` under a touched, non-blank postal code that does not match the selected\n * country's format (the descriptor's `rule.pattern`). Only the postal code reads this prop: any other\n * field that gains a pattern speaks with its own descriptor message. Blank wins over format: a blank\n * field reads `fieldRequiredMessage`, never this. Never contains the typed value. Resolved through\n * `resolveAuthoredCopy`, so a blank value falls back to the English default like every other copy on\n * this form.\n */\n postalCodeInvalidMessage?: string;\n /** Disables the toggle, every form field, the country selector, and the submit control. */\n disabled?: boolean;\n /** Compiler-injected action surface; carries `payment.setBillingAddress`. */\n $actions?: ComponentActions;\n};\n\n/** Canonical field names the custom-address form never renders, even when a country descriptor\n * lists them — because the write contract has no matching input slot (Decision 4). */\nconst OMITTED_FORM_FIELD_NAMES = new Set(['recipientName']);\n\n/** Descriptor field names the custom-address form renders but never marks required, even while\n * \"different\" is selected (PR #544 N1 item 1 — the engineer's own rule, verbatim: \"if the person says\n * the address is different, the new address fields must be required\"). `complement` is the one\n * exception: `complementLabel`'s own default already labels it optional (\"Complement (optional)\"),\n * and neither a real-world address nor the server's own write contract treats it as load-bearing.\n * Shared between the `required` attribute below and `isBillingAddressFormValid`, so the two never\n * drift apart. An authored `complementLabel` that drops the \"(optional)\" suffix loses that visual cue\n * without changing this set — the two are independent (R7, billing-address.md). */\nconst OPTIONAL_FORM_FIELD_NAMES = new Set(['complement']);\n\n/**\n * English override for the descriptor's own country display names (`'Brasil'`, `'United States'`), because\n * the Brazilian entry's name is Portuguese. This map is a stopgap: copy a country's form speaks belongs on\n * its descriptor in `country-descriptors.ts`, as `countryLabel` and `requiredSuffix` already do. Put a new\n * string there, not in another English override map here. T31\n * (specs/make-the-payment-components-authored-copy.md, Decision 5) turned the eight *field* labels into\n * schema props but left this one a plain map: a prop per ISO code does not scale the way eight named\n * field-label props do. Keyed by the descriptor's own ISO code.\n */\nconst COUNTRY_LABELS_EN: Readonly<Record<string, string>> = {\n BRA: 'Brazil',\n USA: 'United States',\n};\n\n/** Resolves a country's English display name, falling back to the descriptor's own label (then the\n * raw ISO code) for a country this map does not yet cover. */\nfunction getCountryLabel(iso: string): string {\n return COUNTRY_LABELS_EN[iso] ?? getCountryDescriptor(iso)?.label ?? iso;\n}\n\n// ---------------------------------------------------------------------------\n// Pure decision functions — exported so both are unit-testable independent of any rendering\n// (specs/billing-address.md § Resolution logic).\n// ---------------------------------------------------------------------------\n\n/**\n * Whether the toggle reads as \"same as delivery\" — `true` whenever the published flag is `null`\n * (never touched, the default) or `false` (explicitly reverted); only an explicit `true` means the\n * shopper is on the custom-address path. Takes no closure over component state, so it is safe to call\n * fresh on every render.\n */\nexport function resolveSameAsDelivery(billingAddressDifferent: boolean | null | undefined): boolean {\n return billingAddressDifferent !== true;\n}\n\n/** Why a rendered field is not acceptable: blank (`required`), or non-blank and not in the selected\n * country's format (`format`). `null` means acceptable. */\ntype FieldIssue = 'required' | 'format' | null;\n\n/**\n * The one verdict on a rendered field, read by both the submit gate (`areRenderableFieldsValid`, behind\n * `isBillingAddressFormValid`) and the per-field error (the `issue` computed per field in the render), so the\n * two can never disagree (T38 Decision 8). Blank (after\n * trim) wins over format: `complement` (`OPTIONAL_FORM_FIELD_NAMES`) may be blank, every other field may\n * not. A non-blank value is tested raw against the descriptor's `rule.pattern` — the same\n * predicate the delivery form's react-hook-form rule applies, so a surrounding space is a format error\n * in both. A whitespace-only value is the one divergence: it is blank here (`required`), where the\n * delivery form would run the pattern on it (see `satisfiesFieldPattern`). No postal format is defined\n * here: the descriptor's `rule.pattern` is the only definition.\n */\nfunction getFieldIssue(field: FieldDescriptor, value: string): FieldIssue {\n if (value.trim().length === 0) return OPTIONAL_FORM_FIELD_NAMES.has(field.name) ? null : 'required';\n return satisfiesFieldPattern(field, value) ? null : 'format';\n}\n\n/**\n * The message under a field whose verdict is `format`. `postalCode` keeps the authored\n * `postalCodeInvalidMessage` (already resolved by the caller, so it always has an English default); every\n * other field speaks with its own descriptor `rule.pattern.message`, the one the delivery form shows for the\n * same rule, so a pattern a descriptor gains later never surfaces the postal-code copy. A field with no\n * pattern never reaches a `format` verdict, so the empty string is not a message anyone sees.\n */\nexport function getFieldFormatMessage(field: FieldDescriptor, postalCodeMessage: string): string {\n if (field.name === 'postalCode') return postalCodeMessage;\n return field.rule?.pattern?.message ?? '';\n}\n\n/** The descriptor's fields the custom-address form renders: everything but `OMITTED_FORM_FIELD_NAMES`. */\nfunction getRenderableFields(descriptor: CountryDescriptor): readonly FieldDescriptor[] {\n return descriptor.fields.filter((field) => !OMITTED_FORM_FIELD_NAMES.has(field.name));\n}\n\n/**\n * Whether the custom-address form may be submitted. Stricter than the server's own\n * `MISSING_BILLING_ADDRESS_FIELDS` floor (\"sameAsDelivery is false but postalCode **or country** was\n * omitted\" [source: `core.graphql:2857-2858`]) — every field the active country's descriptor renders,\n * except `complement` (`OPTIONAL_FORM_FIELD_NAMES`), must be non-blank (PR #544 N1 item 1: the\n * engineer's own rule that the new-address fields are required once \"different\" is selected), and every\n * non-blank value must match its descriptor's `rule.pattern`. Resolves the descriptor from\n * `formDraft.country` itself rather than taking a second parameter, so this stays a single\n * self-contained fact about one `BillingAddressFormDraft` value. `country` itself is not part of\n * `descriptor.fields` (Decision 7) and is never checked here — it always carries a non-blank value from\n * `defaultCountry` or the selector's own current value (review F12), so it can never be the field this\n * gate would fail on.\n */\nexport function isBillingAddressFormValid(formDraft: BillingAddressFormDraft): boolean {\n // No descriptor means no known required-field list to satisfy — a vacuous `true` would be exactly\n // wrong here. Not reachable through the rendered component today (review F7's fallback already\n // resolves `formDraft.country` to a real descriptor before the form ever mounts), but this function\n // is exported and unit-tested directly with such a country, so it must not depend on the render path.\n const descriptor = getCountryDescriptor(formDraft.country);\n return descriptor ? areRenderableFieldsValid(getRenderableFields(descriptor), formDraft) : false;\n}\n\n/**\n * The verdict behind `isBillingAddressFormValid`, taking the renderable fields as an argument so the\n * render path can pass the list it already memoises per country instead of resolving them again on every\n * render. One body for both callers, so the gate can never disagree with itself.\n */\nfunction areRenderableFieldsValid(\n renderableFields: readonly FieldDescriptor[],\n formDraft: BillingAddressFormDraft,\n): boolean {\n return renderableFields.every((field) => getFieldIssue(field, formDraft[field.name] ?? '') === null);\n}\n\n/**\n * Whether the submit control may fire: the active country has a descriptor and every field it renders is\n * valid. Mirrors `isBillingAddressFormValid`, over the descriptor and fields the render path already holds.\n */\nfunction canSaveBillingDraft(\n descriptor: CountryDescriptor | undefined,\n renderableFields: readonly FieldDescriptor[],\n formDraft: BillingAddressFormDraft,\n): boolean {\n return descriptor !== undefined && areRenderableFieldsValid(renderableFields, formDraft);\n}\n\n/** The touched set with `name` added. Returns `prev` itself when it already holds `name`, so a state update\n * built on it never triggers a redundant re-render. */\nfunction withTouched(prev: ReadonlySet<string>, name: string): ReadonlySet<string> {\n return prev.has(name) ? prev : new Set(prev).add(name);\n}\n\n/**\n * Takes focus for a refusal that named the billing form (T212, Decision 3): the first rendered field, in\n * render order, whose value is blank and which is required, since the shopper has to fill it next; else the\n * first field whose value fails its format (the submit control is disabled while one does, so it cannot\n * take focus); else the submit control, since saving is then the next step. Reads the live DOM inside\n * `panel`, so it never sees stale state. `country` is never a target (it always holds a value). Returns\n * whether focus landed: a disabled field, or a disabled submit control while saving, takes none.\n * `onFieldFocused` receives the name of a field that took focus, so the caller can mark it touched: the\n * shopper is sent there to read its message, and blur would never come.\n */\nfunction focusNextBillingControl(\n panel: HTMLElement | null,\n renderableFields: readonly FieldDescriptor[],\n onFieldFocused: (name: string) => void,\n): boolean {\n if (!panel) return false;\n const fields = panel.querySelectorAll<HTMLElement>('[data-fs-billing-address-field]');\n let target: HTMLElement | null = null;\n let malformed: HTMLElement | null = null;\n for (const field of fields) {\n const name = field.dataset.fsBillingAddressField ?? '';\n const input = field.querySelector('input');\n const descriptor = renderableFields.find((candidate) => candidate.name === name);\n if (!input || !descriptor) continue;\n const issue = getFieldIssue(descriptor, input.value);\n if (issue === 'required') {\n target = input;\n break;\n }\n if (issue === 'format') malformed ??= input;\n }\n target ??= malformed ?? panel.querySelector<HTMLElement>('[data-fs-billing-address-submit]');\n if (!target) return false;\n target.focus();\n const landed = target.ownerDocument.activeElement === target;\n const fieldName = target.closest<HTMLElement>('[data-fs-billing-address-field]')?.dataset.fsBillingAddressField;\n if (landed && fieldName) onFieldFocused(fieldName);\n return landed;\n}\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const BillingAddressDefinition = defineComponent<BillingAddressProps>({\n name: 'BillingAddress',\n importPath: '@vtex/faststore/components',\n component: BillingAddress,\n requiresContext: 'payment',\n actions: { payment: ['setBillingAddress'] },\n editor: { label: 'Billing Address', category: 'commerce' },\n schema: {\n sameAsDeliveryLabel: {\n type: 'string',\n default: 'Billing address same as delivery address',\n label: 'Same-as-delivery toggle label',\n },\n defaultCountry: { type: 'string', default: 'BRA', label: 'Default country (ISO-3)' },\n countryLabel: { type: 'string', default: 'Country', label: 'Country field label' },\n submitLabel: { type: 'string', default: 'Save billing address', label: 'Submit button label' },\n submitPendingLabel: { type: 'string', default: 'Saving...', label: 'Submit button pending label' },\n requiredNoticeLabel: {\n type: 'string',\n default: 'This payment method requires a billing address.',\n label: 'Required-billing-address notice label',\n },\n unsavedAddressNoticeLabel: {\n type: 'string',\n default: 'No billing address has been saved yet — the delivery address will be used unless you save one below.',\n label: 'Unsaved-address notice label',\n },\n // T73 (specs/render-the-billing-match-disclaimer-beside.md) — requirement 12.20. Always on unless\n // switched off; gated on these two props alone, never on usePayment()/local state/$data (FR-4).\n // Both defaults are INLINE LITERALS (FR-7): the native catalog builder extracts `schema`\n // statically, so an identifier-valued default is dropped from `dist/catalog.json` with no error.\n billingMatchNoticeLabel: {\n type: 'string',\n default:\n 'Your billing address must match the one on your card or bank statement, or your bank may decline the order.',\n label: 'Billing-match disclaimer',\n },\n billingMatchNoticeVisible: { type: 'boolean', default: true, label: 'Show the billing-match disclaimer' },\n // T31 (specs/make-the-payment-components-authored-copy.md, FR-7, Decision 5) — the eight canonical\n // descriptor field names `FIELD_LABELS_EN` used to hardcode, now schema props defaulting to that\n // same map's own values. `recipientNameLabel` is not rendered by the custom-address form today\n // (OMITTED_FORM_FIELD_NAMES) — kept for parity with the other seven.\n recipientNameLabel: { type: 'string', default: \"Recipient's name\", label: 'Recipient name field label' },\n streetLabel: { type: 'string', default: 'Street', label: 'Street field label' },\n numberLabel: { type: 'string', default: 'Number', label: 'Number field label' },\n complementLabel: { type: 'string', default: 'Complement (optional)', label: 'Complement field label' },\n neighborhoodLabel: { type: 'string', default: 'Neighborhood', label: 'Neighborhood field label' },\n cityLabel: { type: 'string', default: 'City', label: 'City field label' },\n stateLabel: { type: 'string', default: 'State', label: 'State field label' },\n postalCodeLabel: { type: 'string', default: 'Postal code', label: 'Postal code field label' },\n // T38: an inline literal, never an identifier — the native catalog builder extracts `schema`\n // statically and silently drops an identifier-valued default (INV-8).\n fieldRequiredMessage: {\n type: 'string',\n default: 'This field is required.',\n label: 'Field required message',\n },\n // An inline literal as well, for the same reason (INV-6).\n postalCodeInvalidMessage: {\n type: 'string',\n default: 'Enter a valid postal code.',\n label: 'Postal code format message',\n },\n disabled: { type: 'boolean', default: false, label: 'Disabled' },\n },\n guideline:\n \"Same-as-delivery toggle (default on) for the billing address; when off, mounts a country-specific address form (reusing shipping-address's country descriptors) and persists it via $actions.payment.setBillingAddress on an explicit submit control. Re-checking the toggle dispatches sameAsDelivery: true only once an address was actually persisted (otherwise it's a local no-op — there's nothing on the server to revert). Publishes only a boolean (never the address) onto the shared payment draft for usePlaceOrder to read. While the form is open and nothing has been saved yet, renders an inline status notice — the required attribute on the form's own fields is not a submit gate; Place Order can still complete billed to the delivery address if the shopper never saves. Must be inside a PaymentSectionRoot or PaymentSectionController; place as a sibling of CardEntry/SavedCards/Installments/SaveCard/PlaceOrder, never nested inside any of them. See billing-address.md.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport function BillingAddress({\n sameAsDeliveryLabel = 'Billing address same as delivery address',\n defaultCountry = 'BRA',\n countryLabel = 'Country',\n submitLabel = 'Save billing address',\n submitPendingLabel = 'Saving...',\n requiredNoticeLabel = 'This payment method requires a billing address.',\n unsavedAddressNoticeLabel = 'No billing address has been saved yet — the delivery address will be used unless you save one below.',\n billingMatchNoticeLabel = 'Your billing address must match the one on your card or bank statement, or your bank may decline the order.',\n billingMatchNoticeVisible = true,\n recipientNameLabel = \"Recipient's name\",\n streetLabel = 'Street',\n numberLabel = 'Number',\n complementLabel = 'Complement (optional)',\n neighborhoodLabel = 'Neighborhood',\n cityLabel = 'City',\n stateLabel = 'State',\n postalCodeLabel = 'Postal code',\n fieldRequiredMessage,\n postalCodeInvalidMessage,\n disabled = false,\n className,\n $actions: $actionsProp,\n ...props\n}: BillingAddressProps) {\n const $actions = useActionsOverride($actionsProp);\n // T31 (FR-7) — replaces the hardcoded `FIELD_LABELS_EN` map: keyed by the descriptor's own canonical\n // field name, same resolution as before (`renderableFields.map` below falls back to the descriptor's\n // own label when a field name has no matching prop; none does today).\n const fieldLabels: Readonly<Record<string, string>> = {\n recipientName: recipientNameLabel,\n street: streetLabel,\n number: numberLabel,\n complement: complementLabel,\n neighborhood: neighborhoodLabel,\n city: cityLabel,\n state: stateLabel,\n postalCode: postalCodeLabel,\n };\n // Also enforces the PaymentSectionRoot/PaymentSectionController ancestor: usePayment() throws\n // when rendered outside one.\n const {\n draft,\n selectedMethod,\n setDraftBillingAddressDifferent,\n setDraftBillingAddressUnsaved,\n placing,\n cardPaymentCollapsed,\n registerRefusalFocus,\n } = usePayment();\n // Fix pass, findings R2/S2: a stable identity for *this* mounted instance, so its cleanup below\n // retracts only its own contribution to the shared `billingAddressUnsaved` fact — never a second\n // instance's still-open, still-unsaved one (billing-address.md documents multiple instances under one\n // root as allowed). `useId()` is stable for the component's lifetime and distinct per instance, which\n // is exactly what a per-instance set key needs; it is never rendered or sent anywhere.\n const instanceKey = useId();\n // T175 (Decision 3): the id the toggle's `aria-controls` names in every state, rendered on the\n // always-mounted panel below it. Its own `useId()`, so `instanceKey` above stays unrendered.\n const panelId = useId();\n\n const [formDraft, setFormDraft] = useState<BillingAddressFormDraft>(() => ({\n postalCode: '',\n // A `defaultCountry` with no descriptor (a typo) falls back to the first available\n // country rather than shipping a dead form with no fields and a permanently-disabled submit\n // (review F7) — `getAvailableCountries()` is never empty in practice (Decision 7), but the raw\n // prop is the last-resort fallback if it ever were, matching today's behavior for that edge case.\n country: getCountryDescriptor(defaultCountry) ? defaultCountry : (getAvailableCountries()[0] ?? defaultCountry),\n street: '',\n number: '',\n complement: '',\n neighborhood: '',\n city: '',\n state: '',\n }));\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n // T38 (FR-6, Decision 8): per-field validity, mirroring card-entry.tsx:1115/:1150. A field joins this\n // set on blur, or when a refused submit moves focus to it (`markTouched`), where blur never comes.\n // Gated on it so a field is never marked invalid before the shopper reaches it (US-3,\n // Risk R5) — wiring `invalid` off `isBillingAddressFormValid` alone would mark every field invalid\n // the instant the form opens.\n const [touched, setTouched] = useState<ReadonlySet<string>>(() => new Set());\n // Local-only: whether the custom-address form is showing. Decoupled from `draft.billingAddressDifferent`\n // on purpose (PR #544 N1) — that published flag is what `gateway.ts` reads to decide whose address to\n // bill, so it must say \"different\" only once an address has actually been persisted differently, never\n // merely because the shopper opened this form. Initialized from the draft (via the same pure\n // `resolveSameAsDelivery` the checkbox used to read directly) so the two never start out of sync.\n const [formOpen, setFormOpen] = useState(() => !resolveSameAsDelivery(draft.billingAddressDifferent));\n\n // Re-syncs `formOpen` whenever the shared draft's `billingAddressDifferent` changes for a reason\n // outside THIS instance's own clicks (PR #544 N8) — e.g. a sibling `BillingAddress` mounted under the\n // same `PaymentSectionRoot` persists or reverts an address through its own copy of this same shared\n // context. A bare local `useState` only ever reflects the instance that called its own setter; without\n // this effect, a second mounted instance keeps showing whatever `formOpen` it computed at its own\n // mount forever, even after `draft.billingAddressDifferent` — the one fact the gateway actually reads\n // — has moved on. Deliberately keyed on the primitive `draft.billingAddressDifferent`, not the `draft`\n // object itself: `draft` is a stable `useMemo` over seven facts (`use-payment-method-selection.ts`),\n // so its identity changes whenever ANY of them does — including `draftBin`, which `CardEntry`\n // publishes on every keystroke of the card number. Keying this effect on the whole object instead\n // would refire it on each of those keystrokes while this form is open and nothing is yet persisted,\n // recomputing `formOpen` from an unchanged `billingAddressDifferent` and closing the form mid-input\n // (review #544 N15 — a dedicated regression test in `billing-address.test.tsx` pins this). This\n // differs from `shipping-address.tsx:645`'s own snapshot-keyed effect, whose reason is Apollo\n // returning a new-but-equal object reference every render (its own `biome-ignore` comment says so) —\n // `draft` here is a local `useMemo`, already stable; the hazard is refiring on an unrelated fact\n // change, not on render churn. Does NOT reintroduce N1: this component's own uncheck transition (below) sets\n // `formOpen` without ever touching the draft, so that transition alone never changes this effect's\n // dependency and never fires it. Deleting this effect is exactly the mutant PR #544's re-verification\n // named V-P2/N8 — the two-instance regression test below fails without it.\n useEffect(() => {\n setFormOpen(!resolveSameAsDelivery(draft.billingAddressDifferent));\n }, [draft.billingAddressDifferent]);\n\n const sameAsDelivery = !formOpen;\n // PR #544 N7: visible only while the shopper is on the custom-address path AND nothing has actually\n // been persisted differently yet — the exact window in which Place Order would silently bill the\n // delivery address. Mirrors the same `draft.billingAddressDifferent !== true` check\n // `handleSameAsDeliveryChange`'s local-no-op branch already uses, so the notice and the no-op branch\n // can never disagree about what \"nothing persisted\" means. T191 (FR-8): never while the card payment\n // is collapsed, when this component renders nothing — the gate below must not refuse Place order over\n // a form the shopper cannot see, and the notice and the gate keep reading one expression.\n const unsavedAddressNoticeVisible = !cardPaymentCollapsed && formOpen && draft.billingAddressDifferent !== true;\n\n // T24's submit gate (specs/cardholder-document-submit-gate.md FR-6): publishes the EXACT same fact\n // the notice above renders from, so the gate and the notice can never disagree (Risk R5). Keyed by\n // this instance's own `instanceKey` (fix pass, findings R2/S2): `billingAddressUnsaved` is one fact\n // shared by every `BillingAddress` mounted under this root, so the cleanup must retract only *this*\n // instance's own contribution on unmount — never a second, still-open, still-unsaved instance's own\n // block (measured: an unconditional publish here left the CTA proceeding while that instance's own\n // notice kept rendering, billing the declined delivery address anyway). Not cosmetics either way: an\n // unmounted `BillingAddress` must not leave the CTA blocked with no form on screen left to fix it\n // (Risk R2) when it was the *only* instance.\n useEffect(() => {\n setDraftBillingAddressUnsaved(instanceKey, unsavedAddressNoticeVisible);\n return () => setDraftBillingAddressUnsaved(instanceKey, false);\n }, [instanceKey, unsavedAddressNoticeVisible, setDraftBillingAddressUnsaved]);\n\n // T212: while the form is open and on screen, a refusal naming the billing form may move focus into it.\n // Registered by the instance that renders the form, so a closed or collapsed instance never takes it.\n const canTakeFocus = !cardPaymentCollapsed && formOpen;\n const draftCountry = formDraft.country;\n const draftDescriptor = useMemo(() => getCountryDescriptor(draftCountry), [draftCountry]);\n const renderableFields = useMemo(\n () => (draftDescriptor ? getRenderableFields(draftDescriptor) : []),\n [draftDescriptor],\n );\n useEffect(() => {\n if (!canTakeFocus) return;\n const markTouched = (name: string) => setTouched((prev) => withTouched(prev, name));\n return registerRefusalFocus('billing-form', () =>\n focusNextBillingControl(document.getElementById(panelId), renderableFields, markTouched),\n );\n }, [canTakeFocus, renderableFields, panelId, registerRefusalFocus]);\n\n const billingAddressRequired = selectedMethod?.validator.billingAddressRequired ?? false;\n const fieldsDisabled = disabled || pending;\n const requiredMessage = resolveAuthoredCopy(fieldRequiredMessage, 'This field is required.');\n const invalidMessage = resolveAuthoredCopy(postalCodeInvalidMessage, 'Enter a valid postal code.');\n\n async function handleSameAsDeliveryChange(nextSameAsDelivery: boolean) {\n setError(null);\n\n if (!nextSameAsDelivery) {\n // Opens the custom-address form locally. Nothing is dispatched and the shared draft is left\n // untouched here — the shopper has typed nothing yet, and publishing `billingAddressDifferent:\n // true` before an address is actually persisted is exactly the false claim review #544's N1\n // measured reaching the gateway in two clicks. Only a successful submit (handleSubmit below) may\n // ever publish `true` — see this file's own top-level doc comment.\n setFormOpen(true);\n return;\n }\n\n if (draft.billingAddressDifferent !== true) {\n // Nothing was ever persisted differently — either the shopper never submitted, or an earlier\n // submit failed — so there is nothing on the server to revert. Closing the form is a local-only\n // action: dispatching `setBillingAddress({ sameAsDelivery: true })` here would be a wasted round\n // trip reverting an address the server was never asked to set in the first place.\n setFormOpen(false);\n return;\n }\n\n const setBillingAddress = $actions?.payment?.setBillingAddress;\n // Silent no-op when the action namespace is not injected, mirroring `SaveCard`'s / `selectMethod`'s\n // \"no-op (no error, no throw)\" convention.\n if (!setBillingAddress) return;\n\n setPending(true);\n try {\n const userErrors = await setBillingAddress({ sameAsDelivery: true });\n const [firstError] = userErrors;\n if (firstError) {\n setError(firstError.message);\n // A refused revert (review F3): the persisted address is still the custom one the shopper had\n // before, so the form stays open (`formOpen` untouched) and the draft keeps claiming\n // \"different\" — neither claims \"same as delivery\" the server never accepted. There is nothing\n // to roll back here, unlike before this pass: the draft is only ever written `false` a few\n // lines below, after `setBillingAddress` has already resolved with no error.\n } else {\n setDraftBillingAddressDifferent(false);\n // Redundant with the resync effect above once this draft write lands (the effect closes the\n // form on its own whenever `draft.billingAddressDifferent` moves to `false` — review #544 N18)\n // — kept anyway so this instance closes synchronously with its own click rather than waiting on\n // a re-render, which costs nothing since both agree on the outcome.\n setFormOpen(false);\n }\n } finally {\n setPending(false);\n }\n }\n\n function handleFieldChange(name: string, value: string) {\n setFormDraft((prev) => ({ ...prev, [name]: value }));\n }\n\n /** Marks a field touched on its first blur (T38, FR-6). Idempotent on every later blur — a `Set`\n * already containing `name` is returned unchanged, so this never triggers a redundant re-render. */\n function handleFieldBlur(name: string) {\n setTouched((prev) => withTouched(prev, name));\n }\n\n function handleCountryChange(value: string | null) {\n // Base UI's `SelectRoot` (`select/root/SelectRoot.js`) only ever calls `onValueChange` with one of\n // `Select.Item`'s own `value`s — all descriptor-backed, non-null ISO codes here (verified by\n // reading its source, not assumed) — so `value` is `null` on no path this component's own `Select`\n // below can reach. The `?? prev.country` fallback exists only to satisfy\n // `BillingAddressFormDraft.country: string`'s type, not as a runtime branch pretending to be tested\n // (review F10's residual, R5′): dropping the fallback (`country: value`) is a `pnpm typecheck`\n // failure, not merely an unproven `if` — a stronger, mechanical guarantee than a guard clause no\n // real interaction could ever exercise.\n //\n // Preserves every other field's value across the switch (Decision 7) — matches\n // `shipping-address`'s own documented behavior: a value typed under one country resurfaces if the\n // shopper switches away and back, even while the field is hidden for the currently-selected one.\n setFormDraft((prev) => ({ ...prev, country: value ?? prev.country }));\n }\n\n async function handleSubmit() {\n const setBillingAddress = $actions?.payment?.setBillingAddress;\n setError(null);\n if (!setBillingAddress) return;\n\n // Only include a field the active country's descriptor actually renders (mirrors\n // `shipping-address.tsx`'s own `isFieldActive` guard at its submit handler) — otherwise a value\n // typed under a previously-selected country (e.g. `neighborhood` retained in the draft after\n // switching BR -> USA) would leak into the submitted payload even though the field was never\n // rendered, editable, or clearable for the country actually being submitted (review F2).\n // `postalCode` and `country` are unconditional: every descriptor declares both.\n const isFieldActive = (name: string) => renderableFields.some((field) => field.name === name);\n\n setPending(true);\n try {\n const userErrors = await setBillingAddress({\n sameAsDelivery: false,\n postalCode: formDraft.postalCode,\n country: formDraft.country,\n ...(isFieldActive('street') ? { street: formDraft.street } : {}),\n ...(isFieldActive('number') ? { number: formDraft.number } : {}),\n ...(isFieldActive('complement') ? { complement: formDraft.complement } : {}),\n ...(isFieldActive('neighborhood') ? { neighborhood: formDraft.neighborhood } : {}),\n ...(isFieldActive('city') ? { city: formDraft.city } : {}),\n ...(isFieldActive('state') ? { state: formDraft.state } : {}),\n });\n const [firstError] = userErrors;\n if (firstError) {\n setError(firstError.message);\n } else {\n // The one place `billingAddressDifferent` may ever become `true` (PR #544 N1) — only once the\n // server actually holds a different address, so the flag means exactly what `gateway.ts`'s own\n // doc comment already claims: \"`setBillingAddress` already persisted it … so the gateway reads\n // it from there.\"\n setDraftBillingAddressDifferent(true);\n }\n } finally {\n setPending(false);\n }\n }\n\n // T191 (FR-7, Decision 3): nothing renders while the card payment is collapsed. Returned after the\n // last hook, so this instance stays mounted and keeps what the shopper typed for when the card is back.\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-billing-address=\"\"\n {...(placing ? { 'data-fs-billing-address-placing': '' } : {})}\n {...(pending ? { 'data-fs-billing-address-pending': '' } : {})}\n {...(sameAsDelivery ? { 'data-fs-billing-address-same-as-delivery': '' } : {})}\n >\n {billingMatchNoticeVisible && billingMatchNoticeLabel.trim() ? (\n <p data-fs-billing-address-match-notice=\"\">{billingMatchNoticeLabel}</p>\n ) : null}\n <div data-fs-billing-address-toggle-field=\"\">\n <Field.Root disabled={fieldsDisabled} data-fs-field-root=\"\">\n <Field.Label>\n <Checkbox.Root\n checked={sameAsDelivery}\n onCheckedChange={handleSameAsDeliveryChange}\n disabled={fieldsDisabled}\n aria-controls={panelId}\n >\n <Checkbox.Indicator>\n <Icon name=\"checked\" />\n </Checkbox.Indicator>\n </Checkbox.Root>\n {sameAsDeliveryLabel}\n </Field.Label>\n </Field.Root>\n {billingAddressRequired ? <p data-fs-billing-address-required-note=\"\">{requiredNoticeLabel}</p> : null}\n </div>\n\n {/* T175 (Decision 3): always mounted, so the toggle's `aria-controls` resolves in every state. It\n takes no className or props, and it is `hidden` only while empty (INV-2): a store's display\n utility can beat `[hidden]`, but then it shows an empty box, never content. */}\n <div id={panelId} data-fs-billing-address-panel=\"\" hidden={sameAsDelivery}>\n {sameAsDelivery ? null : (\n <div data-fs-billing-address-form=\"\">\n {unsavedAddressNoticeVisible ? (\n <p role=\"status\" data-fs-billing-address-unsaved-notice=\"\">\n {unsavedAddressNoticeLabel}\n </p>\n ) : null}\n <Field.Root disabled={fieldsDisabled} data-fs-field-root=\"\" data-fs-billing-address-field=\"country\">\n <Field.Label>{countryLabel}</Field.Label>\n <Select.Root\n value={formDraft.country}\n onValueChange={handleCountryChange}\n disabled={fieldsDisabled}\n required\n >\n <Select.Trigger>\n {/* Resolves the label directly from the country descriptor rather than relying on\n Base UI's automatic item-label lookup, which only reflects a mounted Select.Item —\n mirrors `ShippingAddressCountryControl`'s identical reasoning. English override per\n `getCountryLabel` (review F5) — the Brazilian descriptor's own name is Portuguese. */}\n <Select.Value>{(value) => (value ? getCountryLabel(value) : value)}</Select.Value>\n <Select.Icon>▾</Select.Icon>\n </Select.Trigger>\n <Select.Portal>\n <Select.Positioner>\n <Select.Popup>\n <Select.List>\n {getAvailableCountries().map((iso) => {\n const label = getCountryLabel(iso);\n return (\n <Select.Item key={iso} value={iso} label={label}>\n {label}\n </Select.Item>\n );\n })}\n </Select.List>\n </Select.Popup>\n </Select.Positioner>\n </Select.Portal>\n </Select.Root>\n </Field.Root>\n\n {renderableFields.map((field) => {\n // T38 (FR-6): gated on `touched` (blur, or a refused submit focusing the field) so nothing is\n // marked invalid before the shopper reaches it (US-3, Risk R5). A touched field is invalid once\n // `getFieldIssue` finds an issue with its value: blank (except `complement`) or not in the\n // selected country's format (INV-3/INV-4). `areRenderableFieldsValid` reads the same\n // `getFieldIssue`, so the two are never allowed to disagree about a field.\n const issue = touched.has(field.name) ? getFieldIssue(field, formDraft[field.name] ?? '') : null;\n const invalid = issue !== null;\n return (\n <Field.Root\n key={field.name}\n disabled={fieldsDisabled}\n invalid={invalid}\n data-fs-field-root=\"\"\n data-fs-billing-address-field={field.name}\n >\n {/* Authored field-label prop per `fieldLabels` (T31, FR-7) — the Brazilian descriptor's own\n labels are Portuguese, so every country gets an authored English one. `||`, not `??`\n (fix pass round 2, finding N1 — same defect class as place-order.tsx's finding 7 from\n round 1): a store author can write `\"\"` for a field's authored label — a valid\n `type: 'string'` prop value — and `??` only falls through on `null`/`undefined`, so an\n authored empty string would render an empty accessible name on a control that is\n required for every field but `complement`. Pre-existing hazard, not fixed here: `||`\n still renders blank for a whitespace-only authored label — inside T31's ownership,\n reported not fixed (T38 § 5). */}\n <Field.Label>{fieldLabels[field.name] || field.label}</Field.Label>\n <Field.Control\n value={formDraft[field.name] ?? ''}\n onChange={(event) => handleFieldChange(field.name, event.currentTarget.value)}\n onBlur={() => handleFieldBlur(field.name)}\n disabled={fieldsDisabled}\n // Every rendered field is required once \"different\" is selected, except `complement`\n // (`OPTIONAL_FORM_FIELD_NAMES`) — PR #544 N1 item 1. Not a like-for-like precedent with\n // `CardholderDocument`'s own `required={documentRequired}`: that one forwards to a\n // particle with its own declared prop, a root state hook, and a paired `Field.Error`\n // driven by a server-sourced validator flag, where this sets `required` directly off a\n // locally hardcoded set instead (full comparison: specs/billing-address.md Accessibility\n // Contract).\n required={!OPTIONAL_FORM_FIELD_NAMES.has(field.name)}\n />\n {invalid ? (\n <Field.Error match>\n {issue === 'format' ? getFieldFormatMessage(field, invalidMessage) : requiredMessage}\n </Field.Error>\n ) : null}\n </Field.Root>\n );\n })}\n\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={fieldsDisabled || !canSaveBillingDraft(draftDescriptor, renderableFields, formDraft)}\n data-fs-billing-address-submit=\"\"\n >\n {pending ? submitPendingLabel : submitLabel}\n </button>\n </div>\n )}\n </div>\n\n {error ? (\n <div role=\"alert\" data-fs-billing-address-error=\"\">\n {error}\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"billing-address.js","names":[],"sources":["../../../src/matter/billing-address/billing-address.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ContentValue,\n defineComponent,\n defineContentType,\n type MessageFieldName,\n type MessageLocaleProp,\n} from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { useEffect, useId, useMemo, useState } from 'react';\nimport { Checkbox } from '../../particles/checkbox/checkbox.js';\nimport { Field } from '../../particles/field/field.js';\nimport { Icon } from '../../particles/icon/icon.js';\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 { countryDisplayName } from '../../sdk/address/country-display.js';\nimport { useStorefrontFormatters } from '../../sdk/format/client.js';\nimport { formatErrorCode } from '../../sdk/i18n/format-error-code.js';\nimport { useMessageFormatter } from '../../sdk/i18n/use-message-formatter.js';\nimport type { PaymentUserError } from '../../sdk/payment/operations.js';\nimport { usePayment } from '../payment/payment.js';\nimport { normalizeAddressField, pickActiveTextFields } from '../shipping-address/address-payload.js';\nimport {\n type CountryDescriptor,\n type FieldDescriptor,\n getAvailableCountries,\n getCountryDescriptor,\n satisfiesFieldRule,\n} from '../shipping-address/country-descriptors.js';\nimport { LIBRARY_COUNTRIES, useLibraryCountryDescriptors } from '../shipping-address/library-country-descriptors.js';\n\n/**\n * The `BillingAddress` matter (specs/billing-address.md, T11 of the Credit Card epic;\n * docs/features/credit-card/rfc.md § D6). A \"same as delivery\" toggle (default on) plus, when off, a\n * country-specific address form that persists via `$actions.payment.setBillingAddress`\n * (already shipped by T4, zero consumers before this task).\n *\n * RFC D6 names one shared `Address` under shipping and billing. What the two matters share is the country\n * descriptors (`../shipping-address/country-descriptors.js`, the one place a country's field set and format\n * rules live), the rule verdict (`satisfiesFieldRule`) and the payload helpers\n * (`../shipping-address/address-payload.js`: value normalisation and which text fields a country sends). The\n * controls are not shared: this component builds its own, over local state, where shipping binds\n * react-hook-form, and their `data-fs-*` hooks, required rules and error copy differ (specs/billing-address.md\n * Decision 1). Its copy is its own `$content`; country names come from `Intl.DisplayNames`.\n *\n * There is NO read-back path for a persisted billing address (specs/billing-address.md Decision 3):\n * `type Payment` carries no `billingAddress` field, and `setBillingAddress` itself resolves only\n * `userErrors`, discarding the mutation's own echo. This component declares no fragment and renders\n * nothing below the toggle while \"same as delivery\" is checked, not even the delivery address\n * (specs/stop-re-rendering-the-full-delivery.md, T175). The custom-address form always starts blank,\n * and its own local state is the sole source of truth for what was typed.\n *\n * Only a boolean rides the shared `draft` (`billingAddressDifferent`, widened onto `PaymentDraft` by\n * this task under sole ownership of `use-payment-method-selection.ts` — specs/billing-address.md\n * Decision 5); the address itself is persisted server-side by this component's own action call, never\n * passed through the draft. `setBillingAddress` is dispatched at exactly two points (Decision 9): the\n * toggle's transition back to \"same as delivery\", and the custom form's explicit submit control —\n * never on mount, never per keystroke.\n *\n * **`draft.billingAddressDifferent` only ever becomes `true` once `setBillingAddress` has actually\n * persisted a different address (PR #544 N1).** Unchecking \"same as delivery\" opens the form\n * (`formOpen`, local-only state below) but does not touch the draft — `gateway.ts`'s own doc comment\n * (\"`setBillingAddress` already persisted it … so the gateway reads it from there\") is the contract\n * this must not break: publishing `true` before anything is persisted would point the gateway at an\n * `invoiceData` address nothing ever wrote. The engineer's own resolution for this (session of\n * 2026-07-31): the new-address fields become required whenever \"different\" is selected, so this\n * component's own submit blocks on an incomplete address (`isBillingAddressFormValid`) — but that\n * alone cannot stop a shopper from declining \"same as delivery\" and clicking Place Order without ever\n * opening the form's own submit control, which is why the draft write itself is gated on success, not\n * merely the submit button.\n *\n * **The native `required` attribute below is cosmetic here, not an enforcement mechanism (PR #544\n * N7).** This file renders no `aria-required` at all — only `required` (native HTML). There is no\n * `<form>` in this render tree and the submit control is `type=\"button\"`, so the browser's own\n * constraint validation never runs — nothing in this file or its tests may claim this attribute itself\n * prevents a shopper from placing an order with an unsaved address; the actual submit gate is\n * `isBillingAddressFormValid`'s verdict (`areRenderableFieldsValid`), read at the submit control's own\n * `disabled` below. Since T38\n * (specs/harden-and-verify-accessibility-across-the.md FR-6) each rendered field does carry a\n * `Field.Error`, but it is driven by a local `touched` set (a field joins it on blur, or when a refused\n * submit moves focus to it) and `getFieldIssue`'s verdict (`required` for a blank field, `format` for a\n * non-blank value that fails the country's postal pattern), never by `required`'s own native\n * `ValidityState` — so this paragraph's point is unchanged: `required` itself still enforces nothing\n * here. What this component owes, and does: make the gap **visible** — an inline `role=\"status\"`\n * notice while the form is open and nothing has been persisted yet — and publish that same fact\n * (`setDraftBillingAddressUnsaved`) onto the shared draft, from the identical expression the notice\n * renders from (T24, specs/cardholder-document-submit-gate.md FR-6). **The actual hard block lives\n * elsewhere**: T24's `usePaymentSubmitGate()` reads that published fact and refuses Place Order\n * client-side — this component's own job stops at publishing the fact and showing the notice, never at\n * gating the CTA itself. See `specs/billing-address.md`'s Documentation Gaps #1 for the deferral this closes.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Local-only values for the \"different\" address form, keyed by the country descriptor's own\n * canonical field names — a plain string index signature, not a closed set of properties, so a future\n * country descriptor introducing a new field name needs no type change here (mirrors\n * `FieldDescriptor.name: string`'s own deliberately untyped choice; specs/billing-address.md\n * Decision 7). `postalCode` and `country` are named explicitly because this component's own logic\n * reads both directly. Never carries a `recipientName` key: `PaymentBillingAddressUpdateInput` has no\n * such field (Decision 4) — the render loop filters it out of whatever the descriptor lists.\n */\nexport type BillingAddressFormDraft = Readonly<{\n postalCode: string;\n country: string;\n [fieldName: string]: string;\n}>;\n\nexport type BillingAddressProps = Omit<ComponentPropsWithRef<'div'>, 'children'> &\n MessageLocaleProp & {\n /** ISO 3166-1 alpha-3 default country for the custom-address form's country selector. */\n defaultCountry?: string;\n /** Switches the always-on billing-match disclaimer (requirement 12.20) on or off. Defaults to `true`\n * (Decision 5 — the requirement's own intent); a blank `billingMatchNoticeLabel` message also\n * suppresses it (FR-3). Reads no `usePayment()` value, no local state, no `$data` (FR-4). */\n billingMatchNoticeVisible?: boolean;\n /** Disables the toggle, every form field, the country selector, and the submit control. */\n disabled?: boolean;\n /** Compiler-injected action surface; carries `payment.setBillingAddress`. */\n $actions?: ComponentActions;\n /** The toggle, notices, field labels, messages and submit labels of the form. */\n $content?: ContentValue<typeof BillingAddressContent>;\n };\n\n/** Canonical field names the custom-address form never renders, even when a country descriptor\n * lists them — because the write contract has no matching input slot (Decision 4). */\nconst OMITTED_FORM_FIELD_NAMES = new Set(['recipientName']);\n\n/** Descriptor field names the custom-address form renders but never marks required, even while\n * \"different\" is selected (PR #544 N1 item 1 — the engineer's own rule, verbatim: \"if the person says\n * the address is different, the new address fields must be required\"). `complement` is the one\n * exception: the `complementLabel` message labels it optional (\"Complement (optional)\"),\n * and neither a real-world address nor the server's own write contract treats it as load-bearing.\n * Shared between the `required` attribute below and `isBillingAddressFormValid`, so the two never\n * drift apart. A `complementLabel` message that drops the \"(optional)\" suffix loses that visual cue\n * without changing this set — the two are independent (R7, billing-address.md). */\nconst OPTIONAL_FORM_FIELD_NAMES = new Set(['complement']);\n\n/**\n * The message field that labels each descriptor field, by canonical field name. `recipientName` is listed\n * though the form never renders it (`OMITTED_FORM_FIELD_NAMES`). A field a library-backed descriptor adds\n * with no entry here shows its canonical name.\n */\nconst LABEL_FIELDS = {\n recipientName: 'recipientNameLabel',\n street: 'streetLabel',\n number: 'numberLabel',\n complement: 'complementLabel',\n neighborhood: 'neighborhoodLabel',\n city: 'cityLabel',\n state: 'stateLabel',\n postalCode: 'postalCodeLabel',\n} as const satisfies Record<string, MessageFieldName<typeof BillingAddressContent>>;\n\nfunction labelFieldOf(name: string): (typeof LABEL_FIELDS)[keyof typeof LABEL_FIELDS] | undefined {\n return Object.hasOwn(LABEL_FIELDS, name) ? LABEL_FIELDS[name as keyof typeof LABEL_FIELDS] : undefined;\n}\n\nfunction countryNameOf(iso: string, displayName: Parameters<typeof countryDisplayName>[1]): string {\n return countryDisplayName(iso, displayName) ?? iso;\n}\n\n/** `defaultCountry` when it has a descriptor or is library-backed, else the first available country. */\nfunction initialCountryOf(defaultCountry: string): string {\n if (getCountryDescriptor(defaultCountry) || LIBRARY_COUNTRIES.includes(defaultCountry)) return defaultCountry;\n return getAvailableCountries()[0] ?? defaultCountry;\n}\n\n/** The field's label message, or its descriptor name when no message field covers it. */\nfunction fieldLabelOf(\n name: string,\n formatLabel: (field: (typeof LABEL_FIELDS)[keyof typeof LABEL_FIELDS]) => string,\n): string {\n const labelField = labelFieldOf(name);\n return labelField ? formatLabel(labelField) : name;\n}\n\n// ---------------------------------------------------------------------------\n// Pure decision functions — exported so both are unit-testable independent of any rendering\n// (specs/billing-address.md § Resolution logic).\n// ---------------------------------------------------------------------------\n\n/**\n * Whether the toggle reads as \"same as delivery\" — `true` whenever the published flag is `null`\n * (never touched, the default) or `false` (explicitly reverted); only an explicit `true` means the\n * shopper is on the custom-address path. Takes no closure over component state, so it is safe to call\n * fresh on every render.\n */\nexport function resolveSameAsDelivery(billingAddressDifferent: boolean | null | undefined): boolean {\n return billingAddressDifferent !== true;\n}\n\n/** Why a rendered field is not acceptable: blank (`required`), or non-blank and not in the selected\n * country's format (`format`). `null` means acceptable. */\ntype FieldIssue = 'required' | 'format' | null;\n\n/**\n * The one verdict on a rendered field, read by both the submit gate (`areRenderableFieldsValid`, behind\n * `isBillingAddressFormValid`) and the per-field error (the `issue` computed per field in the render), so the\n * two can never disagree (T38 Decision 8). Blank (after\n * trim) wins over format: `complement` (`OPTIONAL_FORM_FIELD_NAMES`) may be blank, every other field may\n * not. A non-blank value is tested raw against the descriptor's `rule.pattern` and its function-valued\n * `rule.validate` (`satisfiesFieldRule`) — the same predicates the delivery form's react-hook-form rule\n * applies, so a surrounding space is a format error in both for a pattern. A whitespace-only value is the\n * one divergence: it is blank here (`required`), where the delivery form would run the pattern on it (see\n * `satisfiesFieldPattern`). No postal format is defined here: the descriptor's `rule` is the only definition.\n */\nfunction getFieldIssue(field: FieldDescriptor, value: string): FieldIssue {\n if (value.trim().length === 0) return OPTIONAL_FORM_FIELD_NAMES.has(field.name) ? null : 'required';\n return satisfiesFieldRule(field, value) ? null : 'format';\n}\n\n/**\n * The message under a field whose verdict is `format`. Only the postal code has a format today, and it reads\n * the formatted `postalCodeInvalidMessage`, so a format a descriptor gives another field later never surfaces\n * the postal-code copy: that field needs a message field of its own, and shows none until it has one.\n */\nexport function getFieldFormatMessage(field: FieldDescriptor, postalCodeMessage: string): string {\n return field.name === 'postalCode' ? postalCodeMessage : '';\n}\n\n/** The descriptor's fields the custom-address form renders: everything but `OMITTED_FORM_FIELD_NAMES`. */\nfunction getRenderableFields(descriptor: CountryDescriptor): readonly FieldDescriptor[] {\n return descriptor.fields.filter((field) => !OMITTED_FORM_FIELD_NAMES.has(field.name));\n}\n\n/**\n * Whether the custom-address form may be submitted. Stricter than the server's own\n * `MISSING_BILLING_ADDRESS_FIELDS` floor (\"sameAsDelivery is false but postalCode **or country** was\n * omitted\" [source: `core.graphql:2857-2858`]) — every field the active country's descriptor renders,\n * except `complement` (`OPTIONAL_FORM_FIELD_NAMES`), must be non-blank (PR #544 N1 item 1: the\n * engineer's own rule that the new-address fields are required once \"different\" is selected), and every\n * non-blank value must match its descriptor's `rule.pattern`. Resolves the descriptor from\n * `formDraft.country` itself rather than taking a second parameter, so this stays a single\n * self-contained fact about one `BillingAddressFormDraft` value. `country` itself is not part of\n * `descriptor.fields` (Decision 7) and is never checked here — it always carries a non-blank value from\n * `defaultCountry` or the selector's own current value (review F12), so it can never be the field this\n * gate would fail on.\n */\nexport function isBillingAddressFormValid(formDraft: BillingAddressFormDraft): boolean {\n // No descriptor means no known required-field list to satisfy — a vacuous `true` would be exactly\n // wrong here. Not reachable through the rendered component today (review F7's fallback already\n // resolves `formDraft.country` to a real descriptor before the form ever mounts), but this function\n // is exported and unit-tested directly with such a country, so it must not depend on the render path.\n const descriptor = getCountryDescriptor(formDraft.country);\n return descriptor ? areRenderableFieldsValid(getRenderableFields(descriptor), formDraft) : false;\n}\n\n/**\n * The verdict behind `isBillingAddressFormValid`, taking the renderable fields as an argument so the\n * render path can pass the list it already memoises per country instead of resolving them again on every\n * render. One body for both callers, so the gate can never disagree with itself.\n */\nfunction areRenderableFieldsValid(\n renderableFields: readonly FieldDescriptor[],\n formDraft: BillingAddressFormDraft,\n): boolean {\n return renderableFields.every((field) => getFieldIssue(field, formDraft[field.name] ?? '') === null);\n}\n\n/**\n * Whether the submit control may fire: the active country has a descriptor and every field it renders is\n * valid. Mirrors `isBillingAddressFormValid`, over the descriptor and fields the render path already holds.\n */\nfunction canSaveBillingDraft(\n descriptor: CountryDescriptor | undefined,\n renderableFields: readonly FieldDescriptor[],\n formDraft: BillingAddressFormDraft,\n): boolean {\n return descriptor !== undefined && areRenderableFieldsValid(renderableFields, formDraft);\n}\n\n/** The touched set with `name` added. Returns `prev` itself when it already holds `name`, so a state update\n * built on it never triggers a redundant re-render. */\nfunction withTouched(prev: ReadonlySet<string>, name: string): ReadonlySet<string> {\n return prev.has(name) ? prev : new Set(prev).add(name);\n}\n\n/**\n * Takes focus for a refusal that named the billing form (T212, Decision 3): the first rendered field, in\n * render order, whose value is blank and which is required, since the shopper has to fill it next; else the\n * first field whose value fails its format (the submit control is disabled while one does, so it cannot\n * take focus); else the submit control, since saving is then the next step. Reads the live DOM inside\n * `panel`, so it never sees stale state. `country` is never a target (it always holds a value). Returns\n * whether focus landed: a disabled field, or a disabled submit control while saving, takes none.\n * `onFieldFocused` receives the name of a field that took focus, so the caller can mark it touched: the\n * shopper is sent there to read its message, and blur would never come.\n */\nfunction focusNextBillingControl(\n panel: HTMLElement | null,\n renderableFields: readonly FieldDescriptor[],\n onFieldFocused: (name: string) => void,\n): boolean {\n if (!panel) return false;\n const fields = panel.querySelectorAll<HTMLElement>('[data-fs-billing-address-field]');\n let target: HTMLElement | null = null;\n let malformed: HTMLElement | null = null;\n for (const field of fields) {\n const name = field.dataset.fsBillingAddressField ?? '';\n const input = field.querySelector('input');\n const descriptor = renderableFields.find((candidate) => candidate.name === name);\n if (!input || !descriptor) continue;\n const issue = getFieldIssue(descriptor, input.value);\n if (issue === 'required') {\n target = input;\n break;\n }\n if (issue === 'format') malformed ??= input;\n }\n target ??= malformed ?? panel.querySelector<HTMLElement>('[data-fs-billing-address-submit]');\n if (!target) return false;\n target.focus();\n const landed = target.ownerDocument.activeElement === target;\n const fieldName = target.closest<HTMLElement>('[data-fs-billing-address-field]')?.dataset.fsBillingAddressField;\n if (landed && fieldName) onFieldFocused(fieldName);\n return landed;\n}\n\n// ---------------------------------------------------------------------------\n// Definition\n// ---------------------------------------------------------------------------\n\nexport const BillingAddressContent = defineContentType({\n id: 'billing-address',\n title: 'Billing Address',\n description: 'Copy of the billing address toggle, its notices, the custom-address form and its messages.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n sameAsDeliveryLabel: {\n type: 'string',\n title: 'Same-as-delivery toggle label',\n format: 'icu-message',\n messageSource: 'Billing address same as delivery address',\n },\n countryLabel: { type: 'string', title: 'Country field label', format: 'icu-message', messageSource: 'Country' },\n submitLabel: {\n type: 'string',\n title: 'Submit button label',\n format: 'icu-message',\n messageSource: 'Save billing address',\n },\n submitPendingLabel: {\n type: 'string',\n title: 'Submit button pending label',\n format: 'icu-message',\n messageSource: 'Saving...',\n },\n requiredNoticeLabel: {\n type: 'string',\n title: 'Required-billing-address notice',\n description: \"Shown when the selected method's validator marks a billing address required.\",\n format: 'icu-message',\n messageSource: 'This payment method requires a billing address.',\n },\n unsavedAddressNoticeLabel: {\n type: 'string',\n title: 'Unsaved-address notice',\n description: 'Shown while the form is open and no address has been saved yet.',\n format: 'icu-message',\n messageSource:\n 'No billing address has been saved yet — the delivery address will be used unless you save one below.',\n },\n billingMatchNoticeLabel: {\n type: 'string',\n title: 'Billing-match disclaimer',\n description: 'Always on unless billingMatchNoticeVisible is false; blank shows no disclaimer.',\n format: 'icu-message',\n messageSource:\n 'Your billing address must match the one on your card or bank statement, or your bank may decline the order.',\n },\n recipientNameLabel: {\n type: 'string',\n title: 'Recipient name field label',\n description: 'Not rendered today: the billing write has no recipient name.',\n format: 'icu-message',\n messageSource: \"Recipient's name\",\n },\n streetLabel: { type: 'string', title: 'Street field label', format: 'icu-message', messageSource: 'Street' },\n numberLabel: { type: 'string', title: 'Number field label', format: 'icu-message', messageSource: 'Number' },\n complementLabel: {\n type: 'string',\n title: 'Complement field label',\n description: 'Its \"(optional)\" is the only sign the field is optional.',\n format: 'icu-message',\n messageSource: 'Complement (optional)',\n },\n neighborhoodLabel: {\n type: 'string',\n title: 'Neighborhood field label',\n format: 'icu-message',\n messageSource: 'Neighborhood',\n },\n cityLabel: { type: 'string', title: 'City field label', format: 'icu-message', messageSource: 'City' },\n stateLabel: { type: 'string', title: 'State field label', format: 'icu-message', messageSource: 'State' },\n postalCodeLabel: {\n type: 'string',\n title: 'Postal code field label',\n format: 'icu-message',\n messageSource: 'Postal code',\n },\n fieldRequiredMessage: {\n type: 'string',\n title: 'Field required message',\n description: 'Shown under a required field left blank.',\n format: 'icu-message',\n messageSource: 'This field is required.',\n },\n postalCodeInvalidMessage: {\n type: 'string',\n title: 'Postal code format message',\n description: \"Shown under a postal code not in the selected country's format. Never contains the typed value.\",\n format: 'icu-message',\n messageSource: 'Enter a valid postal code.',\n },\n missingFieldsMessage: {\n type: 'string',\n title: 'Missing address fields message',\n description: 'Shown when the server refuses an address without a postal code or a country.',\n format: 'icu-message',\n messageSource: 'Fill in the postal code and the country of your billing address.',\n },\n invalidCountryMessage: {\n type: 'string',\n title: 'Invalid country message',\n description: 'Shown when the server refuses the selected country.',\n format: 'icu-message',\n messageSource: 'Choose a valid country for your billing address.',\n },\n genericErrorMessage: {\n type: 'string',\n title: 'Save failure message',\n description: 'Shown when the save fails: a network failure or a fault the shopper cannot fix.',\n format: 'icu-message',\n messageSource: 'Unable to save your billing address. Please try again.',\n },\n },\n },\n});\n\n/** The `PaymentBillingAddressUpdateErrorCode` values plus the client-minted transport failure. */\ntype BillingAddressErrorCode =\n | 'MISSING_CART_CONTEXT'\n | 'CART_NOT_FOUND'\n | 'MISSING_BILLING_ADDRESS_FIELDS'\n | 'INVALID_COUNTRY_CODE'\n | 'NETWORK_ERROR';\n\n/**\n * The message for each code an address write can return. A missing field and a refused country are ones\n * the shopper can fix; the rest, and a code this map does not know, share one message.\n */\nexport const BILLING_ADDRESS_ERROR_FIELDS = {\n MISSING_CART_CONTEXT: 'genericErrorMessage',\n CART_NOT_FOUND: 'genericErrorMessage',\n MISSING_BILLING_ADDRESS_FIELDS: 'missingFieldsMessage',\n INVALID_COUNTRY_CODE: 'invalidCountryMessage',\n NETWORK_ERROR: 'genericErrorMessage',\n} as const satisfies Record<BillingAddressErrorCode, MessageFieldName<typeof BillingAddressContent>>;\n\nexport const BillingAddressDefinition = defineComponent<BillingAddressProps>({\n name: 'BillingAddress',\n importPath: '@vtex/faststore/components',\n component: BillingAddress,\n requiresContext: 'payment',\n actions: { payment: ['setBillingAddress'] },\n content: BillingAddressContent,\n editor: { label: 'Billing Address', category: 'commerce' },\n schema: {\n defaultCountry: { type: 'string', default: 'BRA', label: 'Default country (ISO-3)' },\n billingMatchNoticeVisible: { type: 'boolean', default: true, label: 'Show the billing-match disclaimer' },\n disabled: { type: 'boolean', default: false, label: 'Disabled' },\n },\n guideline:\n \"Same-as-delivery toggle (default on) for the billing address; when off, mounts a country-specific address form (reusing shipping-address's country descriptors) and persists it via $actions.payment.setBillingAddress on an explicit submit control. Re-checking the toggle dispatches sameAsDelivery: true only once an address was actually persisted (otherwise it's a local no-op — there's nothing on the server to revert). Publishes only a boolean (never the address) onto the shared payment draft for usePlaceOrder to read. Every label, notice and message is a $content field; country names follow the store locale. While the form is open and nothing has been saved yet, renders an inline status notice — the required attribute on the form's own fields is not a submit gate; Place Order can still complete billed to the delivery address if the shopper never saves. Must be inside a PaymentSectionRoot or PaymentSectionController; place as a sibling of CardEntry/SavedCards/Installments/SaveCard/PlaceOrder, never nested inside any of them. See billing-address.md.\",\n});\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport function BillingAddress({\n defaultCountry = 'BRA',\n billingMatchNoticeVisible = true,\n disabled = false,\n className,\n $actions: $actionsProp,\n $content,\n $locale,\n ...props\n}: BillingAddressProps) {\n const $actions = useActionsOverride($actionsProp);\n const { format } = useMessageFormatter(BillingAddressContent, $content);\n const { displayName } = useStorefrontFormatters();\n const countryName = (iso: string) => countryNameOf(iso, displayName);\n const fieldLabel = (name: string) => fieldLabelOf(name, (labelField) => format(labelField));\n // Also enforces the PaymentSectionRoot/PaymentSectionController ancestor: usePayment() throws\n // when rendered outside one.\n const {\n draft,\n selectedMethod,\n setDraftBillingAddressDifferent,\n setDraftBillingAddressUnsaved,\n placing,\n cardPaymentCollapsed,\n registerRefusalFocus,\n } = usePayment();\n // Fix pass, findings R2/S2: a stable identity for *this* mounted instance, so its cleanup below\n // retracts only its own contribution to the shared `billingAddressUnsaved` fact — never a second\n // instance's still-open, still-unsaved one (billing-address.md documents multiple instances under one\n // root as allowed). `useId()` is stable for the component's lifetime and distinct per instance, which\n // is exactly what a per-instance set key needs; it is never rendered or sent anywhere.\n const instanceKey = useId();\n // T175 (Decision 3): the id the toggle's `aria-controls` names in every state, rendered on the\n // always-mounted panel below it. Its own `useId()`, so `instanceKey` above stays unrendered.\n const panelId = useId();\n\n const [formDraft, setFormDraft] = useState<BillingAddressFormDraft>(() => ({\n postalCode: '',\n // A `defaultCountry` with no descriptor (a typo) falls back to the first available\n // country rather than shipping a dead form with no fields and a permanently-disabled submit\n // (review F7) — `getAvailableCountries()` is never empty in practice (Decision 7), but the raw\n // prop is the last-resort fallback if it ever were, matching today's behavior for that edge case.\n // A library-backed `defaultCountry` that has not loaded yet is kept: the form shows the country and no\n // fields until its descriptor arrives, rather than switching the shopper to another country.\n country: initialCountryOf(defaultCountry),\n street: '',\n number: '',\n complement: '',\n neighborhood: '',\n city: '',\n state: '',\n }));\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<PaymentUserError | null>(null);\n // T38 (FR-6, Decision 8): per-field validity, mirroring card-entry.tsx:1115/:1150. A field joins this\n // set on blur, or when a refused submit moves focus to it (`markTouched`), where blur never comes.\n // Gated on it so a field is never marked invalid before the shopper reaches it (US-3,\n // Risk R5) — wiring `invalid` off `isBillingAddressFormValid` alone would mark every field invalid\n // the instant the form opens.\n const [touched, setTouched] = useState<ReadonlySet<string>>(() => new Set());\n // Local-only: whether the custom-address form is showing. Decoupled from `draft.billingAddressDifferent`\n // on purpose (PR #544 N1) — that published flag is what `gateway.ts` reads to decide whose address to\n // bill, so it must say \"different\" only once an address has actually been persisted differently, never\n // merely because the shopper opened this form. Initialized from the draft (via the same pure\n // `resolveSameAsDelivery` the checkbox used to read directly) so the two never start out of sync.\n const [formOpen, setFormOpen] = useState(() => !resolveSameAsDelivery(draft.billingAddressDifferent));\n\n // Re-syncs `formOpen` whenever the shared draft's `billingAddressDifferent` changes for a reason\n // outside THIS instance's own clicks (PR #544 N8) — e.g. a sibling `BillingAddress` mounted under the\n // same `PaymentSectionRoot` persists or reverts an address through its own copy of this same shared\n // context. A bare local `useState` only ever reflects the instance that called its own setter; without\n // this effect, a second mounted instance keeps showing whatever `formOpen` it computed at its own\n // mount forever, even after `draft.billingAddressDifferent` — the one fact the gateway actually reads\n // — has moved on. Deliberately keyed on the primitive `draft.billingAddressDifferent`, not the `draft`\n // object itself: `draft` is a stable `useMemo` over seven facts (`use-payment-method-selection.ts`),\n // so its identity changes whenever ANY of them does — including `draftBin`, which `CardEntry`\n // publishes on every keystroke of the card number. Keying this effect on the whole object instead\n // would refire it on each of those keystrokes while this form is open and nothing is yet persisted,\n // recomputing `formOpen` from an unchanged `billingAddressDifferent` and closing the form mid-input\n // (review #544 N15 — a dedicated regression test in `billing-address.test.tsx` pins this). This\n // differs from `shipping-address.tsx:645`'s own snapshot-keyed effect, whose reason is Apollo\n // returning a new-but-equal object reference every render (its own `biome-ignore` comment says so) —\n // `draft` here is a local `useMemo`, already stable; the hazard is refiring on an unrelated fact\n // change, not on render churn. Does NOT reintroduce N1: this component's own uncheck transition (below) sets\n // `formOpen` without ever touching the draft, so that transition alone never changes this effect's\n // dependency and never fires it. Deleting this effect is exactly the mutant PR #544's re-verification\n // named V-P2/N8 — the two-instance regression test below fails without it.\n useEffect(() => {\n setFormOpen(!resolveSameAsDelivery(draft.billingAddressDifferent));\n }, [draft.billingAddressDifferent]);\n\n const sameAsDelivery = !formOpen;\n // PR #544 N7: visible only while the shopper is on the custom-address path AND nothing has actually\n // been persisted differently yet — the exact window in which Place Order would silently bill the\n // delivery address. Mirrors the same `draft.billingAddressDifferent !== true` check\n // `handleSameAsDeliveryChange`'s local-no-op branch already uses, so the notice and the no-op branch\n // can never disagree about what \"nothing persisted\" means. T191 (FR-8): never while the card payment\n // is collapsed, when this component renders nothing — the gate below must not refuse Place order over\n // a form the shopper cannot see, and the notice and the gate keep reading one expression.\n const unsavedAddressNoticeVisible = !cardPaymentCollapsed && formOpen && draft.billingAddressDifferent !== true;\n\n // T24's submit gate (specs/cardholder-document-submit-gate.md FR-6): publishes the EXACT same fact\n // the notice above renders from, so the gate and the notice can never disagree (Risk R5). Keyed by\n // this instance's own `instanceKey` (fix pass, findings R2/S2): `billingAddressUnsaved` is one fact\n // shared by every `BillingAddress` mounted under this root, so the cleanup must retract only *this*\n // instance's own contribution on unmount — never a second, still-open, still-unsaved instance's own\n // block (measured: an unconditional publish here left the CTA proceeding while that instance's own\n // notice kept rendering, billing the declined delivery address anyway). Not cosmetics either way: an\n // unmounted `BillingAddress` must not leave the CTA blocked with no form on screen left to fix it\n // (Risk R2) when it was the *only* instance.\n useEffect(() => {\n setDraftBillingAddressUnsaved(instanceKey, unsavedAddressNoticeVisible);\n return () => setDraftBillingAddressUnsaved(instanceKey, false);\n }, [instanceKey, unsavedAddressNoticeVisible, setDraftBillingAddressUnsaved]);\n\n // T212: while the form is open and on screen, a refusal naming the billing form may move focus into it.\n // Registered by the instance that renders the form, so a closed or collapsed instance never takes it.\n const canTakeFocus = !cardPaymentCollapsed && formOpen;\n // Starts the load of the library-backed countries and re-renders this form as each one arrives. The\n // descriptor below is a cache read, stable once it exists, so it needs no dependency on the hook's value.\n useLibraryCountryDescriptors();\n const draftDescriptor = getCountryDescriptor(formDraft.country);\n const renderableFields = useMemo(\n () => (draftDescriptor ? getRenderableFields(draftDescriptor) : []),\n [draftDescriptor],\n );\n useEffect(() => {\n if (!canTakeFocus) return;\n const markTouched = (name: string) => setTouched((prev) => withTouched(prev, name));\n return registerRefusalFocus('billing-form', () =>\n focusNextBillingControl(document.getElementById(panelId), renderableFields, markTouched),\n );\n }, [canTakeFocus, renderableFields, panelId, registerRefusalFocus]);\n\n const billingAddressRequired = selectedMethod?.validator.billingAddressRequired ?? false;\n const fieldsDisabled = disabled || pending;\n\n async function handleSameAsDeliveryChange(nextSameAsDelivery: boolean) {\n setError(null);\n\n if (!nextSameAsDelivery) {\n // Opens the custom-address form locally. Nothing is dispatched and the shared draft is left\n // untouched here — the shopper has typed nothing yet, and publishing `billingAddressDifferent:\n // true` before an address is actually persisted is exactly the false claim review #544's N1\n // measured reaching the gateway in two clicks. Only a successful submit (handleSubmit below) may\n // ever publish `true` — see this file's own top-level doc comment.\n setFormOpen(true);\n return;\n }\n\n if (draft.billingAddressDifferent !== true) {\n // Nothing was ever persisted differently — either the shopper never submitted, or an earlier\n // submit failed — so there is nothing on the server to revert. Closing the form is a local-only\n // action: dispatching `setBillingAddress({ sameAsDelivery: true })` here would be a wasted round\n // trip reverting an address the server was never asked to set in the first place.\n setFormOpen(false);\n return;\n }\n\n const setBillingAddress = $actions?.payment?.setBillingAddress;\n // Silent no-op when the action namespace is not injected, mirroring `SaveCard`'s / `selectMethod`'s\n // \"no-op (no error, no throw)\" convention.\n if (!setBillingAddress) return;\n\n setPending(true);\n try {\n const userErrors = await setBillingAddress({ sameAsDelivery: true });\n const [firstError] = userErrors;\n if (firstError) {\n setError(firstError);\n // A refused revert (review F3): the persisted address is still the custom one the shopper had\n // before, so the form stays open (`formOpen` untouched) and the draft keeps claiming\n // \"different\" — neither claims \"same as delivery\" the server never accepted. There is nothing\n // to roll back here, unlike before this pass: the draft is only ever written `false` a few\n // lines below, after `setBillingAddress` has already resolved with no error.\n } else {\n setDraftBillingAddressDifferent(false);\n // Redundant with the resync effect above once this draft write lands (the effect closes the\n // form on its own whenever `draft.billingAddressDifferent` moves to `false` — review #544 N18)\n // — kept anyway so this instance closes synchronously with its own click rather than waiting on\n // a re-render, which costs nothing since both agree on the outcome.\n setFormOpen(false);\n }\n } finally {\n setPending(false);\n }\n }\n\n function handleFieldChange(name: string, value: string) {\n setFormDraft((prev) => ({ ...prev, [name]: value }));\n }\n\n /** Marks a field touched on its first blur (T38, FR-6). Idempotent on every later blur — a `Set`\n * already containing `name` is returned unchanged, so this never triggers a redundant re-render. */\n function handleFieldBlur(name: string) {\n setTouched((prev) => withTouched(prev, name));\n }\n\n function handleCountryChange(value: string | null) {\n // Base UI's `SelectRoot` (`select/root/SelectRoot.js`) only ever calls `onValueChange` with one of\n // `Select.Item`'s own `value`s — all descriptor-backed, non-null ISO codes here (verified by\n // reading its source, not assumed) — so `value` is `null` on no path this component's own `Select`\n // below can reach. The `?? prev.country` fallback exists only to satisfy\n // `BillingAddressFormDraft.country: string`'s type, not as a runtime branch pretending to be tested\n // (review F10's residual, R5′): dropping the fallback (`country: value`) is a `pnpm typecheck`\n // failure, not merely an unproven `if` — a stronger, mechanical guarantee than a guard clause no\n // real interaction could ever exercise.\n //\n // Preserves every other field's value across the switch (Decision 7) — matches\n // `shipping-address`'s own documented behavior: a value typed under one country resurfaces if the\n // shopper switches away and back, even while the field is hidden for the currently-selected one.\n setFormDraft((prev) => ({ ...prev, country: value ?? prev.country }));\n }\n\n async function handleSubmit() {\n const setBillingAddress = $actions?.payment?.setBillingAddress;\n setError(null);\n if (!setBillingAddress) return;\n\n setPending(true);\n try {\n const userErrors = await setBillingAddress({\n sameAsDelivery: false,\n postalCode: normalizeAddressField(renderableFields, 'postalCode', formDraft.postalCode),\n country: formDraft.country,\n // Only a text field the active country's descriptor renders is sent: a value typed under a\n // previously-selected country (e.g. `neighborhood` kept in the draft after BR -> USA) never\n // leaks into the payload. `postalCode` and `country` are unconditional: every descriptor has both.\n ...pickActiveTextFields(renderableFields, formDraft),\n });\n const [firstError] = userErrors;\n if (firstError) {\n setError(firstError);\n } else {\n // The one place `billingAddressDifferent` may ever become `true` (PR #544 N1) — only once the\n // server actually holds a different address, so the flag means exactly what `gateway.ts`'s own\n // doc comment already claims: \"`setBillingAddress` already persisted it … so the gateway reads\n // it from there.\"\n setDraftBillingAddressDifferent(true);\n }\n } finally {\n setPending(false);\n }\n }\n\n // T191 (FR-7, Decision 3): nothing renders while the card payment is collapsed. Returned after the\n // last hook, so this instance stays mounted and keeps what the shopper typed for when the card is back.\n if (cardPaymentCollapsed) return null;\n\n const billingMatchNotice = billingMatchNoticeVisible ? format('billingMatchNoticeLabel') : '';\n\n return (\n <div\n {...props}\n className={className}\n inert={placing}\n aria-disabled={placing || undefined}\n data-fs-billing-address=\"\"\n {...(placing ? { 'data-fs-billing-address-placing': '' } : {})}\n {...(pending ? { 'data-fs-billing-address-pending': '' } : {})}\n {...(sameAsDelivery ? { 'data-fs-billing-address-same-as-delivery': '' } : {})}\n >\n {billingMatchNotice.trim() ? <p data-fs-billing-address-match-notice=\"\">{billingMatchNotice}</p> : null}\n <div data-fs-billing-address-toggle-field=\"\">\n <Field.Root disabled={fieldsDisabled} data-fs-field-root=\"\">\n <Field.Label>\n <Checkbox.Root\n checked={sameAsDelivery}\n onCheckedChange={handleSameAsDeliveryChange}\n disabled={fieldsDisabled}\n aria-controls={panelId}\n >\n <Checkbox.Indicator>\n <Icon name=\"checked\" />\n </Checkbox.Indicator>\n </Checkbox.Root>\n {format('sameAsDeliveryLabel')}\n </Field.Label>\n </Field.Root>\n {billingAddressRequired ? (\n <p data-fs-billing-address-required-note=\"\">{format('requiredNoticeLabel')}</p>\n ) : null}\n </div>\n\n {/* T175 (Decision 3): always mounted, so the toggle's `aria-controls` resolves in every state. It\n takes no className or props, and it is `hidden` only while empty (INV-2): a store's display\n utility can beat `[hidden]`, but then it shows an empty box, never content. */}\n <div id={panelId} data-fs-billing-address-panel=\"\" hidden={sameAsDelivery}>\n {sameAsDelivery ? null : (\n <div data-fs-billing-address-form=\"\">\n {unsavedAddressNoticeVisible ? (\n <p role=\"status\" data-fs-billing-address-unsaved-notice=\"\">\n {format('unsavedAddressNoticeLabel')}\n </p>\n ) : null}\n <Field.Root disabled={fieldsDisabled} data-fs-field-root=\"\" data-fs-billing-address-field=\"country\">\n <Field.Label>{format('countryLabel')}</Field.Label>\n <Select.Root\n value={formDraft.country}\n onValueChange={handleCountryChange}\n disabled={fieldsDisabled}\n required\n >\n <Select.Trigger>\n {/* Resolves the label directly from the country descriptor rather than relying on\n Base UI's automatic item-label lookup, which only reflects a mounted Select.Item —\n mirrors `ShippingAddressCountryControl`'s identical reasoning. The name is in the\n store locale (`countryDisplayName`). */}\n <Select.Value>{(value) => (value ? countryName(value) : value)}</Select.Value>\n <Select.Icon>▾</Select.Icon>\n </Select.Trigger>\n <Select.Portal>\n <Select.Positioner>\n <Select.Popup>\n <Select.List>\n {getAvailableCountries().map((iso) => {\n const label = countryName(iso);\n return (\n <Select.Item key={iso} value={iso} label={label}>\n {label}\n </Select.Item>\n );\n })}\n </Select.List>\n </Select.Popup>\n </Select.Positioner>\n </Select.Portal>\n </Select.Root>\n </Field.Root>\n\n {renderableFields.map((field) => {\n // T38 (FR-6): gated on `touched` (blur, or a refused submit focusing the field) so nothing is\n // marked invalid before the shopper reaches it (US-3, Risk R5). A touched field is invalid once\n // `getFieldIssue` finds an issue with its value: blank (except `complement`) or not in the\n // selected country's format (INV-3/INV-4). `areRenderableFieldsValid` reads the same\n // `getFieldIssue`, so the two are never allowed to disagree about a field.\n const issue = touched.has(field.name) ? getFieldIssue(field, formDraft[field.name] ?? '') : null;\n const invalid = issue !== null;\n return (\n <Field.Root\n key={field.name}\n disabled={fieldsDisabled}\n invalid={invalid}\n data-fs-field-root=\"\"\n data-fs-billing-address-field={field.name}\n >\n <Field.Label>{fieldLabel(field.name)}</Field.Label>\n <Field.Control\n value={formDraft[field.name] ?? ''}\n onChange={(event) => handleFieldChange(field.name, event.currentTarget.value)}\n onBlur={() => handleFieldBlur(field.name)}\n disabled={fieldsDisabled}\n // Every rendered field is required once \"different\" is selected, except `complement`\n // (`OPTIONAL_FORM_FIELD_NAMES`) — PR #544 N1 item 1. Not a like-for-like precedent with\n // `CardholderDocument`'s own `required={documentRequired}`: that one forwards to a\n // particle with its own declared prop, a root state hook, and a paired `Field.Error`\n // driven by a server-sourced validator flag, where this sets `required` directly off a\n // locally hardcoded set instead (full comparison: specs/billing-address.md Accessibility\n // Contract).\n required={!OPTIONAL_FORM_FIELD_NAMES.has(field.name)}\n />\n {invalid ? (\n <Field.Error match>\n {issue === 'format'\n ? getFieldFormatMessage(field, format('postalCodeInvalidMessage'))\n : format('fieldRequiredMessage')}\n </Field.Error>\n ) : null}\n </Field.Root>\n );\n })}\n\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={fieldsDisabled || !canSaveBillingDraft(draftDescriptor, renderableFields, formDraft)}\n data-fs-billing-address-submit=\"\"\n >\n {format(pending ? 'submitPendingLabel' : 'submitLabel')}\n </button>\n </div>\n )}\n </div>\n\n {error ? (\n <div role=\"alert\" data-fs-billing-address-error=\"\">\n {formatErrorCode(BillingAddressContent, BILLING_ADDRESS_ERROR_FIELDS, error, format, 'genericErrorMessage')}\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { ContentValue, MessageLocaleProp } from "../../core/dist/define-content-type.js";
1
2
  import { ComponentDefinition } from "../../core/dist/define-component.js";
2
3
  import "../../core/dist/index.js";
3
4
  import { ComponentPropsWithRef } from "react";
@@ -9,15 +10,32 @@ type RawBreadcrumbData = {
9
10
  position: number;
10
11
  }>;
11
12
  };
13
+ export declare const BreadcrumbRootContent: {
14
+ readonly id: "breadcrumb-root";
15
+ readonly title: "Breadcrumb Root";
16
+ readonly description: "Accessible name of the breadcrumb navigation.";
17
+ readonly schema: {
18
+ readonly type: "object";
19
+ readonly additionalProperties: false;
20
+ readonly properties: {
21
+ readonly label: {
22
+ readonly type: "string";
23
+ readonly title: "Accessible name";
24
+ readonly format: "icu-message";
25
+ readonly messageSource: "Breadcrumb";
26
+ };
27
+ };
28
+ };
29
+ };
12
30
  type BreadcrumbItemData = {
13
31
  name: string;
14
32
  url: string;
15
33
  position: number;
16
34
  };
17
- export type BreadcrumbRootProps = ComponentPropsWithRef<'nav'> & {
35
+ export type BreadcrumbRootProps = ComponentPropsWithRef<'nav'> & MessageLocaleProp & {
18
36
  items?: BreadcrumbItemData[];
19
37
  $data?: RawBreadcrumbData | null;
20
- label?: string;
38
+ $content?: ContentValue<typeof BreadcrumbRootContent>;
21
39
  };
22
40
  export type BreadcrumbListProps = ComponentPropsWithRef<'ol'>;
23
41
  export type BreadcrumbLinkProps = ComponentPropsWithRef<'a'>;
@@ -26,7 +44,7 @@ export declare const BreadcrumbRootDefinition: ComponentDefinition<BreadcrumbRoo
26
44
  export declare const BreadcrumbListDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").OlHTMLAttributes<HTMLOListElement>, HTMLOListElement>>;
27
45
  export declare const BreadcrumbLinkDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>>;
28
46
  export declare const BreadcrumbSeparatorDefinition: ComponentDefinition<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>>;
29
- export declare function BreadcrumbRoot({ items, $data, label, className, children, ...props }: BreadcrumbRootProps): import("react/jsx-runtime").JSX.Element;
47
+ export declare function BreadcrumbRoot({ items, $data, $content, $locale, className, children, ...props }: BreadcrumbRootProps): import("react/jsx-runtime").JSX.Element;
30
48
  export declare function BreadcrumbList({ className, children, style, ...props }: BreadcrumbListProps): import("react/jsx-runtime").JSX.Element;
31
49
  export declare function BreadcrumbLink({ className, children, ...props }: BreadcrumbLinkProps): import("react/jsx-runtime").JSX.Element;
32
50
  export declare function BreadcrumbSeparator({ className, children, ...props }: BreadcrumbSeparatorProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1,6 +1,8 @@
1
1
  "use client";
2
2
  import { defineComponent } from "../../core/dist/define-component.js";
3
+ import { defineContentType } from "../../core/dist/define-content-type.js";
3
4
  import "../../core/dist/index.js";
5
+ import { useMessageFormatter } from "../../sdk/i18n/use-message-formatter.js";
4
6
  import { NavigationLink } from "../../particles/navigation-link/navigation-link.js";
5
7
  import { createContext, useContext } from "react";
6
8
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -16,6 +18,21 @@ const fragment = `
16
18
  }
17
19
  }
18
20
  `;
21
+ const BreadcrumbRootContent = defineContentType({
22
+ id: "breadcrumb-root",
23
+ title: "Breadcrumb Root",
24
+ description: "Accessible name of the breadcrumb navigation.",
25
+ schema: {
26
+ type: "object",
27
+ additionalProperties: false,
28
+ properties: { label: {
29
+ type: "string",
30
+ title: "Accessible name",
31
+ format: "icu-message",
32
+ messageSource: "Breadcrumb"
33
+ } }
34
+ }
35
+ });
19
36
  const BreadcrumbContext = createContext({ items: [] });
20
37
  const BreadcrumbItemContext = createContext(null);
21
38
  function useBreadcrumbItem() {
@@ -31,16 +48,13 @@ const BreadcrumbRootDefinition = defineComponent({
31
48
  importPath: "@vtex/faststore/components",
32
49
  component: BreadcrumbRoot,
33
50
  fragment,
51
+ content: BreadcrumbRootContent,
34
52
  editor: {
35
53
  label: "Breadcrumb Root",
36
54
  category: "navigation"
37
55
  },
38
- schema: { label: {
39
- type: "string",
40
- default: "Breadcrumb",
41
- label: "Aria Label"
42
- } },
43
- guideline: "Root container for breadcrumb navigation; compose as BreadcrumbRoot > BreadcrumbList > [BreadcrumbLink + BreadcrumbSeparator]."
56
+ schema: {},
57
+ guideline: "Root container for breadcrumb navigation; compose as BreadcrumbRoot > BreadcrumbList > [BreadcrumbLink + BreadcrumbSeparator]. Its accessible name is the $content label."
44
58
  });
45
59
  const BreadcrumbListDefinition = defineComponent({
46
60
  name: "BreadcrumbList",
@@ -76,13 +90,14 @@ const BreadcrumbSeparatorDefinition = defineComponent({
76
90
  guideline: "Decorative separator rendered between breadcrumb items; must be inside BreadcrumbList."
77
91
  });
78
92
  const scrollbarHideStyles = "[data-fs-breadcrumb-list]::-webkit-scrollbar{display:none}";
79
- function BreadcrumbRoot({ items, $data, label = "Breadcrumb", className, children, ...props }) {
93
+ function BreadcrumbRoot({ items, $data, $content, $locale, className, children, ...props }) {
94
+ const { format } = useMessageFormatter(BreadcrumbRootContent, $content);
80
95
  const resolvedItems = items ?? ($data ? transformBreadcrumbData($data) : []);
81
96
  return /* @__PURE__ */ jsx(BreadcrumbContext, {
82
97
  value: { items: resolvedItems },
83
98
  children: /* @__PURE__ */ jsx("nav", {
84
99
  ...props,
85
- "aria-label": label,
100
+ "aria-label": format("label"),
86
101
  className,
87
102
  "data-fs-breadcrumb": "",
88
103
  children
@@ -150,6 +165,6 @@ const Breadcrumb = {
150
165
  Separator: BreadcrumbSeparator
151
166
  };
152
167
  //#endregion
153
- export { Breadcrumb, BreadcrumbLink, BreadcrumbLinkDefinition, BreadcrumbList, BreadcrumbListDefinition, BreadcrumbRoot, BreadcrumbRootDefinition, BreadcrumbSeparator, BreadcrumbSeparatorDefinition };
168
+ export { Breadcrumb, BreadcrumbLink, BreadcrumbLinkDefinition, BreadcrumbList, BreadcrumbListDefinition, BreadcrumbRoot, BreadcrumbRootContent, BreadcrumbRootDefinition, BreadcrumbSeparator, BreadcrumbSeparatorDefinition };
154
169
 
155
170
  //# sourceMappingURL=breadcrumb.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"breadcrumb.js","names":[],"sources":["../../../src/matter/breadcrumb/breadcrumb.tsx"],"sourcesContent":["'use client';\n\nimport { defineComponent } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { createContext, useContext } from 'react';\nimport { NavigationLink } from '../../particles/navigation-link/navigation-link.js';\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment BreadcrumbData on Query {\n productBySlug(slug: $slug) {\n breadcrumbs {\n name\n url\n position\n }\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Raw data types (shape of the fragment result)\n// ---------------------------------------------------------------------------\n\ntype RawBreadcrumbData = {\n breadcrumbs: Array<{\n name: string;\n url: string;\n position: number;\n }>;\n};\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype BreadcrumbItemData = {\n name: string;\n url: string;\n position: number;\n};\n\ntype BreadcrumbContextValue = {\n items: BreadcrumbItemData[];\n};\n\ntype BreadcrumbItemContextValue = BreadcrumbItemData & {\n isLast: boolean;\n};\n\nexport type BreadcrumbRootProps = ComponentPropsWithRef<'nav'> & {\n items?: BreadcrumbItemData[];\n $data?: RawBreadcrumbData | null;\n label?: string;\n};\n\nexport type BreadcrumbListProps = ComponentPropsWithRef<'ol'>;\n\nexport type BreadcrumbLinkProps = ComponentPropsWithRef<'a'>;\n\nexport type BreadcrumbSeparatorProps = ComponentPropsWithRef<'span'>;\n\n// ---------------------------------------------------------------------------\n// Contexts\n// ---------------------------------------------------------------------------\n\nconst BreadcrumbContext = createContext<BreadcrumbContextValue>({ items: [] });\n\nconst BreadcrumbItemContext = createContext<BreadcrumbItemContextValue | null>(null);\n\nfunction useBreadcrumbItem(): BreadcrumbItemContextValue {\n const item = useContext(BreadcrumbItemContext);\n if (!item) {\n throw new Error('Breadcrumb item components must be used inside BreadcrumbList.');\n }\n return item;\n}\n\n// ---------------------------------------------------------------------------\n// Transform\n// ---------------------------------------------------------------------------\n\nfunction transformBreadcrumbData(data: RawBreadcrumbData): BreadcrumbItemData[] {\n return data.breadcrumbs;\n}\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const BreadcrumbRootDefinition = defineComponent<BreadcrumbRootProps>({\n name: 'BreadcrumbRoot',\n importPath: '@vtex/faststore/components',\n component: BreadcrumbRoot,\n fragment,\n editor: { label: 'Breadcrumb Root', category: 'navigation' },\n schema: {\n label: { type: 'string', default: 'Breadcrumb', label: 'Aria Label' },\n },\n guideline:\n 'Root container for breadcrumb navigation; compose as BreadcrumbRoot > BreadcrumbList > [BreadcrumbLink + BreadcrumbSeparator].',\n});\n\nexport const BreadcrumbListDefinition = defineComponent<BreadcrumbListProps>({\n name: 'BreadcrumbList',\n importPath: '@vtex/faststore/components',\n component: BreadcrumbList,\n editor: { label: 'Breadcrumb List', category: 'navigation' },\n schema: {},\n guideline: 'Ordered list of breadcrumb items; must be a direct child of BreadcrumbRoot.',\n});\n\nexport const BreadcrumbLinkDefinition = defineComponent<BreadcrumbLinkProps>({\n name: 'BreadcrumbLink',\n importPath: '@vtex/faststore/components',\n component: BreadcrumbLink,\n editor: { label: 'Breadcrumb Link', category: 'navigation' },\n schema: {},\n guideline: 'Renders the label and link for a breadcrumb item; must be inside BreadcrumbList.',\n});\n\nexport const BreadcrumbSeparatorDefinition = defineComponent<BreadcrumbSeparatorProps>({\n name: 'BreadcrumbSeparator',\n importPath: '@vtex/faststore/components',\n component: BreadcrumbSeparator,\n editor: { label: 'Breadcrumb Separator', category: 'navigation' },\n schema: {},\n guideline: 'Decorative separator rendered between breadcrumb items; must be inside BreadcrumbList.',\n});\n\n// ---------------------------------------------------------------------------\n// Scrollbar hiding styles\n// ---------------------------------------------------------------------------\n\nconst scrollbarHideStyles = '[data-fs-breadcrumb-list]::-webkit-scrollbar{display:none}';\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\nexport function BreadcrumbRoot({\n items,\n $data,\n label = 'Breadcrumb',\n className,\n children,\n ...props\n}: BreadcrumbRootProps) {\n const resolvedItems = items ?? ($data ? transformBreadcrumbData($data) : []);\n\n return (\n <BreadcrumbContext value={{ items: resolvedItems }}>\n <nav {...props} aria-label={label} className={className} data-fs-breadcrumb=\"\">\n {children}\n </nav>\n </BreadcrumbContext>\n );\n}\n\nexport function BreadcrumbList({ className, children, style, ...props }: BreadcrumbListProps) {\n const { items } = useContext(BreadcrumbContext);\n\n return (\n <>\n <style>{scrollbarHideStyles}</style>\n <ol\n {...props}\n className={className}\n data-fs-breadcrumb-list=\"\"\n style={{\n display: 'flex',\n flexWrap: 'nowrap',\n overflowX: 'auto',\n scrollbarWidth: 'none',\n msOverflowStyle: 'none',\n ...style,\n }}\n >\n {items.map((item, index) => (\n <BreadcrumbItemContext key={item.position} value={{ ...item, isLast: index === items.length - 1 }}>\n <li data-fs-breadcrumb-item=\"\">{children}</li>\n </BreadcrumbItemContext>\n ))}\n </ol>\n </>\n );\n}\n\nexport function BreadcrumbLink({ className, children, ...props }: BreadcrumbLinkProps) {\n const { name, url, isLast } = useBreadcrumbItem();\n\n if (isLast) {\n return (\n <span\n {...props}\n className={className}\n aria-current=\"page\"\n data-fs-breadcrumb-link=\"\"\n data-fs-breadcrumb-current=\"\"\n >\n {children ?? name}\n </span>\n );\n }\n\n return (\n <NavigationLink href={url} className={className} data-fs-breadcrumb-link=\"\">\n {children ?? name}\n </NavigationLink>\n );\n}\n\nexport function BreadcrumbSeparator({ className, children, ...props }: BreadcrumbSeparatorProps) {\n const { isLast } = useBreadcrumbItem();\n\n if (isLast) return null;\n\n return (\n <span {...props} aria-hidden=\"true\" className={className} data-fs-breadcrumb-separator=\"\">\n {children ?? '/'}\n </span>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const Breadcrumb = {\n Root: BreadcrumbRoot,\n List: BreadcrumbList,\n Link: BreadcrumbLink,\n Separator: BreadcrumbSeparator,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"breadcrumb.js","names":[],"sources":["../../../src/matter/breadcrumb/breadcrumb.tsx"],"sourcesContent":["'use client';\n\nimport { type ContentValue, defineComponent, defineContentType, type MessageLocaleProp } from '@vtex/fs-core';\nimport type { ComponentPropsWithRef } from 'react';\nimport { createContext, useContext } from 'react';\nimport { NavigationLink } from '../../particles/navigation-link/navigation-link.js';\nimport { useMessageFormatter } from '../../sdk/i18n/use-message-formatter.js';\n\n// ---------------------------------------------------------------------------\n// GraphQL Fragment\n// ---------------------------------------------------------------------------\n\nconst fragment = /* GraphQL */ `\n fragment BreadcrumbData on Query {\n productBySlug(slug: $slug) {\n breadcrumbs {\n name\n url\n position\n }\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Raw data types (shape of the fragment result)\n// ---------------------------------------------------------------------------\n\ntype RawBreadcrumbData = {\n breadcrumbs: Array<{\n name: string;\n url: string;\n position: number;\n }>;\n};\n\n// ---------------------------------------------------------------------------\n// Content model\n// ---------------------------------------------------------------------------\n\nexport const BreadcrumbRootContent = defineContentType({\n id: 'breadcrumb-root',\n title: 'Breadcrumb Root',\n description: 'Accessible name of the breadcrumb navigation.',\n schema: {\n type: 'object',\n additionalProperties: false,\n properties: {\n label: { type: 'string', title: 'Accessible name', format: 'icu-message', messageSource: 'Breadcrumb' },\n },\n },\n});\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype BreadcrumbItemData = {\n name: string;\n url: string;\n position: number;\n};\n\ntype BreadcrumbContextValue = {\n items: BreadcrumbItemData[];\n};\n\ntype BreadcrumbItemContextValue = BreadcrumbItemData & {\n isLast: boolean;\n};\n\nexport type BreadcrumbRootProps = ComponentPropsWithRef<'nav'> &\n MessageLocaleProp & {\n items?: BreadcrumbItemData[];\n $data?: RawBreadcrumbData | null;\n $content?: ContentValue<typeof BreadcrumbRootContent>;\n };\n\nexport type BreadcrumbListProps = ComponentPropsWithRef<'ol'>;\n\nexport type BreadcrumbLinkProps = ComponentPropsWithRef<'a'>;\n\nexport type BreadcrumbSeparatorProps = ComponentPropsWithRef<'span'>;\n\n// ---------------------------------------------------------------------------\n// Contexts\n// ---------------------------------------------------------------------------\n\nconst BreadcrumbContext = createContext<BreadcrumbContextValue>({ items: [] });\n\nconst BreadcrumbItemContext = createContext<BreadcrumbItemContextValue | null>(null);\n\nfunction useBreadcrumbItem(): BreadcrumbItemContextValue {\n const item = useContext(BreadcrumbItemContext);\n if (!item) {\n throw new Error('Breadcrumb item components must be used inside BreadcrumbList.');\n }\n return item;\n}\n\n// ---------------------------------------------------------------------------\n// Transform\n// ---------------------------------------------------------------------------\n\nfunction transformBreadcrumbData(data: RawBreadcrumbData): BreadcrumbItemData[] {\n return data.breadcrumbs;\n}\n\n// ---------------------------------------------------------------------------\n// Definitions\n// ---------------------------------------------------------------------------\n\nexport const BreadcrumbRootDefinition = defineComponent<BreadcrumbRootProps>({\n name: 'BreadcrumbRoot',\n importPath: '@vtex/faststore/components',\n component: BreadcrumbRoot,\n fragment,\n content: BreadcrumbRootContent,\n editor: { label: 'Breadcrumb Root', category: 'navigation' },\n schema: {},\n guideline:\n 'Root container for breadcrumb navigation; compose as BreadcrumbRoot > BreadcrumbList > [BreadcrumbLink + BreadcrumbSeparator]. Its accessible name is the $content label.',\n});\n\nexport const BreadcrumbListDefinition = defineComponent<BreadcrumbListProps>({\n name: 'BreadcrumbList',\n importPath: '@vtex/faststore/components',\n component: BreadcrumbList,\n editor: { label: 'Breadcrumb List', category: 'navigation' },\n schema: {},\n guideline: 'Ordered list of breadcrumb items; must be a direct child of BreadcrumbRoot.',\n});\n\nexport const BreadcrumbLinkDefinition = defineComponent<BreadcrumbLinkProps>({\n name: 'BreadcrumbLink',\n importPath: '@vtex/faststore/components',\n component: BreadcrumbLink,\n editor: { label: 'Breadcrumb Link', category: 'navigation' },\n schema: {},\n guideline: 'Renders the label and link for a breadcrumb item; must be inside BreadcrumbList.',\n});\n\nexport const BreadcrumbSeparatorDefinition = defineComponent<BreadcrumbSeparatorProps>({\n name: 'BreadcrumbSeparator',\n importPath: '@vtex/faststore/components',\n component: BreadcrumbSeparator,\n editor: { label: 'Breadcrumb Separator', category: 'navigation' },\n schema: {},\n guideline: 'Decorative separator rendered between breadcrumb items; must be inside BreadcrumbList.',\n});\n\n// ---------------------------------------------------------------------------\n// Scrollbar hiding styles\n// ---------------------------------------------------------------------------\n\nconst scrollbarHideStyles = '[data-fs-breadcrumb-list]::-webkit-scrollbar{display:none}';\n\n// ---------------------------------------------------------------------------\n// Implementations\n// ---------------------------------------------------------------------------\n\nexport function BreadcrumbRoot({\n items,\n $data,\n $content,\n $locale,\n className,\n children,\n ...props\n}: BreadcrumbRootProps) {\n const { format } = useMessageFormatter(BreadcrumbRootContent, $content);\n const resolvedItems = items ?? ($data ? transformBreadcrumbData($data) : []);\n\n return (\n <BreadcrumbContext value={{ items: resolvedItems }}>\n <nav {...props} aria-label={format('label')} className={className} data-fs-breadcrumb=\"\">\n {children}\n </nav>\n </BreadcrumbContext>\n );\n}\n\nexport function BreadcrumbList({ className, children, style, ...props }: BreadcrumbListProps) {\n const { items } = useContext(BreadcrumbContext);\n\n return (\n <>\n <style>{scrollbarHideStyles}</style>\n <ol\n {...props}\n className={className}\n data-fs-breadcrumb-list=\"\"\n style={{\n display: 'flex',\n flexWrap: 'nowrap',\n overflowX: 'auto',\n scrollbarWidth: 'none',\n msOverflowStyle: 'none',\n ...style,\n }}\n >\n {items.map((item, index) => (\n <BreadcrumbItemContext key={item.position} value={{ ...item, isLast: index === items.length - 1 }}>\n <li data-fs-breadcrumb-item=\"\">{children}</li>\n </BreadcrumbItemContext>\n ))}\n </ol>\n </>\n );\n}\n\nexport function BreadcrumbLink({ className, children, ...props }: BreadcrumbLinkProps) {\n const { name, url, isLast } = useBreadcrumbItem();\n\n if (isLast) {\n return (\n <span\n {...props}\n className={className}\n aria-current=\"page\"\n data-fs-breadcrumb-link=\"\"\n data-fs-breadcrumb-current=\"\"\n >\n {children ?? name}\n </span>\n );\n }\n\n return (\n <NavigationLink href={url} className={className} data-fs-breadcrumb-link=\"\">\n {children ?? name}\n </NavigationLink>\n );\n}\n\nexport function BreadcrumbSeparator({ className, children, ...props }: BreadcrumbSeparatorProps) {\n const { isLast } = useBreadcrumbItem();\n\n if (isLast) return null;\n\n return (\n <span {...props} aria-hidden=\"true\" className={className} data-fs-breadcrumb-separator=\"\">\n {children ?? '/'}\n </span>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Namespace export\n// ---------------------------------------------------------------------------\n\nexport const Breadcrumb = {\n Root: BreadcrumbRoot,\n List: BreadcrumbList,\n Link: BreadcrumbLink,\n Separator: BreadcrumbSeparator,\n};\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { ContentValue } from "../../core/dist/define-content-type.js";
1
+ import { ContentValue, MessageLocaleProp } from "../../core/dist/define-content-type.js";
2
2
  import { ComponentDefinition } from "../../core/dist/define-component.js";
3
3
  import "../../core/dist/index.js";
4
4
  import "../../sdk/generated/graphql.js";
@@ -42,23 +42,24 @@ type RawBuyButtonData = {
42
42
  export declare const BuyButtonLabels: {
43
43
  readonly id: "buy-button-labels";
44
44
  readonly title: "Buy Button Labels";
45
- readonly description: "Editorial labels for the add-to-cart button.";
45
+ readonly description: "Labels of the add-to-cart button.";
46
46
  readonly schema: {
47
47
  readonly type: "object";
48
48
  readonly additionalProperties: false;
49
- readonly required: readonly ["availableLabel", "unavailableLabel"];
50
49
  readonly properties: {
51
50
  readonly availableLabel: {
52
51
  readonly type: "string";
53
52
  readonly minLength: 1;
54
53
  readonly title: "Available label";
55
- readonly default: "Comprar";
54
+ readonly format: "icu-message";
55
+ readonly messageSource: "Buy";
56
56
  };
57
57
  readonly unavailableLabel: {
58
58
  readonly type: "string";
59
59
  readonly minLength: 1;
60
60
  readonly title: "Unavailable label";
61
- readonly default: "Indisponivel";
61
+ readonly format: "icu-message";
62
+ readonly messageSource: "Unavailable";
62
63
  };
63
64
  };
64
65
  };
@@ -69,11 +70,7 @@ export type BuyButtonRootProps = ComponentPropsWithRef<'div'> & {
69
70
  * selection wins over it once set. */
70
71
  selectedVariantId?: string;
71
72
  };
72
- export type BuyButtonButtonProps = ComponentPropsWithRef<'button'> & {
73
- availableLabel?: string;
74
- unavailableLabel?: string;
75
- /** Merchant-authored labels, sliced from the page/layout content entry; win over the label props
76
- * when present. Absent in a non-CMS store — the label props (and their defaults) are the fallback. */
73
+ export type BuyButtonButtonProps = ComponentPropsWithRef<'button'> & MessageLocaleProp & {
77
74
  $content?: ContentValue<typeof BuyButtonLabels>;
78
75
  /** Injected by the compiler from `actions: { cart: ['addItem'] }`; the cart write surface. */
79
76
  $actions?: ComponentActions;
@@ -84,7 +81,7 @@ export declare function BuyButtonRoot({ $data, selectedVariantId, children, ...p
84
81
  export declare namespace BuyButtonRoot {
85
82
  var Skeleton: typeof BuyButtonSkeleton;
86
83
  }
87
- export declare function BuyButtonButton({ availableLabel, unavailableLabel, className, children, onClick, $content, $actions: $actionsProp, ...props }: BuyButtonButtonProps): import("react/jsx-runtime").JSX.Element | null;
84
+ export declare function BuyButtonButton({ className, children, onClick, $content, $locale, $actions: $actionsProp, ...props }: BuyButtonButtonProps): import("react/jsx-runtime").JSX.Element | null;
88
85
  export declare function BuyButtonSkeleton({ className, ...props }: ComponentPropsWithRef<'div'>): import("react/jsx-runtime").JSX.Element;
89
86
  export declare const BuyButton: {
90
87
  Root: typeof BuyButtonRoot;
@@ -2,6 +2,7 @@
2
2
  import { defineComponent } from "../../core/dist/define-component.js";
3
3
  import { defineContentType } from "../../core/dist/define-content-type.js";
4
4
  import "../../core/dist/index.js";
5
+ import { useMessageFormatter } from "../../sdk/i18n/use-message-formatter.js";
5
6
  import { graphql } from "../../sdk/generated/gql.js";
6
7
  import { useActionsOverride } from "../../sdk/actions/actions-override-context.js";
7
8
  import { useDataOverride } from "../../sdk/data/data-override-context.js";
@@ -75,23 +76,24 @@ const variantQuery = graphql(`
75
76
  const BuyButtonLabels = defineContentType({
76
77
  id: "buy-button-labels",
77
78
  title: "Buy Button Labels",
78
- description: "Editorial labels for the add-to-cart button.",
79
+ description: "Labels of the add-to-cart button.",
79
80
  schema: {
80
81
  type: "object",
81
82
  additionalProperties: false,
82
- required: ["availableLabel", "unavailableLabel"],
83
83
  properties: {
84
84
  availableLabel: {
85
85
  type: "string",
86
86
  minLength: 1,
87
87
  title: "Available label",
88
- default: "Comprar"
88
+ format: "icu-message",
89
+ messageSource: "Buy"
89
90
  },
90
91
  unavailableLabel: {
91
92
  type: "string",
92
93
  minLength: 1,
93
94
  title: "Unavailable label",
94
- default: "Indisponivel"
95
+ format: "icu-message",
96
+ messageSource: "Unavailable"
95
97
  }
96
98
  }
97
99
  }
@@ -145,18 +147,7 @@ const BuyButtonButtonDefinition = defineComponent({
145
147
  label: "Buy Button Action",
146
148
  category: "commerce"
147
149
  },
148
- schema: {
149
- availableLabel: {
150
- type: "string",
151
- default: "Comprar",
152
- label: "Available label"
153
- },
154
- unavailableLabel: {
155
- type: "string",
156
- default: "Indisponivel",
157
- label: "Unavailable label"
158
- }
159
- },
150
+ schema: {},
160
151
  actions: { cart: ["addItem"] },
161
152
  guideline: "The add-to-cart button; must be a descendant of BuyButtonRoot. Cart writes flow through $actions.cart, so no CartRoot ancestor is required."
162
153
  });
@@ -207,8 +198,9 @@ function BuyButtonView({ contextValue, className, children, ...props }) {
207
198
  })
208
199
  });
209
200
  }
210
- function BuyButtonButton({ availableLabel = "Comprar", unavailableLabel = "Indisponivel", className, children, onClick, $content, $actions: $actionsProp, ...props }) {
201
+ function BuyButtonButton({ className, children, onClick, $content, $locale, $actions: $actionsProp, ...props }) {
211
202
  const $actions = useActionsOverride($actionsProp);
203
+ const { format } = useMessageFormatter(BuyButtonLabels, $content);
212
204
  const ctx = useBuyButton();
213
205
  const [addItemPending, setAddItemPending] = useState(false);
214
206
  const { quantity } = useProductQuantity();
@@ -227,7 +219,6 @@ function BuyButtonButton({ availableLabel = "Comprar", unavailableLabel = "Indis
227
219
  onClick?.(e);
228
220
  }
229
221
  const isUnavailable = !available;
230
- const defaultText = isUnavailable ? $content?.unavailableLabel ?? unavailableLabel : $content?.availableLabel ?? availableLabel;
231
222
  return /* @__PURE__ */ jsx(Button, {
232
223
  ...props,
233
224
  className,
@@ -237,7 +228,7 @@ function BuyButtonButton({ availableLabel = "Comprar", unavailableLabel = "Indis
237
228
  "data-fs-buy-button-button": "",
238
229
  ...isUnavailable ? { "data-fs-buy-button-unavailable": "" } : {},
239
230
  ...addItemPending ? { "data-fs-buy-button-pending": "" } : {},
240
- children: children ?? defaultText
231
+ children: children ?? format(isUnavailable ? "unavailableLabel" : "availableLabel")
241
232
  });
242
233
  }
243
234
  function BuyButtonSkeleton({ className, ...props }) {