Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add prop to determine first-day-of-week #41

Merged
merged 6 commits into from
Oct 22, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions docs/guide/Props/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,37 @@
| calendar-button | Boolean | false | Show an icon that that can be clicked |
| calendar-button-icon | String | | Use icon for button (ex: fa fa-calendar) |
| calendar-button-icon-content | String | | Use for material-icons (ex: event) |
| calendar-class | String\|Object | | CSS class applied to the calendar el |
| calendar-class | String|Object | | CSS class applied to the calendar el |
MrWook marked this conversation as resolved.
Show resolved Hide resolved
| clear-button | Boolean | false | Show an icon for clearing the date |
| clear-button-icon | String | | Use icon for button (ex: fa fa-times) |
| day-cell-content | Function | | Use to render custom content in day cell |
| disabled | Boolean | false | If `true`, disable Datepicker on screen |
| disabled-dates | Object | | See below for configuration |
| first-day-of-week | String | sun | Sets the first day of the week. Possible values: _sun_, _mon_, _tue_, _wed_, _thu_, _fri_, _sat_ |
| fixed-position | String | | Set a fixed position for the picker. Possible values: _bottom_, _bottom-left_, _bottom-right_, _top_, _top-left_ and _top-right_ |
| format | String\|Function | dd MMM yyyy | Date formatting string or function |
| format | String|Function | dd MMM yyyy | Date formatting string or function |
| full-month-name | Boolean | false | To show the full month name |
| id | String | | Input id |
| initial-view | String | minimumView | If set, open on that view |
| inline | Boolean | | To show the datepicker always open |
| input-class | String\|Object\|Array | | CSS class(es) applied to the input el |
| input-class | String|Object|Array| | CSS class(es) applied to the input element |
| language | Object | en | Translation for days and months |
| maxlength | String | | Sets html `maxlength` attribute on input |
| maximum-view | String | 'year' | If set, higher-level views won't show |
| minimum-view | String | 'day' | If set, lower-level views won't show |
| monday-first | Boolean | false | To start the week on Monday |
| maximum-view | String | year | If set, higher-level views won't show |
| minimum-view | String | day | If set, lower-level views won't show |
| monday-first | Boolean | false | [DEPRECATED in favour of `first-day-of-week`]. To start the week on Monday |
mst101 marked this conversation as resolved.
Show resolved Hide resolved
| name | String | | Input name property |
| open-date | Date\|String | | If set, open on that date |
| open-date | Date|String | | If set, open on that date |
| pattern | String | | Sets html `pattern` attribute on input |
| placeholder | String | | Input placeholder text |
| required | Boolean | false | Sets html required attribute on input |
| ref-name | String | | Sets a ref name directly on the input field |
| show-header | Boolean | true | If `false`, the header section won't show |
| show-calendar-on-focus | Boolean | false | Opens the calendar on input focus |
| show-calendar-on-button-click | Boolean | false | Only open the calendar on calendar-button click |
| tabindex | String\|Number | | Tabindex for the input field |
| tabindex | String|Number | | Tabindex for the input field |
| typeable | Boolean | false | If `true`, allow the user to type the date |
| use-utc | Boolean | false | Use UTC for time calculations |
| value | Date\|String | | Date value of the datepicker |
| wrapper-class | String\|Object | | CSS class applied to the outer div |
| year-picker-range | Number | 10 | Amount of years inside the year picker |
| value | Date|String | | Date value of the datepicker |
| wrapper-class | String|Object | | CSS class applied to the outer div |
| year-picker-range | Number | 10 | How many years to display in the _year picker_ |
36 changes: 22 additions & 14 deletions src/components/Datepicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@
:allowed-to-show-view="allowedToShowView"
:day-cell-content="dayCellContent"
:disabled-dates="disabledDates"
:first-day-of-week="computedFirstDayOfWeek"
:highlighted="highlighted"
:is-rtl="isRtl"
:monday-first="mondayFirst"
:page-date="pageDate"
:page-timestamp="pageTimestamp"
:selected-date="selectedDate"
Expand Down Expand Up @@ -113,12 +113,12 @@
import '~/utils/polyfills'
import en from '~/locale/translations/en'
import { makeDateUtils } from '~/utils/DateUtils'
import calendarSlots from '~/utils/calendarSlots'
import DateInput from '~/components/DateInput'
import inputProps from '~/mixins/inputProps'
import PickerDay from '~/components/PickerDay'
import PickerMonth from '~/components/PickerMonth'
import PickerYear from '~/components/PickerYear'
import inputProps from '~/mixins/inputProps'
import calendarSlots from '~/utils/calendarSlots'
import Popup from '~/components/Popup'

