diff --git a/package.json b/package.json index 55d90270..ae2f8515 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@storybook/design-system", - "version": "0.0.10", + "version": "0.0.11", "description": "Storybook design system", "repository": { "type": "git", diff --git a/src/components/Button.js b/src/components/Button.js index 2def6f30..3f16f2e1 100644 --- a/src/components/Button.js +++ b/src/components/Button.js @@ -1,6 +1,6 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; import { darken, rgba } from 'polished'; import { color, typography } from './shared/styles'; import { easing } from './shared/animation'; @@ -18,13 +18,27 @@ const Loading = styled.span` opacity: 0; `; +const APPEARANCES = { + PRIMARY: 'primary', + PRIMARY_OUTLINE: 'primaryOutline', + SECONDARY: 'secondary', + SECONDARY_OUTLINE: 'secondaryOutline', + TERTIARY: 'tertiary', + OUTLINE: 'outline', +}; + +const SIZES = { + SMALL: 'small', + MEDIUM: 'medium', +}; + const ButtonWrapper = styled.button` border: 0; border-radius: 3em; cursor: pointer; display: inline-block; overflow: hidden; - padding: ${props => (props.size === 'small' ? '8px 16px' : '13px 20px')}; + padding: ${props => (props.size === SIZES.SMALL ? '8px 16px' : '13px 20px')}; position: relative; text-align: center; text-decoration: none; @@ -38,13 +52,13 @@ const ButtonWrapper = styled.button` background: transparent; - font-size: ${props => (props.size === 'small' ? typography.size.s1 : typography.size.s2)}px; + font-size: ${props => (props.size === SIZES.SMALL ? typography.size.s1 : typography.size.s2)}px; font-weight: ${typography.weight.bold}; line-height: 1; ${props => - !props.loading && - css` + !props.isLoading && + ` &:hover { transform: translate3d(0, -2px, 0); box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; @@ -74,12 +88,12 @@ const ButtonWrapper = styled.button` } svg { - height: ${props => (props.size === 'small' ? '14' : '16')}px; - width: ${props => (props.size === 'small' ? '14' : '16')}px; + height: ${props => (props.size === SIZES.SMALL ? '14' : '16')}px; + width: ${props => (props.size === SIZES.SMALL ? '14' : '16')}px; vertical-align: top; - margin-right: ${props => (props.size === 'small' ? '4' : '6')}px; - margin-top: ${props => (props.size === 'small' ? '-1' : '-2')}px; - margin-bottom: ${props => (props.size === 'small' ? '-1' : '-2')}px; + margin-right: ${props => (props.size === SIZES.SMALL ? '4' : '6')}px; + margin-top: ${props => (props.size === SIZES.SMALL ? '-1' : '-2')}px; + margin-bottom: ${props => (props.size === SIZES.SMALL ? '-1' : '-2')}px; /* Necessary for js mouse events to not glitch out when hovering on svgs */ pointer-events: none; @@ -87,7 +101,7 @@ const ButtonWrapper = styled.button` ${props => props.disabled && - css` + ` cursor: not-allowed !important; opacity: 0.5; &:hover { @@ -96,8 +110,8 @@ const ButtonWrapper = styled.button` `} ${props => - props.unclickable && - css` + props.isUnclickable && + ` cursor: default !important; pointer-events: none; &:hover { @@ -106,8 +120,8 @@ const ButtonWrapper = styled.button` `} ${props => - props.loading && - css` + props.isLoading && + ` cursor: progress !important; opacity: 0.7; @@ -129,24 +143,22 @@ const ButtonWrapper = styled.button` ${props => props.containsIcon && - css` + ` svg { display: block; margin: 0; } - padding: ${props.size === 'small' ? '7' : '12'}px; + padding: ${props.size === SIZES.SMALL ? '7' : '12'}px; `} - - ${props => - props.appearance === 'primary' && - css` + props.appearance === APPEARANCES.PRIMARY && + ` background: ${color.primary}; color: ${color.lightest}; - ${!props.loading && - css` + ${!props.isLoading && + ` &:hover { background: ${darken(0.05, color.primary)}; } @@ -163,40 +175,13 @@ const ButtonWrapper = styled.button` `} ${props => - props.appearance === 'primary' && - props.active && - css` - background: transparent; - box-shadow: ${color.primary} 0 0 0 1px inset; - color: ${color.primary}; - - ${!props.loading && - css` - &:hover { - background: transparent; - box-shadow: ${color.primary} 0 0 0 1px inset; - } - &:active { - box-shadow: ${color.primary} 0 0 0 3em inset; - color: ${color.lightest}; - } - &:focus { - box-shadow: ${color.primary} 0 0 0 3em inset, ${rgba(color.primary, 0.4)} 0 1px 9px 2px; - } - &:focus:hover { - box-shadow: ${color.primary} 0 0 0 3em inset, ${rgba(color.primary, 0.2)} 0 8px 18px 0px; - } - `} - `} - - ${props => - props.appearance === 'secondary' && - css` + props.appearance === APPEARANCES.SECONDARY && + ` background: ${color.secondary}; color: ${color.lightest}; - ${!props.loading && - css` + ${!props.isLoading && + ` &:hover { background: ${darken(0.05, color.secondary)}; } @@ -213,42 +198,13 @@ const ButtonWrapper = styled.button` `} ${props => - props.appearance === 'secondary' && - props.active && - css` - background: transparent; - box-shadow: ${color.secondary} 0 0 0 1px inset; - color: ${color.secondary}; - - ${!props.loading && - css` - &:hover { - background: transparent; - box-shadow: ${color.secondary} 0 0 0 1px inset; - } - &:active { - box-shadow: ${color.secondary} 0 0 0 3em inset; - color: ${color.lightest}; - } - &:focus { - box-shadow: ${color.secondary} 0 0 0 3em inset, - ${rgba(color.secondary, 0.4)} 0 1px 9px 2px; - } - &:focus:hover { - box-shadow: ${color.secondary} 0 0 0 3em inset, - ${rgba(color.secondary, 0.2)} 0 8px 18px 0px; - } - `} - `} - - ${props => - props.appearance === 'tertiary' && - css` + props.appearance === APPEARANCES.TERTIARY && + ` background: ${color.tertiary}; color: ${color.darkest}; - ${!props.loading && - css` + ${!props.isLoading && + ` &:hover { background: ${darken(0.05, color.secondary)}; } @@ -265,37 +221,14 @@ const ButtonWrapper = styled.button` `} ${props => - props.appearance === 'tertiary' && - props.active && - css` - background: transparent; - box-shadow: ${color.medium} 0 0 0 1px inset; - color: ${color.darkest}; - - ${!props.loading && - css` - &:hover { - background: transparent; - box-shadow: ${color.medium} 0 0 0 1px inset; - } - &:focus { - box-shadow: ${color.medium} 0 0 0 1px inset, ${rgba(color.tertiary, 0.4)} 0 1px 9px 2px; - } - &:focus:hover { - box-shadow: ${color.medium} 0 0 0 1px inset, ${rgba(color.tertiary, 0.2)} 0 8px 18px 0px; - } - `} - `} - - ${props => - props.appearance === 'outline' && - css` + props.appearance === APPEARANCES.OUTLINE && + ` box-shadow: ${color.medium} 0 0 0 1px inset; color: ${color.dark}; background: transparent; - ${!props.loading && - css` + ${!props.isLoading && + ` &:hover { box-shadow: ${color.mediumdark} 0 0 0 1px inset; } @@ -315,8 +248,8 @@ const ButtonWrapper = styled.button` `}; ${props => - props.appearance === 'outline primary' && - css` + props.appearance === APPEARANCES.PRIMARY_OUTLINE && + ` box-shadow: ${color.primary} 0 0 0 1px inset; color: ${color.primary}; @@ -339,8 +272,8 @@ const ButtonWrapper = styled.button` `}; ${props => - props.appearance === 'outline secondary' && - css` + props.appearance === APPEARANCES.SECONDARY_OUTLINE && + ` box-shadow: ${color.secondary} 0 0 0 1px inset; color: ${color.secondary}; @@ -364,64 +297,34 @@ const ButtonWrapper = styled.button` } `}; - ${props => - props.appearance === 'outline' && - props.active && - css` - background: ${color.medium}; - box-shadow: ${color.medium} 0 0 0 1px inset; - color: ${color.darkest}; - `} - - `; const ButtonLink = ButtonWrapper.withComponent('a'); -export function Button({ - loading, - loadingText, - isLink, - appearance, - size, - containsIcon, - children, - ...props -}) { +export function Button({ isDisabled, isLoading, loadingText, isLink, children, ...props }) { const buttonInner = ( {children} - {loading && {loadingText || 'Loading...'}} + {isLoading && {loadingText || 'Loading...'}} ); + if (isLink) { return ( - + {buttonInner} ); } return ( - + {buttonInner} ); } Button.propTypes = { - loading: PropTypes.bool, + isLoading: PropTypes.bool, /** When a button is in the loading state you can supply custom text */ @@ -431,26 +334,26 @@ Button.propTypes = { */ isLink: PropTypes.bool, children: PropTypes.node.isRequired, - appearance: PropTypes.oneOf(['primary', 'secondary', 'tertiary', 'outline']), - disabled: PropTypes.bool, + appearance: PropTypes.oneOf(Object.values(APPEARANCES)), + isDisabled: PropTypes.bool, /** Prevents users from clicking on a button multiple times (for things like payment forms) */ - unclickable: PropTypes.bool, + isUnclickable: PropTypes.bool, /** Buttons with icons by themselves have a circular shape */ containsIcon: PropTypes.bool, - size: PropTypes.oneOf(['small', 'medium']), // this is enum incase we need to add more sizes + size: PropTypes.oneOf(Object.values(SIZES)), }; Button.defaultProps = { - loading: false, + isLoading: false, loadingText: null, isLink: false, - appearance: 'tertiary', - disabled: false, - unclickable: false, + appearance: APPEARANCES.TERTIARY, + isDisabled: false, + isUnclickable: false, containsIcon: false, - size: 'medium', + size: SIZES.MEDIUM, }; diff --git a/src/components/Button.stories.js b/src/components/Button.stories.js index cce19517..df1cccba 100644 --- a/src/components/Button.stories.js +++ b/src/components/Button.stories.js @@ -11,25 +11,25 @@ storiesOf('Design System|Button', module) - - - + +
- - - - -
@@ -45,7 +45,7 @@ storiesOf('Design System|Button', module) -