Skip to content

Latest commit

 

History

History
385 lines (294 loc) · 10.4 KB

README.md

File metadata and controls

385 lines (294 loc) · 10.4 KB

PowerPages Tool Kit

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.

Features

  • Powerful DOM element manipulation and reference management
  • Type-safe DataVerse API operations
  • Automatic value synchronization for form elements
  • Advanced conditional rendering and validation
  • Radio button and checkbox handling
  • Event management with proper TypeScript typing
  • Mutation observer integration for dynamic content
  • Tooltip and label management utilities

Installation

npm install powerpagestoolkit

Core Modules

DOMNodeReference

A powerful class for managing DOM elements with automatic value synchronization and event handling.

Basic Usage

DOMNodeReferences are instantiated with the help of the following factory function: createRef

createRef(
  target: HTMLElement | string,
  options: {
    multiple: (() => boolean) | boolean = false,
    root: HTMLElement,
    timeout: number
  }
): Promise<DOMNodeReference | DOMNodeReference[]>;

createRef takes two main arguments:

Property Type Details
target
string | HTMLElement
Use standard querySelector syntax to target an element, or elements in the DOM, or pass in an instance of the element itself to create a reference.
options
{
  multiple: () => boolean | boolean,
  root: HTMLElement,
  timeout: number
}
Provides advanced configurations for niche scenarios, such as async DOM element loading, returning arrays of elements, or specifying the parent to search within for the target node.

Import the utility function for creating DOMNodeReference(s)

import { createRef } from "powerpagestoolkit";

Instantiate one, or multiple instances of a DOMNodeReference, and optionally configure advanced options

// Create a single reference
const node = await createRef("#myElement");

// Create multiple references
const nodes = await createRef(".my-class", { multiple: true });

/******************/
// ADVANCED OPTIONS
// in the event that you need to be more granular with how you are targeting
// and retrieving elements, there are additional options
// If the node you are targeted is not available at the initial execution
// of the script, set a timeout for 2 seconds
const node2 = await createRef("#target", { timeout: 2000 });

// need to target a node within a specific node? use that node as the root
const otherElement = document.getElementById("id");
const node3 = await createRef("#target", { root: otherElement });

// implement all options:
const nodes2 = await createRef("#target", {
  multiple: true,
  timeout: 4000,
  root: otherElement,
});

Properties

Property Type Description
element HTMLElement The referenced DOM element
value any Current synchronized value of the element
isLoaded boolean Element load status
target HTMLElement | string Original target selector or element
yesRadio DOMNodeReference | null Reference to 'yes' radio (for boolean fields)
noRadio DOMNodeReference | null Reference to 'no' radio (for boolean fields)
checked boolean Checkbox/radio checked state

Key Methods

Event Handling
// Add event listener with proper 'this' context
// uses standard eventListener API, and so supports all DOM events
node.on("change", function (e) {
  console.log("Current value:", this.value);
});

node.on("click", function (e) {
  console.log(this, " has been clicked");
});

...
Visibility Control
// Basic visibility
node.hide();
node.show();

Advanced conditional rendering

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

Method signature:

configureConditionalRendering(
    condition: () => boolean,
    dependencies?: DOMNodeReference[],
    clearValuesOnHide: boolean = true
  ): DOMNodeReference /* Instance of this returned
  for optional method chaining */

Example implementation:

node.configureConditionalRendering(
  function () // Function to evaluate wether this node should be visible or not
  {
    return otherNode.value === "some value";
  },
  [otherNode] /* Dependency array | if the values or visibility of these
  change, the function is re-evaluated */,

  true /* should the values in the targeted elements (this.element)
  be cleared if this node is hidden? Default = true */
);
Validation and Requirements

This utility enhances PowerPages forms by adding dynamic field validation and conditional requirements based on other field values.

Method signature:

configureValidationAndRequirements(
  isRequired: () => boolean,
  isValid: () => boolean,
  fieldDisplayName: string,
  dependencies: DOMNodeReference[]
): DOMNodeReference; /* instance of this is returned for optional
 method chaining */

Example implementation:

node.configureValidationAndRequirements(
  // Make field required only when "Yes" is checked
  () => dependentNode.yesRadio?.checked ?? false,

  // Basic validation: ensure field isn't empty
  function () {
    return this.value != null && this.value !== "";
  },

  "Contact Phone", // Shows in error message: "Contact Phone is required"

  [dependentNode] // Revalidate when dependentNode changes
);
Element Manipulation

Value management

// set a static value
node.setValue("new value");

// or set a value by using some sort of logic
node.setValue(() => {
  if (true) {
    return "value";
  } else return "default";
});

// Sync with DOM
node.updateValue();

// Clear the value for both the instance and the target element
node.clearValue();

Content manipulation

node.setInnerHTML("<span>New content</span>");
node.append(childElement);
node.prepend(headerElement);
node.after(siblingElement);
node.before(labelElement);

Styling

node.setStyle({
  display: "block",
  color: "red",
});

Enabling/Disabling inputs

node.disable();
node.enable();
Label and Tooltip Management
// LABEL AND INFO OPERATIONS
const label = node.getLabel();
// appends a tooltip to the label associated with the element targeted by 'this'
node.addLabelTooltip(
  "Helper text",
  /* Optionally pass in css styles to customize the tooltip icon*/
  { color: "orange", fontSize: "30px" }
);
// appends a tooltip directly to the element targeted by 'this'
node.addTooltip(
  "Inline helper",
  /* Optionally pass in css styles to customize the tooltip icon*/
  { color: "orange", fontSize: "30px" }
);

Example:

import { createRef } from "powerpagestoolkit";

const title = await createRef("#myTitle");

title.addTooltip("This is an Example of a tooltip!", { color: "red" });

Example

DataVerse API

Perform secure API calls to DataVerse from your PowerPages site. This method implements the shell deferred token to send requests with __RequestVerificationToken

Create Records

await API.createRecord("accounts", {
  name: "Gypsum LLC",
  type: "Vendor",
})
  .then((recordId) => {
    console.log("Created record:", recordId);
  })
  .catch((error) => {
    console.error("Creation failed:", error);
  });

Get Records

// Single record
const record = await API.getRecord(
  "accounts",
  "record-guid",
  "select=name,accountnumber"
);

// Multiple records
const records = await API.getMultiple(
  "contacts",
  '$filter=firstname eq "Jane"&$select=firstname,lastname'
);

Update Record

await API.updateRecord("contacts", "record-guid", {
  name: "Jane Smith",
  email: "[email protected]",
});

Best Practices

  1. Always await DOMNodeReference creation:
const node = await createRef("#element");
  1. Include all referenced nodes in dependency arrays:
node.configureConditionalRendering(
  () => dependentNode.value === "test",
  [dependentNode] // Required!
);
  1. Use TypeScript for better type safety and IntelliSense support.

  2. Use proper error handling with API operations:

/* optionally await */ API.createRecord(/*...*/)
  .then((recordId) => {})
  .catch((error) => {
    // handle your errors appropriately
  });

TypeScript Support

The package includes full TypeScript definitions and type safety. Use TypeScript for the best development experience and catch potential errors at compile time.

Contributing

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.

License

This project is licensed under the AGPL-3.0 License - see the LICENSE file for details.

Funding

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