Skip to content

Commit af9e242

Browse files
committed
feat(davinci-client): support single checkbox component
1 parent 44f9be3 commit af9e242

15 files changed

Lines changed: 394 additions & 115 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ GEMINI.md
9494

9595
.claude/worktrees
9696
.claude/settings.local.json
97+
.claude/skills
9798
.opensource
9899

99100
# Polaris
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) 2026 Ping Identity Corporation. All rights reserved.
3+
*
4+
* This software may be modified and distributed under the terms
5+
* of the MIT license. See the LICENSE file for details.
6+
*/
7+
import type { ValidatedBooleanCollector, Updater } from '@forgerock/davinci-client/types';
8+
9+
/**
10+
* Creates a single checkbox and attaches it to the form
11+
* @param {HTMLFormElement} formEl - The form element to attach the checkboxes to
12+
* @param {ValidatedBooleanCollector} collector - Contains the configuration
13+
* @param {Updater} updater - Function to call when selection changes
14+
*/
15+
export default function booleanComponent(
16+
formEl: HTMLFormElement,
17+
collector: ValidatedBooleanCollector,
18+
updater: Updater<ValidatedBooleanCollector>,
19+
) {
20+
// Create a container for the checkboxes
21+
const containerDiv = document.createElement('div');
22+
containerDiv.className = 'single-checkbox-container';
23+
24+
// Create a heading/label for the checkbox group
25+
const groupLabel = document.createElement('div');
26+
groupLabel.textContent = collector.output.label || 'Single Checkbox';
27+
groupLabel.className = 'single-checkbox-label';
28+
containerDiv.appendChild(groupLabel);
29+
30+
// Create checkboxes for each option
31+
const wrapper = document.createElement('div');
32+
wrapper.className = 'checkbox-wrapper';
33+
34+
const checkbox = document.createElement('input');
35+
checkbox.type = 'checkbox';
36+
checkbox.id = collector.output.key;
37+
checkbox.name = collector.output.key || 'single-checkbox-field';
38+
checkbox.checked = collector.output.value;
39+
checkbox.value = 'checked';
40+
41+
const label = document.createElement('label');
42+
label.htmlFor = checkbox.id;
43+
label.textContent = collector.output.label;
44+
45+
// Add event listener to handle single-select behavior
46+
checkbox.addEventListener('change', (event) => {
47+
const target = event.target as HTMLInputElement;
48+
updater(target.value === 'checked');
49+
});
50+
51+
wrapper.appendChild(checkbox);
52+
wrapper.appendChild(label);
53+
containerDiv.appendChild(wrapper);
54+
55+
// Append the container to the form
56+
formEl.appendChild(containerDiv);
57+
}

e2e/davinci-app/main.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import fidoComponent from './components/fido.js';
3535
import qrCodeComponent from './components/qr-code.js';
3636
import agreementComponent from './components/agreement.js';
3737
import pollingComponent from './components/polling.js';
38+
import booleanComponent from './components/boolean.js';
3839

3940
const loggerFn = {
4041
error: () => {
@@ -294,6 +295,8 @@ const urlParams = new URLSearchParams(window.location.search);
294295
singleValueComponent(formEl, collector, davinciClient.update(collector));
295296
} else if (collector.type === 'MultiSelectCollector') {
296297
multiValueComponent(formEl, collector, davinciClient.update(collector));
298+
} else if (collector.type === 'ValidatedBooleanCollector') {
299+
booleanComponent(formEl, collector, davinciClient.update(collector));
297300
}
298301
});
299302

packages/davinci-client/api-report/davinci-client.api.md

Lines changed: 92 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ export interface CollectorErrors {
178178
}
179179

180180
// @public (undocumented)
181-
export type Collectors = FlowCollector | PasswordCollector | TextCollector | SingleSelectCollector | IdpCollector | SubmitCollector | ActionCollector<'ActionCollector'> | SingleValueCollector<'SingleValueCollector'> | MultiSelectCollector | DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | ReadOnlyCollector | ValidatedTextCollector | ProtectCollector | PollingCollector | FidoRegistrationCollector | FidoAuthenticationCollector | QrCodeCollector | AgreementCollector | UnknownCollector;
181+
export type Collectors = FlowCollector | PasswordCollector | TextCollector | ValidatedBooleanCollector | SingleSelectCollector | IdpCollector | SubmitCollector | ActionCollector<'ActionCollector'> | SingleValueCollector<'SingleValueCollector'> | MultiSelectCollector | DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | PhoneNumberExtensionCollector | ReadOnlyCollector | ValidatedTextCollector | ProtectCollector | PollingCollector | FidoRegistrationCollector | FidoAuthenticationCollector | QrCodeCollector | AgreementCollector | UnknownCollector;
182182

