Skip to content

Commit

Permalink
feat(elements): Add support for signOutOfOtherSessions checkbox (#3791)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcarpenter authored Jul 23, 2024
1 parent d465d70 commit 195fd46
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/tasty-brooms-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clerk/elements": patch
---

Add support for checkbox input usage and `signOutOfOtherSessions` functionality
6 changes: 4 additions & 2 deletions packages/elements/src/internals/machines/form/form.machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface FormMachineContext extends MachineContext {
}

export type FormMachineEvents =
| { type: 'FIELD.ADD'; field: Pick<FieldDetails, 'name' | 'value'> }
| { type: 'FIELD.ADD'; field: Pick<FieldDetails, 'name' | 'type' | 'value' | 'checked'> }
| { type: 'FIELD.REMOVE'; field: Pick<FieldDetails, 'name'> }
| {
type: 'MARK_AS_PROGRESSIVE';
Expand All @@ -35,7 +35,7 @@ export type FormMachineEvents =
| { type: 'UNMARK_AS_PROGRESSIVE' }
| {
type: 'FIELD.UPDATE';
field: Pick<FieldDetails, 'name' | 'value'>;
field: Pick<FieldDetails, 'name' | 'value' | 'checked'>;
}
| { type: 'ERRORS.SET'; error: any }
| { type: 'ERRORS.CLEAR' }
Expand Down Expand Up @@ -160,6 +160,8 @@ export const FormMachine = setup({
if (context.fields.has(event.field.name)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
context.fields.get(event.field.name)!.value = event.field.value;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
context.fields.get(event.field.name)!.checked = event.field.checked;
}

return context.fields;
Expand Down
2 changes: 2 additions & 0 deletions packages/elements/src/internals/machines/form/form.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export interface FeedbackOtherType extends FeedbackBase {

export type FieldDetails = {
name?: string;
type: React.HTMLInputTypeAttribute;
value?: string | readonly string[] | number;
checked?: boolean;
feedback?: FeedbackErrorType | FeedbackOtherType;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export const SignInResetPasswordMachine = setup({
attempt: fromPromise<SignInResource, { parent: SignInRouterMachineActorRef; fields: FormFields }>(
({ input: { fields, parent } }) => {
const password = (fields.get('password')?.value as string) || '';
return parent.getSnapshot().context.clerk.client.signIn.resetPassword({ password });
const signOutOfOtherSessions = fields.get('signOutOfOtherSessions')?.checked || false;
return parent.getSnapshot().context.clerk.client.signIn.resetPassword({ password, signOutOfOtherSessions });
},
),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { fieldsToSignUpParams } from '../fields-to-params';
describe('fieldsToSignUpParams', () => {
it('converts form fields to sign up params', () => {
const fields = new Map([
['firstName', { value: 'John' }],
['emailAddress', { value: '[email protected]' }],
['password', { value: 'password123' }],
['firstName', { type: 'text', value: 'John' }],
['emailAddress', { type: 'text', value: '[email protected]' }],
['password', { type: 'text', value: 'password123' }],
]);

const params = fieldsToSignUpParams(fields);
Expand All @@ -19,9 +19,9 @@ describe('fieldsToSignUpParams', () => {

it('ignores undefined values', () => {
const fields = new Map([
['firstName', { value: 'John' }],
['emailAddress', { value: undefined }],
['password', { value: 'password123' }],
['firstName', { type: 'text', value: 'John' }],
['emailAddress', { type: 'text', value: undefined }],
['password', { type: 'text', value: 'password123' }],
]);

const params = fieldsToSignUpParams(fields);
Expand All @@ -34,9 +34,9 @@ describe('fieldsToSignUpParams', () => {

it('ignores non-sign-up keys', () => {
const fields = new Map([
['firstName', { value: 'John' }],
['foo', { value: 'bar' }],
['bar', { value: 'foo' }],
['firstName', { type: 'text', value: 'John' }],
['foo', { type: 'text', value: 'bar' }],
['bar', { type: 'text', value: 'foo' }],
]);

const params = fieldsToSignUpParams(fields);
Expand Down
22 changes: 17 additions & 5 deletions packages/elements/src/react/common/form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ const useField = ({ name }: Partial<Pick<FieldDetails, 'name'>>) => {
const useInput = ({
name: inputName,
value: providedValue,
checked: providedChecked,
type: inputType,
onChange: onChangeProp,
onBlur: onBlurProp,
Expand Down Expand Up @@ -264,19 +265,27 @@ const useInput = ({
const prevValue = usePrevious(value);
const hasValue = Boolean(value);
const type = inputType ?? determineInputTypeFromName(rawName);
let nativeFieldType = type;
let shouldValidatePassword = false;

if (type === 'password' || type === 'text') {
shouldValidatePassword = Boolean((passthroughProps as PasswordInputProps).validatePassword);
}

if (nativeFieldType === 'otp' || nativeFieldType === 'backup_code') {
nativeFieldType = 'text';
}

// Register the field in the machine context
React.useEffect(() => {
if (!name) {
return;
}

ref.send({ type: 'FIELD.ADD', field: { name, value: providedValue } });
ref.send({
type: 'FIELD.ADD',
field: { name, type: nativeFieldType, value: providedValue, checked: providedChecked },
});

return () => ref.send({ type: 'FIELD.REMOVE', field: { name } });
}, [ref]); // eslint-disable-line react-hooks/exhaustive-deps
Expand All @@ -288,7 +297,7 @@ const useInput = ({
if (!name) {
return;
}
ref.send({ type: 'FIELD.UPDATE', field: { name, value: event.target.value } });
ref.send({ type: 'FIELD.UPDATE', field: { name, value: event.target.value, checked: event.target.checked } });
if (shouldValidatePassword) {
validatePassword(event.target.value);
}
Expand Down Expand Up @@ -321,10 +330,13 @@ const useInput = ({
return;
}

if (providedValue !== undefined) {
ref.send({ type: 'FIELD.UPDATE', field: { name, value: providedValue } });
if (
(type === 'checkbox' && providedChecked !== undefined) ||
(type !== 'checkbox' && providedValue !== undefined)
) {
ref.send({ type: 'FIELD.UPDATE', field: { name, value: providedValue, checked: providedChecked } });
}
}, [name, ref, providedValue]);
}, [name, type, ref, providedValue, providedChecked]);

// TODO: Implement clerk-js utils
const shouldBeHidden = false;
Expand Down

0 comments on commit 195fd46

Please sign in to comment.