├── tests
├── setup.ts
├── resources.ts
├── file-browser.spec.ts
├── toggle-switch.spec.ts
├── text-area.spec.ts
├── select-list.spec.ts
└── input-box.spec.ts
├── .travis.yml
├── .gitignore
├── .npmignore
├── src
├── index.ts
├── templates
│ ├── file-browser.html
│ ├── text-area.html
│ ├── toggle-switch.html
│ ├── input-box.html
│ └── select-list.html
├── file-browser.ts
├── toggle-switch.ts
├── vue-form-components.scss
├── text-area.ts
├── select-list.ts
└── input-box.ts
├── tsconfig.json
├── webpack.test.config.js
├── webpack.config.js
├── package.json
└── README.md
/tests/setup.ts:
--------------------------------------------------------------------------------
1 | require('jsdom-global')();
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "8"
4 | before_script:
5 | - npm install
6 | script: npm run test
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | index.html
3 | .idea
4 | dist
5 | npm-debug.log
6 | coverage
7 | .tmp
8 | .nyc_output
9 | yarn-error.log
10 | yarn.lock
11 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .gitignore
2 | .babelrc
3 | webpack.config.js
4 | src
5 | .idea
6 | npm-debug.log
7 | tests
8 | jest
9 | coverage
10 | .tmp
11 | .nyc_output
12 | yarn-error.log
13 | yarn.lock
14 |
--------------------------------------------------------------------------------
/tests/resources.ts:
--------------------------------------------------------------------------------
1 | import { Component, Emit, Inject, Model, Prop, Provide, Vue, Watch } from 'vue-property-decorator'
2 |
3 | @Component({
4 | template: `addon`
5 | })
6 | export class StandardSlot extends Vue {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import InputBox from "./input-box.ts";
2 | import SelectList from "./select-list.ts";
3 | import TextArea from "./text-area.ts";
4 | import ToggleSwitch from "./toggle-switch.ts";
5 | import FileBrowser from './file-browser.ts';
6 |
7 | exports.InputBox = InputBox;
8 | exports.SelectList = SelectList;
9 | exports.InputBlock = TextArea;
10 | exports.ToggleSwitch = ToggleSwitch;
11 | exports.FileBrowser = FileBrowser;
12 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "module": "es2015",
5 | "target": "es2015",
6 | "moduleResolution": "node",
7 | "removeComments": true,
8 | "isolatedModules": false,
9 | "experimentalDecorators": true,
10 | "emitDecoratorMetadata": true,
11 | "declaration": false,
12 | "noImplicitAny": false,
13 | "noImplicitUseStrict": false,
14 | "noLib": false,
15 | "preserveConstEnums": true,
16 | "suppressImplicitAnyIndexErrors": true,
17 | "pretty": true,
18 | "outDir": "build",
19 | "allowSyntheticDefaultImports": true,
20 | "lib": [
21 | "dom",
22 | "es2015"
23 | ],
24 | },
25 | "types": [
26 | "node"
27 | ],
28 | "exclude": [
29 | "node_modules/*",
30 | "tests"
31 | ],
32 | "files": [
33 | "src/index.ts"
34 | ]
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/templates/file-browser.html:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/templates/text-area.html:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/src/templates/toggle-switch.html:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/webpack.test.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | module: {
3 | rules: [
4 | {
5 | test: /\.tsx?$/,
6 | loader: 'ts-loader',
7 | exclude: /node_modules/,
8 | },
9 | {
10 | test: /\.jsx?$/,
11 | loader: 'babel-loader',
12 | exclude: /node_modules/,
13 | options: {
14 | cacheDirectory: true,
15 | presets: [
16 | ['env', {
17 | 'modules': false,
18 | 'targets': {
19 | 'browsers': ['> 2%'],
20 | uglify: true
21 | }
22 | }]
23 | ],
24 | plugins: ['transform-object-rest-spread']
25 | }
26 | },
27 | {
28 | test: /\.html$/,
29 | loader: 'html-loader',
30 | exclude: /node_modules/,
31 | },
32 | {
33 | test: /\.vue$/,
34 | use: 'vue-loader'
35 | }
36 | ]
37 | },
38 |
39 | resolve: {
40 | alias: {
41 | vue: 'vue/dist/vue.esm.js'
42 | }
43 | },
44 | node: {
45 | fs: "empty"
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/src/file-browser.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Component from 'vue-class-component';
3 |
4 | @Component({
5 | template: require('./templates/file-browser.html'),
6 | props: {
7 | label: {
8 | type: String,
9 | required: false
10 | },
11 | name: {
12 | type: String,
13 | required: true
14 | },
15 | helper: {
16 | type: String,
17 | required: false
18 | },
19 | required: {
20 | type: Boolean,
21 | default: false,
22 | required: false
23 | },
24 | inline: {
25 | type: Boolean,
26 | default: false,
27 | required: false
28 | },
29 | invalid: {
30 | type: Boolean,
31 | default: false,
32 | required: false
33 | },
34 | errorMessage: {
35 | type: String,
36 | required: false,
37 | default: null
38 | },
39 | metaUnderLabel: {
40 | type: Boolean,
41 | default: false,
42 | required: false
43 | }
44 | }
45 | })
46 | export default class FileBrowser extends Vue {
47 | /**
48 | * Emit an input event up to the parent
49 | * @param {[type]} event
50 | */
51 | public fileSelected(event: any): void {
52 | let fileList = event.target.files;
53 | this.$emit('file-selected', fileList[0]);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/toggle-switch.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Component from 'vue-class-component';
3 |
4 | @Component({
5 | template: require('./templates/toggle-switch.html'),
6 | model: {
7 | prop: 'checked',
8 | event: 'change'
9 | },
10 | props: {
11 | label: {
12 | type: String,
13 | required: false
14 | },
15 | labels: {
16 | type: Boolean,
17 | default: false
18 | },
19 | name: {
20 | type: String,
21 | required: true
22 | },
23 | helper: {
24 | type: String,
25 | required: false
26 | },
27 | required: {
28 | type: Boolean,
29 | default: false,
30 | required: false
31 | },
32 | inline: {
33 | type: Boolean,
34 | default: false,
35 | required: false
36 | },
37 | invalid: {
38 | type: Boolean,
39 | default: false,
40 | required: false
41 | },
42 | errorMessage: {
43 | type: String,
44 | required: false,
45 | default: null
46 | },
47 | metaUnderLabel: {
48 | type: Boolean,
49 | default: false,
50 | required: false
51 | },
52 | checked: {
53 | type: Boolean,
54 | default: false
55 | }
56 | }
57 | })
58 | export default class ToggleSwitch extends Vue {
59 | /**
60 | * Emit a change event up to the parent
61 | * @param {[type]} value
62 | */
63 | public updateValue(value): void {
64 | this.$emit('change', value);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/templates/input-box.html:
--------------------------------------------------------------------------------
1 |
25 |
--------------------------------------------------------------------------------
/src/templates/select-list.html:
--------------------------------------------------------------------------------
1 |
28 |
--------------------------------------------------------------------------------
/src/vue-form-components.scss:
--------------------------------------------------------------------------------
1 | $slider-background: #ccc !default;
2 | $slider-primary: #428bca !default;
3 |
4 | /**
5 | * SWITCH COMPONENT STYLING
6 | */
7 | .switch-component {
8 | position: relative;
9 | display: inline-block;
10 | width: 60px;
11 | height: 34px;
12 | }
13 | /* Hide default HTML checkbox */
14 | .switch-component input {display:none;}
15 | /* The slider */
16 | .slider {
17 | position: absolute;
18 | cursor: pointer;
19 | top: 0;
20 | left: 0;
21 | right: 0;
22 | bottom: 0;
23 | background-color: $slider-background;
24 | -webkit-transition: .4s;
25 | transition: .4s;
26 | }
27 | .slider:before {
28 | position: absolute;
29 | content: "";
30 | height: 26px;
31 | width: 26px;
32 | left: 4px;
33 | bottom: 4px;
34 | background-color: white;
35 | -webkit-transition: .4s;
36 | transition: .4s;
37 | }
38 | input:checked + .slider {
39 | background-color: $slider-primary;
40 | }
41 | input:focus + .slider {
42 | box-shadow: 0 0 1px $slider-primary;
43 | }
44 | input:checked + .slider:before {
45 | -webkit-transform: translateX(26px);
46 | -ms-transform: translateX(26px);
47 | transform: translateX(26px);
48 | }
49 | /* Rounded sliders */
50 | .slider.round {
51 | border-radius: 34px;
52 | display:flex;
53 | align-items: center;
54 | font-size: 11px;
55 | justify-content: space-between;
56 | }
57 | .slider.round:before {
58 | border-radius: 50%;
59 | }
60 | .slider.round .yes-label {
61 | padding-left: 5px;
62 | color: #FFF;
63 | text-transform: uppercase;
64 | }
65 | .slider.round .no-label {
66 | padding-right: 7px;
67 | text-transform: uppercase;
68 | }
69 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | let webpack = require('webpack');
2 | let path = require('path');
3 | let UglifyJSPlugin = require('uglifyjs-webpack-plugin');
4 | let nodeExternals = require('webpack-node-externals');
5 | let ExtractTextPlugin = require('extract-text-webpack-plugin');
6 |
7 | module.exports = {
8 | entry: ['./src/index.ts', './src/vue-form-components.scss'],
9 |
10 | output: {
11 | path: path.resolve(__dirname, 'dist/'),
12 | filename: 'build.js',
13 | publicPath: './dist'
14 | },
15 |
16 | module: {
17 | rules: [
18 | {
19 | test: /\.tsx?$/,
20 | loader: 'ts-loader',
21 | exclude: /node_modules/,
22 | },
23 | {
24 | test: /\.jsx?$/,
25 | loader: 'babel-loader',
26 | exclude: /node_modules/,
27 | options: {
28 | cacheDirectory: true,
29 | presets: [
30 | ['env', {
31 | 'modules': false,
32 | 'targets': {
33 | 'browsers': ['> 2%'],
34 | uglify: true
35 | }
36 | }]
37 | ],
38 | plugins: ['transform-decorators-legacy', 'transform-object-rest-spread']
39 | }
40 | },
41 | {
42 | test: /\.html$/,
43 | loader: 'html-loader',
44 | exclude: /node_modules/,
45 | },
46 | {
47 | test: /\.vue$/,
48 | use: 'vue-loader'
49 | },
50 | {
51 | test: /\.(sass|scss)$/,
52 | loader: ExtractTextPlugin.extract(['css-loader', 'sass-loader'])
53 | }
54 | ]
55 | },
56 |
57 | resolve: {
58 | alias: {
59 | vue: 'vue/dist/vue.js'
60 | }
61 | },
62 |
63 | externals: [nodeExternals()],
64 |
65 | plugins: [
66 | new ExtractTextPlugin({ // define where to save the file
67 | filename: '[name].css',
68 | allChunks: true,
69 | }),
70 | ]
71 | };
72 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-form-components",
3 | "version": "2.2.0",
4 | "description": "A collection of bootstrap form components for Vue 2",
5 | "main": "dist/build.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/georgehanson/Vue-Form-Components.git"
9 | },
10 | "scripts": {
11 | "build": "./node_modules/.bin/webpack",
12 | "test": "mocha-webpack --webpack-config=\"webpack.test.config.js\" --require=\"tests/setup.ts\" tests/**/*.spec.ts",
13 | "test:watch": "mocha-webpack --webpack-config=\"webpack.test.config.js\" --watch --require=\"tests/setup.ts\" tests/**/*.spec.ts"
14 | },
15 | "keywords": [
16 | "vue2",
17 | "vue",
18 | "form",
19 | "components",
20 | "bootstrap"
21 | ],
22 | "author": "George Hanson",
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/georgehanson/Vue-Form-Components/issues"
26 | },
27 | "homepage": "https://github.com/georgehanson/Vue-Form-Components#readme",
28 | "devDependencies": {
29 | "@types/node": "^8.0.45",
30 | "avoriaz": "^6.0.1",
31 | "babel-core": "^6.26.0",
32 | "babel-loader": "^7.1.2",
33 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
34 | "babel-preset-env": "^1.6.1",
35 | "babel-preset-es2015": "^6.24.1",
36 | "css-loader": "^0.28.7",
37 | "expect": "^21.2.1",
38 | "extract-text-webpack-plugin": "3.0.1",
39 | "html-loader": "^0.5.1",
40 | "jsdom": "^11.3.0",
41 | "jsdom-global": "^3.0.2",
42 | "mocha": "^4.0.1",
43 | "mocha-typescript": "1.1.11",
44 | "mocha-webpack": "^0.7.0",
45 | "node-sass": "^4.9.2",
46 | "sass-loader": "^6.0.6",
47 | "sinon": "^4.0.1",
48 | "ts-loader": "^3.0.3",
49 | "typescript": "2.5.3",
50 | "uglifyjs-webpack-plugin": "^1.0.0-beta.3",
51 | "vue": "^2.5.17",
52 | "vue-loader": "^13.3.0",
53 | "vue-property-decorator": "^6.0.0",
54 | "vue-template-compiler": "^2.5.17",
55 | "vue-test-utils": "^1.0.0-beta.2",
56 | "webpack": "^3.12.0",
57 | "webpack-node-externals": "^1.6.0",
58 | "webpack-notifier": "^1.5.0"
59 | },
60 | "dependencies": {
61 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
62 | "vue-class-component": "^6.0.0"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/text-area.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Component from 'vue-class-component';
3 |
4 | @Component({
5 | template: require('./templates/text-area.html'),
6 | props: {
7 | label: {
8 | type: String,
9 | required: false
10 | },
11 | placeholder: {
12 | type: String,
13 | required: false
14 | },
15 | name: {
16 | type: String,
17 | required: true
18 | },
19 | helper: {
20 | type: String,
21 | required: false
22 | },
23 | required: {
24 | type: Boolean,
25 | default: false,
26 | required: false
27 | },
28 | readonly: {
29 | type: Boolean,
30 | default: false,
31 | required: false
32 | },
33 | small: {
34 | type: Boolean,
35 | default: false,
36 | required: false
37 | },
38 | large: {
39 | type: Boolean,
40 | default: false,
41 | required: false
42 | },
43 | inline: {
44 | type: Boolean,
45 | default: false,
46 | required: false
47 | },
48 | invalid: {
49 | type: Boolean,
50 | default: false,
51 | required: false
52 | },
53 | errorMessage: {
54 | type: String,
55 | required: false,
56 | default: null
57 | },
58 | metaUnderLabel: {
59 | type: Boolean,
60 | default: false,
61 | required: false
62 | },
63 | value: {
64 | type: String,
65 | default: null,
66 | required: false
67 | }
68 | }
69 | })
70 | export default class TextArea extends Vue {
71 | /**
72 | * The classes for the input field
73 | * @return {string[]}
74 | */
75 | get inputClasses(): any[] {
76 | let initialArray = ['form-control'];
77 |
78 | if (this.$props.large) {
79 | initialArray.push('form-control-lg');
80 | }
81 |
82 | if (this.$props.small) {
83 | initialArray.push('form-control-sm');
84 | }
85 |
86 | if (this.$props.plainText) {
87 | initialArray[0] += '-plaintext';
88 | }
89 |
90 | if (this.$props.invalid) {
91 | initialArray.push('is-invalid');
92 | }
93 |
94 | return initialArray;
95 | }
96 |
97 | /**
98 | * Emit an event that the enter key has been
99 | * pressed by the user
100 | */
101 | public enterKeyPressed(): void {
102 | this.$emit('enter');
103 | }
104 |
105 | /**
106 | * Emit an input event up to the parent
107 | * @param {[type]} value
108 | */
109 | public updateValue(value): void {
110 | this.$emit('input', value);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/select-list.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Component from 'vue-class-component';
3 |
4 | @Component({
5 | template: require('./templates/select-list.html'),
6 | props: {
7 | label: {
8 | type: String,
9 | required: false
10 | },
11 | placeholder: {
12 | type: String,
13 | required: false
14 | },
15 | name: {
16 | type: String,
17 | required: true
18 | },
19 | type: {
20 | type: String,
21 | default: 'text',
22 | required: false
23 | },
24 | helper: {
25 | type: String,
26 | required: false
27 | },
28 | required: {
29 | type: Boolean,
30 | default: false,
31 | required: false
32 | },
33 | disabled: {
34 | type: Boolean,
35 | default: false,
36 | required: false
37 | },
38 | small: {
39 | type: Boolean,
40 | default: false,
41 | required: false
42 | },
43 | large: {
44 | type: Boolean,
45 | default: false,
46 | required: false
47 | },
48 | inline: {
49 | type: Boolean,
50 | default: false,
51 | required: false
52 | },
53 | invalid: {
54 | type: Boolean,
55 | default: false,
56 | required: false
57 | },
58 | errorMessage: {
59 | type: String,
60 | required: false,
61 | default: null
62 | },
63 | metaUnderLabel: {
64 | type: Boolean,
65 | default: false,
66 | required: false
67 | },
68 | value: {
69 | type: [String, Number],
70 | required: false
71 | },
72 | options: {
73 | type: Array,
74 | default: () => { return []; },
75 | required: false
76 | },
77 | keyName: {
78 | type: String,
79 | default: 'id'
80 | },
81 | labelName: {
82 | type: String,
83 | default: 'label'
84 | }
85 | }
86 | })
87 | export default class SelectList extends Vue {
88 | /**
89 | * The classes for the input field
90 | * @return {string[]}
91 | */
92 | get inputClasses(): any[] {
93 | let initialArray = ['form-control'];
94 |
95 | if (this.$props.large && ! this.usingAddons) {
96 | initialArray.push('form-control-lg');
97 | }
98 |
99 | if (this.$props.small && ! this.usingAddons) {
100 | initialArray.push('form-control-sm');
101 | }
102 |
103 | if (this.$props.plainText) {
104 | initialArray[0] += '-plaintext';
105 | }
106 |
107 | if (this.$props.invalid) {
108 | initialArray.push('is-invalid');
109 | }
110 |
111 | return initialArray;
112 | }
113 |
114 | /**
115 | * Check if any add-ons are being used
116 | *
117 | * @return {boolean}
118 | */
119 | get usingAddons(): boolean {
120 | return ! (Object.keys(this.$slots).length === 0 && this.$slots.constructor === Object)
121 | }
122 |
123 | /**
124 | * Check to see if a slot exists
125 | * @param {string} name [description]
126 | * @return {boolean} [description]
127 | */
128 | public slotExists(name: string): boolean {
129 | return (name in this.$slots);
130 | }
131 |
132 | /**
133 | * Emit an input event up to the parent
134 | * @param {[type]} value
135 | */
136 | public updateValue(value): void {
137 | this.$emit('input', value);
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/input-box.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Component from 'vue-class-component';
3 |
4 | @Component({
5 | template: require('./templates/input-box.html'),
6 | props: {
7 | name: {
8 | type: String,
9 | required: true
10 | },
11 | label: {
12 | type: String,
13 | required: false
14 | },
15 | placeholder: {
16 | type: String,
17 | required: false
18 | },
19 | type: {
20 | type: String,
21 | default: 'text',
22 | required: false
23 | },
24 | helper: {
25 | type: String,
26 | required: false
27 | },
28 | required: {
29 | type: Boolean,
30 | default: false,
31 | required: false
32 | },
33 | readonly: {
34 | type: Boolean,
35 | default: false,
36 | required: false
37 | },
38 | small: {
39 | type: Boolean,
40 | default: false,
41 | required: false
42 | },
43 | large: {
44 | type: Boolean,
45 | default: false,
46 | required: false
47 | },
48 | plainText: {
49 | type: Boolean,
50 | default: false,
51 | required: false
52 | },
53 | inline: {
54 | type: Boolean,
55 | default: false,
56 | required: false
57 | },
58 | invalid: {
59 | type: Boolean,
60 | default: false,
61 | required: false
62 | },
63 | errorMessage: {
64 | type: String,
65 | required: false,
66 | default: null
67 | },
68 | metaUnderLabel: {
69 | type: Boolean,
70 | default: false,
71 | required: false
72 | },
73 | value: {
74 | type: String,
75 | default: null,
76 | required: false
77 | },
78 | maxLength: {
79 | type: Number,
80 | default() {
81 | return 524288;
82 | },
83 | required: false
84 | },
85 | autoComplete: {
86 | type: Boolean,
87 | default: true,
88 | required: false
89 | }
90 | }
91 | })
92 | export default class TextArea extends Vue {
93 | /**
94 | * The classes for the input field
95 | * @return {string[]}
96 | */
97 | get inputClasses(): any[] {
98 | let initialArray = ['form-control'];
99 |
100 | if (this.$props.large && ! this.usingAddons) {
101 | initialArray.push('form-control-lg');
102 | }
103 |
104 | if (this.$props.small && ! this.usingAddons) {
105 | initialArray.push('form-control-sm');
106 | }
107 |
108 | if (this.$props.plainText) {
109 | initialArray[0] += '-plaintext';
110 | }
111 |
112 | if (this.$props.invalid) {
113 | initialArray.push('is-invalid');
114 | }
115 |
116 | return initialArray;
117 | }
118 |
119 | /**
120 | * Check if any add-ons are being used
121 | *
122 | * @return {boolean}
123 | */
124 | get usingAddons(): boolean {
125 | return ! (Object.keys(this.$slots).length === 0 && this.$slots.constructor === Object)
126 | }
127 |
128 | /**
129 | * Check to see if a slot exists
130 | * @param {string} name [description]
131 | * @return {boolean} [description]
132 | */
133 | public slotExists(name: string): boolean {
134 | return (name in this.$slots);
135 | }
136 |
137 | /**
138 | * Emit an event that the enter key has been
139 | * pressed by the user
140 | */
141 | public enterKeyPressed(): void {
142 | this.$emit('enter');
143 | }
144 |
145 | /**
146 | * Emit an input event up to the parent
147 | * @param {[type]} value
148 | */
149 | public updateValue(value): void {
150 | this.$emit('input', value);
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/tests/file-browser.spec.ts:
--------------------------------------------------------------------------------
1 | import { suite, test, slow, timeout } from "mocha-typescript";
2 | import {mount} from "avoriaz";
3 | import FileBrowser from "./../src/file-browser.ts";
4 | import expect from 'expect';
5 | import {StandardSlot} from "./resources.ts";
6 | import sinon from 'sinon';
7 |
8 | suite("FileBrowser", () => {
9 | test("it can set the name", () => {
10 | let wrapper: any = mount(FileBrowser, {
11 | propsData: {
12 | name: 'active'
13 | }
14 | });
15 |
16 | expect(wrapper.first('input').hasAttribute('name')).toBe(true);
17 | expect(wrapper.first('input').getAttribute('name')).toBe('active');
18 | });
19 |
20 | test("the label gets output", () => {
21 | let wrapper: any = mount(FileBrowser, {
22 | propsData: {
23 | name: 'active',
24 | label: 'My Input'
25 | }
26 | });
27 |
28 | expect(wrapper.first('label').text()).toBe('My Input');
29 | });
30 |
31 | test("if no label is specified it does not show it", () => {
32 | let wrapper: any = mount(FileBrowser, {
33 | propsData: {
34 | name: 'active'
35 | }
36 | });
37 | expect(wrapper.find('.col-form-label').length).toBe(0);
38 | });
39 |
40 | test("if no helper is provided it does not show one", () => {
41 | let wrapper: any = mount(FileBrowser, {
42 | propsData: {
43 | name: 'active',
44 | }
45 | });
46 | expect(wrapper.find('small.form-text').length).toBe(0);
47 | expect(wrapper.first('.form-group').hasClass('has-helper')).toBe(false);
48 | });
49 |
50 | test("if helper text is provided it does show it", () => {
51 | let wrapper: any = mount(FileBrowser, {
52 | propsData: {
53 | name: 'active',
54 | helper: 'Please select a active'
55 | }
56 | });
57 |
58 | expect(wrapper.find('small.form-text').length).toBe(1);
59 | expect(wrapper.first('small.form-text').text()).toBe("Please select a active");
60 | expect(wrapper.first('.form-group').hasClass('has-helper')).toBe(true);
61 | });
62 |
63 | test("if the field is not required it does not have the required attributes", () => {
64 | let wrapper: any = mount(FileBrowser, {
65 | propsData: {
66 | name: 'active',
67 | }
68 | });
69 |
70 | expect(wrapper.find('span.required').length).toBe(0);
71 | });
72 |
73 | test("if the field is required it have the required attributes", () => {
74 | let wrapper: any = mount(FileBrowser, {
75 | propsData: {
76 | name: 'active',
77 | label: 'Your Email',
78 | required: true
79 | }
80 | });
81 |
82 | expect(wrapper.find('span.required').length).toBe(1);
83 | expect(wrapper.first('span.required').text()).toBe('*');
84 | });
85 |
86 | test("the input can be inline", () => {
87 | let wrapper: any = mount(FileBrowser, {
88 | propsData: {
89 | name: 'active',
90 | label: 'My Label',
91 | inline: true
92 | }
93 | });
94 |
95 | expect(wrapper.find('.col-sm-4').length).toBe(1);
96 | expect(wrapper.first('.form-group').hasClass('row')).toBe(true);
97 | expect(wrapper.first('.col-sm-4').contains('label')).toBe(true);
98 | expect(wrapper.first('label').hasClass('col-form-label')).toBe(true);
99 | expect(wrapper.find('.col-sm-8').length).toBe(1);
100 | });
101 |
102 | test("if the input is not inline, it does not have the row class", () => {
103 | let wrapper: any = mount(FileBrowser, {
104 | propsData: {
105 | name: 'active',
106 | label: 'My Label',
107 | }
108 | });
109 |
110 | expect(wrapper.first('.form-group').hasClass('row')).toBe(false);
111 | });
112 |
113 | test("the field can be marked as invalid", () => {
114 | let wrapper: any = mount(FileBrowser, {
115 | propsData: {
116 | name: 'active',
117 | label: 'My Label',
118 | invalid: true,
119 | errorMessage: "There was an error"
120 | }
121 | });
122 |
123 | expect(wrapper.first('.form-group').contains('.invalid-feedback')).toBe(true);
124 | expect(wrapper.first('.invalid-feedback').text()).toBe("There was an error");
125 | });
126 |
127 | test("the field does not show invalid feedback if it has not been marked as invalid", () => {
128 | let wrapper: any = mount(FileBrowser, {
129 | propsData: {
130 | name: 'active',
131 | label: 'My Label',
132 | errorMessage: "There was an error"
133 | }
134 | });
135 |
136 | expect(wrapper.first('.form-group').contains('.invalid-feedback')).toBe(false);
137 | });
138 |
139 | test("the field does not show invalid feedback if no active has been passed", () => {
140 | let wrapper: any = mount(FileBrowser, {
141 | propsData: {
142 | name: 'active',
143 | label: 'My Label',
144 | invalid: true
145 | }
146 | });
147 |
148 | expect(wrapper.first('.invalid-feedback').contains('.invalid-feedback')).toBe(false);
149 | });
150 |
151 | test("the helper text can be placed under the label", () => {
152 | let wrapper: any = mount(FileBrowser, {
153 | propsData: {
154 | name: 'active',
155 | label: 'My Label',
156 | helper: 'This is helper text',
157 | inline: true,
158 | metaUnderLabel: true
159 | }
160 | });
161 |
162 | expect(wrapper.first('.col-sm-4').contains('.form-text')).toBe(true);
163 | expect(wrapper.first('.col-sm-8').contains('.form-text')).toBe(false);
164 | });
165 |
166 | test("the error text can be placed under the label", () => {
167 | let wrapper: any = mount(FileBrowser, {
168 | propsData: {
169 | name: 'active',
170 | label: 'My Label',
171 | helper: 'This is helper text',
172 | inline: true,
173 | metaUnderLabel: true,
174 | invalid: true,
175 | erroractive: 'There was an error'
176 | }
177 | });
178 |
179 | expect(wrapper.first('.col-sm-4').contains('.invalid-feedback')).toBe(true);
180 | expect(wrapper.first('.col-sm-8').contains('.invalid-feedback')).toBe(false);
181 | });
182 |
183 | test("it emits an event on change", () => {
184 | let wrapper: any = mount(FileBrowser, {
185 | propsData: {
186 | name: 'active',
187 | label: 'My Label'
188 | }
189 | });
190 | const spy = sinon.spy(wrapper.vm, '$emit');
191 | wrapper.first('input').trigger("change");
192 | expect(spy.args[0][0]).toBe('file-selected')
193 | });
194 | });
195 |
--------------------------------------------------------------------------------
/tests/toggle-switch.spec.ts:
--------------------------------------------------------------------------------
1 | import { suite, test, slow, timeout } from "mocha-typescript";
2 | import {mount} from "avoriaz";
3 | import ToggleSwitch from "./../src/toggle-switch.ts";
4 | import expect from 'expect';
5 | import {StandardSlot} from "./resources.ts";
6 | import sinon from 'sinon';
7 |
8 | suite("ToggleSwitch", () => {
9 | test("it can set the name", () => {
10 | let wrapper: any = mount(ToggleSwitch, {
11 | propsData: {
12 | name: 'active'
13 | }
14 | });
15 |
16 | expect(wrapper.first('input').hasAttribute('name')).toBe(true);
17 | expect(wrapper.first('input').getAttribute('name')).toBe('active');
18 | });
19 |
20 | test("the label gets output", () => {
21 | let wrapper: any = mount(ToggleSwitch, {
22 | propsData: {
23 | name: 'active',
24 | label: 'My Input'
25 | }
26 | });
27 |
28 | expect(wrapper.first('label').text()).toBe('My Input');
29 | });
30 |
31 | test("if no label is specified it does not show it", () => {
32 | let wrapper: any = mount(ToggleSwitch, {
33 | propsData: {
34 | name: 'active'
35 | }
36 | });
37 | expect(wrapper.find('.col-form-label').length).toBe(0);
38 | });
39 |
40 | test("if no helper is provided it does not show one", () => {
41 | let wrapper: any = mount(ToggleSwitch, {
42 | propsData: {
43 | name: 'active',
44 | }
45 | });
46 | expect(wrapper.find('small.form-text').length).toBe(0);
47 | expect(wrapper.first('.form-group').hasClass('has-helper')).toBe(false);
48 | });
49 |
50 | test("if helper text is provided it does show it", () => {
51 | let wrapper: any = mount(ToggleSwitch, {
52 | propsData: {
53 | name: 'active',
54 | helper: 'Please select a active'
55 | }
56 | });
57 |
58 | expect(wrapper.find('small.form-text').length).toBe(1);
59 | expect(wrapper.first('small.form-text').text()).toBe("Please select a active");
60 | expect(wrapper.first('.form-group').hasClass('has-helper')).toBe(true);
61 | });
62 |
63 | test("if the field is not required it does not have the required attributes", () => {
64 | let wrapper: any = mount(ToggleSwitch, {
65 | propsData: {
66 | name: 'active',
67 | }
68 | });
69 |
70 | expect(wrapper.find('span.required').length).toBe(0);
71 | });
72 |
73 | test("if the field is required it have the required attributes", () => {
74 | let wrapper: any = mount(ToggleSwitch, {
75 | propsData: {
76 | name: 'active',
77 | label: 'Your Email',
78 | required: true
79 | }
80 | });
81 |
82 | expect(wrapper.find('span.required').length).toBe(1);
83 | expect(wrapper.first('span.required').text()).toBe('*');
84 | });
85 |
86 | test("the input can be inline", () => {
87 | let wrapper: any = mount(ToggleSwitch, {
88 | propsData: {
89 | name: 'active',
90 | label: 'My Label',
91 | inline: true
92 | }
93 | });
94 |
95 | expect(wrapper.find('.col-sm-4').length).toBe(1);
96 | expect(wrapper.first('.form-group').hasClass('row')).toBe(true);
97 | expect(wrapper.first('.col-sm-4').contains('label')).toBe(true);
98 | expect(wrapper.first('label').hasClass('col-form-label')).toBe(true);
99 | expect(wrapper.find('.col-sm-8').length).toBe(1);
100 | });
101 |
102 | test("if the input is not inline, it does not have the row class", () => {
103 | let wrapper: any = mount(ToggleSwitch, {
104 | propsData: {
105 | name: 'active',
106 | label: 'My Label',
107 | }
108 | });
109 |
110 | expect(wrapper.first('.form-group').hasClass('row')).toBe(false);
111 | });
112 |
113 | test("the field can be marked as invalid", () => {
114 | let wrapper: any = mount(ToggleSwitch, {
115 | propsData: {
116 | name: 'active',
117 | label: 'My Label',
118 | invalid: true,
119 | errorMessage: "There was an error"
120 | }
121 | });
122 |
123 | expect(wrapper.first('.form-group').contains('.invalid-feedback')).toBe(true);
124 | expect(wrapper.first('.invalid-feedback').text()).toBe("There was an error");
125 | });
126 |
127 | test("the field does not show invalid feedback if it has not been marked as invalid", () => {
128 | let wrapper: any = mount(ToggleSwitch, {
129 | propsData: {
130 | name: 'active',
131 | label: 'My Label',
132 | errorMessage: "There was an error"
133 | }
134 | });
135 |
136 | expect(wrapper.first('.form-group').contains('.invalid-feedback')).toBe(false);
137 | });
138 |
139 | test("the field does not show invalid feedback if no active has been passed", () => {
140 | let wrapper: any = mount(ToggleSwitch, {
141 | propsData: {
142 | name: 'active',
143 | label: 'My Label',
144 | invalid: true
145 | }
146 | });
147 |
148 | expect(wrapper.first('.invalid-feedback').contains('.invalid-feedback')).toBe(false);
149 | });
150 |
151 | test("the helper text can be placed under the label", () => {
152 | let wrapper: any = mount(ToggleSwitch, {
153 | propsData: {
154 | name: 'active',
155 | label: 'My Label',
156 | helper: 'This is helper text',
157 | inline: true,
158 | metaUnderLabel: true
159 | }
160 | });
161 |
162 | expect(wrapper.first('.col-sm-4').contains('.form-text')).toBe(true);
163 | expect(wrapper.first('.col-sm-8').contains('.form-text')).toBe(false);
164 | });
165 |
166 | test("the error text can be placed under the label", () => {
167 | let wrapper: any = mount(ToggleSwitch, {
168 | propsData: {
169 | name: 'active',
170 | label: 'My Label',
171 | helper: 'This is helper text',
172 | inline: true,
173 | metaUnderLabel: true,
174 | invalid: true,
175 | erroractive: 'There was an error'
176 | }
177 | });
178 |
179 | expect(wrapper.first('.col-sm-4').contains('.invalid-feedback')).toBe(true);
180 | expect(wrapper.first('.col-sm-8').contains('.invalid-feedback')).toBe(false);
181 | });
182 |
183 | test("it emits an event on change", () => {
184 | let wrapper: any = mount(ToggleSwitch, {
185 | propsData: {
186 | name: 'active',
187 | label: 'My Label'
188 | }
189 | });
190 | const spy = sinon.spy(wrapper.vm, '$emit');
191 | wrapper.first('input').trigger("change");
192 | expect(spy.args[0][0]).toBe('change')
193 | });
194 | });
195 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vue Form Components
2 |
3 | [](https://travis-ci.org/georgehanson/Vue-Form-Components)
4 | [](https://badge.fury.io/js/vue-form-components)
5 | [](https://www.npmjs.com/package/vue-form-components)
6 |
7 | This package provides easy form wrappers for Vue2, based upon the [Bootstrap v4](https://getbootstrap.com/) CSS Framework.
8 |
9 | There are a variety of components that this package provides. These are:
10 |
11 | - Standard Input Box
12 | - Text Area
13 | - Select List
14 | - Toggle Switch
15 | - File Browser
16 |
17 | ## Installation
18 |
19 | You can install this package by running the following command: `npm install --save vue-form-components` or `yarn add vue-form-components`
20 |
21 | You can then import this package into your project.
22 |
23 | `Import VueFormComponents from 'vue-form-components'`
24 |
25 | ## Usage
26 |
27 | I recommend when using this package to declare the components as global components. This can be done one of the following ways:
28 |
29 | ```javascript
30 | Import VueFormComponents from 'vue-form-components';
31 | Vue.component('input-box', VueFormComponents.InputBox);
32 | ```
33 |
34 | ### Standard Input Box
35 |
36 | This component is designed to save time and repetition by not having to duplicate form groups. The name for this component is `InputBox`. This component can be used in conjunction with the `v-model` directive. Below is an example of how you might use the component:
37 |
38 | ```html
39 |
40 | ```
41 |
42 | #### Addons
43 | With the Standard input box component, you can also use input group addons. This can be done by simply adding slots.
44 |
45 | | Slot | Description |
46 | | ------------ | ----------- |
47 | | leftAddon | A standard input group addon on the left side of the input field |
48 | | rightAddon | A standard input group addon on the right side of the input field |
49 | | leftButton | An input group button on the left side of the input field |
50 | | rightButton | An input group button on the right side of the input |
51 |
52 | For example, if I wanted to create an input box, with an addon on the right hand side, I could do the following:
53 |
54 | ```html
55 |
56 | @example.com
57 |
58 | ```
59 |
60 | Alternatively, for a button on the right hand side, I could do the following:
61 |
62 | ```html
63 |
64 |
65 |
66 | ```
67 |
68 | | Prop | Type | Default | Required | Description |
69 | | ------------ | ------- | ---------- | --------- | ----------- |
70 | | name | String | | Yes | The input name for the field |
71 | | label | String | | No | The label for the input |
72 | | helper | String | | No | Helper text |
73 | | invalid | Boolean | false | No | Whether or not to show a validation error |
74 | | errorMessage | String | | No | The error message to show |
75 | | placeholder | String | | No | A placeholder for the input |
76 | | inline | Boolean | false | No | Show the label next to the input |
77 | | type | String | text | No | The HTML input type |
78 | | required | Boolean | false | No | Mark the field as required |
79 | | readonly | Boolean | false | No | Mark the field as readonly |
80 | | small | Boolean | false | No | Show a small input |
81 | | large | Boolean | false | No | Show a large input |
82 | | plainText | Boolean | false | No | Show a plain text input |
83 | | metaUnderLabel | Boolean | false | No | Show the meta details under the label instead of the input |
84 |
85 | ### Toggle Switch
86 |
87 | This component displays a nice alternative to a standard checkbox. The name for this component is `ToggleSwitch`.
88 | This component can be used in conjunction with the `v-model` directive.
89 |
90 | | Prop | Type | Default | Required | Description |
91 | | ------------ | ------- | ---------- | --------- | ----------- |
92 | | name | String | | Yes | The checkbox name |
93 | | label | String | | No | The label for the checkbox |
94 | | labels | Boolean | false | No | Show yes / no labels on the switch |
95 | | helper | String | | No | Helper text |
96 | | id | String | | No | The id for the checkbox |
97 | | stacked | Boolean | false | No | Show the label above the switch component
98 | | labelColumn | String | col-sm-2 | No | The bootstrap column for the label |
99 | | inputColumn | String | col-sm-10 | No | The bootstrap column for the input |
100 |
101 | ### Text Area
102 |
103 | This component generates a textarea within a bootstrap form group. The name for this component is `TextArea`.
104 | This component can be used in conjunction with the `v-model` directive.
105 |
106 | | Prop | Type | Default | Required | Description |
107 | | ------------ | ------- | ---------- | --------- | ----------- |
108 | | name | String | | Yes | The textarea name |
109 | | label | String | | No | The label for the textarea |
110 | | helper | String | | No | Helper text |
111 | | showError | Boolean | false | No | Whether or not to show a validation error |
112 | | errorMessage | String | | No | The error message to show |
113 | | stacked | Boolean | false | No | Show the label above the textarea |
114 | | required | Boolean | false | No | Mark the field as required |
115 | | id | String | | No | The id for the html input |
116 |
117 | ### Select List
118 |
119 | This component generates a select input within a bootstrap form group. The name for this component is `SelectList`.
120 | This component can be used in conjunction with the `v-model` directive.
121 |
122 | #### Addons
123 | With the Select List component, you can also use input group addons. This can be done by simply adding slots.
124 |
125 | | Slot | Description |
126 | | ------------ | ----------- |
127 | | leftAddon | An addon on the left side of the input field |
128 | | rightAddon | An addon on the right side of the input field |
129 | | leftBtn | An input group button on the left side of the input field |
130 | | rightBtn | An input group button on the right side of the input |
131 |
132 | | Prop | Type | Default | Required | Description |
133 | | ------------ | ------- | ---------- | --------- | ----------- |
134 | | name | String | | Yes | The select list name |
135 | | label | String | | No | The label for the select list |
136 | | options | Array | | Yes | The options to choose from |
137 | | keyName | String | id | No | The name of the key for the value in the options |
138 | | labelName | String | label | No | The name of the key for the label in the options |
139 | | helper | String | | No | Helper text |
140 | | showError | Boolean | false | No | Whether or not to show a validation error |
141 | | errorMessage | String | | No | The error message to show |
142 | | stacked | Boolean | false | No | Show the label above the textarea |
143 | | required | Boolean | false | No | Mark the field as required |
144 |
145 |
146 | More docs coming soon
147 |
--------------------------------------------------------------------------------
/tests/text-area.spec.ts:
--------------------------------------------------------------------------------
1 | import { suite, test, slow, timeout } from "mocha-typescript";
2 | import {mount} from "avoriaz";
3 | import TextArea from "./../src/text-area.ts";
4 | import expect from 'expect';
5 | import {StandardSlot} from "./resources.ts";
6 | import sinon from 'sinon';
7 |
8 | suite("TextArea", () => {
9 | test("it can set the name", () => {
10 | let wrapper: any = mount(TextArea, {
11 | propsData: {
12 | name: 'message'
13 | }
14 | });
15 |
16 | expect(wrapper.first('textarea').hasAttribute('name')).toBe(true);
17 | expect(wrapper.first('textarea').getAttribute('name')).toBe('message');
18 | });
19 |
20 | test("the label gets output", () => {
21 | let wrapper: any = mount(TextArea, {
22 | propsData: {
23 | name: 'message',
24 | label: 'My Input'
25 | }
26 | });
27 |
28 | expect(wrapper.first('label').text()).toBe('My Input');
29 | });
30 |
31 | test("if no label is specified it does not show it", () => {
32 | let wrapper: any = mount(TextArea, {
33 | propsData: {
34 | name: 'message'
35 | }
36 | });
37 | expect(wrapper.find('label').length).toBe(0);
38 | });
39 |
40 | test("if no placeholder is specified it does not show one", () => {
41 | let wrapper: any = mount(TextArea, {
42 | propsData: {
43 | name: 'message'
44 | }
45 | });
46 | expect(wrapper.find('option').length).toBe(0);
47 | });
48 |
49 | test("if a placeholder is specified it shows one", () => {
50 | let wrapper: any = mount(TextArea, {
51 | propsData: {
52 | name: 'message',
53 | placeholder: 'My Placeholder'
54 | }
55 | });
56 |
57 | expect(wrapper.first('textarea').hasAttribute('placeholder')).toBe(true);
58 | expect(wrapper.first('textarea').getAttribute('placeholder')).toBe('My Placeholder');
59 | });
60 |
61 | test("if no helper is provided it does not show one", () => {
62 | let wrapper: any = mount(TextArea, {
63 | propsData: {
64 | name: 'message',
65 | }
66 | });
67 | expect(wrapper.find('small.form-text').length).toBe(0);
68 | expect(wrapper.first('.form-group').hasClass('has-helper')).toBe(false);
69 | });
70 |
71 | test("if helper text is provided it does show it", () => {
72 | let wrapper: any = mount(TextArea, {
73 | propsData: {
74 | name: 'message',
75 | helper: 'Please select a message'
76 | }
77 | });
78 |
79 | expect(wrapper.find('small.form-text').length).toBe(1);
80 | expect(wrapper.first('small.form-text').text()).toBe("Please select a message");
81 | expect(wrapper.first('.form-group').hasClass('has-helper')).toBe(true);
82 | });
83 |
84 | test("if the field is not required it does not have the required attributes", () => {
85 | let wrapper: any = mount(TextArea, {
86 | propsData: {
87 | name: 'message',
88 | }
89 | });
90 |
91 | expect(wrapper.find('span.required').length).toBe(0);
92 | expect(wrapper.first('textarea').hasAttribute('required')).toBe(false);
93 | });
94 |
95 | test("if the field is required it have the required attributes", () => {
96 | let wrapper: any = mount(TextArea, {
97 | propsData: {
98 | name: 'message',
99 | label: 'Your Email',
100 | required: true
101 | }
102 | });
103 |
104 | expect(wrapper.find('span.required').length).toBe(1);
105 | expect(wrapper.first('span.required').text()).toBe('*');
106 | expect(wrapper.first('textarea').hasAttribute('required')).toBe(true);
107 | });
108 |
109 | test("if the input is required but no label has been specified the input field has the required attribute", () => {
110 | let wrapper: any = mount(TextArea, {
111 | propsData: {
112 | name: 'message',
113 | required: true
114 | }
115 | });
116 |
117 | expect(wrapper.first('textarea').hasAttribute('required')).toBe(true);
118 | });
119 |
120 | test("the input can have the readonly attribute", () => {
121 | let wrapper: any = mount(TextArea, {
122 | propsData: {
123 | name: 'message',
124 | readonly: true,
125 | }
126 | });
127 |
128 | expect(wrapper.first('textarea').hasAttribute('readonly')).toBe(true);
129 | });
130 |
131 | test("by default the input does not have a readonly attribute", () => {
132 | let wrapper: any = mount(TextArea, {
133 | propsData: {
134 | name: 'message'
135 | }
136 | });
137 |
138 | expect(wrapper.first('textarea').hasAttribute('readonly')).toBe(false);
139 | });
140 |
141 | test("by default the class for the input is form-control", () => {
142 | let wrapper: any = mount(TextArea, {
143 | propsData: {
144 | name: 'message'
145 | }
146 | });
147 |
148 | expect(wrapper.first('textarea').hasClass('form-control')).toBe(true);
149 | });
150 |
151 | test("the form-control can be large", () => {
152 | let wrapper: any = mount(TextArea, {
153 | propsData: {
154 | name: 'message',
155 | large: true
156 | }
157 | });
158 |
159 | expect(wrapper.first('textarea').hasClass('form-control')).toBe(true);
160 | expect(wrapper.first('textarea').hasClass('form-control-lg')).toBe(true);
161 | });
162 |
163 | test("the form-control can be small", () => {
164 | let wrapper: any = mount(TextArea, {
165 | propsData: {
166 | name: 'message',
167 | small: true
168 | }
169 | });
170 |
171 | expect(wrapper.first('textarea').hasClass('form-control')).toBe(true);
172 | expect(wrapper.first('textarea').hasClass('form-control-sm')).toBe(true);
173 | });
174 |
175 | test("the input can be inline", () => {
176 | let wrapper: any = mount(TextArea, {
177 | propsData: {
178 | name: 'message',
179 | label: 'My Label',
180 | inline: true
181 | }
182 | });
183 |
184 | expect(wrapper.find('.col-sm-4').length).toBe(1);
185 | expect(wrapper.first('.form-group').hasClass('row')).toBe(true);
186 | expect(wrapper.first('.col-sm-4').contains('label')).toBe(true);
187 | expect(wrapper.first('label').hasClass('col-form-label')).toBe(true);
188 | expect(wrapper.find('.col-sm-8').length).toBe(1);
189 | });
190 |
191 | test("if the input is not inline, it does not have the row class", () => {
192 | let wrapper: any = mount(TextArea, {
193 | propsData: {
194 | name: 'message',
195 | label: 'My Label',
196 | }
197 | });
198 |
199 | expect(wrapper.first('.form-group').hasClass('row')).toBe(false);
200 | });
201 |
202 | test("the field can be marked as invalid", () => {
203 | let wrapper: any = mount(TextArea, {
204 | propsData: {
205 | name: 'message',
206 | label: 'My Label',
207 | invalid: true,
208 | errorMessage: "There was an error"
209 | }
210 | });
211 |
212 | expect(wrapper.first('.form-control').hasClass('is-invalid')).toBe(true);
213 | expect(wrapper.first('.form-group').contains('.invalid-feedback')).toBe(true);
214 | expect(wrapper.first('.invalid-feedback').text()).toBe("There was an error");
215 | });
216 |
217 | test("the field does not show invalid feedback if it has not been marked as invalid", () => {
218 | let wrapper: any = mount(TextArea, {
219 | propsData: {
220 | name: 'message',
221 | label: 'My Label',
222 | errorMessage: "There was an error"
223 | }
224 | });
225 |
226 | expect(wrapper.first('.form-control').hasClass('is-invalid')).toBe(false);
227 | expect(wrapper.first('.form-group').contains('.invalid-feedback')).toBe(false);
228 | });
229 |
230 | test("the field does not show invalid feedback if no message has been passed", () => {
231 | let wrapper: any = mount(TextArea, {
232 | propsData: {
233 | name: 'message',
234 | label: 'My Label',
235 | invalid: true
236 | }
237 | });
238 |
239 | expect(wrapper.first('.form-control').hasClass('is-invalid')).toBe(true);
240 | expect(wrapper.first('.invalid-feedback').contains('.invalid-feedback')).toBe(false);
241 | });
242 |
243 | test("the helper text can be placed under the label", () => {
244 | let wrapper: any = mount(TextArea, {
245 | propsData: {
246 | name: 'message',
247 | label: 'My Label',
248 | helper: 'This is helper text',
249 | inline: true,
250 | metaUnderLabel: true
251 | }
252 | });
253 |
254 | expect(wrapper.first('.col-sm-4').contains('.form-text')).toBe(true);
255 | expect(wrapper.first('.col-sm-8').contains('.form-text')).toBe(false);
256 | });
257 |
258 | test("the error text can be placed under the label", () => {
259 | let wrapper: any = mount(TextArea, {
260 | propsData: {
261 | name: 'message',
262 | label: 'My Label',
263 | helper: 'This is helper text',
264 | inline: true,
265 | metaUnderLabel: true,
266 | invalid: true,
267 | errorMessage: 'There was an error'
268 | }
269 | });
270 |
271 | expect(wrapper.first('.col-sm-4').contains('.invalid-feedback')).toBe(true);
272 | expect(wrapper.first('.col-sm-8').contains('.invalid-feedback')).toBe(false);
273 | });
274 |
275 | test("it emits an event on input", () => {
276 | let wrapper: any = mount(TextArea, {
277 | propsData: {
278 | name: 'message',
279 | label: 'My Label'
280 | }
281 | });
282 | const spy = sinon.spy(wrapper.vm, '$emit');
283 | wrapper.first('.form-control').trigger("input");
284 | expect(spy.args[0][0]).toBe('input')
285 | });
286 |
287 | test("it emits an event on enter key", () => {
288 | let wrapper: any = mount(TextArea, {
289 | propsData: {
290 | name: 'message',
291 | label: 'My Label'
292 | }
293 | });
294 | const spy = sinon.spy(wrapper.vm, '$emit');
295 | wrapper.first('.form-control').trigger("keyup.enter");
296 | expect(spy.args[0][0]).toBe('enter')
297 | });
298 |
299 | test("the value of the textarea can be set", () => {
300 | let wrapper: any = mount(TextArea, {
301 | propsData: {
302 | name: 'message',
303 | label: 'My Label',
304 | value: 'This is the content of the textarea'
305 | }
306 | });
307 |
308 | expect(wrapper.first('textarea').value()).toBe('This is the content of the textarea');
309 | });
310 | });
311 |
--------------------------------------------------------------------------------
/tests/select-list.spec.ts:
--------------------------------------------------------------------------------
1 | import { suite, test, slow, timeout } from "mocha-typescript";
2 | import {mount} from "avoriaz";
3 | import SelectList from "./../src/select-list.ts";
4 | import expect from 'expect';
5 | import {StandardSlot} from "./resources.ts";
6 | import sinon from 'sinon';
7 |
8 | suite("SelectList", () => {
9 | test("it can set the name", () => {
10 | let wrapper: any = mount(SelectList, {
11 | propsData: {
12 | name: 'title'
13 | }
14 | });
15 |
16 | expect(wrapper.first('select').hasAttribute('name')).toBe(true);
17 | expect(wrapper.first('select').getAttribute('name')).toBe('title');
18 | });
19 |
20 | test("the label gets output", () => {
21 | let wrapper: any = mount(SelectList, {
22 | propsData: {
23 | name: 'title',
24 | label: 'My Input'
25 | }
26 | });
27 |
28 | expect(wrapper.first('label').text()).toBe('My Input');
29 | });
30 |
31 | test("if no label is specified it does not show it", () => {
32 | let wrapper: any = mount(SelectList, {
33 | propsData: {
34 | name: 'title'
35 | }
36 | });
37 | expect(wrapper.find('label').length).toBe(0);
38 | });
39 |
40 | test("if no placeholder is specified it does not show one", () => {
41 | let wrapper: any = mount(SelectList, {
42 | propsData: {
43 | name: 'title'
44 | }
45 | });
46 | expect(wrapper.find('option').length).toBe(0);
47 | });
48 |
49 | test("if a placeholder is specified it shows one", () => {
50 | let wrapper: any = mount(SelectList, {
51 | propsData: {
52 | name: 'title',
53 | placeholder: 'My Placeholder',
54 | value: ''
55 | }
56 | });
57 | expect(wrapper.find('option').length).toBe(1);
58 | expect(wrapper.first('option').text()).toBe('My Placeholder');
59 | });
60 |
61 | test("if no helper is provided it does not show one", () => {
62 | let wrapper: any = mount(SelectList, {
63 | propsData: {
64 | name: 'title',
65 | }
66 | });
67 | expect(wrapper.find('small.form-text').length).toBe(0);
68 | expect(wrapper.first('.form-group').hasClass('has-helper')).toBe(false);
69 | });
70 |
71 | test("if helper text is provided it does show it", () => {
72 | let wrapper: any = mount(SelectList, {
73 | propsData: {
74 | name: 'title',
75 | helper: 'Please select a title'
76 | }
77 | });
78 |
79 | expect(wrapper.find('small.form-text').length).toBe(1);
80 | expect(wrapper.first('small.form-text').text()).toBe("Please select a title");
81 | expect(wrapper.first('.form-group').hasClass('has-helper')).toBe(true);
82 | });
83 |
84 | test("if the field is not required it does not have the required attributes", () => {
85 | let wrapper: any = mount(SelectList, {
86 | propsData: {
87 | name: 'title',
88 | }
89 | });
90 |
91 | expect(wrapper.find('span.required').length).toBe(0);
92 | expect(wrapper.first('select').hasAttribute('required')).toBe(false);
93 | });
94 |
95 | test("if the field is required it have the required attributes", () => {
96 | let wrapper: any = mount(SelectList, {
97 | propsData: {
98 | name: 'title',
99 | label: 'Your Email',
100 | required: true
101 | }
102 | });
103 |
104 | expect(wrapper.find('span.required').length).toBe(1);
105 | expect(wrapper.first('span.required').text()).toBe('*');
106 | expect(wrapper.first('select').hasAttribute('required')).toBe(true);
107 | });
108 |
109 | test("if the input is required but no label has been specified the input field has the required attribute", () => {
110 | let wrapper: any = mount(SelectList, {
111 | propsData: {
112 | name: 'title',
113 | required: true
114 | }
115 | });
116 |
117 | expect(wrapper.first('select').hasAttribute('required')).toBe(true);
118 | });
119 |
120 | test("the input can have the disabled attribute", () => {
121 | let wrapper: any = mount(SelectList, {
122 | propsData: {
123 | name: 'title',
124 | disabled: true,
125 | }
126 | });
127 |
128 | expect(wrapper.first('select').hasAttribute('disabled')).toBe(true);
129 | });
130 |
131 | test("by default the input does not have a disabled attribute", () => {
132 | let wrapper: any = mount(SelectList, {
133 | propsData: {
134 | name: 'title'
135 | }
136 | });
137 |
138 | expect(wrapper.first('select').hasAttribute('disabled')).toBe(false);
139 | });
140 |
141 | test("by default the class for the input is form-control", () => {
142 | let wrapper: any = mount(SelectList, {
143 | propsData: {
144 | name: 'title'
145 | }
146 | });
147 |
148 | expect(wrapper.first('select').hasClass('form-control')).toBe(true);
149 | });
150 |
151 | test("the form-control can be large", () => {
152 | let wrapper: any = mount(SelectList, {
153 | propsData: {
154 | name: 'title',
155 | large: true
156 | }
157 | });
158 |
159 | expect(wrapper.first('select').hasClass('form-control')).toBe(true);
160 | expect(wrapper.first('select').hasClass('form-control-lg')).toBe(true);
161 | });
162 |
163 | test("the form-control can be small", () => {
164 | let wrapper: any = mount(SelectList, {
165 | propsData: {
166 | name: 'title',
167 | small: true
168 | }
169 | });
170 |
171 | expect(wrapper.first('select').hasClass('form-control')).toBe(true);
172 | expect(wrapper.first('select').hasClass('form-control-sm')).toBe(true);
173 | });
174 |
175 | test("the input can be inline", () => {
176 | let wrapper: any = mount(SelectList, {
177 | propsData: {
178 | name: 'title',
179 | label: 'My Label',
180 | inline: true
181 | }
182 | });
183 |
184 | expect(wrapper.find('.col-sm-4').length).toBe(1);
185 | expect(wrapper.first('.form-group').hasClass('row')).toBe(true);
186 | expect(wrapper.first('.col-sm-4').contains('label')).toBe(true);
187 | expect(wrapper.first('label').hasClass('col-form-label')).toBe(true);
188 | expect(wrapper.find('.col-sm-8').length).toBe(1);
189 | });
190 |
191 | test("if the input is not inline, it does not have the row class", () => {
192 | let wrapper: any = mount(SelectList, {
193 | propsData: {
194 | name: 'title',
195 | label: 'My Label',
196 | }
197 | });
198 |
199 | expect(wrapper.first('.form-group').hasClass('row')).toBe(false);
200 | });
201 |
202 | test("the field can be marked as invalid", () => {
203 | let wrapper: any = mount(SelectList, {
204 | propsData: {
205 | name: 'title',
206 | label: 'My Label',
207 | invalid: true,
208 | errorMessage: "There was an error"
209 | }
210 | });
211 |
212 | expect(wrapper.first('.form-control').hasClass('is-invalid')).toBe(true);
213 | expect(wrapper.first('.form-group').contains('.invalid-feedback')).toBe(true);
214 | expect(wrapper.first('.invalid-feedback').text()).toBe("There was an error");
215 | });
216 |
217 | test("the field does not show invalid feedback if it has not been marked as invalid", () => {
218 | let wrapper: any = mount(SelectList, {
219 | propsData: {
220 | name: 'title',
221 | label: 'My Label',
222 | errorMessage: "There was an error"
223 | }
224 | });
225 |
226 | expect(wrapper.first('.form-control').hasClass('is-invalid')).toBe(false);
227 | expect(wrapper.first('.form-group').contains('.invalid-feedback')).toBe(false);
228 | });
229 |
230 | test("the field does not show invalid feedback if no message has been passed", () => {
231 | let wrapper: any = mount(SelectList, {
232 | propsData: {
233 | name: 'title',
234 | label: 'My Label',
235 | invalid: true
236 | }
237 | });
238 |
239 | expect(wrapper.first('.form-control').hasClass('is-invalid')).toBe(true);
240 | expect(wrapper.first('.invalid-feedback').contains('.invalid-feedback')).toBe(false);
241 | });
242 |
243 | test("if no slots are being used the usingAddons property is false", () => {
244 | let wrapper: any = mount(SelectList, {
245 | propsData: {
246 | name: 'title',
247 | label: 'My Label',
248 | }
249 | });
250 |
251 | expect(wrapper.vm.usingAddons).toBe(false);
252 | expect(wrapper.vm.slotExists('leftAddon')).toBe(false);
253 | expect(wrapper.vm.slotExists('rightAddon')).toBe(false);
254 | });
255 |
256 | test("if the left add-on slot is being used the using addons property is true", () => {
257 | let wrapper: any = mount(SelectList, {
258 | propsData: {
259 | name: 'title',
260 | label: 'My Label',
261 | },
262 | slots: {
263 | leftAddon: [StandardSlot]
264 | }
265 | });
266 |
267 | expect(wrapper.vm.usingAddons).toBe(true);
268 | expect(wrapper.vm.slotExists('leftAddon')).toBe(true);
269 | expect(wrapper.vm.slotExists('rightAddon')).toBe(false);
270 | });
271 |
272 | test("if the right add-on slot is being used the using addons property is true", () => {
273 | let wrapper: any = mount(SelectList, {
274 | propsData: {
275 | name: 'title',
276 | label: 'My Label',
277 | },
278 | slots: {
279 | rightAddon: [StandardSlot]
280 | }
281 | });
282 |
283 | expect(wrapper.vm.usingAddons).toBe(true);
284 | expect(wrapper.vm.slotExists('leftAddon')).toBe(false);
285 | expect(wrapper.vm.slotExists('rightAddon')).toBe(true);
286 | });
287 |
288 | test("if the left add-on slot is being used it outputs the addon", () => {
289 | let wrapper: any = mount(SelectList, {
290 | propsData: {
291 | name: 'title',
292 | label: 'My Label',
293 | },
294 | slots: {
295 | leftAddon: [StandardSlot]
296 | }
297 | });
298 |
299 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
300 | expect(wrapper.first('.input-group').contains('.input-group-addon')).toBe(true);
301 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
302 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
303 | expect(wrapper.first('.input-group-addon').text()).toBe('addon');
304 | });
305 |
306 | test("if the right add-on slot is being used it outputs the addon", () => {
307 | let wrapper: any = mount(SelectList, {
308 | propsData: {
309 | name: 'title',
310 | label: 'My Label',
311 | },
312 | slots: {
313 | rightAddon: [StandardSlot]
314 | }
315 | });
316 |
317 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
318 | expect(wrapper.first('.input-group').contains('.input-group-addon')).toBe(true);
319 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
320 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
321 | expect(wrapper.first('.input-group-addon').text()).toBe('addon');
322 | });
323 |
324 | test("if the field has input groups and is large it has the correct classes", () => {
325 | let wrapper: any = mount(SelectList, {
326 | propsData: {
327 | name: 'title',
328 | label: 'My Label',
329 | large: true
330 | },
331 | slots: {
332 | rightAddon: [StandardSlot]
333 | }
334 | });
335 |
336 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
337 | expect(wrapper.first('.input-group').hasClass('input-group-lg')).toBe(true);
338 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
339 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
340 | expect(wrapper.first('.form-control').hasClass('form-control-lg')).toBe(false);
341 | });
342 |
343 | test("if the field has input groups and is small it has the correct classes", () => {
344 | let wrapper: any = mount(SelectList, {
345 | propsData: {
346 | name: 'title',
347 | label: 'My Label',
348 | small: true
349 | },
350 | slots: {
351 | rightAddon: [StandardSlot]
352 | }
353 | });
354 |
355 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
356 | expect(wrapper.first('.input-group').hasClass('input-group-sm')).toBe(true);
357 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
358 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
359 | expect(wrapper.first('.form-control').hasClass('form-control-sm')).toBe(false);
360 | });
361 |
362 | test("if the left button slot is being used it outputs the button", () => {
363 | let wrapper: any = mount(SelectList, {
364 | propsData: {
365 | name: 'title',
366 | label: 'My Label',
367 | },
368 | slots: {
369 | leftButton: [StandardSlot]
370 | }
371 | });
372 |
373 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
374 | expect(wrapper.first('.input-group').contains('.input-group-btn')).toBe(true);
375 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
376 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
377 | expect(wrapper.first('.input-group-btn').text()).toBe('addon');
378 | });
379 |
380 | test("if the right button slot is being used it outputs the button", () => {
381 | let wrapper: any = mount(SelectList, {
382 | propsData: {
383 | name: 'title',
384 | label: 'My Label',
385 | },
386 | slots: {
387 | rightButton: [StandardSlot]
388 | }
389 | });
390 |
391 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
392 | expect(wrapper.first('.input-group').contains('.input-group-btn')).toBe(true);
393 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
394 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
395 | expect(wrapper.first('.input-group-btn').text()).toBe('addon');
396 | });
397 |
398 | test("the helper text can be placed under the label", () => {
399 | let wrapper: any = mount(SelectList, {
400 | propsData: {
401 | name: 'title',
402 | label: 'My Label',
403 | helper: 'This is helper text',
404 | inline: true,
405 | metaUnderLabel: true
406 | }
407 | });
408 |
409 | expect(wrapper.first('.col-sm-4').contains('.form-text')).toBe(true);
410 | expect(wrapper.first('.col-sm-8').contains('.form-text')).toBe(false);
411 | });
412 |
413 | test("the error text can be placed under the label", () => {
414 | let wrapper: any = mount(SelectList, {
415 | propsData: {
416 | name: 'title',
417 | label: 'My Label',
418 | helper: 'This is helper text',
419 | inline: true,
420 | metaUnderLabel: true,
421 | invalid: true,
422 | errorMessage: 'There was an error'
423 | }
424 | });
425 |
426 | expect(wrapper.first('.col-sm-4').contains('.invalid-feedback')).toBe(true);
427 | expect(wrapper.first('.col-sm-8').contains('.invalid-feedback')).toBe(false);
428 | });
429 |
430 | test("it emits an event on change", () => {
431 | let wrapper: any = mount(SelectList, {
432 | propsData: {
433 | name: 'title',
434 | label: 'My Label'
435 | }
436 | });
437 | const spy = sinon.spy(wrapper.vm, '$emit');
438 | wrapper.first('.form-control').trigger("change");
439 | expect(spy.args[0][0]).toBe('input')
440 | });
441 |
442 | test("it can output the options for the list", () => {
443 | let options = [
444 | {
445 | id: '1',
446 | label: 'Mr'
447 | },
448 | {
449 | id: '2',
450 | label: 'Mrs'
451 | }
452 | ];
453 |
454 | let wrapper: any = mount(SelectList, {
455 | propsData: {
456 | name: 'title',
457 | label: 'My Label',
458 | options: options
459 | }
460 | });
461 | let foundOptions = wrapper.find('option');
462 |
463 | expect(foundOptions.length).toBe(2);
464 | expect(foundOptions[0].getAttribute('value')).toBe('1');
465 | expect(foundOptions[1].getAttribute('value')).toBe('2');
466 | expect(foundOptions[0].text()).toBe('Mr');
467 | expect(foundOptions[1].text()).toBe('Mrs');
468 | });
469 |
470 | test("the key name can be changed for options", () => {
471 | let options = [
472 | {
473 | key: '1',
474 | label: 'Mr'
475 | },
476 | {
477 | key: '2',
478 | label: 'Mrs'
479 | }
480 | ];
481 |
482 | let wrapper: any = mount(SelectList, {
483 | propsData: {
484 | name: 'title',
485 | label: 'My Label',
486 | options: options,
487 | keyName: 'key'
488 | }
489 | });
490 | let foundOptions = wrapper.find('option');
491 |
492 | expect(foundOptions.length).toBe(2);
493 | expect(foundOptions[0].getAttribute('value')).toBe('1');
494 | expect(foundOptions[1].getAttribute('value')).toBe('2');
495 | expect(foundOptions[0].text()).toBe('Mr');
496 | expect(foundOptions[1].text()).toBe('Mrs');
497 | });
498 |
499 | test("the label name can be changed for options", () => {
500 | let options = [
501 | {
502 | id: '1',
503 | value: 'Mr'
504 | },
505 | {
506 | id: '2',
507 | value: 'Mrs'
508 | }
509 | ];
510 |
511 | let wrapper: any = mount(SelectList, {
512 | propsData: {
513 | name: 'title',
514 | label: 'My Label',
515 | options: options,
516 | labelName: 'value'
517 | }
518 | });
519 | let foundOptions = wrapper.find('option');
520 |
521 | expect(foundOptions.length).toBe(2);
522 | expect(foundOptions[0].getAttribute('value')).toBe('1');
523 | expect(foundOptions[1].getAttribute('value')).toBe('2');
524 | expect(foundOptions[0].text()).toBe('Mr');
525 | expect(foundOptions[1].text()).toBe('Mrs');
526 | });
527 |
528 | test("the value of the input can be set", () => {
529 | let options = [
530 | {
531 | id: '1',
532 | label: 'Mr'
533 | },
534 | {
535 | id: '2',
536 | label: 'Mrs'
537 | }
538 | ];
539 |
540 | let wrapper: any = mount(SelectList, {
541 | propsData: {
542 | name: 'title',
543 | label: 'My Label',
544 | options: options,
545 | value: '2'
546 | }
547 | });
548 |
549 | expect(wrapper.first('select').value()).toBe('2');
550 | });
551 | });
552 |
--------------------------------------------------------------------------------
/tests/input-box.spec.ts:
--------------------------------------------------------------------------------
1 | import { suite, test, slow, timeout } from "mocha-typescript";
2 | import {mount} from "avoriaz";
3 | import InputBox from "./../src/input-box.ts";
4 | import expect from 'expect';
5 | import {StandardSlot} from "./resources.ts";
6 | import sinon from 'sinon';
7 |
8 | suite("InputBox", () => {
9 | test("it can set the name", () => {
10 | let wrapper: any = mount(InputBox, {
11 | propsData: {
12 | name: 'username'
13 | }
14 | });
15 |
16 | expect(wrapper.first('input').hasAttribute('name')).toBe(true);
17 | expect(wrapper.first('input').getAttribute('name')).toBe('username');
18 | });
19 |
20 | test("if no type is specified it defaults to text", () => {
21 | let wrapper: any = mount(InputBox, {
22 | propsData: {
23 | name: 'username'
24 | }
25 | });
26 |
27 | expect(wrapper.first('input').getAttribute('type')).toBe('text');
28 | });
29 |
30 | test("the type can be overwritten", () => {
31 | let wrapper: any = mount(InputBox, {
32 | propsData: {
33 | name: 'username',
34 | type: 'email'
35 | }
36 | });
37 |
38 | expect(wrapper.first('input').getAttribute('type')).toBe('email');
39 | });
40 |
41 | test("the label gets output", () => {
42 | let wrapper: any = mount(InputBox, {
43 | propsData: {
44 | name: 'username',
45 | label: 'My Input'
46 | }
47 | });
48 |
49 | expect(wrapper.first('label').text()).toBe('My Input');
50 | });
51 |
52 | test("if no label is specified it does not show it", () => {
53 | let wrapper: any = mount(InputBox, {
54 | propsData: {
55 | name: 'username'
56 | }
57 | });
58 | expect(wrapper.find('label').length).toBe(0);
59 | });
60 |
61 | test("if no placeholder is specified it does not show one", () => {
62 | let wrapper: any = mount(InputBox, {
63 | propsData: {
64 | name: 'username'
65 | }
66 | });
67 | expect(wrapper.first('input').hasAttribute('placeholder')).toBe(false);
68 | });
69 |
70 | test("if a placeholder is specified it shows one", () => {
71 | let wrapper: any = mount(InputBox, {
72 | propsData: {
73 | name: 'username',
74 | placeholder: 'My Placeholder'
75 | }
76 | });
77 | expect(wrapper.first('input').hasAttribute('placeholder')).toBe(true);
78 | expect(wrapper.first('input').getAttribute('placeholder')).toBe('My Placeholder');
79 | });
80 |
81 | test("if no helper is provided it does not show one", () => {
82 | let wrapper: any = mount(InputBox, {
83 | propsData: {
84 | name: 'username',
85 | }
86 | });
87 | expect(wrapper.find('small.form-text').length).toBe(0);
88 | expect(wrapper.first('.form-group').hasClass('has-helper')).toBe(false);
89 | });
90 |
91 | test("if helper text is provided it does show it", () => {
92 | let wrapper: any = mount(InputBox, {
93 | propsData: {
94 | name: 'username',
95 | helper: 'Please enter your email address'
96 | }
97 | });
98 |
99 | expect(wrapper.find('small.form-text').length).toBe(1);
100 | expect(wrapper.first('small.form-text').text()).toBe("Please enter your email address");
101 | expect(wrapper.first('.form-group').hasClass('has-helper')).toBe(true);
102 | });
103 |
104 | test("if the input is not required it does not have the required attributes", () => {
105 | let wrapper: any = mount(InputBox, {
106 | propsData: {
107 | name: 'username',
108 | }
109 | });
110 |
111 | expect(wrapper.find('span.required').length).toBe(0);
112 | expect(wrapper.first('input').hasAttribute('required')).toBe(false);
113 | });
114 |
115 | test("if the input is required it have the required attributes", () => {
116 | let wrapper: any = mount(InputBox, {
117 | propsData: {
118 | name: 'username',
119 | label: 'Your Email',
120 | required: true
121 | }
122 | });
123 |
124 | expect(wrapper.find('span.required').length).toBe(1);
125 | expect(wrapper.first('span.required').text()).toBe('*');
126 | expect(wrapper.first('input').hasAttribute('required')).toBe(true);
127 | });
128 |
129 | test("if the input is required but no label has been specified the input field has the required attribute", () => {
130 | let wrapper: any = mount(InputBox, {
131 | propsData: {
132 | name: 'username',
133 | required: true
134 | }
135 | });
136 |
137 | expect(wrapper.first('input').hasAttribute('required')).toBe(true);
138 | });
139 |
140 | test("the input can have the readonly attribute", () => {
141 | let wrapper: any = mount(InputBox, {
142 | propsData: {
143 | name: 'username',
144 | readonly: true,
145 | }
146 | });
147 |
148 | expect(wrapper.first('input').hasAttribute('readonly')).toBe(true);
149 | });
150 |
151 | test("by default the input does not have a read only attribute", () => {
152 | let wrapper: any = mount(InputBox, {
153 | propsData: {
154 | name: 'username'
155 | }
156 | });
157 |
158 | expect(wrapper.first('input').hasAttribute('readonly')).toBe(false);
159 | });
160 |
161 | test("by default the class for the input is form-control", () => {
162 | let wrapper: any = mount(InputBox, {
163 | propsData: {
164 | name: 'username'
165 | }
166 | });
167 |
168 | expect(wrapper.first('input').hasClass('form-control')).toBe(true);
169 | });
170 |
171 | test("the form-control can be large", () => {
172 | let wrapper: any = mount(InputBox, {
173 | propsData: {
174 | name: 'username',
175 | large: true
176 | }
177 | });
178 |
179 | expect(wrapper.first('input').hasClass('form-control')).toBe(true);
180 | expect(wrapper.first('input').hasClass('form-control-lg')).toBe(true);
181 | });
182 |
183 | test("the form-control can be small", () => {
184 | let wrapper: any = mount(InputBox, {
185 | propsData: {
186 | name: 'username',
187 | small: true
188 | }
189 | });
190 |
191 | expect(wrapper.first('input').hasClass('form-control')).toBe(true);
192 | expect(wrapper.first('input').hasClass('form-control-sm')).toBe(true);
193 | });
194 |
195 | test("the form-control can be plain text mode", () => {
196 | let wrapper: any = mount(InputBox, {
197 | propsData: {
198 | name: 'username',
199 | plainText: true
200 | }
201 | });
202 |
203 | expect(wrapper.first('input').hasClass('form-control')).toBe(false);
204 | expect(wrapper.first('input').hasClass('form-control-plaintext')).toBe(true);
205 | });
206 |
207 | test("the input box can be inline", () => {
208 | let wrapper: any = mount(InputBox, {
209 | propsData: {
210 | name: 'username',
211 | label: 'My Label',
212 | inline: true
213 | }
214 | });
215 |
216 | expect(wrapper.find('.col-sm-4').length).toBe(1);
217 | expect(wrapper.first('.form-group').hasClass('row')).toBe(true);
218 | expect(wrapper.first('.col-sm-4').contains('label')).toBe(true);
219 | expect(wrapper.first('label').hasClass('col-form-label')).toBe(true);
220 | expect(wrapper.find('.col-sm-8').length).toBe(1);
221 | });
222 |
223 | test("if the input is not inline, it does not have the row class", () => {
224 | let wrapper: any = mount(InputBox, {
225 | propsData: {
226 | name: 'username',
227 | label: 'My Label',
228 | }
229 | });
230 |
231 | expect(wrapper.first('.form-group').hasClass('row')).toBe(false);
232 | });
233 |
234 | test("the field can be marked as invalid", () => {
235 | let wrapper: any = mount(InputBox, {
236 | propsData: {
237 | name: 'username',
238 | label: 'My Label',
239 | invalid: true,
240 | errorMessage: "There was an error"
241 | }
242 | });
243 |
244 | expect(wrapper.first('.form-control').hasClass('is-invalid')).toBe(true);
245 | expect(wrapper.first('.form-group').contains('.invalid-feedback')).toBe(true);
246 | expect(wrapper.first('.invalid-feedback').text()).toBe("There was an error");
247 | });
248 |
249 | test("the field does not show invalid feedback if it has not been marked as invalid", () => {
250 | let wrapper: any = mount(InputBox, {
251 | propsData: {
252 | name: 'username',
253 | label: 'My Label',
254 | errorMessage: "There was an error"
255 | }
256 | });
257 |
258 | expect(wrapper.first('.form-control').hasClass('is-invalid')).toBe(false);
259 | expect(wrapper.first('.form-group').contains('.invalid-feedback')).toBe(false);
260 | });
261 |
262 | test("the field does not show invalid feedback if no message has been passed", () => {
263 | let wrapper: any = mount(InputBox, {
264 | propsData: {
265 | name: 'username',
266 | label: 'My Label',
267 | invalid: true
268 | }
269 | });
270 |
271 | expect(wrapper.first('.form-control').hasClass('is-invalid')).toBe(true);
272 | expect(wrapper.first('.invalid-feedback').contains('.invalid-feedback')).toBe(false);
273 | });
274 |
275 | test("if no slots are being used the usingAddons property is false", () => {
276 | let wrapper: any = mount(InputBox, {
277 | propsData: {
278 | name: 'username',
279 | label: 'My Label',
280 | }
281 | });
282 |
283 | expect(wrapper.vm.usingAddons).toBe(false);
284 | expect(wrapper.vm.slotExists('leftAddon')).toBe(false);
285 | expect(wrapper.vm.slotExists('rightAddon')).toBe(false);
286 | });
287 |
288 | test("if the left add-on slot is being used the using addons property is true", () => {
289 | let wrapper: any = mount(InputBox, {
290 | propsData: {
291 | name: 'username',
292 | label: 'My Label',
293 | },
294 | slots: {
295 | leftAddon: [StandardSlot]
296 | }
297 | });
298 |
299 | expect(wrapper.vm.usingAddons).toBe(true);
300 | expect(wrapper.vm.slotExists('leftAddon')).toBe(true);
301 | expect(wrapper.vm.slotExists('rightAddon')).toBe(false);
302 | });
303 |
304 | test("if the right add-on slot is being used the using addons property is true", () => {
305 | let wrapper: any = mount(InputBox, {
306 | propsData: {
307 | name: 'username',
308 | label: 'My Label',
309 | },
310 | slots: {
311 | rightAddon: [StandardSlot]
312 | }
313 | });
314 |
315 | expect(wrapper.vm.usingAddons).toBe(true);
316 | expect(wrapper.vm.slotExists('leftAddon')).toBe(false);
317 | expect(wrapper.vm.slotExists('rightAddon')).toBe(true);
318 | });
319 |
320 | test("if the left add-on slot is being used it outputs the addon", () => {
321 | let wrapper: any = mount(InputBox, {
322 | propsData: {
323 | name: 'username',
324 | label: 'My Label',
325 | },
326 | slots: {
327 | leftAddon: [StandardSlot]
328 | }
329 | });
330 |
331 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
332 | expect(wrapper.first('.input-group').contains('.input-group-addon')).toBe(true);
333 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
334 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
335 | expect(wrapper.first('.input-group-addon').text()).toBe('addon');
336 | });
337 |
338 | test("if the right add-on slot is being used it outputs the addon", () => {
339 | let wrapper: any = mount(InputBox, {
340 | propsData: {
341 | name: 'username',
342 | label: 'My Label',
343 | },
344 | slots: {
345 | rightAddon: [StandardSlot]
346 | }
347 | });
348 |
349 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
350 | expect(wrapper.first('.input-group').contains('.input-group-addon')).toBe(true);
351 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
352 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
353 | expect(wrapper.first('.input-group-addon').text()).toBe('addon');
354 | });
355 |
356 | test("if the field has input groups and is large it has the correct classes", () => {
357 | let wrapper: any = mount(InputBox, {
358 | propsData: {
359 | name: 'username',
360 | label: 'My Label',
361 | large: true
362 | },
363 | slots: {
364 | rightAddon: [StandardSlot]
365 | }
366 | });
367 |
368 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
369 | expect(wrapper.first('.input-group').hasClass('input-group-lg')).toBe(true);
370 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
371 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
372 | expect(wrapper.first('.form-control').hasClass('form-control-lg')).toBe(false);
373 | });
374 |
375 | test("if the field has input groups and is small it has the correct classes", () => {
376 | let wrapper: any = mount(InputBox, {
377 | propsData: {
378 | name: 'username',
379 | label: 'My Label',
380 | small: true
381 | },
382 | slots: {
383 | rightAddon: [StandardSlot]
384 | }
385 | });
386 |
387 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
388 | expect(wrapper.first('.input-group').hasClass('input-group-sm')).toBe(true);
389 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
390 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
391 | expect(wrapper.first('.form-control').hasClass('form-control-sm')).toBe(false);
392 | });
393 |
394 | test("if the left button slot is being used it outputs the button", () => {
395 | let wrapper: any = mount(InputBox, {
396 | propsData: {
397 | name: 'username',
398 | label: 'My Label',
399 | },
400 | slots: {
401 | leftButton: [StandardSlot]
402 | }
403 | });
404 |
405 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
406 | expect(wrapper.first('.input-group').contains('.input-group-btn')).toBe(true);
407 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
408 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
409 | expect(wrapper.first('.input-group-btn').text()).toBe('addon');
410 | });
411 |
412 | test("if the right button slot is being used it outputs the button", () => {
413 | let wrapper: any = mount(InputBox, {
414 | propsData: {
415 | name: 'username',
416 | label: 'My Label',
417 | },
418 | slots: {
419 | rightButton: [StandardSlot]
420 | }
421 | });
422 |
423 | expect(wrapper.first('.form-group').contains('.input-group')).toBe(true);
424 | expect(wrapper.first('.input-group').contains('.input-group-btn')).toBe(true);
425 | expect(wrapper.first('.input-group').contains('.form-text')).toBe(false);
426 | expect(wrapper.first('.input-group').contains('.invalid-feedback')).toBe(false);
427 | expect(wrapper.first('.input-group-btn').text()).toBe('addon');
428 | });
429 |
430 | test("the helper text can be placed under the label", () => {
431 | let wrapper: any = mount(InputBox, {
432 | propsData: {
433 | name: 'username',
434 | label: 'My Label',
435 | helper: 'This is helper text',
436 | inline: true,
437 | metaUnderLabel: true
438 | }
439 | });
440 |
441 | expect(wrapper.first('.col-sm-4').contains('.form-text')).toBe(true);
442 | expect(wrapper.first('.col-sm-8').contains('.form-text')).toBe(false);
443 | });
444 |
445 | test("the error text can be placed under the label", () => {
446 | let wrapper: any = mount(InputBox, {
447 | propsData: {
448 | name: 'username',
449 | label: 'My Label',
450 | helper: 'This is helper text',
451 | inline: true,
452 | metaUnderLabel: true,
453 | invalid: true,
454 | errorMessage: 'There was an error'
455 | }
456 | });
457 |
458 | expect(wrapper.first('.col-sm-4').contains('.invalid-feedback')).toBe(true);
459 | expect(wrapper.first('.col-sm-8').contains('.invalid-feedback')).toBe(false);
460 | });
461 |
462 | test("when the enter key is pressed it emits an event", () => {
463 | let wrapper: any = mount(InputBox, {
464 | propsData: {
465 | name: 'username',
466 | label: 'My Label'
467 | }
468 | });
469 | const spy = sinon.spy(wrapper.vm, '$emit');
470 | wrapper.first('.form-control').trigger("keyup.enter");
471 | expect(spy.args[0][0]).toBe('enter')
472 | });
473 |
474 | test("it emits an event on input", () => {
475 | let wrapper: any = mount(InputBox, {
476 | propsData: {
477 | name: 'username',
478 | label: 'My Label'
479 | }
480 | });
481 | const spy = sinon.spy(wrapper.vm, '$emit');
482 | wrapper.first('.form-control').trigger("input");
483 | expect(spy.args[0][0]).toBe('input')
484 | });
485 |
486 | test("the value of the input can be set", () => {
487 | let wrapper: any = mount(InputBox, {
488 | propsData: {
489 | name: 'username',
490 | label: 'My Label',
491 | value: 'test'
492 | }
493 | });
494 |
495 | expect(wrapper.first('.form-control').value()).toBe('test');
496 | });
497 |
498 |
499 | test("the max length of the input can be set", () => {
500 | let wrapper: any = mount(InputBox, {
501 | propsData: {
502 | name: 'username',
503 | label: 'My Label',
504 | value: 'test',
505 | maxLength: 10
506 | }
507 | });
508 |
509 | expect(wrapper.first('input').hasAttribute('maxlength')).toBe(true);
510 | expect(wrapper.first('input').getAttribute('maxlength')).toBe("10");
511 | });
512 |
513 | test("the max length of the input is set by default", () => {
514 | let wrapper: any = mount(InputBox, {
515 | propsData: {
516 | name: 'username',
517 | label: 'My Label',
518 | value: 'test'
519 | }
520 | });
521 |
522 | expect(wrapper.first('input').hasAttribute('maxlength')).toBe(true);
523 | expect(wrapper.first('input').getAttribute('maxlength')).toBe("524288");
524 | });
525 |
526 | test("the autocomplete of the input is not rendered by default", () => {
527 | let wrapper: any = mount(InputBox, {
528 | propsData: {
529 | name: 'username',
530 | label: 'My Label',
531 | value: 'test'
532 | }
533 | });
534 |
535 | expect(wrapper.first('input').hasAttribute('autocomplete')).toBe(true);
536 | expect(wrapper.first('input').getAttribute('autocomplete')).toBe('on');
537 | });
538 |
539 | test("the autocomplete of the input can be disabled", () => {
540 | let wrapper: any = mount(InputBox, {
541 | propsData: {
542 | name: 'username',
543 | label: 'My Label',
544 | value: 'test',
545 | autoComplete: false
546 | }
547 | });
548 |
549 | expect(wrapper.first('input').hasAttribute('autocomplete')).toBe(true);
550 | expect(wrapper.first('input').getAttribute('autocomplete')).toBe('off');
551 | });
552 | });
553 |
--------------------------------------------------------------------------------