├── .all-contributorsrc ├── .eslintignore ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmrc ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── examples ├── README.md └── with-jest │ ├── .babelrc │ ├── README.md │ ├── __snapshots__ │ └── index.test.js.snap │ ├── index.js │ ├── index.test.js │ ├── package.json │ ├── testSetup.js │ └── yarn.lock ├── jest.config.js ├── other ├── EMOTION_MIGRATION.md ├── FAQ.md ├── MAINTAINING.md ├── USERS.md ├── __tests__ │ ├── dist-preact.js │ ├── dist-react.js │ └── helpers │ │ └── prop-types.js ├── codemods │ ├── README.md │ ├── __snapshots__ │ │ └── theme-move.test.js.snap │ ├── package.json │ ├── theme-move.js │ └── theme-move.test.js ├── get-exports-code.js ├── glamorous-walkthrough.png ├── jest.config.js ├── logo │ ├── compact.ai │ ├── compact.png │ ├── full.ai │ └── full.png ├── manual-releases.md ├── patch-preact-package-json.js ├── setup-tests.js └── swag │ ├── shirt-dark.png │ ├── shirt-light.png │ ├── sticker-hex.png │ └── sticker-square.png ├── package.json ├── rollup.config.js ├── src ├── .eslintrc ├── __mocks__ │ └── constants.js ├── __tests__ │ ├── .eslintrc │ ├── __snapshots__ │ │ ├── create-glamorous.with-component.js.snap │ │ ├── dev.js.snap │ │ ├── index.js.snap │ │ ├── index.with-props.js.snap │ │ ├── theme-provider.js.snap │ │ ├── tiny.js.snap │ │ ├── typescript.js.snap │ │ └── with-theme.js.snap │ ├── create-glamorous.with-component.js │ ├── dev.js │ ├── index.filter-props.js │ ├── index.js │ ├── index.should-class-name-update.js │ ├── index.with-config.js │ ├── index.with-props.js │ ├── preact.js │ ├── refs.js │ ├── should-forward-property.js │ ├── theme-provider.js │ ├── tiny.js │ ├── typescript.js │ └── with-theme.js ├── cjs-entry.js ├── constants.js ├── create-glamorous.js ├── dom-elements.js ├── get-glamor-classname.js ├── index.js ├── react-compat.js ├── react-props.js ├── should-forward-property.js ├── split-props.js ├── theme-provider.js ├── tiny.js ├── umd-entry.js └── with-theme.js ├── test ├── glamorous-exports.test.ts ├── glamorous.test.tsx ├── preact │ ├── glamorous.test.tsx │ └── should-fail.test.tsx └── should-fail.test.tsx ├── tsconfig.base.json ├── tsconfig.json ├── tsconfig.preact.json ├── tsconfig.react.json ├── tslint.json └── typings ├── built-in-component-factories.d.ts ├── built-in-glamorous-components.d.ts ├── component-factory.d.ts ├── css-properties.d.ts ├── dom-tag-component-factory.d.ts ├── glamorous-component.d.ts ├── glamorous.d.ts ├── helpers.d.ts ├── named-built-in-glamorous-components.d.ts ├── preact ├── built-in-component-factories.d.ts ├── built-in-glamorous-components.d.ts ├── component-factory.d.ts ├── css-properties.d.ts ├── dom-tag-component-factory.d.ts ├── glamorous-component.d.ts ├── glamorous.d.ts ├── helpers.d.ts ├── named-built-in-glamorous-components.d.ts ├── style-arguments.d.ts └── svg-properties.d.ts ├── style-arguments.d.ts └── svg-properties.d.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist 4 | examples -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | README.md merge=union 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # This project is now in an unmaintained status. Please see the README for more information. 2 | 3 | Issues will be closed. Only very serious bugs will be fixed. 4 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # This project is now in an unmaintained status. Please see the README for more information. 2 | 3 | Only minimal changes will be merged. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist 4 | .opt-in 5 | .opt-out 6 | test-ts 7 | *.log 8 | yarn.lock 9 | package-lock.json 10 | .vscode 11 | /preact/ 12 | .eslintcache 13 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=http://registry.npmjs.org/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - ~/.npm 6 | notifications: 7 | email: false 8 | node_js: '8' 9 | script: npm run validate 10 | after_success: kcd-scripts travis-after-success 11 | branches: 12 | only: master 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | The changelog is automatically updated using [semantic-release](https://github.com/semantic-release/semantic-release). 4 | You can see it on the [releases page](../../releases). 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at kent+coc@doddsfamily.us. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for being willing to contribute! 4 | 5 | **Working on your first Pull Request?** You can learn how from this _free_ series 6 | [How to Contribute to an Open Source Project on GitHub][egghead] 7 | 8 | ## Project setup 9 | 10 | 1. Fork and clone the repo 11 | 2. `npm run setup --silent` to setup the project (it installs deps and runs the validate script) 12 | 3. Create a branch for your PR 13 | 14 | > Tip: Keep your `master` branch pointing at the original repository and make 15 | > pull requests from branches on your fork. To do this, run: 16 | > 17 | > ``` 18 | > git remote add upstream https://github.com/paypal/glamorous.git 19 | > git fetch upstream 20 | > git branch --set-upstream-to=upstream/master master 21 | > ``` 22 | > 23 | > This will add the original repository as a "remote" called "upstream," 24 | > Then fetch the git information from that remote, then set your local `master` 25 | > branch to use the upstream master branch whenever you run `git pull`. 26 | > Then you can make all of your pull request branches based on this `master` 27 | > branch. Whenever you want to update your version of `master`, do a regular 28 | > `git pull`. 29 | 30 | ## Add yourself as a contributor 31 | 32 | This project follows the [all contributors][all-contributors] specification. 33 | To add yourself to the table of contributors on the `README.md`, please use the 34 | automated script as part of your PR: 35 | 36 | ```console 37 | npm run add-contributor 38 | ``` 39 | 40 | Follow the prompt and commit `.all-contributorsrc` and `README.md` in the PR. 41 | If you've already added yourself to the list and are making 42 | a new type of contribution, you can run it again and select the added 43 | contribution type. 44 | 45 | ## Committing and Pushing changes 46 | 47 | Please make sure to run the tests before you commit your changes. You can run 48 | `npm run test:update` which will update any snapshots that need updating. 49 | Make sure to include those changes (if they exist) in your commit. 50 | 51 | ### opt into git hooks 52 | 53 | There are git hooks set up with this project that are automatically installed 54 | when you install dependencies. They're really handy, but are turned off by 55 | default (so as to not hinder new contributors). You can opt into these by 56 | creating a file called `.opt-in` at the root of the project and putting this 57 | inside: 58 | 59 | ``` 60 | pre-commit 61 | ``` 62 | 63 | ## Help needed 64 | 65 | Please checkout the [the open issues][issues] 66 | 67 | Also, please watch the repo and respond to questions/bug reports/feature 68 | requests! Thanks! 69 | 70 | [egghead]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github 71 | [all-contributors]: https://github.com/kentcdodds/all-contributors 72 | [issues]: https://github.com/paypal/glamorous/issues 73 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2017 PayPal 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /examples/with-jest/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/with-jest/README.md: -------------------------------------------------------------------------------- 1 | ## Example for `glamorous` with `jest` and `enzyme` 2 | 3 | ### Steps to run 4 | 5 | ```bash 6 | git clone https://github.com/paypal/glamorous.git 7 | cd glamorous/examples/with-jest 8 | npm install 9 | npm run test 10 | ``` 11 | 12 | ### Jest tests 13 | 14 | #### Snapshot 15 | 16 | Relative to [Jest documentation][jest-snapshot-documentation], you don't need to create snapshot manually. 17 | 18 | ### Outputting CSS to snapshots 19 | 20 | If you are using Jest and Glamorous, you should also consider using [jest-glamor-react] so that your CSS gets outputted to snapshots and not just a hashed class. 21 | 22 | 23 | #### Create react app 24 | 25 | Using [Create react app][cra-repository], you won't be able to set `setupTestFrameworkScriptFile` configuration for Jest. 26 | 27 | See [how to][cra-test-environment] use `testSetup.js` file in the CRA test environment. 28 | 29 | [cra-repository]: https://github.com/facebookincubator/create-react-app 30 | [cra-test-environment]: https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#initializing-test-environment 31 | [jest-snapshot-documentation]: https://facebook.github.io/jest/docs/en/snapshot-testing.html 32 | [jest-glamor-react]: https://github.com/kentcdodds/jest-glamor-react 33 | -------------------------------------------------------------------------------- /examples/with-jest/__snapshots__/index.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`enzyme.mount 1`] = ` 4 | .css-1otybxc, 5 | [data-css-1otybxc] { 6 | padding: 4em; 7 | background: papayawhip; 8 | } 9 | 10 | .css-1tnuino, 11 | [data-css-1tnuino] { 12 | font-size: 1.5em; 13 | text-align: center; 14 | color: palevioletred; 15 | } 16 | 17 | 18 |
21 | 22 |

25 | Hello World, this is my first glamor styled component! 26 |

27 |
28 |
29 |
30 | `; 31 | 32 | exports[`enzyme.render 1`] = ` 33 | .css-1otybxc, 34 | [data-css-1otybxc] { 35 | padding: 4em; 36 | background: papayawhip; 37 | } 38 | 39 | .css-1tnuino, 40 | [data-css-1tnuino] { 41 | font-size: 1.5em; 42 | text-align: center; 43 | color: palevioletred; 44 | } 45 | 46 |
49 |

52 | Hello World, this is my first glamor styled component! 53 |

