Skip to content

Commit

Permalink
Fixing types for props with ref
Browse files Browse the repository at this point in the history
  • Loading branch information
mzabriskie committed Oct 15, 2020
1 parent af57d4c commit 783f816
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 18 deletions.
49 changes: 32 additions & 17 deletions packages/components/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface BoxOwnProps extends SpaceProps, ColorProps {
css?: InterpolationWithTheme<any>
}
export interface BoxProps
extends Assign<React.ComponentProps<'div'>, BoxOwnProps> {}
extends Assign<React.ComponentPropsWithRef<'div'>, BoxOwnProps> {}
/**
* Use the Box component as a layout primitive to add margin, padding, and colors to content.
* @see https://theme-ui.com/components/box
Expand Down Expand Up @@ -101,7 +101,7 @@ export interface HeadingProps
export const Heading: ForwardRef<HTMLHeadingElement, HeadingProps>

export interface ImageProps
extends Assign<React.ComponentProps<'img'>, BoxOwnProps> {}
extends Assign<React.ComponentPropsWithRef<'img'>, BoxOwnProps> {}
/**
* Image style variants can be defined in the theme.images object.
* @see https://theme-ui.com/components/image/
Expand All @@ -117,7 +117,7 @@ export type CardProps = BoxProps
export const Card: ForwardRef<HTMLDivElement, CardProps>

export interface LabelProps
extends Assign<React.ComponentProps<'label'>, BoxOwnProps> {}
extends Assign<React.ComponentPropsWithRef<'label'>, BoxOwnProps> {}
/**
* Label variants can be defined in `theme.forms`
* and the component uses the `theme.forms.label` variant by default.
Expand All @@ -126,7 +126,7 @@ export interface LabelProps
export const Label: ForwardRef<HTMLLabelElement, LabelProps>

export interface InputProps
extends Assign<React.ComponentProps<'input'>, BoxOwnProps> {}
extends Assign<React.ComponentPropsWithRef<'input'>, BoxOwnProps> {}
/**
* Input variants can be defined in `theme.forms`
* and the component uses the `theme.forms.input` variant by default.
Expand All @@ -135,7 +135,7 @@ export interface InputProps
export const Input: ForwardRef<HTMLInputElement, InputProps>

export interface SelectProps
extends Assign<React.ComponentProps<'select'>, BoxOwnProps> {}
extends Assign<React.ComponentPropsWithRef<'select'>, BoxOwnProps> {}
/**
* Select variants can be defined in `theme.forms`
* and the component uses the `theme.forms.select` variant by default.
Expand All @@ -144,7 +144,7 @@ export interface SelectProps
export const Select: ForwardRef<HTMLSelectElement, SelectProps>

export interface TextareaProps
extends Assign<React.ComponentProps<'textarea'>, BoxOwnProps> {}
extends Assign<React.ComponentPropsWithRef<'textarea'>, BoxOwnProps> {}
/**
* Form textarea component
*
Expand All @@ -155,7 +155,7 @@ export interface TextareaProps
export const Textarea: ForwardRef<HTMLTextAreaElement, TextareaProps>

export interface RadioProps
extends Assign<React.ComponentProps<'input'>, BoxOwnProps> {}
extends Assign<React.ComponentPropsWithRef<'input'>, BoxOwnProps> {}
/**
* Form radio input component
*
Expand All @@ -166,7 +166,7 @@ export interface RadioProps
export const Radio: ForwardRef<HTMLInputElement, RadioProps>

export interface CheckboxProps
extends Assign<React.ComponentProps<'input'>, BoxOwnProps> {}
extends Assign<React.ComponentPropsWithRef<'input'>, BoxOwnProps> {}
/**
* Form checkbox input component
*
Expand All @@ -177,7 +177,7 @@ export interface CheckboxProps
export const Checkbox: ForwardRef<HTMLInputElement, CheckboxProps>

export interface SliderProps
extends Assign<React.ComponentProps<'input'>, BoxOwnProps> {}
extends Assign<React.ComponentPropsWithRef<'input'>, BoxOwnProps> {}
/**
* Range input element
*
Expand Down Expand Up @@ -212,18 +212,20 @@ export function Field<
>(props: FieldProps<T>): JSX.Element

export interface ProgressProps
extends Assign<React.ComponentProps<'progress'>, BoxOwnProps> {}
extends Assign<React.ComponentPropsWithRef<'progress'>, BoxOwnProps> {}
/**
* @see https://theme-ui.com/components/progress/
*/
export const Progress: ForwardRef<HTMLProgressElement, ProgressProps>

