Update Button component with iconBefore and iconAfter, clean up Icons component

This commit is contained in:
Cassie Tarakajian 2020-05-19 12:31:36 -04:00
parent f359dcec4a
commit 9d68de8dd2
7 changed files with 71 additions and 49 deletions

View file

@ -150,8 +150,10 @@ const StyledIconButton = styled.button`
* A Button performs an primary action * A Button performs an primary action
*/ */
const Button = ({ const Button = ({
children, href, kind, 'aria-label': ariaLabel, to, type, ...props children, href, kind, iconBefore, iconAfter, 'aria-label': ariaLabel, to, type, ...props
}) => { }) => {
const hasChildren = React.Children.count(children) > 0;
const content = <>{iconBefore}{hasChildren && <span>{children}</span>}{iconAfter}</>;
let StyledComponent = StyledButton; let StyledComponent = StyledButton;
if (kind === kinds.inline) { if (kind === kinds.inline) {
@ -161,19 +163,31 @@ const Button = ({
} }
if (href) { if (href) {
return <StyledComponent kind={kind} as="a" aria-label={ariaLabel} href={href} {...props}>{children}</StyledComponent>; return (
<StyledComponent
kind={kind}
as="a"
aria-label={ariaLabel}
href={href}
{...props}
>
{content}
</StyledComponent>
);
} }
if (to) { if (to) {
return <StyledComponent kind={kind} as={Link} aria-label={ariaLabel} to={to} {...props}>{children}</StyledComponent>; return <StyledComponent kind={kind} as={Link} aria-label={ariaLabel} to={to} {...props}>{content}</StyledComponent>;
} }
return <StyledComponent kind={kind} aria-label={ariaLabel} type={type} {...props}>{children}</StyledComponent>; return <StyledComponent kind={kind} aria-label={ariaLabel} type={type} {...props}>{content}</StyledComponent>;
}; };
Button.defaultProps = { Button.defaultProps = {
'children': null, 'children': null,
'disabled': false, 'disabled': false,
'iconAfter': null,
'iconBefore': null,
'kind': kinds.block, 'kind': kinds.block,
'href': null, 'href': null,
'aria-label': null, 'aria-label': null,
@ -193,6 +207,14 @@ Button.propTypes = {
If the button can be activated or not If the button can be activated or not
*/ */
'disabled': PropTypes.bool, 'disabled': PropTypes.bool,
/**
* SVG icon to place after child content
*/
'iconAfter': PropTypes.element,
/**
* SVG icon to place before child content
*/
'iconBefore': PropTypes.element,
/** /**
* The kind of button - determines how it appears visually * The kind of button - determines how it appears visually
*/ */

View file

@ -3,9 +3,7 @@ import { action } from '@storybook/addon-actions';
import { boolean, text } from '@storybook/addon-knobs'; import { boolean, text } from '@storybook/addon-knobs';
import Button from './Button'; import Button from './Button';
import Icons from './Icons'; import { GithubIcon, DropdownArrowIcon, PlusIcon } from './Icons';
const { Github, DropdownArrow, Plus } = Icons;
export default { export default {
title: 'Common/Button', title: 'Common/Button',
@ -39,28 +37,34 @@ export const ReactRouterLink = () => (
); );
export const ButtonWithIconBefore = () => ( export const ButtonWithIconBefore = () => (
<Button> <Button
<Github aria-label="Github logo" /> iconBefore={<GithubIcon aria-label="Github logo" />}
<span>Create</span> >
Create
</Button> </Button>
); );
export const ButtonWithIconAfter = () => ( export const ButtonWithIconAfter = () => (
<Button> <Button
<span>Create</span> iconAfter={<GithubIcon aria-label="Github logo" />}
<Github aria-label="Github logo" /> >
Create
</Button> </Button>
); );
export const InlineButtonWithIconAfter = () => ( export const InlineButtonWithIconAfter = () => (
<Button kind={Button.kinds.inline}> <Button
<span>File name</span> iconAfter={<DropdownArrowIcon />}
<DropdownArrow /> kind={Button.kinds.inline}
>
File name
</Button> </Button>
); );
export const InlineIconOnlyButton = () => ( export const InlineIconOnlyButton = () => (
<Button kind={Button.kinds.inline} aria-label="Add to collection"> <Button
<Plus /> aria-label="Add to collection"
</Button> iconBefore={<PlusIcon />}
kind={Button.kinds.inline}
/>
); );

View file

@ -13,43 +13,39 @@ import DropdownArrow from '../images/down-filled-triangle.svg';
// could also give these a default size, color, etc. based on the theme // could also give these a default size, color, etc. based on the theme
// Need to add size to these - like small icon, medium icon, large icon. etc. // Need to add size to these - like small icon, medium icon, large icon. etc.
function withLabel(Icon) { function withLabel(SvgComponent) {
const render = (props) => { const Icon = (props) => {
const { 'aria-label': ariaLabel } = props; const { 'aria-label': ariaLabel } = props;
if (ariaLabel) { if (ariaLabel) {
return (<Icon return (<SvgComponent
{...props} {...props}
aria-label={ariaLabel} aria-label={ariaLabel}
role="img" role="img"
focusable="false" focusable="false"
/>); />);
} }
return (<Icon return (<SvgComponent
{...props} {...props}
aria-hidden aria-hidden
focusable="false" focusable="false"
/>); />);
}; };
render.propTypes = { Icon.propTypes = {
'aria-label': PropTypes.string 'aria-label': PropTypes.string
}; };
render.defaultProps = { Icon.defaultProps = {
'aria-label': null 'aria-label': null
}; };
return render; return Icon;
} }
const Icons = { export const SortArrowUpIcon = withLabel(SortArrowUp);
SortArrowUp: withLabel(SortArrowUp), export const SortArrowDownIcon = withLabel(SortArrowDown);
SortArrowDown: withLabel(SortArrowDown), export const GithubIcon = withLabel(Github);
Github: withLabel(Github), export const GoogleIcon = withLabel(Google);
Google: withLabel(Google), export const PlusIcon = withLabel(Plus);
Plus: withLabel(Plus), export const CloseIcon = withLabel(Close);
Close: withLabel(Close), export const DropdownArrowIcon = withLabel(DropdownArrow);
DropdownArrow: withLabel(DropdownArrow)
};
export default Icons;

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { select } from '@storybook/addon-knobs'; import { select } from '@storybook/addon-knobs';
import Icons from './Icons'; import * as Icons from './Icons';
export default { export default {
title: 'Common/Icons', title: 'Common/Icons',

View file

@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Button from '../../../common/Button'; import Button from '../../../common/Button';
import Icons from '../../../common/Icons'; import { PlusIcon } from '../../../common/Icons';
import CopyableInput from '../../IDE/components/CopyableInput'; import CopyableInput from '../../IDE/components/CopyableInput';
import APIKeyList from './APIKeyList'; import APIKeyList from './APIKeyList';
@ -82,11 +82,11 @@ class APIKeyForm extends React.Component {
/> />
<Button <Button
disabled={this.state.keyLabel === ''} disabled={this.state.keyLabel === ''}
type="submit" iconBefore={<PlusIcon />}
label="Create new key" label="Create new key"
type="submit"
> >
<Icons.Plus /> Create
<span>Create</span>
</Button> </Button>
</form> </form>

View file

@ -8,7 +8,7 @@ import { bindActionCreators } from 'redux';
import classNames from 'classnames'; import classNames from 'classnames';
import Button from '../../../common/Button'; import Button from '../../../common/Button';
import Icons from '../../../common/Icons'; import { DropdownArrowIcon } from '../../../common/Icons';
import * as ProjectActions from '../../IDE/actions/project'; import * as ProjectActions from '../../IDE/actions/project';
import * as ProjectsActions from '../../IDE/actions/projects'; import * as ProjectsActions from '../../IDE/actions/projects';
import * as CollectionsActions from '../../IDE/actions/collections'; import * as CollectionsActions from '../../IDE/actions/collections';
@ -54,9 +54,9 @@ const ShareURL = ({ value }) => {
<div className="collection-share" ref={node}> <div className="collection-share" ref={node}>
<Button <Button
onClick={() => setShowURL(!showURL)} onClick={() => setShowURL(!showURL)}
iconAfter={<DropdownArrowIcon />}
> >
Share Share
<Icons.DropdownArrow />
</Button> </Button>
{ showURL && { showURL &&
<div className="collection__share-dropdown"> <div className="collection__share-dropdown">

View file

@ -4,7 +4,7 @@ import styled from 'styled-components';
import { remSize } from '../../../theme'; import { remSize } from '../../../theme';
import Icons from '../../../common/Icons'; import { GithubIcon, GoogleIcon } from '../../../common/Icons';
import Button from '../../../common/Button'; import Button from '../../../common/Button';
const authUrls = { const authUrls = {
@ -18,8 +18,8 @@ const labels = {
}; };
const icons = { const icons = {
github: Icons.Github, github: GithubIcon,
google: Icons.Google google: GoogleIcon
}; };
const services = { const services = {
@ -35,10 +35,10 @@ function SocialAuthButton({ service }) {
const ServiceIcon = icons[service]; const ServiceIcon = icons[service];
return ( return (
<StyledButton <StyledButton
iconBefore={<ServiceIcon aria-label={`${service} logo`} />}
href={authUrls[service]} href={authUrls[service]}
> >
<ServiceIcon aria-label={`${service} logo`} /> {labels[service]}
<span>{labels[service]}</span>
</StyledButton> </StyledButton>
); );
} }