Skip to content

Commit

Permalink
fixup! feat(date-picker): replace moment with vanilla JS
Browse files Browse the repository at this point in the history
  • Loading branch information
gcornut committed Sep 8, 2023
1 parent c5c09ac commit ee6c99a
Show file tree
Hide file tree
Showing 17 changed files with 298 additions and 235 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { DatePickerProps, Emphasis, IconButton, Toolbar } from '@lumx/react';
import { mdiChevronLeft, mdiChevronRight } from '@lumx/icons';
import { Comp } from '@lumx/react/utils/type';
import { getMonthCalendar } from '@lumx/react/utils/date/getMonthCalendar';
import { listWeekDays } from '@lumx/react/utils/date/listWeekDays';
import { isSameDay } from '@lumx/react/utils/date/isSameDay';
import { formatDayNumber, formatMonthYear } from '@lumx/react/utils';
import { formatMonthYear } from '@lumx/react/utils/date/formatMonthYear';
import { formatDayNumber } from '@lumx/react/utils/date/formatDayNumber';
import { CLASSNAME } from './constants';

/**
Expand Down Expand Up @@ -47,14 +47,9 @@ export const DatePickerControlled: Comp<DatePickerControlledProps, HTMLDivElemen
todayOrSelectedDateRef,
value,
} = props;
const weeks = React.useMemo(() => getMonthCalendar(locale, selectedMonth, minDate, maxDate), [
locale,
minDate,
maxDate,
selectedMonth,
]);

const weekDays = React.useMemo(() => listWeekDays(locale), [locale]);
const { weeks, weekDays } = React.useMemo(() => {
return getMonthCalendar(locale, selectedMonth, minDate, maxDate);
}, [locale, minDate, maxDate, selectedMonth]);

return (
<div ref={ref} className={`${CLASSNAME}`}>
Expand Down Expand Up @@ -90,9 +85,9 @@ export const DatePickerControlled: Comp<DatePickerControlledProps, HTMLDivElemen
<div className={`${CLASSNAME}__month-days ${CLASSNAME}__days-wrapper`}>
{weeks.flatMap((week, weekIndex) => {
return weekDays.map((weekDay, dayIndex) => {
const { date, isInRange } = week[weekDay.number] || {};
const { date, isOutOfRange } = week[weekDay.number] || {};
const key = `${weekIndex}-${dayIndex}`;
const isToday = date && isSameDay(date, new Date());
const isToday = !isOutOfRange && date && isSameDay(date, new Date());
const isSelected = date && value && isSameDay(value, date);

return (
Expand All @@ -101,10 +96,10 @@ export const DatePickerControlled: Comp<DatePickerControlledProps, HTMLDivElemen
<button
ref={isSelected || (!value && isToday) ? todayOrSelectedDateRef : null}
className={classNames(`${CLASSNAME}__month-day`, {
[`${CLASSNAME}__month-day--is-selected`]: value && isSelected,
[`${CLASSNAME}__month-day--is-today`]: isInRange && isToday,
[`${CLASSNAME}__month-day--is-selected`]: isSelected,
[`${CLASSNAME}__month-day--is-today`]: isToday,
})}
disabled={!isInRange}
disabled={isOutOfRange}
type="button"
onClick={() => onChange(date)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DatePicker, IconButtonProps, Placement, Popover, TextField } from '@lum
import { useFocusTrap } from '@lumx/react/hooks/useFocusTrap';
import { useFocus } from '@lumx/react/hooks/useFocus';
import { Comp, GenericProps } from '@lumx/react/utils/type';
import { formatDate } from '@lumx/react/utils';
import { formatDate } from '@lumx/react/utils/date/formatDate';

/**
* Defines the props of the component.
Expand Down
1 change: 0 additions & 1 deletion packages/lumx-react/src/utils/date/constants.ts

This file was deleted.

5 changes: 0 additions & 5 deletions packages/lumx-react/src/utils/date/dayToWeekDayNumber.ts

This file was deleted.

1 change: 1 addition & 0 deletions packages/lumx-react/src/utils/date/formatDayNumber.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { zeroPad } from '@lumx/react/utils/zeroPad';

/** Format date month day number padded with `0` */
export const formatDayNumber = (date: Date, locale: string) =>
zeroPad(date.toLocaleDateString(locale, { day: 'numeric' }), 2);
27 changes: 27 additions & 0 deletions packages/lumx-react/src/utils/date/getFirstDayOfWeek.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { getFirstDayOfWeek } from './getFirstDayOfWeek';

