Skip to content

Commit

Permalink
feat(button): implement the beginnings of the utds button
Browse files Browse the repository at this point in the history
  • Loading branch information
stdavis committed May 16, 2023
1 parent 959e6f8 commit f2fb7f5
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 7 deletions.
153 changes: 153 additions & 0 deletions src/utah-design-system/Button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { createKeyLookup } from './utils';

const COLORS = {
none: {
outlined: [
'border-slate-600 text-slate-600',
'focus:border-slate-600 focus:ring-slate-600',
'hover:border-slate-600 hover:bg-slate-600',
].join(' '),
solid: [
'border-slate-600 bg-slate-600 text-white',
'focus:border-slate-600 focus:ring-slate-600',
'hover:border-slate-700 hover:bg-slate-700',
].join(' '),
},
primary: {
outlined: [
'border-primary text-primary',
'focus:border-primary focus:ring-primary',
'hover:border-primary hover:bg-primary',
].join(' '),
solid: [
'border-primary bg-primary text-white',
'focus:border-primary focus:ring-primary',
'hover:border-primary-dark hover:bg-primary-dark',
].join(' '),
},
secondary: {
outlined: [
'border-secondary text-secondary',
'focus:border-secondary focus:ring-secondary',
'hover:border-secondary hover:bg-secondary',
].join(' '),
solid: [
'border-secondary bg-secondary text-white',
'focus:border-secondary focus:ring-secondary',
'hover:border-secondary-dark hover:bg-secondary-dark',
].join(' '),
},
accent: {
outlined: [
'border-accent text-accent',
'focus:border-accent focus:ring-accent',
'hover:border-accent hover:bg-accent',
].join(' '),
solid: [
'border-accent bg-accent text-white',
'focus:border-accent focus:ring-accent',
'hover:border-accent-dark hover:bg-accent-dark',
].join(' '),
},
};

const APPEARANCES = {
solid: 'solid',
outlined: 'outlined',
};

const SIZES = {
xs: 'text-xs px-2 py-0',
sm: 'text-sm px-3 py-0 h-6',
base: 'px-7 py-1 h-8 min-h-[2rem]',
lg: 'text-lg px-8 py-2 h-10 min-h-[2.5rem]',
xl: 'text-xl px-10 py-3 h-12 min-h-[3rem]',
};

function Button({
appearance,
busy,
children,
className,
color,
disabled,
onClick,
size,
}) {
if (busy) {
disabled = true;
}

return (
<button
className={clsx(
'flex w-fit cursor-pointer items-center justify-center rounded-full',
appearance === APPEARANCES.outlined && 'border-2',
COLORS[color][appearance],
SIZES[size],
'transition-all duration-200 ease-in-out',
'focus:outline-none focus:ring-2 focus:ring-opacity-50',
'hover:text-white',
!disabled && 'active:scale-95 active:shadow-inner',
'disabled:cursor-not-allowed disabled:opacity-50',
className
)}
disabled={disabled}
onClick={onClick}
>
{children}
{busy && (
<svg
className="ml-1 h-5 w-5 animate-spin motion-reduce:hidden"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
)}
</button>
);
}

Button.propTypes = {
appearance: PropTypes.oneOf(Object.keys(APPEARANCES)),
busy: PropTypes.bool,
children: PropTypes.node.isRequired,
className: PropTypes.string,
color: PropTypes.oneOf(Object.keys(COLORS)),
disabled: PropTypes.bool,
onClick: PropTypes.func,
/**
* Size of the button. Corresponds with the tailwind text sizes (base, sm, lg, xl)
*/
size: PropTypes.oneOf(Object.keys(SIZES)),
};

Button.defaultProps = {
appearance: 'outlined',
busy: false,
color: 'none',
disabled: false,
size: 'base',
};

Button.Colors = createKeyLookup(COLORS);
Button.Appearances = createKeyLookup(APPEARANCES);
Button.Sizes = createKeyLookup(SIZES);

export default Button;
94 changes: 94 additions & 0 deletions src/utah-design-system/Button.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import Button from './Button';

export default {
title: 'Utah Design System/Button',
component: Button,
};

