├── .eslintignore ├── .babelrc ├── src ├── utils.js ├── index.js ├── BaseView.js ├── View.js ├── css.js └── __tests__ │ ├── css.test.js │ └── View.test.js ├── .npmignore ├── .eslintrc ├── .vscode └── settings.json ├── .prettierrc.js ├── .prettierignore ├── .gitignore ├── .storybook ├── addons.js └── config.js ├── jest.config.js ├── .travis.yml ├── stories ├── css.stories.js ├── View.stories.js ├── ThemeProvider.stories.js └── Mixins.stories.js ├── LICENSE ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { "presets": ["@itsjonq/zero/babel"] } 2 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | export { is } from '@itsjonq/is'; 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | __fixtures__ 2 | __mocks__ 3 | __tests__ 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { "extends": "./node_modules/@itsjonq/zero/eslint.js" } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } 4 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@itsjonq/zero/prettier'); 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | package-lock.json 3 | node_modules 4 | dist 5 | coverage -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .cache 3 | .eslintcache 4 | .opt-in 5 | .opt-out 6 | coverage 7 | dist 8 | node_modules 9 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-actions/register'; 2 | import '@storybook/addon-links/register'; 3 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const jestConfig = require('@itsjonq/zero/jest'); 2 | 3 | module.exports = Object.assign(jestConfig, { 4 | // your overrides here 5 | }); 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { View } from './View'; 2 | 3 | export { css, cx } from './css'; 4 | export { BaseView } from './BaseView'; 5 | export { View } from './View'; 6 | 7 | export default View; 8 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@storybook/react'; 2 | 3 | // automatically import all files ending in *.stories.js 4 | configure(require.context('../stories', true, /\.stories\.js$/), module); 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "lts/*" 4 | 5 | cache: 6 | directories: 7 | - node_modules 8 | 9 | install: 10 | - npm install 11 | 12 | script: 13 | - npm run validate 14 | 15 | after_success: 16 | - npm run coverage 17 | 18 | branches: 19 | only: 20 | - master 21 | -------------------------------------------------------------------------------- /stories/css.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { css, View } from '../src/index'; 3 | 4 | export default { 5 | title: 'css', 6 | }; 7 | 8 | export const _default = () => { 9 | const styles = css` 10 | ${({ clr }) => `color: ${clr}`}; 11 | padding: 20px; 12 | `; 13 | 14 | return ( 15 | 16 | Hello 17 | 18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /stories/View.stories.js: -------------------------------------------------------------------------------- 1 | import React, { useRef } from 'react'; 2 | import { View } from '../src/View'; 3 | 4 | export default { 5 | title: 'View', 6 | }; 7 | 8 | export const _default = () => ( 9 | <> 10 | 11 | Hello 12 | 13 | 14 | ); 15 | 16 | const Example = () => { 17 | const ref = useRef(); 18 | console.log(ref); 19 | return Hello; 20 | }; 21 | 22 | export const ref = () => ; 23 | -------------------------------------------------------------------------------- /stories/ThemeProvider.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ThemeProvider } from 'emotion-theming'; 3 | import { View } from '../src/index'; 4 | 5 | const theme = { 6 | fontFamily: 'arial', 7 | }; 8 | 9 | export default { 10 | title: 'ThemeProvider', 11 | }; 12 | 13 | export const _default = () => { 14 | return ( 15 | 16 | Hello 17 | 18 | ); 19 | }; 20 | 21 | export const themeOverride = () => { 22 | return ( 23 | 24 | Hello 25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /src/BaseView.js: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled'; 2 | import { sanitizeStyleProps } from 'is-style-prop-valid'; 3 | import { is } from './utils'; 4 | 5 | const defaultProps = { 6 | boxSizing: 'border-box', 7 | }; 8 | 9 | export const BaseView = styled('div')(props => { 10 | const mergedProps = { ...defaultProps, ...props }; 11 | const { theme, sx, ...restProps } = mergedProps; 12 | 13 | // Theme support 14 | const themeProps = is.plainObject(theme) ? theme : {}; 15 | // Style prop (Override) 16 | const styleProps = is.plainObject(sx) ? sx : {}; 17 | 18 | return sanitizeStyleProps({ 19 | ...themeProps, 20 | ...restProps, 21 | ...styleProps, 22 | }); 23 | }); 24 | 25 | export default BaseView; 26 | -------------------------------------------------------------------------------- /src/View.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { compileToClassName } from './css'; 3 | import BaseView from './BaseView'; 4 | 5 | /** 6 | * UI Primitive with built-in styled CSS support. 7 | * 8 | * @param {object} props Component props. 9 | * @returns React.Component A React component, enhanced with generated styles. 10 | * @example 11 | * Hello 12 | */ 13 | export const View = React.forwardRef((props, ref) => { 14 | const { className, css: cssProp, ...restProps } = props; 15 | const nextClassName = compileToClassName(props); 16 | 17 | return ; 18 | }); 19 | 20 | export default View; 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Jon Quach 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /stories/Mixins.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { space, layout, typography, color } from 'styled-system'; 3 | import { View, css } from '../src/index'; 4 | 5 | export default { 6 | title: 'StyledSystem', 7 | }; 8 | 9 | export const _default = () => { 10 | const customMixin = props => { 11 | const { variant } = props; 12 | 13 | switch (variant) { 14 | case 'primary': 15 | return { 16 | background: '#05f', 17 | color: 'white', 18 | }; 19 | default: 20 | return `background: #ddd;`; 21 | } 22 | }; 23 | 24 | const Box = props => { 25 | return ( 26 | 32 | ); 33 | }; 34 | 35 | return ( 36 | <> 37 | Hello 38 | Hello 39 | 40 | ); 41 | }; 42 | 43 | export const styledSystem = () => { 44 | // Add styled-system functions to your component 45 | const Box = props => { 46 | return ( 47 | 56 | ); 57 | }; 58 | 59 | return ( 60 | 61 | Hello 62 | 63 | ); 64 | }; 65 | -------------------------------------------------------------------------------- /src/css.js: -------------------------------------------------------------------------------- 1 | import { css as emotionCss, cx as emotionCx } from 'emotion'; 2 | import { is } from './utils'; 3 | 4 | export { cx } from 'emotion'; 5 | 6 | export const INTERNAL_COMPILE_KEY = '__SECRET_STYLED_VIEW_TO_COMPILE__'; 7 | 8 | export function css(...args) { 9 | const [firstArg, ...fns] = args; 10 | 11 | if (!fns.length) { 12 | if (is.array(firstArg)) { 13 | return css.apply(css, firstArg); 14 | } 15 | if (is.string(firstArg)) { 16 | return emotionCss`${firstArg}`; 17 | } 18 | if (is.plainObject(firstArg)) { 19 | return emotionCss(firstArg); 20 | } 21 | return ''; 22 | } 23 | 24 | return { 25 | args, 26 | [INTERNAL_COMPILE_KEY]: true, 27 | }; 28 | } 29 | 30 | export function cxx(...args) { 31 | return props => { 32 | const [strings, ...fns] = args; 33 | const results = []; 34 | 35 | strings.forEach((string, index) => { 36 | results.push(string); 37 | const fn = fns[index]; 38 | 39 | if (is.function(fn)) { 40 | try { 41 | const rx = emotionCss` 42 | ${fn(props)}; 43 | `; 44 | results.push(rx); 45 | } catch (err) { 46 | console.warn( 47 | 'styled-view: Error when parsing css', 48 | '\n', 49 | '\n', 50 | err, 51 | ); 52 | results.push(''); 53 | } 54 | } 55 | }); 56 | 57 | return emotionCss` 58 | ${results} 59 | `; 60 | }; 61 | } 62 | 63 | export function compileToClassName(props) { 64 | const { className, css } = props; 65 | let result = ''; 66 | 67 | if (is.plainObject(css)) { 68 | if (css[INTERNAL_COMPILE_KEY]) { 69 | result = cxx.apply(cxx, [...css.args])(props); 70 | } else { 71 | result = emotionCss(css); 72 | } 73 | } 74 | 75 | if (is.array(css)) { 76 | result = emotionCss(css); 77 | } 78 | 79 | if (is.string(css)) { 80 | result = css; 81 | } 82 | 83 | return emotionCx(className, result); 84 | } 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "styled-view", 3 | "version": "0.0.10", 4 | "description": "UI Primitive for React, with CSS-in-JS support", 5 | "main": "dist/cjs/index.js", 6 | "module": "dist/es/index.js", 7 | "sideEffects": false, 8 | "private": false, 9 | "scripts": { 10 | "prestart": "zero prestart", 11 | "build:es": "BUILD_FORMAT=es zero build -d dist/es", 12 | "build:cjs": "BUILD_FORMAT=cjs zero build -d dist/cjs", 13 | "build": "npm run build:cjs && npm run build:es -- --no-clean", 14 | "coverage": "nyc report --temp-directory=coverage --reporter=text-lcov | coveralls", 15 | "open:coverage": "open ./coverage/lcov-report/index.html", 16 | "lint": "zero lint", 17 | "dev": "zero test", 18 | "start": "npm run storybook", 19 | "test": "zero test --coverage", 20 | "test:coverage": "npm run test", 21 | "format": "zero format", 22 | "validate": "zero validate", 23 | "release": "zero release", 24 | "version": "npm run build", 25 | "precommit": "zero pre-commit", 26 | "storybook": "start-storybook -p 6006", 27 | "build-storybook": "build-storybook" 28 | }, 29 | "files": [ 30 | "dist", 31 | "README.md", 32 | "LICENSE" 33 | ], 34 | "author": "Jon Quach (https://jonquach.com)", 35 | "repository": { 36 | "type": "git", 37 | "url": "git+https://github.com/itsjonq/styled-view.git" 38 | }, 39 | "bugs": { 40 | "url": "https://github.com/itsjonq/styled-view/issues" 41 | }, 42 | "homepage": "https://github.com/itsjonq/styled-view#readme", 43 | "keywords": [ 44 | "styled", 45 | "components", 46 | "styled-components", 47 | "emotion", 48 | "view", 49 | "styled-view", 50 | "css-in-js", 51 | "css", 52 | "react" 53 | ], 54 | "license": "MIT", 55 | "publishConfig": { 56 | "access": "public" 57 | }, 58 | "peerDependencies": { 59 | "react": "^16" 60 | }, 61 | "devDependencies": { 62 | "@babel/core": "7.7.4", 63 | "@itsjonq/cyan": "^0.15.1", 64 | "@itsjonq/zero": "^4.1.7", 65 | "@storybook/addon-actions": "5.2.6", 66 | "@storybook/addon-links": "5.2.6", 67 | "@storybook/addons": "5.2.6", 68 | "@storybook/react": "5.2.6", 69 | "@styled-system/css": "5.0.23", 70 | "babel-loader": "8.0.6", 71 | "coveralls": "3.0.9", 72 | "emotion-theming": "10.0.19", 73 | "nyc": "14.1.1", 74 | "react": "16.12.0", 75 | "react-dom": "16.12.0", 76 | "styled-system": "5.1.2" 77 | }, 78 | "dependencies": { 79 | "@emotion/core": "^10.x", 80 | "@emotion/styled": "^10.x", 81 | "@itsjonq/is": "^0.0.2", 82 | "emotion": "^10.x", 83 | "is-style-prop-valid": "^0.0.7" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/__tests__/css.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cy } from '@itsjonq/cyan'; 3 | import { css, View } from '../index'; 4 | import { space, layout, typography, color } from 'styled-system'; 5 | 6 | describe('css', () => { 7 | test('should return string for invalid type', () => { 8 | expect(css(true)).toBe(''); 9 | }); 10 | 11 | test('should render string styles', () => { 12 | cy.render( 13 | , 18 | ); 19 | 20 | const el = cy.get('div'); 21 | 22 | expect(el.style().background).toBe('red'); 23 | }); 24 | 25 | test('should render array styles', () => { 26 | cy.render(); 27 | 28 | const el = cy.get('div'); 29 | 30 | expect(el.style().background).toBe('red'); 31 | }); 32 | 33 | test('should render object styles', () => { 34 | cy.render( 35 | , 40 | ); 41 | 42 | const el = cy.get('div'); 43 | 44 | expect(el.style().background).toBe('red'); 45 | }); 46 | 47 | test('should render function styles', () => { 48 | const bg = ({ bg }) => `background: ${bg};`; 49 | 50 | cy.render( 51 | , 57 | ); 58 | 59 | const el = cy.get('div'); 60 | 61 | expect(el.style().background).toBe('red'); 62 | }); 63 | 64 | test('should render string + function styles', () => { 65 | const bg = ({ bg }) => `background: ${bg};`; 66 | 67 | cy.render( 68 | , 76 | ); 77 | 78 | const el = cy.get('div'); 79 | 80 | expect(el.style().background).toBe('red'); 81 | expect(el.style().padding).toBe('20px'); 82 | expect(el.style().margin).toBe('10px'); 83 | }); 84 | 85 | test('should handle mixins from 3rd party libraries', () => { 86 | cy.render( 87 | , 98 | ); 99 | 100 | const el = cy.get('div'); 101 | 102 | expect(el.style().background).toBe('red'); 103 | expect(el.style().padding).toBe('32px'); 104 | expect(el.style().margin).toBe('8px'); 105 | }); 106 | 107 | test('should handle invalid type', () => { 108 | cy.render( 109 | , 115 | ); 116 | 117 | const el = cy.get('div'); 118 | 119 | expect(el.style().background).toBe('red'); 120 | }); 121 | 122 | test('should handle invalid function return', () => { 123 | const spy = jest.fn(); 124 | console.warn = spy; 125 | 126 | const errorMixin = () => { 127 | throw new Error('Nope'); 128 | }; 129 | 130 | cy.render( 131 | , 137 | ); 138 | 139 | const el = cy.get('div'); 140 | 141 | expect(el.style().background).toBe('red'); 142 | /* eslint-disable jest/prefer-called-with */ 143 | expect(spy).toHaveBeenCalled(); 144 | /* eslint-enable jest/prefer-called-with */ 145 | }); 146 | }); 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ✌️ Styled View 2 | 3 | [![Build Status](https://travis-ci.org/ItsJonQ/styled-view.svg?branch=master)](https://travis-ci.org/ItsJonQ/styled-view) 4 | [![Coverage Status](https://coveralls.io/repos/github/ItsJonQ/styled-view/badge.svg?branch=master)](https://coveralls.io/github/ItsJonQ/styled-view?branch=master) 5 | [![Bundle size](https://badgen.net/bundlephobia/minzip/styled-view)](https://bundlephobia.com/result?p=styled-view) 6 | 7 | > UI Primitive for React, with CSS-in-JS support 8 | 9 | ## Table of contents 10 | 11 | 12 | 13 | 14 | - [Installation](#installation) 15 | - [Usage](#usage) 16 | - [`css` function](#css-function) 17 | - [`css` prop](#css-prop) 18 | - [`sx` prop](#sx-prop) 19 | - [Mixins](#mixins) 20 | - [Theming](#theming) 21 | 22 | 23 | 24 | ## Installation 25 | 26 | ``` 27 | npm install styled-view 28 | ``` 29 | 30 | ## Usage 31 | 32 | The `` component supports all of the default [CSSProperties](https://github.com/ItsJonQ/is-style-prop-valid/blob/master/src/CSSProperty.js#L47) as props. The styles transformed and handled by [Emotion](https://emotion.sh/docs/introduction). 33 | 34 | ```jsx 35 | import React from 'react'; 36 | import { View } from 'styled-view'; 37 | 38 | function Example() { 39 | return ( 40 | 41 | Hello 42 | 43 | ); 44 | } 45 | ``` 46 | 47 | ### `css` function 48 | 49 | `css` is a utility function that works with the `` `css` prop. The API is similar to the [Emotion's css prop](https://emotion.sh/docs/css-prop#string-styles). Unlike the `css` prop, the [tagged template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) allows for functions, which is handy for mixins. 50 | 51 | ```jsx 52 | import React from 'react'; 53 | import { css, View } from 'styled-view'; 54 | 55 | function Example() { 56 | const variant = ({ variant }) => { 57 | switch (variant) { 58 | case 'primary': 59 | return ` 60 | background-color: blue; 61 | color: white; 62 | `; 63 | default: 64 | return ` 65 | background-color: yellow; 66 | `; 67 | } 68 | }; 69 | 70 | return ( 71 | 81 | Hello 82 | 83 | ); 84 | } 85 | ``` 86 | 87 | ### `css` prop 88 | 89 | `` accepts a special `css` prop, which allows you to write styles, just like the [css prop](https://emotion.sh/docs/css-prop#string-styles) or [styled component](https://emotion.sh/docs/styled#styling-elements-and-components) from Emotion. 90 | 91 | ```jsx 92 | import React from 'react'; 93 | import { View } from 'styled-view'; 94 | 95 | function Example() { 96 | const css = ` 97 | &:hover { 98 | background-color: blue; 99 | color: white; 100 | } 101 | 102 | @media (min-width: 768px) { 103 | padding: 40px; 104 | } 105 | `; 106 | 107 | return ( 108 | 109 | Hello 110 | 111 | ); 112 | } 113 | ``` 114 | 115 | ### `sx` prop 116 | 117 | `` accepts a special `sx` prop, which allows you to write style objects. 118 | 119 | ```jsx 120 | import React from 'react'; 121 | import { View } from 'styled-view'; 122 | 123 | function Example() { 124 | return Hello; 125 | } 126 | ``` 127 | 128 | ### Mixins 129 | 130 | `` can render mixins (`function`) when passed into the `css` function. This enables integration with libraries like [Styled Systems](https://github.com/styled-system/styled-system). It also enable you to add your very own custom mixins! 131 | 132 | ```jsx 133 | import React from 'react'; 134 | import { space, layout, typography, color } from 'styled-system'; 135 | import { css, View } from 'styled-view'; 136 | 137 | // Add styled-system functions to your component 138 | function Box(props) { 139 | return ( 140 | 149 | ); 150 | } 151 | 152 | function Example() { 153 | return ( 154 | 155 | Hello 156 | 157 | ); 158 | } 159 | ``` 160 | 161 | This concepts was inspired by [James Newell](https://github.com/jameslnewell) ❤️! 162 | 163 | ### Theming 164 | 165 | Theming `` works as specified by [Emotion Theming](https://emotion.sh/docs/theming). 166 | 167 | ```jsx 168 | import React from 'react'; 169 | import { ThemeProvider } from 'emotion-theming'; 170 | import { View } from 'styled-view'; 171 | 172 | const theme = { 173 | fontFamily: 'arial', 174 | }; 175 | 176 | function Example() { 177 | return ( 178 | 179 | Hello 180 | 181 | ); 182 | } 183 | ``` 184 | -------------------------------------------------------------------------------- /src/__tests__/View.test.js: -------------------------------------------------------------------------------- 1 | import React, { useRef } from 'react'; 2 | import { cy } from '@itsjonq/cyan'; 3 | import { css as emotionCss } from 'emotion'; 4 | import { ThemeProvider } from 'emotion-theming'; 5 | import { View, css } from '../index'; 6 | 7 | describe('View', () => { 8 | describe('rendering', () => { 9 | test('should render a div, by default', () => { 10 | cy.render(); 11 | 12 | const el = cy.get('div'); 13 | 14 | expect(el.exists()).toBeTruthy(); 15 | }); 16 | 17 | test('should render as another component, using `as` prop', () => { 18 | cy.render(Hello); 19 | 20 | const el = cy.get('button'); 21 | 22 | expect(el.exists()).toBeTruthy(); 23 | expect(el.text()).toBe('Hello'); 24 | 25 | expect(cy.get('div').exists()).toBe(false); 26 | }); 27 | }); 28 | 29 | describe('styles', () => { 30 | test('should render styles', () => { 31 | cy.render( 32 | 33 | Hello 34 | , 35 | ); 36 | 37 | const el = cy.get('div'); 38 | 39 | expect(el.style().background).toBe('pink'); 40 | expect(el.style().color).toBe('white'); 41 | }); 42 | 43 | test('should render styles using css prop', () => { 44 | cy.render( 45 | 51 | Hello 52 | , 53 | ); 54 | 55 | const el = cy.get('div'); 56 | 57 | expect(el.style().background).toBe('purple'); 58 | expect(el.style().color).toBe('white'); 59 | }); 60 | 61 | test('should render object styles using css prop', () => { 62 | cy.render( 63 | 70 | Hello 71 | , 72 | ); 73 | 74 | const el = cy.get('div'); 75 | 76 | expect(el.style().background).toBe('red'); 77 | expect(el.style().color).toBe('black'); 78 | expect(el.style().padding).toBe('21px'); 79 | }); 80 | 81 | test('should render styles using sx prop', () => { 82 | cy.render( 83 | 90 | Hello 91 | , 92 | ); 93 | 94 | const el = cy.get('div'); 95 | 96 | expect(el.style().background).toBe('red'); 97 | expect(el.style().color).toBe('black'); 98 | expect(el.style().padding).toBe('21px'); 99 | }); 100 | 101 | test('should handle invalid sx prop', () => { 102 | cy.render(Hello); 103 | 104 | const el = cy.get('div'); 105 | 106 | expect(el.style().zIndex).not.toBe(100); 107 | }); 108 | 109 | test('should handle css prop functions', () => { 110 | const Example = props => { 111 | const mixin = ({ p }) => `padding: ${p * 4}px`; 112 | return ( 113 | 119 | ); 120 | }; 121 | 122 | const customCss = css` 123 | background: pink; 124 | `; 125 | 126 | cy.render(); 127 | 128 | expect(cy.get('div').style().background).toBe('pink'); 129 | expect(cy.get('div').style().padding).toBe('8px'); 130 | }); 131 | }); 132 | 133 | describe('theming', () => { 134 | test('should enable theming', () => { 135 | const theme = { 136 | fontFamily: 'arial', 137 | }; 138 | 139 | cy.render( 140 | 141 | Hello 142 | , 143 | ); 144 | 145 | expect(cy.get('div').style().fontFamily).toBe('arial'); 146 | }); 147 | 148 | test('should recognize own themed props', () => { 149 | const theme = { 150 | fontFamily: 'arial', 151 | }; 152 | 153 | cy.render( 154 | 155 | Hello 156 | , 157 | ); 158 | 159 | expect(cy.get('div').style().fontFamily).toBe('georgia'); 160 | }); 161 | 162 | test('should handle invalid theme prop', () => { 163 | cy.render(Hello); 164 | 165 | expect(cy.get('div').exists()).toBe(true); 166 | }); 167 | 168 | test('should override theming if props are defined', () => { 169 | const theme = { 170 | fontFamily: 'arial', 171 | }; 172 | 173 | cy.render( 174 | 175 | Title 176 | 177 | Hello 178 | 179 | There 180 | Click 181 | 182 | Subtitle 183 | 184 | 185 | 186 | , 187 | ); 188 | 189 | expect(cy.get('h1').style().fontFamily).toBe('arial'); 190 | expect(cy.get('div').style().fontFamily).toBe('georgia'); 191 | expect(cy.get('span').style().fontFamily).toBe('monospace'); 192 | expect(cy.get('button').style().fontFamily).toBe('arial'); 193 | expect(cy.get('h2').style().fontFamily).toBe('serif'); 194 | }); 195 | }); 196 | }); 197 | --------------------------------------------------------------------------------