54 |
55 | `; 56 | 57 | exports[`enzyme.shallow 1`] = ` 58 | .css-1otybxc, 59 | [data-css-1otybxc] { 60 | padding: 4em; 61 | background: papayawhip; 62 | } 63 | 64 |
67 | 68 | Hello World, this is my first glamor styled component! 69 | 70 |
71 | `; 72 | -------------------------------------------------------------------------------- /examples/with-jest/index.js: -------------------------------------------------------------------------------- 1 | import glamorous from 'glamorous' 2 | 3 | export const Wrapper = glamorous.section({ 4 | padding: '4em', 5 | background: 'papayawhip', 6 | }) 7 | 8 | export const Title = glamorous.h1({ 9 | fontSize: '1.5em', 10 | textAlign: 'center', 11 | color: 'palevioletred', 12 | }) 13 | -------------------------------------------------------------------------------- /examples/with-jest/index.test.js: -------------------------------------------------------------------------------- 1 | import {matcher, serializer} from 'jest-glamor-react' 2 | import React from 'react' 3 | import {shallow, render, mount} from 'enzyme' 4 | import {Wrapper, Title} from './index' 5 | 6 | test('enzyme', () => { 7 | const ui = ( 8 | 9 | Hello World, this is my first glamor styled component! 10 | 11 | ) 12 | 13 | expect(shallow(ui)).toMatchSnapshot(`enzyme.shallow`) 14 | expect(mount(ui)).toMatchSnapshot(`enzyme.mount`) 15 | expect(render(ui)).toMatchSnapshot(`enzyme.render`) 16 | }) 17 | -------------------------------------------------------------------------------- /examples/with-jest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "glamorous-jest-react", 3 | "version": "1.0.0", 4 | "description": "Example to test glamorous with jest", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --coverage" 8 | }, 9 | "keywords": [ 10 | "glamorous", 11 | "jest", 12 | "react" 13 | ], 14 | "author": "pksjce", 15 | "license": "ISC", 16 | "dependencies": { 17 | "enzyme": "^2.9.1", 18 | "glamorous": "^1.0.1", 19 | "jest-glamor-react": "^1.4.0", 20 | "react": "^15.6.2" 21 | }, 22 | "devDependencies": { 23 | "babel-preset-env": "^1.6.1", 24 | "babel-preset-react": "^6.24.1", 25 | "enzyme-to-json": "^1.6.0", 26 | "jest": "^19.0.2", 27 | "react-dom": "^15.6.2", 28 | "react-test-renderer": "^15.6.2" 29 | }, 30 | "jest": { 31 | "testEnvironment": "jsdom", 32 | "snapshotSerializers": [ 33 | "enzyme-to-json/serializer" 34 | ], 35 | "setupTestFrameworkScriptFile": "./testSetup.js" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/with-jest/testSetup.js: -------------------------------------------------------------------------------- 1 | import {matcher, serializer} from 'jest-glamor-react' 2 | 3 | // This is what adds the CSS to the output snapshot 4 | expect.addSnapshotSerializer(serializer) 5 | 6 | // this adds toMatchSnapshot to expect and makes the snapshot diff output look nice in the terminal 7 | expect.extend(matcher) 8 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const jestConfig = require('kcd-scripts/jest') 2 | 3 | function push(prop, ...vals) { 4 | jestConfig[prop] = jestConfig[prop] || [] 5 | jestConfig[prop].push(...vals) 6 | } 7 | 8 | push('coveragePathIgnorePatterns', '/src/.*-entry.js$', '/src/constants.js$') 9 | push('snapshotSerializers', 'enzyme-to-json/serializer', 'jest-glamor-react') 10 | push('setupFiles', '/other/setup-tests.js') 11 | 12 | jestConfig.testURL = 'http://localhost' 13 | 14 | module.exports = jestConfig 15 | -------------------------------------------------------------------------------- /other/EMOTION_MIGRATION.md: -------------------------------------------------------------------------------- 1 | # Migrating 2 | 3 | This is for those migrating to [emotion](https://emotion.sh) from glamorous. 4 | 5 | ## Installing 6 | 7 | ``` 8 | yarn add emotion 9 | 10 | # React 11 | yarn add react-emotion 12 | 13 | # Preact 14 | yarn add preact-emotion 15 | 16 | # if you want to use the babel plugin (it's recommended but not required) 17 | yarn add babel-plugin-emotion 18 | 19 | # if you use ThemeProvider 20 | yarn add emotion-theming 21 | ``` 22 | 23 | ### Babel 24 | babel-plugin-emotion is optional but recommended because it enables the element shorthand(`styled.div` instead of `styled('div')`), the css prop, performance optimizations and developer experience improvements like labels. 25 | 26 | If you want to use babel-plugin-emotion, you will need to make some adjustments to your `.babelrc`, the recommended config is shown below. 27 | ```json 28 | { 29 | "env": { 30 | "production": { 31 | "plugins": [["emotion", { "hoist": true }]] 32 | }, 33 | "development": { 34 | "plugins": [ 35 | ["emotion", { "sourceMap": true, "autoLabel": true }] 36 | ] 37 | } 38 | } 39 | } 40 | ``` 41 | 42 | If you need more details see [Install Emotion](https://emotion.sh/docs/install) 43 | 44 | ## Codemod 45 | [There is a codemod](https://github.com/TejasQ/babel-plugin-glamorous-to-emotion) that aims to help migrate an entire existing codebase over to emotion from glamorous. It may be worth looking into this in order to have a quicker and easier migration. 46 | 47 | ## Imports 48 | 49 | If you are in react `import glamorous from 'glamorous'` becomes `import styled from 'react-emotion'` 50 | 51 | If you use ``, `import { ThemeProvider } from "glamorous"` becomes `import { ThemeProvider } from "emotion-theming"` which is a seperate package [emotion-theming](https://github.com/emotion-js/emotion/tree/master/packages/emotion-theming) 52 | 53 | If you're using css.keyframes(glamor) you can adjust them to use `import { keyframes } from "react-emotion"` 54 | 55 | ### Object Syntax 56 | 57 | If you are using babel-plugin-emotion, you only need to replace `glamorous` with `styled` like this 58 | 59 | ```jsx 60 | export const StyledElement = glamorous.div({ ...styles }) 61 | // ↓ ↓ ↓ ↓ ↓ ↓ 62 | export const StyledElement = styled.div({ ...styles }) 63 | ``` 64 | 65 | If you aren't using babel-plugin-emotion, you need to use the function call syntax instead like this 66 | 67 | ```jsx 68 | const SomeComponent = glamorous.div({ ...styles }) 69 | // ↓ ↓ ↓ ↓ ↓ ↓ 70 | const SomeComponent = styled('div')({ ...styles }) 71 | ``` 72 | 73 | 74 | Find and replace `glamorous.` ftw. 75 | 76 | babel-plugin-emotion is required for the css prop. If you use the css prop and use babel-plugin-emotion (e.g. `css={{ backgroundColor: "purple" }}`) you should be good.. but you will need to replace any glamorous objects like `import { Div, Span, Img } from 'glamorous'` with good ol' divs and elements. Also `` needs to become `
` etc. 77 | 78 | ### The `content` CSS property 79 | 80 | One thing to watch out for is that Emotion doesn't add quotes to values of the `content` css property so you have to add quotes around them yourself. 81 | ```jsx 82 | const SomeComponent = glamorous.div({ content: '' }) 83 | // ↓ ↓ ↓ ↓ ↓ ↓ 84 | const SomeComponent = styled.div({ content: '""' }) 85 | ``` 86 | Emotion logs warnings to the console in development when you forget to add quotes so if you have no warnings, you're fine! 87 | 88 | 89 | ### `withProps` 90 | 91 | The emotion library doesn't support `withProps` directly itself, but you can use [`withProps` from `recompose`](https://github.com/emotion-js/emotion/blob/master/docs/with-props.md) instead. For basic usages of `withProps`, you can also use [React's `defaultProps`](https://reactjs.org/docs/react-component.html#defaultprops) like this. 92 | ```jsx 93 | import styled from 'react-emotion' 94 | 95 | const SomeComponent = styled.div({ ...styles }) 96 | 97 | SomeComponent.defaultProps = { 98 | someProp: 'a default value' 99 | } 100 | 101 | ``` 102 | 103 | ### Simulation of Pseudo Selectors 104 | 105 | Emotion doesn't support simulation of pseudo selectors. Glamor creates additional styles for `[data-simulate-*]` selectors in dev mode. Remove any `[data-simulate-*]` selectors that you still have in your styles and any calls to `glamor.simulate`. 106 | 107 | ### Prop Filtering 108 | 109 | Emotion's prop filtering only looks at attributes that are valid for all DOM elements, whereas Glamorous uses the tag to determine which attributes are valid. 110 | 111 | ## Possible Gotchas 112 | 113 | This list is not comprehensive please feel free to make adjustments or additions. 114 | 115 | ## Thanks! 116 | 117 | Glamorous is great, emotion is great.. Happy migrating! 118 | -------------------------------------------------------------------------------- /other/FAQ.md: -------------------------------------------------------------------------------- 1 | ## FAQ 2 | 3 | [How about source mapping in dev tools?](#how-about-source-mapping-in-dev-tools) 4 | 5 | --- 6 | 7 | ### How about source mapping in dev tools? 8 | 9 | Glamorous is based on glamor which does not support source map. This is unfortunate. However you can generally figure out what component is responsible for what you're looking at easily, especially with the [babel plugin](https://www.npmjs.com/package/babel-plugin-glamorous-displayname) (https://github.com/paypal/glamorous#usedisplaynameinclassname). 10 | 11 | ### How does semver work here? 12 | 13 | Glamorous follows semver with regards to official APIs and type definitions. 14 | If a change needs to be made with regards to how glamorous generates the 15 | classNames, that will be made as a patch or minor release rather than a major 16 | release (as you should not be relying on the generated classNames anyway). 17 | If you're concerned about your Jest snapshots breaking due to changes in 18 | the className, you should use [`jest-glamor-react`](https://github.com/kentcdodds/jest-glamor-react) which 19 | will not be impacted by changes to the className. 20 | 21 | ### What does glamorous do that CSS-modules can't do? 22 | 23 | The biggest difference between glamorous and CSSModules is having semantic and declarative render methods. Without glamorous you often rely on 24 | class names or inline styles to style your components. Glamorous gives you a small abstraction around creating components that carry their styles with them. 25 | This small abstraction helps avoid boilerplate and assists in breaking down components into smaller and more declarative components. There are a lot of 26 | additional features that help differentiate glamorous to CSSModules check out the [getting started page](https://glamorous.rocks/getting-started/) or 27 | the [examples](https://glamorous.rocks/examples/). 28 | And for more information, you can see [Kent](https://twitter.com/kentcdodds) (author of glamorous) [talk more about glamorous vs CSSModules](https://youtu.be/biewJRnEiwU?list=PLV5CVI1eNcJh5CTgArGVwANebCrAh2OUE). 29 | -------------------------------------------------------------------------------- /other/MAINTAINING.md: -------------------------------------------------------------------------------- 1 | # Maintaining 2 | 3 | This is documentation for maintainers of this project. 4 | 5 | ## Code of Conduct 6 | 7 | Please review, understand, and be an example of it. Violations of the code of conduct are 8 | taken seriously, even (especially) for maintainers. 9 | 10 | ## Issues 11 | 12 | We want to support and build the community. We do that best by helping people learn to solve 13 | their own problems. We have an issue template and hopefully most folks follow it. If it's 14 | not clear what the issue is, invite them to create a minimal reproduction of what they're trying 15 | to accomplish or the bug they think they've found. 16 | 17 | Once it's determined that a code change is necessary, point people to 18 | [makeapullrequest.com](http://makeapullrequest.com) and invite them to make a pull request. 19 | If they're the one who needs the feature, they're the one who can build it. If they need 20 | some hand holding and you have time to lend a hand, please do so. It's an investment into 21 | another human being, and an investment into a potential maintainer. 22 | 23 | Remember that this is open source, so the code is not yours, it's ours. If someone needs a change 24 | in the codebase, you don't have to make it happen yourself. Commit as much time to the project 25 | as you want/need to. Nobody can ask any more of you than that. 26 | 27 | ## Pull Requests 28 | 29 | As a maintainer, you're fine to make your branches on the main repo or on your own fork. Either 30 | way is fine. 31 | 32 | When we receive a pull request, a travis build is kicked off automatically (see the `.travis.yml` 33 | for what runs in the travis build). We avoid merging anything that breaks the travis build. 34 | 35 | Please review PRs and focus on the code rather than the individual. You never know when this is 36 | someone's first ever PR and we want their experience to be as positive as possible, so be 37 | uplifting and constructive. 38 | 39 | When you merge the pull request, 99% of the time you should use the 40 | [Squash and merge](https://help.github.com/articles/merging-a-pull-request/) feature. This keeps 41 | our git history clean, but more importantly, this allows us to make any necessary changes to the 42 | commit message so we release what we want to release. See the next section on Releases for more 43 | about that. 44 | 45 | ## Release 46 | 47 | Our releases are automatic. They happen whenever code lands into `master`. A travis build gets 48 | kicked off and if it's successful, a tool called 49 | [`semantic-release`](https://github.com/semantic-release/semantic-release) is used to 50 | automatically publish a new release to npm as well as a changelog to GitHub. It is only able to 51 | determine the version and whether a release is necessary by the git commit messages. With this 52 | in mind, **please brush up on [the commit message convention][commit] which drives our releases.** 53 | 54 | > One important note about this: Please make sure that commit messages do NOT contain the words 55 | > "BREAKING CHANGE" in them unless we want to push a major version. I've been burned by this 56 | > more than once where someone will include "BREAKING CHANGE: None" and it will end up releasing 57 | > a new major version. Not a huge deal honestly, but kind of annoying... 58 | 59 | ## Thanks! 60 | 61 | Thank you so much for helping to maintain this project! 62 | 63 | [commit]: https://github.com/conventional-changelog-archived-repos/conventional-changelog-angular/blob/ed32559941719a130bb0327f886d6a32a8cbc2ba/convention.md 64 | -------------------------------------------------------------------------------- /other/USERS.md: -------------------------------------------------------------------------------- 1 | # Glamorous Users 2 | 3 | If you or your company uses glamorous, add your name to this list! Eventually 4 | we may have a website to showcase these (wanna build it!?) 5 | 6 | * [PayPal](https://paypal.com) uses it in [Peer to Peer Payments](https://paypal.com/myaccount/transfer) 7 | * [Kent C. Dodds](https://twitter.com/kentcdodds) uses it on [his website](https://kentcdodds.com) ([source](https://github.com/kentcdodds/kentcdodds.com)) 8 | * FSG uses it in [PlayUp](https://play.playupfantasy.in) and [SFP](https://play.sportsfantasypro.com) 9 | * [Eric Simons](https://twitter.com/ericsimons40) uses it on the [Thinkster.io website](https://thinkster.io) 10 | * [Resto 'N Go](http://restongo.com/en/) uses it in it's food-order app integrated on every restaurant Websites 11 | * [Starhack.it](http://www.starhack.it): A full stack starter kit. 12 | * [Uniteam.pl](http://uniteam.pl): Wherever we use react. 13 | * [The West Australian](https://thewest.com.au) is 14 | * undergoing a feature by feature migration from sass to glamorous 15 | * and uses it on our internal component library to support theming for unreleased products. 16 | * [EDITED](https://edited.com/) uses it in most of the marketing site as well as a few open source projects like [`react-responsive-picture`](https://github.com/EDITD/react-responsive-picture) 17 | * [Songlink](https://songlink.io): A cross-platform music sharing service. 18 | * [Robin](https://robinpowered.com) users [`glamorous-native`](https://github.com/robinpowered/glamorous-native) in their 19 | [mobile](https://robinpowered.com/features/mobile) and [tablet](https://robinpowered.com/features/room-display) 20 | applications 21 | * [Budgee](https://budgee.com)is styled entirely using glamorous. 22 | * [Rubrik](https://rubrik.com) is using glamorous in styling the UI of our next generation product for cloud data management. 23 | * [Mineral UI](http://mineral-ui.com/) is using Glamorous to build an open source design system and React component library. 24 | * [TABtouch](https://www.tabtouch.mobi/) uses it for a screen-by-screen mobile app rewrite, migrating from Sass to Glamorous. 25 | * [The Artling](https://theartling.com/) uses to glamorize their app full of art. 26 | -------------------------------------------------------------------------------- /other/__tests__/dist-preact.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is here to validate that the built version 3 | * of the library exposes the module in the way that we 4 | * want it to. Specifically that the ES6 module import can 5 | * get the glamorous function via default import. Also that 6 | * the CommonJS require returns the glamorous function 7 | * (rather than an object that has the glamorous as a 8 | * `default` property). 9 | * 10 | * This file is unable to validate the global export. 11 | */ 12 | import assert from 'assert' 13 | 14 | import * as esImportStar from '../../preact/dist/glamorous.esm' 15 | import * as esImportStarTiny from '../../preact/dist/glamorous.esm.tiny' 16 | import esImport from '../../preact/dist/glamorous.esm' 17 | import esImportTiny from '../../preact/dist/glamorous.esm.tiny' 18 | 19 | import cjsImport from '../../preact' // picks up the main from package.json 20 | import cjsImportTiny from '../../preact/dist/glamorous.cjs.tiny' 21 | 22 | import umdImport from '../../preact/dist/glamorous.umd' 23 | import umdImportTiny from '../../preact/dist/glamorous.umd.tiny' 24 | 25 | // intentionally left out because you shouldn't ever 26 | // try to require the ES file in CommonJS 27 | // const esRequire = require('../../preact/dist/glamorous.es') 28 | const cjsRequire = require('../../preact') // picks up the main from package.json 29 | const umdRequire = require('../../preact/dist/glamorous.umd') 30 | const cjsRequireTiny = require('../../preact/dist/glamorous.cjs.tiny') 31 | const umdRequireTiny = require('../../preact/dist/glamorous.umd.tiny') 32 | 33 | // eslint-disable-next-line complexity 34 | test('all imports work', () => { 35 | assert( 36 | isGlamorousFunction(esImport) && hasExtraExports(esImportStar), 37 | 'ES build has a problem with ES Modules', 38 | ) 39 | assert( 40 | isGlamorousFunction(esImportTiny) && !hasExtraExports(esImportStarTiny), 41 | 'ES Tiny build has a problem with ES Modules', 42 | ) 43 | 44 | // assert( 45 | // isGlamorousFunction(esRequire) && hasExtraExports(esRequire), 46 | // 'ES build has a problem with CJS', 47 | // ) 48 | // assert( 49 | // isGlamorousFunction(esRequireTiny) && !hasExtraExports(esRequireTiny), 50 | // 'ES Tiny build has a problem with CJS', 51 | // ) 52 | // intentionally left out ☝️ 53 | 54 | assert( 55 | isGlamorousFunction(cjsImport) && hasExtraExports(cjsImport), 56 | 'CJS build has a problem with ES Modules', 57 | ) 58 | 59 | assert( 60 | isGlamorousFunction(cjsImportTiny) && !hasExtraExports(cjsImportTiny), 61 | 'CJS Tiny build has a problem with ES Modules', 62 | ) 63 | 64 | assert( 65 | isGlamorousFunction(cjsRequire) && hasExtraExports(cjsRequire), 66 | 'CJS build has a problem with CJS', 67 | ) 68 | assert( 69 | isGlamorousFunction(cjsRequireTiny) && !hasExtraExports(cjsRequireTiny), 70 | 'CJS Tiny build has a problem with CJS', 71 | ) 72 | 73 | assert( 74 | isGlamorousFunction(umdImport) && hasExtraExports(umdImport), 75 | 'UMD build has a problem with ES Modules', 76 | ) 77 | 78 | assert( 79 | isGlamorousFunction(umdImportTiny) && !hasExtraExports(umdImportTiny), 80 | 'UMD Tiny build has a problem with ES Modules', 81 | ) 82 | 83 | assert( 84 | isGlamorousFunction(umdRequire) && hasExtraExports(umdRequire), 85 | 'UMD build has a problem with CJS', 86 | ) 87 | assert( 88 | isGlamorousFunction(umdRequireTiny) && !hasExtraExports(umdRequireTiny), 89 | 'UMD Tiny build has a problem with CJS', 90 | ) 91 | 92 | assert( 93 | hasNamedExports(esImportStar) && !hasNamedExports(esImportTiny), 94 | 'ES build has a problem with helper tags exports', 95 | ) 96 | 97 | // TODO: how could we validate the global export? 98 | }) 99 | 100 | function isGlamorousFunction(thing) { 101 | if (typeof thing !== 'function') { 102 | console.error( 103 | `glamorous thing should be a function. It's a ${typeof thing} with the properties of: ${Object.keys( 104 | thing, 105 | ).join(', ')}`, 106 | ) 107 | return false 108 | } 109 | return true 110 | } 111 | 112 | function hasExtraExports(thing) { 113 | const extraExports = { 114 | ThemeProvider: 'function', 115 | withTheme: 'function', 116 | } 117 | const keys = Object.keys(extraExports) 118 | return keys.every(key => typeof thing[key] === extraExports[key]) 119 | } 120 | 121 | function hasNamedExports(thing) { 122 | const GlamorousComponents = { 123 | Div: 'function', 124 | ObjectTag: 'function', 125 | } 126 | const keys = Object.keys(GlamorousComponents) 127 | return keys.every( 128 | key => 129 | typeof thing[key] === GlamorousComponents[key] && 130 | thing[key].isGlamorousComponent, 131 | ) 132 | } 133 | 134 | /* 135 | eslint 136 | no-console: 0, 137 | import/extensions: 0, 138 | import/no-unresolved: 0, 139 | import/no-duplicates: 0, 140 | no-duplicate-imports: 0, 141 | */ 142 | -------------------------------------------------------------------------------- /other/__tests__/dist-react.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is here to validate that the built version 3 | * of the library exposes the module in the way that we 4 | * want it to. Specifically that the ES6 module import can 5 | * get the glamorous function via default import. Also that 6 | * the CommonJS require returns the glamorous function 7 | * (rather than an object that has the glamorous as a 8 | * `default` property). 9 | * 10 | * This file is unable to validate the global export. 11 | */ 12 | import assert from 'assert' 13 | import './helpers/prop-types' // eslint-disable-line 14 | 15 | import * as esImportStar from '../../dist/glamorous.esm' 16 | import * as esImportStarTiny from '../../dist/glamorous.esm.tiny' 17 | import esImport from '../../dist/glamorous.esm' 18 | import esImportTiny from '../../dist/glamorous.esm.tiny' 19 | 20 | import cjsImport from '../../' // picks up the main from package.json 21 | import cjsImportTiny from '../../dist/glamorous.cjs.tiny' 22 | 23 | import umdImport from '../../dist/glamorous.umd' 24 | import umdImportTiny from '../../dist/glamorous.umd.tiny' 25 | 26 | // intentionally left out because you shouldn't ever 27 | // try to require the ES file in CommonJS 28 | // const esRequire = require('../../dist/glamorous.es') 29 | const cjsRequire = require('../../') // picks up the main from package.json 30 | const umdRequire = require('../../dist/glamorous.umd') 31 | const cjsRequireTiny = require('../../dist/glamorous.cjs.tiny') 32 | const umdRequireTiny = require('../../dist/glamorous.umd.tiny') 33 | 34 | // eslint-disable-next-line complexity 35 | test('all imports work', () => { 36 | assert( 37 | isGlamorousFunction(esImport) && hasExtraExports(esImportStar), 38 | 'ES build has a problem with ES Modules', 39 | ) 40 | assert( 41 | isGlamorousFunction(esImportTiny) && !hasExtraExports(esImportStarTiny), 42 | 'ES Tiny build has a problem with ES Modules', 43 | ) 44 | 45 | // assert( 46 | // isGlamorousFunction(esRequire) && hasExtraExports(esRequire), 47 | // 'ES build has a problem with CJS', 48 | // ) 49 | // assert( 50 | // isGlamorousFunction(esRequireTiny) && !hasExtraExports(esRequireTiny), 51 | // 'ES Tiny build has a problem with CJS', 52 | // ) 53 | // intentionally left out ☝️ 54 | 55 | assert( 56 | isGlamorousFunction(cjsImport) && hasExtraExports(cjsImport), 57 | 'CJS build has a problem with ES Modules', 58 | ) 59 | 60 | assert( 61 | isGlamorousFunction(cjsImportTiny) && !hasExtraExports(cjsImportTiny), 62 | 'CJS Tiny build has a problem with ES Modules', 63 | ) 64 | 65 | assert( 66 | isGlamorousFunction(cjsRequire) && hasExtraExports(cjsRequire), 67 | 'CJS build has a problem with CJS', 68 | ) 69 | assert( 70 | isGlamorousFunction(cjsRequireTiny) && !hasExtraExports(cjsRequireTiny), 71 | 'CJS Tiny build has a problem with CJS', 72 | ) 73 | 74 | assert( 75 | isGlamorousFunction(umdImport) && hasExtraExports(umdImport), 76 | 'UMD build has a problem with ES Modules', 77 | ) 78 | 79 | assert( 80 | isGlamorousFunction(umdImportTiny) && !hasExtraExports(umdImportTiny), 81 | 'UMD Tiny build has a problem with ES Modules', 82 | ) 83 | 84 | assert( 85 | isGlamorousFunction(umdRequire) && hasExtraExports(umdRequire), 86 | 'UMD build has a problem with CJS', 87 | ) 88 | assert( 89 | isGlamorousFunction(umdRequireTiny) && !hasExtraExports(umdRequireTiny), 90 | 'UMD Tiny build has a problem with CJS', 91 | ) 92 | 93 | assert( 94 | hasNamedExports(esImportStar) && !hasNamedExports(esImportTiny), 95 | 'ES build has a problem with helper tags exports', 96 | ) 97 | 98 | // TODO: how could we validate the global export? 99 | }) 100 | 101 | function isGlamorousFunction(thing) { 102 | if (typeof thing !== 'function') { 103 | console.error( 104 | `glamorous thing should be a function. It's a ${typeof thing} with the properties of: ${Object.keys( 105 | thing, 106 | ).join(', ')}`, 107 | ) 108 | return false 109 | } 110 | return true 111 | } 112 | 113 | function hasExtraExports(thing) { 114 | const extraExports = { 115 | ThemeProvider: 'function', 116 | withTheme: 'function', 117 | } 118 | const keys = Object.keys(extraExports) 119 | return keys.every(key => typeof thing[key] === extraExports[key]) 120 | } 121 | 122 | function hasNamedExports(thing) { 123 | const GlamorousComponents = { 124 | Div: 'function', 125 | ObjectTag: 'function', 126 | } 127 | const keys = Object.keys(GlamorousComponents) 128 | return keys.every( 129 | key => 130 | typeof thing[key] === GlamorousComponents[key] && 131 | thing[key].isGlamorousComponent, 132 | ) 133 | } 134 | 135 | /* 136 | eslint 137 | no-console: 0, 138 | import/extensions: 0, 139 | import/no-unresolved: 0, 140 | import/no-duplicates: 0, 141 | no-duplicate-imports: 0, 142 | */ 143 | -------------------------------------------------------------------------------- /other/__tests__/helpers/prop-types.js: -------------------------------------------------------------------------------- 1 | // just to avoid a warning... 2 | import PropTypes from 'prop-types' 3 | 4 | window.PropTypes = PropTypes 5 | -------------------------------------------------------------------------------- /other/codemods/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | * [glamorous codemods](#glamorous-codemods) 6 | * [Running Codemods](#running-codemods) 7 | * [Available codemods](#available-codemods) 8 | * [`theme-move`](#theme-move) 9 | * [`glamorous-to-emotion`](#glamorous-to-emotion) 10 | 11 | 12 | 13 | # glamorous codemods 14 | 15 | This is where we'll develop and release codemods for `glamorous`. We hopefully 16 | wont have to do it all that much 😄 17 | 18 | ## Running Codemods 19 | 20 | First, you'll need to clone the glamorous repo somewhere: 21 | 22 | ```bash 23 | git clone https://github.com/paypal/glamorous.git 24 | ``` 25 | 26 | The codemods will then be available to you in `glamorous/other/codemods` 27 | 28 | To run the codemods, you'll need to run `babel-codemod`. You can install that 29 | package globally, or you can use `npx` (which comes pre-installed with npm@5.2.0 30 | or greater). Here's how you use it with `npx`: 31 | 32 | ```bash 33 | npx babel-codemod --plugin ./glamorous/other/codemods/{CODEMOD_NAME}.js "src/**/*.js" 34 | ``` 35 | 36 | Where `CODEMOD_NAME` is the filename of the codemod and `src/**/*.js` is a glob 37 | that matches the files you want to run the codemods on. 38 | 39 | > If you don't have `npx` installed, you can install `babel-codemod` globally 40 | > and run the same script without the `npx` prefix. 41 | 42 | ## Available codemods 43 | 44 | ### `theme-move` 45 | 46 | In glamorous v4 the dynamic props API changed from: 47 | 48 | ```javascript 49 | function(props, theme, context) {} 50 | ``` 51 | 52 | To 53 | 54 | ```javascript 55 | function(props, context) {} 56 | ``` 57 | 58 | Where `props` has a `theme` property. 59 | 60 | This codemod updates your code to use the `theme` from props and remove the 61 | `theme` argument. 62 | 63 | ### `glamorous-to-emotion` 64 | 65 | [This codemod](https://github.com/TejasQ/babel-plugin-glamorous-to-emotion) helps migrate an existing codebase from glamorous to emotion. 66 | -------------------------------------------------------------------------------- /other/codemods/__snapshots__/theme-move.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`1. glamorous-theme-move-codemod 1`] = ` 4 | 5 | import glamorous from 'glamorous' 6 | glamorous.div((props, theme) => ({fontSize: theme.fontSize})) 7 | 8 | ↓ ↓ ↓ ↓ ↓ ↓ 9 | 10 | import glamorous from 'glamorous' 11 | glamorous.div(props => ({fontSize: props.theme.fontSize})) 12 | 13 | `; 14 | 15 | exports[`2. glamorous-theme-move-codemod 1`] = ` 16 | 17 | import glamorous from 'glamorous' 18 | glamorous.div((props, theme, context) => ({ fontSize: theme.main.fontSize })) 19 | 20 | ↓ ↓ ↓ ↓ ↓ ↓ 21 | 22 | import glamorous from 'glamorous' 23 | glamorous.div((props, context) => ({fontSize: props.theme.main.fontSize})) 24 | 25 | `; 26 | 27 | exports[`3. glamorous-theme-move-codemod 1`] = ` 28 | 29 | import glamorous from 'glamorous' 30 | 31 | glamorous.div((props, theme) => { 32 | return { 33 | fontSize: theme.main.fontSize, 34 | color: props.important ? 'red' : 'black', 35 | } 36 | }) 37 | 38 | ↓ ↓ ↓ ↓ ↓ ↓ 39 | 40 | import glamorous from 'glamorous' 41 | 42 | glamorous.div(props => { 43 | return { 44 | fontSize: props.theme.main.fontSize, 45 | color: props.important ? 'red' : 'black', 46 | } 47 | }) 48 | 49 | `; 50 | 51 | exports[`3. with require 1`] = ` 52 | 53 | const glamorous = require('glamorous') 54 | glamorous.div((props, theme) => ({fontSize: theme.main.fontSize})) 55 | 56 | ↓ ↓ ↓ ↓ ↓ ↓ 57 | 58 | const glamorous = require('glamorous') 59 | glamorous.div(props => ({fontSize: props.theme.main.fontSize})) 60 | 61 | `; 62 | 63 | exports[`4. glamorous-theme-move-codemod 1`] = ` 64 | 65 | import glamorous from 'glamorous' 66 | 67 | glamorous.div(({important}, theme) => { 68 | return { 69 | fontSize: theme.main.fontSize, 70 | color: important ? 'red' : 'black', 71 | } 72 | }) 73 | 74 | ↓ ↓ ↓ ↓ ↓ ↓ 75 | 76 | import glamorous from 'glamorous' 77 | 78 | glamorous.div(({important, theme}) => { 79 | return { 80 | fontSize: theme.main.fontSize, 81 | color: important ? 'red' : 'black', 82 | } 83 | }) 84 | 85 | `; 86 | 87 | exports[`5. glamorous-theme-move-codemod 1`] = ` 88 | 89 | import glamorous from 'glamorous' 90 | 91 | glamorous.div((props, {main}) => { 92 | return { 93 | fontSize: main.fontSize, 94 | color: props.important ? 'red' : 'black', 95 | } 96 | }) 97 | 98 | ↓ ↓ ↓ ↓ ↓ ↓ 99 | 100 | import glamorous from 'glamorous' 101 | 102 | glamorous.div(props => { 103 | return { 104 | fontSize: props.theme.main.fontSize, 105 | color: props.important ? 'red' : 'black', 106 | } 107 | }) 108 | 109 | `; 110 | 111 | exports[`6. glamorous-theme-move-codemod 1`] = ` 112 | 113 | import glamorous from 'glamorous' 114 | 115 | glamorous.div(({important}, {main}) => { 116 | return { 117 | fontSize: main.fontSize, 118 | color: important ? 'red' : 'black', 119 | } 120 | }) 121 | 122 | ↓ ↓ ↓ ↓ ↓ ↓ 123 | 124 | import glamorous from 'glamorous' 125 | 126 | glamorous.div(({important, theme: {main}}) => { 127 | return { 128 | fontSize: main.fontSize, 129 | color: important ? 'red' : 'black', 130 | } 131 | }) 132 | 133 | `; 134 | 135 | exports[`7. glamorous-theme-move-codemod 1`] = ` 136 | 137 | import glamorous from 'glamorous' 138 | glamorous('div')((props, theme) => ({fontSize: theme.fontSize})) 139 | 140 | ↓ ↓ ↓ ↓ ↓ ↓ 141 | 142 | import glamorous from 'glamorous' 143 | glamorous('div')(props => ({fontSize: props.theme.fontSize})) 144 | 145 | `; 146 | 147 | exports[`8. glamorous-theme-move-codemod 1`] = ` 148 | 149 | import glamorous from 'glamorous' 150 | glamorous.div([(props, theme) => ({fontSize: theme.fontSize})]) 151 | 152 | ↓ ↓ ↓ ↓ ↓ ↓ 153 | 154 | import glamorous from 'glamorous' 155 | glamorous.div([props => ({fontSize: props.theme.fontSize})]) 156 | 157 | `; 158 | 159 | exports[`9. glamorous-theme-move-codemod 1`] = ` 160 | 161 | import glamorous from 'glamorous' 162 | 163 | const dynamicFn = (props, theme) => ({fontSize: theme.fontSize}) 164 | glamorous.div(dynamicFn) 165 | 166 | ↓ ↓ ↓ ↓ ↓ ↓ 167 | 168 | import glamorous from 'glamorous' 169 | 170 | const dynamicFn = props => ({fontSize: props.theme.fontSize}) 171 | glamorous.div(dynamicFn) 172 | 173 | `; 174 | 175 | exports[`10. glamorous-theme-move-codemod 1`] = ` 176 | 177 | import glamorous from 'glamorous' 178 | 179 | function dynamicFn(props, theme) { 180 | return {fontSize: theme.fontSize} 181 | } 182 | glamorous.div([{}, dynamicFn]) 183 | 184 | ↓ ↓ ↓ ↓ ↓ ↓ 185 | 186 | import glamorous from 'glamorous' 187 | 188 | function dynamicFn(props) { 189 | return {fontSize: props.theme.fontSize} 190 | } 191 | glamorous.div([{}, dynamicFn]) 192 | 193 | `; 194 | 195 | exports[`11. glamorous-theme-move-codemod 1`] = ` 196 | 197 | import glamorous from 'glamorous' 198 | const ui = ({fontSize: theme.fontSize})} /> 199 | 200 | ↓ ↓ ↓ ↓ ↓ ↓ 201 | 202 | import glamorous from 'glamorous' 203 | const ui = ({fontSize: props.theme.fontSize})} /> 204 | 205 | `; 206 | 207 | exports[`12. glamorous-theme-move-codemod 1`] = ` 208 | 209 | import glamorous from 'glamorous' 210 | 211 | const dynamicFn = (props, theme) => ({fontSize: theme.fontSize}) 212 | const ui = 213 | 214 | ↓ ↓ ↓ ↓ ↓ ↓ 215 | 216 | import glamorous from 'glamorous' 217 | 218 | const dynamicFn = props => ({fontSize: props.theme.fontSize}) 219 | const ui = 220 | 221 | `; 222 | 223 | exports[`13. glamorous-theme-move-codemod 1`] = ` 224 | 225 | import glamorous from 'glamorous' 226 | 227 | import {Span} from 'glamorous' 228 | const ui = ({fontSize: theme.fontSize})}>content 229 | 230 | ↓ ↓ ↓ ↓ ↓ ↓ 231 | 232 | import glamorous from 'glamorous' 233 | 234 | import {Span} from 'glamorous' 235 | const ui = ( 236 | ({fontSize: props.theme.fontSize})}>content 237 | ) 238 | 239 | `; 240 | 241 | exports[`14. glamorous-theme-move-codemod 1`] = ` 242 | 243 | import glamorous from 'glamorous' 244 | 245 | glamorous.div((props, {primary: {headings: {font: {size: fontSize}}}}) => ({ 246 | color: props.color, 247 | fontSize, 248 | })) 249 | 250 | ↓ ↓ ↓ ↓ ↓ ↓ 251 | 252 | import glamorous from 'glamorous' 253 | 254 | glamorous.div( 255 | ({theme: {primary: {headings: {font: {size: fontSize}}}}, ...props}) => ({ 256 | color: props.color, 257 | fontSize, 258 | }), 259 | ) 260 | 261 | `; 262 | 263 | exports[`15. glamorous-theme-move-codemod 1`] = ` 264 | 265 | import glamorous from 'glamorous' 266 | 267 | glamorous.div(({color}, {primary: {headings: {font: {size: fontSize}}}}) => ({ 268 | color, 269 | fontSize, 270 | })) 271 | 272 | ↓ ↓ ↓ ↓ ↓ ↓ 273 | 274 | import glamorous from 'glamorous' 275 | 276 | glamorous.div( 277 | ({color, theme: {primary: {headings: {font: {size: fontSize}}}}}) => ({ 278 | color, 279 | fontSize, 280 | }), 281 | ) 282 | 283 | `; 284 | -------------------------------------------------------------------------------- /other/codemods/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "glamorous-codemods", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest", 8 | "test:watch": "jest --watch" 9 | }, 10 | "keywords": [], 11 | "author": "Kent C. Dodds (http://kentcdodds.com/)", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "ast-pretty-print": "^2.0.0", 15 | "babel-core": "^6.25.0", 16 | "babel-plugin-tester": "^3.3.0", 17 | "babel-preset-env": "^1.6.0", 18 | "babel-preset-react": "^6.24.1", 19 | "babel-preset-stage-2": "^6.24.1", 20 | "jest": "^20.0.4", 21 | "prettier": "^1.6.1", 22 | "recast": "^0.12.6", 23 | "strip-indent": "^2.0.0" 24 | }, 25 | "babel": { 26 | "presets": ["env", "react", "stage-2"] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /other/codemods/theme-move.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | // this is really handy with: console.log(printAST(node)) 3 | // const printAST = require('ast-pretty-print') 4 | 5 | module.exports = glamorousThemeCodemod 6 | 7 | function glamorousThemeCodemod(babel) { 8 | const {types: t} = babel 9 | const identifiers = {} 10 | 11 | return { 12 | name: 'glamorous-theme-move-codemod', 13 | visitor: { 14 | ImportDeclaration(path, {file: {opts: {filename}}}) { 15 | // find imports of glamorous and add the references 16 | if (path.node.source.value !== 'glamorous') { 17 | return 18 | } 19 | const specifiers = path.get('specifiers') 20 | specifiers.forEach(specifier => { 21 | const {node: {local: {name}}} = specifier 22 | const {referencePaths = []} = path.scope.getBinding(name) || {} 23 | identifiers[filename] = identifiers[filename] || [] 24 | identifiers[filename].push(...referencePaths) 25 | }) 26 | }, 27 | VariableDeclarator(path, {file: {opts: {filename}}}) { 28 | // find requires of glamorous and add the references 29 | const isRequireCall = looksLike(path, { 30 | node: { 31 | init: { 32 | type: 'CallExpression', 33 | callee: { 34 | name: 'require', 35 | }, 36 | arguments: args => 37 | args.length === 1 && args[0].value === 'glamorous', 38 | }, 39 | }, 40 | }) 41 | if (!isRequireCall) { 42 | return 43 | } 44 | const {node: {id: {name}}} = path 45 | const {referencePaths = []} = path.scope.getBinding(name) || {} 46 | identifiers[filename] = identifiers[filename] || [] 47 | identifiers[filename].push(...referencePaths) 48 | }, 49 | Program: { 50 | exit(path, {file: {opts: {filename}}}) { 51 | // now that we've traversed everything, we can go through each of them 52 | // and convert the ones that need to be converted 53 | const fileIdentifiers = identifiers[filename] || [] 54 | // eslint-disable-next-line complexity 55 | fileIdentifiers.forEach(identifier => { 56 | if (identifier.isJSXIdentifier()) { 57 | const openingElement = identifier.findParent( 58 | t.isJSXOpeningElement, 59 | ) 60 | if (!openingElement) { 61 | return 62 | } 63 | openingElement 64 | .get('attributes') 65 | .reduce((expressions, attrPath) => { 66 | if (attrPath.node.name.name === 'css') { 67 | expressions.push(attrPath.get('value.expression')) 68 | } 69 | return expressions 70 | }, []) 71 | .reduce(dynamicFnReducer, []) 72 | .forEach(handleDynamicFunction) 73 | 74 | return 75 | } 76 | const isBuiltInCall = t.isMemberExpression(identifier.parent) 77 | let callExpression = identifier.findParent(t.isCallExpression) 78 | if (!isBuiltInCall && callExpression) { 79 | callExpression = callExpression.findParent(t.isCallExpression) 80 | } 81 | if (!callExpression) { 82 | return 83 | } 84 | callExpression 85 | .get('arguments') 86 | .reduce(dynamicFnReducer, []) 87 | .forEach(handleDynamicFunction) 88 | }) 89 | }, 90 | }, 91 | }, 92 | } 93 | 94 | // eslint-disable-next-line complexity 95 | function handleDynamicFunction(dynamicFn) { 96 | // eslint-disable-next-line prefer-const 97 | let [propsArg, themeArg, ...restArgs] = dynamicFn.node.params 98 | const propsIsDestructured = t.isObjectPattern(propsArg) 99 | const themeIsDestructured = t.isObjectPattern(themeArg) 100 | 101 | if (propsIsDestructured) { 102 | if (themeIsDestructured) { 103 | const key = t.identifier('theme') 104 | const value = t.objectExpression(themeArg.properties) 105 | const themeArgProperties = t.objectProperty(key, value) 106 | propsArg.properties = [...propsArg.properties, themeArgProperties] 107 | } else { 108 | const id = t.identifier(themeArg.name) 109 | const key = id 110 | const value = id 111 | const computed = false 112 | const shorthand = true 113 | propsArg.properties = [ 114 | ...propsArg.properties, 115 | t.objectProperty(key, value, computed, shorthand), 116 | ] 117 | } 118 | } else if (themeIsDestructured) { 119 | const themeIsDestructuredDeeply = themeArg.properties.some(themeProp => { 120 | return t.isObjectPattern(themeProp.value) 121 | }) 122 | if (themeIsDestructuredDeeply) { 123 | handleDeeplyDestructuredTheme() 124 | } 125 | themeArg.properties.forEach(themeProp => { 126 | const {referencePaths = []} = 127 | dynamicFn.scope.getBinding(themeProp.value.name) || {} 128 | referencePaths.forEach(ref => { 129 | ref.replaceWith( 130 | t.memberExpression( 131 | t.memberExpression( 132 | t.identifier(propsArg.name), 133 | t.identifier('theme'), 134 | ), 135 | t.identifier(ref.node.name), 136 | ), 137 | ) 138 | }) 139 | }) 140 | } else { 141 | const {referencePaths = []} = 142 | dynamicFn.scope.getBinding(themeArg.name) || {} 143 | referencePaths.forEach(ref => { 144 | ref.replaceWith( 145 | t.memberExpression( 146 | t.identifier(propsArg.name), 147 | t.identifier(ref.node.name), 148 | ), 149 | ) 150 | }) 151 | } 152 | 153 | // remove the theme arg 154 | dynamicFn.node.params = [propsArg, ...restArgs] 155 | 156 | // utils 157 | function handleDeeplyDestructuredTheme() { 158 | propsArg = t.objectPattern([ 159 | t.objectProperty(t.identifier('theme'), themeArg), 160 | t.restProperty(t.identifier('props')), 161 | ]) 162 | } 163 | } 164 | } 165 | 166 | // eslint-disable-next-line complexity 167 | function dynamicFnReducer(dynamicFns, argPath) { 168 | if (!argPath || !argPath.node) { 169 | return dynamicFns 170 | } 171 | if (isDynamicFunctionWithTheme(argPath)) { 172 | dynamicFns.push(argPath) 173 | } else if (argPath.isArrayExpression()) { 174 | argPath.get('elements').forEach(element => { 175 | dynamicFnReducer(dynamicFns, element) 176 | }) 177 | } else if (argPath.isIdentifier()) { 178 | // try to track the initialization of this identifier and update that 179 | // if it's a function 180 | const binding = argPath.scope.getBinding(argPath.node.name) 181 | if (!binding || !binding.path) { 182 | // global variable referenced 183 | return dynamicFns 184 | } 185 | let initPath = binding.path 186 | if (initPath.isVariableDeclarator()) { 187 | // could be an assignment to an object, array, or function 188 | initPath = initPath.get('init') 189 | } 190 | dynamicFnReducer(dynamicFns, initPath) 191 | } 192 | return dynamicFns 193 | } 194 | 195 | function isDynamicFunctionWithTheme(path) { 196 | return path.node && path.node.params && path.node.params.length > 1 197 | } 198 | 199 | function looksLike(a, b) { 200 | return ( 201 | a && 202 | b && 203 | Object.keys(b).every(bKey => { 204 | const bVal = b[bKey] 205 | const aVal = a[bKey] 206 | if (typeof bVal === 'function') { 207 | return bVal(aVal) 208 | } 209 | return isPrimitive(bVal) ? bVal === aVal : looksLike(aVal, bVal) 210 | }) 211 | ) 212 | } 213 | 214 | function isPrimitive(val) { 215 | // eslint-disable-next-line 216 | return val == null || /^[sbn]/.test(typeof val) 217 | } 218 | -------------------------------------------------------------------------------- /other/codemods/theme-move.test.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len:0 */ 2 | import path from 'path' 3 | import prettier from 'prettier' 4 | import stripIndent from 'strip-indent' 5 | // not sure why, but travis seems to error out 6 | // due to this import 🙃 7 | // eslint-disable-next-line 8 | import pluginTester from 'babel-plugin-tester' 9 | import plugin from './theme-move' 10 | 11 | const projectRoot = path.join(__dirname, '../../') 12 | 13 | // adding this somehow improves the snapshots :shrug: 14 | expect.addSnapshotSerializer({ 15 | print(val) { 16 | return val.split(projectRoot).join('/') 17 | }, 18 | test(val) { 19 | return typeof val === 'string' 20 | }, 21 | }) 22 | 23 | // special cases 24 | pluginTester({ 25 | plugin, 26 | babelOptions: { 27 | filename: __filename, 28 | parserOpts: { 29 | plugins: ['jsx'], 30 | }, 31 | }, 32 | formatResult, 33 | tests: { 34 | 'no glamorous import': ` 35 | import glamorous from 'not-glamorous' 36 | 37 | glamorous.div((props, theme) => theme.main) 38 | `, 39 | 'no theme prop': ` 40 | import glamorous from 'glamorous' 41 | 42 | glamorous.div(({theme}) => ({fontSize: theme.main.fontSize})) 43 | `, 44 | 'with require': { 45 | code: ` 46 | const glamorous = require('glamorous') 47 | glamorous.div((props, theme) => ({fontSize: theme.main.fontSize})) 48 | `, 49 | snapshot: true, 50 | }, 51 | 'no dynamic fn': { 52 | filename: path.join(__dirname, `/test-dynamic-fn.js`), 53 | code: ` 54 | import {Div} from 'glamorous' 55 | 56 | const ui =
57 | `, 58 | }, 59 | 'css prop set to a className': ` 60 | import {Div} from 'glamorous' 61 | 62 | const ui =
63 | `, 64 | 'not called': ` 65 | import glamorous from 'glamorous' 66 | 67 | glamorous 68 | `, 69 | }, 70 | }) 71 | 72 | const dynamicFn = `(props, theme) => ({fontSize: theme.fontSize})` 73 | 74 | // changes 75 | pluginTester({ 76 | plugin, 77 | snapshot: true, 78 | formatResult, 79 | babelOptions: { 80 | parserOpts: { 81 | plugins: ['jsx'], 82 | }, 83 | }, 84 | tests: withGlamorousImport([ 85 | `glamorous.div(${dynamicFn})`, 86 | `glamorous.div((props, theme, context) => ({ fontSize: theme.main.fontSize }))`, 87 | ` 88 | glamorous.div((props, theme) => { 89 | return { 90 | fontSize: theme.main.fontSize, 91 | color: props.important ? 'red' : 'black', 92 | } 93 | }) 94 | `, 95 | ` 96 | glamorous.div(({important}, theme) => { 97 | return { 98 | fontSize: theme.main.fontSize, 99 | color: important ? 'red' : 'black', 100 | } 101 | }) 102 | `, 103 | ` 104 | glamorous.div((props, {main}) => { 105 | return { 106 | fontSize: main.fontSize, 107 | color: props.important ? 'red' : 'black', 108 | } 109 | }) 110 | `, 111 | ` 112 | glamorous.div(({important}, {main}) => { 113 | return { 114 | fontSize: main.fontSize, 115 | color: important ? 'red' : 'black', 116 | } 117 | }) 118 | `, 119 | `glamorous('div')(${dynamicFn})`, 120 | `glamorous.div([${dynamicFn}])`, 121 | ` 122 | const dynamicFn = ${dynamicFn} 123 | glamorous.div(dynamicFn) 124 | `, 125 | ` 126 | function dynamicFn(props, theme) { 127 | return {fontSize: theme.fontSize} 128 | } 129 | glamorous.div([{}, dynamicFn]) 130 | `, 131 | `const ui = `, 132 | ` 133 | const dynamicFn = ${dynamicFn} 134 | const ui = 135 | `, 136 | ` 137 | import {Span} from 'glamorous' 138 | const ui = content 139 | `, 140 | ` 141 | glamorous.div((props, {primary: {headings: {font: {size: fontSize}}}}) => ({ 142 | color: props.color, 143 | fontSize, 144 | })) 145 | `, 146 | ` 147 | glamorous.div(({color}, {primary: {headings: {font: {size: fontSize}}}}) => ({ 148 | color, 149 | fontSize, 150 | })) 151 | `, 152 | ]), 153 | }) 154 | 155 | function withGlamorousImport(tests) { 156 | return tests.map((t, index) => { 157 | const test = {babelOptions: {}} 158 | if (typeof t === 'string') { 159 | test.code = t 160 | } else { 161 | Object.assign(test, t) 162 | } 163 | test.code = `import glamorous from 'glamorous'\n${stripIndent(test.code)}` 164 | test.babelOptions.filename = path.join(__dirname, `/test-${index}.js`) 165 | return test 166 | }) 167 | } 168 | 169 | function formatResult(result) { 170 | return prettier 171 | .format(result, { 172 | printWidth: 80, 173 | tabWidth: 2, 174 | useTabs: false, 175 | semi: false, 176 | singleQuote: true, 177 | trailingComma: 'all', 178 | bracketSpacing: false, 179 | jsxBracketSameLine: false, 180 | }) 181 | .trim() 182 | } 183 | 184 | /* 185 | Here's something we could cover, but probably wont due to the amount of effort invovled. 186 | If anyone wants to try, be my guest 😀 187 | This might help: https://github.com/kentcdodds/css-in-js-precompiler/blob/37f8285b027a08b0c333f8587f0ef75b8964001c/src/get-literalizers.js#L36-L66 188 | 189 | import dynamicFn from './some-utils' 190 | glamorous.div(dynamicFn) 191 | */ 192 | -------------------------------------------------------------------------------- /other/get-exports-code.js: -------------------------------------------------------------------------------- 1 | const htmlTagNames = require('html-tag-names') 2 | const svgTagNames = require('svg-tag-names') 3 | 4 | function capitalize(s) { 5 | return s.slice(0, 1).toUpperCase() + s.slice(1) 6 | } 7 | 8 | function dashToCamelCase(s) { 9 | return s.replace(/-([a-z])/g, ([, char]) => char.toUpperCase()) 10 | } 11 | 12 | const globalNames = Object.getOwnPropertyNames(global) 13 | 14 | function unCollide(name) { 15 | const suffix = 'Tag' 16 | return globalNames.includes(name) ? `${name}${suffix}` : name 17 | } 18 | 19 | const componentExports = htmlTagNames 20 | .concat(svgTagNames) 21 | .filter((tag, index, array) => array.indexOf(tag) === index) 22 | .map(tag => { 23 | const capitalName = capitalize(tag) 24 | const validJsName = dashToCamelCase(capitalName) 25 | // add postfix if name collides with js constructors 26 | const tagName = unCollide(validJsName) 27 | return `export var ${tagName} = glamorous['${capitalName}'];` 28 | }).join`\n` 29 | 30 | const introText = ` 31 | // these exports below are generated 32 | // and will be tree-shaken if you're using Webpack 2 or Rollup 33 | ` 34 | 35 | module.exports = introText + componentExports 36 | -------------------------------------------------------------------------------- /other/glamorous-walkthrough.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/glamorous/031484cd5eff92ff135038200ba49a300859a335/other/glamorous-walkthrough.png -------------------------------------------------------------------------------- /other/jest.config.js: -------------------------------------------------------------------------------- 1 | const jestConfig = require('kcd-scripts/config').jest 2 | 3 | module.exports = Object.assign(jestConfig, { 4 | roots: ['.'], 5 | testURL: 'http://localhost', 6 | testEnvironment: 'jsdom', 7 | }) 8 | -------------------------------------------------------------------------------- /other/logo/compact.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/glamorous/031484cd5eff92ff135038200ba49a300859a335/other/logo/compact.ai -------------------------------------------------------------------------------- /other/logo/compact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/glamorous/031484cd5eff92ff135038200ba49a300859a335/other/logo/compact.png -------------------------------------------------------------------------------- /other/logo/full.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/glamorous/031484cd5eff92ff135038200ba49a300859a335/other/logo/full.ai -------------------------------------------------------------------------------- /other/logo/full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/glamorous/031484cd5eff92ff135038200ba49a300859a335/other/logo/full.png -------------------------------------------------------------------------------- /other/manual-releases.md: -------------------------------------------------------------------------------- 1 | # manual-releases 2 | 3 | This project has an automated release set up. So things are only released when there are 4 | useful changes in the code that justify a release. But sometimes things get messed up one way or another 5 | and we need to trigger the release ourselves. When this happens, simply bump the number below and commit 6 | that with the following commit message based on your needs: 7 | 8 | **Major** 9 | 10 | ``` 11 | fix(release): manually release a major version 12 | 13 | There was an issue with a major release, so this manual-releases.md 14 | change is to release a new major version. 15 | 16 | Reference: # 17 | 18 | BREAKING CHANGE: 19 | ``` 20 | 21 | **Minor** 22 | 23 | ``` 24 | feat(release): manually release a minor version 25 | 26 | There was an issue with a minor release, so this manual-releases.md 27 | change is to release a new minor version. 28 | 29 | Reference: # 30 | ``` 31 | 32 | **Patch** 33 | 34 | ``` 35 | fix(release): manually release a patch version 36 | 37 | There was an issue with a patch release, so this manual-releases.md 38 | change is to release a new patch version. 39 | 40 | Reference: # 41 | ``` 42 | 43 | The number of times we've had to do a manual release is: 2 44 | -------------------------------------------------------------------------------- /other/patch-preact-package-json.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | const packageJsonPath = path.resolve(__dirname, '..', 'preact', 'package.json') 5 | 6 | const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) 7 | packageJson.typings = '../typings/preact/glamorous.d.ts' 8 | 9 | fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`) 10 | -------------------------------------------------------------------------------- /other/setup-tests.js: -------------------------------------------------------------------------------- 1 | import Enzyme from 'enzyme' 2 | import Adapter from 'enzyme-adapter-react-16' 3 | 4 | Enzyme.configure({adapter: new Adapter()}) 5 | -------------------------------------------------------------------------------- /other/swag/shirt-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/glamorous/031484cd5eff92ff135038200ba49a300859a335/other/swag/shirt-dark.png -------------------------------------------------------------------------------- /other/swag/shirt-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/glamorous/031484cd5eff92ff135038200ba49a300859a335/other/swag/shirt-light.png -------------------------------------------------------------------------------- /other/swag/sticker-hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/glamorous/031484cd5eff92ff135038200ba49a300859a335/other/swag/sticker-hex.png -------------------------------------------------------------------------------- /other/swag/sticker-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/glamorous/031484cd5eff92ff135038200ba49a300859a335/other/swag/sticker-square.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "glamorous", 3 | "version": "0.0.0-semantically-released", 4 | "description": "React component styling solved", 5 | "main": "dist/glamorous.cjs.js", 6 | "jsnext:main": "dist/glamorous.esm.js", 7 | "module": "dist/glamorous.esm.js", 8 | "typings": "typings/glamorous.d.ts", 9 | "scripts": { 10 | "add-contributor": "kcd-scripts contributors add", 11 | "prebuild": "rimraf dist preact", 12 | "build": "npm-run-all build:*", 13 | "build:main": "kcd-scripts build --bundle --p-react --no-clean", 14 | "build:tiny": "npm run build:main -- --no-package-json --environment BUILD_TINY", 15 | "postbuild": "node other/patch-preact-package-json.js", 16 | "lint": "kcd-scripts lint", 17 | "test": "kcd-scripts test", 18 | "test:cover": "kcd-scripts test --coverage", 19 | "test:update": "npm run test:cover -s -- --updateSnapshot", 20 | "test:build": "kcd-scripts test --config other/jest.config.js --no-watch", 21 | "build-and-test": "npm run build -s && npm run test:build -s", 22 | "validate": "kcd-scripts validate lint,build-and-test,test:cover", 23 | "setup": "npm install && npm run validate --silent", 24 | "precommit": "kcd-scripts precommit" 25 | }, 26 | "files": [ 27 | "dist", 28 | "typings", 29 | "preact" 30 | ], 31 | "keywords": [ 32 | "react", 33 | "css", 34 | "css-in-js", 35 | "cssinjs", 36 | "styled-components", 37 | "glamor", 38 | "jsxstyle" 39 | ], 40 | "author": "Kent C. Dodds (http://kentcdodds.com/)", 41 | "license": "MIT", 42 | "dependencies": { 43 | "brcast": "^3.0.0", 44 | "csstype": "^2.2.0", 45 | "fast-memoize": "^2.2.7", 46 | "html-tag-names": "^1.1.1", 47 | "is-function": "^1.0.1", 48 | "is-plain-object": "^2.0.4", 49 | "react-html-attributes": "^1.4.2", 50 | "svg-tag-names": "^1.1.0" 51 | }, 52 | "peerDependencies": { 53 | "glamor": ">=2" 54 | }, 55 | "devDependencies": { 56 | "@types/react": "^16.0.33", 57 | "codegen.macro": "^1.0.0", 58 | "cross-spawn": "^6.0.5", 59 | "enzyme": "^3.3.0", 60 | "enzyme-adapter-react-16": "^1.1.1", 61 | "enzyme-to-json": "^3.3.0", 62 | "glamor": "^2.20.37", 63 | "jest-glamor-react": "^3.2.2", 64 | "kcd-scripts": "^0.36.0", 65 | "node": "^8.9.4", 66 | "npm-run-all": "^4.1.2", 67 | "preact": "^8.2.9", 68 | "preval.macro": "^1.0.2", 69 | "prop-types": "^15.5.10", 70 | "react": "^16.2.0", 71 | "react-dom": "^16.2.0", 72 | "react-test-renderer": "^16.2.0", 73 | "rimraf": "^2.6.2", 74 | "tslint": "^5.8.0", 75 | "tslint-microsoft-contrib": "^5.0.0", 76 | "tsutils": "^2.15.0", 77 | "typescript": "^2.9.1" 78 | }, 79 | "eslintConfig": { 80 | "extends": "./node_modules/kcd-scripts/eslint.js", 81 | "rules": { 82 | "import/prefer-default-export": "off", 83 | "react/no-unused-prop-types": "off", 84 | "valid-jsdoc": "off" 85 | } 86 | }, 87 | "eslintIgnore": [ 88 | "node_modules", 89 | "coverage", 90 | "dist", 91 | "storybook-static", 92 | "typings" 93 | ], 94 | "repository": { 95 | "type": "git", 96 | "url": "https://github.com/paypal/glamorous.git" 97 | }, 98 | "bugs": { 99 | "url": "https://github.com/paypal/glamorous/issues" 100 | }, 101 | "homepage": "https://github.com/paypal/glamorous#readme" 102 | } 103 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | const rollupConfig = require('kcd-scripts/config').getRollupConfig() 2 | 3 | const tiny = process.env.TINY 4 | const esm = process.env.BUILD_FORMAT === 'esm' 5 | 6 | Object.assign(rollupConfig, { 7 | external: ['preact', 'react', 'glamor', 'prop-types'], 8 | output: [ 9 | Object.assign(rollupConfig.output[0], { 10 | exports: tiny || !esm ? 'default' : 'named', 11 | name: 'glamorous', 12 | globals: { 13 | react: 'React', 14 | preact: 'preact', 15 | glamor: 'Glamor', 16 | 'prop-types': 'PropTypes', 17 | }, 18 | }), 19 | ], 20 | }) 21 | 22 | if (process.env.BUILD_TINY) { 23 | rollupConfig.output.forEach(o => { 24 | o.file = o.file.replace(/\.js$/, '.tiny.js') 25 | }) 26 | rollupConfig.input = 'src/tiny.js' 27 | } 28 | 29 | module.exports = rollupConfig 30 | -------------------------------------------------------------------------------- /src/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "react/no-deprecated": "off" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/__mocks__/constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | CHANNEL: '__glamorous__', 3 | isPreact: Boolean(process.env.MOCK_PREACT), 4 | } 5 | -------------------------------------------------------------------------------- /src/__tests__/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "react/prop-types": "off" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/create-glamorous.with-component.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`resulting component can have its styles extended further: overriding styles via className 1`] = ` 4 | .glamor-0, 5 | [data-glamor-0] { 6 | color: green; 7 | font-size: 20px; 8 | } 9 | 10 |
13 | `; 14 | 15 | exports[`resulting component can have its styles extended further: overriding styles via css prop 1`] = ` 16 | .glamor-0, 17 | [data-glamor-0] { 18 | color: red; 19 | font-size: 25px; 20 | } 21 | 22 |
25 | `; 26 | 27 | exports[`resulting component can have its styles extended further: overriding styles via wrapping with glamorous 1`] = ` 28 | .glamor-0, 29 | [data-glamor-0] { 30 | color: blue; 31 | font-size: 20px; 32 | } 33 | 34 |
37 | `; 38 | 39 | exports[`withComponent composes the component with provided styles 1`] = ` 40 | .glamor-0, 41 | [data-glamor-0] { 42 | color: red; 43 | font-size: 20px; 44 | } 45 | 46 |
49 | `; 50 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/dev.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`can be configured to use the displayName in the className 1`] = ` 4 | .glamor-0, 5 | [data-glamor-0] { 6 | color: red; 7 | } 8 | 9 |
12 | `; 13 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/index.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`allows you to pass custom props that are allowed 1`] = ` 4 | .glamor-0, 5 | [data-glamor-0] { 6 | padding: 1px; 7 | } 8 | 9 |
13 | `; 14 | 15 | exports[`allows you to specify the tag rendered by a component 1`] = ` 16 | .glamor-0, 17 | [data-glamor-0] { 18 | height: 1px; 19 | width: 2px; 20 | } 21 | 22 | 26 | `; 27 | 28 | exports[`can accept classNames instead of style objects 1`] = ` 29 | .glamor-0, 30 | [data-glamor-0] { 31 | padding-top: 1px; 32 | padding-right: 2px; 33 | padding-bottom: 3px; 34 | padding-left: 4px; 35 | } 36 | 37 |
40 | `; 41 | 42 | exports[`can accept functions which return string class names 1`] = ` 43 | .glamor-0, 44 | [data-glamor-0] { 45 | padding-right: 2px; 46 | padding-bottom: 2px; 47 | } 48 | 49 |
52 | `; 53 | 54 | exports[`can compose built-in glamorous components 1`] = ` 55 | .glamor-0, 56 | [data-glamor-0] { 57 | margin: 2px; 58 | font-size: 1px; 59 | } 60 | 61 |
64 | `; 65 | 66 | exports[`can create custom built-in glamorous components 1`] = ` 67 | .glamor-0, 68 | [data-glamor-0] { 69 | margin: 2px; 70 | font-size: 1px; 71 | } 72 | 73 |
76 | `; 77 | 78 | exports[`can use pre-built glamorous components with css attributes 1`] = ` 79 | .glamor-0, 80 | [data-glamor-0] { 81 | display: -webkit-box; 82 | display: -moz-box; 83 | display: -ms-flexbox; 84 | display: -webkit-flex; 85 | display: flex; 86 | flex-direction: column; 87 | -webkit-box-orient: vertical; 88 | -webkit-box-direction: normal; 89 | -webkit-flex-direction: column; 90 | } 91 | 92 | 97 | `; 98 | 99 | exports[`can use pre-built glamorous components with css prop 1`] = ` 100 | .glamor-0, 101 | [data-glamor-0] { 102 | display: -webkit-box; 103 | display: -moz-box; 104 | display: -ms-flexbox; 105 | display: -webkit-flex; 106 | display: flex; 107 | flex-direction: column; 108 | background: blue; 109 | -webkit-box-orient: vertical; 110 | -webkit-box-direction: normal; 111 | -webkit-flex-direction: column; 112 | } 113 | 114 | 119 | `; 120 | 121 | exports[`does not attempt to merge class names that are not from glamor that begin with css- 1`] = ` 122 | .glamor-0, 123 | [data-glamor-0] { 124 | color: hotpink; 125 | } 126 | 127 |
130 | `; 131 | 132 | exports[`forwards props when the GlamorousComponent.rootEl is known 1`] = ` 133 | .glamor-0, 134 | [data-glamor-0] { 135 | padding: 1px; 136 | margin: 1px; 137 | } 138 | 139 |
142 | He likes it! Hey Mikey! 143 |
144 | `; 145 | 146 | exports[`merges composed component forwardProps 1`] = ` 147 |
150 | `; 151 | 152 | exports[`merges composed component styles for reasonable overrides 1`] = ` 153 | .glamor-0, 154 | [data-glamor-0] { 155 | margin-top: 1px; 156 | margin-right: 2px; 157 | padding-top: 5px; 158 | padding-right: 6px; 159 | margin-bottom: 3px; 160 | margin-left: 4px; 161 | } 162 | 163 |
166 | `; 167 | 168 | exports[`passes an updated theme when theme prop changes: with theme prop of padding 10px 1`] = ` 169 | .glamor-0, 170 | [data-glamor-0] { 171 | color: red; 172 | padding: 10px; 173 | } 174 | 175 | 182 |
185 | 186 | `; 187 | 188 | exports[`passes an updated theme when theme prop changes: with theme prop of padding 20px 1`] = ` 189 | .glamor-0, 190 | [data-glamor-0] { 191 | color: red; 192 | padding: 20px; 193 | } 194 | 195 | 202 |
205 | 206 | `; 207 | 208 | exports[`renders a component with theme properties 1`] = ` 209 | .glamor-0, 210 | [data-glamor-0] { 211 | color: red; 212 | padding: 10px; 213 | } 214 | 215 |
218 | `; 219 | 220 | exports[`sanity test 1`] = ` 221 | .glamor-0, 222 | [data-glamor-0] { 223 | margin-left: 24px; 224 | } 225 | 226 |
229 | `; 230 | 231 | exports[`style objects can be arrays and glamor will merge those 1`] = ` 232 | @media (max-width: 640px) { 233 | .glamor-0, 234 | [data-glamor-0] { 235 | line-height: 1.3; 236 | font-size: 20px; 237 | border-radius: 50%; 238 | } 239 | } 240 | 241 |
244 | `; 245 | 246 | exports[`styles can be functions that accept props 1`] = ` 247 | .glamor-0, 248 | [data-glamor-0] { 249 | margin-top: 2px; 250 | } 251 | 252 |
255 | `; 256 | 257 | exports[`the css prop accepts "GlamorousStyles": css prop with a className 1`] = ` 258 | .glamor-0, 259 | [data-glamor-0] { 260 | color: red; 261 | } 262 | 263 |
266 | `; 267 | 268 | exports[`the css prop accepts "GlamorousStyles": css prop with a function 1`] = ` 269 | .glamor-0, 270 | [data-glamor-0] { 271 | font-size: 10px; 272 | padding: 20px; 273 | margin: 30px; 274 | border: 1px solid pink; 275 | } 276 | 277 |
280 | `; 281 | 282 | exports[`the css prop accepts "GlamorousStyles": css prop with an array 1`] = ` 283 | .glamor-0, 284 | [data-glamor-0] { 285 | margin-top: 1px; 286 | margin-right: 2px; 287 | margin-bottom: 2px; 288 | } 289 | 290 |
293 | `; 294 | 295 | exports[`the css prop accepts "GlamorousStyles": css prop with an object 1`] = ` 296 | .glamor-0, 297 | [data-glamor-0] { 298 | font-size: 12px; 299 | } 300 | 301 |
304 | `; 305 | 306 | exports[`will forward \`color\` to an svg 1`] = ` 307 | 311 | `; 312 | 313 | exports[`will not forward \`color\` to a div 1`] = ` 314 | .glamor-0, 315 | [data-glamor-0] { 316 | color: red; 317 | } 318 | 319 |
322 | `; 323 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/index.with-props.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`composes well with other glamorous components 1`] = ` 4 | .glamor-0, 5 | [data-glamor-0] { 6 | margin-top: 1px; 7 | font-size: 1px; 8 | font-weight: 300; 9 | } 10 | 11 |
14 | `; 15 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/theme-provider.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`does nothing when receive same theme via props: with theme prop of margin 2px 1`] = ` 4 | 11 | `; 12 | 13 | exports[`does nothing when receive same theme via props: with theme prop of margin 2px 2`] = ` 14 | 21 | `; 22 | 23 | exports[`merges nested themes 1`] = ` 24 | .glamor-0, 25 | [data-glamor-0] { 26 | padding: 1px; 27 | margin: 1px; 28 | } 29 | 30 | .glamor-1, 31 | [data-glamor-1] { 32 | padding: 1px; 33 | margin: 2px; 34 | } 35 | 36 |
37 | 45 |
46 | 47 |
50 | 51 | 58 | 59 |
62 | 63 | 64 |
65 |
66 |
67 | `; 68 | 69 | exports[`propagates theme updates through nested ThemeProviders 1`] = ` 70 | .glamor-0, 71 | [data-glamor-0] { 72 | background-color: black; 73 | color: red; 74 | } 75 | 76 | 83 | 86 | 87 |
90 | 91 | 92 | 93 | `; 94 | 95 | exports[`renders a component with theme 1`] = ` 96 | .glamor-0, 97 | [data-glamor-0] { 98 | color: red; 99 | padding: 10px; 100 | } 101 | 102 |
105 | `; 106 | 107 | exports[`renders if children are null 1`] = ` 108 | 115 | `; 116 | 117 | exports[`renders inverted theme 1`] = ` 118 | .glamor-0, 119 | [data-glamor-0] { 120 | background-color: white; 121 | color: palevioletred; 122 | } 123 | 124 | .glamor-1, 125 | [data-glamor-1] { 126 | background-color: palevioletred; 127 | color: white; 128 | } 129 | 130 |
131 |
134 |
137 |
138 | `; 139 | 140 | exports[`theme properties updates get propagated down the tree: with theme prop of padding 10px 1`] = ` 141 | .glamor-0, 142 | [data-glamor-0] { 143 | color: red; 144 | padding: 10px; 145 | } 146 | 147 | 148 | 155 | 156 |
159 | 160 | 161 | 162 | `; 163 | 164 | exports[`theme properties updates get propagated down the tree: with theme prop of padding 20px 1`] = ` 165 | .glamor-0, 166 | [data-glamor-0] { 167 | color: red; 168 | padding: 20px; 169 | } 170 | 171 | 172 | 179 | 180 |
183 | 184 | 185 | 186 | `; 187 | 188 | exports[`throws if inverted theme is not object 1`] = `"[ThemeProvider] Please return an object from your theme function, i.e. theme={() => ({})}!"`; 189 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/tiny.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`should pass glam object prop 1`] = ` 4 | .glamor-0, 5 | [data-glamor-0] { 6 | margin-left: 24px; 7 | font-size: 20px; 8 | } 9 | 10 |
14 | `; 15 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/with-theme.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`pass through when no theme provider found up tree: without theme provider 1`] = ` 4 | 5 | 6 | 9 |
10 | 11 | 12 | 13 | `; 14 | 15 | exports[`renders a non-glamorous component with theme 1`] = ` 16 |
19 | `; 20 | 21 | exports[`theme properties updates get propagated down the tree: with theme prop of padding 10px 1`] = ` 22 | 23 | 30 | 31 | 38 |
45 | 46 | 47 | 48 | 49 | `; 50 | 51 | exports[`theme properties updates get propagated down the tree: with theme prop of padding 20px 1`] = ` 52 | 53 | 60 | 61 | 68 |
75 | 76 | 77 | 78 | 79 | `; 80 | 81 | exports[`works properly with classes: with theme prop of padding 10px 1`] = ` 82 | 83 | 90 | 91 | 98 |
105 | 106 | 107 | 108 | 109 | `; 110 | -------------------------------------------------------------------------------- /src/__tests__/create-glamorous.with-component.js: -------------------------------------------------------------------------------- 1 | /* eslint func-style:0, react/prop-types:0 */ 2 | import React from 'react' 3 | import {render} from 'enzyme' 4 | import * as glamor from 'glamor' 5 | import glamorous from '../' 6 | 7 | jest.mock('../constants') 8 | 9 | test('withComponent composes the component with provided styles', () => { 10 | const Text = glamorous.span({color: 'red', fontSize: 20}) 11 | const View = Text.withComponent('div') 12 | expect(render()).toMatchSnapshot() 13 | }) 14 | 15 | test('withComponent creates a new component with the provided tag', () => { 16 | const Text = glamorous.span({color: 'red', fontSize: 20}) 17 | const View = Text.withComponent('div') 18 | expect(Text.comp).toBe('span') 19 | expect(View.comp).toBe('div') 20 | }) 21 | 22 | test('forwardProps are applied to the new component', () => { 23 | const forwardProps = ['shouldRender'] 24 | const Text = glamorous( 25 | ({shouldRender, ...props}) => (shouldRender ? : null), 26 | {forwardProps}, 27 | )({color: 'red', fontSize: 20}) 28 | const View = Text.withComponent('div') 29 | expect(View.forwardProps).toEqual(forwardProps) 30 | }) 31 | 32 | test('forwardProps can be overridden for the new component', () => { 33 | const Text = glamorous( 34 | ({shouldRender, ...props}) => (shouldRender ? : null), 35 | {forwardProps: ['shouldRender']}, 36 | )({color: 'red', fontSize: 20}) 37 | const forwardProps = ['other-thing'] 38 | const View = Text.withComponent('div', {forwardProps}) 39 | expect(View.forwardProps).toEqual(forwardProps) 40 | }) 41 | 42 | test('forwards options', () => { 43 | const propsToApply = {id: 'my-span', faded: true} 44 | const Text = glamorous 45 | .span({color: 'red', fontSize: 20}) 46 | .withProps(propsToApply) 47 | const View = Text.withComponent('div', {displayName: 'View'}) 48 | expect(View.displayName).toBe('View') 49 | expect(View.propsToApply).toEqual([propsToApply]) 50 | }) 51 | 52 | test('resulting component can have its styles extended further', () => { 53 | const Text = glamorous.span({color: 'red', fontSize: 20}) 54 | const View = Text.withComponent('div') 55 | const StyledView = glamorous(View)({color: 'blue'}) 56 | expect(render()).toMatchSnapshot( 57 | 'overriding styles via wrapping with glamorous', 58 | ) 59 | expect(render()).toMatchSnapshot( 60 | 'overriding styles via css prop', 61 | ) 62 | expect( 63 | render(), 64 | ).toMatchSnapshot('overriding styles via className') 65 | }) 66 | 67 | test('resulting component gets sent valid props', () => { 68 | const A = glamorous.div({}).withComponent('a') 69 | const rendered = render() 70 | expect(rendered.prop('href')).toBe('https://glamorous.rocks') 71 | }) 72 | -------------------------------------------------------------------------------- /src/__tests__/dev.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is here to test things that glamor does when 3 | * NODE_ENV === 'development' 4 | * 5 | * Because glamor stores the value in the module level of 6 | * it's implementation files, we have to do some funny things 7 | * to make sure that glamor gets the value we want. 8 | */ 9 | import React from 'react' 10 | import {render} from 'enzyme' 11 | 12 | const nodeEnv = process.env.NODE_ENV 13 | 14 | jest.mock('../constants') 15 | 16 | beforeEach(() => { 17 | process.env.NODE_ENV = 'development' 18 | jest.resetModules() 19 | }) 20 | 21 | afterEach(() => { 22 | process.env.NODE_ENV = nodeEnv 23 | }) 24 | 25 | test('can be configured to use the displayName in the className', () => { 26 | // eslint-disable-next-line 27 | const glamorous = require('../').default 28 | const MyComp = glamorous(props =>
, { 29 | displayName: 'Hi There', 30 | })({color: 'red'}) 31 | const wrapper = render() 32 | expect(wrapper).toMatchSnapshot() 33 | expect(wrapper[0].attribs.class).toMatch(/hi-there/) 34 | }) 35 | -------------------------------------------------------------------------------- /src/__tests__/index.filter-props.js: -------------------------------------------------------------------------------- 1 | /* eslint func-style:0, react/prop-types:0 */ 2 | import React from 'react' 3 | import {mount as render} from 'enzyme' 4 | import glamorous from '../' 5 | 6 | jest.mock('../constants') 7 | 8 | test('filterProps are not passed to child component', () => { 9 | const filterProps = ['bold'] 10 | const Child = props =>
11 | const Text = glamorous(Child, {filterProps})( 12 | { 13 | fontSize: 20, 14 | }, 15 | ({bold}) => ({ 16 | fontWeight: bold ? 'bold' : undefined, 17 | }), 18 | ) 19 | const rendered = render() 20 | const child = rendered.find(Child) 21 | 22 | expect(rendered.prop('bold')).toBe(true) 23 | expect(rendered.prop('id')).toBe('1') 24 | expect(child.prop('bold')).toBeUndefined() 25 | expect(child.prop('id')).toBe('1') 26 | }) 27 | 28 | test('filterProps takes precedence over built in props', () => { 29 | const filterProps = ['id'] 30 | const Text = glamorous('div', {filterProps})() 31 | 32 | const rendered = render() 33 | const child = rendered.find('div') 34 | 35 | expect(rendered.prop('bar')).toBe('foo') 36 | expect(rendered.prop('id')).toBe('1') 37 | expect(rendered.prop('lang')).toBe('en') 38 | expect(child.prop('bar')).toBeUndefined() 39 | expect(child.prop('id')).toBeUndefined() 40 | expect(child.prop('lang')).toBe('en') 41 | }) 42 | 43 | test('filterProps takes precedence over forwardProps', () => { 44 | const filterProps = ['foo'] 45 | const forwardProps = ['foo', 'bar'] 46 | const Child = ({bar}) =>
47 | const Text = glamorous(Child, { 48 | rootEl: 'div', 49 | filterProps, 50 | forwardProps, 51 | })() 52 | 53 | const rendered = render() 54 | const child = rendered.find(Child) 55 | 56 | expect(rendered.prop('bar')).toBe('foo') 57 | expect(rendered.prop('foo')).toBe('bar') 58 | expect(child.prop('foo')).toBeUndefined() 59 | expect(child.prop('bar')).toBe('foo') 60 | expect(child.find('div').prop('id')).toBe('foo') 61 | }) 62 | 63 | test('filterProps takes precedence over cssOverrides', () => { 64 | const filterProps = ['foo'] 65 | const Text = glamorous('div', { 66 | filterProps, 67 | propsAreCssOverrides: true, 68 | })(({foo}) => (foo ? {padding: '1'} : undefined)) 69 | 70 | const rendered = render() 71 | const child = rendered.find('div') 72 | 73 | expect(rendered.prop('margin')).toBe(1) 74 | expect(rendered.prop('foo')).toBe('bar') 75 | expect(child.prop('foo')).toBeUndefined() 76 | expect(child.prop('margin')).toBeUndefined() 77 | }) 78 | 79 | test('filterProps are applied to new component', () => { 80 | const filterProps = ['shoulRender'] 81 | const Child = props =>
82 | const Text = glamorous(Child, {filterProps})({ 83 | color: 'red', 84 | fontSize: 20, 85 | }) 86 | const View = Text.withComponent('div') 87 | 88 | expect(View.filterProps).toEqual(filterProps) 89 | }) 90 | 91 | test('filterProps can be overridden for the new component', () => { 92 | const Child = props =>
93 | const Text = glamorous(Child, {filterProps: ['shouldRender']})({ 94 | color: 'red', 95 | fontSize: 20, 96 | }) 97 | const filterProps = ['other-thing'] 98 | const View = Text.withComponent('div', {filterProps}) 99 | 100 | expect(View.filterProps).toEqual(filterProps) 101 | }) 102 | 103 | // vim: set ts=2 sw=2 tw=80: 104 | -------------------------------------------------------------------------------- /src/__tests__/index.should-class-name-update.js: -------------------------------------------------------------------------------- 1 | /* eslint func-style:0, react/prop-types:0 */ 2 | import React, {Component} from 'react' 3 | import {css as cssMock} from 'glamor' 4 | import {mount} from 'enzyme' 5 | import glamorous from '../' 6 | 7 | jest.mock('../constants') 8 | 9 | jest.mock('glamor', () => { 10 | const realGlamor = require.requireActual('glamor') 11 | return Object.assign(realGlamor, { 12 | css: jest.fn(() => 'css-mock-12345'), 13 | }) 14 | }) 15 | 16 | beforeEach(() => { 17 | cssMock.mockClear() 18 | }) 19 | 20 | test('calls shouldClassNameUpdate and updates accordingly', () => { 21 | const shouldClassNameUpdate = jest.fn(() => false) 22 | const pureDivFactory = glamorous('div', { 23 | shouldClassNameUpdate, 24 | }) 25 | const Div = pureDivFactory({marginLeft: 1}) 26 | const wrapper = mount(
) 27 | expect(shouldClassNameUpdate).toHaveBeenCalledTimes(0) 28 | expect(cssMock).toHaveBeenCalledTimes(1) 29 | 30 | wrapper.setProps({id: 'goodbye'}) 31 | expect(shouldClassNameUpdate).toHaveBeenCalledTimes(1) 32 | // It was not called again! 33 | expect(cssMock).toHaveBeenCalledTimes(1) 34 | 35 | shouldClassNameUpdate.mockReturnValueOnce(true) 36 | wrapper.setProps({id: 'hi there', css: {marginLeft: 3}}) 37 | expect(shouldClassNameUpdate).toHaveBeenCalledTimes(2) 38 | // It was called again! 39 | expect(cssMock).toHaveBeenCalledTimes(2) 40 | 41 | // let's make sure that we're calling the function 42 | // with the right args. 43 | const props = expect.objectContaining({ 44 | id: 'goodbye', 45 | css: {marginLeft: 2}, 46 | }) 47 | const nextProps = expect.objectContaining({ 48 | css: {marginLeft: 3}, 49 | id: 'hi there', 50 | }) 51 | // ok ok, I'm not really testing `context` very thoroughly, 52 | // but can you blame me? It's so totally boring... 53 | // and I'm pretty sure it's working fine 54 | const context = expect.objectContaining({__glamorous__: undefined}) 55 | const nextContext = expect.objectContaining({__glamorous__: undefined}) 56 | expect(shouldClassNameUpdate).toHaveBeenCalledWith( 57 | props, 58 | nextProps, 59 | context, 60 | nextContext, 61 | ) 62 | }) 63 | 64 | test('shouldClassNameUpdate is specific to the component instance', () => { 65 | const shouldClassNameUpdate = jest.fn() 66 | 67 | // using a class because wrapper.instance() 68 | // returns null when using a stateless function 69 | // eslint-disable-next-line react/prefer-stateless-function 70 | class Container extends Component { 71 | render() { 72 | const {children} = this.props 73 | return
{children()}
74 | } 75 | } 76 | 77 | const Div = glamorous('div', { 78 | shouldClassNameUpdate, 79 | })({ 80 | padding: 10, 81 | }) 82 | 83 | // On the third render (second forceUpdate()), change the css props 84 | let count = 0 85 | const wrapper = mount( 86 | 87 | {() => { 88 | count++ 89 | 90 | return ( 91 |
92 |
93 |
94 |
95 | ) 96 | }} 97 | , 98 | ) 99 | 100 | expect(shouldClassNameUpdate).toHaveBeenCalledTimes(0) 101 | 102 | wrapper.instance().forceUpdate() 103 | 104 | expect(shouldClassNameUpdate).toHaveBeenCalledTimes(2) 105 | expect(shouldClassNameUpdate.mock.calls[0]).toEqual([ 106 | expect.objectContaining({css: {color: 'red'}}), 107 | expect.objectContaining({css: {color: 'red'}}), 108 | expect.objectContaining({__glamorous__: undefined}), 109 | expect.objectContaining({__glamorous__: undefined}), 110 | ]) 111 | expect(shouldClassNameUpdate.mock.calls[1]).toEqual([ 112 | expect.objectContaining({css: {color: 'blue'}}), 113 | expect.objectContaining({css: {color: 'blue'}}), 114 | expect.objectContaining({__glamorous__: undefined}), 115 | expect.objectContaining({__glamorous__: undefined}), 116 | ]) 117 | 118 | wrapper.instance().forceUpdate() 119 | 120 | expect(shouldClassNameUpdate).toHaveBeenCalledTimes(4) 121 | expect(shouldClassNameUpdate.mock.calls[2]).toEqual([ 122 | expect.objectContaining({css: {color: 'red'}}), 123 | expect.objectContaining({css: {color: 'purple'}}), 124 | expect.objectContaining({__glamorous__: undefined}), 125 | expect.objectContaining({__glamorous__: undefined}), 126 | ]) 127 | expect(shouldClassNameUpdate.mock.calls[3]).toEqual([ 128 | expect.objectContaining({css: {color: 'blue'}}), 129 | expect.objectContaining({css: {color: 'green'}}), 130 | expect.objectContaining({__glamorous__: undefined}), 131 | expect.objectContaining({__glamorous__: undefined}), 132 | ]) 133 | }) 134 | -------------------------------------------------------------------------------- /src/__tests__/index.with-config.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import glamorous from '../' 3 | 4 | jest.mock('../constants') 5 | 6 | test('withConfig returns a glamorous component with the new config', () => { 7 | const config = { 8 | displayName: 'MyDiv', 9 | propsAreCssOverrides: true, 10 | } 11 | const styles = {a: 'b'} 12 | const MyDiv = glamorous.strong.withConfig(config)(styles) 13 | expect(ownProps(MyDiv)).toMatchObject({ 14 | ...config, 15 | comp: 'strong', 16 | rootEl: 'strong', 17 | forwardProps: [], 18 | isGlamorousComponent: true, 19 | propsToApply: [], 20 | styles: [styles], 21 | }) 22 | }) 23 | 24 | test('withConfig works for non-built-in components', () => { 25 | const config = { 26 | displayName: 'MyDiv', 27 | propsAreCssOverrides: true, 28 | } 29 | const MyComp = ({shouldRender, ...props}) => 30 | shouldRender ?
: null 31 | const styles = {a: 'b'} 32 | const forwardProps = ['shouldRender'] 33 | const MyDiv = glamorous(MyComp, {rootEl: 'div', forwardProps}).withConfig( 34 | config, 35 | )(styles) 36 | expect(ownProps(MyDiv)).toMatchObject({ 37 | ...config, 38 | comp: MyComp, 39 | rootEl: 'div', 40 | forwardProps, 41 | isGlamorousComponent: true, 42 | propsToApply: [], 43 | styles: [styles], 44 | }) 45 | }) 46 | 47 | function ownProps(thing) { 48 | return Reflect.ownKeys(thing).reduce((o, k) => { 49 | o[k] = thing[k] 50 | return o 51 | }, {}) 52 | } 53 | -------------------------------------------------------------------------------- /src/__tests__/index.with-props.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {render} from 'enzyme' 3 | import glamorous from '../' 4 | 5 | jest.mock('../constants') 6 | const expectContext = expect.objectContaining({__glamorous__: undefined}) 7 | 8 | test('allows you to specify props as an object', () => { 9 | const withPropsObject = {propA: 'a', propB: 'b'} 10 | const dynamicStyles = jest.fn() 11 | const MyDiv = glamorous.div(dynamicStyles).withProps(withPropsObject) 12 | render() 13 | expect(dynamicStyles).toHaveBeenCalledTimes(1) 14 | expect(dynamicStyles).toHaveBeenCalledWith( 15 | expect.objectContaining({ 16 | ...withPropsObject, 17 | otherProp: true, 18 | }), 19 | expectContext, 20 | ) 21 | }) 22 | 23 | test('composes well with other glamorous components', () => { 24 | const MyDiv = glamorous.div({marginTop: 1}, () => ({fontSize: 1})).withProps() 25 | const MyIdDiv = glamorous(MyDiv)(() => ({fontWeight: 300})).withProps() 26 | expect(render()).toMatchSnapshot() 27 | }) 28 | 29 | test('allows you to specify props as a function', () => { 30 | const withPropsObject = {propA: 'a', propB: 'b'} 31 | const withPropsFunction = jest.fn(() => withPropsObject) 32 | const dynamicStyles = jest.fn() 33 | const MyDiv = glamorous.div(dynamicStyles).withProps(withPropsFunction) 34 | render() 35 | expect(withPropsFunction).toHaveBeenCalledTimes(1) 36 | expect(withPropsFunction).toHaveBeenCalledWith( 37 | expect.objectContaining({ 38 | otherProp: true, 39 | }), 40 | expectContext, 41 | ) 42 | expect(dynamicStyles).toHaveBeenCalledTimes(1) 43 | expect(dynamicStyles).toHaveBeenCalledWith( 44 | expect.objectContaining({ 45 | ...withPropsObject, 46 | otherProp: true, 47 | }), 48 | expectContext, 49 | ) 50 | }) 51 | 52 | test('allows you to provide any number of arguments for props', () => { 53 | // important bits of this test: 54 | // 1. props shallowly compose together 55 | // 2. composition order (later has greater precedence): 56 | // 1. withProps option when createing a glamorousComponentFactory 57 | // 2. arguments to withProps (in the same order as Object.assign) 58 | // 3. props applied to the element when rendered 59 | // 3. composition works as you might expect when wrapping a glamorousComponent 60 | const withPropsObject1 = {propA: 1, propB: 1} 61 | const withPropsObject2 = {propB: 2, propC: 2} 62 | const withPropsObject3 = {propC: 3, propD: 3} 63 | const withPropsObject4 = {propD: 4, propE: 4} 64 | const withPropsObject5 = {propE: 5, propF: 5} 65 | const withPropsObject6 = {propF: 6, propG: 6} 66 | const withPropsObject7 = {propG: 7, propH: 7} 67 | const propsRendered = {propZ: 99, propA: 99} 68 | 69 | const withPropsFunction2 = jest.fn(() => withPropsObject2) 70 | const withPropsFunction4 = jest.fn(() => withPropsObject4) 71 | const dynamicStyles = jest.fn() 72 | const MyDiv = glamorous('div', {withProps: withPropsObject1})({}).withProps( 73 | withPropsFunction2, 74 | ) 75 | const MyComposedDiv = glamorous(MyDiv, { 76 | withProps: [withPropsObject3, withPropsFunction4], 77 | })(dynamicStyles).withProps(withPropsObject5, [ 78 | withPropsObject6, 79 | withPropsObject7, 80 | ]) 81 | render() 82 | 83 | expect(withPropsFunction2).toHaveBeenCalledTimes(1) 84 | expect(withPropsFunction2).toHaveBeenCalledWith( 85 | expect.objectContaining({ 86 | ...withPropsObject1, 87 | ...propsRendered, 88 | }), 89 | expectContext, 90 | ) 91 | 92 | expect(withPropsFunction4).toHaveBeenCalledTimes(1) 93 | expect(withPropsFunction4).toHaveBeenCalledWith( 94 | expect.objectContaining({ 95 | // this order is important and part of what we're testing here 96 | ...withPropsObject1, 97 | ...withPropsObject2, 98 | ...withPropsObject3, 99 | ...propsRendered, 100 | }), 101 | expectContext, 102 | ) 103 | 104 | expect(dynamicStyles).toHaveBeenCalledTimes(1) 105 | expect(dynamicStyles).toHaveBeenCalledWith( 106 | expect.objectContaining({ 107 | // this order is important and part of what we're testing here 108 | ...withPropsObject1, 109 | ...withPropsObject2, 110 | ...withPropsObject3, 111 | ...withPropsObject4, 112 | ...withPropsObject5, 113 | ...withPropsObject6, 114 | ...withPropsObject7, 115 | ...propsRendered, 116 | }), 117 | expectContext, 118 | ) 119 | }) 120 | 121 | test('custom component with props to filter custom props', () => { 122 | const MyComp = glamorous(props =>
, {rootEl: 'div'})({}) 123 | const MyCompWithProps = MyComp.withProps({propA: true}) 124 | const rendered = render().find('div') 125 | expect(rendered.prop('propA')).toBeUndefined() 126 | expect(rendered.prop('propB')).toBeUndefined() 127 | }) 128 | -------------------------------------------------------------------------------- /src/__tests__/preact.js: -------------------------------------------------------------------------------- 1 | jest.mock('../constants') 2 | 3 | beforeEach(() => { 4 | process.env.MOCK_PREACT = true 5 | jest.resetModules() 6 | jest.mock('react', () => require('preact')) 7 | }) 8 | 9 | afterEach(() => { 10 | delete process.env.MOCK_PREACT 11 | jest.unmock('react') 12 | }) 13 | 14 | test('should add preact-specific properties for preact build', () => { 15 | const reactProps = require('../react-props').default 16 | expect(reactProps).toContain('autofocus') 17 | }) 18 | 19 | test('should add PropTypes if not present', () => { 20 | const {PropTypes} = require('../react-compat') 21 | expect(PropTypes).toEqual(expect.any(Function)) 22 | }) 23 | 24 | test('added fake PropTypes should not throw', () => { 25 | const {PropTypes} = require('../react-compat') 26 | expect( 27 | () => PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired, 28 | ).not.toThrow() 29 | }) 30 | 31 | test('should not overwrite existing PropTypes', () => { 32 | const mockPropTypes = {} 33 | jest.mock('react', () => { 34 | return {PropTypes: mockPropTypes} 35 | }) 36 | const {PropTypes} = require('../react-compat') 37 | expect(PropTypes).toBe(mockPropTypes) 38 | }) 39 | 40 | test('should add Children to preact if not present', () => { 41 | // eslint-disable-next-line no-unused-vars 42 | const {PropTypes} = require('../react-compat') 43 | const React = require('react') 44 | expect(React.Children).toEqual( 45 | expect.objectContaining({ 46 | count: expect.any(Function), 47 | only: expect.any(Function), 48 | }), 49 | ) 50 | }) 51 | -------------------------------------------------------------------------------- /src/__tests__/refs.js: -------------------------------------------------------------------------------- 1 | /* eslint func-style:0, react/prop-types:0 */ 2 | import React from 'react' 3 | import {mount} from 'enzyme' 4 | import glamorous from '../' 5 | 6 | const nodeEnv = process.env.NODE_ENV 7 | 8 | jest.mock('../constants') 9 | 10 | afterEach(() => { 11 | process.env.NODE_ENV = nodeEnv 12 | }) 13 | 14 | test('should receive inner ref if specified', () => { 15 | const getRef = jest.fn() 16 | const Comp = glamorous.div({ 17 | marginLeft: '24px', 18 | }) 19 | 20 | mount() 21 | 22 | expect(getRef).toHaveBeenCalled() 23 | }) 24 | 25 | test('should pass inner ref to underlying component if forwarded', () => { 26 | let node = null 27 | const getRef = n => (node = n) 28 | 29 | class InnerComp extends React.Component { 30 | render() { 31 | return ( 32 |
33 | 34 |
35 | ) 36 | } 37 | } 38 | 39 | const Comp = glamorous(InnerComp, {forwardProps: ['innerRef']})({ 40 | marginLeft: '24px', 41 | }) 42 | 43 | mount() 44 | 45 | expect(node).toBeInstanceOf(HTMLElement) 46 | expect(node.tagName).toBe('SPAN') 47 | }) 48 | 49 | test('should not pass inner ref to underlying component if not forwarded', () => { 50 | let node = null 51 | const getRef = n => (node = n) 52 | 53 | class InnerComp extends React.Component { 54 | render() { 55 | return ( 56 |
57 | 58 |
59 | ) 60 | } 61 | } 62 | 63 | const Comp = glamorous(InnerComp)({ 64 | marginLeft: '24px', 65 | }) 66 | 67 | mount() 68 | 69 | expect(node).toBeInstanceOf(InnerComp) 70 | }) 71 | 72 | test('should pass inner ref to underlying component if forwarded and rootEl is set', () => { 73 | let node = null 74 | const getRef = n => (node = n) 75 | 76 | class InnerComp extends React.Component { 77 | render() { 78 | return ( 79 |
80 | 81 |
82 | ) 83 | } 84 | } 85 | 86 | const Comp = glamorous(InnerComp, { 87 | rootEl: 'div', 88 | forwardProps: ['innerRef'], 89 | })({ 90 | marginLeft: '24px', 91 | }) 92 | 93 | mount() 94 | 95 | expect(node).toBeInstanceOf(HTMLElement) 96 | expect(node.tagName).toBe('SPAN') 97 | }) 98 | 99 | test('should pass inner ref to underlying component if forwarded and filterProps are specified', () => { 100 | let node = null 101 | const getRef = n => (node = n) 102 | 103 | class InnerComp extends React.Component { 104 | render() { 105 | return ( 106 |
107 | 108 |
109 | ) 110 | } 111 | } 112 | 113 | const Comp = glamorous(InnerComp, { 114 | filterProps: ['noname'], 115 | forwardProps: ['innerRef'], 116 | })({ 117 | marginLeft: '24px', 118 | }) 119 | 120 | mount() 121 | 122 | expect(node).toBeInstanceOf(HTMLElement) 123 | expect(node.tagName).toBe('SPAN') 124 | }) 125 | -------------------------------------------------------------------------------- /src/__tests__/should-forward-property.js: -------------------------------------------------------------------------------- 1 | import reactHTMLAttributes from 'react-html-attributes' 2 | import shouldForwardProperty from '../should-forward-property' 3 | import reactProps from '../react-props' 4 | 5 | jest.mock('../constants') 6 | // Used for css overrides API 7 | const validCssProps = ['color', 'height', 'width'] 8 | 9 | function pickRandomFromArray(array) { 10 | return array[Math.floor(Math.random() * array.length)] 11 | } 12 | 13 | test('should forward a property with function as the tag', () => { 14 | expect(shouldForwardProperty(() => true, 'style')).toBeTruthy() 15 | }) 16 | 17 | test('should forward a random react-supported html attribute', () => { 18 | const randomTag = pickRandomFromArray(Object.keys(reactHTMLAttributes)) 19 | const randomAttribute = pickRandomFromArray( 20 | reactHTMLAttributes['*'].concat(reactHTMLAttributes[randomTag]), 21 | ) 22 | 23 | expect(shouldForwardProperty(randomTag, randomAttribute)).toBeTruthy() 24 | }) 25 | 26 | // this is one example of html attributes not supported by React 27 | // 'class' is not supported because it's a js reserved word 28 | test("should not forward a 'class' attribute on a 'div' tag", () => { 29 | expect(shouldForwardProperty('div', 'class')).toBeFalsy() 30 | }) 31 | 32 | // test an invalid html attribute 33 | test("should not forward a 'xyz' attribute on a 'img' tag", () => { 34 | expect(shouldForwardProperty('img', 'xyz')).toBeFalsy() 35 | }) 36 | 37 | test('should forward a data-* attribute', () => { 38 | expect(shouldForwardProperty('div', 'data-age')).toBeTruthy() 39 | }) 40 | 41 | test('should forward a aria-* attribute', () => { 42 | expect(shouldForwardProperty('div', 'aria-material')).toBeTruthy() 43 | }) 44 | 45 | test('should forward a random react-specific attribute', () => { 46 | expect( 47 | shouldForwardProperty('div', pickRandomFromArray(reactProps)), 48 | ).toBeTruthy() 49 | }) 50 | 51 | test("should not forward a valid css property with a 'div' tag", () => { 52 | validCssProps.forEach(cssProp => 53 | expect(shouldForwardProperty('div', cssProp)).toBeFalsy(), 54 | ) 55 | }) 56 | 57 | test("should forward a valid css property with a 'svg' tag", () => { 58 | validCssProps.forEach(cssProp => 59 | expect(shouldForwardProperty('svg', cssProp)).toBeTruthy(), 60 | ) 61 | }) 62 | 63 | test("should forward the 'fill' property with a 'rect' tag", () => { 64 | expect(shouldForwardProperty('rect', 'fill')).toBeTruthy() 65 | }) 66 | 67 | test("should forward the 'cx' property with a 'circle' tag", () => { 68 | expect(shouldForwardProperty('circle', 'cx')).toBeTruthy() 69 | }) 70 | -------------------------------------------------------------------------------- /src/__tests__/theme-provider.js: -------------------------------------------------------------------------------- 1 | /* eslint func-style:0 */ 2 | import React, {Component} from 'react' 3 | import {render, mount} from 'enzyme' 4 | import glamorous from '../' 5 | import ThemeProvider from '../theme-provider' 6 | import {CHANNEL} from '../constants' 7 | 8 | jest.mock('../constants') 9 | 10 | const getMockedContext = () => ({ 11 | [CHANNEL]: { 12 | getState: () => {}, 13 | setState: () => {}, 14 | subscribe: () => 1, 15 | unsubscribe: jest.fn(), 16 | }, 17 | }) 18 | 19 | test('renders a component with theme', () => { 20 | const Comp = glamorous.div( 21 | { 22 | color: 'red', 23 | }, 24 | ({theme}) => ({padding: theme.padding}), 25 | ) 26 | expect( 27 | render( 28 | 29 | 30 | , 31 | ), 32 | ).toMatchSnapshot() 33 | }) 34 | 35 | test('renders inverted theme', () => { 36 | const Comp = glamorous.div(({theme: {fg, bg}}) => ({ 37 | backgroundColor: bg, 38 | color: fg, 39 | })) 40 | const theme = { 41 | fg: 'palevioletred', 42 | bg: 'white', 43 | } 44 | const invertTheme = ({fg, bg}) => ({ 45 | fg: bg, 46 | bg: fg, 47 | }) 48 | expect( 49 | render( 50 | 51 |
52 | 53 | 54 | 55 | 56 |
57 |
, 58 | ), 59 | ).toMatchSnapshot() 60 | }) 61 | 62 | test('throws if inverted theme is not object', () => { 63 | const Comp = glamorous.div(({theme: {fg, bg}}) => ({ 64 | backgroundColor: bg, 65 | color: fg, 66 | })) 67 | const theme = { 68 | fg: 'palevioletred', 69 | bg: 'white', 70 | } 71 | const invertTheme = () => 1 72 | expect(() => 73 | render( 74 | 75 |
76 | 77 | 78 | 79 | 80 |
81 |
, 82 | ), 83 | ).toThrowErrorMatchingSnapshot() 84 | }) 85 | 86 | test('theme properties updates get propagated down the tree', () => { 87 | class Parent extends Component { 88 | state = { 89 | padding: 10, 90 | } 91 | 92 | render() { 93 | return ( 94 | 95 | 96 | 97 | ) 98 | } 99 | } 100 | const Child = glamorous.div( 101 | { 102 | color: 'red', 103 | }, 104 | ({theme}) => ({padding: theme.padding}), 105 | ) 106 | const wrapper = mount() 107 | expect(wrapper).toMatchSnapshot(`with theme prop of padding 10px`) 108 | wrapper.setState({padding: 20}) 109 | expect(wrapper).toMatchSnapshot(`with theme prop of padding 20px`) 110 | }) 111 | 112 | test('merges nested themes', () => { 113 | const One = glamorous.div({}, ({theme: {padding, margin}}) => ({ 114 | padding, 115 | margin, 116 | })) 117 | const Two = glamorous.div({}, ({theme: {padding, margin}}) => ({ 118 | padding, 119 | margin, 120 | })) 121 | expect( 122 | mount( 123 |
124 | 125 |
126 | 127 | 128 | 129 | 130 |
131 |
132 |
, 133 | ), 134 | ).toMatchSnapshot() 135 | }) 136 | 137 | test('propagates theme updates through nested ThemeProviders', () => { 138 | const theme = {bg: 'white'} 139 | const augment = outerTheme => Object.assign({}, outerTheme, {color: 'red'}) 140 | const update = {bg: 'black'} 141 | 142 | const Child = glamorous.div(({theme: {bg, color}}) => ({ 143 | backgroundColor: bg, 144 | color, 145 | })) 146 | 147 | const wrapper = mount( 148 | 149 | 150 | 151 | 152 | , 153 | ) 154 | 155 | wrapper.setProps({theme: Object.assign({}, theme, update)}) 156 | 157 | expect(wrapper).toMatchSnapshot() 158 | }) 159 | 160 | test('renders if children are null', () => { 161 | expect( 162 | mount( 163 | {false &&
}, 164 | ), 165 | ).toMatchSnapshot() 166 | }) 167 | 168 | test('cleans up outer theme subscription when unmounts', () => { 169 | const context = getMockedContext() 170 | const wrapper = mount(, {context}) 171 | wrapper.unmount() 172 | expect(context[CHANNEL].unsubscribe).toHaveBeenCalled() 173 | }) 174 | 175 | test('does nothing when receive same theme via props', () => { 176 | const theme = {margin: 2} 177 | const wrapper = mount() 178 | expect(wrapper).toMatchSnapshot(`with theme prop of margin 2px`) 179 | wrapper.setProps({theme}) 180 | expect(wrapper).toMatchSnapshot(`with theme prop of margin 2px`) 181 | }) 182 | -------------------------------------------------------------------------------- /src/__tests__/tiny.js: -------------------------------------------------------------------------------- 1 | /* eslint func-style:0, react/prop-types:0 */ 2 | import React from 'react' 3 | import {render, mount} from 'enzyme' 4 | 5 | import glamorousTiny from '../tiny' 6 | 7 | jest.mock('../constants') 8 | 9 | test('should pass glam object prop', () => { 10 | const dynamicStyles = jest.fn(({glam: {big}}) => ({ 11 | fontSize: big ? 20 : 10, 12 | })) 13 | const Comp = glamorousTiny('div')( 14 | { 15 | marginLeft: '24px', 16 | }, 17 | dynamicStyles, 18 | ) 19 | 20 | const glam = {big: true} 21 | const theme = {color: 'blue'} 22 | expect( 23 | render(), 24 | ).toMatchSnapshot() 25 | expect(dynamicStyles).toHaveBeenCalledTimes(1) 26 | const props = { 27 | glam, 28 | id: 'hey-there', 29 | theme, 30 | } 31 | const context = expect.any(Object) // the context 32 | expect(dynamicStyles).toHaveBeenCalledWith(props, context) 33 | }) 34 | 35 | test('should pass inner ref to underlying component if forwarded', () => { 36 | let node = null 37 | const getRef = n => (node = n) 38 | 39 | class InnerComp extends React.Component { 40 | render() { 41 | return ( 42 |
43 | 44 |
45 | ) 46 | } 47 | } 48 | 49 | const Comp = glamorousTiny(InnerComp, {forwardProps: ['innerRef']})({ 50 | marginLeft: '24px', 51 | }) 52 | 53 | mount() 54 | 55 | expect(node).toBeInstanceOf(HTMLElement) 56 | expect(node.tagName).toBe('SPAN') 57 | }) 58 | 59 | test('should not pass inner ref to underlying component if not forwarded', () => { 60 | let node = null 61 | const getRef = n => (node = n) 62 | 63 | class InnerComp extends React.Component { 64 | render() { 65 | return ( 66 |
67 | 68 |
69 | ) 70 | } 71 | } 72 | 73 | const Comp = glamorousTiny(InnerComp)({ 74 | marginLeft: '24px', 75 | }) 76 | 77 | mount() 78 | 79 | expect(node).toBeInstanceOf(InnerComp) 80 | }) 81 | -------------------------------------------------------------------------------- /src/__tests__/typescript.js: -------------------------------------------------------------------------------- 1 | import spawn from 'cross-spawn' 2 | 3 | test('Typescript React', () => { 4 | const typescriptCompilation = spawn.sync('./node_modules/.bin/tsc', [ 5 | '-p', 6 | './tsconfig.react.json', 7 | ]) 8 | 9 | const output = typescriptCompilation.stdout.toString() 10 | 11 | expect(output).toMatchSnapshot('Typescript expected failures') 12 | }) 13 | 14 | test('Typescript Preact', () => { 15 | const typescriptCompilation = spawn.sync('./node_modules/.bin/tsc', [ 16 | '-p', 17 | './tsconfig.preact.json', 18 | ]) 19 | 20 | const output = typescriptCompilation.stdout.toString() 21 | 22 | expect(output).toMatchSnapshot('Typescript expected failures') 23 | }) 24 | -------------------------------------------------------------------------------- /src/__tests__/with-theme.js: -------------------------------------------------------------------------------- 1 | /* eslint func-style:0 */ 2 | import React, {Component} from 'react' 3 | import {render, mount} from 'enzyme' 4 | 5 | import withTheme from '../with-theme' 6 | import ThemeProvider from '../theme-provider' 7 | import {CHANNEL} from '../constants' 8 | import {PropTypes} from '../react-compat' 9 | 10 | jest.mock('../constants') 11 | 12 | const getMockedContext = () => ({ 13 | [CHANNEL]: { 14 | getState: () => {}, 15 | setState: () => {}, 16 | subscribe: () => 1, 17 | unsubscribe: jest.fn(), 18 | }, 19 | }) 20 | 21 | test('renders a non-glamorous component with theme', () => { 22 | const CompWithTheme = withTheme(({theme: {padding}}) => ( 23 |
24 | )) 25 | expect( 26 | render( 27 | 28 | 29 | , 30 | ), 31 | ).toMatchSnapshot() 32 | }) 33 | 34 | test('theme properties updates get propagated down the tree', () => { 35 | class Parent extends Component { 36 | state = { 37 | padding: 10, 38 | } 39 | 40 | render() { 41 | return ( 42 | 43 | 44 | 45 | ) 46 | } 47 | } 48 | 49 | const Child = withTheme(({theme: {padding}}) =>
) 50 | const wrapper = mount() 51 | expect(wrapper).toMatchSnapshot(`with theme prop of padding 10px`) 52 | wrapper.setState({padding: 20}) 53 | expect(wrapper).toMatchSnapshot(`with theme prop of padding 20px`) 54 | }) 55 | 56 | test('works properly with classes', () => { 57 | /* eslint-disable react/prefer-stateless-function */ 58 | class Child extends Component { 59 | render() { 60 | const {theme: {padding}} = this.props 61 | return
62 | } 63 | } 64 | 65 | Child.propTypes = { 66 | theme: PropTypes.object, 67 | } 68 | 69 | const ChildWithTheme = withTheme(Child) 70 | 71 | class Parent extends Component { 72 | state = { 73 | padding: 10, 74 | } 75 | 76 | render() { 77 | return ( 78 | 79 | 80 | 81 | ) 82 | } 83 | } 84 | 85 | const wrapper = mount() 86 | expect(wrapper).toMatchSnapshot(`with theme prop of padding 10px`) 87 | }) 88 | 89 | test('pass through when no theme provider found up tree', () => { 90 | /* eslint-disable no-console */ 91 | const originalWarn = console.warn 92 | console.warn = jest.fn() 93 | 94 | const Child = withTheme(() =>
) 95 | const Parent = () => 96 | const wrapper = mount() 97 | 98 | expect(wrapper).toMatchSnapshot(`without theme provider`) 99 | expect(console.warn).toHaveBeenCalledTimes(1) 100 | expect(console.warn).toHaveBeenCalledWith( 101 | // eslint-disable-next-line max-len 102 | `glamorous warning: Expected component called "FunctionComponent" which uses withTheme to be within a ThemeProvider but none was found.`, 103 | ) 104 | console.warn = originalWarn 105 | }) 106 | 107 | test('does not warn when NODE_ENV is set to `production`', () => { 108 | /* eslint-disable no-console */ 109 | const originalWarn = console.warn 110 | console.warn = jest.fn() 111 | 112 | const originalEnv = process.env.NODE_ENV 113 | process.env.NODE_ENV = 'production' 114 | 115 | const Child = withTheme(() =>
) 116 | const Parent = () => 117 | mount() 118 | 119 | expect(console.warn).not.toHaveBeenCalled() 120 | 121 | console.warn = originalWarn 122 | process.env.NODE_ENV = originalEnv 123 | }) 124 | 125 | test('unsubscribes from theme updates on unmount', () => { 126 | const Child = withTheme(() =>
) 127 | const context = getMockedContext() 128 | const wrapper = mount( 129 | 130 | 131 | , 132 | { 133 | context, 134 | }, 135 | ) 136 | wrapper.unmount() 137 | expect(context[CHANNEL].unsubscribe).toHaveBeenCalled() 138 | }) 139 | 140 | test('ignores context if a theme props is passed', () => { 141 | const context = getMockedContext() 142 | const Comp = withTheme(() =>
) 143 | const wrapper = mount(, {context}) 144 | wrapper.unmount() 145 | expect(context[CHANNEL].unsubscribe).toHaveBeenCalledTimes(0) 146 | }) 147 | -------------------------------------------------------------------------------- /src/cjs-entry.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | import * as glamorousStar from './' 3 | 4 | const glamorous = glamorousStar.default 5 | 6 | Object.assign( 7 | glamorous, 8 | Object.keys(glamorousStar).reduce((e, prop) => { 9 | if (prop !== 'default') { 10 | // eslint-disable-next-line import/namespace 11 | e[prop] = glamorousStar[prop] 12 | } 13 | return e 14 | }, {}), 15 | ) 16 | 17 | export default glamorous 18 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | import preval from 'preval.macro' 3 | 4 | export const CHANNEL = '__glamorous__' 5 | export const isPreact = preval`module.exports = process.env.BUILD_PREACT === 'true'` 6 | -------------------------------------------------------------------------------- /src/dom-elements.js: -------------------------------------------------------------------------------- 1 | import htmlTagNames from 'html-tag-names' 2 | import svgTagNames from 'svg-tag-names' 3 | 4 | const domElements = htmlTagNames 5 | .concat(svgTagNames) 6 | .filter((tag, index, array) => array.indexOf(tag) === index) 7 | 8 | export default domElements 9 | -------------------------------------------------------------------------------- /src/get-glamor-classname.js: -------------------------------------------------------------------------------- 1 | import {css, styleSheet} from 'glamor' 2 | /** 3 | * This function takes a className string and gets all the 4 | * associated glamor styles. It's used to merge glamor styles 5 | * from a className to make sure that specificity is not 6 | * a problem when passing a className to a component. 7 | * @param {String} [className=''] the className string 8 | * @return {Object} { glamorStyles, glamorlessClassName } 9 | * - glamorStyles is an array of all the glamor styles objects 10 | * - glamorlessClassName is the rest of the className string 11 | * without the glamor classNames 12 | */ 13 | function extractGlamorStyles(className) { 14 | const glamorlessClassName = [] 15 | const glamorStyles = [] 16 | className 17 | .toString() 18 | .split(' ') 19 | .forEach(name => { 20 | if (styleSheet.registered[name.substring(4)] === undefined) { 21 | glamorlessClassName.push(name) 22 | } else { 23 | const style = buildGlamorSrcFromClassName(name) 24 | glamorStyles.push(style) 25 | } 26 | }) 27 | 28 | return {glamorlessClassName, glamorStyles} 29 | } 30 | 31 | /** Glamor's css function returns an object with the shape 32 | * 33 | * { 34 | * [`data-css-${hash}`]: '', 35 | * toString() { return `css-${hash}` } 36 | * } 37 | * 38 | * Whenever glamor's build function encounters an object with 39 | * this shape it just pulls the resulting styles from the cache. 40 | * 41 | * note: the toString method is not needed to qualify the shape 42 | **/ 43 | function buildGlamorSrcFromClassName(className) { 44 | return {[`data-${className}`]: ''} 45 | } 46 | 47 | export default getGlamorClassName 48 | 49 | function getGlamorClassName({ 50 | styles, 51 | props, 52 | cssOverrides, 53 | cssProp, 54 | context, 55 | displayName, 56 | }) { 57 | const {mappedArgs, nonGlamorClassNames} = handleStyles( 58 | [...styles, props.className, cssOverrides, cssProp], 59 | props, 60 | context, 61 | ) 62 | // eslint-disable-next-line max-len 63 | const isDev = process.env.NODE_ENV === 'development' || !process.env.NODE_ENV 64 | const devRules = isDev ? {label: displayName} : null 65 | const glamorClassName = css(devRules, ...mappedArgs).toString() 66 | const extras = nonGlamorClassNames.join(' ').trim() 67 | return `${glamorClassName} ${extras}`.trim() 68 | } 69 | 70 | // this next function is on a "hot" code-path 71 | // so it's pretty complex to make sure it's fast. 72 | // eslint-disable-next-line complexity 73 | function handleStyles(styles, props, context) { 74 | let current 75 | const mappedArgs = [] 76 | const nonGlamorClassNames = [] 77 | for (let i = 0; i < styles.length; i++) { 78 | current = styles[i] 79 | while (typeof current === 'function') { 80 | current = current(props, context) 81 | } 82 | if (typeof current === 'string') { 83 | const {glamorStyles, glamorlessClassName} = extractGlamorStyles(current) 84 | mappedArgs.push(...glamorStyles) 85 | nonGlamorClassNames.push(...glamorlessClassName) 86 | } else if (Array.isArray(current)) { 87 | const recursed = handleStyles(current, props, context) 88 | mappedArgs.push(...recursed.mappedArgs) 89 | nonGlamorClassNames.push(...recursed.nonGlamorClassNames) 90 | } else { 91 | mappedArgs.push(current) 92 | } 93 | } 94 | return {mappedArgs, nonGlamorClassNames} 95 | } 96 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import codegen from 'codegen.macro' 2 | import domElements from './dom-elements' 3 | import withTheme from './with-theme' 4 | import ThemeProvider from './theme-provider' 5 | import createGlamorous from './create-glamorous' 6 | import splitProps from './split-props' 7 | 8 | const glamorous = createGlamorous(splitProps) 9 | 10 | /* 11 | * This creates a glamorousComponentFactory for every DOM element so you can 12 | * simply do: 13 | * const GreenButton = glamorous.button({ 14 | * backgroundColor: 'green', 15 | * padding: 20, 16 | * }) 17 | * Click Me! 18 | */ 19 | Object.assign( 20 | glamorous, 21 | domElements.reduce((getters, tag) => { 22 | // TODO: next breaking change, let's make 23 | // the `displayName` be: `glamorous.${tag}` 24 | getters[tag] = glamorous(tag) 25 | return getters 26 | }, {}), 27 | ) 28 | 29 | /* 30 | * This creates a glamorous component for each DOM element so you can 31 | * simply do: 32 | * 36 | * I'm green! 37 | * 38 | */ 39 | Object.assign( 40 | glamorous, 41 | domElements.reduce((comps, tag) => { 42 | const capitalTag = capitalize(tag) 43 | comps[capitalTag] = glamorous[tag]() 44 | comps[capitalTag].displayName = `glamorous.${capitalTag}` 45 | comps[capitalTag].propsAreCssOverrides = true 46 | return comps 47 | }, {}), 48 | ) 49 | 50 | function capitalize(s) { 51 | return s.slice(0, 1).toUpperCase() + s.slice(1) 52 | } 53 | 54 | /* 55 | * Fix importing in typescript after rollup compilation 56 | * https://github.com/rollup/rollup/issues/1156 57 | * https://github.com/Microsoft/TypeScript/issues/13017#issuecomment-268657860 58 | */ 59 | glamorous.default = glamorous 60 | 61 | export default glamorous 62 | export {ThemeProvider, withTheme} 63 | 64 | codegen` 65 | if (process.env.BUILD_FORMAT === 'esm') { 66 | module.exports = require('../other/get-exports-code') 67 | } else { 68 | module.exports = '' 69 | } 70 | ` 71 | -------------------------------------------------------------------------------- /src/react-compat.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import codegen from 'codegen.macro' 3 | import {isPreact} from './constants' 4 | 5 | let PropTypes 6 | 7 | /* istanbul ignore next */ 8 | if (isPreact) { 9 | if (!React.PropTypes) { 10 | PropTypes = () => PropTypes 11 | const allTypes = [ 12 | 'array', 13 | 'bool', 14 | 'func', 15 | 'number', 16 | 'object', 17 | 'string', 18 | 'symbol', 19 | 'any', 20 | 'arrayOf', 21 | 'element', 22 | 'instanceOf', 23 | 'node', 24 | 'objectOf', 25 | 'oneOf', 26 | 'oneOfType', 27 | 'shape', 28 | 'exact', 29 | ] 30 | allTypes.forEach(type => { 31 | PropTypes[type] = PropTypes 32 | }) 33 | } 34 | // copied from preact-compat 35 | /* eslint-disable no-eq-null, eqeqeq, consistent-return */ 36 | if (!React.Children) { 37 | const Children = { 38 | map(children, fn, ctx) { 39 | if (children == null) { 40 | return null 41 | } 42 | children = Children.toArray(children) 43 | if (ctx && ctx !== children) { 44 | fn = fn.bind(ctx) 45 | } 46 | return children.map(fn) 47 | }, 48 | forEach(children, fn, ctx) { 49 | if (children == null) { 50 | return null 51 | } 52 | children = Children.toArray(children) 53 | if (ctx && ctx !== children) { 54 | fn = fn.bind(ctx) 55 | } 56 | children.forEach(fn) 57 | }, 58 | count(children) { 59 | return (children && children.length) || 0 60 | }, 61 | only(children) { 62 | children = Children.toArray(children) 63 | if (children.length !== 1) { 64 | throw new Error('Children.only() expects only one child.') 65 | } 66 | return children[0] 67 | }, 68 | toArray(children) { 69 | if (children == null) { 70 | return [] 71 | } 72 | return [].concat(children) 73 | }, 74 | } 75 | React.Children = Children 76 | } 77 | /* eslint-enable no-eq-null, eqeqeq, consistent-return */ 78 | } else if (parseFloat(React.version.slice(0, 4)) >= 15.5) { 79 | /* istanbul ignore next */ 80 | try { 81 | PropTypes = codegen` 82 | if (process.env.BUILD_FORMAT === 'umd') { 83 | module.exports = "(typeof window !== 'undefined' ? window : global).PropTypes" 84 | } else { 85 | module.exports = "require('prop-types')" 86 | } 87 | ` 88 | /* istanbul ignore next */ 89 | } catch (error) { 90 | // ignore 91 | } 92 | } 93 | /* istanbul ignore next */ 94 | PropTypes = PropTypes || React.PropTypes 95 | 96 | export {PropTypes} 97 | 98 | /* 99 | eslint 100 | import/no-mutable-exports:0, 101 | import/prefer-default-export:0, 102 | react/no-deprecated:0 103 | */ 104 | -------------------------------------------------------------------------------- /src/react-props.js: -------------------------------------------------------------------------------- 1 | import {isPreact} from './constants' 2 | 3 | /* 4 | * This is used to check if a property name is one of the React-specific 5 | * properties and determine if that property should be forwarded 6 | * to the React component 7 | */ 8 | 9 | /* Logic copied from ReactDOMUnknownPropertyHook */ 10 | const reactProps = [ 11 | 'children', 12 | 'dangerouslySetInnerHTML', 13 | 'key', 14 | 'ref', 15 | 'autoFocus', 16 | 'defaultValue', 17 | 'valueLink', 18 | 'defaultChecked', 19 | 'checkedLink', 20 | 'innerHTML', 21 | 'suppressContentEditableWarning', 22 | 'onFocusIn', 23 | 'onFocusOut', 24 | 'className', 25 | 26 | /* List copied from https://facebook.github.io/react/docs/events.html */ 27 | 'onCopy', 28 | 'onCut', 29 | 'onPaste', 30 | 'onCompositionEnd', 31 | 'onCompositionStart', 32 | 'onCompositionUpdate', 33 | 'onKeyDown', 34 | 'onKeyPress', 35 | 'onKeyUp', 36 | 'onFocus', 37 | 'onBlur', 38 | 'onChange', 39 | 'onInput', 40 | 'onInvalid', 41 | 'onSubmit', 42 | 'onClick', 43 | 'onContextMenu', 44 | 'onDoubleClick', 45 | 'onDrag', 46 | 'onDragEnd', 47 | 'onDragEnter', 48 | 'onDragExit', 49 | 'onDragLeave', 50 | 'onDragOver', 51 | 'onDragStart', 52 | 'onDrop', 53 | 'onMouseDown', 54 | 'onMouseEnter', 55 | 'onMouseLeave', 56 | 'onMouseMove', 57 | 'onMouseOut', 58 | 'onMouseOver', 59 | 'onMouseUp', 60 | 'onSelect', 61 | 'onTouchCancel', 62 | 'onTouchEnd', 63 | 'onTouchMove', 64 | 'onTouchStart', 65 | 'onScroll', 66 | 'onWheel', 67 | 'onAbort', 68 | 'onCanPlay', 69 | 'onCanPlayThrough', 70 | 'onDurationChange', 71 | 'onEmptied', 72 | 'onEncrypted', 73 | 'onEnded', 74 | 'onError', 75 | 'onLoadedData', 76 | 'onLoadedMetadata', 77 | 'onLoadStart', 78 | 'onPause', 79 | 'onPlay', 80 | 'onPlaying', 81 | 'onProgress', 82 | 'onRateChange', 83 | 'onSeeked', 84 | 'onSeeking', 85 | 'onStalled', 86 | 'onSuspend', 87 | 'onTimeUpdate', 88 | 'onVolumeChange', 89 | 'onWaiting', 90 | 'onLoad', 91 | 'onAnimationStart', 92 | 'onAnimationEnd', 93 | 'onAnimationIteration', 94 | 'onTransitionEnd', 95 | 96 | 'onCopyCapture', 97 | 'onCutCapture', 98 | 'onPasteCapture', 99 | 'onCompositionEndCapture', 100 | 'onCompositionStartCapture', 101 | 'onCompositionUpdateCapture', 102 | 'onKeyDownCapture', 103 | 'onKeyPressCapture', 104 | 'onKeyUpCapture', 105 | 'onFocusCapture', 106 | 'onBlurCapture', 107 | 'onChangeCapture', 108 | 'onInputCapture', 109 | 'onSubmitCapture', 110 | 'onClickCapture', 111 | 'onContextMenuCapture', 112 | 'onDoubleClickCapture', 113 | 'onDragCapture', 114 | 'onDragEndCapture', 115 | 'onDragEnterCapture', 116 | 'onDragExitCapture', 117 | 'onDragLeaveCapture', 118 | 'onDragOverCapture', 119 | 'onDragStartCapture', 120 | 'onDropCapture', 121 | 'onMouseDownCapture', 122 | 'onMouseEnterCapture', 123 | 'onMouseLeaveCapture', 124 | 'onMouseMoveCapture', 125 | 'onMouseOutCapture', 126 | 'onMouseOverCapture', 127 | 'onMouseUpCapture', 128 | 'onSelectCapture', 129 | 'onTouchCancelCapture', 130 | 'onTouchEndCapture', 131 | 'onTouchMoveCapture', 132 | 'onTouchStartCapture', 133 | 'onScrollCapture', 134 | 'onWheelCapture', 135 | 'onAbortCapture', 136 | 'onCanPlayCapture', 137 | 'onCanPlayThroughCapture', 138 | 'onDurationChangeCapture', 139 | 'onEmptiedCapture', 140 | 'onEncryptedCapture', 141 | 'onEndedCapture', 142 | 'onErrorCapture', 143 | 'onLoadedDataCapture', 144 | 'onLoadedMetadataCapture', 145 | 'onLoadStartCapture', 146 | 'onPauseCapture', 147 | 'onPlayCapture', 148 | 'onPlayingCapture', 149 | 'onProgressCapture', 150 | 'onRateChangeCapture', 151 | 'onSeekedCapture', 152 | 'onSeekingCapture', 153 | 'onStalledCapture', 154 | 'onSuspendCapture', 155 | 'onTimeUpdateCapture', 156 | 'onVolumeChangeCapture', 157 | 'onWaitingCapture', 158 | 'onLoadCapture', 159 | 'onAnimationStartCapture', 160 | 'onAnimationEndCapture', 161 | 'onAnimationIterationCapture', 162 | 'onTransitionEndCapture', 163 | ] 164 | 165 | if (isPreact) { 166 | reactProps.push( 167 | 'autocomplete', 168 | 'autofocus', 169 | 'class', 170 | 'for', 171 | 'onDblClick', 172 | 'onSearch', 173 | 'slot', 174 | 'srcset', 175 | ) 176 | } 177 | 178 | export {reactProps as default} 179 | -------------------------------------------------------------------------------- /src/should-forward-property.js: -------------------------------------------------------------------------------- 1 | /* eslint max-lines:0, func-style:0 */ 2 | // copied from: 3 | // https://github.com/styled-components/styled-components/tree/ 4 | // 956e8210b6277860c89404f9cb08735f97eaa7e1/src/utils/validAttr.js 5 | /* Trying to avoid the unknown-prop errors on glamorous components 6 | by filtering by React's attribute whitelist. 7 | */ 8 | 9 | import memoize from 'fast-memoize' 10 | import reactHTMLAttributes from 'react-html-attributes' 11 | import reactProps from './react-props' 12 | 13 | const globalReactHtmlProps = reactHTMLAttributes['*'] 14 | const supportedSVGTagNames = reactHTMLAttributes.elements.svg 15 | const supportedHtmlTagNames = reactHTMLAttributes.elements.html 16 | 17 | // these are valid attributes that have the 18 | // same name as CSS properties, and is used 19 | // for css overrides API 20 | const cssProps = ['color', 'height', 'width'] 21 | 22 | /* From DOMProperty */ 23 | const ATTRIBUTE_NAME_START_CHAR = 24 | // eslint-disable-next-line max-len 25 | ':A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD' 26 | // eslint-disable-next-line max-len 27 | const ATTRIBUTE_NAME_CHAR = `${ATTRIBUTE_NAME_START_CHAR}\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040` 28 | const isCustomAttribute = RegExp.prototype.test.bind( 29 | new RegExp(`^(data|aria)-[${ATTRIBUTE_NAME_CHAR}]*$`), 30 | ) 31 | 32 | const isSvgTag = tagName => 33 | // in our context, we only say that SVG tags are SVG 34 | // if they are not also HTML. 35 | // See https://github.com/paypal/glamorous/issues/245 36 | // the svg tag will always be treated as svg for 37 | // er... obvious reasons 38 | tagName === 'svg' || 39 | (supportedHtmlTagNames.indexOf(tagName) === -1 && 40 | supportedSVGTagNames.indexOf(tagName) !== -1) 41 | const isHtmlProp = (name, tagName) => { 42 | let elementAttributes 43 | 44 | if (isSvgTag(tagName)) { 45 | // all SVG attributes supported by React are grouped under 'svg' 46 | elementAttributes = reactHTMLAttributes.svg 47 | } else { 48 | elementAttributes = reactHTMLAttributes[tagName] || [] 49 | } 50 | 51 | return ( 52 | globalReactHtmlProps.indexOf(name) !== -1 || 53 | elementAttributes.indexOf(name) !== -1 54 | ) 55 | } 56 | const isCssProp = name => cssProps.indexOf(name) !== -1 57 | const isReactProp = name => reactProps.indexOf(name) !== -1 58 | 59 | // eslint-disable-next-line complexity 60 | const shouldForwardProperty = (tagName, name) => 61 | typeof tagName !== 'string' || 62 | ((isHtmlProp(name, tagName) || 63 | isReactProp(name) || 64 | isCustomAttribute(name.toLowerCase())) && 65 | (!isCssProp(name) || isSvgTag(tagName))) 66 | 67 | export default memoize(shouldForwardProperty) 68 | -------------------------------------------------------------------------------- /src/split-props.js: -------------------------------------------------------------------------------- 1 | import shouldForwardProperty from './should-forward-property' 2 | 3 | // eslint-disable-next-line complexity 4 | export default function splitProps( 5 | { 6 | css: cssProp, 7 | innerRef, 8 | // these are plucked off 9 | theme, // because they 10 | className, // should never 11 | glam, // be forwarded 12 | // to the lower 13 | // component ever 14 | ...rest 15 | }, 16 | {propsAreCssOverrides, rootEl, filterProps, forwardProps}, 17 | ) { 18 | // forward innerRef if user wishes to do so 19 | if (innerRef !== undefined && forwardProps.indexOf('innerRef') !== -1) { 20 | rest.innerRef = innerRef 21 | } 22 | const returnValue = {toForward: {}, cssProp, cssOverrides: {}} 23 | if (!propsAreCssOverrides) { 24 | if (typeof rootEl !== 'string' && filterProps.length === 0) { 25 | // if it's not a string and filterProps is empty, 26 | // then we can forward everything (because it's a component) 27 | returnValue.toForward = rest 28 | return returnValue 29 | } 30 | } 31 | return Object.keys(rest).reduce((split, propName) => { 32 | if (filterProps.indexOf(propName) !== -1) { 33 | return split 34 | } else if ( 35 | forwardProps.indexOf(propName) !== -1 || 36 | shouldForwardProperty(rootEl, propName) 37 | ) { 38 | split.toForward[propName] = rest[propName] 39 | } else if (propsAreCssOverrides) { 40 | split.cssOverrides[propName] = rest[propName] 41 | } 42 | return split 43 | }, returnValue) 44 | } 45 | 46 | /* eslint no-unused-vars:0 */ 47 | -------------------------------------------------------------------------------- /src/theme-provider.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import isFunction from 'is-function' 3 | import isPlainObject from 'is-plain-object' 4 | import brcast from 'brcast' 5 | import {PropTypes} from './react-compat' 6 | import {CHANNEL} from './constants' 7 | 8 | /** 9 | * This is a component which will provide a theme to the entire tree 10 | * via context and event listener 11 | * (because pure components block context updates) 12 | * inspired by the styled-components implementation 13 | * https://github.com/styled-components/styled-components 14 | * @param {Object} theme the theme object.. 15 | */ 16 | class ThemeProvider extends React.Component { 17 | // create theme, by merging with outer theme, if present 18 | getTheme(passedTheme) { 19 | const theme = passedTheme || this.props.theme 20 | if (isFunction(theme)) { 21 | const mergedTheme = theme(this.outerTheme) 22 | if (!isPlainObject(mergedTheme)) { 23 | throw new Error( 24 | '[ThemeProvider] Please return an object from your theme function, ' + 25 | 'i.e. theme={() => ({})}!', 26 | ) 27 | } 28 | return mergedTheme 29 | } 30 | return {...this.outerTheme, ...theme} 31 | } 32 | 33 | getChildContext() { 34 | return { 35 | [CHANNEL]: this.broadcast, 36 | } 37 | } 38 | 39 | setOuterTheme = theme => { 40 | this.outerTheme = theme 41 | if (this.broadcast !== undefined) { 42 | this.publishTheme() 43 | } 44 | } 45 | 46 | publishTheme(theme) { 47 | this.broadcast.setState(this.getTheme(theme)) 48 | } 49 | 50 | componentDidMount() { 51 | // create a new subscription for keeping track of outer theme, if present 52 | if (this.context[CHANNEL]) { 53 | this.subscriptionId = this.context[CHANNEL].subscribe(this.setOuterTheme) 54 | } 55 | } 56 | 57 | componentWillMount() { 58 | // set broadcast state by merging outer theme with own 59 | if (this.context[CHANNEL]) { 60 | this.setOuterTheme(this.context[CHANNEL].getState()) 61 | } 62 | this.broadcast = brcast(this.getTheme(this.props.theme)) 63 | } 64 | 65 | componentWillReceiveProps(nextProps) { 66 | if (this.props.theme !== nextProps.theme) { 67 | this.publishTheme(nextProps.theme) 68 | } 69 | } 70 | 71 | componentWillUnmount() { 72 | this.subscriptionId && 73 | this.context[CHANNEL].unsubscribe(this.subscriptionId) 74 | } 75 | 76 | render() { 77 | return this.props.children ? React.Children.only(this.props.children) : null 78 | } 79 | } 80 | 81 | ThemeProvider.childContextTypes = { 82 | [CHANNEL]: PropTypes.object.isRequired, 83 | } 84 | 85 | ThemeProvider.contextTypes = { 86 | [CHANNEL]: PropTypes.object, 87 | } 88 | 89 | ThemeProvider.propTypes = { 90 | theme: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired, 91 | children: PropTypes.node, 92 | } 93 | 94 | export default ThemeProvider 95 | -------------------------------------------------------------------------------- /src/tiny.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars:0 */ 2 | import createGlamorous from './create-glamorous' 3 | 4 | function splitProps( 5 | { 6 | css: cssProp, 7 | innerRef, 8 | // these are plucked off 9 | theme, // because they 10 | className, // should never 11 | glam, // be forwarded 12 | // to the lower 13 | // component ever 14 | ...rest 15 | }, 16 | {forwardProps}, 17 | ) { 18 | // forward innerRef if user wishes to do so 19 | if (innerRef !== undefined && forwardProps.indexOf('innerRef') !== -1) { 20 | rest.innerRef = innerRef 21 | } 22 | return {toForward: rest, cssProp} 23 | } 24 | 25 | const glamorous = createGlamorous(splitProps) 26 | 27 | export default glamorous 28 | -------------------------------------------------------------------------------- /src/umd-entry.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | import glamorous from './cjs-entry' 3 | 4 | export default glamorous 5 | -------------------------------------------------------------------------------- /src/with-theme.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import {CHANNEL} from './constants' 4 | import {PropTypes} from './react-compat' 5 | 6 | function generateWarningMessage(Comp) { 7 | const componentName = Comp.displayName || Comp.name || 'FunctionComponent' 8 | // eslint-disable-next-line max-len 9 | return `glamorous warning: Expected component called "${componentName}" which uses withTheme to be within a ThemeProvider but none was found.` 10 | } 11 | 12 | export default function withTheme( 13 | ComponentToTheme, 14 | {noWarn = false, createElement = true} = {}, 15 | ) { 16 | class ThemedComponent extends React.Component { 17 | static propTypes = { 18 | theme: PropTypes.object, 19 | } 20 | warned = noWarn 21 | state = {theme: {}} 22 | setTheme = theme => this.setState({theme}) 23 | 24 | // eslint-disable-next-line complexity 25 | componentWillMount() { 26 | if (!this.context[CHANNEL]) { 27 | if (process.env.NODE_ENV !== 'production' && !this.warned) { 28 | this.warned = true 29 | // eslint-disable-next-line no-console 30 | console.warn(generateWarningMessage(ComponentToTheme)) 31 | } 32 | } 33 | const {theme} = this.props 34 | if (this.context[CHANNEL]) { 35 | // if a theme is provided via props, 36 | // it takes precedence over context 37 | this.setTheme(theme ? theme : this.context[CHANNEL].getState()) 38 | } else { 39 | this.setTheme(theme || {}) 40 | } 41 | } 42 | 43 | componentWillReceiveProps(nextProps) { 44 | if (this.props.theme !== nextProps.theme) { 45 | this.setTheme(nextProps.theme) 46 | } 47 | } 48 | 49 | componentDidMount() { 50 | if (this.context[CHANNEL] && !this.props.theme) { 51 | // subscribe to future theme changes 52 | this.subscriptionId = this.context[CHANNEL].subscribe(this.setTheme) 53 | } 54 | } 55 | 56 | componentWillUnmount() { 57 | // cleanup subscription 58 | this.subscriptionId && 59 | this.context[CHANNEL].unsubscribe(this.subscriptionId) 60 | } 61 | 62 | render() { 63 | if (createElement) { 64 | return 65 | } else { 66 | // this allows us to effectively use the GlamorousComponent 67 | // as our `render` method without going through lifecycle hooks. 68 | // Also allows us to forward the context in the scenario where 69 | // a user wants to add more context. 70 | // eslint-disable-next-line babel/new-cap 71 | return ComponentToTheme.call( 72 | this, 73 | {...this.props, ...this.state}, 74 | this.context, 75 | ) 76 | } 77 | } 78 | } 79 | 80 | const defaultContextTypes = { 81 | [CHANNEL]: PropTypes.object, 82 | } 83 | 84 | let userDefinedContextTypes = null 85 | 86 | // configure the contextTypes to be settable by the user, 87 | // however also retaining the glamorous channel. 88 | Object.defineProperty(ThemedComponent, 'contextTypes', { 89 | enumerable: true, 90 | configurable: true, 91 | set(value) { 92 | userDefinedContextTypes = value 93 | }, 94 | get() { 95 | // if the user has provided a contextTypes definition, 96 | // merge the default context types with the provided ones. 97 | if (userDefinedContextTypes) { 98 | return { 99 | ...defaultContextTypes, 100 | ...userDefinedContextTypes, 101 | } 102 | } 103 | return defaultContextTypes 104 | }, 105 | }) 106 | 107 | return ThemedComponent 108 | } 109 | -------------------------------------------------------------------------------- /test/glamorous-exports.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CSSProperties, 3 | CSSPropertiesCompleteSingle, 4 | CSSPropertiesComplete, 5 | CSSPropertiesPseudo, 6 | CSSPropertiesLossy, 7 | SVGProperties, 8 | SVGPropertiesCompleteSingle, 9 | SVGPropertiesComplete, 10 | SVGPropertiesLossy, 11 | GlamorousComponent, 12 | GlamorousComponentProps, 13 | ExtraGlamorousProps, 14 | StyleFunction, 15 | StyleArray, 16 | StyleArgument, 17 | BuiltInGlamorousComponentFactory, 18 | KeyGlamorousComponentFactory, 19 | GlamorousComponentFactory, 20 | HTMLComponentFactory, 21 | HTMLKey, 22 | SVGComponentFactory, 23 | SVGKey, 24 | } from '../' 25 | -------------------------------------------------------------------------------- /test/preact/should-fail.test.tsx: -------------------------------------------------------------------------------- 1 | import {Component, h, FunctionalComponent} from 'preact' 2 | 3 | import glamorous, {withTheme, ThemeProvider} from 'glamorous/preact' 4 | 5 | // built-in DOM component factories 6 | 7 | // ### SVG 8 | 9 | const BuiltInStrictSVGStyleObjectInvalidKey = glamorous.svg({ 10 | fillRule: 'cat', 11 | }) 12 | 13 | const BuiltInStrictSVGStyleObjectInvalidProperty = glamorous.svg({ 14 | fillRule: 'cat', 15 | }) 16 | 17 | const BuiltInStrictSVGStyleFunctionInvalidProperty = glamorous.svg(() => ({ 18 | fillRule: 'cat', 19 | })) 20 | 21 | // ### HTML 22 | 23 | const BuiltInStrictDIVtyleObjectInvlalidKey = glamorous.div({ 24 | float: 'cat', 25 | }) 26 | 27 | const BuiltInStrictDIVtyleObjectInvlalidProperty = glamorous.div({ 28 | float: 'cat', 29 | }) 30 | 31 | const BuiltInStrictDIVStyleFunctionInvlalidKey = glamorous.div(() => ({ 32 | float: 'cat', 33 | })) 34 | 35 | const BuiltInStrictDIVStyleFunctionInvlalidProperty = glamorous.div(() => ({ 36 | float: 'cat', 37 | })) 38 | 39 | // self provided glamorousComponentFactory 40 | 41 | interface TestComponentProps { 42 | className: string 43 | } 44 | 45 | const TestComponent: FunctionalComponent = props => ( 46 |
47 | ) 48 | 49 | const StrictSVGStyleObject = glamorous(TestComponent)({ 50 | fillRule: 'cat', 51 | }) 52 | 53 | const StrictSVGStyleFunction = glamorous(TestComponent)(() => ({ 54 | fillRule: 'cat', 55 | })) 56 | 57 | const BuiltInStrictStyleFunction = glamorous(TestComponent)({ 58 | float: 'cat', 59 | }) 60 | 61 | const BuiltInStrictDivStyleFunction = glamorous(TestComponent)(() => ({ 62 | float: 'cat', 63 | })) 64 | 65 | // Theme 66 | 67 | interface ExampleTheme { 68 | color: string 69 | } 70 | 71 | interface ThemeProps { 72 | title: string 73 | theme: ExampleTheme 74 | } 75 | 76 | const ComponentWithTheme: FunctionalComponent = props => ( 77 |

82 | {props.title} 83 |

84 | ) 85 | 86 | interface PropsWithoutTheme { 87 | title: string 88 | } 89 | 90 | const NonGlamorousThemedComponent = withTheme( 91 | ComponentWithTheme, 92 | ) 93 | 94 | // displayName 95 | 96 | const TestDisplayName: FunctionalComponent = () =>
97 | 98 | glamorous(TestDisplayName, { 99 | displayName: 0, 100 | }) 101 | 102 | // custom glamorous component factory 103 | 104 | interface ExampleComponentProps { 105 | visible: boolean 106 | } 107 | 108 | const ExampleComponent: FunctionalComponent = () => ( 109 |
110 | ) 111 | 112 | glamorous(ExampleComponent)(props => ({ 113 | display: props.visibles ? 'none' : 'hidden', 114 | })) 115 | 116 | glamorous<{visible: boolean}>('div')(props => ({ 117 | display: props.visible ? 'none' : 'hidden', 118 | })) 119 | 120 | const StyledExampleComponent = glamorous(ExampleComponent)() 121 | const StyledExampleComponentKey = glamorous<{visible: boolean}>('div')() 122 | 123 | const usingStyledExampleComponent = ( 124 |
125 | 126 | 127 | 128 | 129 |
130 | ) 131 | 132 | glamorous('circle')({allowReorder: false}) 133 | glamorous('div')({color: false}) 134 | 135 | glamorous<{ 136 | visible: string 137 | }>(ExampleComponent)(props => ({ 138 | display: props.visible ? 'none' : 'hidden', 139 | })) 140 | 141 | glamorous<{visible: boolean}>('div')(props => ({ 142 | display: props.visible === '' ? 'none' : 'hidden', 143 | })) 144 | 145 | // shouldClassNameUpdate 146 | 147 | interface ShouldClassNameUpdateProps { 148 | color: string 149 | } 150 | 151 | const TestShouldClassNameUpdate: FunctionalComponent< 152 | ShouldClassNameUpdateProps 153 | > = () =>
154 | 155 | const pureDivFactory0 = glamorous(TestShouldClassNameUpdate, { 156 | shouldClassNameUpdate: (props, previousProps, context, previousContext) => { 157 | if (props.colors !== props.color) { 158 | return false 159 | } 160 | return true 161 | }, 162 | }) 163 | 164 | const pureDivFactory1 = glamorous(TestShouldClassNameUpdate, { 165 | shouldClassNameUpdate: (props, previousProps, context, previousContext) => { 166 | if (props.color !== props.color) { 167 | return false 168 | } 169 | return 1 170 | }, 171 | }) 172 | 173 | interface ShouldClassNameUpdateContext { 174 | color: string 175 | } 176 | 177 | const pureDivFactory2 = glamorous< 178 | ShouldClassNameUpdateProps, 179 | ShouldClassNameUpdateContext 180 | >(TestShouldClassNameUpdate, { 181 | shouldClassNameUpdate: (props, previousProps, context, previousContext) => { 182 | if (context.colors !== previousContext.color) { 183 | return false 184 | } 185 | 186 | return true 187 | }, 188 | }) 189 | 190 | // withProps 191 | glamorous('div', { 192 | withProps: '', 193 | }) 194 | 195 | glamorous('div', { 196 | withProps: {visible: false}, 197 | })(props => ({ 198 | primaryColor: props.visible, 199 | })) 200 | 201 | glamorous('div')().withProps() 202 | glamorous('div')().withProps('') 203 | glamorous('div')().withProps(false) 204 | 205 | const WithPropsDiv = glamorous('div', { 206 | withProps: {primaryColor: 'red'}, 207 | })(props => ({ 208 | primaryColor: props.primaryColor, 209 | })) 210 | 211 | const SimpleComponent = () =>
212 | 213 | const MethodWithPropsComponent = glamorous(SimpleComponent)({}).withProps({ 214 | primaryColor: 'red', 215 | }) 216 | 217 | const WithPropsSimpleComponent = glamorous(SimpleComponent, { 218 | withProps: {primaryColor: 'red'}, 219 | })(props => ({ 220 | primaryColor: props.primaryColor, 221 | })) 222 | 223 | const useWithProps = ( 224 |
225 | 226 | 227 | 228 | 229 | 230 | 231 |
232 | ) 233 | 234 | // Properties Array 235 | glamorous.div({ 236 | textAlign: 'center', 237 | display: ['block', 'flexs'], 238 | }) 239 | 240 | glamorous.circle({ 241 | textAlign: 'center', 242 | display: ['flexs', 'block'], 243 | }) 244 | 245 | // propsAreCssOverrides 246 | 247 | const ComponentPropsAreCssOverides = glamorous(SimpleComponent, { 248 | propsAreCssOverrides: true, 249 | })({}) 250 | const ComponentPropsAreNotCssOverides = glamorous(SimpleComponent, { 251 | propsAreCssOverrides: false, 252 | })({}) 253 | const ComponentPropsAreAlsoNotCssOverides = glamorous(SimpleComponent)({}) 254 | 255 | const DivPropsAreCssOverides = glamorous('div', {propsAreCssOverrides: true})( 256 | {}, 257 | ) 258 | const DivPropsAreNotCssOverides = glamorous('div', { 259 | propsAreCssOverrides: false, 260 | })({}) 261 | const DivPropsAreAlsoNotCssOverides = glamorous('div')({}) 262 | 263 | const usePropsAreCssOverrides = ( 264 |
265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 |
273 | ) 274 | -------------------------------------------------------------------------------- /test/should-fail.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import glamorous, { withTheme, ThemeProvider } from "../"; 4 | 5 | // built-in DOM component factories 6 | 7 | // ### SVG 8 | 9 | const BuiltInStrictSVGStyleObjectInvalidKey = glamorous.svg( 10 | { 11 | fillRule: 'cat', 12 | }, 13 | ) 14 | 15 | const BuiltInStrictSVGStyleObjectInvalidProperty = glamorous.svg( 16 | { 17 | fillRule: 'cat', 18 | }, 19 | ) 20 | 21 | const BuiltInStrictSVGStyleFunctionInvalidProperty = glamorous.svg( 22 | () => ({ 23 | fillRule: 'cat', 24 | }) 25 | ) 26 | 27 | // ### HTML 28 | 29 | const BuiltInStrictDIVtyleObjectInvlalidKey = glamorous.div( 30 | { 31 | float: "cat", 32 | }, 33 | ) 34 | 35 | const BuiltInStrictDIVtyleObjectInvlalidProperty = glamorous.div( 36 | { 37 | float: "cat", 38 | }, 39 | ) 40 | 41 | const BuiltInStrictDIVStyleFunctionInvlalidKey = glamorous.div( 42 | () => ({ 43 | float: "cat", 44 | }) 45 | ) 46 | 47 | const BuiltInStrictDIVStyleFunctionInvlalidProperty = glamorous.div( 48 | () => ({ 49 | float: "cat", 50 | }) 51 | ) 52 | 53 | // self provided glamorousComponentFactory 54 | 55 | interface TestComponentProps { 56 | className: string 57 | } 58 | 59 | const TestComponent: React.SFC = (props) => ( 60 |
61 | ) 62 | 63 | const StrictSVGStyleObject = glamorous(TestComponent)( 64 | { 65 | fillRule: 'cat', 66 | }, 67 | ) 68 | 69 | const StrictSVGStyleFunction = glamorous(TestComponent)( 70 | () => ({ 71 | fillRule: 'cat', 72 | }) 73 | ) 74 | 75 | const BuiltInStrictStyleFunction = glamorous(TestComponent)( 76 | { 77 | float: "cat", 78 | }, 79 | ) 80 | 81 | const BuiltInStrictDivStyleFunction = glamorous(TestComponent)( 82 | () => ({ 83 | float: "cat", 84 | }) 85 | ) 86 | 87 | // Theme 88 | 89 | interface ExampleTheme { 90 | color: string 91 | } 92 | 93 | interface ThemeProps { 94 | title: string 95 | theme: ExampleTheme 96 | } 97 | 98 | const ComponentWithTheme: React.SFC = (props) => ( 99 |

