@inseefr/lunatic 3.7.6-rc.alphanumeric-sorting.1 → 3.7.7-rc.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 (46) hide show
  1. package/components/Suggester/CustomSuggester.js +1 -1
  2. package/components/Suggester/CustomSuggester.js.map +1 -1
  3. package/components/Suggester/Suggester.js +11 -2
  4. package/components/Suggester/Suggester.js.map +1 -1
  5. package/components/shared/Combobox/Combobox.js +2 -1
  6. package/components/shared/Combobox/Combobox.js.map +1 -1
  7. package/esm/components/Suggester/CustomSuggester.js +1 -1
  8. package/esm/components/Suggester/CustomSuggester.js.map +1 -1
  9. package/esm/components/Suggester/Suggester.js +12 -3
  10. package/esm/components/Suggester/Suggester.js.map +1 -1
  11. package/esm/components/shared/Combobox/Combobox.js +2 -1
  12. package/esm/components/shared/Combobox/Combobox.js.map +1 -1
  13. package/esm/i18n/dictionary.d.ts +4 -0
  14. package/esm/i18n/dictionary.js +5 -1
  15. package/esm/i18n/dictionary.js.map +1 -1
  16. package/esm/i18n/index.d.ts +1 -1
  17. package/esm/use-lunatic/commons/page.js +53 -2
  18. package/esm/use-lunatic/commons/page.js.map +1 -1
  19. package/esm/use-lunatic/reducer/commons/auto-explore-loop.js +1 -1
  20. package/esm/use-lunatic/reducer/commons/auto-explore-loop.js.map +1 -1
  21. package/esm/utils/search/tokenizer.js +6 -3
  22. package/esm/utils/search/tokenizer.js.map +1 -1
  23. package/esm/utils/search/tokenizer.spec.js +10 -0
  24. package/esm/utils/search/tokenizer.spec.js.map +1 -1
  25. package/i18n/dictionary.d.ts +4 -0
  26. package/i18n/dictionary.js +5 -1
  27. package/i18n/dictionary.js.map +1 -1
  28. package/i18n/index.d.ts +1 -1
  29. package/package.json +1 -1
  30. package/src/components/Suggester/CustomSuggester.tsx +1 -1
  31. package/src/components/Suggester/Suggester.tsx +16 -5
  32. package/src/components/shared/Combobox/Combobox.tsx +2 -1
  33. package/src/i18n/dictionary.ts +5 -1
  34. package/src/use-lunatic/commons/page.ts +72 -3
  35. package/src/use-lunatic/reducer/commons/auto-explore-loop.ts +1 -1
  36. package/src/utils/search/tokenizer.spec.ts +14 -0
  37. package/src/utils/search/tokenizer.ts +6 -3
  38. package/tsconfig.build.tsbuildinfo +1 -1
  39. package/use-lunatic/commons/page.js +53 -2
  40. package/use-lunatic/commons/page.js.map +1 -1
  41. package/use-lunatic/reducer/commons/auto-explore-loop.js +1 -1
  42. package/use-lunatic/reducer/commons/auto-explore-loop.js.map +1 -1
  43. package/utils/search/tokenizer.js +6 -3
  44. package/utils/search/tokenizer.js.map +1 -1
  45. package/utils/search/tokenizer.spec.js +10 -0
  46. package/utils/search/tokenizer.spec.js.map +1 -1
@@ -13,6 +13,7 @@ import { ComboboxClearButton } from './Selection/ComboboxClearButton';
13
13
  import { ComboboxSelection } from './Selection/ComboboxSelection';
14
14
  import { between, forceInt } from '../../../utils/number';
15
15
  import { Label } from '../Label/Label';
16
+ import D from '../../../i18n';
16
17
  import { slottableComponent } from '../HOC/slottableComponent';
17
18
  import type { LunaticError } from '../../../use-lunatic/type';
18
19
  import { Declarations } from '../Declarations/Declarations';
