Skip to content

Commit

Permalink
fix: Incorrect handling of allowed-dates in month-picker and `yea…
Browse files Browse the repository at this point in the history
…r-picker` modes (fixes #1035, fixes #1047)
  • Loading branch information
Jasenkoo committed Jan 3, 2025
1 parent 36ed4a2 commit 70a2575
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 51 deletions.
4 changes: 2 additions & 2 deletions src/VueDatePicker/components/MonthPicker/month-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { checkMinMaxValue, getMonths, groupListAndMap } from '@/utils/util';
import {
checkHighlightMonth,
getDate,
getDisabledMonths,
getMaxMonth,
getMinMonth,
isDateBetween,
isMonthAllowed,
isMonthDisabled,
resetDate,
setDateMonthOrYear,
} from '@/utils/date-utils';
Expand Down Expand Up @@ -136,7 +136,7 @@ export const useMonthPicker = (props: PickerBasePropsType, emit: VueEmit) => {
getMinMonth(year.value(instance), propDates.value.minDate),
getMaxMonth(year.value(instance), propDates.value.maxDate),
) ||
getDisabledMonths(propDates.value.disabledDates, year.value(instance)).includes(month.value) ||
isMonthDisabled(propDates.value.disabledDates, year.value(instance), month.value) ||
defaultedFilters.value.months?.includes(month.value) ||
!isMonthAllowed(propDates.value.allowedDates, year.value(instance), month.value);
const isBetween = isMonthBetween(month.value, instance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const useQuarterPicker = (props: PickerBasePropsType, emit: VueEmit) => {
value: start,
active: isQuarterActive.value(start),
highlighted,
disabled: disabled,
disabled,
isBetween,
};
});
Expand Down
19 changes: 18 additions & 1 deletion src/VueDatePicker/components/YearPicker/year-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@ export const useYearPicker = (props: PickerBasePropsType, emit: VueEmit) => {
return false;
};

const isYearAllowed = (year: number) => {
if (propDates.value.allowedDates instanceof Map) {
return propDates.value.allowedDates.size ? propDates.value.allowedDates.has(`${year}`) : false;
}
return true;
};

const isYearDisabled = (year: number) => {
if (propDates.value.disabledDates instanceof Map) {
return propDates.value.disabledDates.size ? propDates.value.disabledDates.has(`${year}`) : false;
}
return true;
};

