Skip to content

Commit 9aa183c

Browse files
committed
feat(davinci-client): add support for extension in PhoneNumberCollector (SDKS-4670)
1 parent 9088443 commit 9aa183c

9 files changed

Lines changed: 192 additions & 36 deletions

File tree

.changeset/long-singers-do.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@forgerock/davinci-client': minor
3+
---
4+
5+
Add support for extension in PhoneNumberCollector
6+
7+
BREAKING CHANGE: `ObjectValueCollectorWithObjectValue` type was removed

e2e/davinci-app/components/object-value.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export default function objectValueComponent(
8787
updater({
8888
phoneNumber: selectedValue,
8989
countryCode: collector.output.value?.countryCode || '',
90+
extension: collector.output.value?.extension || '',
9091
} as any);
9192
});
9293

e2e/davinci-suites/src/form-fields.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ test('Should render form fields', async ({ page }) => {
4242

4343
await page.getByRole('button', { name: 'Submit' }).click();
4444
const request = await requestPromise;
45-
46-
const parsedData = JSON.parse(request.postData());
45+
const postData = request.postData();
46+
const parsedData = postData ? JSON.parse(postData) : {};
4747
const data = parsedData.parameters.data;
48+
4849
expect(data.actionKey).toBe('submit');
4950
expect(data.formData).toStrictEqual({
5051
'text-input-key': 'The input',
@@ -55,6 +56,7 @@ test('Should render form fields', async ({ page }) => {
5556
'phone-field': {
5657
phoneNumber: '1234567890',
5758
countryCode: 'GB',
59+
extension: '4321',
5860
},
5961
});
6062
});

packages/davinci-client/src/lib/collector.types.test-d.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ import type {
2323
InferSingleValueCollectorType,
2424
InferMultiValueCollectorType,
2525
InferActionCollectorType,
26+
PhoneNumberCollector,
27+
ObjectOptionsCollectorWithObjectValue,
28+
InferValueObjectCollectorType,
29+
PhoneNumberInputValue,
30+
PhoneNumberOutputValue,
31+
PhoneNumberOptions,
2632
} from './collector.types.js';
2733

2834
describe('Collector Types', () => {
@@ -354,5 +360,75 @@ describe('Collector Types', () => {
354360

355361
expectTypeOf(tCollector).toMatchTypeOf<FlowCollector>();
356362
});
363+
364+
it('should correctly infer PhoneNumberCollector Type', () => {
365+
const tCollector: InferValueObjectCollectorType<'PhoneNumberCollector'> = {
366+
category: 'ObjectValueCollector',
367+
error: null,
368+
type: 'PhoneNumberCollector',
369+
id: '',
370+
name: '',
371+
input: {
372+
key: '',
373+
value: { countryCode: '', phoneNumber: '', extension: '' },
374+
type: '',
375+
validation: null,
376+
},
377+
output: {
378+
key: '',
379+
label: '',
380+
type: '',
381+
options: { showExtension: false },
382+
value: { countryCode: '', phoneNumber: '', extension: '' },
383+
},
384+
};
385+
386+
expectTypeOf(tCollector).toEqualTypeOf<PhoneNumberCollector>();
387+
});
388+
});
389+
390+
describe('ObjectValueCollector Types', () => {
391+
it('should validate PhoneNumberCollector structure', () => {
392+
expectTypeOf<PhoneNumberCollector>().toEqualTypeOf<
393+
ObjectOptionsCollectorWithObjectValue<
394+
'PhoneNumberCollector',
395+
PhoneNumberInputValue,
396+
PhoneNumberOutputValue,
397+
PhoneNumberOptions
398+
>
399+
>();
400+
expectTypeOf<PhoneNumberCollector>()
401+
.toHaveProperty('category')
402+
.toEqualTypeOf<'ObjectValueCollector'>();
403+
expectTypeOf<PhoneNumberCollector>()
404+
.toHaveProperty('type')
405+
.toEqualTypeOf<'PhoneNumberCollector'>();
406+
expectTypeOf<PhoneNumberCollector['input']['value']>().toEqualTypeOf<PhoneNumberInputValue>();
407+
expectTypeOf<PhoneNumberCollector['output']['options']>().toEqualTypeOf<PhoneNumberOptions>();
408+
});
409+
410+
it('should validate PhoneNumberCollector base type constraints', () => {
411+
const collector: PhoneNumberCollector = {
412+
category: 'ObjectValueCollector',
413+
type: 'PhoneNumberCollector',
414+
error: null,
415+
id: 'test',
416+
name: 'Test',
417+
input: {
418+
key: 'phone',
419+
value: { countryCode: '+1', phoneNumber: '5555555555', extension: '' },
420+
type: 'string',
421+
validation: null,
422+
},
423+
output: {
424+
key: 'phone',
425+
label: 'Phone Number',
426+
type: 'phone',
427+
options: { showExtension: true },
428+
value: { countryCode: '+1', phoneNumber: '5555555555' },
429+
},
430+
};
431+
expectTypeOf(collector).toEqualTypeOf<PhoneNumberCollector>();
432+
});
357433
});
358434
});

