Skip to content

Commit d5f4485

Browse files
authored
refactor(account-center): remove disabled state from CTA buttons (#8114)
Align account-center with the experience package pattern where CTA buttons are always clickable. Instead of disabling buttons when inputs are empty, validation errors are now shown inline on the inputs when the user clicks submit. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 51e05bf commit d5f4485

File tree

4 files changed

+36
-15
lines changed

4 files changed

+36
-15
lines changed

packages/account-center/src/components/PasswordVerification/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@ const PasswordVerification = ({ onBack, onSwitchMethod, hasAlternativeMethod }:
2525
const { setVerificationId, setToast } = useContext(PageContext);
2626
const { loading } = useContext(LoadingContext);
2727
const [password, setPassword] = useState('');
28+
const [passwordError, setPasswordError] = useState<string>();
2829
const asyncVerifyPassword = useApi(verifyPassword);
2930
const handleError = useErrorHandler();
3031

3132
const handleVerify = async (event?: FormEvent<HTMLFormElement>) => {
3233
event?.preventDefault();
34+
setPasswordError(undefined);
3335

34-
if (!password || loading) {
36+
if (!password) {
37+
setPasswordError(t('error.password_required'));
3538
return;
3639
}
3740

@@ -63,6 +66,8 @@ const PasswordVerification = ({ onBack, onSwitchMethod, hasAlternativeMethod }:
6366
autoComplete="current-password"
6467
label={t('input.password')}
6568
value={password}
69+
errorMessage={passwordError}
70+
isDanger={Boolean(passwordError)}
6671
onChange={(event) => {
6772
if (event.target instanceof HTMLInputElement) {
6873
setPassword(String(event.target.value));
@@ -73,7 +78,6 @@ const PasswordVerification = ({ onBack, onSwitchMethod, hasAlternativeMethod }:
7378
className={styles.submit}
7479
htmlType="submit"
7580
title="action.continue"
76-
disabled={!password || loading}
7781
isLoading={loading}
7882
/>
7983
{hasAlternativeMethod && (

packages/account-center/src/components/SetPassword/index.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,25 @@ const SetPassword = ({
3232
const { t } = useTranslation();
3333
const [newPassword, setNewPassword] = useState('');
3434
const [confirmPassword, setConfirmPassword] = useState('');
35+
const [newPasswordError, setNewPasswordError] = useState<string>();
3536
const [confirmPasswordError, setConfirmPasswordError] = useState<string>();
3637
const [isSubmitting, setIsSubmitting] = useState(false);
3738
const [showPassword, setShowPassword] = useState(false);
3839

3940
const handleSubmit = useCallback(
4041
async (event?: FormEvent<HTMLFormElement>) => {
4142
event?.preventDefault();
43+
setNewPasswordError(undefined);
4244
setConfirmPasswordError(undefined);
4345
clearErrorMessage?.();
4446

45-
if (!newPassword || !confirmPassword) {
47+
if (!newPassword) {
48+
setNewPasswordError(t('error.password_required'));
49+
return;
50+
}
51+
52+
if (!confirmPassword) {
53+
setConfirmPasswordError(t('error.password_required'));
4654
return;
4755
}
4856

@@ -74,8 +82,8 @@ const SetPassword = ({
7482
autoFocus={autoFocus}
7583
value={newPassword}
7684
maxLength={maxLength}
77-
isDanger={Boolean(errorMessage)}
78-
errorMessage={errorMessage}
85+
isDanger={Boolean(errorMessage ?? newPasswordError)}
86+
errorMessage={errorMessage ?? newPasswordError}
7987
isSuffixFocusVisible={Boolean(newPassword)}
8088
suffix={
8189
<IconButton
@@ -126,7 +134,6 @@ const SetPassword = ({
126134
title="action.save_password"
127135
htmlType="submit"
128136
isLoading={isSubmitting}
129-
disabled={!newPassword || !confirmPassword}
130137
/>
131138
</form>
132139
);

packages/account-center/src/pages/CodeFlow/IdentifierSendStep.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,17 @@ const IdentifierSendStep = ({
6565
const handleSend = useCallback(
6666
async (event?: FormEvent<HTMLFormElement>) => {
6767
event?.preventDefault();
68+
setLocalErrorMessage(undefined);
6869
clearErrorMessage?.();
6970

7071
const target = pendingValue.trim();
7172

72-
if (!target || loading) {
73+
if (!target) {
74+
setLocalErrorMessage(
75+
t('error.general_required', {
76+
types: [identifierType === 'email' ? t('input.email') : t('input.phone_number')],
77+
})
78+
);
7379
return;
7480
}
7581

@@ -79,9 +85,6 @@ const IdentifierSendStep = ({
7985
return;
8086
}
8187

82-
// Clear any previous validation error
83-
setLocalErrorMessage(undefined);
84-
8588
const [error, result] = await sendCodeRequest(target);
8689

8790
if (error) {
@@ -104,7 +107,6 @@ const IdentifierSendStep = ({
104107
clearErrorMessage,
105108
handleError,
106109
identifierType,
107-
loading,
108110
onCodeSent,
109111
onSendFailed,
110112
pendingValue,
@@ -142,7 +144,6 @@ const IdentifierSendStep = ({
142144
type="primary"
143145
htmlType="submit"
144146
className={styles.submit}
145-
disabled={!pendingValue || loading}
146147
isLoading={loading}
147148
/>
148149
</form>

packages/account-center/src/pages/Username/index.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const Username = () => {
3333
const defaultUsername = userInfo?.username ?? '';
3434
const [pendingUsername, setPendingUsername] = useState(defaultUsername);
3535
const [inputKey, setInputKey] = useState(defaultUsername);
36+
const [usernameError, setUsernameError] = useState<string>();
3637
const updateUsernameRequest = useApi(updateUsername);
3738
const handleError = useErrorHandler();
3839

@@ -56,20 +57,27 @@ const Username = () => {
5657

5758
const handleSubmit = async (event?: FormEvent) => {
5859
event?.preventDefault();
60+
setUsernameError(undefined);
5961

60-
if (!verificationId || loading) {
62+
if (!verificationId) {
6163
return;
6264
}
6365

6466
const username = pendingUsername.trim();
67+
68+
if (!username) {
69+
setUsernameError(t('error.username_required'));
70+
return;
71+
}
72+
6573
const validationError = validateUsername(username);
6674

6775
if (validationError) {
6876
const message =
6977
typeof validationError === 'string'
7078
? t(`error.${validationError}`)
7179
: t(`error.${validationError.code}`, validationError.data ?? {});
72-
setToast(message);
80+
setUsernameError(message);
7381
return;
7482
}
7583

@@ -116,6 +124,8 @@ const Username = () => {
116124
label={t('input.username')}
117125
defaultValue={pendingUsername}
118126
enabledTypes={[SignInIdentifier.Username]}
127+
errorMessage={usernameError}
128+
isDanger={Boolean(usernameError)}
119129
onChange={(inputValue) => {
120130
setPendingUsername(inputValue.value);
121131
}}
@@ -124,7 +134,6 @@ const Username = () => {
124134
className={styles.submit}
125135
title="action.continue"
126136
htmlType="submit"
127-
disabled={!pendingUsername.trim() || loading}
128137
isLoading={loading}
129138
/>
130139
</form>

0 commit comments

Comments
 (0)