Widgets Factory

Introduction

@itsy-ui/core package exposes a singleton WidgetsFactory instance that is used to inject loaders and registry services.

WidgetsFactory has the following features,

  • UI widgets factory - Used in JSON schema based rendering thru <SchemaContainer schema={} />.
  • Loaders - These are ES6 classes that registers services and use these loaders to inject JavaScript code. The most commonly used loaders is the DataLoaderFactory. Below is a sample code to get the ICustomStateMachineProvider,

    const dataLoader = WidgetsFactory.instance.services["DataLoaderFactory"] as DataLoaderFactory;
    const customStateProvider: ICustomStateMachineProvider = dataLoader.getLoader('customStateProvider') as ICustomStateMachineProvider;
    

UI Widget Factory

Use the WidgetsFactory to register a React widget and be able to consume using JSON schema. Below is an example ItsyLabel widget that is a Shell widget,

import { SchemaContainer, WidgetsFactory, withReducer } from "@itsy-ui/core";
import * as React from "react";
import { doLabelBeforeRefresh, doLabelRefresh } from "./actions";
import reducer from "./reducer";

import stateJSON from "./state.json";

class LabelWidget extends React.Component<any> {

	_getLabelUIControlSchema() {
		const { title, headerTag, headerSize, style, alignText, className } = this.props.schema;
		const labelUIControlSchema = {
			name: `label-ui-control`,
			properties: {
				"ui:widget": "label_control",
				title: this.props.title !== undefined && this.props.title !== "" ? this.props.title : title,
				headerSize,
				headerTag,
				style,
				alignText,
				className
			},
		};
		return <SchemaContainer schema={labelUIControlSchema} />;
	}

	render() {
		return (
			this._getLabelUIControlSchema()
		);
	}
}

const mapDispatchToProps = (dispatch) => {
	return {
		onLabelBeforeRefresh: (event) => dispatch(doLabelBeforeRefresh(event)),
		onLabelRefresh: (event) => dispatch(doLabelRefresh(event)),
	};
};

const ItsyLabel = withReducer("LabelWidget", reducer, mapDispatchToProps, stateJSON)(LabelWidget);
ItsyLabel.displayName = "LabelWidget";

WidgetsFactory.instance.registerFactory(ItsyLabel);
WidgetsFactory.instance.registerControls({
	label: "LabelWidget",
});

export default ItsyLabel;

Loaders

Two most important loaders are,

  • Command Manager
  • Custom State Machine Provider

In this section we learn about them.

CommandManager

CommandManager allows to register all the custom commands which is used by command driven controls like Button or Toolbar. Below is an example code that explains how to implement a custom command, and in this code we are implementing a logout command,

import { CommandOptions, DataLoaderFactory, ICommandManager, IAuthService } from "@itsy-ui/core";

const dataLoader = WidgetsFactory.instance.services["DataLoaderFactory"] as DataLoaderFactory;
const commandManager = dataLoader.getLoader<ICommandManager>("commandManager");

const logoutCommand: CommandOptions<any> = {
	canExecute: (_data: any) => {
		return true;
	},
	execute: async (_data: any, _transition: any) => {
		const authService = dataLoader.getLoader<IAuthService>("auth");
		dataLoader.registerLoader({
			"datasource": null
		});
		authService.logout();
		window.location.reload();
	},
};
commandManager.registerCommand("logout", {}, logoutCommand);

CustomStateMachineProvider

The state machine provider maintains the state transitions for overrides. It maintains the override transitions based on contextPath that is set to the widget, and follows a hierarchical structure.

An example is shown below,

const dataLoader = WidgetsFactory.instance.services["DataLoaderFactory"] as DataLoaderFactory;
const customStateProvider: ICustomStateMachineProvider = dataLoader.getLoader('customStateProvider') as ICustomStateMachineProvider;

// this function is called from AppContainer on initial load
function doCustomBeforeCounterInc(evt: any) {
  return async (_getState: any, _dispatch: any, transition: any) => {
    console.log(`Custom override hitting here with ${JSON.stringify(evt)}`);
	// complete the transition
    transition({
      type: "UP_COUNT"
    });
  }
}
// Define a custom transition state
const counterStateMachineOverride = {
  stateJSON: {
    "name": "counterState",
    "states": {
       // Below transition should be available in the original widget 
      "beforeCounterInc": {
        "onEntry": [
          "onCustomBeforeCounterInc"
        ],
        "on": {
          "UP_COUNT": "counterInc"
        }
      },
    },
  },
  mapDispatchToAction: (dispatch: any) => {
    return {
      onCustomBeforeCounterInc: (evt: any) => dispatch(doCustomBeforeCounterInc(evt)),
    };
  },
};
// register the custom override with Control Name, ContextPath in which the
// override needs to occur and the CustomStateMachine object.
customStateProvider.registerCustomStateMachine("Counter", { pageId: "app" }, counterStateMachineOverride);