Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Importing only the icons we need #41

Open
wants to merge 28 commits into
base: master
from
Open
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7cae0ab
#37 install necessary deps
BoyWithSilverWings Sep 2, 2019
17fcd45
remove icon css from button
BoyWithSilverWings Sep 3, 2019
3f0e4f0
add icon on button component
BoyWithSilverWings Sep 6, 2019
8ea63e3
replace fa icon with unicode
BoyWithSilverWings Sep 6, 2019
e6821ff
change to font awesome icons
BoyWithSilverWings Sep 7, 2019
8dc41c0
add an icon component
BoyWithSilverWings Sep 7, 2019
03c0e10
convert usage to fa icon
BoyWithSilverWings Sep 7, 2019
29aab0a
move changes to icon wrapper component
BoyWithSilverWings Sep 7, 2019
78a0831
position icon in center of iconWrapper
BoyWithSilverWings Sep 7, 2019
f2f1388
style the input icons accordingly
BoyWithSilverWings Sep 7, 2019
be621d5
move to maintaing map of icons
BoyWithSilverWings Sep 7, 2019
eecccc7
move iconwrapper to the input
BoyWithSilverWings Sep 7, 2019
71ba1d9
add icon wrapper to timepicker
BoyWithSilverWings Sep 7, 2019
0706db7
add left and right on paging
BoyWithSilverWings Sep 7, 2019
e80dd02
correct icon on table header
BoyWithSilverWings Sep 7, 2019
8a62507
convert action collapse to fa
BoyWithSilverWings Sep 7, 2019
1ac08cb
#37 change to prefix icon
BoyWithSilverWings Sep 8, 2019
7bdc20c
correct button margin
BoyWithSilverWings Sep 8, 2019
bcfd250
remove delete trash icon
BoyWithSilverWings Sep 8, 2019
e679b60
add fa imports to stories
BoyWithSilverWings Sep 8, 2019
2429b1b
correct story imports
BoyWithSilverWings Sep 8, 2019
e9e0788
correct proptype by omitting icon prop
BoyWithSilverWings Sep 8, 2019
de1a188
change list proptypes to reflect node
BoyWithSilverWings Sep 8, 2019
c568564
update tests
BoyWithSilverWings Sep 8, 2019
5aab6d8
add custom input
BoyWithSilverWings Sep 9, 2019
8f6ddee
setup input for rendering icon prop
BoyWithSilverWings Sep 9, 2019
2a563dd
update to correct font size
BoyWithSilverWings Sep 9, 2019
190e354
remove fontawesome fonts
BoyWithSilverWings Sep 10, 2019
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -5,7 +5,6 @@ import { withKnobs } from '@storybook/addon-knobs';
import { themes } from '@storybook/theming';

import 'bootstrap/dist/css/bootstrap.min.css';
import 'font-awesome/css/font-awesome.min.css';

