Example: Drilldown properties in HTML dashboards

This example uses a converted HTML Dashboard to show the result of setting different combinations of drilldown properties for the views that allow drilldown actions.

[example]

This example shows the modified code (which is highlighted) for a converted HTML dashboard, and the Simple XML code for the original dashboard.

This example also uses search results from a sample earthquakes lookup table for the SplunkMap view, and includes lat and lon fields in the search query.

To view this dashboard:

  1. Download earthquakes.csv to a /lookups directory under $SPLUNK_HOME/etc/apps/app_name.
  2. Copy the HTML code, find and replace "APP_NAME" with your app name, then save the HTML file under an existing app in $SPLUNK_HOME/etc/apps/app_name/local/data/ui/html/.
  3. Refresh the Splunk Web server by going to http://<localhost:port>/debug/refresh and clicking Refresh.
  4. In Splunk Web, view the dashboard in your app. For example, to view myexample.html in mysplunkapp, go to http://<localhost:port>/app/mysplunkapp/myexample.

To recreate the example:

  1. Save the XML file under an existing app in $SPLUNK_HOME/etc/apps/app_name/local/data/ui/views/.
  2. Refresh the Splunk Web server by going to http://<localhost:port>/debug/refresh and clicking Refresh.
  3. In Splunk Web, open the dashboard in your app. For example, to view myexample.xml in mysplunkapp, go to http://<localhost:port>/app/mysplunkapp/myexample.
  4. Click ..., then click Convert to HTML.
  5. Click Shared for Permissions, click Convert Dashboard, then click Edit HTML.
  6. Modify the HTML as shown in the example, then click Save.

drilldown_html.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Drilldown properties and events</title>
    <link rel="shortcut icon" href="{{SPLUNKWEB_URL_PREFIX}}/static/img/favicon.ico" />
        <link rel="stylesheet" type="text/css" href="/en-US/static/@f4c1eb50e0f3/css/build/bootstrap.min.css" />
        <link rel="stylesheet" type="text/css" href="/en-US/static/@f4c1eb50e0f3/css/build/pages/dashboard-simple-bootstrap.min.css" />
    <link rel="stylesheet" type="text/css" media="all" href="{{SPLUNKWEB_URL_PREFIX}}/static/app/APP_NAME/dashboard.css" />
</head>
<body class="simplexml preload locale-en">
<!--
BEGIN LAYOUT
This section contains the layout for the dashboard. Splunk uses proprietary
styles in <div> tags, similar to Bootstrap's grid system.
-->
<header>
<a class="navSkip" href="#navSkip" tabindex="1">Screen reader users, click here to skip the navigation bar</a>
<div class="header splunk-header">
        <div id="placeholder-splunk-bar">
            <a href="{{SPLUNKWEB_URL_PREFIX}}/app/launcher/home" class="brand" title="splunk > listen to your data">splunk<strong>></strong></a>
        </div>
            <div id="placeholder-app-bar"></div>
</div>
<a id="navSkip"></a>
</header>
<div class="dashboard-body container-fluid main-section-body" data-role="main">
    <div class="dashboard-header clearfix">
        <h2>Drilldown properties and events</h2>
        <p class="description">You can set drilldown properties for the EventsViewer, Chart, Table, and SplunkMap views. This example shows different ways to set these properties in JavaScript.</p>
    </div>


    <div id="row1" class="dashboard-row dashboard-row1">
        <div id="panel1" class="dashboard-cell" style="width: 100%;">
            <div class="dashboard-panel clearfix">
                <h2 class="panel-title">EVENTS</h2>

                <div class="panel-element-row">
                    <div id="element1" class="dashboard-element event" style="width: 100%">
                        <div class="panel-head">
                            <h3>Change drilldown to "inner", allow redirect to Search</h3>
                        </div>
                        <div class="panel-body"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div id="row2" class="dashboard-row dashboard-row2">
        <div id="panel2" class="dashboard-cell" style="width: 100%;">
            <div class="dashboard-panel clearfix">
                <h2 class="panel-title">CHART</h2>

                <div class="panel-element-row">
                    <div id="element2" class="dashboard-element chart" style="width: 100%">
                        <div class="panel-head">
                            <h3>Disable redirect using the drilldownRedirect property, display chart-click and legend-click info in the console</h3>
                        </div>
                        <div class="panel-body"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div id="row3" class="dashboard-row dashboard-row3">
        <div id="panel3" class="dashboard-cell" style="width: 100%;">
            <div class="dashboard-panel clearfix">
                <h2 class="panel-title">TABLE</h2>

                <div class="panel-element-row">
                    <div id="element3" class="dashboard-element table" style="width: 100%">
                        <div class="panel-head">
                            <h3>Enable drilldown for table cells, disable redirect using the preventDefault method, and display table-click info in the console</h3>
                        </div>
                        <div class="panel-body"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div id="row4" class="dashboard-row dashboard-row4">
        <div id="panel4" class="dashboard-cell" style="width: 100%;">
            <div class="dashboard-panel clearfix">
                <h2 class="panel-title">MAP</h2>

                <div class="panel-element-row">
                    <div id="element4" class="dashboard-element map" style="width: 100%">
                        <div class="panel-head">
                            <h3>Disable redirect and display map-click info in the console</h3>
                        </div>
                        <div class="panel-body"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<!--