describe(getFirstDayOfWeek.name, () => {
it('should return for a valid locales', () => {
expect(getFirstDayOfWeek('fa-ir')).toBe(6);
expect(getFirstDayOfWeek('ar-ma')).toBe(1);
expect(getFirstDayOfWeek('ar')).toBe(6);
expect(getFirstDayOfWeek('ar-eg')).toBe(0);
});

it('should ignore the character case', () => {
expect(getFirstDayOfWeek('zh-hk')).toBe(getFirstDayOfWeek('zh-HK'));
});

it('should return for the lang locale if available', () => {
// Test for a specific locale and its root locale
const localeWithRoot = 'es-ES'; // Spanish (Spain) with root locale es
const expectedFirstDay = getFirstDayOfWeek('es'); // First day for root locale 'es'

expect(getFirstDayOfWeek(localeWithRoot)).toBe(expectedFirstDay);
});

it('should return the undefined for an invalid locale', () => {
// Default first day is Monday (1)
expect(getFirstDayOfWeek('invalid')).toBe(undefined);
});
});
54 changes: 39 additions & 15 deletions packages/lumx-react/src/utils/date/getFirstDayOfWeek.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
import { getCurrentLocale } from '../getCurrentLocale';
import { getLocaleLang } from '@lumx/react/utils/locale/getLocaleLang';

const FIRST_DAY_FOR_LOCALES = [
{
// Locales with Sunday as the first day of the week
localeRX: /^(af|ar-(dz|eg|sa)|bn|cy|en-(ca|us|za)|fr-ca|gd|he|hi|ja|km|ko|pt-br|te|th|ug|zh-hk)$/i,
firstDay: 0,
},
{
// Locales with Monday as the first day of the week
localeRX: /^(ar-(ma|tn)|az|be|bg|bs|ca|cs|da|de|el|en-(au|gb|ie|in|nz)|eo|es|et|eu|fi|fr|fy|gl|gu|hr|ht|hu|hy|id|is|it|ka|kk|kn|lb|lt|lv|mk|mn|ms|mt|nb|nl|nn|oc|pl|pt|ro|ru|sk|sl|sq|sr|sv|ta|tr|uk|uz|vi|zh-(cn|tw))$/i,
firstDay: 1,
},
{
// Locales with Saturday as the first day of the week
localeRX: /^(ar|fa-ir)$/i,
firstDay: 6,
},
];

/** Find first day of week for locale in the constant */
const getFromConstant = (locale: string) => {
for (const { localeRX, firstDay } of FIRST_DAY_FOR_LOCALES) {
if (localeRX.test(locale)) return firstDay;
}
return undefined;
};

