mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-06-22 21:08:12 +08:00
feat(client): change the save buttons from disabled to aria-disabled (#49216)
* feat(client): change the buttons from disabled to aria-disabled Co-authored-by: Bruce Blaser <bbsmooth@gmail.com> * stop the API call when the validation isn't true * use aria disabled instead of disabled on the test? * use have.attr to check for aria disabled * hide the button when it's disabled Co-authored-by: Bruce B <bbsmooth@gmail.com> * update Privacy Settings save button --------- Co-authored-by: Bruce Blaser <bbsmooth@gmail.com>
This commit is contained in:
parent
0f2c524e03
commit
251aecc58f
@ -4,6 +4,26 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:is(
|
||||
.about-settings,
|
||||
.privacy-settings,
|
||||
.email-settings,
|
||||
#usernameSettings,
|
||||
#camper-identity,
|
||||
#internet-presence,
|
||||
#portfolio-items,
|
||||
#honesty-policy
|
||||
)
|
||||
:is(
|
||||
button[aria-disabled='true'],
|
||||
button[aria-disabled='true']:is(:focus, :hover)
|
||||
) {
|
||||
background-color: var(--quaternary-background);
|
||||
color: var(--secondary-color);
|
||||
opacity: 0.65;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.toggle-not-active {
|
||||
background-color: var(--quaternary-background);
|
||||
color: var(--secondary-color);
|
||||
|
||||
@ -118,7 +118,7 @@ class AboutSettings extends Component<AboutProps, AboutState> {
|
||||
e.preventDefault();
|
||||
const { formValues } = this.state;
|
||||
const { submitNewAbout } = this.props;
|
||||
if (this.state.isPictureUrlValid === true) {
|
||||
if (this.state.isPictureUrlValid === true && !this.isFormPristine()) {
|
||||
return this.setState({ formClicked: true }, () =>
|
||||
submitNewAbout(formValues)
|
||||
);
|
||||
@ -259,7 +259,10 @@ class AboutSettings extends Component<AboutProps, AboutState> {
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
<BlockSaveButton disabled={this.isFormPristine()}>
|
||||
<BlockSaveButton
|
||||
aria-disabled={this.isFormPristine()}
|
||||
{...(this.isFormPristine() && { tabindex: -1 })}
|
||||
>
|
||||
{t('buttons.save')}{' '}
|
||||
<span className='sr-only'>
|
||||
{t('settings.headings.personal-info')}
|
||||
|
||||
@ -132,7 +132,10 @@ function EmailSettings({
|
||||
state: confirmEmailValidation,
|
||||
message: confirmEmailValidationMessage
|
||||
} = getValidationForConfirmEmail();
|
||||
|
||||
const isDisabled =
|
||||
newEmailValidation !== 'success' ||
|
||||
confirmEmailValidation !== 'success' ||
|
||||
isPristine;
|
||||
if (!currentEmail) {
|
||||
return (
|
||||
<div>
|
||||
@ -170,7 +173,12 @@ function EmailSettings({
|
||||
</FullWidthRow>
|
||||
)}
|
||||
<FullWidthRow>
|
||||
<form id='form-update-email' onSubmit={handleSubmit}>
|
||||
<form
|
||||
id='form-update-email'
|
||||
{...(!isDisabled
|
||||
? { onSubmit: handleSubmit }
|
||||
: { onSubmit: e => e.preventDefault() })}
|
||||
>
|
||||
<FormGroup controlId='current-email'>
|
||||
<ControlLabel>{t('settings.email.current')}</ControlLabel>
|
||||
<FormControl.Static>{currentEmail}</FormControl.Static>
|
||||
@ -206,11 +214,8 @@ function EmailSettings({
|
||||
</FormGroup>
|
||||
</div>
|
||||
<BlockSaveButton
|
||||
disabled={
|
||||
newEmailValidation !== 'success' ||
|
||||
confirmEmailValidation !== 'success' ||
|
||||
isPristine
|
||||
}
|
||||
aria-disabled={isDisabled}
|
||||
{...(isDisabled && { tabindex: -1 })}
|
||||
>
|
||||
{t('buttons.save')}{' '}
|
||||
<span className='sr-only'>{t('settings.email.heading')}</span>
|
||||
|
||||
@ -1,14 +1,3 @@
|
||||
#honesty-policy
|
||||
:is(
|
||||
button[aria-disabled='true'],
|
||||
button[aria-disabled='true']:is(:focus, :hover)
|
||||
) {
|
||||
background-color: var(--quaternary-background);
|
||||
color: var(--secondary-color);
|
||||
opacity: 0.65;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.honesty-panel p {
|
||||
margin-inline: 10px;
|
||||
font-family: 'Lato', sans-serif;
|
||||
|
||||
@ -66,7 +66,6 @@ class InternetSettings extends Component<InternetProps, InternetState> {
|
||||
twitter !== originalValues.twitter ||
|
||||
website !== originalValues.website
|
||||
) {
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
return this.setState({
|
||||
originalValues: { githubProfile, linkedin, twitter, website }
|
||||
});
|
||||
@ -179,7 +178,7 @@ class InternetSettings extends Component<InternetProps, InternetState> {
|
||||
|
||||
const { state: websiteValidation, message: websiteValidationMessage } =
|
||||
this.getValidationStateFor(website);
|
||||
|
||||
const isDisabled = this.isFormPristine() || !this.isFormValid();
|
||||
return (
|
||||
<>
|
||||
<SectionHeader>{t('settings.headings.internet')}</SectionHeader>
|
||||
@ -244,7 +243,8 @@ class InternetSettings extends Component<InternetProps, InternetState> {
|
||||
</FormGroup>
|
||||
</div>
|
||||
<BlockSaveButton
|
||||
disabled={this.isFormPristine() || !this.isFormValid()}
|
||||
aria-disabled={isDisabled}
|
||||
{...(isDisabled && { tabindex: -1 })}
|
||||
>
|
||||
{t('buttons.save')}{' '}
|
||||
<span className='sr-only'>{t('settings.headings.internet')}</span>
|
||||
|
||||
@ -84,11 +84,6 @@ class PortfolioSettings extends Component<PortfolioProps, PortfolioState> {
|
||||
});
|
||||
};
|
||||
|
||||
handleSubmit = (e: React.FormEvent<HTMLFormElement>, id: string) => {
|
||||
e.preventDefault();
|
||||
this.updateItem(id);
|
||||
};
|
||||
|
||||
updateItem = (id: string) => {
|
||||
const { portfolio, unsavedItemId } = this.state;
|
||||
if (unsavedItemId === id) {
|
||||
@ -218,9 +213,27 @@ class PortfolioSettings extends Component<PortfolioProps, PortfolioState> {
|
||||
);
|
||||
const { state: descriptionState, message: descriptionMessage } =
|
||||
this.getDescriptionValidation(description);
|
||||
|
||||
const isDisabled =
|
||||
pristine ||
|
||||
!title ||
|
||||
!isURL(url, {
|
||||
protocols: ['http', 'https'],
|
||||
/* eslint-disable camelcase, @typescript-eslint/naming-convention */
|
||||
require_tld: true,
|
||||
require_protocol: true
|
||||
/* eslint-enable camelcase, @typescript-eslint/naming-convention */
|
||||
});
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>, id: string) => {
|
||||
e.preventDefault();
|
||||
if (isDisabled) return null;
|
||||
return this.updateItem(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<FullWidthRow key={id}>
|
||||
<form onSubmit={e => this.handleSubmit(e, id)}>
|
||||
<form onSubmit={e => handleSubmit(e, id)} id='portfolio-items'>
|
||||
<FormGroup
|
||||
controlId={`${id}-title`}
|
||||
validationState={
|
||||
@ -276,17 +289,8 @@ class PortfolioSettings extends Component<PortfolioProps, PortfolioState> {
|
||||
) : null}
|
||||
</FormGroup>
|
||||
<BlockSaveButton
|
||||
disabled={
|
||||
pristine ||
|
||||
!title ||
|
||||
!isURL(url, {
|
||||
protocols: ['http', 'https'],
|
||||
/* eslint-disable camelcase, @typescript-eslint/naming-convention */
|
||||
require_tld: true,
|
||||
require_protocol: true
|
||||
/* eslint-enable camelcase, @typescript-eslint/naming-convention */
|
||||
})
|
||||
}
|
||||
aria-disabled={isDisabled}
|
||||
{...(isDisabled && { tabindex: -1 })}
|
||||
>
|
||||
{t('buttons.save-portfolio')}
|
||||
</BlockSaveButton>
|
||||
|
||||
@ -152,7 +152,8 @@ function PrivacySettings({
|
||||
bsStyle='primary'
|
||||
data-cy='save-privacy-settings'
|
||||
block={true}
|
||||
disabled={!madeChanges}
|
||||
aria-disabled={!madeChanges}
|
||||
{...(!madeChanges && { tabindex: -1 })}
|
||||
>
|
||||
{t('buttons.save')}{' '}
|
||||
<span className='sr-only'>{t('settings.headings.privacy')}</span>
|
||||
|
||||
@ -96,7 +96,6 @@ class UsernameSettings extends Component<UsernameProps, UsernameState> {
|
||||
const { username } = this.props;
|
||||
const { formValue } = this.state;
|
||||
if (prevUsername !== username && prevFormValue === formValue) {
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
return this.setState({
|
||||
isFormPristine: username === formValue,
|
||||
submitClicked: false,
|
||||
@ -210,7 +209,8 @@ class UsernameSettings extends Component<UsernameProps, UsernameState> {
|
||||
submitClicked
|
||||
} = this.state;
|
||||
const { isValidUsername, t, validating } = this.props;
|
||||
|
||||
const isDisabled =
|
||||
!(isValidUsername && valid && !isFormPristine) || submitClicked;
|
||||
return (
|
||||
<form
|
||||
id='usernameSettings'
|
||||
@ -235,9 +235,8 @@ class UsernameSettings extends Component<UsernameProps, UsernameState> {
|
||||
this.renderAlerts(validating, error, isValidUsername)}
|
||||
<FullWidthRow>
|
||||
<BlockSaveButton
|
||||
disabled={
|
||||
!(isValidUsername && valid && !isFormPristine) || submitClicked
|
||||
}
|
||||
aria-disabled={isDisabled}
|
||||
{...(isDisabled && { tabindex: -1 })}
|
||||
>
|
||||
{t('buttons.save')}{' '}
|
||||
<span className='sr-only'>{t('settings.labels.username')}</span>
|
||||
|
||||
@ -43,12 +43,11 @@ describe('Username input field', () => {
|
||||
.should('have.class', 'alert alert-info');
|
||||
});
|
||||
|
||||
// eslint-disable-next-line
|
||||
it('Should be able to click the `Save` button if username is available', () => {
|
||||
cy.typeUsername('oliver');
|
||||
|
||||
cy.get('@usernameForm').within(() => {
|
||||
cy.contains('Save').should('not.be.disabled');
|
||||
cy.contains('Save').should('have.attr', 'aria-disabled', 'false');
|
||||
});
|
||||
});
|
||||
|
||||
@ -63,7 +62,6 @@ describe('Username input field', () => {
|
||||
.should('have.class', 'alert alert-warning');
|
||||
});
|
||||
|
||||
// eslint-disable-next-line
|
||||
it('Should not be possible to click the `Save` button if username is unavailable', () => {
|
||||
cy.typeUsername('twaha');
|
||||
|
||||
@ -74,20 +72,25 @@ describe('Username input field', () => {
|
||||
'the URL to your profile and your certifications.'
|
||||
).should('not.exist');
|
||||
|
||||
cy.get('@usernameForm').contains('Save').should('be.disabled');
|
||||
cy.get('@usernameForm')
|
||||
.contains('Save')
|
||||
.should('have.attr', 'aria-disabled', 'true');
|
||||
});
|
||||
|
||||
it('Should not show anything if user types their current name', () => {
|
||||
cy.typeUsername('developmentuser');
|
||||
|
||||
cy.get('@usernameForm').contains('Save').should('be.disabled');
|
||||
cy.get('@usernameForm')
|
||||
.contains('Save')
|
||||
.should('have.attr', 'aria-disabled', 'true');
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('Should not be possible to click the `Save` button if user types their current name', () => {
|
||||
cy.typeUsername('developmentuser');
|
||||
|
||||
cy.get('@usernameForm').contains('Save').should('be.disabled');
|
||||
cy.get('@usernameForm')
|
||||
.contains('Save')
|
||||
.should('have.attr', 'aria-disabled', 'true');
|
||||
});
|
||||
|
||||
it('Should show warning if username includes invalid character', () => {
|
||||
@ -101,11 +104,12 @@ describe('Username input field', () => {
|
||||
.should('have.class', 'alert alert-danger');
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('Should not be able to click the `Save` button if username includes invalid character', () => {
|
||||
cy.typeUsername('Quincy Larson');
|
||||
|
||||
cy.get('@usernameForm').contains('Save').should('be.disabled');
|
||||
cy.get('@usernameForm')
|
||||
.contains('Save')
|
||||
.should('have.attr', 'aria-disabled', 'true');
|
||||
});
|
||||
|
||||
it('Should change username if `Save` button is clicked', () => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user