Skip to content

Commit cbb8b79

Browse files
authored
Move theming APIs to core (#1628)
* Move theming APIs to core * Add changeset * Remove emotion-theming link to the README from the docc * Update weak-islands-confess.md * Apply suggestions from code review Co-Authored-By: Mitchell Hamilton <[email protected]> * Re-expose ThemeContext from @emotion/core
1 parent 8ad4af4 commit cbb8b79

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+231
-473
lines changed

.changeset/weak-islands-confess.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@emotion/core': major
3+
'emotion-theming': major
4+
---
5+
6+
`emotion-theming` has been removed and all its exports were moved to `@emotion/core` package. Please import them like this `import { useTheme, ThemeProvider, withTheme } from '@emotion/core'` from now on.

docs/docs.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
- babel-plugin-emotion
4242
- eslint-plugin-emotion
4343
- emotion-server
44-
- emotion-theming
4544
- jest-emotion
4645
- '@emotion/native'
4746
- '@emotion/primitives'

docs/theming.mdx

Lines changed: 114 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22
title: 'Theming'
33
---
44

5-
Theming is provided by the library [`emotion-theming`](https://emotion.sh/docs/emotion-theming).
5+
Theming is included in the [`@emotion/core`](https://emotion.sh/docs/@emotion/core) package.
66

7-
```bash
8-
npm install -S emotion-theming
9-
```
7+
Add `ThemeProvider` to the top level of your app and access the theme with `props.theme` in a styled component or provide a function that accepts the theme as the css prop.
8+
9+
## Table of Contents
1010

11-
Add `ThemeProvider` to the top level of your app and access the theme with `props.theme` in a styled component or provide a function that accepts the theme as the css prop. The api is laid out in detail [in the documentation](https://emotion.sh/docs/emotion-theming).
11+
- [Examples](#examples)
12+
- [Usage](#usage)
13+
- [API](#api)
14+
- [ThemeProvider](#themeprovider-reactcomponenttype)
15+
- [withTheme](#withthemecomponent-reactcomponenttype-reactcomponenttype)
16+
- [useTheme](#usetheme)
17+
- [Credits](#credits)
1218

1319
## Examples
1420

@@ -17,8 +23,7 @@ Add `ThemeProvider` to the top level of your app and access the theme with `prop
1723
```jsx
1824
// @live
1925
/** @jsx jsx */
20-
import { jsx } from '@emotion/core'
21-
import { ThemeProvider } from 'emotion-theming'
26+
import { jsx, ThemeProvider } from '@emotion/core'
2227

2328
const theme = {
2429
colors: {
@@ -40,9 +45,8 @@ render(
4045
```jsx
4146
// @live
4247
/** @jsx jsx */
43-
import { jsx } from '@emotion/core'
48+
import { jsx, ThemeProvider } from '@emotion/core'
4449
import styled from '@emotion/styled'
45-
import { ThemeProvider } from 'emotion-theming'
4650

4751
const theme = {
4852
colors: {
@@ -66,8 +70,7 @@ render(
6670
```jsx
6771
// @live
6872
/** @jsx jsx */
69-
import { jsx } from '@emotion/core'
70-
import { ThemeProvider, useTheme } from 'emotion-theming'
73+
import { jsx, ThemeProvider, useTheme } from '@emotion/core'
7174

7275
const theme = {
7376
colors: {
@@ -92,3 +95,103 @@ render(
9295
)
9396
```
9497

98+
## API
99+
100+
### ThemeProvider: React.ComponentType
101+
102+
A React component that passes the theme object down the component tree via [context](https://reactjs.org/docs/context.html). Additional `ThemeProvider` components can be added deeper in the tree to override the original theme. The theme object will be merged into its ancestor as if by [`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign). If a function is passed instead of an object it will be called with the ancestor theme and the result will be the new theme.
103+
104+
_Accepts:_
105+
106+
- **`children`: React.Node**
107+
- **`theme`: Object|Object => Object** - An object or function that provides an object.
108+
109+
```jsx
110+
import * as React from 'react'
111+
import styled from '@emotion/styled'
112+
import { ThemeProvider, withTheme } from '@emotion/core'
113+
114+
// object-style theme
115+
116+
const theme = {
117+
backgroundColor: 'green',
118+
color: 'red'
119+
}
120+
121+
// function-style theme; note that if multiple <ThemeProvider> are used,
122+
// the parent theme will be passed as a function argument
123+
124+
const adjustedTheme = ancestorTheme => ({ ...ancestorTheme, color: 'blue' })
125+
126+
class Container extends React.Component {
127+
render() {
128+
return (
129+
<ThemeProvider theme={theme}>
130+
<ThemeProvider theme={adjustedTheme}>
131+
<Text>Boom shaka laka!</Text>
132+
</ThemeProvider>
133+
</ThemeProvider>
134+
)
135+
}
136+
}
137+
```
138+
139+
> Note:
140+
>
141+
> Make sure to hoist your theme out of render otherwise you may have performance problems.
142+
143+
### withTheme(component: React.ComponentType): React.ComponentType
144+
145+
A higher-order component that provides the current theme as a prop to the wrapped child and listens for changes. If the theme is updated, the child component will be re-rendered accordingly.
146+
147+
```jsx
148+
import * as PropTypes from 'prop-types'
149+
import * as React from 'react'
150+
import { withTheme } from '@emotion/core'
151+
152+
class TellMeTheColor extends React.Component {
153+
render() {
154+
return <div>The color is {this.props.theme.color}.</div>
155+
}
156+
}
157+
158+
TellMeTheColor.propTypes = {
159+
theme: PropTypes.shape({
160+
color: PropTypes.string
161+
})
162+
}
163+
164+
const TellMeTheColorWithTheme = withTheme(TellMeTheColor)
165+
```
166+
167+
### useTheme
168+
169+
A React hook that provides the current theme as its value. If the theme is updated, the child component will be re-rendered accordingly.
170+
171+
```jsx
172+
// @live
173+
/** @jsx jsx */
174+
import { jsx, ThemeProvider, useTheme } from '@emotion/core'
175+
import styled from '@emotion/styled'
176+
177+
const theme = {
178+
colors: {
179+
primary: 'hotpink'
180+
}
181+
}
182+
183+
function SomeText(props) {
184+
const theme = useTheme()
185+
return <div css={{ color: theme.colors.primary }} {...props} />
186+
}
187+
188+
render(
189+
<ThemeProvider theme={theme}>
190+
<SomeText>some text</SomeText>
191+
</ThemeProvider>
192+
)
193+
```
194+
195+
## Credits
196+
197+
Thanks goes to the [styled-components team](https://github.com/styled-components/styled-components) and [their contributors](https://github.com/styled-components/styled-components/graphs/contributors) who designed this API.

docs/typescript.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ _styled.tsx_
254254

255255
```tsx
256256
import styled, { CreateStyled } from '@emotion/styled'
257-
import * as emotionTheming from 'emotion-theming'
257+
import { useTheme, ThemeProvider, EmotionTheming } from '@emotion/core'
258258

259259
type Theme = {
260260
color: {
@@ -268,8 +268,8 @@ type Theme = {
268268
export default styled as CreateStyled<Theme>
269269

270270
// You can also create themed versions of all the other theme helpers and hooks
271-
const { ThemeProvider, withTheme, useTheme } = emotionTheming as emotionTheming.EmotionTheming<Theme>
272-
export { ThemeProvider, withTheme, useTheme }
271+
const { ThemeProvider, useTheme } = { ThemeProvider, useTheme } as EmotionTheming<Theme>
272+
export { ThemeProvider, useTheme }
273273
```
274274

275275
_Button.tsx_
@@ -308,4 +308,4 @@ const StyledComponent0 = styled(Component)`
308308
background: ${(props: StyledComponentProps & ComponentProps) =>
309309
props.bgColor};
310310
`
311-
```
311+
```

packages/core/__tests__/class-names.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// @flow
22
import * as React from 'react'
33
import 'test-utils/next-env'
4-
import { ClassNames } from '@emotion/core'
5-
import { ThemeProvider } from 'emotion-theming'
4+
import { ClassNames, ThemeProvider } from '@emotion/core'
65
import renderer from 'react-test-renderer'
76

87
test('css', () => {

packages/core/__tests__/css.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
/** @jsx jsx */
33
import 'test-utils/next-env'
44
import * as React from 'react'
5-
import { jsx, css, CacheProvider } from '@emotion/core'
6-
import { ThemeProvider } from 'emotion-theming'
5+
import { jsx, css, CacheProvider, ThemeProvider } from '@emotion/core'
76
import { render } from '@testing-library/react'
87
import renderer from 'react-test-renderer'
98
import createCache from '@emotion/cache'

packages/core/__tests__/global-with-theme.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
// @flow
22
import 'test-utils/dev-mode'
33
import * as React from 'react'
4-
import { ThemeProvider } from 'emotion-theming'
54
import { render, unmountComponentAtNode } from 'react-dom'
6-
import { Global } from '@emotion/core'
5+
import { Global, ThemeProvider } from '@emotion/core'
76

87
beforeEach(() => {
98
// $FlowFixMe

packages/emotion-theming/__tests__/theme-provider.dom.js renamed to packages/core/__tests__/theme-provider.dom.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import 'test-utils/next-env'
44
import 'test-utils/dev-mode'
55
import { throwIfFalsy, safeQuerySelector } from 'test-utils'
66
import * as React from 'react'
7-
import { ThemeProvider } from 'emotion-theming'
8-
import { jsx } from '@emotion/core'
7+
import { jsx, ThemeProvider } from '@emotion/core'
98
import { render } from 'react-dom'
109

1110
test('provider with theme value that changes', () => {

packages/emotion-theming/__tests__/theme-provider.js renamed to packages/core/__tests__/theme-provider.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
/** @jsx jsx */
33
import 'test-utils/next-env'
44
import { ignoreConsoleErrors } from 'test-utils'
5-
import { ThemeProvider } from 'emotion-theming'
6-
import { jsx } from '@emotion/core'
5+
import { jsx, ThemeProvider } from '@emotion/core'
76
import renderer from 'react-test-renderer'
87
import cases from 'jest-in-case'
98

packages/emotion-theming/__tests__/use-theme.js renamed to packages/core/__tests__/use-theme.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
/** @jsx jsx */
33
import 'test-utils/next-env'
44
import * as renderer from 'react-test-renderer'
5-
import { jsx } from '@emotion/core'
6-
import { useTheme, ThemeProvider } from 'emotion-theming'
5+
import { jsx, useTheme, ThemeProvider } from '@emotion/core'
76

87
test('useTheme works', () => {
98
function TestComponent(props) {

packages/emotion-theming/__tests__/with-theme.js renamed to packages/core/__tests__/with-theme.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @flow
22
import * as React from 'react'
33
import * as renderer from 'react-test-renderer'
4-
import { withTheme, ThemeProvider } from 'emotion-theming'
4+
import { withTheme, ThemeProvider } from '@emotion/core'
55

66
test('withTheme works', () => {
77
class SomeComponent extends React.Component<{ theme: Object }> {

packages/core/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
"@emotion/css": "^11.0.0-next.3",
2828
"@emotion/serialize": "^0.11.15-next.1",
2929
"@emotion/sheet": "0.9.3",
30-
"@emotion/utils": "0.11.2"
30+
"@emotion/utils": "0.11.2",
31+
"@emotion/weak-memoize": "0.2.4",
32+
"hoist-non-react-statics": "^3.3.1"
3133
},
3234
"peerDependencies": {
3335
"@babel/core": "^7.0.0",
@@ -45,7 +47,6 @@
4547
"dtslint": "^0.3.0",
4648
"emotion": "^11.0.0-next.0",
4749
"emotion-server": "^11.0.0-next.0",
48-
"emotion-theming": "^11.0.0-next.5",
4950
"html-tag-names": "^1.1.2",
5051
"react": "^16.11.0",
5152
"svg-tag-names": "^1.1.1"

packages/core/src/class-names.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// @flow
22
import * as React from 'react'
3-
import { useContext } from 'react'
43
import { getRegisteredStyles, insertStyles } from '@emotion/utils'
54
import { serializeStyles } from '@emotion/serialize'
6-
import { withEmotionCache, ThemeContext } from './context'
5+
import { withEmotionCache } from './context'
6+
import { ThemeContext } from './theming'
77
import { isBrowser } from './utils'
88

99
type ClassNameArg =
@@ -112,7 +112,7 @@ export const ClassNames: React.AbstractComponent<
112112
let content = {
113113
css,
114114
cx,
115-
theme: useContext(ThemeContext)
115+
theme: React.useContext(ThemeContext)
116116
}
117117
let ele = props.children(content)
118118
hasRendered = true

packages/core/src/context.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ let EmotionCacheContext: React.Context<EmotionCache | null> = React.createContex
1515
typeof HTMLElement !== 'undefined' ? createCache() : null
1616
)
1717

18-
export let ThemeContext = React.createContext<Object>({})
1918
export let CacheProvider = EmotionCacheContext.Provider
2019

2120
let withEmotionCache = function withEmotionCache<Props, Ref: React.Ref<*>>(

packages/core/src/global.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @flow
22
import * as React from 'react'
3-
import { withEmotionCache, ThemeContext } from './context'
3+
import { withEmotionCache } from './context'
4+
import { ThemeContext } from './theming'
45
import { insertStyles } from '@emotion/utils'
56
import { isBrowser } from './utils'
67

packages/core/src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// @flow
2-
export { withEmotionCache, CacheProvider, ThemeContext } from './context'
2+
export { withEmotionCache, CacheProvider } from './context'
33
export { jsx } from './jsx'
44
export { Global } from './global'
55
export { keyframes } from './keyframes'
66
export { ClassNames } from './class-names'
7+
export { ThemeContext, useTheme, ThemeProvider, withTheme } from './theming'
78
export { default as css } from './css'

packages/core/src/jsx.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @flow
22
import * as React from 'react'
3-
import { withEmotionCache, ThemeContext } from './context'
3+
import { withEmotionCache } from './context'
4+
import { ThemeContext } from './theming'
45
import { getRegisteredStyles, insertStyles } from '@emotion/utils'
56
import { isBrowser } from './utils'
67
import { serializeStyles } from '@emotion/serialize'

packages/emotion-theming/src/theme-provider.js renamed to packages/core/src/theming.js

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// @flow
22
import * as React from 'react'
3-
import { ThemeContext } from '@emotion/core'
43
import weakMemoize from '@emotion/weak-memoize'
4+
import hoistNonReactStatics from 'hoist-non-react-statics'
55

6-
let getTheme = (outerTheme: Object, theme: Object | (Object => Object)) => {
6+
export const ThemeContext = React.createContext<Object>({})
7+
8+
export const useTheme = () => React.useContext(ThemeContext)
9+
10+
const getTheme = (outerTheme: Object, theme: Object | (Object => Object)) => {
711
if (typeof theme === 'function') {
812
const mergedTheme = theme(outerTheme)
913
if (
@@ -36,12 +40,12 @@ let createCacheWithTheme = weakMemoize(outerTheme => {
3640
})
3741
})
3842

39-
type Props = {
43+
type ThemeProviderProps = {
4044
theme: Object | (Object => Object),
4145
children: React.Node
4246
}
4347

44-
let ThemeProvider = (props: Props) => {
48+
export const ThemeProvider = (props: ThemeProviderProps) => {
4549
let theme = React.useContext(ThemeContext)
4650

4751
if (props.theme !== theme) {
@@ -54,4 +58,19 @@ let ThemeProvider = (props: Props) => {
5458
)
5559
}
5660

57-
export default ThemeProvider
61+
export function withTheme<Config: {}>(
62+
Component: React.AbstractComponent<Config>
63+
): React.AbstractComponent<$Diff<Config, { theme: Object }>> {
64+
const componentName = Component.displayName || Component.name || 'Component'
65+
let render = (props, ref) => {
66+
let theme = React.useContext(ThemeContext)
67+
68+
return <Component theme={theme} ref={ref} {...props} />
69+
}
70+
// $FlowFixMe
71+
let WithTheme = React.forwardRef(render)
72+
73+
WithTheme.displayName = `WithTheme(${componentName})`
74+
75+
return hoistNonReactStatics(WithTheme, Component)
76+
}
File renamed without changes.

0 commit comments

Comments
 (0)