powerpagestoolkit 2.5.409 → 2.5.4111

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/README.md CHANGED
@@ -19,7 +19,7 @@ A TypeScript/JavaScript utility package for seamless DOM manipulation and DataVe
19
19
  npm install powerpagestoolkit
20
20
  ```
21
21
 
22
- ## Core Modules
22
+ # Core Modules
23
23
 
24
24
  ### DOMNodeReference
25
25
 
@@ -27,13 +27,28 @@ A powerful class for managing DOM elements with automatic value synchronization
27
27
 
28
28
  #### Basic Usage
29
29
 
30
+ DOMNodeReferences are instantiated with the help of the following factory function
31
+
32
+ ```typescript
33
+ createRef(
34
+ target: HTMLElement | string, /* You can target an HTMLElement directly,
35
+ or use standard querySelector syntax */
36
+ multiple: (() => boolean) | boolean = false /* are you targeting a single
37
+ element, or multiple? true = multiple. Default is false (single) */
38
+ ): Promise<DOMNodeReference | DOMNodeReferenceArray>;
39
+ ```
40
+
41
+ Import the utility function for creating DOMNodeReference(s)
42
+
30
43
  ```typescript
31
44
  import { createRef } from "powerpagestoolkit";
45
+ ```
32
46
 
33
- // Both methods support standard querySelector syntax:
47
+ Instantiate one, or multiple instances of a DOMNodeReference
34
48
 
49
+ ```typescript
35
50
  // Create a single reference
36
- const node = await createRef("#myElement");
51
+ const node = await createRef("#myElement", false);
37
52
 
38
53
  // Create multiple references
39
54
  const nodes = await createRef(".my-class", true);
@@ -57,14 +72,16 @@ const nodes = await createRef(".my-class", true);
57
72
 
58
73
  ```typescript
59
74
  // Add event listener with proper 'this' context
75
+ // uses standard eventListener API, and so supports all DOM events
60
76
  node.on("change", function (e) {
61
77
  console.log("Current value:", this.value);
62
78
  });
63
79
 
64
- // Wait for element to be loaded
65
- node.onceLoaded((instance) => {
66
- console.log("Element ready:", instance.element);
80
+ node.on("click", function (e) {
81
+ console.log(this, " has been clicked");
67
82
  });
83
+
84
+ ...
68
85
  ```
69
86
 
70
87
  ##### Visibility Control
