diff --git a/.eslintrc b/.eslintrc index 0c9597ce98..8de3693a9e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -29,6 +29,7 @@ "tsx": "never" } ], + "import/prefer-default-export": "off", "react/jsx-filename-extension": [1, { "extensions": [".jsx", ".tsx"] }], "comma-dangle": 0, // not sure why airbnb turned this on. gross! "default-param-last": 0, @@ -131,7 +132,8 @@ "rules": { "no-use-before-define": "off", "import/no-extraneous-dependencies": "off", - "no-unused-vars": "off" + "no-unused-vars": "off", + "react/require-default-props": "off" } }, { diff --git a/.prettierrc b/.prettierrc index e2da8bb25a..df6b0841b0 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,7 +5,6 @@ "insertPragma": false, "jsxBracketSameLine": false, "jsxSingleQuote": false, - "parser": "babel", "printWidth": 80, "proseWrap": "never", "requirePragma": false, diff --git a/client/common/Button.test.tsx b/client/common/Button.test.tsx new file mode 100644 index 0000000000..863f2d89b2 --- /dev/null +++ b/client/common/Button.test.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { render, screen, fireEvent } from '../test-utils'; +import Button from './Button'; + +const MockIcon = (props: React.SVGProps) => ( + +); + +describe('Button', () => { + // Tag + it('renders as an anchor when href is provided', () => { + render(); + const anchor = screen.getByRole('link'); + expect(anchor.tagName.toLowerCase()).toBe('a'); + expect(anchor).toHaveAttribute('href', 'https://example.com'); + }); + + it('renders as a React Router when `to` is provided', () => { + render(); + const link = screen.getByRole('link'); + expect(link.tagName.toLowerCase()).toBe('a'); // Link renders as + expect(link).toHaveAttribute('href', '/dashboard'); + }); + + it('renders as a ); + const el = screen.getByRole('button'); + expect(el.tagName.toLowerCase()).toBe('button'); + expect(el).toHaveAttribute('type', 'button'); + }); + + // Children & Icons + it('renders children', () => { + render(); + expect(screen.getByText('Click Me')).toBeInTheDocument(); + }); + + it('renders an iconBefore and button text', () => { + render( + + ); + expect(screen.getByLabelText('iconbefore')).toBeInTheDocument(); + expect(screen.getByRole('button')).toHaveTextContent( + 'This has a before icon' + ); + }); + + it('renders with iconAfter', () => { + render( + + ); + expect(screen.getByLabelText('iconafter')).toBeInTheDocument(); + expect(screen.getByRole('button')).toHaveTextContent( + 'This has an after icon' + ); + }); + + it('renders only the icon if iconOnly', () => { + render( + + ); + expect(screen.getByLabelText('iconafter')).toBeInTheDocument(); + expect(screen.getByRole('button')).not.toHaveTextContent( + 'This has an after icon' + ); + }); + + // HTML attributes + it('calls onClick handler when clicked', () => { + const handleClick = jest.fn(); + render(); + fireEvent.click(screen.getByText('Click')); + expect(handleClick).toHaveBeenCalledTimes(1); + }); + + it('renders disabled state', () => { + render(); + expect(screen.getByRole('button')).toBeDisabled(); + }); + + it('uses aria-label when provided', () => { + render(