diff --git a/tools/ui-components/src/button.test.tsx b/tools/ui-components/src/button.test.tsx deleted file mode 100644 index 739ff92d59..0000000000 --- a/tools/ui-components/src/button.test.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { Button } from './button'; - -const onClick = jest.fn(); - -describe('Button', () => { - it("should have the role 'button' and the correct text", () => { - render( - ); -}; diff --git a/tools/ui-components/src/button.types.ts b/tools/ui-components/src/button.types.ts deleted file mode 100644 index 9c57f74bfd..0000000000 --- a/tools/ui-components/src/button.types.ts +++ /dev/null @@ -1,9 +0,0 @@ -type ButtonSize = 'small' | 'medium' | 'large'; - -export interface ButtonProps { - primary?: boolean; - size?: ButtonSize; - label: string; - customKey?: string; - onClick: () => void; -} diff --git a/tools/ui-components/src/button.css b/tools/ui-components/src/button/button.css similarity index 100% rename from tools/ui-components/src/button.css rename to tools/ui-components/src/button/button.css diff --git a/tools/ui-components/src/button.stories.tsx b/tools/ui-components/src/button/button.stories.tsx similarity index 57% rename from tools/ui-components/src/button.stories.tsx rename to tools/ui-components/src/button/button.stories.tsx index 12f8758210..cd84ad4c03 100644 --- a/tools/ui-components/src/button.stories.tsx +++ b/tools/ui-components/src/button/button.stories.tsx @@ -1,7 +1,7 @@ import { Story } from '@storybook/react'; import React from 'react'; -import { Button } from './button'; -import { ButtonProps } from './button.types'; + +import { Button, ButtonProps } from '.'; const story = { title: 'Example/Button', @@ -12,27 +12,27 @@ const Template: Story = args => { return ); + + expect( + screen.getByRole('button', { name: /hello world/i }) + ).toBeInTheDocument(); + }); + + it("should have the type 'button' by default", () => { + render(); + + expect( + screen.getByRole('button', { name: /hello world/i }) + ).toHaveAttribute('type', 'button'); + }); + + it("should have the type 'submit' if it is specified", () => { + render(); + + expect( + screen.getByRole('button', { name: /hello world/i }) + ).toHaveAttribute('type', 'submit'); + }); + + it('should trigger the onClick prop on click', () => { + const onClick = jest.fn(); + + render(); + + const button = screen.getByRole('button', { name: /hello world/i }); + + userEvent.click(button); + + expect(onClick).toHaveBeenCalledTimes(1); + }); +}); diff --git a/tools/ui-components/src/button/button.tsx b/tools/ui-components/src/button/button.tsx new file mode 100644 index 0000000000..4c668be9c6 --- /dev/null +++ b/tools/ui-components/src/button/button.tsx @@ -0,0 +1,65 @@ +import React, { useMemo } from 'react'; +import { ButtonProps, ButtonSize, ButtonVariant } from './types'; + +const defaultClassNames = ['cursor-pointer', 'inline-block', 'border-3']; + +const computeClassNames = ({ + size, + variant +}: { + size: ButtonSize; + variant: ButtonVariant; +}) => { + const classNames = [...defaultClassNames]; + + // TODO: support 'link' variant + switch (variant) { + case 'danger': + classNames.push( + 'border-default-foreground-danger', + 'bg-default-background-danger', + 'text-default-foreground-danger' + ); + break; + // default variant is 'primary' + default: + classNames.push( + 'border-default-foreground-secondary', + 'bg-default-background-quaternary', + 'text-default-foreground-secondary' + ); + } + + switch (size) { + case 'large': + classNames.push('px-4 py-2.5 text-lg'); + break; + case 'small': + classNames.push('px-2.5 py-1 text-sm'); + break; + // default size is 'medium' + default: + classNames.push('px-3 py-1.5 text-md'); + } + + return classNames.join(' '); +}; + +export const Button = ({ + variant = 'primary', + size = 'medium', + type = 'button', + onClick, + children +}: ButtonProps) => { + const classes = useMemo( + () => computeClassNames({ size, variant }), + [size, variant] + ); + + return ( + + ); +}; diff --git a/tools/ui-components/src/button/index.ts b/tools/ui-components/src/button/index.ts new file mode 100644 index 0000000000..2b28f14478 --- /dev/null +++ b/tools/ui-components/src/button/index.ts @@ -0,0 +1,2 @@ +export { Button } from './button'; +export type { ButtonProps } from './types'; diff --git a/tools/ui-components/src/button/types.ts b/tools/ui-components/src/button/types.ts new file mode 100644 index 0000000000..417bceb0a3 --- /dev/null +++ b/tools/ui-components/src/button/types.ts @@ -0,0 +1,13 @@ +import { MouseEventHandler } from 'react'; + +export type ButtonVariant = 'primary' | 'danger'; + +export type ButtonSize = 'small' | 'medium' | 'large'; + +export interface ButtonProps extends React.HTMLAttributes { + children: React.ReactNode; + variant?: ButtonVariant; + size?: ButtonSize; + onClick?: MouseEventHandler; + type?: 'submit' | 'button'; +} diff --git a/tools/ui-components/src/colors.css b/tools/ui-components/src/colors.css index 0c663d75d7..1df8889430 100644 --- a/tools/ui-components/src/colors.css +++ b/tools/ui-components/src/colors.css @@ -95,10 +95,13 @@ div.light { --default-foreground-secondary: var(--gray85); --default-foreground-tertiary: var(--gray80); --default-foreground-quaternary: var(--gray75); + --default-foreground-danger: var(--red15); + --default-background-primary: var(--gray00); --default-background-secondary: var(--gray05); --default-background-tertiary: var(--gray10); --default-background-quaternary: var(--gray15); + --default-background-danger: var(--red90); } html.dark, @@ -106,8 +109,11 @@ div.dark { --default-foreground-primary: var(--gray00); --default-foreground-secondary: var(--gray05); --default-foreground-quaternary: var(--gray15); + --default-foreground-danger: var(--red90); + --default-background-primary: var(--gray90); --default-background-secondary: var(--gray85); --default-background-tertiary: var(--gray80); --default-background-quaternary: var(--gray75); + --default-background-danger: var(--red15); } diff --git a/tools/ui-components/tailwind.config.js b/tools/ui-components/tailwind.config.js index 267b770fb1..03c7744a21 100644 --- a/tools/ui-components/tailwind.config.js +++ b/tools/ui-components/tailwind.config.js @@ -15,10 +15,12 @@ module.exports = { 'default-foreground-secondary': 'var(--default-foreground-secondary)', 'default-foreground-tertiary': 'var(--default-foreground-tertiary)', 'default-foreground-quaternary': 'var(--default-foreground-quaternary)', + 'default-foreground-danger': 'var(--default-foreground-danger)', 'default-background-primary': 'var(--default-background-primary)', 'default-background-secondary': 'var(--default-background-secondary)', 'default-background-tertiary': 'var(--default-background-tertiary)', 'default-background-quaternary': 'var(--default-background-quaternary)', + 'default-background-danger': 'var(--default-background-danger)', green: { 50: 'var(--green05)', 100: 'var(--green10)', @@ -52,6 +54,16 @@ module.exports = { 800: 'var(--red80)', 900: 'var(--red90)' } + }, + borderWidth: { + 3: '3px' + }, + fontSize: { + // https://tailwindcss.com/docs/font-size#providing-a-default-line-height + // [fontSize, lineHeight] + sm: ['16px', '1.5'], + md: ['18px', '1.42857143'], + lg: ['24px', '1.3333333'] } }, plugins: []