@justifi/webcomponents 4.10.0-rc.0 → 4.10.0

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 (208) hide show
  1. package/dist/cjs/{Business-79361c8a.js → Business-663db337.js} +12 -1
  2. package/dist/cjs/Identity-1c4528b8.js +49 -0
  3. package/dist/cjs/{SubAccount-b3902202.js → SubAccount-05867651.js} +1 -1
  4. package/dist/cjs/additional-questions-details_5.cjs.entry.js +2 -1
  5. package/dist/cjs/form-control-datepart_3.cjs.entry.js +38 -34
  6. package/dist/cjs/form-control-monetary.cjs.entry.js +19 -17
  7. package/dist/cjs/form-control-number_3.cjs.entry.js +6 -2
  8. package/dist/cjs/gross-payment-chart-core.cjs.entry.js +2 -1
  9. package/dist/cjs/justifi-additional-questions-form-step_5.cjs.entry.js +10 -9
  10. package/dist/cjs/justifi-additional-questions_4.cjs.entry.js +3 -1
  11. package/dist/cjs/justifi-billing-form_4.cjs.entry.js +3 -3
  12. package/dist/cjs/justifi-business-address-form-step.cjs.entry.js +1 -1
  13. package/dist/cjs/justifi-business-details.cjs.entry.js +3 -1
  14. package/dist/cjs/justifi-business-form.cjs.entry.js +4 -2
  15. package/dist/cjs/justifi-business-list.cjs.entry.js +3 -2
  16. package/dist/cjs/justifi-checkout-core.cjs.entry.js +5 -4
  17. package/dist/cjs/justifi-owner-form.cjs.entry.js +5 -3
  18. package/dist/cjs/justifi-payment-balance-transactions.cjs.entry.js +2 -1
  19. package/dist/cjs/justifi-payment-form.cjs.entry.js +2 -1
  20. package/dist/cjs/{justifi-business-form-stepped.cjs.entry.js → justifi-payment-provisioning.cjs.entry.js} +4 -4
  21. package/dist/cjs/justifi-proceeds-list.cjs.entry.js +2 -1
  22. package/dist/cjs/justifi-subaccount-details.cjs.entry.js +3 -2
  23. package/dist/cjs/justifi-subaccounts-list.cjs.entry.js +3 -2
  24. package/dist/cjs/loader.cjs.js +1 -1
  25. package/dist/cjs/{payload-parsers-25ed3936.js → payload-parsers-9e72e80f.js} +3 -2
  26. package/dist/cjs/payment-details-core.cjs.entry.js +2 -1
  27. package/dist/cjs/payments-list-core.cjs.entry.js +2 -1
  28. package/dist/cjs/payout-details-core.cjs.entry.js +2 -1
  29. package/dist/cjs/payouts-list-core.cjs.entry.js +2 -1
  30. package/dist/cjs/subaccount-account-details_4.cjs.entry.js +2 -1
  31. package/dist/cjs/{utils-6f62f7a1.js → utils-2b9940e1.js} +7 -0
  32. package/dist/cjs/webcomponents.cjs.js +1 -1
  33. package/dist/collection/api/Business.js +10 -1
  34. package/dist/collection/collection-manifest.json +7 -7
  35. package/dist/collection/components/billing-form/billing-form.js +1 -1
  36. package/dist/collection/components/business-details/additional-questions-details/additional-questions-details.js +4 -4
  37. package/dist/collection/components/business-forms/business-form/business-address/business-address-form.js +1 -1
  38. package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/additional-questions/business-additional-questions-form-step.js +3 -2
  39. package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-address/business-address-form-step.js +1 -1
  40. package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-representative/business-representative-form-step.js +3 -2
  41. package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/legal-address-form/legal-address-form-step.js +2 -1
  42. package/dist/collection/components/business-forms/{business-form-stepped/business-form-stepped.js → payment-provisioning/payment-provisioning.js} +4 -4
  43. package/dist/collection/components/business-forms/payment-provisioning/test/payment-provisioning.spec.js +61 -0
  44. package/dist/collection/components/business-forms/schemas/business-address-schema.js +3 -2
  45. package/dist/collection/components/checkout/checkout-core.js +4 -4
  46. package/dist/collection/components/form/form-control-datepart.js +39 -34
  47. package/dist/collection/components/form/form-control-monetary.js +22 -20
  48. package/dist/collection/components/form/form-control-number.js +1 -0
  49. package/dist/collection/components/form/form-control-select.js +3 -1
  50. package/dist/collection/components/form/form-control-text.js +2 -1
  51. package/dist/collection/components/form/test/form-control-datepart.spec.js +88 -0
  52. package/dist/collection/components/form/test/form-control-monetary.spec.js +69 -0
  53. package/dist/collection/components/form/test/form-control-number.spec.js +77 -0
  54. package/dist/collection/components/form/test/form-control-select.spec.js +84 -0
  55. package/dist/collection/components/form/test/form-control-text.spec.js +104 -0
  56. package/dist/collection/components/form/test/form.spec.js +60 -0
  57. package/dist/collection/utils/utils.js +5 -0
  58. package/dist/docs.json +231 -230
  59. package/dist/esm/{Business-1df362e0.js → Business-6cc4e473.js} +12 -2
  60. package/dist/esm/Identity-5f8f916f.js +46 -0
  61. package/dist/esm/{SubAccount-f4ae9809.js → SubAccount-61ac70eb.js} +1 -1
  62. package/dist/esm/additional-questions-details_5.entry.js +2 -1
  63. package/dist/esm/form-control-datepart_3.entry.js +38 -34
  64. package/dist/esm/form-control-monetary.entry.js +19 -17
  65. package/dist/esm/form-control-number_3.entry.js +6 -2
  66. package/dist/esm/gross-payment-chart-core.entry.js +2 -1
  67. package/dist/esm/justifi-additional-questions-form-step_5.entry.js +10 -9
  68. package/dist/esm/justifi-additional-questions_4.entry.js +3 -1
  69. package/dist/esm/justifi-billing-form_4.entry.js +3 -3
  70. package/dist/esm/justifi-business-address-form-step.entry.js +1 -1
  71. package/dist/esm/justifi-business-details.entry.js +3 -1
  72. package/dist/esm/justifi-business-form.entry.js +4 -2
  73. package/dist/esm/justifi-business-list.entry.js +3 -2
  74. package/dist/esm/justifi-checkout-core.entry.js +5 -4
  75. package/dist/esm/justifi-owner-form.entry.js +5 -3
  76. package/dist/esm/justifi-payment-balance-transactions.entry.js +2 -1
  77. package/dist/esm/justifi-payment-form.entry.js +2 -1
  78. package/dist/esm/{justifi-business-form-stepped.entry.js → justifi-payment-provisioning.entry.js} +4 -4
  79. package/dist/esm/justifi-proceeds-list.entry.js +2 -1
  80. package/dist/esm/justifi-subaccount-details.entry.js +3 -2
  81. package/dist/esm/justifi-subaccounts-list.entry.js +3 -2
  82. package/dist/esm/loader.js +1 -1
  83. package/dist/esm/{payload-parsers-1dd7474f.js → payload-parsers-caea809d.js} +3 -2
  84. package/dist/esm/payment-details-core.entry.js +2 -1
  85. package/dist/esm/payments-list-core.entry.js +2 -1
  86. package/dist/esm/payout-details-core.entry.js +2 -1
  87. package/dist/esm/payouts-list-core.entry.js +2 -1
  88. package/dist/esm/subaccount-account-details_4.entry.js +2 -1
  89. package/dist/esm/{utils-243abdb4.js → utils-d9e90399.js} +7 -1
  90. package/dist/esm/webcomponents.js +1 -1
  91. package/dist/module/Business.js +12 -2
  92. package/dist/module/Identity.js +46 -0
  93. package/dist/module/SubAccount.js +1 -1
  94. package/dist/module/business-additional-questions-form-step.js +3 -2
  95. package/dist/module/business-address-schema.js +3 -2
  96. package/dist/module/business-owners-form-step.js +2 -1
  97. package/dist/module/business-representative-form-step.js +3 -2
  98. package/dist/module/checkout-core.js +4 -4
  99. package/dist/module/form-control-datepart2.js +37 -33
  100. package/dist/module/form-control-monetary2.js +20 -18
  101. package/dist/module/form-control-number2.js +1 -0
  102. package/dist/module/form-control-select2.js +3 -1
  103. package/dist/module/form-control-text2.js +2 -1
  104. package/dist/module/justifi-payment-balance-transactions.js +1 -1
  105. package/dist/module/justifi-payment-provisioning.d.ts +11 -0
  106. package/dist/module/{justifi-business-form-stepped.js → justifi-payment-provisioning.js} +9 -9
  107. package/dist/module/justifi-subaccount-details.js +1 -1
  108. package/dist/module/justifi-subaccounts-list.js +1 -1
  109. package/dist/module/legal-address-form-step.js +3 -2
  110. package/dist/module/owner-form.js +2 -24
  111. package/dist/module/payment-details-core2.js +1 -1
  112. package/dist/module/payment-method-form.js +1 -1
  113. package/dist/module/payments-list-core2.js +1 -1
  114. package/dist/module/payout-details-core2.js +1 -1
  115. package/dist/module/payouts-list-core2.js +1 -1
  116. package/dist/module/subaccount-merchant-details2.js +1 -1
  117. package/dist/module/utils2.js +7 -1
  118. package/dist/types/api/Business.d.ts +10 -3
  119. package/dist/types/components/business-details/additional-questions-details/additional-questions-details.d.ts +2 -2
  120. package/dist/types/components/business-forms/{business-form-stepped → payment-provisioning}/additional-questions/business-additional-questions-form-step.d.ts +2 -1
  121. package/dist/types/components/business-forms/{business-form-stepped → payment-provisioning}/business-representative/business-representative-form-step.d.ts +2 -1
  122. package/dist/types/components/business-forms/{business-form-stepped/business-form-stepped.d.ts → payment-provisioning/payment-provisioning.d.ts} +1 -1
  123. package/dist/types/components/form/form-control-datepart.d.ts +2 -1
  124. package/dist/types/components/form/form-control-monetary.d.ts +2 -1
  125. package/dist/types/components.d.ts +63 -63
  126. package/dist/types/utils/utils.d.ts +1 -0
  127. package/dist/webcomponents/p-03b1d0d5.entry.js +1 -0
  128. package/dist/webcomponents/p-0e189d6a.entry.js +1 -0
  129. package/dist/webcomponents/p-16dcf053.entry.js +1 -0
  130. package/dist/webcomponents/p-221b9504.entry.js +1 -0
  131. package/dist/webcomponents/p-2df32b4a.entry.js +1 -0
  132. package/dist/webcomponents/p-38ba2848.entry.js +1 -0
  133. package/dist/webcomponents/p-3d4aaa4a.entry.js +1 -0
  134. package/dist/webcomponents/p-41870765.entry.js +1 -0
  135. package/dist/webcomponents/p-461f42b8.entry.js +1 -0
  136. package/dist/webcomponents/p-48c2400e.js +1 -0
  137. package/dist/webcomponents/p-491ca87f.js +1 -0
  138. package/dist/webcomponents/p-60714f2a.entry.js +1 -0
  139. package/dist/webcomponents/p-60b2344a.js +1 -0
  140. package/dist/webcomponents/{p-fe2af0cf.entry.js → p-65500b31.entry.js} +1 -1
  141. package/dist/webcomponents/p-6d39b8bc.entry.js +1 -0
  142. package/dist/webcomponents/{p-9ce8d98c.entry.js → p-7ec301b5.entry.js} +1 -1
  143. package/dist/webcomponents/p-817211f2.entry.js +1 -0
  144. package/dist/webcomponents/{p-65510838.js → p-8482c23f.js} +1 -1
  145. package/dist/webcomponents/p-a4122c35.entry.js +1 -0
  146. package/dist/webcomponents/{p-c8d62072.entry.js → p-a68519e5.entry.js} +1 -1
  147. package/dist/webcomponents/p-af8363a1.entry.js +1 -0
  148. package/dist/webcomponents/p-b37d6888.entry.js +1 -0
  149. package/dist/webcomponents/p-b5a3bf86.entry.js +1 -0
  150. package/dist/webcomponents/p-cb8f9cb8.entry.js +1 -0
  151. package/dist/webcomponents/{p-0811bc77.entry.js → p-d0761829.entry.js} +1 -1
  152. package/dist/webcomponents/p-dbe32e75.entry.js +1 -0
  153. package/dist/webcomponents/p-e0c1cba8.js +1 -0
  154. package/dist/webcomponents/p-ea03b424.entry.js +1 -0
  155. package/dist/webcomponents/p-ec230480.entry.js +1 -0
  156. package/dist/webcomponents/{p-583dd5a2.entry.js → p-eed5f4ad.entry.js} +1 -1
  157. package/dist/webcomponents/webcomponents.esm.js +1 -1
  158. package/package.json +1 -1
  159. package/dist/cjs/Identity-b6364aee.js +0 -27
  160. package/dist/collection/components/business-forms/business-form-stepped/test/business-form-stepped.spec.js +0 -61
  161. package/dist/esm/Identity-774788c0.js +0 -25
  162. package/dist/module/justifi-business-form-stepped.d.ts +0 -11
  163. package/dist/webcomponents/p-0399f02b.entry.js +0 -1
  164. package/dist/webcomponents/p-04a98c63.js +0 -1
  165. package/dist/webcomponents/p-06888c2e.entry.js +0 -1
  166. package/dist/webcomponents/p-17f64853.entry.js +0 -1
  167. package/dist/webcomponents/p-22085999.entry.js +0 -1
  168. package/dist/webcomponents/p-252835df.entry.js +0 -1
  169. package/dist/webcomponents/p-28b38699.js +0 -1
  170. package/dist/webcomponents/p-374c44e6.entry.js +0 -1
  171. package/dist/webcomponents/p-4697ccfa.entry.js +0 -1
  172. package/dist/webcomponents/p-4781cd06.js +0 -1
  173. package/dist/webcomponents/p-4d806131.entry.js +0 -1
  174. package/dist/webcomponents/p-4e0b3206.entry.js +0 -1
  175. package/dist/webcomponents/p-5da4fbe7.entry.js +0 -1
  176. package/dist/webcomponents/p-6103c20d.entry.js +0 -1
  177. package/dist/webcomponents/p-7654c70d.entry.js +0 -1
  178. package/dist/webcomponents/p-847441ce.entry.js +0 -1
  179. package/dist/webcomponents/p-861ba3fc.entry.js +0 -1
  180. package/dist/webcomponents/p-a98eea84.entry.js +0 -1
  181. package/dist/webcomponents/p-b27c9b0d.entry.js +0 -1
  182. package/dist/webcomponents/p-e3748ea4.entry.js +0 -1
  183. package/dist/webcomponents/p-eef5ed19.entry.js +0 -1
  184. package/dist/webcomponents/p-efc7025c.entry.js +0 -1
  185. package/dist/webcomponents/p-f3453ca2.js +0 -1
  186. package/dist/webcomponents/p-f3ca6fda.entry.js +0 -1
  187. /package/dist/cjs/{state-options-96d05a98.js → state-options-4fbcb48a.js} +0 -0
  188. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/additional-questions/test/business-additional-questions-form-step.spec.js +0 -0
  189. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-address/business-address-form-step.css +0 -0
  190. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-address/test/business-address-form-step.spec.js +0 -0
  191. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-core-info/business-core-info-form-step.css +0 -0
  192. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-core-info/business-core-info-form-step.js +0 -0
  193. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-core-info/test/business-core-info-form-step.spec.js +0 -0
  194. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-owners/business-owners-form-step.css +0 -0
  195. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-owners/business-owners-form-step.js +0 -0
  196. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-owners/test/business-owners-form-step.spec.js +0 -0
  197. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-representative/business-representative-form-step.css +0 -0
  198. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/business-representative/test/business-representative-form-step.spec.js +0 -0
  199. /package/dist/collection/components/business-forms/{business-form-stepped → payment-provisioning}/legal-address-form/legal-address-form-step.css +0 -0
  200. /package/dist/collection/components/business-forms/{business-form-stepped/business-form-stepped.css → payment-provisioning/payment-provisioning.css} +0 -0
  201. /package/dist/collection/{components/billing-form → utils}/state-options.js +0 -0
  202. /package/dist/esm/{state-options-fc1daf67.js → state-options-a356fb11.js} +0 -0
  203. /package/dist/types/components/business-forms/{business-form-stepped → payment-provisioning}/business-address/business-address-form-step.d.ts +0 -0
  204. /package/dist/types/components/business-forms/{business-form-stepped → payment-provisioning}/business-core-info/business-core-info-form-step.d.ts +0 -0
  205. /package/dist/types/components/business-forms/{business-form-stepped → payment-provisioning}/business-owners/business-owners-form-step.d.ts +0 -0
  206. /package/dist/types/components/business-forms/{business-form-stepped → payment-provisioning}/legal-address-form/legal-address-form-step.d.ts +0 -0
  207. /package/dist/types/{components/billing-form → utils}/state-options.d.ts +0 -0
  208. /package/dist/webcomponents/{p-8e2a88a8.js → p-6078a370.js} +0 -0
