Merge branch 'develop' of https://github.com/jshjohnson/Choices into develop
24
.eslintrc
|
@ -1,32 +1,24 @@
|
|||
{
|
||||
"extends": "airbnb",
|
||||
"ecmaFeatures": {
|
||||
"modules": true
|
||||
},
|
||||
"extends": ["prettier"],
|
||||
"plugins": ["prettier"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"mocha": true,
|
||||
"jasmine": true
|
||||
"mocha": true
|
||||
},
|
||||
"globals": {
|
||||
"__DEV__": true,
|
||||
"describe": true,
|
||||
"it": true,
|
||||
"before": true,
|
||||
"after": true,
|
||||
"beforeEach": true,
|
||||
"afterEach": true,
|
||||
"expect": true,
|
||||
"browser": true,
|
||||
"by": true,
|
||||
"element": true
|
||||
"afterEach": true
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"rules": {
|
||||
"strict": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-console": ["warn", { allow: ["warn", "error"] }],
|
||||
"space-before-function-paren": 0
|
||||
"prettier/prettier": ["error", {
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
|
2
.gitignore
vendored
|
@ -11,3 +11,5 @@ tests/reports
|
|||
tests/results
|
||||
.nyc_output
|
||||
coverage
|
||||
cypress/videos
|
||||
cypress/screenshots
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "6"
|
||||
- "8"
|
||||
before_install:
|
||||
- '[ "${TRAVIS_NODE_VERSION}" != "0.8" ] || npm install -g npm@1.4.28'
|
||||
- npm install -g npm@latest
|
||||
|
|
|
@ -36,11 +36,11 @@ Or include Choices directly:
|
|||
|
||||
```html
|
||||
<!-- Include base CSS (optional) -->
|
||||
<link rel="stylesheet" href="assets/styles/css/base.min.css">
|
||||
<link rel="stylesheet" href="public/styles/base.min.css">
|
||||
<!-- Include Choices CSS -->
|
||||
<link rel="stylesheet" href="assets/styles/css/choices.min.css">
|
||||
<link rel="stylesheet" href="public/styles/choices.min.css">
|
||||
<!-- Include Choices JavaScript -->
|
||||
<script src="/assets/scripts/dist/choices.min.js"></script>
|
||||
<script src="/public/scripts/choices.min.js"></script>
|
||||
```
|
||||
## Setup
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"version": "3.0.2",
|
||||
"description": "A vanilla JS customisable text input/select box plugin",
|
||||
"main": [
|
||||
"./assets/scripts/dist/choices.js",
|
||||
"./assets/styles/css/choices.css"
|
||||
"./public/assets/scripts/choices.min.js",
|
||||
"./public/assets/styles/choices.min.css"
|
||||
],
|
||||
"authors": [
|
||||
"Josh Johnson"
|
||||
|
@ -16,6 +16,7 @@
|
|||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"cypress",
|
||||
"tests"
|
||||
],
|
||||
"keywords": [
|
||||
|
|
1
cypress.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
40
package.json
|
@ -1,25 +1,25 @@
|
|||
{
|
||||
"name": "choices.js",
|
||||
"version": "3.0.2",
|
||||
"version": "4.0.0",
|
||||
"description": "A vanilla JS customisable text input/select box plugin",
|
||||
"main": "./src/scripts/dist/choices.min.js",
|
||||
"main": "./public/assets/scripts/choices.min.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"start": "NODE_ENV=development node server.js",
|
||||
"build": "npm run js:build && npm run css:build",
|
||||
"lint": "eslint assets/**/*.js",
|
||||
"coverage": "nyc npm run test",
|
||||
"test": "mocha --require ./config/test.js --compilers js:babel-core/register \"./src/**/**/**/**/*.test.js\"",
|
||||
"test:watch": "npm run test -- --watch --inspect=5556",
|
||||
"coverage": "nyc npm run test",
|
||||
"css:watch": "nodemon -e scss -x \"npm run css:build\"",
|
||||
"css:build": "npm run css:sass -s && npm run css:prefix -s && npm run css:min -s",
|
||||
"css:sass": "node-sass --output-style expanded --include-path scss src/styles/scss/base.scss src/styles/css/base.css && node-sass --output-style expanded --include-path scss src/styles/scss/choices.scss src/styles/css/choices.css",
|
||||
"css:prefix": "postcss --use autoprefixer -b 'last 2 versions' src/styles/css/*.css -d src/styles/css/",
|
||||
"css:min": "csso src/styles/css/base.css src/styles/css/base.min.css && csso src/styles/css/choices.css src/styles/css/choices.min.css",
|
||||
"css:sass": "node-sass --output-style expanded --include-path scss src/styles/base.scss public/assets/styles/base.css && node-sass --output-style expanded --include-path scss src/styles/choices.scss public/assets/styles/choices.css",
|
||||
"css:prefix": "postcss --use autoprefixer -b 'last 2 versions' public/assets/styles/*.css -d public/assets/styles",
|
||||
"css:min": "csso public/assets/styles/base.css public/assets/styles/base.min.css && csso public/assets/styles/choices.css public/assets/styles/choices.min.css",
|
||||
"js:build": "concurrently --prefix-colors yellow,green \"webpack --env.minimize --config webpack.config.prod.js\" \"webpack --config webpack.config.prod.js\"",
|
||||
"version": "node version.js --current $npm_package_version --new $npm_config_newVersion",
|
||||
"postversion": "npm run js:build",
|
||||
"prepush": "npm run lint && npm run test",
|
||||
"dev": "dev"
|
||||
"prepush": "npm run lint && npm run test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -40,35 +40,35 @@
|
|||
"babel-preset-stage-2": "^6.24.1",
|
||||
"chai": "^4.1.0",
|
||||
"concurrently": "^3.1.0",
|
||||
"core-js": "^2.4.1",
|
||||
"csso": "^1.8.2",
|
||||
"custom-event-autopolyfill": "^0.1.3",
|
||||
"es6-promise": "^3.2.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-config-airbnb": "^15.1.0",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-loader": "^1.5.0",
|
||||
"eslint-plugin-import": "^2.7.0",
|
||||
"eslint-plugin-jsx-a11y": "^5.1.1",
|
||||
"eslint-plugin-prettier": "^2.6.0",
|
||||
"eslint-plugin-react": "^7.2.1",
|
||||
"express": "^4.16.3",
|
||||
"husky": "^0.14.3",
|
||||
"jasmine-core": "2.4.1",
|
||||
"jsdom": "^11.5.1",
|
||||
"mocha": "^3.4.2",
|
||||
"node-sass": "^3.4.2",
|
||||
"nodemon": "^1.9.1",
|
||||
"nyc": "^11.0.3",
|
||||
"open": "0.0.5",
|
||||
"opn-cli": "^3.1.0",
|
||||
"postcss-cli": "^2.5.1",
|
||||
"prettier": "^1.13.0",
|
||||
"sinon": "^2.4.0",
|
||||
"webpack": "^3.8.1",
|
||||
"webpack-dashboard": "^0.1.8",
|
||||
"webpack-dev-server": "^1.14.1",
|
||||
"webpack-dev-middleware": "^2.0.0",
|
||||
"webpack-hot-middleware": "^2.22.2",
|
||||
"whatwg-fetch": "^1.0.0",
|
||||
"wrapper-webpack-plugin": "^0.1.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.5",
|
||||
"core-js": "^2.5.6",
|
||||
"custom-event-polyfill": "^0.3.0",
|
||||
"fuse.js": "^3.1.0",
|
||||
"opn": "^5.1.0",
|
||||
"redux": "^3.3.1"
|
||||
|
@ -78,9 +78,9 @@
|
|||
{
|
||||
"basePath": "src",
|
||||
"files": [
|
||||
"scripts/dist/*",
|
||||
"styles/css/*",
|
||||
"icons/*"
|
||||
"public/scripts/*",
|
||||
"public/styles/*",
|
||||
"src/icons/*"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 574 B After Width: | Height: | Size: 574 B |
Before Width: | Height: | Size: 887 B After Width: | Height: | Size: 887 B |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
1
public/assets/scripts/choices.js.map
Normal file
2
public/assets/scripts/choices.min.js
vendored
Normal file
|
@ -6,11 +6,14 @@
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
*, *:before, *:after {
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
html,
|
||||
body {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
|
@ -46,14 +49,21 @@ hr {
|
|||
height: 1px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
a, a:visited, a:focus {
|
||||
a,
|
||||
a:visited,
|
||||
a:focus {
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
|
@ -73,27 +83,33 @@ a, a:visited, a:focus {
|
|||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
h1, .h1 {
|
||||
h1,
|
||||
.h1 {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
h2, .h2 {
|
||||
h2,
|
||||
.h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h3, .h3 {
|
||||
h3,
|
||||
.h3 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
h4, .h4 {
|
||||
h4,
|
||||
.h4 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h5, .h5 {
|
||||
h5,
|
||||
.h5 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h6, .h6 {
|
||||
h6,
|
||||
.h6 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
@ -116,7 +132,9 @@ h6, .h6 {
|
|||
color: #333;
|
||||
}
|
||||
|
||||
.section a, .section a:visited, .section a:focus {
|
||||
.section a,
|
||||
.section a:visited,
|
||||
.section a:focus {
|
||||
color: #00bcd4;
|
||||
}
|
||||
|
|
@ -15,7 +15,8 @@
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.choices.is-disabled .choices__inner, .choices.is-disabled .choices__input {
|
||||
.choices.is-disabled .choices__inner,
|
||||
.choices.is-disabled .choices__input {
|
||||
background-color: #EAEAEA;
|
||||
cursor: not-allowed;
|
||||
-webkit-user-select: none;
|
||||
|
@ -99,11 +100,13 @@
|
|||
margin-right: 0;
|
||||
}
|
||||
|
||||
.choices[data-type*="select-multiple"] .choices__inner, .choices[data-type*="text"] .choices__inner {
|
||||
.choices[data-type*="select-multiple"] .choices__inner,
|
||||
.choices[data-type*="text"] .choices__inner {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.choices[data-type*="select-multiple"] .choices__button, .choices[data-type*="text"] .choices__button {
|
||||
.choices[data-type*="select-multiple"] .choices__button,
|
||||
.choices[data-type*="text"] .choices__button {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-top: 0;
|
||||
|
@ -120,7 +123,9 @@
|
|||
border-radius: 0;
|
||||
}
|
||||
|
||||
.choices[data-type*="select-multiple"] .choices__button:hover, .choices[data-type*="select-multiple"] .choices__button:focus, .choices[data-type*="text"] .choices__button:hover, .choices[data-type*="text"] .choices__button:focus {
|
||||
.choices[data-type*="select-multiple"] .choices__button:hover, .choices[data-type*="select-multiple"] .choices__button:focus,
|
||||
.choices[data-type*="text"] .choices__button:hover,
|
||||
.choices[data-type*="text"] .choices__button:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
@ -137,7 +142,8 @@
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.is-focused .choices__inner, .is-open .choices__inner {
|
||||
.is-focused .choices__inner,
|
||||
.is-open .choices__inner {
|
||||
border-color: #b7b7b7;
|
||||
}
|
||||
|
||||
|
@ -268,8 +274,7 @@
|
|||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
[dir="rtl"] .choices__list--dropdown .choices__item--selectable {
|
||||
text-align: right;
|
|
@ -6,17 +6,17 @@
|
|||
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
|
||||
<title>Choices</title>
|
||||
<meta name=description itemprop=description content="A lightweight, configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency.">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="src/images/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="src/images/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="src/images/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="src/images/manifest.json">
|
||||
<link rel="mask-icon" href="src/images/safari-pinned-tab.svg" color="#00bcd4">
|
||||
<link rel="shortcut icon" href="src/images/favicon.ico">
|
||||
<meta name="msapplication-config" content="/src/images/browserconfig.xml">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="assets/images/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="assets/images/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="assets/images/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="assets/images/manifest.json">
|
||||
<link rel="mask-icon" href="assets/images/safari-pinned-tab.svg" color="#00bcd4">
|
||||
<link rel="shortcut icon" href="assets/images/favicon.ico">
|
||||
<meta name="msapplication-config" content="/assets/images/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<!-- Ignore these -->
|
||||
<link rel="stylesheet" href="src/styles/css/base.min.css?version=3.0.2">
|
||||
<link rel="stylesheet" href="assets/styles/base.min.css?version=3.0.2">
|
||||
<!-- End ignore these -->
|
||||
|
||||
<!-- Optional includes -->
|
||||
|
@ -24,8 +24,8 @@
|
|||
<!-- End optional includes -->
|
||||
|
||||
<!-- Choices includes -->
|
||||
<link rel="stylesheet" href="src/styles/css/choices.min.css?version=3.0.2">
|
||||
<script src="src/scripts/dist/choices.min.js?version=2.8.8"></script>
|
||||
<link rel="stylesheet" href="assets/styles/choices.min.css?version=3.0.2">
|
||||
<script src="assets/scripts/choices.min.js?version=2.8.8"></script>
|
||||
<!-- End Choices includes -->
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
|
@ -40,7 +40,7 @@
|
|||
<div class="container">
|
||||
<div class="section">
|
||||
<a href="https://github.com/jshjohnson/Choices" class="logo">
|
||||
<img src="src/images/logo.svg" alt="Choices.js logo" class="logo__img hidden-ie">
|
||||
<img src="assets/images/logo.svg" alt="Choices.js logo" class="logo__img hidden-ie">
|
||||
<h1 class="visible-ie">Choices.js</h1>
|
||||
</a>
|
||||
<p>Choices.js is a lightweight, configurable select box/text input plugin. Similar to Select2 and Selectize but without
|
46
server.js
|
@ -1,14 +1,36 @@
|
|||
const webpack = require('webpack');
|
||||
const WebpackDevServer = require('webpack-dev-server');
|
||||
const config = require('./webpack.config.dev');
|
||||
const opn = require('opn');
|
||||
/* eslint-disable no-console,global-require,import/no-extraneous-dependencies */
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
|
||||
new WebpackDevServer(webpack(config), {
|
||||
publicPath: config.output.publicPath,
|
||||
historyApiFallback: true,
|
||||
quiet: true, // lets WebpackDashboard do its thing
|
||||
}).listen(3001, 'localhost', (err) => {
|
||||
if (err) console.log(err);
|
||||
opn('http://localhost:3001');
|
||||
console.log('Listening at localhost:3001');
|
||||
const PORT = 3001;
|
||||
const DIST_DIR = path.join(__dirname, 'public');
|
||||
|
||||
const app = express();
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const webpack = require('webpack');
|
||||
const webpackDevMiddleware = require('webpack-dev-middleware');
|
||||
const webpackHotMiddleware = require('webpack-hot-middleware');
|
||||
const config = require('./webpack.config.dev');
|
||||
|
||||
console.log('Compiling bundle... 👷🏽');
|
||||
const compiler = webpack(config);
|
||||
|
||||
app.use(webpackDevMiddleware(compiler, {
|
||||
publicPath: '/assets/scripts/',
|
||||
stats: {
|
||||
colors: true,
|
||||
},
|
||||
}));
|
||||
|
||||
app.use(webpackHotMiddleware(compiler));
|
||||
}
|
||||
|
||||
app.use(express.static(DIST_DIR));
|
||||
app.listen(PORT, (err) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
console.log(`Listening at http://localhost:${PORT} 👂`);
|
||||
});
|
||||
|
|
|
@ -27,17 +27,19 @@ describe('actions/choices', () => {
|
|||
keyCode,
|
||||
};
|
||||
|
||||
expect(actions.addChoice(
|
||||
value,
|
||||
label,
|
||||
id,
|
||||
groupId,
|
||||
disabled,
|
||||
elementId,
|
||||
customProperties,
|
||||
placeholder,
|
||||
keyCode,
|
||||
)).to.eql(expectedAction);
|
||||
expect(
|
||||
actions.addChoice(
|
||||
value,
|
||||
label,
|
||||
id,
|
||||
groupId,
|
||||
disabled,
|
||||
elementId,
|
||||
customProperties,
|
||||
placeholder,
|
||||
keyCode,
|
||||
),
|
||||
).to.eql(expectedAction);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -88,4 +90,3 @@ describe('actions/choices', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -16,12 +16,9 @@ describe('actions/groups', () => {
|
|||
disabled,
|
||||
};
|
||||
|
||||
expect(actions.addGroup(
|
||||
value,
|
||||
id,
|
||||
active,
|
||||
disabled,
|
||||
)).to.eql(expectedAction);
|
||||
expect(actions.addGroup(value, id, active, disabled)).to.eql(
|
||||
expectedAction,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -25,16 +25,18 @@ describe('actions/items', () => {
|
|||
keyCode,
|
||||
};
|
||||
|
||||
expect(actions.addItem(
|
||||
value,
|
||||
label,
|
||||
id,
|
||||
choiceId,
|
||||
groupId,
|
||||
customProperties,
|
||||
placeholder,
|
||||
keyCode,
|
||||
)).to.eql(expectedAction);
|
||||
expect(
|
||||
actions.addItem(
|
||||
value,
|
||||
label,
|
||||
id,
|
||||
choiceId,
|
||||
groupId,
|
||||
customProperties,
|
||||
placeholder,
|
||||
keyCode,
|
||||
),
|
||||
).to.eql(expectedAction);
|
||||
});
|
||||
});
|
||||
|
|
@ -45,7 +45,7 @@ describe('choices', () => {
|
|||
instance.init();
|
||||
});
|
||||
|
||||
it('doesn\'t set initialise flag', () => {
|
||||
it("doesn't set initialise flag", () => {
|
||||
expect(instance.initialised).to.not.equal(false);
|
||||
});
|
||||
});
|
||||
|
@ -60,7 +60,7 @@ describe('choices', () => {
|
|||
beforeEach(() => {
|
||||
createTemplatesSpy = spy(instance, '_createTemplates');
|
||||
createInputSpy = spy(instance, '_createStructure');
|
||||
storeSubscribeSpy = spy(instance.store, 'subscribe');
|
||||
storeSubscribeSpy = spy(instance._store, 'subscribe');
|
||||
renderSpy = spy(instance, 'render');
|
||||
addEventListenersSpy = spy(instance, '_addEventListeners');
|
||||
|
||||
|
@ -123,7 +123,7 @@ describe('choices', () => {
|
|||
instance.destroy();
|
||||
});
|
||||
|
||||
it('doesn\'t set initialise flag', () => {
|
||||
it("doesn't set initialise flag", () => {
|
||||
expect(instance.initialised).to.not.equal(true);
|
||||
});
|
||||
});
|
||||
|
@ -161,7 +161,9 @@ describe('choices', () => {
|
|||
|
||||
it('reverts outer container', () => {
|
||||
expect(containerOuterUnwrapSpy.called).to.equal(true);
|
||||
expect(containerOuterUnwrapSpy.lastCall.args[0]).to.equal(instance.passedElement.element);
|
||||
expect(containerOuterUnwrapSpy.lastCall.args[0]).to.equal(
|
||||
instance.passedElement.element,
|
||||
);
|
||||
});
|
||||
|
||||
it('clears store', () => {
|
||||
|
@ -263,7 +265,6 @@ describe('choices', () => {
|
|||
inputDisableSpy = spy(instance.input, 'disable');
|
||||
});
|
||||
|
||||
|
||||
afterEach(() => {
|
||||
removeEventListenersSpy.restore();
|
||||
passedElementDisableSpy.restore();
|
||||
|
@ -374,29 +375,43 @@ describe('choices', () => {
|
|||
expect(output).to.eql(instance);
|
||||
});
|
||||
|
||||
it('opens containerOuter', () => {
|
||||
expect(containerOuterOpenSpy.called).to.equal(true);
|
||||
it('opens containerOuter', done => {
|
||||
requestAnimationFrame(() => {
|
||||
expect(containerOuterOpenSpy.called).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows dropdown with blurInput flag', () => {
|
||||
expect(dropdownShowSpy.called).to.equal(true);
|
||||
it('shows dropdown with blurInput flag', done => {
|
||||
requestAnimationFrame(() => {
|
||||
expect(dropdownShowSpy.called).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('triggers event on passedElement', () => {
|
||||
expect(passedElementTriggerEventStub.called).to.equal(true);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.eql(EVENTS.showDropdown);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({});
|
||||
it('triggers event on passedElement', done => {
|
||||
requestAnimationFrame(() => {
|
||||
expect(passedElementTriggerEventStub.called).to.equal(true);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.eql(
|
||||
EVENTS.showDropdown,
|
||||
);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('passing true focusInput flag with canSearch set to true', () => {
|
||||
beforeEach(() => {
|
||||
instance.dropdown.isActive = false;
|
||||
instance.canSearch = true;
|
||||
instance._canSearch = true;
|
||||
output = instance.showDropdown(true);
|
||||
});
|
||||
|
||||
it('focuses input', () => {
|
||||
expect(inputFocusSpy.called).to.equal(true);
|
||||
it('focuses input', done => {
|
||||
requestAnimationFrame(() => {
|
||||
expect(inputFocusSpy.called).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -413,7 +428,10 @@ describe('choices', () => {
|
|||
containerOuterCloseSpy = spy(instance.containerOuter, 'close');
|
||||
dropdownHideSpy = spy(instance.dropdown, 'hide');
|
||||
inputBlurSpy = spy(instance.input, 'blur');
|
||||
inputRemoveActiveDescendantSpy = spy(instance.input, 'removeActiveDescendant');
|
||||
inputRemoveActiveDescendantSpy = spy(
|
||||
instance.input,
|
||||
'removeActiveDescendant',
|
||||
);
|
||||
passedElementTriggerEventStub = stub();
|
||||
|
||||
instance.passedElement.triggerEvent = passedElementTriggerEventStub;
|
||||
|
@ -453,33 +471,50 @@ describe('choices', () => {
|
|||
expect(output).to.eql(instance);
|
||||
});
|
||||
|
||||
it('closes containerOuter', () => {
|
||||
expect(containerOuterCloseSpy.called).to.equal(true);
|
||||
it('closes containerOuter', done => {
|
||||
requestAnimationFrame(() => {
|
||||
expect(containerOuterCloseSpy.called).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('hides dropdown with blurInput flag', () => {
|
||||
expect(dropdownHideSpy.called).to.equal(true);
|
||||
it('hides dropdown with blurInput flag', done => {
|
||||
requestAnimationFrame(() => {
|
||||
expect(dropdownHideSpy.called).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('triggers event on passedElement', () => {
|
||||
expect(passedElementTriggerEventStub.called).to.equal(true);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.eql(EVENTS.hideDropdown);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({});
|
||||
it('triggers event on passedElement', done => {
|
||||
requestAnimationFrame(() => {
|
||||
expect(passedElementTriggerEventStub.called).to.equal(true);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.eql(
|
||||
EVENTS.hideDropdown,
|
||||
);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('passing true blurInput flag with canSearch set to true', () => {
|
||||
beforeEach(() => {
|
||||
instance.dropdown.isActive = true;
|
||||
instance.canSearch = true;
|
||||
instance._canSearch = true;
|
||||
output = instance.hideDropdown(true);
|
||||
});
|
||||
|
||||
it('removes active descendants', () => {
|
||||
expect(inputRemoveActiveDescendantSpy.called).to.equal(true);
|
||||
it('removes active descendants', done => {
|
||||
requestAnimationFrame(() => {
|
||||
expect(inputRemoveActiveDescendantSpy.called).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('blurs input', () => {
|
||||
expect(inputBlurSpy.called).to.equal(true);
|
||||
it('blurs input', done => {
|
||||
requestAnimationFrame(() => {
|
||||
expect(inputBlurSpy.called).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -540,15 +575,15 @@ describe('choices', () => {
|
|||
storeGetGroupByIdStub = stub().returns({
|
||||
value: groupIdValue,
|
||||
});
|
||||
storeDispatchSpy = spy(instance.store, 'dispatch');
|
||||
storeDispatchSpy = spy(instance._store, 'dispatch');
|
||||
|
||||
instance.store.getGroupById = storeGetGroupByIdStub;
|
||||
instance._store.getGroupById = storeGetGroupByIdStub;
|
||||
instance.passedElement.triggerEvent = passedElementTriggerEventStub;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
storeDispatchSpy.restore();
|
||||
instance.store.getGroupById.reset();
|
||||
instance._store.getGroupById.reset();
|
||||
instance.passedElement.triggerEvent.reset();
|
||||
});
|
||||
|
||||
|
@ -597,7 +632,9 @@ describe('choices', () => {
|
|||
|
||||
it('triggers event with null groupValue', () => {
|
||||
expect(passedElementTriggerEventStub.called).to.equal(true);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(EVENTS.highlightItem);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(
|
||||
EVENTS.highlightItem,
|
||||
);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({
|
||||
id: item.id,
|
||||
value: item.value,
|
||||
|
@ -615,7 +652,9 @@ describe('choices', () => {
|
|||
|
||||
it('triggers event with groupValue', () => {
|
||||
expect(passedElementTriggerEventStub.called).to.equal(true);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(EVENTS.highlightItem);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(
|
||||
EVENTS.highlightItem,
|
||||
);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({
|
||||
id: item.id,
|
||||
value: item.value,
|
||||
|
@ -631,7 +670,7 @@ describe('choices', () => {
|
|||
output = instance.highlightItem(item, false);
|
||||
});
|
||||
|
||||
it('doesn\'t trigger event', () => {
|
||||
it("doesn't trigger event", () => {
|
||||
expect(passedElementTriggerEventStub.called).to.equal(false);
|
||||
});
|
||||
|
||||
|
@ -651,15 +690,15 @@ describe('choices', () => {
|
|||
storeGetGroupByIdStub = stub().returns({
|
||||
value: groupIdValue,
|
||||
});
|
||||
storeDispatchSpy = spy(instance.store, 'dispatch');
|
||||
storeDispatchSpy = spy(instance._store, 'dispatch');
|
||||
|
||||
instance.store.getGroupById = storeGetGroupByIdStub;
|
||||
instance._store.getGroupById = storeGetGroupByIdStub;
|
||||
instance.passedElement.triggerEvent = passedElementTriggerEventStub;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
storeDispatchSpy.restore();
|
||||
instance.store.getGroupById.reset();
|
||||
instance._store.getGroupById.reset();
|
||||
instance.passedElement.triggerEvent.reset();
|
||||
});
|
||||
|
||||
|
@ -708,7 +747,9 @@ describe('choices', () => {
|
|||
|
||||
it('triggers event with null groupValue', () => {
|
||||
expect(passedElementTriggerEventStub.called).to.equal(true);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(EVENTS.highlightItem);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(
|
||||
EVENTS.highlightItem,
|
||||
);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({
|
||||
id: item.id,
|
||||
value: item.value,
|
||||
|
@ -726,7 +767,9 @@ describe('choices', () => {
|
|||
|
||||
it('triggers event with groupValue', () => {
|
||||
expect(passedElementTriggerEventStub.called).to.equal(true);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(EVENTS.highlightItem);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(
|
||||
EVENTS.highlightItem,
|
||||
);
|
||||
expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({
|
||||
id: item.id,
|
||||
value: item.value,
|
||||
|
@ -742,7 +785,7 @@ describe('choices', () => {
|
|||
output = instance.highlightItem(item, false);
|
||||
});
|
||||
|
||||
it('doesn\'t trigger event', () => {
|
||||
it("doesn't trigger event", () => {
|
||||
expect(passedElementTriggerEventStub.called).to.equal(false);
|
||||
});
|
||||
|
||||
|
@ -767,7 +810,7 @@ describe('choices', () => {
|
|||
];
|
||||
|
||||
beforeEach(() => {
|
||||
storeGetItemsStub = stub(instance.store, 'items').get(() => items);
|
||||
storeGetItemsStub = stub(instance._store, 'items').get(() => items);
|
||||
highlightItemStub = stub();
|
||||
|
||||
instance.highlightItem = highlightItemStub;
|
||||
|
@ -805,7 +848,7 @@ describe('choices', () => {
|
|||
];
|
||||
|
||||
beforeEach(() => {
|
||||
storeGetItemsStub = stub(instance.store, 'items').get(() => items);
|
||||
storeGetItemsStub = stub(instance._store, 'items').get(() => items);
|
||||
unhighlightItemStub = stub();
|
||||
|
||||
instance.unhighlightItem = unhighlightItemStub;
|
||||
|
@ -832,13 +875,13 @@ describe('choices', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
storeDispatchStub = stub();
|
||||
instance.store.dispatch = storeDispatchStub;
|
||||
instance._store.dispatch = storeDispatchStub;
|
||||
|
||||
output = instance.clearStore();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
instance.store.dispatch.reset();
|
||||
instance._store.dispatch.reset();
|
||||
});
|
||||
|
||||
returnsInstance(output);
|
||||
|
@ -857,21 +900,21 @@ describe('choices', () => {
|
|||
beforeEach(() => {
|
||||
inputClearSpy = spy(instance.input, 'clear');
|
||||
storeDispatchStub = stub();
|
||||
instance.store.dispatch = storeDispatchStub;
|
||||
instance._store.dispatch = storeDispatchStub;
|
||||
output = instance.clearInput();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
inputClearSpy.restore();
|
||||
instance.store.dispatch.reset();
|
||||
instance._store.dispatch.reset();
|
||||
});
|
||||
|
||||
returnsInstance(output);
|
||||
|
||||
describe('text element', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectOneElement = false;
|
||||
instance.isTextElement = false;
|
||||
instance._isSelectOneElement = false;
|
||||
instance._isTextElement = false;
|
||||
|
||||
output = instance.clearInput();
|
||||
});
|
||||
|
@ -884,8 +927,8 @@ describe('choices', () => {
|
|||
|
||||
describe('select element with search enabled', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectOneElement = true;
|
||||
instance.isTextElement = false;
|
||||
instance._isSelectOneElement = true;
|
||||
instance._isTextElement = false;
|
||||
instance.config.searchEnabled = true;
|
||||
|
||||
output = instance.clearInput();
|
||||
|
@ -897,7 +940,7 @@ describe('choices', () => {
|
|||
});
|
||||
|
||||
it('resets search flag', () => {
|
||||
expect(instance.isSearching).to.equal(false);
|
||||
expect(instance._isSearching).to.equal(false);
|
||||
});
|
||||
|
||||
it('dispatches activateChoices action', () => {
|
||||
|
@ -948,7 +991,7 @@ describe('choices', () => {
|
|||
|
||||
describe('text element', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectElement = false;
|
||||
instance._isSelectElement = false;
|
||||
output = instance.ajax(() => {});
|
||||
});
|
||||
|
||||
|
@ -970,7 +1013,7 @@ describe('choices', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
instance.initialised = true;
|
||||
instance.isSelectElement = true;
|
||||
instance._isSelectElement = true;
|
||||
ajaxCallbackStub = stub();
|
||||
callback = stub();
|
||||
output = instance.ajax(callback);
|
||||
|
@ -978,7 +1021,7 @@ describe('choices', () => {
|
|||
|
||||
returnsInstance(output);
|
||||
|
||||
it('sets loading state', (done) => {
|
||||
it('sets loading state', done => {
|
||||
requestAnimationFrame(() => {
|
||||
expect(handleLoadingStateStub.called).to.equal(true);
|
||||
done();
|
||||
|
@ -1067,7 +1110,7 @@ describe('choices', () => {
|
|||
describe('when already initialised and not text element', () => {
|
||||
beforeEach(() => {
|
||||
instance.initialised = true;
|
||||
instance.isTextElement = false;
|
||||
instance._isTextElement = false;
|
||||
});
|
||||
|
||||
describe('passing a string value', () => {
|
||||
|
@ -1081,15 +1124,14 @@ describe('choices', () => {
|
|||
|
||||
it('sets each choice with same value', () => {
|
||||
expect(findAndSelectChoiceByValueStub.called).to.equal(true);
|
||||
expect(findAndSelectChoiceByValueStub.firstCall.args[0]).to.equal(value);
|
||||
expect(findAndSelectChoiceByValueStub.firstCall.args[0]).to.equal(
|
||||
value,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('passing an array of values', () => {
|
||||
const values = [
|
||||
'Value 1',
|
||||
'Value 2',
|
||||
];
|
||||
const values = ['Value 1', 'Value 2'];
|
||||
|
||||
beforeEach(() => {
|
||||
output = instance.setChoiceByValue(values);
|
||||
|
@ -1099,8 +1141,12 @@ describe('choices', () => {
|
|||
|
||||
it('sets each choice with same value', () => {
|
||||
expect(findAndSelectChoiceByValueStub.callCount).to.equal(2);
|
||||
expect(findAndSelectChoiceByValueStub.firstCall.args[0]).to.equal(values[0]);
|
||||
expect(findAndSelectChoiceByValueStub.secondCall.args[0]).to.equal(values[1]);
|
||||
expect(findAndSelectChoiceByValueStub.firstCall.args[0]).to.equal(
|
||||
values[0],
|
||||
);
|
||||
expect(findAndSelectChoiceByValueStub.secondCall.args[0]).to.equal(
|
||||
values[1],
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1120,7 +1166,7 @@ describe('choices', () => {
|
|||
];
|
||||
|
||||
beforeEach(() => {
|
||||
activeItemsStub = stub(instance.store, 'activeItems').get(() => items);
|
||||
activeItemsStub = stub(instance._store, 'activeItems').get(() => items);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -1130,7 +1176,7 @@ describe('choices', () => {
|
|||
describe('passing true valueOnly flag', () => {
|
||||
describe('select one input', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectOneElement = true;
|
||||
instance._isSelectOneElement = true;
|
||||
output = instance.getValue(true);
|
||||
});
|
||||
|
||||
|
@ -1141,12 +1187,12 @@ describe('choices', () => {
|
|||
|
||||
describe('non select one input', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectOneElement = false;
|
||||
instance._isSelectOneElement = false;
|
||||
output = instance.getValue(true);
|
||||
});
|
||||
|
||||
it('returns all active item values', () => {
|
||||
expect(output).to.eql(items.map((item => item.value)));
|
||||
expect(output).to.eql(items.map(item => item.value));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1154,7 +1200,7 @@ describe('choices', () => {
|
|||
describe('passing false valueOnly flag', () => {
|
||||
describe('select one input', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectOneElement = true;
|
||||
instance._isSelectOneElement = true;
|
||||
output = instance.getValue(false);
|
||||
});
|
||||
|
||||
|
@ -1165,7 +1211,7 @@ describe('choices', () => {
|
|||
|
||||
describe('non select one input', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectOneElement = false;
|
||||
instance._isSelectOneElement = false;
|
||||
output = instance.getValue(false);
|
||||
});
|
||||
|
||||
|
@ -1177,51 +1223,41 @@ describe('choices', () => {
|
|||
});
|
||||
|
||||
describe('removeActiveItemsByValue', () => {
|
||||
describe('passing invalid value', () => {
|
||||
beforeEach(() => {
|
||||
output = instance.removeActiveItemsByValue(null);
|
||||
});
|
||||
let activeItemsStub;
|
||||
let removeItemStub;
|
||||
const value = 'Removed';
|
||||
const items = [
|
||||
{
|
||||
id: '1',
|
||||
value: 'Not removed',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
value: 'Removed',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
value: 'Removed',
|
||||
},
|
||||
];
|
||||
|
||||
returnsInstance(output);
|
||||
beforeEach(() => {
|
||||
removeItemStub = stub();
|
||||
activeItemsStub = stub(instance._store, 'activeItems').get(() => items);
|
||||
instance._removeItem = removeItemStub;
|
||||
|
||||
output = instance.removeActiveItemsByValue(value);
|
||||
});
|
||||
|
||||
describe('passing valid value', () => {
|
||||
let activeItemsStub;
|
||||
let removeItemStub;
|
||||
const value = 'Removed';
|
||||
const items = [
|
||||
{
|
||||
id: '1',
|
||||
value: 'Not removed',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
value: 'Removed',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
value: 'Removed',
|
||||
},
|
||||
];
|
||||
afterEach(() => {
|
||||
activeItemsStub.reset();
|
||||
instance._removeItem.reset();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
removeItemStub = stub();
|
||||
activeItemsStub = stub(instance.store, 'activeItems').get(() => items);
|
||||
instance._removeItem = removeItemStub;
|
||||
|
||||
output = instance.removeActiveItemsByValue(value);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
activeItemsStub.reset();
|
||||
instance._removeItem.reset();
|
||||
});
|
||||
|
||||
it('removes each active item in store with matching value', () => {
|
||||
expect(removeItemStub.callCount).to.equal(2);
|
||||
expect(removeItemStub.firstCall.args[0]).to.equal(items[1]);
|
||||
expect(removeItemStub.secondCall.args[0]).to.equal(items[2]);
|
||||
});
|
||||
it('removes each active item in store with matching value', () => {
|
||||
expect(removeItemStub.callCount).to.equal(2);
|
||||
expect(removeItemStub.firstCall.args[0]).to.equal(items[1]);
|
||||
expect(removeItemStub.secondCall.args[0]).to.equal(items[2]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1245,7 +1281,7 @@ describe('choices', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
removeItemStub = stub();
|
||||
activeItemsStub = stub(instance.store, 'activeItems').get(() => items);
|
||||
activeItemsStub = stub(instance._store, 'activeItems').get(() => items);
|
||||
instance._removeItem = removeItemStub;
|
||||
});
|
||||
|
||||
|
@ -1298,9 +1334,11 @@ describe('choices', () => {
|
|||
},
|
||||
];
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
highlightedActiveItemsStub = stub(instance.store, 'highlightedActiveItems').get(() => items);
|
||||
highlightedActiveItemsStub = stub(
|
||||
instance._store,
|
||||
'highlightedActiveItems',
|
||||
).get(() => items);
|
||||
removeItemStub = stub();
|
||||
triggerChangeStub = stub();
|
||||
|
||||
|
@ -1397,7 +1435,7 @@ describe('choices', () => {
|
|||
|
||||
describe('when element is not select element', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectElement = false;
|
||||
instance._isSelectElement = false;
|
||||
instance.setChoices(choices, value, label, false);
|
||||
});
|
||||
|
||||
|
@ -1407,7 +1445,7 @@ describe('choices', () => {
|
|||
describe('passing invalid arguments', () => {
|
||||
describe('passing an empty array', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectElement = true;
|
||||
instance._isSelectElement = true;
|
||||
instance.setChoices([], value, label, false);
|
||||
});
|
||||
|
||||
|
@ -1416,7 +1454,7 @@ describe('choices', () => {
|
|||
|
||||
describe('passing no value', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectElement = true;
|
||||
instance._isSelectElement = true;
|
||||
instance.setChoices(choices, undefined, 'label', false);
|
||||
});
|
||||
|
||||
|
@ -1426,7 +1464,7 @@ describe('choices', () => {
|
|||
|
||||
describe('passing valid arguments', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectElement = true;
|
||||
instance._isSelectElement = true;
|
||||
});
|
||||
|
||||
it('removes loading state', () => {
|
||||
|
@ -1477,8 +1515,8 @@ describe('choices', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('createGroupsFragment', () => {
|
||||
let createChoicesFragmentStub;
|
||||
describe('_createGroupsFragment', () => {
|
||||
let _createChoicesFragmentStub;
|
||||
const choices = [
|
||||
{
|
||||
id: 1,
|
||||
|
@ -1519,12 +1557,12 @@ describe('choices', () => {
|
|||
];
|
||||
|
||||
beforeEach(() => {
|
||||
createChoicesFragmentStub = stub();
|
||||
instance.createChoicesFragment = createChoicesFragmentStub;
|
||||
_createChoicesFragmentStub = stub();
|
||||
instance._createChoicesFragment = _createChoicesFragmentStub;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
instance.createChoicesFragment.reset();
|
||||
instance._createChoicesFragment.reset();
|
||||
});
|
||||
|
||||
describe('returning a fragment of groups', () => {
|
||||
|
@ -1534,24 +1572,28 @@ describe('choices', () => {
|
|||
const childElement = document.createElement('div');
|
||||
fragment.appendChild(childElement);
|
||||
|
||||
output = instance.createGroupsFragment(groups, choices, fragment);
|
||||
output = instance._createGroupsFragment(groups, choices, fragment);
|
||||
const elementToWrapFragment = document.createElement('div');
|
||||
elementToWrapFragment.appendChild(output);
|
||||
|
||||
expect(output).to.be.instanceOf(DocumentFragment);
|
||||
expect(elementToWrapFragment.children[0]).to.eql(childElement);
|
||||
expect(elementToWrapFragment.querySelectorAll('[data-group]').length).to.equal(2);
|
||||
expect(
|
||||
elementToWrapFragment.querySelectorAll('[data-group]').length,
|
||||
).to.equal(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('not passing fragment argument', () => {
|
||||
it('returns new groups fragment', () => {
|
||||
output = instance.createGroupsFragment(groups, choices);
|
||||
output = instance._createGroupsFragment(groups, choices);
|
||||
const elementToWrapFragment = document.createElement('div');
|
||||
elementToWrapFragment.appendChild(output);
|
||||
|
||||
expect(output).to.be.instanceOf(DocumentFragment);
|
||||
expect(elementToWrapFragment.querySelectorAll('[data-group]').length).to.equal(2);
|
||||
expect(
|
||||
elementToWrapFragment.querySelectorAll('[data-group]').length,
|
||||
).to.equal(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1570,7 +1612,7 @@ describe('choices', () => {
|
|||
|
||||
it('sorts groups by config.sortFn', () => {
|
||||
expect(sortFnStub.called).to.equal(false);
|
||||
instance.createGroupsFragment(groups, choices);
|
||||
instance._createGroupsFragment(groups, choices);
|
||||
expect(sortFnStub.called).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
@ -1589,21 +1631,21 @@ describe('choices', () => {
|
|||
});
|
||||
|
||||
it('does not sort groups', () => {
|
||||
instance.createGroupsFragment(groups, choices);
|
||||
instance._createGroupsFragment(groups, choices);
|
||||
expect(sortFnStub.called).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('select-one element', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectOneElement = true;
|
||||
instance._isSelectOneElement = true;
|
||||
});
|
||||
|
||||
it('calls createChoicesFragment with choices that belong to each group', () => {
|
||||
expect(createChoicesFragmentStub.called).to.equal(false);
|
||||
instance.createGroupsFragment(groups, choices);
|
||||
expect(createChoicesFragmentStub.called).to.equal(true);
|
||||
expect(createChoicesFragmentStub.firstCall.args[0]).to.eql([
|
||||
it('calls _createChoicesFragment with choices that belong to each group', () => {
|
||||
expect(_createChoicesFragmentStub.called).to.equal(false);
|
||||
instance._createGroupsFragment(groups, choices);
|
||||
expect(_createChoicesFragmentStub.called).to.equal(true);
|
||||
expect(_createChoicesFragmentStub.firstCall.args[0]).to.eql([
|
||||
{
|
||||
id: 1,
|
||||
selected: true,
|
||||
|
@ -1619,7 +1661,7 @@ describe('choices', () => {
|
|||
label: 'Choice 3',
|
||||
},
|
||||
]);
|
||||
expect(createChoicesFragmentStub.secondCall.args[0]).to.eql([
|
||||
expect(_createChoicesFragmentStub.secondCall.args[0]).to.eql([
|
||||
{
|
||||
id: 2,
|
||||
selected: false,
|
||||
|
@ -1634,15 +1676,15 @@ describe('choices', () => {
|
|||
describe('text/select-multiple element', () => {
|
||||
describe('renderSelectedChoices set to "always"', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectOneElement = false;
|
||||
instance._isSelectOneElement = false;
|
||||
instance.config.renderSelectedChoices = 'always';
|
||||
});
|
||||
|
||||
it('calls createChoicesFragment with choices that belong to each group', () => {
|
||||
expect(createChoicesFragmentStub.called).to.equal(false);
|
||||
instance.createGroupsFragment(groups, choices);
|
||||
expect(createChoicesFragmentStub.called).to.equal(true);
|
||||
expect(createChoicesFragmentStub.firstCall.args[0]).to.eql([
|
||||
it('calls _createChoicesFragment with choices that belong to each group', () => {
|
||||
expect(_createChoicesFragmentStub.called).to.equal(false);
|
||||
instance._createGroupsFragment(groups, choices);
|
||||
expect(_createChoicesFragmentStub.called).to.equal(true);
|
||||
expect(_createChoicesFragmentStub.firstCall.args[0]).to.eql([
|
||||
{
|
||||
id: 1,
|
||||
selected: true,
|
||||
|
@ -1658,7 +1700,7 @@ describe('choices', () => {
|
|||
label: 'Choice 3',
|
||||
},
|
||||
]);
|
||||
expect(createChoicesFragmentStub.secondCall.args[0]).to.eql([
|
||||
expect(_createChoicesFragmentStub.secondCall.args[0]).to.eql([
|
||||
{
|
||||
id: 2,
|
||||
selected: false,
|
||||
|
@ -1672,15 +1714,15 @@ describe('choices', () => {
|
|||
|
||||
describe('renderSelectedChoices not set to "always"', () => {
|
||||
beforeEach(() => {
|
||||
instance.isSelectOneElement = false;
|
||||
instance._isSelectOneElement = false;
|
||||
instance.config.renderSelectedChoices = false;
|
||||
});
|
||||
|
||||
it('calls createChoicesFragment with choices that belong to each group that are not already selected', () => {
|
||||
expect(createChoicesFragmentStub.called).to.equal(false);
|
||||
instance.createGroupsFragment(groups, choices);
|
||||
expect(createChoicesFragmentStub.called).to.equal(true);
|
||||
expect(createChoicesFragmentStub.firstCall.args[0]).to.eql([
|
||||
it('calls _createChoicesFragment with choices that belong to each group that are not already selected', () => {
|
||||
expect(_createChoicesFragmentStub.called).to.equal(false);
|
||||
instance._createGroupsFragment(groups, choices);
|
||||
expect(_createChoicesFragmentStub.called).to.equal(true);
|
||||
expect(_createChoicesFragmentStub.firstCall.args[0]).to.eql([
|
||||
{
|
||||
id: 3,
|
||||
selected: false,
|
||||
|
@ -1689,7 +1731,7 @@ describe('choices', () => {
|
|||
label: 'Choice 3',
|
||||
},
|
||||
]);
|
||||
expect(createChoicesFragmentStub.secondCall.args[0]).to.eql([
|
||||
expect(_createChoicesFragmentStub.secondCall.args[0]).to.eql([
|
||||
{
|
||||
id: 2,
|
||||
selected: false,
|
||||
|
@ -1704,41 +1746,31 @@ describe('choices', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('createChoicesFragment', () => {
|
||||
beforeEach(() => {});
|
||||
it('returns a fragment of choices', () => {});
|
||||
});
|
||||
// describe('render', () => {
|
||||
// beforeEach(() => {});
|
||||
|
||||
describe('createItemsFragment', () => {
|
||||
beforeEach(() => {});
|
||||
it('returns a fragment of items', () => {});
|
||||
});
|
||||
// describe('no change to state', () => {
|
||||
// it('returns early', () => {});
|
||||
// });
|
||||
|
||||
describe('render', () => {
|
||||
beforeEach(() => {});
|
||||
// describe('change to state', () => {
|
||||
// it('updates previous state to current state', () => {});
|
||||
|
||||
describe('no change to state', () => {
|
||||
it('returns early', () => {});
|
||||
});
|
||||
// describe('select element', () => {
|
||||
// it('clears choice list', () => {});
|
||||
|
||||
describe('change to state', () => {
|
||||
it('updates previous state to current state', () => {});
|
||||
// describe('when resetScrollPosition config option is set to true', () => {
|
||||
// it('scrolls to top of choice list', () => {});
|
||||
// });
|
||||
// });
|
||||
|
||||
describe('select element', () => {
|
||||
it('clears choice list', () => {});
|
||||
|
||||
describe('when resetScrollPosition config option is set to true', () => {
|
||||
it('scrolls to top of choice list', () => {});
|
||||
});
|
||||
});
|
||||
|
||||
describe('text element', () => {
|
||||
describe('active items in store', () => {
|
||||
it('clears item list', () => {});
|
||||
it('renders active items inside item list', () => {});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
// describe('text element', () => {
|
||||
// describe('active items in store', () => {
|
||||
// it('clears item list', () => {});
|
||||
// it('renders active items inside item list', () => {});
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
});
|
||||
});
|
|
@ -1,50 +1,35 @@
|
|||
import { getWindowHeight, wrap } from '../lib/utils';
|
||||
|
||||
export default class Container {
|
||||
constructor(instance, element, classNames) {
|
||||
this.parentInstance = instance;
|
||||
this.element = element;
|
||||
this.classNames = classNames;
|
||||
this.config = instance.config;
|
||||
constructor({ element, type, classNames, position }) {
|
||||
Object.assign(this, { element, classNames, type, position });
|
||||
|
||||
this.isOpen = false;
|
||||
this.isFlipped = false;
|
||||
this.isFocussed = false;
|
||||
this.isDisabled = false;
|
||||
this.isLoading = false;
|
||||
this.onFocus = this.onFocus.bind(this);
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
|
||||
this._onFocus = this._onFocus.bind(this);
|
||||
this._onBlur = this._onBlur.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event listeners
|
||||
*/
|
||||
*/
|
||||
addEventListeners() {
|
||||
this.element.addEventListener('focus', this.onFocus);
|
||||
this.element.addEventListener('blur', this.onBlur);
|
||||
this.element.addEventListener('focus', this._onFocus);
|
||||
this.element.addEventListener('blur', this._onBlur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove event listeners
|
||||
*/
|
||||
*/
|
||||
|
||||
/** */
|
||||
removeEventListeners() {
|
||||
this.element.removeEventListener('focus', this.onFocus);
|
||||
this.element.removeEventListener('blur', this.onBlur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set focussed state
|
||||
*/
|
||||
onFocus() {
|
||||
this.isFocussed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove blurred state
|
||||
*/
|
||||
onBlur() {
|
||||
this.isFocussed = false;
|
||||
this.element.removeEventListener('focus', this._onFocus);
|
||||
this.element.removeEventListener('blur', this._onBlur);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,16 +42,16 @@ export default class Container {
|
|||
if (dropdownPos === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If flip is enabled and the dropdown bottom position is
|
||||
// greater than the window height flip the dropdown.
|
||||
let shouldFlip = false;
|
||||
if (this.config.position === 'auto') {
|
||||
if (this.position === 'auto') {
|
||||
shouldFlip = dropdownPos >= windowHeight;
|
||||
} else if (this.config.position === 'top') {
|
||||
} else if (this.position === 'top') {
|
||||
shouldFlip = true;
|
||||
}
|
||||
|
||||
|
||||
return shouldFlip;
|
||||
}
|
||||
|
||||
|
@ -129,7 +114,7 @@ export default class Container {
|
|||
enable() {
|
||||
this.element.classList.remove(this.classNames.disabledState);
|
||||
this.element.removeAttribute('aria-disabled');
|
||||
if (this.parentInstance.isSelectOneElement) {
|
||||
if (this.type === 'select-one') {
|
||||
this.element.setAttribute('tabindex', '0');
|
||||
}
|
||||
this.isDisabled = false;
|
||||
|
@ -141,7 +126,7 @@ export default class Container {
|
|||
disable() {
|
||||
this.element.classList.add(this.classNames.disabledState);
|
||||
this.element.setAttribute('aria-disabled', 'true');
|
||||
if (this.parentInstance.isSelectOneElement) {
|
||||
if (this.type === 'select-one') {
|
||||
this.element.setAttribute('tabindex', '-1');
|
||||
}
|
||||
this.isDisabled = true;
|
||||
|
@ -153,10 +138,7 @@ export default class Container {
|
|||
|
||||
unwrap(element) {
|
||||
// Move passed element outside this element
|
||||
this.element.parentNode.insertBefore(
|
||||
element,
|
||||
this.element,
|
||||
);
|
||||
this.element.parentNode.insertBefore(element, this.element);
|
||||
// Remove this element
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
|
@ -178,4 +160,18 @@ export default class Container {
|
|||
this.element.removeAttribute('aria-busy');
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set focussed state
|
||||
*/
|
||||
_onFocus() {
|
||||
this.isFocussed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove blurred state
|
||||
*/
|
||||
_onBlur() {
|
||||
this.isFocussed = false;
|
||||
}
|
||||
}
|
|
@ -1,23 +1,23 @@
|
|||
import { expect } from 'chai';
|
||||
import { stub } from 'sinon';
|
||||
import Container from './container';
|
||||
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
|
||||
import { DEFAULT_CLASSNAMES } from '../constants';
|
||||
|
||||
describe('components/container', () => {
|
||||
let instance;
|
||||
let choicesInstance;
|
||||
let element;
|
||||
|
||||
beforeEach(() => {
|
||||
choicesInstance = {
|
||||
config: {
|
||||
...DEFAULT_CONFIG,
|
||||
},
|
||||
};
|
||||
element = document.createElement('div');
|
||||
element.id = 'container';
|
||||
|
||||
document.body.appendChild(element);
|
||||
instance = new Container(choicesInstance, document.getElementById('container'), DEFAULT_CLASSNAMES);
|
||||
instance = new Container({
|
||||
element: document.getElementById('container'),
|
||||
classNames: DEFAULT_CLASSNAMES,
|
||||
position: 'auto',
|
||||
type: 'text',
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -27,10 +27,6 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('assigns choices instance to class', () => {
|
||||
expect(instance.parentInstance).to.eql(choicesInstance);
|
||||
});
|
||||
|
||||
it('assigns choices element to class', () => {
|
||||
expect(instance.element).to.eql(element);
|
||||
});
|
||||
|
@ -81,7 +77,7 @@ describe('components/container', () => {
|
|||
describe('onFocus', () => {
|
||||
it('sets isFocussed flag to true', () => {
|
||||
expect(instance.isFocussed).to.equal(false);
|
||||
instance.onFocus();
|
||||
instance._onFocus();
|
||||
expect(instance.isFocussed).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
@ -89,7 +85,7 @@ describe('components/container', () => {
|
|||
describe('onBlur', () => {
|
||||
it('sets isFocussed flag to false', () => {
|
||||
instance.isFocussed = true;
|
||||
instance.onBlur();
|
||||
instance._onBlur();
|
||||
expect(instance.isFocussed).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
@ -104,7 +100,7 @@ describe('components/container', () => {
|
|||
describe('passing dropdownPos', () => {
|
||||
describe('position config option set to "auto"', () => {
|
||||
beforeEach(() => {
|
||||
instance.config.position = 'auto';
|
||||
instance.position = 'auto';
|
||||
});
|
||||
|
||||
describe('dropdownPos is greater than window height', () => {
|
||||
|
@ -122,7 +118,7 @@ describe('components/container', () => {
|
|||
|
||||
describe('position config option set to "top"', () => {
|
||||
beforeEach(() => {
|
||||
instance.config.position = 'top';
|
||||
instance.position = 'top';
|
||||
});
|
||||
|
||||
it('returns true', () => {
|
||||
|
@ -132,7 +128,7 @@ describe('components/container', () => {
|
|||
|
||||
describe('position config option set to "bottom"', () => {
|
||||
beforeEach(() => {
|
||||
instance.config.position = 'bottom';
|
||||
instance.position = 'bottom';
|
||||
});
|
||||
|
||||
it('returns false', () => {
|
||||
|
@ -143,21 +139,32 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
describe('setActiveDescendant', () => {
|
||||
it('sets element\'s aria-activedescendant attribute with passed descendant ID', () => {
|
||||
it("sets element's aria-activedescendant attribute with passed descendant ID", () => {
|
||||
const activeDescendantID = '1234';
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(null);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
|
||||
null,
|
||||
);
|
||||
instance.setActiveDescendant(activeDescendantID);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(activeDescendantID);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
|
||||
activeDescendantID,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeActiveDescendant', () => {
|
||||
it('remove elememnt\'s aria-activedescendant attribute', () => {
|
||||
it("remove elememnt's aria-activedescendant attribute", () => {
|
||||
const activeDescendantID = '1234';
|
||||
instance.element.setAttribute('aria-activedescendant', activeDescendantID);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(activeDescendantID);
|
||||
instance.element.setAttribute(
|
||||
'aria-activedescendant',
|
||||
activeDescendantID,
|
||||
);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
|
||||
activeDescendantID,
|
||||
);
|
||||
instance.removeActiveDescendant();
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(null);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
|
||||
null,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -167,7 +174,9 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
it('adds open state class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.openState)).to.equal(true);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.openState),
|
||||
).to.equal(true);
|
||||
});
|
||||
|
||||
it('sets aria-expanded attribute to true', () => {
|
||||
|
@ -192,7 +201,9 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
it('adds adds flipped state class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.flippedState)).to.equal(true);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.flippedState),
|
||||
).to.equal(true);
|
||||
});
|
||||
|
||||
it('sets isFlipped flag to true', () => {
|
||||
|
@ -207,7 +218,9 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
it('adds open state class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.openState)).to.equal(false);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.openState),
|
||||
).to.equal(false);
|
||||
});
|
||||
|
||||
it('sets aria-expanded attribute to true', () => {
|
||||
|
@ -225,7 +238,9 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
it('removes adds flipped state class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.flippedState)).to.equal(false);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.flippedState),
|
||||
).to.equal(false);
|
||||
});
|
||||
|
||||
it('sets isFlipped flag to false', () => {
|
||||
|
@ -268,9 +283,13 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
it('adds focus state class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState)).to.equal(false);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState),
|
||||
).to.equal(false);
|
||||
instance.addFocusState();
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState)).to.equal(true);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState),
|
||||
).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -280,9 +299,13 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
it('removes focus state class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState)).to.equal(true);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState),
|
||||
).to.equal(true);
|
||||
instance.removeFocusState();
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState)).to.equal(false);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState),
|
||||
).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -292,9 +315,13 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
it('removes disabled state class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState)).to.equal(true);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState),
|
||||
).to.equal(true);
|
||||
instance.enable();
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState)).to.equal(false);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState),
|
||||
).to.equal(false);
|
||||
});
|
||||
|
||||
it('removes aria-disabled attribute', () => {
|
||||
|
@ -310,7 +337,7 @@ describe('components/container', () => {
|
|||
|
||||
describe('select one element', () => {
|
||||
beforeEach(() => {
|
||||
instance.parentInstance.isSelectOneElement = true;
|
||||
instance.type = 'select-one';
|
||||
instance.enable();
|
||||
});
|
||||
|
||||
|
@ -326,9 +353,13 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
it('removes disabled state class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState)).to.equal(false);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState),
|
||||
).to.equal(false);
|
||||
instance.disable();
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState)).to.equal(true);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState),
|
||||
).to.equal(true);
|
||||
});
|
||||
|
||||
it('removes aria-disabled attribute', () => {
|
||||
|
@ -344,7 +375,7 @@ describe('components/container', () => {
|
|||
|
||||
describe('select one element', () => {
|
||||
beforeEach(() => {
|
||||
instance.parentInstance.isSelectOneElement = true;
|
||||
instance.type = 'select-one';
|
||||
instance.disable();
|
||||
});
|
||||
|
||||
|
@ -370,7 +401,9 @@ describe('components/container', () => {
|
|||
it('wraps passed element inside element', () => {
|
||||
expect(instance.element.querySelector('div#wrap-test')).to.equal(null);
|
||||
instance.wrap(document.querySelector('div#wrap-test'));
|
||||
expect(instance.element.querySelector('div#wrap-test')).to.equal(elementToWrap);
|
||||
expect(instance.element.querySelector('div#wrap-test')).to.equal(
|
||||
elementToWrap,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -389,10 +422,14 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
it('moves wrapped element outside of element', () => {
|
||||
expect(instance.element.querySelector('div#unwrap-test')).to.be.instanceof(HTMLElement);
|
||||
expect(
|
||||
instance.element.querySelector('div#unwrap-test'),
|
||||
).to.be.instanceof(HTMLElement);
|
||||
instance.unwrap(elementToUnwrap);
|
||||
expect(instance.element.querySelector('div#unwrap-test')).to.equal(null);
|
||||
expect(document.querySelector('div#unwrap-test')).to.be.instanceof(HTMLElement);
|
||||
expect(document.querySelector('div#unwrap-test')).to.be.instanceof(
|
||||
HTMLElement,
|
||||
);
|
||||
});
|
||||
|
||||
it('removes element from DOM', () => {
|
||||
|
@ -408,9 +445,13 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
it('adds loading state class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState)).to.equal(false);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState),
|
||||
).to.equal(false);
|
||||
instance.addLoadingState();
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState)).to.equal(true);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState),
|
||||
).to.equal(true);
|
||||
});
|
||||
|
||||
it('sets aria-busy attribute to true', () => {
|
||||
|
@ -432,9 +473,13 @@ describe('components/container', () => {
|
|||
});
|
||||
|
||||
it('removes loading state class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState)).to.equal(true);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState),
|
||||
).to.equal(true);
|
||||
instance.removeLoadingState();
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState)).to.equal(false);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState),
|
||||
).to.equal(false);
|
||||
});
|
||||
|
||||
it('removes aria-busy attribute', () => {
|
|
@ -1,10 +1,7 @@
|
|||
export default class Dropdown {
|
||||
constructor(instance, element, classNames) {
|
||||
this.parentInstance = instance;
|
||||
this.element = element;
|
||||
this.classNames = classNames;
|
||||
this.dimensions = null;
|
||||
this.position = null;
|
||||
constructor({ element, type, classNames }) {
|
||||
Object.assign(this, { element, type, classNames });
|
||||
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
|
@ -15,7 +12,9 @@ export default class Dropdown {
|
|||
*/
|
||||
distanceFromTopWindow() {
|
||||
this.dimensions = this.element.getBoundingClientRect();
|
||||
this.position = Math.ceil(this.dimensions.top + window.pageYOffset + this.element.offsetHeight);
|
||||
this.position = Math.ceil(
|
||||
this.dimensions.top + window.pageYOffset + this.element.offsetHeight,
|
||||
);
|
||||
return this.position;
|
||||
}
|
||||
|
||||
|
@ -36,7 +35,7 @@ export default class Dropdown {
|
|||
this.element.classList.add(this.classNames.activeState);
|
||||
this.element.setAttribute('aria-expanded', 'true');
|
||||
this.isActive = true;
|
||||
return this.parentInstance;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,6 +47,6 @@ export default class Dropdown {
|
|||
this.element.classList.remove(this.classNames.activeState);
|
||||
this.element.setAttribute('aria-expanded', 'false');
|
||||
this.isActive = false;
|
||||
return this.parentInstance;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,23 +1,20 @@
|
|||
import { expect } from 'chai';
|
||||
import sinon from 'sinon';
|
||||
import Dropdown from './dropdown';
|
||||
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
|
||||
import { DEFAULT_CLASSNAMES } from '../constants';
|
||||
|
||||
describe('components/dropdown', () => {
|
||||
let instance;
|
||||
let choicesInstance;
|
||||
let choicesElement;
|
||||
|
||||
beforeEach(() => {
|
||||
choicesInstance = {
|
||||
config: {
|
||||
...DEFAULT_CONFIG,
|
||||
},
|
||||
};
|
||||
|
||||
choicesElement = document.createElement('div');
|
||||
document.body.appendChild(choicesElement);
|
||||
instance = new Dropdown(choicesInstance, choicesElement, DEFAULT_CLASSNAMES);
|
||||
instance = new Dropdown({
|
||||
element: choicesElement,
|
||||
type: 'text',
|
||||
classNames: DEFAULT_CLASSNAMES,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -26,10 +23,6 @@ describe('components/dropdown', () => {
|
|||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('assigns choices instance to instance', () => {
|
||||
expect(instance.parentInstance).to.eql(choicesInstance);
|
||||
});
|
||||
|
||||
it('assigns choices element to instance', () => {
|
||||
expect(instance.element).to.eql(choicesElement);
|
||||
});
|
||||
|
@ -57,7 +50,9 @@ describe('components/dropdown', () => {
|
|||
width: 0,
|
||||
};
|
||||
|
||||
getBoundingClientRectStub = sinon.stub(instance.element, 'getBoundingClientRect').returns(dimensions);
|
||||
getBoundingClientRectStub = sinon
|
||||
.stub(instance.element, 'getBoundingClientRect')
|
||||
.returns(dimensions);
|
||||
|
||||
window.pageYOffset = 50;
|
||||
});
|
||||
|
@ -114,7 +109,9 @@ describe('components/dropdown', () => {
|
|||
});
|
||||
|
||||
it('adds active class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.activeState)).to.equal(true);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.activeState),
|
||||
).to.equal(true);
|
||||
});
|
||||
|
||||
it('sets expanded attribute', () => {
|
||||
|
@ -125,8 +122,8 @@ describe('components/dropdown', () => {
|
|||
expect(instance.isActive).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns parent instance', () => {
|
||||
expect(actualResponse).to.eql(choicesInstance);
|
||||
it('returns instance', () => {
|
||||
expect(actualResponse).to.eql(instance);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -142,7 +139,9 @@ describe('components/dropdown', () => {
|
|||
});
|
||||
|
||||
it('adds active class', () => {
|
||||
expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.activeState)).to.equal(false);
|
||||
expect(
|
||||
instance.element.classList.contains(DEFAULT_CLASSNAMES.activeState),
|
||||
).to.equal(false);
|
||||
});
|
||||
|
||||
it('sets expanded attribute', () => {
|
||||
|
@ -153,8 +152,8 @@ describe('components/dropdown', () => {
|
|||
expect(instance.isActive).to.equal(false);
|
||||
});
|
||||
|
||||
it('returns parent instance', () => {
|
||||
expect(actualResponse).to.eql(choicesInstance);
|
||||
it('returns instance', () => {
|
||||
expect(actualResponse).to.eql(instance);
|
||||
});
|
||||
});
|
||||
});
|
8
src/scripts/components/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import Dropdown from './dropdown';
|
||||
import Container from './container';
|
||||
import Input from './input';
|
||||
import List from './list';
|
||||
import WrappedInput from './wrapped-input';
|
||||
import WrappedSelect from './wrapped-select';
|
||||
|
||||
export { Dropdown, Container, Input, List, WrappedInput, WrappedSelect };
|
|
@ -1,18 +1,19 @@
|
|||
import { calcWidthOfInput } from '../lib/utils';
|
||||
|
||||
export default class Input {
|
||||
constructor(instance, element, classNames) {
|
||||
this.parentInstance = instance;
|
||||
constructor({ element, type, classNames, placeholderValue }) {
|
||||
Object.assign(this, { element, type, classNames, placeholderValue });
|
||||
|
||||
this.element = element;
|
||||
this.classNames = classNames;
|
||||
this.isFocussed = this.element === document.activeElement;
|
||||
this.isDisabled = false;
|
||||
|
||||
// Bind event listeners
|
||||
this.onPaste = this.onPaste.bind(this);
|
||||
this.onInput = this.onInput.bind(this);
|
||||
this.onFocus = this.onFocus.bind(this);
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
this._onPaste = this._onPaste.bind(this);
|
||||
this._onInput = this._onInput.bind(this);
|
||||
this._onFocus = this._onFocus.bind(this);
|
||||
this._onBlur = this._onBlur.bind(this);
|
||||
}
|
||||
|
||||
set placeholder(placeholder) {
|
||||
|
@ -28,55 +29,17 @@ export default class Input {
|
|||
}
|
||||
|
||||
addEventListeners() {
|
||||
this.element.addEventListener('input', this.onInput);
|
||||
this.element.addEventListener('paste', this.onPaste);
|
||||
this.element.addEventListener('focus', this.onFocus);
|
||||
this.element.addEventListener('blur', this.onBlur);
|
||||
this.element.addEventListener('input', this._onInput);
|
||||
this.element.addEventListener('paste', this._onPaste);
|
||||
this.element.addEventListener('focus', this._onFocus);
|
||||
this.element.addEventListener('blur', this._onBlur);
|
||||
}
|
||||
|
||||
removeEventListeners() {
|
||||
this.element.removeEventListener('input', this.onInput);
|
||||
this.element.removeEventListener('paste', this.onPaste);
|
||||
this.element.removeEventListener('focus', this.onFocus);
|
||||
this.element.removeEventListener('blur', this.onBlur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Input event
|
||||
* @return
|
||||
* @private
|
||||
*/
|
||||
onInput() {
|
||||
if (!this.parentInstance.isSelectOneElement) {
|
||||
this.setWidth();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paste event
|
||||
* @param {Object} e Event
|
||||
* @return
|
||||
* @private
|
||||
*/
|
||||
onPaste(e) {
|
||||
// Disable pasting into the input if option has been set
|
||||
if (e.target === this.element && !this.parentInstance.config.paste) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set focussed state
|
||||
*/
|
||||
onFocus() {
|
||||
this.isFocussed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove focussed state
|
||||
*/
|
||||
onBlur() {
|
||||
this.isFocussed = false;
|
||||
this.element.removeEventListener('input', this._onInput);
|
||||
this.element.removeEventListener('paste', this._onPaste);
|
||||
this.element.removeEventListener('focus', this._onFocus);
|
||||
this.element.removeEventListener('blur', this._onBlur);
|
||||
}
|
||||
|
||||
enable() {
|
||||
|
@ -115,7 +78,7 @@ export default class Input {
|
|||
this.setWidth();
|
||||
}
|
||||
|
||||
return this.parentInstance;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,12 +87,12 @@ export default class Input {
|
|||
* @return
|
||||
*/
|
||||
setWidth(enforceWidth) {
|
||||
if (this.parentInstance.placeholder) {
|
||||
if (this._placeholderValue) {
|
||||
// If there is a placeholder, we only want to set the width of the input when it is a greater
|
||||
// length than 75% of the placeholder. This stops the input jumping around.
|
||||
if (
|
||||
(this.element.value &&
|
||||
this.element.value.length >= (this.parentInstance.placeholder.length / 1.25)) ||
|
||||
this.element.value.length >= this._placeholderValue.length / 1.25) ||
|
||||
enforceWidth
|
||||
) {
|
||||
this.element.style.width = this.calcWidth();
|
||||
|
@ -151,4 +114,36 @@ export default class Input {
|
|||
removeActiveDescendant() {
|
||||
this.element.removeAttribute('aria-activedescendant');
|
||||
}
|
||||
|
||||
/**
|
||||
* Input event
|
||||
* @return
|
||||
* @private
|
||||
*/
|
||||
_onInput() {
|
||||
if (this.type !== 'select-one') {
|
||||
this.setWidth();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paste event
|
||||
* @param {Object} e Event
|
||||
* @return
|
||||
* @private
|
||||
*/
|
||||
_onPaste(e) {
|
||||
// Disable pasting into the input if option has been set
|
||||
if (e.target === this.element && this.preventPaste) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
_onFocus() {
|
||||
this.isFocussed = true;
|
||||
}
|
||||
|
||||
_onBlur() {
|
||||
this.isFocussed = false;
|
||||
}
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
import { expect } from 'chai';
|
||||
import { stub } from 'sinon';
|
||||
import Input from './input';
|
||||
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
|
||||
import { DEFAULT_CLASSNAMES } from '../constants';
|
||||
|
||||
describe('components/input', () => {
|
||||
let instance;
|
||||
let choicesInstance;
|
||||
let choicesElement;
|
||||
|
||||
beforeEach(() => {
|
||||
choicesInstance = {
|
||||
config: {
|
||||
...DEFAULT_CONFIG,
|
||||
},
|
||||
};
|
||||
choicesElement = document.createElement('input');
|
||||
instance = new Input(choicesInstance, choicesElement, DEFAULT_CLASSNAMES);
|
||||
instance = new Input({
|
||||
element: choicesElement,
|
||||
type: 'text',
|
||||
classNames: DEFAULT_CLASSNAMES,
|
||||
placeholderValue: null,
|
||||
preventPaste: false,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -24,10 +24,6 @@ describe('components/input', () => {
|
|||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('assigns choices instance to class', () => {
|
||||
expect(instance.parentInstance).to.eql(choicesInstance);
|
||||
});
|
||||
|
||||
it('assigns choices element to class', () => {
|
||||
expect(instance.element).to.eql(choicesElement);
|
||||
});
|
||||
|
@ -90,19 +86,18 @@ describe('components/input', () => {
|
|||
setWidthStub.restore();
|
||||
});
|
||||
|
||||
|
||||
describe('when element is select one', () => {
|
||||
it('does not set input width', () => {
|
||||
instance.parentInstance.isSelectOneElement = true;
|
||||
instance.onInput();
|
||||
instance.type = 'select-one';
|
||||
instance._onInput();
|
||||
expect(setWidthStub.callCount).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when element is not a select one', () => {
|
||||
it('sets input width', () => {
|
||||
instance.parentInstance.isSelectOneElement = false;
|
||||
instance.onInput();
|
||||
instance.type = 'text';
|
||||
instance._onInput();
|
||||
expect(setWidthStub.callCount).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
@ -120,16 +115,16 @@ describe('components/input', () => {
|
|||
|
||||
describe('when pasting is disabled and target is the element', () => {
|
||||
it('prevents default pasting behaviour', () => {
|
||||
instance.parentInstance.config.paste = false;
|
||||
instance.onPaste(eventMock);
|
||||
instance.preventPaste = true;
|
||||
instance._onPaste(eventMock);
|
||||
expect(eventMock.preventDefault.callCount).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when pasting is enabled', () => {
|
||||
it('does not prevent default pasting behaviour', () => {
|
||||
instance.parentInstance.config.paste = true;
|
||||
instance.onPaste(eventMock);
|
||||
instance.preventPaste = false;
|
||||
instance._onPaste(eventMock);
|
||||
expect(eventMock.preventDefault.callCount).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
@ -138,7 +133,7 @@ describe('components/input', () => {
|
|||
describe('onFocus', () => {
|
||||
it('sets isFocussed flag to true', () => {
|
||||
expect(instance.isFocussed).to.equal(false);
|
||||
instance.onFocus();
|
||||
instance._onFocus();
|
||||
expect(instance.isFocussed).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
@ -146,7 +141,7 @@ describe('components/input', () => {
|
|||
describe('onBlur', () => {
|
||||
it('sets isFocussed flag to false', () => {
|
||||
instance.isFocussed = true;
|
||||
instance.onBlur();
|
||||
instance._onBlur();
|
||||
expect(instance.isFocussed).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
@ -211,7 +206,6 @@ describe('components/input', () => {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
describe('blur', () => {
|
||||
let blurStub;
|
||||
|
||||
|
@ -224,7 +218,7 @@ describe('components/input', () => {
|
|||
});
|
||||
|
||||
describe('when element is not focussed', () => {
|
||||
it('doesn\'t blur element', () => {
|
||||
it("doesn't blur element", () => {
|
||||
instance.isFocussed = false;
|
||||
instance.blur();
|
||||
expect(blurStub.callCount).to.equal(0);
|
||||
|
@ -251,23 +245,22 @@ describe('components/input', () => {
|
|||
setWidthStub.restore();
|
||||
});
|
||||
|
||||
it('removes the element\'s value if it has one', () => {
|
||||
it("removes the element's value if it has one", () => {
|
||||
instance.element.value = 'test';
|
||||
expect(instance.element.value).to.equal('test');
|
||||
instance.clear();
|
||||
expect(instance.element.value).to.equal('');
|
||||
});
|
||||
|
||||
it('sets the element\'s width if flag passed', () => {
|
||||
it("sets the element's width if flag passed", () => {
|
||||
expect(setWidthStub.callCount).to.equal(0);
|
||||
instance.clear(true);
|
||||
expect(setWidthStub.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('returns parent instance', () => {
|
||||
const actualResponse = instance.clear();
|
||||
const expectedResponse = choicesInstance;
|
||||
expect(actualResponse).to.eql(expectedResponse);
|
||||
it('returns instance', () => {
|
||||
const response = instance.clear();
|
||||
expect(response).to.eql(instance);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -286,7 +279,7 @@ describe('components/input', () => {
|
|||
describe('with a placeholder', () => {
|
||||
describe('when value length is greater or equal to 75% of the placeholder length', () => {
|
||||
it('sets the width of the element based on input value', () => {
|
||||
instance.parentInstance.placeholder = 'This is a test';
|
||||
instance._placeholderValue = 'This is a test';
|
||||
instance.element.value = 'This is a test';
|
||||
expect(instance.element.style.width).to.not.equal(inputWidth);
|
||||
instance.setWidth();
|
||||
|
@ -297,7 +290,7 @@ describe('components/input', () => {
|
|||
|
||||
describe('when width is enforced', () => {
|
||||
it('sets the width of the element based on input value', () => {
|
||||
instance.parentInstance.placeholder = 'This is a test';
|
||||
instance._placeholderValue = 'This is a test';
|
||||
instance.element.value = '';
|
||||
expect(instance.element.style.width).to.not.equal(inputWidth);
|
||||
instance.setWidth(true);
|
||||
|
@ -308,7 +301,7 @@ describe('components/input', () => {
|
|||
|
||||
describe('when value length is less than 75% of the placeholder length', () => {
|
||||
it('does not set the width of the element', () => {
|
||||
instance.parentInstance.placeholder = 'This is a test';
|
||||
instance._placeholderValue = 'This is a test';
|
||||
instance.element.value = 'Test';
|
||||
instance.setWidth();
|
||||
expect(calcWidthStub.callCount).to.equal(0);
|
||||
|
@ -355,21 +348,32 @@ describe('components/input', () => {
|
|||
});
|
||||
|
||||
describe('setActiveDescendant', () => {
|
||||
it('sets element\'s aria-activedescendant attribute with passed descendant ID', () => {
|
||||
it("sets element's aria-activedescendant attribute with passed descendant ID", () => {
|
||||
const activeDescendantID = '1234';
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(null);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
|
||||
null,
|
||||
);
|
||||
instance.setActiveDescendant(activeDescendantID);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(activeDescendantID);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
|
||||
activeDescendantID,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeActiveDescendant', () => {
|
||||
it('remove elememnt\'s aria-activedescendant attribute', () => {
|
||||
it("remove elememnt's aria-activedescendant attribute", () => {
|
||||
const activeDescendantID = '1234';
|
||||
instance.element.setAttribute('aria-activedescendant', activeDescendantID);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(activeDescendantID);
|
||||
instance.element.setAttribute(
|
||||
'aria-activedescendant',
|
||||
activeDescendantID,
|
||||
);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
|
||||
activeDescendantID,
|
||||
);
|
||||
instance.removeActiveDescendant();
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(null);
|
||||
expect(instance.element.getAttribute('aria-activedescendant')).to.equal(
|
||||
null,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
89
src/scripts/components/list.js
Normal file
|
@ -0,0 +1,89 @@
|
|||
import { SCROLLING_SPEED } from '../constants';
|
||||
|
||||
export default class List {
|
||||
constructor({ element }) {
|
||||
Object.assign(this, { element });
|
||||
|
||||
this.scrollPos = this.element.scrollTop;
|
||||
this.height = this.element.offsetHeight;
|
||||
this.hasChildren = !!this.element.children;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.element.innerHTML = '';
|
||||
}
|
||||
|
||||
append(node) {
|
||||
this.element.appendChild(node);
|
||||
}
|
||||
|
||||
getChild(selector) {
|
||||
return this.element.querySelector(selector);
|
||||
}
|
||||
|
||||
scrollToTop() {
|
||||
this.element.scrollTop = 0;
|
||||
}
|
||||
|
||||
scrollToChoice(choice, direction) {
|
||||
if (!choice) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dropdownHeight = this.element.offsetHeight;
|
||||
const choiceHeight = choice.offsetHeight;
|
||||
// Distance from bottom of element to top of parent
|
||||
const choicePos = choice.offsetTop + choiceHeight;
|
||||
// Scroll position of dropdown
|
||||
const containerScrollPos = this.element.scrollTop + dropdownHeight;
|
||||
// Difference between the choice and scroll position
|
||||
const endpoint =
|
||||
direction > 0
|
||||
? this.element.scrollTop + choicePos - containerScrollPos
|
||||
: choice.offsetTop;
|
||||
|
||||
requestAnimationFrame(time => {
|
||||
this._animateScroll(time, endpoint, direction);
|
||||
});
|
||||
}
|
||||
|
||||
_scrollDown(scrollPos, strength, endpoint) {
|
||||
const easing = (endpoint - scrollPos) / strength;
|
||||
const distance = easing > 1 ? easing : 1;
|
||||
|
||||
this.element.scrollTop = scrollPos + distance;
|
||||
}
|
||||
|
||||
_scrollUp(scrollPos, strength, endpoint) {
|
||||
const easing = (scrollPos - endpoint) / strength;
|
||||
const distance = easing > 1 ? easing : 1;
|
||||
|
||||
this.element.scrollTop = scrollPos - distance;
|
||||
}
|
||||
|
||||
_animateScroll(time, endpoint, direction) {
|
||||
const strength = SCROLLING_SPEED;
|
||||
const choiceListScrollTop = this.element.scrollTop;
|
||||
let continueAnimation = false;
|
||||
|
||||
if (direction > 0) {
|
||||
this._scrollDown(choiceListScrollTop, strength, endpoint);
|
||||
|
||||
if (choiceListScrollTop < endpoint) {
|
||||
continueAnimation = true;
|
||||
}
|
||||
} else {
|
||||
this._scrollUp(choiceListScrollTop, strength, endpoint);
|
||||
|
||||
if (choiceListScrollTop > endpoint) {
|
||||
continueAnimation = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (continueAnimation) {
|
||||
requestAnimationFrame(time => {
|
||||
this._animateScroll(time, endpoint, direction);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,15 @@
|
|||
import { expect } from 'chai';
|
||||
import List from './list';
|
||||
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
|
||||
|
||||
describe('components/list', () => {
|
||||
let instance;
|
||||
let choicesInstance;
|
||||
let choicesElement;
|
||||
|
||||
beforeEach(() => {
|
||||
choicesInstance = {
|
||||
config: {
|
||||
...DEFAULT_CONFIG,
|
||||
},
|
||||
};
|
||||
choicesElement = document.createElement('div');
|
||||
instance = new List(choicesInstance, choicesElement, DEFAULT_CLASSNAMES);
|
||||
instance = new List({
|
||||
element: choicesElement,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -23,21 +18,13 @@ describe('components/list', () => {
|
|||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('assigns choices instance to class', () => {
|
||||
expect(instance.parentInstance).to.eql(choicesInstance);
|
||||
});
|
||||
|
||||
it('assigns choices element to class', () => {
|
||||
expect(instance.element).to.eql(choicesElement);
|
||||
});
|
||||
|
||||
it('assigns classnames to class', () => {
|
||||
expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clear', () => {
|
||||
it('clears element\'s inner HTML', () => {
|
||||
it("clears element's inner HTML", () => {
|
||||
const innerHTML = 'test';
|
||||
instance.element.innerHTML = innerHTML;
|
||||
expect(instance.element.innerHTML).to.equal(innerHTML);
|
||||
|
@ -46,25 +33,6 @@ describe('components/list', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('scrollTo', () => {
|
||||
describe('passing position', () => {
|
||||
it('scrolls element to passed position', () => {
|
||||
const scrollPosition = 20;
|
||||
expect(instance.element.scrollTop).to.equal(0);
|
||||
instance.scrollTo(scrollPosition);
|
||||
expect(instance.element.scrollTop).to.equal(scrollPosition);
|
||||
});
|
||||
});
|
||||
|
||||
describe('not passing position', () => {
|
||||
it('scrolls element to default position', () => {
|
||||
expect(instance.element.scrollTop).to.equal(0);
|
||||
instance.scrollTo();
|
||||
expect(instance.element.scrollTop).to.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('append', () => {
|
||||
it('appends passed node to element', () => {
|
||||
const elementToAppend = document.createElement('span');
|
||||
|
@ -72,11 +40,12 @@ describe('components/list', () => {
|
|||
elementToAppend.classList.add(childClass);
|
||||
expect(instance.element.querySelector(`.${childClass}`)).to.equal(null);
|
||||
instance.append(elementToAppend);
|
||||
expect(instance.element.querySelector(`.${childClass}`)).to.equal(elementToAppend);
|
||||
expect(instance.element.querySelector(`.${childClass}`)).to.equal(
|
||||
elementToAppend,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getChild', () => {
|
||||
let childElement;
|
||||
const childClass = 'test-element';
|
|
@ -1,10 +1,13 @@
|
|||
import { dispatchEvent } from '../lib/utils';
|
||||
import { dispatchEvent, isElement } from '../lib/utils';
|
||||
|
||||
export default class WrappedElement {
|
||||
constructor(instance, element, classNames) {
|
||||
this.parentInstance = instance;
|
||||
this.element = element;
|
||||
this.classNames = classNames;
|
||||
constructor({ element, classNames }) {
|
||||
Object.assign(this, { element, classNames });
|
||||
|
||||
if (!isElement(element)) {
|
||||
throw new TypeError('Invalid element passed');
|
||||
}
|
||||
|
||||
this.isDisabled = false;
|
||||
}
|
||||
|
|
@ -1,21 +1,17 @@
|
|||
import { expect } from 'chai';
|
||||
import WrappedElement from './wrapped-element';
|
||||
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
|
||||
import { DEFAULT_CLASSNAMES } from '../constants';
|
||||
|
||||
describe('components/wrappedElement', () => {
|
||||
let instance;
|
||||
let choicesInstance;
|
||||
let element;
|
||||
|
||||
beforeEach(() => {
|
||||
choicesInstance = {
|
||||
config: {
|
||||
...DEFAULT_CONFIG,
|
||||
},
|
||||
};
|
||||
|
||||
element = document.createElement('select');
|
||||
instance = new WrappedElement(choicesInstance, element, DEFAULT_CLASSNAMES);
|
||||
instance = new WrappedElement({
|
||||
element,
|
||||
classNames: DEFAULT_CLASSNAMES
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -24,10 +20,6 @@ describe('components/wrappedElement', () => {
|
|||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('assigns choices instance to class', () => {
|
||||
expect(instance.parentInstance).to.eql(choicesInstance);
|
||||
});
|
||||
|
||||
it('assigns choices element to class', () => {
|
||||
expect(instance.element).to.eql(element);
|
||||
});
|
||||
|
@ -58,11 +50,17 @@ describe('components/wrappedElement', () => {
|
|||
it('hides element', () => {
|
||||
instance.conceal();
|
||||
expect(instance.element.tabIndex).to.equal(-1);
|
||||
expect(instance.element.classList.contains(instance.classNames.input)).to.equal(true);
|
||||
expect(instance.element.classList.contains(instance.classNames.hiddenState)).to.equal(true);
|
||||
expect(
|
||||
instance.element.classList.contains(instance.classNames.input)
|
||||
).to.equal(true);
|
||||
expect(
|
||||
instance.element.classList.contains(instance.classNames.hiddenState)
|
||||
).to.equal(true);
|
||||
expect(instance.element.getAttribute('aria-hidden')).to.equal('true');
|
||||
expect(instance.element.getAttribute('data-choice')).to.equal('active');
|
||||
expect(instance.element.getAttribute('data-choice-orig-style')).to.equal(originalStyling);
|
||||
expect(instance.element.getAttribute('data-choice-orig-style')).to.equal(
|
||||
originalStyling
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -77,12 +75,18 @@ describe('components/wrappedElement', () => {
|
|||
it('shows element', () => {
|
||||
instance.reveal();
|
||||
expect(instance.element.tabIndex).to.equal(0);
|
||||
expect(instance.element.classList.contains(instance.classNames.input)).to.equal(false);
|
||||
expect(instance.element.classList.contains(instance.classNames.hiddenState)).to.equal(false);
|
||||
expect(
|
||||
instance.element.classList.contains(instance.classNames.input)
|
||||
).to.equal(false);
|
||||
expect(
|
||||
instance.element.classList.contains(instance.classNames.hiddenState)
|
||||
).to.equal(false);
|
||||
expect(instance.element.getAttribute('style')).to.equal(originalStyling);
|
||||
expect(instance.element.getAttribute('aria-hidden')).to.equal(null);
|
||||
expect(instance.element.getAttribute('data-choice')).to.equal(null);
|
||||
expect(instance.element.getAttribute('data-choice-orig-style')).to.equal(null);
|
||||
expect(instance.element.getAttribute('data-choice-orig-style')).to.equal(
|
||||
null
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -135,9 +139,9 @@ describe('components/wrappedElement', () => {
|
|||
});
|
||||
|
||||
describe('triggerEvent', () => {
|
||||
it('fires event on element using passed eventType and data', (done) => {
|
||||
it('fires event on element using passed eventType and data', done => {
|
||||
const data = {
|
||||
test: true,
|
||||
test: true
|
||||
};
|
||||
|
||||
instance.element.addEventListener('testEvent', ({ detail }) => {
|
|
@ -2,16 +2,14 @@ import WrappedElement from './wrapped-element';
|
|||
import { reduceToValues } from './../lib/utils';
|
||||
|
||||
export default class WrappedInput extends WrappedElement {
|
||||
constructor(instance, element, classNames) {
|
||||
super(instance, element, classNames);
|
||||
this.parentInstance = instance;
|
||||
this.element = element;
|
||||
this.classNames = classNames;
|
||||
constructor({ element, classNames, delimiter }) {
|
||||
super({ element, classNames });
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
set value(items) {
|
||||
const itemsFiltered = reduceToValues(items);
|
||||
const itemsFilteredString = itemsFiltered.join(this.parentInstance.config.delimiter);
|
||||
const itemsFilteredString = itemsFiltered.join(this.delimiter);
|
||||
|
||||
this.element.setAttribute('value', itemsFilteredString);
|
||||
this.element.value = itemsFilteredString;
|
|
@ -2,21 +2,20 @@ import { expect } from 'chai';
|
|||
import { stub } from 'sinon';
|
||||
import WrappedElement from './wrapped-element';
|
||||
import WrappedInput from './wrapped-input';
|
||||
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
|
||||
import { DEFAULT_CLASSNAMES } from '../constants';
|
||||
|
||||
describe('components/wrappedInput', () => {
|
||||
let instance;
|
||||
let choicesInstance;
|
||||
let element;
|
||||
const delimiter = '-';
|
||||
|
||||
beforeEach(() => {
|
||||
choicesInstance = {
|
||||
config: {
|
||||
...DEFAULT_CONFIG,
|
||||
},
|
||||
};
|
||||
element = document.createElement('input');
|
||||
instance = new WrappedInput(choicesInstance, element, DEFAULT_CLASSNAMES);
|
||||
instance = new WrappedInput({
|
||||
element,
|
||||
classNames: DEFAULT_CLASSNAMES,
|
||||
delimiter,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -25,10 +24,6 @@ describe('components/wrappedInput', () => {
|
|||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('assigns choices instance to class', () => {
|
||||
expect(instance.parentInstance).to.eql(choicesInstance);
|
||||
});
|
||||
|
||||
it('assigns choices element to class', () => {
|
||||
expect(instance.element).to.eql(element);
|
||||
});
|
||||
|
@ -39,7 +34,7 @@ describe('components/wrappedInput', () => {
|
|||
});
|
||||
|
||||
describe('inherited methods', () => {
|
||||
['conceal', 'reveal', 'enable', 'disable'].forEach((method) => {
|
||||
['conceal', 'reveal', 'enable', 'disable'].forEach(method => {
|
||||
describe(method, () => {
|
||||
beforeEach(() => {
|
||||
stub(WrappedElement.prototype, method);
|
||||
|
@ -77,7 +72,9 @@ describe('components/wrappedInput', () => {
|
|||
it('sets delimited value of element based on passed data', () => {
|
||||
expect(instance.element.value).to.equal('');
|
||||
instance.value = data;
|
||||
expect(instance.value).to.equal('Value 1,Value 2,Value 3');
|
||||
expect(instance.value).to.equal(
|
||||
`Value 1${delimiter}Value 2${delimiter}Value 3`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -2,11 +2,8 @@ import WrappedElement from './wrapped-element';
|
|||
import templates from './../templates';
|
||||
|
||||
export default class WrappedSelect extends WrappedElement {
|
||||
constructor(instance, element, classNames) {
|
||||
super(instance, element, classNames);
|
||||
this.parentInstance = instance;
|
||||
this.element = element;
|
||||
this.classNames = classNames;
|
||||
constructor({ element, classNames }) {
|
||||
super({ element, classNames });
|
||||
}
|
||||
|
||||
get placeholderOption() {
|
||||
|
@ -23,7 +20,7 @@ export default class WrappedSelect extends WrappedElement {
|
|||
|
||||
set options(options) {
|
||||
const fragment = document.createDocumentFragment();
|
||||
const addOptionToFragment = (data) => {
|
||||
const addOptionToFragment = data => {
|
||||
// Create a standard select option
|
||||
const template = templates.option(data);
|
||||
// Append it to fragment
|
|
@ -2,20 +2,13 @@ import { expect } from 'chai';
|
|||
import { stub } from 'sinon';
|
||||
import WrappedElement from './wrapped-element';
|
||||
import WrappedSelect from './wrapped-select';
|
||||
import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from '../constants';
|
||||
import { DEFAULT_CLASSNAMES } from '../constants';
|
||||
|
||||
describe('components/wrappedSelect', () => {
|
||||
let instance;
|
||||
let choicesInstance;
|
||||
let element;
|
||||
|
||||
beforeEach(() => {
|
||||
choicesInstance = {
|
||||
config: {
|
||||
...DEFAULT_CONFIG,
|
||||
},
|
||||
};
|
||||
|
||||
element = document.createElement('select');
|
||||
element.id = 'target';
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
|
@ -32,7 +25,10 @@ describe('components/wrappedSelect', () => {
|
|||
}
|
||||
document.body.appendChild(element);
|
||||
|
||||
instance = new WrappedSelect(choicesInstance, document.getElementById('target'), DEFAULT_CLASSNAMES);
|
||||
instance = new WrappedSelect({
|
||||
element: document.getElementById('target'),
|
||||
classNames: DEFAULT_CLASSNAMES,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -41,10 +37,6 @@ describe('components/wrappedSelect', () => {
|
|||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('assigns choices instance to class', () => {
|
||||
expect(instance.parentInstance).to.eql(choicesInstance);
|
||||
});
|
||||
|
||||
it('assigns choices element to class', () => {
|
||||
expect(instance.element).to.eql(element);
|
||||
});
|
||||
|
@ -55,7 +47,7 @@ describe('components/wrappedSelect', () => {
|
|||
});
|
||||
|
||||
describe('inherited methods', () => {
|
||||
['conceal', 'reveal', 'enable', 'disable'].forEach((method) => {
|
||||
['conceal', 'reveal', 'enable', 'disable'].forEach(method => {
|
||||
beforeEach(() => {
|
||||
stub(WrappedElement.prototype, method);
|
||||
});
|
||||
|
@ -84,7 +76,7 @@ describe('components/wrappedSelect', () => {
|
|||
it('returns all option elements', () => {
|
||||
const { options } = instance;
|
||||
expect(options).to.be.an('array');
|
||||
options.forEach((option) => {
|
||||
options.forEach(option => {
|
||||
expect(option).to.be.instanceOf(HTMLOptionElement);
|
||||
});
|
||||
});
|
||||
|
@ -99,7 +91,7 @@ describe('components/wrappedSelect', () => {
|
|||
|
||||
const { optionGroups } = instance;
|
||||
expect(optionGroups.length).to.equal(3);
|
||||
optionGroups.forEach((option) => {
|
||||
optionGroups.forEach(option => {
|
||||
expect(option).to.be.instanceOf(HTMLOptGroupElement);
|
||||
});
|
||||
});
|
||||
|
@ -149,9 +141,13 @@ describe('components/wrappedSelect', () => {
|
|||
|
||||
describe('appendDocFragment', () => {
|
||||
it('empties contents of element', () => {
|
||||
expect(instance.element.getElementsByTagName('option').length).to.equal(4);
|
||||
expect(instance.element.getElementsByTagName('option').length).to.equal(
|
||||
4,
|
||||
);
|
||||
instance.appendDocFragment(document.createDocumentFragment());
|
||||
expect(instance.element.getElementsByTagName('option').length).to.equal(0);
|
||||
expect(instance.element.getElementsByTagName('option').length).to.equal(
|
||||
0,
|
||||
);
|
||||
});
|
||||
|
||||
it('appends passed fragment to element', () => {
|
||||
|
@ -161,7 +157,9 @@ describe('components/wrappedSelect', () => {
|
|||
fragment.appendChild(elementToAppend);
|
||||
expect(instance.element.querySelector('#fragment-target')).to.equal(null);
|
||||
instance.appendDocFragment(fragment);
|
||||
expect(instance.element.querySelector('#fragment-target')).to.eql(elementToAppend);
|
||||
expect(instance.element.querySelector('#fragment-target')).to.eql(
|
||||
elementToAppend,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -65,7 +65,7 @@ export const DEFAULT_CONFIG = {
|
|||
uniqueItemText: 'Only unique values can be added.',
|
||||
addItemText: value => `Press Enter to add <b>"${stripHTML(value)}"</b>`,
|
||||
maxItemText: maxItemCount => `Only ${maxItemCount} values can be added.`,
|
||||
itemComparer: (choice, item) => (choice === item),
|
||||
itemComparer: (choice, item) => choice === item,
|
||||
fuseOptions: {
|
||||
includeScore: true,
|
||||
},
|
|
@ -140,7 +140,7 @@ describe('constants', () => {
|
|||
});
|
||||
|
||||
it('exports each value as a number', () => {
|
||||
Object.keys(KEY_CODES).forEach((key) => {
|
||||
Object.keys(KEY_CODES).forEach(key => {
|
||||
expect(KEY_CODES[key]).to.be.a('number');
|
||||
});
|
||||
});
|
1
src/scripts/dist/choices.min.js.map
vendored
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"choices.min.js","sources":[],"mappings":";;;","sourceRoot":""}
|
2
src/scripts/lib/polyfills.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import 'core-js/fn/array/from';
|
||||
import 'custom-event-polyfill';
|
|
@ -1,14 +1,5 @@
|
|||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Capitalises the first letter of each word in a string
|
||||
* @param {String} str String to capitalise
|
||||
* @return {String} Capitalised string
|
||||
*/
|
||||
export const capitalise = function(str) {
|
||||
return str.replace(/\w\S*/g, txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a string of random chars
|
||||
* @param {Number} length Length of the string to generate
|
||||
|
@ -39,7 +30,6 @@ export const generateId = function(element, prefix) {
|
|||
return id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tests the type of an object
|
||||
* @param {String} type Type to test object against
|
||||
|
@ -61,25 +51,15 @@ export const isType = function(type, obj) {
|
|||
return obj !== undefined && obj !== null && clas === type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests to see if a passed object is a node
|
||||
* @param {Object} obj Object to be tested
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const isNode = o => (
|
||||
typeof Node === 'object' ? o instanceof Node :
|
||||
o && typeof o === 'object' && typeof o.nodeType === 'number' && typeof o.nodeName === 'string'
|
||||
);
|
||||
|
||||
/**
|
||||
* Tests to see if a passed object is an element
|
||||
* @param {Object} obj Object to be tested
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const isElement = o => (
|
||||
typeof HTMLElement === 'object' ? o instanceof HTMLElement : // DOM2
|
||||
o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string'
|
||||
);
|
||||
typeof HTMLElement === 'object' ? o instanceof HTMLElement : // DOM2
|
||||
o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string'
|
||||
);
|
||||
|
||||
/**
|
||||
* Merges unspecified amount of objects into new object
|
||||
|
@ -121,132 +101,6 @@ export const extend = function() {
|
|||
return extended;
|
||||
};
|
||||
|
||||
/**
|
||||
* CSS transition end event listener
|
||||
* @return
|
||||
*/
|
||||
export const whichTransitionEvent = function() {
|
||||
let t,
|
||||
el = document.createElement('fakeelement');
|
||||
|
||||
const transitions = {
|
||||
transition: 'transitionend',
|
||||
OTransition: 'oTransitionEnd',
|
||||
MozTransition: 'transitionend',
|
||||
WebkitTransition: 'webkitTransitionEnd',
|
||||
};
|
||||
|
||||
for (t in transitions) {
|
||||
if (el.style[t] !== undefined) {
|
||||
return transitions[t];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* CSS animation end event listener
|
||||
* @return
|
||||
*/
|
||||
export const whichAnimationEvent = function() {
|
||||
let t,
|
||||
el = document.createElement('fakeelement');
|
||||
|
||||
const animations = {
|
||||
animation: 'animationend',
|
||||
OAnimation: 'oAnimationEnd',
|
||||
MozAnimation: 'animationend',
|
||||
WebkitAnimation: 'webkitAnimationEnd',
|
||||
};
|
||||
|
||||
for (t in animations) {
|
||||
if (el.style[t] !== undefined) {
|
||||
return animations[t];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the ancestors of each element in the current set of matched elements,
|
||||
* up to but not including the element matched by the selector
|
||||
* @param {NodeElement} elem Element to begin search from
|
||||
* @param {NodeElement} parent Parent to find
|
||||
* @param {String} selector Class to find
|
||||
* @return {Array} Array of parent elements
|
||||
*/
|
||||
export const getParentsUntil = function(elem, parent, selector) {
|
||||
const parents = [];
|
||||
// Get matches
|
||||
for (; elem && elem !== document; elem = elem.parentNode) {
|
||||
// Check if parent has been reached
|
||||
if (parent) {
|
||||
const parentType = parent.charAt(0);
|
||||
|
||||
// If parent is a class
|
||||
if (parentType === '.') {
|
||||
if (elem.classList.contains(parent.substr(1))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If parent is an ID
|
||||
if (parentType === '#') {
|
||||
if (elem.id === parent.substr(1)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If parent is a data attribute
|
||||
if (parentType === '[') {
|
||||
if (elem.hasAttribute(parent.substr(1, parent.length - 1))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If parent is a tag
|
||||
if (elem.tagName.toLowerCase() === parent) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (selector) {
|
||||
const selectorType = selector.charAt(0);
|
||||
|
||||
// If selector is a class
|
||||
if (selectorType === '.') {
|
||||
if (elem.classList.contains(selector.substr(1))) {
|
||||
parents.push(elem);
|
||||
}
|
||||
}
|
||||
|
||||
// If selector is an ID
|
||||
if (selectorType === '#') {
|
||||
if (elem.id === selector.substr(1)) {
|
||||
parents.push(elem);
|
||||
}
|
||||
}
|
||||
|
||||
// If selector is a data attribute
|
||||
if (selectorType === '[') {
|
||||
if (elem.hasAttribute(selector.substr(1, selector.length - 1))) {
|
||||
parents.push(elem);
|
||||
}
|
||||
}
|
||||
|
||||
// If selector is a tag
|
||||
if (elem.tagName.toLowerCase() === selector) {
|
||||
parents.push(elem);
|
||||
}
|
||||
} else {
|
||||
parents.push(elem);
|
||||
}
|
||||
}
|
||||
|
||||
// Return parents if any exist
|
||||
if (parents.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return parents;
|
||||
};
|
||||
|
||||
export const wrap = function(element, wrapper) {
|
||||
wrapper = wrapper || document.createElement('div');
|
||||
if (element.nextSibling) {
|
||||
|
@ -257,17 +111,6 @@ export const wrap = function(element, wrapper) {
|
|||
return wrapper.appendChild(element);
|
||||
};
|
||||
|
||||
export const getSiblings = function(elem) {
|
||||
const siblings = [];
|
||||
let sibling = elem.parentNode.firstChild;
|
||||
for (; sibling; sibling = sibling.nextSibling) {
|
||||
if (sibling.nodeType === 1 && sibling !== elem) {
|
||||
siblings.push(sibling);
|
||||
}
|
||||
}
|
||||
return siblings;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find ancestor in DOM tree
|
||||
* @param {NodeElement} el Element to start search from
|
||||
|
@ -299,60 +142,6 @@ export const findAncestorByAttrName = function(el, attr) {
|
|||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Debounce an event handler.
|
||||
* @param {Function} func Function to run after wait
|
||||
* @param {Number} wait The delay before the function is executed
|
||||
* @param {Boolean} immediate If passed, trigger the function on the leading edge, instead of the trailing.
|
||||
* @return {Function} A function will be called after it stops being called for a given delay
|
||||
*/
|
||||
export const debounce = function(func, wait, immediate) {
|
||||
let timeout;
|
||||
return function() {
|
||||
let context = this,
|
||||
args = arguments;
|
||||
const later = function() {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
const callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an element's distance from the top of the page
|
||||
* @private
|
||||
* @param {NodeElement} el Element to test for
|
||||
* @return {Number} Elements Distance from top of page
|
||||
*/
|
||||
export const getElemDistance = function(el) {
|
||||
let location = 0;
|
||||
if (el.offsetParent) {
|
||||
do {
|
||||
location += el.offsetTop;
|
||||
el = el.offsetParent;
|
||||
} while (el);
|
||||
}
|
||||
return location >= 0 ? location : 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine element height multiplied by any offsets
|
||||
* @private
|
||||
* @param {HTMLElement} el Element to test for
|
||||
* @return {Number} Height of element
|
||||
*/
|
||||
export const getElementOffset = function(el, offset) {
|
||||
let elOffset = offset;
|
||||
if (elOffset > 1) elOffset = 1;
|
||||
if (elOffset > 0) elOffset = 0;
|
||||
|
||||
return Math.max(el.offsetHeight * elOffset);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the next or previous element from a given start point
|
||||
* @param {HTMLElement} startEl Element to start position from
|
||||
|
@ -372,31 +161,6 @@ export const getAdjacentEl = (startEl, className, direction = 1) => {
|
|||
return children[startPos + operatorDirection];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get scroll position based on top/bottom position
|
||||
* @private
|
||||
* @return {String} Position of scroll
|
||||
*/
|
||||
export const getScrollPosition = function(position) {
|
||||
if (position === 'bottom') {
|
||||
// Scroll position from the bottom of the viewport
|
||||
return Math.max((window.scrollY || window.pageYOffset) + (window.innerHeight || document.documentElement.clientHeight));
|
||||
}
|
||||
// Scroll position from the top of the viewport
|
||||
return (window.scrollY || window.pageYOffset);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine whether an element is within the viewport
|
||||
* @param {HTMLElement} el Element to test
|
||||
* @return {String} Position of scroll
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const isInView = function(el, position, offset) {
|
||||
// If the user has scrolled further than the distance from the element to the top of its parent
|
||||
return this.getScrollPosition(position) > (this.getElemDistance(el) + this.getElementOffset(el, offset));
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine whether an element is within
|
||||
* @param {HTMLElement} el Element to test
|
||||
|
@ -431,25 +195,6 @@ export const stripHTML = html =>
|
|||
.replace(/</g, '<')
|
||||
.replace(/"/g, '"');
|
||||
|
||||
/**
|
||||
* Adds animation to an element and removes it upon animation completion
|
||||
* @param {Element} el Element to add animation to
|
||||
* @param {String} animation Animation class to add to element
|
||||
* @return
|
||||
*/
|
||||
export const addAnimation = (el, animation) => {
|
||||
const animationEvent = whichAnimationEvent();
|
||||
|
||||
const removeAnimation = () => {
|
||||
el.classList.remove(animation);
|
||||
el.removeEventListener(animationEvent, removeAnimation, false);
|
||||
};
|
||||
|
||||
el.classList.add(animation);
|
||||
el.addEventListener(animationEvent, removeAnimation, false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a random number between a range
|
||||
* @param {Number} min Minimum range
|
||||
|
@ -617,4 +362,18 @@ export const fetchFromObject = function (object, properties){
|
|||
}
|
||||
|
||||
return object[properties];
|
||||
};
|
||||
|
||||
export const isIE11 = () => {
|
||||
return !!(navigator.userAgent.match(/Trident/) && navigator.userAgent.match(/rv[ :]11/));
|
||||
};
|
||||
|
||||
export const existsInArray = (array, value) => {
|
||||
return array.some((item) => {
|
||||
if (isType('String', value)) {
|
||||
return item.value === value.trim();
|
||||
}
|
||||
|
||||
return item.value === value;
|
||||
})
|
||||
};
|
|
@ -43,11 +43,7 @@ describe('utils', () => {
|
|||
];
|
||||
|
||||
it('returns an array of item values', () => {
|
||||
const expectedResponse = [
|
||||
items[0].value,
|
||||
items[1].value,
|
||||
items[2].value,
|
||||
];
|
||||
const expectedResponse = [items[0].value, items[1].value, items[2].value];
|
||||
|
||||
const actualResponse = reduceToValues(items);
|
||||
expect(actualResponse).to.eql(expectedResponse);
|
|
@ -8,26 +8,29 @@ export default function choices(state = defaultState, action) {
|
|||
A selected choice has been added to the passed input's value (added as an item)
|
||||
An active choice appears within the choice dropdown
|
||||
*/
|
||||
return [...state, {
|
||||
id: action.id,
|
||||
elementId: action.elementId,
|
||||
groupId: action.groupId,
|
||||
value: action.value,
|
||||
label: (action.label || action.value),
|
||||
disabled: (action.disabled || false),
|
||||
selected: false,
|
||||
active: true,
|
||||
score: 9999,
|
||||
customProperties: action.customProperties,
|
||||
placeholder: (action.placeholder || false),
|
||||
keyCode: null,
|
||||
}];
|
||||
return [
|
||||
...state,
|
||||
{
|
||||
id: action.id,
|
||||
elementId: action.elementId,
|
||||
groupId: action.groupId,
|
||||
value: action.value,
|
||||
label: action.label || action.value,
|
||||
disabled: action.disabled || false,
|
||||
selected: false,
|
||||
active: true,
|
||||
score: 9999,
|
||||
customProperties: action.customProperties,
|
||||
placeholder: action.placeholder || false,
|
||||
keyCode: null,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
case 'ADD_ITEM': {
|
||||
// If all choices need to be activated
|
||||
if (action.activateOptions) {
|
||||
return state.map((obj) => {
|
||||
return state.map(obj => {
|
||||
const choice = obj;
|
||||
choice.active = action.active;
|
||||
return choice;
|
||||
|
@ -37,7 +40,7 @@ export default function choices(state = defaultState, action) {
|
|||
// When an item is added and it has an associated choice,
|
||||
// we want to disable it so it can't be chosen again
|
||||
if (action.choiceId > -1) {
|
||||
return state.map((obj) => {
|
||||
return state.map(obj => {
|
||||
const choice = obj;
|
||||
if (choice.id === parseInt(action.choiceId, 10)) {
|
||||
choice.selected = true;
|
||||
|
@ -53,7 +56,7 @@ export default function choices(state = defaultState, action) {
|
|||
// When an item is removed and it has an associated choice,
|
||||
// we want to re-enable it so it can be chosen again
|
||||
if (action.choiceId > -1) {
|
||||
return state.map((obj) => {
|
||||
return state.map(obj => {
|
||||
const choice = obj;
|
||||
if (choice.id === parseInt(action.choiceId, 10)) {
|
||||
choice.selected = false;
|
||||
|
@ -66,7 +69,7 @@ export default function choices(state = defaultState, action) {
|
|||
}
|
||||
|
||||
case 'FILTER_CHOICES': {
|
||||
return state.map((obj) => {
|
||||
return state.map(obj => {
|
||||
const choice = obj;
|
||||
// Set active state based on whether choice is
|
||||
// within filtered results
|
||||
|
@ -83,7 +86,7 @@ export default function choices(state = defaultState, action) {
|
|||
}
|
||||
|
||||
case 'ACTIVATE_CHOICES': {
|
||||
return state.map((obj) => {
|
||||
return state.map(obj => {
|
||||
const choice = obj;
|
||||
choice.active = action.active;
|
||||
return choice;
|
|
@ -174,15 +174,16 @@ describe('reducers/choices', () => {
|
|||
score,
|
||||
};
|
||||
|
||||
|
||||
const actualResponse = choices(state, {
|
||||
type: 'FILTER_CHOICES',
|
||||
results: [{
|
||||
item: {
|
||||
id,
|
||||
results: [
|
||||
{
|
||||
item: {
|
||||
id,
|
||||
},
|
||||
score,
|
||||
},
|
||||
score,
|
||||
}],
|
||||
],
|
||||
}).find(choice => choice.id === id);
|
||||
|
||||
expect(actualResponse).to.eql(expectedResponse);
|
|
@ -3,12 +3,15 @@ export const defaultState = [];
|
|||
export default function groups(state = defaultState, action) {
|
||||
switch (action.type) {
|
||||
case 'ADD_GROUP': {
|
||||
return [...state, {
|
||||
id: action.id,
|
||||
value: action.value,
|
||||
active: action.active,
|
||||
disabled: action.disabled,
|
||||
}];
|
||||
return [
|
||||
...state,
|
||||
{
|
||||
id: action.id,
|
||||
value: action.value,
|
||||
active: action.active,
|
||||
disabled: action.disabled,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
case 'CLEAR_CHOICES': {
|
|
@ -4,20 +4,23 @@ export default function items(state = defaultState, action) {
|
|||
switch (action.type) {
|
||||
case 'ADD_ITEM': {
|
||||
// Add object to items array
|
||||
const newState = [...state, {
|
||||
id: action.id,
|
||||
choiceId: action.choiceId,
|
||||
groupId: action.groupId,
|
||||
value: action.value,
|
||||
label: action.label,
|
||||
active: true,
|
||||
highlighted: false,
|
||||
customProperties: action.customProperties,
|
||||
placeholder: (action.placeholder || false),
|
||||
keyCode: null,
|
||||
}];
|
||||
const newState = [
|
||||
...state,
|
||||
{
|
||||
id: action.id,
|
||||
choiceId: action.choiceId,
|
||||
groupId: action.groupId,
|
||||
value: action.value,
|
||||
label: action.label,
|
||||
active: true,
|
||||
highlighted: false,
|
||||
customProperties: action.customProperties,
|
||||
placeholder: action.placeholder || false,
|
||||
keyCode: null,
|
||||
},
|
||||
];
|
||||
|
||||
return newState.map((obj) => {
|
||||
return newState.map(obj => {
|
||||
const item = obj;
|
||||
item.highlighted = false;
|
||||
return item;
|
||||
|
@ -26,7 +29,7 @@ export default function items(state = defaultState, action) {
|
|||
|
||||
case 'REMOVE_ITEM': {
|
||||
// Set item to inactive
|
||||
return state.map((obj) => {
|
||||
return state.map(obj => {
|
||||
const item = obj;
|
||||
if (item.id === action.id) {
|
||||
item.active = false;
|
||||
|
@ -36,7 +39,7 @@ export default function items(state = defaultState, action) {
|
|||
}
|
||||
|
||||
case 'HIGHLIGHT_ITEM': {
|
||||
return state.map((obj) => {
|
||||
return state.map(obj => {
|
||||
const item = obj;
|
||||
if (item.id === action.id) {
|
||||
item.highlighted = action.highlighted;
|
|
@ -56,7 +56,7 @@ describe('reducers/items', () => {
|
|||
});
|
||||
|
||||
it('unhighlights all highlighted items', () => {
|
||||
actualResponse.forEach((item) => {
|
||||
actualResponse.forEach(item => {
|
||||
expect(item.highlighted).to.equal(false);
|
||||
});
|
||||
});
|
|
@ -1,38 +0,0 @@
|
|||
export default class List {
|
||||
constructor(instance, element, classNames) {
|
||||
this.parentInstance = instance;
|
||||
this.element = element;
|
||||
this.classNames = classNames;
|
||||
this.scrollPos = this.element.scrollTop;
|
||||
this.height = this.element.offsetHeight;
|
||||
this.hasChildren = !!this.element.children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear List contents
|
||||
*/
|
||||
clear() {
|
||||
this.element.innerHTML = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to passed position on Y axis
|
||||
*/
|
||||
scrollTo(scrollPos = 0) {
|
||||
this.element.scrollTop = scrollPos;
|
||||
}
|
||||
/**
|
||||
* Append node to element
|
||||
*/
|
||||
append(node) {
|
||||
this.element.appendChild(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find element that matches passed selector
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
getChild(selector) {
|
||||
return this.element.querySelector(selector);
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
/* eslint-disable */
|
||||
(function () {
|
||||
// Production steps of ECMA-262, Edition 6, 22.1.2.1
|
||||
// Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from
|
||||
if (!Array.from) {
|
||||
Array.from = (function() {
|
||||
let toStr = Object.prototype.toString;
|
||||
|
||||
let isCallable = function(fn) {
|
||||
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
|
||||
};
|
||||
|
||||
let toInteger = function(value) {
|
||||
let number = Number(value);
|
||||
if (isNaN(number)) {
|
||||
return 0;
|
||||
}
|
||||
if (number === 0 || !isFinite(number)) {
|
||||
return number;
|
||||
}
|
||||
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
|
||||
};
|
||||
|
||||
let maxSafeInteger = Math.pow(2, 53) - 1;
|
||||
|
||||
let toLength = function(value) {
|
||||
let len = toInteger(value);
|
||||
return Math.min(Math.max(len, 0), maxSafeInteger);
|
||||
};
|
||||
|
||||
// The length property of the from method is 1.
|
||||
return function from(arrayLike /* , mapFn, thisArg */) {
|
||||
// 1. Let C be the this value.
|
||||
let C = this;
|
||||
|
||||
// 2. Let items be ToObject(arrayLike).
|
||||
let items = Object(arrayLike);
|
||||
|
||||
// 3. ReturnIfAbrupt(items).
|
||||
if (arrayLike == null) {
|
||||
throw new TypeError('Array.from requires an array-like object - not null or undefined');
|
||||
}
|
||||
|
||||
// 4. If mapfn is undefined, then let mapping be false.
|
||||
let mapFn = arguments.length > 1 ? arguments[1] : void undefined;
|
||||
let T;
|
||||
if (typeof mapFn !== 'undefined') {
|
||||
// 5. else
|
||||
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
|
||||
if (!isCallable(mapFn)) {
|
||||
throw new TypeError('Array.from: when provided, the second argument must be a function');
|
||||
}
|
||||
|
||||
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
if (arguments.length > 2) {
|
||||
T = arguments[2];
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Let lenValue be Get(items, "length").
|
||||
// 11. Let len be ToLength(lenValue).
|
||||
let len = toLength(items.length);
|
||||
|
||||
// 13. If IsConstructor(C) is true, then
|
||||
// 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
|
||||
// 14. a. Else, Let A be ArrayCreate(len).
|
||||
let A = isCallable(C) ? Object(new C(len)) : new Array(len);
|
||||
|
||||
// 16. Let k be 0.
|
||||
let k = 0;
|
||||
// 17. Repeat, while k < len… (also steps a - h)
|
||||
let kValue;
|
||||
while (k < len) {
|
||||
kValue = items[k];
|
||||
if (mapFn) {
|
||||
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
|
||||
} else {
|
||||
A[k] = kValue;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
// 18. Let putStatus be Put(A, "length", len, true).
|
||||
A.length = len;
|
||||
// 20. Return A.
|
||||
return A;
|
||||
};
|
||||
}());
|
||||
}
|
||||
|
||||
// Reference: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/find
|
||||
if (!Array.prototype.find) {
|
||||
Array.prototype.find = function(predicate) {
|
||||
'use strict';
|
||||
|
||||
if (this == null) {
|
||||
throw new TypeError('Array.prototype.find called on null or undefined');
|
||||
}
|
||||
if (typeof predicate !== 'function') {
|
||||
throw new TypeError('predicate must be a function');
|
||||
}
|
||||
let list = Object(this);
|
||||
let length = list.length >>> 0;
|
||||
let thisArg = arguments[1];
|
||||
let value;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
value = list[i];
|
||||
if (predicate.call(thisArg, value, i, list)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
|
||||
function CustomEvent (event, params) {
|
||||
params = params || {
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
detail: undefined,
|
||||
};
|
||||
let evt = document.createEvent('CustomEvent');
|
||||
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
|
||||
return evt;
|
||||
}
|
||||
|
||||
CustomEvent.prototype = window.Event.prototype;
|
||||
|
||||
window.CustomEvent = CustomEvent;
|
||||
}());
|
|
@ -3,11 +3,9 @@ import rootReducer from './../reducers/index';
|
|||
|
||||
export default class Store {
|
||||
constructor() {
|
||||
this.store = createStore(
|
||||
this._store = createStore(
|
||||
rootReducer,
|
||||
window.devToolsExtension ?
|
||||
window.devToolsExtension() :
|
||||
undefined,
|
||||
window.devToolsExtension ? window.devToolsExtension() : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -17,7 +15,7 @@ export default class Store {
|
|||
* @return
|
||||
*/
|
||||
subscribe(onChange) {
|
||||
this.store.subscribe(onChange);
|
||||
this._store.subscribe(onChange);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,7 +24,7 @@ export default class Store {
|
|||
* @return
|
||||
*/
|
||||
dispatch(action) {
|
||||
this.store.dispatch(action);
|
||||
this._store.dispatch(action);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -34,7 +32,7 @@ export default class Store {
|
|||
* @return {Object} State
|
||||
*/
|
||||
get state() {
|
||||
return this.store.getState();
|
||||
return this._store.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,9 +52,9 @@ export default class Store {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get highlighted items from store
|
||||
* @return {Array} Item objects
|
||||
*/
|
||||
* Get highlighted items from store
|
||||
* @return {Array} Item objects
|
||||
*/
|
||||
get highlightedActiveItems() {
|
||||
return this.items.filter(item => item.active && item.highlighted);
|
||||
}
|
||||
|
@ -122,10 +120,10 @@ export default class Store {
|
|||
const groups = this.groups;
|
||||
const choices = this.choices;
|
||||
|
||||
return groups.filter((group) => {
|
||||
return groups.filter(group => {
|
||||
const isActive = group.active === true && group.disabled === false;
|
||||
const hasActiveOptions = choices.some(choice =>
|
||||
choice.active === true && choice.disabled === false,
|
||||
const hasActiveOptions = choices.some(
|
||||
choice => choice.active === true && choice.disabled === false,
|
||||
);
|
||||
return isActive && hasActiveOptions;
|
||||
}, []);
|
||||
|
@ -138,7 +136,9 @@ export default class Store {
|
|||
getChoiceById(id) {
|
||||
if (id) {
|
||||
const choices = this.activeChoices;
|
||||
const foundChoice = choices.find(choice => choice.id === parseInt(id, 10));
|
||||
const foundChoice = choices.find(
|
||||
choice => choice.id === parseInt(id, 10),
|
||||
);
|
||||
return foundChoice;
|
||||
}
|
||||
return false;
|
|
@ -10,9 +10,9 @@ describe('reducers/store', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
instance = new Store();
|
||||
subscribeStub = sinon.stub(instance.store, 'subscribe');
|
||||
dispatchStub = sinon.stub(instance.store, 'dispatch');
|
||||
getStateStub = sinon.stub(instance.store, 'getState');
|
||||
subscribeStub = sinon.stub(instance._store, 'subscribe');
|
||||
dispatchStub = sinon.stub(instance._store, 'dispatch');
|
||||
getStateStub = sinon.stub(instance._store, 'getState');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -21,10 +21,9 @@ describe('reducers/store', () => {
|
|||
getStateStub.restore();
|
||||
});
|
||||
|
||||
|
||||
describe('constructor', () => {
|
||||
it('creates redux store', () => {
|
||||
expect(instance.store).to.contain.keys([
|
||||
expect(instance._store).to.contain.keys([
|
||||
'subscribe',
|
||||
'dispatch',
|
||||
'getState',
|
||||
|
@ -32,7 +31,6 @@ describe('reducers/store', () => {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
describe('subscribe', () => {
|
||||
it('wraps redux subscribe method', () => {
|
||||
const onChange = () => {};
|
||||
|
@ -163,14 +161,16 @@ describe('reducers/store', () => {
|
|||
|
||||
describe('activeItems getter', () => {
|
||||
it('returns items that are active', () => {
|
||||
const expectedResponse = state.items.filter((item => item.active));
|
||||
const expectedResponse = state.items.filter(item => item.active);
|
||||
expect(instance.activeItems).to.eql(expectedResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe('highlightedActiveItems getter', () => {
|
||||
it('returns items that are active and highlighted', () => {
|
||||
const expectedResponse = state.items.filter((item => item.highlighted && item.active));
|
||||
const expectedResponse = state.items.filter(
|
||||
item => item.highlighted && item.active,
|
||||
);
|
||||
expect(instance.highlightedActiveItems).to.eql(expectedResponse);
|
||||
});
|
||||
});
|
||||
|
@ -184,21 +184,25 @@ describe('reducers/store', () => {
|
|||
|
||||
describe('activeChoices getter', () => {
|
||||
it('returns choices that are active', () => {
|
||||
const expectedResponse = state.choices.filter((choice => choice.active));
|
||||
const expectedResponse = state.choices.filter(choice => choice.active);
|
||||
expect(instance.activeChoices).to.eql(expectedResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectableChoices getter', () => {
|
||||
it('returns choices that are not disabled', () => {
|
||||
const expectedResponse = state.choices.filter((choice => !choice.disabled));
|
||||
const expectedResponse = state.choices.filter(
|
||||
choice => !choice.disabled,
|
||||
);
|
||||
expect(instance.selectableChoices).to.eql(expectedResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchableChoices getter', () => {
|
||||
it('returns choices that are not placeholders and are selectable', () => {
|
||||
const expectedResponse = state.choices.filter((choice => !choice.disabled && !choice.placeholder));
|
||||
const expectedResponse = state.choices.filter(
|
||||
choice => !choice.disabled && !choice.placeholder,
|
||||
);
|
||||
expect(instance.searchableChoices).to.eql(expectedResponse);
|
||||
});
|
||||
});
|
||||
|
@ -207,7 +211,9 @@ describe('reducers/store', () => {
|
|||
describe('passing id', () => {
|
||||
it('returns active choice by passed id', () => {
|
||||
const id = '1';
|
||||
const expectedResponse = state.choices.find((choice => choice.id === parseInt(id, 10)));
|
||||
const expectedResponse = state.choices.find(
|
||||
choice => choice.id === parseInt(id, 10),
|
||||
);
|
||||
const actualResponse = instance.getChoiceById(id);
|
||||
expect(actualResponse).to.eql(expectedResponse);
|
||||
});
|
||||
|
@ -223,7 +229,9 @@ describe('reducers/store', () => {
|
|||
|
||||
describe('placeholderChoice getter', () => {
|
||||
it('returns placeholder choice', () => {
|
||||
const expectedResponse = state.choices.reverse().find(choice => choice.placeholder);
|
||||
const expectedResponse = state.choices
|
||||
.reverse()
|
||||
.find(choice => choice.placeholder);
|
||||
expect(instance.getPlaceholderChoice).to.eql(expectedResponse);
|
||||
});
|
||||
});
|
||||
|
@ -245,7 +253,9 @@ describe('reducers/store', () => {
|
|||
describe('getGroupById', () => {
|
||||
it('returns group by id', () => {
|
||||
const id = '1';
|
||||
const expectedResponse = state.groups.find((group => group.id === parseInt(id, 10)));
|
||||
const expectedResponse = state.groups.find(
|
||||
group => group.id === parseInt(id, 10),
|
||||
);
|
||||
const actualResponse = instance.getGroupById(id);
|
||||
expect(actualResponse).to.eql(expectedResponse);
|
||||
});
|
|
@ -39,13 +39,10 @@ export const TEMPLATES = {
|
|||
`);
|
||||
},
|
||||
itemList(globalClasses, isSelectOneElement) {
|
||||
const localClasses = classNames(
|
||||
globalClasses.list,
|
||||
{
|
||||
[globalClasses.listSingle]: (isSelectOneElement),
|
||||
[globalClasses.listItems]: (!isSelectOneElement),
|
||||
},
|
||||
);
|
||||
const localClasses = classNames(globalClasses.list, {
|
||||
[globalClasses.listSingle]: isSelectOneElement,
|
||||
[globalClasses.listItems]: !isSelectOneElement,
|
||||
});
|
||||
|
||||
return strToEl(`
|
||||
<div class="${localClasses}"></div>
|
||||
|
@ -62,22 +59,18 @@ export const TEMPLATES = {
|
|||
const ariaSelected = data.active ? 'aria-selected="true"' : '';
|
||||
const ariaDisabled = data.disabled ? 'aria-disabled="true"' : '';
|
||||
|
||||
let localClasses = classNames(
|
||||
globalClasses.item, {
|
||||
[globalClasses.highlightedState]: data.highlighted,
|
||||
[globalClasses.itemSelectable]: !data.highlighted,
|
||||
[globalClasses.placeholder]: data.placeholder,
|
||||
},
|
||||
);
|
||||
let localClasses = classNames(globalClasses.item, {
|
||||
[globalClasses.highlightedState]: data.highlighted,
|
||||
[globalClasses.itemSelectable]: !data.highlighted,
|
||||
[globalClasses.placeholder]: data.placeholder,
|
||||
});
|
||||
|
||||
if (removeItemButton) {
|
||||
localClasses = classNames(
|
||||
globalClasses.item, {
|
||||
[globalClasses.highlightedState]: data.highlighted,
|
||||
[globalClasses.itemSelectable]: !data.disabled,
|
||||
[globalClasses.placeholder]: data.placeholder,
|
||||
},
|
||||
);
|
||||
localClasses = classNames(globalClasses.item, {
|
||||
[globalClasses.highlightedState]: data.highlighted,
|
||||
[globalClasses.itemSelectable]: !data.disabled,
|
||||
[globalClasses.placeholder]: data.placeholder,
|
||||
});
|
||||
|
||||
return strToEl(`
|
||||
<div
|
||||
|
@ -116,9 +109,9 @@ export const TEMPLATES = {
|
|||
`);
|
||||
},
|
||||
choiceList(globalClasses, isSelectOneElement) {
|
||||
const ariaMultiSelectable = !isSelectOneElement ?
|
||||
'aria-multiselectable="true"' :
|
||||
'';
|
||||
const ariaMultiSelectable = !isSelectOneElement
|
||||
? 'aria-multiselectable="true"'
|
||||
: '';
|
||||
|
||||
return strToEl(`
|
||||
<div
|
||||
|
@ -132,11 +125,9 @@ export const TEMPLATES = {
|
|||
},
|
||||
choiceGroup(globalClasses, data) {
|
||||
const ariaDisabled = data.disabled ? 'aria-disabled="true"' : '';
|
||||
const localClasses = classNames(
|
||||
globalClasses.group, {
|
||||
[globalClasses.itemDisabled]: data.disabled,
|
||||
},
|
||||
);
|
||||
const localClasses = classNames(globalClasses.group, {
|
||||
[globalClasses.itemDisabled]: data.disabled,
|
||||
});
|
||||
|
||||
return strToEl(`
|
||||
<div
|
||||
|
@ -155,7 +146,8 @@ export const TEMPLATES = {
|
|||
const role = data.groupId > 0 ? 'role="treeitem"' : 'role="option"';
|
||||
const localClasses = classNames(
|
||||
globalClasses.item,
|
||||
globalClasses.itemChoice, {
|
||||
globalClasses.itemChoice,
|
||||
{
|
||||
[globalClasses.itemDisabled]: data.disabled,
|
||||
[globalClasses.itemSelectable]: !data.disabled,
|
||||
[globalClasses.placeholder]: data.placeholder,
|
||||
|
@ -169,9 +161,10 @@ export const TEMPLATES = {
|
|||
data-choice
|
||||
data-id="${data.id}"
|
||||
data-value="${data.value}"
|
||||
${data.disabled ?
|
||||
'data-choice-disabled aria-disabled="true"' :
|
||||
'data-choice-selectable'
|
||||
${
|
||||
data.disabled
|
||||
? 'data-choice-disabled aria-disabled="true"'
|
||||
: 'data-choice-selectable'
|
||||
}
|
||||
id="${data.elementId}"
|
||||
${role}
|
||||
|
@ -217,8 +210,8 @@ export const TEMPLATES = {
|
|||
globalClasses.item,
|
||||
globalClasses.itemChoice,
|
||||
{
|
||||
[globalClasses.noResults]: (type === 'no-results'),
|
||||
[globalClasses.noChoices]: (type === 'no-choices'),
|
||||
[globalClasses.noResults]: type === 'no-results',
|
||||
[globalClasses.noChoices]: type === 'no-choices',
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -230,7 +223,9 @@ export const TEMPLATES = {
|
|||
},
|
||||
option(data) {
|
||||
return strToEl(`
|
||||
<option value="${data.value}" ${data.selected ? 'selected' : ''} ${data.disabled ? 'disabled' : ''}>${data.label}</option>
|
||||
<option value="${data.value}" ${data.selected ? 'selected' : ''} ${
|
||||
data.disabled ? 'disabled' : ''
|
||||
}>${data.label}</option>
|
||||
`);
|
||||
},
|
||||
};
|
|
@ -2,7 +2,8 @@ import { expect } from 'chai';
|
|||
import templates from './templates';
|
||||
import { getType, strToEl } from './lib/utils';
|
||||
|
||||
const stripElement = element => element.outerHTML.replace(/(^|>)\s+|\s+(?=<|$)/g, '$1');
|
||||
const stripElement = element =>
|
||||
element.outerHTML.replace(/(^|>)\s+|\s+(?=<|$)/g, '$1');
|
||||
|
||||
describe('templates', () => {
|
||||
describe('containerOuter', () => {
|
||||
|
@ -41,7 +42,9 @@ describe('templates', () => {
|
|||
);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -73,7 +76,9 @@ describe('templates', () => {
|
|||
);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -106,7 +111,9 @@ describe('templates', () => {
|
|||
);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -138,7 +145,9 @@ describe('templates', () => {
|
|||
);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -148,7 +157,9 @@ describe('templates', () => {
|
|||
const classes = {
|
||||
containerInner: 'test',
|
||||
};
|
||||
const expectedOutput = strToEl(`<div class="${classes.containerInner}"></div>`);
|
||||
const expectedOutput = strToEl(
|
||||
`<div class="${classes.containerInner}"></div>`,
|
||||
);
|
||||
const actualOutput = templates.containerInner(classes);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
|
@ -165,21 +176,29 @@ describe('templates', () => {
|
|||
|
||||
describe('select one element', () => {
|
||||
it('returns expected html', () => {
|
||||
const expectedOutput = strToEl(`<div class="${classes.list} ${classes.listSingle}"></div>`);
|
||||
const expectedOutput = strToEl(
|
||||
`<div class="${classes.list} ${classes.listSingle}"></div>`,
|
||||
);
|
||||
const actualOutput = templates.itemList(classes, true);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('non select one element', () => {
|
||||
it('returns expected html', () => {
|
||||
const expectedOutput = strToEl(`<div class="${classes.list} ${classes.listItems}"></div>`);
|
||||
const expectedOutput = strToEl(
|
||||
`<div class="${classes.list} ${classes.listItems}"></div>`,
|
||||
);
|
||||
const actualOutput = templates.itemList(classes, false);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -191,8 +210,7 @@ describe('templates', () => {
|
|||
};
|
||||
const value = 'test';
|
||||
const expectedOutput = strToEl(`
|
||||
<div class="${classes.placeholder}">${value}</div>`,
|
||||
);
|
||||
<div class="${classes.placeholder}">${value}</div>`);
|
||||
const actualOutput = templates.placeholder(classes, value);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
|
@ -222,7 +240,9 @@ describe('templates', () => {
|
|||
const actualOutput = templates.choiceList(classes, true);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -240,7 +260,9 @@ describe('templates', () => {
|
|||
const actualOutput = templates.choiceList(classes, false);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -278,7 +300,9 @@ describe('templates', () => {
|
|||
const actualOutput = templates.choiceGroup(classes, data);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -306,7 +330,9 @@ describe('templates', () => {
|
|||
const actualOutput = templates.choiceGroup(classes, data);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -339,7 +365,9 @@ describe('templates', () => {
|
|||
it('returns expected html', () => {
|
||||
const expectedOutput = strToEl(`
|
||||
<div
|
||||
class="${classes.item} ${classes.itemChoice} ${classes.itemSelectable}"
|
||||
class="${classes.item} ${classes.itemChoice} ${
|
||||
classes.itemSelectable
|
||||
}"
|
||||
data-select-text="${itemSelectText}"
|
||||
data-choice
|
||||
data-id="${data.id}"
|
||||
|
@ -354,7 +382,9 @@ describe('templates', () => {
|
|||
const actualOutput = templates.choice(classes, data, itemSelectText);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -369,7 +399,9 @@ describe('templates', () => {
|
|||
it('returns expected html', () => {
|
||||
const expectedOutput = strToEl(`
|
||||
<div
|
||||
class="${classes.item} ${classes.itemChoice} ${classes.itemDisabled}"
|
||||
class="${classes.item} ${classes.itemChoice} ${
|
||||
classes.itemDisabled
|
||||
}"
|
||||
data-select-text="${itemSelectText}"
|
||||
data-choice
|
||||
data-id="${data.id}"
|
||||
|
@ -385,7 +417,9 @@ describe('templates', () => {
|
|||
const actualOutput = templates.choice(classes, data, itemSelectText);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -400,7 +434,9 @@ describe('templates', () => {
|
|||
it('returns expected html', () => {
|
||||
const expectedOutput = strToEl(`
|
||||
<div
|
||||
class="${classes.item} ${classes.itemChoice} ${classes.itemSelectable} ${classes.placeholder}"
|
||||
class="${classes.item} ${classes.itemChoice} ${
|
||||
classes.itemSelectable
|
||||
} ${classes.placeholder}"
|
||||
data-select-text="${itemSelectText}"
|
||||
data-choice
|
||||
data-id="${data.id}"
|
||||
|
@ -415,7 +451,9 @@ describe('templates', () => {
|
|||
const actualOutput = templates.choice(classes, data, itemSelectText);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -423,14 +461,16 @@ describe('templates', () => {
|
|||
beforeEach(() => {
|
||||
data = {
|
||||
...data,
|
||||
groupId: 1
|
||||
groupId: 1,
|
||||
};
|
||||
});
|
||||
|
||||
it('returns expected html', () => {
|
||||
const expectedOutput = strToEl(`
|
||||
<div
|
||||
class="${classes.item} ${classes.itemChoice} ${classes.itemSelectable}"
|
||||
class="${classes.item} ${classes.itemChoice} ${
|
||||
classes.itemSelectable
|
||||
}"
|
||||
data-select-text="${itemSelectText}"
|
||||
data-choice
|
||||
data-id="${data.id}"
|
||||
|
@ -445,7 +485,9 @@ describe('templates', () => {
|
|||
const actualOutput = templates.choice(classes, data, itemSelectText);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -479,11 +521,14 @@ describe('templates', () => {
|
|||
const classes = {
|
||||
list: 'test 1',
|
||||
listDropdown: 'test 2',
|
||||
}
|
||||
;
|
||||
};
|
||||
it('returns expected html', () => {
|
||||
const value = 'test';
|
||||
const expectedOutput = strToEl(`<div class="${classes.list} ${classes.listDropdown}" aria-expanded="false"></div>`);
|
||||
const expectedOutput = strToEl(
|
||||
`<div class="${classes.list} ${
|
||||
classes.listDropdown
|
||||
}" aria-expanded="false"></div>`,
|
||||
);
|
||||
const actualOutput = templates.dropdown(classes, value);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLDivElement');
|
||||
|
@ -517,26 +562,34 @@ describe('templates', () => {
|
|||
describe('no results', () => {
|
||||
it('adds no results classname', () => {
|
||||
const expectedOutput = strToEl(`
|
||||
<div class="${classes.item} ${classes.itemChoice} ${classes.noResults}">
|
||||
<div class="${classes.item} ${classes.itemChoice} ${
|
||||
classes.noResults
|
||||
}">
|
||||
${label}
|
||||
</div>
|
||||
`);
|
||||
const actualOutput = templates.notice(classes, label, 'no-results');
|
||||
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('no choices', () => {
|
||||
it('adds no choices classname', () => {
|
||||
const expectedOutput = strToEl(`
|
||||
<div class="${classes.item} ${classes.itemChoice} ${classes.noChoices}">
|
||||
<div class="${classes.item} ${classes.itemChoice} ${
|
||||
classes.noChoices
|
||||
}">
|
||||
${label}
|
||||
</div>
|
||||
`);
|
||||
const actualOutput = templates.notice(classes, label, 'no-choices');
|
||||
|
||||
expect(stripElement(actualOutput)).to.equal(stripElement(expectedOutput));
|
||||
expect(stripElement(actualOutput)).to.equal(
|
||||
stripElement(expectedOutput),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -555,7 +608,11 @@ describe('templates', () => {
|
|||
});
|
||||
|
||||
it('returns expected html', () => {
|
||||
const expectedOutput = strToEl(`<option value="${data.value}" ${data.selected ? 'selected' : ''} ${data.disabled ? 'disabled' : ''}>${data.label}</option>`);
|
||||
const expectedOutput = strToEl(
|
||||
`<option value="${data.value}" ${data.selected ? 'selected' : ''} ${
|
||||
data.disabled ? 'disabled' : ''
|
||||
}>${data.label}</option>`,
|
||||
);
|
||||
const actualOutput = templates.option(data);
|
||||
|
||||
expect(getType(actualOutput)).to.equal('HTMLOptionElement');
|
|
@ -1,27 +1,28 @@
|
|||
$global-guttering: 24px;
|
||||
$global-font-size-h1: 32px;
|
||||
$global-font-size-h2: 24px;
|
||||
$global-font-size-h3: 20px;
|
||||
$global-font-size-h4: 18px;
|
||||
$global-font-size-h5: 16px;
|
||||
$global-font-size-h6: 14px;
|
||||
|
||||
/*=============================================
|
||||
= Generic styling =
|
||||
=============================================*/
|
||||
|
||||
$global-guttering : 24px;
|
||||
$global-font-size-h1 : 32px;
|
||||
$global-font-size-h2 : 24px;
|
||||
$global-font-size-h3 : 20px;
|
||||
$global-font-size-h4 : 18px;
|
||||
$global-font-size-h5 : 16px;
|
||||
$global-font-size-h6 : 14px;
|
||||
|
||||
* {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale
|
||||
}
|
||||
|
||||
|
||||
*, *:before, *:after {
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
|
||||
html, body {
|
||||
html,
|
||||
body {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
|
@ -45,7 +46,9 @@ label {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
p { margin-top: 0; }
|
||||
p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
|
@ -55,14 +58,21 @@ hr {
|
|||
height: 1px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: $global-guttering/2;
|
||||
font-weight: 400;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
a, a:visited, a:focus {
|
||||
a,
|
||||
a:visited,
|
||||
a:focus {
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
|
@ -81,26 +91,55 @@ a, a:visited, a:focus {
|
|||
margin-bottom: $global-guttering;
|
||||
}
|
||||
|
||||
h1, .h1 { font-size: $global-font-size-h1; }
|
||||
h2, .h2 { font-size: $global-font-size-h2; }
|
||||
h3, .h3 { font-size: $global-font-size-h3; }
|
||||
h4, .h4 { font-size: $global-font-size-h4; }
|
||||
h5, .h5 { font-size: $global-font-size-h5; }
|
||||
h6, .h6 { font-size: $global-font-size-h6; }
|
||||
h1,
|
||||
.h1 {
|
||||
font-size: $global-font-size-h1;
|
||||
}
|
||||
|
||||
h2,
|
||||
.h2 {
|
||||
font-size: $global-font-size-h2;
|
||||
}
|
||||
|
||||
h3,
|
||||
.h3 {
|
||||
font-size: $global-font-size-h3;
|
||||
}
|
||||
|
||||
h4,
|
||||
.h4 {
|
||||
font-size: $global-font-size-h4;
|
||||
}
|
||||
|
||||
h5,
|
||||
.h5 {
|
||||
font-size: $global-font-size-h5;
|
||||
}
|
||||
|
||||
h6,
|
||||
.h6 {
|
||||
font-size: $global-font-size-h6;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: block;
|
||||
margin: auto;
|
||||
max-width: 40em;
|
||||
padding: $global-guttering*2;
|
||||
@media (max-width: 620px) { padding: 0; }
|
||||
@media (max-width: 620px) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
background-color: #FFFFFF;
|
||||
padding: $global-guttering;
|
||||
color: #333;
|
||||
a, a:visited, a:focus { color: #00bcd4; }
|
||||
a,
|
||||
a:visited,
|
||||
a:focus {
|
||||
color: #00bcd4;
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
|
@ -117,10 +156,24 @@ h6, .h6 { font-size: $global-font-size-h6; }
|
|||
padding: $global-guttering/4 0;
|
||||
}
|
||||
|
||||
.visible-ie { display: none; }
|
||||
.zero-bottom { margin-bottom: 0; }
|
||||
.zero-top { margin-top: 0; }
|
||||
.text-center { text-align: center; }
|
||||
.is-hidden { display: none; }
|
||||
.visible-ie {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*===== End of Section comment block ======*/
|
||||
.zero-bottom {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.zero-top {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*===== End of Section comment block ======*/
|
|
@ -26,21 +26,30 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
position: relative;
|
||||
margin-bottom: $choices-guttering;
|
||||
font-size: $choices-font-size-lg;
|
||||
&:focus { outline: none; }
|
||||
&:last-child { margin-bottom: 0; }
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&.is-disabled {
|
||||
.#{$choices-selector}__inner, .#{$choices-selector}__input {
|
||||
.#{$choices-selector}__inner,
|
||||
.#{$choices-selector}__input {
|
||||
background-color: $choices-bg-color-disabled;
|
||||
cursor: not-allowed;
|
||||
user-select: none;
|
||||
}
|
||||
.#{$choices-selector}__item { cursor: not-allowed; }
|
||||
.#{$choices-selector}__item {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$choices-selector}[data-type*="select-one"] {
|
||||
cursor: pointer;
|
||||
.#{$choices-selector}__inner { padding-bottom: 7.5px; }
|
||||
.#{$choices-selector}__inner {
|
||||
padding-bottom: 7.5px;
|
||||
}
|
||||
.#{$choices-selector}__input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
@ -62,8 +71,13 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
width: 20px;
|
||||
border-radius: 10em;
|
||||
opacity: .5;
|
||||
&:hover, &:focus { opacity: 1; }
|
||||
&:focus { box-shadow: 0px 0px 0px 2px $choices-highlight-color; }
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
&:focus {
|
||||
box-shadow: 0px 0px 0px 2px $choices-highlight-color;
|
||||
}
|
||||
}
|
||||
&:after {
|
||||
content: "";
|
||||
|
@ -96,8 +110,11 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
}
|
||||
}
|
||||
|
||||
.#{$choices-selector}[data-type*="select-multiple"], .#{$choices-selector}[data-type*="text"] {
|
||||
.#{$choices-selector}__inner { cursor: text; }
|
||||
.#{$choices-selector}[data-type*="select-multiple"],
|
||||
.#{$choices-selector}[data-type*="text"] {
|
||||
.#{$choices-selector}__inner {
|
||||
cursor: text;
|
||||
}
|
||||
.#{$choices-selector}__button {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
@ -113,7 +130,10 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
line-height: 1;
|
||||
opacity: .75;
|
||||
border-radius: 0;
|
||||
&:hover, &:focus { opacity: 1; }
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,9 +148,16 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
font-size: $choices-font-size-md;
|
||||
min-height: 44px;
|
||||
overflow: hidden;
|
||||
.is-focused &, .is-open & { border-color: darken($choices-keyline-color, 15%); }
|
||||
.is-open & { border-radius: $choices-border-radius $choices-border-radius 0 0; }
|
||||
.is-flipped.is-open & { border-radius: 0 0 $choices-border-radius $choices-border-radius; }
|
||||
.is-focused &,
|
||||
.is-open & {
|
||||
border-color: darken($choices-keyline-color, 15%);
|
||||
}
|
||||
.is-open & {
|
||||
border-radius: $choices-border-radius $choices-border-radius 0 0;
|
||||
}
|
||||
.is-flipped.is-open & {
|
||||
border-radius: 0 0 $choices-border-radius $choices-border-radius;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$choices-selector}__list {
|
||||
|
@ -147,7 +174,9 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
padding-right: 4px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
.#{$choices-selector}__item { width: 100%; }
|
||||
.#{$choices-selector}__item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$choices-selector}__list--multiple {
|
||||
|
@ -165,7 +194,9 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
border: 1px solid darken($choices-primary-color, 5%);
|
||||
color: #FFFFFF;
|
||||
word-break: break-all;
|
||||
&[data-deletable] { padding-right: 5px; }
|
||||
&[data-deletable] {
|
||||
padding-right: 5px;
|
||||
}
|
||||
[dir="rtl"] & {
|
||||
margin-right: 0;
|
||||
margin-left: 3.75px;
|
||||
|
@ -194,8 +225,12 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
border-bottom-right-radius: $choices-border-radius;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
&.is-active { display: block; }
|
||||
.is-open & { border-color: darken($choices-keyline-color, 15%); }
|
||||
&.is-active {
|
||||
display: block;
|
||||
}
|
||||
.is-open & {
|
||||
border-color: darken($choices-keyline-color, 15%);
|
||||
}
|
||||
.is-flipped & {
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
|
@ -214,7 +249,9 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
position: relative;
|
||||
padding: 10px;
|
||||
font-size: $choices-font-size-md;
|
||||
[dir="rtl"] & { text-align: right; }
|
||||
[dir="rtl"] & {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
.#{$choices-selector}__item--selectable {
|
||||
@media (min-width: 640px) {
|
||||
|
@ -240,13 +277,21 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
}
|
||||
&.is-highlighted {
|
||||
background-color: mix(#000000, #FFFFFF, 5%);
|
||||
&:after { opacity: .5; }
|
||||
&:after {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$choices-selector}__item { cursor: default; }
|
||||
.#{$choices-selector}__item--selectable { cursor: pointer; }
|
||||
.#{$choices-selector}__item {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.#{$choices-selector}__item--selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.#{$choices-selector}__item--disabled {
|
||||
cursor: not-allowed;
|
||||
user-select: none;
|
||||
|
@ -270,7 +315,9 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
cursor: pointer;
|
||||
&:focus { outline: none; }
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$choices-selector}__input {
|
||||
|
@ -283,14 +330,18 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
border-radius: 0;
|
||||
max-width: 100%;
|
||||
padding: 4px 0 4px 2px;
|
||||
&:focus { outline: 0; }
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
[dir="rtl"] & {
|
||||
padding-right: 2px;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$choices-selector}__placeholder { opacity: .5; }
|
||||
.#{$choices-selector}__placeholder {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.#{$choices-selector}__input.is-hidden,
|
||||
.#{$choices-selector}[data-type*="select-one"] .#{$choices-selector}__input.is-hidden,
|
||||
|
@ -298,4 +349,4 @@ $choices-icon-cross-inverse: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiI
|
|||
display: none;
|
||||
}
|
||||
|
||||
/*===== End of Choices ======*/
|
||||
/*===== End of Choices ======*/
|
2
types/index.d.ts
vendored
|
@ -917,7 +917,7 @@ export default class Choices {
|
|||
private createChoicesFragment(choices: any[], fragment: DocumentFragment, withinGroup?: boolean): DocumentFragment;
|
||||
|
||||
/** Render items into a DOM fragment and append to items list */
|
||||
private createItemsFragment(items: any[], fragment?: DocumentFragment): void;
|
||||
private _createItemsFragment(items: any[], fragment?: DocumentFragment): void;
|
||||
|
||||
/** Render DOM with values */
|
||||
private render(): void;
|
||||
|
|
|
@ -1,25 +1,21 @@
|
|||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const Dashboard = require('webpack-dashboard');
|
||||
const DashboardPlugin = require('webpack-dashboard/plugin');
|
||||
|
||||
const dashboard = new Dashboard();
|
||||
|
||||
module.exports = {
|
||||
devtool: 'eval',
|
||||
entry: [
|
||||
'webpack-dev-server/client?http://localhost:3000',
|
||||
'./src/scripts/src/choices',
|
||||
'webpack/hot/dev-server',
|
||||
'webpack-hot-middleware/client',
|
||||
'./src/scripts/choices',
|
||||
],
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
path: path.resolve('public'),
|
||||
filename: 'choices.min.js',
|
||||
publicPath: '/src/scripts/dist/',
|
||||
publicPath: 'http://localhost:3001/assets/scripts/',
|
||||
library: 'Choices',
|
||||
libraryTarget: 'umd',
|
||||
},
|
||||
plugins: [
|
||||
new DashboardPlugin(dashboard.setData),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
|
@ -32,7 +28,7 @@ module.exports = {
|
|||
{
|
||||
enforce: 'pre',
|
||||
test: /\.js?$/,
|
||||
include: path.join(__dirname, 'src/scripts/src'),
|
||||
include: path.join(__dirname, 'src/scripts'),
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
loader: 'eslint-loader',
|
||||
query: {
|
||||
|
@ -41,7 +37,7 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
test: /\.js?$/,
|
||||
include: path.join(__dirname, 'src/scripts/src'),
|
||||
include: path.join(__dirname, 'src/scripts'),
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
|
|
|
@ -11,12 +11,12 @@ module.exports = (env) => {
|
|||
const config = {
|
||||
devtool: minimize ? false : 'cheap-module-source-map',
|
||||
entry: [
|
||||
'./src/scripts/src/choices',
|
||||
'./src/scripts/choices',
|
||||
],
|
||||
output: {
|
||||
path: path.join(__dirname, '/src/scripts/dist'),
|
||||
path: path.join(__dirname, '/public/assets/scripts'),
|
||||
filename: minimize ? 'choices.min.js' : 'choices.js',
|
||||
publicPath: '/src/scripts/dist/',
|
||||
publicPath: '/public/assets/scripts/',
|
||||
library: 'Choices',
|
||||
libraryTarget: 'umd',
|
||||
auxiliaryComment: {
|
||||
|
@ -43,7 +43,7 @@ module.exports = (env) => {
|
|||
{
|
||||
enforce: 'pre',
|
||||
test: /\.js?$/,
|
||||
include: path.join(__dirname, 'src/scripts/src'),
|
||||
include: path.join(__dirname, 'src/scripts'),
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
loader: 'eslint-loader',
|
||||
query: {
|
||||
|
@ -52,7 +52,7 @@ module.exports = (env) => {
|
|||
},
|
||||
{
|
||||
test: /\.js?$/,
|
||||
include: path.join(__dirname, 'src/scripts/src'),
|
||||
include: path.join(__dirname, 'src/scripts'),
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
|
|