Add in progress and ready states to the Button component

This commit is contained in:
Kimmo Puputti 2017-09-20 16:03:39 +03:00
parent 3d3509db8b
commit 861699b610
15 changed files with 174 additions and 23 deletions

View file

@ -43,3 +43,43 @@
cursor: auto;
}
}
.inProgress {
padding: 0;
&:disabled {
background-color: var(--marketplaceColor);
}
}
.primaryButton.inProgress:disabled {
background-color: var(--successColor);
}
.secondaryButton.inProgress:disabled {
background-color: var(--matterColorLight);
}
.ready {
padding: 0;
}
.spinner {
width: 28px;
height: 28px;
stroke: var(--matterColorLight);
}
.checkmark {
width: 24px;
height: 24px;
stroke: var(--matterColorLight);
}
.secondaryButton .spinner {
stroke: var(--matterColorAnti);
}
.secondaryButton .checkmark {
stroke: var(--matterColorAnti);
}

View file

@ -14,6 +14,6 @@
.customButton {
@apply --marketplaceButtonStyles;
border-radius: 4px;
padding: 12px 0;
border-radius: 8px;
height: 100px;
}

View file

@ -17,18 +17,54 @@ const ButtonsComponent = () => {
<h3>Default button disabled:</h3>
<Button disabled>Click me</Button>
<h3>Default button in progress:</h3>
<Button inProgress>Click me</Button>
<h3>Default button ready:</h3>
<Button ready>Click me</Button>
<h3>Default button disabled and in progress:</h3>
<Button disabled inProgress>Click me</Button>
<h3>Default button disabled and ready:</h3>
<Button disabled ready>Click me</Button>
<h3>Primary button:</h3>
<PrimaryButton>Click me</PrimaryButton>
<h3>Primary button disabled:</h3>
<PrimaryButton disabled>Click me</PrimaryButton>
<h3>Primary button in progress:</h3>
<PrimaryButton inProgress>Click me</PrimaryButton>
<h3>Primary button ready:</h3>
<PrimaryButton ready>Click me</PrimaryButton>
<h3>Primary button disabled and in progress:</h3>
<PrimaryButton disabled inProgress>Click me</PrimaryButton>
<h3>Primary button disabled ready:</h3>
<PrimaryButton disabled ready>Click me</PrimaryButton>
<h3>Secondary button:</h3>
<SecondaryButton>Click me</SecondaryButton>
<h3>Secondary button disabled:</h3>
<SecondaryButton disabled>Click me</SecondaryButton>
<h3>Secondary button in progress:</h3>
<SecondaryButton inProgress>Click me</SecondaryButton>
<h3>Secondary button ready:</h3>
<SecondaryButton ready>Click me</SecondaryButton>
<h3>Secondary button disabled and in progress:</h3>
<SecondaryButton disabled inProgress>Click me</SecondaryButton>
<h3>Secondary button disabled ready:</h3>
<SecondaryButton disabled ready>Click me</SecondaryButton>
<h3>Inline text button:</h3>
<p>
Lorem ipsum <InlineTextButton>button that looks like link</InlineTextButton> dolor sit amet

View file

@ -1,28 +1,48 @@
import React, { PropTypes } from 'react';
import classNames from 'classnames';
import { IconSpinner, IconCheckmark } from '../../components';
import css from './Button.css';
const Button = props => {
const { children, className, rootClassName, ...rest } = props;
const { children, className, rootClassName, inProgress, ready, ...rest } = props;
const rootClass = rootClassName || css.root;
const classes = classNames(rootClass, className);
const classes = classNames(rootClass, className, {
[css.ready]: ready,
[css.inProgress]: inProgress,
});
return <button className={classes} {...rest}>{children}</button>;
let content;
if (inProgress) {
content = <IconSpinner rootClassName={css.spinner} />;
} else if (ready) {
content = <IconCheckmark rootClassName={css.checkmark} />;
} else {
content = children;
}
return <button className={classes} {...rest}>{content}</button>;
};
const { node, string } = PropTypes;
const { node, string, bool } = PropTypes;
Button.defaultProps = {
rootClassName: null,
className: null,
inProgress: false,
ready: false,
children: null,
};
Button.propTypes = {
rootClassName: string,
className: string,
inProgress: bool,
ready: bool,
children: node,
};

View file

@ -0,0 +1,7 @@
@import '../../marketplace.css';
.root {
width: 24px;
height: 24px;
stroke: var(--marketplaceColor);
}

View file

@ -0,0 +1,7 @@
import IconCheckmark from './IconCheckmark';
export const Icon = {
component: IconCheckmark,
props: {},
group: 'icons',
};

View file

@ -0,0 +1,30 @@
import React, { PropTypes } from 'react';
import classNames from 'classnames';
import css from './IconCheckMark.css';
const IconCheckmark = props => {
const { rootClassName, className } = props;
const classes = classNames(rootClassName || css.root, className);
return (
<svg className={classes} strokeWidth="2" xmlns="http://www.w3.org/2000/svg">
<path
d="M22.6 1.2c-.4-.3-1-.2-1.3.2L7.8 19l-5.2-5c-.4-.4-1-.4-1.3 0-.4.3-.4.8 0 1l6 5.6.6.2s.2 0 .4-.4l14.3-18c.3-.5.2-1-.2-1"
/>
</svg>
);
};
IconCheckmark.defaultProps = {
rootClassName: null,
className: null,
};
const { string } = PropTypes;
IconCheckmark.propTypes = {
rootClassName: string,
className: string,
};
export default IconCheckmark;

View file

@ -1,7 +1,7 @@
@import '../../marketplace.css';
.root {
width: 100px;
height: 100px;
width: 28px;
height: 28px;
stroke: var(--marketplaceColor);
}

View file

@ -3,35 +3,34 @@ import classNames from 'classnames';
import css from './IconSpinner.css';
// TODO: SVG needs to be changed so that it doesn't take 100x100 area,
// but just a little bit more than what actual spinner takes (~26px)
const IconSpinner = props => {
const { rootClassName, className } = props;
const classes = classNames(rootClassName || css.root, className);
return (
<svg
className={classes}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
viewBox="0 0 30 30"
strokeWidth="3"
preserveAspectRatio="xMidYMid"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="50" cy="50" fill="none" r="10" strokeWidth="3" strokeLinecap="round">
<circle cx="15" cy="15" r="12" fill="none" strokeLinecap="round">
<animateTransform
attributeName="transform"
type="rotate"
calcMode="linear"
values="0 50 50;180 50 50;720 50 50"
values="0 15 15;180 15 15;720 15 15"
keyTimes="0;0.5;1"
dur="1s"
dur=".9"
begin="0s"
repeatCount="indefinite"
/>
<animate
attributeName="stroke-dasharray"
calcMode="linear"
values="6 56;46 14;6 56"
values="9 56;46 14;9 56"
keyTimes="0;0.5;1"
dur="1"
dur=".9"
begin="0s"
repeatCount="indefinite"
/>

View file

@ -17,6 +17,7 @@ import ExternalLink from './ExternalLink/ExternalLink';
import FilterPanel from './FilterPanel/FilterPanel';
import HeroSection from './HeroSection/HeroSection';
import IconBannedUser from './IconBannedUser/IconBannedUser';
import IconCheckmark from './IconCheckmark/IconCheckmark';
import IconClose from './IconClose/IconClose';
import IconEmailAttention from './IconEmailAttention/IconEmailAttention';
import IconEmailSent from './IconEmailSent/IconEmailSent';
@ -91,6 +92,7 @@ export {
FilterPanel,
HeroSection,
IconBannedUser,
IconCheckmark,
IconClose,
IconEmailAttention,
IconEmailSent,

View file

@ -23,6 +23,8 @@ exports[`EditListingLocationForm matches snapshot 1`] = `
<Button
className={null}
disabled={false}
inProgress={false}
ready={false}
rootClassName={null}
type="submit">
Save location

View file

@ -49,6 +49,8 @@ exports[`EditListingPhotosForm matches snapshot 1`] = `
<Button
className={null}
disabled={false}
inProgress={false}
ready={false}
rootClassName={null}
type="submit">
Save photos

View file

@ -256,7 +256,9 @@ exports[`ListingPage matches snapshot 1`] = `
</div>
<Button
className={null}
inProgress={false}
onClick={[Function]}
ready={false}
rootClassName={null}>
ListingPage.ctaButtonMessage
</Button>

View file

@ -10,6 +10,7 @@ import * as DateRangeInputField from './components/DateRangeInputField/DateRange
import * as EditListingWizard from './components/EditListingWizard/EditListingWizard.example';
import * as ExpandingTextarea from './components/ExpandingTextarea/ExpandingTextarea.example';
import * as IconBannedUser from './components/IconBannedUser/IconBannedUser.example';
import * as IconCheckmark from './components/IconCheckmark/IconCheckmark.example';
import * as IconClose from './components/IconClose/IconClose.example';
import * as IconEmailAttention from './components/IconEmailAttention/IconEmailAttention.example';
import * as IconEmailSent from './components/IconEmailSent/IconEmailSent.example';
@ -79,6 +80,7 @@ export {
EditListingWizard,
ExpandingTextarea,
IconBannedUser,
IconCheckmark,
IconClose,
IconEmailAttention,
IconEmailSent,

View file

@ -150,10 +150,16 @@
@apply --marketplaceButtonFontStyles;
/* Dimensions */
display: block;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
min-height: 65px;
margin: 0;
padding: 19px 0 22px 0;
/* Push the text up from center */
padding-bottom: 2px;
/* Borders */
border: none;
@ -201,10 +207,6 @@
--marketplaceButtonStylesSecondary {
@apply --marketplaceButtonStyles;
/* We must reduce the padding since the border takes space from the
full height that should match the default button height */
padding: 16px 0 19px 0;
background-color: var(--matterColorLight);
color: var(--matterColor);