@@ -0,0 +1,61 @@
1
+ import { newSpecPage } from "@stencil/core/testing";
2
+ import { PaymentProvisioning } from "../payment-provisioning";
3
+ describe.skip('justifi-payment-provisioning', () => {
4
+ let consoleSpy;
5
+ // Initialize the spy in the beforeEach
6
+ beforeEach(() => {
7
+ consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
8
+ });
9
+ // Restore the original function in the afterEach
10
+ afterEach(() => {
11
+ consoleSpy.mockRestore();
12
+ });
13
+ it('should log a warning if no authToken is provided', async () => {
14
+ await newSpecPage({
15
+ components: [PaymentProvisioning],
16
+ html: `<justifi-payment-provisioning></justifi-payment-provisioning>`,
17
+ });
18
+ });
19
+ it('should log a warning if no businessId is provided', async () => {
20
+ await newSpecPage({
21
+ components: [PaymentProvisioning],
22
+ html: `<justifi-payment-provisioning></justifi-payment-provisioning>`,
23
+ });
24
+ });
25
+ it('should emit server error event if server error when fetching data', async () => {
26
+ await newSpecPage({
27
+ components: [PaymentProvisioning],
28
+ html: `<justifi-payment-provisioning business-id="biz_123" auth-token="some-token"></justifi-payment-provisioning>`,
29
+ });
30
+ });
31
+ it('should emit server error event if server error when patching data', async () => {
32
+ await newSpecPage({
33
+ components: [PaymentProvisioning],
34
+ html: `<justifi-payment-provisioning business-id="biz_123" auth-token="some-token"></justifi-payment-provisioning>`,
35
+ });
36
+ });
37
+ it('should render', async () => {
38
+ await newSpecPage({
39
+ components: [PaymentProvisioning],
40
+ html: `<justifi-payment-provisioning business-id="biz_123" auth-token="some-token"></justifi-payment-provisioning>`,
41
+ });
42
+ });
43
+ it('should call onSuccess when a step is successfully completed', async () => {
44
+ await newSpecPage({
45
+ components: [PaymentProvisioning],
46
+ html: `<justifi-payment-provisioning business-id="biz_123" auth-token="some-token"></justifi-payment-provisioning>`,
47
+ });
48
+ });
49
+ it('should emit clickEvent when a button is clicked', async () => {
50
+ await newSpecPage({
51
+ components: [PaymentProvisioning],
52
+ html: `<justifi-payment-provisioning business-id="biz_123" auth-token="some-token"></justifi-payment-provisioning>`,
53
+ });
54
+ });
55
+ it('should emit submitted event when a step is completed', async () => {
56
+ await newSpecPage({
57
+ components: [PaymentProvisioning],
58
+ html: `<justifi-payment-provisioning business-id="biz_123" auth-token="some-token"></justifi-payment-provisioning>`,
59
+ });
60
+ });
61
+ });
@@ -1,15 +1,16 @@
1
1
  import { object, string } from "yup";
