Developing for Splunk Enterprise

What's the Simplest Modal Pop-Up in Splunk?

David
Splunk Employee
Splunk Employee

I'm not a Javascript programmer, but I want to put a pretty modal pop-up in my Splunk dashboard. How do I do that?

1 Solution

David
Splunk Employee
Splunk Employee

I was able to simplify some of the existing examples -- if you already have some javascript, it should be set up in just a minute or two of copy-paste.

Here's what it looks like:
alt text

Just add this somewhere in your Javascript file (I recommend at the start, for simplicity -- it doesn't have to live inside of any require or etc.)

function Modal(){
    require(["underscore"],function(_) {
        var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
        function Modal(id, options) {
            var _this = this;

            _classCallCheck(this, Modal);

            var modalOptions = _.extend({ show: false }, options);

            // if "id" is the element that triggers the modal display, extract the actual id from it; otherwise use it as-is
            var modalId = id //!= null && (typeof id === 'undefined' ? 'undefined' : _typeof(id)) === 'object' && id.jquery != null ? id.attr('data-target').slice(1) : id;

            var header = $('<div>').addClass('modal-header');

            var headerCloseButton = $('<button>').addClass('close').attr({
                'type': 'button',
                'data-dismiss': 'modal',
                'aria-label': 'Close'
            }).append($('<span>').attr('aria-hidden', true).text('&times;'));

            this.title = $('<h3>').addClass('modal-title');

            this.body = $('<div>').addClass('modal-body');

            this.footer = $('<div>').addClass('modal-footer');

            this.$el = $('<div>').addClass('modal mlts-modal').attr('id', modalId).append($('<div>').addClass('modal-dialog').append($('<div>').addClass('modal-content').append(header.append(headerCloseButton, this.title), this.body, this.footer)));

            if (modalOptions.title != null) this.setTitle(modalOptions.title);

            if (modalOptions.type === 'wide') this.$el.addClass('modal-wide');else if (modalOptions.type === 'noPadding') this.$el.addClass('mlts-modal-no-padding');

            // remove the modal from the dom after it's hidden
            if (modalOptions.destroyOnHide !== false) {
                this.$el.on('hidden.bs.modal', function () {
                    return _this.$el.remove();
                });
            }

            this.$el.modal(modalOptions);
        }

        _createClass(Modal, [{
            key: 'setTitle',
            value: function setTitle(titleText) {
                this.title.text(titleText);
            }
        }, {
            key: 'show',
            value: function show() {
                this.$el.modal('show');
            }
        }, {
            key: 'hide',
            value: function hide() {
                this.$el.modal('hide');
            }
        }]);
        window.Modal = Modal
        return Modal;
    })
}

Then to actually use it, you can copy-paste this code:

   var myModal = new Modal('MyModalID-irrelevant-unless-you-want-many', {
        title: 'Test Modal',
        destroyOnHide: true,
        type: 'wide'
    });

    $(myModal.$el).on("hide", function(){
        // Not taking any action on hide, but you can if you want to!
     })

    myModal.body.addClass('mlts-modal-form-inline')
        .append($('<p>Here is what goes in the body.. feel free to add Splunk inputs, reports, etc. Whatever your heart and SplunkJS skill desires.</p>'));

    myModal.footer.append($('<button>').addClass('mlts-modal-submit').attr({
        type: 'button',
        'data-dismiss': 'modal'
    }).addClass('btn btn-primary mlts-modal-submit').text('Close').on('click', function () {
        // Not taking any action on Close... but I could!        
    }))  
    myModal.show(); // Launch it!

View solution in original post

David
Splunk Employee
Splunk Employee

I was able to simplify some of the existing examples -- if you already have some javascript, it should be set up in just a minute or two of copy-paste.

Here's what it looks like:
alt text

Just add this somewhere in your Javascript file (I recommend at the start, for simplicity -- it doesn't have to live inside of any require or etc.)

function Modal(){
    require(["underscore"],function(_) {
        var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
        function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
        function Modal(id, options) {
            var _this = this;

            _classCallCheck(this, Modal);

            var modalOptions = _.extend({ show: false }, options);

            // if "id" is the element that triggers the modal display, extract the actual id from it; otherwise use it as-is
            var modalId = id //!= null && (typeof id === 'undefined' ? 'undefined' : _typeof(id)) === 'object' && id.jquery != null ? id.attr('data-target').slice(1) : id;

            var header = $('<div>').addClass('modal-header');

            var headerCloseButton = $('<button>').addClass('close').attr({
                'type': 'button',
                'data-dismiss': 'modal',
                'aria-label': 'Close'
            }).append($('<span>').attr('aria-hidden', true).text('&times;'));

            this.title = $('<h3>').addClass('modal-title');

            this.body = $('<div>').addClass('modal-body');

            this.footer = $('<div>').addClass('modal-footer');

            this.$el = $('<div>').addClass('modal mlts-modal').attr('id', modalId).append($('<div>').addClass('modal-dialog').append($('<div>').addClass('modal-content').append(header.append(headerCloseButton, this.title), this.body, this.footer)));

            if (modalOptions.title != null) this.setTitle(modalOptions.title);

            if (modalOptions.type === 'wide') this.$el.addClass('modal-wide');else if (modalOptions.type === 'noPadding') this.$el.addClass('mlts-modal-no-padding');

            // remove the modal from the dom after it's hidden
            if (modalOptions.destroyOnHide !== false) {
                this.$el.on('hidden.bs.modal', function () {
                    return _this.$el.remove();
                });
            }

            this.$el.modal(modalOptions);
        }

        _createClass(Modal, [{
            key: 'setTitle',
            value: function setTitle(titleText) {
                this.title.text(titleText);
            }
        }, {
            key: 'show',
            value: function show() {
                this.$el.modal('show');
            }
        }, {
            key: 'hide',
            value: function hide() {
                this.$el.modal('hide');
            }
        }]);
        window.Modal = Modal
        return Modal;
    })
}

Then to actually use it, you can copy-paste this code:

   var myModal = new Modal('MyModalID-irrelevant-unless-you-want-many', {
        title: 'Test Modal',
        destroyOnHide: true,
        type: 'wide'
    });

    $(myModal.$el).on("hide", function(){
        // Not taking any action on hide, but you can if you want to!
     })

    myModal.body.addClass('mlts-modal-form-inline')
        .append($('<p>Here is what goes in the body.. feel free to add Splunk inputs, reports, etc. Whatever your heart and SplunkJS skill desires.</p>'));

    myModal.footer.append($('<button>').addClass('mlts-modal-submit').attr({
        type: 'button',
        'data-dismiss': 'modal'
    }).addClass('btn btn-primary mlts-modal-submit').text('Close').on('click', function () {
        // Not taking any action on Close... but I could!        
    }))  
    myModal.show(); // Launch it!

View solution in original post

kdimaria
Communicator

Do you know how to add a Splunk search/table/visualization to this modal??

0 Karma

David
Splunk Employee
Splunk Employee

Same way you add it to any SplunkJS content, actually. Include a <div id="blah"></div> in your HTML, and then have the form elements trigger there. If you can get it working in normal SplunkJS (including doing a normal dashboard, then converting), it should work just fine in the modal (after you move things around).

Here's a working code segment from Splunk Security Essentials (if you open a time series use case and click "Schedule High Cardinality Alert"). The actual modal code is probably not as polished since it was my first time adapting someone else's code, and the SplunkJS is slightly non-traditional, but it's not too crazy. If you want to view in context, it is in the app Splunk Security Essentials, appserver/static/components/pages/showcase_standard_deviation.js

                outliersVizAlertModal = new Modal('outliersVizAlertModal', {
                    title: 'Schedule an alert',
                    destroyOnHide: false,
                    type: 'wide'
                });

                var outlierSearchTypeControl = new DropdownView({
                    id: 'outliersVizAlertModalTypeControl',
                    el: $('<span>'),
                    labelField: 'label',
                    valueField: 'value',
                    showClearButton: false,
                    choices: [{ value: 'both', label: 'outside both thresholds' }, { value: 'above', label: 'above the upper threshold' }, { value: 'below', label: 'below the lower threshold' }]
                }).render();

                var outliersVizAlertModalValueControl = new TextInputView({
                    id: 'outliersVizAlertModalValueControl',
                    el: $('<span>')
                }).render();



                var indexesSearch = new SearchManager({
                    "id": "indexesSearch",
                    "cancelOnUnload": true,
                    "latest_time": "0",
                    "sample_ratio": null,
                    "status_buckets": 0,
                    "earliest_time": "now",
                    "search": "| rest /services/data/indexes | search disabled=0 title!=_* | table title | sort title | streamstats count | eval count=if(title=\"summary\",0,count) | sort count | fields - count",
                    "app": utils.getCurrentApp(),
                    "auto_cancel": 90,
                    "preview": true,
                    "runWhenTimeIsUndefined": false
                }, {tokens: true});

                var DropdownInput = require("splunkjs/mvc/simpleform/input/dropdown");
               var indexesSearchTypeControl = new DropdownInput({
                        "id": "indexesSearchTypeControl",
                        "choices": [],
                        "labelField": "title",
                        "selectFirstChoice": true,
                        "valueField": "title",
                        "showClearButton": true,
                        "searchWhenChanged": true,
                        "managerid": "indexesSearch",
                        "el": $('<span>')
                    }, {tokens: true}).render();

               var TextInput = require("splunkjs/mvc/simpleform/input/text");
                var SummaryUniqueName = new TextInput({
                    "id": "SummaryUniqueName",
                    "default": "",
                    "el": $('<span>')
                }, {tokens: true}).render();
                SummaryUniqueName.on("load", function(){
                    $(".mlts-modal-submit").last()[0].disabled=true
                });
                SummaryUniqueName.on("keydown",function(properties){

                    if(document.getElementById("SummaryUniqueName_Validation") == null){
                        $("#SummaryUniqueName").after("<div id=\"SummaryUniqueName_Validation\">Invalid</div>")
                    }
                    var Errors=new Array()
                    if(properties.length<8){
                        Errors.push("Name not long enough (" + properties.length + " characters). Performance will be better with at least 8 characters");
                    }
                    if(properties.search(/[^a-zA-Z0-9]/) >0){
                        Errors.push("Invalid character detected. Only alphanumeric characters permitted (a-z, A-Z, 0-9).");
                    }
                    if(Errors.length>0){
                        $(".mlts-modal-submit").last()[0].disabled=true
                        document.getElementById("SummaryUniqueName_Validation").innerHTML = "<p>" + Errors.join("</p><p>") + "</p>"
                    }else{
                         $(".mlts-modal-submit").last()[0].disabled=false
                        document.getElementById("SummaryUniqueName_Validation").innerHTML = "<p>Valid!</p>"
                    }
                });


                outliersVizAlertModal.body.addClass('mlts-modal-form-inline').append($('<p>').text('Alert me when the number of outliers '), outlierSearchTypeControl.$el, $('<p>').text('is greater than '), outliersVizAlertModalValueControl.$el, $('<br>'), $('<br>'), $('<p>').text("Choose the index that our summary indexed results will be stored in:"), indexesSearchTypeControl.$el, $('<br>'), $('&lt;br/&gt;'), $('<p>').text("Choose the unique name that will identify these events -- alphanumeric, no spaces, no punctuation, at least 8 characters:"), SummaryUniqueName.$el, $("<button>Validate String</button>"));

                outliersVizAlertModal.footer.append($('<button>').addClass('mlts-modal-cancel').attr({
                    type: 'button',
                    'data-dismiss': 'modal'
                }).addClass('btn btn-default mlts-modal-cancel').text('Cancel'), $('<button>').attr({
                    type: 'button'
                }).on('click', function () { .....
0 Karma
Register for .conf21 Now! Go Vegas or Go Virtual!

How will you .conf21? You decide! Go in-person in Las Vegas, 10/18-10/21, or go online with .conf21 Virtual, 10/19-10/20.