@boxcustodia/library 2.0.0-alpha.22 → 2.0.0-alpha.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/calendar/calendar.cjs.js +1 -1
- package/dist/components/calendar/calendar.es.js +43 -44
- package/dist/components/date-picker/date-input.cjs.js +1 -1
- package/dist/components/date-picker/date-input.es.js +160 -140
- package/dist/components/pagination/pagination.cjs.js +1 -1
- package/dist/components/pagination/pagination.es.js +37 -35
- package/dist/components/popover/popover.cjs.js +1 -1
- package/dist/components/popover/popover.es.js +1 -1
- package/dist/components/scroll-area/scroll-area.cjs.js +1 -1
- package/dist/components/scroll-area/scroll-area.es.js +4 -4
- package/dist/components/select/select.cjs.js +1 -1
- package/dist/components/select/select.es.js +94 -90
- package/dist/components/tag/tag.cjs.js +1 -1
- package/dist/components/tag/tag.es.js +37 -18
- package/dist/hooks/use-action/use-action.cjs.js +1 -0
- package/dist/hooks/use-action/use-action.es.js +41 -0
- package/dist/hooks/use-pagination/use-pagination.cjs.js +1 -1
- package/dist/hooks/use-pagination/use-pagination.es.js +77 -32
- package/dist/hooks/use-range-pagination/use-range-pagination.cjs.js +1 -1
- package/dist/hooks/use-range-pagination/use-range-pagination.es.js +8 -5
- package/dist/hooks/use-selection/use-selection.cjs.js +1 -1
- package/dist/hooks/use-selection/use-selection.es.js +95 -33
- package/dist/hooks/use-session-storage/use-session-storage.cjs.js +1 -0
- package/dist/hooks/use-session-storage/use-session-storage.es.js +57 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +61 -63
- package/dist/src/components/select/select.d.ts +9 -2
- package/dist/src/components/tag/tag.d.ts +2 -1
- package/dist/src/hooks/index.d.ts +2 -3
- package/dist/src/hooks/internal/index.d.ts +1 -0
- package/dist/src/hooks/internal/serializer.d.ts +4 -0
- package/dist/src/hooks/use-action/index.d.ts +1 -0
- package/dist/src/hooks/use-action/use-action.d.ts +22 -0
- package/dist/src/hooks/use-local-storage/use-local-storage.d.ts +2 -4
- package/dist/src/hooks/use-pagination/use-pagination.d.ts +47 -32
- package/dist/src/hooks/use-range-pagination/use-range-pagination.d.ts +16 -10
- package/dist/src/hooks/use-selection/use-selection.d.ts +39 -45
- package/dist/src/hooks/use-session-storage/index.d.ts +1 -0
- package/dist/src/hooks/use-session-storage/use-session-storage.d.ts +11 -0
- package/package.json +1 -1
- package/src/components/calendar/calendar.tsx +10 -8
- package/src/components/combobox/combobox.stories.tsx +16 -0
- package/src/components/date-picker/date-input.tsx +23 -2
- package/src/components/form/form.tsx +3 -2
- package/src/components/pagination/pagination.tsx +5 -3
- package/src/components/popover/popover.tsx +1 -1
- package/src/components/scroll-area/scroll-area.tsx +2 -2
- package/src/components/select/select.tsx +14 -3
- package/src/components/tag/tag.stories.tsx +47 -2
- package/src/components/tag/tag.tsx +28 -6
- package/src/hooks/index.ts +2 -3
- package/src/hooks/internal/index.ts +1 -0
- package/src/hooks/internal/serializer.ts +4 -0
- package/src/hooks/use-action/index.ts +1 -0
- package/src/hooks/{use-mutation/use-mutation.stories.tsx → use-action/use-action.stories.tsx} +34 -34
- package/src/hooks/{use-mutation/use-mutation.test.ts → use-action/use-action.test.ts} +53 -53
- package/src/hooks/{use-mutation/use-mutation.ts → use-action/use-action.ts} +20 -20
- package/src/hooks/use-click-outside/use-click-outside.stories.tsx +0 -1
- package/src/hooks/use-clipboard/use-clipboard.stories.tsx +0 -1
- package/src/hooks/use-document-title/use-document-title.stories.tsx +0 -1
- package/src/hooks/use-is-visible/use-is-visible.test.tsx +1 -1
- package/src/hooks/use-local-storage/use-local-storage.stories.tsx +0 -1
- package/src/hooks/use-local-storage/use-local-storage.ts +2 -5
- package/src/hooks/use-media-query/use-media-query.stories.tsx +0 -1
- package/src/hooks/use-pagination/use-pagination.stories.tsx +720 -57
- package/src/hooks/use-pagination/use-pagination.test.tsx +560 -48
- package/src/hooks/use-pagination/use-pagination.ts +266 -0
- package/src/hooks/use-prevent-page-close/use-prevent-page-close.stories.tsx +0 -1
- package/src/hooks/use-range-pagination/use-range-pagination.test.tsx +2 -2
- package/src/hooks/use-range-pagination/use-range-pagination.tsx +24 -21
- package/src/hooks/use-selection/use-selection.stories.tsx +339 -84
- package/src/hooks/use-selection/use-selection.test.tsx +417 -2
- package/src/hooks/use-selection/use-selection.ts +212 -102
- package/src/hooks/use-session-storage/index.ts +1 -0
- package/src/hooks/use-session-storage/use-session-storage.stories.tsx +122 -0
- package/src/hooks/use-session-storage/use-session-storage.test.ts +164 -0
- package/src/hooks/use-session-storage/use-session-storage.ts +115 -0
- package/dist/hooks/use-async/use-async.cjs.js +0 -1
- package/dist/hooks/use-async/use-async.es.js +0 -57
- package/dist/hooks/use-focus-trap/scope-tab.cjs.js +0 -1
- package/dist/hooks/use-focus-trap/scope-tab.es.js +0 -21
- package/dist/hooks/use-focus-trap/tabbable.cjs.js +0 -1
- package/dist/hooks/use-focus-trap/tabbable.es.js +0 -38
- package/dist/hooks/use-focus-trap/use-focus-trap.cjs.js +0 -1
- package/dist/hooks/use-focus-trap/use-focus-trap.es.js +0 -34
- package/dist/hooks/use-mutation/use-mutation.cjs.js +0 -1
- package/dist/hooks/use-mutation/use-mutation.es.js +0 -41
- package/dist/src/hooks/use-async/index.d.ts +0 -1
- package/dist/src/hooks/use-async/use-async.d.ts +0 -21
- package/dist/src/hooks/use-focus-trap/index.d.ts +0 -1
- package/dist/src/hooks/use-focus-trap/scope-tab.d.ts +0 -1
- package/dist/src/hooks/use-focus-trap/tabbable.d.ts +0 -4
- package/dist/src/hooks/use-focus-trap/use-focus-trap.d.ts +0 -1
- package/dist/src/hooks/use-mutation/index.d.ts +0 -1
- package/dist/src/hooks/use-mutation/use-mutation.d.ts +0 -22
- package/dist/src/hooks/use-mutation/use-mutation.test.d.ts +0 -1
- package/src/hooks/use-async/index.ts +0 -1
- package/src/hooks/use-async/use-async.stories.tsx +0 -272
- package/src/hooks/use-async/use-async.test.ts +0 -397
- package/src/hooks/use-async/use-async.ts +0 -135
- package/src/hooks/use-focus-trap/index.ts +0 -1
- package/src/hooks/use-focus-trap/scope-tab.ts +0 -38
- package/src/hooks/use-focus-trap/tabbable.ts +0 -70
- package/src/hooks/use-focus-trap/use-focus-trap.stories.tsx +0 -37
- package/src/hooks/use-focus-trap/use-focus-trap.test.ts +0 -355
- package/src/hooks/use-focus-trap/use-focus-trap.ts +0 -78
- package/src/hooks/use-mutation/index.ts +0 -1
- package/src/hooks/use-pagination/use-pagination.tsx +0 -84
- /package/dist/src/hooks/{use-async/use-async.test.d.ts → use-action/use-action.test.d.ts} +0 -0
- /package/dist/src/hooks/{use-focus-trap/use-focus-trap.test.d.ts → use-session-storage/use-session-storage.test.d.ts} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { act, renderHook } from "@testing-library/react";
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import {
|
|
3
|
+
import { useAction } from "../use-action";
|
|
4
4
|
|
|
5
|
-
describe("
|
|
5
|
+
describe("useAction", () => {
|
|
6
6
|
beforeEach(() => {
|
|
7
7
|
vi.useFakeTimers();
|
|
8
8
|
});
|
|
@@ -14,7 +14,7 @@ describe("useMutation", () => {
|
|
|
14
14
|
// 1 — Initial state
|
|
15
15
|
it("initializes with idle status and null data/error", () => {
|
|
16
16
|
const { result } = renderHook(() =>
|
|
17
|
-
|
|
17
|
+
useAction({ fn: () => Promise.resolve("x") }),
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
expect(result.current.status).toBe("idle");
|
|
@@ -33,10 +33,10 @@ describe("useMutation", () => {
|
|
|
33
33
|
resolve = res;
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
const { result } = renderHook(() =>
|
|
36
|
+
const { result } = renderHook(() => useAction({ fn: () => deferred }));
|
|
37
37
|
|
|
38
38
|
act(() => {
|
|
39
|
-
result.current.
|
|
39
|
+
result.current.execute(undefined);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
expect(result.current.status).toBe("pending");
|
|
@@ -54,11 +54,11 @@ describe("useMutation", () => {
|
|
|
54
54
|
// 3 — Success path
|
|
55
55
|
it("sets status=success and data on successful mutation", async () => {
|
|
56
56
|
const { result } = renderHook(() =>
|
|
57
|
-
|
|
57
|
+
useAction({ fn: () => Promise.resolve("ok") }),
|
|
58
58
|
);
|
|
59
59
|
|
|
60
60
|
await act(async () => {
|
|
61
|
-
await result.current.
|
|
61
|
+
await result.current.executeAsync(undefined);
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
expect(result.current.status).toBe("success");
|
|
@@ -71,11 +71,11 @@ describe("useMutation", () => {
|
|
|
71
71
|
it("sets status=error and error on failed mutation", async () => {
|
|
72
72
|
const err = new Error("boom");
|
|
73
73
|
const { result } = renderHook(() =>
|
|
74
|
-
|
|
74
|
+
useAction({ fn: () => Promise.reject(err) }),
|
|
75
75
|
);
|
|
76
76
|
|
|
77
77
|
await act(async () => {
|
|
78
|
-
await result.current.
|
|
78
|
+
await result.current.executeAsync(undefined).catch(() => {});
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
expect(result.current.status).toBe("error");
|
|
@@ -84,30 +84,30 @@ describe("useMutation", () => {
|
|
|
84
84
|
expect(result.current.data).toBeNull();
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
// 5 —
|
|
88
|
-
it("
|
|
87
|
+
// 5 — executeAsync resolves with data
|
|
88
|
+
it("executeAsync resolves with the value returned by fn", async () => {
|
|
89
89
|
const { result } = renderHook(() =>
|
|
90
|
-
|
|
90
|
+
useAction({ fn: () => Promise.resolve(42) }),
|
|
91
91
|
);
|
|
92
92
|
|
|
93
93
|
let resolved: number | undefined;
|
|
94
94
|
await act(async () => {
|
|
95
|
-
resolved = await result.current.
|
|
95
|
+
resolved = await result.current.executeAsync(undefined);
|
|
96
96
|
});
|
|
97
97
|
|
|
98
98
|
expect(resolved).toBe(42);
|
|
99
99
|
});
|
|
100
100
|
|
|
101
|
-
// 6 —
|
|
102
|
-
it("
|
|
101
|
+
// 6 — executeAsync re-throws on error
|
|
102
|
+
it("executeAsync re-throws on error", async () => {
|
|
103
103
|
const err = new Error("rethrow-me");
|
|
104
104
|
const { result } = renderHook(() =>
|
|
105
|
-
|
|
105
|
+
useAction({ fn: () => Promise.reject(err) }),
|
|
106
106
|
);
|
|
107
107
|
|
|
108
108
|
let caught: Error | undefined;
|
|
109
109
|
await act(async () => {
|
|
110
|
-
caught = await result.current.
|
|
110
|
+
caught = await result.current.executeAsync(undefined).catch((e) => e);
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
expect(caught).toBe(err);
|
|
@@ -116,12 +116,12 @@ describe("useMutation", () => {
|
|
|
116
116
|
// 7 — mutate is fire-and-forget (void, no throw)
|
|
117
117
|
it("mutate returns void and does not throw on error", async () => {
|
|
118
118
|
const fn = vi.fn().mockRejectedValue(new Error("ignored"));
|
|
119
|
-
const { result } = renderHook(() =>
|
|
119
|
+
const { result } = renderHook(() => useAction({ fn }));
|
|
120
120
|
|
|
121
121
|
let threw = false;
|
|
122
122
|
await act(async () => {
|
|
123
123
|
try {
|
|
124
|
-
result.current.
|
|
124
|
+
result.current.execute(undefined);
|
|
125
125
|
await Promise.resolve();
|
|
126
126
|
} catch {
|
|
127
127
|
threw = true;
|
|
@@ -131,8 +131,8 @@ describe("useMutation", () => {
|
|
|
131
131
|
expect(threw).toBe(false);
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
-
// 8 —
|
|
135
|
-
it("calls
|
|
134
|
+
// 8 — onExecute called before fn resolves
|
|
135
|
+
it("calls onExecute(variables) before fn resolves", async () => {
|
|
136
136
|
const callOrder: string[] = [];
|
|
137
137
|
let resolveFn!: () => void;
|
|
138
138
|
const fn = vi.fn(
|
|
@@ -141,20 +141,20 @@ describe("useMutation", () => {
|
|
|
141
141
|
resolveFn = () => res("done");
|
|
142
142
|
}),
|
|
143
143
|
);
|
|
144
|
-
const
|
|
145
|
-
callOrder.push("
|
|
144
|
+
const onExecute = vi.fn(() => {
|
|
145
|
+
callOrder.push("onExecute");
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
-
const { result } = renderHook(() =>
|
|
148
|
+
const { result } = renderHook(() => useAction({ fn, onExecute }));
|
|
149
149
|
|
|
150
150
|
act(() => {
|
|
151
|
-
result.current.
|
|
151
|
+
result.current.execute(undefined);
|
|
152
152
|
});
|
|
153
153
|
|
|
154
|
-
//
|
|
155
|
-
expect(
|
|
154
|
+
// onExecute called synchronously — fn has not resolved yet
|
|
155
|
+
expect(onExecute).toHaveBeenCalledTimes(1);
|
|
156
156
|
expect(fn).toHaveBeenCalledTimes(1);
|
|
157
|
-
expect(callOrder).toEqual(["
|
|
157
|
+
expect(callOrder).toEqual(["onExecute"]);
|
|
158
158
|
|
|
159
159
|
await act(async () => {
|
|
160
160
|
resolveFn();
|
|
@@ -168,14 +168,14 @@ describe("useMutation", () => {
|
|
|
168
168
|
const vars = { id: 99 };
|
|
169
169
|
|
|
170
170
|
const { result } = renderHook(() =>
|
|
171
|
-
|
|
171
|
+
useAction({
|
|
172
172
|
fn: (v: { id: number }) => Promise.resolve(`result-${v.id}`),
|
|
173
173
|
onSuccess,
|
|
174
174
|
}),
|
|
175
175
|
);
|
|
176
176
|
|
|
177
177
|
await act(async () => {
|
|
178
|
-
await result.current.
|
|
178
|
+
await result.current.executeAsync(vars);
|
|
179
179
|
});
|
|
180
180
|
|
|
181
181
|
expect(onSuccess).toHaveBeenCalledOnce();
|
|
@@ -189,14 +189,14 @@ describe("useMutation", () => {
|
|
|
189
189
|
const vars = { id: 7 };
|
|
190
190
|
|
|
191
191
|
const { result } = renderHook(() =>
|
|
192
|
-
|
|
192
|
+
useAction({
|
|
193
193
|
fn: (_v: { id: number }) => Promise.reject(err),
|
|
194
194
|
onError,
|
|
195
195
|
}),
|
|
196
196
|
);
|
|
197
197
|
|
|
198
198
|
await act(async () => {
|
|
199
|
-
await result.current.
|
|
199
|
+
await result.current.executeAsync(vars).catch(() => {});
|
|
200
200
|
});
|
|
201
201
|
|
|
202
202
|
expect(onError).toHaveBeenCalledOnce();
|
|
@@ -209,14 +209,14 @@ describe("useMutation", () => {
|
|
|
209
209
|
const vars = { id: 1 };
|
|
210
210
|
|
|
211
211
|
const { result } = renderHook(() =>
|
|
212
|
-
|
|
212
|
+
useAction({
|
|
213
213
|
fn: (v: { id: number }) => Promise.resolve(`ok-${v.id}`),
|
|
214
214
|
onSettled,
|
|
215
215
|
}),
|
|
216
216
|
);
|
|
217
217
|
|
|
218
218
|
await act(async () => {
|
|
219
|
-
await result.current.
|
|
219
|
+
await result.current.executeAsync(vars);
|
|
220
220
|
});
|
|
221
221
|
|
|
222
222
|
expect(onSettled).toHaveBeenCalledOnce();
|
|
@@ -230,14 +230,14 @@ describe("useMutation", () => {
|
|
|
230
230
|
const vars = { id: 2 };
|
|
231
231
|
|
|
232
232
|
const { result } = renderHook(() =>
|
|
233
|
-
|
|
233
|
+
useAction({
|
|
234
234
|
fn: (_v: { id: number }) => Promise.reject(err),
|
|
235
235
|
onSettled,
|
|
236
236
|
}),
|
|
237
237
|
);
|
|
238
238
|
|
|
239
239
|
await act(async () => {
|
|
240
|
-
await result.current.
|
|
240
|
+
await result.current.executeAsync(vars).catch(() => {});
|
|
241
241
|
});
|
|
242
242
|
|
|
243
243
|
expect(onSettled).toHaveBeenCalledOnce();
|
|
@@ -247,11 +247,11 @@ describe("useMutation", () => {
|
|
|
247
247
|
// 13 — reset after success
|
|
248
248
|
it("reset() after success returns to idle and clears data", async () => {
|
|
249
249
|
const { result } = renderHook(() =>
|
|
250
|
-
|
|
250
|
+
useAction({ fn: () => Promise.resolve("done") }),
|
|
251
251
|
);
|
|
252
252
|
|
|
253
253
|
await act(async () => {
|
|
254
|
-
await result.current.
|
|
254
|
+
await result.current.executeAsync(undefined);
|
|
255
255
|
});
|
|
256
256
|
|
|
257
257
|
expect(result.current.status).toBe("success");
|
|
@@ -268,11 +268,11 @@ describe("useMutation", () => {
|
|
|
268
268
|
// 14 — reset after error
|
|
269
269
|
it("reset() after error returns to idle and clears error", async () => {
|
|
270
270
|
const { result } = renderHook(() =>
|
|
271
|
-
|
|
271
|
+
useAction({ fn: () => Promise.reject(new Error("e")) }),
|
|
272
272
|
);
|
|
273
273
|
|
|
274
274
|
await act(async () => {
|
|
275
|
-
await result.current.
|
|
275
|
+
await result.current.executeAsync(undefined).catch(() => {});
|
|
276
276
|
});
|
|
277
277
|
|
|
278
278
|
expect(result.current.status).toBe("error");
|
|
@@ -290,14 +290,14 @@ describe("useMutation", () => {
|
|
|
290
290
|
it("mutate reference is stable when inline fn changes", () => {
|
|
291
291
|
let fn = vi.fn().mockResolvedValue("a");
|
|
292
292
|
|
|
293
|
-
const { result, rerender } = renderHook(() =>
|
|
293
|
+
const { result, rerender } = renderHook(() => useAction({ fn }));
|
|
294
294
|
|
|
295
|
-
const
|
|
295
|
+
const executeRef1 = result.current.execute;
|
|
296
296
|
|
|
297
297
|
fn = vi.fn().mockResolvedValue("b");
|
|
298
298
|
rerender();
|
|
299
299
|
|
|
300
|
-
expect(result.current.
|
|
300
|
+
expect(result.current.execute).toBe(executeRef1);
|
|
301
301
|
});
|
|
302
302
|
|
|
303
303
|
// 16 — mutate stable when onSuccess changes
|
|
@@ -305,35 +305,35 @@ describe("useMutation", () => {
|
|
|
305
305
|
let onSuccess = vi.fn();
|
|
306
306
|
|
|
307
307
|
const { result, rerender } = renderHook(() =>
|
|
308
|
-
|
|
308
|
+
useAction({ fn: () => Promise.resolve("x"), onSuccess }),
|
|
309
309
|
);
|
|
310
310
|
|
|
311
|
-
const
|
|
311
|
+
const executeRef1 = result.current.execute;
|
|
312
312
|
|
|
313
313
|
onSuccess = vi.fn();
|
|
314
314
|
rerender();
|
|
315
315
|
|
|
316
|
-
expect(result.current.
|
|
316
|
+
expect(result.current.execute).toBe(executeRef1);
|
|
317
317
|
});
|
|
318
318
|
|
|
319
319
|
// 17 — variables forwarded to fn and all callbacks
|
|
320
|
-
it("forwards variables to fn,
|
|
320
|
+
it("forwards variables to fn, onExecute, onSuccess, and onSettled", async () => {
|
|
321
321
|
const vars = { userId: 42, name: "Alice" };
|
|
322
322
|
const fn = vi.fn().mockResolvedValue("created");
|
|
323
|
-
const
|
|
323
|
+
const onExecute = vi.fn();
|
|
324
324
|
const onSuccess = vi.fn();
|
|
325
325
|
const onSettled = vi.fn();
|
|
326
326
|
|
|
327
327
|
const { result } = renderHook(() =>
|
|
328
|
-
|
|
328
|
+
useAction({ fn, onExecute, onSuccess, onSettled }),
|
|
329
329
|
);
|
|
330
330
|
|
|
331
331
|
await act(async () => {
|
|
332
|
-
await result.current.
|
|
332
|
+
await result.current.executeAsync(vars);
|
|
333
333
|
});
|
|
334
334
|
|
|
335
335
|
expect(fn).toHaveBeenCalledWith(vars);
|
|
336
|
-
expect(
|
|
336
|
+
expect(onExecute).toHaveBeenCalledWith(vars);
|
|
337
337
|
expect(onSuccess).toHaveBeenCalledWith("created", vars);
|
|
338
338
|
expect(onSettled).toHaveBeenCalledWith("created", null, vars);
|
|
339
339
|
});
|
|
@@ -347,11 +347,11 @@ describe("useMutation", () => {
|
|
|
347
347
|
const onSettled = vi.fn();
|
|
348
348
|
|
|
349
349
|
const { result } = renderHook(() =>
|
|
350
|
-
|
|
350
|
+
useAction({ fn: () => Promise.reject(err), onError, onSettled }),
|
|
351
351
|
);
|
|
352
352
|
|
|
353
353
|
await act(async () => {
|
|
354
|
-
await result.current.
|
|
354
|
+
await result.current.executeAsync(undefined).catch(() => {});
|
|
355
355
|
});
|
|
356
356
|
|
|
357
357
|
expect(onSettled).toHaveBeenCalledOnce();
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useCallback, useState } from "react";
|
|
2
2
|
import { useLatestRef } from "../internal/use-latest-ref";
|
|
3
3
|
|
|
4
|
-
type
|
|
4
|
+
type ActionStatus = "idle" | "pending" | "success" | "error";
|
|
5
5
|
|
|
6
|
-
export interface
|
|
6
|
+
export interface UseActionOptions<TVariables = void, TData = unknown> {
|
|
7
7
|
fn: (variables: TVariables) => Promise<TData>;
|
|
8
|
-
|
|
8
|
+
onExecute?: (variables: TVariables) => void;
|
|
9
9
|
onSuccess?: (data: TData, variables: TVariables) => void;
|
|
10
10
|
onError?: (error: Error, variables: TVariables) => void;
|
|
11
11
|
onSettled?: (
|
|
@@ -15,35 +15,35 @@ export interface UseMutationOptions<TVariables = void, TData = unknown> {
|
|
|
15
15
|
) => void;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export interface
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
export interface UseActionReturn<TVariables = void, TData = unknown> {
|
|
19
|
+
execute: (variables: TVariables) => void;
|
|
20
|
+
executeAsync: (variables: TVariables) => Promise<TData>;
|
|
21
21
|
reset: () => void;
|
|
22
22
|
data: TData | null;
|
|
23
23
|
error: Error | null;
|
|
24
|
-
status:
|
|
24
|
+
status: ActionStatus;
|
|
25
25
|
isIdle: boolean;
|
|
26
26
|
isPending: boolean;
|
|
27
27
|
isSuccess: boolean;
|
|
28
28
|
isError: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export const
|
|
32
|
-
options:
|
|
33
|
-
):
|
|
34
|
-
const [status, setStatus] = useState<
|
|
31
|
+
export const useAction = <TVariables = void, TData = unknown>(
|
|
32
|
+
options: UseActionOptions<TVariables, TData>,
|
|
33
|
+
): UseActionReturn<TVariables, TData> => {
|
|
34
|
+
const [status, setStatus] = useState<ActionStatus>("idle");
|
|
35
35
|
const [data, setData] = useState<TData | null>(null);
|
|
36
36
|
const [error, setError] = useState<Error | null>(null);
|
|
37
37
|
|
|
38
38
|
const fnRef = useLatestRef(options.fn);
|
|
39
|
-
const
|
|
39
|
+
const onExecuteRef = useLatestRef(options.onExecute);
|
|
40
40
|
const onSuccessRef = useLatestRef(options.onSuccess);
|
|
41
41
|
const onErrorRef = useLatestRef(options.onError);
|
|
42
42
|
const onSettledRef = useLatestRef(options.onSettled);
|
|
43
43
|
|
|
44
|
-
const
|
|
44
|
+
const executeAsync = useCallback(
|
|
45
45
|
async (variables: TVariables): Promise<TData> => {
|
|
46
|
-
|
|
46
|
+
onExecuteRef.current?.(variables);
|
|
47
47
|
setStatus("pending");
|
|
48
48
|
setData(null);
|
|
49
49
|
setError(null);
|
|
@@ -66,14 +66,14 @@ export const useMutation = <TVariables = void, TData = unknown>(
|
|
|
66
66
|
onSettledRef.current?.(result, caught, variables);
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
|
-
[fnRef,
|
|
69
|
+
[fnRef, onExecuteRef, onSuccessRef, onErrorRef, onSettledRef],
|
|
70
70
|
);
|
|
71
71
|
|
|
72
|
-
const
|
|
72
|
+
const execute = useCallback(
|
|
73
73
|
(variables: TVariables): void => {
|
|
74
|
-
|
|
74
|
+
executeAsync(variables).catch(() => {});
|
|
75
75
|
},
|
|
76
|
-
[
|
|
76
|
+
[executeAsync],
|
|
77
77
|
);
|
|
78
78
|
|
|
79
79
|
const reset = useCallback(() => {
|
|
@@ -83,8 +83,8 @@ export const useMutation = <TVariables = void, TData = unknown>(
|
|
|
83
83
|
}, []);
|
|
84
84
|
|
|
85
85
|
return {
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
execute,
|
|
87
|
+
executeAsync,
|
|
88
88
|
reset,
|
|
89
89
|
data,
|
|
90
90
|
error,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { act, render, screen } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { useIsVisible } from "./use-is-visible";
|
|
4
4
|
|
|
5
5
|
// IntersectionObserver isn't available in jsdom. Provide a controllable mock.
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { useCallback, useMemo, useSyncExternalStore } from "react";
|
|
2
|
-
import { isBrowser } from "../internal";
|
|
2
|
+
import { isBrowser, type Serializer } from "../internal";
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
read: (raw: string) => T;
|
|
6
|
-
write: (value: T) => string;
|
|
7
|
-
}
|
|
4
|
+
export type { Serializer };
|
|
8
5
|
|
|
9
6
|
export interface UseLocalStorageOptions<T> {
|
|
10
7
|
serializer?: Serializer<T>;
|