@beinformed/ui 1.60.5 → 1.61.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 (139) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/esm/hooks/__tests__/useForm.spec.js.flow +1 -1
  3. package/esm/hooks/useForm.js +15 -20
  4. package/esm/hooks/useForm.js.flow +5 -14
  5. package/esm/hooks/useForm.js.map +1 -1
  6. package/esm/hooks/useModularUI.d.ts +0 -1
  7. package/esm/hooks/useModularUI.js +3 -11
  8. package/esm/hooks/useModularUI.js.flow +4 -23
  9. package/esm/hooks/useModularUI.js.map +1 -1
  10. package/esm/models/attributes/AttributeCollection.js +2 -2
  11. package/esm/models/attributes/AttributeCollection.js.map +1 -1
  12. package/esm/models/attributes/AttributeContent.js +1 -1
  13. package/esm/models/attributes/AttributeContent.js.flow +1 -1
  14. package/esm/models/attributes/AttributeContent.js.map +1 -1
  15. package/esm/models/attributes/CompositeAttributeChildCollection.js +4 -4
  16. package/esm/models/attributes/CompositeAttributeChildCollection.js.map +1 -1
  17. package/esm/models/concepts/ConceptDetailModel.js +9 -9
  18. package/esm/models/concepts/ConceptDetailModel.js.map +1 -1
  19. package/esm/models/content/ContentTOCModel.d.ts +7 -0
  20. package/esm/models/content/ContentTOCModel.js +35 -8
  21. package/esm/models/content/ContentTOCModel.js.flow +36 -9
  22. package/esm/models/content/ContentTOCModel.js.map +1 -1
  23. package/esm/models/content/SectionModel.d.ts +3 -0
  24. package/esm/models/content/SectionModel.js +11 -2
  25. package/esm/models/content/SectionModel.js.flow +11 -0
  26. package/esm/models/content/SectionModel.js.map +1 -1
  27. package/esm/models/content/SubSectionModel.d.ts +1 -1
  28. package/esm/models/content/SubSectionModel.js.flow +1 -1
  29. package/esm/models/content/SubSectionModel.js.map +1 -1
  30. package/esm/models/content/__tests__/Formalsource.spec.js.flow +91 -0
  31. package/esm/models/form/FormModel.js +33 -33
  32. package/esm/models/form/FormModel.js.map +1 -1
  33. package/esm/models/form/FormObjectModel.d.ts +13 -12
  34. package/esm/models/form/FormObjectModel.js +10 -9
  35. package/esm/models/form/FormObjectModel.js.flow +41 -27
  36. package/esm/models/form/FormObjectModel.js.map +1 -1
  37. package/esm/models/form/__tests__/FormValidation.spec.js.flow +79 -0
  38. package/esm/models/href/Href.js +6 -6
  39. package/esm/models/href/Href.js.map +1 -1
  40. package/esm/models/links/LinkCollection.js +4 -4
  41. package/esm/models/links/LinkCollection.js.map +1 -1
  42. package/esm/redux/_modularui/ModularUIActions.d.ts +2 -2
  43. package/esm/redux/_modularui/ModularUIActions.js +27 -10
  44. package/esm/redux/_modularui/ModularUIActions.js.flow +24 -5
  45. package/esm/redux/_modularui/ModularUIActions.js.map +1 -1
  46. package/esm/redux/_modularui/ModularUIMiddleware.js +1 -1
  47. package/esm/redux/_modularui/ModularUIMiddleware.js.flow +1 -1
  48. package/esm/redux/_modularui/ModularUIMiddleware.js.map +1 -1
  49. package/esm/redux/_modularui/ModularUIReducer.js +4 -2
  50. package/esm/redux/_modularui/ModularUIReducer.js.flow +10 -1
  51. package/esm/redux/_modularui/ModularUIReducer.js.map +1 -1
  52. package/esm/redux/_modularui/ModularUISelectors.js +2 -2
  53. package/esm/redux/_modularui/ModularUISelectors.js.map +1 -1
  54. package/esm/redux/_modularui/__tests__/actions.spec.js.flow +5 -1
  55. package/esm/redux/_modularui/types.d.ts +1 -0
  56. package/esm/redux/_modularui/types.js.flow +2 -0
  57. package/esm/redux/_modularui/types.js.map +1 -1
  58. package/esm/utils/helpers/text.d.ts +1 -1
  59. package/esm/utils/helpers/text.js +1 -1
  60. package/esm/utils/helpers/text.js.flow +2 -2
  61. package/esm/utils/helpers/text.js.map +1 -1
  62. package/lib/hooks/useDeepCompareEffect.js +1 -2
  63. package/lib/hooks/useDeepCompareEffect.js.map +1 -1
  64. package/lib/hooks/useForm.js +14 -19
  65. package/lib/hooks/useForm.js.map +1 -1
  66. package/lib/hooks/useModularUI.d.ts +0 -1
  67. package/lib/hooks/useModularUI.js +4 -13
  68. package/lib/hooks/useModularUI.js.map +1 -1
  69. package/lib/i18n/index.js +1 -2
  70. package/lib/i18n/index.js.map +1 -1
  71. package/lib/models/attributes/AttributeCollection.js +2 -2
  72. package/lib/models/attributes/AttributeCollection.js.map +1 -1
  73. package/lib/models/attributes/AttributeContent.js +1 -1
  74. package/lib/models/attributes/AttributeContent.js.map +1 -1
  75. package/lib/models/attributes/CompositeAttributeChildCollection.js +4 -4
  76. package/lib/models/attributes/CompositeAttributeChildCollection.js.map +1 -1
  77. package/lib/models/concepts/ConceptDetailModel.js +9 -9
  78. package/lib/models/concepts/ConceptDetailModel.js.map +1 -1
  79. package/lib/models/content/ContentTOCModel.d.ts +7 -0
  80. package/lib/models/content/ContentTOCModel.js +35 -8
  81. package/lib/models/content/ContentTOCModel.js.map +1 -1
  82. package/lib/models/content/SectionModel.d.ts +3 -0
  83. package/lib/models/content/SectionModel.js +11 -2
  84. package/lib/models/content/SectionModel.js.map +1 -1
  85. package/lib/models/content/SubSectionModel.d.ts +1 -1
  86. package/lib/models/content/SubSectionModel.js.map +1 -1
  87. package/lib/models/form/FormModel.js +33 -33
  88. package/lib/models/form/FormModel.js.map +1 -1
  89. package/lib/models/form/FormObjectModel.d.ts +13 -12
  90. package/lib/models/form/FormObjectModel.js +10 -9
  91. package/lib/models/form/FormObjectModel.js.map +1 -1
  92. package/lib/models/href/Href.js +6 -6
  93. package/lib/models/href/Href.js.map +1 -1
  94. package/lib/models/index.js +1 -2
  95. package/lib/models/index.js.map +1 -1
  96. package/lib/models/links/LinkCollection.js +4 -4
  97. package/lib/models/links/LinkCollection.js.map +1 -1
  98. package/lib/react-server/serverUtil.js +1 -2
  99. package/lib/react-server/serverUtil.js.map +1 -1
  100. package/lib/redux/_modularui/ModularUIActions.d.ts +2 -2
  101. package/lib/redux/_modularui/ModularUIActions.js +27 -10
  102. package/lib/redux/_modularui/ModularUIActions.js.map +1 -1
  103. package/lib/redux/_modularui/ModularUIMiddleware.js +1 -1
  104. package/lib/redux/_modularui/ModularUIMiddleware.js.map +1 -1
  105. package/lib/redux/_modularui/ModularUIReducer.js +4 -2
  106. package/lib/redux/_modularui/ModularUIReducer.js.map +1 -1
  107. package/lib/redux/_modularui/ModularUISelectors.js +2 -2
  108. package/lib/redux/_modularui/ModularUISelectors.js.map +1 -1
  109. package/lib/redux/_modularui/types.d.ts +1 -0
  110. package/lib/redux/_modularui/types.js.map +1 -1
  111. package/lib/utils/helpers/text.d.ts +1 -1
  112. package/lib/utils/helpers/text.js +1 -1
  113. package/lib/utils/helpers/text.js.map +1 -1
  114. package/package.json +17 -17
  115. package/src/hooks/__tests__/useForm.spec.js +1 -1
  116. package/src/hooks/useForm.js +5 -14
  117. package/src/hooks/useModularUI.js +4 -23
  118. package/src/models/attributes/AttributeContent.js +1 -1
  119. package/src/models/content/ContentTOCModel.js +36 -9
  120. package/src/models/content/SectionModel.js +11 -0
  121. package/src/models/content/SubSectionModel.js +1 -1
  122. package/src/models/content/__tests__/Formalsource.spec.js +91 -0
  123. package/src/models/content/__tests__/formalsource-complete.json +234 -0
  124. package/src/models/content/__tests__/formalsource-contributions.json +110 -0
  125. package/src/models/content/__tests__/formalsource-section-contributions.json +84 -0
  126. package/src/models/content/__tests__/formalsource-section.json +60 -0
  127. package/src/models/content/__tests__/formalsource-toc.json +119 -0
  128. package/src/models/form/FormObjectModel.js +41 -27
  129. package/src/models/form/__tests__/FormValidation.spec.js +79 -0
  130. package/src/models/form/__tests__/FormValidationContributions.json +45 -0
  131. package/src/models/form/__tests__/FormValidationDataInitial.json +33 -0
  132. package/src/models/form/__tests__/FormValidationDataUpdate1.json +25 -0
  133. package/src/models/form/__tests__/FormValidationDataUpdate2.json +33 -0
  134. package/src/redux/_modularui/ModularUIActions.js +24 -5
  135. package/src/redux/_modularui/ModularUIMiddleware.js +1 -1
  136. package/src/redux/_modularui/ModularUIReducer.js +10 -1
  137. package/src/redux/_modularui/__tests__/actions.spec.js +5 -1
  138. package/src/redux/_modularui/types.js +2 -0
  139. package/src/utils/helpers/text.js +2 -2
