From 77f512adc80ef40e263f5c03302968ae3b1418a8 Mon Sep 17 00:00:00 2001 From: Renato Ribeiro Date: Fri, 13 Oct 2017 00:54:24 -0300 Subject: [PATCH 01/13] Add typings to react-emotion package --- packages/react-emotion/package.json | 10 +- .../typescript_tests/tsconfig.json | 14 +++ .../typescript_tests/typescript_tests.tsx | 78 +++++++++++++ .../react-emotion/typings/react-emotion.d.ts | 103 ++++++++++++++++++ 4 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 packages/react-emotion/typescript_tests/tsconfig.json create mode 100644 packages/react-emotion/typescript_tests/typescript_tests.tsx create mode 100644 packages/react-emotion/typings/react-emotion.d.ts diff --git a/packages/react-emotion/package.json b/packages/react-emotion/package.json index 806e4d3fc..895ed1ade 100644 --- a/packages/react-emotion/package.json +++ b/packages/react-emotion/package.json @@ -4,13 +4,17 @@ "description": "The Next Generation of CSS-in-JS, for React projects.", "main": "dist/index.cjs.js", "module": "dist/index.es.js", + "types": "typings/react-emotion.d.ts", "files": [ "src", "dist", - "macro.js" + "macro.js", + "typings" ], "scripts": { "build": "npm-run-all clean rollup rollup:umd", + "test:typescript": "tsc --noEmit -p --jsx typescript_tests/tsconfig.json", + "pretest:typescript": "npm run build", "clean": "rimraf dist", "rollup": "rollup -c ../../rollup.config.js", "watch": "rollup -c ../../rollup.config.js --watch", @@ -24,11 +28,13 @@ "emotion": "^8.0.5" }, "devDependencies": { + "@types/react": "^16.0.10", "cross-env": "^5.0.5", "emotion": "^8.0.5", "npm-run-all": "^4.0.2", "rimraf": "^2.6.1", - "rollup": "^0.43.0" + "rollup": "^0.43.0", + "typescript": "^2.0.0" }, "author": "Kye Hohenberger", "homepage": "https://github.com/tkh44/emotion#readme", diff --git a/packages/react-emotion/typescript_tests/tsconfig.json b/packages/react-emotion/typescript_tests/tsconfig.json new file mode 100644 index 000000000..37c43b7f5 --- /dev/null +++ b/packages/react-emotion/typescript_tests/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "es2015", + "strict": true, + "allowSyntheticDefaultImports": true, + "moduleResolution": "node", + "jsx": "react" + }, + "include": [ + "./*.ts", + "./*.tsx" + ] +} diff --git a/packages/react-emotion/typescript_tests/typescript_tests.tsx b/packages/react-emotion/typescript_tests/typescript_tests.tsx new file mode 100644 index 000000000..01c17f146 --- /dev/null +++ b/packages/react-emotion/typescript_tests/typescript_tests.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import styled from '../'; + +let Component; +let mount; + +/* + * Inference HTML Tag Props + */ +Component = styled.div({ color: 'red' }); +mount = event} />; + +Component = styled('div')({ color: 'red' }); +mount = event} />; + +Component = styled.div`color: red;`; +mount = e} />; + +Component = styled('div')`color: red;`; +mount = e} />; + +Component = styled.a({ color: 'red' }); +mount = ; + +Component = styled('a')({ color: 'red' }); +mount = ; + +/* + * Passing custom props + */ +type CustomProps = { lookColor: string }; + +Component = styled.div( + { color: 'blue' }, + props => ({ + background: props.lookColor, + }), + props => ({ + border: `1px solid ${props.lookColor}`, + }), +); +mount = ; + +Component = styled('div')( + { color: 'blue' }, + props => ({ + background: props.lookColor, + }), +); +mount = ; + +const anotherColor = 'blue'; +Component = styled('div')` + background: ${props => props.lookColor}; + color: ${anotherColor}; +` +mount = ; + +/* + * With other components + */ +type AnotherCustomProps = { customProp: string } +type SFCComponentProps = { className?: string, foo: 'bar' } +const SFCComponent: React.StatelessComponent = props => ( +
{props.children}
+) + +// infer SFCComponentProps +Component = styled(SFCComponent)({ + color: 'red', +}) +mount = + +// do not infer SFCComponentProps, need to pass +Component = styled(SFCComponent)({ + color: 'red', +}) +mount = diff --git a/packages/react-emotion/typings/react-emotion.d.ts b/packages/react-emotion/typings/react-emotion.d.ts new file mode 100644 index 000000000..6e41958ca --- /dev/null +++ b/packages/react-emotion/typings/react-emotion.d.ts @@ -0,0 +1,103 @@ +import { StatelessComponent, Component as ClassComponent, CSSProperties } from 'react' +import { Interpolation as EmotionInterpolation } from 'emotion' + +export type InterpolationFn = ( + props: Props +) => EmotionInterpolation + +export type Interpolation = + | InterpolationFn + | EmotionInterpolation + +export interface Options { + string?: string, +} + +type Component = + | ClassComponent + | StatelessComponent + +export type ThemedProps = Props & { + theme: Theme, +} + +export interface StyledComponent + extends + ClassComponent, + StatelessComponent +{ + withComponent(tag: Tag): + StyledComponent + + withComponent(component: Component): + StyledComponent + + displayName: string + + __emotion_styles: string[] + __emotion_base: string | Component + __emotion_real: ThemedReactEmotionInterface +} + +export type ObjectStyleAttributes = + | CSSProperties + | { [key: string]: number | string | ObjectStyleAttributes } + +export interface CreateStyled { + // overload for template string as styles + ( + strings: TemplateStringsArray, + ...vars: Interpolation>[], + ): StyledComponent + + // overload for object as styles + ( + ...styles: ( + | ObjectStyleAttributes + | ((props: ThemedProps) => ObjectStyleAttributes) + )[] + ): StyledComponent +} + +// TODO: find a way to reuse CreateStyled here +// for now I needed to repeat all fn types/overloads +type ShorthandsFactories = { + [Tag in keyof JSX.IntrinsicElements]: { + // overload for template string as styles + ( + strings: TemplateStringsArray, + ...vars: Interpolation>[], + ): StyledComponent + + // overload for object as styles + ( + ...styles: ( + | ObjectStyleAttributes + | ((props: ThemedProps) => ObjectStyleAttributes) + )[] + ): StyledComponent + }; +}; + +export interface ThemedReactEmotionInterface extends ShorthandsFactories { + // overload for dom tag + ( + tag: Tag | Component, + options?: Options, + ): CreateStyled + + // overload for component + ( + component: Component, + options?: Options, + ): CreateStyled +} + +export interface ThemedReactEmotionModule { + default: ThemedReactEmotionInterface +} + +declare const styled: ThemedReactEmotionInterface +export default styled + + From f8841cd3dbfc8d369d7a3fd4ebc4cfd20a9f8661 Mon Sep 17 00:00:00 2001 From: Renato Ribeiro Date: Fri, 13 Oct 2017 01:12:58 -0300 Subject: [PATCH 02/13] Fix CSSProperties and add more tests --- .../typescript_tests/typescript_tests.tsx | 51 +++++++++++++++---- .../react-emotion/typings/react-emotion.d.ts | 4 +- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/packages/react-emotion/typescript_tests/typescript_tests.tsx b/packages/react-emotion/typescript_tests/typescript_tests.tsx index 01c17f146..9b8435011 100644 --- a/packages/react-emotion/typescript_tests/typescript_tests.tsx +++ b/packages/react-emotion/typescript_tests/typescript_tests.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import styled from '../'; +import styled, { ThemedReactEmotionInterface } from '../'; let Component; let mount; @@ -8,10 +8,10 @@ let mount; * Inference HTML Tag Props */ Component = styled.div({ color: 'red' }); -mount = event} />; +mount = event} />; Component = styled('div')({ color: 'red' }); -mount = event} />; +mount = event} />; Component = styled.div`color: red;`; mount = e} />; @@ -60,19 +60,50 @@ mount = ; * With other components */ type AnotherCustomProps = { customProp: string } -type SFCComponentProps = { className?: string, foo: 'bar' } +type SFCComponentProps = { className?: string, foo: string } + const SFCComponent: React.StatelessComponent = props => ( -
{props.children}
+
{props.children} {props.foo}
) // infer SFCComponentProps -Component = styled(SFCComponent)({ - color: 'red', -}) +Component = styled(SFCComponent)({ color: 'red' }) +mount = + +// infer SFCComponentProps +Component = styled(SFCComponent)`color: red`; mount = -// do not infer SFCComponentProps, need to pass +// do not infer SFCComponentProps with pass CustomProps, need to pass both Component = styled(SFCComponent)({ color: 'red', -}) +}, props => ({ + background: props.customProp, +})) +mount = + +// do not infer SFCComponentProps with pass CustomProps, need to pass both +Component = styled(SFCComponent)` + color: red; + background: ${props => props.customProp}; +`; mount = + + +/* + * With explicit theme + */ + +type Theme = { + color: { + primary: string, + secondary: string, + } +} + +const _styled = styled as ThemedReactEmotionInterface + +Component = _styled.div` + color: ${props => props.theme.color.primary} +` +mount = event} /> diff --git a/packages/react-emotion/typings/react-emotion.d.ts b/packages/react-emotion/typings/react-emotion.d.ts index 6e41958ca..91c31842d 100644 --- a/packages/react-emotion/typings/react-emotion.d.ts +++ b/packages/react-emotion/typings/react-emotion.d.ts @@ -41,7 +41,7 @@ export interface StyledComponent export type ObjectStyleAttributes = | CSSProperties - | { [key: string]: number | string | ObjectStyleAttributes } + | { [key: string]: ObjectStyleAttributes } export interface CreateStyled { // overload for template string as styles @@ -87,7 +87,7 @@ export interface ThemedReactEmotionInterface extends ShorthandsFactories< ): CreateStyled // overload for component - ( + ( component: Component, options?: Options, ): CreateStyled From 789acc0be48f70052e0744f9f51d2321b12a7a54 Mon Sep 17 00:00:00 2001 From: Renato Ribeiro Date: Fri, 13 Oct 2017 01:15:02 -0300 Subject: [PATCH 03/13] Remove --jsx flag from tsc command --- packages/react-emotion/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-emotion/package.json b/packages/react-emotion/package.json index 895ed1ade..cfeda5619 100644 --- a/packages/react-emotion/package.json +++ b/packages/react-emotion/package.json @@ -13,7 +13,7 @@ ], "scripts": { "build": "npm-run-all clean rollup rollup:umd", - "test:typescript": "tsc --noEmit -p --jsx typescript_tests/tsconfig.json", + "test:typescript": "tsc --noEmit -p typescript_tests/tsconfig.json", "pretest:typescript": "npm run build", "clean": "rimraf dist", "rollup": "rollup -c ../../rollup.config.js", From 71397a18bce94e243fe668b35b76b8096fabdd95 Mon Sep 17 00:00:00 2001 From: Renato Ribeiro Date: Fri, 13 Oct 2017 02:48:02 -0300 Subject: [PATCH 04/13] Add withComponent to typescript_tests --- .../typescript_tests/typescript_tests.tsx | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/packages/react-emotion/typescript_tests/typescript_tests.tsx b/packages/react-emotion/typescript_tests/typescript_tests.tsx index 9b8435011..44e32d36b 100644 --- a/packages/react-emotion/typescript_tests/typescript_tests.tsx +++ b/packages/react-emotion/typescript_tests/typescript_tests.tsx @@ -59,35 +59,35 @@ mount = ; /* * With other components */ -type AnotherCustomProps = { customProp: string } -type SFCComponentProps = { className?: string, foo: string } +type CustomProps2 = { customProp: string }; +type SFCComponentProps = { className?: string, foo: string }; const SFCComponent: React.StatelessComponent = props => (
{props.children} {props.foo}
-) +); // infer SFCComponentProps -Component = styled(SFCComponent)({ color: 'red' }) -mount = +Component = styled(SFCComponent)({ color: 'red' }); +mount = ; // infer SFCComponentProps Component = styled(SFCComponent)`color: red`; -mount = +mount = ; // do not infer SFCComponentProps with pass CustomProps, need to pass both -Component = styled(SFCComponent)({ +Component = styled(SFCComponent)({ color: 'red', }, props => ({ background: props.customProp, -})) -mount = +})); +mount = ; // do not infer SFCComponentProps with pass CustomProps, need to pass both -Component = styled(SFCComponent)` +Component = styled(SFCComponent)` color: red; background: ${props => props.customProp}; `; -mount = +mount = ; /* @@ -99,11 +99,29 @@ type Theme = { primary: string, secondary: string, } -} +}; -const _styled = styled as ThemedReactEmotionInterface +const _styled = styled as ThemedReactEmotionInterface; Component = _styled.div` color: ${props => props.theme.color.primary} -` -mount = event} /> +`; +mount = event} />; + +/* + * withComponent + */ + +type CustomProps3 = { + bgColor: string, +}; + +Component = styled.div(props => ({ + bgColor: props.bgColor, +})); + +let Link = Component.withComponent('a'); +mount = ; + +let Button = Component.withComponent('button'); +mount =