├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .npmrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── no-important.js ├── package.json ├── src ├── aphroditeInterface.js ├── aphroditeInterfaceFactory.js ├── no-important.js └── utils │ ├── resolveLTR.js │ ├── resolveRTL.js │ └── separateStyles.js └── test ├── .eslintrc ├── aphroditeInterfaceFactory_test.js ├── aphroditeInterface_test.js ├── mocha.opts ├── no-important_test.js └── utils ├── resolveLTR_test.js └── resolveRTL_test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["airbnb"], 3 | "plugins": [ 4 | ["transform-replace-object-assign", { "moduleSpecifier": "object.assign" }], 5 | ], 6 | } 7 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "env": { 4 | "browser": true, 5 | "node": true 6 | }, 7 | 8 | "rules": { 9 | "no-underscore-dangle": 0 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Built files 40 | /lib 41 | 42 | # Only apps should have lockfiles 43 | npm-shrinkwrap.json 44 | package-lock.json 45 | yarn.lock 46 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "10" 5 | - "8" 6 | - "6" 7 | before_install: 8 | - 'nvm install-latest-npm' 9 | install: 10 | - 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ] || [ "${TRAVIS_NODE_VERSION}" = "0.9" ]; then nvm install --latest-npm 0.8 && npm install && nvm use "${TRAVIS_NODE_VERSION}"; else npm install; fi;' 11 | script: 12 | - 'if [ -n "${PRETEST-}" ]; then npm run pretest ; fi' 13 | - 'if [ -n "${POSTTEST-}" ]; then npm run posttest ; fi' 14 | - 'if [ -n "${COVERAGE-}" ]; then npm run coverage ; fi' 15 | - 'if [ -n "${TEST-}" ]; then npm run tests-only ; fi' 16 | sudo: false 17 | env: 18 | - TEST=true 19 | matrix: 20 | fast_finish: true 21 | include: 22 | - node_js: "lts/*" 23 | env: PRETEST=true 24 | - node_js: "lts/*" 25 | env: COVERAGE=true 26 | allow_failures: 27 | - os: osx 28 | - env: TEST=true ALLOW_FAILURE=true 29 | - env: COVERAGE=true 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v6.0.1 4 | 5 | - [fix] Update rtl-css-js ^1.13.0 -> ^1.13.1 6 | 7 | ## v6.0.0 8 | 9 | - [Deps] Include `react-with-styles v4.0.0` in peer deps 10 | - [breaking] [Dev Deps] Update `@babel*`, `airbnb-js-shims`, `aphrodite`, `babel-plugin-transform-replace-object-assign`, `babel-preset-airbnb`, `chai`, `eslint*`, `prop-types`, `react`, `react-dom`, `sinon-sandbox` and drop support for node < 6 11 | - [Deps] update `object.entries`, `rtl-css-js` 12 | - [Dev Deps] update `eslint`, `eslint-config-airbnb`, `eslint-plugin-import`, `eslint-plugin-jsx-a11y`, `eslint-plugin-react`, `rimraf`, `safe-publish-latest`, `airbnb-js-shims` 13 | 14 | ## v5.0.1 15 | 16 | - [Deps] Replace `array-flatten` with `array.prototype.flat` 17 | - [Deps] Update `has` and `rtl-css-js` 18 | - [Dev] Update devDependencies 19 | 20 | ## v5.0.0 21 | 22 | - Remove support for Aphrodite v1 and earlier 23 | 24 | ## v4.0.2 25 | 26 | - Add support for Aphrodite v2 27 | 28 | ## v4.0.1 29 | 30 | - Fix `this` reference in create and resolve methods 31 | 32 | ## v4.0.0 33 | 34 | - Remove with-rtl interface in favor of separate LTR/RTL create and resolve methods 35 | 36 | ## v3.1.1 37 | 38 | - Allow boolean values for styles to run without failure 39 | 40 | ## v3.1.0 41 | 42 | - Add RTL support for `linear-gradient` 43 | 44 | ## v3.0.1 45 | 46 | - Fix for `with-rtl` interface when an aphrodite style is called multiple times 47 | 48 | ## v3.0.0 49 | 50 | - Revert default export to v1 version and export `with-rtl` interface in addition 51 | 52 | ## v2.1.0 53 | 54 | - Add RTL support for `borderLeftWidth`, `translateX`, `translate`, and `translate3d` 55 | 56 | ## v2.0.1 57 | 58 | - Fix RTL support for pseudoselectors and match media queries 59 | 60 | ## v2.0.0 61 | 62 | - Add RTL support in the `resolve` method 63 | 64 | ## v1.2.0 65 | 66 | - Add support for using Aphrodite in no-important mode, which disables adding 67 | `!important` to every style. 68 | 69 | ## v1.1.1 70 | 71 | - This release reverts all of the changes from 1.1.0, so inline styles no longer 72 | include vendor prefixes. 73 | 74 | ## v1.1.0 75 | 76 | - Inline styles will now automatically include vendor prefixes. 77 | 78 | ## v1.0.0 79 | 80 | - Initial release. 81 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Airbnb 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-with-styles-interface-aphrodite [![Version Badge][npm-version-svg]][package-url] 2 | 3 | [![Build Status][travis-svg]][travis-url] 4 | [![dependency status][deps-svg]][deps-url] 5 | [![dev dependency status][dev-deps-svg]][dev-deps-url] 6 | [![License][license-image]][license-url] 7 | [![Downloads][downloads-image]][downloads-url] 8 | 9 | [![npm badge][npm-badge-png]][package-url] 10 | 11 | Interface to use [react-with-styles][react-with-styles] with [Aphrodite][aphrodite]. 12 | 13 | [package-url]: https://npmjs.org/package/react-with-styles-interface-aphrodite 14 | [npm-version-svg]: http://versionbadg.es/airbnb/react-with-styles-interface-aphrodite.svg 15 | [travis-svg]: https://travis-ci.org/airbnb/react-with-styles-interface-aphrodite.svg 16 | [travis-url]: https://travis-ci.org/airbnb/react-with-styles-interface-aphrodite 17 | [deps-svg]: https://david-dm.org/airbnb/react-with-styles-interface-aphrodite.svg 18 | [deps-url]: https://david-dm.org/airbnb/react-with-styles-interface-aphrodite 19 | [dev-deps-svg]: https://david-dm.org/airbnb/react-with-styles-interface-aphrodite/dev-status.svg 20 | [dev-deps-url]: https://david-dm.org/airbnb/react-with-styles-interface-aphrodite#info=devDependencies 21 | [npm-badge-png]: https://nodei.co/npm/react-with-styles-interface-aphrodite.png?downloads=true&stars=true 22 | [license-image]: http://img.shields.io/npm/l/react-with-styles-interface-aphrodite.svg 23 | [license-url]: LICENSE 24 | [downloads-image]: http://img.shields.io/npm/dm/react-with-styles-interface-aphrodite.svg 25 | [downloads-url]: http://npm-stat.com/charts.html?package=react-with-styles-interface-aphrodite 26 | 27 | [react-with-styles]: https://github.com/airbnb/react-with-styles 28 | [aphrodite]: https://github.com/khan/aphrodite 29 | 30 | ## Import 31 | 32 | ```js 33 | import aphroditeInterface from 'react-with-styles-interface-aphrodite'; 34 | ``` 35 | 36 | or when you need to [disable `!important`](https://github.com/Khan/aphrodite#disabling-important): 37 | 38 | ```js 39 | import aphroditeInterface from 'react-with-styles-interface-aphrodite/no-important'; 40 | ``` 41 | 42 | ## Built-in RTL support 43 | 44 | `react-with-styles-interface-aphrodite` has built-in LTR/RTL context support. Specifically, it uses [rtl-css-js](https://github.com/kentcdodds/rtl-css-js) to automatically flip styles (`margin`, `padding`, `float`, `textAlign`, etc.) that were written for an LTR page when your app is wrapped in [react-with-direction](https://github.com/airbnb/react-with-direction)'s `DirectionProvider` with direction set to `DIRECTIONS.RTL`. 45 | 46 | It accomplishes this by providing a directional `create` and `resolve` method. `react-with-styles` automatically uses the correct create method based on the direction value set in context and then passes down the appropriate `resolve` method as a prop named `css`. 47 | 48 | For instance, if you were to write your styles as follows: 49 | 50 | ```jsx 51 | import ThemedStyleSheet from 'react-with-styles/lib/ThemedStyleSheet'; 52 | import aphroditeInterface from 'react-with-styles-interface-aphrodite'; 53 | import { withStyles, css } from 'react-with-styles'; 54 | 55 | ThemedStyleSheet.registerInterface(aphroditeInterface); 56 | 57 | ... 58 | 59 | function MyComponent({ css }) { 60 | return
Hello World
; 61 | } 62 | 63 | export default withStyles(() => ({ 64 | container: { 65 | background: '#fff', 66 | float: 'left', 67 | }, 68 | }))(MyComponent); 69 | ``` 70 | 71 | The generated css for an app where you set `` at the top would look like: 72 | 73 | ```css 74 | .container_r5r4of { 75 | background: #fff !important; 76 | float: 'left' !important; 77 | } 78 | ``` 79 | 80 | whereas if you had set ``, the generated css would be: 81 | ```css 82 | .container_kui6s4 { 83 | background: #fff !important; 84 | float: 'right' !important; 85 | } 86 | ``` 87 | 88 | If you used an inline style instead: 89 | 90 | ```jsx 91 | import { css } from 'react-with-styles'; 92 | 93 | export default function MyComponent() { 94 | return
Hello World
; 95 | } 96 | ``` 97 | 98 | In the case where `` is wrapping your component, this would map to a `style={{ background: '#fff', float: 'left' }}` on the div in question. If `` is present instead, this would simply apply `style={{ background: '#fff', float: 'right' }}` to the div. 99 | -------------------------------------------------------------------------------- /no-important.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/no-important.js'); // eslint-disable-line import/no-unresolved 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-with-styles-interface-aphrodite", 3 | "version": "6.0.1", 4 | "description": "react-with-styles interface for Aphrodite", 5 | "main": "lib/aphroditeInterface.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "prebuild": "npm run clean", 11 | "build": "babel src -d lib", 12 | "check-changelog": "expr $(git status --porcelain 2>/dev/null| grep \"^\\s*M.*CHANGELOG.md\" | wc -l) >/dev/null || (echo 'Please edit CHANGELOG.md' && exit 1)", 13 | "check-only-changelog-changed": "(expr $(git status --porcelain 2>/dev/null| grep -v \"CHANGELOG.md\" | wc -l) >/dev/null && echo 'Only CHANGELOG.md may have uncommitted changes' && exit 1) || exit 0", 14 | "clean": "rimraf lib", 15 | "lint": "eslint .", 16 | "mocha": "mocha", 17 | "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag && git push && git push --tags && npm publish", 18 | "prepublish": "in-publish && safe-publish-latest && npm run build || not-in-publish", 19 | "pretest": "npm run --silent lint", 20 | "preversion": "npm run test && npm run check-changelog && npm run check-only-changelog-changed", 21 | "tag": "git tag v$npm_package_version", 22 | "test": "npm run tests-only", 23 | "tests-only": "npm run --silent mocha test", 24 | "version:major": "npm --no-git-tag-version version major", 25 | "version:minor": "npm --no-git-tag-version version minor", 26 | "version:patch": "npm --no-git-tag-version version patch" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/airbnb/react-with-styles-interface-aphrodite.git" 31 | }, 32 | "keywords": [ 33 | "react-with-styles", 34 | "aphrodite" 35 | ], 36 | "author": "Joe Lencioni ", 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/airbnb/react-with-styles-interface-aphrodite/issues" 40 | }, 41 | "homepage": "https://github.com/airbnb/react-with-styles-interface-aphrodite#readme", 42 | "devDependencies": { 43 | "@babel/cli": "^7.5.5", 44 | "@babel/core": "^7.5.5", 45 | "@babel/register": "^7.5.5", 46 | "airbnb-js-shims": "^2.2.0", 47 | "aphrodite": "^2.3.1", 48 | "babel-plugin-transform-replace-object-assign": "^2.0.0", 49 | "babel-preset-airbnb": "^4.0.1", 50 | "chai": "^4.2.0", 51 | "eslint": "^5.16.0", 52 | "eslint-config-airbnb": "^17.1.1", 53 | "eslint-plugin-import": "^2.18.2", 54 | "eslint-plugin-jsx-a11y": "^6.2.3", 55 | "eslint-plugin-react": "^7.14.3", 56 | "in-publish": "^2.0.0", 57 | "mocha": "^5.2.0", 58 | "prop-types": "^15.7.2", 59 | "react": "^16.8.6", 60 | "react-dom": "^16.8.6", 61 | "rimraf": "^2.6.3", 62 | "safe-publish-latest": "^1.1.2", 63 | "sinon": "^6.3.5", 64 | "sinon-sandbox": "^2.0.5" 65 | }, 66 | "peerDependencies": { 67 | "aphrodite": "^2.1.0", 68 | "react-with-styles": "^3.0.0 || ^4.0.0" 69 | }, 70 | "dependencies": { 71 | "array.prototype.flat": "^1.2.1", 72 | "has": "^1.0.3", 73 | "object.assign": "^4.1.0", 74 | "object.entries": "^1.1.0", 75 | "rtl-css-js": "^1.13.1" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/aphroditeInterface.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, css, flushToStyleTag } from 'aphrodite'; 2 | import aphroditeInterfaceFactory from './aphroditeInterfaceFactory'; 3 | 4 | export default aphroditeInterfaceFactory({ 5 | StyleSheet, 6 | css, 7 | flushToStyleTag, 8 | }); 9 | -------------------------------------------------------------------------------- /src/aphroditeInterfaceFactory.js: -------------------------------------------------------------------------------- 1 | import rtlCSSJS from 'rtl-css-js'; 2 | import entries from 'object.entries'; 3 | 4 | import resolveLTR from './utils/resolveLTR'; 5 | import resolveRTL from './utils/resolveRTL'; 6 | 7 | export default ({ StyleSheet, css, flushToStyleTag }/* aphrodite */) => ({ 8 | create(styleHash) { 9 | return StyleSheet.create(styleHash); 10 | }, 11 | 12 | createLTR(styleHash) { 13 | return StyleSheet.create(styleHash); 14 | }, 15 | 16 | createRTL(styleHash) { 17 | const styleHashRTL = {}; 18 | entries(styleHash).forEach(([styleKey, styleDef]) => { 19 | styleHashRTL[styleKey] = rtlCSSJS(styleDef); 20 | }); 21 | 22 | return StyleSheet.create(styleHashRTL); 23 | }, 24 | 25 | resolve(styles) { 26 | return resolveLTR(css, styles); 27 | }, 28 | 29 | resolveLTR(styles) { 30 | return resolveLTR(css, styles); 31 | }, 32 | 33 | resolveRTL(styles) { 34 | return resolveRTL(css, styles); 35 | }, 36 | 37 | // Flushes all buffered styles to a style tag. Required for components 38 | // that depend upon previous styles in the component tree (i.e. 39 | // for calculating container width, including padding/margin). 40 | flush() { 41 | flushToStyleTag(); 42 | }, 43 | }); 44 | -------------------------------------------------------------------------------- /src/no-important.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, css, flushToStyleTag } from 'aphrodite/no-important'; 2 | import aphroditeInterfaceFactory from './aphroditeInterfaceFactory'; 3 | 4 | export default aphroditeInterfaceFactory({ 5 | StyleSheet, 6 | css, 7 | flushToStyleTag, 8 | }); 9 | -------------------------------------------------------------------------------- /src/utils/resolveLTR.js: -------------------------------------------------------------------------------- 1 | import flat from 'array.prototype.flat'; 2 | 3 | import separateStyles from './separateStyles'; 4 | 5 | // Styles is an array of properties returned by `create()`, a POJO, or an 6 | // array thereof. POJOs are treated as inline styles. This version of the 7 | // resolve function explicitly does no work to flip styles for an RTL context. 8 | // This function returns an object to be spread onto an element. 9 | export default function resolveLTR(css, styles) { 10 | const flattenedStyles = flat(styles, Infinity); 11 | 12 | const { 13 | aphroditeStyles, 14 | hasInlineStyles, 15 | inlineStyles, 16 | } = separateStyles(flattenedStyles); 17 | 18 | const result = {}; 19 | if (aphroditeStyles.length > 0) { 20 | result.className = css(...aphroditeStyles); 21 | } 22 | 23 | if (hasInlineStyles) { 24 | result.style = inlineStyles; 25 | } 26 | 27 | return result; 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/resolveRTL.js: -------------------------------------------------------------------------------- 1 | import flat from 'array.prototype.flat'; 2 | import rtlCSSJS from 'rtl-css-js'; 3 | 4 | import separateStyles from './separateStyles'; 5 | 6 | // Styles is an array of properties returned by `create()`, a POJO, or an 7 | // array thereof. POJOs are treated as inline styles. This version of the 8 | // resolve function explicitly flips inline styles for an RTL context. 9 | // This function returns an object to be spread onto an element. 10 | export default function resolveRTL(css, styles) { 11 | const flattenedStyles = flat(styles, Infinity); 12 | 13 | const { 14 | aphroditeStyles, 15 | hasInlineStyles, 16 | inlineStyles, 17 | } = separateStyles(flattenedStyles); 18 | 19 | const result = {}; 20 | if (aphroditeStyles.length > 0) { 21 | result.className = css(...aphroditeStyles); 22 | } 23 | 24 | if (hasInlineStyles) { 25 | result.style = rtlCSSJS(inlineStyles); 26 | } 27 | 28 | return result; 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/separateStyles.js: -------------------------------------------------------------------------------- 1 | import has from 'has'; 2 | 3 | // This function takes the array of styles and separates them into styles that 4 | // are handled by Aphrodite and inline styles. 5 | export default function separateStyles(stylesArray) { 6 | const aphroditeStyles = []; 7 | 8 | // Since determining if an Object is empty requires collecting all of its 9 | // keys, and we want the best performance in this code because we are in the 10 | // render path, we are going to do a little bookkeeping ourselves. 11 | let hasInlineStyles = false; 12 | const inlineStyles = {}; 13 | 14 | // This is run on potentially every node in the tree when rendering, where 15 | // performance is critical. Normally we would prefer using `forEach`, but 16 | // old-fashioned for loops are faster so that's what we have chosen here. 17 | for (let i = 0; i < stylesArray.length; i += 1) { 18 | const style = stylesArray[i]; 19 | 20 | // If this style is falsey, we just want to disregard it. This allows for 21 | // syntax like: 22 | // 23 | // css(isFoo && styles.foo) 24 | if (style) { 25 | if (has(style, '_name') && has(style, '_definition')) { 26 | aphroditeStyles.push(style); 27 | } else { 28 | Object.assign(inlineStyles, style); 29 | hasInlineStyles = true; 30 | } 31 | } 32 | } 33 | 34 | return { 35 | aphroditeStyles, 36 | hasInlineStyles, 37 | inlineStyles, 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "rules": { 6 | "import/no-extraneous-dependencies": [2, { 7 | "devDependencies": true 8 | }] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/aphroditeInterfaceFactory_test.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import sinon from 'sinon-sandbox'; 3 | import { 4 | StyleSheet, 5 | css, 6 | flushToStyleTag, 7 | StyleSheetTestUtils, 8 | } from 'aphrodite'; 9 | import aphroditeInterfaceFactory from '../src/aphroditeInterfaceFactory'; 10 | 11 | import * as resolveLTR from '../src/utils/resolveLTR'; 12 | import * as resolveRTL from '../src/utils/resolveRTL'; 13 | 14 | describe('aphroditeInterfaceFactory', () => { 15 | const aphroditeInterface = aphroditeInterfaceFactory({ 16 | StyleSheet, 17 | css, 18 | flushToStyleTag, 19 | }); 20 | 21 | let resolveLTRSpy; 22 | let resolveRTLSpy; 23 | 24 | beforeEach(() => { 25 | StyleSheetTestUtils.suppressStyleInjection(); 26 | resolveLTRSpy = sinon.spy(resolveLTR, 'default'); 27 | resolveRTLSpy = sinon.spy(resolveRTL, 'default'); 28 | }); 29 | 30 | afterEach(() => { 31 | StyleSheetTestUtils.clearBufferAndResumeStyleInjection(); 32 | sinon.restore(); 33 | }); 34 | 35 | describe('.create()', () => { 36 | it('processes the styles with Aphrodite', () => { 37 | expect(aphroditeInterface.create({ 38 | foo: { 39 | color: 'red', 40 | }, 41 | })).to.eql({ 42 | foo: { 43 | _definition: { 44 | color: 'red', 45 | }, 46 | _len: 15, 47 | _name: 'foo_137u7ef', 48 | }, 49 | }); 50 | }); 51 | }); 52 | 53 | describe('.createLTR()', () => { 54 | it('processes the styles with Aphrodite', () => { 55 | expect(aphroditeInterface.createLTR({ 56 | foo: { 57 | left: 10, 58 | }, 59 | })).to.eql({ 60 | foo: { 61 | _definition: { 62 | left: 10, 63 | }, 64 | _len: 11, 65 | _name: 'foo_lg4jz7', 66 | }, 67 | }); 68 | }); 69 | }); 70 | 71 | describe('.createRTL()', () => { 72 | it('processes the styles with Aphrodite', () => { 73 | expect(aphroditeInterface.createRTL({ 74 | foo: { 75 | left: 10, 76 | }, 77 | })).to.eql({ 78 | foo: { 79 | _definition: { 80 | right: 10, 81 | }, 82 | _len: 12, 83 | _name: 'foo_6eoou0', 84 | }, 85 | }); 86 | }); 87 | }); 88 | 89 | describe('.resolve()', () => { 90 | it('calls resolveLTR method', () => { 91 | aphroditeInterface.resolve([]); 92 | expect(resolveLTRSpy.callCount).to.equal(1); 93 | }); 94 | }); 95 | 96 | describe('.resolveLTR()', () => { 97 | it('calls resolveLTR method', () => { 98 | aphroditeInterface.resolveLTR([]); 99 | expect(resolveLTRSpy.callCount).to.equal(1); 100 | }); 101 | }); 102 | 103 | describe('.resolveRTL()', () => { 104 | it('calls resolveRTL method', () => { 105 | aphroditeInterface.resolveRTL([]); 106 | expect(resolveRTLSpy.callCount).to.equal(1); 107 | }); 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /test/aphroditeInterface_test.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import React from 'react'; 3 | import { css, StyleSheetServer, StyleSheetTestUtils } from 'aphrodite'; 4 | import ReactDOMServer from 'react-dom/server'; 5 | import aphroditeInterface from '../src/aphroditeInterface'; 6 | 7 | describe('aphroditeInterface', () => { 8 | beforeEach(() => { 9 | StyleSheetTestUtils.suppressStyleInjection(); 10 | }); 11 | 12 | afterEach(() => { 13 | StyleSheetTestUtils.clearBufferAndResumeStyleInjection(); 14 | }); 15 | 16 | it('is an interface', () => { 17 | expect(typeof aphroditeInterface.create).to.equal('function'); 18 | expect(typeof aphroditeInterface.createLTR).to.equal('function'); 19 | expect(typeof aphroditeInterface.createRTL).to.equal('function'); 20 | expect(typeof aphroditeInterface.resolve).to.equal('function'); 21 | expect(typeof aphroditeInterface.resolveLTR).to.equal('function'); 22 | expect(typeof aphroditeInterface.resolveRTL).to.equal('function'); 23 | }); 24 | 25 | it('uses !important', () => { 26 | const styles = aphroditeInterface.create({ 27 | foo: { 28 | color: 'red', 29 | }, 30 | }); 31 | const result = StyleSheetServer.renderStatic(() => ( 32 | ReactDOMServer.renderToString(React.createElement('div', { className: css(styles.foo) })) 33 | )); 34 | expect(result.css.content.includes('!important')).to.equal(true); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require @babel/register test/**/*.{js,jsx} 2 | --require airbnb-js-shims 3 | --recursive 4 | -------------------------------------------------------------------------------- /test/no-important_test.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import React from 'react'; 3 | import { css, StyleSheetServer, StyleSheetTestUtils } from 'aphrodite/no-important'; 4 | import ReactDOMServer from 'react-dom/server'; 5 | import aphroditeInterface from '../src/no-important'; 6 | 7 | describe('no-important', () => { 8 | beforeEach(() => { 9 | StyleSheetTestUtils.suppressStyleInjection(); 10 | }); 11 | 12 | afterEach(() => { 13 | StyleSheetTestUtils.clearBufferAndResumeStyleInjection(); 14 | }); 15 | 16 | it('is an interface', () => { 17 | expect(typeof aphroditeInterface.create).to.equal('function'); 18 | expect(typeof aphroditeInterface.createLTR).to.equal('function'); 19 | expect(typeof aphroditeInterface.createRTL).to.equal('function'); 20 | expect(typeof aphroditeInterface.resolve).to.equal('function'); 21 | expect(typeof aphroditeInterface.resolveLTR).to.equal('function'); 22 | expect(typeof aphroditeInterface.resolveRTL).to.equal('function'); 23 | }); 24 | 25 | it('does not use !important', () => { 26 | const styles = aphroditeInterface.create({ 27 | foo: { 28 | color: 'red', 29 | }, 30 | }); 31 | const result = StyleSheetServer.renderStatic(() => ( 32 | ReactDOMServer.renderToString(React.createElement('div', { className: css(styles.foo) })) 33 | )); 34 | expect(result.css.content.includes('!important')).to.equal(false); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/utils/resolveLTR_test.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { StyleSheetTestUtils, StyleSheet, css } from 'aphrodite'; 3 | 4 | import resolveLTR from '../../src/utils/resolveLTR'; 5 | 6 | describe('#resolveLTR', () => { 7 | beforeEach(() => { 8 | StyleSheetTestUtils.suppressStyleInjection(); 9 | }); 10 | 11 | afterEach(() => { 12 | StyleSheetTestUtils.clearBufferAndResumeStyleInjection(); 13 | }); 14 | 15 | it('turns a processed style into a className', () => { 16 | const styles = StyleSheet.create({ 17 | foo: { 18 | color: 'red', 19 | }, 20 | }); 21 | 22 | expect(resolveLTR(css, [styles.foo])) 23 | .to.eql({ className: 'foo_137u7ef' }); 24 | }); 25 | 26 | it('turns multiple processed styles into a className', () => { 27 | const styles = StyleSheet.create({ 28 | foo: { 29 | color: 'red', 30 | }, 31 | 32 | bar: { 33 | display: 'inline-block', 34 | }, 35 | }); 36 | 37 | expect(resolveLTR(css, [styles.foo, styles.bar])) 38 | .to.eql({ className: 'foo_137u7ef-o_O-bar_36rlri' }); 39 | }); 40 | 41 | it('handles an object with inline styles', () => { 42 | const style = { 43 | color: 'red', 44 | marginLeft: 10, 45 | }; 46 | 47 | expect(resolveLTR(css, [style])) 48 | .to.eql({ 49 | style: { 50 | color: 'red', 51 | marginLeft: 10, 52 | }, 53 | }); 54 | }); 55 | 56 | it('handles multiple objects with inline styles', () => { 57 | const styleA = { 58 | color: 'red', 59 | }; 60 | 61 | const styleB = { 62 | display: 'inline-block', 63 | }; 64 | 65 | expect(resolveLTR(css, [styleA, styleB])) 66 | .to.eql({ 67 | style: { 68 | color: 'red', 69 | display: 'inline-block', 70 | }, 71 | }); 72 | }); 73 | 74 | it('prefers inline styles from later arguments', () => { 75 | const styleA = { 76 | color: 'red', 77 | }; 78 | 79 | const styleB = { 80 | color: 'blue', 81 | }; 82 | 83 | expect(resolveLTR(css, [styleA, styleB])) 84 | .to.eql({ 85 | style: { 86 | color: 'blue', 87 | }, 88 | }); 89 | }); 90 | 91 | it('handles a mix of Aphrodite and inline styles', () => { 92 | const styles = StyleSheet.create({ 93 | foo: { 94 | color: 'red', 95 | }, 96 | }); 97 | 98 | const style = { 99 | display: 'inline-block', 100 | }; 101 | 102 | expect(resolveLTR(css, [styles.foo, style])) 103 | .to.eql({ 104 | className: 'foo_137u7ef', 105 | style: { 106 | display: 'inline-block', 107 | }, 108 | }); 109 | }); 110 | 111 | it('handles nested arrays', () => { 112 | const styles = StyleSheet.create({ 113 | foo: { 114 | color: 'red', 115 | }, 116 | }); 117 | 118 | const styleA = { 119 | display: 'inline-block', 120 | }; 121 | 122 | const styleB = { 123 | padding: 1, 124 | }; 125 | 126 | expect(resolveLTR(css, [[styles.foo], [[styleA, styleB]]])) 127 | .to.eql({ 128 | className: 'foo_137u7ef', 129 | style: { 130 | display: 'inline-block', 131 | padding: 1, 132 | }, 133 | }); 134 | }); 135 | 136 | it('handles multiple calls to the same style definition', () => { 137 | const styles = StyleSheet.create({ 138 | container: { 139 | textAlign: 'left', 140 | }, 141 | }); 142 | 143 | resolveLTR(css, [styles.container]); 144 | const definition = { ...styles.container._definition }; 145 | 146 | resolveLTR(css, [styles.container]); 147 | const newDefinition = styles.container._definition; 148 | expect(definition).to.eql(newDefinition); 149 | }); 150 | }); 151 | -------------------------------------------------------------------------------- /test/utils/resolveRTL_test.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { StyleSheetTestUtils, StyleSheet, css } from 'aphrodite'; 3 | 4 | import resolveRTL from '../../src/utils/resolveRTL'; 5 | 6 | describe('#resolveRTL', () => { 7 | beforeEach(() => { 8 | StyleSheetTestUtils.suppressStyleInjection(); 9 | }); 10 | 11 | afterEach(() => { 12 | StyleSheetTestUtils.clearBufferAndResumeStyleInjection(); 13 | }); 14 | 15 | it('turns a processed style into a className', () => { 16 | const styles = StyleSheet.create({ 17 | foo: { 18 | color: 'red', 19 | }, 20 | }); 21 | 22 | expect(resolveRTL(css, [styles.foo])) 23 | .to.eql({ className: 'foo_137u7ef' }); 24 | }); 25 | 26 | it('turns multiple processed styles into a className', () => { 27 | const styles = StyleSheet.create({ 28 | foo: { 29 | color: 'red', 30 | }, 31 | 32 | bar: { 33 | display: 'inline-block', 34 | }, 35 | }); 36 | 37 | expect(resolveRTL(css, [styles.foo, styles.bar])) 38 | .to.eql({ className: 'foo_137u7ef-o_O-bar_36rlri' }); 39 | }); 40 | 41 | it('handles an object with inline styles', () => { 42 | const style = { 43 | color: 'red', 44 | }; 45 | 46 | expect(resolveRTL(css, [style])) 47 | .to.eql({ 48 | style: { 49 | color: 'red', 50 | }, 51 | }); 52 | }); 53 | 54 | it('flips inline styles', () => { 55 | const style = { 56 | marginLeft: 10, 57 | }; 58 | 59 | expect(resolveRTL(css, [style])).to.eql({ style: { marginRight: 10 } }); 60 | }); 61 | 62 | it('handles multiple objects with inline styles', () => { 63 | const styleA = { 64 | color: 'red', 65 | }; 66 | 67 | const styleB = { 68 | display: 'inline-block', 69 | }; 70 | 71 | expect(resolveRTL(css, [styleA, styleB])) 72 | .to.eql({ 73 | style: { 74 | color: 'red', 75 | display: 'inline-block', 76 | }, 77 | }); 78 | }); 79 | 80 | it('prefers inline styles from later arguments', () => { 81 | const styleA = { 82 | color: 'red', 83 | }; 84 | 85 | const styleB = { 86 | color: 'blue', 87 | }; 88 | 89 | expect(resolveRTL(css, [styleA, styleB])) 90 | .to.eql({ 91 | style: { 92 | color: 'blue', 93 | }, 94 | }); 95 | }); 96 | 97 | it('handles a mix of Aphrodite and inline styles', () => { 98 | const styles = StyleSheet.create({ 99 | foo: { 100 | color: 'red', 101 | }, 102 | }); 103 | 104 | const style = { 105 | display: 'inline-block', 106 | }; 107 | 108 | expect(resolveRTL(css, [styles.foo, style])) 109 | .to.eql({ 110 | className: 'foo_137u7ef', 111 | style: { 112 | display: 'inline-block', 113 | }, 114 | }); 115 | }); 116 | 117 | it('handles nested arrays', () => { 118 | const styles = StyleSheet.create({ 119 | foo: { 120 | color: 'red', 121 | }, 122 | }); 123 | 124 | const styleA = { 125 | display: 'inline-block', 126 | }; 127 | 128 | const styleB = { 129 | padding: 1, 130 | }; 131 | 132 | expect(resolveRTL(css, [[styles.foo], [[styleA, styleB]]])) 133 | .to.eql({ 134 | className: 'foo_137u7ef', 135 | style: { 136 | display: 'inline-block', 137 | padding: 1, 138 | }, 139 | }); 140 | }); 141 | 142 | it('handles multiple calls to the same style definition', () => { 143 | const styles = StyleSheet.create({ 144 | container: { 145 | textAlign: 'left', 146 | }, 147 | }); 148 | 149 | resolveRTL(css, [styles.container]); 150 | const definition = { ...styles.container._definition }; 151 | 152 | resolveRTL(css, [styles.container]); 153 | const newDefinition = styles.container._definition; 154 | expect(definition).to.eql(newDefinition); 155 | }); 156 | }); 157 | --------------------------------------------------------------------------------