2
+ import StateOptions from "../../../utils/state-options";
2
3
  export const legalAddressSchema = object({
3
4
  line1: string().required('Enter street address'),
4
5
  line2: string().nullable(),
5
6
  city: string().required('Enter city'),
6
- state: string().required('Select state'),
7
+ state: string().required('Select state').oneOf(StateOptions.map((option) => option.value), 'Select state'),
7
8
  postal_code: string().required('Enter postal code'),
8
9
  country: string().required('Select country'),
9
10
  });
10
11
  export const identityAddressSchema = object({
11
12
  line1: string().required('Enter street address'),
12
13
  city: string().required('Enter city'),
13
- state: string().required('Select state'),
14
+ state: string().required('Select state').oneOf(StateOptions.map((option) => option.value), 'Select state'),
14
15
  postal_code: string().required('Enter postal code'),
15
16
  });
@@ -1,5 +1,5 @@
1
1
  import { h, Host } from "@stencil/core";
2
- import { extractComputedFontsToLoad } from "../../utils/utils";
2
+ import { extractComputedFontsToLoad, formatCurrency } from "../../utils/utils";
3
3
  import { config } from "../../../config";
4
4
  export class CheckoutCore {
5
5
  constructor() {
@@ -11,7 +11,7 @@ export class CheckoutCore {
11
11
  this.checkoutId = undefined;
12
12
  this.hasLoadedFonts = false;
13
13
  this.isLoading = false;
14
- this.checkout = {};
14
+ this.checkout = undefined;
15
15
  this.serverError = false;
16
16
  this.errorMessage = '';
17
17
  this.creatingNewPaymentMethod = false;
@@ -78,8 +78,8 @@ export class CheckoutCore {
78
78
  });
79
79
  }
80
80
  render() {
81
- var _a, _b;
82
- return (h(Host, null, h("div", { class: "row gy-3" }, h("div", { class: "col-12 mb-4" }, h("h2", { class: "fs-5 fw-bold" }, "Summary"), h("div", null, "Product desctiption"), h("div", null, "Total $100.00")), h("div", { class: "col-12" }, h("h2", { class: "fs-5 fw-bold border-bottom pb-3" }, "Payment"), h("h3", { class: "fs-6 fw-bold lh-lg" }, "Select payment type"), h("div", { class: "d-flex flex-column" }, h("justifi-payment-method-options", { ref: (el) => (this.paymentMethodOptionsRef = el), "show-card": ((_a = this.checkout.payment_settings) === null || _a === void 0 ? void 0 : _a.credit_card_payments) || true, "show-ach": ((_b = this.checkout.payment_settings) === null || _b === void 0 ? void 0 : _b.ach_payments) || true, "client-id": this.checkout.payment_client_id, "account-id": this.checkout.account_id, savedPaymentMethods: this.checkout.payment_methods }))), h("div", { class: "col-12" }, h("div", { class: "d-flex justify-content-end" }, h("button", { type: "submit", onClick: event => this.submit(event), disabled: this.isLoading, class: `btn btn-primary jfi-submit-button ${this.isLoading ? 'jfi-submit-button-loading' : ''}` }, this.isLoading ? this.loadingSpinner : 'Pay'))))));
81
+ var _a, _b, _c, _d, _e, _f, _g, _h;
82
+ return (h(Host, null, h("div", { class: "row gy-3" }, h("div", { class: "col-12 mb-4" }, h("h2", { class: "fs-5 fw-bold" }, "Summary"), this.checkout && (h("div", null, h("div", null, (_a = this.checkout) === null || _a === void 0 ? void 0 : _a.payment_description), h("div", null, "Total ", formatCurrency(+this.checkout.payment_amount))))), h("div", { class: "col-12" }, h("h2", { class: "fs-5 fw-bold border-bottom pb-3" }, "Payment"), h("h3", { class: "fs-6 fw-bold lh-lg" }, "Select payment type"), h("div", { class: "d-flex flex-column" }, h("justifi-payment-method-options", { ref: (el) => (this.paymentMethodOptionsRef = el), "show-card": ((_c = (_b = this.checkout) === null || _b === void 0 ? void 0 : _b.payment_settings) === null || _c === void 0 ? void 0 : _c.credit_card_payments) || true, "show-ach": ((_e = (_d = this.checkout) === null || _d === void 0 ? void 0 : _d.payment_settings) === null || _e === void 0 ? void 0 : _e.ach_payments) || true, "client-id": (_f = this.checkout) === null || _f === void 0 ? void 0 : _f.payment_client_id, "account-id": (_g = this.checkout) === null || _g === void 0 ? void 0 : _g.account_id, savedPaymentMethods: (_h = this.checkout) === null || _h === void 0 ? void 0 : _h.payment_methods }))), h("div", { class: "col-12" }, h("div", { class: "d-flex justify-content-end" }, h("button", { type: "submit", onClick: event => this.submit(event), disabled: this.isLoading, class: `btn btn-primary jfi-submit-button ${this.isLoading ? 'jfi-submit-button-loading' : ''}` }, this.isLoading ? this.loadingSpinner : 'Pay'))))));
83
83
  }
84
84
  static get is() { return "justifi-checkout-core"; }
85
85
  static get encapsulation() { return "shadow"; }
@@ -7,7 +7,7 @@ export class DatePartInput {
7
7
  this.name = undefined;
8
8
  this.error = undefined;
9
9
  this.defaultValue = undefined;
10
- this.type = undefined;
10
+ this.type = 'day';
11
11
  this.inputHandler = undefined;
12
12
  this.disabled = undefined;
13
13
  }
@@ -15,45 +15,49 @@ export class DatePartInput {
15
15
  this.updateInput(newValue);
16
16
  }
17
17
  componentDidLoad() {
18
- if (this.textInput) {
19
- let maskOptions;
20
- switch (this.type) {
21
- case 'day':
22
- maskOptions = { mask: Number, min: 1, max: 31 };
23
- break;
24
- case 'month':
25
- maskOptions = { mask: Number, min: 1, max: 12 };
26
- break;
27
- case 'year':
28
- maskOptions = {
29
- mask: Number,
30
- min: 1900,
31
- max: new Date().getFullYear(),
32
- };
33
- break;
34
- default:
35
- throw new Error('Invalid type prop');
36
- }
37
- this.imask = IMask(this.textInput, maskOptions);
38
- this.imask.on('accept', () => {
39
- const rawValue = this.imask.unmaskedValue;
40
- this.inputHandler(this.name, rawValue);
41
- });
42
- this.textInput.addEventListener('blur', () => {
43
- this.formControlBlur.emit();
44
- });
45
- this.updateInput(this.defaultValue);
18
+ this.initializeMask();
19
+ this.updateInput(this.defaultValue);
20
+ }
21
+ disconnectedCallback() {
22
+ var _a;
23
+ (_a = this.imask) === null || _a === void 0 ? void 0 : _a.destroy();
24
+ }
25
+ initializeMask() {
26
+ if (!this.textInput) {
27
+ return;
46
28
  }
29
+ let maskOptions;
30
+ switch (this.type) {
31
+ default:
32
+ case 'day':
33
+ maskOptions = { mask: Number, min: 1, max: 31 };
34
+ break;
35
+ case 'month':
36
+ maskOptions = { mask: Number, min: 1, max: 12 };
37
+ break;
38
+ case 'year':
39
+ maskOptions = {
40
+ mask: Number,
41
+ min: 1900,
42
+ max: new Date().getFullYear(),
43
+ };
44
+ break;
45
+ }
46
+ this.imask = IMask(this.textInput, maskOptions);
47
+ this.imask.on('accept', () => {
48
+ const rawValue = this.imask.unmaskedValue;
49
+ this.inputHandler(this.name, rawValue);
50
+ this.formControlInput.emit({ name: this.name, value: rawValue });
51
+ });
52
+ this.textInput.addEventListener('blur', () => {
53
+ this.formControlBlur.emit();
54
+ });
47
55
  }
48
56
  updateInput(newValue) {
49
57
  if (this.imask) {
50
58
  this.imask.value = String(newValue);
51
59
  }
52
60
  }
53
- disconnectedCallback() {
54
- var _a;
55
- (_a = this.imask) === null || _a === void 0 ? void 0 : _a.destroy();
56
- }
57
61
  render() {
58
62
  return (h(Host, { exportparts: "label,input,input-invalid" }, h("label", { part: "label", class: "form-label", htmlFor: this.name }, this.label), h("input", { ref: el => (this.textInput = el), id: this.name, name: this.name, onBlur: () => this.formControlBlur.emit(), part: `input ${this.error && 'input-invalid'}`, class: this.error ? 'form-control is-invalid' : 'form-control', type: "text", disabled: this.disabled }), this.error && h("div", { class: "invalid-feedback" }, this.error)));
59
63
  }
@@ -154,7 +158,8 @@ export class DatePartInput {
154
158
  "text": ""
155
159
  },
156
160
  "attribute": "type",
157
- "reflect": false
161
+ "reflect": false,
162
+ "defaultValue": "'day'"
158
163
  },
159
164
  "inputHandler": {
160
165
  "type": "unknown",
@@ -13,29 +13,31 @@ export class MonetaryInput {
13
13
  this.updateInput(newValue);
14
14
  }
15
15
  componentDidLoad() {
16
- if (this.textInput) {
17
- this.imask = IMask(this.textInput, {
18
- mask: Number,
19
- scale: 2,
20
- thousandsSeparator: ',',
21
- padFractionalZeros: true,
22
- normalizeZeros: true,
23
- radix: '.',
24
- });
25
- this.imask.on('accept', () => {
26
- const rawValue = this.imask.unmaskedValue;
27
- this.inputHandler(this.name, rawValue);
28
- });
29
- this.textInput.addEventListener('blur', () => {
30
- this.formControlBlur.emit();
31
- });
32
- }
16
+ this.initializeIMask();
33
17
  this.updateInput(this.defaultValue);
34
18
  }
35
19
  disconnectedCallback() {
36
20
  var _a;
37
21
  (_a = this.imask) === null || _a === void 0 ? void 0 : _a.destroy();
38
22
  }
23
+ initializeIMask() {
24
+ if (!this.textInput)
25
+ return;
26
+ this.imask = IMask(this.textInput, {
27
+ mask: Number,
28
+ scale: 2,
29
+ thousandsSeparator: ',',
30
+ padFractionalZeros: true,
31
+ normalizeZeros: true,
32
+ radix: '.',
33
+ });
34
+ this.imask.on('accept', () => {
35
+ const rawValue = this.imask.unmaskedValue;
36
+ this.inputHandler(this.name, rawValue);
37
+ this.formControlInput.emit({ name: this.name, value: rawValue });
38
+ });
39
+ this.textInput.addEventListener('blur', () => this.formControlBlur.emit());
40
+ }
39
41
  updateInput(newValue) {
40
42
  if (this.imask) {
41
43
  this.imask.value = String(newValue);
@@ -76,11 +78,11 @@ export class MonetaryInput {
76
78
  "reflect": false
77
79
  },
78
80
  "name": {
79
- "type": "any",
81
+ "type": "string",
80
82
  "mutable": false,
81
83
  "complexType": {
82
- "original": "any",
83
- "resolved": "any",
84
+ "original": "string",
85
+ "resolved": "string",
84
86
  "references": {}
85
87
  },
86
88
  "required": false,
@@ -22,6 +22,7 @@ export class NumberInput {
22
22
  const target = event.target;
23
23
  const name = target.getAttribute('name');
24
24
  this.inputHandler(name, target.value);
25
+ this.formControlInput.emit({ name, value: target.value });
25
26
  }
26
27
  componentDidLoad() {
27
28
  this.updateInput(this.defaultValue);
@@ -19,12 +19,14 @@ export class SelectInput {
19
19
  const target = event.target;
20
20
  const name = target.getAttribute('name');
21
21
  this.inputHandler(name, target.value);
22
+ this.formControlInput.emit(target.value);
22
23
  }
23
24
  componentDidLoad() {
24
25
  this.updateInput(this.defaultValue);
25
26
  }
26
27
  render() {
27
- return (h(Host, { exportparts: "label,input,input-invalid" }, h("label", { part: "label", class: "form-label", htmlFor: this.name }, this.label), h("select", { ref: el => (this.selectElement = el), id: this.name, name: this.name, onInput: (event) => this.handleFormControlInput(event), onBlur: () => this.formControlBlur.emit(), part: `input ${this.error && 'input-invalid'}`, class: this.error ? 'form-select is-invalid' : 'form-select', disabled: this.disabled }, this.options.map(option => (h("option", { value: option.value }, option.label)))), this.error && h("div", { class: "invalid-feedback" }, this.error)));
28
+ var _a;
29
+ return (h(Host, { exportparts: "label,input,input-invalid" }, h("label", { part: "label", class: "form-label", htmlFor: this.name }, this.label), h("select", { ref: el => (this.selectElement = el), id: this.name, name: this.name, onInput: (event) => this.handleFormControlInput(event), onBlur: () => this.formControlBlur.emit(), part: `input ${this.error ? 'input-invalid' : ''}`, class: this.error ? 'form-select is-invalid' : 'form-select', disabled: this.disabled }, (_a = this.options) === null || _a === void 0 ? void 0 : _a.map(option => (h("option", { value: option.value }, option.label)))), this.error && h("div", { class: "invalid-feedback" }, this.error)));
28
30
  }
29
31
  static get is() { return "form-control-select"; }
30
32
  static get encapsulation() { return "shadow"; }
@@ -22,12 +22,13 @@ export class TextInput {
22
22
  const target = event.target;
23
23
  const name = target.getAttribute('name');
24
24
  this.inputHandler(name, target.value);
25
+ this.formControlInput.emit(target.value);
25
26
  }
26
27
  componentDidLoad() {
27
28
  this.updateInput(this.defaultValue);
28
29
  }
29
30
  render() {
30
- return (h(Host, { exportparts: "label,input,input-invalid" }, h("label", { part: "label", class: "form-label", htmlFor: this.name }, this.label), h("input", { id: this.name, name: this.name, onInput: (event) => this.handleFormControlInput(event), onBlur: () => this.formControlBlur.emit(), part: `input ${this.error && 'input-invalid'} ${this.disabled && 'input-disabled'}`, class: this.error ? 'form-control is-invalid' : 'form-control', type: "text", disabled: this.disabled }), this.error && h("div", { class: "invalid-feedback" }, this.error)));
31
+ return (h(Host, { exportparts: "label,input,input-invalid" }, h("label", { part: "label", class: "form-label", htmlFor: this.name }, this.label), h("input", { id: this.name, name: this.name, onInput: (event) => this.handleFormControlInput(event), onBlur: () => this.formControlBlur.emit(), part: `input ${this.error ? 'input-invalid ' : ''}${this.disabled ? ' input-disabled' : ''}`, class: this.error ? 'form-control is-invalid' : 'form-control', type: "text", disabled: this.disabled }), this.error && h("div", { class: "invalid-feedback" }, this.error)));
31
32
  }
32
33
  static get is() { return "form-control-text"; }
33
34
  static get encapsulation() { return "shadow"; }
@@ -0,0 +1,88 @@
1
+ import { newSpecPage } from "@stencil/core/testing";
2
+ import { DatePartInput } from "../form-control-datepart";
3
+ import * as IMaskPackage from "imask";
4
+ jest.mock('imask', () => ({
5
+ default: jest.fn().mockImplementation(() => ({
6
+ on: jest.fn(),
7
+ destroy: jest.fn(),
8
+ value: '',
9
+ unmaskedValue: '',
10
+ })),
11
+ }));
12
+ const IMaskSpy = jest.spyOn(IMaskPackage, 'default');
13
+ afterEach(() => {
14
+ jest.clearAllMocks();
15
+ });
16
+ describe('form-control-datepart', () => {
17
+ it('renders correctly with default props', async () => {
18
+ const page = await newSpecPage({
19
+ components: [DatePartInput],
20
+ html: `<form-control-datepart label="Date Part" name="datepart"></form-control-datepart>`,
21
+ });
22
+ expect(page.root).toMatchSnapshot();
23
+ });
24
+ it('should initialize IMask with correct options for "day" type', async () => {
25
+ await newSpecPage({
26
+ components: [DatePartInput],
27
+ html: '<form-control-datepart type="day"></form-control-datepart>',
28
+ });
29
+ expect(IMaskSpy).toHaveBeenCalled();
30
+ expect(IMaskSpy.mock.calls[0][1]).toEqual({ mask: Number, min: 1, max: 31 });
31
+ });
32
+ it('should initialize IMask with correct options for "month" type', async () => {
33
+ // Clear mocks to reset the calls count
34
+ jest.clearAllMocks();
35
+ await newSpecPage({
36
+ components: [DatePartInput],
37
+ html: '<form-control-datepart type="month"></form-control-datepart>',
38
+ });
39
+ expect(IMaskSpy).toHaveBeenCalled();
40
+ expect(IMaskSpy.mock.calls[0][1]).toEqual({ mask: Number, min: 1, max: 12 });
41
+ });
42
+ it('should initialize IMask with correct options for "year" type', async () => {
43
+ // Clear mocks to reset the calls count
44
+ jest.clearAllMocks();
45
+ await newSpecPage({
46
+ components: [DatePartInput],
47
+ html: '<form-control-datepart type="year"></form-control-datepart>',
48
+ });
49
+ expect(IMaskSpy).toHaveBeenCalled();
50
+ expect(IMaskSpy.mock.calls[0][1]).toEqual({ mask: Number, min: 1900, max: new Date().getFullYear() });
51
+ });
52
+ it('emits formControlBlur on input blur', async () => {
53
+ const page = await newSpecPage({
54
+ components: [DatePartInput],
55
+ html: `<form-control-datepart></form-control-datepart>`,
56
+ });
57
+ const blurSpy = jest.fn();
58
+ page.win.addEventListener('formControlBlur', blurSpy);
59
+ const input = page.root.shadowRoot.querySelector('input');
60
+ input.dispatchEvent(new Event('blur', { bubbles: true }));
61
+ await page.waitForChanges();
62
+ expect(blurSpy).toHaveBeenCalled();
63
+ });
64
+ it('calls inputHandler and emits formControlInput on user input', async () => {
65
+ const inputHandlerMock = jest.fn();
66
+ const page = await newSpecPage({
67
+ components: [DatePartInput],
68
+ html: `<form-control-datepart type="day" name="day"></form-control-datepart>`,
69
+ });
70
+ page.rootInstance.inputHandler = inputHandlerMock;
71
+ await page.waitForChanges();
72
+ const inputSpy = jest.fn();
73
+ page.win.addEventListener('formControlInput', inputSpy);
74
+ const input = page.root.shadowRoot.querySelector('input');
75
+ input.value = '15'; // Assuming 'day' type for simplicity
76
+ input.dispatchEvent(new Event('input', { bubbles: true }));
77
+ await page.waitForChanges();
78
+ expect(inputHandlerMock).toHaveBeenCalledWith('day', '15');
79
+ expect(inputSpy).toHaveBeenCalled();
80
+ });
81
+ it('displays error message correctly', async () => {
82
+ const page = await newSpecPage({
83
+ components: [DatePartInput],
84
+ html: `<form-control-datepart error="Error message"></form-control-datepart>`,
85
+ });
86
+ expect(page.root).toMatchSnapshot();
87
+ });
88
+ });
@@ -0,0 +1,69 @@
1
+ import { newSpecPage } from "@stencil/core/testing";
2
+ import { MonetaryInput } from "../form-control-monetary";
3
+ describe('form-control-monetary', () => {
4
+ it('renders correctly with default props', async () => {
5
+ const page = await newSpecPage({
6
+ components: [MonetaryInput],
7
+ html: `<form-control-monetary></form-control-monetary>`,
8
+ });
9
+ expect(page.root).toMatchSnapshot();
10
+ });
11
+ it('handles props correctly', async () => {
12
+ const page = await newSpecPage({
13
+ components: [MonetaryInput],
14
+ html: `
15
+ <form-control-monetary
16
+ label="Amount"
17
+ name="amount"
18
+ error="Invalid amount"
19
+ defaultValue="1000"
20
+ ></form-control-monetary>
21
+ `,
22
+ });
23
+ await page.waitForChanges();
24
+ const label = page.root.shadowRoot.querySelector('label');
25
+ const input = page.root.shadowRoot.querySelector('input');
26
+ const errorDiv = page.root.shadowRoot.querySelector('.invalid-feedback');
27
+ expect(label.textContent).toBe('Amount');
28
+ expect(input.getAttribute('name')).toBe('amount');
29
+ expect(errorDiv.textContent).toBe('Invalid amount');
30
+ });
31
+ it('calls inputHandler and emits formControlInput on user input', async () => {
32
+ const inputHandlerMock = jest.fn();
33
+ const page = await newSpecPage({
34
+ components: [MonetaryInput],
35
+ html: `<form-control-monetary></form-control-monetary>`,
36
+ });
37
+ page.rootInstance.inputHandler = inputHandlerMock;
38
+ await page.waitForChanges();
39
+ const inputSpy = jest.fn();
40
+ page.win.addEventListener('formControlInput', inputSpy);
41
+ const input = page.root.shadowRoot.querySelector('input');
42
+ input.value = '1234.56';
43
+ input.dispatchEvent(new Event('input', { bubbles: true }));
44
+ await page.waitForChanges();
45
+ expect(inputHandlerMock).toHaveBeenCalled();
46
+ expect(inputSpy).toHaveBeenCalled();
47
+ });
48
+ it('emits formControlBlur on input blur', async () => {
49
+ const page = await newSpecPage({
50
+ components: [MonetaryInput],
51
+ html: `<form-control-monetary></form-control-monetary>`,
52
+ });
53
+ const blurSpy = jest.fn();
54
+ page.win.addEventListener('formControlBlur', blurSpy);
55
+ const input = page.root.shadowRoot.querySelector('input');
56
+ input.dispatchEvent(new Event('blur', { bubbles: true }));
57
+ await page.waitForChanges();
58
+ expect(blurSpy).toHaveBeenCalled();
59
+ });
60
+ it('displays error message when error prop is set', async () => {
61
+ const page = await newSpecPage({
62
+ components: [MonetaryInput],
63
+ html: `<form-control-monetary error="Invalid amount"></form-control-monetary>`,
64
+ });
65
+ const errorDiv = page.root.shadowRoot.querySelector('.invalid-feedback');
66
+ expect(errorDiv.textContent).toBe('Invalid amount');
67
+ expect(page.root).toMatchSnapshot();
68
+ });
69
+ });
@@ -0,0 +1,77 @@
1
+ import { newSpecPage } from "@stencil/core/testing";
2
+ import { NumberInput } from "../form-control-number";
3
+ describe('form-control-number', () => {
4
+ it('renders with default props', async () => {
5
+ const page = await newSpecPage({
6
+ components: [NumberInput],
7
+ html: `<form-control-number label="Age"></form-control-number>`,
8
+ });
9
+ expect(page.root).toMatchSnapshot();
10
+ });
11
+ it('renders with an error message', async () => {
12
+ const page = await newSpecPage({
13
+ components: [NumberInput],
14
+ html: `<form-control-number label="Age" error="Invalid input"></form-control-number>`,
15
+ });
16
+ expect(page.root).toMatchSnapshot();
17
+ });
18
+ it('handles the disabled state', async () => {
19
+ const page = await newSpecPage({
20
+ components: [NumberInput],
21
+ html: `<form-control-number label="Age" disabled></form-control-number>`,
22
+ });
23
+ expect(page.root).toMatchSnapshot();
24
+ });
25
+ it('updates the input value when defaultValue changes', async () => {
26
+ const page = await newSpecPage({
27
+ components: [NumberInput],
28
+ html: `<form-control-number label="Age"></form-control-number>`,
29
+ });
30
+ let input = page.root.shadowRoot.querySelector('input');
31
+ expect(input.value).toBe(''); // Default should be empty
32
+ page.root.defaultValue = '30';
33
+ await page.waitForChanges();
34
+ input = page.root.shadowRoot.querySelector('input');
35
+ expect(input.value).toBe('30');
36
+ });
37
+ it('calls inputHandler on user input', async () => {
38
+ const inputHandlerMock = jest.fn();
39
+ const page = await newSpecPage({
40
+ components: [NumberInput],
41
+ html: `<form-control-number name="age"></form-control-number>`,
42
+ });
43
+ page.rootInstance.inputHandler = inputHandlerMock;
44
+ await page.waitForChanges();
45
+ const input = page.root.shadowRoot.querySelector('input');
46
+ input.value = '25';
47
+ await input.dispatchEvent(new Event('input', { bubbles: true }));
48
+ expect(inputHandlerMock).toHaveBeenCalledWith('age', '25');
49
+ });
50
+ it('emits formControlBlur on input blur', async () => {
51
+ const page = await newSpecPage({
52
+ components: [NumberInput],
53
+ html: `<form-control-number></form-control-number>`,
54
+ });
55
+ const blurSpy = jest.fn();
56
+ page.win.addEventListener('formControlBlur', blurSpy);
57
+ const input = page.root.shadowRoot.querySelector('input');
58
+ await input.dispatchEvent(new CustomEvent('blur'));
59
+ expect(blurSpy).toHaveBeenCalled();
60
+ });
61
+ it('emits formControlInput on input', async () => {
62
+ const page = await newSpecPage({
63
+ components: [NumberInput],
64
+ html: `<form-control-number name="age"></form-control-number>`,
65
+ });
66
+ page.rootInstance.inputHandler = jest.fn();
67
+ await page.waitForChanges();
68
+ const inputSpy = jest.fn();
69
+ page.win.addEventListener('formControlInput', inputSpy);
70
+ const input = page.root.shadowRoot.querySelector('input');
71
+ input.value = '25';
72
+ const mockEvent = new Event('input', { bubbles: true });
73
+ Object.defineProperty(mockEvent, 'target', { value: input, enumerable: true });
74
+ await input.dispatchEvent(mockEvent);
75
+ expect(inputSpy).toHaveBeenCalled();
76
+ });
77
+ });
@@ -0,0 +1,84 @@
1
+ import { newSpecPage } from "@stencil/core/testing";
2
+ import { SelectInput } from "../form-control-select";
3
+ describe('form-control-select', () => {
4
+ it('renders with default props', async () => {
5
+ const page = await newSpecPage({
6
+ components: [SelectInput],
7
+ html: `<form-control-select label="Test Select" name="test"></form-control-select>`,
8
+ });
9
+ expect(page.root).toMatchSnapshot();
10
+ expect(page.rootInstance.label).toBe('Test Select');
11
+ expect(page.rootInstance.name).toBe('test');
12
+ });
13
+ it('populates options correctly and handles selection', async () => {
14
+ const options = [
15
+ { label: 'Option 1', value: '1' },
16
+ { label: 'Option 2', value: '2' }
17
+ ];
18
+ const page = await newSpecPage({
19
+ components: [SelectInput],
20
+ html: `<form-control-select></form-control-select>`,
21
+ });
22
+ page.rootInstance.options = options;
23
+ await page.waitForChanges();
24
+ const selectElement = page.root.shadowRoot.querySelector('select');
25
+ expect(selectElement.children.length).toBe(options.length);
26
+ expect(selectElement.children[0].textContent).toBe(options[0].label);
27
+ selectElement.value = options[1].value;
28
+ expect(selectElement.value).toBe('2');
29
+ });
30
+ it('shows error and applies error styling when error prop is provided', async () => {
31
+ const page = await newSpecPage({
32
+ components: [SelectInput],
33
+ html: `<form-control-select error="This field is required."></form-control-select>`,
34
+ });
35
+ const shadowRoot = page.root.shadowRoot;
36
+ expect(shadowRoot.querySelector('.invalid-feedback').textContent).toBe('This field is required.');
37
+ expect(shadowRoot.querySelector('.form-select').classList.contains('is-invalid')).toBeTruthy();
38
+ });
39
+ it('emits formControlInput event on input', async () => {
40
+ const page = await newSpecPage({
41
+ components: [SelectInput],
42
+ html: `<form-control-select></form-control-select>`,
43
+ });
44
+ page.rootInstance.inputHandler = jest.fn();
45
+ const inputEventSpy = jest.fn();
46
+ page.root.addEventListener('formControlInput', inputEventSpy);
47
+ const selectElement = page.root.shadowRoot.querySelector('select');
48
+ selectElement.value = '1';
49
+ selectElement.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
50
+ await page.waitForChanges();
51
+ expect(inputEventSpy).toHaveBeenCalled();
52
+ });
53
+ it('emits formControlBlur event on blur', async () => {
54
+ const page = await newSpecPage({
55
+ components: [SelectInput],
56
+ html: `<form-control-select></form-control-select>`,
57
+ });
58
+ const blurEventSpy = jest.fn();
59
+ page.root.addEventListener('formControlBlur', blurEventSpy);
60
+ const selectElement = page.root.shadowRoot.querySelector('select');
61
+ selectElement.dispatchEvent(new Event('blur'));
62
+ expect(blurEventSpy).toHaveBeenCalled();
63
+ });
64
+ it('disables select when disabled prop is true', async () => {
65
+ const page = await newSpecPage({
66
+ components: [SelectInput],
67
+ html: `<form-control-select disabled="true"></form-control-select>`,
68
+ });
69
+ const selectElement = page.root.shadowRoot.querySelector('select');
70
+ // This is weird, but the value of the disabled
71
+ // attribute is an empty string on Stencil mock DOM
72
+ expect(selectElement.attributes.getNamedItem('disabled').value).toBe('');
73
+ });
74
+ it('handles empty options array', async () => {
75
+ const page = await newSpecPage({
76
+ components: [SelectInput],
77
+ html: `<form-control-select></form-control-select>`,
78
+ });
79
+ page.rootInstance.options = [];
80
+ await page.waitForChanges();
81
+ const selectElement = page.root.shadowRoot.querySelector('select');
82
+ expect(selectElement.children.length).toBe(0);
83
+ });
84
+ });