Implement <ChangeCredential /> component across JavaScript React and Vue SDKs - #100
Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
d6c589c to
ee6c563
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved issues remain in credential handling, Vue i18n overrides, and sample behavior and accessibility.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds self-service credential updates across the JavaScript, React, and Vue SDKs, with account-page integrations in all quickstart samples.
Changes:
- Adds credential APIs, validation, error mapping, localization, and tests.
- Introduces React/Vue
ChangeCredentialcomponents and dropdown enhancements. - Replaces sample profile popups with account pages and security forms.
File summaries
| File | Description |
|---|---|
samples/vue/quickstart/src/style.css |
Vue account-page styling |
samples/vue/quickstart/src/pages/AccountPage.vue |
Vue account page |
samples/vue/quickstart/src/components/Nav.vue |
Vue account navigation |
samples/vue/quickstart/src/App.vue |
Vue route integration |
samples/react/quickstart/src/pages/AccountPage.jsx |
React account page |
samples/react/quickstart/src/components/Nav.jsx |
React account navigation |
samples/react/quickstart/src/App.jsx |
React route integration |
samples/react/quickstart/src/App.css |
React account styling |
samples/browser/quickstart/src/style.css |
Browser account styling |
samples/browser/quickstart/src/pages/account.js |
Browser account page |
samples/browser/quickstart/src/main.js |
Browser page mounting |
samples/browser/quickstart/src/components/profileFields.js |
Profile field helpers |
samples/browser/quickstart/src/components/nav.js |
Browser account navigation |
packages/vue/src/styles/injectStyles.ts |
Vue style registration |
packages/vue/src/index.ts |
Vue exports |
packages/vue/src/components/primitives/PasswordField/PasswordField.ts |
Vue password autocomplete |
packages/vue/src/components/presentation/user-dropdown/UserDropdown.ts |
Vue dropdown API wiring |
packages/vue/src/components/presentation/user-dropdown/UserDropdown.css.ts |
Vue dropdown styling |
packages/vue/src/components/presentation/user-dropdown/BaseUserDropdown.ts |
Vue dropdown rendering |
packages/vue/src/components/presentation/change-credential/ChangeCredential.ts |
Vue credential container |
packages/vue/src/components/presentation/change-credential/ChangeCredential.css.ts |
Vue credential styles |
packages/vue/src/components/presentation/change-credential/BaseChangeCredential.ts |
Vue credential form |
packages/vue/src/api/updateMeCredentials.ts |
Vue credential API wrapper |
packages/vue/src/__tests__/components/change-credential.test.ts |
Vue component tests |
packages/vue/src/__tests__/api/update-me-credentials.test.ts |
Vue API tests |
packages/react/src/index.ts |
React exports |
packages/react/src/components/primitives/PasswordField/PasswordField.tsx |
React password autocomplete |
packages/react/src/components/presentation/UserDropdown/UserDropdown.tsx |
React dropdown API wiring |
packages/react/src/components/presentation/UserDropdown/BaseUserDropdown.tsx |
React dropdown rendering |
packages/react/src/components/presentation/ChangeCredential/ChangeCredential.tsx |
React credential container |
packages/react/src/components/presentation/ChangeCredential/BaseChangeCredential.tsx |
React credential form |
packages/react/src/components/presentation/ChangeCredential/BaseChangeCredential.styles.ts |
React credential styles |
packages/react/src/components/presentation/ChangeCredential/__tests__/ChangeCredential.test.tsx |
React component tests |
packages/react/src/api/updateMeCredentials.ts |
React credential API wrapper |
packages/javascript/src/utils/supportsCredential.ts |
Credential availability detection |
packages/javascript/src/utils/resolveResourceEndpoint.ts |
Credential endpoint resolution |
packages/javascript/src/utils/resolveChangeCredentialPolicy.ts |
Policy resolution |
packages/javascript/src/utils/mapCredentialUpdateError.ts |
Credential error mapping |
packages/javascript/src/utils/evaluatePasswordPolicy.ts |
Password policy evaluation |
packages/javascript/src/utils/evaluateChangePasswordForm.ts |
Form evaluation |
packages/javascript/src/utils/__tests__/supportsCredential.test.ts |
Availability tests |
packages/javascript/src/utils/__tests__/resolveResourceEndpoint.test.ts |
Endpoint tests |
packages/javascript/src/utils/__tests__/resolveChangeCredentialPolicy.test.ts |
Policy tests |
packages/javascript/src/utils/__tests__/mapCredentialUpdateError.test.ts |
Error-mapping tests |
packages/javascript/src/utils/__tests__/evaluatePasswordPolicy.test.ts |
Password policy tests |
packages/javascript/src/utils/__tests__/evaluateChangePasswordForm.test.ts |
Form evaluation tests |
packages/javascript/src/models/config.ts |
Endpoint configuration |
packages/javascript/src/index.ts |
Core exports |
packages/javascript/src/i18n/translations/te-IN.ts |
Telugu translations |
packages/javascript/src/i18n/translations/ta-IN.ts |
Tamil translations |
packages/javascript/src/i18n/translations/si-LK.ts |
Sinhala translations |
packages/javascript/src/i18n/translations/pt-PT.ts |
Portuguese translations |
packages/javascript/src/i18n/translations/pt-BR.ts |
Brazilian Portuguese translations |
packages/javascript/src/i18n/translations/ja-JP.ts |
Japanese translations |
packages/javascript/src/i18n/translations/hi-IN.ts |
Hindi translations |
packages/javascript/src/i18n/translations/fr-FR.ts |
French translations |
packages/javascript/src/i18n/translations/en-US.ts |
English translations |
packages/javascript/src/i18n/models/i18n.ts |
Translation type declarations |
packages/javascript/src/constants/CredentialConstants.ts |
Credential constants |
packages/javascript/src/api/updateMeCredentials.ts |
Core credential API |
packages/javascript/src/api/__tests__/updateMeCredentials.test.ts |
Core API tests |
Review details
Suppressed comments (11)
packages/javascript/src/constants/CredentialConstants.ts:14
- This example has the same payload-shape mismatch as the public API: credential values must be plain strings, not
{newValue: ...}objects, becauseupdateMeCredentialsperforms theattributeswrapping. Consumers copying this snippet would send the wrong shape.
* payload: {[CredentialConstants.PASSWORD]: {newValue: newPassword}},
packages/javascript/src/utils/mapCredentialUpdateError.ts:64
- When a 400/other API response has an empty body,
ThunderIDAPIError.messageis an empty string. Returning that value makes the React/Vue callers'message ?? t(messageKey, ...)choose the empty string and render no error, even thoughmessageKeyis intended as the generic fallback. Normalize empty messages toundefinedin both ThunderIDError branches so the fallback translation is reachable.
return {field: 'newPassword', message: error.message, messageKey: GENERIC_MESSAGE_KEY};
packages/react/src/api/updateMeCredentials.ts:19
- The default transport is
FetchHttpClient, which uses the native Fetch API, not an axioshttp.requestwrapper. This public configuration description is misleading for consumers deciding whether to provide a custom fetcher.
* which is a wrapper around axios http.request
packages/vue/src/api/updateMeCredentials.ts:27
- This new
FetchHttpClient-to-Responseadapter duplicates the React implementation line-for-line. Since both framework SDKs depend on@thunderid/browser, the conversion and error handling should live in that shared layer rather than being maintained independently in Vue and React.
packages/vue/src/components/presentation/change-credential/BaseChangeCredential.ts:115 BaseChangeCredentialacceptspreferences, but its fallback translator is always the unconfigureduseI18n()result and never readsprops.preferences. A standalone base component usingpreferences.i18ntherefore silently ignores the override. Implement preference-aware translation for this headless variant or remove the prop and its promise.
packages/vue/src/components/presentation/change-credential/ChangeCredential.ts:85- The merged component preferences do not affect Vue translations:
tis created fromuseI18n()withoutresolvedPreferencesand is then passed to the base form. Consequentlypreferences.i18ncannot override any label or error message, unlike the React component. Resolve a preference-aware translator here, or remove the advertised per-component i18n override.
samples/browser/quickstart/src/pages/account.js:92 - Both visibility buttons are labelled
Show passwordeven for the PIN credential, and the label is never changed toHide ...after toggling. The PIN form therefore exposes an incorrect accessible name and the current visibility state is not announced; derive the label from the credential and update it when the input type changes.
samples/browser/quickstart/src/pages/account.js:109 - Unlike the React/Vue forms, these fields are not inside a
<form>and the submit control istype="button". Pressing Enter from either credential input therefore does nothing, which prevents keyboard users from submitting the account form. Use a form submit handler (or explicitly handle Enter) rather than relying only on the click listener.
samples/react/quickstart/src/App.css:722 - This selector hardcodes the default
thunderid-vendor prefix, butUserProfilebuilds its root class throughwithVendorCSSClassPrefix. If a consuming app overridesvendor, the selector stops matching and the Account page regains the extra profile styling. Target an app-owned class passed throughUserProfile(or otherwise avoid hardcoding the SDK vendor prefix).
samples/react/quickstart/src/pages/AccountPage.jsx:92 - The tab state is initialized from
requestedTabonly on the first render. Browser back/forward navigation or another query-string change re-renders this component but leaves the localtabunchanged, so the URL and visible tab can disagree. Use the current search parameter as the source of truth or synchronize the state when it changes.
samples/vue/quickstart/src/style.css:957 - This selector has the same vendor-prefix problem as the React sample: it only matches the default
thunderid-user-profileclass, while the component's class prefix follows the configured vendor. With a vendor override the card neutralization silently stops working; use an app-owned class instead of a hardcoded SDK prefix.
- Files reviewed: 61/61 changed files
- Comments generated: 11
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
1124da3 to
013b3f2
Compare
|
Does this PR adds new E2E's to make sure that the credential reset feature is working as expected? |
013b3f2 to
7d63abb
Compare
…SDKs Signed-off-by: janithjay <janithjayashan018@gmail.com>
7d63abb to
d1922dd
Compare
Update: E2E tests and API docs were added and linked to this PR. |
Purpose
There was no way for a signed-in user to update their own credentials from any ThunderID SDK.
BaseUserProfiledeliberately filters credential attributes out of the profile form, sopasswordwas unreachable from any SDK component, even though the backend already supportsPOST /users/me/update-credentials.This PR adds a
ChangeCredentialcomponent to@thunderid/javascript,@thunderid/react, and@thunderid/vue, and wires it into a real "My Account" page (Personal info + Security) in all three quickstart samples - React, Vue, and the vanilla browser sample.Approach
Core
updateMeCredentials-POST /users/me/update-credentials, payload wrapped underattributes. Returnsvoid(204 No Content).evaluatePasswordPolicy/resolveChangeCredentialPolicy/evaluateChangePasswordForm- client-side validation driven entirely by the schema's ownregex, no hardcoded rules. One function (evaluateChangePasswordForm) feeds both the checklist and the submit gate so they can't disagree.supportsCredential-trueunless the schema is known and the attribute isn't markedcredential: true.mapCredentialUpdateError,CredentialConstants.PASSWORD- error-to-field mapping and the default credential key.user.change_password.*/validation.password.patternstrings across all 9 locales.React / Vue
ChangeCredential(container) /BaseChangeCredential(presentational) - same split asUserProfile/BaseUserProfile. Two fields (new + confirm), no current-password field (server doesn't verify one yet).displayName, falling back to title-case.attributeprop selects which credential (defaults topassword); render once per credential to manage more than one.UserDropdowncan now redirect its profile action to a custom route (onManageProfile+ relabelable menu item) instead of only opening the built-in popup; Vue gainedshowTriggerLabel, matching React.updateMeCredentials's React/Vue wrappers now share one adapter (createHttpClientFetcher, moved into@thunderid/browser) instead of duplicating it.Samples
ChangeCredentialform.@thunderid/browser's core functions directly (includingevaluateChangePasswordForm). Unavailable state usesinert(not just CSS) so it's actually unreachable by keyboard; form fields have properlabel/idassociations.passwordonly -attribute="pin"works and is documented but isn't demoed in the quickstart itself.New interface
Related Issues
Related PRs
<ChangeCredential />reference docs for React and Vue SDKs thunderid#5397Checklist
breaking changelabel added.Security checks