@@ -45,7 +46,7 @@ function LunaticComboBox({
45
46
  className,
46
47
  classNamePrefix,
47
48
  classStyle = 'default-style',
48
- placeholder = 'Commencez votre saisie...',
49
+ placeholder = D.PLACEHOLDER,
49
50
  editable = false,
50
51
  disabled,
51
52
  readOnly,
@@ -5,7 +5,11 @@ const dictionary = {
5
5
  MODAL_CORRECT: { fr: 'Corriger ma réponse', en: 'Correct' },
6
6
  DK: { fr: 'Ne sais pas', en: "Don't know" },
7
7
  RF: { fr: 'Refus', en: 'Refused' },
8
- PLACEHOLDER: { fr: 'Commencez votre saisie...', en: 'Start typing...' },
8
+ PLACEHOLDER: { fr: 'Sélectionnez une modalité', en: 'Select a modality' },
9
+ SUGGESTER_PLACEHOLDER: {
10
+ fr: 'Commencez votre saisie...',
11
+ en: 'Start typing...',
12
+ },
9
13
  SUGGESTER_LOADING: {
10
14
  fr: 'Liste en cours de chargement',
11
15
  en: 'List is loading',
@@ -1,4 +1,4 @@
1
- import type { LunaticReducerState } from '../type';
1
+ import type { LunaticComponentDefinition, LunaticReducerState } from '../type';
2
2
  import { getComponentsFromState } from './get-components-from-state';
3
3
  import executeConditionFilter from './execute-condition-filter';
4
4
 
@@ -16,7 +16,69 @@ export function getPageId({
16
16
  * Converts a page number (3.1.2) to an array of numbers [3, 1, 2]
17
17
  */
18
18
  export function pageStringToNumbers(page: string): number[] {
19
- return page.split('.').map((v) => parseInt(v, 10));
19
+ return page.split('.').map((v) => Number.parseInt(v, 10));
20
+ }
21
+
22
+ // see useLoopUtils.ts
23
+ function getIterationOfLoop(
24
+ component: LunaticComponentDefinition,
25
+ executeExpression: LunaticReducerState['executeExpression']
26
+ ) {
27
+ const min =
28
+ 'lines' in component ? executeExpression<number>(component.lines.min) : 0;
29
+ const iterations =
30
+ 'iterations' in component
31
+ ? executeExpression<number>(component.iterations)
32
+ : 0;
33
+ return Math.max(min, iterations);
34
+ }
35
+
36
+ /**
37
+ * Check if component has a conditionFilter defined
38
+ * @param component
39
+ * @returns
40
+ */
41
+ function hasConditionFilter(component: LunaticComponentDefinition): boolean {
42
+ return 'conditionFilter' in component && !!component.conditionFilter;
43
+ }
44
+
45
+ /**
46
+ * Check if a not paginated Loop has at least one component to display
47
+ * @param component
48
+ * @param state
49
+ * @returns boolean indicating if the not paginated Loop is empty
50
+ */
51
+ function hasAtLeastOneComponentVisible(
52
+ component: LunaticComponentDefinition,
53
+ state: LunaticReducerState
54
+ ): boolean {
55
+ if (component.componentType === 'Loop' && !component.paginatedLoop) {
56
+ const nbIteration = getIterationOfLoop(component, state.executeExpression);
57
+ for (
58
+ let iterationOfLoop = 0;
59
+ iterationOfLoop < nbIteration;
60
+ iterationOfLoop++
61
+ ) {
62
+ for (const c of component.components) {
63
+ // if no conditionFilter -> component is visible
64
+ if (!hasConditionFilter(c)) return true;
65
+ if (
66
+ executeConditionFilter(
67
+ // @ts-expect-error Seem to be a typescript issue since we check type with hasConditionFilter, c.conditionFilter is defined
68
+ c.conditionFilter,
69
+ state.executeExpression,
70
+ iterationOfLoop
71
+ )
72
+ ) {
73
+ return true;
74
+ }
75
+ }
76
+ }
77
+ // no component visible in all iterations
78
+ return false;
79
+ }
80
+ // not a Loop
81
+ return true;
20
82
  }
21
83
 
22
84
  /**
@@ -27,6 +89,7 @@ export function isPageEmpty(state: LunaticReducerState): boolean {
27
89
  const { executeExpression, pager, options } = state;
28
90
  const { iteration } = pager;
29
91
  const components = getComponentsFromState(state, true);
92
+
30
93
  const visibleComponents = components.filter((component) => {
31
94
  if (options.disableFilters) {
32
95
  return true;
@@ -42,11 +105,17 @@ export function isPageEmpty(state: LunaticReducerState): boolean {
42
105
 
43
106
  // Use condition filter if present
44
107
  if ('conditionFilter' in component && component.conditionFilter) {
45
- return executeConditionFilter(
108
+ const conditionFilterResult = executeConditionFilter(
46
109
  component.conditionFilter,
47
110
  executeExpression,
48
111
  iteration
49
112
  );
113
+ // early return if the result of filter is false
114
+ if (!conditionFilterResult) return false;
115
+ // early return if the component is not a not Loop
116
+ if (component.componentType !== 'Loop') return conditionFilterResult;
117
+ // if the conditionFilter of NOT paginated Loop is true (have to be visible), we have to check if at least one component is visible inside
118
+ return hasAtLeastOneComponentVisible(component, state);
50
119
  }
51
120
  return true;
52
121
  });
@@ -35,7 +35,7 @@ export function autoExploreLoop(
35
35
  hasExploredLoop = true;
36
36
  };
37
37
 
38
- // The page is a loop
38
+ // The page is a paginated loop
39
39
  if (page.isLoop && page.subPages && page.subPages.length > 0) {
40
40
  goInsideSubpage(
41
41
  page.subPages,
@@ -121,6 +121,13 @@ describe('tokenizeQuery', () => {
121
121
  expect(result).toEqual(['eleve', 'etudiant']);
122
122
  });
123
123
 
124
+ it('should normalize ligatures like œ and æ', () => {
125
+ const queryParser = { type: 'soft' } as SearchInfo['queryParser'];
126
+
127
+ const result = tokenizeQuery('œuvre Œuvre æternam Æternam', queryParser);
128
+ expect(result).toEqual(['oeuvre', 'oeuvre', 'aeternam', 'aeternam']);
129
+ });
130
+
124
131
  it('should return an empty array for unmatched patterns', () => {
125
132
  const queryParser = {
126
133
  type: 'tokenized',
@@ -154,6 +161,13 @@ describe('tokenizeIndex', () => {
154
161
  expect(result).toEqual(['eleve', 'etudiant']);
155
162
  });
156
163
 
164
+ it('should normalize ligatures like œ and æ', () => {
165
+ const fieldInfo = mockSearchInfo.fields[0];
166
+
167
+ const result = tokenizeIndex('œuvre Œuvre æternam Æternam', fieldInfo);
168
+ expect(result).toEqual(['oeuvre', 'oeuvre', 'aeternam', 'aeternam']);
169
+ });
170
+
157
171
  it('should filter out stopWords', () => {
158
172
  const fieldInfo = { ...mockSearchInfo.fields[0], min: 1 };
159
173
  const stopWords = ['is', 'the', 'of', 'this', 'a'];
@@ -70,14 +70,17 @@ export const tokenizeIndex = (
70
70
 
71
71
  /**
72
72
  * Normalize a string
73
- * - Remove accent (é => e, à => a
73
+ * - Remove accent (é => e, à => a)
74
+ * - remove ligatures (æ => ae, , Æ => ae, œ => oe, Œ => oe)
74
75
  * - Lowercase
75
76
  */
76
77
  const normalizeStr = (str: string) => {
77
78
  return str
79
+ .toLowerCase()
80
+ .replaceAll('œ', 'oe')
81
+ .replaceAll('æ', 'ae')
78
82
  .normalize('NFD')
79
- .replace(/[\u0300-\u036f]/g, '')
80
- .toLowerCase();
83
+ .replace(/[\u0300-\u036f]/g, '');
81
84
  };
82
85
 
83
86
  /**