@@ -73,63 +90,113 @@ node.onceLoaded((instance) => {
73
90
  // Basic visibility
74
91
  node.hide();
75
92
  node.show();
93
+ ```
94
+
95
+ **_Advanced conditional rendering_**
96
+
97
+ Out of the box, Microsoft does not provide PowerPages developers the ability to hide or show fields or form elements based on the value of another field. This method allows such configurations
76
98
 
77
- // Advanced conditional rendering
99
+ _Method signature:_
100
+
101
+ ```typescript
102
+ DOMNodeReference.configureConditionalRendering(
103
+ condition: () => boolean,
104
+ dependencies?: Array<DOMNodeReference>,
105
+ clearValuesOnHide: boolean = true
106
+ ): DOMNodeReference
107
+ ```
108
+
109
+ _Example implementation:_
110
+
111
+ ```typescript
78
112
  node.configureConditionalRendering(
79
113
  // Function to evaluate wether this node should be visible or not
80
114
  function () {
81
- return otherNode.value === "expected";
115
+ return otherNode.value === "some value";
82
116
  },
83
- [otherNode] // Dependency array | if the values or visibility of these change, the function is re-evaluated
117
+ [otherNode] /* Dependency array | if the values or visibility of these
118
+ change, the function is re-evaluated */,
119
+ true /* should the values in the targeted elements (this.element)
120
+ be cleared if this node is hidden? Default = true */
84
121
  );
85
122
  ```
86
123
 
87
124
  ##### Validation and Requirements
88
125
 
126
+ This utility enhances PowerPages forms by adding dynamic field validation and conditional requirements based on other field values.
127
+
128
+ _Method signature:_
129
+
130
+ ```typescript
131
+ // Core method for setting up validation
132
+ function configureValidationAndRequirements(
133
+ isRequired: () => boolean, // Determines if field is required
134
+ isValid: () => boolean, // Validates field content
135
+ fieldDisplayName: string, // User-facing field name for error messages
136
+ dependencies: DOMNodeReference[] // Fields that trigger validation checks
137
+ ): DOMNodeReference; /* instance of this is returned for optional
138
+ method chaining */
139
+ ```
140
+
141
+ _Example implementation:_
142
+
89
143
  ```typescript
90
144
  node.configureValidationAndRequirements(
91
- // Function to evaluate if this field should be required
92
- function () {
93
- return dependentNode.yesRadio?.checked ?? false;
94
- },
95
- // Function to evaluate if the data in this field is valid
145
+ // Make field required only when "Yes" is checked
146
+ () => dependentNode.yesRadio?.checked ?? false,
147
+
148
+ // Basic validation: ensure field isn't empty
96
149
  function () {
97
150
  return this.value != null && this.value !== "";
98
151
  },
99
- "Field Display Name", // the name that will be displayed along side a validation failure message
100
- [dependentNode] // Dependency array | if the requirement level of these change, this element is re-evaluated
152
+
153
+ "Contact Phone", // Shows in error message: "Contact Phone is required"
154
+
155
+ [dependentNode] // Revalidate when dependentNode changes
101
156
  );
102
157
  ```
103
158
 
104
159
  ##### Element Manipulation
105
160
 
106
161
  ```typescript
107
- // Value management
108
- node.setValue("new value"); // set a static value
162
+ /****/ Value management /****/
163
+
164
+ // set a static value
165
+ node.setValue("new value");
166
+
109
167
  // or set a value by using some sort of logic
110
168
  node.setValue(() => {
111
169
  if (true) {
112
170
  return "value";
113
171
  } else return "default";
114
172
  });
115
- node.updateValue(); // Sync with DOM
116
173
 
117
- // Content manipulation
174
+ // Sync with DOM
175
+ node.updateValue();
176
+
177
+ // Clear the value for both the instance and the target element
178
+ node.clearValue()
179
+
180
+ /****/ Content manipulation /****/
181
+
118
182
  node.setInnerHTML("<span>New content</span>");
119
183
  node.append(childElement);
120
184
  node.prepend(headerElement);
121
185
  node.after(siblingElement);
122
186
  node.before(labelElement);
123
187
 
124
- // Styling
188
+ /****/ Styling /****/
189
+
125
190
  node.setStyle({
126
191
  display: "block",
127
192
  color: "red",
128
193
  });
129
194
 
130
- // State management
195
+ /****/ State management /****/
196
+
131
197
  node.disable();
132
198
  node.enable();
199
+
133
200
  ```
134
201
 
135
202
  ##### Label and Tooltip Management
@@ -153,9 +220,9 @@ node.addTooltip(
153
220
 
154
221
  ### DataVerse API
155
222
 
156
- Type-safe wrapper for DataVerse API operations.
223
+ Perform secure API calls to DataVerse from your PowerPages site. This method implements the shell deferred token to send requests with `__RequestVerificationToken`
157
224
 
158
- #### Create Record
225
+ #### Create Records
159
226
 
160
227
  ```typescript
161
228
  await API.createRecord("accounts", {
@@ -215,18 +282,10 @@ node.configureConditionalRendering(
215
282
 
216
283
  3. Use TypeScript for better type safety and IntelliSense support.
217
284
 
218
- 4. Handle loading states:
219
-
220
- ```typescript
221
- node.onceLoaded((instance) => {
222
- // Safe to manipulate the element here
223
- });
224
- ```
225
-
226
- 5. Use proper error handling with API operations:
285
+ 4. Use proper error handling with API operations:
227
286
 
228
287
  ```typescript
229
- await API.createRecord(/*...*/)
288
+ /* optionally await */ API.createRecord(/*...*/)
230
289
  .then((recordId) => {})
231
290
  .catch((error) => {
232
291
  // handle your errors appropriately
@@ -244,3 +303,7 @@ Contributions are welcome, feel free to create a pull request with enhancements.
244
303
  ## License
245
304
 
246
305
  This project is licensed under the AGPL-3.0 License - see the [LICENSE](LICENSE) file for details.
306
+
307
+ ## Funding
308
+
309
+ If you like this project, found it useful, or would like to help support the long-term support of this package, please feel free to contribute via GitHub Sponsors: [Keaton-Brewster](https://github.com/sponsors/Keaton-Brewster)
@@ -6,14 +6,12 @@ export default class DOMNodeReference {
6
6
  /**
7
7
  * The value of the element that this node represents
8
8
  * stays in syncs with the live DOM elements?.,m via event handler
9
- * @type {any}
10
9
  */
11
10
  value: any;
12
11
  /**
13
12
  * The element targeted when instantiating DOMNodeReference.
14
13
  * Made available in order to perform normal DOM traversal,
15
14
  * or access properties not available through this class.
16
- * @property {HTMLElement | null}
17
15
  */
18
16
  element: Element;
19
17
  private visibilityController;
@@ -22,19 +20,17 @@ export default class DOMNodeReference {
22
20
  * Represents the 'yes' option of a boolean radio field.
23
21
  * This property is only available when the parent node
24
22
  * is a main field for a boolean radio input.
25
- * @property {DOMNodeReferenceProxy | null}
26
23
  */
27
24
  yesRadio?: DOMNodeReference | null;
28
25
  /**
29
26
  * Represents the 'no' option of a boolean radio field.
30
27
  * This property is only available when the parent node
31
28
  * is a main field for a boolean radio input.
32
- * @property {DOMNodeReferenceProxy | null}
33
29
  */
34
30
  noRadio?: DOMNodeReference | null;
35
31
  /**
36
32
  * Creates an instance of DOMNodeReference.
37
- * @param {string} target - The CSS selector to find the desired DOM element.
33
+ * @param target - The CSS selector to find the desired DOM element.
38
34
  */
39
35
  /******/ /******/ constructor(target: HTMLElement | string);
40
36
  [_init](): Promise<void>;
@@ -84,7 +80,7 @@ export default class DOMNodeReference {
84
80
  show(): DOMNodeReference;
85
81
  /**
86
82
  *
87
- * @param {function(instance: DOMNodeReference): boolean | boolean} shouldShow - Either a function that returns true or false,
83
+ * @param shouldShow - Either a function that returns true or false,
88
84
  * or a natural boolean to determine the visibility of this
89
85
  * @returns - Instance of this [provides option to method chain]
90
86
  */
@@ -109,7 +105,7 @@ export default class DOMNodeReference {
109
105
  * @returns - Instance of this [provides option to method chain]
110
106
  * @throws If clearing values fails
111
107
  */
112
- clearValues(): Promise<DOMNodeReference>;
108
+ clearValue(): Promise<DOMNodeReference>;
113
109
  /**
114
110
  * Enables the element so that users can input data
115
111
  * @returns - Instance of this [provides option to method chain]
@@ -206,10 +202,13 @@ export default class DOMNodeReference {
206
202
  */
207
203
  configureValidationAndRequirements(isRequired: () => boolean, isValid: () => boolean, fieldDisplayName: string, dependencies: Array<DOMNodeReference>): DOMNodeReference;
208
204
  /**
209
- * Sets up tracking for dependent fields using both event listeners and mutation observers.
205
+ * Sets up tracking for dependencies using both event listeners and mutation observers.
210
206
  * @private
207
+ * @param handler - The function to execute when dependencies change
208
+ * @param dependencies - Array of dependent DOM nodes to track
209
+ * @param options - Additional configuration options
211
210
  */
212
- private _setupDependencyTracking;
211
+ private _configDependencyTracking;
213
212
  /**
214
213
  * Sets the required level for the field by adding or removing the "required-field" class on the label.
215
214
  *
package/dist/bundle.js CHANGED
@@ -237,12 +237,11 @@ var DOMNodeReference = class _DOMNodeReference {
237
237
  /**
238
238
  * The value of the element that this node represents
239
239
  * stays in syncs with the live DOM elements?.,m via event handler
240
- * @type {any}
241
240
  */
242
241
  value;
243
242
  /**
244
243
  * Creates an instance of DOMNodeReference.
245
- * @param {string} target - The CSS selector to find the desired DOM element.
244
+ * @param target - The CSS selector to find the desired DOM element.
246
245
  */
247
246
  /******/
248
247
  /******/
@@ -442,7 +441,7 @@ var DOMNodeReference = class _DOMNodeReference {
442
441
  }
443
442
  /**
444
443
  *
445
- * @param {function(instance: DOMNodeReference): boolean | boolean} shouldShow - Either a function that returns true or false,
444
+ * @param shouldShow - Either a function that returns true or false,
446
445
  * or a natural boolean to determine the visibility of this
447
446
  * @returns - Instance of this [provides option to method chain]
448
447
  */
@@ -494,7 +493,7 @@ var DOMNodeReference = class _DOMNodeReference {
494
493
  * @returns - Instance of this [provides option to method chain]
495
494
  * @throws If clearing values fails
496
495
  */
497
- async clearValues() {
496
+ async clearValue() {
498
497
  try {
499
498
  const element = this.element;
500
499
  if (element instanceof HTMLInputElement) {
@@ -535,14 +534,14 @@ var DOMNodeReference = class _DOMNodeReference {
535
534
  if (childInputs.length > 0) {
536
535
  const clearPromises = childInputs.map(async (input) => {
537
536
  const inputRef = await createDOMNodeReference(input, false);
538
- return inputRef.clearValues();
537
+ return inputRef.clearValue();
539
538
  });
540
539
  await Promise.all(clearPromises);
541
540
  }
542
541
  }
543
542
  if (this.yesRadio instanceof _DOMNodeReference && this.noRadio instanceof _DOMNodeReference) {
544
- await this.yesRadio.clearValues();
545
- await this.noRadio.clearValues();
543
+ await this.yesRadio.clearValue();
544
+ await this.noRadio.clearValue();
546
545
  }
547
546
  const events = [
548
547
  new Event("input", { bubbles: true }),
@@ -710,40 +709,16 @@ var DOMNodeReference = class _DOMNodeReference {
710
709
  );
711
710
  return this;
712
711
  }
713
- dependencies.forEach((node) => {
714
- if (!node || !(node instanceof _DOMNodeReference)) {
715
- throw new TypeError(
716
- "Each dependency must be a valid DOMNodeReference instance"
717
- );
712
+ this._configDependencyTracking(
713
+ () => this.toggleVisibility(condition()),
714
+ dependencies,
715
+ {
716
+ clearValuesOnHide,
717
+ observeVisibility: true,
718
+ trackInputEvents: false,
719
+ trackRadioButtons: false
718
720
  }
719
- const handleChange = () => {
720
- try {
721
- this.toggleVisibility(condition());
722
- if (condition() === false && clearValuesOnHide) {
723
- this.clearValues();
724
- }
725
- } catch (error) {
726
- console.error("Error in change handler:", error);
727
- }
728
- };
729
- node.on("change", handleChange);
730
- const observer = new MutationObserver(() => {
731
- try {
732
- const display = window.getComputedStyle(
733
- node.visibilityController
734
- ).display;
735
- this.toggleVisibility(display !== "none" && condition());
736
- } catch (error) {
737
- 0;
738
- console.error("Error in mutation observer:", error);
739
- observer.disconnect();
740
- }
741
- });
742
- observer.observe(node.visibilityController, {
743
- attributes: true,
744
- attributeFilter: ["style"]
745
- });
746
- });
721
+ );
747
722
  return this;
748
723
  } catch (error) {
749
724
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -792,7 +767,10 @@ var DOMNodeReference = class _DOMNodeReference {
792
767
  Object.assign(newValidator, validatorConfig);
793
768
  Page_Validators.push(newValidator);
794
769
  this.setRequiredLevel(isRequired());
795
- this._setupDependencyTracking(isRequired, dependencies);
770
+ this._configDependencyTracking(
771
+ () => this.setRequiredLevel(isRequired()),
772
+ dependencies
773
+ );
796
774
  } catch (error) {
797
775
  throw new ValidationConfigError(
798
776
  this,
@@ -802,35 +780,64 @@ var DOMNodeReference = class _DOMNodeReference {
802
780
  return this;
803
781
  }
804
782
  /**
805
- * Sets up tracking for dependent fields using both event listeners and mutation observers.
783
+ * Sets up tracking for dependencies using both event listeners and mutation observers.
806
784
  * @private
807
- */
808
- _setupDependencyTracking(isRequired, dependencies) {
809
- if (dependencies.length === 0) {
785
+ * @param handler - The function to execute when dependencies change
786
+ * @param dependencies - Array of dependent DOM nodes to track
787
+ * @param options - Additional configuration options
788
+ */
789
+ _configDependencyTracking(handler, dependencies, options = {
790
+ clearValuesOnHide: false,
791
+ observeVisibility: true,
792
+ trackInputEvents: true,
793
+ trackRadioButtons: true
794
+ }) {
795
+ const {
796
+ clearValuesOnHide = false,
797
+ observeVisibility = true,
798
+ trackInputEvents = true,
799
+ trackRadioButtons = true
800
+ } = options;
801
+ if (!dependencies?.length) {
810
802
  console.warn(
811
- `powerpagestoolkit: No dependencies specified for ${this.element.id}. Include all referenced nodes in the dependency array for proper validation.`
803
+ `powerpagestoolkit: No dependencies specified for ${this.element.id}. Include all referenced nodes in the dependency array for proper tracking.`
812
804
  );
813
805
  return;
814
806
  }
815
807
  dependencies.forEach((dep) => {
816
- dep.on("change", () => this.setRequiredLevel(isRequired(this)));
817
- dep.on("input", () => this.setRequiredLevel(isRequired(this)));
818
- const observer = new MutationObserver(() => {
819
- const display = window.getComputedStyle(
820
- dep.visibilityController
821
- ).display;
822
- if (display !== "none") {
823
- this.setRequiredLevel(isRequired(this));
808
+ if (!dep || !(dep instanceof _DOMNodeReference)) {
809
+ throw new TypeError(
810
+ "Each dependency must be a valid DOMNodeReference instance"
811
+ );
812
+ }
813
+ const handleChange = () => {
814
+ handler();
815
+ if (clearValuesOnHide && window.getComputedStyle(this.visibilityController).display === "none") {
816
+ this.clearValue();
824
817
  }
825
- });
826
- observer.observe(dep.visibilityController, {
827
- attributes: true,
828
- attributeFilter: ["style"],
829
- subtree: false
830
- });
831
- if (dep.yesRadio || dep.noRadio) {
818
+ };
819
+ dep.on("change", handleChange);
820
+ if (trackInputEvents) {
821
+ dep.on("input", handleChange);
822
+ }
823
+ if (observeVisibility) {
824
+ const observer = new MutationObserver(() => {
825
+ const display = window.getComputedStyle(
826
+ dep.visibilityController
827
+ ).display;
828
+ if (display !== "none") {
829
+ handler();
830
+ }
831
+ });
832
+ observer.observe(dep.visibilityController, {
833
+ attributes: true,
834
+ attributeFilter: ["style"],
835
+ subtree: false
836
+ });
837
+ }
838
+ if (trackRadioButtons && (dep.yesRadio || dep.noRadio)) {
832
839
  [dep.yesRadio, dep.noRadio].forEach((radio) => {
833
- radio?.on("change", () => this.setRequiredLevel(isRequired(this)));
840
+ radio?.on("change", handleChange);
834
841
  });
835
842
  }
836
843
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powerpagestoolkit",
3
- "version": "2.5.409",
3
+ "version": "2.5.4111",
4
4
  "description": "Reference, manipulate, and engage with Power Pages sites through the nodes in the DOM; use a variety of custom methods that allow customizing your power pages site quicker and easier. ",
5
5
  "main": "./dist/bundle.js",
6
6
  "types": "./dist/index.d.ts",