Skip to content

Commit 0620d6d

Browse files
authored
TS strict random assortment (#5560)
* TS strict smattering
1 parent 476595b commit 0620d6d

File tree

9 files changed

+59
-46
lines changed

9 files changed

+59
-46
lines changed

packages/@react-aria/color/test/useColorField.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ describe('useColorField', function () {
4242
expect(inputProps['aria-valuetext']).toBeNull();
4343
expect(inputProps['aria-valuemin']).toBeNull();
4444
expect(inputProps['aria-valuemax']).toBeNull();
45-
expect(inputProps['aria-required']).toBeNull();
46-
expect(inputProps['aria-disabled']).toBeNull();
47-
expect(inputProps['aria-readonly']).toBeNull();
45+
expect(inputProps['aria-required']).toBeUndefined();
46+
expect(inputProps['aria-disabled']).toBeUndefined();
47+
expect(inputProps['aria-readonly']).toBeUndefined();
4848
expect(inputProps['aria-invalid']).toBeUndefined();
4949
expect(inputProps.disabled).toBe(false);
5050
expect(inputProps.readOnly).toBe(false);

packages/@react-aria/numberfield/test/useNumberField.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('useNumberField hook', () => {
2727
expect(inputProps.disabled).toBeFalsy();
2828
expect(inputProps.readOnly).toBeFalsy();
2929
expect(inputProps['aria-invalid']).toBeUndefined();
30-
expect(inputProps['aria-required']).toBeNull();
30+
expect(inputProps['aria-required']).toBeUndefined();
3131
expect(inputProps['aria-valuenow']).toBeNull();
3232
expect(inputProps['aria-valuetext']).toBeNull();
3333
expect(inputProps['aria-valuemin']).toBeNull();

packages/@react-aria/spinbutton/src/useSpinButton.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -73,41 +73,41 @@ export function useSpinButton(
7373
case 'PageUp':
7474
if (onIncrementPage) {
7575
e.preventDefault();
76-
onIncrementPage();
76+
onIncrementPage?.();
7777
break;
7878
}
7979
// fallthrough!
8080
case 'ArrowUp':
8181
case 'Up':
8282
if (onIncrement) {
8383
e.preventDefault();
84-
onIncrement();
84+
onIncrement?.();
8585
}
8686
break;
8787
case 'PageDown':
8888
if (onDecrementPage) {
8989
e.preventDefault();
90-
onDecrementPage();
90+
onDecrementPage?.();
9191
break;
9292
}
9393
// fallthrough
9494
case 'ArrowDown':
9595
case 'Down':
9696
if (onDecrement) {
9797
e.preventDefault();
98-
onDecrement();
98+
onDecrement?.();
9999
}
100100
break;
101101
case 'Home':
102102
if (onDecrementToMin) {
103103
e.preventDefault();
104-
onDecrementToMin();
104+
onDecrementToMin?.();
105105
}
106106
break;
107107
case 'End':
108108
if (onIncrementToMax) {
109109
e.preventDefault();
110-
onIncrementToMax();
110+
onIncrementToMax?.();
111111
}
112112
break;
113113
}
@@ -126,23 +126,23 @@ export function useSpinButton(
126126
// This ensures that macOS VoiceOver announces it as "minus" even with other characters between the minus sign
127127
// and the number (e.g. currency symbol). Otherwise it announces nothing because it assumes the character is a hyphen.
128128
// In addition, replace the empty string with the word "Empty" so that iOS VoiceOver does not read "50%" for an empty field.
129-
textValue = textValue === '' ? stringFormatter.format('Empty') : (textValue || `${value}`).replace('-', '\u2212');
129+
let ariaTextValue = textValue === '' ? stringFormatter.format('Empty') : (textValue || `${value}`).replace('-', '\u2212');
130130

131131
useEffect(() => {
132132
if (isFocused.current) {
133133
clearAnnouncer('assertive');
134-
announce(textValue, 'assertive');
134+
announce(ariaTextValue, 'assertive');
135135
}
136-
}, [textValue]);
136+
}, [ariaTextValue]);
137137

138138
const onIncrementPressStart = useEffectEvent(
139139
(initialStepDelay: number) => {
140140
clearAsync();
141-
onIncrement();
141+
onIncrement?.();
142142
// Start spinning after initial delay
143143
_async.current = window.setTimeout(
144144
() => {
145-
if (isNaN(maxValue) || isNaN(value) || value < maxValue) {
145+
if ((maxValue === undefined || isNaN(maxValue)) || (value === undefined || isNaN(value)) || value < maxValue) {
146146
onIncrementPressStart(60);
147147
}
148148
},
@@ -154,11 +154,11 @@ export function useSpinButton(
154154
const onDecrementPressStart = useEffectEvent(
155155
(initialStepDelay: number) => {
156156
clearAsync();
157-
onDecrement();
157+
onDecrement?.();
158158
// Start spinning after initial delay
159159
_async.current = window.setTimeout(
160160
() => {
161-
if (isNaN(minValue) || isNaN(value) || value > minValue) {
161+
if ((minValue === undefined || isNaN(minValue)) || (value === undefined || isNaN(value)) || value > minValue) {
162162
onDecrementPressStart(60);
163163
}
164164
},
@@ -176,13 +176,13 @@ export function useSpinButton(
176176
return {
177177
spinButtonProps: {
178178
role: 'spinbutton',
179-
'aria-valuenow': !isNaN(value) ? value : null,
180-
'aria-valuetext': textValue,
179+
'aria-valuenow': value !== undefined && !isNaN(value) ? value : undefined,
180+
'aria-valuetext': ariaTextValue,
181181
'aria-valuemin': minValue,
182182
'aria-valuemax': maxValue,
183-
'aria-disabled': isDisabled || null,
184-
'aria-readonly': isReadOnly || null,
185-
'aria-required': isRequired || null,
183+
'aria-disabled': isDisabled || undefined,
184+
'aria-readonly': isReadOnly || undefined,
185+
'aria-required': isRequired || undefined,
186186
onKeyDown,
187187
onFocus,
188188
onBlur

packages/@react-aria/steplist/src/useStepListItem.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function useStepListItem<T>(props: AriaStepListItemProps, state: StepList
5151
event.stopPropagation();
5252
}
5353

54-
itemProps.onKeyDown(event);
54+
itemProps.onKeyDown?.(event);
5555
};
5656

5757
return {

packages/@react-aria/tag/src/useTag.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,22 +58,23 @@ export function useTag<T>(props: AriaTagProps<T>, state: ListState<T>, ref: RefO
5858

5959
// We want the group to handle keyboard navigation between tags.
6060
delete rowProps.onKeyDownCapture;
61-
delete states.descriptionProps;
61+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
62+
let {descriptionProps: _, ...stateWithoutDescription} = states;
6263

6364
let onKeyDown = (e: KeyboardEvent) => {
6465
if (e.key === 'Delete' || e.key === 'Backspace') {
6566
e.preventDefault();
6667
if (state.selectionManager.isSelected(item.key)) {
67-
onRemove(new Set(state.selectionManager.selectedKeys));
68+
onRemove?.(new Set(state.selectionManager.selectedKeys));
6869
} else {
69-
onRemove(new Set([item.key]));
70+
onRemove?.(new Set([item.key]));
7071
}
7172
}
7273
};
7374

74-
let modality: string = useInteractionModality();
75+
let modality = useInteractionModality();
7576
if (modality === 'virtual' && (typeof window !== 'undefined' && 'ontouchstart' in window)) {
76-
modality = 'touch';
77+
modality = 'pointer';
7778
}
7879
let description = onRemove && (modality === 'keyboard' || modality === 'virtual') ? stringFormatter.format('removeDescription') : '';
7980
let descProps = useDescription(description);
@@ -99,7 +100,7 @@ export function useTag<T>(props: AriaTagProps<T>, state: ListState<T>, ref: RefO
99100
'aria-errormessage': props['aria-errormessage'],
100101
'aria-label': props['aria-label']
101102
}),
102-
...states,
103+
...stateWithoutDescription,
103104
allowsRemoving: !!onRemove
104105
};
105106
}

packages/@react-aria/tag/src/useTagGroup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export function useTagGroup<T>(props: AriaTagGroupOptions<T>, state: ListState<T
9191
// If the last tag is removed, focus the container.
9292
let prevCount = useRef(state.collection.size);
9393
useEffect(() => {
94-
if (prevCount.current > 0 && state.collection.size === 0 && isFocusWithin) {
94+
if (ref.current && prevCount.current > 0 && state.collection.size === 0 && isFocusWithin) {
9595
ref.current.focus();
9696
}
9797
prevCount.current = state.collection.size;

packages/@react-spectrum/inlinealert/src/InlineAlert.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ function InlineAlert(props: SpectrumInlineAlertProps, ref: DOMRef<HTMLDivElement
6666
};
6767

6868
let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/inlinealert');
69-
let Icon = null;
70-
let iconAlt: string;
69+
let Icon: typeof React.Component | null = null;
70+
let iconAlt: string = '';
7171
if (variant in ICONS) {
7272
Icon = ICONS[variant];
7373
iconAlt = stringFormatter.format(variant);

packages/@react-stately/steplist/src/useStepListState.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export interface StepListProps<T> extends CollectionBase<T>, SingleSelection {
2121
/** The key of the initially last completed step (uncontrolled). */
2222
defaultLastCompletedStep?: Key,
2323
/** Callback for when the last completed step changes. */
24-
onLastCompletedStepChange?: (key: Key) => void,
24+
onLastCompletedStepChange?: (key: Key | null) => void,
2525
/** Whether the step list is disabled. Steps will not be focusable or interactive. */
2626
isDisabled?: boolean,
2727
/** Whether the step list is read only. Steps will be focusable but non-interactive. */
@@ -38,25 +38,29 @@ export interface StepListState<T> extends SingleSelectListState<T> {
3838
export function useStepListState<T extends object>(props: StepListProps<T>): StepListState<T> {
3939
let state = useSingleSelectListState<T>(props);
4040

41-
let [lastCompletedStep, setLastCompletedStep] = useControlledState<Key>(props.lastCompletedStep, props.defaultLastCompletedStep, props.onLastCompletedStepChange);
41+
let [lastCompletedStep, setLastCompletedStep] = useControlledState<Key | null>(props.lastCompletedStep, props.defaultLastCompletedStep ?? null, props.onLastCompletedStepChange);
4242
const {setSelectedKey: realSetSelectedKey, selectedKey, collection} = state;
4343
const {indexMap, keysLinkedList} = useMemo(() => buildKeysMaps(collection), [collection]);
4444
const selectedIdx = indexMap.get(selectedKey);
4545

46-
const isCompleted = useCallback((step: Key) => {
46+
const isCompleted = useCallback((step: Key | null | undefined) => {
4747
if (step === undefined) {
4848
return false;
4949
}
5050

51-
return indexMap.get(step) <= indexMap.get(lastCompletedStep);
51+
return (step !== null &&
52+
lastCompletedStep !== null &&
53+
indexMap.has(step) &&
54+
indexMap.has(lastCompletedStep) &&
55+
indexMap.get(step)! <= indexMap.get(lastCompletedStep)!);
5256
}, [indexMap, lastCompletedStep]);
5357

5458
const findDefaultSelectedKey = useCallback((collection: Collection<Node<T>> | null, disabledKeys: Set<Key>) => {
55-
let selectedKey = null;
56-
if (collection) {
59+
let selectedKey: Key | null = null;
60+
if (collection && collection.size > 0) {
5761
selectedKey = collection.getFirstKey();
5862
// loop over keys until we find one that isn't completed or disabled and select that
59-
while (selectedKey !== collection.getLastKey() && (disabledKeys.has(selectedKey) || isCompleted(selectedKey))) {
63+
while (selectedKey !== collection.getLastKey() && selectedKey && (disabledKeys.has(selectedKey) || isCompleted(selectedKey))) {
6064
selectedKey = collection.getKeyAfter(selectedKey);
6165
}
6266
}
@@ -66,18 +70,21 @@ export function useStepListState<T extends object>(props: StepListProps<T>): Ste
6670

6771
useEffect(() => {
6872
// Ensure a step is always selected (in case no selected key was specified or if selected item was deleted from collection)
69-
let selectedKey = state.selectedKey;
73+
let selectedKey: Key | null = state.selectedKey;
7074
if (state.selectionManager.isEmpty || !state.collection.getItem(selectedKey)) {
7175
selectedKey = findDefaultSelectedKey(state.collection, state.disabledKeys);
72-
state.selectionManager.replaceSelection(selectedKey);
76+
if (selectedKey !== null) {
77+
state.selectionManager.replaceSelection(selectedKey);
78+
}
7379
}
7480

7581
if (state.selectionManager.focusedKey == null) {
7682
state.selectionManager.setFocusedKey(selectedKey);
7783
}
7884

79-
if (selectedIdx > 0 && selectedIdx > (indexMap.get(lastCompletedStep) ?? -1) + 1) {
80-
setLastCompletedStep(keysLinkedList.get(selectedKey));
85+
let lcs = (lastCompletedStep !== null ? indexMap.get(lastCompletedStep) : -1) ?? -1;
86+
if (selectedIdx !== undefined && selectedIdx > 0 && selectedIdx > lcs + 1 && selectedKey !== null && keysLinkedList.has(selectedKey)) {
87+
setLastCompletedStep(keysLinkedList.get(selectedKey)!);
8188
}
8289
});
8390

@@ -111,11 +118,11 @@ export function useStepListState<T extends object>(props: StepListProps<T>): Ste
111118
};
112119
}
113120

114-
function buildKeysMaps<T>(coll: Collection<Node<T>>): { indexMap: Map<Key, number>, keysLinkedList: Map<Key, Key> } {
121+
function buildKeysMaps<T>(coll: Collection<Node<T>>) {
115122
const indexMap = new Map<Key, number>();
116-
const keysLinkedList = new Map<Key, Key>();
123+
const keysLinkedList = new Map<Key, Key | undefined>();
117124
let i = 0;
118-
let prev: Node<T> = undefined;
125+
let prev: Node<T> | undefined = undefined;
119126
for (const item of coll) {
120127
indexMap.set(item.key, i);
121128
keysLinkedList.set(item.key, prev?.key);

tsconfig.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,12 @@
5656
"./packages/@react-aria/searchfield",
5757
"./packages/@react-aria/separator",
5858
"./packages/@react-aria/sidenav",
59+
"./packages/@react-aria/spinbutton",
5960
"./packages/@react-aria/ssr",
61+
"./packages/@react-aria/steplist",
6062
"./packages/@react-aria/switch",
6163
"./packages/@react-aria/tabs",
64+
"./packages/@react-aria/tag",
6265
"./packages/@react-aria/toggle",
6366
"./packages/@react-aria/visually-hidden",
6467
"./packages/@react-aria/w",
@@ -76,6 +79,7 @@
7679
"./packages/@react-spectrum/form",
7780
"./packages/@react-spectrum/icon",
7881
"./packages/@react-spectrum/illustratedmessage",
82+
"./packages/@react-spectrum/inlinealert",
7983
"./packages/@react-spectrum/image",
8084
"./packages/@react-spectrum/label",
8185
"./packages/@react-spectrum/link",
@@ -102,6 +106,7 @@
102106
"./packages/@react-stately/overlays",
103107
"./packages/@react-stately/pagination",
104108
"./packages/@react-stately/searchfield",
109+
"./packages/@react-stately/steplist",
105110
"./packages/@react-stately/radio",
106111
"./packages/@react-stately/toggle",
107112
"./packages/@react-stately/utils",

0 commit comments

Comments
 (0)