piral-search 1.0.0-pre.2296 → 1.0.0

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.
Files changed (52) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +8 -2
  3. package/esm/Search.js +3 -3
  4. package/esm/Search.js.map +1 -1
  5. package/esm/SearchInput.js +2 -2
  6. package/esm/SearchInput.js.map +1 -1
  7. package/esm/actions.js +46 -49
  8. package/esm/actions.js.map +1 -1
  9. package/esm/components.d.ts +4 -5
  10. package/esm/components.js +3 -3
  11. package/esm/components.js.map +1 -1
  12. package/esm/create.js +37 -38
  13. package/esm/create.js.map +1 -1
  14. package/esm/default.js +3 -3
  15. package/esm/default.js.map +1 -1
  16. package/esm/types.d.ts +11 -3
  17. package/esm/useDebounce.d.ts +8 -0
  18. package/esm/useDebounce.js +17 -0
  19. package/esm/useDebounce.js.map +1 -0
  20. package/esm/useSearch.js +7 -6
  21. package/esm/useSearch.js.map +1 -1
  22. package/lib/Search.js +7 -6
  23. package/lib/Search.js.map +1 -1
  24. package/lib/SearchInput.js +6 -5
  25. package/lib/SearchInput.js.map +1 -1
  26. package/lib/actions.js +49 -52
  27. package/lib/actions.js.map +1 -1
  28. package/lib/components.d.ts +4 -5
  29. package/lib/components.js +4 -4
  30. package/lib/components.js.map +1 -1
  31. package/lib/create.js +43 -44
  32. package/lib/create.js.map +1 -1
  33. package/lib/default.js +8 -5
  34. package/lib/default.js.map +1 -1
  35. package/lib/index.js +1 -1
  36. package/lib/types.d.ts +11 -3
  37. package/lib/useDebounce.d.ts +8 -0
  38. package/lib/useDebounce.js +21 -0
  39. package/lib/useDebounce.js.map +1 -0
  40. package/lib/useSearch.js +8 -7
  41. package/lib/useSearch.js.map +1 -1
  42. package/package.json +29 -6
  43. package/piral-search.min.js +1 -0
  44. package/src/actions.test.ts +45 -44
  45. package/src/components.tsx +3 -5
  46. package/src/create.test.ts +16 -4
  47. package/src/create.ts +45 -23
  48. package/src/types.ts +12 -3
  49. package/src/useDebounce.test.ts +67 -0
  50. package/src/useDebounce.ts +19 -0
  51. package/src/useSearch.test.ts +17 -7
  52. package/src/useSearch.ts +2 -1
@@ -1,6 +1,7 @@
1
- import { Atom, deref } from '@dbeining/react-atom';
1
+ import create from 'zustand';
2
+ import { createListener } from 'piral-base';
3
+ import { createActions } from 'piral-core';
2
4
  import { createActions as createSearchActions } from './actions';
3
- import { createActions, createListener } from 'piral-core';
4
5
 
