Skip to content

Commit

Permalink
pickers refactoring (#2664)
Browse files Browse the repository at this point in the history
* v-picker

* v-time-picker-clock

* v-time-picker-title

* v-date-picker-title

* v-date-picker-header

* v-date-picker-years

* v-date-picker-date-table, v-date-picker-month-table

* v-time-picker

* v-date-picker

* misc, cleanup

* test: touch usage in pickers

* added transition prop to v-picker

* fixes, codebeat improvements

* fixes, codebeat improvements

* fixes, codebeat improvements

* codebeat improvements

* fixed picker title background in dark mode

* code style update

* ampm computed prop

* even more refactoring (picker-button mixin)

* fixed test

* fix(date-picker): incorrect button styles

active and current dates (and months) in date-picker had incorrect styles

fixes #2812

(cherry picked from commit 2e9d6af)

* fix(date-picker): allow external switching between date and month view

switching between date and month view using type prop was not working

closes #2809

(cherry picked from commit d6935aa)

* fix: do not pad hours with 0 in am/pm mode

* fix: time picker clock size

* fix: time picker title colon size in landscape mode

* fix: time picker active & disabled button text color

* fix: do not emit input if hour/minute is not allowed

* fix: restored firstAllowed
  • Loading branch information
jacekkarczmarczyk authored Jan 6, 2018
1 parent 8b96ef9 commit e2dbf20
Show file tree
Hide file tree
Showing 64 changed files with 7,280 additions and 5,834 deletions.
256 changes: 166 additions & 90 deletions src/components/VDatePicker/VDatePicker.date.spec.js

Large diffs are not rendered by default.

349 changes: 163 additions & 186 deletions src/components/VDatePicker/VDatePicker.js

Large diffs are not rendered by default.

130 changes: 60 additions & 70 deletions src/components/VDatePicker/VDatePicker.month.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
import Vue from 'vue'
import { test } from '@util/testing'
import VDatePicker from '@components/VDatePicker'
import VDatePicker from './VDatePicker'
import VMenu from '@components/VMenu'

function createMenuPicker (mount, props) {
const wrapper = mount(Vue.component('test', {
components: {
VDatePicker,
VMenu
},
render (h) {
return h('v-menu', {
ref: 'menu'
}, [h('v-date-picker', {
props,
ref: 'picker'
})])
}
}))

const menu = wrapper.vm.$refs.menu
menu.isActive = true

const picker = menu.$slots.default[0].context.$refs.picker

expect('Unable to locate target [data-app]').toHaveBeenTipped()

return { wrapper, menu, picker }
}

test('VDatePicker.js', ({ mount, compileToFunctions }) => {
it('should emit input event on year click', async () => {
Expand All @@ -16,7 +43,7 @@ test('VDatePicker.js', ({ mount, compileToFunctions }) => {
})

wrapper.vm.$on('input', cb);
wrapper.find('.picker--date__years li.active + li')[0].trigger('click')
wrapper.find('.date-picker-years li.active + li')[0].trigger('click')
expect(cb).toBeCalledWith('2012-05')
})

Expand All @@ -34,7 +61,7 @@ test('VDatePicker.js', ({ mount, compileToFunctions }) => {
})

wrapper.vm.$on('input', cb);
wrapper.find('.picker--date__years li.active + li')[0].trigger('click')
wrapper.find('.date-picker-years li.active + li')[0].trigger('click')
expect(cb).not.toBeCalled()
})

Expand All @@ -48,7 +75,7 @@ test('VDatePicker.js', ({ mount, compileToFunctions }) => {
})

wrapper.vm.$on('input', cb);
wrapper.find('.picker--date__table tbody tr:first-child td:first-child button')[0].trigger('click')
wrapper.find('.date-picker-table--month button')[0].trigger('click')
expect(cb).toBeCalledWith('2013-01')
})

Expand All @@ -61,9 +88,9 @@ test('VDatePicker.js', ({ mount, compileToFunctions }) => {
}
})

wrapper.find('.picker--date__table')[0].trigger('wheel')
wrapper.find('.date-picker-table--month')[0].trigger('wheel')
await wrapper.vm.$nextTick()
expect(wrapper.vm.tableYear).toBe(2012)
expect(wrapper.vm.tableDate).toBe('2014')
})

it('should match snapshot with pick-month prop', () => {
Expand All @@ -86,31 +113,7 @@ test('VDatePicker.js', ({ mount, compileToFunctions }) => {
}
})

expect(wrapper.find('.picker--date__table tbody')[0].html()).toMatchSnapshot()
})

