├── .env.testing ├── .env.production ├── .env.development ├── .browserslistrc ├── tests └── unit │ ├── setup.js │ ├── coverage │ └── lcov-report │ │ ├── favicon.png │ │ ├── sort-arrow-sprite.png │ │ ├── prettify.css │ │ ├── block-navigation.js │ │ ├── src │ │ ├── shared │ │ │ ├── utils.ts.html │ │ │ ├── enum.ts.html │ │ │ └── index.html │ │ ├── main.ts.html │ │ ├── directives │ │ │ └── index.html │ │ ├── components │ │ │ └── datepicker │ │ │ │ ├── locale │ │ │ │ └── index.html │ │ │ │ ├── utils │ │ │ │ └── index.html │ │ │ │ └── index.html │ │ └── index.html │ │ ├── wrapper │ │ └── index.html │ │ ├── prismcomponent │ │ └── index.html │ │ ├── iconview │ │ └── index.html │ │ ├── datepicker │ │ ├── locale │ │ │ └── index.html │ │ ├── utils │ │ │ └── index.html │ │ └── index.html │ │ ├── sorter.js │ │ ├── index.html │ │ └── base.css │ ├── .eslintrc │ ├── jest.conf.js │ └── specs │ ├── PickerDay │ ├── initialDom.spec.js │ ├── changeMonths.spec.js │ ├── mondayFirst.spec.js │ ├── pickerDay.spec.js │ ├── disabledDates.spec.js │ └── highlightedDates.spec.js │ ├── Datepicker │ ├── inline.spec.js │ ├── openDate.spec.js │ └── restrictedViews.spec.js │ ├── PickerMonth │ ├── disabledMonths.spec.js │ └── pickerMonth.spec.js │ ├── PickerYear │ ├── pickerYear.spec.js │ └── disabledYears.spec.js │ ├── DateInput │ ├── typedDates.spec.js │ └── DateInput.spec.js │ └── DateUtils.spec.js ├── .gitignore ├── .husky ├── pre-commit └── commit-msg ├── .editorconfig ├── src ├── vite-env.d.ts ├── main.ts ├── assets │ ├── styles │ │ └── css │ │ │ ├── _settings.css │ │ │ └── app.css │ ├── calendar.svg │ └── github.svg ├── components │ ├── examples │ │ ├── InlineView.vue │ │ ├── CalendarIcon.vue │ │ ├── YearView.vue │ │ ├── HighlightedView.vue │ │ ├── DayView.vue │ │ ├── StringView.vue │ │ ├── MonthView.vue │ │ ├── DefaultValue.vue │ │ ├── Typeable.vue │ │ ├── Language.vue │ │ ├── ProgrammaticAccess.vue │ │ ├── VModel.vue │ │ ├── Disabled.vue │ │ └── Slotview.vue │ ├── prismcomponent │ │ └── index.ts │ ├── wrapper │ │ └── Wrapper.vue │ ├── iconview │ │ └── IconView.vue │ └── datepicker │ │ └── datepicker.css ├── style.css ├── directives │ └── click-outside.ts └── App.vue ├── .eslintignore ├── .prettierrc.json ├── tsconfig.node.json ├── index.html ├── postcss.config.cjs ├── vite.config.project.ts ├── .releaserc ├── tsconfig.json ├── vite.config.lib.ts ├── LICENSE ├── commitlint.config.cjs ├── public └── vite.svg ├── CHANGELOG.md ├── package.json └── .eslintrc.cjs /.env.testing: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | 3 | 4 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /tests/unit/setup.js: -------------------------------------------------------------------------------- 1 | // import Vue from 'vue' 2 | 3 | // Vue.config.productionTip = false 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .vscode 4 | dist/ 5 | testumdbuild/ 6 | demo/ 7 | example/*.js -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run precommit -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit "$1" -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shubhadip/vuejs3-datepicker/HEAD/tests/unit/coverage/lcov-report/favicon.png -------------------------------------------------------------------------------- /tests/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | }, 5 | "globals": { 6 | "expect": true, 7 | "sinon": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/sort-arrow-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shubhadip/vuejs3-datepicker/HEAD/tests/unit/coverage/lcov-report/sort-arrow-sprite.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | end_of_line = lf 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | max_line_length = 100 8 | format_on_save = true -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | declare module '*.vue' { 5 | import type { DefineComponent } from 'vue'; 6 | const component: DefineComponent<{}, {}, any>; 7 | export default component; 8 | } -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | // import { createApp } from 'vue' 2 | // import './style.css' 3 | // import App from './App.vue' 4 | 5 | // createApp(App).mount('#app') 6 | 7 | import { createApp } from 'vue' 8 | import './style.css' 9 | import App from './App.vue' 10 | 11 | createApp(App).mount('#app') 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | src/shims-tsx.d.ts 4 | src/shims-vue.d.ts 5 | *.config.js 6 | src/main.ts 7 | test/ 8 | dist/*.hot-update.json 9 | dist/index.html 10 | dist/webpack-stats.json 11 | tests/ 12 | vite.config.lib.ts 13 | vite.config.project.ts 14 | postcss.config.cjs 15 | *.d.ts -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "tabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "quoteProps": "as-needed", 8 | "trailingComma": "es5", 9 | "bracketSpacing": true, 10 | "jsxBracketSameLine": false, 11 | "arrowParens": "always" 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.lib.ts","vite.config.project.ts",] 10 | } 11 | -------------------------------------------------------------------------------- /src/assets/styles/css/_settings.css: -------------------------------------------------------------------------------- 1 | $important: !important; 2 | 3 | /* Color system */ 4 | $white: #ffffff; 5 | $black: #2f2f2f; 6 | $green: #40b983; 7 | $darkGreen: #2f9668; 8 | $darkestGreen: #217751; 9 | $blue: #00b9f5; 10 | $dark-blue: #012b72; 11 | $grey-600: #6c757d; 12 | $grey-800: #485e7f; 13 | $hovered: #f8f8f8; 14 | $highlight: $green; 15 | 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Vue + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | plugins: [ 5 | require('postcss-import')({}), 6 | require('postcss-simple-vars')({}), 7 | require('postcss-nested')({}), 8 | require('autoprefixer')({ 9 | overrideBrowserslist: '> 1%, IE 6, Explorer >= 10, Safari >= 7', 10 | }), 11 | require('cssnano')({ 12 | zindex: false, 13 | }), 14 | ], 15 | }; 16 | -------------------------------------------------------------------------------- /vite.config.project.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import { fileURLToPath, URL } from 'node:url' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | base: './', 8 | plugins: [vue()], 9 | build: { 10 | outDir: "dist", 11 | emptyOutDir: true, 12 | }, 13 | resolve: { 14 | alias: { 15 | '@': fileURLToPath(new URL('./src', import.meta.url)) 16 | } 17 | }, 18 | }) 19 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /tests/unit/jest.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel', 5 | rootDir: path.resolve(__dirname, '../../'), 6 | moduleFileExtensions: ['ts', 'tsx', 'js', 'json', 'vue'], 7 | moduleNameMapper: { 8 | '^@/(.*)$': '/src/$1', 9 | }, 10 | transform: { 11 | '^.+\\.js$': 'babel-jest', 12 | '.*\\.(vue)$': 'vue-jest', 13 | '^.+\\ts$': 'ts-jest', 14 | }, 15 | setupFiles: ['/tests/unit/setup'], 16 | coverageDirectory: '/tests/unit/coverage', 17 | collectCoverageFrom: ['src/components/datepicker/**/*.{ts,js,vue}', '!src/locale/translations/**/*.ts','!src/components/iconview/**/*.vue'], 18 | }; 19 | -------------------------------------------------------------------------------- /tests/unit/specs/PickerDay/initialDom.spec.js: -------------------------------------------------------------------------------- 1 | import PickerDay from '@/components/datepicker/PickerDay.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | let en; 5 | 6 | describe('PickerDay: DOM', () => { 7 | beforeEach(()=>{ 8 | en = Langlist.data['en']; 9 | }) 10 | let wrapper 11 | beforeEach(() => { 12 | wrapper = mount(PickerDay, { 13 | props: { 14 | allowedToShowView: () => true, 15 | translation: en, 16 | pageDate: new Date(2018, 1, 1), 17 | selectedDate: new Date(2018, 2, 24) 18 | } 19 | }) 20 | }) 21 | 22 | it('should render correct contents', () => { 23 | expect(wrapper.findAll('.vuejs3-datepicker__calendar')).toHaveLength(1) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main", 4 | "master" 5 | ], 6 | "plugins": [ 7 | "@semantic-release/commit-analyzer", 8 | "@semantic-release/release-notes-generator", 9 | [ 10 | "@semantic-release/changelog", 11 | { 12 | "changelogFile": "CHANGELOG.md" 13 | } 14 | ], 15 | [ 16 | "@semantic-release/npm", 17 | { 18 | "npmPublish": true, 19 | "pkgRoot": "./" 20 | } 21 | ], 22 | [ 23 | "@semantic-release/github" 24 | ], 25 | [ 26 | "@semantic-release/git", 27 | { 28 | "assets": [ 29 | "/build", 30 | "CHANGELOG.md", 31 | "package.json" 32 | ], 33 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 34 | } 35 | ] 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "preserve", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true, 22 | "baseUrl": ".", 23 | "paths": { 24 | "@/*": ["src/*"] 25 | }, 26 | }, 27 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 28 | "references": [{ "path": "./tsconfig.node.json" }] 29 | } 30 | -------------------------------------------------------------------------------- /tests/unit/specs/Datepicker/inline.spec.js: -------------------------------------------------------------------------------- 1 | import Datepicker from '@/components/datepicker/DatePickerComponent.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | let en; 6 | describe('Datepicker.vue inline', () => { 7 | beforeEach(()=>{ 8 | en = Langlist.data['en']; 9 | }) 10 | let wrapper 11 | beforeEach(() => { 12 | wrapper = mount(Datepicker, { 13 | propsData: { 14 | inline: true 15 | } 16 | }) 17 | }) 18 | 19 | it('should not showCalendar as already open', () => { 20 | expect(wrapper.vm.showCalendar()).toBeFalsy() 21 | expect(wrapper.vm.isInline).toEqual(true) 22 | }) 23 | 24 | it('should not close the calendar when date is selected', () => { 25 | const date = new Date() 26 | wrapper.vm.selectDate({timestamp: date.getTime()}) 27 | expect(wrapper.vm.isOpen).toEqual(true) 28 | document.body.click() 29 | expect(wrapper.vm.isOpen).toEqual(true) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /vite.config.lib.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import { fileURLToPath, URL } from 'node:url' 4 | import { resolve } from 'path' 5 | import libCss from 'vite-plugin-libcss'; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | plugins: [vue(), libCss()], 10 | build: { 11 | minify: true, 12 | outDir: 'build', 13 | emptyOutDir: true, 14 | cssCodeSplit: true, 15 | lib: { 16 | entry: resolve(__dirname, './src/components/datepicker/DatePickerComponent.vue'), 17 | name:'vuejs3-datepicker' 18 | }, 19 | rollupOptions: { 20 | external: ['vue'], 21 | output: { 22 | // Provide global variables to use in the UMD build 23 | // for externalized deps 24 | globals: { 25 | vue: 'Vue', 26 | }, 27 | }, 28 | } 29 | }, 30 | resolve: { 31 | alias: { 32 | '@': fileURLToPath(new URL('./src', import.meta.url)) 33 | } 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /src/assets/calendar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/examples/InlineView.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 46 | -------------------------------------------------------------------------------- /tests/unit/specs/PickerDay/changeMonths.spec.js: -------------------------------------------------------------------------------- 1 | import PickerDay from '@/components/datepicker/PickerDay.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | let en; 6 | describe('PickerDay: changing months', () => { 7 | beforeEach(()=>{ 8 | en = Langlist.data['en']; 9 | }) 10 | 11 | let wrapper 12 | beforeEach(() => { 13 | wrapper = mount(PickerDay, { 14 | props: { 15 | translation: en, 16 | allowedToShowView: () => true, 17 | selectedDate: new Date(2018, 2, 24), 18 | pageDate: new Date(2018, 1, 1) 19 | } 20 | }) 21 | }) 22 | 23 | it('can set the next month', () => { 24 | wrapper.vm.nextMonth() 25 | expect(wrapper.emitted()['changed-month']).toBeTruthy() 26 | expect(wrapper.emitted()['changed-month'][0][0].getMonth()).toEqual(2) 27 | }) 28 | 29 | it('can set the previous month', () => { 30 | wrapper.vm.previousMonth() 31 | expect(wrapper.emitted()['changed-month']).toBeTruthy() 32 | expect(wrapper.emitted()['changed-month'][0][0].getMonth()).toEqual(0) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 shubhadip 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /commitlint.config.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | * Definition Description 4 | 5 | - feat A new feature 6 | 7 | - fix A bug fix 8 | 9 | - docs Documentation only changes 10 | 11 | - style Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 12 | 13 | - refactor A code change that neither fixes a bug nor adds a feature 14 | 15 | - perf A code change that improves performance 16 | 17 | - test Adding missing or correcting existing tests 18 | 19 | - chore Changes to the build process or auxiliary tools and libraries such as documentation generation 20 | 21 | 22 | * X.Y.Z => Release versioning format 23 | 24 | - fix(pencil): loader element not loading fix => Will bump up Z 25 | 26 | - feat(pencil): new datepicker component added => Will bump up Y 27 | 28 | - perf(pencil): vue3 released => Will bump the X 29 | 30 | - BREAKING CHANGE: vue3 released => Will bump the Y 31 | 32 | - Refer for the format of commit messages: https://github.com/conventional-changelog/commitlint/#what-is-commitlint 33 | 34 | */ 35 | 36 | module.exports = { 37 | 38 | extends: ['@commitlint/config-conventional'], 39 | ignores: [(message) => message.includes('WIP')], 40 | rules: { 41 | 'body-max-line-length': [0, 'always', Infinity], // added this due to semantic release bug 42 | }, 43 | 44 | }; -------------------------------------------------------------------------------- /tests/unit/specs/PickerDay/mondayFirst.spec.js: -------------------------------------------------------------------------------- 1 | import PickerDay from '@/components/datepicker/PickerDay.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | let en; 6 | 7 | describe('PickerDay: Datepicker with monday as first day of week', () => { 8 | beforeEach(()=>{ 9 | en = Langlist.data['en']; 10 | }) 11 | 12 | let wrapper 13 | beforeEach(() => { 14 | wrapper = mount(PickerDay, { 15 | props: { 16 | mondayFirst: true, 17 | translation: en, 18 | allowedToShowView: () => true, 19 | pageDate: new Date(2018, 1, 1) 20 | } 21 | }) 22 | }) 23 | 24 | it('should return Monday as a first day of week', () => { 25 | expect(wrapper.vm.daysOfWeek[0]).toEqual('Mon') 26 | }) 27 | 28 | it('should return Sunday as a seventh day of week', () => { 29 | expect(wrapper.vm.daysOfWeek[6]).toEqual('Sun') 30 | }) 31 | 32 | it('should have 6 blankDays when month starts from Sunday', async () => { 33 | await wrapper.setProps({ 34 | pageDate: new Date(2018, 3, 1) 35 | }) 36 | expect(wrapper.vm.blankDays).toEqual(6) 37 | }) 38 | 39 | it('should have no blankDays when month starts from Monday', async () => { 40 | await wrapper.setProps({ 41 | pageDate: new Date(2018, 9, 1) 42 | }) 43 | expect(wrapper.vm.blankDays).toEqual(0) 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /src/components/examples/CalendarIcon.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 52 | -------------------------------------------------------------------------------- /src/components/examples/YearView.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 52 | -------------------------------------------------------------------------------- /tests/unit/specs/PickerDay/pickerDay.spec.js: -------------------------------------------------------------------------------- 1 | import PickerDay from '@/components/datepicker/PickerDay.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | let en; 6 | 7 | describe('PickerDay: DOM', () => { 8 | 9 | beforeEach(()=>{ 10 | en = Langlist.data['en']; 11 | }) 12 | 13 | let wrapper 14 | beforeEach(() => { 15 | wrapper = mount(PickerDay, { 16 | props: { 17 | allowedToShowView: () => true, 18 | translation: en, 19 | pageDate: new Date(2018, 1, 1), 20 | selectedDate: new Date(2018, 2, 24) 21 | } 22 | }) 23 | }) 24 | 25 | it('knows the selected date', async () => { 26 | const newDate = new Date(2016, 9, 15) 27 | await wrapper.setProps({ 28 | selectedDate: newDate 29 | }) 30 | expect(wrapper.vm.isSelectedDate(newDate)).toEqual(true) 31 | expect(wrapper.vm.isSelectedDate(new Date(2017, 1, 1))).toEqual(false) 32 | }) 33 | 34 | it('emits an event when selected', () => { 35 | wrapper.vm.selectDate({isDisabled: false}) 36 | expect(wrapper.emitted()['select-date']).toBeTruthy() 37 | }) 38 | 39 | it('emits show year calendar event when clicked on the year', () => { 40 | const yearBtn = wrapper.find('.day__month_btn') 41 | yearBtn.trigger('click') 42 | expect(wrapper.emitted()['show-month-calendar']).toBeTruthy() 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /src/components/prismcomponent/index.ts: -------------------------------------------------------------------------------- 1 | import * as Vue from 'vue'; 2 | import Prism from 'prismjs'; 3 | import { Slots, VNode } from 'vue'; 4 | 5 | declare type Data = Record; 6 | 7 | export default Vue.defineComponent({ 8 | functional: true, 9 | props: { 10 | code: { 11 | type: String, 12 | }, 13 | inline: { 14 | type: Boolean, 15 | default: false, 16 | }, 17 | language: { 18 | type: String, 19 | default: 'markup', 20 | }, 21 | }, 22 | setup(props, { slots, attrs }: { slots: Slots; attrs: Data }) { 23 | const { h } = Vue; 24 | const slotsData = (slots && slots.default && slots.default()) || []; 25 | const code = props.code || (slotsData.length > 0 ? slotsData[0].children : ''); 26 | const { inline, language } = props; 27 | const className = `language-${language}`; 28 | 29 | if (inline) { 30 | return (): VNode => 31 | h('code', { 32 | ...attrs, 33 | class: [attrs.class, className], 34 | innerHTML: Prism.highlight(code as string, Prism.languages.javascript, language), 35 | }); 36 | } 37 | 38 | const d = Prism.highlight(code as string, Prism.languages.javascript, language); 39 | return (): VNode => 40 | h('pre', { ...attrs, class: [attrs.class, className] }, [ 41 | h('code', { 42 | class: className, 43 | innerHTML: d, 44 | }), 45 | ]); 46 | }, 47 | }); 48 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.1.3](https://github.com/shubhadip/vuejs3-datepicker/compare/v1.1.2...v1.1.3) (2024-05-14) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * update hook name for directives ([cf25828](https://github.com/shubhadip/vuejs3-datepicker/commit/cf2582815847d76f88c54324eb68ee64624c92eb)) 7 | * update hook name for directives ([c2af407](https://github.com/shubhadip/vuejs3-datepicker/commit/c2af4075562b9e52572cff3c74a666cc54a941e9)) 8 | 9 | ## [1.1.2](https://github.com/shubhadip/vuejs3-datepicker/compare/v1.1.1...v1.1.2) (2023-11-26) 10 | 11 | 12 | ### Bug Fixes 13 | 14 | * app level css fix ([49cc5ae](https://github.com/shubhadip/vuejs3-datepicker/commit/49cc5ae7ad08fedd6631ab7073c8747f389643fe)) 15 | 16 | ## [1.1.1](https://github.com/shubhadip/vuejs3-datepicker/compare/v1.1.0...v1.1.1) (2023-11-04) 17 | 18 | 19 | ### Bug Fixes 20 | 21 | * added slot for top header section ([a62c029](https://github.com/shubhadip/vuejs3-datepicker/commit/a62c029366d7be028868e72ca5fb8a7f9311ca78)) 22 | * semantic config changes ([ab1cbb5](https://github.com/shubhadip/vuejs3-datepicker/commit/ab1cbb5d9000a495c6300e471e24ef4af820b05a)) 23 | 24 | # [1.1.0](https://github.com/shubhadip/vuejs3-datepicker/compare/v1.0.19...v1.1.0) (2023-11-04) 25 | 26 | 27 | ### Features 28 | 29 | * Add Arabic-Tunisia language ([cdc0b14](https://github.com/shubhadip/vuejs3-datepicker/commit/cdc0b1440a8a69f7af1a7d14f3a0801b99a3d07e)) 30 | * vite migration ([0b3541a](https://github.com/shubhadip/vuejs3-datepicker/commit/0b3541a7fd8992bc1c822b2e8ebc5c9d6009179d)) 31 | -------------------------------------------------------------------------------- /src/assets/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/examples/HighlightedView.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 60 | -------------------------------------------------------------------------------- /tests/unit/specs/PickerMonth/disabledMonths.spec.js: -------------------------------------------------------------------------------- 1 | import PickerMonth from '@/components/datepicker/PickerMonth.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | let en; 6 | 7 | 8 | describe('PickerMonth', () => { 9 | let wrapper 10 | 11 | beforeEach(()=>{ 12 | en = Langlist.data['en']; 13 | }) 14 | 15 | beforeEach(() => { 16 | wrapper = mount(PickerMonth, { 17 | propsData: { 18 | allowedToShowView: () => true, 19 | translation: en, 20 | pageDate: new Date(2018, 3, 1), 21 | selectedDate: new Date(2018, 3, 19), 22 | disabledDates: { 23 | to: new Date(2018, 2, 14), 24 | from: new Date(2018, 4, 15) 25 | } 26 | } 27 | }) 28 | }) 29 | 30 | it('cant select a disabled month', () => { 31 | const month = {isDisabled: true} 32 | expect(wrapper.vm.selectMonth(month)).toBeFalsy() 33 | }) 34 | 35 | it('can accept a customPredictor to check if the month is disabled', async () => { 36 | await wrapper.setProps({ 37 | disabledDates: { 38 | customPredictor (date) { 39 | if (date.getMonth() % 4 === 0) { 40 | return true 41 | } 42 | } 43 | } 44 | }) 45 | expect(wrapper.vm.isDisabledMonth(new Date(2018, 4, 29))).toEqual(true) 46 | expect(wrapper.vm.isDisabledMonth(new Date(2018, 9, 28))).toEqual(false) 47 | expect(wrapper.vm.isDisabledMonth(new Date(2018, 8, 24))).toEqual(true) 48 | expect(wrapper.vm.isDisabledMonth(new Date(2018, 2, 11))).toEqual(false) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /src/components/examples/DayView.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 59 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | button:focus, 54 | button:focus-visible { 55 | outline: 4px auto -webkit-focus-ring-color; 56 | } 57 | 58 | .card { 59 | padding: 2em; 60 | } 61 | 62 | #app { 63 | max-width: 1280px; 64 | margin: 0 auto; 65 | padding: 2rem; 66 | } 67 | 68 | @media (prefers-color-scheme: light) { 69 | :root { 70 | color: #213547; 71 | background-color: #ffffff; 72 | } 73 | a:hover { 74 | color: #747bff; 75 | } 76 | button { 77 | background-color: #f9f9f9; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/components/wrapper/Wrapper.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 55 | 76 | -------------------------------------------------------------------------------- /src/components/examples/StringView.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 63 | -------------------------------------------------------------------------------- /src/components/examples/MonthView.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 66 | -------------------------------------------------------------------------------- /tests/unit/specs/Datepicker/openDate.spec.js: -------------------------------------------------------------------------------- 1 | import Datepicker from '@/components/datepicker/DatePickerComponent.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | let en; 6 | 7 | describe('Datepicker with open date', () => { 8 | beforeEach(()=>{ 9 | en = Langlist.data['en']; 10 | }) 11 | 12 | const openDate = new Date() 13 | let wrapper 14 | beforeEach(() => { 15 | wrapper = mount(Datepicker, { 16 | props: { 17 | openDate: openDate 18 | } 19 | }) 20 | }) 21 | 22 | it('should be set to October', () => { 23 | expect(wrapper.vm.pageDate.getMonth()).toEqual(new Date().getMonth()) 24 | expect(wrapper.vm.pageDate.getFullYear()).toEqual(new Date().getFullYear()) 25 | }) 26 | 27 | it('should set pageTimestamp to be first day of open date\'s month', () => { 28 | const date = new Date(wrapper.vm.pageTimestamp) 29 | expect(wrapper.vm.openDate.getTime()).toEqual(openDate.getTime()) 30 | wrapper.vm.setPageDate() 31 | expect(date.getFullYear()).toEqual(openDate.getFullYear()) 32 | expect(date.getMonth()).toEqual(openDate.getMonth()) 33 | expect(date.getDate()).toEqual(1) 34 | }) 35 | 36 | it('should open with selected date if one is set', () => { 37 | const newDate = new Date(2018, 10, 9) 38 | wrapper.vm.selectDate({timestamp: newDate.getTime()}) 39 | expect(wrapper.vm.pageDate.getMonth()).toEqual(10) 40 | expect(wrapper.vm.pageDate.getFullYear()).toEqual(2018) 41 | }) 42 | 43 | it('should show today\'s date if no open date is set', () => { 44 | wrapper = mount(Datepicker) 45 | const today = new Date() 46 | expect(wrapper.vm.pageDate.getMonth()).toEqual(today.getMonth()) 47 | expect(wrapper.vm.pageDate.getFullYear()).toEqual(today.getFullYear()) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /tests/unit/specs/PickerMonth/pickerMonth.spec.js: -------------------------------------------------------------------------------- 1 | import PickerMonth from '@/components/datepicker/PickerMonth.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | 6 | let en; 7 | 8 | describe('PickerMonth', () => { 9 | 10 | beforeEach(()=>{ 11 | en = Langlist.data['en']; 12 | }) 13 | 14 | let wrapper 15 | 16 | beforeEach(() => { 17 | wrapper = mount(PickerMonth, { 18 | propsData: { 19 | allowedToShowView: () => true, 20 | translation: en, 21 | pageDate: new Date(2018, 1, 1), 22 | selectedDate: new Date(2018, 2, 24) 23 | } 24 | }) 25 | }) 26 | 27 | it('knows the selected month', async () => { 28 | const newDate = new Date(2016, 9, 15) 29 | await wrapper.setProps({ 30 | selectedDate: newDate 31 | }) 32 | expect(wrapper.vm.isSelectedMonth(newDate)).toEqual(true) 33 | expect(wrapper.vm.isSelectedMonth(new Date(2017, 1, 1))).toEqual(false) 34 | }) 35 | 36 | it('can set the next year', () => { 37 | wrapper.vm.nextYear() 38 | expect(wrapper.emitted()['changed-year'][0][0].getFullYear()).toEqual(2019) 39 | }) 40 | 41 | it('can set the previous year', () => { 42 | wrapper.vm.previousYear() 43 | expect(wrapper.emitted()['changed-year'][0][0].getFullYear()).toEqual(2017) 44 | }) 45 | 46 | it('emits date on selection', () => { 47 | const time = new Date().getTime() 48 | wrapper.vm.selectMonth({timestamp: time}) 49 | expect(wrapper.emitted()['select-month']).toBeTruthy() 50 | expect(wrapper.emitted()['select-month'][0][0].timestamp).toEqual(time) 51 | }) 52 | 53 | it('emits show year calendar event when clicked on the year', () => { 54 | const yearBtn = wrapper.find('.month__year_btn') 55 | yearBtn.trigger('click') 56 | expect(wrapper.emitted()['show-year-calendar']).toBeTruthy() 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /src/components/iconview/IconView.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 51 | 52 | -------------------------------------------------------------------------------- /tests/unit/specs/PickerYear/pickerYear.spec.js: -------------------------------------------------------------------------------- 1 | import PickerYear from '@/components/datepicker/PickerYear.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | import { 5 | getFullYear, 6 | } from '@/components/datepicker/utils/DateUtils'; 7 | 8 | let en; 9 | 10 | describe('PickerYear', () => { 11 | 12 | beforeEach(()=>{ 13 | en = Langlist.data['en']; 14 | }) 15 | 16 | let wrapper 17 | beforeEach(() => { 18 | wrapper = mount(PickerYear, { 19 | shallow: true, 20 | props: { 21 | allowedToShowView: () => true, 22 | translation: en, 23 | pageDate: new Date(2018, 1, 1), 24 | selectedDate: new Date(2018, 2, 24) 25 | } 26 | }) 27 | }) 28 | 29 | it('knows the selected year', async () => { 30 | const newDate = new Date(2020, 1, 1) 31 | await wrapper.setProps({ 32 | selectedDate: newDate 33 | }) 34 | expect(wrapper.vm.selectedDate).toBe(newDate) 35 | expect(getFullYear(wrapper.vm.selectedDate)).toBe(2020) 36 | expect(wrapper.vm.selectedDate).not.toBe(new Date(2017, 1, 1).toISOString()) 37 | }) 38 | 39 | it('can set the next decade', () => { 40 | wrapper.vm.nextDecade(); 41 | expect(wrapper.emitted()['changed-decade']).toBeTruthy() 42 | }) 43 | 44 | it('can set the previous decade', () => { 45 | wrapper.vm.previousDecade() 46 | expect(wrapper.emitted()['changed-decade']).toBeTruthy() 47 | }) 48 | 49 | it('formats the decade range', async () => { 50 | await wrapper.setProps({ 51 | pageDate: new Date(2021, 1, 1) 52 | }) 53 | expect(wrapper.vm.getPageDecade).toEqual('2020 - 2029') 54 | await wrapper.setProps({ 55 | pageDate: new Date(2001, 1, 1) 56 | }) 57 | expect(wrapper.vm.getPageDecade).toEqual('2000 - 2009') 58 | }) 59 | 60 | it('emits an event when selected', async () => { 61 | await wrapper.find('.cell').trigger('click') 62 | expect(wrapper.emitted()['select-year']).toBeTruthy() 63 | }) 64 | }) 65 | -------------------------------------------------------------------------------- /src/components/examples/DefaultValue.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 75 | -------------------------------------------------------------------------------- /src/components/examples/Typeable.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 80 | -------------------------------------------------------------------------------- /src/components/examples/Language.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 77 | -------------------------------------------------------------------------------- /src/components/examples/ProgrammaticAccess.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 79 | -------------------------------------------------------------------------------- /src/components/examples/VModel.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 83 | -------------------------------------------------------------------------------- /src/directives/click-outside.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // https://github.com/simplesmiler/vue-clickaway 3 | // https://github.com/ndelvalle/v-click-outside/blob/master/lib/v-click-outside.js 4 | // Mixed both :) 5 | 6 | const EVENTS = ['click']; 7 | 8 | const instances: any[] = []; 9 | 10 | const ClickOutside = { 11 | instances, 12 | beforeMount: bind, 13 | updated: (el: any, binding: any) => { 14 | if (JSON.stringify(binding.value) === JSON.stringify(binding.oldValue)) return; 15 | bind(el, binding); 16 | }, 17 | unmounted: unbind, 18 | }; 19 | 20 | function bind(el: any, { value }: { value: any }) { 21 | unbind(el); 22 | 23 | const bindingValue = value; 24 | const isFunction = typeof bindingValue === 'function'; 25 | const isObject = typeof bindingValue === 'object'; 26 | 27 | if (!isFunction && !isObject) return; 28 | 29 | const isActive = !(bindingValue.isActive === false); 30 | if (!isActive) return; 31 | 32 | const handler = isFunction ? bindingValue : bindingValue.handler; 33 | const instance = createInstance({ el, handler }); 34 | 35 | instance.eventHandlers.forEach(({ event, handler }) => 36 | setTimeout(() => document.addEventListener(event, handler, false), 0) 37 | ); 38 | instances.push(instance); 39 | } 40 | 41 | function unbind(el: any) { 42 | const instanceIndex = instances.findIndex((instance) => instance.el === el); 43 | if (instanceIndex === -1) return; 44 | 45 | const instance = instances[instanceIndex]; 46 | instance.eventHandlers.forEach(({ event, handler }: { event: any; handler: any }) => 47 | document.removeEventListener(event, handler, false) 48 | ); 49 | instances.splice(instanceIndex, 1); 50 | } 51 | 52 | // -------------------- 53 | // Helpers 54 | // -------------------- 55 | function createInstance({ el, handler }: { el: any; handler: any }) { 56 | return { 57 | el, 58 | eventHandlers: EVENTS.map((eventName) => ({ 59 | event: eventName, 60 | handler: (event: any) => onEvent({ event, el, handler }), 61 | })), 62 | }; 63 | } 64 | 65 | function onEvent({ event, el, handler }: { event: any; el: any; handler: any }) { 66 | const path = event.path || (event.composedPath ? event.composedPath() : undefined); 67 | if (path ? path.indexOf(el) < 0 : !el.contains(event.target)) { 68 | return handler && handler(event, el); 69 | } 70 | } 71 | 72 | export default ClickOutside; 73 | -------------------------------------------------------------------------------- /tests/unit/specs/DateInput/typedDates.spec.js: -------------------------------------------------------------------------------- 1 | import DateInput from '@/components/datepicker/DateInput.vue'; 2 | import { shallowMount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | let en; 6 | 7 | describe('DateInput', () => { 8 | 9 | beforeEach(()=>{ 10 | en = Langlist.data['en']; 11 | }) 12 | 13 | let wrapper 14 | 15 | beforeEach(() => { 16 | wrapper = shallowMount(DateInput, { 17 | propsData: { 18 | format: 'dd MMM yyyy', 19 | translation: en, 20 | typeable: true 21 | } 22 | }) 23 | }) 24 | 25 | it('does not format the date when typed', async () => { 26 | const dateString = '2018-04-24' 27 | const input = await wrapper.find('input') 28 | wrapper.vm.inputRef.value = dateString 29 | expect(wrapper.vm.inputRef.value).toEqual(dateString) 30 | input.trigger('keyup') 31 | await wrapper.setProps({ 32 | selectedDate: new Date(dateString) 33 | }) 34 | expect(wrapper.vm.typedDate).toEqual(dateString) 35 | expect(wrapper.vm.formattedValue).toEqual(dateString) 36 | }) 37 | 38 | it('emits the date when typed', async () => { 39 | const input = await wrapper.find('input') 40 | wrapper.vm.inputRef.value = '2018-04-24' 41 | input.trigger('keyup') 42 | expect(wrapper.emitted()['typed-date']).toBeDefined() 43 | expect(wrapper.emitted()['typed-date'][0][0]).toBeInstanceOf(Date) 44 | }) 45 | 46 | it('emits closeCalendar when return is pressed', () => { 47 | const input = wrapper.find('input') 48 | const blurSpy = jest.spyOn(input.element, 'blur') 49 | input.trigger('keyup', {keyCode: 13}) 50 | expect(blurSpy).toBeCalled() 51 | }) 52 | 53 | it('clears a typed date if it does not parse', async () => { 54 | const input = await wrapper.find('input') 55 | wrapper.vm.inputRef.value = 'not-a-date' 56 | input.trigger('blur') 57 | expect(wrapper.emitted()['clear-date']).toBeDefined() 58 | expect(wrapper.emitted()['close-calendar']).toBeDefined() 59 | }) 60 | 61 | it('input is hidden if typeable=false', async () => { 62 | const wrapper = await shallowMount(DateInput, { 63 | propsData: { 64 | format: 'dd MMM yyyy', 65 | translation: en, 66 | typeable: false 67 | } 68 | }); 69 | expect(wrapper.find('input').exists()).toBeFalsy() 70 | expect(wrapper.find('#calendar-div').exists()).toBeTruthy() 71 | }) 72 | }) 73 | -------------------------------------------------------------------------------- /src/components/examples/Disabled.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 91 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/block-navigation.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var jumpToCode = (function init() { 3 | // Classes of code we would like to highlight in the file view 4 | var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; 5 | 6 | // Elements to highlight in the file listing view 7 | var fileListingElements = ['td.pct.low']; 8 | 9 | // We don't want to select elements that are direct descendants of another match 10 | var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` 11 | 12 | // Selecter that finds elements on the page to which we can jump 13 | var selector = 14 | fileListingElements.join(', ') + 15 | ', ' + 16 | notSelector + 17 | missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` 18 | 19 | // The NodeList of matching elements 20 | var missingCoverageElements = document.querySelectorAll(selector); 21 | 22 | var currentIndex; 23 | 24 | function toggleClass(index) { 25 | missingCoverageElements 26 | .item(currentIndex) 27 | .classList.remove('highlighted'); 28 | missingCoverageElements.item(index).classList.add('highlighted'); 29 | } 30 | 31 | function makeCurrent(index) { 32 | toggleClass(index); 33 | currentIndex = index; 34 | missingCoverageElements.item(index).scrollIntoView({ 35 | behavior: 'smooth', 36 | block: 'center', 37 | inline: 'center' 38 | }); 39 | } 40 | 41 | function goToPrevious() { 42 | var nextIndex = 0; 43 | if (typeof currentIndex !== 'number' || currentIndex === 0) { 44 | nextIndex = missingCoverageElements.length - 1; 45 | } else if (missingCoverageElements.length > 1) { 46 | nextIndex = currentIndex - 1; 47 | } 48 | 49 | makeCurrent(nextIndex); 50 | } 51 | 52 | function goToNext() { 53 | var nextIndex = 0; 54 | 55 | if ( 56 | typeof currentIndex === 'number' && 57 | currentIndex < missingCoverageElements.length - 1 58 | ) { 59 | nextIndex = currentIndex + 1; 60 | } 61 | 62 | makeCurrent(nextIndex); 63 | } 64 | 65 | return function jump(event) { 66 | switch (event.which) { 67 | case 78: // n 68 | case 74: // j 69 | goToNext(); 70 | break; 71 | case 66: // b 72 | case 75: // k 73 | case 80: // p 74 | goToPrevious(); 75 | break; 76 | } 77 | }; 78 | })(); 79 | window.addEventListener('keydown', jumpToCode); 80 | -------------------------------------------------------------------------------- /src/components/examples/Slotview.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 109 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuejs3-datepicker", 3 | "version": "1.1.3", 4 | "description": "Vue 3 datepicker", 5 | "files": [ 6 | "build/*" 7 | ], 8 | "type": "module", 9 | "module": "./build/vuejs3-datepicker.js", 10 | "main": "./build/vuejs3-datepicker.umd.cjs", 11 | "exports": { 12 | ".": { 13 | "import": "./build/vuejs3-datepicker.js", 14 | "require": "./build/vuejs3-datepicker.umd.cjs" 15 | } 16 | }, 17 | "scripts": { 18 | "dev": "vite --config vite.config.project.ts", 19 | "cz": "git-cz", 20 | "precommit": "lint-staged", 21 | "build": "vue-tsc && vite build --config vite.config.project.ts", 22 | "build:lib": "vue-tsc && vite build --config vite.config.lib.ts", 23 | "preview": "vite preview", 24 | "lint": "eslint --cache --fix", 25 | "prepublishOnly": "npm run lint && npm run build:lib", 26 | "commit-message": "npm run commitlint -e -V", 27 | "semantic-release:local": "npm run build:lib && semantic-release --no-ci", 28 | "semantic-release": "semantic-release", 29 | "prepare": "husky install" 30 | }, 31 | "dependencies": { 32 | "prismjs": "^1.22.0", 33 | "vite-plugin-dts": "^3.6.3", 34 | "vue": "^3.0.0" 35 | }, 36 | "devDependencies": { 37 | "@commitlint/cli": "^18.2.0", 38 | "@commitlint/config-conventional": "^18.1.0", 39 | "@semantic-release/changelog": "^6.0.3", 40 | "@semantic-release/commit-analyzer": "^11.0.0", 41 | "@semantic-release/git": "^10.0.1", 42 | "@semantic-release/npm": "^11.0.1", 43 | "@semantic-release/release-notes-generator": "^12.0.1", 44 | "@types/node": "^20.8.10", 45 | "@types/prismjs": "^1.26.2", 46 | "@typescript-eslint/eslint-plugin": "^6.9.1", 47 | "@typescript-eslint/parser": "^6.9.1", 48 | "@vitejs/plugin-vue": "^4.2.3", 49 | "@vue/eslint-config-airbnb": "^7.0.0", 50 | "@vue/eslint-config-prettier": "^8.0.0", 51 | "@vue/eslint-config-typescript": "^12.0.0", 52 | "autoprefixer": "^10.4.16", 53 | "cssnano": "^6.0.1", 54 | "cz-conventional-changelog": "^3.3.0", 55 | "eslint": "^8.53.0", 56 | "eslint-plugin-import": "^2.29.0", 57 | "eslint-plugin-prettier": "^5.0.1", 58 | "eslint-plugin-vue": "^9.18.1", 59 | "git-cz": "^4.9.0", 60 | "husky": "^8.0.0", 61 | "jest": "^29.7.0", 62 | "lint-staged": "^15.0.2", 63 | "postcss": "^8.4.31", 64 | "postcss-import": "^15.1.0", 65 | "postcss-nested": "^6.0.1", 66 | "postcss-simple-vars": "^7.0.1", 67 | "semantic-release": "^22.0.7", 68 | "typescript": "^5.0.2", 69 | "vite": "^4.4.5", 70 | "vite-plugin-libcss": "^1.1.1", 71 | "vue-tsc": "^1.8.5" 72 | }, 73 | "lint-staged": { 74 | "*.ts": "eslint --cache --fix", 75 | "*.vue": "eslint --cache --fix" 76 | }, 77 | "keywords": [ 78 | "vue", 79 | "vuejs", 80 | "vue 3", 81 | "javascript", 82 | "typescript", 83 | "vue-component", 84 | "date", 85 | "library", 86 | "month", 87 | "picker", 88 | "datepicker", 89 | "monthpicker" 90 | ], 91 | "repository": { 92 | "type": "git", 93 | "url": "git+https://github.com/shubhadip/vuejs3-datepicker.git" 94 | }, 95 | "bugs": { 96 | "url": "https://github.com/shubhadip/vuejs3-datepicker/issues" 97 | }, 98 | "config": { 99 | "commitizen": { 100 | "path": "git-cz" 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tests/unit/specs/DateInput/DateInput.spec.js: -------------------------------------------------------------------------------- 1 | import DateInput from '@/components/datepicker/DateInput.vue'; 2 | import {shallowMount} from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | let en; 6 | 7 | describe('DateInput', () => { 8 | 9 | beforeEach(()=>{ 10 | en = Langlist.data['en']; 11 | }) 12 | 13 | let wrapper 14 | 15 | beforeEach(() => { 16 | wrapper = shallowMount(DateInput, { 17 | propsData: { 18 | selectedDate: new Date(2018, 2, 24), 19 | format: 'dd MMM yyyy', 20 | translation: en, 21 | hideInput: false 22 | } 23 | }) 24 | }) 25 | 26 | it('should render correct contents', () => { 27 | expect(wrapper.findAll('input')).toHaveLength(1) 28 | }) 29 | 30 | it('nulls date', async () => { 31 | await wrapper.setProps({ 32 | selectedDate: null 33 | }) 34 | expect(wrapper.vm.formattedValue).toBeNull() 35 | expect(wrapper.find('input').element.value).toEqual('') 36 | }) 37 | 38 | it('formats date', () => { 39 | expect(wrapper.vm.formattedValue).toEqual('24 Mar 2018') 40 | expect(wrapper.find('input').element.value).toEqual('24 Mar 2018') 41 | }) 42 | 43 | it('delegates date formatting', async () => { 44 | await wrapper.setProps({ 45 | selectedDate: new Date(2016, 1, 15), 46 | format: () => '2016/1/15' 47 | }) 48 | expect(wrapper.vm.formattedValue).toEqual('2016/1/15') 49 | expect(wrapper.find('input').element.value).toEqual('2016/1/15') 50 | }) 51 | 52 | it('emits showCalendar', () => { 53 | wrapper.vm.showCalendar() 54 | expect(wrapper.emitted()['show-calendar']).toBeTruthy() 55 | }) 56 | 57 | it('adds bootstrap classes', async () => { 58 | await wrapper.setProps({ 59 | addBootstrapClass: true 60 | }) 61 | expect(wrapper.find('input').element.classList).toContain('form-control') 62 | }) 63 | 64 | it('appends bootstrap classes', async () => { 65 | await wrapper.setProps({ 66 | inputClass: 'someClass', 67 | addBootstrapClass: true 68 | }) 69 | expect(wrapper.find('input').element.classList).toContain('form-control') 70 | expect(wrapper.find('input').element.classList).toContain('someClass') 71 | }) 72 | 73 | it('can be disabled', async () => { 74 | await wrapper.setProps({ 75 | disabled: true 76 | }) 77 | expect(wrapper.find('input').attributes().disabled).toBeDefined() 78 | }) 79 | 80 | it('accepts a function as a formatter', async () => { 81 | await wrapper.setProps({ 82 | format: () => '!' 83 | }) 84 | expect(wrapper.find('input').element.value).toEqual('!') 85 | }) 86 | 87 | it('triggers closeCalendar on blur', async () => { 88 | await wrapper.find('input').trigger('blur') 89 | expect(wrapper.emitted('close-calendar')).toBeTruthy() 90 | }) 91 | 92 | it('month for minimum, maximum view', async () => { 93 | await wrapper.setProps({ 94 | minimumView: 'month', 95 | maximumView: 'month', 96 | }) 97 | expect(wrapper.vm.formattedValue).toEqual('Mar') 98 | }) 99 | 100 | it('year for minimum, maximum view', async () => { 101 | await wrapper.setProps({ 102 | minimumView: 'year', 103 | maximumView: 'year', 104 | }) 105 | expect(wrapper.vm.formattedValue).toEqual('2018') 106 | }) 107 | 108 | it('month for minimum, maximum view', async () => { 109 | await wrapper.setProps({ 110 | minimumView: 'month', 111 | maximumView: 'month', 112 | 'full-month-name': true 113 | }) 114 | expect(wrapper.vm.formattedValue).toEqual('March') 115 | }) 116 | }) 117 | -------------------------------------------------------------------------------- /tests/unit/specs/PickerYear/disabledYears.spec.js: -------------------------------------------------------------------------------- 1 | import PickerYear from '@/components/datepicker/PickerYear.vue'; 2 | import { mount, shallowMount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | let en; 6 | 7 | describe('PickerYear', () => { 8 | 9 | beforeEach(()=>{ 10 | en = Langlist.data['en']; 11 | }) 12 | 13 | let wrapper 14 | 15 | beforeEach(() => { 16 | wrapper = mount(PickerYear, { 17 | shallow: true, 18 | propsData: { 19 | allowedToShowView: () => true, 20 | translation: en, 21 | pageDate: new Date(2018, 3, 1), 22 | selectedDate: new Date(2018, 3, 19), 23 | disabledDates: { 24 | to: new Date(2018, 2, 14), 25 | from: new Date(2018, 4, 15) 26 | } 27 | } 28 | }) 29 | }) 30 | 31 | it('cant select a disabled year', async () => { 32 | await wrapper.find('.cell').trigger('click') 33 | expect(wrapper.emitted()['select-year']).toBeFalsy() 34 | }) 35 | 36 | it('cant navigate to a disabled year', () => { 37 | expect(wrapper.vm.previousDecade()).toBeFalsy() 38 | expect(wrapper.vm.nextDecade()).toBeFalsy() 39 | }) 40 | 41 | it('can\'t change decade when previous or next decades are disabled', () => { 42 | const shallowWrapper = shallowMount(PickerYear, { 43 | props: { 44 | allowedToShowView: () => true, 45 | translation: en, 46 | pageDate: new Date(2018, 3, 1), 47 | selectedDate: new Date(2018, 3, 19), 48 | disabledDates: { 49 | to: new Date(2018, 2, 14), 50 | from: new Date(2018, 4, 15) 51 | } 52 | } 53 | }) 54 | expect(shallowWrapper.vm.isPreviousDecadeDisabled()).toBeTruthy() 55 | expect(shallowWrapper.vm.isNextDecadeDisabled()).toBeTruthy() 56 | }) 57 | 58 | it('can change decade despite having a disabled decade', async () => { 59 | wrapper.setProps({ 60 | pageDate: new Date(2016, 9, 15), 61 | disabledDates: { 62 | to: new Date(2010, 11, 19), 63 | from: new Date(2021, 11, 19) 64 | } 65 | }) 66 | await expect(wrapper.vm.isPreviousDecadeDisabled()).toEqual(true) 67 | await expect(wrapper.vm.isNextDecadeDisabled()).toEqual(false) 68 | }) 69 | 70 | it('can accept a customPredictor to check if the year is disabled', () => { 71 | const shallowWrapper = shallowMount(PickerYear, { 72 | props: { 73 | allowedToShowView: () => true, 74 | translation: en, 75 | pageDate: new Date(2018, 3, 1), 76 | selectedDate: new Date(2018, 3, 19), 77 | disabledDates: { 78 | customPredictor (date) { 79 | return (date.getFullYear() % 3) === 0 80 | } 81 | } 82 | } 83 | }) 84 | expect(shallowWrapper.vm.isDisabledYear(new Date(2018, 4, 29))).toEqual(false) 85 | expect(shallowWrapper.vm.isDisabledYear(new Date(2019, 9, 28))).toEqual(true) 86 | expect(shallowWrapper.vm.isDisabledYear(new Date(2020, 8, 24))).toEqual(false) 87 | expect(shallowWrapper.vm.isDisabledYear(new Date(2021, 2, 11))).toEqual(false) 88 | expect(shallowWrapper.vm.isDisabledYear(new Date(2022, 2, 11))).toEqual(true) 89 | }) 90 | 91 | it('does not disable the next decade button when disabled from date is in the first year of the next decade', () => { 92 | wrapper.setProps({ 93 | pageDate: new Date(1998, 9, 15), 94 | disabledDates: { 95 | from: new Date(2000, 0, 1) 96 | } 97 | }) 98 | expect(wrapper.vm.isNextDecadeDisabled()).toEqual(true) 99 | }) 100 | 101 | it('does disable the next decade button when disabled from date is the last day year of the current decade', () => { 102 | const shallowWrapper = shallowMount(PickerYear, { 103 | props: { 104 | allowedToShowView: () => true, 105 | translation: en, 106 | pageDate: new Date(2029, 3, 1), 107 | selectedDate: new Date(2029, 3, 19), 108 | disabledDates: { 109 | from: new Date(2029, 12, 31) 110 | } 111 | } 112 | }) 113 | expect(shallowWrapper.vm.isNextDecadeDisabled()).toEqual(true) 114 | }) 115 | }) 116 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | 'plugin:@typescript-eslint/recommended-requiring-type-checking', 8 | 'plugin:vue/vue3-essential', 9 | '@vue/airbnb', 10 | '@vue/typescript/recommended', 11 | '@vue/prettier', 12 | // '@vue/prettier/@typescript-eslint', 13 | ], 14 | parserOptions: { 15 | ecmaVersion: 2020, 16 | sourceType: 'module', 17 | project: './tsconfig.json', 18 | createDefaultProgram: true, 19 | }, 20 | plugins: ['@typescript-eslint'], 21 | rules: { 22 | 'no-unused-vars': 'off', 23 | 'no-useless-constructor': 'off', 24 | indent: 'off', 25 | 'no-tabs': 'error', 26 | 'max-len': [2, 200, 4, { ignoreUrls: true }], 27 | 'no-template-curly-in-string': 'error', 28 | 'prefer-arrow-callback': 'error', 29 | 'no-param-reassign': 'off', 30 | 'class-methods-use-this': 'off', 31 | 'import/prefer-default-export': 'off', 32 | 'comma-dangle': 'off', 33 | 'no-restricted-syntax': 'off', 34 | 'no-unused-expressions': ['warn', { allowShortCircuit: true, allowTernary: true }], 35 | '@typescript-eslint/no-unused-vars': 'error', 36 | '@typescript-eslint/no-useless-constructor': 'error', 37 | '@typescript-eslint/default-param-last': 'error', 38 | '@typescript-eslint/explicit-function-return-type': 'error', 39 | '@typescript-eslint/no-empty-interface': 'warn', 40 | '@typescript-eslint/no-explicit-any': 'off', 41 | '@typescript-eslint/no-implied-eval': 'error', 42 | '@typescript-eslint/no-inferrable-types': 'error', 43 | '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', 44 | '@typescript-eslint/interface-name-prefix': 'off', 45 | '@typescript-eslint/indent': ['error', 2], 46 | // TODO: refactor these rules starts here 47 | 'vuejs-accessibility/form-control-has-label': 'off', 48 | 'import/extensions': 'off', 49 | 'vue/require-default-prop': 'warn', 50 | '@typescript-eslint/no-unsafe-argument': 'off', 51 | '@typescript-eslint/no-unsafe-assignment': 'off', 52 | '@typescript-eslint/no-unsafe-call': 'off', 53 | 'vuejs-accessibility/click-events-have-key-events':'warn', 54 | '@typescript-eslint/no-unsafe-member-access': 'off', 55 | '@typescript-eslint/no-unsafe-return': 'off', 56 | 'vue/multi-word-component-names': 'off', 57 | // TODO: refactor these rules ends here 58 | '@typescript-eslint/naming-convention': [ 59 | 'warn', 60 | { 61 | selector: 'parameter', 62 | format: ['camelCase'], 63 | leadingUnderscore: 'allow', 64 | }, 65 | 66 | { 67 | selector: 'memberLike', 68 | modifiers: ['private'], 69 | format: ['camelCase'], 70 | leadingUnderscore: 'allow', 71 | }, 72 | 73 | { 74 | selector: 'typeLike', 75 | format: ['PascalCase'], 76 | }, 77 | { 78 | selector: 'class', 79 | format: ['PascalCase'], 80 | }, 81 | { 82 | selector: 'interface', 83 | format: ['PascalCase'], 84 | prefix: ['I'], 85 | }, 86 | { 87 | selector: 'enum', 88 | format: ['PascalCase'], 89 | }, 90 | ], 91 | 'require-jsdoc': [ 92 | 'error', 93 | { 94 | require: { 95 | FunctionDeclaration: true, 96 | MethodDefinition: false, 97 | ClassDeclaration: false, 98 | ArrowFunctionExpression: false, 99 | FunctionExpression: false, 100 | }, 101 | }, 102 | ], 103 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 104 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 105 | }, 106 | settings: { 107 | 'import/extensions': ['.js', '.ts'], 108 | 'import/parsers': { 109 | '@typescript-eslint/parser': ['.ts', '.tsx'], 110 | }, 111 | 'import/resolver': { 112 | node: { 113 | extensions: ['.js', '.jsx', '.ts', '.tsx', 'd.ts'], 114 | }, 115 | }, 116 | }, 117 | overrides: [ 118 | { 119 | files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'], 120 | env: { 121 | jest: true, 122 | }, 123 | }, 124 | ], 125 | }; 126 | -------------------------------------------------------------------------------- /tests/unit/specs/PickerDay/disabledDates.spec.js: -------------------------------------------------------------------------------- 1 | import PickerDay from '@/components/datepicker/PickerDay.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | let en; 6 | describe('PickerDay: disabled', () => { 7 | beforeEach(()=>{ 8 | en = Langlist.data['en']; 9 | }) 10 | 11 | let wrapper 12 | beforeEach(() => { 13 | wrapper = mount(PickerDay, { 14 | props: { 15 | allowedToShowView: () => true, 16 | showMonthCalendar: () => {}, 17 | translation: en, 18 | disabledDates: { 19 | to: new Date(2016, 9, 4), 20 | from: new Date(2016, 9, 26) 21 | }, 22 | pageDate: new Date(2016, 9, 1) 23 | } 24 | }) 25 | }) 26 | 27 | it('should detect a disabled date', () => { 28 | expect(wrapper.vm.isDisabledDate(new Date(2006, 9, 2))).toEqual(true) 29 | expect(wrapper.vm.isDisabledDate(new Date(2026, 9, 2))).toEqual(true) 30 | }) 31 | 32 | it('should not select a disabled date', () => { 33 | expect(wrapper.vm.selectDate({isDisabled: true})).toBeFalsy() 34 | }) 35 | 36 | it('cant change to a disabled month', () => { 37 | wrapper.vm.previousMonth() 38 | expect(wrapper.vm.pageDate.getMonth()).toEqual(9) 39 | wrapper.vm.nextMonth() 40 | expect(wrapper.vm.pageDate.getMonth()).toEqual(9) 41 | }) 42 | 43 | it('can change month despite having a disabled month', () => { 44 | expect(wrapper.vm.isNextMonthDisabled()).toBeTruthy() 45 | }) 46 | 47 | it('should detect disabled dates', async () => { 48 | await wrapper.setProps({ 49 | disabledDates: { 50 | ranges: [{ 51 | from: new Date(2005, 6, 5), 52 | to: new Date(2016, 9, 4) 53 | }, { 54 | from: new Date(2016, 9, 26), 55 | to: new Date(2030, 12, 25) 56 | }] 57 | } 58 | }) 59 | expect(wrapper.vm.isDisabledDate(new Date(2006, 9, 2))).toEqual(true) 60 | expect(wrapper.vm.isDisabledDate(new Date(2026, 9, 2))).toEqual(true) 61 | }) 62 | 63 | it('can accept an array of disabled dates', async () => { 64 | await wrapper.setProps({ 65 | disabledDates: { 66 | dates: [ 67 | new Date(2016, 9, 2), 68 | new Date(2016, 9, 9), 69 | new Date(2016, 9, 16) 70 | ] 71 | } 72 | }) 73 | expect(wrapper.vm.isDisabledDate(new Date(2016, 9, 2))).toEqual(true) 74 | expect(wrapper.vm.isDisabledDate(new Date(2016, 9, 3))).toEqual(false) 75 | }) 76 | 77 | it('can accept an array of disabled days of the week', async () => { 78 | await wrapper.setProps({ 79 | disabledDates: { 80 | days: [6, 0] 81 | } 82 | }) 83 | expect(wrapper.vm.isDisabledDate(new Date(2016, 9, 2))).toEqual(true) 84 | expect(wrapper.vm.isDisabledDate(new Date(2016, 9, 3))).toEqual(false) 85 | }) 86 | 87 | it('can accept an array of disabled days of the month', async () => { 88 | await wrapper.setProps({ 89 | disabledDates: { 90 | daysOfMonth: [29, 30, 31] 91 | } 92 | }) 93 | expect(wrapper.vm.isDisabledDate(new Date(2016, 8, 29))).toEqual(true) 94 | expect(wrapper.vm.isDisabledDate(new Date(2016, 9, 31))).toEqual(true) 95 | expect(wrapper.vm.isDisabledDate(new Date(2016, 10, 30))).toEqual(true) 96 | expect(wrapper.vm.isDisabledDate(new Date(2016, 9, 11))).toEqual(false) 97 | }) 98 | 99 | it('can accept a customPredictor to check if the date is disabled', async () => { 100 | await wrapper.setProps({ 101 | disabledDates: { 102 | customPredictor (date) { 103 | if (date.getDate() % 4 === 0) { 104 | return true 105 | } 106 | } 107 | } 108 | }) 109 | expect(wrapper.vm.isDisabledDate(new Date(2016, 8, 29))).toEqual(false) 110 | expect(wrapper.vm.isDisabledDate(new Date(2016, 9, 28))).toEqual(true) 111 | expect(wrapper.vm.isDisabledDate(new Date(2016, 10, 24))).toEqual(true) 112 | expect(wrapper.vm.isDisabledDate(new Date(2016, 9, 11))).toEqual(false) 113 | }) 114 | 115 | it('should emit a selectedDisabled event for a disabled date', () => { 116 | expect(wrapper.vm.selectDate({isDisabled: true})).toBeFalsy() 117 | expect(wrapper.emitted()['selected-disabled']).toBeTruthy() 118 | }) 119 | }) 120 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/src/shared/utils.ts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src/shared/utils.ts 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files / src/shared utils.ts

