├── .babelrc
├── .codeclimate.yml
├── .editorconfig
├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── components
├── Alert.jsx
├── AnimatedMenu.jsx
├── Block.jsx
├── Block
│ └── Installments.jsx
├── Button.jsx
├── Checklist.jsx
├── ContextMenu.jsx
├── Dialog.jsx
├── Dropdown.jsx
├── Field.jsx
├── Fieldset.jsx
├── IconButton.jsx
├── Input.jsx
├── Installments.jsx
├── Label.jsx
├── Link.jsx
├── Loader
│ ├── index.jsx
│ └── styles.scss
├── Menu.jsx
├── PayButton.jsx
├── Preview.jsx
├── RadioGroup.jsx
├── Selector.jsx
├── Switch.jsx
├── Text.jsx
├── Theme.jsx
├── Tooltip.jsx
├── icons
│ ├── AccountActivated.jsx
│ ├── AllSet.jsx
│ ├── Arrow.jsx
│ ├── Cancel.jsx
│ ├── Checkmark.jsx
│ ├── CreditCard.jsx
│ ├── Details.jsx
│ ├── Done.jsx
│ ├── Download.jsx
│ ├── Error.jsx
│ ├── ExtendDate.jsx
│ ├── Items.jsx
│ ├── KlarnaLogo.jsx
│ ├── Letter.jsx
│ ├── Logout.jsx
│ ├── Mail.jsx
│ ├── NotFound.jsx
│ ├── OpenLetter.jsx
│ ├── PadLock.jsx
│ ├── Password.jsx
│ ├── Person.jsx
│ ├── Phone.jsx
│ ├── Question.jsx
│ ├── Remind.jsx
│ ├── SMS.jsx
│ ├── Time.jsx
│ ├── Warning.jsx
│ ├── Wrong.jsx
│ ├── constants
│ │ └── colors.es6
│ └── parts
│ │ ├── Circle.jsx
│ │ └── File.jsx
├── texts
│ ├── Amount.jsx
│ ├── Paragraph.jsx
│ ├── PrimaryTitle.jsx
│ ├── SecondaryTitle.jsx
│ ├── Subtitle.jsx
│ ├── TextLabel.jsx
│ └── palette.es6
├── themeable
│ ├── Button.jsx
│ ├── Checklist.jsx
│ ├── Dropdown.jsx
│ ├── Field.jsx
│ ├── Installments.jsx
│ ├── Link.jsx
│ ├── Switch.jsx
│ ├── Text.jsx
│ └── texts
│ │ ├── Paragraph.jsx
│ │ ├── PrimaryTitle.jsx
│ │ ├── SecondaryTitle.jsx
│ │ └── Subtitle.jsx
└── uncontrolled
│ ├── Field.jsx
│ ├── Input.jsx
│ ├── Installments.jsx
│ └── RadioGroup.jsx
├── docs
└── patterns.md
├── example
├── Alerts.jsx
├── Blocks.jsx
├── Buttons.jsx
├── Checklists.jsx
├── Code.jsx
├── ContextMenus.jsx
├── Dialogs.jsx
├── Dropdowns.jsx
├── Fields.jsx
├── Icons.jsx
├── Inputs.jsx
├── Installments.jsx
├── Labels.jsx
├── Links.jsx
├── Loaders.jsx
├── Menus.jsx
├── Previews.jsx
├── RadioGroups.jsx
├── Selectors.jsx
├── Switches.jsx
├── Texts.jsx
├── Tooltips.jsx
├── examples.es6
├── index.html
├── index.jsx
└── index.scss
├── karma.conf.js
├── lib
├── combinations.es6
├── compose.es6
├── decorators
│ ├── statefulFocus.jsx
│ ├── statefulValue.jsx
│ └── themeable.jsx
├── features
│ ├── fieldStates
│ │ └── index.es6
│ ├── inlinedIcon
│ │ └── index.jsx
│ ├── keyboardEvents
│ │ └── index.es6
│ ├── programmaticFocus
│ │ └── index.es6
│ └── stacking
│ │ └── index.es6
├── toObjectWithValue.es6
├── validators.es6
└── values.es6
├── package.json
├── propTypes
├── fieldSizeFraction.es6
└── validateSize.es6
├── tests
├── Alert.spec.jsx
├── Amount.spec.jsx
├── Button.spec.jsx
├── Checklist.spec.jsx
├── Dialog.spec.jsx
├── Dropdown.spec.jsx
├── Field.spec.jsx
├── Fieldset.spec.jsx
├── Input.spec.jsx
├── Installments.spec.jsx
├── Label.spec.jsx
├── Link.spec.jsx
├── Loader.spec.jsx
├── Menu.spec.jsx
├── Paragraph.spec.jsx
├── PayButton.spec.jsx
├── Preview.spec.jsx
├── PrimaryTitle.spec.jsx
├── RadioGroup.spec.jsx
├── SecondaryTitle.spec.jsx
├── Selector.spec.jsx
├── Subtitle.spec.jsx
├── Switch.spec.jsx
├── TextLabel.spec.jsx
├── Tooltip.spec.jsx
├── decorators
│ ├── statefulFocus.spec.jsx
│ └── statefulValue.spec.jsx
├── describePalette.es6
├── helpers.jsx
├── propTypes
│ ├── fieldSizeFraction.spec.es6
│ └── validateSize.spec.es6
└── uncontrolled
│ └── RadioGroup.spec.jsx
├── webpack.config.js
└── webpack.config.test.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "react",
5 | "stage-0"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | exclude_paths:
2 | - example/**/*
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | indent_style = space
7 | end_of_line = lf
8 | insert_final_newline = true
9 |
10 | [*.{json,js,es6,jsx}]
11 | charset = utf-8
12 | indent_size = 2
13 | trim_trailing_whitespace = true
14 |
15 | [*.scss]
16 | charset = utf-8
17 | indent_size = 2
18 | trim_trailing_whitespace = true
19 |
20 | [Makefile]
21 | indent_style = tab
22 |
23 | [*.yml]
24 | indent_size = 2
25 |
26 | [package.json]
27 | indent_size = 2
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .idea/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | example
2 | karma.conf.js
3 | webpack.config.js
4 | webpack.config.test.js
5 | tests
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '4'
4 | before_script:
5 | - npm install
6 | script:
7 | - npm run lint
8 | - BROWSER=SL_IE_10,SL_IE_11,SL_MAC_SAFARI_9_0,SL_Android_4_3,SL_Android_5_0,SL_IOS_9_2,PhantomJS npm test
9 | deploy:
10 | provider: npm
11 | email: ui-admin.e@klarna.com
12 | api_key:
13 | secure: TSbAVMniIdqjogdG99ZbNbv00vg6TWnWbVdQDTNauGMeRxyZkZnb4Bh6z0SvpXiBC+Cf+AxiAjoCVf+SwL6Rzsrlef6qrmJcDxdDmxserlHvG9krZiwinTNWPmdNOssGdymP3d5KL4jVWIS5QybEl/TaAodbYVsS66ns7SjSEjl3UVA+FeexDGglntKa9ruIqxS9XSnA0SbwW/CLY8U2tYhbaz9ITPmNr5pJtSRJbPavo664mIlP1ndYpIYmzZsbqt30/PVD81X1lGYAgP63i8vzsPWBvwPGFLYa32HoYTrIYbfdf8lHiwilAUL3RQsl0NC8Onez1bfnr8wScoEBArQ0ytvDkO6/IZoCne1ZHWTdUGxUPTHOZoaSYR9e83pLfoRaiX1qJer40SqU+1ocfVxcndufYK1312DlJdJj6oexGH05lQsMeHJJMFZ7snastxk2OX1+WPSmCW9FRciwT+giqiVPRTkyMSsGuoQBQeDolTpSKu1dWWFKhSI2sXZcmdHGvOEFgYJZlzzh8JeVmoCBfXx9urCUEEjUf8X6OxvN1/gHc/OiY0t0mIsXIwohl+M6qJaA2ewRmPKZLvpVhvkgmwuj2ug2azwCyJbojt2AN7hfrrdkxO8Sws8DPyBTsm44bF0iJ25Ysi3wSAJiS4AgJIiWgW0H8VtwJMhXGc0=
14 | on:
15 | branch: master
16 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 0.16.0
2 |
3 | - Revise support for customizable/themeable border-radius.
4 | Currently we can only support vertically-stacked fields.
5 | - Add support for themeable Dropdown.
6 |
7 | # 0.15.0
8 |
9 | - Add support for wide `Dialog`
10 |
11 | # 0.14.1
12 |
13 | - Fixed missing borderColor on themeable Installments
14 |
15 | # 0.14.0
16 |
17 | - Add Theme and themeable for several components
18 |
19 | # 0.13.2
20 |
21 | - Fix dynamic styling of input border
22 |
23 | # 0.13.0
24 |
25 | - Update `Dialog` component
26 |
27 | # 0.12.11
28 |
29 | - Add alert box
30 |
31 | # 0.12.10
32 |
33 | - Spread props in Dropdown options to be able to set hidden and disabled
34 | - Set `describe` and `it` as globals for standard in the package.json
35 |
36 | # 0.12.9
37 |
38 | - Fix horizontal positioning of the Loader inside a Button
39 |
40 | # 0.12.8
41 |
42 | - Add dynamic styling support to Field, Switch, Button and Link
43 |
44 | # 0.12.7
45 |
46 | - Fix 'aria-labelledby' attribute on SVG in Checklist component
47 |
48 | # 0.12.6
49 |
50 | - Update Switch "checked" state in `componentWillReceiveProps`, based on the "checked" prop.
51 |
52 | # 0.12.5
53 |
54 | - Add `Dropdown` component
55 |
56 | # 0.12.4
57 |
58 | - Add a field with details icon inside
59 |
60 | # 0.12.3
61 |
62 | - Icons can receive `className` prop
63 |
64 | # 0.12.2
65 |
66 | - Add Cancel icon
67 |
68 | # 0.12.1
69 |
70 | - Add `viewBox` property to all icons
71 |
72 | # 0.12.0
73 |
74 | - Update all icons to use the CSS classes
75 | - Remove the icon HOC
76 |
77 | # 0.11.0
78 |
79 | - Update CSS components with default margins to 0
80 | - Add the `margins` flag to text components so text margins can be re enabled
81 | - Use components consistently throughout the showroom
82 |
83 | # 0.10.2
84 |
85 | - Add pad lock icon
86 | - Updated the css component version to support ie9 fields
87 |
88 | # 0.10.1
89 |
90 | - Change prop type of menu label to node
91 |
92 | # 0.10.0
93 |
94 | - Add legal variation of the Switch component
95 |
96 | # 0.9.0
97 |
98 | - Add Checklist component
99 |
100 | # 0.8.6
101 |
102 | - Added white loaders
103 |
104 | # 0.8.4
105 |
106 | - Merge 0.7.5 into 0.8.3
107 |
108 | # 0.8.0
109 |
110 | - New approach using decorators for adding the Uncontrolled versions
111 | Complete rewrite of the icon approach (needs review since some icons are clearly - meant for buttons)
112 | - Update naming convention (from stateless/stateful to controlled/uncontrolled)
113 | Add support for Field and Input stacking (buggy in the case of Input, but that's - probably due to ui-css-components shortcomings)
114 | - Add support for declarative focus in Field and Input
115 | - Cleanup composition of features in Field and Input
116 | - Added propType for fractional props
117 |
118 | # 0.7.5
119 |
120 | - Using ui-css-components to v5.6.5 to get some minor margins improvements
121 |
122 | # 0.7.4
123 |
124 | - Selector onChange callback called with an object instead of id
125 |
126 | # 0.7.2 & 0.7.3
127 |
128 | - Hotfixes for AnimatedMenu's transition
129 |
130 | # 0.7.0
131 |
132 | - Borderless input fields
133 | - Adds 'giant' input type
134 | - Controlled/uncontrolled inputs
135 |
136 | # 0.6.0
137 |
138 | - Adds white versions for texts
139 |
140 | # 0.5.4
141 |
142 | - Fixes ContextMenu prop types
143 |
144 | # 0.5.3
145 |
146 | - Adds ContextMenu
147 |
148 | # 0.5.2
149 |
150 | - Adds Selector with check icon
151 |
152 | # 0.5.1
153 |
154 | - Adds IconButtons with correct palette
155 |
156 | # 0.5.0
157 |
158 | - Adds LICENSE and open source
159 |
160 | # 0.4.0
161 |
162 | - Moves all icons from ui-illustrations
163 | - Use these icons for the Field
164 |
165 | # 0.3.0
166 |
167 | - Adds Switch error state
168 |
169 | # 0.2.5
170 |
171 | - Adds Label
172 | - Adds Dialog.Icon
173 |
174 | # 0.2.4
175 |
176 | - Adds Dialog
177 |
178 | # 0.2.3
179 |
180 | - Quickfix for AnimatedMenu
181 |
182 | # 0.2.2
183 |
184 | - Adds Tooltip with border
185 |
186 | # 0.2.1
187 |
188 | - Adds AnimatedMenu
189 |
190 | # 0.2.0
191 |
192 | - Adds Block
193 | - Adds Ammount
194 | - Reorganize Text components
195 | - Refactos Menus (breaking change)
196 |
197 | # 0.1.4
198 |
199 | - Adds TabMenu
200 |
201 | # 0.1.3
202 |
203 | - Adds RadioGroup & Stateteful.RadioGroup
204 |
205 | # 0.1.2
206 |
207 | - Adds Tooltip
208 |
209 | # 0.1.1
210 |
211 | - Adds Preview
212 | - Adds Switch
213 |
214 | # 0.1.0
215 |
216 | - Adds PayButton
217 | - Adds Field
218 | - Adds Icon
219 | - Adds Loader
220 | - Adds Button
221 | - Setup
222 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Source code is licensed under Apache 2.0
2 | http://www.apache.org/licenses/LICENSE-2.0
3 |
4 | All icons and images are licensed under Creative Commons Attribution-NoDerivatives 4.0
5 | http://creativecommons.org/licenses/by-nd/4.0/
6 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: test
2 |
3 | clean:
4 | rm -rf node_modules
5 |
6 | install:
7 | npm install
8 |
9 | lint:
10 | npm run lint
11 |
12 | dev:
13 | npm start
14 |
15 | test:
16 | npm test
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Klarna UI React Components
2 |
3 | [](https://travis-ci.org/klarna/ui-react-components)
4 | [](https://codeclimate.com/github/klarna/ui-react-components)
5 | [](https://www.npmjs.com/package/@klarna/ui-react-components)
6 |
7 | ## Note: This project and its sister project [klarna/ui-css-components](https://github.com/klarna/ui-css-components) are deprecated. Use [@klarna/ui](https://github.com/klarna/ui) instead.
8 |
9 | This library is a [React](https://facebook.github.io/react/) wrapper on top of [ui-css-components](https://github.com/klarna/ui-css-components).
10 |
11 | ## Install
12 |
13 | ```sh
14 | npm install @klarna/ui-react-components --save
15 | ```
16 |
17 | This package doesn't have a build, so you must have a Babel pipeline to use it. The minimal set of loaders is:
18 |
19 | ```javascript
20 | test: /\.(jsx|es6)$/
21 | loader: 'babel'
22 |
23 | test: /\.scss$/,
24 | loaders: [
25 | 'style',
26 | 'css?modules,localIdentName=[local]',
27 | 'sass'
28 | ]
29 |
30 | test: /\.(jpe?g|png|gif|svg|ico|eot|woff|ttf|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/i,
31 | loader: 'file' // or url
32 | ```
33 |
34 | You can see more in the project's `webpack.config.js`.
35 |
36 | ## Run locally
37 |
38 | > To run the project, NPM 3+ is required.
39 |
40 | To run the showroom locally:
41 |
42 | ```sh
43 | npm install
44 | npm start
45 | ```
46 |
47 | Open [localhost:7777](http://localhost:7777/).
48 |
49 | ## Using locally
50 |
51 | Most of the time you'll want to change things in `ui-react-components` and see how they reflect in your project. To do that without having to push and publish versions, you need to create a global symlink from `ui-react-components` and then use this symlink in your project.
52 |
53 | So first, create the global symlink by doing:
54 |
55 | ```sh
56 | cd path/to/ui-react-components
57 | npm link
58 | ```
59 |
60 | Then go to your project and:
61 |
62 | ```
63 | npm link @klarna/ui-react-components
64 | UV_THREADPOOL_SIZE=100 npm start
65 | ```
66 |
67 | This uses the global symlink of `ui-react-components` that points to our local git copy. Replace `npm start` with the command you use to start your app, if you use something different.
68 |
69 | The `UV_THREADPOOL_SIZE=100` solves a problem you may encounter with symlinks when importing Sass files [https://github.com/jtangelder/sass-loader/issues/100](https://github.com/jtangelder/sass-loader/issues/100).
70 |
71 | ### Running the tests in PhantomJS locally
72 |
73 | ```sh
74 | npm test
75 | ```
76 |
77 | ## Running the tests in different browsers
78 |
79 | ### Prerequisites
80 |
81 | First install the required npm packages.
82 | ```sh
83 | npm install karma-chrome-launcher
84 | npm install karma-firefox-launcher
85 | npm install karma-ie-launcher
86 | npm install karma-safari-launcher
87 | npm install karma-webdriver-launcher
88 | ```
89 |
90 | ### Run the tests on OS X
91 | ```sh
92 | BROWSER=PhantomJS,Chrome,Safari,Firefox npm test
93 | ```
94 |
95 | ### Run the tests on Windows
96 | ```sh
97 | BROWSER=PhantomJS,Chrome,Firefox npm test
98 | ```
99 |
100 | ## License
101 |
102 | Please check the [LICENSE](LICENSE) file.
103 |
104 | ## Contributing
105 |
106 | Make sure:
107 |
108 | 1. Your contribution is aligned with the styleguide.
109 | 2. Your contribution doesn't break the grid. To avoid that, always use the `$grid` variable to define your sizes, as in `line-height: ($grid * 4)`. As a rule of thumb, if your element total height (sum of content, paddings, margins, etc.) has an integer multiple of `$grid` you should be good.
110 | 3. Your code is linted: `npm run lint`.
111 | 4. It works in the major browsers, the simplest way is to spawn [ngrok](https://ngrok.com/) and use the cloud service of your choice. Else, you can download IE virtual machines for VirtualBox using `curl -s https://raw.githubusercontent.com/xdissent/ievms/master/ievms.sh | env IEVMS_VERSIONS="9" bash`.
112 |
113 | Then:
114 |
115 | 1. Send a PR to GitHub.
116 | 2. Once approved:
117 | 1. Update the version using `npm version` (tag will have `v` prefix) & update `CHANGELOG.md`.
118 | 2. Merge to master and push (with the new tag as well).
119 |
120 | Travis will take care of publishing your new version to npm.
121 |
--------------------------------------------------------------------------------
/components/Alert.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/alert.scss'
4 |
5 | export function Title ({ children, className, styles, ...remainingProps }) {
6 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
7 | const cls = classNames('cui__alert__title', className)
8 |
9 | return (
10 |
11 | {children}
12 |
13 | )
14 | }
15 |
16 | Title.propTypes = {
17 | children: PropTypes.node,
18 | className: PropTypes.string,
19 | design: PropTypes.oneOf(Alert.designs),
20 | styles: PropTypes.object
21 | }
22 |
23 | Title.defaultProps = {
24 | styles: {}
25 | }
26 |
27 | export function Paragraph ({ children, className, styles, ...remainingProps }) {
28 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
29 | const cls = classNames('cui__alert__paragraph', className)
30 |
31 | return (
32 |
33 | {children}
34 |
35 | )
36 | }
37 |
38 | Paragraph.propTypes = {
39 | children: PropTypes.node,
40 | className: PropTypes.string,
41 | design: PropTypes.oneOf(Alert.designs),
42 | styles: PropTypes.object
43 | }
44 |
45 | Paragraph.defaultProps = {
46 | styles: {}
47 | }
48 |
49 | export default function Alert ({ children, className, styles, design, ...remainingProps }) {
50 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
51 | const cls = classNames(`cui__alert--${design}`, className)
52 |
53 | return (
54 |
55 | {children}
56 |
57 | )
58 | }
59 |
60 | Alert.designs = ['error']
61 |
62 | Alert.defaultProps = {
63 | design: 'error',
64 | styles: {}
65 | }
66 |
67 | Alert.propTypes = {
68 | children: PropTypes.node,
69 | className: PropTypes.string,
70 | design: PropTypes.oneOf(Alert.designs),
71 | styles: PropTypes.object
72 | }
73 |
--------------------------------------------------------------------------------
/components/AnimatedMenu.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import { Motion, spring } from 'react-motion'
3 | import Menu from './Menu'
4 | import styles from '@klarna/ui-css-components/src/components/tab-menu.scss'
5 |
6 | const AnimatedSelectedBar = ({ width, left }) => (
7 |
8 | {(style) =>
}
9 |
10 | )
11 |
12 | export default class AnimatedMenu extends React.Component {
13 | constructor (props) {
14 | super(props)
15 |
16 | this.state = { width: 0, left: 0 }
17 | }
18 |
19 | update () {
20 | const tab = document.getElementById(`${this.props.name}-${this.props.selected}-tab`)
21 | const { left, width } = tab.getBoundingClientRect()
22 | const parentLeft = tab.parentNode.getBoundingClientRect().left
23 |
24 | this.setState({ width, left: (left - parentLeft) })
25 | }
26 |
27 | componentDidUpdate (prevProps) {
28 | if (this.props.options.length !== prevProps.options.length || this.props.selected !== prevProps.selected) {
29 | this.update()
30 | }
31 | }
32 |
33 | componentDidMount () {
34 | setTimeout(this.update.bind(this), 0)
35 | }
36 |
37 | render () {
38 | return (
39 |
40 | {this.state.width > 0 && }
41 |
42 | )
43 | }
44 | }
45 |
46 | AnimatedMenu.propTypes = {
47 | onChange: PropTypes.func.isRequired,
48 | selected: PropTypes.string.isRequired
49 | }
50 |
--------------------------------------------------------------------------------
/components/Block.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/block.scss'
4 |
5 | export default function Block ({className, blue, children, styles, ...remainingProps}) {
6 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
7 | const cls = classNames('cui__block', { blue }, className)
8 |
9 | return (
10 |
11 | {children}
12 |
13 | )
14 | }
15 |
16 | Block.propTypes = {
17 | blue: PropTypes.bool,
18 | children: PropTypes.node,
19 | className: PropTypes.string,
20 | styles: PropTypes.object
21 | }
22 |
--------------------------------------------------------------------------------
/components/Block/Installments.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/block.scss'
4 |
5 | const baseClass = 'cui__block--installments'
6 |
7 | const classes = {
8 | title: `${baseClass}__title`,
9 | value: `${baseClass}__value`,
10 | valueContent: `${baseClass}__value__content`,
11 | valueContentClarification: `${baseClass}__value__content__clarification`,
12 | valueTitle: `${baseClass}__value__title`,
13 | values: `${baseClass}__values`
14 | }
15 |
16 | export function Main ({ className, children, styles, ...props }) {
17 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
18 |
19 | return (
20 |
21 | {children}
22 |
23 | )
24 | }
25 |
26 | Main.displayName = 'BlockInstallments.Main'
27 |
28 | Main.propTypes = {
29 | className: PropTypes.string,
30 | children: PropTypes.node,
31 | styles: PropTypes.object
32 | }
33 |
34 | export function Title ({ className, children, styles, ...props }) {
35 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
36 |
37 | return (
38 |
39 | {children}
40 |
41 | )
42 | }
43 |
44 | Title.displayName = 'BlockInstallments.Title'
45 |
46 | Title.propTypes = {
47 | className: PropTypes.string,
48 | children: PropTypes.node,
49 | styles: PropTypes.object
50 | }
51 |
52 | export function Content ({ className, children, styles, ...props }) {
53 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
54 |
55 | return (
56 |
57 | {children}
58 |
59 | )
60 | }
61 |
62 | Content.displayName = 'BlockInstallments.Content'
63 |
64 | Content.propTypes = {
65 | className: PropTypes.string,
66 | children: PropTypes.node,
67 | styles: PropTypes.object
68 | }
69 |
70 | export function Value ({ className, clarification, children, title, styles, value, ...props }) {
71 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
72 |
73 | return (
74 |
75 |
76 | {title}
77 |
78 |
79 |
80 | {value}
81 | {clarification && (
82 |
83 | {clarification}
84 |
85 | )}
86 |
87 |
88 | )
89 | }
90 |
91 | Value.displayName = 'BlockInstallments.Value'
92 |
93 | Value.propTypes = {
94 | clarification: PropTypes.string,
95 | className: PropTypes.string,
96 | children: PropTypes.node,
97 | title: PropTypes.string.isRequired,
98 | value: PropTypes.string.isRequired,
99 | styles: PropTypes.object
100 | }
101 |
--------------------------------------------------------------------------------
/components/Button.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import Loader from './Loader'
3 | import classNamesBind from 'classnames/bind'
4 | import defaultStyles from '@klarna/ui-css-components/src/components/button.scss'
5 | import parseColor from 'parse-color'
6 |
7 | export default function Button (props) {
8 | const {
9 | children,
10 | className,
11 | customize,
12 | design,
13 | disabled,
14 | loading,
15 | size,
16 | styles,
17 | success,
18 | ...remainingProps } = props
19 |
20 | const content =
21 | success && '✔' || !loading && children
22 |
23 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
24 |
25 | const cls = classNames(`cui__button--${design}`, size, {
26 | 'is-disabled': disabled,
27 | 'is-loading': loading,
28 | 'dynamic-styling': customize
29 | }, className)
30 |
31 | const labelCls = {
32 | label: classNames('cui__button__label'),
33 | alt: classNames('cui__button__label--alt')
34 | }
35 |
36 | const isDisabled = (loading || success || disabled)
37 |
38 | const loaderColor = getLoaderColor(
39 | design,
40 | (customize || {}).textColor,
41 | (customize || {}).backgroundColor
42 | )
43 |
44 | const dynamicRenderer = design === 'primary'
45 | ? renderDynamicallyStyledPrimaryButton
46 | : renderDynamicallyStyledSecondaryButton
47 |
48 | return customize
49 | ? dynamicRenderer(content, cls, labelCls, isDisabled, loading, loaderColor, customize, {...remainingProps})
50 | : renderButton(content, cls, isDisabled, loading, loaderColor, {...remainingProps})
51 | }
52 |
53 | Button.defaultProps = {
54 | design: 'primary',
55 | loading: false,
56 | success: false,
57 | disabled: false
58 | }
59 |
60 | Button.designs = ['primary', 'secondary']
61 | Button.sizes = ['small', 'big']
62 |
63 | Button.propTypes = {
64 | children: PropTypes.node,
65 | className: PropTypes.string,
66 | customize: PropTypes.shape({
67 | textColor: PropTypes.string.isRequired,
68 | backgroundColor: PropTypes.string.isRequired,
69 | borderRadius: PropTypes.string.isRequired
70 | }),
71 | design: PropTypes.oneOf(Button.designs),
72 | size: PropTypes.oneOf(Button.sizes),
73 | loading: PropTypes.bool,
74 | success: PropTypes.bool,
75 | disabled: PropTypes.bool,
76 | styles: PropTypes.object
77 | }
78 |
79 | const renderButton = (content, classNames, disabled, loading, loaderColor, remainingProps) => (
80 |
81 | {loading ? : content}
82 |
83 | )
84 |
85 | const renderDynamicallyStyledPrimaryButton = (content, classNames, labelClassNames, disabled, loading, loaderColor, {textColor, backgroundColor, borderRadius}, remainingProps) => (
86 |
92 |
93 | {loading ? : content}
94 |
95 |
96 | )
97 |
98 | const renderDynamicallyStyledSecondaryButton = (content, classNames, labelClassNames, disabled, loading, loaderColor, {textColor, backgroundColor, borderRadius}, remainingProps) => (
99 |
105 |
106 | {loading ? : content}
107 | {
108 | disabled ||
109 |
113 |
114 | }
115 |
116 |
117 | )
118 |
119 | const getLoaderColor = (design, textColor, backgroundColor) => {
120 | if (textColor && backgroundColor) {
121 | const { rgb } = parseColor(design === 'primary' ? textColor : backgroundColor)
122 | return rgb
123 | }
124 |
125 | return design === 'primary' ? 'white' : 'blue'
126 | }
127 |
--------------------------------------------------------------------------------
/components/Checklist.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/checklist.scss'
4 |
5 | export default function Checklist ({ chromeless, className, children, customize, styles }) {
6 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
7 |
8 | const dynamicStyles = customize
9 | ? {
10 | borderRadius: customize.borderRadius,
11 | borderColor: customize.borderColor
12 | }
13 | : undefined
14 |
15 | return (
16 |
21 | )
22 | }
23 |
24 | Checklist.propTypes = {
25 | className: PropTypes.string,
26 | children: PropTypes.node,
27 | chromeless: PropTypes.bool,
28 | styles: PropTypes.object,
29 | customize: PropTypes.shape({
30 | borderColor: PropTypes.string.isRequired,
31 | borderRadius: PropTypes.string.isRequired
32 | })
33 | }
34 |
35 | Checklist.defaultProps = {
36 | styles: {}
37 | }
38 |
39 | Checklist.Item = ({ className, children, customize, styles }) => {
40 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
41 | const iconDynamicStyles = customize
42 | ? {
43 | stroke: customize.strokeColor
44 | }
45 | : undefined
46 |
47 | return (
48 |
50 |
55 |
56 |
57 | {children}
58 |
59 | )
60 | }
61 |
62 | Checklist.Item.displayName = 'Checklist.Item'
63 |
64 | Checklist.Item.propTypes = {
65 | className: PropTypes.string,
66 | children: PropTypes.node,
67 | styles: PropTypes.object,
68 | customize: PropTypes.shape({
69 | strokeColor: PropTypes.string.isRequired
70 | })
71 | }
72 |
73 | Checklist.Item.defaultProps = {
74 | styles: {}
75 | }
76 |
--------------------------------------------------------------------------------
/components/ContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/context-menu.scss'
4 |
5 | const baseClass = 'cui__context-menu'
6 |
7 | const ContextMenu = ({ className, children, styles, ...props }) => {
8 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
9 |
10 | return (
11 |
12 | {children}
13 |
14 | )
15 | }
16 |
17 | ContextMenu.Link = ({ className, children, styles, ...props }) => {
18 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
19 |
20 | return (
21 |
22 |
23 | {children}
24 |
25 |
26 | )
27 | }
28 | ContextMenu.Link.displayName = 'ContextMenu.Link'
29 |
30 | ContextMenu.Item = ({ className, children, styles, ...props }) => {
31 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
32 |
33 | return (
34 |
35 | {children}
36 |
37 | )
38 | }
39 | ContextMenu.Item.displayName = 'ContextMenu.Item'
40 |
41 | ContextMenu.propTypes = ContextMenu.Link.propTypes = ContextMenu.Item.propTypes = {
42 | className: PropTypes.string,
43 | children: PropTypes.node,
44 | styles: PropTypes.object
45 | }
46 |
47 | ContextMenu.Separator = ({ className, styles, ...props }) => {
48 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
49 |
50 | return (
51 |
52 | )
53 | }
54 | ContextMenu.Separator.displayName = 'ContextMenu.Separator'
55 | ContextMenu.Separator.propTypes = {
56 | className: PropTypes.string,
57 | styles: PropTypes.object
58 | }
59 |
60 | ContextMenu.Icon = ({ className, children, styles }) => {
61 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
62 |
63 | return (
64 | React.cloneElement(React.Children.only(children), {
65 | className: classNames(`${baseClass}__icon`, className)
66 | })
67 | )
68 | }
69 | ContextMenu.Icon.displayName = 'ContextMenu.Icon'
70 | ContextMenu.Icon.propTypes = {
71 | className: PropTypes.string,
72 | children: PropTypes.element,
73 | styles: PropTypes.object
74 | }
75 |
76 | export default ContextMenu
77 |
--------------------------------------------------------------------------------
/components/Dialog.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/dialog.scss'
4 |
5 | export default function Dialog ({ children, className, styles, ...props }) {
6 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
7 |
8 | return (
9 |
10 | {children}
11 |
12 | )
13 | }
14 |
15 | Dialog.propTypes = {
16 | children: PropTypes.node,
17 | className: PropTypes.string,
18 | styles: PropTypes.object
19 | }
20 |
21 | Dialog.Icon = ({ children, className, styles, ...props }) => {
22 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
23 |
24 | return (
25 |
26 | {children}
27 |
28 | )
29 | }
30 |
31 | Dialog.Icon.propTypes = {
32 | children: PropTypes.node,
33 | className: PropTypes.string,
34 | styles: PropTypes.object
35 | }
36 |
37 | Dialog.Icon.displayName = 'Dialog.Icon'
38 |
39 | Dialog.Content = ({ children, className, styles, ...props }) => {
40 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
41 |
42 | return (
43 |
44 |
45 | {children}
46 |
47 |
48 | )
49 | }
50 |
51 | Dialog.Content.displayName = 'Dialog.Content'
52 |
53 | Dialog.Content.propTypes = {
54 | children: PropTypes.node,
55 | className: PropTypes.string,
56 | styles: PropTypes.object
57 | }
58 |
59 | Dialog.Footer = ({ children, className, styles, ...props }) => {
60 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
61 |
62 | return (
63 |
65 |
67 | {children}
68 |
69 |
70 | )
71 | }
72 |
73 | Dialog.Footer.displayName = 'Dialog.Footer'
74 |
75 | Dialog.Footer.propTypes = {
76 | children: PropTypes.node,
77 | className: PropTypes.string,
78 | styles: PropTypes.object
79 | }
80 |
81 | Dialog.Overlay = ({ children, className, show, wide, styles, ...props }) => {
82 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
83 |
84 | return (
85 |
87 |
89 |
91 | {children}
92 |
93 |
94 |
95 | )
96 | }
97 |
98 | Dialog.Overlay.propTypes = {
99 | children: PropTypes.node,
100 | className: PropTypes.string,
101 | show: PropTypes.bool,
102 | styles: PropTypes.object
103 | }
104 |
--------------------------------------------------------------------------------
/components/Fieldset.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes} from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/field.scss'
4 |
5 | export default function Fieldset (props) {
6 | const {
7 | className,
8 | children,
9 | styles,
10 | ...remainingProps } = props
11 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
12 |
13 | const cls = classNames('cui__fieldset', className)
14 |
15 | return (
16 | {children}
17 | )
18 | }
19 |
20 | Fieldset.propTypes = {
21 | children: PropTypes.node,
22 | className: PropTypes.string,
23 | styles: PropTypes.object
24 | }
25 |
--------------------------------------------------------------------------------
/components/IconButton.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 |
5 | const iconPropTypes = {
6 | className: PropTypes.string,
7 | color: PropTypes.oneOf(['gray', 'inverse']),
8 | styles: PropTypes.object
9 | }
10 |
11 | export const BackButton = ({ className, color, styles, ...props }) => {
12 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
13 |
14 | return (
15 |
21 |
25 |
26 | )
27 | }
28 |
29 | export const CloseButton = ({ className, color, styles, ...props }) => {
30 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
31 |
32 | return (
33 |
39 |
41 |
43 |
44 | )
45 | }
46 |
47 | export const HamburgerButton = ({ className, color, styles, ...props }) => {
48 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
49 |
50 | return (
51 |
57 | {[8, 13, 18].map((y) =>
58 |
62 | )}
63 |
64 | )
65 | }
66 |
67 | export const OptionsButton = ({ className, color, styles, ...props }) => {
68 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
69 |
70 | return (
71 |
75 | {[7, 13, 19].map((y) =>
76 |
80 | )}
81 |
82 | )
83 | }
84 |
85 | export const SearchButton = ({ className, color, styles, ...props }) => {
86 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
87 |
88 | return (
89 |
95 |
98 |
101 |
102 | )
103 | }
104 |
105 | BackButton.propTypes = CloseButton.propTypes = HamburgerButton.propTypes = OptionsButton.propTypes = SearchButton.propTypes = iconPropTypes
106 |
--------------------------------------------------------------------------------
/components/Input.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes, Component } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/input.scss'
4 | import * as programmaticFocus from '../lib/features/programmaticFocus'
5 | import * as fieldStates from '../lib/features/fieldStates'
6 | import * as inlinedIcon from '../lib/features/inlinedIcon'
7 | import { position, size } from '../lib/features/stacking'
8 | import { handleKeyDown } from '../lib/features/keyboardEvents'
9 |
10 | export default class Input extends Component {
11 | componentDidMount () {
12 | programmaticFocus.maybeFocus(document)(this.props.focus, this.refs.input)
13 | }
14 |
15 | componentDidUpdate () {
16 | programmaticFocus.maybeFocus(document)(this.props.focus, this.refs.input)
17 | }
18 |
19 | render () {
20 | const {
21 | big,
22 | className,
23 | centered,
24 | disabled,
25 | giant,
26 | icon,
27 | label,
28 | loading,
29 | onBlur,
30 | onChange,
31 | onClick,
32 | onFocus,
33 | square,
34 | value,
35 | styles,
36 | ...props
37 | } = this.props
38 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
39 |
40 | const classes = {
41 | field: classNames(
42 | (icon ? 'cui__input--icon' : 'cui__input'), {
43 | big,
44 | giant,
45 | 'is-centered': centered,
46 | 'is-filled': value != null && value !== '',
47 | 'is-loading': loading,
48 | square
49 | },
50 | fieldStates.getClassName(this.props),
51 | programmaticFocus.getClassName(this.props),
52 | size.getClassName(this.props),
53 | position.getClassName(this.props),
54 | className),
55 | label: classNames(
56 | icon
57 | ? 'cui__input--icon__label'
58 | : 'cui__input__label'
59 | ),
60 | input: classNames(
61 | icon
62 | ? 'cui__input--icon__input'
63 | : 'cui__input__input'
64 | )
65 | }
66 |
67 | return (
68 |
72 | {
73 | inlinedIcon.renderInlinedIcon(this.props, {
74 | icon: classNames('cui__input--icon__icon'),
75 | fill: classNames('cui__input--icon__icon__fill'),
76 | stroke: classNames('cui__input--icon__icon__stroke')
77 | })
78 | }
79 |
80 | {label}
81 |
82 |
93 |
94 | )
95 | }
96 | }
97 |
98 | Input.defaultProps = {
99 | big: false,
100 | centered: false,
101 | giant: false,
102 | loading: false,
103 | onChange: function () {},
104 | ...inlinedIcon.defaultProps,
105 | ...fieldStates.defaultProps,
106 | ...position.defaultProps,
107 | ...handleKeyDown.defaultProps,
108 | ...size.defaultProps
109 | }
110 |
111 | Input.propTypes = {
112 | big: PropTypes.bool,
113 | centered: PropTypes.bool,
114 | giant: PropTypes.bool,
115 | loading: PropTypes.bool,
116 | label: PropTypes.string.isRequired,
117 | onBlur: PropTypes.func,
118 | onChange: PropTypes.func,
119 | onClick: PropTypes.func,
120 | onFocus: PropTypes.func,
121 | value: PropTypes.string,
122 | styles: PropTypes.object,
123 | ...inlinedIcon.propTypes,
124 | ...fieldStates.propTypes,
125 | ...handleKeyDown.propTypes,
126 | ...position.propTypes,
127 | ...programmaticFocus.propTypes,
128 | ...size.propTypes
129 | }
130 |
--------------------------------------------------------------------------------
/components/Label.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/label.scss'
4 |
5 | export default function Label ({
6 | children,
7 | className,
8 | design,
9 | outline,
10 | inverted,
11 | styles,
12 | ...props
13 | }) {
14 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
15 | const cls = classNames('cui__label', design, className, {
16 | outline,
17 | inverted
18 | })
19 |
20 | return (
21 |
22 | {children}
23 |
24 | )
25 | }
26 |
27 | Label.designs = [
28 | 'information',
29 | 'warning',
30 | 'notice',
31 | 'success',
32 | 'brown',
33 | 'purple',
34 | 'light-blue',
35 | 'ultramarine',
36 | 'yellow',
37 | 'grey',
38 | 'black'
39 | ]
40 |
41 | Label.propTypes = {
42 | children: PropTypes.node,
43 | className: PropTypes.string,
44 | design: PropTypes.oneOf(Label.designs),
45 | inverted: PropTypes.bool,
46 | outline: PropTypes.bool,
47 | styles: PropTypes.object
48 | }
49 |
--------------------------------------------------------------------------------
/components/Link.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/link.scss'
4 | import palette from './texts/palette'
5 |
6 | export default function Link ({className, color, children, styles, customize, ...props}) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 | const cls = classNames('cui__link', color, className, {
9 | 'dynamic-styling': customize
10 | })
11 |
12 | const customizedStyles = customize
13 | ? {
14 | color: customize.textColor
15 | }
16 | : undefined
17 |
18 | return (
19 |
20 | {children}
21 |
22 | )
23 | }
24 |
25 | Link.propTypes = {
26 | children: PropTypes.node,
27 | className: PropTypes.string,
28 | color: PropTypes.oneOf(palette),
29 | customize: PropTypes.shape({
30 | textColor: PropTypes.string.isRequired
31 | }),
32 | styles: PropTypes.object
33 | }
34 |
--------------------------------------------------------------------------------
/components/Loader/index.jsx:
--------------------------------------------------------------------------------
1 | // Based on http://codepen.io/skuester/pen/Hejbz
2 |
3 | import React, { PropTypes } from 'react'
4 | import classNamesBind from 'classnames/bind'
5 | import defaultStyles from './styles.scss'
6 |
7 | const colors = {
8 | blue: [0, 116, 200],
9 | white: [255, 255, 255]
10 | }
11 | colors.default = [158, 158, 160]
12 |
13 | const sizes = {
14 | big: 30,
15 | small: 15,
16 | tiny: 10
17 | }
18 | sizes.default = 20
19 |
20 | const gradients = [
21 | {x1: 0, y1: '0', x2: '0', y2: '1'},
22 | {x1: '1', y1: '0', x2: '0', y2: '1'},
23 | {x1: '1', y1: '1', x2: '0', y2: '0'},
24 | {x1: '0', y1: '1', x2: '0', y2: '0'},
25 | {x1: '0', y1: '1', x2: '1', y2: '0'},
26 | {x1: '0', y1: '0', x2: '1', y2: '1'}
27 | ]
28 |
29 | export default function Loader ({ className, color, inline, size, styles }) {
30 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
31 |
32 | const _color = Array.isArray(color) ? color : colors[color] || colors.default
33 | const _size = sizes[size] || sizes.default
34 | const step = 0.2
35 | const stroke = 2
36 | const half = _size / 2
37 | const quarter = _size / 4
38 | const corner = _size * 0.433
39 |
40 | return (
41 |
42 |
43 | {
44 | gradients.map((props, index) => (
45 |
46 |
47 |
48 |
49 | ))
50 | }
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | )
62 | }
63 |
64 | Loader.propTypes = {
65 | className: PropTypes.string,
66 | color: PropTypes.oneOfType([
67 | PropTypes.oneOf(Object.keys(colors)),
68 | PropTypes.array
69 | ]),
70 | inline: PropTypes.bool,
71 | size: PropTypes.oneOf(Object.keys(sizes)),
72 | styles: PropTypes.object
73 | }
74 |
--------------------------------------------------------------------------------
/components/Loader/styles.scss:
--------------------------------------------------------------------------------
1 | @keyframes spin {
2 | 0% { transform: rotate(0deg); }
3 | 100% { transform: rotate(360deg); }
4 | }
5 |
6 | .loader {
7 | animation: spin .8s linear infinite;
8 | display: block;
9 | margin: 0;
10 | }
11 |
12 | .inline {
13 | margin: 0 auto;
14 | }
15 |
--------------------------------------------------------------------------------
/components/Menu.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import segmentedStyles from '@klarna/ui-css-components/src/components/segmentedcontrol.scss'
4 | import tabStyles from '@klarna/ui-css-components/src/components/tab-menu.scss'
5 |
6 | const designs = {
7 | tab: {
8 | baseClass: 'cui__tab-menu',
9 | styles: tabStyles
10 | },
11 | segmented: {
12 | baseClass: 'cui__segmentedcontrol',
13 | styles: segmentedStyles
14 | }
15 | }
16 |
17 | export default function Menu (props) {
18 | const {
19 | className,
20 | design,
21 | tabDisplay,
22 | options,
23 | name,
24 | onChange,
25 | onClick,
26 | selected,
27 | selectable,
28 | white,
29 | children,
30 | ...remainingProps } = props
31 |
32 | const { baseClass, styles } = designs[design]
33 | const classNames = classNamesBind.bind(styles)
34 |
35 | const cls = classNames(baseClass, tabDisplay, className, {
36 | 'is-selectable': selectable,
37 | white
38 | })
39 |
40 | const tabStyle = tabDisplay === 'static'
41 | ? {width: (100 / options.length) + '%'}
42 | : {}
43 |
44 | const items = options.map(({ key, label }, index) => {
45 | const id = `${name}-${key}`
46 |
47 | const tabClass = classNames(`${baseClass}__button`, {
48 | left: index === 0,
49 | center: index > 0 && index < options.length - 1,
50 | right: index === options.length - 1
51 | })
52 |
53 | return (
54 |
55 | onChange(key))}
61 | defaultChecked={key === selected} />
62 | onClick(event))}
67 | htmlFor={id}>
68 | {label}
69 |
70 |
71 | )
72 | })
73 |
74 | return (
75 |
76 | {children}
77 | {items}
78 |
79 | )
80 | }
81 |
82 | Menu.defaultProps = {
83 | design: 'tab',
84 | tabDisplay: 'fluid',
85 | selectable: true
86 | }
87 |
88 | Menu.designs = ['tab', 'segmented']
89 | Menu.tabDisplays = ['fluid', 'static']
90 |
91 | Menu.optionsSchema = PropTypes.shape({
92 | label: PropTypes.node.isRequired,
93 | key: PropTypes.string.isRequired
94 | })
95 |
96 | Menu.propTypes = {
97 | options: PropTypes.arrayOf(Menu.optionsSchema).isRequired,
98 | className: PropTypes.string,
99 | design: PropTypes.oneOf(Menu.designs),
100 | tabDisplay: PropTypes.oneOf(Menu.tabDisplays),
101 | onChange: PropTypes.func,
102 | onClick: PropTypes.func,
103 | name: PropTypes.string.isRequired,
104 | selected: PropTypes.string,
105 | selectable: PropTypes.bool,
106 | white: PropTypes.bool,
107 | children: PropTypes.node
108 | }
109 |
--------------------------------------------------------------------------------
/components/PayButton.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import defaultStyles from '@klarna/ui-css-components/src/components/button.scss'
3 | import Button from './Button'
4 | import classNamesBind from 'classnames/bind'
5 |
6 | export default function PayButton ({
7 | price,
8 | children,
9 | className,
10 | loading,
11 | styles,
12 | ...remainingProps
13 | }) {
14 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
15 | const cls = classNames(loading || 'has-price', className)
16 |
17 | return (
18 |
19 | {children}
20 |
21 | {price}
22 |
23 |
24 | )
25 | }
26 |
27 | PayButton.defaultProps = Button.defaultProps
28 |
29 | PayButton.propTypes = {
30 | ...Button.propTypes,
31 | price: PropTypes.string.isRequired
32 | }
33 |
--------------------------------------------------------------------------------
/components/Preview.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/preview.scss'
4 |
5 | export default function Preview ({ className, children, styles }) {
6 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
7 | const cls = classNames('cui__preview', className)
8 |
9 | return (
10 |
11 |
12 | {children}
13 |
14 |
15 | )
16 | }
17 |
18 | Preview.propTypes = {
19 | className: PropTypes.string,
20 | children: PropTypes.node,
21 | styles: PropTypes.object
22 | }
23 |
24 | export function PreviewTitle ({ children, className, styles }) {
25 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
26 | const cls = classNames('cui__preview__title', className)
27 |
28 | return (
29 |
30 | {children}
31 |
32 | )
33 | }
34 |
35 | PreviewTitle.propTypes = {
36 | className: PropTypes.string,
37 | children: PropTypes.node,
38 | styles: PropTypes.object
39 | }
40 |
41 | export function PreviewLink ({ children, className, styles, ...remainingProps }) {
42 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
43 | const cls = classNames('cui__preview__footer__link', className)
44 |
45 | return (
46 |
51 | )
52 | }
53 |
54 | PreviewLink.propTypes = {
55 | className: PropTypes.string,
56 | children: PropTypes.node,
57 | styles: PropTypes.object
58 | }
59 |
--------------------------------------------------------------------------------
/components/RadioGroup.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/dropdown.scss'
4 |
5 | export default function RadioGroup (props) {
6 | const { selected, onChange, className, data, styles, ...remainingProps } = props
7 | const baseClass = 'cui__dropdown--radio'
8 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
9 | const cls = classNames(baseClass, className)
10 |
11 | const options = data.map(({id, label, description}) => {
12 | const optionClass = classNames(`${baseClass}__option`, {
13 | 'is-selected': id === selected
14 | })
15 | const labelClass = classNames(`${baseClass}__option__heading`)
16 | const descriptionClass = classNames(`${baseClass}__option__description`)
17 |
18 | return (
19 | onChange(id)}>
20 |
{label}
21 | {description &&
{description}
}
22 |
23 | )
24 | })
25 |
26 | return (
27 |
28 | {options}
29 |
30 | )
31 | }
32 |
33 | RadioGroup.optionSchema = PropTypes.shape({
34 | id: PropTypes.any.isRequired,
35 | label: PropTypes.string.isRequired,
36 | description: PropTypes.node
37 | })
38 |
39 | RadioGroup.propTypes = {
40 | // Allows any type to be an id, as long as it is comparable
41 | selected: React.PropTypes.any.isRequired,
42 | onChange: React.PropTypes.func.isRequired,
43 | className: PropTypes.string,
44 | data: PropTypes.arrayOf(RadioGroup.optionSchema).isRequired,
45 | styles: PropTypes.object
46 | }
47 |
--------------------------------------------------------------------------------
/components/Selector.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/selector.scss'
4 | import Checkmark from './icons/Checkmark'
5 |
6 | export default function Selector ({
7 | selected,
8 | onChange,
9 | className,
10 | data,
11 | styles,
12 | ...remainingProps
13 | }) {
14 | const baseClass = 'cui__selector--direct'
15 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
16 | const cls = classNames(baseClass, 'title', className)
17 |
18 | const options = data.map((d) => {
19 | const optionClass = classNames(`${baseClass}__item`)
20 | const labelClass = classNames(`${baseClass}__label`)
21 | const iconClass = classNames(`${baseClass}__icon`)
22 |
23 | return (
24 | onChange(d)}>
25 |
{d.label}
26 | {d.id === selected ?
: null}
27 |
28 | )
29 | })
30 |
31 | return (
32 |
33 | {options}
34 |
35 | )
36 | }
37 |
38 | Selector.optionSchema = PropTypes.shape({
39 | id: PropTypes.any.isRequired,
40 | label: PropTypes.string.isRequired
41 | })
42 |
43 | Selector.propTypes = {
44 | // Allows any type to be an id, as long as it is comparable
45 | selected: React.PropTypes.any.isRequired,
46 | onChange: React.PropTypes.func.isRequired,
47 | className: PropTypes.string,
48 | data: PropTypes.arrayOf(Selector.optionSchema).isRequired,
49 | styles: PropTypes.object
50 | }
51 |
--------------------------------------------------------------------------------
/components/Text.jsx:
--------------------------------------------------------------------------------
1 | export { default as Amount } from './texts/Amount'
2 | export { default as Paragraph } from './texts/Paragraph'
3 | export { default as PrimaryTitle } from './texts/PrimaryTitle'
4 | export { default as SecondaryTitle } from './texts/SecondaryTitle'
5 | export { default as Subtitle } from './texts/Subtitle'
6 | export { default as TextLabel } from './texts/TextLabel'
7 |
--------------------------------------------------------------------------------
/components/Theme.jsx:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react'
2 | import { getContextualizer } from 'react-context-props'
3 |
4 | const Theme = getContextualizer({
5 | customizations: PropTypes.shape({
6 | color_button: PropTypes.string,
7 | color_button_text: PropTypes.string,
8 | color_checkbox: PropTypes.string,
9 | color_checkbox_checkmark: PropTypes.string,
10 | color_text: PropTypes.string,
11 | color_header: PropTypes.string,
12 | color_link: PropTypes.string,
13 | color_border: PropTypes.string,
14 | color_border_selected: PropTypes.string,
15 | color_details: PropTypes.string,
16 | color_text_secondary: PropTypes.string,
17 | radius_border: PropTypes.string
18 | })
19 | })
20 |
21 | Theme.displayName = 'Theme'
22 |
23 | export default Theme
24 |
--------------------------------------------------------------------------------
/components/Tooltip.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/tooltip.scss'
4 |
5 | export default function Tooltip ({ className, arrow, children, border, styles }) {
6 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
7 | const cls = classNames('cui__tooltip', arrow, className, { border })
8 |
9 | return (
10 |
11 | {children}
12 |
13 | )
14 | }
15 |
16 | Tooltip.arrows = [
17 | 'top', 'top-left', 'top-right',
18 | 'bottom', 'bottom-left', 'bottom-right',
19 | 'left', 'left-top', 'left-bottom',
20 | 'right', 'right-top', 'right-bottom'
21 | ]
22 |
23 | Tooltip.propTypes = {
24 | className: PropTypes.string,
25 | arrow: PropTypes.oneOf(Tooltip.arrows),
26 | children: PropTypes.node,
27 | border: PropTypes.bool,
28 | styles: PropTypes.object
29 | }
30 |
--------------------------------------------------------------------------------
/components/icons/AccountActivated.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function AccountActivated ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
15 |
18 |
19 |
20 |
22 |
24 |
26 |
28 |
29 |
30 | )
31 | }
32 |
33 | AccountActivated.defaultProps = {
34 | color: 'blue',
35 | styles: {}
36 | }
37 |
38 | AccountActivated.propTypes = {
39 | color: PropTypes.oneOf(colors),
40 | styles: PropTypes.object
41 | }
42 |
--------------------------------------------------------------------------------
/components/icons/AllSet.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function AllSet ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
17 |
19 |
21 |
22 |
23 | )
24 | }
25 |
26 | AllSet.defaultProps = {
27 | color: 'blue',
28 | styles: {}
29 | }
30 |
31 | AllSet.propTypes = {
32 | color: PropTypes.oneOf(colors),
33 | styles: PropTypes.object
34 | }
35 |
--------------------------------------------------------------------------------
/components/icons/Arrow.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Arrow ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
18 |
19 | )
20 | }
21 |
22 | Arrow.defaultProps = {
23 | color: 'blue',
24 | styles: {}
25 | }
26 |
27 | Arrow.propTypes = {
28 | color: PropTypes.oneOf(colors),
29 | styles: PropTypes.object
30 | }
31 |
--------------------------------------------------------------------------------
/components/icons/Cancel.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Cancel ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
20 |
23 |
24 |
25 |
26 | )
27 | }
28 |
29 | Cancel.defaultProps = {
30 | color: 'blue',
31 | styles: {}
32 | }
33 |
34 | Cancel.propTypes = {
35 | color: PropTypes.oneOf(colors),
36 | styles: PropTypes.object
37 | }
38 |
--------------------------------------------------------------------------------
/components/icons/Checkmark.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Checkmark ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
16 |
19 |
20 | )
21 | }
22 |
23 | Checkmark.defaultProps = {
24 | color: 'blue',
25 | styles: {}
26 | }
27 |
28 | Checkmark.propTypes = {
29 | color: PropTypes.oneOf(colors),
30 | styles: PropTypes.object
31 | }
32 |
--------------------------------------------------------------------------------
/components/icons/CreditCard.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import Amex from './cards/Amex'
3 | import Discover from './cards/Discover'
4 | import Maestro from './cards/Maestro'
5 | import Mastercard from './cards/Mastercard'
6 | import Visa from './cards/Visa'
7 | import VisaElectron from './cards/VisaElectron'
8 |
9 | const brands = {
10 | amex: Amex,
11 | discover: Discover,
12 | maestro: Maestro,
13 | master: Mastercard,
14 | visa: Visa,
15 | visaelectron: VisaElectron
16 | }
17 |
18 | const CreditCard = ({ brand, ...props }) => {
19 | const Brand = brands[brand]
20 | if (!Brand) {
21 | return {brand}
22 | }
23 |
24 | return
25 | }
26 |
27 | CreditCard.brands = Object.keys(brands)
28 |
29 | CreditCard.propTypes = {
30 | brand: PropTypes.oneOf(CreditCard.brands)
31 | }
32 |
33 | export default CreditCard
34 |
--------------------------------------------------------------------------------
/components/icons/Details.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Details ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
17 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
30 | )
31 | }
32 |
33 | Details.defaultProps = {
34 | color: 'blue',
35 | styles: {}
36 | }
37 |
38 | Details.propTypes = {
39 | color: PropTypes.oneOf(colors),
40 | styles: PropTypes.object
41 | }
42 |
--------------------------------------------------------------------------------
/components/icons/Done.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 | import Circle from './parts/Circle.jsx'
6 |
7 | export default function Done ({ color, styles, className, ...props }) {
8 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
9 |
10 | return (
11 |
17 |
18 |
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | Done.defaultProps = {
26 | color: 'blue',
27 | styles: {}
28 | }
29 |
30 | Done.propTypes = {
31 | color: PropTypes.oneOf(colors),
32 | styles: PropTypes.object
33 | }
34 |
--------------------------------------------------------------------------------
/components/icons/Download.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Download ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
17 |
19 |
21 |
22 |
23 | )
24 | }
25 |
26 | Download.defaultProps = {
27 | color: 'blue',
28 | styles: {}
29 | }
30 |
31 | Download.propTypes = {
32 | color: PropTypes.oneOf(colors),
33 | styles: PropTypes.object
34 | }
35 |
--------------------------------------------------------------------------------
/components/icons/Error.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 | import Circle from './parts/Circle.jsx'
6 |
7 | export default function Error ({ color, styles, className, ...props }) {
8 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
9 |
10 | return (
11 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | Error.defaultProps = {
26 | color: 'blue',
27 | styles: {}
28 | }
29 |
30 | Error.propTypes = {
31 | color: PropTypes.oneOf(colors),
32 | styles: PropTypes.object
33 | }
34 |
--------------------------------------------------------------------------------
/components/icons/ExtendDate.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function ExtendDate ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
17 |
19 |
21 |
23 |
25 |
27 |
29 |
30 |
31 | )
32 | }
33 |
34 | ExtendDate.defaultProps = {
35 | color: 'blue',
36 | styles: {}
37 | }
38 |
39 | ExtendDate.propTypes = {
40 | color: PropTypes.oneOf(colors),
41 | styles: PropTypes.object
42 | }
43 |
--------------------------------------------------------------------------------
/components/icons/Items.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Items ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
15 | {[6.5, 11.5, 16.5].map((y) => (
16 |
19 |
20 |
21 |
22 | ))}
23 |
24 | )
25 | }
26 |
27 | Items.defaultProps = {
28 | color: 'blue',
29 | styles: {}
30 | }
31 |
32 | Items.propTypes = {
33 | color: PropTypes.oneOf(colors),
34 | styles: PropTypes.object
35 | }
36 |
--------------------------------------------------------------------------------
/components/icons/KlarnaLogo.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import Icon, { icon } from './Icon.jsx'
3 |
4 | const KlarnaLogo = (hexColor, {width, ...otherProps}) => {
5 | const height = width / KlarnaLogo.ratio
6 |
7 | return (
8 |
14 |
15 |
16 | )
17 | }
18 |
19 | KlarnaLogo.ratio = 101 / 28
20 | KlarnaLogo.propTypes = {
21 | width: PropTypes.number
22 | }
23 |
24 | KlarnaLogo.defaultProps = {
25 | width: 63,
26 | color: 'blue'
27 | }
28 |
29 | export default icon(KlarnaLogo)
30 |
--------------------------------------------------------------------------------
/components/icons/Letter.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Letter ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | Letter.defaultProps = {
26 | color: 'blue',
27 | styles: {}
28 | }
29 |
30 | Letter.propTypes = {
31 | color: PropTypes.oneOf(colors),
32 | styles: PropTypes.object
33 | }
34 |
--------------------------------------------------------------------------------
/components/icons/Logout.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Logout ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
17 |
19 |
21 |
22 |
23 | )
24 | }
25 |
26 | Logout.defaultProps = {
27 | color: 'blue',
28 | styles: {}
29 | }
30 |
31 | Logout.propTypes = {
32 | color: PropTypes.oneOf(colors),
33 | styles: PropTypes.object
34 | }
35 |
--------------------------------------------------------------------------------
/components/icons/Mail.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Mail ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | )
22 | }
23 |
24 | Mail.defaultProps = {
25 | color: 'blue',
26 | styles: {}
27 | }
28 |
29 | Mail.propTypes = {
30 | color: PropTypes.oneOf(colors),
31 | styles: PropTypes.object
32 | }
33 |
--------------------------------------------------------------------------------
/components/icons/NotFound.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 | import File from './parts/File.jsx'
6 |
7 | export default function NotFound ({ color, styles, className, ...props }) {
8 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
9 |
10 | return (
11 |
15 |
18 |
19 |
21 |
22 |
23 |
25 |
26 |
27 |
28 |
29 | )
30 | }
31 |
32 | NotFound.defaultProps = {
33 | color: 'blue',
34 | styles: {}
35 | }
36 |
37 | NotFound.propTypes = {
38 | color: PropTypes.oneOf(colors),
39 | styles: PropTypes.object
40 | }
41 |
--------------------------------------------------------------------------------
/components/icons/OpenLetter.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function OpenLetter ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
17 |
19 |
21 |
22 |
23 | )
24 | }
25 |
26 | OpenLetter.defaultProps = {
27 | color: 'blue',
28 | styles: {}
29 | }
30 |
31 | OpenLetter.propTypes = {
32 | color: PropTypes.oneOf(colors),
33 | styles: PropTypes.object
34 | }
35 |
--------------------------------------------------------------------------------
/components/icons/PadLock.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function PadLock ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
17 |
18 |
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | PadLock.defaultProps = {
26 | color: 'blue',
27 | styles: {}
28 | }
29 |
30 | PadLock.propTypes = {
31 | color: PropTypes.oneOf(colors),
32 | styles: PropTypes.object
33 | }
34 |
--------------------------------------------------------------------------------
/components/icons/Password.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Password ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | )
22 | }
23 |
24 | Password.defaultProps = {
25 | color: 'blue',
26 | styles: {}
27 | }
28 |
29 | Password.propTypes = {
30 | color: PropTypes.oneOf(colors),
31 | styles: PropTypes.object
32 | }
33 |
--------------------------------------------------------------------------------
/components/icons/Person.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Person ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
20 |
23 |
25 |
26 |
27 | )
28 | }
29 |
30 | Person.defaultProps = {
31 | color: 'blue',
32 | styles: {}
33 | }
34 |
35 | Person.propTypes = {
36 | color: PropTypes.oneOf(colors),
37 | styles: PropTypes.object
38 | }
39 |
--------------------------------------------------------------------------------
/components/icons/Phone.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Phone ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
16 |
18 |
19 |
20 | )
21 | }
22 |
23 | Phone.defaultProps = {
24 | color: 'blue',
25 | styles: {}
26 | }
27 |
28 | Phone.propTypes = {
29 | color: PropTypes.oneOf(colors),
30 | styles: PropTypes.object
31 | }
32 |
--------------------------------------------------------------------------------
/components/icons/Question.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Question ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
20 |
26 |
29 |
31 |
32 |
33 | )
34 | }
35 |
36 | Question.defaultProps = {
37 | color: 'blue',
38 | styles: {}
39 | }
40 |
41 | Question.propTypes = {
42 | color: PropTypes.oneOf(colors),
43 | styles: PropTypes.object
44 | }
45 |
--------------------------------------------------------------------------------
/components/icons/Remind.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Remind ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
17 |
19 |
21 |
23 |
25 |
26 |
27 | )
28 | }
29 |
30 | Remind.defaultProps = {
31 | color: 'blue',
32 | styles: {}
33 | }
34 |
35 | Remind.propTypes = {
36 | color: PropTypes.oneOf(colors),
37 | styles: PropTypes.object
38 | }
39 |
--------------------------------------------------------------------------------
/components/icons/SMS.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function SMS ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
17 |
19 |
21 |
23 |
25 |
27 |
28 |
29 | )
30 | }
31 |
32 | SMS.defaultProps = {
33 | color: 'blue',
34 | styles: {}
35 | }
36 |
37 | SMS.propTypes = {
38 | color: PropTypes.oneOf(colors),
39 | styles: PropTypes.object
40 | }
41 |
--------------------------------------------------------------------------------
/components/icons/Time.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 | import Circle from './parts/Circle.jsx'
6 |
7 | export default function Time ({ color, styles, className, ...props }) {
8 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
9 |
10 | return (
11 |
17 |
18 |
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | Time.defaultProps = {
26 | color: 'blue',
27 | styles: {}
28 | }
29 |
30 | Time.propTypes = {
31 | color: PropTypes.oneOf(colors),
32 | styles: PropTypes.object
33 | }
34 |
--------------------------------------------------------------------------------
/components/icons/Warning.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 |
6 | export default function Warning ({ color, styles, className, ...props }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 |
9 | return (
10 |
14 |
17 |
19 |
21 |
23 |
24 |
25 | )
26 | }
27 |
28 | Warning.defaultProps = {
29 | color: 'blue',
30 | styles: {}
31 | }
32 |
33 | Warning.propTypes = {
34 | color: PropTypes.oneOf(colors),
35 | styles: PropTypes.object
36 | }
37 |
--------------------------------------------------------------------------------
/components/icons/Wrong.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/illustration.scss'
4 | import colors from './constants/colors'
5 | import File from './parts/File.jsx'
6 |
7 | export default function Wrong ({ color, styles, className, ...props }) {
8 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
9 |
10 | return (
11 |
15 |
18 |
19 |
21 |
23 |
25 |
26 |
27 | )
28 | }
29 |
30 | Wrong.defaultProps = {
31 | color: 'blue',
32 | styles: {}
33 | }
34 |
35 | Wrong.propTypes = {
36 | color: PropTypes.oneOf(colors),
37 | styles: PropTypes.object
38 | }
39 |
--------------------------------------------------------------------------------
/components/icons/constants/colors.es6:
--------------------------------------------------------------------------------
1 | export default [
2 | 'blue',
3 | 'black',
4 | 'gray',
5 | 'success',
6 | 'error',
7 | 'warning',
8 | 'inverse'
9 | ]
10 |
--------------------------------------------------------------------------------
/components/icons/parts/Circle.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default function Circle (props) {
4 | return (
5 |
6 | )
7 | }
8 |
--------------------------------------------------------------------------------
/components/icons/parts/File.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default function File (props) {
4 | return (
5 |
8 | )
9 | }
10 |
--------------------------------------------------------------------------------
/components/texts/Amount.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/text.scss'
4 | import palette from './palette'
5 |
6 | export default function Amount ({ children, className, color, styles, ...remainingProps }) {
7 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
8 | const cls = classNames('cui__amount-text', color, className)
9 |
10 | return (
11 |
12 | {children}
13 |
14 | )
15 | }
16 |
17 | Amount.defaultProps = {
18 | color: 'black',
19 | styles: {}
20 | }
21 |
22 | Amount.propTypes = {
23 | color: PropTypes.oneOf(palette),
24 | children: PropTypes.node,
25 | className: PropTypes.string,
26 | styles: PropTypes.object
27 | }
28 |
--------------------------------------------------------------------------------
/components/texts/Paragraph.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/text.scss'
4 | import palette from './palette'
5 |
6 | export default function Paragraph ({
7 | children,
8 | className,
9 | color,
10 | condensed,
11 | margins,
12 | design,
13 | styles,
14 | ...props
15 | }) {
16 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
17 | const cls = classNames(
18 | `cui__paragraph--${design}`,
19 | color,
20 | className,
21 | {
22 | condensed,
23 | 'default-margins': margins
24 | }
25 | )
26 |
27 | return (
28 |
29 | {children}
30 |
31 | )
32 | }
33 |
34 | Paragraph.designs = ['primary', 'secondary', 'legal']
35 |
36 | Paragraph.defaultProps = {
37 | color: undefined,
38 | condensed: false,
39 | margins: false,
40 | design: 'primary',
41 | styles: {}
42 | }
43 |
44 | Paragraph.propTypes = {
45 | children: PropTypes.node,
46 | className: PropTypes.string,
47 | color: PropTypes.oneOf(palette),
48 | condensed: PropTypes.bool,
49 | margins: PropTypes.bool,
50 | design: PropTypes.oneOf(Paragraph.designs),
51 | styles: PropTypes.object
52 | }
53 |
--------------------------------------------------------------------------------
/components/texts/PrimaryTitle.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/text.scss'
4 | import palette from './palette'
5 |
6 | export default function PrimaryTitle ({
7 | children,
8 | className,
9 | color,
10 | margins,
11 | small,
12 | strong,
13 | styles,
14 | ...props
15 | }) {
16 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
17 |
18 | const cls = classNames(
19 | 'cui__title--primary',
20 | color,
21 | className,
22 | {
23 | 'default-margins': margins,
24 | small,
25 | strong
26 | }
27 | )
28 |
29 | return (
30 |
31 | {children}
32 |
33 | )
34 | }
35 |
36 | PrimaryTitle.defaultProps = {
37 | color: 'black',
38 | small: false,
39 | strong: false,
40 | margins: false,
41 | styles: {}
42 | }
43 |
44 | PrimaryTitle.propTypes = {
45 | children: PropTypes.node,
46 | className: PropTypes.string,
47 | color: PropTypes.oneOf(palette),
48 | margins: PropTypes.bool,
49 | small: PropTypes.bool,
50 | strong: PropTypes.bool,
51 | styles: PropTypes.object
52 | }
53 |
--------------------------------------------------------------------------------
/components/texts/SecondaryTitle.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/text.scss'
4 | import palette from './palette'
5 |
6 | export default function SecondaryTitle ({
7 | className,
8 | color,
9 | condensed,
10 | children,
11 | margins,
12 | styles,
13 | ...props
14 | }) {
15 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
16 |
17 | const cls = classNames(
18 | 'cui__title--secondary',
19 | color,
20 | className,
21 | {
22 | condensed,
23 | 'default-margins': margins
24 | }
25 | )
26 |
27 | return (
28 |
29 | {children}
30 |
31 | )
32 | }
33 |
34 | SecondaryTitle.defaultProps = {
35 | color: 'black',
36 | condensed: false,
37 | margins: false,
38 | styles: {}
39 | }
40 |
41 | SecondaryTitle.propTypes = {
42 | children: PropTypes.node,
43 | className: PropTypes.string,
44 | color: PropTypes.oneOf(palette),
45 | condensed: PropTypes.bool,
46 | margins: PropTypes.bool,
47 | styles: PropTypes.object
48 | }
49 |
--------------------------------------------------------------------------------
/components/texts/Subtitle.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/text.scss'
4 | import palette from './palette'
5 |
6 | export default function Subtitle ({
7 | children,
8 | className,
9 | color,
10 | condensed,
11 | margins,
12 | styles,
13 | ...props
14 | }) {
15 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
16 |
17 | const cls = classNames(
18 | 'cui__subtitle',
19 | color,
20 | className,
21 | {
22 | condensed,
23 | 'default-margins': margins
24 | }
25 | )
26 |
27 | return (
28 |
29 | {children}
30 |
31 | )
32 | }
33 |
34 | Subtitle.defaultProps = {
35 | color: 'black',
36 | condensed: false,
37 | margins: false,
38 | styles: {}
39 | }
40 |
41 | Subtitle.propTypes = {
42 | children: PropTypes.node,
43 | className: PropTypes.string,
44 | color: PropTypes.oneOf(palette),
45 | margins: PropTypes.bool,
46 | styles: PropTypes.object
47 | }
48 |
--------------------------------------------------------------------------------
/components/texts/TextLabel.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 | import classNamesBind from 'classnames/bind'
3 | import defaultStyles from '@klarna/ui-css-components/src/components/text.scss'
4 |
5 | export default function TextLabel ({
6 | className,
7 | children,
8 | margins,
9 | styles,
10 | ...remainingProps
11 | }) {
12 | const classNames = classNamesBind.bind({ ...defaultStyles, ...styles })
13 | const cls = classNames(
14 | 'cui__text-label',
15 | className,
16 | {
17 | 'default-margins': margins
18 | }
19 | )
20 |
21 | return (
22 | {children}
23 | )
24 | }
25 |
26 | TextLabel.defaultProps = {
27 | margins: false,
28 | styles: {}
29 | }
30 |
31 | TextLabel.propTypes = {
32 | children: PropTypes.node,
33 | className: PropTypes.string,
34 | margins: PropTypes.bool,
35 | styles: PropTypes.object
36 | }
37 |
--------------------------------------------------------------------------------
/components/texts/palette.es6:
--------------------------------------------------------------------------------
1 | export default ['black', 'blue', 'white']
2 |
--------------------------------------------------------------------------------
/components/themeable/Button.jsx:
--------------------------------------------------------------------------------
1 | import Button from '../Button'
2 | import themeable from '../../lib/decorators/themeable'
3 |
4 | // TODO: borderRadius works for PrimaryButton, but not for SecondaryButton
5 | // because of the inner border-radius not matching up.
6 | export default themeable(Button, (customizations, props) => ({
7 | ...props,
8 | customize: {
9 | ...props.customize,
10 | backgroundColor: customizations.color_button,
11 | borderRadius: customizations.radius_border,
12 | textColor: customizations.color_button_text
13 | }
14 | }))
15 |
--------------------------------------------------------------------------------
/components/themeable/Checklist.jsx:
--------------------------------------------------------------------------------
1 | import Checklist from '../Checklist'
2 | import themeable from '../../lib/decorators/themeable'
3 |
4 | const ThemeableChecklist = themeable(Checklist, (customizations, props) => ({
5 | ...props,
6 | customize: {
7 | ...props.customize,
8 | borderColor: customizations.color_border,
9 | borderRadius: customizations.radius_border
10 | }
11 | }))
12 |
13 | const ThemeableChecklistItem = themeable(Checklist.Item, (customizations, props) => ({
14 | ...props,
15 | customize: {
16 | ...props.customize,
17 | strokeColor: customizations.color_detail,
18 | borderRadius: customizations.radius_border
19 | }
20 | }))
21 |
22 | ThemeableChecklist.Item = ThemeableChecklistItem
23 |
24 | export default ThemeableChecklist
25 |
--------------------------------------------------------------------------------
/components/themeable/Dropdown.jsx:
--------------------------------------------------------------------------------
1 | import Dropdown from '../Dropdown'
2 | import themeable from '../../lib/decorators/themeable'
3 |
4 | export default themeable(Dropdown, (customizations, props) => ({
5 | customize: {
6 | ...props.customize,
7 | borderColor: customizations.color_border,
8 | borderColorSelected: customizations.color_border_selected,
9 | borderRadius: customizations.radius_border,
10 | labelColor: customizations.color_text_secondary
11 | }
12 | }))
13 |
--------------------------------------------------------------------------------
/components/themeable/Field.jsx:
--------------------------------------------------------------------------------
1 | import Field from '../Field'
2 | import themeable from '../../lib/decorators/themeable'
3 |
4 | export default themeable(Field, (customizations, props) => ({
5 | customize: {
6 | ...props.customize,
7 | borderColor: customizations.color_border,
8 | borderColorSelected: customizations.color_border_selected,
9 | borderRadius: customizations.radius_border,
10 | labelColor: customizations.color_text_secondary
11 | }
12 | }))
13 |
--------------------------------------------------------------------------------
/components/themeable/Installments.jsx:
--------------------------------------------------------------------------------
1 | import Installments from '../Installments'
2 | import themeable from '../../lib/decorators/themeable'
3 |
4 | export default themeable(Installments, (customizations, props) => ({
5 | ...props,
6 | customize: {
7 | ...props.customize,
8 | borderColor: customizations.color_border,
9 | borderColorSelected: customizations.color_border_selected,
10 | borderRadius: customizations.radius_border,
11 | labelColor: customizations.color_text
12 | }
13 | }))
14 |
--------------------------------------------------------------------------------
/components/themeable/Link.jsx:
--------------------------------------------------------------------------------
1 | import Link from '../Link'
2 | import themeable from '../../lib/decorators/themeable'
3 |
4 | export default themeable(Link, (customizations, props) => ({
5 | customize: {
6 | ...props.customize,
7 | textColor: customizations.color_link
8 | }
9 | }))
10 |
--------------------------------------------------------------------------------
/components/themeable/Switch.jsx:
--------------------------------------------------------------------------------
1 | import Switch from '../Switch'
2 | import themeable from '../../lib/decorators/themeable'
3 |
4 | export default themeable(Switch, (customizations, props) => ({
5 | ...props,
6 | customize: {
7 | ...props.customize,
8 | backgroundColor: customizations.color_checkbox,
9 | bulletColor: customizations.color_checkbox_checkmark
10 | }
11 | }))
12 |
--------------------------------------------------------------------------------
/components/themeable/Text.jsx:
--------------------------------------------------------------------------------
1 | export { default as Paragraph } from './texts/Paragraph'
2 | export { default as PrimaryTitle } from './texts/PrimaryTitle'
3 | export { default as SecondaryTitle } from './texts/SecondaryTitle'
4 | export { default as Subtitle } from './texts/Subtitle'
5 |
--------------------------------------------------------------------------------
/components/themeable/texts/Paragraph.jsx:
--------------------------------------------------------------------------------
1 | import Paragraph from '../../texts/Paragraph'
2 | import themeable from '../../../lib/decorators/themeable'
3 |
4 | const mapColor = ({ design }, { color_text, color_text_secondary }) => {
5 | switch (design) {
6 | case 'legal': return undefined
7 | case 'secondary': return color_text_secondary
8 | default: return color_text
9 | }
10 | }
11 |
12 | export default themeable(Paragraph, (customizations, props) => ({
13 | ...props,
14 | style: {
15 | ...props.style,
16 | color: mapColor(props, customizations)
17 | }
18 | }))
19 |
--------------------------------------------------------------------------------
/components/themeable/texts/PrimaryTitle.jsx:
--------------------------------------------------------------------------------
1 | import PrimaryTitle from '../../texts/PrimaryTitle'
2 | import themeable from '../../../lib/decorators/themeable'
3 |
4 | export default themeable(PrimaryTitle, (customizations, props) => ({
5 | style: {
6 | ...props.style,
7 | color: customizations.color_header
8 | }
9 | }))
10 |
--------------------------------------------------------------------------------
/components/themeable/texts/SecondaryTitle.jsx:
--------------------------------------------------------------------------------
1 | import SecondaryTitle from '../../texts/SecondaryTitle'
2 | import themeable from '../../../lib/decorators/themeable'
3 |
4 | export default themeable(SecondaryTitle, (customizations, props) => ({
5 | style: {
6 | ...props.style,
7 | color: customizations.color_header
8 | }
9 | }))
10 |
--------------------------------------------------------------------------------
/components/themeable/texts/Subtitle.jsx:
--------------------------------------------------------------------------------
1 | import Subtitle from '../../texts/Subtitle'
2 | import themeable from '../../../lib/decorators/themeable'
3 |
4 | export default themeable(Subtitle, (customizations, props) => ({
5 | style: {
6 | ...props.style,
7 | color: customizations.color_header
8 | }
9 | }))
10 |
--------------------------------------------------------------------------------
/components/uncontrolled/Field.jsx:
--------------------------------------------------------------------------------
1 | import compose from '../../lib/compose'
2 | import statefulValue from '../../lib/decorators/statefulValue'
3 | import statefulFocus from '../../lib/decorators/statefulFocus'
4 | import Field from '../Field'
5 |
6 | const UncontrolledField = compose(statefulFocus, statefulValue)(Field)
7 |
8 | UncontrolledField.displayName = 'UncontrolledField'
9 |
10 | export default UncontrolledField
11 |
--------------------------------------------------------------------------------
/components/uncontrolled/Input.jsx:
--------------------------------------------------------------------------------
1 | import compose from '../../lib/compose'
2 | import statefulValue from '../../lib/decorators/statefulValue'
3 | import statefulFocus from '../../lib/decorators/statefulFocus'
4 | import Input from '../Input'
5 |
6 | const UncontrolledInput = compose(statefulFocus, statefulValue)(Input)
7 |
8 | UncontrolledInput.displayName = 'UncontrolledInput'
9 |
10 | export default UncontrolledInput
11 |
--------------------------------------------------------------------------------
/components/uncontrolled/Installments.jsx:
--------------------------------------------------------------------------------
1 | import statefulValue from '../../lib/decorators/statefulValue'
2 | import Installments from '../Installments'
3 |
4 | const UncontrolledInstallments = statefulValue(Installments)
5 |
6 | UncontrolledInstallments.displayName = 'UncontrolledInstallments'
7 |
8 | export default UncontrolledInstallments
9 |
--------------------------------------------------------------------------------
/components/uncontrolled/RadioGroup.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react'
2 | import RadioGroup from '../RadioGroup'
3 |
4 | export default class UncontrolledRadioGroup extends Component {
5 |
6 | constructor (props) {
7 | super(props)
8 |
9 | this.state = {selected: props.selected || (props.data && props.data[0].id)}
10 |
11 | this.onChange = this.onChange.bind(this)
12 | }
13 |
14 | onChange (selected) {
15 | this.setState({ selected })
16 | this.props.onChange && this.props.onChange(selected)
17 | }
18 |
19 | render () {
20 | const { name, ...remainingProps } = this.props
21 |
22 | return (
23 |
24 |
27 | {name && }
28 |
29 | )
30 | }
31 | }
32 |
33 | UncontrolledRadioGroup.propTypes = {
34 | name: PropTypes.string,
35 | // Following PropTypes are the same as RadioGroup's
36 | // except selected and onChange are optional.
37 | selected: React.PropTypes.any,
38 | onChange: React.PropTypes.func,
39 | className: PropTypes.string,
40 | data: PropTypes.arrayOf(RadioGroup.optionSchema).isRequired
41 | }
42 |
--------------------------------------------------------------------------------
/docs/patterns.md:
--------------------------------------------------------------------------------
1 | # Patterns & practices
2 |
3 | #### Avoid HTML's attribute names as props
4 |
5 | Using HTML's original attributes may make it hard or impossible to override them. E.g, prefer `design='primary'` over `type='primary'`.
6 |
7 |
8 | #### Write Controlled / uncontrolled components
9 |
10 | We follow React's controlled/uncontrolled pattern for most components (inputs, selectors...). This means that if the component has a value (`value`, `checked`, `selected`...), and this value is passed by it's father, it will be a controlled component, so some kind of change event (`onChange`, `onInput`, ...) must be implemented. Else, it's an uncontrolled component, and the initial value can be passed as `defaultValue`/`defaultChecked`. Ex:
11 |
12 | ```
13 | // controlled
14 |
17 | this.setState({theoreticalInputValue: value})
18 | } />
19 | ```
20 |
21 | ```
22 | // uncontrolled
23 |
27 | ```
28 |
29 | #### Selector callback data should return full objects
30 |
31 | Every selector component (a component that contains a list and a selected value) should return the selected _object_ and the original event on the `onChange` callback. Ex:
32 |
33 | ```
34 | {
36 | console.log(
37 | selectedItem.key,
38 | selectedItem.label,
39 | selectedItem.customInfo,
40 | event.target
41 | )
42 | })
43 | options={[
44 | { key: 'foo', label: 'Foo', customInfo: 'bam' },
45 | { key: 'bar', label: 'Bar' },
46 | { key: 'baz', label: 'Baz' },
47 | ]} />
48 | ```
49 |
50 | This way we avoid object searches (in case we returned only the key) and allow event manipulation if needed.
--------------------------------------------------------------------------------
/example/Alerts.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Code from './Code'
3 | import { Subtitle } from '../components/Text'
4 | import Alert, { Title, Paragraph } from '../components/Alert'
5 |
6 | export default function Alerts () {
7 | return (
8 |
9 |
Error
10 |
11 |
12 | An error alert box heading
13 | Some text inside helps to get an idea of how the alert would look like. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
14 | Correlation ID: a4c531de-e35d-4901-93ae-44e32639b4b1
15 |
16 |
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/example/Blocks.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Code from './Code'
3 | import { Subtitle } from '../components/Text'
4 | import * as BlockInstallments from '../components/Block/Installments'
5 |
6 | export default function Blocks () {
7 | return (
8 |
9 | Installments
10 |
11 |
12 | Your Installments
13 |
14 |
18 |
23 |
24 |
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/example/Checklists.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Code from './Code'
3 | import Checklist from '../components/Checklist'
4 | import Theme from '../components/Theme'
5 | import ThemeableChecklist from '../components/themeable/Checklist'
6 | import { SecondaryTitle } from '../components/Text'
7 |
8 | export default function Checklists () {
9 | return (
10 |
11 | Regular
12 |
13 |
14 |
15 | Just one click and you're done
16 | Very little hassle
17 | Just do it! It can be done today, so why wait for tomorrow?
18 |
19 |
20 |
21 | Chromeless
22 |
23 |
24 | Just one click and you're done
25 | Very little hassle
26 | Just do it! It can be done today, so why wait for tomorrow?
27 |
28 |
29 |
30 | Themeable
31 |
32 |
33 |
34 | Just one click and you're done
35 | Very little hassle
36 | Just do it! It can be done today, so why wait for tomorrow?
37 |
38 |
39 |
40 |
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/example/Code.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import SyntaxHighlighter from 'react-syntax-highlighter'
3 | import asString from 'react-to-jsx'
4 |
5 | export default function Code ({ children, width }) {
6 | const code = React.Children.map(children, (item) => {
7 | return asString(item, { indent: ' ' })
8 | }).join('')
9 |
10 | return (
11 |
12 |
13 | {children}
14 |
15 |
16 | {code}
17 |
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/example/ContextMenus.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Code from './Code'
3 | import ContextMenu from '../components/ContextMenu'
4 | import Logout from '../components/icons/Logout'
5 |
6 | export default function ContextMenus () {
7 | return (
8 |
9 |
10 |
11 | Buttons
12 | Icons
13 | Labels
14 |
15 |
16 |
17 |
18 |
19 |
20 | Logout
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Buttons
29 | Icons
30 | Labels
31 |
32 |
33 |
34 | Logout
35 |
36 |
37 |
38 |
39 |
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/example/Dialogs.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import Button from '../components/Button'
3 | import Dialog from '../components/Dialog'
4 | import { CloseButton } from '../components/IconButton'
5 | import { PrimaryTitle, Subtitle, Paragraph } from '../components/Text'
6 |
7 | export default class Dialogs extends Component {
8 | constructor () {
9 | super()
10 |
11 | this.state = {
12 | dialog: {
13 | open: false
14 | }
15 | }
16 | }
17 |
18 | render () {
19 | return (
20 |
21 |
22 | Dialogs are full screen. Please click the button to show it.
23 |
24 |
25 |
this.setState({
26 | dialog: {
27 | open: true
28 | }
29 | })}>
30 | Show Dialog
31 |
32 |
33 |
34 |
35 |
36 | this.setState({ dialog: { open: false } })}
38 | />
39 |
40 |
41 |
42 | The title is primary
43 | Just trying to fill up space
44 | Completely synergize resource taxing relationships via premier niche markets. Professionally cultivate one-to-one customer service with robust ideas. Dynamically innovate resource-leveling customer service for state of the art customer service.
45 |
46 |
47 |
48 | this.setState({ dialog: { open: false } })} >Close the nice dialog
49 |
50 |
51 |
52 |
53 | )
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/example/Icons.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import AccountActivated from '../components/icons/AccountActivated'
4 | import AllSet from '../components/icons/AllSet'
5 | import Arrow from '../components/icons/Arrow'
6 | import Checkmark from '../components/icons/Checkmark'
7 | import Done from '../components/icons/Done'
8 | import Error from '../components/icons/Error'
9 | import Details from '../components/icons/Details'
10 | import Download from '../components/icons/Download'
11 | import ExtendDate from '../components/icons/ExtendDate'
12 | import Items from '../components/icons/Items'
13 | import Letter from '../components/icons/Letter'
14 | import Logout from '../components/icons/Logout'
15 | import Mail from '../components/icons/Mail'
16 | import NotFound from '../components/icons/NotFound'
17 | import OpenLetter from '../components/icons/OpenLetter'
18 | import PadLock from '../components/icons/PadLock'
19 | import Password from '../components/icons/Password'
20 | import Person from '../components/icons/Person'
21 | import Phone from '../components/icons/Phone'
22 | import Question from '../components/icons/Question'
23 | import Cancel from '../components/icons/Cancel'
24 | import Remind from '../components/icons/Remind'
25 | import Time from '../components/icons/Time'
26 | import SMS from '../components/icons/SMS'
27 | import Warning from '../components/icons/Warning'
28 | import Wrong from '../components/icons/Wrong'
29 |
30 | import { SecondaryTitle, Subtitle, Paragraph } from '../components/Text'
31 | import Code from './Code'
32 | import colors from '../components/icons/constants/colors'
33 |
34 | const icons = {
35 | big: [
36 | AccountActivated,
37 | AllSet,
38 | Done,
39 | Error,
40 | Letter,
41 | NotFound,
42 | OpenLetter,
43 | PadLock,
44 | SMS,
45 | Time,
46 | Warning,
47 | Wrong
48 | ],
49 |
50 | tiny: [
51 | Arrow,
52 | Checkmark,
53 | Details,
54 | Download,
55 | ExtendDate,
56 | Items,
57 | Logout,
58 | Mail,
59 | Password,
60 | Person,
61 | Phone,
62 | Question,
63 | Cancel,
64 | Remind
65 | ]
66 | }
67 |
68 | export default function Icons () {
69 | return (
70 |
71 |
72 | Each type of icon is designed for the size that it is displayed in. Resizing the icons is possible since they are SVG, but it's not recommended since they are drawn to have the line widths matching the line styles of the rest of the components.
73 |
74 |
75 |
Colors
76 |
Big
77 |
78 | {colors.map((name) =>
79 |
87 | )}
88 |
89 |
90 |
Tiny
91 |
92 | {colors.map((name) =>
93 |
101 | )}
102 |
103 |
104 |
Big icons
105 |
106 | {icons.big.map((Icon) => )}
107 |
108 |
109 |
Tiny icons
110 |
111 | {icons.tiny.map((Icon) => )}
112 |
113 |
114 | )
115 | }
116 |
--------------------------------------------------------------------------------
/example/Installments.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import InstallmentsComponent from '../components/Installments'
3 | import Theme from '../components/Theme'
4 | import ThemeableInstallmentsComponent from '../components/themeable/Installments'
5 | import UncontrolledInstallments from '../components/uncontrolled/Installments'
6 | import { SecondaryTitle, Subtitle } from '../components/Text'
7 | import Code from './Code'
8 |
9 | const options = [
10 | { key: 'installments_6', value: '$64.17/mo.', connector: 'for', info: ' 6 months' },
11 | { key: 'installments_12', value: '$32.09/mo.', connector: 'for', info: ' 12 months' },
12 | { key: 'installments_24', value: '$16.05/mo.', connector: 'for', info: ' 24 months' }
13 | ]
14 |
15 | export default function Installments () {
16 | return (
17 |
18 | Regular
19 |
20 | console.log('You selected', key)}
22 | name='installments'
23 | value='installments_12'
24 | options={options}
25 | />
26 |
27 |
28 | Uncontrolled
29 |
30 | console.log('You selected', key)}
32 | name='installments2'
33 | value='installments_24'
34 | options={options}
35 | />
36 |
37 |
38 | Themeable
39 |
40 |
41 | console.log('You selected', key)}
43 | name='installments'
44 | value='installments_24'
45 | options={options}
46 | />
47 |
48 |
49 |
50 | )
51 | }
52 |
--------------------------------------------------------------------------------
/example/Labels.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Label from '../components/Label'
3 | import Code from './Code'
4 |
5 | export default function Labels () {
6 | return (
7 |
8 |
9 | {
10 | Label.designs.map((design) => (
11 |
12 | {design}
13 |
14 | ))
15 | }
16 |
17 | {
18 | Label.designs.map((design) => (
19 |
20 | {design}
21 |
22 | ))
23 | }
24 |
25 |
26 |
27 | inverted
28 |
29 |
30 | inverted outline
31 |
32 |
33 |
34 |
35 |
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/example/Links.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from '../components/Link'
3 | import Code from './Code'
4 | import { SecondaryTitle } from '../components/Text'
5 | import Theme from '../components/Theme'
6 | import ThemeableLink from '../components/themeable/Link'
7 |
8 | export default function Links () {
9 | return (
10 |
11 |
Regular
12 |
13 | Click me!
14 |
15 |
16 |
Dynamic styling
17 |
18 | Click me!
19 |
20 |
21 |
Themeable
22 |
23 |
24 |
25 | Click me!
26 |
27 |
28 |
29 |
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/example/Loaders.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Loader from '../components/Loader'
3 | import { SecondaryTitle, Subtitle } from '../components/Text'
4 | import Code from './Code'
5 |
6 | export default function Loaders () {
7 | return (
8 |
9 |
Primary
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
Inline
18 |
19 |
20 |
21 |
22 |
23 |
Secondary
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
White
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/example/Previews.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Preview, { PreviewTitle, PreviewLink } from '../components/Preview'
3 | import { SecondaryTitle } from '../components/Text'
4 | import Code from './Code'
5 |
6 | export default function Previews () {
7 | return (
8 |
9 |
Default
10 |
11 |
12 | John Smith
13 | 1425 North Avenue Street
14 | San Francisco
15 | 94100 California
16 | United States
17 |
18 |
19 |
20 |
With link
21 |
22 |
23 | John Smith
24 | 1425 North Avenue Street
25 | San Francisco
26 | 94100 California
27 | United States
28 | Change address
29 |
30 |
31 |
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/example/RadioGroups.jsx:
--------------------------------------------------------------------------------
1 | /* globals alert */
2 |
3 | import React from 'react'
4 | import RadioGroup from '../components/RadioGroup'
5 | import UncontrolledRadioGroup from '../components/uncontrolled/RadioGroup'
6 | import Button from '../components/Button'
7 | import { SecondaryTitle, Subtitle, Paragraph } from '../components/Text'
8 | import Code from './Code'
9 |
10 | export default function RadioGroups () {
11 | const data = [
12 | {id: 1, label: 'Lorem', description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.'},
13 | {id: 2, label: 'Ipsum', description: "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."}
14 | ]
15 |
16 | return (
17 |
18 |
19 | RadioGroups allow the user to pick one option among many
20 | with a design similar to HTML's input type radio.
21 |
22 |
23 |
Stateless
24 |
25 | Stateless RadioGroups shall be used when you will control
26 | it's selected value. Therefore, you're in charge of
27 | implementing the props onChange and selected .
28 |
29 |
30 | As a rule of thumb, you'll use the stateless component if
31 | the RadioGroup is not in a form, and you want to trigger
32 | something on every change of this component.
33 |
34 |
35 | An example implementation of how you should consume the
36 | RadioGroup is the UncontrolledRadioGroup itself.
37 |
38 |
39 |
40 |
41 |
42 |
Statefull
43 |
44 | Use the statefull selector if you don't want to control
45 | the selector, or you're using it on a form, passing the
46 | prop name .
47 |
48 |
49 |
50 |
51 |
52 |
In a form
53 |
54 |
61 |
62 |
63 | )
64 | }
65 |
--------------------------------------------------------------------------------
/example/Selectors.jsx:
--------------------------------------------------------------------------------
1 | /* globals alert */
2 |
3 | import React from 'react'
4 | import Button from '../components/Button'
5 | import RadioGroup from '../components/RadioGroup'
6 | import UncontrolledRadioGroup from '../components/uncontrolled/RadioGroup'
7 | import Selector from '../components/Selector'
8 | import { SecondaryTitle, Subtitle, Paragraph } from '../components/Text'
9 | import Code from './Code'
10 |
11 | export default function Selectors () {
12 | const data = [
13 | {id: 1, label: 'Lorem', description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.'},
14 | {id: 2, label: 'Ipsum', description: "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."}
15 | ]
16 |
17 | return (
18 |
19 |
RadioGroup
20 |
21 | RadioGroups allow the user to pick one option among many
22 | with a design similar to HTML's input type radio.
23 |
24 |
25 |
Controlled
26 |
27 | Controlled RadioGroups shall be used when you will control
28 | it's selected value. Therefore, you're in charge of
29 | implementing the props onChange and selected .
30 |
31 |
32 | As a rule of thumb, you'll use the stateless component if
33 | the RadioGroup is not in a form, and you wanna trigger
34 | something on every change of this component.
35 |
36 |
37 | An example implementation of how you should consume the
38 | RadioGroup is the UncontrolledRadioGroup itself.
39 |
40 |
41 |
42 |
43 |
44 |
Uncontrolled
45 |
46 | Use the stateful selector if you don't wanna control
47 | the selector, or you're using it on a form, passing the
48 | prop name .
49 |
50 |
51 |
52 |
53 |
54 |
Uncontrolled in a form
55 |
56 |
63 |
64 |
65 |
Selector
66 |
67 | Same as RadioGroups with different style.
68 |
69 |
70 |
71 |
72 |
73 | )
74 | }
75 |
--------------------------------------------------------------------------------
/example/Switches.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Switch from '../components/Switch'
3 | import Theme from '../components/Theme'
4 | import ThemeableSwitch from '../components/themeable/Switch'
5 | import Button from '../components/Button'
6 | import { SecondaryTitle } from '../components/Text'
7 | import Code from './Code'
8 |
9 | export default function Switches () {
10 | return (
11 |
12 | Regular
13 |
14 | This is a toggle switch
15 |
16 |
17 | Checked
18 |
19 | This is a toggle switch checked
20 |
21 |
22 | Error
23 |
24 | This is a toggle switch in an error state
25 |
26 |
27 | Right
28 |
29 | This is a toggle switch on the right side
30 |
31 |
32 | Disabled
33 |
34 | This is a toggle switch disabled
35 |
36 |
37 | Dynamic styling
38 |
39 | This is a toggle switch with dynamic styling
40 |
41 |
42 | Checkbox
43 |
44 | This is a toggle switch with checkbox design
45 |
46 |
47 | Checkbox with legal size text
48 |
49 | This is a toggle switch with checkbox design and legal size text that usually will fold into multiple lines
50 |
51 |
52 | Checkbox with dynamic styling
53 |
54 | This is a toggle switch with checkbox design and dynamic styling
55 |
56 |
57 | In a form
58 |
59 |
66 |
67 |
68 | Outside a form (using a callback)
69 |
70 | {
71 | window.alert(value)
72 | }}>Would you like fries?
73 |
74 |
75 | Themeable
76 |
77 |
78 | Would you like fries?
79 |
80 |
81 |
82 | )
83 | }
84 |
--------------------------------------------------------------------------------
/example/examples.es6:
--------------------------------------------------------------------------------
1 | export Alerts from './Alerts'
2 | export Buttons from './Buttons'
3 | export Blocks from './Blocks'
4 | export Checklists from './Checklists'
5 | export ContextMenus from './ContextMenus'
6 | export Dialogs from './Dialogs'
7 | export Dropdowns from './Dropdowns'
8 | export Fields from './Fields'
9 | export Icons from './Icons'
10 | export Inputs from './Inputs'
11 | export Installments from './Installments'
12 | export Labels from './Labels'
13 | export Links from './Links'
14 | export Loaders from './Loaders'
15 | export Menus from './Menus'
16 | export Previews from './Previews'
17 | export RadioGroups from './RadioGroups'
18 | export Selectors from './Selectors'
19 | export Switches from './Switches'
20 | export Texts from './Texts'
21 | export Tooltips from './Tooltips'
22 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
10 |
11 |
12 |
13 | {%=o.htmlWebpackPlugin.options.title || 'Webpack App'%}
14 | {% if (o.htmlWebpackPlugin.files.favicon) { %}
15 |
16 | {% } %}
17 | {% for (var css in o.htmlWebpackPlugin.files.css) { %}
18 |
19 | {% } %}
20 |
21 |
22 |
23 |
24 |
25 | {% for (var chunk in o.htmlWebpackPlugin.files.chunks) { %}
26 |
27 | {% } %}
28 |
29 |
30 |
--------------------------------------------------------------------------------
/example/index.jsx:
--------------------------------------------------------------------------------
1 | import 'babel-polyfill'
2 | import 'normalize.css'
3 | import React from 'react'
4 | import ReactDOM from 'react-dom'
5 | import classNames from 'classnames'
6 | import { PrimaryTitle } from '../components/Text'
7 | import Link from '../components/Link'
8 | import * as examples from './examples'
9 | import styles from './index.scss'
10 |
11 | function Root () {
12 | const anchor = window.location.hash.substring(1)
13 | const Example = anchor && examples[anchor] || Object.values(examples)[0]
14 |
15 | return (
16 |
17 |
18 |
19 | {
20 | Object.values(examples).map(({ name }) => (
21 |
25 | {name}
26 |
27 | ))
28 | }
29 |
30 |
31 |
32 |
{Example.name}
33 |
34 |
35 |
36 | )
37 | }
38 |
39 | const render = () => (
40 | ReactDOM.render(
41 | ,
42 | document.getElementById('root')
43 | )
44 | )
45 |
46 | window.onhashchange = render
47 | render()
48 |
--------------------------------------------------------------------------------
/example/index.scss:
--------------------------------------------------------------------------------
1 | @import url('//fonts.googleapis.com/css?family=Open+Sans:300,400,600');
2 |
3 | body {
4 | font-family: 'Open Sans';
5 | }
6 |
7 | main {
8 | display: table;
9 | }
10 |
11 | aside {
12 | -webkit-font-smoothing: antialiased;
13 | display: table-cell;
14 | font-size: 13px;
15 | font-weight: normal;
16 | padding: 10px 0 0 10px;
17 | width: 200px;
18 | }
19 |
20 | nav {
21 | position: fixed;
22 |
23 | a {
24 | display: block;
25 | padding: 5px;
26 | text-decoration: none;
27 |
28 | &.selected {
29 | text-decoration: underline;
30 | }
31 | }
32 | }
33 |
34 | .example {
35 | display: table-cell;
36 | max-width: 580px;
37 | width: 580px;
38 | }
39 |
40 | pre {
41 | background: #F5F5F7 !important;
42 | padding: 2em !important;
43 | margin: 0;
44 | }
45 |
46 | section {
47 | border-radius: 6px;
48 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.4);
49 | box-sizing: border-box;
50 | margin-bottom: 1em;
51 | overflow: hidden;
52 | }
53 |
54 | article {
55 | padding: 1em;
56 | }
57 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | var webpackConfig = require('./webpack.config.test.js')
2 | var argv = require('yargs').argv
3 |
4 | var browserToUse = ['PhantomJS']
5 | if (process.env.BROWSER) {
6 | browserToUse = process.env.BROWSER.split(',')
7 | }
8 |
9 | module.exports = function (config) {
10 | config.set({
11 | basePath: '',
12 | frameworks: ['mocha'],
13 | files: [
14 | 'node_modules/babel-polyfill/dist/polyfill.js',
15 | 'tests/**/*.spec.*'
16 | ],
17 | exclude: [],
18 | preprocessors: {
19 | 'tests/**/*': ['webpack']
20 | },
21 | webpack: {
22 | devtool: 'inline-source-map',
23 | module: webpackConfig.module,
24 | resolve: webpackConfig.resolve,
25 | resolveLoader: webpackConfig.resolveLoader
26 | },
27 | webpackMiddleware: {
28 | noInfo: true
29 | },
30 |
31 | reporters: ['spec', 'saucelabs'],
32 |
33 | colors: true,
34 | logLevel: config.LOG_INFO,
35 | autoWatch: argv.watch,
36 |
37 | sauceLabs: {
38 | public: 'public',
39 | testName: 'klarna-react-components'
40 | },
41 |
42 | captureTimeout: 120000,
43 | browserNoActivityTimeout: 1000000,
44 |
45 | customLaunchers: {
46 | SL_Chrome: {
47 | base: 'SauceLabs',
48 | browserName: 'chrome'
49 | },
50 | SL_MAC_SAFARI_9_0: {
51 | base: 'SauceLabs',
52 | browserName: 'safari',
53 | platform: 'OS X 10.11',
54 | version: '9.0'
55 | },
56 | SL_IE_10: {
57 | base: 'SauceLabs',
58 | browserName: 'internet explorer',
59 | platform: 'Windows 8',
60 | version: '10.0'
61 | },
62 | SL_IE_11: {
63 | base: 'SauceLabs',
64 | browserName: 'internet explorer',
65 | platform: 'Windows 8.1',
66 | version: '11.0'
67 | },
68 | SL_Android_4_2: {
69 | base: 'SauceLabs',
70 | browserName: 'android',
71 | platform: 'Linux',
72 | version: '4.2'
73 | },
74 | SL_Android_4_3: {
75 | base: 'SauceLabs',
76 | browserName: 'android',
77 | platform: 'Linux',
78 | version: '4.3'
79 | },
80 | SL_Android_4_4: {
81 | base: 'SauceLabs',
82 | browserName: 'android',
83 | platform: 'Linux',
84 | version: '4.4'
85 | },
86 | SL_Android_5_0: {
87 | base: 'SauceLabs',
88 | browserName: 'android',
89 | platform: 'Linux',
90 | version: '5.0'
91 | },
92 | SL_Android_5_1: {
93 | base: 'SauceLabs',
94 | browserName: 'android',
95 | platform: 'Linux',
96 | version: '5.1'
97 | },
98 | SL_IOS_8_0: {
99 | base: 'SauceLabs',
100 | browserName: 'iPhone',
101 | platform: 'OS X 10.10',
102 | version: '8.0'
103 | },
104 | SL_IOS_8_1: {
105 | base: 'SauceLabs',
106 | browserName: 'iPhone',
107 | platform: 'OS X 10.10',
108 | version: '8.1'
109 | },
110 | SL_IOS_9_2: {
111 | base: 'SauceLabs',
112 | browserName: 'iPhone',
113 | platform: 'OS X 10.10',
114 | version: '9.2'
115 | }
116 | },
117 |
118 | browsers: browserToUse,
119 | singleRun: !argv.watch,
120 | concurrency: Infinity
121 | })
122 | }
123 |
--------------------------------------------------------------------------------
/lib/combinations.es6:
--------------------------------------------------------------------------------
1 | export default (xs, ys) =>
2 | xs
3 | .map((x) => ys.map((y) => [x, y]))
4 | .reduce((a, b) => a.concat(b), [])
5 |
--------------------------------------------------------------------------------
/lib/compose.es6:
--------------------------------------------------------------------------------
1 | export default (...fs) => (x) => fs.reduceRight((acc, f) => f(acc), x)
2 |
--------------------------------------------------------------------------------
/lib/decorators/statefulFocus.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | export default (Target) => {
4 | class DecoratedComponent extends Component {
5 | render () {
6 | const {
7 | onBlur,
8 | onFocus,
9 | ...props
10 | } = this.props
11 |
12 | return (
13 |
19 | )
20 | }
21 |
22 | handleBlur (onBlur, e) {
23 | this.setState({
24 | focus: undefined
25 | })
26 |
27 | onBlur && onBlur(e)
28 | }
29 |
30 | handleFocus (onFocus, e) {
31 | this.setState({
32 | focus: true
33 | })
34 |
35 | onFocus && onFocus(e)
36 | }
37 | }
38 |
39 | DecoratedComponent.displayName = Target.displayName || Target.name
40 |
41 | return DecoratedComponent
42 | }
43 |
--------------------------------------------------------------------------------
/lib/decorators/statefulValue.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | export default (Target) => {
4 | class DecoratedComponent extends Component {
5 | render () {
6 | const {
7 | onChange,
8 | ...props
9 | } = this.props
10 |
11 | return (
12 |
17 | )
18 | }
19 |
20 | handleChange (onChange, e) {
21 | this.setState({
22 | value:
23 | e && e.target
24 | ? e.target.value
25 | : e
26 | })
27 |
28 | onChange && onChange(e)
29 | }
30 | }
31 |
32 | DecoratedComponent.displayName = Target.displayName || Target.name
33 |
34 | return DecoratedComponent
35 | }
36 |
--------------------------------------------------------------------------------
/lib/decorators/themeable.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { withPropsFromContext } from 'react-context-props'
3 |
4 | const themeable = (Target, adapter) => {
5 | const ThemeableComponent = withPropsFromContext(
6 | ({ customizations, ...props }) => (
7 |
13 | ),
14 | ['customizations']
15 | )
16 |
17 | ThemeableComponent.displayName = Target.displayName || Target.name
18 |
19 | return ThemeableComponent
20 | }
21 |
22 | export default themeable
23 |
--------------------------------------------------------------------------------
/lib/features/fieldStates/index.es6:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react'
2 |
3 | export const getClassName = ({ disabled, error, warning }) => ({
4 | 'is-disabled': disabled,
5 | 'is-error': error,
6 | 'is-warning': warning
7 | })
8 |
9 | export const defaultProps = {
10 | disabled: false,
11 | error: false,
12 | warning: false
13 | }
14 |
15 | export const propTypes = {
16 | disabled: PropTypes.bool,
17 | error: PropTypes.bool,
18 | warning: PropTypes.bool
19 | }
20 |
--------------------------------------------------------------------------------
/lib/features/keyboardEvents/index.es6:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react'
2 |
3 | const noop = () => {}
4 |
5 | const KEYS = {
6 | ENTER: 13,
7 | TAB: 9
8 | }
9 |
10 | export const handleKeyDown = ({ onTab, onEnter }) => (e) => {
11 | switch (e.keyCode) {
12 | case KEYS.TAB: {
13 | const direction = e.shiftKey ? -1 : 1
14 | return onTab(direction, e)
15 | }
16 | case KEYS.ENTER: {
17 | return onEnter(e)
18 | }
19 | }
20 | }
21 |
22 | handleKeyDown.defaultProps = {
23 | onTab: noop,
24 | onEnter: noop
25 | }
26 |
27 | handleKeyDown.propTypes = {
28 | onTab: PropTypes.func,
29 | onEnter: PropTypes.func
30 | }
31 |
--------------------------------------------------------------------------------
/lib/features/programmaticFocus/index.es6:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react'
2 | import values from '../../values'
3 |
4 | export const FOCUS_TYPES = {
5 | FAKE: 'fake',
6 | REAL: 'real'
7 | }
8 |
9 | export const getClassName = ({ focus }) => focus && 'is-focused'
10 |
11 | export const maybeFocus = (document) => (type, input) => {
12 | switch (type) {
13 | case FOCUS_TYPES.REAL:
14 | if (document.activeElement !== input) {
15 | input.focus()
16 | }
17 | break
18 | case FOCUS_TYPES.FAKE:
19 | if (typeof input.scrollIntoViewIfNeeded === 'function') {
20 | input.scrollIntoViewIfNeeded()
21 | }
22 | break
23 | }
24 | }
25 |
26 | export const propTypes = {
27 | focus: PropTypes.oneOfType([
28 | PropTypes.bool,
29 | PropTypes.oneOf(values(FOCUS_TYPES))
30 | ])
31 | }
32 |
--------------------------------------------------------------------------------
/lib/features/stacking/index.es6:
--------------------------------------------------------------------------------
1 | import { PropTypes } from 'react'
2 | import combinations from '../../../lib/combinations'
3 | import fieldSizeFraction from '../../../propTypes/fieldSizeFraction'
4 |
5 | export const position = {
6 | defaultProps: {
7 | bottom: false,
8 | center: false,
9 | left: false,
10 | right: false,
11 | top: false
12 | },
13 |
14 | getClassName: (props) =>
15 | (
16 | position.positionCombinations
17 | .concat(position.positionList.map((x) => [x]))
18 | .find((combination) =>
19 | combination.length === 1
20 | ? props[combination[0]]
21 | : (props[combination[0]] && props[combination[1]])
22 | ) || []
23 | ).join('-')
24 | ,
25 |
26 | getBorderRadii: ({ top, bottom, square }, radius) =>
27 | (
28 | top
29 | ? {
30 | borderBottomLeftRadius: '0px',
31 | borderBottomRightRadius: '0px',
32 | borderTopLeftRadius: radius,
33 | borderTopRightRadius: radius
34 | }
35 | : bottom
36 | ? {
37 | borderBottomLeftRadius: radius,
38 | borderBottomRightRadius: radius,
39 | borderTopLeftRadius: '0px',
40 | borderTopRightRadius: '0px'
41 | }
42 | : square
43 | ? {
44 | borderRadius: '0px'
45 | }
46 | : {
47 | borderRadius: radius
48 | }
49 | )
50 | ,
51 |
52 | positionCombinations: combinations(
53 | ['bottom', 'top'],
54 | ['left', 'right']
55 | ),
56 |
57 | positionList: [
58 | 'bottom',
59 | 'center',
60 | 'left',
61 | 'right',
62 | 'top'
63 | ],
64 |
65 | propTypes: {
66 | bottom: PropTypes.bool,
67 | center: PropTypes.bool,
68 | left: PropTypes.bool,
69 | right: PropTypes.bool,
70 | top: PropTypes.bool
71 | }
72 | }
73 |
74 | const MAX_SIZE = 5
75 |
76 | export const size = {
77 | defaultProps: {
78 | size: '1/1'
79 | },
80 |
81 | getClassName: (props) => size.map[props.size],
82 |
83 | map: {
84 | '1/2': 'half',
85 | '1/3': 'third',
86 | '2/3': 'two-thirds',
87 | '1/4': 'quarter',
88 | '2/4': 'half',
89 | '3/4': 'three-quarters',
90 | '1/5': 'twenty',
91 | '2/5': 'forty',
92 | '3/5': 'sixty',
93 | '4/5': 'eighty'
94 | },
95 |
96 | max: MAX_SIZE,
97 |
98 | propTypes: {
99 | size: fieldSizeFraction(MAX_SIZE)
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/lib/toObjectWithValue.es6:
--------------------------------------------------------------------------------
1 | export default (value) => (list) =>
2 | list.reduce((accumulator, item) => ({
3 | ...accumulator,
4 | [item]: value
5 | }), {})
6 |
--------------------------------------------------------------------------------
/lib/validators.es6:
--------------------------------------------------------------------------------
1 | export const composeValidators = (...validators) => (...values) =>
2 | validators.reduceRight(
3 | (accumulator, validator) => accumulator || validator(...values),
4 | null
5 | )
6 |
7 | const any = (...conditions) => conditions.reduce((a, b) => a || b)
8 |
9 | export const isFraction = (props, propName, componentName) => {
10 | const values = props[propName].toString().split('/')
11 |
12 | return any(
13 | values.length !== 2,
14 | isNaN(values[0]),
15 | isNaN(values[1])
16 | )
17 | ? new Error(`Invalid prop \`${propName}\` supplied to \`${componentName}\`: needs to be a fraction, such as \`1/3\``)
18 | : undefined
19 | }
20 |
21 |
22 | export const isPositiveIntegerFraction = (props, propName, componentName) => {
23 | const values = props[propName].split('/')
24 | const notAPositiveInteger = (value) => /[^0-9]/.test(value)
25 |
26 | return any(
27 | notAPositiveInteger(values[0]),
28 | notAPositiveInteger(values[1]),
29 | parseInt(values[0], 10) === 0,
30 | parseInt(values[1], 10) === 0
31 | )
32 | ? new Error(`Invalid prop \`${propName}\` supplied to \`${componentName}\`: fraction values need to be positive integers`)
33 | : undefined
34 | }
35 |
36 | export const isDenominatorBelowThreshold = (threshold) => (props, propName, componentName) => {
37 | const denominator = parseInt(props[propName].split('/')[1], 10)
38 |
39 | return denominator > threshold
40 | ? new Error(`Invalid prop \`${propName}\` supplied to \`${componentName}\`: values needs to be lower or equal to \`${threshold}\``)
41 | : undefined
42 | }
43 |
44 | export const isNumeratorAboveDenominator = (props, propName, componentName) => {
45 | const [numerator, denominator] = props[propName].split('/').map((value) => parseInt(value, 10))
46 |
47 | return numerator > denominator
48 | ? new Error(`Invalid prop \`${propName}\` supplied to \`${componentName}\`: numerator needs to be lower or equal to the denominator (\`${denominator}\` in this case)`)
49 | : undefined
50 | }
51 |
--------------------------------------------------------------------------------
/lib/values.es6:
--------------------------------------------------------------------------------
1 | export default (x) => Object.keys(x).map((k) => x[k])
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@klarna/ui-react-components",
3 | "version": "0.16.0",
4 | "description": "Klarna's UI React Components",
5 | "repository": "https://github.com/klarna/ui-react-components",
6 | "license": "SEE LICENSE IN LICENSE",
7 | "scripts": {
8 | "start": "webpack-dev-server --port 7777 --host 0.0.0.0 --history-api-fallback",
9 | "build": "webpack",
10 | "test": "standard && karma start karma.conf.js",
11 | "lint": "standard",
12 | "test:watch": "npm test -- --watch"
13 | },
14 | "author": "Klarna front end people",
15 | "dependencies": {
16 | "@klarna/ui-css-components": "9.3.0",
17 | "classnames": "2.2.5",
18 | "parse-color": "1.0.0",
19 | "react-context-props": "^0.1.3"
20 | },
21 | "peerDependencies": {
22 | "react": "^15.0.2"
23 | },
24 | "devDependencies": {
25 | "autoprefixer-loader": "^3.1.0",
26 | "babel-core": "^6.7.4",
27 | "babel-loader": "^6.2.4",
28 | "babel-polyfill": "^6.0.16",
29 | "babel-preset-es2015": "^6.1.2",
30 | "babel-preset-react": "^6.1.2",
31 | "babel-preset-stage-0": "^6.1.2",
32 | "css-loader": "^0.19.0",
33 | "file-loader": "^0.8.5",
34 | "html-webpack-plugin": "^1.6.1",
35 | "karma": "^0.13.19",
36 | "karma-chrome-launcher": "0.2.2",
37 | "karma-coverage": "0.5.2",
38 | "karma-firefox-launcher": "0.1.7",
39 | "karma-ie-launcher": "0.2.0",
40 | "karma-mocha": "^0.2.1",
41 | "karma-phantomjs-launcher": "^0.2.3",
42 | "karma-prettybrowser-reporter": "git+https://github.com/cpapazaf/karma-prettybrowser-reporter.git",
43 | "karma-safari-launcher": "0.1.1",
44 | "karma-sauce-launcher": "^0.3.1",
45 | "karma-spec-reporter": "0.0.23",
46 | "karma-webdriver-launcher": "1.0.4",
47 | "karma-webpack": "^1.7.0",
48 | "mocha": "^2.3.4",
49 | "node-sass": "^3.4.2",
50 | "normalize.css": "^4.1.1",
51 | "null-loader": "^0.1.1",
52 | "phantomjs": "^1.9.19",
53 | "ramda": "^0.21.0",
54 | "react": "^15.0.2",
55 | "react-addons-test-utils": "^15.0.2",
56 | "react-dom": "^15.0.2",
57 | "react-motion": "^0.4.2",
58 | "react-syntax-highlighter": "^1.1.2",
59 | "react-to-jsx": "^1.3.2",
60 | "resolve-url-loader": "^1.2.0",
61 | "sass-loader": "^3.2.0",
62 | "sinon": "git+https://github.com/sinonjs/sinon.git",
63 | "standard": "^6.0.8",
64 | "style-loader": "^0.12.4",
65 | "url-loader": "^0.5.6",
66 | "webpack": "^1.12.2",
67 | "webpack-dev-server": "^1.12.1",
68 | "webpack-error-notification": "^0.1.6",
69 | "yargs": "^3.31.0"
70 | },
71 | "standard": {
72 | "globals": [
73 | "describe",
74 | "it"
75 | ]
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/propTypes/fieldSizeFraction.es6:
--------------------------------------------------------------------------------
1 | import {
2 | composeValidators,
3 | isDenominatorBelowThreshold,
4 | isFraction,
5 | isNumeratorAboveDenominator,
6 | isPositiveIntegerFraction
7 | } from '../lib/validators'
8 |
9 | export default (threshold) => composeValidators(
10 | isNumeratorAboveDenominator,
11 | isDenominatorBelowThreshold(threshold),
12 | isPositiveIntegerFraction,
13 | isFraction
14 | )
15 |
--------------------------------------------------------------------------------
/propTypes/validateSize.es6:
--------------------------------------------------------------------------------
1 | export default function validateSize (props, prop, component) {
2 | if (isSize(props[prop])) {
3 | return null
4 | }
5 |
6 | return new Error(`${prop} '${props[prop]}' in ${component} is not valid.`)
7 | }
8 |
9 | const isSize = (size) => (
10 | typeof size === 'undefined' ||
11 | typeof size === 'string' &&
12 | size.match(/^(0|\d*\.?\d+?(px|%|em|rem|ex|cm|mm|in|pt|pc|ch|vh|vw|vmin|vmax))$/)
13 | )
14 |
--------------------------------------------------------------------------------
/tests/Alert.spec.jsx:
--------------------------------------------------------------------------------
1 | import Alert, { Title, Paragraph } from '../components/Alert'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 |
5 | const render = renderer(Alert)
6 | const renderTitle = renderer(Title)
7 | const renderParagraph = renderer(Paragraph)
8 |
9 | describe('Alert', () => {
10 | describe('error', () => {
11 | const alert = render({design: 'error'}, [
12 | renderTitle({}, 'Header'),
13 | renderParagraph({}, 'LoremIpsum')
14 | ])
15 | const title = alert.props.children[0]
16 | const paragraph = alert.props.children[1]
17 |
18 | it("renders tag 'div'", () => {
19 | equal(alert.type, 'div')
20 | })
21 |
22 | it("has className 'cui__alert--error'", () => {
23 | ok(alert.props.className.match('cui__alert--error'))
24 | })
25 |
26 | it('should have the content', () => {
27 | equal(alert.props.children.length, 2)
28 | ok(title.props.children.match('Header'))
29 | ok(paragraph.props.children.match('LoremIpsum'))
30 | })
31 | })
32 |
33 | describe('Title', () => {
34 | const alert = render({design: 'error'}, renderTitle({}, 'Header'))
35 | const title = alert.props.children
36 |
37 | it("renders tag 'h1'", () => {
38 | equal(title.type, 'h1')
39 | })
40 |
41 | it("has className 'cui__alert__title'", () => {
42 | ok(title.props.className.match('cui__alert__title'))
43 | })
44 |
45 | it('should have the content', () => {
46 | ok(title.props.children.match('Header'))
47 | })
48 | })
49 |
50 | describe('Paragraph', () => {
51 | const alert = render({design: 'error'}, renderParagraph({}, 'LoremIpsum'))
52 | const paragraph = alert.props.children
53 |
54 | it("renders tag 'p'", () => {
55 | equal(paragraph.type, 'p')
56 | })
57 |
58 | it("has className 'cui__alert__paragraph'", () => {
59 | ok(paragraph.props.className.match('cui__alert__paragraph'))
60 | })
61 |
62 | it('should have the content', () => {
63 | ok(paragraph.props.children.match('LoremIpsum'))
64 | })
65 | })
66 | })
67 |
--------------------------------------------------------------------------------
/tests/Amount.spec.jsx:
--------------------------------------------------------------------------------
1 | import Amount from '../components/texts/Amount'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 | import describePalette from './describePalette'
5 |
6 | const render = renderer(Amount)
7 |
8 | describe('Amount', () => {
9 | describe('default', () => {
10 | const primaryTitle = render({}, 'LoremIpsum')
11 |
12 | it('renders tag "p"', () => {
13 | equal(primaryTitle.type, 'p')
14 | })
15 |
16 | it('has className "cui__amount-text"', () => {
17 | ok(primaryTitle.props.className.match('cui__amount-text'))
18 | })
19 |
20 | it('should have the content', () => {
21 | ok(primaryTitle.props.children.match('LoremIpsum'))
22 | })
23 | })
24 |
25 | describePalette(render)
26 | })
27 |
--------------------------------------------------------------------------------
/tests/Checklist.spec.jsx:
--------------------------------------------------------------------------------
1 | import Checklist from '../components/Checklist'
2 | import { equal, ok } from 'assert'
3 | import { renderer } from './helpers'
4 |
5 | const renderChecklist = renderer(Checklist)
6 | const renderChecklistItem = renderer(Checklist.Item)
7 |
8 | const items = [
9 | 'Just one click and you\'re done',
10 | 'Very little hassle',
11 | 'Just do it! It can be done today, so why wait for tomorrow?'
12 | ]
13 |
14 | describe('Checklist', () => {
15 | describe('default', () => {
16 | const checklist = renderChecklist(
17 | {},
18 | items.map((item, index) =>
19 | renderChecklistItem({ key: index }, item))
20 | )
21 |
22 | it('is "ul"', () => {
23 | equal(checklist.type, 'ul')
24 | })
25 |
26 | it('has class ".cui__checklist"', () => {
27 | equal(checklist.props.className, 'cui__checklist')
28 | })
29 |
30 | items.forEach((item, index) => {
31 | describe(`item #${index}`, () => {
32 | const renderedItem = checklist.props.children[index]
33 |
34 | it('is tag "li"', () => {
35 | equal(renderedItem.type, 'li')
36 | })
37 |
38 | it('has class ".cui__checklist__item"', () => {
39 | equal(renderedItem.props.className, 'cui__checklist__item')
40 | })
41 |
42 | it('has svg icon', () => {
43 | equal(renderedItem.props.children[0].type, 'svg')
44 | })
45 |
46 | it('icon has class "cui__checklist__checkmark"', () => {
47 | equal(renderedItem.props.children[0].props.className, 'cui__checklist__checkmark')
48 | })
49 |
50 | it('has content of the first item', () => {
51 | equal(renderedItem.props.children[1], items[index])
52 | })
53 | })
54 | })
55 | })
56 |
57 | describe('chromeless', () => {
58 | const checklist = renderChecklist({ chromeless: true }, [])
59 |
60 | it('has class chromeless', () => {
61 | ok(checklist.props.className.match('chromeless'))
62 | })
63 | })
64 | })
65 |
--------------------------------------------------------------------------------
/tests/Dialog.spec.jsx:
--------------------------------------------------------------------------------
1 | import Dialog from '../components/Dialog'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 |
5 | const render = renderer(Dialog)
6 | const renderIcon = renderer(Dialog.Icon)
7 | const renderContent = renderer(Dialog.Content)
8 | const renderFooter = renderer(Dialog.Footer)
9 | const renderOverlay = renderer(Dialog.Overlay)
10 |
11 | describe('Dialog', () => {
12 | describe('default', () => {
13 | const dialog = render({})
14 |
15 | it("renders tag 'div'", () => {
16 | equal(dialog.type, 'div')
17 | })
18 |
19 | it("has className 'cui__dialog'", () => {
20 | equal(dialog.props.className, 'cui__dialog')
21 | })
22 | })
23 |
24 | describe('icon', () => {
25 | const dialog = render({}, renderIcon({}, 'X'))
26 | const dialogIcon = dialog.props.children
27 |
28 | it("renders tag 'div'", () => {
29 | equal(dialogIcon.type, 'div')
30 | })
31 |
32 | it("has className 'cui__dialog__icon'", () => {
33 | equal(dialogIcon.props.className, 'cui__dialog__icon')
34 | })
35 |
36 | it('has the content', () => {
37 | ok(dialogIcon.props.children.match('X'))
38 | })
39 | })
40 |
41 | describe('content', () => {
42 | const dialog = render({}, renderContent({}, 'Some text'))
43 | const dialogContent = dialog.props.children
44 | const dialogContentInner = dialogContent.props.children
45 |
46 | it("renders tag 'div'", () => {
47 | equal(dialogContent.type, 'div')
48 | })
49 |
50 | it("has className 'cui__dialog__content'", () => {
51 | equal(dialogContent.props.className, 'cui__dialog__content')
52 | })
53 |
54 | describe('inner', () => {
55 | it("renders tag 'div'", () => {
56 | equal(dialogContentInner.type, 'div')
57 | })
58 |
59 | it("has className 'cui__dialog__content--inner'", () => {
60 | equal(dialogContentInner.props.className, 'cui__dialog__content--inner')
61 | })
62 |
63 | it('has the content', () => {
64 | ok(dialogContentInner.props.children.match('Some text'))
65 | })
66 | })
67 | })
68 |
69 | describe('footer', () => {
70 | const dialog = render({}, renderFooter({}, 'Some text'))
71 | const dialogFooter = dialog.props.children
72 | const dialogFooterInner = dialogFooter.props.children
73 |
74 | it("renders tag 'div'", () => {
75 | equal(dialogFooter.type, 'div')
76 | })
77 |
78 | it("has className 'cui__dialog__footer'", () => {
79 | equal(dialogFooter.props.className, 'cui__dialog__footer')
80 | })
81 |
82 | describe('inner', () => {
83 | it("renders tag 'div'", () => {
84 | equal(dialogFooterInner.type, 'div')
85 | })
86 |
87 | it("has className 'cui__dialog__footer--inner'", () => {
88 | equal(dialogFooterInner.props.className, 'cui__dialog__footer--inner')
89 | })
90 |
91 | it('has the content', () => {
92 | ok(dialogFooterInner.props.children.match('Some text'))
93 | })
94 | })
95 | })
96 |
97 | describe('overlay', () => {
98 | const dialogOverlay = renderOverlay({}, 'Some text')
99 | const dialogTable = dialogOverlay.props.children
100 | const dialogCell = dialogTable.props.children
101 |
102 | it("renders tag 'div'", () => {
103 | equal(dialogOverlay.type, 'div')
104 | })
105 |
106 | it("has className 'cui__dialog__overlay'", () => {
107 | equal(dialogOverlay.props.className, 'cui__dialog__overlay')
108 | })
109 |
110 | describe('table', () => {
111 | it("renders tag 'div'", () => {
112 | equal(dialogTable.type, 'div')
113 | })
114 |
115 | it("has className 'cui__dialog__table'", () => {
116 | equal(dialogTable.props.className, 'cui__dialog__table')
117 | })
118 | })
119 |
120 | describe('cell', () => {
121 | it("renders tag 'div'", () => {
122 | equal(dialogCell.type, 'div')
123 | })
124 |
125 | it("has className 'cui__dialog__cell'", () => {
126 | equal(dialogCell.props.className, 'cui__dialog__cell')
127 | })
128 |
129 | it('has the content', () => {
130 | ok(dialogCell.props.children.match('Some text'))
131 | })
132 | })
133 |
134 | describe('show', () => {
135 | const dialogOverlay = renderOverlay({ show: true })
136 |
137 | it('has class "is-visible"', () => {
138 | ok(dialogOverlay.props.className.match('is-visible'))
139 | })
140 | })
141 | })
142 | })
143 |
--------------------------------------------------------------------------------
/tests/Fieldset.spec.jsx:
--------------------------------------------------------------------------------
1 | import Fieldset from '../components/Fieldset'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 |
5 | const render = renderer(Fieldset)
6 |
7 | describe('Fieldset', () => {
8 | describe('default', () => {
9 | const fieldset = render({}, 'LoremIpsum')
10 |
11 | it("renders tag 'div'", () => {
12 | equal(fieldset.type, 'div')
13 | })
14 |
15 | it("has className 'cui__fieldset'", () => {
16 | equal(fieldset.props.className, 'cui__fieldset')
17 | })
18 |
19 | it('should have the content', () => {
20 | ok(fieldset.props.children.match('LoremIpsum'))
21 | })
22 | })
23 | })
24 |
--------------------------------------------------------------------------------
/tests/Input.spec.jsx:
--------------------------------------------------------------------------------
1 | import Input from '../components/Input'
2 | import assert, { equal } from 'assert'
3 | import { renderer, shallow } from './helpers'
4 |
5 | const render = renderer(Input)
6 |
7 | const label = (input) => input.props.children[1]
8 | const _input = (input) => input.props.children[2]
9 |
10 | describe('Input', () => {
11 | describe('default', () => {
12 | const input = render({ name: 'test', label: 'Test' })
13 |
14 | it('renders cui__input', () => {
15 | equal(input.type, 'div')
16 | equal(input.props.className, 'cui__input')
17 | })
18 |
19 | it('renders a label', () => {
20 | equal(label(input).type, 'label')
21 | equal(label(input).props.children, 'Test')
22 | equal(label(input).props.className, 'cui__input__label')
23 | })
24 |
25 | it('renders an enabled text input', () => {
26 | equal(_input(input).type, 'input')
27 | equal(_input(input).props.value, '')
28 | equal(_input(input).props.name, 'test')
29 | equal(_input(input).props.className, 'cui__input__input')
30 | assert(_input(input).props.disabled === false)
31 | })
32 | })
33 |
34 | describe('sizes', () => {
35 | it("when 'big' has className 'big'", () => {
36 | const input = render({ big: true, name: 'test', label: 'Test' })
37 |
38 | equal(input.props.className, 'cui__input big')
39 | })
40 |
41 | it("when 'giant' has className 'giant'", () => {
42 | const input = render({ giant: true, name: 'test', label: 'Test' })
43 |
44 | equal(input.props.className, 'cui__input giant')
45 | })
46 | })
47 |
48 | describe('filled', () => {
49 | it("when has initial value has className 'is-filled'", () => {
50 | const input = render({ value: 'something', name: 'filled', label: 'Filled' })
51 |
52 | equal(input.props.className, 'cui__input is-filled')
53 | })
54 | })
55 |
56 | describe('focused', () => {
57 | it("has className 'is-focused'", () => {
58 | const renderer = shallow(Input, { name: 'focused', label: 'Focused', focus: true })
59 |
60 | equal(renderer.getRenderOutput().props.className, 'cui__input is-focused')
61 | })
62 | })
63 |
64 | describe('error', () => {
65 | const input = render({ error: true, name: 'filled', label: 'Ooops' })
66 |
67 | it("has className 'is-error'", () => {
68 | equal(input.props.className, 'cui__input is-error')
69 | })
70 |
71 | it('error is in the label', () => {
72 | equal(label(input).props.children, 'Ooops')
73 | })
74 | })
75 |
76 | describe('warning', () => {
77 | const input = render({ warning: true, name: 'filled', label: 'Hey!' })
78 |
79 | it("has className 'is-warning'", () => {
80 | equal(input.props.className, 'cui__input is-warning')
81 | })
82 |
83 | it('warning is in the label', () => {
84 | equal(label(input).props.children, 'Hey!')
85 | })
86 | })
87 |
88 | describe('disabled', () => {
89 | const input = render({ disabled: true, name: 'filled', label: 'Filled' })
90 |
91 | it("has className 'is-disabled'", () => {
92 | equal(input.props.className, 'cui__input is-disabled')
93 | })
94 |
95 | it('input is disabled', () => {
96 | assert(_input(input).props.disabled === true)
97 | })
98 | })
99 | })
100 |
--------------------------------------------------------------------------------
/tests/Label.spec.jsx:
--------------------------------------------------------------------------------
1 | import Label from '../components/Label'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 |
5 | const render = renderer(Label)
6 |
7 | describe('Label', () => {
8 | describe('default', () => {
9 | const label = render({}, 'LoremIpsum')
10 |
11 | it("renders tag 'span'", () => {
12 | equal(label.type, 'span')
13 | })
14 |
15 | it("has className 'cui__label'", () => {
16 | equal(label.props.className, 'cui__label')
17 | })
18 |
19 | it('should have the content', () => {
20 | ok(label.props.children.match('LoremIpsum'))
21 | })
22 | })
23 |
24 | describe('designs', () => {
25 | Label.designs.map((design) => {
26 | const label = render({ design }, 'LoremIpsum')
27 |
28 | it(`has className "${design}" when design is set`, () => {
29 | ok(label.props.className.match(design))
30 | })
31 | })
32 | })
33 |
34 | describe('outline', () => {
35 | const label = render({ outline: true }, 'LoremIpsum')
36 |
37 | it('has className "outline"', () => {
38 | ok(label.props.className.match('outline'))
39 | })
40 | })
41 |
42 | describe('inverted', () => {
43 | const label = render({ inverted: true }, 'LoremIpsum')
44 |
45 | it('has className "inverted"', () => {
46 | ok(label.props.className.match('inverted'))
47 | })
48 | })
49 | })
50 |
--------------------------------------------------------------------------------
/tests/Link.spec.jsx:
--------------------------------------------------------------------------------
1 | import Link from '../components/Link'
2 | import { equal } from 'assert'
3 | import { renderer } from './helpers'
4 |
5 | const render = renderer(Link)
6 |
7 | describe('Link', () => {
8 | describe('default', () => {
9 | const link = render()
10 |
11 | it('renders tag "a"', () => {
12 | equal('a', link.type)
13 | })
14 |
15 | it('has className "cui__link"', () => {
16 | equal('cui__link', link.props.className)
17 | })
18 | })
19 |
20 | it('allows passing custom "className" without overriding defaults', () => {
21 | const link = render({ className: 'custom' })
22 |
23 | equal('cui__link custom', link.props.className)
24 | })
25 |
26 | describe('customize', () => {
27 | const link = render({ customize: { textColor: 'green' } })
28 |
29 | it('has className "dynamic-styling"', () => {
30 | equal('cui__link dynamic-styling', link.props.className)
31 | })
32 |
33 | it('has the correct customized styles', () => {
34 | equal(link.props.style.color, 'green')
35 | })
36 | })
37 |
38 | it('allows more props', () => {
39 | const link = render({ href: 'http://test.com', onClick: () => {} })
40 |
41 | equal('http://test.com', link.props.href)
42 | equal('function', typeof link.props.onClick)
43 | })
44 | })
45 |
--------------------------------------------------------------------------------
/tests/Loader.spec.jsx:
--------------------------------------------------------------------------------
1 | import Loader from '../components/Loader'
2 | import { equal, ok } from 'assert'
3 | import { renderer } from './helpers'
4 |
5 | const render = renderer(Loader)
6 |
7 | describe('Loader', () => {
8 | describe('default', () => {
9 | const loader = render()
10 |
11 | it('renders tag "svg"', () => {
12 | equal('svg', loader.type)
13 | })
14 |
15 | it('has className "loader"', () => {
16 | equal('loader', loader.props.className)
17 | })
18 | })
19 |
20 | it("allows passing custom 'className' without overriding defaults", () => {
21 | const loader = render({ className: 'custom' })
22 |
23 | equal('loader custom', loader.props.className)
24 | })
25 |
26 | describe('inline', () => {
27 | const loader = render({ inline: true })
28 |
29 | it('has class "inline"', () => {
30 | ok(loader.props.className.match('inline'))
31 | })
32 | })
33 |
34 | describe('size', () => {
35 | it('has width 30 when size is "big"', () => {
36 | equal(30, render({ size: 'big' }).props.width)
37 | })
38 |
39 | it('has width 20 when size is default', () => {
40 | equal(20, render().props.width)
41 | })
42 |
43 | it('has width 15 when size is "small"', () => {
44 | equal(15, render({ size: 'small' }).props.width)
45 | })
46 |
47 | it('has width 10 when size is "tiny"', () => {
48 | equal(10, render({ size: 'tiny' }).props.width)
49 | })
50 | })
51 | })
52 |
--------------------------------------------------------------------------------
/tests/Paragraph.spec.jsx:
--------------------------------------------------------------------------------
1 | import Paragraph from '../components/texts/Paragraph'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 | import describePalette from './describePalette'
5 |
6 | const render = renderer(Paragraph)
7 |
8 | describe('Paragraph', () => {
9 | describe('default', () => {
10 | const paragraph = render({}, 'LoremIpsum')
11 |
12 | it("renders tag 'p'", () => {
13 | equal(paragraph.type, 'p')
14 | })
15 |
16 | it("has className 'cui__paragraph--primary'", () => {
17 | ok(paragraph.props.className.match('cui__paragraph--primary'))
18 | })
19 |
20 | it('should have the content', () => {
21 | ok(paragraph.props.children.match('LoremIpsum'))
22 | })
23 | })
24 |
25 | describePalette(render)
26 |
27 | describe('condensed', () => {
28 | const paragraph = render({condensed: true}, 'LoremIpsum')
29 |
30 | it('does have class condensed', () => {
31 | ok(paragraph.props.className.match('condensed'))
32 | })
33 | })
34 |
35 | describe('margins', () => {
36 | const paragraph = render({margins: true}, 'LoremIpsum')
37 |
38 | it("has class 'default-margins'", () => {
39 | ok(paragraph.props.className.match('default-margins'))
40 | })
41 | })
42 |
43 | describe('design:secondary', () => {
44 | const paragraph = render({design: 'secondary'}, 'LoremIpsum')
45 |
46 | it("has className 'cui__paragraph--secondary'", () => {
47 | ok(paragraph.props.className.match('cui__paragraph--secondary'))
48 | })
49 | })
50 |
51 | describe('design:legal', () => {
52 | const paragraph = render({design: 'legal'}, 'LoremIpsum')
53 |
54 | it("has className 'cui__paragraph--legal'", () => {
55 | ok(paragraph.props.className.match('cui__paragraph--legal'))
56 | })
57 | })
58 | })
59 |
--------------------------------------------------------------------------------
/tests/PayButton.spec.jsx:
--------------------------------------------------------------------------------
1 | import PayButton from '../components/PayButton'
2 | import Button from '../components/Button'
3 | import { ok, equal } from 'assert'
4 | import { renderer } from './helpers'
5 |
6 | const render = renderer(PayButton)
7 |
8 | describe('PayButton', () => {
9 | describe('default', () => {
10 | const payButton = render({ price: '10.15' }, 'Click me')
11 |
12 | it('renders element Button', () => {
13 | equal(payButton.type, Button)
14 | })
15 |
16 | it("has class 'has-price'", () => {
17 | equal(payButton.props.className, 'has-price')
18 | })
19 |
20 | describe('loading', () => {
21 | const payButton = render({ price: '10.15', loading: true }, 'Click me')
22 |
23 | it("does not have class 'has-price'", () => {
24 | ok(!payButton.props.className.match('has-price'))
25 | })
26 | })
27 |
28 | describe('price', () => {
29 | it('has the span component inside the button', () => {
30 | equal(payButton.props.children[1].type, 'span')
31 | })
32 |
33 | it('has the cui__button__price component inside the button', () => {
34 | equal(payButton.props.children[1].props.className, 'cui__button__price')
35 | })
36 |
37 | it('has the price as the content of the span', () => {
38 | ok(payButton.props.children[1].props.children.match('10.15'))
39 | })
40 | })
41 | })
42 | })
43 |
--------------------------------------------------------------------------------
/tests/Preview.spec.jsx:
--------------------------------------------------------------------------------
1 | import Preview, { PreviewTitle, PreviewLink } from '../components/Preview'
2 | import { equal } from 'assert'
3 | import { renderer } from './helpers'
4 |
5 | describe('Preview', () => {
6 | const render = renderer(Preview)
7 |
8 | describe('default', () => {
9 | const preview = render()
10 |
11 | it("renders tag 'div'", () => {
12 | equal('div', preview.type)
13 | })
14 |
15 | it("has className 'cui__preview'", () => {
16 | equal('cui__preview', preview.props.className)
17 | })
18 |
19 | it("contains a div with className 'cui__preview__content'", () => {
20 | equal('cui__preview__content', preview.props.children.props.className)
21 | })
22 | })
23 |
24 | it("allows passing custom 'className' without overriding defaults", () => {
25 | const preview = render({ className: 'custom' })
26 |
27 | equal('cui__preview custom', preview.props.className)
28 | })
29 |
30 | it('allows passing children', () => {
31 | const preview = render({}, 'Test')
32 |
33 | equal('Test', preview.props.children.props.children)
34 | })
35 | })
36 |
37 | describe('PreviewTitle', () => {
38 | const render = renderer(PreviewTitle)
39 |
40 | describe('default', () => {
41 | const previewTitle = render()
42 |
43 | it("renders tag 'h2'", () => {
44 | equal('h2', previewTitle.type)
45 | })
46 |
47 | it("has className 'cui__preview__title'", () => {
48 | equal('cui__preview__title', previewTitle.props.className)
49 | })
50 | })
51 |
52 | it("allows passing custom 'className' without overriding defaults", () => {
53 | const previewTitle = render({ className: 'custom' })
54 |
55 | equal('cui__preview__title custom', previewTitle.props.className)
56 | })
57 |
58 | it('allows passing children', () => {
59 | const previewTitle = render({}, 'Test')
60 |
61 | equal('Test', previewTitle.props.children)
62 | })
63 | })
64 |
65 | describe('PreviewLink', () => {
66 | const render = renderer(PreviewLink)
67 | const link = (component) => component.props.children
68 |
69 | describe('default', () => {
70 | const previewLink = render()
71 |
72 | it("renders tag 'div'", () => {
73 | equal('div', previewLink.type)
74 | })
75 |
76 | it("has className 'cui__preview__footer'", () => {
77 | equal('cui__preview__footer', previewLink.props.className)
78 | })
79 |
80 | it('contains link', () => {
81 | equal('a', link(previewLink).type)
82 | equal('cui__preview__footer__link', link(previewLink).props.className)
83 | })
84 | })
85 |
86 | it("allows passing custom 'className' without overriding link defaults", () => {
87 | const previewLink = render({ className: 'custom' })
88 |
89 | equal('cui__preview__footer__link custom', link(previewLink).props.className)
90 | })
91 |
92 | it('allows passing other props to link', () => {
93 | const previewLink = render({ href: '#test', rel: 'test' })
94 |
95 | equal('#test', link(previewLink).props.href)
96 | equal('test', link(previewLink).props.rel)
97 | })
98 |
99 | it('allows passing children', () => {
100 | const previewLink = render({}, 'Test')
101 |
102 | equal('Test', previewLink.props.children.props.children)
103 | })
104 | })
105 |
--------------------------------------------------------------------------------
/tests/PrimaryTitle.spec.jsx:
--------------------------------------------------------------------------------
1 | import PrimaryTitle from '../components/texts/PrimaryTitle'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 | import describePalette from './describePalette'
5 |
6 | const render = renderer(PrimaryTitle)
7 |
8 | describe('PrimaryTitle', () => {
9 | describe('default', () => {
10 | const primaryTitle = render({}, 'LoremIpsum')
11 |
12 | it("renders tag 'h1'", () => {
13 | equal(primaryTitle.type, 'h1')
14 | })
15 |
16 | it("has className 'cui__title--primary'", () => {
17 | ok(primaryTitle.props.className.match('cui__title--primary'))
18 | })
19 |
20 | it('should have the content', () => {
21 | ok(primaryTitle.props.children.match('LoremIpsum'))
22 | })
23 | })
24 |
25 | describePalette(render)
26 |
27 | describe('small', () => {
28 | const primaryTitle = render({small: true}, 'LoremIpsum')
29 |
30 | it('does have class small', () => {
31 | ok(primaryTitle.props.className.match('small'))
32 | })
33 | })
34 |
35 | describe('strong', () => {
36 | const primaryTitle = render({strong: true}, 'LoremIpsum')
37 |
38 | it('does have class strong', () => {
39 | ok(primaryTitle.props.className.match('strong'))
40 | })
41 | })
42 |
43 | describe('margins', () => {
44 | const primaryTitle = render({margins: true}, 'LoremIpsum')
45 |
46 | it("has class 'default-margins'", () => {
47 | ok(primaryTitle.props.className.match('default-margins'))
48 | })
49 | })
50 | })
51 |
--------------------------------------------------------------------------------
/tests/RadioGroup.spec.jsx:
--------------------------------------------------------------------------------
1 | import RadioGroup from '../components/RadioGroup'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 | import { spy } from 'sinon'
5 |
6 | const render = renderer(RadioGroup)
7 | const data = [
8 | {id: 1, label: 'option1', description: 'description1'},
9 | {id: 2, label: 'option2'}
10 | ]
11 |
12 | describe('RadioGroup', () => {
13 | describe('default', () => {
14 | const onChange = spy()
15 | const radioGroup = render({ data, onChange, selected: 2 })
16 | const option = radioGroup.props.children
17 | const label = (option) => option.props.children[0]
18 | const description = (option) => option.props.children[1]
19 |
20 | it("renders tag 'div'", () => {
21 | equal(radioGroup.type, 'div')
22 | })
23 |
24 | it("has className 'cui__dropdown--radio'", () => {
25 | equal(radioGroup.props.className, 'cui__dropdown--radio')
26 | })
27 |
28 | it('first option is not selected', () => {
29 | equal(option[0].props.className, 'cui__dropdown--radio__option')
30 | })
31 |
32 | it('first option has label and description', () => {
33 | equal(label(option[0]).props.className, 'cui__dropdown--radio__option__heading')
34 | equal(label(option[0]).props.children, 'option1')
35 | equal(description(option[0]).props.className, 'cui__dropdown--radio__option__description')
36 | equal(description(option[0]).props.children, 'description1')
37 | })
38 |
39 | it('second option is selected', () => {
40 | equal(option[1].props.className, 'cui__dropdown--radio__option is-selected')
41 | })
42 |
43 | it('second option has label and no description', () => {
44 | equal(label(option[1]).props.className, 'cui__dropdown--radio__option__heading')
45 | equal(label(option[1]).props.children, 'option2')
46 | equal(description(option[1]), undefined)
47 | })
48 |
49 | it('calls onChange callback when options are clicked', () => {
50 | option[0].props.onClick()
51 | ok(onChange.calledWith(1))
52 | option[1].props.onClick()
53 | ok(onChange.calledWith(2))
54 | })
55 | })
56 | })
57 |
--------------------------------------------------------------------------------
/tests/SecondaryTitle.spec.jsx:
--------------------------------------------------------------------------------
1 | import SecondaryTitle from '../components/texts/SecondaryTitle'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 | import describePalette from './describePalette'
5 |
6 | const render = renderer(SecondaryTitle)
7 |
8 | describe('SecondaryTitle', () => {
9 | describe('default', () => {
10 | const secondaryTitle = render({}, 'LoremIpsum')
11 |
12 | it("renders tag 'h2'", () => {
13 | equal(secondaryTitle.type, 'h2')
14 | })
15 |
16 | it("has className 'cui__title--secondary'", () => {
17 | ok(secondaryTitle.props.className.match('cui__title--secondary'))
18 | })
19 |
20 | it('should have the content', () => {
21 | ok(secondaryTitle.props.children.match('LoremIpsum'))
22 | })
23 | })
24 |
25 | describePalette(render)
26 |
27 | describe('condensed', () => {
28 | const secondaryTitle = render({condensed: true}, 'LoremIpsum')
29 |
30 | it('does have class condensed', () => {
31 | ok(secondaryTitle.props.className.match('condensed'))
32 | })
33 | })
34 |
35 | describe('margins', () => {
36 | const secondaryTitle = render({margins: true}, 'LoremIpsum')
37 |
38 | it("has class 'default-margins'", () => {
39 | ok(secondaryTitle.props.className.match('default-margins'))
40 | })
41 | })
42 | })
43 |
--------------------------------------------------------------------------------
/tests/Selector.spec.jsx:
--------------------------------------------------------------------------------
1 | import Selector from '../components/Selector'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 | import { spy } from 'sinon'
5 |
6 | const render = renderer(Selector)
7 | const data = [
8 | {id: 1, label: 'option1'},
9 | {id: 2, label: 'option2'}
10 | ]
11 |
12 | describe('Selector', () => {
13 | describe('default', () => {
14 | const onChange = spy()
15 | const selector = render({ data, onChange, selected: 2 })
16 | const options = selector.props.children
17 | const label = (option) => option.props.children[0]
18 | const icon = (option) => option.props.children[1]
19 |
20 | it("renders tag 'div'", () => {
21 | equal(selector.type, 'div')
22 | })
23 |
24 | it("has className 'cui__selector--direct title'", () => {
25 | equal(selector.props.className, 'cui__selector--direct title')
26 | })
27 |
28 | it('first option is not selected', () => {
29 | equal(icon(options[0]), null)
30 | })
31 |
32 | it('first option has label', () => {
33 | equal(label(options[0]).props.className, 'cui__selector--direct__label')
34 | })
35 |
36 | it('second option is selected', () => {
37 | equal(icon(options[1]).props.className, 'cui__selector--direct__icon')
38 | })
39 |
40 | it('second option has label', () => {
41 | equal(label(options[1]).props.className, 'cui__selector--direct__label')
42 | })
43 |
44 | it('calls onChange callback when options are clicked', () => {
45 | options[0].props.onClick()
46 | ok(onChange.calledWith(data[0]))
47 | options[1].props.onClick()
48 | ok(onChange.calledWith(data[1]))
49 | })
50 | })
51 | })
52 |
--------------------------------------------------------------------------------
/tests/Subtitle.spec.jsx:
--------------------------------------------------------------------------------
1 | import Subtitle from '../components/texts/Subtitle'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 | import describePalette from './describePalette'
5 |
6 | const render = renderer(Subtitle)
7 |
8 | describe('Subtitle', () => {
9 | describe('default', () => {
10 | const subtitle = render({}, 'LoremIpsum')
11 |
12 | it("renders tag 'h3'", () => {
13 | equal(subtitle.type, 'h3')
14 | })
15 |
16 | it("has className 'cui__subtitle'", () => {
17 | ok(subtitle.props.className.match('cui__subtitle'))
18 | })
19 |
20 | it('should have the content', () => {
21 | ok(subtitle.props.children.match('LoremIpsum'))
22 | })
23 | })
24 |
25 | describePalette(render)
26 |
27 | describe('condensed', () => {
28 | const subtitle = render({condensed: true}, 'LoremIpsum')
29 |
30 | it('does have class condensed', () => {
31 | ok(subtitle.props.className.match('condensed'))
32 | })
33 | })
34 |
35 | describe('margins', () => {
36 | const subtitle = render({margins: true}, 'LoremIpsum')
37 |
38 | it("has class 'default-margins'", () => {
39 | ok(subtitle.props.className.match('default-margins'))
40 | })
41 | })
42 | })
43 |
--------------------------------------------------------------------------------
/tests/TextLabel.spec.jsx:
--------------------------------------------------------------------------------
1 | import TextLabel from '../components/texts/TextLabel'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 |
5 | const render = renderer(TextLabel)
6 |
7 | describe('TextLabel', () => {
8 | describe('default', () => {
9 | const textLabel = render({}, 'LoremIpsum')
10 |
11 | it("renders tag 'h4'", () => {
12 | equal(textLabel.type, 'h4')
13 | })
14 |
15 | it("has className 'cui__text-label'", () => {
16 | equal(textLabel.props.className, 'cui__text-label')
17 | })
18 |
19 | it('should have the content', () => {
20 | ok(textLabel.props.children.match('LoremIpsum'))
21 | })
22 | })
23 |
24 | describe('margins', () => {
25 | const textLabel = render({margins: true}, 'LoremIpsum')
26 |
27 | it("has class 'default-margins'", () => {
28 | ok(textLabel.props.className.match('default-margins'))
29 | })
30 | })
31 | })
32 |
--------------------------------------------------------------------------------
/tests/Tooltip.spec.jsx:
--------------------------------------------------------------------------------
1 | import Tooltip from '../components/Tooltip'
2 | import { ok, equal } from 'assert'
3 | import { renderer } from './helpers'
4 |
5 | const render = renderer(Tooltip)
6 |
7 | describe('Tooltip', () => {
8 | describe('default', () => {
9 | const tooltip = render({}, 'Lorem ipsum')
10 |
11 | it("renders tag 'div'", () => {
12 | equal(tooltip.type, 'div')
13 | })
14 |
15 | it("has className 'cui__tooltip'", () => {
16 | equal(tooltip.props.className, 'cui__tooltip')
17 | })
18 |
19 | it('should have the content', () => {
20 | ok(tooltip.props.children.match('Lorem ipsum'))
21 | })
22 | })
23 |
24 | describe('arrows', () => {
25 | Tooltip.arrows.forEach((arrow) => {
26 | describe(arrow, () => {
27 | const tooltip = render({ arrow }, 'Toggle me')
28 |
29 | it(`has className '${arrow}'`, () => {
30 | ok(tooltip.props.className.match(arrow))
31 | })
32 | })
33 | })
34 | })
35 |
36 | describe('border', () => {
37 | const tooltip = render({ border: true }, 'Lorem ipsum')
38 |
39 | it("has className 'cui__tooltip'", () => {
40 | ok(tooltip.props.className.match('border'))
41 | })
42 | })
43 | })
44 |
--------------------------------------------------------------------------------
/tests/decorators/statefulFocus.spec.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import statefulFocus from '../../lib/decorators/statefulFocus'
3 | import { shallow } from '../helpers'
4 | import { equal } from 'assert'
5 |
6 | class TestComponent extends Component {
7 | render () {
8 | return (
9 |
14 | )
15 | }
16 | }
17 |
18 | describe('statefulFocus', () => {
19 | const DecoratedComponent = statefulFocus(TestComponent)
20 |
21 | describe('onBlur', () => {
22 | it('set focus to false in the inner component', () => {
23 | const decorated = shallow(
24 | DecoratedComponent,
25 | { onBlur: () => {}, onFocus: () => {} }
26 | )
27 |
28 | decorated.getRenderOutput().props.onBlur({ target: { value: 'foo' } })
29 |
30 | equal(decorated.getRenderOutput().props.focus, undefined)
31 | })
32 |
33 | it('update the state focus value', () => {
34 | const decorated = shallow(
35 | DecoratedComponent,
36 | { onBlur: () => {}, onFocus: () => {} }
37 | )
38 |
39 | decorated.getRenderOutput().props.onBlur({ target: { value: 'foo' } })
40 |
41 | equal(decorated.getMountedInstance().state.focus, undefined)
42 | })
43 | })
44 |
45 | describe('onFocus', () => {
46 | it('set focus to true in the inner component', () => {
47 | const decorated = shallow(
48 | DecoratedComponent,
49 | { onBlur: () => {}, onFocus: () => {} }
50 | )
51 |
52 | decorated.getRenderOutput().props.onFocus({ target: { value: 'foo' } })
53 |
54 | equal(decorated.getRenderOutput().props.focus, true)
55 | })
56 |
57 | it('update the state focus value', () => {
58 | const decorated = shallow(
59 | DecoratedComponent,
60 | { onBlur: () => {}, onFocus: () => {} }
61 | )
62 |
63 | decorated.getRenderOutput().props.onFocus({ target: { value: 'foo' } })
64 |
65 | equal(decorated.getMountedInstance().state.focus, true)
66 | })
67 | })
68 | })
69 |
--------------------------------------------------------------------------------
/tests/decorators/statefulValue.spec.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import statefulValue from '../../lib/decorators/statefulValue'
3 | import { shallow } from '../helpers'
4 | import { equal } from 'assert'
5 |
6 | class TestComponent extends Component {
7 | render () {
8 | return (
9 |
13 | )
14 | }
15 | }
16 |
17 | describe('statefulValue', () => {
18 | const DecoratedComponent = statefulValue(TestComponent)
19 |
20 | describe('onChange', () => {
21 | it('update the value of the inner component', () => {
22 | const decorated = shallow(
23 | DecoratedComponent,
24 | { onChange: () => { } }
25 | )
26 |
27 | decorated.getRenderOutput().props.onChange({ target: { value: 'foo' } })
28 |
29 | equal(decorated.getRenderOutput().props.value, 'foo')
30 | })
31 |
32 | it('update the state value', () => {
33 | const decorated = shallow(
34 | DecoratedComponent,
35 | { onChange: () => { } }
36 | )
37 |
38 | decorated.getRenderOutput().props.onChange({ target: { value: 'foo' } })
39 |
40 | equal(decorated.getMountedInstance().state.value, 'foo')
41 | })
42 | })
43 | })
44 |
--------------------------------------------------------------------------------
/tests/describePalette.es6:
--------------------------------------------------------------------------------
1 | import { ok } from 'assert'
2 | import palette from '../components/texts/palette'
3 |
4 | export default (render) => {
5 | describe('palette', () => {
6 | palette.map((color) => {
7 | it(color, () => {
8 | ok(render({ color }).props.className.match(color))
9 | })
10 | })
11 | })
12 | }
13 |
--------------------------------------------------------------------------------
/tests/helpers.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createRenderer } from 'react-addons-test-utils'
3 |
4 | export const renderer = (Component) => (props = {}, children = null) => (
5 | shallow(Component, props, children).getRenderOutput()
6 | )
7 |
8 | export const shallow = (Component, props = {}, children = null) => {
9 | const shallowRenderer = createRenderer()
10 | shallowRenderer.render({children} )
11 | return shallowRenderer
12 | }
13 |
14 | export const instance = (Component) => (props = {}, children = null) => (
15 | shallow(Component, props, children)._instance._instance
16 | )
17 |
--------------------------------------------------------------------------------
/tests/propTypes/fieldSizeFraction.spec.es6:
--------------------------------------------------------------------------------
1 | import fieldSizeFraction, { FIELD_SIZE_ERRORS } from '../../propTypes/fieldSizeFraction'
2 | import { equal } from 'assert'
3 |
4 | describe('fieldSizeFraction', () => {
5 | describe('random text', () => {
6 | it('returns an error', () => (
7 | equal(
8 | fieldSizeFraction(3)({ fraction: 'asdfa' }, 'fraction', 'Field').message,
9 | "Invalid prop `fraction` supplied to `Field`: needs to be a fraction, such as `1/3`"
10 | )
11 | ))
12 | })
13 |
14 | describe('floats in the values', () => {
15 | it('returns an error', () => (
16 | equal(
17 | fieldSizeFraction(3)({ fraction: '1.3/6' }, 'fraction', 'Field').message,
18 | "Invalid prop `fraction` supplied to `Field`: fraction values need to be positive integers"
19 | )
20 | ))
21 | })
22 |
23 | describe('denominator above threshold', () => {
24 | it('returns an error', () => (
25 | equal(
26 | fieldSizeFraction(3)({ fraction: '1/5' }, 'fraction', 'Field').message,
27 | "Invalid prop `fraction` supplied to `Field`: values needs to be lower or equal to `3`"
28 | )
29 | ))
30 | })
31 |
32 | describe('numerator above denominator', () => {
33 | it('returns an error', () => (
34 | equal(
35 | fieldSizeFraction(3)({ fraction: '3/2' }, 'fraction', 'Field').message,
36 | "Invalid prop `fraction` supplied to `Field`: numerator needs to be lower or equal to the denominator (`2` in this case)"
37 | )
38 | ))
39 | })
40 |
41 | describe('default', () => {
42 | it('returns null', () => (
43 | equal(
44 | fieldSizeFraction(3)({ fraction: '2/3' }, 'fraction', 'Field'),
45 | null
46 | )
47 | ))
48 | })
49 | })
50 |
--------------------------------------------------------------------------------
/tests/propTypes/validateSize.spec.es6:
--------------------------------------------------------------------------------
1 | import validateSize from '../../propTypes/validateSize'
2 | import assert, { ok } from 'assert'
3 |
4 | const invalidSizes = [1, {}, [], '1.', '', 'px', '1p', '%', 'wrong', 'em2']
5 | const validSizes = [undefined, '0', '5px', '100%', '3.5em', '12rem', '0ex', '7.55cm', '300mm', '2in', '10pt', '3pc', '11ch', '77vh', '0.4vw', '.1vmin', '.2vmax']
6 |
7 | describe('validateSize', () => {
8 | invalidSizes.map((invalid) => {
9 | it(`rejects invalid size '${invalid}'`, () => {
10 | ok(validateSize({size: invalid}, 'size', 'Test') instanceof Error)
11 | assert.deepEqual(
12 | validateSize({size: invalid}, 'size', 'Test').message,
13 | `size '${invalid}' in Test is not valid.`
14 | )
15 | })
16 | })
17 |
18 | validSizes.map((valid) => {
19 | it(`accepts valid size '${valid}'`, () => {
20 | assert(validateSize({size: valid}, 'size') === null)
21 | })
22 | })
23 | })
24 |
--------------------------------------------------------------------------------
/tests/uncontrolled/RadioGroup.spec.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | * The default behaviour here is defined by
3 | * RadioGroup, so the tests focus on
4 | * the state of the object
5 | */
6 |
7 | import StatefulRadioGroup from '../../components/uncontrolled/RadioGroup'
8 | import { equal } from 'assert'
9 | import { shallow } from '../helpers'
10 |
11 | const data = [
12 | {id: 1, label: 'option1'},
13 | {id: 2, label: 'option2'}
14 | ]
15 |
16 | const renderer = shallow(StatefulRadioGroup, { data, name: 'lorem' })
17 | const radioGroup = () => renderer.getRenderOutput().props.children[0]
18 | const input = () => renderer.getRenderOutput().props.children[1]
19 |
20 | describe('StatefulRadioGroup', () => {
21 | it("changes the radio group selected when it's changed", () => {
22 | equal(radioGroup().props.selected, 1)
23 | equal(input().props.value, 1)
24 | radioGroup().props.onChange(2)
25 | equal(radioGroup().props.selected, 2)
26 | equal(input().props.value, 2)
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var Webpack = require('webpack')
3 | var WebpackHtmlWebpackPlugin = require('html-webpack-plugin')
4 | var WebpackErrorNotificationPlugin = require('webpack-error-notification')
5 |
6 | module.exports = {
7 | cache: true,
8 | debug: true,
9 | devtool: 'source-map',
10 | entry: {
11 | example: './example/index'
12 | },
13 | output: {
14 | path: './',
15 | filename: '[name].js',
16 | chunkFilename: '[id].js',
17 | publicPath: '/'
18 | },
19 | module: {
20 | loaders: [
21 | {
22 | test: /\.(jsx|es6)$/,
23 | loader: 'babel'
24 | },
25 | {
26 | test: /\.scss$/,
27 | loaders: [
28 | 'style',
29 | 'css?modules,localIdentName=[local]',
30 | 'sass'
31 | ]
32 | },
33 | {
34 | test: /\.css$/,
35 | loaders: [
36 | 'style',
37 | 'css?modules',
38 | 'autoprefixer'
39 | ]
40 | },
41 | {
42 | test: /\.(jpe?g|png|gif|svg|ico|eot|woff|ttf|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/i,
43 | loader: 'file'
44 | }
45 |
46 | ]
47 | },
48 | plugins: [
49 | new WebpackErrorNotificationPlugin(),
50 | new Webpack.NoErrorsPlugin(),
51 | new WebpackHtmlWebpackPlugin({
52 | title: 'UI React components example',
53 | template: 'example/index.html'
54 | })
55 | ],
56 | resolveLoader: {
57 | fallback: [path.join(__dirname, 'node_modules')]
58 | },
59 | resolve: {
60 | fallback: [path.join(__dirname, 'node_modules')],
61 | modulesDirectories: [
62 | './node_modules'
63 | ],
64 | root: path.join(__dirname),
65 | extensions: ['', '.js', '.jsx', '.es6']
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/webpack.config.test.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | var path = require('path')
3 | var devServerPort = 3808
4 | var devServerHostname = '127.0.0.1'
5 |
6 | var config = {
7 | output: {
8 | filename: 'test.build.js',
9 | path: 'tests/',
10 | publicPath: 'http://' + devServerHostname + ':' + devServerPort + '/tests'
11 | },
12 | resolve: {
13 | fallback: [path.join(__dirname, 'node_modules')],
14 | extensions: ['', '.js', '.jsx', '.es6'],
15 | root: path.join(__dirname)
16 | },
17 | resolveLoader: {
18 | fallback: [path.join(__dirname, 'node_modules')]
19 | },
20 | module: {
21 | loaders: [
22 | {
23 | test: /\.(jsx|es6)$/,
24 | loader: 'babel'
25 | },
26 | {
27 | test: /\.scss$/,
28 | loaders: [
29 | 'style',
30 | 'css?modules,localIdentName=[local]',
31 | 'sass'
32 | ]
33 | },
34 | {
35 | test: /\.(jpe?g|png|gif|svg|ico|eot|woff|ttf|woff2)(\?v=[0-9]\.[0-9]\.[0-9])?$/i,
36 | loader: 'null'
37 | }
38 | ]
39 | },
40 | devServer: {
41 | host: devServerHostname,
42 | port: devServerPort
43 | }
44 | }
45 |
46 | module.exports = config
47 |
--------------------------------------------------------------------------------