0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00

refactor: updated button component in packages/ui (closes #1859) (#1958)

* refactor: updated button component in packages/ui

* docs: added comment for window.timeout

* docs: added prefix to comment

* fix: removed unwanted style changes

* fix(ui): update button props

update button props, add ut

* fix(ui): fix merge conflicts

fix merge conflicts

Co-authored-by: simeng-li <simeng@silverhand.io>
This commit is contained in:
5war00p 2022-09-21 11:50:47 +05:30 committed by GitHub
parent 5f81cd1ef5
commit 970d360988
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 62 additions and 62 deletions

View file

@ -2,8 +2,8 @@ import { render, fireEvent } from '@testing-library/react';
import { mockSocialConnectorData } from '@/__mocks__/logto';
import Button from '.';
import SocialLinkButton from './SocialLinkButton';
import Button from './index';
describe('Button Component', () => {
const onClick = jest.fn();
@ -12,16 +12,11 @@ describe('Button Component', () => {
onClick.mockClear();
});
it('render Button', () => {
const { queryByText, container } = render(<Button onClick={onClick}>foo</Button>);
expect(queryByText('foo')).not.toBeNull();
const button = container.querySelector('button');
if (button) {
fireEvent.click(button);
expect(onClick).toBeCalled();
}
it('render Default Button', () => {
const { queryByText, getByText } = render(<Button title="action.confirm" onClick={onClick} />);
expect(queryByText('action.confirm')).not.toBeNull();
fireEvent.click(getByText('action.confirm'));
expect(onClick).toBeCalled();
});
it('render SocialLinkButton', () => {

View file

@ -1,41 +1,56 @@
import { I18nKey } from '@logto/phrases-ui';
import classNames from 'classnames';
import { ReactNode } from 'react';
import { HTMLProps } from 'react';
import { useTranslation } from 'react-i18next';
import * as styles from './index.module.scss';
export type Props = {
export type ButtonType = 'primary' | 'secondary' | 'outline';
type BaseProps = Omit<HTMLProps<HTMLButtonElement>, 'type' | 'size' | 'title'> & {
htmlType?: 'button' | 'submit' | 'reset';
type?: ButtonType;
size?: 'small' | 'large';
isDisabled?: boolean;
className?: string;
children: ReactNode | Record<string, unknown>;
type?: 'primary' | 'secondary' | 'outline';
size?: 'small' | 'large';
onClick?: React.MouseEventHandler;
};
type Props = BaseProps & {
title: I18nKey;
i18nProps?: Record<string, string>;
};
const Button = ({
htmlType = 'button',
type = 'primary',
size = 'large',
isDisabled,
title,
i18nProps,
className,
children,
isDisabled = false,
onClick,
}: Props) => (
<button
disabled={isDisabled}
className={classNames(
styles.button,
styles[type],
styles[size],
isDisabled && styles.disabled,
className
)}
type={htmlType}
onClick={onClick}
>
{children}
</button>
);
...rest
}: Props) => {
const { t } = useTranslation();
return (
<button
disabled={isDisabled}
className={classNames(
styles.button,
styles[type],
styles[size],
isDisabled && styles.isDisabled,
className
)}
type={htmlType}
onClick={onClick}
{...rest}
>
{t(title, { ...i18nProps })}
</button>
);
};
export default Button;

View file

@ -37,12 +37,8 @@ const AcModal = ({
</div>
<div className={styles.content}>{children}</div>
<div className={styles.footer}>
<Button type="outline" size="small" onClick={onClose}>
{t(cancelText)}
</Button>
<Button size="small" onClick={onConfirm ?? onClose}>
{t(confirmText)}
</Button>
<Button title={cancelText} type="outline" size="small" onClick={onClose} />
<Button title={confirmText} size="small" onClick={onConfirm ?? onClose} />
</div>
</div>
</ReactModal>

View file

@ -52,10 +52,8 @@ const IframeConfirmModal = ({
/>
</div>
<div className={styles.footer}>
<Button type="secondary" onClick={onClose}>
{t(cancelText)}
</Button>
<Button onClick={onConfirm ?? onClose}>{t(confirmText)}</Button>
<Button title={cancelText} type="secondary" onClick={onClose} />
<Button title={confirmText} onClick={onConfirm ?? onClose} />
</div>
</div>
</ReactModal>

View file

@ -29,10 +29,8 @@ const MobileModal = ({
<div className={styles.container}>
<div className={styles.content}>{children}</div>
<div className={styles.footer}>
<Button type="secondary" onClick={onClose}>
{t(cancelText)}
</Button>
<Button onClick={onConfirm ?? onClose}>{t(confirmText)}</Button>
<Button title={cancelText} type="secondary" onClick={onClose} />
<Button title={confirmText} onClick={onConfirm ?? onClose} />
</div>
</div>
</ReactModal>

View file

@ -122,7 +122,7 @@ const CreateAccount = ({ className, autoFocus }: Props) => {
/>
<TermsOfUse className={styles.terms} />
<Button onClick={async () => onSubmitHandler()}>{t('action.create')}</Button>
<Button title="action.create" onClick={async () => onSubmitHandler()} />
<input hidden type="submit" />
</form>

View file

@ -119,7 +119,7 @@ const EmailPasswordless = ({ type, autoFocus, onSubmitValidation, children, clas
{children && <div className={styles.childWrapper}>{children}</div>}
<Button onClick={async () => onSubmitHandler()}>{t('action.continue')}</Button>
<Button title="action.continue" onClick={async () => onSubmitHandler()} />
<input hidden type="submit" />
</form>

View file

@ -133,7 +133,7 @@ const PhonePasswordless = ({ type, autoFocus, onSubmitValidation, children, clas
/>
{children && <div className={styles.childWrapper}>{children}</div>}
<Button onClick={async () => onSubmitHandler()}>{t('action.continue')}</Button>
<Button title="action.continue" onClick={async () => onSubmitHandler()} />
<input hidden type="submit" />
</form>

View file

@ -81,7 +81,7 @@ const ResetPassword = ({ className, autoFocus }: Props) => {
}}
/>
<Button onClick={async () => onSubmitHandler()}>{t('action.confirm')}</Button>
<Button title="action.confirm" onClick={async () => onSubmitHandler()} />
<input hidden type="submit" />
</form>

View file

@ -25,23 +25,22 @@ const SocialCreateAccount = ({ connectorId, className }: Props) => {
<>
<div className={styles.desc}>{t('description.social_bind_with_existing')}</div>
<Button
title="action.bind"
i18nProps={{ address: relatedUser }}
onClick={() => {
bindSocialRelatedUser(connectorId);
}}
>
{t('action.bind', { address: relatedUser })}
</Button>
/>
</>
)}
<div className={styles.desc}>{t('description.social_create_account')}</div>
<Button
title="action.create"
type={relatedUser ? 'secondary' : 'primary'}
onClick={() => {
registerWithSocial(connectorId);
}}
>
{t('action.create')}
</Button>
/>
<SignInMethodsLink
signInMethods={localSignInMethods}
template="social_bind_with"

View file

@ -114,7 +114,7 @@ const UsernameSignIn = ({ className, autoFocus }: Props) => {
)}
<TermsOfUse className={styles.terms} />
<Button onClick={async () => onSubmitHandler()}>{t('action.sign_in')}</Button>
<Button title="action.sign_in" onClick={async () => onSubmitHandler()} />
<input hidden type="submit" />
</form>

View file

@ -29,12 +29,11 @@ const ErrorPage = ({ title = 'description.not_found', message, rawMessage }: Pro
</div>
<Button
className={styles.backButton}
title="action.back"
onClick={() => {
navigate(-1);
}}
>
{t('action.back')}
</Button>
/>
</div>
);
};