MicroStrategy ONE

Modify the Plugin Structure for the React Format Panel

This feature is available starting in MicroStrategy 2021 Update 3.

Before moving to the new Format panel structure, you must modify the plugin structure. This section details what changes need to be made.

Modify Logic for Managing Properties

Create a properties folder to contain the files that handle the logic responsible for managing property definitions and values. Download the entire folder, with the same properties used in the sample plugin, in properties.zip. The properties folder contains the following files:

propertiesInfo.js

This file contains definitions for all used properties. All definitions are stored inside the PROPERTIES_INFO variable. The variable is also used inside Format panel logic.

Each property is defined according to the template below.

Copy
 SHOW_METRIC: {                  // SHOW_METRIC - enum that is binded to the property
    name: "showMetric",          // name - name that represents the enum
    defaultValue: true,          // defaultValue - initial value for the property
  },

adjustProperties.js

This file contains the logic responsible for modifying property values before they are passed to render functions, if necessary.

getDefaultProperties.js

This file contains the logic responsible for dynamically setting the default values for properties, if necessary.

Modify Files and Functions that Initialize and Render the Plugin

Create a src/utils folder to contain all of the files and functions used during various stages of rendering and initializing the plugin. This folder contains enhanceHost.js, which adds new methods to the main visualization object (host) to enable the management of properties using one unified property.

Add Format Panel Icons

Add example icons for the Format panel components using src/images/format_panel_icons.svg.

Update the CSS

Update src/vis.less to add styles for rendered components, components inside the Format panel, and all necessary icons. This CSS file is used in the sample visualization.

Modify the Main File that Initializes and Renders the Plugin

This section details the src/SampleViz.js file, which is the main file used to initialize the plugin, as well as render the visualization. A few modifications need to be made to this file. All of these modifications are included in the sample plugin.

Imports

Import all necessary functions at the top of the file.

Copy
import { adjustProperties } from "./properties/adjustProperties";
import { getDefaultProperties } from "./properties/getDefaultProperties";
import { enhanceHost } from "./utils/enhanceHost";

init function

In the init function, add new methods that enhance the host.

Copy
init(props) {
      this._super(props);
      enhanceHost(this);
      const defaultPropertyValues = getDefaultProperties();
      this.setDefaultPropertyValues(defaultPropertyValues);
    },

clearDomNode function

Add the clearDomNode function before the plot function to clear the DOM node.

Copy
clearDomNode() {
      const host = this;
      const { domNode } = host;
      domNode.innerHTML = "";
    },

appliedProperties argument

Add this argument to the plot function and set the default value to null.