/**
* Try to get the first day of a week from the `Intl` API or fallback to Monday.
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getWeekInfo
*
* @param locale (default to the browser locale)
* @return number between 1 (Monday) and 7 (Sunday)
* Get first day of the week for the given locale code (language + region with fallback to language only if not found).
*/
export const getFirstDayOfWeek = (locale = getCurrentLocale()): number => {
// Fallback to monday
const FALLBACK = 1;
try {
const localeMetadata = new Intl.Locale(locale) as any;
const weekInfo = localeMetadata.getWeekInfo?.() || localeMetadata.weekInfo;
return weekInfo?.firstDay || FALLBACK;
} catch (e) {
return FALLBACK;
export const getFirstDayOfWeek = (locale: string): number | undefined => {
// Get first day of week for locale code
const firstDay = getFromConstant(locale);
if (firstDay !== undefined) return firstDay;

// Fallback search for locale lang (local code without the region part)
const localeLang = getLocaleLang(locale);
if (localeLang !== locale) {
return getFromConstant(localeLang);
}

return undefined;
};
10 changes: 0 additions & 10 deletions packages/lumx-react/src/utils/date/getLastDayOfWeek.ts

This file was deleted.

193 changes: 103 additions & 90 deletions packages/lumx-react/src/utils/date/getMonthCalendar.test.ts
Original file line number Diff line number Diff line change
@@ -1,106 +1,119 @@
import { getFirstDayOfWeek } from '@lumx/react/utils/date/getFirstDayOfWeek';
import { getMonthCalendar } from './getMonthCalendar';

jest.mock('@lumx/react/utils/date/getFirstDayOfWeek');

describe(getMonthCalendar.name, () => {
it('should generate calendar', () => {
// First day of week: monday
(getFirstDayOfWeek as any).mockReturnValue(1);

const referenceDate = new Date('2017-02-03');
const month = getMonthCalendar('fr', referenceDate);

expect(month).toEqual([
{
'3': { date: new Date('2017-02-01'), isInRange: true },
'4': { date: new Date('2017-02-02'), isInRange: true },
'5': { date: new Date('2017-02-03'), isInRange: true },
'6': { date: new Date('2017-02-04'), isInRange: true },
'7': { date: new Date('2017-02-05'), isInRange: true },
},
{
'1': { date: new Date('2017-02-06'), isInRange: true },
'2': { date: new Date('2017-02-07'), isInRange: true },
'3': { date: new Date('2017-02-08'), isInRange: true },
'4': { date: new Date('2017-02-09'), isInRange: true },
'5': { date: new Date('2017-02-10'), isInRange: true },
'6': { date: new Date('2017-02-11'), isInRange: true },
'7': { date: new Date('2017-02-12'), isInRange: true },
},
{
'1': { date: new Date('2017-02-13'), isInRange: true },
'2': { date: new Date('2017-02-14'), isInRange: true },
'3': { date: new Date('2017-02-15'), isInRange: true },
'4': { date: new Date('2017-02-16'), isInRange: true },
'5': { date: new Date('2017-02-17'), isInRange: true },
'6': { date: new Date('2017-02-18'), isInRange: true },
'7': { date: new Date('2017-02-19'), isInRange: true },
},
{
'1': { date: new Date('2017-02-20'), isInRange: true },
'2': { date: new Date('2017-02-21'), isInRange: true },
'3': { date: new Date('2017-02-22'), isInRange: true },
'4': { date: new Date('2017-02-23'), isInRange: true },
'5': { date: new Date('2017-02-24'), isInRange: true },
'6': { date: new Date('2017-02-25'), isInRange: true },
'7': { date: new Date('2017-02-26'), isInRange: true },
},
{
'1': { date: new Date('2017-02-27'), isInRange: true },
'2': { date: new Date('2017-02-28'), isInRange: true },
},
]);
expect(month).toEqual({
weekDays: [
{ letter: 'L', number: 1 },
{ letter: 'M', number: 2 },
{ letter: 'M', number: 3 },
{ letter: 'J', number: 4 },
{ letter: 'V', number: 5 },
{ letter: 'S', number: 6 },
{ letter: 'D', number: 0 },
],
weeks: [
{
'3': { date: new Date('2017-02-01') },
'4': { date: new Date('2017-02-02') },
'5': { date: new Date('2017-02-03') },
'6': { date: new Date('2017-02-04') },
'0': { date: new Date('2017-02-05') },
},
{
'1': { date: new Date('2017-02-06') },
'2': { date: new Date('2017-02-07') },
'3': { date: new Date('2017-02-08') },
'4': { date: new Date('2017-02-09') },
'5': { date: new Date('2017-02-10') },
'6': { date: new Date('2017-02-11') },
'0': { date: new Date('2017-02-12') },
},
{
'1': { date: new Date('2017-02-13') },
'2': { date: new Date('2017-02-14') },
'3': { date: new Date('2017-02-15') },
'4': { date: new Date('2017-02-16') },
'5': { date: new Date('2017-02-17') },
'6': { date: new Date('2017-02-18') },
'0': { date: new Date('2017-02-19') },
},
{
'1': { date: new Date('2017-02-20') },
'2': { date: new Date('2017-02-21') },
'3': { date: new Date('2017-02-22') },
'4': { date: new Date('2017-02-23') },
'5': { date: new Date('2017-02-24') },
'6': { date: new Date('2017-02-25') },
'0': { date: new Date('2017-02-26') },
},
{
'1': { date: new Date('2017-02-27') },
'2': { date: new Date('2017-02-28') },
},
],
});
});

it('should generate calendar with sunday as start of week and mark dates in range', () => {
// First day of week: sunday
(getFirstDayOfWeek as any).mockReturnValue(7);

const referenceDate = new Date('2017-02-03');
const minDate = new Date('2017-02-06');
const maxDate = new Date('2017-02-10');
const month = getMonthCalendar('fr', referenceDate, minDate, maxDate);
const month = getMonthCalendar('en-US', referenceDate, minDate, maxDate);

expect(month).toEqual([
{
'3': { date: new Date('2017-02-01'), isInRange: false },
'4': { date: new Date('2017-02-02'), isInRange: false },
'5': { date: new Date('2017-02-03'), isInRange: false },
'6': { date: new Date('2017-02-04'), isInRange: false },
},
{
'7': { date: new Date('2017-02-05'), isInRange: false },
'1': { date: new Date('2017-02-06'), isInRange: false },
'2': { date: new Date('2017-02-07'), isInRange: true },
'3': { date: new Date('2017-02-08'), isInRange: true },
'4': { date: new Date('2017-02-09'), isInRange: true },
'5': { date: new Date('2017-02-10'), isInRange: false },
'6': { date: new Date('2017-02-11'), isInRange: false },
},
{
'7': { date: new Date('2017-02-12'), isInRange: false },
'1': { date: new Date('2017-02-13'), isInRange: false },
'2': { date: new Date('2017-02-14'), isInRange: false },
'3': { date: new Date('2017-02-15'), isInRange: false },
'4': { date: new Date('2017-02-16'), isInRange: false },
'5': { date: new Date('2017-02-17'), isInRange: false },
'6': { date: new Date('2017-02-18'), isInRange: false },
},
{
'7': { date: new Date('2017-02-19'), isInRange: false },
'1': { date: new Date('2017-02-20'), isInRange: false },
'2': { date: new Date('2017-02-21'), isInRange: false },
'3': { date: new Date('2017-02-22'), isInRange: false },
'4': { date: new Date('2017-02-23'), isInRange: false },
'5': { date: new Date('2017-02-24'), isInRange: false },
'6': { date: new Date('2017-02-25'), isInRange: false },
},
{
'7': { date: new Date('2017-02-26'), isInRange: false },
'1': { date: new Date('2017-02-27'), isInRange: false },
'2': { date: new Date('2017-02-28'), isInRange: false },
},
]);
expect(month).toEqual({
weekDays: [
{ letter: 'S', number: 0 },
{ letter: 'M', number: 1 },
{ letter: 'T', number: 2 },
{ letter: 'W', number: 3 },
{ letter: 'T', number: 4 },
{ letter: 'F', number: 5 },
{ letter: 'S', number: 6 },
],
weeks: [
{
'3': { date: new Date('2017-02-01'), isOutOfRange: true },
'4': { date: new Date('2017-02-02'), isOutOfRange: true },
'5': { date: new Date('2017-02-03'), isOutOfRange: true },
'6': { date: new Date('2017-02-04'), isOutOfRange: true },
},
{
'0': { date: new Date('2017-02-05'), isOutOfRange: true },
'1': { date: new Date('2017-02-06'), isOutOfRange: true },
'2': { date: new Date('2017-02-07') },
'3': { date: new Date('2017-02-08') },
'4': { date: new Date('2017-02-09') },
'5': { date: new Date('2017-02-10'), isOutOfRange: true },
'6': { date: new Date('2017-02-11'), isOutOfRange: true },
},
{
'0': { date: new Date('2017-02-12'), isOutOfRange: true },
'1': { date: new Date('2017-02-13'), isOutOfRange: true },
'2': { date: new Date('2017-02-14'), isOutOfRange: true },
'3': { date: new Date('2017-02-15'), isOutOfRange: true },
'4': { date: new Date('2017-02-16'), isOutOfRange: true },
'5': { date: new Date('2017-02-17'), isOutOfRange: true },
'6': { date: new Date('2017-02-18'), isOutOfRange: true },
},
{
'0': { date: new Date('2017-02-19'), isOutOfRange: true },
'1': { date: new Date('2017-02-20'), isOutOfRange: true },
'2': { date: new Date('2017-02-21'), isOutOfRange: true },
'3': { date: new Date('2017-02-22'), isOutOfRange: true },
'4': { date: new Date('2017-02-23'), isOutOfRange: true },
'5': { date: new Date('2017-02-24'), isOutOfRange: true },
'6': { date: new Date('2017-02-25'), isOutOfRange: true },
},
{
'0': { date: new Date('2017-02-26'), isOutOfRange: true },
'1': { date: new Date('2017-02-27'), isOutOfRange: true },
'2': { date: new Date('2017-02-28'), isOutOfRange: true },
},
],
});
});
});
Loading

0 comments on commit ee6c99a

Please sign in to comment.