-
Notifications
You must be signed in to change notification settings - Fork 16
feat(reactjs-todo-davinci): add new collectors (SDKS-5052) #117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
baacf37
ec9bb05
52306e8
68aa53a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| /* | ||
| * ping-sample-web-react-davinci | ||
| * | ||
| * boolean.js | ||
| * | ||
| * 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 React, { useState } from 'react'; | ||
| import { interpolateRichContent } from '../utilities/rich-content'; | ||
|
|
||
| export default function BooleanComponent({ collector, inputName, updater }) { | ||
| const [isChecked, setIsChecked] = useState(collector.output.value ?? false); | ||
|
|
||
| const fieldId = collector.output.key || `${inputName}-checkbox-field`; | ||
|
|
||
| const { richContent } = collector.output; | ||
| const label = richContent?.replacements?.length | ||
| ? interpolateRichContent(richContent) | ||
| : collector.output.label || ''; | ||
|
|
||
| const required = collector.input.validation?.some( | ||
| (validation) => validation.type === 'required' && validation.rule === true, | ||
| ); | ||
|
|
||
| const handleChange = (event) => { | ||
| const value = event.target.checked; | ||
| setIsChecked(value); | ||
| updater(value); | ||
| }; | ||
|
|
||
| return ( | ||
| <div className={'mb-5 form-check'}> | ||
| <label htmlFor={fieldId} className="form-label"> | ||
| {label} | ||
| </label> | ||
| <input | ||
| type="checkbox" | ||
| id={fieldId} | ||
| name={fieldId} | ||
| checked={isChecked} | ||
| onChange={handleChange} | ||
| className="form-check-input" | ||
| required={required} | ||
| /> | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| /* | ||
| * ping-sample-web-react-davinci | ||
| * | ||
| * device.js | ||
| * | ||
| * 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 React, { useEffect, useContext, useState } from 'react'; | ||
| import { ThemeContext } from '../../context/theme.context.js'; | ||
|
|
||
| export default function DeviceComponent({ collector, updater }) { | ||
| const [selectedDevice, setSelectedDevice] = useState( | ||
| collector.output.options?.[0]?.value ?? null, | ||
| ); | ||
| const theme = useContext(ThemeContext); | ||
|
|
||
| useEffect(() => { | ||
| if (selectedDevice) { | ||
| const result = updater(selectedDevice); | ||
| if (result && 'error' in result) { | ||
| console.error('Failed to update device', result.error); | ||
| } | ||
| } | ||
| }, [selectedDevice, updater]); | ||
|
|
||
| return ( | ||
| <div className="d-flex flex-column align-items-center mt-2 mb-2"> | ||
| <label | ||
| htmlFor="device-select" | ||
| className={`form-label cstm_subhead-text mb-4 fw-bold text-center ${theme.textMutedClass}`} | ||
| > | ||
| {collector.output.label || 'select an option'} | ||
| </label> | ||
| <select | ||
| id="device-select" | ||
| className="form-select form-select-lg w-100" | ||
|
Comment on lines
+32
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Use a unique Line 32 and Line 38 hard-code Suggested fix import React, { useEffect, useContext, useState } from 'react';
+import { useId } from 'react';
@@
export default function DeviceComponent({ collector, updater }) {
+ const deviceSelectId = useId();
@@
<label
- htmlFor="device-select"
+ htmlFor={deviceSelectId}
@@
<select
- id="device-select"
+ id={deviceSelectId}🤖 Prompt for AI Agents |
||
| value={selectedDevice} | ||
| onChange={(event) => setSelectedDevice(event.target.value)} | ||
| > | ||
| {collector.output.options?.map((option) => ( | ||
| <option key={option.value + option.label} value={option.value}> | ||
| {option.label} | ||
| </option> | ||
| ))} | ||
| </select> | ||
| <button type="submit" className="mt-5 justify-content w-100 btn btn-primary"> | ||
| Next | ||
| </button> | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,16 +9,21 @@ | |
| */ | ||
| import React, { Fragment, useContext, useEffect, useState } from 'react'; | ||
| import { useNavigate } from 'react-router-dom'; | ||
| import Readonly from './readonly.js'; | ||
| import ReadOnly from './readonly.js'; | ||
| import Text from './text.js'; | ||
| import Error from './error.js'; | ||
| import Password from './password.js'; | ||
| import SocialLoginButton from './social-login-button.js'; | ||
| import SubmitButton from './submit-button.js'; | ||
| import Protect from './protect.js'; | ||
| import ObjectValueComponent from './object-value.js'; | ||
| import PhoneNumberComponent from './phone-number.js'; | ||
| import DeviceComponent from './device.js'; | ||
| import SingleSelect from './single-select.js'; | ||
| import FlowLink from './flow-link.js'; | ||
| import FidoComponent from './fido.js'; | ||
| import PollingComponent from './polling.js'; | ||
| import BooleanComponent from './boolean.js'; | ||
| import QrCode from './qr-code.js'; | ||
| import Unknown from './unknown.js'; | ||
| import Alert from './alert.js'; | ||
| import KeyIcon from '../icons/key-icon'; | ||
|
|
@@ -30,7 +35,6 @@ import { ProtectContext } from '../../context/protect.context.js'; | |
| import { ThemeContext } from '../../context/theme.context.js'; | ||
| import useDavinci from './hooks/davinci.hook.js'; | ||
| import useOAuth from './hooks/oauth.hook.js'; | ||
| import FidoComponent from './fido.js'; | ||
|
|
||
| /** | ||
| * @function Form - React view for managing the user authentication journey | ||
|
|
@@ -58,7 +62,7 @@ export default function Form() { | |
| const [user, setCode] = useOAuth(); | ||
| const [ | ||
| { formName, formAction, node, collectors }, | ||
| { getError, setNext, startNewFlow, updater, externalIdp }, | ||
| { getError, setNext, startNewFlow, updater, pollStatus, externalIdp }, | ||
| ] = useDavinci(); | ||
|
|
||
| /** | ||
|
|
@@ -169,17 +173,35 @@ export default function Form() { | |
| case 'ERROR_DISPLAY': | ||
| return <Error key={idx + 'err'} getError={getError} />; | ||
| case 'ReadOnlyCollector': | ||
| return <Readonly key={idx + collectorName} collector={collector} />; | ||
| case 'RichTextCollector': | ||
| return <ReadOnly key={idx + collectorName} collector={collector} />; | ||
| case 'BooleanCollector': | ||
| case 'ValidatedBooleanCollector': | ||
| return ( | ||
| <BooleanComponent | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We aren't passing in the collectorName here but we use the prop in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ancheetah I think this is the only outstanding thing from my review.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nevermind, I see we removed the prop |
||
| key={idx + collectorName} | ||
| collector={collector} | ||
| updater={updater(collector)} | ||
| inputName={idx + collectorName} | ||
| /> | ||
| ); | ||
| case 'PhoneNumberCollector': | ||
| case 'PhoneNumberExtensionCollector': | ||
| return ( | ||
| <PhoneNumberComponent | ||
| inputName={idx + collectorName} | ||
| collector={collector} | ||
| updater={updater(collector)} | ||
| key={idx + collectorName} | ||
| /> | ||
| ); | ||
| case 'DeviceRegistrationCollector': | ||
| case 'DeviceAuthenticationCollector': | ||
| return ( | ||
| <ObjectValueComponent | ||
| inputName={collectorName} | ||
| <DeviceComponent | ||
| collector={collector} | ||
| updater={updater(collector)} | ||
| key={collectorName} | ||
| submitForm={setNext} | ||
| key={idx + collectorName} | ||
| /> | ||
| ); | ||
| case 'IdpCollector': | ||
|
|
@@ -201,8 +223,21 @@ export default function Form() { | |
| submitForm={setNext} | ||
| /> | ||
| ); | ||
| case 'PollingCollector': | ||
| return ( | ||
| <PollingComponent | ||
| collector={collector} | ||
| updater={updater(collector)} | ||
| pollStatus={pollStatus(collector)} | ||
| cacheKey={node?.cache?.key} | ||
| key={collectorName} | ||
|
Comment on lines
+232
to
+233
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the component use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| submitForm={setNext} | ||
| /> | ||
| ); | ||
| case 'ProtectCollector': | ||
| return <Protect collector={collector} key={collectorName} />; | ||
| case 'QrCodeCollector': | ||
| return <QrCode collector={collector} key={collectorName} />; | ||
| case 'SubmitCollector': | ||
| return <SubmitButton collector={collector} isLoading={isLoading} key={collectorName} />; | ||
| case 'FlowCollector': | ||
|
|
||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Guard
collector.inputbefore accessingvalidation.This can throw at runtime when
collector.inputis absent. Add optional chaining atinputlevel and default tofalseto keep rendering resilient.Proposed fix
🤖 Prompt for AI Agents