Skip to content

Commit ee6c99a

Browse files
committed
fixup! feat(date-picker): replace moment with vanilla JS
1 parent c5c09ac commit ee6c99a

17 files changed

+298
-235
lines changed

packages/lumx-react/src/components/date-picker/DatePickerControlled.tsx

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { DatePickerProps, Emphasis, IconButton, Toolbar } from '@lumx/react';
44
import { mdiChevronLeft, mdiChevronRight } from '@lumx/icons';
55
import { Comp } from '@lumx/react/utils/type';
66
import { getMonthCalendar } from '@lumx/react/utils/date/getMonthCalendar';
7-
import { listWeekDays } from '@lumx/react/utils/date/listWeekDays';
87
import { isSameDay } from '@lumx/react/utils/date/isSameDay';
9-
import { formatDayNumber, formatMonthYear } from '@lumx/react/utils';
8+
import { formatMonthYear } from '@lumx/react/utils/date/formatMonthYear';
9+
import { formatDayNumber } from '@lumx/react/utils/date/formatDayNumber';
1010
import { CLASSNAME } from './constants';
1111

1212
/**
@@ -47,14 +47,9 @@ export const DatePickerControlled: Comp<DatePickerControlledProps, HTMLDivElemen
4747
todayOrSelectedDateRef,
4848
value,
4949
} = props;
50-
const weeks = React.useMemo(() => getMonthCalendar(locale, selectedMonth, minDate, maxDate), [
51-
locale,
52-
minDate,
53-
maxDate,
54-
selectedMonth,
55-
]);
56-
57-
const weekDays = React.useMemo(() => listWeekDays(locale), [locale]);
50+
const { weeks, weekDays } = React.useMemo(() => {
51+
return getMonthCalendar(locale, selectedMonth, minDate, maxDate);
52+
}, [locale, minDate, maxDate, selectedMonth]);
5853

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

9893
return (
@@ -101,10 +96,10 @@ export const DatePickerControlled: Comp<DatePickerControlledProps, HTMLDivElemen
10196
<button
10297
ref={isSelected || (!value && isToday) ? todayOrSelectedDateRef : null}
10398
className={classNames(`${CLASSNAME}__month-day`, {
104-
[`${CLASSNAME}__month-day--is-selected`]: value && isSelected,
105-
[`${CLASSNAME}__month-day--is-today`]: isInRange && isToday,
99+
[`${CLASSNAME}__month-day--is-selected`]: isSelected,
100+
[`${CLASSNAME}__month-day--is-today`]: isToday,
106101
})}
107-
disabled={!isInRange}
102+
disabled={isOutOfRange}
108103
type="button"
109104
onClick={() => onChange(date)}
110105
>

packages/lumx-react/src/components/date-picker/DatePickerField.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { DatePicker, IconButtonProps, Placement, Popover, TextField } from '@lum
44
import { useFocusTrap } from '@lumx/react/hooks/useFocusTrap';
55
import { useFocus } from '@lumx/react/hooks/useFocus';
66
import { Comp, GenericProps } from '@lumx/react/utils/type';
7-
import { formatDate } from '@lumx/react/utils';
7+
import { formatDate } from '@lumx/react/utils/date/formatDate';
88

99
/**
1010
* Defines the props of the component.

packages/lumx-react/src/utils/date/constants.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

packages/lumx-react/src/utils/date/dayToWeekDayNumber.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { zeroPad } from '@lumx/react/utils/zeroPad';
22

3+
/** Format date month day number padded with `0` */
34
export const formatDayNumber = (date: Date, locale: string) =>
45
zeroPad(date.toLocaleDateString(locale, { day: 'numeric' }), 2);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { getFirstDayOfWeek } from './getFirstDayOfWeek';
2+
3+
describe(getFirstDayOfWeek.name, () => {
4+
it('should return for a valid locales', () => {
5+
expect(getFirstDayOfWeek('fa-ir')).toBe(6);
6+
expect(getFirstDayOfWeek('ar-ma')).toBe(1);
7+
expect(getFirstDayOfWeek('ar')).toBe(6);
8+
expect(getFirstDayOfWeek('ar-eg')).toBe(0);
9+
});
10+
11+
it('should ignore the character case', () => {
12+
expect(getFirstDayOfWeek('zh-hk')).toBe(getFirstDayOfWeek('zh-HK'));
13+
});
14+
15+
it('should return for the lang locale if available', () => {
16+
// Test for a specific locale and its root locale
17+
const localeWithRoot = 'es-ES'; // Spanish (Spain) with root locale es
18+
const expectedFirstDay = getFirstDayOfWeek('es'); // First day for root locale 'es'
19+
20+
expect(getFirstDayOfWeek(localeWithRoot)).toBe(expectedFirstDay);
21+
});
22+
23+
it('should return the undefined for an invalid locale', () => {
24+
// Default first day is Monday (1)
25+
expect(getFirstDayOfWeek('invalid')).toBe(undefined);
26+
});
27+
});
Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,44 @@
1-
import { getCurrentLocale } from '../getCurrentLocale';
1+
import { getLocaleLang } from '@lumx/react/utils/locale/getLocaleLang';
2+
3+
const FIRST_DAY_FOR_LOCALES = [
4+
{
5+
// Locales with Sunday as the first day of the week
6+
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,
7+
firstDay: 0,
8+
},
9+
{
10+
// Locales with Monday as the first day of the week
11+
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,
12+
firstDay: 1,
13+
},
14+
{
15+
// Locales with Saturday as the first day of the week
16+
localeRX: /^(ar|fa-ir)$/i,
17+
firstDay: 6,
18+
},
19+
];
20+
21+
/** Find first day of week for locale in the constant */
22+
const getFromConstant = (locale: string) => {
23+
for (const { localeRX, firstDay } of FIRST_DAY_FOR_LOCALES) {
24+
if (localeRX.test(locale)) return firstDay;
25+
}
26+
return undefined;
27+
};
228

