Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member

Last time out we created the Hello World extension for SAP Lumira. If you have not created an SAP Lumira extension before I'd suggest reading that post first.

This time I will walk you through a more real world example showing how to bring in a D3 chart as extension for SAP Lumira, I will be using the D3 Bullet Chart as an example but the steps should be similar for other charts.

Updated for SAP Lumira 1.20 as there were improvements made for creating SAP Lumira  extensions since this was first written.

You will need to be comfortable with JavaScript and D3 to follow this guide as this is a more advanced example.

Step by step guide

What applications do you need?

You need to install:

  1. SAP Lumira 1.20 or later (which includes vizPacker), you can get your free Personal Edition here
  2. Google Chrome as vizPacker only works with Google Chrome

Prepare the D3 code for re-use

To save time I have already made these changes to a reusable local copy of the D3 Bullet Chart code (look for the // MDL comments), see the resources section (below), you can find the modified files on GitHub.

The Bullet Chart uses a simple JSON data structure, think of it being a JSON version of a CSV file.

[

  {"title":"Revenue","subtitle":"US$, in thousands","ranges":[150,225,300],"measures":[220,270],"markers":[250]},

  {"title":"Profit","subtitle":"%","ranges":[20,25,30],"measures":[21,23],"markers":[26]},

  {"title":"Order Size","subtitle":"US$, average","ranges":[350,500,600],"measures":[100,320],"markers":[550]},

  {"title":"New Customers","subtitle":"count","ranges":[1400,2000,2500],"measures":[1000,1650],"markers":[2100]},

  {"title":"Satisfaction","subtitle":"out of 5","ranges":[3.5,4.25,5],"measures":[3.2,4.7],"markers":[4.4]}

]

Originally vizPacker used this JSON format which suited the D3 Bullet Chart but not many other D3 charts. The majority of D3 charts use D3.csv data format, so vizPacker now uses the D3.csv data format as well to make things easier.

We will see later on in this blog how to convert/map from the D3.csv format to the JSON format for use with the Bullet Chart.

At a high level here are the changes I made:

    1. Save a local copy of the bullet chart index.html, bullet.js and bullets.json files from: Bullet Charts
    2. Update index.html:
      • We are going to mimic the vizPacker behavior
      • Add an SVG tag as the container for the extension into the page body and set the id to vis

        <svg id="vis"></svg>

        <script src="http://d3js.org/d3.v3.min.js"></script>

      • Add the vis variable and assign it from the vis SVG tag in the top of the script - this is the variable the vizPacker render code uses:

        <script>

        var vis = d3.select("#vis");

      • Hard code the test JSON data in a variable called fdata - this is what the vizPacker render function does by default
        • See the following optional step
      • Optional step - change the fdata JSON property names to make them more user friendly when they are shown in Lumira:
        • Combine title and subtitle into one property called Titles
        • Change measures to Actuals
        • Change markers to Target
        • Change ranges to Ranges
        • Update the bullet chart code to use the user friendly property names:

          var vis = d3.select("#vis");

          // MDL: embed JSON so we can test with Google Chrome locally.

          // MDL: renamed "data" to "fdata".

          // MDL: Combined "title" and "subtitle" as "Titles" array.

          // MDL: Renamed ranges to "Ranges".

          // MDL: Renamed measures to "Actuals".

          // MDL: Renamed markers to "Target".

          var fdata = [

                {"Titles":["Revenue","US$, in thousands"],"Ranges":[150,225,300],"Actuals":[220,270],"Target":[250]},

                {"Titles":["Profit","%"],"Ranges":[20,25,30],"Actuals":[21,23],"Target":[26]},

                {"Titles":["Order Size","US$, average"],"Ranges":[350,500,600],"Actuals":[100,320],"Target":[550]},

                {"Titles":["New Customers","count"],"Ranges":[1400,2000,2500],"Actuals":[1000,1650],"Target":[2100]},

                {"Titles":["Satisfaction","out of 5"],"Ranges":[3.5,4.25,5],"Actuals":[3.2,4.7],"Target":[4.4]}

              ];

          // MDL: end

      • Add a y position attribute that positions each bullet chart in the vis SVG tag
        • Note: The original bullet chart relied on a DIV container so it did not need the y position, it would automatically put the next bullet chart onto a new line in the web page, that does not happen when you are using SVG so you need to add the y position to get the same look
    • Remove the randomize button and code as that is not needed in Lumira
  1. Update bullet.js:
    • Add code so that the bullet chart can still draw when there are no Titles, Actuals, Ranges, or Target passed to it as that is what Lumira will do when you first create the chart
      • Calculate the max range even if there is no data
  2. Test the updated index.html file in Google Chrome:
    • To make sure it runs and visually looks the same as the original did

Create the D3 extension

Follow these steps to create and test your D3 Bullet Chart extension inside the vizPacker:

  1. Using Google Chrome
    Open: <installdir>\SAP Lumira\Desktop\utilities\vizPacker\vizPacker.html
  2. The LAYOUT DESIGN tab is where you define the ID, Name and Layout of your extension as well as edit the code
  3. Using the LAYOUT DESIGN tab, set the ID and name of your extension:
    • Click the outer gray box to display the chart visualization properties:
    • About IDs:
      • The ID naming convention is similar to making unique Java class names
      • You use the reverse of your company web address and then add the name of the extension
        For example: com.sap.sample.d3.bulletchart
      • IMPORTANT note for SAP Lumira 1.15:
        • The ID must be all lowercase otherwise it will not work inside SAP Lumira
    • Settings:
      • ID:       com.sap.sample.d3.bulletchart
      • Name: Bullet Chart
    • Note: As you close the properties window or move between fields the vizPacker updates the ID and Name used in the code editor
  4. Using the LAYOUT DESIGN tab, set the ID and name of your extension plot area:
    • Click the inner gray box to display the chart plot are properties:
    • About IDs:
      • Follow the same rules as for the extension ID, but append module to the end
    • Settings:
      • ID:       com.sap.sample.d3.bulletchartmodule
      • Name: Bullet Chart Module
    • Note: As you close the properties window or move between fields the vizPacker updates the ID and Name used in the code editor
  5. Remove the legend as the Bullet Chart does not need a legend
    • Click the X at the top of Legend:
  6. Import test data for the Bullet Chart:
    • Create a CSV version of the JSON Bullet Chart data, see the resources section (below) for the CSV file I made for the Bullet Chart
    • Switch to the DATA MODEL tab
    • Click to select and upload the CSV file:
    • After you have chosen your CSV file.
    • Click OK when you see a JavaScript alert as you import the CSV file:
    • The data model now shows the imported CSV data:
    • Note: There are 8 dimensions (one for each column) and 0 measures
  7. Next we will create the measure and dimension groups (another names for arrays, as they will be used in Lumira) and map test data columns to them.
    • Columns Title and Subtile ===> Titles
    • Columns Target ===> Target
    • Columns Range 1, Range 2 and Range 3 ===> Ranges
  8. Measure and dimension groups:
    • Measure groups (arrays) are for the numeric values in Lumira
      • So for the Bullet Chart these are the Actuals, Target and Ranges
    • Dimension groups (arrays) are for everything else:
      • So for the Bullet Chart these are the Titles
  9. Create the measure and dimension groups to match the JSON format that we need:
    • Create Titles as the first dimension group:
      • Click the drop down arrow next to Title
      • Then the right arrow (if expanded will show a drop down arrow as below) for the dimension
      • Double click the name X Axis (the name the first radio button) and change the name to Titles, then press ENTER
      • And select the first radio button, so the Title column is now mapped into the dimension group called Titles
    • Map the Subtitle column to the Titles dimension group as well
    • Create Actuals as the first measure group:
      • Click the drop down arrow next to Actual
      • Then the right arrow (if expanded will show a drop down arrow as below) for the measure
      • Double click the name Y Axis (the name of the first measure radio button) and change the name to Actuals, then press ENTER
      • And select the first radio button, so the Actual column is now mapped into the measure group called Actuals
    • Map Pace into the Actuals measure group
    • Map Target into measure group 2 and call it Target
    • Map Range 1, Range 2 and Range 3 into measure group 3 and call it Ranges
    • Click to Apply the Data Model:

      The data model, structure and test data are now applied in the vizPacker
  10. Prepare the render function:
    • The only lines we really need from the template render function are for creating the vis container
    • So update the render function so it looks like this:
      var render = function(data, container, width, height, colorPalette, properties, dispatch) {
              //prepare canvas with width and height of container
              container.selectAll('svg').remove();
              var vis = container.append('svg').attr('width', width).attr('height', height)
                          .append('g').attr('class', 'vis').attr('width', width).attr('height', height);

      };
  11. Add the code from bullets.js:
    • The code is wrapped in an anonymous function that we do not need
    • We do not want that anonymous function, so copy all the code except the first and last line in the file (note they have a red strike through them in these images):

      Copy down to:
    • And paste the code you just copied into the bottom of the vizPcker just before the return render call:
    • Change the bullet chart from a global namespace (as that is bad practice for extensions - d3.bullet) to a local variable (var d3_bullet):
      • Look for the line that says:
        d3.bullet = function() {
      • And change it to a local variable like this:
        var d3_bullet = function() {
      • We can now refer to d3_bullet when we need to create the bullet chart later on.
  12. Add in the code from index.html to create the bullet chart:
    • Copy all of the script code after the fdata line because we already have the fdata generated in our render function:

      Copy down to:
    • And paste the code you just copied into the bottom of the render function:
    • Change from the d3.bullet call to d3_bullet (the local variable):
      • Look for the line that says:
        var chart = d3.bullet
      • And change the dot to an underscore like this:
        var chart = d3_bullet
  13. Remember I mentioned that the Bullet Chart expects data as JSON, that means we need to convert the data from D3.csv.
    If your D3 extension uses the D3.csv data format there is no need to do this conversion.
    This is how I chose to do this for this example:
    • Copy the code from VizPacker__d3csv_to_JSON.txt
    • Paste the code in just below the vis container and above the bullet chart code
    • Here are the first few lines of that code to convert between D3.csv and JSON formats:
      var fdata = [];
      var meta = data.meta;
      var titleFields = meta.dimensions('Titles');
      var actualFields = meta.measures('Actuals');
      var targetFields = meta.measures('Target');
      var rangeFields = meta.measures('Ranges');
    • Let's break this code down as it is specific to the vizPacker and how you get the names of the CSV columns that are used in the measure and dimension groups (in vizPacker and later in Lumira as well) that we defined earlier in the DATA MODEL tab:
    • var fdata = [[;
      This is the JSON data variable that was used in the original vizPacker, this is where we will store the converted JSON data.
    • var meta = data.meta;
      Get access to the metadata that is passed in by vizPacker (and later in Lumira as well).
    • var titleFields = meta.dimensions('Titles');
      Get the list of column names from the CSV file that are mapped into the Titles dimension group (this matches to the Titles dimension group we mapped Title and Subtitle to earlier).
    • var actualFields = meta.measures('Actuals');
      Get the list of column names from the CSV file that are mapped to the Actuals measure group.
    • Then we do the same for Target and Ranges measure groups.
  14. That is most of what we need to do because we now have the bullet chart code and rendering code in.
    What we are missing are the CSS styles, we will do that soon.
  15. Change the Bullet Chart so it adjusts to the width variable passed in to the render function:
    • Look for the line:
      var bulletWidth = 960;
    • And change it to:
      var bulletWidth = width;
  16. Add in the CSS styles from index.html to style the bullet chart:
    • Switch to the style tab, go to >> and then select style.css:
    • Copy the CSS bullet styles:
      .bullet { font: 10px sans-serif; }
      .bullet .marker { stroke: #000; stroke-width: 2px; }
      .bullet .tick line { stroke: #666; stroke-width: .5px; }
      .bullet .range.s0 { fill: #eee; }
      .bullet .range.s1 { fill: #ddd; }
      .bullet .range.s2 { fill: #ccc; }
      .bullet .measure.s0 { fill: lightsteelblue; }
      .bullet .measure.s1 { fill: steelblue; }
      .bullet .title { font-size: 14px; font-weight: bold; }
      .bullet .subtitle { fill: #999; }
    • Delete all of existing style code from style.css
    • Paste in the CSS bullet chart styles
    • Alter the first line of CSS (the added bit is in bold) to stop Lumira from overriding the font style:

      .bullet { font: 10px sans-serif; !important; }
  17. Validate your code inside the vizPacker:
    • Click on Run Code
    • If you get a JavaScript error message then there is something wrong with your code:
      • Close the JavaScript error message
      • Right-click on code editor and select Inspect Element to enable the Google Chrome developer area
      • Then go to the Console tab and look at the error messages
      • Fix your code in the vizPacker code editor and then click Run Code again
  18. Once your code is validated, preview your extension inside the vizPacker:
    • Click RUN CODE
    • Click to turn on preview mode:
    • The preview window will appear inside the code editor and you should see your Bullet Chart:
  19. Next you are ready to pack(age) and test inside SAP Lumira

Pack and install your extension into SAP Lumira

  1. Click Pack
  2. The extension will be packed into a ZIP file. Click the ZIP file link:
  3. Google Chrome will download the packed ZIP file
  4. Select the downloaded ZIP file and select Show in folder
  5. Extract out the the packed extension and install it into SAP Lumira:
    • Extract out all the files and folders from the ZIP file
    • Copy the bundles/com folder to <installdir>\SAP Lumira\Desktop\extensions\bundles

      For example: <installdir>\SAP Lumira\Desktop\extensions\bundles\com

      If you already have a com folder you will be asked to merge. That is Okay because like we did for Hello World you should have created a unique ID for this extension to the sub-folders will not clash with another extension.
  6. Next we can test the extension inside SAP Lumira

Test your D3 chart inside SAP Lumira

  1. Start SAP Lumira
  2. Import the bullet chart test CSV file, see the resources section (below)
  3. Select the Bullet Chart extension:
  4. Use these settings with the chart:
    TitlesTitle, Subtitle
    ActualsActual, Pace
    TargetTarget
    RangesRange 1, Range 2, Range 3
  5. The Bullet Chart should now look like this when running in Lumira:


    Note: If this does not work, you will need to refer to the SAP Lumira SDK Getting Started Guide on how to debug within SAP Lumira
  6. That's it, you have created, packaged and tested your first D3 extension using the vizPacker!
  7. Congratulations and happy coding...

More information

Have a look at the:

Resources

You can find all of the Bullet Chart files on GitHub.

15 Comments