102 | {props.title} 103 |

104 | ) 105 | 106 | interface PropsWithoutTheme { 107 | title: string 108 | } 109 | 110 | const NonGlamorousThemedComponent = withTheme< 111 | PropsWithoutTheme 112 | >(ComponentWithTheme) 113 | 114 | // displayName 115 | 116 | const TestDisplayName: React.SFC = () =>
117 | 118 | glamorous( 119 | TestDisplayName, 120 | { 121 | displayName: 0 122 | }, 123 | ) 124 | 125 | // custom glamorous component factory 126 | 127 | interface ExampleComponentProps { 128 | visible: boolean 129 | } 130 | 131 | const ExampleComponent: React.SFC = () =>
132 | 133 | glamorous(ExampleComponent)( 134 | (props) => ({ 135 | display: props.visibles ? 'none' : 'hidden' 136 | }) 137 | ) 138 | 139 | glamorous<{ visible: boolean }>('div')( 140 | (props) => ({ 141 | display: props.visible ? 'none' : 'hidden' 142 | }) 143 | ) 144 | 145 | const StyledExampleComponent = glamorous(ExampleComponent)() 146 | const StyledExampleComponentKey = glamorous<{ visible: boolean }>('div')() 147 | 148 | const usingStyledExampleComponent = ( 149 |
150 | 151 | 152 | 153 | 154 |
155 | ) 156 | 157 | glamorous('circle')({ allowReorder: false }) 158 | glamorous('div')({ color: false }) 159 | 160 | glamorous<{ 161 | visible: string 162 | }>(ExampleComponent)( 163 | (props) => ({ 164 | display: props.visible ? 'none' : 'hidden' 165 | }) 166 | ) 167 | 168 | glamorous<{ visible: boolean }>('div')( 169 | (props) => ({ 170 | display: props.visible === '' ? 'none' : 'hidden' 171 | }) 172 | ) 173 | 174 | // shouldClassNameUpdate 175 | 176 | interface ShouldClassNameUpdateProps { 177 | color: string 178 | } 179 | 180 | const TestShouldClassNameUpdate: React.SFC = () =>
181 | 182 | const pureDivFactory0 = glamorous(TestShouldClassNameUpdate, { 183 | shouldClassNameUpdate: (props, previousProps, context, previousContext) => { 184 | if (props.colors !== props.color) { 185 | return false 186 | } 187 | return true 188 | }, 189 | }) 190 | 191 | const pureDivFactory1 = glamorous(TestShouldClassNameUpdate, { 192 | shouldClassNameUpdate: (props, previousProps, context, previousContext) => { 193 | if (props.color !== props.color) { 194 | return false 195 | } 196 | return 1 197 | }, 198 | }) 199 | 200 | 201 | interface ShouldClassNameUpdateContext { 202 | color: string 203 | } 204 | 205 | const pureDivFactory2 = glamorous(TestShouldClassNameUpdate, { 206 | shouldClassNameUpdate: (props, previousProps, context, previousContext) => { 207 | if (context.colors !== previousContext.color) { 208 | return false 209 | } 210 | 211 | return true 212 | }, 213 | }) 214 | 215 | // withProps 216 | 217 | glamorous('div', { 218 | withProps: '' 219 | }) 220 | 221 | glamorous('div', { 222 | withProps: { visible: false } 223 | })( 224 | (props) => ({ 225 | primaryColor: props.visible 226 | }) 227 | ) 228 | 229 | glamorous('div')().withProps() 230 | glamorous('div')().withProps('') 231 | glamorous('div')().withProps(false) 232 | 233 | const WithPropsDiv = glamorous('div', { 234 | withProps: {primaryColor: 'red'} 235 | })( 236 | (props) => ({ 237 | primaryColor: props.primaryColor 238 | }) 239 | ) 240 | 241 | const SimpleComponent = () =>
242 | 243 | const MethodWithPropsComponent = glamorous(SimpleComponent)({}).withProps({ 244 | primaryColor: 'red' 245 | }) 246 | 247 | const WithPropsSimpleComponent = glamorous(SimpleComponent, { 248 | withProps: {primaryColor: 'red'} 249 | })( 250 | (props) => ({ 251 | primaryColor: props.primaryColor 252 | }) 253 | ) 254 | 255 | const useWithProps = ( 256 |
257 | 258 | 259 | 260 | 261 | 262 | 263 |
264 | ) 265 | 266 | // Properties Array 267 | glamorous.div({ 268 | textAlign: "center", 269 | display: ["block", "flexs"], 270 | }) 271 | 272 | glamorous.circle({ 273 | textAlign: "center", 274 | display: ["flexs", "block"], 275 | }) 276 | 277 | // propsAreCssOverrides 278 | 279 | const ComponentPropsAreCssOverides = glamorous(SimpleComponent, {propsAreCssOverrides: true})({}) 280 | const ComponentPropsAreNotCssOverides = glamorous(SimpleComponent, {propsAreCssOverrides: false})({}) 281 | const ComponentPropsAreAlsoNotCssOverides = glamorous(SimpleComponent)({}) 282 | 283 | const DivPropsAreCssOverides = glamorous('div', {propsAreCssOverrides: true})({}) 284 | const DivPropsAreNotCssOverides = glamorous('div', {propsAreCssOverrides: false})({}) 285 | const DivPropsAreAlsoNotCssOverides = glamorous('div')({}) 286 | 287 | const usePropsAreCssOverrides = ( 288 |
289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 |
297 | ) 298 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": false, 4 | "declaration": true, 5 | "inlineSources": true, 6 | "jsx": "react", 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "noEmit": true, 10 | "noImplicitAny": true, 11 | "noImplicitReturns": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "outDir": "./test-ts", 15 | "removeComments": false, 16 | "sourceMap": true, 17 | "strictNullChecks": true, 18 | "target": "es6" 19 | }, 20 | "exclude": ["typings/preact"] 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "jsx": "preserve", 6 | "jsxFactory": "h", 7 | "paths": { 8 | "glamorous/preact": ["typings/preact/glamorous.d.ts"] 9 | }, 10 | "types": ["preact"] 11 | }, 12 | "include": ["test/preact/**/*", "typings/preact/**/*"], 13 | "exclude": ["**/react/**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.preact.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "jsx": "preserve", 6 | "jsxFactory": "h", 7 | "paths": { 8 | "glamorous/preact": ["typings/preact/glamorous.d.ts"], 9 | "preact": ["node_modules/preact"] 10 | }, 11 | "types": ["preact"] 12 | }, 13 | "include": ["test/preact/*", "typings/preact/*"], 14 | "exclude": ["**/react/**/*"] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.react.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "types": ["react"] 5 | }, 6 | "include": ["test/*", "typings/*"], 7 | "exclude": ["typings/preact"] 8 | } 9 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | // "extends": "tslint-microsoft-contrib" 3 | } 4 | -------------------------------------------------------------------------------- /typings/built-in-component-factories.d.ts: -------------------------------------------------------------------------------- 1 | import {SVGProperties} from './svg-properties' 2 | import {CSSProperties} from './css-properties' 3 | import {BuiltInGlamorousComponentFactory} from './component-factory' 4 | 5 | export type HTMLGlamorousComponentFactory< 6 | HTMLElement 7 | > = BuiltInGlamorousComponentFactory< 8 | React.HTMLProps, 9 | CSSProperties 10 | > 11 | 12 | export type SVGGlamorousComponentFactory< 13 | SVGElement 14 | > = BuiltInGlamorousComponentFactory< 15 | React.SVGAttributes, 16 | SVGProperties 17 | > 18 | 19 | export interface HTMLComponentFactory { 20 | a: HTMLGlamorousComponentFactory 21 | abbr: HTMLGlamorousComponentFactory 22 | address: HTMLGlamorousComponentFactory 23 | area: HTMLGlamorousComponentFactory 24 | article: HTMLGlamorousComponentFactory 25 | aside: HTMLGlamorousComponentFactory 26 | audio: HTMLGlamorousComponentFactory 27 | b: HTMLGlamorousComponentFactory 28 | base: HTMLGlamorousComponentFactory 29 | bdi: HTMLGlamorousComponentFactory 30 | bdo: HTMLGlamorousComponentFactory 31 | big: HTMLGlamorousComponentFactory 32 | blockquote: HTMLGlamorousComponentFactory 33 | body: HTMLGlamorousComponentFactory 34 | br: HTMLGlamorousComponentFactory 35 | button: HTMLGlamorousComponentFactory 36 | canvas: HTMLGlamorousComponentFactory 37 | caption: HTMLGlamorousComponentFactory 38 | cite: HTMLGlamorousComponentFactory 39 | code: HTMLGlamorousComponentFactory 40 | col: HTMLGlamorousComponentFactory 41 | colgroup: HTMLGlamorousComponentFactory 42 | data: HTMLGlamorousComponentFactory 43 | datalist: HTMLGlamorousComponentFactory 44 | dd: HTMLGlamorousComponentFactory 45 | del: HTMLGlamorousComponentFactory 46 | details: HTMLGlamorousComponentFactory 47 | dfn: HTMLGlamorousComponentFactory 48 | dialog: HTMLGlamorousComponentFactory 49 | div: HTMLGlamorousComponentFactory 50 | dl: HTMLGlamorousComponentFactory 51 | dt: HTMLGlamorousComponentFactory 52 | em: HTMLGlamorousComponentFactory 53 | embed: HTMLGlamorousComponentFactory 54 | fieldset: HTMLGlamorousComponentFactory 55 | figcaption: HTMLGlamorousComponentFactory 56 | figure: HTMLGlamorousComponentFactory 57 | footer: HTMLGlamorousComponentFactory 58 | form: HTMLGlamorousComponentFactory 59 | h1: HTMLGlamorousComponentFactory 60 | h2: HTMLGlamorousComponentFactory 61 | h3: HTMLGlamorousComponentFactory 62 | h4: HTMLGlamorousComponentFactory 63 | h5: HTMLGlamorousComponentFactory 64 | h6: HTMLGlamorousComponentFactory 65 | head: HTMLGlamorousComponentFactory 66 | header: HTMLGlamorousComponentFactory 67 | hgroup: HTMLGlamorousComponentFactory 68 | hr: HTMLGlamorousComponentFactory 69 | html: HTMLGlamorousComponentFactory 70 | i: HTMLGlamorousComponentFactory 71 | iframe: HTMLGlamorousComponentFactory 72 | img: HTMLGlamorousComponentFactory 73 | input: HTMLGlamorousComponentFactory 74 | ins: HTMLGlamorousComponentFactory 75 | kbd: HTMLGlamorousComponentFactory 76 | keygen: HTMLGlamorousComponentFactory 77 | label: HTMLGlamorousComponentFactory 78 | legend: HTMLGlamorousComponentFactory 79 | li: HTMLGlamorousComponentFactory 80 | link: HTMLGlamorousComponentFactory 81 | main: HTMLGlamorousComponentFactory 82 | map: HTMLGlamorousComponentFactory 83 | mark: HTMLGlamorousComponentFactory 84 | menu: HTMLGlamorousComponentFactory 85 | menuitem: HTMLGlamorousComponentFactory 86 | meta: HTMLGlamorousComponentFactory 87 | meter: HTMLGlamorousComponentFactory 88 | nav: HTMLGlamorousComponentFactory 89 | noscript: HTMLGlamorousComponentFactory 90 | object: HTMLGlamorousComponentFactory 91 | ol: HTMLGlamorousComponentFactory 92 | optgroup: HTMLGlamorousComponentFactory 93 | option: HTMLGlamorousComponentFactory 94 | output: HTMLGlamorousComponentFactory 95 | p: HTMLGlamorousComponentFactory 96 | param: HTMLGlamorousComponentFactory 97 | picture: HTMLGlamorousComponentFactory 98 | pre: HTMLGlamorousComponentFactory 99 | progress: HTMLGlamorousComponentFactory 100 | q: HTMLGlamorousComponentFactory 101 | rp: HTMLGlamorousComponentFactory 102 | rt: HTMLGlamorousComponentFactory 103 | ruby: HTMLGlamorousComponentFactory 104 | s: HTMLGlamorousComponentFactory 105 | samp: HTMLGlamorousComponentFactory 106 | script: HTMLGlamorousComponentFactory 107 | section: HTMLGlamorousComponentFactory 108 | select: HTMLGlamorousComponentFactory 109 | small: HTMLGlamorousComponentFactory 110 | source: HTMLGlamorousComponentFactory 111 | span: HTMLGlamorousComponentFactory 112 | strong: HTMLGlamorousComponentFactory 113 | style: HTMLGlamorousComponentFactory 114 | sub: HTMLGlamorousComponentFactory 115 | summary: HTMLGlamorousComponentFactory 116 | sup: HTMLGlamorousComponentFactory 117 | table: HTMLGlamorousComponentFactory 118 | tbody: HTMLGlamorousComponentFactory 119 | td: HTMLGlamorousComponentFactory 120 | textarea: HTMLGlamorousComponentFactory 121 | tfoot: HTMLGlamorousComponentFactory 122 | th: HTMLGlamorousComponentFactory 123 | thead: HTMLGlamorousComponentFactory 124 | time: HTMLGlamorousComponentFactory 125 | title: HTMLGlamorousComponentFactory 126 | tr: HTMLGlamorousComponentFactory 127 | track: HTMLGlamorousComponentFactory 128 | u: HTMLGlamorousComponentFactory 129 | ul: HTMLGlamorousComponentFactory 130 | var: HTMLGlamorousComponentFactory 131 | video: HTMLGlamorousComponentFactory 132 | wbr: HTMLGlamorousComponentFactory 133 | } 134 | 135 | export type HTMLKey = keyof HTMLComponentFactory 136 | 137 | export interface SVGComponentFactory { 138 | circle: SVGGlamorousComponentFactory 139 | clipPath: SVGGlamorousComponentFactory 140 | defs: SVGGlamorousComponentFactory 141 | ellipse: SVGGlamorousComponentFactory 142 | g: SVGGlamorousComponentFactory 143 | image: SVGGlamorousComponentFactory 144 | line: SVGGlamorousComponentFactory 145 | linearGradient: SVGGlamorousComponentFactory 146 | mask: SVGGlamorousComponentFactory 147 | path: SVGGlamorousComponentFactory 148 | pattern: SVGGlamorousComponentFactory 149 | polygon: SVGGlamorousComponentFactory 150 | polyline: SVGGlamorousComponentFactory 151 | radialGradient: SVGGlamorousComponentFactory 152 | rect: SVGGlamorousComponentFactory 153 | stop: SVGGlamorousComponentFactory 154 | svg: SVGGlamorousComponentFactory 155 | text: SVGGlamorousComponentFactory 156 | tspan: SVGGlamorousComponentFactory 157 | } 158 | 159 | export type SVGKey = keyof SVGComponentFactory 160 | -------------------------------------------------------------------------------- /typings/component-factory.d.ts: -------------------------------------------------------------------------------- 1 | import {GlamorousComponent} from './glamorous-component' 2 | import {Omit} from './helpers' 3 | 4 | import {StyleArgument, StaticStyleArgument} from './style-arguments' 5 | 6 | // # built-in DOM - component factories glamorous.div 7 | 8 | export interface BuiltInGlamorousComponentFactory { 9 | (...styles: StaticStyleArgument[]): GlamorousComponent< 10 | ElementProps, 11 | object 12 | > 13 | 14 | ( 15 | ...styles: StyleArgument[] 16 | ): GlamorousComponent & ElementProps, Props> 17 | 18 | (...styles: StyleArgument[]): GlamorousComponent< 19 | Props & ElementProps, 20 | Props 21 | > 22 | } 23 | 24 | // # dom tag - component factories glamorous('div') 25 | 26 | // ## without propsAreCssOverides 27 | 28 | export interface KeyGlamorousComponentFactory< 29 | ElementProps, 30 | Properties, 31 | ExternalProps, 32 | DefaultProps 33 | > { 34 | (...styles: StaticStyleArgument[]): GlamorousComponent< 35 | ElementProps & ExternalProps & Partial, 36 | ExternalProps 37 | > 38 | 39 | ( 40 | ...styles: StyleArgument[] 41 | ): GlamorousComponent< 42 | ElementProps & 43 | ExternalProps & 44 | Partial & 45 | Omit & 46 | Props, 47 | ExternalProps 48 | > 49 | 50 | ( 51 | ...styles: StyleArgument[] 52 | ): GlamorousComponent< 53 | ElementProps & ExternalProps & Partial & Props, 54 | ExternalProps 55 | > 56 | } 57 | 58 | // ## with propsAreCssOverides 59 | 60 | export interface KeyGlamorousComponentFactoryCssOverides< 61 | ElementProps, 62 | Properties, 63 | ExternalProps, 64 | DefaultProps 65 | > { 66 | (...styles: StaticStyleArgument[]): GlamorousComponent< 67 | ElementProps & ExternalProps & Partial & Properties, 68 | ExternalProps 69 | > 70 | 71 | ( 72 | ...styles: StyleArgument[] 73 | ): GlamorousComponent< 74 | ElementProps & 75 | ExternalProps & 76 | Partial & 77 | Omit & 78 | Properties, 79 | ExternalProps 80 | > 81 | 82 | ( 83 | ...styles: StyleArgument[] 84 | ): GlamorousComponent< 85 | ElementProps & ExternalProps & Partial & Props & Properties, 86 | ExternalProps 87 | > 88 | } 89 | 90 | // # react component - component factories glamorous(Component) 91 | 92 | // ## without propsAreCssOverides 93 | 94 | export interface GlamorousComponentFactory< 95 | ExternalProps, 96 | Properties, 97 | DefaultProps 98 | > { 99 | (...styles: StaticStyleArgument[]): GlamorousComponent< 100 | ExternalProps & Partial, 101 | object 102 | > 103 | 104 | ( 105 | ...styles: StyleArgument[] 106 | ): GlamorousComponent< 107 | ExternalProps & Partial & Omit, 108 | Props 109 | > 110 | 111 | ( 112 | ...styles: StyleArgument[] 113 | ): GlamorousComponent & Props, Props> 114 | } 115 | 116 | // ## with propsAreCssOverides 117 | 118 | export interface GlamorousComponentFactoryCssOverides< 119 | ExternalProps, 120 | Properties, 121 | DefaultProps 122 | > { 123 | (...styles: StaticStyleArgument[]): GlamorousComponent< 124 | ExternalProps & Partial & Properties, 125 | object 126 | > 127 | 128 | ( 129 | ...styles: StyleArgument[] 130 | ): GlamorousComponent< 131 | ExternalProps & Partial & Properties & Omit, 132 | Props 133 | > 134 | 135 | ( 136 | ...styles: StyleArgument[] 137 | ): GlamorousComponent< 138 | ExternalProps & Partial & Properties & Props, 139 | Props 140 | > 141 | } 142 | -------------------------------------------------------------------------------- /typings/css-properties.d.ts: -------------------------------------------------------------------------------- 1 | import * as CSS from 'csstype' 2 | import {SingleOrArray} from './helpers' 3 | 4 | export interface CSSPropertiesCompleteSingle 5 | extends CSS.Properties {} 6 | 7 | export type CSSPropertiesPseudo = {[K in CSS.Pseudos]?: CSSProperties} 8 | 9 | type CSSPropertiesComplete = SingleOrArray< 10 | CSSPropertiesCompleteSingle, 11 | keyof CSSPropertiesCompleteSingle 12 | > 13 | 14 | export interface CSSPropertiesLossy { 15 | [propertyName: string]: 16 | | string 17 | | number 18 | | CSSPropertiesComplete 19 | | undefined 20 | | Array 21 | | CSSPropertiesLossy 22 | } 23 | 24 | export interface CSSProperties 25 | extends CSSPropertiesComplete, 26 | CSSPropertiesPseudo, 27 | CSSPropertiesLossy {} 28 | 29 | export type CSSPropertiesRecursive = 30 | | CSSProperties 31 | | CSSPropertiesArray 32 | | CSSFunction 33 | 34 | // TODO: This could be made generic. Issue PR if you're so inclined! 35 | export interface CSSFunction { 36 | (props: {}): CSSPropertiesRecursive 37 | } 38 | 39 | export interface CSSPropertiesArray extends Array {} 40 | -------------------------------------------------------------------------------- /typings/glamorous-component.d.ts: -------------------------------------------------------------------------------- 1 | import {CSSPropertiesRecursive} from './css-properties' 2 | 3 | import {Component} from './glamorous' 4 | 5 | import {Omit} from './helpers' 6 | 7 | /** 8 | * `glamorousComponentFactory` returns a ComponentClass 9 | * 10 | * @see {@link https://github.com/paypal/glamorous/blob/master/src/create-glamorous.js#L28-L131} 11 | */ 12 | 13 | export interface ExtraGlamorousProps { 14 | /** 15 | * Called with the inner element's reference 16 | */ 17 | innerRef?: object | ((instance: any) => void) 18 | 19 | className?: string 20 | /** 21 | * Same type as any of the styles provided, will be merged with this component's styles and take highest priority over the component's predefined styles 22 | */ 23 | css?: CSSPropertiesRecursive 24 | theme?: object 25 | } 26 | 27 | export interface GlamorousComponentFunctions { 28 | /** 29 | * Copies the styles of an already created glamorous component with a different tag 30 | */ 31 | withComponent: ( 32 | component: string | Component, 33 | ) => GlamorousComponent 34 | 35 | /** 36 | * Applies props by default for a component 37 | */ 38 | withProps: ( 39 | props: DefaultProps, 40 | ) => GlamorousComponent, Props> 41 | } 42 | 43 | export interface GlamorousComponent 44 | extends React.ComponentClass, 45 | GlamorousComponentFunctions {} 46 | 47 | export type GlamorousComponentProps = JSX.IntrinsicAttributes & 48 | JSX.IntrinsicClassAttributes< 49 | React.Component 50 | > & 51 | ExternalProps & {children?: React.ReactNode} 52 | -------------------------------------------------------------------------------- /typings/glamorous.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Glamorous v3.2.0 2 | // Project: https://github.com/paypal/glamorous 3 | // Definitions by: Kok Sam 4 | 5 | import * as React from 'react' 6 | import {HTMLComponent, SVGComponent} from './built-in-glamorous-components' 7 | import { 8 | HTMLComponentFactory, 9 | HTMLKey, 10 | SVGComponentFactory, 11 | SVGKey, 12 | } from './built-in-component-factories' 13 | import { 14 | GlamorousComponent, 15 | GlamorousComponentProps, 16 | GlamorousComponentFunctions, 17 | ExtraGlamorousProps, 18 | } from './glamorous-component' 19 | import { 20 | BuiltInGlamorousComponentFactory, 21 | KeyGlamorousComponentFactory, 22 | KeyGlamorousComponentFactoryCssOverides, 23 | GlamorousComponentFactory, 24 | GlamorousComponentFactoryCssOverides, 25 | } from './component-factory' 26 | import { 27 | CSSProperties, 28 | CSSPropertiesCompleteSingle, 29 | CSSPropertiesComplete, 30 | CSSPropertiesPseudo, 31 | CSSPropertiesLossy, 32 | } from './css-properties' 33 | import { 34 | SVGProperties, 35 | SVGPropertiesCompleteSingle, 36 | SVGPropertiesComplete, 37 | SVGPropertiesLossy, 38 | } from './svg-properties' 39 | import {StyleFunction, StyleArray, StyleArgument} from './style-arguments' 40 | import { 41 | DOMTagComponentFactory, 42 | SVGTagComponentFactory, 43 | HTMLDomTags, 44 | SVGDomTags, 45 | DOMTagGlamorousComponentFactory, 46 | SVGTagGlamorousComponentFactory, 47 | } from './dom-tag-component-factory' 48 | 49 | import {Omit} from './helpers' 50 | 51 | export { 52 | CSSProperties, 53 | CSSPropertiesCompleteSingle, 54 | CSSPropertiesComplete, 55 | CSSPropertiesPseudo, 56 | CSSPropertiesLossy, 57 | SVGProperties, 58 | SVGPropertiesCompleteSingle, 59 | SVGPropertiesComplete, 60 | SVGPropertiesLossy, 61 | GlamorousComponent, 62 | GlamorousComponentProps, 63 | GlamorousComponentFunctions, 64 | ExtraGlamorousProps, 65 | StyleFunction, 66 | StyleArray, 67 | StyleArgument, 68 | BuiltInGlamorousComponentFactory, 69 | KeyGlamorousComponentFactory, 70 | KeyGlamorousComponentFactoryCssOverides, 71 | GlamorousComponentFactory, 72 | GlamorousComponentFactoryCssOverides, 73 | HTMLComponent, 74 | SVGComponent, 75 | HTMLComponentFactory, 76 | HTMLKey, 77 | SVGComponentFactory, 78 | SVGKey, 79 | DOMTagComponentFactory, 80 | SVGTagComponentFactory, 81 | HTMLDomTags, 82 | SVGDomTags, 83 | DOMTagGlamorousComponentFactory, 84 | SVGTagGlamorousComponentFactory, 85 | } 86 | 87 | export interface GlamorousOptions { 88 | /** 89 | * Used by React in the React DevTools 90 | */ 91 | displayName: string 92 | /** 93 | * Applies the same logic to components (html/svg) for which props to forward as it does for the built-in factories 94 | */ 95 | rootEl: string | Element 96 | /** 97 | * Specifies extra props that should be forwarded and spread on the DOM element 98 | */ 99 | forwardProps: string[] 100 | /** 101 | * Specifies properties to filter out from being forwarded to the child components; Useful for components like react-router Link 102 | */ 103 | filterProps: string[] 104 | /** 105 | * Allows you to prevent glamor from computing your styles when you know the class name should not change. 106 | */ 107 | shouldClassNameUpdate: ( 108 | props: Props, 109 | prevProps: Props, 110 | context: Context, 111 | prevContext: Context, 112 | ) => boolean 113 | /** 114 | * Allows you to use props as CSS 115 | */ 116 | propsAreCssOverrides?: false 117 | /** 118 | * Applies props by default for a component 119 | */ 120 | withProps: DefaultProps 121 | } 122 | 123 | export interface PropsAreCssOverridesGlamorousOptions< 124 | Props, 125 | Context, 126 | DefaultProps 127 | > { 128 | /** 129 | * Used by React in the React DevTools 130 | */ 131 | displayName?: string 132 | /** 133 | * Applies the same logic to components (html/svg) for which props to forward as it does for the built-in factories 134 | */ 135 | rootEl?: string | Element 136 | /** 137 | * Specifies extra props that should be forwarded and spread on the DOM element 138 | */ 139 | forwardProps?: string[] 140 | /** 141 | * Specifies properties to filter out from being forwarded to the child components; Useful for components like react-router Link 142 | */ 143 | filterProps?: string[] 144 | /** 145 | * Allows you to prevent glamor from computing your styles when you know the class name should not change. 146 | */ 147 | shouldClassNameUpdate?: ( 148 | props: Props, 149 | prevProps: Props, 150 | context: Context, 151 | prevContext: Context, 152 | ) => boolean 153 | /** 154 | * Allows you to use props as CSS 155 | */ 156 | propsAreCssOverrides: true 157 | /** 158 | * Applies props by default for a component 159 | */ 160 | withProps?: DefaultProps 161 | } 162 | 163 | export type Component = React.ComponentClass | React.StatelessComponent 164 | 165 | type OmitInternals = Omit< 166 | Props, 167 | 'className' | 'theme' 168 | > 169 | 170 | type GlamorousProps = {className?: string; theme?: object} 171 | 172 | export interface GlamorousInterface 173 | extends HTMLComponentFactory, 174 | SVGComponentFactory, 175 | HTMLComponent, 176 | SVGComponent, 177 | DOMTagComponentFactory, 178 | SVGTagComponentFactory { 179 | // # Glamarous Component factories 180 | 181 | // Two overloads are needed per shape due to a union return of CSSProperties | SVGProperties 182 | // resulting in a loss of typesafety on function arguments 183 | 184 | // ## create a component factory from your own component 185 | 186 | ( 187 | component: Component, 188 | options?: Partial>, 189 | ): GlamorousComponentFactory 190 | ( 191 | component: Component, 192 | options?: Partial>, 193 | ): GlamorousComponentFactory 194 | 195 | ( 196 | component: Component, 197 | options?: PropsAreCssOverridesGlamorousOptions< 198 | ExternalProps, 199 | Context, 200 | DefaultProps 201 | >, 202 | ): GlamorousComponentFactoryCssOverides< 203 | ExternalProps, 204 | CSSProperties, 205 | DefaultProps 206 | > 207 | ( 208 | component: Component, 209 | options?: PropsAreCssOverridesGlamorousOptions< 210 | ExternalProps, 211 | Context, 212 | DefaultProps 213 | >, 214 | ): GlamorousComponentFactoryCssOverides< 215 | ExternalProps, 216 | SVGProperties, 217 | DefaultProps 218 | > 219 | } 220 | 221 | interface ThemeProps { 222 | theme: object 223 | } 224 | 225 | export class ThemeProvider extends React.Component {} 226 | 227 | export function withTheme( 228 | component: React.ComponentClass, 229 | ): React.ComponentClass> 230 | 231 | export function withTheme( 232 | component: React.StatelessComponent, 233 | ): React.StatelessComponent> 234 | 235 | export * from './named-built-in-glamorous-components' 236 | 237 | declare const glamorous: GlamorousInterface 238 | 239 | export default glamorous 240 | -------------------------------------------------------------------------------- /typings/helpers.d.ts: -------------------------------------------------------------------------------- 1 | export type Omit = Pick> 2 | 3 | export type SingleOrArray = { 4 | [P in T]: Properties[P] | Array 5 | } 6 | -------------------------------------------------------------------------------- /typings/preact/built-in-component-factories.d.ts: -------------------------------------------------------------------------------- 1 | import {SVGProperties} from './svg-properties' 2 | import {CSSProperties} from './css-properties' 3 | import {BuiltInGlamorousComponentFactory} from './component-factory' 4 | 5 | export type HTMLGlamorousComponentFactory< 6 | HTMLElement 7 | > = BuiltInGlamorousComponentFactory 8 | 9 | export type SVGGlamorousComponentFactory< 10 | SVGElement 11 | > = BuiltInGlamorousComponentFactory 12 | 13 | export interface HTMLComponentFactory { 14 | a: HTMLGlamorousComponentFactory 15 | abbr: HTMLGlamorousComponentFactory 16 | address: HTMLGlamorousComponentFactory 17 | area: HTMLGlamorousComponentFactory 18 | article: HTMLGlamorousComponentFactory 19 | aside: HTMLGlamorousComponentFactory 20 | audio: HTMLGlamorousComponentFactory 21 | b: HTMLGlamorousComponentFactory 22 | base: HTMLGlamorousComponentFactory 23 | bdi: HTMLGlamorousComponentFactory 24 | bdo: HTMLGlamorousComponentFactory 25 | big: HTMLGlamorousComponentFactory 26 | blockquote: HTMLGlamorousComponentFactory 27 | body: HTMLGlamorousComponentFactory 28 | br: HTMLGlamorousComponentFactory 29 | button: HTMLGlamorousComponentFactory 30 | canvas: HTMLGlamorousComponentFactory 31 | caption: HTMLGlamorousComponentFactory 32 | cite: HTMLGlamorousComponentFactory 33 | code: HTMLGlamorousComponentFactory 34 | col: HTMLGlamorousComponentFactory 35 | colgroup: HTMLGlamorousComponentFactory 36 | data: HTMLGlamorousComponentFactory 37 | datalist: HTMLGlamorousComponentFactory 38 | dd: HTMLGlamorousComponentFactory 39 | del: HTMLGlamorousComponentFactory 40 | details: HTMLGlamorousComponentFactory 41 | dfn: HTMLGlamorousComponentFactory 42 | dialog: HTMLGlamorousComponentFactory 43 | div: HTMLGlamorousComponentFactory 44 | dl: HTMLGlamorousComponentFactory 45 | dt: HTMLGlamorousComponentFactory 46 | em: HTMLGlamorousComponentFactory 47 | embed: HTMLGlamorousComponentFactory 48 | fieldset: HTMLGlamorousComponentFactory 49 | figcaption: HTMLGlamorousComponentFactory 50 | figure: HTMLGlamorousComponentFactory 51 | footer: HTMLGlamorousComponentFactory 52 | form: HTMLGlamorousComponentFactory 53 | h1: HTMLGlamorousComponentFactory 54 | h2: HTMLGlamorousComponentFactory 55 | h3: HTMLGlamorousComponentFactory 56 | h4: HTMLGlamorousComponentFactory 57 | h5: HTMLGlamorousComponentFactory 58 | h6: HTMLGlamorousComponentFactory 59 | head: HTMLGlamorousComponentFactory 60 | header: HTMLGlamorousComponentFactory 61 | hgroup: HTMLGlamorousComponentFactory 62 | hr: HTMLGlamorousComponentFactory 63 | html: HTMLGlamorousComponentFactory 64 | i: HTMLGlamorousComponentFactory 65 | iframe: HTMLGlamorousComponentFactory 66 | img: HTMLGlamorousComponentFactory 67 | input: HTMLGlamorousComponentFactory 68 | ins: HTMLGlamorousComponentFactory 69 | kbd: HTMLGlamorousComponentFactory 70 | keygen: HTMLGlamorousComponentFactory 71 | label: HTMLGlamorousComponentFactory 72 | legend: HTMLGlamorousComponentFactory 73 | li: HTMLGlamorousComponentFactory 74 | link: HTMLGlamorousComponentFactory 75 | main: HTMLGlamorousComponentFactory 76 | map: HTMLGlamorousComponentFactory 77 | mark: HTMLGlamorousComponentFactory 78 | menu: HTMLGlamorousComponentFactory 79 | menuitem: HTMLGlamorousComponentFactory 80 | meta: HTMLGlamorousComponentFactory 81 | meter: HTMLGlamorousComponentFactory 82 | nav: HTMLGlamorousComponentFactory 83 | noscript: HTMLGlamorousComponentFactory 84 | object: HTMLGlamorousComponentFactory 85 | ol: HTMLGlamorousComponentFactory 86 | optgroup: HTMLGlamorousComponentFactory 87 | option: HTMLGlamorousComponentFactory 88 | output: HTMLGlamorousComponentFactory 89 | p: HTMLGlamorousComponentFactory 90 | param: HTMLGlamorousComponentFactory 91 | picture: HTMLGlamorousComponentFactory 92 | pre: HTMLGlamorousComponentFactory 93 | progress: HTMLGlamorousComponentFactory 94 | q: HTMLGlamorousComponentFactory 95 | rp: HTMLGlamorousComponentFactory 96 | rt: HTMLGlamorousComponentFactory 97 | ruby: HTMLGlamorousComponentFactory 98 | s: HTMLGlamorousComponentFactory 99 | samp: HTMLGlamorousComponentFactory 100 | script: HTMLGlamorousComponentFactory 101 | section: HTMLGlamorousComponentFactory 102 | select: HTMLGlamorousComponentFactory 103 | small: HTMLGlamorousComponentFactory 104 | source: HTMLGlamorousComponentFactory 105 | span: HTMLGlamorousComponentFactory 106 | strong: HTMLGlamorousComponentFactory 107 | style: HTMLGlamorousComponentFactory 108 | sub: HTMLGlamorousComponentFactory 109 | summary: HTMLGlamorousComponentFactory 110 | sup: HTMLGlamorousComponentFactory 111 | table: HTMLGlamorousComponentFactory 112 | tbody: HTMLGlamorousComponentFactory 113 | td: HTMLGlamorousComponentFactory 114 | textarea: HTMLGlamorousComponentFactory 115 | tfoot: HTMLGlamorousComponentFactory 116 | th: HTMLGlamorousComponentFactory 117 | thead: HTMLGlamorousComponentFactory 118 | time: HTMLGlamorousComponentFactory 119 | title: HTMLGlamorousComponentFactory 120 | tr: HTMLGlamorousComponentFactory 121 | track: HTMLGlamorousComponentFactory 122 | u: HTMLGlamorousComponentFactory 123 | ul: HTMLGlamorousComponentFactory 124 | var: HTMLGlamorousComponentFactory 125 | video: HTMLGlamorousComponentFactory 126 | wbr: HTMLGlamorousComponentFactory 127 | } 128 | 129 | export type HTMLKey = keyof HTMLComponentFactory 130 | 131 | export interface SVGComponentFactory { 132 | circle: SVGGlamorousComponentFactory 133 | clipPath: SVGGlamorousComponentFactory 134 | defs: SVGGlamorousComponentFactory 135 | ellipse: SVGGlamorousComponentFactory 136 | g: SVGGlamorousComponentFactory 137 | image: SVGGlamorousComponentFactory 138 | line: SVGGlamorousComponentFactory 139 | linearGradient: SVGGlamorousComponentFactory 140 | mask: SVGGlamorousComponentFactory 141 | path: SVGGlamorousComponentFactory 142 | pattern: SVGGlamorousComponentFactory 143 | polygon: SVGGlamorousComponentFactory 144 | polyline: SVGGlamorousComponentFactory 145 | radialGradient: SVGGlamorousComponentFactory 146 | rect: SVGGlamorousComponentFactory 147 | stop: SVGGlamorousComponentFactory 148 | svg: SVGGlamorousComponentFactory 149 | text: SVGGlamorousComponentFactory 150 | tspan: SVGGlamorousComponentFactory 151 | } 152 | 153 | export type SVGKey = keyof SVGComponentFactory 154 | -------------------------------------------------------------------------------- /typings/preact/component-factory.d.ts: -------------------------------------------------------------------------------- 1 | import {GlamorousComponent} from './glamorous-component' 2 | import {Omit} from './helpers' 3 | 4 | import {StyleArgument, StaticStyleArgument} from './style-arguments' 5 | 6 | // # built-in DOM - component factories glamorous.div 7 | 8 | export interface BuiltInGlamorousComponentFactory { 9 | (...styles: StaticStyleArgument[]): GlamorousComponent< 10 | ElementProps, 11 | object 12 | > 13 | 14 | ( 15 | ...styles: StyleArgument[] 16 | ): GlamorousComponent & ElementProps, Props> 17 | 18 | (...styles: StyleArgument[]): GlamorousComponent< 19 | Props & ElementProps, 20 | Props 21 | > 22 | } 23 | 24 | // # dom tag - component factories glamorous('div') 25 | 26 | // ## without propsAreCssOverides 27 | 28 | export interface KeyGlamorousComponentFactory< 29 | ElementProps, 30 | Properties, 31 | ExternalProps, 32 | DefaultProps 33 | > { 34 | (...styles: StaticStyleArgument[]): GlamorousComponent< 35 | ElementProps & ExternalProps & Partial, 36 | ExternalProps 37 | > 38 | 39 | ( 40 | ...styles: StyleArgument[] 41 | ): GlamorousComponent< 42 | ElementProps & 43 | ExternalProps & 44 | Partial & 45 | Omit & 46 | Props, 47 | ExternalProps 48 | > 49 | 50 | ( 51 | ...styles: StyleArgument[] 52 | ): GlamorousComponent< 53 | ElementProps & ExternalProps & Partial & Props, 54 | ExternalProps 55 | > 56 | } 57 | 58 | // ## with propsAreCssOverides 59 | 60 | export interface KeyGlamorousComponentFactoryCssOverides< 61 | ElementProps, 62 | Properties, 63 | ExternalProps, 64 | DefaultProps 65 | > { 66 | (...styles: StaticStyleArgument[]): GlamorousComponent< 67 | ElementProps & ExternalProps & Partial & Properties, 68 | ExternalProps 69 | > 70 | 71 | ( 72 | ...styles: StyleArgument[] 73 | ): GlamorousComponent< 74 | ElementProps & 75 | ExternalProps & 76 | Partial & 77 | Omit & 78 | Properties, 79 | ExternalProps 80 | > 81 | 82 | ( 83 | ...styles: StyleArgument[] 84 | ): GlamorousComponent< 85 | ElementProps & ExternalProps & Partial & Props & Properties, 86 | ExternalProps 87 | > 88 | } 89 | 90 | // # react component - component factories glamorous(Component) 91 | 92 | // ## without propsAreCssOverides 93 | 94 | export interface GlamorousComponentFactory< 95 | ExternalProps, 96 | Properties, 97 | DefaultProps 98 | > { 99 | (...styles: StaticStyleArgument[]): GlamorousComponent< 100 | ExternalProps & Partial, 101 | object 102 | > 103 | 104 | ( 105 | ...styles: StyleArgument[] 106 | ): GlamorousComponent< 107 | ExternalProps & Partial & Omit, 108 | Props 109 | > 110 | 111 | ( 112 | ...styles: StyleArgument[] 113 | ): GlamorousComponent & Props, Props> 114 | } 115 | 116 | // ## with propsAreCssOverides 117 | 118 | export interface GlamorousComponentFactoryCssOverides< 119 | ExternalProps, 120 | Properties, 121 | DefaultProps 122 | > { 123 | (...styles: StaticStyleArgument[]): GlamorousComponent< 124 | ExternalProps & Partial & Properties, 125 | object 126 | > 127 | 128 | ( 129 | ...styles: StyleArgument[] 130 | ): GlamorousComponent< 131 | ExternalProps & Partial & Properties & Omit, 132 | Props 133 | > 134 | 135 | ( 136 | ...styles: StyleArgument[] 137 | ): GlamorousComponent< 138 | ExternalProps & Partial & Properties & Props, 139 | Props 140 | > 141 | } 142 | -------------------------------------------------------------------------------- /typings/preact/css-properties.d.ts: -------------------------------------------------------------------------------- 1 | import * as CSS from 'csstype' 2 | import {SingleOrArray} from './helpers' 3 | 4 | export interface CSSPropertiesCompleteSingle 5 | extends CSS.Properties {} 6 | 7 | export type CSSPropertiesPseudo = {[K in CSS.Pseudos]?: CSSProperties} 8 | 9 | type CSSPropertiesComplete = SingleOrArray< 10 | CSSPropertiesCompleteSingle, 11 | keyof CSSPropertiesCompleteSingle 12 | > 13 | 14 | export interface CSSPropertiesLossy { 15 | [propertyName: string]: 16 | | string 17 | | number 18 | | CSSPropertiesComplete 19 | | undefined 20 | | Array 21 | | CSSPropertiesLossy 22 | } 23 | 24 | export interface CSSProperties 25 | extends CSSPropertiesComplete, 26 | CSSPropertiesPseudo, 27 | CSSPropertiesLossy {} 28 | 29 | export type CSSPropertiesRecursive = 30 | | CSSProperties 31 | | CSSPropertiesArray 32 | | CSSFunction 33 | 34 | // TODO: This could be made generic. Issue PR if you're so inclined! 35 | export interface CSSFunction { 36 | (props: {}): CSSPropertiesRecursive 37 | } 38 | 39 | export interface CSSPropertiesArray extends Array {} 40 | -------------------------------------------------------------------------------- /typings/preact/glamorous-component.d.ts: -------------------------------------------------------------------------------- 1 | import {CSSPropertiesRecursive} from './css-properties' 2 | 3 | import {Component} from './glamorous' 4 | 5 | import {Omit} from './helpers' 6 | 7 | import * as preact from 'preact' 8 | 9 | /** 10 | * `glamorousComponentFactory` returns a ComponentClass 11 | * 12 | * @see {@link https://github.com/paypal/glamorous/blob/master/src/create-glamorous.js#L28-L131} 13 | */ 14 | 15 | export interface ExtraGlamorousProps { 16 | /** 17 | * Called with the inner element's reference 18 | */ 19 | innerRef?: object | ((instance: any) => void) 20 | 21 | className?: string 22 | /** 23 | * Same type as any of the styles provided, will be merged with this component's styles and take highest priority over the component's predefined styles 24 | */ 25 | css?: CSSPropertiesRecursive 26 | theme?: object 27 | } 28 | 29 | export interface GlamorousComponentFunctions { 30 | /** 31 | * Copies the styles of an already created glamorous component with a different tag 32 | */ 33 | withComponent: ( 34 | component: string | Component, 35 | ) => GlamorousComponent 36 | 37 | /** 38 | * Applies props by default for a component 39 | */ 40 | withProps: ( 41 | props: DefaultProps, 42 | ) => GlamorousComponent, Props> 43 | } 44 | 45 | export interface GlamorousComponent 46 | extends preact.ComponentConstructor, 47 | GlamorousComponentFunctions {} 48 | 49 | interface IntrinsicAttributes { 50 | key?: preact.Key 51 | } 52 | interface IntrinsicClassAttributes extends IntrinsicAttributes { 53 | ref?: preact.Ref 54 | } 55 | 56 | export type GlamorousComponentProps = IntrinsicAttributes & 57 | IntrinsicClassAttributes< 58 | preact.Component 59 | > & 60 | ExternalProps & {children?: JSX.Element} 61 | -------------------------------------------------------------------------------- /typings/preact/glamorous.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Glamorous v3.2.0 2 | // Project: https://github.com/paypal/glamorous 3 | // Definitions by: Kok Sam 4 | 5 | import * as preact from 'preact' 6 | import {HTMLComponent, SVGComponent} from './built-in-glamorous-components' 7 | import { 8 | HTMLComponentFactory, 9 | HTMLKey, 10 | SVGComponentFactory, 11 | SVGKey, 12 | } from './built-in-component-factories' 13 | import { 14 | GlamorousComponent, 15 | GlamorousComponentProps, 16 | GlamorousComponentFunctions, 17 | ExtraGlamorousProps, 18 | } from './glamorous-component' 19 | import { 20 | BuiltInGlamorousComponentFactory, 21 | KeyGlamorousComponentFactory, 22 | KeyGlamorousComponentFactoryCssOverides, 23 | GlamorousComponentFactory, 24 | GlamorousComponentFactoryCssOverides, 25 | } from './component-factory' 26 | import { 27 | CSSProperties, 28 | CSSPropertiesCompleteSingle, 29 | CSSPropertiesComplete, 30 | CSSPropertiesPseudo, 31 | CSSPropertiesLossy, 32 | } from './css-properties' 33 | import { 34 | SVGProperties, 35 | SVGPropertiesCompleteSingle, 36 | SVGPropertiesComplete, 37 | SVGPropertiesLossy, 38 | } from './svg-properties' 39 | import {StyleFunction, StyleArray, StyleArgument} from './style-arguments' 40 | import { 41 | DOMTagComponentFactory, 42 | SVGTagComponentFactory, 43 | HTMLDomTags, 44 | SVGDomTags, 45 | DOMTagGlamorousComponentFactory, 46 | SVGTagGlamorousComponentFactory, 47 | } from './dom-tag-component-factory' 48 | 49 | import {Omit} from './helpers' 50 | 51 | export { 52 | CSSProperties, 53 | CSSPropertiesCompleteSingle, 54 | CSSPropertiesComplete, 55 | CSSPropertiesPseudo, 56 | CSSPropertiesLossy, 57 | SVGProperties, 58 | SVGPropertiesCompleteSingle, 59 | SVGPropertiesComplete, 60 | SVGPropertiesLossy, 61 | GlamorousComponent, 62 | GlamorousComponentProps, 63 | GlamorousComponentFunctions, 64 | ExtraGlamorousProps, 65 | StyleFunction, 66 | StyleArray, 67 | StyleArgument, 68 | BuiltInGlamorousComponentFactory, 69 | KeyGlamorousComponentFactory, 70 | KeyGlamorousComponentFactoryCssOverides, 71 | GlamorousComponentFactory, 72 | GlamorousComponentFactoryCssOverides, 73 | HTMLComponent, 74 | SVGComponent, 75 | HTMLComponentFactory, 76 | HTMLKey, 77 | SVGComponentFactory, 78 | SVGKey, 79 | DOMTagComponentFactory, 80 | SVGTagComponentFactory, 81 | HTMLDomTags, 82 | SVGDomTags, 83 | DOMTagGlamorousComponentFactory, 84 | SVGTagGlamorousComponentFactory, 85 | } 86 | 87 | export interface GlamorousOptions { 88 | /** 89 | * Used by React in the React DevTools 90 | */ 91 | displayName: string 92 | /** 93 | * Applies the same logic to components (html/svg) for which props to forward as it does for the built-in factories 94 | */ 95 | rootEl: string | Element 96 | /** 97 | * Specifies extra props that should be forwarded and spread on the DOM element 98 | */ 99 | forwardProps: string[] 100 | /** 101 | * Specifies properties to filter out from being forwarded to the child components; Useful for components like react-router Link 102 | */ 103 | filterProps: string[] 104 | /** 105 | * Allows you to prevent glamor from computing your styles when you know the class name should not change. 106 | */ 107 | shouldClassNameUpdate: ( 108 | props: Props, 109 | prevProps: Props, 110 | context: Context, 111 | prevContext: Context, 112 | ) => boolean 113 | /** 114 | * Allows you to use props as CSS 115 | */ 116 | propsAreCssOverrides?: false 117 | /** 118 | * Applies props by default for a component 119 | */ 120 | withProps: DefaultProps 121 | } 122 | 123 | export interface PropsAreCssOverridesGlamorousOptions< 124 | Props, 125 | Context, 126 | DefaultProps 127 | > { 128 | /** 129 | * Used by React in the React DevTools 130 | */ 131 | displayName?: string 132 | /** 133 | * Applies the same logic to components (html/svg) for which props to forward as it does for the built-in factories 134 | */ 135 | rootEl?: string | Element 136 | /** 137 | * Specifies extra props that should be forwarded and spread on the DOM element 138 | */ 139 | forwardProps?: string[] 140 | /** 141 | * Specifies properties to filter out from being forwarded to the child components; Useful for components like react-router Link 142 | */ 143 | filterProps?: string[] 144 | /** 145 | * Allows you to prevent glamor from computing your styles when you know the class name should not change. 146 | */ 147 | shouldClassNameUpdate?: ( 148 | props: Props, 149 | prevProps: Props, 150 | context: Context, 151 | prevContext: Context, 152 | ) => boolean 153 | /** 154 | * Allows you to use props as CSS 155 | */ 156 | propsAreCssOverrides: true 157 | /** 158 | * Applies props by default for a component 159 | */ 160 | withProps?: DefaultProps 161 | } 162 | 163 | export type Component = 164 | | preact.ComponentConstructor 165 | | preact.FunctionalComponent 166 | 167 | type OmitInternals = Omit< 168 | Props, 169 | 'className' | 'theme' 170 | > 171 | 172 | type GlamorousProps = {className?: string; theme?: object} 173 | 174 | export interface GlamorousInterface 175 | extends HTMLComponentFactory, 176 | SVGComponentFactory, 177 | HTMLComponent, 178 | SVGComponent, 179 | DOMTagComponentFactory, 180 | SVGTagComponentFactory { 181 | // # Glamarous Component factories 182 | 183 | // Two overloads are needed per shape due to a union return of CSSProperties | SVGProperties 184 | // resulting in a loss of typesafety on function arguments 185 | 186 | // ## create a component factory from your own component 187 | 188 | ( 189 | component: Component, 190 | options?: Partial>, 191 | ): GlamorousComponentFactory 192 | ( 193 | component: Component, 194 | options?: Partial>, 195 | ): GlamorousComponentFactory 196 | 197 | ( 198 | component: Component, 199 | options?: PropsAreCssOverridesGlamorousOptions< 200 | ExternalProps, 201 | Context, 202 | DefaultProps 203 | >, 204 | ): GlamorousComponentFactoryCssOverides< 205 | ExternalProps, 206 | CSSProperties, 207 | DefaultProps 208 | > 209 | ( 210 | component: Component, 211 | options?: PropsAreCssOverridesGlamorousOptions< 212 | ExternalProps, 213 | Context, 214 | DefaultProps 215 | >, 216 | ): GlamorousComponentFactoryCssOverides< 217 | ExternalProps, 218 | SVGProperties, 219 | DefaultProps 220 | > 221 | } 222 | 223 | interface ThemeProps { 224 | theme: object 225 | } 226 | 227 | export abstract class ThemeProvider extends preact.Component {} 228 | 229 | export function withTheme( 230 | component: preact.ComponentConstructor, 231 | ): preact.ComponentConstructor> 232 | 233 | export function withTheme( 234 | component: preact.FunctionalComponent, 235 | ): preact.FunctionalComponent> 236 | 237 | export * from './named-built-in-glamorous-components' 238 | 239 | declare const glamorous: GlamorousInterface 240 | 241 | export default glamorous 242 | -------------------------------------------------------------------------------- /typings/preact/helpers.d.ts: -------------------------------------------------------------------------------- 1 | export type Omit = Pick> 2 | 3 | export type SingleOrArray = { 4 | [P in T]: Properties[P] | Array 5 | } 6 | -------------------------------------------------------------------------------- /typings/preact/style-arguments.d.ts: -------------------------------------------------------------------------------- 1 | export interface StyleFunction { 2 | (props: Props): 3 | | Properties 4 | | string 5 | | Array> 6 | } 7 | 8 | export type StyleArray = Array< 9 | Properties | string | StyleFunction 10 | > 11 | 12 | export type StaticStyleArray = Array 13 | 14 | export type StyleArgument = 15 | | Properties 16 | | string 17 | | StyleFunction 18 | | StyleArray 19 | 20 | export type StaticStyleArgument = 21 | | Properties 22 | | string 23 | | StaticStyleArray 24 | -------------------------------------------------------------------------------- /typings/preact/svg-properties.d.ts: -------------------------------------------------------------------------------- 1 | import * as CSS from 'csstype' 2 | import {SingleOrArray} from './helpers' 3 | 4 | export interface SVGPropertiesCompleteSingle 5 | extends CSS.SvgProperties {} 6 | 7 | type SVGPropertiesComplete = SingleOrArray< 8 | SVGPropertiesCompleteSingle, 9 | keyof SVGPropertiesCompleteSingle 10 | > 11 | 12 | export interface SVGPropertiesLossy { 13 | [propertyName: string]: 14 | | string 15 | | number 16 | | SVGProperties 17 | | undefined 18 | | Array 19 | | SVGPropertiesLossy 20 | } 21 | 22 | export interface SVGProperties 23 | extends SVGPropertiesComplete, 24 | SVGPropertiesLossy {} 25 | -------------------------------------------------------------------------------- /typings/style-arguments.d.ts: -------------------------------------------------------------------------------- 1 | export interface StyleFunction { 2 | (props: Props): 3 | | Properties 4 | | string 5 | | Array> 6 | } 7 | 8 | export type StyleArray = Array< 9 | Properties | string | StyleFunction 10 | > 11 | 12 | export type StaticStyleArray = Array 13 | 14 | export type StyleArgument = 15 | | Properties 16 | | string 17 | | StyleFunction 18 | | StyleArray 19 | 20 | export type StaticStyleArgument = 21 | | Properties 22 | | string 23 | | StaticStyleArray 24 | -------------------------------------------------------------------------------- /typings/svg-properties.d.ts: -------------------------------------------------------------------------------- 1 | import * as CSS from 'csstype' 2 | import {SingleOrArray} from './helpers' 3 | 4 | export interface SVGPropertiesCompleteSingle 5 | extends CSS.SvgProperties {} 6 | 7 | type SVGPropertiesComplete = SingleOrArray< 8 | SVGPropertiesCompleteSingle, 9 | keyof SVGPropertiesCompleteSingle 10 | > 11 | 12 | export interface SVGPropertiesLossy { 13 | [propertyName: string]: 14 | | string 15 | | number 16 | | SVGProperties 17 | | undefined 18 | | Array 19 | | SVGPropertiesLossy 20 | } 21 | 22 | export interface SVGProperties 23 | extends SVGPropertiesComplete, 24 | SVGPropertiesLossy {} 25 | --------------------------------------------------------------------------------