diff --git a/.changeset/proud-hairs-check.md b/.changeset/proud-hairs-check.md
new file mode 100644
index 0000000000..939ebe378f
--- /dev/null
+++ b/.changeset/proud-hairs-check.md
@@ -0,0 +1,5 @@
+---
+'@clerk/clerk-js': patch
+---
+
+Refactor of internal input group, password field, and checkbox inputs in forms.
diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifiedDomainPage.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifiedDomainPage.tsx
index 2dc351302b..c6283731d6 100644
--- a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifiedDomainPage.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifiedDomainPage.tsx
@@ -263,7 +263,7 @@ export const VerifiedDomainPage = withCardStateProvider(() => {
{allowsEdit && (
-
+
)}
diff --git a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifyDomainPage.tsx b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifyDomainPage.tsx
index 5429b09f53..c551ab7143 100644
--- a/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifyDomainPage.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/OrganizationProfile/VerifyDomainPage.tsx
@@ -151,11 +151,11 @@ export const VerifyDomainPage = withCardStateProvider(() => {
>
-
diff --git a/packages/clerk-js/src/ui.retheme/components/SignIn/ResetPassword.tsx b/packages/clerk-js/src/ui.retheme/components/SignIn/ResetPassword.tsx
index 27b80d34cb..8e6a6f9676 100644
--- a/packages/clerk-js/src/ui.retheme/components/SignIn/ResetPassword.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/SignIn/ResetPassword.tsx
@@ -88,7 +88,7 @@ export const _ResetPassword = () => {
return console.error(clerkInvalidFAPIResponse(status, supportEmail));
}
} catch (e) {
- handleError(e, [passwordField, confirmField], card.setError);
+ return handleError(e, [passwordField, confirmField], card.setError);
}
};
@@ -117,14 +117,14 @@ export const _ResetPassword = () => {
style={{ display: 'none' }}
/>
-
- {
if (e.target.value) {
@@ -136,7 +136,7 @@ export const _ResetPassword = () => {
{!requiresNewPassword && (
-
+
)}
-
diff --git a/packages/clerk-js/src/ui.retheme/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui.retheme/components/SignIn/SignInStart.tsx
index ff34cec6a0..d5b7c2a70e 100644
--- a/packages/clerk-js/src/ui.retheme/components/SignIn/SignInStart.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/SignIn/SignInStart.tsx
@@ -368,7 +368,7 @@ const InstantPasswordRow = ({ field }: { field?: FormControlState<'password'> })
!field.value && !autofilled ? { opacity: 0, height: 0, pointerEvents: 'none', marginTop: '-1rem' } : undefined
}
>
- {
)}
{shouldShow('password') && (
- {
/>
{user.passwordEnabled && (
-
)}
-
- {
if (e.target.value) {
@@ -170,12 +170,15 @@ export const PasswordPage = withCardStateProvider(() => {
}
return confirmField.props.onChange(e);
}}
+ isRequired
isDisabled={passwordEditDisabled}
/>
-
diff --git a/packages/clerk-js/src/ui.retheme/components/UserProfile/__tests__/PasswordPage.test.tsx b/packages/clerk-js/src/ui.retheme/components/UserProfile/__tests__/PasswordPage.test.tsx
index 26c1fd20e1..1d4f7998e5 100644
--- a/packages/clerk-js/src/ui.retheme/components/UserProfile/__tests__/PasswordPage.test.tsx
+++ b/packages/clerk-js/src/ui.retheme/components/UserProfile/__tests__/PasswordPage.test.tsx
@@ -79,7 +79,7 @@ describe('PasswordPage', () => {
expect(screen.getByLabelText(/new password/i)).toBeDisabled();
expect(screen.getByLabelText(/confirm password/i)).toBeDisabled();
- expect(screen.getByRole('checkbox', { name: 'Sign out of all other devices' })).toBeDisabled();
+ expect(screen.getByRole('checkbox', { name: /sign out of all other devices/i })).toBeDisabled();
expect(
screen.getByText(
@@ -113,7 +113,7 @@ describe('PasswordPage', () => {
expect(screen.getByLabelText(/new password/i)).not.toBeDisabled();
expect(screen.getByLabelText(/confirm password/i)).not.toBeDisabled();
- expect(screen.getByRole('checkbox', { name: 'Sign out of all other devices' })).not.toBeDisabled();
+ expect(screen.getByRole('checkbox', { name: /sign out of all other devices/i })).not.toBeDisabled();
expect(
screen.queryByText(
@@ -149,7 +149,7 @@ describe('PasswordPage', () => {
expect(screen.getByLabelText(/current password/i)).toBeDisabled();
expect(screen.getByLabelText(/new password/i)).toBeDisabled();
expect(screen.getByLabelText(/confirm password/i)).toBeDisabled();
- expect(screen.getByRole('checkbox', { name: 'Sign out of all other devices' })).toBeDisabled();
+ expect(screen.getByRole('checkbox', { name: /sign out of all other devices/i })).toBeDisabled();
expect(
screen.getByText(
@@ -185,7 +185,7 @@ describe('PasswordPage', () => {
expect(screen.getByLabelText(/current password/i)).not.toBeDisabled();
expect(screen.getByLabelText(/new password/i)).not.toBeDisabled();
expect(screen.getByLabelText(/confirm password/i)).not.toBeDisabled();
- expect(screen.getByRole('checkbox', { name: 'Sign out of all other devices' })).not.toBeDisabled();
+ expect(screen.getByRole('checkbox', { name: /sign out of all other devices/i })).not.toBeDisabled();
expect(
screen.queryByText(
diff --git a/packages/clerk-js/src/ui.retheme/elements/FieldControl.tsx b/packages/clerk-js/src/ui.retheme/elements/FieldControl.tsx
index cebfec3819..8ffecae983 100644
--- a/packages/clerk-js/src/ui.retheme/elements/FieldControl.tsx
+++ b/packages/clerk-js/src/ui.retheme/elements/FieldControl.tsx
@@ -22,7 +22,9 @@ import { useFormControlFeedback } from '../utils';
import { useCardState } from './contexts';
import type { FormFeedbackProps } from './FormControl';
import { FormFeedback } from './FormControl';
-import { RadioItem } from './RadioGroup';
+import { InputGroup } from './InputGroup';
+import { PasswordInput } from './PasswordInput';
+import { RadioItem, RadioLabel } from './RadioGroup';
type FormControlProps = Omit, 'label' | 'placeholder' | 'disabled' | 'required'> &
ReturnType>['props'];
@@ -37,7 +39,6 @@ const Root = (props: PropsWithChildren) => {
setInfo,
setSuccess,
setWarning,
- setHasPassedComplexity,
clearFeedback,
feedbackType,
feedback,
@@ -63,7 +64,6 @@ const Root = (props: PropsWithChildren) => {
setSuccess={setSuccess}
setWarning={setWarning}
setInfo={setInfo}
- setHasPassedComplexity={setHasPassedComplexity}
clearFeedback={clearFeedback}
sx={sx}
>
@@ -199,6 +199,66 @@ const FieldFeedback = (props: Pick) =>
);
};
+const PasswordInputElement = forwardRef((_, ref) => {
+ const { t } = useLocalizations();
+ const formField = useFormField();
+ const { placeholder, ...inputProps } = sanitizeInputProps(formField, [
+ 'validatePassword',
+ 'setError',
+ 'setWarning',
+ 'setSuccess',
+ 'setInfo',
+ 'setHasPassedComplexity',
+ ]);
+
+ return (
+ // @ts-expect-error
+
+ );
+});
+
+const CheckboxIndicator = forwardRef((_, ref) => {
+ const formField = useFormField();
+ const { placeholder, ...inputProps } = sanitizeInputProps(formField);
+
+ return (
+ ({
+ width: 'fit-content',
+ marginTop: t.space.$0x5,
+ })}
+ type='checkbox'
+ />
+ );
+});
+
+const CheckboxLabel = (props: { description?: string | LocalizationKey }) => {
+ const { label, id } = useFormField();
+
+ if (!label) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
const InputElement = forwardRef((_, ref) => {
const { t } = useLocalizations();
const formField = useFormField();
@@ -214,12 +274,40 @@ const InputElement = forwardRef((_, ref) => {
);
});
+const InputGroupElement = forwardRef<
+ HTMLInputElement,
+ {
+ groupPrefix?: string;
+ groupSuffix?: string;
+ }
+>((props, ref) => {
+ const { t } = useLocalizations();
+ const formField = useFormField();
+ const { placeholder, ...inputProps } = sanitizeInputProps(formField);
+
+ return (
+
+ );
+});
+
export const Field = {
Root: Root,
Label: FieldLabel,
LabelRow: FieldLabelRow,
Input: InputElement,
+ PasswordInput: PasswordInputElement,
+ InputGroup: InputGroupElement,
RadioItem: RadioItem,
+ CheckboxIndicator: CheckboxIndicator,
+ CheckboxLabel: CheckboxLabel,
Action: FieldAction,
AsOptional: FieldOptionalLabel,
LabelIcon: FieldLabelIcon,
diff --git a/packages/clerk-js/src/ui.retheme/elements/Form.tsx b/packages/clerk-js/src/ui.retheme/elements/Form.tsx
index aded9dfa07..caa4bf0198 100644
--- a/packages/clerk-js/src/ui.retheme/elements/Form.tsx
+++ b/packages/clerk-js/src/ui.retheme/elements/Form.tsx
@@ -156,6 +156,43 @@ const PlainInput = (props: CommonInputProps) => {
);
};
+const PasswordInput = (props: CommonInputProps) => {
+ return (
+
+
+
+ );
+};
+
+const InputGroup = (
+ props: CommonInputProps & {
+ groupPrefix?: string;
+ groupSuffix?: string;
+ },
+) => {
+ const { groupSuffix, groupPrefix, ...fieldProps } = props;
+ return (
+
+
+
+ );
+};
+
+const Checkbox = (
+ props: CommonFieldRootProps & {
+ description?: string | LocalizationKey;
+ },
+) => {
+ return (
+
+
+
+
+
+
+ );
+};
+
const RadioGroup = (
props: Omit, 'infoText' | 'type' | 'validatePassword' | 'label' | 'placeholder'>,
) => {
@@ -189,7 +226,10 @@ export const Form = {
*/
Control: FormControl,
PlainInput,
+ PasswordInput,
+ InputGroup,
RadioGroup,
+ Checkbox,
SubmitButton: FormSubmit,
ResetButton: FormReset,
};
diff --git a/packages/clerk-js/src/ui.retheme/elements/FormControl.tsx b/packages/clerk-js/src/ui.retheme/elements/FormControl.tsx
index b075ce8ccd..228e943801 100644
--- a/packages/clerk-js/src/ui.retheme/elements/FormControl.tsx
+++ b/packages/clerk-js/src/ui.retheme/elements/FormControl.tsx
@@ -22,15 +22,13 @@ import {
} from '../customizables';
import type { ElementDescriptor } from '../customizables/elementDescriptors';
import { usePrefersReducedMotion } from '../hooks';
+import { sanitizeInputProps } from '../primitives/hooks';
import type { PropsOfComponent, ThemableCssProp } from '../styledSystem';
import { animations } from '../styledSystem';
import type { FeedbackType } from '../utils';
import { useFormControlFeedback } from '../utils';
import { useCardState } from './contexts';
-import { InputGroup } from './InputGroup';
-import { PasswordInput } from './PasswordInput';
import { PhoneInput } from './PhoneInput';
-import { RadioGroup } from './RadioGroup';
type FormControlProps = Omit, 'label' | 'placeholder'> & {
id: FieldId;
@@ -43,45 +41,23 @@ type FormControlProps = Omit, 'label' | 'placehol
placeholder?: string | LocalizationKey;
actionLabel?: string | LocalizationKey;
icon?: React.ComponentType;
- validatePassword?: boolean;
setError: (error: string | ClerkAPIError | undefined) => void;
setWarning: (warning: string) => void;
setInfo: (info: string) => void;
setSuccess: (message: string) => void;
feedback: string;
feedbackType: FeedbackType;
- setHasPassedComplexity: (b: boolean) => void;
clearFeedback: () => void;
hasPassedComplexity: boolean;
infoText?: string | LocalizationKey;
- radioOptions?: {
- value: string;
- label: string | LocalizationKey;
- description?: string | LocalizationKey;
- }[];
- groupPreffix?: string;
- groupSuffix?: string;
};
// TODO: Convert this into a Component?
-const getInputElementForType = ({
- type,
- groupPreffix,
- groupSuffix,
-}: {
- type: FormControlProps['type'];
- groupPreffix: string | undefined;
- groupSuffix: string | undefined;
-}) => {
+const getInputElementForType = ({ type }: { type: FormControlProps['type'] }) => {
const CustomInputs = {
- password: PasswordInput,
tel: PhoneInput,
- radio: RadioGroup,
};
- if (groupPreffix || groupSuffix) {
- return InputGroup;
- }
if (!type) {
return Input;
}
@@ -283,50 +259,18 @@ export const FormControl = forwardRef {
- const propMap = {
- password: restInputProps,
- radio: {
- ...inputProps,
- radioOptions,
- },
- };
-
- if (groupPreffix || groupSuffix) {
- return {
- ...inputProps,
- groupPreffix,
- groupSuffix,
- };
- }
-
- if (!props.type) {
- return inputProps;
- }
- const type = props.type as keyof typeof propMap;
- return propMap[type] || inputProps;
- }, [restInputProps]);
-
const InputElement = getInputElementForType({
type: props.type,
- groupPreffix,
- groupSuffix,
});
- const isCheckbox = props.type === 'checkbox';
-
const { debounced: debouncedState } = useFormControlFeedback({ feedback, feedbackType, isFocused });
const ActionLabel = actionLabel ? (
@@ -400,13 +344,7 @@ export const FormControl = forwardRef {
- inputElementProps.onFocus?.(e);
- }}
- onBlur={e => {
- inputElementProps.onBlur?.(e);
- }}
+ {...sanitizedInputProps}
ref={ref}
placeholder={t(placeholder)}
/>
@@ -424,49 +362,25 @@ export const FormControl = forwardRef
- {isCheckbox ? (
-
- {Input}
- ({
- marginBottom: isCheckbox ? 0 : theme.space.$1,
- marginLeft: !isCheckbox ? 0 : theme.space.$1,
- })}
- >
- {FieldLabel}
- {Icon}
- {HintLabel}
- {ActionLabel}
-
-
- ) : (
- <>
- ({
- marginBottom: isCheckbox ? 0 : theme.space.$1,
- marginLeft: !isCheckbox ? 0 : theme.space.$1,
- })}
- >
- {FieldLabel}
- {Icon}
- {HintLabel}
- {ActionLabel}
-
- {Input}
- >
- )}
+ ({
+ marginBottom: theme.space.$1,
+ marginLeft: 0,
+ })}
+ >
+ {FieldLabel}
+ {Icon}
+ {HintLabel}
+ {ActionLabel}
+
+ {Input}
;
export const InputGroup = forwardRef<
HTMLInputElement,
InputGroupProps & {
- groupPreffix?: string;
+ groupPrefix?: string;
groupSuffix?: string;
}
>((props, ref) => {
- const { sx, groupPreffix, groupSuffix, ...rest } = props;
+ const { sx, groupPrefix, groupSuffix, ...rest } = props;
- const inputBorder = groupPreffix
+ const inputBorder = groupPrefix
? {
borderTopLeftRadius: '0',
borderBottomLeftRadius: '0',
@@ -47,7 +47,7 @@ export const InputGroup = forwardRef<
borderColor: theme.colors.$blackAlpha300, // we use this value in the Input primitive
})}
>
- {groupPreffix && {groupPreffix}}
+ {groupPrefix && {groupPrefix}}
& {
validatePassword?: boolean;
+ setError: (error: string | ClerkAPIError | undefined) => void;
+ setWarning: (warning: string) => void;
+ setSuccess: (message: string) => void;
+ setInfo: (info: string) => void;
+ setHasPassedComplexity: (b: boolean) => void;
};
export const PasswordInput = forwardRef((props, ref) => {
const [hidden, setHidden] = React.useState(true);
- const { id, onChange: onChangeProp, validatePassword: validatePasswordProp = false, ...rest } = props;
+ const {
+ id,
+ onChange: onChangeProp,
+ validatePassword: validatePasswordProp = false,
+ setInfo,
+ setSuccess,
+ setWarning,
+ setError,
+ setHasPassedComplexity,
+ ...rest
+ } = props;
const inputRef = useRef(null);
const [timeoutState, setTimeoutState] = useState | null>(null);
@@ -25,25 +40,23 @@ export const PasswordInput = forwardRef((p
userSettings: { passwordSettings },
} = useEnvironment();
- const formControlProps = useFormControl();
const { t } = useLocalizations();
const { validatePassword } = usePassword(
{ ...passwordSettings, validatePassword: validatePasswordProp },
{
- onValidationSuccess: () =>
- formControlProps?.setSuccess?.(t(localizationKeys('unstable__errors.zxcvbn.goodPassword'))),
- onValidationError: message => formControlProps?.setError?.(message),
- onValidationWarning: message => formControlProps?.setWarning?.(message),
+ onValidationSuccess: () => setSuccess(t(localizationKeys('unstable__errors.zxcvbn.goodPassword'))),
+ onValidationError: message => setError(message),
+ onValidationWarning: message => setWarning(message),
onValidationInfo: message => {
if (inputRef.current === document.activeElement) {
- formControlProps?.setInfo?.(message);
+ setInfo(message);
} else {
// Turn the suggestion into an error if not focused.
- formControlProps?.setError?.(message);
+ setError(message);
}
},
- onValidationComplexity: hasPassed => formControlProps?.setHasPassedComplexity?.(hasPassed),
+ onValidationComplexity: hasPassed => setHasPassedComplexity(hasPassed),
},
);
@@ -79,7 +92,7 @@ export const PasswordInput = forwardRef((p
// Call validate password because to calculate the new feedbackType as the element is now focused
validatePassword(e.target.value);
}}
- //@ts-expect-error
+ //@ts-expect-error Type mismatch between ForwardRef and RefObject due to null
ref={mergeRefs(ref, inputRef)}
type={hidden ? 'password' : 'text'}
sx={theme => ({ paddingRight: theme.space.$10 })}
diff --git a/packages/clerk-js/src/ui.retheme/elements/RadioGroup.tsx b/packages/clerk-js/src/ui.retheme/elements/RadioGroup.tsx
index 2d857c0ce1..0b2d4461a9 100644
--- a/packages/clerk-js/src/ui.retheme/elements/RadioGroup.tsx
+++ b/packages/clerk-js/src/ui.retheme/elements/RadioGroup.tsx
@@ -1,98 +1,8 @@
import { forwardRef, useId } from 'react';
import type { LocalizationKey } from '../customizables';
-import { Col, descriptors, Flex, FormLabel, Input, Text } from '../customizables';
+import { descriptors, Flex, FormLabel, Input, Text } from '../customizables';
import { sanitizeInputProps, useFormField } from '../primitives/hooks';
-import type { PropsOfComponent } from '../styledSystem';
-
-/**
- * @deprecated
- */
-export const RadioGroup = (
- props: PropsOfComponent & {
- radioOptions?: {
- value: string;
- label: string | LocalizationKey;
- description?: string | LocalizationKey;
- }[];
- },
-) => {
- const { radioOptions, ...rest } = props;
- return (
-
- {radioOptions?.map(r => (
-
- ))}
-
- );
-};
-
-/**
- * @deprecated
- */
-const RadioGroupItem = (props: {
- inputProps: PropsOfComponent;
- value: string;
- label: string | LocalizationKey;
- description?: string | LocalizationKey;
-}) => {
- const id = useId();
- return (
-
- ({
- width: 'fit-content',
- marginTop: t.space.$0x5,
- }),
- props.inputProps.sx,
- ]}
- type='radio'
- value={props.value}
- checked={props.value === props.inputProps.value}
- />
-
- ({
- padding: `${t.space.$none} ${t.space.$2}`,
- display: 'flex',
- flexDirection: 'column',
- })}
- >
-
-
- {props.description && (
-
- )}
-
-
- );
-};
const RadioIndicator = forwardRef((props, ref) => {
const formField = useFormField();
diff --git a/packages/clerk-js/src/ui.retheme/elements/__tests__/RadioGroup.test.tsx b/packages/clerk-js/src/ui.retheme/elements/__tests__/RadioGroup.test.tsx
index aafcca099d..dd0d8ea76c 100644
--- a/packages/clerk-js/src/ui.retheme/elements/__tests__/RadioGroup.test.tsx
+++ b/packages/clerk-js/src/ui.retheme/elements/__tests__/RadioGroup.test.tsx
@@ -29,28 +29,6 @@ const createField = (...params: Parameters) => {
};
};
-// TODO: Remove this once FormControl is no longer used
-const createFormControl = (...params: Parameters) => {
- const MockFieldWrapper = withCardStateProvider((props: Partial[0]>) => {
- const field = useFormControl(...params);
-
- return (
- <>
- {/* @ts-ignore*/}
-
-
- >
- );
- });
-
- return {
- Field: MockFieldWrapper,
- };
-};
-
describe('RadioGroup', () => {
it('renders the component', async () => {
const { wrapper } = await createFixtures();
@@ -204,148 +182,3 @@ describe('RadioGroup', () => {
});
});
});
-
-/**
- * This tests ensure that the deprecated FormControl and RadioGroup continue to behave the same and nothing broke during the refactoring.
- */
-describe('Form control as text', () => {
- it('renders the component', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', '', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- });
-
- const { getAllByRole } = render(, { wrapper });
-
- const radios = getAllByRole('radio');
- expect(radios[0]).toHaveAttribute('value', 'one');
- expect(radios[0].nextSibling).toHaveTextContent('One');
- expect(radios[1]).toHaveAttribute('value', 'two');
- expect(radios[1].nextSibling).toHaveTextContent('Two');
-
- radios.forEach(radio => {
- expect(radio).not.toBeChecked();
- expect(radio).toHaveAttribute('name', 'some-radio');
- expect(radio).not.toHaveAttribute('required');
- expect(radio).not.toHaveAttribute('disabled');
- });
-
- expect(radios[1]).not.toBeChecked();
- });
-
- it('renders the component with default value', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', 'two', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- });
-
- const { getAllByRole } = render(, { wrapper });
-
- const radios = getAllByRole('radio');
- expect(radios[0]).toHaveAttribute('value', 'one');
- expect(radios[0].nextSibling).toHaveTextContent('One');
- expect(radios[1]).toHaveAttribute('value', 'two');
- expect(radios[1].nextSibling).toHaveTextContent('Two');
-
- radios.forEach(radio => {
- expect(radio).toHaveAttribute('type', 'radio');
- expect(radio).not.toHaveAttribute('required');
- expect(radio).not.toHaveAttribute('disabled');
- expect(radio).toHaveAttribute('aria-required', 'false');
- expect(radio).toHaveAttribute('aria-disabled', 'false');
- });
-
- expect(radios[0]).not.toBeChecked();
- expect(radios[1]).toBeChecked();
- });
-
- it('disabled', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', 'two', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- });
-
- const { getAllByRole } = render(, { wrapper });
-
- const radios = getAllByRole('radio');
- radios.forEach(radio => {
- expect(radio).not.toHaveAttribute('required');
- expect(radio).toHaveAttribute('disabled');
- expect(radio).toHaveAttribute('aria-disabled', 'true');
- });
- });
-
- it('required', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', 'two', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- });
-
- const { getAllByRole } = render(, { wrapper });
-
- const radios = getAllByRole('radio');
- radios.forEach(radio => {
- expect(radio).toHaveAttribute('required');
- expect(radio).toHaveAttribute('aria-required', 'true');
- });
- });
-
- it('with error', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', 'two', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- });
-
- const { getAllByRole, getByRole, getByText } = render(, { wrapper });
-
- await act(() => userEvent.click(getByRole('button', { name: /set error/i })));
-
- await waitFor(() => {
- const radios = getAllByRole('radio');
- radios.forEach(radio => {
- expect(radio).toHaveAttribute('aria-invalid', 'true');
- expect(radio).toHaveAttribute('aria-describedby', 'error-some-radio');
- });
- expect(getByText('some error')).toBeInTheDocument();
- });
- });
-
- it('with info', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', '', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- infoText: 'some info',
- });
-
- const { getByLabelText, getByText } = render(, { wrapper });
-
- await act(() => fireEvent.focus(getByLabelText('One')));
- await waitFor(() => {
- expect(getByText('some info')).toBeInTheDocument();
- });
- });
-});
diff --git a/packages/clerk-js/src/ui.retheme/primitives/FormControl.tsx b/packages/clerk-js/src/ui.retheme/primitives/FormControl.tsx
index 9056e551f8..55dd23794d 100644
--- a/packages/clerk-js/src/ui.retheme/primitives/FormControl.tsx
+++ b/packages/clerk-js/src/ui.retheme/primitives/FormControl.tsx
@@ -10,18 +10,7 @@ import { FormControlContextProvider } from './hooks';
* Field.Root is just a Provider
*/
export const FormControl = (props: React.PropsWithChildren) => {
- const {
- hasError,
- id,
- isRequired,
- setError,
- setInfo,
- clearFeedback,
- setSuccess,
- setWarning,
- setHasPassedComplexity,
- ...rest
- } = props;
+ const { hasError, id, isRequired, setError, setInfo, clearFeedback, setSuccess, setWarning, ...rest } = props;
return (
) =>
clearFeedback,
setSuccess,
setWarning,
- setHasPassedComplexity,
}}
>
{props.children}
diff --git a/packages/clerk-js/src/ui.retheme/primitives/hooks/useFormControl.tsx b/packages/clerk-js/src/ui.retheme/primitives/hooks/useFormControl.tsx
index 98f10eea84..12caafe294 100644
--- a/packages/clerk-js/src/ui.retheme/primitives/hooks/useFormControl.tsx
+++ b/packages/clerk-js/src/ui.retheme/primitives/hooks/useFormControl.tsx
@@ -20,7 +20,6 @@ export type FormControlProps = {
setSuccess: (message: string) => void;
setWarning: (warning: string) => void;
setInfo: (info: string) => void;
- setHasPassedComplexity: (b: boolean) => void;
clearFeedback: () => void;
};
@@ -48,7 +47,6 @@ export const FormControlContextProvider = (props: React.PropsWithChildren{props.children};
};
diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx
index 2dc351302b..c6283731d6 100644
--- a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifiedDomainPage.tsx
@@ -263,7 +263,7 @@ export const VerifiedDomainPage = withCardStateProvider(() => {
{allowsEdit && (
-
+
)}
diff --git a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifyDomainPage.tsx b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifyDomainPage.tsx
index 5429b09f53..c551ab7143 100644
--- a/packages/clerk-js/src/ui/components/OrganizationProfile/VerifyDomainPage.tsx
+++ b/packages/clerk-js/src/ui/components/OrganizationProfile/VerifyDomainPage.tsx
@@ -151,11 +151,11 @@ export const VerifyDomainPage = withCardStateProvider(() => {
>
-
diff --git a/packages/clerk-js/src/ui/components/SignIn/ResetPassword.tsx b/packages/clerk-js/src/ui/components/SignIn/ResetPassword.tsx
index 27b80d34cb..8e6a6f9676 100644
--- a/packages/clerk-js/src/ui/components/SignIn/ResetPassword.tsx
+++ b/packages/clerk-js/src/ui/components/SignIn/ResetPassword.tsx
@@ -88,7 +88,7 @@ export const _ResetPassword = () => {
return console.error(clerkInvalidFAPIResponse(status, supportEmail));
}
} catch (e) {
- handleError(e, [passwordField, confirmField], card.setError);
+ return handleError(e, [passwordField, confirmField], card.setError);
}
};
@@ -117,14 +117,14 @@ export const _ResetPassword = () => {
style={{ display: 'none' }}
/>
-
- {
if (e.target.value) {
@@ -136,7 +136,7 @@ export const _ResetPassword = () => {
{!requiresNewPassword && (
-
+
)}
-
diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
index ff34cec6a0..d5b7c2a70e 100644
--- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
+++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx
@@ -368,7 +368,7 @@ const InstantPasswordRow = ({ field }: { field?: FormControlState<'password'> })
!field.value && !autofilled ? { opacity: 0, height: 0, pointerEvents: 'none', marginTop: '-1rem' } : undefined
}
>
- {
)}
{shouldShow('password') && (
- {
/>
{user.passwordEnabled && (
-
)}
-
- {
if (e.target.value) {
@@ -170,12 +170,15 @@ export const PasswordPage = withCardStateProvider(() => {
}
return confirmField.props.onChange(e);
}}
+ isRequired
isDisabled={passwordEditDisabled}
/>
-
diff --git a/packages/clerk-js/src/ui/components/UserProfile/__tests__/PasswordPage.test.tsx b/packages/clerk-js/src/ui/components/UserProfile/__tests__/PasswordPage.test.tsx
index c881829a3e..f0a5fbb0f3 100644
--- a/packages/clerk-js/src/ui/components/UserProfile/__tests__/PasswordPage.test.tsx
+++ b/packages/clerk-js/src/ui/components/UserProfile/__tests__/PasswordPage.test.tsx
@@ -77,7 +77,7 @@ describe('PasswordPage', () => {
expect(screen.getByLabelText(/new password/i)).toBeDisabled();
expect(screen.getByLabelText(/confirm password/i)).toBeDisabled();
- expect(screen.getByRole('checkbox', { name: 'Sign out of all other devices' })).toBeDisabled();
+ expect(screen.getByRole('checkbox', { name: /sign out of all other devices/i })).toBeDisabled();
expect(
screen.getByText(
@@ -111,7 +111,7 @@ describe('PasswordPage', () => {
expect(screen.getByLabelText(/new password/i)).not.toBeDisabled();
expect(screen.getByLabelText(/confirm password/i)).not.toBeDisabled();
- expect(screen.getByRole('checkbox', { name: 'Sign out of all other devices' })).not.toBeDisabled();
+ expect(screen.getByRole('checkbox', { name: /sign out of all other devices/i })).not.toBeDisabled();
expect(
screen.queryByText(
@@ -147,7 +147,7 @@ describe('PasswordPage', () => {
expect(screen.getByLabelText(/current password/i)).toBeDisabled();
expect(screen.getByLabelText(/new password/i)).toBeDisabled();
expect(screen.getByLabelText(/confirm password/i)).toBeDisabled();
- expect(screen.getByRole('checkbox', { name: 'Sign out of all other devices' })).toBeDisabled();
+ expect(screen.getByRole('checkbox', { name: /sign out of all other devices/i })).toBeDisabled();
expect(
screen.getByText(
@@ -183,7 +183,7 @@ describe('PasswordPage', () => {
expect(screen.getByLabelText(/current password/i)).not.toBeDisabled();
expect(screen.getByLabelText(/new password/i)).not.toBeDisabled();
expect(screen.getByLabelText(/confirm password/i)).not.toBeDisabled();
- expect(screen.getByRole('checkbox', { name: 'Sign out of all other devices' })).not.toBeDisabled();
+ expect(screen.getByRole('checkbox', { name: /sign out of all other devices/i })).not.toBeDisabled();
expect(
screen.queryByText(
diff --git a/packages/clerk-js/src/ui/elements/FieldControl.tsx b/packages/clerk-js/src/ui/elements/FieldControl.tsx
index cebfec3819..8ffecae983 100644
--- a/packages/clerk-js/src/ui/elements/FieldControl.tsx
+++ b/packages/clerk-js/src/ui/elements/FieldControl.tsx
@@ -22,7 +22,9 @@ import { useFormControlFeedback } from '../utils';
import { useCardState } from './contexts';
import type { FormFeedbackProps } from './FormControl';
import { FormFeedback } from './FormControl';
-import { RadioItem } from './RadioGroup';
+import { InputGroup } from './InputGroup';
+import { PasswordInput } from './PasswordInput';
+import { RadioItem, RadioLabel } from './RadioGroup';
type FormControlProps = Omit, 'label' | 'placeholder' | 'disabled' | 'required'> &
ReturnType>['props'];
@@ -37,7 +39,6 @@ const Root = (props: PropsWithChildren) => {
setInfo,
setSuccess,
setWarning,
- setHasPassedComplexity,
clearFeedback,
feedbackType,
feedback,
@@ -63,7 +64,6 @@ const Root = (props: PropsWithChildren) => {
setSuccess={setSuccess}
setWarning={setWarning}
setInfo={setInfo}
- setHasPassedComplexity={setHasPassedComplexity}
clearFeedback={clearFeedback}
sx={sx}
>
@@ -199,6 +199,66 @@ const FieldFeedback = (props: Pick) =>
);
};
+const PasswordInputElement = forwardRef((_, ref) => {
+ const { t } = useLocalizations();
+ const formField = useFormField();
+ const { placeholder, ...inputProps } = sanitizeInputProps(formField, [
+ 'validatePassword',
+ 'setError',
+ 'setWarning',
+ 'setSuccess',
+ 'setInfo',
+ 'setHasPassedComplexity',
+ ]);
+
+ return (
+ // @ts-expect-error
+
+ );
+});
+
+const CheckboxIndicator = forwardRef((_, ref) => {
+ const formField = useFormField();
+ const { placeholder, ...inputProps } = sanitizeInputProps(formField);
+
+ return (
+ ({
+ width: 'fit-content',
+ marginTop: t.space.$0x5,
+ })}
+ type='checkbox'
+ />
+ );
+});
+
+const CheckboxLabel = (props: { description?: string | LocalizationKey }) => {
+ const { label, id } = useFormField();
+
+ if (!label) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
const InputElement = forwardRef((_, ref) => {
const { t } = useLocalizations();
const formField = useFormField();
@@ -214,12 +274,40 @@ const InputElement = forwardRef((_, ref) => {
);
});
+const InputGroupElement = forwardRef<
+ HTMLInputElement,
+ {
+ groupPrefix?: string;
+ groupSuffix?: string;
+ }
+>((props, ref) => {
+ const { t } = useLocalizations();
+ const formField = useFormField();
+ const { placeholder, ...inputProps } = sanitizeInputProps(formField);
+
+ return (
+
+ );
+});
+
export const Field = {
Root: Root,
Label: FieldLabel,
LabelRow: FieldLabelRow,
Input: InputElement,
+ PasswordInput: PasswordInputElement,
+ InputGroup: InputGroupElement,
RadioItem: RadioItem,
+ CheckboxIndicator: CheckboxIndicator,
+ CheckboxLabel: CheckboxLabel,
Action: FieldAction,
AsOptional: FieldOptionalLabel,
LabelIcon: FieldLabelIcon,
diff --git a/packages/clerk-js/src/ui/elements/Form.tsx b/packages/clerk-js/src/ui/elements/Form.tsx
index aded9dfa07..caa4bf0198 100644
--- a/packages/clerk-js/src/ui/elements/Form.tsx
+++ b/packages/clerk-js/src/ui/elements/Form.tsx
@@ -156,6 +156,43 @@ const PlainInput = (props: CommonInputProps) => {
);
};
+const PasswordInput = (props: CommonInputProps) => {
+ return (
+
+
+
+ );
+};
+
+const InputGroup = (
+ props: CommonInputProps & {
+ groupPrefix?: string;
+ groupSuffix?: string;
+ },
+) => {
+ const { groupSuffix, groupPrefix, ...fieldProps } = props;
+ return (
+
+
+
+ );
+};
+
+const Checkbox = (
+ props: CommonFieldRootProps & {
+ description?: string | LocalizationKey;
+ },
+) => {
+ return (
+
+
+
+
+
+
+ );
+};
+
const RadioGroup = (
props: Omit, 'infoText' | 'type' | 'validatePassword' | 'label' | 'placeholder'>,
) => {
@@ -189,7 +226,10 @@ export const Form = {
*/
Control: FormControl,
PlainInput,
+ PasswordInput,
+ InputGroup,
RadioGroup,
+ Checkbox,
SubmitButton: FormSubmit,
ResetButton: FormReset,
};
diff --git a/packages/clerk-js/src/ui/elements/FormControl.tsx b/packages/clerk-js/src/ui/elements/FormControl.tsx
index b075ce8ccd..228e943801 100644
--- a/packages/clerk-js/src/ui/elements/FormControl.tsx
+++ b/packages/clerk-js/src/ui/elements/FormControl.tsx
@@ -22,15 +22,13 @@ import {
} from '../customizables';
import type { ElementDescriptor } from '../customizables/elementDescriptors';
import { usePrefersReducedMotion } from '../hooks';
+import { sanitizeInputProps } from '../primitives/hooks';
import type { PropsOfComponent, ThemableCssProp } from '../styledSystem';
import { animations } from '../styledSystem';
import type { FeedbackType } from '../utils';
import { useFormControlFeedback } from '../utils';
import { useCardState } from './contexts';
-import { InputGroup } from './InputGroup';
-import { PasswordInput } from './PasswordInput';
import { PhoneInput } from './PhoneInput';
-import { RadioGroup } from './RadioGroup';
type FormControlProps = Omit, 'label' | 'placeholder'> & {
id: FieldId;
@@ -43,45 +41,23 @@ type FormControlProps = Omit, 'label' | 'placehol
placeholder?: string | LocalizationKey;
actionLabel?: string | LocalizationKey;
icon?: React.ComponentType;
- validatePassword?: boolean;
setError: (error: string | ClerkAPIError | undefined) => void;
setWarning: (warning: string) => void;
setInfo: (info: string) => void;
setSuccess: (message: string) => void;
feedback: string;
feedbackType: FeedbackType;
- setHasPassedComplexity: (b: boolean) => void;
clearFeedback: () => void;
hasPassedComplexity: boolean;
infoText?: string | LocalizationKey;
- radioOptions?: {
- value: string;
- label: string | LocalizationKey;
- description?: string | LocalizationKey;
- }[];
- groupPreffix?: string;
- groupSuffix?: string;
};
// TODO: Convert this into a Component?
-const getInputElementForType = ({
- type,
- groupPreffix,
- groupSuffix,
-}: {
- type: FormControlProps['type'];
- groupPreffix: string | undefined;
- groupSuffix: string | undefined;
-}) => {
+const getInputElementForType = ({ type }: { type: FormControlProps['type'] }) => {
const CustomInputs = {
- password: PasswordInput,
tel: PhoneInput,
- radio: RadioGroup,
};
- if (groupPreffix || groupSuffix) {
- return InputGroup;
- }
if (!type) {
return Input;
}
@@ -283,50 +259,18 @@ export const FormControl = forwardRef {
- const propMap = {
- password: restInputProps,
- radio: {
- ...inputProps,
- radioOptions,
- },
- };
-
- if (groupPreffix || groupSuffix) {
- return {
- ...inputProps,
- groupPreffix,
- groupSuffix,
- };
- }
-
- if (!props.type) {
- return inputProps;
- }
- const type = props.type as keyof typeof propMap;
- return propMap[type] || inputProps;
- }, [restInputProps]);
-
const InputElement = getInputElementForType({
type: props.type,
- groupPreffix,
- groupSuffix,
});
- const isCheckbox = props.type === 'checkbox';
-
const { debounced: debouncedState } = useFormControlFeedback({ feedback, feedbackType, isFocused });
const ActionLabel = actionLabel ? (
@@ -400,13 +344,7 @@ export const FormControl = forwardRef {
- inputElementProps.onFocus?.(e);
- }}
- onBlur={e => {
- inputElementProps.onBlur?.(e);
- }}
+ {...sanitizedInputProps}
ref={ref}
placeholder={t(placeholder)}
/>
@@ -424,49 +362,25 @@ export const FormControl = forwardRef
- {isCheckbox ? (
-
- {Input}
- ({
- marginBottom: isCheckbox ? 0 : theme.space.$1,
- marginLeft: !isCheckbox ? 0 : theme.space.$1,
- })}
- >
- {FieldLabel}
- {Icon}
- {HintLabel}
- {ActionLabel}
-
-
- ) : (
- <>
- ({
- marginBottom: isCheckbox ? 0 : theme.space.$1,
- marginLeft: !isCheckbox ? 0 : theme.space.$1,
- })}
- >
- {FieldLabel}
- {Icon}
- {HintLabel}
- {ActionLabel}
-
- {Input}
- >
- )}
+ ({
+ marginBottom: theme.space.$1,
+ marginLeft: 0,
+ })}
+ >
+ {FieldLabel}
+ {Icon}
+ {HintLabel}
+ {ActionLabel}
+
+ {Input}
;
export const InputGroup = forwardRef<
HTMLInputElement,
InputGroupProps & {
- groupPreffix?: string;
+ groupPrefix?: string;
groupSuffix?: string;
}
>((props, ref) => {
- const { sx, groupPreffix, groupSuffix, ...rest } = props;
+ const { sx, groupPrefix, groupSuffix, ...rest } = props;
- const inputBorder = groupPreffix
+ const inputBorder = groupPrefix
? {
borderTopLeftRadius: '0',
borderBottomLeftRadius: '0',
@@ -47,7 +47,7 @@ export const InputGroup = forwardRef<
borderColor: theme.colors.$blackAlpha300, // we use this value in the Input primitive
})}
>
- {groupPreffix && {groupPreffix}}
+ {groupPrefix && {groupPrefix}}
& {
validatePassword?: boolean;
+ setError: (error: string | ClerkAPIError | undefined) => void;
+ setWarning: (warning: string) => void;
+ setSuccess: (message: string) => void;
+ setInfo: (info: string) => void;
+ setHasPassedComplexity: (b: boolean) => void;
};
export const PasswordInput = forwardRef((props, ref) => {
const [hidden, setHidden] = useState(true);
- const { id, onChange: onChangeProp, validatePassword: validatePasswordProp = false, ...rest } = props;
+ const {
+ id,
+ onChange: onChangeProp,
+ validatePassword: validatePasswordProp = false,
+ setInfo,
+ setSuccess,
+ setWarning,
+ setError,
+ setHasPassedComplexity,
+ ...rest
+ } = props;
const inputRef = useRef(null);
const [timeoutState, setTimeoutState] = useState | null>(null);
@@ -25,25 +40,23 @@ export const PasswordInput = forwardRef((p
userSettings: { passwordSettings },
} = useEnvironment();
- const formControlProps = useFormControl();
const { t } = useLocalizations();
const { validatePassword } = usePassword(
{ ...passwordSettings, validatePassword: validatePasswordProp },
{
- onValidationSuccess: () =>
- formControlProps?.setSuccess?.(t(localizationKeys('unstable__errors.zxcvbn.goodPassword'))),
- onValidationError: message => formControlProps?.setError?.(message),
- onValidationWarning: message => formControlProps?.setWarning?.(message),
+ onValidationSuccess: () => setSuccess(t(localizationKeys('unstable__errors.zxcvbn.goodPassword'))),
+ onValidationError: message => setError(message),
+ onValidationWarning: message => setWarning(message),
onValidationInfo: message => {
if (inputRef.current === document.activeElement) {
- formControlProps?.setInfo?.(message);
+ setInfo(message);
} else {
// Turn the suggestion into an error if not focused.
- formControlProps?.setError?.(message);
+ setError(message);
}
},
- onValidationComplexity: hasPassed => formControlProps?.setHasPassedComplexity?.(hasPassed),
+ onValidationComplexity: hasPassed => setHasPassedComplexity(hasPassed),
},
);
@@ -79,7 +92,7 @@ export const PasswordInput = forwardRef((p
// Call validate password because to calculate the new feedbackType as the element is now focused
validatePassword(e.target.value);
}}
- //@ts-expect-error
+ //@ts-expect-error Type mismatch between ForwardRef and RefObject due to null
ref={mergeRefs(ref, inputRef)}
type={hidden ? 'password' : 'text'}
sx={theme => ({ paddingRight: theme.space.$10 })}
diff --git a/packages/clerk-js/src/ui/elements/RadioGroup.tsx b/packages/clerk-js/src/ui/elements/RadioGroup.tsx
index 2d857c0ce1..0b2d4461a9 100644
--- a/packages/clerk-js/src/ui/elements/RadioGroup.tsx
+++ b/packages/clerk-js/src/ui/elements/RadioGroup.tsx
@@ -1,98 +1,8 @@
import { forwardRef, useId } from 'react';
import type { LocalizationKey } from '../customizables';
-import { Col, descriptors, Flex, FormLabel, Input, Text } from '../customizables';
+import { descriptors, Flex, FormLabel, Input, Text } from '../customizables';
import { sanitizeInputProps, useFormField } from '../primitives/hooks';
-import type { PropsOfComponent } from '../styledSystem';
-
-/**
- * @deprecated
- */
-export const RadioGroup = (
- props: PropsOfComponent & {
- radioOptions?: {
- value: string;
- label: string | LocalizationKey;
- description?: string | LocalizationKey;
- }[];
- },
-) => {
- const { radioOptions, ...rest } = props;
- return (
-
- {radioOptions?.map(r => (
-
- ))}
-
- );
-};
-
-/**
- * @deprecated
- */
-const RadioGroupItem = (props: {
- inputProps: PropsOfComponent;
- value: string;
- label: string | LocalizationKey;
- description?: string | LocalizationKey;
-}) => {
- const id = useId();
- return (
-
- ({
- width: 'fit-content',
- marginTop: t.space.$0x5,
- }),
- props.inputProps.sx,
- ]}
- type='radio'
- value={props.value}
- checked={props.value === props.inputProps.value}
- />
-
- ({
- padding: `${t.space.$none} ${t.space.$2}`,
- display: 'flex',
- flexDirection: 'column',
- })}
- >
-
-
- {props.description && (
-
- )}
-
-
- );
-};
const RadioIndicator = forwardRef((props, ref) => {
const formField = useFormField();
diff --git a/packages/clerk-js/src/ui/elements/__tests__/RadioGroup.test.tsx b/packages/clerk-js/src/ui/elements/__tests__/RadioGroup.test.tsx
index aafcca099d..dd0d8ea76c 100644
--- a/packages/clerk-js/src/ui/elements/__tests__/RadioGroup.test.tsx
+++ b/packages/clerk-js/src/ui/elements/__tests__/RadioGroup.test.tsx
@@ -29,28 +29,6 @@ const createField = (...params: Parameters) => {
};
};
-// TODO: Remove this once FormControl is no longer used
-const createFormControl = (...params: Parameters) => {
- const MockFieldWrapper = withCardStateProvider((props: Partial[0]>) => {
- const field = useFormControl(...params);
-
- return (
- <>
- {/* @ts-ignore*/}
-
-
- >
- );
- });
-
- return {
- Field: MockFieldWrapper,
- };
-};
-
describe('RadioGroup', () => {
it('renders the component', async () => {
const { wrapper } = await createFixtures();
@@ -204,148 +182,3 @@ describe('RadioGroup', () => {
});
});
});
-
-/**
- * This tests ensure that the deprecated FormControl and RadioGroup continue to behave the same and nothing broke during the refactoring.
- */
-describe('Form control as text', () => {
- it('renders the component', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', '', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- });
-
- const { getAllByRole } = render(, { wrapper });
-
- const radios = getAllByRole('radio');
- expect(radios[0]).toHaveAttribute('value', 'one');
- expect(radios[0].nextSibling).toHaveTextContent('One');
- expect(radios[1]).toHaveAttribute('value', 'two');
- expect(radios[1].nextSibling).toHaveTextContent('Two');
-
- radios.forEach(radio => {
- expect(radio).not.toBeChecked();
- expect(radio).toHaveAttribute('name', 'some-radio');
- expect(radio).not.toHaveAttribute('required');
- expect(radio).not.toHaveAttribute('disabled');
- });
-
- expect(radios[1]).not.toBeChecked();
- });
-
- it('renders the component with default value', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', 'two', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- });
-
- const { getAllByRole } = render(, { wrapper });
-
- const radios = getAllByRole('radio');
- expect(radios[0]).toHaveAttribute('value', 'one');
- expect(radios[0].nextSibling).toHaveTextContent('One');
- expect(radios[1]).toHaveAttribute('value', 'two');
- expect(radios[1].nextSibling).toHaveTextContent('Two');
-
- radios.forEach(radio => {
- expect(radio).toHaveAttribute('type', 'radio');
- expect(radio).not.toHaveAttribute('required');
- expect(radio).not.toHaveAttribute('disabled');
- expect(radio).toHaveAttribute('aria-required', 'false');
- expect(radio).toHaveAttribute('aria-disabled', 'false');
- });
-
- expect(radios[0]).not.toBeChecked();
- expect(radios[1]).toBeChecked();
- });
-
- it('disabled', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', 'two', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- });
-
- const { getAllByRole } = render(, { wrapper });
-
- const radios = getAllByRole('radio');
- radios.forEach(radio => {
- expect(radio).not.toHaveAttribute('required');
- expect(radio).toHaveAttribute('disabled');
- expect(radio).toHaveAttribute('aria-disabled', 'true');
- });
- });
-
- it('required', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', 'two', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- });
-
- const { getAllByRole } = render(, { wrapper });
-
- const radios = getAllByRole('radio');
- radios.forEach(radio => {
- expect(radio).toHaveAttribute('required');
- expect(radio).toHaveAttribute('aria-required', 'true');
- });
- });
-
- it('with error', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', 'two', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- });
-
- const { getAllByRole, getByRole, getByText } = render(, { wrapper });
-
- await act(() => userEvent.click(getByRole('button', { name: /set error/i })));
-
- await waitFor(() => {
- const radios = getAllByRole('radio');
- radios.forEach(radio => {
- expect(radio).toHaveAttribute('aria-invalid', 'true');
- expect(radio).toHaveAttribute('aria-describedby', 'error-some-radio');
- });
- expect(getByText('some error')).toBeInTheDocument();
- });
- });
-
- it('with info', async () => {
- const { wrapper } = await createFixtures();
- const { Field } = createFormControl('some-radio', '', {
- type: 'radio',
- radioOptions: [
- { value: 'one', label: 'One' },
- { value: 'two', label: 'Two' },
- ],
- infoText: 'some info',
- });
-
- const { getByLabelText, getByText } = render(, { wrapper });
-
- await act(() => fireEvent.focus(getByLabelText('One')));
- await waitFor(() => {
- expect(getByText('some info')).toBeInTheDocument();
- });
- });
-});
diff --git a/packages/clerk-js/src/ui/primitives/FormControl.tsx b/packages/clerk-js/src/ui/primitives/FormControl.tsx
index 9056e551f8..55dd23794d 100644
--- a/packages/clerk-js/src/ui/primitives/FormControl.tsx
+++ b/packages/clerk-js/src/ui/primitives/FormControl.tsx
@@ -10,18 +10,7 @@ import { FormControlContextProvider } from './hooks';
* Field.Root is just a Provider
*/
export const FormControl = (props: React.PropsWithChildren) => {
- const {
- hasError,
- id,
- isRequired,
- setError,
- setInfo,
- clearFeedback,
- setSuccess,
- setWarning,
- setHasPassedComplexity,
- ...rest
- } = props;
+ const { hasError, id, isRequired, setError, setInfo, clearFeedback, setSuccess, setWarning, ...rest } = props;
return (
) =>
clearFeedback,
setSuccess,
setWarning,
- setHasPassedComplexity,
}}
>
{props.children}
diff --git a/packages/clerk-js/src/ui/primitives/hooks/useFormControl.tsx b/packages/clerk-js/src/ui/primitives/hooks/useFormControl.tsx
index 98f10eea84..12caafe294 100644
--- a/packages/clerk-js/src/ui/primitives/hooks/useFormControl.tsx
+++ b/packages/clerk-js/src/ui/primitives/hooks/useFormControl.tsx
@@ -20,7 +20,6 @@ export type FormControlProps = {
setSuccess: (message: string) => void;
setWarning: (warning: string) => void;
setInfo: (info: string) => void;
- setHasPassedComplexity: (b: boolean) => void;
clearFeedback: () => void;
};
@@ -48,7 +47,6 @@ export const FormControlContextProvider = (props: React.PropsWithChildren{props.children};
};