├── .nvmrc
├── .eslintignore
├── examples
├── favicon.ico
├── multiple.js
├── basic.js
├── styling.js
├── optgroups.js
├── index.html
└── assets
│ └── main.css
├── .npmignore
├── bin
└── deploy.sh
├── .editorconfig
├── .githooks
├── deploy
└── pre-commit
├── webpack
├── webpack.config.prod.js
└── webpack.config.dev.js
├── .travis.yml
├── .gitignore
├── CHANGELOG.md
├── lib
├── index.js
├── __snapshots__
│ └── index.test.js.snap
└── index.test.js
├── README.md
└── package.json
/.nvmrc:
--------------------------------------------------------------------------------
1 | 8
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.min.js
3 | coverage/
4 | dist/
5 | pages
6 |
--------------------------------------------------------------------------------
/examples/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/springload/react-simpler-select/HEAD/examples/favicon.ico
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | coverage
2 | server
3 | .babelrc
4 | examples
5 | docs
6 | lib
7 | .npmignore
8 | webpack
9 | .nvmrc
10 | .travis.yml
11 | .eslintignore
12 | .eslintrc
13 | .editorconfig
14 | .githooks
15 | .github
16 | bin
17 | .env
18 | pages
19 | webpack-stats.json
20 | *.test.js
21 |
--------------------------------------------------------------------------------
/bin/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # From the project's root.
4 | # First make sure your master is up to date.
5 | # Then push the new changes
6 | git checkout -B gh-pages
7 | git add -f pages
8 | git commit -am "Rebuild website" --no-verify
9 | git filter-branch -f --prune-empty --subdirectory-filter pages
10 | git push -f origin gh-pages
11 | git checkout -
12 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Defines the coding style for different editors and IDEs.
2 | # http://editorconfig.org
3 |
4 | # top-most EditorConfig file
5 | root = true
6 |
7 | # Rules for source code.
8 | [*]
9 | charset = utf-8
10 | end_of_line = lf
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_style = space
14 | indent_size = 2
15 |
16 | # Rules for tool configuration.
17 | [{package.json,*.yml, *.yaml}]
18 | indent_size = 2
19 |
20 | # Rules for markdown documents.
21 | [*.md]
22 | trim_trailing_whitespace = false
23 |
--------------------------------------------------------------------------------
/.githooks/deploy:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # https://gist.github.com/apexskier/efb7c1aaa6e77e8127a8
4 | # Deploy hooks stored in your git repo to everyone!
5 | #
6 | # I keep this in $ROOT/$HOOK_DIR/deploy
7 | # From the top level of your git repo, run ./hook/deploy (or equivalent) after
8 | # cloning or adding a new hook.
9 | # No output is good output.
10 |
11 | BASE=`git rev-parse --git-dir`
12 | ROOT=`git rev-parse --show-toplevel`
13 | HOOK_DIR=.githooks
14 | HOOKS=$ROOT/$HOOK_DIR/*
15 |
16 | if [ ! -d "$ROOT/$HOOK_DIR" ]
17 | then
18 | echo "Couldn't find hooks dir."
19 | exit 1
20 | fi
21 |
22 | # Clean up existing hooks.
23 | rm -f $BASE/hooks/*
24 |
25 | # Synlink new hooks.
26 | for HOOK in $HOOKS
27 | do
28 | (cd $BASE/hooks ; ln -s $HOOK `basename $HOOK` || echo "Failed to link $HOOK to `basename $HOOK`.")
29 | done
30 |
31 | echo "Hooks deployed to $BASE/hooks."
32 | exit 0
33 |
--------------------------------------------------------------------------------
/webpack/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const config = require('./webpack.config.dev');
4 |
5 | config.watch = false;
6 | config.devtool = false;
7 | config.output.path = path.join(__dirname, '..', 'pages', 'assets');
8 |
9 | config.plugins = [
10 | new webpack.DefinePlugin({
11 | 'process.env': {
12 | NODE_ENV: JSON.stringify('production'),
13 | },
14 | }),
15 | new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.bundle.js' }),
16 | new webpack.optimize.UglifyJsPlugin({
17 | compress: {
18 | screw_ie8: true, // React doesn't support IE8
19 | warnings: false,
20 | },
21 | mangle: {
22 | screw_ie8: true,
23 | },
24 | output: {
25 | comments: false,
26 | screw_ie8: true,
27 | },
28 | }),
29 | ];
30 |
31 | module.exports = config;
32 |
--------------------------------------------------------------------------------
/.githooks/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Check if this is the initial commit
4 | if git rev-parse --verify HEAD >/dev/null 2>&1
5 | then
6 | against=HEAD
7 | else
8 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
9 | fi
10 |
11 | # Use git diff-index to check for whitespace errors
12 | if ! git diff-index --check --cached $against
13 | then
14 | echo "Aborting commit due to whitespace errors."
15 | exit 1
16 | else
17 | # Fail on first line that fails.
18 | set -e
19 |
20 | NEW_FILES=$(git --no-pager diff --name-only --cached --diff-filter=d)
21 | JS_FILES=$(echo "$NEW_FILES" | { grep .js$ || true; })
22 | SNAPSHOT_FILES=$(echo "$NEW_FILES" | { grep .snap$ || true; })
23 |
24 | if [ -n "$JS_FILES" ];
25 | then
26 | npm run linter:js -s -- $JS_FILES
27 | fi
28 |
29 | if [ -n "$JS_FILES" ] || [ -n "$SNAPSHOT_FILES" ];
30 | then
31 | npm run test:coverage -s
32 | fi
33 |
34 | exit 0
35 | fi
36 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | cache:
4 | directories:
5 | - node_modules
6 | script:
7 | - npm run test:ci
8 | after_success:
9 | - npm run coveralls
10 | branches:
11 | except:
12 | - gh-pages
13 | notifications:
14 | email: false
15 | slack:
16 | rooms:
17 | secure: cBEjOYxg1h7ya34vUPwJKkEeNdu4uI9Vw1oSdCZg+6TXAD65lUfWeHfTjB5i2C2YiWNoDiaf8W4BLF1C8gV1VBqHsoIKsLIBkCE7wVORjgDaSUbDsHkiHWOyroGi4l1fxvT6r0u/VexwjL6i25lxEKMSvi0Ou31mn1jo8fE6kmqwY/dCRCXciiE3aAv37vKKoG6ITz30E+uG0KnTeBCsDxt4IO9NWqz6aeGsxw+K5lV3QruDPnCDm//naXZKgiJL4AZe4/0GeRP4j2cNRNEXXZzKG+DjwimNcxco2mIcvugUIcC0GmCHbReGUXzDaxcraWrjLITS9ls5ktXVW4EUXdwV2kjDfYDsCABSk2Mz1CS6a8PmSLV/znUEziEwHhQrun7qX56Nh02CDxGhVB3W4ZO6M78sFaipqibOjWqMpo9rpgXveZIno2xkWE9AA6eH6/eSfN1nf/Im6yDQEjX5/uIkdrY9rMSLReYdsZHsGiU5OGNUPfyS3u8JIb9KzftIP4p3iVaPSLNc0hDJWqzkNhFV/R/wGVElplN+1dNSqkfbV6C9OwmLpDh21dVNmOP13Yu8RnFeZmvKMiQqYnj3mxLUvBj+sJkWLFYd8/Kz/8m3gLN2YioGI2F5MCwW6u21OHi78KuB+B4jA303DDMYovjXy4dCSykIVFoqCBaqQjA=
18 |
--------------------------------------------------------------------------------
/examples/multiple.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import Select from '../lib/index';
4 |
5 | const options = [
6 | { value: 'es', label: 'Spanish' },
7 | { value: 'cs', label: 'Czech' },
8 | { value: 'en', label: 'English' },
9 | { value: 'fi', label: 'Finnish' },
10 | { value: 'fr', label: 'French' },
11 | ];
12 |
13 | class App extends React.Component {
14 | constructor(props) {
15 | super(props);
16 |
17 | this.state = {
18 | values: [''],
19 | };
20 |
21 | this.handleChange = this.handleChange.bind(this);
22 | }
23 |
24 | handleChange(newValues) {
25 | this.setState({
26 | values: newValues,
27 | });
28 | }
29 |
30 | render() {
31 | const { values } = this.state;
32 |
33 | return (
34 |
40 | );
41 | }
42 | }
43 |
44 | ReactDOM.render(, document.querySelector('[data-mount-multiple]'));
45 |
--------------------------------------------------------------------------------
/examples/basic.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import Select from '../lib/index';
4 |
5 | const options = [
6 | { value: 'es', label: 'Spanish' },
7 | { value: 'cs', label: 'Czech' },
8 | { value: 'en', label: 'English' },
9 | { value: 'fi', label: 'Finnish' },
10 | { value: 'fr', label: 'French' },
11 | ];
12 |
13 | class App extends React.Component {
14 | constructor(props) {
15 | super(props);
16 |
17 | this.state = {
18 | value: '',
19 | };
20 |
21 | this.handleChange = this.handleChange.bind(this);
22 | }
23 |
24 | handleChange(newValue) {
25 | this.setState({
26 | value: newValue,
27 | });
28 | }
29 |
30 | render() {
31 | const { value } = this.state;
32 |
33 | return (
34 |
42 | );
43 | }
44 | }
45 |
46 | ReactDOM.render(, document.querySelector('[data-mount-basic]'));
47 |
--------------------------------------------------------------------------------
/examples/styling.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import Select from '../lib/index';
4 |
5 | const options = [
6 | { label: 'C', value: 'c' },
7 | { label: 'C#', value: 'cs' },
8 | { label: 'C++', value: 'cpp' },
9 | { label: 'Clojure', value: 'clojure' },
10 | { label: 'Elm', value: 'elm' },
11 | { label: 'Go', value: 'go' },
12 | { label: 'Haskell', value: 'haskell' },
13 | { label: 'Java', value: 'java' },
14 | { label: 'Javascript', value: 'javascript' },
15 | { label: 'Perl', value: 'perl' },
16 | { label: 'PHP', value: 'php' },
17 | { label: 'Python', value: 'python' },
18 | { label: 'Ruby', value: 'ruby' },
19 | { label: 'Scala', value: 'scala' },
20 | ];
21 |
22 | // eslint-disable-next-line no-console
23 | const onChange = console.log.bind(console);
24 |
25 | const select = (
26 |
27 |
34 |
35 | );
36 |
37 | const mount = document.querySelector('[data-mount-styling]');
38 |
39 | ReactDOM.render(select, mount);
40 |
--------------------------------------------------------------------------------
/examples/optgroups.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import Select from '../lib/index';
4 |
5 | const optgroups = [
6 | {
7 | title: 'North Island',
8 | id: 'north-island',
9 | options: [
10 | { value: 'auckland-region', label: 'Auckland Region' },
11 | { value: 'bay-of-plenty', label: 'Bay of Plenty' },
12 | { value: 'gisborne', label: 'Gisborne' },
13 | { value: 'hawkes-bay', label: 'Hawke\'s Bay' },
14 | { value: 'manawatu-whanganui', label: 'Manawatu / Whanganui' },
15 | { value: 'northland', label: 'Northland' },
16 | { value: 'taranaki', label: 'Taranaki' },
17 | { value: 'waikato', label: 'Waikato' },
18 | { value: 'wairarapa', label: 'Wairarapa' },
19 | { value: 'wellington-region', label: 'Wellington Region' },
20 | ],
21 | },
22 | {
23 | title: 'South Island',
24 | id: 'south-island',
25 | options: [
26 | { value: 'central-otago', label: 'Central Otago' },
27 | { value: 'marlborough', label: 'Marlborough' },
28 | { value: 'nelson', label: 'Nelson' },
29 | { value: 'north-canterbury', label: 'North Canterbury' },
30 | { value: 'otago', label: 'Otago' },
31 | { value: 'south-canterbury', label: 'South Canterbury' },
32 | { value: 'southland', label: 'Southland' },
33 | { value: 'tasman-golden-bay', label: 'Tasman / Golden Bay' },
34 | { value: 'west-coast', label: 'West Coast' },
35 | ],
36 | },
37 | ];
38 |
39 | // eslint-disable-next-line no-console
40 | const onChange = console.log.bind(console);
41 |
42 | const select = (
43 |
49 | );
50 |
51 | const mount = document.querySelector('[data-mount-optgroups]');
52 |
53 | ReactDOM.render(select, mount);
54 |
--------------------------------------------------------------------------------
/webpack/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 |
4 | const stats = {
5 | // Add chunk information (setting this to `false` allows for a less verbose output)
6 | chunks: false,
7 | // Add the hash of the compilation
8 | hash: false,
9 | // `webpack --colors` equivalent
10 | colors: true,
11 | // Add information about the reasons why modules are included
12 | reasons: false,
13 | // Add webpack version information
14 | version: false,
15 | // Set the maximum number of modules to be shown
16 | maxModules: 0,
17 | };
18 |
19 | module.exports = {
20 | // See http://webpack.github.io/docs/configuration.html#devtool
21 | devtool: 'inline-source-map',
22 | entry: {
23 | vendor: ['react', 'react-dom', 'prop-types'],
24 | basic: './examples/basic',
25 | optgroups: './examples/optgroups',
26 | styling: './examples/styling',
27 | multiple: './examples/multiple',
28 | },
29 | output: {
30 | path: path.join(__dirname, '..', 'build'),
31 | filename: '[name].bundle.js',
32 | publicPath: '/assets/',
33 | },
34 | plugins: [
35 | new webpack.HotModuleReplacementPlugin(),
36 | new webpack.NoEmitOnErrorsPlugin(),
37 | new webpack.DefinePlugin({
38 | 'process.env': {
39 | NODE_ENV: JSON.stringify('development'),
40 | },
41 | }),
42 | new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.bundle.js' }),
43 | ],
44 | module: {
45 | rules: [
46 | {
47 | test: /\.js$/,
48 | use: ['babel-loader'],
49 | exclude: /node_modules/,
50 | },
51 | ],
52 | },
53 | stats: stats,
54 | // Some libraries import Node modules but don't use them in the browser.
55 | // Tell Webpack to provide empty mocks for them so importing them works.
56 | node: {
57 | fs: 'empty',
58 | net: 'empty',
59 | tls: 'empty',
60 | },
61 | devServer: {
62 | contentBase: path.join(__dirname, '..', 'examples'),
63 | compress: true,
64 | hot: true,
65 | port: 4000,
66 | overlay: true,
67 | stats: stats,
68 | },
69 | };
70 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # -------------------------------------------------
4 | # OS files
5 | # -------------------------------------------------
6 | .DS_Store
7 | .DS_Store?
8 | ._*
9 | .Spotlight-V100
10 | .Trashes
11 | ehthumbs.db
12 | Thumbs.db
13 |
14 | # -------------------------------------------------
15 | # Logs and databases
16 | # -------------------------------------------------
17 | logs
18 | *.log
19 | npm-debug.log*
20 | *.sql
21 | *.sqlite3
22 |
23 | # -------------------------------------------------
24 | # Runtime data and caches
25 | # -------------------------------------------------
26 | pids
27 | *.pid
28 | *.seed
29 | *.pyc
30 | *.pyo
31 | *.pot
32 |
33 | # -------------------------------------------------
34 | # Instrumentation and tooling
35 | # -------------------------------------------------
36 | lib-cov
37 | coverage
38 | .coverage
39 | .grunt
40 | .bundle
41 | docs/pattern-library/index.html
42 | webpack-stats.json
43 |
44 | # -------------------------------------------------
45 | # Dependency directories
46 | # -------------------------------------------------
47 | node_modules*
48 | python_modules*
49 | bower_components
50 | .venv
51 | venv
52 | $virtualenv.tar.gz
53 | $node_modules.tar.gz
54 |
55 | # -------------------------------------------------
56 | # Users Environment
57 | # -------------------------------------------------
58 | .lock-wscript
59 | .idea
60 | .installed.cfg
61 | .vagrant
62 | .anaconda
63 | Vagrantfile.local
64 | .env
65 | /local
66 | local.py
67 | *.sublime-project
68 | *.sublime-workspace
69 | .vscode
70 |
71 | # -------------------------------------------------
72 | # Generated files
73 | # -------------------------------------------------
74 | dist
75 | es
76 | build
77 | /var/static/
78 | /var/media/
79 | /docs/_build/
80 | develop-eggs
81 | *.egg-info
82 | downloads
83 | media
84 | eggs
85 | parts
86 | lib64
87 | .sass-cache
88 |
89 | # -------------------------------------------------
90 | # Your own project's ignores
91 | # -------------------------------------------------
92 | core/static/js
93 | core/static/css
94 | core/svg.html
95 | core/static
96 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | > All notable changes to this project are documented in this file. This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
4 |
5 | ## Unreleased
6 |
7 | ## [[v3.0.0]](https://github.com/springload/react-simpler-select/releases/tag/v3.0.0)
8 |
9 | ### Added
10 |
11 | - Add `prop-types` as peerDependency ([#26](https://github.com/springload/react-simpler-select/pull/26)).
12 | - Add ES module target under `pkg.module` field to enable tree shaking.
13 |
14 | ### Changed
15 |
16 | - Convert all code to be compatible with React 15.5 and React 16 ([#26](https://github.com/springload/react-simpler-select/pull/26)).
17 | - Convert to stateless functional component for file size.
18 |
19 | ### Removed
20 |
21 | - Remove animationend vendor prefixing.
22 | - Remove support for React 0.14.7, 0.14.8, 15.1, 15.2.
23 | - Remove UMD build.
24 | - Start compiling only for latest browser versions and IE11.
25 |
26 | ## [[v2.0.1]](https://github.com/springload/react-simpler-select/releases/tag/v2.0.1)
27 |
28 | ### Added
29 |
30 | - Supports multi select (HTML `multiple` attribute).
31 |
32 | ### Changed
33 |
34 | - displayName is now `Select`.
35 |
36 | ## [[v2.0.0]](https://github.com/springload/react-simpler-select/releases/tag/v2.0.0)
37 |
38 | ### Added
39 |
40 | - `options` are now required (the array can be empty)
41 | - Supports React 15.2
42 |
43 | ### Fixed
44 |
45 | - `unknown-prop` warning with React 15.2 (https://github.com/springload/react-simpler-select/issues/23)
46 |
47 | ### Removed
48 |
49 | - Removed unused key for placeholder option (keys should not be relied upon anyway)
50 |
51 | ## [[v1.1.0]](https://github.com/springload/react-simpler-select/releases/tag/v1.1.0)
52 |
53 | ### Added
54 |
55 | - Support for React 15
56 |
57 | ## [[v1.0.0]](https://github.com/springload/react-simpler-select/releases/tag/v1.0.0)
58 |
59 | First stable release!
60 |
61 | -------------
62 |
63 | ## [[x.y.z]](https://github.com/springload/react-simpler-select/releases/tag/x.y.z) (Template: http://keepachangelog.com/)
64 |
65 | ### Added
66 |
67 | - Something was added to the API / a new feature was introduced.
68 |
69 | ### Changed
70 |
71 | ### Fixed
72 |
73 | ### Removed
74 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | // Filters props to remove before transmission to the select element.
5 | const filterProps = (props) => {
6 | return Object.keys(props)
7 | .filter(k => ['onChange', 'options', 'placeholder'].indexOf(k) === -1)
8 | .reduce((filtered, k) => {
9 | // eslint-disable-next-line no-param-reassign
10 | filtered[k] = props[k];
11 | return filtered;
12 | }, {});
13 | };
14 |
15 | const renderOption = option => (
16 |
23 | );
24 |
25 | const renderOptions = (options, placeholder) => {
26 | const children = options.map((item) => {
27 | let child;
28 |
29 | if (item.options) {
30 | child = (
31 |
34 | );
35 | } else {
36 | child = renderOption(item);
37 | }
38 |
39 | return child;
40 | });
41 |
42 | if (placeholder) {
43 | children.unshift(renderOption({ label: placeholder, value: '', disabled: true }));
44 | }
45 |
46 | return children;
47 | };
48 |
49 | const handleChange = (multiple, onChange, e) => {
50 | let value;
51 |
52 | if (multiple) {
53 | value = Array.prototype.slice.call(e.target.options)
54 | .filter(option => option.selected)
55 | .map(option => option.value);
56 | } else {
57 | value = e.target.value;
58 | }
59 |
60 | onChange(value);
61 | };
62 |
63 | /**
64 | * The Select component. Renders to a plain HTML