329
/**
4-
* Try to get the first day of a week from the `Intl` API or fallback to Monday.
5-
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getWeekInfo
6-
*
7-
* @param locale (default to the browser locale)
8-
* @return number between 1 (Monday) and 7 (Sunday)
30+
* Get first day of the week for the given locale code (language + region with fallback to language only if not found).
931
*/
10-
export const getFirstDayOfWeek = (locale = getCurrentLocale()): number => {
11-
// Fallback to monday
12-
const FALLBACK = 1;
13-
try {
14-
const localeMetadata = new Intl.Locale(locale) as any;
15-
const weekInfo = localeMetadata.getWeekInfo?.() || localeMetadata.weekInfo;
16-
return weekInfo?.firstDay || FALLBACK;
17-
} catch (e) {
18-
return FALLBACK;
32+
export const getFirstDayOfWeek = (locale: string): number | undefined => {
33+
// Get first day of week for locale code
34+
const firstDay = getFromConstant(locale);
35+
if (firstDay !== undefined) return firstDay;
36+
37+
// Fallback search for locale lang (local code without the region part)
38+
const localeLang = getLocaleLang(locale);
39+
if (localeLang !== locale) {
40+
return getFromConstant(localeLang);
1941
}
42+
43+
return undefined;
2044
};

packages/lumx-react/src/utils/date/getLastDayOfWeek.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 103 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,119 @@
1-
import { getFirstDayOfWeek } from '@lumx/react/utils/date/getFirstDayOfWeek';
21
import { getMonthCalendar } from './getMonthCalendar';
32

4-
jest.mock('@lumx/react/utils/date/getFirstDayOfWeek');
5-
63
describe(getMonthCalendar.name, () => {
74
it('should generate calendar', () => {
8-
// First day of week: monday
9-
(getFirstDayOfWeek as any).mockReturnValue(1);
10-
115
const referenceDate = new Date('2017-02-03');
126
const month = getMonthCalendar('fr', referenceDate);
137

14-
expect(month).toEqual([
15-
{
16-
'3': { date: new Date('2017-02-01'), isInRange: true },
17-
'4': { date: new Date('2017-02-02'), isInRange: true },
18-
'5': { date: new Date('2017-02-03'), isInRange: true },
19-
'6': { date: new Date('2017-02-04'), isInRange: true },
20-
'7': { date: new Date('2017-02-05'), isInRange: true },
21-
},
22-
{
23-
'1': { date: new Date('2017-02-06'), isInRange: true },
24-
'2': { date: new Date('2017-02-07'), isInRange: true },
25-
'3': { date: new Date('2017-02-08'), isInRange: true },
26-
'4': { date: new Date('2017-02-09'), isInRange: true },
27-
'5': { date: new Date('2017-02-10'), isInRange: true },
28-
'6': { date: new Date('2017-02-11'), isInRange: true },
29-
'7': { date: new Date('2017-02-12'), isInRange: true },
30-
},
31-
{
32-
'1': { date: new Date('2017-02-13'), isInRange: true },
33-
'2': { date: new Date('2017-02-14'), isInRange: true },
34-
'3': { date: new Date('2017-02-15'), isInRange: true },
35-
'4': { date: new Date('2017-02-16'), isInRange: true },
36-
'5': { date: new Date('2017-02-17'), isInRange: true },
37-
'6': { date: new Date('2017-02-18'), isInRange: true },
38-
'7': { date: new Date('2017-02-19'), isInRange: true },
39-
},
40-
{
41-
'1': { date: new Date('2017-02-20'), isInRange: true },
42-
'2': { date: new Date('2017-02-21'), isInRange: true },
43-
'3': { date: new Date('2017-02-22'), isInRange: true },
44-
'4': { date: new Date('2017-02-23'), isInRange: true },
45-
'5': { date: new Date('2017-02-24'), isInRange: true },
46-
'6': { date: new Date('2017-02-25'), isInRange: true },
47-
'7': { date: new Date('2017-02-26'), isInRange: true },
48-
},
49-
{
50-
'1': { date: new Date('2017-02-27'), isInRange: true },
51-
'2': { date: new Date('2017-02-28'), isInRange: true },
52-
},
53-
]);
8+
expect(month).toEqual({
9+
weekDays: [
10+
{ letter: 'L', number: 1 },
11+
{ letter: 'M', number: 2 },
12+
{ letter: 'M', number: 3 },
13+
{ letter: 'J', number: 4 },
14+
{ letter: 'V', number: 5 },
15+
{ letter: 'S', number: 6 },
16+
{ letter: 'D', number: 0 },
17+
],
18+
weeks: [
19+
{
20+
'3': { date: new Date('2017-02-01') },
21+
'4': { date: new Date('2017-02-02') },
22+
'5': { date: new Date('2017-02-03') },
23+
'6': { date: new Date('2017-02-04') },
24+
'0': { date: new Date('2017-02-05') },
25+
},
26+
{
27+
'1': { date: new Date('2017-02-06') },
28+
'2': { date: new Date('2017-02-07') },
29+
'3': { date: new Date('2017-02-08') },
30+
'4': { date: new Date('2017-02-09') },
31+
'5': { date: new Date('2017-02-10') },
32+
'6': { date: new Date('2017-02-11') },
33+
'0': { date: new Date('2017-02-12') },
34+
},
35+
{
36+
'1': { date: new Date('2017-02-13') },
37+
'2': { date: new Date('2017-02-14') },
38+
'3': { date: new Date('2017-02-15') },
39+
'4': { date: new Date('2017-02-16') },
40+
'5': { date: new Date('2017-02-17') },
41+
'6': { date: new Date('2017-02-18') },
42+
'0': { date: new Date('2017-02-19') },
43+
},
44+
{
45+
'1': { date: new Date('2017-02-20') },
46+
'2': { date: new Date('2017-02-21') },
47+
'3': { date: new Date('2017-02-22') },
48+
'4': { date: new Date('2017-02-23') },
49+
'5': { date: new Date('2017-02-24') },
50+
'6': { date: new Date('2017-02-25') },
51+
'0': { date: new Date('2017-02-26') },
52+
},
53+
{
54+
'1': { date: new Date('2017-02-27') },
55+
'2': { date: new Date('2017-02-28') },
56+
},
57+
],
58+
});
5459
});
5560

5661
it('should generate calendar with sunday as start of week and mark dates in range', () => {
57-
// First day of week: sunday
58-
(getFirstDayOfWeek as any).mockReturnValue(7);
59-
6062
const referenceDate = new Date('2017-02-03');
6163
const minDate = new Date('2017-02-06');
6264
const maxDate = new Date('2017-02-10');
63-
const month = getMonthCalendar('fr', referenceDate, minDate, maxDate);
65+
const month = getMonthCalendar('en-US', referenceDate, minDate, maxDate);
6466

65-
expect(month).toEqual([
66-
{
67-
'3': { date: new Date('2017-02-01'), isInRange: false },
68-
'4': { date: new Date('2017-02-02'), isInRange: false },
69-
'5': { date: new Date('2017-02-03'), isInRange: false },
70-
'6': { date: new Date('2017-02-04'), isInRange: false },
71-
},
72-
{
73-
'7': { date: new Date('2017-02-05'), isInRange: false },
74-
'1': { date: new Date('2017-02-06'), isInRange: false },
75-
'2': { date: new Date('2017-02-07'), isInRange: true },
76-
'3': { date: new Date('2017-02-08'), isInRange: true },
77-
'4': { date: new Date('2017-02-09'), isInRange: true },
78-
'5': { date: new Date('2017-02-10'), isInRange: false },
79-
'6': { date: new Date('2017-02-11'), isInRange: false },
80-
},
81-
{
82-
'7': { date: new Date('2017-02-12'), isInRange: false },
83-
'1': { date: new Date('2017-02-13'), isInRange: false },
84-
'2': { date: new Date('2017-02-14'), isInRange: false },
85-
'3': { date: new Date('2017-02-15'), isInRange: false },
86-
'4': { date: new Date('2017-02-16'), isInRange: false },
87-
'5': { date: new Date('2017-02-17'), isInRange: false },
88-
'6': { date: new Date('2017-02-18'), isInRange: false },
89-
},
90-
{
91-
'7': { date: new Date('2017-02-19'), isInRange: false },
92-
'1': { date: new Date('2017-02-20'), isInRange: false },
93-
'2': { date: new Date('2017-02-21'), isInRange: false },
94-
'3': { date: new Date('2017-02-22'), isInRange: false },
95-
'4': { date: new Date('2017-02-23'), isInRange: false },
96-
'5': { date: new Date('2017-02-24'), isInRange: false },
97-
'6': { date: new Date('2017-02-25'), isInRange: false },
98-
},
99-
{
100-
'7': { date: new Date('2017-02-26'), isInRange: false },
101-
'1': { date: new Date('2017-02-27'), isInRange: false },
102-
'2': { date: new Date('2017-02-28'), isInRange: false },
103-
},
104-
]);
67+
expect(month).toEqual({
68+
weekDays: [
69+
{ letter: 'S', number: 0 },
70+
{ letter: 'M', number: 1 },
71+
{ letter: 'T', number: 2 },
72+
{ letter: 'W', number: 3 },
73+
{ letter: 'T', number: 4 },
74+
{ letter: 'F', number: 5 },
75+
{ letter: 'S', number: 6 },
76+
],
77+
weeks: [
78+
{
79+
'3': { date: new Date('2017-02-01'), isOutOfRange: true },
80+
'4': { date: new Date('2017-02-02'), isOutOfRange: true },
81+
'5': { date: new Date('2017-02-03'), isOutOfRange: true },
82+
'6': { date: new Date('2017-02-04'), isOutOfRange: true },
83+
},
84+
{
85+
'0': { date: new Date('2017-02-05'), isOutOfRange: true },
86+
'1': { date: new Date('2017-02-06'), isOutOfRange: true },
87+
'2': { date: new Date('2017-02-07') },
88+
'3': { date: new Date('2017-02-08') },
89+
'4': { date: new Date('2017-02-09') },
90+
'5': { date: new Date('2017-02-10'), isOutOfRange: true },
91+
'6': { date: new Date('2017-02-11'), isOutOfRange: true },
92+
},
93+
{
94+
'0': { date: new Date('2017-02-12'), isOutOfRange: true },
95+
'1': { date: new Date('2017-02-13'), isOutOfRange: true },
96+
'2': { date: new Date('2017-02-14'), isOutOfRange: true },
97+
'3': { date: new Date('2017-02-15'), isOutOfRange: true },
98+
'4': { date: new Date('2017-02-16'), isOutOfRange: true },
99+
'5': { date: new Date('2017-02-17'), isOutOfRange: true },
100+
'6': { date: new Date('2017-02-18'), isOutOfRange: true },
101+
},
102+
{
103+
'0': { date: new Date('2017-02-19'), isOutOfRange: true },
104+
'1': { date: new Date('2017-02-20'), isOutOfRange: true },
105+
'2': { date: new Date('2017-02-21'), isOutOfRange: true },
106+
'3': { date: new Date('2017-02-22'), isOutOfRange: true },
107+
'4': { date: new Date('2017-02-23'), isOutOfRange: true },
108+
'5': { date: new Date('2017-02-24'), isOutOfRange: true },
109+
'6': { date: new Date('2017-02-25'), isOutOfRange: true },
110+
},
111+
{
112+
'0': { date: new Date('2017-02-26'), isOutOfRange: true },
113+
'1': { date: new Date('2017-02-27'), isOutOfRange: true },
114+
'2': { date: new Date('2017-02-28'), isOutOfRange: true },
115+
},
116+
],
117+
});
105118
});
106119
});

0 commit comments

Comments
 (0)