From c37dc2fae8ee40179b65dd4442449b44aeb514a4 Mon Sep 17 00:00:00 2001 From: Jannik Schubert Date: Thu, 11 Mar 2021 14:52:19 +0100 Subject: [PATCH] Add the possibility to display weeknumbers in front of every week Add the possibility to display weeknumbers in front of every week --- README.md | 1 + src/Datetime.vue | 8 +- src/DatetimeCalendar.vue | 109 ++++++++++++++++++++++++---- src/DatetimePopup.vue | 20 +++-- test/specs/DatetimeCalendar.spec.js | 106 ++++++++++++++++++++------- 5 files changed, 196 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index c680923..7013be2 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ flow | `Array` | Depends of *type* | Customize steps flow, steps available: time title | `String` | `''` | Popup title. hide-backdrop | `Boolean` | `false` | Show/Hide backdrop. backdrop-click | `Boolean` | `true` | Enable/Disable backdrop click to cancel (outside click). +show-week-numbers | `Boolean`| `false` | Display weeknumber in front of every week in the calendar Input inherits all props not defined above but `style` and `class` will be inherited by root element. [See inheritAttrs option](https://vuejs.org/v2/api/#inheritAttrs) diff --git a/src/Datetime.vue b/src/Datetime.vue index a8d9e99..c271332 100644 --- a/src/Datetime.vue +++ b/src/Datetime.vue @@ -31,7 +31,9 @@ :auto="auto" :week-start="weekStart" :flow="flow" - :title="title"> + :title="title" + :show-week-numbers="showWeekNumbers" + > @@ -142,6 +144,10 @@ export default { backdropClick: { type: Boolean, default: true + }, + showWeekNumbers: { + type: Boolean, + default: false } }, diff --git a/src/DatetimeCalendar.vue b/src/DatetimeCalendar.vue index b34bb40..f504b59 100644 --- a/src/DatetimeCalendar.vue +++ b/src/DatetimeCalendar.vue @@ -13,10 +13,19 @@ -
-
{{ weekday }}
-
- {{ day.number }} +
+
+ + + + +
{{ weekNumber }}
+
+
+
{{ weekday }}
+
+ {{ day.number }} +
@@ -54,17 +63,25 @@ export default { weekStart: { type: Number, default: 1 + }, + showWeekNumbers: { + type: Boolean, + default: false } }, - data () { return { newDate: DateTime.fromObject({ year: this.year, month: this.month, zone: 'UTC' }), weekdays: weekdays(this.weekStart), - months: months() + months: months(), + weekNumbers: [] + } + }, + mounted () { + if (this.showWeekNumbers) { + this.getWeekNumberForDisplayedMonth() } }, - computed: { newYear () { return this.newDate.year @@ -92,11 +109,43 @@ export default { this.$emit('change', this.newYear, this.newMonth, day.number) }, + getWeekNumberForDisplayedMonth () { + const weekNumbers = [] + const weeksInMonth = this.days.length / 7 + const firstWeekOfMonth = DateTime.local(this.newYear, this.newMonth, 1) + .weekNumber + + weekNumbers.push(this.getFormattedWeekNumber(firstWeekOfMonth)) + let weekNumber + if (DateTime.local(this.newYear, this.newMonth, 7).weekNumber === 1) { + weekNumber = 0 + } else { + weekNumber = firstWeekOfMonth + } + for (let counter = 1; counter < weeksInMonth; counter++) { + weekNumber++ + weekNumbers.push(this.getFormattedWeekNumber(weekNumber)) + } + this.weekNumbers = weekNumbers + }, + getFormattedWeekNumber (weekNumber) { + let stringifiedWeekNumber = weekNumber.toString() + if (stringifiedWeekNumber.length === 1) { + stringifiedWeekNumber = '0' + stringifiedWeekNumber + } + return stringifiedWeekNumber + }, previousMonth () { this.newDate = this.newDate.minus({ months: 1 }) + if (this.showWeekNumbers) { + this.getWeekNumberForDisplayedMonth() + } }, nextMonth () { this.newDate = this.newDate.plus({ months: 1 }) + if (this.showWeekNumbers) { + this.getWeekNumberForDisplayedMonth() + } } } } @@ -151,13 +200,45 @@ export default { text-transform: capitalize; } -.vdatetime-calendar__month { +.vdatetime-calendar__display { + display: flex; + justify-content: space-evenly; +} + +.vdatetime-calendar__display__weeknumbers { + width: 20%; + padding-top: 36px; + padding-left: 20px; + + & table { + width: 100%; + } + + & tr { + height: 36px; + width: 100%; + } +} + +@media (max-width: 375px) { + .vdatetime-calendar__display__weeknumbers tr { + height: 10vw; + } +} + +.vdatetime-calendar__display__month--weeknumbers { + padding: 0 20px 0 5px; + transition: height .2s; + width: 80%; +} + +.vdatetime-calendar__display__month { padding: 0 20px; transition: height .2s; } -.vdatetime-calendar__month__weekday, -.vdatetime-calendar__month__day { +.vdatetime-calendar__display__month__weekday, +.vdatetime-calendar__display__month__day { display: inline-block; width: calc(100% / 7); line-height: 36px; @@ -190,15 +271,15 @@ export default { } } -.vdatetime-calendar__month__weekday { +.vdatetime-calendar__display__month__weekday { font-weight: bold; } -.vdatetime-calendar__month__day:hover > span > span { +.vdatetime-calendar__display__month__day:hover > span > span { background: #eee; } -.vdatetime-calendar__month__day--selected { +.vdatetime-calendar__display__month__day--selected { & > span > span, &:hover > span > span { color: #fff; @@ -206,7 +287,7 @@ export default { } } -.vdatetime-calendar__month__day--disabled { +.vdatetime-calendar__display__month__day--disabled { opacity: 0.4; cursor: default; diff --git a/src/DatetimePopup.vue b/src/DatetimePopup.vue index 5f62913..875aee8 100644 --- a/src/DatetimePopup.vue +++ b/src/DatetimePopup.vue @@ -20,14 +20,15 @@ :year="year" :month="month"> el.textContent) - expect(weekdays).deep.equal(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']) + const weekdays = vm.$$('.vdatetime-calendar__display__month__weekday').map(el => el.textContent) + const trimmedWeekdays = weekdays.map(weekday => weekday.trim()) + expect(trimmedWeekdays).deep.equal(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']) - const days = vm.$$('.vdatetime-calendar__month__day').map(el => el.textContent) + const days = vm.$$('.vdatetime-calendar__display__month__day').map(el => el.textContent) expect(days).deep.equal(['', '', '', '', '', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30']) }) @@ -32,7 +33,7 @@ describe('DatetimeCalendar.vue', function () { expect(vm.$('.vdatetime-calendar__current--month')).to.have.text('Julio 2018') - const weekdays = vm.$$('.vdatetime-calendar__month__weekday').map(el => el.textContent) + const weekdays = vm.$$('.vdatetime-calendar__display__month__weekday').map(el => el.textContent) expect(weekdays).deep.equal(['Lun.', 'Mar.', 'Mié.', 'Jue.', 'Vie.', 'Sáb.', 'Dom.']) }) @@ -46,10 +47,11 @@ describe('DatetimeCalendar.vue', function () { expect(vm.$('.vdatetime-calendar')).to.exist - const weekdays = vm.$$('.vdatetime-calendar__month__weekday').map(el => el.textContent) - expect(weekdays).deep.equal(['Thu', 'Fri', 'Sat', 'Sun', 'Mon', 'Tue', 'Wed']) + const weekdays = vm.$$('.vdatetime-calendar__display__month__weekday').map(el => el.textContent) + const trimmedWeekdays = weekdays.map(weekday => weekday.trim()) + expect(trimmedWeekdays).deep.equal(['Thu', 'Fri', 'Sat', 'Sun', 'Mon', 'Tue', 'Wed']) - const days = vm.$$('.vdatetime-calendar__month__day').map(el => el.textContent) + const days = vm.$$('.vdatetime-calendar__display__month__day').map(el => el.textContent) expect(days).deep.equal(['', '', '', '', '', '', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '', '', '', '', '']) }) @@ -60,7 +62,7 @@ describe('DatetimeCalendar.vue', function () { components: { DatetimeCalendar } }) - expect(vm.$('.vdatetime-calendar__month__day--selected')).to.have.text('10') + expect(vm.$('.vdatetime-calendar__display__month__day--selected')).to.have.text('10') }) it('should disable days before min date', function () { @@ -68,22 +70,22 @@ describe('DatetimeCalendar.vue', function () { ``, { components: { DatetimeCalendar }, - data () { + data() { return { minDate: LuxonDatetime.fromISO('2018-07-06T12:00:00.000Z').toUTC() } } }) - const monthDays = vm.$$('.vdatetime-calendar__month__day') + const monthDays = vm.$$('.vdatetime-calendar__display__month__day') monthDays.forEach(monthDay => { const dayNumber = parseInt(monthDay.textContent) if (isNaN(dayNumber) || dayNumber < 6) { - expect(monthDay).to.have.class('vdatetime-calendar__month__day--disabled') + expect(monthDay).to.have.class('vdatetime-calendar__display__month__day--disabled') } else { - expect(monthDay).to.have.not.class('vdatetime-calendar__month__day--disabled') + expect(monthDay).to.have.not.class('vdatetime-calendar__display__month__day--disabled') } }) }) @@ -93,25 +95,51 @@ describe('DatetimeCalendar.vue', function () { ``, { components: { DatetimeCalendar }, - data () { + data() { return { maxDate: LuxonDatetime.fromISO('2018-07-12T00:00:00.000Z').toUTC() } } }) - const monthDays = vm.$$('.vdatetime-calendar__month__day') + const monthDays = vm.$$('.vdatetime-calendar__display__month__day') monthDays.forEach(monthDay => { const dayNumber = parseInt(monthDay.textContent) if (isNaN(dayNumber) || dayNumber > 12) { - expect(monthDay).to.have.class('vdatetime-calendar__month__day--disabled') + expect(monthDay).to.have.class('vdatetime-calendar__display__month__day--disabled') } else { - expect(monthDay).to.have.not.class('vdatetime-calendar__month__day--disabled') + expect(monthDay).to.have.not.class('vdatetime-calendar__display__month__day--disabled') } }) }) + + it('should render a calendar with weeknumbers', function (done) { + const vm = createVM(this, + ``, + { + components: { DatetimeCalendar } + }) + + vm.$nextTick(() => { + expect(vm.$('.vdatetime-calendar__display__weeknumbers table tr:nth-child(1) td')).to.have.text('53') + done() + }) + }) + + it('should not render a calendar with weeknumbers', function (done) { + const vm = createVM(this, + ``, + { + components: { DatetimeCalendar } + }) + + vm.$nextTick(() => { + expect(vm.$('.vdatetime-calendar__display__weeknumbers')).not.exist + done() + }) + }) }) describe('navigation', function () { @@ -126,7 +154,7 @@ describe('DatetimeCalendar.vue', function () { vm.$nextTick(() => { expect(vm.$('.vdatetime-calendar__current--month')).to.have.text('May 2017') - const days = vm.$$('.vdatetime-calendar__month__day').map(el => el.textContent) + const days = vm.$$('.vdatetime-calendar__display__month__day').map(el => el.textContent) expect(days).deep.equal(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '', '', '', '']) done() }) @@ -143,7 +171,7 @@ describe('DatetimeCalendar.vue', function () { vm.$nextTick(() => { expect(vm.$('.vdatetime-calendar__current--month')).to.have.text('January 2018') - const days = vm.$$('.vdatetime-calendar__month__day').map(el => el.textContent) + const days = vm.$$('.vdatetime-calendar__display__month__day').map(el => el.textContent) expect(days).deep.equal(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '', '', '', '']) done() }) @@ -160,7 +188,7 @@ describe('DatetimeCalendar.vue', function () { vm.$nextTick(() => { expect(vm.$('.vdatetime-calendar__current--month')).to.have.text('March 2017') - const days = vm.$$('.vdatetime-calendar__month__day').map(el => el.textContent) + const days = vm.$$('.vdatetime-calendar__display__month__day').map(el => el.textContent) expect(days).deep.equal(['', '', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '', '']) done() }) @@ -177,11 +205,39 @@ describe('DatetimeCalendar.vue', function () { vm.$nextTick(() => { expect(vm.$('.vdatetime-calendar__current--month')).to.have.text('December 2016') - const days = vm.$$('.vdatetime-calendar__month__day').map(el => el.textContent) + const days = vm.$$('.vdatetime-calendar__display__month__day').map(el => el.textContent) expect(days).deep.equal(['', '', '', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '']) done() }) }) + + it('should render weeknumbers when clicking next', function (done) { + const vm = createVM(this, + ``, + { + components: { DatetimeCalendar } + }) + vm.$('.vdatetime-calendar__navigation--next').click() + + vm.$nextTick(() => { + expect(vm.$('.vdatetime-calendar__display__weeknumbers table tr:nth-child(1) td')).to.have.text('05') + done() + }) + }) + + it('should render weeknumbers when clicking previous', function (done) { + const vm = createVM(this, + ``, + { + components: { DatetimeCalendar } + }) + vm.$('.vdatetime-calendar__navigation--previous').click() + + vm.$nextTick(() => { + expect(vm.$('.vdatetime-calendar__display__weeknumbers table tr:nth-child(5) td')).to.have.text('53') + done() + }) + }) }) describe('events', function () { @@ -190,7 +246,7 @@ describe('DatetimeCalendar.vue', function () { ``, { components: { DatetimeCalendar }, - data () { + data() { return { year: null, month: null, @@ -198,7 +254,7 @@ describe('DatetimeCalendar.vue', function () { } }, methods: { - onChange (year, month, day) { + onChange(year, month, day) { this.year = year this.month = month this.day = day @@ -206,7 +262,7 @@ describe('DatetimeCalendar.vue', function () { } }) - vm.$$('.vdatetime-calendar__month__day')[10].click() + vm.$$('.vdatetime-calendar__display__month__day')[10].click() expect(vm.year).to.be.equal(2017) expect(vm.month).to.be.equal(4) expect(vm.day).to.be.equal(6) @@ -217,7 +273,7 @@ describe('DatetimeCalendar.vue', function () { ``, { components: { DatetimeCalendar }, - data () { + data() { return { maxDate: LuxonDatetime.fromISO('2017-04-04T04:04:04.000Z'), spy: sinon.spy() @@ -225,7 +281,7 @@ describe('DatetimeCalendar.vue', function () { } }) - vm.$$('.vdatetime-calendar__month__day')[10].click() + vm.$$('.vdatetime-calendar__display__month__day')[10].click() expect(vm.spy).to.have.not.been.called }) })