Merge pull request #30827 from Kovensky/styled-components-css

Add support to the `css` prop from babel-plugin-styled-components
This commit is contained in:
Mine Starks
2018-12-02 16:01:04 -08:00
committed by GitHub
7 changed files with 158 additions and 9 deletions

View File

@@ -34,6 +34,10 @@ export interface CommonProps {
px?: ResponsiveProp;
py?: ResponsiveProp;
theme?: any;
// this is actually more powerful than the plugin because of some limitations of the transform
/**
* This works even without babel-plugin-styled-components.
*/
css?: Interpolation<any>;
}

View File

@@ -1,8 +1,11 @@
import * as React from "react";
import { Flex, Box } from "@rebass/grid";
import { css } from "styled-components";
const Layout = () => (
<Flex m={4}>
<Box px={3} py={2} />
</Flex>
);
const cssTest = <Flex css='background: transparent;'><Box css={css`${{color: 'inherit'}}`}/></Flex>;

19
types/styled-components/cssprop.d.ts vendored Normal file
View File

@@ -0,0 +1,19 @@
import {} from "react";
import { CSSProp } from ".";
declare module "react" {
interface Attributes {
// NOTE: unlike the plain javascript version, it is not possible to get access
// to the element's own attributes inside function interpolations.
// Only theme will be accessible, and only with the DefaultTheme due to the global
// nature of this declaration.
// If you are writing this inline you already have access to all the attributes anyway,
// no need for the extra indirection.
/**
* If present, this React element will be converted by
* `babel-plugin-styled-components` into a styled component
* with the given css as its styles.
*/
css?: CSSProp;
}
}

View File

@@ -440,4 +440,35 @@ export class StyleSheetManager extends React.Component<
StyleSheetManagerProps
> {}
/**
* The CSS prop is not declared by default in the types as it would cause 'css' to be present
* on the types of anything that uses styled-components indirectly, even if they do not use the
* babel plugin.
*
* You can load a default declaration by using writing this special import from
* a typescript file. This module does not exist in reality, which is why the {} is important:
*
* ```ts
* import {} from 'styled-components/cssprop'
* ```
*
* Or you can declare your own module augmentation, which allows you to specify the type of Theme:
*
* ```ts
* import { CSSProp } from 'styled-components'
*
* interface MyTheme {}
*
* declare module 'react' {
* interface Attributes {
* css?: CSSProp<MyTheme>
* }
* }
* ```
*/
// ONLY string literals and inline invocations of css`` are supported, anything else crashes the plugin
export type CSSProp<T = AnyIfEmpty<DefaultTheme>> =
string |
FlattenInterpolation<ThemeProps<T>>;
export default styled;

View File

@@ -1,2 +1,7 @@
export { default } from '.';
export * from '.';
/**
* Recommended: also `import {} from 'styled-components/cssprop'`,
* or augment react's `Attribute` interface with your own version.
*/

View File

@@ -16,6 +16,7 @@ import styled, {
StyledComponent,
ThemedStyledComponentsModule
} from "styled-components";
import {} from "styled-components/cssprop";
/**
* general usage
@@ -187,7 +188,6 @@ const styledButton = styled.button`
const name = "hey";
const ThemedMyButton = withTheme(MyButton);
<ThemedMyButton name={name} />;
/**
@@ -305,7 +305,6 @@ const ObjectStylesBox = styled.div`
fontSize: 2
}};
`;
<ObjectStylesBox size="big" />;
/**
@@ -337,7 +336,6 @@ const AttrsWithOnlyNewProps = styled.h2.attrs({ as: "h1" })`
`;
const AttrsInputExtra = styled(AttrsInput).attrs({ autoComplete: "off" })``;
<AttrsInputExtra />;
/**
@@ -434,10 +432,8 @@ const Component = (props: WithThemeProps) => (
);
const ComponentWithTheme = withTheme(Component);
<ComponentWithTheme text={"hi"} />; // ok
<ComponentWithTheme text={"hi"} theme={{ color: "red" }} />; // ok
<ThemeConsumer>{theme => <Component text="hi" theme={theme} />}</ThemeConsumer>;
/**
@@ -625,7 +621,6 @@ const divFnRef = (ref: HTMLDivElement | null) => {
const divRef = React.createRef<HTMLDivElement>();
const StyledDiv = styled.div``;
<StyledDiv ref={divRef} />;
<StyledDiv ref={divFnRef} />;
<StyledDiv ref="string" />; // $ExpectError
@@ -786,3 +781,89 @@ async function themeAugmentation() {
</base.ThemeProvider>
);
}
// NOTE: this is needed for some tests inside cssProp,
// but actually running this module augmentation will cause
// tests elsewhere to break, and there is no way to contain it.
// Uncomment out as needed to run tests.
// declare module "styled-components" {
// interface DefaultTheme {
// background: string;
// }
// }
function cssProp() {
function Custom(props: React.ComponentPropsWithoutRef<"div">) {
return <div {...props} />;
}
const myCss = "background: blue;";
return (
<>
<div css="background: blue;" />
{/*
For some reason $ExpectError doesn't work on this expression.
Only strings work, objects crash the plugin.
<div css={{ background: "blue" }} />
*/}
<div
// would be nice to be able to turn this into an error as it also crashes the plugin,
// but this is how optional properties work in TypeScript...
css={undefined}
/>
<div
// css used as tagged function is fine and is correctly handled by the plugin
css={css`
background: blue;
`}
/>
<div
// but this crashes the plugin, even though it's valid type-wise and we can't forbid it
css={css({ background: "blue" })}
/>
<div
// this also crashes the plugin, only inline strings or css template tag work
css={myCss}
/>
<div
css={css`
background: ${() => "blue"};
`}
/>
<div
css={css`
background: ${props => {
// This requires the DefaultTheme augmentation
// // $ExpectType string
// props.theme.background;
return props.theme.background;
}};
`}
/>
<Custom css="background: blue;" />
<Custom css={undefined} />
<Custom
css={css`
background: blue;
`}
/>
<Custom
css={css`
background: ${() => "blue"};
`}
/>
<Custom
css={css`
background: ${props => {
// This requires the DefaultTheme augmentation
// // $ExpectType string
// props.theme.background;
return props.theme.background;
}};
`}
/>
</>
);
}

View File

@@ -3,20 +3,26 @@
"baseUrl": "../",
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"lib": ["es6", "dom"],
"lib": [
"es6",
"dom"
],
"module": "commonjs",
"noEmit": true,
"noImplicitAny": true,
"noImplicitThis": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"typeRoots": ["../"],
"typeRoots": [
"../"
],
"types": []
},
"files": [
"index.d.ts",
"macro.d.ts",
"cssprop.d.ts",
"test/index.tsx",
"test/macro.tsx"
]
}
}