import GlobalStyle from '../src/styled/GlobalStyle';
import Global from '../stories/ui/Global';
@@ -46,7 +46,10 @@
"babel-core": "7.0.0-bridge.0"
},
"dependencies": {
"font-awesome": "^4.7.0",
"@fortawesome/fontawesome-svg-core": "^1.2.22",

This comment has been minimized.

Copy link
@Aurelsicoko

Aurelsicoko Sep 13, 2019

Contributor

Why do we need all these icon libraries?

This comment has been minimized.

Copy link
@BoyWithSilverWings

BoyWithSilverWings Sep 13, 2019

Author Contributor

When using the fontawesome css and fontawesome font as earlier, the user has to download the entire font and css even if he or she does not use any of them.
When moving to these libraries, the clients who are using webpack, parcel or any of the other bundlers with tree shaking can have a bundle with only the icons we use without any extra baggage

"@fortawesome/free-regular-svg-icons": "^5.10.2",
"@fortawesome/free-solid-svg-icons": "^5.10.2",
"@fortawesome/react-fontawesome": "^0.1.4",
"invariant": "^2.2.4",
"moment": "^2.24.0",
"rc-input-number": "^4.3.9",
Binary file not shown.
Binary file not shown.

This file was deleted.

Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,12 +6,25 @@

import React from 'react';
import PropTypes from 'prop-types';
import { omit } from 'lodash';
import StyledButton from '../../styled/Button';
import PrefixIcon from '../../styled/Button/PrefixIcon';

function Button(props) {
const content = props.label ? props.label : props.children;
const icon =
props.icon === true ? (
<PrefixIcon icon="plus" />
) : (
<PrefixIcon icon={props.icon} />
);

return <StyledButton {...props}>{content}</StyledButton>;
return (
<StyledButton {...omit(props, ['icon'])}>
{icon}
{content}
</StyledButton>
);
}

Button.defaultProps = {
@@ -25,7 +38,7 @@ Button.defaultProps = {
Button.propTypes = {
children: PropTypes.node,
color: PropTypes.string,
icon: PropTypes.bool,
icon: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]),
label: PropTypes.string,
type: PropTypes.oneOf(['submit', 'reset', 'button', 'menu']),
};
@@ -9,16 +9,19 @@ import PropTypes from 'prop-types';
import moment from 'moment';
import momentPropTypes from 'react-moment-proptypes';
import 'react-dates/initialize';
import { SingleDatePicker } from 'react-dates';
import { DayPickerSingleDateController } from 'react-dates';
import { faCalendarAlt } from '@fortawesome/free-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
uncontrolledDefaultProps,
uncontrolledPropTypes,
} from '../../commonPropTypes/input';
import Input from '../InputText';

import Datepicker from '../../styled/Datepicker';

