diff --git a/example/.babelrc b/example/.babelrc index 7a1353afd..610172959 100644 --- a/example/.babelrc +++ b/example/.babelrc @@ -4,13 +4,16 @@ "env", { "modules": false, - "loose": true + "loose": true, + "targets": { + "uglify": true + } } ], "stage-2", "react" ], "plugins": [ - 'emotion/babel' + ['emotion/babel'] ] } diff --git a/example/package.json b/example/package.json index 093c270a1..7221c15d3 100755 --- a/example/package.json +++ b/example/package.json @@ -15,8 +15,8 @@ "devDependencies": { "babel-core": "^6.23.1", "babel-eslint": "^7.2.3", - "babel-loader": "^7.0.0", - "babel-preset-env": "^1.5.1", + "babel-loader": "^7.1.1", + "babel-preset-env": "^1.6.0", "babel-preset-react": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "babel-standalone": "^6.24.2", diff --git a/example/src/blocks/composes.example b/example/src/blocks/composes.example index 421a99753..5c0cd2335 100644 --- a/example/src/blocks/composes.example +++ b/example/src/blocks/composes.example @@ -13,4 +13,4 @@ const Avatar = styled('img')` } ` -render(, mountNode) +render(, mountNode) diff --git a/example/src/blocks/css-prop.example b/example/src/blocks/css-prop.example index 815388e39..023b0d858 100644 --- a/example/src/blocks/css-prop.example +++ b/example/src/blocks/css-prop.example @@ -12,7 +12,7 @@ render( background-color: #8c81d8; border-radius: 4px; - img { + & img { width: 96px; height: 96px; border-radius: 50%; @@ -24,7 +24,7 @@ render( } `} > - + , mountNode ) diff --git a/example/src/blocks/intro.example b/example/src/blocks/intro.example index 917ed86f9..326946f1c 100644 --- a/example/src/blocks/intro.example +++ b/example/src/blocks/intro.example @@ -4,4 +4,4 @@ const Avatar = styled('img')` border-radius: 50%; ` -render(, mountNode) +render(, mountNode) diff --git a/example/src/blocks/keyframes.example b/example/src/blocks/keyframes.example index e49379e2e..4ce1be48a 100644 --- a/example/src/blocks/keyframes.example +++ b/example/src/blocks/keyframes.example @@ -25,6 +25,6 @@ const Avatar = styled('img')` ` render( - , + , mountNode ) diff --git a/example/src/blocks/media.example b/example/src/blocks/media.example index a63653867..04b8d5a27 100644 --- a/example/src/blocks/media.example +++ b/example/src/blocks/media.example @@ -10,6 +10,6 @@ const Avatar = styled('img')` ` render( - , + , mountNode ) diff --git a/example/src/blocks/named.example b/example/src/blocks/named.example index ecd6b2fe0..2b4704f67 100644 --- a/example/src/blocks/named.example +++ b/example/src/blocks/named.example @@ -6,6 +6,6 @@ const Avatar = styled('img')` ` render( - , + , mountNode ) diff --git a/example/src/blocks/nested.example b/example/src/blocks/nested.example index 06ebdd237..b660f7b44 100644 --- a/example/src/blocks/nested.example +++ b/example/src/blocks/nested.example @@ -1,5 +1,5 @@ const Avatar = styled('div')` - img { + & img { width: 96px; height: 96px; border-radius: 50%; @@ -7,6 +7,6 @@ const Avatar = styled('div')` ` render( - , + , mountNode ) diff --git a/example/src/blocks/objects.example b/example/src/blocks/objects.example index c3adb4f89..0ab68d334 100644 --- a/example/src/blocks/objects.example +++ b/example/src/blocks/objects.example @@ -3,6 +3,25 @@ const imageStyles = css({ height: 96 }) +const fakeBlue = css([ + { + color: "blue" + } +]); + +const red = css([ + { + color: "red" + } +]); + +const blue = css([ + red, + { + color: "blue" + } +]); + const prettyStyles = css([ { borderRadius: '50%', @@ -11,29 +30,14 @@ const prettyStyles = css([ transform: 'scale(1.2)' } }, - { border: '3px solid #8c81d8' } + {border: '3px solid currentColor'} ]) const Avatar = styled('img')` - composes: ${prettyStyles} ${imageStyles}; + composes: ${prettyStyles} ${imageStyles} ${blue} ` -const AvatarContainer = styled.div( - imageStyles, - { - borderRadius: 10, - display: 'flex', - alignItems: 'center', - justifyContent: 'center' - }, - props => ({ - backgroundColor: props.backgroundColor - }) -) - render( - - - , + , mountNode ) diff --git a/example/src/blocks/props.example b/example/src/blocks/props.example index 51b11b91a..4b1457329 100644 --- a/example/src/blocks/props.example +++ b/example/src/blocks/props.example @@ -6,6 +6,6 @@ const Avatar = styled('img')` ` render( - , + , mountNode ) diff --git a/example/src/blocks/pseudo.example b/example/src/blocks/pseudo.example index 448ec0334..8a9629881 100644 --- a/example/src/blocks/pseudo.example +++ b/example/src/blocks/pseudo.example @@ -10,6 +10,6 @@ const Avatar = styled('img')` ` render( - , + , mountNode ) diff --git a/example/src/blocks/styled-with-object.example b/example/src/blocks/styled-with-object.example index e6401c9df..4ad40987c 100644 --- a/example/src/blocks/styled-with-object.example +++ b/example/src/blocks/styled-with-object.example @@ -4,4 +4,4 @@ const Avatar = styled('img')({ borderRadius: '50%' }) -render(, mountNode) +render(, mountNode) diff --git a/example/src/blocks/styling-any-component.example b/example/src/blocks/styling-any-component.example index 16c56317a..e5d0f4d02 100644 --- a/example/src/blocks/styling-any-component.example +++ b/example/src/blocks/styling-any-component.example @@ -7,4 +7,4 @@ const Avatar = styled(Image)` border-radius: 50%; ` -render(, mountNode) +render(, mountNode) diff --git a/example/src/blocks/theming.example b/example/src/blocks/theming.example index 38b8e1a15..d5d1e15c7 100644 --- a/example/src/blocks/theming.example +++ b/example/src/blocks/theming.example @@ -14,7 +14,7 @@ const Avatar = styled('img')` render( - + , mountNode ) diff --git a/example/src/main.emotion.css b/example/src/main.emotion.css deleted file mode 100644 index f6092f436..000000000 --- a/example/src/main.emotion.css +++ /dev/null @@ -1,27 +0,0 @@ -html, body { - font-family: -apple-system, - BlinkMacSystemFont, - "Segoe UI", - "Roboto", - "Roboto Light", - "Oxygen", - "Ubuntu", - "Cantarell", - "Fira Sans", - "Droid Sans", - "Helvetica Neue", - sans-serif, - "Apple Color Emoji", - "Segoe UI Emoji", - "Segoe UI Symbol"; - color: #495057; - width: 100%; - height: 100%; - padding: 0; - margin: 0; - } -@font-face {font-family: 'Oxygen'; - font-style: normal; - font-weight: 400; - src: local('Oxygen Regular'), local('Oxygen-Regular'), url(https://fonts.gstatic.com/s/oxygen/v6/qBSyz106i5ud7wkBU-FrPevvDin1pK8aKteLpeZ5c0A.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;} \ No newline at end of file diff --git a/example/src/main.js b/example/src/main.js index a25f0da4c..a00a6fd4c 100755 --- a/example/src/main.js +++ b/example/src/main.js @@ -1,7 +1,7 @@ import React from 'react' import { render } from 'react-dom' import { ThemeProvider } from 'emotion/react/theming' -import styled, { css, fontFace, keyframes, injectGlobal } from 'emotion/react' +import styled, { css, fontFace, keyframes, injectGlobal } from 'emotion/react' import Markdown from './markdown' import Playground from './playground' import logoUrl from '../../emotion.png' @@ -60,7 +60,7 @@ const theme = { const PlaygroundWrapper = styled('div')` font-family: 'Oxygen', sans-serif; flex:1; - color: attr(color, #343a40); + color: #343a40; background: #f8f9fa; & .inner { @@ -224,7 +224,7 @@ class App extends React.Component { {/* }} */} {/* /> */} - + p.theme.purple}; - p & { + & p & { margin: 0; } @@ -34,7 +35,7 @@ const Paragraph = styled('p')` font-size: 0.85rem; color: ${colors.gray[8]}; - a { + & a { font-size: 0.85rem; } @@ -47,7 +48,7 @@ const Code = styled('code')` background-color: ${colors.gray[1]}; padding: 1px; - p & { + & p & { font-size: 0.99rem; } ` diff --git a/example/src/playground/index.emotion.css b/example/src/playground/index.emotion.css deleted file mode 100644 index b601f13d6..000000000 --- a/example/src/playground/index.emotion.css +++ /dev/null @@ -1,96 +0,0 @@ -.css-dracula-uybcup .cm-s-dracula.CodeMirror, .css-dracula-uybcup .cm-s-dracula .CodeMirror-gutters { - background-color: #282a36 !important; - color: #f8f8f2 !important; - border: none; -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-gutters { - color: #282a36; -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-cursor { - border-left: solid thin #f8f8f2 !important; -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-linenumber { - color: #6D8A88; -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-selected { - background: rgba(255, 255, 255, 0.10); -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-line::selection, .css-dracula-uybcup .cm-s-dracula .CodeMirror-line > span::selection, .css-dracula-uybcup .cm-s-dracula .CodeMirror-line > span > span::selection { - background: rgba(255, 255, 255, 0.10); -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-line::-moz-selection, .css-dracula-uybcup .cm-s-dracula .CodeMirror-line > span::-moz-selection, .css-dracula-uybcup .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { - background: rgba(255, 255, 255, 0.10); -} -.css-dracula-uybcup .cm-s-dracula span.cm-comment { - color: #6272a4; -} -.css-dracula-uybcup .cm-s-dracula span.cm-string, .css-dracula-uybcup .cm-s-dracula span.cm-string-2 { - color: #f1fa8c; -} -.css-dracula-uybcup .cm-s-dracula span.cm-number { - color: #bd93f9; -} -.css-dracula-uybcup .cm-s-dracula span.cm-variable { - color: #50fa7b; -} -.css-dracula-uybcup .cm-s-dracula span.cm-variable-2 { - color: white; -} -.css-dracula-uybcup .cm-s-dracula span.cm-def { - color: #50fa7b; -} -.css-dracula-uybcup .cm-s-dracula span.cm-operator { - color: #ff79c6; -} -.css-dracula-uybcup .cm-s-dracula span.cm-keyword { - color: #ff79c6; -} -.css-dracula-uybcup .cm-s-dracula span.cm-atom { - color: #bd93f9; -} -.css-dracula-uybcup .cm-s-dracula span.cm-meta { - color: #f8f8f2; -} -.css-dracula-uybcup .cm-s-dracula span.cm-tag { - color: #ff79c6; -} -.css-dracula-uybcup .cm-s-dracula span.cm-attribute { - color: #50fa7b; -} -.css-dracula-uybcup .cm-s-dracula span.cm-qualifier { - color: #50fa7b; -} -.css-dracula-uybcup .cm-s-dracula span.cm-property { - color: #66d9ef; -} -.css-dracula-uybcup .cm-s-dracula span.cm-builtin { - color: #50fa7b; -} -.css-dracula-uybcup .cm-s-dracula span.cm-variable-3, .css-dracula-uybcup .cm-s-dracula span.cm-type { - color: #ffb86c; -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-activeline-background { - background: rgba(255, 255, 255, 0.1); -} -.css-dracula-uybcup .cm-s-dracula .CodeMirror-matchingbracket { - text-decoration: underline; - color: white !important; -} -.css-PlaygroundContent-1ecmb3 { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - margin: 0 auto 16px auto; - padding: 16px 0; - max-width: 560px; } -.css-PlaygroundContent-1ecmb3 .ReactCodeMirror { - -webkit-flex: 1 1 70%; - -ms-flex: 1 1 70%; - flex: 1 1 70%; } -.css-PlaygroundContent-1ecmb3 .ReactCodeMirror textarea { - border-radius: 4px; } -.css-PlaygroundContent-1ecmb3 .ReactCodeMirror .CodeMirror { - height: auto; - border-radius: 4px; } \ No newline at end of file diff --git a/example/src/playground/index.js b/example/src/playground/index.js index e95a608dd..a4683614e 100644 --- a/example/src/playground/index.js +++ b/example/src/playground/index.js @@ -11,105 +11,105 @@ import styled from 'emotion/react' import colors from 'open-color' const dracula = css` - .cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters { + & .cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters { background-color: #282a36 !important; color: #f8f8f2 !important; border: none; } - .cm-s-dracula .CodeMirror-gutters { + & .cm-s-dracula .CodeMirror-gutters { color: #282a36; } - .cm-s-dracula .CodeMirror-cursor { + & .cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f2 !important; } - .cm-s-dracula .CodeMirror-linenumber { + & .cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; } - .cm-s-dracula .CodeMirror-selected { + & .cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } - .cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { + & .cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); } - .cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { + & .cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); } - .cm-s-dracula span.cm-comment { + & .cm-s-dracula span.cm-comment { color: #6272a4; } - .cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { + & .cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; } - .cm-s-dracula span.cm-number { + & .cm-s-dracula span.cm-number { color: #bd93f9; } - .cm-s-dracula span.cm-variable { + & .cm-s-dracula span.cm-variable { color: #50fa7b; } - .cm-s-dracula span.cm-variable-2 { + & .cm-s-dracula span.cm-variable-2 { color: white; } - .cm-s-dracula span.cm-def { + & .cm-s-dracula span.cm-def { color: #50fa7b; } - .cm-s-dracula span.cm-operator { + & .cm-s-dracula span.cm-operator { color: #ff79c6; } - .cm-s-dracula span.cm-keyword { + & .cm-s-dracula span.cm-keyword { color: #ff79c6; } - .cm-s-dracula span.cm-atom { + & .cm-s-dracula span.cm-atom { color: #bd93f9; } - .cm-s-dracula span.cm-meta { + & .cm-s-dracula span.cm-meta { color: #f8f8f2; } - .cm-s-dracula span.cm-tag { + & .cm-s-dracula span.cm-tag { color: #ff79c6; } - .cm-s-dracula span.cm-attribute { + & .cm-s-dracula span.cm-attribute { color: #50fa7b; } - .cm-s-dracula span.cm-qualifier { + & .cm-s-dracula span.cm-qualifier { color: #50fa7b; } - .cm-s-dracula span.cm-property { + & .cm-s-dracula span.cm-property { color: #66d9ef; } - .cm-s-dracula span.cm-builtin { + & .cm-s-dracula span.cm-builtin { color: #50fa7b; } - .cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { + & .cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; } - .cm-s-dracula .CodeMirror-activeline-background { + & .cm-s-dracula .CodeMirror-activeline-background { background: rgba(255, 255, 255, 0.1); } - .cm-s-dracula .CodeMirror-matchingbracket { + & .cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } @@ -128,7 +128,7 @@ const PlaygroundContent = styled('div')` border-radius: 4px; } - .CodeMirror { + & .CodeMirror { height: auto; border-radius: 4px; } diff --git a/example/src/preview/index.js b/example/src/preview/index.js index 7e1263407..51b290c2b 100644 --- a/example/src/preview/index.js +++ b/example/src/preview/index.js @@ -5,9 +5,7 @@ import PropTypes from 'prop-types' import { registerPlugin, transform } from 'babel-standalone' import styled from 'emotion/react' -const ric = - window.requestIdleCallback || - window.requestAnimationFrame +const ric = window.requestIdleCallback || window.requestAnimationFrame registerPlugin('emotion/babel', require('emotion/babel')) diff --git a/example/webpack.config.js b/example/webpack.config.js index f7431c79b..e3e7b8b22 100755 --- a/example/webpack.config.js +++ b/example/webpack.config.js @@ -14,34 +14,32 @@ module.exports = env => { { test: /\.css$/, exclude: /emotion\.css$/, - use: PROD ? ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - options: { - sourceMap: true, - modules: true + use: PROD + ? ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: { + loader: 'css-loader', + options: { + sourceMap: true, + modules: true + } } - } - }) : [ - 'style-loader', - { loader: 'css-loader', options: { modules: true } } - ] + }) + : ['style-loader', { loader: 'css-loader', options: { modules: true } }] }, { test: /emotion\.css$/, - use: PROD ? ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: { - loader: 'css-loader', - options: { - sourceMap: true + use: PROD + ? ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: { + loader: 'css-loader', + options: { + sourceMap: true + } } - } - }) : [ - 'style-loader', - { loader: 'css-loader' } - ] + }) + : ['style-loader', { loader: 'css-loader' }] }, { test: /\.(jpg|png|svg)$/, diff --git a/jest-utils/index.js b/jest-utils/index.js deleted file mode 100644 index 82590fb4a..000000000 --- a/jest-utils/index.js +++ /dev/null @@ -1,7 +0,0 @@ -const matcher = require('./matcher') -const serializer = require('./serializer') - -module.exports = { - matcher, - serializer -} diff --git a/jest-utils/matcher.js b/jest-utils/matcher.js deleted file mode 100644 index 80f769045..000000000 --- a/jest-utils/matcher.js +++ /dev/null @@ -1,81 +0,0 @@ -const chalk = require('chalk') -const diff = require('jest-diff') -const stripAnsi = require('strip-ansi') -const { toMatchSnapshot } = require('jest-snapshot') - -const isAddition = line => /^\+/.test(line) - -const isDeletion = line => /^-/.test(line) - -const removeGlamorClassNames = line => - line.replace(/css-.+?( |")/, '').slice(1).trim() -const isClassNameAttribute = ( - line, - previous = '', - /* istanbul ignore next */ next = '' -) => { - const containsGlamorClassName = /class(Name)?=".*css-.*"/.test(line) - if (containsGlamorClassName) { - // just because the line contains a glamor class name doesn't mean that - // it doesn't also have some other change, so we'll remove everything - // except the glamor class name and see if it's the same as the previous - // or the next line. It's not really perfect, but it's less likely that - // we'll show something in white that shouldn't be :) - const lineRemoved = removeGlamorClassNames(line) - const previousRemoved = removeGlamorClassNames(previous) - const nextRemoved = removeGlamorClassNames(next) - return lineRemoved === previousRemoved || lineRemoved === nextRemoved - } - return containsGlamorClassName -} -const isDataAttribute = line => /data-css-.*/.test(line) -const isClassNameSelector = line => /\.css-.*,/.test(line) -const isDataSelector = line => /\[data-css-.*\] {/.test(line) - -const isClassName = (line, previous, next) => - (isAddition(line) || isDeletion(line)) && - (isClassNameAttribute(line, previous, next) || - isDataAttribute(line) || - isClassNameSelector(line) || - isDataSelector(line)) - -const colorize = message => { - const messageLines = message.split('\n') - - return messageLines - .map((line, index) => { - const previous = messageLines[index - 1] - const next = messageLines[index + 1] - if (isClassName(line, previous, next)) { - return chalk.white(line) - } - if (isAddition(line)) { - return chalk.red(line) - } - if (isDeletion(line)) { - return chalk.green(line) - } - return chalk.dim(line) - }) - .join('\n') -} - -const matcher = { - toMatchSnapshotWithEmotion (...args) { - const result = toMatchSnapshot.apply(this, args) - let message - - if (!result.pass) { - message = diff(result.expected, result.actual, { - aAnnotation: 'Snapshot', - bAnnotation: 'Received' - }) - message = stripAnsi(message) - message = colorize(message) - } - - return { pass: result.pass, message } - } -} - -module.exports = matcher diff --git a/jest-utils/serializer.js b/jest-utils/serializer.js deleted file mode 100644 index 6675fa9ac..000000000 --- a/jest-utils/serializer.js +++ /dev/null @@ -1,95 +0,0 @@ -const css = require('css') -const { sheet } = require('../src/index') - -const serializer = { test, print } - -module.exports = serializer - -function test (val) { - return ( - val && !val.withStyles && val.$$typeof === Symbol.for('react.test.json') - ) -} - -function print (val, printer) { - const selectors = getSelectors(val) - const styles = getStyles(selectors) - val.withStyles = true - const printedVal = printer(val) - if (styles) { - return `${styles}\n\n${printedVal}` - } else { - return printedVal - } -} - -function getSelectors (node) { - let selectors = [] - if (node.children && node.children.reduce) { - selectors = node.children.reduce( - (acc, child) => acc.concat(getSelectors(child)), - [] - ) - } - if (node.props) { - return getSelectorsFromProps(selectors, node.props) - } - return selectors -} - -function getSelectorsFromProps (selectors, props) { - const className = props.className || props.class - if (className) { - selectors = selectors.concat( - className.toString().split(' ').map(cn => `.${cn}`) - ) - } - const dataProps = Object.keys(props).reduce((dProps, key) => { - if (key.startsWith('data-')) { - dProps.push(`[${key}]`) - } - return dProps - }, []) - if (dataProps.length) { - selectors = selectors.concat(dataProps) - } - return selectors -} - -function getStyles (nodeSelectors) { - const styles = sheet.tags - .map(tag => /* istanbul ignore next */ tag.textContent || '') - .join('\n') - const ast = css.parse(styles) - const rules = ast.stylesheet.rules.filter(filter) - const mediaQueries = getMediaQueries(ast, filter) - - ast.stylesheet.rules = [...rules, ...mediaQueries] - - const ret = css.stringify(ast) - return ret - - function filter (rule) { - if (rule.type === 'rule') { - return rule.selectors.some(selector => { - const baseSelector = selector.split(/:| /)[0] - return nodeSelectors.includes(baseSelector) - }) - } - return false - } -} - -function getMediaQueries (ast, filter) { - return ast.stylesheet.rules - .filter(rule => rule.type === 'media') - .reduce((acc, mediaQuery) => { - mediaQuery.rules = mediaQuery.rules.filter(filter) - - if (mediaQuery.rules.length) { - return acc.concat(mediaQuery) - } - - return acc - }, []) -} diff --git a/package.json b/package.json index 4a61b198e..2878d966c 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "lib", "react", "server.js", - "vue", "dist/DO-NOT-USE.min.js", "macro.js" ], @@ -30,11 +29,16 @@ "format": "prettier-eslint --write \"src/**/*.js\" \"test/**/*.js\" \"example/**/*.js\" \"jest-utils/**/*.js\"" }, "dependencies": { - "autoprefixer": "^7.1.2", "@arr/filter.mutate": "^1.0.0", "@arr/foreach": "^1.0.0", "@arr/map": "^1.0.0", + "@arr/reduce": "^1.0.0", + "autoprefixer": "^7.1.2", + "babel-errors": "^1.1.1", "babel-plugin-syntax-jsx": "^6.18.0", + "babel-types": "^6.25.0", + "fbjs": "^0.8.12", + "inline-style-prefixer": "^3.0.6", "postcss-js": "^1.0.0", "styled-components": "2.0.0", "theming": "^1.0.1", @@ -51,10 +55,12 @@ "babel-preset-react": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "bundlesize": "^0.5.7", + "caniuse-api": "^2.0.0", + "enzyme": "^2.9.1", + "enzyme-to-json": "^1.5.1", "jest": "^20.0.4", "jest-cli": "^20.0.4", - "jest-emotion-react": "^0.0.2", - "jest-glamor-react": "^1.4.0", + "jest-glamor-react": "^2.0.0", "npm-run-all": "^4.0.2", "polished": "^1.2.1", "prettier-eslint-cli": "^4.0.3", @@ -67,6 +73,7 @@ "rollup-plugin-babel": "^2.7.1", "rollup-plugin-commonjs": "^8.0.2", "rollup-plugin-node-resolve": "^3.0.0", + "rollup-plugin-replace": "^1.1.1", "rollup-plugin-uglify": "^2.0.1", "standard": "^10.0.2", "vue": "^2.3.4" @@ -116,7 +123,7 @@ "bundlesize": [ { "path": "./dist/DO-NOT-USE.min.js", - "threshold": "3 Kb" + "threshold": "6 Kb" } ] } diff --git a/rollup.config.js b/rollup.config.js index ab2bf60dc..013d59d81 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -2,6 +2,7 @@ import resolve from 'rollup-plugin-node-resolve' import commonjs from 'rollup-plugin-commonjs' import babel from 'rollup-plugin-babel' import uglify from 'rollup-plugin-uglify' +import replace from 'rollup-plugin-replace' import pkg from './package.json' export default { @@ -30,6 +31,9 @@ export default { ignoreGlobal: true, include: 'node_modules/**' }), + replace({ + 'process.env.NODE_ENV': JSON.stringify('production') + }), uglify() ], targets: [ diff --git a/src/ast-object.js b/src/ast-object.js new file mode 100644 index 000000000..a19a58ce1 --- /dev/null +++ b/src/ast-object.js @@ -0,0 +1,204 @@ +import { forEach, reduce } from './utils' + +export default class ASTObject { + obj: { [string]: any } + expressions: Array + composesCount: number + t: any + + constructor (obj, expressions, composesCount, t) { + this.obj = obj + this.expressions = expressions + this.composesCount = composesCount + this.t = t + } + + toAST () { + const { obj, t } = this + + const props = [] + for (let key in obj) { + const rawValue = obj[key] + const { computed, composes, ast: keyAST } = this.objKeyToAst(key) + + let valueAST + if (composes) { + // valueAST = t.arrayExpression(expressions.slice(0, composesCount)) + continue + } else { + valueAST = this.objValueToAst(rawValue) + } + + props.push(t.objectProperty(keyAST, valueAST, computed)) + } + return t.objectExpression(props) + } + + objKeyToAst (key): { computed: boolean, ast: any, composes: boolean } { + const { t } = this + const matches = this.getDynamicMatches(key) + + if (matches.length) { + return { + computed: true, + composes: key === 'composes', + ast: this.replacePlaceholdersWithExpressions(matches, key) + } + } + + return { + computed: false, + composes: key === 'composes', + ast: t.stringLiteral(key) + } + } + + objValueToAst (value) { + const { expressions, composesCount, t } = this + + if (typeof value === 'string') { + const matches = this.getDynamicMatches(value) + if (matches.length) { + return this.replacePlaceholdersWithExpressions(matches, value) + } + return t.stringLiteral(value) + } else if (Array.isArray(value)) { + // should never hit here + return t.arrayExpression(value.map(v => this.objValueToAst(v))) + } + + const obj = new this.constructor(value, expressions, composesCount, t) + return obj.toAST() + } + + getDynamicMatches (str) { + const re = /xxx(\d+)xxx/gm + let match + const matches = [] + while ((match = re.exec(str)) !== null) { + matches.push({ + value: match[0], + p1: parseInt(match[1], 10), + index: match.index + }) + } + + return matches + } + + replacePlaceholdersWithExpressions (matches: any[], str: string) { + const { expressions, composesCount, t } = this + + const templateElements = [] + const templateExpressions = [] + let cursor = 0 + // not sure how to detect when to add 'px' + // let hasSingleInterpolation = false + forEach(matches, ({ value, p1, index }, i) => { + const preMatch = str.substring(cursor, index) + cursor = cursor + preMatch.length + value.length + if (preMatch) { + templateElements.push( + t.templateElement({ raw: preMatch, cooked: preMatch }) + ) + } else if (i === 0) { + templateElements.push(t.templateElement({ raw: '', cooked: '' })) + } + // if (value === str) { + // hasSingleInterpolation = true + // } + + templateExpressions.push( + expressions + ? expressions[p1 - composesCount] + : t.identifier(`x${p1 - composesCount}`) + ) + if (i === matches.length - 1) { + templateElements.push( + t.templateElement( + { + raw: str.substring(index + value.length), + cooked: str.substring(index + value.length) + }, + true + ) + ) + } + }) + // if (hasSingleInterpolation) { + // return templateExpressions[0] + // } + return t.templateLiteral(templateElements, templateExpressions) + } + + static fromAST (astObj, t) { + function isLiteral (value) { + return ( + t.isStringLiteral(value) || + t.isNumericLiteral(value) || + t.isBooleanLiteral(value) + ) + } + + let expressions = [] + + function replaceExpressionsWithPlaceholders (node) { + if (t.isArrayExpression(node)) { + return node.elements.map(replaceExpressionsWithPlaceholders) + } else if (isLiteral(node)) { + return node.value + } else if (t.isTemplateLiteral(node)) { + const strs = node.quasis.map(x => x.value.cooked) + const exprs = node.expressions + return reduce( + strs, + (arr, str, i) => { + arr.push(str) + if (i !== strs.length - 1) { + expressions.push(exprs[i]) + arr.push(`xxx${expressions.length - 1}xxx`) + } + return arr + }, + [] + ) + .join('') + .trim() + } + + expressions.push(node) + return `xxx${expressions.length - 1}xxx` + } + + function toObj (astObj) { + let obj = {} + + forEach(astObj.properties, property => { + // nested objects + let key + if (property.computed) { + key = replaceExpressionsWithPlaceholders(property.key) + } else { + key = t.isIdentifier(property.key) + ? property.key.name + : property.key.value + } + if (t.isObjectExpression(property.value)) { + obj[key] = toObj(property.value) + } else { + obj[key] = replaceExpressionsWithPlaceholders(property.value) + } + }) + + return obj + } + + const obj = toObj(astObj) + return new ASTObject( + obj, + expressions, + 0, // composesCount: we should support this, + t + ) + } +} diff --git a/src/attrs.js b/src/attrs.js deleted file mode 100644 index 4a1f0d323..000000000 --- a/src/attrs.js +++ /dev/null @@ -1,59 +0,0 @@ -export default function createAttrExpression (match, vars, composes, t) { - const placeholderRegex = /xxx(\d+)xxx/gm - const propNameMatch = placeholderRegex.exec(match.propName) - let propName = t.identifier(match.propName) - if (propNameMatch) { - const varsIndex = propNameMatch[1] - composes - propName = vars[varsIndex] - } - - const valueTypeMatch = placeholderRegex.exec(match.valueType) - let valueType = t.stringLiteral(match.valueType || '') - if (valueTypeMatch) { - const varsIndex = valueTypeMatch[1] - composes - valueType = vars[varsIndex] - } - - const defaultValueMatch = placeholderRegex.exec(match.defaultValue) - let defaultValue = t.stringLiteral(match.defaultValue || '') - if (defaultValueMatch) { - const varsIndex = defaultValueMatch[1] - composes - defaultValue = vars[varsIndex] - } - - let createMemberExpression = () => - t.memberExpression(t.identifier('props'), propName, !!propNameMatch) - - let returnValue = createMemberExpression() - - if (match.valueType) { - returnValue = t.binaryExpression( - '+', - createMemberExpression(), - valueType - ) - } - - if (match.defaultValue) { - returnValue = t.binaryExpression( - '+', - t.conditionalExpression( - createMemberExpression(), - createMemberExpression(), - defaultValue - ), - valueType - ) - } - - const body = t.blockStatement([t.returnStatement(returnValue)]) - - const expr = t.functionExpression( - t.identifier( - `get${propNameMatch ? 'Prop' : match.propName.charAt(0).toUpperCase() + match.propName.slice(1)}` - ), - [t.identifier('props')], - body - ) - return expr -} diff --git a/src/babel-utils.js b/src/babel-utils.js index 8d60eacd2..2687a2786 100644 --- a/src/babel-utils.js +++ b/src/babel-utils.js @@ -20,7 +20,9 @@ export function getRuntimeImportPath (path, t) { export function buildMacroRuntimeNode (path, state, importName, t) { const runtimeImportPath = getRuntimeImportPath(path, t) if (state.emotionImports === undefined) state.emotionImports = {} - if (state.emotionImports[runtimeImportPath] === undefined) { state.emotionImports[runtimeImportPath] = {} } + if (state.emotionImports[runtimeImportPath] === undefined) { + state.emotionImports[runtimeImportPath] = {} + } if (state.emotionImports[runtimeImportPath][importName] === undefined) { state.emotionImports[runtimeImportPath][ importName diff --git a/src/babel.js b/src/babel.js index b638bf808..948992822 100644 --- a/src/babel.js +++ b/src/babel.js @@ -1,142 +1,122 @@ +// @flow weak import fs from 'fs' import { basename } from 'path' import { touchSync } from 'touch' -import postcssJs from 'postcss-js' -import autoprefixer from 'autoprefixer' -import { inline, keyframes, fontFace, injectGlobal } from './inline' +import { inline } from './inline' +import { parseCSS } from './parser' import { getIdentifierName } from './babel-utils' +import { map } from './utils' import cssProps from './css-prop' -import createAttrExpression from './attrs' +import ASTObject from './ast-object' -function joinExpressionsWithSpaces (expressions, t) { - const quasis = [t.templateElement({ cooked: '', raw: '' }, true)] - expressions.forEach((x, i) => { - if (i === expressions.length - 1) { - return quasis.push(t.templateElement({ cooked: '', raw: '' }, true)) - } - quasis.push(t.templateElement({ cooked: ' ', raw: ' ' }, true)) - }) - return t.templateLiteral(quasis, expressions) -} +export function replaceCssWithCallExpression ( + path, + identifier, + state, + t, + staticCSSTextCreator = (name, hash, src) => `.${name}-${hash} { ${src} }`, + removePath = false +) { + try { + const { name, hash, src } = inline( + path.node.quasi, + getIdentifierName(path, t), + 'css' + ) + if (state.extractStatic && !path.node.quasi.expressions.length) { + const cssText = staticCSSTextCreator(name, hash, src) + const { staticCSSRules } = parseCSS(cssText, true) -function parseDynamicValues (rules, t, options) { - if (options.composes === undefined) options.composes = 0 - return rules.map(rule => { - const re = /xxx(\d+)xxx|attr\(([^,\s)]+)(?:\s*([^,)]*)?)(?:,\s*(\S+))?\)/gm - const VAR = 'VAR' - const ATTR = 'ATTR' - let match - const matches = [] - while ((match = re.exec(rule)) !== null) { - if (match[1]) { - matches.push({ - value: match[0], - p1: match[1], - index: match.index, - type: VAR - }) + state.insertStaticRules(staticCSSRules) + if (!removePath) { + return path.replaceWith(t.stringLiteral(`${name}-${hash}`)) + } + if (t.isExpressionStatement(path.parent)) { + path.parentPath.remove() } else { - matches.push({ - value: match[0], - propName: match[2], - valueType: match[3], - defaultValue: match[4], - index: match.index, - type: ATTR - }) + path.replaceWith(t.identifier('undefined')) } + return } - let cursor = 0 - const [quasis, expressions] = matches.reduce( - (accum, match, i) => { - const [quasis, expressions] = accum - const preMatch = rule.substring(cursor, match.index) - cursor = cursor + preMatch.length + match.value.length - if (preMatch) { - quasis.push(t.templateElement({ raw: preMatch, cooked: preMatch })) - } - if (match.type === VAR) { - if (options.inputExpressions) { - expressions.push( - options.inputExpressions[match.p1 - options.composes] - ) - } else { - expressions.push(t.identifier(`x${match.p1 - options.composes}`)) - } - } - if (match.type === ATTR) { - const expr = createAttrExpression( - match, - options.vars, - options.composes, - t - ) - options.vars.push(expr) - expressions.push(t.identifier(`x${options.vars.length - 1}`)) - } + const { styles, composesCount } = parseCSS(src, false) - if (i === matches.length - 1 && cursor <= rule.length) { - const postMatch = rule.substring(cursor) + if (!removePath) { + path.addComment('leading', '#__PURE__') + } - quasis.push( - t.templateElement( - { - raw: postMatch, - cooked: postMatch - }, - true + const composeValues = path.node.quasi.expressions.slice(0, composesCount) + const vars = path.node.quasi.expressions.slice(composesCount) + path.replaceWith( + t.callExpression(identifier, [ + t.arrayExpression(composeValues), + t.arrayExpression(vars), + t.functionExpression( + t.identifier('createEmotionStyledRules'), + vars.map((x, i) => t.identifier(`x${i}`)), + t.blockStatement([ + t.returnStatement( + t.arrayExpression([ + new ASTObject(styles, false, composesCount, t).toAST() + ]) ) - ) - } - return accum - }, - [[], []] + ]) + ) + ]) ) - - if (!matches.length) { - return t.templateLiteral( - [t.templateElement({ raw: rule, cooked: rule }, true)], - [] - ) + } catch (e) { + if (path) { + throw path.buildCodeFrameError(e) } - return t.templateLiteral(quasis, expressions) - }) + throw e + } } export function buildStyledCallExpression (identifier, tag, path, state, t) { const identifierName = getIdentifierName(path, t) - let { hash, rules, name, hasOtherMatch, composes, hasCssFunction } = inline( - path.node.quasi, - identifierName, - 'css', - state.inline - ) - // hash will be '0' when no styles are passed so we can just return the original tag - if (hash === '0') { - return tag - } - const inputClasses = [t.stringLiteral(`${name}-${hash}`)] - for (var i = 0; i < composes; i++) { - inputClasses.push(path.node.quasi.expressions.shift()) + if (state.extractStatic && !path.node.quasi.expressions.length) { + const { name, hash, src } = inline( + path.node.quasi, + identifierName, + 'styled' // we don't want these styles to be merged in css`` + ) + + const cssText = `.${name}-${hash} { ${src} }` + const { staticCSSRules } = parseCSS(cssText, true) + + state.insertStaticRules(staticCSSRules) + return t.callExpression(identifier, [ + tag, + t.arrayExpression([t.stringLiteral(`${name}-${hash}`)]) + ]) } - const vars = path.node.quasi.expressions + const { src } = inline(path.node.quasi, identifierName, 'css') - const dynamicValues = parseDynamicValues(rules, t, { composes, vars }) - const args = [tag, t.arrayExpression(inputClasses), t.arrayExpression(vars)] - if (!hasOtherMatch && !state.inline && !hasCssFunction) { - state.insertStaticRules(rules) - } else if (rules.length !== 0) { - const inlineContentExpr = t.functionExpression( + path.addComment('leading', '#__PURE__') + + const { styles, composesCount } = parseCSS(src, false) + + const objs = path.node.quasi.expressions.slice(0, composesCount) + const vars = path.node.quasi.expressions.slice(composesCount) + const args = [ + tag, + t.arrayExpression(objs), + t.arrayExpression(vars), + t.functionExpression( t.identifier('createEmotionStyledRules'), vars.map((x, i) => t.identifier(`x${i}`)), - t.blockStatement([t.returnStatement(t.arrayExpression(dynamicValues))]) + t.blockStatement([ + t.returnStatement( + t.arrayExpression([ + new ASTObject(styles, false, composesCount, t).toAST() + ]) + ) + ]) ) - args.push(inlineContentExpr) - } + ] return t.callExpression(identifier, args) } @@ -147,182 +127,33 @@ export function buildStyledObjectCallExpression (path, identifier, t) { : t.stringLiteral(path.node.callee.property.name) return t.callExpression(identifier, [ tag, - t.arrayExpression(prefixAst(path.node.arguments, t)), - t.arrayExpression() + t.arrayExpression(buildProcessedStylesFromObjectAST(path.node.arguments, t)) ]) } -export function replaceGlobalWithCallExpression ( - identifier, - processQuasi, - path, - state, - t -) { - const { rules, hasInterpolation } = processQuasi(path.node.quasi) - if (!hasInterpolation && !state.inline) { - state.insertStaticRules(rules) - if (t.isExpressionStatement(path.parent)) { - path.parentPath.remove() - } else { - path.replaceWith(t.identifier('undefined')) - } - } else { - path.replaceWith( - t.callExpression(identifier, [ - t.arrayExpression( - parseDynamicValues(rules, t, { - inputExpressions: path.node.quasi.expressions - }) - ) - ]) - ) +function buildProcessedStylesFromObjectAST (objectAST, t) { + if (t.isObjectExpression(objectAST)) { + const astObject = ASTObject.fromAST(objectAST, t) + const { styles } = parseCSS(astObject.obj, false) + astObject.obj = styles + return astObject.toAST() } -} - -function prefixAst (args, t) { - const prefixer = postcssJs.sync([autoprefixer]) - - function isLiteral (value) { - return ( - t.isStringLiteral(value) || - t.isNumericLiteral(value) || - t.isBooleanLiteral(value) + if (t.isArrayExpression(objectAST)) { + return t.arrayExpression( + buildProcessedStylesFromObjectAST(objectAST.elements, t) ) } - if (Array.isArray(args)) { - return args.map(element => prefixAst(element, t)) + if (Array.isArray(objectAST)) { + return map(objectAST, obj => buildProcessedStylesFromObjectAST(obj, t)) } - if (t.isObjectExpression(args)) { - let properties = [] - args.properties.forEach(property => { - // nested objects - if (t.isObjectExpression(property.value)) { - const key = t.isStringLiteral(property.key) - ? t.stringLiteral(property.key.value) - : t.identifier(property.key.name) - return properties.push( - t.objectProperty(key, prefixAst(property.value, t)) - ) - - // literal value or array of literal values - } else if ( - isLiteral(property.value) || - (t.isArrayExpression(property.value) && - property.value.elements.every(isLiteral)) - ) { - // handle array values: { display: ['flex', 'block'] } - const propertyValue = t.isArrayExpression(property.value) - ? property.value.elements.map(element => element.value) - : property.value.value - - const style = { [property.key.name]: propertyValue } - const prefixedStyle = prefixer(style) - - for (var k in prefixedStyle) { - const key = t.isStringLiteral(property.key) - ? t.stringLiteral(k) - : t.identifier(k) - const val = prefixedStyle[k] - let value - - if (typeof val === 'number') { - value = t.numericLiteral(val) - } else if (typeof val === 'string') { - value = t.stringLiteral(val) - } else if (Array.isArray(val)) { - value = t.arrayExpression(val.map(i => t.stringLiteral(i))) - } - - properties.push(t.objectProperty(key, value)) - } - - // expressions - } else { - properties.push(property) - } - }) - - return t.objectExpression(properties) - } - - if (t.isArrayExpression(args)) { - return t.arrayExpression(prefixAst(args.elements, t)) - } - - return args -} - -export function replaceCssWithCallExpression (path, identifier, state, t) { - try { - const { hash, name, rules, hasVar, composes, hasOtherMatch } = inline( - path.node.quasi, - getIdentifierName(path, t), - 'css', - state.inline - ) - const inputClasses = [t.stringLiteral(`${name}-${hash}`)] - for (var i = 0; i < composes; i++) { - inputClasses.push(path.node.quasi.expressions.shift()) - } - const args = [ - t.arrayExpression(inputClasses), - t.arrayExpression(path.node.quasi.expressions) - ] - if (!hasOtherMatch && !state.inline) { - state.insertStaticRules(rules) - if (!hasVar) { - return path.replaceWith(joinExpressionsWithSpaces(inputClasses, t)) - } - } else if (rules.length !== 0) { - const expressions = path.node.quasi.expressions.map((x, i) => - t.identifier(`x${i}`) - ) - const inlineContentExpr = t.functionExpression( - t.identifier('createEmotionRules'), - expressions, - t.blockStatement([ - t.returnStatement( - t.arrayExpression( - parseDynamicValues(rules, t, { - inputExpressions: expressions, - composes - }) - ) - ) - ]) - ) - args.push(inlineContentExpr) - } - path.replaceWith(t.callExpression(identifier, args)) - } catch (e) { - throw path.buildCodeFrameError(e) - } + return objectAST } -export function replaceKeyframesWithCallExpression (path, identifier, state, t) { - const { hash, name, rules, hasInterpolation } = keyframes( - path.node.quasi, - getIdentifierName(path, t), - 'animation' - ) - const animationName = `${name}-${hash}` - if (!hasInterpolation && !state.inline) { - state.insertStaticRules([`@keyframes ${animationName} ${rules.join('')}`]) - path.replaceWith(t.stringLiteral(animationName)) - } else { - path.replaceWith( - t.callExpression(identifier, [ - t.stringLiteral(animationName), - t.arrayExpression( - parseDynamicValues(rules, t, { - inputExpressions: path.node.quasi.expressions - }) - ) - ]) - ) - } +export function replaceCssObjectCallExpression (path, identifier, t) { + const argWithStyles = path.node.arguments[0] + const styles = buildProcessedStylesFromObjectAST(argWithStyles, t) + path.replaceWith(t.callExpression(identifier, [styles])) } const visited = Symbol('visited') @@ -336,9 +167,12 @@ export default function (babel) { visitor: { Program: { enter (path, state) { - state.inline = - path.hub.file.opts.filename === 'unknown' || state.opts.inline + state.extractStatic = + // path.hub.file.opts.filename !== 'unknown' || + state.opts.extractStatic + state.staticRules = [] + state.insertStaticRules = function (staticRules) { state.staticRules.push(...staticRules) } @@ -375,36 +209,35 @@ export default function (babel) { if (path[visited]) { return } - if ( - (t.isCallExpression(path.node.callee) && - path.node.callee.callee.name === 'styled') || - (t.isMemberExpression(path.node.callee) && - t.isIdentifier(path.node.callee.object) && - path.node.callee.object.name === 'styled') - ) { - const identifier = t.isCallExpression(path.node.callee) - ? path.node.callee.callee - : path.node.callee.object - path.replaceWith(buildStyledObjectCallExpression(path, identifier, t)) + try { + if ( + (t.isCallExpression(path.node.callee) && + path.node.callee.callee.name === 'styled') || + (t.isMemberExpression(path.node.callee) && + t.isIdentifier(path.node.callee.object) && + path.node.callee.object.name === 'styled') + ) { + const identifier = t.isCallExpression(path.node.callee) + ? path.node.callee.callee + : path.node.callee.object + path.replaceWith( + buildStyledObjectCallExpression(path, identifier, t) + ) + } + if ( + path.node.callee.name === 'css' && + !path.node.arguments[1] && + path.node.arguments[0] + ) { + replaceCssObjectCallExpression(path, t.identifier('css'), t) + } + } catch (e) { + throw path.buildCodeFrameError(e) } - if (t.isCallExpression(path.node) && path.node.callee.name === 'css') { - const prefixedAst = prefixAst(path.node.arguments, t) - path.replaceWith(t.callExpression(t.identifier('css'), prefixedAst)) - } path[visited] = true }, TaggedTemplateExpression (path, state) { - // in: - // styled.h1`color:${color};` - // - // out: - // styled('h1', "css-r1aqtk", [colorVar, heightVar], function inlineCss(x0, x1) { - // return [`.css-r1aqtk { - // margin: 12px; - // color: ${x0}; - // height: ${x1}; }`]; - // }); if ( // styled.h1`color:${color};` t.isMemberExpression(path.node.tag) && @@ -437,27 +270,30 @@ export default function (babel) { if (path.node.tag.name === 'css') { replaceCssWithCallExpression(path, t.identifier('css'), state, t) } else if (path.node.tag.name === 'keyframes') { - replaceKeyframesWithCallExpression( + replaceCssWithCallExpression( path, t.identifier('keyframes'), state, - t + t, + (name, hash, src) => `@keyframes ${name}-${hash} { ${src} }` ) } else if (path.node.tag.name === 'fontFace') { - replaceGlobalWithCallExpression( - t.identifier('fontFace'), - fontFace, + replaceCssWithCallExpression( path, + t.identifier('fontFace'), state, - t + t, + (name, hash, src) => `@font-face {${src}}`, + true ) } else if (path.node.tag.name === 'injectGlobal') { - replaceGlobalWithCallExpression( - t.identifier('injectGlobal'), - injectGlobal, + replaceCssWithCallExpression( path, + t.identifier('injectGlobal'), state, - t + t, + (name, hash, src) => src, + true ) } } diff --git a/src/glamor/CSSPropertyOperations/CSSProperty.js b/src/glamor/CSSPropertyOperations/CSSProperty.js new file mode 100644 index 000000000..67958f241 --- /dev/null +++ b/src/glamor/CSSPropertyOperations/CSSProperty.js @@ -0,0 +1,83 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule CSSProperty + */ + +/** + * CSS properties which accept numbers but are not in units of "px". + */ + +let isUnitlessNumber = { + animationIterationCount: true, + borderImageOutset: true, + borderImageSlice: true, + borderImageWidth: true, + boxFlex: true, + boxFlexGroup: true, + boxOrdinalGroup: true, + columnCount: true, + flex: true, + flexGrow: true, + flexPositive: true, + flexShrink: true, + flexNegative: true, + flexOrder: true, + gridRow: true, + gridRowStart: true, + gridRowEnd: true, + gridColumn: true, + gridColumnStart: true, + gridColumnEnd: true, + fontWeight: true, + lineClamp: true, + lineHeight: true, + opacity: true, + order: true, + orphans: true, + tabSize: true, + widows: true, + zIndex: true, + zoom: true, + + // SVG-related properties + fillOpacity: true, + floodOpacity: true, + stopOpacity: true, + strokeDasharray: true, + strokeDashoffset: true, + strokeMiterlimit: true, + strokeOpacity: true, + strokeWidth: true +} + +/** + * @param {string} prefix vendor-specific prefix, eg: Webkit + * @param {string} key style name, eg: transitionDuration + * @return {string} style name prefixed with `prefix`, properly camelCased, eg: + * WebkitTransitionDuration + */ +function prefixKey (prefix, key) { + return prefix + key.charAt(0).toUpperCase() + key.substring(1) +} + +/** + * Support style names that may come passed in prefixed by adding permutations + * of vendor prefixes. + */ +let prefixes = ['Webkit', 'ms', 'Moz', 'O'] + +// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an +// infinite loop, because it iterates over the newly added props too. +Object.keys(isUnitlessNumber).forEach(function (prop) { + prefixes.forEach(function (prefix) { + isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop] + }) +}) + +export default isUnitlessNumber diff --git a/src/glamor/CSSPropertyOperations/dangerousStyleValue.js b/src/glamor/CSSPropertyOperations/dangerousStyleValue.js new file mode 100644 index 000000000..6db41a341 --- /dev/null +++ b/src/glamor/CSSPropertyOperations/dangerousStyleValue.js @@ -0,0 +1,54 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule dangerousStyleValue + */ + +import isUnitlessNumber from './CSSProperty' + +let styleWarnings = {} + +/** + * Convert a value into the proper css writable value. The style name `name` + * should be logical (no hyphens), as specified + * in `CSSProperty.isUnitlessNumber`. + * + * @param {string} name CSS property name such as `topMargin`. + * @param {*} value CSS property value such as `10px`. + * @param {ReactDOMComponent} component + * @return {string} Normalized style value with dimensions applied. + */ +function dangerousStyleValue (name, value, component) { + let isEmpty = value == null || typeof value === 'boolean' || value === '' + if (isEmpty) { + return '' + } + + let isNonNumeric = isNaN(value) + if ( + isNonNumeric || + value === 0 || + (isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name]) + ) { + return '' + value // cast to string + } + + if (typeof value === 'string') { + if (component && value !== '0') { + let owner = component._currentElement._owner + let ownerName = owner ? owner.getName() : null + if (ownerName && !styleWarnings[ownerName]) { + styleWarnings[ownerName] = {} + } + } + value = value.trim() + } + return value + 'px' +} + +export default dangerousStyleValue diff --git a/src/glamor/CSSPropertyOperations/index.js b/src/glamor/CSSPropertyOperations/index.js new file mode 100644 index 000000000..760102827 --- /dev/null +++ b/src/glamor/CSSPropertyOperations/index.js @@ -0,0 +1,170 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule CSSPropertyOperations + */ + +import camelizeStyleName from 'fbjs/lib/camelizeStyleName' +import dangerousStyleValue from './dangerousStyleValue' +import hyphenateStyleName from 'fbjs/lib/hyphenateStyleName' +import memoizeStringOnly from 'fbjs/lib/memoizeStringOnly' +import warning from 'fbjs/lib/warning' + +export const processStyleName = memoizeStringOnly(hyphenateStyleName) + +if (process.env.NODE_ENV !== 'production') { + // 'msTransform' is correct, but the other prefixes should be capitalized + let badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/ + + // style values shouldn't contain a semicolon + let badStyleValueWithSemicolonPattern = /;\s*$/ + + let warnedStyleNames = {} + let warnedStyleValues = {} + let warnedForNaNValue = false + + let warnHyphenatedStyleName = function (name, owner) { + if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { + return + } + + warnedStyleNames[name] = true + process.env.NODE_ENV !== 'production' + ? warning( + false, + 'Unsupported style property %s. Did you mean %s?%s', + name, + camelizeStyleName(name), + checkRenderMessage(owner) + ) + : void 0 + } + + let warnBadVendoredStyleName = function (name, owner) { + if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { + return + } + + warnedStyleNames[name] = true + process.env.NODE_ENV !== 'production' + ? warning( + false, + 'Unsupported vendor-prefixed style property %s. Did you mean %s?%s', + name, + name.charAt(0).toUpperCase() + name.slice(1), + checkRenderMessage(owner) + ) + : void 0 + } + + let warnStyleValueWithSemicolon = function (name, value, owner) { + if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) { + return + } + + warnedStyleValues[value] = true + process.env.NODE_ENV !== 'production' + ? warning( + false, + "Style property values shouldn't contain a semicolon.%s " + + 'Try "%s: %s" instead.', + checkRenderMessage(owner), + name, + value.replace(badStyleValueWithSemicolonPattern, '') + ) + : void 0 + } + + let warnStyleValueIsNaN = function (name, value, owner) { + if (warnedForNaNValue) { + return + } + + warnedForNaNValue = true + process.env.NODE_ENV !== 'production' + ? warning( + false, + '`NaN` is an invalid value for the `%s` css style property.%s', + name, + checkRenderMessage(owner) + ) + : void 0 + } + + let checkRenderMessage = function (owner) { + if (owner) { + let name = owner.getName() + if (name) { + return ' Check the render method of `' + name + '`.' + } + } + return '' + } + + /** + * @param {string} name + * @param {*} value + * @param {ReactDOMComponent} component + */ + var warnValidStyle = function (name, value, component) { + // eslint-disable-line no-var + let owner + if (component) { + owner = component._currentElement._owner + } + if (name.indexOf('-') > -1) { + warnHyphenatedStyleName(name, owner) + } else if (badVendoredStyleNamePattern.test(name)) { + warnBadVendoredStyleName(name, owner) + } else if (badStyleValueWithSemicolonPattern.test(value)) { + warnStyleValueWithSemicolon(name, value, owner) + } + + if (typeof value === 'number' && isNaN(value)) { + warnStyleValueIsNaN(name, value, owner) + } + } +} + +/** + * Serializes a mapping of style properties for use as inline styles: + * + * > createMarkupForStyles({width: '200px', height: 0}) + * "width:200px;height:0;" + * + * Undefined values are ignored so that declarative programming is easier. + * The result should be HTML-escaped before insertion into the DOM. + * + * @param {object} styles + * @param {ReactDOMComponent} component + * @return {?string} + */ + +export function createMarkupForStyles (styles, component) { + let serialized = '' + for (let styleName in styles) { + const isCustomProp = styleName.indexOf('--') === 0 + if (!styles.hasOwnProperty(styleName)) { + continue + } + let styleValue = styles[styleName] + if (process.env.NODE_ENV !== 'production' && !isCustomProp) { + warnValidStyle(styleName, styleValue, component) + } + if (styleValue != null) { + if (isCustomProp) { + serialized += `${styleName}:${styleValue};` + } else { + serialized += processStyleName(styleName) + ':' + serialized += + dangerousStyleValue(styleName, styleValue, component) + ';' + } + } + } + return serialized || null +} diff --git a/src/glamor/clean.js b/src/glamor/clean.js new file mode 100644 index 000000000..139621eb3 --- /dev/null +++ b/src/glamor/clean.js @@ -0,0 +1,54 @@ +// Returns true for null, false, undefined and {} +function isFalsy (value) { + return ( + value === null || + value === undefined || + value === false || + (typeof value === 'object' && Object.keys(value).length === 0) + ) +} + +function cleanObject (object) { + if (isFalsy(object)) return null + if (typeof object !== 'object') return object + + let acc = {}, + keys = Object.keys(object), + hasFalsy = false + for (let i = 0; i < keys.length; i++) { + let value = object[keys[i]] + const filteredValue = clean(value) + if (filteredValue === null || filteredValue !== value) { + hasFalsy = true + } + if (filteredValue !== null) { + acc[keys[i]] = filteredValue + } + } + return Object.keys(acc).length === 0 ? null : hasFalsy ? acc : object +} + +function cleanArray (rules) { + let hasFalsy = false + const filtered = [] + rules.forEach(rule => { + const filteredRule = clean(rule) + if (filteredRule === null || filteredRule !== rule) { + hasFalsy = true + } + if (filteredRule !== null) { + filtered.push(filteredRule) + } + }) + return filtered.length == 0 ? null : hasFalsy ? filtered : rules +} + +// Takes style array or object provided by user and clears all the falsy data +// If there is no styles left after filtration returns null +export default function clean (input) { + if (typeof input === 'string') { + return input.trim() + } + + return Array.isArray(input) ? cleanArray(input) : cleanObject(input) +} diff --git a/src/index.js b/src/index.js index 56d0361ca..1fdb73d04 100644 --- a/src/index.js +++ b/src/index.js @@ -1,186 +1,501 @@ // @flow import { StyleSheet } from './sheet' -import { hashArray, hashObject } from './hash' -import { forEach } from './utils' +import { forEach, map, reduce } from './utils' +import { hashString as hash, hashObject } from './hash' +import { createMarkupForStyles } from './glamor/CSSPropertyOperations' +import clean from './glamor/clean.js' + +const IS_DEV = process.env.NODE_ENV === 'development' || !process.env.NODE_ENV export const sheet = new StyleSheet() +// 🚀 sheet.inject() -export let inserted: { [string]: boolean | void } = {} +export let inserted: { [string | number]: boolean | void } = {} type inputVar = string | number type vars = Array -function values (cls: string, vars: vars) { - const hash = hashArray([cls, ...vars]) - const varCls = `vars-${hash}` - if (inserted[hash]) { - return varCls - } - let src = '' - forEach(vars, (val: inputVar, i: number) => { - src && (src += '; ') - src += `--${cls}-${i}: ${val}` - }) - sheet.insert(`.${varCls} {${src}}`) - inserted[hash] = true - - return varCls -} - export function flush () { sheet.flush() inserted = {} sheet.inject() } -export function css (classes: string[], vars: vars, content: () => string[]) { - if (!Array.isArray(classes)) { - classes = [classes] +// a simple cache to store generated obj styles +let registered = (sheet.registered = {}) + +function register (spec) { + if (!registered[spec.id]) { + registered[spec.id] = spec + } +} + +function _getRegistered (rule) { + if (isLikeRule(rule)) { + let ret = registered[idFor(rule)] + if (ret == null) { + throw new Error( + '[emotion] an unexpected rule cache miss occurred. This is probably a sign of multiple glamor instances in your app. See https://github.com/threepointone/glamor/issues/79' + ) + } + return ret + } + return rule +} + +// The idea on how to merge object class names come from glamorous +// 💄 +// https://github.com/paypal/glamorous/blob/master/src/get-glamor-classname.js +function getEmotionStylesFromClassName (className) { + const id = className.trim().slice('css-'.length) + if (sheet.registered[id]) { + return sheet.registered[id].style + } else { + return [] } +} +function buildStyles (objs) { let computedClassName = '' - forEach(classes, (cls): string => { - computedClassName && (computedClassName += ' ') - computedClassName += typeof cls === 'string' ? cls : objStyle(cls) - }) + let objectStyles = [] - if (content) { - // inline mode - let src = content(...vars) // returns an array - let hash = hashArray(src) - - if (!inserted[hash]) { - inserted[hash] = true - const rgx = new RegExp(classes[0], 'gm') - forEach(src, r => { - sheet.insert(r.replace(rgx, `${classes[0]}-${hash}`)) - }) + // This needs to be moved into the core + forEach(objs, (cls): void => { + if (typeof cls === 'string') { + if (cls.trim().indexOf('css-') === 0) { + objectStyles.push(getEmotionStylesFromClassName(cls)) + } else { + computedClassName && (computedClassName += ' ') + computedClassName += cls + } + } else { + objectStyles.push(cls) } - return `${classes[0]}-${hash} ${computedClassName}` + }) + + return { computedClassName, objectStyles } +} + +export function css (objs: any, vars: Array, content: () => Array) { + if (!Array.isArray(objs)) { + objs = [objs] } - return ( - computedClassName + - (vars && vars.length > 0 ? ' ' + values(classes[0], vars) : '') + let { computedClassName = '', objectStyles = [] } = buildStyles( + content ? objs.concat(content.apply(null, vars)) : objs ) + if (objectStyles.length) { + computedClassName += ' ' + objStyle.apply(null, objectStyles).toString() + } + + return computedClassName.trim() } -export function injectGlobal (src: string[]) { - const hash = hashArray(src) - if (!inserted[hash]) { - inserted[hash] = true - forEach(src, r => sheet.insert(r)) +function insertRawRule (css: string) { + let spec = { + id: hash(css, css.length), + css, + type: 'raw' + } + + register(spec) + + if (!inserted[spec.id]) { + sheet.insert(spec.css) + inserted[spec.id] = true } } -export const fontFace = injectGlobal +export function injectGlobal ( + objs: Array, + vars: Array, + content: () => Array +) { + const combined = content ? objs.concat(content.apply(null, vars)) : objs -export function keyframes (kfm: string, src: string[]) { - const hash = hashArray(src) - const animationName = `${kfm}-${hash}` - if (!inserted[hash]) { - inserted[hash] = true - forEach(src, r => sheet.insert(`@keyframes ${animationName} ${r}`)) + // injectGlobal is flattened by postcss + // we don't support nested selectors on objects + forEach(combined, obj => { + forEach(Object.keys(obj), selector => { + insertRawRule(`${selector} {${createMarkupForStyles(obj[selector])}}`) + }) + }) +} + +export function fontFace ( + objs: Array, + vars: Array, + content: () => Array +) { + const combined = reduce( + content ? objs.concat(content.apply(null, vars)) : objs, + (accum, item, i) => Object.assign(accum, item), + {} + ) + + insertRawRule(`@font-face{${createMarkupForStyles(combined)}}`) +} + +function insertKeyframe (spec) { + if (!inserted[spec.id]) { + const inner = map( + Object.keys(spec.keyframes), + kf => `${kf} {${createMarkupForStyles(spec.keyframes[kf])}}` + ).join('') + + forEach(['-webkit-', ''], prefix => + sheet.insert(`@${prefix}keyframes ${spec.name + '_' + spec.id}{${inner}}`) + ) + + inserted[spec.id] = true } - return animationName +} + +export function keyframes ( + objs: any, + vars: Array, + content: () => Array +) { + const [kfs] = content.apply(null, vars) + const name = 'animation' + + let spec = { + id: hashObject(kfs), + type: 'keyframes', + name, + keyframes: kfs + } + + register(spec) + insertKeyframe(spec) + return `${name}_${spec.id}` } export function hydrate (ids: string[]) { forEach(ids, id => (inserted[id] = true)) } +type EmotionRule = { [string]: any } + +type CSSRuleList = Array + +type EmotionClassName = { + [string]: any +} + +let cachedCss: (rules: CSSRuleList) => EmotionClassName = + typeof WeakMap !== 'undefined' ? multiIndexCache(_css) : _css + // 🍩 -// https://github.com/jxnblk/cxs/blob/master/src/monolithic/index.js -export function objStyle (style: { [string]: any }) { - const hash = hashObject(style) - const className = `css-${hash}` - const selector = '.' + className +// https://github.com/threepointone/glamor +export function objStyle (...rules: CSSRuleList): EmotionClassName { + rules = clean(rules) + if (!rules) { + return nullrule + } - if (inserted[hash]) return className + return cachedCss(rules) +} - const rules = deconstruct(selector, style) - forEach(rules, rule => sheet.insert(rule)) +function _css (rules) { + let style = {} + build(style, { src: rules }) // mutative! but worth it. - inserted[hash] = true + let spec = { + id: hashObject(style), + style, + type: 'css' + } + return toRule(spec) +} - return className +// of shape { 'data-css-': '' } +export function isLikeRule (rule: EmotionRule) { + let keys = Object.keys(rule).filter(x => x !== 'toString') + if (keys.length !== 1) { + return false + } + return !!/css-([a-zA-Z0-9]+)/.exec(keys[0]) +} + +// extracts id from a { 'css-': ''} like object +export function idFor (rule: EmotionRule) { + let keys = Object.keys(rule).filter(x => x !== 'toString') + if (keys.length !== 1) throw new Error('not a rule') + let regex = /css-([a-zA-Z0-9]+)/ + let match = regex.exec(keys[0]) + if (!match) throw new Error('not a rule') + return match[1] } -function deconstruct (selector, styles, media) { - const decs = [] - const rules = [] +function selector (id: string, path: string = '') { + if (!id) { + return path.replace(/&/g, '') + } + if (!path) return `.css-${id}` + + let x = path + .split(',') + .map( + x => + x.indexOf('&') >= 0 ? x.replace(/&/gm, `.css-${id}`) : `.css-${id}${x}` + ) + .join(',') - for (let key in styles) { - const value = styles[key] - const type = typeof value + return x +} - if (type === 'number' || type === 'string') { - decs.push(createDec(key, value)) - } else if (Array.isArray(value)) { - forEach(value, val => decs.push(createDec(key, val))) - } else if (key.charCodeAt(0) === 58) { - forEach(deconstruct(selector + key, value, media), r => rules.push(r)) - } else if (key.indexOf('@media') !== -1) { - forEach(deconstruct(selector, value, key), r => rules.push(r)) +function deconstruct (style) { + // we can be sure it's not infinitely nested here + let plain, selects, medias, supports + Object.keys(style).forEach(key => { + if (key.indexOf('&') >= 0) { + selects = selects || {} + selects[key] = style[key] + } else if (key.indexOf('@media') === 0) { + medias = medias || {} + medias[key] = deconstruct(style[key]) + } else if (key.indexOf('@supports') === 0) { + supports = supports || {} + supports[key] = deconstruct(style[key]) } else { - forEach(deconstruct(selector + ' ' + key, value, media), r => rules.push(r)) + plain = plain || {} + plain[key] = style[key] } + }) + return { plain, selects, medias, supports } +} + +function deconstructedStyleToCSS (id, style) { + let { plain, selects, medias, supports } = style + let css = [] + + if (plain) { + css.push(`${selector(id)}{${createMarkupForStyles(plain)}}`) + } + if (selects) { + Object.keys(selects).forEach((key: string) => + css.push(`${selector(id, key)}{${createMarkupForStyles(selects[key])}}`) + ) + } + if (medias) { + Object.keys(medias).forEach(key => + css.push(`${key}{${deconstructedStyleToCSS(id, medias[key]).join('')}}`) + ) + } + if (supports) { + Object.keys(supports).forEach(key => + css.push(`${key}{${deconstructedStyleToCSS(id, supports[key]).join('')}}`) + ) + } + return css +} + +// and helpers to insert rules into said sheet +function insert (spec) { + if (!inserted[spec.id]) { + inserted[spec.id] = true + let deconstructed = deconstruct(spec.style) + deconstructedStyleToCSS(spec.id, deconstructed).map(cssRule => + sheet.insert(cssRule) + ) + } +} + +// todo - perf +let ruleCache = {} + +function toRule (spec) { + register(spec) + insert(spec) + if (ruleCache[spec.id]) { + return ruleCache[spec.id] + } + + let ret = { [`css-${spec.id}`]: '' } + Object.defineProperty(ret, 'toString', { + enumerable: false, + value () { + return 'css-' + spec.id + } + }) + ruleCache[spec.id] = ret + return ret +} + +function isSelector (key) { + const possibles = [':', '.', '[', '>', ' '] + let found = false + const ch = key.charAt(0) + for (let i = 0; i < possibles.length; i++) { + if (ch === possibles[i]) { + found = true + break + } + } + return found || key.indexOf('&') >= 0 +} + +function joinSelectors (a, b) { + let as = map(a.split(','), a => (!(a.indexOf('&') >= 0) ? '&' + a : a)) + let bs = map(b.split(','), b => (!(b.indexOf('&') >= 0) ? '&' + b : b)) + + return bs + .reduce((arr, b) => arr.concat(as.map(a => b.replace(/&/g, a))), []) + .join(',') +} + +function joinMediaQueries (a, b) { + return a ? `@media ${a.substring(6)} and ${b.substring(6)}` : b +} + +function isMediaQuery (key) { + return key.indexOf('@media') === 0 +} + +function isSupports (key) { + return key.indexOf('@supports') === 0 +} + +function joinSupports (a, b) { + return a ? `@supports ${a.substring(9)} and ${b.substring(9)}` : b +} + +// flatten a nested array +function flatten (inArr) { + let arr = [] + for (let i = 0; i < inArr.length; i++) { + if (Array.isArray(inArr[i])) arr = arr.concat(flatten(inArr[i])) + else arr = arr.concat(inArr[i]) + } + return arr +} + +// mutable! modifies dest. +function build (dest, { selector = '', mq = '', supp = '', src = {} }) { + if (!Array.isArray(src)) { + src = [src] } + src = flatten(src) + + src.forEach(_src => { + if (isLikeRule(_src)) { + let reg = _getRegistered(_src) + if (reg.type !== 'css') { + throw new Error('cannot merge this rule') + } + _src = reg.style + } + _src = clean(_src) + if (_src && _src.composes) { + build(dest, { selector, mq, supp, src: _src.composes }) + } + Object.keys(_src || {}).forEach(key => { + if (isSelector(key)) { + build(dest, { + selector: joinSelectors(selector, key), + mq, + supp, + src: _src[key] + }) + } else if (isMediaQuery(key)) { + build(dest, { + selector, + mq: joinMediaQueries(mq, key), + supp, + src: _src[key] + }) + } else if (isSupports(key)) { + build(dest, { + selector, + mq, + supp: joinSupports(supp, key), + src: _src[key] + }) + } else if (key === 'composes') { + // ignore, we already dealt with it + } else { + let _dest = dest + if (supp) { + _dest[supp] = _dest[supp] || {} + _dest = _dest[supp] + } + if (mq) { + _dest[mq] = _dest[mq] || {} + _dest = _dest[mq] + } + if (selector) { + _dest[selector] = _dest[selector] || {} + _dest = _dest[selector] + } - rules.unshift(createRule(selector, decs, media)) - - return rules -} - -function createDec (key, value) { - const prop = hyphenate(key) - const val = addPx(key, value) - return prop + ':' + val -} - -function createRule (selector, decs, media) { - const rule = `${selector}{${decs.join(';')}}` - return media ? `${media}{${rule}}` : rule -} - -function hyphenate (str) { - return ('' + str).replace(/[A-Z]|^ms/g, '-$&').toLowerCase() -} - -function addPx (prop, value) { - if (typeof value !== 'number' || unitlessProps[prop] !== undefined) return value - return value + 'px' -} - -const unitlessProps = { - animationIterationCount: 1, - boxFlex: 1, - boxFlexGroup: 1, - boxOrdinalGroup: 1, - columnCount: 1, - flex: 1, - flexGrow: 1, - flexPositive: 1, - flexShrink: 1, - flexNegative: 1, - flexOrder: 1, - gridRow: 1, - gridColumn: 1, - fontWeight: 1, - lineClamp: 1, - lineHeight: 1, - opacity: 1, - order: 1, - orphans: 1, - tabSize: 1, - widows: 1, - zIndex: 1, - zoom: 1, - fillOpacity: 1, - stopOpacity: 1, - strokeDashoffset: 1, - strokeOpacity: 1, - strokeWidth: 1 + _dest[key] = _src[key] + } + }) + }) +} + +let nullrule: EmotionClassName = { + // 'data-css-nil': '' +} + +Object.defineProperty(nullrule, 'toString', { + enumerable: false, + value () { + return 'css-nil' + } +}) + +let inputCaches = + typeof WeakMap !== 'undefined' + ? [nullrule, new WeakMap(), new WeakMap(), new WeakMap()] + : [nullrule] + +let warnedWeakMapError = false + +function multiIndexCache (fn) { + return function (args) { + if (inputCaches[args.length]) { + let coi = inputCaches[args.length] + let ctr = 0 + while (ctr < args.length - 1) { + if (!coi.has(args[ctr])) { + coi.set(args[ctr], new WeakMap()) + } + coi = coi.get(args[ctr]) + ctr++ + } + if (coi.has(args[args.length - 1])) { + let ret = coi.get(args[ctr]) + + if (registered[ret.toString().substring(4)]) { + // make sure it hasn't been flushed + return ret + } + } + } + let value = fn(args) + if (inputCaches[args.length]) { + let ctr = 0, + coi = inputCaches[args.length] + while (ctr < args.length - 1) { + coi = coi.get(args[ctr]) + ctr++ + } + try { + coi.set(args[ctr], value) + } catch (err) { + if (IS_DEV && !warnedWeakMapError) { + warnedWeakMapError = true + console.warn('failed setting the WeakMap cache for args:', ...args) // eslint-disable-line no-console + console.warn( + 'this should NOT happen, please file a bug on the github repo.' + ) // eslint-disable-line no-console + } + } + } + return value + } } diff --git a/src/inline.js b/src/inline.js index 9a42f0baa..735213183 100644 --- a/src/inline.js +++ b/src/inline.js @@ -1,5 +1,4 @@ // @flow -import { parseCSS } from './parser' import { hashArray } from './hash' function extractNameFromProperty (str: string) { @@ -25,116 +24,45 @@ function getName ( return parts.join('-') } -function createSrc ( - strs: string[], - name: string, - hash: string -): { src: string, matches: number } { - let matches = 0 +function createRawStringFromQuasi ( + strs: string[] +): { src: string, dynamicValueCount: number } { + let dynamicValueCount = 0 const src = strs .reduce((arr, str, i) => { arr.push(str) if (i !== strs.length - 1) { - matches++ + dynamicValueCount++ arr.push(`xxx${i}xxx`) } return arr }, []) .join('') .trim() - return { src, matches } + return { src, dynamicValueCount } } export function inline ( quasi: any, identifierName?: string, - prefix: string, - inlineMode: boolean + prefix: string ): { - hash: string, name: string, - rules: string[], - hasVar: boolean, - hasOtherMatch: boolean, - composes: number, - hasCssFunction: boolean + hash: string, + src: string } { let strs = quasi.quasis.map(x => x.value.cooked) - let hash = hashArray([...strs]) // todo - add current filename? + let hash = hashArray([...strs]) let name = getName( extractNameFromProperty(strs.join('xxx')), identifierName, prefix ) - let { src, matches } = createSrc(strs, name, hash) - let { - rules, - hasVar, - hasOtherMatch, - composes, - hasCssFunction - } = parseCSS(`.${name}-${hash} { ${src} }`, { - inlineMode: inlineMode, - matches, - name, - hash, - canCompose: true - }) - return { hash, name, rules, hasVar, hasOtherMatch, composes, hasCssFunction } -} + let { src } = createRawStringFromQuasi(strs) -export function keyframes ( - quasi: any, - identifierName?: string, - prefix: string -): { - hash: string, - name: string, - rules: string[], - hasInterpolation: boolean -} { - const strs = quasi.quasis.map(x => x.value.cooked) - const hash = hashArray([...strs]) - const name = getName( - extractNameFromProperty(strs.join('xxx')), - identifierName, - prefix - ) - const { src, matches } = createSrc(strs, name, hash) - let { rules, hasVar, hasOtherMatch } = parseCSS(`{ ${src} }`, { - nested: false, - inlineMode: true, - matches, + return { name, - hash - }) - return { hash, name, rules, hasInterpolation: hasVar || hasOtherMatch } -} - -export function fontFace ( - quasi: any -): { rules: string[], hasInterpolation: boolean } { - let strs = quasi.quasis.map(x => x.value.cooked) - const { src, matches } = createSrc(strs, 'name', 'hash') - let { rules, hasVar, hasOtherMatch } = parseCSS(`@font-face {${src}}`, { - matches, - inlineMode: true, - name: 'name', - hash: 'hash' - }) - return { rules, hasInterpolation: hasVar || hasOtherMatch } -} - -export function injectGlobal ( - quasi: any -): { rules: string[], hasInterpolation: boolean } { - let strs = quasi.quasis.map(x => x.value.cooked) - const { src, matches } = createSrc(strs, 'name', 'hash') - let { rules, hasVar, hasOtherMatch } = parseCSS(src, { - matches, - inlineMode: true, - name: 'name', - hash: 'hash' - }) - return { rules, hasInterpolation: hasVar || hasOtherMatch } + hash, + src + } } diff --git a/src/macro.js b/src/macro.js index 2070bb804..979cc5343 100644 --- a/src/macro.js +++ b/src/macro.js @@ -1,16 +1,13 @@ import { - replaceGlobalWithCallExpression, replaceCssWithCallExpression, - replaceKeyframesWithCallExpression + replaceCssObjectCallExpression } from './babel' import { buildMacroRuntimeNode, addRuntimeImports } from './babel-utils' -import { injectGlobal, fontFace } from './inline' -import { forEach } from './utils' -import { keys } from './utils' +import { forEach, keys } from './utils' module.exports = function macro ({ references, state, babel: { types: t } }) { if (!state.inline) state.inline = true - forEach(keys(references), (referenceKey) => { + forEach(keys(references), referenceKey => { if (referenceKey === 'injectGlobal') { references.injectGlobal.forEach(injectGlobalReference => { const path = injectGlobalReference.parentPath @@ -18,17 +15,18 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { t.isIdentifier(path.node.tag) && t.isTemplateLiteral(path.node.quasi) ) { - replaceGlobalWithCallExpression( + replaceCssWithCallExpression( + path, buildMacroRuntimeNode( injectGlobalReference, state, 'injectGlobal', t ), - injectGlobal, - path, state, - t + t, + undefined, + true ) } }) @@ -39,12 +37,13 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { t.isIdentifier(path.node.tag) && t.isTemplateLiteral(path.node.quasi) ) { - replaceGlobalWithCallExpression( - buildMacroRuntimeNode(fontFaceReference, state, 'fontFace', t), - fontFace, + replaceCssWithCallExpression( path, + buildMacroRuntimeNode(fontFaceReference, state, 'fontFace', t), state, - t + t, + undefined, + true ) } }) @@ -57,6 +56,8 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { t.isTemplateLiteral(path.node.quasi) ) { replaceCssWithCallExpression(path, runtimeNode, state, t) + } else if (!path.node.arguments[1] && path.node.arguments[0]) { + replaceCssObjectCallExpression(path, runtimeNode, t) } else { cssReference.replaceWith(runtimeNode) } @@ -68,7 +69,7 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { t.isIdentifier(path.node.tag) && t.isTemplateLiteral(path.node.quasi) ) { - replaceKeyframesWithCallExpression( + replaceCssWithCallExpression( path, buildMacroRuntimeNode(keyframesReference, state, 'keyframes', t), state, @@ -77,7 +78,7 @@ module.exports = function macro ({ references, state, babel: { types: t } }) { } }) } else { - references[referenceKey].forEach((reference) => { + references[referenceKey].forEach(reference => { reference.replaceWith( buildMacroRuntimeNode(reference, state, referenceKey, t) ) diff --git a/src/parser.js b/src/parser.js index a19bd064a..c29135c5d 100644 --- a/src/parser.js +++ b/src/parser.js @@ -2,9 +2,21 @@ import parse from 'styled-components/lib/vendor/postcss-safe-parser/parse' import postcssNested from 'styled-components/lib/vendor/postcss-nested' import stringify from 'styled-components/lib/vendor/postcss/stringify' -import autoprefix from 'styled-components/lib/utils/autoprefix' +import postcssJs from 'postcss-js' +import objParse from 'postcss-js/parser' +import autoprefixer from 'autoprefixer' +import { processStyleName } from './glamor/CSSPropertyOperations' +import { objStyle } from './index' -type CSSDecl = { +const prefixer = postcssJs.sync([autoprefixer]) + +type Rule = { + parent: { selector: string, nodes: Array }, + selector: string, + remove: () => {} +} + +type Decl = { parent: { selector: string, nodes: Array }, prop: string, value: string, @@ -13,91 +25,49 @@ type CSSDecl = { export function parseCSS ( css: string, - options: { - nested?: boolean, - inlineMode: boolean, - matches: number, - name: string, - hash: string, - canCompose?: boolean - } = { - nested: true, - inlineMode: true, - matches: 0, - name: 'name', - hash: 'hash', - canCompose: false - } + extractStatic: boolean ): { - rules: string[], - hasOtherMatch: boolean, - hasVar: boolean, - composes: number, - hasCssFunction: boolean + staticCSSRules: Array, + styles: { [string]: any }, + composesCount: number } { // todo - handle errors - const root = parse(css) - if (options.nested !== false) postcssNested(root) - + let root + if (typeof css === 'object') { + root = objParse(css) + } else { + root = parse(css) + } let vars = 0 let composes: number = 0 - let hasCssFunction = false - root.walkDecls((decl: CSSDecl): void => { - if (decl.prop === 'name') decl.remove() - if (decl.value.match(/attr/)) { - hasCssFunction = true - } - if (options.canCompose) { - if (decl.prop === 'composes') { - if (decl.parent.selector !== `.${options.name}-${options.hash}`) { - throw new Error('composes cannot be on nested selectors') - } - if (!/xxx(\d+)xxx/gm.exec(decl.value)) { - throw new Error('composes must be a interpolation') - } - if (decl.parent.nodes[0] !== decl) { - throw new Error('composes must be the first rule') - } - const composeMatches = decl.value.match(/xxx(\d+)xxx/gm) - const numOfComposes: number = !composeMatches - ? 0 - : composeMatches.length - composes += numOfComposes - vars += numOfComposes - decl.remove() - return + + postcssNested(root) + + root.walkDecls((decl: Decl): void => { + if (decl.prop === 'composes') { + if (!/xxx(\d+)xxx/gm.exec(decl.value)) { + throw new Error('composes must be a interpolation') } - } - if (!options.inlineMode) { - const match = /xxx(\d+)xxx/gm.exec(decl.value) - if (match) { - vars++ + if (decl.parent.nodes[0] !== decl) { + throw new Error('composes must be the first rule') } + const composeMatches = decl.value.match(/xxx(\d+)xxx/gm) + const numOfComposes: number = !composeMatches ? 0 : composeMatches.length + composes += numOfComposes + vars += numOfComposes + decl.remove() } }) - if (!options.inlineMode && vars === options.matches && !hasCssFunction) { - root.walkDecls(decl => { - decl.value = decl.value.replace(/xxx(\d+)xxx/gm, (match, p1) => { - return `var(--${options.name}-${options.hash}-${p1 - composes})` - }) - }) - } - root.walkRules(rule => { - if (rule.nodes.length === 0) { - rule.remove() - } - }) + const styles = expandCSSFallbacks(prefixer(postcssJs.objectify(root))) - autoprefix(root) return { - rules: stringifyCSSRoot(root), - hasOtherMatch: vars !== options.matches, - hasVar: - (!!vars && vars !== composes) || - !!(options.inlineMode && options.matches), - composes, - hasCssFunction + styles, + staticCSSRules: + vars === 0 && extractStatic + ? stringifyCSSRoot(postcssJs.parse(styles)) + : [], + composesCount: composes } } @@ -110,3 +80,21 @@ function stringifyCSSRoot (root) { return str }) } + +export function expandCSSFallbacks (style: { [string]: any }) { + let flattened = Object.keys(style).reduce((accum, key) => { + if (Array.isArray(style[key])) { + accum[key] = style[key].join(`; ${processStyleName(key)}: `) + } else if ( + Object.prototype.toString.call(style[key]) === '[object Object]' + ) { + accum[key] = expandCSSFallbacks(style[key]) + } else { + accum[key] = style[key] + } + return accum + }, {}) + // todo - + // flatten arrays which haven't been flattened yet + return flattened +} diff --git a/src/react/index.js b/src/react/index.js index ebcd2f5ef..e52246cbf 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -1,18 +1,30 @@ import { Component, createElement as h } from 'react' import PropTypes from 'prop-types' import { css } from '../index' -import { map, omit } from '../utils' +import { map, omit, reduce } from '../utils' import { CHANNEL } from './constants' -export { flush, css, injectGlobal, fontFace, keyframes, hydrate, objStyle } from '../index' +export { + flush, + css, + injectGlobal, + fontFace, + keyframes, + hydrate, + objStyle +} from '../index' -export default function (tag, cls, vars = [], content) { +const push = (obj, items) => Array.prototype.push.apply(obj, items) + +export default function (tag, objs, vars = [], content) { if (!tag) { throw new Error( 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.' ) } + const componentTag = tag.displayName || tag.name || 'Component' + class Styled extends Component { state = { theme: {} @@ -37,25 +49,62 @@ export default function (tag, cls, vars = [], content) { } render () { - const { props, state } = this + const { props, state, context } = this const mergedProps = { ...props, theme: state.theme } - const getValue = v => - v && typeof v === 'function' ? v.cls || v(mergedProps) : v + const getValue = v => { + if (v && typeof v === 'function') { + if (v.__emotion_spec) { + return css( + map(v.__emotion_spec.objs, getValue), + map(v.__emotion_spec.vars, getValue), + v.__emotion_spec.content + ) + } + return v(mergedProps, context) + } + + return v + } + + let finalObjs = [] + + if (tag.__emotion_spec) { + push( + finalObjs, + reduce( + tag.__emotion_spec, + (accum, spec) => { + push(accum, spec.objs) + push(accum, spec.content.apply(null, map(spec.vars, getValue))) + return accum + }, + [] + ) + ) + } + + push(finalObjs, objs) + + if (content) { + push(finalObjs, content.apply(null, map(vars, getValue))) + } + + if (mergedProps.className) { + push(finalObjs, mergedProps.className.split(' ')) + } - const className = css(map(cls, getValue), map(vars, getValue), content) + const className = css(map(finalObjs, getValue)) return h( tag, omit( Object.assign({}, mergedProps, { ref: mergedProps.innerRef, - className: mergedProps.className - ? className + ' ' + mergedProps.className - : className + className }), ['innerRef', 'theme'] ) @@ -69,9 +118,14 @@ export default function (tag, cls, vars = [], content) { [CHANNEL]: PropTypes.object } - const name = typeof cls[0] === 'string' ? cls[0].split('-')[1] : '' - const componentTag = tag.displayName || tag.name || 'Component' - Styled.displayName = `styled(${componentTag}${name})` - Styled.cls = '.' + cls + Styled.displayName = `styled(${componentTag})` + const spec = { + vars, + content, + objs + } + Styled.__emotion_spec = tag.__emotion_spec + ? tag.__emotion_spec.concat(spec) + : [spec] return Styled } diff --git a/src/utils.js b/src/utils.js index 27decbeae..00febe4ac 100644 --- a/src/utils.js +++ b/src/utils.js @@ -49,3 +49,28 @@ export function map ( return out } + +export function reduce ( + arr: Array, + fn: (out: Array, item: any, index: number, arr: Array) => any, + val: any +) { + if (arr == null) { + return [] + } + + let i = 0 + let len = arr.length + let out = val + + if (out === void 0) { + out = arr[0] + i = 1 + } + + for (; i < len; i++) { + out = fn(out, arr[i], i, arr) + } + + return out +} diff --git a/src/vue/index.js b/src/vue/index.js deleted file mode 100644 index d6796388d..000000000 --- a/src/vue/index.js +++ /dev/null @@ -1,28 +0,0 @@ -import { css as magic } from '../index' - -export { flush, css, injectGlobal, fontFace, keyframes, hydrate, objStyle } from '../index' - -const styled = (tag, cls, vars = [], content) => { - return { - cls: '.' + cls, - functional: true, - render (h, context) { - const getValue = v => (v && typeof v === 'function' ? v(context.props) : v.cls || v) - const className = magic( - cls.map(getValue), - vars.map(getValue), - content - ) - return h( - tag, - { - ...context.data, - class: [context.data.class, className] - }, - context.children - ) - } - } -} - -export default styled diff --git a/src/vue/macro.js b/src/vue/macro.js deleted file mode 100644 index e098e05c0..000000000 --- a/src/vue/macro.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../macro-styled') diff --git a/test/__snapshots__/css-prop.test.js.snap b/test/__snapshots__/css-prop.test.js.snap index f922c0301..5b5d16412 100644 --- a/test/__snapshots__/css-prop.test.js.snap +++ b/test/__snapshots__/css-prop.test.js.snap @@ -1,94 +1,77 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`css prop react basic 1`] = ` -.css-tree-7sri7n-1yz7ela { +.glamor-0 { color: red; font-size: 1px; }

hello world

`; exports[`css prop react kitchen sink 1`] = ` -.css-bold-15qcrjv-18w8kdp { +.glamor-4 { display: -webkit-box; - display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; font-weight: bold; -} - -.css-small-1wh34bm-yjtkkb { - font-size: 6; -} - -.css-flexCenter-mdk64u-llada3 { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; -webkit-box-pack: center; + -ms-flex-pack: center; justify-content: center; - -webkit-align-items: center; - -ms-flex-align: center; -webkit-box-align: center; + -ms-flex-align: center; align-items: center; } -.css-tree-17jiuxc-pi0syi { +.glamor-0 { + font-size: 6px; color: red; } -.css-tree-14jcta0-1fj87hp { +.glamor-1 { color: blue; } -.css-tree-1gdxe9e-1vltlgj { - display: -webkit-box; - display: -moz-box; +.glamor-2 { + display: -webkit-inline-box; display: -ms-inline-flexbox; - display: -webkit-inline-flex; display: inline-flex; } -.css-tree-1g8ltio-i3faqz { +.glamor-3 { color: red; - border-radius: 5; + border-radius: 5px; } -.css-tree-1g8ltio-i3faqz:hover { +.glamor-3:hover { font-weight: bold; color: gray; }

BOOM

Hello

World

hello world

@@ -96,14 +79,14 @@ exports[`css prop react kitchen sink 1`] = ` `; exports[`css prop react string expression 1`] = ` -.css-tree-7sqgip-8upfq4 { +.glamor-0 { color: red; background: blue; font-size: 48px; }

hello world

diff --git a/test/__snapshots__/css.test.js.snap b/test/__snapshots__/css.test.js.snap index 8025db85b..dfaec41dc 100644 --- a/test/__snapshots__/css.test.js.snap +++ b/test/__snapshots__/css.test.js.snap @@ -1,83 +1,133 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`css composes 1`] = `"css-cls1-vyoujf-va5xsk css-cls1-vyoujf"`; - -exports[`css composes 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-cls1-vyoujf-va5xsk css-cls1-vyoujf"`; - -exports[`css composes 3`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }" +exports[`css composes 1`] = ` +.glamor-0 { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} + +
+`; + +exports[`css composes with objects 1`] = ` +.glamor-0 { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + display: block; + width: 30px; + height: calc(40vw - 50px); + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} + +.glamor-0:hover { + color: blue; +} + +.glamor-0:after { + content: " "; + color: red; +} + +@media (min-width: 420px) { + .glamor-0 { + color: green; + } +} + +
`; -exports[`css composes with objects 1`] = `"css-1puxias"`; - -exports[`css composes with objects 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-1puxias"`; - -exports[`css composes with objects 3`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }.css-xmm9em{display:-webkit-box;display:-ms-flexbox;display:flex}.css-1puxias{display:-webkit-box;display:-ms-flexbox;display:flex;display:block;width:30px;height:calc(40vw - 50px)}.css-1puxias:hover{color:blue}.css-1puxias:after{content:\\" \\";color:red}@media(min-width: 420px){.css-1puxias{color:green}}" +exports[`css composes with undefined values 1`] = ` +.glamor-0 { + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} + +
`; -exports[`css composes with undefined values 1`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-15famh2"`; - -exports[`css composes with undefined values 2`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }" +exports[`css computed key is only dynamic 1`] = ` +.glamor-0 { + font-size: 10px; + width: 20px; +} + +
`; -exports[`css handles more than 10 dynamic properties 1`] = `"css-cls1-1gi569l-b877w5 css-cls1-1gi569l"`; - -exports[`css handles more than 10 dynamic properties 2`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }" +exports[`css flushes correctly 1`] = ` +.glamor-0 { + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} + +
`; -exports[`css handles objects 1`] = `"css-xmm9em"`; +exports[`css flushes correctly 2`] = ` +
+`; + +exports[`css handles more than 10 dynamic properties 1`] = ` +.glamor-0 { + -webkit-text-decoration: underline; + text-decoration: underline; + border-right: solid blue 54px; + background: white; + color: black; + display: block; + border-radius: 3px; + padding: 25px; + width: 500px; + z-index: 100; + font-size: 18px; + text-align: center; +} + +
+`; + +exports[`css handles objects 1`] = ` +.glamor-0 { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + color: blue; + font-size: 20px; + height: 50px; + width: 20px; +} + +
+`; + +exports[`css null rule 1`] = ` +
+`; diff --git a/test/__snapshots__/font-face.test.js.snap b/test/__snapshots__/font-face.test.js.snap index 5dc1c8505..2964673ce 100644 --- a/test/__snapshots__/font-face.test.js.snap +++ b/test/__snapshots__/font-face.test.js.snap @@ -1,21 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`font-face adds font-face to sheet 1`] = ` -"@font-face {font-family: 'Patrick Hand SC'; - font-style: normal; - font-weight: 400; - src: local('Patrick Hand SC'), local('PatrickHandSC-Regular'), url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2) format('woff2'); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;}" -` +exports[`font-face adds font-face to sheet 1`] = `"@font-face{font-family:'Patrick Hand SC';font-style:normal;font-weight:400;src:local('Patrick Hand SC'), local('PatrickHandSC-Regular'), url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2) format('woff2');unicode-range:U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;}"`; exports[`font-face adds font-face to sheet with interpolation 1`] = ` -"@font-face {font-family: 'Patrick Hand SC'; - font-style: normal; - font-weight: 400; - src: local('Patrick Hand SC'), local('PatrickHandSC-Regular'), url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2) format('woff2'); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;}@font-face {font-family: MyHelvetica; - src: local(\\"Helvetica Neue Bold\\"), +"@font-face{font-family:'Patrick Hand SC';font-style:normal;font-weight:400;src:local('Patrick Hand SC'), local('PatrickHandSC-Regular'), url(https://fonts.gstatic.com/s/patrickhandsc/v4/OYFWCgfCR-7uHIovjUZXsZ71Uis0Qeb9Gqo8IZV7ckE.woff2) format('woff2');unicode-range:U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;}@font-face{font-family:MyHelvetica;src:local(\\"Helvetica Neue Bold\\"), local(\\"HelveticaNeue-Bold\\"), - url(MgOpenModernaBold.ttf); - font-weight: bold;}" -` + url(MgOpenModernaBold.ttf);font-weight:bold;}" +`; diff --git a/test/__snapshots__/inject-global.test.js.snap b/test/__snapshots__/inject-global.test.js.snap index 3d8a6aaa3..afc879b40 100644 --- a/test/__snapshots__/inject-global.test.js.snap +++ b/test/__snapshots__/inject-global.test.js.snap @@ -1,13 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`injectGlobal 1`] = ` -"html { - background: pink; - }html.active { - background: red; - }body { - color: yellow; - margin: 0; - padding: 0; - }" -`; +exports[`injectGlobal 1`] = `"html {background:pink;}html.active {background:red;}body {color:yellow;margin:0px;padding:0px;}"`; diff --git a/test/__snapshots__/keyframes.test.js.snap b/test/__snapshots__/keyframes.test.js.snap index 832a5245b..db944dbab 100644 --- a/test/__snapshots__/keyframes.test.js.snap +++ b/test/__snapshots__/keyframes.test.js.snap @@ -1,58 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`keyframes keyframes with interpolation 1`] = ` -"@keyframes animation-bounce-10a3qiv-cmo0tx { from, 20%, 53%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - -webkit-transform: translate3d(0,0,0); - -ms-transform: translate3d(0,0,0); - transform: translate3d(0,0,0); - } - - 40%, 43% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -30px, 0); - -ms-transform: translate3d(0, -30px, 0); - transform: translate3d(0, -30px, 0); - } - - 70% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -15px, 0); - -ms-transform: translate3d(0, -15px, 0); - transform: translate3d(0, -15px, 0); - } - - 90% { - -webkit-transform: translate3d(0,-4px,0); - -ms-transform: translate3d(0,-4px,0); - transform: translate3d(0,-4px,0); - } }.css-H1-o0kcx2-fy8ana { font-size: 20px; - -webkit-animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; - animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; }@keyframes animation-1fpnjxj-1mmv2re { from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }" +.glamor-0 { + -webkit-animation: animation_1cnjdtk 2s linear infinite; + animation: animation_1cnjdtk 2s linear infinite; +} + +

+ hello world +

`; +exports[`keyframes keyframes with interpolation 2`] = `"@-webkit-keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}@keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}.css-1m1x9re{-webkit-animation:animation_7jdctn 2s linear infinite;animation:animation_7jdctn 2s linear infinite;}@-webkit-keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}.css-1nja0ph{-webkit-animation:animation_1cnjdtk 2s linear infinite;animation:animation_1cnjdtk 2s linear infinite;}"`; + exports[`keyframes renders 1`] = ` -.css-H1-o0kcx2-fy8ana { - font-size: 20px; - -webkit-animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; - animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; +.glamor-0 { + -webkit-animation: animation_7jdctn 2s linear infinite; + animation: animation_7jdctn 2s linear infinite; }

hello world

diff --git a/test/__snapshots__/parser.test.js.snap b/test/__snapshots__/parser.test.js.snap index c1a88a6ad..05d786b39 100644 --- a/test/__snapshots__/parser.test.js.snap +++ b/test/__snapshots__/parser.test.js.snap @@ -2,82 +2,72 @@ exports[`parser basic 1`] = ` Object { - "composes": 0, - "hasCssFunction": false, - "hasOtherMatch": false, - "hasVar": false, - "rules": Array [ - ".thing { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - width: var(--css-hash-0); -}", - ], + "composesCount": 0, + "staticCSSRules": Array [], + "styles": Object { + ".thing": Object { + "WebkitBoxPack": "center", + "display": "-webkit-box; display: -ms-flexbox; display: flex", + "justifyContent": "center", + "msFlexPack": "center", + "width": "var(--css-hash-0)", + }, + }, } `; exports[`parser fancy 1`] = ` Object { - "composes": 0, - "hasCssFunction": false, - "hasOtherMatch": false, - "hasVar": false, - "rules": Array [ - ".some-selector { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - width: var(--css-hash-0); -}", - ".some-selector:hover { - background-color: green; -}", - "@media (max-width: 500px) { - .some-selector { - height: var(--css-hash-1); - position: fixed; - } -}", - "@media print { - .some-selector { - display: none; - } -}", - ".some-selector::before { - color: blue; - width: 20px; - height: 20px; - content: 'pseudo' -}", - ], + "composesCount": 0, + "staticCSSRules": Array [], + "styles": Object { + ".some-selector": Object { + "WebkitBoxPack": "center", + "display": "-webkit-box; display: -ms-flexbox; display: flex", + "justifyContent": "center", + "msFlexPack": "center", + "width": "var(--css-hash-0)", + }, + ".some-selector::before": Object { + "color": "blue", + "content": "'pseudo'", + "height": "20px", + "width": "20px", + }, + ".some-selector:hover": Object { + "backgroundColor": "green", + }, + "@media (max-width: 500px)": Object { + ".some-selector": Object { + "height": "var(--css-hash-1)", + "position": "fixed", + }, + }, + "@media print": Object { + ".some-selector": Object { + "display": "none", + }, + }, + }, } `; exports[`parser static 1`] = ` Object { - "composes": 0, - "hasCssFunction": false, - "hasOtherMatch": false, - "hasVar": false, - "rules": Array [ + "composesCount": 0, + "staticCSSRules": Array [ ".thing { - display: block; - height: 50px; - width: 30px + display: block; + height: 50px; + width: 30px }", ], + "styles": Object { + ".thing": Object { + "display": "block", + "height": "50px", + "width": "30px", + }, + }, } `; diff --git a/test/__snapshots__/react.test.js.snap b/test/__snapshots__/react.test.js.snap index 2d77caae7..0a1233d60 100644 --- a/test/__snapshots__/react.test.js.snap +++ b/test/__snapshots__/react.test.js.snap @@ -1,77 +1,140 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`styled attr 1`] = ` -.css-H1-l44jbj-1u4tp6a { - font-size: 48; - margin: 4rem; - position: absolute; -} - -

-`; - exports[`styled basic render 1`] = ` -.css-H1-8xpzga-1e133rd { +.glamor-0 { + color: blue; font-size: 20px; }

hello world

`; exports[`styled basic render with object as style 1`] = ` -.css-kp2uw9 { +.glamor-0 { font-size: 20px; }

hello world

`; exports[`styled call expression 1`] = ` -.css-H1-13wdnau-xy96vj { +.glamor-0 { font-size: 20px; }

hello world

`; -exports[`styled component as selector 1`] = ` -.css-Thing-1kdnbhf-19sb43k { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; +exports[`styled change theme 1`] = ` +.glamor-0 { + color: green; } -.css-Thing-1kdnbhf-19sb43k .css-H1-1t8i2zo { - color: green; + + + +
+ this will be green then pink +
+
+
+
+`; + +exports[`styled change theme 2`] = ` +.glamor-0 { + color: pink; } -.css-H1-1t8i2zo-1c6u60b { + + + +
+ this will be green then pink +
+
+
+
+`; + +exports[`styled change theme 3`] = ` + + + +`; + +exports[`styled component as selector 1`] = ` +.glamor-0 { font-size: 20px; } +.glamor-1 { + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} +
hello

This will be green

@@ -80,24 +143,13 @@ exports[`styled component as selector 1`] = ` `; exports[`styled composes 1`] = ` -.css-cssA-cy897j-1p4ppuj { +.glamor-0 { color: blue; -} - -.css-cssB-ob3sm8-fwh0v { - height: 64px; -} - -.css-H2-idm3bz-361gww { font-size: 32px; } -.css-H1-zejj71-ovrrqc { - font-size: 20px; -} -

hello world @@ -105,46 +157,35 @@ exports[`styled composes 1`] = ` `; exports[`styled composes based on props 1`] = ` -.css-cssA-cy897j-1p4ppuj { +.glamor-0 { color: blue; }

hello world

`; exports[`styled composes based on props 2`] = ` -.css-cssB-7o6h5j-dcpp5a { +.glamor-0 { color: green; }

hello world

`; exports[`styled composes with objects 1`] = ` -.css-cssB-ob3sm8-fwh0v { - height: 64px; -} - -.css-H2-idm3bz-361gww { - font-size: 32px; -} - -.css-1olphq9 { +.glamor-0 { color: #333; - font-size: 1.333em; -} - -.css-H1-zejj71-1ym7xv { - font-size: 3.157334518321em; + font-size: 32px; + height: 64px; } @media only screen and (-webkit-min-device-pixel-ratio: 1.5), @@ -152,13 +193,13 @@ exports[`styled composes with objects 1`] = ` only screen and (-o-min-device-pixel-ratio: 1.5/1), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) { - .css-1olphq9 { + .glamor-0 { font-size: 1.4323121856191332em; } }

hello world @@ -166,108 +207,210 @@ exports[`styled composes with objects 1`] = ` `; exports[`styled composition 1`] = ` -.css-H2-vxb7tq-1gn1gp5 { - font-size: 13.333333333333334; -} - -.css-H1-rs9k70-qz486c { - font-size: 20px; +.glamor-0 { + font-size: 13.333333333333334px; }

hello world

`; exports[`styled function in expression 1`] = ` -.css-H1-rs9k70-qz486c { - font-size: 20px; -} - -.css-H2-vxb7tq-1pd0qgw { +.glamor-0 { font-size: 40px; }

hello world

`; -exports[`styled higher order component 1`] = ` -.css-squirtle-blue-bg-135ox65-8g3djl { - background-color: #7FC8D6; +exports[`styled handles more than 10 dynamic properties 1`] = ` +.glamor-0 { + -webkit-text-decoration: underline; + text-decoration: underline; + border-right: solid blue 54px; + background: white; + color: black; + display: block; + border-radius: 3px; + padding: 25px; + width: 500px; + z-index: 100; + font-size: 18px; + text-align: center; + border-left: undefined; } -.css-onyx-165zlfj-sv1xya { +

+ hello world +

+`; + +exports[`styled higher order component 1`] = ` +.glamor-0 { + font-size: 20px; + name: onyx; background-color: '#343a40'; - -webkit-flex-direction: column; - -ms-flex-direction: column; -webkit-box-orient: vertical; -webkit-box-direction: normal; + -ms-flex-direction: column; flex-direction: column; } -.css-Content-13wdnau-1z6hk6 { - font-size: 20px; -} -
`; exports[`styled innerRef 1`] = ` -.css-H1-ijh7uz-10q50rl { +.glamor-0 { font-size: 12px; }

hello world

`; +exports[`styled input placeholder 1`] = ` +.glamor-0::-webkit-input-placeholder { + background-color: green; +} + +.glamor-0:-ms-input-placeholder { + background-color: green; +} + +.glamor-0::placeholder { + background-color: green; +} + + + hello world + +`; + +exports[`styled input placeholder object 1`] = ` +.glamor-0::-webkit-input-placeholder { + background-color: green; +} + +.glamor-0:-ms-input-placeholder { + background-color: green; +} + +.glamor-0::placeholder { + background-color: green; +} + + + hello world + +`; + exports[`styled name 1`] = ` -.css-FancyH1-1azfbv1-1fzd8kz { +.glamor-0 { + name: FancyH1; font-size: 20px; }

hello world

`; +exports[`styled nested 1`] = ` +.glamor-0 { + font-size: 20px; +} + +.glamor-1 { + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} + +.glamor-1 div { + color: green; +} + +.glamor-1 div span { + color: red; +} + +
+ hello +

+ This will be green +

+ world +
+`; + exports[`styled no dynamic 1`] = ` -.css-H1-ijh7uz-10q50rl { +.glamor-0 { font-size: 12px; }

hello world

`; -exports[`styled objects 1`] = ` -.css-1viuxsa { - padding: 10px; +exports[`styled object composition 1`] = ` +.glamor-0 { + border-radius: 50%; + -webkit-transition: -webkit-transform 400ms ease-in-out; + transition: -webkit-transform 400ms ease-in-out; + transition: transform 400ms ease-in-out; + transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out; + border: 3px solid currentColor; + width: 96px; + height: 96px; + color: blue; } -.css-1fe3owl { +.glamor-0:hover { + -webkit-transform: scale(1.2); + transform: scale(1.2); +} + + +`; + +exports[`styled objects 1`] = ` +.glamor-0 { + padding: 10px; display: flex; }

hello world @@ -275,29 +418,15 @@ exports[`styled objects 1`] = ` `; exports[`styled themes 1`] = ` -.css-cssA-cy897j-1p4ppuj { - color: blue; -} - -.css-cssB-ob3sm8-fwh0v { +.glamor-0 { + background-color: #ffd43b; + color: #8c81d8; height: 64px; -} - -.css-H2-idm3bz-361gww { font-size: 32px; } -.css-H1-iwhfi5-s8bsgs { - font-size: 20px; - color: #8c81d8; -} - -.css-Heading-1ybg51t-91l5qw { - background-color: #ffd43b; -} - hello world diff --git a/test/__snapshots__/server.test.js.snap b/test/__snapshots__/server.test.js.snap index a1135c476..4f5c996b9 100644 --- a/test/__snapshots__/server.test.js.snap +++ b/test/__snapshots__/server.test.js.snap @@ -2,67 +2,21 @@ exports[`extractCritical returns static css 1`] = ` Object { - "css": ".no-prefix { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - }.css-Main-17vxl0k-1g2c78b { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }.css-Image-1d2rnu3-1tqe2cj { border-radius: 50%; - height: 30px; - width: 30px; - background-color: red }.css-Image-1d2rnu3-1rejdxz { border-radius: 50%; - height: 100px; - width: 100px; - background-color: red }.css-Image-1d2rnu3-1d9ph5s { border-radius: 50%; - height: 50px; - width: 50px; - background-color: red }", - "html": "
", + "css": ".no-prefix {display:-webkit-box; display: -ms-flexbox; display: flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;}.css-71uu79{display:-webkit-box; display: -ms-flexbox; display: flex;}.css-l2qi8{border-radius:50%;height:50px;width:50px;background-color:red;}", + "html": "
", "ids": Array [ - "1g2c78b", - "1tqe2cj", - "1rejdxz", - "1d9ph5s", + "71uu79", + "l2qi8", ], "rules": Array [ Object { - "cssText": ".no-prefix { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - }", + "cssText": ".no-prefix {display:-webkit-box; display: -ms-flexbox; display: flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;}", }, Object { - "cssText": ".css-Main-17vxl0k-1g2c78b { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }", + "cssText": ".css-71uu79{display:-webkit-box; display: -ms-flexbox; display: flex;}", }, Object { - "cssText": ".css-Image-1d2rnu3-1tqe2cj { border-radius: 50%; - height: 30px; - width: 30px; - background-color: red }", - }, - Object { - "cssText": ".css-Image-1d2rnu3-1rejdxz { border-radius: 50%; - height: 100px; - width: 100px; - background-color: red }", - }, - Object { - "cssText": ".css-Image-1d2rnu3-1d9ph5s { border-radius: 50%; - height: 50px; - width: 50px; - background-color: red }", + "cssText": ".css-l2qi8{border-radius:50%;height:50px;width:50px;background-color:red;}", }, ], } diff --git a/test/__snapshots__/vue.test.js.snap b/test/__snapshots__/vue.test.js.snap deleted file mode 100644 index d19994206..000000000 --- a/test/__snapshots__/vue.test.js.snap +++ /dev/null @@ -1,63 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`vue styled based on props 1`] = ` -" -

- " -`; - -exports[`vue styled based on props composes 1`] = ` -" -
- " -`; - -exports[`vue styled basic 1`] = ` -" -
- " -`; - -exports[`vue styled child selector 1`] = ` -" -
wow
- " -`; - -exports[`vue styled creates the correct styles 1`] = ` -".css-StyledComponent-1brvt4i-ulwf5d { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: center; - -ms-flex-align: center; - -webkit-box-align: center; - align-items: center; - position: inherit; }.css-BasedOnProps-csm9t8-1qo3zsu { font-weight: bold; }.css-NonHtmlComponent-15287sz-1wq2ikh { background-color: purple; - color: green; }.css-ChildSelector-13pvedz-1e7xvxz { color: yellow; }.css-ChildSelector-13pvedz-1e7xvxz .css-StyledComponent-1brvt4i { display: none; }" -`; - -exports[`vue styled custom external component 1`] = ` -" -

some text

- " -`; - -exports[`vue styled with class 1`] = ` -" -
- " -`; - -exports[`vue styled with properties 1`] = ` -" -
- " -`; - -exports[`vue styled with style 1`] = ` -" -
- " -`; diff --git a/test/babel/__snapshots__/css-prop.test.js.snap b/test/babel/__snapshots__/css-prop.test.js.snap index 496ae76a0..796edddc9 100644 --- a/test/babel/__snapshots__/css-prop.test.js.snap +++ b/test/babel/__snapshots__/css-prop.test.js.snap @@ -1,60 +1,84 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`babel css prop StringLiteral css prop value 1`] = ` -"
;" `; exports[`babel css prop basic 1`] = ` "import \\"./css-prop.test.emotion.css\\"; -
;" +
;" `; -exports[`babel css prop basic 2`] = `".css-jf1v9l { color: brown; }"`; +exports[`babel css prop basic 2`] = ` +".css-jf1v9l { + color: brown +}" +`; exports[`babel css prop basic inline 1`] = ` -"
;" `; exports[`babel css prop className as expression 1`] = ` -"
;" `; exports[`babel css prop className as expression string 1`] = ` -"
;" `; -exports[`babel css prop css empty 1`] = `"
;"`; +exports[`babel css prop css empty 1`] = ` +"
;" +`; exports[`babel css prop dynamic inline 1`] = ` -"
;" `; exports[`babel css prop emptyClassName 1`] = ` -"
;" `; exports[`babel css prop no css attr 1`] = `"
;"`; exports[`babel css prop noClassName 1`] = ` -"
;" `; exports[`babel css prop with spread arg in jsx opening tag 1`] = ` -"
;" `; diff --git a/test/babel/__snapshots__/css.test.js.snap b/test/babel/__snapshots__/css.test.js.snap index e12cf308d..0529276a4 100644 --- a/test/babel/__snapshots__/css.test.js.snap +++ b/test/babel/__snapshots__/css.test.js.snap @@ -2,206 +2,261 @@ exports[`babel css extract basic object support 1`] = ` "css({ - display: ['-webkit-box', '-ms-flexbox', 'flex'] + 'display': '-webkit-box; display: -ms-flexbox; display: flex' });" `; exports[`babel css extract composes 1`] = ` "import './css.test.emotion.css'; -const cls1 = \`\${'css-cls1-1q8jsgx'}\`; -const cls2 = css(['css-cls2-1fsihoz', ['one-class', 'another-class', cls1]], ['center']);" +const cls1 = 'css-cls1-1q8jsgx'; +const cls2 = /*#__PURE__*/css([['one-class', 'another-class', cls1]], ['center'], function createEmotionStyledRules(x0) { + return [{ + 'WebkitBoxPack': 'center', + 'msFlexPack': 'center', + 'justifyContent': 'center', + 'WebkitBoxAlign': \`\${x0}\`, + 'msFlexAlign': \`\${x0}\`, + 'alignItems': \`\${x0}\` + }]; +});" `; exports[`babel css extract composes 2`] = ` -".css-cls1-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; } -.css-cls2-1fsihoz { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: var(--css-cls2-1fsihoz-0); - -ms-flex-align: var(--css-cls2-1fsihoz-0); - -webkit-box-align: var(--css-cls2-1fsihoz-0); - align-items: var(--css-cls2-1fsihoz-0) }" +".css-cls1-1q8jsgx { + display: -webkit-box; display: -ms-flexbox; display: flex +}" `; exports[`babel css extract composes no dynamic 1`] = ` "import './css.test.emotion.css'; -const cls1 = \`\${'css-cls1-1q8jsgx'}\`; -const cls2 = \`\${'css-cls2-m25v9m'} \${'one-class'} \${'another-class'} \${cls1}\`;" +const cls1 = 'css-cls1-1q8jsgx'; +const cls2 = /*#__PURE__*/css(['one-class', 'another-class', cls1], [], function createEmotionStyledRules() { + return [{ + 'WebkitBoxPack': 'center', + 'msFlexPack': 'center', + 'justifyContent': 'center', + 'WebkitBoxAlign': 'center', + 'msFlexAlign': 'center', + 'alignItems': 'center' + }]; +});" `; exports[`babel css extract composes no dynamic 2`] = ` -".css-cls1-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; } -.css-cls2-m25v9m { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: center; - -ms-flex-align: center; - -webkit-box-align: center; - align-items: center }" +".css-cls1-1q8jsgx { + display: -webkit-box; display: -ms-flexbox; display: flex +}" `; exports[`babel css extract composes with objects 1`] = ` -"import './css.test.emotion.css'; - +" const cls1 = css({ - display: ['-webkit-box', '-ms-flexbox', 'flex'] + 'display': '-webkit-box; display: -ms-flexbox; display: flex' }); -const cls2 = \`\${'css-cls2-k8jueb'} \${cls1}\`;" +const cls2 = /*#__PURE__*/css([cls1], [], function createEmotionStyledRules() { + return [{ + 'height': '20', + 'WebkitBoxPack': 'center', + 'msFlexPack': 'center', + 'justifyContent': 'center' + }]; +});" `; exports[`babel css extract composes with objects 2`] = ` -".css-cls2-k8jueb { - height: 20; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }" +".css-cls1-1q8jsgx { + display: -webkit-box; display: -ms-flexbox; display: flex +}" `; exports[`babel css extract css basic 1`] = ` "import \\"./css.test.emotion.css\\"; -css([\\"css-cu78iu\\"], [widthVar]);" +\\"css-153l48f\\";" `; exports[`babel css extract css basic 2`] = ` -".css-cu78iu { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: var(--css-cu78iu-0); }" -`; - -exports[`babel css extract interpolation in selector 1`] = ` +".css-153l48f { + margin: 12px 48px; + color: #ffffff; color: blue; + display: -webkit-box; display: -ms-flexbox; display: flex; + -webkit-box-flex: 1; + -ms-flex: 1 0 auto; + flex: 1 0 auto +}" +`; + +exports[`babel css extract dynamic property objects 1`] = ` " -const cls2 = css([\\"css-cls2-16o34vq\\"], [className], function createEmotionRules(x0) { - return [\`.css-cls2-16o34vq { margin: 12px 48px; - color: #ffffff; }\`, \`.css-cls2-16o34vq .\${x0} { display: none; }\`]; +css({ + 'fontSize': '10px', + [\`w\${'idth'}\`]: '20px' });" `; exports[`babel css extract prefixed array of objects 1`] = ` " css([{ - borderRadius: '50%', - WebkitBoxSizing: 'border-box', - boxSizing: 'border-box', - display: ['-webkit-box', '-ms-flexbox', 'flex'], + 'borderRadius': '50%', + 'WebkitBoxSizing': 'border-box', + 'boxSizing': 'border-box', + [\`\${display}\`]: 'flex', ':hover': { - WebkitTransform: 'scale(1.2)', - transform: 'scale(1.2)' + 'WebkitTransform': 'scale(1.2)', + 'transform': 'scale(1.2)' } }, { - WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: ['-webkit-transform 400ms ease-in-out', 'transform 400ms ease-in-out', 'transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out'] + 'WebkitTransition': '-webkit-transform 400ms ease-in-out', + 'transition': '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out' }]);" `; exports[`babel css extract prefixed objects 1`] = ` " css({ - borderRadius: '50%', - WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: ['-webkit-transform 400ms ease-in-out', 'transform 400ms ease-in-out', 'transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out'], - WebkitBoxSizing: 'border-box', - boxSizing: 'border-box', - display: ['-webkit-box', '-ms-flexbox', 'flex'], + 'borderRadius': '50%', + 'WebkitTransition': '-webkit-transform 400ms ease-in-out', + 'transition': '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', + 'WebkitBoxSizing': 'border-box', + 'boxSizing': 'border-box', + 'display': '-webkit-box; display: -ms-flexbox; display: flex', ':hover': { - WebkitTransform: 'scale(1.2)', - transform: 'scale(1.2)' + 'WebkitTransform': 'scale(1.2)', + 'transform': 'scale(1.2)' } });" `; +exports[`babel css inline array of objects 1`] = ` +" +const cls2 = css([{ + 'display': '-webkit-box; display: -ms-flexbox; display: flex', + 'WebkitBoxFlex': '1', + 'msFlex': '1', + 'flex': '1', + 'WebkitBoxAlign': \`\${'center'}\`, + 'msFlexAlign': \`\${'center'}\`, + 'alignItems': \`\${'center'}\` +}, { + 'WebkitBoxPack': 'start', + 'msFlexPack': 'start', + 'justifyContent': 'flex-start' +}]);" +`; + exports[`babel css inline composes 1`] = ` " -const cls1 = css(['css-cls1-1q8jsgx'], [], function createEmotionRules() { - return [\`.css-cls1-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const cls1 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { + return [{ + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + }]; }); -const cls2 = css(['css-cls2-v9djpl', 'one-class', 'another-class', cls1], ['center'], function createEmotionRules(x0) { - return [\`.css-cls2-v9djpl { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: \${x0}; - -ms-flex-align: \${x0}; - -webkit-box-align: \${x0}; - align-items: \${x0} }\`]; +const cls2 = /*#__PURE__*/css(['one-class', 'another-class', cls1], ['center'], function createEmotionStyledRules(x0) { + return [{ + 'WebkitBoxPack': 'center', + 'msFlexPack': 'center', + 'justifyContent': 'center', + 'WebkitBoxAlign': \`\${x0}\`, + 'msFlexAlign': \`\${x0}\`, + 'alignItems': \`\${x0}\` + }]; });" `; exports[`babel css inline css basic 1`] = ` " -css([\\"css-cu78iu\\"], [widthVar], function createEmotionRules(x0) { - return [\`.css-cu78iu { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: \${x0}; }\`]; +/*#__PURE__*/css([], [widthVar], function createEmotionStyledRules(x0) { + return [{ + \\"margin\\": \\"12px 48px\\", + \\"color\\": \\"#ffffff; color: blue\\", + \\"display\\": \\"-webkit-box; display: -ms-flexbox; display: flex\\", + \\"WebkitBoxFlex\\": \\"1\\", + \\"msFlex\\": \\"1 0 auto\\", + \\"flex\\": \\"1 0 auto\\", + \\"width\\": \`\${x0}\` + }]; });" `; exports[`babel css inline interpolation in selector 1`] = ` " -const cls2 = css([\\"css-cls2-16o34vq\\"], [className], function createEmotionRules(x0) { - return [\`.css-cls2-16o34vq { margin: 12px 48px; - color: #ffffff; }\`, \`.css-cls2-16o34vq .\${x0} { display: none; }\`]; +const cls2 = /*#__PURE__*/css([], [className], function createEmotionStyledRules(x0) { + return [{ + \\"margin\\": \\"12px 48px\\", + \\"color\\": \\"#ffffff\\", + [\`\${x0}\`]: { + \\"display\\": \\"none\\" + } + }]; });" `; exports[`babel css inline lots of composes 1`] = ` " -const cls2 = css(['css-cls2-hal2lb', 'one-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class'], ['center'], function createEmotionRules(x0) { - return [\`.css-cls2-hal2lb { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: \${x0}; - -ms-flex-align: \${x0}; - -webkit-box-align: \${x0}; - align-items: \${x0} }\`]; +const cls2 = /*#__PURE__*/css(['one-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class', 'another-class'], ['center'], function createEmotionStyledRules(x0) { + return [{ + 'WebkitBoxPack': 'center', + 'msFlexPack': 'center', + 'justifyContent': 'center', + 'WebkitBoxAlign': \`\${x0}\`, + 'msFlexAlign': \`\${x0}\`, + 'alignItems': \`\${x0}\` + }]; +});" +`; + +exports[`babel css inline nested expanded properties 1`] = ` +" +/*#__PURE__*/css([], [], function createEmotionStyledRules() { + return [{ + \\"margin\\": \\"12px 48px\\", + \\"& .div\\": { + \\"display\\": \\"'flex'\\" + } + }]; +});" +`; + +exports[`babel css inline object with a bunch of stuff 1`] = ` +" +const cls2 = css({ + 'display': '-webkit-box; display: -ms-flexbox; display: flex', + 'WebkitBoxFlex': '1', + 'msFlex': '1', + 'flex': '1', + 'WebkitBoxAlign': \`\${'center'}\`, + 'msFlexAlign': \`\${'center'}\`, + 'alignItems': \`\${'center'}\` });" `; exports[`babel css inline only composes 1`] = ` " -const cls1 = css(['css-cls1-1q8jsgx'], [], function createEmotionRules() { - return [\`.css-cls1-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const cls1 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { + return [{ + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + }]; }); -const cls2 = css(['css-cls2-64ycl', 'one-class', 'another-class', cls1], []);" +const cls2 = /*#__PURE__*/css(['one-class', 'another-class', cls1], [], function createEmotionStyledRules() { + return [{}]; +});" `; exports[`babel css inline only styles on nested selector 1`] = ` " -const cls1 = css([\\"css-cls1-1q8jsgx\\"], [], function createEmotionRules() { - return [\`.css-cls1-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const cls1 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { + return [{ + \\"display\\": \\"-webkit-box; display: -ms-flexbox; display: flex\\" + }]; }); -const cls2 = css([\\"css-cls2-1nbi349\\"], [], function createEmotionRules() { - return [\`.css-cls2-1nbi349:hover { - background: pink; -}\`]; +const cls2 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { + return [{ + \\"&:hover\\": { + \\"background\\": \\"pink\\" + } + }]; });" `; diff --git a/test/babel/__snapshots__/font-face.test.js.snap b/test/babel/__snapshots__/font-face.test.js.snap index 3f2864075..767317ad4 100644 --- a/test/babel/__snapshots__/font-face.test.js.snap +++ b/test/babel/__snapshots__/font-face.test.js.snap @@ -3,11 +3,13 @@ exports[`fontFace babel extract basic 1`] = `"import \\"./font-face.test.emotion.css\\";"`; exports[`fontFace babel extract basic 2`] = ` -"@font-face {font-family: MyHelvetica; - src: local(\\"Helvetica Neue Bold\\"), +"@font-face { + font-family: MyHelvetica; + src: local(\\"Helvetica Neue Bold\\"), local(\\"HelveticaNeue-Bold\\"), url(MgOpenModernaBold.ttf); - font-weight: bold;}" + font-weight: bold +}" `; exports[`fontFace babel extract basic assign to variable 1`] = ` @@ -17,36 +19,44 @@ const thisWillBeUndefined = undefined;" `; exports[`fontFace babel extract basic assign to variable 2`] = ` -"@font-face {font-family: MyHelvetica; - src: local(\\"Helvetica Neue Bold\\"), +"@font-face { + font-family: MyHelvetica; + src: local(\\"Helvetica Neue Bold\\"), local(\\"HelveticaNeue-Bold\\"), url(MgOpenModernaBold.ttf); - font-weight: bold;}" + font-weight: bold +}" `; exports[`fontFace babel extract interpolation 1`] = ` " -fontFace([\`@font-face {font-family: \${fontFamilyName}; - src: local(\\"Helvetica Neue Bold\\"), - local(\\"HelveticaNeue-Bold\\"), - url(MgOpenModernaBold.ttf); - font-weight: bold;}\`]);" +fontFace([], [fontFamilyName], function createEmotionStyledRules(x0) { + return [{ + \\"fontFamily\\": \`\${x0}\`, + \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", + \\"fontWeight\\": \\"bold\\" + }]; +});" `; exports[`fontFace babel inline basic 1`] = ` " -fontFace([\`@font-face {font-family: MyHelvetica; - src: local(\\"Helvetica Neue Bold\\"), - local(\\"HelveticaNeue-Bold\\"), - url(MgOpenModernaBold.ttf); - font-weight: bold;}\`]);" +fontFace([], [], function createEmotionStyledRules() { + return [{ + \\"fontFamily\\": \\"MyHelvetica\\", + \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", + \\"fontWeight\\": \\"bold\\" + }]; +});" `; exports[`fontFace babel inline interpolation 1`] = ` " -fontFace([\`@font-face {font-family: \${fontFamilyName}; - src: local(\\"Helvetica Neue Bold\\"), - local(\\"HelveticaNeue-Bold\\"), - url(MgOpenModernaBold.ttf); - font-weight: bold;}\`]);" +fontFace([], [fontFamilyName], function createEmotionStyledRules(x0) { + return [{ + \\"fontFamily\\": \`\${x0}\`, + \\"src\\": \\"local(\\\\\\"Helvetica Neue Bold\\\\\\"),\\\\n local(\\\\\\"HelveticaNeue-Bold\\\\\\"),\\\\n url(MgOpenModernaBold.ttf)\\", + \\"fontWeight\\": \\"bold\\" + }]; +});" `; diff --git a/test/babel/__snapshots__/fs.test.js.snap b/test/babel/__snapshots__/fs.test.js.snap index 76948692f..e0839964b 100644 --- a/test/babel/__snapshots__/fs.test.js.snap +++ b/test/babel/__snapshots__/fs.test.js.snap @@ -1,49 +1,43 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`babel plugin fs creates and writes to the css file when it does not exist 1`] = ` -".css-class-lk4ur6 { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: var(--css-class-lk4ur6-0); }" +".css-class-1yfv4zm { + margin: 12px 48px; + color: #ffffff; color: blue; + display: -webkit-box; display: -ms-flexbox; display: flex; + -webkit-box-flex: 1; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + name: class +}" `; exports[`babel plugin fs creates and writes to the css file when it does not exist 2`] = ` "import \\"./fs.test.emotion.css\\"; -css([\\"css-class-lk4ur6\\"], [widthVar]);" +\\"css-class-1yfv4zm\\";" `; exports[`babel plugin fs does not write to the css file when it is the same as is already written 1`] = ` "import \\"./fs.test.emotion.css\\"; -css([\\"css-class-lk4ur6\\"], [widthVar]);" +\\"css-class-1yfv4zm\\";" `; exports[`babel plugin fs writes to the css file when it does exist 1`] = ` -".css-class-lk4ur6 { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: var(--css-class-lk4ur6-0); }" +".css-class-1yfv4zm { + margin: 12px 48px; + color: #ffffff; color: blue; + display: -webkit-box; display: -ms-flexbox; display: flex; + -webkit-box-flex: 1; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + name: class +}" `; exports[`babel plugin fs writes to the css file when it does exist 2`] = ` "import \\"./fs.test.emotion.css\\"; -css([\\"css-class-lk4ur6\\"], [widthVar]);" +\\"css-class-1yfv4zm\\";" `; diff --git a/test/babel/__snapshots__/inject-global.test.js.snap b/test/babel/__snapshots__/inject-global.test.js.snap index e20412de4..746032a1a 100644 --- a/test/babel/__snapshots__/inject-global.test.js.snap +++ b/test/babel/__snapshots__/inject-global.test.js.snap @@ -8,66 +8,84 @@ const thisWillBeUndefined = undefined;" exports[`babel injectGlobal extract injectGlobal assign to variable 2`] = ` "body { - margin: 0; - padding: 0; - } + margin: 0; + padding: 0 +} body > div { - display: none; + display: none } html { - background: green; - }" + background: green +}" `; exports[`babel injectGlobal extract injectGlobal basic 1`] = `"import \\"./inject-global.test.emotion.css\\";"`; exports[`babel injectGlobal extract injectGlobal basic 2`] = ` "body { - margin: 0; - padding: 0; - } + margin: 0; + padding: 0 +} body > div { - display: none; + display: none } html { - background: green; - }" + background: green +}" `; exports[`babel injectGlobal extract injectGlobal with interpolation 1`] = ` " -injectGlobal([\`body { - margin: 0; - padding: 0; - display: \${display}; - }\`, \`body > div { - display: none; -}\`, \`html { - background: green; - }\`]);" +injectGlobal([], [display], function createEmotionStyledRules(x0) { + return [{ + \\"body\\": { + \\"margin\\": \\"0\\", + \\"padding\\": \\"0\\", + \\"display\\": \`\${x0}\` + }, + \\"body > div\\": { + \\"display\\": \\"none\\" + }, + \\"html\\": { + \\"background\\": \\"green\\" + } + }]; +});" `; exports[`babel injectGlobal inline injectGlobal basic 1`] = ` " -injectGlobal([\`body { - margin: 0; - padding: 0; - }\`, \`body > div { - display: none; -}\`, \`html { - background: green; - }\`]);" +injectGlobal([], [], function createEmotionStyledRules() { + return [{ + \\"body\\": { + \\"margin\\": \\"0\\", + \\"padding\\": \\"0\\" + }, + \\"body > div\\": { + \\"display\\": \\"-webkit-box; display: -ms-flexbox; display: flex\\" + }, + \\"html\\": { + \\"background\\": \\"green\\" + } + }]; +});" `; exports[`babel injectGlobal inline injectGlobal with interpolation 1`] = ` " -injectGlobal([\`body { - margin: 0; - padding: 0; - display: \${display}; - }\`, \`body > div { - display: none; -}\`, \`html { - background: green; - }\`]);" +injectGlobal([], [display], function createEmotionStyledRules(x0) { + return [{ + \\"body\\": { + \\"margin\\": \\"0\\", + \\"padding\\": \\"0\\", + \\"display\\": \`\${x0}\` + }, + \\"body > div\\": { + \\"display\\": \\"none\\" + }, + \\"html\\": { + \\"background\\": \\"green\\" + } + }]; +});" `; diff --git a/test/babel/__snapshots__/keyframes.test.js.snap b/test/babel/__snapshots__/keyframes.test.js.snap index ef2be43d4..adb87d248 100644 --- a/test/babel/__snapshots__/keyframes.test.js.snap +++ b/test/babel/__snapshots__/keyframes.test.js.snap @@ -3,62 +3,76 @@ exports[`babel keyframes extract keyframes basic 1`] = ` "import \\"./keyframes.test.emotion.css\\"; -const rotate360 = \\"animation-rotate360-f35ahc\\";" +const rotate360 = \\"css-rotate360-f35ahc\\";" `; exports[`babel keyframes extract keyframes basic 2`] = ` -"@keyframes animation-rotate360-f35ahc { from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }" +"@-webkit-keyframes css-rotate360-f35ahc { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg) + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg) + } +} +@keyframes css-rotate360-f35ahc { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg) + } + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg) + } +}" `; exports[`babel keyframes extract keyframes with interpolation 1`] = ` " -const rotate360 = keyframes(\\"animation-rotate360-9bsj7q\\", [\`{ from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - - to { - -webkit-transform: rotate(\${endingRotation}); - -ms-transform: rotate(\${endingRotation}); - transform: rotate(\${endingRotation}); - } }\`]);" +const rotate360 = /*#__PURE__*/keyframes([], [endingRotation], function createEmotionStyledRules(x0) { + return [{ + \\"from\\": { + \\"WebkitTransform\\": \\"rotate(0deg)\\", + \\"transform\\": \\"rotate(0deg)\\" + }, + \\"to\\": { + \\"WebkitTransform\\": \`rotate(\${x0})\`, + \\"transform\\": \`rotate(\${x0})\` + } + }]; +});" `; exports[`babel keyframes inline keyframes basic 1`] = ` " -const rotate360 = keyframes(\\"animation-rotate360-10gy9ar\\", [\`{ from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }\`]);" +const rotate360 = /*#__PURE__*/keyframes([], [], function createEmotionStyledRules() { + return [{ + \\"from\\": { + \\"WebkitTransform\\": \\"rotate(0deg)\\", + \\"transform\\": \\"rotate(0deg)\\" + }, + \\"to\\": { + \\"WebkitTransform\\": \\"rotate(360deg)\\", + \\"transform\\": \\"rotate(360deg)\\" + } + }]; +});" `; exports[`babel keyframes inline keyframes with interpolation 1`] = ` " -const rotate360 = keyframes(\\"animation-rotate360-r2494l\\", [\`{ from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - to { - -webkit-transform: rotate(\${endingRotation}); - -ms-transform: rotate(\${endingRotation}); - transform: rotate(\${endingRotation}); - } }\`]);" +const rotate360 = /*#__PURE__*/keyframes([], [endingRotation], function createEmotionStyledRules(x0) { + return [{ + \\"from\\": { + \\"WebkitTransform\\": \\"rotate(0deg)\\", + \\"transform\\": \\"rotate(0deg)\\" + }, + \\"to\\": { + \\"WebkitTransform\\": \`rotate(\${x0})\`, + \\"transform\\": \`rotate(\${x0})\` + } + }]; +});" `; diff --git a/test/babel/__snapshots__/macro.test.js.snap b/test/babel/__snapshots__/macro.test.js.snap index cde705b36..9103468fd 100644 --- a/test/babel/__snapshots__/macro.test.js.snap +++ b/test/babel/__snapshots__/macro.test.js.snap @@ -3,26 +3,31 @@ exports[`babel macro css 1`] = ` "import { css as _css } from '../../src'; -_css(['css-kgvccl'], [widthVar], function createEmotionRules(x0) { - return [\`.css-kgvccl { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: \${x0}; }\`]; +/*#__PURE__*/_css([], [widthVar], function createEmotionStyledRules(x0) { + return [{ + 'margin': '12px 48px', + 'color': '#ffffff; color: blue', + 'display': '-webkit-box; display: -ms-flexbox; display: flex', + 'WebkitBoxFlex': '1', + 'msFlex': '1 0 auto', + 'flex': '1 0 auto', + 'width': \`\${x0}\` + }]; });" `; +exports[`babel macro css call with no args 1`] = ` +"import { css as _css } from '../../src'; + +const cls1 = _css();" +`; + exports[`babel macro css object 1`] = ` "import { css as _css } from '../../src'; -const cls1 = _css({ display: 'flex' });" +const cls1 = _css({ + 'display': '-webkit-box; display: -ms-flexbox; display: flex' +});" `; exports[`babel macro flush 1`] = ` @@ -34,11 +39,13 @@ const someOtherVar = _flush;" exports[`babel macro fontFace 1`] = ` "import { fontFace as _fontFace } from '../../src'; -_fontFace([\`@font-face {font-family: MyHelvetica; - src: local(\\"Helvetica Neue Bold\\"), - local(\\"HelveticaNeue-Bold\\"), - url(MgOpenModernaBold.ttf); - font-weight: bold;}\`]);" +_fontFace([], [], function createEmotionStyledRules() { + return [{ + 'fontFamily': 'MyHelvetica', + 'src': 'local(\\"Helvetica Neue Bold\\"),\\\\n local(\\"HelveticaNeue-Bold\\"),\\\\n url(MgOpenModernaBold.ttf)', + 'fontWeight': 'bold' + }]; +});" `; exports[`babel macro hydrate 1`] = ` @@ -50,57 +57,73 @@ const someOtherVar = _hydrate;" exports[`babel macro injectGlobal 1`] = ` "import { injectGlobal as _injectGlobal } from '../../src'; -_injectGlobal([\`body { - margin: 0; - padding: 0; - }\`, \`body > div { - display: none; -}\`, \`html { - background: green; - }\`]);" +_injectGlobal([], [], function createEmotionStyledRules() { + return [{ + 'body': { + 'margin': '0', + 'padding': '0' + }, + 'body > div': { + 'display': 'none' + }, + 'body > div:hover': { + 'color': 'green' + }, + 'body > div:hover span': { + 'color': 'red' + }, + 'body > div:hover span:after': { + 'content': '\\"end of line\\"' + }, + 'html': { + 'background': 'green' + } + }]; +});" `; exports[`babel macro keyframes 1`] = ` "import { keyframes as _keyframes } from '../../src'; -const rotate360 = _keyframes('animation-rotate360-1e2ipf5', [\`{ from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); +const rotate360 = /*#__PURE__*/_keyframes([], [], function createEmotionStyledRules() { + return [{ + 'from': { + 'WebkitTransform': 'rotate(0deg)', + 'transform': 'rotate(0deg)' + }, + 'to': { + 'WebkitTransform': 'rotate(360deg)', + 'transform': 'rotate(360deg)' } - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }\`]);" + }]; +});" `; exports[`babel macro multiple imports 1`] = ` "import { keyframes as _keyframes, css as _css } from '../../src'; -const rotate360 = _keyframes('animation-rotate360-1e2ipf5', [\`{ from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); +const rotate360 = /*#__PURE__*/_keyframes([], [], function createEmotionStyledRules() { + return [{ + 'from': { + 'WebkitTransform': 'rotate(0deg)', + 'transform': 'rotate(0deg)' + }, + 'to': { + 'WebkitTransform': 'rotate(360deg)', + 'transform': 'rotate(360deg)' } - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }\`]); -const thing = _css(['css-thing-dn301t'], [widthVar], function createEmotionRules(x0) { - return [\`.css-thing-dn301t { margin: 12px 48px; - color: #ffffff; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex: 1 0 auto; - -ms-flex: 1 0 auto; - flex: 1 0 auto; - color: blue; - width: \${x0}; }\`]; + }]; +}); +const thing = /*#__PURE__*/_css([], [widthVar], function createEmotionStyledRules(x0) { + return [{ + 'margin': '12px 48px', + 'color': '#ffffff; color: blue', + 'display': '-webkit-box; display: -ms-flexbox; display: flex', + 'WebkitBoxFlex': '1', + 'msFlex': '1 0 auto', + 'flex': '1 0 auto', + 'width': \`\${x0}\` + }]; });" `; @@ -113,8 +136,10 @@ const someOtherVar = _thisDoesNotExist;" exports[`babel macro styled css from react 1`] = ` "import { css as _css } from '../../src/react'; -const someCls = _css(['css-someCls-ymvpej'], [], function createEmotionRules() { - return [\`.css-someCls-ymvpej { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const someCls = /*#__PURE__*/_css([], [], function createEmotionStyledRules() { + return [{ + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + }]; });" `; @@ -122,16 +147,16 @@ exports[`babel macro styled object function 1`] = ` "import _styled from '../../src/react'; const SomeComponent = _styled('div', [{ - display: ['-webkit-box', '-ms-flexbox', 'flex'] -}], []);" + 'display': '-webkit-box; display: -ms-flexbox; display: flex' +}]);" `; exports[`babel macro styled object member 1`] = ` "import _styled from '../../src/react'; const SomeComponent = _styled('div', [{ - display: ['-webkit-box', '-ms-flexbox', 'flex'] -}], []);" + 'display': '-webkit-box; display: -ms-flexbox; display: flex' +}]);" `; exports[`babel macro styled some import that does not exist 1`] = ` @@ -143,15 +168,19 @@ const someOtherVar = _thisDoesNotExist;" exports[`babel macro styled tagged template literal function 1`] = ` "import _styled from '../../src/react'; -const SomeComponent = _styled('div', ['css-SomeComponent-1q8jsgx'], [], function createEmotionStyledRules() { - return [\`.css-SomeComponent-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const SomeComponent = /*#__PURE__*/_styled('div', [], [], function createEmotionStyledRules() { + return [{ + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + }]; });" `; exports[`babel macro styled tagged template literal member 1`] = ` "import _styled from '../../src/react'; -const SomeComponent = _styled('div', ['css-SomeComponent-1q8jsgx'], [], function createEmotionStyledRules() { - return [\`.css-SomeComponent-1q8jsgx { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }\`]; +const SomeComponent = /*#__PURE__*/_styled('div', [], [], function createEmotionStyledRules() { + return [{ + 'display': '-webkit-box; display: -ms-flexbox; display: flex' + }]; });" `; diff --git a/test/babel/__snapshots__/styled.test.js.snap b/test/babel/__snapshots__/styled.test.js.snap index d42654e85..7740df455 100644 --- a/test/babel/__snapshots__/styled.test.js.snap +++ b/test/babel/__snapshots__/styled.test.js.snap @@ -1,292 +1,270 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`babel styled component extract based on props 1`] = ` -"import './styled.test.emotion.css'; -const H1 = styled('h1', ['css-H1-1kwokni'], [fontSize + 'px', props => props.translateX]);" -`; - -exports[`babel styled component extract based on props 2`] = ` -".css-H1-1kwokni { font-size: var(--css-H1-1kwokni-0); - height: 20px; - -webkit-transform: translateX(var(--css-H1-1kwokni-1)); - -ms-transform: translateX(var(--css-H1-1kwokni-1)); - transform: translateX(var(--css-H1-1kwokni-1)); }" -`; - exports[`babel styled component extract basic 1`] = ` -"import './styled.test.emotion.css'; -const H1 = styled('h1', ['css-H1-1mh2el5'], [fontSize + 'px']);" -`; - -exports[`babel styled component extract basic 2`] = `".css-H1-1mh2el5 { font-size: var(--css-H1-1mh2el5-0); height: 20px }"`; - -exports[`babel styled component extract composes 1`] = ` -"import './styled.test.emotion.css'; -const cls1 = \`\${'css-cls1-1ltut9y'}\`; -const H1 = styled('h1', ['css-H1-13ogl0z', cls1], [fontSize + 'px', props => props.translateX]);" +"import \\"./styled.test.emotion.css\\"; +const H1 = styled(\\"h1\\", [\\"styled-H1-10x82eg\\"]);" `; -exports[`babel styled component extract composes 2`] = ` -".css-cls1-1ltut9y { width: 20px; } -.css-H1-13ogl0z { - font-size: var(--css-H1-13ogl0z-0); - height: 20px; - -webkit-transform: translateX(var(--css-H1-13ogl0z-1)); - -ms-transform: translateX(var(--css-H1-13ogl0z-1)); - transform: translateX(var(--css-H1-13ogl0z-1)); }" +exports[`babel styled component extract basic 2`] = ` +".styled-H1-10x82eg { + display: -webkit-box; display: -ms-flexbox; display: flex; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + width: var(--css-hash-0) +} +.styled-H1-10x82eg:hover { + background-color: green +} +@media (max-width: 500px) { + .styled-H1-10x82eg { + height: var(--css-hash-1); + position: fixed + } +} +@media print { + .styled-H1-10x82eg { + display: none + } +} +.styled-H1-10x82eg::before { + color: blue; + width: 20px; + height: 20px; + content: 'pseudo' +}" `; -exports[`babel styled component extract no dynamic 1`] = ` +exports[`babel styled component extract no use 1`] = ` "import \\"./styled.test.emotion.css\\"; -styled(\\"h1\\", [\\"css-14ksm7b\\"], []);" +styled(\\"h1\\", [\\"styled-0\\"]);" `; -exports[`babel styled component extract no dynamic 2`] = `".css-14ksm7b { color: blue; }"`; +exports[`babel styled component extract no use 2`] = `".styled-0 {}"`; -exports[`babel styled component extract no use 1`] = `"\\"h1\\";"`; - -exports[`babel styled component inline attr 1`] = ` -"styled('input', ['css-yj7w7r'], [props => props.height * props.scale, flex, function getMargin(props) { - return props.margin; -}, function getWidth(props) { - return props.width; -}], function createEmotionStyledRules(x0, x1, x2, x3) { - return [\`.css-yj7w7r { margin: \${x2}; - color: #ffffff; - height: \${x0}; - width: \${x3}; - color: blue; - display: \${x1}; }\`]; +exports[`babel styled component inline basic 1`] = ` +"const H1 = /*#__PURE__*/styled('h1', [], [fontSize + 'px'], function createEmotionStyledRules(x0) { + return [{ + 'fontSize': \`\${x0}\` + }]; });" `; -exports[`babel styled component inline attr kitchen sink 1`] = ` -"styled('input', ['css-y6q35k'], [alignItemsUnit, defaultValue, thing, function getMargin(props) { - return (props.margin ? props.margin : '16') + 'px'; -}, function getPadding(props) { - return (props.padding ? props.padding : '16') + 'em'; -}, function getFontSize(props) { - return (props.fontSize ? props.fontSize : '8') + 'ch'; -}, function getWidth(props) { - return (props.width ? props.width : '95') + '%'; -}, function getHeight(props) { - return (props.height ? props.height : '90') + 'vw'; -}, function getDisplay(props) { - return (props.display ? props.display : 'flex') + ''; -}, function getAlignItems(props) { - return (props.alignItems ? props.alignItems : 'fallback') + alignItemsUnit; -}, function getThing(props) { - return (props.thing ? props.thing : defaultValue) + 'px'; -}, function getProp(props) { - return props[thing] + 'px'; -}], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) { - return [\`.css-y6q35k { margin: \${x3}; - padding: \${x4}; - font-size: \${x5}; - width: \${x6}; - height: \${x7}; - display: \${x8}; - position: \${x9}; - position: \${x10}; - position: \${x11}; }\`]; +exports[`babel styled component inline composes based on props 1`] = ` +"const cls1 = /*#__PURE__*/css([], [], function createEmotionStyledRules() { + return [{ + 'width': '20px' + }]; +}); +const H1 = /*#__PURE__*/styled('h1', [props => { + return props.a ? cssA : cssB; +}], [fontSize + 'px', props => props.translateX], function createEmotionStyledRules(x0, x1) { + return [{ + 'fontSize': \`\${x0}\`, + 'height': '20px', + 'WebkitTransform': \`translateX(\${x1})\`, + 'transform': \`translateX(\${x1})\` + }]; });" `; -exports[`babel styled component inline attr with default value 1`] = ` -"styled('input', ['css-fj5c3o'], [function getMargin(props) { - return (props.margin ? props.margin : '16') + ''; -}], function createEmotionStyledRules(x0) { - return [\`.css-fj5c3o { margin: \${x0}; }\`]; +exports[`babel styled component inline dynamic fns 1`] = ` +"const Avatar = /*#__PURE__*/styled('img', [], [props => props.theme.borderRadius, props => props.theme.borderColor], function createEmotionStyledRules(x0, x1) { + return [{ + 'width': '96px', + 'height': '96px', + 'borderRadius': \`\${x0}\`, + 'border': \`1px solid \${x1}\` + }]; });" `; -exports[`babel styled component inline attr with value type 1`] = ` -"styled('input', ['css-3jy6co'], [function getMargin(props) { - return props.margin + 'px'; -}], function createEmotionStyledRules(x0) { - return [\`.css-3jy6co { margin: \${x0}; }\`]; +exports[`babel styled component inline function call 1`] = ` +"/*#__PURE__*/styled(MyComponent, [], [fontSize + 'px'], function createEmotionStyledRules(x0) { + return [{ + 'fontSize': \`\${x0}\` + }]; });" `; -exports[`babel styled component inline attr with value type and default value 1`] = ` -"styled('input', ['css-vb5gjx'], [function getMargin(props) { - return (props.margin ? props.margin : '16') + 'px'; -}], function createEmotionStyledRules(x0) { - return [\`.css-vb5gjx { margin: \${x0}; }\`]; +exports[`babel styled component inline interpolation in different places 1`] = ` +" +const H1 = /*#__PURE__*/styled('h1', [], [fontSize + 'px', props => props.translateX, something, something, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX, props => props.translateX], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) { + return [{ + 'fontSize': \`\${x0}\`, + 'height': '20px', + 'WebkitTransform': \`translateX(\${x1}); -webkit-transform: translateX(\${x4}) translateY(\${x5})\`, + 'transform': \`translateX(\${x1}); transform: translateX(\${x4}) translateY(\${x5})\`, + 'height1': \`\${x2}wow\`, + 'width': \`w\${x3}ow\`, + 'transform1': \`translateX(\${x6}) translateY(\${x7})\`, + 'transform2': \`translateX(\${x8}) translateY(\${x9}\` + }]; });" `; -exports[`babel styled component inline basic 1`] = ` -"const H1 = styled('h1', ['css-H1-1t8i2zo'], [fontSize + 'px'], function createEmotionStyledRules(x0) { - return [\`.css-H1-1t8i2zo { font-size: \${x0}; }\`]; +exports[`babel styled component inline media query 1`] = ` +"const H1 = /*#__PURE__*/styled(\\"h1\\", [], [], function createEmotionStyledRules() { + return [{ + \\"@media print\\": { + \\"fontSize\\": \\"10pt\\" + }, + \\"@media screen\\": { + \\".child-selector\\": { + \\"fontSize\\": \\"13px\\" + } + }, + \\"@media screen, print\\": { + \\"&:hover + &\\": { + \\"lineHeight\\": \\"1.2\\" + } + }, + \\"@media only screen and (min-device-width: 320px) and (max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2)\\": { + \\".child-selector\\": { + \\"lineHeight\\": \\"1.4\\" + } + } + }]; });" `; -exports[`babel styled component inline function call 1`] = ` -"styled(MyComponent, ['css-1t8i2zo'], [fontSize + 'px'], function createEmotionStyledRules(x0) { - return [\`.css-1t8i2zo { font-size: \${x0}; }\`]; +exports[`babel styled component inline more than 10 dynamic values 1`] = ` +"const H1 = /*#__PURE__*/styled('h1', [], ['underline', 54, 'white', 'black', 'block', '3px', '25px', '500px', 100, '18px', 'center', p => p.theme.blue], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) { + return [{ + 'WebkitTextDecoration': \`\${x0}\`, + 'textDecoration': \`\${x0}\`, + 'borderRight': \`solid blue \${x1}px\`, + 'background': \`\${x2}\`, + 'color': \`\${x3}\`, + 'display': \`\${x4}\`, + 'borderRadius': \`\${x5}\`, + 'padding': \`\${x6}\`, + 'width': \`\${x7}\`, + 'zIndex': \`\${x8}\`, + 'fontSize': \`\${x9}\`, + 'textAlign': \`\${x10}\`, + 'borderLeft': \`\${x11}\` + }]; });" `; -exports[`babel styled component inline lots of attrs with interpolated values 1`] = ` -"styled('input', ['css-1kdt7pt'], [marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, marginProp, displayProp, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[marginProp]; -}, function getProp(props) { - return props[displayProp]; -}], function createEmotionStyledRules(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, x31, x32, x33, x34, x35) { - return [\`.css-1kdt7pt { margin: \${x18}; - margin: \${x19}; - margin: \${x20}; - margin: \${x21}; - margin: \${x22}; - margin: \${x23}; - margin: \${x24}; - margin: \${x25}; - margin: \${x26}; - margin: \${x27}; - margin: \${x28}; - margin: \${x29}; - margin: \${x30}; - margin: \${x31}; - margin: \${x32}; - margin: \${x33}; - margin: \${x34}; - display: \${x35}; }\`]; +exports[`babel styled component inline name is correct with no identifier 1`] = ` +" +/*#__PURE__*/css([], [], function createEmotionStyledRules() { + return [{ + \\"margin\\": \\"12px 48px\\", + \\"color\\": \\"#ffffff\\" + }]; });" `; -exports[`babel styled component inline match works on multiple 1`] = ` -"styled('input', ['css-1vvfgof'], [function getMargin(props) { - return (props.margin ? props.margin : '16') + 'px'; -}, function getPadding(props) { - return (props.padding ? props.padding : '16') + 'em'; -}], function createEmotionStyledRules(x0, x1) { - return [\`.css-1vvfgof { margin: \${x0}; - color: blue; - padding: \${x1}; }\`]; +exports[`babel styled component inline nested 1`] = ` +"const H1 = /*#__PURE__*/styled('h1', [], [fontSize + 'px'], function createEmotionStyledRules(x0) { + return [{ + 'fontSize': \`\${x0}\`, + '& div': { + 'color': 'blue' + }, + '& div span': { + 'color': 'red' + } + }]; });" `; -exports[`babel styled component inline name is correct with no identifier 1`] = ` -" -css([\\"css-1cppx8p\\"], [], function createEmotionRules() { - return [\`.css-1cppx8p { margin: 12px 48px; - color: #ffffff; }\`]; +exports[`babel styled component inline no dynamic 1`] = ` +"/*#__PURE__*/styled(\\"h1\\", [], [], function createEmotionStyledRules() { + return [{ + \\"color\\": \\"blue\\" + }]; });" `; -exports[`babel styled component inline no dynamic 1`] = ` -"styled(\\"h1\\", [\\"css-14ksm7b\\"], [], function createEmotionStyledRules() { - return [\`.css-14ksm7b { color: blue; }\`]; +exports[`babel styled component inline no use 1`] = ` +"/*#__PURE__*/styled(\\"h1\\", [], [], function createEmotionStyledRules() { + return [{}]; });" `; -exports[`babel styled component inline no use 1`] = `"\\"h1\\";"`; - exports[`babel styled component inline object composes with classes 1`] = ` " const H1 = styled('h1', ['some-class', props => ({ display: props.display -})], []);" +})]);" `; exports[`babel styled component inline objects based on props 1`] = ` " const H1 = styled('h1', [{ - padding: '10px' + 'padding': '10px' }, props => ({ display: props.display -})], []);" +})]);" `; exports[`babel styled component inline objects fn call 1`] = ` " const H1 = styled('h1', [{ - display: ['-webkit-box', '-ms-flexbox', 'flex'] -}], []);" + 'display': '-webkit-box; display: -ms-flexbox; display: flex' +}]);" `; exports[`babel styled component inline objects prefixed 1`] = ` " const H1 = styled('h1', [{ - borderRadius: '50%', - WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: ['-webkit-transform 400ms ease-in-out', 'transform 400ms ease-in-out', 'transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out'], - WebkitBoxSizing: 'border-box', - boxSizing: 'border-box', - display: ['-webkit-box', '-ms-flexbox', 'flex'], + 'borderRadius': '50%', + 'WebkitTransition': '-webkit-transform 400ms ease-in-out', + 'transition': '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', + 'WebkitBoxSizing': 'border-box', + 'boxSizing': 'border-box', + 'display': '-webkit-box; display: -ms-flexbox; display: flex', ':hover': { - WebkitTransform: 'scale(1.2)', - transform: 'scale(1.2)' + 'WebkitTransform': 'scale(1.2)', + 'transform': 'scale(1.2)' } }, props => { padding: props.padding; -}], []);" +}]);" `; exports[`babel styled component inline styled component as selector 1`] = ` " -const SomeComponent = styled(\\"div\\", [\\"css-SomeComponent-1rk3iho\\"], []); -styled(\\"h1\\", [\\"css-byjn67\\"], [SomeComponent], function createEmotionStyledRules(x0) { - return [\`.css-byjn67 { color: blue; }\`, \`.css-byjn67 \${x0} { color: green; }\`]; +const SomeComponent = /*#__PURE__*/styled(\\"div\\", [], [], function createEmotionStyledRules() { + return [{}]; +}); +/*#__PURE__*/styled(\\"h1\\", [], [SomeComponent], function createEmotionStyledRules(x0) { + return [{ + \\"color\\": \\"blue\\", + [\`.\${x0}\`]: { + \\"color\\": \\"green\\" + } + }]; });" `; -exports[`babel styled component inline styled. objects 1`] = ` +exports[`babel styled component inline styled objects prefixed 1`] = ` " -const H1 = styled(\\"h1\\", [{ - padding: \\"10px\\" +const H1 = styled('h1', [{ + 'borderRadius': '50%', + 'WebkitTransition': '-webkit-transform 400ms ease-in-out', + 'transition': '-webkit-transform 400ms ease-in-out; transition: transform 400ms ease-in-out; transition: transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out', + 'WebkitBoxSizing': 'border-box', + 'boxSizing': 'border-box', + 'display': '-webkit-box; display: -ms-flexbox; display: flex', + ':hover': { + 'WebkitTransform': 'scale(1.2)', + 'transform': 'scale(1.2)' + } }, props => ({ display: props.display -})], []);" +})]);" `; -exports[`babel styled component inline styled. objects prefixed 1`] = ` +exports[`babel styled component inline styled. objects 1`] = ` " -const H1 = styled('h1', [{ - borderRadius: '50%', - WebkitTransition: '-webkit-transform 400ms ease-in-out', - transition: ['-webkit-transform 400ms ease-in-out', 'transform 400ms ease-in-out', 'transform 400ms ease-in-out, -webkit-transform 400ms ease-in-out'], - WebkitBoxSizing: 'border-box', - boxSizing: 'border-box', - display: ['-webkit-box', '-ms-flexbox', 'flex'], - ':hover': { - WebkitTransform: 'scale(1.2)', - transform: 'scale(1.2)' - } +const H1 = styled(\\"h1\\", [{ + \\"padding\\": \\"10px\\" }, props => ({ display: props.display -})], []);" +})]);" `; diff --git a/test/babel/css-prop.test.js b/test/babel/css-prop.test.js index d6144e299..802e47a97 100644 --- a/test/babel/css-prop.test.js +++ b/test/babel/css-prop.test.js @@ -11,7 +11,7 @@ describe('babel css prop', () => { test('basic', () => { const basic = '(
)' const { code } = babel.transform(basic, { - plugins: [plugin], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -23,7 +23,7 @@ describe('babel css prop', () => { test('basic inline', () => { const basic = '(
)' const { code } = babel.transform(basic, { - plugins: [[plugin, { inline: true }]] + plugins: [plugin] }) expect(code).toMatchSnapshot() }) @@ -31,7 +31,7 @@ describe('babel css prop', () => { test('dynamic inline', () => { const basic = `(
)` const { code } = babel.transform(basic, { - plugins: [[plugin, { inline: true }]] + plugins: [plugin] }) expect(code).toMatchSnapshot() }) @@ -47,7 +47,7 @@ describe('babel css prop', () => { test('with spread arg in jsx opening tag', () => { const basic = '(
)' const { code } = babel.transform(basic, { - plugins: [[plugin, { inline: true }]] + plugins: [plugin] }) expect(code).toMatchSnapshot() }) @@ -64,7 +64,7 @@ describe('babel css prop', () => { const basic = '(
)' expect(() => babel.transform(basic, { - plugins: [[plugin, { inline: true }]] + plugins: [plugin] }) ).toThrow() }) diff --git a/test/babel/css.test.js b/test/babel/css.test.js index 56911a193..1b343a6a6 100644 --- a/test/babel/css.test.js +++ b/test/babel/css.test.js @@ -24,12 +24,26 @@ describe('babel css', () => { expect(code).toMatchSnapshot() }) + test('nested expanded properties', () => { + const basic = ` + css\` + margin: 12px 48px; + & .div { + display: 'flex'; + } + \`` + const { code } = babel.transform(basic, { + plugins: [[plugin]] + }) + expect(code).toMatchSnapshot() + }) + test('interpolation in selector', () => { const basic = ` const cls2 = css\` margin: 12px 48px; color: #ffffff; - .\${className} { + \${className} { display: none; } \` @@ -77,7 +91,7 @@ describe('babel css', () => { display: flex; \` const cls2 = css\` - composes: \${'one-class'} \${'another-class'}\${cls1} + composes: \${'one-class'} \${'another-class'}\${cls1}; \` ` const { code } = babel.transform(basic, { @@ -137,8 +151,10 @@ describe('babel css', () => { }) ).toThrowErrorMatchingSnapshot() }) - test('throws correct error when composes is on a nested selector', () => { - const basic = ` + test.skip( + 'throws correct error when composes is on a nested selector', + () => { + const basic = ` const cls1 = css\` display: flex; \` @@ -146,15 +162,44 @@ describe('babel css', () => { justify-content: center; align-items: center; .some-class { - composes: \${cls1} + composes: \${cls1}; } \` ` - expect(() => - babel.transform(basic, { - plugins: [[plugin]] - }) - ).toThrowErrorMatchingSnapshot() + expect(() => + babel.transform(basic, { + plugins: [[plugin]] + }) + ).toThrowErrorMatchingSnapshot() + } + ) + test('object with a bunch of stuff', () => { + const basic = ` + const cls2 = css({ + display: 'flex', + flex: 1, + alignItems: \`\${'center'}\` + }) + ` + const { code } = babel.transform(basic, { + plugins: [[plugin]] + }) + expect(code).toMatchSnapshot() + }) + test('array of objects', () => { + const basic = ` + const cls2 = css([{ + display: 'flex', + flex: 1, + alignItems: \`\${'center'}\` + }, { + justifyContent: 'flex-start' + }]) + ` + const { code } = babel.transform(basic, { + plugins: [[plugin]] + }) + expect(code).toMatchSnapshot() }) }) describe('extract', () => { @@ -166,10 +211,9 @@ describe('babel css', () => { display: flex; flex: 1 0 auto; color: blue; - width: \${widthVar}; \`` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -178,25 +222,6 @@ describe('babel css', () => { expect(fs.writeFileSync.mock.calls[0][1]).toMatchSnapshot() }) - test('interpolation in selector', () => { - const basic = ` - const cls2 = css\` - margin: 12px 48px; - color: #ffffff; - .\${className} { - display: none; - } - \` - ` - const { code } = babel.transform(basic, { - plugins: [[plugin]], - filename: __filename, - babelrc: false - }) - expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(1) - }) - test('composes', () => { const basic = ` const cls1 = css\` @@ -209,7 +234,7 @@ describe('babel css', () => { \` ` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -229,7 +254,7 @@ describe('babel css', () => { \` ` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -264,12 +289,25 @@ describe('babel css', () => { expect(code).toMatchSnapshot() }) + test('dynamic property objects', () => { + const basic = ` + css({ + fontSize: 10, + [\`w$\{'idth'}\`]: 20 + }) + ` + const { code } = babel.transform(basic, { + plugins: [[plugin]] + }) + expect(code).toMatchSnapshot() + }) + test('prefixed array of objects', () => { const basic = ` css([{ borderRadius: '50%', - boxSizing: 'border-box', - display: 'flex', + boxSizing: ['border-box'], + [display]: 'flex', ':hover': { transform: 'scale(1.2)' } @@ -293,14 +331,14 @@ describe('babel css', () => { \` ` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(4) - expect(fs.writeFileSync.mock.calls[3][1]).toMatchSnapshot() + expect(fs.writeFileSync).toHaveBeenCalledTimes(3) + expect(fs.writeFileSync.mock.calls[2][1]).toMatchSnapshot() }) }) }) diff --git a/test/babel/font-face.test.js b/test/babel/font-face.test.js index 79cd3d88d..c69abc08f 100644 --- a/test/babel/font-face.test.js +++ b/test/babel/font-face.test.js @@ -48,7 +48,7 @@ describe('fontFace babel', () => { font-weight: bold; \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -66,7 +66,7 @@ describe('fontFace babel', () => { font-weight: bold; \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -84,7 +84,7 @@ describe('fontFace babel', () => { font-weight: bold; \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], babelrc: false, filename: __filename }) diff --git a/test/babel/fs.test.js b/test/babel/fs.test.js index 6e22fcff3..47c1c3d40 100644 --- a/test/babel/fs.test.js +++ b/test/babel/fs.test.js @@ -14,7 +14,6 @@ css\` flex: 1 0 auto; color: blue; name: class; - width: \${widthVar}; \`` let output @@ -28,7 +27,7 @@ describe('babel plugin fs', () => { test('creates and writes to the css file when it does not exist', () => { fs.existsSync.mockReturnValueOnce(false) const { code } = transform(basic, { - plugins: [emotionPlugin], + plugins: [[emotionPlugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -44,7 +43,7 @@ describe('babel plugin fs', () => { fs.existsSync.mockReturnValueOnce(true) fs.readFileSync.mockReturnValueOnce('') const { code } = transform(basic, { - plugins: [emotionPlugin], + plugins: [[emotionPlugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -59,7 +58,7 @@ describe('babel plugin fs', () => { fs.existsSync.mockReturnValueOnce(true) fs.readFileSync.mockReturnValueOnce(output) const { code } = transform(basic, { - plugins: [emotionPlugin], + plugins: [[emotionPlugin, { extractStatic: true }]], filename: __filename, babelrc: false }) diff --git a/test/babel/inject-global.test.js b/test/babel/inject-global.test.js index f9aa16769..1cec8aaab 100644 --- a/test/babel/inject-global.test.js +++ b/test/babel/inject-global.test.js @@ -15,7 +15,7 @@ describe('babel injectGlobal', () => { margin: 0; padding: 0; & > div { - display: none; + display: flex; } } html { @@ -64,10 +64,11 @@ describe('babel injectGlobal', () => { } \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) + expect(code).toMatchSnapshot() expect(fs.writeFileSync).toHaveBeenCalledTimes(1) expect(fs.writeFileSync.mock.calls[0][1]).toMatchSnapshot() @@ -87,7 +88,7 @@ describe('babel injectGlobal', () => { } \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -111,7 +112,9 @@ describe('babel injectGlobal', () => { } \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]] + plugins: [[plugin, { extractStatic: true }]], + filename: __filename, + babelrc: false }) expect(code).toMatchSnapshot() expect(fs.writeFileSync).toHaveBeenCalledTimes(2) diff --git a/test/babel/keyframes.test.js b/test/babel/keyframes.test.js index 3ed460cc5..381b9bdcb 100644 --- a/test/babel/keyframes.test.js +++ b/test/babel/keyframes.test.js @@ -52,7 +52,7 @@ describe('babel keyframes', () => { } \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], babelrc: false, filename: __filename }) @@ -72,7 +72,7 @@ describe('babel keyframes', () => { } \`;` const { code } = babel.transform(basic, { - plugins: [[plugin]], + plugins: [[plugin, { extractStatic: true }]], babelrc: false, filename: __filename }) diff --git a/test/babel/macro.test.js b/test/babel/macro.test.js index 2118ce76e..ef4b41f1c 100644 --- a/test/babel/macro.test.js +++ b/test/babel/macro.test.js @@ -93,11 +93,13 @@ describe('babel macro', () => { display: flex; \` ` - expect(() => babel.transform(basic, { - plugins: ['babel-macros'], - filename: __filename, - babelrc: false - })).toThrowError(/the emotion macro must be imported with es modules/) + expect(() => + babel.transform(basic, { + plugins: ['babel-macros'], + filename: __filename, + babelrc: false + }) + ).toThrowError(/the emotion macro must be imported with es modules/) }) }) test('injectGlobal', () => { @@ -109,6 +111,18 @@ describe('babel macro', () => { padding: 0; & > div { display: none; + + &:hover { + color: green; + + & span { + color: red; + + &:after { + content: "end of line" + } + } + } } } html { @@ -193,6 +207,18 @@ describe('babel macro', () => { }) expect(code).toMatchSnapshot() }) + test('css call with no args', () => { + const basic = ` + import { css } from '../../src/macro' + const cls1 = css() + ` + const { code } = babel.transform(basic, { + plugins: ['babel-macros'], + filename: __filename, + babelrc: false + }) + expect(code).toMatchSnapshot() + }) test('some import that does not exist', () => { const basic = ` import { thisDoesNotExist } from '../../src/macro' diff --git a/test/babel/styled.test.js b/test/babel/styled.test.js index ef68fc4fe..148865e72 100644 --- a/test/babel/styled.test.js +++ b/test/babel/styled.test.js @@ -25,58 +25,54 @@ describe('babel styled component', () => { expect(code).toMatchSnapshot() }) - test('styled component as selector', () => { - const basic = ` - const SomeComponent = styled.div\` \` - styled.h1\` - color:blue; - \${SomeComponent} { - color: green; - } + test('dynamic fns', () => { + const basic = `const Avatar = styled('img')\` + width: 96px; + height: 96px; + + border-radius: $\{props => + props.theme.borderRadius}; + + border: 1px solid $\{props => + props.theme.borderColor}; \`` const { code } = babel.transform(basic, { plugins: [plugin] }) - expect(code).toMatchSnapshot() - }) - test('basic', () => { - const basic = "const H1 = styled.h1`font-size: ${fontSize + 'px'};`" - const { code } = babel.transform(basic, { - plugins: [plugin] - }) expect(code).toMatchSnapshot() }) - test('function call', () => { - const basic = "styled(MyComponent)`font-size: ${fontSize + 'px'};`" + test('more than 10 dynamic values', () => { + const basic = `const H1 = styled('h1')\` + text-decoration: $\{'underline'}; + border-right: solid blue $\{54}px; + background: $\{'white'}; + color: $\{'black'}; + display: $\{'block'}; + border-radius: $\{'3px'}; + padding: $\{'25px'}; + width: $\{'500px'}; + z-index: $\{100}; + font-size: $\{'18px'}; + text-align: $\{'center'}; + border-left: $\{p => p.theme.blue}; + \`` const { code } = babel.transform(basic, { plugins: [plugin] }) - expect(code).toMatchSnapshot() - }) - test('name is correct with no identifier', () => { - const basic = ` - css\` - margin: 12px 48px; - color: #ffffff; - \` - ` - const { code } = babel.transform(basic, { - plugins: [[plugin]] - }) expect(code).toMatchSnapshot() }) - test('attr', () => { - const basic = `styled('input')\` - margin: attr(margin); - color: #ffffff; - height: \${props => props.height * props.scale}; - width: attr(width); - color: blue; - display: \${flex}; + test('styled component as selector', () => { + const basic = ` + const SomeComponent = styled.div\` \` + styled.h1\` + color:blue; + .\${SomeComponent} { + color: green; + } \`` const { code } = babel.transform(basic, { plugins: [plugin] @@ -84,92 +80,89 @@ describe('babel styled component', () => { expect(code).toMatchSnapshot() }) - test('lots of attrs with interpolated values', () => { - const basic = `styled('input')\` - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - margin: attr(\${marginProp}); - display: attr(\${displayProp}); - \`` + test('basic', () => { + const basic = "const H1 = styled.h1`font-size: ${fontSize + 'px'};`" const { code } = babel.transform(basic, { plugins: [plugin] }) expect(code).toMatchSnapshot() }) - test('attr with value type', () => { - const basic = `styled('input')\` - margin: attr(margin px); - \`` + test('nested', () => { + const basic = + 'const H1 = styled.h1`' + + "font-size: ${fontSize + 'px'};" + + '& div { color: blue;' + + '& span { color: red } }' + + '`' const { code } = babel.transform(basic, { plugins: [plugin] }) expect(code).toMatchSnapshot() }) - test('attr with default value', () => { - const basic = `styled('input')\` - margin: attr(margin, 16); - \`` + test('interpolation in different places', () => { + const basic = ` + const H1 = styled.h1\` + font-size: \${fontSize + 'px'}; + height: 20px; + transform: translateX(\${(props) => props.translateX}); + height1: \${something}wow; + width: w\${something}ow; + transform: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); + transform1: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}); + transform2: translateX(\${(props) => props.translateX}) translateY(\${(props) => props.translateX}; + \`` const { code } = babel.transform(basic, { plugins: [plugin] }) expect(code).toMatchSnapshot() }) - test('attr with value type and default value', () => { - const basic = `styled('input')\` - margin: attr(margin px, 16); - \`` + test('media query', () => { + const basic = + 'const H1 = styled.h1`@media print {' + + ' font-size: 10pt' + + '}' + + '@media screen {' + + ' .child-selector { font-size: 13px }' + + '}' + + '@media screen, print {' + + ' &:hover + & { line-height: 1.2 }' + + '}' + + '@media only screen ' + + ' and (min-device-width: 320px) ' + + ' and (max-device-width: 480px)' + + ' and (-webkit-min-device-pixel-ratio: 2) {' + + ' .child-selector { line-height: 1.4 }' + + '}`' const { code } = babel.transform(basic, { plugins: [plugin] }) expect(code).toMatchSnapshot() }) - test('match works on multiple', () => { - const basic = `styled('input')\` - margin: attr(margin px, 16); - color: blue; - padding: attr(padding em, 16); - \`` + test('function call', () => { + const basic = "styled(MyComponent)`font-size: ${fontSize + 'px'};`" const { code } = babel.transform(basic, { plugins: [plugin] }) expect(code).toMatchSnapshot() }) - test('attr kitchen sink', () => { - const basic = `styled('input')\` - margin: attr(margin px, 16); - padding: attr(padding em, 16); - font-size: attr(fontSize ch, 8); - width: attr(width %, 95); - height: attr(height vw, 90); - display: attr(display, flex); - position: attr(alignItems \${alignItemsUnit}, fallback); - position: attr(thing px, \${defaultValue}); - position: attr(\${thing} px); - \`` + test('name is correct with no identifier', () => { + const basic = ` + css\` + margin: 12px 48px; + color: #ffffff; + \` + ` const { code } = babel.transform(basic, { - plugins: [plugin] + plugins: [[plugin]] }) expect(code).toMatchSnapshot() }) + test('objects fn call', () => { const basic = ` const H1 = styled('h1')({ @@ -180,6 +173,7 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) + test('objects based on props', () => { const basic = ` const H1 = styled('h1')({ padding: 10 },props => ({ @@ -190,6 +184,7 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) + test('object composes with classes', () => { const basic = ` const H1 = styled('h1')('some-class',props => ({ @@ -200,6 +195,7 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) + test('objects prefixed', () => { const basic = ` const H1 = styled('h1')({ @@ -218,6 +214,7 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) + test('styled. objects', () => { const basic = ` const H1 = styled.h1({ padding: 10 },props => ({ @@ -228,7 +225,8 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) - test('styled. objects prefixed', () => { + + test('styled objects prefixed', () => { const basic = ` const H1 = styled.h1({ borderRadius: '50%', @@ -246,23 +244,29 @@ describe('babel styled component', () => { }) expect(code).toMatchSnapshot() }) - }) - describe('extract', () => { - test('no use', () => { - const basic = 'styled.h1``' + + test('composes based on props', () => { + const basic = `const cls1 = css\` width: 20px; \` + const H1 = styled.h1\` + composes: $\{props => { + return props.a ? cssA : cssB + }}; + font-size: \${fontSize + 'px'}; + height: 20px; + transform: translateX(\${(props) => props.translateX}); + \`` const { code } = babel.transform(basic, { - plugins: [plugin], - filename: __filename, - babelrc: false + plugins: [plugin] }) expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(0) }) + }) - test('no dynamic', () => { - const basic = 'styled.h1`color:blue;`' + describe('extract', () => { + test('no use', () => { + const basic = 'styled.h1``' const { code } = babel.transform(basic, { - plugins: [plugin], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) @@ -273,48 +277,16 @@ describe('babel styled component', () => { test('basic', () => { const basic = - "const H1 = styled.h1`font-size: ${fontSize + 'px'}; height: 20px`" + "const H1 = styled.h1`display: flex; justify-content: center; width: var(--css-hash-0); &:hover { background-color: green; } @media (max-width: 500px) { height: var(--css-hash-1); position: fixed; } @media print { display: none; } &::before { color: blue; width: 20px; height: 20px; content: 'pseudo' }`" const { code } = babel.transform(basic, { - plugins: [plugin], + plugins: [[plugin, { extractStatic: true }]], filename: __filename, babelrc: false }) + expect(code).toMatchSnapshot() expect(fs.writeFileSync).toHaveBeenCalledTimes(2) expect(fs.writeFileSync.mock.calls[1][1]).toMatchSnapshot() }) - - test('based on props', () => { - const basic = `const H1 = styled.h1\` - font-size: \${fontSize + 'px'}; - height: 20px; - transform: translateX(\${(props) => props.translateX}); - \`` - const { code } = babel.transform(basic, { - plugins: [plugin], - filename: __filename, - babelrc: false - }) - expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(3) - expect(fs.writeFileSync.mock.calls[2][1]).toMatchSnapshot() - }) - test('composes', () => { - const basic = `const cls1 = css\` width: 20px; \` - const H1 = styled.h1\` - composes: \${cls1}; - font-size: \${fontSize + 'px'}; - height: 20px; - transform: translateX(\${(props) => props.translateX}); - \`` - const { code } = babel.transform(basic, { - plugins: [plugin], - filename: __filename, - babelrc: false - }) - expect(code).toMatchSnapshot() - expect(fs.writeFileSync).toHaveBeenCalledTimes(4) - expect(fs.writeFileSync.mock.calls[3][1]).toMatchSnapshot() - }) }) }) diff --git a/test/css-prop.test.js b/test/css-prop.test.js index f53071e9a..fb75e1d24 100644 --- a/test/css-prop.test.js +++ b/test/css-prop.test.js @@ -1,10 +1,10 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../jest-utils' -import { css } from '../src/index' +import { matcher, serializer } from 'jest-glamor-react' +import { css, sheet } from '../src/index' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('css prop react', () => { @@ -14,17 +14,17 @@ describe('css prop react', () => { .create(

hello world

) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('string expression', () => { const tree = renderer .create( -

hello world

+

hello world

) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('kitchen sink', () => { @@ -51,7 +51,7 @@ describe('css prop react', () => { const tree = renderer .create(
{ > BOOM

-

+

Hello

World @@ -89,6 +89,6 @@ describe('css prop react', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) }) diff --git a/test/css.test.js b/test/css.test.js index 13aeacf9e..fbbd69da4 100644 --- a/test/css.test.js +++ b/test/css.test.js @@ -1,12 +1,19 @@ /* eslint-env jest */ -import { sheet, css } from '../src/index' +import React from 'react' +import renderer from 'react-test-renderer' +import { matcher, serializer } from 'jest-glamor-react' +import { sheet, css, flush } from '../src/index' + +expect.addSnapshotSerializer(serializer(sheet)) +expect.extend(matcher) describe('css', () => { test('handles more than 10 dynamic properties', () => { const cls1 = css` + text-decoration: ${'underline'}; + border-right: solid blue 54px; background: ${'white'}; color: ${'black'}; - text-decoration: ${'underline'}; display: ${'block'}; border-radius: ${'3px'}; padding: ${'25px'}; @@ -14,13 +21,10 @@ describe('css', () => { z-index: ${100}; font-size: ${'18px'}; text-align: ${'center'}; - border: ${'solid 1px red'}; ` - expect(cls1).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(

).toJSON() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with undefined values', () => { @@ -28,10 +32,8 @@ describe('css', () => { composes: ${undefined}; justifyContent: center; ` - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(
).toJSON() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes', () => { @@ -42,16 +44,29 @@ describe('css', () => { composes: ${cls1}; justifyContent: center; ` - expect(cls1).toMatchSnapshot() - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(
).toJSON() + expect(tree).toMatchSnapshotWithGlamor() }) test('handles objects', () => { - const cls1 = css({ display: 'flex' }) - expect(cls1).toMatchSnapshot() + const cls1 = css({ + display: 'flex', + color: `${'blue'}`, + fontSize: `${'20px'}`, + height: `${50}`, + width: 20 + }) + const tree = renderer.create(
).toJSON() + expect(tree).toMatchSnapshotWithGlamor() + }) + + test('computed key is only dynamic', () => { + const cls1 = css({ + fontSize: 10, + [`w${'idth'}`]: 20 + }) + const tree = renderer.create(
).toJSON() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with objects', () => { @@ -59,7 +74,7 @@ describe('css', () => { display: ['flex', 'block'], width: 30, height: 'calc(40vw - 50px)', - ':hover': {color: 'blue'}, + ':hover': { color: 'blue' }, ':after': { content: '" "', color: 'red' @@ -73,10 +88,23 @@ describe('css', () => { justifyContent: center; ` - expect(cls1).toMatchSnapshot() - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(
).toJSON() + expect(tree).toMatchSnapshotWithGlamor() + }) + test('null rule', () => { + const cls1 = css() + + const tree = renderer.create(
).toJSON() + expect(tree).toMatchSnapshotWithGlamor() + }) + test('flushes correctly', () => { + const cls1 = css` + display: flex; + ` + const tree = renderer.create(
).toJSON() + expect(tree).toMatchSnapshotWithGlamor() + flush() + const tree2 = renderer.create(
).toJSON() + expect(tree2).toMatchSnapshotWithGlamor() }) }) diff --git a/test/extract/.babelrc b/test/extract/.babelrc index 2efecbb12..c31fa42db 100644 --- a/test/extract/.babelrc +++ b/test/extract/.babelrc @@ -4,5 +4,5 @@ "env", "react" ], - "plugins": ["../babel-plugin-emotion-test"] + "plugins": [["../babel-plugin-emotion-test", {extractStatic: true}]] } diff --git a/test/extract/__snapshots__/extract.test.js.snap b/test/extract/__snapshots__/extract.test.js.snap index df273dc7f..1d5baa017 100644 --- a/test/extract/__snapshots__/extract.test.js.snap +++ b/test/extract/__snapshots__/extract.test.js.snap @@ -1,157 +1,55 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`styled another attr 1`] = ` -.css-PlaygroundWrapper-1shjoz3-tra3vx { - color: #343a40; -} - -
-`; - -exports[`styled attr 1`] = ` -.css-H1-nt9mdf-vddukf { - font-size: 48; - margin: 4rem; -} - -

-`; - -exports[`styled basic render 1`] = ` -.vars-ch9j2 { - --css-H1-1t8i2zo-0: 20px; -} - +exports[`styled basic render nested 1`] = `

hello world

`; -exports[`styled call expression 1`] = ` -.vars-57j4qc { - --css-H1-rs9k70-0: 20px; -} - +exports[`styled name 1`] = `

hello world

`; -exports[`styled composes 1`] = ` -.vars-1m8nl4c { - --css-H1-1pnnqpk-0: 20px; -} - +exports[`styled no dynamic 1`] = `

hello world

`; -exports[`styled composition 1`] = ` -.vars-57j4qc { - --css-H1-rs9k70-0: 20px; +exports[`styled writes the correct css 1`] = ` +".styled-H1-ijh7uz { + font-size: 12px } - -.vars-1u8m2k4 { - --css-H2-vxb7tq-0: 13.333333333333334px; +.styled-H1-1l4sfy8 { + font-size: 20px } - -

- hello world -

-`; - -exports[`styled function in expression 1`] = ` -.vars-57j4qc { - --css-H1-rs9k70-0: 20px; +.styled-H1-1l4sfy8 span { + color: blue } - -.vars-6khrnp { - --css-H2-vxb7tq-0: NaN; +.styled-H1-1l4sfy8 span:hover { + color: green } - -

- hello world -

-`; - -exports[`styled higher order component 1`] = ` -.vars-12p149b { - --css-Content-rs9k70-0: 20px; +.styled-H1-1l4sfy8 span:hover:after { + content: \\"after\\" } - -
-`; - -exports[`styled name 1`] = ` -.vars-1vv3got { - --css-FancyH1-1o9p8a2-0: 20px; +.styled-FancyH1-131pfth { + name: FancyH1; + font-size: 38px } - -

- hello world -

-`; - -exports[`styled no dynamic 1`] = ` -

- hello world -

-`; - -exports[`styled writes the correct css 1`] = ` -".css-H1-ijh7uz { font-size: 12px; } -.css-H1-1t8i2zo { font-size: var(--css-H1-1t8i2zo-0); } -.css-FancyH1-1o9p8a2 { - font-size: var(--css-FancyH1-1o9p8a2-0); } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H2-vxb7tq { font-size: var(--css-H2-vxb7tq-0) } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H2-vxb7tq { font-size: var(--css-H2-vxb7tq-0) } -.css-cls1-cy897j { color: blue; } -.css-cls2-y5rq6p { - height: 64px; } -.css-H1-1pnnqpk { - font-size: var(--css-H1-1pnnqpk-0); } -.css-H2-idm3bz { font-size: 32px; } -.css-Content-rs9k70 { font-size: var(--css-Content-rs9k70-0); } -.css-squirtle-blue-bg-135ox65 { - background-color: #7FC8D6; } -.css-onyx-13ds1s2 { - background-color: '#343a40'; - -webkit-flex-direction: column; - -ms-flex-direction: column; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - flex-direction: column; } html { - background: pink; - } -.css-14yvnlz { font-family: sans-serif; - color: yellow; - background-color: purple; }" + background: pink +} +.css-14yvnlz { + font-family: sans-serif; + color: yellow; + background-color: purple +}" `; diff --git a/test/extract/extract.test.emotion.css b/test/extract/extract.test.emotion.css index 9e26fe85b..00d1ee444 100644 --- a/test/extract/extract.test.emotion.css +++ b/test/extract/extract.test.emotion.css @@ -1,31 +1,27 @@ -.css-H1-ijh7uz { font-size: 12px; } -.css-H1-1t8i2zo { font-size: var(--css-H1-1t8i2zo-0); } -.css-FancyH1-1o9p8a2 { - font-size: var(--css-FancyH1-1o9p8a2-0); } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H2-vxb7tq { font-size: var(--css-H2-vxb7tq-0) } -.css-H1-rs9k70 { font-size: var(--css-H1-rs9k70-0); } -.css-H2-vxb7tq { font-size: var(--css-H2-vxb7tq-0) } -.css-cls1-cy897j { color: blue; } -.css-cls2-y5rq6p { - height: 64px; } -.css-H1-1pnnqpk { - font-size: var(--css-H1-1pnnqpk-0); } -.css-H2-idm3bz { font-size: 32px; } -.css-Content-rs9k70 { font-size: var(--css-Content-rs9k70-0); } -.css-squirtle-blue-bg-135ox65 { - background-color: #7FC8D6; } -.css-onyx-13ds1s2 { - background-color: '#343a40'; - -webkit-flex-direction: column; - -ms-flex-direction: column; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - flex-direction: column; } +.styled-H1-ijh7uz { + font-size: 12px +} +.styled-H1-1l4sfy8 { + font-size: 20px +} +.styled-H1-1l4sfy8 span { + color: blue +} +.styled-H1-1l4sfy8 span:hover { + color: green +} +.styled-H1-1l4sfy8 span:hover:after { + content: "after" +} +.styled-FancyH1-131pfth { + name: FancyH1; + font-size: 38px +} html { - background: pink; - } -.css-14yvnlz { font-family: sans-serif; - color: yellow; - background-color: purple; } \ No newline at end of file + background: pink +} +.css-14yvnlz { + font-family: sans-serif; + color: yellow; + background-color: purple +} \ No newline at end of file diff --git a/test/extract/extract.test.js b/test/extract/extract.test.js index bddc1c86c..e4f112cfa 100644 --- a/test/extract/extract.test.js +++ b/test/extract/extract.test.js @@ -3,11 +3,11 @@ import React from 'react' import renderer from 'react-test-renderer' import { basename } from 'path' -import { matcher, serializer } from '../../jest-utils' -import { injectGlobal, css } from '../../src/index' +import { matcher, serializer } from 'jest-glamor-react' +import { injectGlobal, css, sheet } from '../../src' import styled from '../../src/react' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('styled', () => { @@ -16,163 +16,41 @@ describe('styled', () => { const tree = renderer.create(

hello world

).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) - test('basic render', () => { - const fontSize = '20px' - const H1 = styled.h1`font-size: ${fontSize};` - + test('basic render nested', () => { + const H1 = styled.h1` + font-size: 20px; + & span { + color: blue; + + &:hover { + color: green; + + &:after { + content: "after"; + } + } + } + ` const tree = renderer.create(

hello world

).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('name', () => { const fontSize = '20px' const H1 = styled.h1` name: FancyH1; - font-size: ${fontSize}; + font-size: 38px; ` const tree = renderer.create(

hello world

).toJSON() - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('attr', () => { - const H1 = styled.h1` - font-size: attr(fontSize); - margin: attr(margin rem, 4); - ` - - const Title = ({ title }) => { - return ( -

- {title} -

- ) - } - - const tree = renderer.create().toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('another attr', () => { - const PlaygroundWrapper = styled('div')` - color: attr(color, #343a40); - ` - expect(renderer.create(<PlaygroundWrapper />).toJSON()).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) - test('call expression', () => { - const fontSize = '20px' - const H1 = styled('h1')` - font-size: ${fontSize}; - ` - - const tree = renderer - .create(<H1 className={'legacy__class'}>hello world</H1>) - .toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('composition', () => { - const fontSize = 20 - const H1 = styled('h1')` - font-size: ${fontSize + 'px'}; - ` - - const H2 = styled(H1)`font-size: ${fontSize * 2 / 3 + 'px'}` - - const tree = renderer - .create(<H2 className={'legacy__class'}>hello world</H2>) - .toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('function in expression', () => { - const fontSize = '20px' - const H1 = styled('h1')` - font-size: ${fontSize}; - ` - - const H2 = styled(H1)`font-size: ${({ scale }) => fontSize * scale}` - - const tree = renderer - .create( - <H2 scale={2} className={'legacy__class'}> - hello world - </H2> - ) - .toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('composes', () => { - const fontSize = '20px' - - const cls1 = css` - color: blue; - ` - - const cls2 = css` - composes: ${cls1}; - height: 64px; - ` - - const H1 = styled('h1')` - composes: ${cls2}; - font-size: ${fontSize}; - ` - - const H2 = styled(H1)`font-size:32px;` - - const tree = renderer - .create( - <H2 scale={2} className={'legacy__class'}> - hello world - </H2> - ) - .toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('higher order component', () => { - const fontSize = '20px' - const Content = styled('div')` - font-size: ${fontSize}; - ` - - const squirtleBlueBackground = css` - name: squirtle-blue-bg; - background-color: #7FC8D6; - ` - - const flexColumn = Component => { - const NewComponent = styled(Component)` - composes: ${squirtleBlueBackground}; - name: onyx; - background-color: '#343a40'; - flex-direction: column; - ` - - return NewComponent - } - - const ColumnContent = flexColumn(Content) - - // expect(ColumnContent.displayName).toMatchSnapshotWithEmotion() - - const tree = renderer.create(<ColumnContent />).toJSON() - - expect(tree).toMatchSnapshotWithEmotion() - }) test('injectGlobal', () => { injectGlobal` html { diff --git a/test/keyframes.test.js b/test/keyframes.test.js index 569d3b08d..ccaaf5f5d 100644 --- a/test/keyframes.test.js +++ b/test/keyframes.test.js @@ -1,11 +1,11 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../jest-utils' +import { matcher, serializer } from 'jest-glamor-react' import { keyframes, sheet } from '../src/index' import styled from '../src/react' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('keyframes', () => { @@ -33,17 +33,18 @@ describe('keyframes', () => { ` const H1 = styled.h1` - font-size: ${fontSize}px; animation: ${bounce} 2s linear infinite; ` const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('keyframes with interpolation', () => { const endingRotation = '360deg' - keyframes` + + const H1 = styled.h1` + animation: ${keyframes` from { transform: rotate(0deg); } @@ -51,7 +52,13 @@ describe('keyframes', () => { to { transform: rotate(${endingRotation}); } + `} 2s linear infinite; ` + + const tree = renderer.create(<H1>hello world</H1>).toJSON() + + expect(tree).toMatchSnapshotWithGlamor() + expect( sheet.tags.map(tag => tag.textContent || '').join('') ).toMatchSnapshot() diff --git a/test/macro/__snapshots__/css.test.js.snap b/test/macro/__snapshots__/css.test.js.snap index c4a23a783..8b4d7e6f0 100644 --- a/test/macro/__snapshots__/css.test.js.snap +++ b/test/macro/__snapshots__/css.test.js.snap @@ -1,83 +1,100 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`css composes 1`] = `"css-cls1-vyoujf-va5xsk css-cls1-vyoujf"`; +exports[`css macro composes 1`] = ` +.glamor-0 { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} -exports[`css composes 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-cls1-vyoujf-va5xsk css-cls1-vyoujf"`; - -exports[`css composes 3`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }" +<div + className="glamor-0" +/> `; -exports[`css composes with objects 1`] = `"css-q8izmm"`; +exports[`css macro composes with objects 1`] = ` +.glamor-0 { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + display: block; + width: 30px; + height: calc(40vw - 50px); + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} + +.glamor-0:hover { + color: blue; +} -exports[`css composes with objects 2`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-q8izmm"`; +.glamor-0:after { + content: " "; + color: red; +} -exports[`css composes with objects 3`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }.css-cls1-vyoujf-va5xsk { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; }.css-1fe3owl{display:flex}.css-q8izmm{display:flex;display:block;width:30px;height:calc(40vw - 50px)}.css-q8izmm:hover{color:blue}.css-q8izmm:after{content:\\" \\";color:red}@media(min-width: 420px){.css-q8izmm{color:green}}" +@media (min-width: 420px) { + .glamor-0 { + color: green; + } +} + +<div + className="glamor-0" +/> `; -exports[`css composes with undefined values 1`] = `"css-cls2-1qb2ovg-1em6aur css-cls2-1qb2ovg css-15famh2"`; +exports[`css macro composes with undefined values 1`] = ` +.glamor-0 { + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} -exports[`css composes with undefined values 2`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }.css-15famh2{}.css-cls2-1qb2ovg-1em6aur { - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; }" +<div + className="glamor-0" +/> `; -exports[`css handles more than 10 dynamic properties 1`] = `"css-cls1-1gi569l-b877w5 css-cls1-1gi569l"`; +exports[`css macro handles more than 10 dynamic properties 1`] = ` +.glamor-0 { + background: white; + color: black; + -webkit-text-decoration: underline; + text-decoration: underline; + display: block; + border-radius: 3px; + padding: 25px; + width: 500px; + z-index: 100; + font-size: 18px; + text-align: center; + border: solid 1px red; +} -exports[`css handles more than 10 dynamic properties 2`] = ` -".css-cls1-1gi569l-b877w5 { background: white; - color: black; - text-decoration: underline; - display: block; - border-radius: 3px; - padding: 25px; - width: 500px; - z-index: 100; - font-size: 18px; - text-align: center; - border: solid 1px red; }" +<div + className="glamor-0" +/> `; -exports[`css handles objects 1`] = `"css-1fe3owl"`; +exports[`css macro handles objects 1`] = ` +.glamor-0 { + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} + +<div + className="glamor-0" +/> +`; + +exports[`css macro null rule 1`] = ` +<div + className="css-nil" +/> +`; diff --git a/test/macro/__snapshots__/inject-global.test.js.snap b/test/macro/__snapshots__/inject-global.test.js.snap index 3d8a6aaa3..afc879b40 100644 --- a/test/macro/__snapshots__/inject-global.test.js.snap +++ b/test/macro/__snapshots__/inject-global.test.js.snap @@ -1,13 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`injectGlobal 1`] = ` -"html { - background: pink; - }html.active { - background: red; - }body { - color: yellow; - margin: 0; - padding: 0; - }" -`; +exports[`injectGlobal 1`] = `"html {background:pink;}html.active {background:red;}body {color:yellow;margin:0px;padding:0px;}"`; diff --git a/test/macro/__snapshots__/keyframes.test.js.snap b/test/macro/__snapshots__/keyframes.test.js.snap index 832a5245b..9a61faa49 100644 --- a/test/macro/__snapshots__/keyframes.test.js.snap +++ b/test/macro/__snapshots__/keyframes.test.js.snap @@ -1,58 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`keyframes keyframes with interpolation 1`] = ` -"@keyframes animation-bounce-10a3qiv-cmo0tx { from, 20%, 53%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - -webkit-transform: translate3d(0,0,0); - -ms-transform: translate3d(0,0,0); - transform: translate3d(0,0,0); - } - - 40%, 43% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -30px, 0); - -ms-transform: translate3d(0, -30px, 0); - transform: translate3d(0, -30px, 0); - } - - 70% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -15px, 0); - -ms-transform: translate3d(0, -15px, 0); - transform: translate3d(0, -15px, 0); - } - - 90% { - -webkit-transform: translate3d(0,-4px,0); - -ms-transform: translate3d(0,-4px,0); - transform: translate3d(0,-4px,0); - } }.css-H1-o0kcx2-fy8ana { font-size: 20px; - -webkit-animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; - animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; }@keyframes animation-1fpnjxj-1mmv2re { from { - -webkit-transform: rotate(0deg); - -ms-transform: rotate(0deg); - transform: rotate(0deg); - } - - to { - -webkit-transform: rotate(360deg); - -ms-transform: rotate(360deg); - transform: rotate(360deg); - } }" +exports[`keyframes - macro keyframes with interpolation 1`] = ` +.glamor-0 { + -webkit-animation: animation_1cnjdtk 2s linear infinite; + animation: animation_1cnjdtk 2s linear infinite; +} + +<h1 + className="glamor-0" +> + hello world +</h1> `; -exports[`keyframes renders 1`] = ` -.css-H1-o0kcx2-fy8ana { - font-size: 20px; - -webkit-animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; - animation: animation-bounce-10a3qiv-cmo0tx 2s linear infinite; +exports[`keyframes - macro keyframes with interpolation 2`] = `"@-webkit-keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}@keyframes animation_7jdctn{from, 20%, 53%, 80%, to {-webkit-animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);animation-timing-function:cubic-bezier(0.215, 0.610, 0.355, 1.000);-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}40%, 43% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -30px, 0);transform:translate3d(0, -30px, 0);}70% {-webkit-animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);animation-timing-function:cubic-bezier(0.755, 0.050, 0.855, 0.060);-webkit-transform:translate3d(0, -15px, 0);transform:translate3d(0, -15px, 0);}90% {-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0);}}.css-1m1x9re{-webkit-animation:animation_7jdctn 2s linear infinite;animation:animation_7jdctn 2s linear infinite;}@-webkit-keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes animation_1cnjdtk{from {-webkit-transform:rotate(0deg);transform:rotate(0deg);}to {-webkit-transform:rotate(360deg);transform:rotate(360deg);}}.css-1nja0ph{-webkit-animation:animation_1cnjdtk 2s linear infinite;animation:animation_1cnjdtk 2s linear infinite;}"`; + +exports[`keyframes - macro renders 1`] = ` +.glamor-0 { + -webkit-animation: animation_7jdctn 2s linear infinite; + animation: animation_7jdctn 2s linear infinite; } <h1 - className="css-H1-o0kcx2-fy8ana css-H1-o0kcx2" + className="glamor-0" > hello world </h1> diff --git a/test/macro/__snapshots__/react.test.js.snap b/test/macro/__snapshots__/react.test.js.snap index 2a5f93f18..00201709c 100644 --- a/test/macro/__snapshots__/react.test.js.snap +++ b/test/macro/__snapshots__/react.test.js.snap @@ -1,77 +1,58 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`styled attr 1`] = ` -.css-H1-l44jbj-1u4tp6a { - font-size: 48; - margin: 4rem; - position: absolute; -} - -<h1 - className="css-H1-l44jbj-1u4tp6a css-H1-l44jbj" - fontSize={48} -/> -`; - exports[`styled basic render 1`] = ` -.css-H1-8xpzga-1e133rd { +.glamor-0 { font-size: 20px; } <h1 - className="css-H1-8xpzga-1e133rd css-H1-8xpzga" + className="glamor-0" > hello world </h1> `; exports[`styled basic render with object as style 1`] = ` -.css-kp2uw9 { +.glamor-0 { font-size: 20px; } <h1 - className="css-kp2uw9" + className="glamor-0" > hello world </h1> `; exports[`styled call expression 1`] = ` -.css-H1-13wdnau-xy96vj { +.glamor-0 { font-size: 20px; } <h1 - className="css-H1-13wdnau-xy96vj css-H1-13wdnau legacy__class" + className="glamor-0" > hello world </h1> `; exports[`styled component as selector 1`] = ` -.css-H1-rs9k70-qz486c { +.glamor-0 { font-size: 20px; } -.css-Thing-1kdnbhf-d995d2 { +.glamor-1 { display: -webkit-box; - display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; } -.css-Thing-1kdnbhf-d995d2 .css-H1-rs9k70 { - color: green; -} - <div - className="css-Thing-1kdnbhf-d995d2 css-Thing-1kdnbhf" + className="glamor-1" > hello <h1 - className="css-H1-rs9k70-qz486c css-H1-rs9k70" + className="glamor-0" > This will be green </h1> @@ -80,24 +61,14 @@ exports[`styled component as selector 1`] = ` `; exports[`styled composes 1`] = ` -.css-cssA-cy897j-1p4ppuj { +.glamor-0 { color: blue; -} - -.css-cssB-ob3sm8-fwh0v { height: 64px; -} - -.css-H2-idm3bz-361gww { font-size: 32px; } -.css-H1-zejj71-ovrrqc { - font-size: 20px; -} - <h1 - className="css-H1-zejj71-ovrrqc css-H1-zejj71 css-cssB-ob3sm8-fwh0v css-cssB-ob3sm8 css-cssA-cy897j-1p4ppuj css-cssA-cy897j css-H2-idm3bz-361gww css-H2-idm3bz legacy__class" + className="glamor-0" scale={2} > hello world @@ -105,46 +76,36 @@ exports[`styled composes 1`] = ` `; exports[`styled composes based on props 1`] = ` -.css-cssA-cy897j-1p4ppuj { +.glamor-0 { color: blue; } <h1 a={true} - className="css-H1-16dw226 css-cssA-cy897j-1p4ppuj css-cssA-cy897j" + className="glamor-0" > hello world </h1> `; exports[`styled composes based on props 2`] = ` -.css-cssB-7o6h5j-dcpp5a { +.glamor-0 { color: green; } <h1 - className="css-H1-16dw226 css-cssB-7o6h5j-dcpp5a css-cssB-7o6h5j" + className="glamor-0" > hello world </h1> `; exports[`styled composes with objects 1`] = ` -.css-cssB-ob3sm8-fwh0v { - height: 64px; -} - -.css-H2-idm3bz-361gww { - font-size: 32px; -} - -.css-1olphq9 { +.glamor-0 { color: #333; font-size: 1.333em; -} - -.css-H1-zejj71-1ym7xv { - font-size: 3.157334518321em; + height: 64px; + font-size: 32px; } @media only screen and (-webkit-min-device-pixel-ratio: 1.5), @@ -152,13 +113,13 @@ exports[`styled composes with objects 1`] = ` only screen and (-o-min-device-pixel-ratio: 1.5/1), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) { - .css-1olphq9 { + .glamor-0 { font-size: 1.4323121856191332em; } } <h1 - className="css-H1-zejj71-1ym7xv css-H1-zejj71 css-cssB-ob3sm8-fwh0v css-cssB-ob3sm8 css-1olphq9 css-H2-idm3bz-361gww css-H2-idm3bz legacy__class" + className="glamor-0" scale={2} > hello world @@ -166,32 +127,24 @@ exports[`styled composes with objects 1`] = ` `; exports[`styled composition 1`] = ` -.css-H2-vxb7tq-1gn1gp5 { - font-size: 13.333333333333334; -} - -.css-H1-rs9k70-qz486c { - font-size: 20px; +.glamor-0 { + font-size: 13.333333333333334px; } <h1 - className="css-H1-rs9k70-qz486c css-H1-rs9k70 css-H2-vxb7tq-1gn1gp5 css-H2-vxb7tq legacy__class" + className="glamor-0" > hello world </h1> `; exports[`styled function in expression 1`] = ` -.css-H1-rs9k70-qz486c { - font-size: 20px; -} - -.css-H2-vxb7tq-1pd0qgw { +.glamor-0 { font-size: 40px; } <h1 - className="css-H1-rs9k70-qz486c css-H1-rs9k70 css-H2-vxb7tq-1pd0qgw css-H2-vxb7tq legacy__class" + className="glamor-0" scale={2} > hello world @@ -199,75 +152,65 @@ exports[`styled function in expression 1`] = ` `; exports[`styled higher order component 1`] = ` -.css-squirtle-blue-bg-135ox65-8g3djl { - background-color: #7FC8D6; -} - -.css-onyx-165zlfj-sv1xya { +.glamor-0 { + font-size: 20px; background-color: '#343a40'; - -webkit-flex-direction: column; - -ms-flex-direction: column; -webkit-box-orient: vertical; -webkit-box-direction: normal; + -ms-flex-direction: column; flex-direction: column; } -.css-Content-13wdnau-1z6hk6 { - font-size: 20px; -} - <div - className="css-Content-13wdnau-1z6hk6 css-Content-13wdnau css-onyx-165zlfj-sv1xya css-onyx-165zlfj css-squirtle-blue-bg-135ox65-8g3djl css-squirtle-blue-bg-135ox65" + className="glamor-0" /> `; exports[`styled innerRef 1`] = ` -.css-H1-ijh7uz-10q50rl { +.glamor-0 { font-size: 12px; } <h1 - className="css-H1-ijh7uz-10q50rl css-H1-ijh7uz" + className="glamor-0" > hello world </h1> `; exports[`styled name 1`] = ` -.css-FancyH1-1azfbv1-1fzd8kz { +.glamor-0 { + name: FancyH1; font-size: 20px; } <h1 - className="css-FancyH1-1azfbv1-1fzd8kz css-FancyH1-1azfbv1" + className="glamor-0" > hello world </h1> `; exports[`styled no dynamic 1`] = ` -.css-H1-ijh7uz-10q50rl { +.glamor-0 { font-size: 12px; } <h1 - className="css-H1-ijh7uz-10q50rl css-H1-ijh7uz" + className="glamor-0" > hello world </h1> `; exports[`styled objects 1`] = ` -.css-1viuxsa { +.glamor-0 { padding: 10px; -} - -.css-1fe3owl { display: flex; } <h1 - className="some-class css-1viuxsa css-1fe3owl" + className="some-class glamor-0" display="flex" > hello world @@ -275,29 +218,15 @@ exports[`styled objects 1`] = ` `; exports[`styled themes 1`] = ` -.css-cssA-cy897j-1p4ppuj { - color: blue; -} - -.css-cssB-ob3sm8-fwh0v { +.glamor-0 { + background-color: #ffd43b; + color: #8c81d8; height: 64px; -} - -.css-H2-idm3bz-361gww { font-size: 32px; } -.css-H1-iwhfi5-s8bsgs { - font-size: 20px; - color: #8c81d8; -} - -.css-Heading-1ybg51t-91l5qw { - background-color: #ffd43b; -} - <span - className="css-Heading-1ybg51t-91l5qw css-Heading-1ybg51t css-H1-iwhfi5-s8bsgs css-H1-iwhfi5 css-cssB-ob3sm8-fwh0v css-cssB-ob3sm8 css-cssA-cy897j-1p4ppuj css-cssA-cy897j css-H2-idm3bz-361gww css-H2-idm3bz" + className="glamor-0" scale={2} > hello world diff --git a/test/macro/__snapshots__/vue.test.js.snap b/test/macro/__snapshots__/vue.test.js.snap deleted file mode 100644 index d19994206..000000000 --- a/test/macro/__snapshots__/vue.test.js.snap +++ /dev/null @@ -1,63 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`vue styled based on props 1`] = ` -" - <div id=\\"app\\"><h1 weight=\\"bold\\" class=\\"css-BasedOnProps-csm9t8-1qo3zsu css-BasedOnProps-csm9t8\\"></h1></div> - " -`; - -exports[`vue styled based on props composes 1`] = ` -" - <div id=\\"app\\"><div thing=\\"true\\" class=\\"css-basedOnPropsComposes-17rcs0o some-special-class\\"></div></div> - " -`; - -exports[`vue styled basic 1`] = ` -" - <div id=\\"app\\"><div class=\\"css-StyledComponent-1brvt4i-ulwf5d css-StyledComponent-1brvt4i\\"></div></div> - " -`; - -exports[`vue styled child selector 1`] = ` -" - <div id=\\"app\\"><main class=\\"css-ChildSelector-13pvedz-1e7xvxz css-ChildSelector-13pvedz\\"><div class=\\"css-StyledComponent-1brvt4i-ulwf5d css-StyledComponent-1brvt4i\\">wow</div></main></div> - " -`; - -exports[`vue styled creates the correct styles 1`] = ` -".css-StyledComponent-1brvt4i-ulwf5d { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; - -webkit-justify-content: center; - -ms-flex-pack: center; - -webkit-box-pack: center; - justify-content: center; - -webkit-align-items: center; - -ms-flex-align: center; - -webkit-box-align: center; - align-items: center; - position: inherit; }.css-BasedOnProps-csm9t8-1qo3zsu { font-weight: bold; }.css-NonHtmlComponent-15287sz-1wq2ikh { background-color: purple; - color: green; }.css-ChildSelector-13pvedz-1e7xvxz { color: yellow; }.css-ChildSelector-13pvedz-1e7xvxz .css-StyledComponent-1brvt4i { display: none; }" -`; - -exports[`vue styled custom external component 1`] = ` -" - <div id=\\"app\\"><div class=\\"css-NonHtmlComponent-15287sz-1wq2ikh css-NonHtmlComponent-15287sz\\"><div></div><div></div><h1>some text</h1><div></div></div></div> - " -`; - -exports[`vue styled with class 1`] = ` -" - <div id=\\"app\\"><div class=\\"some-class css-StyledComponent-1brvt4i-ulwf5d css-StyledComponent-1brvt4i\\"></div></div> - " -`; - -exports[`vue styled with properties 1`] = ` -" - <div id=\\"app\\"><div aria-label=\\"label\\" class=\\"css-StyledComponent-1brvt4i-ulwf5d css-StyledComponent-1brvt4i\\"></div></div> - " -`; - -exports[`vue styled with style 1`] = ` -" - <div id=\\"app\\"><div class=\\"css-StyledComponent-1brvt4i-ulwf5d css-StyledComponent-1brvt4i\\" style=\\"display: none;\\"></div></div> - " -`; diff --git a/test/macro/css.test.js b/test/macro/css.test.js index b8df5ec6e..c82b5fb0a 100644 --- a/test/macro/css.test.js +++ b/test/macro/css.test.js @@ -1,8 +1,14 @@ /* eslint-env jest */ import { css } from '../../src/macro' import { sheet } from '../../src' +import React from 'react' +import renderer from 'react-test-renderer' +import { matcher, serializer } from 'jest-glamor-react' -describe('css', () => { +expect.addSnapshotSerializer(serializer(sheet)) +expect.extend(matcher) + +describe('css macro', () => { test('handles more than 10 dynamic properties', () => { const cls1 = css` background: ${'white'}; @@ -18,10 +24,8 @@ describe('css', () => { border: ${'solid 1px red'}; ` - expect(cls1).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls1} />).toJSON() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with undefined values', () => { @@ -29,10 +33,8 @@ describe('css', () => { composes: ${undefined}; justifyContent: center; ` - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls2} />).toJSON() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes', () => { @@ -43,16 +45,14 @@ describe('css', () => { composes: ${cls1}; justifyContent: center; ` - expect(cls1).toMatchSnapshot() - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls2} />).toJSON() + expect(tree).toMatchSnapshotWithGlamor() }) test('handles objects', () => { const cls1 = css({ display: 'flex' }) - expect(cls1).toMatchSnapshot() + const tree = renderer.create(<div className={cls1} />).toJSON() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with objects', () => { @@ -60,7 +60,7 @@ describe('css', () => { display: ['flex', 'block'], width: 30, height: 'calc(40vw - 50px)', - ':hover': {color: 'blue'}, + ':hover': { color: 'blue' }, ':after': { content: '" "', color: 'red' @@ -74,10 +74,13 @@ describe('css', () => { justifyContent: center; ` - expect(cls1).toMatchSnapshot() - expect(cls2).toMatchSnapshot() - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() + const tree = renderer.create(<div className={cls2} />).toJSON() + expect(tree).toMatchSnapshotWithGlamor() + }) + test('null rule', () => { + const cls1 = css() + + const tree = renderer.create(<div className={cls1} />).toJSON() + expect(tree).toMatchSnapshotWithGlamor() }) }) diff --git a/test/macro/keyframes.test.js b/test/macro/keyframes.test.js index 039e94d46..eed0b804e 100644 --- a/test/macro/keyframes.test.js +++ b/test/macro/keyframes.test.js @@ -1,14 +1,14 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../../jest-utils' +import { matcher, serializer } from 'jest-glamor-react' import { keyframes, sheet } from '../../src/macro' import styled from '../../src/react/macro' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) -describe('keyframes', () => { +describe('keyframes - macro', () => { test('renders', () => { const fontSize = 20 const bounce = keyframes` @@ -33,17 +33,18 @@ describe('keyframes', () => { ` const H1 = styled.h1` - font-size: ${fontSize}px; animation: ${bounce} 2s linear infinite; ` const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('keyframes with interpolation', () => { const endingRotation = '360deg' - keyframes` + + const H1 = styled.h1` + animation: ${keyframes` from { transform: rotate(0deg); } @@ -51,7 +52,13 @@ describe('keyframes', () => { to { transform: rotate(${endingRotation}); } + `} 2s linear infinite; ` + + const tree = renderer.create(<H1>hello world</H1>).toJSON() + + expect(tree).toMatchSnapshotWithGlamor() + expect( sheet.tags.map(tag => tag.textContent || '').join('') ).toMatchSnapshot() diff --git a/test/macro/react.test.js b/test/macro/react.test.js index d63a1bf75..5ce3cf0cc 100644 --- a/test/macro/react.test.js +++ b/test/macro/react.test.js @@ -1,13 +1,14 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../../jest-utils' -import styled, { css } from '../../src/react/macro' +import { matcher, serializer } from 'jest-glamor-react' +import { css, sheet } from '../../src/macro' +import styled from '../../src/react/macro' import { ThemeProvider } from '../../src/react/theming' import { lighten, hiDPI, modularScale } from 'polished' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('styled', () => { @@ -16,7 +17,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('basic render', () => { @@ -25,7 +26,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('basic render with object as style', () => { @@ -34,7 +35,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('name', () => { @@ -46,27 +47,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('attr', () => { - const H1 = styled.h1` - font-size: attr(fontSize); - margin: attr(margin rem, 4); - position: attr(position, absolute); - ` - - const Title = ({ title }) => { - return ( - <H1 fontSize={48}> - {title} - </H1> - ) - } - - const tree = renderer.create(<Title />).toJSON() - - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('call expression', () => { @@ -75,11 +56,9 @@ describe('styled', () => { font-size: ${fontSize}px; ` - const tree = renderer - .create(<H1 className={'legacy__class'}>hello world</H1>) - .toJSON() + const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composition', () => { @@ -90,11 +69,9 @@ describe('styled', () => { const H2 = styled(H1)`font-size: ${fontSize * 2 / 3}` - const tree = renderer - .create(<H2 className={'legacy__class'}>hello world</H2>) - .toJSON() + const tree = renderer.create(<H2>hello world</H2>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('component as selector', () => { @@ -105,7 +82,7 @@ describe('styled', () => { const Thing = styled.div` display: flex; - ${H1} { + .${H1} { color: green; } ` @@ -114,26 +91,26 @@ describe('styled', () => { .create(<Thing>hello <H1>This will be green</H1> world</Thing>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('function in expression', () => { const fontSize = 20 const H1 = styled('h1')` - font-size: ${fontSize + 'px'}; + font-size: ${fontSize}; ` - const H2 = styled(H1)`font-size: ${({ scale }) => fontSize * scale + 'px'}` + const H2 = styled(H1)`font-size: ${({ scale }) => fontSize * scale};` const tree = renderer .create( - <H2 scale={2} className={'legacy__class'}> + <H2 scale={2}> hello world </H2> ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes', () => { @@ -157,20 +134,20 @@ describe('styled', () => { const tree = renderer .create( - <H2 scale={2} className={'legacy__class'}> + <H2 scale={2}> hello world </H2> ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with objects', () => { const cssA = { color: lighten(0.2, '#000'), 'font-size': modularScale(1), - [hiDPI(1.5)]: { + [hiDPI(1.5).replace('\n', ' ').trim()]: { 'font-size': modularScale(1.25) } } @@ -189,13 +166,13 @@ describe('styled', () => { const tree = renderer .create( - <H2 scale={2} className={'legacy__class'}> + <H2 scale={2}> hello world </H2> ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('innerRef', () => { @@ -207,7 +184,7 @@ describe('styled', () => { .create(<H1 innerRef={refFunction}>hello world</H1>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() expect(refFunction).toBeCalled() }) @@ -251,7 +228,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('higher order component', () => { @@ -261,14 +238,12 @@ describe('styled', () => { ` const squirtleBlueBackground = css` - name: squirtle-blue-bg; background-color: #7FC8D6; ` const flexColumn = Component => { const NewComponent = styled(Component)` - composes: ${squirtleBlueBackground} - name: onyx; + composes: ${squirtleBlueBackground}; background-color: '#343a40'; flex-direction: column; ` @@ -278,11 +253,11 @@ describe('styled', () => { const ColumnContent = flexColumn(Content) - // expect(ColumnContent.displayName).toMatchSnapshotWithEmotion() + // expect(ColumnContent.displayName).toMatchSnapshotWithGlamor() const tree = renderer.create(<ColumnContent />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes based on props', () => { @@ -295,7 +270,9 @@ describe('styled', () => { ` const H1 = styled('h1')` - composes: ${props => (props.a ? cssA : cssB)} + composes: ${props => { + return props.a ? cssA : cssB + }}; ` const tree = renderer @@ -306,7 +283,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() const tree2 = renderer .create( <H1> @@ -315,7 +292,7 @@ describe('styled', () => { ) .toJSON() - expect(tree2).toMatchSnapshotWithEmotion() + expect(tree2).toMatchSnapshotWithGlamor() }) test('objects', () => { @@ -330,7 +307,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('throws if undefined is passed as the component', () => { diff --git a/test/macro/vue.test.js b/test/macro/vue.test.js deleted file mode 100644 index 05bc00d3c..000000000 --- a/test/macro/vue.test.js +++ /dev/null @@ -1,161 +0,0 @@ -/* eslint-disable no-new */ -/* eslint-env jest */ -import Vue from 'vue/dist/vue' -import { sheet } from '../../src' -import styled from '../../src/vue/macro' - -const inherit = 'inherit' - -const StyledComponent = styled.div` - display: flex; - justify-content: center; - align-items: center; - position: ${inherit}; -` - -const BasedOnProps = styled.h1`font-weight: ${props => props.weight};` - -const baseComponent = { - render (h) { - return h('div', {}, [ - h('div'), - h('div'), - h('h1', {}, 'some text'), - h('div') - ]) - } -} - -const NonHtmlComponent = styled(baseComponent)` - background-color: purple; - color: green; -` - -const basedOnPropsComposes = styled.div` - composes: ${(props) => props.thing ? 'some-special-class' : ''} -` - -const ChildSelector = styled.main` - color: yellow; - ${StyledComponent} { - display: none; - } -` - -Vue.component('styled-component', StyledComponent) -Vue.component('based-on-props', BasedOnProps) -Vue.component('based-on-props-composes', basedOnPropsComposes) -Vue.component('non-html-component', NonHtmlComponent) -Vue.component('child-selector', ChildSelector) - -describe('vue styled', () => { - test('basic', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with properties', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component aria-label="label"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with class', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component class="some-class"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with style', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component style="display: none"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('based on props', async () => { - document.body.innerHTML = ` - <div id="app"> - <based-on-props weight="bold"></based-on-props> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('based on props composes', async () => { - document.body.innerHTML = ` - <div id="app"> - <based-on-props-composes thing="true"></based-on-props-composes> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('custom external component', async () => { - document.body.innerHTML = ` - <div id="app"> - <non-html-component></non-html-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('child selector', async () => { - document.body.innerHTML = ` - <div id="app"> - <child-selector><styled-component>wow</styled-component></child-selector> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('creates the correct styles', () => { - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() - }) -}) diff --git a/test/parser.test.js b/test/parser.test.js index 246919314..53387ed50 100644 --- a/test/parser.test.js +++ b/test/parser.test.js @@ -44,6 +44,6 @@ describe('parser', () => { expect(parseCSS(fancyCSS)).toMatchSnapshot() }) test('static', () => { - expect(parseCSS(staticCSS)).toMatchSnapshot() + expect(parseCSS(staticCSS, true)).toMatchSnapshot() }) }) diff --git a/test/react.test.js b/test/react.test.js index d0102b030..5d2ba51b9 100644 --- a/test/react.test.js +++ b/test/react.test.js @@ -1,14 +1,16 @@ /* eslint-env jest */ import React from 'react' import renderer from 'react-test-renderer' -import { matcher, serializer } from '../jest-utils' -import { css } from '../src/index' +import { matcher, serializer } from 'jest-glamor-react' +import { css, sheet } from '../src/index' import styled from '../src/react' import { ThemeProvider } from '../src/react/theming' +import { mount } from 'enzyme' +import enzymeToJson from 'enzyme-to-json' -import { lighten, hiDPI, modularScale, borderWidth } from 'polished' +import { lighten, hiDPI, modularScale } from 'polished' -expect.addSnapshotSerializer(serializer) +expect.addSnapshotSerializer(serializer(sheet)) expect.extend(matcher) describe('styled', () => { @@ -17,16 +19,19 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('basic render', () => { const fontSize = 20 - const H1 = styled.h1`font-size: ${fontSize}px;` + const H1 = styled.h1` + color: blue; + font-size: ${fontSize}; + ` const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('basic render with object as style', () => { @@ -35,7 +40,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('name', () => { @@ -47,27 +52,7 @@ describe('styled', () => { const tree = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() - }) - - test('attr', () => { - const H1 = styled.h1` - font-size: attr(fontSize); - margin: attr(margin rem, 4); - position: attr(position, absolute); - ` - - const Title = ({ title }) => { - return ( - <H1 fontSize={48}> - {title} - </H1> - ) - } - - const tree = renderer.create(<Title />).toJSON() - - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('call expression', () => { @@ -80,7 +65,33 @@ describe('styled', () => { .create(<H1 className={'legacy__class'}>hello world</H1>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() + }) + + test('nested', () => { + const fontSize = '20px' + const H1 = styled.h1`font-size: ${fontSize};` + + const Thing = styled.div` + display: flex; + & div { + color: green; + + & span { + color: red; + } + } + ` + + const tree = renderer + .create( + <Thing> + hello <H1>This will be green</H1> world + </Thing> + ) + .toJSON() + + expect(tree).toMatchSnapshotWithGlamor() }) test('composition', () => { @@ -95,7 +106,106 @@ describe('styled', () => { .create(<H2 className={'legacy__class'}>hello world</H2>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() + }) + + test('input placeholder', () => { + const Input = styled.input` + ::placeholder { + background-color: green; + } + ` + const tree = renderer + .create(<Input>hello world</Input>) + .toJSON() + + expect(tree).toMatchSnapshotWithGlamor() + }) + + test('input placeholder object', () => { + const Input = styled('input')({ + '::placeholder': { + backgroundColor: 'green' + } + }) + + const tree = renderer + .create(<Input>hello world</Input>) + .toJSON() + + expect(tree).toMatchSnapshotWithGlamor() + }) + + test('object composition', () => { + const imageStyles = css({ + width: 96, + height: 96 + }) + + const fakeBlue = css([ + { + color: 'blue' + } + ]) + + const red = css([ + { + color: 'red' + } + ]) + + const blue = css([ + red, + { + color: 'blue' + } + ]) + + const prettyStyles = css([ + { + borderRadius: '50%', + transition: 'transform 400ms ease-in-out', + ':hover': { + transform: 'scale(1.2)' + } + }, + { border: '3px solid currentColor' } + ]) + + const Avatar = styled('img')` + composes: ${prettyStyles} ${imageStyles} ${blue} + ` + + const tree = renderer.create(<Avatar />).toJSON() + + expect(tree).toMatchSnapshotWithGlamor() + }) + + test('handles more than 10 dynamic properties', () => { + const H1 = styled('h1')` + text-decoration: ${'underline'}; + border-right: solid blue 54px; + background: ${'white'}; + color: ${'black'}; + display: ${'block'}; + border-radius: ${'3px'}; + padding: ${'25px'}; + width: ${'500px'}; + z-index: ${100}; + font-size: ${'18px'}; + text-align: ${'center'}; + border-left: ${p => p.theme.blue}; + ` + + const tree = renderer + .create( + <H1 className={'legacy__class'} theme={{ blue: 'blue' }}> + hello world + </H1> + ) + .toJSON() + + expect(tree).toMatchSnapshotWithGlamor() }) test('component as selector', () => { @@ -104,7 +214,7 @@ describe('styled', () => { const Thing = styled.div` display: flex; - ${H1} { + .${H1} { color: green; } ` @@ -117,7 +227,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('function in expression', () => { @@ -136,7 +246,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes', () => { @@ -148,33 +258,34 @@ describe('styled', () => { const cssB = css` composes: ${cssA} - height: 64px; + color: red; ` - const H1 = styled('h1')` - composes: ${cssB} + const BlueH1 = styled('h1')` + composes: ${cssB}; + color: blue; font-size: ${fontSize}; ` - const H2 = styled(H1)`font-size:32px;` + const FinalH2 = styled(BlueH1)`font-size:32px;` const tree = renderer .create( - <H2 scale={2} className={'legacy__class'}> + <FinalH2 scale={2} className={'legacy__class'}> hello world - </H2> + </FinalH2> ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes with objects', () => { const cssA = { color: lighten(0.2, '#000'), - 'font-size': modularScale(1), - [hiDPI(1.5)]: { - 'font-size': modularScale(1.25) + fontSize: modularScale(1), + [hiDPI(1.5).replace('\n', ' ').trim()]: { + fontSize: modularScale(1.25) } } @@ -198,7 +309,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('innerRef', () => { @@ -210,7 +321,7 @@ describe('styled', () => { .create(<H1 innerRef={refFunction}>hello world</H1>) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() expect(refFunction).toBeCalled() }) @@ -254,7 +365,7 @@ describe('styled', () => { ) .toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('higher order component', () => { @@ -281,11 +392,11 @@ describe('styled', () => { const ColumnContent = flexColumn(Content) - // expect(ColumnContent.displayName).toMatchSnapshotWithEmotion() + // expect(ColumnContent.displayName).toMatchSnapshotWithGlamor() const tree = renderer.create(<ColumnContent />).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) test('composes based on props', () => { @@ -303,10 +414,10 @@ describe('styled', () => { const tree = renderer.create(<H1 a>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() const tree2 = renderer.create(<H1>hello world</H1>).toJSON() - expect(tree2).toMatchSnapshotWithEmotion() + expect(tree2).toMatchSnapshotWithGlamor() }) test('objects', () => { @@ -315,9 +426,21 @@ describe('styled', () => { })) const tree = renderer.create(<H1 display='flex'>hello world</H1>).toJSON() - expect(tree).toMatchSnapshotWithEmotion() + expect(tree).toMatchSnapshotWithGlamor() }) + test('change theme', () => { + const Div = styled.div` + color: ${props => props.theme.primary} + ` + const TestComponent = (props) => (<ThemeProvider theme={props.theme}>{props.renderChild ? <Div>this will be green then pink</Div> : null}</ThemeProvider>) + const wrapper = mount(<TestComponent renderChild theme={{ primary: 'green' }} />) + expect(enzymeToJson(wrapper)).toMatchSnapshotWithGlamor() + wrapper.setProps({ theme: { primary: 'pink' } }) + expect(enzymeToJson(wrapper)).toMatchSnapshotWithGlamor() + wrapper.setProps({ renderChild: false }) + expect(enzymeToJson(wrapper)).toMatchSnapshotWithGlamor() + }) test('throws if undefined is passed as the component', () => { expect( () => styled(undefined)`display: flex;` diff --git a/test/server.test.js b/test/server.test.js index fd867ca69..44f7ea372 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -14,8 +14,8 @@ const Main = styled.main`display: flex;` const Image = styled.img` border-radius: 50%; - height: attr(size px, 50) - width: attr(size px, 50) + height: 50px; + width: 50px; background-color: ${color} ` @@ -43,8 +43,6 @@ const Page = () => describe('extractCritical', () => { test('returns static css', () => { - expect( - extractCritical(renderToString(<Page />)) - ).toMatchSnapshot() + expect(extractCritical(renderToString(<Page />))).toMatchSnapshot() }) }) diff --git a/test/vue.test.js b/test/vue.test.js deleted file mode 100644 index e88ad9349..000000000 --- a/test/vue.test.js +++ /dev/null @@ -1,161 +0,0 @@ -/* eslint-disable no-new */ -/* eslint-env jest */ -import Vue from 'vue/dist/vue' -import { sheet } from '../src/index' -import styled from '../src/vue' - -const inherit = 'inherit' - -const StyledComponent = styled.div` - display: flex; - justify-content: center; - align-items: center; - position: ${inherit}; -` - -const BasedOnProps = styled.h1`font-weight: ${props => props.weight};` - -const baseComponent = { - render (h) { - return h('div', {}, [ - h('div'), - h('div'), - h('h1', {}, 'some text'), - h('div') - ]) - } -} - -const NonHtmlComponent = styled(baseComponent)` - background-color: purple; - color: green; -` - -const basedOnPropsComposes = styled.div` - composes: ${(props) => props.thing ? 'some-special-class' : ''} -` - -const ChildSelector = styled.main` - color: yellow; - ${StyledComponent} { - display: none; - } -` - -Vue.component('styled-component', StyledComponent) -Vue.component('based-on-props', BasedOnProps) -Vue.component('based-on-props-composes', basedOnPropsComposes) -Vue.component('non-html-component', NonHtmlComponent) -Vue.component('child-selector', ChildSelector) - -describe('vue styled', () => { - test('basic', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with properties', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component aria-label="label"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with class', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component class="some-class"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('with style', async () => { - document.body.innerHTML = ` - <div id="app"> - <styled-component style="display: none"></styled-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('based on props', async () => { - document.body.innerHTML = ` - <div id="app"> - <based-on-props weight="bold"></based-on-props> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('based on props composes', async () => { - document.body.innerHTML = ` - <div id="app"> - <based-on-props-composes thing="true"></based-on-props-composes> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('custom external component', async () => { - document.body.innerHTML = ` - <div id="app"> - <non-html-component></non-html-component> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('child selector', async () => { - document.body.innerHTML = ` - <div id="app"> - <child-selector><styled-component>wow</styled-component></child-selector> - </div> - ` - new Vue({ - el: '#app' - }) - - await Vue.nextTick(() => {}) - expect(document.body.innerHTML).toMatchSnapshot() - }) - test('creates the correct styles', () => { - expect( - sheet.tags.map(tag => tag.textContent || '').join('') - ).toMatchSnapshot() - }) -}) diff --git a/vue/index.js b/vue/index.js deleted file mode 100644 index 9b0c265eb..000000000 --- a/vue/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../lib/vue') diff --git a/vue/macro.js b/vue/macro.js deleted file mode 100644 index b6de0934d..000000000 --- a/vue/macro.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../lib/vue/macro')