END LAYOUT
-->

<script src="{{SPLUNKWEB_URL_PREFIX}}/config?autoload=1"></script>
<script src="{{SPLUNKWEB_URL_PREFIX}}/static/js/i18n.js"></script>
<script src="{{SPLUNKWEB_URL_PREFIX}}/i18ncatalog?autoload=1"></script>
<script src="{{SPLUNKWEB_URL_PREFIX}}/static/build/simplexml/index.js"></script>
<script type="text/javascript">
// <![CDATA[
// <![CDATA[

//
// LIBRARY REQUIREMENTS
//
// In the require function, we include the necessary libraries and modules for
// the HTML dashboard. Then, we pass variable names for these libraries and
// modules as function parameters, in order.
//
// When you add libraries or modules, remember to retain this mapping order
// between the library or module and its function parameter. You can do this by
// adding to the end of these lists, as shown in the commented examples below.

require([
    "splunkjs/mvc",
    "splunkjs/mvc/utils",
    "splunkjs/mvc/tokenutils",
    "underscore",
    "jquery",
    "splunkjs/mvc/simplexml",
    "splunkjs/mvc/layoutview",
    "splunkjs/mvc/simplexml/dashboardview",
    "splunkjs/mvc/simplexml/dashboard/panelref",
    "splunkjs/mvc/simplexml/element/chart",
    "splunkjs/mvc/simplexml/element/event",
    "splunkjs/mvc/simplexml/element/html",
    "splunkjs/mvc/simplexml/element/list",
    "splunkjs/mvc/simplexml/element/map",
    "splunkjs/mvc/simplexml/element/single",
    "splunkjs/mvc/simplexml/element/table",
    "splunkjs/mvc/simpleform/formutils",
    "splunkjs/mvc/simplexml/eventhandler",
    "splunkjs/mvc/simplexml/searcheventhandler",
    "splunkjs/mvc/simpleform/input/dropdown",
    "splunkjs/mvc/simpleform/input/radiogroup",
    "splunkjs/mvc/simpleform/input/linklist",
    "splunkjs/mvc/simpleform/input/multiselect",
    "splunkjs/mvc/simpleform/input/checkboxgroup",
    "splunkjs/mvc/simpleform/input/text",
    "splunkjs/mvc/simpleform/input/timerange",
    "splunkjs/mvc/simpleform/input/submit",
    "splunkjs/mvc/searchmanager",
    "splunkjs/mvc/savedsearchmanager",
    "splunkjs/mvc/postprocessmanager",
    "splunkjs/mvc/simplexml/urltokenmodel"
    // Add comma-separated libraries and modules manually here, for example:
    // ..."splunkjs/mvc/simplexml/urltokenmodel",
    // "splunkjs/mvc/checkboxview"
    ],
    function(
        mvc,
        utils,
        TokenUtils,
        _,
        $,
        DashboardController,
        LayoutView,
        Dashboard,
        PanelRef,
        ChartElement,
        EventElement,
        HtmlElement,
        ListElement,
        MapElement,
        SingleElement,
        TableElement,
        FormUtils,
        EventHandler,
        SearchEventHandler,
        DropdownInput,
        RadioGroupInput,
        LinkListInput,
        MultiSelectInput,
        CheckboxGroupInput,
        TextInput,
        TimeRangeInput,
        SubmitButton,
        SearchManager,
        SavedSearchManager,
        PostProcessManager,
        UrlTokenModel

        // Add comma-separated parameter names here, for example:
        // ...UrlTokenModel,
        // CheckboxView
        ) {

        var pageLoading = true;


        //
        // TOKENS
        //

        // Create token namespaces
        var urlTokenModel = new UrlTokenModel();
        mvc.Components.registerInstance('url', urlTokenModel);
        var defaultTokenModel = mvc.Components.getInstance('default', {create: true});
        var submittedTokenModel = mvc.Components.getInstance('submitted', {create: true});

        urlTokenModel.on('url:navigate', function() {
            defaultTokenModel.set(urlTokenModel.toJSON());
            if (!_.isEmpty(urlTokenModel.toJSON()) && !_.all(urlTokenModel.toJSON(), _.isUndefined)) {
                submitTokens();
            } else {
                submittedTokenModel.clear();
            }
        });

        // Initialize tokens
        defaultTokenModel.set(urlTokenModel.toJSON());

        function submitTokens() {
            // Copy the contents of the defaultTokenModel to the submittedTokenModel and urlTokenModel
            FormUtils.submitForm({ replaceState: pageLoading });
        }

        function setToken(name, value) {
            defaultTokenModel.set(name, value);
            submittedTokenModel.set(name, value);
        }

        function unsetToken(name) {
            defaultTokenModel.unset(name);
            submittedTokenModel.unset(name);
        }



        //
        // SEARCH MANAGERS
        //

        var search1 = new SearchManager({
            "id": "search1",
            "cancelOnUnload": true,
            "latest_time": "$latest$",
            "search": "index=_internal | head 100 | fields *",
            "earliest_time": "0",
            "status_buckets": 300,
            "app": utils.getCurrentApp(),
            "auto_cancel": 90,
            "preview": true,
            "runWhenTimeIsUndefined": false
        }, {tokens: true, tokenNamespace: "submitted"});

        // search2 and search3 are variations of search1. A good practice to save resources is to remove
        // duplication by using search1 as a main search, and search2 and search3 as post-process searches.
        var search2 = new PostProcessManager({
            id: "search2",
            managerid: "search1",
            "id": "search2",
            "search": " | stats count by sourcetype"
        }, {tokens: true, tokenNamespace: "submitted"});

        var search3 = new PostProcessManager({
            id: "search3",
            managerid: "search1",
            "search": " | fields sourcetype, source, host"
        }, {tokens: true, tokenNamespace: "submitted"});

        var search4 = new SearchManager({
            "id": "search4",
            "cancelOnUnload": true,
            "latest_time": "$latest$",
            "search": "| inputlookup earthquakes.csv | rename Lat as lat Lon as lon | geostats count",
            "earliest_time": "0",
            "status_buckets": 0,
            "app": utils.getCurrentApp(),
            "auto_cancel": 90,
            "preview": true,
            "runWhenTimeIsUndefined": false
        }, {tokens: true, tokenNamespace: "submitted"});



        //
        // SPLUNK LAYOUT
        //

        $('header').remove();
        new LayoutView({"hideFooter": false, "hideSplunkBar": false, "hideAppBar": false, "hideChrome": false})
            .render()
            .getContainerElement()
            .appendChild($('.dashboard-body')[0]);


        //
        // DASHBOARD EDITOR
        //

        new Dashboard({
            id: 'dashboard',
            el: $('.dashboard-body'),
            showTitle: true,
            editable: true
        }, {tokens: true}).render();


        //
        // VIEWS: VISUALIZATION ELEMENTS
        //

        var element1 = new EventElement({
            "id": "element1",
            "table.drilldown": "all",
            "type": "raw",
            "list.drilldown": "inner",
            "raw.drilldown": "inner",
            "maxLines": 5,
            "rowNumbers": "0",
            "fields": ["host", "source", "sourcetype"],
            "list.wrap": "1",
            "count": 3,
            "table.wrap": "1",
            "managerid": "search1",
            "el": $('#element1')
        }, {tokens: true, tokenNamespace: "submitted"}).render();

        var element2 = new ChartElement({
            "id": "element2",
            "charting.chart.nullValueMode": "gaps",
            "charting.chart.sliceCollapsingThreshold": "0.01",
            "charting.legend.placement": "right",
            "charting.axisLabelsX.majorLabelStyle.rotation": "0",
            "charting.axisY.scale": "linear",
            "charting.chart.bubbleSizeBy": "area",
            "charting.chart.bubbleMinimumSize": "10",
            "resizable": true,
            "charting.axisLabelsX.majorLabelStyle.overflowMode": "ellipsisNone",
            "charting.layout.splitSeries.allowIndependentYRanges": "0",
            "charting.layout.splitSeries": "0",
            "charting.drilldown": "all",
            "charting.chart.stackMode": "default",
            "charting.chart.style": "shiny",
            "charting.axisTitleY.visibility": "visible",
            "charting.chart.showDataLabels": "none",
            "charting.axisY2.enabled": "false",
            "charting.legend.labelStyle.overflowMode": "ellipsisMiddle",
            "charting.axisY2.scale": "inherit",
            "charting.axisX.scale": "linear",
            "charting.chart": "bar",
            "charting.drilldownRedirect": "false",
            "charting.axisTitleX.visibility": "visible",
            "charting.chart.bubbleMaximumSize": "50",
            "charting.axisTitleY2.visibility": "visible",
            "managerid": "search2",
            "el": $('#element2')
        }, {tokens: true, tokenNamespace: "submitted"}).render();


        var element3 = new TableElement({
            "id": "element3",
            "count": 3,
            "dataOverlayMode": "none",
            "drilldown": "cell",
            "rowNumbers": "false",
            "wrap": "true",
            "managerid": "search3",
            "el": $('#element3')
        }, {tokens: true, tokenNamespace: "submitted"}).render();

        var element4 = new MapElement({
            "id": "element4",
            "mapping.tileLayer.minZoom": "0",
            "mapping.map.panning": "true",
            "mapping.choroplethLayer.maximumColor": "0xDB5800",
            "mapping.markerLayer.markerOpacity": "0.8",
            "mapping.map.scrollZoom": "false",
            "mapping.markerLayer.markerMinSize": "10",
            "mapping.choroplethLayer.neutralPoint": "0",
            "mapping.choroplethLayer.shapeOpacity": "0.75",
            "mapping.showTiles": "1",
            "mapping.tileLayer.tileOpacity": "1",
            "mapping.map.zoom": "2",
            "mapping.choroplethLayer.showBorder": "1",
            "mapping.tileLayer.maxZoom": "7",
            "drilldownRedirect": "false",
            "mapping.data.maxClusters": "100",
            "resizable": true,
            "mapping.choroplethLayer.colorMode": "auto",
            "mapping.map.center": "(0,0)",
            "drilldown": "all",
            "mapping.type": "marker",
            "mapping.markerLayer.markerMaxSize": "50",
            "mapping.choroplethLayer.minimumColor": "0x2F25BA",
            "mapping.choroplethLayer.colorBins": "5",
            "managerid": "search4",
            "el": $('#element4')
        }, {tokens: true, tokenNamespace: "submitted"}).render();


        //
        // Add events
        //

        // Set up event handlers to customize drilldown behavior
        element2.on("click:legend", function(e) {
            // Displays a data object in the console--the legend text
            console.log("Clicked the chart legend: ", e.name2);
        });

        element2.on("click:chart", function(e) {
            // Displays a data object in the console
            console.log("Clicked the chart: ", e.value);
        });

        element3.on("click", function(e) {
            // Bypass the default behavior
            e.preventDefault();

            // Displays a data object in the console
            console.log("Clicked the table:", e.data);
        });

        element4.on("click:marker", function(e) {
            // Bypass the default behavior
            e.preventDefault();

            // Display a data object in the console
            console.log("Map click: ", e.data['row.count'] + " earthquakes near (" + e.data['row.latitude'] + ", " +e .data['row.longitude'] + ")");
        });


        // Initialize time tokens to default
        if (!defaultTokenModel.has('earliest') && !defaultTokenModel.has('latest')) {
            defaultTokenModel.set({ earliest: '0', latest: '' });
        }

        submitTokens();


        //
        // DASHBOARD READY
        //

        DashboardController.ready();
        pageLoading = false;

    }
);
// ]]>
</script>
</body>
</html>