const validDate = (val) => val === null
Expand All @@ -139,6 +139,10 @@ export default {
inputProps,
],
props: {
appendToBody: {
type: Boolean,
default: false,
},
calendarClass: {
type: [
String,
Expand All @@ -157,6 +161,10 @@ export default {
return {}
},
},
firstDayOfWeek: {
type: String,
default: 'sun',
},
fixedPosition: {
type: String,
default: '',
Expand Down Expand Up @@ -194,6 +202,7 @@ export default {
type: String,
default: 'day',
},
// Suggest this should be deprecated in favour of 'firstDayOfWeek'
mondayFirst: {
mst101 marked this conversation as resolved.
Show resolved Hide resolved
type: Boolean,
default: false,
Expand Down Expand Up @@ -223,10 +232,6 @@ export default {
type: Number,
default: 10,
},
appendToBody: {
type: Boolean,
default: false,
},
},
data() {
// const startDate = this.openDate ? new Date(this.openDate) : new Date()
Expand All @@ -239,28 +244,31 @@ export default {
}
const pageTimestamp = constructedDateUtils.setDate(startDate, 1)
return {
/*
* Positioning
*/
calendarHeight: 0,
calendarSlots,
currentPicker: '',
/*
* Vue cannot observe changes to a Date Object so date must be stored as a timestamp
* This represents the first day of the current viewing month
* {Number}
*/
pageTimestamp,
currentPicker: '',
resetTypedDate: constructedDateUtils.getNewDateObject(),
/*
* Selected Date
* {Date}
*/
selectedDate: null,
/*
* Positioning
*/
calendarHeight: 0,
resetTypedDate: constructedDateUtils.getNewDateObject(),
utils: constructedDateUtils,
calendarSlots,
}
},
computed: {
computedFirstDayOfWeek() {
return this.mondayFirst ? 1 : this.utils.getDayFromAbbr(this.firstDayOfWeek)
},
computedInitialView() {
return this.initialView ? this.initialView : this.minimumView
},
Expand Down
34 changes: 13 additions & 21 deletions src/components/PickerDay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ export default {
return {}
},
},
mondayFirst: {
type: Boolean,
default: false,
firstDayOfWeek: {
type: Number,
default: 0,
},
showFullMonthName: {
type: Boolean,
Expand All @@ -87,10 +87,7 @@ export default {
*/
blankDays() {
const dObj = this.newPageDate()
if (this.mondayFirst) {
return this.utils.getDay(dObj) > 0 ? this.utils.getDay(dObj) - 1 : 6
}
return this.utils.getDay(dObj)
return (7 - this.firstDayOfWeek + this.utils.getDay(dObj)) % 7
},
/**
* Gets the name of the month the current page is on
Expand Down Expand Up @@ -130,12 +127,7 @@ export default {
* @return {String[]}
*/
daysOfWeek() {
if (this.mondayFirst) {
const tempDays = this.translation.days.slice()
tempDays.push(tempDays.shift())
return tempDays
}
return this.translation.days
return this.translation.getDaysStartingOn(this.firstDayOfWeek)
},
/**
* Is the next month disabled?
Expand Down Expand Up @@ -334,14 +326,6 @@ export default {
isSunday,
}
},
/**
* Increment the current page month
*/
nextMonth() {
if (!this.isNextDisabled) {
this.changeMonth(+1)
}
},
/**
* Set up a new date object to the first day of the current 'page'
* @return Date
Expand All @@ -352,6 +336,14 @@ export default {
? new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1))
: new Date(d.getFullYear(), d.getMonth(), 1, d.getHours(), d.getMinutes())
},
/**
* Increment the current page month
*/
nextMonth() {
if (!this.isNextDisabled) {
this.changeMonth(+1)
}
},
/**
* Decrement the page month
*/
Expand Down
4 changes: 2 additions & 2 deletions src/components/PickerHeader.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<template>
<header v-if="config.showHeader">
<span
:class="{'disabled': isLeftNavDisabled}"
class="prev"
:class="{'disabled': isLeftNavDisabled}"
@click="config.isRtl ? next() : previous()"
>
<slot name="prevIntervalBtn">
Expand All @@ -11,8 +11,8 @@
</span>
<slot />
<span
:class="{'disabled': isRightNavDisabled}"
class="next"
:class="{'disabled': isRightNavDisabled}"
@click="config.isRtl ? previous() : next()"
>
<slot name="nextIntervalBtn">
Expand Down
4 changes: 2 additions & 2 deletions src/components/PickerMonth.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
:previous="previousYear"
>
<span
:class="allowedToShowView('year') ? 'up' : ''"
class="month__year_btn"
:class="allowedToShowView('year') ? 'up' : ''"
@click="showPickerCalendar('year')"
>
{{ pageYearName }}
Expand All @@ -24,9 +24,9 @@
</PickerHeader>
<span
v-for="month in months"
:key="month.timestamp"
:class="{'selected': month.isSelected, 'disabled': month.isDisabled}"
class="cell month"
:key="month.timestamp"
mst101 marked this conversation as resolved.
Show resolved Hide resolved
@click.stop="selectMonth(month)"
>
{{ month.month }}
Expand Down
1 change: 0 additions & 1 deletion src/components/PickerYear.vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ export default {
years() {
const d = this.pageDate
const years = []

const year = this.useUtc
? Math.floor(d.getUTCFullYear() / this.yearRange) * this.yearRange
: Math.floor(d.getFullYear() / this.yearRange) * this.yearRange
Expand Down
7 changes: 7 additions & 0 deletions src/locale/Language.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ export default class Language {
this._days = days
}

getDaysStartingOn(firstDayOfWeek) {
const firstDays = this._days.slice(firstDayOfWeek)
const lastDays = this._days.slice(0, firstDayOfWeek)

return firstDays.concat(lastDays)
}

getMonthByAbbrName(name) {
const monthValue = this._monthsAbbr.findIndex((month) => month === name) + 1
return monthValue < 10 ? `0${monthValue}` : `${monthValue}`
Expand Down
14 changes: 14 additions & 0 deletions src/utils/DateUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,20 @@ const utils = {
return days[this.getDay(date)]
},

/**
* Return day number from abbreviated week day name
* @param {String} abbr
* @return {Number}
*/
getDayFromAbbr(abbr) {
for (let i = 0; i < en.days.length; i += 1) {
if (abbr.toLowerCase() === en.days[i].toLowerCase()) {
return i
}
}
throw TypeError('Invalid week day')
},

/**
* Return name of the month
* @param {Number|Date} month
Expand Down
6 changes: 6 additions & 0 deletions test/unit/specs/DateUtils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,10 @@ describe('UTC functions', () => {
expect(DateUtils.setDate(date, 31)).toEqual(date.setDate(31))
expect(utcUtils.setDate(date, 31)).toEqual(date.setUTCDate(31))
})

it('returns the correct day number from an abbreviated day name', () => {
expect(DateUtils.getDayFromAbbr('sun')).toEqual(0)
expect(DateUtils.getDayFromAbbr('sat')).toEqual(6)
expect(() => DateUtils.getDayFromAbbr('nonsense')).toThrow('Invalid week day')
})
})
75 changes: 75 additions & 0 deletions test/unit/specs/PickerDay/firstDayOfWeek.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { shallowMount } from '@vue/test-utils'
import PickerDay from '~/components/PickerDay'
import { en } from '~/locale'

describe('PickerDay: Datepicker with Monday as first day of week', () => {
mst101 marked this conversation as resolved.
Show resolved Hide resolved
let wrapper
beforeEach(() => {
wrapper = shallowMount(PickerDay, {
propsData: {
firstDayOfWeek: 1,
translation: en,
allowedToShowView: () => true,
pageDate: new Date(2018, 1, 1),
},
})
})

afterEach(() => {
wrapper.destroy()
})

it('should return Monday as a first day of week', () => {
expect(wrapper.vm.daysOfWeek[0]).toEqual('Mon')
})

it('should return Sunday as a seventh day of week', () => {
expect(wrapper.vm.daysOfWeek[6]).toEqual('Sun')
})

it('should have 6 blankDays when month starts on a Sunday', () => {
wrapper.setProps({
pageDate: new Date(2018, 3, 1),
})
expect(wrapper.vm.blankDays).toEqual(6)
})

it('should have no blankDays when month starts on a Monday', () => {
wrapper.setProps({
pageDate: new Date(2018, 9, 1),
})
expect(wrapper.vm.blankDays).toEqual(0)
})
})

describe('PickerDay: Datepicker with Saturday as first day of week', () => {
let wrapper
beforeEach(() => {
wrapper = shallowMount(PickerDay, {
propsData: {
firstDayOfWeek: 6,
translation: en,
allowedToShowView: () => true,
pageDate: new Date(2018, 1, 1),
},
})
})

afterEach(() => {
wrapper.destroy()
})

it('should have 6 blankDays when month starts on a Friday', () => {
wrapper.setProps({
pageDate: new Date(2021, 0, 1),
})
expect(wrapper.vm.blankDays).toEqual(6)
})

it('should have no blankDays when month starts on a Saturday', () => {
wrapper.setProps({
pageDate: new Date(2020, 7, 1),
})
expect(wrapper.vm.blankDays).toEqual(0)
})
})
Loading