Compare commits
No commits in common. "master" and "v2.6.1" have entirely different histories.
16
.babelrc
|
@ -1,17 +1,3 @@
|
|||
{
|
||||
"presets": [["@babel/preset-env", { "loose": true }]],
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": true
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
"presets": ["es2015"]
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
> 1%
|
|
@ -1,3 +0,0 @@
|
|||
public/**
|
||||
webpack.config.*.js
|
||||
*.js
|
|
@ -1,4 +0,0 @@
|
|||
coverage:
|
||||
parsers:
|
||||
javascript:
|
||||
enable_partials: yes
|
|
@ -1,3 +0,0 @@
|
|||
node_modules/
|
||||
types/
|
||||
public/
|
27
.eslintrc
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"ecmaFeatures": {
|
||||
"modules": true
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"rules": {
|
||||
"quotes": [2, "single"],
|
||||
"strict": [2, "never"],
|
||||
"indent": ["error", 2, {"SwitchCase": 1}],
|
||||
"eol-last": "off",
|
||||
"arrow-body-style": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-new": 0,
|
||||
"max-len": "off",
|
||||
"no-console": "off",
|
||||
"consistent-return": "off",
|
||||
"no-param-reassign": ["error", { "props": false }],
|
||||
"no-unused-vars": ["error", { "args": "none" }],
|
||||
"no-lonely-if": "off",
|
||||
"class-methods-use-this": "error",
|
||||
"react/require-extension": "off",
|
||||
},
|
||||
}
|
123
.eslintrc.json
|
@ -1,123 +0,0 @@
|
|||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint", "prettier", "sort-class-members"],
|
||||
"extends": [
|
||||
"airbnb-base",
|
||||
"airbnb-typescript",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:compat/recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"mocha": true,
|
||||
"cypress/globals": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json",
|
||||
"ecmaVersion": 2020
|
||||
},
|
||||
"rules": {
|
||||
"import/prefer-default-export": "off",
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
{
|
||||
"devDependencies": true
|
||||
}
|
||||
],
|
||||
"no-console": [
|
||||
"warn",
|
||||
{
|
||||
"allow": ["warn", "error"]
|
||||
}
|
||||
],
|
||||
"no-plusplus": "off",
|
||||
"no-unused-expressions": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"consistent-return": "off",
|
||||
"import/no-useless-path-segments": "warn",
|
||||
"prefer-destructuring": [
|
||||
"warn",
|
||||
{
|
||||
"array": false,
|
||||
"object": true
|
||||
}
|
||||
],
|
||||
"curly": ["error", "all"],
|
||||
"newline-before-return": "error",
|
||||
"sort-class-members/sort-class-members": [
|
||||
2,
|
||||
{
|
||||
"order": [
|
||||
"[static-properties]",
|
||||
"[static-methods]",
|
||||
"[properties]",
|
||||
"[conventional-private-properties]",
|
||||
"constructor",
|
||||
"[methods]",
|
||||
"[conventional-private-methods]"
|
||||
],
|
||||
"accessorPairPositioning": "getThenSet"
|
||||
}
|
||||
],
|
||||
"lines-between-class-members": "off",
|
||||
"@typescript-eslint/no-namespace": "off",
|
||||
"react/jsx-filename-extension": [0]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.test.ts"],
|
||||
"env": {
|
||||
"mocha": true
|
||||
},
|
||||
"rules": {
|
||||
"no-restricted-syntax": "off",
|
||||
"compat/compat": "off",
|
||||
"no-new": "off",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"error",
|
||||
{
|
||||
"selector": "default",
|
||||
"format": ["camelCase", "PascalCase", "UPPER_CASE"],
|
||||
"leadingUnderscore": "allow"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["cypress/**"],
|
||||
"plugins": ["cypress"],
|
||||
"rules": {
|
||||
"no-unused-vars": "warn"
|
||||
},
|
||||
"env": {
|
||||
"cypress/globals": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"polyfills": [
|
||||
"Array.from",
|
||||
"Array.prototype.find",
|
||||
"Array.prototype.includes",
|
||||
"Symbol",
|
||||
"Symbol.iterator",
|
||||
"DOMTokenList",
|
||||
"Object.assign",
|
||||
"CustomEvent",
|
||||
"Element.prototype.classList",
|
||||
"Element.prototype.closest",
|
||||
"Element.prototype.dataset"
|
||||
],
|
||||
"import/resolver": {
|
||||
"node": {
|
||||
"extensions": [".js", ".ts"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
64
.gitattributes
vendored
|
@ -1,64 +0,0 @@
|
|||
## GITATTRIBUTES FOR WEB PROJECTS
|
||||
#
|
||||
# These settings are for any web project.
|
||||
#
|
||||
# Details per file setting:
|
||||
# text These files should be normalized (i.e. convert CRLF to LF).
|
||||
# binary These files are binary and should be left untouched.
|
||||
#
|
||||
# Note that binary is a macro for -text -diff.
|
||||
######################################################################
|
||||
|
||||
# Auto detect
|
||||
## Handle line endings automatically for files detected as
|
||||
## text and leave all files detected as binary untouched.
|
||||
## This will handle all files NOT defined below.
|
||||
* text eol=lf
|
||||
|
||||
# Source code
|
||||
*.css text eol=lf
|
||||
*.html text diff=html eol=lf
|
||||
*.js text eol=lf
|
||||
*.json text eol=lf
|
||||
*.scss text diff=css eol=lf
|
||||
*.ts text eol=lf
|
||||
|
||||
# Documentation
|
||||
*.md text eol=lf
|
||||
*.txt text eol=lf
|
||||
AUTHORS text eol=lf
|
||||
CHANGELOG text eol=lf
|
||||
CHANGES text eol=lf
|
||||
CONTRIBUTING text eol=lf
|
||||
COPYING text eol=lf
|
||||
copyright text eol=lf
|
||||
*COPYRIGHT* text eol=lf
|
||||
INSTALL text eol=lf
|
||||
license text eol=lf
|
||||
LICENSE text eol=lf
|
||||
NEWS text eol=lf
|
||||
readme text eol=lf
|
||||
*README* text eol=lf
|
||||
TODO text eol=lf
|
||||
|
||||
# Linters
|
||||
.eslintrc text eol=lf
|
||||
.stylelintrc text eol=lf
|
||||
|
||||
# Configs
|
||||
.babelrc text eol=lf
|
||||
.browserslistrc text eol=lf
|
||||
.editorconfig text eol=lf
|
||||
.env text eol=lf
|
||||
.gitattributes text eol=lf
|
||||
.gitconfig text eol=lf
|
||||
package-lock.json text -diff eol=lf
|
||||
*.npmignore text eol=lf
|
||||
*.yaml text eol=lf
|
||||
*.yml text eol=lf
|
||||
browserslist text eol=lf
|
||||
|
||||
# Graphics
|
||||
# SVG treated as an asset (binary) by default.
|
||||
*.svg text eol=lf
|
||||
*.png binary
|
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,38 +0,0 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: feature request
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -1,28 +0,0 @@
|
|||
## Description
|
||||
|
||||
<!--- Describe your changes in detail -->
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
<!--- If it fixes an open issue, please link to the issue here. -->
|
||||
|
||||
## Screenshots (if appropriate)
|
||||
|
||||
## Types of changes
|
||||
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||
|
||||
- [ ] Chore (tooling change or documentation change)
|
||||
- [ ] Refactor (non-breaking change which maintains existing functionality)
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
|
||||
- [ ] My code follows the code style of this project.
|
||||
- [ ] I have added new tests for the bug I fixed/the new feature I added.
|
||||
- [ ] I have modified existing tests for the bug I fixed/the new feature I added.
|
||||
- [ ] My change requires a change to the documentation.
|
||||
- [ ] I have updated the documentation accordingly.
|
Before Width: | Height: | Size: 24 KiB |
BIN
.github/actions-scripts/__snapshots__/edge-win32.png
vendored
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 104 KiB |
BIN
.github/actions-scripts/__snapshots__/ie-win32.png
vendored
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 297 KiB |
Before Width: | Height: | Size: 284 KiB |
14
.github/actions-scripts/polyfills-sync.js
vendored
|
@ -1,14 +0,0 @@
|
|||
const { readFileSync } = require('fs');
|
||||
const path = require('path');
|
||||
const assert = require('assert');
|
||||
|
||||
const readme = readFileSync(path.resolve(__dirname, '../../README.md'), 'utf8');
|
||||
|
||||
const polyfillsFromDocs = /^```polyfills\s*\n([^`]+)\n^```/m
|
||||
.exec(readme)[1]
|
||||
.split('\n')
|
||||
.map(v => v.trim())
|
||||
.sort();
|
||||
// @ts-ignore
|
||||
const polyfillsFromSettings = require('../../.eslintrc.json').settings.polyfills.sort();
|
||||
assert.deepStrictEqual(polyfillsFromDocs, polyfillsFromSettings);
|
92
.github/actions-scripts/puppeteer.js
vendored
|
@ -1,92 +0,0 @@
|
|||
const { readFileSync, writeFileSync, mkdirSync } = require('fs');
|
||||
const path = require('path');
|
||||
const { once } = require('events');
|
||||
|
||||
const puppeteer = require('puppeteer');
|
||||
const pixelmatch = require('pixelmatch');
|
||||
const { PNG } = require('pngjs');
|
||||
|
||||
const server = require('../../server');
|
||||
|
||||
async function test() {
|
||||
const browser = await puppeteer.launch();
|
||||
const page = await browser.newPage();
|
||||
const artifactsPath = 'screenshot';
|
||||
const snapshotName = `puppeteer-${process.platform}.png`;
|
||||
let error;
|
||||
let pixelDifference;
|
||||
let diff;
|
||||
|
||||
if (!server.listening) await once(server, 'listening');
|
||||
|
||||
try {
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') throw new Error(msg.text());
|
||||
});
|
||||
page.on('pageerror', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
await page.goto(`http://127.0.0.1:${server.address().port}`, {
|
||||
waitUntil: 'networkidle2',
|
||||
});
|
||||
await page.setViewport({ width: 640, height: 1000 });
|
||||
await page.waitForTimeout(500); // Wait for resize to complete
|
||||
await page.click('label[for="choices-single-custom-templates"]');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
|
||||
mkdirSync(artifactsPath, { recursive: true });
|
||||
const imageBuffer = await page.screenshot({
|
||||
path: path.join(artifactsPath, snapshotName),
|
||||
fullPage: true,
|
||||
});
|
||||
|
||||
// compare with snapshot
|
||||
const screenshot = PNG.sync.read(imageBuffer);
|
||||
const snapshot = PNG.sync.read(
|
||||
readFileSync(path.resolve(__dirname, `./__snapshots__/${snapshotName}`)),
|
||||
);
|
||||
const { width, height } = screenshot;
|
||||
diff = new PNG({ width, height });
|
||||
pixelDifference = pixelmatch(
|
||||
screenshot.data,
|
||||
snapshot.data,
|
||||
diff.data,
|
||||
width,
|
||||
height,
|
||||
{
|
||||
threshold: 0.6,
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
error = err;
|
||||
} finally {
|
||||
if (diff) {
|
||||
writeFileSync(path.join(artifactsPath, 'diff-' + snapshotName), PNG.sync.write(diff));
|
||||
}
|
||||
await Promise.all([
|
||||
browser.close(),
|
||||
new Promise(resolve => server.close(resolve)),
|
||||
]);
|
||||
}
|
||||
|
||||
if (pixelDifference > 200) {
|
||||
console.error(
|
||||
`Snapshot is different from screenshot by ${pixelDifference} pixels`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
if (error) process.exit(1);
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
process.once('uncaughtException', err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
setImmediate(test);
|
155
.github/actions-scripts/selenium.js
vendored
|
@ -1,155 +0,0 @@
|
|||
const path = require('path');
|
||||
const { readFileSync, writeFileSync, mkdirSync } = require('fs');
|
||||
const { once } = require('events');
|
||||
|
||||
const pixelmatch = require('pixelmatch');
|
||||
const { PNG } = require('pngjs');
|
||||
const {
|
||||
Builder,
|
||||
By,
|
||||
Key,
|
||||
until,
|
||||
Capabilities,
|
||||
logging,
|
||||
} = require('selenium-webdriver');
|
||||
|
||||
const server = require('../../server');
|
||||
|
||||
async function test() {
|
||||
let pixelDifference;
|
||||
let error;
|
||||
|
||||
let capabilities;
|
||||
switch (process.env.BROWSER) {
|
||||
case 'ie':
|
||||
capabilities = Capabilities.ie();
|
||||
capabilities.set('ignoreProtectedModeSettings', true);
|
||||
capabilities.set('ignoreZoomSetting', true);
|
||||
capabilities.set('ie.options', {
|
||||
enableFullPageScreenshot: true,
|
||||
ensureCleanSession: true,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'edge':
|
||||
capabilities = Capabilities.edge();
|
||||
break;
|
||||
|
||||
case 'safari':
|
||||
capabilities = Capabilities.safari();
|
||||
capabilities.set('safari.options', { technologyPreview: false });
|
||||
break;
|
||||
|
||||
case 'firefox': {
|
||||
capabilities = Capabilities.firefox().setLoggingPrefs({ browser: 'ALL' });
|
||||
break;
|
||||
}
|
||||
case 'chrome': {
|
||||
capabilities = Capabilities.chrome().setLoggingPrefs({ browser: 'ALL' });
|
||||
capabilities.set('chromeOptions', {
|
||||
args: ['--headless', '--no-sandbox', '--disable-gpu'],
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let driver = await new Builder().withCapabilities(capabilities).build();
|
||||
|
||||
if (!server.listening) await once(server, 'listening');
|
||||
|
||||
try {
|
||||
await driver.get(`http://127.0.0.1:${server.address().port}`);
|
||||
|
||||
// wait for last choice to init
|
||||
await driver.wait(
|
||||
until.elementLocated(By.css('#reset-multiple ~ .choices__list')),
|
||||
10000,
|
||||
'waiting for all Choices instances to init',
|
||||
);
|
||||
|
||||
// Resize window
|
||||
await driver
|
||||
.manage()
|
||||
.window()
|
||||
.maximize();
|
||||
await driver
|
||||
.manage()
|
||||
.window()
|
||||
// magic numbers here to make sure all demo page are fit inside
|
||||
.setRect({ x: 0, y: 0, width: 630, height: 4000 });
|
||||
|
||||
// and click on press space on it, so it should open choices
|
||||
await driver
|
||||
.findElement(By.css('#reset-multiple ~ .choices__list button'))
|
||||
.sendKeys(Key.SPACE);
|
||||
await driver.sleep(1000);
|
||||
|
||||
// take screenshot
|
||||
const image = await driver.takeScreenshot();
|
||||
const imageBuffer = Buffer.from(image, 'base64');
|
||||
|
||||
const snapshotName = `${process.env.BROWSER}-${process.platform}.png`;
|
||||
const artifactsPath = 'screenshot';
|
||||
mkdirSync(artifactsPath, { recursive: true });
|
||||
|
||||
writeFileSync(path.join(artifactsPath, snapshotName), imageBuffer);
|
||||
|
||||
// compare with snapshot
|
||||
const screenshot = PNG.sync.read(imageBuffer);
|
||||
const snapshot = PNG.sync.read(
|
||||
readFileSync(path.resolve(__dirname, `./__snapshots__/${snapshotName}`)),
|
||||
);
|
||||
const { width, height } = screenshot;
|
||||
const diff = new PNG({ width, height });
|
||||
pixelDifference = pixelmatch(
|
||||
screenshot.data,
|
||||
snapshot.data,
|
||||
diff.data,
|
||||
width,
|
||||
height,
|
||||
{
|
||||
threshold: 1,
|
||||
},
|
||||
);
|
||||
writeFileSync(path.join(artifactsPath, 'diff.png'), PNG.sync.write(diff));
|
||||
|
||||
// getting console logs
|
||||
// ensure no errors in console (only supported in Chrome currently)
|
||||
if (process.env.BROWSER === 'chrome') {
|
||||
const entries = await driver
|
||||
.manage()
|
||||
.logs()
|
||||
.get(logging.Type.BROWSER);
|
||||
if (
|
||||
Array.isArray(entries) &&
|
||||
entries.some(entry => entry.level.name_ === 'SEVERE')
|
||||
)
|
||||
throw new Error(JSON.stringify(entries));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
error = err;
|
||||
} finally {
|
||||
await Promise.all([
|
||||
driver.quit(),
|
||||
new Promise(resolve => server.close(resolve)),
|
||||
]);
|
||||
}
|
||||
if (pixelDifference > 200) {
|
||||
console.error(
|
||||
`Snapshot is different from screenshot by ${pixelDifference} pixels`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
if (error) process.exit(1);
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
process.once('uncaughtException', err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
setImmediate(test);
|
29
.github/release-drafter.yml
vendored
|
@ -1,29 +0,0 @@
|
|||
name-template: 'Draft (next release)'
|
||||
tag-template: 'v$NEXT_PATCH_VERSION'
|
||||
sort-direction: descending
|
||||
exclude-labels:
|
||||
- 'skip-changelog'
|
||||
- 'release'
|
||||
categories:
|
||||
- title: '🚨 Breaking changes'
|
||||
labels:
|
||||
- 'breaking change'
|
||||
- title: '🚀 Features'
|
||||
labels:
|
||||
- 'feature'
|
||||
- 'enhancement'
|
||||
- title: '🐛 Bug Fixes'
|
||||
labels:
|
||||
- 'bugfix'
|
||||
- title: '🔧 Maintenance'
|
||||
labels:
|
||||
- 'chore'
|
||||
- 'housekeeping'
|
||||
- 'refactor'
|
||||
- 'documentation'
|
||||
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
|
||||
template: |
|
||||
# Changes
|
||||
$CHANGES
|
||||
# Contributors
|
||||
$CONTRIBUTORS
|
131
.github/workflows/browsers.yml
vendored
|
@ -1,131 +0,0 @@
|
|||
name: Browsers
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'package-lock.json'
|
||||
- '.browserslistrc'
|
||||
- '.babelrc'
|
||||
- 'webpack.config.*'
|
||||
- 'public/index.html'
|
||||
- '.github/actions-scripts/__snapshots__/**'
|
||||
- '.github/workflows/browsers.yml'
|
||||
|
||||
jobs:
|
||||
selenium:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-latest, macos-latest]
|
||||
browser: [edge, firefox, safari, chrome]
|
||||
exclude:
|
||||
- os: windows-latest
|
||||
browser: safari
|
||||
- os: macos-latest
|
||||
browser: edge
|
||||
- os: macos-latest
|
||||
browser: chrome
|
||||
# Safari workaround is not working in Catalina
|
||||
- browser: safari
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.OS }}-build-${{ matrix.browser }}
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-build-${{ env.cache-name }}-
|
||||
${{ runner.OS }}-build-
|
||||
${{ runner.OS }}-
|
||||
|
||||
- run: |
|
||||
npm ci
|
||||
npm run build
|
||||
env:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
HUSKY_SKIP_INSTALL: true
|
||||
|
||||
# install drivers
|
||||
- name: Enable Safari Driver
|
||||
run: |
|
||||
# Workaround for `sudo safardriver --enable` not working:
|
||||
# https://github.com/web-platform-tests/wpt/issues/19845
|
||||
# https://github.com/web-platform-tests/wpt/blob/master/tools/ci/azure/install_safari.yml
|
||||
mkdir -p ~/Library/WebDriver/
|
||||
curl https://raw.githubusercontent.com/web-platform-tests/wpt/master/tools/ci/azure/com.apple.Safari.plist -o ~/Library/WebDriver/com.apple.Safari.plist
|
||||
defaults write com.apple.Safari WebKitJavaScriptCanOpenWindowsAutomatically 1
|
||||
# sudo safaridriver --enable
|
||||
if: matrix.browser == 'safari'
|
||||
|
||||
- run: |
|
||||
brew install --cask firefox
|
||||
brew install geckodriver
|
||||
if: matrix.browser == 'firefox' && matrix.os == 'macos-latest'
|
||||
|
||||
- run: echo "$env:GeckoWebDriver" >> $GITHUB_PATH
|
||||
if: matrix.browser == 'firefox' && matrix.os == 'windows-latest'
|
||||
|
||||
- run: echo "$env:EdgeWebDriver" >> $GITHUB_PATH
|
||||
if: matrix.browser == 'edge' && matrix.os == 'windows-latest'
|
||||
|
||||
- run: echo "$env:ChromeWebDriver" >> $GITHUB_PATH
|
||||
if: matrix.browser == 'chrome' && matrix.os == 'windows-latest'
|
||||
|
||||
- run: npm i --no-optional --no-audit selenium-webdriver pixelmatch pngjs
|
||||
- run: node .github/actions-scripts/selenium.js
|
||||
env:
|
||||
BROWSER: ${{ matrix.browser }}
|
||||
PORT: 0
|
||||
NODE_ENV: production # prevent watching
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: screenshot-${{ matrix.browser }}-${{ matrix.os }}
|
||||
path: screenshot
|
||||
|
||||
puppeteer:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.OS }}-build-puppeteer
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-build-puppeteer
|
||||
- run: |
|
||||
npm ci
|
||||
npm run build
|
||||
env:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
HUSKY_SKIP_INSTALL: true
|
||||
- run: npm i --no-optional --no-audit puppeteer pixelmatch pngjs
|
||||
- run: node .github/actions-scripts/puppeteer.js
|
||||
env:
|
||||
PORT: 0
|
||||
NODE_ENV: production # prevent watching
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: screenshot-puppeteer-darwin
|
||||
path: screenshot
|
60
.github/workflows/build-and-test.yml
vendored
|
@ -1,60 +0,0 @@
|
|||
name: Build and test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Build and run all tests
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
npx bundlesize
|
||||
npm run test:unit:coverage
|
||||
npm run test:e2e
|
||||
env:
|
||||
CI: true
|
||||
CI_REPO_NAME: ${{ github.event.repository.name }}
|
||||
CI_REPO_OWNER: ${{ github.event.organization.login }}
|
||||
CI_COMMIT_SHA: ${{ github.sha }}
|
||||
GIT_COMMIT: ${{ github.sha }}
|
||||
CI_BRANCH: ${{ github.head_ref }}
|
||||
BUNDLESIZE_GITHUB_TOKEN: ${{secrets.BUNDLESIZE_GITHUB_TOKEN}}
|
||||
FORCE_COLOR: 2
|
||||
HUSKY_SKIP_INSTALL: true
|
||||
##
|
||||
## Disabling for now. There does not appear to be a secure way to do this
|
||||
## with protected branches. See discussion:
|
||||
## https://github.community/t/how-to-push-to-protected-branches-in-a-github-action/16101
|
||||
##
|
||||
# - name: Commit built files
|
||||
# run: |
|
||||
# git config --local user.email "action@github.com"
|
||||
# git config --local user.name "GitHub Action"
|
||||
# git commit -m "Update build files 🏗" -a || echo "No changes to commit" && exit 0
|
||||
# - name: Push changes
|
||||
# uses: ad-m/github-push-action@master
|
||||
# with:
|
||||
# github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
-f ./coverage/lcov.info
|
||||
-B ${{ github.head_ref }}
|
||||
-C ${{ github.sha }}
|
||||
-Z || echo 'Codecov upload failed'
|
||||
env:
|
||||
CI: true
|
||||
GITLAB_CI: true # pretend we are GitLab CI, while Codecov adding support for Github Actions
|
||||
CODECOV_ENV: github-action
|
||||
CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}
|
42
.github/workflows/bundlesize.yml
vendored
|
@ -1,42 +0,0 @@
|
|||
name: Bundle size checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/scripts/**'
|
||||
- 'src/styles/**'
|
||||
- 'package-lock.json'
|
||||
- '.browserslistrc'
|
||||
|
||||
jobs:
|
||||
measure:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies and build
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
env:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
HUSKY_SKIP_INSTALL: true
|
||||
|
||||
# we don't need to build here, as even minized assets expected to be commited
|
||||
|
||||
- run: npx bundlesize
|
||||
env:
|
||||
CI: true
|
||||
BUNDLESIZE_GITHUB_TOKEN: ${{secrets.BUNDLESIZE_GITHUB_TOKEN}}
|
||||
CI_REPO_NAME: ${{ github.event.repository.name }}
|
||||
CI_REPO_OWNER: ${{ github.event.organization.login }}
|
||||
CI_COMMIT_SHA: ${{ github.event.after }}
|
||||
GIT_COMMIT: ${{ github.event.after }}
|
||||
CI_BRANCH: ${{ github.head_ref }}
|
||||
FORCE_COLOR: 2
|
32
.github/workflows/deploy-pages.yml
vendored
|
@ -1,32 +0,0 @@
|
|||
name: Deploy Pages
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy-gh-pages:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- name: Build
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
rm -rf public/test
|
||||
env:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
HUSKY_SKIP_INSTALL: true
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PUBLISH_BRANCH: gh-pages
|
||||
PUBLISH_DIR: ./public
|
24
.github/workflows/deployment.yml
vendored
|
@ -1,24 +0,0 @@
|
|||
name: Publish to npm
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
publish-npm:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- run: npm ci
|
||||
env:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
HUSKY_SKIP_INSTALL: true
|
||||
- run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
71
.github/workflows/e2e-tests.yml
vendored
|
@ -1,71 +0,0 @@
|
|||
name: End-to-end tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'package-lock.json'
|
||||
- '.browserslistrc'
|
||||
- '.babelrc'
|
||||
- 'webpack.config.*'
|
||||
- 'public/test/**'
|
||||
- 'cypress/**'
|
||||
- '.github/workflows/e2e-tests.yml'
|
||||
|
||||
jobs:
|
||||
test-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CI: true
|
||||
TERM: xterm-256color
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-build-${{ env.cache-name }}-
|
||||
${{ runner.OS }}-build-
|
||||
${{ runner.OS }}-
|
||||
|
||||
- name: Get Cypress info
|
||||
id: cypress-info
|
||||
run: |
|
||||
echo ::set-output name=version::$(jq -r .devDependencies.cypress ./package.json)
|
||||
echo ::set-output name=cache::$(npx cypress cache path)
|
||||
env:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
- name: Cache Cypress cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.cypress-info.outputs.cache }}
|
||||
key: ${{ runner.OS }}-cypress-${{ steps.cypress-info.outputs.version }}
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-cypress-${{ steps.cypress-info.outputs.version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
HUSKY_SKIP_INSTALL: true
|
||||
|
||||
- name: run Cypress (with or without recording)
|
||||
# if we have ran out of free Cypress recordings, run Cypress with recording switched off
|
||||
run: npm exec -- run-p --race start cypress:ci || npm exec -- run-p --race start cypress:run
|
||||
env:
|
||||
NODE_ENV: production # prevent watching
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
DEBUG: commit-info,cypress:server:record
|
||||
# https://docs.cypress.io/guides/guides/continuous-integration.html#Environment-variables
|
||||
COMMIT_INFO_BRANCH: ${{ github.head_ref }}
|
||||
COMMIT_INFO_AUTHOR: ${{ github.event.sender.login }}
|
||||
COMMIT_INFO_SHA: ${{ github.event.after }}
|
41
.github/workflows/lint.yml
vendored
|
@ -1,41 +0,0 @@
|
|||
name: Code linting
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/scripts/**'
|
||||
- 'src/styles/**'
|
||||
- package-lock.json
|
||||
- '.browserslistrc'
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
env:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
HUSKY_SKIP_INSTALL: true
|
||||
|
||||
- name: run eslint
|
||||
run: npm run lint:js
|
||||
|
||||
## Can't use same eslint config for TypeScript and JavaScript
|
||||
## TypeScript rules cause rule definition not found errors
|
||||
## Can be re-enabled if this is resolved: https://github.com/eslint/eslint/issues/14851
|
||||
# - name: Lint JS bundle
|
||||
# run: |
|
||||
# npm run js:build
|
||||
# npx eslint --no-ignore ./public/assets/scripts/*.js
|
||||
|
||||
- name: run stylelint
|
||||
run: npm run lint:scss
|
23
.github/workflows/polyfills-sync.yml
vendored
|
@ -1,23 +0,0 @@
|
|||
name: Polyfills documentation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'README.md'
|
||||
- '.browserslistrc'
|
||||
- '.eslintrc.json'
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Check Polyfills documentation and settings sync
|
||||
run: node .github/actions-scripts/polyfills-sync.js
|
14
.github/workflows/release-drafter.yml
vendored
|
@ -1,14 +0,0 @@
|
|||
name: Release drafter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
update-draft-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
42
.github/workflows/unit-tests.yml
vendored
|
@ -1,42 +0,0 @@
|
|||
name: Unit tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/scripts/**'
|
||||
- package-lock.json
|
||||
- '.browserslistrc'
|
||||
|
||||
jobs:
|
||||
test-unit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install --no-optional --no-audit --ignore-scripts
|
||||
env:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
HUSKY_SKIP_INSTALL: true
|
||||
|
||||
- run: npm run test:unit:coverage
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
-f ./coverage/lcov.info
|
||||
-B ${{ github.head_ref }}
|
||||
-C ${{ github.sha }}
|
||||
-Z || echo 'Codecov upload failed'
|
||||
env:
|
||||
CI: true
|
||||
GITLAB_CI: true # pretend we are GitLab CI, while Codecov adding support for Github Actions
|
||||
CODECOV_ENV: github-action
|
||||
CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}
|
7
.gitignore
vendored
|
@ -1,12 +1,7 @@
|
|||
node_modules
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
.idea
|
||||
|
||||
# Test
|
||||
tests/reports
|
||||
tests/results
|
||||
.nyc_output
|
||||
coverage
|
||||
cypress/videos
|
||||
cypress/screenshots
|
||||
tests/results
|
|
@ -1,8 +0,0 @@
|
|||
require:
|
||||
- 'ts-node/register'
|
||||
- './config/jsdom.js'
|
||||
exit: true
|
||||
spec: src/**/**/*.test.ts
|
||||
extension:
|
||||
- ts
|
||||
- js
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"endOfLine": "lf",
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.svg"],
|
||||
"options": {
|
||||
"parser": "html",
|
||||
"htmlWhitespaceSensitivity": "ignore"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["public/*.html"],
|
||||
"options": {
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"extends": "stylelint-config-standard-scss",
|
||||
"rules": {
|
||||
"declaration-block-no-redundant-longhand-properties": null
|
||||
}
|
||||
}
|
9
.travis.yml
Normal file
|
@ -0,0 +1,9 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
before_install:
|
||||
- '[ "${TRAVIS_NODE_VERSION}" != "0.8" ] || npm install -g npm@1.4.28'
|
||||
- npm install -g npm@latest
|
||||
install:
|
||||
- npm install
|
||||
script: npm run js:test
|
17
.vscode/extensions.json
vendored
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
// we enforce ESLint rules, so, recommend extension
|
||||
"dbaeumer.vscode-eslint",
|
||||
// we use prettier, so, recommend extension
|
||||
"esbenp.prettier-vscode",
|
||||
// we are on GitHub, so, recommend extension
|
||||
"github.vscode-pull-request-github",
|
||||
// needed for our configured debug configuration with Chrome
|
||||
"msjsdiag.debugger-for-chrome"
|
||||
// Mocha recommended - https://mochajs.org/#mocha-sidebar-vs-code,
|
||||
// but it's buggy
|
||||
// "maty.vscode-mocha-sidebar"
|
||||
]
|
||||
}
|
70
.vscode/launch.json
vendored
|
@ -1,70 +0,0 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome",
|
||||
"preLaunchTask": "buildAndWatch",
|
||||
"url": "http://localhost:3001",
|
||||
"webRoot": "${workspaceFolder}",
|
||||
"sourceMapPathOverrides": {
|
||||
"webpack://Choices/*": "${workspaceFolder}/*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Mocha Current File",
|
||||
"program": "${workspaceFolder}/node_modules/mocha/bin/mocha",
|
||||
"args": ["-u", "bdd", "--timeout", "999999", "--colors", "${file}"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"env": {
|
||||
"NODE_ENV": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Mocha All",
|
||||
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
|
||||
"args": ["-u", "bdd", "--timeout", "999999", "--colors"],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"env": {
|
||||
"NODE_ENV": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Cypress Current File",
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/cypress",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceFolder}\\node_modules\\.bin\\cypress.cmd"
|
||||
},
|
||||
"runtimeArgs": [
|
||||
"run",
|
||||
"--headed",
|
||||
"--no-exit",
|
||||
"--browser=electron",
|
||||
"--port",
|
||||
"9898",
|
||||
"--spec"
|
||||
],
|
||||
"protocol": "legacy",
|
||||
"port": 9898,
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"preLaunchTask": "buildAndWatch",
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"timeout": 999999999999999,
|
||||
"autoAttachChildProcesses": false,
|
||||
"env": {
|
||||
"NODE_ENV": "test"
|
||||
// "DEBUG": "cypress:*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
72
.vscode/settings.json
vendored
|
@ -1,72 +0,0 @@
|
|||
{
|
||||
"eslint.enable": true,
|
||||
// prevent watch task failures on lint errors
|
||||
"eslint.autoFixOnSave": true,
|
||||
// switch off default VSCode formatting rules
|
||||
"javascript.format.enable": false,
|
||||
// Javascript prettier runs via ESLint
|
||||
"prettier.disableLanguages": ["javascript"],
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": false
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"public/assets": true,
|
||||
"**/coverage": true
|
||||
},
|
||||
// Mocha Sidebar settings
|
||||
"mocha.env": {
|
||||
"NODE_ENV": "test"
|
||||
},
|
||||
"mocha.files.glob": "src/scripts/**/*.test.js",
|
||||
"mocha.requires": ["@babel/register", "./config/jsdom.js"],
|
||||
// for Windows collaborators
|
||||
"files.eol": "\n",
|
||||
"files.encoding": "utf8",
|
||||
// associations for some files this project is using
|
||||
"files.associations": {
|
||||
".browserslistrc": "gitignore",
|
||||
".huskyrc": "jsonc",
|
||||
".npmrc": "ini"
|
||||
},
|
||||
// We use NPM as package manager
|
||||
"npm.packageManager": "npm",
|
||||
"npm.autoDetect": "on",
|
||||
"npm.fetchOnlinePackageInfo": true,
|
||||
"eslint.packageManager": "npm",
|
||||
"json.schemas": [
|
||||
// Cypress related settings - https://docs.cypress.io/guides/tooling/intelligent-code-completion.html#Features-1
|
||||
{
|
||||
"fileMatch": ["cypress.json"],
|
||||
"url": "https://on.cypress.io/cypress.schema.json"
|
||||
},
|
||||
// Husky config file
|
||||
{
|
||||
"fileMatch": [".huskyrc"],
|
||||
"url": "http://json.schemastore.org/huskyrc"
|
||||
},
|
||||
// Prettier config
|
||||
{
|
||||
"fileMatch": [".prettierrc.json"],
|
||||
"url": "http://json.schemastore.org/prettierrc"
|
||||
}
|
||||
],
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"stylelint.validate": [
|
||||
"css",
|
||||
"less",
|
||||
"postcss",
|
||||
"scss"
|
||||
]
|
||||
}
|
87
.vscode/tasks.json
vendored
|
@ -1,87 +0,0 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"label": "buildAndWatch",
|
||||
"script": "js:watch",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "dedicated",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$eslint-stylish",
|
||||
{
|
||||
"owner": "webpack",
|
||||
"fileLocation": "absolute",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^Module build failed \\(from (\\.+)\\)",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3
|
||||
},
|
||||
{
|
||||
"regexp": "\\s*TS\\d+:\\s*(.*)",
|
||||
"message": 1
|
||||
}
|
||||
],
|
||||
"severity": "error",
|
||||
"source": "webpack",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": "^Listening at",
|
||||
"endsPattern": "Compiled successfully\\."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "css:build",
|
||||
"group": "build",
|
||||
"problemMatcher": ["$node-sass"]
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "lint",
|
||||
"problemMatcher": ["$eslint-stylish"]
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "build",
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"group": "test"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test:e2e",
|
||||
"group": "test"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test:unit",
|
||||
"group": "test"
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "cypress:open",
|
||||
"isBackground": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at josh@joshuajohnson.co.uk. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
|
@ -1,24 +0,0 @@
|
|||
# Contributions
|
||||
In lieu of a formal styleguide, take care to maintain the existing coding style ensuring there are no linting errors. Add unit tests for any new or changed functionality. Lint and test your code using the npm scripts below:
|
||||
|
||||
### NPM tasks
|
||||
| Task | Usage |
|
||||
| -------------------- | ------------------------------------------------------------ |
|
||||
| `npm run start` | Fire up local server for development |
|
||||
| `npm run test:unit` | Run sequence of unit tests once |
|
||||
| `npm run test:e2e` | Run sequence of integration tests once |
|
||||
| `npm run test:watch` | Fire up test server and re-test on file change |
|
||||
| `npm run js:build` | Compile Choices to an uglified JavaScript file |
|
||||
| `npm run css:watch` | Watch SCSS files for changes. On a change, run build process |
|
||||
| `npm run css:build` | Compile, minify and prefix SCSS files to CSS |
|
||||
|
||||
## Pull requests
|
||||
When submitting a pull request that resolves a bug, feel free to use the following template:
|
||||
|
||||
```md
|
||||
## This is the problem:
|
||||
|
||||
## Steps to reproduce:
|
||||
|
||||
## This is my solution:
|
||||
```
|
Before Width: | Height: | Size: 244 B After Width: | Height: | Size: 244 B |
Before Width: | Height: | Size: 244 B After Width: | Height: | Size: 244 B |
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 |
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/assets/images/mstile-150x150.png"/>
|
||||
<TileColor>#ffffff</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/assets/images/mstile-150x150.png"/>
|
||||
<TileColor>#ffffff</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
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 |
5480
assets/scripts/dist/choices.js
vendored
Normal file
1
assets/scripts/dist/choices.js.map
vendored
Normal file
4
assets/scripts/dist/choices.min.js
vendored
Normal file
1
assets/scripts/dist/choices.min.js.map
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"choices.min.js","sources":[],"mappings":";;","sourceRoot":""}
|
73
assets/scripts/src/actions/index.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
export const addItem = (value, label, id, choiceId, groupId) => {
|
||||
return {
|
||||
type: 'ADD_ITEM',
|
||||
value,
|
||||
label,
|
||||
id,
|
||||
choiceId,
|
||||
groupId,
|
||||
};
|
||||
};
|
||||
|
||||
export const removeItem = (id, choiceId) => {
|
||||
return {
|
||||
type: 'REMOVE_ITEM',
|
||||
id,
|
||||
choiceId,
|
||||
};
|
||||
};
|
||||
|
||||
export const highlightItem = (id, highlighted) => {
|
||||
return {
|
||||
type: 'HIGHLIGHT_ITEM',
|
||||
id,
|
||||
highlighted,
|
||||
};
|
||||
};
|
||||
|
||||
export const addChoice = (value, label, id, groupId, disabled) => {
|
||||
return {
|
||||
type: 'ADD_CHOICE',
|
||||
value,
|
||||
label,
|
||||
id,
|
||||
groupId,
|
||||
disabled,
|
||||
};
|
||||
};
|
||||
|
||||
export const filterChoices = (results) => {
|
||||
return {
|
||||
type: 'FILTER_CHOICES',
|
||||
results,
|
||||
};
|
||||
};
|
||||
|
||||
export const activateChoices = (active = true) => {
|
||||
return {
|
||||
type: 'ACTIVATE_CHOICES',
|
||||
active,
|
||||
};
|
||||
};
|
||||
|
||||
export const clearChoices = () => {
|
||||
return {
|
||||
type: 'CLEAR_CHOICES',
|
||||
};
|
||||
};
|
||||
|
||||
export const addGroup = (value, id, active, disabled) => {
|
||||
return {
|
||||
type: 'ADD_GROUP',
|
||||
value,
|
||||
id,
|
||||
active,
|
||||
disabled,
|
||||
};
|
||||
};
|
||||
|
||||
export const clearAll = () => {
|
||||
return {
|
||||
type: 'CLEAR_ALL',
|
||||
};
|
||||
};
|
2221
assets/scripts/src/choices.js
Normal file
129
assets/scripts/src/lib/polyfills.js
Normal file
|
@ -0,0 +1,129 @@
|
|||
/* 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() {
|
||||
var toStr = Object.prototype.toString;
|
||||
|
||||
var isCallable = function(fn) {
|
||||
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
|
||||
};
|
||||
|
||||
var toInteger = function(value) {
|
||||
var 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));
|
||||
};
|
||||
|
||||
var maxSafeInteger = Math.pow(2, 53) - 1;
|
||||
|
||||
var toLength = function(value) {
|
||||
var 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.
|
||||
var C = this;
|
||||
|
||||
// 2. Let items be ToObject(arrayLike).
|
||||
var 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.
|
||||
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
|
||||
var 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).
|
||||
var 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).
|
||||
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
|
||||
|
||||
// 16. Let k be 0.
|
||||
var k = 0;
|
||||
// 17. Repeat, while k < len… (also steps a - h)
|
||||
var 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');
|
||||
}
|
||||
var list = Object(this);
|
||||
var length = list.length >>> 0;
|
||||
var thisArg = arguments[1];
|
||||
var value;
|
||||
|
||||
for (var 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
|
||||
};
|
||||
var evt = document.createEvent('CustomEvent');
|
||||
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
|
||||
return evt;
|
||||
}
|
||||
|
||||
CustomEvent.prototype = window.Event.prototype;
|
||||
|
||||
window.CustomEvent = CustomEvent;
|
||||
})();
|
506
assets/scripts/src/lib/utils.js
Normal file
|
@ -0,0 +1,506 @@
|
|||
/* 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, function(txt) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests the type of an object
|
||||
* @param {String} type Type to test object against
|
||||
* @param {Object} obj Object to be tested
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const isType = function(type, obj) {
|
||||
var clas = Object.prototype.toString.call(obj).slice(8, -1);
|
||||
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) => {
|
||||
return (
|
||||
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) => {
|
||||
return (
|
||||
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
|
||||
* @private
|
||||
* @return {Object} Merged object of arguments
|
||||
*/
|
||||
export const extend = function() {
|
||||
let extended = {};
|
||||
let length = arguments.length;
|
||||
|
||||
/**
|
||||
* Merge one object into another
|
||||
* @param {Object} obj Object to merge into extended object
|
||||
*/
|
||||
let merge = function(obj) {
|
||||
for (let prop in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
|
||||
// If deep merge and property is an object, merge properties
|
||||
if (isType('Object', obj[prop])) {
|
||||
extended[prop] = extend(true, extended[prop], obj[prop]);
|
||||
} else {
|
||||
extended[prop] = obj[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Loop through each passed argument
|
||||
for (let i = 0; i < length; i++) {
|
||||
// store argument at position i
|
||||
let obj = arguments[i];
|
||||
|
||||
// If we are in fact dealing with an object, merge it.
|
||||
if (isType('Object', obj)) {
|
||||
merge(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return extended;
|
||||
};
|
||||
|
||||
/**
|
||||
* CSS transition end event listener
|
||||
* @return
|
||||
*/
|
||||
export const whichTransitionEvent = function() {
|
||||
var t,
|
||||
el = document.createElement("fakeelement");
|
||||
|
||||
var 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() {
|
||||
var t,
|
||||
el = document.createElement('fakeelement');
|
||||
|
||||
var 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) {
|
||||
var parents = [];
|
||||
// Get matches
|
||||
for (; elem && elem !== document; elem = elem.parentNode) {
|
||||
|
||||
// Check if parent has been reached
|
||||
if (parent) {
|
||||
|
||||
var 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) {
|
||||
var 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;
|
||||
} else {
|
||||
return parents;
|
||||
}
|
||||
};
|
||||
|
||||
export const wrap = function(element, wrapper) {
|
||||
wrapper = wrapper || document.createElement('div');
|
||||
if (element.nextSibling) {
|
||||
element.parentNode.insertBefore(wrapper, element.nextSibling);
|
||||
} else {
|
||||
element.parentNode.appendChild(wrapper);
|
||||
}
|
||||
return wrapper.appendChild(element);
|
||||
};
|
||||
|
||||
export const getSiblings = function(elem) {
|
||||
var siblings = [];
|
||||
var 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
|
||||
* @param {[type]} cls Class of parent
|
||||
* @return {NodeElement} Found parent element
|
||||
*/
|
||||
export const findAncestor = function(el, cls) {
|
||||
while ((el = el.parentElement) && !el.classList.contains(cls));
|
||||
return el;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var timeout;
|
||||
return function() {
|
||||
var context = this,
|
||||
args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
var 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) {
|
||||
var 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) {
|
||||
var 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
|
||||
* @param {String} className The class we will look through
|
||||
* @param {Number} direction Positive next element, negative previous element
|
||||
* @return {[HTMLElement} Found element
|
||||
*/
|
||||
export const getAdjacentEl = (startEl, className, direction = 1) => {
|
||||
if (!startEl || !className) return;
|
||||
|
||||
const parent = startEl.parentNode.parentNode;
|
||||
const children = Array.from(parent.querySelectorAll(className));
|
||||
|
||||
const startPos = children.indexOf(startEl);
|
||||
const operatorDirection = direction > 0 ? 1 : -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));
|
||||
} else {
|
||||
// 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)) ? true : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine whether an element is within
|
||||
* @param {HTMLElement} el Element to test
|
||||
* @param {HTMLElement} parent Scrolling parent
|
||||
* @param {Number} direction Whether element is visible from above or below
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const isScrolledIntoView = (el, parent, direction = 1) => {
|
||||
if (!el) return;
|
||||
|
||||
let isVisible;
|
||||
|
||||
if (direction > 0) {
|
||||
// In view from bottom
|
||||
isVisible = (parent.scrollTop + parent.offsetHeight) >= (el.offsetTop + el.offsetHeight);
|
||||
} else {
|
||||
// In view from top
|
||||
isVisible = el.offsetTop >= parent.scrollTop;
|
||||
}
|
||||
|
||||
return isVisible;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove html tags from a string
|
||||
* @param {String} Initial string/html
|
||||
* @return {String} Sanitised string
|
||||
*/
|
||||
export const stripHTML = function(html) {
|
||||
let el = document.createElement("DIV");
|
||||
el.innerHTML = html;
|
||||
return el.textContent || el.innerText || "";
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) => {
|
||||
let animationEvent = whichAnimationEvent();
|
||||
|
||||
let 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
|
||||
* @param {Number} max Maximum range
|
||||
* @return {Number} Random number
|
||||
*/
|
||||
export const getRandomNumber = function(min, max) {
|
||||
return Math.floor(Math.random() * (max - min) + min);
|
||||
};
|
||||
|
||||
/**
|
||||
* Turn a string into a node
|
||||
* @param {String} String to convert
|
||||
* @return {HTMLElement} Converted node element
|
||||
*/
|
||||
export const strToEl = (function() {
|
||||
var tmpEl = document.createElement('div');
|
||||
return function(str) {
|
||||
var r;
|
||||
tmpEl.innerHTML = str;
|
||||
r = tmpEl.children[0];
|
||||
|
||||
while (tmpEl.firstChild) {
|
||||
tmpEl.removeChild(tmpEl.firstChild);
|
||||
}
|
||||
|
||||
return r;
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* Sets the width of a passed input based on its value
|
||||
* @return {Number} Width of input
|
||||
*/
|
||||
export const getWidthOfInput = (input) => {
|
||||
const value = input.value || input.placeholder;
|
||||
let width = input.offsetWidth;
|
||||
|
||||
if (value) {
|
||||
const testEl = strToEl(`<span>${ value }</span>`);
|
||||
testEl.style.position = 'absolute';
|
||||
testEl.style.padding = '0';
|
||||
testEl.style.top = '-9999px';
|
||||
testEl.style.left = '-9999px';
|
||||
testEl.style.width = 'auto';
|
||||
testEl.style.whiteSpace = 'pre';
|
||||
|
||||
document.body.appendChild(testEl);
|
||||
|
||||
if (value && testEl.offsetWidth !== input.offsetWidth) {
|
||||
width = testEl.offsetWidth + 4;
|
||||
}
|
||||
|
||||
document.body.removeChild(testEl);
|
||||
}
|
||||
|
||||
return `${width}px`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sorting function for current and previous string
|
||||
* @param {String} a Current value
|
||||
* @param {String} b Next value
|
||||
* @return {Number} -1 for after previous,
|
||||
* 1 for before,
|
||||
* 0 for same location
|
||||
*/
|
||||
export const sortByAlpha = (a, b) => {
|
||||
const labelA = (a.label || a.value).toLowerCase();
|
||||
const labelB = (b.label || b.value).toLowerCase();
|
||||
|
||||
if (labelA < labelB) return -1;
|
||||
if (labelA > labelB) return 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort by numeric score
|
||||
* @param {Object} a Current value
|
||||
* @param {Object} b Next value
|
||||
* @return {Number} -1 for after previous,
|
||||
* 1 for before,
|
||||
* 0 for same location
|
||||
*/
|
||||
export const sortByScore = (a, b) => {
|
||||
return a.score - b.score;
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger native event
|
||||
* @param {NodeElement} element Element to trigger event on
|
||||
* @param {String} type Type of event to trigger
|
||||
* @param {Object} customArgs Data to pass with event
|
||||
* @return {Object} Triggered event
|
||||
*/
|
||||
export const triggerEvent = (element, type, customArgs = null) => {
|
||||
const event = new CustomEvent(type, {
|
||||
detail: customArgs,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
|
||||
return element.dispatchEvent(event);
|
||||
};
|
97
assets/scripts/src/reducers/choices.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
const choices = (state = [], action) => {
|
||||
switch (action.type) {
|
||||
case 'ADD_CHOICE': {
|
||||
/*
|
||||
A disabled choice appears in the choice dropdown but cannot be selected
|
||||
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,
|
||||
groupId: action.groupId,
|
||||
value: action.value,
|
||||
label: action.label,
|
||||
disabled: action.disabled,
|
||||
selected: false,
|
||||
active: true,
|
||||
score: 9999,
|
||||
}];
|
||||
}
|
||||
|
||||
case 'ADD_ITEM': {
|
||||
let newState = state;
|
||||
|
||||
// If all choices need to be activated
|
||||
if (action.activateOptions) {
|
||||
newState = state.map((choice) => {
|
||||
choice.active = action.active;
|
||||
return choice;
|
||||
});
|
||||
}
|
||||
// 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) {
|
||||
newState = state.map((choice) => {
|
||||
if (choice.id === parseInt(action.choiceId, 10)) {
|
||||
choice.selected = true;
|
||||
}
|
||||
return choice;
|
||||
});
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
case 'REMOVE_ITEM': {
|
||||
// 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((choice) => {
|
||||
if (choice.id === parseInt(action.choiceId, 10)) {
|
||||
choice.selected = false;
|
||||
}
|
||||
return choice;
|
||||
});
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
case 'FILTER_CHOICES': {
|
||||
const filteredResults = action.results;
|
||||
const filteredState = state.map((choice) => {
|
||||
// Set active state based on whether choice is
|
||||
// within filtered results
|
||||
|
||||
choice.active = filteredResults.some((result) => {
|
||||
if (result.item.id === choice.id) {
|
||||
choice.score = result.score;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return choice;
|
||||
});
|
||||
|
||||
return filteredState;
|
||||
}
|
||||
|
||||
case 'ACTIVATE_CHOICES': {
|
||||
return state.map((choice) => {
|
||||
choice.active = action.active;
|
||||
return choice;
|
||||
});
|
||||
}
|
||||
|
||||
case 'CLEAR_CHOICES': {
|
||||
return state.choices = [];
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default choices;
|
22
assets/scripts/src/reducers/groups.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
const groups = (state = [], action) => {
|
||||
switch (action.type) {
|
||||
case 'ADD_GROUP': {
|
||||
return [...state, {
|
||||
id: action.id,
|
||||
value: action.value,
|
||||
active: action.active,
|
||||
disabled: action.disabled,
|
||||
}];
|
||||
}
|
||||
|
||||
case 'CLEAR_CHOICES': {
|
||||
return state.groups = [];
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default groups;
|
|
@ -2,33 +2,21 @@ import { combineReducers } from 'redux';
|
|||
import items from './items';
|
||||
import groups from './groups';
|
||||
import choices from './choices';
|
||||
import loading from './loading';
|
||||
import { cloneObject } from '../lib/utils';
|
||||
|
||||
export const defaultState = {
|
||||
groups: [],
|
||||
items: [],
|
||||
choices: [],
|
||||
loading: false,
|
||||
};
|
||||
|
||||
const appReducer = combineReducers({
|
||||
items,
|
||||
groups,
|
||||
choices,
|
||||
loading,
|
||||
});
|
||||
|
||||
const rootReducer = (passedState, action): object => {
|
||||
const rootReducer = (passedState, action) => {
|
||||
let state = passedState;
|
||||
// If we are clearing all items, groups and options we reassign
|
||||
// state and then pass that state to our proper reducer. This isn't
|
||||
// mutating our actual state
|
||||
// See: http://stackoverflow.com/a/35641992
|
||||
if (action.type === 'CLEAR_ALL') {
|
||||
state = defaultState;
|
||||
} else if (action.type === 'RESET_TO') {
|
||||
return cloneObject(action.state);
|
||||
state = undefined;
|
||||
}
|
||||
|
||||
return appReducer(state, action);
|
48
assets/scripts/src/reducers/items.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
const items = (state = [], 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,
|
||||
}];
|
||||
|
||||
return newState.map((item) => {
|
||||
if (item.highlighted) {
|
||||
item.highlighted = false;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
case 'REMOVE_ITEM': {
|
||||
// Set item to inactive
|
||||
return state.map((item) => {
|
||||
if (item.id === action.id) {
|
||||
item.active = false;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
case 'HIGHLIGHT_ITEM': {
|
||||
return state.map((item) => {
|
||||
if (item.id === action.id) {
|
||||
item.highlighted = action.highlighted;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default items;
|
164
assets/scripts/src/store/index.js
Normal file
|
@ -0,0 +1,164 @@
|
|||
import { createStore } from 'redux';
|
||||
import rootReducer from './../reducers/index.js';
|
||||
|
||||
export default class Store {
|
||||
constructor() {
|
||||
this.store = createStore(
|
||||
rootReducer
|
||||
, window.devToolsExtension ? window.devToolsExtension() : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get store object (wrapping Redux method)
|
||||
* @return {Object} State
|
||||
*/
|
||||
getState() {
|
||||
return this.store.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch event to store (wrapped Redux method)
|
||||
* @param {Function} action Action function to trigger
|
||||
* @return
|
||||
*/
|
||||
dispatch(action) {
|
||||
this.store.dispatch(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe store to function call (wrapped Redux method)
|
||||
* @param {Function} onChange Function to trigger when state changes
|
||||
* @return
|
||||
*/
|
||||
subscribe(onChange) {
|
||||
this.store.subscribe(onChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items from store
|
||||
* @return {Array} Item objects
|
||||
*/
|
||||
getItems() {
|
||||
const state = this.store.getState();
|
||||
return state.items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active items from store
|
||||
* @return {Array} Item objects
|
||||
*/
|
||||
getItemsFilteredByActive() {
|
||||
const items = this.getItems();
|
||||
const values = items.filter((item) => {
|
||||
return item.active === true;
|
||||
}, []);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items from store reduced to just their values
|
||||
* @return {Array} Item objects
|
||||
*/
|
||||
getItemsReducedToValues(items = this.getItems()) {
|
||||
const values = items.reduce((prev, current) => {
|
||||
prev.push(current.value);
|
||||
return prev;
|
||||
}, []);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get choices from store
|
||||
* @return {Array} Option objects
|
||||
*/
|
||||
getChoices() {
|
||||
const state = this.store.getState();
|
||||
return state.choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active choices from store
|
||||
* @return {Array} Option objects
|
||||
*/
|
||||
getChoicesFilteredByActive() {
|
||||
const choices = this.getChoices();
|
||||
const values = choices.filter((choice) => {
|
||||
return choice.active === true;
|
||||
}, []);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get selectable choices from store
|
||||
* @return {Array} Option objects
|
||||
*/
|
||||
getChoicesFilteredBySelectable() {
|
||||
const choices = this.getChoices();
|
||||
const values = choices.filter((choice) => {
|
||||
return choice.disabled !== true;
|
||||
}, []);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single choice by it's ID
|
||||
* @return {Object} Found choice
|
||||
*/
|
||||
getChoiceById(id) {
|
||||
if (id) {
|
||||
const choices = this.getChoicesFilteredByActive();
|
||||
const foundChoice = choices.find((choice) => choice.id === parseInt(id, 10));
|
||||
return foundChoice;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get groups from store
|
||||
* @return {Array} Group objects
|
||||
*/
|
||||
getGroups() {
|
||||
const state = this.store.getState();
|
||||
return state.groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active groups from store
|
||||
* @return {Array} Group objects
|
||||
*/
|
||||
getGroupsFilteredByActive() {
|
||||
const groups = this.getGroups();
|
||||
const choices = this.getChoices();
|
||||
|
||||
const values = groups.filter((group) => {
|
||||
const isActive = group.active === true && group.disabled === false;
|
||||
const hasActiveOptions = choices.some((choice) => {
|
||||
return choice.active === true && choice.disabled === false;
|
||||
});
|
||||
return isActive && hasActiveOptions;
|
||||
}, []);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get group by group id
|
||||
* @param {Number} id Group ID
|
||||
* @return {Object} Group data
|
||||
*/
|
||||
getGroupById(id) {
|
||||
const groups = this.getGroups();
|
||||
const foundGroup = groups.find((group) => {
|
||||
return group.id === id;
|
||||
});
|
||||
|
||||
return foundGroup;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Store;
|
|
@ -1,19 +1,16 @@
|
|||
/* =============================================
|
||||
/*=============================================
|
||||
= Generic styling =
|
||||
============================================= */
|
||||
=============================================*/
|
||||
* {
|
||||
-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%;
|
||||
|
@ -24,7 +21,7 @@ body {
|
|||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
color: #fff;
|
||||
color: #FFFFFF;
|
||||
background-color: #333;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
@ -39,33 +36,25 @@ label {
|
|||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
margin: 30px 0;
|
||||
margin: 36px 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
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 {
|
||||
color: #fff;
|
||||
a, a:visited, a:focus {
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
@ -79,50 +68,42 @@ a:focus {
|
|||
border-radius: 2.5px;
|
||||
font-size: 14px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
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;
|
||||
}
|
||||
|
||||
label + p {
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: block;
|
||||
margin: auto;
|
||||
max-width: 40em;
|
||||
padding: 48px;
|
||||
}
|
||||
|
||||
@media (max-width: 620px) {
|
||||
.container {
|
||||
padding: 0;
|
||||
|
@ -130,13 +111,12 @@ label + p {
|
|||
}
|
||||
|
||||
.section {
|
||||
background-color: #fff;
|
||||
background-color: #FFFFFF;
|
||||
padding: 24px;
|
||||
color: #333;
|
||||
}
|
||||
.section a,
|
||||
.section a:visited,
|
||||
.section a:focus {
|
||||
|
||||
.section a, .section a:visited, .section a:focus {
|
||||
color: #00bcd4;
|
||||
}
|
||||
|
||||
|
@ -145,7 +125,7 @@ label + p {
|
|||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.logo-img {
|
||||
.logo__img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: inline-block;
|
||||
|
@ -158,24 +138,4 @@ label + p {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.push-bottom {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.zero-bottom {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.zero-top {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
[data-test-hook] {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* ===== End of Section comment block ====== */
|
||||
/*===== End of Section comment block ======*/
|
1
assets/styles/css/base.min.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,:after,:before{box-sizing:border-box}body,html{position:relative;margin:0;width:100%;height:100%}body{font-family:"Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;font-size:16px;line-height:1.4;color:#fff;background-color:#333;overflow-x:hidden}hr,label{display:block}label{margin-bottom:8px;font-size:14px;font-weight:500;cursor:pointer}p{margin-top:0}hr{margin:36px 0;border:0;border-bottom:1px solid #eaeaea;height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:12px;font-weight:400;line-height:1.2}a,a:focus,a:visited{color:#fff;text-decoration:none;font-weight:600}.form-control{display:block;width:100%;background-color:#f9f9f9;padding:12px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;margin-bottom:24px}.h1,h1{font-size:32px}.h2,h2{font-size:24px}.h3,h3{font-size:20px}.h4,h4{font-size:18px}.h5,h5{font-size:16px}.h6,h6{font-size:14px}.container{display:block;margin:auto;max-width:40em;padding:48px}@media (max-width:620px){.container{padding:0}}.section{background-color:#fff;padding:24px;color:#333}.section a,.section a:focus,.section a:visited{color:#00bcd4}.logo{display:block;margin-bottom:12px}.logo__img{width:100%;height:auto;display:inline-block;max-width:100%;vertical-align:top;padding:6px 0}.visible-ie{display:none}
|
|
@ -1,53 +1,55 @@
|
|||
/* ===============================
|
||||
/*===============================
|
||||
= Choices =
|
||||
=============================== */
|
||||
===============================*/
|
||||
.choices {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin-bottom: 24px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.choices:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.choices:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.choices.is-open {
|
||||
overflow: visible;
|
||||
}
|
||||
.choices.is-disabled .choices__inner,
|
||||
.choices.is-disabled .choices__input {
|
||||
background-color: #eaeaea;
|
||||
|
||||
.choices.is-disabled .choices__inner, .choices.is-disabled .choices__input {
|
||||
background-color: #EAEAEA;
|
||||
cursor: not-allowed;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.choices.is-disabled .choices__item {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.choices [hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.choices[data-type*=select-one] {
|
||||
.choices[data-type*="select-one"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
.choices[data-type*=select-one] .choices__inner {
|
||||
|
||||
.choices[data-type*="select-one"] .choices__inner {
|
||||
padding-bottom: 7.5px;
|
||||
}
|
||||
.choices[data-type*=select-one] .choices__input {
|
||||
|
||||
.choices[data-type*="select-one"] .choices__input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #DDDDDD;
|
||||
background-color: #FFFFFF;
|
||||
margin: 0;
|
||||
}
|
||||
.choices[data-type*=select-one] .choices__button {
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==");
|
||||
|
||||
.choices[data-type*="select-one"] .choices__button {
|
||||
background-image: url("../../icons//cross-inverse.svg");
|
||||
padding: 0;
|
||||
background-size: 8px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
|
@ -56,23 +58,23 @@
|
|||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 10em;
|
||||
opacity: 0.25;
|
||||
opacity: .5;
|
||||
}
|
||||
.choices[data-type*=select-one] .choices__button:hover, .choices[data-type*=select-one] .choices__button:focus {
|
||||
|
||||
.choices[data-type*="select-one"] .choices__button:hover, .choices[data-type*="select-one"] .choices__button:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
.choices[data-type*=select-one] .choices__button:focus {
|
||||
box-shadow: 0 0 0 2px #00bcd4;
|
||||
|
||||
.choices[data-type*="select-one"] .choices__button:focus {
|
||||
box-shadow: 0px 0px 0px 2px #00BCD4;
|
||||
}
|
||||
.choices[data-type*=select-one] .choices__item[data-value=""] .choices__button {
|
||||
display: none;
|
||||
}
|
||||
.choices[data-type*=select-one]::after {
|
||||
|
||||
.choices[data-type*="select-one"]:after {
|
||||
content: "";
|
||||
height: 0;
|
||||
width: 0;
|
||||
border-style: solid;
|
||||
border-color: #333 transparent transparent transparent;
|
||||
border-color: #333333 transparent transparent transparent;
|
||||
border-width: 5px;
|
||||
position: absolute;
|
||||
right: 11.5px;
|
||||
|
@ -80,27 +82,29 @@
|
|||
margin-top: -2.5px;
|
||||
pointer-events: none;
|
||||
}
|
||||
.choices[data-type*=select-one].is-open::after {
|
||||
border-color: transparent transparent #333 transparent;
|
||||
|
||||
.choices[data-type*="select-one"].is-open:after {
|
||||
border-color: transparent transparent #333333 transparent;
|
||||
margin-top: -7.5px;
|
||||
}
|
||||
.choices[data-type*=select-one][dir=rtl]::after {
|
||||
|
||||
.choices[data-type*="select-one"][dir="rtl"]:after {
|
||||
left: 11.5px;
|
||||
right: auto;
|
||||
}
|
||||
.choices[data-type*=select-one][dir=rtl] .choices__button {
|
||||
|
||||
.choices[data-type*="select-one"][dir="rtl"] .choices__button {
|
||||
right: auto;
|
||||
left: 0;
|
||||
margin-left: 25px;
|
||||
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;
|
||||
|
@ -109,16 +113,14 @@
|
|||
margin-left: 8px;
|
||||
padding-left: 16px;
|
||||
border-left: 1px solid #008fa1;
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==");
|
||||
background-image: url("../../icons//cross.svg");
|
||||
background-size: 8px;
|
||||
width: 8px;
|
||||
line-height: 1;
|
||||
opacity: 0.75;
|
||||
border-radius: 0;
|
||||
opacity: .75;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
|
||||
|
@ -128,18 +130,21 @@
|
|||
width: 100%;
|
||||
background-color: #f9f9f9;
|
||||
padding: 7.5px 7.5px 3.75px;
|
||||
border: 1px solid #ddd;
|
||||
border: 1px solid #DDDDDD;
|
||||
border-radius: 2.5px;
|
||||
font-size: 14px;
|
||||
min-height: 44px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.is-focused .choices__inner, .is-open .choices__inner {
|
||||
border-color: #b7b7b7;
|
||||
}
|
||||
|
||||
.is-open .choices__inner {
|
||||
border-radius: 2.5px 2.5px 0 0;
|
||||
}
|
||||
|
||||
.is-flipped.is-open .choices__inner {
|
||||
border-radius: 0 0 2.5px 2.5px;
|
||||
}
|
||||
|
@ -149,15 +154,18 @@
|
|||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.choices__list--single {
|
||||
display: inline-block;
|
||||
padding: 4px 16px 4px 4px;
|
||||
width: 100%;
|
||||
}
|
||||
[dir=rtl] .choices__list--single {
|
||||
|
||||
[dir="rtl"] .choices__list--single {
|
||||
padding-right: 4px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.choices__list--single .choices__item {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -165,6 +173,7 @@
|
|||
.choices__list--multiple {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.choices__list--multiple .choices__item {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
@ -174,99 +183,111 @@
|
|||
font-weight: 500;
|
||||
margin-right: 3.75px;
|
||||
margin-bottom: 3.75px;
|
||||
background-color: #00bcd4;
|
||||
background-color: #00BCD4;
|
||||
border: 1px solid #00a5bb;
|
||||
color: #fff;
|
||||
color: #FFFFFF;
|
||||
word-break: break-all;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.choices__list--multiple .choices__item[data-deletable] {
|
||||
padding-right: 5px;
|
||||
}
|
||||
[dir=rtl] .choices__list--multiple .choices__item {
|
||||
|
||||
[dir="rtl"] .choices__list--multiple .choices__item {
|
||||
margin-right: 0;
|
||||
margin-left: 3.75px;
|
||||
}
|
||||
|
||||
.choices__list--multiple .choices__item.is-highlighted {
|
||||
background-color: #00a5bb;
|
||||
border: 1px solid #008fa1;
|
||||
}
|
||||
|
||||
.is-disabled .choices__list--multiple .choices__item {
|
||||
background-color: #aaaaaa;
|
||||
border: 1px solid #919191;
|
||||
}
|
||||
|
||||
.choices__list--dropdown, .choices__list[aria-expanded] {
|
||||
visibility: hidden;
|
||||
.choices__list--dropdown {
|
||||
display: none;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #DDDDDD;
|
||||
top: 100%;
|
||||
margin-top: -1px;
|
||||
border-bottom-left-radius: 2.5px;
|
||||
border-bottom-right-radius: 2.5px;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
will-change: visibility;
|
||||
}
|
||||
.is-active.choices__list--dropdown, .is-active.choices__list[aria-expanded] {
|
||||
visibility: visible;
|
||||
|
||||
.choices__list--dropdown.is-active {
|
||||
display: block;
|
||||
}
|
||||
.is-open .choices__list--dropdown, .is-open .choices__list[aria-expanded] {
|
||||
|
||||
.is-open .choices__list--dropdown {
|
||||
border-color: #b7b7b7;
|
||||
}
|
||||
.is-flipped .choices__list--dropdown, .is-flipped .choices__list[aria-expanded] {
|
||||
|
||||
.is-flipped .choices__list--dropdown {
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
margin-top: 0;
|
||||
margin-bottom: -1px;
|
||||
border-radius: 0.25rem 0.25rem 0 0;
|
||||
border-radius: .25rem .25rem 0 0;
|
||||
}
|
||||
.choices__list--dropdown .choices__list, .choices__list[aria-expanded] .choices__list {
|
||||
|
||||
.choices__list--dropdown .choices__list {
|
||||
position: relative;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
will-change: scroll-position;
|
||||
}
|
||||
.choices__list--dropdown .choices__item, .choices__list[aria-expanded] .choices__item {
|
||||
|
||||
.choices__list--dropdown .choices__item {
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
[dir=rtl] .choices__list--dropdown .choices__item, [dir=rtl] .choices__list[aria-expanded] .choices__item {
|
||||
|
||||
[dir="rtl"] .choices__list--dropdown .choices__item {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.choices__list--dropdown .choices__item--selectable, .choices__list[aria-expanded] .choices__item--selectable {
|
||||
.choices__list--dropdown .choices__item--selectable {
|
||||
padding-right: 100px;
|
||||
}
|
||||
.choices__list--dropdown .choices__item--selectable::after, .choices__list[aria-expanded] .choices__item--selectable::after {
|
||||
.choices__list--dropdown .choices__item--selectable:after {
|
||||
content: attr(data-select-text);
|
||||
font-size: 12px;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
-webkit-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
[dir=rtl] .choices__list--dropdown .choices__item--selectable, [dir=rtl] .choices__list[aria-expanded] .choices__item--selectable {
|
||||
[dir="rtl"] .choices__list--dropdown .choices__item--selectable {
|
||||
text-align: right;
|
||||
padding-left: 100px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
[dir=rtl] .choices__list--dropdown .choices__item--selectable::after, [dir=rtl] .choices__list[aria-expanded] .choices__item--selectable::after {
|
||||
[dir="rtl"] .choices__list--dropdown .choices__item--selectable:after {
|
||||
right: auto;
|
||||
left: 10px;
|
||||
}
|
||||
}
|
||||
.choices__list--dropdown .choices__item--selectable.is-highlighted, .choices__list[aria-expanded] .choices__item--selectable.is-highlighted {
|
||||
|
||||
.choices__list--dropdown .choices__item--selectable.is-highlighted {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.choices__list--dropdown .choices__item--selectable.is-highlighted::after, .choices__list[aria-expanded] .choices__item--selectable.is-highlighted::after {
|
||||
opacity: 0.5;
|
||||
|
||||
.choices__list--dropdown .choices__item--selectable.is-highlighted:after {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.choices__item {
|
||||
|
@ -280,8 +301,10 @@
|
|||
.choices__item--disabled {
|
||||
cursor: not-allowed;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
opacity: 0.5;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.choices__heading {
|
||||
|
@ -295,13 +318,15 @@
|
|||
.choices__button {
|
||||
text-indent: -9999px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.choices__button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
@ -317,24 +342,18 @@
|
|||
max-width: 100%;
|
||||
padding: 4px 0 4px 2px;
|
||||
}
|
||||
|
||||
.choices__input:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.choices__input::-webkit-search-decoration, .choices__input::-webkit-search-cancel-button, .choices__input::-webkit-search-results-button, .choices__input::-webkit-search-results-decoration {
|
||||
display: none;
|
||||
}
|
||||
.choices__input::-ms-clear, .choices__input::-ms-reveal {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
[dir=rtl] .choices__input {
|
||||
|
||||
[dir="rtl"] .choices__input {
|
||||
padding-right: 2px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.choices__placeholder {
|
||||
opacity: 0.5;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
/* ===== End of Choices ====== */
|
||||
/*===== End of Choices ======*/
|
1
assets/styles/css/choices.min.css
vendored
Normal file
122
assets/styles/scss/base.scss
Normal file
|
@ -0,0 +1,122 @@
|
|||
$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 =
|
||||
=============================================*/
|
||||
|
||||
* {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale
|
||||
}
|
||||
|
||||
|
||||
*, *:before, *:after {
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
|
||||
html, body {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
color: #FFFFFF;
|
||||
background-color: #333;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
p { margin-top: 0; }
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
margin: $global-guttering*1.5 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #eaeaea;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
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 {
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
display: block;
|
||||
width: 100%;
|
||||
background-color: #f9f9f9;
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 2.5px;
|
||||
font-size: 14px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
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; }
|
||||
|
||||
.container {
|
||||
display: block;
|
||||
margin: auto;
|
||||
max-width: 40em;
|
||||
padding: $global-guttering*2;
|
||||
@media (max-width: 620px) { padding: 0; }
|
||||
}
|
||||
|
||||
.section {
|
||||
background-color: #FFFFFF;
|
||||
padding: $global-guttering;
|
||||
color: #333;
|
||||
a, a:visited, a:focus { color: #00bcd4; }
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin-bottom: $global-guttering/2;
|
||||
}
|
||||
|
||||
.logo__img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
vertical-align: top;
|
||||
padding: $global-guttering/4 0;
|
||||
}
|
||||
|
||||
.visible-ie { display: none; }
|
||||
|
||||
/*===== End of Section comment block ======*/
|
|
@ -1,10 +1,8 @@
|
|||
/* ===============================
|
||||
/*===============================
|
||||
= Choices =
|
||||
=============================== */
|
||||
===============================*/
|
||||
|
||||
@use "sass:color";
|
||||
|
||||
$choices-selector: "choices" !default;
|
||||
$choices-selector: 'choices' !default;
|
||||
$choices-font-size-lg: 16px !default;
|
||||
$choices-font-size-md: 14px !default;
|
||||
$choices-font-size-sm: 12px !default;
|
||||
|
@ -12,71 +10,49 @@ $choices-guttering: 24px !default;
|
|||
$choices-border-radius: 2.5px !default;
|
||||
$choices-border-radius-item: 20px !default;
|
||||
$choices-bg-color: #f9f9f9 !default;
|
||||
$choices-bg-color-disabled: #eaeaea !default;
|
||||
$choices-bg-color-dropdown: #fff !default;
|
||||
$choices-text-color: #333 !default;
|
||||
$choices-keyline-color: #ddd !default;
|
||||
$choices-primary-color: #00bcd4 !default;
|
||||
$choices-bg-color-disabled: #EAEAEA !default;
|
||||
$choices-bg-color-dropdown: #FFFFFF !default;
|
||||
$choices-text-color: #333333 !default;
|
||||
$choices-keyline-color: #DDDDDD !default;
|
||||
$choices-primary-color: #00BCD4 !default;
|
||||
$choices-disabled-color: #eaeaea !default;
|
||||
$choices-highlight-color: $choices-primary-color !default;
|
||||
$choices-button-icon-path: '../../icons/' !default;
|
||||
$choices-button-dimension: 8px !default;
|
||||
$choices-button-offset: 8px !default;
|
||||
$choices-icon-cross: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==") !default;
|
||||
$choices-icon-cross-inverse: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==") !default;
|
||||
$choices-z-index: 1 !default;
|
||||
|
||||
.#{$choices-selector} {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin-bottom: $choices-guttering;
|
||||
font-size: $choices-font-size-lg;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.is-open {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
&: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;
|
||||
}
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
.#{$choices-selector}__item { cursor: not-allowed; }
|
||||
}
|
||||
}
|
||||
|
||||
.#{$choices-selector}[data-type*='select-one'] {
|
||||
.#{$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%;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid $choices-keyline-color;
|
||||
background-color: #fff;
|
||||
background-color: #FFFFFF;
|
||||
margin: 0;
|
||||
}
|
||||
.#{$choices-selector}__button {
|
||||
background-image: $choices-icon-cross-inverse;
|
||||
background-image: url($choices-button-icon-path + '/cross-inverse.svg');
|
||||
padding: 0;
|
||||
background-size: 8px;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
|
@ -85,22 +61,11 @@ $choices-z-index: 1 !default;
|
|||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 10em;
|
||||
opacity: 0.25;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px $choices-highlight-color;
|
||||
}
|
||||
opacity: .5;
|
||||
&:hover, &:focus { opacity: 1; }
|
||||
&:focus { box-shadow: 0px 0px 0px 2px $choices-highlight-color; }
|
||||
}
|
||||
.#{$choices-selector}__item[data-value=''] .#{$choices-selector}__button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::after {
|
||||
&:after {
|
||||
content: "";
|
||||
height: 0;
|
||||
width: 0;
|
||||
|
@ -113,14 +78,12 @@ $choices-z-index: 1 !default;
|
|||
margin-top: -2.5px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.is-open::after {
|
||||
&.is-open:after {
|
||||
border-color: transparent transparent $choices-text-color transparent;
|
||||
margin-top: -7.5px;
|
||||
}
|
||||
|
||||
&[dir="rtl"] {
|
||||
&::after {
|
||||
&:after {
|
||||
left: 11.5px;
|
||||
right: auto;
|
||||
}
|
||||
|
@ -133,31 +96,23 @@ $choices-z-index: 1 !default;
|
|||
}
|
||||
}
|
||||
|
||||
.#{$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;
|
||||
margin-top: 0;
|
||||
margin-right: -$choices-button-offset * 0.5;
|
||||
margin-right: -$choices-button-offset/2;
|
||||
margin-bottom: 0;
|
||||
margin-left: $choices-button-offset;
|
||||
padding-left: $choices-button-offset * 2;
|
||||
border-left: 1px solid color.adjust($choices-primary-color, $lightness: -10%);
|
||||
background-image: $choices-icon-cross;
|
||||
padding-left: $choices-button-offset*2;
|
||||
border-left: 1px solid darken($choices-primary-color, 10%);
|
||||
background-image: url($choices-button-icon-path + '/cross.svg');
|
||||
background-size: $choices-button-dimension;
|
||||
width: $choices-button-dimension;
|
||||
line-height: 1;
|
||||
opacity: 0.75;
|
||||
border-radius: 0;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
opacity: .75;
|
||||
&:hover, &:focus { opacity: 1; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,43 +127,26 @@ $choices-z-index: 1 !default;
|
|||
font-size: $choices-font-size-md;
|
||||
min-height: 44px;
|
||||
overflow: hidden;
|
||||
|
||||
.is-focused &,
|
||||
.is-open & {
|
||||
border-color: color.adjust($choices-keyline-color, $lightness: -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 {
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
|
||||
&[aria-expanded] {
|
||||
@extend %choices-dropdown;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$choices-selector}__list--single {
|
||||
display: inline-block;
|
||||
padding: 4px 16px 4px 4px;
|
||||
width: 100%;
|
||||
|
||||
[dir="rtl"] & {
|
||||
padding-right: 4px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
.#{$choices-selector}__item {
|
||||
width: 100%;
|
||||
}
|
||||
.#{$choices-selector}__item { width: 100%; }
|
||||
}
|
||||
|
||||
.#{$choices-selector}__list--multiple {
|
||||
|
@ -223,35 +161,28 @@ $choices-z-index: 1 !default;
|
|||
margin-right: 3.75px;
|
||||
margin-bottom: 3.75px;
|
||||
background-color: $choices-primary-color;
|
||||
border: 1px solid color.adjust($choices-primary-color, $lightness: -5%);
|
||||
color: #fff;
|
||||
border: 1px solid darken($choices-primary-color, 5%);
|
||||
color: #FFFFFF;
|
||||
word-break: break-all;
|
||||
box-sizing: border-box;
|
||||
|
||||
&[data-deletable] {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
&[data-deletable] { padding-right: 5px; }
|
||||
[dir="rtl"] & {
|
||||
margin-right: 0;
|
||||
margin-left: 3.75px;
|
||||
}
|
||||
|
||||
&.is-highlighted {
|
||||
background-color: color.adjust($choices-primary-color, $lightness: -5%);
|
||||
border: 1px solid color.adjust($choices-primary-color, $lightness: -10%);
|
||||
background-color: darken($choices-primary-color, 5%);
|
||||
border: 1px solid darken($choices-primary-color, 10%);
|
||||
}
|
||||
|
||||
.is-disabled & {
|
||||
background-color: color.adjust($choices-disabled-color, $lightness: -25%);
|
||||
border: 1px solid color.adjust($choices-disabled-color, $lightness: -35%);
|
||||
background-color: darken($choices-disabled-color, 25%);
|
||||
border: 1px solid darken($choices-disabled-color, 35%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
%choices-dropdown {
|
||||
visibility: hidden;
|
||||
z-index: $choices-z-index;
|
||||
.#{$choices-selector}__list--dropdown {
|
||||
display: none;
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
background-color: $choices-bg-color-dropdown;
|
||||
|
@ -262,22 +193,14 @@ $choices-z-index: 1 !default;
|
|||
border-bottom-right-radius: $choices-border-radius;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
will-change: visibility;
|
||||
|
||||
&.is-active {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.is-open & {
|
||||
border-color: color.adjust($choices-keyline-color, $lightness: -15%);
|
||||
}
|
||||
|
||||
&.is-active { display: block; }
|
||||
.is-open & { border-color: darken($choices-keyline-color, 15%); }
|
||||
.is-flipped & {
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
margin-top: 0;
|
||||
margin-bottom: -1px;
|
||||
border-radius: 0.25rem 0.25rem 0 0;
|
||||
border-radius: .25rem .25rem 0 0;
|
||||
}
|
||||
.#{$choices-selector}__list {
|
||||
position: relative;
|
||||
|
@ -290,16 +213,12 @@ $choices-z-index: 1 !default;
|
|||
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) {
|
||||
padding-right: 100px;
|
||||
|
||||
&::after {
|
||||
&:after {
|
||||
content: attr(data-select-text);
|
||||
font-size: $choices-font-size-sm;
|
||||
opacity: 0;
|
||||
|
@ -308,106 +227,68 @@ $choices-z-index: 1 !default;
|
|||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
[dir="rtl"] & {
|
||||
text-align: right;
|
||||
padding-left: 100px;
|
||||
padding-right: 10px;
|
||||
|
||||
&::after {
|
||||
&:after {
|
||||
right: auto;
|
||||
left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-highlighted {
|
||||
background-color: color.mix(#000, #fff, 5%);
|
||||
|
||||
&::after {
|
||||
opacity: 0.5;
|
||||
}
|
||||
background-color: mix(#000000, #FFFFFF, 5%);
|
||||
&:after { opacity: .5; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$choices-selector}__list--dropdown {
|
||||
@extend %choices-dropdown;
|
||||
}
|
||||
|
||||
.#{$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;
|
||||
opacity: 0.5;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.#{$choices-selector}__heading {
|
||||
font-weight: 600;
|
||||
font-size: $choices-font-size-sm;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid color.adjust($choices-keyline-color, $lightness: 10%);
|
||||
color: color.adjust(#333, $lightness: 30%);
|
||||
border-bottom: 1px solid lighten($choices-keyline-color, 10%);
|
||||
color: lighten(#333, 30%);
|
||||
}
|
||||
|
||||
.#{$choices-selector}__button {
|
||||
text-indent: -9999px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
&:focus { outline: none; }
|
||||
}
|
||||
|
||||
.#{$choices-selector}__input {
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
background-color: $choices-bg-color;
|
||||
background-color: mix(#000000, #FFFFFF, 2.5%);
|
||||
font-size: $choices-font-size-md;
|
||||
margin-bottom: 5px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
max-width: 100%;
|
||||
padding: 4px 0 4px 2px;
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&::-webkit-search-decoration,
|
||||
&::-webkit-search-cancel-button,
|
||||
&::-webkit-search-results-button,
|
||||
&::-webkit-search-results-decoration {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-ms-clear,
|
||||
&::-ms-reveal {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
&:focus { outline: 0; }
|
||||
[dir="rtl"] & {
|
||||
padding-right: 2px;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.#{$choices-selector}__placeholder {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.#{$choices-selector}__placeholder { opacity: .5; }
|
||||
|
||||
/* ===== End of Choices ====== */
|
||||
/*===== End of Choices ======*/
|
29
bower.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "choices.js",
|
||||
"version": "2.6.1",
|
||||
"description": "A vanilla JS customisable text input/select box plugin",
|
||||
"main": [
|
||||
"./assets/scripts/dist/choices.js",
|
||||
"./assets/styles/css/choices.css"
|
||||
],
|
||||
"authors": [
|
||||
"Josh Johnson"
|
||||
],
|
||||
"license": "MIT",
|
||||
"homepage": "https://joshuajohnson.co.uk/Choices/",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
],
|
||||
"keywords": [
|
||||
"customisable",
|
||||
"input",
|
||||
"select",
|
||||
"vanilla",
|
||||
"plugin",
|
||||
"js"
|
||||
]
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
|
||||
const { JSDOM } = require('jsdom');
|
||||
|
||||
const jsdom = new JSDOM(
|
||||
'<!doctype html><html><head><meta charset="utf-8"></head><body></body></html>',
|
||||
{
|
||||
pretendToBeVisual: true,
|
||||
},
|
||||
);
|
||||
const { window } = jsdom;
|
||||
|
||||
function copyProps(src, target) {
|
||||
const props = Object.getOwnPropertyNames(src)
|
||||
.filter(prop => typeof target[prop] === 'undefined')
|
||||
.map(prop => Object.getOwnPropertyDescriptor(src, prop));
|
||||
Object.defineProperties(target, props);
|
||||
}
|
||||
|
||||
function ignoreExtensions(extensions = [], returnValue = {}) {
|
||||
function noop() {
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
extensions.forEach(ext => {
|
||||
require.extensions[ext] = noop;
|
||||
});
|
||||
}
|
||||
|
||||
global.window = window;
|
||||
global.document = window.document;
|
||||
global.navigator = {
|
||||
userAgent: 'node.js',
|
||||
};
|
||||
global.CustomEvent = window.CustomEvent;
|
||||
global.Element = window.Element;
|
||||
global.HTMLElement = window.HTMLElement;
|
||||
global.Option = window.Option;
|
||||
global.HTMLOptionElement = window.HTMLOptionElement;
|
||||
global.HTMLOptGroupElement = window.HTMLOptGroupElement;
|
||||
global.HTMLSelectElement = window.HTMLSelectElement;
|
||||
global.HTMLInputElement = window.HTMLInputElement;
|
||||
global.DocumentFragment = window.DocumentFragment;
|
||||
global.requestAnimationFrame = window.requestAnimationFrame;
|
||||
window.matchMedia = () => true;
|
||||
|
||||
copyProps(window, global);
|
||||
|
||||
ignoreExtensions(['.scss', '.css']);
|
||||
ignoreExtensions(['.jpg', '.png', '.svg'], '');
|
|
@ -1,15 +0,0 @@
|
|||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
video: false,
|
||||
projectId: 'n7g5qp',
|
||||
e2e: {
|
||||
// We've imported your old cypress plugins here.
|
||||
// You may want to clean this up later by importing these.
|
||||
setupNodeEvents(on, config) {
|
||||
return require('./cypress/plugins/index.js')(on, config)
|
||||
},
|
||||
baseUrl: 'http://localhost:3001/test',
|
||||
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
|
||||
},
|
||||
})
|
|
@ -1,431 +0,0 @@
|
|||
describe('Choices - text element', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/text', {
|
||||
onBeforeLoad(win) {
|
||||
cy.stub(win.console, 'warn').as('consoleWarn');
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe('scenarios', () => {
|
||||
const textInput = 'testing';
|
||||
|
||||
describe('basic', () => {
|
||||
describe('adding items', () => {
|
||||
it('allows me to input items', () => {
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__input--cloned')
|
||||
.type(textInput)
|
||||
.type('{enter}');
|
||||
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.last()
|
||||
.should(($el) => {
|
||||
expect($el).to.contain(textInput);
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the value of the original input', () => {
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__input--cloned')
|
||||
.type(textInput)
|
||||
.type('{enter}');
|
||||
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__input[hidden]')
|
||||
.should('have.value', textInput);
|
||||
});
|
||||
|
||||
describe('inputting data', () => {
|
||||
it('shows a dropdown prompt', () => {
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__input--cloned')
|
||||
.type(textInput);
|
||||
|
||||
cy.get('[data-test-hook=basic]')
|
||||
.find('.choices__list--dropdown')
|
||||
.should('be.visible')
|
||||
.should(($dropdown) => {
|
||||
const dropdownText = $dropdown.text().trim();
|
||||
expect(dropdownText).to.equal(
|
||||
`Press Enter to add "${textInput}"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('editing items', () => {
|
||||
beforeEach(() => {
|
||||
for (let index = 0; index < 3; index++) {
|
||||
cy.get('[data-test-hook=edit-items]')
|
||||
.find('.choices__input--cloned')
|
||||
.type(textInput)
|
||||
.type('{enter}');
|
||||
}
|
||||
});
|
||||
|
||||
describe('on back space', () => {
|
||||
it('allows me to change my entry', () => {
|
||||
cy.get('[data-test-hook=edit-items]')
|
||||
.find('.choices__input--cloned')
|
||||
.type('{backspace}')
|
||||
.type('-edited')
|
||||
.type('{enter}');
|
||||
|
||||
cy.get('[data-test-hook=edit-items]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.last()
|
||||
.should(($choice) => {
|
||||
expect($choice.data('value')).to.equal(`${textInput}-edited`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('on cmd+a', () => {
|
||||
beforeEach(() => {
|
||||
cy.get('[data-test-hook=edit-items]')
|
||||
.find('.choices__input--cloned')
|
||||
.type('{cmd}a');
|
||||
});
|
||||
|
||||
it('highlights all items', () => {
|
||||
cy.get('[data-test-hook=edit-items]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.each(($choice) => {
|
||||
expect($choice.hasClass('is-highlighted')).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('on backspace', () => {
|
||||
it('clears all inputted values', () => {
|
||||
// two backspaces are needed as Cypress has an issue where
|
||||
// it will also insert an 'a' character into the text input
|
||||
cy.get('[data-test-hook=edit-items]')
|
||||
.find('.choices__input--cloned')
|
||||
.type('{backspace}{backspace}');
|
||||
|
||||
cy.get('[data-test-hook=edit-items]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.should('have.length', 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove button', () => {
|
||||
beforeEach(() => {
|
||||
cy.get('[data-test-hook=remove-button]')
|
||||
.find('.choices__input--cloned')
|
||||
.type(`${textInput}`)
|
||||
.type('{enter}');
|
||||
});
|
||||
|
||||
describe('on click', () => {
|
||||
it('removes respective choice', () => {
|
||||
cy.get('[data-test-hook=remove-button]')
|
||||
.find('.choices__list--multiple')
|
||||
.children()
|
||||
.should(($items) => {
|
||||
expect($items.length).to.equal(1);
|
||||
});
|
||||
|
||||
cy.get('[data-test-hook=remove-button]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.last()
|
||||
.find('.choices__button')
|
||||
.focus()
|
||||
.click();
|
||||
|
||||
cy.get('[data-test-hook=remove-button]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.should(($items) => {
|
||||
expect($items.length).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the value of the original input', () => {
|
||||
cy.get('[data-test-hook=remove-button]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.last()
|
||||
.find('.choices__button')
|
||||
.focus()
|
||||
.click();
|
||||
|
||||
cy.get('[data-test-hook=remove-button]')
|
||||
.find('.choices__input[hidden]')
|
||||
.then(($input) => {
|
||||
expect($input.val()).to.not.contain(textInput);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('unique values only', () => {
|
||||
describe('unique values', () => {
|
||||
beforeEach(() => {
|
||||
cy.get('[data-test-hook=unique-values]')
|
||||
.find('.choices__input--cloned')
|
||||
.type(`${textInput}`)
|
||||
.type('{enter}')
|
||||
.type(`${textInput}`)
|
||||
.type('{enter}');
|
||||
});
|
||||
|
||||
it('only allows me to input unique values', () => {
|
||||
cy.get('[data-test-hook=unique-values]')
|
||||
.find('.choices__list--multiple')
|
||||
.first()
|
||||
.children()
|
||||
.should(($items) => {
|
||||
expect($items.length).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inputting a non-unique value', () => {
|
||||
it('displays dropdown prompt', () => {
|
||||
cy.get('[data-test-hook=unique-values]')
|
||||
.find('.choices__list--dropdown')
|
||||
.should('be.visible')
|
||||
.should(($dropdown) => {
|
||||
const dropdownText = $dropdown.text().trim();
|
||||
expect(dropdownText).to.equal(
|
||||
'Only unique values can be added',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('input limit', () => {
|
||||
const inputLimit = 5;
|
||||
beforeEach(() => {
|
||||
for (let index = 0; index < inputLimit + 1; index++) {
|
||||
cy.get('[data-test-hook=input-limit]')
|
||||
.find('.choices__input--cloned')
|
||||
.type(`${textInput} + ${index}`)
|
||||
.type('{enter}');
|
||||
}
|
||||
});
|
||||
|
||||
it('does not let me input more than 5 choices', () => {
|
||||
cy.get('[data-test-hook=input-limit]')
|
||||
.find('.choices__list--multiple')
|
||||
.first()
|
||||
.children()
|
||||
.should(($items) => {
|
||||
expect($items.length).to.equal(inputLimit);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reaching input limit', () => {
|
||||
it('displays dropdown prompt', () => {
|
||||
cy.get('[data-test-hook=input-limit]')
|
||||
.find('.choices__list--dropdown')
|
||||
.should('be.visible')
|
||||
.should(($dropdown) => {
|
||||
const dropdownText = $dropdown.text().trim();
|
||||
expect(dropdownText).to.equal(
|
||||
`Only ${inputLimit} values can be added`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('add item filter', () => {
|
||||
describe('inputting a value that satisfies the filter', () => {
|
||||
const input = 'joe@bloggs.com';
|
||||
|
||||
it('allows me to add choice', () => {
|
||||
cy.get('[data-test-hook=add-item-filter]')
|
||||
.find('.choices__input--cloned')
|
||||
.type(input)
|
||||
.type('{enter}');
|
||||
|
||||
cy.get('[data-test-hook=add-item-filter]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.last()
|
||||
.should(($choice) => {
|
||||
expect($choice.text().trim()).to.equal(input);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('inputting a value that does not satisfy the regex', () => {
|
||||
it('displays dropdown prompt', () => {
|
||||
cy.get('[data-test-hook=add-item-filter]')
|
||||
.find('.choices__input--cloned')
|
||||
.type(`this is not an email address`)
|
||||
.type('{enter}');
|
||||
|
||||
cy.get('[data-test-hook=add-item-filter]')
|
||||
.find('.choices__list--dropdown')
|
||||
.should('be.visible')
|
||||
.should(($dropdown) => {
|
||||
const dropdownText = $dropdown.text().trim();
|
||||
expect(dropdownText).to.equal(
|
||||
'Only values matching specific conditions can be added',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('prepend/append', () => {
|
||||
beforeEach(() => {
|
||||
cy.get('[data-test-hook=prepend-append]')
|
||||
.find('.choices__input--cloned')
|
||||
.type(textInput)
|
||||
.type('{enter}');
|
||||
});
|
||||
|
||||
it('prepends and appends value to inputted value', () => {
|
||||
cy.get('[data-test-hook=prepend-append]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.last()
|
||||
.should(($choice) => {
|
||||
expect($choice.data('value')).to.equal(`before-${textInput}-after`);
|
||||
});
|
||||
});
|
||||
|
||||
it('displays just the inputted value to the user', () => {
|
||||
cy.get('[data-test-hook=prepend-append]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.last()
|
||||
.should(($choice) => {
|
||||
expect($choice.text()).to.not.contain(`before-${textInput}-after`);
|
||||
expect($choice.text()).to.contain(textInput);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('adding items disabled', () => {
|
||||
it('does not allow me to input data', () => {
|
||||
cy.get('[data-test-hook=adding-items-disabled]')
|
||||
.find('.choices__input--cloned')
|
||||
.should('be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
describe('disabled via attribute', () => {
|
||||
it('does not allow me to input data', () => {
|
||||
cy.get('[data-test-hook=disabled-via-attr]')
|
||||
.find('.choices__input--cloned')
|
||||
.should('be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
describe('pre-populated choices', () => {
|
||||
it('pre-populates choices', () => {
|
||||
cy.get('[data-test-hook=prepopulated]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.should(($choices) => {
|
||||
expect($choices.length).to.equal(2);
|
||||
});
|
||||
|
||||
cy.get('[data-test-hook=prepopulated]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.first()
|
||||
.should(($choice) => {
|
||||
expect($choice.text().trim()).to.equal('Josh Johnson');
|
||||
});
|
||||
|
||||
cy.get('[data-test-hook=prepopulated]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.last()
|
||||
.should(($choice) => {
|
||||
expect($choice.text().trim()).to.equal('Joe Bloggs');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('placeholder', () => {
|
||||
/*
|
||||
{
|
||||
placeholder: true,
|
||||
placeholderValue: 'I am a placeholder',
|
||||
}
|
||||
*/
|
||||
describe('when no value has been inputted', () => {
|
||||
it('displays a placeholder', () => {
|
||||
cy.get('[data-test-hook=placeholder]')
|
||||
.find('.choices__input--cloned')
|
||||
.should('have.attr', 'placeholder', 'I am a placeholder');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('allow html', () => {
|
||||
describe('is undefined', () => {
|
||||
it('logs a deprecation warning', () => {
|
||||
cy.get('@consoleWarn').should(
|
||||
'be.calledOnceWithExactly',
|
||||
'Deprecation warning: allowHTML will default to false in a future release. To render HTML in Choices, you will need to set it to true. Setting allowHTML will suppress this message.',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not show html as text', () => {
|
||||
cy.get('[data-test-hook=allowhtml-undefined]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.first()
|
||||
.should(($choice) => {
|
||||
expect($choice.text().trim()).to.equal('Mason Rogers');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('set to true', () => {
|
||||
it('does not show html as text', () => {
|
||||
cy.get('[data-test-hook=allowhtml-true]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.first()
|
||||
.should(($choice) => {
|
||||
expect($choice.text().trim()).to.equal('Mason Rogers');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('set to false', () => {
|
||||
it('shows html as text', () => {
|
||||
cy.get('[data-test-hook=allowhtml-false]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.first()
|
||||
.should(($choice) => {
|
||||
expect($choice.text().trim()).to.equal('<b>Mason Rogers</b>');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('within form', () => {
|
||||
describe('inputting item', () => {
|
||||
describe('on enter key', () => {
|
||||
it('does not submit form', () => {
|
||||
cy.get('[data-test-hook=within-form] form').then(($form) => {
|
||||
$form.submit(() => {
|
||||
// this will fail the test if the form submits
|
||||
throw new Error('Form submitted');
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('[data-test-hook=within-form]')
|
||||
.find('.choices__input--cloned')
|
||||
.type(textInput)
|
||||
.type('{enter}');
|
||||
|
||||
cy.get('[data-test-hook=within-form]')
|
||||
.find('.choices__list--multiple .choices__item')
|
||||
.last()
|
||||
.should(($el) => {
|
||||
expect($el).to.contain(textInput);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
};
|
|
@ -1,25 +0,0 @@
|
|||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
|
@ -1,20 +0,0 @@
|
|||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
466
index.html
Normal file
|
@ -0,0 +1,466 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<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="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="assets/styles/css/base.min.css?version=2.6.1">
|
||||
<!-- End ignore these -->
|
||||
|
||||
<!-- Optional includes -->
|
||||
<script src="https://cdn.polyfill.io/v2/polyfill.js?features=es5,fetch,Element.prototype.classList,requestAnimationFrame,Node.insertBefore,Node.firstChild,Object.assign"></script>
|
||||
<!-- End optional includes -->
|
||||
|
||||
<!-- Choices includes -->
|
||||
<link rel="stylesheet" href="assets/styles/css/choices.min.css?version=2.6.1">
|
||||
<script src="assets/scripts/dist/choices.min.js?version=2.6.1"></script>
|
||||
<!-- End Choices includes -->
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<style>
|
||||
.hidden-ie { display: none; }
|
||||
.visible-ie { display: block; }
|
||||
</style>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="section">
|
||||
<a href="https://github.com/jshjohnson/Choices" class="logo">
|
||||
<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 the jQuery dependency.</p>
|
||||
<p>For all config options, visit the <a href="https://github.com/jshjohnson/Choices">GitHub repo</a>.</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Text inputs</h2>
|
||||
<label for="choices-text-remove-button">Limited to 5 values with remove button</label>
|
||||
<input class="form-control" id="choices-text-remove-button" type="text" value="preset-1,preset-2" placeholder="Enter something">
|
||||
|
||||
<label for="choices-text-unique-values">Unique values only, no pasting</label>
|
||||
<input class="form-control" id="choices-text-unique-values" type="text" value="preset-1, preset-2" placeholder="This is a placeholder" class="custom class">
|
||||
|
||||
<label for="choices-text-email-filter">Email addresses only</label>
|
||||
<input class="form-control" id="choices-text-email-filter" type="text" placeholder="This is a placeholder">
|
||||
|
||||
<label for="choices-text-disabled">Disabled</label>
|
||||
<input class="form-control" id="choices-text-disabled" type="text" value="josh@joshuajohnson.co.uk, joe@bloggs.co.uk" placeholder="This is a placeholder">
|
||||
|
||||
<label for="choices-text-prepend-append-value">Prepends and appends a value to each items return value</label>
|
||||
<input class="form-control" id="choices-text-prepend-append-value" type="text" value="preset-1, preset-2" placeholder="This is a placeholder">
|
||||
|
||||
<label for="choices-text-preset-values">Preset values passed through options</label>
|
||||
<input class="form-control" id="choices-text-preset-values" type="text" value="olivia@benson.com" placeholder="This is a placeholder">
|
||||
|
||||
<label for="choices-text-i18n">I18N labels</label>
|
||||
<input class="form-control" data-trigger id="choices-text-i18n" type="text">
|
||||
|
||||
<label for="choices-text-rtl">Right-to-left</label>
|
||||
<input class="form-control" data-trigger id="choices-text-rtl" type="text" value="Value 1, Value 2" dir="rtl">
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Multiple select input</h2>
|
||||
<label for="choices-multiple-default">Default</label>
|
||||
<select class="form-control" data-trigger name="choices-multiple-default" id="choices-multiple-default" placeholder="This is a placeholder" multiple>
|
||||
<option value="Dropdown item 1" selected>Dropdown item 1</option>
|
||||
<option value="Dropdown item 2">Dropdown item 2</option>
|
||||
<option value="Dropdown item 3">Dropdown item 3</option>
|
||||
<option value="Dropdown item 4" disabled>Dropdown item 4</option>
|
||||
</select>
|
||||
|
||||
<label for="choices-multiple-remove-button">With remove button</label>
|
||||
<select class="form-control" name="choices-multiple-remove-button" id="choices-multiple-remove-button" placeholder="This is a placeholder" multiple>
|
||||
<option value="Dropdown item 1" selected>Dropdown item 1</option>
|
||||
<option value="Dropdown item 2">Dropdown item 2</option>
|
||||
<option value="Dropdown item 3">Dropdown item 3</option>
|
||||
<option value="Dropdown item 4">Dropdown item 4</option>
|
||||
</select>
|
||||
|
||||
<label for="choices-multiple-groups">Option groups</label>
|
||||
<select class="form-control" name="choices-multiple-groups" id="choices-multiple-groups" placeholder="This is a placeholder" multiple>
|
||||
<optgroup label="UK">
|
||||
<option value="London">London</option>
|
||||
<option value="Manchester">Manchester</option>
|
||||
<option value="Liverpool">Liverpool</option>
|
||||
</optgroup>
|
||||
<optgroup label="FR">
|
||||
<option value="Paris">Paris</option>
|
||||
<option value="Lyon">Lyon</option>
|
||||
<option value="Marseille">Marseille</option>
|
||||
</optgroup>
|
||||
<optgroup label="DE" disabled>
|
||||
<option value="Hamburg">Hamburg</option>
|
||||
<option value="Munich">Munich</option>
|
||||
<option value="Berlin">Berlin</option>
|
||||
</optgroup>
|
||||
<optgroup label="US">
|
||||
<option value="New York">New York</option>
|
||||
<option value="Washington" disabled>Washington</option>
|
||||
<option value="Michigan">Michigan</option>
|
||||
</optgroup>
|
||||
<optgroup label="SP">
|
||||
<option value="Madrid">Madrid</option>
|
||||
<option value="Barcelona">Barcelona</option>
|
||||
<option value="Malaga">Malaga</option>
|
||||
</optgroup>
|
||||
<optgroup label="CA">
|
||||
<option value="Montreal">Montreal</option>
|
||||
<option value="Toronto">Toronto</option>
|
||||
<option value="Vancouver">Vancouver</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
|
||||
<p><small>If the following example do not load, the Discogs rate limit has probably been reached. Try again later!</small></p>
|
||||
|
||||
<label for="choices-multiple-remote-fetch">Options from remote source (Fetch API) & limited to 5</label>
|
||||
<select class="form-control" name="choices-multiple-remote-fetch" id="choices-multiple-remote-fetch" multiple></select>
|
||||
|
||||
<label for="choices-multiple-rtl">Right-to-left</label>
|
||||
<select class="form-control" data-trigger name="choices-multiple-rtl" id="choices-multiple-rtl" placeholder="This is a placeholder" multiple dir="rtl">
|
||||
<option value="Dropdown item 1" selected>Dropdown item 1</option>
|
||||
<option value="Dropdown item 2">Dropdown item 2</option>
|
||||
<option value="Dropdown item 3">Dropdown item 3</option>
|
||||
<option value="Dropdown item 4" disabled>Dropdown item 4</option>
|
||||
</select>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Single select input</h2>
|
||||
<label for="choices-single-default">Default</label>
|
||||
<select class="form-control" data-trigger name="choices-single-default" id="choices-single-default" placeholder="This is a search placeholder">
|
||||
<option selected disabled>This is a placeholder</option>
|
||||
<option value="Dropdown item 1">Dropdown item 1</option>
|
||||
<option value="Dropdown item 2">Dropdown item 2</option>
|
||||
<option value="Dropdown item 3">Dropdown item 3</option>
|
||||
</select>
|
||||
|
||||
<p><small>If the following two examples do not load, the Discogs rate limit has probably been reached. Try again later!</small></p>
|
||||
|
||||
<label for="choices-single-remote-fetch">Options from remote source (Fetch API)</label>
|
||||
<select class="form-control" name="choices-single-remote-fetch" id="choices-single-remote-fetch" placeholder="Pick an Arctic Monkeys record"></select>
|
||||
|
||||
<label for="choices-single-remove-xhr">Options from remote source (XHR) & remove button</label>
|
||||
<select class="form-control" name="choices-single-remove-xhr" id="choices-single-remove-xhr" placeholder="Pick a Smiths record"></select>
|
||||
|
||||
<label for="choices-single-groups">Option groups</label>
|
||||
<select class="form-control" data-trigger name="choices-single-groups" id="choices-single-groups" placeholder="This is a placeholder">
|
||||
<optgroup label="UK">
|
||||
<option value="London">London</option>
|
||||
<option value="Manchester">Manchester</option>
|
||||
<option value="Liverpool">Liverpool</option>
|
||||
</optgroup>
|
||||
<optgroup label="FR">
|
||||
<option value="Paris">Paris</option>
|
||||
<option value="Lyon">Lyon</option>
|
||||
<option value="Marseille">Marseille</option>
|
||||
</optgroup>
|
||||
<optgroup label="DE" disabled>
|
||||
<option value="Hamburg">Hamburg</option>
|
||||
<option value="Munich">Munich</option>
|
||||
<option value="Berlin">Berlin</option>
|
||||
</optgroup>
|
||||
<optgroup label="US">
|
||||
<option value="New York">New York</option>
|
||||
<option value="Washington" disabled>Washington</option>
|
||||
<option value="Michigan">Michigan</option>
|
||||
</optgroup>
|
||||
<optgroup label="SP">
|
||||
<option value="Madrid">Madrid</option>
|
||||
<option value="Barcelona">Barcelona</option>
|
||||
<option value="Malaga">Malaga</option>
|
||||
</optgroup>
|
||||
<optgroup label="CA">
|
||||
<option value="Montreal">Montreal</option>
|
||||
<option value="Toronto">Toronto</option>
|
||||
<option value="Vancouver">Vancouver</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
|
||||
<label for="choices-single-rtl">Right-to-left</label>
|
||||
<select class="form-control" data-trigger name="choices-single-rtl" id="choices-single-rtl" placeholder="This is a placeholder" dir="rtl">
|
||||
<option value="Dropdown item 1">Dropdown item 1</option>
|
||||
<option value="Dropdown item 2">Dropdown item 2</option>
|
||||
<option value="Dropdown item 3">Dropdown item 3</option>
|
||||
</select>
|
||||
|
||||
<label for="choices-single-no-search">Options added via config with no search</label>
|
||||
<select class="form-control" name="choices-single-no-search" id="choices-single-no-search">
|
||||
<option value="0">Zero</option>
|
||||
</select>
|
||||
|
||||
<label for="choices-single-preset-options">Option and option groups added via config</label>
|
||||
<select class="form-control" name="choices-single-preset-options" id="choices-single-preset-options" placeholder="This is a placeholder"></select>
|
||||
|
||||
<label for="choices-single-selected-option">Option selected via config</label>
|
||||
<select class="form-control" name="choices-single-selected-option" id="choices-single-selected-option" placeholder="This is a placeholder"></select>
|
||||
|
||||
<label for="choices-single-no-sorting">Options without sorting</label>
|
||||
<select class="form-control" name="choices-single-no-sorting" id="choices-single-no-sorting" placeholder="This is a placeholder">
|
||||
<option value="Madrid">Madrid</option>
|
||||
<option value="Toronto">Toronto</option>
|
||||
<option value="Vancouver">Vancouver</option>
|
||||
<option value="London">London</option>
|
||||
<option value="Manchester">Manchester</option>
|
||||
<option value="Liverpool">Liverpool</option>
|
||||
<option value="Paris">Paris</option>
|
||||
<option value="Malaga">Malaga</option>
|
||||
<option value="Washington" disabled>Washington</option>
|
||||
<option value="Lyon">Lyon</option>
|
||||
<option value="Marseille">Marseille</option>
|
||||
<option value="Hamburg">Hamburg</option>
|
||||
<option value="Munich">Munich</option>
|
||||
<option value="Barcelona">Barcelona</option>
|
||||
<option value="Berlin">Berlin</option>
|
||||
<option value="Montreal">Montreal</option>
|
||||
<option value="New York">New York</option>
|
||||
<option value="Michigan">Michigan</option>
|
||||
</select>
|
||||
|
||||
<label for="choices-custom-templates">Custom templates</label>
|
||||
<select class="form-control" name="choices-custom-templates" id="choices-custom-templates" placeholder="This is a placeholder">
|
||||
<option value="React">React</option>
|
||||
<option value="React">Angular</option>
|
||||
<option value="React">Ember</option>
|
||||
<option value="React">Vue</option>
|
||||
</select>
|
||||
|
||||
<p>Below is an example of how you could have two select inputs depend on eachother. 'Boroughs' will only be enabled if the value of 'States' is 'New York'</p>
|
||||
<label for="states">States</label>
|
||||
<select class="form-control" name="states" id="states" placeholder="Choose a state">
|
||||
<option value="Michigan">Michigan</option>
|
||||
<option value="Texas">Texas</option>
|
||||
<option value="Chicago">Chicago</option>
|
||||
<option value="New York">New York</option>
|
||||
<option value="Washington">Washington</option>
|
||||
</select>
|
||||
|
||||
<label for="boroughs">Boroughs</label>
|
||||
<select class="form-control" name="boroughs" id="boroughs" placeholder="Choose a borough">
|
||||
<option value="The Bronx">The Bronx</option>
|
||||
<option value="Brooklyn">Brooklyn</option>
|
||||
<option value="Manhattan">Manhattan</option>
|
||||
<option value="Queens">Queens</option>
|
||||
<option value="Staten Island">Staten Island</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var textRemove = new Choices(document.getElementById('choices-text-remove-button'), {
|
||||
delimiter: ',',
|
||||
editItems: true,
|
||||
maxItemCount: 5,
|
||||
removeItemButton: true,
|
||||
});
|
||||
|
||||
var textUniqueVals = new Choices('#choices-text-unique-values', {
|
||||
paste: false,
|
||||
duplicateItems: false,
|
||||
editItems: true,
|
||||
});
|
||||
|
||||
var texti18n = new Choices('#choices-text-i18n', {
|
||||
paste: false,
|
||||
duplicateItems: false,
|
||||
editItems: true,
|
||||
addItemText: (value) => {
|
||||
return `Appuyez sur Entrée pour ajouter <b>"${value}"</b>`;
|
||||
},
|
||||
maxItemText: (maxItemCount) => {
|
||||
return `${maxItemCount} valeurs peuvent être ajoutées`;
|
||||
},
|
||||
uniqueItemText: 'Cette valeur est déjà présente',
|
||||
});
|
||||
|
||||
var textEmailFilter = new Choices('#choices-text-email-filter', {
|
||||
editItems: true,
|
||||
regexFilter: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
|
||||
});
|
||||
|
||||
var textDisabled = new Choices('#choices-text-disabled', {
|
||||
addItems: false,
|
||||
removeItems: false,
|
||||
}).disable();
|
||||
|
||||
var textPrependAppendVal = new Choices('#choices-text-prepend-append-value', {
|
||||
prependValue: 'item-',
|
||||
appendValue: '-' + Date.now(),
|
||||
}).removeActiveItems();
|
||||
|
||||
var textPresetVal = new Choices('#choices-text-preset-values', {
|
||||
items: ['josh@joshuajohnson.co.uk', { value: 'joe@bloggs.co.uk', label: 'Joe Bloggs' } ],
|
||||
});
|
||||
|
||||
var multipleDefault = new Choices(document.getElementById('choices-multiple-groups'));
|
||||
|
||||
var multipleFetch = new Choices('#choices-multiple-remote-fetch', {
|
||||
placeholder: true,
|
||||
placeholderValue: 'Pick an Strokes record',
|
||||
maxItemCount: 5,
|
||||
}).ajax(function(callback) {
|
||||
fetch('https://api.discogs.com/artists/55980/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW')
|
||||
.then(function(response) {
|
||||
response.json().then(function(data) {
|
||||
callback(data.releases, 'title', 'title');
|
||||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
|
||||
var multipleCancelButton = new Choices('#choices-multiple-remove-button', {
|
||||
removeItemButton: true,
|
||||
})
|
||||
|
||||
var singleFetch = new Choices('#choices-single-remote-fetch', {
|
||||
placeholder: true,
|
||||
placeholderValue: 'Pick an Arctic Monkeys record'
|
||||
}).ajax(function(callback) {
|
||||
fetch('https://api.discogs.com/artists/391170/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW')
|
||||
.then(function(response) {
|
||||
response.json().then(function(data) {
|
||||
callback(data.releases, 'title', 'title');
|
||||
singleFetch.setValueByChoice('Fake Tales Of San Francisco');
|
||||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
|
||||
var singleXhrRemove = new Choices('#choices-single-remove-xhr', {
|
||||
removeItemButton: true,
|
||||
}).ajax(function(callback) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('get', 'https://api.discogs.com/artists/83080/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW', true);
|
||||
request.onreadystatechange = function() {
|
||||
var status;
|
||||
var data;
|
||||
if (request.readyState === 4) {
|
||||
status = request.status;
|
||||
if (status === 200) {
|
||||
data = JSON.parse(request.responseText);
|
||||
callback(data.releases, 'title', 'title');
|
||||
singleXhrRemove.setValueByChoice('How Soon Is Now?');
|
||||
} else {
|
||||
console.error(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
request.send();
|
||||
});
|
||||
|
||||
var genericExamples = new Choices('[data-trigger]', {
|
||||
placeholderValue: 'This is a placeholder set in the config'
|
||||
});
|
||||
|
||||
var singleNoSearch = new Choices('#choices-single-no-search', {
|
||||
search: false,
|
||||
removeItemButton: true,
|
||||
choices: [
|
||||
{value: 'One', label: 'Label One'},
|
||||
{value: 'Two', label: 'Label Two', disabled: true},
|
||||
{value: 'Three', label: 'Label Three'},
|
||||
],
|
||||
}).setChoices([
|
||||
{value: 'Four', label: 'Label Four', disabled: true},
|
||||
{value: 'Five', label: 'Label Five'},
|
||||
{value: 'Six', label: 'Label Six', selected: true},
|
||||
], 'value', 'label', false);
|
||||
|
||||
var singlePresetOpts = new Choices('#choices-single-preset-options', {
|
||||
placeholder: true,
|
||||
}).setChoices([{
|
||||
label: 'Group one',
|
||||
id: 1,
|
||||
disabled: false,
|
||||
choices: [
|
||||
{value: 'Child One', label: 'Child One', selected: true},
|
||||
{value: 'Child Two', label: 'Child Two', disabled: true},
|
||||
{value: 'Child Three', label: 'Child Three'},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Group two',
|
||||
id: 2,
|
||||
disabled: false,
|
||||
choices: [
|
||||
{value: 'Child Four', label: 'Child Four', disabled: true},
|
||||
{value: 'Child Five', label: 'Child Five'},
|
||||
{value: 'Child Six', label: 'Child Six'},
|
||||
]
|
||||
}], 'value', 'label');
|
||||
|
||||
var singleSelectedOpt = new Choices('#choices-single-selected-option', {
|
||||
choices: [
|
||||
{value: 'One', label: 'Label One', selected: true},
|
||||
{value: 'Two', label: 'Label Two', disabled: true},
|
||||
{value: 'Three', label: 'Label Three'},
|
||||
],
|
||||
}).setValueByChoice('Two');
|
||||
|
||||
var singleNoSorting = new Choices('#choices-single-no-sorting', {
|
||||
shouldSort: false,
|
||||
});
|
||||
|
||||
var states = new Choices(document.getElementById('states'));
|
||||
|
||||
states.passedElement.addEventListener('change', function(e) {
|
||||
if(e.detail.value === 'New York') {
|
||||
boroughs.enable();
|
||||
} else {
|
||||
boroughs.disable();
|
||||
}
|
||||
});
|
||||
|
||||
var customTemplates = new Choices(document.getElementById('choices-custom-templates'), {
|
||||
callbackOnCreateTemplates: function (strToEl) {
|
||||
var classNames = this.config.classNames;
|
||||
return {
|
||||
item: (data) => {
|
||||
return strToEl(`
|
||||
<div class="${classNames.item} ${data.highlighted ? classNames.highlightedState : classNames.itemSelectable}" data-item data-id="${data.id}" data-value="${data.value}" ${data.active ? 'aria-selected="true"' : ''} ${data.disabled ? 'aria-disabled="true"' : ''}>
|
||||
<span style="margin-right:10px;">🎉</span> ${data.label}
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
choice: (data) => {
|
||||
return strToEl(`
|
||||
<div class="${classNames.item} ${classNames.itemChoice} ${data.disabled ? classNames.itemDisabled : classNames.itemSelectable}" data-select-text="${this.config.itemSelectText}" data-choice ${data.disabled ? 'data-choice-disabled aria-disabled="true"' : 'data-choice-selectable'} data-id="${data.id}" data-value="${data.value}" ${data.groupId > 0 ? 'role="treeitem"' : 'role="option"'}>
|
||||
<span style="margin-right:10px;">👉🏽</span> ${data.label}
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
var boroughs = new Choices(document.getElementById('boroughs')).disable();
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Google Analytics - Ignore me -->
|
||||
<script>
|
||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||
ga('create', 'UA-31575166-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||||
<!-- End Google Analytics -->
|
||||
</body>
|
||||
</html>
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"checkJs": true,
|
||||
"target": "es2020",
|
||||
"lib": ["esnext", "dom"],
|
||||
"types": ["cypress"],
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
/* Additional Checks */
|
||||
"noImplicitAny": false,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"strictNullChecks": false
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
module.exports = {
|
||||
'*.js': ['eslint --fix --quiet -f visualstudio', 'git add'],
|
||||
'*.{ts,scss,yaml,yml,md,html,json,babelrc,eslintrc}': [
|
||||
'prettier --write',
|
||||
'git add',
|
||||
],
|
||||
'src/icons/*.svg': [
|
||||
'prettier --write --parser=html --html-whitespace-sensitivity=ignore',
|
||||
'git add',
|
||||
],
|
||||
'.codecov.yml': () =>
|
||||
'curl -f --silent --data-binary @.codecov.yml https://codecov.io/validate',
|
||||
'src/scripts/**/*.js': () => 'npm run test:unit',
|
||||
};
|
23844
package-lock.json
generated
164
package.json
|
@ -1,33 +1,18 @@
|
|||
{
|
||||
"name": "choices.js",
|
||||
"version": "10.2.0",
|
||||
"version": "2.6.1",
|
||||
"description": "A vanilla JS customisable text input/select box plugin",
|
||||
"main": "./public/assets/scripts/choices.js",
|
||||
"types": "./public/types/src/index.d.ts",
|
||||
"main": "./assets/scripts/dist/choices.min.js",
|
||||
"scripts": {
|
||||
"start": "run-p js:watch css:watch",
|
||||
"build": "run-p js:build css:build",
|
||||
"lint": "run-p lint:js lint:scss",
|
||||
"lint:js": "eslint src/scripts/**/*.ts",
|
||||
"lint:scss": "stylelint src/**/*.scss",
|
||||
"bundlesize": "bundlesize",
|
||||
"cypress:run": "cypress run --browser chrome",
|
||||
"cypress:open": "cypress open",
|
||||
"cypress:ci": "cypress run --browser chrome --record --group $GITHUB_REF --ci-build-id $GITHUB_SHA",
|
||||
"test": "run-s test:unit test:e2e",
|
||||
"test:unit": "cross-env TS_NODE_TRANSPILE_ONLY=true NODE_ENV=test mocha",
|
||||
"test:unit:watch": "npm run test:unit -- --watch --inspect=5556",
|
||||
"test:unit:coverage": "NODE_ENV=test nyc --reporter=lcov --reporter=text --reporter=text-summary mocha",
|
||||
"test:e2e": "run-p --race start cypress:run",
|
||||
"js:watch": "cross-env NODE_ENV=development node server.js",
|
||||
"js:build": "webpack --config webpack.config.prod.js",
|
||||
"start": "node server.js",
|
||||
"css:watch": "nodemon -e scss -x \"npm run css:build\"",
|
||||
"css:build": "run-s css:sass css:prefix css:min",
|
||||
"css:sass": "sass -I scss src/styles/base.scss public/assets/styles/base.css && sass -I scss src/styles/choices.scss public/assets/styles/choices.css",
|
||||
"css:prefix": "postcss public/assets/styles/*.css --use autoprefixer --no-map --env prod --dir public/assets/styles",
|
||||
"css:min": "csso public/assets/styles/base.css --output public/assets/styles/base.min.css && csso public/assets/styles/choices.css --output public/assets/styles/choices.min.css",
|
||||
"deploy": "git subtree push --prefix public origin gh-pages",
|
||||
"prepublishOnly": "npm run 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 assets/styles/scss/base.scss assets/styles/css/base.css && node-sass --output-style expanded --include-path scss assets/styles/scss/choices.scss assets/styles/css/choices.css",
|
||||
"css:prefix": "postcss --use autoprefixer -b 'last 2 versions' assets/styles/css/*.css -d assets/styles/css/",
|
||||
"css:min": "csso assets/styles/css/base.css assets/styles/css/base.min.css && csso assets/styles/css/choices.css assets/styles/css/choices.min.css",
|
||||
"js:build": "concurrently --prefix-colors yellow,green \"webpack --minimize --config webpack.config.prod.js\" \"webpack --config webpack.config.prod.js\"",
|
||||
"js:test": "./node_modules/karma/bin/karma start --single-run --no-auto-watch tests/karma.config.js",
|
||||
"js:test:watch": "./node_modules/karma/bin/karma start --auto-watch --no-single-run tests/karma.config.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -35,110 +20,57 @@
|
|||
},
|
||||
"author": "Josh Johnson",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"public/assets/scripts",
|
||||
"public/assets/styles",
|
||||
"public/types",
|
||||
"src",
|
||||
"!src/**/*.test.js",
|
||||
"types"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://github.com/jshjohnson/Choices/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jshjohnson/Choices#readme",
|
||||
"keywords": [
|
||||
"customisable",
|
||||
"input",
|
||||
"select",
|
||||
"vanilla",
|
||||
"plugin",
|
||||
"js"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/register": "^7.18.9",
|
||||
"@types/chai": "^4.3.4",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/sinon": "^10.0.13",
|
||||
"@types/sinon-chai": "^3.2.9",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"@typescript-eslint/parser": "^5.45.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"babel-loader": "^9.1.0",
|
||||
"bundlesize": "^0.18.1",
|
||||
"chai": "^4.3.7",
|
||||
"cross-env": "^7.0.3",
|
||||
"csso-cli": "^4.0.1",
|
||||
"cypress": "11.2.0",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-airbnb-typescript": "^17.0.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-compat": "4.0.2",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-sort-class-members": "^1.15.2",
|
||||
"eslint-webpack-plugin": "^3.2.0",
|
||||
"express": "^4.18.2",
|
||||
"husky": "^8.0.2",
|
||||
"jsdom": "^20.0.3",
|
||||
"lint-staged": "^13.0.4",
|
||||
"mocha": "^10.1.0",
|
||||
"nodemon": "^2.0.20",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"nyc": "^15.1.0",
|
||||
"postcss": "^8.4.19",
|
||||
"postcss-cli": "^10.0.0",
|
||||
"prettier": "^2.8.0",
|
||||
"sass": "^1.56.1",
|
||||
"sinon": "^15.0.0",
|
||||
"sinon-chai": "^3.7.0",
|
||||
"stylelint": "^14.15.0",
|
||||
"stylelint-config-standard": "^29.0.0",
|
||||
"stylelint-config-standard-scss": "^6.1.0",
|
||||
"ts-loader": "^9.4.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.3",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.0",
|
||||
"webpack-dev-middleware": "^6.0.1",
|
||||
"webpack-hot-middleware": "^2.25.3"
|
||||
"autoprefixer": "^6.3.3",
|
||||
"babel-core": "^6.7.2",
|
||||
"babel-eslint": "^6.1.2",
|
||||
"babel-loader": "^6.2.4",
|
||||
"babel-preset-es2015": "^6.6.0",
|
||||
"concurrently": "^3.1.0",
|
||||
"csso": "^1.8.2",
|
||||
"es6-promise": "^3.2.1",
|
||||
"eslint": "^3.3.0",
|
||||
"eslint-config-airbnb": "^10.0.1",
|
||||
"eslint-loader": "^1.5.0",
|
||||
"eslint-plugin-import": "^1.13.0",
|
||||
"eslint-plugin-jsx-a11y": "^2.2.3",
|
||||
"eslint-plugin-react": "^6.4.1",
|
||||
"jasmine-core": "2.4.1",
|
||||
"karma": "^1.1.0",
|
||||
"karma-coverage": "^1.0.0",
|
||||
"karma-es6-shim": "^1.0.0",
|
||||
"karma-htmlfile-reporter": "^0.3.4",
|
||||
"karma-jasmine": "^1.0.2",
|
||||
"karma-phantomjs-launcher": "^1.0.1",
|
||||
"karma-spec-reporter": "0.0.26",
|
||||
"karma-webpack": "^1.7.0",
|
||||
"node-sass": "^3.4.2",
|
||||
"nodemon": "^1.9.1",
|
||||
"opn-cli": "^3.1.0",
|
||||
"postcss-cli": "^2.5.1",
|
||||
"webpack": "^1.12.14",
|
||||
"webpack-dashboard": "^0.1.8",
|
||||
"webpack-dev-server": "^1.14.1",
|
||||
"whatwg-fetch": "^1.0.0",
|
||||
"wrapper-webpack-plugin": "^0.1.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"deepmerge": "^4.2.2",
|
||||
"fuse.js": "^6.6.2",
|
||||
"redux": "^4.2.0"
|
||||
"redux": "^3.3.1",
|
||||
"fuse.js": "^2.2.2"
|
||||
},
|
||||
"npmName": "choices.js",
|
||||
"npmFileMap": [
|
||||
{
|
||||
"basePath": "assets",
|
||||
"files": [
|
||||
"public/assets/scripts/*",
|
||||
"public/assets/styles/*",
|
||||
"public/types/*",
|
||||
"src/icons/*"
|
||||
"scripts/dist/*",
|
||||
"styles/css/*",
|
||||
"icons/*"
|
||||
]
|
||||
}
|
||||
],
|
||||
"nyc": {
|
||||
"include": [
|
||||
"src/scripts/**/**/*.js"
|
||||
],
|
||||
"exclude": [
|
||||
"src/scripts/**/**/*.test.js"
|
||||
]
|
||||
},
|
||||
"bundlesize": [
|
||||
{
|
||||
"path": "public/assets/scripts/choices.min.js",
|
||||
"maxSize": "25 kB"
|
||||
},
|
||||
{
|
||||
"path": "public/assets/styles/choices.min.css",
|
||||
"maxSize": "2.5 kB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
// get polyfill settings from top level config
|
||||
// @ts-ignore
|
||||
const { settings } = require('../../../.eslintrc.json');
|
||||
|
||||
// Adding non-polyfilable Symbol-related functions as they are most probably
|
||||
// behind the flag
|
||||
|
||||
settings.polyfills.push(
|
||||
'Symbol.toStringTag',
|
||||
'Symbol.for',
|
||||
'Object.getOwnPropertySymbols',
|
||||
'Object.getOwnPropertyDescriptors',
|
||||
'Promise', // Promise is gate checked
|
||||
);
|
||||
|
||||
module.exports = /** @type {import('eslint').Linter.Config} */ ({
|
||||
root: true,
|
||||
extends: ['plugin:compat/recommended'],
|
||||
parserOptions: {
|
||||
// ensure that it's compatible with ES5 browsers, so, no `const`, etc
|
||||
ecmaVersion: 5,
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
},
|
||||
settings,
|
||||
});
|
2
public/assets/scripts/choices.min.js
vendored
|
@ -1 +0,0 @@
|
|||
/*! choices.js v10.2.0 | © 2022 Josh Johnson | https://github.com/jshjohnson/Choices#readme */
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"sourceRoot":"","sources":["../../../src/styles/base.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAYA;EACE;EACA;;;AAGF;AAAA;AAAA;EAGE;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;EAME;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;EAGE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,eAtFiB;;;AAyFnB;AAAA;EAEE,WA1FoB;;;AA6FtB;AAAA;EAEE,WA9FoB;;;AAiGtB;AAAA;EAEE,WAlGoB;;;AAqGtB;AAAA;EAEE,WAtGoB;;;AAyGtB;AAAA;EAEE,WA1GoB;;;AA6GtB;AAAA;EAEE,WA9GoB;;;AAiHtB;EACE;;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EANF;IAOI;;;;AAIJ;EACE;EACA,SAxIiB;EAyIjB;;AAEA;AAAA;AAAA;EAGE;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE,eArKiB;;;AAwKnB;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE,eArLiB;;;AAwLnB","file":"base.css"}
|
1
public/assets/styles/base.min.css
vendored
|
@ -1 +0,0 @@
|
|||
*{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,::after,::before{box-sizing:border-box}body,html{position:relative;margin:0;width:100%;height:100%}body{font-family:"Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;font-size:16px;line-height:1.4;color:#fff;background-color:#333;overflow-x:hidden}hr,label{display:block}label,p{margin-bottom:8px}label{font-size:14px;font-weight:500;cursor:pointer}p{margin-top:0}hr{margin:30px 0;border:0;border-bottom:1px solid #eaeaea;height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:12px;font-weight:400;line-height:1.2}a,a:focus,a:visited{color:#fff;text-decoration:none;font-weight:600}.form-control{display:block;width:100%;background-color:#f9f9f9;padding:12px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;-webkit-appearance:none;appearance:none;margin-bottom:24px}.h1,h1{font-size:32px}.h2,h2{font-size:24px}.h3,h3{font-size:20px}.h4,h4{font-size:18px}.h5,h5{font-size:16px}.h6,h6{font-size:14px}label+p{margin-top:-4px}.container{display:block;margin:auto;max-width:40em;padding:48px}@media (max-width:620px){.container{padding:0}}.section{background-color:#fff;padding:24px;color:#333}.section a,.section a:focus,.section a:visited{color:#00bcd4}.logo{display:block;margin-bottom:12px}.logo-img{width:100%;height:auto;display:inline-block;max-width:100%;vertical-align:top;padding:6px 0}.visible-ie{display:none}.push-bottom{margin-bottom:24px}.zero-bottom{margin-bottom:0}.zero-top{margin-top:0}.text-center{text-align:center}[data-test-hook]{margin-bottom:24px}
|