Skip to content

Commit 54d260d

Browse files
authored
Incremental TS Strict stately - utils (#3926)
* Incremental TS Strict stately - utils
1 parent 53503ef commit 54d260d

File tree

6 files changed

+23
-12
lines changed

6 files changed

+23
-12
lines changed

packages/@react-stately/selection/src/useMultipleSelectionState.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export function useMultipleSelectionState(props: MultipleSelectionStateProps): M
119119
};
120120
}
121121

122-
function convertSelection(selection: 'all' | Iterable<Key>, defaultValue?: Selection): 'all' | Selection {
122+
function convertSelection(selection: 'all' | Iterable<Key>, defaultValue?: Selection): 'all' | Set<Key> {
123123
if (!selection) {
124124
return defaultValue;
125125
}

packages/@react-stately/utils/src/number.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ export function clamp(value: number, min: number = -Infinity, max: number = Infi
1818
return newValue;
1919
}
2020

21-
export function snapValueToStep(value: number, min: number, max: number, step: number): number {
21+
export function snapValueToStep(value: number, min: number | undefined, max: number | undefined, step: number): number {
22+
min = Number(min);
23+
max = Number(max);
2224
let remainder = ((value - (isNaN(min) ? 0 : min)) % step);
2325
let snappedValue = Math.abs(remainder) * 2 >= step
2426
? value + Math.sign(remainder) * (step - Math.abs(remainder))

packages/@react-stately/utils/src/useControlledState.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,9 @@
1212

1313
import {useCallback, useEffect, useRef, useState} from 'react';
1414

15-
export function useControlledState<T>(
16-
value: T,
17-
defaultValue: T,
18-
onChange: (value: T, ...args: any[]) => void
19-
): [T, (value: T, ...args: any[]) => void] {
15+
export function useControlledState<T, C = T>(value: T, defaultValue: T | undefined, onChange?: (v: C, ...args: any[]) => void): [T, (value: T) => void];
16+
export function useControlledState<T, C = T>(value: T | undefined, defaultValue: T, onChange?: (v: C, ...args: any[]) => void): [T, (value: T) => void];
17+
export function useControlledState<T, C = T>(value: T, defaultValue: T, onChange?: (v: C, ...args: any[]) => void): [T, (value: T) => void] {
2018
let [stateValue, setStateValue] = useState(value || defaultValue);
2119

2220
let isControlledRef = useRef(value !== undefined);

packages/@react-stately/utils/test/useControlledState.test.js renamed to packages/@react-stately/utils/test/useControlledState.test.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('useControlledState tests', function () {
2323

2424
it('can handle default setValue behavior, wont invoke onChange for the same value twice in a row', () => {
2525
let onChangeSpy = jest.fn();
26-
let {result} = renderHook(() => useControlledState(undefined, 'defaultValue', onChangeSpy));
26+
let {result} = renderHook(() => useControlledState<string>(undefined, 'defaultValue', onChangeSpy));
2727
let [value, setValue] = result.current;
2828
expect(value).toBe('defaultValue');
2929
expect(onChangeSpy).not.toHaveBeenCalled();
@@ -54,7 +54,7 @@ describe('useControlledState tests', function () {
5454

5555
it('using NaN will only trigger onChange once', () => {
5656
let onChangeSpy = jest.fn();
57-
let {result} = renderHook(() => useControlledState(undefined, undefined, onChangeSpy));
57+
let {result} = renderHook(() => useControlledState<number | undefined>(undefined, undefined, onChangeSpy));
5858
let [value, setValue] = result.current;
5959
expect(value).not.toBeDefined();
6060
expect(onChangeSpy).not.toHaveBeenCalled();
@@ -70,13 +70,15 @@ describe('useControlledState tests', function () {
7070
expect(onChangeSpy).toHaveBeenCalledTimes(1);
7171
});
7272

73+
// @deprecated - ignore TS
7374
it('can handle callback setValue behavior', () => {
7475
let onChangeSpy = jest.fn();
7576
let consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
76-
let {result} = renderHook(() => useControlledState(undefined, 'defaultValue', onChangeSpy));
77+
let {result} = renderHook(() => useControlledState<string>(undefined, 'defaultValue', onChangeSpy));
7778
let [value, setValue] = result.current;
7879
expect(value).toBe('defaultValue');
7980
expect(onChangeSpy).not.toHaveBeenCalled();
81+
// @ts-ignore
8082
act(() => setValue((prevValue) => {
8183
expect(prevValue).toBe('defaultValue');
8284
return 'newValue';
@@ -116,7 +118,7 @@ describe('useControlledState tests', function () {
116118

117119
it('can handle controlled setValue behavior', () => {
118120
let onChangeSpy = jest.fn();
119-
let {result} = renderHook(() => useControlledState('controlledValue', 'defaultValue', onChangeSpy));
121+
let {result} = renderHook(() => useControlledState<string>('controlledValue', 'defaultValue', onChangeSpy));
120122
let [value, setValue] = result.current;
121123
expect(value).toBe('controlledValue');
122124
expect(onChangeSpy).not.toHaveBeenCalled();
@@ -134,6 +136,7 @@ describe('useControlledState tests', function () {
134136
expect(onChangeSpy).not.toHaveBeenCalled();
135137
});
136138

139+
// @deprecated - ignore TS
137140
it('can handle controlled callback setValue behavior', () => {
138141
let onChangeSpy = jest.fn();
139142
let consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
@@ -142,6 +145,7 @@ describe('useControlledState tests', function () {
142145
expect(value).toBe('controlledValue');
143146
expect(onChangeSpy).not.toHaveBeenCalled();
144147

148+
// @ts-ignore
145149
act(() => setValue((prevValue) => {
146150
expect(prevValue).toBe('controlledValue');
147151
return 'newValue';
@@ -152,6 +156,7 @@ describe('useControlledState tests', function () {
152156

153157
onChangeSpy.mockClear();
154158

159+
// @ts-ignore
155160
act(() => setValue((prevValue) => {
156161
expect(prevValue).toBe('controlledValue');
157162
return 'controlledValue';
@@ -162,6 +167,7 @@ describe('useControlledState tests', function () {
162167
expect(consoleWarnSpy).toHaveBeenLastCalledWith('We can not support a function callback. See Github Issues for details https://github.com/adobe/react-spectrum/issues/2320');
163168
});
164169

170+
// @deprecated - ignore TS
165171
it('can handle controlled callback setValue behavior after prop change', () => {
166172
let onChangeSpy = jest.fn();
167173
let propValue = 'controlledValue';
@@ -175,6 +181,7 @@ describe('useControlledState tests', function () {
175181
rerender();
176182
[value, setValue] = result.current;
177183

184+
// @ts-ignore
178185
act(() => setValue((prevValue) => {
179186
expect(prevValue).toBe('updated');
180187
return 'newValue';
@@ -185,6 +192,7 @@ describe('useControlledState tests', function () {
185192

186193
onChangeSpy.mockClear();
187194

195+
// @ts-ignore
188196
act(() => setValue((prevValue) => {
189197
expect(prevValue).toBe('updated');
190198
return 'updated';
@@ -213,6 +221,7 @@ describe('useControlledState tests', function () {
213221
let [value] = result.current;
214222
expect(value).toBe('controlledValue');
215223
expect(onChangeSpy).not.toHaveBeenCalled();
224+
// @ts-ignore this is an invalid case anyways
216225
rerender({value: undefined, defaultValue: 'defaultValue', onChange: onChangeSpy});
217226
expect(consoleWarnSpy).toHaveBeenLastCalledWith('WARN: A component changed from controlled to uncontrolled.');
218227
});
@@ -233,6 +242,7 @@ describe('useControlledState tests', function () {
233242
let [value] = result.current;
234243
expect(value).toBe('defaultValue');
235244
expect(onChangeSpy).not.toHaveBeenCalled();
245+
// @ts-ignore this is an invalid case anyways
236246
rerender({value: 'controlledValue', defaultValue: 'defaultValue', onChange: onChangeSpy});
237247
expect(consoleWarnSpy).toHaveBeenLastCalledWith('WARN: A component changed from uncontrolled to controlled.');
238248
});
@@ -323,7 +333,7 @@ describe('useControlledState tests', function () {
323333
let resolve;
324334
const AsyncChild = React.lazy(() => new Promise((r) => {resolve = r;}));
325335
function Test(props) {
326-
let [value, setValue] = useControlledState(undefined, 1, props.onChange);
336+
let [value, setValue] = useControlledState<number>(undefined, 1, props.onChange);
327337
let [showChild, setShowChild] = useState(false);
328338
let [isPending, startTransition] = React.useTransition();
329339

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"./packages/@react-spectrum/text",
5050
"./packages/@react-spectrum/view",
5151
"./packages/@react-spectrum/well",
52+
"./packages/@react-stately/utils",
5253
"./packages/@react-types/a",
5354
"./packages/@react-types/b",
5455
"./packages/@react-types/checkbox",

0 commit comments

Comments
 (0)