@@ -1,5 +1,11 @@ | |||
export type Type = 'submit' | 'reset' | 'button'; | |||
export const AVAILABLE_TYPES = ['submit', 'reset', 'button'] as const; | |||
export type Variant = 'bare' | 'filled' | 'outline'; | |||
export type Type = typeof AVAILABLE_TYPES[number]; | |||
export type Size = 'small' | 'medium' | 'large'; | |||
export const AVAILABLE_VARIANTS = ['bare', 'filled', 'outline'] as const; | |||
export type Variant = typeof AVAILABLE_VARIANTS[number]; | |||
export const AVAILABLE_SIZES = ['small', 'medium', 'large'] as const; | |||
export type Size = typeof AVAILABLE_SIZES[number]; |
@@ -1,7 +1,15 @@ | |||
export type Size = 'small' | 'medium' | 'large'; | |||
export const AVAILABLE_SIZES = ['small', 'medium', 'large'] as const; | |||
export type Variant = 'default' | 'alternate'; | |||
export type Size = typeof AVAILABLE_SIZES[number]; | |||
export type InputType = 'text' | 'search'; | |||
export const AVAILABLE_VARIANTS = ['default', 'alternate'] as const; | |||
export type InputMode = 'none' | 'numeric' | 'decimal' | InputType; | |||
export type Variant = typeof AVAILABLE_VARIANTS[number]; | |||
export const AVAILABLE_INPUT_TYPES = ['text', 'search'] as const; | |||
export type InputType = typeof AVAILABLE_INPUT_TYPES[number]; | |||
export const AVAILABLE_INPUT_MODES = ['none', 'numeric', 'decimal', ...AVAILABLE_INPUT_TYPES] as const; | |||
export type InputMode = typeof AVAILABLE_INPUT_MODES[number]; |
@@ -16,6 +16,7 @@ import { | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { | |||
ActionButton, | |||
ActionButtonDerivedElement, | |||
} from '.'; | |||
expect.extend(matchers); | |||
@@ -29,7 +30,7 @@ describe('ActionButton', () => { | |||
render( | |||
<ActionButton />, | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('button'); | |||
const button = screen.getByRole('button'); | |||
expect(button).toBeInTheDocument(); | |||
expect(button).toHaveProperty('type', 'button'); | |||
}); | |||
@@ -40,7 +41,7 @@ describe('ActionButton', () => { | |||
subtext="subtext" | |||
/>, | |||
); | |||
const subtext: HTMLElement = screen.getByTestId('subtext'); | |||
const subtext = screen.getByTestId('subtext'); | |||
expect(subtext).toBeInTheDocument(); | |||
}); | |||
@@ -50,7 +51,7 @@ describe('ActionButton', () => { | |||
badge="badge" | |||
/>, | |||
); | |||
const badge: HTMLElement = screen.getByTestId('badge'); | |||
const badge = screen.getByTestId('badge'); | |||
expect(badge).toBeInTheDocument(); | |||
}); | |||
@@ -60,21 +61,23 @@ describe('ActionButton', () => { | |||
menuItem | |||
/>, | |||
); | |||
const menuItemIndicator: HTMLElement = screen.getByTestId('menuItemIndicator'); | |||
const menuItemIndicator = screen.getByTestId('menuItemIndicator'); | |||
expect(menuItemIndicator).toBeInTheDocument(); | |||
}); | |||
it('handles click events', async () => { | |||
const onClick = vi.fn().mockImplementationOnce((e: React.MouseEvent) => { | |||
e.preventDefault(); | |||
}); | |||
const onClick = vi.fn().mockImplementationOnce( | |||
(e: React.MouseEvent<ActionButtonDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<ActionButton | |||
onClick={onClick} | |||
/>, | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('button'); | |||
const button = screen.getByRole('button'); | |||
await userEvent.click(button); | |||
expect(onClick).toBeCalled(); | |||
}); | |||
@@ -86,7 +89,7 @@ describe('ActionButton', () => { | |||
/>, | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('button'); | |||
const button = screen.getByRole('button'); | |||
expect(button).toHaveClass('pl-2 gap-2 pr-2'); | |||
}); | |||
@@ -106,7 +109,7 @@ describe('ActionButton', () => { | |||
/>, | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('button'); | |||
const button = screen.getByRole('button'); | |||
expect(button).toHaveClass(className); | |||
}); | |||
@@ -138,7 +141,7 @@ describe('ActionButton', () => { | |||
/>, | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('button'); | |||
const button = screen.getByRole('button'); | |||
expect(button).toHaveClass(className); | |||
}); | |||
@@ -149,7 +152,7 @@ describe('ActionButton', () => { | |||
/>, | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('button'); | |||
const button = screen.getByRole('button'); | |||
expect(button).toHaveClass('w-full flex'); | |||
}); | |||
@@ -160,17 +163,17 @@ describe('ActionButton', () => { | |||
</ActionButton>, | |||
); | |||
const children: HTMLElement = screen.getByTestId('children'); | |||
const children = screen.getByTestId('children'); | |||
expect(children).toHaveTextContent('Foo'); | |||
}); | |||
it.each(['button', 'submit'] as const)('renders a button with type %s', (buttonType) => { | |||
it.each(Button.AVAILABLE_TYPES)('renders a button with type %s', (buttonType) => { | |||
render( | |||
<ActionButton | |||
type={buttonType} | |||
/>, | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('button'); | |||
const button = screen.getByRole('button'); | |||
expect(button).toHaveProperty('type', buttonType); | |||
}); | |||
@@ -180,7 +183,7 @@ describe('ActionButton', () => { | |||
disabled | |||
/>, | |||
); | |||
const button: HTMLButtonElement = screen.getByTestId('button'); | |||
const button = screen.getByTestId('button'); | |||
expect(button).toBeDisabled(); | |||
}); | |||
}); |
@@ -86,4 +86,4 @@ | |||
"typesVersions": { | |||
"*": {} | |||
} | |||
} | |||
} |
@@ -15,7 +15,7 @@ import { | |||
} from 'vitest'; | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { | |||
DropdownSelect, | |||
DropdownSelect, DropdownSelectDerivedElement, | |||
} from '.'; | |||
expect.extend(matchers); | |||
@@ -231,9 +231,11 @@ describe('DropdownSelect', () => { | |||
}); | |||
it('handles change events', async () => { | |||
const onChange = vi.fn().mockImplementationOnce((e: React.ChangeEvent) => { | |||
e.preventDefault(); | |||
}); | |||
const onChange = vi.fn().mockImplementationOnce( | |||
(e: React.ChangeEvent<DropdownSelectDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<DropdownSelect | |||
@@ -10,10 +10,14 @@ import { | |||
vi, | |||
expect, | |||
describe, | |||
it, afterEach, | |||
it, | |||
afterEach, | |||
} from 'vitest'; | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { RadioButton } from '.'; | |||
import { | |||
RadioButton, | |||
RadioButtonDerivedElement, | |||
} from '.'; | |||
expect.extend(matchers); | |||
@@ -36,7 +40,7 @@ describe('RadioButton', () => { | |||
subtext="subtext" | |||
/>, | |||
); | |||
const subtext: HTMLElement = screen.getByTestId('subtext'); | |||
const subtext = screen.getByTestId('subtext'); | |||
expect(subtext).toBeInTheDocument(); | |||
}); | |||
@@ -46,24 +50,10 @@ describe('RadioButton', () => { | |||
badge="badge" | |||
/>, | |||
); | |||
const badge: HTMLElement = screen.getByTestId('badge'); | |||
const badge = screen.getByTestId('badge'); | |||
expect(badge).toBeInTheDocument(); | |||
}); | |||
it('handles click events', async () => { | |||
const onClick = vi.fn().mockImplementationOnce((e: React.MouseEvent) => { | |||
e.preventDefault(); | |||
}); | |||
render( | |||
<RadioButton | |||
onClick={onClick} | |||
/>, | |||
); | |||
const button: HTMLInputElement = screen.getByRole('radio'); | |||
await userEvent.click(button); | |||
expect(onClick).toBeCalled(); | |||
}); | |||
it('renders a compact button', () => { | |||
render( | |||
<RadioButton | |||
@@ -91,7 +81,7 @@ describe('RadioButton', () => { | |||
/>, | |||
); | |||
const button: HTMLButtonElement = screen.getByTestId('button'); | |||
const button = screen.getByTestId('button'); | |||
expect(button).toHaveClass(className); | |||
}); | |||
@@ -123,7 +113,7 @@ describe('RadioButton', () => { | |||
/>, | |||
); | |||
const button: HTMLButtonElement = screen.getByTestId('button'); | |||
const button = screen.getByTestId('button'); | |||
expect(button).toHaveClass(className); | |||
}); | |||
@@ -134,7 +124,7 @@ describe('RadioButton', () => { | |||
/>, | |||
); | |||
const button: HTMLButtonElement = screen.getByTestId('button'); | |||
const button = screen.getByTestId('button'); | |||
expect(button).toHaveClass('w-full flex'); | |||
}); | |||
@@ -145,7 +135,7 @@ describe('RadioButton', () => { | |||
</RadioButton>, | |||
); | |||
const children: HTMLElement = screen.getByTestId('children'); | |||
const children = screen.getByTestId('children'); | |||
expect(children).toHaveTextContent('Foo'); | |||
}); | |||
@@ -155,14 +145,32 @@ describe('RadioButton', () => { | |||
disabled | |||
/>, | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('radio'); | |||
const button = screen.getByRole('radio'); | |||
expect(button).toBeDisabled(); | |||
}); | |||
it('handles click events', async () => { | |||
const onClick = vi.fn().mockImplementationOnce( | |||
(e: React.MouseEvent<RadioButtonDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<RadioButton | |||
onClick={onClick} | |||
/>, | |||
); | |||
const button = screen.getByRole('radio'); | |||
await userEvent.click(button); | |||
expect(onClick).toBeCalled(); | |||
}); | |||
it('handles change events', async () => { | |||
const onChange = vi.fn().mockImplementationOnce((e: React.ChangeEvent) => { | |||
e.preventDefault(); | |||
}); | |||
const onChange = vi.fn().mockImplementationOnce( | |||
(e: React.ChangeEvent<RadioButtonDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<RadioButton | |||
@@ -78,7 +78,6 @@ export const RadioButton = React.forwardRef<RadioButtonDerivedElement, RadioButt | |||
'peer-focus:outline-0 peer-focus:ring-4 peer-focus:ring-secondary/50', | |||
'active:ring-tertiary/50 active:ring-4', | |||
'peer-disabled:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:ring-0', | |||
'text-primary peer-disabled:text-primary peer-focus:text-secondary peer-checked:text-tertiary active:text-tertiary', | |||
{ | |||
'flex w-full': block, | |||
'inline-flex max-w-full align-middle': !block, | |||
@@ -89,7 +88,7 @@ export const RadioButton = React.forwardRef<RadioButtonDerivedElement, RadioButt | |||
}, | |||
{ | |||
'border-2 border-primary peer-disabled:border-primary peer-focus:border-secondary peer-checked:border-tertiary active:border-tertiary': variant !== 'bare', | |||
'bg-negative': variant === 'bare', | |||
'bg-negative text-primary peer-disabled:text-primary peer-focus:text-secondary peer-checked:text-tertiary active:text-tertiary': variant !== 'filled', | |||
'bg-primary text-negative peer-disabled:bg-primary peer-focus:bg-secondary peer-checked:bg-tertiary active:bg-tertiary': variant === 'filled', | |||
}, | |||
{ | |||
@@ -104,7 +103,6 @@ export const RadioButton = React.forwardRef<RadioButtonDerivedElement, RadioButt | |||
className={clsx( | |||
'w-6 h-6 block rounded-full border-2 p-0.5 box-border', | |||
{ | |||
'-mr-2': compact, | |||
'border-current': variant !== 'filled', | |||
'border-negative': variant === 'filled', | |||
}, | |||
@@ -0,0 +1,90 @@ | |||
import * as React from 'react'; | |||
import { | |||
cleanup, | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import userEvent from '@testing-library/user-event'; | |||
import { | |||
vi, | |||
expect, | |||
describe, | |||
it, | |||
afterEach, | |||
} from 'vitest'; | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { | |||
RadioTickBox, | |||
RadioTickBoxDerivedElement, | |||
} from '.'; | |||
expect.extend(matchers); | |||
describe('RadioTickBox', () => { | |||
afterEach(() => { | |||
cleanup(); | |||
}); | |||
it('renders a radio button', () => { | |||
render( | |||
<RadioTickBox />, | |||
); | |||
const checkbox = screen.getByRole('radio'); | |||
expect(checkbox).toBeInTheDocument(); | |||
}); | |||
it('renders a block tick box', () => { | |||
render( | |||
<RadioTickBox | |||
block | |||
/>, | |||
); | |||
const base = screen.getByTestId('base'); | |||
expect(base).toHaveClass('flex'); | |||
}); | |||
it('renders a subtext', () => { | |||
render( | |||
<RadioTickBox | |||
subtext="subtext" | |||
/>, | |||
); | |||
const subtext = screen.getByTestId('subtext'); | |||
expect(subtext).toBeInTheDocument(); | |||
}); | |||
it('handles click events', async () => { | |||
const onClick = vi.fn().mockImplementationOnce( | |||
(e: React.MouseEvent<RadioTickBoxDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<RadioTickBox | |||
onClick={onClick} | |||
/>, | |||
); | |||
const radio = screen.getByRole('radio'); | |||
await userEvent.click(radio); | |||
expect(onClick).toBeCalled(); | |||
}); | |||
it('handles change events', async () => { | |||
const onChange = vi.fn().mockImplementationOnce( | |||
(e: React.ChangeEvent<RadioTickBoxDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<RadioTickBox | |||
onChange={onChange} | |||
/>, | |||
); | |||
const radio = screen.getByRole('radio'); | |||
await userEvent.click(radio); | |||
expect(onChange).toBeCalled(); | |||
}); | |||
}); |
@@ -48,6 +48,7 @@ export const RadioTickBox = React.forwardRef<RadioTickBoxDerivedElement, RadioTi | |||
className, | |||
)} | |||
style={style} | |||
data-testid="base" | |||
> | |||
<input | |||
{...etcProps} | |||
@@ -2,7 +2,8 @@ | |||
"root": true, | |||
"rules": { | |||
"quote-props": "off", | |||
"react/jsx-props-no-spreading": "off" | |||
"react/jsx-props-no-spreading": "off", | |||
"import/no-extraneous-dependencies": "off" | |||
}, | |||
"extends": [ | |||
"lxsmnsyc/typescript/react" | |||
@@ -15,8 +15,11 @@ | |||
"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", | |||
"@vitest/coverage-v8": "^0.33.0", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
@@ -26,7 +29,7 @@ | |||
"react-test-renderer": "^18.2.0", | |||
"tslib": "^2.5.0", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
"vitest": "^0.33.0" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
@@ -0,0 +1,242 @@ | |||
import * as React from 'react'; | |||
import { | |||
cleanup, | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import userEvent from '@testing-library/user-event'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import { | |||
vi, | |||
expect, | |||
describe, | |||
it, | |||
afterEach, | |||
} from 'vitest'; | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { | |||
MaskedTextInput, | |||
MaskedTextInputDerivedElement, | |||
} from '.'; | |||
expect.extend(matchers); | |||
describe('MaskedTextInput', () => { | |||
afterEach(() => { | |||
cleanup(); | |||
}); | |||
it('renders a password input', () => { | |||
render( | |||
<MaskedTextInput />, | |||
); | |||
const textbox = screen.getByTestId('input'); | |||
expect(textbox).toBeInTheDocument(); | |||
expect(textbox).toHaveProperty('type', 'password'); | |||
}); | |||
it('renders a border', () => { | |||
render( | |||
<MaskedTextInput | |||
border | |||
/>, | |||
); | |||
const border = screen.getByTestId('border'); | |||
expect(border).toBeInTheDocument(); | |||
}); | |||
it('renders a label', () => { | |||
render( | |||
<MaskedTextInput | |||
label="foo" | |||
/>, | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.getByTestId('label'); | |||
expect(label).toHaveTextContent('foo'); | |||
}); | |||
it('renders a hidden label', () => { | |||
render( | |||
<MaskedTextInput | |||
label="foo" | |||
hiddenLabel | |||
/>, | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.queryByTestId('label'); | |||
expect(label).toBeInTheDocument(); | |||
expect(label).toHaveClass('sr-only'); | |||
}); | |||
it('renders a hint', () => { | |||
render( | |||
<MaskedTextInput | |||
hint="foo" | |||
/>, | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toBeInTheDocument(); | |||
}); | |||
it('renders an indicator', () => { | |||
render( | |||
<MaskedTextInput | |||
indicator={ | |||
<div /> | |||
} | |||
/>, | |||
); | |||
const indicator = screen.getByTestId('indicator'); | |||
expect(indicator).toBeInTheDocument(); | |||
}); | |||
describe.each` | |||
size | inputClassName | hintClassName | indicatorClassName | |||
${'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, | |||
inputClassName, | |||
hintClassName, | |||
indicatorClassName, | |||
}: { | |||
size: TextControl.Size, | |||
inputClassName: string, | |||
hintClassName: string, | |||
indicatorClassName: string, | |||
}) => { | |||
it('renders input styles', () => { | |||
render( | |||
<MaskedTextInput | |||
size={size} | |||
/>, | |||
); | |||
const input = screen.getByTestId('input'); | |||
expect(input).toHaveClass(inputClassName); | |||
}); | |||
it('renders label styles with indicator', () => { | |||
render( | |||
<MaskedTextInput | |||
size={size} | |||
label="foo" | |||
indicator={<div />} | |||
/>, | |||
); | |||
const label = screen.getByTestId('label'); | |||
expect(label).toHaveClass(hintClassName); | |||
}); | |||
it('renders hint styles when indicator is present', () => { | |||
render( | |||
<MaskedTextInput | |||
size={size} | |||
hint="hint" | |||
indicator={<div />} | |||
/>, | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toHaveClass(hintClassName); | |||
}); | |||
it('renders indicator styles', () => { | |||
render( | |||
<MaskedTextInput | |||
size={size} | |||
indicator={ | |||
<div /> | |||
} | |||
/>, | |||
); | |||
const indicator = screen.getByTestId('indicator'); | |||
expect(indicator).toHaveClass(indicatorClassName); | |||
}); | |||
}); | |||
it('renders a block textbox', () => { | |||
render( | |||
<MaskedTextInput | |||
block | |||
/>, | |||
); | |||
const base = screen.getByTestId('base'); | |||
expect(base).toHaveClass('block'); | |||
}); | |||
describe.each` | |||
variant | inputClassName | hintClassName | |||
${'default'} | ${'pl-4'} | ${'bottom-0 pl-4 pb-1'} | |||
${'alternate'} | ${'pl-1.5 pt-4'} | ${'top-0.5'} | |||
`('on $variant style', ({ | |||
variant, | |||
inputClassName, | |||
hintClassName, | |||
}: { | |||
variant: TextControl.Variant, | |||
inputClassName: string, | |||
hintClassName: string, | |||
}) => { | |||
it('renders input styles', () => { | |||
render( | |||
<MaskedTextInput | |||
variant={variant} | |||
/>, | |||
); | |||
const input = screen.getByTestId('input'); | |||
expect(input).toHaveClass(inputClassName); | |||
}); | |||
it('renders hint styles', () => { | |||
render( | |||
<MaskedTextInput | |||
variant={variant} | |||
hint="hint" | |||
/>, | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toHaveClass(hintClassName); | |||
}); | |||
}); | |||
it('handles change events', async () => { | |||
const onChange = vi.fn().mockImplementationOnce( | |||
(e: React.ChangeEvent<MaskedTextInputDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<MaskedTextInput | |||
onChange={onChange} | |||
/>, | |||
); | |||
const textbox = screen.getByTestId('input'); | |||
await userEvent.type(textbox, 'foobar'); | |||
expect(onChange).toBeCalled(); | |||
}); | |||
it('handles input events', async () => { | |||
const onInput = vi.fn().mockImplementationOnce( | |||
(e: React.SyntheticEvent<MaskedTextInputDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<MaskedTextInput | |||
onInput={onInput} | |||
/>, | |||
); | |||
const textbox = screen.getByTestId('input'); | |||
await userEvent.type(textbox, 'foobar'); | |||
expect(onInput).toBeCalled(); | |||
}); | |||
}); |
@@ -78,6 +78,7 @@ export const MaskedTextInput = React.forwardRef< | |||
className, | |||
)} | |||
style={style} | |||
data-testid="base" | |||
> | |||
<input | |||
{...etcProps} | |||
@@ -177,6 +178,7 @@ export const MaskedTextInput = React.forwardRef< | |||
)} | |||
{indicator && ( | |||
<div | |||
data-testid="indicator" | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none', | |||
{ | |||
@@ -0,0 +1,241 @@ | |||
import * as React from 'react'; | |||
import { | |||
cleanup, | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import userEvent from '@testing-library/user-event'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import { | |||
vi, | |||
expect, | |||
describe, | |||
it, | |||
afterEach, | |||
} from 'vitest'; | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { | |||
MultilineTextInput, | |||
MultilineTextInputDerivedElement, | |||
} from '.'; | |||
expect.extend(matchers); | |||
describe('TextInput', () => { | |||
afterEach(() => { | |||
cleanup(); | |||
}); | |||
it('renders a textbox', () => { | |||
render( | |||
<MultilineTextInput />, | |||
); | |||
const textbox = screen.getByRole('textbox'); | |||
expect(textbox).toBeInTheDocument(); | |||
}); | |||
it('renders a border', () => { | |||
render( | |||
<MultilineTextInput | |||
border | |||
/>, | |||
); | |||
const border = screen.getByTestId('border'); | |||
expect(border).toBeInTheDocument(); | |||
}); | |||
it('renders a label', () => { | |||
render( | |||
<MultilineTextInput | |||
label="foo" | |||
/>, | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.getByTestId('label'); | |||
expect(label).toHaveTextContent('foo'); | |||
}); | |||
it('renders a hidden label', () => { | |||
render( | |||
<MultilineTextInput | |||
label="foo" | |||
hiddenLabel | |||
/>, | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.queryByTestId('label'); | |||
expect(label).toBeInTheDocument(); | |||
expect(label).toHaveClass('sr-only'); | |||
}); | |||
it('renders a hint', () => { | |||
render( | |||
<MultilineTextInput | |||
hint="foo" | |||
/>, | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toBeInTheDocument(); | |||
}); | |||
it('renders an indicator', () => { | |||
render( | |||
<MultilineTextInput | |||
indicator={ | |||
<div /> | |||
} | |||
/>, | |||
); | |||
const indicator = screen.getByTestId('indicator'); | |||
expect(indicator).toBeInTheDocument(); | |||
}); | |||
describe.each` | |||
size | inputClassName | hintClassName | indicatorClassName | |||
${'small'} | ${'min-h-10'} | ${'pr-10'} | ${'w-10'} | |||
${'medium'} | ${'min-h-12'} | ${'pr-12'} | ${'w-12'} | |||
${'large'} | ${'min-h-16'} | ${'pr-16'} | ${'w-16'} | |||
`('on $size size', ({ | |||
size, | |||
inputClassName, | |||
hintClassName, | |||
indicatorClassName, | |||
}: { | |||
size: TextControl.Size, | |||
inputClassName: string, | |||
hintClassName: string, | |||
indicatorClassName: string, | |||
}) => { | |||
it('renders input styles', () => { | |||
render( | |||
<MultilineTextInput | |||
size={size} | |||
/>, | |||
); | |||
const input = screen.getByTestId('input'); | |||
expect(input).toHaveClass(inputClassName); | |||
}); | |||
it('renders label styles with indicator', () => { | |||
render( | |||
<MultilineTextInput | |||
size={size} | |||
label="foo" | |||
indicator={<div />} | |||
/>, | |||
); | |||
const label = screen.getByTestId('label'); | |||
expect(label).toHaveClass(hintClassName); | |||
}); | |||
it('renders hint styles when indicator is present', () => { | |||
render( | |||
<MultilineTextInput | |||
size={size} | |||
hint="hint" | |||
indicator={<div />} | |||
/>, | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toHaveClass(hintClassName); | |||
}); | |||
it('renders indicator styles', () => { | |||
render( | |||
<MultilineTextInput | |||
size={size} | |||
indicator={ | |||
<div /> | |||
} | |||
/>, | |||
); | |||
const indicator = screen.getByTestId('indicator'); | |||
expect(indicator).toHaveClass(indicatorClassName); | |||
}); | |||
}); | |||
it('renders a block textbox', () => { | |||
render( | |||
<MultilineTextInput | |||
block | |||
/>, | |||
); | |||
const base = screen.getByTestId('base'); | |||
expect(base).toHaveClass('block'); | |||
}); | |||
describe.each` | |||
variant | inputClassName | hintClassName | |||
${'default'} | ${'pl-4'} | ${'bottom-0 pl-4 pb-1'} | |||
${'alternate'} | ${'pl-1.5'} | ${'top-0.5'} | |||
`('on $variant style', ({ | |||
variant, | |||
inputClassName, | |||
hintClassName, | |||
}: { | |||
variant: TextControl.Variant, | |||
inputClassName: string, | |||
hintClassName: string, | |||
}) => { | |||
it('renders input styles', () => { | |||
render( | |||
<MultilineTextInput | |||
variant={variant} | |||
/>, | |||
); | |||
const input = screen.getByTestId('input'); | |||
expect(input).toHaveClass(inputClassName); | |||
}); | |||
it('renders hint styles', () => { | |||
render( | |||
<MultilineTextInput | |||
variant={variant} | |||
hint="hint" | |||
/>, | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toHaveClass(hintClassName); | |||
}); | |||
}); | |||
it('handles change events', async () => { | |||
const onChange = vi.fn().mockImplementationOnce( | |||
(e: React.ChangeEvent<MultilineTextInputDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<MultilineTextInput | |||
onChange={onChange} | |||
/>, | |||
); | |||
const textbox: HTMLInputElement = screen.getByRole('textbox'); | |||
await userEvent.type(textbox, 'foobar'); | |||
expect(onChange).toBeCalled(); | |||
}); | |||
it('handles input events', async () => { | |||
const onInput = vi.fn().mockImplementationOnce( | |||
(e: React.SyntheticEvent<MultilineTextInputDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<MultilineTextInput | |||
onInput={onInput} | |||
/>, | |||
); | |||
const textbox: HTMLInputElement = screen.getByTestId('input'); | |||
await userEvent.type(textbox, 'foobar'); | |||
expect(onInput).toBeCalled(); | |||
}); | |||
}); |
@@ -80,6 +80,7 @@ export const MultilineTextInput = React.forwardRef< | |||
className, | |||
)} | |||
style={style} | |||
data-testid="base" | |||
> | |||
<textarea | |||
{...etcProps} | |||
@@ -192,6 +193,7 @@ export const MultilineTextInput = React.forwardRef< | |||
)} | |||
{indicator && ( | |||
<div | |||
data-testid="indicator" | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none', | |||
{ | |||
@@ -0,0 +1,263 @@ | |||
import * as React from 'react'; | |||
import { | |||
cleanup, | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import userEvent from '@testing-library/user-event'; | |||
import { TextControl } from '@tesseract-design/web-base'; | |||
import { | |||
vi, | |||
expect, | |||
describe, | |||
it, | |||
afterEach, | |||
} from 'vitest'; | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { | |||
TextInput, | |||
TextInputDerivedElement, | |||
} from '.'; | |||
expect.extend(matchers); | |||
describe('TextInput', () => { | |||
afterEach(() => { | |||
cleanup(); | |||
}); | |||
it('renders a textbox', () => { | |||
render( | |||
<TextInput />, | |||
); | |||
const textbox = screen.getByRole('textbox'); | |||
expect(textbox).toBeInTheDocument(); | |||
expect(textbox).toHaveProperty('type', 'text'); | |||
}); | |||
it('renders a border', () => { | |||
render( | |||
<TextInput | |||
border | |||
/>, | |||
); | |||
const border = screen.getByTestId('border'); | |||
expect(border).toBeInTheDocument(); | |||
}); | |||
it('renders a label', () => { | |||
render( | |||
<TextInput | |||
label="foo" | |||
/>, | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.getByTestId('label'); | |||
expect(label).toHaveTextContent('foo'); | |||
}); | |||
it('renders a hidden label', () => { | |||
render( | |||
<TextInput | |||
label="foo" | |||
hiddenLabel | |||
/>, | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.queryByTestId('label'); | |||
expect(label).toBeInTheDocument(); | |||
expect(label).toHaveClass('sr-only'); | |||
}); | |||
it('renders a hint', () => { | |||
render( | |||
<TextInput | |||
hint="foo" | |||
/>, | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toBeInTheDocument(); | |||
}); | |||
it('renders an indicator', () => { | |||
render( | |||
<TextInput | |||
indicator={ | |||
<div /> | |||
} | |||
/>, | |||
); | |||
const indicator = screen.getByTestId('indicator'); | |||
expect(indicator).toBeInTheDocument(); | |||
}); | |||
describe.each` | |||
size | inputClassName | hintClassName | indicatorClassName | |||
${'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, | |||
inputClassName, | |||
hintClassName, | |||
indicatorClassName, | |||
}: { | |||
size: TextControl.Size, | |||
inputClassName: string, | |||
hintClassName: string, | |||
indicatorClassName: string, | |||
}) => { | |||
it('renders input styles', () => { | |||
render( | |||
<TextInput | |||
size={size} | |||
/>, | |||
); | |||
const input = screen.getByTestId('input'); | |||
expect(input).toHaveClass(inputClassName); | |||
}); | |||
it('renders label styles with indicator', () => { | |||
render( | |||
<TextInput | |||
size={size} | |||
label="foo" | |||
indicator={<div />} | |||
/>, | |||
); | |||
const label = screen.getByTestId('label'); | |||
expect(label).toHaveClass(hintClassName); | |||
}); | |||
it('renders hint styles when indicator is present', () => { | |||
render( | |||
<TextInput | |||
size={size} | |||
hint="hint" | |||
indicator={<div />} | |||
/>, | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toHaveClass(hintClassName); | |||
}); | |||
it('renders indicator styles', () => { | |||
render( | |||
<TextInput | |||
size={size} | |||
indicator={ | |||
<div /> | |||
} | |||
/>, | |||
); | |||
const indicator = screen.getByTestId('indicator'); | |||
expect(indicator).toHaveClass(indicatorClassName); | |||
}); | |||
}); | |||
it('renders a block textbox', () => { | |||
render( | |||
<TextInput | |||
block | |||
/>, | |||
); | |||
const base = screen.getByTestId('base'); | |||
expect(base).toHaveClass('block'); | |||
}); | |||
it.each(TextControl.AVAILABLE_INPUT_TYPES)('renders a textbox with type %s', (inputType) => { | |||
render( | |||
<TextInput | |||
type={inputType} | |||
/>, | |||
); | |||
const textbox = screen.getByTestId('input'); | |||
expect(textbox).toHaveProperty('type', inputType); | |||
}); | |||
it('falls back to text input mode when it clashes with the input type', () => { | |||
render( | |||
<TextInput | |||
type="text" | |||
inputMode="search" | |||
/>, | |||
); | |||
const textbox = screen.getByTestId('input'); | |||
expect(textbox).toHaveProperty('inputMode', 'text'); | |||
}); | |||
describe.each` | |||
variant | inputClassName | hintClassName | |||
${'default'} | ${'pl-4'} | ${'bottom-0 pl-4 pb-1'} | |||
${'alternate'} | ${'pl-1.5 pt-4'} | ${'top-0.5'} | |||
`('on $variant style', ({ | |||
variant, | |||
inputClassName, | |||
hintClassName, | |||
}: { | |||
variant: TextControl.Variant, | |||
inputClassName: string, | |||
hintClassName: string, | |||
}) => { | |||
it('renders input styles', () => { | |||
render( | |||
<TextInput | |||
variant={variant} | |||
/>, | |||
); | |||
const input = screen.getByTestId('input'); | |||
expect(input).toHaveClass(inputClassName); | |||
}); | |||
it('renders hint styles', () => { | |||
render( | |||
<TextInput | |||
variant={variant} | |||
hint="hint" | |||
/>, | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toHaveClass(hintClassName); | |||
}); | |||
}); | |||
it('handles change events', async () => { | |||
const onChange = vi.fn().mockImplementationOnce( | |||
(e: React.ChangeEvent<TextInputDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<TextInput | |||
onChange={onChange} | |||
/>, | |||
); | |||
const textbox: HTMLInputElement = screen.getByRole('textbox'); | |||
await userEvent.type(textbox, 'foobar'); | |||
expect(onChange).toBeCalled(); | |||
}); | |||
it('handles input events', async () => { | |||
const onInput = vi.fn().mockImplementationOnce( | |||
(e: React.SyntheticEvent<TextInputDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<TextInput | |||
onInput={onInput} | |||
/>, | |||
); | |||
const textbox: HTMLInputElement = screen.getByTestId('input'); | |||
await userEvent.type(textbox, 'foobar'); | |||
expect(onInput).toBeCalled(); | |||
}); | |||
}); |
@@ -94,6 +94,7 @@ export const TextInput = React.forwardRef<TextInputDerivedElement, TextInputProp | |||
className, | |||
)} | |||
style={style} | |||
data-testid="base" | |||
> | |||
<input | |||
{...etcProps} | |||
@@ -194,6 +195,7 @@ export const TextInput = React.forwardRef<TextInputDerivedElement, TextInputProp | |||
)} | |||
{indicator && ( | |||
<div | |||
data-testid="indicator" | |||
className={clsx( | |||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none select-none', | |||
{ | |||
@@ -0,0 +1,12 @@ | |||
import { describe, it, expect } from 'vitest'; | |||
import * as WebFreeformReact from '.'; | |||
describe('web-freeform-react', () => { | |||
it.each([ | |||
'MaskedTextInput', | |||
'MultilineTextInput', | |||
'TextInput', | |||
])('exports %s', (namedExport) => { | |||
expect(WebFreeformReact).toHaveProperty(namedExport); | |||
}); | |||
}); |
@@ -15,8 +15,11 @@ | |||
"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", | |||
"@vitest/coverage-v8": "^0.33.0", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
@@ -26,7 +29,7 @@ | |||
"react-test-renderer": "^18.2.0", | |||
"tslib": "^2.5.0", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
"vitest": "^0.33.0" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
@@ -0,0 +1,44 @@ | |||
import * as React from 'react'; | |||
import { | |||
cleanup, | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import { | |||
expect, | |||
describe, | |||
it, | |||
afterEach, | |||
} from 'vitest'; | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { | |||
Badge, | |||
} from '.'; | |||
expect.extend(matchers); | |||
describe('Badge', () => { | |||
afterEach(() => { | |||
cleanup(); | |||
}); | |||
it('renders a badge', () => { | |||
render( | |||
<Badge />, | |||
); | |||
const badge: HTMLButtonElement = screen.getByTestId('badge'); | |||
expect(badge).toBeInTheDocument(); | |||
expect(badge).toHaveClass('rounded px-1'); | |||
}); | |||
it('renders a rounded badge', () => { | |||
render( | |||
<Badge | |||
rounded | |||
/>, | |||
); | |||
const badge: HTMLButtonElement = screen.getByTestId('badge'); | |||
expect(badge).toBeInTheDocument(); | |||
expect(badge).toHaveClass('rounded-full px-2'); | |||
}); | |||
}); |
@@ -28,6 +28,7 @@ export const Badge = React.forwardRef<BadgeDerivedElement, BadgeProps>(( | |||
}, | |||
className, | |||
)} | |||
data-testid="badge" | |||
> | |||
<span className="relative w-full"> | |||
{children} | |||
@@ -0,0 +1,11 @@ | |||
import { describe, it, expect } from 'vitest'; | |||
import * as WebInformationReact from '.'; | |||
describe('web-information-react', () => { | |||
it.each([ | |||
'Badge', | |||
'KeyValueTable', | |||
])('exports %s', (namedExport) => { | |||
expect(WebInformationReact).toHaveProperty(namedExport); | |||
}); | |||
}); |
@@ -2,7 +2,8 @@ | |||
"root": true, | |||
"rules": { | |||
"quote-props": "off", | |||
"react/jsx-props-no-spreading": "off" | |||
"react/jsx-props-no-spreading": "off", | |||
"import/no-extraneous-dependencies": "off" | |||
}, | |||
"extends": [ | |||
"lxsmnsyc/typescript/react" | |||
@@ -15,8 +15,11 @@ | |||
"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", | |||
"@vitest/coverage-v8": "^0.33.0", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
@@ -27,7 +30,7 @@ | |||
"tslib": "^2.5.0", | |||
"tsx": "^3.12.7", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
"vitest": "^0.33.0" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
@@ -0,0 +1,224 @@ | |||
import * as React from 'react'; | |||
import { | |||
cleanup, | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import userEvent from '@testing-library/user-event'; | |||
import { Button } from '@tesseract-design/web-base'; | |||
import { | |||
vi, | |||
expect, | |||
describe, | |||
it, | |||
afterEach, | |||
} from 'vitest'; | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { | |||
ToggleButton, | |||
ToggleButtonDerivedElement, | |||
} from '.'; | |||
expect.extend(matchers); | |||
describe('ToggleButton', () => { | |||
afterEach(() => { | |||
cleanup(); | |||
}); | |||
it('renders a checkbox', () => { | |||
render( | |||
<ToggleButton />, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toBeInTheDocument(); | |||
}); | |||
it('renders a subtext', () => { | |||
render( | |||
<ToggleButton | |||
subtext="subtext" | |||
/>, | |||
); | |||
const subtext = screen.getByTestId('subtext'); | |||
expect(subtext).toBeInTheDocument(); | |||
}); | |||
it('renders a badge', () => { | |||
render( | |||
<ToggleButton | |||
badge="badge" | |||
/>, | |||
); | |||
const badge = screen.getByTestId('badge'); | |||
expect(badge).toBeInTheDocument(); | |||
}); | |||
describe('on indeterminate', () => { | |||
it('renders an indeterminate checkbox', () => { | |||
render( | |||
<ToggleButton | |||
indeterminate | |||
/>, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toHaveProperty('indeterminate', true); | |||
}); | |||
it('acknowledges passed ref object', () => { | |||
const ref = React.createRef<ToggleButtonDerivedElement>(); | |||
render( | |||
<ToggleButton | |||
indeterminate | |||
ref={ref} | |||
/>, | |||
); | |||
expect(ref.current).toHaveProperty('indeterminate', true); | |||
}); | |||
it('acknowledges passed legacy ref', () => { | |||
let refElement = null as null | ToggleButtonDerivedElement; | |||
const ref = (element: ToggleButtonDerivedElement) => { | |||
refElement = element; | |||
}; | |||
render( | |||
<ToggleButton | |||
indeterminate | |||
ref={ref} | |||
/>, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toHaveProperty('indeterminate', true); | |||
expect(refElement).toBe(checkbox); | |||
}); | |||
}); | |||
it('handles click events', async () => { | |||
const onClick = vi.fn().mockImplementationOnce( | |||
(e: React.MouseEvent<ToggleButtonDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<ToggleButton | |||
onClick={onClick} | |||
/>, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
await userEvent.click(checkbox); | |||
expect(onClick).toBeCalled(); | |||
}); | |||
it('renders a compact button', () => { | |||
render( | |||
<ToggleButton | |||
compact | |||
/>, | |||
); | |||
const button = screen.getByTestId('button'); | |||
expect(button).toHaveClass('pl-2 gap-2 pr-2'); | |||
}); | |||
describe.each` | |||
size | className | |||
${'small'} | ${'h-10'} | |||
${'medium'} | ${'h-12'} | |||
${'large'} | ${'h-16'} | |||
`('on $size size', ({ | |||
size, | |||
className, | |||
}: { size: Button.Size, className: string }) => { | |||
it('renders button styles', () => { | |||
render( | |||
<ToggleButton | |||
size={size} | |||
/>, | |||
); | |||
const button = screen.getByTestId('button'); | |||
expect(button).toHaveClass(className); | |||
}); | |||
it('renders badge styles', () => { | |||
render( | |||
<ToggleButton | |||
size={size} | |||
badge="badge" | |||
/>, | |||
); | |||
const badge = screen.getByTestId('badge'); | |||
expect(badge).toBeInTheDocument(); | |||
}); | |||
}); | |||
it.each` | |||
variant | className | |||
${'bare'} | ${'bg-negative'} | |||
${'outline'} | ${'border-2'} | |||
${'filled'} | ${'bg-primary'} | |||
`('renders a button with $variant variant', ({ | |||
variant, | |||
className, | |||
}: { variant: Button.Variant, className: string }) => { | |||
render( | |||
<ToggleButton | |||
variant={variant} | |||
/>, | |||
); | |||
const button = screen.getByTestId('button'); | |||
expect(button).toHaveClass(className); | |||
}); | |||
it('renders a block button', () => { | |||
render( | |||
<ToggleButton | |||
block | |||
/>, | |||
); | |||
const button = screen.getByTestId('button'); | |||
expect(button).toHaveClass('w-full flex'); | |||
}); | |||
it('renders children', () => { | |||
render( | |||
<ToggleButton> | |||
Foo | |||
</ToggleButton>, | |||
); | |||
const children = screen.getByTestId('children'); | |||
expect(children).toHaveTextContent('Foo'); | |||
}); | |||
it('renders a disabled button', () => { | |||
render( | |||
<ToggleButton | |||
disabled | |||
/>, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toBeDisabled(); | |||
}); | |||
it('handles change events', async () => { | |||
const onChange = vi.fn().mockImplementationOnce( | |||
(e: React.ChangeEvent<ToggleButtonDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<ToggleButton | |||
onChange={onChange} | |||
/>, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
await userEvent.click(checkbox); | |||
expect(onChange).toBeCalled(); | |||
}); | |||
}); |
@@ -36,29 +36,30 @@ export const ToggleButton = React.forwardRef<ToggleButtonDerivedElement, ToggleB | |||
const id = idProp ?? defaultId; | |||
React.useEffect(() => { | |||
if (!(typeof ref === 'object' && ref)) { | |||
return; | |||
} | |||
const { current: element } = ref; | |||
if (!element) { | |||
if (typeof ref === 'function') { | |||
const defaultElement = defaultRef.current as ToggleButtonDerivedElement; | |||
defaultElement.indeterminate = indeterminate; | |||
ref(defaultElement); | |||
return; | |||
} | |||
const element = ref.current as ToggleButtonDerivedElement; | |||
element.indeterminate = indeterminate; | |||
}, [indeterminate, ref]); | |||
}, [indeterminate, defaultRef, ref]); | |||
return ( | |||
<> | |||
<input | |||
{...etcProps} | |||
ref={ref} | |||
ref={typeof ref === 'function' ? defaultRef : ref} | |||
type="checkbox" | |||
id={id} | |||
className="sr-only peer tesseract-design-toggle-button" | |||
/> | |||
<label | |||
data-testid="button" | |||
htmlFor={id} | |||
className={clsx( | |||
'items-center justify-start rounded overflow-hidden ring-secondary/50 gap-4 leading-none select-none cursor-pointer', | |||
'items-center justify-start rounded overflow-hidden ring-secondary/50 leading-none select-none cursor-pointer', | |||
'peer-focus:outline-0 peer-focus:ring-4 peer-focus:ring-secondary/50', | |||
'active:ring-tertiary/50 active:ring-4', | |||
'peer-disabled:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:ring-0', | |||
@@ -68,11 +69,12 @@ export const ToggleButton = React.forwardRef<ToggleButtonDerivedElement, ToggleB | |||
'inline-flex max-w-full align-middle': !block, | |||
}, | |||
{ | |||
'pl-2 pr-2': compact, | |||
'pl-4 pr-4': !compact, | |||
'pl-2 gap-2 pr-2': compact, | |||
'pl-4 gap-4 pr-4': !compact, | |||
}, | |||
{ | |||
'border-2 border-primary peer-disabled:border-primary peer-focus:border-secondary peer-checked:border-tertiary active:border-tertiary': variant !== 'bare', | |||
'bg-negative text-primary peer-disabled:text-primary peer-focus:text-secondary peer-checked:text-tertiary active:text-tertiary': variant !== 'filled', | |||
'bg-primary text-negative peer-disabled:bg-primary peer-focus:bg-secondary peer-checked:bg-tertiary active:bg-tertiary': variant === 'filled', | |||
}, | |||
{ | |||
@@ -89,7 +91,6 @@ export const ToggleButton = React.forwardRef<ToggleButtonDerivedElement, ToggleB | |||
{ | |||
'border-current': variant !== 'filled', | |||
'border-negative': variant === 'filled', | |||
'-mr-2': compact, | |||
}, | |||
)} | |||
> | |||
@@ -0,0 +1,158 @@ | |||
import * as React from 'react'; | |||
import { | |||
cleanup, | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import userEvent from '@testing-library/user-event'; | |||
import { | |||
vi, | |||
expect, | |||
describe, | |||
it, | |||
afterEach, | |||
} from 'vitest'; | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { | |||
ToggleSwitch, | |||
ToggleSwitchDerivedElement, | |||
} from '.'; | |||
expect.extend(matchers); | |||
describe('ToggleSwitch', () => { | |||
afterEach(() => { | |||
cleanup(); | |||
}); | |||
it('renders a checkbox', () => { | |||
render( | |||
<ToggleSwitch />, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toBeInTheDocument(); | |||
}); | |||
it('renders a block switch', () => { | |||
render( | |||
<ToggleSwitch | |||
block | |||
/>, | |||
); | |||
const base = screen.getByTestId('base'); | |||
expect(base).toHaveClass('flex'); | |||
}); | |||
it('renders a label when the component is unchecked', () => { | |||
render( | |||
<ToggleSwitch | |||
uncheckedLabel="label" | |||
/>, | |||
); | |||
const uncheckedLabel = screen.getByTestId('uncheckedLabel'); | |||
expect(uncheckedLabel).toBeInTheDocument(); | |||
}); | |||
describe('on subtext', () => { | |||
it('renders without an unchecked label', () => { | |||
render( | |||
<ToggleSwitch | |||
subtext="subtext" | |||
/>, | |||
); | |||
const subtext = screen.getByTestId('subtext'); | |||
expect(subtext).toBeInTheDocument(); | |||
expect(subtext).toHaveClass('pl-16'); | |||
}); | |||
it('renders with an unchecked label', () => { | |||
render( | |||
<ToggleSwitch | |||
subtext="subtext" | |||
uncheckedLabel="label" | |||
/>, | |||
); | |||
const subtext = screen.getByTestId('subtext'); | |||
expect(subtext).toBeInTheDocument(); | |||
expect(subtext).toHaveClass('pt-2'); | |||
}); | |||
}); | |||
describe('on indeterminate', () => { | |||
it('renders an indeterminate checkbox', () => { | |||
render( | |||
<ToggleSwitch | |||
indeterminate | |||
/>, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toHaveProperty('indeterminate', true); | |||
}); | |||
it('acknowledges passed ref object', () => { | |||
const ref = React.createRef<ToggleSwitchDerivedElement>(); | |||
render( | |||
<ToggleSwitch | |||
indeterminate | |||
ref={ref} | |||
/>, | |||
); | |||
expect(ref.current).toHaveProperty('indeterminate', true); | |||
}); | |||
it('acknowledges passed legacy ref', () => { | |||
let refElement = null as null | ToggleSwitchDerivedElement; | |||
const ref = (element: ToggleSwitchDerivedElement) => { | |||
refElement = element; | |||
}; | |||
render( | |||
<ToggleSwitch | |||
indeterminate | |||
ref={ref} | |||
/>, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toHaveProperty('indeterminate', true); | |||
expect(refElement).toBe(checkbox); | |||
}); | |||
}); | |||
it('handles click events', async () => { | |||
const onClick = vi.fn().mockImplementationOnce( | |||
(e: React.MouseEvent<ToggleSwitchDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<ToggleSwitch | |||
onClick={onClick} | |||
/>, | |||
); | |||
const checkbox: HTMLInputElement = screen.getByRole('checkbox'); | |||
await userEvent.click(checkbox); | |||
expect(onClick).toBeCalled(); | |||
}); | |||
it('handles change events', async () => { | |||
const onChange = vi.fn().mockImplementationOnce( | |||
(e: React.ChangeEvent<ToggleSwitchDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<ToggleSwitch | |||
onChange={onChange} | |||
/>, | |||
); | |||
const checkbox: HTMLInputElement = screen.getByRole('checkbox'); | |||
await userEvent.click(checkbox); | |||
expect(onChange).toBeCalled(); | |||
}); | |||
}); |
@@ -31,15 +31,15 @@ export const ToggleSwitch = React.forwardRef<ToggleSwitchDerivedElement, ToggleS | |||
const id = idProp ?? defaultId; | |||
React.useEffect(() => { | |||
if (!(typeof ref === 'object' && ref)) { | |||
return; | |||
} | |||
const { current: element } = ref; | |||
if (!element) { | |||
if (typeof ref === 'function') { | |||
const defaultElement = defaultRef.current as ToggleSwitchDerivedElement; | |||
defaultElement.indeterminate = indeterminate; | |||
ref(defaultElement); | |||
return; | |||
} | |||
const element = ref.current as ToggleSwitchDerivedElement; | |||
element.indeterminate = indeterminate; | |||
}, [indeterminate, ref]); | |||
}, [indeterminate, defaultRef, ref]); | |||
return ( | |||
<div | |||
@@ -50,10 +50,11 @@ export const ToggleSwitch = React.forwardRef<ToggleSwitchDerivedElement, ToggleS | |||
className, | |||
)} | |||
style={style} | |||
data-testid="base" | |||
> | |||
<input | |||
{...etcProps} | |||
ref={ref} | |||
ref={typeof ref === 'function' ? defaultRef : ref} | |||
type="checkbox" | |||
id={id} | |||
className="sr-only peer/radio tesseract-design-toggle-switch" | |||
@@ -0,0 +1,130 @@ | |||
import * as React from 'react'; | |||
import { | |||
cleanup, | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import userEvent from '@testing-library/user-event'; | |||
import { | |||
vi, | |||
expect, | |||
describe, | |||
it, | |||
afterEach, | |||
} from 'vitest'; | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { | |||
ToggleTickBox, | |||
ToggleTickBoxDerivedElement, | |||
} from '.'; | |||
expect.extend(matchers); | |||
describe('ToggleTickBox', () => { | |||
afterEach(() => { | |||
cleanup(); | |||
}); | |||
it('renders a checkbox', () => { | |||
render( | |||
<ToggleTickBox />, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toBeInTheDocument(); | |||
}); | |||
describe('on indeterminate', () => { | |||
it('renders an indeterminate checkbox', () => { | |||
render( | |||
<ToggleTickBox | |||
indeterminate | |||
/>, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toHaveProperty('indeterminate', true); | |||
}); | |||
it('acknowledges passed ref object', () => { | |||
const ref = React.createRef<ToggleTickBoxDerivedElement>(); | |||
render( | |||
<ToggleTickBox | |||
indeterminate | |||
ref={ref} | |||
/>, | |||
); | |||
expect(ref.current).toHaveProperty('indeterminate', true); | |||
}); | |||
it('acknowledges passed legacy ref', () => { | |||
let refElement = null as null | ToggleTickBoxDerivedElement; | |||
const ref = (element: ToggleTickBoxDerivedElement) => { | |||
refElement = element; | |||
}; | |||
render( | |||
<ToggleTickBox | |||
indeterminate | |||
ref={ref} | |||
/>, | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toHaveProperty('indeterminate', true); | |||
expect(refElement).toBe(checkbox); | |||
}); | |||
}); | |||
it('renders a block tick box', () => { | |||
render( | |||
<ToggleTickBox | |||
block | |||
/>, | |||
); | |||
const base = screen.getByTestId('base'); | |||
expect(base).toHaveClass('flex'); | |||
}); | |||
it('renders a subtext', () => { | |||
render( | |||
<ToggleTickBox | |||
subtext="subtext" | |||
/>, | |||
); | |||
const subtext: HTMLElement = screen.getByTestId('subtext'); | |||
expect(subtext).toBeInTheDocument(); | |||
}); | |||
it('handles click events', async () => { | |||
const onClick = vi.fn().mockImplementationOnce( | |||
(e: React.MouseEvent<ToggleTickBoxDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<ToggleTickBox | |||
onClick={onClick} | |||
/>, | |||
); | |||
const checkbox: HTMLInputElement = screen.getByRole('checkbox'); | |||
await userEvent.click(checkbox); | |||
expect(onClick).toBeCalled(); | |||
}); | |||
it('handles change events', async () => { | |||
const onChange = vi.fn().mockImplementationOnce( | |||
(e: React.MouseEvent<ToggleTickBoxDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<ToggleTickBox | |||
onChange={onChange} | |||
/>, | |||
); | |||
const checkbox: HTMLInputElement = screen.getByRole('checkbox'); | |||
await userEvent.click(checkbox); | |||
expect(onChange).toBeCalled(); | |||
}); | |||
}); |
@@ -28,15 +28,15 @@ export const ToggleTickBox = React.forwardRef<ToggleTickBoxDerivedElement, Toggl | |||
const id = idProp ?? defaultId; | |||
React.useEffect(() => { | |||
if (!(typeof ref === 'object' && ref)) { | |||
return; | |||
} | |||
const { current: element } = ref; | |||
if (!element) { | |||
if (typeof ref === 'function') { | |||
const defaultElement = defaultRef.current as ToggleTickBoxDerivedElement; | |||
defaultElement.indeterminate = indeterminate; | |||
ref(defaultElement); | |||
return; | |||
} | |||
const element = ref.current as ToggleTickBoxDerivedElement; | |||
element.indeterminate = indeterminate; | |||
}, [indeterminate, ref]); | |||
}, [indeterminate, defaultRef, ref]); | |||
return ( | |||
<div | |||
@@ -47,10 +47,11 @@ export const ToggleTickBox = React.forwardRef<ToggleTickBoxDerivedElement, Toggl | |||
className, | |||
)} | |||
style={style} | |||
data-testid="base" | |||
> | |||
<input | |||
{...etcProps} | |||
ref={ref} | |||
ref={typeof ref === 'function' ? defaultRef : ref} | |||
type="checkbox" | |||
id={id} | |||
className="sr-only peer/radio tesseract-design-toggle-tick-box" | |||
@@ -0,0 +1,14 @@ | |||
import { describe, it, expect } from 'vitest'; | |||
import * as WebMultiChoiceReact from '.'; | |||
describe('web-multichoice-react', () => { | |||
it.each([ | |||
'MenuMultiSelect', | |||
'TagInput', | |||
'ToggleButton', | |||
'ToggleSwitch', | |||
'ToggleTickBox', | |||
])('exports %s', (namedExport) => { | |||
expect(WebMultiChoiceReact).toHaveProperty(namedExport); | |||
}); | |||
}); |
@@ -15,8 +15,11 @@ | |||
"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", | |||
"@vitest/coverage-v8": "^0.33.0", | |||
"eslint": "^8.35.0", | |||
"eslint-config-lxsmnsyc": "^0.5.0", | |||
"jsdom": "^21.1.0", | |||
@@ -26,7 +29,7 @@ | |||
"react-test-renderer": "^18.2.0", | |||
"tslib": "^2.5.0", | |||
"typescript": "^4.9.5", | |||
"vitest": "^0.28.1" | |||
"vitest": "^0.33.0" | |||
}, | |||
"peerDependencies": { | |||
"react": "^16.8 || ^17.0 || ^18.0", | |||
@@ -0,0 +1,203 @@ | |||
import * as React from 'react'; | |||
import { | |||
cleanup, | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import userEvent from '@testing-library/user-event'; | |||
import { Button } from '@tesseract-design/web-base'; | |||
import { | |||
vi, | |||
expect, | |||
describe, | |||
it, | |||
afterEach, | |||
} from 'vitest'; | |||
import matchers from '@testing-library/jest-dom/matchers'; | |||
import { | |||
LinkButton, | |||
LinkButtonDerivedElement, | |||
} from '.'; | |||
expect.extend(matchers); | |||
describe('LinkButton', () => { | |||
afterEach(() => { | |||
cleanup(); | |||
}); | |||
it('renders a button', () => { | |||
render( | |||
<LinkButton | |||
href="https://www.example.com" | |||
/>, | |||
); | |||
const button = screen.getByRole('link'); | |||
expect(button).toBeInTheDocument(); | |||
}); | |||
it('renders a subtext', () => { | |||
render( | |||
<LinkButton | |||
href="#" | |||
subtext="subtext" | |||
/>, | |||
); | |||
const subtext = screen.getByTestId('subtext'); | |||
expect(subtext).toBeInTheDocument(); | |||
}); | |||
it('renders a badge', () => { | |||
render( | |||
<LinkButton | |||
href="#" | |||
badge="badge" | |||
/>, | |||
); | |||
const badge = screen.getByTestId('badge'); | |||
expect(badge).toBeInTheDocument(); | |||
}); | |||
it('renders as a menu item', () => { | |||
render( | |||
<LinkButton | |||
href="#" | |||
menuItem | |||
/>, | |||
); | |||
const menuItemIndicator = screen.getByTestId('menuItemIndicator'); | |||
expect(menuItemIndicator).toBeInTheDocument(); | |||
}); | |||
it('handles click events', async () => { | |||
const onClick = vi.fn().mockImplementationOnce( | |||
(e: React.MouseEvent<LinkButtonDerivedElement>) => { | |||
e.preventDefault(); | |||
}, | |||
); | |||
render( | |||
<LinkButton | |||
href="#" | |||
onClick={onClick} | |||
/>, | |||
); | |||
const button = screen.getByRole('link'); | |||
await userEvent.click(button); | |||
expect(onClick).toBeCalled(); | |||
}); | |||
it('renders a compact link', () => { | |||
render( | |||
<LinkButton | |||
href="#" | |||
compact | |||
/>, | |||
); | |||
const button = screen.getByRole('link'); | |||
expect(button).toHaveClass('pl-2 gap-2 pr-2'); | |||
}); | |||
describe.each` | |||
size | className | |||
${'small'} | ${'h-10'} | |||
${'medium'} | ${'h-12'} | |||
${'large'} | ${'h-16'} | |||
`('on $size size', ({ | |||
size, | |||
className, | |||
}: { size: Button.Size, className: string }) => { | |||
it('renders link styles', () => { | |||
render( | |||
<LinkButton | |||
href="#" | |||
size={size} | |||
/>, | |||
); | |||
const button = screen.getByRole('link'); | |||
expect(button).toHaveClass(className); | |||
}); | |||
it('renders badge styles', () => { | |||
render( | |||
<LinkButton | |||
href="#" | |||
size={size} | |||
badge="badge" | |||
/>, | |||
); | |||
const badge = screen.getByTestId('badge'); | |||
expect(badge).toBeInTheDocument(); | |||
}); | |||
}); | |||
it.each` | |||
variant | className | |||
${'bare'} | ${'bg-negative'} | |||
${'outline'} | ${'border-2'} | |||
${'filled'} | ${'bg-primary'} | |||
`('renders a link with $variant variant', ({ | |||
variant, | |||
className, | |||
}: { variant: Button.Variant, className: string }) => { | |||
render( | |||
<LinkButton | |||
href="#" | |||
variant={variant} | |||
/>, | |||
); | |||
const button = screen.getByRole('link'); | |||
expect(button).toHaveClass(className); | |||
}); | |||
it('renders a block link', () => { | |||
render( | |||
<LinkButton | |||
href="#" | |||
block | |||
/>, | |||
); | |||
const button = screen.getByRole('link'); | |||
expect(button).toHaveClass('w-full flex'); | |||
}); | |||
it('renders children', () => { | |||
render( | |||
<LinkButton | |||
href="#" | |||
> | |||
Foo | |||
</LinkButton>, | |||
); | |||
const children = screen.getByTestId('children'); | |||
expect(children).toHaveTextContent('Foo'); | |||
}); | |||
it.each(Button.AVAILABLE_TYPES)('renders a link with type %s', (buttonType) => { | |||
render( | |||
<LinkButton | |||
href="#" | |||
type={buttonType} | |||
/>, | |||
); | |||
const button = screen.getByRole('link'); | |||
expect(button).toHaveProperty('type', buttonType); | |||
}); | |||
it('renders a disabled link', () => { | |||
render( | |||
<LinkButton | |||
href="#" | |||
disabled | |||
/>, | |||
); | |||
const button = screen.queryByRole('link'); | |||
expect(button).toBeNull(); | |||
}); | |||
}); |
@@ -13,6 +13,7 @@ export interface LinkButtonProps<T = any> extends Omit<React.HTMLProps<LinkButto | |||
size?: Button.Size; | |||
compact?: boolean; | |||
component?: React.ElementType<T>; | |||
disabled?: boolean; | |||
} | |||
export const LinkButton = React.forwardRef<LinkButtonDerivedElement, LinkButtonProps>(( | |||
@@ -26,97 +27,103 @@ export const LinkButton = React.forwardRef<LinkButtonDerivedElement, LinkButtonP | |||
compact = false, | |||
className, | |||
block = false, | |||
component: Component = 'a', | |||
component: EnabledComponent = 'a', | |||
disabled = false, | |||
...etcProps | |||
}, | |||
forwardedRef, | |||
) => ( | |||
<Component | |||
{...etcProps} | |||
ref={forwardedRef} | |||
className={clsx( | |||
'items-center justify-center rounded overflow-hidden ring-secondary/50 leading-none select-none no-underline', | |||
'focus:outline-0 focus:ring-4', | |||
'active:ring-tertiary/50', | |||
{ | |||
'flex w-full': block, | |||
'inline-flex max-w-full align-middle': !block, | |||
}, | |||
{ | |||
'pl-2 gap-2': compact, | |||
'pl-4 gap-4': !compact, | |||
'pr-4': !(compact || menuItem), | |||
'pr-2': compact || menuItem, | |||
}, | |||
{ | |||
'border-2 border-primary focus:border-secondary active:border-tertiary': variant !== 'bare', | |||
'bg-negative text-primary focus:text-secondary active:text-tertiary': variant !== 'filled', | |||
'bg-primary text-negative focus:bg-secondary active:bg-tertiary focus:text-negative active:text-negative': variant === 'filled', | |||
}, | |||
{ | |||
'h-10': size === 'small', | |||
'h-12': size === 'medium', | |||
'h-16': size === 'large', | |||
}, | |||
className, | |||
)} | |||
> | |||
<span | |||
) => { | |||
const Component = disabled ? 'span' : EnabledComponent; | |||
return ( | |||
<Component | |||
{...etcProps} | |||
ref={forwardedRef} | |||
className={clsx( | |||
'flex-auto min-w-0', | |||
'items-center justify-center rounded overflow-hidden ring-secondary/50 leading-none select-none no-underline m-0', | |||
'focus:outline-0 focus:ring-4', | |||
'active:ring-tertiary/50', | |||
disabled && 'opacity-50 cursor-not-allowed', | |||
{ | |||
'text-left': compact || menuItem, | |||
'text-center': !(compact || menuItem), | |||
'flex w-full': block, | |||
'inline-flex max-w-full align-middle': !block, | |||
}, | |||
{ | |||
'pl-2 gap-2': compact, | |||
'pl-4 gap-4': !compact, | |||
'pr-4': !(compact || menuItem), | |||
'pr-2': compact || menuItem, | |||
}, | |||
{ | |||
'border-2 border-primary focus:border-secondary active:border-tertiary': variant !== 'bare', | |||
'bg-negative text-primary focus:text-secondary active:text-tertiary': variant !== 'filled', | |||
'bg-primary text-negative focus:bg-secondary active:bg-tertiary focus:text-negative active:text-negative': variant === 'filled', | |||
}, | |||
{ | |||
'h-10': size === 'small', | |||
'h-12': size === 'medium', | |||
'h-16': size === 'large', | |||
}, | |||
className, | |||
)} | |||
data-testid="link" | |||
> | |||
<span | |||
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded" | |||
data-testid="children" | |||
className={clsx( | |||
'flex-auto min-w-0', | |||
{ | |||
'text-left': compact || menuItem, | |||
'text-center': !(compact || menuItem), | |||
}, | |||
)} | |||
> | |||
{children} | |||
<span | |||
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded" | |||
data-testid="children" | |||
> | |||
{children} | |||
</span> | |||
{subtext && ( | |||
<> | |||
<span className="sr-only"> | |||
{' - '} | |||
</span> | |||
<span | |||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||
data-testid="subtext" | |||
> | |||
{subtext} | |||
</span> | |||
</> | |||
)} | |||
</span> | |||
{subtext && ( | |||
{badge && ( | |||
<> | |||
<span className="sr-only"> | |||
{' - '} | |||
</span> | |||
<span | |||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||
data-testid="subtext" | |||
data-testid="badge" | |||
> | |||
{subtext} | |||
{badge} | |||
</span> | |||
</> | |||
)} | |||
</span> | |||
{badge && ( | |||
<> | |||
<span className="sr-only"> | |||
{' - '} | |||
</span> | |||
{menuItem && ( | |||
<span | |||
data-testid="badge" | |||
data-testid="menuItemIndicator" | |||
> | |||
{badge} | |||
<svg | |||
className="w-6 h-6 fill-none stroke-current stroke-2 linejoin-round linecap-round" | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
<polyline points="9 18 15 12 9 6" /> | |||
</svg> | |||
</span> | |||
</> | |||
)} | |||
{menuItem && ( | |||
<span | |||
data-testid="menuItemIndicator" | |||
> | |||
<svg | |||
className="w-6 h-6 fill-none stroke-current stroke-2 linejoin-round linecap-round" | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
<polyline points="9 18 15 12 9 6" /> | |||
</svg> | |||
</span> | |||
)} | |||
</Component> | |||
)); | |||
)} | |||
</Component> | |||
); | |||
}); | |||
LinkButton.displayName = 'LinkButton'; | |||
@@ -129,4 +136,5 @@ LinkButton.defaultProps = { | |||
badge: undefined, | |||
subtext: undefined, | |||
block: false, | |||
disabled: false, | |||
}; |
@@ -0,0 +1,10 @@ | |||
import { describe, it, expect } from 'vitest'; | |||
import * as WebNavigationReact from '.'; | |||
describe('web-navigation-react', () => { | |||
it.each([ | |||
'LinkButton', | |||
])('exports %s', (namedExport) => { | |||
expect(WebNavigationReact).toHaveProperty(namedExport); | |||
}); | |||
}); |
@@ -2,7 +2,8 @@ | |||
"root": true, | |||
"rules": { | |||
"quote-props": "off", | |||
"react/jsx-props-no-spreading": "off" | |||
"react/jsx-props-no-spreading": "off", | |||
"import/no-extraneous-dependencies": "off" | |||
}, | |||
"extends": [ | |||
"lxsmnsyc/typescript/react" | |||
@@ -329,12 +329,21 @@ importers: | |||
'@testing-library/react': | |||
specifier: ^13.4.0 | |||
version: 13.4.0(react-dom@18.2.0)(react@18.2.0) | |||
'@testing-library/user-event': | |||
specifier: ^14.4.3 | |||
version: 14.4.3(@testing-library/dom@8.20.1) | |||
'@types/node': | |||
specifier: ^18.14.1 | |||
version: 18.14.1 | |||
'@types/react': | |||
specifier: ^18.0.27 | |||
version: 18.2.14 | |||
'@types/testing-library__jest-dom': | |||
specifier: ^5.14.7 | |||
version: 5.14.7 | |||
'@vitest/coverage-v8': | |||
specifier: ^0.33.0 | |||
version: 0.33.0(vitest@0.33.0) | |||
eslint: | |||
specifier: ^8.35.0 | |||
version: 8.43.0 | |||
@@ -363,8 +372,8 @@ importers: | |||
specifier: ^4.9.5 | |||
version: 4.9.5 | |||
vitest: | |||
specifier: ^0.28.1 | |||
version: 0.28.1(jsdom@21.1.0) | |||
specifier: ^0.33.0 | |||
version: 0.33.0(jsdom@21.1.0) | |||
categories/information/react: | |||
dependencies: | |||
@@ -378,12 +387,21 @@ importers: | |||
'@testing-library/react': | |||
specifier: ^13.4.0 | |||
version: 13.4.0(react-dom@18.2.0)(react@18.2.0) | |||
'@testing-library/user-event': | |||
specifier: ^14.4.3 | |||
version: 14.4.3(@testing-library/dom@8.20.1) | |||
'@types/node': | |||
specifier: ^18.14.1 | |||
version: 18.14.1 | |||
'@types/react': | |||
specifier: ^18.0.27 | |||
version: 18.2.14 | |||
'@types/testing-library__jest-dom': | |||
specifier: ^5.14.7 | |||
version: 5.14.7 | |||
'@vitest/coverage-v8': | |||
specifier: ^0.33.0 | |||
version: 0.33.0(vitest@0.33.0) | |||
eslint: | |||
specifier: ^8.35.0 | |||
version: 8.43.0 | |||
@@ -412,8 +430,8 @@ importers: | |||
specifier: ^4.9.5 | |||
version: 4.9.5 | |||
vitest: | |||
specifier: ^0.28.1 | |||
version: 0.28.1(jsdom@21.1.0) | |||
specifier: ^0.33.0 | |||
version: 0.33.0(jsdom@21.1.0) | |||
categories/multichoice/react: | |||
dependencies: | |||
@@ -436,12 +454,21 @@ importers: | |||
'@testing-library/react': | |||
specifier: ^13.4.0 | |||
version: 13.4.0(react-dom@18.2.0)(react@18.2.0) | |||
'@testing-library/user-event': | |||
specifier: ^14.4.3 | |||
version: 14.4.3(@testing-library/dom@8.20.1) | |||
'@types/node': | |||
specifier: ^18.14.1 | |||
version: 18.14.1 | |||
'@types/react': | |||
specifier: ^18.0.27 | |||
version: 18.2.14 | |||
'@types/testing-library__jest-dom': | |||
specifier: ^5.14.7 | |||
version: 5.14.7 | |||
'@vitest/coverage-v8': | |||
specifier: ^0.33.0 | |||
version: 0.33.0(vitest@0.33.0) | |||
eslint: | |||
specifier: ^8.35.0 | |||
version: 8.43.0 | |||
@@ -473,8 +500,8 @@ importers: | |||
specifier: ^4.9.5 | |||
version: 4.9.5 | |||
vitest: | |||
specifier: ^0.28.1 | |||
version: 0.28.1(jsdom@21.1.0) | |||
specifier: ^0.33.0 | |||
version: 0.33.0(jsdom@21.1.0) | |||
categories/navigation/react: | |||
dependencies: | |||
@@ -491,12 +518,21 @@ importers: | |||
'@testing-library/react': | |||
specifier: ^13.4.0 | |||
version: 13.4.0(react-dom@18.2.0)(react@18.2.0) | |||
'@testing-library/user-event': | |||
specifier: ^14.4.3 | |||
version: 14.4.3(@testing-library/dom@8.20.1) | |||
'@types/node': | |||
specifier: ^18.14.1 | |||
version: 18.14.1 | |||
'@types/react': | |||
specifier: ^18.0.27 | |||
version: 18.2.14 | |||
'@types/testing-library__jest-dom': | |||
specifier: ^5.14.7 | |||
version: 5.14.7 | |||
'@vitest/coverage-v8': | |||
specifier: ^0.33.0 | |||
version: 0.33.0(vitest@0.33.0) | |||
eslint: | |||
specifier: ^8.35.0 | |||
version: 8.43.0 | |||
@@ -525,8 +561,8 @@ importers: | |||
specifier: ^4.9.5 | |||
version: 4.9.5 | |||
vitest: | |||
specifier: ^0.28.1 | |||
version: 0.28.1(jsdom@21.1.0) | |||
specifier: ^0.33.0 | |||
version: 0.33.0(jsdom@21.1.0) | |||
categories/number/react: | |||
dependencies: | |||
@@ -905,6 +905,8 @@ const OptionPage: NextPage<Props> = ({ | |||
<div> | |||
<MultiChoice.ToggleButton | |||
variant="bare" | |||
indeterminate | |||
ref={(el) => { console.log(el); }} | |||
block | |||
> | |||
Button | |||