Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/single-checkbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@forgerock/davinci-client': minor
---

Adds support for the SINGLE_CHECKBOX field. A new ValidatedBooleanCollector type was introduced including validation support for required checkboxes and updater support for booleans.

**Type improvements**

- `SingleValueCollectorWithValue<T, V>` and `ValidatedSingleValueCollectorWithValue<T, V>` are now generic over their value type (`V`, defaults to `string`), replacing the loose `string | number | boolean` union

- `Validator` is now generic over collector type `T`, replacing the hardcoded `string` input with `CollectorValueType<T>` — so validators receive the value type that matches their collector (e.g. `boolean` for `ValidatedBooleanCollector`, `string` for text collectors) rather than always `string`
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ GEMINI.md

.claude/worktrees
.claude/settings.local.json
.claude/skills
.claude/CLAUDE.md
.opensource

# Polaris
Expand Down
57 changes: 57 additions & 0 deletions e2e/davinci-app/components/boolean.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2026 Ping Identity Corporation. All rights reserved.
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
import type { ValidatedBooleanCollector, Updater } from '@forgerock/davinci-client/types';

/**
* Creates a single checkbox and attaches it to the form
* @param {HTMLFormElement} formEl - The form element to attach the checkboxes to
* @param {ValidatedBooleanCollector} collector - Contains the configuration
* @param {Updater} updater - Function to call when selection changes
*/
export default function booleanComponent(
formEl: HTMLFormElement,
collector: ValidatedBooleanCollector,
updater: Updater<ValidatedBooleanCollector>,
) {
// Create a container for the checkboxes
const containerDiv = document.createElement('div');
containerDiv.className = 'single-checkbox-container';

// Create a heading/label for the checkbox group
const groupLabel = document.createElement('div');
groupLabel.textContent = collector.output.label || 'Single Checkbox';
groupLabel.className = 'single-checkbox-label';
containerDiv.appendChild(groupLabel);

// Create checkboxes for each option
const wrapper = document.createElement('div');
wrapper.className = 'checkbox-wrapper';

const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = collector.output.key;
checkbox.name = collector.output.key || 'single-checkbox-field';
checkbox.checked = collector.output.value;
checkbox.value = 'checked';
Comment on lines +34 to +39
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Minor: Consider adding required attribute support.

The SingleCheckboxField type includes a required property, but the component doesn't set the HTML required attribute on the checkbox element. This means required validation won't work as expected.

🛡️ Proposed fix
   const checkbox = document.createElement('input');
   checkbox.type = 'checkbox';
   checkbox.id = collector.output.key;
   checkbox.name = collector.output.key || 'single-checkbox-field';
   checkbox.checked = collector.output.value as boolean;
   checkbox.value = 'checked';
+  if (collector.output.required) {
+    checkbox.required = true;
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = collector.output.key;
checkbox.name = collector.output.key || 'single-checkbox-field';
checkbox.checked = collector.output.value as boolean;
checkbox.value = 'checked';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = collector.output.key;
checkbox.name = collector.output.key || 'single-checkbox-field';
checkbox.checked = collector.output.value as boolean;
checkbox.value = 'checked';
if (collector.output.required) {
checkbox.required = true;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/davinci-app/components/boolean.ts` around lines 34 - 39, The
SingleCheckboxField's required flag isn't applied to the created checkbox
element; update the element creation in the component that builds the checkbox
(where checkbox is created from collector.output) to set checkbox.required =
!!collector.output.required (and optionally
checkbox.setAttribute('aria-required','true') for accessibility) so the HTML
required validation is honored; ensure you reference collector.output.required
and the checkbox element in the SingleCheckboxField handling logic.


const label = document.createElement('label');
label.htmlFor = checkbox.id;
label.textContent = collector.output.label;

// Add event listener to handle single-select behavior
checkbox.addEventListener('change', (event) => {
const target = event.target as HTMLInputElement;
updater(target.checked);
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

wrapper.appendChild(checkbox);
wrapper.appendChild(label);
containerDiv.appendChild(wrapper);

// Append the container to the form
formEl.appendChild(containerDiv);
}
3 changes: 3 additions & 0 deletions e2e/davinci-app/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import fidoComponent from './components/fido.js';
import qrCodeComponent from './components/qr-code.js';
import agreementComponent from './components/agreement.js';
import pollingComponent from './components/polling.js';
import booleanComponent from './components/boolean.js';

const loggerFn = {
error: () => {
Expand Down Expand Up @@ -298,6 +299,8 @@ const urlParams = new URLSearchParams(window.location.search);
singleValueComponent(formEl, collector, davinciClient.update(collector));
} else if (collector.type === 'MultiSelectCollector') {
multiValueComponent(formEl, collector, davinciClient.update(collector));
} else if (collector.type === 'ValidatedBooleanCollector') {
booleanComponent(formEl, collector, davinciClient.update(collector));
}
});

Expand Down
1 change: 1 addition & 0 deletions e2e/davinci-suites/src/form-fields.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ test('Should render form fields', async ({ page }) => {
'checkbox-field-key': ['option1 value', 'option2 value'],
'dropdown-field-key': 'dropdown-option2-value',
'radio-group-key': 'option2 value',
'single-checkbox-field': false,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
'combobox-field-key': ['option1 value', 'option3 value'],
'phone-field': {
phoneNumber: '1234567890',
Expand Down
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
},
"author": "ForgeRock",
"scripts": {
"api-report": "pnpm nx run-many -t api-report -p journey-client oidc-client device-client davinci-client",
"api-report:fix": "for pkg in packages/journey-client packages/oidc-client packages/device-client packages/davinci-client; do pnpm tsx tools/api-report/src/main.ts $pkg --fix; done && pnpm api-report",
"build": "nx sync && nx affected --target=build",
"changeset": "changeset",
"ci:release": "pnpm nx run-many -t build --no-agents --skip-nx-cache && pnpm publish -r --no-git-checks && changeset tag",
Expand All @@ -24,15 +26,13 @@
"create-package": "nx g @nx/js:library",
"format": "pnpm nx format:write",
"generate-docs": "typedoc",
"preinstall": "npx only-allow pnpm",
"postinstall": "ts-patch install",
"lint": "nx affected --target=lint",
"api-report": "pnpm nx run-many -t api-report -p journey-client oidc-client device-client davinci-client",
"api-report:fix": "for pkg in packages/journey-client packages/oidc-client packages/device-client packages/davinci-client; do pnpm tsx tools/api-report/src/main.ts $pkg --fix; done && pnpm api-report",
"mapping:validate": "pnpm tsx tools/interface-mapping-validator/src/main.ts",
"mapping:generate": "pnpm tsx tools/interface-mapping-validator/src/main.ts --generate",
"local-release": "pnpm ts-node tools/release/release.ts",
"mapping:generate": "pnpm tsx tools/interface-mapping-validator/src/main.ts --generate",
"mapping:validate": "pnpm tsx tools/interface-mapping-validator/src/main.ts",
"nx": "nx",
"postinstall": "ts-patch install",
"preinstall": "npx only-allow pnpm",
"prepare": "lefthook install",
"serve": "nx serve",
"test": "CI=true nx affected:test",
Expand All @@ -55,6 +55,7 @@
"@effect/cli": "catalog:effect",
"@eslint/eslintrc": "^3.0.0",
"@eslint/js": "~9.39.0",
"@evilmartians/lefthook": "^2.1.4",
"@nx/devkit": "22.6.5",
"@nx/eslint": "22.6.5",
"@nx/eslint-plugin": "22.6.5",
Expand Down Expand Up @@ -92,7 +93,6 @@
"eslint-plugin-playwright": "^2.0.0",
"eslint-plugin-prettier": "^5.2.3",
"fast-check": "^4.0.0",
"@evilmartians/lefthook": "^2.1.4",
"jiti": "2.6.1",
"jsdom": "27.4.0",
"jsonc-eslint-parser": "^2.1.0",
Expand All @@ -104,9 +104,9 @@
"shx": "^0.4.0",
"swc-loader": "0.2.7",
"ts-node": "10.9.2",
"tsx": "^4.20.0",
"ts-patch": "3.3.0",
"tslib": "^2.5.0",
"tsx": "^4.20.0",
"typedoc": "^0.27.4",
"typedoc-github-theme": "0.2.1",
"typedoc-plugin-rename-defaults": "^0.7.2",
Expand Down
Loading
Loading