diff --git a/src/components/Box/Box.spec.tsx b/src/components/Box/Box.spec.tsx index 10f0d1f7..0bb69183 100644 --- a/src/components/Box/Box.spec.tsx +++ b/src/components/Box/Box.spec.tsx @@ -35,27 +35,45 @@ describe('Box', () => { expect(container.firstChild).toMatchSnapshot(); }); - test('it works with default testId', () => { - setup(Hello world); - expect(screen.getByTestId('Box')).toBeTruthy(); - }); + describe('as prop', () => { + test('it falls back to div', () => { + setup(Hello world); + expect(screen.getByTestId('Component').nodeName).toBe('DIV'); + }); - test('it works with specified testId', () => { - setup( - - Hello world - - ); - expect(screen.getByTestId('TestId')).toBeTruthy(); + test('it works with HTML element', () => { + setup( + + Hello world + + ); + expect(screen.getByTestId('Box').nodeName).toBe('BUTTON'); + expect(screen.getByRole('button')).toBeTruthy(); + }); + + test('it works with React component', () => { + const TestComponent = ({ status }: { status: string }) => ( +

test {status}

+ ); + setup(); + expect(screen.getByText('test info')).toBeTruthy(); + }); }); - test('it works with as', () => { - setup( - - Hello world - - ); - expect(screen.getByTestId('Box').nodeName).toBe('SPAN'); + describe('testId prop', () => { + test('it works with default testId', () => { + setup(Hello world); + expect(screen.getByTestId('Box')).toBeTruthy(); + }); + + test('it works with specified testId', () => { + setup( + + Hello world + + ); + expect(screen.getByTestId('TestId')).toBeTruthy(); + }); }); test('it works with className', () => { diff --git a/src/components/Box/index.tsx b/src/components/Box/index.tsx index ed1874e0..b6059e81 100644 --- a/src/components/Box/index.tsx +++ b/src/components/Box/index.tsx @@ -1,41 +1,44 @@ -import React from 'react'; +import React, { ElementType } from 'react'; import cx from 'classnames'; -import type { BaseProps, SizeType } from '../types'; +import type { CommonComponentCombinedProps, SizeType } from '../types'; import styles from './Box.module.scss'; -export interface BoxProps { - readonly elevation?: SizeType; - readonly stroke?: SizeType; - readonly gutter?: SizeType; - readonly gutterX?: SizeType; - readonly gutterY?: SizeType; -} +export type BoxProps = { + elevation?: SizeType; + gutter?: SizeType; + gutterX?: SizeType; + gutterY?: SizeType; +}; -const Box = ({ +const Box = ({ + as, className, - as: Component = 'div', testId = 'Box', elevation = 'none', gutter = 'none', gutterX = 'none', gutterY = 'none', - ...restProps -}: BaseProps & BoxProps) => ( - -); + ...props +}: CommonComponentCombinedProps) => { + const PolymorphicComponent = as || 'div'; + return ( + + ); +}; Box.displayName = 'Box'; export default Box; diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 77ed5b51..20e8a27a 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,24 +1,27 @@ -import React from 'react'; +import React, { ElementType } from 'react'; import cx from 'classnames'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import type { IconProp } from '@fortawesome/fontawesome-svg-core'; -import type { BaseProps, LevelType, SizeType } from '../types'; +import type { + CommonComponentCombinedProps, + LevelType, + SizeType +} from '../types'; import Box from '../Box'; import Text from '../Text'; import styles from './Button.module.scss'; -export interface ButtonProps - extends Omit, 'size'> { - readonly level?: LevelType; - readonly size?: SizeType; - readonly icon?: IconProp; - readonly block?: boolean; - readonly disabled?: boolean; -} +export type ButtonProps = { + level?: LevelType; + size?: SizeType; + icon?: IconProp; + block?: boolean; + disabled?: boolean; +}; -const Button = ({ +const Button = ({ className, - as = 'button', + as, testId = 'Button', level = 'primary', size = 'medium', @@ -26,27 +29,30 @@ const Button = ({ icon, children, ...restProps -}: BaseProps & ButtonProps) => ( - - ) => { + const PolymorphicComponent = as || 'button'; + return ( + - {icon && } - {children} - - -); + + {icon && } + {children} + + + ); +}; Button.displayName = 'Button'; export default Button; diff --git a/src/components/types.ts b/src/components/types.ts index 6a9234a2..c0496d53 100644 --- a/src/components/types.ts +++ b/src/components/types.ts @@ -1,16 +1,25 @@ -import React from 'react'; +import { + ElementType, + ReactNode, + CSSProperties, + ComponentPropsWithoutRef +} from 'react'; -export type AsType = keyof JSX.IntrinsicElements; +export type CommonComponentProps = { + as?: T; + testId?: string; + children?: ReactNode; + className?: string; + style?: CSSProperties; +}; + +export type CommonComponentCombinedProps< + T extends ElementType, + K +> = CommonComponentProps & + Omit, keyof CommonComponentProps> & + K; export type LevelType = 'primary' | 'secondary' | 'tertiary' | 'danger'; export type SizeType = 'none' | 'small' | 'medium' | 'large'; - -export type BaseProps = { - readonly className?: string; - readonly as?: AsType; - readonly testId?: string; - readonly children?: React.ReactNode | React.ReactNode[]; - readonly style?: React.CSSProperties; - readonly htmlFor?: string; -};