From b44870437887d02dd13c07fb81972f63b4c2ec84 Mon Sep 17 00:00:00 2001 From: andreagostinho-meli Date: Tue, 25 Jul 2023 18:17:05 -0300 Subject: [PATCH 1/9] feat: check if amount is different between two objects --- src/bricks/util/amount/amount.test.ts | 208 ++++++++++++++++++++++++++ src/bricks/util/amount/amount.ts | 25 ++++ src/bricks/util/deepCopy.ts | 25 ++++ 3 files changed, 258 insertions(+) create mode 100644 src/bricks/util/amount/amount.test.ts create mode 100644 src/bricks/util/amount/amount.ts create mode 100644 src/bricks/util/deepCopy.ts diff --git a/src/bricks/util/amount/amount.test.ts b/src/bricks/util/amount/amount.test.ts new file mode 100644 index 0000000..5c59e8d --- /dev/null +++ b/src/bricks/util/amount/amount.test.ts @@ -0,0 +1,208 @@ +import { TCardPayment } from '../../cardPayment/type'; +import { checkOnlyAmountIsDifferent } from './amount'; + +describe('Amount - Card Payment Brick', () => { + test('should return true when only the amount is different between two objects', () => { + const obj1: TCardPayment = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + types: { + excluded: ['debit_card'], + }, + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + const obj2: TCardPayment = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + types: { + excluded: ['debit_card'], + }, + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 90, + }, + onSubmit: async () => { + return; + }, + }; + + const result = checkOnlyAmountIsDifferent(obj1, obj2); + + expect(result).toBe(true); + }); + + test('should return false when the amount is equal between two objects and other fields are equal', () => { + const obj1: TCardPayment = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + types: { + excluded: ['debit_card'], + }, + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + const obj2: TCardPayment = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + types: { + excluded: ['debit_card'], + }, + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + + const result = checkOnlyAmountIsDifferent(obj1, obj2); + + expect(result).toBe(false); + }); + + test('should return false when the amount is equal between two objects and other fields are different', () => { + const obj1: TCardPayment = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + types: { + excluded: ['debit_card'], + }, + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + const obj2: TCardPayment = { + locale: 'en-US', + customization: { + paymentMethods: { + maxInstallments: 6, + types: { + excluded: ['credit_card'], + }, + }, + visual: { + style: { + theme: 'bootstrap', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + + const result = checkOnlyAmountIsDifferent(obj1, obj2); + + expect(result).toBe(false); + }); + + test('should return false when the amount and other field(s) are different between two objects', () => { + const obj1: TCardPayment = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + types: { + excluded: ['debit_card'], + }, + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + const obj2: TCardPayment = { + locale: 'en-US', + customization: { + paymentMethods: { + maxInstallments: 6, + types: { + excluded: ['credit_card'], + }, + }, + visual: { + style: { + theme: 'bootstrap', + }, + }, + }, + initialization: { + amount: 90, + }, + onSubmit: async () => { + return; + }, + }; + + const result = checkOnlyAmountIsDifferent(obj1, obj2); + + expect(result).toBe(false); + }); +}); diff --git a/src/bricks/util/amount/amount.ts b/src/bricks/util/amount/amount.ts new file mode 100644 index 0000000..5210932 --- /dev/null +++ b/src/bricks/util/amount/amount.ts @@ -0,0 +1,25 @@ +import { TCardPayment } from '../../cardPayment/type'; +import { TPaymentType } from '../../payment/type'; +import { deepCopy } from '../deepCopy'; + +export const checkOnlyAmountIsDifferent = ( + obj1: TCardPayment | TPaymentType, + obj2: TCardPayment | TPaymentType, +): boolean => { + const obj1WithoutAmount: { + initialization: { amount?: number }; + } = deepCopy(obj1); + const obj2WithoutAmount: { + initialization: { amount?: number }; + } = deepCopy(obj2); + + delete obj1WithoutAmount.initialization.amount; + delete obj2WithoutAmount.initialization.amount; + + if (JSON.stringify(obj1WithoutAmount) === JSON.stringify(obj2WithoutAmount)) { + if (obj1.initialization.amount !== obj2.initialization.amount) { + return true; + } + } + return false; +}; diff --git a/src/bricks/util/deepCopy.ts b/src/bricks/util/deepCopy.ts new file mode 100644 index 0000000..e184f34 --- /dev/null +++ b/src/bricks/util/deepCopy.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +export function deepCopy(value: T): T { + if (typeof value !== 'object' || value === null) { + return value; + } + if (Array.isArray(value)) { + return deepArray(value); + } + return deepObject(value); +} + +function deepObject(source: T) { + const result = {} as T; + Object.keys(source).forEach((key) => { + const value = source[key as keyof T]; + result[key as keyof T] = deepCopy(value); + }, {}); + return result as T; +} + +function deepArray(collection: T): any { + return collection.map((value) => { + return deepCopy(value); + }); +} From 4ee75c3a292bea07b280318916cbb9f143973ca3 Mon Sep 17 00:00:00 2001 From: andreagostinho-meli Date: Tue, 25 Jul 2023 18:18:31 -0300 Subject: [PATCH 2/9] enhancement: add build to start script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ff7c610..af9818e 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "Checkout" ], "scripts": { - "start": "start-storybook -p 6006", + "start": "npm run build && start-storybook -p 6006", "build": "rm -rf ./dist && tsc -b ./tsconfig.prod.json && cp package.json ./dist && cp LICENSE ./dist && cp README.md ./dist", "test": "jest --c ./jest.config.ts" }, From acb283233860452a1e71b19a26bb6b459ef09ac8 Mon Sep 17 00:00:00 2001 From: andreagostinho-meli Date: Tue, 25 Jul 2023 18:24:59 -0300 Subject: [PATCH 3/9] feat: update amount for Card Payment Brick --- .../bricks/CardPayment/2-UpdateAmount.tsx | 31 +++++++++++++++++++ src/@types/global.d.ts | 3 +- src/bricks/cardPayment/index.tsx | 26 ++++++++++++++-- 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 examples/bricks/CardPayment/2-UpdateAmount.tsx diff --git a/examples/bricks/CardPayment/2-UpdateAmount.tsx b/examples/bricks/CardPayment/2-UpdateAmount.tsx new file mode 100644 index 0000000..c396f9b --- /dev/null +++ b/examples/bricks/CardPayment/2-UpdateAmount.tsx @@ -0,0 +1,31 @@ +import React, { useState } from 'react'; +import Card from '../../../src/bricks/cardPayment'; + +import initMercadoPago from '../../../src/mercadoPago/initMercadoPago'; + +initMercadoPago('TEST-f4563544-ce69-40c3-b88e-6e7d1bd93a83', { locale: 'pt-BR' }); + +const App = () => { + const initialAmount = 100; + const [amount, setAmount] = useState(initialAmount); + const updateAmount = () => { + setAmount(90); + }; + + return ( + <> + + + { + console.log(param); + }} + /> + + ); +}; + +export default App; diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index ae10997..3ce3beb 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -1,4 +1,4 @@ -import { Field } from "../secureFields/util/types"; +import { Field } from '../secureFields/util/types'; export {}; @@ -11,6 +11,7 @@ declare global { cardPaymentBrickController: { unmount: () => void; getFormData: () => Promise; + update: (updateValues: { amount: number }) => boolean; }; walletBrickController: { unmount: () => void; diff --git a/src/bricks/cardPayment/index.tsx b/src/bricks/cardPayment/index.tsx index cbecb59..f4cd46e 100644 --- a/src/bricks/cardPayment/index.tsx +++ b/src/bricks/cardPayment/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { memo, useEffect } from 'react'; import { DEBOUNCE_TIME_RENDER } from '../util/constants'; import { onErrorDefault, @@ -8,6 +8,7 @@ import { } from '../util/initial'; import { initBrick } from '../util/renderBrick'; import { TCardPayment } from './type'; +import { checkOnlyAmountIsDifferent } from '../util/amount/amount'; /** * Card Payment Brick allows you to offer payments with credit and debit card at yout checkout. @@ -74,4 +75,25 @@ const CardPayment = ({ return
; }; -export default CardPayment; +const memoizedCardPayment = memo( + CardPayment, + (prevProps: TCardPayment, nextProps: TCardPayment) => { + if (JSON.stringify(prevProps) !== JSON.stringify(nextProps)) { + const result = checkOnlyAmountIsDifferent(prevProps, nextProps); + if (result) { + if (window.cardPaymentBrickController) { + window.cardPaymentBrickController.update({ amount: nextProps.initialization.amount }); + } else { + console.warn( + '[Checkout Bricks] Card Payment Brick is not initialized yet, please try again after a few seconds.', + ); + } + return true; + } + return false; + } + return true; + }, +); + +export default memoizedCardPayment; From 9a7a6046c521d43fe7bf99f60dfe322b833f995b Mon Sep 17 00:00:00 2001 From: andreagostinho-meli Date: Tue, 25 Jul 2023 18:51:25 -0300 Subject: [PATCH 4/9] feat: update amount for Payment Brick --- examples/bricks/Payment/2-UpdateAmount.tsx | 48 +++++ src/@types/global.d.ts | 1 + src/bricks/payment/index.tsx | 25 ++- src/bricks/util/amount/amount.test.ts | 197 +++++++++++++++++++++ 4 files changed, 268 insertions(+), 3 deletions(-) create mode 100644 examples/bricks/Payment/2-UpdateAmount.tsx diff --git a/examples/bricks/Payment/2-UpdateAmount.tsx b/examples/bricks/Payment/2-UpdateAmount.tsx new file mode 100644 index 0000000..fbc7a78 --- /dev/null +++ b/examples/bricks/Payment/2-UpdateAmount.tsx @@ -0,0 +1,48 @@ +import React, { useState } from 'react'; +import Payment from '../../../src/bricks/payment'; + +import initMercadoPago from '../../../src/mercadoPago/initMercadoPago'; + +initMercadoPago('TEST-f4563544-ce69-40c3-b88e-6e7d1bd93a83', { locale: 'pt-BR' }); + +const App = () => { + const initialAmount = 100; + const [amount, setAmount] = useState(initialAmount); + const updateAmount = () => { + setAmount(90); + }; + + const initialization = { + amount, + preferenceId: '207446753-ea3adb2e-a4f2-41dd-a656-11cb01b8772c', + }; + + const customization = { + paymentMethods: { + atm: 'all', + ticket: 'all', + bankTransfer: ['pix'], + creditCard: 'all', + debitCard: 'all', + mercadoPago: 'all', + }, + }; + + return ( + <> + + + { + console.log(param); + }} + /> + + ); +}; + +export default App; diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 3ce3beb..b296437 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -7,6 +7,7 @@ declare global { MercadoPago: any; paymentBrickController: { unmount: () => void; + update: (updateValues: { amount: number }) => boolean; }; cardPaymentBrickController: { unmount: () => void; diff --git a/src/bricks/payment/index.tsx b/src/bricks/payment/index.tsx index 360a0a6..6567500 100644 --- a/src/bricks/payment/index.tsx +++ b/src/bricks/payment/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { memo, useEffect } from 'react'; import { DEBOUNCE_TIME_RENDER } from '../util/constants'; import { onBinChangeDefault, @@ -8,6 +8,7 @@ import { } from '../util/initial'; import { initBrick } from '../util/renderBrick'; import { TPaymentType } from './type'; +import { checkOnlyAmountIsDifferent } from '../util/amount/amount'; /** * Payment Brick allows you to add several payment methods to a store and save card data for future purchases with just one Brick. @@ -32,7 +33,7 @@ import { TPaymentType } from './type'; * * @tutorial {@link https://www.mercadopago.com/developers/en/docs/checkout-bricks/payment-brick/introduction Payment Brick documentation} for more information. */ -const BrickPayment = ({ +const PaymentBrick = ({ onReady = onReadyDefault, onError = onErrorDefault, onSubmit = onSubmitDefault, @@ -73,4 +74,22 @@ const BrickPayment = ({ return
; }; -export default BrickPayment; +const memoizedPayment = memo(PaymentBrick, (prevProps: TPaymentType, nextProps: TPaymentType) => { + if (JSON.stringify(prevProps) !== JSON.stringify(nextProps)) { + const result = checkOnlyAmountIsDifferent(prevProps, nextProps); + if (result) { + if (window.paymentBrickController) { + window.paymentBrickController.update({ amount: nextProps.initialization.amount }); + } else { + console.warn( + '[Checkout Bricks] Payment Brick is not initialized yet, please try again after a few seconds.', + ); + } + return true; + } + return false; + } + return true; +}); + +export default memoizedPayment; diff --git a/src/bricks/util/amount/amount.test.ts b/src/bricks/util/amount/amount.test.ts index 5c59e8d..7ca3f32 100644 --- a/src/bricks/util/amount/amount.test.ts +++ b/src/bricks/util/amount/amount.test.ts @@ -1,4 +1,5 @@ import { TCardPayment } from '../../cardPayment/type'; +import { TPaymentType } from '../../payment/type'; import { checkOnlyAmountIsDifferent } from './amount'; describe('Amount - Card Payment Brick', () => { @@ -206,3 +207,199 @@ describe('Amount - Card Payment Brick', () => { expect(result).toBe(false); }); }); + +describe('Amount - Payment Brick', () => { + test('should return true when only the amount is different between two objects', () => { + const obj1: TPaymentType = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + creditCard: 'all', + debitCard: 'all', + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + const obj2: TPaymentType = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + creditCard: 'all', + debitCard: 'all', + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 90, + }, + onSubmit: async () => { + return; + }, + }; + + const result = checkOnlyAmountIsDifferent(obj1, obj2); + + expect(result).toBe(true); + }); + + test('should return false when the amount is equal between two objects and other fields are equal', () => { + const obj1: TPaymentType = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + creditCard: 'all', + debitCard: 'all', + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + const obj2: TPaymentType = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + creditCard: 'all', + debitCard: 'all', + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + + const result = checkOnlyAmountIsDifferent(obj1, obj2); + + expect(result).toBe(false); + }); + + test('should return false when the amount is equal between two objects and other fields are different', () => { + const obj1: TPaymentType = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + creditCard: ['visa', 'master'], + debitCard: 'all', + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + const obj2: TPaymentType = { + locale: 'en-US', + customization: { + paymentMethods: { + maxInstallments: 6, + creditCard: ['visa'], + }, + visual: { + style: { + theme: 'bootstrap', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + + const result = checkOnlyAmountIsDifferent(obj1, obj2); + + expect(result).toBe(false); + }); + + test('should return false when the amount and other field(s) are different between two objects', () => { + const obj1: TPaymentType = { + locale: 'pt-BR', + customization: { + paymentMethods: { + maxInstallments: 3, + creditCard: ['visa', 'master'], + debitCard: 'all', + }, + visual: { + style: { + theme: 'dark', + }, + }, + }, + initialization: { + amount: 100, + }, + onSubmit: async () => { + return; + }, + }; + const obj2: TPaymentType = { + locale: 'en-US', + customization: { + paymentMethods: { + maxInstallments: 6, + creditCard: ['visa'], + }, + visual: { + style: { + theme: 'bootstrap', + }, + }, + }, + initialization: { + amount: 90, + }, + onSubmit: async () => { + return; + }, + }; + + const result = checkOnlyAmountIsDifferent(obj1, obj2); + + expect(result).toBe(false); + }); +}); From a7a38e1e7626204ebc9e23752ac6819ea7291102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= Date: Wed, 26 Jul 2023 11:19:23 -0300 Subject: [PATCH 5/9] enhancement: use == in deepCopy Co-authored-by: Ismael Caldana --- src/bricks/util/deepCopy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bricks/util/deepCopy.ts b/src/bricks/util/deepCopy.ts index e184f34..c077979 100644 --- a/src/bricks/util/deepCopy.ts +++ b/src/bricks/util/deepCopy.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ export function deepCopy(value: T): T { - if (typeof value !== 'object' || value === null) { + if (typeof value !== 'object' || value == null) { return value; } if (Array.isArray(value)) { From eb267b447d6e086308853e104e536bc550b53edd Mon Sep 17 00:00:00 2001 From: andreagostinho-meli Date: Wed, 26 Jul 2023 18:40:36 -0300 Subject: [PATCH 6/9] refactor: create custom hook to update amount --- .../bricks/CardPayment/2-UpdateAmount.tsx | 14 +- examples/bricks/Payment/2-UpdateAmount.tsx | 14 +- src/@types/global.d.ts | 5 +- src/bricks/cardPayment/index.tsx | 37 +- src/bricks/payment/index.tsx | 38 +- src/bricks/util/amount/amount.test.ts | 405 ------------------ src/bricks/util/amount/amount.ts | 25 -- src/bricks/util/types/common.ts | 4 + 8 files changed, 49 insertions(+), 493 deletions(-) delete mode 100644 src/bricks/util/amount/amount.test.ts delete mode 100644 src/bricks/util/amount/amount.ts diff --git a/examples/bricks/CardPayment/2-UpdateAmount.tsx b/examples/bricks/CardPayment/2-UpdateAmount.tsx index c396f9b..0fe157c 100644 --- a/examples/bricks/CardPayment/2-UpdateAmount.tsx +++ b/examples/bricks/CardPayment/2-UpdateAmount.tsx @@ -1,25 +1,21 @@ -import React, { useState } from 'react'; -import Card from '../../../src/bricks/cardPayment'; +import React from 'react'; +import Card, { useCardPaymentBrick } from '../../../src/bricks/cardPayment'; import initMercadoPago from '../../../src/mercadoPago/initMercadoPago'; initMercadoPago('TEST-f4563544-ce69-40c3-b88e-6e7d1bd93a83', { locale: 'pt-BR' }); const App = () => { - const initialAmount = 100; - const [amount, setAmount] = useState(initialAmount); - const updateAmount = () => { - setAmount(90); - }; + const { update } = useCardPaymentBrick(); return ( <> - { console.log(param); }} diff --git a/examples/bricks/Payment/2-UpdateAmount.tsx b/examples/bricks/Payment/2-UpdateAmount.tsx index fbc7a78..e20ef5d 100644 --- a/examples/bricks/Payment/2-UpdateAmount.tsx +++ b/examples/bricks/Payment/2-UpdateAmount.tsx @@ -1,19 +1,15 @@ -import React, { useState } from 'react'; -import Payment from '../../../src/bricks/payment'; +import React from 'react'; +import Payment, { usePaymentBrick } from '../../../src/bricks/payment'; import initMercadoPago from '../../../src/mercadoPago/initMercadoPago'; initMercadoPago('TEST-f4563544-ce69-40c3-b88e-6e7d1bd93a83', { locale: 'pt-BR' }); const App = () => { - const initialAmount = 100; - const [amount, setAmount] = useState(initialAmount); - const updateAmount = () => { - setAmount(90); - }; + const { update } = usePaymentBrick(); const initialization = { - amount, + amount: 100, preferenceId: '207446753-ea3adb2e-a4f2-41dd-a656-11cb01b8772c', }; @@ -30,7 +26,7 @@ const App = () => { return ( <> - diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index b296437..48a92a2 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -1,3 +1,4 @@ +import { UpdateValues } from '../bricks/util/types/common'; import { Field } from '../secureFields/util/types'; export {}; @@ -7,12 +8,12 @@ declare global { MercadoPago: any; paymentBrickController: { unmount: () => void; - update: (updateValues: { amount: number }) => boolean; + update: (updateValues: UpdateValues) => boolean; }; cardPaymentBrickController: { unmount: () => void; getFormData: () => Promise; - update: (updateValues: { amount: number }) => boolean; + update: (updateValues: UpdateValues) => boolean; }; walletBrickController: { unmount: () => void; diff --git a/src/bricks/cardPayment/index.tsx b/src/bricks/cardPayment/index.tsx index f4cd46e..48fe693 100644 --- a/src/bricks/cardPayment/index.tsx +++ b/src/bricks/cardPayment/index.tsx @@ -1,4 +1,4 @@ -import React, { memo, useEffect } from 'react'; +import React, { useEffect } from 'react'; import { DEBOUNCE_TIME_RENDER } from '../util/constants'; import { onErrorDefault, @@ -8,7 +8,7 @@ import { } from '../util/initial'; import { initBrick } from '../util/renderBrick'; import { TCardPayment } from './type'; -import { checkOnlyAmountIsDifferent } from '../util/amount/amount'; +import { UpdateValues } from '../util/types/common'; /** * Card Payment Brick allows you to offer payments with credit and debit card at yout checkout. @@ -75,25 +75,18 @@ const CardPayment = ({ return
; }; -const memoizedCardPayment = memo( - CardPayment, - (prevProps: TCardPayment, nextProps: TCardPayment) => { - if (JSON.stringify(prevProps) !== JSON.stringify(nextProps)) { - const result = checkOnlyAmountIsDifferent(prevProps, nextProps); - if (result) { - if (window.cardPaymentBrickController) { - window.cardPaymentBrickController.update({ amount: nextProps.initialization.amount }); - } else { - console.warn( - '[Checkout Bricks] Card Payment Brick is not initialized yet, please try again after a few seconds.', - ); - } - return true; - } - return false; +const useCardPaymentBrick = () => { + const update = (data: UpdateValues) => { + if (window.cardPaymentBrickController) { + window.cardPaymentBrickController.update({ amount: data.amount }); + } else { + console.warn( + '[Checkout Bricks] Card Payment Brick is not initialized yet, please try again after a few seconds.', + ); } - return true; - }, -); + }; + return { update }; +}; -export default memoizedCardPayment; +export default CardPayment; +export { useCardPaymentBrick }; diff --git a/src/bricks/payment/index.tsx b/src/bricks/payment/index.tsx index 6567500..ae9ca51 100644 --- a/src/bricks/payment/index.tsx +++ b/src/bricks/payment/index.tsx @@ -1,4 +1,4 @@ -import React, { memo, useEffect } from 'react'; +import React, { useEffect } from 'react'; import { DEBOUNCE_TIME_RENDER } from '../util/constants'; import { onBinChangeDefault, @@ -8,7 +8,7 @@ import { } from '../util/initial'; import { initBrick } from '../util/renderBrick'; import { TPaymentType } from './type'; -import { checkOnlyAmountIsDifferent } from '../util/amount/amount'; +import { UpdateValues } from '../util/types/common'; /** * Payment Brick allows you to add several payment methods to a store and save card data for future purchases with just one Brick. @@ -33,7 +33,7 @@ import { checkOnlyAmountIsDifferent } from '../util/amount/amount'; * * @tutorial {@link https://www.mercadopago.com/developers/en/docs/checkout-bricks/payment-brick/introduction Payment Brick documentation} for more information. */ -const PaymentBrick = ({ +const Payment = ({ onReady = onReadyDefault, onError = onErrorDefault, onSubmit = onSubmitDefault, @@ -43,7 +43,7 @@ const PaymentBrick = ({ locale, }: TPaymentType) => { useEffect(() => { - // CardPayment uses a debounce to prevent unnecessary reRenders. + // Payment uses a debounce to prevent unnecessary reRenders. let timer: ReturnType; const PaymentBrickController = { settings: { @@ -74,22 +74,18 @@ const PaymentBrick = ({ return
; }; -const memoizedPayment = memo(PaymentBrick, (prevProps: TPaymentType, nextProps: TPaymentType) => { - if (JSON.stringify(prevProps) !== JSON.stringify(nextProps)) { - const result = checkOnlyAmountIsDifferent(prevProps, nextProps); - if (result) { - if (window.paymentBrickController) { - window.paymentBrickController.update({ amount: nextProps.initialization.amount }); - } else { - console.warn( - '[Checkout Bricks] Payment Brick is not initialized yet, please try again after a few seconds.', - ); - } - return true; +const usePaymentBrick = () => { + const update = (data: UpdateValues) => { + if (window.paymentBrickController) { + window.paymentBrickController.update({ amount: data.amount }); + } else { + console.warn( + '[Checkout Bricks] Payment Brick is not initialized yet, please try again after a few seconds.', + ); } - return false; - } - return true; -}); + }; + return { update }; +}; -export default memoizedPayment; +export default Payment; +export { usePaymentBrick }; diff --git a/src/bricks/util/amount/amount.test.ts b/src/bricks/util/amount/amount.test.ts deleted file mode 100644 index 7ca3f32..0000000 --- a/src/bricks/util/amount/amount.test.ts +++ /dev/null @@ -1,405 +0,0 @@ -import { TCardPayment } from '../../cardPayment/type'; -import { TPaymentType } from '../../payment/type'; -import { checkOnlyAmountIsDifferent } from './amount'; - -describe('Amount - Card Payment Brick', () => { - test('should return true when only the amount is different between two objects', () => { - const obj1: TCardPayment = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - types: { - excluded: ['debit_card'], - }, - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - const obj2: TCardPayment = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - types: { - excluded: ['debit_card'], - }, - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 90, - }, - onSubmit: async () => { - return; - }, - }; - - const result = checkOnlyAmountIsDifferent(obj1, obj2); - - expect(result).toBe(true); - }); - - test('should return false when the amount is equal between two objects and other fields are equal', () => { - const obj1: TCardPayment = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - types: { - excluded: ['debit_card'], - }, - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - const obj2: TCardPayment = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - types: { - excluded: ['debit_card'], - }, - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - - const result = checkOnlyAmountIsDifferent(obj1, obj2); - - expect(result).toBe(false); - }); - - test('should return false when the amount is equal between two objects and other fields are different', () => { - const obj1: TCardPayment = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - types: { - excluded: ['debit_card'], - }, - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - const obj2: TCardPayment = { - locale: 'en-US', - customization: { - paymentMethods: { - maxInstallments: 6, - types: { - excluded: ['credit_card'], - }, - }, - visual: { - style: { - theme: 'bootstrap', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - - const result = checkOnlyAmountIsDifferent(obj1, obj2); - - expect(result).toBe(false); - }); - - test('should return false when the amount and other field(s) are different between two objects', () => { - const obj1: TCardPayment = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - types: { - excluded: ['debit_card'], - }, - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - const obj2: TCardPayment = { - locale: 'en-US', - customization: { - paymentMethods: { - maxInstallments: 6, - types: { - excluded: ['credit_card'], - }, - }, - visual: { - style: { - theme: 'bootstrap', - }, - }, - }, - initialization: { - amount: 90, - }, - onSubmit: async () => { - return; - }, - }; - - const result = checkOnlyAmountIsDifferent(obj1, obj2); - - expect(result).toBe(false); - }); -}); - -describe('Amount - Payment Brick', () => { - test('should return true when only the amount is different between two objects', () => { - const obj1: TPaymentType = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - creditCard: 'all', - debitCard: 'all', - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - const obj2: TPaymentType = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - creditCard: 'all', - debitCard: 'all', - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 90, - }, - onSubmit: async () => { - return; - }, - }; - - const result = checkOnlyAmountIsDifferent(obj1, obj2); - - expect(result).toBe(true); - }); - - test('should return false when the amount is equal between two objects and other fields are equal', () => { - const obj1: TPaymentType = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - creditCard: 'all', - debitCard: 'all', - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - const obj2: TPaymentType = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - creditCard: 'all', - debitCard: 'all', - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - - const result = checkOnlyAmountIsDifferent(obj1, obj2); - - expect(result).toBe(false); - }); - - test('should return false when the amount is equal between two objects and other fields are different', () => { - const obj1: TPaymentType = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - creditCard: ['visa', 'master'], - debitCard: 'all', - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - const obj2: TPaymentType = { - locale: 'en-US', - customization: { - paymentMethods: { - maxInstallments: 6, - creditCard: ['visa'], - }, - visual: { - style: { - theme: 'bootstrap', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - - const result = checkOnlyAmountIsDifferent(obj1, obj2); - - expect(result).toBe(false); - }); - - test('should return false when the amount and other field(s) are different between two objects', () => { - const obj1: TPaymentType = { - locale: 'pt-BR', - customization: { - paymentMethods: { - maxInstallments: 3, - creditCard: ['visa', 'master'], - debitCard: 'all', - }, - visual: { - style: { - theme: 'dark', - }, - }, - }, - initialization: { - amount: 100, - }, - onSubmit: async () => { - return; - }, - }; - const obj2: TPaymentType = { - locale: 'en-US', - customization: { - paymentMethods: { - maxInstallments: 6, - creditCard: ['visa'], - }, - visual: { - style: { - theme: 'bootstrap', - }, - }, - }, - initialization: { - amount: 90, - }, - onSubmit: async () => { - return; - }, - }; - - const result = checkOnlyAmountIsDifferent(obj1, obj2); - - expect(result).toBe(false); - }); -}); diff --git a/src/bricks/util/amount/amount.ts b/src/bricks/util/amount/amount.ts deleted file mode 100644 index 5210932..0000000 --- a/src/bricks/util/amount/amount.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { TCardPayment } from '../../cardPayment/type'; -import { TPaymentType } from '../../payment/type'; -import { deepCopy } from '../deepCopy'; - -export const checkOnlyAmountIsDifferent = ( - obj1: TCardPayment | TPaymentType, - obj2: TCardPayment | TPaymentType, -): boolean => { - const obj1WithoutAmount: { - initialization: { amount?: number }; - } = deepCopy(obj1); - const obj2WithoutAmount: { - initialization: { amount?: number }; - } = deepCopy(obj2); - - delete obj1WithoutAmount.initialization.amount; - delete obj2WithoutAmount.initialization.amount; - - if (JSON.stringify(obj1WithoutAmount) === JSON.stringify(obj2WithoutAmount)) { - if (obj1.initialization.amount !== obj2.initialization.amount) { - return true; - } - } - return false; -}; diff --git a/src/bricks/util/types/common.ts b/src/bricks/util/types/common.ts index 580be52..3cb9085 100644 --- a/src/bricks/util/types/common.ts +++ b/src/bricks/util/types/common.ts @@ -152,3 +152,7 @@ export interface IBrickCustomVariables { borderRadiusFull?: string; formPadding?: string; } + +export type UpdateValues = { + amount: number; +}; From 33387709675a969e96e0bdf2634963d945584213 Mon Sep 17 00:00:00 2001 From: andreagostinho-meli Date: Wed, 26 Jul 2023 18:48:44 -0300 Subject: [PATCH 7/9] enhancement: delete deepCopy util --- src/bricks/util/deepCopy.ts | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/bricks/util/deepCopy.ts diff --git a/src/bricks/util/deepCopy.ts b/src/bricks/util/deepCopy.ts deleted file mode 100644 index c077979..0000000 --- a/src/bricks/util/deepCopy.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -export function deepCopy(value: T): T { - if (typeof value !== 'object' || value == null) { - return value; - } - if (Array.isArray(value)) { - return deepArray(value); - } - return deepObject(value); -} - -function deepObject(source: T) { - const result = {} as T; - Object.keys(source).forEach((key) => { - const value = source[key as keyof T]; - result[key as keyof T] = deepCopy(value); - }, {}); - return result as T; -} - -function deepArray(collection: T): any { - return collection.map((value) => { - return deepCopy(value); - }); -} From f7015a7da5ca3c1cc5f31ba0eca743c7f29faff3 Mon Sep 17 00:00:00 2001 From: andreagostinho-meli Date: Thu, 27 Jul 2023 16:57:37 -0300 Subject: [PATCH 8/9] enhancement: pass update values to update method --- src/bricks/cardPayment/index.tsx | 4 ++-- src/bricks/payment/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bricks/cardPayment/index.tsx b/src/bricks/cardPayment/index.tsx index 48fe693..ad3a54b 100644 --- a/src/bricks/cardPayment/index.tsx +++ b/src/bricks/cardPayment/index.tsx @@ -76,9 +76,9 @@ const CardPayment = ({ }; const useCardPaymentBrick = () => { - const update = (data: UpdateValues) => { + const update = (updateValues: UpdateValues) => { if (window.cardPaymentBrickController) { - window.cardPaymentBrickController.update({ amount: data.amount }); + window.cardPaymentBrickController.update(updateValues); } else { console.warn( '[Checkout Bricks] Card Payment Brick is not initialized yet, please try again after a few seconds.', diff --git a/src/bricks/payment/index.tsx b/src/bricks/payment/index.tsx index ae9ca51..b110c71 100644 --- a/src/bricks/payment/index.tsx +++ b/src/bricks/payment/index.tsx @@ -75,9 +75,9 @@ const Payment = ({ }; const usePaymentBrick = () => { - const update = (data: UpdateValues) => { + const update = (updateValues: UpdateValues) => { if (window.paymentBrickController) { - window.paymentBrickController.update({ amount: data.amount }); + window.paymentBrickController.update(updateValues); } else { console.warn( '[Checkout Bricks] Payment Brick is not initialized yet, please try again after a few seconds.', From 18ffbb55eaa3d8bb5adedc1eec01116d0127604d Mon Sep 17 00:00:00 2001 From: andreagostinho-meli Date: Wed, 16 Aug 2023 17:03:28 -0300 Subject: [PATCH 9/9] docs: update amount documentation --- package.json | 2 +- src/@types/global.d.ts | 10 ++++++++++ src/bricks/util/types/common.ts | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e6bf44c..83f41b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mercadopago/sdk-react", - "version": "0.0.13", + "version": "0.0.14", "description": "Mercado Pago SDK React", "main": "index.js", "keywords": [ diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 48a92a2..7d85848 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -8,11 +8,21 @@ declare global { MercadoPago: any; paymentBrickController: { unmount: () => void; + /** + * Updates data in Payment Brick preserving the current instance. + * + * @see {@link https://www.mercadopago.com/developers/en/docs/checkout-bricks/payment-brick/additional-customization/update-data Payment Brick # Default rendering} documentation. + */ update: (updateValues: UpdateValues) => boolean; }; cardPaymentBrickController: { unmount: () => void; getFormData: () => Promise; + /** + * Updates data in Card Payment Brick preserving the current instance. + * + * @see {@link https://www.mercadopago.com/developers/en/docs/checkout-bricks/card-payment-brick/additional-customization/update-data Payment Brick # Default rendering} documentation. + */ update: (updateValues: UpdateValues) => boolean; }; walletBrickController: { diff --git a/src/bricks/util/types/common.ts b/src/bricks/util/types/common.ts index 3cb9085..637859b 100644 --- a/src/bricks/util/types/common.ts +++ b/src/bricks/util/types/common.ts @@ -153,6 +153,9 @@ export interface IBrickCustomVariables { formPadding?: string; } +/** + * Available update values. + */ export type UpdateValues = { amount: number; };