Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Use Hooks #967

Merged
merged 34 commits into from
Nov 5, 2019
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4c74a12
Tests are broken :( but it works
emmatown Oct 26, 2018
2560905
Add displayName
emmatown Oct 26, 2018
b0a2363
Use ref
emmatown Oct 26, 2018
1608f42
Remove stuff
emmatown Oct 26, 2018
306886f
Update react-test-renderer
emmatown Oct 26, 2018
6b1bbfc
Use useContext in more places
emmatown Oct 26, 2018
3b84076
Fix a Global insertion order bug
emmatown Oct 26, 2018
44f6c7e
Add a test
emmatown Oct 26, 2018
6c5fa1a
Fix typo
emmatown Oct 26, 2018
587b561
Merge branch 'master' into hooks
emmatown Oct 26, 2018
dd42c9c
Merge branch 'master' into hooks
emmatown Oct 27, 2018
897fede
Remove passing the theme to the css prop, Global and ClassNames
emmatown Oct 28, 2018
1aa4655
Merge branch 'master' into hooks
emmatown Oct 31, 2018
2bf9383
Merge branch 'master' into hooks
emmatown Nov 25, 2018
9d8cc37
stuff
emmatown Nov 25, 2018
e725feb
stuff
emmatown Nov 25, 2018
fae9f0a
changes
emmatown Nov 25, 2018
33df35a
Merge branch 'master' into hooks
emmatown Nov 25, 2018
d1e3293
Add a test back
emmatown Nov 25, 2018
b899c8b
Fix a thing
emmatown Nov 25, 2018
cb0feb2
Update stuff
emmatown Nov 25, 2018
70118b7
Merge branch 'master' into hooks
emmatown May 22, 2019
7e9b819
Update things
emmatown May 22, 2019
4890527
Merge branch 'next' into hooks
Andarist Nov 5, 2019
95b1398
Drop support for innerRef entirely
Andarist Nov 5, 2019
8b6bee6
Set correct peerDeps on react + upgrade react-related devDeps
Andarist Nov 5, 2019
407a062
Merge branch 'next' into hooks
Andarist Nov 5, 2019
6e98780
Fix tests
Andarist Nov 5, 2019
a6439f2
Fix linting error
Andarist Nov 5, 2019
bd7222e
Remove custom useContext flow types
Andarist Nov 5, 2019
65d1ccc
Fix flow error
Andarist Nov 5, 2019
e45467d
Fix layour effect input array to include serialized.name instead of w…
Andarist Nov 5, 2019
1103861
Add a changeset
emmatown Nov 5, 2019
6af574b
Add another changeset
emmatown Nov 5, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/--documentation-issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ assignees: ''

<!--
If existing documentation is available, but needs to be improved,
is wrong, or unclear, please post here the relevant
is wrong, or unclear, please post here the relevant
https://emotion.sh/docs URLs
-->
1 change: 0 additions & 1 deletion .github/ISSUE_TEMPLATE/--feature-request.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ labels: feature request, needs triage
assignees: ''
---


**The problem**

<!-- A clear and concise description of what the problem is. e.g. I'm always frustrated when [...] -->
Expand Down
7 changes: 6 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,27 @@ merge of your pull request!
-->

<!-- What changes are being made? (What feature/bug is being fixed here?) -->

**What**:

<!-- Why are these changes necessary? -->

**Why**:

<!-- How were these changes implemented? -->

**How**:

<!-- Have you done all of these things? -->

**Checklist**:

<!-- add "N/A" to the end of each line that's irrelevant to your changes -->
<!-- to check an item, place an "x" in the box like so: "- [x] Documentation" -->

- [ ] Documentation
- [ ] Tests
- [ ] Code complete
- [ ] Changeset <!-- This is necessary if your changes should release any packages. Run `yarn changeset` to create a changeset -->


<!-- feel free to add additional comments -->
6 changes: 3 additions & 3 deletions docs/css-prop.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ There are 2 ways to get started with the `css` prop.

Both methods result in the same compiled code.
After adding the preset or setting the pragma as a comment, compiled jsx code will use emotion's `jsx` function instead of `React.createElement`.
| | Input | Output |

| | Input | Output |
| ------ | -------------------------- | --------------------------------------------------- |
| Before | `<img src="avatar.png" />` | `React.createElement('img', { src: 'avatar.png' })` |
| After | `<img src="avatar.png" />` | `jsx('img', { src: 'avatar.png' })` |
| After | `<img src="avatar.png" />` | `jsx('img', { src: 'avatar.png' })` |

#### Babel Preset

Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"release": "yarn build && changeset publish"
},
"resolutions": {
"**/react": "16.8.1",
"**/react-dom": "16.8.1",
"**/react": "16.11.0",
"**/react-dom": "16.11.0",
"**/browserslist": "^3.2.8",
"**/graphql-type-json": "0.2.4"
},
Expand Down Expand Up @@ -174,10 +174,10 @@
"@manypkg/cli": "^0.5.2",
"@mdx-js/mdx": "^1.1.0",
"@mdx-js/react": "^1.0.27",
"@testing-library/react": "^8.0.2",
"@testing-library/react": "^9.3.2",
"@types/jest": "^23.0.2",
"@types/node": "^10.11.4",
"@types/react": "^16.8.20",
"@types/react": "^16.9.11",
"babel-check-duplicated-nodes": "^1.0.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
Expand Down Expand Up @@ -236,9 +236,9 @@
"puppeteer": "^1.6.0",
"raf": "^3.4.0",
"razzle": "^2.4.0",
"react": "^16.5.2",
"react": "^16.11.0",
"react-art": "^16.5.2",
"react-dom": "^16.5.2",
"react-dom": "^16.11.0",
"react-helmet": "^5.2.0",
"react-icons": "^2.2.7",
"react-live": "1.10.0",
Expand All @@ -247,7 +247,7 @@
"react-primitives": "^0.7.0",
"react-router-dom": "^4.2.2",
"react-scripts": "1.1.5",
"react-test-renderer": "^16.3.2",
"react-test-renderer": "16.8.6",
"stylis": "3.5.4",
"stylis-rule-sheet": "^0.0.10",
"svg-tag-names": "^1.1.1",
Expand Down
1 change: 0 additions & 1 deletion packages/core/__tests__/class-names.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ it('should get the theme', () => {
</ClassNames>
</ThemeProvider>
)

expect(tree.toJSON()).toMatchSnapshot()
})

