Skip to content

Commit 02f242f

Browse files
TS Strict Calendar (#6076)
* TS Strict Calendar Co-authored-by: Ishank Sharma <[email protected]>
1 parent 88c7f4e commit 02f242f

File tree

13 files changed

+105
-85
lines changed

13 files changed

+105
-85
lines changed

packages/@internationalized/date/src/queries.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ export function getWeeksInMonth(date: DateValue, locale: string): number {
238238
}
239239

240240
/** Returns the lesser of the two provider dates. */
241-
export function minDate<A extends DateValue, B extends DateValue>(a: A, b: B): A | B {
241+
export function minDate<A extends DateValue, B extends DateValue>(a?: A | null, b?: B | null): A | B | null | undefined {
242242
if (a && b) {
243243
return a.compare(b) <= 0 ? a : b;
244244
}
@@ -247,7 +247,7 @@ export function minDate<A extends DateValue, B extends DateValue>(a: A, b: B): A
247247
}
248248

249249
/** Returns the greater of the two provider dates. */
250-
export function maxDate<A extends DateValue, B extends DateValue>(a: A, b: B): A | B {
250+
export function maxDate<A extends DateValue, B extends DateValue>(a?: A | null, b?: B | null): A | B | null | undefined {
251251
if (a && b) {
252252
return a.compare(b) >= 0 ? a : b;
253253
}

packages/@react-aria/textfield/src/useTextField.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export interface AriaTextFieldOptions<T extends TextFieldIntrinsicElements> exte
8181
*/
8282
inputElementType?: T,
8383
/**
84-
* Controls whether inputted text is automatically capitalized and, if so, in what manner.
84+
* Controls whether inputted text is automatically capitalized and, if so, in what manner.
8585
* See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize).
8686
*/
8787
autoCapitalize?: 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters'
@@ -166,7 +166,7 @@ export function useTextField<T extends TextFieldIntrinsicElements = DefaultEleme
166166
labelProps,
167167
inputProps: mergeProps(
168168
domProps,
169-
inputElementType === 'input' && inputOnlyProps,
169+
inputElementType === 'input' ? inputOnlyProps : undefined,
170170
{
171171
disabled: isDisabled,
172172
readOnly: isReadOnly,

packages/@react-spectrum/calendar/src/Calendar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function Calendar<T extends DateValue>(props: SpectrumCalendarProps<T>, ref: Foc
3434
createCalendar
3535
});
3636

37-
let domRef = useRef();
37+
let domRef = useRef(null);
3838
useImperativeHandle(ref, () => ({
3939
...createDOMRef(domRef),
4040
focus() {

packages/@react-spectrum/calendar/src/CalendarBase.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {DOMProps, StyleProps} from '@react-types/shared';
2222
import {HelpText} from '@react-spectrum/label';
2323
// @ts-ignore
2424
import intlMessages from '../intl/*.json';
25-
import React, {HTMLAttributes, RefObject} from 'react';
25+
import React, {HTMLAttributes, JSX, RefObject} from 'react';
2626
import styles from '@adobe/spectrum-css-temp/components/calendar/vars.css';
2727
import {useDateFormatter, useLocale, useLocalizedStringFormatter} from '@react-aria/i18n';
2828
import {VisuallyHidden} from '@react-aria/visually-hidden';
@@ -59,8 +59,8 @@ export function CalendarBase<T extends CalendarState | RangeCalendarState>(props
5959
timeZone: state.timeZone
6060
});
6161

62-
let titles = [];
63-
let calendars = [];
62+
let titles: JSX.Element[] = [];
63+
let calendars: JSX.Element[] = [];
6464
for (let i = 0; i < visibleMonths; i++) {
6565
let d = currentMonth.add({months: i});
6666
titles.push(

packages/@react-spectrum/calendar/src/CalendarCell.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ interface CalendarCellProps extends AriaCalendarCellProps {
2727
}
2828

2929
export function CalendarCell({state, currentMonth, ...props}: CalendarCellProps) {
30-
let ref = useRef<HTMLElement>();
30+
let ref = useRef<HTMLElement>(null);
3131
let {
3232
cellProps,
3333
buttonProps,

packages/@react-spectrum/calendar/src/RangeCalendar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function RangeCalendar<T extends DateValue>(props: SpectrumRangeCalendarProps<T>
3434
createCalendar
3535
});
3636

37-
let domRef = useRef();
37+
let domRef = useRef(null);
3838
useImperativeHandle(ref, () => ({
3939
...createDOMRef(domRef),
4040
focus() {

packages/@react-spectrum/calendar/stories/Calendar.stories.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,16 @@ function Example(props) {
202202
let [calendar, setCalendar] = React.useState<Key>(calendars[0].key);
203203
let {locale: defaultLocale} = useLocale();
204204

205-
let pref = preferences.find(p => p.locale === locale);
205+
let pref = preferences.find(p => p.locale === locale)!;
206206
let preferredCalendars = React.useMemo(() => pref ? pref.ordering.split(' ').map(p => calendars.find(c => c.key === p)).filter(Boolean) : [calendars[0]], [pref]);
207-
let otherCalendars = React.useMemo(() => calendars.filter(c => !preferredCalendars.some(p => p.key === c.key)), [preferredCalendars]);
207+
let otherCalendars = React.useMemo(() => calendars.filter(c => !preferredCalendars.some(p => p!.key === c.key)), [preferredCalendars]);
208208

209209
let updateLocale = locale => {
210210
setLocale(locale);
211211
let pref = preferences.find(p => p.locale === locale);
212-
setCalendar(pref.ordering.split(' ')[0]);
212+
if (pref) {
213+
setCalendar(pref.ordering.split(' ')[0]);
214+
}
213215
};
214216

215217
return (
@@ -220,14 +222,14 @@ function Example(props) {
220222
</Picker>
221223
<Picker label="Calendar" selectedKey={calendar} onSelectionChange={setCalendar}>
222224
<Section title="Preferred" items={preferredCalendars}>
223-
{item => <Item>{item.name}</Item>}
225+
{item => <Item>{item!.name}</Item>}
224226
</Section>
225227
<Section title="Other" items={otherCalendars}>
226228
{item => <Item>{item.name}</Item>}
227229
</Section>
228230
</Picker>
229231
</Flex>
230-
<Provider locale={(locale || defaultLocale) + (calendar && calendar !== preferredCalendars[0].key ? '-u-ca-' + calendar : '')}>
232+
<Provider locale={(locale || defaultLocale) + (calendar && calendar !== preferredCalendars![0]!.key ? '-u-ca-' + calendar : '')}>
231233
<View maxWidth="100vw" padding="size-10" overflow="auto">
232234
<Calendar {...props} />
233235
</View>

packages/@react-stately/calendar/src/types.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ interface CalendarStateBase {
2222
/** The date range that is currently visible in the calendar. */
2323
readonly visibleRange: RangeValue<CalendarDate>,
2424
/** The minimum allowed date that a user may select. */
25-
readonly minValue?: DateValue,
25+
readonly minValue?: DateValue | null,
2626
/** The maximum allowed date that a user may select. */
27-
readonly maxValue?: DateValue,
27+
readonly maxValue?: DateValue | null,
2828
/** The time zone of the dates currently being displayed. */
2929
readonly timeZone: string,
3030
/**
3131
* The current validation state of the selected value.
3232
* @deprecated Use `isValueInvalid` instead.
3333
*/
34-
readonly validationState: ValidationState,
34+
readonly validationState: ValidationState | null,
3535
/** Whether the calendar is invalid. */
3636
readonly isValueInvalid: boolean,
3737
/** The currently focused date. */
@@ -108,17 +108,17 @@ export interface CalendarState extends CalendarStateBase {
108108

109109
export interface RangeCalendarState extends CalendarStateBase {
110110
/** The currently selected date range. */
111-
readonly value: RangeValue<DateValue>,
111+
readonly value: RangeValue<DateValue> | null,
112112
/** Sets the currently selected date range. */
113-
setValue(value: RangeValue<DateValue>): void,
113+
setValue(value: RangeValue<DateValue> | null): void,
114114
/** Highlights the given date during selection, e.g. by hovering or dragging. */
115115
highlightDate(date: CalendarDate): void,
116116
/** The current anchor date that the user clicked on to begin range selection. */
117117
readonly anchorDate: CalendarDate | null,
118118
/** Sets the anchor date that the user clicked on to begin range selection. */
119119
setAnchorDate(date: CalendarDate | null): void,
120120
/** The currently highlighted date range. */
121-
readonly highlightedRange: RangeValue<CalendarDate>,
121+
readonly highlightedRange: RangeValue<CalendarDate> | null,
122122
/** Whether the user is currently dragging over the calendar. */
123123
readonly isDragging: boolean,
124124
/** Sets whether the user is dragging over the calendar. */

packages/@react-stately/calendar/src/useCalendarState.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
toCalendarDate,
2828
today
2929
} from '@internationalized/date';
30-
import {CalendarProps, DateValue} from '@react-types/calendar';
30+
import {CalendarProps, DateValue, MappedDateValue} from '@react-types/calendar';
3131
import {CalendarState} from './types';
3232
import {useControlledState} from '@react-stately/utils';
3333
import {useMemo, useState} from 'react';
@@ -51,7 +51,6 @@ export interface CalendarStateOptions<T extends DateValue = DateValue> extends C
5151
/** Determines how to align the initial selection relative to the visible date range. */
5252
selectionAlignment?: 'start' | 'center' | 'end'
5353
}
54-
5554
/**
5655
* Provides state management for a calendar component.
5756
* A calendar displays one or more date grids and allows users to select a single date.
@@ -71,7 +70,7 @@ export function useCalendarState<T extends DateValue = DateValue>(props: Calenda
7170
} = props;
7271
let calendar = useMemo(() => createCalendar(resolvedOptions.calendar), [createCalendar, resolvedOptions.calendar]);
7372

74-
let [value, setControlledValue] = useControlledState<DateValue>(props.value, props.defaultValue, props.onChange);
73+
let [value, setControlledValue] = useControlledState<DateValue | null, MappedDateValue<T>>(props.value!, props.defaultValue ?? null!, props.onChange);
7574
let calendarDateValue = useMemo(() => value ? toCalendar(toCalendarDate(value), calendar) : null, [value, calendar]);
7675
let timeZone = useMemo(() => value && 'timeZone' in value ? value.timeZone : resolvedOptions.timeZone, [value, resolvedOptions.timeZone]);
7776
let focusedCalendarDate = useMemo(() => (
@@ -138,25 +137,26 @@ export function useCalendarState<T extends DateValue = DateValue>(props: Calenda
138137

139138
function setValue(newValue: CalendarDate | null) {
140139
if (!props.isDisabled && !props.isReadOnly) {
141-
if (newValue === null) {
140+
let localValue = newValue;
141+
if (localValue === null) {
142142
setControlledValue(null);
143143
return;
144144
}
145-
newValue = constrainValue(newValue, minValue, maxValue);
146-
newValue = previousAvailableDate(newValue, startDate, isDateUnavailable);
147-
if (!newValue) {
145+
localValue = constrainValue(localValue, minValue, maxValue);
146+
localValue = previousAvailableDate(localValue, startDate, isDateUnavailable);
147+
if (!localValue) {
148148
return;
149149
}
150150

151151
// The display calendar should not have any effect on the emitted value.
152152
// Emit dates in the same calendar as the original value, if any, otherwise gregorian.
153-
newValue = toCalendar(newValue, value?.calendar || new GregorianCalendar());
153+
localValue = toCalendar(localValue, value?.calendar || new GregorianCalendar());
154154

155155
// Preserve time if the input value had one.
156156
if (value && 'hour' in value) {
157-
setControlledValue(value.set(newValue));
157+
setControlledValue(value.set(localValue));
158158
} else {
159-
setControlledValue(newValue);
159+
setControlledValue(localValue);
160160
}
161161
}
162162
}
@@ -173,7 +173,7 @@ export function useCalendarState<T extends DateValue = DateValue>(props: Calenda
173173
return isInvalid(calendarDateValue, minValue, maxValue);
174174
}, [calendarDateValue, isDateUnavailable, minValue, maxValue]);
175175
let isValueInvalid = props.isInvalid || props.validationState === 'invalid' || isUnavailable;
176-
let validationState: ValidationState = isValueInvalid ? 'invalid' : null;
176+
let validationState: ValidationState | null = isValueInvalid ? 'invalid' : null;
177177

178178
let pageDuration = useMemo(() => {
179179
if (pageBehavior === 'visible') {
@@ -184,8 +184,8 @@ export function useCalendarState<T extends DateValue = DateValue>(props: Calenda
184184
}, [pageBehavior, visibleDuration]);
185185

186186
return {
187-
isDisabled: props.isDisabled,
188-
isReadOnly: props.isReadOnly,
187+
isDisabled: props.isDisabled ?? false,
188+
isReadOnly: props.isReadOnly ?? false,
189189
value: calendarDateValue,
190190
setValue,
191191
visibleRange: {
@@ -308,25 +308,25 @@ export function useCalendarState<T extends DateValue = DateValue>(props: Calenda
308308
return isFocused && focusedDate && isSameDay(date, focusedDate);
309309
},
310310
isCellDisabled(date) {
311-
return props.isDisabled || date.compare(startDate) < 0 || date.compare(endDate) > 0 || this.isInvalid(date, minValue, maxValue);
311+
return props.isDisabled || date.compare(startDate) < 0 || date.compare(endDate) > 0 || this.isInvalid(date);
312312
},
313313
isCellUnavailable(date) {
314-
return props.isDateUnavailable && props.isDateUnavailable(date);
314+
return props.isDateUnavailable ? props.isDateUnavailable(date) : false;
315315
},
316316
isPreviousVisibleRangeInvalid() {
317317
let prev = startDate.subtract({days: 1});
318-
return isSameDay(prev, startDate) || this.isInvalid(prev, minValue, maxValue);
318+
return isSameDay(prev, startDate) || this.isInvalid(prev);
319319
},
320320
isNextVisibleRangeInvalid() {
321321
// Adding may return the same date if we reached the end of time
322322
// according to the calendar system (e.g. 9999-12-31).
323323
let next = endDate.add({days: 1});
324-
return isSameDay(next, endDate) || this.isInvalid(next, minValue, maxValue);
324+
return isSameDay(next, endDate) || this.isInvalid(next);
325325
},
326326
getDatesInWeek(weekIndex, from = startDate) {
327327
// let date = startOfWeek(from, locale);
328328
let date = from.add({weeks: weekIndex});
329-
let dates = [];
329+
let dates: (CalendarDate | null)[] = [];
330330

331331
date = startOfWeek(date, locale);
332332

0 commit comments

Comments
 (0)