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

[Badge] Port Badge Component #6043

Merged
merged 7 commits into from
Feb 7, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/api/Badge/Badge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Badge
======



Props
-----

| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| accent | bool | false | If true, the badge will use the accent badge colors. |
| badgeClassName | string | | The CSS class name of the badge element. |
| <span style="color: #31a148">badgeContent *</span> | node | | This is the content rendered within the badge. |
| <span style="color: #31a148">children *</span> | node | | The badge will be added relativelty to this node. |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in relatively, but it should probably be relative, and even then the sentence is awkward.

How about simply "The node that the badge will be applied to."

| className | string | | The CSS class name of the root element. |
| primary | bool | false | If true, the badge will use the primary badge colors. |

Any other properties supplied will be spread to the root element.
37 changes: 37 additions & 0 deletions docs/site/src/demos/badges/FurtherBadge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// @flow weak

import React from 'react';
import { createStyleSheet } from 'jss-theme-reactor';
import Badge from 'material-ui/Badge';
import customPropTypes from 'material-ui/utils/customPropTypes';
import IconButton from 'material-ui/IconButton';
import ImageIcon from 'material-ui/svg-icons/image';
import FaceIcon from 'material-ui/svg-icons/face';

const styleSheet = createStyleSheet('FurtherBadge', () => ({
badge: {
fontSize: 20,
right: 12,
},
}));

export default function FurtherBadge(props, context) {
const classes = context.styleManager.render(styleSheet);
return (
<div className={classes.row}>
<Badge badgeContent={<IconButton><FaceIcon /></IconButton>}>
<ImageIcon />
</Badge>
<Badge
badgeContent="&copy;"
badgeClassName={classes.badge}
>
Company Name
</Badge>
</div>
);
}

FurtherBadge.contextTypes = {
styleManager: customPropTypes.muiRequired,
};
39 changes: 39 additions & 0 deletions docs/site/src/demos/badges/SimpleBadge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// @flow weak

import React from 'react';
import { createStyleSheet } from 'jss-theme-reactor';
import Badge from 'material-ui/Badge';
import customPropTypes from 'material-ui/utils/customPropTypes';
import IconButton from 'material-ui/IconButton';
import NotificationIcon from 'material-ui/svg-icons/notification';

const styleSheet = createStyleSheet('SimpleBadge', () => ({
badge: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two points here:

  1. That style doesn't have enough specificity to be applied. It's a dead style
    You need to add the badge component into the MUI_SHEET_ORDER.
  2. Users shouldn't have to add that style. It should be handled by the badge.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm @oliviertassinari , I didn't get that.

  1. I am not sure what MUI_SHEET_ORDER is for? You mean adding it in docs/site/src/components/App?
  2. That style is not needed, it is just for the demo. I have updated the example code, have a look

top: 12,
right: 12,
},
}));

export default function SimpleBadge(props, context) {
const classes = context.styleManager.render(styleSheet);
return (
<div className={classes.row}>
<Badge badgeContent={4} primary>
<NotificationIcon />
</Badge>
<Badge
accent
badgeClassName={classes.badge}
badgeContent={10}
>
<IconButton>
<NotificationIcon />
</IconButton>
</Badge>
</div>
);
}

SimpleBadge.contextTypes = {
styleManager: customPropTypes.muiRequired,
};
15 changes: 15 additions & 0 deletions docs/site/src/demos/badges/badges.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Badge

Badge generates a small badge to the top-right of its child(ren).

## Simple examples

Two examples of badges containing text, using primary and secondary colors. The badge is applied to its children - an icon for the first example, and an Icon Button with tooltip for the second.

{{demo='demos/badges/SimpleBadge.js'}}

## Further examples

Badges containing an Icon Button and text, applied to an icon, and text.

{{demo='demos/badges/FurtherBadge.js'}}
114 changes: 114 additions & 0 deletions src/Badge/Badge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// @flow weak

import React, { PropTypes } from 'react';
import classNames from 'classnames';
import { createStyleSheet } from 'jss-theme-reactor';
import customPropTypes from '../utils/customPropTypes';

const radius = 12;
const radius2x = 2 * radius;

export const styleSheet = createStyleSheet('Badge', (theme) => {
const { typography, palette } = theme;

return {
root: {
position: 'relative',
display: 'inline-block',
padding: `${radius2x}px ${radius2x}px ${radius}px ${radius}px`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @mbrookes. I don't think that the badge should impact the layout of the element.
What about removing that padding property?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree too. done

},
badge: {
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
alignContent: 'center',
alignItems: 'center',
position: 'absolute',
top: 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about?

top: -radius,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

right: 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about?

right: -radius,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

fontWeight: typography.fontWeight,
fontSize: radius,
width: radius2x,
height: radius2x,
borderRadius: '50%',
backgroundColor: palette.color,
color: palette.textColor,
},
primary: {
backgroundColor: palette.primary[500],
color: palette.getContrastText(palette.primary[500]),
},
accent: {
backgroundColor: palette.accent.A200,
color: palette.getContrastText(palette.accent.A200),
},
};
});

