Skip to content
This repository has been archived by the owner on Feb 19, 2024. It is now read-only.

Commit

Permalink
Enable disabling of dates, by passing an array of dates/strings via prop
Browse files Browse the repository at this point in the history
  • Loading branch information
MikaelEdebro committed Mar 24, 2018
1 parent e0b0f3a commit 04ec02b
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 24 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ The `formatDates()` methods is just an example of how it can be solved.
| fullscreenMobile | Show fullscreen view on mobile.<br>Type: Boolean, Default: false |
| mobileHeader | Text to show on mobile header<br>Type: String, Default: 'Select dates' |
| inline | Use inline mode (datepicker always showing)<br>Type: Boolean, Default: false |
| disabledDates | Disable specific dates.<br>Type: Array<string> |
| @date-one-selected | Event emitted when second date is selected.<br>Required |
| @date-two-selected | Event emitted when second date is selected.<br>Required if using `mode="range"` |
| @closed | Event emitted when datepicker is closed. |
Expand All @@ -167,6 +168,7 @@ The `formatDates()` methods is just an example of how it can be solved.
:fullscreen-mobile="true"
:mobile-header="'Mobile header text'"
:inline="true"
:disabled-dates="['2018-10-20', '2018-10-22']"
@date-one-selected="val => { dateOne = val }"
@date-two-selected="val => { dateTwo = val }"
/>
Expand Down
1 change: 1 addition & 0 deletions dev/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
:fullscreen-mobile="false"
:date-one="inlineDateOne"
:months-to-show="1"
:disabled-dates="['2018-03-30', '2018-04-10']"
@date-one-selected="val => { inlineDateOne = val }"
/>
</div>
Expand Down
23 changes: 9 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
"url": "MikaelEdebro/vue-airbnb-style-datepicker",
"type": "git"
},
"files": [
"dist"
],
"files": ["dist"],
"scripts": {
"build": "rimraf dist/ && npm run build-main && npm run build-no-dep",
"build-main": "bili --format cjs,es,umd,umd-min --plugin vue --vue.css dist/styles.css",
"build-no-dep": "bili --outDir dist/no-dep --format umd,umd-min --plugin vue --vue.css dist/styles.css --no-inline --global.date-fns/format dateFns.format --global.date-fns/add_months dateFns.addMonths --global.date-fns/sub_months dateFns.subMonths --global.date-fns/get_days_in_month dateFns.getDaysInMonth --global.date-fns/is_before dateFns.isBefore --global.date-fns/is_after dateFns.isAfter",
"build-main":
"bili --format cjs,es,umd,umd-min --plugin vue --vue.css dist/styles.css",
"build-no-dep":
"bili --outDir dist/no-dep --format umd,umd-min --plugin vue --vue.css dist/styles.css --no-inline --global.date-fns/format dateFns.format --global.date-fns/add_months dateFns.addMonths --global.date-fns/sub_months dateFns.subMonths --global.date-fns/get_days_in_month dateFns.getDaysInMonth --global.date-fns/is_before dateFns.isBefore --global.date-fns/is_after dateFns.isAfter",
"dev": "poi --port 5000",
"t": "jest --watch",
"test": "jest --watch",
Expand Down Expand Up @@ -54,20 +54,15 @@
"vue": "2.x"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"vue"
],
"moduleFileExtensions": ["js", "json", "vue"],
"transform": {
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
"^.+\\.js$": "<rootDir>/node_modules/babel-jest"
},
"moduleNameMapper": {
"^@/(.*)$": "<rootDir>/src/$1"
"^@/(.*)$": "<rootDir>/src/$1",
"^test/(.*)$": "<rootDir>/test/$1"
},
"snapshotSerializers": [
"<rootDir>/node_modules/jest-serializer-vue"
]
"snapshotSerializers": ["<rootDir>/node_modules/jest-serializer-vue"]
}
}
30 changes: 21 additions & 9 deletions src/components/AirbnbStyleDatepicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@
class="day"
v-for="({fullDate, dayNumber}, index) in week"
:key="index + '_' + dayNumber"
:data-date="fullDate"
:class="{
enabled: dayNumber !== 0,
empty: dayNumber === 0,
disabled: (isBeforeMinDate(fullDate) || isAfterEndDate(fullDate)),
disabled: isBeforeMinDate(fullDate) || isAfterEndDate(fullDate) || isDateDisabled(fullDate),
selected: selectedDate1 === fullDate || selectedDate2 === fullDate,
'in-range': isInRange(fullDate)
}"
Expand Down Expand Up @@ -116,7 +117,8 @@ export default {
startOpen: { type: Boolean },
fullscreenMobile: { type: Boolean },
inline: { type: Boolean },
mobileHeader: { type: String, default: 'Select date' }
mobileHeader: { type: String, default: 'Select date' },
disabledDates: { type: Array, default: () => [] }
},
data() {
return {
Expand Down Expand Up @@ -157,7 +159,8 @@ export default {
triggerWrapperPosition: {},
viewportWidth: window.innerWidth + 'px',
isMobile: window.innerWidth < 768,
isTablet: window.innerWidth >= 768 && window.innerWidth <= 1024
isTablet: window.innerWidth >= 768 && window.innerWidth <= 1024,
triggerElement: {}
}
},
computed: {
Expand Down Expand Up @@ -270,6 +273,7 @@ export default {
})
},
mounted() {
this.triggerElement = document.getElementById(this.triggerElementId)
this.setStartDates()
for (let i = 0; i < this.showMonths + 2; i++) {
Expand Down Expand Up @@ -371,7 +375,11 @@ export default {
return weeks
},
selectDate(date) {
if (this.isBeforeMinDate(date) || this.isAfterEndDate(date)) {
if (
this.isBeforeMinDate(date) ||
this.isAfterEndDate(date) ||
this.isDateDisabled(date)
) {
return
}
Expand Down Expand Up @@ -431,6 +439,10 @@ export default {
}
return isAfter(date, this.endDate)
},
isDateDisabled(date) {
const isDisabled = this.disabledDates.indexOf(date) > -1
return isDisabled
},
previousMonth() {
this.startingDate = this.subtractMonths(this.months[0].firstDateOfMonth)
Expand Down Expand Up @@ -482,12 +494,12 @@ export default {
this.$emit('closed')
},
positionDatepicker() {
const triggerElement = document.getElementById(this.triggerElementId)
//const triggerElement = document.getElementById(this.triggerElementId)
const triggerWrapperElement = findAncestor(
triggerElement,
this.triggerElement,
'.datepicker-trigger'
)
this.triggerPosition = triggerElement.getBoundingClientRect()
this.triggerPosition = this.triggerElement.getBoundingClientRect()
if (triggerWrapperElement) {
this.triggerWrapperPosition = triggerWrapperElement.getBoundingClientRect()
} else {
Expand All @@ -507,12 +519,12 @@ export default {
this.$nextTick(function() {
const datepickerWrapper = document.getElementById(this.wrapperId)
if (!triggerElement || !datepickerWrapper) {
if (!this.triggerElement || !datepickerWrapper) {
return
}
const rightPosition =
triggerElement.getBoundingClientRect().left +
this.triggerElement.getBoundingClientRect().left +
datepickerWrapper.getBoundingClientRect().width
this.alignRight = rightPosition > viewPortWidth
})
Expand Down
23 changes: 22 additions & 1 deletion src/components/__tests__/AirbnbStyleDatepicker.spec.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { shallow, createLocalVue } from '@vue/test-utils'
import AirbnbStyleDatepicker from '@/components/AirbnbStyleDatepicker'
import ClickOutside from '@/directives/ClickOutside'
import TestHelpers from 'test/test-helpers'

const localVue = createLocalVue()
localVue.directive('click-outside', ClickOutside)
let h

const createDatePickerInstance = (propsData, options) => {
if (!propsData) {
Expand All @@ -20,10 +22,12 @@ const createDatePickerInstance = (propsData, options) => {
...AirbnbStyleDatepicker,
...options
}
return shallow(component, {
const wrapper = shallow(component, {
localVue,
propsData
})
h = new TestHelpers(wrapper, expect)
return wrapper
}
const datepickerWrapper = '.airbnb-style-datepicker-wrapper'
let wrapper
Expand Down Expand Up @@ -187,5 +191,22 @@ describe('AirbnbStyleDatepicker', () => {
let dWrapper = wrapper.find(datepickerWrapper)
expect(dWrapper.classes()).toContain('full-screen')
})
test('disabled dates are not selectable', () => {
wrapper = createDatePickerInstance({
mode: 'single',
dateOne: '2018-10-10',
disabledDates: ['2018-10-20']
})
wrapper.setData({ triggerElement: document.createElement('div') })
wrapper.setData({ showDatepicker: true })

const disabledDate = wrapper.find('.day[data-date="2018-10-20"]')
expect(disabledDate.classes()).toContain('disabled')

disabledDate.find('button').trigger('click')
expect(wrapper.emitted()['date-one-selected'][0]).not.toEqual([
'2018-10-20'
])
})
})
})
51 changes: 51 additions & 0 deletions test/test-helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
class TestHelpers {
constructor(wrapper, expect) {
this.wrapper = wrapper
this.expect = expect
}

see(text, selector) {
let wrap = selector ? this.wrapper.find(selector) : this.wrapper
this.expect(wrap.html()).toContain(text)
}
doNotSee(text) {
this.expect(this.wrapper.html()).not.toContain(text)
}
type(text, input) {
let node = this.find(input)
node.element.value = text
node.trigger('input')
}
click(selector) {
this.wrapper.find(selector).trigger('click')
}
inputValueIs(text, selector) {
this.expect(this.find(selector).element.value).toBe(text)
}
inputValueIsNot(text, selector) {
this.expect(this.find(selector).element.value).not.toBe(text)
}
domHas(selector) {
this.expect(this.wrapper.contains(selector)).toBe(true)
}
domHasNot(selector) {
this.expect(this.wrapper.contains(selector)).toBe(false)
}
domHasLength(selector, length) {
this.expect(this.wrapper.findAll(selector).length).toBe(length)
}
isVisible(selector) {
this.expect(this.find(selector).hasStyle('display', 'none')).toBe(false)
}
isHidden(selector) {
this.expect(this.find(selector).hasStyle('display', 'none')).toBe(true)
}
find(selector) {
return this.wrapper.find(selector)
}
hasAttribute(selector, attribute) {
return this.expect(this.find(selector).attributes()[attribute]).toBeTruthy()
}
}

export default TestHelpers

0 comments on commit 04ec02b

Please sign in to comment.