Commit 68e364211969e574fc824e8c0a543bec5fc1d948
Committed by
GitHub
Merge pull request #1712 from SergioCrisostomo/add-more-datepicker-tests
Refactor cell click handler and add more date picker and date range picker tests
Showing
3 changed files
with
157 additions
and
82 deletions
Show diff stats
src/components/date-picker/base/date-table.vue
| 1 | <template> | 1 | <template> |
| 2 | <div | 2 | <div |
| 3 | :class="classes" | 3 | :class="classes" |
| 4 | - @click="handleClick" | ||
| 5 | @mousemove="handleMouseMove"> | 4 | @mousemove="handleMouseMove"> |
| 6 | <div :class="[prefixCls + '-header']"> | 5 | <div :class="[prefixCls + '-header']"> |
| 7 | <span>{{ t('i.datepicker.weeks.sun') }}</span><span>{{ t('i.datepicker.weeks.mon') }}</span><span>{{ t('i.datepicker.weeks.tue') }}</span><span>{{ t('i.datepicker.weeks.wed') }}</span><span>{{ t('i.datepicker.weeks.thu') }}</span><span>{{ t('i.datepicker.weeks.fri') }}</span><span>{{ t('i.datepicker.weeks.sat') }}</span> | 6 | <span>{{ t('i.datepicker.weeks.sun') }}</span><span>{{ t('i.datepicker.weeks.mon') }}</span><span>{{ t('i.datepicker.weeks.tue') }}</span><span>{{ t('i.datepicker.weeks.wed') }}</span><span>{{ t('i.datepicker.weeks.thu') }}</span><span>{{ t('i.datepicker.weeks.fri') }}</span><span>{{ t('i.datepicker.weeks.sat') }}</span> |
| 8 | </div> | 7 | </div> |
| 9 | - <span :class="getCellCls(cell)" v-for="(cell, index) in readCells"><em :index="index">{{ cell.text }}</em></span> | 8 | + <span :class="getCellCls(cell)" v-for="(cell, index) in readCells"><em :index="index" @click="handleClick(cell)">{{ cell.text }}</em></span> |
| 10 | </div> | 9 | </div> |
| 11 | </template> | 10 | </template> |
| 12 | <script> | 11 | <script> |
| @@ -106,6 +105,7 @@ | @@ -106,6 +105,7 @@ | ||
| 106 | const cell_tmpl = { | 105 | const cell_tmpl = { |
| 107 | text: '', | 106 | text: '', |
| 108 | type: '', | 107 | type: '', |
| 108 | + date: null, | ||
| 109 | selected: false, | 109 | selected: false, |
| 110 | disabled: false, | 110 | disabled: false, |
| 111 | range: false, | 111 | range: false, |
| @@ -117,14 +117,8 @@ | @@ -117,14 +117,8 @@ | ||
| 117 | const cell = deepCopy(cell_tmpl); | 117 | const cell = deepCopy(cell_tmpl); |
| 118 | cell.type = 'prev-month'; | 118 | cell.type = 'prev-month'; |
| 119 | cell.text = dateCountOfLastMonth - (day - 1) + i; | 119 | cell.text = dateCountOfLastMonth - (day - 1) + i; |
| 120 | - | ||
| 121 | - let prevMonth = this.month - 1; | ||
| 122 | - let prevYear = this.year; | ||
| 123 | - if (this.month === 0) { | ||
| 124 | - prevMonth = 11; | ||
| 125 | - prevYear -= 1; | ||
| 126 | - } | ||
| 127 | - const time = clearHours(new Date(prevYear, prevMonth, cell.text)); | 120 | + cell.date = new Date(this.year, this.month - 1, cell.text); |
| 121 | + const time = clearHours(cell.date); | ||
| 128 | cell.disabled = typeof disabledDate === 'function' && disabledDate(new Date(time)); | 122 | cell.disabled = typeof disabledDate === 'function' && disabledDate(new Date(time)); |
| 129 | cells.push(cell); | 123 | cells.push(cell); |
| 130 | } | 124 | } |
| @@ -132,9 +126,10 @@ | @@ -132,9 +126,10 @@ | ||
| 132 | 126 | ||
| 133 | for (let i = 1; i <= dateCountOfMonth; i++) { | 127 | for (let i = 1; i <= dateCountOfMonth; i++) { |
| 134 | const cell = deepCopy(cell_tmpl); | 128 | const cell = deepCopy(cell_tmpl); |
| 135 | - const time = clearHours(new Date(this.year, this.month, i)); | ||
| 136 | - cell.type = time === today ? 'today' : 'normal'; | ||
| 137 | cell.text = i; | 129 | cell.text = i; |
| 130 | + cell.date = new Date(this.year, this.month, cell.text); | ||
| 131 | + const time = clearHours(cell.date); | ||
| 132 | + cell.type = time === today ? 'today' : 'normal'; | ||
| 138 | cell.selected = time === selectDay; | 133 | cell.selected = time === selectDay; |
| 139 | cell.disabled = typeof disabledDate === 'function' && disabledDate(new Date(time)); | 134 | cell.disabled = typeof disabledDate === 'function' && disabledDate(new Date(time)); |
| 140 | cell.range = time >= minDay && time <= maxDay; | 135 | cell.range = time >= minDay && time <= maxDay; |
| @@ -149,14 +144,8 @@ | @@ -149,14 +144,8 @@ | ||
| 149 | const cell = deepCopy(cell_tmpl); | 144 | const cell = deepCopy(cell_tmpl); |
| 150 | cell.type = 'next-month'; | 145 | cell.type = 'next-month'; |
| 151 | cell.text = i; | 146 | cell.text = i; |
| 152 | - | ||
| 153 | - let nextMonth = this.month + 1; | ||
| 154 | - let nextYear = this.year; | ||
| 155 | - if (this.month === 11) { | ||
| 156 | - nextMonth = 0; | ||
| 157 | - nextYear += 1; | ||
| 158 | - } | ||
| 159 | - const time = clearHours(new Date(nextYear, nextMonth, cell.text)); | 147 | + cell.date = new Date(this.year, this.month + 1, cell.text); |
| 148 | + const time = clearHours(cell.date); | ||
| 160 | cell.disabled = typeof disabledDate === 'function' && disabledDate(new Date(time)); | 149 | cell.disabled = typeof disabledDate === 'function' && disabledDate(new Date(time)); |
| 161 | cells.push(cell); | 150 | cells.push(cell); |
| 162 | } | 151 | } |
| @@ -165,71 +154,39 @@ | @@ -165,71 +154,39 @@ | ||
| 165 | } | 154 | } |
| 166 | }, | 155 | }, |
| 167 | methods: { | 156 | methods: { |
| 168 | - getDateOfCell (cell) { | ||
| 169 | - let year = this.year; | ||
| 170 | - let month = this.month; | ||
| 171 | - let day = cell.text; | ||
| 172 | - | ||
| 173 | - const date = this.date; | ||
| 174 | - const hours = date.getHours(); | ||
| 175 | - const minutes = date.getMinutes(); | ||
| 176 | - const seconds = date.getSeconds(); | ||
| 177 | - | ||
| 178 | - if (cell.type === 'prev-month') { | ||
| 179 | - if (month === 0) { | ||
| 180 | - month = 11; | ||
| 181 | - year--; | ||
| 182 | - } else { | ||
| 183 | - month--; | ||
| 184 | - } | ||
| 185 | - } else if (cell.type === 'next-month') { | ||
| 186 | - if (month === 11) { | ||
| 187 | - month = 0; | ||
| 188 | - year++; | ||
| 189 | - } else { | ||
| 190 | - month++; | ||
| 191 | - } | ||
| 192 | - } | ||
| 193 | - | ||
| 194 | - return new Date(year, month, day, hours, minutes, seconds); | ||
| 195 | - }, | ||
| 196 | - handleClick (event) { | ||
| 197 | - const target = event.target; | ||
| 198 | - if (target.tagName === 'EM') { | ||
| 199 | - const cell = this.cells[parseInt(event.target.getAttribute('index'))]; | ||
| 200 | - if (cell.disabled) return; | ||
| 201 | - | ||
| 202 | - const newDate = this.getDateOfCell(cell); | ||
| 203 | - | ||
| 204 | - if (this.selectionMode === 'range') { | ||
| 205 | - if (this.minDate && this.maxDate) { | 157 | + handleClick (cell) { |
| 158 | + | ||
| 159 | + if (cell.disabled) return; | ||
| 160 | + const newDate = cell.date; | ||
| 161 | + | ||
| 162 | + if (this.selectionMode === 'range') { | ||
| 163 | + if (this.minDate && this.maxDate) { | ||
| 164 | + const minDate = new Date(newDate.getTime()); | ||
| 165 | + const maxDate = null; | ||
| 166 | + this.rangeState.selecting = true; | ||
| 167 | + this.markRange(this.minDate); | ||
| 168 | + | ||
| 169 | + this.$emit('on-pick', {minDate, maxDate}, false); | ||
| 170 | + } else if (this.minDate && !this.maxDate) { | ||
| 171 | + if (newDate >= this.minDate) { | ||
| 172 | + const maxDate = new Date(newDate.getTime()); | ||
| 173 | + this.rangeState.selecting = false; | ||
| 174 | + | ||
| 175 | + this.$emit('on-pick', {minDate: this.minDate, maxDate}); | ||
| 176 | + } else { | ||
| 206 | const minDate = new Date(newDate.getTime()); | 177 | const minDate = new Date(newDate.getTime()); |
| 207 | - const maxDate = null; | ||
| 208 | - this.rangeState.selecting = true; | ||
| 209 | - this.markRange(this.minDate); | ||
| 210 | - | ||
| 211 | - this.$emit('on-pick', {minDate, maxDate}, false); | ||
| 212 | - } else if (this.minDate && !this.maxDate) { | ||
| 213 | - if (newDate >= this.minDate) { | ||
| 214 | - const maxDate = new Date(newDate.getTime()); | ||
| 215 | - this.rangeState.selecting = false; | ||
| 216 | - | ||
| 217 | - this.$emit('on-pick', {minDate: this.minDate, maxDate}); | ||
| 218 | - } else { | ||
| 219 | - const minDate = new Date(newDate.getTime()); | ||
| 220 | - | ||
| 221 | - this.$emit('on-pick', {minDate, maxDate: this.maxDate}, false); | ||
| 222 | - } | ||
| 223 | - } else if (!this.minDate) { | ||
| 224 | - const minDate = new Date(newDate.getTime()); | ||
| 225 | - this.rangeState.selecting = true; | ||
| 226 | - this.markRange(this.minDate); | ||
| 227 | 178 | ||
| 228 | this.$emit('on-pick', {minDate, maxDate: this.maxDate}, false); | 179 | this.$emit('on-pick', {minDate, maxDate: this.maxDate}, false); |
| 229 | } | 180 | } |
| 230 | - } else { | ||
| 231 | - this.$emit('on-pick', newDate); | 181 | + } else if (!this.minDate) { |
| 182 | + const minDate = new Date(newDate.getTime()); | ||
| 183 | + this.rangeState.selecting = true; | ||
| 184 | + this.markRange(this.minDate); | ||
| 185 | + | ||
| 186 | + this.$emit('on-pick', {minDate, maxDate: this.maxDate}, false); | ||
| 232 | } | 187 | } |
| 188 | + } else { | ||
| 189 | + this.$emit('on-pick', newDate); | ||
| 233 | } | 190 | } |
| 234 | this.$emit('on-pick-click'); | 191 | this.$emit('on-pick-click'); |
| 235 | }, | 192 | }, |
| @@ -246,7 +203,7 @@ | @@ -246,7 +203,7 @@ | ||
| 246 | if (target.tagName === 'EM') { | 203 | if (target.tagName === 'EM') { |
| 247 | const cell = this.cells[parseInt(event.target.getAttribute('index'))]; | 204 | const cell = this.cells[parseInt(event.target.getAttribute('index'))]; |
| 248 | // if (cell.disabled) return; // todo 待确定 | 205 | // if (cell.disabled) return; // todo 待确定 |
| 249 | - this.rangeState.endDate = this.getDateOfCell(cell); | 206 | + this.rangeState.endDate = cell.date; |
| 250 | } | 207 | } |
| 251 | }, | 208 | }, |
| 252 | markRange (maxDate) { | 209 | markRange (maxDate) { |
test/unit/specs/date-picker.spec.js
| 1 | -import { createVue, destroyVM } from '../util'; | 1 | +import { createVue, destroyVM, stringToDate } from '../util'; |
| 2 | 2 | ||
| 3 | describe('DatePicker.vue', () => { | 3 | describe('DatePicker.vue', () => { |
| 4 | let vm; | 4 | let vm; |
| @@ -25,4 +25,112 @@ describe('DatePicker.vue', () => { | @@ -25,4 +25,112 @@ describe('DatePicker.vue', () => { | ||
| 25 | done(); | 25 | done(); |
| 26 | }); | 26 | }); |
| 27 | }); | 27 | }); |
| 28 | + | ||
| 29 | + it('should create a DatePicker component of type="datetimerange"', done => { | ||
| 30 | + vm = createVue(` | ||
| 31 | + <Date-Picker type="datetimerange"></Date-Picker> | ||
| 32 | + `); | ||
| 33 | + const picker = vm.$children[0]; | ||
| 34 | + expect(picker.$children.length).to.equal(2); | ||
| 35 | + expect(Array.isArray(picker.currentValue)).to.equal(true); | ||
| 36 | + done(); | ||
| 37 | + }); | ||
| 38 | + | ||
| 39 | + it('should create a datetimerange component and pick 2 dates in the current month', done => { | ||
| 40 | + vm = createVue(` | ||
| 41 | + <Date-picker type="datetimerange"></Date-picker> | ||
| 42 | + `); | ||
| 43 | + | ||
| 44 | + const picker = vm.$children[0]; | ||
| 45 | + picker.handleIconClick(); | ||
| 46 | + vm.$nextTick(() => { | ||
| 47 | + const displayField = vm.$el.querySelector('.ivu-input'); | ||
| 48 | + const clickableCells = vm.$el.querySelectorAll('.ivu-date-picker-cells-cell'); | ||
| 49 | + const lastMonthClass = 'ivu-date-picker-cells-cell-prev-month'; | ||
| 50 | + const firstDayInMonthIndex = [...clickableCells].findIndex(cell => !cell.classList.contains(lastMonthClass)); | ||
| 51 | + | ||
| 52 | + clickableCells[firstDayInMonthIndex].firstElementChild.click(); | ||
| 53 | + vm.$nextTick(() => { | ||
| 54 | + clickableCells[firstDayInMonthIndex + 4].firstElementChild.click(); | ||
| 55 | + vm.$nextTick(() => { | ||
| 56 | + const dayOne = new Date(); | ||
| 57 | + dayOne.setDate(1); | ||
| 58 | + dayOne.setHours(0, 0, 0, 0); | ||
| 59 | + const dayFive = new Date(dayOne.getTime()); | ||
| 60 | + dayFive.setDate(5); | ||
| 61 | + dayFive.setHours(0, 0, 0, 0); | ||
| 62 | + | ||
| 63 | + // check pickers internal value | ||
| 64 | + const [startInternalValue, endInternalValue] = picker.currentValue; // Date Objects | ||
| 65 | + expect(Math.abs(dayOne - startInternalValue)).to.equal(0); | ||
| 66 | + expect(Math.abs(dayFive - endInternalValue)).to.equal(0); | ||
| 67 | + | ||
| 68 | + // check pickers display value | ||
| 69 | + const [startDisplayValue, endDisplayValue] = displayField.value.split(' - ').map(stringToDate); // Date Objects | ||
| 70 | + expect(Math.abs(dayOne - startDisplayValue)).to.equal(0); | ||
| 71 | + expect(Math.abs(dayFive - endDisplayValue)).to.equal(0); | ||
| 72 | + | ||
| 73 | + done(); | ||
| 74 | + }); | ||
| 75 | + }); | ||
| 76 | + }); | ||
| 77 | + }); | ||
| 78 | + | ||
| 79 | + it('should have same behavior after a reset as before the reset', done => { | ||
| 80 | + vm = createVue(` | ||
| 81 | + <Date-picker type="datetimerange"></Date-picker> | ||
| 82 | + `); | ||
| 83 | + | ||
| 84 | + const picker = vm.$children[0]; | ||
| 85 | + picker.handleIconClick(); | ||
| 86 | + vm.$nextTick(() => { | ||
| 87 | + const displayField = vm.$el.querySelector('.ivu-input'); | ||
| 88 | + const clickableCells = vm.$el.querySelectorAll('.ivu-date-picker-cells-cell'); | ||
| 89 | + const lastMonthClass = 'ivu-date-picker-cells-cell-prev-month'; | ||
| 90 | + const firstDayInMonthIndex = [...clickableCells].findIndex(cell => !cell.classList.contains(lastMonthClass)); | ||
| 91 | + | ||
| 92 | + // choose first date | ||
| 93 | + clickableCells[firstDayInMonthIndex].firstElementChild.click(); | ||
| 94 | + vm.$nextTick(() => { | ||
| 95 | + // choose second date | ||
| 96 | + clickableCells[firstDayInMonthIndex + 4].firstElementChild.click(); | ||
| 97 | + vm.$nextTick(() => { | ||
| 98 | + // cache first values | ||
| 99 | + const [startInternalValue, endInternalValue] = picker.currentValue; // Date Objects | ||
| 100 | + const [startDisplayValue, endDisplayValue] = displayField.value.split(' - ').map(stringToDate); // Date Objects | ||
| 101 | + | ||
| 102 | + // clear picker | ||
| 103 | + picker.handleClear(); | ||
| 104 | + vm.$nextTick(() => { | ||
| 105 | + // it should be closed by now | ||
| 106 | + expect(picker.visible).to.equal(false); | ||
| 107 | + // open picker again | ||
| 108 | + picker.handleIconClick(); | ||
| 109 | + | ||
| 110 | + vm.$nextTick(() => { | ||
| 111 | + expect(picker.visible).to.equal(true); | ||
| 112 | + expect(JSON.stringify(picker.currentValue)).to.equal('[null,null]'); | ||
| 113 | + expect(displayField.value).to.equal(''); | ||
| 114 | + | ||
| 115 | + clickableCells[firstDayInMonthIndex].firstElementChild.click(); | ||
| 116 | + vm.$nextTick(() => { | ||
| 117 | + clickableCells[firstDayInMonthIndex + 4].firstElementChild.click(); | ||
| 118 | + vm.$nextTick(() => { | ||
| 119 | + // recheck internal values | ||
| 120 | + expect(Math.abs(picker.currentValue[0] - startInternalValue)).to.equal(0); | ||
| 121 | + expect(Math.abs(picker.currentValue[1] - endInternalValue)).to.equal(0); | ||
| 122 | + // recheck display value | ||
| 123 | + const [_startDisplayValue, _endDisplayValue] = displayField.value.split(' - ').map(stringToDate); // Date Objects | ||
| 124 | + expect(Math.abs(_startDisplayValue - startDisplayValue)).to.equal(0); | ||
| 125 | + expect(Math.abs(_endDisplayValue - endDisplayValue)).to.equal(0); | ||
| 126 | + | ||
| 127 | + done(); | ||
| 128 | + }); | ||
| 129 | + }); | ||
| 130 | + }); | ||
| 131 | + }); | ||
| 132 | + }); | ||
| 133 | + }); | ||
| 134 | + }); | ||
| 135 | + }); | ||
| 28 | }); | 136 | }); |
test/unit/util.js
| @@ -58,6 +58,16 @@ exports.createTest = function(Compo, propsData = {}, mounted = false) { | @@ -58,6 +58,16 @@ exports.createTest = function(Compo, propsData = {}, mounted = false) { | ||
| 58 | }; | 58 | }; |
| 59 | 59 | ||
| 60 | /** | 60 | /** |
| 61 | + * Transform Date string (yyyy-mm-dd hh:mm:ss) to Date object | ||
| 62 | + * @param {String} | ||
| 63 | + */ | ||
| 64 | +exports.stringToDate = function(str) { | ||
| 65 | + const parts = str.split(/[^\d]/).filter(Boolean); | ||
| 66 | + parts[1] = parts[1] - 1; | ||
| 67 | + return new Date(...parts); | ||
| 68 | +}; | ||
| 69 | + | ||
| 70 | +/** | ||
| 61 | * 触发一个事件 | 71 | * 触发一个事件 |
| 62 | * mouseenter, mouseleave, mouseover, keyup, change, click 等 | 72 | * mouseenter, mouseleave, mouseover, keyup, change, click 等 |
| 63 | * @param {Element} elm | 73 | * @param {Element} elm |