drilldown.xml

<dashboard>
  <label>Drilldown properties and events</label>
  <description>You can set drilldown properties for the EventsViewer, Chart, Table, and SplunkMap views. This example shows different ways to set these properties in JavaScript.</description>
  <row>
    <panel>
      <title>EVENTS</title>
      <event>
        <title>Change drilldown to "inner", allow redirect to Search</title>
        <search>
          <query>index=_internal | head 100 | fields *</query>
          <earliest>0</earliest>
          <latest></latest>
        </search>
        <option name="count">3</option>
        <option name="list.drilldown">inner</option>
        <option name="list.wrap">1</option>
        <option name="maxLines">5</option>
        <option name="raw.drilldown">inner</option>
        <option name="rowNumbers">0</option>
        <option name="table.drilldown">all</option>
        <option name="table.wrap">1</option>
        <option name="type">raw</option>
        <fields>["host","source","sourcetype"]</fields>
      </event>
    </panel>
  </row>
  <row>
    <panel>
      <title>CHART</title>
      <chart>
        <title>Disable redirect using the drilldownRedirect property, display chart-click and legend-click info in the console</title>
        <search>
          <query>index=_internal | head 100 | fields * | stats count by sourcetype</query>
          <earliest>0</earliest>
          <latest></latest>
        </search>
        <option name="charting.chart">bar</option>
        <option name="charting.axisY2.enabled">false</option>
        <option name="charting.axisLabelsX.majorLabelStyle.overflowMode">ellipsisNone</option>
        <option name="charting.axisLabelsX.majorLabelStyle.rotation">0</option>
        <option name="charting.axisTitleX.visibility">visible</option>
        <option name="charting.axisTitleY.visibility">visible</option>
        <option name="charting.axisTitleY2.visibility">visible</option>
        <option name="charting.axisX.scale">linear</option>
        <option name="charting.axisY.scale">linear</option>
        <option name="charting.axisY2.scale">inherit</option>
        <option name="charting.chart.bubbleMaximumSize">50</option>
        <option name="charting.chart.bubbleMinimumSize">10</option>
        <option name="charting.chart.bubbleSizeBy">area</option>
        <option name="charting.chart.nullValueMode">gaps</option>
        <option name="charting.chart.showDataLabels">none</option>
        <option name="charting.chart.sliceCollapsingThreshold">0.01</option>
        <option name="charting.chart.stackMode">default</option>
        <option name="charting.chart.style">shiny</option>
        <option name="charting.drilldown">all</option>
        <option name="charting.drilldownRedirect">false</option><!-- Manually added-->
        <option name="charting.layout.splitSeries">0</option>
        <option name="charting.layout.splitSeries.allowIndependentYRanges">0</option>
        <option name="charting.legend.labelStyle.overflowMode">ellipsisMiddle</option>
        <option name="charting.legend.placement">right</option>
      </chart>
    </panel>
  </row>
  <row>
    <panel>
      <title>TABLE</title>
      <table>
        <title>Enable drilldown for table cells, disable redirect using the preventDefault method, and display table-click info in the console</title>
        <search>
          <query>index=_internal | head 100 | fields * | fields sourcetype, source, host</query>
          <earliest>0</earliest>
          <latest></latest>
        </search>
        <option name="wrap">true</option>
        <option name="rowNumbers">false</option>
        <option name="drilldown">cell</option>
        <option name="dataOverlayMode">none</option>
        <option name="count">3</option>
      </table>
    </panel>
  </row>
  <row>
    <panel>
      <title>MAP</title>
      <map>
        <title>Disable redirect and display map-click info in the console</title>
        <search>
          <query>| inputlookup earthquakes.csv | rename Lat as lat Lon as lon | geostats count</query>
          <earliest>0</earliest>
          <latest></latest>
        </search>
        <option name="mapping.type">marker</option>
        <option name="mapping.choroplethLayer.colorBins">5</option>
        <option name="mapping.choroplethLayer.colorMode">auto</option>
        <option name="mapping.choroplethLayer.maximumColor">0xDB5800</option>
        <option name="mapping.choroplethLayer.minimumColor">0x2F25BA</option>
        <option name="mapping.choroplethLayer.neutralPoint">0</option>
        <option name="mapping.choroplethLayer.shapeOpacity">0.75</option>
        <option name="mapping.choroplethLayer.showBorder">1</option>
        <option name="mapping.data.maxClusters">100</option>
        <option name="mapping.map.center">(0,0)</option>
        <option name="mapping.map.panning">true</option>
        <option name="mapping.map.scrollZoom">false</option>
        <option name="mapping.map.zoom">2</option>
        <option name="mapping.markerLayer.markerMaxSize">50</option>
        <option name="mapping.markerLayer.markerMinSize">10</option>
        <option name="mapping.markerLayer.markerOpacity">0.8</option>
        <option name="mapping.showTiles">1</option>
        <option name="mapping.tileLayer.maxZoom">7</option>
        <option name="mapping.tileLayer.minZoom">0</option>
        <option name="mapping.tileLayer.tileOpacity">1</option>
        <option name="drilldown">all</option>
        <option name="drilldownRedirect">false</option><!-- Manually added-->
      </map>
    </panel>
  </row>
</dashboard>