packages/davinci-client/src/lib/collector.types.ts

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,17 @@ export interface DeviceValue {
297297
export interface PhoneNumberInputValue {
298298
countryCode: string;
299299
phoneNumber: string;
300+
extension: string;
300301
}
301302

302303
export interface PhoneNumberOutputValue {
303304
countryCode?: string;
304305
phoneNumber?: string;
306+
extension?: string;
307+
}
308+
309+
export interface PhoneNumberOptions {
310+
showExtension: boolean;
305311
}
306312

307313
export interface ObjectOptionsCollectorWithStringValue<
@@ -331,6 +337,7 @@ export interface ObjectOptionsCollectorWithObjectValue<
331337
T extends ObjectValueCollectorTypes,
332338
V = Record<string, string>,
333339
D = Record<string, string>,
340+
O = Record<string, string>,
334341
> {
335342
category: 'ObjectValueCollector';
336343
error: string | null;
@@ -341,38 +348,14 @@ export interface ObjectOptionsCollectorWithObjectValue<
341348
key: string;
342349
value: V;
343350
type: string;
344-
validation: ValidationRequired[] | null;
345-
};
346-
output: {
347-
key: string;
348-
label: string;
349-
type: string;
350-
options: DeviceOptionWithDefault[];
351-
value?: D | null;
352-
};
353-
}
354-
355-
export interface ObjectValueCollectorWithObjectValue<
356-
T extends ObjectValueCollectorTypes,
357-
IV = Record<string, string>,
358-
OV = Record<string, string>,
359-
> {
360-
category: 'ObjectValueCollector';
361-
error: string | null;
362-
type: T;
363-
id: string;
364-
name: string;
365-
input: {
366-
key: string;
367-
value: IV;
368-
type: string;
369351
validation: (ValidationRequired | ValidationPhoneNumber)[] | null;
370352
};
371353
output: {
372354
key: string;
373355
label: string;
374356
type: string;
375-
value?: OV | null;
357+
options: O;
358+
value?: D | null;
376359
};
377360
}
378361

@@ -396,21 +379,23 @@ export type ObjectValueCollectors =
396379

397380
export type ObjectValueCollector<T extends ObjectValueCollectorTypes> =
398381
| ObjectOptionsCollectorWithObjectValue<T>
399-
| ObjectOptionsCollectorWithStringValue<T>
400-
| ObjectValueCollectorWithObjectValue<T>;
382+
| ObjectOptionsCollectorWithStringValue<T>;
401383

402384
export type DeviceRegistrationCollector = ObjectOptionsCollectorWithStringValue<
403385
'DeviceRegistrationCollector',
404386
string
405387
>;
406388
export type DeviceAuthenticationCollector = ObjectOptionsCollectorWithObjectValue<
407389
'DeviceAuthenticationCollector',
408-
DeviceValue
390+
DeviceValue,
391+
Record<string, string>,
392+
DeviceOptionWithDefault[]
409393
>;
410-
export type PhoneNumberCollector = ObjectValueCollectorWithObjectValue<
394+
export type PhoneNumberCollector = ObjectOptionsCollectorWithObjectValue<
411395
'PhoneNumberCollector',
412396
PhoneNumberInputValue,
413-
PhoneNumberOutputValue
397+
PhoneNumberOutputValue,
398+
PhoneNumberOptions
414399
>;
415400