it('should match snapshot with allowed dates as function', () => {
const wrapper = mount(VDatePicker, {
propsData: {
value: '2013-05',
type: 'month',
allowedDates: date => date.substr(6, 1) === '1'
}
})

expect(wrapper.find('.picker--date__table tbody')[0].html()).toMatchSnapshot()
})

it('should match snapshot with allowed dates as object', () => {
const wrapper = mount(VDatePicker, {
propsData: {
value: '2013-05',
type: 'month',
allowedDates: { min: '2013-03', max: '2013-07' }
}
})

expect(wrapper.find('.picker--date__table tbody')[0].html()).toMatchSnapshot()
expect(wrapper.find('.date-picker-table--month tbody')[0].html()).toMatchSnapshot()
})

it('should match snapshot with month formatting functions', () => {
Expand All @@ -122,7 +125,7 @@ test('VDatePicker.js', ({ mount, compileToFunctions }) => {
}
})

expect(wrapper.find('.picker--date__table tbody')[0].html()).toMatchSnapshot()
expect(wrapper.find('.date-picker-table--month tbody')[0].html()).toMatchSnapshot()
})

it('should match snapshot with colored picker', () => {
Expand Down Expand Up @@ -158,7 +161,7 @@ test('VDatePicker.js', ({ mount, compileToFunctions }) => {
}
})

const [leftButton, rightButton] = wrapper.find('.picker--date__header-selector button')
const [leftButton, rightButton] = wrapper.find('.date-picker-header button')

leftButton.trigger('click')
expect(wrapper.vm.tableDate).toBe('2004')
Expand All @@ -175,7 +178,7 @@ test('VDatePicker.js', ({ mount, compileToFunctions }) => {
}
})

const button = wrapper.find('.picker--date__header-selector-date strong')[0]
const button = wrapper.find('.date-picker-header strong')[0]

button.trigger('click')
expect(wrapper.vm.activePicker).toBe('YEAR')
Expand All @@ -192,34 +195,24 @@ test('VDatePicker.js', ({ mount, compileToFunctions }) => {
}
})

wrapper.find('.picker--date__years li.active + li')[0].trigger('click')
wrapper.find('.date-picker-years li.active + li')[0].trigger('click')
expect(wrapper.vm.activePicker).toBe('MONTH')
expect(wrapper.vm.tableDate).toBe('2004')
})

