@inseefr/lunatic 3.8.0 → 3.8.1-rc.1

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 (34) hide show
  1. package/components/Suggester/Suggester.js +23 -8
  2. package/components/Suggester/Suggester.js.map +1 -1
  3. package/components/Suggester/Suggester.spec.js +75 -25
  4. package/components/Suggester/Suggester.spec.js.map +1 -1
  5. package/components/Suggester/useSuggestions.d.ts +21 -2
  6. package/components/Suggester/useSuggestions.js +26 -12
  7. package/components/Suggester/useSuggestions.js.map +1 -1
  8. package/esm/components/Suggester/Suggester.js +25 -9
  9. package/esm/components/Suggester/Suggester.js.map +1 -1
  10. package/esm/components/Suggester/Suggester.spec.js +78 -25
  11. package/esm/components/Suggester/Suggester.spec.js.map +1 -1
  12. package/esm/components/Suggester/useSuggestions.d.ts +21 -2
  13. package/esm/components/Suggester/useSuggestions.js +24 -10
  14. package/esm/components/Suggester/useSuggestions.js.map +1 -1
  15. package/esm/utils/search/SearchInterface.d.ts +1 -0
  16. package/esm/utils/search/SearchMinisearch.d.ts +1 -0
  17. package/esm/utils/search/SearchMinisearch.js +6 -0
  18. package/esm/utils/search/SearchMinisearch.js.map +1 -1
  19. package/esm/utils/search/SuggestersDatabase.d.ts +7 -0
  20. package/esm/utils/search/SuggestersDatabase.js.map +1 -1
  21. package/package.json +1 -1
  22. package/src/components/Suggester/Suggester.spec.tsx +93 -26
  23. package/src/components/Suggester/Suggester.tsx +25 -9
  24. package/src/components/Suggester/useSuggestions.ts +39 -18
  25. package/src/utils/search/SearchInterface.ts +2 -0
  26. package/src/utils/search/SearchMinisearch.ts +7 -0
  27. package/src/utils/search/SuggestersDatabase.ts +10 -0
  28. package/tsconfig.build.tsbuildinfo +1 -1
  29. package/utils/search/SearchInterface.d.ts +1 -0
  30. package/utils/search/SearchMinisearch.d.ts +1 -0
  31. package/utils/search/SearchMinisearch.js +6 -0
  32. package/utils/search/SearchMinisearch.js.map +1 -1
  33. package/utils/search/SuggestersDatabase.d.ts +7 -0
  34. package/utils/search/SuggestersDatabase.js.map +1 -1
@@ -1,12 +1,31 @@
1
1
  import type { SuggesterOptionType } from './SuggesterType';
2
+ import { SearchStore } from '../../utils/search/SuggestersDatabase';
2
3
  type Props = {
3
- storeName: string;
4
+ store: SearchStore;
5
+ storeState: State;
6
+ setStoreState: (s: State) => any;
4
7
  selectedOptions: SuggesterOptionType[];
5
8
  allowArbitrary: boolean;
6
9
  };
7
10
  export declare const OTHER_VALUE = "OTHER";
8
11
  type State = 'success' | 'loading' | 'error';
