Rework Button component to pass its label via children content

This commit is contained in:
ErickSkrauch 2020-07-22 14:20:10 +03:00
parent 993d0cdedb
commit d84497ac28
No known key found for this signature in database
GPG Key ID: 669339FCBB30EE0E
17 changed files with 175 additions and 152 deletions

View File

@ -120,23 +120,12 @@ export class AccountSwitcher extends React.Component<Props> {
))} ))}
{allowAdd ? ( {allowAdd ? (
<Link to="/login" onClick={this.props.onAfterAction}> <Link to="/login" onClick={this.props.onAfterAction}>
<Button <Button color={COLOR_WHITE} data-testid="add-account" block small className={styles.addAccount}>
color={COLOR_WHITE} <span>
data-testid="add-account" <div className={styles.addIcon} />
block <Message key="addAccount" defaultMessage="Add account" />
small </span>
className={styles.addAccount} </Button>
label={
<Message key="addAccount" defaultMessage="Add account">
{(message) => (
<span>
<div className={styles.addIcon} />
{message}
</span>
)}
</Message>
}
/>
</Link> </Link>
) : null} ) : null}
</div> </div>

View File

@ -1,15 +1,11 @@
import React from 'react'; import React from 'react';
import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { FormattedMessage as Message } from 'react-intl';
import { Button } from 'app/components/ui/form'; import { Button } from 'app/components/ui/form';
import { FooterMenu } from 'app/components/footerMenu'; import { FooterMenu } from 'app/components/footerMenu';
import appName from './appName.intl'; import appName from './appName.intl';
import styles from './appInfo.scss'; import styles from './appInfo.scss';
const messages = defineMessages({
goToAuth: 'Go to auth',
});
export default class AppInfo extends React.Component<{ export default class AppInfo extends React.Component<{
name?: string; name?: string;
description?: string; description?: string;
@ -51,7 +47,9 @@ export default class AppInfo extends React.Component<{
)} )}
</div> </div>
<div className={styles.goToAuth}> <div className={styles.goToAuth}>
<Button onClick={onGoToAuth} label={messages.goToAuth} /> <Button onClick={onGoToAuth}>
<Message key="goToAuth" defaultMessage="Go to auth" />
</Button>
</div> </div>
<div className={styles.footer}> <div className={styles.footer}>

View File

@ -1,5 +1,5 @@
import React, { MouseEventHandler } from 'react'; import React, { MouseEventHandler } from 'react';
import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { FormattedMessage as Message } from 'react-intl';
import { Helmet } from 'react-helmet-async'; import { Helmet } from 'react-helmet-async';
import { connect } from 'app/functions'; import { connect } from 'app/functions';
@ -8,10 +8,6 @@ import copy from 'app/services/copy';
import styles from './finish.scss'; import styles from './finish.scss';
const messages = defineMessages({
copy: 'Copy',
});
interface Props { interface Props {
appName: string; appName: string;
code?: string; code?: string;
@ -58,7 +54,9 @@ class Finish extends React.Component<Props> {
<div className={styles.codeContainer}> <div className={styles.codeContainer}>
<div className={styles.code}>{code}</div> <div className={styles.code}>{code}</div>
</div> </div>
<Button color="green" small label={messages.copy} onClick={this.onCopyClick} /> <Button color="green" small onClick={this.onCopyClick}>
<Message key="copy" defaultMessage="Copy" />
</Button>
</div> </div>
) : ( ) : (
<div className={styles.description}> <div className={styles.description}>

View File

@ -120,7 +120,9 @@ const ContactFormPopup: ComponentType<Props> = ({ initEmail = '', onSubmit, onCl
/> />
</div> </div>
<Button label={<Message key="send" defaultMessage="Send" />} block type="submit" disabled={isLoading} /> <Button block type="submit" disabled={isLoading}>
<Message key="send" defaultMessage="Send" />
</Button>
</Form> </Form>
</Popup> </Popup>
); );

View File

@ -30,7 +30,9 @@ const SuccessContactFormPopup: ComponentType<Props> = ({ email, onClose }) => (
</div> </div>
<div className={styles.footer}> <div className={styles.footer}>
<Button label={<Message key="close" defaultMessage="Close" />} block onClick={onClose} /> <Button block onClick={onClose}>
<Message key="close" defaultMessage="Close" />
</Button>
</div> </div>
</Popup> </Popup>
); );

View File

@ -15,7 +15,6 @@ import ApplicationsList from './list';
const labels = defineMessages({ const labels = defineMessages({
addNew: 'Add new', addNew: 'Add new',
authorization: 'Authorization',
}); });
type Props = { type Props = {
@ -123,10 +122,11 @@ function Loader({ noApps }: { noApps: boolean }) {
<LinkButton <LinkButton
to="/dev/applications/new" to="/dev/applications/new"
data-e2e="newApp" data-e2e="newApp"
label={labels.addNew}
color={COLOR_GREEN} color={COLOR_GREEN}
className={styles.emptyStateActionButton} className={styles.emptyStateActionButton}
/> >
<Message {...labels.addNew} />
</LinkButton>
</div> </div>
</div> </div>
); );
@ -145,12 +145,9 @@ function Guest() {
</div> </div>
</div> </div>
<LinkButton <LinkButton to="/login" color={COLOR_BLUE} className={styles.emptyStateActionButton}>
to="/login" <Message key="authorization" defaultMessage="Authorization" />
label={labels.authorization} </LinkButton>
color={COLOR_BLUE}
className={styles.emptyStateActionButton}
/>
</div> </div>
); );
} }

