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:
Muhammed Mustafa 2023-02-18 22:16:35 +02:00 committed by GitHub
parent 0f2c524e03
commit 251aecc58f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 80 additions and 55 deletions

View File

@ -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);

View File

@ -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')}

View File

@ -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>

View File

@ -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;

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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', () => {