Copy
plot(appliedProperties = null) {

clearDomNode in the plot function

At the beginning of the plot function, add new lines to clear the DOM node before each render.

Copy
const host = this;
host.clearDomNode();

Add functions to incorporate property management

Add functions to enable better management of property values.

Copy
let properties;
if (appliedProperties) {
   properties = adjustProperties(appliedProperties, host);
else {
  const savedProperties = JSON.parse(host.getProperty("unifiedProperty"));
  properties = adjustProperties(savedProperties, host);
}

Modify code for rendering the plugin

Adjust the code responsible for rendering the content of the plugin according to your needs. For the sample plugin, make the changes shown below.

  • Remove unused imports from the top of the file:

    Copy
    import * as d3 from 'd3';
    import cloud from 'd3-cloud';
  • Remove the section formerly responsible for rendering the content in the plot function.

    Copy
    const minFont = this.getProperty('minFont');
          const maxFont = this.getProperty('maxFont');
          const numOfWords = this.getProperty('numOfWords');
          const spiral = this.getProperty('spiral');
          const textFont = this.getProperty('textFont');

          let data = graphicModels
            .sort((a, b) => b.value - a.value)
            .slice(0, Math.max(graphicModels.length, numOfWords));

          const scaleFont = d3.scaleLinear()
            .domain([data[0].value, data[data.length - 1].value])
            .range([maxFont, minFont]);
          const width = parseInt(this.width, 10);
          const height = parseInt(this.height, 10);
          const container = d3.select(this.domNode);

          data = data.map(d => ({ ...d, size: scaleFont(d.value) }));

          cloud()
            .size([width, height])
            .timeInterval(1000)
            .words(data)
            .padding(1)
            .font(textFont.fontFamily)
            .fontSize(d => d.size)
            .spiral(spiral.ellipse === 'true' ? 'archimedean' : 'rectangular')
            .on('end', words => {
              const hashStr2Color = str => {
                let num = 0;
                for (let i = 0; i < str.length; i++) {
                  num += str.charCodeAt(i);
                }
                return d3.schemePaired[num % d3.schemePaired.length];
              };

              container
                .html(null)
                .append('svg')
                .attr('width', width)
                .attr('height', height)
                .append('g')
                .attr('transform', `translate(${width / 2},${height / 2})`)
                .selectAll('text')
                .data(words)
                .enter()
                .append('text')
                .style('font-size', d => `${d.size}px`)
                .style('font-family', textFont.fontFamily)
                .style('fill', d => hashStr2Color(d.text))
                .attr('text-anchor', 'middle')
                .attr('transform', d => `translate(${[d.x, d.y]})rotate(${d.rotate})`)
                .text(d => d.text);
            })
            .start();
  • Add the new code for rendering the content in the plot function.

    Copy
    const {
            showAttribute,
            showMetric,
            attributeBackgroundColor,
            metricBackgroundColor,
            attributeFontFamily,
            attributeFontStyle,
            attributeFontColor,
            attributeFontSize,
            metricFontFamily,
            metricFontStyle,
            metricFontColor,
            metricFontSize,
          } = properties;

          const addStyle = (
            element,
            fontFamily,
            fontColor,
            fontSize,
            fontStyle
          ) => {
            element.style.fontFamily = fontFamily;
            element.style.color = fontColor;
            element.style.fontSize = `${fontSize}px`;
            const [isBold, isItalic, isUnderlined, isCrossedOut] = fontStyle;
            element.style.fontWeight = isBold ? "bold" : "normal";
            element.style.fontStyle = isItalic ? "italic" : "normal";
            const textDecorationOptions = [];
            if (isUnderlined) textDecorationOptions.push("underline");
            if (isCrossedOut) textDecorationOptions.push("line-through");
            const textDecoration = textDecorationOptions.join(" ");
            element.style.textDecoration = textDecoration;
          };

          graphicModels.forEach((graphicModel) => {
            const container = document.createElement("div");
            container.classList.add("container");
            const attributeContainer = document.createElement("div");
            attributeContainer.classList.add("attributeContainer");
            attributeContainer.style.backgroundColor = attributeBackgroundColor;
            const attributeContainerText = document.createTextNode(
              showAttribute ? graphicModel.text : ""
            );
            addStyle(
              attributeContainer,
              attributeFontFamily,
              attributeFontColor,
              attributeFontSize,
              attributeFontStyle
            );
            attributeContainer.appendChild(attributeContainerText);

            const metricContainer = document.createElement("div");
            metricContainer.classList.add("metricContainer");
            metricContainer.style.backgroundColor = metricBackgroundColor;
            const metricContainerText = document.createTextNode(
              showMetric ? graphicModel.value : ""
            );
            addStyle(
              metricContainer,
              metricFontFamily,
              metricFontColor,
              metricFontSize,
              metricFontStyle
            );
            metricContainer.appendChild(metricContainerText);

            container.appendChild(attributeContainer);
            container.appendChild(metricContainer);

            host.domNode.appendChild(container);
          });

Internationalization (i18n)

  1. To internationalize plugin text, you must populate the appropriate files with the translated strings. See Add New Custom Translations for more information.

  2. Once you have added the translated strings, see step 6 in Add New Custom Translations to add the strings to the code.

  3. Add the function responsible for loading translated strings. In the sample plugin, add the following line just after the imports at the top of the file.

    Copy
    mstrmojo.requiresDescsWPrefix("SampleViz.", 10, 11, 12, 13);

In the sample plugin, translations have already been added to the Format panel. However, you can also internationalize error messages, labels included in the drop zone area, or visualization text.