it.skip('should calculate the first allowed month', () => {
const today = new Date().toISOString().substr(0, 7)
it('should calculate the first allowed date', () => {
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth()

const wrapper1 = mount(VDatePicker, {
const wrapper2 = mount(VDatePicker, {
propsData: {
value: null,
type: 'month'
type: 'month',
allowedDates: [`${year}-03`]
}
})
expect(wrapper1.vm.inputDate).toBe(today)

// The behaviour is dependent on the current date
// TODO refactor the test or change firstAllowedMonth implementation
//
// const allowedMonth = today.replace(/..$/, today.substr(5, 2) === '02' ? '03' : '02')
// const wrapper2 = mount(VDatePicker, {
// propsData: {
// value: null,
// type: 'month',
// allowedDates: [allowedMonth]
// }
// })
// expect(wrapper2.vm.inputDate).toBe(allowedMonth)
expect(wrapper2.vm.inputDate).toBe(`${year}-03`)
})

it('should set the table date when value has changed', () => {
Expand All @@ -234,24 +227,21 @@ test('VDatePicker.js', ({ mount, compileToFunctions }) => {
expect(wrapper.vm.tableDate).toBe('2005')
})

it('should update the active picker if type has changed', () => {
const wrapper = mount(VDatePicker, {
propsData: {
value: '1999-12-01',
type: 'date'
}
it('should update with autosave on month click', async () => {
const { wrapper, menu, picker } = createMenuPicker(mount, {
type: 'month',
value: '2013-05',
autosave: true
})

expect(wrapper.vm.activePicker).toBe('DATE')
wrapper.setProps({ type: 'month' })
expect(wrapper.vm.inputDate).toBe('1999-12')
expect(wrapper.vm.activePicker).toBe('MONTH')
wrapper.setProps({ type: 'date' })
expect(wrapper.vm.inputDate).toBe('1999-12-01')
expect(wrapper.vm.activePicker).toBe('DATE')
wrapper.setProps({ type: 'year' })
expect(wrapper.vm.inputDate).toBe('1999')
expect(wrapper.vm.activePicker).toBe('YEAR')
const input = jest.fn()
picker.$on('input', input)

picker.monthClick('2013-06')
expect(menu.isActive).toBe(true)
await wrapper.vm.$nextTick()
expect(menu.isActive).toBe(false)
expect(input).toBeCalledWith('2013-06')
})

it('should use prev and next icons', () => {
Expand All @@ -263,7 +253,7 @@ test('VDatePicker.js', ({ mount, compileToFunctions }) => {
}
})

const icons = wrapper.find('.picker--date__header-selector .icon')
const icons = wrapper.find('.date-picker-header .icon')
expect(icons[0].element.textContent).toBe('block')
expect(icons[1].element.textContent).toBe('check')
})
Expand Down
137 changes: 137 additions & 0 deletions src/components/VDatePicker/VDatePickerDateTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Mixins
import Colorable from '../../mixins/colorable'
import DatePickerTable from './mixins/date-picker-table'

// Utils
import { pad, createNativeLocaleFormatter, monthChange } from './util'
import { createRange } from '../../util/helpers'
import isValueAllowed from '../../util/isValueAllowed'

export default {
name: 'v-date-picker-date-table',

mixins: [
Colorable,
DatePickerTable
],

props: {
firstDayOfWeek: {
type: [String, Number],
default: 0
},
weekdayFormat: {
type: Function,
default: null
}
},

computed: {
formatter () {
return this.format || createNativeLocaleFormatter(this.locale, { day: 'numeric', timeZone: 'UTC' }, { start: 8, length: 2 })
},
weekdayFormatter () {
return this.weekdayFormat || createNativeLocaleFormatter(this.locale, { weekday: 'narrow', timeZone: 'UTC' })
},
weekDays () {
const first = parseInt(this.firstDayOfWeek, 10)

return this.weekdayFormatter
? createRange(7).map(i => this.weekdayFormatter(`2017-01-${first + i + 15}`)) // 2017-01-15 is Sunday
: createRange(7).map(i => ['S', 'M', 'T', 'W', 'T', 'F', 'S'][(i + first) % 7])
}
},

methods: {
calculateTableDate (delta) {
return monthChange(this.tableDate, Math.sign(delta || 1))
},
genTHead () {
const days = this.weekDays.map(day => this.$createElement('th', day))
return this.$createElement('thead', this.genTR(days))
},
genButtonClasses (day, disabled) {
const isActive = this.isActive(day)
const isCurrent = this.isCurrent(day)
const classes = Object.assign({
'btn--active': isActive,
'btn--outline': isCurrent && !isActive,
'btn--disabled': disabled
}, this.themeClasses)

return (isActive || isCurrent)
? this.addBackgroundColorClassChecks(classes)
: classes
},
genButton (day) {
const date = `${this.displayedYear}-${pad(this.displayedMonth + 1)}-${pad(day)}`
const disabled = !isValueAllowed(date, this.allowedDates)

return this.$createElement('button', {
staticClass: 'btn btn--raised btn--icon',
'class': this.genButtonClasses(day, disabled),
attrs: {
type: 'button'
},
domProps: {
disabled,
innerHTML: `<span class="btn__content">${this.formatter(date)}</span>`
},
on: disabled ? {} : {
click: () => this.$emit('input', date)
}
})
},
// Returns number of the days from the firstDayOfWeek to the first day of the current month
weekDaysBeforeFirstDayOfTheMonth () {
const firstDayOfTheMonth = new Date(`${this.displayedYear}-${pad(this.displayedMonth + 1)}-01T00:00:00+00:00`)
const weekDay = firstDayOfTheMonth.getUTCDay()
return (weekDay - parseInt(this.firstDayOfWeek) + 7) % 7
},
genTBody () {
const children = []
const daysInMonth = new Date(this.displayedYear, this.displayedMonth + 1, 0).getDate()
let rows = []
const day = this.weekDaysBeforeFirstDayOfTheMonth()

for (let i = 0; i < day; i++) {
rows.push(this.$createElement('td'))
}

for (let i = 1; i <= daysInMonth; i++) {
rows.push(this.$createElement('td', [this.genButton(i)]))

if (rows.length % 7 === 0) {
children.push(this.genTR(rows))
rows = []
}
}

if (rows.length) {
children.push(this.genTR(rows))
}

return this.$createElement('tbody', children)
},
genTR (children) {
return [this.$createElement('tr', children)]
},
isActive (date) {
return this.selectedYear === this.displayedYear &&
this.selectedMonth === this.displayedMonth &&
this.selectedDate === date
},
isCurrent (date) {
return this.currentYear === this.displayedYear &&
this.currentMonth === this.displayedMonth &&
this.currentDate === date
}
},

render (h) {
return this.genTable('date-picker-table date-picker-table--date', [
this.genTHead(),
this.genTBody()
])
}
}
Loading

0 comments on commit e2dbf20

Please sign in to comment.