Skip to content
This repository was archived by the owner on Feb 27, 2024. It is now read-only.

Commit 6ad1ab1

Browse files
author
Greg Rickaby
authored
Merge pull request #105 from WebDevStudios/feature/26-design-system
Feature/26 design system
2 parents 6b56e1b + 9c44aaf commit 6ad1ab1

31 files changed

+2259
-286
lines changed

.storybook/main.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ module.exports = {
66
fastRefresh: true,
77
strictMode: true
88
},
9-
stories: ['../components/**/**/*.stories.@(js|mdx)'],
9+
stories: [
10+
'../components/**/**/*.stories.@(js|mdx)',
11+
'../docs/**/**/*.stories.@(mdx)'
12+
],
1013
addons: [
1114
'@storybook/addon-a11y',
1215
'@storybook/addon-essentials',

.storybook/preview-head.html

Lines changed: 0 additions & 11 deletions
This file was deleted.

components/atoms/Button/Button.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import Icon from '@/components/atoms/Icon'
2+
import cn from 'classnames'
3+
import NextLink from 'next/link'
4+
import PropTypes from 'prop-types'
5+
import React from 'react'
6+
import styles from './Button.module.css'
7+
8+
/**
9+
* Render the common inner part of the button component.
10+
*
11+
* @param {object} props The props object.
12+
* @param {string} props.icon Optional icon.
13+
* @param {boolean} props.iconOnly Whether this button is an icon only.
14+
* @param {string} props.iconPosition Position to render the icon.
15+
* @param {string} props.text Button text or aria-label.
16+
* @return {Element} The inside of the Button component.
17+
*/
18+
function ButtonInner({icon, iconOnly, iconPosition, text}) {
19+
return (
20+
<>
21+
{icon && iconPosition === 'left' && (
22+
<Icon icon={icon} title={text} ariaHidden={text ? true : false} />
23+
)}
24+
{!iconOnly && <span className={styles.text}>{text}</span>}
25+
{icon && iconPosition === 'right' && (
26+
<Icon icon={icon} title={text} ariaHidden={text ? true : false} />
27+
)}
28+
</>
29+
)
30+
}
31+
32+
ButtonInner.propTypes = {
33+
icon: PropTypes.string,
34+
iconOnly: PropTypes.bool,
35+
iconPosition: PropTypes.bool,
36+
text: PropTypes.string
37+
}
38+
39+
/**
40+
* @param {object} props The props object.
41+
* @param {string} props.attributes Optional attributes to add to the button.
42+
* @param {string} props.tag The wrapper tag.
43+
* @param {string} props.className Optional classNames.
44+
* @param {boolean} props.disabled Whether the button is disabled.
45+
* @param {boolean} props.fluid Whether the button should be full width.
46+
* @param {string} props.icon Icon to render inside the button.
47+
* @param {boolean} props.iconOnly Whether this button should render as an icon only button.
48+
* @param {string} props.iconPosition Position to render the icon.
49+
* @param {Function} props.onClick Button onClick function.
50+
* @param {string} props.size Button size.
51+
* @param {string} props.text Button text.
52+
* @param {string} props.type Button type.
53+
* @param {string} props.url Button link url.
54+
* @param {boolean} props.urlExternal Whether the url on this button links to an external site.
55+
* @return {Element} The button component.
56+
*/
57+
export default function Button({
58+
attributes,
59+
tag,
60+
className,
61+
disabled,
62+
fluid,
63+
icon,
64+
iconOnly,
65+
iconPosition,
66+
onClick,
67+
size,
68+
text,
69+
type,
70+
url,
71+
urlExternal
72+
}) {
73+
const buttonClassNames = cn(
74+
styles.button,
75+
className,
76+
iconOnly && styles.iconOnly,
77+
fluid && styles.fluid,
78+
disabled && styles.disabled,
79+
styles[size],
80+
styles[type]
81+
)
82+
83+
if (url) {
84+
return urlExternal ? (
85+
<a
86+
href={url}
87+
className={buttonClassNames}
88+
aria-label={text}
89+
{...attributes}
90+
>
91+
<ButtonInner
92+
icon={icon}
93+
iconOnly={iconOnly}
94+
iconPosition={iconPosition}
95+
text={text}
96+
/>
97+
</a>
98+
) : (
99+
<NextLink href={url}>
100+
<a className={buttonClassNames} aria-label={text} {...attributes}>
101+
<ButtonInner
102+
icon={icon}
103+
iconOnly={iconOnly}
104+
iconPosition={iconPosition}
105+
text={text}
106+
/>
107+
</a>
108+
</NextLink>
109+
)
110+
} else {
111+
return (
112+
// Render element with default button tag.
113+
React.createElement(
114+
`${tag}`,
115+
{
116+
className: buttonClassNames,
117+
'aria-label': text,
118+
onClick,
119+
...attributes,
120+
disabled
121+
},
122+
<ButtonInner
123+
icon={icon}
124+
iconOnly={iconOnly}
125+
iconPosition={iconPosition}
126+
text={text}
127+
/>
128+
)
129+
)
130+
}
131+
}
132+
133+
Button.propTypes = {
134+
attributes: PropTypes.object,
135+
className: PropTypes.string,
136+
disabled: PropTypes.bool,
137+
fluid: PropTypes.bool,
138+
icon: PropTypes.string,
139+
iconOnly: PropTypes.bool,
140+
iconPosition: PropTypes.oneOf(['left', 'right']),
141+
onClick: PropTypes.func,
142+
size: PropTypes.oneOf(['sm', 'md', 'lg']),
143+
tag: PropTypes.string,
144+
text: PropTypes.string.isRequired,
145+
type: PropTypes.oneOf(['primary', 'secondary']),
146+
url: PropTypes.string,
147+
urlExternal: PropTypes.bool
148+
}
149+
150+
Button.defaultProps = {
151+
disabled: false,
152+
iconOnly: false,
153+
iconPosition: 'left',
154+
size: 'md',
155+
tag: 'button',
156+
type: 'primary',
157+
urlExternal: false
158+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
.button {
2+
@apply inline-flex items-center justify-center rounded border transition-colors duration-150 ease-in-out cursor-pointer uppercase font-secondary;
3+
4+
&:hover,
5+
&:focus,
6+
&:active:not([disabled]) {
7+
@apply no-underline;
8+
}
9+
10+
/* SIZES */
11+
&.lg,
12+
&.md,
13+
&.sm {
14+
& svg + span {
15+
@apply ml-12;
16+
}
17+
18+
& span + svg {
19+
@apply ml-12;
20+
}
21+
}
22+
23+
&.lg {
24+
@apply px-16 py-12 text-h5;
25+
26+
line-height: 1.5625rem;
27+
}
28+
29+
&.md {
30+
@apply px-16 py-12 text-sm;
31+
32+
line-height: 1.25rem;
33+
}
34+
35+
&.sm {
36+
@apply px-16 py-8 text-caption;
37+
38+
line-height: 1.0625rem;
39+
}
40+
41+
&.fluid {
42+
@apply block w-full text-center;
43+
}
44+
45+
/* TYPES */
46+
&.primary {
47+
@apply bg-primary text-white border-white border-opacity-0;
48+
49+
&.disabled {
50+
@apply opacity-50;
51+
}
52+
}
53+
54+
&.secondary {
55+
@apply bg-white bg-opacity-0 text-primary border-primary;
56+
57+
&.disabled {
58+
@apply opacity-50;
59+
}
60+
}
61+
62+
&.disabled {
63+
@apply cursor-not-allowed;
64+
}
65+
}

0 commit comments

Comments
 (0)