diff --git a/TODO.md b/TODO.md index 4913bde..e8c73b8 100644 --- a/TODO.md +++ b/TODO.md @@ -66,3 +66,4 @@ # Others - [X] Add `select-none` to input labels, etc. +- [ ] Test all components! diff --git a/categories/action/react/.eslintrc b/categories/action/react/.eslintrc index 8a6172f..8cd8ffc 100644 --- a/categories/action/react/.eslintrc +++ b/categories/action/react/.eslintrc @@ -3,7 +3,10 @@ "rules": { "quote-props": "off", "react/jsx-props-no-spreading": "off", - "react/button-has-type": "off" + "react/button-has-type": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "import/no-extraneous-dependencies": "off" }, "extends": [ "lxsmnsyc/typescript/react" diff --git a/categories/action/react/package.json b/categories/action/react/package.json index ed95c06..98bde3e 100644 --- a/categories/action/react/package.json +++ b/categories/action/react/package.json @@ -18,6 +18,7 @@ "@testing-library/user-event": "^14.4.3", "@types/node": "^18.14.1", "@types/react": "^18.0.27", + "@types/testing-library__jest-dom": "^5.14.7", "eslint": "^8.35.0", "eslint-config-lxsmnsyc": "^0.5.0", "jsdom": "^21.1.0", diff --git a/categories/action/react/setupTests.ts b/categories/action/react/setupTests.ts deleted file mode 100644 index b210af5..0000000 --- a/categories/action/react/setupTests.ts +++ /dev/null @@ -1,4 +0,0 @@ -import matchers from '@testing-library/jest-dom/matchers'; -import { expect } from 'vitest'; - -expect.extend(matchers); diff --git a/categories/action/react/src/components/ActionButton/ActionButton.test.tsx b/categories/action/react/src/components/ActionButton/ActionButton.test.tsx index 7498c15..eb81840 100644 --- a/categories/action/react/src/components/ActionButton/ActionButton.test.tsx +++ b/categories/action/react/src/components/ActionButton/ActionButton.test.tsx @@ -12,13 +12,12 @@ import { it, expect, afterEach, } from 'vitest'; +import matchers from '@testing-library/jest-dom/matchers'; import { ActionButton, } from '.'; -import matchers from '@testing-library/jest-dom/matchers'; -expect.extend(matchers); -vi.mock('@tesseract-design/web-base-button'); +expect.extend(matchers); describe('ActionButton', () => { afterEach(() => { @@ -65,7 +64,7 @@ describe('ActionButton', () => { }); it('handles click events', async () => { - const onClick = vi.fn().mockImplementationOnce((e) => { e.preventDefault() }); + const onClick = vi.fn().mockImplementationOnce((e) => { e.preventDefault(); }); render( { ${'small'} | ${'h-10'} ${'medium'} | ${'h-12'} ${'large'} | ${'h-16'} - `('on %s size', ({ + `('on $size size', ({ size, className, }: { size: Button.Size, className: string }) => { @@ -127,7 +126,7 @@ describe('ActionButton', () => { ${'bare'} | ${'bg-negative'} ${'outline'} | ${'border-2'} ${'filled'} | ${'bg-primary'} - `('renders a button with variant %s', ({ + `('renders a button with $variant variant', ({ variant, className, }: { variant: Button.Variant, className: string }) => { @@ -157,7 +156,7 @@ describe('ActionButton', () => { render( Foo - + , ); const children: HTMLElement = screen.getByTestId('children'); @@ -168,7 +167,7 @@ describe('ActionButton', () => { render( + />, ); const button: HTMLButtonElement = screen.getByRole('button'); expect(button).toHaveProperty('type', buttonType); @@ -178,7 +177,7 @@ describe('ActionButton', () => { render( + />, ); const button: HTMLButtonElement = screen.getByTestId('button'); expect(button).toBeDisabled(); diff --git a/categories/choice/react/package.json b/categories/choice/react/package.json index 8763864..e3b842d 100644 --- a/categories/choice/react/package.json +++ b/categories/choice/react/package.json @@ -15,8 +15,10 @@ "devDependencies": { "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^14.4.3", "@types/node": "^18.14.1", "@types/react": "^18.0.27", + "@types/testing-library__jest-dom": "^5.14.7", "eslint": "^8.35.0", "eslint-config-lxsmnsyc": "^0.5.0", "jsdom": "^21.1.0", @@ -83,4 +85,4 @@ "typesVersions": { "*": {} } -} \ No newline at end of file +} diff --git a/categories/choice/react/pridepack.json b/categories/choice/react/pridepack.json index f07dd67..0bc7a8f 100644 --- a/categories/choice/react/pridepack.json +++ b/categories/choice/react/pridepack.json @@ -1,9 +1,3 @@ { - "target": "es2018", - "entryPoints": { - ".": "src/index.ts", - "./dist/DropdownSelect.css": "src/components/DropdownSelect/DropdownSelect.css", - "./dist/RadioButton.css": "src/components/RadioButton/RadioButton.css", - "./dist/RadioTickBox.css": "src/components/RadioTickBox/RadioTickBox.css" - } + "target": "es2018" } diff --git a/categories/choice/react/src/components/ComboBox/index.tsx b/categories/choice/react/src/components/ComboBox/index.tsx index 5460f70..b23a6c5 100644 --- a/categories/choice/react/src/components/ComboBox/index.tsx +++ b/categories/choice/react/src/components/ComboBox/index.tsx @@ -10,7 +10,7 @@ export type ComboBoxDerivedElement = HTMLInputElement; /** * Props of the {@link ComboBox} component. */ -export interface ComboBoxProps extends Omit, 'size' | 'type' | 'style' | 'label' | 'list' | 'inputMode'> { +export interface ComboBoxProps extends Omit, 'size' | 'type' | 'label' | 'list' | 'inputMode'> { /** * Short textual description indicating the nature of the component's value. */ @@ -73,6 +73,7 @@ export const ComboBox = React.forwardRef( children, inputMode = 'text' as const, id: idProp, + style, ...etcProps }: ComboBoxProps, forwardedRef, @@ -106,6 +107,7 @@ export const ComboBox = React.forwardRef( }, className, )} + style={style} > { + afterEach(() => { + cleanup(); + }); + + it('renders a combobox', () => { + render(); + const combobox = screen.getByRole('combobox'); + expect(combobox).toBeInTheDocument(); + }); + + it('renders a border', () => { + render( + , + ); + const border = screen.getByTestId('border'); + expect(border).toBeInTheDocument(); + }); + + it('renders a label', () => { + render( + , + ); + const combobox = screen.getByLabelText('foo'); + expect(combobox).toBeInTheDocument(); + const label = screen.getByTestId('label'); + expect(label).toHaveTextContent('foo'); + }); + + it('renders a hidden label', () => { + render( + , + ); + const combobox = screen.getByLabelText('foo'); + expect(combobox).toBeInTheDocument(); + const label = screen.getByTestId('label'); + expect(label).toHaveClass('sr-only'); + }); + + it('renders a hint', () => { + render( + , + ); + const hint = screen.getByTestId('hint'); + expect(hint).toBeInTheDocument(); + }); + + it('render options with implicit values', () => { + render( + + + + , + ); + const combobox = screen.getByRole('combobox'); + expect(combobox.children).toHaveLength(2); + }); + + it('renders valid options', () => { + render( + + + + , + ); + const combobox = screen.getByRole('combobox'); + expect(combobox.children).toHaveLength(2); + }); + + it('renders option groups', () => { + render( + + + + + + + + + , + ); + const combobox = screen.getByRole('combobox'); + expect(combobox.children).toHaveLength(2); + expect(combobox.children[0].children).toHaveLength(1); + expect(combobox.children[1].children).toHaveLength(2); + }); + + describe.each` + size | inputClassNames | hintClassNames | indicatorClassNames + ${'small'} | ${['h-10']} | ${['pr-10']} | ${['w-10']} + ${'medium'} | ${['h-12']} | ${['pr-12']} | ${['w-12']} + ${'large'} | ${['h-16']} | ${['pr-16']} | ${['w-16']} + `('on $size size', ({ + size, + inputClassNames, + hintClassNames, + indicatorClassNames, + }: { + size: TextControl.Size, + inputClassNames: string[], + hintClassNames: string[], + indicatorClassNames: string[], + }) => { + it('renders input styles', () => { + render( + , + ); + + const combobox = screen.getByRole('combobox'); + expect(combobox).toHaveClass(...inputClassNames); + }); + + it('renders hint styles', () => { + render( + , + ); + + const hint = screen.getByTestId('hint'); + expect(hint).toHaveClass(...hintClassNames); + }); + + it('renders indicator styles', () => { + render( + , + ); + + const indicator = screen.getByTestId('indicator'); + expect(indicator).toHaveClass(...indicatorClassNames); + }); + }); + + it('renders a block input', () => { + render( + , + ); + + const base = screen.getByTestId('base'); + expect(base).toHaveClass('block'); + }); + + describe.each` + variant | inputClassNames | hintClassNames + ${'default'} | ${['pl-4']} | ${['bottom-0', 'pl-4', 'pb-1']} + ${'alternate'} | ${['pl-1.5', 'pt-4']} | ${['top-0.5']} + `('on $variant variant', ({ + variant, + inputClassNames, + hintClassNames, + }: { + variant: TextControl.Variant, + inputClassNames: string[], + hintClassNames: string[], + }) => { + it('renders input styles', () => { + render( + , + ); + + const combobox = screen.getByRole('combobox'); + expect(combobox).toHaveClass(...inputClassNames); + }); + + it('renders hint styles', () => { + render( + , + ); + + const hint = screen.getByTestId('hint'); + expect(hint).toHaveClass(...hintClassNames); + }); + }); + + it('handles change events', async () => { + const onChange = vi.fn().mockImplementationOnce((e) => { e.preventDefault(); }) + render( + + + + , + ); + const combobox: HTMLSelectElement = screen.getByRole('combobox'); + const [, secondOption] = screen.getAllByRole('option'); + await userEvent.selectOptions(combobox, secondOption); + expect(onChange).toBeCalled(); + }); +}); diff --git a/categories/choice/react/src/components/DropdownSelect/index.tsx b/categories/choice/react/src/components/DropdownSelect/index.tsx index 7c83fc9..9c10e41 100644 --- a/categories/choice/react/src/components/DropdownSelect/index.tsx +++ b/categories/choice/react/src/components/DropdownSelect/index.tsx @@ -10,7 +10,7 @@ export type DropdownSelectDerivedElement = HTMLSelectElement; /** * Props of the {@link DropdownSelect} component. */ -export interface DropdownSelectProps extends Omit, 'size' | 'type' | 'style' | 'label' | 'list' | 'multiple'> { +export interface DropdownSelectProps extends Omit, 'size' | 'type' | 'label' | 'list' | 'multiple'> { /** * Short textual description indicating the nature of the component's value. */ @@ -61,6 +61,7 @@ export const DropdownSelect = React.forwardRef