fix(components): [date-picker] disabledDate is invalid when selecting year or month picker (#15848)

* fix(components): [date-picker] props.disabledDate is invalid
when selecting year or month picker

* test(components): [date-picker] props disabledDate

* fix(components): [date-picker] props.disabledDate is invalid

* fix: test

* fix: error

* fix: Disable time validation for date selectors of years and months

* fix: test

* fix: [date-picker] props.disabledDate is invalid

* fix: [date-picker] props.disabledDate is invalid

---------

Co-authored-by: qiang <qw13131wang@gmail.com>
This commit is contained in:
xingyixiang 2024-08-11 19:25:01 +08:00 committed by GitHub
parent e815102d70
commit 9aa70ecd4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 140 additions and 16 deletions

View File

@ -365,6 +365,66 @@ describe('DatePicker', () => {
expect(document.querySelector('.disabled')).not.toBeNull()
})
it('select year picker when using disabledDate prop', async () => {
const wrapper = _mount(
`<el-date-picker
v-model="value"
:disabledDate="disabledDate"
/>`,
() => ({
value: '2024-01-01',
disabledDate(time) {
const dayTime = new Date(2023, 1, 4).getTime()
return time.getTime() < dayTime
},
})
)
const input = wrapper.find('input')
input.trigger('blur')
input.trigger('focus')
await nextTick()
const yearLabel: HTMLElement = document.querySelectorAll(
'.el-date-picker__header-label'
)[0]
yearLabel.click()
await nextTick()
const yearCells = document.querySelectorAll('.el-date-table-cell__text')
const year2023 = [...yearCells].find((item) => item.innerHTML === '2023')
year2023.click()
await nextTick()
expect(input.element.value).toBe('2023-02-04')
})
it('select month picker when using disabledDate prop', async () => {
const wrapper = _mount(
`<el-date-picker
v-model="value"
:disabledDate="disabledDate"
/>`,
() => ({
value: '2023-05-01',
disabledDate(time) {
const dayTime = new Date(2023, 1, 4).getTime()
return time.getTime() < dayTime
},
})
)
const input = wrapper.find('input')
input.trigger('blur')
input.trigger('focus')
await nextTick()
const monthLabel: HTMLElement = document.querySelectorAll(
'.el-date-picker__header-label'
)[1]
monthLabel.click()
await nextTick()
const monthCells = document.querySelectorAll('.el-date-table-cell__text')
const februaryCell = monthCells[1]
februaryCell.click()
await nextTick()
expect(input.element.value).toBe('2023-02-04')
})
it('should work when using disabledDate prop and daterange type', async () => {
const wrapper = _mount(
`<el-date-picker

View File

@ -35,9 +35,9 @@
import { computed, nextTick, ref, watch } from 'vue'
import dayjs from 'dayjs'
import { useLocale, useNamespace } from '@element-plus/hooks'
import { rangeArr } from '@element-plus/components/time-picker'
import { castArray, hasClass } from '@element-plus/utils'
import { basicMonthTableProps } from '../props/basic-month-table'
import { datesInMonth, getValidDateOfMonth } from '../utils'
import ElDatePickerCell from './basic-cell-render'
type MonthCell = {
@ -51,12 +51,6 @@ type MonthCell = {
inRange: boolean
}
const datesInMonth = (year: number, month: number, lang: string) => {
const firstDay = dayjs().locale(lang).startOf('month').month(month).year(year)
const numOfDays = firstDay.daysInMonth()
return rangeArr(numOfDays).map((n) => firstDay.add(n, 'day').toDate())
}
const props = defineProps(basicMonthTableProps)
const emit = defineEmits(['changerange', 'pick', 'select'])
@ -230,11 +224,15 @@ const handleMonthTableClick = (event: MouseEvent | KeyboardEvent) => {
emit('pick', castArray(props.parsedValue), false)
return
}
const newMonth = props.date.startOf('month').month(month)
const newMonth = getValidDateOfMonth(
props.date.year(),
month,
lang.value,
props.disabledDate
)
const newValue = hasClass(target, 'current')
? castArray(props.parsedValue).filter(
(d) => Number(d) !== Number(newMonth)
(d) => d?.month() !== newMonth.month()
)
: castArray(props.parsedValue).concat([dayjs(newMonth)])
emit('pick', newValue)

View File

@ -34,6 +34,7 @@ import { useLocale, useNamespace } from '@element-plus/hooks'
import { rangeArr } from '@element-plus/components/time-picker'
import { castArray, hasClass } from '@element-plus/utils'
import { basicYearTableProps } from '../props/basic-year-table'
import { getValidDateOfYear } from '../utils'
import ElDatePickerCell from './basic-cell-render'
type YearCell = {
@ -201,9 +202,14 @@ const handleYearTableClick = (event: MouseEvent | KeyboardEvent) => {
emit('pick', castArray(props.parsedValue), false)
return
}
const vaildYear = getValidDateOfYear(
newDate.startOf('year'),
lang.value,
props.disabledDate
)
const newValue = hasClass(target, 'current')
? castArray(props.parsedValue).filter((d) => d?.year() !== selectedYear)
: castArray(props.parsedValue).concat([newDate])
: castArray(props.parsedValue).concat([vaildYear])
emit('pick', newValue)
} else {
emit('pick', selectedYear)

View File

@ -224,6 +224,7 @@ import {
} from '@element-plus/icons-vue'
import { TOOLTIP_INJECTION_KEY } from '@element-plus/components/tooltip'
import { panelDatePickProps } from '../props/panel-date-pick'
import { getValidDateOfMonth, getValidDateOfYear } from '../utils'
import DateTable from './basic-date-table.vue'
import MonthTable from './basic-month-table.vue'
import YearTable from './basic-year-table.vue'
@ -429,12 +430,22 @@ const handleMonthPick = async (
keepOpen?: boolean
) => {
if (selectionMode.value === 'month') {
innerDate.value = innerDate.value.startOf('month').month(month as number)
innerDate.value = getValidDateOfMonth(
innerDate.value.year(),
month as number,
lang.value,
disabledDate
)
emit(innerDate.value, false)
} else if (selectionMode.value === 'months') {
emit(month as MonthsPickerEmits, keepOpen ?? true)
} else {
innerDate.value = innerDate.value.startOf('month').month(month as number)
innerDate.value = getValidDateOfMonth(
innerDate.value.year(),
month as number,
lang.value,
disabledDate
)
currentView.value = 'date'
if (['month', 'year', 'date', 'week'].includes(selectionMode.value)) {
emit(innerDate.value, true)
@ -450,12 +461,14 @@ const handleYearPick = async (
keepOpen?: boolean
) => {
if (selectionMode.value === 'year') {
innerDate.value = innerDate.value.startOf('year').year(year as number)
const data = innerDate.value.startOf('year').year(year as number)
innerDate.value = getValidDateOfYear(data, lang.value, disabledDate)
emit(innerDate.value, false)
} else if (selectionMode.value === 'years') {
emit(year as YearsPickerEmits, keepOpen ?? true)
} else {
innerDate.value = innerDate.value.year(year as number)
const data = innerDate.value.year(year as number)
innerDate.value = getValidDateOfYear(data, lang.value, disabledDate)
currentView.value = 'month'
if (['month', 'year', 'date', 'week'].includes(selectionMode.value)) {
emit(innerDate.value, true)

View File

@ -21,9 +21,11 @@ export type RangeState = {
selecting: boolean
}
export type DisabledDateType = (date: Date) => boolean
export const datePickerSharedProps = buildProps({
disabledDate: {
type: definePropType<(date: Date) => boolean>(Function),
type: definePropType<DisabledDateType>(Function),
},
date: {
type: definePropType<Dayjs>(Object),

View File

@ -1,8 +1,10 @@
import dayjs from 'dayjs'
import { isArray } from '@element-plus/utils'
import { rangeArr } from '@element-plus/components/time-picker'
import type { Dayjs } from 'dayjs'
import type { DateCell } from './date-picker.type'
import type { DisabledDateType } from './props/shared'
type DayRange = [Dayjs | undefined, Dayjs | undefined]
@ -132,3 +134,46 @@ export const buildPickerTable = (
setRowMetadata?.(row)
}
}
export const datesInMonth = (year: number, month: number, lang: string) => {
const firstDay = dayjs().locale(lang).startOf('month').month(month).year(year)
const numOfDays = firstDay.daysInMonth()
return rangeArr(numOfDays).map((n) => firstDay.add(n, 'day').toDate())
}
export const getValidDateOfMonth = (
year: number,
month: number,
lang: string,
disabledDate?: DisabledDateType
) => {
const _value = dayjs().year(year).month(month).startOf('month')
const _date = datesInMonth(year, month, lang).find((date) => {
return !disabledDate?.(date)
})
if (_date) {
return dayjs(_date).locale(lang)
}
return _value.locale(lang)
}
export const getValidDateOfYear = (
value: Dayjs,
lang: string,
disabledDate?: DisabledDateType
) => {
const year = value.year()
if (!disabledDate?.(value.toDate())) {
return value.locale(lang)
}
const month = value.month()
if (!datesInMonth(year, month, lang).every(disabledDate)) {
return getValidDateOfMonth(year, month, lang, disabledDate)
}
for (let i = 0; i < 12; i++) {
if (!datesInMonth(year, i, lang).every(disabledDate)) {
return getValidDateOfMonth(year, i, lang, disabledDate)
}
}
return value
}