5
6
  const state = {
6
7
  search: {
@@ -14,7 +15,7 @@ const state = {
14
15
 
15
16
  describe('Search Action Module', () => {
16
17
  it('appendSearchResults cont appends new items with still loading', () => {
17
- const state = Atom.of({
18
+ const state: any = create(() => ({
18
19
  foo: 5,
19
20
  search: {
20
21
  input: 'yo',
@@ -23,11 +24,11 @@ describe('Search Action Module', () => {
23
24
  items: ['c'],
24
25
  },
25
26
  },
26
- });
27
+ }));
27
28
  const ctx = createActions(state, createListener({}));
28
29
  ctx.defineActions(createSearchActions());
29
30
  ctx.appendSearchResults(['a', 'b'], false);
30
- expect(deref(state)).toEqual({
31
+ expect(state.getState()).toEqual({
31
32
  foo: 5,
32
33
  search: {
33
34
  input: 'yo',
@@ -40,7 +41,7 @@ describe('Search Action Module', () => {
40
41
  });
41
42
 
42
43
  it('appendSearchResults stop appends new items without still loading', () => {
43
- const state = Atom.of({
44
+ const state: any = create(() => ({
44
45
  foo: [1, 2],
45
46
  search: {
46
47
  input: 'yo',
@@ -49,11 +50,11 @@ describe('Search Action Module', () => {
49
50
  items: ['c'],
50
51
  },
51
52
  },
52
- });
53
+ }));
53
54
  const ctx = createActions(state, createListener({}));
54
55
  ctx.defineActions(createSearchActions());
55
56
  ctx.appendSearchResults(['a'], true);
56
- expect(deref(state)).toEqual({
57
+ expect(state.getState()).toEqual({
57
58
  foo: [1, 2],
58
59
  search: {
59
60
  input: 'yo',
@@ -66,7 +67,7 @@ describe('Search Action Module', () => {
66
67
  });
67
68
 
68
69
  it('prependSearchResults cont prepends new items with still loading', () => {
69
- const state = Atom.of({
70
+ const state: any = create(() => ({
70
71
  foo: 5,
71
72
  search: {
72
73
  input: 'yo',
@@ -75,11 +76,11 @@ describe('Search Action Module', () => {
75
76
  items: ['c'],
76
77
  },
77
78
  },
78
- });
79
+ }));
79
80
  const ctx = createActions(state, createListener({}));
80
81
  ctx.defineActions(createSearchActions());
81
82
  ctx.prependSearchResults(['a', 'b'], false);
82
- expect(deref(state)).toEqual({
83
+ expect(state.getState()).toEqual({
83
84
  foo: 5,
84
85
  search: {
85
86
  input: 'yo',
@@ -92,7 +93,7 @@ describe('Search Action Module', () => {
92
93
  });
93
94
 
94
95
  it('prependSearchResults stop prepends new items without still loading', () => {
95
- const state = Atom.of({
96
+ const state: any = create(() => ({
96
97
  foo: [1, 2],
97
98
  search: {
98
99
  input: 'yo',
@@ -101,11 +102,11 @@ describe('Search Action Module', () => {
101
102
  items: ['c'],
102
103
  },
103
104
  },
104
- });
105
+ }));
105
106
  const ctx = createActions(state, createListener({}));
106
107
  ctx.defineActions(createSearchActions());
107
108
  ctx.prependSearchResults(['a'], true);
108
- expect(deref(state)).toEqual({
109
+ expect(state.getState()).toEqual({
109
110
  foo: [1, 2],
110
111
  search: {
111
112
  input: 'yo',
@@ -118,7 +119,7 @@ describe('Search Action Module', () => {
118
119
  });
119
120
 
120
121
  it('resetSearchResults cont resets the search results while still loading', () => {
121
- const state = Atom.of({
122
+ const state: any = create(() => ({
122
123
  foo: [1, 2],
123
124
  search: {
124
125
  input: 'yo',
@@ -127,11 +128,11 @@ describe('Search Action Module', () => {
127
128
  items: ['c'],
128
129
  },
129
130
  },
130
- });
131
+ }));
131
132
  const ctx = createActions(state, createListener({}));
132
133
  ctx.defineActions(createSearchActions());
133
134
  ctx.resetSearchResults('yo', true);
134
- expect(deref(state)).toEqual({
135
+ expect(state.getState()).toEqual({
135
136
  foo: [1, 2],
136
137
  search: {
137
138
  input: 'yo',
@@ -144,7 +145,7 @@ describe('Search Action Module', () => {
144
145
  });
145
146
 
146
147
  it('resetSearchResults stop resets the search results without loading', () => {
147
- const state = Atom.of({
148
+ const state: any = create(() => ({
148
149
  foo: 5,
149
150
  search: {
150
151
  input: 'yo y',
@@ -153,11 +154,11 @@ describe('Search Action Module', () => {
153
154
  items: ['c'],
154
155
  },
155
156
  },
156
- });
157
+ }));
157
158
  const ctx = createActions(state, createListener({}));
158
159
  ctx.defineActions(createSearchActions());
159
160
  ctx.resetSearchResults('yo y', false);
160
- expect(deref(state)).toEqual({
161
+ expect(state.getState()).toEqual({
161
162
  foo: 5,
162
163
  search: {
163
164
  input: 'yo y',
@@ -170,7 +171,7 @@ describe('Search Action Module', () => {
170
171
  });
171
172
 
172
173
  it('setSearchInput sets the input field accordingly', () => {
173
- const state = Atom.of({
174
+ const state: any = create(() => ({
174
175
  foo: 5,
175
176
  search: {
176
177
  input: 'yo y',
@@ -179,11 +180,11 @@ describe('Search Action Module', () => {
179
180
  items: ['c'],
180
181
  },
181
182
  },
182
- });
183
+ }));
183
184
  const ctx = createActions(state, createListener({}));
184
185
  ctx.defineActions(createSearchActions());
185
186
  ctx.setSearchInput('test input');
186
- expect(deref(state)).toEqual({
187
+ expect(state.getState()).toEqual({
187
188
  foo: 5,
188
189
  search: {
189
190
  input: 'test input',
@@ -197,11 +198,11 @@ describe('Search Action Module', () => {
197
198
 
198
199
  it('immediately resets with loading false if some value is given but no provider found', () => {
199
200
  state.search.input = 'foo';
200
- const globalState = Atom.of(state as any);
201
+ const globalState: any = create(() => state);
201
202
  const ctx = createActions(globalState, createListener({}));
202
203
  ctx.defineActions(createSearchActions());
203
204
  ctx.triggerSearch();
204
- expect(deref(globalState).search.results.loading).toBe(false);
205
+ expect(globalState.getState().search.results.loading).toBe(false);
205
206
  });
206
207
 
207
208
  it('immediately resets with loading true if some value is given and a provider is found', () => {
@@ -213,38 +214,38 @@ describe('Search Action Module', () => {
213
214
  clear() {},
214
215
  cancel() {},
215
216
  };
216
- const globalState = Atom.of(state as any);
217
+ const globalState: any = create(() => state);
217
218
  const ctx = createActions(globalState, createListener({}));
218
219
  ctx.defineActions(createSearchActions());
219
220
  ctx.triggerSearch();
220
- expect(deref(globalState).search.results.loading).toBe(true);
221
+ expect(globalState.getState().search.results.loading).toBe(true);
221
222
  });
222
223
 
223
224
  it('immediately resets with loading false if no value is given implicitly', () => {
224
225
  state.search.input = '';
225
- const globalState = Atom.of(state as any);
226
+ const globalState: any = create(() => state);
226
227
  const ctx = createActions(globalState, createListener({}));
227
228
  ctx.defineActions(createSearchActions());
228
229
  ctx.triggerSearch();
229
- expect(deref(globalState).search.results.loading).toBe(false);
230
+ expect(globalState.getState().search.results.loading).toBe(false);
230
231
  });
231
232
 
232
233
  it('immediately resets with loading false if no value is given explicitly', () => {
233
- const gs = Atom.of(state as any);
234
+ const gs: any = create(() => state);
234
235
  const ctx = createActions(gs, createListener({}));
235
236
  ctx.defineActions(createSearchActions());
236
237
  const dispose = ctx.triggerSearch('');
237
238
  dispose();
238
- expect(deref(gs).search.results.loading).toBe(false);
239
+ expect(gs.getState().search.results.loading).toBe(false);
239
240
  });
240
241
 
241
242
  it('immediately resets with loading false if no value is given explicitly though immediate', () => {
242
- const gs = Atom.of(state as any);
243
+ const gs: any = create(() => state);
243
244
  const ctx = createActions(gs, createListener({}));
244
245
  ctx.defineActions(createSearchActions());
245
246
  const dispose = ctx.triggerSearch('', true);
246
247
  dispose();
247
- expect(deref(gs).search.results.loading).toBe(false);
248
+ expect(gs.getState().search.results.loading).toBe(false);
248
249
  });
249
250
 
250
251
  it('resets with loading true if no value is given explicitly', () => {
@@ -256,11 +257,11 @@ describe('Search Action Module', () => {
256
257
  clear() {},
257
258
  cancel() {},
258
259
  };
259
- const gs = Atom.of(state as any);
260
+ const gs: any = create(() => state);
260
261
  const ctx = createActions(gs, createListener({}));
261
262
  ctx.defineActions(createSearchActions());
262
263
  ctx.triggerSearch('foo');
263
- expect(deref(gs).search.results.loading).toBe(true);
264
+ expect(gs.getState().search.results.loading).toBe(true);
264
265
  });
265
266
 
266
267
  it('walks over all search providers', () => {
@@ -272,7 +273,7 @@ describe('Search Action Module', () => {
272
273
  foo: { search, clear, cancel },
273
274
  bar: { search, clear, cancel },
274
275
  };
275
- const gs = Atom.of(state as any);
276
+ const gs: any = create(() => state);
276
277
  const ctx = createActions(gs, createListener({}));
277
278
  ctx.defineActions(createSearchActions());
278
279
  ctx.triggerSearch();
@@ -290,7 +291,7 @@ describe('Search Action Module', () => {
290
291
  cancel() {},
291
292
  },
292
293
  };
293
- const gs = Atom.of(state as any);
294
+ const gs: any = create(() => state);
294
295
  const ctx = createActions(gs, createListener({}));
295
296
  ctx.defineActions(createSearchActions());
296
297
  ctx.triggerSearch();
@@ -309,7 +310,7 @@ describe('Search Action Module', () => {
309
310
  cancel,
310
311
  },
311
312
  };
312
- const gs = Atom.of(state as any);
313
+ const gs: any = create(() => state);
313
314
  const ctx = createActions(gs, createListener({}));
314
315
  ctx.defineActions(createSearchActions());
315
316
  ctx.triggerSearch();
@@ -329,7 +330,7 @@ describe('Search Action Module', () => {
329
330
  cancel,
330
331
  },
331
332
  };
332
- const gs = Atom.of(state as any);
333
+ const gs: any = create(() => state);
333
334
  const ctx = createActions(gs, createListener({}));
334
335
  ctx.defineActions(createSearchActions());
335
336
  const dispose = ctx.triggerSearch();
@@ -350,7 +351,7 @@ describe('Search Action Module', () => {
350
351
  cancel() {},
351
352
  },
352
353
  };
353
- const gs = Atom.of(state as any);
354
+ const gs: any = create(() => state);
354
355
  const ctx = createActions(gs, createListener({}));
355
356
  ctx.defineActions(createSearchActions());
356
357
  ctx.triggerSearch();
@@ -359,17 +360,17 @@ describe('Search Action Module', () => {
359
360
  });
360
361
 
361
362
  it('registerSearchProvider and unregisterSearchProvider', () => {
362
- const state = Atom.of({
363
+ const state: any = create(() => ({
363
364
  foo: 5,
364
365
  registry: {
365
366
  foo: 5,
366
367
  searchProviders: {},
367
368
  },
368
- });
369
+ }));
369
370
  const ctx = createActions(state, createListener({}));
370
371
  ctx.defineActions(createSearchActions());
371
372
  ctx.registerSearchProvider('foo', 10 as any);
372
- expect(deref(state)).toEqual({
373
+ expect(state.getState()).toEqual({
373
374
  foo: 5,
374
375
  registry: {
375
376
  foo: 5,
@@ -379,7 +380,7 @@ describe('Search Action Module', () => {
379
380
  },
380
381
  });
381
382
  ctx.unregisterSearchProvider('foo');
382
- expect(deref(state)).toEqual({
383
+ expect(state.getState()).toEqual({
383
384
  foo: 5,
384
385
  registry: {
385
386
  foo: 5,
@@ -1,7 +1,5 @@
1
- import * as React from 'react';
2
1
  import { getPiralComponent } from 'piral-core';
3
- import { SearchContainerProps, SearchInputProps, SearchResultProps } from './types';
4
2
 
5
- export const PiralSearchContainer: React.ComponentType<SearchContainerProps> = getPiralComponent('SearchContainer');
6
- export const PiralSearchInput: React.ComponentType<SearchInputProps> = getPiralComponent('SearchInput');
7
- export const PiralSearchResult: React.ComponentType<SearchResultProps> = getPiralComponent('SearchResult');
3
+ export const PiralSearchContainer = getPiralComponent('SearchContainer');
4
+ export const PiralSearchInput = getPiralComponent('SearchInput');
5
+ export const PiralSearchResult = getPiralComponent('SearchResult');
@@ -1,8 +1,13 @@
1
- import { Atom, swap } from '@dbeining/react-atom';
1
+ import create from 'zustand';
2
2
  import { createSearchApi } from './create';
3
3
 
4
4
  function createMockContainer() {
5
- const state = Atom.of({});
5
+ const state = create(() => ({
6
+ registry: {
7
+ wrappers: {},
8
+ extensions: {},
9
+ },
10
+ }));
6
11
  return {
7
12
  context: {
8
13
  on: jest.fn(),
@@ -10,11 +15,18 @@ function createMockContainer() {
10
15
  emit: jest.fn(),
11
16
  defineActions() {},
12
17
  state,
18
+ readState(cb) {
19
+ return cb(state.getState());
20
+ },
13
21
  dispatch(update) {
14
- swap(state, update);
22
+ state.setState(update(state.getState()));
23
+ },
24
+ } as any,
25
+ api: {
26
+ meta: {
27
+ name: '',
15
28
  },
16
29
  } as any,
17
- api: {} as any,
18
30
  };
19
31
  }
20
32
 
package/src/create.ts CHANGED
@@ -1,8 +1,20 @@
1
- import { isfunc } from 'piral-base';
2
1
  import { ReactChild, isValidElement, createElement } from 'react';
3
- import { buildName, PiralPlugin, Dict, withApi, PiletApi, GlobalStateContext } from 'piral-core';
2
+ import {
3
+ isfunc,
4
+ buildName,
5
+ PiralPlugin,
6
+ Dict,
7
+ withApi,
8
+ PiletApi,
9
+ GlobalStateContext,
10
+ withAll,
11
+ GlobalState,
12
+ withRootExtension,
13
+ } from 'piral-core';
4
14
  import { createActions } from './actions';
5
15
  import { DefaultContainer, DefaultInput, DefaultResult } from './default';
16
+ import { Search } from './Search';
17
+ import { SearchInput } from './SearchInput';
6
18
  import {
7
19
  PiletSearchApi,
8
20
  SearchSettings,
@@ -86,7 +98,7 @@ function getSearchProviders(providers: Array<InitialSearchProvider>) {
86
98
  }
87
99
 
88
100
  function toChild(content: SearchResultType, api: PiletApi, context: GlobalStateContext): ReactChild {
89
- if (typeof content === 'string' || isValidElement(content)) {
101
+ if (typeof content === 'string' || isValidElement<any>(content)) {
90
102
  return content;
91
103
  } else {
92
104
  const component = withApi(context, content, api, 'extension');
@@ -103,6 +115,29 @@ function wrapResults(
103
115
  return results.map((item) => toChild(item, api, context));
104
116
  }
105
117
 
118
+ function withSearch(searchProviders: Dict<SearchProviderRegistration>, query: string, items: Array<ReactChild>) {
119
+ return (state: GlobalState): GlobalState => ({
120
+ ...state,
121
+ components: {
122
+ SearchContainer: DefaultContainer,
123
+ SearchInput: DefaultInput,
124
+ SearchResult: DefaultResult,
125
+ ...state.components,
126
+ },
127
+ registry: {
128
+ ...state.registry,
129
+ searchProviders,
130
+ },
131
+ search: {
132
+ input: query,
133
+ results: {
134
+ loading: false,
135
+ items,
136
+ },
137
+ },
138
+ });
139
+ }
140
+
106
141
  /**
107
142
  * Creates new Pilet API extensions for search and filtering.
108
143
  */
@@ -112,26 +147,13 @@ export function createSearchApi(config: SearchConfig = {}): PiralPlugin<PiletSea
112
147
  return (context) => {
113
148
  context.defineActions(createActions(actionConfig));
114
149
 
115
- context.dispatch((state) => ({
116
- ...state,
117
- components: {
118
- SearchContainer: DefaultContainer,
119
- SearchInput: DefaultInput,
120
- SearchResult: DefaultResult,
121
- ...state.components,
122
- },
123
- registry: {
124
- ...state.registry,
125
- searchProviders: getSearchProviders(providers),
126
- },
127
- search: {
128
- input: query,
129
- results: {
130
- loading: false,
131
- items: results,
132
- },
133
- },
134
- }));
150
+ context.dispatch(
151
+ withAll(
152
+ withSearch(getSearchProviders(providers), query, results),
153
+ withRootExtension('piral-search', Search),
154
+ withRootExtension('piral-search-input', SearchInput),
155
+ ),
156
+ );
135
157
 
136
158
  return (api, target) => {
137
159
  const pilet = target.name;
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { ReactChild, ComponentType, ReactElement } from 'react';
2
- import {
1
+ import type { ReactChild, ComponentType, ReactElement, ReactNode } from 'react';
2
+ import type {
3
3
  Dict,
4
4
  Disposable,
5
5
  PiletApi,
@@ -90,6 +90,10 @@ export interface SearchContainerProps {
90
90
  * Gets if the results are still gathered.
91
91
  */
92
92
  loading: boolean;
93
+ /**
94
+ * The search to display.
95
+ */
96
+ children?: ReactNode;
93
97
  }
94
98
 
95
99
  export interface SearchInputProps {
@@ -99,7 +103,12 @@ export interface SearchInputProps {
99
103
 
100
104
  export interface SearchResultComponentProps extends BaseComponentProps {}
101
105
 
102
- export interface SearchResultProps {}
106
+ export interface SearchResultProps {
107
+ /**
108
+ * The search results to display.
109
+ */
110
+ children?: ReactNode;
111
+ }
103
112
 
104
113
  export type SearchResultType = string | ReactElement<any> | AnyComponent<SearchResultComponentProps>;
105
114
 
@@ -0,0 +1,67 @@
1
+ import * as React from 'react';
2
+ import { useDebounce } from './useDebounce';
3
+
4
+ jest.mock('react');
5
+
6
+ describe('Debounce Hook Module', () => {
7
+ it('just returns initial value if nothing has been changed', () => {
8
+ const usedEffect = jest.fn();
9
+ const usedState = jest.fn((value) => [value]);
10
+ (React as any).useState = usedState;
11
+ (React as any).useEffect = usedEffect;
12
+ const result = useDebounce('foo');
13
+ expect(usedEffect).toHaveBeenCalled();
14
+ expect(usedState).toHaveBeenCalled();
15
+ expect(result).toBe('foo');
16
+ });
17
+
18
+ it('invokes useEffect immediately, but does not set value immediately', () => {
19
+ const usedEffect = jest.fn((fn) => fn());
20
+ const setValue = jest.fn();
21
+ const usedState = jest.fn((value) => [value, setValue]);
22
+ (React as any).useState = usedState;
23
+ (React as any).useEffect = usedEffect;
24
+ useDebounce('foo');
25
+ expect(setValue).not.toHaveBeenCalled();
26
+ });
27
+
28
+ it('invokes useEffect immediately, but sets value immediately if 0', () => {
29
+ jest.useFakeTimers();
30
+ const usedEffect = jest.fn((fn) => fn());
31
+ const setValue = jest.fn();
32
+ const usedState = jest.fn((value) => [value, setValue]);
33
+ (React as any).useState = usedState;
34
+ (React as any).useEffect = usedEffect;
35
+ useDebounce('foo', 0);
36
+ jest.advanceTimersByTime(0);
37
+ expect(setValue).toHaveBeenCalled();
38
+ });
39
+
40
+ it('invokes useEffect immediately, but sets value after wait time', () => {
41
+ jest.useFakeTimers();
42
+ const usedEffect = jest.fn((fn) => fn());
43
+ const setValue = jest.fn();
44
+ const usedState = jest.fn((value) => [value, setValue]);
45
+ (React as any).useState = usedState;
46
+ (React as any).useEffect = usedEffect;
47
+ expect(setValue).not.toHaveBeenCalled();
48
+ useDebounce('foo', 300);
49
+ jest.advanceTimersByTime(300);
50
+ expect(setValue).toHaveBeenCalled();
51
+ });
52
+
53
+ it('invokes useEffect immediately and resets timer if needed', () => {
54
+ jest.useFakeTimers();
55
+ const usedEffect = jest.fn((fn) => fn());
56
+ const setValue = jest.fn();
57
+ const usedState = jest.fn((value) => [value, setValue]);
58
+ (React as any).useState = usedState;
59
+ (React as any).useEffect = usedEffect;
60
+ expect(setValue).not.toHaveBeenCalled();
61
+ useDebounce('foo', 300);
62
+ jest.advanceTimersByTime(250);
63
+ usedEffect.mock.results[0].value();
64
+ jest.advanceTimersByTime(50);
65
+ expect(setValue).not.toHaveBeenCalled();
66
+ });
67
+ });
@@ -0,0 +1,19 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ /**
4
+ * Hook that returns the debounced (i.e., delayed) value.
5
+ * Useful when user input should not fire immediately, but rather
6
+ * only after a certain timespan without any changes passed.
7
+ * @param value The value to consider.
8
+ * @param delay The timespan to pass before applying the value.
9
+ */
10
+ export function useDebounce<T>(value: T, delay = 300) {
11
+ const [debouncedValue, setDebouncedValue] = useState(value);
12
+
13
+ useEffect(() => {
14
+ const handler = setTimeout(() => setDebouncedValue(value), delay);
15
+ return () => clearTimeout(handler);
16
+ }, [value, delay]);
17
+
18
+ return debouncedValue;
19
+ }
@@ -1,6 +1,4 @@
1
1
  import * as React from 'react';
2
- import * as piralCore from 'piral-core';
3
- import { useSearch } from './useSearch';
4
2
 
5
3
  const state = {
6
4
  search: {
@@ -11,19 +9,28 @@ const state = {
11
9
  },
12
10
  };
13
11
 
14
- (piralCore as any).useGlobalState = (select: any) => select(state);
15
-
16
- (piralCore as any).useDebounce = (value) => value;
17
-
18
12
  const availableActions = {
19
13
  setSearchInput: jest.fn(),
20
14
  triggerSearch: jest.fn(),
21
15
  };
22
16
 
23
- (piralCore as any).useActions = () => availableActions;
17
+ const useGlobalState = jest.fn((select: any) => select(state));
18
+ const useActions = jest.fn(() => availableActions);
19
+ const useDebounce = jest.fn((value) => value);
20
+
21
+ jest.mock('piral-core', () => ({
22
+ ...jest.requireActual('piral-core'),
23
+ useGlobalState,
24
+ useActions,
25
+ }));
26
+
27
+ jest.mock('./useDebounce.ts', () => ({
28
+ useDebounce,
29
+ }));
24
30
 
25
31
  describe('Search Hook Module', () => {
26
32
  it('just returns current input value', () => {
33
+ const { useSearch } = require('./useSearch');
27
34
  const usedEffect = jest.fn();
28
35
  (React as any).useEffect = usedEffect;
29
36
  (React as any).useRef = (current) => ({ current });
@@ -32,6 +39,7 @@ describe('Search Hook Module', () => {
32
39
  });
33
40
 
34
41
  it('sets the value using the action', () => {
42
+ const { useSearch } = require('./useSearch');
35
43
  const usedEffect = jest.fn();
36
44
  (React as any).useEffect = usedEffect;
37
45
  (React as any).useRef = (current) => ({ current });
@@ -42,6 +50,7 @@ describe('Search Hook Module', () => {
42
50
  });
43
51
 
44
52
  it('triggers the search without immediate mode', () => {
53
+ const { useSearch } = require('./useSearch');
45
54
  const usedEffect = jest.fn((fn) => fn());
46
55
  (React as any).useEffect = usedEffect;
47
56
  (React as any).useRef = (current) => ({ current });
@@ -51,6 +60,7 @@ describe('Search Hook Module', () => {
51
60
  });
52
61
 
53
62
  it('cancels the current search', () => {
63
+ const { useSearch } = require('./useSearch');
54
64
  const usedEffect = jest.fn((fn) => fn());
55
65
  const cancel = jest.fn();
56
66
  (React as any).useEffect = usedEffect;
package/src/useSearch.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { useEffect, useRef } from 'react';
2
- import { useGlobalState, useDebounce, useActions, Disposable } from 'piral-core';
2
+ import { useGlobalState, useActions, Disposable } from 'piral-core';
3
+ import { useDebounce } from './useDebounce';
3
4
 
4
5
  /**
5
6
  * Hook that yields the possibility of searching in Piral.