const groupedYears = computed(() => {
return groupListAndMap(getYears(props.yearRange, props.locale, props.reverseYears), (year: IDefaultSelect) => {
const active = isYearActive(year.value);
Expand All @@ -58,7 +72,10 @@ export const useYearPicker = (props: PickerBasePropsType, emit: VueEmit) => {
year.value,
getMinMaxYear(propDates.value.minDate),
getMinMaxYear(propDates.value.maxDate),
) || defaultedFilters.value.years.includes(year.value);
) ||
defaultedFilters.value.years.includes(year.value) ||
!isYearAllowed(year.value) ||
isYearDisabled(year.value);
const isBetween = isYearBetween(year.value) && !active;
const highlighted = checkHighlightYear(defaultedHighlight.value, year.value);
return { active, disabled, isBetween, highlighted };
Expand Down
2 changes: 2 additions & 0 deletions src/VueDatePicker/composables/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ export const useDefaults = (props: AllPropsType | PickerBasePropsType) => {
markers: props.markers,
timezone: defaultedTz.value,
isSpecific: props.monthPicker || props.yearPicker || props.quarterPicker,
isMonthPicker: props.monthPicker,
isYearPicker: props.yearPicker,
}),
);

Expand Down
6 changes: 6 additions & 0 deletions src/VueDatePicker/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,9 @@ export enum EventKey {
pageUp = 'PageUp',
pageDown = 'PageDown',
}

export enum MAP_KEY_FORMAT {
MONTH_AND_YEAR = 'MM-yyyy',
YEAR = 'yyyy',
DATE = 'dd-MM-yyyy',
}
2 changes: 2 additions & 0 deletions src/VueDatePicker/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,8 @@ export interface MapPropDatesOpts {
markers: IMarker[];
timezone: TimeZoneConfig | undefined;
isSpecific?: boolean;
isMonthPicker: boolean;
isYearPicker: boolean;
}

export type CustomClass = string | string[];
Expand Down
21 changes: 10 additions & 11 deletions src/VueDatePicker/utils/date-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import type {
} from '@/interfaces';

import type { Duration, Locale } from 'date-fns';
import { padZero } from '../../../tests/utils.ts';

const parseTextToDate = (
value: string,
Expand Down Expand Up @@ -403,28 +404,26 @@ export const checkTimeMinMax = (
// Returns a getDate object with a set of time from a provided date
export const setTimeValue = (date: Date): Date => set(getDate(), getTimeObj(date));

export const getDisabledMonths = (
export const isMonthDisabled = (
disabledDates: Map<string, Date | null> | null | ((date: Date) => boolean),
year: number,
month: number,
) => {
if (disabledDates instanceof Map) {
return Array.from(disabledDates.values())
.filter((date) => getYear(getDate(date)) === year)
.map((date) => getMonth(date as Date));
const key = `${padZero(month + 1)}-${year}`;
return disabledDates.size ? disabledDates.has(key) : false;
}
return [];
return false;
};

export const isMonthAllowed = (
disabledDates: Map<string, Date | null> | null | ((date: Date) => boolean),
allowedDates: Map<string, Date | null> | null | ((date: Date) => boolean),
year: number,
month: number,
) => {
if (disabledDates instanceof Map) {
const months = Array.from(disabledDates.values())
.filter((date) => getYear(getDate(date)) === year)
.map((date) => getMonth(date as Date));
return months.length ? months.includes(month) : true;
if (allowedDates instanceof Map) {
const key = `${padZero(month + 1)}-${year}`;
return allowedDates.size ? allowedDates.has(key) : true;
}
return true;
};
Expand Down
57 changes: 30 additions & 27 deletions src/VueDatePicker/utils/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
import type {
AriaLabels,
IFormat,
Transition,
TextInputOptions,
DateFilter,
ActionRowData,
MultiCalendarsProp,
MultiCalendarsOptions,
OptionEnabled,
TextInputProp,
InlineProp,
InlineOptions,
AriaLabels,
Config,
HighlightProp,
DateFilter,
Highlight,
HighlightFn,
WeekNumbersProp,
WeekNumbersOpts,
RangeProp,
RangeConfig,
TimeZoneProp,
TimeZoneConfig,
HighlightProp,
IFormat,
IMarker,
PropDates,
MultiDatesProp,
MultiDatesDefault,
InlineOptions,
InlineProp,
MapPropDatesOpts,
MultiCalendarsOptions,
MultiCalendarsProp,
MultiDatesDefault,
MultiDatesProp,
OptionEnabled,
PropDates,
RangeConfig,
RangeProp,
TextInputOptions,
TextInputProp,
TimeZoneConfig,
TimeZoneProp,
Transition,
UIOpts,
UIParsed,
WeekNumbersOpts,
WeekNumbersProp,
} from '@/interfaces';
import { getDate } from '@/utils/date-utils';
import { dateToTimezoneSafe, sanitizeDateToLocal } from '@/utils/timezone';
import { getMapKey, shouldMap } from '@/utils/util';
import { getMapKey, getMapKeyType, shouldMap } from '@/utils/util';
import { MAP_KEY_FORMAT } from '@/constants';

export const mergeDefaultTransitions = (conf: Partial<Transition>): Transition => ({
menuAppearTop: 'dp-menu-appear-top',
Expand Down Expand Up @@ -254,12 +255,13 @@ export const getDefaultTimeZone = (timeZone: TimeZoneProp) => {
const datesArrToMap = (
datesArr: (Date | string | number)[],
timezone: TimeZoneConfig | undefined,
format: MAP_KEY_FORMAT,
reset?: boolean,
): Map<string, Date | null> => {
return new Map(
datesArr.map((date) => {
const d = dateToTimezoneSafe(date, timezone, reset);
return [getMapKey(d), d];
return [getMapKey(d, format), d];
}),
);
};
Expand All @@ -269,7 +271,7 @@ const mapMarkers = (markers: IMarker[], timezone: TimeZoneConfig | undefined) =>
return new Map(
markers.map((marker) => {
const date = dateToTimezoneSafe(marker.date, timezone);
return [getMapKey(date), marker];
return [getMapKey(date, MAP_KEY_FORMAT.DATE), marker];
}),
);
}
Expand All @@ -281,18 +283,19 @@ const mapMarkers = (markers: IMarker[], timezone: TimeZoneConfig | undefined) =>
* All validation that is done from these props will now be in sync with provided timezone config
*/
export const mapPropDates = (opts: MapPropDatesOpts): PropDates => {
const format = getMapKeyType(opts.isMonthPicker, opts.isYearPicker);
return {
minDate: sanitizeDateToLocal(opts.minDate, opts.timezone, opts.isSpecific),
maxDate: sanitizeDateToLocal(opts.maxDate, opts.timezone, opts.isSpecific),
disabledDates: shouldMap(opts.disabledDates)
? datesArrToMap(opts.disabledDates, opts.timezone, opts.isSpecific)
? datesArrToMap(opts.disabledDates, opts.timezone, format, opts.isSpecific)
: opts.disabledDates,
allowedDates: shouldMap(opts.allowedDates)
? datesArrToMap(opts.allowedDates, opts.timezone, opts.isSpecific)
? datesArrToMap(opts.allowedDates, opts.timezone, format, opts.isSpecific)
: null,
highlight:
typeof opts.highlight === 'object' && shouldMap(opts.highlight?.dates)
? datesArrToMap(opts.highlight.dates, opts.timezone)
? datesArrToMap(opts.highlight.dates, opts.timezone, format)
: (opts.highlight as HighlightFn),
markers: mapMarkers(opts.markers, opts.timezone),
};
Expand Down
21 changes: 13 additions & 8 deletions src/VueDatePicker/utils/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ComponentPublicInstance } from 'vue';
import { unref } from 'vue';
import { format } from 'date-fns';
import { format, type Locale } from 'date-fns';

import type {
Config,
Expand All @@ -10,11 +11,9 @@ import type {
ModelValue,
OverlayGridItem,
} from '@/interfaces';
import { type Locale } from 'date-fns';
import type { ComponentPublicInstance } from 'vue';
import { getDate } from '@/utils/date-utils';
import { localToTz } from '@/utils/timezone';
import { EventKey } from '@/constants';
import { EventKey, MAP_KEY_FORMAT } from '@/constants';

export const getArrayInArray = <T>(list: T[], increment = 3): T[][] => {
const items = [];
Expand Down Expand Up @@ -280,16 +279,16 @@ export const formatNumber = (num: number, locale: string): string => {
return new Intl.NumberFormat(locale, { useGrouping: false, style: 'decimal' }).format(num);
};

export const getMapKey = (date: Date | string | number) => {
return format(date, 'dd-MM-yyyy');
export const getMapKey = (date: Date | string | number, mapKeyFormat?: MAP_KEY_FORMAT) => {
return format(date, mapKeyFormat ?? MAP_KEY_FORMAT.DATE);
};

export const shouldMap = (arr: any): arr is Date[] | string[] | boolean => {
return Array.isArray(arr);
};

export const getMapDate = <T>(date: Date, map: Map<string, T>): T | undefined => {
return map.get(getMapKey(date));
export const getMapDate = <T>(date: Date, map: Map<string, T>, format?: MAP_KEY_FORMAT): T | undefined => {
return map.get(getMapKey(date, format));
};

export const matchDate = (date: Date, mapOrFn: Map<string, any> | ((date: Date) => boolean) | null) => {
Expand Down Expand Up @@ -319,3 +318,9 @@ export const isIOS = () => {
(navigator.userAgent.includes('Mac') && 'ontouchend' in document)
);
};

export const getMapKeyType = (monthPicker: boolean, yearPicker: boolean): MAP_KEY_FORMAT => {
if (monthPicker) return MAP_KEY_FORMAT.MONTH_AND_YEAR;
if (yearPicker) return MAP_KEY_FORMAT.YEAR;
return MAP_KEY_FORMAT.DATE;
};
2 changes: 1 addition & 1 deletion tests/unit/components/MonthYearPicker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ describe('Month and Year picker components', () => {
it('Should disable months based on disabled dates', async () => {
const currentMonth = getMonth(new Date());
const wrapper = mount(MonthPicker, {
props: { ...props, disabledDates: [new Date()] },
props: { ...props, disabledDates: [new Date()], monthPicker: true },
}) as unknown as MonthPickerCmp<{ groupedMonths: (i: number) => OverlayGridItem[][] }>;

const monthValues = wrapper.vm.groupedMonths(0);
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ const getMapDatesOpts = (date: Date, timezone: TimeZoneConfig, highlightFn: (dat
markers: [],
highlight: highlightFn,
timezone,
isMonthPicker: false,
isYearPicker: false,
};
};

Expand Down

0 comments on commit 70a2575

Please sign in to comment.