Skip to content

Commit

Permalink
Merge branch 'main' into use-insertion-effect
Browse files Browse the repository at this point in the history
  • Loading branch information
emmatown authored Jan 6, 2022
2 parents 2177c9a + 242f7d8 commit f820725
Show file tree
Hide file tree
Showing 19 changed files with 465 additions and 68 deletions.
5 changes: 5 additions & 0 deletions .changeset/perfect-cameras-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@emotion/react': patch
---

Added `@emotion/babel-plugin` as a dependency - this is an actual dependency of the `@emotion/react/macro` entrypoint and it has to be explicitly declared to fix compatibility with strict package managers.
2 changes: 1 addition & 1 deletion docs/install.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: 'Install'
---

There are lots of ways to use Emotion, if you're using React, the easiest way to get started is to use the [`@emotion/react` package](/packages/react). If you're not using React, you should use [the `emotion` package](#vanilla).
There are lots of ways to use Emotion, if you're using React, the easiest way to get started is to use the [`@emotion/react` package](/packages/react). If you're not using React, you should use [the `@emotion/css` package](#vanilla).

```bash
yarn add @emotion/react
Expand Down
2 changes: 1 addition & 1 deletion docs/styled.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ const H1 = styled('h1', {
shouldForwardProp: prop =>
isPropValid(prop) && prop !== 'color'
})(props => ({
color: 'hotpink'
color: props.color
}))

render(<H1 color="lightgreen">This is lightgreen.</H1>)
Expand Down
92 changes: 35 additions & 57 deletions docs/typescript.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@
title: 'TypeScript'
---

Emotion includes TypeScript definitions for `@emotion/react` and `@emotion/styled`. These definitions also infer types for css properties with the object syntax, HTML/SVG tag names, and prop types.
Emotion includes TypeScript definitions for `@emotion/react` and `@emotion/styled`. These definitions infer types for css properties with the object syntax, HTML/SVG tag names, and prop types.

## @emotion/react

The easiest way to use the css prop with TypeScript is with the new JSX transform and the `jsxImportSource` TSConfig option (available since TS 4.1). For this approach, your TSConfig `compilerOptions` should contain

```json
"jsx": "react-jsx",
"jsxImportSource": "@emotion/react"
```

For most users, this is all the setup that is required. You can now define styles using the object syntax or template literal syntax and pass them to your components via the `css` prop.

```tsx
import { css } from '@emotion/react'

Expand All @@ -22,7 +31,7 @@ const subtitleStyle = css`
`
```

TypeScript checks css properties with the object style syntax using [csstype](https://www.npmjs.com/package/csstype) package, so following code will emit errors.
Object styles are recommended since they are type checked with the help of the [csstype](https://www.npmjs.com/package/csstype) package. For example, the following code will emit an error.

```tsx
import { css } from '@emotion/react';
Expand All @@ -35,47 +44,41 @@ const titleStyle = css({
});
```

To make the css prop work with pure TypeScript (without babel plugin) you need to add `/** @jsx jsx */` at the top of every file that is using the css prop:
When using our JSX factory, TypeScript only allows the `css` prop on components that accept a `className` prop. This is because `@emotion/react` resolves the value of the `css` prop to a class name and then passes this class name down to the rendered component.

### With the Babel plugin

[`@emotion/babel-plugin`](/docs/babel) is completely optional for TypeScript users. If you are not already using Babel, you probably shouldn't add it to your build tooling unless you truly need one of the features offered by `@emotion/babel-plugin`. On the other hand, there's no reason not to use `@emotion/babel-plugin` if you are already using Babel to transpile your TypeScript code.

### With the old JSX transform

If you are unable to upgrade to the `react-jsx` transform, you will need to specify the JSX factory at the top of every file:

```tsx
/** @jsx jsx */
import { jsx } from '@emotion/react'

<div css={{ background: 'black' }} />
```

As a result you may be not able to use react fragment shorthand syntax - `<></>`, but still you can use `<Fragment></Fragment>`.
This is a limitation of the TypeScript compiler not being able to independently specify jsx pragma and jsxFrag pragma.
As a result, you may be not able to use the shorthand syntax `<></>` for React fragments, but you can still use `<Fragment></Fragment>`. This is a limitation of the TypeScript compiler not being able to independently specify jsx pragma and jsxFrag pragma.

You can still use the css helper and pass the className yourself (ensure you are importing from the `@emotion/css` package, not `@emotion/react`).

```tsx
import { css } from '@emotion/css'

<div className={css({ background: 'black' })} />
const el = <div className={css({ background: 'black' })} />
```

### `css` prop

When using our JSX factories the support for `css` prop is being added only for components that accepts `className` prop as they take provided `css` prop, resolves it and pass the generated `className` to the rendered component.

If using the automatic runtime you should just add this to your `tsconfig.json` to let TypeScript know where it should look for the `JSX` namespace:
```json
{
"compilerOptions": {
"jsxImportSource": "@emotion/react"
}
}
```

The same `JSX` namespace is resolved if you are still using the classic runtime through the `@jsx` pragma. However, it's not possible to leverage `css` prop support being added conditionally based on a type of rendered component when one is not using our jsx pragma or the automatic runtime. For those cases when people use our pragma implicitly (for example when using our `@emotion/babel-preset-css-prop`) we have a special file that can be imported once to add support for the `css` prop globally, for all components. Use it like this:
It's not possible to leverage `css` prop support being added conditionally based on the type of a rendered component when not using our jsx pragma or the `react-jsx` transform. If you use our pragma implicitly (for example when using our `@emotion/babel-preset-css-prop`) we have a special file that can be imported once to add support for the `css` prop globally, for all components. Use it like this:

```ts
/// <reference types="@emotion/react/types/css-prop" />
```

## @emotion/styled

`@emotion/styled` works with TypeScript without any additional configuration.

### HTML/SVG elements

```tsx
Expand Down Expand Up @@ -170,13 +173,12 @@ interface ComponentProps {
label: string
}

const Component: FC<ComponentProps> = ({
label,
className
}) => <div className={className}>{label}</div>
const Component: FC<ComponentProps> = ({ label, className }) => (
<div className={className}>{label}</div>
)

const StyledComponent0 = styled(Component)`
color: ${props => props.label === 'Important' ? 'red' : 'green'};
color: ${props => (props.label === 'Important' ? 'red' : 'green')};
`

const StyledComponent1 = styled(Component)({
Expand All @@ -195,12 +197,12 @@ const App = () => (

Sometimes you want to wrap an existing component and override the type of a prop. Emotion allows you to specify a `shouldForwardProp` hook to filter properties which should be passed to the wrapped component.

If you make `shouldForwardProp` a [type guard](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) then only the props from the type guard will be exposed.
If you make `shouldForwardProp` a [type guard](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) then only the props from the type guard will be exposed.

For example:

``` ts
const Original: React.FC<{ prop1: string, prop2: string }> = () => null
```ts
const Original: React.FC<{ prop1: string; prop2: string }> = () => null

interface StyledOriginalExtraProps {
// This prop would conflict with the `prop2` on Original
Expand Down Expand Up @@ -286,7 +288,7 @@ declare module '@emotion/react' {

// You are also able to use a 3rd party theme this way:
import '@emotion/react'
import { LibTheme } from 'some-lib'
import { LibTheme } from 'some-lib'

declare module '@emotion/react' {
export interface Theme extends LibTheme {}
Expand All @@ -307,38 +309,14 @@ const Button = styled('button')`
export default Button
```

If you were previously relying on `theme` being an `any` type, you have to restore compatibility with:
If you were previously relying on `theme` being an `any` type, you can restore compatibility with:

_emotion.d.ts_

```ts
import '@emotion/react'

declare module '@emotion/react' {
export interface Theme extends Record<string, any> {}
export interface Theme extends Record<string, any> {}
}
```

### TypeScript < 2.9

For Typescript <2.9, the generic type version only works with object styles due to https://github.com/Microsoft/TypeScript/issues/11947.

You can work around this by specifying the prop types in your style callback:

``` ts
const StyledComponent0 = styled(Component)`
color: red;
background: ${(props: StyledComponentProps) =>
props.bgColor};
`
```

NOTE: This approach you will have to perform the intersection with the component props yourself to get at the component props

``` ts
const StyledComponent0 = styled(Component)`
color: red;
background: ${(props: StyledComponentProps & ComponentProps) =>
props.bgColor};
`
```
10 changes: 10 additions & 0 deletions packages/babel-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# @emotion/babel-plugin

## 11.7.2

### Patch Changes

- [#2585](https://github.com/emotion-js/emotion/pull/2585) [`b830c7dc`](https://github.com/emotion-js/emotion/commit/b830c7dc9da1c75c88e655150f04ef52b8176212) Thanks [@Andarist](https://github.com/Andarist)! - Fixed label extraction crashing in some cases involving variable declarations with array and object patterns.

* [#2585](https://github.com/emotion-js/emotion/pull/2585) [`b830c7dc`](https://github.com/emotion-js/emotion/commit/b830c7dc9da1c75c88e655150f04ef52b8176212) Thanks [@kddc](https://github.com/kddc), [@Andarist](https://github.com/Andarist)! - Improved label extraction for named function expressions and anonymous functions used as object property values.

- [#2602](https://github.com/emotion-js/emotion/pull/2602) [`b02f349d`](https://github.com/emotion-js/emotion/commit/b02f349d28df7bc77cad6d7e1b62aecef9f19405) Thanks [@Andarist](https://github.com/Andarist)! - Fixed an issue with styled transformer sometimes not using the used local name for the imported named export when used with `importMap`.

## 11.7.1

### Patch Changes
Expand Down
162 changes: 162 additions & 0 deletions packages/babel-plugin/__tests__/__snapshots__/css.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,37 @@ const thing = process.env.NODE_ENV === \\"production\\" ? {
};"
`;
exports[`emotion-babel-plugin css label-arrow-as-obj-property 1`] = `
"import { css } from '@emotion/react'
export const styles = {
colorFn1: () => css\`
color: hotpink;
\`
}
↓ ↓ ↓ ↓ ↓ ↓
function _EMOTION_STRINGIFIED_CSS_ERROR__() { return \\"You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop).\\"; }
import { css } from '@emotion/react';
var _ref = process.env.NODE_ENV === \\"production\\" ? {
name: \\"3sn2xs\\",
styles: \\"color:hotpink\\"
} : {
name: \\"1pr7txl-colorFn1\\",
styles: \\"color:hotpink;label:colorFn1;\\",
map: \\"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxhYmVsLWFycm93LWFzLW9iai1wcm9wZXJ0eS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFHcUIiLCJmaWxlIjoibGFiZWwtYXJyb3ctYXMtb2JqLXByb3BlcnR5LmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3NzIH0gZnJvbSAnQGVtb3Rpb24vcmVhY3QnXG5cbmV4cG9ydCBjb25zdCBzdHlsZXMgPSB7XG4gIGNvbG9yRm4xOiAoKSA9PiBjc3NgXG4gICAgY29sb3I6IGhvdHBpbms7XG4gIGBcbn1cbiJdfQ== */\\",
toString: _EMOTION_STRINGIFIED_CSS_ERROR__
};
export const styles = {
colorFn1: () => _ref
};"
`;
exports[`emotion-babel-plugin css label-arrow-function-expression 1`] = `
"import { css } from '@emotion/react'
Expand Down Expand Up @@ -421,6 +452,72 @@ const thing = function () {
};"
`;
exports[`emotion-babel-plugin css label-function-expression-as-obj-property 1`] = `
"import { css } from '@emotion/react'
export const styles = {
colorFn1: function () {
return css\`
color: hotpink;
\`
}
}
↓ ↓ ↓ ↓ ↓ ↓
function _EMOTION_STRINGIFIED_CSS_ERROR__() { return \\"You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop).\\"; }
import { css } from '@emotion/react';
var _ref = process.env.NODE_ENV === \\"production\\" ? {
name: \\"3sn2xs\\",
styles: \\"color:hotpink\\"
} : {
name: \\"1pr7txl-colorFn1\\",
styles: \\"color:hotpink;label:colorFn1;\\",
map: \\"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxhYmVsLWZ1bmN0aW9uLWV4cHJlc3Npb24tYXMtb2JqLXByb3BlcnR5LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUljIiwiZmlsZSI6ImxhYmVsLWZ1bmN0aW9uLWV4cHJlc3Npb24tYXMtb2JqLXByb3BlcnR5LmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3NzIH0gZnJvbSAnQGVtb3Rpb24vcmVhY3QnXG5cbmV4cG9ydCBjb25zdCBzdHlsZXMgPSB7XG4gIGNvbG9yRm4xOiBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIGNzc2BcbiAgICAgIGNvbG9yOiBob3RwaW5rO1xuICAgIGBcbiAgfVxufVxuIl19 */\\",
toString: _EMOTION_STRINGIFIED_CSS_ERROR__
};
export const styles = {
colorFn1: function () {
return _ref;
}
};"
`;
exports[`emotion-babel-plugin css label-function-expression-named 1`] = `
"import { css } from '@emotion/react'
const thing = function someName() {
return css\`
color: hotpink;
\`
}
↓ ↓ ↓ ↓ ↓ ↓
function _EMOTION_STRINGIFIED_CSS_ERROR__() { return \\"You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop).\\"; }
import { css } from '@emotion/react';
var _ref = process.env.NODE_ENV === \\"production\\" ? {
name: \\"3sn2xs\\",
styles: \\"color:hotpink\\"
} : {
name: \\"sn4mkg-someName\\",
styles: \\"color:hotpink;label:someName;\\",
map: \\"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxhYmVsLWZ1bmN0aW9uLWV4cHJlc3Npb24tbmFtZWQuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBR1kiLCJmaWxlIjoibGFiZWwtZnVuY3Rpb24tZXhwcmVzc2lvbi1uYW1lZC5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuXG5jb25zdCB0aGluZyA9IGZ1bmN0aW9uIHNvbWVOYW1lKCkge1xuICByZXR1cm4gY3NzYFxuICAgIGNvbG9yOiBob3RwaW5rO1xuICBgXG59XG4iXX0= */\\",
toString: _EMOTION_STRINGIFIED_CSS_ERROR__
};
const thing = function someName() {
return _ref;
};"
`;
exports[`emotion-babel-plugin css label-no-final-semi 1`] = `
"import { css } from '@emotion/react'
Expand Down Expand Up @@ -656,6 +753,71 @@ exports[`emotion-babel-plugin css no-actual-import 1`] = `
import '@emotion/react';"
`;
exports[`emotion-babel-plugin css no-label-array-pattern 1`] = `
"import { css } from '@emotion/react'
const [weirdo] = [
css\`
color: hotpink;
\`
]
export default weirdo
↓ ↓ ↓ ↓ ↓ ↓
function _EMOTION_STRINGIFIED_CSS_ERROR__() { return \\"You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop).\\"; }
import { css } from '@emotion/react';
const [weirdo] = [process.env.NODE_ENV === \\"production\\" ? {
name: \\"3sn2xs\\",
styles: \\"color:hotpink\\"
} : {
name: \\"3sn2xs\\",
styles: \\"color:hotpink\\",
map: \\"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vLWxhYmVsLWFycmF5LXBhdHRlcm4uanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBR0siLCJmaWxlIjoibm8tbGFiZWwtYXJyYXktcGF0dGVybi5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuXG5jb25zdCBbd2VpcmRvXSA9IFtcbiAgY3NzYFxuICAgIGNvbG9yOiBob3RwaW5rO1xuICBgXG5dXG5cbmV4cG9ydCBkZWZhdWx0IHdlaXJkb1xuIl19 */\\",
toString: _EMOTION_STRINGIFIED_CSS_ERROR__
}];
export default weirdo;"
`;
exports[`emotion-babel-plugin css no-label-obj-pattern-computed-property 1`] = `
"import { css } from '@emotion/react'
const computed = 'weirdo'
const { weirdo } = {
[computed]: css\`
color: hotpink;
\`
}
export default weirdo
↓ ↓ ↓ ↓ ↓ ↓
function _EMOTION_STRINGIFIED_CSS_ERROR__() { return \\"You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop).\\"; }
import { css } from '@emotion/react';
const computed = 'weirdo';
const {
weirdo
} = {
[computed]: process.env.NODE_ENV === \\"production\\" ? {
name: \\"3sn2xs\\",
styles: \\"color:hotpink\\"
} : {
name: \\"3sn2xs\\",
styles: \\"color:hotpink\\",
map: \\"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vLWxhYmVsLW9iai1wYXR0ZXJuLWNvbXB1dGVkLXByb3BlcnR5LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUtpQiIsImZpbGUiOiJuby1sYWJlbC1vYmotcGF0dGVybi1jb21wdXRlZC1wcm9wZXJ0eS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcyB9IGZyb20gJ0BlbW90aW9uL3JlYWN0J1xuXG5jb25zdCBjb21wdXRlZCA9ICd3ZWlyZG8nXG5cbmNvbnN0IHsgd2VpcmRvIH0gPSB7XG4gIFtjb21wdXRlZF06IGNzc2BcbiAgICBjb2xvcjogaG90cGluaztcbiAgYFxufVxuXG5leHBvcnQgZGVmYXVsdCB3ZWlyZG9cbiJdfQ== */\\",
toString: _EMOTION_STRINGIFIED_CSS_ERROR__
}
};
export default weirdo;"
`;
exports[`emotion-babel-plugin css object-dynamic-property 1`] = `
"import { css } from '@emotion/react'
Expand Down
Loading

0 comments on commit f820725

Please sign in to comment.