From 805497640894aa0910fe5ce230bbcddb1edd450f Mon Sep 17 00:00:00 2001 From: rart Date: Thu, 19 Mar 2020 13:36:42 +0100 Subject: [PATCH 1/4] [WIP] Add CardHeader types and tests for nested typography components (title & subheader) --- .../src/CardHeader/CardHeader.d.ts | 59 +++++- .../src/CardHeader/CardHeader.spec.tsx | 180 ++++++++++++++++++ 2 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 packages/material-ui/src/CardHeader/CardHeader.spec.tsx diff --git a/packages/material-ui/src/CardHeader/CardHeader.d.ts b/packages/material-ui/src/CardHeader/CardHeader.d.ts index 0a3c3f564e2f8b..d4289e00bf24c8 100644 --- a/packages/material-ui/src/CardHeader/CardHeader.d.ts +++ b/packages/material-ui/src/CardHeader/CardHeader.d.ts @@ -2,17 +2,29 @@ import * as React from 'react'; import { TypographyProps } from '../Typography'; import { OverridableComponent, OverrideProps } from '../OverridableComponent'; -export interface CardHeaderTypeMap

{ - props: P & { +export interface CardHeaderTypeMap< +export interface CardHeaderTypeMap< + Props = {}, + DefaultComponent extends React.ElementType = 'div', + TitleTypographyComponent extends React.ElementType = 'span', + SubheaderTypographyComponent extends React.ElementType = 'span' +> { + props: Props & { action?: React.ReactNode; avatar?: React.ReactNode; disableTypography?: boolean; subheader?: React.ReactNode; - subheaderTypographyProps?: Partial; + subheaderTypographyProps?: TypographyProps< + SubheaderTypographyComponent, + { component?: SubheaderTypographyComponent } + >; title?: React.ReactNode; - titleTypographyProps?: Partial; + titleTypographyProps?: TypographyProps< + TitleTypographyComponent, + { component?: TitleTypographyComponent } + >; }; - defaultComponent: D; + defaultComponent: DefaultComponent; classKey: CardHeaderClassKey; } /** @@ -25,13 +37,42 @@ export interface CardHeaderTypeMap

* * - [CardHeader API](https://material-ui.com/api/card-header/) */ -declare const CardHeader: OverridableComponent; +declare const CardHeader: OverridableCardHeader; + +export interface OverridableCardHeader extends OverridableComponent { + < + DefaultComponent extends React.ElementType = CardHeaderTypeMap['defaultComponent'], + Props = {}, + TitleTypographyComponent extends React.ElementType = 'span', + SubheaderTypographyComponent extends React.ElementType = 'span' + >( + props: { component?: DefaultComponent } & OverrideProps< + CardHeaderTypeMap< + Props, + DefaultComponent, + TitleTypographyComponent, + SubheaderTypographyComponent + >, + DefaultComponent + >, + ): JSX.Element; +} export type CardHeaderClassKey = 'root' | 'avatar' | 'action' | 'content' | 'title' | 'subheader'; export type CardHeaderProps< - D extends React.ElementType = CardHeaderTypeMap['defaultComponent'], - P = {} -> = OverrideProps, D>; + DefaultComponent extends React.ElementType = CardHeaderTypeMap['defaultComponent'], + Props = {}, + TitleTypographyComponent extends React.ElementType = 'span', + SubheaderTypographyComponent extends React.ElementType = 'span' +> = OverrideProps< + CardHeaderTypeMap< + Props, + DefaultComponent, + TitleTypographyComponent, + SubheaderTypographyComponent + >, + DefaultComponent +>; export default CardHeader; diff --git a/packages/material-ui/src/CardHeader/CardHeader.spec.tsx b/packages/material-ui/src/CardHeader/CardHeader.spec.tsx new file mode 100644 index 00000000000000..639bdf814398b3 --- /dev/null +++ b/packages/material-ui/src/CardHeader/CardHeader.spec.tsx @@ -0,0 +1,180 @@ +import * as React from 'react'; +import CardHeader from '@material-ui/core/CardHeader'; + +const CustomComponent: React.FC<{ stringProp: string; numberProp: number }> = () =>

; + +function componentPropTest() { + ; + ; + // $ExpectError + ; + // $ExpectError + ; +} + +function mixedCardHeaderComponentAndTypographyTest() { + ; + ; + ; + ; + // $ExpectError + ; + // $ExpectError + ; + ; + // $ExpectError + ; + ; +} + +function titleTypographyPropsTest() { + // $ExpectError + ; + ; + ; + ; + ; + ; + // $ExpectError + ; + ; +} + +function subheaderTypographyPropsTest() { + ; + ; + ; + ; + ; + ; + // $ExpectError + ; + // $ExpectError + ; +} + +function mixedTypographyPropsTest() { + ; + ; + // $ExpectError + ; + ; + // $ExpectError + ; + ; + ; +} From 5c5338c0fca3be84080d212c04bd16e17504657b Mon Sep 17 00:00:00 2001 From: rart Date: Thu, 19 Mar 2020 13:53:11 +0100 Subject: [PATCH 2/4] Fix double line typo --- packages/material-ui/src/CardHeader/CardHeader.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/material-ui/src/CardHeader/CardHeader.d.ts b/packages/material-ui/src/CardHeader/CardHeader.d.ts index d4289e00bf24c8..272fcec32f0968 100644 --- a/packages/material-ui/src/CardHeader/CardHeader.d.ts +++ b/packages/material-ui/src/CardHeader/CardHeader.d.ts @@ -2,7 +2,6 @@ import * as React from 'react'; import { TypographyProps } from '../Typography'; import { OverridableComponent, OverrideProps } from '../OverridableComponent'; -export interface CardHeaderTypeMap< export interface CardHeaderTypeMap< Props = {}, DefaultComponent extends React.ElementType = 'div', From c21f7f5ba50d0c755dcf9b0f2015b128d52de898 Mon Sep 17 00:00:00 2001 From: rart Date: Fri, 20 Mar 2020 09:31:22 +0100 Subject: [PATCH 3/4] Refactor repeated code Add additional tests using React.createElement --- .../src/CardHeader/CardHeader.d.ts | 25 ++-- .../src/CardHeader/CardHeader.spec.tsx | 118 +++++++++++++++++- 2 files changed, 130 insertions(+), 13 deletions(-) diff --git a/packages/material-ui/src/CardHeader/CardHeader.d.ts b/packages/material-ui/src/CardHeader/CardHeader.d.ts index 272fcec32f0968..61873bb281abc5 100644 --- a/packages/material-ui/src/CardHeader/CardHeader.d.ts +++ b/packages/material-ui/src/CardHeader/CardHeader.d.ts @@ -45,14 +45,11 @@ export interface OverridableCardHeader extends OverridableComponent( - props: { component?: DefaultComponent } & OverrideProps< - CardHeaderTypeMap< - Props, - DefaultComponent, - TitleTypographyComponent, - SubheaderTypographyComponent - >, - DefaultComponent + props: CardHeaderPropsWithComponent< + DefaultComponent, + Props, + TitleTypographyComponent, + SubheaderTypographyComponent >, ): JSX.Element; } @@ -74,4 +71,16 @@ export type CardHeaderProps< DefaultComponent >; +export type CardHeaderPropsWithComponent< + DefaultComponent extends React.ElementType = CardHeaderTypeMap['defaultComponent'], + Props = {}, + TitleTypographyComponent extends React.ElementType = 'span', + SubheaderTypographyComponent extends React.ElementType = 'span' +> = { component?: DefaultComponent } & CardHeaderProps< + DefaultComponent, + Props, + TitleTypographyComponent, + SubheaderTypographyComponent +>; + export default CardHeader; diff --git a/packages/material-ui/src/CardHeader/CardHeader.spec.tsx b/packages/material-ui/src/CardHeader/CardHeader.spec.tsx index 639bdf814398b3..0c3bde6076b80f 100644 --- a/packages/material-ui/src/CardHeader/CardHeader.spec.tsx +++ b/packages/material-ui/src/CardHeader/CardHeader.spec.tsx @@ -1,8 +1,108 @@ import * as React from 'react'; -import CardHeader from '@material-ui/core/CardHeader'; +import CardHeader, { CardHeaderProps, CardHeaderTypeMap } from '@material-ui/core/CardHeader'; const CustomComponent: React.FC<{ stringProp: string; numberProp: number }> = () =>
; +type DefaultComponent = CardHeaderTypeMap['defaultComponent']; + +interface ComponentProp { + component?: React.ElementType; +} + +function createElementBasePropMixedTest() { + React.createElement>(CardHeader); + React.createElement>(CardHeader, { + component: 'div', + }); + React.createElement(CardHeader, { + disableTypography: true, + }); + // $ExpectError + React.createElement>(CardHeader, { + unknownProp: 'shouldNotWork', + }); + // $ExpectError + React.createElement(CardHeader, { + disableTypography: 'hello', + }); + // $ExpectError + React.createElement(CardHeader, { + disableTypography: 'hello', + }); + // $ExpectError + React.createElement>(CardHeader, { + component: 'incorrectElement', + }); +} + +function createElementTypographyTest() { + React.createElement(CardHeader, { + titleTypographyProps: { + align: 'center', + }, + }); + // $ExpectError + React.createElement(CardHeader, { + titleTypographyProps: { + align: 'incorrectAlign', + }, + }); + React.createElement(CardHeader, { + titleTypographyProps: { + variant: 'body1', + }, + }); + // $ExpectError + React.createElement(CardHeader, { + titleTypographyProps: { + variant: 123, + }, + }); + React.createElement>(CardHeader, { + titleTypographyProps: { + component: 'div', + }, + }); + // ExpectError: This is expected to err; the type system should catch required props from "CustomComponent". + React.createElement>(CardHeader, { + titleTypographyProps: { + component: CustomComponent, + }, + }); + React.createElement>(CardHeader, { + titleTypographyProps: { + component: CustomComponent, + stringProp: '', + numberProp: 0, + }, + }); + // ExpectError: This is expected to err; the type system should catch the props type mismatch + // from "CustomComponent" props. + React.createElement>(CardHeader, { + titleTypographyProps: { + component: CustomComponent, + stringProp: 0, + numberProp: '', + }, + }); + // ExpectError: This is expected to err; the type system is welcoming unknown props. + React.createElement>(CardHeader, { + titleTypographyProps: { + unknownProp: 'shouldNotWork', + }, + }); + // $ExpectError + React.createElement>(CardHeader, { + titleTypographyProps: { + component: 'incorrectComponent', + }, + }); + // $ExpectError + React.createElement(CardHeader, { + titleTypographyProps: true, + }); +} + function componentPropTest() { ; ; @@ -89,11 +189,19 @@ function titleTypographyPropsTest() { }} />; // $ExpectError + ; + // $ExpectError ; ; @@ -122,7 +230,7 @@ function subheaderTypographyPropsTest() { ; @@ -153,12 +261,12 @@ function mixedTypographyPropsTest() { ; From 30c75b8e0798a0fe67e7dd68ad1487bc575902e4 Mon Sep 17 00:00:00 2001 From: rart Date: Fri, 20 Mar 2020 09:50:09 +0100 Subject: [PATCH 4/4] Modify redundant test, add additional CardHeader `component` prop tests --- .../material-ui/src/CardHeader/CardHeader.spec.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/material-ui/src/CardHeader/CardHeader.spec.tsx b/packages/material-ui/src/CardHeader/CardHeader.spec.tsx index 0c3bde6076b80f..98e49411350ecb 100644 --- a/packages/material-ui/src/CardHeader/CardHeader.spec.tsx +++ b/packages/material-ui/src/CardHeader/CardHeader.spec.tsx @@ -14,6 +14,17 @@ function createElementBasePropMixedTest() { React.createElement>(CardHeader, { component: 'div', }); + // ExpectError: type system should be demanding the required props of "CustomComponent" + React.createElement>(CardHeader, { + component: CustomComponent, + }); + // $ExpectError + React.createElement>(CardHeader, { + // This test shouldn't fail but does; stringProp & numberProp are required props of CustomComponent + component: CustomComponent, + stringProp: '', + numberProp: 0, + }); React.createElement(CardHeader, { disableTypography: true, }); @@ -27,7 +38,7 @@ function createElementBasePropMixedTest() { }); // $ExpectError React.createElement(CardHeader, { - disableTypography: 'hello', + disableTypography: 1, }); // $ExpectError React.createElement>(CardHeader, {