Implement tests to increase code coverage The option module was modified to split components and avoid overloading.master
@@ -34,4 +34,5 @@ yarn-error.log* | |||
# vercel | |||
.vercel | |||
.idea/ | |||
.idea/ | |||
coverage/ |
@@ -0,0 +1,45 @@ | |||
# Tesseract Web | |||
## Rationale | |||
- Every component library makes their own conventions of component organization | |||
- Graceful degradation through backwards compatibility with HTML controls is not the main focus | |||
- In return, aspects such as accessibility may suffer since HTML controls are generally compliant to accessibility | |||
considerations suggested by approved standards | |||
- Emerging Web applications are at risk of deviating through said standards which can fragment the Web platform | |||
implementation | |||
- Hopefully we can inspire component development through the component organization we are proposing; | |||
everyone can have their own implementations | |||
- cf. Authoring (publishing interfaces) vs Dependency (coercing consumers to adapt to concrete implementations) | |||
## Ground Rules | |||
- Each component is an enhanced version of an HTML control (see [HTML enhancement](#html-enhancement)) | |||
- Each component should only do one thing and one thing only in terms of purpose and appearance (contrast to HTML's | |||
`<input>` where its function is overloaded depending on its `type` attribute) | |||
- Each component has decoupled styling, wherein each styling has their own API | |||
- This styling API is then made cross-framework compatible | |||
- This styling API allows use of dynamic styles (which then requires CSS-in-JS way of application) | |||
## HTML enhancement | |||
| HTML element | Tesseract counterpart | Remarks | | |||
|----------------------------------------------------------------------------|-------------------------------|-----------------------------------------------------------------------| | |||
| `<button>` | `action/ActionButton` | | | |||
| `<input type="button">` | `action/ActionButton` | | | |||
| `<input type="reset">` | `action/ActionButton` | | | |||
| `<input type="submit">` | `action/ActionButton` | | | |||
| `<textarea>` | `freeform/MultilineTextInput` | | | |||
| `<input type="text">` | `freeform/TextInput` | | | |||
| `<input type="search">` | `freeform/TextInput` | | | |||
| `<input type="password">` | `freeform/MaskedTextInput` | | | |||
| `<a>` with button appearance | `navigation/LinkButton` | | | |||
| `<select>` without `multiple` attribute | `option/DropdownSelect` | | | |||
| `<input type="checkbox">` with `indeterminate` state | `option/ToggleTickBox` | | | |||
| `<input type="checkbox">` with `indeterminate` state and button appearance | `option/ToggleButton` | | | |||
| `<input type="checkbox">` without `indeterminate` state | `option/ToggleSwitch` | Prefer using this component when indeterminate state is not expected. | | |||
| `<input type="radio">` | `option/RadioTickBox` | | | |||
| `<input type="radio">` with button appearance | `option/RadioButton` | | | |||
| `<input type="number">` | `number/Spinner` | Use this component for discrete values. | | |||
| `<input type="range">` | `number/Slider` | Use this component for continuous values. | | |||
@@ -1,3 +1,5 @@ | |||
const tsconfig = require('./tsconfig.json'); | |||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ | |||
module.exports = { | |||
preset: 'ts-jest', | |||
@@ -7,4 +9,13 @@ module.exports = { | |||
tsconfig: 'tsconfig.test.json', | |||
}, | |||
}, | |||
moduleNameMapper: Object.fromEntries( | |||
Object | |||
.entries(tsconfig.compilerOptions.paths) | |||
.map(([alias, paths]) => [alias, paths[0].replace('.', '<rootDir>')]) | |||
), | |||
collectCoverageFrom: [ | |||
'src/modules/**/*.{ts,tsx}', | |||
'!src/modules/base-*/**/*.*', | |||
], | |||
}; |
@@ -6,7 +6,8 @@ | |||
"dev": "next dev", | |||
"build": "next build", | |||
"start": "next start", | |||
"lint": "next lint" | |||
"lint": "next lint", | |||
"test": "jest" | |||
}, | |||
"dependencies": { | |||
"goober": "^2.0.41", | |||
@@ -16,6 +17,7 @@ | |||
"tailwindcss": "^2.2.16" | |||
}, | |||
"devDependencies": { | |||
"@testing-library/user-event": "^13.5.0", | |||
"@types/jest": "^27.0.3", | |||
"@types/react": "17.0.27", | |||
"eslint": "^7.32.0", | |||
@@ -0,0 +1,201 @@ | |||
import * as React from 'react'; | |||
import { | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import { | |||
ActionButton, | |||
ActionButtonType, | |||
} from '.'; | |||
import userEvent from '@testing-library/user-event'; | |||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||
jest.mock('@tesseract-design/web-base-button'); | |||
describe('ActionButton', () => { | |||
it('should render a button', () => { | |||
render( | |||
<ActionButton /> | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('button'); | |||
expect(button).toBeInTheDocument(); | |||
expect(button).toHaveProperty('type', 'button'); | |||
}); | |||
it('should render a subtext', () => { | |||
render( | |||
<ActionButton | |||
subtext="subtext" | |||
/> | |||
); | |||
const subtext: HTMLElement = screen.getByTestId('subtext'); | |||
expect(subtext).toBeInTheDocument(); | |||
}); | |||
it('should render a badge', () => { | |||
render( | |||
<ActionButton | |||
badge="badge" | |||
/> | |||
); | |||
const badge: HTMLElement = screen.getByTestId('badge'); | |||
expect(badge).toBeInTheDocument(); | |||
}); | |||
it('should render as a menu item', () => { | |||
render( | |||
<ActionButton | |||
menuItem | |||
/> | |||
); | |||
const menuItemIndicator: HTMLElement = screen.getByTestId('menuItemIndicator'); | |||
expect(menuItemIndicator).toBeInTheDocument(); | |||
}); | |||
it('should handle click events', () => { | |||
const onClick = jest.fn(); | |||
render( | |||
<ActionButton | |||
onClick={onClick} | |||
/> | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('button'); | |||
userEvent.click(button); | |||
expect(onClick).toBeCalled(); | |||
}); | |||
it('should render a compact button', () => { | |||
render( | |||
<ActionButton | |||
compact | |||
/> | |||
); | |||
expect(ButtonBase.Button).toBeCalledWith(expect.objectContaining({ | |||
compact: true, | |||
})); | |||
expect(ButtonBase.Label).toBeCalledWith(expect.objectContaining({ | |||
compact: true, | |||
})); | |||
}); | |||
describe.each([ | |||
ButtonBase.ButtonSize.SMALL, | |||
ButtonBase.ButtonSize.MEDIUM, | |||
ButtonBase.ButtonSize.LARGE, | |||
])('on %s size', (size) => { | |||
it('should render button styles', () => { | |||
render( | |||
<ActionButton | |||
size={size} | |||
/> | |||
); | |||
expect(ButtonBase.Button).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render badge styles', () => { | |||
render( | |||
<ActionButton | |||
size={size} | |||
badge="badge" | |||
/> | |||
); | |||
expect(ButtonBase.BadgeContainer).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render indicator styles', () => { | |||
render( | |||
<ActionButton | |||
size={size} | |||
menuItem | |||
/> | |||
); | |||
expect(ButtonBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
}); | |||
it.each([ | |||
ButtonBase.ButtonVariant.OUTLINE, | |||
ButtonBase.ButtonVariant.FILLED, | |||
])('should render a button with variant %s', (variant) => { | |||
render( | |||
<ActionButton | |||
variant={variant} | |||
/> | |||
); | |||
expect(ButtonBase.Button).toBeCalledWith(expect.objectContaining({ | |||
variant, | |||
})); | |||
}); | |||
it('should render a bordered button', () => { | |||
render( | |||
<ActionButton | |||
border | |||
/> | |||
); | |||
expect(ButtonBase.Border).toBeCalledWith(expect.objectContaining({ | |||
border: true, | |||
})); | |||
}); | |||
it('should render a block button', () => { | |||
render( | |||
<ActionButton | |||
block | |||
/> | |||
); | |||
expect(ButtonBase.Border).toBeCalledWith(expect.objectContaining({ | |||
block: true, | |||
})); | |||
}); | |||
it('should render children', () => { | |||
render( | |||
<ActionButton> | |||
Foo | |||
</ActionButton> | |||
); | |||
const children: HTMLElement = screen.getByTestId('children'); | |||
expect(children).toHaveTextContent('Foo'); | |||
}); | |||
it.each([ | |||
ActionButtonType.BUTTON, | |||
ActionButtonType.RESET, | |||
ActionButtonType.SUBMIT, | |||
])('should render a button with type %s', (buttonType) => { | |||
render( | |||
<ActionButton | |||
type={buttonType} | |||
/> | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('button'); | |||
expect(button).toHaveProperty('type', buttonType); | |||
}); | |||
it('should render a disabled button', () => { | |||
render( | |||
<ActionButton | |||
disabled | |||
/> | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('button'); | |||
expect(button).toBeDisabled(); | |||
}); | |||
}); |
@@ -1,12 +1,18 @@ | |||
import * as React from 'react'; | |||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||
/** | |||
* Available ActionButton type values. | |||
*/ | |||
export enum ActionButtonType { | |||
SUBMIT = 'submit', | |||
RESET = 'reset', | |||
BUTTON = 'button', | |||
} | |||
/** | |||
* Props for the component. | |||
*/ | |||
export type ActionButtonProps = Omit<React.HTMLProps<HTMLButtonElement>, 'size' | 'type' | 'style'> & { | |||
/** | |||
* Size of the component. | |||
@@ -28,10 +34,6 @@ export type ActionButtonProps = Omit<React.HTMLProps<HTMLButtonElement>, 'size' | |||
* Type of the component. | |||
*/ | |||
type?: ActionButtonType, | |||
/** | |||
* Style of the component. | |||
*/ | |||
style?: ButtonBase.ButtonStyle, | |||
/** | |||
* Does the component need to conserve space? | |||
*/ | |||
@@ -64,7 +66,6 @@ export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProp | |||
children, | |||
type = ActionButtonType.BUTTON, | |||
block = false, | |||
style = ButtonBase.ButtonStyle.DEFAULT, | |||
disabled = false, | |||
compact = false, | |||
subtext, | |||
@@ -81,11 +82,10 @@ export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProp | |||
block, | |||
variant, | |||
border, | |||
style, | |||
compact, | |||
menuItem, | |||
disabled, | |||
}), [size, block, variant, border, style, compact, menuItem]); | |||
}), [size, block, variant, border, compact, menuItem, disabled]); | |||
return ( | |||
<button | |||
@@ -93,7 +93,7 @@ export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProp | |||
disabled={disabled} | |||
className={ButtonBase.Button(styleProps)} | |||
ref={ref} | |||
type={type ?? ActionButtonType.BUTTON} | |||
type={type} | |||
> | |||
<span | |||
className={ButtonBase.Border(styleProps)} | |||
@@ -103,6 +103,7 @@ export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProp | |||
> | |||
<span | |||
className={ButtonBase.MainText()} | |||
data-testid="children" | |||
> | |||
<span | |||
className={ButtonBase.OverflowText()} | |||
@@ -115,7 +116,10 @@ export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProp | |||
&& ( | |||
<> | |||
{' '} | |||
<span className={ButtonBase.Subtext()}> | |||
<span | |||
className={ButtonBase.Subtext()} | |||
data-testid="subtext" | |||
> | |||
<span | |||
className={ButtonBase.OverflowText()} | |||
> | |||
@@ -133,6 +137,7 @@ export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProp | |||
{' '} | |||
<span | |||
className={ButtonBase.BadgeContainer(styleProps)} | |||
data-testid="badge" | |||
> | |||
{badge} | |||
</span> | |||
@@ -146,6 +151,7 @@ export const ActionButton = React.forwardRef<HTMLButtonElement, ActionButtonProp | |||
{' '} | |||
<span | |||
className={ButtonBase.IndicatorWrapper(styleProps)} | |||
data-testid="menuItemIndicator" | |||
> | |||
<svg | |||
className={ButtonBase.Indicator()} | |||
@@ -0,0 +1,9 @@ | |||
import * as WebActionReact from '.'; | |||
describe('web-action-react', () => { | |||
it.each([ | |||
'ActionButton', | |||
])('should export %s', (namedExport) => { | |||
expect(WebActionReact).toHaveProperty(namedExport); | |||
}); | |||
}); |
@@ -0,0 +1,42 @@ | |||
import { css } from '@tesseract-design/css-utils'; | |||
export type BadgeBaseArgs = { | |||
rounded: boolean, | |||
} | |||
export const Root = ({ | |||
rounded, | |||
}: BadgeBaseArgs) => css.cx( | |||
css` | |||
position: relative; | |||
height: 1.5em; | |||
min-width: 1.5em; | |||
display: inline-grid; | |||
vertical-align: middle; | |||
place-content: center; | |||
overflow: hidden; | |||
font-stretch: var(--font-stretch-base, normal); | |||
padding: 0 0.25rem; | |||
box-sizing: border-box; | |||
&::before { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
height: 100%; | |||
background-color: currentColor; | |||
opacity: 0.25; | |||
content: ''; | |||
} | |||
`, | |||
css.dynamic({ | |||
'border-radius': rounded ? '0.75em' : '0.25rem', | |||
}), | |||
); | |||
export const Content = () => css.cx( | |||
css` | |||
position: relative; | |||
font-size: 0.75em; | |||
` | |||
); |
@@ -11,17 +11,12 @@ export enum ButtonVariant { | |||
FILLED = 'filled', | |||
} | |||
export enum ButtonStyle { | |||
DEFAULT = 'default', | |||
} | |||
export type ButtonBaseArgs = { | |||
size: ButtonSize, | |||
block: boolean, | |||
variant: ButtonVariant, | |||
border: boolean, | |||
disabled: boolean, | |||
style: ButtonStyle, | |||
compact: boolean, | |||
menuItem: boolean, | |||
} | |||
@@ -186,7 +181,6 @@ export const Border = ({ | |||
), | |||
); | |||
export const Label = ({ | |||
compact, | |||
menuItem, | |||
@@ -1,7 +1,7 @@ | |||
import { css } from '@tesseract-design/css-utils' | |||
export enum CheckControlAppearance { | |||
DEFAULT = 'default', | |||
TICK_BOX = 'tick-box', | |||
BUTTON = 'button', | |||
SWITCH = 'switch', | |||
} | |||
@@ -62,7 +62,7 @@ export const CheckStateContainer = ({ | |||
`, | |||
css.nest('&:checked + * > :first-child + * > *') ( | |||
css.if ( | |||
appearance === CheckControlAppearance.DEFAULT | |||
appearance === CheckControlAppearance.TICK_BOX | |||
|| appearance === CheckControlAppearance.BUTTON | |||
) ( | |||
css.if (type === 'checkbox') ( | |||
@@ -106,7 +106,7 @@ export const CheckStateContainer = ({ | |||
css.nest('&:indeterminate[type="checkbox"] + * > :first-child + * > *') ( | |||
css.if ( | |||
appearance === CheckControlAppearance.BUTTON | |||
|| appearance === CheckControlAppearance.DEFAULT | |||
|| appearance === CheckControlAppearance.TICK_BOX | |||
) ( | |||
css` | |||
width: 1.5em; | |||
@@ -125,7 +125,7 @@ export const CheckStateContainer = ({ | |||
css.nest('&:indeterminate[type="checkbox"] + * > :first-child + * > * > :first-child + *') ( | |||
css.if ( | |||
appearance === CheckControlAppearance.BUTTON | |||
|| appearance === CheckControlAppearance.DEFAULT | |||
|| appearance === CheckControlAppearance.TICK_BOX | |||
) ( | |||
css` | |||
display: block; | |||
@@ -135,7 +135,7 @@ export const CheckStateContainer = ({ | |||
css.nest('&:checked + * > :first-child + * > * > :first-child') ( | |||
css.if ( | |||
appearance === CheckControlAppearance.BUTTON | |||
|| appearance === CheckControlAppearance.DEFAULT | |||
|| appearance === CheckControlAppearance.TICK_BOX | |||
) ( | |||
css` | |||
display: block; | |||
@@ -177,7 +177,7 @@ export const CheckIndicatorArea = ({ | |||
box-sizing: border-box; | |||
} | |||
`, | |||
css.if (appearance === CheckControlAppearance.DEFAULT) ( | |||
css.if (appearance === CheckControlAppearance.TICK_BOX) ( | |||
css` | |||
width: 1.5em; | |||
height: 1.5em; | |||
@@ -240,7 +240,7 @@ export const CheckIndicatorWrapper = ({ | |||
border-radius: inherit; | |||
`, | |||
css.if( | |||
appearance === CheckControlAppearance.DEFAULT | |||
appearance === CheckControlAppearance.TICK_BOX | |||
|| appearance === CheckControlAppearance.BUTTON | |||
) ( | |||
css` | |||
@@ -289,7 +289,7 @@ export const ClickAreaWrapper = ({ | |||
css.dynamic({ | |||
display: block ? 'block' : 'inline-block', | |||
}), | |||
css.if (appearance === CheckControlAppearance.DEFAULT) ( | |||
css.if (appearance === CheckControlAppearance.TICK_BOX) ( | |||
css` | |||
padding-left: 2.25rem; | |||
text-indent: -2.25rem; | |||
@@ -57,7 +57,7 @@ export type TextControlBaseArgs = { | |||
predefinedValues: boolean, | |||
} | |||
export const ComponentBase = ({ | |||
export const Root = ({ | |||
block, | |||
}: TextControlBaseArgs): string => css.cx( | |||
css` | |||
@@ -365,3 +365,15 @@ export const IndicatorWrapper = ({ | |||
height: MIN_HEIGHTS[size], | |||
}), | |||
); | |||
export const Indicator = (): string => css.cx( | |||
css` | |||
width: 1.5em; | |||
height: 1.5em; | |||
fill: none; | |||
stroke: currentColor; | |||
stroke-width: 2; | |||
stroke-linecap: round; | |||
stroke-linejoin: round; | |||
`, | |||
); |
@@ -0,0 +1,22 @@ | |||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||
// TODO check if a utility library like this is needed! | |||
export type ButtonBaseProps<Node> = { | |||
/** | |||
* Size of the component. | |||
*/ | |||
size?: ButtonBase.ButtonSize, | |||
/** | |||
* Variant of the component. | |||
*/ | |||
variant?: ButtonBase.ButtonVariant, | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Short complementary content displayed at the edge of the component. | |||
*/ | |||
badge?: Node, | |||
} |
@@ -1,5 +1,11 @@ | |||
import { css, CssIfStringImpl, CssStringImpl } from '.'; | |||
jest.mock('goober', () => { | |||
return { | |||
css: () => 'gooberClass', | |||
}; | |||
}); | |||
describe('css-utils', () => { | |||
describe('css', () => { | |||
it('should return CssString', () => { | |||
@@ -9,7 +15,7 @@ describe('css-utils', () => { | |||
` | |||
expect(c).toBeInstanceOf(CssStringImpl); | |||
expect(c.toString()).toBe('background-color:white; color:black;'); | |||
expect(c.toString()).toBe('background-color:white;color:black;'); | |||
}) | |||
}) | |||
@@ -35,7 +41,7 @@ describe('css-utils', () => { | |||
expect(c.toString()).toBe('') | |||
}) | |||
it('should return CssString with .else when the condition is true', () => { | |||
it('should return CssString with .else when the if condition is false', () => { | |||
const c = css.if(false)( | |||
css` | |||
background-color: white; | |||
@@ -46,42 +52,21 @@ describe('css-utils', () => { | |||
` | |||
) | |||
expect(c).toBeInstanceOf(CssStringImpl) | |||
expect(c.toString()).toBe('background-color:black;') | |||
}) | |||
it('should return CssString with .else.if when the condition is true', () => { | |||
const c = css.if(false)( | |||
css` | |||
background-color: white; | |||
` | |||
).else.if(true)( | |||
css` | |||
background-color: black; | |||
` | |||
) | |||
expect(c).toBeInstanceOf(CssIfStringImpl) | |||
expect(c.toString()).toBe('background-color:black;') | |||
}) | |||
it('should return CssString with .else.if.else when the condition is false', () => { | |||
const c = css.if('a'.toUpperCase() === 'C')( | |||
it('should return CssString with .else when the if condition is true', () => { | |||
const c = css.if(true)( | |||
css` | |||
background-color: white; | |||
` | |||
).else.if('b'.toUpperCase() === 'C')( | |||
css` | |||
background-color: black; | |||
` | |||
).else( | |||
css` | |||
background-color: gray; | |||
background-color: black; | |||
` | |||
) | |||
expect(c).toBeInstanceOf(CssStringImpl) | |||
expect(c.toString()).toBe('background-color:gray;') | |||
expect(c.toString()).toBe('background-color:white;') | |||
}) | |||
}) | |||
@@ -118,7 +103,35 @@ describe('css-utils', () => { | |||
` | |||
) | |||
expect(c.toString()).toBe('@media only screen and (min-width: 720px){color:black; background-color:white;}') | |||
expect(c.toString()).toBe('@media only screen and (min-width: 720px){color:black;background-color:white;}') | |||
}) | |||
}) | |||
describe('css.cx', () => { | |||
it('should accept strings as classnames', () => { | |||
expect(css.cx('class1', 'class2')).toBe('class1 class2'); | |||
}) | |||
it('should accept CSS strings for classname generation', () => { | |||
expect( | |||
css.cx( | |||
css` | |||
color: white; | |||
` | |||
) | |||
).toBe('gooberClass'); | |||
}) | |||
it('should accept mixed values', () => { | |||
expect( | |||
css.cx( | |||
'class1', | |||
'class2', | |||
css` | |||
color: white; | |||
` | |||
) | |||
).toBe('class1 class2 gooberClass'); | |||
}) | |||
}) | |||
}) |
@@ -1,18 +1,198 @@ | |||
import * as React from 'react'; | |||
import { | |||
render, | |||
screen | |||
screen, | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import userEvent from '@testing-library/user-event'; | |||
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | |||
import { | |||
MaskedTextInput | |||
} from '.'; | |||
jest.mock('@tesseract-design/web-base-textcontrol'); | |||
describe('MaskedTextInput', () => { | |||
it('should render an input', () => { | |||
render(<MaskedTextInput />); | |||
render( | |||
<MaskedTextInput /> | |||
); | |||
const textbox: HTMLInputElement = screen.getByTestId('input'); | |||
expect(textbox).toBeInTheDocument(); | |||
expect(textbox.type).toBe('password'); | |||
expect(textbox).toHaveProperty('type', 'password'); | |||
}); | |||
it('should render a border', () => { | |||
render( | |||
<MaskedTextInput | |||
border | |||
/> | |||
); | |||
const border = screen.getByTestId('border'); | |||
expect(border).toBeInTheDocument(); | |||
}); | |||
it('should render a label', () => { | |||
render( | |||
<MaskedTextInput | |||
label="foo" | |||
/> | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.getByTestId('label'); | |||
expect(label).toHaveTextContent('foo'); | |||
}); | |||
it('should render a hidden label', () => { | |||
render( | |||
<MaskedTextInput | |||
label="foo" | |||
hiddenLabel | |||
/> | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.queryByTestId('label'); | |||
expect(label).toBeNull(); | |||
}); | |||
it('should render a hint', () => { | |||
render( | |||
<MaskedTextInput | |||
hint="foo" | |||
/> | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toBeInTheDocument(); | |||
}); | |||
it('should render an indicator', () => { | |||
render( | |||
<MaskedTextInput | |||
indicator={ | |||
<div data-testid="indicator" /> | |||
} | |||
/> | |||
); | |||
const indicator = screen.getByTestId('indicator'); | |||
expect(indicator).toBeInTheDocument(); | |||
}); | |||
describe.each([ | |||
TextControlBase.TextControlSize.SMALL, | |||
TextControlBase.TextControlSize.MEDIUM, | |||
TextControlBase.TextControlSize.LARGE, | |||
])('on %s size', (size) => { | |||
it('should render input styles', () => { | |||
render( | |||
<MaskedTextInput | |||
size={size} | |||
/> | |||
); | |||
expect(TextControlBase.Input).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render hint styles', () => { | |||
render( | |||
<MaskedTextInput | |||
size={size} | |||
hint="hint" | |||
/> | |||
); | |||
expect(TextControlBase.HintWrapper).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render indicator styles', () => { | |||
render( | |||
<MaskedTextInput | |||
size={size} | |||
indicator={ | |||
<div data-testid="indicator" /> | |||
} | |||
/> | |||
); | |||
expect(TextControlBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
}); | |||
it('should render a block textbox', () => { | |||
render( | |||
<MaskedTextInput | |||
block | |||
/> | |||
); | |||
expect(TextControlBase.Root).toBeCalledWith(expect.objectContaining({ | |||
block: true, | |||
})); | |||
}); | |||
describe.each([ | |||
TextControlBase.TextControlStyle.DEFAULT, | |||
TextControlBase.TextControlStyle.ALTERNATE, | |||
])('on %s style', (style) => { | |||
it('should render input styles', () => { | |||
render( | |||
<MaskedTextInput | |||
style={style} | |||
/> | |||
); | |||
expect(TextControlBase.Input).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
it('should render hint styles', () => { | |||
render( | |||
<MaskedTextInput | |||
style={style} | |||
hint="hint" | |||
/> | |||
); | |||
expect(TextControlBase.HintWrapper).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
it('should render indicator styles', () => { | |||
render( | |||
<MaskedTextInput | |||
style={style} | |||
indicator={ | |||
<div | |||
data-testid="indicator" | |||
/> | |||
} | |||
/> | |||
); | |||
expect(TextControlBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
}); | |||
it('should handle change events', () => { | |||
const onChange = jest.fn(); | |||
render( | |||
<MaskedTextInput | |||
onChange={onChange} | |||
/> | |||
); | |||
const textbox: HTMLInputElement = screen.getByTestId('input'); | |||
userEvent.type(textbox, 'foobar'); | |||
expect(onChange).toBeCalled(); | |||
}); | |||
}); |
@@ -71,7 +71,7 @@ export const MaskedTextInput = React.forwardRef<HTMLInputElement, MaskedTextInpu | |||
return ( | |||
<div | |||
className={TextControlBase.ComponentBase(textInputBaseArgs)} | |||
className={TextControlBase.Root(textInputBaseArgs)} | |||
> | |||
<input | |||
{...etcProps} | |||
@@ -81,10 +81,17 @@ export const MaskedTextInput = React.forwardRef<HTMLInputElement, MaskedTextInpu | |||
type="password" | |||
data-testid="input" | |||
/> | |||
{border && <span />} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
/> | |||
) | |||
} | |||
{ | |||
label && !hiddenLabel && ( | |||
<div | |||
data-testid="label" | |||
className={TextControlBase.LabelWrapper(textInputBaseArgs)} | |||
> | |||
{label} | |||
@@ -94,6 +101,7 @@ export const MaskedTextInput = React.forwardRef<HTMLInputElement, MaskedTextInpu | |||
{hint && ( | |||
<div | |||
className={TextControlBase.HintWrapper(textInputBaseArgs)} | |||
data-testid="hint" | |||
> | |||
<div | |||
className={TextControlBase.Hint()} | |||
@@ -4,14 +4,192 @@ import { | |||
screen | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import userEvent from '@testing-library/user-event'; | |||
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | |||
import { | |||
MultilineTextInput | |||
} from '.'; | |||
jest.mock('@tesseract-design/web-base-textcontrol'); | |||
describe('MultilineTextInput', () => { | |||
it('should render a textbox', () => { | |||
render(<MultilineTextInput />); | |||
const textbox = screen.getByRole('textbox'); | |||
const textbox: HTMLTextAreaElement = screen.getByRole('textbox'); | |||
expect(textbox).toBeInTheDocument(); | |||
}); | |||
it('should render a border', () => { | |||
render( | |||
<MultilineTextInput | |||
border | |||
/> | |||
); | |||
const border = screen.getByTestId('border'); | |||
expect(border).toBeInTheDocument(); | |||
}); | |||
it('should render a label', () => { | |||
render( | |||
<MultilineTextInput | |||
label="foo" | |||
/> | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.getByTestId('label'); | |||
expect(label).toHaveTextContent('foo'); | |||
}); | |||
it('should render a hidden label', () => { | |||
render( | |||
<MultilineTextInput | |||
label="foo" | |||
hiddenLabel | |||
/> | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.queryByTestId('label'); | |||
expect(label).toBeNull(); | |||
}); | |||
it('should render a hint', () => { | |||
render( | |||
<MultilineTextInput | |||
hint="foo" | |||
/> | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toBeInTheDocument(); | |||
}); | |||
it('should render an indicator', () => { | |||
render( | |||
<MultilineTextInput | |||
indicator={ | |||
<div data-testid="indicator" /> | |||
} | |||
/> | |||
); | |||
const indicator = screen.getByTestId('indicator'); | |||
expect(indicator).toBeInTheDocument(); | |||
}); | |||
describe.each([ | |||
TextControlBase.TextControlSize.SMALL, | |||
TextControlBase.TextControlSize.MEDIUM, | |||
TextControlBase.TextControlSize.LARGE, | |||
])('on %s size', (size) => { | |||
it('should render input styles', () => { | |||
render( | |||
<MultilineTextInput | |||
size={size} | |||
/> | |||
); | |||
expect(TextControlBase.Input).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render hint styles', () => { | |||
render( | |||
<MultilineTextInput | |||
size={size} | |||
hint="hint" | |||
/> | |||
); | |||
expect(TextControlBase.HintWrapper).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render indicator styles', () => { | |||
render( | |||
<MultilineTextInput | |||
size={size} | |||
indicator={ | |||
<div data-testid="indicator" /> | |||
} | |||
/> | |||
); | |||
expect(TextControlBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
}); | |||
it('should render a block textbox', () => { | |||
render( | |||
<MultilineTextInput | |||
block | |||
/> | |||
); | |||
expect(TextControlBase.Root).toBeCalledWith(expect.objectContaining({ | |||
block: true, | |||
})); | |||
}); | |||
describe.each([ | |||
TextControlBase.TextControlStyle.DEFAULT, | |||
TextControlBase.TextControlStyle.ALTERNATE, | |||
])('on %s style', (style) => { | |||
it('should render input styles', () => { | |||
render( | |||
<MultilineTextInput | |||
style={style} | |||
/> | |||
); | |||
expect(TextControlBase.Input).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
it('should render hint styles', () => { | |||
render( | |||
<MultilineTextInput | |||
style={style} | |||
hint="hint" | |||
/> | |||
); | |||
expect(TextControlBase.HintWrapper).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
it('should render indicator styles', () => { | |||
render( | |||
<MultilineTextInput | |||
style={style} | |||
indicator={ | |||
<div | |||
data-testid="indicator" | |||
/> | |||
} | |||
/> | |||
); | |||
expect(TextControlBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
}); | |||
it('should handle change events', () => { | |||
const onChange = jest.fn(); | |||
render( | |||
<MultilineTextInput | |||
onChange={onChange} | |||
/> | |||
); | |||
const textbox: HTMLTextAreaElement = screen.getByRole('textbox'); | |||
userEvent.type(textbox, 'foobar'); | |||
expect(onChange).toBeCalled(); | |||
}); | |||
}); |
@@ -71,7 +71,7 @@ export const MultilineTextInput = React.forwardRef<HTMLTextAreaElement, Multilin | |||
return ( | |||
<div | |||
className={TextControlBase.ComponentBase(textInputBaseArgs)} | |||
className={TextControlBase.Root(textInputBaseArgs)} | |||
> | |||
<textarea | |||
{...etcProps} | |||
@@ -81,11 +81,19 @@ export const MultilineTextInput = React.forwardRef<HTMLTextAreaElement, Multilin | |||
style={{ | |||
height: TextControlBase.MIN_HEIGHTS[size], | |||
}} | |||
data-testid="input" | |||
/> | |||
{border && <span />} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
/> | |||
) | |||
} | |||
{ | |||
label && !hiddenLabel && ( | |||
<div | |||
data-testid="label" | |||
className={TextControlBase.LabelWrapper(textInputBaseArgs)} | |||
> | |||
{label} | |||
@@ -95,6 +103,7 @@ export const MultilineTextInput = React.forwardRef<HTMLTextAreaElement, Multilin | |||
{hint && ( | |||
<div | |||
className={TextControlBase.HintWrapper(textInputBaseArgs)} | |||
data-testid="hint" | |||
> | |||
<div | |||
className={TextControlBase.Hint()} | |||
@@ -4,14 +4,208 @@ import { | |||
screen | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import userEvent from '@testing-library/user-event'; | |||
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | |||
import { | |||
TextInput | |||
TextInput, TextInputType, | |||
} from '.'; | |||
jest.mock('@tesseract-design/web-base-textcontrol'); | |||
describe('TextInput', () => { | |||
it('should render a textbox', () => { | |||
render(<TextInput />); | |||
render( | |||
<TextInput /> | |||
); | |||
const textbox = screen.getByRole('textbox'); | |||
expect(textbox).toBeInTheDocument(); | |||
expect(textbox).toHaveProperty('type', 'text'); | |||
}); | |||
it('should render a border', () => { | |||
render( | |||
<TextInput | |||
border | |||
/> | |||
); | |||
const border = screen.getByTestId('border'); | |||
expect(border).toBeInTheDocument(); | |||
}); | |||
it('should render a label', () => { | |||
render( | |||
<TextInput | |||
label="foo" | |||
/> | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.getByTestId('label'); | |||
expect(label).toHaveTextContent('foo'); | |||
}); | |||
it('should render a hidden label', () => { | |||
render( | |||
<TextInput | |||
label="foo" | |||
hiddenLabel | |||
/> | |||
); | |||
const textbox = screen.getByLabelText('foo'); | |||
expect(textbox).toBeInTheDocument(); | |||
const label = screen.queryByTestId('label'); | |||
expect(label).toBeNull(); | |||
}); | |||
it('should render a hint', () => { | |||
render( | |||
<TextInput | |||
hint="foo" | |||
/> | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toBeInTheDocument(); | |||
}); | |||
it('should render an indicator', () => { | |||
render( | |||
<TextInput | |||
indicator={ | |||
<div data-testid="indicator" /> | |||
} | |||
/> | |||
); | |||
const indicator = screen.getByTestId('indicator'); | |||
expect(indicator).toBeInTheDocument(); | |||
}); | |||
describe.each([ | |||
TextControlBase.TextControlSize.SMALL, | |||
TextControlBase.TextControlSize.MEDIUM, | |||
TextControlBase.TextControlSize.LARGE, | |||
])('on %s size', (size) => { | |||
it('should render input styles', () => { | |||
render( | |||
<TextInput | |||
size={size} | |||
/> | |||
); | |||
expect(TextControlBase.Input).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render hint styles', () => { | |||
render( | |||
<TextInput | |||
size={size} | |||
hint="hint" | |||
/> | |||
); | |||
expect(TextControlBase.HintWrapper).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render indicator styles', () => { | |||
render( | |||
<TextInput | |||
size={size} | |||
indicator={ | |||
<div data-testid="indicator" /> | |||
} | |||
/> | |||
); | |||
expect(TextControlBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
}); | |||
it('should render a block textbox', () => { | |||
render( | |||
<TextInput | |||
block | |||
/> | |||
); | |||
expect(TextControlBase.Root).toBeCalledWith(expect.objectContaining({ | |||
block: true, | |||
})); | |||
}); | |||
it.each([ | |||
TextInputType.TEXT, | |||
TextInputType.SEARCH, | |||
])('should render a textbox with type %s', (buttonType) => { | |||
render( | |||
<TextInput | |||
type={buttonType} | |||
/> | |||
); | |||
const textbox: HTMLButtonElement = screen.getByTestId('input'); | |||
expect(textbox).toHaveProperty('type', buttonType); | |||
}); | |||
describe.each([ | |||
TextControlBase.TextControlStyle.DEFAULT, | |||
TextControlBase.TextControlStyle.ALTERNATE, | |||
])('on %s style', (style) => { | |||
it('should render input styles', () => { | |||
render( | |||
<TextInput | |||
style={style} | |||
/> | |||
); | |||
expect(TextControlBase.Input).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
it('should render hint styles', () => { | |||
render( | |||
<TextInput | |||
style={style} | |||
hint="hint" | |||
/> | |||
); | |||
expect(TextControlBase.HintWrapper).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
it('should render indicator styles', () => { | |||
render( | |||
<TextInput | |||
style={style} | |||
indicator={ | |||
<div | |||
data-testid="indicator" | |||
/> | |||
} | |||
/> | |||
); | |||
expect(TextControlBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
}); | |||
it('should handle change events', () => { | |||
const onChange = jest.fn(); | |||
render( | |||
<TextInput | |||
onChange={onChange} | |||
/> | |||
); | |||
const textbox: HTMLInputElement = screen.getByRole('textbox'); | |||
userEvent.type(textbox, 'foobar'); | |||
expect(onChange).toBeCalled(); | |||
}); | |||
}); |
@@ -81,7 +81,7 @@ export const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>( | |||
return ( | |||
<div | |||
className={TextControlBase.ComponentBase(textInputBaseArgs)} | |||
className={TextControlBase.Root(textInputBaseArgs)} | |||
> | |||
<input | |||
{...etcProps} | |||
@@ -89,11 +89,19 @@ export const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>( | |||
ref={ref} | |||
aria-label={label} | |||
type={type} | |||
data-testid="input" | |||
/> | |||
{border && <span />} | |||
{ | |||
border && ( | |||
<span | |||
data-testid="border" | |||
/> | |||
) | |||
} | |||
{ | |||
label && !hiddenLabel && ( | |||
<div | |||
data-testid="label" | |||
className={TextControlBase.LabelWrapper(textInputBaseArgs)} | |||
> | |||
{label} | |||
@@ -103,6 +111,7 @@ export const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>( | |||
{hint && ( | |||
<div | |||
className={TextControlBase.HintWrapper(textInputBaseArgs)} | |||
data-testid="hint" | |||
> | |||
<div | |||
className={TextControlBase.Hint()} | |||
@@ -0,0 +1,11 @@ | |||
import * as WebFreeformReact from '.'; | |||
describe('web-freeform-react', () => { | |||
it.each([ | |||
'MaskedTextInput', | |||
'MultilineTextInput', | |||
'TextInput', | |||
])('should export %s', (namedExport) => { | |||
expect(WebFreeformReact).toHaveProperty(namedExport); | |||
}); | |||
}); |
@@ -1,66 +0,0 @@ | |||
import * as React from 'react'; | |||
import { | |||
css, | |||
} from 'goober'; | |||
const BadgeBase = ({ rounded }: { rounded: boolean }) => css` | |||
position: relative; | |||
height: 1.5em; | |||
min-width: 1.5em; | |||
display: inline-grid; | |||
vertical-align: middle; | |||
place-content: center; | |||
border-radius: ${rounded ? '0.75em' : '0.25rem'}; | |||
overflow: hidden; | |||
font-stretch: var(--font-stretch-base, normal); | |||
padding: 0 0.25rem; | |||
box-sizing: border-box; | |||
&::before { | |||
position: absolute; | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
height: 100%; | |||
background-color: currentColor; | |||
opacity: 0.25; | |||
content: ''; | |||
} | |||
`; | |||
const Content = () => css` | |||
position: relative; | |||
font-size: 0.75em; | |||
`; | |||
export type BadgeProps = React.HTMLProps<HTMLSpanElement> & { | |||
rounded?: boolean, | |||
}; | |||
export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>( | |||
( | |||
{ | |||
children, | |||
rounded = false, | |||
}, | |||
ref, | |||
) => { | |||
const badgeStyleProps = React.useMemo(() => ({ | |||
rounded, | |||
}), [rounded]); | |||
return ( | |||
<strong | |||
ref={ref} | |||
className={BadgeBase(badgeStyleProps)} | |||
> | |||
<span | |||
className={Content()} | |||
> | |||
{children} | |||
</span> | |||
</strong> | |||
) | |||
} | |||
) | |||
Badge.displayName = 'Badge'; |
@@ -0,0 +1,31 @@ | |||
import * as React from 'react'; | |||
import { | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import { | |||
Badge, | |||
} from '.'; | |||
jest.mock('@tesseract-design/web-base-badge'); | |||
describe('Badge', () => { | |||
it('should render a badge', () => { | |||
render( | |||
<Badge /> | |||
); | |||
const button: HTMLButtonElement = screen.getByTestId('badge'); | |||
expect(button).toBeInTheDocument(); | |||
}); | |||
it('should render a rounded badge', () => { | |||
render( | |||
<Badge | |||
rounded | |||
/> | |||
); | |||
const button: HTMLButtonElement = screen.getByTestId('badge'); | |||
expect(button).toBeInTheDocument(); | |||
}); | |||
}); |
@@ -0,0 +1,36 @@ | |||
import * as React from 'react'; | |||
import * as BadgeBase from '@tesseract-design/web-base-badge'; | |||
export type BadgeProps = React.HTMLProps<HTMLSpanElement> & { | |||
rounded?: boolean, | |||
}; | |||
export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>( | |||
( | |||
{ | |||
children, | |||
rounded = false, | |||
}, | |||
ref, | |||
) => { | |||
const badgeStyleProps = React.useMemo<BadgeBase.BadgeBaseArgs>(() => ({ | |||
rounded, | |||
}), [rounded]); | |||
return ( | |||
<strong | |||
ref={ref} | |||
className={BadgeBase.Root(badgeStyleProps)} | |||
data-testid="badge" | |||
> | |||
<span | |||
className={BadgeBase.Content()} | |||
> | |||
{children} | |||
</span> | |||
</strong> | |||
) | |||
} | |||
) | |||
Badge.displayName = 'Badge'; |
@@ -0,0 +1,9 @@ | |||
import * as WebInformationReact from '.'; | |||
describe('web-information-react', () => { | |||
it.each([ | |||
'Badge', | |||
])('should export %s', (namedExport) => { | |||
expect(WebInformationReact).toHaveProperty(namedExport); | |||
}); | |||
}); |
@@ -0,0 +1,189 @@ | |||
import * as React from 'react'; | |||
import { | |||
render, | |||
screen, | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import { | |||
LinkButton, | |||
} from '.'; | |||
import userEvent from '@testing-library/user-event'; | |||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||
jest.mock('@tesseract-design/web-base-button'); | |||
describe('LinkButton', () => { | |||
it('should render a link', () => { | |||
render( | |||
<LinkButton | |||
href="http://example.com" | |||
/> | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('link'); | |||
expect(button).toBeInTheDocument(); | |||
}); | |||
it('should render a subtext', () => { | |||
render( | |||
<LinkButton | |||
subtext="subtext" | |||
/> | |||
); | |||
const subtext: HTMLElement = screen.getByTestId('subtext'); | |||
expect(subtext).toBeInTheDocument(); | |||
}); | |||
it('should render a badge', () => { | |||
render( | |||
<LinkButton | |||
badge="badge" | |||
/> | |||
); | |||
const badge: HTMLElement = screen.getByTestId('badge'); | |||
expect(badge).toBeInTheDocument(); | |||
}); | |||
it('should render as a menu item', () => { | |||
render( | |||
<LinkButton | |||
menuItem | |||
/> | |||
); | |||
const menuItemIndicator: HTMLElement = screen.getByTestId('menuItemIndicator'); | |||
expect(menuItemIndicator).toBeInTheDocument(); | |||
}); | |||
it('should handle click events', () => { | |||
const onClick = jest.fn(); | |||
render( | |||
<LinkButton | |||
href="http://example.com" | |||
onClick={onClick} | |||
/> | |||
); | |||
const button: HTMLButtonElement = screen.getByRole('link'); | |||
userEvent.click(button); | |||
expect(onClick).toBeCalled(); | |||
}); | |||
it('should render a compact button', () => { | |||
render( | |||
<LinkButton | |||
compact | |||
/> | |||
); | |||
expect(ButtonBase.Button).toBeCalledWith(expect.objectContaining({ | |||
compact: true, | |||
})); | |||
expect(ButtonBase.Label).toBeCalledWith(expect.objectContaining({ | |||
compact: true, | |||
})); | |||
}); | |||
describe.each([ | |||
ButtonBase.ButtonSize.SMALL, | |||
ButtonBase.ButtonSize.MEDIUM, | |||
ButtonBase.ButtonSize.LARGE, | |||
])('on %s size', (size) => { | |||
it('should render button styles', () => { | |||
render( | |||
<LinkButton | |||
size={size} | |||
/> | |||
); | |||
expect(ButtonBase.Button).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render badge styles', () => { | |||
render( | |||
<LinkButton | |||
size={size} | |||
badge="badge" | |||
/> | |||
); | |||
expect(ButtonBase.BadgeContainer).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render indicator styles', () => { | |||
render( | |||
<LinkButton | |||
size={size} | |||
menuItem | |||
/> | |||
); | |||
expect(ButtonBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
}); | |||
it.each([ | |||
ButtonBase.ButtonVariant.OUTLINE, | |||
ButtonBase.ButtonVariant.FILLED, | |||
])('should render a button with variant %s', (variant) => { | |||
render( | |||
<LinkButton | |||
variant={variant} | |||
/> | |||
); | |||
expect(ButtonBase.Button).toBeCalledWith(expect.objectContaining({ | |||
variant, | |||
})); | |||
}); | |||
it('should render a bordered button', () => { | |||
render( | |||
<LinkButton | |||
border | |||
/> | |||
); | |||
expect(ButtonBase.Border).toBeCalledWith(expect.objectContaining({ | |||
border: true, | |||
})); | |||
}); | |||
it('should render a block button', () => { | |||
render( | |||
<LinkButton | |||
block | |||
/> | |||
); | |||
expect(ButtonBase.Border).toBeCalledWith(expect.objectContaining({ | |||
block: true, | |||
})); | |||
}); | |||
it('should render children', () => { | |||
render( | |||
<LinkButton> | |||
Foo | |||
</LinkButton> | |||
); | |||
const children: HTMLElement = screen.getByTestId('children'); | |||
expect(children).toHaveTextContent('Foo'); | |||
}); | |||
it('should render a disabled link', () => { | |||
render( | |||
<LinkButton | |||
href="http://example.com" | |||
disabled | |||
/> | |||
); | |||
const button = screen.queryByRole('link'); | |||
expect(button).toBeNull(); | |||
}); | |||
}); |
@@ -20,14 +20,6 @@ export type LinkButtonProps = Omit<React.HTMLProps<LinkButtonElement>, 'size' | | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Can the component be activated? | |||
*/ | |||
disabled?: boolean, | |||
/** | |||
* Style of the component. | |||
*/ | |||
style?: ButtonBase.ButtonStyle, | |||
/** | |||
* Does the component need to conserve space? | |||
*/ | |||
@@ -60,7 +52,6 @@ export const LinkButton = React.forwardRef<LinkButtonElement, LinkButtonProps>( | |||
children, | |||
block = false, | |||
disabled = false, | |||
style = ButtonBase.ButtonStyle.DEFAULT, | |||
onClick, | |||
href, | |||
target, | |||
@@ -81,10 +72,9 @@ export const LinkButton = React.forwardRef<LinkButtonElement, LinkButtonProps>( | |||
variant, | |||
border, | |||
disabled, | |||
style, | |||
compact, | |||
menuItem, | |||
}), [size, block, variant, border, disabled, style, compact, menuItem]); | |||
}), [size, block, variant, border, disabled, compact, menuItem]); | |||
const commonChildren = ( | |||
<> | |||
@@ -96,6 +86,7 @@ export const LinkButton = React.forwardRef<LinkButtonElement, LinkButtonProps>( | |||
> | |||
<span | |||
className={ButtonBase.MainText()} | |||
data-testid="children" | |||
> | |||
<span | |||
className={ButtonBase.OverflowText()} | |||
@@ -110,6 +101,7 @@ export const LinkButton = React.forwardRef<LinkButtonElement, LinkButtonProps>( | |||
{' '} | |||
<span | |||
className={ButtonBase.Subtext()} | |||
data-testid="subtext" | |||
> | |||
<span | |||
className={ButtonBase.OverflowText()} | |||
@@ -128,6 +120,7 @@ export const LinkButton = React.forwardRef<LinkButtonElement, LinkButtonProps>( | |||
{' '} | |||
<span | |||
className={ButtonBase.BadgeContainer(styleProps)} | |||
data-testid="badge" | |||
> | |||
{badge} | |||
</span> | |||
@@ -141,6 +134,7 @@ export const LinkButton = React.forwardRef<LinkButtonElement, LinkButtonProps>( | |||
{' '} | |||
<span | |||
className={ButtonBase.IndicatorWrapper(styleProps)} | |||
data-testid="menuItemIndicator" | |||
> | |||
<svg | |||
className={ButtonBase.Indicator()} | |||
@@ -0,0 +1,9 @@ | |||
import * as WebNavigationReact from '.'; | |||
describe('web-navigation-react', () => { | |||
it.each([ | |||
'LinkButton', | |||
])('should export %s', (namedExport) => { | |||
expect(WebNavigationReact).toHaveProperty(namedExport); | |||
}); | |||
}); |
@@ -1,277 +0,0 @@ | |||
import * as React from 'react'; | |||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||
export type CheckboxProps = Omit<React.HTMLProps<HTMLInputElement>, 'size' | 'type' | 'style'> & { | |||
/** | |||
* Size of the component. | |||
*/ | |||
size?: ButtonBase.ButtonSize, | |||
/** | |||
* Variant of the component. | |||
*/ | |||
variant?: ButtonBase.ButtonVariant, | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Style of the component. | |||
*/ | |||
style?: ButtonBase.ButtonStyle, | |||
/** | |||
* Does the component need to conserve space? | |||
*/ | |||
compact?: boolean, | |||
/** | |||
* Complementary content of the component. | |||
*/ | |||
subtext?: React.ReactNode, | |||
/** | |||
* Short complementary content displayed at the edge of the component. | |||
*/ | |||
badge?: React.ReactNode, | |||
/** | |||
* Appearance of the component. | |||
*/ | |||
appearance?: CheckControlBase.CheckControlAppearance, | |||
/** | |||
* Does the component have indeterminate check state? | |||
*/ | |||
indeterminate?: boolean, | |||
} | |||
/** | |||
* Component for performing an action upon activation (e.g. when clicked). | |||
* | |||
* This component functions as a regular button. | |||
*/ | |||
export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>( | |||
( | |||
{ | |||
size = ButtonBase.ButtonSize.MEDIUM, | |||
variant = ButtonBase.ButtonVariant.OUTLINE, | |||
border = false, | |||
children, | |||
block = false, | |||
style = ButtonBase.ButtonStyle.DEFAULT, | |||
disabled = false, | |||
compact = false, | |||
subtext, | |||
badge, | |||
appearance = CheckControlBase.CheckControlAppearance.DEFAULT, | |||
indeterminate = false, | |||
className: _className, | |||
as: _as, | |||
...etcProps | |||
}: CheckboxProps, | |||
ref, | |||
) => { | |||
const styleProps = React.useMemo<ButtonBase.ButtonBaseArgs & CheckControlBase.CheckControlBaseArgs>(() => ({ | |||
size, | |||
block, | |||
variant, | |||
border, | |||
style, | |||
compact, | |||
menuItem: false, | |||
disabled, | |||
appearance, | |||
type: 'checkbox', | |||
}), [size, block, variant, border, style, compact, disabled, appearance]); | |||
const defaultRef = React.useRef<HTMLInputElement>(null); | |||
const theRef = (ref ?? defaultRef) as React.MutableRefObject<HTMLInputElement>; | |||
React.useEffect(() => { | |||
if (!(indeterminate && theRef.current)) { | |||
return; | |||
} | |||
theRef.current.indeterminate = indeterminate; | |||
}, [theRef, indeterminate]); | |||
const checkIndicatorCommon = ( | |||
<span | |||
className={CheckControlBase.CheckIndicatorArea(styleProps)} | |||
> | |||
<span | |||
className={CheckControlBase.CheckIndicatorWrapper(styleProps)} | |||
> | |||
<svg | |||
className={CheckControlBase.CheckIndicator(styleProps)} | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
<polyline | |||
points="20 6 9 17 4 12" | |||
/> | |||
</svg> | |||
<svg | |||
className={CheckControlBase.CheckIndicator(styleProps)} | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
<polyline | |||
points="20 12 4 12" | |||
/> | |||
</svg> | |||
</span> | |||
</span> | |||
) | |||
return ( | |||
<div | |||
className={CheckControlBase.ClickAreaWrapper(styleProps)} | |||
> | |||
<label | |||
className={CheckControlBase.ClickArea()} | |||
> | |||
<input | |||
{...etcProps} | |||
disabled={disabled} | |||
type="checkbox" | |||
ref={theRef} | |||
className={CheckControlBase.CheckStateContainer(styleProps)} | |||
/> | |||
{ | |||
appearance === CheckControlBase.CheckControlAppearance.DEFAULT | |||
&& ( | |||
<span> | |||
<span /> | |||
{checkIndicatorCommon} | |||
<span> | |||
{children} | |||
{ | |||
subtext | |||
&& ( | |||
<> | |||
<br /> | |||
<span | |||
className={CheckControlBase.Subtext()} | |||
> | |||
{subtext} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
{ | |||
badge | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={ButtonBase.BadgeContainer(styleProps)} | |||
> | |||
{badge} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
) | |||
} | |||
{ | |||
appearance === CheckControlBase.CheckControlAppearance.BUTTON | |||
&& ( | |||
<span | |||
className={ButtonBase.Button(styleProps)} | |||
> | |||
<span | |||
className={ButtonBase.Border(styleProps)} | |||
/> | |||
{checkIndicatorCommon} | |||
<span | |||
className={ButtonBase.Label(styleProps)} | |||
> | |||
<span | |||
className={ButtonBase.MainText()} | |||
> | |||
<span | |||
className={ButtonBase.OverflowText()} | |||
> | |||
{children} | |||
</span> | |||
</span> | |||
{ | |||
subtext | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={ButtonBase.Subtext()} | |||
> | |||
<span | |||
className={ButtonBase.OverflowText()} | |||
> | |||
{subtext} | |||
</span> | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
{ | |||
badge | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={ButtonBase.BadgeContainer(styleProps)} | |||
> | |||
{badge} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
) | |||
} | |||
{ | |||
appearance === CheckControlBase.CheckControlAppearance.SWITCH | |||
&& ( | |||
<span> | |||
<span /> | |||
{checkIndicatorCommon} | |||
<span> | |||
{children} | |||
{ | |||
subtext | |||
&& ( | |||
<> | |||
<br /> | |||
<span | |||
className={CheckControlBase.Subtext()} | |||
> | |||
{subtext} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
{ | |||
badge | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={ButtonBase.BadgeContainer(styleProps)} | |||
> | |||
{badge} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
) | |||
} | |||
</label> | |||
</div> | |||
); | |||
}, | |||
); | |||
Checkbox.displayName = 'ActionButton'; |
@@ -4,14 +4,279 @@ import { | |||
screen | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | |||
import { | |||
DropdownSelect | |||
} from '.'; | |||
jest.mock('@tesseract-design/web-base-textcontrol'); | |||
describe('DropdownSelect', () => { | |||
it('should render a combobox', () => { | |||
render(<DropdownSelect />); | |||
const combobox = screen.getByRole('combobox'); | |||
expect(combobox).toBeInTheDocument(); | |||
}); | |||
it('should render a border', () => { | |||
render( | |||
<DropdownSelect | |||
border | |||
/> | |||
); | |||
const border = screen.getByTestId('border'); | |||
expect(border).toBeInTheDocument(); | |||
}); | |||
it('should render a label', () => { | |||
render( | |||
<DropdownSelect | |||
label="foo" | |||
/> | |||
); | |||
const combobox = screen.getByLabelText('foo'); | |||
expect(combobox).toBeInTheDocument(); | |||
const label = screen.getByTestId('label'); | |||
expect(label).toHaveTextContent('foo'); | |||
}); | |||
it('should render a hidden label', () => { | |||
render( | |||
<DropdownSelect | |||
label="foo" | |||
hiddenLabel | |||
/> | |||
); | |||
const combobox = screen.getByLabelText('foo'); | |||
expect(combobox).toBeInTheDocument(); | |||
const label = screen.queryByTestId('label'); | |||
expect(label).toBeNull(); | |||
}); | |||
it('should render a hint', () => { | |||
render( | |||
<DropdownSelect | |||
hint="foo" | |||
/> | |||
); | |||
const hint = screen.getByTestId('hint'); | |||
expect(hint).toBeInTheDocument(); | |||
}); | |||
it('should not render invalid options', () => { | |||
render( | |||
<DropdownSelect | |||
options={[ | |||
{ | |||
label: 'foo', | |||
}, | |||
{ | |||
label: 'bar', | |||
} | |||
]} | |||
/> | |||
); | |||
const combobox = screen.getByRole('combobox'); | |||
expect(combobox.children).toHaveLength(0); | |||
}); | |||
it('should render valid options', () => { | |||
render( | |||
<DropdownSelect | |||
options={[ | |||
{ | |||
label: 'foo', | |||
value: 'foo', | |||
}, | |||
{ | |||
label: 'bar', | |||
value: 'bar', | |||
} | |||
]} | |||
/> | |||
); | |||
const combobox = screen.getByRole('combobox'); | |||
expect(combobox.children).toHaveLength(2); | |||
}); | |||
it('should render shallow option groups', () => { | |||
render( | |||
<DropdownSelect | |||
options={[ | |||
{ | |||
label: 'foo', | |||
children: [ | |||
{ | |||
label: 'baz', | |||
value: 'baz', | |||
}, | |||
], | |||
}, | |||
{ | |||
label: 'bar', | |||
children: [ | |||
{ | |||
label: 'quux', | |||
value: 'quux', | |||
}, | |||
{ | |||
label: 'quuux', | |||
value: 'quuux', | |||
}, | |||
], | |||
} | |||
]} | |||
/> | |||
); | |||
const combobox = screen.getByRole('combobox'); | |||
expect(combobox.children).toHaveLength(2); | |||
expect(combobox.children[0].children).toHaveLength(1); | |||
expect(combobox.children[1].children).toHaveLength(2); | |||
}); | |||
it('should render deep option groups', () => { | |||
render( | |||
<DropdownSelect | |||
options={[ | |||
{ | |||
label: 'foo', | |||
children: [ | |||
{ | |||
label: 'baz', | |||
children: [ | |||
{ | |||
label: 'quuuux', | |||
value: 'quuuux', | |||
}, | |||
{ | |||
label: 'quuuuux', | |||
value: 'quuuuux', | |||
}, | |||
{ | |||
label: 'quuuuuux', | |||
value: 'quuuuuux', | |||
}, | |||
], | |||
}, | |||
], | |||
}, | |||
{ | |||
label: 'bar', | |||
children: [ | |||
{ | |||
label: 'quux', | |||
value: 'quux', | |||
}, | |||
{ | |||
label: 'quuux', | |||
value: 'quuux', | |||
}, | |||
], | |||
} | |||
]} | |||
/> | |||
); | |||
const combobox = screen.getByRole('combobox'); | |||
expect(combobox.children).toHaveLength(2); | |||
expect(combobox.children[0].children).toHaveLength(4); | |||
expect(combobox.children[1].children).toHaveLength(2); | |||
}); | |||
describe.each([ | |||
TextControlBase.TextControlSize.SMALL, | |||
TextControlBase.TextControlSize.MEDIUM, | |||
TextControlBase.TextControlSize.LARGE, | |||
])('on %s size', (size) => { | |||
it('should render input styles', () => { | |||
render( | |||
<DropdownSelect | |||
size={size} | |||
/> | |||
); | |||
expect(TextControlBase.Input).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render hint styles', () => { | |||
render( | |||
<DropdownSelect | |||
size={size} | |||
hint="hint" | |||
/> | |||
); | |||
expect(TextControlBase.HintWrapper).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
it('should render indicator styles', () => { | |||
render( | |||
<DropdownSelect | |||
size={size} | |||
/> | |||
); | |||
expect(TextControlBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({ | |||
size, | |||
})); | |||
}); | |||
}); | |||
it('should render a block textbox', () => { | |||
render( | |||
<DropdownSelect | |||
block | |||
/> | |||
); | |||
expect(TextControlBase.Root).toBeCalledWith(expect.objectContaining({ | |||
block: true, | |||
})); | |||
}); | |||
describe.each([ | |||
TextControlBase.TextControlStyle.DEFAULT, | |||
TextControlBase.TextControlStyle.ALTERNATE, | |||
])('on %s style', (style) => { | |||
it('should render input styles', () => { | |||
render( | |||
<DropdownSelect | |||
style={style} | |||
/> | |||
); | |||
expect(TextControlBase.Input).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
it('should render hint styles', () => { | |||
render( | |||
<DropdownSelect | |||
style={style} | |||
hint="hint" | |||
/> | |||
); | |||
expect(TextControlBase.HintWrapper).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
it('should render indicator styles', () => { | |||
render( | |||
<DropdownSelect | |||
style={style} | |||
/> | |||
); | |||
expect(TextControlBase.IndicatorWrapper).toBeCalledWith(expect.objectContaining({ | |||
style, | |||
})); | |||
}); | |||
}); | |||
}); |
@@ -1,19 +1,6 @@ | |||
import * as React from 'react'; | |||
import { | |||
css | |||
} from 'goober'; | |||
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | |||
const Indicator = () => css` | |||
width: 1.5em; | |||
height: 1.5em; | |||
fill: none; | |||
stroke: currentColor; | |||
stroke-width: 2; | |||
stroke-linecap: round; | |||
stroke-linejoin: round; | |||
`; | |||
export interface SelectOption { | |||
label: string, | |||
value?: string | number | readonly string[] | |||
@@ -22,12 +9,8 @@ export interface SelectOption { | |||
type RenderOptionsProps = { | |||
options: SelectOption[], | |||
// FIXME bug in eslint does not play well with React.VFC | |||
// eslint-disable-next-line react/require-default-props | |||
optionComponent?: React.ElementType, | |||
// eslint-disable-next-line react/require-default-props | |||
optgroupComponent?: React.ElementType, | |||
// eslint-disable-next-line react/require-default-props | |||
level?: number, | |||
} | |||
@@ -62,6 +45,7 @@ const RenderOptions: React.VFC<RenderOptionsProps> = ({ | |||
options={o.children} | |||
optionComponent={Option} | |||
optgroupComponent={Optgroup} | |||
level={level + 1} | |||
/> | |||
</Optgroup> | |||
); | |||
@@ -79,6 +63,7 @@ const RenderOptions: React.VFC<RenderOptionsProps> = ({ | |||
options={o.children} | |||
optionComponent={Option} | |||
optgroupComponent={Optgroup} | |||
level={level + 1} | |||
/> | |||
</React.Fragment> | |||
); | |||
@@ -123,10 +108,6 @@ export type DropdownSelectProps = Omit<React.HTMLProps<HTMLSelectElement>, 'size | |||
* Options available for the component's values. | |||
*/ | |||
options?: SelectOption[], | |||
/** | |||
* Component for rendering options. | |||
*/ | |||
renderOptions?: React.ElementType, | |||
} | |||
/** | |||
@@ -149,7 +130,6 @@ export const DropdownSelect = React.forwardRef<HTMLSelectElement, DropdownSelect | |||
placeholder: _placeholder, | |||
as: _as, | |||
options = [], | |||
renderOptions: Render = RenderOptions, | |||
...etcProps | |||
}: DropdownSelectProps, | |||
ref, | |||
@@ -166,7 +146,7 @@ export const DropdownSelect = React.forwardRef<HTMLSelectElement, DropdownSelect | |||
return ( | |||
<div | |||
className={TextControlBase.ComponentBase(styleArgs)} | |||
className={TextControlBase.Root(styleArgs)} | |||
> | |||
<select | |||
{...etcProps} | |||
@@ -174,14 +154,19 @@ export const DropdownSelect = React.forwardRef<HTMLSelectElement, DropdownSelect | |||
ref={ref} | |||
aria-label={label} | |||
> | |||
<Render | |||
<RenderOptions | |||
options={options} | |||
/> | |||
</select> | |||
{border && <span />} | |||
{border && ( | |||
<span | |||
data-testid="border" | |||
/> | |||
)} | |||
{label && !hiddenLabel && ( | |||
<div | |||
className={TextControlBase.LabelWrapper(styleArgs)} | |||
data-testid="label" | |||
> | |||
{label} | |||
</div> | |||
@@ -189,6 +174,7 @@ export const DropdownSelect = React.forwardRef<HTMLSelectElement, DropdownSelect | |||
{hint && ( | |||
<div | |||
className={TextControlBase.HintWrapper(styleArgs)} | |||
data-testid="hint" | |||
> | |||
<div | |||
className={TextControlBase.Hint()} | |||
@@ -201,7 +187,7 @@ export const DropdownSelect = React.forwardRef<HTMLSelectElement, DropdownSelect | |||
className={TextControlBase.IndicatorWrapper(styleArgs)} | |||
> | |||
<svg | |||
className={Indicator()} | |||
className={TextControlBase.Indicator()} | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
@@ -0,0 +1,22 @@ | |||
import * as React from 'react'; | |||
import { | |||
render, | |||
screen | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||
import { RadioButton } from '.'; | |||
jest.mock('@tesseract-design/web-base-button'); | |||
jest.mock('@tesseract-design/web-base-checkcontrol'); | |||
describe('RadioButton', () => { | |||
it('should render a radio button', () => { | |||
render( | |||
<RadioButton /> | |||
); | |||
const checkbox = screen.getByRole('radio'); | |||
expect(checkbox).toBeInTheDocument(); | |||
}); | |||
}); |
@@ -19,10 +19,6 @@ export type RadioButtonProps = Omit<React.HTMLProps<HTMLInputElement>, 'size' | | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Style of the component. | |||
*/ | |||
style?: ButtonBase.ButtonStyle, | |||
/** | |||
* Does the component need to conserve space? | |||
*/ | |||
@@ -35,10 +31,6 @@ export type RadioButtonProps = Omit<React.HTMLProps<HTMLInputElement>, 'size' | | |||
* Short complementary content displayed at the edge of the component. | |||
*/ | |||
badge?: React.ReactNode, | |||
/** | |||
* Appearance of the component. | |||
*/ | |||
appearance?: CheckControlBase.CheckControlAppearance, | |||
} | |||
/** | |||
@@ -54,12 +46,10 @@ export const RadioButton = React.forwardRef<HTMLInputElement, RadioButtonProps>( | |||
border = false, | |||
children, | |||
block = false, | |||
style = ButtonBase.ButtonStyle.DEFAULT, | |||
disabled = false, | |||
compact = false, | |||
subtext, | |||
badge, | |||
appearance = CheckControlBase.CheckControlAppearance.DEFAULT, | |||
className: _className, | |||
as: _as, | |||
...etcProps | |||
@@ -71,23 +61,12 @@ export const RadioButton = React.forwardRef<HTMLInputElement, RadioButtonProps>( | |||
block, | |||
variant, | |||
border, | |||
style, | |||
compact, | |||
menuItem: false, | |||
disabled, | |||
appearance, | |||
appearance: CheckControlBase.CheckControlAppearance.BUTTON, | |||
type: 'radio', | |||
}), [size, block, variant, border, style, compact, disabled, appearance]); | |||
const checkIndicatorCommon = ( | |||
<span | |||
className={CheckControlBase.CheckIndicatorArea(styleProps)} | |||
> | |||
<span | |||
className={CheckControlBase.CheckIndicatorWrapper(styleProps)} | |||
/> | |||
</span> | |||
) | |||
}), [size, block, variant, border, compact, disabled]); | |||
return ( | |||
<div | |||
@@ -103,144 +82,63 @@ export const RadioButton = React.forwardRef<HTMLInputElement, RadioButtonProps>( | |||
ref={ref} | |||
className={CheckControlBase.CheckStateContainer(styleProps)} | |||
/> | |||
{ | |||
appearance === CheckControlBase.CheckControlAppearance.DEFAULT | |||
&& ( | |||
<span> | |||
<span /> | |||
{checkIndicatorCommon} | |||
<span> | |||
{children} | |||
{ | |||
subtext | |||
&& ( | |||
<> | |||
<br /> | |||
<span | |||
className={CheckControlBase.Subtext()} | |||
> | |||
{subtext} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
{ | |||
badge | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={ButtonBase.BadgeContainer(styleProps)} | |||
> | |||
{badge} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
) | |||
} | |||
{ | |||
appearance === CheckControlBase.CheckControlAppearance.BUTTON | |||
&& ( | |||
<span | |||
className={ButtonBase.Button(styleProps)} | |||
> | |||
<span | |||
className={ButtonBase.Border(styleProps)} | |||
/> | |||
<span | |||
className={CheckControlBase.CheckIndicatorArea(styleProps)} | |||
> | |||
<span | |||
className={ButtonBase.Button(styleProps)} | |||
className={CheckControlBase.CheckIndicatorWrapper(styleProps)} | |||
/> | |||
</span> | |||
<span | |||
className={ButtonBase.Label(styleProps)} | |||
> | |||
<span | |||
className={ButtonBase.MainText()} | |||
> | |||
<span | |||
className={ButtonBase.Border(styleProps)} | |||
/> | |||
{checkIndicatorCommon} | |||
<span | |||
className={ButtonBase.Label(styleProps)} | |||
className={ButtonBase.OverflowText()} | |||
> | |||
<span | |||
className={ButtonBase.MainText()} | |||
> | |||
{children} | |||
</span> | |||
</span> | |||
{ | |||
subtext | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={ButtonBase.OverflowText()} | |||
className={ButtonBase.Subtext()} | |||
> | |||
{children} | |||
</span> | |||
</span> | |||
{ | |||
subtext | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={ButtonBase.Subtext()} | |||
> | |||
<span | |||
className={ButtonBase.OverflowText()} | |||
> | |||
{subtext} | |||
</span> | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
{ | |||
badge | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={ButtonBase.BadgeContainer(styleProps)} | |||
className={ButtonBase.OverflowText()} | |||
> | |||
{badge} | |||
{subtext} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
) | |||
} | |||
{ | |||
appearance === CheckControlBase.CheckControlAppearance.SWITCH | |||
&& ( | |||
<span> | |||
<span /> | |||
<span | |||
className={CheckControlBase.CheckIndicatorArea(styleProps)} | |||
> | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
{ | |||
badge | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={CheckControlBase.CheckIndicatorWrapper(styleProps)} | |||
/> | |||
</span> | |||
<span> | |||
{children} | |||
{ | |||
subtext | |||
&& ( | |||
<> | |||
<br /> | |||
<span | |||
className={CheckControlBase.Subtext()} | |||
> | |||
{subtext} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
{ | |||
badge | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={ButtonBase.BadgeContainer(styleProps)} | |||
> | |||
{badge} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
) | |||
} | |||
className={ButtonBase.BadgeContainer(styleProps)} | |||
> | |||
{badge} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
</label> | |||
</div> | |||
); | |||
@@ -0,0 +1,20 @@ | |||
import * as React from 'react'; | |||
import { | |||
render, | |||
screen | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||
import { RadioTickBox } from '.'; | |||
jest.mock('@tesseract-design/web-base-checkcontrol'); | |||
describe('RadioTickBox', () => { | |||
it('should render a radio button', () => { | |||
render( | |||
<RadioTickBox /> | |||
); | |||
const checkbox = screen.getByRole('radio'); | |||
expect(checkbox).toBeInTheDocument(); | |||
}); | |||
}); |
@@ -0,0 +1,89 @@ | |||
import * as React from 'react'; | |||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||
export type RadioTickBoxProps = Omit<React.HTMLProps<HTMLInputElement>, 'size' | 'type' | 'style'> & { | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Does the component need to conserve space? | |||
*/ | |||
compact?: boolean, | |||
/** | |||
* Complementary content of the component. | |||
*/ | |||
subtext?: React.ReactNode, | |||
} | |||
/** | |||
* Component for performing an action upon activation (e.g. when clicked). | |||
* | |||
* This component functions as a regular button. | |||
*/ | |||
export const RadioTickBox = React.forwardRef<HTMLInputElement, RadioTickBoxProps>( | |||
( | |||
{ | |||
children, | |||
block = false, | |||
compact = false, | |||
subtext, | |||
className: _className, | |||
as: _as, | |||
...etcProps | |||
}: RadioTickBoxProps, | |||
ref, | |||
) => { | |||
const styleProps = React.useMemo<CheckControlBase.CheckControlBaseArgs>(() => ({ | |||
block, | |||
compact, | |||
appearance: CheckControlBase.CheckControlAppearance.TICK_BOX, | |||
type: 'radio', | |||
}), [block, compact]); | |||
return ( | |||
<div | |||
className={CheckControlBase.ClickAreaWrapper(styleProps)} | |||
> | |||
<label | |||
className={CheckControlBase.ClickArea()} | |||
> | |||
<input | |||
{...etcProps} | |||
type="radio" | |||
ref={ref} | |||
className={CheckControlBase.CheckStateContainer(styleProps)} | |||
/> | |||
<span> | |||
<span /> | |||
<span | |||
className={CheckControlBase.CheckIndicatorArea(styleProps)} | |||
> | |||
<span | |||
className={CheckControlBase.CheckIndicatorWrapper(styleProps)} | |||
/> | |||
</span> | |||
<span> | |||
{children} | |||
{ | |||
subtext | |||
&& ( | |||
<> | |||
<br /> | |||
<span | |||
className={CheckControlBase.Subtext()} | |||
> | |||
{subtext} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
</span> | |||
</label> | |||
</div> | |||
); | |||
}, | |||
); | |||
RadioTickBox.displayName = 'ActionButton'; |
@@ -0,0 +1,32 @@ | |||
import * as React from 'react'; | |||
import { | |||
render, | |||
screen | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||
import { ToggleButton } from '.'; | |||
jest.mock('@tesseract-design/web-base-button'); | |||
jest.mock('@tesseract-design/web-base-checkcontrol'); | |||
describe('ToggleButton', () => { | |||
it('should render a checkbox', () => { | |||
render( | |||
<ToggleButton /> | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toBeInTheDocument(); | |||
}); | |||
it('should render an indeterminate checkbox', () => { | |||
render( | |||
<ToggleButton | |||
indeterminate | |||
/> | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toHaveProperty('indeterminate', true); | |||
}); | |||
}); |
@@ -0,0 +1,181 @@ | |||
import * as React from 'react'; | |||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||
export type ToggleButtonProps = Omit<React.HTMLProps<HTMLInputElement>, 'size' | 'type' | 'style'> & { | |||
/** | |||
* Size of the component. | |||
*/ | |||
size?: ButtonBase.ButtonSize, | |||
/** | |||
* Variant of the component. | |||
*/ | |||
variant?: ButtonBase.ButtonVariant, | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Does the component need to conserve space? | |||
*/ | |||
compact?: boolean, | |||
/** | |||
* Complementary content of the component. | |||
*/ | |||
subtext?: React.ReactNode, | |||
/** | |||
* Short complementary content displayed at the edge of the component. | |||
*/ | |||
badge?: React.ReactNode, | |||
/** | |||
* Does the component have indeterminate check state? | |||
*/ | |||
indeterminate?: boolean, | |||
} | |||
/** | |||
* Component for performing an action upon activation (e.g. when clicked). | |||
* | |||
* This component functions as a regular button. | |||
*/ | |||
export const ToggleButton = React.forwardRef<HTMLInputElement, ToggleButtonProps>( | |||
( | |||
{ | |||
size = ButtonBase.ButtonSize.MEDIUM, | |||
variant = ButtonBase.ButtonVariant.OUTLINE, | |||
border = false, | |||
children, | |||
block = false, | |||
disabled = false, | |||
compact = false, | |||
subtext, | |||
badge, | |||
indeterminate = false, | |||
className: _className, | |||
as: _as, | |||
...etcProps | |||
}: ToggleButtonProps, | |||
ref, | |||
) => { | |||
const styleProps = React.useMemo<ButtonBase.ButtonBaseArgs & CheckControlBase.CheckControlBaseArgs>(() => ({ | |||
size, | |||
block, | |||
variant, | |||
border, | |||
compact, | |||
menuItem: false, | |||
disabled, | |||
appearance: CheckControlBase.CheckControlAppearance.BUTTON, | |||
type: 'checkbox', | |||
}), [size, block, variant, border, compact, disabled]); | |||
const defaultRef = React.useRef<HTMLInputElement>(null); | |||
const theRef = (ref ?? defaultRef) as React.MutableRefObject<HTMLInputElement>; | |||
React.useEffect(() => { | |||
if (!(indeterminate && theRef.current)) { | |||
return; | |||
} | |||
theRef.current.indeterminate = indeterminate; | |||
}, [theRef, indeterminate]); | |||
return ( | |||
<div | |||
className={CheckControlBase.ClickAreaWrapper(styleProps)} | |||
> | |||
<label | |||
className={CheckControlBase.ClickArea()} | |||
> | |||
<input | |||
{...etcProps} | |||
disabled={disabled} | |||
type="checkbox" | |||
ref={theRef} | |||
className={CheckControlBase.CheckStateContainer(styleProps)} | |||
/> | |||
<span | |||
className={ButtonBase.Button(styleProps)} | |||
> | |||
<span | |||
className={ButtonBase.Border(styleProps)} | |||
/> | |||
<span | |||
className={CheckControlBase.CheckIndicatorArea(styleProps)} | |||
> | |||
<span | |||
className={CheckControlBase.CheckIndicatorWrapper(styleProps)} | |||
> | |||
<svg | |||
className={CheckControlBase.CheckIndicator(styleProps)} | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
<polyline | |||
points="20 6 9 17 4 12" | |||
/> | |||
</svg> | |||
<svg | |||
className={CheckControlBase.CheckIndicator(styleProps)} | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
<polyline | |||
points="20 12 4 12" | |||
/> | |||
</svg> | |||
</span> | |||
</span> | |||
<span | |||
className={ButtonBase.Label(styleProps)} | |||
> | |||
<span | |||
className={ButtonBase.MainText()} | |||
> | |||
<span | |||
className={ButtonBase.OverflowText()} | |||
> | |||
{children} | |||
</span> | |||
</span> | |||
{ | |||
subtext | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={ButtonBase.Subtext()} | |||
> | |||
<span | |||
className={ButtonBase.OverflowText()} | |||
> | |||
{subtext} | |||
</span> | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
{ | |||
badge | |||
&& ( | |||
<> | |||
{' '} | |||
<span | |||
className={ButtonBase.BadgeContainer(styleProps)} | |||
> | |||
{badge} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
</label> | |||
</div> | |||
); | |||
}, | |||
); | |||
ToggleButton.displayName = 'ActionButton'; |
@@ -0,0 +1,20 @@ | |||
import * as React from 'react'; | |||
import { | |||
render, | |||
screen | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||
import { ToggleSwitch } from '.'; | |||
jest.mock('@tesseract-design/web-base-checkcontrol'); | |||
describe('ToggleSwitch', () => { | |||
it('should render a checkbox', () => { | |||
render( | |||
<ToggleSwitch /> | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toBeInTheDocument(); | |||
}); | |||
}); |
@@ -0,0 +1,109 @@ | |||
import * as React from 'react'; | |||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||
export type ToggleSwitchProps = Omit<React.HTMLProps<HTMLInputElement>, 'size' | 'type' | 'style'> & { | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Does the component need to conserve space? | |||
*/ | |||
compact?: boolean, | |||
/** | |||
* Complementary content of the component. | |||
*/ | |||
subtext?: React.ReactNode, | |||
} | |||
/** | |||
* Component for performing an action upon activation (e.g. when clicked). | |||
* | |||
* This component functions as a regular button. | |||
*/ | |||
export const ToggleSwitch = React.forwardRef<HTMLInputElement, ToggleSwitchProps>( | |||
( | |||
{ | |||
children, | |||
block = false, | |||
compact = false, | |||
subtext, | |||
className: _className, | |||
as: _as, | |||
...etcProps | |||
}: ToggleSwitchProps, | |||
ref, | |||
) => { | |||
const styleProps = React.useMemo<CheckControlBase.CheckControlBaseArgs>(() => ({ | |||
block, | |||
compact, | |||
menuItem: false, | |||
appearance: CheckControlBase.CheckControlAppearance.SWITCH, | |||
type: 'checkbox', | |||
}), [block, compact]); | |||
return ( | |||
<div | |||
className={CheckControlBase.ClickAreaWrapper(styleProps)} | |||
> | |||
<label | |||
className={CheckControlBase.ClickArea()} | |||
> | |||
<input | |||
{...etcProps} | |||
type="checkbox" | |||
ref={ref} | |||
className={CheckControlBase.CheckStateContainer(styleProps)} | |||
/> | |||
<span> | |||
<span /> | |||
<span | |||
className={CheckControlBase.CheckIndicatorArea(styleProps)} | |||
> | |||
<span | |||
className={CheckControlBase.CheckIndicatorWrapper(styleProps)} | |||
> | |||
<svg | |||
className={CheckControlBase.CheckIndicator(styleProps)} | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
<polyline | |||
points="20 6 9 17 4 12" | |||
/> | |||
</svg> | |||
<svg | |||
className={CheckControlBase.CheckIndicator(styleProps)} | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
<polyline | |||
points="20 12 4 12" | |||
/> | |||
</svg> | |||
</span> | |||
</span> | |||
<span> | |||
{children} | |||
{ | |||
subtext | |||
&& ( | |||
<> | |||
<br /> | |||
<span | |||
className={CheckControlBase.Subtext()} | |||
> | |||
{subtext} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
</span> | |||
</label> | |||
</div> | |||
); | |||
}, | |||
); | |||
ToggleSwitch.displayName = 'ActionButton'; |
@@ -0,0 +1,30 @@ | |||
import * as React from 'react'; | |||
import { | |||
render, | |||
screen | |||
} from '@testing-library/react'; | |||
import '@testing-library/jest-dom'; | |||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||
import { ToggleTickBox } from '.'; | |||
jest.mock('@tesseract-design/web-base-checkcontrol'); | |||
describe('ToggleTickBox', () => { | |||
it('should render a checkbox', () => { | |||
render( | |||
<ToggleTickBox /> | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toBeInTheDocument(); | |||
}); | |||
it('should render an indeterminate checkbox', () => { | |||
render( | |||
<ToggleTickBox | |||
indeterminate | |||
/> | |||
); | |||
const checkbox = screen.getByRole('checkbox'); | |||
expect(checkbox).toHaveProperty('indeterminate', true); | |||
}); | |||
}); |
@@ -0,0 +1,122 @@ | |||
import * as React from 'react'; | |||
import * as CheckControlBase from '@tesseract-design/web-base-checkcontrol'; | |||
export type ToggleTickBoxProps = Omit<React.HTMLProps<HTMLInputElement>, 'type' | 'style'> & { | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Does the component need to conserve space? | |||
*/ | |||
compact?: boolean, | |||
/** | |||
* Complementary content of the component. | |||
*/ | |||
subtext?: React.ReactNode, | |||
/** | |||
* Does the component have indeterminate check state? | |||
*/ | |||
indeterminate?: boolean, | |||
} | |||
/** | |||
* Component for performing an action upon activation (e.g. when clicked). | |||
* | |||
* This component functions as a regular button. | |||
*/ | |||
export const ToggleTickBox = React.forwardRef<HTMLInputElement, ToggleTickBoxProps>( | |||
( | |||
{ | |||
children, | |||
block = false, | |||
compact = false, | |||
subtext, | |||
indeterminate = false, | |||
className: _className, | |||
as: _as, | |||
...etcProps | |||
}: ToggleTickBoxProps, | |||
ref, | |||
) => { | |||
const styleProps = React.useMemo<CheckControlBase.CheckControlBaseArgs>(() => ({ | |||
block, | |||
compact, | |||
appearance: CheckControlBase.CheckControlAppearance.TICK_BOX, | |||
type: 'checkbox', | |||
}), [block, compact]); | |||
const defaultRef = React.useRef<HTMLInputElement>(null); | |||
const theRef = (ref ?? defaultRef) as React.MutableRefObject<HTMLInputElement>; | |||
React.useEffect(() => { | |||
if (!(indeterminate && theRef.current)) { | |||
return; | |||
} | |||
theRef.current.indeterminate = indeterminate; | |||
}, [theRef, indeterminate]); | |||
return ( | |||
<div | |||
className={CheckControlBase.ClickAreaWrapper(styleProps)} | |||
> | |||
<label | |||
className={CheckControlBase.ClickArea()} | |||
> | |||
<input | |||
{...etcProps} | |||
type="checkbox" | |||
ref={theRef} | |||
className={CheckControlBase.CheckStateContainer(styleProps)} | |||
/> | |||
<span> | |||
<span /> | |||
<span | |||
className={CheckControlBase.CheckIndicatorArea(styleProps)} | |||
> | |||
<span | |||
className={CheckControlBase.CheckIndicatorWrapper(styleProps)} | |||
> | |||
<svg | |||
className={CheckControlBase.CheckIndicator(styleProps)} | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
<polyline | |||
points="20 6 9 17 4 12" | |||
/> | |||
</svg> | |||
<svg | |||
className={CheckControlBase.CheckIndicator(styleProps)} | |||
viewBox="0 0 24 24" | |||
role="presentation" | |||
> | |||
<polyline | |||
points="20 12 4 12" | |||
/> | |||
</svg> | |||
</span> | |||
</span> | |||
<span> | |||
{children} | |||
{ | |||
subtext | |||
&& ( | |||
<> | |||
<br /> | |||
<span | |||
className={CheckControlBase.Subtext()} | |||
> | |||
{subtext} | |||
</span> | |||
</> | |||
) | |||
} | |||
</span> | |||
</span> | |||
</label> | |||
</div> | |||
); | |||
}, | |||
); | |||
ToggleTickBox.displayName = 'ActionButton'; |
@@ -1,3 +1,6 @@ | |||
export * from './components/Checkbox'; | |||
export * from './components/DropdownSelect'; | |||
export * from './components/RadioButton'; | |||
export * from './components/RadioTickBox'; | |||
export * from './components/ToggleButton'; | |||
export * from './components/ToggleSwitch'; | |||
export * from './components/ToggleTickBox'; |
@@ -0,0 +1,14 @@ | |||
import * as WebOptionReact from '.'; | |||
describe('web-option-react', () => { | |||
it.each([ | |||
'DropdownSelect', | |||
'RadioButton', | |||
'RadioTickBox', | |||
'ToggleButton', | |||
'ToggleSwitch', | |||
'ToggleTickBox', | |||
])('should export %s', (namedExport) => { | |||
expect(WebOptionReact).toHaveProperty(namedExport); | |||
}); | |||
}); |
@@ -1,7 +1,7 @@ | |||
import { NextPage } from 'next'; | |||
import { ButtonSize, ButtonVariant } from '@tesseract-design/web-base-button'; | |||
import * as Navigation from '@tesseract-design/web-navigation-react'; | |||
import * as Info from '@tesseract-design/web-info-react'; | |||
import * as Info from '@tesseract-design/web-information-react'; | |||
const ActionPage: NextPage = () => { | |||
return ( | |||
@@ -614,21 +614,21 @@ const OptionPage: NextPage<Props> = ({ | |||
<div> | |||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
subtext="Subtext" | |||
> | |||
Checkbox | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
indeterminate | |||
subtext="Subtext" | |||
> | |||
Checkbox | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -638,23 +638,23 @@ const OptionPage: NextPage<Props> = ({ | |||
<div> | |||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
subtext="Subtext" | |||
appearance={CheckControlAppearance.SWITCH} | |||
> | |||
Checkbox | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
indeterminate | |||
subtext="Subtext" | |||
appearance={CheckControlAppearance.SWITCH} | |||
> | |||
Checkbox | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -664,72 +664,72 @@ const OptionPage: NextPage<Props> = ({ | |||
<div> | |||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
variant={ButtonVariant.FILLED} | |||
block | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
border | |||
block | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
border | |||
variant={ButtonVariant.FILLED} | |||
block | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
disabled | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
variant={ButtonVariant.FILLED} | |||
block | |||
disabled | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
border | |||
block | |||
disabled | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
border | |||
variant={ButtonVariant.FILLED} | |||
block | |||
@@ -737,20 +737,20 @@ const OptionPage: NextPage<Props> = ({ | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
border | |||
block | |||
appearance={CheckControlAppearance.BUTTON} | |||
indeterminate | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
border | |||
variant={ButtonVariant.FILLED} | |||
block | |||
@@ -758,7 +758,7 @@ const OptionPage: NextPage<Props> = ({ | |||
indeterminate | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -768,17 +768,17 @@ const OptionPage: NextPage<Props> = ({ | |||
<div> | |||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
border | |||
size={ButtonSize.SMALL} | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
border | |||
variant={ButtonVariant.FILLED} | |||
@@ -786,20 +786,20 @@ const OptionPage: NextPage<Props> = ({ | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
border | |||
size={ButtonSize.MEDIUM} | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
border | |||
variant={ButtonVariant.FILLED} | |||
@@ -807,20 +807,20 @@ const OptionPage: NextPage<Props> = ({ | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
border | |||
size={ButtonSize.LARGE} | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
border | |||
variant={ButtonVariant.FILLED} | |||
@@ -828,7 +828,7 @@ const OptionPage: NextPage<Props> = ({ | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -838,17 +838,17 @@ const OptionPage: NextPage<Props> = ({ | |||
<div> | |||
<div className="grid md:grid-cols-2 gap-4 my-4"> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
compact | |||
border | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
compact | |||
border | |||
@@ -856,10 +856,10 @@ const OptionPage: NextPage<Props> = ({ | |||
appearance={CheckControlAppearance.BUTTON} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
compact | |||
border | |||
@@ -871,10 +871,10 @@ const OptionPage: NextPage<Props> = ({ | |||
} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
compact | |||
border | |||
@@ -887,10 +887,10 @@ const OptionPage: NextPage<Props> = ({ | |||
} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
compact | |||
border | |||
@@ -903,10 +903,10 @@ const OptionPage: NextPage<Props> = ({ | |||
} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
<div> | |||
<Option.Checkbox | |||
<Option.ToggleTickBox | |||
block | |||
compact | |||
border | |||
@@ -920,7 +920,7 @@ const OptionPage: NextPage<Props> = ({ | |||
} | |||
> | |||
Button | |||
</Option.Checkbox> | |||
</Option.ToggleTickBox> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -17,9 +17,10 @@ | |||
"paths": { | |||
"@tesseract-design/web-action-react": ["./src/modules/action"], | |||
"@tesseract-design/web-freeform-react": ["./src/modules/freeform"], | |||
"@tesseract-design/web-info-react": ["./src/modules/info"], | |||
"@tesseract-design/web-information-react": ["./src/modules/information"], | |||
"@tesseract-design/web-navigation-react": ["./src/modules/navigation"], | |||
"@tesseract-design/web-option-react": ["./src/modules/option"], | |||
"@tesseract-design/web-base-badge": ["./src/modules/base-badge"], | |||
"@tesseract-design/web-base-button": ["./src/modules/base-button"], | |||
"@tesseract-design/web-base-checkcontrol": ["./src/modules/base-checkcontrol"], | |||
"@tesseract-design/web-base-textcontrol": ["./src/modules/base-textcontrol"], | |||
@@ -1,3 +1,6 @@ | |||
{ | |||
"extends": "./tsconfig.json" | |||
"extends": "./tsconfig.json", | |||
"compilerOptions": { | |||
"jsx": "react" | |||
} | |||
} |
@@ -317,6 +317,13 @@ | |||
dependencies: | |||
regenerator-runtime "^0.13.4" | |||
"@babel/runtime@^7.12.5": | |||
version "7.16.3" | |||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" | |||
integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== | |||
dependencies: | |||
regenerator-runtime "^0.13.4" | |||
"@babel/template@^7.16.0", "@babel/template@^7.3.3": | |||
version "7.16.0" | |||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" | |||
@@ -707,6 +714,13 @@ | |||
dependencies: | |||
"@sinonjs/commons" "^1.7.0" | |||
"@testing-library/user-event@^13.5.0": | |||
version "13.5.0" | |||
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.5.0.tgz#69d77007f1e124d55314a2b73fd204b333b13295" | |||
integrity sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg== | |||
dependencies: | |||
"@babel/runtime" "^7.12.5" | |||
"@tootallnate/once@1": | |||
version "1.1.2" | |||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" | |||