From 9f58967c63644f59812f2da5e19c963e97e5e954 Mon Sep 17 00:00:00 2001 From: Matt Zabriskie Date: Thu, 15 Oct 2020 10:26:01 -0600 Subject: [PATCH] Fixing types for props with ref --- packages/components/index.d.ts | 51 ++-- packages/components/test/types.tsx | 376 +++++++++++++++++++++++++++++ packages/components/tsconfig.json | 8 +- 3 files changed, 416 insertions(+), 19 deletions(-) create mode 100644 packages/components/test/types.tsx diff --git a/packages/components/index.d.ts b/packages/components/index.d.ts index 3d1a56c94..22163b950 100644 --- a/packages/components/index.d.ts +++ b/packages/components/index.d.ts @@ -24,7 +24,7 @@ export interface BoxOwnProps extends SpaceProps, ColorProps { css?: InterpolationWithTheme } export interface BoxProps - extends Assign, BoxOwnProps> {} + extends Assign, BoxOwnProps> {} /** * Use the Box component as a layout primitive to add margin, padding, and colors to content. * @see https://theme-ui.com/components/box @@ -101,7 +101,7 @@ export interface HeadingProps export const Heading: ForwardRef export interface ImageProps - extends Assign, BoxOwnProps> {} + extends Assign, BoxOwnProps> {} /** * Image style variants can be defined in the theme.images object. * @see https://theme-ui.com/components/image/ @@ -117,7 +117,7 @@ export type CardProps = BoxProps export const Card: ForwardRef export interface LabelProps - extends Assign, BoxOwnProps> {} + extends Assign, BoxOwnProps> {} /** * Label variants can be defined in `theme.forms` * and the component uses the `theme.forms.label` variant by default. @@ -126,7 +126,7 @@ export interface LabelProps export const Label: ForwardRef export interface InputProps - extends Assign, BoxOwnProps> {} + extends Assign, BoxOwnProps> {} /** * Input variants can be defined in `theme.forms` * and the component uses the `theme.forms.input` variant by default. @@ -135,7 +135,7 @@ export interface InputProps export const Input: ForwardRef export interface SelectProps - extends Assign, BoxOwnProps> {} + extends Assign, BoxOwnProps> {} /** * Select variants can be defined in `theme.forms` * and the component uses the `theme.forms.select` variant by default. @@ -144,7 +144,7 @@ export interface SelectProps export const Select: ForwardRef export interface TextareaProps - extends Assign, BoxOwnProps> {} + extends Assign, BoxOwnProps> {} /** * Form textarea component * @@ -155,7 +155,7 @@ export interface TextareaProps export const Textarea: ForwardRef export interface RadioProps - extends Assign, BoxOwnProps> {} + extends Assign, BoxOwnProps> {} /** * Form radio input component * @@ -166,7 +166,7 @@ export interface RadioProps export const Radio: ForwardRef export interface CheckboxProps - extends Assign, BoxOwnProps> {} + extends Assign, BoxOwnProps> {} /** * Form checkbox input component * @@ -177,7 +177,7 @@ export interface CheckboxProps export const Checkbox: ForwardRef export interface SliderProps - extends Assign, BoxOwnProps> {} + extends Assign, BoxOwnProps> {} /** * Range input element * @@ -198,7 +198,7 @@ export interface FieldOwnProps extends MarginProps { name: string } export type FieldProps = FieldOwnProps & - Omit, 'as' | keyof FieldOwnProps> & { + Omit, 'as' | keyof FieldOwnProps> & { /** * form control to render, default Input */ @@ -212,18 +212,20 @@ export function Field< >(props: FieldProps): JSX.Element export interface ProgressProps - extends Assign, BoxOwnProps> {} + extends Assign, BoxOwnProps> {} /** * @see https://theme-ui.com/components/progress/ */ export const Progress: ForwardRef export interface DonutProps - extends Omit< - React.SVGProps, + extends Assign< + Omit< + React.ComponentPropsWithRef<'svg'>, 'opacity' | 'color' | 'css' | 'sx' | 'max' | 'min' >, - BoxOwnProps { + BoxOwnProps + > { value: number min?: number max?: number @@ -237,11 +239,13 @@ export interface DonutProps export const Donut: ForwardRef export interface SpinnerProps - extends Omit< - React.SVGProps, + extends Assign< + Omit< + React.ComponentPropsWithRef<'svg'>, 'opacity' | 'color' | 'css' | 'sx' >, - BoxOwnProps { + BoxOwnProps + > { size?: number | string } export const Spinner: ForwardRef @@ -281,7 +285,18 @@ export type DividerProps = BoxProps */ export const Divider: ForwardRef -export interface EmbedProps extends BoxProps { +/** + * EmbedProps are a bit tricky. It is a composite component that uses a + * as the parent element which is what `props` are spread onto. The actual `ref` + * however is a nested 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, + BoxOwnProps + > { ratio?: number src?: React.IframeHTMLAttributes['src'] frameBorder?: React.IframeHTMLAttributes['frameBorder'] diff --git a/packages/components/test/types.tsx b/packages/components/test/types.tsx new file mode 100644 index 000000000..3859fa9f2 --- /dev/null +++ b/packages/components/test/types.tsx @@ -0,0 +1,376 @@ +/** @jsx jsx */ +import { jsx } from 'theme-ui' + +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 Elements', () => { + const SectionBox = Box.withComponent('section') + + const _ = ( + ref}> + e.pointerType} + sx={{ + ':first-of-type': { + bg: 'red', + }, + }} + /> + + ref}> + Box + Box + Box + Box + + + Box + Box + Box + Box + +