@@ -0,0 +1,119 @@
1
+ {
2
+ "content": {
3
+ "filter": {
4
+ "booleanfilter": {
5
+ "param": "complete",
6
+ "name": "complete",
7
+ "value": "false"
8
+ },
9
+ "stringfilter": {
10
+ "param": "searchTerm",
11
+ "name": "searchTerm"
12
+ },
13
+ "entryDate": {
14
+ "param": "entryDate",
15
+ "name": "entryDate",
16
+ "value": "2025-05-28"
17
+ }
18
+ },
19
+ "_links": {
20
+ "self": {
21
+ "href": "/content/bundle-com.beinformed.source.GrantFundingAgreementTermsandConditions/Grant%20Funding%20Agreement.formalsource"
22
+ },
23
+ "api_doc": {
24
+ "href": "/api-docs/v3/content/(content-identifier)"
25
+ },
26
+ "contributions": {
27
+ "href": "/contributions/content/(content-identifier)"
28
+ }
29
+ },
30
+ "label": "Grant Funding Agreement - Terms and Conditions",
31
+ "sections": [
32
+ {
33
+ "_links": {
34
+ "self": {
35
+ "href": "/content/bundle-com.beinformed.source.GrantFundingAgreementTermsandConditions/Grant%20Funding%20Agreement.formalsource/Chapter1"
36
+ }
37
+ },
38
+ "_id": "Chapter1",
39
+ "label": "1. Introduction"
40
+ },
41
+ {
42
+ "_links": {
43
+ "self": {
44
+ "href": "/content/bundle-com.beinformed.source.GrantFundingAgreementTermsandConditions/Grant%20Funding%20Agreement.formalsource/Chapter2"
45
+ }
46
+ },
47
+ "_id": "Chapter2",
48
+ "label": "2. Grant Offer",
49
+ "sections": [
50
+ {
51
+ "_links": {
52
+ "self": {
53
+ "href": "/content/bundle-com.beinformed.source.GrantFundingAgreementTermsandConditions/Grant%20Funding%20Agreement.formalsource/Chapter2_1"
54
+ }
55
+ },
56
+ "_id": "Chapter2_1",
57
+ "label": "2.1 Payment of Grant"
58
+ },
59
+ {
60
+ "_links": {
61
+ "self": {
62
+ "href": "/content/bundle-com.beinformed.source.GrantFundingAgreementTermsandConditions/Grant%20Funding%20Agreement.formalsource/Chapter2_2"
63
+ }
64
+ },
65
+ "_id": "Chapter2_2",
66
+ "label": "2.2 Purpose of Funding"
67
+ }
68
+ ]
69
+ },
70
+ {
71
+ "_links": {
72
+ "self": {
73
+ "href": "/content/bundle-com.beinformed.source.GrantFundingAgreementTermsandConditions/Grant%20Funding%20Agreement.formalsource/Chapter3"
74
+ }
75
+ },
76
+ "_id": "Chapter3",
77
+ "label": "3. Amount of Grant"
78
+ },
79
+ {
80
+ "_links": {
81
+ "self": {
82
+ "href": "/content/bundle-com.beinformed.source.GrantFundingAgreementTermsandConditions/Grant%20Funding%20Agreement.formalsource/Chapter4"
83
+ }
84
+ },
85
+ "_id": "Chapter4",
86
+ "label": "4. Purpose of Grant"
87
+ },
88
+ {
89
+ "_links": {
90
+ "self": {
91
+ "href": "/content/bundle-com.beinformed.source.GrantFundingAgreementTermsandConditions/Grant%20Funding%20Agreement.formalsource/Chapter5"
92
+ }
93
+ },
94
+ "_id": "Chapter5",
95
+ "label": "5. Eligibility of Grant",
96
+ "sections": [
97
+ {
98
+ "_links": {
99
+ "self": {
100
+ "href": "/content/bundle-com.beinformed.source.GrantFundingAgreementTermsandConditions/Grant%20Funding%20Agreement.formalsource/Chapter5_1"
101
+ }
102
+ },
103
+ "_id": "Chapter5_1",
104
+ "label": "5.1 Eligibility Criteria"
105
+ }
106
+ ]
107
+ },
108
+ {
109
+ "_links": {
110
+ "self": {
111
+ "href": "/content/bundle-com.beinformed.source.GrantFundingAgreementTermsandConditions/Grant%20Funding%20Agreement.formalsource/AnnexA"
112
+ }
113
+ },
114
+ "_id": "AnnexA",
115
+ "label": "Annex A. Innovation levels research grant projects"
116
+ }
117
+ ]
118
+ }
119
+ }
@@ -18,8 +18,19 @@ import type {
18
18
  import LayoutHintCollection from "../layouthint/LayoutHintCollection";
19
19
 
20
20
  import type LinkModel from "../links/LinkModel";
21
- import type { MessageParameters } from "../../i18n";
22
21
  import type ErrorResponse from "../error/ErrorResponse";
22
+ import type { MessageParameters } from "../../i18n";
23
+
24
+ type ErrorObject = {
25
+ anchor: {
26
+ objectid: string,
27
+ elementid?: string,
28
+ },
29
+ id: string,
30
+ message: string,
31
+ properties: MessageParameters,
32
+ layouthint: Array<string>,
33
+ };
23
34
 
24
35
  /**
25
36
  * Form Object
@@ -474,22 +485,12 @@ export default class FormObjectModel extends BaseModel {
474
485
  /**
475
486
  * Convert error json from a form service to validation messages on the form object and attributes
476
487
  */
477
- handleErrorValidations(
478
- errors: Array<{
479
- anchor: {
480
- objectid: string,
481
- elementid?: string,
482
- },
483
- id: string,
484
- message: string,
485
- properties: MessageParameters,
486
- layouthint: Array<string>,
487
- }>,
488
- ) {
488
+ handleErrorValidations(errors: Array<ErrorObject>) {
489
489
  const attributeErrors = [];
490
490
 
491
- errors.forEach((error) => {
492
- if (error.anchor?.objectid === this.key) {
491
+ errors
492
+ .filter((error) => error.anchor?.objectid === this.key)
493
+ .forEach((error) => {
493
494
  if (has(error.anchor, "elementid")) {
494
495
  attributeErrors.push(error);
495
496
  } else {
@@ -500,8 +501,7 @@ export default class FormObjectModel extends BaseModel {
500
501
  new LayoutHintCollection(error.layouthint),
501
502
  );
502
503
  }
503
- }
504
- });
504
+ });
505
505
 
506
506
  this.attributeCollection.updateValidations(attributeErrors);
507
507
  }
@@ -509,15 +509,18 @@ export default class FormObjectModel extends BaseModel {
509
509
  /**
510
510
  * Convert missing json from a form service to mandatory validation constraints and messages
511
511
  */
512
- handleMissingValidations(missing: {
513
- anchors: Array<
514
- | {
515
- elements: Array<{ elementid: string }>,
516
- objectid?: string,
517
- }
518
- | { anchor: { elementid: string, objectid?: string } },
519
- >,
520
- }) {
512
+ handleMissingValidations(
513
+ missing: {
514
+ anchors: Array<
515
+ | {
516
+ elements: Array<{ elementid: string }>,
517
+ objectid?: string,
518
+ }
519
+ | { anchor: { elementid: string, objectid?: string } },
520
+ >,
521
+ },
522
+ errors: Array<ErrorObject>,
523
+ ) {
521
524
  const attributeErrors = [];
522
525
 
523
526
  for (const anchor of missing.anchors) {
@@ -538,6 +541,17 @@ export default class FormObjectModel extends BaseModel {
538
541
  }
539
542
  }
540
543
 
544
+ errors
545
+ .filter(
546
+ (error) =>
547
+ error.id === "Constraint.Mandatory" &&
548
+ error.anchor?.objectid === this.key &&
549
+ has(error.anchor, "elementid"),
550
+ )
551
+ .forEach((error) => {
552
+ attributeErrors.push(error);
553
+ });
554
+
541
555
  this.attributeCollection.updateValidations(attributeErrors);
542
556
  }
543
557
 
@@ -552,7 +566,7 @@ export default class FormObjectModel extends BaseModel {
552
566
 
553
567
  // missing attribute errors
554
568
  if (Array.isArray(data.missing?.anchors)) {
555
- this.handleMissingValidations(data.missing);
569
+ this.handleMissingValidations(data.missing, data.errors);
556
570
  }
557
571
 
558
572
  this.dynamicValidationsLoaded = true;
@@ -0,0 +1,79 @@
1
+ import { ModularUIResponse } from "../../../modularui";
2
+
3
+ import initialData from "./FormValidationDataInitial.json";
4
+ import contributions from "./FormValidationContributions.json";
5
+ import updateData from "./FormValidationDataUpdate1.json";
6
+ import updateData2 from "./FormValidationDataUpdate2.json";
7
+
8
+ import FormModel from "../FormModel";
9
+
10
+ describe("Form validation update", () => {
11
+ it("should be able to update the validations of a form", () => {
12
+ const response = ModularUIResponse.create({
13
+ key: "Form",
14
+ data: initialData,
15
+ contributions: contributions,
16
+ });
17
+
18
+ const form = new FormModel(response);
19
+ const formModel = form.currentFormObject;
20
+
21
+ const validation = formModel.getAttributeByKey("Validation");
22
+ const compareWith = formModel.getAttributeByKey("CompareWith");
23
+ const optional = formModel.getAttributeByKey("ThisOneIsOptional");
24
+
25
+ expect(validation.mandatory).toBe(true);
26
+ expect(compareWith.mandatory).toBe(true);
27
+ expect(optional.mandatory).toBe(false);
28
+
29
+ const newForm = form.clone();
30
+ newForm.currentFormObject.updateAttribute(validation, "test");
31
+
32
+ expect(validation.mandatory).toBe(true);
33
+ expect(compareWith.mandatory).toBe(true);
34
+ expect(optional.mandatory).toBe(false);
35
+
36
+ const updateResponse = ModularUIResponse.create({
37
+ key: "Form",
38
+ data: updateData,
39
+ contributions: contributions,
40
+ });
41
+ const updateForm = new FormModel(updateResponse);
42
+
43
+ newForm.updateValidations(updateForm.data);
44
+
45
+ expect(
46
+ newForm.currentFormObject.getAttributeByKey("Validation").mandatory,
47
+ ).toBe(true);
48
+ expect(
49
+ newForm.currentFormObject.getAttributeByKey("CompareWith").mandatory,
50
+ ).toBe(true);
51
+ expect(
52
+ newForm.currentFormObject.getAttributeByKey("ThisOneIsOptional")
53
+ .mandatory,
54
+ ).toBe(false);
55
+
56
+ const newForm2 = form.clone();
57
+ newForm2.currentFormObject.updateAttribute(validation, "");
58
+
59
+ const update2Response = ModularUIResponse.create({
60
+ key: "Form",
61
+ data: updateData2,
62
+ contributions: contributions,
63
+ });
64
+ const update2Form = new FormModel(update2Response);
65
+
66
+ newForm.updateValidations(update2Form.data);
67
+
68
+ expect(
69
+ newForm.currentFormObject.getAttributeByKey("Validation").mandatory,
70
+ ).toBe(true);
71
+ expect(
72
+ newForm.currentFormObject.getAttributeByKey("CompareWith").mandatory,
73
+ ).toBe(true);
74
+ expect(
75
+ newForm.currentFormObject.getAttributeByKey("ThisOneIsOptional")
76
+ .mandatory,
77
+ ).toBe(false);
78
+ });
79
+ });
@@ -0,0 +1,45 @@
1
+ {
2
+ "ValidationMakesItOptional": {
3
+ "label": "Validation makes it optional",
4
+ "resourcetype": "Form",
5
+ "actiontype": "form",
6
+ "objects": {
7
+ "ValidationMakesItOptional": {
8
+ "dynamicObject": false,
9
+ "repeatable": false,
10
+ "dynamicValidations": true,
11
+ "label": "Validation makes it optional",
12
+ "mandatory": true,
13
+ "attributes": [
14
+ {
15
+ "Validation": {
16
+ "type": "string",
17
+ "label": "Validation",
18
+ "mandatory": true,
19
+ "displaysize": 50,
20
+ "maxLength": 255
21
+ }
22
+ },
23
+ {
24
+ "CompareWith": {
25
+ "type": "string",
26
+ "label": "Compare with",
27
+ "mandatory": true,
28
+ "displaysize": 50,
29
+ "maxLength": 255
30
+ }
31
+ },
32
+ {
33
+ "ThisOneIsOptional": {
34
+ "type": "string",
35
+ "label": "This one is optional",
36
+ "mandatory": false,
37
+ "displaysize": 50,
38
+ "maxLength": 255
39
+ }
40
+ }
41
+ ]
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "formresponse": {
3
+ "_links": {
4
+ "self": {
5
+ "href": "/all-cases/validation-makes/validation-makes-it-optional"
6
+ },
7
+ "api_doc": {
8
+ "href": "/api-docs/v3/all-cases/validation-makes/validation-makes-it-optional"
9
+ },
10
+ "contributions": {
11
+ "href": "/contributions/all-cases/validation-makes/validation-makes-it-optional"
12
+ }
13
+ },
14
+ "missing": {
15
+ "anchors": [
16
+ {
17
+ "objectid": "ValidationMakesItOptional",
18
+ "elements": [
19
+ {
20
+ "elementid": "Validation"
21
+ },
22
+ {
23
+ "elementid": "CompareWith"
24
+ },
25
+ {
26
+ "elementid": "ThisOneIsOptional"
27
+ }
28
+ ]
29
+ }
30
+ ]
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "formresponse": {
3
+ "_links": {
4
+ "self": {
5
+ "href": "/all-cases/validation-makes/validation-makes-it-optional"
6
+ },
7
+ "api_doc": {
8
+ "href": "/api-docs/v3/all-cases/validation-makes/validation-makes-it-optional"
9
+ },
10
+ "contributions": {
11
+ "href": "/contributions/all-cases/validation-makes/validation-makes-it-optional"
12
+ }
13
+ },
14
+ "errors": [
15
+ {
16
+ "anchor": {
17
+ "objectid": "ValidationMakesItOptional",
18
+ "elementid": "CompareWith"
19
+ },
20
+ "id": "Constraint.Mandatory",
21
+ "message": "Field is mandatory"
22
+ }
23
+ ]
24
+ }
25
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "formresponse": {
3
+ "_links": {
4
+ "self": {
5
+ "href": "/all-cases/validation-makes/validation-makes-it-optional"
6
+ },
7
+ "api_doc": {
8
+ "href": "/api-docs/v3/all-cases/validation-makes/validation-makes-it-optional"
9
+ },
10
+ "contributions": {
11
+ "href": "/contributions/all-cases/validation-makes/validation-makes-it-optional"
12
+ }
13
+ },
14
+ "missing": {
15
+ "anchors": [
16
+ {
17
+ "objectid": "ValidationMakesItOptional",
18
+ "elementid": "Validation"
19
+ }
20
+ ]
21
+ },
22
+ "errors": [
23
+ {
24
+ "anchor": {
25
+ "objectid": "ValidationMakesItOptional",
26
+ "elementid": "CompareWith"
27
+ },
28
+ "id": "Constraint.Mandatory",
29
+ "message": "Field is mandatory"
30
+ }
31
+ ]
32
+ }
33
+ }
@@ -81,9 +81,10 @@ export const resetModularUI = (): ResetModularUIAction => ({
81
81
  export const updateStatus = (
82
82
  key: string,
83
83
  status: $Keys<typeof MODULARUI_STATUS>,
84
+ requestOptions: any,
84
85
  ): UpdateStatusAction => ({
85
86
  type: "MODULARUI/STATUS",
86
- payload: { key, status },
87
+ payload: { key, status, requestOptions },
87
88
  });
88
89
 
89
90
  /**
@@ -177,13 +178,31 @@ export const loadModularUI =
177
178
  key: string,
178
179
  href: Href | string,
179
180
  options?: RequestModularUIOptions,
181
+ retryCount: number = 3,
182
+ retryDelay: number = 200,
180
183
  ): ThunkAction =>
181
184
  (dispatch: Dispatch, getState) => {
182
185
  const modularuiStore = getState()?.modularui;
183
- if (
184
- modularuiStore &&
185
- modularuiStore[key]?.status === MODULARUI_STATUS.LOADING
186
- ) {
186
+ const currentRequest = modularuiStore?.[key];
187
+
188
+ const isLoading = currentRequest?.status === MODULARUI_STATUS.LOADING;
189
+
190
+ if (isLoading) {
191
+ const isDifferentRequest =
192
+ currentRequest.requestOptions?.href?.toString() !== href.toString();
193
+ if (isDifferentRequest && retryCount > 0) {
194
+ return new Promise((resolve) => {
195
+ setTimeout(() => {
196
+ resolve(
197
+ dispatch(
198
+ loadModularUI(key, href, options, retryCount - 1, retryDelay),
199
+ ),
200
+ );
201
+ }, retryDelay);
202
+ });
203
+ }
204
+
205
+ // Same request is already loading — do nothing
187
206
  return Promise.resolve();
188
207
  }
189
208
 
@@ -150,7 +150,7 @@ const handleFetch = (
150
150
 
151
151
  const modularuiRequest = createRequest(requestOptions);
152
152
 
153
- dispatch(updateStatus(key, MODULARUI_STATUS.LOADING));
153
+ dispatch(updateStatus(key, MODULARUI_STATUS.LOADING, requestOptions));
154
154
 
155
155
  return modularuiRequest
156
156
  .fetch()
@@ -11,7 +11,15 @@ import type { ModularUIModel } from "../../models";
11
11
  */
12
12
  const updateStatus = (
13
13
  state: ModularUIState,
14
- { key, status }: { key: string, status: $Keys<typeof MODULARUI_STATUS> },
14
+ {
15
+ key,
16
+ status,
17
+ requestOptions,
18
+ }: {
19
+ key: string,
20
+ status: $Keys<typeof MODULARUI_STATUS>,
21
+ requestOptions: any,
22
+ },
15
23
  ) => {
16
24
  // model should always be available when status is not loading
17
25
  if (status !== MODULARUI_STATUS.LOADING && !state[key]) {
@@ -24,6 +32,7 @@ const updateStatus = (
24
32
  ...state[key],
25
33
  status,
26
34
  lastModification: Date.now(),
35
+ requestOptions: requestOptions || state[key]?.requestOptions,
27
36
  },
28
37
  };
29
38
  };
@@ -72,7 +72,11 @@ describe("modularui actions", () => {
72
72
  expect(store.getActions()).toStrictEqual([
73
73
  {
74
74
  type: "MODULARUI/STATUS",
75
- payload: { key: "application", status: MODULARUI_STATUS.LOADING },
75
+ payload: {
76
+ key: "application",
77
+ status: MODULARUI_STATUS.LOADING,
78
+ requestOptions: undefined,
79
+ },
76
80
  },
77
81
  ]);
78
82
  });
@@ -21,6 +21,7 @@ export type ModelEntry = {
21
21
  +status: string,
22
22
  +model: ModularUIModel,
23
23
  +lastModification: number,
24
+ +requestOptions?: any,
24
25
  };
25
26
 
26
27
  export type ModularUIState = {
@@ -111,6 +112,7 @@ export type UpdateStatusAction = {
111
112
  payload: {
112
113
  key: string,
113
114
  status: $Keys<MODULARUI_STATUS>,
115
+ requestOptions: any,
114
116
  },
115
117
  };
116
118
 
@@ -10,7 +10,7 @@ export const retrieveText = (
10
10
  text:
11
11
  | string
12
12
  | { id?: string, message: string, properties?: { [key: string]: any } },
13
- ): string => {
13
+ ): string | null => {
14
14
  if (typeof text === "string") {
15
15
  return text;
16
16
  }
@@ -19,5 +19,5 @@ export const retrieveText = (
19
19
  return text.message;
20
20
  }
21
21
 
22
- return "";
22
+ return null;
23
23
  };