fix(components): [date-picker] add missing data slot (#19839)

* feat: add missing date-picker data slot

* chore: fix review & forward old test case

Co-authored-by: btea <2356281422@qq.com>

* Update docs/en-US/component/date-picker.md

Co-authored-by: btea <2356281422@qq.com>

* chore: update

---------

Co-authored-by: btea <2356281422@qq.com>
This commit is contained in:
Noblet Ouways
2025-08-21 18:19:29 +02:00
committed by GitHub
parent 5647b3e392
commit 2e19e59e67
11 changed files with 147 additions and 42 deletions

View File

@@ -134,14 +134,15 @@ For data details, please refer:
```ts
interface DateCell {
column: number
customClass: string
customClass: string | undefined
disabled: boolean
end: boolean
inRange: boolean
row: number
selected: Dayjs
isCurrent: boolean
selected: Dayjs | undefined
isCurrent: boolean | undefined
isSelected: boolean
renderText: string | undefined
start: boolean
text: number
timestamp: number

View File

@@ -259,13 +259,6 @@ export const useBasicDateTable = (
}
}
const isSelectedCell = (cell: DateCell) => {
return (
(!unref(hasCurrent) && cell?.text === 1 && cell.type === 'normal') ||
cell.isCurrent
)
}
const handleFocus = (event: FocusEvent) => {
if (focusWithClick || unref(hasCurrent) || props.selectionMode !== 'date')
return
@@ -391,7 +384,6 @@ export const useBasicDateTable = (
focus,
isCurrent,
isWeekActive,
isSelectedCell,
handlePickDate,
handleMouseUp,

View File

@@ -30,11 +30,11 @@
<td
v-for="(cell, columnKey) in row"
:key="`${rowKey}.${columnKey}`"
:ref="(el) => !isUnmounting && isSelectedCell(cell) && (currentCellRef = el as HTMLElement)"
:ref="(el) => !isUnmounting && cell.isSelected && (currentCellRef = el as HTMLElement)"
:class="getCellClasses(cell)"
:aria-current="cell.isCurrent ? 'date' : undefined"
:aria-selected="cell.isCurrent"
:tabindex="isSelectedCell(cell) ? 0 : -1"
:tabindex="cell.isSelected ? 0 : -1"
@focus="handleFocus"
>
<el-date-picker-cell :cell="cell" />
@@ -68,7 +68,6 @@ const {
focus,
isCurrent,
isWeekActive,
isSelectedCell,
handlePickDate,
handleMouseUp,

View File

@@ -11,11 +11,11 @@
<td
v-for="(cell, key_) in row"
:key="key_"
:ref="(el) => isSelectedCell(cell) && (currentCellRef = el as HTMLElement)"
:ref="(el) => cell.isSelected && (currentCellRef = el as HTMLElement)"
:class="getCellStyle(cell)"
:aria-selected="`${isSelectedCell(cell)}`"
:aria-selected="!!cell.isSelected"
:aria-label="t(`el.datepicker.month${+cell.text + 1}`)"
:tabindex="isSelectedCell(cell) ? 0 : -1"
:tabindex="cell.isSelected ? 0 : -1"
@keydown.space.prevent.stop="handleMonthTableClick"
@keydown.enter.prevent.stop="handleMonthTableClick"
>
@@ -40,15 +40,25 @@ import { basicMonthTableProps } from '../props/basic-month-table'
import { datesInMonth, getValidDateOfMonth } from '../utils'
import ElDatePickerCell from './basic-cell-render'
import type { Dayjs } from 'dayjs'
type MonthCell = {
column: number
row: number
customClass: string | undefined
disabled: boolean
start: boolean
end: boolean
text: number
type: 'normal' | 'today'
inRange: boolean
row: number
selected: Dayjs | undefined
isCurrent: boolean | undefined
isSelected: boolean
start: boolean
text: number
renderText: string | undefined
timestamp: number | undefined
date: Date | undefined
dayjs: Dayjs | undefined
type: 'normal' | 'today'
}
const props = defineProps(basicMonthTableProps)
@@ -66,11 +76,7 @@ const months = ref(
.monthsShort()
.map((_) => _.toLowerCase())
)
const tableRows = ref<MonthCell[][]>([
[] as MonthCell[],
[] as MonthCell[],
[] as MonthCell[],
])
const tableRows = ref<MonthCell[][]>([[], [], []])
const lastRow = ref<number>()
const lastColumn = ref<number>()
const rows = computed<MonthCell[][]>(() => {
@@ -90,6 +96,14 @@ const rows = computed<MonthCell[][]>(() => {
end: false,
text: -1,
disabled: false,
isSelected: false,
customClass: undefined,
date: undefined,
dayjs: undefined,
isCurrent: undefined,
selected: undefined,
renderText: undefined,
timestamp: undefined,
})
cell.type = 'normal'
@@ -130,8 +144,14 @@ const rows = computed<MonthCell[][]>(() => {
cell.type = 'today'
}
const cellDate = calTime.toDate()
cell.text = index
cell.disabled = props.disabledDate?.(calTime.toDate()) || false
cell.disabled = props.disabledDate?.(cellDate) || false
cell.date = cellDate
cell.customClass = props.cellClassName?.(cellDate)
cell.dayjs = calTime
cell.timestamp = calTime.valueOf()
cell.isSelected = isSelectedCell(cell)
}
}
return rows
@@ -161,6 +181,9 @@ const getCellStyle = (cell: MonthCell) => {
) >= 0
style.today = today.getFullYear() === year && today.getMonth() === month
if (cell.customClass) {
style[cell.customClass] = true
}
if (cell.inRange) {
style['in-range'] = true

View File

@@ -11,12 +11,12 @@
<td
v-for="(cell, cellKey) in row"
:key="`${rowKey}_${cellKey}`"
:ref="(el) => isSelectedCell(cell) && (currentCellRef = el as HTMLElement)"
:ref="(el) => cell.isSelected && (currentCellRef = el as HTMLElement)"
class="available"
:class="getCellKls(cell)"
:aria-selected="isSelectedCell(cell)"
:aria-selected="cell.isSelected"
:aria-label="String(cell.text)"
:tabindex="isSelectedCell(cell) ? 0 : -1"
:tabindex="cell.isSelected ? 0 : -1"
@keydown.space.prevent.stop="handleYearTableClick"
@keydown.enter.prevent.stop="handleYearTableClick"
>
@@ -37,15 +37,25 @@ import { basicYearTableProps } from '../props/basic-year-table'
import { getValidDateOfYear } from '../utils'
import ElDatePickerCell from './basic-cell-render'
import type { Dayjs } from 'dayjs'
type YearCell = {
column: number
row: number
customClass: string | undefined
disabled: boolean
start: boolean
end: boolean
text: number
type: 'normal' | 'today'
inRange: boolean
row: number
selected: Dayjs | undefined
isCurrent: boolean | undefined
isSelected: boolean
start: boolean
text: number
renderText: string | undefined
timestamp: number | undefined
type: 'normal' | 'today'
date: Date | undefined
dayjs: Dayjs | undefined
}
const datesInYear = (year: number, lang: string) => {
@@ -91,6 +101,14 @@ const rows = computed(() => {
end: false,
text: -1,
disabled: false,
isSelected: false,
customClass: undefined,
date: undefined,
dayjs: undefined,
isCurrent: undefined,
selected: undefined,
renderText: undefined,
timestamp: undefined,
}
}
cell.type = 'normal'
@@ -131,8 +149,12 @@ const rows = computed(() => {
}
cell.text = index
const cellDate = calTime.toDate()
cell.disabled =
(props.disabledDate && props.disabledDate(cellDate)) || false
cell.disabled = props.disabledDate?.(cellDate) || false
cell.date = cellDate
cell.customClass = props.cellClassName?.(cellDate)
cell.dayjs = calTime
cell.timestamp = calTime.valueOf()
cell.isSelected = isSelectedCell(cell)
row[j] = cell
}
}
@@ -158,6 +180,9 @@ const getCellKls = (cell: YearCell) => {
kls.current =
castArray(props.parsedValue).findIndex((d) => d!.year() === year) >= 0
if (cell.customClass) {
kls[cell.customClass] = true
}
if (cell.inRange) {
kls['in-range'] = true

View File

@@ -166,6 +166,7 @@
:disabled-date="disabledDate"
:disabled="disabled"
:parsed-value="parsedValue"
:cell-class-name="cellClassName"
@pick="handleYearPick"
/>
<month-table
@@ -176,6 +177,7 @@
:parsed-value="parsedValue"
:disabled-date="disabledDate"
:disabled="disabled"
:cell-class-name="cellClassName"
@pick="handleMonthPick"
/>
</div>

View File

@@ -63,6 +63,7 @@
:range-state="rangeState"
:disabled-date="disabledDate"
:disabled="disabled"
:cell-class-name="cellClassName"
@changerange="handleChangeRange"
@pick="handleRangePick"
@select="onSelect"
@@ -103,6 +104,7 @@
:range-state="rangeState"
:disabled-date="disabledDate"
:disabled="disabled"
:cell-class-name="cellClassName"
@changerange="handleChangeRange"
@pick="handleRangePick"
@select="onSelect"
@@ -151,7 +153,7 @@ const isDefaultFormat = inject(
ROOT_PICKER_IS_DEFAULT_FORMAT_INJECTION_KEY,
undefined
) as any
const { shortcuts, disabledDate } = pickerBase.props
const { shortcuts, disabledDate, cellClassName } = pickerBase.props
const format = toRef(pickerBase.props, 'format')
const defaultValue = toRef(pickerBase.props, 'defaultValue')
const leftDate = ref(dayjs().locale(lang.value))

View File

@@ -48,6 +48,7 @@
:range-state="rangeState"
:disabled-date="disabledDate"
:disabled="disabled"
:cell-class-name="cellClassName"
@changerange="handleChangeRange"
@pick="handleRangePick"
@select="onSelect"
@@ -86,6 +87,7 @@
:range-state="rangeState"
:disabled-date="disabledDate"
:disabled="disabled"
:cell-class-name="cellClassName"
@changerange="handleChangeRange"
@pick="handleRangePick"
@select="onSelect"
@@ -137,7 +139,7 @@ const isDefaultFormat = inject(
undefined
) as any
const pickerBase = inject(PICKER_BASE_INJECTION_KEY) as any
const { shortcuts, disabledDate } = pickerBase.props
const { shortcuts, disabledDate, cellClassName } = pickerBase.props
const format = toRef(pickerBase.props, 'format')
const defaultValue = toRef(pickerBase.props, 'defaultValue')

View File

@@ -1,4 +1,4 @@
import { buildProps, definePropType } from '@element-plus/utils'
import { buildProps } from '@element-plus/utils'
import { datePickerSharedProps, selectionModeWithDefault } from './shared'
import type { ExtractPropTypes, __ExtractPublicPropTypes } from 'vue'
@@ -6,9 +6,6 @@ import type { Dayjs } from 'dayjs'
export const basicDateTableProps = buildProps({
...datePickerSharedProps,
cellClassName: {
type: definePropType<(date: Date) => string>(Function),
},
showWeekNumber: Boolean,
selectionMode: selectionModeWithDefault('date'),
} as const)

View File

@@ -23,8 +23,12 @@ export type RangeState = {
}
export type DisabledDateType = (date: Date) => boolean
export type CellClassNameType = (date: Date) => string
export const datePickerSharedProps = buildProps({
cellClassName: {
type: definePropType<CellClassNameType>(Function),
},
disabledDate: {
type: definePropType<DisabledDateType>(Function),
},

View File

@@ -588,6 +588,64 @@ describe('DatePicker', () => {
vi.useRealTimers()
})
it('should have same common propreties for default slot', async () => {
const testCellData = (cell) => {
const cellProperties = [
'column',
'type',
'text',
'start',
'timestamp',
'dayjs',
'date',
'isSelected',
'inRange',
'row',
'customClass',
'end',
] //TODO: we should later increase the list in order to fit DateCell perfectly
expect(Object.keys(cell)).toEqual(expect.arrayContaining(cellProperties))
const values = Object.entries(cell)
.filter(([key]) => cellProperties.includes(key))
.map(([, val]) => val)
for (const value of values) {
expect(value).toBeDefined()
}
}
const wrapper = _mount(
`
<el-date-picker :cellClassName="() => 'hello'">
<template #default="cell">
<div class="custom-cell" data-testid="" @click="testCellData(cell)">
click me
</div>
</template>
</el-date-picker>
`,
() => ({ testCellData })
)
const types = [
'year',
'years',
'month',
'months',
'date',
'dates',
'week',
'datetime',
'datetimerange',
'daterange',
'monthrange',
'yearrange',
]
for (const type of types) {
await wrapper.setProps({ type })
{
;(document.querySelector('.custom-cell') as HTMLElement).click()
}
}
})
it('custom content', async () => {
const wrapper = _mount(
`<el-date-picker