Repository: fengyuanchen/vue-number-input Branch: main Commit: 87099b1f0ab1 Files: 35 Total size: 58.1 KB Directory structure: gitextract_qqq1rvje/ ├── .browserslistrc ├── .eslintignore ├── .eslintrc.js ├── .github/ │ └── workflows/ │ ├── ci.yml │ └── deploy.yml ├── .gitignore ├── .husky/ │ ├── commit-msg │ └── pre-commit ├── .stylelintignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.js ├── commitlint.config.js ├── docs/ │ ├── app.vue │ ├── components/ │ │ └── demo-block.vue │ ├── index.html │ └── index.ts ├── jest.config.js ├── lint-staged.config.js ├── package.json ├── postcss.config.js ├── rollup.config.js ├── src/ │ ├── README.md │ ├── index.ts │ ├── shims.d.ts │ └── vue-number-input.vue ├── stylelint.config.js ├── tests/ │ ├── events.spec.ts │ ├── methods.spec.ts │ ├── others.spec.ts │ └── props.spec.ts ├── tsconfig.eslint.json ├── tsconfig.json └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .browserslistrc ================================================ defaults not ie 11 ================================================ FILE: .eslintignore ================================================ *.local* coverage dist node_modules ================================================ FILE: .eslintrc.js ================================================ module.exports = { root: true, env: { browser: true, node: true, }, extends: [ 'airbnb-typescript/base', 'plugin:@typescript-eslint/recommended', 'plugin:vue/vue3-recommended', ], parser: 'vue-eslint-parser', parserOptions: { parser: '@typescript-eslint/parser', project: 'tsconfig.eslint.json', sourceType: 'module', extraFileExtensions: ['.vue'], }, plugins: [ '@typescript-eslint', 'import', 'vue', ], rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-var-requires': 'off', 'no-restricted-properties': 'off', }, overrides: [ { files: ['tests/**/*.ts'], env: { jest: true, }, }, ], }; ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: branches: [main] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: node-version: 16 - run: npm install - run: npm run lint - run: npm run build - run: npm test - run: npm run test:coverage ================================================ FILE: .github/workflows/deploy.yml ================================================ name: Deploy on: push: tags: - v2.* workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: node-version: 16 - run: npm install - run: npm run build:docs - run: | cd docs/dist git init git config user.name "${{ github.actor }}" git config user.email "${{ github.actor }}@users.noreply.github.com" git add --all git commit --message "♥" git push --force https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git master:gh-pages ================================================ FILE: .gitignore ================================================ *.local* *.log *.map .DS_Store coverage dist node_modules ================================================ FILE: .husky/commit-msg ================================================ #!/bin/sh . "$(dirname "$0")/_/husky.sh" npx commitlint --edit $1 ================================================ FILE: .husky/pre-commit ================================================ #!/bin/sh . "$(dirname "$0")/_/husky.sh" npx lint-staged ================================================ FILE: .stylelintignore ================================================ *.local* coverage dist node_modules ================================================ FILE: CHANGELOG.md ================================================ ## [2.0.1](https://github.com/fengyuanchen/vue-number-input/compare/v2.0.0...v2.0.1) (2022-02-13) ### Bug Fixes * round the normalized new value ([30aaa6a](https://github.com/fengyuanchen/vue-number-input/commit/30aaa6a4a74b99e177477fb21f8ad178d13bb8af)), closes [#32](https://github.com/fengyuanchen/vue-number-input/issues/32) # [2.0.0](https://github.com/fengyuanchen/vue-number-input/compare/v2.0.0-rc.2...v2.0.0) (2022-02-07) # [2.0.0-rc.2](https://github.com/fengyuanchen/vue-number-input/compare/v2.0.0-rc.1...v2.0.0-rc.2) (2022-01-22) ### Bug Fixes * correct the declaration file name ([7f96219](https://github.com/fengyuanchen/vue-number-input/commit/7f96219dc88afa516244db39715b087bcb04dbcf)) # [2.0.0-rc.1](https://github.com/fengyuanchen/vue-number-input/compare/v2.0.0-rc...v2.0.0-rc.1) (2022-01-08) ### Bug Fixes * add `oldValue` to `update:modelValue` event ([4fa92f0](https://github.com/fengyuanchen/vue-number-input/commit/4fa92f03047ebe5dbc45a5e531575d1df081f20c)) * avoid specifying `NaN` value to number input ([101abce](https://github.com/fengyuanchen/vue-number-input/commit/101abce5375f938fd06301877a84cb25c88df71c)), closes [#31](https://github.com/fengyuanchen/vue-number-input/issues/31) ### Features * add auto-generated declaration file ([5ecd316](https://github.com/fengyuanchen/vue-number-input/commit/5ecd316699c3e60d0f7b6d591c0d3476e3f03cd2)) # [2.0.0-rc](https://github.com/fengyuanchen/vue-number-input/compare/v2.0.0-beta.1...v2.0.0-rc) (2021-06-12) # [2.0.0-beta.1](https://github.com/fengyuanchen/vue-number-input/compare/v2.0.0-beta...v2.0.0-beta.1) (2021-04-10) ### Bug Fixes * round the new value in the last setp ([c43a69f](https://github.com/fengyuanchen/vue-number-input/commit/c43a69f5b67c5e02fbd1eccb007c947aa5482f8e)) * should emit a single value ([cb8bed9](https://github.com/fengyuanchen/vue-number-input/commit/cb8bed9b9470882588e8b281466cea088afc0009)) ### Features * support to clear the input value through the keyboard ([fd9faf2](https://github.com/fengyuanchen/vue-number-input/commit/fd9faf23737fb568426fbbe0d879c076cb6cf419)), closes [#28](https://github.com/fengyuanchen/vue-number-input/issues/28) # [2.0.0-beta](https://github.com/fengyuanchen/vue-number-input/compare/v2.0.0-alpha...v2.0.0-beta) (2021-01-16) # [2.0.0-alpha](https://github.com/fengyuanchen/vue-number-input/compare/v1.2.1...v2.0.0-alpha) (2021-01-08) * refactor!: upgrade to Vue 3 ([65be700](https://github.com/fengyuanchen/vue-number-input/commit/65be700df03c167abb347c546ba579035d59fc16)) ### BREAKING CHANGES * drop support for Vue 2. ## [1.2.1](https://github.com/fengyuanchen/vue-number-input/compare/v1.2.0...v1.2.1) (2020-01-18) ### Bug Fixes * add `tabindex` attribute to control buttons ([05869bc](https://github.com/fengyuanchen/vue-number-input/commit/05869bcb058d06608085141b0260962b6f028262)), closes [#20](https://github.com/fengyuanchen/vue-number-input/issues/20) # [1.2.0](https://github.com/fengyuanchen/vue-number-input/compare/v1.1.2...v1.2.0) (2019-10-19) ### Features * add new `attrs` prop ([e8b1498](https://github.com/fengyuanchen/vue-number-input/commit/e8b1498fa485253392afe505c854f6967d6e5990)) ## [1.1.2](https://github.com/fengyuanchen/vue-number-input/compare/v1.1.1...v1.1.2) (2019-10-02) ### Bug Fixes * hide the spin box in number input box in Firefox ([580ec5c](https://github.com/fengyuanchen/vue-number-input/commit/580ec5c960c6b9bd4984631a1932321443673ba5)), closes [#17](https://github.com/fengyuanchen/vue-number-input/issues/17) ## [1.1.1](https://github.com/fengyuanchen/vue-number-input/compare/v1.1.0...v1.1.1) (2019-03-09) ### Bug Fixes * add missing `-webkit-` prefix to `appearance` property ([f71d0ae](https://github.com/fengyuanchen/vue-number-input/commit/f71d0aeb2e6211e2b37dd66cc348fb035247c207)), closes [#12](https://github.com/fengyuanchen/vue-number-input/issues/12) * force to override the number in the input box ([7d816d4](https://github.com/fengyuanchen/vue-number-input/commit/7d816d4ec7652160aae2b330e9502ce2e256fc9a)), closes [#13](https://github.com/fengyuanchen/vue-number-input/issues/13) # [1.1.0](https://github.com/fengyuanchen/vue-number-input/compare/v1.0.0...v1.1.0) (2019-01-26) ### Features * register as a Vue plugin ([b23b09e](https://github.com/fengyuanchen/vue-number-input/commit/b23b09ef0e14601f0bb8b673bd2313b77d6c2e28)) * register the component automatically once loaded ([e86eab1](https://github.com/fengyuanchen/vue-number-input/commit/e86eab1db441100f71e667e5edb297c1030f6830)) # [1.0.0](https://github.com/fengyuanchen/vue-number-input/compare/v0.5.3...v1.0.0) (2018-12-20) ### Bug Fixes * avoid the size to less than 1px ([03d804d](https://github.com/fengyuanchen/vue-number-input/commit/03d804d418d1b21c971fe0e7df8a50cec2413bd9)), closes [#10](https://github.com/fengyuanchen/vue-number-input/issues/10) ### Performance Improvements * avoid to trigger change event when created ([b1cd06f](https://github.com/fengyuanchen/vue-number-input/commit/b1cd06f901f5c2567e94cb3353f326514ac30ed0)) ## [0.5.3](https://github.com/fengyuanchen/vue-number-input/compare/v0.5.2...v0.5.3) (2018-12-01) ### Bug Fixes * sync value only when it is different ([7100b1f](https://github.com/fengyuanchen/vue-number-input/commit/7100b1f01923a9dd4e5927e181a6f1fb15ae8711)), closes [#8](https://github.com/fengyuanchen/vue-number-input/issues/8) * update input value automatically ([b2dae7f](https://github.com/fengyuanchen/vue-number-input/commit/b2dae7f5358914d9e863b2a0604eaa2201ed7b1d)), closes [#9](https://github.com/fengyuanchen/vue-number-input/issues/9) ## [0.5.2](https://github.com/fengyuanchen/vue-number-input/compare/v0.5.1...v0.5.2) (2018-08-05) ### Bug Fixes * the 0.30000000000000004 problem ([8f5a3e0](https://github.com/fengyuanchen/vue-number-input/commit/8f5a3e0f63ed46417fa80d597aa7c246fde65ba4)), closes [#6](https://github.com/fengyuanchen/vue-number-input/issues/6) ## [0.5.1](https://github.com/fengyuanchen/vue-number-input/compare/v0.5.0...v0.5.1) (2018-06-09) ### Bug Fixes * change the input value only when it is mounted ([cada801](https://github.com/fengyuanchen/vue-number-input/commit/cada8012e9428e633188eab93b19cff6e35f8f17)), closes [#4](https://github.com/fengyuanchen/vue-number-input/issues/4) # [0.5.0](https://github.com/fengyuanchen/vue-number-input/compare/v0.4.1...v0.5.0) (2018-06-06) ### Bug Fixes * off auto complete ([d14af46](https://github.com/fengyuanchen/vue-number-input/commit/d14af4611b182615298e841db5806e00fac34615)) ### Features * add `placeholder` prop ([331caa6](https://github.com/fengyuanchen/vue-number-input/commit/331caa6a2a1828043a0df778ddd175cd63dce5c1)) * add `rounded` prop ([99dd179](https://github.com/fengyuanchen/vue-number-input/commit/99dd179d8e9b0438e867248e540d6f2a737a225d)) ## [0.4.1](https://github.com/fengyuanchen/vue-number-input/compare/v0.4.0...v0.4.1) (2018-05-27) # [0.4.0](https://github.com/fengyuanchen/vue-number-input/compare/v0.3.0...v0.4.0) (2018-05-27) ### Features * add `inputtable` prop ([98131c9](https://github.com/fengyuanchen/vue-number-input/commit/98131c9d6890b7a0a4aa4f41b13891a766054f2e)), closes [#2](https://github.com/fengyuanchen/vue-number-input/issues/2) # [0.3.0](https://github.com/fengyuanchen/vue-number-input/compare/v0.2.0...v0.3.0) (2018-03-25) # [0.2.0](https://github.com/fengyuanchen/vue-number-input/compare/v0.1.0...v0.2.0) (2018-03-21) # 0.1.0 (2018-03-13) ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright 2018-present Chen Fengyuan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # vue-number-input [![Coverage Status](https://img.shields.io/codecov/c/github/fengyuanchen/vue-number-input.svg)](https://codecov.io/gh/fengyuanchen/vue-number-input) [![Downloads](https://img.shields.io/npm/dm/@chenfengyuan/vue-number-input.svg)](https://www.npmjs.com/package/@chenfengyuan/vue-number-input) [![Version](https://img.shields.io/npm/v/@chenfengyuan/vue-number-input.svg)](https://www.npmjs.com/package/@chenfengyuan/vue-number-input) [![Gzip Size](https://img.shields.io/bundlephobia/minzip/@chenfengyuan/vue-number-input.svg)](https://unpkg.com/@chenfengyuan/vue-number-input/dist/vue-number-input.js) > Number input component for Vue 3. For Vue 2, check out the [`v1`](https://github.com/fengyuanchen/vue-number-input/tree/v1) branch. - [Docs](src/README.md) - [Demo](https://fengyuanchen.github.io/vue-number-input) ## Main npm package files ```text dist/ ├── vue-number-input.js (UMD, default) ├── vue-number-input.min.js (UMD, compressed) ├── vue-number-input.esm.js (ECMAScript Module) ├── vue-number-input.esm.min.js (ECMAScript Module, compressed) └── vue-number-input.d.ts (TypeScript Declaration File) ``` ## Getting started ### Installation Using npm: ```shell npm install vue@3 @chenfengyuan/vue-number-input@2 ``` Using pnpm: ```shell pnpm add vue@3 @chenfengyuan/vue-number-input@2 ``` Using Yarn: ```shell yarn add vue@3 @chenfengyuan/vue-number-input@2 ``` Using CDN: ```html ``` ### Usage ```js import { createApp } from 'vue'; import VueNumberInput from '@chenfengyuan/vue-number-input'; const app = createApp({}); app.component(VueNumberInput.name, VueNumberInput); ``` ```html ``` ## Browser support Same as Vue 3. ## Versioning Maintained under the [Semantic Versioning guidelines](https://semver.org/). ## License [MIT](https://opensource.org/licenses/MIT) © [Chen Fengyuan](https://chenfengyuan.com/) ================================================ FILE: babel.config.js ================================================ module.exports = { presets: ['@babel/preset-env'], }; ================================================ FILE: commitlint.config.js ================================================ module.exports = { extends: [ '@commitlint/config-conventional', ], }; ================================================ FILE: docs/app.vue ================================================ ================================================ FILE: docs/components/demo-block.vue ================================================ ================================================ FILE: docs/index.html ================================================ vue-number-input
================================================ FILE: docs/index.ts ================================================ import { createApp } from 'vue'; import App from './app.vue'; import DemoBlock from './components/demo-block.vue'; import VueNumberInput from '../src'; const app = createApp(App); app.component(VueNumberInput.name, VueNumberInput); app.component(DemoBlock.name, DemoBlock); app.mount('#app'); ================================================ FILE: jest.config.js ================================================ module.exports = { preset: 'ts-jest', collectCoverage: true, coverageDirectory: 'coverage', coverageReporters: ['html', 'lcov', 'text'], moduleFileExtensions: ['js', 'ts', 'vue'], transform: { '^.+\\.js$': 'babel-jest', '^.+\\.vue$': '@vue/vue3-jest', }, testEnvironment: 'jsdom', testMatch: ['**/tests/*.spec.ts'], }; ================================================ FILE: lint-staged.config.js ================================================ module.exports = { '*.{js,ts,vue}': 'eslint --fix', '*.{css,scss,vue}': 'stylelint --fix', }; ================================================ FILE: package.json ================================================ { "name": "@chenfengyuan/vue-number-input", "version": "2.0.1", "description": "Number input component for Vue 3.", "main": "dist/vue-number-input.js", "module": "dist/vue-number-input.esm.js", "types": "dist/vue-number-input.d.ts", "files": [ "dist" ], "scripts": { "build": "rollup -c --environment BUILD:production", "build:docs": "webpack --env production", "build:types": "move-file dist/vue-number-input.vue.d.ts dist/vue-number-input.d.ts", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0", "clean": "del-cli dist", "lint": "npm run lint:js && npm run lint:css", "lint:css": "stylelint **/*.{css,scss,vue} --fix", "lint:js": "eslint . --ext .js,.ts,.vue --fix", "prepare": "husky install", "release": "npm run clean && npm run lint && npm run build && npm run build:types && npm run build:docs && npm test && npm run changelog", "serve": "webpack serve --hot --open", "start": "npm run serve", "test": "jest", "test:coverage": "cat coverage/lcov.info | codecov" }, "repository": { "type": "git", "url": "https://github.com/fengyuanchen/vue-number-input.git" }, "keywords": [ "number", "input", "vue", "vue3", "vue-component", "front-end", "web" ], "author": "Chen Fengyuan (https://chenfengyuan.com/)", "license": "MIT", "bugs": "https://github.com/fengyuanchen/vue-number-input/issues", "homepage": "https://fengyuanchen.github.io/vue-number-input", "devDependencies": { "@babel/core": "^7.17.2", "@babel/preset-env": "^7.16.11", "@commitlint/cli": "^16.2.1", "@commitlint/config-conventional": "^16.2.1", "@types/jest": "^27.4.0", "@typescript-eslint/eslint-plugin": "^5.11.0", "@typescript-eslint/parser": "^5.11.0", "@vue/test-utils": "^2.0.0-rc.18", "@vue/vue3-jest": "^27.0.0-alpha.4", "babel-jest": "^27.5.1", "babel-loader": "^8.2.3", "change-case": "^4.1.2", "codecov": "^3.8.3", "conventional-changelog-cli": "^2.2.2", "create-banner": "^2.0.0", "css-loader": "^6.6.0", "del-cli": "^4.0.1", "eslint": "^8.9.0", "eslint-config-airbnb-typescript": "^16.1.0", "eslint-plugin-import": "^2.25.4", "eslint-plugin-vue": "^8.4.1", "html-webpack-plugin": "^5.5.0", "husky": "^7.0.4", "jest": "^27.5.1", "lint-staged": "^12.3.3", "markdown-to-vue-loader": "^3.1.3", "mini-css-extract-plugin": "^2.5.3", "move-file-cli": "^3.0.0", "postcss": "^8.4.6", "rollup": "^2.67.2", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.2", "rollup-plugin-vue": "^6.0.0", "sass": "^1.49.7", "sass-loader": "^12.4.0", "style-loader": "^3.3.1", "stylelint": "^14.5.0", "stylelint-config-recommended-scss": "^5.0.2", "stylelint-config-recommended-vue": "^1.3.0", "stylelint-order": "^5.0.0", "ts-jest": "^27.1.3", "ts-loader": "^9.2.6", "tslib": "^2.3.1", "typescript": "^4.5.5", "vue": "^3.2.31", "vue-loader": "^17.0.0", "webpack": "^5.68.0", "webpack-cli": "^4.9.2", "webpack-dev-server": "^4.7.4" }, "peerDependencies": { "vue": "^3.0.0" }, "publishConfig": { "access": "public" } } ================================================ FILE: postcss.config.js ================================================ module.exports = { plugins: { stylelint: { fix: true, }, }, }; ================================================ FILE: rollup.config.js ================================================ import createBanner from 'create-banner'; import postcss from 'rollup-plugin-postcss'; import typescript from 'rollup-plugin-typescript2'; import vue from 'rollup-plugin-vue'; import { pascalCase } from 'change-case'; import { terser } from 'rollup-plugin-terser'; import pkg from './package.json'; const name = pascalCase(pkg.name.replace(/^.+\//, '')); const banner = createBanner({ data: { year: '2018-present', }, template: 'inline', }); export default ['umd', 'esm'].map((format) => ({ input: 'src/vue-number-input.vue', output: ['development', 'production'].map((mode) => { const output = { banner, format, name, file: pkg.main, globals: { vue: 'Vue', }, }; if (format === 'esm') { output.file = pkg.module; } if (mode === 'production') { output.compact = true; output.file = output.file.replace(/(\.js)$/, '.min$1'); output.plugins = [ terser(), ]; } return output; }), external: Object.keys(pkg.peerDependencies), plugins: [ typescript({ tsconfigOverride: { compilerOptions: { declaration: format === 'esm', }, exclude: [ 'src/index.ts', 'docs', 'tests', ], }, }), vue({ preprocessStyles: true, }), postcss({ extensions: ['.css', '.scss'], minimize: true, }), ], })); ================================================ FILE: src/README.md ================================================ # Number Input > Number input with optional controls. ## Basic usage ```html ``` ## Step ```html ``` ## Inline ```html ``` ## Center number ```html ``` ## Sizes ```html ``` ## Without controls ```html ``` ## Rounded ```html ``` ## Not inputtable The input is not inputtable, but still allow to change the value by controls. ```html ``` ## Readonly ```html ``` ## Disabled ```html ``` ## Customize attributes for the input element ```html ``` ## Props | Name | Type | Default | Options | Description | | --- | --- | --- | --- | --- | | attrs | `Object` | - | - | Specify attributes for the built-in input element. | | center | `boolean` | `false` | - | Indicate if the number is center or not. | | controls | `boolean` | `false` | - | Indicate if the controls is visible or not. | | disabled | `boolean` | `false` | - | Indicate if the component is disabled or not. | | inline | `boolean` | `false` | - | Indicate if the input is inline or not. | | inputtable | `boolean` | `true` | - | Indicate if the input element is inputtable or not. | | max | `number` | `Infinity` | - | The maximum value. | | min | `number` | `-Infinity` | - | The minimum value. | | name | `string` | - | - | The name of the input element. | | placeholder | `string` | - | - | The placeholder of the input element. | | readonly | `boolean` | `false` | - | Indicate if the component is read only or not. | | rounded | `boolean` | `false` | - | Indicate if the number is rounded or not. | | size | `string` | - | small, large | The size of the component. | | step | `number` | `1` | - | The increment of each step. | | modelValue | `number` | - | - | The binding value. | ## Events | Name | Parameters | Description | | --- | --- | --- | | update:model-value | `(newValue, oldValue)` | Fire when the value is changed. | > Native events that bubble up from child elements are also available. ```html ``` ================================================ FILE: src/index.ts ================================================ import VueNumberInput from './vue-number-input.vue'; export default VueNumberInput; ================================================ FILE: src/shims.d.ts ================================================ declare module '*.vue' { const content: any; export default content; } declare module '*.md' { const content: any; export default content; } ================================================ FILE: src/vue-number-input.vue ================================================ ================================================ FILE: stylelint.config.js ================================================ module.exports = { extends: 'stylelint-config-recommended-vue/scss', plugins: [ 'stylelint-order', ], rules: { 'no-descending-specificity': null, 'no-empty-source': null, 'order/properties-alphabetical-order': true, }, }; ================================================ FILE: tests/events.spec.ts ================================================ import { mount } from '@vue/test-utils'; import VueNumberInput from '../src'; describe('events', () => { describe('custom', () => { it('should trigger the custom `update:model-value` event', (done) => { const wrapper = mount({ components: { VueNumberInput, }, methods: { onModelValueChange(newValue: number) { expect(newValue).toBe(1); done(); }, }, template: '', }); wrapper.get('.vue-number-input__input').setValue('1'); }); }); describe('native', () => { it('should trigger the native `change` event', (done) => { const wrapper = mount({ components: { VueNumberInput, }, methods: { onChange(event: Event) { expect(event.type).toBe('change'); expect((event.target as HTMLInputElement).value).toBe('1'); done(); }, }, template: '', }); wrapper.get('.vue-number-input__input').setValue('1'); }); it('should trigger the native `input` event', (done) => { const wrapper = mount({ components: { VueNumberInput, }, methods: { onInput(event: Event) { expect(event.type).toBe('input'); expect((event.target as HTMLInputElement).value).toBe('1'); done(); }, }, template: '', }); wrapper.get('.vue-number-input__input').setValue('1'); }); }); }); ================================================ FILE: tests/methods.spec.ts ================================================ import { mount } from '@vue/test-utils'; import VueNumberInput from '../src'; describe('methods', () => { describe('increase', () => { it('should increase the number', () => { const wrapper = mount(VueNumberInput); expect(wrapper.vm.value).toBeNaN(); wrapper.vm.increase(); expect(wrapper.vm.value).toBe(1); }); it('should not increase the number when the current value is equal to the maximum value', () => { const wrapper = mount(VueNumberInput, { props: { max: 0, modelValue: 0, }, }); expect(wrapper.vm.value).toBe(0); wrapper.vm.increase(); expect(wrapper.vm.value).toBe(0); }); }); describe('decrease', () => { it('should decrease the number', () => { const wrapper = mount(VueNumberInput); expect(wrapper.vm.value).toBeNaN(); wrapper.vm.decrease(); expect(wrapper.vm.value).toBe(-1); }); it('should not decrease the number when the current value is equal to the maximum value', () => { const wrapper = mount(VueNumberInput, { props: { min: 0, modelValue: 0, }, }); expect(wrapper.vm.value).toBe(0); wrapper.vm.decrease(); expect(wrapper.vm.value).toBe(0); }); }); describe('setValue', () => { it('should change the value', () => { const wrapper = mount(VueNumberInput); expect(wrapper.vm.value).toBeNaN(); wrapper.vm.setValue(1); expect(wrapper.vm.value).toBe(1); }); it('should transform the given value when it is less than the minimum value', () => { const wrapper = mount(VueNumberInput, { props: { min: 0, }, }); expect(wrapper.vm.value).toBeNaN(); wrapper.vm.setValue(-1); expect(wrapper.vm.value).toBe(0); }); it('should transform the given value when it is greater than the maximum value', () => { const wrapper = mount(VueNumberInput, { props: { max: 0, }, }); expect(wrapper.vm.value).toBeNaN(); wrapper.vm.setValue(1); expect(wrapper.vm.value).toBe(0); }); it('should not transform the given value when the maximum value is less than the minimum value', () => { const wrapper = mount(VueNumberInput, { props: { max: -10, min: 10, }, }); expect(wrapper.vm.value).toBeNaN(); wrapper.vm.setValue(1); expect(wrapper.vm.value).toBe(1); }); }); }); ================================================ FILE: tests/others.spec.ts ================================================ import { mount } from '@vue/test-utils'; import VueNumberInput from '../src'; describe('others', () => { it('should fix the `0.30000000000000004` problem', (done) => { const wrapper = mount({ components: { VueNumberInput, }, data() { return { value: 0.1, }; }, template: '', }); expect(wrapper.vm.value).toBe(0.1); wrapper.get('.vue-number-input__button--plus').trigger('click').then(() => { expect(wrapper.vm.value).toBe(0.3); done(); }); }); it('should update the model value when value changed', (done) => { const wrapper = mount({ components: { VueNumberInput, }, data() { return { value: 0, }; }, template: '', }); expect(wrapper.vm.value).toBe(0); wrapper.get('.vue-number-input__input').setValue(1).then(() => { expect(wrapper.vm.value).toBe(1); done(); }); }); it('should not update the value when paste nothing', (done) => { const wrapper = mount({ components: { VueNumberInput, }, data() { return { value: 0, }; }, template: '', }); expect(wrapper.vm.value).toBe(0); wrapper.get('.vue-number-input__input').trigger('paste').then(() => { expect(wrapper.vm.value).toBe(0); done(); }); }); }); ================================================ FILE: tests/props.spec.ts ================================================ import { mount } from '@vue/test-utils'; import VueNumberInput from '../src'; describe('props', () => { describe('attrs', () => { it('should be undefined by default', () => { const wrapper = mount(VueNumberInput); expect(wrapper.props('attrs')).toBeUndefined(); }); it('should apply the given attributes', () => { const wrapper = mount(VueNumberInput, { props: { attrs: { tabindex: 0, }, }, }); expect(wrapper.props('attrs')).toEqual({ tabindex: 0, }); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).tabIndex).toBe(0); }); }); describe('center', () => { it('should not be center by default', () => { const wrapper = mount(VueNumberInput); expect(wrapper.props('center')).toBe(false); expect(wrapper.classes()).not.toContain('vue-number-input--center'); }); it('should be center', () => { const wrapper = mount(VueNumberInput, { props: { center: true, }, }); expect(wrapper.props('center')).toBe(true); expect(wrapper.classes()).toContain('vue-number-input--center'); }); }); describe('controls', () => { it('should not display the controls by default', () => { const wrapper = mount(VueNumberInput); expect(wrapper.props('controls')).toBe(false); expect(wrapper.classes()).not.toContain('vue-number-input--controls'); expect(wrapper.find('.vue-number-input__button').exists()).toBe(false); }); it('should display the controls', () => { const wrapper = mount(VueNumberInput, { props: { controls: true, }, }); expect(wrapper.props('controls')).toBe(true); expect(wrapper.classes()).toContain('vue-number-input--controls'); expect(wrapper.findAll('.vue-number-input__button').length).toBe(2); }); it('should increase the number when click the plus control', (done) => { const wrapper = mount(VueNumberInput, { props: { controls: true, }, }); expect(wrapper.vm.value).toBeNaN(); wrapper.get('.vue-number-input__button--plus').trigger('click').then(() => { expect(wrapper.vm.value).toBe(1); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).value).toBe('1'); done(); }); }); it('should decrease the number when click the minus control', (done) => { const wrapper = mount(VueNumberInput, { props: { controls: true, }, }); expect(wrapper.vm.value).toBeNaN(); wrapper.get('.vue-number-input__button--minus').trigger('click').then(() => { expect(wrapper.vm.value).toBe(-1); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).value).toBe('-1'); done(); }); }); }); describe('disabled', () => { it('should not be disabled by default', () => { const wrapper = mount(VueNumberInput, { props: { controls: true, }, }); expect(wrapper.props('disabled')).toBe(false); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).disabled).toBe(false); expect((wrapper.get('.vue-number-input__button--plus').element as HTMLButtonElement).disabled).toBe(false); expect((wrapper.get('.vue-number-input__button--minus').element as HTMLButtonElement).disabled).toBe(false); }); it('should by disabled', () => { const wrapper = mount(VueNumberInput, { props: { controls: true, disabled: true, }, }); expect(wrapper.props('disabled')).toBe(true); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).disabled).toBe(true); expect((wrapper.get('.vue-number-input__button--plus').element as HTMLButtonElement).disabled).toBe(true); expect((wrapper.get('.vue-number-input__button--minus').element as HTMLButtonElement).disabled).toBe(true); }); }); describe('inline', () => { it('should not be inline by default', () => { const wrapper = mount(VueNumberInput); expect(wrapper.props('inline')).toBe(false); expect(wrapper.classes()).not.toContain('vue-number-input--inline'); }); it('should be inline', () => { const wrapper = mount(VueNumberInput, { props: { inline: true, }, }); expect(wrapper.props('inline')).toBe(true); expect(wrapper.classes()).toContain('vue-number-input--inline'); }); }); describe('inputtable', () => { it('should be inputtable by default', () => { const wrapper = mount(VueNumberInput, { props: { controls: true, }, }); expect(wrapper.props('inputtable')).toBe(true); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).readOnly).toBe(false); expect((wrapper.get('.vue-number-input__button--plus').element as HTMLButtonElement).disabled).toBe(false); expect((wrapper.get('.vue-number-input__button--minus').element as HTMLButtonElement).disabled).toBe(false); }); it('should not be inputtable', () => { const wrapper = mount(VueNumberInput, { props: { controls: true, inputtable: false, }, }); expect(wrapper.props('inputtable')).toBe(false); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).readOnly).toBe(true); expect((wrapper.get('.vue-number-input__button--plus').element as HTMLButtonElement).disabled).toBe(false); expect((wrapper.get('.vue-number-input__button--minus').element as HTMLButtonElement).disabled).toBe(false); }); }); describe('max', () => { it('should be `Infinity` by default', () => { const wrapper = mount(VueNumberInput); expect(wrapper.props('max')).toBe(Infinity); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).max).toBe('Infinity'); }); it('should be equal to the given value', () => { const wrapper = mount(VueNumberInput, { props: { max: 10, }, }); expect(wrapper.props('max')).toBe(10); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).max).toBe('10'); }); it('should not be greater than the given maximum value', () => { const wrapper = mount(VueNumberInput, { props: { modelValue: 11, max: 10, }, }); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).value).toBe('10'); }); it('should fix the out of range value', () => { const wrapper = mount(VueNumberInput, { props: { max: 10, }, }); wrapper.get('.vue-number-input__input').setValue('11').then(() => { expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).value).toBe('10'); }); }); }); describe('min', () => { it('should be `-Infinity` by default', () => { const wrapper = mount(VueNumberInput); expect(wrapper.props('min')).toBe(-Infinity); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).min).toBe('-Infinity'); }); it('should be equal to the given value', () => { const wrapper = mount(VueNumberInput, { props: { min: -10, }, }); expect(wrapper.props('min')).toBe(-10); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).min).toBe('-10'); }); it('should not be less than the given minimum value', () => { const wrapper = mount(VueNumberInput, { props: { modelValue: -11, min: -10, }, }); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).value).toBe('-10'); }); it('should fix the out of range value', () => { const wrapper = mount(VueNumberInput, { props: { min: -10, }, }); wrapper.get('.vue-number-input__input').setValue('-11').then(() => { expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).value).toBe('-10'); }); }); }); describe('name', () => { it('should be undefined by default', () => { const wrapper = mount(VueNumberInput); expect(wrapper.props('name')).toBeUndefined(); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).name).toBe(''); }); it('should be equal to the given value', () => { const wrapper = mount(VueNumberInput, { props: { name: 'digit', }, }); expect(wrapper.props('name')).toBe('digit'); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).name).toBe('digit'); }); }); describe('placeholder', () => { it('should be undefined by default', () => { const wrapper = mount(VueNumberInput); expect(wrapper.props('placeholder')).toBeUndefined(); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).placeholder).toBe(''); }); it('should be equal to the given value', () => { const wrapper = mount(VueNumberInput, { props: { placeholder: 'Number input', }, }); expect(wrapper.props('placeholder')).toBe('Number input'); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).placeholder).toBe('Number input'); }); }); describe('readonly', () => { it('should not be read-only by default', () => { const wrapper = mount(VueNumberInput, { props: { controls: true, }, }); expect(wrapper.props('readonly')).toBe(false); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).readOnly).toBe(false); expect((wrapper.get('.vue-number-input__button--plus').element as HTMLButtonElement).disabled).toBe(false); expect((wrapper.get('.vue-number-input__button--minus').element as HTMLButtonElement).disabled).toBe(false); }); it('should be read-only', () => { const wrapper = mount(VueNumberInput, { props: { controls: true, readonly: true, }, }); expect(wrapper.props('readonly')).toBe(true); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).readOnly).toBe(true); expect((wrapper.get('.vue-number-input__button--plus').element as HTMLButtonElement).disabled).toBe(true); expect((wrapper.get('.vue-number-input__button--minus').element as HTMLButtonElement).disabled).toBe(true); }); }); describe('rounded', () => { it('should not round the number by default', () => { const wrapper = mount(VueNumberInput, { props: { modelValue: 1.5, }, }); expect(wrapper.props('rounded')).toBe(false); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).value).toBe('1.5'); }); it('should round the number', () => { const wrapper = mount(VueNumberInput, { props: { modelValue: 1.5, rounded: true, }, }); expect(wrapper.props('rounded')).toBe(true); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).value).toBe('2'); }); }); describe('size', () => { it('should be small size', () => { const wrapper = mount(VueNumberInput, { props: { size: 'small', }, }); expect(wrapper.props('size')).toBe('small'); expect(wrapper.classes()).toContain('vue-number-input--small'); }); it('should be large size', () => { const wrapper = mount(VueNumberInput, { props: { size: 'large', }, }); expect(wrapper.props('size')).toBe('large'); expect(wrapper.classes()).toContain('vue-number-input--large'); }); }); describe('step', () => { it('should be `1` by default', (done) => { const wrapper = mount(VueNumberInput, { props: { controls: true, }, }); expect(wrapper.props('step')).toBe(1); wrapper.get('.vue-number-input__button--plus').trigger('click').then(() => { expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).step).toBe('1'); done(); }); }); it('should match the given value', (done) => { const wrapper = mount(VueNumberInput, { props: { controls: true, step: 2, }, }); expect(wrapper.props('step')).toBe(2); wrapper.get('.vue-number-input__button--plus').trigger('click').then(() => { expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).step).toBe('2'); done(); }); }); }); describe('modelValue', () => { it('should be `NaN` by default', () => { const wrapper = mount(VueNumberInput); expect(wrapper.props('modelValue')).toBeNaN(); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).value).toBe(''); }); it('should be equal to the given value', () => { const wrapper = mount(VueNumberInput, { props: { modelValue: 10, }, }); expect(wrapper.props('modelValue')).toBe(10); expect((wrapper.get('.vue-number-input__input').element as HTMLInputElement).value).toBe('10'); }); }); }); ================================================ FILE: tsconfig.eslint.json ================================================ { "extends": "./tsconfig", "include": [ "*.js", ".*.js", "docs/**/*", "src/**/*", "tests/**/*" ] } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "allowSyntheticDefaultImports": true, "moduleResolution": "node", "resolveJsonModule": true, "strict": true, "target": "esnext" }, "include": [ "src/**/*", "docs/**/*", "tests/**/*" ] } ================================================ FILE: webpack.config.js ================================================ const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const VueLoaderPlugin = require('vue-loader/dist/plugin').default; const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = (env) => ({ mode: env.production ? 'production' : 'development', entry: './docs', output: { path: path.resolve(__dirname, './docs/dist'), }, module: { rules: [ { test: /\.js$/, use: 'babel-loader', }, { test: /\.ts$/, loader: 'ts-loader', options: { appendTsSuffixTo: [/\.vue$/], }, }, { test: /\.vue$/, loader: 'vue-loader', }, { test: /\.scss$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader', ], }, { test: /\.md$/, use: [ 'vue-loader', { loader: 'markdown-to-vue-loader', options: { componentWrapper: '', tableClass: 'table', tableWrapper: '
', }, }, ], }, ], }, plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', template: './docs/index.html', }), new MiniCssExtractPlugin(), new VueLoaderPlugin(), new webpack.DefinePlugin({ __VUE_OPTIONS_API__: true, __VUE_PROD_DEVTOOLS__: false, }), ], externals: env.production ? { vue: 'Vue', } : {}, resolve: { alias: { vue$: 'vue/dist/vue.esm-bundler', }, extensions: ['.js', '.json', '.ts', '.d.ts', '.vue'], }, });