export default function Badge(props, context) {
const {
badgeClassName: badgeClassNameProp,
badgeContent,
className: classNameProp,
children,
primary,
accent,
...other
} = props;

const classes = context.styleManager.render(styleSheet);
const className = classNames({
[classes.root]: true,
}, classNameProp);
const badgeClassName = classNames({
[classes.badge]: true,
[classes.primary]: primary,
[classes.accent]: accent,
}, badgeClassNameProp);

return (
<div className={className} {...other}>
{children}
<span className={badgeClassName}>
{badgeContent}
</span>
</div>
);
}

Badge.propTypes = {
/**
* If true, the badge will use the accent badge colors.
*/
accent: PropTypes.bool,
/**
* The css class name of the badge element.
*/
badgeClassName: PropTypes.string,
/**
* This is the content rendered within the badge.
*/
badgeContent: PropTypes.node.isRequired,
/**
* The badge will be added relativelty to this node.
*/
children: PropTypes.node.isRequired,
/**
* The css class name of the root element.
*/
className: PropTypes.string,
/**
* If true, the badge will use the primary badge colors.
*/
primary: PropTypes.bool,
};

Badge.defaultProps = {
primary: false,
accent: false,
};

Badge.contextTypes = {
styleManager: customPropTypes.muiRequired,
};
89 changes: 89 additions & 0 deletions src/Badge/Badge.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// @flow weak
/* eslint-env mocha */
import React from 'react';
import { createShallowWithContext } from 'test/utils';
import { assert } from 'chai';
import Badge, { styleSheet } from './Badge';

describe('<Badge />', () => {
let shallow;
let classes;

before(() => {
shallow = createShallowWithContext();
classes = shallow.context.styleManager.render(styleSheet);
});

const testChildren = <div className="unique">Hello World</div>;

it('renders children and badgeContent', () => {
const wrapper = shallow(
<Badge badgeContent={10}>{testChildren}</Badge>,
);

assert.strictEqual(wrapper.contains(testChildren), true, 'should contain the children');
assert.ok(wrapper.find('span').length, 'should contain the badgeContent');
});

it('renders children and overwrite badge class', () => {
const badgeClassName = 'testBadgeClassName';

const wrapper = shallow(
<Badge badgeContent={10} badgeClassName={badgeClassName}>{testChildren}</Badge>,
);

assert.strictEqual(wrapper.contains(testChildren), true, 'should contain the children');
assert.strictEqual(wrapper.find('span').hasClass('testBadgeClassName'), true,
'should contain the badgeClassName');
});

it('renders children by default', () => {
const wrapper = shallow(
<Badge badgeContent={10}>{testChildren}</Badge>,
);

assert.strictEqual(wrapper.contains(testChildren), true, 'should contain the children');
});

it('renders children and className', () => {
const wrapper = shallow(
<Badge badgeContent={10} className="testClassName">{testChildren}</Badge>,
);

assert.strictEqual(wrapper.contains(testChildren), true, 'should contain the children');
assert.strictEqual(wrapper.is('.testClassName'), true, 'should contain the className');
});

it('renders children and have primary styles', () => {
const wrapper = shallow(
<Badge badgeContent={10} primary>{testChildren}</Badge>,
);

assert.strictEqual(wrapper.contains(testChildren), true, 'should contain the children');
assert.strictEqual(wrapper.find('span').hasClass(classes.primary), true,
'should have primary class');
});

it('renders children and have accent styles', () => {
const wrapper = shallow(
<Badge badgeContent={10} accent>{testChildren}</Badge>,
);

assert.strictEqual(wrapper.contains(testChildren), true, 'should contain the children');
assert.strictEqual(wrapper.find('span').hasClass(classes.accent), true,
'should have accent class');
});

it('renders children and overwrite root styles', () => {
const style = {
backgroundColor: 'red',
};
const wrapper = shallow(
<Badge badgeContent={10} style={style}>{testChildren}</Badge>,
);

assert.strictEqual(wrapper.contains(testChildren), true, 'should contain the children');
assert.strictEqual(wrapper.node.props.style.backgroundColor, style.backgroundColor,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert.strictEqual(wrapper.props().style.backgroundColor, style.backgroundColor,

'should overwrite badge backgroundColor');
});
});
3 changes: 3 additions & 0 deletions src/Badge/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* eslint-disable flowtype/require-valid-file-annotation */

export default from './Badge';
15 changes: 15 additions & 0 deletions src/svg-icons/notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-disable */

import React from 'react';
import pure from 'recompose/pure';
import SvgIcon from '../SvgIcon';

let Notification = (props) => (
<SvgIcon {...props}>
<path d="M14,20A2,2 0 0,1 12,22A2,2 0 0,1 10,20H14M12,2A1,1 0 0,1 13,3V4.08C15.84,4.56 18,7.03 18,10V16L21,19H3L6,16V10C6,7.03 8.16,4.56 11,4.08V3A1,1 0 0,1 12,2Z"/>
</SvgIcon>
);
Notification = pure(Notification);
Notification.muiName = 'SvgIcon';

export default Notification;