mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-01-07 00:03:44 -05:00
refactor(client): migrate buttons on the settings page to ui-components (#53739)
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faRightToBracket } from '@fortawesome/free-solid-svg-icons';
|
||||
import React, { ReactNode } from 'react';
|
||||
@@ -32,8 +31,7 @@ const Login = ({
|
||||
|
||||
const href = isSignedIn ? `${homeLocation}/learn` : `${apiLocation}/signin`;
|
||||
return (
|
||||
<Button
|
||||
bsStyle='default'
|
||||
<a
|
||||
className={(block ? 'btn-cta-big btn-block' : '') + ' signup-btn btn-cta'}
|
||||
data-test-label={dataTestLabel}
|
||||
data-playwright-test-label='header-sign-in-button'
|
||||
@@ -44,7 +42,7 @@ const Login = ({
|
||||
<span className='sr-only'> {t('buttons.sign-in')}</span>
|
||||
</span>
|
||||
<span className='login-btn-text'>{children || t('buttons.sign-in')}</span>
|
||||
</Button>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -342,6 +342,7 @@ li > button.nav-link-signout:not([aria-disabled='true']):is(:hover, :focus) {
|
||||
max-height: var(--header-element-size);
|
||||
min-width: var(--header-element-size);
|
||||
padding: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media (min-width: 601px) {
|
||||
|
||||
@@ -29,7 +29,7 @@ test('should render', () => {
|
||||
|
||||
const button = screen.getByText(/submit/i);
|
||||
expect(button).toHaveAttribute('type', 'submit');
|
||||
expect(button).toBeDisabled();
|
||||
expect(button).toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
|
||||
test('should render with default values', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
exports[`<BlockSaveButton /> snapshot 1`] = `
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-primary btn-block"
|
||||
class=" relative inline-block mt-[0.5px] border-solid border-3 active:before:w-full active:before:h-full active:before:absolute active:before:inset-0 active:before:border-3 active:before:border-transparent active:before:bg-gray-900 active:before:opacity-20 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 focus:outline-none focus-visible:ring focus-visible:ring-focus-outline-color text-center cursor-pointer no-underline block w-full border-foreground-secondary bg-background-quaternary text-foreground-secondary hover:bg-foreground-primary hover:text-background-primary hover:border-foreground-secondary dark:hover:bg-background-primary dark:hover:text-foreground-primary px-3 py-1.5 text-md"
|
||||
type="submit"
|
||||
>
|
||||
buttons.save
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import { Button, type ButtonProps } from '@freecodecamp/ui';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -9,7 +9,7 @@ function BlockSaveButton({
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
bgSize?: string;
|
||||
bgSize?: ButtonProps['size'];
|
||||
}): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -18,8 +18,7 @@ function BlockSaveButton({
|
||||
block={true}
|
||||
// the button is used to submit solutions in projects that require external URL
|
||||
// these buttons don't use bgSize, that's why the bgSize is optional.
|
||||
bsSize={bgSize}
|
||||
bsStyle='primary'
|
||||
size={bgSize}
|
||||
type='submit'
|
||||
{...restProps}
|
||||
>
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
.toggle-active.btn[disabled] {
|
||||
background-color: var(--secondary-color);
|
||||
color: var(--secondary-background);
|
||||
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);
|
||||
}
|
||||
.toggle-not-active:hover {
|
||||
color: var(--secondary-background);
|
||||
}
|
||||
|
||||
.toggle-not-active:hover,
|
||||
.toggle-not-active:focus {
|
||||
background-color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.btn-group .btn.toggle-not-active,
|
||||
.btn-group .btn.toggle-active {
|
||||
border-color: var(--tertiary-color);
|
||||
padding-inline: 30px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.btn-group .btn-primary,
|
||||
.btn-group .btn-primary:focus,
|
||||
.btn-group .btn-primary:hover {
|
||||
border-color: var(--secondary-color);
|
||||
}
|
||||
@@ -285,16 +285,14 @@ fieldset[disabled] .btn-primary.focus {
|
||||
.btn-cta {
|
||||
background-color: #feac32;
|
||||
background-image: linear-gradient(#fecc4c, #ffac33);
|
||||
border-width: 3px;
|
||||
border-color: #feac32;
|
||||
border: 3px solid #feac32;
|
||||
color: #0a0a23 !important;
|
||||
}
|
||||
.btn-cta:hover,
|
||||
.btn-cta:focus,
|
||||
.btn-cta:active:hover {
|
||||
background-color: #fecc4c !important;
|
||||
border-width: 3px;
|
||||
border-color: #f1a02a;
|
||||
border: 3px solid #f1a02a;
|
||||
background-image: none;
|
||||
color: #0a0a23 !important;
|
||||
}
|
||||
|
||||
@@ -44,13 +44,10 @@ exports[`<Honesty /> <Honesty /> snapshot when isHonest is false: Honesty 1`] =
|
||||
</p>
|
||||
</y>
|
||||
<Button
|
||||
active={false}
|
||||
aria-disabled={false}
|
||||
block={true}
|
||||
bsClass="btn"
|
||||
bsStyle="primary"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
variant="primary"
|
||||
>
|
||||
buttons.agree-honesty
|
||||
</Button>
|
||||
@@ -102,13 +99,10 @@ exports[`<Honesty /> <Honesty /> snapshot when isHonest is true: HonestyAccepted
|
||||
</p>
|
||||
</y>
|
||||
<Button
|
||||
active={false}
|
||||
aria-disabled={true}
|
||||
block={true}
|
||||
bsClass="btn"
|
||||
bsStyle="primary"
|
||||
disabled={false}
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
variant="primary"
|
||||
>
|
||||
buttons.accepted-honesty
|
||||
</Button>
|
||||
|
||||
@@ -267,8 +267,8 @@ class AboutSettings extends Component<AboutProps, AboutState> {
|
||||
</FormGroup>
|
||||
</div>
|
||||
<BlockSaveButton
|
||||
aria-disabled={this.isFormPristine()}
|
||||
bgSize='lg'
|
||||
disabled={this.isFormPristine()}
|
||||
bgSize='large'
|
||||
{...(this.isFormPristine() && { tabIndex: -1 })}
|
||||
>
|
||||
{t('buttons.save')}{' '}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import { Link, navigate } from 'gatsby';
|
||||
import { find } from 'lodash-es';
|
||||
import React, { MouseEvent, useState } from 'react';
|
||||
@@ -7,7 +6,7 @@ import type { TFunction } from 'i18next';
|
||||
import { createSelector } from 'reselect';
|
||||
import ScrollableAnchor, { configureAnchors } from 'react-scrollable-anchor';
|
||||
import { connect } from 'react-redux';
|
||||
import { Table } from '@freecodecamp/ui';
|
||||
import { Table, Button } from '@freecodecamp/ui';
|
||||
|
||||
import { regeneratePathAndHistory } from '../../../../shared/utils/polyvinyl';
|
||||
import ProjectPreviewModal from '../../templates/Challenges/components/project-preview-modal';
|
||||
@@ -180,12 +179,6 @@ const LegacyFullStack = (props: CertificationSettingsProps) => {
|
||||
const certSlug = Certification.LegacyFullStack;
|
||||
const certLocation = `/certification/${username}/${certSlug}`;
|
||||
|
||||
const buttonStyle = {
|
||||
marginBottom: '30px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '18px'
|
||||
};
|
||||
|
||||
const createClickHandler =
|
||||
(certSlug: keyof typeof certSlugTypeMap) =>
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
@@ -225,29 +218,28 @@ const LegacyFullStack = (props: CertificationSettingsProps) => {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className={'col-xs-12'}>
|
||||
<div>
|
||||
{fullStackClaimable ? (
|
||||
<Button
|
||||
bsSize='sm'
|
||||
bsStyle='primary'
|
||||
className={'col-xs-12'}
|
||||
size='small'
|
||||
variant='primary'
|
||||
block={true}
|
||||
href={certLocation}
|
||||
id={'button-' + certSlug}
|
||||
// This floating promise is acceptable
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onClick={createClickHandler(certSlug)}
|
||||
style={buttonStyle}
|
||||
target='_blank'
|
||||
>
|
||||
{isFullStackCert ? t('buttons.show-cert') : t('buttons.claim-cert')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
bsSize='sm'
|
||||
bsStyle='primary'
|
||||
className={'col-xs-12'}
|
||||
size='small'
|
||||
variant='primary'
|
||||
block={true}
|
||||
disabled={true}
|
||||
id={'button-' + certSlug}
|
||||
style={buttonStyle}
|
||||
target='_blank'
|
||||
>
|
||||
{t('buttons.claim-cert')}
|
||||
</Button>
|
||||
@@ -402,10 +394,11 @@ function CertificationSettings(props: CertificationSettingsProps) {
|
||||
<td colSpan={2}>
|
||||
<Button
|
||||
block={true}
|
||||
bsStyle='primary'
|
||||
className={'col-xs-12'}
|
||||
variant='primary'
|
||||
href={certLocation}
|
||||
data-cy={`btn-for-${certSlug}`}
|
||||
// This floating promise is acceptable
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
onClick={clickHandler}
|
||||
>
|
||||
{isCert ? t('buttons.show-cert') : t('buttons.claim-cert')}{' '}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
.btn-danger {
|
||||
background-color: var(--danger-color);
|
||||
color: var(--danger-background);
|
||||
border-color: var(--danger-background);
|
||||
}
|
||||
|
||||
.btn-danger:hover,
|
||||
.btn-danger:focus {
|
||||
color: var(--danger-color);
|
||||
background-color: var(--danger-background);
|
||||
border-color: var(--danger-background);
|
||||
}
|
||||
.danger-zone p {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
@@ -1,19 +1,16 @@
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import React, { useState } from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
import { Panel } from '@freecodecamp/ui';
|
||||
import { Panel, Button } from '@freecodecamp/ui';
|
||||
|
||||
import { deleteAccount, resetProgress } from '../../redux/settings/actions';
|
||||
import { FullWidthRow, Spacer } from '../helpers';
|
||||
import DeleteModal from './delete-modal';
|
||||
import ResetModal from './reset-modal';
|
||||
|
||||
import './danger-zone.css';
|
||||
|
||||
interface DangerZoneProps {
|
||||
deleteAccount: () => void;
|
||||
resetProgress: () => void;
|
||||
@@ -52,9 +49,8 @@ function DangerZone({ deleteAccount, resetProgress, t }: DangerZoneProps) {
|
||||
<FullWidthRow>
|
||||
<Button
|
||||
block={true}
|
||||
bsSize='lg'
|
||||
bsStyle='danger'
|
||||
className='btn-danger'
|
||||
size='large'
|
||||
variant='danger'
|
||||
onClick={toggleResetModal}
|
||||
type='button'
|
||||
>
|
||||
@@ -63,9 +59,8 @@ function DangerZone({ deleteAccount, resetProgress, t }: DangerZoneProps) {
|
||||
<Spacer size='small' />
|
||||
<Button
|
||||
block={true}
|
||||
bsSize='lg'
|
||||
bsStyle='danger'
|
||||
className='btn-danger'
|
||||
size='large'
|
||||
variant='danger'
|
||||
onClick={toggleDeleteModal}
|
||||
type='button'
|
||||
>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Button, Modal } from '@freecodecamp/react-bootstrap';
|
||||
import { Modal } from '@freecodecamp/react-bootstrap';
|
||||
import React from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Button } from '@freecodecamp/ui';
|
||||
|
||||
import { Spacer } from '../helpers';
|
||||
|
||||
import './danger-zone.css';
|
||||
|
||||
type DeleteModalProps = {
|
||||
delete: () => void;
|
||||
onHide: () => void;
|
||||
@@ -44,9 +43,8 @@ function DeleteModal(props: DeleteModalProps): JSX.Element {
|
||||
<hr />
|
||||
<Button
|
||||
block={true}
|
||||
bsSize='lg'
|
||||
bsStyle='primary'
|
||||
className='btn-invert'
|
||||
size='large'
|
||||
variant='primary'
|
||||
onClick={props.onHide}
|
||||
type='button'
|
||||
>
|
||||
@@ -55,9 +53,8 @@ function DeleteModal(props: DeleteModalProps): JSX.Element {
|
||||
<Spacer size='small' />
|
||||
<Button
|
||||
block={true}
|
||||
bsSize='lg'
|
||||
bsStyle='danger'
|
||||
className='btn-danger'
|
||||
size='large'
|
||||
variant='danger'
|
||||
onClick={props.delete}
|
||||
type='button'
|
||||
>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import {
|
||||
HelpBlock,
|
||||
Alert,
|
||||
FormGroup,
|
||||
FormGroupProps,
|
||||
FormControl,
|
||||
ControlLabel
|
||||
ControlLabel,
|
||||
Button
|
||||
} from '@freecodecamp/ui';
|
||||
import { Link } from 'gatsby';
|
||||
import React, { useState } from 'react';
|
||||
@@ -150,11 +150,14 @@ function EmailSettings({
|
||||
<p className='large-p text-center'>{t('settings.email.missing')}</p>
|
||||
</FullWidthRow>
|
||||
<FullWidthRow>
|
||||
<Link style={{ textDecoration: 'none' }} to='/update-email'>
|
||||
<Button block={true} bsSize='lg' bsStyle='primary'>
|
||||
{t('buttons.edit')}
|
||||
</Button>
|
||||
</Link>
|
||||
<Button
|
||||
block={true}
|
||||
size='large'
|
||||
variant='primary'
|
||||
href='/update-email'
|
||||
>
|
||||
{t('buttons.edit')}
|
||||
</Button>
|
||||
</FullWidthRow>
|
||||
</div>
|
||||
);
|
||||
@@ -237,8 +240,8 @@ function EmailSettings({
|
||||
</FormGroup>
|
||||
</div>
|
||||
<BlockSaveButton
|
||||
aria-disabled={isDisabled}
|
||||
bgSize='lg'
|
||||
disabled={isDisabled}
|
||||
bgSize='large'
|
||||
{...(isDisabled && { tabIndex: -1 })}
|
||||
>
|
||||
{t('buttons.save')}{' '}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import { Button } from '@freecodecamp/ui';
|
||||
import React from 'react';
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import React from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Panel } from '@freecodecamp/ui';
|
||||
import { Panel, Button } from '@freecodecamp/ui';
|
||||
|
||||
import { FullWidthRow } from '../helpers';
|
||||
import SectionHeader from './section-header';
|
||||
@@ -39,8 +38,8 @@ const Honesty = ({ isHonest, updateIsHonest }: HonestyProps): JSX.Element => {
|
||||
</Panel>
|
||||
<Button
|
||||
block={true}
|
||||
bsStyle='primary'
|
||||
aria-disabled={isHonest}
|
||||
variant='primary'
|
||||
disabled={isHonest}
|
||||
onClick={() => !isHonest && updateIsHonest({ isHonest: true })}
|
||||
>
|
||||
{buttonText}
|
||||
|
||||
@@ -292,8 +292,8 @@ class InternetSettings extends Component<InternetProps, InternetState> {
|
||||
</FormGroup>
|
||||
</div>
|
||||
<BlockSaveButton
|
||||
aria-disabled={isDisabled}
|
||||
bgSize='lg'
|
||||
disabled={isDisabled}
|
||||
bgSize='large'
|
||||
{...(isDisabled && { tabIndex: -1 })}
|
||||
>
|
||||
{t('buttons.save')}{' '}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import { findIndex, find, isEqual } from 'lodash-es';
|
||||
import { nanoid } from 'nanoid';
|
||||
import React, { Component } from 'react';
|
||||
@@ -8,7 +7,8 @@ import {
|
||||
FormControl,
|
||||
ControlLabel,
|
||||
HelpBlock,
|
||||
FormGroupProps
|
||||
FormGroupProps,
|
||||
Button
|
||||
} from '@freecodecamp/ui';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import isURL from 'validator/lib/isURL';
|
||||
@@ -340,8 +340,8 @@ class PortfolioSettings extends Component<PortfolioProps, PortfolioState> {
|
||||
) : null}
|
||||
</FormGroup>
|
||||
<BlockSaveButton
|
||||
aria-disabled={isButtonDisabled}
|
||||
bgSize='lg'
|
||||
disabled={isButtonDisabled}
|
||||
bgSize='large'
|
||||
{...(isButtonDisabled && { tabIndex: -1 })}
|
||||
>
|
||||
{t('buttons.save-portfolio')}
|
||||
@@ -349,8 +349,8 @@ class PortfolioSettings extends Component<PortfolioProps, PortfolioState> {
|
||||
<Spacer size='small' />
|
||||
<Button
|
||||
block={true}
|
||||
bsSize='lg'
|
||||
bsStyle='danger'
|
||||
size='large'
|
||||
variant='danger'
|
||||
onClick={() => this.handleRemoveItem(id)}
|
||||
type='button'
|
||||
>
|
||||
@@ -380,8 +380,8 @@ class PortfolioSettings extends Component<PortfolioProps, PortfolioState> {
|
||||
<Button
|
||||
data-cy='add-portfolio'
|
||||
block={true}
|
||||
bsSize='lg'
|
||||
bsStyle='primary'
|
||||
size='large'
|
||||
variant='primary'
|
||||
disabled={unsavedItemId !== null}
|
||||
onClick={this.handleAdd}
|
||||
type='button'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation, withTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import type { Dispatch } from 'redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { Button } from '@freecodecamp/ui';
|
||||
|
||||
import { userSelector } from '../../redux/selectors';
|
||||
import type { ProfileUI } from '../../redux/prop-types';
|
||||
@@ -145,11 +145,11 @@ function PrivacySettings({ submitProfileUI, user }: PrivacyProps): JSX.Element {
|
||||
</div>
|
||||
<Button
|
||||
type='submit'
|
||||
bsSize='lg'
|
||||
bsStyle='primary'
|
||||
size='large'
|
||||
variant='primary'
|
||||
data-cy='save-privacy-settings'
|
||||
block={true}
|
||||
aria-disabled={!madeChanges}
|
||||
disabled={!madeChanges}
|
||||
{...(!madeChanges && { tabIndex: -1 })}
|
||||
>
|
||||
{t('buttons.save')}{' '}
|
||||
@@ -162,8 +162,8 @@ function PrivacySettings({ submitProfileUI, user }: PrivacyProps): JSX.Element {
|
||||
<p>{t('settings.data')}</p>
|
||||
<Button
|
||||
block={true}
|
||||
bsSize='lg'
|
||||
bsStyle='primary'
|
||||
size='large'
|
||||
variant='primary'
|
||||
download={`${user.username}.json`}
|
||||
href={`data:text/json;charset=utf-8,${encodeURIComponent(
|
||||
JSON.stringify(user)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Button, Modal } from '@freecodecamp/react-bootstrap';
|
||||
import { Modal } from '@freecodecamp/react-bootstrap';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button } from '@freecodecamp/ui';
|
||||
|
||||
import { Spacer } from '../helpers';
|
||||
|
||||
@@ -35,9 +36,8 @@ function ResetModal(props: ResetModalProps): JSX.Element {
|
||||
<hr />
|
||||
<Button
|
||||
block={true}
|
||||
bsSize='lg'
|
||||
bsStyle='primary'
|
||||
className='btn-invert'
|
||||
size='large'
|
||||
variant='primary'
|
||||
onClick={props.onHide}
|
||||
type='button'
|
||||
>
|
||||
@@ -46,9 +46,8 @@ function ResetModal(props: ResetModalProps): JSX.Element {
|
||||
<Spacer size='small' />
|
||||
<Button
|
||||
block={true}
|
||||
bsSize='lg'
|
||||
bsStyle='danger'
|
||||
className='btn-danger'
|
||||
size='large'
|
||||
variant='danger'
|
||||
onClick={props.reset}
|
||||
type='button'
|
||||
>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import ToggleCheck from '../../assets/icons/toggle-check';
|
||||
import type { ToggleSettingProps } from './toggle-radio-setting';
|
||||
import '../helpers/toggle-button.css';
|
||||
import './toggle-setting.css';
|
||||
|
||||
export default function ToggleButtonSetting({
|
||||
@@ -35,7 +34,6 @@ export default function ToggleButtonSetting({
|
||||
aria-pressed={flag}
|
||||
{...(!flag && { onClick: toggleFlag })}
|
||||
value='1'
|
||||
className='toggle-button-right'
|
||||
>
|
||||
<span>
|
||||
{onLabel}
|
||||
@@ -47,7 +45,6 @@ export default function ToggleButtonSetting({
|
||||
aria-pressed={!flag}
|
||||
{...(flag && { onClick: toggleFlag })}
|
||||
value='2'
|
||||
className='toggle-button-left'
|
||||
>
|
||||
<span>
|
||||
{offLabel}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import '../helpers/toggle-button.css';
|
||||
import './toggle-setting.css';
|
||||
|
||||
export type ToggleSettingProps = {
|
||||
|
||||
@@ -221,8 +221,8 @@ class UsernameSettings extends Component<UsernameProps, UsernameState> {
|
||||
this.renderAlerts(validating, error, isValidUsername)}
|
||||
<FullWidthRow>
|
||||
<BlockSaveButton
|
||||
aria-disabled={isDisabled}
|
||||
bgSize='lg'
|
||||
disabled={isDisabled}
|
||||
bgSize='large'
|
||||
{...(isDisabled && { tabIndex: -1 })}
|
||||
>
|
||||
{t('buttons.save')}{' '}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import { Link } from 'gatsby';
|
||||
import { isString } from 'lodash-es';
|
||||
import React, { useState, type FormEvent, type ChangeEvent } from 'react';
|
||||
@@ -16,7 +15,8 @@ import {
|
||||
FormControl,
|
||||
ControlLabel,
|
||||
Col,
|
||||
Row
|
||||
Row,
|
||||
Button
|
||||
} from '@freecodecamp/ui';
|
||||
|
||||
import { Spacer } from '../components/helpers';
|
||||
@@ -102,8 +102,8 @@ function UpdateEmail({ isNewEmail, t, updateMyEmail }: UpdateEmailProps) {
|
||||
</FormGroup>
|
||||
<Button
|
||||
block={true}
|
||||
bsSize='lg'
|
||||
bsStyle='primary'
|
||||
size='large'
|
||||
variant='primary'
|
||||
disabled={getEmailValidationState() !== 'success'}
|
||||
type='submit'
|
||||
>
|
||||
|
||||
@@ -89,6 +89,11 @@ describe('project submission', () => {
|
||||
// cy.url().should('not.have.string', url);
|
||||
});
|
||||
});
|
||||
|
||||
// Access to the clipboard reliably works in Electron browser.
|
||||
// In other browsers, there are popups asking for permission
|
||||
// thus we should only run these tests in Electron
|
||||
// Ref: https://github.com/cypress-io/cypress-example-recipes/tree/master/examples/testing-dom__clipboard
|
||||
it(
|
||||
'JavaScript projects can be submitted and then viewed in /settings and on the certifications',
|
||||
{ browser: 'electron' },
|
||||
|
||||
@@ -39,6 +39,7 @@ describe('Front End Development Libraries Superblock', () => {
|
||||
cy.url().should('match', /\/settings\/?#certification-settings/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('After submitting all 5 projects', () => {
|
||||
before(() => {
|
||||
cy.task('seed');
|
||||
|
||||
@@ -32,6 +32,9 @@ describe('Picture input field', () => {
|
||||
}
|
||||
);
|
||||
cy.wait(500);
|
||||
cy.get('#camper-identity > .btn').should('not.be.disabled');
|
||||
cy.get('#camper-identity')
|
||||
.find('button')
|
||||
.contains('Save')
|
||||
.should('not.be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,7 +12,9 @@ describe('Add Portfolio Item', () => {
|
||||
|
||||
cy.get('[data-cy="validation-message"]').contains('A title is required');
|
||||
cy.get('[data-cy="portfolio-title"]').type('This is a portfolio item');
|
||||
cy.get('button').filter(':disabled').should('have.length.gt', 0);
|
||||
cy.get('button')
|
||||
.contains('Save this portfolio item')
|
||||
.should('not.be.disabled');
|
||||
|
||||
cy.get('[data-cy="portfolio-url"]').type('This is a portfolio item');
|
||||
cy.get('[data-cy="validation-message"]').contains(
|
||||
@@ -43,7 +45,10 @@ describe('Add Portfolio Item', () => {
|
||||
cy.get('[data-cy="validation-message"]').contains(
|
||||
'There is a maximum limit of 288 characters, you have 0 left'
|
||||
);
|
||||
cy.get('button').filter(':disabled').should('have.length.gt', 0);
|
||||
|
||||
cy.get('button')
|
||||
.contains('Save this portfolio item')
|
||||
.should('not.be.disabled');
|
||||
|
||||
cy.get('[data-cy="portfolio-description"]').type('{backspace}');
|
||||
cy.get('button[type=submit]').contains('Save this portfolio item').click();
|
||||
|
||||
@@ -33,7 +33,7 @@ describe('<Button />', () => {
|
||||
).toHaveAttribute('type', 'submit');
|
||||
});
|
||||
|
||||
it('should trigger the onClick prop on click', async () => {
|
||||
it('should trigger the onClick prop on click if the component is a button element', async () => {
|
||||
const onClick = jest.fn();
|
||||
|
||||
render(<Button onClick={onClick}>Hello world</Button>);
|
||||
@@ -98,4 +98,20 @@ describe('<Button />', () => {
|
||||
// Ensure that a link element is not rendered
|
||||
expect(link).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should trigger the onClick prop on click if the component is an anchor element', async () => {
|
||||
const onClick = jest.fn();
|
||||
|
||||
render(
|
||||
<Button href='https://www.freecodecamp.org' onClick={onClick}>
|
||||
freeCodeCamp
|
||||
</Button>
|
||||
);
|
||||
|
||||
const link = screen.getByRole('link', { name: /freeCodeCamp/i });
|
||||
|
||||
await userEvent.click(link);
|
||||
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -189,6 +189,7 @@ export const HeadlessButton = React.forwardRef<
|
||||
download={download}
|
||||
target={target}
|
||||
ref={ref as React.Ref<HTMLAnchorElement>}
|
||||
onClick={onClick}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Use this file as the entry point for component export
|
||||
export { Alert, type AlertProps } from './alert';
|
||||
export { Button } from './button';
|
||||
export { Button, type ButtonProps } from './button';
|
||||
export { CloseButton } from './close-button';
|
||||
export { Image } from './image';
|
||||
export { Table } from './table';
|
||||
|
||||
Reference in New Issue
Block a user