9
- export declare function useSuggestions({ storeName, selectedOptions, allowArbitrary, }: Props): {
12
+ export declare function useStore({ storeName }: {
13
+ storeName: string;
14
+ }): {
15
+ store: {
16
+ error: string;
17
+ search?: undefined;
18
+ index?: undefined;
19
+ } | {
20
+ error: null;
21
+ search: import("../../utils/search/SearchInterface").SearchInterface<import("../../utils/search/SearchInterface").IndexEntry>;
22
+ index: () => Promise<void>;
23
+ };
24
+ storeState: State;
25
+ setStoreState: import("react").Dispatch<import("react").SetStateAction<State>>;
26
+ getLabelById: (id: any) => string;
27
+ };
28
+ export declare function useSuggestions({ store, storeState: state, setStoreState: setState, selectedOptions, allowArbitrary, }: Props): {
10
29
  search: string;
11
30
  setSearch: (s: string) => void;
12
31
  state: State;
@@ -1,17 +1,14 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import { useEffectDebounced } from '../../hooks/useDebounce';
3
- import { getSearchForStore } from '../../utils/search/SuggestersDatabase';
3
+ import { getSearchForStore, } from '../../utils/search/SuggestersDatabase';
4
4
  import { useRefSync } from '../../hooks/useRefSync';
5
5
  export const OTHER_VALUE = 'OTHER';
6
- export function useSuggestions({ storeName, selectedOptions, allowArbitrary, }) {
7
- const [searchQuery, setSearchQuery] = useState('');
6
+ export function useStore({ storeName }) {
8
7
  const store = getSearchForStore(storeName);
9
8
  const searchIndexRef = useRefSync(store.index);
10
- // eslint-disable-next-line prefer-const
11
- let [options, setOptions] = useState(selectedOptions);
12
9
  const [state, setState] = useState(store.error !== null
13
10
  ? 'error'
14
- : store.search.isIndexed()
11
+ : (store === null || store === void 0 ? void 0 : store.search.isIndexed())
15
12
  ? 'success'
16
13
  : 'loading');
17
14
  // Index the data when the component is loaded
@@ -28,16 +25,33 @@ export function useSuggestions({ storeName, selectedOptions, allowArbitrary, })
28
25
  setState('error');
29
26
  });
30
27
  }, [searchIndexRef]);
