From b1df533ee45e5c71fccae16e4abecfbb885ea1bc Mon Sep 17 00:00:00 2001 From: Michael Dougall Date: Thu, 16 Apr 2020 17:09:01 +1000 Subject: [PATCH 1/2] fix: fixes css prop typing --- packages/css-in-js/src/index.tsx | 4 +++- .../src/jsx/__tests__/index.test.tsx | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/css-in-js/src/index.tsx b/packages/css-in-js/src/index.tsx index 318a33745..5bed9959e 100644 --- a/packages/css-in-js/src/index.tsx +++ b/packages/css-in-js/src/index.tsx @@ -6,9 +6,11 @@ export { ClassNames } from './class-names'; export type CSSProps = CSSProperties; +export type AnyKeyCssProps = { [key: string]: AnyKeyCssProps | CSSProperties }; + declare module 'react' { interface DOMAttributes { - css?: CSSProps | { [key: string]: CSSProps } | string | (string | CSSProps)[]; + css?: CSSProps | AnyKeyCssProps | string | (string | CSSProps)[]; } } diff --git a/packages/css-in-js/src/jsx/__tests__/index.test.tsx b/packages/css-in-js/src/jsx/__tests__/index.test.tsx index 9328b1f08..c85ea061e 100644 --- a/packages/css-in-js/src/jsx/__tests__/index.test.tsx +++ b/packages/css-in-js/src/jsx/__tests__/index.test.tsx @@ -22,6 +22,29 @@ describe('css prop', () => { expect(getByText('hello world')).toHaveCompiledCss('font-size', '12px'); }); + it('should not have type errors when using pseduo selectors', () => { +
+ hello world +
; + }); + it('should create css from string', () => { const { getByText } = render(
hello world
); From 3a70eeaa062f69f601d346925f0f1b941c8cdbf4 Mon Sep 17 00:00:00 2001 From: Michael Dougall Date: Thu, 16 Apr 2020 18:31:15 +1000 Subject: [PATCH 2/2] fix: consolidates all typing for styles --- packages/css-in-js/package.json | 3 +- .../src/class-names/__tests__/index.test.tsx | 46 ++++++++++++++ packages/css-in-js/src/class-names/index.tsx | 17 ++--- packages/css-in-js/src/index.tsx | 11 +--- .../src/jsx/__tests__/index.test.tsx | 16 ++++- .../src/styled/__tests__/index.test.tsx | 62 +++++++++++++++++++ packages/css-in-js/src/styled/index.tsx | 23 +++---- packages/css-in-js/src/types.tsx | 19 +++++- yarn.lock | 16 +++-- 9 files changed, 173 insertions(+), 40 deletions(-) diff --git a/packages/css-in-js/package.json b/packages/css-in-js/package.json index 577728bc6..9b766b24f 100644 --- a/packages/css-in-js/package.json +++ b/packages/css-in-js/package.json @@ -28,7 +28,8 @@ "src" ], "dependencies": { - "@compiled/style": "0.2.14" + "@compiled/style": "0.2.14", + "csstype": "^2.0.0" }, "peerDependencies": { "react": "^16.12.0" diff --git a/packages/css-in-js/src/class-names/__tests__/index.test.tsx b/packages/css-in-js/src/class-names/__tests__/index.test.tsx index 91ae59595..8f6d749c6 100644 --- a/packages/css-in-js/src/class-names/__tests__/index.test.tsx +++ b/packages/css-in-js/src/class-names/__tests__/index.test.tsx @@ -30,6 +30,52 @@ describe('class names component', () => { expect(getByText('hello world')).toHaveCompiledCss('font-size', '13px'); }); + it('should create css from template literal', () => { + const fontSize = 12; + const { getByText } = render( + + {({ css, style }) => ( +
+ hello world +
+ )} +
+ ); + + expect(getByText('hello world')).toHaveCompiledCss('font-size', '12px'); + }); + + it('should not type error with nested selectors', () => { + + {({ css }) => ( +
+ hello world +
+ )} +
; + }); + it('should create css from string literal', () => { const { getByText } = render( diff --git a/packages/css-in-js/src/class-names/index.tsx b/packages/css-in-js/src/class-names/index.tsx index 54fb902eb..967b62d17 100644 --- a/packages/css-in-js/src/class-names/index.tsx +++ b/packages/css-in-js/src/class-names/index.tsx @@ -1,17 +1,12 @@ -import { ReactNode, CSSProperties } from 'react'; +import { ReactNode } from 'react'; import { createSetupError } from '../utils/error'; - -export type CSSFunction = (css: ObjectLiteralCSS) => string; - -export type ObjectLiteralCSS = - | TemplateStringsArray - | CSSProperties - | string - | (CSSProperties | TemplateStringsArray | string)[] - | { [key: string]: TExtraProps | CSSProperties }; +import { CssFunction, TemplateInterpolations } from '../types'; export interface ClassNamesProps { - children: (opts: { css: CSSFunction; style: { [key: string]: string } }) => ReactNode; + children: (opts: { + css: (css: CssFunction | CssFunction[], ...interpoltations: TemplateInterpolations[]) => string; + style: { [key: string]: string }; + }) => ReactNode; } export function ClassNames(_: ClassNamesProps): JSX.Element { diff --git a/packages/css-in-js/src/index.tsx b/packages/css-in-js/src/index.tsx index 5bed9959e..5cd4d689b 100644 --- a/packages/css-in-js/src/index.tsx +++ b/packages/css-in-js/src/index.tsx @@ -1,23 +1,18 @@ -import { CSSProperties } from 'react'; - export { default as Style } from '@compiled/style'; export { styled } from './styled'; export { ClassNames } from './class-names'; - -export type CSSProps = CSSProperties; - -export type AnyKeyCssProps = { [key: string]: AnyKeyCssProps | CSSProperties }; +import { CssFunction } from './types'; declare module 'react' { interface DOMAttributes { - css?: CSSProps | AnyKeyCssProps | string | (string | CSSProps)[]; + css?: CssFunction | CssFunction[]; } } declare global { namespace JSX { interface IntrinsicAttributes { - css?: CSSProps; + css?: CssFunction | CssFunction[]; } } } diff --git a/packages/css-in-js/src/jsx/__tests__/index.test.tsx b/packages/css-in-js/src/jsx/__tests__/index.test.tsx index c85ea061e..621cf4b0b 100644 --- a/packages/css-in-js/src/jsx/__tests__/index.test.tsx +++ b/packages/css-in-js/src/jsx/__tests__/index.test.tsx @@ -9,6 +9,20 @@ describe('css prop', () => { expect(getByText('hello world')).toHaveCompiledCss('font-size', '15px'); }); + it('should use string literal with identifier', () => { + const fontSize = 12; + const { getByText } = render( +
+ hello world +
+ ); + + expect(getByText('hello world')).toHaveCompiledCss('font-size', '12px'); + }); + it('should create css from string literal', () => { const { getByText } = render(
{ expect(getByText('hello world')).toHaveCompiledCss('font-size', '12px'); }); - it('should not have type errors when using pseduo selectors', () => { + it('should not type error with nested selectors', () => {
{ expect(getByText('hello world').getAttribute('href')).toEqual('#'); }); + it('should not type error with nested selectors', () => { + styled.div({ + color: 'currentColor', + textDecoration: 'none', + position: 'relative', + ':before': { + opacity: 0, + content: '⚓', + position: 'absolute', + left: '-5rem', + fontSize: '3rem', + }, + ':hover': { + ':before': { + opacity: 1, + }, + }, + }); + }); + + it('should not type error with nested arrow funcs', () => { + styled.div<{ fontSize: string }>({ + color: 'currentColor', + textDecoration: 'none', + position: 'relative', + fontSize: props => props.fontSize, + ':before': { + fontSize: props => props.fontSize, + opacity: 0, + content: '⚓', + position: 'absolute', + left: '-5rem', + }, + ':hover': { + fontSize: props => props.fontSize, + ':before': { + fontSize: props => props.fontSize, + opacity: 1, + }, + }, + }); + }); + it('should forward ref', () => { let ref: HTMLAnchorElement | null = null; const Link = styled.a``; @@ -162,6 +205,25 @@ describe('styled component', () => { expect(getByText('Hello world').getAttribute('href')).toEqual('/world'); }); + it('should compose from array', () => { + const extra = { + color: 'blue', + }; + const StyledLink = styled.div([ + { + fontSize: 12, + }, + extra, + ]); + + const { getByText } = render(Hello world); + + expect(getByText('Hello world')).toHaveCompiledCss({ + color: 'blue', + 'font-size': '12px', + }); + }); + it('should create css from string', () => { const StyledDiv = styled.div('font-size: 15px;'); diff --git a/packages/css-in-js/src/styled/index.tsx b/packages/css-in-js/src/styled/index.tsx index 6a69c0ad8..e94e8da70 100644 --- a/packages/css-in-js/src/styled/index.tsx +++ b/packages/css-in-js/src/styled/index.tsx @@ -1,18 +1,15 @@ -import { CSSProperties, ComponentType } from 'react'; +import { ComponentType } from 'react'; import { createSetupError } from '../utils/error'; +import { CssFunction, TemplateInterpolations } from '../types'; -/** - * Typing for the CSS object. - */ -export type CssObject = - | CSSProperties - | string - | Record string | number) | string | number>; +export interface FunctionIterpolation { + (props: TProps): string | number; +} /** - * Typing for the interpolations. + * Typing for the CSS object. */ -export type Interpolations = string | number | ((props: TProps) => string | number); +export type CssObject = CssFunction>; /** * Extra props added to the output Styled Component. @@ -29,8 +26,8 @@ export interface StyledProps { export interface StyledFunctionFromTag { ( // Allows either string or object (`` or ({})) - css: CssObject | CssObject[] | TemplateStringsArray, - ...interpoltations: Interpolations[] + css: CssObject | CssObject[], + ...interpoltations: (TemplateInterpolations | FunctionIterpolation)[] ): React.ComponentType; } @@ -38,7 +35,7 @@ export interface StyledFunctionFromComponent { ( // Allows either string or object (`` or ({})) css: CssObject | TemplateStringsArray, - ...interpoltations: Interpolations[] + ...interpoltations: (TemplateInterpolations | FunctionIterpolation)[] ): React.ComponentType; } diff --git a/packages/css-in-js/src/types.tsx b/packages/css-in-js/src/types.tsx index fcd4f9c8e..19476fb28 100644 --- a/packages/css-in-js/src/types.tsx +++ b/packages/css-in-js/src/types.tsx @@ -1,4 +1,21 @@ -export type Unused = any; +import * as CSS from 'csstype'; + +/** + * Typing for the interpolations. + */ +export type TemplateInterpolations = string | number; + +export type CSSProps = CSS.Properties; + +export type AnyKeyCssProps = { + [key: string]: AnyKeyCssProps | CSSProps | TValue; +}; + +export type CssFunction = + | CSSProps + | AnyKeyCssProps + | TemplateStringsArray + | string; declare global { namespace jest { diff --git a/yarn.lock b/yarn.lock index b641480d3..cbe5f0f5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1868,13 +1868,14 @@ version "0.0.30" resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" -"@types/styled-components@4.1.8", "@types/styled-components@^5.0.1": - version "4.1.8" - resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-4.1.8.tgz#15c8a53bb4b9066e528fafb7558963dee5690ae0" - integrity sha512-NrG0wmB9Rafy5i00GFxUM/uEge148bX2QPr+Q/MI2fXrew6WOp1hN2A3YEG0AeT45z47CMdJ3BEffPsdQCWayA== +"@types/styled-components@^5.0.1": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.0.tgz#24d3412ba5395aa06e14fbc93c52f9454cebd0d6" + integrity sha512-ZFlLCuwF5r+4Vb7JUmd+Yr2S0UBdBGmI7ctFTgJMypIp3xOHI4LCFVn2dKMvpk6xDB2hLRykrEWMBwJEpUAUIQ== dependencies: - "@types/node" "*" + "@types/hoist-non-react-statics" "*" "@types/react" "*" + "@types/react-native" "*" csstype "^2.2.0" "@types/testing-library__dom@*", "@types/testing-library__dom@^6.0.0": @@ -3822,6 +3823,11 @@ csstype@^2.2.0, csstype@^2.5.7: version "2.6.8" resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.8.tgz#0fb6fc2417ffd2816a418c9336da74d7f07db431" +csstype@^2.6.10: + version "2.6.10" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.10.tgz#e63af50e66d7c266edb6b32909cfd0aabe03928b" + integrity sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w== + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"