Expand Down
1 change: 0 additions & 1 deletion packages/core/__tests__/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ test('theming with the css prop', () => {
<div css={theme => ({ color: theme.primary })} />
</ThemeProvider>
)

expect(tree.toJSON()).toMatchSnapshot()
})

Expand Down
33 changes: 33 additions & 0 deletions packages/core/__tests__/global-insertion-after-others.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/** @jsx jsx */
import { jsx, Global } from '@emotion/core'
import { render } from '@testing-library/react'

let getDataAttributes = () =>
Array.from(document.querySelectorAll('style[data-emotion]'), x =>
x.getAttribute('data-emotion')
)

test('Global style element insertion after insertion of other styles', () => {
let Comp = ({ second }) => (
<div>
<div
css={{
color: 'green'
}}
/>
{second && (
<Global
styles={{
html: {
backgroundColor: 'hotpink'
}
}}
/>
)}
</div>
)
let { rerender } = render(<Comp />)
expect(getDataAttributes()).toEqual(['css'])
rerender(<Comp second />)
expect(getDataAttributes()).toEqual(['css-global', 'css'])
})
6 changes: 3 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,17 @@
"@emotion/utils": "0.11.2"
},
"peerDependencies": {
"react": ">=16.3.0"
"react": ">=16.8.0"
},
"devDependencies": {
"@emotion/styled": "^11.0.0-next.0",
"@types/react": "^16.8.20",
"@types/react": "^16.9.11",
"dtslint": "^0.3.0",
"emotion": "^11.0.0-next.0",
"emotion-server": "^11.0.0-next.0",
"emotion-theming": "^11.0.0-next.0",
"html-tag-names": "^1.1.2",
"react": "^16.5.2",
"react": "^16.11.0",
"svg-tag-names": "^1.1.1"
},
"repository": "https://github.com/emotion-js/emotion/tree/master/packages/core",
Expand Down
109 changes: 54 additions & 55 deletions packages/core/src/class-names.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import * as React from 'react'
import { useContext } from 'react'
import { getRegisteredStyles, insertStyles } from '@emotion/utils'
import { serializeStyles } from '@emotion/serialize'
import { withEmotionCache, ThemeContext } from './context'
Expand Down Expand Up @@ -71,65 +72,63 @@ function merge(

type Props = {
children: ({
css: (...args: Array<any>) => string,
css: (...args: any) => string,
cx: (...args: Array<ClassNameArg>) => string,
theme: Object
}) => React.Node
}

export const ClassNames = withEmotionCache<Props>((props, context) => {
return (
<ThemeContext.Consumer>
{theme => {
let rules = ''
let serializedHashes = ''
let hasRendered = false
export const ClassNames: React.AbstractComponent<Props> = withEmotionCache(
(props, cache) => {
let rules = ''
let serializedHashes = ''
let hasRendered = false

let css = (...args: Array<any>) => {
if (hasRendered && process.env.NODE_ENV !== 'production') {
throw new Error('css can only be used during render')
}
let serialized = serializeStyles(args, context.registered)
if (isBrowser) {
insertStyles(context, serialized, false)
} else {
let res = insertStyles(context, serialized, false)
if (res !== undefined) {
rules += res
}
}
if (!isBrowser) {
serializedHashes += ` ${serialized.name}`
}
return `${context.key}-${serialized.name}`
}
let cx = (...args: Array<ClassNameArg>) => {
if (hasRendered && process.env.NODE_ENV !== 'production') {
throw new Error('cx can only be used during render')
}
return merge(context.registered, css, classnames(args))
}
let content = { css, cx, theme }
let ele = props.children(content)
hasRendered = true
if (!isBrowser && rules.length !== 0) {
return (
<React.Fragment>
<style
{...{
[`data-emotion-${context.key}`]: serializedHashes.substring(
1
),
dangerouslySetInnerHTML: { __html: rules },
nonce: context.sheet.nonce
}}
/>
{ele}
</React.Fragment>
)
let css = (...args: Array<any>) => {
if (hasRendered && process.env.NODE_ENV !== 'production') {
throw new Error('css can only be used during render')
}
let serialized = serializeStyles(args, cache.registered)
if (isBrowser) {
insertStyles(cache, serialized, false)
} else {
let res = insertStyles(cache, serialized, false)
if (res !== undefined) {
rules += res
}
return ele
}}
</ThemeContext.Consumer>
)
})
}
if (!isBrowser) {
serializedHashes += ` ${serialized.name}`
}
return `${cache.key}-${serialized.name}`
}
let cx = (...args: Array<ClassNameArg>) => {
if (hasRendered && process.env.NODE_ENV !== 'production') {
throw new Error('cx can only be used during render')
}
return merge(cache.registered, css, classnames(args))
}
let content = {
css,
cx,
theme: useContext(ThemeContext)
}
let ele = props.children(content)
hasRendered = true
if (!isBrowser && rules.length !== 0) {
return (
<React.Fragment>
<style
{...{
[`data-emotion-${cache.key}`]: serializedHashes.substring(1),
dangerouslySetInnerHTML: { __html: rules },
nonce: cache.sheet.nonce
}}
/>
{ele}
</React.Fragment>
)
}
return ele
}
)
75 changes: 28 additions & 47 deletions packages/core/src/context.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow
import { type EmotionCache } from '@emotion/utils'
import * as React from 'react'
import { useContext, forwardRef } from 'react'
import createCache from '@emotion/cache'
import { isBrowser } from './utils'

Expand All @@ -17,60 +18,40 @@ let EmotionCacheContext: React.Context<EmotionCache | null> = React.createContex
export let ThemeContext = React.createContext<Object>({})
export let CacheProvider = EmotionCacheContext.Provider

let withEmotionCache = function withEmotionCache<Props>(
func: (props: Props, cache: EmotionCache, ref: React.Ref<*>) => React.Node
): React.StatelessFunctionalComponent<Props> {
let render = (props: Props, ref: React.Ref<*>) => {
return (
<EmotionCacheContext.Consumer>
{(
// $FlowFixMe we know it won't be null
cache: EmotionCache
) => {
return func(props, cache, ref)
}}
</EmotionCacheContext.Consumer>
)
}
let withEmotionCache = function withEmotionCache<Props, Ref: React.Ref<*>>(
func: (props: Props, cache: EmotionCache, ref: Ref) => React.Node
): React.AbstractComponent<Props> {
// $FlowFixMe
return React.forwardRef(render)
return forwardRef((props: Props, ref: Ref) => {
// the cache will never be null in the browser
let cache = ((useContext(EmotionCacheContext): any): EmotionCache)

return func(props, cache, ref)
})
}

if (!isBrowser) {
class BasicProvider extends React.Component<
{ children: EmotionCache => React.Node },
{ value: EmotionCache }
> {
state = { value: createCache() }
render() {
return (
<EmotionCacheContext.Provider {...this.state}>
{this.props.children(this.state.value)}
</EmotionCacheContext.Provider>
)
}
}

withEmotionCache = function withEmotionCache<Props>(
func: (props: Props, cache: EmotionCache) => React.Node
): React.StatelessFunctionalComponent<Props> {
return (props: Props) => (
<EmotionCacheContext.Consumer>
{context => {
if (context === null) {
return (
<BasicProvider>
{newContext => {
return func(props, newContext)
}}
</BasicProvider>
)
} else {
return func(props, context)
}
}}
</EmotionCacheContext.Consumer>
)
return (props: Props) => {
let cache = useContext(EmotionCacheContext)
if (cache === null) {
// yes, we're potentially creating this on every render
// it doesn't actually matter though since it's only on the server
// so there will only every be a single render
// that could change in the future because of suspense and etc. but for now,
// this works and i don't want to optimise for a future thing that we aren't sure about
cache = createCache()
return (
<EmotionCacheContext.Provider value={cache}>
{func(props, cache)}
</EmotionCacheContext.Provider>
)
} else {
return func(props, cache)
}
}
}
}

Expand Down
Loading