Update Button component with iconBefore and iconAfter, clean up Icons component
This commit is contained in:
parent
f359dcec4a
commit
9d68de8dd2
7 changed files with 71 additions and 49 deletions
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue