Skip to content

Commit 504ab3c

Browse files
authored
Merge pull request #6 from machielsdev/feature/buttons
Feature/buttons
2 parents 0b884d1 + 3b30804 commit 504ab3c

File tree

10 files changed

+404
-7
lines changed

10 files changed

+404
-7
lines changed

__tests__/Button.test.tsx

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { shallow } from 'enzyme';
2+
import React from 'react';
3+
import Button from '@/components/Button';
4+
import { Variant } from '@/components';
5+
6+
describe('Button test', () => {
7+
it('should render button', () => {
8+
const container = shallow(
9+
<Button
10+
variant={Variant.PRIMARY}
11+
>
12+
Hello world
13+
</Button>
14+
);
15+
16+
expect(container.find('button').length).toBe(1);
17+
});
18+
19+
it('should render button as link', () => {
20+
const container = shallow(
21+
<Button
22+
as="a"
23+
variant={Variant.PRIMARY}
24+
>
25+
Hello world
26+
</Button>
27+
);
28+
29+
expect(container.find('a').length).toBe(1);
30+
});
31+
32+
it('should render block button', () => {
33+
const container = shallow(
34+
<Button
35+
variant={Variant.PRIMARY}
36+
block
37+
>
38+
Hello world
39+
</Button>
40+
);
41+
42+
expect(container.find('button').hasClass('btn-block')).toBeTruthy();
43+
});
44+
45+
it('should render small button', () => {
46+
const container = shallow(
47+
<Button
48+
variant={Variant.PRIMARY}
49+
small
50+
>
51+
Hello world
52+
</Button>
53+
);
54+
55+
expect(container.find('button').hasClass('btn-small')).toBeTruthy();
56+
});
57+
58+
it('should render button with icon left', () => {
59+
const container = shallow(
60+
<Button
61+
variant={Variant.PRIMARY}
62+
iconLeft={<i>icon</i>}
63+
>
64+
Hello world
65+
</Button>
66+
);
67+
68+
expect(container.find('button').find('i').text()).toContain('icon');
69+
});
70+
71+
it('should render button with icon left', () => {
72+
const container = shallow(
73+
<Button
74+
variant={Variant.PRIMARY}
75+
iconRight={<i>icon</i>}
76+
>
77+
Hello world
78+
</Button>
79+
);
80+
81+
expect(container.find('button').find('i').text()).toContain('icon');
82+
});
83+
});

package-lock.json

+35-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
"webpack-dev-server": "^3.11.0"
4141
},
4242
"dependencies": {
43+
"@fortawesome/fontawesome-free": "^5.15.1",
44+
"@fortawesome/fontawesome-svg-core": "^1.2.32",
45+
"@fortawesome/free-regular-svg-icons": "^5.15.1",
46+
"@fortawesome/react-fontawesome": "^0.1.11",
4347
"clsx": "^1.1.1",
4448
"react": "^17.0.1"
4549
}

src/components/Button/index.tsx

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import * as React from 'react';
2+
import clsx from 'clsx';
3+
import PropTypes from 'prop-types';
4+
import { Variant } from '@/components';
5+
6+
export type ButtonComponentTypes = 'button' | 'a';
7+
8+
export interface ButtonProps extends React.HTMLAttributes<HTMLElement> {
9+
as?: ButtonComponentTypes;
10+
variant: Variant | string;
11+
iconLeft?: React.ReactElement;
12+
iconRight?: React.ReactElement;
13+
block?: boolean;
14+
small?: boolean;
15+
}
16+
17+
const Button = React.forwardRef<HTMLElement, ButtonProps>((
18+
{
19+
as: Component = 'button',
20+
children,
21+
className,
22+
variant,
23+
iconLeft,
24+
iconRight,
25+
block,
26+
small,
27+
...rest
28+
},
29+
ref
30+
): React.ReactElement => {
31+
return (
32+
<Component
33+
ref={ref as React.RefObject<any>}
34+
className={clsx(
35+
'btn',
36+
`btn-${variant}`,
37+
block &&'btn-block',
38+
small && 'btn-small',
39+
className
40+
)}
41+
{...rest}
42+
>
43+
{iconLeft && (
44+
<div className="icon-container icon-left">
45+
{iconLeft}
46+
</div>
47+
)}
48+
<span>{children}</span>
49+
{iconRight && (
50+
<div className="icon-container icon-right">
51+
{iconRight}
52+
</div>
53+
)}
54+
</Component>
55+
);
56+
});
57+
58+
Button.displayName = 'Button';
59+
Button.propTypes = {
60+
as: PropTypes.oneOf(['button', 'a']),
61+
children: PropTypes.node.isRequired,
62+
className: PropTypes.string,
63+
variant: PropTypes.string.isRequired,
64+
iconLeft: PropTypes.element,
65+
iconRight: PropTypes.element,
66+
block: PropTypes.bool,
67+
small: PropTypes.bool
68+
};
69+
70+
export default Button;

src/components/utils/Variant.ts

+13
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,15 @@
11
export enum Variant {
2+
PRIMARY = 'primary',
3+
PRIMARY_GHOST = 'primary-ghost',
4+
SECONDARY = 'secondary',
5+
DANGER = 'danger',
6+
DANGER_GHOST = 'danger-ghost'
27
}
8+
9+
export const variantList: string[] = [
10+
'primary',
11+
'primary-ghost',
12+
'secondary',
13+
'danger',
14+
'danger-ghost'
15+
];

src/style/base/_base.scss

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
html {
22
font-size: var(--base-font-size);
3+
-moz-osx-font-smoothing: grayscale;
4+
-webkit-font-smoothing: antialiased;
35
}
46