class DatePicker extends React.PureComponent {
state = { date: null, isFocused: false };
state = { date: null, isFocused: false, visible: false };

componentDidMount() {
const { value, withDefaultValue } = this.props;
@@ -38,7 +41,7 @@ class DatePicker extends React.PureComponent {
handleDateChange = date => {
const { name, onChange } = this.props;
if (moment(date).isValid()) {
this.setState({ date }, () =>
this.setState({ date, visible: false }, () =>
onChange({ target: { name, type: 'date', value: date } }),
);
}
@@ -49,21 +52,45 @@ class DatePicker extends React.PureComponent {
this.setState({ isFocused: focused });
};

handleOutsideClick = () => {
this.setState({
visible: false,
});
};

showDatepicker = () => {
this.setState({
visible: true,
});
};

render() {
const { className, displayFormat, id, name } = this.props;
const { date, isFocused } = this.state;
const { date, isFocused, visible } = this.state;

return (
<Datepicker className={className}>
<SingleDatePicker
date={date}
focused={isFocused}
id={id || name}
displayFormat={displayFormat}
numberOfMonths={1}
onFocusChange={this.handleFocusChange}
onDateChange={this.handleDateChange}
/>
<div>
<Input
type="text"
name="start_date"
id={id || name}
value={moment(date).format(displayFormat)}
readOnly
icon={<FontAwesomeIcon icon={faCalendarAlt} />}
onClick={this.showDatepicker}
/>
</div>
{visible && (
<DayPickerSingleDateController
date={date}
focused={isFocused}
numberOfMonths={1}
onFocusChange={this.handleFocusChange}
onDateChange={this.handleDateChange}
onOutsideClick={this.handleOutsideClick}
/>
)}
</Datepicker>
);
}
@@ -0,0 +1,52 @@
import React, { isValidElement } from 'react';
import PropTypes from 'prop-types';
import {
faSearch,
faEye,
faAngleLeft,
faAngleRight,
faSortDown,
faSortUp,
faTrash,
faPlus,
} from '@fortawesome/free-solid-svg-icons';
import { faClock } from '@fortawesome/free-regular-svg-icons';

import StyledIcon from '../../styled/Icon';

const iconMap = new Map([
['time', faClock],
['password', faEye],
['search', faSearch],
['left', faAngleLeft],
['right', faAngleRight],
['asc', faSortUp],
['desc', faSortDown],
['trash', faTrash],
['plus', faPlus],
]);

function Icon({ icon, className }) {
if (iconMap.has(icon)) {
return (
<StyledIcon icon={iconMap.get(icon)} className={className || undefined} />
);
}
if (isValidElement(icon)) {
return <span className={className}>{icon}</span>;
}

return null;
}

Icon.defaultProps = {
className: null,
icon: 'search',
};

Icon.propTypes = {
className: PropTypes.string,
icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
};

export default Icon;
@@ -12,25 +12,20 @@ describe('<Icon />', () => {
renderedComponent.unmount();
});

it('should have a default type equals to search', () => {
renderedComponent = renderComponent({ type: 'search' });
it('should have a default icon equals to search', () => {
renderedComponent = renderComponent({ icon: 'search' });
const defaultType = 'search';
expect(renderedComponent.at(0).prop('type')).toBe(defaultType);
expect(renderedComponent.at(0).prop('icon')).toBe(defaultType);
});
});
describe('<Icon /> icon attributes', () => {
it('should render the search icon', () => {
const tree = renderer.create(<Icon type="search" />).toJSON();
expect(tree).toMatchSnapshot();
});

it('should render the email icon', () => {
const tree = renderer.create(<Icon type="email" />).toJSON();
const tree = renderer.create(<Icon icon="search" />).toJSON();
expect(tree).toMatchSnapshot();
});

it('should render the password icon', () => {
const tree = renderer.create(<Icon type="password" />).toJSON();
const tree = renderer.create(<Icon icon="password" />).toJSON();
expect(tree).toMatchSnapshot();
});
});
@@ -0,0 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<Icon /> <Icon /> icon attributes should render the password icon 1`] = `
<svg
aria-hidden="true"
className="svg-inline--fa fa-eye fa-w-18 sc-bdVaJa bDWFJH"
data-icon="eye"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M572.52 241.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400a144 144 0 1 1 144-144 143.93 143.93 0 0 1-144 144zm0-240a95.31 95.31 0 0 0-25.31 3.79 47.85 47.85 0 0 1-66.9 66.9A95.78 95.78 0 1 0 288 160z"
fill="currentColor"
style={Object {}}
/>
</svg>
`;

exports[`<Icon /> <Icon /> icon attributes should render the search icon 1`] = `
<svg
aria-hidden="true"
className="svg-inline--fa fa-search fa-w-16 sc-bdVaJa bDWFJH"
data-icon="search"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"
fill="currentColor"
style={Object {}}
/>
</svg>
`;
@@ -8,13 +8,15 @@ import React from 'react';
import PropTypes from 'prop-types';

import StyledLinks from '../../styled/Links';
import Icon from '../Icon';

function IconLinks({ links }) {
return (
<StyledLinks>
{links.map(icon => (
<button key={icon.icon} onClick={icon.onClick} type="button">
<i className={`${icon.icon} link-icon`} />
{links.map((icon, index) => (
// eslint-disable-next-line react/no-array-index-key
<button key={index} onClick={icon.onClick} type="button">
<Icon className="link-icon" icon={icon.icon} />
</button>
))}
</StyledLinks>
@@ -28,7 +30,7 @@ IconLinks.defaultProps = {
IconLinks.propTypes = {
links: PropTypes.arrayOf(
PropTypes.shape({
icon: PropTypes.string,
icon: PropTypes.node,
onClick: PropTypes.func,
}),
),
@@ -19,7 +19,7 @@ describe('<IconLinks />', () => {
expect(
buttons
.at(0)
.find('i')
.find('svg')
.prop('className'),
).toContain('trash');
});
@@ -0,0 +1,38 @@
import React from 'react';
import PropTypes from 'prop-types';
import IconWrapper from '../../styled/InputText/IconWrapper';
import Icon from '../Icon';
import IconText from '../../styled/InputText/IconText';

function PrefixIcon({ type, icon }) {
if (icon) {
return (
<IconWrapper>
<Icon icon={icon} />
</IconWrapper>
);
}
if (type === 'search') {
return (
<IconWrapper>
<Icon icon={type} />
</IconWrapper>
);
}
if (type === 'email') {
return <IconText text="@" />;
}

return null;
}

PrefixIcon.defaultProps = {
icon: null,
};

PrefixIcon.propTypes = {
icon: PropTypes.node,
type: PropTypes.string.isRequired,
};

export default PrefixIcon;
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.