Skip to content

Commit

Permalink
feat: Button (#33)
Browse files Browse the repository at this point in the history
* chore: copied over button files

* chore: updated util funcs

* chore: removed console.logs, fixed Button icon props, removed Color library references

* chore: delete references to outline-shadow prop

* fix: merge icon changes

* feat: add withInfo to button

* feat: Icons (#24)

* feat: add icon directory

* feat: add icon story

* chore: added Icon to index.js

* chore: update snaps

* fix: support custom svgs

* test: update snapshots

* test: updated snaps

test: update snaps

* fix: update iconSize propTypes

* fix: update default props

* chore: remove console logs

* fix: add PADDING_BASE to size variables
  • Loading branch information
Kait Moreno committed Jan 12, 2018
1 parent a9669d6 commit 1e39844
Show file tree
Hide file tree
Showing 27 changed files with 2,784 additions and 191 deletions.
137 changes: 99 additions & 38 deletions src/components/Button/Button.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,106 @@
import React from "react";
import styled from "styled-components";
import { PropTypes } from "prop-types";
import { getColor, getPadding } from "./helpers";
import { lighten } from "polished";
import { FONT_SIZE_BASE } from "../../style/fonts/fontVariables";
import React from "react";

const Button = styled.button.attrs({
type: "button" // defaults to button rather than submit
})`
box-sizing: border-box;
user-select: none;
overflow: hidden;
color: ${props => (props.disabled ? "#bbb" : getColor(props).color)};
border: 1px solid #f0f0f0;
border-radius: 3px;
border-top-color: ${lighten("3%", "#ececec")};
border-bottom-color: #cdcdcd;
box-shadow: inset 0 0 0 rgba(0, 0, 0, 0), 0 0 0 rgba(0, 0, 0, 0),
0 0 0 rgba(0, 0, 0, 0);
width: ${props => (props.size === "fit" ? "100%" : "fit-content")};
background: ${props =>
props.disabled ? "#eee" : getColor(props).background};
padding: ${props => getPadding(props)};
text-align: center;
cursor: ${props => (props.disabled ? "default" : "pointer")};
font-size: ${FONT_SIZE_BASE};
&:hover {
background: ${props =>
props.disabled ? "#fbfbfb" : getColor(props).hover};
}
`;
import Icon from "../Icon";

Button.defaultProps = {
size: "md",
appearance: "primary"
};
import ButtonWrap from "./components/ButtonWrap";
import ButtonLabelPrefix from "./components/ButtonLabelPrefix";
import ButtonLabelSuffix from "./components/ButtonLabelSuffix";

Button.propTypes = {
onClick: PropTypes.any.isRequired, // click handler
size: PropTypes.oneOf(["xs", "sm", "md", "lg", "fit"]), // Relative size of the button
appearance: PropTypes.oneOf(["danger", "info", "primary", "warning"])
active: PropTypes.bool, // If the button should be style as active or not
children: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
clickAction: PropTypes.any.isRequired, // click handler
disabled: PropTypes.bool, // disables the button
glyph: PropTypes.string, // Glyph to display in the button
glyphColor: PropTypes.string, // Color for the glyph
glyphRatio: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // Relative size for the glyph
iconSize: PropTypes.oneOf(["normal", "xs", "sm", "lg", "xl"]),
label: PropTypes.string.isRequired, // label for the button
labelStyle: PropTypes.object,
orientation: PropTypes.oneOf(["vertical", "horizontal"]), // Vertical: Icon top, label bottom; Horizontal: Icon left, label right;
outline: PropTypes.oneOf([
"raised", // Add highlight effect to top edge and shadow effect to bottom edge
"outline", // Add outline effect
"shadow", // Add shadow effect to bottom edge
"none", // No effects
"raised-outline"
]),
prefix: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // Add prefix text to button label
size: PropTypes.oneOf(["normal", "xs", "sm", "lg", "xl"]), // Relative size of the button
style: PropTypes.object, // style prop
suffix: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // Add suffix text to button label
tabIndex: PropTypes.number,
type: PropTypes.oneOf([
"danger",
"info",
"primary",
"secondary",
"warning",
"polling"
])
};

Button.defaultProps = {
active: false,
clickAction: () => {},
disabled: false,
size: "normal",
orientation: "horizontal"
};

export default Button;
/**
* General purpose button
* @param {Object} props - see propTypes
* @returns JSX.Element
*/
export default function Button({
active,
children,
clickAction,
disabled,
glyph,
glyphRatio,
glyphColor,
iconSize,
label,
orientation,
prefix,
size,
style,
suffix,
outline,
tabIndex,
type,
labelStyle
}) {
return (
<ButtonWrap
active={active}
type={type}
size={size}
outline={outline}
orientation={orientation}
disabled={disabled}
onClick={clickAction}
tabIndex={tabIndex}
title={label}
style={style}
iconSize={iconSize}
>
{glyph && (
<Icon
glyphColor={glyphColor}
glyph={glyph}
glyphSizeRatio={glyphRatio}
/>
)}
{children}
<span style={labelStyle}>
{prefix ? <ButtonLabelPrefix>{prefix}</ButtonLabelPrefix> : ""}
{label}
{suffix ? <ButtonLabelSuffix>{suffix}</ButtonLabelSuffix> : ""}
</span>
</ButtonWrap>
);
}
141 changes: 141 additions & 0 deletions src/components/Button/Button.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React from "react";
import { mount, render } from "enzyme";

import Button from "./Button";
import ButtonWrap from "./components/ButtonWrap";

const types = ["danger", "info", "primary", "warning", "polling"];
const outlines = ["raised", "outline", "shadow", "none", "raised-outline"];
const sizes = ["normal", "xs", "sm", "lg", "xl"];
const orientations = ["vertical", "horizontal"];

const props = {
active: false,
clickAction: () => {},
disabled: false,
glyph: "Bell",
glyphColor: "#fff",
glyphRatio: 1,
iconSize: "sm",
label: "Button",
labelStyle: {},
orientation: "horizontal",
outline: "outline",
prefix: "pre",
size: "normal",
style: {},
suffix: "suff",
tabIndex: 0,
type: "primary"
};

describe("Button", () => {
describe("Snapshots", () => {
test("matches type snapshots", () => {
let tree;
types.forEach(type => {
tree = render(
<Button type={type} label={type} key={type} clickAction={() => {}} />
);
expect(tree).toMatchSnapshot();
});
});

test("matches outline snapshots", () => {
let tree;
outlines.forEach(outline => {
tree = render(
<Button
outline={outline}
label={outline}
key={outline}
clickAction={() => {}}
/>
);
expect(tree).toMatchSnapshot();
});
});

test("matches size snapshots", () => {
let tree;
sizes.forEach(size => {
tree = render(
<Button size={size} label={size} key={size} clickAction={() => {}} />
);
expect(tree).toMatchSnapshot();
});
});

test("matches orientation snapshots", () => {
let tree;
orientations.forEach(orientation => {
tree = render(
<Button
orientation={orientation}
label={orientation}
key={orientation}
clickAction={() => {}}
/>
);
expect(tree).toMatchSnapshot();
});
});

test("matches prefix/suffix snapshot", () => {
let tree = (
<Button
prefix="Prefix"
suffix="Suffix"
label="Button"
clickAction={() => {}}
/>
);
expect(tree).toMatchSnapshot();
});

test("matches active snapshot", () => {
let tree = <Button active label="Button" clickAction={() => {}} />;
expect(tree).toMatchSnapshot();
});

test("matches disabled snapshot", () => {
let tree = <Button disabled label="Button" clickAction={() => {}} />;
expect(tree).toMatchSnapshot();
});
});

describe("Props", () => {
let wrapper;
beforeEach(() => {
wrapper = mount(<Button {...props}>{["Hello World"]}</Button>);
});

test("passes correct props to ButtonWrap", () => {
expect(wrapper.find(ButtonWrap).props()).toMatchObject({
active: false,
disabled: false,
iconSize: "sm",
onClick: wrapper.props().clickAction,
orientation: "horizontal",
outline: "outline",
size: "normal",
style: {},
tabIndex: 0,
title: "Button",
type: "primary"
});
});

test("passes correct props to Glyph", () => {
expect(wrapper.find("Glyph").props()).toMatchObject({
glyphColor: "#fff",
name: "Bell",
ratio: 1
});
});

test("renders children", () => {
expect(wrapper.html().includes("Hello World")).toBe(true);
});
});
});
Loading

0 comments on commit 1e39844

Please sign in to comment.