Skip to content

Commit 7cf3a0b

Browse files
committed
<Button /> supports icons directly to style hover states
1 parent 5686504 commit 7cf3a0b

File tree

7 files changed

+52
-80
lines changed

7 files changed

+52
-80
lines changed

Diff for: client/common/Button.jsx

+27-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import styled from 'styled-components';
44
import { Link } from 'react-router';
55

66
import { remSize, prop } from '../theme';
7+
import Icon, { ValidIconNameType } from './Icon';
78

89
// The '&&&' will increase the specificity of the
910
// component's CSS so that it overrides the more
@@ -28,6 +29,10 @@ const StyledButton = styled.button`
2829
&:hover:not(:disabled) {
2930
color: ${prop('buttonHoverColor')};
3031
background-color: ${prop('buttonHoverColorBackground')};
32+
33+
svg * {
34+
fill: ${prop('buttonHoverColor')};
35+
}
3136
}
3237
3338
&:disabled {
@@ -46,27 +51,36 @@ const StyledButton = styled.button`
4651
* A Button performs an primary action
4752
*/
4853
const Button = ({
49-
children, href, label, to, type, ...props
54+
children, href, iconAfterName, iconBeforeName, label, to, type, ...props
5055
}) => {
56+
const iconAfter = iconAfterName && <Icon name={iconAfterName} />;
57+
const iconBefore = iconBeforeName && <Icon name={iconBeforeName} />;
58+
59+
const content = <>{iconBefore}<span>{children}</span>{iconAfter}</>;
60+
5161
if (href) {
52-
return <StyledButton as="a" aria-label={label} href={href} {...props}>{children}</StyledButton>;
62+
return <StyledButton as="a" aria-label={label} href={href} {...props}>{content}</StyledButton>;
5363
}
5464

5565
if (to) {
56-
return <StyledButton as={Link} aria-label={label} to={to} {...props}>{children}</StyledButton>;
66+
return <StyledButton as={Link} aria-label={label} to={to} {...props}>{content}</StyledButton>;
5767
}
5868

59-
return <StyledButton aria-label={label} type={type} {...props}>{children}</StyledButton>;
69+
return <StyledButton aria-label={label} type={type} {...props}>{content}</StyledButton>;
6070
};
6171

6272
Button.defaultProps = {
6373
disabled: false,
74+
iconAfterName: null,
75+
iconBeforeName: null,
6476
href: null,
6577
label: null,
6678
to: null,
6779
type: 'button',
6880
};
6981

82+
Button.iconNames = Icon.names;
83+
7084
Button.propTypes = {
7185
/**
7286
* The visible part of the button, telling the user what
@@ -77,6 +91,15 @@ Button.propTypes = {
7791
If the button can be activated or not
7892
*/
7993
disabled: PropTypes.bool,
94+
/**
95+
* Name of icon to place before child content
96+
*/
97+
iconAfterName: ValidIconNameType,
98+
99+
/**
100+
* Name of icon to place after child content
101+
*/
102+
iconBeforeName: ValidIconNameType,
80103
/**
81104
* Specifying an href will use an <a> to link to the URL
82105
*/

Diff for: client/common/Button.stories.jsx

+6-8
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,10 @@ export const ReactRouterLink = () => (
3535
<Button to="./somewhere" label="submit">Actually a Link</Button>
3636
);
3737

38-
export const InternalElementMargin = () => (
39-
<Button>
40-
<span>Internal</span>
41-
<span>elements</span>
42-
<span>have</span>
43-
<span>right</span>
44-
<span>margins</span>
45-
</Button>
38+
export const ButtonWithIconBefore = () => (
39+
<Button iconBeforeName={Button.iconNames.github}>Create</Button>
40+
);
41+
42+
export const ButtonWithIconAfter = () => (
43+
<Button iconAfterName={Button.iconNames.github}>Create</Button>
4644
);

Diff for: client/common/Icon.jsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,20 @@ const icons = {
2222
*/
2323
const names = lodash.mapValues(icons, (value, key) => key);
2424

25+
export const ValidIconNameType = PropTypes.oneOf(Object.keys(names));
26+
2527

2628
function Icon({ name, ...props }) {
2729
return (
2830
<InlineSVG src={icons[name]} {...props} />
2931
);
3032
}
3133

34+
3235
Icon.names = names;
3336

3437
Icon.propTypes = {
35-
name: PropTypes.oneOf(Object.keys(names)).isRequired
38+
name: ValidIconNameType.isRequired
3639
};
3740

3841
export default Icon;

Diff for: client/images/google.svg

+8-46
Loading

Diff for: client/modules/User/components/APIKeyForm.jsx

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import PropTypes from 'prop-types';
22
import React from 'react';
3-
import InlineSVG from 'react-inlinesvg';
43

54
import Button from '../../../common/Button';
6-
import Icon from '../../../common/Icon';
75
import CopyableInput from '../../IDE/components/CopyableInput';
86

97
import APIKeyList from './APIKeyList';
@@ -82,13 +80,12 @@ class APIKeyForm extends React.Component {
8280
value={this.state.keyLabel}
8381
/>
8482
<Button
85-
83+
iconBeforeName={Button.iconNames.plus}
8684
disabled={this.state.keyLabel === ''}
8785
type="submit"
8886
label="Create new key"
8987
>
90-
<Icon name={Icon.names.plus} />
91-
<span>Create</span>
88+
Create
9289
</Button>
9390
</form>
9491

Diff for: client/modules/User/components/Collection.jsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { bindActionCreators } from 'redux';
99
import classNames from 'classnames';
1010

1111
import Button from '../../../common/Button';
12-
import Icon from '../../../common/Icon';
1312
import * as ProjectActions from '../../IDE/actions/project';
1413
import * as ProjectsActions from '../../IDE/actions/projects';
1514
import * as CollectionsActions from '../../IDE/actions/collections';
@@ -54,9 +53,9 @@ const ShareURL = ({ value }) => {
5453
return (
5554
<div className="collection-share" ref={node}>
5655
<Button
56+
iconAfterName={Button.iconNames.sortArrowDown}
5757
onClick={() => setShowURL(!showURL)}
58-
><span>Share</span>
59-
<Icon name={Icon.names.sortArrowDown} />
58+
>Share
6059
</Button>
6160
{ showURL &&
6261
<div className="collection__share-dropdown">

Diff for: client/modules/User/components/SocialAuthButton.jsx

+3-13
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import PropTypes from 'prop-types';
22
import React from 'react';
33
import styled from 'styled-components';
44

5-
import { remSize, prop } from '../../../theme';
5+
import { remSize } from '../../../theme';
66

77
import Button from '../../../common/Button';
8-
import Icon from '../../../common/Icon';
98

109
const authUrls = {
1110
github: '/auth-github',
@@ -24,24 +23,15 @@ const services = {
2423

2524
const StyledButton = styled(Button)`
2625
width: ${remSize(300)};
27-
28-
> * + * {
29-
margin-left: ${remSize(10)};
30-
}
31-
`;
32-
33-
const StyledIcon = styled(Icon)`
34-
width: ${remSize(32)};
35-
height: ${remSize(32)};
3626
`;
3727

3828
function SocialAuthButton({ service }) {
3929
return (
4030
<StyledButton
31+
iconBeforeName={Button.iconNames[service]}
4132
href={authUrls[service]}
4233
>
43-
<StyledIcon name={Icon.names[service]} />
44-
<span>{labels[service]}</span>
34+
{labels[service]}
4535
</StyledButton>
4636
);
4737
}

0 commit comments

Comments
 (0)