Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: TS Strict aria C and D #6761

Merged
merged 11 commits into from
Oct 24, 2024
26 changes: 13 additions & 13 deletions packages/@react-aria/calendar/src/useCalendarCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export interface CalendarCellAria {
*/
export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarState | RangeCalendarState, ref: RefObject<HTMLElement | null>): CalendarCellAria {
let {date, isDisabled} = props;
let {errorMessageId, selectedDateDescription} = hookData.get(state);
let {errorMessageId, selectedDateDescription} = hookData.get(state)!;
let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/calendar');
let dateFormatter = useDateFormatter({
weekday: 'long',
Expand All @@ -89,7 +89,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
isDisabled = isDisabled || state.isCellDisabled(date);
let isUnavailable = state.isCellUnavailable(date);
let isSelectable = !isDisabled && !isUnavailable;
let isInvalid = state.isValueInvalid && (
let isInvalid = state.isValueInvalid && Boolean(
'highlightedRange' in state
? !state.anchorDate && state.highlightedRange && date.compare(state.highlightedRange.start) >= 0 && date.compare(state.highlightedRange.end) <= 0
: state.value && isSameDay(state.value, date)
Expand Down Expand Up @@ -159,7 +159,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta

let isAnchorPressed = useRef(false);
let isRangeBoundaryPressed = useRef(false);
let touchDragTimerRef = useRef(null);
let touchDragTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
devongovett marked this conversation as resolved.
Show resolved Hide resolved
let {pressProps, isPressed} = usePress({
// When dragging to select a range, we don't want dragging over the original anchor
// again to trigger onPressStart. Cancel presses immediately when the pointer exits.
Expand Down Expand Up @@ -195,7 +195,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta

let startDragging = () => {
state.setDragging(true);
touchDragTimerRef.current = null;
touchDragTimerRef.current = undefined;

state.selectDate(date);
state.setFocusedDate(date);
Expand All @@ -215,7 +215,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
isRangeBoundaryPressed.current = false;
isAnchorPressed.current = false;
clearTimeout(touchDragTimerRef.current);
touchDragTimerRef.current = null;
touchDragTimerRef.current = undefined;
},
onPress() {
// For non-range selection, always select on press up.
Expand Down Expand Up @@ -269,7 +269,7 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
}
});

let tabIndex = null;
let tabIndex: number | undefined = undefined;
if (!isDisabled) {
tabIndex = isSameDay(date, state.focusedDate) ? 0 : -1;
}
Expand Down Expand Up @@ -298,14 +298,14 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
calendar: date.calendar.identifier
});

let formattedDate = useMemo(() => cellDateFormatter.formatToParts(nativeDate).find(part => part.type === 'day').value, [cellDateFormatter, nativeDate]);
let formattedDate = useMemo(() => cellDateFormatter.formatToParts(nativeDate).find(part => part.type === 'day')!.value, [cellDateFormatter, nativeDate]);

return {
cellProps: {
role: 'gridcell',
'aria-disabled': !isSelectable || null,
'aria-selected': isSelected || null,
'aria-invalid': isInvalid || null
'aria-disabled': !isSelectable || undefined,
'aria-selected': isSelected || undefined,
'aria-invalid': isInvalid || undefined
},
buttonProps: mergeProps(pressProps, {
onFocus() {
Expand All @@ -315,11 +315,11 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
},
tabIndex,
role: 'button',
'aria-disabled': !isSelectable || null,
'aria-disabled': !isSelectable || undefined,
'aria-label': label,
'aria-invalid': isInvalid || null,
'aria-invalid': isInvalid || undefined,
'aria-describedby': [
isInvalid ? errorMessageId : null,
isInvalid ? errorMessageId : undefined,
descriptionProps['aria-describedby']
].filter(Boolean).join(' ') || undefined,
onPointerEnter(e) {
Expand Down
6 changes: 3 additions & 3 deletions packages/@react-aria/calendar/src/useCalendarGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export function useCalendarGrid(props: AriaCalendarGridProps, state: CalendarSta

let visibleRangeDescription = useVisibleRangeDescription(startDate, endDate, state.timeZone, true);

let {ariaLabel, ariaLabelledBy} = hookData.get(state);
let {ariaLabel, ariaLabelledBy} = hookData.get(state)!;
let labelProps = useLabels({
'aria-label': [ariaLabel, visibleRangeDescription].filter(Boolean).join(', '),
'aria-labelledby': ariaLabelledBy
Expand All @@ -148,8 +148,8 @@ export function useCalendarGrid(props: AriaCalendarGridProps, state: CalendarSta
return {
gridProps: mergeProps(labelProps, {
role: 'grid',
'aria-readonly': state.isReadOnly || null,
'aria-disabled': state.isDisabled || null,
'aria-readonly': state.isReadOnly || undefined,
'aria-disabled': state.isDisabled || undefined,
'aria-multiselectable': ('highlightedRange' in state) || undefined,
onKeyDown,
onFocus: () => state.setFocused(true),
Expand Down
10 changes: 5 additions & 5 deletions packages/@react-aria/calendar/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,26 @@ import {useDateFormatter, useLocalizedStringFormatter} from '@react-aria/i18n';
import {useMemo} from 'react';

interface HookData {
ariaLabel: string,
ariaLabelledBy: string,
ariaLabel?: string,
ariaLabelledBy?: string,
errorMessageId: string,
selectedDateDescription: string
}

export const hookData = new WeakMap<CalendarState | RangeCalendarState, HookData>();

export function getEraFormat(date: CalendarDate): 'short' | undefined {
export function getEraFormat(date: CalendarDate | undefined): 'short' | undefined {
return date?.calendar.identifier === 'gregory' && date.era === 'BC' ? 'short' : undefined;
}

export function useSelectedDateDescription(state: CalendarState | RangeCalendarState) {
let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/calendar');

let start: CalendarDate, end: CalendarDate;
let start: CalendarDate | undefined, end: CalendarDate | undefined;
if ('highlightedRange' in state) {
({start, end} = state.highlightedRange || {});
} else {
start = end = state.value;
start = end = state.value ?? undefined;
}

let dateFormatter = useDateFormatter({
Expand Down
10 changes: 5 additions & 5 deletions packages/@react-aria/calendar/stories/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
import {Button} from '@react-spectrum/button';
import {CalendarState, RangeCalendarState, useCalendarState} from '@react-stately/calendar';
import {createCalendar, DateDuration, getWeeksInMonth, startOfWeek} from '@internationalized/date';
import React, {useMemo, useRef} from 'react';
import React, {ReactElement, useMemo, useRef} from 'react';
import {useCalendar, useCalendarCell, useCalendarGrid} from '../src';
import {useDateFormatter, useLocale} from '@react-aria/i18n';


export function Example(props) {
let {locale} = useLocale();
const {visibleDuration} = props;

let state = useCalendarState({
...props,
locale,
Expand All @@ -35,7 +35,7 @@ export function Example(props) {
gridCount = visibleDuration.months;
}

let components = [];
let components: Array<ReactElement> = [];
for (let i = 0; i < gridCount; i++) {
components.push(<CalendarGrid key={i} state={state} visibleDuration={visibleDuration} offset={{months: i}} />);
}
Expand Down Expand Up @@ -78,11 +78,11 @@ function CalendarGrid({state, visibleDuration, offset = {}}: {state: CalendarSta
</div>
))}
</div>);

}

function Cell(props) {
let ref = useRef(undefined);
let ref = useRef<HTMLSpanElement | null>(null);
let {cellProps, buttonProps} = useCalendarCell(props, props.state, ref);

let dateFormatter = useDateFormatter({
Expand Down
24 changes: 13 additions & 11 deletions packages/@react-aria/combobox/src/useComboBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
isReadOnly,
isDisabled
} = props;
let backupBtnRef = useRef(null);
buttonRef = buttonRef ?? backupBtnRef;

let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/combobox');
let {menuTriggerProps, menuProps} = useMenuTrigger<T>(
Expand Down Expand Up @@ -136,11 +138,11 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
}

// If the focused item is a link, trigger opening it. Items that are links are not selectable.
if (state.isOpen && state.selectionManager.focusedKey != null && state.selectionManager.isLink(state.selectionManager.focusedKey)) {
if (e.key === 'Enter') {
let item = listBoxRef.current.querySelector(`[data-key="${CSS.escape(state.selectionManager.focusedKey.toString())}"]`);
if (item instanceof HTMLAnchorElement) {
let collectionItem = state.collection.getItem(state.selectionManager.focusedKey);
if (state.isOpen && listBoxRef.current && state.selectionManager.focusedKey != null && state.selectionManager.isLink(state.selectionManager.focusedKey)) {
let item = listBoxRef.current.querySelector(`[data-key="${CSS.escape(state.selectionManager.focusedKey.toString())}"]`);
if (e.key === 'Enter' && item instanceof HTMLAnchorElement) {
let collectionItem = state.collection.getItem(state.selectionManager.focusedKey);
if (collectionItem) {
router.open(item, e, collectionItem.props.href, collectionItem.props.routerOptions as RouterOptions);
}
}
Expand Down Expand Up @@ -217,14 +219,14 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
let onPress = (e: PressEvent) => {
if (e.pointerType === 'touch') {
// Focus the input field in case it isn't focused yet
inputRef.current.focus();
inputRef.current?.focus();
state.toggle(null, 'manual');
}
};

let onPressStart = (e: PressEvent) => {
if (e.pointerType !== 'touch') {
inputRef.current.focus();
inputRef.current?.focus();
state.toggle((e.pointerType === 'keyboard' || e.pointerType === 'virtual') ? 'first' : null, 'manual');
}
};
Expand All @@ -251,7 +253,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
// Sometimes VoiceOver on iOS fires two touchend events in quick succession. Ignore the second one.
if (e.timeStamp - lastEventTime.current < 500) {
e.preventDefault();
inputRef.current.focus();
inputRef.current?.focus();
return;
}

Expand All @@ -263,7 +265,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta

if (touch.clientX === centerX && touch.clientY === centerY) {
e.preventDefault();
inputRef.current.focus();
inputRef.current?.focus();
state.toggle(null, 'manual');

lastEventTime.current = e.timeStamp;
Expand All @@ -287,7 +289,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
let sectionTitle = section?.['aria-label'] || (typeof section?.rendered === 'string' ? section.rendered : '') || '';

let announcement = stringFormatter.format('focusAnnouncement', {
isGroupChange: section && sectionKey !== lastSection.current,
isGroupChange: (section && sectionKey !== lastSection.current) ?? false,
groupTitle: sectionTitle,
groupCount: section ? [...getChildNodes(section, state.collection)].length : 0,
optionText: focusedItem['aria-label'] || focusedItem.textValue || '',
Expand Down Expand Up @@ -336,7 +338,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta

useEffect(() => {
if (state.isOpen) {
return ariaHideOutside([inputRef.current, popoverRef.current]);
return ariaHideOutside([inputRef.current, popoverRef.current].filter(element => element != null));
}
}, [state.isOpen, inputRef, popoverRef]);

Expand Down
2 changes: 1 addition & 1 deletion packages/@react-aria/combobox/stories/example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ function ListBox(props) {
}

function Option({item, state}) {
let ref = React.useRef(undefined);
let ref = React.useRef<HTMLLIElement | null>(null);
let {optionProps, isSelected, isFocused, isDisabled} = useOption({key: item.key}, state, ref);

let backgroundColor;
Expand Down
6 changes: 3 additions & 3 deletions packages/@react-aria/datepicker/src/useDateField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ export interface DateFieldAria extends ValidationResult {

// Data that is passed between useDateField and useDateSegment.
interface HookData {
ariaLabel: string,
ariaLabelledBy: string,
ariaDescribedBy: string,
ariaLabel?: string,
ariaLabelledBy?: string,
ariaDescribedBy?: string,
focusManager: FocusManager
}

Expand Down
3 changes: 3 additions & 0 deletions packages/@react-aria/datepicker/src/useDatePickerGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState

// Focus the first placeholder segment from the end on mouse down/touch up in the field.
let focusLast = () => {
if (!ref.current) {
return;
}
// Try to find the segment prior to the element that was clicked on.
let target = window.event?.target as FocusableElement;
let walker = getFocusableTreeWalker(ref.current, {tabbable: true});
Expand Down
Loading