@jobber/hooks 2.0.3-dar.45 → 2.0.3-dar.47
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/package.json +4 -3
- package/src/index.ts +10 -0
- package/src/useAssert/index.ts +1 -0
- package/src/useAssert/useAssert.stories.mdx +32 -0
- package/src/useAssert/useAssert.tsx +19 -0
- package/src/useCollectionQuery/index.ts +1 -0
- package/src/useCollectionQuery/mdxUtils.ts +190 -0
- package/src/useCollectionQuery/test-utilities/index.ts +3 -0
- package/src/useCollectionQuery/test-utilities/mocks.tsx +147 -0
- package/src/useCollectionQuery/test-utilities/queries.ts +95 -0
- package/src/useCollectionQuery/test-utilities/utils.ts +3 -0
- package/src/useCollectionQuery/uniqueEdges.tsx +26 -0
- package/src/useCollectionQuery/uniqueNodes.tsx +12 -0
- package/src/useCollectionQuery/useCollectionQuery.stories.mdx +129 -0
- package/src/useCollectionQuery/useCollectionQuery.test.tsx +419 -0
- package/src/useCollectionQuery/useCollectionQuery.ts +359 -0
- package/src/useFocusTrap/index.ts +1 -0
- package/src/useFocusTrap/useFocusTrap.stories.mdx +49 -0
- package/src/useFocusTrap/useFocusTrap.test.tsx +66 -0
- package/src/useFocusTrap/useFocusTrap.ts +64 -0
- package/src/useFormState/index.ts +1 -0
- package/src/useFormState/useFormState.stories.mdx +70 -0
- package/src/useFormState/useFormState.ts +10 -0
- package/src/useIsMounted/index.ts +1 -0
- package/src/useIsMounted/useIsMounted.stories.mdx +59 -0
- package/src/useIsMounted/useIsMounted.test.tsx +18 -0
- package/src/useIsMounted/useIsMounted.ts +30 -0
- package/src/useLiveAnnounce/index.ts +1 -0
- package/src/useLiveAnnounce/useLiveAnnounce.stories.mdx +38 -0
- package/src/useLiveAnnounce/useLiveAnnounce.test.tsx +55 -0
- package/src/useLiveAnnounce/useLiveAnnounce.tsx +47 -0
- package/src/useOnKeyDown/index.ts +1 -0
- package/src/useOnKeyDown/useOnKeyDown.stories.mdx +67 -0
- package/src/useOnKeyDown/useOnKeyDown.test.tsx +31 -0
- package/src/useOnKeyDown/useOnKeyDown.ts +52 -0
- package/src/usePasswordStrength/index.ts +1 -0
- package/src/usePasswordStrength/usePasswordStrength.stories.mdx +51 -0
- package/src/usePasswordStrength/usePasswordStrength.ts +21 -0
- package/src/useRefocusOnActivator/index.ts +1 -0
- package/src/useRefocusOnActivator/useRefocusOnActivator.stories.mdx +39 -0
- package/src/useRefocusOnActivator/useRefocusOnActivator.ts +26 -0
- package/src/useResizeObserver/index.ts +1 -0
- package/src/useResizeObserver/useResizeObserver.stories.mdx +134 -0
- package/src/useResizeObserver/useResizeObserver.ts +78 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { DocumentNode } from "@apollo/client";
|
|
2
|
+
import { act, renderHook } from "@testing-library/react-hooks";
|
|
3
|
+
import { waitFor } from "@testing-library/react";
|
|
4
|
+
import { useCollectionQuery } from "./useCollectionQuery";
|
|
5
|
+
import {
|
|
6
|
+
LIST_QUERY,
|
|
7
|
+
LIST_QUERY_WITH_TOTAL_COUNT,
|
|
8
|
+
ListQueryType,
|
|
9
|
+
SUBSCRIPTION_QUERY,
|
|
10
|
+
SubscriptionQueryType,
|
|
11
|
+
buildListRequestMock,
|
|
12
|
+
buildListRequestMockForNextPage,
|
|
13
|
+
buildSubscriptionRequestMock,
|
|
14
|
+
listQueryResponseMock,
|
|
15
|
+
listQueryWithTotalCountResponseMock,
|
|
16
|
+
setListQueryMockHasNextPage,
|
|
17
|
+
subscriptionQueryMock,
|
|
18
|
+
wait,
|
|
19
|
+
wrapper,
|
|
20
|
+
} from "./test-utilities";
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
setListQueryMockHasNextPage(true);
|
|
24
|
+
listQueryResponseMock.mockClear();
|
|
25
|
+
subscriptionQueryMock.mockClear();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function useCollectionQueryHook(query: DocumentNode) {
|
|
29
|
+
return useCollectionQuery<ListQueryType>({
|
|
30
|
+
query: query,
|
|
31
|
+
getCollectionByPath(data) {
|
|
32
|
+
return data?.conversation?.smsMessages;
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function useCollectionQueryHookWithSubscription(query: DocumentNode) {
|
|
38
|
+
return useCollectionQuery<ListQueryType, SubscriptionQueryType>({
|
|
39
|
+
query: query,
|
|
40
|
+
getCollectionByPath(data) {
|
|
41
|
+
return data?.conversation?.smsMessages;
|
|
42
|
+
},
|
|
43
|
+
subscription: {
|
|
44
|
+
document: SUBSCRIPTION_QUERY,
|
|
45
|
+
getNodeByPath(conversationData) {
|
|
46
|
+
return conversationData?.conversationMessage?.smsMessage;
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function useCollectionQueryHookWithSubscriptionAndSearch(
|
|
53
|
+
query: DocumentNode,
|
|
54
|
+
searchTerm: string,
|
|
55
|
+
) {
|
|
56
|
+
return useCollectionQuery<ListQueryType, SubscriptionQueryType>({
|
|
57
|
+
query: query,
|
|
58
|
+
queryOptions: {
|
|
59
|
+
variables: {
|
|
60
|
+
searchTerm: searchTerm,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
getCollectionByPath(data) {
|
|
64
|
+
return data?.conversation?.smsMessages;
|
|
65
|
+
},
|
|
66
|
+
subscription: {
|
|
67
|
+
document: SUBSCRIPTION_QUERY,
|
|
68
|
+
getNodeByPath(conversationData) {
|
|
69
|
+
return conversationData?.conversationMessage?.smsMessage;
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
describe("useCollectionQuery", () => {
|
|
76
|
+
describe.each`
|
|
77
|
+
testCase | query | responseMock
|
|
78
|
+
${"excludes totalCount"} | ${LIST_QUERY} | ${listQueryResponseMock}
|
|
79
|
+
${"includes totalCount"} | ${LIST_QUERY_WITH_TOTAL_COUNT} | ${listQueryWithTotalCountResponseMock}
|
|
80
|
+
`("when the query run $testCase", ({ query, responseMock }) => {
|
|
81
|
+
describe("when useCollectionQuery is first called", () => {
|
|
82
|
+
describe("when nextPage is called while it's still loading initial content", () => {
|
|
83
|
+
it("should not trigger a the next page to be fetched", async () => {
|
|
84
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
85
|
+
wrapper: wrapper([
|
|
86
|
+
buildListRequestMock(query, responseMock),
|
|
87
|
+
buildListRequestMockForNextPage(query, responseMock),
|
|
88
|
+
]),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
act(() => {
|
|
92
|
+
result.current.nextPage();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
await act(wait);
|
|
96
|
+
expect(responseMock).toHaveBeenCalledTimes(1);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("when refresh is called while it's still loading initial content", () => {
|
|
101
|
+
it("should not trigger a refresh", async () => {
|
|
102
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
103
|
+
wrapper: wrapper([
|
|
104
|
+
buildListRequestMock(query, responseMock),
|
|
105
|
+
buildListRequestMock(query, responseMock),
|
|
106
|
+
]),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
act(() => {
|
|
110
|
+
result.current.refresh();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
await act(wait);
|
|
114
|
+
expect(responseMock).toHaveBeenCalledTimes(1);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("when there is no error", () => {
|
|
119
|
+
it("should update data", async () => {
|
|
120
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
121
|
+
wrapper: wrapper([buildListRequestMock(query, responseMock)]),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
await act(wait);
|
|
125
|
+
|
|
126
|
+
expect(
|
|
127
|
+
result.current.data?.conversation?.smsMessages?.edges?.length,
|
|
128
|
+
).toBe(1);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should set initialLoading while loading data", async () => {
|
|
132
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
133
|
+
wrapper: wrapper([buildListRequestMock(query, responseMock)]),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expect(result.current.loadingInitialContent).toBe(true);
|
|
137
|
+
|
|
138
|
+
await act(wait);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("#nextPage", () => {
|
|
144
|
+
describe("when nextPage is called while it's still loadingNextPage", () => {
|
|
145
|
+
it("should not trigger a nextPage", async () => {
|
|
146
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
147
|
+
wrapper: wrapper([
|
|
148
|
+
buildListRequestMock(query, responseMock),
|
|
149
|
+
buildListRequestMockForNextPage(query, responseMock),
|
|
150
|
+
buildListRequestMockForNextPage(query, responseMock),
|
|
151
|
+
]),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
await act(wait);
|
|
155
|
+
act(() => {
|
|
156
|
+
result.current.nextPage();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
act(() => {
|
|
160
|
+
result.current.nextPage();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
await act(wait);
|
|
164
|
+
|
|
165
|
+
await waitFor(() => {
|
|
166
|
+
expect(
|
|
167
|
+
result.current.data?.conversation?.smsMessages?.edges,
|
|
168
|
+
).toHaveLength(2);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("when refresh is called while it's still loadingNextPage", () => {
|
|
174
|
+
it("should trigger a refresh", async () => {
|
|
175
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
176
|
+
wrapper: wrapper([
|
|
177
|
+
buildListRequestMock(query, responseMock),
|
|
178
|
+
buildListRequestMock(query, responseMock),
|
|
179
|
+
buildListRequestMockForNextPage(query, responseMock),
|
|
180
|
+
]),
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
await act(wait);
|
|
184
|
+
|
|
185
|
+
act(() => {
|
|
186
|
+
result.current.nextPage();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
act(() => {
|
|
190
|
+
result.current.refresh();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
expect(result.current.loadingRefresh).toBe(true);
|
|
194
|
+
|
|
195
|
+
await act(wait);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("when there is no more data to fetch", () => {
|
|
200
|
+
it("should not fetch more data", async () => {
|
|
201
|
+
setListQueryMockHasNextPage(false);
|
|
202
|
+
|
|
203
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
204
|
+
wrapper: wrapper([
|
|
205
|
+
buildListRequestMock(query, responseMock),
|
|
206
|
+
buildListRequestMockForNextPage(query, responseMock),
|
|
207
|
+
]),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
await act(wait);
|
|
211
|
+
act(() => {
|
|
212
|
+
result.current.nextPage();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
await act(wait);
|
|
216
|
+
|
|
217
|
+
expect(responseMock).toHaveBeenCalledTimes(1);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("when there is no error", () => {
|
|
222
|
+
it("should update data", async () => {
|
|
223
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
224
|
+
wrapper: wrapper([
|
|
225
|
+
buildListRequestMock(query, responseMock),
|
|
226
|
+
buildListRequestMockForNextPage(query, responseMock),
|
|
227
|
+
]),
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
await act(wait);
|
|
231
|
+
act(() => {
|
|
232
|
+
result.current.nextPage();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
await act(wait);
|
|
236
|
+
|
|
237
|
+
await waitFor(() => {
|
|
238
|
+
expect(
|
|
239
|
+
result.current.data?.conversation?.smsMessages?.edges,
|
|
240
|
+
).toHaveLength(2);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should set loadingNextPage while loading data", async () => {
|
|
245
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
246
|
+
wrapper: wrapper([
|
|
247
|
+
buildListRequestMock(query, responseMock),
|
|
248
|
+
buildListRequestMockForNextPage(query, responseMock),
|
|
249
|
+
]),
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
await act(wait);
|
|
253
|
+
|
|
254
|
+
act(() => {
|
|
255
|
+
result.current.nextPage();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
expect(result.current.loadingNextPage).toBe(true);
|
|
259
|
+
|
|
260
|
+
await act(wait);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
describe("#refresh", () => {
|
|
266
|
+
describe("when refresh is called while it's still loadingRefresh", () => {
|
|
267
|
+
it("should not trigger a refresh", async () => {
|
|
268
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
269
|
+
wrapper: wrapper([
|
|
270
|
+
buildListRequestMock(query, responseMock),
|
|
271
|
+
buildListRequestMock(query, responseMock),
|
|
272
|
+
buildListRequestMock(query, responseMock),
|
|
273
|
+
]),
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
await act(wait);
|
|
277
|
+
|
|
278
|
+
act(() => {
|
|
279
|
+
result.current.refresh();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
act(() => {
|
|
283
|
+
result.current.refresh();
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
await act(wait);
|
|
287
|
+
|
|
288
|
+
expect(responseMock).toHaveBeenCalledTimes(2);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe("when nextPage is called while it's still loadingRefresh", () => {
|
|
293
|
+
it("should not trigger a nextPage", async () => {
|
|
294
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
295
|
+
wrapper: wrapper([
|
|
296
|
+
buildListRequestMock(query, responseMock),
|
|
297
|
+
buildListRequestMock(query, responseMock),
|
|
298
|
+
buildListRequestMockForNextPage(query, responseMock),
|
|
299
|
+
]),
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
await act(wait);
|
|
303
|
+
|
|
304
|
+
act(() => {
|
|
305
|
+
result.current.refresh();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
act(() => {
|
|
309
|
+
result.current.nextPage();
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
expect(result.current.loadingNextPage).toBe(false);
|
|
313
|
+
|
|
314
|
+
await act(wait);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe("when there is no error", () => {
|
|
319
|
+
it("should set loadingRefresh while loading data", async () => {
|
|
320
|
+
const { result } = renderHook(() => useCollectionQueryHook(query), {
|
|
321
|
+
wrapper: wrapper([
|
|
322
|
+
buildListRequestMock(query, responseMock),
|
|
323
|
+
buildListRequestMock(query, responseMock),
|
|
324
|
+
]),
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
await act(wait);
|
|
328
|
+
|
|
329
|
+
act(() => {
|
|
330
|
+
result.current.refresh();
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
expect(result.current.loadingRefresh).toBe(true);
|
|
334
|
+
|
|
335
|
+
await act(wait);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe("#subscribeToMore", () => {
|
|
341
|
+
describe("when hook receives update from item not in collection", () => {
|
|
342
|
+
it("should add new item to collection", async () => {
|
|
343
|
+
const { result, waitForNextUpdate } = renderHook(
|
|
344
|
+
() => useCollectionQueryHookWithSubscription(query),
|
|
345
|
+
{
|
|
346
|
+
wrapper: wrapper([
|
|
347
|
+
buildListRequestMock(query, responseMock, "1"),
|
|
348
|
+
buildSubscriptionRequestMock("2"),
|
|
349
|
+
]),
|
|
350
|
+
},
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
// Wait for initial load
|
|
354
|
+
await act(waitForNextUpdate);
|
|
355
|
+
|
|
356
|
+
// Wait for subscription
|
|
357
|
+
await act(waitForNextUpdate);
|
|
358
|
+
|
|
359
|
+
expect(
|
|
360
|
+
result?.current?.data?.conversation?.smsMessages?.edges?.length,
|
|
361
|
+
).toBe(2);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
describe("when hook receives update from item already in collection", () => {
|
|
366
|
+
it("should return the existing collection", async () => {
|
|
367
|
+
const { result, waitForNextUpdate } = renderHook(
|
|
368
|
+
() => useCollectionQueryHookWithSubscription(query),
|
|
369
|
+
{
|
|
370
|
+
wrapper: wrapper([
|
|
371
|
+
buildListRequestMock(query, responseMock, "1"),
|
|
372
|
+
buildSubscriptionRequestMock("1"),
|
|
373
|
+
]),
|
|
374
|
+
},
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
// Wait for initial load
|
|
378
|
+
await act(waitForNextUpdate);
|
|
379
|
+
|
|
380
|
+
// Wait for subscription
|
|
381
|
+
await act(() => wait(200));
|
|
382
|
+
|
|
383
|
+
expect(
|
|
384
|
+
result?.current?.data?.conversation?.smsMessages?.edges?.length,
|
|
385
|
+
).toBe(1);
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
describe("when hook receives `update` but is currently searching a collection", () => {
|
|
390
|
+
it("should return the existing collection without adding the subscribed content", async () => {
|
|
391
|
+
const searchTerm = "FooBar";
|
|
392
|
+
const { result, waitForNextUpdate } = renderHook(
|
|
393
|
+
() =>
|
|
394
|
+
useCollectionQueryHookWithSubscriptionAndSearch(
|
|
395
|
+
query,
|
|
396
|
+
searchTerm,
|
|
397
|
+
),
|
|
398
|
+
{
|
|
399
|
+
wrapper: wrapper([
|
|
400
|
+
buildListRequestMock(query, responseMock, "1", searchTerm),
|
|
401
|
+
buildSubscriptionRequestMock("1"),
|
|
402
|
+
]),
|
|
403
|
+
},
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
// Wait for initial load
|
|
407
|
+
await act(waitForNextUpdate);
|
|
408
|
+
|
|
409
|
+
// Wait for subscription
|
|
410
|
+
await act(() => wait(200));
|
|
411
|
+
|
|
412
|
+
expect(
|
|
413
|
+
result?.current?.data?.conversation?.smsMessages?.edges?.length,
|
|
414
|
+
).toBe(1);
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
});
|