` 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 |
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 |
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