Skip to content

Commit 3615192

Browse files
committed
Add strict eslint config, fix tests and breakages
1 parent 25233c6 commit 3615192

27 files changed

+2116
-1346
lines changed

eslint.config.mjs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,22 @@ export default tseslint.config({
1111

1212
extends: [
1313
eslintJs.configs.recommended,
14-
tseslint.configs.recommended,
15-
eslintReact.configs['recommended-typescript'],
14+
tseslint.configs.strictTypeChecked,
15+
eslintReact.configs['recommended-type-checked'],
1616
eslintPluginPrettierRecommended
1717
],
1818

1919
languageOptions: {
2020
parser: tseslint.parser,
2121
parserOptions: {
22-
projectService: true,
23-
tsconfigRootDir: import.meta.dirname
22+
tsconfigRootDir: import.meta.dirname,
23+
project: 'tsconfig.eslint.json'
2424
}
2525
},
2626

2727
rules: {
28-
'@eslint-react/no-missing-key': 'warn',
28+
'@typescript-eslint/no-confusing-void-expression': 'off',
29+
"@typescript-eslint/no-unnecessary-condition": 'off',
2930
'prettier/prettier': ['error', prettierrc]
3031
}
3132
})

jest.config.mjs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @ts-check
2+
import { createDefaultPreset } from 'ts-jest'
3+
4+
/** @type {import("jest").Config} **/
5+
const config = {
6+
...createDefaultPreset({
7+
tsconfig: 'tsconfig.test.json'
8+
}),
9+
testEnvironment: 'jsdom',
10+
setupFilesAfterEnv: ['<rootDir>/test/jest.setup.js']
11+
}
12+
13+
export default config