export const Default = () => (
<>
<h3>Outlined</h3>
<p>
<Button>Default</Button>
<Button color={Button.Colors.primary}>Primary</Button>
<Button color={Button.Colors.secondary}>Secondary</Button>
<Button color={Button.Colors.accent}>Accent</Button>
</p>

<h3>Solid</h3>
<p>
<Button appearance={Button.Appearances.solid}>Default</Button>
<Button
appearance={Button.Appearances.solid}
color={Button.Colors.primary}
>
Primary
</Button>
<Button
appearance={Button.Appearances.solid}
color={Button.Colors.secondary}
>
Secondary
</Button>
<Button
appearance={Button.Appearances.solid}
color={Button.Colors.accent}
>
Accent
</Button>
</p>

<h3>Disabled</h3>
<p>
<Button disabled>Default</Button>
<Button
disabled
appearance={Button.Appearances.solid}
color={Button.Colors.primary}
>
Primary
</Button>
<Button disabled color={Button.Colors.secondary}>
Secondary
</Button>
<Button
disabled
appearance={Button.Appearances.solid}
color={Button.Colors.accent}
>
Accent
</Button>
</p>

<h3>Busy</h3>
<p>
<Button busy>Default</Button>
<Button
busy
color={Button.Colors.secondary}
appearance={Button.Appearances.solid}
>
Secondary
</Button>
</p>

<h3>Sizes</h3>
<p>
<Button size={Button.Sizes.xs}>Extra Small</Button>
<Button size={Button.Sizes.sm}>Small</Button>
<Button size={Button.Sizes.base} appearance="solid" color="primary">
Medium
</Button>
<Button size={Button.Sizes.lg}>Large</Button>
<Button size={Button.Sizes.xl} appearance={Button.Appearances.solid}>
Extra Large
</Button>
</p>

<h3>Widths</h3>
<p>
<Button>Default</Button>
<Button className="w-full">Full Width</Button>
</p>
</>
);
7 changes: 3 additions & 4 deletions src/utah-design-system/Icon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as AccessibleIcon from '@radix-ui/react-accessible-icon';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { forwardRef } from 'react';
import { createKeyLookup } from './utils';

const ICONS = {
account: {
Expand Down Expand Up @@ -183,9 +184,7 @@ Icon.defaultProps = {
size: 'base',
};

Icon.Names = Object.keys(ICONS).reduce((acc, key) => {
acc[key] = key;
return acc;
}, {});
Icon.Names = createKeyLookup(ICONS);
Icon.Sizes = createKeyLookup(SIZE_CLASS_NAMES);

export default Icon;
7 changes: 7 additions & 0 deletions src/utah-design-system/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function createKeyLookup(object) {
return Object.keys(object).reduce((acc, key) => {
acc[key] = key;

return acc;
}, {});
}
18 changes: 18 additions & 0 deletions src/utah-design-system/utils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, it } from 'vitest';
import { createKeyLookup } from './utils';

describe('createKeyLookup', () => {
it('should return an object with keys equal to values', () => {
const object = {
key1: 'value1',
key2: 'value2',
};
const result = createKeyLookup(object);
const expected = {
key1: 'key1',
key2: 'key2',
};

expect(result).toEqual(expected);
});
});
21 changes: 18 additions & 3 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@ export default {
content: ['./index.html', './src/**/*.{js,jsx}'],
theme: {
colors: {
primary: '#0080b7',
secondary: '#09b0da',
accent: '#add361',
primary: {
light: '#009DE0',
DEFAULT: '#0080B7',
dark: '#00597F',
},
secondary: {
light: '#16C9F5',
DEFAULT: '#09B0DA',
dark: '#0785A4',
},
accent: {
light: '#CEE5A1',
DEFAULT: '#ADD361',
dark: '#74992B',
},
slate: colors.slate,
white: colors.white,
transparent: colors.transparent,
Expand Down Expand Up @@ -49,6 +61,9 @@ export default {
},
},
},
boxShadow: {
inner: 'inset 0 2px 4px rgba(0, 0, 0, 0.5)',
},
},
},
plugins: [require('@tailwindcss/forms')],
Expand Down

0 comments on commit f2fb7f5

Please sign in to comment.