View File

@ -20,10 +20,7 @@ const messages = defineMessages({
minecraftServer: 'Minecraft server', minecraftServer: 'Minecraft server',
creatingApplication: 'Creating an application', creatingApplication: 'Creating an application',
createApplication: 'Create application',
updatingApplication: 'Updating an application', updatingApplication: 'Updating an application',
updateApplication: 'Update application',
}); });
type TypeToForm = Record< type TypeToForm = Record<
@ -115,12 +112,13 @@ export default class ApplicationForm extends React.Component<{
</div> </div>
{!!FormComponent && ( {!!FormComponent && (
<Button <Button color={COLOR_GREEN} block type="submit">
color={COLOR_GREEN} {isUpdate ? (
block <Message key="updateApplication" defaultMessage="Update application" />
label={isUpdate ? messages.updateApplication : messages.createApplication} ) : (
type="submit" <Message key="createApplication" defaultMessage="Create application" />
/> )}
</Button>
)} )}
</div> </div>
</Form> </Form>

View File

@ -16,15 +16,15 @@ const ACTION_DELETE = 'delete';
const actionButtons = [ const actionButtons = [
{ {
type: ACTION_REVOKE_TOKENS, type: ACTION_REVOKE_TOKENS,
label: messages.revokeAllTokens, label: <Message {...messages.revokeAllTokens} />,
}, },
{ {
type: ACTION_RESET_SECRET, type: ACTION_RESET_SECRET,
label: messages.resetClientSecret, label: <Message {...messages.resetClientSecret} />,
}, },
{ {
type: ACTION_DELETE, type: ACTION_DELETE,
label: messages.delete, label: <Message {...messages.delete} />,
}, },
]; ];
@ -122,13 +122,14 @@ export default class ApplicationItem extends React.Component<
{actionButtons.map(({ type, label }) => ( {actionButtons.map(({ type, label }) => (
<Button <Button
key={type} key={type}
label={label}
color={COLOR_BLACK} color={COLOR_BLACK}
className={styles.appActionButton} className={styles.appActionButton}
disabled={!!selectedAction && selectedAction !== type} disabled={!!selectedAction && selectedAction !== type}
onClick={this.onActionButtonClick(type)} onClick={this.onActionButtonClick(type)}
small small
/> >
{label}
</Button>
))} ))}
</div> </div>
@ -160,12 +161,13 @@ export default class ApplicationItem extends React.Component<
</div> </div>
<div className={styles.appActionsButtons}> <div className={styles.appActionsButtons}>
<Button <Button
label={messages.cancel}
color={COLOR_BLACK} color={COLOR_BLACK}
className={styles.appActionButton} className={styles.appActionButton}
onClick={this.onActionButtonClick(null)} onClick={this.onActionButtonClick(null)}
small small
/> >
<Message {...messages.cancel} />
</Button>
<div className={styles.continueActionButtonWrapper}> <div className={styles.continueActionButtonWrapper}>
{isActionPerforming ? ( {isActionPerforming ? (
<div className={styles.performingAction}> <div className={styles.performingAction}>
@ -193,12 +195,13 @@ export default class ApplicationItem extends React.Component<
</div> </div>
<div className={styles.appActionsButtons}> <div className={styles.appActionsButtons}>
<Button <Button
label={messages.cancel}
color={COLOR_BLACK} color={COLOR_BLACK}
className={styles.appActionButton} className={styles.appActionButton}
onClick={this.onActionButtonClick(null)} onClick={this.onActionButtonClick(null)}
small small
/> >
<Message {...messages.cancel} />
</Button>
<div className={styles.continueActionButtonWrapper}> <div className={styles.continueActionButtonWrapper}>
{isActionPerforming ? ( {isActionPerforming ? (
<div className={styles.performingAction}> <div className={styles.performingAction}>
@ -206,13 +209,14 @@ export default class ApplicationItem extends React.Component<
</div> </div>
) : ( ) : (
<Button <Button
label={messages.delete}
color={COLOR_RED} color={COLOR_RED}
className={styles.appActionButton} className={styles.appActionButton}
onClick={this.onSubmitDelete} onClick={this.onSubmitDelete}
data-testid="delete-app" data-testid="delete-app"
small small
/> >
<Message {...messages.delete} />
</Button>
)} )}
</div> </div>
</div> </div>

View File

@ -49,10 +49,11 @@ export default class ApplicationsList extends React.Component<Props, State> {
<LinkButton <LinkButton
to="/dev/applications/new" to="/dev/applications/new"
data-e2e="newApp" data-e2e="newApp"
label={messages.addNew}
color={COLOR_GREEN} color={COLOR_GREEN}
className={styles.appsListAddNewAppBtn} className={styles.appsListAddNewAppBtn}
/> >
<Message {...messages.addNew} />
</LinkButton>
</div> </div>
<div className={styles.appsListContainer}> <div className={styles.appsListContainer}>
{applications.map((app) => ( {applications.map((app) => (

View File

@ -1,6 +1,7 @@
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { defineMessages, FormattedMessage as Message } from 'react-intl';
import { Helmet } from 'react-helmet-async'; import { Helmet } from 'react-helmet-async';
import { SlideMotion } from 'app/components/ui/motion'; import { SlideMotion } from 'app/components/ui/motion';
import { ScrollIntoView } from 'app/components/ui/scroll'; import { ScrollIntoView } from 'app/components/ui/scroll';
import { Input, Button, Form, FormModel, FormError } from 'app/components/ui/form'; import { Input, Button, Form, FormModel, FormError } from 'app/components/ui/form';
@ -39,8 +40,6 @@ interface FormStepParams {
} }
const labels = defineMessages({ const labels = defineMessages({
changeEmailButton: 'Change Email',
sendEmailButton: 'Send Email',
codePlaceholder: 'Paste the code here', codePlaceholder: 'Paste the code here',
newEmailPlaceholder: 'Enter new Email', newEmailPlaceholder: 'Enter new Email',
}); });
@ -107,12 +106,13 @@ export default class ChangeEmail extends React.Component<Props, State> {
{this.renderStepForms()} {this.renderStepForms()}
<Button <Button color="violet" type="submit" block>
color="violet" {this.isLastStep() ? (
type="submit" <Message key="changeEmailButton" defaultMessage="Change Email" />
block ) : (
label={this.isLastStep() ? labels.changeEmailButton : labels.sendEmailButton} <Message key="sendEmailButton" defaultMessage="Send Email" />
/> )}
</Button>
</div> </div>
<div className={helpLinks.helpLinks}> <div className={helpLinks.helpLinks}>

View File

@ -7,7 +7,6 @@ import { BackButton } from '../ProfileForm';
import styles from '../profileForm.scss'; import styles from '../profileForm.scss';
const labels = defineMessages({ const labels = defineMessages({
changePasswordButton: 'Change password',
newPasswordLabel: 'New password:', newPasswordLabel: 'New password:',
repeatNewPasswordLabel: 'Repeat the password:', repeatNewPasswordLabel: 'Repeat the password:',
logoutOnAllDevices: 'Logout on all devices', logoutOnAllDevices: 'Logout on all devices',
@ -99,7 +98,9 @@ export default class ChangePassword extends React.Component<Props> {
</div> </div>
</div> </div>
<Button color="green" block label={labels.changePasswordButton} type="submit" /> <Button color="green" block type="submit">
<Message key="changePasswordButton" defaultMessage="Change password" />
</Button>
</div> </div>
</div> </div>
</Form> </Form>

View File

@ -1,15 +1,11 @@
import React from 'react'; import React from 'react';
import { defineMessages, FormattedMessage as Message } from 'react-intl'; import { FormattedMessage as Message } from 'react-intl';
import { Helmet } from 'react-helmet-async'; import { Helmet } from 'react-helmet-async';
import { Input, Button, Form, FormModel } from 'app/components/ui/form'; import { Input, Button, Form, FormModel } from 'app/components/ui/form';
import { BackButton } from 'app/components/profile/ProfileForm'; import { BackButton } from 'app/components/profile/ProfileForm';
import styles from '../profileForm.scss'; import styles from '../profileForm.scss';
const labels = defineMessages({
changeUsernameButton: 'Change nickname',
});
interface Props { interface Props {
username: string; username: string;
form: FormModel; form: FormModel;
@ -72,7 +68,9 @@ export default class ChangeUsername extends React.Component<Props> {
</div> </div>
</div> </div>
<Button color="green" block label={labels.changeUsernameButton} type="submit" /> <Button color="green" block type="submit">
<Message key="changeUsernameButton" defaultMessage="Change nickname" />
</Button>
</div> </div>
</div> </div>
</Form> </Form>

View File

@ -7,7 +7,6 @@ import mfaStyles from './mfa.scss';
const messages = defineMessages({ const messages = defineMessages({
codePlaceholder: 'Enter the code here', codePlaceholder: 'Enter the code here',
disable: 'Disable',
}); });
export default class MfaDisableForm extends React.Component<{ export default class MfaDisableForm extends React.Component<{
@ -49,7 +48,9 @@ export default class MfaDisableForm extends React.Component<{
</div> </div>
</div> </div>
<Button type="submit" color="green" block label={messages.disable} /> <Button type="submit" color="green" block>
<Message key="disable" defaultMessage="Disable" />
</Button>
</Form> </Form>
); );
} }

View File

@ -1,4 +1,6 @@
import React from 'react'; import React from 'react';
import { FormattedMessage as Message, defineMessages } from 'react-intl';
import { Button, FormModel } from 'app/components/ui/form'; import { Button, FormModel } from 'app/components/ui/form';
import styles from 'app/components/profile/profileForm.scss'; import styles from 'app/components/profile/profileForm.scss';
import Stepper from 'app/components/ui/stepper'; import Stepper from 'app/components/ui/stepper';
@ -7,7 +9,6 @@ import { ScrollIntoView } from 'app/components/ui/scroll';
import logger from 'app/services/logger'; import logger from 'app/services/logger';
import { getSecret, enable as enableMFA } from 'app/services/api/mfa'; import { getSecret, enable as enableMFA } from 'app/services/api/mfa';
import { Form } from 'app/components/ui/form'; import { Form } from 'app/components/ui/form';
import { defineMessages } from 'react-intl';
import Context from '../Context'; import Context from '../Context';
import Instructions from './instructions'; import Instructions from './instructions';
@ -105,7 +106,9 @@ export default class MfaEnable extends React.PureComponent<Props, State> {
{this.renderStepForms()} {this.renderStepForms()}
<Button color="green" onClick={buttonAction} loading={isLoading} block label={buttonLabel} /> <Button color="green" onClick={buttonAction} loading={isLoading} block>
<Message {...buttonLabel} />
</Button>
</div> </div>
</div> </div>
); );

View File

@ -37,7 +37,9 @@ const PasswordRequestForm: ComponentType<Props> = ({ form, onSubmit }) => (
/> />
</div> </div>
<Button type="submit" color="green" label={<Message key="continue" defaultMessage="Continue" />} block /> <Button type="submit" color="green" block>
<Message key="continue" defaultMessage="Continue" />
</Button>
</Form> </Form>
</Popup> </Popup>
); );

View File

@ -6,38 +6,77 @@ import Button from './Button';
storiesOf('UI/Form', module).add('Button', () => ( storiesOf('UI/Form', module).add('Button', () => (
<> <>
<div> <div>
<Button label="Green Button" /> <Button label="Blue Button" color="blue" />{' '} <Button>Green Button</Button> <Button color="blue">Blue Button</Button>{' '}
<Button label="DarkBlue Button" color="darkBlue" /> <Button label="Violet Button" color="violet" />{' '} <Button color="darkBlue">DarkBlue Button</Button> <Button color="violet">Violet Button</Button>{' '}
<Button label="LightViolet Button" color="lightViolet" /> <Button label="Orange Button" color="orange" />{' '} <Button color="lightViolet">LightViolet Button</Button> <Button color="orange">Orange Button</Button>{' '}
<Button label="Red Button" color="red" /> <Button label="Black Button" color="black" />{' '} <Button color="red">Red Button</Button> <Button color="black">Black Button</Button>{' '}
<Button label="White Button" color="white" /> <Button color="white">White Button</Button>
</div> </div>
<div> <div>
<h2>Disabled buttons</h2> <h2>Disabled buttons</h2>
<Button disabled label="Green Button" /> <Button disabled label="Blue Button" color="blue" />{' '} <Button disabled>Green Button</Button>
<Button disabled label="DarkBlue Button" color="darkBlue" />{' '} <Button disabled color="blue">
<Button disabled label="Violet Button" color="violet" />{' '} Blue Button
<Button disabled label="LightViolet Button" color="lightViolet" />{' '} </Button>{' '}
<Button disabled label="Orange Button" color="orange" /> <Button disabled label="Red Button" color="red" />{' '} <Button disabled color="darkBlue">
<Button disabled label="Black Button" color="black" />{' '} DarkBlue Button
<Button disabled label="White Button" color="white" /> </Button>{' '}
<Button disabled color="violet">
Violet Button
</Button>{' '}
<Button disabled color="lightViolet">
LightViolet Button
</Button>{' '}
<Button disabled color="orange">
Orange Button
</Button>{' '}
<Button disabled color="red">
Red Button
</Button>{' '}
<Button disabled color="black">
Black Button
</Button>{' '}
<Button disabled color="white">
White Button
</Button>
</div> </div>
<div> <div>
<h2>Button sizes</h2> <h2>Button sizes</h2>
<Button label="Default button" /> <Button label="Small button" small /> <br /> <Button>Default button</Button> <Button small>Small button</Button> <br />
<br /> <br />
<Button label="Block button" block /> <Button block>Block button</Button>
<br /> <br />
<Button label="Small block button" small block /> <Button small block>
Small block button
</Button>
</div> </div>
<div> <div>
<h2>Loading button</h2> <h2>Loading button</h2>
<Button loading label="Green Button" /> <Button loading label="Blue Button" color="blue" />{' '} <Button loading>Green Button</Button>{' '}
<Button loading label="DarkBlue Button" color="darkBlue" />{' '} <Button loading color="blue">
<Button loading label="Violet Button" color="violet" />{' '} Blue Button
<Button loading label="LightViolet Button" color="lightViolet" />{' '} </Button>{' '}
<Button loading label="Orange Button" color="orange" /> <Button loading label="Red Button" color="red" />{' '} <Button loading color="darkBlue">
<Button loading label="Black Button" color="black" /> <Button loading label="White Button" color="white" /> DarkBlue Button
</Button>{' '}
<Button loading color="violet">
Violet Button
</Button>{' '}
<Button loading color="lightViolet">
LightViolet Button
</Button>{' '}
<Button loading color="orange">
Orange Button
</Button>{' '}
<Button loading color="red">
Red Button
</Button>{' '}
<Button loading color="black">
Black Button
</Button>{' '}
<Button loading color="white">
White Button
</Button>
</div> </div>
</> </>
)); ));

View File

@ -1,55 +1,45 @@
import React from 'react'; import React, { ComponentType } from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
import { COLOR_GREEN } from 'app/components/ui'; import { COLOR_GREEN } from 'app/components/ui';
import { MessageDescriptor } from 'react-intl';
import { Color } from 'app/components/ui'; import { Color } from 'app/components/ui';
import buttons from '../buttons.scss'; import buttons from '../buttons.scss';
import FormComponent from './FormComponent';
export default class Button extends FormComponent< interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {
{ block?: boolean;
// TODO: drop MessageDescriptor support. It should be React.ReactNode only small?: boolean;
label: string | MessageDescriptor | React.ReactElement; loading?: boolean;
block?: boolean; className?: string;
small?: boolean; color?: Color;
loading?: boolean; disabled?: boolean;
className?: string; component?: string | React.ComponentType<any>;
color?: Color;
disabled?: boolean;
component?: string | React.ComponentType<any>;
} & React.ButtonHTMLAttributes<HTMLButtonElement>
> {
render() {
const {
color = COLOR_GREEN,
block,
small,
disabled,
className,
loading,
label,
component: ComponentProp = 'button',
...restProps
} = this.props;
return (
<ComponentProp
className={clsx(
buttons[color],
{
[buttons.loading]: loading,
[buttons.block]: block,
[buttons.smallButton]: small,
[buttons.disabled]: disabled,
},
className,
)}
disabled={disabled}
{...restProps}
>
{typeof label === 'object' && React.isValidElement(label) ? label : this.formatMessage(label)}
</ComponentProp>
);
}
} }
const Button: ComponentType<Props> = ({
color = COLOR_GREEN,
block,
small,
disabled,
className,
loading,
component: ComponentProp = 'button',
...restProps
}) => (
<ComponentProp
className={clsx(
buttons[color],
{
[buttons.loading]: loading,
[buttons.block]: block,
[buttons.smallButton]: small,
[buttons.disabled]: disabled,
},
className,
)}
disabled={disabled}
{...restProps}
/>
);
export default Button;