28
+ return {
29
+ store,
30
+ storeState: state,
31
+ setStoreState: setState,
32
+ getLabelById: (id) => {
33
+ var _a, _b, _c;
34
+ if (!id)
35
+ return '';
36
+ return (_c = (_b = (_a = store.search) === null || _a === void 0 ? void 0 : _a.getFieldsById(id)) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : '';
37
+ },
38
+ };
39
+ }
40
+ export function useSuggestions({ store, storeState: state, setStoreState: setState, selectedOptions, allowArbitrary, }) {
41
+ const [searchQuery, setSearchQuery] = useState('');
42
+ // eslint-disable-next-line prefer-const
43
+ let [options, setOptions] = useState(selectedOptions);
31
44
  useEffectDebounced(() => {
32
45
  var _a;
33
46
  // Do not reset search for empty search
34
47
  if (!searchQuery) {
35
48
  return;
36
49
  }
37
- (_a = store.search) === null || _a === void 0 ? void 0 : _a.search(searchQuery).then((r) => {
38
- setOptions(r);
39
- setState('success');
40
- }).catch(() => setState('error'));
50
+ if (store.error === null)
51
+ (_a = store.search) === null || _a === void 0 ? void 0 : _a.search(searchQuery).then((r) => {
52
+ setOptions(r);
53
+ setState('success');
54
+ }).catch(() => setState('error'));
41
55
  }, [searchQuery], 300);
42
56
  if (searchQuery &&
43
57
  allowArbitrary &&
@@ -1 +1 @@
1
- {"version":3,"file":"useSuggestions.js","sourceRoot":"","sources":["../../../src/components/Suggester/useSuggestions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSuggestions.js","sourceRoot":"","sources":["../../../src/components/Suggester/useSuggestions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACN,iBAAiB,GAEjB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAUpD,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC;AAInC,MAAM,UAAU,QAAQ,CAAC,EAAE,SAAS,EAAyB;IAC5D,MAAM,KAAK,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC3C,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CACjC,KAAK,CAAC,KAAK,KAAK,IAAI;QACnB,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,MAAM,CAAC,SAAS,EAAE;YAC1B,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,SAAS,CACb,CAAC;IAEF,8CAA8C;IAC9C,SAAS,CAAC,GAAG,EAAE;QACd,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;YAC7B,OAAO;QACR,CAAC;QACD,cAAc;aACZ,OAAO,EAAE;aACT,IAAI,CAAC,GAAG,EAAE;YACV,QAAQ,CAAC,SAAS,CAAC,CAAC;QACrB,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACX,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;IAErB,OAAO;QACN,KAAK;QACL,UAAU,EAAE,KAAK;QACjB,aAAa,EAAE,QAAQ;QACvB,YAAY,EAAE,CAAC,EAAO,EAAE,EAAE;;YACzB,IAAI,CAAC,EAAE;gBAAE,OAAO,EAAE,CAAC;YACnB,OAAO,MAAA,MAAA,MAAA,KAAK,CAAC,MAAM,0CAAE,aAAa,CAAC,EAAE,CAAC,0CAAE,KAAK,mCAAI,EAAE,CAAC;QACrD,CAAC;KACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAC9B,KAAK,EACL,UAAU,EAAE,KAAK,EACjB,aAAa,EAAE,QAAQ,EACvB,eAAe,EACf,cAAc,GACP;IACP,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACnD,wCAAwC;IACxC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;IAEtD,kBAAkB,CACjB,GAAG,EAAE;;QACJ,uCAAuC;QACvC,IAAI,CAAC,WAAW,EAAE,CAAC;YAClB,OAAO;QACR,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI;YACvB,MAAA,KAAK,CAAC,MAAM,0CACT,MAAM,CAAC,WAAW,EACnB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;gBACX,UAAU,CAAC,CAAC,CAAC,CAAC;gBACd,QAAQ,CAAC,SAAS,CAAC,CAAC;YACrB,CAAC,EACA,KAAK,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACnC,CAAC,EACD,CAAC,WAAW,CAAC,EACb,GAAG,CACH,CAAC;IAEF,IACC,WAAW;QACX,cAAc;QACd,OAAO,CAAC,MAAM,KAAK,CAAC;QACpB,KAAK,KAAK,SAAS,EAClB,CAAC;QACF,OAAO,GAAG;YACT;gBACC,EAAE,EAAE,WAAW;gBACf,KAAK,EAAE,WAAW;gBAClB,KAAK,EAAE,WAAW;aAClB;SACD,CAAC;IACH,CAAC;IAED,iIAAiI;IACjI,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEhD,OAAO;QACN,MAAM,EAAE,WAAW;QACnB,SAAS,EAAE,CAAC,CAAS,EAAE,EAAE;YACxB,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBAChC,UAAU,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;YACD,qGAAqG;YACrG,cAAc,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;QACD,KAAK;QACL,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;QAC9C,MAAM,EAAE,GAAG,EAAE;YACZ,UAAU,CAAC,KAAK,CAAC,CAAC;YAClB,cAAc,CAAC,EAAE,CAAC,CAAC;YACnB,sBAAsB;YACtB,IAAI,CAAC,SAAS,EAAE,CAAC;gBAChB,OAAO;YACR,CAAC;YACD,UAAU,CAAC,eAAe,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,EAAE,GAAG,EAAE;;YACb,IAAI,CAAC,SAAS,EAAE,CAAC;gBAChB,UAAU,CAAC,IAAI,CAAC,CAAC;gBACjB,cAAc,CAAC,MAAA,MAAA,eAAe,CAAC,CAAC,CAAC,0CAAE,KAAK,mCAAI,EAAE,CAAC,CAAC;YACjD,CAAC;QACF,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -9,4 +9,5 @@ export interface SearchInterface<T extends IndexEntry> {
9
9
  isIndexed(): boolean;
10
10
  index(data: T[]): Promise<void>;
11
11
  search(q: string): Promise<T[]>;
12
+ getFieldsById(id: any): T;
12
13
  }
@@ -8,4 +8,5 @@ export declare class SearchMinisearch<T extends IndexEntry> implements SearchInt
8
8
  isIndexed(): boolean;
9
9
  index(data: T[]): Promise<void>;
10
10
  search(q: string): Promise<T[]>;
11
+ getFieldsById(id: any): T;
11
12
  }
@@ -35,5 +35,11 @@ export class SearchMinisearch {
35
35
  data = data.slice(0, this.info.max);
36
36
  return data;
37
37
  }
38
+ getFieldsById(id) {
39
+ if (!this.db) {
40
+ return {};
41
+ }
42
+ return this.db.getStoredFields(id);
43
+ }
38
44
  }
39
45
  //# sourceMappingURL=SearchMinisearch.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SearchMinisearch.js","sourceRoot":"","sources":["../../../src/utils/search/SearchMinisearch.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,OAAO,gBAAgB;IAO5B,YAAY,IAAgB;QAJ5B,OAAE,GAAyB,IAAI,CAAC;QAEhC,YAAO,GAAG,KAAK,CAAC;QAGf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;IAED,SAAS;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAS;QACpB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QAC5D,IAAI,CAAC,EAAE,GAAG,IAAI,UAAU,CAAC;YACxB,MAAM,EAAE,UAAU;YAClB,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;YACxD,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;SAC9B,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,CAAS;QACrB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACxC,OAAO,EAAE,CAAC;QACX,CAAC;QACD,IAAI,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE;YAC5B,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;SACjC,CAAe,CAAC;QAEjB,oCAAoC;QACpC,IAAI,GAAG,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAE7B,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEpC,OAAO,IAAI,CAAC;IACb,CAAC;CACD"}
1
+ {"version":3,"file":"SearchMinisearch.js","sourceRoot":"","sources":["../../../src/utils/search/SearchMinisearch.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,OAAO,gBAAgB;IAO5B,YAAY,IAAgB;QAJ5B,OAAE,GAAyB,IAAI,CAAC;QAEhC,YAAO,GAAG,KAAK,CAAC;QAGf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;IAED,SAAS;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAS;QACpB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QAC5D,IAAI,CAAC,EAAE,GAAG,IAAI,UAAU,CAAC;YACxB,MAAM,EAAE,UAAU;YAClB,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;YACxD,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;SAC9B,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,CAAS;QACrB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACxC,OAAO,EAAE,CAAC;QACX,CAAC;QACD,IAAI,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE;YAC5B,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;SACjC,CAAe,CAAC;QAEjB,oCAAoC;QACpC,IAAI,GAAG,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAE7B,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEpC,OAAO,IAAI,CAAC;IACb,CAAC;IAED,aAAa,CAAC,EAAO;QACpB,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACd,OAAO,EAAO,CAAC;QAChB,CAAC;QACD,OAAO,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,CAAM,CAAC;IACzC,CAAC;CACD"}
@@ -4,6 +4,13 @@ import type { LunaticSource } from '../../use-lunatic/type';
4
4
  * This file retains a dictionary of all the search indexed by storeName
5
5
  */
6
6
  export declare function registerSuggesters(infos: LunaticSource['suggesters'], fetcher: (name: string) => Promise<Array<IndexEntry>>): void;
7
+ export type SearchStore = {
8
+ error: string;
9
+ } | {
10
+ error?: null;
11
+ search: SearchInterface<IndexEntry>;
12
+ index: () => Promise<void>;
13
+ };
7
14
  export declare function getSearchForStore(storeName: string): {
8
15
  error: string;
9
16
  search?: undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"SuggestersDatabase.js","sourceRoot":"","sources":["../../../src/utils/search/SuggestersDatabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAItD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAuC,CAAC;AAClE,6DAA6D;AAC7D,IAAI,WAAW,GAAG,CAAC,CAAS,EAA8B,EAAE,CAC3D,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAErB;;GAEG;AACH,MAAM,UAAU,kBAAkB,CACjC,KAAkC,EAClC,OAAqD;IAErD,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO;IACR,CAAC;IACD,WAAW,GAAG,OAAO,CAAC;IACtB,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC/B,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAa,SAAS,CAAC,CAAC,CAAC;IAC7E,CAAC;AACF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IAClD,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO;YACN,KAAK,EAAE,iCAAiC,GAAG,SAAS;SACpD,CAAC;IACH,CAAC;IACD,OAAO;QACN,KAAK,EAAE,IAAI;QACX,MAAM;QACN,KAAK,EAAE,GAAG,EAAE;YACX,IAAI,CAAC,MAAM,EAAE,CAAC;gBACb,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;YACzB,CAAC;YACD,IAAI,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC;gBACxB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;YAC1B,CAAC;YACD,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,CAAC;KACD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"SuggestersDatabase.js","sourceRoot":"","sources":["../../../src/utils/search/SuggestersDatabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAItD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAuC,CAAC;AAClE,6DAA6D;AAC7D,IAAI,WAAW,GAAG,CAAC,CAAS,EAA8B,EAAE,CAC3D,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAErB;;GAEG;AACH,MAAM,UAAU,kBAAkB,CACjC,KAAkC,EAClC,OAAqD;IAErD,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO;IACR,CAAC;IACD,WAAW,GAAG,OAAO,CAAC;IACtB,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC/B,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAa,SAAS,CAAC,CAAC,CAAC;IAC7E,CAAC;AACF,CAAC;AAYD,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IAClD,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO;YACN,KAAK,EAAE,iCAAiC,GAAG,SAAS;SACpD,CAAC;IACH,CAAC;IACD,OAAO;QACN,KAAK,EAAE,IAAI;QACX,MAAM;QACN,KAAK,EAAE,GAAG,EAAE;YACX,IAAI,CAAC,MAAM,EAAE,CAAC;gBACb,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;YACzB,CAAC;YACD,IAAI,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC;gBACxB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;YAC1B,CAAC;YACD,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,CAAC;KACD,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inseefr/lunatic",
3
- "version": "3.8.0",
3
+ "version": "3.8.1-rc.1",
4
4
  "description": "Library of questionnaire components",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,6 +1,6 @@
1
1
  import { render, screen } from '@testing-library/react';
2
2
  import { describe, it, expect, vi } from 'vitest';
3
- import { WrappedSuggester } from './Suggester'; // Ajuste le chemin selon ton projet
3
+ import { WrappedSuggester } from './Suggester';
4
4
 
5
5
  // Mock of useSuggestions
6
6
  vi.mock('./useSuggestions', () => ({
@@ -11,52 +11,119 @@ vi.mock('./useSuggestions', () => ({
11
11
  setSearch: vi.fn(),
12
12
  onFocus: vi.fn(),
13
13
  onBlur: vi.fn(),
14
+ getSelectedLabelById: vi.fn(),
15
+ })),
16
+
17
+ useStore: vi.fn(() => ({
18
+ store: {},
19
+ storeState: 'success',
20
+ setStoreState: vi.fn(),
21
+ getLabelById: vi.fn(),
14
22
  })),
15
23
  }));
16
24
 
25
+ const FAKE_PLACE_HOLDER = 'place holder...';
17
26
  // Mock of CustomSuggester
18
27
  vi.mock('./CustomSuggester', () => ({
19
28
  CustomSuggester: vi.fn(({ value }) => (
20
- <div data-testid="custom-suggester">{JSON.stringify(value)}</div>
29
+ <div data-testid="custom-suggester">
30
+ {Array.isArray(value) && value.length > 0
31
+ ? (value[0].value ?? FAKE_PLACE_HOLDER)
32
+ : FAKE_PLACE_HOLDER}
33
+ </div>
21
34
  )),
22
35
  }));
23
36
 
24
37
  describe('WrappedSuggester useEffect', () => {
25
- it('should update selectedOptions when value prop changes', () => {
26
- // Given initial props
27
- const initialProps = {
28
- storeName: 'store',
29
- id: 'suggester-1',
30
- className: '',
31
- handleChanges: vi.fn(),
32
- disabled: false,
33
- value: 'initialValue',
34
- label: 'Label',
35
- declarations: [],
36
- description: '',
37
- errors: {},
38
- readOnly: false,
39
- response: { name: 'response' },
40
- optionResponses: [{ name: 'labelResponse', attribute: 'label' }],
41
- executeExpression: vi.fn(),
42
- iteration: 1,
43
- arbitrary: { response: { name: 'ARBITRARY' } },
44
- optionRenderer: vi.fn(),
45
- labelRenderer: vi.fn(),
46
- focused: false,
47
- };
38
+ // Given initial props
39
+ const initialProps = {
40
+ storeName: 'store',
41
+ id: 'suggester-1',
42
+ className: '',
43
+ handleChanges: vi.fn(),
44
+ disabled: false,
45
+ value: 'initialValue',
46
+ label: 'Label',
47
+ declarations: [],
48
+ description: '',
49
+ errors: {},
50
+ readOnly: false,
51
+ response: { name: 'response' },
52
+ optionResponses: [{ name: 'labelResponse', attribute: 'label' }],
53
+ executeExpression: vi.fn(),
54
+ iteration: 1,
55
+ arbitrary: { response: { name: 'ARBITRARY' } },
56
+ optionRenderer: vi.fn(),
57
+ labelRenderer: vi.fn(),
58
+ focused: false,
59
+ };
60
+ it('should display place holder when no value is set', () => {
61
+ // Given the composant initialize
62
+
63
+ const { rerender } = render(
64
+ <WrappedSuggester {...initialProps} value={null} />
65
+ );
66
+ const suggesterValue = screen.getByTestId('custom-suggester').textContent;
67
+ expect(suggesterValue).toContain(FAKE_PLACE_HOLDER);
48
68
 
69
+ const forcedValue = 'FORCED value';
70
+
71
+ // When we change the value
72
+ const newProps = { ...initialProps, value: forcedValue };
73
+ rerender(<WrappedSuggester {...newProps} />);
74
+
75
+ // Then selectedOptions, i.e value props of customSuggester have to be updated
76
+ const updatedValue = screen.getByTestId('custom-suggester').textContent;
77
+ expect(updatedValue).toContain('FORCED value');
78
+ });
79
+
80
+ it('should update selectedOptions when value prop changes', () => {
49
81
  // Given the composant initialize
50
82
  const { rerender } = render(<WrappedSuggester {...initialProps} />);
51
83
  const suggesterValue = screen.getByTestId('custom-suggester').textContent;
52
84
  expect(suggesterValue).toContain('initialValue');
53
85
 
86
+ const forcedValue = 'FORCED value';
87
+
54
88
  // When we change the value
55
- const newProps = { ...initialProps, value: 'FORCED value' };
89
+ const newProps = { ...initialProps, value: forcedValue };
56
90
  rerender(<WrappedSuggester {...newProps} />);
57
91
 
58
92
  // Then selectedOptions, i.e value props of customSuggester have to be updated
59
93
  const updatedValue = screen.getByTestId('custom-suggester').textContent;
60
94
  expect(updatedValue).toContain('FORCED value');
61
95
  });
96
+
97
+ it('should update selectedOptions when value prop is set to null', () => {
98
+ // Given the composant initialize
99
+ const { rerender } = render(<WrappedSuggester {...initialProps} />);
100
+ const suggesterValue = screen.getByTestId('custom-suggester').textContent;
101
+ expect(suggesterValue).toContain('initialValue');
102
+
103
+ const forcedValue = null;
104
+
105
+ // When we change the value
106
+ const newProps = { ...initialProps, value: forcedValue };
107
+ rerender(<WrappedSuggester {...newProps} />);
108
+
109
+ // Then selectedOptions, i.e value props of customSuggester have to be updated
110
+ const updatedValue = screen.getByTestId('custom-suggester').textContent;
111
+ expect(updatedValue).toContain(FAKE_PLACE_HOLDER);
112
+ });
113
+
114
+ it('should update selectedOptions when value prop is set to an empty string ("")', () => {
115
+ // Given the composant initialize
116
+ const { rerender } = render(<WrappedSuggester {...initialProps} />);
117
+ const suggesterValue = screen.getByTestId('custom-suggester').textContent;
118
+ expect(suggesterValue).toContain('initialValue');
119
+
120
+ const forcedValue = '';
121
+ // When we change the value
122
+ const newProps = { ...initialProps, value: forcedValue };
123
+ rerender(<WrappedSuggester {...newProps} />);
124
+
125
+ // Then selectedOptions, i.e value props of customSuggester have to be updated
126
+ const updatedValue = screen.getByTestId('custom-suggester').textContent;
127
+ expect(updatedValue).toContain(FAKE_PLACE_HOLDER);
128
+ });
62
129
  });
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
2
2
  import type { LunaticComponentProps } from '../type';
3
3
  import { CustomSuggester } from './CustomSuggester';
4
4
  import { getComponentErrors } from '../shared/ComponentErrors/ComponentErrors';
5
- import { OTHER_VALUE, useSuggestions } from './useSuggestions';
5
+ import { OTHER_VALUE, useStore, useSuggestions } from './useSuggestions';
6
6
  import D from '../../i18n';
7
7
  import type { SuggesterOptionType } from './SuggesterType';
8
8
 
@@ -15,6 +15,8 @@ export function Suggester(props: LunaticComponentProps<'Suggester'>) {
15
15
  return <WrappedSuggester {...props} key={suggesterKey} />;
16
16
  }
17
17
 
18
+ const ARBITRARY_ID = 'OTHER';
19
+
18
20
  export function WrappedSuggester({
19
21
  storeName,
20
22
  id,
@@ -36,18 +38,22 @@ export function WrappedSuggester({
36
38
  arbitrary,
37
39
  arbitraryValue,
38
40
  }: LunaticComponentProps<'Suggester'>) {
41
+ const { store, storeState, setStoreState, getLabelById } = useStore({
42
+ storeName,
43
+ });
44
+
39
45
  // Default options should not change between render
40
46
  // so we can break the rule of hooks here
41
47
  const computeSelectedOptions = (): [SuggesterOptionType] | [] => {
42
48
  if (arbitraryValue) {
43
- return [{ id: 'OTHER', label: arbitraryValue, value: 'OTHER' }];
49
+ return [{ id: ARBITRARY_ID, label: arbitraryValue, value: ARBITRARY_ID }];
44
50
  }
45
51
  if (!value) {
46
52
  return [];
47
53
  }
48
54
  const labelResponse = optionResponses?.find((o) => o.attribute === 'label');
49
55
  if (!labelResponse) {
50
- return [{ id: value, label: value, value: value }];
56
+ return [{ id: value, label: getLabelById(value), value: value }];
51
57
  }
52
58
  const label = executeExpression(
53
59
  { value: labelResponse.name, type: 'VTL' },
@@ -73,7 +79,9 @@ export function WrappedSuggester({
73
79
 
74
80
  const { state, options, search, setSearch, onFocus, onBlur } = useSuggestions(
75
81
  {
76
- storeName: storeName,
82
+ store,
83
+ storeState,
84
+ setStoreState,
77
85
  allowArbitrary: !!arbitrary,
78
86
  selectedOptions: selectedOptions,
79
87
  }
@@ -110,7 +118,7 @@ export function WrappedSuggester({
110
118
  { name: response.name, value: null },
111
119
  ];
112
120
  // User chose an arbitrary option or clear the value
113
- if (arbitrary && arbitrary.response) {
121
+ if (arbitrary?.response) {
114
122
  newResponses.push({
115
123
  name: arbitrary.response.name,
116
124
  value: v?.id === OTHER_VALUE ? search : null,
@@ -145,11 +153,19 @@ export function WrappedSuggester({
145
153
  setSearch('');
146
154
  };
147
155
 
148
- // Fix display issue (when handleChanges is called outside this component (in management mode, return to FORCED value by example) )
149
- // We have to re-compute actual selection
150
156
  useEffect(() => {
151
- if (value) {
152
- setSelectedOptions(computeSelectedOptions());
157
+ // Fix display issue (when handleChanges is called outside this component (in management mode, return to FORCED value by example)
158
+ // "value" does'nt match selectedOption's "id"
159
+ if (selectedOptions[0]?.id !== value) {
160
+ const actualSelection = computeSelectedOptions();
161
+ const selectedOptionsWithLabel = actualSelection.map((selection) => {
162
+ if (selection.id === ARBITRARY_ID) return selection;
163
+ return {
164
+ ...selection,
165
+ label: getLabelById(selection.id),
166
+ };
167
+ }) as [SuggesterOptionType];
168
+ setSelectedOptions(selectedOptionsWithLabel);
153
169
  }
154
170
  // eslint-disable-next-line react-hooks/exhaustive-deps
155
171
  }, [value]);
@@ -1,11 +1,16 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import type { SuggesterOptionType } from './SuggesterType';
3
3
  import { useEffectDebounced } from '../../hooks/useDebounce';
4
- import { getSearchForStore } from '../../utils/search/SuggestersDatabase';
4
+ import {
5
+ getSearchForStore,
6
+ SearchStore,
7
+ } from '../../utils/search/SuggestersDatabase';
5
8
  import { useRefSync } from '../../hooks/useRefSync';
6
9
 
7
10
  type Props = {
8
- storeName: string;
11
+ store: SearchStore;
12
+ storeState: State;
13
+ setStoreState: (s: State) => any;
9
14
  selectedOptions: SuggesterOptionType[];
10
15
  allowArbitrary: boolean;
11
16
  };
@@ -14,20 +19,13 @@ export const OTHER_VALUE = 'OTHER';
14
19
 
15
20
  type State = 'success' | 'loading' | 'error';
16
21
 
17
- export function useSuggestions({
18
- storeName,
19
- selectedOptions,
20
- allowArbitrary,
21
- }: Props) {
22
- const [searchQuery, setSearchQuery] = useState('');
22
+ export function useStore({ storeName }: { storeName: string }) {
23
23
  const store = getSearchForStore(storeName);
24
24
  const searchIndexRef = useRefSync(store.index);
25
- // eslint-disable-next-line prefer-const
26
- let [options, setOptions] = useState(selectedOptions);
27
25
  const [state, setState] = useState<State>(
28
26
  store.error !== null
29
27
  ? 'error'
30
- : store.search.isIndexed()
28
+ : store?.search.isIndexed()
31
29
  ? 'success'
32
30
  : 'loading'
33
31
  );
@@ -47,19 +45,42 @@ export function useSuggestions({
47
45
  });
48
46
  }, [searchIndexRef]);
49
47
 
48
+ return {
49
+ store,
50
+ storeState: state,
51
+ setStoreState: setState,
52
+ getLabelById: (id: any) => {
53
+ if (!id) return '';
54
+ return store.search?.getFieldsById(id)?.label ?? '';
55
+ },
56
+ };
57
+ }
58
+
59
+ export function useSuggestions({
60
+ store,
61
+ storeState: state,
62
+ setStoreState: setState,
63
+ selectedOptions,
64
+ allowArbitrary,
65
+ }: Props) {
66
+ const [searchQuery, setSearchQuery] = useState('');
67
+ // eslint-disable-next-line prefer-const
68
+ let [options, setOptions] = useState(selectedOptions);
69
+
50
70
  useEffectDebounced(
51
71
  () => {
52
72
  // Do not reset search for empty search
53
73
  if (!searchQuery) {
54
74
  return;
55
75
  }
56
- store.search
57
- ?.search(searchQuery)
58
- .then((r) => {
59
- setOptions(r);
60
- setState('success');
61
- })
62
- .catch(() => setState('error'));
76
+ if (store.error === null)
77
+ store.search
78
+ ?.search(searchQuery)
79
+ .then((r) => {
80
+ setOptions(r);
81
+ setState('success');
82
+ })
83
+ .catch(() => setState('error'));
63
84
  },
64
85
  [searchQuery],
65
86
  300
@@ -14,4 +14,6 @@ export interface SearchInterface<T extends IndexEntry> {
14
14
  index(data: T[]): Promise<void>;
15
15
 
16
16
  search(q: string): Promise<T[]>;
17
+
18
+ getFieldsById(id: any): T;
17
19
  }
@@ -51,4 +51,11 @@ export class SearchMinisearch<T extends IndexEntry>
51
51
 
52
52
  return data;
53
53
  }
54
+
55
+ getFieldsById(id: any) {
56
+ if (!this.db) {
57
+ return {} as T;
58
+ }
59
+ return this.db.getStoredFields(id) as T;
60
+ }
54
61
  }
@@ -23,6 +23,16 @@ export function registerSuggesters(
23
23
  }
24
24
  }
25
25
 
26
+ export type SearchStore =
27
+ | {
28
+ error: string;
29
+ }
30
+ | {
31
+ error?: null;
32
+ search: SearchInterface<IndexEntry>;
33
+ index: () => Promise<void>;
34
+ };
35
+
26
36
  export function getSearchForStore(storeName: string) {
27
37
  const search = suggesters.get(storeName);
28
38
  if (!search) {