57
body {
@@ -9,4 +11,5 @@ body {
911
font: {
1012
family: var(--base-font-family);
1113
}
14+
line-height: 1.2rem;
1215
}

src/style/base/_typography.scss

+2
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@
8282
url('../fonts/inter/inter-v2-latin-900.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
8383
}
8484

85+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap');
86+
8587
h1 {
8688
font-size: 2rem;
8789
color: mixins.color('gray', 900);

src/style/base/_variables.scss

+86-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
@use "mixins";
22
@use "colors";
33

4-
$primary-radius: 4px;
5-
64
/**
75
* 1. Base
86
*/
@@ -12,6 +10,8 @@ $base-font-color: mixins.color('blueGray', 900);
1210
$base-font-family: 'Inter, apple-sf-pro-text, Helvetica, Arial, sans-serif';
1311
$base-font-size: 16px;
1412
$base-border-color: mixins.color('gray', 300);
13+
$base-border-radius: 4px;
14+
$base-gutter: 1rem;
1515

1616
:root {
1717
--base-font-size: #{$base-font-size};
@@ -20,10 +20,41 @@ $base-border-color: mixins.color('gray', 300);
2020
--base-font-color: #{$base-font-color};
2121
--base-font-family: #{$base-font-family};
2222
--base-border-color: #{$base-border-color};
23+
--base-border-radius: #{$base-border-radius};
24+
--base-gutter: #{$base-gutter};
2325
}
2426

2527
/**
26-
* 2. Panel
28+
* 2. Colors
29+
*/
30+
$primary-color: mixins.color('deepPurple', 600);
31+
$primary-color-hover: mixins.color('deepPurple', 700);
32+
$primary-color-active: mixins.color('deepPurple', 800);
33+
34+
$secondary-color: mixins.color('red', 600);
35+
$secondary-color-hover: mixins.color('red', 700);
36+
$secondary-color-active: mixins.color('red', 800);
37+
38+
$danger-color: mixins.color('red', 700);
39+
$danger-color-hover: mixins.color('red', 800);
40+
$danger-color-active: mixins.color('red', 900);
41+
42+
:root {
43+
--primary-color: #{$primary-color};
44+
--primary-color-hover: #{$primary-color-hover};
45+
--primary-color-active: #{$primary-color-active};
46+
47+
--secondary-color: #{$secondary-color};
48+
--secondary-color-hover: #{$secondary-color-hover};
49+
--secondary-color-active: #{$secondary-color-active};
50+
51+
--danger-color: #{$danger-color};
52+
--danger-color-hover: #{$danger-color-hover};
53+
--danger-color-active: #{$danger-color-active};
54+
}
55+
56+
/**
57+
* 3. Panel
2758
*/
2859
$panel-background: #fff;
2960
$panel-gutter: 2rem;
@@ -34,10 +65,61 @@ $panel-gutter: 2rem;
3465
}
3566

3667
/**
37-
* 3. Page
68+
* 4. Page
3869
*/
3970
$page-gutter: 2rem;
4071

4172
:root {
4273
--page-gutter: #{$page-gutter};
4374
}
75+
76+
/**
77+
* 5. Button
78+
*/
79+
$button-padding: 1rem;
80+
$button-font-weight: 600;
81+
$button-font-size: 1rem;
82+
83+
$primary-button-ghost-hover-background: mixins.color('deepPurple', 50);
84+
$primary-button-ghost-active-background: mixins.color('deepPurple', 75);
85+
86+
$secondary-button-background: transparent;
87+
$secondary-button-hover-background: mixins.color('deepPurple', 50);
88+
$secondary-button-active-background: mixins.color('deepPurple', 75);
89+
90+
$danger-button-ghost-hover-background: mixins.color('red', 50);
91+
$danger-button-ghost-active-background: mixins.color('red', 75);
92+
93+
:root {
94+
--button-padding: #{$button-padding};
95+
--button-font-weight: #{$button-font-weight};
96+
--button-font-size: #{$button-font-size};
97+
98+
--primary-button-background: var(--primary-color);
99+
--primary-button-border: var(--primary-color);
100+
--primary-button-hover-background: var(--primary-color-hover);
101+
--primary-button-hover-border: var(--primary-color-hover);
102+
--primary-button-active-background: var(--primary-color-active);
103+
--primary-button-active-border: var(--primary-color-active);
104+
105+
--primary-button-ghost-color: var(--primary-color);
106+
--primary-button-ghost-border: var(--primary-color);
107+
--primary-button-ghost-hover-background: #{$primary-button-ghost-hover-background};
108+
--primary-button-ghost-active-background: #{$primary-button-ghost-active-background};
109+
110+
--secondary-button-background: #{$secondary-button-background};
111+
--secondary-button-hover-background: #{$secondary-button-hover-background};
112+
--secondary-button-active-background: #{$secondary-button-active-background};
113+
114+
--danger-button-background: var(--danger-color);
115+
--danger-button-border: var(--danger-color);
116+
--danger-button-hover-background: var(--danger-color-hover);
117+
--danger-button-hover-border: var(--danger-color-hover);
118+
--danger-button-active-background: var(--danger-color-active);
119+
--danger-button-active-border: var(--danger-color-active);
120+
121+
--danger-button-ghost-color: var(--danger-color);
122+
--danger-button-ghost-border: var(--danger-color);
123+
--danger-button-ghost-hover-background: #{$danger-button-ghost-hover-background};
124+
--danger-button-ghost-active-background: #{$danger-button-ghost-active-background};
125+
}

0 commit comments

Comments
 (0)