Skip to content

Commit

Permalink
Build the Button component
Browse files Browse the repository at this point in the history
  • Loading branch information
jessepinho committed Jul 16, 2024
1 parent 834ab0e commit 2ae6d51
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 0 deletions.
19 changes: 19 additions & 0 deletions packages/ui/src/Button/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, expect, it } from 'vitest';
import { getBackgroundColor } from './helpers';
import { DefaultTheme } from 'styled-components';

describe('getBackgroundColor()', () => {
const theme = { color: { primary: { main: '#f00', dark: '#a00' } } } as DefaultTheme;

it('returns the main color for the `strong` subvariant', () => {
expect(getBackgroundColor('primary', 'strong', theme)).toBe('#f00');
});

it('returns the dark color for the `subtle` subvariant', () => {
expect(getBackgroundColor('primary', 'subtle', theme)).toBe('#a00');
});

it('returns `transparent` for the `outlined` subvariant', () => {
expect(getBackgroundColor('primary', 'outlined', theme)).toBe('transparent');
});
});
17 changes: 17 additions & 0 deletions packages/ui/src/Button/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { DefaultTheme } from 'styled-components';
import { Subvariant, Variant } from './types';

export const getBackgroundColor = (
variant: Variant,
subvariant: Subvariant,
theme: DefaultTheme,
): string => {
switch (subvariant) {
case 'strong':
return theme.color[variant].main;
case 'subtle':
return theme.color[variant].dark;
default:
return 'transparent';
}
};
22 changes: 22 additions & 0 deletions packages/ui/src/Button/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Meta, StoryObj } from '@storybook/react';

import { Button } from '.';

const meta: Meta<typeof Button> = {
component: Button,
title: 'Button',
tags: ['autodocs', '!dev'],
};
export default meta;

type Story = StoryObj<typeof Button>;

export const Basic: Story = {
args: {
size: 'sparse',
children: 'Save',
variant: 'primary',
subvariant: 'strong',
disabled: false,
},
};
91 changes: 91 additions & 0 deletions packages/ui/src/Button/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { ReactNode } from 'react';
import styled, { css, DefaultTheme } from 'styled-components';
import { asTransientProps, AsTransientProps } from '../utils/asTransientProps';
import { Subvariant, Variant } from './types';
import { getBackgroundColor } from './helpers';
import { button } from '../utils/typography';

const dense = css`
border-radius: ${props => props.theme.borderRadius.full};
padding-left: ${props => props.theme.spacing(4)};
padding-right: ${props => props.theme.spacing(4)};
padding-top: ${props => props.theme.spacing(2)};
padding-bottom: ${props => props.theme.spacing(2)};
`;

const sparse = css`
border-radius: ${props => props.theme.borderRadius.sm};
padding: ${props => props.theme.spacing(4)};
`;

const outlineColorByVariant: Record<Variant, keyof DefaultTheme['color']['action']> = {
primary: 'primaryFocusOutline',
secondary: 'secondaryFocusOutline',
unshield: 'unshieldFocusOutline',
neutral: 'neutralFocusOutline',
destructive: 'destructiveFocusOutline',
};

const StyledButton = styled.button<AsTransientProps<Omit<ButtonProps, 'children'>>>`
${button}
background-color: ${props => getBackgroundColor(props.$variant, props.$subvariant, props.theme)};
border: none;
outline: ${props =>
props.$subvariant === 'outlined'
? `1px solid ${props.theme.color[props.$variant].main}`
: 'none'};
box-sizing: border-box;
color: ${props => props.theme.color.neutral.contrast};
cursor: pointer;
overflow: hidden;
position: relative;
${props => (props.$size === 'dense' ? dense : sparse)}
&:hover::before {
content: '';
position: absolute;
inset: 0;
background-color: ${props => props.theme.color.action.hoverOverlay};
z-index: 1;
}
&:active::before {
content: '';
position: absolute;
inset: 0;
background-color: ${props => props.theme.color.action.activeOverlay};
z-index: 1;
}
&:focus {
outline: 2px solid ${props => props.theme.color.action[outlineColorByVariant[props.$variant]]};
}
&:disabled::before {
content: '';
position: absolute;
inset: 0;
background-color: ${props => props.theme.color.action.disabledOverlay};
z-index: 1;
cursor: not-allowed;
}
`;

export interface ButtonProps {
children: ReactNode;
size: 'dense' | 'sparse';
variant: Variant;
subvariant: Subvariant;
disabled?: boolean;
}

export const Button = ({ children, disabled, ...rest }: ButtonProps) => {
return (
<StyledButton {...asTransientProps(rest)} disabled={disabled}>
{children}
</StyledButton>
);
};
3 changes: 3 additions & 0 deletions packages/ui/src/Button/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type Variant = 'primary' | 'secondary' | 'unshield' | 'neutral' | 'destructive';

export type Subvariant = 'strong' | 'subtle' | 'outlined';

0 comments on commit 2ae6d51

Please sign in to comment.