23 |
24 | 25 |
26 | 0% 27 | Statements 28 | 0/2 29 |
30 | 31 | 32 |
33 | 0% 34 | Branches 35 | 0/2 36 |
37 | 38 | 39 |
40 | 0% 41 | Functions 42 | 0/1 43 |
44 | 45 | 46 |
47 | 0% 48 | Lines 49 | 0/2 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |

 60 | 
1 61 | 2 62 | 3 63 | 4 64 | 5 65 | 6 66 | 7 67 | 8  68 |   69 |   70 |   71 |   72 |   73 |   74 |  
/**
 75 |  * Returns true if data is null or undefined
 76 |  * @param data
 77 |  */
 78 | export const isNullUndefined = (data: any): boolean => {
 79 |   return data === null || data === undefined;
 80 | };
 81 |  
82 | 83 |
84 |
85 | 90 | 91 | 92 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/assets/styles/css/app.css: -------------------------------------------------------------------------------- 1 | @import './_settings.css'; 2 | 3 | *{ 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | font-family: 'Open Sans', sans-serif; 9 | color: $black; 10 | margin: 0px; 11 | padding: 0px; 12 | input { 13 | outline: none; 14 | &:focus{ 15 | outline: none; 16 | } 17 | } 18 | } 19 | 20 | .flex-block{ 21 | display: flex; 22 | align-items: center; 23 | margin-top: 20px; 24 | .change-btn{ 25 | margin-left: 20px; 26 | display: flex; 27 | align-items: center; 28 | button{ 29 | margin-right: 20px; 30 | } 31 | } 32 | @media screen and (max-width: 768px) { 33 | flex-flow: row wrap; 34 | .change-btn{ 35 | margin-top: 20px; 36 | margin-left: 0px; 37 | flex-flow: row wrap; 38 | button{ 39 | margin-right: 0px; 40 | } 41 | } 42 | } 43 | } 44 | 45 | .page-head { 46 | background-color: $darkGreen; 47 | text-align: center; 48 | padding: 20px; 49 | display: flex; 50 | justify-content: center; 51 | align-items: center; 52 | a { 53 | display: inline-block; 54 | margin-right: 10px; 55 | } 56 | } 57 | 58 | .ind { 59 | width: 100%; 60 | padding: 30px; 61 | } 62 | .app-wrapper { 63 | width: calc(100% - 280px); 64 | position: relative; 65 | left: 280px; 66 | flex: 1; 67 | .wrapper { 68 | display: flex; 69 | flex: 1; 70 | justify-content: center; 71 | flex-wrap: wrap; 72 | } 73 | @media screen and (max-width: 768px) { 74 | width: 100%; 75 | left: auto; 76 | } 77 | .code-block{ 78 | display: flex; 79 | justify-content: space-around; 80 | @media screen and (max-width: 768px) { 81 | flex-direction: column; 82 | .codesnippet{ 83 | width: 100%; 84 | } 85 | } 86 | } 87 | } 88 | .app-sidebar { 89 | position: fixed; 90 | left: 0px; 91 | top: 0px; 92 | width: 280px; 93 | height: 100%; 94 | background-color: $darkGreen; 95 | h1 { 96 | text-align: center; 97 | padding-top: 20px; 98 | color: $white; 99 | font-size: 25px; 100 | margin: 0px; 101 | @media screen and (max-width: 768px) { 102 | padding: 0px; 103 | font-size: 18px; 104 | margin-top: -10px; 105 | } 106 | } 107 | ul { 108 | margin: 0px; 109 | padding: 0px; 110 | position: relative; 111 | top: 20px; 112 | padding: 40px; 113 | list-style: none; 114 | overflow-y: scroll; 115 | height: 650px; 116 | @media screen and (max-width: 768px) { 117 | padding-top: 0px; 118 | } 119 | li { 120 | color: $white; 121 | font-size: 15px; 122 | margin-bottom: 10px; 123 | cursor: pointer; 124 | background-color: $darkestGreen; 125 | border-radius: 2px; 126 | transition: 0.45s; 127 | a { 128 | padding: 10px; 129 | display: block; 130 | color: $white; 131 | text-decoration: none; 132 | } 133 | &:hover { 134 | transform: translateX(5px); 135 | } 136 | } 137 | } 138 | @media screen and (max-width: 768px) { 139 | position: relative; 140 | width: 100%; 141 | } 142 | } 143 | .md-hidden{ 144 | @media screen and (min-width: 768px) { 145 | display: none; 146 | } 147 | } 148 | .sm-hidden{ 149 | @media screen and (max-width: 768px) { 150 | display: none; 151 | } 152 | } 153 | .hamburger-icon { 154 | position: absolute; 155 | height: 25px; 156 | width: 30px; 157 | display: block; 158 | z-index: 3; 159 | right: 40px; 160 | z-index: 10; 161 | hr { 162 | background-color: $white; 163 | position: absolute; 164 | height: 3px; 165 | border: none; 166 | width: 100%; 167 | border-radius: 10px; 168 | margin: 0px; 169 | top: 11px; 170 | transition: 0.45s; 171 | } 172 | &.active { 173 | hr { 174 | opacity: 0; 175 | } 176 | } 177 | &:after, 178 | &:before { 179 | content: ''; 180 | transform-origin: center; 181 | left: 0px; 182 | height: 3px; 183 | border-radius: 10px; 184 | transition: 0.45s; 185 | width: 100%; 186 | background-color: $white; 187 | position: absolute; 188 | } 189 | &:after { 190 | top: 0px; 191 | } 192 | &:before { 193 | bottom: 0px; 194 | } 195 | &.active { 196 | &:before { 197 | transform: rotate(45deg); 198 | top: 11px; 199 | left: 1px; 200 | } 201 | &:after { 202 | transform: rotate(-45deg); 203 | top: 11px; 204 | left: 1px; 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/wrapper/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for wrapper 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files wrapper

23 |
24 | 25 |
26 | 0% 27 | Statements 28 | 0/6 29 |
30 | 31 | 32 |
33 | 100% 34 | Branches 35 | 0/0 36 |
37 | 38 | 39 |
40 | 0% 41 | Functions 42 | 0/1 43 |
44 | 45 | 46 |
47 | 0% 48 | Lines 49 | 0/6 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
FileStatementsBranchesFunctionsLines
Wrapper.vue 78 |
79 |
0%0/6100%0/00%0/10%0/6
92 |
93 |
94 |
95 | 100 | 101 | 102 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/prismcomponent/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for prismcomponent 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files prismcomponent

23 |
24 | 25 |
26 | 0% 27 | Statements 28 | 0/18 29 |
30 | 31 | 32 |
33 | 0% 34 | Branches 35 | 0/10 36 |
37 | 38 | 39 |
40 | 0% 41 | Functions 42 | 0/3 43 |
44 | 45 | 46 |
47 | 0% 48 | Lines 49 | 0/14 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
FileStatementsBranchesFunctionsLines
index.ts 78 |
79 |
0%0/180%0/100%0/30%0/14
92 |
93 |
94 |
95 | 100 | 101 | 102 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/iconview/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for iconview 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files iconview

23 |
24 | 25 |
26 | 50% 27 | Statements 28 | 1/2 29 |
30 | 31 | 32 |
33 | 100% 34 | Branches 35 | 0/0 36 |
37 | 38 | 39 |
40 | 0% 41 | Functions 42 | 0/1 43 |
44 | 45 | 46 |
47 | 50% 48 | Lines 49 | 1/2 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
FileStatementsBranchesFunctionsLines
IconView.vue 78 |
79 |
50%1/2100%0/00%0/150%1/2
92 |
93 |
94 |
95 | 100 | 101 | 102 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 87 | 88 | 128 | 131 | -------------------------------------------------------------------------------- /tests/unit/specs/Datepicker/restrictedViews.spec.js: -------------------------------------------------------------------------------- 1 | import Datepicker from '@/components/datepicker/DatePickerComponent.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | 5 | let en; 6 | 7 | describe('Datepicker with restricted views', () => { 8 | beforeEach(()=>{ 9 | en = Langlist.data['en']; 10 | }) 11 | let wrapper 12 | it('should default initialView to minimumView', async () => { 13 | wrapper = await mount(Datepicker, { 14 | props: { 15 | minimumView: 'month', 16 | maximumView: 'month' 17 | } 18 | }) 19 | expect(wrapper.vm.computedInitialView).toEqual('month') 20 | }) 21 | 22 | it('should save and close when selecting on minimum-view "month"', async () => { 23 | wrapper = await mount(Datepicker, { 24 | props: { 25 | minimumView: 'month', 26 | maximumView: 'year' 27 | } 28 | }) 29 | const date = new Date(2016, 9, 12) 30 | wrapper.vm.selectYear({timestamp: date.getTime()}) 31 | expect(wrapper.vm.isOpen).toEqual(true) 32 | wrapper.vm.selectMonth({timestamp: date.getTime()}) 33 | expect(date.getFullYear()).toEqual(wrapper.vm.selectedDate.getFullYear()) 34 | expect(date.getMonth()).toEqual(wrapper.vm.selectedDate.getMonth()) 35 | expect(wrapper.vm.isOpen).toEqual(false) 36 | }) 37 | 38 | it('should save and close when selecting on minimum-view "year"', async () => { 39 | wrapper = await mount(Datepicker, { 40 | props: { 41 | minimumView: 'year', 42 | maximumView: 'year' 43 | } 44 | }) 45 | const date = new Date(2016, 9, 12) 46 | wrapper.vm.selectYear({timestamp: date.getTime()}) 47 | expect(wrapper.vm.isOpen).toEqual(false) 48 | expect(date.getFullYear()).toEqual(wrapper.vm.selectedDate.getFullYear()) 49 | }) 50 | 51 | it('should only allow views in min-max range', async () => { 52 | wrapper = await mount(Datepicker, { 53 | props: { 54 | minimumView: 'day', 55 | maximumView: 'month' 56 | } 57 | }) 58 | expect(wrapper.vm.allowedToShowView('year')).toEqual(false) 59 | expect(wrapper.vm.allowedToShowView('day')).toEqual(true) 60 | expect(wrapper.vm.allowedToShowView('month')).toEqual(true) 61 | expect(wrapper.vm.showYearCalendar()).toEqual(false) 62 | 63 | wrapper = await mount(Datepicker, { 64 | propsData: { 65 | minimumView: 'month', 66 | maximumView: 'month' 67 | } 68 | }) 69 | expect(wrapper.vm.allowedToShowView('day')).toEqual(false) 70 | expect(wrapper.vm.allowedToShowView('year')).toEqual(false) 71 | expect(wrapper.vm.allowedToShowView('month')).toEqual(true) 72 | expect(wrapper.vm.showDayCalendar()).toEqual(false) 73 | expect(wrapper.vm.showYearCalendar()).toEqual(false) 74 | 75 | wrapper = await mount(Datepicker, { 76 | props: { 77 | minimumView: 'day', 78 | maximumView: 'year' 79 | } 80 | }) 81 | expect(wrapper.vm.allowedToShowView('day')).toEqual(true) 82 | expect(wrapper.vm.allowedToShowView('year')).toEqual(true) 83 | expect(wrapper.vm.allowedToShowView('month')).toEqual(true) 84 | }) 85 | 86 | it('should throw an error on disallowed initial views', async () => { 87 | wrapper = await mount(Datepicker, { 88 | props: { 89 | minimumView: 'day', 90 | maximumView: 'month', 91 | initialView: 'year' 92 | } 93 | }) 94 | 95 | expect(function () { 96 | wrapper.vm.setInitialView() 97 | }).toThrow() 98 | }) 99 | 100 | it('should not render unused views', async () => { 101 | wrapper = await mount(Datepicker, { 102 | props: { 103 | minimumView: 'day', 104 | maximumView: 'day' 105 | } 106 | }) 107 | wrapper.vm.showCalendar() 108 | expect(wrapper.vm.$el.querySelectorAll('.vuejs3-datepicker__calendar').length).toEqual(1) 109 | expect(wrapper.vm.$el.querySelectorAll('.vuejs3-datepicker__calendar .cell.month').length).toEqual(0) 110 | expect(wrapper.vm.$el.querySelectorAll('.vuejs3-datepicker__calendar .cell.year').length).toEqual(0) 111 | expect(wrapper.vm.showMonthCalendar()).toEqual(false) 112 | 113 | wrapper = await mount(Datepicker, { 114 | props: { 115 | minimumView: 'month', 116 | maximumView: 'year' 117 | } 118 | }) 119 | wrapper.vm.showCalendar() 120 | expect(wrapper.vm.$el.querySelectorAll('.vuejs3-datepicker__calendar').length).toEqual(2) 121 | expect(wrapper.vm.$el.querySelectorAll('.vuejs3-datepicker__calendar .cell.day').length).toEqual(0) 122 | expect(wrapper.vm.$el.querySelectorAll('.vuejs3-datepicker__calendar .cell.year').length).toBeGreaterThan(0) 123 | }) 124 | }) 125 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/src/main.ts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src/main.ts 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files / src main.ts

23 |
24 | 25 |
26 | 0% 27 | Statements 28 | 0/6 29 |
30 | 31 | 32 |
33 | 100% 34 | Branches 35 | 0/0 36 |
37 | 38 | 39 |
40 | 100% 41 | Functions 42 | 0/0 43 |
44 | 45 | 46 |
47 | 0% 48 | Lines 49 | 0/6 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |

 60 | 
1 61 | 2 62 | 3 63 | 4 64 | 5 65 | 6 66 | 7 67 | 8 68 | 9 69 | 10  70 |   71 |   72 |   73 |   74 |   75 |   76 |   77 |   78 |  
import { createApp } from 'vue';
 79 | import App from './App.vue';
 80 | import clickOutside from '@/directives/click-outside'
 81 |  
 82 | const app = createApp(App)
 83 |  
 84 | app.directive('clickoutside', clickOutside);
 85 |  
 86 | app.mount('#app');
 87 |  
88 | 89 |
90 |
91 | 96 | 97 | 98 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/src/directives/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src/directives 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files src/directives

23 |
24 | 25 |
26 | 0% 27 | Statements 28 | 0/46 29 |
30 | 31 | 32 |
33 | 0% 34 | Branches 35 | 0/22 36 |
37 | 38 | 39 |
40 | 0% 41 | Functions 42 | 0/11 43 |
44 | 45 | 46 |
47 | 0% 48 | Lines 49 | 0/32 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
FileStatementsBranchesFunctionsLines
click-outside.ts 78 |
79 |
0%0/460%0/220%0/110%0/32
92 |
93 |
94 |
95 | 100 | 101 | 102 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/datepicker/locale/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for datepicker/locale 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files datepicker/locale

23 |
24 | 25 |
26 | 100% 27 | Statements 28 | 20/20 29 |
30 | 31 | 32 |
33 | 100% 34 | Branches 35 | 0/0 36 |
37 | 38 | 39 |
40 | 100% 41 | Functions 42 | 2/2 43 |
44 | 45 | 46 |
47 | 100% 48 | Lines 49 | 20/20 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
FileStatementsBranchesFunctionsLines
index.ts 78 |
79 |
100%20/20100%0/0100%2/2100%20/20
92 |
93 |
94 |
95 | 100 | 101 | 102 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/datepicker/utils/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for datepicker/utils 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files datepicker/utils

23 |
24 | 25 |
26 | 96.77% 27 | Statements 28 | 90/93 29 |
30 | 31 | 32 |
33 | 93.94% 34 | Branches 35 | 93/99 36 |
37 | 38 | 39 |
40 | 100% 41 | Functions 42 | 20/20 43 |
44 | 45 | 46 |
47 | 96.43% 48 | Lines 49 | 81/84 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
FileStatementsBranchesFunctionsLines
DateUtils.ts 78 |
79 |
96.77%90/9393.94%93/99100%20/2096.43%81/84
92 |
93 |
94 |
95 | 100 | 101 | 102 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/src/components/datepicker/locale/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src/components/datepicker/locale 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files src/components/datepicker/locale

23 |
24 | 25 |
26 | 100% 27 | Statements 28 | 20/20 29 |
30 | 31 | 32 |
33 | 100% 34 | Branches 35 | 0/0 36 |
37 | 38 | 39 |
40 | 100% 41 | Functions 42 | 2/2 43 |
44 | 45 | 46 |
47 | 100% 48 | Lines 49 | 20/20 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
FileStatementsBranchesFunctionsLines
index.ts 78 |
79 |
100%20/20100%0/0100%2/2100%20/20
92 |
93 |
94 |
95 | 100 | 101 | 102 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/src/components/datepicker/utils/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src/components/datepicker/utils 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files src/components/datepicker/utils

23 |
24 | 25 |
26 | 88.76% 27 | Statements 28 | 79/89 29 |
30 | 31 | 32 |
33 | 89.69% 34 | Branches 35 | 87/97 36 |
37 | 38 | 39 |
40 | 89.47% 41 | Functions 42 | 17/19 43 |
44 | 45 | 46 |
47 | 87.5% 48 | Lines 49 | 70/80 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
FileStatementsBranchesFunctionsLines
DateUtils.ts 78 |
79 |
88.76%79/8989.69%87/9789.47%17/1987.5%70/80
92 |
93 |
94 |
95 | 100 | 101 | 102 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /tests/unit/specs/PickerDay/highlightedDates.spec.js: -------------------------------------------------------------------------------- 1 | import PickerDay from '@/components/datepicker/PickerDay.vue'; 2 | import { mount } from '@vue/test-utils' 3 | import * as Langlist from '@/components/datepicker/locale'; 4 | let en; 5 | describe('PickerDay highlight date', () => { 6 | beforeEach(()=>{ 7 | en = Langlist.data['en']; 8 | }) 9 | 10 | let wrapper 11 | beforeEach(() => { 12 | wrapper = mount(PickerDay, { 13 | props: { 14 | allowedToShowView: () => true, 15 | translation: en, 16 | pageDate: new Date(2016, 9, 1), 17 | highlighted: { 18 | to: new Date(2016, 12, 8), 19 | from: new Date(2016, 12, 4) 20 | }, 21 | disabledDates: { 22 | dates: [ new Date(2016, 12, 5) ] 23 | } 24 | } 25 | }) 26 | }) 27 | 28 | it('should detect a highlighted date', () => { 29 | expect(wrapper.vm.isHighlightedDate(new Date(2006, 9, 2))).toEqual(false) 30 | expect(wrapper.vm.isHighlightedDate(new Date(2026, 9, 2))).toEqual(false) 31 | }) 32 | 33 | it('should not highlight a disabled date', () => { 34 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 12, 5))).toEqual(false) 35 | }) 36 | 37 | it('should highlight a disabled date when explicitly configured to', async () => { 38 | await wrapper.setProps({ 39 | highlighted: { 40 | to: new Date(2016, 12, 8), 41 | from: new Date(2016, 12, 4), 42 | includeDisabled: true 43 | } 44 | }) 45 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 12, 5))).toEqual(true) 46 | }) 47 | 48 | it('should highlight a date before the to property', () => { 49 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 12, 7))).toEqual(true) 50 | }) 51 | 52 | it('should not highlight a date after the to property', () => { 53 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 12, 9))).toEqual(false) 54 | }) 55 | 56 | it('should highlight a date after the from property', () => { 57 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 12, 6))).toEqual(true) 58 | }) 59 | 60 | it('should not highlight a date before the from property', () => { 61 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 12, 3))).toEqual(false) 62 | }) 63 | 64 | it('can accept an array of highlighted dates', async () => { 65 | await wrapper.setProps({ 66 | highlighted: { 67 | dates: [ 68 | new Date(2016, 9, 2), 69 | new Date(2016, 9, 9), 70 | new Date(2016, 9, 16) 71 | ] 72 | } 73 | }) 74 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 9, 2))).toEqual(true) 75 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 9, 3))).toEqual(false) 76 | }) 77 | 78 | it('can accept an array of highlighted days of the week', async () => { 79 | await wrapper.setProps({ 80 | highlighted: { 81 | days: [6, 0] 82 | } 83 | }) 84 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 9, 2))).toEqual(true) 85 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 9, 3))).toEqual(false) 86 | }) 87 | 88 | it('can accept an array of highlighted days of the month', async () => { 89 | await wrapper.setProps({ 90 | highlighted: { 91 | daysOfMonth: [1, 10, 31] 92 | } 93 | }) 94 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 9, 1))).toEqual(true) 95 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 10, 10))).toEqual(true) 96 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 11, 31))).toEqual(true) 97 | expect(wrapper.vm.isHighlightedDate(new Date(2017, 8, 10))).toEqual(true) 98 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 8, 7))).toEqual(false) 99 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 7, 20))).toEqual(false) 100 | }) 101 | 102 | it('can accept a customPredictor to check if the date is highlighted', async () => { 103 | await wrapper.setProps({ 104 | highlighted: { 105 | customPredictor (date) { 106 | if (date.getDate() % 5 === 0) { 107 | return true 108 | } 109 | } 110 | } 111 | }) 112 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 8, 30))).toEqual(true) 113 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 9, 28))).toEqual(false) 114 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 10, 20))).toEqual(true) 115 | expect(wrapper.vm.isHighlightedDate(new Date(2016, 9, 11))).toEqual(false) 116 | }) 117 | 118 | it('should detect the first date of the highlighted dates', () => { 119 | expect(wrapper.vm.isHighlightStart(new Date(2016, 12, 4))).toEqual(true) 120 | expect(wrapper.vm.isHighlightStart(new Date(2016, 12, 3))).toEqual(false) 121 | expect(wrapper.vm.isHighlightStart(new Date(2016, 12, 5))).toEqual(false) 122 | }) 123 | 124 | it('should detect the last date of the highlighted dates', () => { 125 | expect(wrapper.vm.isHighlightEnd(new Date(2016, 12, 8))).toEqual(true) 126 | expect(wrapper.vm.isHighlightEnd(new Date(2016, 12, 6))).toEqual(false) 127 | expect(wrapper.vm.isHighlightEnd(new Date(2016, 12, 7))).toEqual(false) 128 | }) 129 | }) 130 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files src

