Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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 flow-typed/npm/jest_v21.x.x.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ type JestExpectType = {
*/
toMatchSnapshot(name?: string): void,
toMatchInlineSnapshot(...args: any): void,

toThrowErrorMatchingInlineSnapshot(...args: any): void,
/**
* Use .toThrow to test that a function throws when it is called.
* If you want to test that a specific error gets thrown, you can provide an
Expand Down
32 changes: 32 additions & 0 deletions next-packages/core/__tests__/__snapshots__/style.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`css 1`] = `
.emotion-0 {
color: hotpink;
}

<div
className="emotion-0"
/>
`;

exports[`cx 1`] = `
.emotion-0 {
color: hotpink;
color: green;
}

<div
className="some-other-class emotion-0"
/>
`;

exports[`should get the theme 1`] = `
.emotion-0 {
color: green;
}

<div
className="emotion-0"
/>
`;
86 changes: 86 additions & 0 deletions next-packages/core/__tests__/style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// @flow
import * as React from 'react'
import 'test-utils/next-env'
import { Style } from '@emotion/core'
import { ThemeProvider } from 'emotion-theming'
import renderer from 'react-test-renderer'

test('css', () => {
const tree = renderer.create(
<Style>
{({ css }) => (
<div
className={css`
color: hotpink;
`}
/>
)}
</Style>
)

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

it('should get the theme', () => {
const tree = renderer.create(
<ThemeProvider theme={{ color: 'green' }}>
<Style>
{({ css, theme }) => (
<div
className={css`
color: ${theme.color};
`}
/>
)}
</Style>
</ThemeProvider>
)

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

test('cx', () => {
const tree = renderer.create(
<Style>
{({ css, cx }) => {
let secondClassButItsInsertedFirst = css`
color: green;
`
let firstClassButItsInsertedSecond = css`
color: hotpink;
`

return (
<div
className={cx(
firstClassButItsInsertedSecond,
'some-other-class',
secondClassButItsInsertedFirst
)}
/>
)
}}
</Style>
)

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

test('css and cx throws when used after render', () => {
let cx, css
renderer.create(
<Style>
{arg => {
;({ cx, css } = arg)
return null
}}
</Style>
)

expect(cx).toThrowErrorMatchingInlineSnapshot(
`"cx can only be used during render"`
)
expect(css).toThrowErrorMatchingInlineSnapshot(
`"css can only be used during render"`
)
})
1 change: 1 addition & 0 deletions next-packages/core/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export { withCSSContext, Provider } from './context'
export { jsx } from './jsx'
export { Global } from './global'
export { keyframes } from './keyframes'
export { Style } from './style'
export { default as css } from '@emotion/css'
127 changes: 127 additions & 0 deletions next-packages/core/src/style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// @flow
import * as React from 'react'
import {
getRegisteredStyles,
insertStyles,
isBrowser,
getClassName
} from '@emotion/utils'
import { serializeStyles } from '@emotion/serialize'
import { withCSSContext } from '@emotion/core'

type ClassNameArg =
| string
| boolean
| (() => ClassNameArg)
| { [key: string]: boolean }
| Array<ClassNameArg>

let classnames = (args: Array<ClassNameArg>): string => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can u explain more why this is duplicated? I suspect it's like a global caching registry issue, but couldnt this share the implementation with original cx, css & merge?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m just lazy, I think I’ll move it and merge into @emotion/utils. There’s no point in sharing css and cx since they need slightly different things in here and in create-emotion.

let len = args.length
let i = 0
let cls = ''
for (; i < len; i++) {
let arg = args[i]
if (arg == null) continue

let toAdd
switch (typeof arg) {
case 'boolean':
break
case 'function':
if (process.env.NODE_ENV !== 'production') {
console.error(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this needed? it's already "next major version of Emotion" :D

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, I made this a while ago and copied classnames from v9.

'Passing functions to cx is deprecated and will be removed in the next major version of Emotion.\n' +
'Please call the function before passing it to cx.'
)
}
toAdd = classnames([arg()])
break
case 'object': {
if (Array.isArray(arg)) {
toAdd = classnames(arg)
} else {
toAdd = ''
for (const k in arg) {
if (arg[k] && k) {
toAdd && (toAdd += ' ')
toAdd += k
}
}
}
break
}
default: {
toAdd = arg
}
}
if (toAdd) {
cls && (cls += ' ')
cls += toAdd
}
}
return cls
}
function merge(registered: Object, css: (*) => string, className: string) {
const registeredStyles = []

const rawClassName = getRegisteredStyles(
registered,
registeredStyles,
className
)

if (registeredStyles.length < 2) {
return className
}
return rawClassName + css(registeredStyles)
}

export const Style = withCSSContext((props, context) => {
let rules = ''
let serializedHashes = ''
let hasRendered = false

let css = (...args) => {
if (hasRendered && process.env.NODE_ENV !== 'production') {
throw new Error('css can only be used during render')
}
let serialized = serializeStyles(context.registered, args)
if (isBrowser) {
insertStyles(context, serialized, false)
} else {
let res = insertStyles(context, serialized, false)
if (res !== undefined) {
rules += res
}
}
if (!isBrowser) {
serializedHashes += ` ${serialized.name}`
}
return getClassName(context, serialized)
}
let cx = (...args) => {
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: context.theme }
let ele = props.children(content)
hasRendered = true
if (!isBrowser && rules !== undefined) {
return (
<React.Fragment>
<style
{...{
[`data-emotion-${context.key}`]: serializedHashes.substring(1),
dangerouslySetInnerHTML: { __html: rules },
nonce: context.sheet.nonce
}}
/>
{ele}
</React.Fragment>
)
}
return ele
})