183183
// @public
184184
export type CollectorValueType<T> = T extends {
@@ -194,6 +194,8 @@ export type CollectorValueType<T> = T extends {
194194
} ? string : T extends {
195195
type: 'MultiSelectCollector';
196196
} ? string[] : T extends {
197+
type: 'ValidatedBooleanCollector';
198+
} ? boolean : T extends {
197199
type: 'DeviceRegistrationCollector';
198200
} ? string : T extends {
199201
type: 'DeviceAuthenticationCollector';
@@ -212,7 +214,7 @@ export type CollectorValueType<T> = T extends {
212214
} ? string[] : string | string[] | PhoneNumberInputValue | FidoRegistrationInputValue | FidoAuthenticationInputValue;
213215

214216
// @public (undocumented)
215-
export type ComplexValueFields = DeviceAuthenticationField | DeviceRegistrationField | PhoneNumberField | FidoRegistrationField | FidoAuthenticationField | PollingField;
217+
export type ComplexValueFields = DeviceAuthenticationField | DeviceRegistrationField | PhoneNumberField | PhoneNumberExtensionField | FidoRegistrationField | FidoAuthenticationField | PollingField;
216218

217219
// @public (undocumented)
218220
export interface ContinueNode {
@@ -270,7 +272,7 @@ export function davinci<ActionType extends ActionTypes = ActionTypes>(input: {
270272
start: <QueryParams extends OutgoingQueryParams = OutgoingQueryParams>(options?: StartOptions<QueryParams> | undefined) => Promise<ContinueNode | StartNode | ErrorNode | FailureNode | SuccessNode>;
271273
update: <T extends SingleValueCollectors | MultiSelectCollector | ObjectValueCollectors | AutoCollectors>(collector: T) => Updater<T>;
272274
validate: (collector: SingleValueCollectors | ObjectValueCollectors | MultiValueCollectors | AutoCollectors) => Validator;
273-
poll: (collector: PollingCollector) => Poller;
275+
pollStatus: (collector: PollingCollector) => Poller;
274276
getClient: () => {
275277
status: "start";
276278
} | {
@@ -1032,10 +1034,10 @@ export type InferMultiValueCollectorType<T extends MultiValueCollectorTypes> = T
10321034
export type InferNoValueCollectorType<T extends NoValueCollectorTypes> = T extends 'ReadOnlyCollector' ? NoValueCollectorBase<'ReadOnlyCollector'> : T extends 'QrCodeCollector' ? QrCodeCollectorBase : T extends 'AgreementCollector' ? AgreementCollector : NoValueCollectorBase<'NoValueCollector'>;
10331035

10341036
// @public
1035-
export type InferSingleValueCollectorType<T extends SingleValueCollectorTypes> = T extends 'TextCollector' ? TextCollector : T extends 'SingleSelectCollector' ? SingleSelectCollector : T extends 'ValidatedTextCollector' ? ValidatedTextCollector : T extends 'PasswordCollector' ? PasswordCollector : SingleValueCollectorWithValue<'SingleValueCollector'> | SingleValueCollectorNoValue<'SingleValueCollector'>;
1037+
export type InferSingleValueCollectorType<T extends SingleValueCollectorTypes> = T extends 'TextCollector' ? TextCollector : T extends 'SingleSelectCollector' ? SingleSelectCollector : T extends 'ValidatedTextCollector' ? ValidatedTextCollector : T extends 'PasswordCollector' ? PasswordCollector : T extends 'ValidatedBooleanCollector' ? ValidatedBooleanCollector : SingleValueCollectorWithValue<'SingleValueCollector'> | SingleValueCollectorNoValue<'SingleValueCollector'>;
10361038

10371039
// @public (undocumented)
1038-
export type InferValueObjectCollectorType<T extends ObjectValueCollectorTypes> = T extends 'DeviceAuthenticationCollector' ? DeviceAuthenticationCollector : T extends 'DeviceRegistrationCollector' ? DeviceRegistrationCollector : T extends 'PhoneNumberCollector' ? PhoneNumberCollector : ObjectOptionsCollectorWithObjectValue<'ObjectValueCollector'> | ObjectOptionsCollectorWithStringValue<'ObjectValueCollector'>;
1040+
export type InferValueObjectCollectorType<T extends ObjectValueCollectorTypes> = T extends 'DeviceAuthenticationCollector' ? DeviceAuthenticationCollector : T extends 'DeviceRegistrationCollector' ? DeviceRegistrationCollector : T extends 'PhoneNumberCollector' ? PhoneNumberCollector : T extends 'PhoneNumberExtensionCollector' ? PhoneNumberExtensionCollector : ObjectOptionsCollectorWithObjectValue<'ObjectValueCollector'> | ObjectOptionsCollectorWithStringValue<'ObjectValueCollector'>;
10391041

10401042
// @public (undocumented)
10411043
export type InitFlow = () => Promise<FlowNode | InternalErrorResponse>;
@@ -1170,8 +1172,8 @@ value: Record<string, unknown>;
11701172
}, string>;
11711173

11721174
// @public
1173-
export const nodeCollectorReducer: Reducer<(TextCollector | SingleSelectCollector | ValidatedTextCollector | PasswordCollector | MultiSelectCollector | DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | IdpCollector | SubmitCollector | FlowCollector | QrCodeCollectorBase | AgreementCollector | ReadOnlyCollector | UnknownCollector | ProtectCollector | FidoRegistrationCollector | FidoAuthenticationCollector | PollingCollector | ActionCollector<"ActionCollector"> | SingleValueCollector<"SingleValueCollector">)[]> & {
1174-
getInitialState: () => (TextCollector | SingleSelectCollector | ValidatedTextCollector | PasswordCollector | MultiSelectCollector | DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | IdpCollector | SubmitCollector | FlowCollector | QrCodeCollectorBase | AgreementCollector | ReadOnlyCollector | UnknownCollector | ProtectCollector | FidoRegistrationCollector | FidoAuthenticationCollector | PollingCollector | ActionCollector<"ActionCollector"> | SingleValueCollector<"SingleValueCollector">)[];
1175+
export const nodeCollectorReducer: Reducer<(TextCollector | SingleSelectCollector | ValidatedTextCollector | PasswordCollector | ValidatedBooleanCollector | MultiSelectCollector | PhoneNumberExtensionCollector | DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | IdpCollector | SubmitCollector | FlowCollector | QrCodeCollectorBase | AgreementCollector | ReadOnlyCollector | UnknownCollector | ProtectCollector | FidoRegistrationCollector | FidoAuthenticationCollector | PollingCollector | ActionCollector<"ActionCollector"> | SingleValueCollector<"SingleValueCollector">)[]> & {
1176+
getInitialState: () => (TextCollector | SingleSelectCollector | ValidatedTextCollector | PasswordCollector | ValidatedBooleanCollector | MultiSelectCollector | PhoneNumberExtensionCollector | DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | IdpCollector | SubmitCollector | FlowCollector | QrCodeCollectorBase | AgreementCollector | ReadOnlyCollector | UnknownCollector | ProtectCollector | FidoRegistrationCollector | FidoAuthenticationCollector | PollingCollector | ActionCollector<"ActionCollector"> | SingleValueCollector<"SingleValueCollector">)[];
11751177
};
11761178

11771179
// @public (undocumented)
@@ -1283,10 +1285,10 @@ export type ObjectValueAutoCollectorTypes = 'ObjectValueAutoCollector' | 'FidoRe
12831285
export type ObjectValueCollector<T extends ObjectValueCollectorTypes> = ObjectOptionsCollectorWithObjectValue<T> | ObjectOptionsCollectorWithStringValue<T> | ObjectValueCollectorWithObjectValue<T>;
12841286

12851287
// @public (undocumented)
1286-
export type ObjectValueCollectors = DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | ObjectOptionsCollectorWithObjectValue<'ObjectSelectCollector'> | ObjectOptionsCollectorWithStringValue<'ObjectSelectCollector'>;
1288+
export type ObjectValueCollectors = DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | PhoneNumberExtensionCollector | ObjectOptionsCollectorWithObjectValue<'ObjectSelectCollector'> | ObjectOptionsCollectorWithStringValue<'ObjectSelectCollector'>;
12871289

12881290
// @public
1289-
export type ObjectValueCollectorTypes = 'DeviceAuthenticationCollector' | 'DeviceRegistrationCollector' | 'PhoneNumberCollector' | 'ObjectOptionsCollector' | 'ObjectValueCollector' | 'ObjectSelectCollector';
1291+
export type ObjectValueCollectorTypes = 'DeviceAuthenticationCollector' | 'DeviceRegistrationCollector' | 'PhoneNumberCollector' | 'PhoneNumberExtensionCollector' | 'ObjectOptionsCollector' | 'ObjectValueCollector' | 'ObjectSelectCollector';
12901292

12911293
// @public (undocumented)
12921294
export interface ObjectValueCollectorWithObjectValue<T extends ObjectValueCollectorTypes, IV = Record<string, string>, OV = Record<string, string>> {
@@ -1328,13 +1330,68 @@ export type PasswordCollector = SingleValueCollectorNoValue<'PasswordCollector'>
13281330
// @public (undocumented)
13291331
export type PhoneNumberCollector = ObjectValueCollectorWithObjectValue<'PhoneNumberCollector', PhoneNumberInputValue, PhoneNumberOutputValue>;
13301332

1333+
// @public (undocumented)
1334+
export interface PhoneNumberExtensionCollector {
1335+
// (undocumented)
1336+
category: 'ObjectValueCollector';
1337+
// (undocumented)
1338+
error: string | null;
1339+
// (undocumented)
1340+
id: string;
1341+
// (undocumented)
1342+
input: {
1343+
key: string;
1344+
value: PhoneNumberExtensionInputValue;
1345+
type: string;
1346+
validation: (ValidationRequired | ValidationPhoneNumber)[] | null;
1347+
};
1348+
// (undocumented)
1349+
name: string;
1350+
// (undocumented)
1351+
output: {
1352+
key: string;
1353+
label: string;
1354+
type: string;
1355+
extensionLabel: string;
1356+
value: PhoneNumberExtensionOutputValue;
1357+
};
1358+
// (undocumented)
1359+
type: 'PhoneNumberExtensionCollector';
1360+
}
1361+
1362+
// @public (undocumented)
1363+
export type PhoneNumberExtensionField = PhoneNumberField & {
1364+
showExtension: boolean;
1365+
extensionLabel: string;
1366+
};
1367+
1368+
// @public (undocumented)
1369+
export interface PhoneNumberExtensionInputValue {
1370+
// (undocumented)
1371+
countryCode: string;
1372+
// (undocumented)
1373+
extension: string;
1374+
// (undocumented)
1375+
phoneNumber: string;
1376+
}
1377+
1378+
// @public (undocumented)
1379+
export interface PhoneNumberExtensionOutputValue {
1380+
// (undocumented)
1381+
countryCode?: string;
1382+
// (undocumented)
1383+
extension?: string;
1384+
// (undocumented)
1385+
phoneNumber?: string;
1386+
}
1387+
13311388
// @public (undocumented)
13321389
export type PhoneNumberField = {
13331390
type: 'PHONE_NUMBER';
13341391
key: string;
13351392
label: string;
1336-
defaultCountryCode: string | null;
13371393
required: boolean;
1394+
defaultCountryCode: string | null;
13381395
validatePhoneNumber: boolean;
13391396
};
13401397

@@ -1481,6 +1538,18 @@ export interface SelectorOption {
14811538
value: string;
14821539
}
14831540

1541+
// @public (undocumented)
1542+
export type SingleCheckboxField = {
1543+
type: 'SINGLE_CHECKBOX';
1544+
inputType: 'BOOLEAN';
1545+
key: string;
1546+
label: string;
1547+
required: boolean;
1548+
validation?: {
1549+
errorMessage: string;
1550+
};
1551+
};
1552+
14841553
// @public (undocumented)
14851554
export type SingleSelectCollector = SingleSelectCollectorWithValue<'SingleSelectCollector'>;
14861555

@@ -1588,13 +1657,13 @@ export interface SingleValueCollectorNoValue<T extends SingleValueCollectorTypes
15881657
}
15891658

15901659
// @public (undocumented)
1591-
export type SingleValueCollectors = SingleValueCollectorNoValue<'PasswordCollector'> | SingleSelectCollectorWithValue<'SingleSelectCollector'> | SingleValueCollectorWithValue<'SingleValueCollector'> | SingleValueCollectorWithValue<'TextCollector'> | ValidatedSingleValueCollectorWithValue<'TextCollector'>;
1660+
export type SingleValueCollectors = PasswordCollector | SingleSelectCollector | TextCollector | ValidatedTextCollector | ValidatedBooleanCollector | SingleValueCollectorWithValue<'SingleValueCollector'>;
15921661

15931662
// @public
1594-
export type SingleValueCollectorTypes = 'PasswordCollector' | 'SingleValueCollector' | 'SingleSelectCollector' | 'SingleSelectObjectCollector' | 'TextCollector' | 'ValidatedTextCollector';
1663+
export type SingleValueCollectorTypes = 'PasswordCollector' | 'ValidatedBooleanCollector' | 'SingleValueCollector' | 'SingleSelectCollector' | 'SingleSelectObjectCollector' | 'TextCollector' | 'ValidatedTextCollector';
15951664

15961665
// @public (undocumented)
1597-
export interface SingleValueCollectorWithValue<T extends SingleValueCollectorTypes> {
1666+
export interface SingleValueCollectorWithValue<T extends SingleValueCollectorTypes, V = string> {
15981667
// (undocumented)
15991668
category: 'SingleValueCollector';
16001669
// (undocumented)
@@ -1604,7 +1673,7 @@ export interface SingleValueCollectorWithValue<T extends SingleValueCollectorTyp
16041673
// (undocumented)
16051674
input: {
16061675
key: string;
1607-
value: string | number | boolean;
1676+
value: V;
16081677
type: string;
16091678
};
16101679
// (undocumented)
@@ -1614,14 +1683,14 @@ export interface SingleValueCollectorWithValue<T extends SingleValueCollectorTyp
16141683
key: string;
16151684
label: string;
16161685
type: string;
1617-
value: string | number | boolean;
1686+
value: V;
16181687
};
16191688
// (undocumented)
16201689
type: T;
16211690
}
16221691

16231692
// @public (undocumented)
1624-
export type SingleValueFields = StandardField | ValidatedField | SingleSelectField | ProtectField;
1693+
export type SingleValueFields = StandardField | ValidatedField | SingleCheckboxField | SingleSelectField | ProtectField;
16251694

16261695
// @public (undocumented)
16271696
export type StandardField = {
@@ -1724,13 +1793,16 @@ export type UnknownField = Record<string, unknown>;
17241793
// @public (undocumented)
17251794
export const updateCollectorValues: ActionCreatorWithPayload< {
17261795
id: string;
1727-
value: string | string[] | PhoneNumberInputValue | FidoRegistrationInputValue | FidoAuthenticationInputValue;
1796+
value: string | string[] | boolean | PhoneNumberInputValue | PhoneNumberExtensionInputValue | FidoRegistrationInputValue | FidoAuthenticationInputValue;
17281797
index?: number;
17291798
}, string>;
17301799

17311800
// @public
17321801
export type Updater<T = unknown> = (value: CollectorValueType<T>, index?: number) => InternalErrorResponse | null;
17331802

1803+
// @public (undocumented)
1804+
export type ValidatedBooleanCollector = ValidatedSingleValueCollectorWithValue<'ValidatedBooleanCollector', boolean>;
1805+
17341806
// @public (undocumented)
17351807
export type ValidatedField = {
17361808
type: 'TEXT';
@@ -1744,7 +1816,7 @@ export type ValidatedField = {
17441816
};
17451817

17461818
// @public (undocumented)
1747-
export interface ValidatedSingleValueCollectorWithValue<T extends SingleValueCollectorTypes> {
1819+
export interface ValidatedSingleValueCollectorWithValue<T extends SingleValueCollectorTypes, V = string> {
17481820
// (undocumented)
17491821
category: 'ValidatedSingleValueCollector';
17501822
// (undocumented)
@@ -1754,9 +1826,9 @@ export interface ValidatedSingleValueCollectorWithValue<T extends SingleValueCol
17541826
// (undocumented)
17551827
input: {
17561828
key: string;
1757-
value: string | number | boolean;
17581829
type: string;
17591830
validation: (ValidationRequired | ValidationRegex)[];
1831+
value: V;
17601832
};
17611833
// (undocumented)
17621834
name: string;
@@ -1765,7 +1837,7 @@ export interface ValidatedSingleValueCollectorWithValue<T extends SingleValueCol
17651837
key: string;
17661838
label: string;
17671839
type: string;
1768-
value: string | number | boolean;
1840+
value: V;
17691841
};
17701842
// (undocumented)
17711843
type: T;

0 commit comments

Comments
 (0)