From 9e2b983947df9c63ad018b6eea4ed86b7e815e4f Mon Sep 17 00:00:00 2001
From: skyflow-shravan <121150537+skyflow-shravan@users.noreply.github.com>
Date: Mon, 30 Jun 2025 22:55:32 +0530
Subject: [PATCH 01/23] SK-2126 insert unformatted card number to vault (#107)
* SK-2126 insert unformatted card number to vault
* SK-2126 change default collect error to Invalid value
* SK-2126 ability to pass testIDs for collect elements
* SK-2126 ability to pass testIDs for collect elements
---
.../__snapshots__/components.test.js.snap | 9 ++++++
__tests__/core-utils/collect.test.js | 30 ++++++++++++++++++-
__tests__/core/collectElement.test.js | 12 ++++++++
.../CardHolderNameElement/index.tsx | 3 +-
src/components/CardNumberElement/index.tsx | 3 +-
src/components/CvvElement/index.tsx | 3 +-
.../ExpirationDateElement/index.tsx | 3 +-
.../ExpirationMonthElement/index.tsx | 3 +-
.../ExpirationYearElement/index.tsx | 3 +-
src/components/InputFieldElement/index.tsx | 3 +-
src/components/PinElement/index.tsx | 3 +-
src/components/RevealElement/index.tsx | 4 +--
src/core-utils/collect/index.ts | 16 ++++++----
src/core/CollectElement/index.ts | 4 +++
src/core/constants/index.ts | 2 +-
src/utils/constants/index.ts | 2 ++
16 files changed, 86 insertions(+), 17 deletions(-)
diff --git a/__tests__/components/__snapshots__/components.test.js.snap b/__tests__/components/__snapshots__/components.test.js.snap
index ad718fd..53f6bc9 100644
--- a/__tests__/components/__snapshots__/components.test.js.snap
+++ b/__tests__/components/__snapshots__/components.test.js.snap
@@ -26,6 +26,7 @@ exports[`test Collect And Reveal Elements Components test CardHolderNameElement
/>
`;
@@ -89,6 +90,7 @@ exports[`test Collect And Reveal Elements Components test CardNumberElement comp
`;
@@ -121,6 +123,7 @@ exports[`test Collect And Reveal Elements Components test CvvElement component 1
/>
`;
@@ -153,6 +156,7 @@ exports[`test Collect And Reveal Elements Components test ExpirationDateElement
/>
`;
@@ -185,6 +189,7 @@ exports[`test Collect And Reveal Elements Components test ExpirationMonthElement
/>
`;
@@ -217,6 +222,7 @@ exports[`test Collect And Reveal Elements Components test ExpirationYearElement
/>
`;
@@ -247,6 +253,7 @@ exports[`test Collect And Reveal Elements Components test InputFieldElement comp
/>
`;
@@ -279,6 +286,7 @@ exports[`test Collect And Reveal Elements Components test PinElement component 1
/>
`;
@@ -298,6 +306,7 @@ Array [
,
,
]
`;
diff --git a/__tests__/core-utils/collect.test.js b/__tests__/core-utils/collect.test.js
index e223c7e..48eca10 100644
--- a/__tests__/core-utils/collect.test.js
+++ b/__tests__/core-utils/collect.test.js
@@ -1,7 +1,7 @@
/*
Copyright (c) 2022 Skyflow, Inc.
*/
-import { tokenize } from '../../src/core-utils/collect';
+import { tokenize, getElementValueToInsert } from '../../src/core-utils/collect';
import CollectElement from '../../src/core/CollectElement';
import Skyflow from '../../src/core/Skyflow';
import { ElementType, Env, LogLevel } from '../../src/utils/constants';
@@ -525,3 +525,31 @@ describe('test collect utils class', () => {
});
});
+
+describe('getVaultInsertValue', () => {
+ class MockCollectElement {
+ constructor(type, value) {
+ this.type = type;
+ this.value = value;
+ }
+ getClientState() {
+ return { elementType: this.type };
+ }
+ getUnformattedValue() {
+ return this.value.replace(/[\s-]/g, '');
+ }
+ getInternalState() {
+ return { value: this.value };
+ }
+ }
+
+ test('returns unformatted value for CARD_NUMBER', () => {
+ const element = new MockCollectElement(ElementType.CARD_NUMBER, '4111 1111 1111 1111');
+ expect(getElementValueToInsert(element)).toBe('4111111111111111');
+ });
+
+ test('returns value as is for non-CARD_NUMBER', () => {
+ const element = new MockCollectElement(ElementType.CVV, '123');
+ expect(getElementValueToInsert(element)).toBe('123');
+ });
+});
diff --git a/__tests__/core/collectElement.test.js b/__tests__/core/collectElement.test.js
index 185c480..34433e8 100644
--- a/__tests__/core/collectElement.test.js
+++ b/__tests__/core/collectElement.test.js
@@ -517,4 +517,16 @@ describe('test Collect Element class', () => {
cardNumberElement.onChangeElement('', true);
expect(cardNumberElement.getClientState().selectedCardScheme).toEqual('DISCOVER');
})
+
+ it('should remove spaces 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');
+ });
});
\ No newline at end of file
diff --git a/src/components/CardHolderNameElement/index.tsx b/src/components/CardHolderNameElement/index.tsx
index e9ec317..8bd1307 100644
--- a/src/components/CardHolderNameElement/index.tsx
+++ b/src/components/CardHolderNameElement/index.tsx
@@ -49,6 +49,7 @@ const CardHolderNameElement: React.FC = ({ container, optio
ref={textInputRef}
value={elementValue}
placeholder={rest.placeholder}
+ testID={rest?.testID}
onChangeText={(text) => {
element?.onChangeElement(text)
setElementValue(element.getInternalState().value)
@@ -75,7 +76,7 @@ const CardHolderNameElement: React.FC = ({ container, optio
{
container && container?.type === ContainerType.COLLECT
&&
- {errorText}
+ {errorText}
}
);
diff --git a/src/components/CardNumberElement/index.tsx b/src/components/CardNumberElement/index.tsx
index de3b8b3..0ce2ba4 100644
--- a/src/components/CardNumberElement/index.tsx
+++ b/src/components/CardNumberElement/index.tsx
@@ -109,6 +109,7 @@ const CardNumberElement: React.FC = ({ container, options,
ref={textInputRef}
value={elementValue}
placeholder={rest.placeholder}
+ testID={rest?.testID}
onChangeText={(text) => {
element?.onChangeElement(text)
setElementValue(element.getInternalState().value)
@@ -141,7 +142,7 @@ const CardNumberElement: React.FC = ({ container, options,
{
container && container?.type === ContainerType.COLLECT
&&
- {errorText}
+ {errorText}
}
);
}
diff --git a/src/components/CvvElement/index.tsx b/src/components/CvvElement/index.tsx
index dddbe8d..5754c7a 100644
--- a/src/components/CvvElement/index.tsx
+++ b/src/components/CvvElement/index.tsx
@@ -49,6 +49,7 @@ const CvvElement: React.FC = ({ container, options = { requ
ref={textInputRef}
value={elementValue}
placeholder={rest.placeholder}
+ testID={rest?.testID}
onChangeText={(text) => {
element?.onChangeElement(text);
setElementValue(element.getInternalState().value)
@@ -76,7 +77,7 @@ const CvvElement: React.FC = ({ container, options = { requ
{
container && container?.type === ContainerType.COLLECT
&&
- {errorText}
+ {errorText}
}
);
}
diff --git a/src/components/ExpirationDateElement/index.tsx b/src/components/ExpirationDateElement/index.tsx
index 04cd240..af9c071 100644
--- a/src/components/ExpirationDateElement/index.tsx
+++ b/src/components/ExpirationDateElement/index.tsx
@@ -54,6 +54,7 @@ const ExpirationDateElement: React.FC = ({ container, optio
ref={textInputRef}
value={elementValue}
placeholder={rest.placeholder}
+ testID={rest?.testID}
onChangeText={(text) => {
element?.onChangeElement(text);
setElementValue(element.getInternalState().value)
@@ -84,7 +85,7 @@ const ExpirationDateElement: React.FC = ({ container, optio
{
container && container?.type === ContainerType.COLLECT
&&
- {errorText}
+ {errorText}
}
);
}
diff --git a/src/components/ExpirationMonthElement/index.tsx b/src/components/ExpirationMonthElement/index.tsx
index 8092770..6e61fe7 100644
--- a/src/components/ExpirationMonthElement/index.tsx
+++ b/src/components/ExpirationMonthElement/index.tsx
@@ -49,6 +49,7 @@ const ExpirationMonthElement: React.FC = ({ container, opti
ref={textInputRef}
value={elementValue}
placeholder={rest.placeholder}
+ testID={rest?.testID}
onChangeText={(text) => {
element?.onChangeElement(text);
setElementValue(element.getInternalState().value)
@@ -79,7 +80,7 @@ const ExpirationMonthElement: React.FC = ({ container, opti
{
container && container?.type === ContainerType.COLLECT
&&
- {errorText}
+ {errorText}
}
);
}
diff --git a/src/components/ExpirationYearElement/index.tsx b/src/components/ExpirationYearElement/index.tsx
index c21439e..adba727 100644
--- a/src/components/ExpirationYearElement/index.tsx
+++ b/src/components/ExpirationYearElement/index.tsx
@@ -55,6 +55,7 @@ const ExpirationYearElement: React.FC = ({ container, optio
ref={textInputRef}
value={elementValue}
placeholder={rest.placeholder}
+ testID={rest?.testID}
onChangeText={(text) => {
element?.onChangeElement(text);
setElementValue(element.getInternalState().value)
@@ -85,7 +86,7 @@ const ExpirationYearElement: React.FC = ({ container, optio
{
container && container?.type === ContainerType.COLLECT
&&
- {errorText}
+ {errorText}
}
);
}
diff --git a/src/components/InputFieldElement/index.tsx b/src/components/InputFieldElement/index.tsx
index 0825173..35e85ae 100644
--- a/src/components/InputFieldElement/index.tsx
+++ b/src/components/InputFieldElement/index.tsx
@@ -51,6 +51,7 @@ const InputFieldElement: React.FC = ({ container, options =
ref={textInputRef}
value={elementValue}
placeholder={rest.placeholder}
+ testID={rest?.testID}
onChangeText={(text) => {
element?.onChangeElement(text);
setElementValue(element.getInternalState().value)
@@ -76,7 +77,7 @@ const InputFieldElement: React.FC = ({ container, options =
{
container && container?.type === ContainerType.COLLECT
&&
- {errorText}
+ {errorText}
}
);
}
diff --git a/src/components/PinElement/index.tsx b/src/components/PinElement/index.tsx
index 6bab3dc..79576a6 100644
--- a/src/components/PinElement/index.tsx
+++ b/src/components/PinElement/index.tsx
@@ -49,6 +49,7 @@ const PinElement: React.FC = ({ container, options = { requ
ref={textInputRef}
value={elementValue}
placeholder={rest.placeholder}
+ testID={rest?.testID}
onChangeText={(text) => {
element?.onChangeElement(text)
setElementValue(element.getInternalState().value)
@@ -76,7 +77,7 @@ const PinElement: React.FC = ({ container, options = { requ
{
container && container?.type === ContainerType.COLLECT
&&
- {errorText}
+ {errorText}
}
);
}
diff --git a/src/components/RevealElement/index.tsx b/src/components/RevealElement/index.tsx
index d08872a..5aa885a 100644
--- a/src/components/RevealElement/index.tsx
+++ b/src/components/RevealElement/index.tsx
@@ -27,8 +27,8 @@ const RevealElement: React.FC = ({ container, label, ...rest
return <>
{label}
- {value}
- {errorText}
+ {value}
+ {errorText}
>
}
diff --git a/src/core-utils/collect/index.ts b/src/core-utils/collect/index.ts
index 5d2cc3d..5280bfa 100644
--- a/src/core-utils/collect/index.ts
+++ b/src/core-utils/collect/index.ts
@@ -7,7 +7,7 @@ import _ from 'lodash';
import Skyflow from '../../core/Skyflow';
import SkyflowError from '../../utils/skyflow-error';
import SKYFLOW_ERROR_CODE from '../../utils/skyflow-error-code';
-import { IInsertRecord, IInsertResponse } from '../../../src/utils/constants';
+import { ElementType, IInsertRecord, IInsertResponse } from '../../../src/utils/constants';
import omit from 'lodash/omit';
const set = require('set-value');
@@ -184,6 +184,12 @@ export const constructElementsInsertReq = (req: any, options: any) => {
return { records };
};
+export function getElementValueToInsert(element: CollectElement) {
+ return element.getClientState().elementType === ElementType.CARD_NUMBER
+ ? element.getUnformattedValue()
+ : element.getInternalState().value;
+}
+
export const tokenize = (
skyflowClient: Skyflow,
elementList: CollectElement[],
@@ -223,14 +229,14 @@ export const tokenize = (
set(
elementsUpdateData[skyflowID],
column,
- currentElement.getInternalState().value,
+ getElementValueToInsert(currentElement),
);
} else {
elementsUpdateData[skyflowID] = {};
set(
elementsUpdateData[skyflowID],
column,
- currentElement.getInternalState().value,
+ getElementValueToInsert(currentElement),
);
set(
elementsUpdateData[skyflowID],
@@ -240,10 +246,10 @@ export const tokenize = (
}
}
else if (elementsData[table]) {
- set(elementsData[table], column, currentElement.getInternalState().value);
+ set(elementsData[table], column, getElementValueToInsert(currentElement));
} else {
elementsData[table] = {};
- set(elementsData[table], column, currentElement.getInternalState().value);
+ set(elementsData[table], column, getElementValueToInsert(currentElement));
}
});
diff --git a/src/core/CollectElement/index.ts b/src/core/CollectElement/index.ts
index 68f79bf..53c0405 100644
--- a/src/core/CollectElement/index.ts
+++ b/src/core/CollectElement/index.ts
@@ -136,6 +136,10 @@ class CollectElement extends SkyflowElement {
return this.#state;
}
+ getUnformattedValue() {
+ return this.#state.value.trim().replace(/[\s-]/g, '');
+ }
+
getElementInput() {
return this.#elementInput;
}
diff --git a/src/core/constants/index.ts b/src/core/constants/index.ts
index 0924bcd..111c708 100644
--- a/src/core/constants/index.ts
+++ b/src/core/constants/index.ts
@@ -163,7 +163,7 @@ export const DEFAULT_CARD_INPUT_MAX_LENGTH = 23;
export const REVEAL_ELEMENT_ERROR_TEXT = 'Invalid Token';
-export const DEFAULT_COLLECT_ELEMENT_ERROR_TEXT = 'Invalid Value';
+export const DEFAULT_COLLECT_ELEMENT_ERROR_TEXT = 'Invalid value';
export const DEFAULT_VALIDATION_ERROR_TEXT = 'Validation Failed';
diff --git a/src/utils/constants/index.ts b/src/utils/constants/index.ts
index 97bc0b5..ab507ad 100644
--- a/src/utils/constants/index.ts
+++ b/src/utils/constants/index.ts
@@ -55,6 +55,7 @@ export interface CollectElementProps {
errorTextStyles?: StylesBaseVariant;
containerMethods?: Record;
skyflowID?: string;
+ testID?: string;
}
export enum ElementType {
@@ -114,6 +115,7 @@ export interface RevealElementProps {
labelStyles?: StylesBaseVariant;
errorTextStyles?: StylesBaseVariant;
redaction?: RedactionType;
+ testID?: string;
}
export enum MessageType {
From dbbc6f12ddd1b7c7274a22e5c2f81f722630f82b Mon Sep 17 00:00:00 2001
From: skyflow-shravan
Date: Mon, 30 Jun 2025 17:26:29 +0000
Subject: [PATCH 02/23] [AUTOMATED] Private Release 1.9.0-dev.9e2b983
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 03d5000..ca10170 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "skyflow-react-native",
- "version": "1.9.0",
+ "version": "1.9.0-dev.9e2b983",
"description": "Skyflow React Native SDK",
"main": "lib/commonjs/index",
"module": "lib/module/index",
From dde97df3e00dfbf7338f272b53694e3686fa6fa7 Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Tue, 8 Jul 2025 16:20:40 +0530
Subject: [PATCH 03/23] SK-2131 fix expiry date validation (#109)
* SK-2131 fix expiry date validation
---
.../core-utils/element-validations.test.js | 36 +++++++++++++++++++
src/core-utils/element-validations/index.ts | 11 ++++--
2 files changed, 45 insertions(+), 2 deletions(-)
diff --git a/__tests__/core-utils/element-validations.test.js b/__tests__/core-utils/element-validations.test.js
index b9960f6..9f46068 100644
--- a/__tests__/core-utils/element-validations.test.js
+++ b/__tests__/core-utils/element-validations.test.js
@@ -8,6 +8,7 @@ import {
validatePin,
validateRevealElementRecords,
validateGetInput,
+ validateExpiryDate,
} from '../../src/core-utils/element-validations';
import SKYFLOW_ERROR_CODE from '../../src/utils/skyflow-error-code';
import { parameterizedString } from '../../src/utils/logs-helper';
@@ -186,6 +187,41 @@ describe('test validatePin function', () => {
});
});
+describe('test validateExpiryDate function with various format', () => {
+ it('should return true for current month/year', () => {
+ const today = new Date();
+ const month = (today.getMonth() + 1).toString().padStart(2, '0');
+ const year = today.getFullYear();
+ expect(validateExpiryDate(`${month}/${year}`, 'MM/YYYY')).toBe(true);
+ expect(validateExpiryDate(`${year}/${month}`, 'YYYY/MM')).toBe(true);
+
+ const yearTwoDigit = year % 100;
+ expect(validateExpiryDate(`${month}/${yearTwoDigit}`, 'MM/YY')).toBe(true);
+ expect(validateExpiryDate(`${yearTwoDigit}/${month}`, 'YY/MM')).toBe(true);
+ });
+
+ it('should return true for valid future date', () => {
+ expect(validateExpiryDate('07/2074', 'MM/YYYY')).toBe(true);
+ expect(validateExpiryDate('2074/04', 'YYYY/MM')).toBe(true);
+ expect(validateExpiryDate('01/35', 'MM/YY')).toBe(true);
+ expect(validateExpiryDate('35/04', 'YY/MM')).toBe(true);
+ });
+
+ it('should return false for expired dates', () => {
+ expect(validateExpiryDate('01/2020', 'MM/YYYY')).toBe(false);
+ expect(validateExpiryDate('2000/04', 'YYYY/MM')).toBe(false);
+ expect(validateExpiryDate('04/20', 'MM/YY')).toBe(false);
+ expect(validateExpiryDate('20/04', 'YY/MM')).toBe(false);
+ });
+
+ it('should return false for invalid month for expired date', () => {
+ expect(validateExpiryDate('15/2040', 'MM/YYYY')).toBe(false);
+ expect(validateExpiryDate('2040/16', 'YYYY/MM')).toBe(false);
+ expect(validateExpiryDate('14/20', 'MM/YY')).toBe(false);
+ expect(validateExpiryDate('40/14', 'YY/MM')).toBe(false);
+ });
+});
+
describe('test validateRevealElementRecords', () => {
it('should throw error when invalid redaction type is passed', (done) => {
try {
diff --git a/src/core-utils/element-validations/index.ts b/src/core-utils/element-validations/index.ts
index b9491bf..9f20042 100644
--- a/src/core-utils/element-validations/index.ts
+++ b/src/core-utils/element-validations/index.ts
@@ -58,13 +58,20 @@ export const validateExpiryDate = (date: string, format: string) => {
if (date.trim().length === 0) return true;
if (!date.includes('/')) return false;
const { month, year } = getYearAndMonthBasedOnFormat(date, format);
- const expiryDate = new Date(`${year}-${month}-01`);
+
+ if (month < 1 || month > 12) return false;
+
+ const expiryDate = new Date(Number(year), Number(month) - 1, 1);
const today = new Date();
+ today.setDate(1);
+ today.setHours(0, 0, 0, 0);
const maxDate = new Date();
maxDate.setFullYear(today.getFullYear() + 50);
+ maxDate.setDate(1);
+ maxDate.setHours(0, 0, 0, 0);
- return expiryDate > today && expiryDate <= maxDate;
+ return expiryDate >= today && expiryDate <= maxDate;
};
export const validateCreditCardNumber = (cardNumber: string) => {
From c040c47a4d01596c18cbbce2f48ad31b75accc91 Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Tue, 8 Jul 2025 10:51:38 +0000
Subject: [PATCH 04/23] [AUTOMATED] Private Release 1.9.0-dev.dde97df
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index ca10170..987a23b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "skyflow-react-native",
- "version": "1.9.0-dev.9e2b983",
+ "version": "1.9.0-dev.dde97df",
"description": "Skyflow React Native SDK",
"main": "lib/commonjs/index",
"module": "lib/module/index",
From 915c8c968ced0783d60bd865cf58cf743658622a Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Tue, 8 Jul 2025 16:54:59 +0530
Subject: [PATCH 05/23] SK-2157 custom input formatting for collect elements
(#108)
* SK-2157 custom input formatting for collect elements
* SK-2168 input formatting with combination of constants & dynamic input in InputFieldElement
---
__tests__/core/collectElement.test.js | 128 +++++++++++-
__tests__/utils/helper.test.js | 220 +++++++++++++++++++++
src/components/CardNumberElement/index.tsx | 6 +-
src/components/InputFieldElement/index.tsx | 5 +-
src/core/CollectElement/index.ts | 6 +-
src/core/constants/index.ts | 8 +
src/utils/helpers/index.ts | 108 +++++++++-
src/utils/logs/index.ts | 2 +
8 files changed, 472 insertions(+), 11 deletions(-)
diff --git a/__tests__/core/collectElement.test.js b/__tests__/core/collectElement.test.js
index 34433e8..403b859 100644
--- a/__tests__/core/collectElement.test.js
+++ b/__tests__/core/collectElement.test.js
@@ -257,7 +257,11 @@ describe('test Collect Element class', () => {
new SkyflowError(SKYFLOW_ERROR_CODE.INVALID_COLUMN_IN_COLLECT, [], true)
);
- collecteElement = new CollectElement({ table: 'cards', column: 'test', skyflowID: '' }, {}, context);
+ collecteElement = new CollectElement(
+ { table: 'cards', column: 'test', skyflowID: '' },
+ {},
+ context
+ );
expect(isValid).toThrow(
new SkyflowError(SKYFLOW_ERROR_CODE.EMPTY_SKYFLOW_ID_COLLECT, [], true)
);
@@ -515,8 +519,10 @@ describe('test Collect Element class', () => {
);
cardNumberElement.onDropdownSelect(CardType.DISCOVER);
cardNumberElement.onChangeElement('', true);
- expect(cardNumberElement.getClientState().selectedCardScheme).toEqual('DISCOVER');
- })
+ expect(cardNumberElement.getClientState().selectedCardScheme).toEqual(
+ 'DISCOVER'
+ );
+ });
it('should remove spaces from value', () => {
const elementInput = {
@@ -529,4 +535,118 @@ describe('test Collect Element class', () => {
collectElement.updateValue('4111 1111 1111 1111');
expect(collectElement.getUnformattedValue()).toBe('4111111111111111');
});
-});
\ No newline at end of file
+
+ it('test format option in card number element', () => {
+ const formatTestCases = [
+ // Test different valid formats for the card number
+ {
+ format: 'XXXX-XXXX-XXXX-XXXX',
+ input: '4111111111111111',
+ expected: '4111-1111-1111-1111',
+ },
+ {
+ format: 'XXXX XXXX XXXX XXXX',
+ input: '4111111111111111',
+ expected: '4111 1111 1111 1111',
+ },
+ // Test partial input
+ {
+ format: 'XXXX-XXXX-XXXX-XXXX',
+ input: '411111',
+ expected: '4111-11',
+ },
+ {
+ format: 'XXXX XXXX XXXX XXXX',
+ input: '411111',
+ expected: '4111 11',
+ },
+ // Test for invalid format, It will fallback to default format
+ {
+ format: ' xxxx-xxxx-xxxx ',
+ input: ' 4111111111111111 ',
+ expected: '4111 1111 1111 1111',
+ },
+ ];
+
+ formatTestCases.forEach((testCase) => {
+ const collectElement = new CollectElement(
+ {
+ table: 'cards',
+ column: 'card_number',
+ type: ElementType.CARD_NUMBER,
+ containerType: ContainerType.COLLECT,
+ },
+ {
+ format: testCase.format,
+ },
+ context
+ );
+
+ collectElement.onChangeElement(testCase.input);
+ expect(collectElement.getInternalState().value).toBe(testCase.expected);
+ });
+ });
+
+ it('test format option in input field element', () => {
+ const formatTestCases = [
+ {
+ format: 'XX-XX-XX',
+ input: '123456',
+ expected: '12-34-56',
+ },
+ {
+ format: 'XXX XXX',
+ input: '123456',
+ expected: '123 456',
+ },
+ {
+ format: 'XX/XX/XX',
+ input: '123456',
+ expected: '12/34/56',
+ },
+ // Test partial input
+ {
+ format: 'XX-XX-XX',
+ input: '1234',
+ expected: '12-34',
+ },
+ // Test with special characters
+ {
+ format: 'XX@XX#XX',
+ input: '123456',
+ expected: '12@34#56',
+ },
+ // Test input longer than format
+ {
+ format: 'XX-XX',
+ input: '123456',
+ expected: '12-34',
+ },
+ // Test input with leading, trailing spaces & small format
+ {
+ format: ' xx-xx-xxx ',
+ input: ' 1234567 ',
+ expected: ' xx-xx-xxx ',
+ },
+ ];
+
+ formatTestCases.forEach((testCase) => {
+ const collectElement = new CollectElement(
+ {
+ table: 'table',
+ column: 'column',
+ type: ElementType.INPUT_FIELD,
+ containerType: ContainerType.COLLECT,
+ },
+ {
+ format: testCase.format,
+ },
+ context
+ );
+
+ collectElement.onChangeElement(testCase.input);
+ expect(collectElement.getInternalState().value).toBe(testCase.expected);
+ expect(collectElement.getInternalState().isValid).toBe(true);
+ });
+ });
+});
diff --git a/__tests__/utils/helper.test.js b/__tests__/utils/helper.test.js
index c1a2a36..13b2ca4 100644
--- a/__tests__/utils/helper.test.js
+++ b/__tests__/utils/helper.test.js
@@ -16,6 +16,7 @@ import {
getYearAndMonthBasedOnFormat,
getMetaObject,
getDeviceModel,
+ formatInputFieldValue,
} from '../../src/utils/helpers';
import {
CardType,
@@ -300,4 +301,223 @@ describe('test getYearAndMonthBasedOnFormat function', () => {
year: '2032',
});
});
+
+ it('should format expiration date options correctly', () => {
+ const validFormats = ['MM/YY', 'MM/YYYY', 'YYYY/MM', 'YY/MM'];
+ validFormats.forEach((format) => {
+ const result = formatCollectElementOptions(
+ ElementType.EXPIRATION_DATE,
+ { format },
+ LogLevel.WARN
+ );
+ expect(result).toEqual({
+ format,
+ required: false,
+ });
+ });
+ });
+
+ it('should use default format for invalid expiration date format', () => {
+ const result = formatCollectElementOptions(
+ ElementType.EXPIRATION_DATE,
+ { format: 'INVALID' },
+ LogLevel.WARN
+ );
+ expect(result).toEqual({
+ format: DEFAULT_EXPIRATION_DATE_FORMAT,
+ required: false,
+ });
+ expect(printLog).toBeCalled();
+ });
+
+ it('should format expiration year options correctly', () => {
+ const validFormats = ['YY', 'YYYY'];
+ validFormats.forEach((format) => {
+ const result = formatCollectElementOptions(
+ ElementType.EXPIRATION_YEAR,
+ { format },
+ LogLevel.WARN
+ );
+ expect(result).toEqual({
+ format,
+ required: false,
+ });
+ });
+ });
+
+ it('should use default format for invalid expiration year format', () => {
+ const result = formatCollectElementOptions(
+ ElementType.EXPIRATION_YEAR,
+ { format: 'INVALID' },
+ LogLevel.WARN
+ );
+ expect(result).toEqual({
+ format: DEFAULT_EXPIRATION_YEAR_FORMAT,
+ required: false,
+ });
+ expect(printLog).toBeCalled();
+ });
+});
+
+describe('test formatCardNumber with different formats', () => {
+ const testCases = [
+ {
+ input: '4111111111111111',
+ type: CardType.VISA,
+ format: 'XXXX-XXXX-XXXX-XXXX',
+ expected: '4111-1111-1111-1111',
+ },
+ {
+ input: '378282246310005',
+ type: CardType.AMEX,
+ format: 'XXXX-XXXX-XXXX-XXXX',
+ expected: '3782-822463-10005',
+ },
+ {
+ input: '6011111111111117',
+ type: CardType.DISCOVER,
+ format: 'XXXX XXXX XXXX XXXX',
+ expected: '6011 1111 1111 1117',
+ },
+ // Test partial inputs
+ {
+ input: '411111',
+ type: CardType.VISA,
+ format: 'XXXX-XXXX-XXXX-XXXX',
+ expected: '4111-11',
+ },
+ // Test empty input
+ {
+ input: '',
+ type: CardType.DEFAULT,
+ format: 'XXXX-XXXX-XXXX-XXXX',
+ expected: '',
+ },
+ ];
+
+ testCases.forEach((testCase) => {
+ it(`should format ${testCase.type} card number correctly with format ${testCase.format}`, () => {
+ expect(
+ formatCardNumber(testCase.input, testCase.type, testCase.format)
+ ).toBe(testCase.expected);
+ });
+ });
+});
+
+describe('test formatInputFieldValue with different formats', () => {
+ const testCases = [
+ {
+ input: '123456',
+ format: 'XX-XX-XX',
+ expected: '12-34-56',
+ },
+ {
+ input: '123456',
+ format: 'XXX XXX',
+ expected: '123 456',
+ },
+ {
+ input: '12345',
+ format: 'XX/XX/X',
+ expected: '12/34/5',
+ },
+ {
+ input: '1567C89',
+ format: 'XX/XYX/X',
+ translation: { X: '[5-9]', Y: '[A-C]' },
+ expected: '56/7C8/9',
+ },
+ // Combination of constants and dynamic inputs
+ {
+ input: '1234121234',
+ format: '+91 XXXX-XX-XXXX',
+ expected: '+91 1234-12-1234',
+ },
+ {
+ input: 'B5678978C7QAB97',
+ format: '91 YXX-XZX-XXY-XZYXX',
+ translation: { X: '[5-9]', Y: '[A-C]' },
+ expected: '91 B56-7Z8-97C-7ZA97',
+ },
+ // Test with special characters in format
+ {
+ input: '123456',
+ format: 'XX@XX#XX',
+ expected: '12@34#56',
+ },
+ // Test partial input
+ {
+ input: '123',
+ format: 'XX-XX-XX',
+ expected: '12-3',
+ },
+ // Test input longer than format
+ {
+ input: '1234567',
+ format: 'XX-XX',
+ expected: '12-34',
+ },
+ // Test empty input
+ {
+ input: '',
+ format: 'XX-XX',
+ expected: '',
+ },
+ // Test with no format
+ {
+ input: '123456',
+ format: '',
+ expected: '123456',
+ },
+ ];
+
+ testCases.forEach((testCase) => {
+ const translation = testCase.translation || {
+ X: '[0-9]',
+ };
+ it(`should format input '${testCase.input}' with format '${
+ testCase.format
+ }' & translation '${JSON.stringify(translation)}' correctly`, () => {
+ expect(
+ formatInputFieldValue(
+ testCase.input,
+ testCase.format,
+ testCase?.translation
+ )
+ ).toBe(testCase.expected);
+ });
+ });
+});
+
+describe('test formatCollectElementOptions for different element types', () => {
+ it('should format card number options correctly', () => {
+ const options = {
+ format: 'XXXX-XXXX-XXXX-XXXX',
+ required: true,
+ };
+ const result = formatCollectElementOptions(
+ ElementType.CARD_NUMBER,
+ options,
+ LogLevel.WARN
+ );
+ expect(result).toEqual({
+ format: 'XXXX-XXXX-XXXX-XXXX',
+ required: true,
+ });
+ });
+
+ it('should preserve other options while formatting', () => {
+ const options = {
+ format: 'XXXX-XXXX-XXXX-XXXX',
+ required: true,
+ enableCardIcon: true,
+ customOption: 'value',
+ };
+ const result = formatCollectElementOptions(
+ ElementType.CARD_NUMBER,
+ options,
+ LogLevel.WARN
+ );
+ expect(result).toEqual(options);
+ });
});
diff --git a/src/components/CardNumberElement/index.tsx b/src/components/CardNumberElement/index.tsx
index 0ce2ba4..0817f0e 100644
--- a/src/components/CardNumberElement/index.tsx
+++ b/src/components/CardNumberElement/index.tsx
@@ -5,11 +5,12 @@ import React, { useEffect, useRef } from "react";
import { Image, Text, TextInput, View } from "react-native";
import type CollectElement from "../../core/CollectElement";
import { CARD_ENCODED_ICONS, CARD_NUMBER_MASK, CardType, CardTypeValues, DEFAULT_CARD_INPUT_MAX_LENGTH } from "../../core/constants";
-import { CollectElementProps, ElementType, ELEMENT_REQUIRED_ASTERISK, REQUIRED_MARK_DEFAULT_STYLE, ContainerType, CARD_NUMBER_ELEMENT_DEFAULT_STYLE, CARD_ICON_DEFAULT_STYLE, IListItem } from "../../utils/constants";
+import { CollectElementProps, ElementType, ELEMENT_REQUIRED_ASTERISK, REQUIRED_MARK_DEFAULT_STYLE, ContainerType, CARD_NUMBER_ELEMENT_DEFAULT_STYLE, CARD_ICON_DEFAULT_STYLE, IListItem, CollectElementOptions } from "../../utils/constants";
import SkyflowError from "../../utils/skyflow-error";
import SKYFLOW_ERROR_CODE from "../../utils/skyflow-error-code";
import uuid from 'react-native-uuid';
import Dropdown from "../../core/Dropdown";
+import { formatCollectElementOptions } from "../../utils/helpers";
const CardNumberElement: React.FC = ({ container, options, ...rest }) => {
@@ -33,7 +34,8 @@ const CardNumberElement: React.FC = ({ container, options,
useEffect(() => {
if (container) {
- const element: CollectElement = container.create({ ...rest, type: ElementType.CARD_NUMBER, containerType: container.type }, mergedOptions);
+ const elementOptions: CollectElementOptions = formatCollectElementOptions(ElementType.CARD_NUMBER, options, container.getContext().logLevel);
+ const element: CollectElement = container.create({ ...rest, type: ElementType.CARD_NUMBER, containerType: container.type }, {...mergedOptions , ...elementOptions});
setElement(element);
if (container.type === ContainerType.COLLECT)
element.setMethods(setErrorText, { setInputStyles: setInputStyles, setLabelStyles: setLabelStyles });
diff --git a/src/components/InputFieldElement/index.tsx b/src/components/InputFieldElement/index.tsx
index 35e85ae..8fd6177 100644
--- a/src/components/InputFieldElement/index.tsx
+++ b/src/components/InputFieldElement/index.tsx
@@ -4,7 +4,7 @@
import React, { useEffect, useRef } from "react";
import { Text, TextInput, View } from "react-native";
import type CollectElement from "../../core/CollectElement";
-import { CollectElementProps, ElementType, ELEMENT_REQUIRED_ASTERISK, REQUIRED_MARK_DEFAULT_STYLE, ContainerType } from "../../utils/constants";
+import { CollectElementProps, ElementType, ELEMENT_REQUIRED_ASTERISK, REQUIRED_MARK_DEFAULT_STYLE, ContainerType, CollectElementOptions } from "../../utils/constants";
import SkyflowError from "../../utils/skyflow-error";
import SKYFLOW_ERROR_CODE from "../../utils/skyflow-error-code";
import uuid from 'react-native-uuid';
@@ -48,10 +48,11 @@ const InputFieldElement: React.FC = ({ container, options =
)
}
{
element?.onChangeElement(text);
setElementValue(element.getInternalState().value)
diff --git a/src/core/CollectElement/index.ts b/src/core/CollectElement/index.ts
index 53c0405..13bf271 100644
--- a/src/core/CollectElement/index.ts
+++ b/src/core/CollectElement/index.ts
@@ -32,6 +32,7 @@ import {
appendZeroToOne,
detectCardType,
formatCardNumber,
+ formatInputFieldValue,
formatExpirationDate,
formatExpirationMonthValue,
getReturnValue,
@@ -190,7 +191,10 @@ class CollectElement extends SkyflowElement {
break;
case ElementType.CARD_NUMBER:
this.#cardType = detectCardType(value);
- this.updateElement(formatCardNumber(value, this.#cardType));
+ this.updateElement(formatCardNumber(value, this.#cardType, this.#options.format));
+ break;
+ case ElementType.INPUT_FIELD:
+ this.updateElement(formatInputFieldValue(value, this.#options.format, this.#options.translation));
break;
default:
this.updateElement(value);
diff --git a/src/core/constants/index.ts b/src/core/constants/index.ts
index 111c708..3be2ea7 100644
--- a/src/core/constants/index.ts
+++ b/src/core/constants/index.ts
@@ -40,6 +40,7 @@ export const DEFAULT_EXPIRATION_YEAR_FORMAT = 'YY';
export const FOUR_DIGIT_YEAR_FORMAT = 'YYYY';
export const DEFAULT_EXPIRATION_DATE_FORMAT = 'MM/YY';
+export const DEFAULT_CARD_NUMBER_FORMAT = 'XXXX XXXX XXXX XXXX';
export const MONTH_FORMAT = 'MM';
@@ -166,6 +167,9 @@ export const REVEAL_ELEMENT_ERROR_TEXT = 'Invalid Token';
export const DEFAULT_COLLECT_ELEMENT_ERROR_TEXT = 'Invalid value';
export const DEFAULT_VALIDATION_ERROR_TEXT = 'Validation Failed';
+export const DEFAULT_INPUT_FIELD_TRANSLATION: Record = {
+ 'X': '[0-9]',
+};
export const ALLOWED_EXPIRY_YEAR_FORMATS = [
DEFAULT_EXPIRATION_YEAR_FORMAT,
@@ -178,6 +182,10 @@ export const ALLOWED_EXPIRY_DATE_FORMATS = [
'YY/MM',
'MM/YYYY',
];
+export const ALLOWED_CARD_NUMBER_FORMATS = [
+ DEFAULT_CARD_NUMBER_FORMAT,
+ 'XXXX-XXXX-XXXX-XXXX',
+]
export const DEFAULT_COLLECT_ELEMENT_REQUIRED_TEXT = 'Field is required';
diff --git a/src/utils/helpers/index.ts b/src/utils/helpers/index.ts
index 8c523de..4fe207c 100644
--- a/src/utils/helpers/index.ts
+++ b/src/utils/helpers/index.ts
@@ -12,6 +12,9 @@ import {
DEFAULT_EXPIRATION_DATE_FORMAT,
ALLOWED_EXPIRY_DATE_FORMATS,
ALLOWED_EXPIRY_YEAR_FORMATS,
+ ALLOWED_CARD_NUMBER_FORMATS,
+ DEFAULT_CARD_NUMBER_FORMAT,
+ DEFAULT_INPUT_FIELD_TRANSLATION,
} from '../../core/constants';
import { ElementType, MessageType } from '../constants';
import logs from '../logs';
@@ -141,8 +144,81 @@ export const detectCardType = (cardNumber: string) => {
return detectedType;
};
-export const formatCardNumber = (cardNumber, type) => {
- return applyMask(cardNumber, CARD_NUMBER_MASK[type]);
+export const formatCardNumber = (
+ cardNumber: string,
+ type: string | number,
+ format = DEFAULT_CARD_NUMBER_FORMAT
+) => {
+ if (!cardNumber || cardNumber.length === 0) return '';
+ const isvalidFormat = isValidCardNumberFormat(
+ format
+ );
+
+ if (!isvalidFormat) {
+ format = DEFAULT_CARD_NUMBER_FORMAT;
+ };
+
+ const mask = CARD_NUMBER_MASK[type] || CARD_NUMBER_MASK[CardType.DEFAULT];
+ const maskedValue = applyMask(cardNumber, mask);
+ const separator = format.includes('-') ? '-' : ' ';
+ const formattedValue = maskedValue.replace(/[\s-]+/g, separator).trim();
+
+ return formattedValue;
+};
+
+export const formatInputFieldValue = (
+ input: string,
+ format: string,
+ translation: Record = DEFAULT_INPUT_FIELD_TRANSLATION
+): string => {
+ if (!format || format.length === 0) return input;
+ if (!input || input.length === 0) return '';
+
+ const inputArray = Array.from(input);
+ const formatArray = Array.from(format);
+ let formattedOutput = '';
+ let formatIndex = 0;
+
+ for (let inputIndex = 0; inputIndex < inputArray.length; inputIndex++) {
+ const inputChar = inputArray[inputIndex];
+
+ if (formatIndex < formatArray.length) {
+ const currentFormatChar = formatArray[formatIndex];
+
+ if (translation[currentFormatChar]) {
+ const regex = new RegExp(translation[currentFormatChar]);
+ if (regex.test(inputChar)) {
+ formattedOutput += inputChar;
+ formatIndex++;
+ }
+ } else {
+ if (inputChar === currentFormatChar) {
+ formattedOutput += inputChar;
+ formatIndex++;
+ } else {
+ for (let scanIndex = formatIndex; scanIndex < formatArray.length; scanIndex++) {
+ const nextFormatChar = formatArray[scanIndex];
+
+ if (translation[nextFormatChar]) {
+ const regex = new RegExp(translation[nextFormatChar]);
+ if (regex.test(inputChar)) {
+ formattedOutput += inputChar;
+ formatIndex = scanIndex + 1;
+ }
+ break;
+ } else {
+ formattedOutput += nextFormatChar;
+ formatIndex = scanIndex + 1;
+ }
+ }
+ }
+ }
+ } else {
+ break;
+ }
+ }
+
+ return formattedOutput;
};
export const getReturnValue = (
@@ -204,6 +280,13 @@ export const isValidExpiryYearFormat = (format: string): boolean => {
return false;
};
+export const isValidCardNumberFormat = (format: string): boolean => {
+ if (format) {
+ return ALLOWED_CARD_NUMBER_FORMATS.includes(format);
+ }
+ return false;
+};
+
export const formatCollectElementOptions = (
elementType: ElementType,
options,
@@ -259,6 +342,27 @@ export const formatCollectElementOptions = (
? formattedOptions.format.toUpperCase()
: DEFAULT_EXPIRATION_YEAR_FORMAT,
};
+ } else if (elementType === ElementType.CARD_NUMBER) {
+ let isvalidFormat = false;
+ if (formattedOptions.format) {
+ isvalidFormat = isValidCardNumberFormat(
+ formattedOptions.format.toUpperCase()
+ );
+ if (!isvalidFormat) {
+ printLog(
+ parameterizedString(
+ logs.warnLogs.INVALID_CARD_NUMBER_FORMAT,
+ ALLOWED_CARD_NUMBER_FORMATS.toString()
+ ),
+ MessageType.WARN,
+ logLevel
+ );
+ }
+ }
+ formattedOptions = {
+ ...formattedOptions,
+ format: isvalidFormat ? formattedOptions.format : DEFAULT_CARD_NUMBER_FORMAT,
+ };
}
return formattedOptions;
};
diff --git a/src/utils/logs/index.ts b/src/utils/logs/index.ts
index fb6a125..b140d95 100644
--- a/src/utils/logs/index.ts
+++ b/src/utils/logs/index.ts
@@ -226,6 +226,8 @@ const logs = {
'EXPIRATION_DATE format must be in one of %s1, the format is set to default MM/YY',
INVALID_EXPIRATION_YEAR_FORMAT:
'EXPIRATION_YEAR format must be in one of %s1, the format is set to default YY',
+ INVALID_CARD_NUMBER_FORMAT:
+ `CARD_NUMBER format must be in one of %s1, the format is set to default XXXX XXXX XXXX XXXX`,
},
};
From e797cf600046e60831efd33aa374052243beb375 Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Tue, 8 Jul 2025 11:25:58 +0000
Subject: [PATCH 06/23] [AUTOMATED] Private Release 1.9.0-dev.915c8c9
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 987a23b..29485f1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "skyflow-react-native",
- "version": "1.9.0-dev.dde97df",
+ "version": "1.9.0-dev.915c8c9",
"description": "Skyflow React Native SDK",
"main": "lib/commonjs/index",
"module": "lib/module/index",
From f3edc4a1f2cdb1f8df5bcd544c75ca01dc111c42 Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Fri, 11 Jul 2025 11:07:49 +0530
Subject: [PATCH 07/23] SK-2173 support input formatting in reveal elements
(#110)
---
.../__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 044185e48ebc25d4e209eb6482107b440896508d Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Fri, 11 Jul 2025 05:38:45 +0000
Subject: [PATCH 08/23] [AUTOMATED] Private Release 1.9.0-dev.f3edc4a
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 29485f1..872b782 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "skyflow-react-native",
- "version": "1.9.0-dev.915c8c9",
+ "version": "1.9.0-dev.f3edc4a",
"description": "Skyflow React Native SDK",
"main": "lib/commonjs/index",
"module": "lib/module/index",
From 40c7b7de3ece3ad0d5e71018898fcd752d36c977 Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Thu, 17 Jul 2025 09:43:19 +0530
Subject: [PATCH 09/23] SK-2173 fix card number regex (#111)
* 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;
From d365d2ae621b005ab5c698efdf882cbc92aef6d9 Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Thu, 17 Jul 2025 04:14:21 +0000
Subject: [PATCH 10/23] [AUTOMATED] Private Release 1.9.0-dev.40c7b7d
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 872b782..98c1fcc 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "skyflow-react-native",
- "version": "1.9.0-dev.f3edc4a",
+ "version": "1.9.0-dev.40c7b7d",
"description": "Skyflow React Native SDK",
"main": "lib/commonjs/index",
"module": "lib/module/index",
From 4e22480abf32c267629bc8803df6d4a7fb75fd72 Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Fri, 18 Jul 2025 17:18:20 +0530
Subject: [PATCH 11/23] SK-2173 revert unformatted card no insertion to vault
(#112)
---
__tests__/core-utils/collect.test.js | 30 +--------------------------
__tests__/core/collectElement.test.js | 24 ---------------------
src/core-utils/collect/index.ts | 8 +++----
3 files changed, 5 insertions(+), 57 deletions(-)
diff --git a/__tests__/core-utils/collect.test.js b/__tests__/core-utils/collect.test.js
index 48eca10..e223c7e 100644
--- a/__tests__/core-utils/collect.test.js
+++ b/__tests__/core-utils/collect.test.js
@@ -1,7 +1,7 @@
/*
Copyright (c) 2022 Skyflow, Inc.
*/
-import { tokenize, getElementValueToInsert } from '../../src/core-utils/collect';
+import { tokenize } from '../../src/core-utils/collect';
import CollectElement from '../../src/core/CollectElement';
import Skyflow from '../../src/core/Skyflow';
import { ElementType, Env, LogLevel } from '../../src/utils/constants';
@@ -525,31 +525,3 @@ describe('test collect utils class', () => {
});
});
-
-describe('getVaultInsertValue', () => {
- class MockCollectElement {
- constructor(type, value) {
- this.type = type;
- this.value = value;
- }
- getClientState() {
- return { elementType: this.type };
- }
- getUnformattedValue() {
- return this.value.replace(/[\s-]/g, '');
- }
- getInternalState() {
- return { value: this.value };
- }
- }
-
- test('returns unformatted value for CARD_NUMBER', () => {
- const element = new MockCollectElement(ElementType.CARD_NUMBER, '4111 1111 1111 1111');
- expect(getElementValueToInsert(element)).toBe('4111111111111111');
- });
-
- test('returns value as is for non-CARD_NUMBER', () => {
- const element = new MockCollectElement(ElementType.CVV, '123');
- expect(getElementValueToInsert(element)).toBe('123');
- });
-});
diff --git a/__tests__/core/collectElement.test.js b/__tests__/core/collectElement.test.js
index 5f37845..f3a176b 100644
--- a/__tests__/core/collectElement.test.js
+++ b/__tests__/core/collectElement.test.js
@@ -524,30 +524,6 @@ describe('test Collect Element class', () => {
);
});
- it('should remove spaces 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('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/src/core-utils/collect/index.ts b/src/core-utils/collect/index.ts
index 5280bfa..436afdc 100644
--- a/src/core-utils/collect/index.ts
+++ b/src/core-utils/collect/index.ts
@@ -229,14 +229,14 @@ export const tokenize = (
set(
elementsUpdateData[skyflowID],
column,
- getElementValueToInsert(currentElement),
+ currentElement.getInternalState().value,
);
} else {
elementsUpdateData[skyflowID] = {};
set(
elementsUpdateData[skyflowID],
column,
- getElementValueToInsert(currentElement),
+ currentElement.getInternalState().value,
);
set(
elementsUpdateData[skyflowID],
@@ -246,10 +246,10 @@ export const tokenize = (
}
}
else if (elementsData[table]) {
- set(elementsData[table], column, getElementValueToInsert(currentElement));
+ set(elementsData[table], column, currentElement.getInternalState().value);
} else {
elementsData[table] = {};
- set(elementsData[table], column, getElementValueToInsert(currentElement));
+ set(elementsData[table], column, currentElement.getInternalState().value);
}
});
From ffdb6b426e319bb97e98c3e8c58dec30ecc10092 Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Fri, 18 Jul 2025 11:49:16 +0000
Subject: [PATCH 12/23] [AUTOMATED] Private Release 1.9.0-dev.4e22480
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 98c1fcc..05afab8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "skyflow-react-native",
- "version": "1.9.0-dev.40c7b7d",
+ "version": "1.9.0-dev.4e22480",
"description": "Skyflow React Native SDK",
"main": "lib/commonjs/index",
"module": "lib/module/index",
From 80b44eb86e7cfbd807cccf8b375bcd6b25633d9c Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Tue, 22 Jul 2025 18:36:07 +0530
Subject: [PATCH 13/23] SK-2167 fix critical vulnerabilities (#113)
---
example/package.json | 5 +++
example/yarn.lock | 87 ++++++++++++++++++++++++------------
package.json | 5 ++-
yarn.lock | 103 ++++++++++++++++++++++++++++++-------------
4 files changed, 140 insertions(+), 60 deletions(-)
diff --git a/example/package.json b/example/package.json
index c0c7860..97db816 100644
--- a/example/package.json
+++ b/example/package.json
@@ -14,6 +14,11 @@
"react": "18.2.0",
"react-native": "^0.71.19"
},
+ "resolutions": {
+ "@babel/runtime": "^7.26.10",
+ "@babel/helpers": "^7.26.10",
+ "brace-expansion": "^1.1.12"
+ },
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/runtime": "^7.12.5",
diff --git a/example/yarn.lock b/example/yarn.lock
index 6dc241b..1ce7df6 100644
--- a/example/yarn.lock
+++ b/example/yarn.lock
@@ -19,6 +19,15 @@
js-tokens "^4.0.0"
picocolors "^1.0.0"
+"@babel/code-frame@^7.27.1":
+ version "7.27.1"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be"
+ integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.27.1"
+ js-tokens "^4.0.0"
+ picocolors "^1.1.1"
+
"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9":
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.3.tgz#99488264a56b2aded63983abd6a417f03b92ed02"
@@ -182,11 +191,21 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
+"@babel/helper-string-parser@^7.27.1":
+ version "7.27.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
+ integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
+
"@babel/helper-validator-identifier@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
+"@babel/helper-validator-identifier@^7.27.1":
+ version "7.27.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
+ integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
+
"@babel/helper-validator-option@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72"
@@ -201,13 +220,13 @@
"@babel/traverse" "^7.25.9"
"@babel/types" "^7.25.9"
-"@babel/helpers@^7.26.0":
- version "7.26.0"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4"
- integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==
+"@babel/helpers@^7.26.0", "@babel/helpers@^7.26.10":
+ version "7.27.6"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.6.tgz#6456fed15b2cb669d2d1fabe84b66b34991d812c"
+ integrity sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==
dependencies:
- "@babel/template" "^7.25.9"
- "@babel/types" "^7.26.0"
+ "@babel/template" "^7.27.2"
+ "@babel/types" "^7.27.6"
"@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.20.0", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3":
version "7.26.3"
@@ -216,6 +235,13 @@
dependencies:
"@babel/types" "^7.26.3"
+"@babel/parser@^7.27.2":
+ version "7.28.0"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.0.tgz#979829fbab51a29e13901e5a80713dbcb840825e"
+ integrity sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==
+ dependencies:
+ "@babel/types" "^7.28.0"
+
"@babel/plugin-proposal-async-generator-functions@^7.0.0":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326"
@@ -633,12 +659,10 @@
pirates "^4.0.6"
source-map-support "^0.5.16"
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4":
- version "7.26.0"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
- integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
- dependencies:
- regenerator-runtime "^0.14.0"
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.26.10", "@babel/runtime@^7.8.4":
+ version "7.27.6"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6"
+ integrity sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==
"@babel/template@^7.0.0", "@babel/template@^7.25.9":
version "7.25.9"
@@ -649,6 +673,15 @@
"@babel/parser" "^7.25.9"
"@babel/types" "^7.25.9"
+"@babel/template@^7.27.2":
+ version "7.27.2"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d"
+ integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==
+ dependencies:
+ "@babel/code-frame" "^7.27.1"
+ "@babel/parser" "^7.27.2"
+ "@babel/types" "^7.27.1"
+
"@babel/traverse@^7.20.0", "@babel/traverse@^7.25.9":
version "7.26.4"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd"
@@ -670,6 +703,14 @@
"@babel/helper-string-parser" "^7.25.9"
"@babel/helper-validator-identifier" "^7.25.9"
+"@babel/types@^7.27.1", "@babel/types@^7.27.6", "@babel/types@^7.28.0":
+ version "7.28.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.0.tgz#2fd0159a6dc7353933920c43136335a9b264d950"
+ integrity sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==
+ dependencies:
+ "@babel/helper-string-parser" "^7.27.1"
+ "@babel/helper-validator-identifier" "^7.27.1"
+
"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
version "9.3.0"
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
@@ -1282,21 +1323,14 @@ bl@^4.1.0:
inherits "^2.0.4"
readable-stream "^3.4.0"
-brace-expansion@^1.1.7:
- version "1.1.11"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
- integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+brace-expansion@^1.1.12, brace-expansion@^1.1.7, brace-expansion@^2.0.1:
+ version "1.1.12"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
+ integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
-brace-expansion@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
- integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
- dependencies:
- balanced-match "^1.0.0"
-
braces@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
@@ -3059,7 +3093,7 @@ path-scurry@^1.6.1:
lru-cache "^10.2.0"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
-picocolors@^1.0.0, picocolors@^1.1.0:
+picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
@@ -3321,11 +3355,6 @@ regenerator-runtime@^0.13.2:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
-regenerator-runtime@^0.14.0:
- version "0.14.1"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
- integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
-
regenerator-transform@^0.15.2:
version "0.15.2"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4"
diff --git a/package.json b/package.json
index 05afab8..c16e302 100644
--- a/package.json
+++ b/package.json
@@ -64,7 +64,10 @@
"typescript": "^4.5.2"
},
"resolutions": {
- "@types/react": "17.0.21"
+ "@types/react": "17.0.21",
+ "@babel/runtime": "^7.26.10",
+ "@babel/helpers": "^7.26.10",
+ "brace-expansion": "^1.1.12"
},
"peerDependencies": {
"react": "^17.0.2 || ^18.0.0",
diff --git a/yarn.lock b/yarn.lock
index 21546bc..50752c4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -24,6 +24,15 @@
js-tokens "^4.0.0"
picocolors "^1.0.0"
+"@babel/code-frame@^7.27.1":
+ version "7.27.1"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be"
+ integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.27.1"
+ js-tokens "^4.0.0"
+ picocolors "^1.1.1"
+
"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9", "@babel/compat-data@^7.26.0":
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.3.tgz#99488264a56b2aded63983abd6a417f03b92ed02"
@@ -196,11 +205,21 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
+"@babel/helper-string-parser@^7.27.1":
+ version "7.27.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
+ integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
+
"@babel/helper-validator-identifier@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
+"@babel/helper-validator-identifier@^7.27.1":
+ version "7.27.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
+ integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
+
"@babel/helper-validator-option@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72"
@@ -215,13 +234,13 @@
"@babel/traverse" "^7.25.9"
"@babel/types" "^7.25.9"
-"@babel/helpers@^7.26.0":
- version "7.26.0"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4"
- integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==
+"@babel/helpers@^7.26.0", "@babel/helpers@^7.26.10":
+ version "7.27.6"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.6.tgz#6456fed15b2cb669d2d1fabe84b66b34991d812c"
+ integrity sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==
dependencies:
- "@babel/template" "^7.25.9"
- "@babel/types" "^7.26.0"
+ "@babel/template" "^7.27.2"
+ "@babel/types" "^7.27.6"
"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3":
version "7.26.3"
@@ -230,6 +249,13 @@
dependencies:
"@babel/types" "^7.26.3"
+"@babel/parser@^7.27.2":
+ version "7.28.0"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.0.tgz#979829fbab51a29e13901e5a80713dbcb840825e"
+ integrity sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==
+ dependencies:
+ "@babel/types" "^7.28.0"
+
"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe"
@@ -1082,12 +1108,10 @@
pirates "^4.0.6"
source-map-support "^0.5.16"
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.8.4":
- version "7.26.0"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
- integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
- dependencies:
- regenerator-runtime "^0.14.0"
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.26.10", "@babel/runtime@^7.8.4":
+ version "7.27.6"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6"
+ integrity sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==
"@babel/template@^7.0.0", "@babel/template@^7.25.9", "@babel/template@^7.3.3":
version "7.25.9"
@@ -1098,6 +1122,15 @@
"@babel/parser" "^7.25.9"
"@babel/types" "^7.25.9"
+"@babel/template@^7.27.2":
+ version "7.27.2"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d"
+ integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==
+ dependencies:
+ "@babel/code-frame" "^7.27.1"
+ "@babel/parser" "^7.27.2"
+ "@babel/types" "^7.27.1"
+
"@babel/traverse@^7.20.0", "@babel/traverse@^7.25.9", "@babel/traverse@^7.7.2":
version "7.26.4"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd"
@@ -1119,6 +1152,14 @@
"@babel/helper-string-parser" "^7.25.9"
"@babel/helper-validator-identifier" "^7.25.9"
+"@babel/types@^7.27.1", "@babel/types@^7.27.6", "@babel/types@^7.28.0":
+ version "7.28.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.0.tgz#2fd0159a6dc7353933920c43136335a9b264d950"
+ integrity sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==
+ dependencies:
+ "@babel/helper-string-parser" "^7.27.1"
+ "@babel/helper-validator-identifier" "^7.27.1"
+
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -2112,7 +2153,16 @@
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/react@17.0.21", "@types/react@~17.0.21":
+"@types/react@*", "@types/react@17.0.21":
+ version "17.0.21"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.21.tgz#069c43177cd419afaab5ce26bb4e9056549f7ea6"
+ integrity sha512-GzzXCpOthOjXvrAUFQwU/svyxu658cwu00Q9ugujS4qc1zXgLFaO0kS2SLOaMWLt2Jik781yuHCWB7UcYdGAeQ==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
+"@types/react@~17.0.21":
version "17.0.83"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.83.tgz#b477c56387b74279281149dcf5ba2a1e2216d131"
integrity sha512-l0m4ArKJvmFtR4e8UmKrj1pB4tUgOhJITf+mADyF/p69Ts1YAR/E+G9XEM0mHXKVRa1dQNHseyyDNzeuAXfXQw==
@@ -2121,6 +2171,11 @@
"@types/scheduler" "^0.16"
csstype "^3.0.2"
+"@types/scheduler@*":
+ version "0.26.0"
+ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.26.0.tgz#2b7183b9bbb622d130b23bedf06899b7fec7eed5"
+ integrity sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA==
+
"@types/scheduler@^0.16":
version "0.16.8"
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff"
@@ -2678,21 +2733,14 @@ bl@^4.1.0:
inherits "^2.0.4"
readable-stream "^3.4.0"
-brace-expansion@^1.1.7:
- version "1.1.11"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
- integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+brace-expansion@^1.1.12, brace-expansion@^1.1.7, brace-expansion@^2.0.1:
+ version "1.1.12"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
+ integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
-brace-expansion@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
- integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
- dependencies:
- balanced-match "^1.0.0"
-
braces@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
@@ -6285,7 +6333,7 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
-picocolors@^1.0.0, picocolors@^1.1.0:
+picocolors@^1.0.0, picocolors@^1.1.0, picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
@@ -6667,11 +6715,6 @@ regenerator-runtime@^0.13.2:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
-regenerator-runtime@^0.14.0:
- version "0.14.1"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
- integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
-
regenerator-transform@^0.15.2:
version "0.15.2"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4"
From 5968122adb825688d68d010b33990b1217c4d9b6 Mon Sep 17 00:00:00 2001
From: raushan-skyflow
Date: Tue, 22 Jul 2025 13:07:03 +0000
Subject: [PATCH 14/23] [AUTOMATED] Private Release 1.9.0-dev.80b44eb
---
package.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index c16e302..bb21399 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "skyflow-react-native",
- "version": "1.9.0-dev.4e22480",
+ "version": "1.9.0-dev.80b44eb",
"description": "Skyflow React Native SDK",
"main": "lib/commonjs/index",
"module": "lib/module/index",
@@ -67,7 +67,7 @@
"@types/react": "17.0.21",
"@babel/runtime": "^7.26.10",
"@babel/helpers": "^7.26.10",
- "brace-expansion": "^1.1.12"
+ "brace-expansion": "^1.1.12"
},
"peerDependencies": {
"react": "^17.0.2 || ^18.0.0",
From 7036b4559efcc0c27e5905bcc31a0d5bcb8cd150 Mon Sep 17 00:00:00 2001
From: saileshwar-skyflow
<156889717+saileshwar-skyflow@users.noreply.github.com>
Date: Fri, 19 Dec 2025 15:09:01 +0530
Subject: [PATCH 15/23] SK-2050: Add Set Token in Reveal Element (#117)
* SK-2050: set tokens in reveal elements
---
.../__snapshots__/components.test.js.snap | 21 ++++-
__tests__/components/components.test.js | 81 ++++++++++++++++++-
__tests__/core/RevealContainer.test.js | 52 ++++++++++++
src/components/RevealElement/index.tsx | 34 +++++---
src/core/RevealContainer/index.ts | 17 +++-
src/core/RevealSkyflowElement/index.ts | 4 +
src/utils/logs/index.ts | 2 +
src/utils/skyflow-error-code/index.ts | 4 +
8 files changed, 202 insertions(+), 13 deletions(-)
diff --git a/__tests__/components/__snapshots__/components.test.js.snap b/__tests__/components/__snapshots__/components.test.js.snap
index ad718fd..11db7c9 100644
--- a/__tests__/components/__snapshots__/components.test.js.snap
+++ b/__tests__/components/__snapshots__/components.test.js.snap
@@ -302,8 +302,27 @@ Array [
]
`;
+exports[`test Collect And Reveal Elements Components test RevealElement component rendering 1`] = `
+Array [
+
+ Card Number
+ ,
+
+ XXXX XXXX XXXX XXXX
+ ,
+ ,
+]
+`;
+
exports[`test Collect And Reveal Elements Components test skyflow provider 1`] = `
- Provider Childern
+ Provider Children
`;
diff --git a/__tests__/components/components.test.js b/__tests__/components/components.test.js
index aa2fb49..207df89 100644
--- a/__tests__/components/components.test.js
+++ b/__tests__/components/components.test.js
@@ -21,6 +21,7 @@ import { Text } from 'react-native';
import SkyflowError from '../../src/utils/skyflow-error';
import SKYFLOW_ERROR_CODE from '../../src/utils/skyflow-error-code';
import { ContainerType, ElementType } from '../../src/utils/constants';
+import { act } from 'react-test-renderer';
const testSkyflowClient = new Skyflow({
vaultID: '1234',
@@ -598,6 +599,84 @@ describe('test Collect And Reveal Elements Components', () => {
}
});
+ it('test RevealElement component rendering', () => {
+ const revealSetMethodMock = jest.fn();
+ const revealSetTokenMock = jest.fn();
+ const revealContainer = new RevealContainer(testSkyflowClient);
+
+ jest.spyOn(revealContainer, 'create').mockImplementation(() => ({
+ setMethods: revealSetMethodMock,
+ setToken: revealSetTokenMock,
+ }));
+
+ const revealElement = render(
+
+ );
+
+ expect(revealElement).toMatchSnapshot();
+ expect(revealSetMethodMock).toBeCalledTimes(1);
+
+ render(
+
+ );
+ expect(screen.getByText('test_token_no_alt')).toBeTruthy();
+
+ try {
+ render();
+ } catch (err) {
+ expect(err).toEqual(
+ new SkyflowError(
+ SKYFLOW_ERROR_CODE.CONTAINER_OBJECT_IS_REQUIRED,
+ ['Reveal', 'useRevealContainer()'],
+ true
+ )
+ );
+ }
+ });
+
+ it('test RevealElement setToken via ref updates UI and internal element', () => {
+ const revealSetMethodMock = jest.fn();
+ const revealSetTokenMock = jest.fn();
+ const revealContainer = new RevealContainer(testSkyflowClient);
+
+ jest.spyOn(revealContainer, 'create').mockImplementation(() => ({
+ setMethods: revealSetMethodMock,
+ setToken: revealSetTokenMock,
+ }));
+
+ const ref = React.createRef();
+ const initialToken = 'initial_token_123';
+ const newToken = 'updated_token_456';
+
+ const { getByText } = render(
+
+ );
+
+ expect(getByText(initialToken)).toBeTruthy();
+
+ act(() => {
+ ref.current.setToken(newToken);
+ });
+
+ expect(revealSetTokenMock).toHaveBeenCalledWith(newToken);
+
+ expect(getByText(newToken)).toBeTruthy();
+ });
+
it('test skyflow provider', () => {
const testSkyflowConfig = {
vaultID: '1234',
@@ -607,7 +686,7 @@ describe('test Collect And Reveal Elements Components', () => {
const providerElement = render(
- Provider Childern
+ Provider Children
);
expect(providerElement).toMatchSnapshot();
diff --git a/__tests__/core/RevealContainer.test.js b/__tests__/core/RevealContainer.test.js
index a126ee6..9fb5530 100644
--- a/__tests__/core/RevealContainer.test.js
+++ b/__tests__/core/RevealContainer.test.js
@@ -146,4 +146,56 @@ describe('test RevealConatiner Class', () => {
}
});
});
+
+ it('test reveal method uses updated token when setToken is called', (done) => {
+ const initialToken = 'initial_token';
+ const updatedToken = 'updated_token';
+
+ const revealElement = revealContainer.create({
+ token: initialToken,
+ });
+
+ const revealSuccessValue = {
+ records: [
+ {
+ token: updatedToken,
+ value: 'revealed_value',
+ },
+ ],
+ };
+
+ const fetchSpy = jest
+ .spyOn(revealUtils, 'fetchRecordsByTokenId')
+ .mockResolvedValue(revealSuccessValue);
+
+ const setValueMock = jest.fn();
+ const setErrorMock = jest.fn();
+ revealElement.setMethods(setValueMock, setErrorMock);
+
+ revealElement.setToken(updatedToken);
+
+ revealContainer
+ .reveal()
+ .then((res) => {
+ try {
+ expect(fetchSpy).toHaveBeenCalledWith(
+ expect.anything(),
+ expect.arrayContaining([
+ expect.objectContaining({
+ token: updatedToken,
+ elementId: revealElement.elementId,
+ }),
+ ])
+ );
+
+ expect(res).toEqual({ success: [{ token: updatedToken }] });
+ done();
+ } catch (assertionError) {
+ done(assertionError);
+ }
+ })
+ .catch((err) => {
+ done(err);
+ });
+ });
});
diff --git a/src/components/RevealElement/index.tsx b/src/components/RevealElement/index.tsx
index d08872a..41ab521 100644
--- a/src/components/RevealElement/index.tsx
+++ b/src/components/RevealElement/index.tsx
@@ -1,16 +1,16 @@
/*
Copyright (c) 2022 Skyflow, Inc.
*/
-import React, { useEffect } from "react";
+import React, { useEffect, useImperativeHandle, forwardRef } from "react";
import { Text } from "react-native";
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";
-
-const RevealElement: React.FC = ({ container, label, ...rest }) => {
- const [element, setElement] = React.useState(undefined);
+const RevealElement = forwardRef((props: RevealElementProps, ref) => {
+ const { container, label, ...rest } = props;
+ const [element, setElement] = React.useState(undefined);
const [errorText, setErrorText] = React.useState('');
const [value, setValue] = React.useState(rest?.altText || rest.token);
@@ -25,12 +25,26 @@ const RevealElement: React.FC = ({ container, label, ...rest
}, []);
- return <>
- {label}
- {value}
- {errorText}
- >
+ useImperativeHandle(ref, () => ({
+ setToken: (newToken: string) => {
+ if (element) {
+ element.setToken(newToken);
+ setValue(newToken);
+ } else {
+ throw new SkyflowError(SKYFLOW_ERROR_CODE.ELEMENT_NOT_FOUND, ['RevealElement'], true);
+ }
+ }
+ }), [element]);
+
+ return (
+ <>
+ {label}
+ {value}
+ {errorText}
+ >
+ );
+});
-}
+RevealElement.displayName = 'RevealElement';
export default RevealElement;
\ No newline at end of file
diff --git a/src/core/RevealContainer/index.ts b/src/core/RevealContainer/index.ts
index 35552ae..bd20cc4 100644
--- a/src/core/RevealContainer/index.ts
+++ b/src/core/RevealContainer/index.ts
@@ -70,7 +70,22 @@ class RevealContainer extends Container {
try {
validateInitConfig(this.#skyflowClient.getSkyflowConfig());
validateRevealElementRecords(this.#revealRecords);
- fetchRecordsByTokenId(this.#skyflowClient, this.#tokensList).then(
+
+ const freshTokensList = this.#tokensList.map((record) => {
+ const elementInstance = this.#elementList.find(
+ (e) => e.elementId === record.elementId
+ );
+
+ if (elementInstance) {
+ return {
+ ...record,
+ token: elementInstance.getToken(),
+ };
+ }
+ return record;
+ });
+
+ fetchRecordsByTokenId(this.#skyflowClient, freshTokensList).then(
(resolvedResult) => {
const formattedResult = formatRecordsForIframe(resolvedResult);
this.setRevealValuesInElements(formattedResult);
diff --git a/src/core/RevealSkyflowElement/index.ts b/src/core/RevealSkyflowElement/index.ts
index bd4986c..c56e984 100644
--- a/src/core/RevealSkyflowElement/index.ts
+++ b/src/core/RevealSkyflowElement/index.ts
@@ -27,6 +27,10 @@ class RevealSkyflowElement extends SkyflowElement {
this.#setErrorText(REVEAL_ELEMENT_ERROR_TEXT);
}
+ setToken(newToken: string) {
+ this.#token = newToken;
+ }
+
getToken() {
return this.#token;
}
diff --git a/src/utils/logs/index.ts b/src/utils/logs/index.ts
index fb6a125..622246f 100644
--- a/src/utils/logs/index.ts
+++ b/src/utils/logs/index.ts
@@ -133,6 +133,8 @@ const logs = {
`Skyflow ${SDK_NAME_VERSION} initialization failed - SkyflowProvider config is missing. `,
CONTAINER_OBJECT_IS_REQUIRED:
`${SDK_NAME_VERSION} cannot create %s1 element without container object, create a container using %s2 hook.`,
+ ELEMENT_NOT_FOUND:
+ `${SDK_NAME_VERSION} %s1 not found. The specified element does not exist in the container.`,
INVALID_UPSERT_OPTION_TYPE:
`${SDK_NAME_VERSION} Validation error. Invalid \'upsert\' key in insert options. Specify a value of type array instead`,
EMPTY_UPSERT_OPTIONS_ARRAY:
diff --git a/src/utils/skyflow-error-code/index.ts b/src/utils/skyflow-error-code/index.ts
index 14e93eb..333351f 100644
--- a/src/utils/skyflow-error-code/index.ts
+++ b/src/utils/skyflow-error-code/index.ts
@@ -190,6 +190,10 @@ const SKYFLOW_ERROR_CODE = {
code: 400,
description: logs.errorLogs.CONTAINER_OBJECT_IS_REQUIRED,
},
+ ELEMENT_NOT_FOUND: {
+ code: 400,
+ description: logs.errorLogs.ELEMENT_NOT_FOUND,
+ },
INVALID_UPSERT_OPTION_TYPE: {
code: 400,
description: logs.errorLogs.INVALID_UPSERT_OPTION_TYPE,
From 48cf8c6cd8fe3e73e878b34336ad750b08f15163 Mon Sep 17 00:00:00 2001
From: saileshwar-skyflow
Date: Fri, 19 Dec 2025 09:40:01 +0000
Subject: [PATCH 16/23] [AUTOMATED] Private Release 1.9.0-dev.7036b45
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 03d5000..f49668c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "skyflow-react-native",
- "version": "1.9.0",
+ "version": "1.9.0-dev.7036b45",
"description": "Skyflow React Native SDK",
"main": "lib/commonjs/index",
"module": "lib/module/index",
From 999b188a64b65bd242cee101b6157979c85fe997 Mon Sep 17 00:00:00 2001
From: saileshwar-skyflow
Date: Sat, 20 Dec 2025 17:23:08 +0000
Subject: [PATCH 17/23] [AUTOMATED] Private Release 1.9.0-dev.896cf63
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 9fd5a3d..6f64d63 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "skyflow-react-native",
- "version": "1.9.0-dev.7036b45",
+ "version": "1.9.0-dev.896cf63",
"description": "Skyflow React Native SDK",
"main": "lib/commonjs/index",
"module": "lib/module/index",
From ef764b9f9449d3b425a58a443979bb0abcf0cc41 Mon Sep 17 00:00:00 2001
From: saileshwar-skyflow
Date: Mon, 22 Dec 2025 13:28:05 +0530
Subject: [PATCH 18/23] SK-2050: add SkyflowRevealElementRef type
---
example/src/App.tsx | 35 +++++++++++++++++++-------
example/src/ElementView.tsx | 4 +--
example/src/RevealElements.tsx | 33 +++++++++++-------------
src/components/RevealElement/index.tsx | 4 +--
src/index.ts | 1 +
src/utils/constants/index.ts | 4 +++
6 files changed, 49 insertions(+), 32 deletions(-)
diff --git a/example/src/App.tsx b/example/src/App.tsx
index c22b8ff..380fe4c 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -26,11 +26,28 @@ const App = () => {
return new Promise((resolve, reject) => {
const Http = new XMLHttpRequest();
- resolve('eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2MiOiJiOTYzZTcxMjFkZDY0YTZkOTY0MGQ3ZTNlNGNjODdhNyIsImF1ZCI6Imh0dHBzOi8vbWFuYWdlLWJsaXR6LnNreWZsb3dhcGlzLmRldiIsImV4cCI6MTc2NjMyNzg5NCwiaWF0IjoxNzY2MjQxNDk0LCJpc3MiOiJzYS1hdXRoQG1hbmFnZS1ibGl0ei5za3lmbG93YXBpcy5kZXYiLCJqdGkiOiJwZWViYTFiYmExZTA0ODFjODk0ZGM2MWVjN2ZhODk5OSIsInN1YiI6Im0xODQ2YzA2M2FlODQ4MjhhNzM4OWQ3ZTc0OTE5MzE5In0.c1n87PWuKU0LJ2-tSwlK5rsTUfjE_zKKAkeDYclkP0Xh_6gnAynA83UoxJV8qDbPsmu1zuCXW7Pt4-M6kCDUViVkv2KhHYDbDByXl_PG7cVNrj0d6kxkUm_FORRFmyyCWZKy3S2tomdw_gQar2LakuENuWXAR2gL5LSywb_XZmJxgolLe6RdqcHxdSQTV5j7PkjbhfSnkuMaFK0_z8fA8Wdm-44aCa-eALAFEHCZ0-YiQqlB_24ERQxL9bfD6dxiVJqyWVEg-hrGUE0WDUtNQVhoevbcQdC7GxH7AA_yk2xUxxJh7w7OzN1-YBmQWXGgQUZg4DXT9qnugNxeH8B6Tg')
+ Http.onreadystatechange = () => {
+ if (Http.readyState === 4) {
+ if (Http.status === 200) {
+ const response = JSON.parse(Http.responseText);
+ resolve(response.accessToken);
+ } else {
+ reject('Error occured');
+ }
+ }
+ };
+
+ Http.onerror = error => {
+ reject('Error occured');
+ };
+
+ const url = '';
+ Http.open('GET', url);
+ Http.send();
});
},
- vaultID: 'tfe60a6eef66434b82e982285610e668',
- vaultURL: 'https://qhdmceurtnlz.vault.skyflowapis.dev',
+ vaultID: '',
+ vaultURL: '',
options: {
logLevel: LogLevel.ERROR,
env: Env.PROD,
@@ -40,7 +57,7 @@ const App = () => {
return (
- {/* {!displayCollect && !displayComposable && !displayCobrandedCard && <>
+ {!displayCollect && !displayComposable && !displayCobrandedCard && <>
@@ -50,14 +67,14 @@ const App = () => {
- >} */}
+ >}
- {true && }
- {/* {displayComposable && }
- {displayCobrandedCard && } */}
+ {displayCollect && }
+ {displayComposable && }
+ {displayCobrandedCard && }
);
};
-export default App;
+export default App;
\ No newline at end of file
diff --git a/example/src/ElementView.tsx b/example/src/ElementView.tsx
index 3ecf1ab..2e1a7f0 100644
--- a/example/src/ElementView.tsx
+++ b/example/src/ElementView.tsx
@@ -19,7 +19,7 @@ const ElementView = (props) => {
return (
- {true ? (
+ {showReveal ? (
) : (
@@ -35,4 +35,4 @@ const styles = StyleSheet.create({
},
});
-export default ElementView;
+export default ElementView;
\ No newline at end of file
diff --git a/example/src/RevealElements.tsx b/example/src/RevealElements.tsx
index f2a345a..0cd6cab 100644
--- a/example/src/RevealElements.tsx
+++ b/example/src/RevealElements.tsx
@@ -2,7 +2,7 @@
Copyright (c) 2022 Skyflow, Inc.
*/
-import React, { useRef } from 'react';
+import React from 'react';
import { Button, StyleSheet, View } from 'react-native';
import {
RedactionType,
@@ -15,8 +15,6 @@ const RevealElements = (props) => {
const revealContainer = useRevealContainer();
const skyflowContainer = useSkyflow();
- const revealElementRef = useRef(null);
-
const handleReveal = () => {
revealContainer
.reveal()
@@ -28,16 +26,6 @@ const RevealElements = (props) => {
});
};
- const handleSetToken = () => {
- if (revealElementRef.current) {
- console.log('Updating token via Ref...');
- // This calls the setToken method you defined in useImperativeHandle inside the SDK
- revealElementRef.current.setToken('57b28973-9690-4031-88fe-b98650d3ac93');
- } else {
- console.error('Ref is null, cannot set token');
- }
- };
-
const handleGet = () =>{
const getRecord1 = {
ids: [
@@ -86,8 +74,7 @@ const RevealElements = (props) => {
return (
{
errorTextStyles={revealerrorTextStyles}
redaction={RedactionType.REDACTED}
/>
- {/* {
inputStyles={revealInputStyles}
labelStyles={revealLabelStyles}
errorTextStyles={revealerrorTextStyles}
- /> */}
+ />
-
+
+
+
+