powerpagestoolkit 2.3.311 → 2.4.113

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
@@ -1,317 +1,242 @@
1
1
  # PowerPages Tool Kit
2
2
 
3
- This package provides utilities for managing and interacting with the DOM and making AJAX requests to a DataVerse API. It includes the `API` module for handling CRUD operations and the `DOMNodeReference` class for seamless DOM manipulations.
3
+ A TypeScript/JavaScript utility package for seamless DOM manipulation and DataVerse API interactions in PowerPages applications. This toolkit provides robust DOM element management and standardized DataVerse CRUD operations with full TypeScript support.
4
+
5
+ ## Features
6
+
7
+ - Powerful DOM element manipulation and reference management
8
+ - Type-safe DataVerse API operations
9
+ - Automatic value synchronization for form elements
10
+ - Advanced conditional rendering and validation
11
+ - Radio button and checkbox handling
12
+ - Event management with proper TypeScript typing
13
+ - Mutation observer integration for dynamic content
14
+ - Tooltip and label management utilities
4
15
 
5
16
  ## Installation
6
17
 
7
- After installing
18
+ ```bash
19
+ npm install powerpagestoolkit
20
+ ```
21
+
22
+ ## Core Modules
8
23
 
9
- `npm install powerpagestoolkit`
24
+ ### DOMNodeReference
10
25
 
11
- You can then import into your JavaScript files as follows:
26
+ A powerful class for managing DOM elements with automatic value synchronization and event handling.
12
27
 
13
- ```javascript
28
+ #### Basic Usage
29
+
30
+ ```typescript
14
31
  import {
15
- API,
16
32
  createDOMNodeReference,
17
33
  createMultipleDOMNodeReferences,
18
34
  } from "powerpagestoolkit";
19
- ```
20
35
 
21
- # Modules
36
+ // Both methods support standard querySelector syntax:
22
37
 
23
- ### `DOMNodereference`
38
+ // Create a single reference
39
+ const node = await createDOMNodeReference("#myElement");
24
40
 
25
- The `DOMNodeReference` module simplifies DOM element management. It provides functionalities for creating and interacting with DOM elements:
26
-
27
- #### Usage
41
+ // Create multiple references
42
+ const nodes = await createMultipleDOMNodeReferences(".my-class");
43
+ ```
28
44
 
29
- - **`createDOMNodeReference(selector)`**: Creates a `DOMNodeReference` instance for a single DOM element specified by a CSS selector or HTMLElement. Returns a `DOMNodeReference` instance.
45
+ #### Properties
30
46
 
31
- - **`createMultipleDOMNodeReferences(selector)`**: Creates multiple `DOMNodeReference` instances for all elements matching the specified CSS selector. Returns an array of `DOMNodeReference` instances.
47
+ | Property | Type | Description |
48
+ | -------- | ------------------------ | --------------------------------------------- |
49
+ | element | HTMLElement | The referenced DOM element |
50
+ | value | any | Current synchronized value of the element |
51
+ | isLoaded | boolean | Element load status |
52
+ | target | HTMLElement \| string | Original target selector or element |
53
+ | yesRadio | DOMNodeReference \| null | Reference to 'yes' radio (for boolean fields) |
54
+ | noRadio | DOMNodeReference \| null | Reference to 'no' radio (for boolean fields) |
55
+ | checked | boolean | Checkbox/radio checked state |
32
56
 