23 |
24 | 25 |
26 | 0% 27 | Statements 28 | 0/19 29 |
30 | 31 | 32 |
33 | 100% 34 | Branches 35 | 0/0 36 |
37 | 38 | 39 |
40 | 0% 41 | Functions 42 | 0/10 43 |
44 | 45 | 46 |
47 | 0% 48 | Lines 49 | 0/19 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 |
FileStatementsBranchesFunctionsLines
App.vue 78 |
79 |
0%0/13100%0/00%0/100%0/13
main.ts 93 |
94 |
0%0/6100%0/0100%0/00%0/6
107 |
108 |
109 |
110 | 115 | 116 | 117 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/src/shared/enum.ts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src/shared/enum.ts 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files / src/shared enum.ts

23 |
24 | 25 |
26 | 0% 27 | Statements 28 | 0/10 29 |
30 | 31 | 32 |
33 | 0% 34 | Branches 35 | 0/6 36 |
37 | 38 | 39 |
40 | 0% 41 | Functions 42 | 0/3 43 |
44 | 45 | 46 |
47 | 0% 48 | Lines 49 | 0/10 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |

 60 | 
1 61 | 2 62 | 3 63 | 4 64 | 5 65 | 6 66 | 7 67 | 8 68 | 9 69 | 10 70 | 11 71 | 12 72 | 13 73 | 14 74 | 15 75 | 16  76 |   77 |   78 |   79 |   80 |   81 |   82 |   83 |   84 |   85 |   86 |   87 |   88 |   89 |   90 |  
export enum ValidationMessages {
 91 |   required = 'Please enter this value',
 92 | }
 93 |  
 94 | export enum VALIDATORS {
 95 |   REQUIRED = 'required',
 96 | }
 97 |  
 98 | export enum KeyName {
 99 |   Enter = 'Enter',
100 |   ArrowUp = 'ArrowUp',
101 |   ArrowDown = 'ArrowDown',
102 |   Escape = 'Escape',
103 |   Tab = 'Tab',
104 | }
105 |  
106 | 107 |
108 |
109 | 114 | 115 | 116 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/components/datepicker/datepicker.css: -------------------------------------------------------------------------------- 1 | @import "../../assets/styles/css/_settings.css"; 2 | 3 | .rtl { 4 | direction: rtl; 5 | } 6 | 7 | .vuejs3-datepicker { 8 | color: $black; 9 | position: relative; 10 | display: inline-block; 11 | * { 12 | box-sizing: border-box; 13 | } 14 | input { 15 | border: 1px solid; 16 | } 17 | &__icon { 18 | display: flex; 19 | } 20 | &__value { 21 | min-width: 200px; 22 | display: inline-flex; 23 | border-radius: 5px; 24 | padding: 13px 15px; 25 | cursor: pointer; 26 | border: 1px solid ; 27 | } 28 | &__content{ 29 | margin-left: 10px; 30 | font-size: 15px; 31 | } 32 | &__typeablecalendar{ 33 | position: absolute; 34 | top: 10px; 35 | left: 10px; 36 | } 37 | &__inputvalue{ 38 | min-width: 200px; 39 | display: inline-flex; 40 | border-radius: 5px; 41 | padding: 12px 10px 13px 35px; 42 | cursor: pointer; 43 | border: 1px solid ; 44 | } 45 | &__calendar { 46 | position: absolute; 47 | z-index: 100; 48 | background: $white; 49 | width: 300px; 50 | box-shadow: 0 0.2rem 1rem rgba(0,0,0,.12); 51 | border-radius: 4px; 52 | margin-top: 4px; 53 | &-topbar{ 54 | background-color: $green; 55 | color: $white; 56 | border-radius: 4px 4px 0px 0px; 57 | padding: 25px; 58 | text-align: center; 59 | &-year{ 60 | cursor: pointer; 61 | font-size: 30px; 62 | margin: 0px; 63 | padding-bottom: 10px; 64 | } 65 | &-day{ 66 | font-size: 20px; 67 | margin: 0px; 68 | } 69 | } 70 | &-actionarea{ 71 | padding: 10px; 72 | } 73 | header { 74 | display: block; 75 | line-height: 40px; 76 | span { 77 | text-align: center; 78 | width: 71.42857142857143%; 79 | float: left; 80 | } 81 | .next, .prev { 82 | width: 14.285714285714286%; 83 | float: left; 84 | text-indent: -10000px; 85 | position: relative; 86 | &:after { 87 | content: ''; 88 | position: absolute; 89 | left: 50%; 90 | width: 8px; 91 | height: 8px; 92 | top: 50%; 93 | transform: translateX(-50%) translateY(-50%) rotate(45deg); 94 | } 95 | } 96 | .prev:after { 97 | border-left: 1px solid $black; 98 | border-bottom: 1px solid $black; 99 | } 100 | .prev.disabled:after, .next.disabled:after{ 101 | opacity: 0.5; 102 | } 103 | .next:after { 104 | border-top: 1px solid $black; 105 | border-right: 1px solid $black; 106 | } 107 | .prev:not(.disabled), .next:not(.disabled), .up:not(.disabled){ 108 | cursor: pointer; 109 | font-size: 15px; 110 | border-radius: 4px; 111 | &:hover{ 112 | background: $hovered; 113 | } 114 | } 115 | } 116 | .disabled { 117 | color: #ddd; 118 | cursor: default; 119 | } 120 | .flex-rtl { 121 | display: flex; 122 | width: inherit; 123 | flex-wrap: wrap; 124 | } 125 | .cell { 126 | display: inline-block; 127 | padding: 0 5px; 128 | width: 14.285714285714286%; 129 | height: 40px; 130 | line-height: 40px; 131 | text-align: center; 132 | font-size: 14px; 133 | vertical-align: middle; 134 | border: 1px solid transparent; 135 | &.month, &.year{ 136 | padding: 10px 5px; 137 | height: 50px; 138 | line-height: 28px; 139 | } 140 | &.day-header{ 141 | text-transform: uppercase; 142 | } 143 | } 144 | .cell:not(.blank):not(.disabled).day, .cell:not(.blank):not(.disabled).month, .cell:not(.blank):not(.disabled).year{ 145 | cursor: pointer; 146 | transition: 0.45s; 147 | &:hover{ 148 | border: 1px solid $green; 149 | } 150 | } 151 | .cell.selected { 152 | background: $green; 153 | color: $white; 154 | &:hover{ 155 | background: $green; 156 | } 157 | } 158 | .cell.highlighted { 159 | background: $green; 160 | color: $white; 161 | &.selected { 162 | background: $green; 163 | } 164 | &.disabled { 165 | color: #a3a3a3; 166 | } 167 | &.highlight-start, &:last-child { 168 | background: $darkGreen; 169 | } 170 | } 171 | .cell.grey { 172 | color: #888; 173 | &:hover{ 174 | background: inherit; 175 | } 176 | } 177 | .cell.day-header { 178 | font-size: 75%; 179 | white-space: nowrap; 180 | cursor: inherit; 181 | &:hover{ 182 | background: inherit; 183 | } 184 | } 185 | .month, .year{ 186 | width: 33.333%; 187 | } 188 | } 189 | &__clear-button, &__calendar-button{ 190 | cursor: pointer; 191 | font-style: normal; 192 | position: absolute; 193 | top: 12px; 194 | right:12px; 195 | &.disabled { 196 | color: #999; 197 | cursor: default; 198 | } 199 | } 200 | #calendar-div{ 201 | background-color: white; 202 | border-radius:5px; 203 | } 204 | } 205 | 206 | .dp-error { 207 | color: red; 208 | font-size: 12px 209 | } 210 | 211 | .backdrop{ 212 | position: fixed; /* Sit on top of the page content */ 213 | display: none; /* Hidden by default */ 214 | width: 100%; /* Full width (cover the whole page) */ 215 | height: 100%; /* Full height (cover the whole page) */ 216 | top: 0; 217 | left: 0; 218 | right: 0; 219 | bottom: 0; 220 | background-color: rgba(0,0,0,0.5); /* Black background with opacity */ 221 | z-index: 2; /* Specify a stack order in case you're using a different order for other elements */ 222 | cursor: pointer; /* Add a pointer on hover */ 223 | } -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/sorter.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var addSorting = (function() { 3 | 'use strict'; 4 | var cols, 5 | currentSort = { 6 | index: 0, 7 | desc: false 8 | }; 9 | 10 | // returns the summary table element 11 | function getTable() { 12 | return document.querySelector('.coverage-summary'); 13 | } 14 | // returns the thead element of the summary table 15 | function getTableHeader() { 16 | return getTable().querySelector('thead tr'); 17 | } 18 | // returns the tbody element of the summary table 19 | function getTableBody() { 20 | return getTable().querySelector('tbody'); 21 | } 22 | // returns the th element for nth column 23 | function getNthColumn(n) { 24 | return getTableHeader().querySelectorAll('th')[n]; 25 | } 26 | 27 | // loads all columns 28 | function loadColumns() { 29 | var colNodes = getTableHeader().querySelectorAll('th'), 30 | colNode, 31 | cols = [], 32 | col, 33 | i; 34 | 35 | for (i = 0; i < colNodes.length; i += 1) { 36 | colNode = colNodes[i]; 37 | col = { 38 | key: colNode.getAttribute('data-col'), 39 | sortable: !colNode.getAttribute('data-nosort'), 40 | type: colNode.getAttribute('data-type') || 'string' 41 | }; 42 | cols.push(col); 43 | if (col.sortable) { 44 | col.defaultDescSort = col.type === 'number'; 45 | colNode.innerHTML = 46 | colNode.innerHTML + ''; 47 | } 48 | } 49 | return cols; 50 | } 51 | // attaches a data attribute to every tr element with an object 52 | // of data values keyed by column name 53 | function loadRowData(tableRow) { 54 | var tableCols = tableRow.querySelectorAll('td'), 55 | colNode, 56 | col, 57 | data = {}, 58 | i, 59 | val; 60 | for (i = 0; i < tableCols.length; i += 1) { 61 | colNode = tableCols[i]; 62 | col = cols[i]; 63 | val = colNode.getAttribute('data-value'); 64 | if (col.type === 'number') { 65 | val = Number(val); 66 | } 67 | data[col.key] = val; 68 | } 69 | return data; 70 | } 71 | // loads all row data 72 | function loadData() { 73 | var rows = getTableBody().querySelectorAll('tr'), 74 | i; 75 | 76 | for (i = 0; i < rows.length; i += 1) { 77 | rows[i].data = loadRowData(rows[i]); 78 | } 79 | } 80 | // sorts the table using the data for the ith column 81 | function sortByIndex(index, desc) { 82 | var key = cols[index].key, 83 | sorter = function(a, b) { 84 | a = a.data[key]; 85 | b = b.data[key]; 86 | return a < b ? -1 : a > b ? 1 : 0; 87 | }, 88 | finalSorter = sorter, 89 | tableBody = document.querySelector('.coverage-summary tbody'), 90 | rowNodes = tableBody.querySelectorAll('tr'), 91 | rows = [], 92 | i; 93 | 94 | if (desc) { 95 | finalSorter = function(a, b) { 96 | return -1 * sorter(a, b); 97 | }; 98 | } 99 | 100 | for (i = 0; i < rowNodes.length; i += 1) { 101 | rows.push(rowNodes[i]); 102 | tableBody.removeChild(rowNodes[i]); 103 | } 104 | 105 | rows.sort(finalSorter); 106 | 107 | for (i = 0; i < rows.length; i += 1) { 108 | tableBody.appendChild(rows[i]); 109 | } 110 | } 111 | // removes sort indicators for current column being sorted 112 | function removeSortIndicators() { 113 | var col = getNthColumn(currentSort.index), 114 | cls = col.className; 115 | 116 | cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); 117 | col.className = cls; 118 | } 119 | // adds sort indicators for current column being sorted 120 | function addSortIndicators() { 121 | getNthColumn(currentSort.index).className += currentSort.desc 122 | ? ' sorted-desc' 123 | : ' sorted'; 124 | } 125 | // adds event listeners for all sorter widgets 126 | function enableUI() { 127 | var i, 128 | el, 129 | ithSorter = function ithSorter(i) { 130 | var col = cols[i]; 131 | 132 | return function() { 133 | var desc = col.defaultDescSort; 134 | 135 | if (currentSort.index === i) { 136 | desc = !currentSort.desc; 137 | } 138 | sortByIndex(i, desc); 139 | removeSortIndicators(); 140 | currentSort.index = i; 141 | currentSort.desc = desc; 142 | addSortIndicators(); 143 | }; 144 | }; 145 | for (i = 0; i < cols.length; i += 1) { 146 | if (cols[i].sortable) { 147 | // add the click event handler on the th so users 148 | // dont have to click on those tiny arrows 149 | el = getNthColumn(i).querySelector('.sorter').parentElement; 150 | if (el.addEventListener) { 151 | el.addEventListener('click', ithSorter(i)); 152 | } else { 153 | el.attachEvent('onclick', ithSorter(i)); 154 | } 155 | } 156 | } 157 | } 158 | // adds sorting functionality to the UI 159 | return function() { 160 | if (!getTable()) { 161 | return; 162 | } 163 | cols = loadColumns(); 164 | loadData(); 165 | addSortIndicators(); 166 | enableUI(); 167 | }; 168 | })(); 169 | 170 | window.addEventListener('load', addSorting); 171 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/src/shared/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src/shared 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files src/shared

23 |
24 | 25 |
26 | 0% 27 | Statements 28 | 0/47 29 |
30 | 31 | 32 |
33 | 0% 34 | Branches 35 | 0/33 36 |
37 | 38 | 39 |
40 | 0% 41 | Functions 42 | 0/7 43 |
44 | 45 | 46 |
47 | 0% 48 | Lines 49 | 0/45 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 |
FileStatementsBranchesFunctionsLines
enum.ts 78 |
79 |
0%0/100%0/60%0/30%0/10
utils.ts 93 |
94 |
0%0/20%0/20%0/10%0/2
validations.ts 108 |
109 |
0%0/350%0/250%0/30%0/33
122 |
123 |
124 |
125 | 130 | 131 | 132 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for All files 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files

23 |
24 | 25 |
26 | 96.03% 27 | Statements 28 | 556/579 29 |
30 | 31 | 32 |
33 | 91.74% 34 | Branches 35 | 422/460 36 |
37 | 38 | 39 |
40 | 93.48% 41 | Functions 42 | 129/138 43 |
44 | 45 | 46 |
47 | 95.94% 48 | Lines 49 | 544/567 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 |
FileStatementsBranchesFunctionsLines
datepicker 78 |
79 |
95.71%446/46691.14%329/36192.24%107/11695.68%443/463
datepicker/locale 93 |
94 |
100%20/20100%0/0100%2/2100%20/20
datepicker/utils 108 |
109 |
96.77%90/9393.94%93/99100%20/2096.43%81/84
122 |
123 |
124 |
125 | 130 | 131 | 132 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/base.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | margin:0; padding: 0; 3 | height: 100%; 4 | } 5 | body { 6 | font-family: Helvetica Neue, Helvetica, Arial; 7 | font-size: 14px; 8 | color:#333; 9 | } 10 | .small { font-size: 12px; } 11 | *, *:after, *:before { 12 | -webkit-box-sizing:border-box; 13 | -moz-box-sizing:border-box; 14 | box-sizing:border-box; 15 | } 16 | h1 { font-size: 20px; margin: 0;} 17 | h2 { font-size: 14px; } 18 | pre { 19 | font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; 20 | margin: 0; 21 | padding: 0; 22 | -moz-tab-size: 2; 23 | -o-tab-size: 2; 24 | tab-size: 2; 25 | } 26 | a { color:#0074D9; text-decoration:none; } 27 | a:hover { text-decoration:underline; } 28 | .strong { font-weight: bold; } 29 | .space-top1 { padding: 10px 0 0 0; } 30 | .pad2y { padding: 20px 0; } 31 | .pad1y { padding: 10px 0; } 32 | .pad2x { padding: 0 20px; } 33 | .pad2 { padding: 20px; } 34 | .pad1 { padding: 10px; } 35 | .space-left2 { padding-left:55px; } 36 | .space-right2 { padding-right:20px; } 37 | .center { text-align:center; } 38 | .clearfix { display:block; } 39 | .clearfix:after { 40 | content:''; 41 | display:block; 42 | height:0; 43 | clear:both; 44 | visibility:hidden; 45 | } 46 | .fl { float: left; } 47 | @media only screen and (max-width:640px) { 48 | .col3 { width:100%; max-width:100%; } 49 | .hide-mobile { display:none!important; } 50 | } 51 | 52 | .quiet { 53 | color: #7f7f7f; 54 | color: rgba(0,0,0,0.5); 55 | } 56 | .quiet a { opacity: 0.7; } 57 | 58 | .fraction { 59 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; 60 | font-size: 10px; 61 | color: #555; 62 | background: #E8E8E8; 63 | padding: 4px 5px; 64 | border-radius: 3px; 65 | vertical-align: middle; 66 | } 67 | 68 | div.path a:link, div.path a:visited { color: #333; } 69 | table.coverage { 70 | border-collapse: collapse; 71 | margin: 10px 0 0 0; 72 | padding: 0; 73 | } 74 | 75 | table.coverage td { 76 | margin: 0; 77 | padding: 0; 78 | vertical-align: top; 79 | } 80 | table.coverage td.line-count { 81 | text-align: right; 82 | padding: 0 5px 0 20px; 83 | } 84 | table.coverage td.line-coverage { 85 | text-align: right; 86 | padding-right: 10px; 87 | min-width:20px; 88 | } 89 | 90 | table.coverage td span.cline-any { 91 | display: inline-block; 92 | padding: 0 5px; 93 | width: 100%; 94 | } 95 | .missing-if-branch { 96 | display: inline-block; 97 | margin-right: 5px; 98 | border-radius: 3px; 99 | position: relative; 100 | padding: 0 4px; 101 | background: #333; 102 | color: yellow; 103 | } 104 | 105 | .skip-if-branch { 106 | display: none; 107 | margin-right: 10px; 108 | position: relative; 109 | padding: 0 4px; 110 | background: #ccc; 111 | color: white; 112 | } 113 | .missing-if-branch .typ, .skip-if-branch .typ { 114 | color: inherit !important; 115 | } 116 | .coverage-summary { 117 | border-collapse: collapse; 118 | width: 100%; 119 | } 120 | .coverage-summary tr { border-bottom: 1px solid #bbb; } 121 | .keyline-all { border: 1px solid #ddd; } 122 | .coverage-summary td, .coverage-summary th { padding: 10px; } 123 | .coverage-summary tbody { border: 1px solid #bbb; } 124 | .coverage-summary td { border-right: 1px solid #bbb; } 125 | .coverage-summary td:last-child { border-right: none; } 126 | .coverage-summary th { 127 | text-align: left; 128 | font-weight: normal; 129 | white-space: nowrap; 130 | } 131 | .coverage-summary th.file { border-right: none !important; } 132 | .coverage-summary th.pct { } 133 | .coverage-summary th.pic, 134 | .coverage-summary th.abs, 135 | .coverage-summary td.pct, 136 | .coverage-summary td.abs { text-align: right; } 137 | .coverage-summary td.file { white-space: nowrap; } 138 | .coverage-summary td.pic { min-width: 120px !important; } 139 | .coverage-summary tfoot td { } 140 | 141 | .coverage-summary .sorter { 142 | height: 10px; 143 | width: 7px; 144 | display: inline-block; 145 | margin-left: 0.5em; 146 | background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; 147 | } 148 | .coverage-summary .sorted .sorter { 149 | background-position: 0 -20px; 150 | } 151 | .coverage-summary .sorted-desc .sorter { 152 | background-position: 0 -10px; 153 | } 154 | .status-line { height: 10px; } 155 | /* yellow */ 156 | .cbranch-no { background: yellow !important; color: #111; } 157 | /* dark red */ 158 | .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } 159 | .low .chart { border:1px solid #C21F39 } 160 | .highlighted, 161 | .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ 162 | background: #C21F39 !important; 163 | } 164 | /* medium red */ 165 | .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } 166 | /* light red */ 167 | .low, .cline-no { background:#FCE1E5 } 168 | /* light green */ 169 | .high, .cline-yes { background:rgb(230,245,208) } 170 | /* medium green */ 171 | .cstat-yes { background:rgb(161,215,106) } 172 | /* dark green */ 173 | .status-line.high, .high .cover-fill { background:rgb(77,146,33) } 174 | .high .chart { border:1px solid rgb(77,146,33) } 175 | /* dark yellow (gold) */ 176 | .status-line.medium, .medium .cover-fill { background: #f9cd0b; } 177 | .medium .chart { border:1px solid #f9cd0b; } 178 | /* light yellow */ 179 | .medium { background: #fff4c2; } 180 | 181 | .cstat-skip { background: #ddd; color: #111; } 182 | .fstat-skip { background: #ddd; color: #111 !important; } 183 | .cbranch-skip { background: #ddd !important; color: #111; } 184 | 185 | span.cline-neutral { background: #eaeaea; } 186 | 187 | .coverage-summary td.empty { 188 | opacity: .5; 189 | padding-top: 4px; 190 | padding-bottom: 4px; 191 | line-height: 1; 192 | color: #888; 193 | } 194 | 195 | .cover-fill, .cover-empty { 196 | display:inline-block; 197 | height: 12px; 198 | } 199 | .chart { 200 | line-height: 0; 201 | } 202 | .cover-empty { 203 | background: white; 204 | } 205 | .cover-full { 206 | border-right: none !important; 207 | } 208 | pre.prettyprint { 209 | border: none !important; 210 | padding: 0 !important; 211 | margin: 0 !important; 212 | } 213 | .com { color: #999 !important; } 214 | .ignore-none { color: #999; font-weight: normal; } 215 | 216 | .wrapper { 217 | min-height: 100%; 218 | height: auto !important; 219 | height: 100%; 220 | margin: 0 auto -48px; 221 | } 222 | .footer, .push { 223 | height: 48px; 224 | } 225 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/src/components/datepicker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for src/components/datepicker 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files src/components/datepicker

23 |
24 | 25 |
26 | 15.18% 27 | Statements 28 | 68/448 29 |
30 | 31 | 32 |
33 | 13.14% 34 | Branches 35 | 46/350 36 |
37 | 38 | 39 |
40 | 15.65% 41 | Functions 42 | 18/115 43 |
44 | 45 | 46 |
47 | 15.06% 48 | Lines 49 | 67/445 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 |
FileStatementsBranchesFunctionsLines
DateInput.vue 78 |
79 |
0%0/420%0/260%0/90%0/42
Datepicker.vue 93 |
94 |
0%0/1480%0/620%0/360%0/148
PickerDay.vue 108 |
109 |
0%0/1200%0/1410%0/310%0/119
PickerMonth.vue 123 |
124 |
0%0/680%0/660%0/200%0/67
PickerYear.vue 138 |
139 |
97.14%68/7083.64%46/5594.74%18/1997.1%67/69
152 |
153 |
154 |
155 | 160 | 161 | 162 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /tests/unit/coverage/lcov-report/datepicker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Code coverage report for datepicker 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 |
21 |
22 |

All files datepicker

23 |
24 | 25 |
26 | 95.71% 27 | Statements 28 | 446/466 29 |
30 | 31 | 32 |
33 | 91.14% 34 | Branches 35 | 329/361 36 |
37 | 38 | 39 |
40 | 92.24% 41 | Functions 42 | 107/116 43 |
44 | 45 | 46 |
47 | 95.68% 48 | Lines 49 | 443/463 50 |
51 | 52 | 53 |
54 |

55 | Press n or j to go to the next uncovered block, b, p or k for the previous block. 56 |

57 |
58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 |
FileStatementsBranchesFunctionsLines
DateInput.vue 78 |
79 |
100%47/4793.75%30/32100%9/9100%47/47
Datepicker.vue 93 |
94 |
91.89%136/14891.94%57/6283.33%30/3691.89%136/148
PickerDay.vue 108 |
109 |
97.66%125/12893.15%136/14696.88%31/3297.64%124/127
PickerMonth.vue 123 |
124 |
95.71%67/7086.36%57/6695%19/2095.65%66/69
PickerYear.vue 138 |
139 |
97.26%71/7389.09%49/5594.74%18/1997.22%70/72
152 |
153 |
154 |
155 | 160 | 161 | 162 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /tests/unit/specs/DateUtils.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | isValidDate, 3 | getMonthName, 4 | getMonthNameAbbr, 5 | getDayNameAbbr, 6 | setFullYear, 7 | setMonth, 8 | setDate, 9 | getMinutes, 10 | getHours, 11 | getDay, 12 | daysInMonth, 13 | formatDate, 14 | createDateArray, 15 | getFullYear, 16 | getMonth, 17 | getDate, 18 | } from '@/components/datepicker/utils/DateUtils'; 19 | import * as Langlist from '@/components/datepicker/locale'; 20 | 21 | let en; 22 | 23 | describe('DateUtils', () => { 24 | beforeEach(()=>{ 25 | en = Langlist.data['en']; 26 | }) 27 | it('should detect invalid date object', () => { 28 | expect(isValidDate(null)).toEqual(false); 29 | expect(isValidDate(123)).toEqual(false); 30 | expect(isValidDate('abc')).toEqual(false); 31 | expect(isValidDate({})).toEqual(false); 32 | expect(isValidDate(new Date())).toEqual(true); 33 | }); 34 | 35 | it('should give correct days in a month', () => { 36 | expect(daysInMonth(2020, 0)).toEqual(31); 37 | expect(daysInMonth(2020, 1)).toEqual(29); 38 | expect(daysInMonth(2015, 1)).toEqual(28); 39 | expect(daysInMonth(2020, 2)).toEqual(31); 40 | expect(daysInMonth(2020, 3)).toEqual(30); 41 | expect(daysInMonth(2020, 4)).toEqual(31); 42 | expect(daysInMonth(2020, 5)).toEqual(30); 43 | expect(daysInMonth(2020, 6)).toEqual(31); 44 | expect(daysInMonth(2020, 7)).toEqual(31); 45 | expect(daysInMonth(2020, 8)).toEqual(30); 46 | expect(daysInMonth(2020, 9)).toEqual(31); 47 | expect(daysInMonth(2020, 10)).toEqual(30); 48 | expect(daysInMonth(2020, 11)).toEqual(31); 49 | }); 50 | 51 | it('should format date strings correctly in English', () => { 52 | expect(formatDate(new Date(2020, 0, 1), 'd MMMM yyyy', en)).toEqual('1 January 2020'); 53 | expect(formatDate(new Date(2020, 0, 9), 'dd MMM yyyy', en)).toEqual('09 Jan 2020'); 54 | expect(formatDate(new Date(2020, 0, 9), 'dd MMM yy', en)).toEqual('09 Jan 20'); 55 | expect(formatDate(new Date(2020, 2, 9), 'yyyy-MM-dd', en)).toEqual('2020-03-09'); 56 | expect(formatDate(new Date(2020, 2, 9), 'dsu MMMM yyyy', en)).toEqual('9th March 2020'); 57 | expect(formatDate(new Date(2020, 2, 1), 'dsu MMMM yyyy', en)).toEqual('1st March 2020'); 58 | expect(formatDate(new Date(2020, 2, 2), 'dsu MMMM yyyy', en)).toEqual('2nd March 2020'); 59 | expect(formatDate(new Date(2020, 2, 3), 'dsu MMMM yyyy', en)).toEqual('3rd March 2020'); 60 | expect(formatDate(new Date(2020, 7, 2), 'D dsu MMMM yyyy', en)).toEqual('Sun 2nd August 2020'); 61 | expect(formatDate(new Date(2020, 8, 1), 'D dsu MMMM yyyy', en)).toEqual('Tue 1st September 2020'); 62 | expect(formatDate(new Date(2020, 7, 7), 'D dsu MMMM yyyy', en)).toEqual('Fri 7th August 2020'); 63 | expect(formatDate(new Date(2020, 11, 2), 'dd MMM yyyy', en)).toEqual('02 Dec 2020'); 64 | }); 65 | 66 | it('should give the correct day', () => { 67 | expect(formatDate(new Date(2020, 0, 1), 'D', en)).toEqual('Wed'); 68 | }); 69 | 70 | it('can create an array of dates', () => { 71 | const start = new Date(2020, 9, 12); 72 | const end = new Date(2020, 9, 16); 73 | const dates = createDateArray(start, end); 74 | expect(dates.length).toEqual(5); 75 | let day = 12; 76 | dates.forEach((date) => { 77 | expect(date.getDate()).toEqual(day); 78 | day++; 79 | }); 80 | }); 81 | 82 | it('gives days in a month', () => { 83 | expect(daysInMonth(2020, 0)).toEqual(31); 84 | expect(daysInMonth(2020, 1)).toEqual(29); 85 | expect(daysInMonth(2020, 2)).toEqual(31); 86 | }); 87 | 88 | it('getDayNameAbbr moans if date is not a Date object', () => { 89 | expect(() => getDayNameAbbr(123, en.months)).toThrow(TypeError); 90 | }); 91 | 92 | it('getMonthName moans if date is not a Date object', () => { 93 | expect(() => getMonthName('string', en.months)).toThrow(TypeError); 94 | }); 95 | 96 | it('getMonthName complains if missing months array', () => { 97 | expect(() => getMonthName(new Date())).toThrow(Error); 98 | }); 99 | 100 | it('getMonthName accepts a number', () => { 101 | expect(getMonthName(3, en.months)).toEqual('April'); 102 | }); 103 | 104 | it('getMonthName accepts a Date object', () => { 105 | expect(getMonthName(new Date(2016, 9, 10), en.months)).toEqual('October'); 106 | }); 107 | 108 | it('getMonthNameAbbr moans if date is not a Date object', () => { 109 | expect(() => getMonthNameAbbr('abc', en.months)).toThrow(TypeError); 110 | }); 111 | 112 | it('getMonthNameAbbr complains if missing months array', () => { 113 | expect(() => getMonthNameAbbr(new Date())).toThrow(Error); 114 | }); 115 | 116 | it('getMonthNameAbbr accepts a Date object', () => { 117 | expect(getMonthNameAbbr(new Date(2016, 9, 10), en.monthsAbbr)).toEqual('Oct'); 118 | }); 119 | 120 | it('getMonthName accepts a number', () => { 121 | expect(getMonthNameAbbr(3, en.monthsAbbr)).toEqual('Apr'); 122 | }); 123 | }); 124 | 125 | describe('daysInMonth', () => { 126 | it('should give the correct days in a month', () => { 127 | expect(daysInMonth(2017, 0)).toEqual(31); // Jan 128 | expect(daysInMonth(2017, 1)).toEqual(28); // Feb 129 | expect(daysInMonth(2017, 2)).toEqual(31); // Mar 130 | expect(daysInMonth(2017, 3)).toEqual(30); // Apr 131 | expect(daysInMonth(2017, 4)).toEqual(31); // May 132 | expect(daysInMonth(2017, 5)).toEqual(30); // Jun 133 | expect(daysInMonth(2017, 6)).toEqual(31); // Jul 134 | expect(daysInMonth(2017, 7)).toEqual(31); // Aug 135 | expect(daysInMonth(2017, 8)).toEqual(30); // Sep 136 | expect(daysInMonth(2017, 9)).toEqual(31); // Oct 137 | expect(daysInMonth(2017, 10)).toEqual(30); // Nov 138 | expect(daysInMonth(2017, 11)).toEqual(31); // Dec 139 | }); 140 | }); 141 | 142 | const getAmbiguousDate = (_) => { 143 | const timezoneOffset = new Date().getTimezoneOffset() / 60; 144 | const ambiguousHour = 25 - timezoneOffset; 145 | const ambiguousDate = new Date(2018, 11, 31, ambiguousHour); 146 | return ambiguousDate; 147 | }; 148 | 149 | describe('UTC functions', () => { 150 | // const utcUtils = makeDateUtils(true) 151 | 152 | it('getFullYear', () => { 153 | const date = getAmbiguousDate(); 154 | expect(getFullYear(date)).toEqual(date.getFullYear()); 155 | expect(getFullYear(date, true)).toEqual(date.getUTCFullYear()); 156 | }); 157 | 158 | it('getMonth', () => { 159 | const date = getAmbiguousDate(); 160 | expect(getMonth(date)).toEqual(date.getMonth()); 161 | expect(getMonth(date, true)).toEqual(date.getUTCMonth()); 162 | }); 163 | 164 | it('getDate', () => { 165 | const date = getAmbiguousDate(); 166 | expect(getDate(date)).toEqual(date.getDate()); 167 | expect(getDate(date, true)).toEqual(date.getUTCDate()); 168 | }); 169 | 170 | it('getDay', () => { 171 | const date = getAmbiguousDate(); 172 | expect(getDay(date)).toEqual(date.getDay()); 173 | expect(getDay(date, true)).toEqual(date.getUTCDay()); 174 | }); 175 | 176 | it('getHours', () => { 177 | const date = getAmbiguousDate(); 178 | expect(getHours(date)).toEqual(date.getHours()); 179 | expect(getHours(date, true)).toEqual(date.getUTCHours()); 180 | }); 181 | 182 | it('getMinutes', () => { 183 | const date = getAmbiguousDate(); 184 | expect(getMinutes(date)).toEqual(date.getMinutes()); 185 | expect(getMinutes(date, true)).toEqual(date.getUTCMinutes()); 186 | }); 187 | 188 | it('setFullYear', () => { 189 | const date = getAmbiguousDate(); 190 | expect(setFullYear(date, 2018)).toEqual(date.setFullYear(2018)); 191 | expect(setFullYear(date, 2018, true)).toEqual(date.setUTCFullYear(2018)); 192 | }); 193 | 194 | it('setMonth', () => { 195 | const date = getAmbiguousDate(); 196 | expect(setMonth(date, 11)).toEqual(date.setMonth(11)); 197 | expect(setMonth(date, 11, true)).toEqual(date.setUTCMonth(11)); 198 | }); 199 | 200 | it('setDate', () => { 201 | const date = getAmbiguousDate(); 202 | expect(setDate(date, 31)).toEqual(date.setDate(31)); 203 | expect(setDate(date, 31, true)).toEqual(date.setUTCDate(31)); 204 | }); 205 | 206 | }); 207 | --------------------------------------------------------------------------------