416401
/** *********************************************************************

packages/davinci-client/src/lib/collector.utils.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ describe('returnPhoneNumberCollector', () => {
567567
type: 'PHONE_NUMBER',
568568
required: true,
569569
validatePhoneNumber: true,
570+
showExtension: false,
570571
};
571572

572573
const result = returnObjectValueCollector(mockField, 1, {});
@@ -581,6 +582,7 @@ describe('returnPhoneNumberCollector', () => {
581582
value: {
582583
countryCode: '',
583584
phoneNumber: '',
585+
extension: '',
584586
},
585587
type: mockField.type,
586588
validation: [
@@ -600,9 +602,11 @@ describe('returnPhoneNumberCollector', () => {
600602
key: mockField.key,
601603
label: mockField.label,
602604
type: mockField.type,
605+
options: { showExtension: false },
603606
value: {
604607
countryCode: '',
605608
phoneNumber: '',
609+
extension: '',
606610
},
607611
},
608612
});
@@ -616,6 +620,7 @@ describe('returnPhoneNumberCollector', () => {
616620
type: 'PHONE_NUMBER',
617621
required: false,
618622
validatePhoneNumber: false,
623+
showExtension: false,
619624
};
620625
const result = returnObjectValueCollector(mockField, 1, {});
621626
expect(result).toEqual({
@@ -629,6 +634,7 @@ describe('returnPhoneNumberCollector', () => {
629634
value: {
630635
countryCode: mockField.defaultCountryCode,
631636
phoneNumber: '',
637+
extension: '',
632638
},
633639
type: mockField.type,
634640
validation: null,
@@ -637,9 +643,11 @@ describe('returnPhoneNumberCollector', () => {
637643
key: mockField.key,
638644
label: mockField.label,
639645
type: mockField.type,
646+
options: { showExtension: false },
640647
value: {
641648
countryCode: mockField.defaultCountryCode,
642649
phoneNumber: '',
650+
extension: '',
643651
},
644652
},
645653
});
@@ -653,6 +661,7 @@ describe('returnPhoneNumberCollector', () => {
653661
type: 'PHONE_NUMBER',
654662
required: false,
655663
validatePhoneNumber: false,
664+
showExtension: false,
656665
};
657666
const prefillMock: PhoneNumberOutputValue = {
658667
countryCode: 'CA',
@@ -669,6 +678,7 @@ describe('returnPhoneNumberCollector', () => {
669678
value: {
670679
countryCode: prefillMock.countryCode,
671680
phoneNumber: '',
681+
extension: '',
672682
},
673683
type: mockField.type,
674684
validation: null,
@@ -677,9 +687,11 @@ describe('returnPhoneNumberCollector', () => {
677687
key: mockField.key,
678688
label: mockField.label,
679689
type: mockField.type,
690+
options: { showExtension: false },
680691
value: {
681692
countryCode: prefillMock.countryCode,
682693
phoneNumber: '',
694+
extension: '',
683695
},
684696
},
685697
});
@@ -695,6 +707,7 @@ describe('returnPhoneNumberCollector', () => {
695707
type: 'PHONE_NUMBER',
696708
required: false,
697709
validatePhoneNumber: false,
710+
showExtension: false,
698711
};
699712
const prefillMock: PhoneNumberOutputValue = {
700713
phoneNumber: '1234567890',
@@ -711,6 +724,7 @@ describe('returnPhoneNumberCollector', () => {
711724
value: {
712725
countryCode: '',
713726
phoneNumber: prefillMock.phoneNumber,
727+
extension: '',
714728
},
715729
type: mockField.type,
716730
validation: null,
@@ -719,9 +733,11 @@ describe('returnPhoneNumberCollector', () => {
719733
key: mockField.key,
720734
label: mockField.label,
721735
type: mockField.type,
736+
options: { showExtension: false },
722737
value: {
723738
countryCode: '',
724739
phoneNumber: prefillMock.phoneNumber,
740+
extension: '',
725741
},
726742
},
727743
});
@@ -735,6 +751,7 @@ describe('returnPhoneNumberCollector', () => {
735751
type: 'PHONE_NUMBER',
736752
required: false,
737753
validatePhoneNumber: false,
754+
showExtension: false,
738755
};
739756
const prefillMock: PhoneNumberOutputValue = {
740757
countryCode: 'CA',
@@ -752,6 +769,7 @@ describe('returnPhoneNumberCollector', () => {
752769
value: {
753770
countryCode: prefillMock.countryCode,
754771
phoneNumber: prefillMock.phoneNumber,
772+
extension: '',
755773
},
756774
type: mockField.type,
757775
validation: null,
@@ -760,13 +778,48 @@ describe('returnPhoneNumberCollector', () => {
760778
key: mockField.key,
761779
label: mockField.label,
762780
type: mockField.type,
781+
options: { showExtension: false },
763782
value: {
764783
countryCode: prefillMock.countryCode,
765784
phoneNumber: prefillMock.phoneNumber,
785+
extension: '',
766786
},
767787
},
768788
});
769789
});
790+
791+
it('showExtension is reflected in output options', () => {
792+
const mockField: PhoneNumberField = {
793+
key: 'phone-number-key',
794+
defaultCountryCode: null,
795+
label: 'Phone Number',
796+
type: 'PHONE_NUMBER',
797+
required: false,
798+
validatePhoneNumber: false,
799+
showExtension: true,
800+
};
801+
const result = returnObjectValueCollector(mockField, 1, {});
802+
expect(result.output.options).toEqual({ showExtension: true });
803+
});
804+
805+
it('prefilled extension is set on collector', () => {
806+
const mockField: PhoneNumberField = {
807+
key: 'phone-number-key',
808+
defaultCountryCode: null,
809+
label: 'Phone Number',
810+
type: 'PHONE_NUMBER',
811+
required: false,
812+
validatePhoneNumber: false,
813+
showExtension: true,
814+
};
815+
const prefillMock: PhoneNumberOutputValue = {
816+
phoneNumber: '1234567890',
817+
extension: '123',
818+
};
819+
const result = returnObjectValueCollector(mockField, 1, prefillMock);
820+
expect(result.input.value.extension).toBe('123');
821+
expect(result.output.value?.extension).toBe('123');
822+
});
770823
});
771824

772825
describe('No Value Collectors', () => {

0 commit comments

Comments
 (0)