mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-11-30 19:19:26 +08:00
Added validRange props in calendar to control date range
This commit is contained in:
parent
2aed46012e
commit
71f65a0be8
@ -15,6 +15,7 @@ export interface HeaderProps {
|
|||||||
onValueChange?: (value: moment.Moment) => void;
|
onValueChange?: (value: moment.Moment) => void;
|
||||||
onTypeChange?: (type: string) => void;
|
onTypeChange?: (type: string) => void;
|
||||||
value: any;
|
value: any;
|
||||||
|
validRange ?: [moment.Moment, moment.Moment];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Header extends React.Component<HeaderProps, any> {
|
export default class Header extends React.Component<HeaderProps, any> {
|
||||||
@ -27,11 +28,21 @@ export default class Header extends React.Component<HeaderProps, any> {
|
|||||||
private calenderHeaderNode: HTMLDivElement;
|
private calenderHeaderNode: HTMLDivElement;
|
||||||
|
|
||||||
getYearSelectElement(year: number) {
|
getYearSelectElement(year: number) {
|
||||||
const { yearSelectOffset, yearSelectTotal, locale, prefixCls, fullscreen } = this.props;
|
const {
|
||||||
const start = year - (yearSelectOffset as number);
|
yearSelectOffset,
|
||||||
const end = start + (yearSelectTotal as number);
|
yearSelectTotal,
|
||||||
|
locale,
|
||||||
|
prefixCls,
|
||||||
|
fullscreen,
|
||||||
|
validRange,
|
||||||
|
} = this.props;
|
||||||
|
let start = year - (yearSelectOffset as number);
|
||||||
|
let end = start + (yearSelectTotal as number);
|
||||||
|
if (validRange) {
|
||||||
|
start = validRange[0].get('year');
|
||||||
|
end = validRange[1].get('year') + 1;
|
||||||
|
}
|
||||||
const suffix = locale.year === '年' ? '年' : '';
|
const suffix = locale.year === '年' ? '年' : '';
|
||||||
|
|
||||||
const options: React.ReactElement<any>[] = [];
|
const options: React.ReactElement<any>[] = [];
|
||||||
for (let index = start; index < end; index++) {
|
for (let index = start; index < end; index++) {
|
||||||
options.push(<Option key={`${index}`}>{index + suffix}</Option>);
|
options.push(<Option key={`${index}`}>{index + suffix}</Option>);
|
||||||
@ -63,13 +74,22 @@ export default class Header extends React.Component<HeaderProps, any> {
|
|||||||
|
|
||||||
getMonthSelectElement(month: number, months: number[]) {
|
getMonthSelectElement(month: number, months: number[]) {
|
||||||
const props = this.props;
|
const props = this.props;
|
||||||
const { prefixCls, fullscreen } = props;
|
const { prefixCls, fullscreen, validRange, value } = props;
|
||||||
const options: React.ReactElement<any>[] = [];
|
const options: React.ReactElement<any>[] = [];
|
||||||
|
let start = 0;
|
||||||
for (let index = 0; index < 12; index++) {
|
let end = 12;
|
||||||
|
if (validRange) {
|
||||||
|
const [rangeStart, rangeEnd] = validRange;
|
||||||
|
const currentYear = value.get('year');
|
||||||
|
if (rangeEnd.get('year') === currentYear) {
|
||||||
|
end = rangeEnd.get('month') + 1;
|
||||||
|
} else {
|
||||||
|
start = rangeStart.get('month');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let index = start; index < end; index++) {
|
||||||
options.push(<Option key={`${index}`}>{months[index]}</Option>);
|
options.push(<Option key={`${index}`}>{months[index]}</Option>);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
size={fullscreen ? 'default' : 'small'}
|
size={fullscreen ? 'default' : 'small'}
|
||||||
@ -85,8 +105,21 @@ export default class Header extends React.Component<HeaderProps, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onYearChange = (year: string) => {
|
onYearChange = (year: string) => {
|
||||||
const newValue = this.props.value.clone();
|
const { value, validRange } = this.props;
|
||||||
|
const newValue = value.clone();
|
||||||
newValue.year(parseInt(year, 10));
|
newValue.year(parseInt(year, 10));
|
||||||
|
// switch the month so that it remains within range when year changes
|
||||||
|
if (validRange) {
|
||||||
|
const [ start, end ] = validRange;
|
||||||
|
const newYear = newValue.get('year');
|
||||||
|
const newMonth = newValue.get('month');
|
||||||
|
if (newYear === end.get('year') && newMonth > end.get('month')) {
|
||||||
|
newValue.month(end.get('month'));
|
||||||
|
}
|
||||||
|
if (newYear === start.get('year') && newMonth < start.get('month')) {
|
||||||
|
newValue.month(start.get('month'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onValueChange = this.props.onValueChange;
|
const onValueChange = this.props.onValueChange;
|
||||||
if (onValueChange) {
|
if (onValueChange) {
|
||||||
|
@ -15,6 +15,54 @@ describe('Calendar', () => {
|
|||||||
expect(Moment.isMoment(value)).toBe(true);
|
expect(Moment.isMoment(value)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('only Valid range should be selectable', () => {
|
||||||
|
const onSelect = jest.fn();
|
||||||
|
const validRange = [Moment('2018-02-02'), Moment('2018-02-18')];
|
||||||
|
const wrapper = mount(
|
||||||
|
<Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />
|
||||||
|
);
|
||||||
|
wrapper.find('[title="February 1, 2018"]').at(0).simulate('click');
|
||||||
|
wrapper.find('[title="February 2, 2018"]').at(0).simulate('click');
|
||||||
|
expect(onSelect.mock.calls.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dates other than in valid range should be disabled', () => {
|
||||||
|
const onSelect = jest.fn();
|
||||||
|
const validRange = [Moment('2018-02-02'), Moment('2018-02-18')];
|
||||||
|
const wrapper = mount(
|
||||||
|
<Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />
|
||||||
|
);
|
||||||
|
wrapper.find('[title="February 20, 2018"]').at(0).simulate('click');
|
||||||
|
const elem = wrapper.find('[title="February 20, 2018"]').hasClass('ant-fullcalendar-disabled-cell');
|
||||||
|
expect(elem).toEqual(true);
|
||||||
|
expect(onSelect.mock.calls.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('months other than in valid range should be disabled', () => {
|
||||||
|
const onSelect = jest.fn();
|
||||||
|
const validRange = [Moment('2018-02-02'), Moment('2018-05-18')];
|
||||||
|
const wrapper = mount(
|
||||||
|
<Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} mode="year" />
|
||||||
|
);
|
||||||
|
expect(wrapper.find('[title="Jan"]').at(0).hasClass('ant-fullcalendar-month-panel-cell-disabled')).toBe(true);
|
||||||
|
expect(wrapper.find('[title="Feb"]').at(0).hasClass('ant-fullcalendar-month-panel-cell-disabled')).toBe(false);
|
||||||
|
expect(wrapper.find('[title="Jun"]').at(0).hasClass('ant-fullcalendar-month-panel-cell-disabled')).toBe(true);
|
||||||
|
wrapper.find('[title="Jan"]').at(0).simulate('click');
|
||||||
|
wrapper.find('[title="Mar"]').at(0).simulate('click');
|
||||||
|
expect(onSelect.mock.calls.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getDateRange should returns a disabledDate function', () => {
|
||||||
|
const validRange = [Moment('2018-02-02'), Moment('2018-05-18')];
|
||||||
|
const wrapper = mount(
|
||||||
|
<Calendar validRange={validRange} defaultValue={Moment('2018-02-02')} />
|
||||||
|
);
|
||||||
|
const instance = wrapper.instance();
|
||||||
|
const disabledDate = instance.getDateRange(validRange);
|
||||||
|
expect(disabledDate(Moment('2018-06-02'))).toBe(true);
|
||||||
|
expect(disabledDate(Moment('2018-04-02'))).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('Calendar should change mode by prop', () => {
|
it('Calendar should change mode by prop', () => {
|
||||||
const monthMode = 'month';
|
const monthMode = 'month';
|
||||||
const yearMode = 'year';
|
const yearMode = 'year';
|
||||||
|
@ -43,3 +43,4 @@ When data is in the form of dates, such as schedules, timetables, prices calenda
|
|||||||
| value | The current selected date | [moment](http://momentjs.com/) | current date |
|
| value | The current selected date | [moment](http://momentjs.com/) | current date |
|
||||||
| onPanelChange | Callback for when panel changes | function(date: moment, mode: string) | - |
|
| onPanelChange | Callback for when panel changes | function(date: moment, mode: string) | - |
|
||||||
| onSelect | Callback for when a date is selected | function(date: moment) | - |
|
| onSelect | Callback for when a date is selected | function(date: moment) | - |
|
||||||
|
| validRange | to set valid range | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - |
|
||||||
|
@ -37,6 +37,7 @@ export interface CalendarProps {
|
|||||||
onPanelChange?: (date?: moment.Moment, mode?: CalendarMode) => void;
|
onPanelChange?: (date?: moment.Moment, mode?: CalendarMode) => void;
|
||||||
onSelect?: (date?: moment.Moment) => void;
|
onSelect?: (date?: moment.Moment) => void;
|
||||||
disabledDate?: (current: moment.Moment) => boolean;
|
disabledDate?: (current: moment.Moment) => boolean;
|
||||||
|
validRange ?: [moment.Moment, moment.Moment];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CalendarState {
|
export interface CalendarState {
|
||||||
@ -166,6 +167,21 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
|||||||
this.setValue(value, 'select');
|
this.setValue(value, 'select');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDateRange = (
|
||||||
|
validRange: [moment.Moment, moment.Moment],
|
||||||
|
disabledDate?: (current: moment.Moment) => boolean,
|
||||||
|
) => (current: moment.Moment) => {
|
||||||
|
if (!current) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const [ startDate, endDate ] = validRange;
|
||||||
|
const inRange = !current.isBetween(startDate, endDate, 'days', '[]');
|
||||||
|
if (disabledDate) {
|
||||||
|
return (disabledDate(current) || inRange);
|
||||||
|
}
|
||||||
|
return inRange;
|
||||||
|
}
|
||||||
|
|
||||||
renderCalendar = (locale: any, localeCode: string) => {
|
renderCalendar = (locale: any, localeCode: string) => {
|
||||||
const { state, props } = this;
|
const { state, props } = this;
|
||||||
const { value, mode } = state;
|
const { value, mode } = state;
|
||||||
@ -183,6 +199,12 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
|||||||
const monthCellRender = monthFullCellRender || this.monthCellRender;
|
const monthCellRender = monthFullCellRender || this.monthCellRender;
|
||||||
const dateCellRender = dateFullCellRender || this.dateCellRender;
|
const dateCellRender = dateFullCellRender || this.dateCellRender;
|
||||||
|
|
||||||
|
let disabledDate = props.disabledDate;
|
||||||
|
|
||||||
|
if (props.validRange) {
|
||||||
|
disabledDate = this.getDateRange(props.validRange, disabledDate);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cls} style={style}>
|
<div className={cls} style={style}>
|
||||||
<Header
|
<Header
|
||||||
@ -193,9 +215,11 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
|||||||
prefixCls={prefixCls}
|
prefixCls={prefixCls}
|
||||||
onTypeChange={this.onHeaderTypeChange}
|
onTypeChange={this.onHeaderTypeChange}
|
||||||
onValueChange={this.onHeaderValueChange}
|
onValueChange={this.onHeaderValueChange}
|
||||||
|
validRange={props.validRange}
|
||||||
/>
|
/>
|
||||||
<FullCalendar
|
<FullCalendar
|
||||||
{...props}
|
{...props}
|
||||||
|
disabledDate={disabledDate}
|
||||||
Select={noop}
|
Select={noop}
|
||||||
locale={locale.lang}
|
locale={locale.lang}
|
||||||
type={type}
|
type={type}
|
||||||
|
Loading…
Reference in New Issue
Block a user