export interface DonutProps
extends Omit<
React.SVGProps<SVGSVGElement>,
extends Assign<
Omit<
React.ComponentPropsWithRef<'svg'>,
'opacity' | 'color' | 'css' | 'sx' | 'max' | 'min'
>,
BoxOwnProps {
BoxOwnProps
> {
value: number
min?: number
max?: number
Expand All @@ -237,11 +239,13 @@ export interface DonutProps
export const Donut: ForwardRef<SVGSVGElement, DonutProps>

export interface SpinnerProps
extends Omit<
React.SVGProps<SVGSVGElement>,
extends Assign<
Omit<
React.ComponentPropsWithRef<'svg'>,
'opacity' | 'color' | 'css' | 'sx'
>,
BoxOwnProps {
BoxOwnProps
> {
size?: number | string
}
export const Spinner: ForwardRef<SVGSVGElement, SpinnerProps>
Expand Down Expand Up @@ -281,7 +285,18 @@ export type DividerProps = BoxProps
*/
export const Divider: ForwardRef<HTMLDivElement, DividerProps>

export interface EmbedProps extends BoxProps {
/**
* EmbedProps are a bit tricky. It is a composite component that uses a <Box />
* as the parent element which is what `props` are spread onto. The actual `ref`
* however is a nested <Box as="iframe" /> element. To support these props we
* need to use an intersection of the intrinsic attributes of HTMLDivElement,
* with the ref attributes of HTMLIFrameElement.
*/
export interface EmbedProps
extends Assign<
React.ComponentProps<'div'> & React.RefAttributes<HTMLIFrameElement>,
BoxOwnProps
> {
ratio?: number
src?: React.IframeHTMLAttributes<any>['src']
frameBorder?: React.IframeHTMLAttributes<any>['frameBorder']
Expand Down
181 changes: 181 additions & 0 deletions packages/components/test/types.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import React from 'react'
import {
Alert,
AlertProps,
AspectImage,
AspectImageProps,
AspectRatio,
AspectRatioProps,
Avatar,
AvatarProps,
Badge,
BadgeProps,
Box,
BoxProps,
Button,
ButtonProps,
Card,
CardProps,
Checkbox,
CheckboxProps,
Close,
CloseProps,
Container,
ContainerProps,
Divider,
DividerProps,
Donut,
DonutProps,
Embed,
EmbedProps,
Field,
FieldProps,
Flex,
FlexProps,
Grid,
GridProps,
Heading,
HeadingProps,
IconButton,
IconButtonProps,
Image,
ImageProps,
Input,
InputProps,
Label,
LabelProps,
Link,
LinkProps,
MenuButton,
MenuButtonProps,
Message,
MessageProps,
NavLink,
NavLinkProps,
Progress,
ProgressProps,
Radio,
RadioProps,
Select,
SelectProps,
Slider,
SliderProps,
Spinner,
SpinnerProps,
Text,
TextProps,
Textarea,
TextareaProps,
} from '../'

/**
* This isn't technically a Jest test, as Jest won't catch any type errors.
* It makes more sense to put this under the test/ folder organizationally.
* Wrap everything in describe/it so Jest won't complain, but this actually
* gets run in a meaningul way with tsc as part of the typecheck command.
*/
describe('components type check', () => {
it('should pass type check for props', () => {
// Alert
;((props: AlertProps) => <Alert {...props} />)({})

// AspectImage
;((props: AspectImageProps) => <AspectImage {...props} />)({})

// AspectRatio
;((props: AspectRatioProps) => <AspectRatio {...props} />)({})

// Avatar
;((props: AvatarProps) => <Avatar {...props} />)({})

// Badge
;((props: BadgeProps) => <Badge {...props} />)({})

// Box
;((props: BoxProps) => <Box {...props} />)({})

// Button
;((props: ButtonProps) => <Button {...props} />)({})

// Card
;((props: CardProps) => <Card {...props} />)({})

// Checkbox
;((props: CheckboxProps) => <Checkbox {...props} />)({})

// Close
;((props: CloseProps) => <Close {...props} />)({})

// Container
;((props: ContainerProps) => <Container {...props} />)({})

// Divider
;((props: DividerProps) => <Divider {...props} />)({})

// Donut
;((props: DonutProps) => <Donut {...props} />)({ value: 50 })

// Embed
;((props: EmbedProps) => <Embed {...props} />)({})

// Field
;((props: FieldProps<typeof Input>) => <Field {...props} />)({
label: 'Email',
name: 'email',
})

// Flex
;((props: FlexProps) => <Flex {...props} />)({})

// Grid
;((props: GridProps) => <Grid {...props} />)({})

// Heading
;((props: HeadingProps) => <Heading {...props} />)({})

// IconButton
;((props: IconButtonProps) => <IconButton {...props} />)({})

// Image
;((props: ImageProps) => <Image {...props} />)({})

// Input
;((props: InputProps) => <Input {...props} />)({})

// Label
;((props: LabelProps) => <Label {...props} />)({})

// Link
;((props: LinkProps) => <Link {...props} />)({})

// MenuButton
;((props: MenuButtonProps) => <MenuButton {...props} />)({})

// Message
;((props: MessageProps) => <Message {...props} />)({})

// NavLink
;((props: NavLinkProps) => <NavLink {...props} />)({})

// Progress
;((props: ProgressProps) => <Progress {...props} />)({})

// Radio
;((props: RadioProps) => <Radio {...props} />)({})

// Select
;((props: SelectProps) => <Select {...props} />)({})

// Slider
;((props: SliderProps) => <Slider {...props} />)({})

// Spinner
;((props: SpinnerProps) => <Spinner {...props} />)({})

// Text
;((props: TextProps) => <Text {...props} />)({})

// Textarea
;((props: TextareaProps) => <Textarea {...props} />)({})
})
})
8 changes: 7 additions & 1 deletion packages/components/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*.ts", "src/**/*.tsx", "index.d.ts"]
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"test/**/*.ts",
"test/**/*.tsx",
"index.d.ts"
]
}

0 comments on commit 783f816

Please sign in to comment.