33
- `selector` uses standard ED6 `document.querySelector()` syntax. For more information, read [here](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
57
+ #### Key Methods
34
58
 
35
- ```javascript
36
- // single instance of DOMNodeReference
37
- const node = await createDOMNodeReference("#my-element");
59
+ ##### Event Handling
38
60
 
39
- node.onceLoaded(() => {
40
- console.log("Element is loaded: ", node.element);
61
+ ```typescript
62
+ // Add event listener with proper 'this' context
63
+ node.on("change", function (e) {
64
+ console.log("Current value:", this.value);
41
65
  });
42
66
 
43
- // to imitate 'querySelectorAll', and return an array of DOMNodeReferences
44
- const nodeArray = await createMultipleDOMNodeReferences('div[class="row"]');
45
-
46
- nodeArray.forEach((node) => {
47
- node.oneLoaded(() => {
48
- console.log("Element loaded: ", node.element")
49
- })
50
- })
67
+ // Wait for element to be loaded
68
+ node.onceLoaded((instance) => {
69
+ console.log("Element ready:", instance.element);
70
+ });
51
71
  ```
52
72
 
53
- ##### Available Properties
54
-
55
- These properties are public and can be used in any custom logic/configurations
73
+ ##### Visibility Control
56
74
 
57
75
  ```typescript
58
- target: HTMLElement | string;
59
- element: HTMLElement;
60
- isLoaded: boolean;
61
- value: any;
62
- /**
63
- * If the element targeted is the main input for a yes/no radio control,
64
- * yesRadio and noRadio will be available as properties of 'this'
65
- */
66
- yesRadio: DOMNodeReference;
67
- noRadio: DOMNodeReference;
68
- // and if 'this' is the instance of a yesRadio or noRadio
69
- // checked will represent wether the radio has been checked or not
70
- checked: boolean;
76
+ // Basic visibility
77
+ node.hide();
78
+ node.show();
79
+
80
+ // Advanced conditional rendering
81
+ node.configureConditionalRendering(
82
+ // Function to evaluate wether this node should be visible or not
83
+ function () {
84
+ return otherNode.value === "expected";
85
+ },
86
+ [otherNode] // Dependency array | if the values or visibility of these change, the function is re-evaluated
87
+ );
71
88
  ```
72
89
 
73
- ##### Methods
74
-
75
- Here are the key methods you can use with a DOMNodeReference instance:
76
-
77
- ```javascript
78
-
79
- /********/
80
- // VISIBILITY / ACCESSIBILITY
81
-
82
- // Hides the associated DOM element.
83
- hide()
84
-
85
- // Shows the associated DOM element.
86
- show()
87
-
88
- /**
89
- * advanced visibility control in the case you need to apply
90
- * custom logic to the visibility of an element
91
- */
92
- toggleVisibility(shouldShow: boolean | () => boolean)
93
-
94
- /**
95
- * Configures conditional rendering for the target element
96
- * based on a condition and the visibility of one or more trigger elements.
97
- *
98
- * @param {(this: DOMNodeReference) => boolean} condition -
99
- * A function that returns a boolean to determine the visibility
100
- * of the target element. If `condition()` returns true, the
101
- * element is shown; otherwise, it is hidden.
102
- * @param {Array<DOMNodeReference>} dependencies - An array
103
- * of `DOMNodeReference` instances. Event listeners are
104
- * registered on each to toggle the visibility of the
105
- * target element based on the `condition` and the
106
- * visibility of the target node.
107
- */
108
- configureConditionalRendering(
109
- condition: (this: DOMNodeReference) => boolean,
110
- dependencies: Array<DOMNodeReference>
111
- )
112
-
113
-
114
- // EXAMPLE:
115
- const your_node = await createDOMNodeReference("#element_id")
116
- const other_node = await createDOMNodeReference(".element_class")
117
-
118
- your_node.configureConditionalRendering(() =>
119
- other_node.value == "3",
120
- /* your_node will only be
121
- visible when the value of other_node is "3"
122
- */
123
- [other_node]
124
- /* and we have to include any DOMNodeReferences used
125
- in the evaluation logic, so that changes to them can
126
- be watched and the condition evaluated again
127
- */
128
- );
129
-
130
-
131
- /**
132
- * Sets up validation and requirement rules for the field.
133
- * This function dynamically updates the field's required status
134
- * and validates its input based on the specified conditions.
135
- *
136
- * @param {function(this: DOMNodeReference): boolean} isRequired
137
- * A function that determines whether the field should be required.
138
- * Return `true` if required, `false` to not be required.
139
- * @param {function(this: DOMNodeReference): boolean} isValid
140
- * A function that checks if the field's input is valid.
141
- * Return `true` if validation satisfied, `false` if not.
142
- * @param {string} fieldDisplayName - The name of the field, used
143
- * in error messages if validation fails.
144
- * @param {Array<DOMNodeReference>} [dependencies]
145
- * Other fields that this field’s requirement depends on. When
146
- * these Nodes or their values change, the required status
147
- * of this field is re-evaluated. Make sure any DOMNodeReference
148
- * used in `isRequired` or `isValid` is included in this array.
149
- */
150
- configureValidationAndRequirements(
151
- isRequired: (this: this) => boolean,
152
- isValid: (this: this) => boolean,
153
- fieldDisplayName: string,
154
- dependencies: Array<DOMNodeReference>
155
- )
156
-
157
- // EXAMPLE:
158
- const your_node = await createDOMNodeReference("#element_id")
159
- const other_node = await createDOMNodeReference(".element_class")
160
-
161
- your_node.configureValidationAndRequirements(
162
- () => other_node.yesRadio.checked,
163
- /* if 'yes' is checked for this other node,
164
- this function will evaluate to true,
165
- meaning that 'your_node' will be required */
166
-
167
- function () {
168
- /* important to use standard 'function' declaration,
169
- instead of arrow function when needing to
170
- access 'this' (the instance of 'your_node') */
171
-
172
- if (other_node.yesRadio.checked) {
173
- // when other_node radio is checked 'yes'
174
- return this.value; // this is only 'valid' if it has a value
175
- } else return true;
176
- },
177
- "Your Field Name",
178
- [other_node]
179
- /* since our conditions depend on
180
- 'other_node' it must be included in the dependency
181
- array so that the requirement conditions can be
182
- re-evaluated when the value of 'other_node' changes */
183
- );
184
-
185
-
186
- /* sets the elements 'disabled' to true - useful for inputs
187
- that need to be enabled/disabled conditionally */
188
- disable()
189
-
190
- // Sets the element 'disabled' to false
191
- enable()
192
- ```
90
+ ##### Validation and Requirements
193
91
 
194
- ```javascript
195
- // OTHER METHODS
92
+ ```typescript
93
+ node.configureValidationAndRequirements(
94
+ // Function to evaluate if this field should be required
95
+ function () {
96
+ return dependentNode.yesRadio?.checked ?? false;
97
+ },
98
+ // Function to evaluate if the data in this field is valid
99
+ function () {
100
+ return this.value != null && this.value !== "";
101
+ },
102
+ "Field Display Name", // the name that will be displayed along side a validation failure message
103
+ [dependentNode] // Dependency array | if the requirement level of these change, this element is re-evaluated
104
+ );
105
+ ```
196
106
 
197
- // Sets the value of the associated HTML element.
198
- setValue(value: any)
107
+ ##### Element Manipulation
199
108
 
200
- // Sets the inner HTML content of the associated HTML element.
201
- setTextContent(text: string)
109
+ ```typescript
110
+ // Value management
111
+ node.setValue("new value"); // set a static value
112
+ // or set a value by using some sort of logic
113
+ node.setValue(() => {
114
+ if (true) {
115
+ return "value";
116
+ } else return "default";
117
+ });
118
+ node.updateValue(); // Sync with DOM
119
+
120
+ // Content manipulation
121
+ node.setInnerHTML("<span>New content</span>");
122
+ node.append(childElement);
123
+ node.prepend(headerElement);
124
+ node.after(siblingElement);
125
+ node.before(labelElement);
126
+
127
+ // Styling
128
+ node.setStyle({
129
+ display: "block",
130
+ color: "red",
131
+ });
202
132
 
203
- // set any style attribute for 'this' with standard CSS style declaration
204
- setStyle(options: Partial<CSSStyleDeclaration>): void;
133
+ // State management
134
+ node.disable();
135
+ node.enable();
136
+ ```
205
137
 
206
- // Appends child elements to the associated HTML element.
207
- append(...elements: HTMLElement[])
138
+ ##### Label and Tooltip Management
208
139
 
209
- // Inserts elements after the associated HTML element.
210
- after(...elements: HTMLElement[])
140
+ ```typescript
141
+ // LABEL AND INFO OPERATIONS
142
+ const label = node.getLabel();
143
+ node.appendToLabel(infoElement);
144
+ // appends a tooltip to the label associated with the element targeted by 'this'
145
+ node.addLabelTooltip("Helper text");
146
+ // appends a tooltip directly to the element targeted by 'this'
147
+ node.addTooltip("Inline helper");
148
+ ```
211
149
 
212
- // Retrieves the label associated with the HTML element.
213
- getLabel(): HTMLElement | null
150
+ ### DataVerse API
214
151
 
215
- // Appends child elements to the label associated with the HTML element.
216
- appendToLabel(...elements: HTMLElement[])
152
+ Type-safe wrapper for DataVerse API operations.
217
153
 
218
- // Create an event listener on the target element. Provide access to 'this'
219
- // in the event handler function
220
- on(eventType: string, eventHandler: (this: DOMNodeReference) => void)
154
+ #### Create Record
221
155
 
222
- // Unchecks both yes and no radio buttons if they exist.
223
- uncheckRadios()
156
+ ```typescript
157
+ const recordId = await API.createRecord("accounts", {
158
+ name: "New Account",
159
+ type: "Customer",
160
+ })
161
+ .then(() => {
162
+ console.log("Created record:", recordId);
163
+ })
164
+ .catch(() => {
165
+ console.error("Creation failed:", error);
166
+ });
167
+ ```
224
168
 
225
- // Adds a tooltip to the label associated with the HTML element.
226
- addLabelTooltip(text: string)
169
+ #### Get Records
227
170
 
228
- // Adds a tooltip with the specified text to the element
229
- addTooltip(text: string)
171
+ ```typescript
172
+ // Single record
173
+ const record = await API.getRecord(
174
+ "accounts",
175
+ "record-guid",
176
+ "select=name,accountnumber"
177
+ );
178
+
179
+ // Multiple records
180
+ const records = await API.getMultiple(
181
+ "contacts",
182
+ '$filter=firstname eq "Jane"&$select=firstname,lastname'
183
+ );
184
+ ```
230
185
 
231
- // Executes a callback function once the element is fully loaded.
232
- onceLoaded(callback: (instance: DOMNodeReference) => void)
186
+ #### Update Record
233
187
 
188
+ ```typescript
189
+ await API.updateRecord("contacts", "record-guid", {
190
+ name: "Jane Smith",
191
+ email: "jane@example.com",
192
+ });
234
193
  ```
235
194
 
236
- ### `API`
195
+ ## Best Practices
237
196
 
238
- The `API` module provides functions for creating and retrieving records from a DataVerse. It includes the following methods:
197
+ 1. Always await DOMNodeReference creation:
239
198
 
240
- - **`createRecord(schema)`**: Creates a new record in the DataVerse using the provided schema instance. Returns a Promise that resolves with the record ID or rejects with an error.
241
- - **`getRecord(tableSetName, recordID, selectColumns)`**: Retrieves a specific record from the DataVerse. Returns a Promise that resolves with the retrieved record or rejects with an error.
199
+ ```typescript
200
+ const node = await createDOMNodeReference("#element");
201
+ ```
242
202
 
243
- - **`getMultiple(tableSetName, queryParameters)`**: Retrieves multiple records from the DataVerse based on specified query parameters. Returns a Promise that resolves with the list of retrieved records or rejects with an error.
203
+ 2. Include all referenced nodes in dependency arrays:
244
204
 
245
- #### Usage
205
+ ```typescript
206
+ node.configureConditionalRendering(
207
+ () => dependentNode.value === "test",
208
+ [dependentNode] // Required!
209
+ );
210
+ ```
246
211
 
247
- ###### 1. Creating a Record
212
+ 3. Use TypeScript for better type safety and IntelliSense support.
248
213
 
249
- To create a new record in the DataVerse, you can use the `createRecord` method. This method takes an instance of a schema class containing the data for the record.
214
+ 4. Handle loading states:
250
215
 
251
- ```javascript
252
- // Assuming you have a schema class defined
253
- const schema = new YourSchemaClass({
254
- name: "Sample Record",
255
- description: "This is a sample record for demonstration.",
216
+ ```typescript
217
+ node.onceLoaded((instance) => {
218
+ // Safe to manipulate the element here
256
219
  });
257
-
258
- API.createRecord(schema)
259
- .then((recordId) => {
260
- console.log("Record created successfully with ID:", recordId);
261
- })
262
- .catch((error) => {
263
- console.error("Error creating record:", error);
264
- });
265
220
  ```
266
221
 
267
- ###### 2. Getting a Single Record
222
+ 5. Use proper error handling with API operations:
268
223
 
269
- To retrieve a specific record from the DataVerse, use the `getRecord` method. You need to provide the table set name and the record ID.
270
-
271
- ```javascript
272
- const tableSetName = "accounts"; // The DataVerse table set name
273
- const recordID = "your-record-id"; // The GUID of the record to retrieve
274
-
275
- API.getRecord(tableSetName, recordID)
276
- .then((record) => {
277
- console.log("Retrieved record:", record);
278
- })
279
- .catch((error) => {
280
- console.error("Error retrieving record:", error);
281
- });
224
+ ```typescript
225
+ try {
226
+ await API.createRecord(/*...*/);
227
+ } catch (error) {
228
+ // Handle error appropriately
229
+ }
282
230
  ```
283
231
 
284
- ###### 3. Getting Multiple Records
232
+ ## TypeScript Support
285
233
 
286
- If you need to retrieve multiple records with specific query parameters, you can use the `getMultiple` method. This method accepts the table set name and optional query parameters for filtering.
234
+ The package includes full TypeScript definitions and type safety. Use TypeScript for the best development experience and catch potential errors at compile time.
287
235
 
288
- ```javascript
289
- const tableSetName = "contacts"; // The DataVerse table set name
290
- const queryParameters =
291
- "$filter=firstName eq 'John'&$select=firstName,lastName"; // OData query parameters
236
+ ## Contributing
292
237
 
293
- API.getMultiple(tableSetName, queryParameters)
294
- .then((records) => {
295
- console.log("Retrieved records:", records);
296
- })
297
- .catch((error) => {
298
- console.error("Error retrieving records:", error);
299
- });
300
- ```
301
-
302
- ##### Example Schema Class
238
+ Contributions are welcome, feel free to create a pull request with enhancements. Please include an explanation of the changes made. All pull requests will be reviewed by the project owner.
303
239
 
304
- Here's a simple example of a schema class that you might use with the createRecord method:
240
+ ## License
305
241
 
306
- ```javascript
307
- class YourSchemaClass {
308
- constructor(tableSetName, data) {
309
- this.setName = tableSetName;
310
- this.data = data;
311
- }
312
-
313
- value() {
314
- return JSON.stringify(this.data); // Convert data to JSON format for the API
315
- }
316
- }
317
- ```
242
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
package/dist/API.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  declare const API: {
2
2
  /**
3
- *
4
- * @param {Schema} schema an instance of a schema class, containing the desired information for the POST request
3
+ * @param tableSetName The dataverse set name for the table that you are updating a record in
4
+ * @param data The JSON of the fields and data that are to be updated on the targeted record
5
5
  * @returns a Promise resolving the successful results *[record id]* of the POST request, or rejecting the failed results *[error]* of the POST request.
6
6
  */
7
- createRecord(schema: Schema): Promise<string>;
7
+ createRecord(tableSetName: string, data: object): Promise<string>;
8
8
  /**
9
9
  *
10
10
  * @param tableSetName The DataVerse SET name of the table being queried
package/dist/bundle.js CHANGED
@@ -23,16 +23,16 @@ function safeAjax(ajaxOptions) {
23
23
  // src/API.ts
24
24
  var API = {
25
25
  /**
26
- *
27
- * @param {Schema} schema an instance of a schema class, containing the desired information for the POST request
26
+ * @param tableSetName The dataverse set name for the table that you are updating a record in
27
+ * @param data The JSON of the fields and data that are to be updated on the targeted record
28
28
  * @returns a Promise resolving the successful results *[record id]* of the POST request, or rejecting the failed results *[error]* of the POST request.
29
29
  */
30
- createRecord(schema) {
30
+ createRecord(tableSetName, data) {
31
31
  return new Promise((resolve, reject) => {
32
32
  safeAjax({
33
33
  type: "POST",
34
- url: `/_api/${schema.logicalName()}`,
35
- data: schema.value(),
34
+ url: `/_api/${tableSetName}`,
35
+ data: JSON.stringify(data),
36
36
  contentType: "application/json",
37
37
  success: function(response, status, xhr) {
38
38
  resolve(xhr.getResponseHeader("entityid"));
@@ -277,7 +277,11 @@ var DOMNodeReference = class _DOMNodeReference {
277
277
  this.value = this.element.value !== "" ? Number(this.element.value) : null;
278
278
  break;
279
279
  default:
280
- this.value = this.element.value;
280
+ if (this.element.classList.contains("decimal")) {
281
+ this.value = parseFloat(this.element.value);
282
+ } else {
283
+ this.value = this.element.value;
284
+ }
281
285
  break;
282
286
  }
283
287
  if (this.yesRadio instanceof _DOMNodeReference) {
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "powerpagestoolkit",
3
- "version": "2.3.311",
3
+ "version": "2.4.113",
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",
7
7
  "scripts": {
8
8
  "typecheck": "tsc",
9
9
  "node:build": "node build.js",
10
+ "clean": "rimraf dist",
10
11
  "build:types": "tsc --emitDeclarationOnly --declaration",
11
- "build": "npm run typecheck && npm run node:build && npm run build:types",
12
+ "build": "npm run clean && npm run typecheck && npm run node:build && npm run build:types",
12
13
  "dev": "tsc --watch"
13
14
  },
14
15
  "devDependencies": {
@@ -19,6 +20,7 @@
19
20
  "esbuild-css-modules-plugin": "^3.1.2",
20
21
  "eslint": "^8.57.1",
21
22
  "eslint-plugin-import": "^2.31.0",
23
+ "rimraf": "^6.0.1",
22
24
  "ts-loader": "^9.5.1",
23
25
  "typescript": "^5.6.3",
24
26
  "typescript-eslint": "^8.12.2"
@@ -70,5 +72,8 @@
70
72
  "./style.css": {
71
73
  "import": "./dist/bundle.css"
72
74
  }
75
+ },
76
+ "dependencies": {
77
+ "powerpagestoolkit": "^2.3.4"
73
78
  }
74
79
  }