From 4627af313c7d251666f10623806d713e501186d1 Mon Sep 17 00:00:00 2001 From: raushan-skyflow Date: Thu, 10 Jul 2025 12:11:40 +0530 Subject: [PATCH 1/2] SK-2173 support input formatting in reveal elements --- .../__snapshots__/components.test.js.snap | 20 +-- __tests__/components/components.test.js | 128 +++++++++++++----- src/components/RevealElement/index.tsx | 14 +- src/utils/constants/index.ts | 2 + 4 files changed, 116 insertions(+), 48 deletions(-) diff --git a/__tests__/components/__snapshots__/components.test.js.snap b/__tests__/components/__snapshots__/components.test.js.snap index 53f6bc9..792f7d3 100644 --- a/__tests__/components/__snapshots__/components.test.js.snap +++ b/__tests__/components/__snapshots__/components.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`test Collect And Reveal Elements Components test CardHolderNameElement component 1`] = ` +exports[`test Collect Elements Components test CardHolderNameElement component 1`] = ` `; -exports[`test Collect And Reveal Elements Components test CardNumberElement component 1`] = ` +exports[`test Collect Elements Components test CardNumberElement component 1`] = ` `; -exports[`test Collect And Reveal Elements Components test CvvElement component 1`] = ` +exports[`test Collect Elements Components test CvvElement component 1`] = ` `; -exports[`test Collect And Reveal Elements Components test ExpirationDateElement component 1`] = ` +exports[`test Collect Elements Components test ExpirationDateElement component 1`] = ` `; -exports[`test Collect And Reveal Elements Components test ExpirationMonthElement component 1`] = ` +exports[`test Collect Elements Components test ExpirationMonthElement component 1`] = ` `; -exports[`test Collect And Reveal Elements Components test ExpirationYearElement component 1`] = ` +exports[`test Collect Elements Components test ExpirationYearElement component 1`] = ` `; -exports[`test Collect And Reveal Elements Components test InputFieldElement component 1`] = ` +exports[`test Collect Elements Components test InputFieldElement component 1`] = ` `; -exports[`test Collect And Reveal Elements Components test PinElement component 1`] = ` +exports[`test Collect Elements Components test PinElement component 1`] = ` `; -exports[`test Collect And Reveal Elements Components test RevealElement component 1`] = ` +exports[`test Collect Elements Components test Reveal Elements Components renders correctly with the handler & snapshot 1`] = ` Array [ Provider Childern diff --git a/__tests__/components/components.test.js b/__tests__/components/components.test.js index aa2fb49..f08a705 100644 --- a/__tests__/components/components.test.js +++ b/__tests__/components/components.test.js @@ -37,7 +37,7 @@ const changeTrigger = jest.fn(); const foucsTrigger = jest.fn(); const blurTrigger = jest.fn(); -describe('test Collect And Reveal Elements Components', () => { +describe('test Collect Elements Components', () => { let collectContainer; beforeEach(() => { jest.clearAllMocks(); @@ -558,44 +558,102 @@ describe('test Collect And Reveal Elements Components', () => { } }); - it('test RevealElement component', () => { - const revealSetMethodMock = jest.fn(); - const revealContainer = new RevealContainer(testSkyflowClient); - jest.spyOn(revealContainer, 'create').mockImplementation(() => ({ - setMethods: revealSetMethodMock, - })); + describe('test Reveal Elements Components', () => { + it('renders correctly with the handler & snapshot', () => { + const revealSetMethodMock = jest.fn(); + const revealContainer = new RevealContainer(testSkyflowClient); + jest.spyOn(revealContainer, 'create').mockImplementation(() => ({ + setMethods: revealSetMethodMock, + })); + + const revealElement = render( + + ); - const revealElement = render( - - ); + expect(revealElement).toMatchSnapshot(); + expect(revealSetMethodMock).toBeCalledTimes(1); - expect(revealElement).toMatchSnapshot(); - expect(revealSetMethodMock).toBeCalledTimes(1); + // render without alttext + const revealElement2 = render( + + ); + try { + render(); + } catch (err) { + expect(err).toEqual( + new SkyflowError( + SKYFLOW_ERROR_CODE.CONTAINER_OBJECT_IS_REQUIRED, + ['Reveal', 'useRevealContainer()'], + true + ) + ); + } + }); + + it('renders formatted value when format is provided', () => { + const revealSetMethodMock = jest.fn(); + const revealContainer = new RevealContainer(testSkyflowClient); + jest.spyOn(revealContainer, 'create').mockImplementation(() => ({ + setMethods: revealSetMethodMock, + })); + + const format = 'XXXX XXXX XXXX XXXX'; + const altText = '4111111111111111'; - // render without alttext - const revealElement2 = render( - - ); - try { - render(); - } catch (err) { - expect(err).toEqual( - new SkyflowError( - SKYFLOW_ERROR_CODE.CONTAINER_OBJECT_IS_REQUIRED, - ['Reveal', 'useRevealContainer()'], - true - ) + render( + ); - } + + // Should format altText using format + expect(screen.getByTestId('reveal-format').props.children).toBe( + '4111 1111 1111 1111' + ); + }); + + it('renders formatted value when format and translation are provided', () => { + const revealSetMethodMock = jest.fn(); + const revealContainer = new RevealContainer(testSkyflowClient); + jest.spyOn(revealContainer, 'create').mockImplementation(() => ({ + setMethods: revealSetMethodMock, + })); + + const format = 'XX-XX-XX-YYZ-ZZX'; + const translation = { X: '[0-9]', Y: '[A-Z]' }; + const altText = '123456AB7'; + + render( + + ); + + // Should format altText using format and translation + expect( + screen.getByTestId('reveal-format-translation').props.children + ).toBe('12-34-56-ABZ-ZZ7'); + }); }); it('test skyflow provider', () => { diff --git a/src/components/RevealElement/index.tsx b/src/components/RevealElement/index.tsx index 5aa885a..36bbbf6 100644 --- a/src/components/RevealElement/index.tsx +++ b/src/components/RevealElement/index.tsx @@ -7,13 +7,22 @@ import RevealSkyflowElement from "../../core/RevealSkyflowElement"; import { RevealElementProps } from "../../utils/constants" import SkyflowError from "../../utils/skyflow-error"; import SKYFLOW_ERROR_CODE from "../../utils/skyflow-error-code"; +import { formatInputFieldValue } from "../../utils/helpers"; +import { DEFAULT_INPUT_FIELD_TRANSLATION } from "../../core/constants"; -const RevealElement: React.FC = ({ container, label, ...rest }) => { +const RevealElement: React.FC = ({ container, label, format, translation, ...rest }) => { const [element, setElement] = React.useState(undefined); const [errorText, setErrorText] = React.useState(''); const [value, setValue] = React.useState(rest?.altText || rest.token); + const formattedValue = React.useMemo(() => { + if (!format) return value; + const valueTranslation = translation ?? DEFAULT_INPUT_FIELD_TRANSLATION; + const formattedText = formatInputFieldValue(value, format, valueTranslation); + return formattedText ? formattedText : value; + }, [value, format, translation]); + useEffect(() => { if (container) { const revealElement = container.create(rest); @@ -22,12 +31,11 @@ const RevealElement: React.FC = ({ container, label, ...rest } else { throw new SkyflowError(SKYFLOW_ERROR_CODE.CONTAINER_OBJECT_IS_REQUIRED, ['Reveal', 'useRevealContainer()'], true) } - }, []); return <> {label} - {value} + {formattedValue} {errorText} diff --git a/src/utils/constants/index.ts b/src/utils/constants/index.ts index ab507ad..e6f8786 100644 --- a/src/utils/constants/index.ts +++ b/src/utils/constants/index.ts @@ -109,6 +109,8 @@ export interface RevealElementInput { export interface RevealElementProps { token: string; container: RevealContainer; + format?: string + translation?: Record; label?: string; altText?: string; inputStyles?: StylesBaseVariant; From 6060e8aa9367d4750f9b9c036e76e2f45ccfde21 Mon Sep 17 00:00:00 2001 From: raushan-skyflow Date: Wed, 16 Jul 2025 15:38:00 +0530 Subject: [PATCH 2/2] SK-2173 fix the card number regex for proper bin lookup --- __tests__/core/collectElement.test.js | 12 ++++++++++++ __tests__/utils/helper.test.js | 10 ++++++++++ src/utils/helpers/index.ts | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/__tests__/core/collectElement.test.js b/__tests__/core/collectElement.test.js index 403b859..5f37845 100644 --- a/__tests__/core/collectElement.test.js +++ b/__tests__/core/collectElement.test.js @@ -536,6 +536,18 @@ describe('test Collect Element class', () => { expect(collectElement.getUnformattedValue()).toBe('4111111111111111'); }); + it('should remove hyphens from value', () => { + const elementInput = { + table: 'cards', + column: 'number', + type: ElementType.CARD_NUMBER, + containerType: ContainerType.COLLECT, + }; + const collectElement = new CollectElement(elementInput, {}, context); + collectElement.updateValue('4111-1111-1111-1111'); + expect(collectElement.getUnformattedValue()).toBe('4111111111111111'); + }); + it('test format option in card number element', () => { const formatTestCases = [ // Test different valid formats for the card number diff --git a/__tests__/utils/helper.test.js b/__tests__/utils/helper.test.js index 13b2ca4..708ad0f 100644 --- a/__tests__/utils/helper.test.js +++ b/__tests__/utils/helper.test.js @@ -219,6 +219,16 @@ describe('test getReturnValue function', () => { ).toBe('4111111111111111'); }); + it('should return unformatted value for card number element', () => { + expect( + getReturnValue('4111 1111 1111 1111', true, ElementType.CARD_NUMBER) + ).toBe('4111111111111111'); + + expect( + getReturnValue('4111-1111-1111-1111', true, ElementType.CARD_NUMBER) + ).toBe('4111111111111111'); + }); + it('should return non string value when return value is true', () => { expect(getReturnValue(1000, true, ElementType.INPUT_FIELD)).toBe(1000); }); diff --git a/src/utils/helpers/index.ts b/src/utils/helpers/index.ts index 4fe207c..face546 100644 --- a/src/utils/helpers/index.ts +++ b/src/utils/helpers/index.ts @@ -229,7 +229,7 @@ export const getReturnValue = ( ) => { if (typeof value === 'string') { if (elementType === ElementType.CARD_NUMBER) { - value = value && value.replace(/\s/g, ''); + value = value && value.replace(/[\s-]/g, ''); if (!doesReturnValue) { const threshold = cardType !== CardType.DEFAULT && cardType === CardType.AMEX ? 6 : 8;