package.json

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
"types": "./build/index.d.ts",
1616
"default": "./build/index.js"
1717
}
18-
}
18+
},
19+
"./style.css": "./build/style.css",
20+
"./style.scss": "./src/style.scss",
21+
"./build/style.css": "./build/style.css",
22+
"./src/style.scss": "./src/style.scss"
1923
},
2024
"author": "Hernán Sartorio",
2125
"license": "MIT",
@@ -29,8 +33,7 @@
2933
"homepage": "https://github.com/hernansartorio/react-nice-dates#readme",
3034
"files": [
3135
"build",
32-
"src/style.scss",
33-
"index.d.ts"
36+
"src/style.scss"
3437
],
3538
"scripts": {
3639
"start": "parcel website/index.html -d tmp",
@@ -39,7 +42,7 @@
3942
"build:website": "rm -rf build-website/* && parcel build website/index.html -d build-website --experimental-scope-hoisting --no-content-hash && cp website/CNAME build-website/CNAME",
4043
"deploy": "gh-pages -d build-website",
4144
"dev:package": "tsup src/index.ts --watch",
42-
"lint": "eslint --cache src",
45+
"lint": "eslint src test --ext .ts,.tsx",
4346
"release": "yarn run build && np",
4447
"test": "jest",
4548
"test:watch": "jest --watch"
@@ -57,15 +60,17 @@
5760
"@eslint/js": "^9.30.0",
5861
"@testing-library/dom": "^10.4.0",
5962
"@testing-library/jest-dom": "^6.6.3",
60-
"@testing-library/react": "^16.0.1",
63+
"@testing-library/react": "^16.3.0",
64+
"@types/jest": "^30.0.0",
6165
"@types/react": "^18",
6266
"@types/react-dom": "^18",
6367
"autoprefixer": "^9.7.3",
6468
"date-fns": "^2.9.0",
6569
"eslint": "^9.30.0",
6670
"eslint-config-prettier": "^10.1.5",
6771
"eslint-plugin-prettier": "^5.5.1",
68-
"jest": "^24.9.0",
72+
"jest": "^30.0.3",
73+
"jest-environment-jsdom": "^30.0.2",
6974
"parcel-bundler": "^1.12.4",
7075
"parcel-plugin-prerender": "^1.4.1",
7176
"postcss-cli": "^7.1.0",
@@ -75,6 +80,7 @@
7580
"react-dom": "^18",
7681
"react-test-renderer": "^18.3.1",
7782
"sass": "^1.25.0",
83+
"ts-jest": "^29.4.0",
7884
"tsup": "^8.5.0",
7985
"typescript": "^5.8.3",
8086
"typescript-eslint": "^8.35.0"

src/CalendarDay.tsx

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { MouseEvent, TouchEvent } from 'react'
2-
import { getDate, format, isToday } from 'date-fns'
2+
import { getDate, format, isToday, Locale } from 'date-fns'
33
import classNames from 'classnames'
44

55
const defaultModifiersClassNames = {
@@ -16,7 +16,7 @@ const defaultModifiersClassNames = {
1616
export interface CalendarDayProps {
1717
date: Date
1818
height: number
19-
locale: object
19+
locale: Locale
2020
modifiers?: Record<string, boolean>
2121
modifiersClassNames?: Record<string, string>
2222
onClick?: (date: Date) => void
@@ -36,7 +36,6 @@ export function CalendarDay({
3636
onHover = defaultEventHandler
3737
}: CalendarDayProps): React.JSX.Element {
3838
const dayOfMonth = getDate(date)
39-
const dayClassNames: Record<string, boolean> = {}
4039
const modifiers: Record<string, boolean> = {
4140
today: isToday(date),
4241
...receivedModifiers
@@ -46,22 +45,20 @@ export function CalendarDay({
4645
...receivedModifiersClassNames
4746
}
4847

49-
Object.keys(modifiers).forEach(name => {
50-
dayClassNames[modifiersClassNames[name]] = modifiers[name]
51-
})
48+
const dayClassNames = Object.fromEntries(
49+
Object.entries(modifiers).map(([name, modifier]) => [
50+
modifiersClassNames[name] as string,
51+
modifier
52+
])
53+
)
5254

5355
const handleClick = (event: MouseEvent | TouchEvent) => {
5456
onClick(date)
5557
event.preventDefault()
5658
}
5759

58-
const handleMouseEnter = () => {
59-
onHover(date)
60-
}
61-
62-
const handleMouseLeave = () => {
63-
onHover(null)
64-
}
60+
const handleMouseEnter = () => onHover(date)
61+
const handleMouseLeave = () => onHover(null)
6562

6663
return (
6764
<span

src/CalendarGrid.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react'
2-
import { eachDayOfInterval, isSameMonth, lightFormat, startOfMonth } from 'date-fns'
2+
import { eachDayOfInterval, isSameMonth, lightFormat, startOfMonth, Locale } from 'date-fns'
33
import classNames from 'classnames'
44
import { useGrid } from './useGrid'
55
import { ORIGIN_BOTTOM, ORIGIN_TOP } from './constants'
@@ -93,8 +93,8 @@ export function CalendarGrid({
9393
})}
9494
ref={containerElementRef}
9595
style={{
96-
transform: `translate3d(0, ${offset}px, 0)`,
97-
transitionDuration: `${transitionDuration}ms`
96+
transform: `translate3d(0, ${String(offset)}px, 0)`,
97+
transitionDuration: `${String(transitionDuration)}ms`
9898
}}
9999
>
100100
{days}

src/DateRangePickerCalendar.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,38 @@ export function DateRangePickerCalendar({
5151
const displayedStartDate =
5252
focus === START_DATE && !startDate && endDate && hoveredDate && !isSameDay(hoveredDate, endDate)
5353
? hoveredDate
54-
: startOfDay(new Date())
54+
: startDate
55+
? startOfDay(startDate)
56+
: null
5557

5658
const displayedEndDate =
5759
focus === END_DATE && !endDate && startDate && hoveredDate && !isSameDay(hoveredDate, startDate)
5860
? hoveredDate
59-
: startOfDay(new Date())
61+
: endDate
62+
? startOfDay(endDate)
63+
: null
6064

6165
const isStartDate = (date: Date) =>
62-
isSameDay(date, displayedStartDate) && isBefore(date, displayedEndDate)
66+
Boolean(
67+
displayedStartDate &&
68+
displayedEndDate &&
69+
isSameDay(date, displayedStartDate) &&
70+
isBefore(date, displayedEndDate)
71+
)
6372
const isMiddleDate = (date: Date) =>
64-
isAfter(date, displayedStartDate) && isBefore(date, displayedEndDate)
73+
Boolean(
74+
displayedStartDate &&
75+
displayedEndDate &&
76+
isAfter(date, displayedStartDate) &&
77+
isBefore(date, displayedEndDate)
78+
)
6579
const isEndDate = (date: Date) =>
66-
isSameDay(date, displayedEndDate) && isAfter(date, displayedStartDate)
80+
Boolean(
81+
displayedStartDate &&
82+
displayedEndDate &&
83+
isSameDay(date, displayedEndDate) &&
84+
isAfter(date, displayedStartDate)
85+
)
6786

6887
const modifiers = mergeModifiers(
6988
{

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ export interface InputProps {
1515
export type DefaultModifiers = 'disabled' | 'selected' | 'today'
1616
export type ModifierMatcher = (date: Date) => boolean
1717

18+
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
1819
export type Modifiers = { [key in DefaultModifiers | string]: ModifierMatcher }
20+
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
1921
export type ModifiersClassNames = { [key in DefaultModifiers | string]: string }
2022

2123
export interface CommonProps {

src/useDateInput.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ export function useDateInput({
3030
onDateChange,
3131
validate
3232
}: UseDateInputProps): UseDateInputReturn {
33-
const formatString = receivedFormatString || locale.formatLong?.date({ width: 'short' })
33+
const formatString =
34+
receivedFormatString || (locale.formatLong?.date({ width: 'short' }) as string)
3435

3536
const formatDate = (date: Date) => format(date, formatString, { locale })
3637
const parseDate = (dateString: string) =>

src/useGrid.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,16 +201,22 @@ export function useGrid<TRef extends HTMLElement = HTMLElement>({
201201
})
202202
}
203203

204-
containerElement.style.transform = `translate3d(0, ${
204+
if (!event.touches[0]) {
205+
return
206+
}
207+
containerElement.style.transform = `translate3d(0, ${String(
205208
computedOffset || -currentMonthPosition
206-
}px, 0)`
209+
)}px, 0)`
207210
containerElement.classList.remove('-transition')
208211
containerElement.classList.add('-moving')
209212
initialDragPositionRef.current =
210213
event.touches[0].clientY + (-computedOffset || currentMonthPosition)
211214
}
212215

213216
const handleDrag = (event: TouchEvent) => {
217+
if (!event.touches[0]) {
218+
return
219+
}
214220
const initialDragPosition = initialDragPositionRef.current
215221
const dragOffset = event.touches[0].clientY - initialDragPosition
216222
const previousMonth = subMonths(currentMonth, 1)
@@ -250,14 +256,14 @@ export function useGrid<TRef extends HTMLElement = HTMLElement>({
250256
onMonthChange(previousMonth)
251257
}
252258

253-
containerElement.style.transform = `translate3d(0, ${dragOffset}px, 0)`
259+
containerElement.style.transform = `translate3d(0, ${String(dragOffset)}px, 0)`
254260
event.preventDefault()
255261
}
256262

257263
const handleDragEnd = (event: TouchEvent) => {
258264
const currentMonthPosition =
259265
(rowsBetweenDates(startDate, currentMonth, locale) - 1) * cellHeight
260-
containerElement.style.transform = `translate3d(0, ${-currentMonthPosition}px, 0)`
266+
containerElement.style.transform = `translate3d(0, ${String(-currentMonthPosition)}px, 0)`
261267
containerElement.classList.add('-transition')
262268
containerElement.classList.remove('-moving')
263269

@@ -269,6 +275,7 @@ export function useGrid<TRef extends HTMLElement = HTMLElement>({
269275
}, transitionDuration)
270276

271277
if (
278+
event.changedTouches[0] &&
272279
Math.abs(
273280
initialDragPositionRef.current - currentMonthPosition - event.changedTouches[0].clientY
274281
) > 10

src/useOutsideClickHandler.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ export function useOutsideClickHandler<
1010
const refC = useRef<TRefC>(null)
1111
const callbackRef = useRef(callback)
1212

13+
useEffect(() => {
14+
callbackRef.current = callback
15+
}, [callback])
16+
1317
useEffect(() => {
1418
const handleOutsideClick = (event: MouseEvent) => {
1519
const target = event.target as Node | null
1620
if (
17-
(!refA.current || (refA.current.contains && !refA.current.contains(target))) &&
18-
(!refB.current || (refB.current.contains && !refB.current.contains(target))) &&
19-
(!refC.current || (refC.current.contains && !refC.current.contains(target)))
21+
(!refA.current || ('contains' in refA.current && !refA.current.contains(target))) &&
22+
(!refB.current || ('contains' in refB.current && !refB.current.contains(target))) &&
23+
(!refC.current || ('contains' in refC.current && !refC.current.contains(target)))
2024
) {
2125
callbackRef.current()
2226
}

0 commit comments

Comments
 (0)